Log inGet started
·
assettype · drop-in viewer
asset⌬ assettypeassetTypeprimary: behavior.luau·part ofpackage zerojs.package·originates fromworld 0fa533e8-7…●2problems

zJs.assetType

A zJs asset is a JavaScript ES module: a `<name>.zJs/` folder whose `main.js` holds JavaScript source parsed and evaluated by the `zerojs` interpreter. The identity is `<name>` (the `.zJs` suffix strips).

by◐Deata·posted 3mo ago
What it does

zJs (asset type)

A zJs asset is a JavaScript ES module: a <name>.zJs/ folder whose main.js holds JavaScript source parsed and evaluated by the zerojs interpreter. The identity is <name> (the .zJs suffix strips).

Folder shape

  • main.js — the module body. Required.
  • README.md — instance documentation. Required.
  • .metadata — tags sidecar ({"tags": []}). Required.

How to create one

asset.create("zJs", "greeter")
-- Creates /zero/source/greeter.zJs/ with a hello-world main.js.
asset.create("zJs", "greeter", { source = "export const x = 1" })
-- main.js is the given source verbatim.

Static diagnostics

Every write to main.js reparses the module and resolves its import specifiers against the live asset index. asset.resolve("<name>", "zJs"):check() returns { ok, diagnostics }: ok is true when the module parses and every import resolves to another zJs module; each diagnostic carries a severity, message, line, and col. No JavaScript runs during a check.

Interface

What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.

conforms to

zero/source-extract/v2

module ZJsAssetTypeRef Per-instance methods exposed on every `AssetRef<zJs>`. Loaded lazily by `asset_ref.module` the first time a zJs ref is touched in a VM. A zJs asset is a `<name>.zJs/` folder whose `main.js` holds a JavaScript ES module. The methods here surface the static write-time diagnostics the type computes for that module and execute it: `run` / `runAsync` for a script or module, and `exports` to read a module's export namespace as a Luau table.

hashSource(src: string) → number

Cheap content hash: a 31-polynomial rolling sum over the source bytes, kept inside 2^31-1 so every intermediate stays exactly representable as a double. It observes whether a cached parse still matches the on-disk source; it is not a cryptographic digest.

argtypedescription
srcstring

mainPathOf(self: ?) → string

Resolve THIS instance's `main.js` from the reference's tracked path. `asset.resolve` hands back the `.zJs` folder; the body lives in its `main.js` (the path may already point at the entry when resolved from it).

argtypedescription
self?

identityOf(ref: ?) → string

A stable identity string for a reference, the same across every specifier spelling that resolves to it: the asset guid, falling back to its canonical identity, then its tracked path. Module evaluation keys modules by this, so two imports of one asset dedupe onto one evaluation and a self-import is seen as a cycle.

argtypedescription
ref?

nameOf(ref: ?) → string

The instance name a refusal message names, falling back to its identity.

argtypedescription
ref?

describeParseError(err: ?) → void

Render a parser error object (`{ message, line, col }`) into diagnostic fields.

argtypedescription
err?

resolveRef(spec: string) → any

Resolve an import specifier to the asset reference it names, or nil when the live index registers nothing under either the bare specifier or its `.zJs`-suffixed identity form. `asset.resolve` raises on an unknown name, so each candidate is guarded.

argtypedescription
specstring

resolveSpecifierType(spec: string) → string

The type name of the asset a specifier resolves to, or nil when nothing matches. Both spellings an import may use are tried through `resolveRef`.

argtypedescription
specstring

resolveZjsRef(spec: string) → any

The asset reference a specifier resolves to when it names a zJs module, or nil otherwise. The runtime resolver hands `vm:evalModule` only zJs sources.

argtypedescription
specstring

parsePipeline(self: ?) → Cache

Parse `main.js` in module mode and resolve its imports, producing the cache consumed by `check` and the execution methods. Pure: it reads the source and the asset index, and never writes to the VFS. A parse failure yields a single error diagnostic and a nil ast; a successful parse yields per-import diagnostics for specifiers that do not resolve to a zJs module. The source is captured on `src` so a later `run`/`exports` drives the VM from the cache.

argtypedescription
self?

ensureCache(self: ?) → Cache

Return the cached pipeline result, running the pipeline once on a cold ref.

argtypedescription
self?

requireClean(self: ?, verb: string) → Cache

