{"id":41018718,"url":"https://github.com/fastschema/qjs","last_synced_at":"2026-01-22T09:26:09.393Z","repository":{"id":308086073,"uuid":"1031308282","full_name":"fastschema/qjs","owner":"fastschema","description":"QJS is a CGO-Free, modern, secure JavaScript runtime for Go applications, built on the powerful QuickJS engine and Wazero WebAssembly runtime","archived":false,"fork":false,"pushed_at":"2025-10-28T11:59:46.000Z","size":2133,"stargazers_count":419,"open_issues_count":1,"forks_count":12,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-28T13:33:13.273Z","etag":null,"topics":["javascript-engine","quickjs","wasm"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fastschema.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-03T13:20:50.000Z","updated_at":"2025-10-28T11:59:14.000Z","dependencies_parsed_at":"2025-08-04T04:53:20.393Z","dependency_job_id":"777ccab6-a752-4114-ba91-9c019e38abe1","html_url":"https://github.com/fastschema/qjs","commit_stats":null,"previous_names":["fastschema/qjs"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/fastschema/qjs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastschema%2Fqjs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastschema%2Fqjs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastschema%2Fqjs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastschema%2Fqjs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fastschema","download_url":"https://codeload.github.com/fastschema/qjs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fastschema%2Fqjs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28660647,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T01:17:37.254Z","status":"online","status_checked_at":"2026-01-22T02:00:07.137Z","response_time":144,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript-engine","quickjs","wasm"],"created_at":"2026-01-22T09:26:09.269Z","updated_at":"2026-01-22T09:26:09.382Z","avatar_url":"https://github.com/fastschema.png","language":"Go","readme":"# QJS - JavaScript in Go with QuickJS and Wazero\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://pkg.go.dev/github.com/fastschema/qjs#section-readme\" target=\"_blank\" rel=\"noopener\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/go.dev-reference-blue?logo=go\u0026logoColor=white\" alt=\"Go.Dev reference\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://goreportcard.com/report/github.com/fastschema/qjs\" target=\"_blank\" rel=\"noopener\"\u003e\n    \u003cimg src=\"https://goreportcard.com/badge/github.com/fastschema/qjs\" alt=\"go report card\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/fastschema/qjs/branch/master\" \u003e\n    \u003cimg src=\"https://codecov.io/gh/fastschema/qjs/branch/master/graph/badge.svg?token=yluqOtL5z0\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/fastschema/qjs/actions\" target=\"_blank\" rel=\"noopener\"\u003e\n    \u003cimg src=\"https://github.com/fastschema/qjs/actions/workflows/ci.yml/badge.svg\" alt=\"test status\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://opensource.org/licenses/MIT\" target=\"_blank\" rel=\"noopener\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/license-MIT-brightgreen.svg\" alt=\"MIT license\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n\t\u003ca href=\"https://app.fossa.com/projects/git%2Bgithub.com%2Ffastschema%2Fqjs?ref=badge_shield\u0026issueType=license\" alt=\"FOSSA Status\"\u003e\n\t\t\u003cimg src=\"https://app.fossa.com/api/projects/git%2Bgithub.com%2Ffastschema%2Fqjs.svg?type=shield\u0026issueType=license\"/\u003e\n\t\u003c/a\u003e\n\t\u003ca href=\"https://app.fossa.com/projects/git%2Bgithub.com%2Ffastschema%2Fqjs?ref=badge_shield\u0026issueType=security\" alt=\"FOSSA Status\"\u003e\n\t\t\u003cimg src=\"https://app.fossa.com/api/projects/git%2Bgithub.com%2Ffastschema%2Fqjs.svg?type=shield\u0026issueType=security\"/\u003e\n\t\u003c/a\u003e\n\u003c/p\u003e\n\nQJS is a CGO-Free, modern, secure JavaScript runtime for Go applications, built on the powerful QuickJS engine and Wazero WebAssembly runtime.\n\nQJS allows you to run JavaScript code safely and efficiently, with full support for ES2023 features, async/await, and Go-JS interoperability.\n\n## Features\n\n- **JavaScript ES6+ Support**: Full ES2023 compatibility via QuickJS (NG fork).\n- **WebAssembly Execution**: Secure, sandboxed runtime using Wazero.\n- **Go-JS Interoperability**: Seamless data conversion between Go and JavaScript.\n- **ProxyValue Support**: Zero-copy sharing of Go values with JavaScript via lightweight proxies.\n- **Function Binding**: Expose Go functions to JavaScript and vice versa.\n- **Async/Await**: Full support for asynchronous JavaScript execution.\n- **Memory Safety**: Memory-safe execution environment with configurable limits.\n- **No CGO Dependencies**: Pure Go implementation with WebAssembly.\n\n## Benchmarks\n\n### Factorial Calculation\n\nComputing factorial(10) 1,000,000 times\n\n| Iteration | GOJA | ModerncQuickJS | QJS |\n| --- | --- | --- | --- |\n| 1 | 1.128s | 1.897s | 737.635ms |\n| 2 | 1.134s | 1.936s | 742.670ms |\n| 3 | 1.123s | 1.898s | 738.737ms |\n| 4 | 1.120s | 1.900s | 754.692ms |\n| 5 | 1.132s | 1.918s | 756.924ms |\n| Average | 1.127s | 1.910s | **746.132ms** |\n| Total | 5.637s | 9.549s | **3.731s** |\n| Speed | 1.51x | 2.56x | 1.00x |\n\n*Benchmarks run on AMD Ryzen 7 7840HS, 32GB RAM, Linux*\n\n### AreWeFastYet V8-V7\n\n| Metric | GOJA | ModerncQuickJS | QJS |\n| --- | --- | --- | --- |\n| Richards | 345 | 189 | **434** |\n| DeltaBlue | 411 | 205 | **451** |\n| Crypto | 203 | 305 | **393** |\n| RayTrace | 404 | 347 | **488** |\n| EarleyBoyer | 779 | 531 | **852** |\n| RegExp | **381** | 145 | 142 |\n| Splay | 1289 | 856 | **1408** |\n| NavierStokes | 324 | 436 | **588** |\n| Score (version 7) | 442 | 323 | **498** |\n| Duration (seconds) | 78.349s | 97.240s | **72.004s** |\n\n*Benchmarks run on AMD Ryzen 7 7840HS, 32GB RAM, Linux*\n\n## Example Usage\n\n### Basic Execution\n\n```go\nrt, err := qjs.New()\nif err != nil {\n\tlog.Fatal(err)\n}\n\ndefer rt.Close()\nctx := rt.Context()\n\nresult, err := ctx.Eval(\"test.js\", qjs.Code(`\n\tconst person = {\n\t\tname: \"Alice\",\n\t\tage: 30,\n\t\tcity: \"New York\"\n\t};\n\n\tconst info = Object.keys(person).map(key =\u003e\n\t\tkey + \": \" + person[key]\n\t).join(\", \");\n\n\t// The last expression is the return value\n\t({ person: person, info: info });\n`))\nif err != nil {\n\tlog.Fatal(\"Eval error:\", err)\n}\ndefer result.Free()\n// Output: name: Alice, age: 30, city: New York\nlog.Println(result.GetPropertyStr(\"info\").String())\n// Output: Alice\nlog.Println(result.GetPropertyStr(\"person\").GetPropertyStr(\"name\").String())\n// Output: 30\nlog.Println(result.GetPropertyStr(\"person\").GetPropertyStr(\"age\").Int32())\n```\n\n### Go function binding\n\n```go\nctx.SetFunc(\"goFunction\", func(this *qjs.This) (*qjs.Value, error) {\n    return this.Context().NewString(\"Hello from Go!\"), nil\n})\n\nresult, err := ctx.Eval(\"test.js\", qjs.Code(`\n\tconst message = goFunction();\n\tmessage;\n`))\nif err != nil {\n\tlog.Fatal(\"Eval error:\", err)\n}\ndefer result.Free()\n\n// Output: Hello from Go!\nlog.Println(result.String())\n```\n\n### HTTP Handlers in JavaScript\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/fastschema/qjs\"\n)\n\nfunc must[T any](val T, err error) T {\n\tif err != nil {\n\t\tlog.Fatalf(\"Error: %v\", err)\n\t}\n\treturn val\n}\n\nconst script = `\n// JS handlers for HTTP routes\nconst about = () =\u003e {\n\treturn \"QuickJS in Go - Hello World!\";\n};\n\nconst contact = () =\u003e {\n\treturn \"Contact us at contact@example.com\";\n};\n\nexport default { about, contact };\n`\n\nfunc main() {\n\trt := must(qjs.New())\n\tdefer rt.Close()\n\tctx := rt.Context()\n\n\t// Precompile the script to bytecode\n\tbyteCode := must(ctx.Compile(\"script.js\", qjs.Code(script), qjs.TypeModule()))\n\t// Use a pool of runtimes for concurrent requests\n\tpool := qjs.NewPool(3, \u0026qjs.Option{}, func(r *qjs.Runtime) error {\n\t\tresults := must(r.Context().Eval(\"script.js\", qjs.Bytecode(byteCode), qjs.TypeModule()))\n\t\t// Store the exported functions in the global object for easy access\n\t\tr.Context().Global().SetPropertyStr(\"handlers\", results)\n\t\treturn nil\n\t})\n\n\t// Register HTTP handlers based on JS functions\n\tval := must(ctx.Eval(\"script.js\", qjs.Bytecode(byteCode), qjs.TypeModule()))\n\tmethodNames := must(val.GetOwnPropertyNames())\n\tval.Free()\n\tfor _, methodName := range methodNames {\n\t\thttp.HandleFunc(\"/\"+methodName, func(w http.ResponseWriter, r *http.Request) {\n\t\t\truntime := must(pool.Get())\n\t\t\tdefer pool.Put(runtime)\n\n\t\t\t// Call the corresponding JS function\n\t\t\thandlers := runtime.Context().Global().GetPropertyStr(\"handlers\")\n\t\t\tresult := must(handlers.InvokeJS(methodName))\n\t\t\tfmt.Fprint(w, result.String())\n\t\t\tresult.Free()\n\t\t})\n\t}\n\n\thttp.HandleFunc(\"/\", func(w http.ResponseWriter, r *http.Request) {\n\t\tfmt.Fprintf(w, \"Hello from Go's HTTP server!\")\n\t})\n\n\tlog.Println(\"Server listening on :8080\")\n\tif err := http.ListenAndServe(\":8080\", nil); err != nil {\n\t\tlog.Fatalf(\"Server error: %v\\n\", err)\n\t}\n}\n```\n\n### Async operations\n\n**Awaiting a promise**\n\n```go\nctx.SetAsyncFunc(\"asyncFunction\", func(this *qjs.This) {\n\tgo func() {\n\t\ttime.Sleep(100 * time.Millisecond)\n\t\tresult := this.Context().NewString(\"Async result from Go!\")\n\t\tthis.Promise().Resolve(result)\n\t}()\n})\n\nresult, err := ctx.Eval(\"test.js\", qjs.Code(`\nasync function main() {\n\tconst result = await asyncFunction();\n\treturn result;\n}\n({ main: main() });\n`))\n\nif err != nil {\n\tlog.Fatal(\"Eval error:\", err)\n}\ndefer result.Free()\n\nmainFunc := result.GetPropertyStr(\"main\")\n\n// Wait for the promise to resolve\nval, err := mainFunc.Await()\nif err != nil {\n\tlog.Fatal(\"Await error:\", err)\n}\n\n// Output: Async result from Go!\nlog.Println(\"Awaited value:\", val.String())\n```\n\n**Top level await**\n\n```go\n// asyncFunction is already defined above\nresult, err := ctx.Eval(\"test.js\", qjs.Code(`\n\tasync function main() {\n\t\tconst result = await asyncFunction();\n\t\treturn result;\n\t}\n\tawait main()\n`), qjs.FlagAsync())\n\nif err != nil {\n\tlog.Fatal(\"Eval error:\", err)\n}\n\ndefer result.Free()\nlog.Println(result.String())\n```\n\n### Call JS function from Go\n\n```go\n// Call JS function from Go\nresult, err := ctx.Eval(\"test.js\", qjs.Code(`\n\tfunction add(a, b) {\n\t\treturn a + b;\n\t}\n\n\tfunction errorFunc() {\n\t\tthrow new Error(\"test error\");\n\t}\n\n\t({\n\t\taddFunc: add,\n\t\terrorFunc: errorFunc\n\t});\n`))\n\nif err != nil {\n\tlog.Fatal(\"Eval error:\", err)\n}\ndefer result.Free()\n\njsAddFunc := result.GetPropertyStr(\"addFunc\")\ndefer jsAddFunc.Free()\n\ngoAddFunc, err := qjs.JsFuncToGo[func(int, int) (int, error)](jsAddFunc)\nif err != nil {\n\tlog.Fatal(\"Func conversion error:\", err)\n}\n\ntotal, err := goAddFunc(1, 2)\nif err != nil {\n\tlog.Fatal(\"Func execution error:\", err)\n}\n\n// Output: 3\nlog.Println(\"Addition result:\", total)\n\njsErrorFunc := result.GetPropertyStr(\"errorFunc\")\ndefer jsErrorFunc.Free()\n\ngoErrorFunc, err := qjs.JsFuncToGo[func() (any, error)](jsErrorFunc)\nif err != nil {\n\tlog.Fatal(\"Func conversion error:\", err)\n}\n\n_, err = goErrorFunc()\nif err != nil {\n\t// Output:\n\t// JS function execution failed: Error: test error\n  //  at errorFunc (test.js:7:13)\n\tlog.Println(err.Error())\n}\n```\n\n### ES Modules\n\n```go\n// Load a utility module\nif _, err = ctx.Load(\"math-utils.js\", qjs.Code(`\n\texport function add(a, b) {\n\t\treturn a + b;\n\t}\n\n\texport function multiply(a, b) {\n\t\treturn a * b;\n\t}\n\n\texport function power(base, exponent) {\n\t\treturn Math.pow(base, exponent);\n\t}\n\n\texport const PI = 3.14159;\n\texport const E = 2.71828;\n\texport default {\n\t\tadd,\n\t\tmultiply,\n\t\tpower,\n\t\tPI,\n\t\tE\n\t};\n`)); err != nil {\n\tlog.Fatal(\"Module load error:\", err)\n}\n\n// Use the module\nresult, err := ctx.Eval(\"use-math.js\", qjs.Code(`\n\timport mathUtils, { add, multiply, power, PI } from 'math-utils.js';\n\n\tconst calculations = {\n\t\taddition: add(10, 20),\n\t\tmultiplication: multiply(6, 7),\n\t\tpower: power(2, 8),\n\t\tcircleArea: PI * power(5, 2),\n\t\tdefaultAdd: mathUtils.add(10, 20)\n\t};\n\n\texport default calculations;\n`), qjs.TypeModule())\n\nif err != nil {\n\tlog.Fatal(\"Module eval error:\", err)\n}\n\n// Output:\n// Addition: 30\n// Multiplication: 42\n// Power: 256\n// Circle Area: 78.54\n// Default Add: 30\nfmt.Printf(\"Addition: %d\\n\", result.GetPropertyStr(\"addition\").Int32())\nfmt.Printf(\"Multiplication: %.0f\\n\", result.GetPropertyStr(\"multiplication\").Float64())\nfmt.Printf(\"Power: %.0f\\n\", result.GetPropertyStr(\"power\").Float64())\nfmt.Printf(\"Circle Area: %.2f\\n\", result.GetPropertyStr(\"circleArea\").Float64())\nfmt.Printf(\"Default Add: %.d\\n\", result.GetPropertyStr(\"defaultAdd\").Int32())\nresult.Free()\n```\n\n### Bytecode Compilation\n\n```go\nscript := `\n\tfunction fibonacci(n) {\n\t\tif (n \u003c= 1) return n;\n\t\treturn fibonacci(n - 1) + fibonacci(n - 2);\n\t}\n\n\tfunction factorial(n) {\n\t\treturn n \u003c= 1 ? 1 : n * factorial(n - 1);\n\t}\n\n\tconst result = {\n\t\tfib10: fibonacci(10),\n\t\tfact5: factorial(5),\n\t\ttimestamp: Date.now()\n\t};\n\n\tresult;\n`\n\n// Compile the script to bytecode\nbytecode, err := ctx.Compile(\"math-functions.js\", qjs.Code(script))\nif err != nil {\n\tlog.Fatal(\"Compilation error:\", err)\n}\n\nfmt.Printf(\"Bytecode size: %d bytes\\n\", len(bytecode))\n\n// Execute the compiled bytecode\nresult, err := ctx.Eval(\"compiled-math.js\", qjs.Bytecode(bytecode))\nif err != nil {\n\tlog.Fatal(\"Bytecode execution error:\", err)\n}\n\nfmt.Printf(\"Fibonacci(10): %d\\n\", result.GetPropertyStr(\"fib10\").Int32())\nfmt.Printf(\"Factorial(5): %d\\n\", result.GetPropertyStr(\"fact5\").Int32())\nresult.Free()\n```\n\n### ProxyValue Support\n\nProxyValue is a feature that allows you to pass Go values directly to JavaScript without full serialization, enabling efficient sharing of complex objects, functions, and resources.\n\nProxyValue creates a lightweight JavaScript wrapper around Go values, storing only a reference ID rather than copying the entire value. This is particularly useful for **pass-through scenarios** where JavaScript receives a Go value and passes it back to Go without needing to access its contents.\n\nKey benefits:\n- **Zero-copy data sharing** - no serialization/deserialization overhead.\n- **Pass-through efficiency** - JavaScript can hold and return Go values without conversion.\n- **Type preservation** - original Go types are maintained across boundaries.\n- **Resource efficiency** - perfect for objects like `context.Context`, database connections, or large structs.\n\n#### Basic ProxyValue Usage\n\n```go\n// Create a Go function that accepts context and a number\ngoFuncWithContext := func(c context.Context, num int) int {\n\t// Access context values in Go\n\tlog.Println(\"Context value:\", c.Value(\"key\"))\n\treturn num * 2\n}\n\n// Convert Go function to JavaScript function\njsFuncWithContext, err := qjs.ToJSValue(ctx, goFuncWithContext)\nif err != nil {\n\tlog.Fatal(\"Func conversion error:\", err)\n}\ndefer jsFuncWithContext.Free()\nctx.Global().SetPropertyStr(\"funcWithContext\", jsFuncWithContext)\n\n// Create a helper function that returns a ProxyValue\nctx.SetFunc(\"$context\", func(this *qjs.This) (*qjs.Value, error) {\n\t// Create context as ProxyValue - JavaScript will never access its contents\n\tpassContext := context.WithValue(context.Background(), \"key\", \"value123\")\n\tval := ctx.NewProxyValue(passContext)\n\treturn val, nil\n})\n\n// JavaScript gets context as ProxyValue and passes it to Go function\nresult, err := ctx.Eval(\"test.js\", qjs.Code(`\n\tfuncWithContext($context(), 10);\n`))\nif err != nil {\n\tlog.Fatal(\"Eval error:\", err)\n}\ndefer result.Free()\n\n// Output: 20\nlog.Println(\"Result:\", result.Int32())\n```\n\n### GO-JS Conversion\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/fastschema/qjs\"\n)\n\ntype Post struct {\n\tID     int    `json:\"id\"`\n\tName   string `json:\"name\"`\n\tAuthor User   `json:\"author\"`\n}\n\ntype User struct {\n\tID   int    `json:\"id\"`\n\tName string `json:\"name\"`\n\tAge  int    `json:\"age\"`\n}\n\n// Method on User struct\nfunc (u User) GetDisplayName() string {\n\treturn fmt.Sprintf(\"%s (%d)\", u.Name, u.Age)\n}\n\nfunc (u User) IsAdult() bool {\n\treturn u.Age \u003e= 18\n}\n\nfunc main() {\n\trt, err := qjs.New()\n\tif err != nil {\n\t\tlog.Fatalf(\"Failed to create QuickJS runtime: %v\", err)\n\t}\n\tdefer rt.Close()\n\tctx := rt.Context()\n\n\tctx.Global().SetPropertyStr(\"goInt\", ctx.NewInt32(55))\n\tctx.Global().SetPropertyStr(\"goString\", ctx.NewString(\"Hello, World!\"))\n\tjsUser, err := qjs.ToJSValue(ctx, User{ID: 1, Name: \"Alice\", Age: 25})\n\tif err != nil {\n\t\tlog.Fatalf(\"Failed to convert User to JS value: %v\", err)\n\t}\n\tctx.Global().SetPropertyStr(\"goUser\", jsUser)\n\n\tresult, err := ctx.Eval(\"test.js\", qjs.Code(`\n\t\tconst post = {\n\t\t\tid: goInt,\n\t\t\tname: goString,\n\t\t\tauthor: goUser,\n\t\t\tdisplayName: goUser.GetDisplayName(),\n\t\t\tisAdult: goUser.IsAdult()\n\t\t};\n\t\tpost;\n\t`))\n\tif err != nil {\n\t\tlog.Fatalf(\"Failed to evaluate JS code: %v\", err)\n\t}\n\tdefer result.Free()\n\n\tgoPost, err := qjs.JsValueToGo[Post](result)\n\tif err != nil {\n\t\tlog.Fatalf(\"Failed to convert JS value to Post: %v\", err)\n\t}\n\n\t// Output:\n\t// Post ID: 55\n\t// Post Name: Hello, World!\n\t// Author ID: 1\n\t// Author Name: Alice\n\t// Author Age: 25\n\t// Author Display Name: Alice (25)\n\t// Author Is Adult: true\n\tlog.Printf(\"Post ID: %d\\n\", goPost.ID)\n\tlog.Printf(\"Post Name: %s\\n\", goPost.Name)\n\tlog.Printf(\"Author ID: %d\\n\", goPost.Author.ID)\n\tlog.Printf(\"Author Name: %s\\n\", goPost.Author.Name)\n\tlog.Printf(\"Author Age: %d\\n\", goPost.Author.Age)\n\tlog.Printf(\"Author Display Name: %s\\n\", goPost.Author.GetDisplayName())\n\tlog.Printf(\"Author Is Adult: %t\\n\", goPost.Author.IsAdult())\n}\n```\n\n### Pool\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"sync\"\n\n\t\"github.com/fastschema/qjs\"\n)\n\nfunc main() {\n\tsetupFunc := func(rt *qjs.Runtime) error {\n\t\tctx := rt.Context()\n\t\tctx.Eval(\"setup.js\", qjs.Code(`\n\t\t\tfunction getMessage(workerId, taskId) {\n\t\t\t\treturn \"Hello from pooled runtime: \" + workerId + \"-\" + taskId;\n\t\t\t}\n\t\t`))\n\t\treturn nil\n\t}\n\t// Create a pool with 3 runtimes\n\tpool := qjs.NewPool(3, \u0026qjs.Option{}, setupFunc)\n\tnumWorkers := 5\n\tnumTasks := 3\n\tvar wg sync.WaitGroup\n\n\tfor i := range numWorkers {\n\t\twg.Add(1)\n\t\tgo func(workerID int) {\n\t\t\tdefer wg.Done()\n\t\t\tfor j := 0; j \u003c numTasks; j++ {\n\t\t\t\trt, err := pool.Get()\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Fatalf(\"Failed to get runtime from pool: %v\", err)\n\t\t\t\t}\n\t\t\t\tdefer pool.Put(rt)\n\t\t\t\tctx := rt.Context()\n\t\t\t\tworkerIdValue := ctx.NewInt32(int32(workerID))\n\t\t\t\ttaskIdValue := ctx.NewInt32(int32(j))\n\t\t\t\tctx.Global().SetPropertyStr(\"workerID\", workerIdValue)\n\t\t\t\tctx.Global().SetPropertyStr(\"taskID\", taskIdValue)\n\n\t\t\t\t// Use the runtime\n\t\t\t\tresult, err := ctx.Eval(\"pool-test.js\", qjs.Code(`\n\t\t\t\t\t({\n\t\t\t\t\t\tmessage: getMessage(workerID, taskID),\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t});\n\t\t\t\t`))\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Fatalf(\"JS execution error: %v\", err)\n\t\t\t\t}\n\t\t\t\tdefer result.Free()\n\t\t\t\tlog.Println(result.GetPropertyStr(\"message\").String())\n\t\t\t}\n\t\t}(i)\n\t}\n\twg.Wait()\n}\n```\n\n## Installation\n\n```bash\ngo get github.com/fastschema/qjs\n```\n\n\n```go\nimport \"github.com/fastschema/qjs\"\n```\n\n**Compatible with Go 1.22.0+**\n\n## Architecture\n\n```\n┌─────────────┐      ┌─────────────┐      ┌─────────────┐\n│   Your Go   │      │   Wazero    │      │   QuickJS   │\n│ Application │ ---\u003e │ WebAssembly │ ---\u003e │ JavaScript  │\n│             │      │  Runtime    │      │   Engine    │\n└─────────────┘      └─────────────┘      └─────────────┘\n       ^                    ^                     ^\n       │                    │                     │\n   Structured           Sandboxed              ES2023\n      Data              Execution            JavaScript\n       │                    │                     │\n       └────────────────────┴─────────────────────┘\n                           QJS\n```\n\n## API Reference\n\n### Core Types\n\n| Type | Description |\n|------|-------------|\n| `Runtime` | Main JavaScript runtime instance |\n| `Context` | JavaScript execution context |\n| `Value` | JavaScript value wrapper |\n| `Pool` | Runtime pool for performance |\n| `ProxyRegistry` | Thread-safe registry for ProxyValue objects |\n\n### Key Methods\n\n```go\n// Runtime Management\nrt, err := qjs.New(options...)           // Create runtime\nrt.Close()                               // Cleanup runtime\nctx.Eval(filename, code, flags...)        // Execute JavaScript\nrt.Load(filename, code)                  // Load module\nrt.Compile(filename, code)               // Compile to bytecode\n...\n\n// Context Operations  \nctx := ctx                      // Get context\nctx.Global()                             // Access global object\nctx.SetFunc(name, fn)                    // Bind Go function\nctx.SetAsyncFunc(name, fn)               // Bind async function\nctx.NewString(s)                         // Create JS string\nctx.NewObject()                          // Create JS object\nctx.NewProxyValue(v)                     // Create ProxyValue from Go value\n...\n\n// Value Operations\nvalue.String()                           // Convert to Go string\nvalue.Int32()                            // Convert to Go int32\nvalue.Bool()                             // Convert to Go bool\nvalue.GetPropertyStr(name)               // Get object property\nvalue.SetPropertyStr(name, val)          // Set object property\nvalue.IsQJSProxyValue()                  // Check if value is a ProxyValue\nvalue.Free()                             // Release memory\n...\n\n// ProxyValue Operations\nqjs.JsValueToGo[T](value)               // Extract Go value from ProxyValue\nqjs.ToJSValue(ctx, goValue)             // Convert Go value to JS (auto-detects ProxyValue need)\n...\n```\n\n### Configuration Options\n\n```go\ntype Option struct {\n    CWD                string  // Working directory\n    MaxStackSize       int     // Stack size limit\n    MemoryLimit        int     // Memory usage limit  \n    MaxExecutionTime   int     // Execution timeout\n    GCThreshold        int     // GC trigger threshold\n    CacheDir           string  // Compilation cache directory\n}\n```\n\n## Performance \u0026 Security\n\n**Optimization Tips:**\n1. Use runtime pools for concurrent applications.\n2. Compile frequently-used scripts to bytecode.\n3. Use ProxyValue for large objects or shared state to avoid serialization overhead.\n4. Minimize small object conversions between Go and JS - prefer ProxyValue for complex types.\n5. Set appropriate memory limits.\n\n**Security**\n\n- **Complete filesystem isolation** (unless explicitly configured).\n- **No network access** from JavaScript (unless explicitly allowed).\n- **Memory safe** - no buffer overflows.\n- **No CGO attack surface**.\n- **Deterministic resource cleanup**.\n\n### Memory Management\n\n**Critical Rules:**\n- Always call `result.Free()` on JavaScript values.\n- Always call `rt.Close()` when done with runtime.\n- Don't free functions registered to global object.\n- Don't free object properties directly – free the entire object.\n\n```go\n// Correct pattern\nresult, err := ctx.Eval(\"script.js\", code)\nif err != nil {\n  return err\n}\n\n// Always free values\ndefer result.Free()\n```\n\n**Choose QJS when you need:**\n- Secure modern JavaScript features.\n- Single dependency (Wazero), no CGO.\n- Supports plugin systems and user-generated code.\n- Compliant with strict security requirements.\n- High performance with low memory footprint.\n\n## Building from Source\n\n### Prerequisites\n\n- Go 1.23.0+\n- WASI SDK (for WebAssembly compilation)\n- CMake 3.16+\n- Make\n\n### Quick Build\n\n**Development Setup:**\n\n```bash\n# Clone with submodules\ngit clone --recursive https://github.com/fastschema/qjs.git\ncd qjs\n\n# Install WASI SDK (Linux/macOS)\ncurl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sdk-20.0-linux.tar.gz | tar xz\nsudo mv wasi-sdk-20.0 /opt/wasi-sdk\n\n# Build WebAssembly module\nmake build\n\n# Run tests\ngo test ./...\n```\n\n**Code Standards:**\n- Follow standard Go conventions (`gofmt`, `golangci-lint`).\n- Add tests for new features.\n- Update documentation for API changes.\n- Keep commit messages clear and descriptive.\n\n## Contributing\n\nWe'd love your help making QJS better! Here's how:\n\n1. **Found a bug?** [Open an issue](https://github.com/fastschema/qjs/issues).\n2. **Want a feature?** Start a discussion.\n3. **Ready to code?** Fork, branch, test, and submit a PR.\n4. **Review PRs** - help review and test contributions.\n5. **Star the repo** - it helps us grow!\n\n## Support \u0026 Community\n\n- **Documentation**: [GoDoc](https://godoc.org/github.com/fastschema/qjs)\n- **Issues**: [GitHub Issues](https://github.com/fastschema/qjs/issues)\n- **Discussions**: [GitHub Discussions](https://github.com/fastschema/qjs/discussions)\n\n**Getting Help:**\n1. Check existing issues and documentation.\n2. Create a minimal reproduction case.\n3. Include Go version, OS, and QJS version.\n4. Be specific about expected vs actual behavior.\n\n## Roadmap\nPlanned features and improvements:\n- Enhanced ProxyValue capabilities.\n- Improved GO-JS type conversions.\n- More examples and documentation.\n- Performance optimizations.\n- Node.js-like standard library.\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file.\n\n## Acknowledgments\n\nBuilt on the shoulders of giants:\n\n- **[QuickJS](https://bellard.org/quickjs/)** by Fabrice Bellard - The elegant JavaScript engine.\n- **[Wazero](https://wazero.io/)** - Pure Go WebAssembly runtime.\n- **[QuickJS-NG](https://github.com/quickjs-ng/quickjs)** - Maintained QuickJS fork.\n\n---\n\n**Ready to run JavaScript safely in your Go apps?**\n\n```bash\ngo get github.com/fastschema/qjs\n```\n\n**Questions? Ideas? Contributions?** We're here to help → [Start a discussion](https://github.com/fastschema/qjs/discussions)\n","funding_links":[],"categories":["Go"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffastschema%2Fqjs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffastschema%2Fqjs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffastschema%2Fqjs/lists"}