SkillAgentSearch skills...

Qjs

QJS is a CGO-Free, modern, secure JavaScript runtime for Go applications, built on the powerful QuickJS engine and Wazero WebAssembly runtime

Install / Use

/learn @fastschema/Qjs
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

QJS - JavaScript in Go with QuickJS and Wazero

<p align="center"> <a href="https://pkg.go.dev/github.com/fastschema/qjs#section-readme" target="_blank" rel="noopener"> <img src="https://img.shields.io/badge/go.dev-reference-blue?logo=go&logoColor=white" alt="Go.Dev reference" /> </a> <a href="https://goreportcard.com/report/github.com/fastschema/qjs" target="_blank" rel="noopener"> <img src="https://goreportcard.com/badge/github.com/fastschema/qjs" alt="go report card" /> </a> <a href="https://codecov.io/gh/fastschema/qjs/branch/master" > <img src="https://codecov.io/gh/fastschema/qjs/branch/master/graph/badge.svg?token=yluqOtL5z0"/> </a> <a href="https://github.com/fastschema/qjs/actions" target="_blank" rel="noopener"> <img src="https://github.com/fastschema/qjs/actions/workflows/ci.yml/badge.svg" alt="test status" /> </a> <a href="https://opensource.org/licenses/MIT" target="_blank" rel="noopener"> <img src="https://img.shields.io/badge/license-MIT-brightgreen.svg" alt="MIT license" /> </a> </p> <p align="center"> <a href="https://app.fossa.com/projects/git%2Bgithub.com%2Ffastschema%2Fqjs?ref=badge_shield&issueType=license" alt="FOSSA Status"> <img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2Ffastschema%2Fqjs.svg?type=shield&issueType=license"/> </a> <a href="https://app.fossa.com/projects/git%2Bgithub.com%2Ffastschema%2Fqjs?ref=badge_shield&issueType=security" alt="FOSSA Status"> <img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2Ffastschema%2Fqjs.svg?type=shield&issueType=security"/> </a> </p>

QJS is a CGO-Free, modern, secure JavaScript runtime for Go applications, built on the powerful QuickJS engine and Wazero WebAssembly runtime.

QJS allows you to run JavaScript code safely and efficiently, with full support for ES2023 features, async/await, and Go-JS interoperability.

Features

  • JavaScript ES6+ Support: Full ES2023 compatibility via QuickJS (NG fork).
  • WebAssembly Execution: Secure, sandboxed runtime using Wazero.
  • Go-JS Interoperability: Seamless data conversion between Go and JavaScript.
  • ProxyValue Support: Zero-copy sharing of Go values with JavaScript via lightweight proxies.
  • Function Binding: Expose Go functions to JavaScript and vice versa.
  • Async/Await: Full support for asynchronous JavaScript execution.
  • Memory Safety: Memory-safe execution environment with configurable limits.
  • No CGO Dependencies: Pure Go implementation with WebAssembly.

Benchmarks

Factorial Calculation

Computing factorial(10) 1,000,000 times

| Iteration | GOJA | ModerncQuickJS | QJS | | --- | --- | --- | --- | | 1 | 1.128s | 1.897s | 737.635ms | | 2 | 1.134s | 1.936s | 742.670ms | | 3 | 1.123s | 1.898s | 738.737ms | | 4 | 1.120s | 1.900s | 754.692ms | | 5 | 1.132s | 1.918s | 756.924ms | | Average | 1.127s | 1.910s | 746.132ms | | Total | 5.637s | 9.549s | 3.731s | | Speed | 1.51x | 2.56x | 1.00x |

Benchmarks run on AMD Ryzen 7 7840HS, 32GB RAM, Linux

AreWeFastYet V8-V7

