poe-code 4.0.72 → 5.0.0

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.
@@ -1,4 +1,5 @@
1
- import { SandboxError } from "./budget.js";
1
+ import { createSubsetErrorValue } from "./exceptions.js";
2
+ import { runPromiseJob } from "./jobs.js";
2
3
  import { observeSandboxPromise } from "./promise-tracker.js";
3
4
  import { createSandboxClosure, createSandboxPromise, isSandboxClosure, isSandboxPromise } from "./values.js";
4
5
  export function createPromiseGlobals(options) {
@@ -29,11 +30,13 @@ export function createPromiseGlobals(options) {
29
30
  }),
30
31
  any: createSandboxClosure({
31
32
  async: true,
32
- call: ([values]) => createSandboxPromise(settleIterable(values, (entries) => Promise.any(entries).catch((error) => Promise.reject(budgetSandboxValue({
33
- errors: Array.isArray(error.errors) ? error.errors : [],
34
- message: "All promises were rejected",
35
- name: "AggregateError"
36
- }, options.budget))), options.budget)),
33
+ call: ([values]) => createSandboxPromise(settleIterable(values, (entries) => Promise.any(entries).catch((error) => {
34
+ const aggregate = createSubsetErrorValue("AggregateError", "All promises were rejected", [], options.budget);
35
+ aggregate.errors = Array.isArray(error.errors)
36
+ ? error.errors
37
+ : [];
38
+ throw budgetSandboxValue(aggregate, options.budget);
39
+ }), options.budget)),
37
40
  name: "any"
38
41
  }),
39
42
  resolve: createSandboxClosure({
@@ -51,13 +54,13 @@ export function createPromiseGlobals(options) {
51
54
  }
52
55
  };
53
56
  }
