poe-code 14.0.51 → 14.0.52

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 (42) hide show
  1. package/dist/index.js +287 -90
  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-7AIY5C3F.js → chunk-DVWTNFTV.js} +208 -48
  6. package/packages/safe-js/dist/chunks/chunk-DVWTNFTV.js.map +7 -0
  7. package/packages/safe-js/dist/chunks/{chunk-MQU3AETK.js → chunk-XIBH4BH2.js} +2 -2
  8. package/packages/safe-js/dist/cli.js +2 -2
  9. package/packages/safe-js/dist/core.js +1 -1
  10. package/packages/safe-js/dist/graph-depth.js +10 -4
  11. package/packages/safe-js/dist/index.js +2 -2
  12. package/packages/safe-js/dist/interp/cancel.js +3 -0
  13. package/packages/safe-js/dist/interp/collection-brands.d.ts +5 -0
  14. package/packages/safe-js/dist/interp/collection-brands.js +8 -0
  15. package/packages/safe-js/dist/interp/collection-iterator.d.ts +27 -0
  16. package/packages/safe-js/dist/interp/collection-iterator.js +61 -0
  17. package/packages/safe-js/dist/interp/globals/collections.js +1 -1
  18. package/packages/safe-js/dist/interp/globals/misc.js +3 -2
  19. package/packages/safe-js/dist/interp/globals/object-array.js +2 -2
  20. package/packages/safe-js/dist/interp/globals/object.js +3 -0
  21. package/packages/safe-js/dist/interp/interpreter.js +15 -10
  22. package/packages/safe-js/dist/interp/iteration.d.ts +2 -1
  23. package/packages/safe-js/dist/interp/iteration.js +4 -1
  24. package/packages/safe-js/dist/interp/methods/collection-iterator.d.ts +4 -0
  25. package/packages/safe-js/dist/interp/methods/collection-iterator.js +19 -0
  26. package/packages/safe-js/dist/interp/methods/map.js +3 -4
  27. package/packages/safe-js/dist/interp/methods/set.js +3 -3
  28. package/packages/safe-js/dist/interp/patterns.d.ts +2 -0
  29. package/packages/safe-js/dist/interp/patterns.js +20 -2
  30. package/packages/safe-js/dist/interp/promise.js +1 -1
  31. package/packages/safe-js/dist/interp/string-coercion.js +4 -0
  32. package/packages/safe-js/dist/interp/values.d.ts +4 -5
  33. package/packages/safe-js/dist/interp/values.js +42 -10
  34. package/packages/safe-js/dist/snapshot/replay-data.d.ts +10 -0
  35. package/packages/safe-js/dist/snapshot/replay-data.js +32 -0
  36. package/packages/safe-js/dist/snapshot/replay-inputs.js +15 -0
  37. package/packages/safe-js/dist/snapshot/restore.js +16 -0
  38. package/packages/safe-js/dist/snapshot/serialize.d.ts +10 -1
  39. package/packages/safe-js/dist/snapshot/serialize.js +25 -9
  40. package/packages/safe-js/dist/snapshot/validation.js +14 -1
  41. package/packages/safe-js/dist/chunks/chunk-7AIY5C3F.js.map +0 -7
  42. /package/packages/safe-js/dist/chunks/{chunk-MQU3AETK.js.map → chunk-XIBH4BH2.js.map} +0 -0
@@ -27,7 +27,7 @@ import {
27
27
  validateMigrationSemantics,
28
28
  validateSnapshotData,
29
29
  validateSnapshotMigration
30
- } from "./chunk-7AIY5C3F.js";
30
+ } from "./chunk-DVWTNFTV.js";
31
31
  import {
32
32
  createFileSystem,
33
33
  createNodeFileSystemAdapterRegistry,
@@ -8245,4 +8245,4 @@ export {
8245
8245
  parseMcpConfig,
8246
8246
  makeMcpModule
8247
8247
  };
8248
- //# sourceMappingURL=chunk-MQU3AETK.js.map
8248
+ //# sourceMappingURL=chunk-XIBH4BH2.js.map
@@ -22,7 +22,7 @@ import {
22
22
  resolveFsConfig,
23
23
  splitFrontmatter,
24
24
  withBrokenPipeGuard
25
- } from "./chunks/chunk-MQU3AETK.js";
25
+ } from "./chunks/chunk-XIBH4BH2.js";
26
26
  import {
27
27
  Budget,
28
28
  SandboxError,
@@ -34,7 +34,7 @@ import {
34
34
  replaceErrorStack,
35
35
  restore,
36
36
  run
37
- } from "./chunks/chunk-7AIY5C3F.js";
37
+ } from "./chunks/chunk-DVWTNFTV.js";
38
38
  import "./chunks/chunk-UXMKEUH3.js";
39
39
  import "./chunks/chunk-5DYEOCT5.js";
40
40
  import "./chunks/chunk-4YIDX44G.js";