Read the parse cache, refusing when it carries diagnostics: `run`, `runAsync` and `exports` never surface a parse error at call time, so a non-empty diagnostics list (a parse error or an unresolved import, both produced by `onChange`) becomes a refusal naming the first diagnostic. A clean cache is returned for the caller to execute.

argtypedescription
self?
verbstring

isModuleCache(c: Cache) → boolean

A module has at least one import or export; a plain script has neither. The distinction decides whether `run` evaluates through the module graph (a module has no completion value) or as a script (whose completion value is returned).

argtypedescription
cCache

makeResolver( ) → void

The host resolver `vm:evalModule` calls for each import specifier. It resolves a specifier to a sibling zJs asset through the live index (the same world-relative and `.zJs`-suffixed forms the diagnostics pipeline tries), keys the target by its stable identity so two spellings of one asset dedupe onto one evaluation and a self-import reads as a cycle, and reads the target's `main.js` from the VFS so an edited sibling is picked up on the next call. A specifier that resolves to no readable zJs asset yields nil, which the module graph reports as an unresolved-import error naming the specifier and its importer.

evalModuleNamespace(self: ?, c: Cache, env: { [string]: any }?) → void

Evaluate `self` as the root of a module graph on a fresh VM, threading each import through the asset-backed resolver. `c` is the already-clean cache; its source drives the root and `identityOf(self)` is the root id, so a self-import is a cycle. Returns the VM realm and the root namespace object for the caller to marshal or discard.

argtypedescription
self?
cCache
env{ [string]: any }?

check(self: ?) →

Static diagnostics for THIS module's `main.js`: parse errors plus imports that do not resolve to a zJs module. Reads the cache `onChange` maintains, running the parse+resolve pipeline once on a cold reference.

argtypedescription
self?

examples

local r = asset.resolve("greeter", "zJs"):check(); if not r.ok then ... end

run(self: ?, opts: { env: { [string]: any }? }?, opts.env: ?) → any

Execute THIS module on a fresh JavaScript VM and marshal the result to Luau. A plain script (no imports or exports) runs as a script and returns its completion value; a module evaluates through the module graph and returns nil, since module evaluation has no completion value. `opts.env` seeds host values as JavaScript globals. Refuses when the module carries diagnostics, naming the first, and directs a script that leaves asynchronous work pending to `runAsync`.

argtypedescription
self?
opts{ env: { [string]: any }? }?
opts.env?host values injected as JavaScript globals for this run.

examples

local sum = asset.resolve("calc", "zJs"):run({ env = { base = 10 } })

runAsync(self: ?, opts: { env: { [string]: any }?, timeoutMs: number? }?, opts.env: ?, opts.timeoutMs: ?) → any

Execute THIS module's program on a fresh VM under the asynchronous runner, driving armed timers and pending promises to completion. Returns the engine run handle: poll `handle.done`, then read `handle.ok` / `handle.value` / `handle.error`, or call `handle:await()` from a yieldable context. `opts.timeoutMs` bounds a never-settling run. Refuses when the module carries diagnostics, naming the first.

argtypedescription
self?
opts{ env: { [string]: any }?, timeoutMs: number? }?
opts.env?host values injected as JavaScript globals for this run.
opts.timeoutMs?milliseconds after which a never-settling run rejects.

examples

local h = asset.resolve("job", "zJs"):runAsync({ timeoutMs = 1000 }); h:await()

exports(self: ?, opts: { env: { [string]: any }? }?, opts.env: ?) → any

Evaluate THIS module as an ES module on a fresh VM and return its export namespace marshaled to a Luau table: each named export plus `default`. Cross-asset imports resolve through the live asset index, so a JS library built from several `.zJs` assets is consumed as one table. Refuses when the module carries diagnostics, naming the first; a cycle, an unresolved import, or a thrown module body surfaces as a Luau error.

argtypedescription
self?
opts{ env: { [string]: any }? }?
opts.env?host values injected as JavaScript globals for this evaluation.

examples

local lib = asset.resolve("mathlib", "zJs"):exports(); print(lib.add(1, 2))

helloWorld(name: string) → string

Default JavaScript module scaffolded when `asset.create("zJs", name)` is called without an explicit `source`. Mirrors `template/main.js` with `[name]` bound to the instance name.

argtypedescription
namestring

onCreate(name: string, opts: CreateOpts, opts.source: ?) →

