poe-code 14.0.5 → 14.0.6

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.
Files changed (36) hide show
  1. package/dist/index.js +1176 -242
  2. package/dist/index.js.map +4 -4
  3. package/dist/metafile.json +1 -1
  4. package/package.json +1 -1
  5. package/packages/safe-js/dist/chunks/{chunk-B7H4ZL65.js → chunk-2M6MNQBY.js} +1937 -1008
  6. package/packages/safe-js/dist/chunks/chunk-2M6MNQBY.js.map +7 -0
  7. package/packages/safe-js/dist/chunks/{chunk-7B7BKWVF.js → chunk-QSSQJNKE.js} +2 -2
  8. package/packages/safe-js/dist/cli.js +2 -2
  9. package/packages/safe-js/dist/core.d.ts +3 -0
  10. package/packages/safe-js/dist/core.js +5 -1
  11. package/packages/safe-js/dist/extensions.d.ts +42 -0
  12. package/packages/safe-js/dist/extensions.js +83 -0
  13. package/packages/safe-js/dist/index.d.ts +4 -1
  14. package/packages/safe-js/dist/index.js +6 -2
  15. package/packages/safe-js/dist/index.js.map +2 -2
  16. package/packages/safe-js/dist/interp/async.d.ts +1 -0
  17. package/packages/safe-js/dist/interp/globals/object-array.js +5 -0
  18. package/packages/safe-js/dist/interp/globals.d.ts +31 -0
  19. package/packages/safe-js/dist/interp/globals.js +22 -0
  20. package/packages/safe-js/dist/interp/host-bridge.d.ts +14 -1
  21. package/packages/safe-js/dist/interp/host-bridge.js +19 -4
  22. package/packages/safe-js/dist/interp/host-capabilities.d.ts +43 -0
  23. package/packages/safe-js/dist/interp/host-capabilities.js +123 -0
  24. package/packages/safe-js/dist/interp/interpreter.d.ts +4 -0
  25. package/packages/safe-js/dist/interp/interpreter.js +22 -4
  26. package/packages/safe-js/dist/interp/object-model.js +7 -0
  27. package/packages/safe-js/dist/interp/values.d.ts +1 -0
  28. package/packages/safe-js/dist/interp/values.js +15 -0
  29. package/packages/safe-js/dist/modules/registry.d.ts +3 -1
  30. package/packages/safe-js/dist/modules/registry.js +2 -1
  31. package/packages/safe-js/dist/realm.d.ts +43 -0
  32. package/packages/safe-js/dist/realm.js +713 -0
  33. package/packages/safe-js/dist/run.d.ts +5 -1
  34. package/packages/safe-js/dist/run.js +6 -35
  35. package/packages/safe-js/dist/chunks/chunk-B7H4ZL65.js.map +0 -7
  36. /package/packages/safe-js/dist/chunks/{chunk-7B7BKWVF.js.map → chunk-QSSQJNKE.js.map} +0 -0
