The contract was written down twice — zip declares Module and Loader, and the loader beside base's runners declared its own copy. Structural typing was the only thing keeping them equal, which is a property nobody can see and a compiler will not check. This implements zip's. The loader, the manifest and the runners live here so cloud can stop carrying a second goja runtime of its own — a shim, a bundler and a third registry that answered the same question this one does. native, goja, wazero and pyvm. wazero is the reason this is not a list of four languages: anything compiled to WebAssembly ahead of time arrives without a new runner, and Rust walks through that door rather than getting one built for it. pyvm is real CPython through cgo with PEP 684 sub-interpreters, behind a build tag, because embedding the language beats reimplementing a subset of it. Its one dependency on base was a single call for an unguessable string, which crypto/rand says directly. Co-authored-by: Hanzo Dev <[email protected]>
1.7 KiB
runtime
One plugin runtime for the Hanzo estate: a module declares what it exports, and a caller invokes an export by name with JSON in and JSON out. What language the module is written in is a property of the module, not of the caller.
type Module interface {
Name() string
Runtime() string
Exports() []string
Invoke(ctx context.Context, fn string, payload []byte) ([]byte, error)
}
That interface is zip's. This module implements it rather than declaring its
own copy, which is the whole reason it exists: the same contract was written
down twice — once in zip, once beside the loader — and only structural typing
kept the two honest.
The runtimes
| runtime | language | how |
|---|---|---|
native |
Go | in-process, no boundary |
goja |
JavaScript, TypeScript | in-process, no Node |
wazero |
Rust, and anything else | compiled to WebAssembly ahead of time |
pyvm |
Python | CPython via cgo, PEP 684 sub-interpreters — build tag pyvm |
wazero is why this list is not a list of four languages. Anything that
compiles to WebAssembly arrives without a new runner, and Rust is the first
caller of that door rather than a special case built for it.
A module declares itself
extension.json, beside the module:
{
"name": "brave-search",
"version": "1.0.0",
"runtime": "goja",
"module": "index.js",
"exports": ["search"]
}
Exports is the contract. A caller reads it to know what can be invoked, and
nothing else has to be registered anywhere — which is what lets one registry
publish a Go action and a Python one side by side.