poe-code 14.0.62 → 14.0.64

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 (49) hide show
  1. package/dist/index.js +977 -528
  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-F7TJSPDZ.js → chunk-3EXDJDQM.js} +2 -2
  6. package/packages/safe-js/dist/chunks/{chunk-VKGWGZBH.js → chunk-XO7B4P27.js} +945 -545
  7. package/packages/safe-js/dist/chunks/chunk-XO7B4P27.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/graph-depth.js +3 -0
  11. package/packages/safe-js/dist/index.js +2 -2
  12. package/packages/safe-js/dist/interp/boxed.d.ts +14 -0
  13. package/packages/safe-js/dist/interp/boxed.js +50 -0
  14. package/packages/safe-js/dist/interp/globals/collections.js +4 -4
  15. package/packages/safe-js/dist/interp/globals/console-json.js +15 -18
  16. package/packages/safe-js/dist/interp/globals/misc.js +1 -1
  17. package/packages/safe-js/dist/interp/globals/object-array.js +19 -14
  18. package/packages/safe-js/dist/interp/globals/object.js +10 -5
  19. package/packages/safe-js/dist/interp/globals/primitives.d.ts +8 -0
  20. package/packages/safe-js/dist/interp/globals/primitives.js +69 -0
  21. package/packages/safe-js/dist/interp/host-bridge.js +22 -0
  22. package/packages/safe-js/dist/interp/interpreter.js +175 -122
  23. package/packages/safe-js/dist/interp/iteration.d.ts +5 -3
  24. package/packages/safe-js/dist/interp/iteration.js +22 -1
  25. package/packages/safe-js/dist/interp/methods/array.js +6 -3
  26. package/packages/safe-js/dist/interp/methods/function.js +32 -15
  27. package/packages/safe-js/dist/interp/methods/number.d.ts +1 -0
  28. package/packages/safe-js/dist/interp/methods/number.js +4 -1
  29. package/packages/safe-js/dist/interp/methods/string.d.ts +2 -0
  30. package/packages/safe-js/dist/interp/methods/string.js +2 -2
  31. package/packages/safe-js/dist/interp/object-model.d.ts +5 -0
  32. package/packages/safe-js/dist/interp/object-model.js +66 -14
  33. package/packages/safe-js/dist/interp/promise.js +68 -61
  34. package/packages/safe-js/dist/interp/string-coercion.js +4 -1
  35. package/packages/safe-js/dist/interp/values.d.ts +1 -0
  36. package/packages/safe-js/dist/interp/values.js +78 -3
  37. package/packages/safe-js/dist/migrate.d.ts +2 -2
  38. package/packages/safe-js/dist/migration-file.d.ts +4 -4
  39. package/packages/safe-js/dist/snapshot/boxed.d.ts +16 -0
  40. package/packages/safe-js/dist/snapshot/boxed.js +52 -0
  41. package/packages/safe-js/dist/snapshot/dump-format.js +18 -5
  42. package/packages/safe-js/dist/snapshot/replay-data.d.ts +5 -0
  43. package/packages/safe-js/dist/snapshot/replay-data.js +24 -0
  44. package/packages/safe-js/dist/snapshot/restore.js +8 -0
  45. package/packages/safe-js/dist/snapshot/serialize.d.ts +4 -2
  46. package/packages/safe-js/dist/snapshot/serialize.js +18 -10
  47. package/packages/safe-js/dist/snapshot/validation.js +22 -1
  48. package/packages/safe-js/dist/chunks/chunk-VKGWGZBH.js.map +0 -7
  49. /package/packages/safe-js/dist/chunks/{chunk-F7TJSPDZ.js.map → chunk-3EXDJDQM.js.map} +0 -0