@@ -0,0 +1,123 @@
1
+ import { readDataRecord } from "../extensions.js";
2
+ const hostObjects = new WeakMap();
3
+ const guestObjects = new WeakMap();
4
+ const guestCallbacks = new WeakMap();
5
+ export function createLiveHostObject(definition, controller) {
6
+ const input = readDataRecord(definition, "Host object definition");
7
+ if (Object.keys(input).some((key) => key !== "properties" && key !== "methods"))
8
+ throw new TypeError("Unknown host object definition field.");
9
+ const properties = new Map();
10
+ for (const [name, inputProperty] of Object.entries(readDataRecord(input.properties ?? {}, "Host properties"))) {
11
+ const property = readDataRecord(inputProperty, `Host property '${name}'`);
12
+ if (Object.keys(property).some((key) => key !== "get" && key !== "set"))
13
+ throw new TypeError("Unknown host property field.");
14
+ if ((property.get !== undefined && typeof property.get !== "function") ||
15
+ (property.set !== undefined && typeof property.set !== "function"))
16
+ throw new TypeError("Host property operations must be functions.");
17
+ properties.set(name, property);
18
+ }
19
+ const operations = readDataRecord(input.methods ?? {}, "Host methods");
20
+ for (const [name, operation] of Object.entries(operations)) {
21
+ if (typeof operation !== "function")
22
+ throw new TypeError("Host methods must be functions.");
23
+ if (properties.has(name))
24
+ throw new TypeError(`Conflicting host member '${name}'.`);
25
+ }
26
+ for (const name of [...properties.keys(), ...Object.keys(operations)]) {
27
+ if (["constructor", "prototype", "__proto__"].includes(name))
28
+ throw new TypeError(`Reserved host member '${name}'.`);
29
+ }
30
+ controller.assertActive();
31
+ controller.chargeWork(properties.size + Object.keys(operations).length + 1);
32
+ const host = Object.freeze(Object.create(null));
33
+ const guest = Object.freeze(Object.create(null));
34
+ const methods = new Map(Object.entries(operations).map(([name, operation]) => [
35
+ name,
36
+ controller.method(operation)
37
+ ]));
38
+ const state = { host, guest, controller, properties, methods };
39
+ hostObjects.set(host, state);
40
+ guestObjects.set(guest, state);
41
+ return host;
42
+ }
43
+ export function isGuestHostObject(value) {
44
+ return typeof value === "object" && value !== null && guestObjects.has(value);
45
+ }
46
+ export function isLiveCapability(value) {
47
+ return (((typeof value === "object" && value !== null) || typeof value === "function") &&
48
+ (hostObjects.has(value) || guestObjects.has(value) || guestCallbacks.has(value)));
49
+ }
50
+ export function importHostCapability(value, owner) {
51
+ const object = hostObjects.get(value);
52
+ if (object !== undefined) {
53
+ if (object.controller.owner !== owner)
54
+ throw new TypeError("Foreign realm host capability.");
55
+ object.controller.assertActive();
56
+ return object.guest;
57
+ }
58
+ const callback = guestCallbacks.get(value);
59
+ if (callback !== undefined) {
60
+ if (callback.owner !== owner)
61
+ throw new TypeError("Foreign realm guest callback.");
62
+ callback.assertActive();
63
+ if (callback.closure === undefined)
64
+ throw new TypeError("Guest callback is revoked.");
65
+ return callback.closure;
66
+ }
67
+ throw new TypeError("Unsupported live capability conversion.");
68
+ }
69
+ export function exportHostCapability(value, owner) {
70
+ const state = guestObjects.get(value);
71
+ if (state === undefined || state.controller.owner !== owner)
72
+ throw new TypeError("Foreign realm host capability.");
73
+ state.controller.assertActive();
74
+ return state.host;
75
+ }
76
+ export function registerGuestCallback(callback, state) {
77
+ guestCallbacks.set(callback, state);
78
+ }
79
+ export function readGuestCallback(callback, owner) {
80
+ const state = typeof callback === "function" ? guestCallbacks.get(callback) : undefined;
81
+ if (state === undefined || state.owner !== owner)
82
+ throw new TypeError("Foreign or invalid guest callback.");
83
+ state.assertActive();
84
+ if (state.closure === undefined)
85
+ throw new TypeError("Guest callback is revoked.");
86
+ return state.closure;
87
+ }
88
+ export function revokeGuestCallback(callback, owner) {
89
+ const state = guestCallbacks.get(callback);
90
+ if (state === undefined || state.owner !== owner)
91
+ throw new TypeError("Foreign guest callback.");
92
+ state.closure = undefined;
93
+ }
94
+ export function revokeHostObject(value, owner) {
95
+ const state = hostObjects.get(value);
96
+ if (state === undefined || state.controller.owner !== owner)
97
+ throw new TypeError("Foreign host object.");
98
+ state.properties.clear();
99
+ state.methods.clear();
100
+ }
101
+ export function getHostObjectMember(value, key) {
102
+ const state = guestObjects.get(value);
103
+ state.controller.assertActive();
104
+ state.controller.chargeWork();
105
+ const property = state.properties.get(key);
106
+ if (property !== undefined)
107
+ return property.get === undefined ? undefined : state.controller.read(property.get);
108
+ return state.methods.get(key);
109
+ }
110
+ export function setHostObjectMember(value, key, entry) {
111
+ const state = guestObjects.get(value);
112
+ state.controller.assertActive();
113
+ state.controller.chargeWork();
114
+ const property = state.properties.get(key);
115
+ if (property?.set === undefined)
116
+ throw new TypeError(`Host property '${key}' is not writable.`);
117
+ state.controller.write(property.set, entry);
118
+ }
119
+ export function getHostObjectKeys(value) {
120
+ const state = guestObjects.get(value);
121
+ state.controller.assertActive();
122
+ return [...state.properties.keys(), ...state.methods.keys()];
123
+ }
@@ -1,3 +1,4 @@
1
+ import { SandboxJobQueue } from "./jobs.js";
1
2
  import type { ParseResult, SourceSpan } from "../parse.js";
