poe-code 5.0.4 → 7.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 (52) hide show
  1. package/dist/index.js +3457 -3542
  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.d.ts +2 -1
  6. package/packages/safejs/dist/interp/async.js +56 -33
  7. package/packages/safejs/dist/interp/cancel.d.ts +7 -2
  8. package/packages/safejs/dist/interp/cancel.js +128 -92
  9. package/packages/safejs/dist/interp/exceptions.js +7 -3
  10. package/packages/safejs/dist/interp/globals/collections.js +2 -0
  11. package/packages/safejs/dist/interp/globals/console-json.js +4 -0
  12. package/packages/safejs/dist/interp/globals/error.js +1 -5
  13. package/packages/safejs/dist/interp/globals/math.js +2 -4
  14. package/packages/safejs/dist/interp/globals/misc.js +5 -0
  15. package/packages/safejs/dist/interp/globals/object-array.js +25 -0
  16. package/packages/safejs/dist/interp/globals/regex.js +1 -1
  17. package/packages/safejs/dist/interp/host-bridge.js +8 -5
  18. package/packages/safejs/dist/interp/interpreter.js +33 -37
  19. package/packages/safejs/dist/interp/iteration.d.ts +3 -3
  20. package/packages/safejs/dist/interp/iteration.js +20 -9
  21. package/packages/safejs/dist/interp/methods/array.js +1 -0
  22. package/packages/safejs/dist/interp/methods/function.d.ts +1 -1
  23. package/packages/safejs/dist/interp/methods/function.js +17 -1
  24. package/packages/safejs/dist/interp/methods/generator.js +1 -0
  25. package/packages/safejs/dist/interp/methods/map.js +1 -0
  26. package/packages/safejs/dist/interp/methods/number.js +1 -0
  27. package/packages/safejs/dist/interp/methods/regex.js +1 -0
  28. package/packages/safejs/dist/interp/methods/set.js +1 -0
  29. package/packages/safejs/dist/interp/methods/string.js +1 -0
  30. package/packages/safejs/dist/interp/patterns.d.ts +1 -0
  31. package/packages/safejs/dist/interp/patterns.js +1 -1
  32. package/packages/safejs/dist/interp/promise-tracker.d.ts +12 -2
  33. package/packages/safejs/dist/interp/promise-tracker.js +65 -7
  34. package/packages/safejs/dist/interp/promise.d.ts +7 -5
  35. package/packages/safejs/dist/interp/promise.js +531 -148
  36. package/packages/safejs/dist/interp/values.d.ts +6 -0
  37. package/packages/safejs/dist/interp/values.js +25 -5
  38. package/packages/safejs/dist/interp/var-hoist.js +5 -135
  39. package/packages/safejs/dist/lint/index.js +0 -4
  40. package/packages/safejs/dist/lint/rules/AS001.js +4 -9
  41. package/packages/safejs/dist/lint/rules/AS003.js +47 -7
  42. package/packages/safejs/dist/lint/rules/AS006-007.js +48 -7
  43. package/packages/safejs/dist/parse/bindings.d.ts +6 -0
  44. package/packages/safejs/dist/parse/bindings.js +90 -0
  45. package/packages/safejs/dist/parse/parser.js +70 -37
  46. package/packages/safejs/dist/snapshot/dump-format.d.ts +1 -1
  47. package/packages/safejs/dist/snapshot/dump-format.js +1 -1
  48. package/packages/safejs/dist/snapshot/replay-data.js +3 -0
  49. package/packages/safejs/dist/lint/rules/AS-await-non-promise.d.ts +0 -13
  50. package/packages/safejs/dist/lint/rules/AS-await-non-promise.js +0 -301
  51. package/packages/safejs/dist/lint/rules/AS008.d.ts +0 -13
  52. package/packages/safejs/dist/lint/rules/AS008.js +0 -386