Generic-creation hook for `asset.create("zJs", name, opts)`. Pure: returns the instance's content file map for the caller to persist. `opts.source`, when given, becomes `main.js` verbatim; otherwise a hello-world module naming the instance is scaffolded from the template.

argtypedescription
namestringzJs module identity (the instance name).
optsCreateOpts
opts.source?JavaScript module source for `main.js`.

examples

asset.create("zJs", "greeter", { source = "export const x = 1" })

onChange(ref: ?, change: ?) → void

Asset-type change callback: recompute this module's static diagnostics whenever its `main.js` is edited or the whole instance is seeded. Convergent: it reads the source and asset index and replaces the ref's cached pipeline result; it never writes to the VFS. Edits to sidecars (`README.md`, `.metadata`) leave the cache untouched.

argtypedescription
ref?
change?
⌬ Types
Diagnostic = {Cache = {CreateOpts = {

Sub-parts

Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.

Showing up to 3 levels deep. Open a part to see what it holds further down.

18+items
❒
package · pulled in
❒asset
ZeroJS is a JavaScript interpreter for the Zero engine, written in Luau. It parses and executes ES2017-practical JavaScript entirely inside the Luau VM — no native code, no WebAssembly, no external dependency — and marshals values back to Luau transparently. ## Importing ```luau local zerojs = require("~zerojs.api") ``` `~zerojs` is this package's alias, registered from the `name` in its `package.yaml` wherever the package sits. The same spelling therefore works from the world that authors the package and from a world that installed it, at any depth. Every module is reachable the same way — `~zerojs.parser`, `~zerojs.stdlib.promise` — and the package's own modules reference each other with those ids. ## API surface ### Module-level | Symbol | Type | Description | |--------|------|-------------| | `zerojs.eval(src)` | `(string) -> any` | Parse and execute `src` in a fresh one-shot VM. Returns the marshaled JS completion value. Raises a Luau error on a JS exception. | | `zerojs.new(opts?)` | `(table?) -> VM` | Create a persistent VM. `opts.env` seeds additional JS globals from a Luau table. | | `zerojs.null` | sentinel | The JS `null` value (distinct from Luau `nil`). | | `zerojs.undefined` | sentinel | The JS `undefined` value, which marshals to Luau `nil` when returned. | | `zerojs.version` | `string` | Version string of this package. | | `zerojs.lastError(vm)` | `(VM) -> table?` | Returns the structured payload `{ name, message, stack }` from the most recent JS exception on `vm`. `nil` if no exception has occurred on that VM. | ### VM instance | Method | Description | |--------|-------------| | `vm:run(src, opts?)` | Execute `src` in this VM's persistent realm. `opts.sourceName` names the source for error messages and stack frames. Returns the marshaled completion value. Raises directing to `runAsync` when the script leaves a timer armed or a coroutine suspended across a frame. | | `vm:runAsync(src, opts?)` | Execute `src`, driving its event loop across engine frames until it settles. Returns a `RunHandle` immediately. `opts.timeoutMs` rejects the run if it outlasts that many milliseconds. Only one `runAsync` may be in flight per VM at a time; a second call while the first is unsettled raises. | | `vm:get(name)` | Read a global from the VM's realm. | | `vm:set(name, value)` | Write a marshaled Luau value as a JS global in the VM's realm. | ### `RunHandle` (returned by `vm:runAsync`) | Field | Description | |-------|-------------| | `done` | `false` until the run reaches a terminal state, then `true`. | | `ok` | Once `done`, `true` for a resolved run and `false` for a rejection or timeout. | | `value` | The marshaled completion value on a resolved run. | | `error` | The rendered message text on a rejected or timed-out run. | | `lastError` | The structured `{ name, message, stack }` payload for a failed run. | | `thread` | The engine `task.spawn` driver pumping the loop each frame — present only while the run is still pumping, and `nil` once the run settles on its arming frame (a synchronous or microtask-only completion needs no per-frame driver). | | `handle:await(maxFrames?)` | From a yieldable Luau context, poll `done` frame-by-frame until the handle settles, then return it. | A `runAsync` stays pending while the loop has work: an armed `setInterval` keeps it pending exactly as an un-`unref`'d handle keeps Node alive — clear the interval or bound the run with `timeoutMs`. ## Marshaling Luau values passed to `opts.env` or `vm:set` are converted to JS values. JS values returned from `run`/`eval` are converted back to Luau: numbers, strings, and booleans are transparent; arrays become 1-based Luau tables; objects become string-keyed Luau tables; JS functions become callable Luau closures; `undefined` becomes `nil`; `null` becomes `zerojs.null`. ## Step-budget sandbox Each VM runs with a default execution budget of 1,000,000 interpreter steps. A script that exceeds the budget raises a JS `RangeError: step budget exceeded`, which surfaces as a Luau error. The budget resets on each `run`/`eval` call. ## Language scope - Variable declarations: `var` (function-scoped hoisting), `let` and `const` with full TDZ enforcement - Functions: declarations, expressions, arrow functions, closures, rest parameters, default parameters - Classes: declarations and expressions, inheritance via `extends`, `super()` in constructors and methods, getter and setter accessors, instance fields, static fields and methods, computed method keys, named class expressions, TDZ before declaration - Destructuring: object and array patterns in declarations, assignment, and function parameters; nested patterns; rename, default, rest; computed keys in object patterns; elision in array patterns - Spread: array literals, call arguments, `new` arguments, object literals - Template literals (untagged) - Full coercion and equality semantics: `==`/`!=` abstract equality, `===`/`!==` strict equality, `typeof`, `instanceof` - `try`/`catch`/`finally`, `throw` - All loop forms: `for`, `while`, `do-while`, `for-in`, `for-of`; `switch` with fallthrough - `async`/`await` — real event-loop semantics: an async function returns a promise, runs synchronously up to the first `await`, and resumes from a microtask when its awaited operand settles - Property enumerability: `for-in` yields only own enumerable keys; object rest excludes prototype methods ### Asynchrony and the event loop - `Promise` with the full ES surface: `then`/`catch`/`finally`, `resolve`/`reject`, and the combinators `all`, `allSettled`, `race`, and `any` (with `AggregateError` when every input to `any` rejects) - `queueMicrotask` for scheduling a microtask directly - Timers: `setTimeout`/`clearTimeout` and `setInterval`/`clearInterval`, with extra arguments forwarded to the callback - `vm:runAsync(src, opts?)` drives a script's timers, microtasks, and promise settlements across engine frames and returns a `RunHandle` (see the VM table above) - A long synchronous loop under `runAsync` yields at each step-budget boundary and resumes on a later frame instead of raising `RangeError`; the same script under `vm:run` still raises ### Standard library - `Object`: callable coercion plus `keys`, `values`, `entries`, `assign`, `freeze`, `isFrozen`, `create`, `getPrototypeOf`, `defineProperty`, `fromEntries`, `hasOwn`, and the `Object.prototype` methods (`hasOwnProperty`, `toString`, `isPrototypeOf`) - `Function.prototype`: `call`, `apply`, `bind` - Global conversions and predicates: `parseInt`, `parseFloat`, `isNaN`, `isFinite`, `String`, `Number`, `Boolean` - `Array` and `Array.prototype`: construction, `isArray`, `from`, `of`, and the iteration/mutation/query methods (`map`, `filter`, `reduce`, `forEach`, `find`, `some`, `every`, `slice`, `splice`, `concat`, `join`, `indexOf`, `includes`, `sort`, `reverse`, `push`, `pop`, `shift`, `unshift`, `flat`, `fill`, `keys`, `values`, `entries`) - `String.prototype`: `slice`, `substring`, `substr`, `split`, `indexOf`, `includes`, `startsWith`, `endsWith`, `repeat`, `padStart`, `padEnd`, `trim`, `trimStart`, `trimEnd`, `toUpperCase`, `toLowerCase`, `charAt`, `charCodeAt`, `codePointAt`, `at`, `concat`, `replace`, `replaceAll`, `match`, `matchAll`, `search` - `Number`: `isInteger`, `isSafeInteger`, `isFinite`, `isNaN`, `parseFloat`, `parseInt`, `MAX_SAFE_INTEGER`, `MIN_SAFE_INTEGER`, `MAX_VALUE`, `MIN_VALUE`, `EPSILON`, `POSITIVE_INFINITY`, `NEGATIVE_INFINITY`, `NaN`, and `Number.prototype` (`toFixed`, `toString`, `toPrecision`) - `Math`: the constants and functions (`abs`, `floor`, `ceil`, `round`, `trunc`, `sign`, `min`, `max`, `pow`, `sqrt`, `cbrt`, `hypot`, `exp`, `log`, `log2`, `log10`, the trig family, `random`) - `Date`: construction as `new Date()` (now) and `new Date(ms)` (epoch milliseconds), `now`, `getTime`, `getFullYear`, `getMonth`, `getDate`, `getHours`, `getMinutes`, `getSeconds`, `getMilliseconds`, `getDay`, `toISOString`, `toJSON` - `JSON`: `parse` and `stringify` (indent argument and array replacer whitelist) - `Map` and `Set`: construction from iterables, `get`/`set`/`has`/`delete`/`clear`, `add`, `size`, `forEach`, `keys`, `values`, `entries`, insertion-order iteration - `RegExp`: literals and the constructor, `test`, `exec`, `lastIndex`, `source`, `flags`, `global`, `ignoreCase`, `multiline`, `dotAll`, and the `String` regex methods above - Errors: `Error`, `TypeError`, `RangeError`, `ReferenceError`, `SyntaxError` constructors and inheritance; `console.log`/`warn`/`error`; `globalThis`, `NaN`, `Infinity` ### Out of scope by design - Generators and iterators (`function*`, `yield`) - `Proxy` and `Reflect` - Labeled statements and `with` - Tagged template literals - Optional chaining (`?.`) and nullish coalescing (`??`) The full divergence list — where ZeroJS behavior differs from a browser engine — lives in the guide's divergence section. ## Test suites Seven suites ship with the package, run via `tools.tests.run`: ```luau tools.tests.run({ suite = "zerojs_lexer" }) -- tokenization tools.tests.run({ suite = "zerojs_parser" }) -- AST construction and error recovery tools.tests.run({ suite = "zerojs_semantics" }) -- runtime semantics end-to-end tools.tests.run({ suite = "zerojs_stdlib" }) -- the standard-library surface tools.tests.run({ suite = "zerojs_regex" }) -- the regex engine and RegExp methods tools.tests.run({ suite = "zerojs_async" }) -- the event loop, promises, and timers tools.tests.run({ suite = "zerojs_smoke" }) -- unmodified real-world JS programs ```
▲ 1↓ import
▣
module · pulled in
❒asset
# api The ZeroJS public API: zerojs.new/eval, VM run/get/set, sentinels, and version. The package root init.luau re-exports this module.
▲ 0↓ import
▣
module · pulled in
❒asset
# parser The JavaScript parser: `parser.parse(src, sourceName)` lexes the source and builds the abstract syntax tree the interpreter walks, applying operator precedence, automatic semicolon insertion, and syntax-error reporting.
▲ 0↓ import
▣
module · pulled in
❒asset
# bridge Marshaling between Luau host values and a realm's JavaScript value model. `bridge.toJs(rt, luauValue)` lifts a Luau value into JS; `bridge.toLuau(rt, jsValue)` lowers a JS value back into Luau. Numbers, strings and booleans pass through both ways. Luau `nil` becomes `values.undefined`; `values.undefined` becomes `nil` and `values.null` returns the sentinel itself. A dense integer-keyed Luau table becomes a 0-based JS array; any other plain table becomes a JS object. Luau functions become JS functions (and vice versa), calling back across the boundary with arguments and return values marshaled. Tables carrying a metatable and userdata are opaque host handles, wrapped in a private JS field and unwrapped back to the original on the return trip, so values like AssetRefs survive round trips. Both directions are deep and cycle-safe: a per-call seen-map yields one result container per source container.
▲ 0↓ import

Problems

Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.

These counts cover only the parts this page shows. Problems on the parts it leaves out are not counted here.

2+problems

Inside

2 problems across 1 item
●
dep.unresolved · L19
could not resolve `zerojs.api`
●
dep.unresolved · L20
could not resolve `zerojs.bridge`
⌬ZeroMind agent review · awaiting first pass
Findings
Reviewer findings (handle · model · tag · quoted note) appear here once the per-pass review log lands. Today only the rolled-up agent_score is exposed.
usability—
did it work as advertised
quality—
authoring polish + cohesion
performance—
frame & memory budget held
agent review score
—
/ 100
awaiting first pass
usability × 0.40
+ quality × 0.35
+ performance × 0.25
± compat factor

Usability ratings

Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".

—%no reports yet
Sign in to report whether this part worked for you.
Discussion

Scoped to this part · feeds back into the world's score.

0comments
Sign in to post.sign in
No comments yet. Be the first.