poe-code 14.0.83 → 14.0.84

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 +588 -93
  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-37V6Q4K3.js → chunk-2EZQJQ2C.js} +2 -2
  6. package/packages/safe-js/dist/chunks/{chunk-PT7PBLVT.js → chunk-CYVI4ZUK.js} +536 -85
  7. package/packages/safe-js/dist/chunks/chunk-CYVI4ZUK.js.map +7 -0
  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/index.js +2 -2
  11. package/packages/safe-js/dist/interp/array-buffer.d.ts +12 -0
  12. package/packages/safe-js/dist/interp/array-buffer.js +33 -0
  13. package/packages/safe-js/dist/interp/float32.d.ts +6 -1
  14. package/packages/safe-js/dist/interp/float32.js +38 -10
  15. package/packages/safe-js/dist/interp/globals/array-buffer.d.ts +3 -0
  16. package/packages/safe-js/dist/interp/globals/array-buffer.js +112 -0
  17. package/packages/safe-js/dist/interp/globals/float32array.d.ts +1 -1
  18. package/packages/safe-js/dist/interp/globals/float32array.js +104 -15
  19. package/packages/safe-js/dist/interp/globals.d.ts +1 -0
  20. package/packages/safe-js/dist/interp/globals.js +2 -0
  21. package/packages/safe-js/dist/interp/host-bridge.d.ts +1 -0
  22. package/packages/safe-js/dist/interp/host-bridge.js +21 -0
  23. package/packages/safe-js/dist/interp/host-call.js +26 -2
  24. package/packages/safe-js/dist/interp/methods/array-iterator.js +1 -1
  25. package/packages/safe-js/dist/interp/object-model.js +6 -0
  26. package/packages/safe-js/dist/interp/values.d.ts +1 -1
  27. package/packages/safe-js/dist/interp/values.js +47 -5
  28. package/packages/safe-js/dist/lint/rules/known-globals.d.ts +1 -1
  29. package/packages/safe-js/dist/lint/rules/known-globals.js +1 -0
  30. package/packages/safe-js/dist/snapshot/array-buffer.d.ts +13 -0
  31. package/packages/safe-js/dist/snapshot/array-buffer.js +40 -0
  32. package/packages/safe-js/dist/snapshot/dump-format.js +25 -9
  33. package/packages/safe-js/dist/snapshot/float32array.d.ts +8 -1
  34. package/packages/safe-js/dist/snapshot/float32array.js +23 -5
  35. package/packages/safe-js/dist/snapshot/replay-data.d.ts +5 -0
  36. package/packages/safe-js/dist/snapshot/replay-data.js +35 -10
  37. package/packages/safe-js/dist/snapshot/restore.js +13 -1
  38. package/packages/safe-js/dist/snapshot/serialize.d.ts +5 -2
  39. package/packages/safe-js/dist/snapshot/serialize.js +20 -11
  40. package/packages/safe-js/dist/snapshot/validation.js +11 -1
  41. package/packages/safe-js/dist/chunks/chunk-PT7PBLVT.js.map +0 -7
  42. /package/packages/safe-js/dist/chunks/{chunk-37V6Q4K3.js.map → chunk-2EZQJQ2C.js.map} +0 -0
@@ -1,5 +1,6 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
- import { float32DataProperties, float32Storage, isFloat32Array } from "./float32.js";
2
+ import { float32DataProperties, float32Storage, float32ViewLayouts, isFloat32Array } from "./float32.js";
3
+ import { arrayBufferDataProperties, arrayBufferLength, arrayBufferOptions, isSandboxArrayBuffer } from "./array-buffer.js";
3
4
  import { copyNativeDate, serializedDateTime } from "./date.js";
4
5
  import { cloneSandboxValue, createSandboxClosure, defineOwnDataProperty, isArrayIndexKey, measureSandboxData } from "./values.js";
5
6
  import { CompileScope } from "./regex/compile-guard.js";
@@ -585,8 +586,26 @@ function normalize(value, seen) {
585
586
  const date = copyNativeDate(value);
586
587
  if (date !== undefined)
587
588
  return Object.assign(Object.create(null), { $type: "date", time: serializedDateTime(date) });
589
+ if (isSandboxArrayBuffer(value)) {
590
+ arrayBufferLength(value);
591
+ const properties = Object.create(null);
592
+ const entries = [];
593
+ for (const [key, descriptor] of arrayBufferDataProperties(value)) {
594
+ if (typeof key === "symbol")
595
+ throw new TypeError("ArrayBuffer symbol properties require an explicit host-call identity.");
596
+ entries.push([key, descriptor]);
597
+ }
598
+ for (const [key, descriptor] of entries.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0))
599
+ defineOwnDataProperty(properties, key, normalize(descriptor.value, seen));
600
+ return Object.assign(Object.create(null), { $type: "arraybuffer",
601
+ bytes: normalize(Array.from(new Uint8Array(value)), seen), ...arrayBufferOptions(value), properties });
602
+ }
588
603
  if (isFloat32Array(value)) {
589
604
  const storage = float32Storage(value);
605
+ const resizable = arrayBufferOptions(storage.buffer) !== undefined;
606
+ const layout = resizable ? float32ViewLayouts.get(value) : undefined;
607
+ if (resizable && layout === undefined)
608
+ throw new TypeError("Resizable Float32Array host-call identity requires known view layout.");
590
609
  const properties = Object.create(null);
591
610
  for (const [key, descriptor] of float32DataProperties(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)) {
592
611
  defineOwnDataProperty(properties, key, normalize(descriptor.value, seen));
@@ -596,7 +615,12 @@ function normalize(value, seen) {
596
615
  bytes: normalize(Array.from(new Uint8Array(storage.buffer)), seen),
597
616
  byteOffset: storage.byteOffset,
598
617
  length: storage.length,
599
- properties
618
+ properties,
619
+ ...(resizable || Reflect.ownKeys(storage.buffer).length > 0
620
+ ? { buffer: normalize(storage.buffer, seen) } : {}),
621
+ ...(layout === undefined ? {} : { viewLayout: {
622
+ byteOffset: layout.byteOffset, length: layout.length ?? null
623
+ } })
600
624
  });
601
625
  }