54
- export function getPromiseMember(target, property, budget, enqueue = (task) => Promise.resolve().then(task)) {
57
+ export function getPromiseMember(target, property, budget) {
55
58
  if (property === "then") {
56
59
  return createSandboxClosure({
57
60
  async: true,
58
61
  call: ([onFulfilled, onRejected]) => {
59
62
  observeSandboxPromise(target);
60
- const chained = createSandboxPromise(enqueue(() => target.promise.then((value) => runPromiseReaction(onFulfilled, value, "fulfilled", budget, chained), (reason) => runPromiseReaction(onRejected, reason, "rejected", budget, chained))));
63
+ const chained = createSandboxPromise(target.promise.then((value) => runPromiseReaction(onFulfilled, value, "fulfilled", budget, chained), (reason) => runPromiseReaction(onRejected, reason, "rejected", budget, chained)));
61
64
  return chained;
62
65
  },
63
66
  name: "then"
@@ -68,7 +71,7 @@ export function getPromiseMember(target, property, budget, enqueue = (task) => P
68
71
  async: true,
69
72
  call: ([onRejected]) => {
70
73
  observeSandboxPromise(target);
71
- const chained = createSandboxPromise(enqueue(() => target.promise.then((value) => runPromiseReaction(undefined, value, "fulfilled", budget, chained), (reason) => runPromiseReaction(onRejected, reason, "rejected", budget, chained))));
74
+ const chained = createSandboxPromise(target.promise.then((value) => runPromiseReaction(undefined, value, "fulfilled", budget, chained), (reason) => runPromiseReaction(onRejected, reason, "rejected", budget, chained)));
72
75
  return chained;
73
76
  },
74
77
  name: "catch"
@@ -79,7 +82,7 @@ export function getPromiseMember(target, property, budget, enqueue = (task) => P
79
82
  async: true,
80
83
  call: ([onFinally]) => {
81
84
  observeSandboxPromise(target);
82
- const chained = createSandboxPromise(enqueue(() => target.promise.then((value) => runPromiseFinally(onFinally, value, "fulfilled", budget, chained), (reason) => runPromiseFinally(onFinally, reason, "rejected", budget, chained))));
85
+ const chained = createSandboxPromise(target.promise.then((value) => runPromiseFinally(onFinally, value, "fulfilled", budget, chained), (reason) => runPromiseFinally(onFinally, reason, "rejected", budget, chained)));
83
86
  return chained;
84
87
  },
85
88
  name: "finally"
@@ -190,62 +193,53 @@ function resolveThenable(value, then, options, seenThenables) {
190
193
  return new Promise((resolve, reject) => {
191
194
  let settlement;
192
195
  let completed = false;
196
+ let invocationPending = true;
193
197
  const complete = () => {
194
- if (completed || settlement === undefined)
198
+ if (completed || invocationPending || settlement === undefined)
195
199
  return;
196
200
  completed = true;
197
- if (settlement.state === "fulfilled") {
198
- resolve(resolveSandboxValueNow(settlement.value, options, seenThenables));
201
+ try {
202
+ if (settlement.state === "fulfilled") {
203
+ resolve(resolveSandboxValueNow(settlement.value, options, seenThenables));
204
+ }
205
+ else {
206
+ reject(budgetIfNeeded(settlement.value, options.budget));
207
+ }
199
208
  }
200
- else {
201
- reject(budgetIfNeeded(settlement.value, options.budget));
209
+ catch (error) {
210
+ reject(error);
202
211
  }
203
212
  };
204
213
  const recordSettlement = (state, settledValue) => {
205
214
  if (settlement !== undefined) {
206
- throw new SandboxError("reentry");
215
+ return;
207
216
  }
208
217
  settlement = { state, value: settledValue };
209
218
  queueMicrotask(complete);
210
219
  };
211
- const resolveOnce = (resolved) => {
212
- recordSettlement("fulfilled", resolved);
213
- };
214
- const rejectOnce = (reason) => {
215
- recordSettlement("rejected", reason);
216
- };
217
- try {
218
- const result = then.call([
219
- createSandboxClosure({
220
- call: ([resolved]) => {
221
- resolveOnce(resolved);
222
- return undefined;
223
- },
224
- name: "resolve"
225
- }),
226
- createSandboxClosure({
227
- call: ([reason]) => {
228
- rejectOnce(reason);
229
- return undefined;
230
- },
231
- name: "reject"
232
- })
233
- ]);
234
- if (isPromiseLike(result)) {
235
- Promise.resolve(result).catch((reason) => {
236
- rejectOnce(reason);
237
- });
238
- }
239
- }
240
- catch (error) {
241
- if (settlement !== undefined) {
242
- settlement = { state: "rejected", value: error };
243
- queueMicrotask(complete);
244
- }
245
- else {
246
- rejectOnce(error);
247
- }
248
- }
220
+ callInPromiseJob(then, [
221
+ createSandboxClosure({
222
+ call: ([resolved]) => {
223
+ recordSettlement("fulfilled", resolved);
224
+ return undefined;
225
+ },
226
+ name: "resolve"
227
+ }),
228
+ createSandboxClosure({
229
+ call: ([reason]) => {
230
+ recordSettlement("rejected", reason);
231
+ return undefined;
232
+ },
233
+ name: "reject"
234
+ })
235
+ ], value).then(() => {
236
+ invocationPending = false;
237
+ complete();
238
+ }, (error) => {
239
+ invocationPending = false;
240
+ recordSettlement("rejected", error);
241
+ complete();
242
+ });
249
243
  }).then((resolved) => budgetIfNeeded(resolved, options.budget));
250
244
  }
251
245
  function runPromiseReaction(handler, value, state, budget, self) {
@@ -254,25 +248,24 @@ function runPromiseReaction(handler, value, state, budget, self) {
254
248
  ? Promise.resolve(value)
255
249
  : Promise.reject(budgetSandboxValue(value, budget));
256
250
  }
257
- try {
258
- const result = handler.call([value]);
259
- return resolveReactionResult(result, budget, self);
260
- }
261
- catch (error) {
262
- return Promise.reject(error);
263
- }
251
+ return callInPromiseJob(handler, [value]).then(({ value: result }) => resolveReactionResult(result, budget, self));
264
252
  }
265
253
  function runPromiseFinally(handler, value, state, budget, self) {
266
254
  if (!isSandboxClosure(handler)) {
267
255
  return runPromiseReaction(undefined, value, state, budget);
268
256
  }
269
- try {
270
- const result = handler.call([]);
271
- return resolveReactionResult(result, budget, self).then(() => runPromiseReaction(undefined, value, state, budget));
272
- }
273
- catch (error) {
274
- return Promise.reject(error);
275
- }
257
+ return callInPromiseJob(handler, []).then(({ value: result }) => resolveReactionResult(result, budget, self).then(() => runPromiseReaction(undefined, value, state, budget)));
258
+ }
259
+ function callInPromiseJob(handler, args, thisValue = undefined) {
260
+ return runPromiseJob(async () => {
261
+ let result = handler.call(args, { stack: [], thisValue });
262
+ if (handler.async !== true)
263
+ result = await result;
264
+ if (isSandboxPromise(result) && result.synchronousPrefix !== undefined) {
265
+ await result.synchronousPrefix;
266
+ }
267
+ return { value: result };
268
+ });
276
269
  }
277
270
  function resolveReactionResult(result, budget, self) {
278
271
  return Promise.resolve(result).then((resolved) => {
@@ -289,14 +282,13 @@ function isSelfResolution(result, self) {
289
282
  return (self !== undefined &&
290
283
  (result === self || (isSandboxPromise(result) && result.promise === self.promise)));
291
284
  }
292
- function getThenable(value) {
293
- if (typeof value !== "object" ||
294
- value === null ||
295
- isSandboxClosure(value) ||
296
- isSandboxPromise(value)) {
285
+ export function getThenable(value) {
286
+ if (typeof value !== "object" || value === null || isSandboxPromise(value)) {
297
287
  return undefined;
298
288
  }
299
- const then = value.then;
289
+ const then = isSandboxClosure(value)
290
+ ? value.properties?.then
291
+ : value.then;
300
292
  return isSandboxClosure(then) ? then : undefined;
301
293
  }
302
294
  function budgetIfNeeded(value, budget) {
@@ -62,6 +62,7 @@ export type SandboxClosure = {
62
62
  };
63
63
  export type SandboxPromise = {
64
64
  readonly kind: "promise";
65
+ readonly synchronousPrefix?: Promise<void>;
65
66
  readonly promise: Promise<SandboxValue>;
66
67
  readonly hostCall?: import("./host-call.js").HostCallRecord;
67
68
  readonly hostCallJournal?: import("./host-call.js").HostCallJournal;
@@ -89,6 +90,7 @@ export declare function createSandboxClosure(input: {
89
90
  }): SandboxClosure;
90
91
  export declare function createSandboxPromise(promise: Promise<SandboxValue>, metadata?: {
91
92
  trackReplay?: boolean;
93
+ synchronousPrefix?: Promise<void>;
92
94
  hostCall?: import("./host-call.js").HostCallRecord;
93
95
  hostCallJournal?: import("./host-call.js").HostCallJournal;
94
96
  span?: SandboxCallContext["span"];
@@ -50,11 +50,17 @@ export function createSandboxPromise(promise, metadata = {}) {
50
50
  enumerable: false,
51
51
  value: true
52
52
  });
53
+ Object.defineProperty(sandboxPromise, Symbol.toStringTag, { value: "Promise" });
53
54
  if (metadata.span !== undefined) {
54
55
  Object.defineProperty(sandboxPromise, "span", {
55
56
  value: metadata.span
56
57
  });
57
58
  }
59
+ if (metadata.synchronousPrefix !== undefined) {
60
+ Object.defineProperty(sandboxPromise, "synchronousPrefix", {
61
+ value: metadata.synchronousPrefix
62
+ });
63
+ }
58
64
  if (metadata.hostCall !== undefined) {
59
65
  Object.defineProperty(sandboxPromise, "hostCall", { value: metadata.hostCall });
60
66
  }
@@ -1,6 +1,7 @@
1
1
  import { hashSource } from "./parse/hash.js";
2
2
  import { replaceErrorStack } from "./error/shape.js";
3
- import { validateDumpEnvelope } from "./snapshot/validation.js";
3
+ import { SnapshotValidationError, validateDumpEnvelope } from "./snapshot/validation.js";
4
+ import { EXECUTION_SEMANTICS } from "./snapshot/dump-format.js";
4
5
  import { assertSnapshotInactive } from "./interp/running-state.js";
5
6
  export class SnapshotMismatchError extends Error {
6
7
  actualHash;
@@ -16,6 +17,13 @@ export class SnapshotMismatchError extends Error {
16
17
  export function restore(snapshot, options) {
17
18
  assertSnapshotInactive(snapshot);
18
19
  validateDumpEnvelope(snapshot);
20
+ if (snapshot.executionSemantics !== EXECUTION_SEMANTICS &&
21
+ (snapshot.executionSemantics !== undefined ||
22
+ snapshot.promiseReplay !== undefined ||
23
+ snapshot.replay !== undefined ||
24
+ snapshot.initialInputs !== undefined)) {
25
+ throw new SnapshotValidationError("unsupportedVersion", "$.executionSemantics", "incompatible execution semantics; resume with the SafeJS version that created this snapshot. Migration requires explicit reconciliation, not changing its version marker.");
26
+ }
19
27
  const currentSourceHash = hashSource(options.source);
20
28
  if (snapshot.sourceHash !== currentSourceHash) {
21
29
  throw new SnapshotMismatchError(snapshot.sourceHash, currentSourceHash);
@@ -23,7 +23,7 @@ import { deepCopyToSandbox, isSandboxClosure, isSandboxPromise, measureSandboxDa
23
23
  import { resolveModuleImports } from "./modules/registry.js";
24
24
  import { activateOtelSink, getActiveOtelSpan, safeAddEvent } from "./observability/otel.js";
25
25
  import { attachDumpController, createDumpController } from "./snapshot/dump.js";
26
- import { DUMP_FORMAT_VERSION } from "./snapshot/dump-format.js";
26
+ import { DUMP_FORMAT_VERSION, EXECUTION_SEMANTICS } from "./snapshot/dump-format.js";
27
27
  import { createSnapshotScheduler } from "./snapshot/scheduler.js";
28
28
  import { UnsnapshotableValueError } from "./snapshot/serialize.js";
29
29
  import { prepareReplayInputs } from "./snapshot/replay-inputs.js";
@@ -110,7 +110,6 @@ export function run(source, options = {}) {
110
110
  }),
111
111
  ...createRegexGlobals()
112
112
  };
113
- const bindings = wrapCancelableBindings(builtinBindings, options.signal);
114
113
  const initialInputs = prepareReplayInputs({
115
114
  bindings: callerBindings,
116
115
  imports: resolveModuleImports(module, options.modules, {
@@ -151,7 +150,7 @@ export function run(source, options = {}) {
151
150
  leaveInputReplay = () => budget.setRetainedDataUsage(initialInputs, 0);
152
151
  const entryPointArgs = initialInputs.values.entryPointArgs;
153
152
  const cancelableCallerBindings = wrapCancelableBindings(initialInputs.values.bindings, options.signal);
154
- const scope = new Scope(bindings, undefined, undefined, { chargeData: false }, interpreterSnapshot?.bindings);
153
+ const scope = new Scope(builtinBindings, undefined, undefined, { chargeData: false }, interpreterSnapshot?.bindings);
155
154
  const callerScope = scope.child(cancelableCallerBindings);
156
155
  const executionScope = new Scope(initialInputs.values.imports, callerScope, initialInputs.values.importMeta, { functionBoundary: true });
157
156
  const activeSnapshotScheduler = createSnapshotScheduler({
@@ -202,6 +201,7 @@ export function run(source, options = {}) {
202
201
  dumpController.onYield(createSnapshot);
203
202
  },
204
203
  scope: executionScope,
204
+ signal: options.signal,
205
205
  snapshot: interpreterSnapshot,
206
206
  surfaceUnhandledThrows: true,
207
207
  useScopeDirectly: true
@@ -247,6 +247,7 @@ export function run(source, options = {}) {
247
247
  dumpController.onYield(createSnapshot);
248
248
  },
249
249
  scope: executionScope,
250
+ signal: options.signal,
250
251
  snapshot: interpreterSnapshot
251
252
  });
252
253
  await throwIfReturnedPromiseRejected(result);
@@ -350,6 +351,7 @@ async function callEntryPoint(input) {
350
351
  ...argumentBindings
351
352
  });
352
353
  return interpret(createEntryPointAwait(input.args.length, input.module.span), {
354
+ signal: input.signal,
353
355
  budget: input.budget,
354
356
  captureReplayState: input.captureReplayState,
355
357
  onYield: input.onYield,
@@ -407,6 +409,7 @@ function createExecutableNode(module) {
407
409
  function createRunSnapshot(input) {
408
410
  return {
409
411
  version: DUMP_FORMAT_VERSION,
412
+ executionSemantics: EXECUTION_SEMANTICS,
410
413
  sourceHash: input.sourceHash,
411
414
  bindings: input.bindings,
412
415
  clock: input.clock?.snapshot(),
@@ -1,4 +1,5 @@
1
1
  export declare const DUMP_FORMAT_VERSION = 1;
2
+ export declare const EXECUTION_SEMANTICS = "jobs-v1";
2
3
  export type DumpableSnapshot = {
3
4
  sourceHash: string;
4
5
  [key: string]: unknown;
@@ -1,4 +1,5 @@
1
1
  export const DUMP_FORMAT_VERSION = 1;
2
+ export const EXECUTION_SEMANTICS = "jobs-v1";
2
3
  import { assertSnapshotGraphDepth } from "../graph-depth.js";
3
4
  import { getSandboxArgumentEntries, isSandboxArguments } from "../interp/arguments.js";
4
5
  import { serializeArguments } from "./arguments.js";