dop251/goja
ECMAScript/JavaScript engine in pure Go
Healthy across the board
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 8w ago
- ✓16 active contributors
- ✓MIT licensed
Show all 6 evidence items →Show less
- ✓CI configured
- ✓Tests present
- ⚠Concentrated ownership — top contributor handles 77% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/dop251/goja)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/dop251/goja on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: dop251/goja
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/dop251/goja shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 8w ago
- 16 active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Concentrated ownership — top contributor handles 77% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live dop251/goja
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/dop251/goja.
What it runs against: a local clone of dop251/goja — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in dop251/goja | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 87 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of dop251/goja. If you don't
# have one yet, run these first:
#
# git clone https://github.com/dop251/goja.git
# cd goja
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of dop251/goja and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "dop251/goja(\\.git)?\\b" \\
&& ok "origin remote is dop251/goja" \\
|| miss "origin remote is not dop251/goja (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "compiler.go" \\
&& ok "compiler.go" \\
|| miss "missing critical file: compiler.go"
test -f "parser/parser.go" \\
&& ok "parser/parser.go" \\
|| miss "missing critical file: parser/parser.go"
test -f "runtime.go" \\
&& ok "runtime.go" \\
|| miss "missing critical file: runtime.go"
test -f "object.go" \\
&& ok "object.go" \\
|| miss "missing critical file: object.go"
test -f "func.go" \\
&& ok "func.go" \\
|| miss "missing critical file: func.go"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 87 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~57d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/dop251/goja"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Goja is a complete ECMAScript 5.1 JavaScript engine written in pure Go, capable of executing JavaScript code including regex, strict mode, and most ES6 features. It passes nearly all tc39 test262 tests and can run complex real-world projects like Babel and TypeScript compilers without native bindings. Monolithic single-package structure: runtime/VM logic lives in core files (compiler.go, compiler_expr.go, compiler_stmt.go for AST→bytecode), builtin_*.go files provide ES5/ES6 standard library implementations, and support modules in ast/ and file/ handle parsing and source maps. Array logic is split between array.go and array_sparse.go for performance.
👥Who it's for
Go developers and DevOps engineers who need to embed a JavaScript runtime in Go applications—whether for plugin systems, templating, configuration scripting, or sandboxed code execution—without relying on Node.js or V8 bindings.
🌱Maturity & risk
Production-ready and actively maintained. The project has a substantial codebase (1.8M lines of Go), comprehensive test coverage (builtin_*_test.go files throughout), CI/CD via GitHub Actions (.github/workflows/main.yml), and passes the extensive tc39 test262 suite. Regular commits indicate ongoing maintenance.
Low risk overall but with specific caveats: WeakMap/WeakRef/FinalizationRegistry have documented limitations due to Go's GC constraints (see README), JSON parsing doesn't handle broken UTF-16 surrogates, and Date arithmetic may overflow with large values. Single maintainer (dop251) is a moderate maintenance risk, though the project is mature enough to be stable.
Active areas of work
Active ES6 feature expansion (see milestone/1 mentioned in README), tc39 test262 compliance improvements (tracked via .tc39_test262_checkout.sh), and maintenance of the standard library builtins. No specific recent PR data visible in file list, but the presence of multiple builtin modules and test files suggests ongoing feature additions.
🚀Get running
git clone https://github.com/dop251/goja.git
cd goja
go mod download
go test ./...
Daily commands:
No server/CLI to start—Goja is a library. Use it in Go code: vm := New(); vm.RunString("console.log('hello')"). For testing: go test ./... or go test -run TestName for specific tests.
🗺️Map of the codebase
compiler.go— Core compiler that transforms parsed AST into executable bytecode; all JavaScript execution flows through this.parser/parser.go— ECMAScript parser that converts source code into AST; foundation for all script execution.runtime.go— JavaScript runtime execution engine; manages scope, values, and instruction execution (inferred as core runtime layer).object.go— Represents JavaScript objects and their properties; critical for nearly all runtime operations.func.go— Function object implementation including closures and call handling; essential for function-oriented JavaScript.builtin_array.go— Standard array prototype and methods; heavily used built-in that many scripts depend on.value.go— Core value type system representing all JavaScript values and their operations (inferred as foundational).
🛠️How to make changes
Add a new built-in object or prototype method
- Create or edit the appropriate builtin_*.go file (e.g., builtin_string.go for String methods) (
builtin_string.go) - Implement the method as a native Go function that takes (*vm, *Object, Call) and returns Value (
builtin_string.go) - Register the method in the proto setup function using o.defineOwnProperty or defineMethod (
builtin_string.go) - Add test cases to the corresponding builtin_*_test.go file (
builtin_string_test.go)
Add ES6 feature support
- Update parser/lexer.go if new tokens are needed (e.g., new keywords) (
parser/lexer.go) - Add AST node types to ast/node.go for the new syntax (
ast/node.go) - Implement parsing rules in parser/expression.go or parser/statement.go (
parser/expression.go) - Add bytecode generation in compiler_expr.go or compiler_stmt.go (
compiler_expr.go) - Implement runtime support in appropriate files (e.g., new builtin_feature.go) (
builtin_map.go) - Add comprehensive tests to compiler_test.go or feature-specific _test.go (
compiler_test.go)
Expose a Go function/type to JavaScript
- In object_template.go or object_goreflect.go, add reflection logic to wrap your Go type (
object_template.go) - Define a template method that maps Go fields/methods to JavaScript properties (
object_template.go) - In your main Go code, call vm.Set() or vm.ToValue() to register Go values in the JS context (
func.go) - Test the interop with func_test.go or object_goreflect_test.go patterns (
func_test.go)
Fix a parsing or compilation issue
- Add a failing test to parser/parser_test.go or compiler_test.go that exhibits the bug (
compiler_test.go) - Locate the issue in parser/parser.go (parsing) or compiler.go/compiler_expr.go (codegen) (
compiler.go) - Fix the logic (e.g., operator precedence in parser/expression.go or bytecode emission in compiler_expr.go) (
compiler_expr.go) - Ensure the test now passes and run the full test suite (
compiler_test.go)
🔧Why these technologies
- Pure Go implementation — No external C dependencies; single cross-platform binary; full introspection of runtime state.
- Bytecode compilation model — Faster execution than tree-walking; enables optimization passes; more similar to real JS engines.
- regexp2 (Go regex library) — Provides .NET-style regex syntax support required by ECMAScript RegExp specification.
- Reflection (Go reflect package) — Enables seamless interop with Go types; wraps structs, slices, maps without code generation.
- Sourcemaps support (go-sourcemap) — Maps minified/transpiled bytecode errors back to original source for debugging.
⚖️Trade-offs already made
-
WeakMap implemented via reference embedding rather than true weak references
- Why: Go's GC does not expose weak references; embedding prevents garbage collection of keys.
- Consequence: WeakMap keys remain reachable even if logically "weak"; memory is held longer than spec-compliant implementations.
-
Bytecode-based execution over tree-walking interpreter
- Why: Better performance; clearer separation between compilation and execution.
- Consequence: More complex codebase; harder to debug; compilation overhead for one-off scripts.
-
Go
- Why: undefined
- Consequence: undefined
🪤Traps & gotchas
WeakMap caveat: values embedded in keys remain reachable until keys are unreachable—can cause unexpected memory usage (documented in README). UTF-16 JSON: JSON.parse() uses Go UTF-8 stdlib and cannot parse broken surrogate pairs like \uD800. Date overflow: integer arithmetic in Go means Date constructor can overflow with large values unlike JS (uses int not float). Test262 coupling: The .tc39_test262_checkout.sh script pins a specific commit of the external test suite; updating it requires verifying tests still pass. No async/await: Only ES5.1+ core is guaranteed; Promises exist but async/await is not implemented.
🏗️Architecture
💡Concepts to learn
- Bytecode compilation — Goja converts JavaScript AST to bytecode before execution rather than tree-walking; understanding compiler.go's instruction set is needed to optimize or debug runtime behavior
- Sparse arrays — array_sparse.go optimizes memory for arrays with gaps (e.g., [1,,3]); essential to understand when adding array methods or debugging memory usage
- Property descriptors — ES5 Object.defineProperty uses descriptor objects (value, writable, enumerable, configurable); builtin_object.go implements this and it's core to how properties work
- Weak references (WeakMap/WeakSet) — Goja's WeakMap implementation embeds references in keys instead of using weak pointers due to Go GC constraints; understanding this trade-off prevents misuse and memory leaks
- Source maps — Goja supports source maps via go-sourcemap; needed when debugging transpiled code or integrating with build tools like Babel
- Strict mode semantics — Goja implements full ES5 strict mode which changes variable scoping, 'this' binding, and error handling; compiler.go tracks strict mode state during compilation
- Prototype-based inheritance — JavaScript's object model uses prototype chains rather than classes; builtin_object.go and builtin_function.go implement this, and it's fundamental to understanding how methods are resolved
🔗Related repos
robertkrimen/otto— Direct predecessor mentioned in README; another Go JavaScript engine that inspired goja's designgrafana/cortex— Large production project using Goja for rule evaluation and metric processing in the Prometheus ecosystemdop251/goja_nodejs— Companion library (listed in go.mod) adding Node.js compatible APIs (fs, path, http, etc.) on top of Gojatc39/test262— The official ECMAScript conformance test suite that Goja tracks via .tc39_test262_checkout.sh; defines compliance targetsgolang/go— Goja depends on Go's runtime GC behavior and stdlib (regexp, text, time); understanding Go's memory model is essential for WeakMap limitations
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive BigInt arithmetic and comparison tests in builtin_bigint_test.go
The repo has builtin_bigint.go implemented but the test file is sparse. BigInt is a critical ES2020 feature that needs robust coverage for arithmetic operations (+, -, *, /, %), bitwise operations, and edge cases like very large numbers and type coercion. This will improve standard compliance and catch regressions.
- [ ] Review existing builtin_bigint_test.go and identify gaps in arithmetic operation tests
- [ ] Add test cases for BigInt +, -, *, /, % operations with various operand sizes
- [ ] Add test cases for bitwise operations (&, |, ^, <<, >>, >>>) on BigInt values
- [ ] Add test cases for BigInt comparison operators and type coercion edge cases
- [ ] Add test cases for BigInt with negative values and zero
- [ ] Run against tc39 test262 BigInt tests to ensure compliance
Implement comprehensive Promise rejection handling tests in builtin_promise.go
The repo has builtin_promise.go but lacks dedicated test coverage. Promise rejection handling is critical for async code reliability. Adding tests for catch chains, finally blocks, and race conditions will ensure Promise implementation is production-ready and catches edge cases in async flow.
- [ ] Create builtin_promise_test.go file if it doesn't exist with basic Promise constructor tests
- [ ] Add test cases for Promise.then() with resolution and rejection
- [ ] Add test cases for Promise.catch() chains and error propagation
- [ ] Add test cases for Promise.finally() semantics
- [ ] Add test cases for Promise.all(), Promise.race(), Promise.allSettled() with mixed resolved/rejected promises
- [ ] Add test cases for unhandled rejection scenarios and error recovery
Add missing Symbol feature tests in builtin_symbol.go and implement Symbol.iterator support
Symbol is an ES6 feature with builtin_symbol.go present but minimal test coverage. Symbol.iterator is fundamental for for...of loops and custom iteration. Adding comprehensive tests and ensuring Symbol.iterator works across Array, String, Map, Set will increase ES6 compliance significantly.
- [ ] Create builtin_symbol_test.go with basic Symbol creation and property tests
- [ ] Add test cases for Symbol.for() and Symbol.keyFor() global registry
- [ ] Add test cases for well-known Symbols (Symbol.iterator, Symbol.hasInstance, Symbol.toStringTag)
- [ ] Add test cases for Symbol.iterator on built-in types (Array, String, Map, Set)
- [ ] Add test cases for custom iterators using Symbol.iterator
- [ ] Test for...of loops and spread operator with custom iterables
- [ ] Verify integration with existing array_test.go and builtin_array.go for iteration support
🌿Good first issues
- Add comprehensive test coverage for builtin_promise.go (notice Promise is listed but builtin_promise_test.go doesn't exist in the file list)—verify promise chains, rejection handling, and edge cases
- Implement missing Math object methods or ensure all Math.* functions match the ES5 spec by comparing builtin_math.go against the tc39 test262 Math test suite
- Create a detailed guide in the README with concrete Go examples showing how to call JavaScript functions from Go and marshal different value types (objects, arrays, functions), since this is common but undocumented
⭐Top contributors
Click to expand
Top contributors
- @dop251 — 77 commits
- @shiroyk — 4 commits
- @mstoykov — 3 commits
- @monkeyWie — 3 commits
- [@Valeriy Gorelov](https://github.com/Valeriy Gorelov) — 2 commits
📝Recent commits
Click to expand
Recent commits
065cd97— Ensure the environment is properly restored before entering 'finally' in leaveTry. Fixes #703. (dop251)6a7976c— Fixed generator return() so that it correctly handles yields from finally blocks. (dop251)913bd86— Fixed delete with optional chain as operand. It was returning true, but was not actually deleting. (dop251)0ba9a54— bump test262 version (#701) (mstoykov)8b74ce4— Simplified unicodeStringBuilder making sure Grow() adds the header when the buffer is empty. Fixes #699, closes #700 (dop251)651366f— Fix stack overflow when converting arrays with circular references to primitives (#695) (monkeyWie)2bb4c72— Replaced map[Value]... with propNameSet because Value can be unhashable (if it's a unicodeString). Fixes #691. (dop251)56b1242— Merge pull request #689 from Luo-Studio/master (dop251)00ab4e8— Merge pull request #687 from ankur22/fix/subarray-set (dop251)9b5d4e4— Fix https://github.com/dop251/goja/issues/688 (orktes)
🔒Security observations
The Goja JavaScript engine codebase has a reasonable security baseline but suffers from outdated dependencies and Go version requirements. The primary concerns are the use of unmaintained/old versions of Go (1.20) and security-sensitive libraries like yaml.v2 (with known CVEs). The project appears focused on core functionality without significant injection, hardcoding, or infrastructure misconfiguration risks visible in the provided files. The main recommendations are: (1) update Go version requirement, (2) migrate to newer dependency versions with security patches, (3) establish security disclosure procedures, and (4) implement automated dependency vulnerability scanning in the CI/CD pipeline.
- Medium · Outdated Go Version Requirement —
go.mod, README.md. The project requires Go 1.20, which is significantly outdated. Go 1.20 was released in February 2023 and is no longer receiving security updates. Current stable versions (1.21+) contain important security patches for the Go runtime, standard library, and dependency management. Fix: Update the minimum required Go version to at least 1.22 or the latest stable version. Review and test the codebase for compatibility with newer Go versions. - Medium · Outdated Dependencies with Known Vulnerabilities —
go.mod, go.sum. The go.sum and go.mod files reference dependencies with outdated versions that may contain known security vulnerabilities. Specifically: golang.org/x/text v0.3.8 (released August 2022), gopkg.in/yaml.v2 v2.4.0 (YAML parsing library with known CVEs), and google/pprof from early 2023. Fix: Update all dependencies to their latest versions. Run 'go get -u' and 'go mod tidy'. Check for security advisories using 'go list -u -m all' and review CVE databases for these packages. - Medium · YAML Parsing Library with Known Vulnerabilities —
go.mod. gopkg.in/yaml.v2 v2.4.0 has known security issues including potential denial-of-service vulnerabilities through crafted YAML input. yaml.v2 is in maintenance mode and yaml.v3 should be preferred. Fix: Migrate from gopkg.in/yaml.v2 to gopkg.in/yaml.v3 if possible. If yaml.v2 is required, ensure strict input validation and size limits on processed YAML files. - Low · Indirect Dependency Audit Gap —
go.mod. The codebase has indirect dependencies (kr/pretty v0.3.0) that should be periodically audited. These transitive dependencies can introduce vulnerabilities indirectly. Fix: Regularly run 'go mod audit' (Go 1.22+) or use 'nancy' tool to scan for known vulnerabilities in indirect dependencies. Implement dependency scanning in CI/CD pipeline. - Low · Lack of Security Documentation —
Repository root. No SECURITY.md file is present in the repository to communicate security policies, vulnerability disclosure procedures, or security contact information to researchers and users. Fix: Create a SECURITY.md file documenting: supported versions, how to report security vulnerabilities responsibly, expected response timeline, and any security policies. - Low · Missing SBOM and Dependency Verification —
Repository root. No Software Bill of Materials (SBOM) or go.sum integrity verification mechanisms are visible. This could affect supply chain security auditing. Fix: Ensure go.sum is committed and verified. Consider generating SBOM files (using tools like syft or cyclonedx-go) as part of the release process for transparency.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.