| Metric | GOJA | ModerncQuickJS | QJS | | --- | --- | --- | --- | | Richards | 345 | 189 | 434 | | DeltaBlue | 411 | 205 | 451 | | Crypto | 203 | 305 | 393 | | RayTrace | 404 | 347 | 488 | | EarleyBoyer | 779 | 531 | 852 | | RegExp | 381 | 145 | 142 | | Splay | 1289 | 856 | 1408 | | NavierStokes | 324 | 436 | 588 | | Score (version 7) | 442 | 323 | 498 | | Duration (seconds) | 78.349s | 97.240s | 72.004s |

Benchmarks run on AMD Ryzen 7 7840HS, 32GB RAM, Linux

Example Usage

Basic Execution

rt, err := qjs.New()
if err != nil {
	log.Fatal(err)
}

defer rt.Close()
ctx := rt.Context()

result, err := ctx.Eval("test.js", qjs.Code(`
	const person = {
		name: "Alice",
		age: 30,
		city: "New York"
	};

	const info = Object.keys(person).map(key =>
		key + ": " + person[key]
	).join(", ");

	// The last expression is the return value
	({ person: person, info: info });
`))
if err != nil {
	log.Fatal("Eval error:", err)
}
defer result.Free()
// Output: name: Alice, age: 30, city: New York
log.Println(result.GetPropertyStr("info").String())
// Output: Alice
log.Println(result.GetPropertyStr("person").GetPropertyStr("name").String())
// Output: 30
log.Println(result.GetPropertyStr("person").GetPropertyStr("age").Int32())

Go function binding

ctx.SetFunc("goFunction", func(this *qjs.This) (*qjs.Value, error) {
    return this.Context().NewString("Hello from Go!"), nil
})

result, err := ctx.Eval("test.js", qjs.Code(`
	const message = goFunction();
	message;
`))
if err != nil {
	log.Fatal("Eval error:", err)
}
defer result.Free()

// Output: Hello from Go!
log.Println(result.String())

HTTP Handlers in JavaScript

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/fastschema/qjs"
)

func must[T any](val T, err error) T {
	if err != nil {
		log.Fatalf("Error: %v", err)
	}
	return val
}

const script = `
// JS handlers for HTTP routes
const about = () => {
	return "QuickJS in Go - Hello World!";
};

const contact = () => {
	return "Contact us at contact@example.com";
};

export default { about, contact };
`

func main() {
	rt := must(qjs.New())
	defer rt.Close()
	ctx := rt.Context()

	// Precompile the script to bytecode
	byteCode := must(ctx.Compile("script.js", qjs.Code(script), qjs.TypeModule()))
	// Use a pool of runtimes for concurrent requests
	pool := qjs.NewPool(3, &qjs.Option{}, func(r *qjs.Runtime) error {
		results := must(r.Context().Eval("script.js", qjs.Bytecode(byteCode), qjs.TypeModule()))
		// Store the exported functions in the global object for easy access
		r.Context().Global().SetPropertyStr("handlers", results)
		return nil
	})

	// Register HTTP handlers based on JS functions
	val := must(ctx.Eval("script.js", qjs.Bytecode(byteCode), qjs.TypeModule()))
	methodNames := must(val.GetOwnPropertyNames())
	val.Free()
	for _, methodName := range methodNames {
		http.HandleFunc("/"+methodName, func(w http.ResponseWriter, r *http.Request) {
			runtime := must(pool.Get())
			defer pool.Put(runtime)

			// Call the corresponding JS function
			handlers := runtime.Context().Global().GetPropertyStr("handlers")
			result := must(handlers.InvokeJS(methodName))
			fmt.Fprint(w, result.String())
			result.Free()
		})
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello from Go's HTTP server!")
	})

	log.Println("Server listening on :8080")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatalf("Server error: %v\n", err)
	}
}

Async operations

Awaiting a promise

ctx.SetAsyncFunc("asyncFunction", func(this *qjs.This) {
	go func() {
		time.Sleep(100 * time.Millisecond)
		result := this.Context().NewString("Async result from Go!")
		this.Promise().Resolve(result)
	}()
})

