@poe-platform/safe-js 0.1.21 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -1
- package/dist/safe-js/chunks/{chunk-B7H4ZL65.js → chunk-MXUOEOBE.js} +2037 -1010
- package/dist/safe-js/chunks/chunk-MXUOEOBE.js.map +7 -0
- package/dist/safe-js/chunks/{chunk-GMDOPMVZ.js → chunk-X32RVLSM.js} +2 -2
- package/dist/safe-js/cli.js +2 -2
- package/dist/safe-js/core.d.ts +3 -0
- package/dist/safe-js/core.js +5 -1
- package/dist/safe-js/extensions.d.ts +44 -0
- package/dist/safe-js/index.d.ts +4 -1
- package/dist/safe-js/index.js +6 -2
- package/dist/safe-js/index.js.map +2 -2
- package/dist/safe-js/interp/async.d.ts +1 -0
- package/dist/safe-js/interp/host-bridge.d.ts +18 -1
- package/dist/safe-js/interp/host-capabilities.d.ts +50 -0
- package/dist/safe-js/interp/interpreter.d.ts +4 -0
- package/dist/safe-js/interp/jobs.d.ts +20 -0
- package/dist/safe-js/interp/values.d.ts +1 -0
- package/dist/safe-js/modules/registry.d.ts +3 -1
- package/dist/safe-js/realm.d.ts +45 -0
- package/dist/safe-js/run.d.ts +5 -1
- package/package.json +2 -2
- package/dist/safe-js/chunks/chunk-B7H4ZL65.js.map +0 -7
- /package/dist/safe-js/chunks/{chunk-GMDOPMVZ.js.map → chunk-X32RVLSM.js.map} +0 -0
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ console.log(result.returnValue);
|
|
|
27
27
|
|
|
28
28
|
`run()` takes source text, not a file path. Success returns `ok`, `returnValue`, `snapshot`, and `stats`. Handle both an `ok: false` result and a rejected promise: parsing, budget exhaustion, cancellation, and some execution failures can reject. Top-level `await` in this example lets rejections reach Node.
|
|
29
29
|
|
|
30
|
-
`@poe-platform/safe-js/core` exposes `run`, `lint`, `Budget`, and replayable-random helpers. The shared filesystem lives in `@poe-platform/safe-fs`, with a portable `/core` entry. Existing `@poe-platform/safe-js/fs`, `/fs/core`, and `/fs/node` imports re-export it. Legacy `poe-code/safe-js` imports remain available through the CLI package but use a separate runtime; keep factories and errors within one import family.
|
|
30
|
+
`@poe-platform/safe-js/core` exposes `run`, `createRealm`, `defineExtension`, `lint`, `Budget`, and replayable-random helpers. The shared filesystem lives in `@poe-platform/safe-fs`, with a portable `/core` entry. Existing `@poe-platform/safe-js/fs`, `/fs/core`, and `/fs/node` imports re-export it. Legacy `poe-code/safe-js` imports remain available through the CLI package but use a separate runtime; keep factories and errors within one import family.
|
|
31
31
|
|
|
32
32
|
## Supported features
|
|
33
33
|
|
|
@@ -35,6 +35,7 @@ console.log(result.returnValue);
|
|
|
35
35
|
- **Guest function objects:** own properties on functions and arrows; ordinary constructors with shared prototypes, inherited methods and `instanceof`. `Object.create`, `getPrototypeOf`, `setPrototypeOf`, own-property inspection, and data descriptors work on ordinary sandbox records.
|
|
36
36
|
- **Data processing:** arrays, objects, strings, numbers, JSON, Math, Map, Set, Float32Array, promises, and a bounded regular-expression subset. These are selected APIs, not complete ECMAScript implementations.
|
|
37
37
|
- **Explicit capabilities:** named, default, and namespace imports resolve against host-supplied modules. Optional helpers cover agents, MCP tools, files, environment reads, time, logging, and metrics.
|
|
38
|
+
- **Persistent realms:** keep guest state across evaluations; register trusted extensions with explicit grants, live host objects, revocable callbacks, and ordered cleanup.
|
|
38
39
|
- **Execution controls:** step, call-depth, string, array, and retained-data budgets; an absolute deadline; host cancellation; console and telemetry sinks.
|
|
39
40
|
- **Checkpoints:** capture execution state, restore compatible source, and reconcile pending host operations. Changed programs can use explicit continuation migration.
|
|
40
41
|
- **Authoring tools:** lint diagnostics and fixes, source-positioned errors, Markdown harnesses, and paired Markdown/script files. `run()` does not lint automatically; harness runners do.
|
|
@@ -91,6 +92,92 @@ console.log(result.returnValue);
|
|
|
91
92
|
|
|
92
93
|
The lint registry describes exports; the runtime registry supplies their values. Both accept records or Maps. Module names are host-defined identifiers, not file paths or npm packages. Validate arguments and enforce permissions inside each host operation. Adding a function does not make its effects safe to replay.
|
|
93
94
|
|
|
95
|
+
## Keep state between evaluations
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
import { Budget, createRealm } from "@poe-platform/safe-js/core";
|
|
99
|
+
|
|
100
|
+
const realm = createRealm({ budget: new Budget({ maxSteps: 10_000 }) });
|
|
101
|
+
try {
|
|
102
|
+
await realm.evaluate("let total = 1;");
|
|
103
|
+
const result = await realm.evaluate("return ++total;");
|
|
104
|
+
if (!result.ok) throw new Error(result.error.message);
|
|
105
|
+
console.log(result.returnValue);
|
|
106
|
+
} finally {
|
|
107
|
+
await realm.close();
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
This prints `2`. Evaluations share declarations, closures and object identity without rerunning earlier source. Budgets are cumulative. `evaluate(source, { filename? })` returns `ok`, `returnValue` or `error`, and `stats`; it can also reject. Concurrent evaluations are rejected. Deferred callbacks can run while guest code awaits their result; overlapping invocation of the same callback is rejected. Close cancels pending work, revokes capabilities and awaits cleanup; repeated close does not rerun cleanup. Unhandled execution failures also close the realm.
|
|
112
|
+
|
|
113
|
+
`createRealm(options?)` accepts `bindings`, `modules`, `budget`, `signal`, `sink` and `randomSeed` as described below, plus:
|
|
114
|
+
|
|
115
|
+
| Option | Purpose / default |
|
|
116
|
+
| --- | --- |
|
|
117
|
+
| `extensions` | Explicit `defineExtension(...)` registrations; `[]`. Setup runs once, on first evaluation, not on construction or unused close. |
|
|
118
|
+
| `grants` | Granted capability names; `[]`. Every requested capability must be granted before any extension setup runs. |
|
|
119
|
+
| `limits` | Positive integer caps: `extensions: 32`, `hostObjects: 1024`, `callbacks: 1024`, `guestReferences: 1024`, `cleanups: 1024`, `nestedEvaluations: 16`. Collection budgets also apply. |
|
|
120
|
+
|
|
121
|
+
Ordinary host arguments/results are still copied. To preserve live native identity, explicitly create a host object. A guest function crossing to the host becomes an opaque callback: invoke it with `realm.invokeCallback(callback, { thisValue?, args? })`, then `realm.releaseCallback(callback)` when no longer needed. Callbacks and live objects cannot cross realms or survive close. For deferred arguments that must preserve guest identity, opt into retained references as described below.
|
|
122
|
+
|
|
123
|
+
<details>
|
|
124
|
+
<summary>Trusted extensions and live host objects</summary>
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
import { createRealm, defineExtension } from "@poe-platform/safe-js/core";
|
|
128
|
+
|
|
129
|
+
const counter = defineExtension({
|
|
130
|
+
manifest: {
|
|
131
|
+
version: 1,
|
|
132
|
+
name: "counter",
|
|
133
|
+
capabilities: ["counter-state"],
|
|
134
|
+
globals: ["counter"]
|
|
135
|
+
},
|
|
136
|
+
setup(context) {
|
|
137
|
+
let value = 0;
|
|
138
|
+
return { globals: {
|
|
139
|
+
counter: context.createHostObject({
|
|
140
|
+
properties: { value: { get: () => value } },
|
|
141
|
+
methods: { increment: () => ++value }
|
|
142
|
+
})
|
|
143
|
+
} };
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const realm = createRealm({ extensions: [counter], grants: ["counter-state"] });
|
|
148
|
+
try {
|
|
149
|
+
await realm.evaluate("counter.increment();");
|
|
150
|
+
console.log((await realm.evaluate("return counter.value;")).returnValue);
|
|
151
|
+
} finally {
|
|
152
|
+
await realm.close();
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The manifest requires `version: 1` and a nonempty `name`. Optional `capabilities` and `globals` are name arrays; `modules` maps module names to export-name arrays. Synchronous `setup(context)` returns `{ globals?, modules? }` matching those declarations exactly. Module exports use the existing record/Map registry. Duplicate names, incompatible versions, missing grants and conflicts with intrinsics or caller values are rejected before setup. Accessor-based declarations and asynchronous factories are unsupported.
|
|
157
|
+
|
|
158
|
+
| Context member | Contract |
|
|
159
|
+
| --- | --- |
|
|
160
|
+
| `signal` | Realm cancellation signal; aborted on close or failure. |
|
|
161
|
+
| `onCleanup(fn)` | Register a sync/async disposer. Cleanup runs in reverse order, awaits every disposer, and reports failures without skipping the rest. |
|
|
162
|
+
| `chargeWork(units = 1)` | Charge a nonnegative integer against the shared execution budget. Fatal exhaustion cannot be swallowed to continue execution. |
|
|
163
|
+
| `createHostObject({ properties?, methods? })` | Create a realm-owned capability. Properties declare synchronous `get`/`set` functions; methods are host functions. Undeclared members expose no native prototype. |
|
|
164
|
+
| `invokeCallback(callback, { thisValue?, args? })` | Invoke a captured guest function with the realm's state, cancellation and budgets. Same operation as on the realm. |
|
|
165
|
+
| `releaseCallback(callback)` | Revoke the callback and release its retained guest state. |
|
|
166
|
+
| `retainGuestArguments(operation, from)` | During setup, opt an operation into opaque argument references starting at the zero-based index `from`. Requires declared and granted `guest:retain`. Earlier arguments keep normal conversion; live host methods preserve the declaration. |
|
|
167
|
+
| `releaseGuestReference(reference)` | Revoke one reference and release its retained state. Also available on the realm. |
|
|
168
|
+
| `nestedOperation(fn)` | During setup, mark a host operation authorized to run nested source. Requires declared and granted `source:nested`. |
|
|
169
|
+
| `evaluateNested(source)` | Only inside that extension's authorized operation. Completes before the enclosing call returns to guest code, shares scope/budgets, and propagates errors. Parallel nested evaluations and ordinary source reentry are rejected. |
|
|
170
|
+
|
|
171
|
+
For a timer-shaped `schedule(callback, delay, ...args)`, register `context.retainGuestArguments(schedule, 2)`. The host receives normal callback/delay values and opaque `GuestReference` handles for the remaining arguments. Pass those handles to `context.invokeCallback(callback, { args })` to recover the original guest objects and observe mutations made after scheduling. References also work as callback receivers and host return values, including cycles, closures, primitives and live host objects.
|
|
172
|
+
|
|
173
|
+
Release each reference when the host no longer needs it; returning it does not release it. Retained graphs count against data budgets and `limits.guestReferences`. Synchronous native failure releases references captured for that call; asynchronous operations must release theirs in host cleanup. Close revokes all remaining references. Handles cannot be inspected, used in another realm, or serialized into replay/error data. Unmarked operations still copy values.
|
|
174
|
+
|
|
175
|
+
Live objects do not support native prototypes, property-descriptor manipulation or portable serialization. Realm state is not a checkpoint: snapshot/replay and live-capability error-data conversion are rejected. Extensions are trusted native code; grants are a registration contract, not OS isolation. Native work still needs host timeouts and external process supervision for hard limits. No DOM, timers or browser engine are bundled.
|
|
176
|
+
|
|
177
|
+
For one-shot use, `run(source, { extensions, grants, ... })` accepts the same realm options plus `filename`, returns data only, and closes resources before settling. Run-only features such as snapshots, `entryPointArgs`, `importMeta`, custom clocks/random generators and telemetry are rejected in this mode rather than silently ignored.
|
|
178
|
+
|
|
179
|
+
</details>
|
|
180
|
+
|
|
94
181
|
## Options
|
|
95
182
|
|
|
96
183
|
### Execution
|
|
@@ -101,6 +188,7 @@ The lint registry describes exports; the runtime registry supplies their values.
|
|
|
101
188
|
| --- | --- |
|
|
102
189
|
| `bindings` | Global input values and host functions; none by default. |
|
|
103
190
|
| `modules` | Module names mapped to export records or Maps; none by default. |
|
|
191
|
+
| `extensions`, `grants`, `limits` | Opt into a one-shot extension realm; see the supported options and lifetime rules above. |
|
|
104
192
|
| `budget` | A `Budget` instance. Without one, only the default call-depth limit of 1,000 is configured. |
|
|
105
193
|
| `signal` | Host `AbortSignal` for cancellation. |
|
|
106
194
|
| `filename` | Diagnostic filename; defaults to `<input>`. |
|