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
@@ -1,4 +1,6 @@
1
1
  import { bindOtelSpan, getBoundOtelSpan } from "../observability/otel.js";
2
+ import { isSandboxMap, isSandboxSet, sandboxMapBrand, sandboxSetBrand } from "./collection-brands.js";
3
+ import { collectionIteratorState, isSandboxCollectionIterator, restoreSandboxCollectionIterator, snapshotCollectionIterator } from "./collection-iterator.js";
2
4
  import { copyNativeDate, exportDate, isSandboxDate } from "./date.js";
3
5
  import { getHostObjectKeys, getHostObjectMember, hasHostObjectMember, measureHostObjectData, isGuestHostObject, isLiveCapability } from "./host-capabilities.js";
4
6
  import { types as nodeTypes } from "node:util";
@@ -14,13 +16,12 @@ import { sandboxErrorTypes } from "../error/shape.js";
14
16
  import { getGuestFunctionProperties, getSandboxPrototype, hasGuestObjectState, hasManagedDescriptors, isGuestClosure, registerGuestClosure } from "./object-model.js";
15
17
  import { copySandboxArgumentProperties, createSandboxArguments, getSandboxArgumentEntries, isSandboxArguments } from "./arguments.js";
16
18
  export { createSandboxArguments, isSandboxArguments } from "./arguments.js";
19
+ export { isSandboxMap, isSandboxSet } from "./collection-brands.js";
17
20
  const sandboxClosureBrand = Symbol("SandboxClosure");
18
21
  const sandboxGeneratorBrand = Symbol("SandboxGenerator");
19
- const sandboxMapBrand = Symbol("SandboxMap");
20
22
  const sandboxPromiseBrand = Symbol("SandboxPromise");
21
23
  const sandboxRegexBrand = Symbol("SandboxRegex");
22
24
  const sandboxRegexPattern = Symbol("SandboxRegexPattern");
23
- const sandboxSetBrand = Symbol("SandboxSet");
24
25
  const sandboxRetainedValues = Symbol("SandboxRetainedValues");