@@ -5,7 +5,7 @@ import {
5
5
  defineExtension,
6
6
  lint,
7
7
  run
8
- } from "./chunks/chunk-7AIY5C3F.js";
8
+ } from "./chunks/chunk-DVWTNFTV.js";
9
9
  import "./chunks/chunk-4YIDX44G.js";
10
10
  export {
11
11
  Budget,
@@ -1,6 +1,8 @@
1
1
  import { replaceErrorStack } from "./error/shape.js";
2
2
  import { SandboxError } from "./interp/budget.js";
3
3
  import { getSandboxArgumentEntries, isSandboxArguments } from "./interp/arguments.js";
4
+ import { collectionIteratorState, isSandboxCollectionIterator } from "./interp/collection-iterator.js";
5
+ import { isSandboxMap, isSandboxSet } from "./interp/collection-brands.js";
4
6
  export const MAX_DATA_DEPTH = 1_024;
5
7
  export class SnapshotBudgetError extends Error {
6
8
  code = "budgetExceeded";
@@ -64,16 +66,20 @@ function graphEntries(value) {
64
66
  if (isSandboxArguments(value)) {
65
67
  return getSandboxArgumentEntries(value).map(([key, entry]) => [`.${key}`, entry]);
66
68
  }
67
- if (value instanceof Map) {
68
- return [...value.entries()].flatMap(([key, entry], index) => [
69
+ const map = isSandboxMap(value) ? value.entries : value instanceof Map ? value : undefined;
70
+ if (map !== undefined) {
71
+ return [...map.entries()].flatMap(([key, entry], index) => [
69
72
  [`.<map>[${index}].key`, key],
70
73
  [`.<map>[${index}].value`, entry]
71
74
  ]);
72
75
  }
73
- if (value instanceof Set) {
74
- return [...value.values()].map((entry, index) => [`.<set>[${index}]`, entry]);
76
+ const set = isSandboxSet(value) ? value.values : value instanceof Set ? value : undefined;
77
+ if (set !== undefined) {
78
+ return [...set.values()].map((entry, index) => [`.<set>[${index}]`, entry]);
75
79
  }
76
80
  const entries = [];
81
+ if (isSandboxCollectionIterator(value))
82
+ entries.push([".<collection>", collectionIteratorState(value).collection]);
77
83
  for (const key of Object.keys(value)) {
78
84
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
79
85
  if (descriptor !== undefined && "value" in descriptor)
@@ -26,7 +26,7 @@ import {
26
26
  runWithSpawnUsageAccumulator,
27
27
  splitFrontmatter,
28
28
  supportsSpawnMode
29
- } from "./chunks/chunk-MQU3AETK.js";
29
+ } from "./chunks/chunk-XIBH4BH2.js";
30
30
  import {
31
31
  Budget,
32
32
  FileSnapshotBackend,
@@ -50,7 +50,7 @@ import {
50
50
  registerPendingHostCallPolicy,
51
51
  restore,
52
52
  run
53
- } from "./chunks/chunk-7AIY5C3F.js";
53
+ } from "./chunks/chunk-DVWTNFTV.js";
54
54
  import "./chunks/chunk-UXMKEUH3.js";
55
55
  import "./chunks/chunk-5DYEOCT5.js";
56
56
  import "./chunks/chunk-4YIDX44G.js";
@@ -1,4 +1,5 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { collectionIteratorState, isSandboxCollectionIterator } from "./collection-iterator.js";
2
3
  import { getSandboxPrototype } from "./object-model.js";
3
4
  import { createSandboxPromise, isSandboxClosure, isSandboxMap, isSandboxPromise, isSandboxSet } from "./values.js";
4
5
  import { assertPromiseExecutionAllowed, interruptOnFatalPromiseRejection, onFatalPromiseRejection, observeSandboxPromise, trackSandboxPromise } from "./promise-tracker.js";
@@ -97,6 +98,8 @@ function registerCancelablePromises(value, signal) {
97
98
  pending.push(entry);
98
99
  }
99
100
  else {
101
+ if (isSandboxCollectionIterator(current))
102
+ pending.push(collectionIteratorState(current).collection);
100
103
  for (const descriptor of Object.values(Object.getOwnPropertyDescriptors(current))) {
101
104
  if ("value" in descriptor)
102
105
  pending.push(descriptor.value);
@@ -0,0 +1,5 @@
1
+ import type { SandboxMap, SandboxSet } from "./values.js";
2
+ export declare const sandboxMapBrand: unique symbol;
3
+ export declare const sandboxSetBrand: unique symbol;
4
+ export declare function isSandboxMap(value: unknown): value is SandboxMap;
5
+ export declare function isSandboxSet(value: unknown): value is SandboxSet;
@@ -0,0 +1,8 @@
1
+ export const sandboxMapBrand = Symbol("SandboxMap");
2
+ export const sandboxSetBrand = Symbol("SandboxSet");
3
+ export function isSandboxMap(value) {
4
+ return typeof value === "object" && value !== null && sandboxMapBrand in value;
5
+ }
6
+ export function isSandboxSet(value) {
7
+ return typeof value === "object" && value !== null && sandboxSetBrand in value;
8
+ }
@@ -0,0 +1,27 @@
1
+ import type { Budget } from "./budget.js";
2
+ import type { SandboxMap, SandboxSet, SandboxValue } from "./values.js";
3
+ declare const collectionIteratorBrand: unique symbol;
4
+ export type SandboxCollectionIterator = {
5
+ readonly [collectionIteratorBrand]: true;
6
+ };
7
+ export type CollectionIterationMethod = "keys" | "values" | "entries";
8
+ export type CollectionIteratorSnapshot = {
9
+ collection: SandboxMap | SandboxSet | undefined;
10
+ collectionKind: "map" | "set";
11
+ method: CollectionIterationMethod;
12
+ index: number;
13
+ exhausted: boolean;
14
+ };
15
+ type IteratorState = Omit<CollectionIteratorSnapshot, "index"> & {
16
+ iterator: Iterator<SandboxValue> | undefined;
17
+ };
18
+ export declare function isSandboxCollectionIterator(value: unknown): value is SandboxCollectionIterator;
19
+ export declare function createSandboxCollectionIterator(collection: SandboxMap | SandboxSet, method: CollectionIterationMethod): SandboxCollectionIterator;
20
+ export declare function restoreSandboxCollectionIterator(snapshot: CollectionIteratorSnapshot, target?: SandboxCollectionIterator): SandboxCollectionIterator;
21
+ export declare function collectionIteratorState(value: SandboxCollectionIterator): Readonly<IteratorState>;
22
+ export declare function snapshotCollectionIterator(value: SandboxCollectionIterator): CollectionIteratorSnapshot;
23
+ export declare function nextCollectionIterator(value: SandboxCollectionIterator, budget?: Budget): {
24
+ value: SandboxValue;
25
+ done: boolean;
26
+ };
27
+ export {};
@@ -0,0 +1,61 @@
1
+ const states = new WeakMap();
2
+ export function isSandboxCollectionIterator(value) {
3
+ return typeof value === "object" && value !== null && states.has(value);
4
+ }
5
+ export function createSandboxCollectionIterator(collection, method) {
6
+ return restoreSandboxCollectionIterator({ collection, collectionKind: collection.kind, method, index: 0, exhausted: false });
7
+ }
8
+ // A registered placeholder lets snapshot readers resolve collection/iterator cycles.
9
+ // Initialize its cursor only after the source collection has been fully restored.
10
+ export function restoreSandboxCollectionIterator(snapshot, target = Object.create(null)) {
11
+ const { collection, collectionKind, method, index, exhausted } = snapshot;
12
+ if (collection !== undefined && collection.kind !== collectionKind)
13
+ throw new TypeError("Collection iterator source has the wrong brand.");
14
+ if (!exhausted && collection === undefined)
15
+ throw new TypeError("Live collection iterator requires its source collection.");
16
+ const iterator = exhausted ? undefined : nativeIterator(collection, method);
17
+ for (let skipped = 0; skipped < index; skipped += 1) {
18
+ if (iterator === undefined || iterator.next().done)
19
+ throw new TypeError("Collection iterator cursor exceeds its source.");
20
+ }
21
+ states.set(target, { collection: exhausted ? undefined : collection, collectionKind, method, exhausted, iterator });
22
+ return target;
23
+ }
24
+ export function collectionIteratorState(value) {
25
+ const state = states.get(value);
26
+ if (state === undefined)
27
+ throw new TypeError("Expected a collection iterator.");
28
+ return state;
29
+ }
30
+ export function snapshotCollectionIterator(value) {
31
+ const state = collectionIteratorState(value);
32
+ if (state.exhausted)
33
+ return { ...state, index: 0 };
34
+ const collection = state.collection;
35
+ const size = collection.kind === "map" ? collection.entries.size : collection.values.size;
36
+ let remaining = 0;
37
+ while (!state.iterator.next().done)
38
+ remaining += 1;
39
+ const snapshot = { ...state, index: size - remaining };
40
+ restoreSandboxCollectionIterator(snapshot, value);
41
+ return snapshot;
42
+ }
43
+ export function nextCollectionIterator(value, budget) {
44
+ const state = states.get(value);
45
+ budget?.visitNode();
46
+ if (state.exhausted)
47
+ return { value: undefined, done: true };
48
+ const result = state.iterator.next();
49
+ if (result.done) {
50
+ state.exhausted = true;
51
+ state.collection = undefined;
52
+ state.iterator = undefined;
53
+ return { value: undefined, done: true };
54
+ }
55
+ if (state.method === "entries")
56
+ budget?.allocateArrayLength(2);
57
+ return { value: result.value, done: false };
58
+ }
59
+ function nativeIterator(collection, method) {
60
+ return collection.kind === "map" ? collection.entries[method]() : collection.values[method]();
61
+ }
@@ -71,7 +71,7 @@ function populateCollection(source, collection, { name, budget, context, append,
71
71
  budget.allocateCollectionEntries(0);
72
72
  if (source === undefined || source === null)
73
73
  return collection;
74
- const iterator = getSandboxIterator(source);
74
+ const iterator = getSandboxIterator(source, budget);
75
75
  if (iterator === undefined)
76
76
  throw new TypeError(`${name} constructor requires an iterable.`);
77
77
  let entry;
@@ -1,3 +1,4 @@
1
+ import { isSandboxCollectionIterator } from "../collection-iterator.js";
1
2
  import { assertSandboxGraphDepth } from "../../graph-depth.js";
2
3
  import { allocateProducedSandboxValue, cloneSandboxValue, createSandboxClosure, isSandboxClosure, isSandboxMap, isSandboxPromise, isSandboxSet } from "../values.js";
3
4
  export function createMiscGlobals(options) {
@@ -36,8 +37,8 @@ function structuredCloneSandboxValue(value, budget) {
36
37
  return allocateProducedSandboxValue(clone, budget);
37
38
  }
38
39
  function assertStructuredCloneable(value, seen) {
39
- if (isSandboxClosure(value) || isSandboxPromise(value)) {
40
- throw new TypeError("structuredClone() cannot clone closures or promises.");
40
+ if (isSandboxClosure(value) || isSandboxPromise(value) || isSandboxCollectionIterator(value)) {
41
+ throw new TypeError("structuredClone() cannot clone closures, promises, or collection iterators.");
41
42
  }
42
43
  if (typeof value !== "object" || value === null || seen.has(value)) {
43
44
  return;
@@ -122,7 +122,7 @@ export function createObjectArrayGlobals(options) {
122
122
  fromEntries: createSandboxClosure({
123
123
  sandbox: true,
124
124
  call: ([value], context) => {
125
- const iterator = getSandboxIterator(value);
125
+ const iterator = getSandboxIterator(value, options.budget);
126
126
  if (iterator === undefined) {
127
127
  throw new TypeError("Object.fromEntries requires an iterable.");
128
128
  }
@@ -411,7 +411,7 @@ async function arrayFromSandboxValues(args, budget, context) {
411
411
  const read = (property) => context?.getProperty !== undefined
412
412
  ? context.getProperty(items, property)
413
413
  : getSandboxDataProperty(items, property, budget);
414
- const iterator = getSandboxIterator(items);
414
+ const iterator = getSandboxIterator(items, budget);
415
415
  const constructor = context?.thisValue;
416
416
  let result;
417
417
  let currentValue;
@@ -3,6 +3,7 @@ import { isSandboxDate } from "../date.js";
3
3
  import { isSandboxErrorConstructorInstance } from "../exceptions.js";
4
4
  import { isFloat32Array } from "../float32.js";
5
5
  import { hasHostObjectMember, isGuestHostObject } from "../host-capabilities.js";
6
+ import { collectionIteratorState, isSandboxCollectionIterator } from "../collection-iterator.js";
6
7
  import { getSandboxPrototype, installObjectPrototype, isGuestClosure, markDescriptorObject, materializeFunctionProperties } from "../object-model.js";
7
8
  import { sandboxString } from "../string-coercion.js";
8
9
  import { createSandboxClosure, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet } from "../values.js";
@@ -143,6 +144,8 @@ function typeTag(value) {
143
144
  return "Map";
144
145
  if (isSandboxSet(value))
145
146
  return "Set";
147
+ if (isSandboxCollectionIterator(value))
148
+ return collectionIteratorState(value).collectionKind === "map" ? "Map Iterator" : "Set Iterator";
146
149
  if (isSandboxPromise(value))
147
150
  return "Promise";
148
151
  if (isSandboxGenerator(value))
@@ -27,6 +27,8 @@ import { callSetMethod, getSetMember, isSetMethodName } from "./methods/set.js";
27
27
  import { isSandboxErrorConstructorInstance } from "./globals/error.js";
28
28
  import { hasOwnSandboxProperty } from "./globals/object.js";
29
29
  import { isSandboxMapConstructor, isSandboxSetConstructor } from "./globals/collections.js";
30
+ import { collectionIteratorState, isSandboxCollectionIterator } from "./collection-iterator.js";
31
+ import { getCollectionIteratorMember } from "./methods/collection-iterator.js";
30
32
  import { getFloat32Member, isFloat32ArrayConstructor, setFloat32Member } from "./globals/float32array.js";
31
33
  import { isFloat32Array } from "./float32.js";
32
34
  import { dateString, dateTime, isSandboxDate } from "./date.js";
@@ -734,6 +736,9 @@ function isRestorableBindingValue(value, seen = new WeakSet()) {
734
736
  if (seen.has(value))
735
737
  return true;
736
738
  seen.add(value);
739
+ if (isSandboxCollectionIterator(value)) {
740
+ return isRestorableBindingValue(collectionIteratorState(value).collection, seen);
741
+ }
737
742
  if (isSandboxMap(value)) {
738
743
  for (const [key, entry] of value.entries) {
739
744
  if (!isRestorableBindingValue(key, seen) || !isRestorableBindingValue(entry, seen)) {
@@ -977,7 +982,7 @@ async function evaluateForOfStatement(node, context) {
977
982
  const restored = context.scope.consumeRestoredBinding(restoredIteration.values[0]);
978
983
  if (restored.found && Array.isArray(restored.value)) {
979
984
  restoredEntry = { done: false, value: restored.value[1] };
980
- if (isSandboxMap(restored.value[0]) || isSandboxSet(restored.value[0])) {
985
+ if (isSandboxMap(restored.value[0]) || isSandboxSet(restored.value[0]) || isSandboxCollectionIterator(restored.value[0])) {
981
986
  return evaluateForOfIterator(node, restored.value[0], context, restoredEntry);
982
987
  }
983
988
  }
@@ -1025,7 +1030,7 @@ async function evaluateForOfStatement(node, context) {
1025
1030
  };
1026
1031
  }
1027
1032
  async function evaluateForOfIterator(node, value, context, restoredEntry) {
1028
- const iterator = getSandboxIterator(value);
1033
+ const iterator = getSandboxIterator(value, context.budget);
1029
1034
  if (iterator === undefined) {
1030
1035
  throw new TypeError(`${String(value)} is not a supported iterable`);
1031
1036
  }
@@ -1341,7 +1346,7 @@ function createLoopIterationContext(context, scope) {
1341
1346
  snapshot.loopIterations = {};
1342
1347
  for (const [nodeId, iteration] of context.activeLoopIterations) {
1343
1348
  if (typeof iteration !== "number" &&
1344
- (isSandboxMap(iteration.values[0]) || isSandboxSet(iteration.values[0]))) {
1349
+ (isSandboxMap(iteration.values[0]) || isSandboxSet(iteration.values[0]) || isSandboxCollectionIterator(iteration.values[0]))) {
1345
1350
  const bindingName = `#for-of:${nodeId}`;
1346
1351
  snapshot.bindings[bindingName] = iteration.values;
1347
1352
  snapshot.loopIterations[nodeId] = { index: iteration.index, values: [bindingName] };
@@ -1469,7 +1474,7 @@ async function evaluateYieldDelegate(node, context) {
1469
1474
  if (argument.kind !== "normal") {
1470
1475
  return argument;
1471
1476
  }
1472
- const iterator = getSandboxIterator(argument.value);
1477
+ const iterator = getSandboxIterator(argument.value, context.budget);
1473
1478
  if (iterator === undefined) {
1474
1479
  throw new TypeError(`${String(argument.value)} is not a supported iterable`);
1475
1480
  }
@@ -1679,6 +1684,8 @@ function getPropertyValue(target, property, context) {
1679
1684
  return getMapMember(target, property, createMapMethodOptions(context));
1680
1685
  if (isSandboxSet(target))
1681
1686
  return getSetMember(target, property, createSetMethodOptions(context));
1687
+ if (isSandboxCollectionIterator(target))
1688
+ return getCollectionIteratorMember(target, property, context.budget);
1682
1689
  if (isSandboxGenerator(target))
1683
1690
  return getGeneratorMember(target, property, context.budget);
1684
1691
  if (isSandboxClosure(target))
@@ -1695,6 +1702,7 @@ function getPropertyValue(target, property, context) {
1695
1702
  export function createPatternContext(context, scope = context.scope, evaluate = evaluateNode) {
1696
1703
  const evaluationContext = { ...context, scope };
1697
1704
  return {
1705
+ budget: context.budget,
1698
1706
  evaluate: node => evaluate(node, evaluationContext),
1699
1707
  toPropertyKey: value => toPropertyKey(value, context.budget, createCoercionContext(evaluationContext)),
1700
1708
  getProperty: (value, key) => getPropertyValue(value, key, evaluationContext),
@@ -2141,7 +2149,7 @@ function hasSandboxProperty(value, key, context) {
2141
2149
  if (hasOwnSandboxProperty(current, key, false))
2142
2150
  return true;
2143
2151
  if (Array.isArray(current) || !isPlainSandboxObject(current) ||
2144
- isSandboxDate(current) || isFloat32Array(current) || isSandboxGenerator(current)) {
2152
+ isSandboxDate(current) || isFloat32Array(current) || isSandboxGenerator(current) || isSandboxCollectionIterator(current)) {
2145
2153
  return getPropertyValue(current, key, context) !== undefined;
2146
2154
  }
2147
2155
  current = getSandboxPrototype(current, context.budget);
@@ -2359,7 +2367,7 @@ function getMemberValue(target, property, context) {
2359
2367
  return getClosureMemberValue(current, property, context);
2360
2368
  if (Array.isArray(current))
2361
2369
  return getArrayMemberValue(current, property, context);
2362
- if (!isPlainSandboxObject(current) || isSandboxGenerator(current) || isFloat32Array(current)) {
2370
+ if (!isPlainSandboxObject(current) || isSandboxGenerator(current) || isSandboxCollectionIterator(current) || isFloat32Array(current)) {
2363
2371
  return getPropertyValue(current, property, context);
2364
2372
  }
2365
2373
  if (Object.hasOwn(current, String(property)))
@@ -2578,7 +2586,7 @@ async function evaluateSpreadElement(node, context) {
2578
2586
  result: value
2579
2587
  };
2580
2588
  }
2581
- const iterator = getSpreadIterator(value.value);
2589
+ const iterator = getSandboxIterator(value.value, context.budget);
2582
2590
  if (iterator === undefined) {
2583
2591
  throw new TypeError("Spread arguments must evaluate to an iterable.");
2584
2592
  }
@@ -2647,9 +2655,6 @@ function describeObjectSpreadValue(value) {
2647
2655
  }
2648
2656
  return typeof value;
2649
2657
  }
2650
- function getSpreadIterator(value) {
2651
- return getSandboxIterator(value);
2652
- }
2653
2658
  async function closeIterator(iterator) {
2654
2659
  if (iterator.generator && iterator.return !== undefined) {
2655
2660
  await iterator.return();
@@ -1,4 +1,5 @@
1
1
  import { type SandboxValue } from "./values.js";
2
+ import type { Budget } from "./budget.js";
2
3
  export type SandboxIterator = {
3
4
  readonly generator?: true;
4
5
  snapshotIndex?(): number;
@@ -6,4 +7,4 @@ export type SandboxIterator = {
6
7
  return?(value?: SandboxValue): IteratorResult<SandboxValue> | Promise<IteratorResult<SandboxValue>>;
7
8
  throw?(error?: SandboxValue): IteratorResult<SandboxValue> | Promise<IteratorResult<SandboxValue>>;
8
9
  };
9
- export declare function getSandboxIterator(value: SandboxValue): SandboxIterator | undefined;
10
+ export declare function getSandboxIterator(value: SandboxValue, budget?: Budget): SandboxIterator | undefined;
@@ -2,7 +2,10 @@ import { isSandboxGenerator, isSandboxMap, isSandboxSet } from "./values.js";
2
2
  import { enterRunningState } from "./running-state.js";
3
3
  import { isFloat32Array } from "./float32.js";
4
4
  import { getHostObjectIterator, isGuestHostObject } from "./host-capabilities.js";
5
- export function getSandboxIterator(value) {
5
+ import { isSandboxCollectionIterator, nextCollectionIterator } from "./collection-iterator.js";
6
+ export function getSandboxIterator(value, budget) {
7
+ if (isSandboxCollectionIterator(value))
8
+ return { next: () => nextCollectionIterator(value, budget), snapshotIndex: () => 0 };
6
9
  if (isGuestHostObject(value))
7
10
  return getHostObjectIterator(value);
8
11
  if (isFloat32Array(value)) {
@@ -0,0 +1,4 @@
1
+ import type { Budget } from "../budget.js";
2
+ import { type SandboxCollectionIterator } from "../collection-iterator.js";
3
+ import { type SandboxValue } from "../values.js";
4
+ export declare function getCollectionIteratorMember(target: SandboxCollectionIterator, property: string | number, budget: Budget): SandboxValue;
@@ -0,0 +1,19 @@
1
+ import { collectionIteratorState, isSandboxCollectionIterator, nextCollectionIterator } from "../collection-iterator.js";
2
+ import { createSandboxClosure } from "../values.js";
3
+ export function getCollectionIteratorMember(target, property, budget) {
4
+ if (Object.hasOwn(target, property))
5
+ return target[property];
6
+ if (property !== "next")
7
+ return undefined;
8
+ const { collectionKind } = collectionIteratorState(target);
9
+ return createSandboxClosure({
10
+ sandbox: true,
11
+ name: "next",
12
+ call: (_args, context) => {
13
+ const receiver = context?.thisValue;
14
+ if (!isSandboxCollectionIterator(receiver) || collectionIteratorState(receiver).collectionKind !== collectionKind)
15
+ throw new TypeError("Iterator next requires a matching collection iterator receiver.");
16
+ return nextCollectionIterator(receiver, budget);
17
+ }
18
+ });
19
+ }
@@ -1,6 +1,7 @@
1
- import { allocateProducedSandboxValue, createSandboxClosure, isSandboxClosure, isSandboxMap } from "../values.js";
1
+ import { createSandboxClosure, isSandboxClosure, isSandboxMap } from "../values.js";
2
2
  import { assertCollectionMutable } from "../running-state.js";
3
3
  import { enterKeyedCollectionCallback, updateKeyedCollectionCallbacks } from "./collection-callback.js";
4
+ import { createSandboxCollectionIterator } from "../collection-iterator.js";
4
5
  const mapMethodNames = new Set([
5
6
  "get",
6
7
  "set",
@@ -80,10 +81,8 @@ export async function callMapMethod(target, methodName, args, options, stack = [
80
81
  return undefined;
81
82
  }
82
83
  case "keys":
83
- return allocateProducedSandboxValue([...target.entries.keys()], options.budget);
84
84
  case "values":
85
- return allocateProducedSandboxValue([...target.entries.values()], options.budget);
86
85
  case "entries":
87
- return allocateProducedSandboxValue([...target.entries].map(([key, value]) => [key, value]), options.budget);
86
+ return createSandboxCollectionIterator(target, methodName);
88
87
  }
89
88
  }
@@ -1,6 +1,7 @@
1
- import { allocateProducedSandboxValue, createSandboxClosure, isSandboxClosure, isSandboxSet } from "../values.js";
1
+ import { createSandboxClosure, isSandboxClosure, isSandboxSet } from "../values.js";
2
2
  import { assertCollectionMutable } from "../running-state.js";
3
3
  import { enterKeyedCollectionCallback, updateKeyedCollectionCallbacks } from "./collection-callback.js";
4
+ import { createSandboxCollectionIterator } from "../collection-iterator.js";
4
5
  const setMethodNames = new Set([
5
6
  "add",
6
7
  "has",
@@ -77,8 +78,7 @@ export async function callSetMethod(target, methodName, args, options, stack = [
77
78
  }
78
79
  case "keys":
79
80
  case "values":
80
- return allocateProducedSandboxValue([...target.values], options.budget);
81
81
  case "entries":
82
- return allocateProducedSandboxValue([...target.values].map((value) => [value, value]), options.budget);
82
+ return createSandboxCollectionIterator(target, methodName);
83
83
  }
84
84
  }
@@ -1,6 +1,7 @@
1
1
  import type { AssignmentPattern, MemberExpression, ParseResult, RestElement, VariableDeclarationKind, VariableDeclarator } from "../parse.js";
2
2
  import type { AsyncEvaluationResult } from "./async.js";
3
3
  import type { Scope } from "./scope.js";
4
+ import type { Budget } from "./budget.js";
4
5
  import { type SandboxValue } from "./values.js";
5
6
  type Pattern = VariableDeclarator["id"] | AssignmentPattern | MemberExpression | RestElement;
6
7
  export type PatternTarget = {
@@ -10,6 +11,7 @@ export type PatternTarget = {
10
11
  assign: true;
11
12
  };
12
13
  export type PatternContext = {
14
+ budget?: Budget;
13
15
  evaluate(node: ParseResult): Promise<AsyncEvaluationResult>;
14
16
  toPropertyKey(value: SandboxValue): Promise<string>;
15
17
  getProperty(value: SandboxValue, key: string | number): SandboxValue;
@@ -1,3 +1,4 @@
1
+ import { isSandboxCollectionIterator, nextCollectionIterator } from "./collection-iterator.js";
1
2
  import { isSandboxMap, isSandboxSet, ownEnumerableSandboxEntries } from "./values.js";
2
3
  export async function bindPattern(pattern, value, target, scope, context) {
3
4
  switch (pattern.type) {
@@ -46,13 +47,30 @@ async function bindAssignmentPattern(pattern, value, target, scope, context) {
46
47
  return bindPattern(pattern.left, defaultValue.value, target, scope, context);
47
48
  }
48
49
  async function bindArrayPattern(pattern, value, target, scope, context) {
49
- const values = getArrayPatternValues(value);
50
+ const iterator = isSandboxCollectionIterator(value) ? value : undefined;
51
+ const values = iterator === undefined ? getArrayPatternValues(value) : undefined;
50
52
  for (let index = 0; index < pattern.elements.length; index += 1) {
51
53
  const element = pattern.elements[index];
52
54
  if (element === null) {
55
+ if (iterator !== undefined)
56
+ nextCollectionIterator(iterator, context.budget);
53
57
  continue;
54
58
  }
55
- const elementValue = element.type === "RestElement" ? values.slice(index) : values[index];
59
+ let elementValue;
60
+ if (iterator === undefined) {
61
+ elementValue = element.type === "RestElement" ? values.slice(index) : values[index];
62
+ }
63
+ else if (element.type === "RestElement") {
64
+ const rest = [];
65
+ for (let entry = nextCollectionIterator(iterator, context.budget); !entry.done; entry = nextCollectionIterator(iterator, context.budget)) {
66
+ context.budget?.allocateArrayLength(rest.length + 1);
67
+ rest.push(entry.value);
68
+ }
69
+ elementValue = rest;
70
+ }
71
+ else {
72
+ elementValue = nextCollectionIterator(iterator, context.budget).value;
73
+ }
56
74
  const binding = await bindPattern(element, elementValue, target, scope, context);
57
75
  if (!binding.ok) {
58
76
  return binding;
@@ -337,7 +337,7 @@ async function settleIterable(iterable, method, budget, constructor, context) {
337
337
  const promiseResolve = readPromiseReceiverProperty(constructor, "resolve", prototype);
338
338
  if (!isSandboxClosure(promiseResolve))
339
339
  throw new TypeError("Promise constructor requires a callable resolve.");
340
- const iterator = getSandboxIterator(iterable);
340
+ const iterator = getSandboxIterator(iterable, budget);
341
341
  if (iterator === undefined)
342
342
  throw new TypeError("Promise helpers require an iterable.");
343
343
  let index = 0;
@@ -4,6 +4,7 @@ import { invokeBuiltinClosure } from "./builtin-call.js";
4
4
  import { float32Storage, isFloat32Array } from "./float32.js";
5
5
  import { assertSandboxDataDepth } from "../graph-depth.js";
6
6
  import { isGuestHostObject } from "./host-capabilities.js";
7
+ import { collectionIteratorState, isSandboxCollectionIterator } from "./collection-iterator.js";
7
8
  import { getGuestFunctionProperties, getSandboxPrototype, hasExplicitSandboxPrototype, isGuestClosure } from "./object-model.js";
8
9
  import { getRegexMember } from "./methods/regex.js";
9
10
  import { isSandboxClosure, isSandboxPromise, isSandboxMap, isSandboxSet, isSandboxRegex, isSandboxGenerator } from "./values.js";
@@ -65,6 +66,7 @@ function conversionHook(value, name, budget) {
65
66
  isSandboxClosure(value) ||
66
67
  isSandboxMap(value) ||
67
68
  isSandboxSet(value) ||
69
+ isSandboxCollectionIterator(value) ||
68
70
  isSandboxPromise(value) ||
69
71
  isSandboxRegex(value) ||
70
72
  isSandboxGenerator(value) ||
@@ -97,6 +99,8 @@ async function defaultToString(value, budget, context, joining) {
97
99
  return "[object Map]";
98
100
  if (isSandboxSet(value))
99
101
  return "[object Set]";
102
+ if (isSandboxCollectionIterator(value))
103
+ return collectionIteratorState(value).collectionKind === "map" ? "[object Map Iterator]" : "[object Set Iterator]";
100
104
  if (isSandboxGenerator(value))
101
105
  return "[object Generator]";
102
106
  if (isSandboxRegex(value)) {
@@ -1,18 +1,19 @@
1
+ import { sandboxMapBrand, sandboxSetBrand } from "./collection-brands.js";
2
+ import { type SandboxCollectionIterator } from "./collection-iterator.js";
1
3
  import type { Budget, CompileTicket } from "./budget.js";
2
4
  import { CompileScope } from "./regex/compile-guard.js";
3
5
  import type { GeneratorChannel } from "./generator.js";
4
6
  import { type RegexPattern } from "./regex/parse.js";
5
7
  export { createSandboxArguments, isSandboxArguments } from "./arguments.js";
8
+ export { isSandboxMap, isSandboxSet } from "./collection-brands.js";
6
9
  declare const sandboxClosureBrand: unique symbol;
7
10
  declare const sandboxGeneratorBrand: unique symbol;
8
- declare const sandboxMapBrand: unique symbol;
9
11
  declare const sandboxPromiseBrand: unique symbol;
10
12
  declare const sandboxRegexBrand: unique symbol;
11
13
  declare const sandboxRegexPattern: unique symbol;
12
- declare const sandboxSetBrand: unique symbol;
13
14
  declare const sandboxRetainedValues: unique symbol;
14
15
  export type SandboxPrimitive = string | number | boolean | null | undefined;
15
- export type SandboxValue = SandboxPrimitive | Date | Float32Array | SandboxObject | SandboxArray | SandboxClosure | SandboxGenerator | SandboxMap | SandboxSet | SandboxPromise | SandboxRegex;
16
+ export type SandboxValue = SandboxPrimitive | Date | Float32Array | SandboxObject | SandboxArray | SandboxClosure | SandboxGenerator | SandboxCollectionIterator | SandboxMap | SandboxSet | SandboxPromise | SandboxRegex;
16
17
  export type SandboxObject = {
17
18
  [key: string]: SandboxValue;
18
19
  };
@@ -123,9 +124,7 @@ export declare function createSandboxSet(values?: Iterable<SandboxValue>): Sandb
123
124
  export declare function createSandboxRegex(source: string, flags?: string, lastIndex?: number, compilation?: CompileScope): SandboxRegex;
124
125
  export declare function getSandboxRegexPattern(regex: SandboxRegex): RegexPattern;
125
126
  export declare function isSandboxClosure(value: unknown): value is SandboxClosure;
126
- export declare function isSandboxMap(value: unknown): value is SandboxMap;
127
127
  export declare function isSandboxPromise(value: unknown): value is SandboxPromise;
128
- export declare function isSandboxSet(value: unknown): value is SandboxSet;
129
128
  export declare function isSandboxGenerator(value: unknown): value is SandboxGenerator;
130
129
  export declare function isSandboxRegex(value: unknown): value is SandboxRegex;
131
130
  export declare function deepCopyToSandbox(value: unknown): SandboxValue;