poe-code 5.0.4 → 6.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.
Files changed (38) hide show
  1. package/dist/index.js +3181 -2636
  2. package/dist/index.js.map +4 -4
  3. package/dist/metafile.json +1 -1
  4. package/package.json +1 -1
  5. package/packages/safejs/dist/interp/async.js +31 -9
  6. package/packages/safejs/dist/interp/cancel.d.ts +7 -2
  7. package/packages/safejs/dist/interp/cancel.js +128 -92
  8. package/packages/safejs/dist/interp/exceptions.js +7 -3
  9. package/packages/safejs/dist/interp/globals/collections.js +2 -0
  10. package/packages/safejs/dist/interp/globals/console-json.js +4 -0
  11. package/packages/safejs/dist/interp/globals/error.js +1 -5
  12. package/packages/safejs/dist/interp/globals/math.js +2 -4
  13. package/packages/safejs/dist/interp/globals/misc.js +5 -0
  14. package/packages/safejs/dist/interp/globals/object-array.js +25 -0
  15. package/packages/safejs/dist/interp/globals/regex.js +1 -1
  16. package/packages/safejs/dist/interp/host-bridge.js +8 -5
  17. package/packages/safejs/dist/interp/interpreter.js +26 -32
  18. package/packages/safejs/dist/interp/iteration.d.ts +3 -3
  19. package/packages/safejs/dist/interp/iteration.js +20 -9
  20. package/packages/safejs/dist/interp/methods/array.js +1 -0
  21. package/packages/safejs/dist/interp/methods/function.d.ts +1 -1
  22. package/packages/safejs/dist/interp/methods/function.js +17 -1
  23. package/packages/safejs/dist/interp/methods/generator.js +1 -0
  24. package/packages/safejs/dist/interp/methods/map.js +1 -0
  25. package/packages/safejs/dist/interp/methods/number.js +1 -0
  26. package/packages/safejs/dist/interp/methods/regex.js +1 -0
  27. package/packages/safejs/dist/interp/methods/set.js +1 -0
  28. package/packages/safejs/dist/interp/methods/string.js +1 -0
  29. package/packages/safejs/dist/interp/promise-tracker.d.ts +12 -2
  30. package/packages/safejs/dist/interp/promise-tracker.js +65 -7
  31. package/packages/safejs/dist/interp/promise.d.ts +7 -5
  32. package/packages/safejs/dist/interp/promise.js +531 -148
  33. package/packages/safejs/dist/interp/values.d.ts +6 -0
  34. package/packages/safejs/dist/interp/values.js +25 -5
  35. package/packages/safejs/dist/lint/rules/AS001.js +0 -5
  36. package/packages/safejs/dist/snapshot/dump-format.d.ts +1 -1
  37. package/packages/safejs/dist/snapshot/dump-format.js +1 -1
  38. package/packages/safejs/dist/snapshot/replay-data.js +3 -0
