{"id":40375095,"url":"https://github.com/buke/quickjs-go","last_synced_at":"2026-01-20T11:34:41.499Z","repository":{"id":37439239,"uuid":"501546390","full_name":"buke/quickjs-go","owner":"buke","description":"Go bindings to QuickJS","archived":false,"fork":false,"pushed_at":"2025-12-31T03:58:15.000Z","size":516170,"stargazers_count":152,"open_issues_count":3,"forks_count":22,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-01-03T05:15:45.811Z","etag":null,"topics":["golang-library","interpreter","javascript","quickjs"],"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/buke.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog","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":"2022-06-09T07:19:53.000Z","updated_at":"2026-01-02T07:59:54.000Z","dependencies_parsed_at":"2024-03-08T11:29:03.106Z","dependency_job_id":"a2855af9-f21f-432c-8182-6bfe8d43b660","html_url":"https://github.com/buke/quickjs-go","commit_stats":null,"previous_names":[],"tags_count":74,"template":false,"template_full_name":null,"purl":"pkg:github/buke/quickjs-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buke%2Fquickjs-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buke%2Fquickjs-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buke%2Fquickjs-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buke%2Fquickjs-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/buke","download_url":"https://codeload.github.com/buke/quickjs-go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buke%2Fquickjs-go/sbom","scorecard":{"id":257873,"data":{"date":"2025-08-11","repo":{"name":"github.com/buke/quickjs-go","commit":"61f947409cbed655444ececfc876b704f709b8dd"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/15 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":10,"reason":"30 commit(s) and 12 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":0,"reason":"dangerous workflow patterns detected","details":["Warn: untrusted code checkout '${{ github.event.pull_request.head.sha }}': .github/workflows/test.yml:20"],"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql.yml:28","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql.yml:29","Warn: no topLevel permission defined: .github/workflows/build_static_lib.yml:1","Warn: no topLevel permission defined: .github/workflows/codeql.yml:1","Info: topLevel 'contents' permission set to 'read': .github/workflows/dependency-review.yml:11","Warn: no topLevel permission defined: .github/workflows/test.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":4,"reason":"binaries present in source code","details":["Warn: binary detected: deps/libs/darwin_amd64/libquickjs.a:1","Warn: binary detected: deps/libs/darwin_arm64/libquickjs.a:1","Warn: binary detected: deps/libs/linux_amd64/libquickjs.a:1","Warn: binary detected: deps/libs/linux_arm64/libquickjs.a:1","Warn: binary detected: deps/libs/windows_386/libquickjs.a:1","Warn: binary detected: deps/libs/windows_amd64/libquickjs.a:1"],"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:48: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:52: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:72: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:83: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:105: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:116: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:138: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:149: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:154: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:174: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:185: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:190: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:209: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:220: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:228: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build_static_lib.yml:37: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/build_static_lib.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:41: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:45: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:59: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:72: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/dependency-review.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/dependency-review.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/dependency-review.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/dependency-review.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/test.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/test.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/test.yml:54: update your workflow using https://app.stepsecurity.io/secureworkflow/buke/quickjs-go/test.yml/main?enable=pin","Info:   0 out of  15 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of  12 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":8,"reason":"SAST tool detected but not run on all commits","details":["Info: SAST configuration detected: CodeQL","Warn: 15 commits out of 26 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-17T10:05:55.214Z","repository_id":37439239,"created_at":"2025-08-17T10:05:55.215Z","updated_at":"2025-08-17T10:05:55.215Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28602485,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T10:46:13.255Z","status":"ssl_error","status_checked_at":"2026-01-20T10:42:51.865Z","response_time":117,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["golang-library","interpreter","javascript","quickjs"],"created_at":"2026-01-20T11:34:41.373Z","updated_at":"2026-01-20T11:34:41.480Z","avatar_url":"https://github.com/buke.png","language":"Go","readme":"# quickjs-go\n\nEnglish | [简体中文](README_zh-cn.md)\n\n[![Test](https://github.com/buke/quickjs-go/workflows/Test/badge.svg)](https://github.com/buke/quickjs-go/actions?query=workflow%3ATest)\n[![codecov](https://codecov.io/gh/buke/quickjs-go/graph/badge.svg?token=8z6vgOaIIS)](https://codecov.io/gh/buke/quickjs-go)\n[![Go Report Card](https://goreportcard.com/badge/github.com/buke/quickjs-go)](https://goreportcard.com/report/github.com/buke/quickjs-go)\n[![GoDoc](https://pkg.go.dev/badge/github.com/buke/quickjs-go?status.svg)](https://pkg.go.dev/github.com/buke/quickjs-go?tab=doc)\n[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fbuke%2Fquickjs-go.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fbuke%2Fquickjs-go?ref=badge_shield)\n\nGo bindings to QuickJS: a fast, small, and embeddable ES2020 JavaScript interpreter.\n\n**⚠️ This project is not ready for production use yet. Use at your own risk. APIs may change without notice.**\n\n## Features\n\n- Evaluate script\n- Compile script into bytecode and Eval from bytecode\n- Operate JavaScript values and objects in Go\n- Bind Go function to JavaScript async/sync function\n- Simple exception throwing and catching\n- **Marshal/Unmarshal Go values to/from JavaScript values**\n- **Full TypedArray support (Int8Array, Uint8Array, Float32Array, etc.)**\n- **Create JavaScript Classes from Go with ClassBuilder**\n- **Create JavaScript Modules from Go with ModuleBuilder*o\n- **Cross-platform:** Prebuilt QuickJS static libraries for Linux (x64/arm64), Windows (x64/x86), MacOS (x64/arm64).  \n  *(See [deps/libs](deps/libs) for details. For Windows build tips, see: https://github.com/buke/quickjs-go/issues/151#issuecomment-2134307728)*\n\n\n## Guidelines\n\n### Error Handling\n- Use `Value.IsException()` or `Context.HasException()` to check for exceptions\n- Use `Context.Exception()` to get the exception as a Go error\n- Always call `defer value.Free()` for returned values to prevent memory leaks\n- Check `Context.HasException()` after operations that might throw\n\n### Memory Management\n- Call  `value.Free()` for `*Value` objects you create or receive. QuickJS uses reference counting for memory management, so if a value is referenced by other objects, you only need to ensure the referencing objects are properly freed.\n- Runtime and Context objects have their own cleanup methods (`Close()`). Close them once you are done using them.\n- Use `runtime.SetFinalizer()` cautiously as it may interfere with QuickJS's GC.\n\n### Performance Tips\n- QuickJS is not thread-safe. For concurrency or isolation, use a thread pool pattern with pre-initialized runtimes, or manage separate Runtime/Context instances for different tasks or users (such as : [https://github.com/buke/js-executor](https://github.com/buke/js-executor)).\n- Reuse Runtime and Context objects when possible.\n- Avoid frequent conversion between Go and JS values.\n- Consider using bytecode compilation for frequently executed scripts.\n\n### Best Practices\n- Use appropriate `EvalOptions` for different script types.\n- Handle both JavaScript exceptions and Go errors appropriately.\n- Test memory usage under load to prevent leaks.\n\n\n## Usage\n\n```go\nimport \"github.com/buke/quickjs-go\"\n```\n\n### Run a script\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/buke/quickjs-go\"\n)\n\nfunc main() {\n    // Create a new runtime\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n\n    // Create a new context\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    ret := ctx.Eval(\"'Hello ' + 'QuickJS!'\")\n    defer ret.Free()\n    \n    if ret.IsException() {\n        err := ctx.Exception()\n        println(err.Error())\n        return\n    }\n    \n    fmt.Println(ret.ToString())\n}\n```\n\n### Get/Set Javascript Object\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/buke/quickjs-go\"\n)\n\nfunc main() {\n    // Create a new runtime\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n\n    // Create a new context\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    test := ctx.NewObject()\n    test.Set(\"A\", ctx.NewString(\"String A\"))\n    test.Set(\"B\", ctx.NewString(\"String B\"))\n    test.Set(\"C\", ctx.NewString(\"String C\"))\n    ctx.Globals().Set(\"test\", test)\n\n    ret := ctx.Eval(`Object.keys(test).map(key =\u003e test[key]).join(\" \")`)\n    defer ret.Free()\n    \n    if ret.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n    \n    fmt.Println(ret.ToString())\n}\n\n```\n\n### Bind Go Funtion to Javascript async/sync function\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"time\"\n\n    \"github.com/buke/quickjs-go\"\n)\n\nfunc main() {\n    // Create a new runtime\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n\n    // Create a new context\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    // Create a new object\n    test := ctx.NewObject()\n    // bind properties to the object\n    test.Set(\"A\", ctx.NewString(\"String A\"))\n    test.Set(\"B\", ctx.NewInt32(0))\n    test.Set(\"C\", ctx.NewBool(false))\n    // bind go function to js object (sync)\n    test.Set(\"hello\", ctx.NewFunction(func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {\n        return ctx.NewString(\"Hello \" + args[0].ToString())\n    }))\n\n    // bind \"test\" object to global object\n    ctx.Globals().Set(\"test\", test)\n\n    // call js function by js (sync)\n    jsRet := ctx.Eval(`test.hello(\"Javascript!\")`)\n    defer jsRet.Free()\n    if jsRet.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n    fmt.Println(jsRet.ToString())\n\n    // call js function by go (sync)\n    goRet := test.Call(\"hello\", ctx.NewString(\"Golang!\"))\n    defer goRet.Free()\n    if goRet.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n    fmt.Println(goRet.ToString())\n\n    // --- Sync Promise example (immediate resolve/reject, no goroutine) ---\n    ctx.Globals().Set(\"syncPromise\", ctx.NewFunction(func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {\n        return ctx.NewPromise(func(resolve, reject func(*quickjs.Value)) {\n            // You can resolve the Promise synchronously:\n            msg := ctx.NewString(\"Hello from sync Promise\")\n            defer msg.Free()\n            resolve(msg)\n\n            // Or reject synchronously instead (example):\n            // errVal := ctx.NewString(\"sync error\")\n            // defer errVal.Free()\n            // reject(errVal)\n        })\n    }))\n\n    syncPromise := ctx.Eval(`syncPromise()`)\n    defer syncPromise.Free()\n\n    syncRet := ctx.Await(syncPromise)\n    defer syncRet.Free()\n    if syncRet.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n    fmt.Println(syncRet.ToString())\n\n    // --- Async binding example using Promise + scheduler ---\n    ctx.Globals().Set(\"testAsync\", ctx.NewFunction(func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {\n        return ctx.NewPromise(func(resolve, reject func(*quickjs.Value)) {\n            // Heavy or blocking work can happen in a goroutine.\n            go func() {\n                time.Sleep(10 * time.Millisecond)\n\n                // But QuickJS/Context APIs must be used back on\n                // the Context's own thread via ctx.Schedule.\n                ctx.Schedule(func(inner *quickjs.Context) {\n                    value := inner.NewString(\"Hello Async Function!\")\n                    defer value.Free()\n                    resolve(value)\n                })\n            }()\n        })\n    }))\n\n    // From JS side, this is just a normal Promise-returning function.\n    promiseResult := ctx.Eval(`testAsync()`)\n    defer promiseResult.Free()\n\n    // ctx.Await drives the QuickJS pending-job queue and the\n    // context-level scheduler, so you usually don't need ctx.Loop()\n    // if you only care about the Promise result.\n    asyncRet := ctx.Await(promiseResult)\n    defer asyncRet.Free()\n\n    if asyncRet.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n\n    fmt.Println(asyncRet.ToString())\n\n    // Output:\n    // Hello Javascript!\n    // Hello Golang!\n    // Hello Async Function!\n}\n\n// NOTE:\n// - Goroutines must NOT call QuickJS/Context APIs directly.\n// - Always schedule back to the Context thread via ctx.Schedule\n//   before creating values or resolving/rejecting Promises.\n// - ctx.Await will internally drive pending jobs and the\n//   scheduler until the Promise settles.\n```\n\n### Error Handling\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"errors\"\n\n    \"github.com/buke/quickjs-go\"\n)\n\nfunc main() {\n    // Create a new runtime\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n\n    // Create a new context\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    ctx.Globals().SetFunction(\"A\", func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {\n        // raise error\n        return ctx.ThrowError(errors.New(\"expected error\"))\n    })\n\n    result := ctx.Eval(\"A()\")\n    defer result.Free()\n    \n    if result.IsException() {\n        actual := ctx.Exception()\n        fmt.Println(actual.Error())\n    }\n}\n```\n\n### TypedArray Support\n\nQuickJS-Go provides support for JavaScript TypedArrays, enabling binary data processing between Go and JavaScript.\n\n#### Creating TypedArrays from Go\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/buke/quickjs-go\"\n)\n\nfunc main() {\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    // Create various TypedArrays from Go slices\n    int8Data := []int8{-128, -1, 0, 1, 127}\n    int8Array := ctx.NewInt8Array(int8Data)\n\n    uint8Data := []uint8{0, 128, 255}\n    uint8Array := ctx.NewUint8Array(uint8Data)\n\n    float32Data := []float32{-3.14, 0.0, 2.718, 100.5}\n    float32Array := ctx.NewFloat32Array(float32Data)\n\n    int64Data := []int64{-9223372036854775808, 0, 9223372036854775807}\n    bigInt64Array := ctx.NewBigInt64Array(int64Data)\n\n    // Set TypedArrays as global variables\n    ctx.Globals().Set(\"int8Array\", int8Array)\n    ctx.Globals().Set(\"uint8Array\", uint8Array)\n    ctx.Globals().Set(\"float32Array\", float32Array)\n    ctx.Globals().Set(\"bigInt64Array\", bigInt64Array)\n\n    // Use in JavaScript\n    result := ctx.Eval(`\n        // Check types\n        const results = {\n            int8Type: int8Array instanceof Int8Array,\n            uint8Type: uint8Array instanceof Uint8Array,\n            float32Type: float32Array instanceof Float32Array,\n            bigInt64Type: bigInt64Array instanceof BigInt64Array,\n            // Calculate sum of float32 array\n            float32Sum: float32Array.reduce((sum, val) =\u003e sum + val, 0)\n        };\n        results;\n    `)\n    defer result.Free()\n\n    if result.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n\n    fmt.Println(\"Results:\", result.JSONStringify())\n}\n```\n\n#### Converting JavaScript TypedArrays to Go\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/buke/quickjs-go\"\n)\n\nfunc main() {\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    // Create TypedArrays in JavaScript\n    jsTypedArrays := ctx.Eval(`\n        ({\n            int8: new Int8Array([-128, -1, 0, 1, 127]),\n            uint16: new Uint16Array([0, 32768, 65535]),\n            float64: new Float64Array([Math.PI, Math.E, 42.5]),\n            bigUint64: new BigUint64Array([0n, 18446744073709551615n])\n        })\n    `)\n    defer jsTypedArrays.Free()\n\n    if jsTypedArrays.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n\n    // Convert to Go slices\n    int8Array := jsTypedArrays.Get(\"int8\")\n    defer int8Array.Free()\n    if int8Array.IsInt8Array() {\n        goInt8Slice, err := int8Array.ToInt8Array()\n        if err == nil {\n            fmt.Printf(\"Int8Array: %v\\n\", goInt8Slice)\n        }\n    }\n\n    uint16Array := jsTypedArrays.Get(\"uint16\")\n    defer uint16Array.Free()\n    if uint16Array.IsUint16Array() {\n        goUint16Slice, err := uint16Array.ToUint16Array()\n        if err == nil {\n            fmt.Printf(\"Uint16Array: %v\\n\", goUint16Slice)\n        }\n    }\n\n    float64Array := jsTypedArrays.Get(\"float64\")\n    defer float64Array.Free()\n    if float64Array.IsFloat64Array() {\n        goFloat64Slice, err := float64Array.ToFloat64Array()\n        if err == nil {\n            fmt.Printf(\"Float64Array: %v\\n\", goFloat64Slice)\n        }\n    }\n\n    bigUint64Array := jsTypedArrays.Get(\"bigUint64\")\n    defer bigUint64Array.Free()\n    if bigUint64Array.IsBigUint64Array() {\n        goBigUint64Slice, err := bigUint64Array.ToBigUint64Array()\n        if err == nil {\n            fmt.Printf(\"BigUint64Array: %v\\n\", goBigUint64Slice)\n        }\n    }\n}\n```\n\n#### TypedArray Types Support\n\n| Go Type    | JavaScript TypedArray | Context Method          | Value Method        |\n|------------|----------------------|-------------------------|---------------------|\n| `[]int8`   | `Int8Array`          | `ctx.NewInt8Array()`       | `val.ToInt8Array()` |\n| `[]uint8`  | `Uint8Array`         | `ctx.NewUint8Array()`      | `val.ToUint8Array()` |\n| `[]uint8`  | `Uint8ClampedArray`  | `ctx.NewUint8ClampedArray()` | `val.ToUint8Array()` |\n| `[]int16`  | `Int16Array`         | `ctx.NewInt16Array()`      | `val.ToInt16Array()` |\n| `[]uint16` | `Uint16Array`        | `ctx.NewUint16Array()`     | `val.ToUint16Array()` |\n| `[]int32`  | `Int32Array`         | `ctx.NewInt32Array()`      | `val.ToInt32Array()` |\n| `[]uint32` | `Uint32Array`        | `ctx.NewUint32Array()`     | `val.ToUint32Array()` |\n| `[]float32` | `Float32Array`      | `ctx.NewFloat32Array()`    | `val.ToFloat32Array()` |\n| `[]float64` | `Float64Array`      | `ctx.NewFloat64Array()`    | `val.ToFloat64Array()` |\n| `[]int64`  | `BigInt64Array`      | `ctx.NewBigInt64Array()`   | `val.ToBigInt64Array()` |\n| `[]uint64` | `BigUint64Array`     | `ctx.NewBigUint64Array()`  | `val.ToBigUint64Array()` |\n| `[]byte`   | `ArrayBuffer`        | `ctx.NewArrayBuffer()`     | `val.ToByteArray()` |\n\n#### TypedArray Detection\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/buke/quickjs-go\"\n)\n\nfunc main() {\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    // Create various arrays\n    regularArray := ctx.Eval(`[1, 2, 3]`)\n    defer regularArray.Free()\n\n    int32Array := ctx.NewInt32Array([]int32{1, 2, 3})\n    float64Array := ctx.NewFloat64Array([]float64{1.1, 2.2, 3.3})\n\n    // Set arrays as global variables to be referenced by globals\n    ctx.Globals().Set(\"int32Array\", int32Array)\n    ctx.Globals().Set(\"float64Array\", float64Array)\n\n    // Detect array types\n    fmt.Printf(\"Regular array IsArray: %v\\n\", regularArray.IsArray())\n    fmt.Printf(\"Regular array IsTypedArray: %v\\n\", regularArray.IsTypedArray())\n\n    fmt.Printf(\"Int32Array IsTypedArray: %v\\n\", int32Array.IsTypedArray())\n    fmt.Printf(\"Int32Array IsInt32Array: %v\\n\", int32Array.IsInt32Array())\n    fmt.Printf(\"Int32Array IsFloat64Array: %v\\n\", int32Array.IsFloat64Array())\n\n    fmt.Printf(\"Float64Array IsTypedArray: %v\\n\", float64Array.IsTypedArray())\n    fmt.Printf(\"Float64Array IsFloat64Array: %v\\n\", float64Array.IsFloat64Array())\n    fmt.Printf(\"Float64Array IsInt32Array: %v\\n\", float64Array.IsInt32Array())\n}\n```\n\n#### Binary Data Processing Example\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/buke/quickjs-go\"\n)\n\nfunc main() {\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    // Process image-like data (simulate RGB pixels)\n    imageData := []uint8{\n        255, 0, 0,    // Red pixel\n        0, 255, 0,    // Green pixel  \n        0, 0, 255,    // Blue pixel\n        255, 255, 0,  // Yellow pixel\n    }\n\n    // Send to JavaScript as Uint8Array\n    imageArray := ctx.NewUint8Array(imageData)\n    ctx.Globals().Set(\"imageData\", imageArray)\n\n    // Process in JavaScript\n    result := ctx.Eval(`\n        // Convert RGB to grayscale\n        const grayscale = new Uint8Array(imageData.length / 3);\n        for (let i = 0; i \u003c imageData.length; i += 3) {\n            const r = imageData[i];\n            const g = imageData[i + 1];\n            const b = imageData[i + 2];\n            grayscale[i / 3] = Math.round(0.299 * r + 0.587 * g + 0.114 * b);\n        }\n        grayscale;\n    `)\n    defer result.Free()\n\n    if result.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n\n    // Convert back to Go\n    if result.IsUint8Array() {\n        grayscaleData, err := result.ToUint8Array()\n        if err == nil {\n            fmt.Printf(\"Original RGB: %v\\n\", imageData)\n            fmt.Printf(\"Grayscale: %v\\n\", grayscaleData)\n        }\n    }\n}\n```\n\n### Marshal/Unmarshal Go Values\n\nQuickJS-Go provides conversion between Go and JavaScript values through the `Marshal` and `Unmarshal` methods.\n\n#### Basic Types\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/buke/quickjs-go\"\n)\n\nfunc main() {\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    // Marshal Go values to JavaScript\n    data := map[string]interface{}{\n        \"name\":    \"John Doe\",\n        \"age\":     30,\n        \"active\":  true,\n        \"scores\":  []int{85, 92, 78},\n        \"address\": map[string]string{\n            \"city\":    \"New York\",\n            \"country\": \"USA\",\n        },\n        // TypedArray will be automatically created for typed slices\n        \"floatData\": []float32{1.1, 2.2, 3.3},\n        \"intData\":   []int32{100, 200, 300},\n        \"byteData\":  []byte{0x48, 0x65, 0x6C, 0x6C, 0x6F}, // \"Hello\" in bytes\n    }\n\n    jsVal, err := ctx.Marshal(data)\n    if err != nil {\n        panic(err)\n    }\n    defer jsVal.Free()\n\n    // Use the marshaled value in JavaScript\n    ctx.Globals().Set(\"user\", jsVal)\n    result := ctx.Eval(`\n        const info = user.name + \" is \" + user.age + \" years old\";\n        const floatArrayType = user.floatData instanceof Float32Array;\n        const intArrayType = user.intData instanceof Int32Array;\n        const byteArrayType = user.byteData instanceof ArrayBuffer;\n        \n        ({\n            info: info,\n            floatArrayType: floatArrayType,\n            intArrayType: intArrayType,\n            byteArrayType: byteArrayType,\n            byteString: new TextDecoder().decode(user.byteData)\n        });\n    `)\n    defer result.Free()\n    \n    if result.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n    \n    fmt.Println(\"Result:\", result.JSONStringify())\n\n    // Unmarshal JavaScript values back to Go\n    var userData map[string]interface{}\n    err = ctx.Unmarshal(jsVal, \u0026userData)\n    if err != nil {\n        panic(err)\n    }\n    fmt.Printf(\"Unmarshaled: %+v\\n\", userData)\n}\n```\n\n#### Struct Marshaling with Tags\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"time\"\n    \"github.com/buke/quickjs-go\"\n)\n\ntype User struct {\n    ID        int64     `js:\"id\"`\n    Name      string    `js:\"name\"`\n    Email     string    `json:\"email_address\"`\n    CreatedAt time.Time `js:\"created_at\"`\n    IsActive  bool      `js:\"is_active\"`\n    Tags      []string  `js:\"tags\"`\n    // TypedArray fields\n    Scores    []float32 `js:\"scores\"`    // Will become Float32Array\n    Data      []int32   `js:\"data\"`      // Will become Int32Array\n    Binary    []byte    `js:\"binary\"`    // Will become ArrayBuffer\n    // unexported fields are ignored\n    password  string\n    // Fields with \"-\" tag are skipped\n    Secret    string `js:\"-\"`\n}\n\nfunc main() {\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    user := User{\n        ID:        123,\n        Name:      \"Alice\",\n        Email:     \"alice@example.com\",\n        CreatedAt: time.Now(),\n        IsActive:  true,\n        Tags:      []string{\"admin\", \"user\"},\n        Scores:    []float32{95.5, 87.2, 92.0},\n        Data:      []int32{1000, 2000, 3000},\n        Binary:    []byte{0x41, 0x42, 0x43}, // \"ABC\"\n        password:  \"secret123\",\n        Secret:    \"top-secret\",\n    }\n\n    // Marshal struct to JavaScript\n    jsVal, err := ctx.Marshal(user)\n    if err != nil {\n        panic(err)\n    }\n    defer jsVal.Free()\n\n    // Check TypedArray types in JavaScript\n    ctx.Globals().Set(\"user\", jsVal)\n    result := ctx.Eval(`\n        ({\n            scoresType: user.scores instanceof Float32Array,\n            dataType: user.data instanceof Int32Array,\n            binaryType: user.binary instanceof ArrayBuffer,\n            binaryString: new TextDecoder().decode(user.binary),\n            avgScore: user.scores.reduce((sum, score) =\u003e sum + score) / user.scores.length\n        });\n    `)\n    defer result.Free()\n\n    if result.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n\n    // Modify in JavaScript\n    modifyResult := ctx.Eval(`\n        user.name = \"Alice Smith\";\n        user.tags.push(\"moderator\");\n        // Modify TypedArray data\n        user.scores[0] = 98.5;\n        user;\n    `)\n    defer modifyResult.Free()\n\n    if modifyResult.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n\n    // Unmarshal back to Go struct\n    var updatedUser User\n    err = ctx.Unmarshal(modifyResult, \u0026updatedUser)\n    if err != nil {\n        panic(err)\n    }\n\n    fmt.Printf(\"Updated user: %+v\\n\", updatedUser)\n    // Note: password and Secret fields remain unchanged (not serialized)\n}\n```\n\n#### Custom Marshal/Unmarshal\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"strings\"\n    \"github.com/buke/quickjs-go\"\n)\n\ntype CustomType struct {\n    Value string\n}\n\n// Implement Marshaler interface\nfunc (c CustomType) MarshalJS(ctx *quickjs.Context) (*quickjs.Value, error) {\n    return ctx.NewString(\"custom:\" + c.Value), nil\n}\n\n// Implement Unmarshaler interface\nfunc (c *CustomType) UnmarshalJS(ctx *quickjs.Context, val *quickjs.Value) error {\n    if val.IsString() {\n        str := val.ToString()\n        if strings.HasPrefix(str, \"custom:\") {\n            c.Value = str[7:] // Remove \"custom:\" prefix\n        } else {\n            c.Value = str\n        }\n    }\n    return nil\n}\n\nfunc main() {\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    // Marshal custom type\n    custom := CustomType{Value: \"hello\"}\n    jsVal, err := ctx.Marshal(custom)\n    if err != nil {\n        panic(err)\n    }\n    defer jsVal.Free()\n\n    fmt.Println(\"Marshaled:\", jsVal.ToString()) // Output: custom:hello\n\n    // Unmarshal back\n    var result CustomType\n    err = ctx.Unmarshal(jsVal, \u0026result)\n    if err != nil {\n        panic(err)\n    }\n    fmt.Printf(\"Unmarshaled: %+v\\n\", result) // Output: {Value:hello}\n}\n```\n\n#### Type Mappings\n\n**Go to JavaScript:**\n- `bool` → JavaScript boolean\n- `int`, `int8`, `int16`, `int32` → JavaScript number (32-bit)\n- `int64` → JavaScript number (64-bit)\n- `uint`, `uint8`, `uint16`, `uint32` → JavaScript number (32-bit unsigned)\n- `uint64` → JavaScript BigInt\n- `float32`, `float64` → JavaScript number\n- `string` → JavaScript string\n- `[]byte` → JavaScript ArrayBuffer\n- `[]int8` → JavaScript Int8Array\n- `[]uint8` → JavaScript Uint8Array\n- `[]int16` → JavaScript Int16Array\n- `[]uint16` → JavaScript Uint16Array\n- `[]int32` → JavaScript Int32Array\n- `[]uint32` → JavaScript Uint32Array\n- `[]float32` → JavaScript Float32Array\n- `[]float64` → JavaScript Float64Array\n- `[]int64` → JavaScript BigInt64Array\n- `[]uint64` → JavaScript BigUint64Array\n- `slice/array` → JavaScript Array (for non-typed arrays)\n- `map` → JavaScript Object\n- `struct` → JavaScript Object\n- `pointer` → recursively marshal pointed value (nil becomes null)\n\n**JavaScript to Go:**\n- JavaScript null/undefined → Go nil pointer or zero value\n- JavaScript boolean → Go bool\n- JavaScript number → Go numeric types (with appropriate conversion)\n- JavaScript BigInt → Go `uint64`/`int64`/`*big.Int`\n- JavaScript string → Go string\n- JavaScript Array → Go slice/array\n- JavaScript Object → Go map/struct\n- JavaScript ArrayBuffer → Go `[]byte`\n- JavaScript Int8Array → Go `[]int8`\n- JavaScript Uint8Array/Uint8ClampedArray → Go `[]uint8`\n- JavaScript Int16Array → Go `[]int16`\n- JavaScript Uint16Array → Go `[]uint16`\n- JavaScript Int32Array → Go `[]int32`\n- JavaScript Uint32Array → Go `[]uint32`\n- JavaScript Float32Array → Go `[]float32`\n- JavaScript Float64Array → Go `[]float64`\n- JavaScript BigInt64Array → Go `[]int64`\n- JavaScript BigUint64Array → Go `[]uint64`\n\nWhen unmarshaling into `interface{}`, the following types are used:\n- `nil` for null/undefined\n- `bool` for boolean\n- `int64` for integer numbers\n- `float64` for floating-point numbers\n- `string` for string\n- `[]interface{}` for Array\n- `map[string]interface{}` for Object\n- `*big.Int` for BigInt\n- `[]byte` for ArrayBuffer\n\n### Create JavaScript Modules from Go with ModuleBuilder\n\nThe ModuleBuilder API allows you to create JavaScript modules from Go code, making Go functions, values, and objects available for standard ES6 import syntax in JavaScript applications.\n\n#### Basic Module Creation\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/buke/quickjs-go\"\n)\n\nfunc main() {\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    // Create a math module with Go functions and values\n    addFunc := ctx.NewFunction(func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {\n        if len(args) \u003e= 2 {\n            return ctx.NewFloat64(args[0].ToFloat64() + args[1].ToFloat64())\n        }\n        return ctx.NewFloat64(0)\n    })\n    defer addFunc.Free()\n\n    // Build the module using fluent API\n    module := quickjs.NewModuleBuilder(\"math\").\n        Export(\"PI\", ctx.NewFloat64(3.14159)).\n        Export(\"add\", addFunc).\n        Export(\"version\", ctx.NewString(\"1.0.0\")).\n        Export(\"default\", ctx.NewString(\"Math Module\"))\n\n    err := module.Build(ctx)\n    if err != nil {\n        panic(err)\n    }\n\n    // Use the module in JavaScript with standard ES6 import\n    result := ctx.Eval(`\n        (async function() {\n            // Named imports\n            const { PI, add, version } = await import('math');\n            \n            // Use imported functions and values\n            const sum = add(PI, 1.0);\n            return { sum, version };\n        })()\n    `, quickjs.EvalAwait(true))\n    defer result.Free()\n\n    if result.IsException() {\n        err := ctx.Exception()\n        panic(err)\n    }\n\n    fmt.Println(\"Module result:\", result.JSONStringify())\n    // Output: Module result: {\"sum\":4.14159,\"version\":\"1.0.0\"}\n}\n```\n\n#### Advanced Module Features\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/buke/quickjs-go\"\n)\n\nfunc main() {\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    // Create a utilities module with complex objects\n    config := ctx.NewObject()\n    config.Set(\"appName\", ctx.NewString(\"MyApp\"))\n    config.Set(\"version\", ctx.NewString(\"2.0.0\"))\n    config.Set(\"debug\", ctx.NewBool(true))\n\n    greetFunc := ctx.NewFunction(func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {\n        name := \"World\"\n        if len(args) \u003e 0 {\n            name = args[0].ToString()\n        }\n        return ctx.NewString(fmt.Sprintf(\"Hello, %s!\", name))\n    })\n    defer greetFunc.Free()\n\n    jsonVal := ctx.ParseJSON(`{\"MAX\": 100, \"MIN\": 1}`)\n    defer jsonVal.Free()\n\n    // Create module with various export types\n    module := quickjs.NewModuleBuilder(\"utils\").\n        Export(\"config\", config).                    // Object export\n        Export(\"greet\", greetFunc).                  // Function export\n        Export(\"constants\", jsonVal).                // JSON export\n        Export(\"default\", ctx.NewString(\"Utils Library\"))  // Default export\n\n    err := module.Build(ctx)\n    if err != nil {\n        panic(err)\n    }\n\n    // Use mixed imports in JavaScript\n    result := ctx.Eval(`\n        (async function() {\n            // Import from utils module\n            const { greet, config, constants } = await import('utils');\n            \n            // Combine functionality\n            const message = greet(\"JavaScript\");\n            const info = config.appName + \" v\" + config.version;\n            const limits = \"Max: \" + constants.MAX + \", Min: \" + constants.MIN;\n            \n            return { message, info, limits };\n        })()\n    `, quickjs.EvalAwait(true))\n    defer result.Free()\n\n    if result.IsException() {\n        err := ctx.Exception()\n        panic(err)\n    }\n\n    fmt.Println(\"Advanced module result:\", result.JSONStringify())\n}\n```\n\n#### Multiple Module Integration\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"strings\"\n    \"github.com/buke/quickjs-go\"\n)\n\nfunc main() {\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    // Create math module\n    addFunc := ctx.NewFunction(func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {\n        if len(args) \u003e= 2 {\n            return ctx.NewFloat64(args[0].ToFloat64() + args[1].ToFloat64())\n        }\n        return ctx.NewFloat64(0)\n    })\n    defer addFunc.Free()\n\n    mathModule := quickjs.NewModuleBuilder(\"math\").\n        Export(\"add\", addFunc).\n        Export(\"PI\", ctx.NewFloat64(3.14159))\n\n    // Create string utilities module\n    upperFunc := ctx.NewFunction(func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {\n        if len(args) \u003e 0 {\n            return ctx.NewString(strings.ToUpper(args[0].ToString()))\n        }\n        return ctx.NewString(\"\")\n    })\n    defer upperFunc.Free()\n\n    stringModule := quickjs.NewModuleBuilder(\"strings\").\n        Export(\"upper\", upperFunc)\n\n    // Build both modules\n    err := mathModule.Build(ctx)\n    if err != nil {\n        panic(err)\n    }\n\n    err = stringModule.Build(ctx)\n    if err != nil {\n        panic(err)\n    }\n\n    // Use multiple modules together\n    result := ctx.Eval(`\n        (async function() {\n            // Import from multiple modules\n            const { add, PI } = await import('math');\n            const { upper } = await import('strings');\n            \n            // Combine functionality\n            const sum = add(PI, 1);\n            const message = \"Result: \" + sum.toFixed(2);\n            const finalMessage = upper(message);\n            \n            return finalMessage;\n        })()\n    `, quickjs.EvalAwait(true))\n    defer result.Free()\n\n    if result.IsException() {\n        err := ctx.Exception()\n        panic(err)\n    }\n\n    fmt.Println(\"Multiple modules result:\", result.ToString())\n    // Output: Multiple modules result: RESULT: 4.14\n}\n```\n\n#### ModuleBuilder API Reference\n\n**Core Methods:**\n- `NewModuleBuilder(name)` - Create a new module builder with the specified name\n- `Export(name, value)` - Add a named export to the module (chainable method)\n- `Build(ctx)` - Register the module in the JavaScript context\n\n### Create JavaScript Classes from Go with ClassBuilder\n\nThe ClassBuilder API allows you to create JavaScript classes from Go code.\n\n#### Manual Class Creation\n\nCreate JavaScript classes manually with control over methods, properties, and accessors:\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"math\"\n    \"github.com/buke/quickjs-go\"\n)\n\ntype Point struct {\n    X, Y float64\n    Name string\n}\n\nfunc (p *Point) Distance() float64 {\n    return math.Sqrt(p.X*p.X + p.Y*p.Y)\n}\n\nfunc (p *Point) Move(dx, dy float64) {\n    p.X += dx\n    p.Y += dy\n}\n\nfunc main() {\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    // Create Point class using ClassBuilder\n    pointConstructor, _ := quickjs.NewClassBuilder(\"Point\").\n        Constructor(func(ctx *quickjs.Context, instance *quickjs.Value, args []*quickjs.Value) (interface{}, error) {\n            x, y := 0.0, 0.0\n            name := \"Unnamed Point\"\n            \n            if len(args) \u003e 0 { x = args[0].ToFloat64() }\n            if len(args) \u003e 1 { y = args[1].ToFloat64() }\n            if len(args) \u003e 2 { name = args[2].ToString() }\n            \n            // Return Go object for automatic association\n            return \u0026Point{X: x, Y: y, Name: name}, nil\n        }).\n        // Accessors provide getter/setter functionality with custom logic\n        Accessor(\"x\", \n            func(ctx *quickjs.Context, this *quickjs.Value) *quickjs.Value {\n                point, _ := this.GetGoObject()\n                return ctx.NewFloat64(point.(*Point).X)\n            },\n            func(ctx *quickjs.Context, this *quickjs.Value, value *quickjs.Value) *quickjs.Value {\n                point, _ := this.GetGoObject()\n                point.(*Point).X = value.ToFloat64()\n                return ctx.NewUndefined()\n            }).\n        Accessor(\"y\",\n            func(ctx *quickjs.Context, this *quickjs.Value) *quickjs.Value {\n                point, _ := this.GetGoObject()\n                return ctx.NewFloat64(point.(*Point).Y)\n            },\n            func(ctx *quickjs.Context, this *quickjs.Value, value *quickjs.Value) *quickjs.Value {\n                point, _ := this.GetGoObject()\n                point.(*Point).Y = value.ToFloat64()\n                return ctx.NewUndefined()\n            }).\n        // Properties are bound directly to each instance\n        Property(\"version\", ctx.NewString(\"1.0.0\")).\n        Property(\"type\", ctx.NewString(\"Point\")).\n        // Read-only property\n        Property(\"readOnly\", ctx.NewBool(true), quickjs.PropertyConfigurable).\n        // Instance methods\n        Method(\"distance\", func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {\n            point, _ := this.GetGoObject()\n            return ctx.NewFloat64(point.(*Point).Distance())\n        }).\n        Method(\"move\", func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {\n            point, _ := this.GetGoObject()\n            dx, dy := 0.0, 0.0\n            if len(args) \u003e 0 { dx = args[0].ToFloat64() }\n            if len(args) \u003e 1 { dy = args[1].ToFloat64() }\n            point.(*Point).Move(dx, dy)\n            return ctx.NewUndefined()\n        }).\n        Method(\"getName\", func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {\n            point, _ := this.GetGoObject()\n            return ctx.NewString(point.(*Point).Name)\n        }).\n        // Static method\n        StaticMethod(\"origin\", func(ctx *quickjs.Context, this *quickjs.Value, args []*quickjs.Value) *quickjs.Value {\n            // Create a new Point at origin\n            origin := \u0026Point{X: 0, Y: 0, Name: \"Origin\"}\n            jsVal, _ := ctx.Marshal(origin)\n            return jsVal\n        }).\n        Build(ctx)\n\n    // Register the class\n    ctx.Globals().Set(\"Point\", pointConstructor)\n\n    // Use in JavaScript\n    result := ctx.Eval(`\n        const p = new Point(3, 4, \"My Point\");\n        const dist1 = p.distance();\n        p.move(1, 1);\n        const dist2 = p.distance();\n        \n        // Static method usage\n        const origin = Point.origin();\n        \n        ({ \n            // Accessor usage\n            x: p.x,\n            y: p.y,\n            // Property usage\n            version: p.version,\n            type: p.type,\n            readOnly: p.readOnly,\n            hasOwnProperty: p.hasOwnProperty('version'), // true for properties\n            // Method results\n            name: p.getName(),\n            initialDistance: dist1,\n            finalDistance: dist2,\n            // Static method result\n            originDistance: Math.sqrt(origin.x * origin.x + origin.y * origin.y)\n        });\n    `)\n    defer result.Free()\n    \n    if result.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n    \n    fmt.Println(\"Result:\", result.JSONStringify())\n    \n    // Demonstrate the difference between accessors and properties\n    propertyTest := ctx.Eval(`\n        const p1 = new Point(1, 1);\n        const p2 = new Point(2, 2);\n        \n        // Properties are instance-specific values\n        const sameVersion = p1.version === p2.version; // true, same static value\n        \n        // Accessors provide dynamic values from Go object\n        const differentX = p1.x !== p2.x; // true, different values from Go objects\n        \n        ({ sameVersion, differentX });\n    `)\n    defer propertyTest.Free()\n    \n    if propertyTest.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n    \n    fmt.Println(\"Property vs Accessor:\", propertyTest.JSONStringify())\n}\n```\n\n#### Automatic Class Creation with Reflection\n\nAutomatically generate JavaScript classes from Go structs using reflection. Go struct fields are automatically converted to JavaScript class accessors, providing getter/setter functionality that directly maps to the underlying Go object fields.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"github.com/buke/quickjs-go\"\n)\n\ntype User struct {\n    ID        int64     `js:\"id\"`           // Becomes accessor: user.id\n    Name      string    `js:\"name\"`         // Becomes accessor: user.name\n    Email     string    `json:\"email_address\"` // Becomes accessor: user.email_address\n    Age       int       `js:\"age\"`          // Becomes accessor: user.age\n    IsActive  bool      `js:\"is_active\"`    // Becomes accessor: user.is_active\n    Scores    []float32 `js:\"scores\"`       // Becomes accessor: user.scores (Float32Array)\n    private   string    // Not accessible (unexported)\n    Secret    string    `js:\"-\"`            // Explicitly ignored\n}\n\nfunc (u *User) GetFullInfo() string {\n    return fmt.Sprintf(\"%s (%s) - Age: %d\", u.Name, u.Email, u.Age)\n}\n\nfunc (u *User) UpdateEmail(newEmail string) {\n    u.Email = newEmail\n}\n\nfunc (u *User) AddScore(score float32) {\n    u.Scores = append(u.Scores, score)\n}\n\nfunc main() {\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    // Automatically create User class from struct\n    userConstructor, _ := ctx.BindClass(\u0026User{})\n\n    ctx.Globals().Set(\"User\", userConstructor)\n\n    // Use with positional arguments\n    result1 := ctx.Eval(`\n        const user1 = new User(1, \"Alice\", \"alice@example.com\", 25, true, [95.5, 87.2]);\n        user1.GetFullInfo();\n    `)\n    defer result1.Free()\n    \n    if result1.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n    \n    fmt.Println(\"Positional:\", result1.ToString())\n\n    // Use with named arguments (object parameter)\n    result2 := ctx.Eval(`\n        const user2 = new User({\n            id: 2,\n            name: \"Bob\",\n            email_address: \"bob@example.com\",\n            age: 30,\n            is_active: true,\n            scores: [88.0, 92.5, 85.0]\n        });\n        \n        // Call methods\n        user2.UpdateEmail(\"bob.smith@example.com\");\n        user2.AddScore(95.0);\n        \n        // Access fields via accessors (directly map to Go struct fields)\n        user2.age = 31;        // Setter: modifies the Go struct field\n        const newAge = user2.age; // Getter: reads from the Go struct field\n        \n        ({\n            info: user2.GetFullInfo(),\n            email: user2.email_address,  // Accessor getter\n            age: newAge,                 // Modified via accessor setter\n            scoresType: user2.scores instanceof Float32Array,\n            scoresLength: user2.scores.length\n        });\n    `)\n    defer result2.Free()\n    \n    if result2.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n    \n    fmt.Println(\"Named:\", result2.JSONStringify())\n\n    // Demonstrate field accessor synchronization\n    result3 := ctx.Eval(`\n        const user3 = new User(3, \"Charlie\", \"charlie@example.com\", 35, true, []);\n        \n        // Field accessors provide direct access to Go struct fields\n        const originalName = user3.name;  // Getter: reads Go struct field\n        \n        user3.name = \"Charles\";           // Setter: modifies Go struct field\n        const newName = user3.name;       // Getter: reads modified field\n        \n        // Changes are synchronized with Go object\n        const info = user3.GetFullInfo(); // Method sees the changed name\n        \n        // Verify synchronization by changing multiple fields\n        user3.age = 36;\n        user3.email_address = \"charles.updated@example.com\";\n        const updatedInfo = user3.GetFullInfo();\n        \n        ({\n            originalName: originalName,\n            newName: newName,\n            infoAfterNameChange: info,\n            finalInfo: updatedInfo,\n            // Demonstrate that Go object is synchronized\n            goObjectAge: user3.age,\n            goObjectEmail: user3.email_address\n        });\n    `)\n    defer result3.Free()\n    \n    if result3.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n    \n    fmt.Println(\"Synchronization demonstration:\", result3.JSONStringify())\n}\n```\n\n### Bytecode Compiler\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/buke/quickjs-go\"\n)\n\nfunc main() {\n    // Create a new runtime\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n    // Create a new context\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    jsStr := `\n    function fib(n)\n    {\n        if (n \u003c= 0)\n            return 0;\n        else if (n == 1)\n            return 1;\n        else\n            return fib(n - 1) + fib(n - 2);\n    }\n    fib(10)\n    `\n    // Compile the script to bytecode\n    buf, err := ctx.Compile(jsStr)\n    if err != nil {\n        panic(err)\n    }\n\n    // Create a new runtime\n    rt2 := quickjs.NewRuntime()\n    defer rt2.Close()\n\n    // Create a new context\n    ctx2 := rt2.NewContext()\n    defer ctx2.Close()\n\n    //Eval bytecode\n    result := ctx2.EvalBytecode(buf)\n    defer result.Free()\n    \n    if result.IsException() {\n        err := ctx2.Exception()\n        fmt.Println(\"Error:\", err.Error())\n        return\n    }\n    \n    fmt.Println(result.ToInt32())\n}\n```\n\n### Runtime Options: memory, stack, GC, ...\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/buke/quickjs-go\"\n)\n\nfunc main() {\n    // Create a new runtime\n    rt := quickjs.NewRuntime()\n    defer rt.Close()\n\n    // set runtime options\n    rt.SetExecuteTimeout(30) // Set execute timeout to 30 seconds\n    rt.SetMemoryLimit(256 * 1024) // Set memory limit to 256KB\n    rt.SetMaxStackSize(65534) // Set max stack size to 65534\n    rt.SetGCThreshold(256 * 1024) // Set GC threshold to 256KB\n    rt.SetCanBlock(true) // Set can block to true\n\n    // Create a new context\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    result := ctx.Eval(`var array = []; while (true) { array.push(null) }`)\n    defer result.Free()\n    \n    if result.IsException() {\n        err := ctx.Exception()\n        fmt.Println(\"Memory limit exceeded:\", err.Error())\n    }\n}\n```\n\n### ES6 Module Support\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n\n    \"github.com/buke/quickjs-go\"\n)\n\nfunc main() {\n    // enable module import\n    rt := quickjs.NewRuntime(quickjs.WithModuleImport(true))\n    defer rt.Close()\n\n    ctx := rt.NewContext()\n    defer ctx.Close()\n\n    // eval module\n    r1 := ctx.EvalFile(\"./test/hello_module.js\")\n    defer r1.Free()\n    if r1.IsException() {\n        err := ctx.Exception()\n        panic(err)\n    }\n\n    // load module\n    r2 := ctx.LoadModuleFile(\"./test/fib_module.js\", \"fib_foo\")\n    defer r2.Free()\n    if r2.IsException() {\n        err := ctx.Exception()\n        panic(err)\n    }\n\n    // call module\n    r3 := ctx.Eval(`\n    import {fib} from 'fib_foo';\n    globalThis.result = fib(9);\n    `)\n    defer r3.Free()\n    if r3.IsException() {\n        err := ctx.Exception()\n        panic(err)\n    }\n\n    result := ctx.Globals().Get(\"result\")\n    defer result.Free()\n    fmt.Println(\"Fibonacci result:\", result.ToInt32())\n}\n```\n\n## License\n\n[MIT License](LICENSE)","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbuke%2Fquickjs-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbuke%2Fquickjs-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbuke%2Fquickjs-go/lists"}