poe-code 14.0.61 → 14.0.63

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 (67) hide show
  1. package/dist/index.js +1147 -574
  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-VAYQP2VZ.js → chunk-KMTTD3BL.js} +7 -3
  6. package/packages/safe-js/dist/chunks/chunk-KMTTD3BL.js.map +7 -0
  7. package/packages/safe-js/dist/chunks/{chunk-ZTGKRKQH.js → chunk-RU2BCN4G.js} +1069 -577
  8. package/packages/safe-js/dist/chunks/chunk-RU2BCN4G.js.map +7 -0
  9. package/packages/safe-js/dist/cli.js +2 -2
  10. package/packages/safe-js/dist/core.js +1 -1
  11. package/packages/safe-js/dist/graph-depth.js +3 -0
  12. package/packages/safe-js/dist/index.js +2 -2
  13. package/packages/safe-js/dist/interp/async.js +3 -0
  14. package/packages/safe-js/dist/interp/boxed.d.ts +14 -0
  15. package/packages/safe-js/dist/interp/boxed.js +50 -0
  16. package/packages/safe-js/dist/interp/function-string.d.ts +2 -0
  17. package/packages/safe-js/dist/interp/function-string.js +21 -0
  18. package/packages/safe-js/dist/interp/globals/collections.js +4 -4
  19. package/packages/safe-js/dist/interp/globals/console-json.js +15 -18
  20. package/packages/safe-js/dist/interp/globals/misc.js +12 -13
  21. package/packages/safe-js/dist/interp/globals/numeric-parsers.d.ts +5 -0
  22. package/packages/safe-js/dist/interp/globals/numeric-parsers.js +47 -0
  23. package/packages/safe-js/dist/interp/globals/object-array.js +21 -24
  24. package/packages/safe-js/dist/interp/globals/object.js +10 -5
  25. package/packages/safe-js/dist/interp/globals/primitives.d.ts +8 -0
  26. package/packages/safe-js/dist/interp/globals/primitives.js +69 -0
  27. package/packages/safe-js/dist/interp/host-bridge.js +22 -0
  28. package/packages/safe-js/dist/interp/interpreter.js +173 -120
  29. package/packages/safe-js/dist/interp/iteration.d.ts +5 -3
  30. package/packages/safe-js/dist/interp/iteration.js +22 -1
  31. package/packages/safe-js/dist/interp/methods/array.js +6 -3
  32. package/packages/safe-js/dist/interp/methods/function.d.ts +2 -0
  33. package/packages/safe-js/dist/interp/methods/function.js +10 -1
  34. package/packages/safe-js/dist/interp/methods/number.d.ts +1 -0
  35. package/packages/safe-js/dist/interp/methods/number.js +4 -1
  36. package/packages/safe-js/dist/interp/methods/string.d.ts +2 -0
  37. package/packages/safe-js/dist/interp/methods/string.js +2 -2
  38. package/packages/safe-js/dist/interp/object-model.d.ts +5 -0
  39. package/packages/safe-js/dist/interp/object-model.js +41 -3
  40. package/packages/safe-js/dist/interp/promise.js +68 -61
  41. package/packages/safe-js/dist/interp/resources.d.ts +1 -0
  42. package/packages/safe-js/dist/interp/string-coercion.js +7 -1
  43. package/packages/safe-js/dist/interp/values.d.ts +4 -0
  44. package/packages/safe-js/dist/interp/values.js +81 -3
  45. package/packages/safe-js/dist/migrate.d.ts +2 -2
  46. package/packages/safe-js/dist/migrate.js +1 -1
  47. package/packages/safe-js/dist/migration-file.d.ts +4 -4
  48. package/packages/safe-js/dist/parse/function-source.d.ts +7 -0
  49. package/packages/safe-js/dist/parse/function-source.js +1 -0
  50. package/packages/safe-js/dist/parse/hash.d.ts +2 -2
  51. package/packages/safe-js/dist/parse/hash.js +23 -9
  52. package/packages/safe-js/dist/parse/parser.js +36 -23
  53. package/packages/safe-js/dist/restore.js +2 -1
  54. package/packages/safe-js/dist/run.js +16 -5
  55. package/packages/safe-js/dist/snapshot/boxed.d.ts +16 -0
  56. package/packages/safe-js/dist/snapshot/boxed.js +52 -0
  57. package/packages/safe-js/dist/snapshot/dump-format.d.ts +1 -1
  58. package/packages/safe-js/dist/snapshot/dump-format.js +19 -6
  59. package/packages/safe-js/dist/snapshot/migration.js +2 -2
  60. package/packages/safe-js/dist/snapshot/replay-data.d.ts +5 -0
  61. package/packages/safe-js/dist/snapshot/replay-data.js +24 -0
  62. package/packages/safe-js/dist/snapshot/restore.js +10 -0
  63. package/packages/safe-js/dist/snapshot/serialize.d.ts +4 -2
  64. package/packages/safe-js/dist/snapshot/serialize.js +18 -10
  65. package/packages/safe-js/dist/snapshot/validation.js +22 -1
  66. package/packages/safe-js/dist/chunks/chunk-VAYQP2VZ.js.map +0 -7
  67. package/packages/safe-js/dist/chunks/chunk-ZTGKRKQH.js.map +0 -7
@@ -1,6 +1,7 @@
1
1
  import { boundIdentifiers } from "./bindings.js";
2
2
  import { tokenize } from "./tokenizer.js";
3
3
  import { assignIds } from "./assign-ids.js";