2
3
  import { type AsyncEvaluationContext, type AsyncEvaluationResult, type InterpreterYieldPoint } from "./async.js";
3
4
  import type { GeneratorCompletion } from "./generator.js";
@@ -43,6 +44,9 @@ export type InterpreterResult = {
43
44
  stats: InterpreterStats;
44
45
  };
45
46
  export type InterpretOptions = {
47
+ assertActive?: () => void;
48
+ jobs?: SandboxJobQueue;
49
+ nested?: boolean;
46
50
  compilation?: CompileScope;
47
51
  compileOwner?: CompileOwner;
48
52
  captureReplayState?: () => unknown;
@@ -1,6 +1,7 @@
1
1
  import { promiseReplayContext } from "./promise-replay.js";
2
+ import { getHostObjectKeys, getHostObjectMember, isGuestHostObject, setHostObjectMember } from "./host-capabilities.js";
2
3
  import { assertPromiseExecutionAllowed } from "./promise-tracker.js";
3
- import { SandboxJobQueue, suspendJob } from "./jobs.js";
4
+ import { SandboxJobQueue, runAsyncPrefix, suspendJob } from "./jobs.js";
4
5
  import { withCancellationSignal } from "./cancel.js";
5
6
  import { attachErrorSpan, formatErrorStack, readErrorSpan, replaceErrorStack } from "../error/shape.js";
6
7
  import { evaluateArrowFunctionExpression, evaluateFunctionExpression, evaluateAwaitExpression, emitResumeBreakpoint, createInterpretedClosure, normalizeClosureResult } from "./async.js";
@@ -98,9 +99,10 @@ export async function interpret(node, options = {}) {
98
99
  peakDataSize: { enumerable: false, value: 0, writable: true }
99
100
  });
100
101
  const activeLoopIterations = new Map();
101
- const jobs = new SandboxJobQueue();
102
+ const jobs = options.jobs ?? new SandboxJobQueue();
102
103
  hoistVarDeclarations(node, scope);
