JavaScript Blocks
Implement deterministic code in the isolated JavaScript runtime.
Computation code may use StillMade.emit(name, value) instead of returning an output object. Names must be declared outputs; emit each once, and do not combine emission with a return value or cooperative pending result. Emissions remain inside the sandbox until final host validation and normal result acceptance. StillMade.resolveRequirement(inputName) reads a declared input from the fixed host-validated snapshot and returns {resolved:true, requirement, value} or {resolved:false, requirement} for an absent optional input. Undeclared names throw; this method does not search sources or call Chat.
StillMade.getProjectContext() returns an isolated snapshot of declared context-bound inputs, keyed by their context field names. An optional declared field argument reads one value; undeclared fields throw and absent optional values return undefined. It does not enumerate the whole project or bypass host permissions, and it does not fetch newer values during a run.
StillMade.validate(value, schema) returns a boolean for supported JSON/schema validation only. It does not establish semantic correctness, media validity, permissions, or provenance. Final host output validation remains mandatory. These computation helpers are not available to view.javascript. Isolated computation code also has StillMade.requestFromUser and StillMade.requestFromChat (see UNIVERSAL_RESOLVER.md) and StillMade.patchProject (reviewable workspace-edit proposals). See JAVASCRIPT_RESULTS.md for state, continuation and permission boundaries.
Create an editable JavaScript block with:
node packages/block-cli/cli.js create ./my-js-block javascript
node packages/block-cli/cli.js test ./my-js-block
node packages/block-cli/cli.js pack ./my-js-block ./my-js-block.stillmade.jsonSet manifest.runtime to javascript. The source file is src/run.js, packed as {manifest,code,tests}. Write a synchronous function body: input contains validated named inputs; return an object whose keys match declared outputs. For example, with a text input and text/words outputs:
const text = input.text.trim();
return { text, words: text ? text.split(/\s+/).length : 0 };Use await runPackageAsync(package, input) from the SDK to dispatch either runtime. The older synchronous runPackage remains recipe-only. In browsers, invoke the async API inside a disposable worker; the app does this automatically. On Node, the SDK creates and terminates a separate worker for every JavaScript execution. The downloadable SDK includes its pinned QuickJS WASM runtime and dependency licenses, so these examples run without installing packages or accessing a network.
The guest has standard QuickJS JavaScript built-ins. It has no DOM, browser storage, fetch, Node globals, timers, module loader, filesystem, secrets, or host callbacks. An asset reference is data, not file access. npm packages and imports are unsupported. Promises are not awaited; return synchronously. Values cross through JSON and are validated against output ports. Avoid Date/randomness in behavior fixtures.
Limits per run: 256 KiB source, 4 MiB serialized input and output, 500 ms guest execution, 16 MiB guest heap, 512 KiB stack, and a fixed 32 MiB WASM memory. Node adds a five-second worker watchdog and at most four concurrent executions; app workers have a 15-second outer watchdog. Each package fixture suite has a ten-second deadline (callers may shorten it); all embedded packages share a fifteen-second project-type deadline. Timeouts, allocation failures, invalid outputs and syntax errors fail the run; no partial output is committed. Each run receives a fresh VM.