4
+ import { functionSources } from "./function-source.js";
4
5
  import { formatParseError } from "./format-error.js";
5
6
  import { createExportDefaultDeclaration, createExportNamedDeclaration } from "./parse-export.js";
6
7
  import { createImportMeta, isImportMetaTokenSequence } from "./parse-import-meta.js";
@@ -42,7 +43,7 @@ const TOP_LEVEL_STATEMENT_KEYWORDS = new Set([
42
43
  export function parse(source, filename = "<input>", owner) {
43
44
  const compilation = new CompileScope(owner);
44
45
  try {
45
- const result = assignIds(parseTokens(tokenize(source, { allowRegexLiterals: true, compilation }), compilation));
46
+ const result = assignIds(new Parser(tokenize(source, { allowRegexLiterals: true, compilation }), source, compilation).parseTopLevel());
46
47
  const regexLiteral = findRegexLiteral(result);
47
48
  if (regexLiteral !== undefined) {
48
49
  throw new Error(`Regular expression literals are not supported at line ${regexLiteral.span.start.line}, column ${regexLiteral.span.start.column}.`);
@@ -66,7 +67,7 @@ export function parse(source, filename = "<input>", owner) {
66
67
  export function parseModule(source, filename = "<input>", owner) {
67
68
  const compilation = new CompileScope(owner);
68
69
  try {
69
- return assignIds(parseModuleTokens(tokenize(source, { allowRegexLiterals: true, compilation }), compilation));
70
+ return assignIds(new Parser(tokenize(source, { allowRegexLiterals: true, compilation }), source, compilation).parseModule());
70
71
  }
71
72
  catch (error) {
72
73
  if (error instanceof DisallowedSyntaxError || error instanceof SandboxError) {
@@ -84,7 +85,7 @@ export function parseModule(source, filename = "<input>", owner) {
84
85
  export function parseExecutableModule(source, filename = "<input>", owner) {
85
86
  const compilation = new CompileScope(owner);
86
87
  try {
87
- const result = assignIds(parseModuleTokens(tokenize(source, { allowRegexLiterals: true, compilation }), compilation));
88
+ const result = assignIds(new Parser(tokenize(source, { allowRegexLiterals: true, compilation }), source, compilation).parseModule());
88
89
  throwIfImportMetaAssignment(result);
89
90
  return result;
90
91
  }
@@ -101,14 +102,9 @@ export function parseExecutableModule(source, filename = "<input>", owner) {
101
102
  compilation.dispose();
102
103
  }
103
104
  }
104
- function parseTokens(tokens, compilation) {
105
- return new Parser(tokens, compilation).parseTopLevel();
106
- }
107
- function parseModuleTokens(tokens, compilation) {
108
- return new Parser(tokens, compilation).parseModule();
109
- }
110
105
  class Parser {
111
106
  tokens;
107
+ source;
112
108
  compilation;
113
109
  functionContext;
114
110
  index = 0;
@@ -119,12 +115,21 @@ class Parser {
119
115
  scopes = [new Map()];
120
116
  functionScopes = new WeakSet();
121
117
  varNames = new WeakMap();
122
- constructor(tokens, compilation, functionContext = "normal") {
118
+ constructor(tokens, source, compilation, functionContext = "normal") {
123
119
  this.tokens = tokens;
120
+ this.source = source;
124
121
  this.compilation = compilation;
125
122
  this.functionContext = functionContext;
126
123
  this.functionScopes.add(this.scopes[0]);
127
124
  }
125
+ withFunctionSource(node) {
126
+ functionSources.set(node, {
127
+ text: this.source,
128
+ start: node.span.start.offset,
129
+ end: node.span.end.offset
130
+ });
131
+ return node;
132
+ }
128
133
  parseTopLevel() {
129
134
  if (this.isExportToken(this.currentToken())) {
130
135
  throw new DisallowedSyntaxError("export", this.currentToken().start);
@@ -270,14 +275,14 @@ class Parser {
270
275
  });
271
276
  this.expectPunctuator("=>");
272
277
  const body = this.parseArrowFunctionBody(params);
273
- return {
278
+ return this.withFunctionSource({
274
279
  type: "ArrowFunctionExpression",
275
280
  async: isAsync,
276
281
  body,
277
282
  expression: body.type !== "BlockStatement",
278
283
  params,
279
284
  span: createSpan(start, body.span.end)
280
- };
285
+ });
281
286
  }
282
287
  parseConditionalExpression() {
283
288
  if (this.conditionalExpressionDepth >= MAX_CONDITIONAL_EXPRESSION_DEPTH) {
@@ -920,7 +925,7 @@ class Parser {
920
925
  });
921
926
  const generator = generatorToken !== undefined;
922
927
  const body = this.withFunctionContext(generator ? "generator" : "normal", () => this.parseBlockStatement(params));
923
- return {
928
+ return this.withFunctionSource({
924
929
  type: "FunctionDeclaration",
925
930
  async: asyncToken !== undefined,
926
931
  body,
@@ -928,7 +933,7 @@ class Parser {
928
933
  id,
929
934
  params,
930
935
  span: createSpan(asyncToken?.start ?? functionToken.start, body.span.end)
931
- };
936
+ });
932
937
  }
933
938
  parseVariableDeclarator(kind) {
934
939
  const id = this.parseBindingTarget();
@@ -1669,7 +1674,11 @@ class Parser {
1669
1674
  continue;
1670
1675
  }
1671
1676
  if (this.currentToken().type === "template") {
1672
- const quasi = createTemplateLiteral(this.currentToken(), { allowMalformedEscapes: true, functionContext: this.functionContext }, this.compilation);
1677
+ const quasi = createTemplateLiteral(this.currentToken(), {
1678
+ allowMalformedEscapes: true,
1679
+ functionContext: this.functionContext,
1680
+ source: this.source
1681
+ }, this.compilation);
1673
1682
  this.index += 1;
1674
1683
  expression = {
1675
1684
  node: {
@@ -1757,7 +1766,11 @@ class Parser {
1757
1766
  if (token.type === "template") {
1758
1767
  this.index += 1;
1759
1768
  return {
1760
- node: createTemplateLiteral(token, { allowMalformedEscapes: false, functionContext: this.functionContext }, this.compilation),
1769
+ node: createTemplateLiteral(token, {
1770
+ allowMalformedEscapes: false,
1771
+ functionContext: this.functionContext,
1772
+ source: this.source
1773
+ }, this.compilation),
1761
1774
  parenthesized: false
1762
1775
  };
1763
1776
  }
@@ -1875,7 +1888,7 @@ class Parser {
1875
1888
  });
1876
1889
  const generator = generatorToken !== undefined;
1877
1890
  const body = this.withFunctionContext(generator ? "generator" : "normal", () => this.parseBlockStatement(params));
1878
- return {
1891
+ return this.withFunctionSource({
1879
1892
  type: "FunctionExpression",
1880
1893
  async: asyncToken !== undefined,
1881
1894
  body,
@@ -1883,7 +1896,7 @@ class Parser {
1883
1896
  id,
1884
1897
  params,
1885
1898
  span: createSpan(asyncToken?.start ?? functionToken.start, body.span.end)
1886
- };
1899
+ });
1887
1900
  }
1888
1901
  parseArrayExpression() {
1889
1902
  const start = this.expectPunctuator("[");
@@ -2139,7 +2152,7 @@ class Parser {
2139
2152
  return parsedParams;
2140
2153
  });
2141
2154
  const body = this.withFunctionContext("normal", () => this.parseBlockStatement(params));
2142
- return {
2155
+ return this.withFunctionSource({
2143
2156
  type: "FunctionExpression",
2144
2157
  async: asyncToken !== undefined,
2145
2158
  body,
@@ -2148,7 +2161,7 @@ class Parser {
2148
2161
  method: true,
2149
2162
  params,
2150
2163
  span: createSpan(asyncToken?.start ?? methodStart, body.span.end)
2151
- };
2164
+ });
2152
2165
  }
2153
2166
  parseIdentifierName() {
2154
2167
  const token = this.currentToken();
@@ -3033,7 +3046,7 @@ function createTemplateLiteral(token, options, compilation) {
3033
3046
  quasis.push(createTemplateElement(token.start, raw, quasiStart, cursor, false, options));
3034
3047
  const expressionStart = cursor + 2;
3035
3048
  const expressionEnd = findTemplateExpressionEnd(raw, expressionStart);
3036
- expressions.push(parseEmbeddedExpression(raw.slice(expressionStart, expressionEnd), positionWithinRaw(token.start, raw, expressionStart), options.functionContext, compilation));
3049
+ expressions.push(parseEmbeddedExpression(raw.slice(expressionStart, expressionEnd), positionWithinRaw(token.start, raw, expressionStart), options.functionContext, options.source, compilation));
3037
3050
  quasiStart = expressionEnd + 1;
3038
3051
  cursor = expressionEnd + 1;
3039
3052
  continue;
@@ -3394,13 +3407,13 @@ function decodeHexEscape(value, start) {
3394
3407
  end: index + 2
3395
3408
  };
3396
3409
  }
3397
- function parseEmbeddedExpression(source, base, functionContext, compilation) {
3410
+ function parseEmbeddedExpression(source, base, functionContext, fullSource, compilation) {
3398
3411
  const tokens = tokenize(source, { allowRegexLiterals: true, compilation }).map((token) => ({
3399
3412
  ...token,
3400
3413
  start: rebasePosition(token.start, base),
3401
3414
  end: rebasePosition(token.end, base)
3402
3415
  }));
3403
- return new Parser(tokens, compilation, functionContext).parseExpressionOnly();
3416
+ return new Parser(tokens, fullSource, compilation, functionContext).parseExpressionOnly();
3404
3417
  }
3405
3418
  export function findRegexLiteral(node) {
3406
3419
  if (node === null || node === undefined) {
@@ -21,13 +21,14 @@ export function restore(snapshot, options, owner) {
21
21
  validateSnapshotMigration(snapshot.migration, snapshot.sourceHash, owner);
22
22
  if (snapshot.executionSemantics !== EXECUTION_SEMANTICS &&
23
23
  snapshot.executionSemantics !== "jobs-v6" &&
24
+ snapshot.executionSemantics !== "jobs-v7" &&
24
25
  (snapshot.executionSemantics !== undefined ||
25
26
  snapshot.promiseReplay !== undefined ||
26
27
  snapshot.replay !== undefined ||
27
28
  snapshot.initialInputs !== undefined)) {
28
29
  throw new SnapshotValidationError("unsupportedVersion", "$.executionSemantics", "incompatible execution semantics; resume with the SafeJS version that created this snapshot. Migration requires explicit reconciliation, not changing its version marker.");
29
30
  }
30
- const currentSourceHash = hashSource(options.source, owner);
31
+ const currentSourceHash = hashSource(options.source, owner, snapshot.executionSemantics !== "jobs-v6" && snapshot.executionSemantics !== "jobs-v7");
31
32
  if (snapshot.sourceHash !== currentSourceHash) {
32
33
  throw new SnapshotMismatchError(snapshot.sourceHash, currentSourceHash);
33
34
  }
@@ -1,7 +1,7 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { runWithExtensions } from "./realm.js";
3
3
  import { hashParsedAst, hashSource } from "./parse/hash.js";
4
- import { withRunResources } from "./interp/resources.js";
4
+ import { runResources, withRunResources } from "./interp/resources.js";
5
5
  import { createReplayableRandom } from "./random.js";
6
6
  import { attachErrorSpan, describeThrownValue, materializeWrappedErrorCause, replaceErrorStack } from "./error/shape.js";
7
7
  import { findRegexLiteral, parseExecutableModule } from "./parse/parser.js";
@@ -76,7 +76,11 @@ export function run(source, options = {}) {
76
76
  const restoredSnapshot = options.snapshot === undefined
77
77
  ? undefined
78
78
  : restore(options.snapshot, { source }, operation.owner);
79
- const executionSemantics = restoredSnapshot?.executionSemantics === "jobs-v6" ? "jobs-v6" : EXECUTION_SEMANTICS;
79
+ const executionSemantics = restoredSnapshot?.executionSemantics === "jobs-v6" ||
80
+ restoredSnapshot?.executionSemantics === "jobs-v7"
81
+ ? restoredSnapshot.executionSemantics
82
+ : EXECUTION_SEMANTICS;
83
+ runResources.getStore().functionSourceText = executionSemantics === EXECUTION_SEMANTICS;
80
84
  const convertInitialInput = (convert) => executionSemantics === "jobs-v6" ? convert() : promiseReplayContext.exit(convert);
81
85
  if (restoredSnapshot !== undefined) {
82
86
  leaveSnapshotRun = enterSnapshotRun(restoredSnapshot);
@@ -86,8 +90,8 @@ export function run(source, options = {}) {
86
90
  const module = parseExecutableModule(source, filename, operation.owner);
87
91
  promiseReplay.validateNodes(module);
88
92
  const sourceHash = findRegexLiteral(module) === undefined
89
- ? hashSource(source, operation.owner)
90
- : hashParsedAst(module);
93
+ ? hashSource(source, operation.owner, executionSemantics === EXECUTION_SEMANTICS)
94
+ : hashParsedAst(module, executionSemantics === EXECUTION_SEMANTICS);
91
95
  const hostCalls = new HostCallJournal(sourceHash, readHostCallSnapshot(restoredSnapshot), options.hostCallResumeProvider, restoredSnapshot?.replay, budget, operation.owner);
92
96
  leaveHostReplay = hostCalls.dispose.bind(hostCalls);
93
97
  promiseReplay.validateCallbacks(hostCalls.callbackPositions());
@@ -110,7 +114,14 @@ export function run(source, options = {}) {
110
114
  signal: options.signal,
111
115
  lifecycle
112
116
  }));
113
- const builtinBindings = createBuiltinBindings({ compileOwner: operation.owner, budget, hostCalls, sink: options.sink, random: random?.generator.next, clock: options.clock });
117
+ const builtinBindings = createBuiltinBindings({
118
+ compileOwner: operation.owner,
119
+ budget,
120
+ hostCalls,
121
+ sink: options.sink,
122
+ random: random?.generator.next,
123
+ clock: options.clock
124
+ });
114
125
  const importMeta = convertInitialInput(() => deepCopyToSandbox(options.importMeta ?? {}));
115
126
  if (restoredSnapshot?.migration !== undefined) {
116
127
  if (Object.hasOwn(importMeta, "migration"))
@@ -0,0 +1,16 @@
1
+ import { type SandboxBox } from "../interp/boxed.js";
2
+ import type { SandboxValue } from "../interp/values.js";
3
+ export type BoxedData<T> = {
4
+ kind: "boxed";
5
+ value: T;
6
+ properties: Record<string, {
7
+ value: T;
8
+ configurable: boolean;
9
+ enumerable: boolean;
10
+ writable: boolean;
11
+ }>;
12
+ extensible: boolean;
13
+ };
14
+ export declare function encodeBoxedData<T>(value: SandboxBox, encode: (value: SandboxValue, key: string) => T): BoxedData<T>;
15
+ export declare function restoreBoxedProperties<T>(value: SandboxBox, data: BoxedData<T>, decode: (value: T) => unknown): void;
16
+ export declare function validateBoxedProperties(data: Record<string, unknown>): void;
@@ -0,0 +1,52 @@
1
+ import { boxedDataProperties, boxedValue } from "../interp/boxed.js";
2
+ export function encodeBoxedData(value, encode) {
3
+ const properties = Object.create(null);
4
+ for (const [key, descriptor] of boxedDataProperties(value)) {
5
+ if (!("value" in descriptor))
6
+ throw new TypeError("Boxed data cannot contain accessors.");
7
+ properties[key] = {
8
+ value: encode(descriptor.value, key),
9
+ configurable: descriptor.configurable === true,
10
+ enumerable: descriptor.enumerable === true,
11
+ writable: descriptor.writable === true
12
+ };
13
+ }
14
+ return {
15
+ kind: "boxed",
16
+ value: encode(boxedValue(value), "<payload>"),
17
+ properties,
18
+ extensible: Object.isExtensible(value)
19
+ };
20
+ }
21
+ export function restoreBoxedProperties(value, data, decode) {
22
+ for (const [key, descriptor] of Object.entries(data.properties))
23
+ Object.defineProperty(value, key, { ...descriptor, value: decode(descriptor.value) });
24
+ if (!data.extensible)
25
+ Object.preventExtensions(value);
26
+ }
27
+ export function validateBoxedProperties(data) {
28
+ if (!Object.hasOwn(data, "kind") ||
29
+ data.kind !== "boxed" ||
30
+ !Object.hasOwn(data, "value") ||
31
+ !Object.hasOwn(data, "extensible") ||
32
+ !Object.hasOwn(data, "properties") ||
33
+ Object.keys(data).length !== 4 ||
34
+ typeof data.extensible !== "boolean" ||
35
+ typeof data.properties !== "object" ||
36
+ data.properties === null ||
37
+ Array.isArray(data.properties))
38
+ throw new TypeError("Invalid boxed primitive properties.");
39
+ for (const descriptor of Object.values(data.properties)) {
40
+ if (typeof descriptor !== "object" ||
41
+ descriptor === null ||
42
+ !Object.hasOwn(descriptor, "value") ||
43
+ !Object.hasOwn(descriptor, "writable") ||
44
+ !Object.hasOwn(descriptor, "enumerable") ||
45
+ !Object.hasOwn(descriptor, "configurable") ||
46
+ typeof descriptor.writable !== "boolean" ||
47
+ typeof descriptor.enumerable !== "boolean" ||
48
+ typeof descriptor.configurable !== "boolean" ||
49
+ Object.keys(descriptor).length !== 4)
50
+ throw new TypeError("Invalid boxed primitive descriptor.");
51
+ }
52
+ }
@@ -1,5 +1,5 @@
1
1
  export declare const DUMP_FORMAT_VERSION = 1;
2
- export declare const EXECUTION_SEMANTICS = "jobs-v7";
2
+ export declare const EXECUTION_SEMANTICS = "jobs-v8";
3
3
  export type DumpableSnapshot = {
4
4
  sourceHash: string;
5
5
  [key: string]: unknown;
@@ -1,5 +1,5 @@
1
1
  export const DUMP_FORMAT_VERSION = 1;
2
- export const EXECUTION_SEMANTICS = "jobs-v7";
2
+ export const EXECUTION_SEMANTICS = "jobs-v8";
3
3
  import { assertSnapshotGraphDepth } from "../graph-depth.js";
4
4
  import { hasGuestObjectState } from "../interp/object-model.js";
5
5
  import { sandboxErrorTypes } from "../error/shape.js";
@@ -9,6 +9,8 @@ import { requiresArrayEntries, serializeArray } from "./arrays.js";
9
9
  import { float32DataProperties, isFloat32Array } from "../interp/float32.js";
10
10
  import { encodeFloat32Storage } from "./float32array.js";
11
11
  import { isSandboxDate, serializedDateTime } from "../interp/date.js";
12
+ import { boxedDataProperties, isSandboxBox } from "../interp/boxed.js";
13
+ import { encodeBoxedData } from "./boxed.js";
12
14
  const SKIP_VALUE = Symbol("SafeJS.skip-dump-value");
13
15
  export function serializeSafeJSSnapshot(snapshot) {
14
16
  const replayError = Object.getOwnPropertyDescriptor(snapshot, "replayError");
@@ -73,7 +75,7 @@ function serializeDumpValue(value, path, state) {
73
75
  if (hasGuestObjectState(value)) {
74
76
  throw new TypeError("Guest function properties and prototype links cannot be serialized.");
75
77
  }
76
- if (isSandboxDate(value) || isFloat32Array(value))
78
+ if (isSandboxBox(value) || isSandboxDate(value) || isFloat32Array(value))
77
79
  return serializeHeapReference(value, path, state);
78
80
  if (Array.isArray(value)) {
79
81
  const reference = serializeHeapReference(value, path, state);
@@ -98,7 +100,15 @@ function serializeHeapReference(value, path, state) {
98
100
  }
99
101
  if (!state.serializedHeapIds.has(id)) {
100
102
  state.serializedHeapIds.add(id);
101
- if (isSandboxDate(value)) {
103
+ if (isSandboxBox(value)) {
104
+ state.heap[String(id)] = encodeBoxedData(value, (entry, key) => {
105
+ if (Object.is(entry, -0))
106
+ return { kind: "number", value: "-0" };
107
+ const serialized = serializeDumpValue(entry, `${path}.${key}`, state);
108
+ return serialized === SKIP_VALUE ? { kind: "undefined" } : serialized;
109
+ });
110
+ }
111
+ else if (isSandboxDate(value)) {
102
112
  state.heap[String(id)] = { kind: "date", time: serializedDateTime(value) };
103
113
  }
104
114
  else if (isFloat32Array(value)) {
@@ -163,6 +173,7 @@ function indexHeapContainers(snapshot) {
163
173
  for (const [value, stat] of stats.entries()) {
164
174
  if (stat.count > 1 ||
165
175
  stat.cyclic ||
176
+ isSandboxBox(value) ||
166
177
  isSandboxDate(value) ||
167
178
  isFloat32Array(value) ||
168
179
  (Array.isArray(value) && requiresArrayEntries(value)) ||
@@ -200,9 +211,11 @@ function collectContainerStats(value, stats, ancestors) {
200
211
  }
201
212
  stat.expanded = true;
202
213
  ancestors.add(value);
203
- const entries = isSandboxArguments(value)
204
- ? getSandboxArgumentEntries(value).map(([, entry]) => entry)
205
- : getEnumerableDataValues(value);
214
+ const entries = isSandboxBox(value)
215
+ ? boxedDataProperties(value).map(([, descriptor]) => descriptor.value)
216
+ : isSandboxArguments(value)
217
+ ? getSandboxArgumentEntries(value).map(([, entry]) => entry)
218
+ : getEnumerableDataValues(value);
206
219
  for (const entry of entries) {
207
220
  collectContainerStats(entry, stats, ancestors);
208
221
  }
@@ -3,8 +3,8 @@ import { decodeReplayData } from "./replay-data.js";
3
3
  import { validateSnapshotData } from "./validation.js";
4
4
  import { CompileScope } from "../interp/regex/compile-guard.js";
5
5
  export function validateMigrationSemantics(value) {
6
- if (!["jobs-v1", "jobs-v2", "jobs-v3", "jobs-v4", "jobs-v5", "jobs-v6", "jobs-v7"].includes(value))
7
- throw new TypeError("Migration requires a supported execution-semantics marker (jobs-v1 through jobs-v7).");
6
+ if (!["jobs-v1", "jobs-v2", "jobs-v3", "jobs-v4", "jobs-v5", "jobs-v6", "jobs-v7", "jobs-v8"].includes(value))
7
+ throw new TypeError("Migration requires a supported execution-semantics marker (jobs-v1 through jobs-v8).");
8
8
  }
9
9
  export function validateMigrationJournal(sourceHash, replay, hostCalls = [], owner) {
10
10
  if (replay === undefined)
@@ -26,6 +26,11 @@ type Properties = Record<string, {
26
26
  writable: boolean;
27
27
  }>;
28
28
  type DataNode = {
29
+ kind: "boxed";
30
+ value: Atom;
31
+ properties: Properties;
32
+ extensible: boolean;
33
+ } | {
29
34
  kind: "collection-iterator";
30
35
  collectionKind: "map" | "set";
31
36
  method: CollectionIterationMethod;
@@ -5,6 +5,8 @@ import { CompileScope } from "../interp/regex/compile-guard.js";
5
5
  import { float32DataProperties, isFloat32Array } from "../interp/float32.js";
6
6
  import { decodeFloat32Storage, encodeFloat32Storage } from "./float32array.js";
7
7
  import { isSandboxDate, restoreDateTime, serializedDateTime } from "../interp/date.js";
8
+ import { boxedDataProperties, createSandboxBox, nativeBoxedValue } from "../interp/boxed.js";
9
+ import { validateBoxedProperties } from "./boxed.js";
8
10
  import { sandboxErrorNames, sandboxErrorTypes } from "../error/shape.js";
9
11
  import { createSandboxArguments, createSandboxClosure, createSandboxMap, createSandboxRegex, createSandboxSet, isSandboxArguments, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet } from "../interp/values.js";
10
12
  import { serializeArguments } from "./arguments.js";
@@ -65,6 +67,15 @@ export function encodeReplayData(value, options = {}) {
65
67
  properties: child(entry.properties, "properties")
66
68
  };
67
69
  }
70
+ else if (nativeBoxedValue(entry) !== undefined) {
71
+ const properties = Object.create(null);
72
+ nodes[id] = { kind: "boxed", value: child(nativeBoxedValue(entry), "<payload>"), properties, extensible: Object.isExtensible(entry) };
73
+ for (const [key, descriptor] of boxedDataProperties(entry)) {
74
+ if (!("value" in descriptor))
75
+ throw new TypeError(`Cannot record replay data accessor '${key}'.`);
76
+ properties[key] = { value: child(descriptor.value, JSON.stringify(["property", key])), configurable: descriptor.configurable === true, enumerable: descriptor.enumerable === true, writable: descriptor.writable === true };
77
+ }
78
+ }
68
79
  else if (isSandboxDate(entry)) {
69
80
  nodes[id] = { kind: "date", time: serializedDateTime(entry) };
70
81
  }
@@ -247,6 +258,19 @@ export function decodeReplayData(input, options = {}, parent) {
247
258
  options.onCapabilityRestored?.(capability, copy);
248
259
  return copy;
249
260
  }
261
+ if (kind === "boxed") {
262
+ validateBoxedProperties(node);
263
+ const payload = own(node, "value");
264
+ if (typeof payload !== "number" && typeof payload !== "string" && typeof payload !== "boolean" &&
265
+ (payload === null || typeof payload !== "object" || own(record(payload), "tag") !== "number"))
266
+ throw new TypeError("Invalid boxed primitive payload.");
267
+ const result = createSandboxBox(child(payload));
268
+ restored.set(id, result);
269
+ defineProperties(result, record(own(node, "properties")), child);
270
+ if (!node.extensible)
271
+ Object.preventExtensions(result);
272
+ return result;
273
+ }
250
274
  if (kind === "date") {
251
275
  if (Object.keys(node).length !== 2)
252
276
  throw new TypeError("Invalid serialized Date fields.");
@@ -4,10 +4,13 @@ import { toPropertyKey } from "../interp/property-key.js";
4
4
  import { CompileScope } from "../interp/regex/compile-guard.js";
5
5
  import { decodeFloat32Storage } from "./float32array.js";
6
6
  import { restoreDateTime } from "../interp/date.js";
7
+ import { createSandboxBox } from "../interp/boxed.js";
8
+ import { restoreBoxedProperties } from "./boxed.js";
7
9
  import { sandboxErrorTypes } from "../error/shape.js";
8
10
  import { SnapshotMismatchError } from "../restore.js";
9
11
  import { Scope, setSandboxProperty } from "../interp/interpreter.js";
10
12
  import { getGuestFunctionProperties, getGuestFunctionProperty, getSandboxDataProperty, registerGuestClosure, setSandboxPrototype } from "../interp/object-model.js";
13
+ import { functionSources } from "../parse/function-source.js";
11
14
  import { wrapCallerInjectedBindings } from "../interp/host-bridge.js";
12
15
  import { restoreSandboxCollectionIterator } from "../interp/collection-iterator.js";
13
16
  import { isSandboxMap, isSandboxSet } from "../interp/values.js";
@@ -348,6 +351,12 @@ function restoreHeapValue(id, state) {
348
351
  if (serialized === undefined) {
349
352
  throw new Error(`Snapshot references unknown heap value ${id}.`);
350
353
  }
354
+ if (serialized.kind === "boxed") {
355
+ const value = createSandboxBox(deserializeValue(serialized.value, state));
356
+ state.heapValueById.set(id, value);
357
+ restoreBoxedProperties(value, serialized, entry => deserializeValue(entry, state));
358
+ return value;
359
+ }
351
360
  if (serialized.kind === "date") {
352
361
  const value = restoreDateTime(serialized.time);
353
362
  state.heapValueById.set(id, value);
@@ -454,6 +463,7 @@ function restoreClosureValue(astNodeId, capturedScopeId, state) {
454
463
  throw new Error(`Snapshot references unknown closure AST node ${astNodeId}.`);
455
464
  }
456
465
  const baseClosure = createSandboxClosure({
466
+ sourceRange: functionSources.get(node),
457
467
  async: node.async,
458
468
  sandbox: true,
459
469
  length: getFunctionLength(node.params),
@@ -3,6 +3,8 @@ import { type SandboxErrorName } from "../error/shape.js";
3
3
  import { type SerializedArguments } from "./arguments.js";
4
4
  import { type SerializedArray } from "./arrays.js";
5
5
  import { type Float32Data } from "./float32array.js";
6
+ import { type SandboxBox } from "../interp/boxed.js";
7
+ import { type BoxedData } from "./boxed.js";
6
8
  import { type SandboxMap, type SandboxGenerator, type SandboxRegex, type SandboxSet } from "../interp/values.js";
7
9
  type SnapshotId = number | string;
8
10
  type SerializedUndefinedValue = {
@@ -41,7 +43,7 @@ export type SerializedReferenceValue = {
41
43
  kind: "ref";
42
44
  id: number;
43
45
  };
44
- export type SerializedHeapValue = {
46
+ export type SerializedHeapValue = BoxedData<SerializedSnapshotValue> | {
45
47
  kind: "regex-object";
46
48
  source: string;
47
49
  flags: string;
@@ -89,7 +91,7 @@ export type RuntimePromiseValue = {
89
91
  id: SnapshotId;
90
92
  promise?: Promise<unknown>;
91
93
  };
92
- export type RuntimeSnapshotValue = Date | Float32Array | boolean | null | number | string | undefined | RuntimeClosureValue | SandboxGenerator | SandboxCollectionIterator | RuntimePromiseValue | SandboxMap | SandboxRegex | SandboxSet | RuntimeSnapshotValue[] | {
94
+ export type RuntimeSnapshotValue = SandboxBox | Date | Float32Array | boolean | null | number | string | undefined | RuntimeClosureValue | SandboxGenerator | SandboxCollectionIterator | RuntimePromiseValue | SandboxMap | SandboxRegex | SandboxSet | RuntimeSnapshotValue[] | {
93
95
  [key: string]: RuntimeSnapshotValue;
94
96
  };
95
97
  export type RuntimeScopeFrame = {
@@ -8,6 +8,8 @@ import { requiresArrayEntries, serializeArray } from "./arrays.js";
8
8
  import { float32DataProperties, isFloat32Array } from "../interp/float32.js";
9
9
  import { encodeFloat32Storage } from "./float32array.js";
10
10
  import { isSandboxDate, serializedDateTime } from "../interp/date.js";
11
+ import { boxedDataProperties, isSandboxBox } from "../interp/boxed.js";
12
+ import { encodeBoxedData } from "./boxed.js";
11
13
  import { isSandboxArguments, isSandboxGenerator, isSandboxMap, isSandboxRegex, isSandboxSet } from "../interp/values.js";
12
14
  export class UnsnapshotableValueError extends Error {
13
15
  path;
@@ -170,7 +172,7 @@ function serializeValue(value, path, state) {
170
172
  throw new TypeError(`Missing regex heap reference at ${path}.`);
171
173
  return { kind: "regex", source: value.source, flags: value.flags, lastIndex: value.lastIndex };
172
174
  }
173
- if (isSandboxDate(value) || isSandboxMap(value) || isSandboxSet(value) || isSandboxCollectionIterator(value) || isFloat32Array(value)) {
175
+ if (isSandboxBox(value) || isSandboxDate(value) || isSandboxMap(value) || isSandboxSet(value) || isSandboxCollectionIterator(value) || isFloat32Array(value)) {
174
176
  const reference = serializeHeapReference(value, path, state);
175
177
  if (reference === undefined) {
176
178
  throw new TypeError(`Cannot serialize collection without a heap reference at ${path}.`);
@@ -214,6 +216,9 @@ function serializeHeapReference(value, path, state) {
214
216
  for (const [key, entry] of Object.entries(value))
215
217
  entries[key] = serializeValue(entry, `${path}.${key}`, state);
216
218
  }
219
+ else if (isSandboxBox(value)) {
220
+ state.heap[String(id)] = encodeBoxedData(value, (entry, key) => serializeValue(entry, `${path}.${key}`, state));
221
+ }
217
222
  else if (isSandboxDate(value)) {
218
223
  state.heap[String(id)] = { kind: "date", time: serializedDateTime(value) };
219
224
  }
@@ -326,6 +331,7 @@ function indexHeapContainers(input) {
326
331
  stat.cyclic ||
327
332
  (isSandboxRegex(value) && (!Number.isSafeInteger(value.lastIndex) ||
328
333
  (typeof value.lastIndex === "number" && value.lastIndex < 0) || Object.is(value.lastIndex, -0))) ||
334
+ isSandboxBox(value) ||
329
335
  isSandboxDate(value) ||
330
336
  isFloat32Array(value) ||
331
337
  (Array.isArray(value) && requiresArrayEntries(value)) ||
@@ -376,15 +382,17 @@ function collectContainerStats(value, stats, ancestors) {
376
382
  }
377
383
  stat.expanded = true;
378
384
  ancestors.add(value);
379
- const entries = isSandboxCollectionIterator(value)
380
- ? [collectionIteratorState(value).collection, ...Object.values(value)]
381
- : isSandboxArguments(value)
382
- ? Object.values(Object.getOwnPropertyDescriptors(value)).flatMap((descriptor) => "value" in descriptor ? [descriptor.value] : [])
383
- : isSandboxMap(value)
384
- ? [...value.entries].flatMap(([key, entry]) => [key, entry])
385
- : isSandboxSet(value)
386
- ? [...value.values]
387
- : Object.values(value);
385
+ const entries = isSandboxBox(value)
386
+ ? boxedDataProperties(value).map(([, descriptor]) => descriptor.value)
387
+ : isSandboxCollectionIterator(value)
388
+ ? [collectionIteratorState(value).collection, ...Object.values(value)]
389
+ : isSandboxArguments(value)
390
+ ? Object.values(Object.getOwnPropertyDescriptors(value)).flatMap((descriptor) => "value" in descriptor ? [descriptor.value] : [])
391
+ : isSandboxMap(value)
392
+ ? [...value.entries].flatMap(([key, entry]) => [key, entry])
393
+ : isSandboxSet(value)
394
+ ? [...value.values]
395
+ : Object.values(value);
388
396
  for (const entry of entries) {
389
397
  collectContainerStats(entry, stats, ancestors);
390
398
  }
@@ -4,6 +4,7 @@ import { DUMP_FORMAT_VERSION } from "./dump-format.js";
4
4
  import { MAX_DATA_DEPTH } from "../graph-depth.js";
5
5
  import { validateFloat32Storage } from "./float32array.js";
6
6
  import { restoreDateTime } from "../interp/date.js";
7
+ import { validateBoxedProperties } from "./boxed.js";
7
8
  import { hasGuestObjectState } from "../interp/object-model.js";
8
9
  const DEFAULT_MAX_DEPTH = MAX_DATA_DEPTH;
9
10
  const DEFAULT_MAX_ENTRIES = 100_000;
@@ -81,6 +82,10 @@ function validateDumpHeap(root, state) {
81
82
  addUnique(heapIds, id, path);
82
83
  const entry = requireRecord(value, path);
83
84
  validateErrorType(entry, path);
85
+ if (entry.kind === "boxed") {
86
+ validateBoxedRecord(entry, path);
87
+ continue;
88
+ }
84
89
  if (entry.kind === "date") {
85
90
  validateDateRecord(entry, path);
86
91
  continue;
@@ -414,7 +419,7 @@ function validateGeneratorShape(record, path, state) {
414
419
  function validateHeapValue(value, path, state) {
415
420
  const record = requireRecord(value, path);
416
421
  validateErrorType(record, path);
417
- if (!["arguments", "array", "object", "map", "set", "float32array", "date", "collection-iterator", "regex-object"].includes(String(record.kind)))
422
+ if (!["arguments", "array", "object", "map", "set", "float32array", "date", "boxed", "collection-iterator", "regex-object"].includes(String(record.kind)))
418
423
  fail("unknownTag", `${path}.kind`, "unknown heap tag");
419
424
  validateValue(record, path, 1, state);
420
425
  if (record.kind === "arguments")
@@ -423,6 +428,8 @@ function validateHeapValue(value, path, state) {
423
428
  validateArrayHeap(record, path, state);
424
429
  if (record.kind === "object")
425
430
  requireRecord(record.entries, `${path}.entries`);
431
+ if (record.kind === "boxed")
432
+ validateBoxedRecord(record, path);
426
433
  if (record.kind === "date")
427
434
  validateDateRecord(record, path);
428
435
  if (record.kind === "float32array") {
@@ -451,6 +458,20 @@ function validateHeapValue(value, path, state) {
451
458
  requireRecord(record.entries, `${path}.entries`);
452
459
  }
453
460
  }
461
+ function validateBoxedRecord(record, path) {
462
+ try {
463
+ validateBoxedProperties(record);
464
+ }
465
+ catch {
466
+ fail("invalidValue", path, "invalid boxed primitive properties");
467
+ }
468
+ const value = record.value;
469
+ if (typeof value === "number" || typeof value === "string" || typeof value === "boolean")
470
+ return;
471
+ const number = requireRecord(value, `${path}.value`);
472
+ if (number.kind !== "number" || !["NaN", "Infinity", "-Infinity", "-0"].includes(String(number.value)))
473
+ fail("invalidValue", `${path}.value`, "invalid boxed primitive payload");
474
+ }
454
475
  function validateDateRecord(record, path) {
455
476
  if (Object.keys(record).length !== 2)
456
477
  fail("invalidValue", path, "invalid Date fields");