@@ -22,7 +22,7 @@ import {
22
22
  resolveFsConfig,
23
23
  splitFrontmatter,
24
24
  withBrokenPipeGuard
25
- } from "./chunks/chunk-F7TJSPDZ.js";
25
+ } from "./chunks/chunk-3EXDJDQM.js";
26
26
  import {
27
27
  Budget,
28
28
  SandboxError,
@@ -34,7 +34,7 @@ import {
34
34
  replaceErrorStack,
35
35
  restore,
36
36
  run
37
- } from "./chunks/chunk-VKGWGZBH.js";
37
+ } from "./chunks/chunk-XO7B4P27.js";
38
38
  import "./chunks/chunk-UXMKEUH3.js";
39
39
  import "./chunks/chunk-5DYEOCT5.js";
40
40
  import "./chunks/chunk-4YIDX44G.js";
@@ -5,7 +5,7 @@ import {
5
5
  defineExtension,
6
6
  lint,
7
7
  run
8
- } from "./chunks/chunk-VKGWGZBH.js";
8
+ } from "./chunks/chunk-XO7B4P27.js";
9
9
  import "./chunks/chunk-4YIDX44G.js";
10
10
  export {
11
11
  Budget,
@@ -3,6 +3,7 @@ import { SandboxError } from "./interp/budget.js";
3
3
  import { getSandboxArgumentEntries, isSandboxArguments } from "./interp/arguments.js";
4
4
  import { collectionIteratorState, isSandboxCollectionIterator } from "./interp/collection-iterator.js";
5
5
  import { isSandboxMap, isSandboxSet } from "./interp/collection-brands.js";
6
+ import { boxedDataProperties, isSandboxBox } from "./interp/boxed.js";
6
7
  export const MAX_DATA_DEPTH = 1_024;
7
8
  export class SnapshotBudgetError extends Error {
8
9
  code = "budgetExceeded";
@@ -63,6 +64,8 @@ function walkGraphDepth(root, rootPath, assertDepth) {
63
64
  }
64
65
  }
65
66
  function graphEntries(value) {
67
+ if (isSandboxBox(value))
68
+ return boxedDataProperties(value).map(([key, descriptor]) => [`.${key}`, descriptor.value]);
66
69
  if (isSandboxArguments(value)) {
67
70
  return getSandboxArgumentEntries(value).map(([key, entry]) => [`.${key}`, entry]);
68
71
  }
@@ -26,7 +26,7 @@ import {
26
26
  runWithSpawnUsageAccumulator,
27
27
  splitFrontmatter,
28
28
  supportsSpawnMode
29
- } from "./chunks/chunk-F7TJSPDZ.js";
29
+ } from "./chunks/chunk-3EXDJDQM.js";
30
30
  import {
31
31
  Budget,
32
32
  FileSnapshotBackend,
@@ -50,7 +50,7 @@ import {
50
50
  registerPendingHostCallPolicy,
51
51
  restore,
52
52
  run
53
- } from "./chunks/chunk-VKGWGZBH.js";
53
+ } from "./chunks/chunk-XO7B4P27.js";
54
54
  import "./chunks/chunk-UXMKEUH3.js";
55
55
  import "./chunks/chunk-5DYEOCT5.js";
56
56
  import "./chunks/chunk-4YIDX44G.js";
@@ -0,0 +1,14 @@
1
+ import type { SandboxObject } from "./values.js";
2
+ export type BoxedPrimitive = string | number | boolean;
3
+ export type BoxedKind = "string" | "number" | "boolean";
4
+ declare const boxedPrimitiveBrand: unique symbol;
5
+ export type SandboxBox = SandboxObject & {
6
+ readonly [boxedPrimitiveBrand]: true;
7
+ };
8
+ export declare function nativeBoxedValue(value: unknown): BoxedPrimitive | undefined;
9
+ export declare function createSandboxBox(value: unknown): SandboxBox;
10
+ export declare function isSandboxBox(value: unknown): value is SandboxBox;
11
+ export declare function boxedValue(value: object): BoxedPrimitive;
12
+ export declare function primitiveReceiver(value: unknown, kind: BoxedKind): BoxedPrimitive;
13
+ export declare function boxedDataProperties(value: object): Array<[string, PropertyDescriptor]>;
14
+ export {};
@@ -0,0 +1,50 @@
1
+ import { types } from "node:util";
2
+ const boxes = new WeakSet();
3
+ const numberValue = Number.prototype.valueOf;
4
+ const stringValue = String.prototype.valueOf;
5
+ const booleanValue = Boolean.prototype.valueOf;
6
+ export function nativeBoxedValue(value) {
7
+ if (types.isNumberObject(value))
8
+ return Reflect.apply(numberValue, value, []);
9
+ if (types.isStringObject(value))
10
+ return Reflect.apply(stringValue, value, []);
11
+ if (types.isBooleanObject(value))
12
+ return Reflect.apply(booleanValue, value, []);
13
+ return undefined;
14
+ }
15
+ export function createSandboxBox(value) {
16
+ if (typeof value !== "number" && typeof value !== "string" && typeof value !== "boolean")
17
+ throw new TypeError("Invalid boxed primitive payload.");
18
+ const box = Object(value);
19
+ Object.setPrototypeOf(box, null);
20
+ boxes.add(box);
21
+ return box;
22
+ }
23
+ export function isSandboxBox(value) {
24
+ return typeof value === "object" && value !== null && boxes.has(value);
25
+ }
26
+ export function boxedValue(value) {
27
+ if (!boxes.has(value))
28
+ throw new TypeError("Expected a sandbox boxed primitive.");
29
+ return nativeBoxedValue(value);
30
+ }
31
+ export function primitiveReceiver(value, kind) {
32
+ const primitive = isSandboxBox(value) ? boxedValue(value) : value;
33
+ if (typeof primitive !== kind)
34
+ throw new TypeError(`${kind} method requires a ${kind} receiver.`);
35
+ return primitive;
36
+ }
37
+ export function boxedDataProperties(value) {
38
+ const primitive = nativeBoxedValue(value);
39
+ return Object.entries(Object.getOwnPropertyDescriptors(value)).filter(([key]) => {
40
+ if (typeof primitive !== "string")
41
+ return true;
42
+ if (key === "length")
43
+ return false;
44
+ const index = Number(key);
45
+ return !(Number.isInteger(index) &&
46
+ index >= 0 &&
47
+ index < primitive.length &&
48
+ String(index) === key);
49
+ });
50
+ }
@@ -71,13 +71,13 @@ function populateCollection(source, collection, { name, budget, context, append,
71
71
  budget.allocateCollectionEntries(0);
72
72
  if (source === undefined || source === null)
73
73
  return collection;
74
- const iterator = getSandboxIterator(source, budget);
74
+ const iterator = getSandboxIterator(source, budget, context);
75
75
  if (iterator === undefined)
76
76
  throw new TypeError(`${name} constructor requires an iterable.`);
77
77
  let entry;
78
78
  let failure;
79
79
  const retained = {};
80
- budget.setRetainedValues(retained, () => [source, collection, entry, failure, ...(retainedValues?.() ?? [])]);
80
+ budget.setRetainedValues(retained, () => [source, iterator.retainedValue, collection, entry, failure, ...(retainedValues?.() ?? [])]);
81
81
  const checkData = createDataCheckpoint(budget, context);
82
82
  const closeOnThrow = (error) => {
83
83
  failure = isCapturedException(error) ? error.reason : error;
@@ -88,7 +88,7 @@ function populateCollection(source, collection, { name, budget, context, append,
88
88
  };
89
89
  try {
90
90
  const closing = iterator.return?.();
91
- if (iterator.generator)
91
+ if (iterator.generator || iterator.asynchronous)
92
92
  return Promise.resolve(closing).then(() => { throw error; }, rethrow);
93
93
  }
94
94
  catch (closeError) {
@@ -113,7 +113,7 @@ function populateCollection(source, collection, { name, budget, context, append,
113
113
  }
114
114
  return false;
115
115
  };
116
- if (iterator.generator) {
116
+ if (iterator.generator || iterator.asynchronous) {
117
117
  return (async () => {
118
118
  try {
119
119
  checkData(collection, 0, true);
@@ -1,4 +1,6 @@
1
1
  import { dateMethods, isSandboxDate } from "../date.js";
2
+ import { boxedValue, isSandboxBox } from "../boxed.js";
3
+ import { sandboxNumber, sandboxString } from "../string-coercion.js";
2
4
  import { CompileScope } from "../regex/compile-guard.js";
3
5
  import { wrapCallerInjectedBindings } from "../host-bridge.js";
4
6
  import { allocateProducedSandboxValue, createSandboxClosure, deepCopyFromSandbox, isSandboxClosure, isSandboxPromise } from "../values.js";
@@ -8,12 +10,15 @@ export function createConsoleJsonGlobals(options) {
8
10
  JSON: {
9
11
  parse: createSandboxClosure({
10
12
  sandbox: true,
11
- call: async ([text]) => parseJson(text, options.budget),
13
+ call: async ([text], context) => {
14
+ const converted = sandboxString(text, options.budget, context);
15
+ return copyJsonToSandbox(JSON.parse(converted instanceof Promise ? await converted : converted), options.budget);
16
+ },
12
17
  name: "parse"
13
18
  }),
14
19
  stringify: createSandboxClosure({
15
20
  sandbox: true,
16
- call: async ([value, replacer, indent]) => stringifyJson(value, replacer, indent, options.budget),
21
+ call: async ([value, replacer, indent], context) => stringifyJson(value, replacer, indent, options.budget, context),
17
22
  name: "stringify"
18
23
  })
19
24
  },
@@ -69,11 +74,7 @@ export function createConsoleJsonGlobals(options) {
69
74
  })
70
75
  };
71
76
  }
72
- function parseJson(input, budget) {
73
- const text = budget.allocateString(toJsonParseText(input));
74
- return copyJsonToSandbox(JSON.parse(text), budget);
75
- }
76
- async function stringifyJson(value, replacer, indent, budget) {
77
+ async function stringifyJson(value, replacer, indent, budget, context) {
77
78
  if (replacer !== undefined && replacer !== null && !isSandboxClosure(replacer)) {
78
79
  throw new TypeError("JSON.stringify(value, replacer, indent) only supports function, null, or undefined replacers.");
79
80
  }
@@ -84,6 +85,7 @@ async function stringifyJson(value, replacer, indent, budget) {
84
85
  defineDataProperty(holder, "", value);
85
86
  const output = await stringifyProperty("", holder, {
86
87
  budget,
88
+ context,
87
89
  gap: normalizeStringifyGap(indent),
88
90
  replacer: isSandboxClosure(replacer) ? replacer : undefined,
89
91
  stack: []
@@ -93,17 +95,6 @@ async function stringifyJson(value, replacer, indent, budget) {
93
95
  }
94
96
  return budget.allocateString(output);
95
97
  }
96
- function toJsonParseText(input) {
97
- if (Array.isArray(input)) {
98
- return input
99
- .map((entry) => (entry === null || entry === undefined ? "" : toJsonParseText(entry)))
100
- .join(",");
101
- }
102
- if (typeof input === "object" && input !== null) {
103
- return "[object Object]";
104
- }
105
- return String(input);
106
- }
107
98
  async function stringifyProperty(key, holder, state, indent = "") {
108
99
  let value = getOwnDataValue(holder, key);
109
100
  if (isSandboxDate(value)) {
@@ -121,6 +112,12 @@ async function stringifyProperty(key, holder, state, indent = "") {
121
112
  return stringifyValue(value, state, indent);
122
113
  }
123
114
  async function stringifyValue(value, state, indent) {
115
+ if (isSandboxBox(value)) {
116
+ const primitive = boxedValue(value);
117
+ value = typeof primitive === "number" ? await sandboxNumber(value, state.budget, state.context)
118
+ : typeof primitive === "string" ? await sandboxString(value, state.budget, state.context)
119
+ : primitive;
120
+ }
124
121
  if (value === null) {
125
122
  return "null";
126
123
  }
@@ -34,7 +34,7 @@ function structuredCloneSandboxValue(value, budget, parent) {
34
34
  const operation = budget.acquireCompileOwner(false, parent?.owner);
35
35
  const compilation = parent?.owner === operation.owner ? parent : new CompileScope(operation.owner);
36
36
  try {
37
- const clone = cloneSandboxValue(value, { compilation, resetRegexLastIndex: true });
37
+ const clone = cloneSandboxValue(value, { compilation, resetRegexLastIndex: true, structuredClone: true });
38
38
  assertSandboxGraphDepth(clone);
39
39
  assertStructuredCloneable(clone, new WeakSet());
40
40
  allocateProducedSandboxValue(clone, budget);
@@ -3,6 +3,7 @@ import { invokeBuiltinClosure } from "../builtin-call.js";
3
3
  import { createDataCheckpoint } from "../data-checkpoint.js";
4
4
  import { isCapturedException } from "../exceptions.js";
5
5
  import { isSandboxDate } from "../date.js";
6
+ import { createSandboxBox } from "../boxed.js";
6
7
  import { getDatePrototype } from "./date.js";
7
8
  import { createObjectGlobal, hasOwnSandboxProperty } from "./object.js";
8
9
  import { getHostObjectKeys, isGuestHostObject } from "../host-capabilities.js";
@@ -11,6 +12,7 @@ import { setSandboxProperty } from "../interpreter.js";
11
12
  import { getSandboxIterator } from "../iteration.js";
12
13
  import { sandboxNumber, sandboxString } from "../string-coercion.js";
13
14
  import { createNumericParsers } from "./numeric-parsers.js";
15
+ import { createPrimitiveConstructor } from "./primitives.js";
14
16
  import { getSandboxDataProperty, getSandboxPrototype, isGuestClosure, markDescriptorObject, materializeFunctionProperties, setSandboxPrototype } from "../object-model.js";
15
17
  import { allocateProducedSandboxValue, createSandboxClosure, deepCopyToSandbox, defineOwnDataProperty, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet, measureSandboxData, ownEnumerableSandboxEntries as getOwnEnumerableEntries } from "../values.js";
16
18
  export function createObjectArrayGlobals(options) {
@@ -82,6 +84,8 @@ export function createObjectArrayGlobals(options) {
82
84
  call: ([value]) => {
83
85
  if (isSandboxDate(value))
84
86
  return getDatePrototype(value, options.budget, options.compileOwner);
87
+ if (value !== null && value !== undefined && typeof value !== "object")
88
+ value = createSandboxBox(value);
85
89
  objectProperties(value);
86
90
  return getSandboxPrototype(value, options.budget);
87
91
  },
@@ -123,11 +127,11 @@ export function createObjectArrayGlobals(options) {
123
127
  fromEntries: createSandboxClosure({
124
128
  sandbox: true,
125
129
  call: ([value], context) => {
126
- const iterator = getSandboxIterator(value, options.budget);
130
+ const iterator = getSandboxIterator(value, options.budget, context);
127
131
  if (iterator === undefined) {
128
132
  throw new TypeError("Object.fromEntries requires an iterable.");
129
133
  }
130
- if (context === undefined && !iterator.generator) {
134
+ if (context === undefined && !iterator.generator && !iterator.asynchronous) {
131
135
  // The direct host adapter preserves synchronous results and native hooks.
132
136
  return allocateProducedSandboxValue(Object.setPrototypeOf(Reflect.apply(Object.fromEntries, Object, [{ [Symbol.iterator]: () => iterator }]), null), options.budget);
133
137
  }
@@ -181,8 +185,7 @@ export function createObjectArrayGlobals(options) {
181
185
  })
182
186
  }
183
187
  }),
184
- String: createSandboxClosure({
185
- sandbox: true,
188
+ String: createPrimitiveConstructor({
186
189
  call: (args, context) => sandboxString(args.length === 0 ? "" : args[0], options.budget, context),
187
190
  name: "String",
188
191
  properties: {
@@ -202,9 +205,8 @@ export function createObjectArrayGlobals(options) {
202
205
  name: "fromCodePoint"
203
206
  })
204
207
  }
205
- }),
206
- Number: createSandboxClosure({
207
- sandbox: true,
208
+ }, options.budget),
209
+ Number: createPrimitiveConstructor({
208
210
  call: (args, context) => sandboxNumber(args.length === 0 ? 0 : args[0], options.budget, context),
209
211
  name: "Number",
210
212
  properties: {
@@ -238,12 +240,11 @@ export function createObjectArrayGlobals(options) {
238
240
  NEGATIVE_INFINITY: Number.NEGATIVE_INFINITY,
239
241
  POSITIVE_INFINITY: Number.POSITIVE_INFINITY
240
242
  }
241
- }),
242
- Boolean: createSandboxClosure({
243
- sandbox: true,
243
+ }, options.budget),
244
+ Boolean: createPrimitiveConstructor({
244
245
  call: ([value]) => Boolean(value),
245
246
  name: "Boolean"
246
- })
247
+ }, options.budget)
247
248
  };
248
249
  }
249
250
  async function objectFromSandboxEntries(items, iterator, budget, context) {
@@ -253,7 +254,7 @@ async function objectFromSandboxEntries(items, iterator, budget, context) {
253
254
  let value;
254
255
  let failure;
255
256
  const retained = {};
256
- budget.setRetainedValues(retained, () => [items, object, entry, key, value, failure]);
257
+ budget.setRetainedValues(retained, () => [items, iterator.retainedValue, object, entry, key, value, failure]);
257
258
  const checkData = createDataCheckpoint(budget, context);
258
259
  const closeOnThrow = async (error) => {
259
260
  failure = isCapturedException(error) ? error.reason : error;
@@ -315,6 +316,10 @@ function assignSandboxValues(target, sources, budget) {
315
316
  if (target === null || target === undefined) {
316
317
  throw new TypeError("Object.assign(target, ...sources) requires a non-null target.");
317
318
  }
319
+ if (typeof target !== "object") {
320
+ target = createSandboxBox(target);
321
+ budget.chargeDataUsage(measureSandboxData([target]));
322
+ }
318
323
  if (!isGuestClosure(target) && !isAssignableSandboxTarget(target)) {
319
324
  throw new TypeError("Object.assign(target, ...sources) requires an object or array target.");
320
325
  }
@@ -403,13 +408,13 @@ async function arrayFromSandboxValues(args, budget, context) {
403
408
  const read = (property) => context?.getProperty !== undefined
404
409
  ? context.getProperty(items, property)
405
410
  : getSandboxDataProperty(items, property, budget);
406
- const iterator = getSandboxIterator(items, budget);
411
+ const iterator = getSandboxIterator(items, budget, context);
407
412
  const constructor = context?.thisValue;
408
413
  let result;
409
414
  let currentValue;
410
415
  let failure;
411
416
  const retained = {};
412
- budget.setRetainedValues(retained, () => [items, mapFn, constructor, result, currentValue, failure]);
417
+ budget.setRetainedValues(retained, () => [items, iterator?.retainedValue, mapFn, constructor, result, currentValue, failure]);
413
418
  const checkData = createDataCheckpoint(budget, context);
414
419
  const closeOnThrow = async (error) => {
415
420
  failure = isCapturedException(error) ? error.reason : error;
@@ -1,5 +1,6 @@
1
1
  import { assertSandboxDataDepth } from "../../graph-depth.js";
2
2
  import { isSandboxDate } from "../date.js";
3
+ import { boxedValue, createSandboxBox, isSandboxBox } from "../boxed.js";
3
4
  import { isSandboxErrorConstructorInstance } from "../exceptions.js";
4
5
  import { isFloat32Array } from "../float32.js";
5
6
  import { hasHostObjectMember, isGuestHostObject } from "../host-capabilities.js";
@@ -7,14 +8,18 @@ import { collectionIteratorState, isSandboxCollectionIterator } from "../collect
7
8
  import { getSandboxPrototype, installObjectPrototype, isGuestClosure, markDescriptorObject, materializeFunctionProperties } from "../object-model.js";
8
9
  import { sandboxString } from "../string-coercion.js";
9
10
  import { createSandboxClosure, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet } from "../values.js";
11
+ import { measureSandboxData } from "../values.js";
10
12
  export function createObjectGlobal(methods, budget) {
11
13
  const construct = ([value]) => {
12
14
  if (value === null || value === undefined) {
13
15
  budget.chargeDataUsage(1);
14
16
  return Object.create(null);
15
17
  }
16
- if (typeof value !== "object")
17
- throw new TypeError("Object primitive boxing is not supported.");
18
+ if (typeof value !== "object") {
19
+ const box = createSandboxBox(value);
20
+ budget.chargeDataUsage(measureSandboxData([box]));
21
+ return box;
22
+ }
18
23
  return value;
19
24
  };
20
25
  const constructor = createSandboxClosure({
@@ -44,9 +49,7 @@ export function createObjectGlobal(methods, budget) {
44
49
  length: 0,
45
50
  call: (_args, context) => {
46
51
  const value = requireReceiver(context?.thisValue);
47
- if (typeof value !== "object")
48
- throw new TypeError("Object primitive boxing is not supported.");
49
- return value;
52
+ return construct([value]);
50
53
  }
51
54
  }),
52
55
  hasOwnProperty: createSandboxClosure({
@@ -117,6 +120,8 @@ export function hasOwnSandboxProperty(value, key, enumerable) {
117
120
  return descriptor !== undefined && (!enumerable || descriptor.enumerable === true);
118
121
  }
119
122
  function typeTag(value) {
123
+ if (isSandboxBox(value))
124
+ value = boxedValue(value);
120
125
  if (value === undefined)
121
126
  return "Undefined";
122
127
  if (value === null)
@@ -0,0 +1,8 @@
1
+ import type { Budget } from "../budget.js";
2
+ import { type BoxedPrimitive } from "../boxed.js";
3
+ import { type SandboxCallContext, type SandboxClosure, type SandboxObject, type SandboxValue } from "../values.js";
4
+ export declare function createPrimitiveConstructor(options: {
5
+ name: "Number" | "String" | "Boolean";
6
+ call(args: readonly SandboxValue[], context?: SandboxCallContext): BoxedPrimitive | Promise<BoxedPrimitive>;
7
+ properties?: SandboxObject;
8
+ }, budget: Budget): SandboxClosure;
@@ -0,0 +1,69 @@
1
+ import { createSandboxBox, primitiveReceiver } from "../boxed.js";
2
+ import { installBoxedPrototype, materializeFunctionProperties } from "../object-model.js";
3
+ import { getNumberMember, numberMethodNames } from "../methods/number.js";
4
+ import { getStringMember, stringMethodNames } from "../methods/string.js";
5
+ import { createSandboxClosure, isSandboxClosure, measureSandboxData } from "../values.js";
6
+ export function createPrimitiveConstructor(options, budget) {
7
+ const initial = { Number: 0, String: "", Boolean: false }[options.name];
8
+ const kind = typeof initial;
9
+ const prototype = createSandboxBox(initial);
10
+ const allocate = (value) => {
11
+ const box = createSandboxBox(value);
12
+ budget.chargeDataUsage(measureSandboxData([box]));
13
+ return box;
14
+ };
15
+ const constructor = createSandboxClosure({
16
+ guest: true,
17
+ sandbox: true,
18
+ name: options.name,
19
+ length: 1,
20
+ call: options.call,
21
+ construct: (args, context) => {
22
+ const value = options.call(args, context);
23
+ return value instanceof Promise ? value.then(allocate) : allocate(value);
24
+ }
25
+ });
26
+ const properties = materializeFunctionProperties(constructor);
27
+ Object.defineProperty(properties, "prototype", { value: prototype, writable: false });
28
+ Object.defineProperty(prototype, "constructor", {
29
+ value: constructor,
30
+ writable: true,
31
+ configurable: true
32
+ });
33
+ for (const [name, value] of Object.entries(options.properties ?? {}))
34
+ Object.defineProperty(properties, name, {
35
+ value,
36
+ writable: isSandboxClosure(value),
37
+ configurable: isSandboxClosure(value)
38
+ });
39
+ const methods = new Map([
40
+ [
41
+ "valueOf",
42
+ createSandboxClosure({
43
+ sandbox: true,
44
+ name: "valueOf",
45
+ length: 0,
46
+ call: (_args, context) => primitiveReceiver(context?.thisValue, kind)
47
+ })
48
+ ]
49
+ ]);
50
+ if (kind === "number") {
51
+ for (const name of numberMethodNames)
52
+ methods.set(name, getNumberMember(name, budget));
53
+ }
54
+ else {
55
+ methods.set("toString", createSandboxClosure({
56
+ sandbox: true,
57
+ name: "toString",
58
+ length: 0,
59
+ call: (_args, context) => budget.allocateString(String(primitiveReceiver(context?.thisValue, kind)))
60
+ }));
61
+ if (kind === "string")
62
+ for (const name of stringMethodNames)
63
+ methods.set(name, getStringMember("", name, budget));
64
+ }
65
+ for (const [name, value] of methods)
66
+ Object.defineProperty(prototype, name, { value, writable: true, configurable: true });
67
+ installBoxedPrototype(budget, prototype, constructor);
68
+ return constructor;
69
+ }
@@ -1,5 +1,6 @@
1
1
  import { normalizeClosureResult } from "./async.js";
2
2
  import { copyNativeDate } from "./date.js";
3
+ import { boxedDataProperties, createSandboxBox, nativeBoxedValue } from "./boxed.js";
3
4
  import { exportHostCapability, importHostCapability, isLiveCapability } from "./host-capabilities.js";
4
5
  import { attachErrorSpan, replaceErrorStack } from "../error/shape.js";
5
6
  import { SandboxError } from "./budget.js";
@@ -670,6 +671,27 @@ export function copyHostValueToSandbox(value, stackFrames, options, state, path)
670
671
  : path
671
672
  }, state);
672
673
  }
674
+ const primitive = nativeBoxedValue(value);
675
+ if (primitive !== undefined) {
676
+ if (typeof primitive === "string")
677
+ budget.allocateString(primitive);
678
+ const original = value;
679
+ const existing = state.seen.get(original);
680
+ if (existing !== undefined)
681
+ return existing;
682
+ const copy = createSandboxBox(primitive);
683
+ state.seen.set(original, copy);
684
+ budget.chargeDataUsage(measureSandboxData([copy]));
685
+ for (const [key, descriptor] of boxedDataProperties(original)) {
686
+ if (!("value" in descriptor))
687
+ throw new TypeError(`Unsupported sandbox value at ${joinPath(path, key)}: accessor property`);
688
+ Object.defineProperty(copy, budget.allocateString(key), {
689
+ ...descriptor,
690
+ value: copyHostValueToSandbox(descriptor.value, stackFrames, { ...options, capabilityPath: [...(options.capabilityPath ?? []), key] }, state, joinPath(path, key))
691
+ });
692
+ }
693
+ return copy;
694
+ }
673
695
  const date = copyNativeDate(value);
674
696
  if (date !== undefined) {
675
697
  const existing = state.seen.get(value);