25
26
  export function createSandboxClosure(input) {
26
27
  const closure = {
@@ -195,15 +196,9 @@ export function getSandboxRegexPattern(regex) {
195
196
  export function isSandboxClosure(value) {
196
197
  return typeof value === "object" && value !== null && sandboxClosureBrand in value;
197
198
  }
198
- export function isSandboxMap(value) {
199
- return typeof value === "object" && value !== null && sandboxMapBrand in value;
200
- }
201
199
  export function isSandboxPromise(value) {
202
200
  return typeof value === "object" && value !== null && sandboxPromiseBrand in value;
203
201
  }
204
- export function isSandboxSet(value) {
205
- return typeof value === "object" && value !== null && sandboxSetBrand in value;
206
- }
207
202
  export function isSandboxGenerator(value) {
208
203
  return typeof value === "object" && value !== null && sandboxGeneratorBrand in value;
209
204
  }
@@ -216,9 +211,14 @@ export function deepCopyToSandbox(value) {
216
211
  });
217
212
  }
218
213
  export function cloneSandboxValue(value) {
219
- return copyToSandbox(value, {
220
- seen: new WeakMap()
214
+ const initializeIterators = [];
215
+ const copy = copyToSandbox(value, {
216
+ seen: new WeakMap(),
217
+ initializeIterators
221
218
  }, "<root>", true);
219
+ for (const initialize of initializeIterators)
220
+ initialize();
221
+ return copy;
222
222
  }
223
223
  export function allocateProducedSandboxValue(value, budget) {
224
224
  allocateSandboxValue(value, budget, new WeakSet());
@@ -289,6 +289,15 @@ export function measureSandboxData(values, options = {}) {
289
289
  }
290
290
  return;
291
291
  }
292
+ if (isSandboxCollectionIterator(value)) {
293
+ visit(collectionIteratorState(value).collection, depth + 1);
294
+ for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
295
+ usage += key.length + 1;
296
+ if ("value" in descriptor)
297
+ visit(descriptor.value, depth + 1);
298
+ }
299
+ return;
300
+ }
292
301
  if (isSandboxSet(value)) {
293
302
  usage += value.values.size;
294
303
  for (const entry of value.values)
@@ -409,6 +418,26 @@ function copyToSandbox(value, state, path = "<root>", cloneSandboxCollections =
409
418
  isSandboxPromise(value)) {
410
419
  return value;
411
420
  }
421
+ if (isSandboxCollectionIterator(value)) {
422
+ if (hasGuestObjectState(value))
423
+ throw new TypeError("Guest prototype links and custom descriptors cannot be copied as data.");
424
+ if (!cloneSandboxCollections)
425
+ return value;
426
+ const existing = state.seen.get(value);
427
+ if (existing !== undefined)
428
+ return existing;
429
+ const snapshot = snapshotCollectionIterator(value);
430
+ const copy = restoreSandboxCollectionIterator({ ...snapshot, collection: undefined, index: 0, exhausted: true });
431
+ state.seen.set(value, copy);
432
+ const collection = copyToSandbox(snapshot.collection, state, `${path}.<collection>`, true, depth + 1);
433
+ if (collection !== undefined && !isSandboxMap(collection) && !isSandboxSet(collection))
434
+ throw new TypeError("Invalid cloned collection iterator source.");
435
+ state.initializeIterators.push(() => { restoreSandboxCollectionIterator({ ...snapshot, collection }, copy); });
436
+ for (const entry of getEnumerableObjectEntries(value, path)) {
437
+ defineOwnDataProperty(copy, entry.key, copyToSandbox(entry.value, state, joinPath(path, entry.key), true, depth + 1));
438
+ }
439
+ return copy;
440
+ }
412
441
  if (typeof value === "object" && value !== null && hasGuestObjectState(value)) {
413
442
  throw new TypeError("Guest prototype links and custom descriptors cannot be copied as data.");
414
443
  }
@@ -634,6 +663,9 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
634
663
  if (isSandboxGenerator(value)) {
635
664
  throw new TypeError("Sandbox generators cannot cross into host values.");
636
665
  }
666
+ if (isSandboxCollectionIterator(value)) {
667
+ throw new TypeError("Sandbox collection iterators cannot cross into host values.");
668
+ }
637
669
  if (isSandboxRegex(value)) {
638
670
  throw new TypeError("Invalid sandbox RegExp brand.");
639
671
  }
@@ -1,3 +1,4 @@
1
+ import { type CollectionIterationMethod } from "../interp/collection-iterator.js";
1
2
  import { CompileScope } from "../interp/regex/compile-guard.js";
2
3
  import { type Float32Data } from "./float32array.js";
3
4
  import { type SandboxErrorName } from "../error/shape.js";
@@ -25,6 +26,15 @@ type Properties = Record<string, {
25
26
  writable: boolean;
26
27
  }>;
27
28
  type DataNode = {
29
+ kind: "collection-iterator";
30
+ collectionKind: "map" | "set";
31
+ method: CollectionIterationMethod;
32
+ collection: Atom;
33
+ index: number;
34
+ exhausted: boolean;
35
+ properties: Properties;
36
+ extensible: boolean;
37
+ } | {
28
38
  kind: "date";
29
39
  time: number | null;
30
40
  } | (Float32Data<Atom> & {
@@ -1,4 +1,5 @@
1
1
  import { MAX_DATA_DEPTH } from "../graph-depth.js";
2
+ import { isSandboxCollectionIterator, restoreSandboxCollectionIterator, snapshotCollectionIterator } from "../interp/collection-iterator.js";
2
3
  import { hasGuestObjectState } from "../interp/object-model.js";
3
4
  import { CompileScope } from "../interp/regex/compile-guard.js";
4
5
  import { float32DataProperties, isFloat32Array } from "../interp/float32.js";
@@ -83,6 +84,16 @@ export function encodeReplayData(value, options = {}) {
83
84
  }
84
85
  nodes[id] = { ...storage, properties, extensible: Object.isExtensible(entry) };
85
86
  }
87
+ else if (isSandboxCollectionIterator(entry)) {
88
+ const snapshot = snapshotCollectionIterator(entry);
89
+ const properties = Object.create(null);
90
+ for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(entry))) {
91
+ if (!("value" in descriptor))
92
+ throw new TypeError(`Cannot record replay data accessor '${key}'.`);
93
+ properties[key] = { value: child(descriptor.value, JSON.stringify(["property", key])), configurable: descriptor.configurable === true, enumerable: descriptor.enumerable === true, writable: descriptor.writable === true };
94
+ }
95
+ nodes[id] = { kind: "collection-iterator", collectionKind: snapshot.collectionKind, method: snapshot.method, collection: child(snapshot.collection, "<collection>"), index: snapshot.index, exhausted: snapshot.exhausted, properties, extensible: Object.isExtensible(entry) };
96
+ }
86
97
  else if (isSandboxMap(entry)) {
87
98
  nodes[id] = {
88
99
  kind: "map",
@@ -146,6 +157,7 @@ export function decodeReplayData(input, options = {}, parent) {
146
157
  const graph = record(input);
147
158
  const nodes = list(own(graph, "nodes"));
148
159
  const restored = new Map();
160
+ const initializeIterators = [];
149
161
  const decode = (entry, depth = 0) => {
150
162
  if (depth > MAX_DATA_DEPTH)
151
163
  throw new TypeError("Replay data exceeds the nesting limit.");
@@ -252,6 +264,24 @@ export function decodeReplayData(input, options = {}, parent) {
252
264
  Object.preventExtensions(result);
253
265
  return result;
254
266
  }
267
+ if (kind === "collection-iterator") {
268
+ const collectionKind = own(node, "collectionKind");
269
+ const method = own(node, "method");
270
+ const index = own(node, "index");
271
+ const exhausted = own(node, "exhausted");
272
+ if ((collectionKind !== "map" && collectionKind !== "set") || (method !== "keys" && method !== "values" && method !== "entries") || typeof index !== "number" || !Number.isSafeInteger(index) || index < 0 || typeof exhausted !== "boolean" || typeof node.extensible !== "boolean")
273
+ throw new TypeError("Invalid replay collection iterator.");
274
+ const result = restoreSandboxCollectionIterator({ collection: undefined, collectionKind, method, index: 0, exhausted: true });
275
+ restored.set(id, result);
276
+ const collection = child(own(node, "collection"));
277
+ if (collection !== undefined && !isSandboxMap(collection) && !isSandboxSet(collection))
278
+ throw new TypeError("Invalid replay collection iterator source.");
279
+ initializeIterators.push(() => { restoreSandboxCollectionIterator({ collection, collectionKind, method, index, exhausted }, result); });
280
+ defineProperties(result, record(own(node, "properties")), child);
281
+ if (!node.extensible)
282
+ Object.preventExtensions(result);
283
+ return result;
284
+ }
255
285
  if (kind === "map") {
256
286
  const result = createSandboxMap();
257
287
  restored.set(id, result);
@@ -320,6 +350,8 @@ export function decodeReplayData(input, options = {}, parent) {
320
350
  return result;
321
351
  };
322
352
  const result = decode(own(graph, "root"));
353
+ for (const initialize of initializeIterators)
354
+ initialize();
323
355
  if (parent !== undefined)
324
356
  compilation.forward(compilation.tickets, parent);
325
357
  return result;
@@ -1,3 +1,4 @@
1
+ import { collectionIteratorState, isSandboxCollectionIterator } from "../interp/collection-iterator.js";
1
2
  import { createSandboxPromise, isSandboxClosure, isSandboxMap, isSandboxSet, isSandboxPromise } from "../interp/values.js";
2
3
  import { decodeReplayData, encodeReplayData } from "./replay-data.js";
3
4
  import { CompileScope } from "../interp/regex/compile-guard.js";
@@ -41,6 +42,18 @@ export function prepareReplayInputs(current, saved, preparePromise, onCapability
41
42
  throw new TypeError("Invalid replay input capability path.");
42
43
  let value = current;
43
44
  for (const key of path) {
45
+ if (isSandboxCollectionIterator(value)) {
46
+ if (key === "<collection>")
47
+ value = collectionIteratorState(value).collection;
48
+ else {
49
+ const property = JSON.parse(key);
50
+ if (!Array.isArray(property) || property.length !== 2 || property[0] !== "property" || typeof property[1] !== "string")
51
+ throw new TypeError("Invalid replay input iterator capability path.");
52
+ const descriptor = Object.getOwnPropertyDescriptor(value, property[1]);
53
+ value = descriptor !== undefined && "value" in descriptor ? descriptor.value : undefined;
54
+ }
55
+ continue;
56
+ }
44
57
  if (isSandboxMap(value)) {
45
58
  const [kind, ordinal] = key.split(":");
46
59
  const index = Number(ordinal);
@@ -104,6 +117,7 @@ function assertReplayInputShape(restored) {
104
117
  Array.isArray(restored) ||
105
118
  isSandboxClosure(restored) ||
106
119
  isSandboxPromise(restored) ||
120
+ isSandboxCollectionIterator(restored) ||
107
121
  isSandboxMap(restored) ||
108
122
  isSandboxSet(restored))
109
123
  throw new TypeError("Invalid replay inputs.");
@@ -114,6 +128,7 @@ function assertReplayInputShape(restored) {
114
128
  Array.isArray(values[key]) ||
115
129
  isSandboxClosure(values[key]) ||
116
130
  isSandboxPromise(values[key]) ||
131
+ isSandboxCollectionIterator(values[key]) ||
117
132
  isSandboxMap(values[key]) ||
118
133
  isSandboxSet(values[key]))
119
134
  throw new TypeError(`Invalid replay input ${key}.`);
@@ -9,6 +9,8 @@ import { SnapshotMismatchError } from "../restore.js";
9
9
  import { Scope, setSandboxProperty } from "../interp/interpreter.js";
10
10
  import { getGuestFunctionProperties, getGuestFunctionProperty, getSandboxDataProperty, registerGuestClosure, setSandboxPrototype } from "../interp/object-model.js";
11
11
  import { wrapCallerInjectedBindings } from "../interp/host-bridge.js";
12
+ import { restoreSandboxCollectionIterator } from "../interp/collection-iterator.js";
13
+ import { isSandboxMap, isSandboxSet } from "../interp/values.js";
12
14
  import { createSandboxArguments, createSandboxClosure, createSandboxGenerator, createSandboxMap, createSandboxPromise, createSandboxRegex, createSandboxSet, reconcileCompiledValues } from "../interp/values.js";
13
15
  import { createGeneratorChannel, restoreGeneratorChannel } from "../interp/generator.js";
14
16
  import { hashSource } from "../parse/hash.js";
@@ -45,6 +47,7 @@ export function restore(snapshot, options, owner) {
45
47
  throw new Error(`Snapshot references unknown AST node ${snapshot.currentAstNodeId}.`);
46
48
  }
47
49
  const state = {
50
+ initializeIterators: [],
48
51
  budget,
49
52
  compilation,
50
53
  heap: snapshot.heap ?? {},
@@ -71,6 +74,8 @@ export function restore(snapshot, options, owner) {
71
74
  throw new Error(`Snapshot references unknown scope ${String(currentScopeId)}.`);
72
75
  }
73
76
  const callStack = snapshot.callStack.map((frame) => restoreCallFrame(frame, state));
77
+ for (const initialize of state.initializeIterators)
78
+ initialize();
74
79
  reconcileCompiledValues(budget, [
75
80
  ...currentScope.retainedValues(),
76
81
  ...pendingPromises.flatMap((pending) => Object.values(pending).filter(isSandboxSnapshotValue))
@@ -397,6 +402,17 @@ function restoreHeapValue(id, state) {
397
402
  Object.preventExtensions(args);
398
403
  return args;
399
404
  }
405
+ if (serialized.kind === "collection-iterator") {
406
+ const iterator = restoreSandboxCollectionIterator({ collection: undefined, collectionKind: serialized.collectionKind, method: serialized.method, index: 0, exhausted: true });
407
+ state.heapValueById.set(id, iterator);
408
+ const collection = deserializeValue(serialized.collection, state);
409
+ if (collection !== undefined && !isSandboxMap(collection) && !isSandboxSet(collection))
410
+ throw new TypeError("Invalid collection iterator source.");
411
+ state.initializeIterators.push(() => { restoreSandboxCollectionIterator({ ...serialized, collection }, iterator); });
412
+ for (const [key, entry] of Object.entries(serialized.entries))
413
+ Object.defineProperty(iterator, key, { value: deserializeValue(entry, state), enumerable: true, configurable: true, writable: true });
414
+ return iterator;
415
+ }
400
416
  if (serialized.kind === "map") {
401
417
  const map = createSandboxMap();
402
418
  state.heapValueById.set(id, map);
@@ -1,3 +1,4 @@
1
+ import { type CollectionIterationMethod, type SandboxCollectionIterator } from "../interp/collection-iterator.js";
1
2
  import { type SandboxErrorName } from "../error/shape.js";
2
3
  import { type SerializedArguments } from "./arguments.js";
3
4
  import { type SerializedArray } from "./arrays.js";
@@ -41,6 +42,14 @@ export type SerializedReferenceValue = {
41
42
  id: number;
42
43
  };
43
44
  export type SerializedHeapValue = {
45
+ kind: "collection-iterator";
46
+ collectionKind: "map" | "set";
47
+ method: CollectionIterationMethod;
48
+ collection: SerializedSnapshotValue;
49
+ index: number;
50
+ exhausted: boolean;
51
+ entries: Record<string, SerializedSnapshotValue>;
52
+ } | {
44
53
  kind: "date";
45
54
  time: number | null;
46
55
  } | (Float32Data<SerializedReferenceValue> & {
@@ -75,7 +84,7 @@ export type RuntimePromiseValue = {
75
84
  id: SnapshotId;
76
85
  promise?: Promise<unknown>;
77
86
  };
78
- export type RuntimeSnapshotValue = Date | Float32Array | boolean | null | number | string | undefined | RuntimeClosureValue | SandboxGenerator | RuntimePromiseValue | SandboxMap | SandboxRegex | SandboxSet | RuntimeSnapshotValue[] | {
87
+ export type RuntimeSnapshotValue = Date | Float32Array | boolean | null | number | string | undefined | RuntimeClosureValue | SandboxGenerator | SandboxCollectionIterator | RuntimePromiseValue | SandboxMap | SandboxRegex | SandboxSet | RuntimeSnapshotValue[] | {
79
88
  [key: string]: RuntimeSnapshotValue;
80
89
  };
81
90
  export type RuntimeScopeFrame = {
@@ -1,4 +1,5 @@
1
1
  import { hashSource } from "../parse/hash.js";
2
+ import { collectionIteratorState, isSandboxCollectionIterator, snapshotCollectionIterator } from "../interp/collection-iterator.js";
2
3
  import { hasGuestObjectState } from "../interp/object-model.js";
3
4
  import { sandboxErrorTypes } from "../error/shape.js";
4
5
  import { assertSnapshotGraphDepth } from "../graph-depth.js";
@@ -162,7 +163,7 @@ function serializeValue(value, path, state) {
162
163
  if (isSandboxRegex(value)) {
163
164
  return { kind: "regex", source: value.source, flags: value.flags, lastIndex: value.lastIndex };
164
165
  }
165
- if (isSandboxDate(value) || isSandboxMap(value) || isSandboxSet(value) || isFloat32Array(value)) {
166
+ if (isSandboxDate(value) || isSandboxMap(value) || isSandboxSet(value) || isSandboxCollectionIterator(value) || isFloat32Array(value)) {
166
167
  const reference = serializeHeapReference(value, path, state);
167
168
  if (reference === undefined) {
168
169
  throw new TypeError(`Cannot serialize collection without a heap reference at ${path}.`);
@@ -191,7 +192,18 @@ function serializeHeapReference(value, path, state) {
191
192
  }
192
193
  if (!state.serializedHeapIds.has(id)) {
193
194
  state.serializedHeapIds.add(id);
194
- if (isSandboxDate(value)) {
195
+ if (isSandboxCollectionIterator(value)) {
196
+ const snapshot = snapshotCollectionIterator(value);
197
+ const entries = Object.create(null);
198
+ state.heap[String(id)] = {
199
+ kind: "collection-iterator", collectionKind: snapshot.collectionKind, method: snapshot.method,
200
+ collection: serializeValue(snapshot.collection, `${path}.<collection>`, state),
201
+ index: snapshot.index, exhausted: snapshot.exhausted, entries
202
+ };
203
+ for (const [key, entry] of Object.entries(value))
204
+ entries[key] = serializeValue(entry, `${path}.${key}`, state);
205
+ }
206
+ else if (isSandboxDate(value)) {
195
207
  state.heap[String(id)] = { kind: "date", time: serializedDateTime(value) };
196
208
  }
197
209
  else if (isFloat32Array(value)) {
@@ -306,6 +318,7 @@ function indexHeapContainers(input) {
306
318
  (Array.isArray(value) && requiresArrayEntries(value)) ||
307
319
  sandboxErrorTypes.has(value) ||
308
320
  isSandboxArguments(value) ||
321
+ isSandboxCollectionIterator(value) ||
309
322
  isSandboxMap(value) ||
310
323
  isSandboxSet(value)) {
311
324
  heapIds.set(value, nextId);
@@ -327,6 +340,7 @@ function collectContainerStats(value, stats, ancestors) {
327
340
  !isSandboxDate(value) &&
328
341
  !isFloat32Array(value) &&
329
342
  !isSandboxMap(value) &&
343
+ !isSandboxCollectionIterator(value) &&
330
344
  !isSandboxSet(value)) {
331
345
  return;
332
346
  }
@@ -349,13 +363,15 @@ function collectContainerStats(value, stats, ancestors) {
349
363
  }
350
364
  stat.expanded = true;
351
365
  ancestors.add(value);
352
- const entries = isSandboxArguments(value)
353
- ? Object.values(Object.getOwnPropertyDescriptors(value)).flatMap((descriptor) => "value" in descriptor ? [descriptor.value] : [])
354
- : isSandboxMap(value)
355
- ? [...value.entries].flatMap(([key, entry]) => [key, entry])
356
- : isSandboxSet(value)
357
- ? [...value.values]
358
- : Object.values(value);
366
+ const entries = isSandboxCollectionIterator(value)
367
+ ? [collectionIteratorState(value).collection, ...Object.values(value)]
368
+ : isSandboxArguments(value)
369
+ ? Object.values(Object.getOwnPropertyDescriptors(value)).flatMap((descriptor) => "value" in descriptor ? [descriptor.value] : [])
370
+ : isSandboxMap(value)
371
+ ? [...value.entries].flatMap(([key, entry]) => [key, entry])
372
+ : isSandboxSet(value)
373
+ ? [...value.values]
374
+ : Object.values(value);
359
375
  for (const entry of entries) {
360
376
  collectContainerStats(entry, stats, ancestors);
361
377
  }
@@ -364,6 +364,7 @@ function validateTaggedValue(record, path, state) {
364
364
  case "object":
365
365
  case "map":
366
366
  case "set":
367
+ case "collection-iterator":
367
368
  return;
368
369
  }
369
370
  }
@@ -407,7 +408,7 @@ function validateGeneratorShape(record, path, state) {
407
408
  function validateHeapValue(value, path, state) {
408
409
  const record = requireRecord(value, path);
409
410
  validateErrorType(record, path);
410
- if (!["arguments", "array", "object", "map", "set", "float32array", "date"].includes(String(record.kind)))
411
+ if (!["arguments", "array", "object", "map", "set", "float32array", "date", "collection-iterator"].includes(String(record.kind)))
411
412
  fail("unknownTag", `${path}.kind`, "unknown heap tag");
412
413
  validateValue(record, path, 1, state);
413
414
  if (record.kind === "arguments")
@@ -431,6 +432,18 @@ function validateHeapValue(value, path, state) {
431
432
  }
432
433
  if (record.kind === "set")
433
434
  requireArray(record.values, `${path}.values`, state);
435
+ if (record.kind === "collection-iterator") {
436
+ if (record.collectionKind !== "map" && record.collectionKind !== "set")
437
+ fail("invalidValue", `${path}.collectionKind`, "invalid iterator brand");
438
+ if (record.method !== "keys" && record.method !== "values" && record.method !== "entries")
439
+ fail("invalidValue", `${path}.method`, "invalid iteration method");
440
+ if (typeof record.exhausted !== "boolean")
441
+ fail("invalidValue", `${path}.exhausted`, "invalid iterator exhaustion");
442
+ requireSafeInteger(record.index, `${path}.index`, 0);
443
+ if (!Object.hasOwn(record, "collection"))
444
+ fail("invalidValue", `${path}.collection`, "missing iterator source");
445
+ requireRecord(record.entries, `${path}.entries`);
446
+ }
434
447
  }
435
448
  function validateDateRecord(record, path) {
436
449
  if (Object.keys(record).length !== 2)