@@ -46,8 +46,11 @@ function wrapCallerInjectedFunction(name, value, options, state) {
46
46
  const callable = value;
47
47
  return createSandboxClosure({
48
48
  ...(isAsyncFunction(callable) ? { async: true } : {}),
49
+ cancellationSignal: options.signal,
49
50
  call: (args, context) => {
50
51
  try {
52
+ if (options.signal?.aborted)
53
+ throw readAbortReason(options.signal);
51
54
  const stackFrames = context?.stack ?? [];
52
55
  const callbacks = {
53
56
  journal: options.hostCalls,
@@ -183,12 +186,12 @@ function executeHostCall(record, restored, invoke, stackFrames, options, span, c
183
186
  }
184
187
  })();
185
188
  void promise.catch(() => undefined);
186
- return createSandboxPromise(promise);
189
+ return createSandboxPromise(promise, { hostCall: record, hostCallJournal: hostCalls });
187
190
  }
188
191
  if (replayed !== undefined) {
189
192
  restoreReplayedHostState(record, replayed, onReplay);
190
193
  if (record.asynchronous)
191
- return createReplayedHostCallResult(replayed);
194
+ return createReplayedHostCallResult(replayed, record, hostCalls);
192
195
  if (replayed.status === "rejected")
193
196
  throw replayed.reason;
194
197
  return replayed.value;
@@ -197,7 +200,7 @@ function executeHostCall(record, restored, invoke, stackFrames, options, span, c
197
200
  record.policy === "read-side-effect" &&
198
201
  record.lifecycle === "consumed" &&
199
202
  record.outcome !== undefined) {
200
- return createReplayedHostCallResult(record.outcome);
203
+ return createReplayedHostCallResult(record.outcome, record, hostCalls);
201
204
  }
202
205
  if (restored &&
203
206
  record.policy === "read-side-effect" &&
@@ -285,12 +288,12 @@ function restoreReplayedHostState(record, outcome, onReplay) {
285
288
  throw error;
286
289
  }
287
290
  }
288
- function createReplayedHostCallResult(outcome) {
291
+ function createReplayedHostCallResult(outcome, record, hostCalls) {
289
292
  const promise = outcome.status === "fulfilled"
290
293
  ? Promise.resolve(outcome.value)
291
294
  : Promise.reject(outcome.reason);
292
295
  promise.catch(() => undefined);
293
- return createSandboxPromise(promise);
296
+ return createSandboxPromise(promise, { hostCall: record, hostCallJournal: hostCalls });
294
297
  }
295
298
  function createHostCallPromise(record, outcomePromise, hostCalls) {
296
299
  const promise = outcomePromise.then((outcome) => {
@@ -1,5 +1,7 @@
1
1
  import { promiseReplayContext } from "./promise-replay.js";
2
+ import { assertPromiseExecutionAllowed } from "./promise-tracker.js";
2
3
  import { SandboxJobQueue, suspendJob } from "./jobs.js";
4
+ import { withCancellationSignal } from "./cancel.js";
3
5
  import { attachErrorSpan, formatErrorStack, readErrorSpan, replaceErrorStack } from "../error/shape.js";
4
6
  import { evaluateArrowFunctionExpression, evaluateFunctionExpression, evaluateAwaitExpression, emitResumeBreakpoint, createInterpretedClosure, normalizeClosureResult } from "./async.js";
5
7
  import { HostCallResumabilityError } from "./host-call.js";
@@ -9,7 +11,7 @@ import { callArrayMethod, getArrayMember, isArrayMethodName } from "./methods/ar
9
11
  import { getFunctionMember } from "./methods/function.js";
10
12
  import { callMapMethod, getMapMember, isMapMethodName } from "./methods/map.js";
11
13
  import { callNumberMethod, getNumberMember, isNumberMethodName } from "./methods/number.js";
12
- import { getPromiseMember } from "./promise.js";
14
+ import { getPromiseMember, isSandboxPromiseConstructor } from "./promise.js";
13
15
  import { getSandboxIterator } from "./iteration.js";
14
16
  import { assertCollectionMutable } from "./running-state.js";
15
17
  import { getGeneratorMember } from "./methods/generator.js";
@@ -108,7 +110,7 @@ export async function interpret(node, options = {}) {
108
110
  generatorYield: options.generatorYield,
109
111
  resumeTarget: { nodeId: options.snapshot?.resumeNodeId }
110
112
  };
111
- const evaluation = await jobs.run(() => evaluateNode(node, context));
113
+ const evaluation = await withCancellationSignal(options.signal, () => jobs.run(() => evaluateNode(node, context)));
112
114
  await jobs.drain();
113
115
  const snapshot = scope.snapshot();
114
116
  reconcileDataBudget(budget, stats, scope, "hasValue" in evaluation && evaluation.hasValue ? evaluation.value : undefined);
@@ -154,6 +156,7 @@ async function evaluateNode(node, context) {
154
156
  const replayWait = promiseReplayContext.getStore()?.beforeNode(node.nodeId);
155
157
  if (replayWait !== undefined)
156
158
  await suspendJob(replayWait);
159
+ assertPromiseExecutionAllowed();
157
160
  context.budget.visitNode();
158
161
  context.stats.nodeVisits += 1;
159
162
  if (node.nodeId !== undefined && context.resumeTarget?.nodeId === node.nodeId) {
@@ -1597,7 +1600,7 @@ async function evaluateMemberExpression(node, context) {
1597
1600
  return {
1598
1601
  kind: "normal",
1599
1602
  hasValue: true,
1600
- value: getPromiseMember(member.object, member.property, context.budget)
1603
+ value: getPromiseMember(member.property, context.budget)
1601
1604
  };
1602
1605
  }
1603
1606
  if (isSandboxRegex(member.object)) {
@@ -1634,35 +1637,19 @@ async function evaluateNewExpression(node, context) {
1634
1637
  return callee;
1635
1638
  }
1636
1639
  const name = getConstructorName(node.callee);
1637
- if (!isSandboxClosure(callee.value) || callee.value.construct === undefined) {
1638
- throw new TypeError(`${name} is not a constructor.`);
1639
- }
1640
1640
  const args = await evaluateCallArguments(node.arguments, context);
1641
1641
  if (!args.ok) {
1642
1642
  return args.result;
1643
1643
  }
1644
- const stack = [...context.callStack, formatStackFrame(node, callee.value.name ?? name)];
1645
- const leaveCall = context.budget.enterCall();
1646
- try {
1647
- return {
1648
- kind: "normal",
1649
- hasValue: true,
1650
- value: await wrapHostResult(callee.value.construct(args.value, {
1651
- stack,
1652
- thisValue: undefined,
1653
- span: node.span
1654
- }), stack)
1655
- };
1656
- }
1657
- catch (error) {
1658
- if (isFatalSandboxError(error)) {
1659
- throw error;
1660
- }
1661
- throw captureException(error, stack);
1662
- }
1663
- finally {
1664
- leaveCall();
1644
+ if (!isSandboxClosure(callee.value) || callee.value.construct === undefined) {
1645
+ throw new TypeError(`${name} is not a constructor.`);
1665
1646
  }
1647
+ const stack = [...context.callStack, formatStackFrame(node, callee.value.name ?? name)];
1648
+ return {
1649
+ kind: "normal",
1650
+ hasValue: true,
1651
+ value: await invokeSandboxClosure(callee.value, args.value, context, stack, node.span, undefined, true)
1652
+ };
1666
1653
  }
1667
1654
  function getConstructorName(callee) {
1668
1655
  if (callee.type === "Identifier") {
@@ -1839,7 +1826,7 @@ async function evaluateMemberCallExpression(node, context) {
1839
1826
  return evaluateResolvedCallExpression(node, memberValue, context, member.object);
1840
1827
  }
1841
1828
  if (isSandboxPromise(member.object)) {
1842
- return evaluateResolvedCallExpression(node, getPromiseMember(member.object, member.property, context.budget), context, member.object);
1829
+ return evaluateResolvedCallExpression(node, getPromiseMember(member.property, context.budget), context, member.object);
1843
1830
  }
1844
1831
  if (isSandboxRegex(member.object) && isRegexMethodName(member.property)) {
1845
1832
  return evaluateResolvedCallExpression(node, getRegexMember(member.object, member.property), context, member.object);
@@ -2021,6 +2008,10 @@ function applyBinaryOperator(node, left, right, context) {
2021
2008
  case ">>>":
2022
2009
  return toNumber(left) >>> toNumber(right);
2023
2010
  case "instanceof":
2011
+ while (isSandboxClosure(right) && right.boundTarget !== undefined)
2012
+ right = right.boundTarget;
2013
+ if (isSandboxPromiseConstructor(right))
2014
+ return isSandboxPromise(left);
2024
2015
  if (isSandboxMapConstructor(right) && isSandboxMap(left)) {
2025
2016
  return true;
2026
2017
  }
@@ -2322,13 +2313,16 @@ function createSetMethodOptions(context) {
2322
2313
  }
2323
2314
  function createFunctionMethodOptions(context) {
2324
2315
  return {
2325
- callClosure: (closure, args, stack, thisValue) => invokeSandboxClosure(closure, args, context, stack, undefined, thisValue)
2316
+ callClosure: (closure, args, stack, thisValue, construct) => invokeSandboxClosure(closure, args, context, stack, undefined, thisValue, construct)
2326
2317
  };
2327
2318
  }
2328
- async function invokeSandboxClosure(callee, args, context, stack, span, thisValue = undefined) {
2319
+ async function invokeSandboxClosure(callee, args, context, stack, span, thisValue = undefined, construct = false) {
2329
2320
  const leaveCall = context.budget.enterCall();
2330
2321
  try {
2331
- const result = Reflect.apply(callee.call, undefined, [
2322
+ const invoke = construct ? callee.construct : callee.call;
2323
+ if (invoke === undefined)
2324
+ throw new TypeError("Value is not a constructor.");
2325
+ const result = Reflect.apply(invoke, undefined, [
2332
2326
  args,
2333
2327
  {
2334
2328
  stack,
@@ -2339,7 +2333,7 @@ async function invokeSandboxClosure(callee, args, context, stack, span, thisValu
2339
2333
  if (isSandboxPromise(result) && result.synchronousPrefix !== undefined) {
2340
2334
  await result.synchronousPrefix;
2341
2335
  }
2342
- return callee.async === true
2336
+ return !construct && callee.async === true
2343
2337
  ? normalizeClosureResult(wrapHostResult(result, stack), context.budget)
2344
2338
  : await wrapHostResult(result, stack);
2345
2339
  }
@@ -1,8 +1,8 @@
1
1
  import { type SandboxValue } from "./values.js";
2
2
  export type SandboxIterator = {
3
3
  readonly generator?: true;
4
- next(value?: SandboxValue): Promise<IteratorResult<SandboxValue>>;
5
- return?(value?: SandboxValue): Promise<IteratorResult<SandboxValue>>;
6
- throw?(error?: SandboxValue): Promise<IteratorResult<SandboxValue>>;
4
+ next(value?: SandboxValue): IteratorResult<SandboxValue> | Promise<IteratorResult<SandboxValue>>;
5
+ return?(value?: SandboxValue): IteratorResult<SandboxValue> | Promise<IteratorResult<SandboxValue>>;
6
+ throw?(error?: SandboxValue): IteratorResult<SandboxValue> | Promise<IteratorResult<SandboxValue>>;
7
7
  };
8
8
  export declare function getSandboxIterator(value: SandboxValue): SandboxIterator | undefined;
@@ -47,22 +47,33 @@ function generatorIterator(generator) {
47
47
  };
48
48
  }
49
49
  function syncIterator(iterator) {
50
- const invoke = async (method, value) => {
50
+ const next = iterator.next;
51
+ const invoke = (method, args) => {
51
52
  const leaveRunning = enterRunningState(iterator);
52
53
  try {
53
- return await iterator[method](value);
54
+ return Reflect.apply(method, iterator, args);
54
55
  }
55
56
  finally {
56
57
  leaveRunning();
57
58
  }
58
59
  };
59
60
  return {
60
- next: (value) => invoke("next", value),
61
- ...(typeof iterator.return === "function"
62
- ? { return: (value) => invoke("return", value) }
63
- : {}),
64
- ...(typeof iterator.throw === "function"
65
- ? { throw: (error) => invoke("throw", error) }
66
- : {})
61
+ next: (...args) => invoke(next, args),
62
+ get return() {
63
+ const method = iterator.return;
64
+ if (method === undefined || method === null)
65
+ return undefined;
66
+ if (typeof method !== "function")
67
+ throw new TypeError("Iterator return must be callable.");
68
+ return (...args) => invoke(method, args);
69
+ },
70
+ get throw() {
71
+ const method = iterator.throw;
72
+ if (method === undefined || method === null)
73
+ return undefined;
74
+ if (typeof method !== "function")
75
+ throw new TypeError("Iterator throw must be callable.");
76
+ return (...args) => invoke(method, args);
77
+ }
67
78
  };
68
79
  }
@@ -45,6 +45,7 @@ export function getArrayMember(value, property, options) {
45
45
  }
46
46
  if (isArrayMethodName(property)) {
47
47
  return createSandboxClosure({
48
+ sandbox: true,
48
49
  name: `Array#${property}`,
49
50
  call: (args, context) => callArrayMethod(value, property, args, options, context?.stack ?? [])
50
51
  });
@@ -1,5 +1,5 @@
1
1
  import { type SandboxClosure, type SandboxValue } from "../values.js";
2
2
  export type FunctionMethodOptions = {
3
- callClosure: (closure: SandboxClosure, args: readonly SandboxValue[], stack: readonly string[], thisValue: SandboxValue) => Promise<SandboxValue> | SandboxValue;
3
+ callClosure: (closure: SandboxClosure, args: readonly SandboxValue[], stack: readonly string[], thisValue: SandboxValue, construct?: boolean) => Promise<SandboxValue> | SandboxValue;
4
4
  };
5
5
  export declare function getFunctionMember(target: SandboxClosure, property: string | number, options: FunctionMethodOptions): SandboxValue | undefined;
@@ -1,5 +1,5 @@
1
1
  import { createSandboxClosure } from "../values.js";
2
- const functionMethodNames = new Set(["apply", "call"]);
2
+ const functionMethodNames = new Set(["apply", "bind", "call"]);
3
3
  export function getFunctionMember(target, property, options) {
4
4
  const propertyValue = target.properties?.[String(property)];
5
5
  if (propertyValue !== undefined || Object.hasOwn(target.properties ?? {}, String(property))) {
@@ -9,6 +9,7 @@ export function getFunctionMember(target, property, options) {
9
9
  return undefined;
10
10
  }
11
11
  return createSandboxClosure({
12
+ sandbox: true,
12
13
  name: `Function#${property}`,
13
14
  call: (args, context) => callFunctionMethod(target, property, args, options, context?.stack ?? [])
14
15
  });
@@ -18,6 +19,21 @@ function isFunctionMethodName(property) {
18
19
  }
19
20
  function callFunctionMethod(target, methodName, args, options, stack) {
20
21
  const thisValue = args[0];
22
+ if (methodName === "bind") {
23
+ const boundArgs = args.slice(1);
24
+ return createSandboxClosure({
25
+ sandbox: true,
26
+ name: `bound ${target.name ?? ""}`,
27
+ boundTarget: target,
28
+ retainedValues: () => [target, thisValue, ...boundArgs],
29
+ call: (callArgs, context) => options.callClosure(target, [...boundArgs, ...callArgs], context?.stack ?? [], thisValue),
30
+ ...(target.construct === undefined
31
+ ? {}
32
+ : {
33
+ construct: (callArgs, context) => options.callClosure(target, [...boundArgs, ...callArgs], context?.stack ?? [], undefined, true)
34
+ })
35
+ });
36
+ }
21
37
  if (methodName === "call") {
22
38
  return options.callClosure(target, args.slice(1), stack, thisValue);
23
39
  }
@@ -6,6 +6,7 @@ export function getGeneratorMember(target, property, budget) {
6
6
  return undefined;
7
7
  }
8
8
  return createSandboxClosure({
9
+ sandbox: true,
9
10
  name: property,
10
11
  call: async ([value]) => {
11
12
  const iterator = getSandboxIterator(target);
@@ -22,6 +22,7 @@ export function getMapMember(target, property, options) {
22
22
  return undefined;
23
23
  }
24
24
  return createSandboxClosure({
25
+ sandbox: true,
25
26
  call: (args, context) => callMapMethod(target, property, args, options, context?.stack ?? []),
26
27
  name: property
27
28
  });
@@ -10,6 +10,7 @@ export function getNumberMember(value, property, budget) {
10
10
  return undefined;
11
11
  }
12
12
  return createSandboxClosure({
13
+ sandbox: true,
13
14
  name: `Number#${property}`,
14
15
  call: (args) => callNumberMethod(value, property, args, budget)
15
16
  });
@@ -12,6 +12,7 @@ export function getRegexMember(target, property) {
12
12
  return undefined;
13
13
  }
14
14
  return createSandboxClosure({
15
+ sandbox: true,
15
16
  name: `RegExp#${property}`,
16
17
  call: (args) => callRegexMethod(target, property, args)
17
18
  });
@@ -21,6 +21,7 @@ export function getSetMember(target, property, options) {
21
21
  return undefined;
22
22
  }
23
23
  return createSandboxClosure({
24
+ sandbox: true,
24
25
  call: (args, context) => callSetMethod(target, property, args, options, context?.stack ?? []),
25
26
  name: property
26
27
  });
@@ -44,6 +44,7 @@ export function getStringMember(value, property, budget) {
44
44
  return undefined;
45
45
  }
46
46
  return createSandboxClosure({
47
+ sandbox: true,
47
48
  name: `String#${property}`,
48
49
  call: (args) => callStringMethod(value, property, args, budget)
49
50
  });
@@ -1,16 +1,26 @@
1
1
  import type { ErrorSourceSpan } from "../error/shape.js";
2
+ import { SandboxError } from "./budget.js";
2
3
  import type { SandboxPromise, SandboxValue } from "./values.js";
3
4
  export declare class SandboxPromiseRejectionTracker {
4
5
  private readonly records;
5
6
  private readonly recordsByPromise;
7
+ private fatalRejection;
8
+ private readonly fatalWaiters;
9
+ get failure(): SandboxError | undefined;
10
+ onFatalRejection(interrupt: (error: SandboxError) => void): () => void;
11
+ interruptOnFatalRejection(pending: Promise<SandboxValue>): Promise<SandboxValue>;
6
12
  observe(promise: SandboxPromise): void;
7
- track(promise: SandboxPromise): void;
13
+ track(promise: SandboxPromise, outcome?: Promise<SandboxValue>): void;
8
14
  findUnhandledRejection(): Promise<{
9
15
  reason: SandboxValue | undefined;
10
16
  span: ErrorSourceSpan | undefined;
11
17
  } | undefined>;
12
18
  }
19
+ export declare function assertPromiseExecutionAllowed(): void;
20
+ export declare function onFatalPromiseRejection(interrupt: (error: SandboxError) => void): (() => void) | undefined;
21
+ export declare function interruptOnFatalPromiseRejection(pending: Promise<SandboxValue>): Promise<SandboxValue>;
22
+ export declare function withFatalPromiseCleanup<TResult>(callback: () => TResult): TResult;
13
23
  export declare function createSandboxPromiseRejectionTracker(): SandboxPromiseRejectionTracker;
14
24
  export declare function observeSandboxPromise(promise: SandboxPromise): void;
15
- export declare function trackSandboxPromise(promise: SandboxPromise): void;
25
+ export declare function trackSandboxPromise(promise: SandboxPromise, outcome?: Promise<SandboxValue>): void;
16
26
  export declare function withSandboxPromiseRejectionTracker<TResult>(tracker: SandboxPromiseRejectionTracker, callback: () => TResult): TResult;
@@ -1,32 +1,71 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { SandboxError } from "./budget.js";
2
3
  export class SandboxPromiseRejectionTracker {
3
4
  records = new Set();
4
5
  recordsByPromise = new WeakMap();
6
+ fatalRejection;
7
+ fatalWaiters = new Set();
8
+ get failure() {
9
+ return this.fatalRejection;
10
+ }
11
+ onFatalRejection(interrupt) {
12
+ if (this.fatalRejection === undefined)
13
+ this.fatalWaiters.add(interrupt);
14
+ else
15
+ interrupt(this.fatalRejection);
16
+ return () => this.fatalWaiters.delete(interrupt);
17
+ }
18
+ interruptOnFatalRejection(pending) {
19
+ return new Promise((resolve, reject) => {
20
+ const detach = this.onFatalRejection(reject);
21
+ pending.then((value) => {
22
+ detach();
23
+ resolve(value);
24
+ }, (reason) => {
25
+ detach();
26
+ reject(reason);
27
+ });
28
+ });
29
+ }
5
30
  observe(promise) {
6
31
  const record = this.recordsByPromise.get(promise);
7
32
  if (record !== undefined) {
8
33
  record.observed = true;
9
34
  }
10
35
  }
11
- track(promise) {
12
- if (this.recordsByPromise.has(promise)) {
36
+ track(promise, outcome) {
37
+ const existing = this.recordsByPromise.get(promise);
38
+ if (existing !== undefined && outcome === undefined) {
13
39
  return;
14
40
  }
15
- const record = {
41
+ const record = existing ?? {
16
42
  observed: false,
17
43
  promise,
18
44
  rejected: false
19
45
  };
20
46
  this.records.add(record);
21
47
  this.recordsByPromise.set(promise, record);
22
- promise.promise.then(() => undefined, (reason) => {
48
+ const pending = outcome ?? promise.promise;
49
+ pending.then(() => undefined, (reason) => {
50
+ if (this.fatalRejection === undefined &&
51
+ reason instanceof SandboxError &&
52
+ (reason.code === "budgetExceeded" || reason.code === "reentry")) {
53
+ this.fatalRejection = reason;
54
+ for (const interrupt of this.fatalWaiters)
55
+ interrupt(reason);
56
+ this.fatalWaiters.clear();
57
+ }
58
+ if (record.rejected)
59
+ return;
23
60
  record.rejected = true;
24
61
  record.rejectionReason = reason;
25
62
  });
26
- promise.promise.catch(() => undefined);
63
+ pending.catch(() => undefined);
27
64
  }
28
65
  async findUnhandledRejection() {
29
66
  await flushPromiseJobs();
67
+ if (this.fatalRejection !== undefined)
68
+ throw this.fatalRejection;
30
69
  for (const record of this.records) {
31
70
  if (record.rejected && !record.observed) {
32
71
  return {
@@ -39,14 +78,33 @@ export class SandboxPromiseRejectionTracker {
39
78
  }
40
79
  }
41
80
  const activePromiseTracker = new AsyncLocalStorage();
81
+ const fatalRejectionCleanup = new AsyncLocalStorage();
82
+ export function assertPromiseExecutionAllowed() {
83
+ const failure = activePromiseTracker.getStore()?.failure;
84
+ if (failure !== undefined && fatalRejectionCleanup.getStore() !== true)
85
+ throw failure;
86
+ }
87
+ export function onFatalPromiseRejection(interrupt) {
88
+ if (fatalRejectionCleanup.getStore() === true)
89
+ return undefined;
90
+ return activePromiseTracker.getStore()?.onFatalRejection(interrupt);
91
+ }
92
+ export function interruptOnFatalPromiseRejection(pending) {
93
+ if (fatalRejectionCleanup.getStore() === true)
94
+ return pending;
95
+ return activePromiseTracker.getStore()?.interruptOnFatalRejection(pending) ?? pending;
96
+ }
97
+ export function withFatalPromiseCleanup(callback) {
98
+ return fatalRejectionCleanup.run(true, callback);
99
+ }
42
100
  export function createSandboxPromiseRejectionTracker() {
43
101
  return new SandboxPromiseRejectionTracker();
44
102
  }
45
103
  export function observeSandboxPromise(promise) {
46
104
  activePromiseTracker.getStore()?.observe(promise);
47
105
  }
48
- export function trackSandboxPromise(promise) {
49
- activePromiseTracker.getStore()?.track(promise);
106
+ export function trackSandboxPromise(promise, outcome) {
107
+ activePromiseTracker.getStore()?.track(promise, outcome);
50
108
  }
51
109
  export function withSandboxPromiseRejectionTracker(tracker, callback) {
52
110
  return activePromiseTracker.run(tracker, callback);
@@ -1,14 +1,16 @@
1
- import type { Budget } from "./budget.js";
2
- import { type SandboxClosure, type SandboxObject, type SandboxPromise, type SandboxValue } from "./values.js";
1
+ import { type Budget } from "./budget.js";
2
+ import { type SandboxClosure, type SandboxPromise, type SandboxValue } from "./values.js";
3
3
  export type PromiseGlobals = {
4
- Promise: SandboxObject;
4
+ Promise: SandboxClosure;
5
5
  };
6
+ export declare function isSandboxPromiseConstructor(value: unknown): value is SandboxClosure;
6
7
  export declare function createPromiseGlobals(options: {
7
8
  budget: Budget;
8
9
  }): PromiseGlobals;
9
- export declare function getPromiseMember(target: SandboxPromise, property: string | number, budget: Budget): SandboxValue;
10
+ export declare function getPromiseMember(property: string | number, budget: Budget): SandboxValue;
10
11
  export declare function resolveSandboxValue(value: SandboxValue | Promise<SandboxValue> | PromiseLike<SandboxValue>, options?: {
11
12
  budget?: Budget;
13
+ self?: SandboxPromise;
12
14
  }): Promise<SandboxValue>;
13
- export declare function consumeSettledHostCall(value: SandboxPromise): void;
15
+ export declare function consumeSettledHostCall(value: SandboxPromise): undefined;
14
16
  export declare function getThenable(value: SandboxValue): SandboxClosure | undefined;