602
626
  if (Array.isArray(value)) {
@@ -18,7 +18,7 @@ export async function nextArrayIterator(value, budget, context) {
18
18
  ?? getSandboxPropertyDescriptor(source, key, budget) ?? { value: undefined }, source, context);
19
19
  try {
20
20
  budget.visitNode();
21
- const number = isFloat32Array(source) ? float32Storage(source).length
21
+ const number = isFloat32Array(source) ? float32Storage(source, true).length
22
22
  : await sandboxNumber(await read("length"), budget, context);
23
23
  const length = Number.isNaN(number) || number <= 0 ? 0 : Math.min(Math.trunc(number), Number.MAX_SAFE_INTEGER);
24
24
  if (index >= length) {
@@ -10,6 +10,7 @@ import { getHostObjectMember, isGuestHostObject, isLiveCapability } from "./host
10
10
  import { errorPrototypes } from "./error-prototypes.js";
11
11
  import { float32Properties, isFloat32Array, isFloat32Index } from "./float32.js";
12
12
  import { float32Prototypes } from "./float32-prototypes.js";
13
+ import { arrayBufferPrototypes, isSandboxArrayBuffer } from "./array-buffer.js";
13
14
  import { sandboxErrorTypes } from "../error/shape.js";
14
15
  import { boxedValue, isSandboxBox } from "./boxed.js";
15
16
  import { isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, getPromiseProperties, isSandboxRegex, getRegexProperties, getCollectionProperties, isSandboxSet } from "./values.js";
@@ -279,12 +280,15 @@ export function releaseObjectPrototype(budget) {
279
280
  generatorPrototypes.delete(budget);
280
281
  errorPrototypes.delete(budget);
281
282
  float32Prototypes.delete(budget);
283
+ arrayBufferPrototypes.delete(budget);
282
284
  initialRegexDescriptors.delete(budget);
283
285
  intrinsicPrototypes.delete(budget);
284
286
  }
285
287
  export function getSandboxPrototype(value, budget) {
286
288
  if (prototypes.has(value))
287
289
  return prototypes.get(value) ?? null;
290
+ if (isSandboxArrayBuffer(value))
291
+ return budget === undefined ? null : arrayBufferPrototypes.get(budget) ?? null;
288
292
  if (isFloat32Array(value))
289
293
  return budget === undefined ? null : float32Prototypes.get(budget) ?? null;
290
294
  // Host transport records are data-only. Resolve their default prototype in
@@ -427,6 +431,8 @@ export function setSandboxPrototype(value, prototype, budget) {
427
431
  prototypes.set(value, prototype);
428
432
  }
429
433
  function isPrototypeRecord(value) {
434
+ if (isSandboxArrayBuffer(value))
435
+ return true;
430
436
  if (isFloat32Array(value))
431
437
  return true;
432
438
  if (isGuestClosure(value))
@@ -17,7 +17,7 @@ declare const sandboxRegexBrand: unique symbol;
17
17
  declare const sandboxRegexPattern: unique symbol;
18
18
  declare const sandboxRetainedValues: unique symbol;
19
19
  export type SandboxPrimitive = string | number | bigint | boolean | symbol | null | undefined;
20
- export type SandboxValue = SandboxPrimitive | Date | Float32Array | SandboxObject | SandboxArray | SandboxClosure | SandboxGenerator | SandboxCollectionIterator | SandboxRegExpIterator | SandboxMap | SandboxSet | SandboxPromise | SandboxRegex;
20
+ export type SandboxValue = SandboxPrimitive | Date | Float32Array | ArrayBuffer | SandboxObject | SandboxArray | SandboxClosure | SandboxGenerator | SandboxCollectionIterator | SandboxRegExpIterator | SandboxMap | SandboxSet | SandboxPromise | SandboxRegex;
21
21
  export type SandboxObject = {
22
22
  [key: string]: SandboxValue;
23
23
  [key: symbol]: SandboxValue;
@@ -1,4 +1,5 @@
1
1
  import { bindOtelSpan, getBoundOtelSpan } from "../observability/otel.js";
2
+ import { arrayBufferDataProperties, arrayBufferLength, copyArrayBufferStorage, isSandboxArrayBuffer } from "./array-buffer.js";
2
3
  import { internalSymbols } from "./internal-symbols.js";
3
4
  import { getIntrinsicIdentity } from "./intrinsics.js";
4
5
  import { getGeneratorProperties } from "./generator-properties.js";
@@ -399,12 +400,11 @@ export function measureSandboxData(values, options = {}) {
399
400
  const prototype = getSandboxPrototype(value);
400
401
  if (prototype !== null)
401
402
  visit(prototype, depth + 1);
403
+ if (isSandboxArrayBuffer(value))
404
+ usage += arrayBufferLength(value);
402
405
  if (isFloat32Array(value)) {
403
406
  const storage = float32Storage(value);
404
- if (!seen.has(storage.buffer)) {
405
- seen.add(storage.buffer);
406
- usage += storage.byteLength;
407
- }
407
+ visit(storage.buffer, depth + 1);
408
408
  for (const [key, descriptor] of float32Properties(value)) {
409
409
  usage += 1;
410
410
  if (typeof key === "string")
@@ -587,7 +587,7 @@ export function measureSandboxData(values, options = {}) {
587
587
  if (descriptor !== undefined)
588
588
  descriptors.push([key, descriptor]);
589
589
  }
590
- const includeNonEnumerable = isSandboxDate(value) || sandboxErrorTypes.has(value) || hasManagedDescriptors(value);
590
+ const includeNonEnumerable = isSandboxDate(value) || isSandboxArrayBuffer(value) || sandboxErrorTypes.has(value) || hasManagedDescriptors(value);
591
591
  for (const [key, descriptor] of descriptors) {
592
592
  if (!descriptor.enumerable && !includeNonEnumerable)
593
593
  continue;
@@ -804,6 +804,20 @@ function copyToSandbox(value, state, path = "<root>", cloneSandboxCollections =
804
804
  }
805
805
  return sandboxPromise;
806
806
  }
807
+ if (isSandboxArrayBuffer(value)) {
808
+ const existing = state.seen.get(value);
809
+ if (existing !== undefined)
810
+ return existing;
811
+ const copy = copyArrayBufferStorage(value, state);
812
+ state.seen.set(value, copy);
813
+ for (const [key, descriptor] of arrayBufferDataProperties(value)) {
814
+ Object.defineProperty(copy, key, { ...descriptor,
815
+ value: copyToSandbox(descriptor.value, state, joinPath(path, key), cloneSandboxCollections, depth + 1) });
816
+ }
817
+ if (!Object.isExtensible(value))
818
+ Object.preventExtensions(copy);
819
+ return copy;
820
+ }
807
821
  if (nodeTypes.isDate(value)) {
808
822
  const existing = state.seen.get(value);
809
823
  if (existing !== undefined)
@@ -827,6 +841,7 @@ function copyToSandbox(value, state, path = "<root>", cloneSandboxCollections =
827
841
  return existing;
828
842
  const copy = copyFloat32Storage(value, state);
829
843
  state.seen.set(value, copy);
844
+ copyToSandbox(float32Storage(value).buffer, state, `${path}.buffer`, cloneSandboxCollections, depth + 1);
830
845
  for (const [key, descriptor] of float32DataProperties(value)) {
831
846
  Object.defineProperty(copy, key, {
832
847
  ...descriptor,
@@ -1021,6 +1036,7 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
1021
1036
  return existing;
1022
1037
  const copy = copyFloat32Storage(value, state);
1023
1038
  state.seen.set(value, copy);
1039
+ copyFromSandbox(float32Storage(value).buffer, state, `${path}.buffer`, options, depth + 1);
1024
1040
  for (const [key, descriptor] of float32DataProperties(value)) {
1025
1041
  Object.defineProperty(copy, key, {
1026
1042
  ...descriptor,
@@ -1031,6 +1047,20 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
1031
1047
  Object.preventExtensions(copy);
1032
1048
  return copy;
1033
1049
  }
1050
+ if (isSandboxArrayBuffer(value)) {
1051
+ const existing = state.seen.get(value);
1052
+ if (existing !== undefined)
1053
+ return existing;
1054
+ const copy = copyArrayBufferStorage(value, state);
1055
+ state.seen.set(value, copy);
1056
+ for (const [key, descriptor] of arrayBufferDataProperties(value)) {
1057
+ Object.defineProperty(copy, key, { ...descriptor,
1058
+ value: copyFromSandbox(descriptor.value, state, joinPath(path, key), options, depth + 1) });
1059
+ }
1060
+ if (!Object.isExtensible(value))
1061
+ Object.preventExtensions(copy);
1062
+ return copy;
1063
+ }
1034
1064
  if (isSandboxDate(value)) {
1035
1065
  const existing = state.seen.get(value);
1036
1066
  if (existing !== undefined)
@@ -1188,6 +1218,18 @@ function allocateSandboxValue(value, budget, seen) {
1188
1218
  budget.allocateString(value);
1189
1219
  return;
1190
1220
  }
1221
+ if (isSandboxArrayBuffer(value)) {
1222
+ if (seen.has(value))
1223
+ return;
1224
+ seen.add(value);
1225
+ budget.allocateArrayLength(arrayBufferLength(value));
1226
+ for (const [key, descriptor] of arrayBufferDataProperties(value)) {
1227
+ if (typeof key === "string")
1228
+ budget.allocateString(key);
1229
+ allocateSandboxValue(descriptor.value, budget, seen);
1230
+ }
1231
+ return;
1232
+ }
1191
1233
  if (isSandboxBox(value)) {
1192
1234
  if (seen.has(value))
1193
1235
  return;
@@ -1 +1 @@
1
- export declare const KNOWN_RUNTIME_GLOBALS: readonly ["AggregateError", "Array", "Boolean", "BigInt", "Date", "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", "EvalError", "Infinity", "isFinite", "isNaN", "JSON", "Map", "Float32Array", "Math", "NaN", "Number", "Object", "parseFloat", "parseInt", "Promise", "RangeError", "ReferenceError", "RegExp", "Set", "String", "Symbol", "structuredClone", "SyntaxError", "TypeError", "URIError", "console"];
1
+ export declare const KNOWN_RUNTIME_GLOBALS: readonly ["AggregateError", "Array", "ArrayBuffer", "Boolean", "BigInt", "Date", "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error", "EvalError", "Infinity", "isFinite", "isNaN", "JSON", "Map", "Float32Array", "Math", "NaN", "Number", "Object", "parseFloat", "parseInt", "Promise", "RangeError", "ReferenceError", "RegExp", "Set", "String", "Symbol", "structuredClone", "SyntaxError", "TypeError", "URIError", "console"];
@@ -1,6 +1,7 @@
1
1
  export const KNOWN_RUNTIME_GLOBALS = [
2
2
  "AggregateError",
3
3
  "Array",
4
+ "ArrayBuffer",
4
5
  "Boolean",
5
6
  "BigInt",
6
7
  "Date",
@@ -0,0 +1,13 @@
1
+ import type { GuestObjectState } from "./guest-heap.js";
2
+ export type ArrayBufferData<TReference> = {
3
+ kind: "arraybuffer";
4
+ } & ({
5
+ bytes: number[];
6
+ maxByteLength?: number;
7
+ } | {
8
+ buffer: TReference;
9
+ });
10
+ export declare function captureArrayBufferState<T>(value: ArrayBuffer, encode: (value: unknown) => T): GuestObjectState<T>;
11
+ export declare function encodeArrayBufferStorage<T>(value: ArrayBuffer, id: number, buffers: WeakMap<ArrayBuffer, number>, reference: (id: number) => T): ArrayBufferData<T>;
12
+ export declare function validateArrayBufferStorage(value: Record<string, unknown>): void;
13
+ export declare function decodeArrayBufferStorage(value: Record<string, unknown>, resolve: (reference: unknown) => unknown): ArrayBuffer;
@@ -0,0 +1,40 @@
1
+ import { arrayBufferLength, arrayBufferOptions, isSandboxArrayBuffer } from "../interp/array-buffer.js";
2
+ import { float32Storage, isFloat32Array } from "../interp/float32.js";
3
+ import { getSandboxPrototype, hasExplicitSandboxPrototype } from "../interp/object-model.js";
4
+ import { serializePropertyDescriptors } from "./property-descriptors.js";
5
+ export function captureArrayBufferState(value, encode) {
6
+ return { properties: serializePropertyDescriptors(value, encode),
7
+ ...(hasExplicitSandboxPrototype(value) ? { prototype: encode(getSandboxPrototype(value)) } : {}) };
8
+ }
9
+ export function encodeArrayBufferStorage(value, id, buffers, reference) {
10
+ arrayBufferLength(value);
11
+ const existing = buffers.get(value);
12
+ if (existing === undefined)
13
+ buffers.set(value, id);
14
+ return { kind: "arraybuffer", ...(existing === undefined ? { bytes: Array.from(new Uint8Array(value)), ...arrayBufferOptions(value) } : { buffer: reference(existing) }) };
15
+ }
16
+ export function validateArrayBufferStorage(value) {
17
+ if (Object.hasOwn(value, "bytes") === Object.hasOwn(value, "buffer"))
18
+ throw new TypeError("ArrayBuffer requires one backing storage description.");
19
+ if (Object.hasOwn(value, "bytes") && (!Array.isArray(value.bytes) ||
20
+ value.bytes.some(byte => !Number.isInteger(byte) || byte < 0 || byte > 255)))
21
+ throw new TypeError("Invalid ArrayBuffer bytes.");
22
+ if (Object.hasOwn(value, "maxByteLength") && (!Array.isArray(value.bytes) ||
23
+ !Number.isSafeInteger(value.maxByteLength) || Number(value.maxByteLength) < value.bytes.length))
24
+ throw new TypeError("Invalid ArrayBuffer maximum length.");
25
+ }
26
+ export function decodeArrayBufferStorage(value, resolve) {
27
+ validateArrayBufferStorage(value);
28
+ if (Array.isArray(value.bytes)) {
29
+ const buffer = Reflect.construct(ArrayBuffer, [value.bytes.length,
30
+ Object.hasOwn(value, "maxByteLength") ? { maxByteLength: value.maxByteLength } : undefined]);
31
+ new Uint8Array(buffer).set(value.bytes);
32
+ return buffer;
33
+ }
34
+ const referenced = resolve(value.buffer);
35
+ if (isSandboxArrayBuffer(referenced))
36
+ return referenced;
37
+ if (isFloat32Array(referenced))
38
+ return float32Storage(referenced).buffer;
39
+ throw new TypeError("Invalid ArrayBuffer backing reference.");
40
+ }
@@ -12,8 +12,10 @@ import { sandboxErrorTypes } from "../error/shape.js";
12
12
  import { getSandboxArgumentEntries, isSandboxArguments } from "../interp/arguments.js";
13
13
  import { serializeArguments } from "./arguments.js";
14
14
  import { requiresArrayEntries, serializeArray } from "./arrays.js";
15
- import { float32DataProperties, isFloat32Array } from "../interp/float32.js";
16
- import { encodeFloat32Storage } from "./float32array.js";
15
+ import { float32DataProperties, float32Storage, isFloat32Array } from "../interp/float32.js";
16
+ import { encodeFloat32Layout } from "./float32array.js";
17
+ import { isSandboxArrayBuffer } from "../interp/array-buffer.js";
18
+ import { captureArrayBufferState, encodeArrayBufferStorage } from "./array-buffer.js";
17
19
  import { dateDataProperties, isSandboxDate } from "../interp/date.js";
18
20
  import { serializeDate } from "./date-properties.js";
19
21
  import { boxedDataProperties, isSandboxBox } from "../interp/boxed.js";
@@ -109,7 +111,7 @@ function serializeDumpValue(value, path, state) {
109
111
  if (hasGuestObjectState(value) && !(state.trustedRunReplay && (isSandboxDate(value) || isSandboxPromise(value) || isPromiseResolvingFunction(value)))) {
110
112
  throw new TypeError("Guest function properties and prototype links cannot be serialized.");
111
113
  }
112
- if (isSandboxRegExpIterator(value) || (isSandboxRegex(value) && hasCustomRegexProperties(value)) || isSandboxBox(value) || isSandboxDate(value) || isFloat32Array(value))
114
+ if (isSandboxArrayBuffer(value) || isSandboxRegExpIterator(value) || (isSandboxRegex(value) && hasCustomRegexProperties(value)) || isSandboxBox(value) || isSandboxDate(value) || isFloat32Array(value))
113
115
  return serializeHeapReference(value, path, state);
114
116
  if (Array.isArray(value)) {
115
117
  const reference = serializeHeapReference(value, path, state);
@@ -186,11 +188,17 @@ function serializeHeapReference(value, path, state) {
186
188
  return serialized === SKIP_VALUE ? { kind: "undefined" } : serialized;
187
189
  });
188
190
  }
191
+ else if (isSandboxArrayBuffer(value)) {
192
+ state.heap[String(id)] = { ...encodeArrayBufferStorage(value, id, state.float32Buffers, id => ({ kind: "ref", id })),
193
+ state: captureArrayBufferState(value, entry => {
194
+ const serialized = serializeDumpValue(entry, `${path}.<buffer>`, state);
195
+ return serialized === SKIP_VALUE ? { kind: "undefined" } : serialized;
196
+ }) };
197
+ }
189
198
  else if (isFloat32Array(value)) {
190
- const storage = encodeFloat32Storage(value, id, state.float32Buffers, (id) => ({
191
- kind: "ref",
192
- id
193
- }));
199
+ const backing = float32Storage(value);
200
+ const storage = { kind: "float32array", ...encodeFloat32Layout(value),
201
+ buffer: serializeHeapReference(backing.buffer, `${path}.buffer`, state) };
194
202
  const entries = Object.create(null);
195
203
  state.heap[String(id)] = { ...storage, entries };
196
204
  for (const [key, descriptor] of float32DataProperties(value)) {
@@ -271,6 +279,7 @@ function indexHeapContainers(snapshot) {
271
279
  (isSandboxRegex(value) && hasCustomRegexProperties(value)) ||
272
280
  isSandboxDate(value) ||
273
281
  isFloat32Array(value) ||
282
+ isSandboxArrayBuffer(value) ||
274
283
  (Array.isArray(value) && requiresArrayEntries(value)) ||
275
284
  isSandboxArguments(value) ||
276
285
  sandboxErrorTypes.has(value)) {
@@ -305,9 +314,14 @@ function collectContainerStats(value, stats, ancestors, guestValues, depth = 0)
305
314
  stat.expanded = true;
306
315
  ancestors.add(value);
307
316
  const guestEntries = [];
308
- const guest = captureGuestHeapNode(value, entry => { guestEntries.push(entry); return null; });
317
+ if (isFloat32Array(value))
318
+ guestEntries.push(float32Storage(value).buffer);
319
+ const guest = isSandboxArrayBuffer(value)
320
+ ? { kind: "arraybuffer", state: captureArrayBufferState(value, entry => { guestEntries.push(entry); return null; }) }
321
+ : captureGuestHeapNode(value, entry => { guestEntries.push(entry); return null; });
309
322
  if (guest !== undefined) {
310
- guestValues.add(value);
323
+ if (!isSandboxArrayBuffer(value))
324
+ guestValues.add(value);
311
325
  for (const entry of guestEntries) {
312
326
  collectContainerStats(entry, stats, ancestors, guestValues, depth + 1);
313
327
  if (entry !== null && typeof entry === "object")
@@ -316,6 +330,8 @@ function collectContainerStats(value, stats, ancestors, guestValues, depth = 0)
316
330
  ancestors.delete(value);
317
331
  return;
318
332
  }
333
+ for (const entry of guestEntries)
334
+ collectContainerStats(entry, stats, ancestors, guestValues, depth + 1);
319
335
  if (!Array.isArray(value) && !isPlainObject(value) && !isFloat32Array(value) && !isSandboxDate(value)) {
320
336
  ancestors.delete(value);
321
337
  return;
@@ -1,15 +1,22 @@
1
1
  import type { GuestObjectState } from "./guest-heap.js";
2
+ import type { Budget } from "../interp/budget.js";
2
3
  export declare function captureFloat32State<T>(value: Float32Array, encode: (value: unknown) => T): GuestObjectState<T>;
3
4
  export declare function restoreFloat32Properties<T>(value: Float32Array, state: GuestObjectState<T>, decode: (value: T) => unknown): void;
4
5
  export type Float32Data<TReference> = {
5
6
  kind: "float32array";
6
7
  byteOffset: number;
7
8
  length: number;
9
+ lengthTracking?: true;
8
10
  } & ({
9
11
  bytes: number[];
10
12
  } | {
11
13
  buffer: TReference;
12
14
  });
15
+ export declare function encodeFloat32Layout(value: Float32Array): {
16
+ byteOffset: number;
17
+ length: number;
18
+ lengthTracking?: true;
19
+ };
13
20
  export declare function encodeFloat32Storage<TReference>(value: Float32Array, id: number, buffers: WeakMap<ArrayBuffer, number>, reference: (id: number) => TReference): Float32Data<TReference>;
14
21
  export declare function validateFloat32Storage(value: Record<string, unknown>): void;
15
- export declare function decodeFloat32Storage(value: Record<string, unknown>, resolve: (reference: unknown) => unknown): Float32Array;
22
+ export declare function decodeFloat32Storage(value: Record<string, unknown>, resolve: (reference: unknown) => unknown, budget?: Budget): Float32Array;
@@ -1,6 +1,7 @@
1
- import { float32Properties, float32Storage, isFloat32Array, isFloat32Index } from "../interp/float32.js";
1
+ import { float32Properties, float32Storage, float32ViewLayouts, restoreFloat32View, isFloat32Array, isFloat32Index } from "../interp/float32.js";
2
2
  import { getSandboxPrototype, hasExplicitSandboxPrototype } from "../interp/object-model.js";
3
3
  import { restorePropertyDescriptors, serializePropertyDescriptors } from "./property-descriptors.js";
4
+ import { arrayBufferOptions, isSandboxArrayBuffer } from "../interp/array-buffer.js";
4
5
  export function captureFloat32State(value, encode) {
5
6
  const metadata = Object.defineProperties(Object.create(null), Object.fromEntries(float32Properties(value)));
6
7
  if (!Object.isExtensible(value))
@@ -18,6 +19,16 @@ export function restoreFloat32Properties(value, state, decode) {
18
19
  if (!Object.isExtensible(metadata))
19
20
  Object.preventExtensions(value);
20
21
  }
22
+ export function encodeFloat32Layout(value) {
23
+ const storage = float32Storage(value);
24
+ if (arrayBufferOptions(storage.buffer) === undefined)
25
+ return { byteOffset: storage.byteOffset, length: storage.length };
26
+ const layout = float32ViewLayouts.get(value);
27
+ if (layout === undefined)
28
+ throw new TypeError("Resizable Float32Array snapshots require known view layout.");
29
+ return { byteOffset: layout.byteOffset, length: layout.length ?? 0,
30
+ ...(layout.length === undefined ? { lengthTracking: true } : {}) };
31
+ }
21
32
  export function encodeFloat32Storage(value, id, buffers, reference) {
22
33
  const storage = float32Storage(value);
23
34
  const existing = buffers.get(storage.buffer);
@@ -33,6 +44,8 @@ export function encodeFloat32Storage(value, id, buffers, reference) {
33
44
  };
34
45
  }
35
46
  export function validateFloat32Storage(value) {
47
+ if (Object.hasOwn(value, "lengthTracking") && (value.lengthTracking !== true || value.length !== 0 || Object.hasOwn(value, "bytes")))
48
+ throw new TypeError("Invalid Float32Array length-tracking layout.");
36
49
  if (!Number.isSafeInteger(value.length) ||
37
50
  Number(value.length) < 0 ||
38
51
  !Number.isSafeInteger(value.byteOffset) ||
@@ -48,7 +61,7 @@ export function validateFloat32Storage(value) {
48
61
  throw new TypeError("Invalid Float32Array backing bytes.");
49
62
  }
50
63
  }
51
- export function decodeFloat32Storage(value, resolve) {
64
+ export function decodeFloat32Storage(value, resolve, budget) {
52
65
  validateFloat32Storage(value);
53
66
  let buffer;
54
67
  if (Array.isArray(value.bytes)) {
@@ -57,9 +70,14 @@ export function decodeFloat32Storage(value, resolve) {
57
70
  }
58
71
  else {
59
72
  const referenced = resolve(value.buffer);
60
- if (!isFloat32Array(referenced))
73
+ if (isSandboxArrayBuffer(referenced))
74
+ buffer = referenced;
75
+ else if (isFloat32Array(referenced))
76
+ buffer = float32Storage(referenced).buffer;
77
+ else
61
78
  throw new TypeError("Invalid Float32Array backing reference.");
62
- buffer = float32Storage(referenced).buffer;
63
79
  }
64
- return new Float32Array(buffer, Number(value.byteOffset), Number(value.length));
80
+ if (value.lengthTracking === true && arrayBufferOptions(buffer) === undefined)
81
+ throw new TypeError("Length-tracking layout requires resizable backing storage.");
82
+ return restoreFloat32View(buffer, Number(value.byteOffset), value.lengthTracking === true ? undefined : Number(value.length), budget);
65
83
  }
@@ -4,6 +4,7 @@ import { type SerializedSymbol, type SerializedSymbolProperty } from "./symbols.
4
4
  import { type CollectionIterationMethod } from "../interp/collection-iterator.js";
5
5
  import { CompileScope } from "../interp/regex/compile-guard.js";
6
6
  import { type Float32Data } from "./float32array.js";
7
+ import { type ArrayBufferData } from "./array-buffer.js";
7
8
  import { type SandboxErrorName } from "../error/shape.js";
8
9
  import { type SandboxClosure, type SandboxPromise, type SandboxValue } from "../interp/values.js";
9
10
  import { type SerializedArguments } from "./arguments.js";
@@ -69,6 +70,10 @@ type DataNode = {
69
70
  } | (Float32Data<Atom> & {
70
71
  properties: Properties;
71
72
  extensible: boolean;
73
+ }) | (ArrayBufferData<Atom> & {
74
+ properties: Properties;
75
+ extensible: boolean;
76
+ symbolEntries?: Array<SerializedSymbolProperty<Atom>>;
72
77
  }) | {
73
78
  kind: "capability";
74
79
  id: string;
@@ -10,8 +10,10 @@ import { isSandboxCollectionIterator, restoreSandboxCollectionIterator, snapshot
10
10
  import { isSandboxRegExpIterator, regexpIteratorState, restoreSandboxRegExpIterator } from "../interp/regexp-iterator.js";
11
11
  import { hasGuestObjectState, hasNullObjectPrototype, setSandboxPrototype } from "../interp/object-model.js";
12
12
  import { CompileScope } from "../interp/regex/compile-guard.js";
13
- import { float32DataProperties, isFloat32Array } from "../interp/float32.js";
14
- import { decodeFloat32Storage, encodeFloat32Storage } from "./float32array.js";
13
+ import { float32DataProperties, float32Storage, isFloat32Array } from "../interp/float32.js";
14
+ import { decodeFloat32Storage, encodeFloat32Layout } from "./float32array.js";
15
+ import { arrayBufferDataProperties, isSandboxArrayBuffer } from "../interp/array-buffer.js";
16
+ import { decodeArrayBufferStorage, encodeArrayBufferStorage } from "./array-buffer.js";
15
17
  import { dateDataProperties, isSandboxDate, restoreDateTime, serializedDateTime } from "../interp/date.js";
16
18
  import { createRawJson, isRawJson } from "../interp/raw-json.js";
17
19
  import { boxedDataProperties, createSandboxBox, nativeBoxedValue } from "../interp/boxed.js";
@@ -119,11 +121,22 @@ export function encodeReplayData(value, options = {}) {
119
121
  ...(Object.isExtensible(entry) ? {} : { extensible: false })
120
122
  };
121
123
  }
124
+ else if (isSandboxArrayBuffer(entry)) {
125
+ const storage = encodeArrayBufferStorage(entry, id, float32Buffers, id => ({ tag: "ref", id }));
126
+ const properties = Object.create(null);
127
+ for (const [key, descriptor] of arrayBufferDataProperties(entry)) {
128
+ if (typeof key === "symbol")
129
+ continue;
130
+ properties[key] = { value: child(descriptor.value, key), configurable: descriptor.configurable === true,
131
+ enumerable: descriptor.enumerable === true, writable: descriptor.writable === true };
132
+ }
133
+ let symbolIndex = 0;
134
+ const symbolEntries = serializeSymbolProperties(entry, value => encode(value, depth + 1, [...path, { symbol: Math.floor(symbolIndex++ / 2) }]));
135
+ nodes[id] = { ...storage, properties, extensible: Object.isExtensible(entry), symbolEntries };
136
+ }
122
137
  else if (isFloat32Array(entry)) {
123
- const storage = encodeFloat32Storage(entry, id, float32Buffers, (id) => ({
124
- tag: "ref",
125
- id
126
- }));
138
+ const backing = float32Storage(entry);
139
+ const storage = { kind: "float32array", ...encodeFloat32Layout(entry), buffer: child(backing.buffer, "<buffer>") };
127
140
  const properties = Object.create(null);
128
141
  for (const [key, descriptor] of float32DataProperties(entry)) {
129
142
  properties[key] = {
@@ -227,7 +240,7 @@ export function decodeReplayData(input, options = {}, parent) {
227
240
  const graph = record(input);
228
241
  const nodes = list(own(graph, "nodes"));
229
242
  const restored = new Map();
230
- const initializeIterators = [];
243
+ const initializeValues = [];
231
244
  const decode = (entry, depth = 0) => {
232
245
  if (depth > MAX_DATA_DEPTH)
233
246
  throw new TypeError("Replay data exceeds the nesting limit.");
@@ -378,10 +391,22 @@ export function decodeReplayData(input, options = {}, parent) {
378
391
  Object.preventExtensions(result);
379
392
  return result;
380
393
  }
394
+ if (kind === "arraybuffer") {
395
+ if (typeof node.extensible !== "boolean")
396
+ throw new TypeError("Invalid ArrayBuffer extensibility.");
397
+ const result = decodeArrayBufferStorage(node, child);
398
+ restored.set(id, result);
399
+ initializeValues.push(() => {
400
+ defineProperties(result, record(own(node, "properties")), child, node.symbolEntries);
401
+ if (!node.extensible)
402
+ Object.preventExtensions(result);
403
+ });
404
+ return result;
405
+ }
381
406
  if (kind === "float32array") {
382
407
  if (typeof node.extensible !== "boolean")
383
408
  throw new TypeError("Invalid Float32Array extensibility.");
384
- const result = decodeFloat32Storage(node, child);
409
+ const result = decodeFloat32Storage(node, child, compilation.owner?.budget);
385
410
  restored.set(id, result);
386
411
  defineProperties(result, record(own(node, "properties")), child);
387
412
  if (!node.extensible)
@@ -421,7 +446,7 @@ export function decodeReplayData(input, options = {}, parent) {
421
446
  const collection = child(own(node, "collection"));
422
447
  if (collection !== undefined && !isSandboxMap(collection) && !isSandboxSet(collection))
423
448
  throw new TypeError("Invalid replay collection iterator source.");
424
- initializeIterators.push(() => { restoreSandboxCollectionIterator({ collection, collectionKind, method, index, exhausted }, result); });
449
+ initializeValues.push(() => { restoreSandboxCollectionIterator({ collection, collectionKind, method, index, exhausted }, result); });
425
450
  defineProperties(result, record(own(node, "properties")), child);
426
451
  if (!node.extensible)
427
452
  Object.preventExtensions(result);
@@ -503,7 +528,7 @@ export function decodeReplayData(input, options = {}, parent) {
503
528
  return result;
504
529
  };
505
530
  const result = decode(own(graph, "root"));
506
- for (const initialize of initializeIterators)
531
+ for (const initialize of initializeValues)
507
532
  initialize();
508
533
  if (parent !== undefined)
509
534
  compilation.forward(compilation.tickets, parent);
@@ -18,6 +18,7 @@ import { runAsyncPrefix } from "../interp/jobs.js";
18
18
  import { toPropertyKey } from "../interp/property-key.js";
19
19
  import { CompileScope } from "../interp/regex/compile-guard.js";
20
20
  import { decodeFloat32Storage, restoreFloat32Properties } from "./float32array.js";
21
+ import { decodeArrayBufferStorage } from "./array-buffer.js";
21
22
  import { restoreDateTime } from "../interp/date.js";
22
23
  import { createRawJson } from "../interp/raw-json.js";
23
24
  import { createSandboxBox } from "../interp/boxed.js";
@@ -456,10 +457,21 @@ function restoreHeapValue(id, state) {
456
457
  restoreRegexProperties(value, serialized, entry => deserializeValue(entry, state));
457
458
  return value;
458
459
  }
460
+ if (serialized.kind === "arraybuffer") {
461
+ initializeIntrinsicRealm(state);
462
+ const value = decodeArrayBufferStorage(serialized, reference => deserializeValue(reference, state));
463
+ state.heapValueById.set(id, value);
464
+ state.initializeIterators.push(() => {
465
+ if (serialized.state.prototype !== undefined)
466
+ setSandboxPrototype(value, deserializeValue(serialized.state.prototype, state), state.budget);
467
+ restorePropertyDescriptors(value, serialized.state.properties, entry => deserializeValue(entry, state));
468
+ });
469
+ return value;
470
+ }
459
471
  if (serialized.kind === "float32array") {
460
472
  if (serialized.state !== undefined)
461
473
  initializeIntrinsicRealm(state);
462
- const value = decodeFloat32Storage(serialized, (reference) => deserializeValue(reference, state));
474
+ const value = decodeFloat32Storage(serialized, (reference) => deserializeValue(reference, state), state.budget);
463
475
  state.heapValueById.set(id, value);
464
476
  if (serialized.state !== undefined) {
465
477
  const objectState = serialized.state;
@@ -8,6 +8,7 @@ import { type GuestHeapNode, type GuestObjectState } from "./guest-heap.js";
8
8
  import { type SerializedArguments } from "./arguments.js";
9
9
  import { type SerializedArray } from "./arrays.js";
10
10
  import { type Float32Data } from "./float32array.js";
11
+ import { type ArrayBufferData } from "./array-buffer.js";
11
12
  import { type SerializedDate } from "./date-properties.js";
12
13
  import { type SandboxBox } from "../interp/boxed.js";
13
14
  import { type BoxedData } from "./boxed.js";
@@ -51,7 +52,9 @@ export type SerializedReferenceValue = {
51
52
  kind: "ref";
52
53
  id: number;
53
54
  };
54
- export type SerializedHeapValue = GuestHeapNode<SerializedSnapshotValue> | {
55
+ export type SerializedHeapValue = (ArrayBufferData<SerializedReferenceValue> & {
56
+ state: GuestObjectState<SerializedSnapshotValue>;
57
+ }) | GuestHeapNode<SerializedSnapshotValue> | {
55
58
  kind: "regexp-iterator";
56
59
  matcher: SerializedSnapshotValue;
57
60
  input: SerializedSnapshotValue;
@@ -112,7 +115,7 @@ export type RuntimePromiseValue = {
112
115
  id: SnapshotId;
113
116
  promise?: Promise<unknown>;
114
117
  };
115
- export type RuntimeSnapshotValue = bigint | symbol | SandboxBox | Date | Float32Array | boolean | null | number | string | undefined | RuntimeClosureValue | SandboxClosure | SandboxGenerator | SandboxCollectionIterator | SandboxRegExpIterator | RuntimePromiseValue | SandboxMap | SandboxRegex | SandboxSet | RuntimeSnapshotValue[] | {
118
+ export type RuntimeSnapshotValue = bigint | symbol | SandboxBox | Date | Float32Array | ArrayBuffer | boolean | null | number | string | undefined | RuntimeClosureValue | SandboxClosure | SandboxGenerator | SandboxCollectionIterator | SandboxRegExpIterator | RuntimePromiseValue | SandboxMap | SandboxRegex | SandboxSet | RuntimeSnapshotValue[] | {
116
119
  [key: string]: RuntimeSnapshotValue;
117
120
  };
118
121
  export type RuntimeScopeFrame = {