4.0 KiB
@polymech/xblox
Block-based control flow for Polymech: if / else-if, for / while, switch / case / default, runScript, and break, with a nested JSON graph format, Zod validation, and a small Node CLI.
This package is a modern take on ideas from the legacy xblox AMD stack (ref-control-freak/ in this repo). It does not reimplement the full legacy Expression.js pipeline ([variables], {block} substitution, block-store UI); it focuses on execution and schema.
Install
From the monorepo, using npm in this package:
cd packages/xblox
npm install
npm run build
Package exports
| Import | Use |
|---|---|
@polymech/xblox |
Types, enums, ModelBase, RunScriptBlock, engine entrypoints you re-export from app code. No vm2. |
@polymech/xblox/runtime |
runScriptLegacyVm, evalExpression, runRunScriptBlock — Node only, uses vm2. Do not import this path in browser bundles if you must avoid vm2. |
CLI: pm-xblox
After npm run build, the pm-xblox binary runs validated graphs from disk.
npx pm-xblox run --source=./graph.json
npx pm-xblox run --source=./graph.json --output=./out.json
npx pm-xblox run --source=./graph.json --loglevel=info
| Option | Default | Description |
|---|---|---|
--source |
(required) | Path to a blocks JSON file (version: 1). |
--output |
— | If set, writes { "result": … } as JSON. |
--loglevel |
info |
debug · info · warn · error |
Blocks file format
Top-level shape (see src/schema/blocks-file.ts):
{
"version": 1,
"context": {},
"root": { "kind": "…", … }
}
context— plain object merged into the executionthis(expressions and scripts usethislike the legacyapply(ctx, args)model).root— a discriminated union onkind:if,runScript,for,while,switch,break, pluscase/switchDefaultinsideswitch.items.
Legacy note: older UIs often stored a flat array of blocks with id, parentId, declaredClass. This package currently executes the nested tree; you can add an adapter layer (flat → tree) for editors without changing the executor.
Expressions and scripts
- Conditions and
runScript.methodbodies are evaluated viavm2in@polymech/xblox/runtime, following the samenew Function("{ … }").apply(ctx, args)idea as legacy Run Script blocks. - This is not a port of legacy
Expression.js(no[var]substitution,{block}calls, or expression cache). For full parity with that layer, plan a separate module or bridge.
Security: vm2 is unmaintained; treat user scripts as trusted only in controlled environments, or replace the runtime with a stricter evaluator later.
Development
| Script | Description |
|---|---|
npm run build |
tsc → dist-in/ |
npm run dev |
tsc --watch |
npm run clean |
Remove dist/ and dist-in/ |
npm test |
Run Vitest once |
npm run test:watch |
Vitest watch mode |
npm run test:coverage |
Vitest with V8 coverage |
npm run test:ui |
Vitest browser UI |
npm run lint |
ESLint on src/ |
npm run webpack |
After build, emits dist/main_node.js from dist-in/main.js (default package exports only; not @polymech/xblox/runtime / vm2). |
Tests live under tests/; shared paths use tests/test-commons.ts. Fixtures are in tests/fixtures/.
Publishing to npm
package.json→filesis an allowlist: onlydist-in/,README.md, andLICENSEare packed.ref-control-freak/,tests/,src/, webpack/vitest configs, etc. are not published.prepackrunsnpm run build, sodist-in/exists when younpm publishornpm pack.- Dry-run the tarball:
npm pack --dry-run(see “Files included” in the log) ornpm packand inspect the.tgz. dist/(webpack output) is intentionally not infiles; consumers usedist-in/fromtsc.
License
MIT — see LICENSE.