result, err := ctx.Eval("test.js", qjs.Code(`
async function main() {
	const result = await asyncFunction();
	return result;
}
({ main: main() });
`))

if err != nil {
	log.Fatal("Eval error:", err)
}
defer result.Free()

mainFunc := result.GetPropertyStr("main")

// Wait for the promise to resolve
val, err := mainFunc.Await()
if err != nil {
	log.Fatal("Await error:", err)
}

// Output: Async result from Go!
log.Println("Awaited value:", val.String())

Top level await

// asyncFunction is already defined above
result, err := ctx.Eval("test.js", qjs.Code(`
	async function main() {
		const result = await asyncFunction();
		return result;
	}
	await main()
`), qjs.FlagAsync())

if err != nil {
	log.Fatal("Eval error:", err)
}

defer result.Free()
log.Println(result.String())

Call JS function from Go

// Call JS function from Go
result, err := ctx.Eval("test.js", qjs.Code(`
	function add(a, b) {
		return a + b;
	}

	function errorFunc() {
		throw new Error("test error");
	}

	({
		addFunc: add,
		errorFunc: errorFunc
	});
`))

if err != nil {
	log.Fatal("Eval error:", err)
}
defer result.Free()

jsAddFunc := result.GetPropertyStr("addFunc")
defer jsAddFunc.Free()

goAddFunc, err := qjs.JsFuncToGo[func(int, int) (int, error)](jsAddFunc)
if err != nil {
	log.Fatal("Func conversion error:", err)
}

total, err := goAddFunc(1, 2)
if err != nil {
	log.Fatal("Func execution error:", err)
}

// Output: 3
log.Println("Addition result:", total)

jsErrorFunc := result.GetPropertyStr("errorFunc")
defer jsErrorFunc.Free()

goErrorFunc, err := qjs.JsFuncToGo[func() (any, error)](jsErrorFunc)
if err != nil {
	log.Fatal("Func conversion error:", err)
}

_, err = goErrorFunc()
if err != nil {
	// Output:
	// JS function execution failed: Error: test error
  //  at errorFunc (test.js:7:13)
	log.Println(err.Error())
}

ES Modules

// Load a utility module
if _, err = ctx.Load("math-utils.js", qjs.Code(`
	export function add(a, b) {
		return a + b;
	}

	export function multiply(a, b) {
		return a * b;
	}

	export function power(base, exponent) {
		return Math.pow(base, exponent);
	}

	export const PI = 3.14159;
	export const E = 2.71828;
	export default {
		add,
		multiply,
		power,
		PI,
		E
	};
`)); err != nil {
	log.Fatal("Module load error:", err)
}

// Use the module
result, err := ctx.Eval("use-math.js", qjs.Code(`
	import mathUtils, { add, multiply, power, PI } from 'math-utils.js';

	const calculations = {
		addition: add(10, 20),
		multiplication: multiply(6, 7),
		power: power(2, 8),
		circleArea: PI * power(5, 2),
		defaultAdd: mathUtils.add(10, 20)
	};

	export default calculations;
`), qjs.TypeModule())

if err != nil {
	log.Fatal("Module eval error:", err)
}

// Output:
// Addition: 30
// Multiplication: 42
// Power: 256
// Circle Area: 78.54
// Default Add: 30
fmt.Printf("Addition: %d\n", result.GetPropertyStr("addition").Int32())
fmt.Printf("Multiplication: %.0f\n", result.GetPropertyStr("multiplication").Float64())
fmt.Printf("Power: %.0f\n", result.GetPropertyStr("power").Float64())
fmt.Printf("Circle Area: %.2f\n", result.GetPropertyStr("ci
View on GitHub
GitHub Stars552
CategoryDevelopment
Updated1d ago
Forks22

Languages

Go

Security Score

100/100

Audited on Mar 31, 2026

No findings