@@ -4,30 +4,37 @@ export function createObjectArrayGlobals(options) {
4
4
  return {
5
5
  Object: {
6
6
  keys: createSandboxClosure({
7
+ sandbox: true,
7
8
  call: ([value]) => budgetSandboxValue(getOwnEnumerableKeys(value), options.budget),
8
9
  name: "keys"
9
10
  }),
10
11
  values: createSandboxClosure({
12
+ sandbox: true,
11
13
  call: ([value]) => budgetSandboxValue(getOwnEnumerableValues(value), options.budget),
12
14
  name: "values"
13
15
  }),
14
16
  entries: createSandboxClosure({
17
+ sandbox: true,
15
18
  call: ([value]) => budgetSandboxValue(getOwnEnumerableEntries(value), options.budget),
16
19
  name: "entries"
17
20
  }),
18
21
  hasOwn: createSandboxClosure({
22
+ sandbox: true,
19
23
  call: ([value, key]) => Reflect.apply(Object.hasOwn, Object, [value, key]),
20
24
  name: "hasOwn"
21
25
  }),
22
26
  is: createSandboxClosure({
27
+ sandbox: true,
23
28
  call: ([left, right]) => Reflect.apply(Object.is, Object, [left, right]),
24
29
  name: "is"
25
30
  }),
26
31
  fromEntries: createSandboxClosure({
32
+ sandbox: true,
27
33
  call: ([value]) => budgetSandboxValue(Reflect.apply(Object.fromEntries, Object, [value]), options.budget),
28
34
  name: "fromEntries"
29
35
  }),
30
36
  freeze: createSandboxClosure({
37
+ sandbox: true,
31
38
  call: ([value]) => {
32
39
  if (typeof value === "object" && value !== null) {
33
40
  Object.freeze(value);
@@ -37,76 +44,93 @@ export function createObjectArrayGlobals(options) {
37
44
  name: "freeze"
38
45
  }),
39
46
  isFrozen: createSandboxClosure({
47
+ sandbox: true,
40
48
  call: ([value]) => Object.isFrozen(value),
41
49
  name: "isFrozen"
42
50
  }),
43
51
  assign: createSandboxClosure({
52
+ sandbox: true,
44
53
  call: ([target, ...sources]) => assignSandboxValues(target, sources),
45
54
  name: "assign"
46
55
  })
47
56
  },
48
57
  Array: createSandboxClosure({
58
+ sandbox: true,
49
59
  call: (args) => createArrayFromConstructorArgs(args, options.budget),
50
60
  construct: (args) => createArrayFromConstructorArgs(args, options.budget),
51
61
  name: "Array",
52
62
  properties: {
53
63
  isArray: createSandboxClosure({
64
+ sandbox: true,
54
65
  call: ([value]) => Array.isArray(value),
55
66
  name: "isArray"
56
67
  }),
57
68
  from: createSandboxClosure({
69
+ sandbox: true,
58
70
  call: (args) => arrayFromSandboxValues(args, options.budget),
59
71
  name: "from"
60
72
  }),
61
73
  of: createSandboxClosure({
74
+ sandbox: true,
62
75
  call: (args) => budgetSandboxValue(Reflect.apply(Array.of, Array, [...args]), options.budget),
63
76
  name: "of"
64
77
  })
65
78
  }
66
79
  }),
67
80
  String: createSandboxClosure({
81
+ sandbox: true,
68
82
  call: ([value]) => options.budget.allocateString(String(value)),
69
83
  name: "String",
70
84
  properties: {
71
85
  raw: createSandboxClosure({
86
+ sandbox: true,
72
87
  call: (args) => stringRaw(args, options.budget),
73
88
  name: "raw"
74
89
  }),
75
90
  fromCharCode: createSandboxClosure({
91
+ sandbox: true,
76
92
  call: (args) => options.budget.allocateString(Reflect.apply(String.fromCharCode, String, [...args])),
77
93
  name: "fromCharCode"
78
94
  }),
79
95
  fromCodePoint: createSandboxClosure({
96
+ sandbox: true,
80
97
  call: (args) => options.budget.allocateString(Reflect.apply(String.fromCodePoint, String, [...args])),
81
98
  name: "fromCodePoint"
82
99
  })
83
100
  }
84
101
  }),
85
102
  Number: createSandboxClosure({
103
+ sandbox: true,
86
104
  call: ([value]) => Number(value),
87
105
  name: "Number",
88
106
  properties: {
89
107
  isFinite: createSandboxClosure({
108
+ sandbox: true,
90
109
  call: ([value]) => typeof value === "number" && Number.isFinite(value),
91
110
  name: "isFinite"
92
111
  }),
93
112
  isNaN: createSandboxClosure({
113
+ sandbox: true,
94
114
  call: ([value]) => typeof value === "number" && Number.isNaN(value),
95
115
  name: "isNaN"
96
116
  }),
97
117
  isInteger: createSandboxClosure({
118
+ sandbox: true,
98
119
  call: ([value]) => typeof value === "number" && Number.isInteger(value),
99
120
  name: "isInteger"
100
121
  }),
101
122
  parseInt: createSandboxClosure({
123
+ sandbox: true,
102
124
  call: (args) => Reflect.apply(Number.parseInt, Number, [...args]),
103
125
  name: "parseInt"
104
126
  }),
105
127
  parseFloat: createSandboxClosure({
128
+ sandbox: true,
106
129
  call: (args) => Reflect.apply(Number.parseFloat, Number, [...args]),
107
130
  name: "parseFloat"
108
131
  }),
109
132
  isSafeInteger: createSandboxClosure({
133
+ sandbox: true,
110
134
  call: ([value]) => typeof value === "number" && Number.isSafeInteger(value),
111
135
  name: "isSafeInteger"
112
136
  }),
@@ -118,6 +142,7 @@ export function createObjectArrayGlobals(options) {
118
142
  }
119
143
  }),
120
144
  Boolean: createSandboxClosure({
145
+ sandbox: true,
121
146
  call: ([value]) => Boolean(value),
122
147
  name: "Boolean"
123
148
  })
@@ -2,6 +2,6 @@ import { createSandboxClosure, createSandboxRegex } from "../values.js";
2
2
  export function createRegexGlobals() {
3
3
  const construct = (args) => createSandboxRegex(String(args[0] ?? ""), String(args[1] ?? ""));
4
4
  return {
5
- RegExp: createSandboxClosure({ name: "RegExp", call: construct, construct })
5
+ RegExp: createSandboxClosure({ sandbox: true, name: "RegExp", call: construct, construct })
6
6
  };
7
7
  }
@@ -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) {
@@ -800,7 +803,9 @@ function isParseResult(value) {
800
803
  Object.hasOwn(value, "span"));
801
804
  }
802
805
  function createBlockContext(node, context) {
803
- const scope = node === context.rootNode || context.generatorResume !== undefined
806
+ const scope = node === context.rootNode ||
807
+ node === context.functionBody ||
808
+ context.generatorResume !== undefined
804
809
  ? context.scope
805
810
  : context.scope.child();
806
811
  const blockContext = {
@@ -813,15 +818,15 @@ function createBlockContext(node, context) {
813
818
  return blockContext;
814
819
  }
815
820
  function predeclareBlockBindings(node, context) {
816
- predeclareStatementListBindings(node.body, context);
821
+ predeclareStatementListBindings(node.body, context, node === context.functionBody || node === context.rootNode);
817
822
  }
818
- function predeclareStatementListBindings(statements, context) {
823
+ function predeclareStatementListBindings(statements, context, functionBody = false) {
819
824
  const { scope } = context;
820
825
  const names = new Set();
821
826
  for (const statement of statements) {
822
827
  if (statement.type === "FunctionDeclaration") {
823
828
  const name = statement.id.name;
824
- if (names.has(name)) {
829
+ if (names.has(name) && !(functionBody && scope.getOwnBindingKind(name) === "var")) {
825
830
  throw new Error(`Cannot redeclare binding '${name}' in the same scope.`);
826
831
  }
827
832
  const closure = createInterpretedClosure(statement, context, evaluateNode);
@@ -835,7 +840,7 @@ function predeclareStatementListBindings(statements, context) {
835
840
  throw new Error(`Cannot redeclare binding '${name}' in the same scope.`);
836
841
  }
837
842
  names.add(name);
838
- scope.declare(name, "const", closure);
843
+ scope.declare(name, functionBody ? "var" : "let", closure);
839
844
  continue;
840
845
  }
841
846
  if (statement.type !== "VariableDeclaration" || statement.kind === "var") {
@@ -1597,7 +1602,7 @@ async function evaluateMemberExpression(node, context) {
1597
1602
  return {
1598
1603
  kind: "normal",
1599
1604
  hasValue: true,
1600
- value: getPromiseMember(member.object, member.property, context.budget)
1605
+ value: getPromiseMember(member.property, context.budget)
1601
1606
  };
1602
1607
  }
1603
1608
  if (isSandboxRegex(member.object)) {
@@ -1634,35 +1639,19 @@ async function evaluateNewExpression(node, context) {
1634
1639
  return callee;
1635
1640
  }
1636
1641
  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
1642
  const args = await evaluateCallArguments(node.arguments, context);
1641
1643
  if (!args.ok) {
1642
1644
  return args.result;
1643
1645
  }
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();
1646
+ if (!isSandboxClosure(callee.value) || callee.value.construct === undefined) {
1647
+ throw new TypeError(`${name} is not a constructor.`);
1665
1648
  }
1649
+ const stack = [...context.callStack, formatStackFrame(node, callee.value.name ?? name)];
1650
+ return {
1651
+ kind: "normal",
1652
+ hasValue: true,
1653
+ value: await invokeSandboxClosure(callee.value, args.value, context, stack, node.span, undefined, true)
1654
+ };
1666
1655
  }
1667
1656
  function getConstructorName(callee) {
1668
1657
  if (callee.type === "Identifier") {
@@ -1839,7 +1828,7 @@ async function evaluateMemberCallExpression(node, context) {
1839
1828
  return evaluateResolvedCallExpression(node, memberValue, context, member.object);
1840
1829
  }
1841
1830
  if (isSandboxPromise(member.object)) {
1842
- return evaluateResolvedCallExpression(node, getPromiseMember(member.object, member.property, context.budget), context, member.object);
1831
+ return evaluateResolvedCallExpression(node, getPromiseMember(member.property, context.budget), context, member.object);
1843
1832
  }
1844
1833
  if (isSandboxRegex(member.object) && isRegexMethodName(member.property)) {
1845
1834
  return evaluateResolvedCallExpression(node, getRegexMember(member.object, member.property), context, member.object);
@@ -2021,6 +2010,10 @@ function applyBinaryOperator(node, left, right, context) {
2021
2010
  case ">>>":
2022
2011
  return toNumber(left) >>> toNumber(right);
2023
2012
  case "instanceof":
2013
+ while (isSandboxClosure(right) && right.boundTarget !== undefined)
2014
+ right = right.boundTarget;
2015
+ if (isSandboxPromiseConstructor(right))
2016
+ return isSandboxPromise(left);
2024
2017
  if (isSandboxMapConstructor(right) && isSandboxMap(left)) {
2025
2018
  return true;
2026
2019
  }
@@ -2322,13 +2315,16 @@ function createSetMethodOptions(context) {
2322
2315
  }
2323
2316
  function createFunctionMethodOptions(context) {
2324
2317
  return {
2325
- callClosure: (closure, args, stack, thisValue) => invokeSandboxClosure(closure, args, context, stack, undefined, thisValue)
2318
+ callClosure: (closure, args, stack, thisValue, construct) => invokeSandboxClosure(closure, args, context, stack, undefined, thisValue, construct)
2326
2319
  };
2327
2320
  }
2328
- async function invokeSandboxClosure(callee, args, context, stack, span, thisValue = undefined) {
2321
+ async function invokeSandboxClosure(callee, args, context, stack, span, thisValue = undefined, construct = false) {
2329
2322
  const leaveCall = context.budget.enterCall();
2330
2323
  try {
2331
- const result = Reflect.apply(callee.call, undefined, [
2324
+ const invoke = construct ? callee.construct : callee.call;
2325
+ if (invoke === undefined)
2326
+ throw new TypeError("Value is not a constructor.");
2327
+ const result = Reflect.apply(invoke, undefined, [
2332
2328
  args,
2333
2329
  {
2334
2330
  stack,
@@ -2339,7 +2335,7 @@ async function invokeSandboxClosure(callee, args, context, stack, span, thisValu
2339
2335
  if (isSandboxPromise(result) && result.synchronousPrefix !== undefined) {
2340
2336
  await result.synchronousPrefix;
2341
2337
  }
2342
- return callee.async === true
2338
+ return !construct && callee.async === true
2343
2339
  ? normalizeClosureResult(wrapHostResult(result, stack), context.budget)
2344
2340
  : await wrapHostResult(result, stack);
2345
2341
  }
@@ -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
  });
@@ -5,6 +5,7 @@ import { type SandboxValue } from "./values.js";
5
5
  type Pattern = VariableDeclarator["id"] | AssignmentPattern | MemberExpression | RestElement;
6
6
  export type PatternTarget = {
7
7
  kind: VariableDeclarationKind;
8
+ initialize?: true;
8
9
  } | {
9
10
  assign: true;
10
11
  };
@@ -20,7 +20,7 @@ export async function bindPattern(pattern, value, target, scope, context) {
20
20
  }
21
21
  }
22
22
  function bindIdentifier(pattern, value, target, scope) {
23
- if ("assign" in target || target.kind === "var") {
23
+ if ("assign" in target || (target.kind === "var" && target.initialize !== true)) {
24
24
  if ("assign" in target) {
25
25
  const binding = scope.lookup(pattern.name);
26
26
  if (!binding.found) {
@@ -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;