103
104
  const context = {
105
+ assertActive: options.assertActive,
104
106
  compilation,
105
107
  budget,
106
108
  callStack: [],
@@ -119,8 +121,9 @@ export async function interpret(node, options = {}) {
119
121
  generatorYield: options.generatorYield,
120
122
  resumeTarget: { nodeId: options.snapshot?.resumeNodeId }
121
123
  };
122
- const evaluation = await withCancellationSignal(options.signal, () => jobs.run(() => evaluateNode(node, context)));
123
- await jobs.drain();
124
+ const evaluation = await withCancellationSignal(options.signal, () => options.nested ? runAsyncPrefix(() => evaluateNode(node, context)) : jobs.run(() => evaluateNode(node, context)));
125
+ if (!options.nested)
126
+ await jobs.drain();
124
127
  const snapshot = scope.snapshot();
125
128
  reconcileDataBudget(budget, stats, scope, "hasValue" in evaluation && evaluation.hasValue ? evaluation.value : undefined, compilation, options.compilation);
126
129
  if (evaluation.kind === "error") {
@@ -167,6 +170,7 @@ export async function interpret(node, options = {}) {
167
170
  }
168
171
  export { Scope } from "./scope.js";
169
172
  async function evaluateNode(node, context) {
173
+ context.assertActive?.();
170
174
  const replayWait = promiseReplayContext.getStore()?.beforeNode(node.nodeId);
171
175
  if (replayWait !== undefined)
172
176
  await suspendJob(replayWait);
@@ -1119,6 +1123,8 @@ function forInObject(value) {
1119
1123
  return undefined;
1120
1124
  }
1121
1125
  function forInKeys(object, budget) {
1126
+ if (isGuestHostObject(object))
1127
+ return getHostObjectKeys(object);
1122
1128
  const keys = [];
1123
1129
  const seen = new Set();
1124
1130
  let depth = 0;
@@ -1140,6 +1146,8 @@ function forInKeys(object, budget) {
1140
1146
  return keys;
1141
1147
  }
1142
1148
  function hasForInProperty(object, key, budget) {
1149
+ if (isGuestHostObject(object))
1150
+ return getHostObjectKeys(object).includes(key);
1143
1151
  let depth = 0;
1144
1152
  for (let current = object; current !== null; current = getSandboxPrototype(current)) {
1145
1153
  if (depth > 0)
@@ -1630,6 +1638,8 @@ async function evaluateMemberExpression(node, context) {
1630
1638
  };
1631
1639
  }
1632
1640
  function getPropertyValue(target, property, context) {
1641
+ if (isGuestHostObject(target))
1642
+ return getHostObjectMember(target, String(property));
1633
1643
  if (typeof target === "string")
1634
1644
  return getStringMember(target, property, context.budget);
1635
1645
  if (typeof target === "number")
@@ -2284,6 +2294,8 @@ function isPlainSandboxObject(value) {
2284
2294
  !isSandboxRegex(value));
2285
2295
  }
2286
2296
  function getMemberValue(target, property, context) {
2297
+ if (isGuestHostObject(target))
2298
+ return getHostObjectMember(target, String(property));
2287
2299
  let current = target;
2288
2300
  let depth = 0;
2289
2301
  while (typeof current === "object" && current !== null) {
@@ -2311,6 +2323,10 @@ function getArrayMemberValue(target, property, context) {
2311
2323
  return getArrayMember(target, property, createArrayMethodOptions(context));
2312
2324
  }
2313
2325
  export function setSandboxProperty(target, property, value, budget) {
2326
+ if (isGuestHostObject(target)) {
2327
+ setHostObjectMember(target, String(property), value);
2328
+ return;
2329
+ }
2314
2330
  const prototypeOwner = target;
2315
2331
  if (isGuestClosure(target))
2316
2332
  target = materializeFunctionProperties(target);
@@ -2359,6 +2375,8 @@ export function setSandboxProperty(target, property, value, budget) {
2359
2375
  }
2360
2376
  }
2361
2377
  function deleteSandboxProperty(target, property) {
2378
+ if (isGuestHostObject(target))
2379
+ throw new TypeError("Live host properties cannot be deleted.");
2362
2380
  if (isGuestClosure(target))
2363
2381
  target = materializeFunctionProperties(target);
2364
2382
  if (Array.isArray(target)) {
@@ -1,4 +1,5 @@
1
1
  import { assertSandboxDataDepth } from "../graph-depth.js";
2
+ import { getHostObjectMember, isGuestHostObject, isLiveCapability } from "./host-capabilities.js";
2
3
  import { isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet } from "./values.js";
3
4
  const guestClosures = new WeakSet();
4
5
  const functionProperties = new WeakMap();
@@ -62,6 +63,8 @@ export function getSandboxDataProperty(value, key, budget) {
62
63
  let current = value;
63
64
  let depth = 0;
64
65
  while (typeof current === "object" && current !== null) {
66
+ if (isGuestHostObject(current))
67
+ return getHostObjectMember(current, String(key));
65
68
  if (isGuestClosure(current))
66
69
  return getGuestFunctionProperty(current, String(key));
67
70
  if (isSandboxClosure(current)) {
@@ -108,6 +111,8 @@ export function setSandboxPrototype(value, prototype, budget) {
108
111
  prototypes.set(value, prototype);
109
112
  }
110
113
  function isPrototypeRecord(value) {
114
+ if (isGuestHostObject(value))
115
+ return false;
111
116
  if (isSandboxClosure(value) ||
112
117
  isSandboxGenerator(value) ||
113
118
  isSandboxMap(value) ||
@@ -125,6 +130,8 @@ export function hasManagedDescriptors(value) {
125
130
  return descriptorObjects.has(value);
126
131
  }
127
132
  export function hasGuestObjectState(value) {
133
+ if (isLiveCapability(value))
134
+ return true;
128
135
  if (functionProperties.has(value) || prototypes.has(value))
129
136
  return true;
130
137
  return (descriptorObjects.has(value) &&
@@ -86,6 +86,7 @@ export type SandboxGenerator = {
86
86
  };
87
87
  type CopyFromSandboxOptions = {
88
88
  wrapClosure?: (value: SandboxClosure) => unknown;
89
+ unwrapHostObject?: (value: SandboxObject) => unknown;
89
90
  compilation?: CompileScope;
90
91
  };
91
92
  export declare function createSandboxClosure(input: {
@@ -1,4 +1,5 @@
1
1
  import { bindOtelSpan, getBoundOtelSpan } from "../observability/otel.js";
2
+ import { getHostObjectKeys, getHostObjectMember, isGuestHostObject, isLiveCapability } from "./host-capabilities.js";
2
3
  import { types as nodeTypes } from "node:util";
3
4
  import { CompileScope, RegexCompileGuard, regexCompiledData } from "./regex/compile-guard.js";
4
5
  import { copyFloat32Storage, float32DataProperties, float32Storage, isFloat32Array } from "./float32.js";
@@ -70,6 +71,8 @@ export function createSandboxClosure(input) {
70
71
  return Object.freeze(closure);
71
72
  }
72
73
  export function ownEnumerableSandboxEntries(value) {
74
+ if (isGuestHostObject(value))
75
+ return getHostObjectKeys(value).map(key => [key, getHostObjectMember(value, key)]);
73
76
  if (value === null || value === undefined)
74
77
  throw new TypeError("Cannot convert undefined or null to object.");
75
78
  if (isGuestClosure(value))
@@ -222,6 +225,11 @@ export function measureSandboxData(values, options = {}) {
222
225
  assertSandboxDataDepth(depth);
223
226
  seen.add(value);
224
227
  usage += 1;
228
+ if (isGuestHostObject(value)) {
229
+ for (const key of getHostObjectKeys(value))
230
+ usage += key.length + 1;
231
+ return;
232
+ }
225
233
  const prototype = getSandboxPrototype(value);
226
234
  if (prototype !== null)
227
235
  visit(prototype, depth + 1);
@@ -376,6 +384,8 @@ function copyToSandbox(value, state, path = "<root>", cloneSandboxCollections =
376
384
  if (isSandboxPrimitive(value)) {
377
385
  return value;
378
386
  }
387
+ if (isLiveCapability(value))
388
+ throw new TypeError("Live capabilities require their owning realm bridge.");
379
389
  if (isSandboxClosure(value) ||
380
390
  isSandboxGenerator(value) ||
381
391
  isSandboxRegex(value) ||
@@ -508,6 +518,11 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
508
518
  if (isSandboxPrimitive(value)) {
509
519
  return value;
510
520
  }
521
+ if (isGuestHostObject(value)) {
522
+ if (options.unwrapHostObject === undefined)
523
+ throw new TypeError("Live capabilities require their owning realm bridge.");
524
+ return options.unwrapHostObject(value);
525
+ }
511
526
  if (nodeTypes.isProxy(value))
512
527
  throw new TypeError("Unsupported proxy sandbox value.");
513
528
  if (!isSandboxClosure(value) && hasGuestObjectState(value)) {
@@ -1,6 +1,6 @@
1
1
  import type { Budget, CompileOwner } from "../interp/budget.js";
2
2
  import type { HostCallJournal } from "../interp/host-call.js";
3
- import { type CallerInjectedBinding } from "../interp/host-bridge.js";
3
+ import { type RealmBridge, type CallerInjectedBinding } from "../interp/host-bridge.js";
4
4
  import type { SandboxValue } from "../interp/values.js";
5
5
  import type { Module } from "../parse/parser.js";
6
6
  export type ModuleExports = ReadonlyMap<string, CallerInjectedBinding> | Record<string, CallerInjectedBinding>;
@@ -9,6 +9,8 @@ export declare function createUnknownModuleMessage(moduleName: string, moduleNam
9
9
  export declare function createUnknownExportMessage(moduleName: string, exportName: string, availableExports: readonly string[]): string;
10
10
  export declare function resolveModuleImports(module: Module, modules: ModuleRegistry | undefined, options: {
11
11
  budget: Budget;
12
+ realm?: RealmBridge;
13
+ wrappedModules?: Map<string, Record<string, SandboxValue>>;
12
14
  compileOwner?: CompileOwner;
13
15
  hostCalls?: HostCallJournal;
14
16
  signal?: AbortSignal;
@@ -17,7 +17,7 @@ export function createUnknownExportMessage(moduleName, exportName, availableExpo
17
17
  export function resolveModuleImports(module, modules, options) {
18
18
  const registry = normalizeModuleRegistry(modules);
19
19
  const bindings = createBindingRecord();
20
- const wrappedModules = new Map();
20
+ const wrappedModules = options.wrappedModules ?? new Map();
21
21
  for (const statement of module.body) {
22
22
  if (statement.type !== "ImportDeclaration") {
23
23
  continue;
@@ -36,6 +36,7 @@ function bindImportDeclaration(declaration, registry, wrappedModules, bindings,
36
36
  }
37
37
  const wrappedExports = wrappedModules.get(moduleName) ??
38
38
  createBindingRecord(wrapCancelableBindings(wrapCallerInjectedBindings(Object.fromEntries(moduleExports), {
39
+ realm: options.realm,
39
40
  budget: options.budget,
40
41
  compileOwner: options.compileOwner,
41
42
  hostCalls: options.hostCalls,
@@ -0,0 +1,43 @@
1
+ import { Budget } from "./interp/budget.js";
2
+ import { type InterpreterResult } from "./interp/interpreter.js";
3
+ import { type CallerInjectedBinding } from "./interp/host-bridge.js";
4
+ import { type CallbackOptions, type SafeJSExtension } from "./extensions.js";
5
+ import { type ModuleRegistry } from "./modules/registry.js";
6
+ import type { ConsoleSink } from "./interp/globals/console-json.js";
7
+ import type { RunOptions, RunResult } from "./run.js";
8
+ export type RealmLimits = {
9
+ extensions?: number;
10
+ hostObjects?: number;
11
+ callbacks?: number;
12
+ cleanups?: number;
13
+ nestedEvaluations?: number;
14
+ };
15
+ export type RealmOptions = {
16
+ bindings?: Record<string, CallerInjectedBinding>;
17
+ modules?: ModuleRegistry;
18
+ extensions?: readonly SafeJSExtension[];
19
+ grants?: readonly string[];
20
+ budget?: Budget;
21
+ signal?: AbortSignal;
22
+ sink?: ConsoleSink;
23
+ randomSeed?: number;
24
+ limits?: RealmLimits;
25
+ };
26
+ export type RealmResult = (Omit<Extract<InterpreterResult, {
27
+ ok: true;
28
+ }>, "snapshot" | "returnValue"> & {
29
+ returnValue?: unknown;
30
+ }) | Omit<Extract<InterpreterResult, {
31
+ ok: false;
32
+ }>, "snapshot">;
33
+ export type SafeJSRealm = {
34
+ readonly extensions: readonly SafeJSExtension["manifest"][];
35
+ evaluate(source: string, options?: {
36
+ filename?: string;
37
+ }): Promise<RealmResult>;
38
+ invokeCallback(callback: unknown, options?: CallbackOptions): Promise<unknown>;
39
+ releaseCallback(callback: unknown): void;
40
+ close(): Promise<void>;
41
+ };
42
+ export declare function createRealm(options?: RealmOptions): SafeJSRealm;
43
+ export declare function runWithExtensions(source: string, options: RunOptions): Promise<RunResult>;