poe-code 14.0.62 → 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.
- package/dist/index.js +929 -504
- package/dist/index.js.map +4 -4
- package/dist/metafile.json +1 -1
- package/package.json +1 -1
- package/packages/safe-js/dist/chunks/{chunk-F7TJSPDZ.js → chunk-KMTTD3BL.js} +2 -2
- package/packages/safe-js/dist/chunks/{chunk-VKGWGZBH.js → chunk-RU2BCN4G.js} +898 -521
- package/packages/safe-js/dist/chunks/chunk-RU2BCN4G.js.map +7 -0
- package/packages/safe-js/dist/cli.js +2 -2
- package/packages/safe-js/dist/core.js +1 -1
- package/packages/safe-js/dist/graph-depth.js +3 -0
- package/packages/safe-js/dist/index.js +2 -2
- package/packages/safe-js/dist/interp/boxed.d.ts +14 -0
- package/packages/safe-js/dist/interp/boxed.js +50 -0
- package/packages/safe-js/dist/interp/globals/collections.js +4 -4
- package/packages/safe-js/dist/interp/globals/console-json.js +15 -18
- package/packages/safe-js/dist/interp/globals/misc.js +1 -1
- package/packages/safe-js/dist/interp/globals/object-array.js +19 -14
- package/packages/safe-js/dist/interp/globals/object.js +10 -5
- package/packages/safe-js/dist/interp/globals/primitives.d.ts +8 -0
- package/packages/safe-js/dist/interp/globals/primitives.js +69 -0
- package/packages/safe-js/dist/interp/host-bridge.js +22 -0
- package/packages/safe-js/dist/interp/interpreter.js +172 -120
- package/packages/safe-js/dist/interp/iteration.d.ts +5 -3
- package/packages/safe-js/dist/interp/iteration.js +22 -1
- package/packages/safe-js/dist/interp/methods/array.js +6 -3
- package/packages/safe-js/dist/interp/methods/number.d.ts +1 -0
- package/packages/safe-js/dist/interp/methods/number.js +4 -1
- package/packages/safe-js/dist/interp/methods/string.d.ts +2 -0
- package/packages/safe-js/dist/interp/methods/string.js +2 -2
- package/packages/safe-js/dist/interp/object-model.d.ts +5 -0
- package/packages/safe-js/dist/interp/object-model.js +41 -3
- package/packages/safe-js/dist/interp/promise.js +68 -61
- package/packages/safe-js/dist/interp/string-coercion.js +4 -1
- package/packages/safe-js/dist/interp/values.d.ts +1 -0
- package/packages/safe-js/dist/interp/values.js +78 -3
- package/packages/safe-js/dist/migrate.d.ts +2 -2
- package/packages/safe-js/dist/migration-file.d.ts +4 -4
- package/packages/safe-js/dist/snapshot/boxed.d.ts +16 -0
- package/packages/safe-js/dist/snapshot/boxed.js +52 -0
- package/packages/safe-js/dist/snapshot/dump-format.js +18 -5
- package/packages/safe-js/dist/snapshot/replay-data.d.ts +5 -0
- package/packages/safe-js/dist/snapshot/replay-data.js +24 -0
- package/packages/safe-js/dist/snapshot/restore.js +8 -0
- package/packages/safe-js/dist/snapshot/serialize.d.ts +4 -2
- package/packages/safe-js/dist/snapshot/serialize.js +18 -10
- package/packages/safe-js/dist/snapshot/validation.js +22 -1
- package/packages/safe-js/dist/chunks/chunk-VKGWGZBH.js.map +0 -7
- /package/packages/safe-js/dist/chunks/{chunk-F7TJSPDZ.js.map → chunk-KMTTD3BL.js.map} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SandboxError } from "./budget.js";
|
|
2
2
|
import { coerceThrownValue, createSubsetErrorValue } from "./exceptions.js";
|
|
3
3
|
import { getSandboxIterator } from "./iteration.js";
|
|
4
|
+
import { retainValues } from "./resources.js";
|
|
4
5
|
import { runPromiseJob } from "./jobs.js";
|
|
5
6
|
import { observeSandboxPromise } from "./promise-tracker.js";
|
|
6
7
|
import { createSandboxClosure, createSandboxPromise, isSandboxClosure, isSandboxPromise } from "./values.js";
|
|
@@ -337,76 +338,82 @@ async function settleIterable(iterable, method, budget, constructor, context) {
|
|
|
337
338
|
const promiseResolve = readPromiseReceiverProperty(constructor, "resolve", prototype);
|
|
338
339
|
if (!isSandboxClosure(promiseResolve))
|
|
339
340
|
throw new TypeError("Promise constructor requires a callable resolve.");
|
|
340
|
-
const iterator = getSandboxIterator(iterable, budget);
|
|
341
|
+
const iterator = getSandboxIterator(iterable, budget, context);
|
|
341
342
|
if (iterator === undefined)
|
|
342
343
|
throw new TypeError("Promise helpers require an iterable.");
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
344
|
+
const releaseIterator = retainValues(budget, () => [iterator.retainedValue]);
|
|
345
|
+
try {
|
|
346
|
+
let index = 0;
|
|
347
|
+
while (true) {
|
|
348
|
+
budget.visitNode();
|
|
349
|
+
const next = iterator.generator || iterator.asynchronous
|
|
350
|
+
? await iterator.next()
|
|
351
|
+
: iterator.next();
|
|
352
|
+
if (typeof next !== "object" || next === null)
|
|
353
|
+
throw new TypeError("Iterator result must be an object.");
|
|
354
|
+
if (next.done)
|
|
355
|
+
break;
|
|
356
|
+
const value = next.value;
|
|
357
|
+
try {
|
|
358
|
+
budget.allocateArrayLength(index + 1);
|
|
359
|
+
const entryIndex = index++;
|
|
360
|
+
if (method !== "race")
|
|
361
|
+
values.push(undefined);
|
|
362
|
+
const entry = await callPromiseClosure(promiseResolve, [value], constructor, budget, context);
|
|
363
|
+
let called = false;
|
|
364
|
+
const handlers = ["fulfilled", "rejected"].map((state) => {
|
|
365
|
+
if (method === "race" || (method === "any" && state === "fulfilled"))
|
|
366
|
+
return state === "fulfilled" ? capability.resolve : capability.reject;
|
|
367
|
+
if (method === "all" && state === "rejected")
|
|
368
|
+
return capability.reject;
|
|
369
|
+
return createSandboxClosure({
|
|
370
|
+
sandbox: true,
|
|
371
|
+
retainedValues: () => [
|
|
372
|
+
capability.promise,
|
|
373
|
+
capability.resolve,
|
|
374
|
+
capability.reject,
|
|
375
|
+
values
|
|
376
|
+
],
|
|
377
|
+
call: async ([settlement]) => {
|
|
378
|
+
if (called)
|
|
379
|
+
return undefined;
|
|
380
|
+
called = true;
|
|
381
|
+
values[entryIndex] =
|
|
382
|
+
method === "allSettled"
|
|
383
|
+
? state === "fulfilled"
|
|
384
|
+
? { status: state, value: settlement }
|
|
385
|
+
: { status: state, reason: settlement }
|
|
386
|
+
: settlement;
|
|
387
|
+
remaining--;
|
|
388
|
+
await complete();
|
|
376
389
|
return undefined;
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
method === "allSettled"
|
|
380
|
-
? state === "fulfilled"
|
|
381
|
-
? { status: state, value: settlement }
|
|
382
|
-
: { status: state, reason: settlement }
|
|
383
|
-
: settlement;
|
|
384
|
-
remaining--;
|
|
385
|
-
await complete();
|
|
386
|
-
return undefined;
|
|
387
|
-
}
|
|
390
|
+
}
|
|
391
|
+
});
|
|
388
392
|
});
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
await callPromiseClosure(then, handlers, entry, budget, context);
|
|
395
|
-
}
|
|
396
|
-
catch (error) {
|
|
397
|
-
try {
|
|
398
|
-
const closed = iterator.return?.();
|
|
399
|
-
if (iterator.generator)
|
|
400
|
-
await closed;
|
|
393
|
+
remaining++;
|
|
394
|
+
const then = readPromiseReceiverProperty(entry, "then", prototype);
|
|
395
|
+
if (!isSandboxClosure(then))
|
|
396
|
+
throw new TypeError("Promise resolver result requires a callable then.");
|
|
397
|
+
await callPromiseClosure(then, handlers, entry, budget, context);
|
|
401
398
|
}
|
|
402
|
-
catch {
|
|
399
|
+
catch (error) {
|
|
400
|
+
try {
|
|
401
|
+
const closed = iterator.return?.();
|
|
402
|
+
if (iterator.generator || iterator.asynchronous)
|
|
403
|
+
await closed;
|
|
404
|
+
}
|
|
405
|
+
catch {
|
|
406
|
+
throw error;
|
|
407
|
+
}
|
|
403
408
|
throw error;
|
|
404
409
|
}
|
|
405
|
-
throw error;
|
|
406
410
|
}
|
|
411
|
+
remaining--;
|
|
412
|
+
await complete();
|
|
413
|
+
}
|
|
414
|
+
finally {
|
|
415
|
+
releaseIterator();
|
|
407
416
|
}
|
|
408
|
-
remaining--;
|
|
409
|
-
await complete();
|
|
410
417
|
}
|
|
411
418
|
catch (error) {
|
|
412
419
|
if (error instanceof SandboxError &&
|
|
@@ -9,6 +9,7 @@ import { getGuestFunctionProperties, getSandboxPrototype, hasExplicitSandboxProt
|
|
|
9
9
|
import { getRegexMember } from "./methods/regex.js";
|
|
10
10
|
import { retainValues } from "./resources.js";
|
|
11
11
|
import { functionString } from "./function-string.js";
|
|
12
|
+
import { boxedValue, isSandboxBox } from "./boxed.js";
|
|
12
13
|
import { isSandboxClosure, isSandboxPromise, isSandboxMap, isSandboxSet, isSandboxRegex, isSandboxGenerator } from "./values.js";
|
|
13
14
|
const defaultStringHook = Symbol("defaultStringHook");
|
|
14
15
|
const defaultValueHook = Symbol("defaultValueHook");
|
|
@@ -40,7 +41,7 @@ async function objectToPrimitive(value, budget, context, joining, hint) {
|
|
|
40
41
|
result = await defaultToString(value, budget, context, joining);
|
|
41
42
|
}
|
|
42
43
|
else if (hook === defaultValueHook) {
|
|
43
|
-
result = isSandboxDate(value) ? dateTime(value) : value;
|
|
44
|
+
result = isSandboxDate(value) ? dateTime(value) : isSandboxBox(value) ? boxedValue(value) : value;
|
|
44
45
|
}
|
|
45
46
|
else {
|
|
46
47
|
if (!isSandboxClosure(hook))
|
|
@@ -97,6 +98,8 @@ function conversionHook(value, name, budget) {
|
|
|
97
98
|
return undefined;
|
|
98
99
|
}
|
|
99
100
|
async function defaultToString(value, budget, context, joining) {
|
|
101
|
+
if (isSandboxBox(value))
|
|
102
|
+
return budget.allocateString(String(boxedValue(value)));
|
|
100
103
|
if (isSandboxClosure(value))
|
|
101
104
|
return budget.allocateString(functionString(value));
|
|
102
105
|
if (isSandboxMap(value))
|
|
@@ -134,6 +134,7 @@ export declare function deepCopyToSandbox(value: unknown): SandboxValue;
|
|
|
134
134
|
export declare function cloneSandboxValue(value: SandboxValue, options?: {
|
|
135
135
|
compilation?: CompileScope;
|
|
136
136
|
resetRegexLastIndex?: boolean;
|
|
137
|
+
structuredClone?: boolean;
|
|
137
138
|
}): SandboxValue;
|
|
138
139
|
export declare function allocateProducedSandboxValue(value: SandboxValue, budget: Budget): SandboxValue;
|
|
139
140
|
export declare function measureSandboxData(values: Iterable<unknown>, options?: {
|
|
@@ -2,6 +2,7 @@ import { bindOtelSpan, getBoundOtelSpan } from "../observability/otel.js";
|
|
|
2
2
|
import { isSandboxMap, isSandboxSet, sandboxMapBrand, sandboxSetBrand } from "./collection-brands.js";
|
|
3
3
|
import { collectionIteratorState, isSandboxCollectionIterator, restoreSandboxCollectionIterator, snapshotCollectionIterator } from "./collection-iterator.js";
|
|
4
4
|
import { copyNativeDate, exportDate, isSandboxDate } from "./date.js";
|
|
5
|
+
import { boxedDataProperties, boxedValue, createSandboxBox, isSandboxBox, nativeBoxedValue } from "./boxed.js";
|
|
5
6
|
import { getHostObjectKeys, getHostObjectMember, hasHostObjectMember, measureHostObjectData, isGuestHostObject, isLiveCapability } from "./host-capabilities.js";
|
|
6
7
|
import { types as nodeTypes } from "node:util";
|
|
7
8
|
import { CompileScope, RegexCompileGuard, regexCompiledData } from "./regex/compile-guard.js";
|
|
@@ -13,7 +14,7 @@ import { invokeCancelableClosure, readPromiseCancellation, registerPromiseCancel
|
|
|
13
14
|
import { parseRegex } from "./regex/parse.js";
|
|
14
15
|
import { assertSandboxDataDepth } from "../graph-depth.js";
|
|
15
16
|
import { sandboxErrorTypes } from "../error/shape.js";
|
|
16
|
-
import { getGuestFunctionProperties, getSandboxPrototype, hasGuestObjectState, hasManagedDescriptors, isGuestClosure, registerGuestClosure } from "./object-model.js";
|
|
17
|
+
import { getGuestFunctionProperties, getSandboxPrototype, hasGuestObjectState, hasManagedDescriptors, isGuestClosure, isIntrinsicConstructor, registerGuestClosure } from "./object-model.js";
|
|
17
18
|
import { copySandboxArgumentProperties, createSandboxArguments, getSandboxArgumentEntries, isSandboxArguments } from "./arguments.js";
|
|
18
19
|
export { createSandboxArguments, isSandboxArguments } from "./arguments.js";
|
|
19
20
|
export { isSandboxMap, isSandboxSet } from "./collection-brands.js";
|
|
@@ -243,6 +244,19 @@ export function measureSandboxData(values, options = {}) {
|
|
|
243
244
|
assertSandboxDataDepth(depth);
|
|
244
245
|
seen.add(value);
|
|
245
246
|
usage += 1;
|
|
247
|
+
if (isSandboxBox(value)) {
|
|
248
|
+
const primitive = boxedValue(value);
|
|
249
|
+
usage += typeof primitive === "string" ? primitive.length : 8;
|
|
250
|
+
const prototype = getSandboxPrototype(value);
|
|
251
|
+
if (prototype !== null)
|
|
252
|
+
visit(prototype, depth + 1);
|
|
253
|
+
for (const [key, descriptor] of boxedDataProperties(value)) {
|
|
254
|
+
usage += key.length + 1;
|
|
255
|
+
if ("value" in descriptor)
|
|
256
|
+
visit(descriptor.value, depth + 1);
|
|
257
|
+
}
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
246
260
|
if (isSandboxDate(value)) {
|
|
247
261
|
usage += 8;
|
|
248
262
|
return;
|
|
@@ -311,8 +325,19 @@ export function measureSandboxData(values, options = {}) {
|
|
|
311
325
|
if (isSandboxClosure(value)) {
|
|
312
326
|
if (options.ignoreClosures)
|
|
313
327
|
return;
|
|
314
|
-
if (value.properties !== undefined)
|
|
315
|
-
|
|
328
|
+
if (value.properties !== undefined) {
|
|
329
|
+
if (isIntrinsicConstructor(value)) {
|
|
330
|
+
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value.properties))) {
|
|
331
|
+
if (key === "prototype" || key === "name" || key === "length")
|
|
332
|
+
continue;
|
|
333
|
+
usage += key.length + 1;
|
|
334
|
+
if ("value" in descriptor)
|
|
335
|
+
visit(descriptor.value, depth + 1);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
else
|
|
339
|
+
visit(value.properties, depth + 1);
|
|
340
|
+
}
|
|
316
341
|
if (!options.ignoreClosureCaptures)
|
|
317
342
|
for (const retained of value[sandboxRetainedValues]?.() ?? [])
|
|
318
343
|
visit(retained, depth + 1);
|
|
@@ -457,6 +482,26 @@ function copyToSandbox(value, state, path = "<root>", cloneSandboxCollections =
|
|
|
457
482
|
if (typeof value === "object" && value !== null && hasGuestObjectState(value)) {
|
|
458
483
|
throw new TypeError("Guest prototype links and custom descriptors cannot be copied as data.");
|
|
459
484
|
}
|
|
485
|
+
const primitive = nativeBoxedValue(value);
|
|
486
|
+
if (primitive !== undefined) {
|
|
487
|
+
const original = value;
|
|
488
|
+
const existing = state.seen.get(original);
|
|
489
|
+
if (existing !== undefined)
|
|
490
|
+
return existing;
|
|
491
|
+
const copy = createSandboxBox(primitive);
|
|
492
|
+
state.seen.set(original, copy);
|
|
493
|
+
if (!state.structuredClone) {
|
|
494
|
+
for (const [key, descriptor] of boxedDataProperties(original)) {
|
|
495
|
+
if (!("value" in descriptor))
|
|
496
|
+
throw new TypeError("Boxed data cannot contain accessors.");
|
|
497
|
+
Object.defineProperty(copy, key, {
|
|
498
|
+
...descriptor,
|
|
499
|
+
value: copyToSandbox(descriptor.value, state, joinPath(path, key), cloneSandboxCollections, depth + 1)
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return copy;
|
|
504
|
+
}
|
|
460
505
|
if (isSandboxMap(value)) {
|
|
461
506
|
if (!cloneSandboxCollections)
|
|
462
507
|
return value;
|
|
@@ -598,6 +643,22 @@ function copyFromSandbox(value, state, path = "<root>", options, depth = 0) {
|
|
|
598
643
|
if (!isSandboxClosure(value) && hasGuestObjectState(value)) {
|
|
599
644
|
throw new TypeError("Guest prototype links and custom descriptors cannot be copied as data.");
|
|
600
645
|
}
|
|
646
|
+
if (isSandboxBox(value)) {
|
|
647
|
+
const existing = state.seen.get(value);
|
|
648
|
+
if (existing !== undefined)
|
|
649
|
+
return existing;
|
|
650
|
+
const copy = Object(boxedValue(value));
|
|
651
|
+
state.seen.set(value, copy);
|
|
652
|
+
for (const [key, descriptor] of boxedDataProperties(value)) {
|
|
653
|
+
if (!("value" in descriptor))
|
|
654
|
+
throw new TypeError("Boxed data cannot contain accessors.");
|
|
655
|
+
Object.defineProperty(copy, key, {
|
|
656
|
+
...descriptor,
|
|
657
|
+
value: copyFromSandbox(descriptor.value, state, joinPath(path, key), options, depth + 1)
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
return copy;
|
|
661
|
+
}
|
|
601
662
|
const regexBrand = Object.getOwnPropertyDescriptor(value, sandboxRegexBrand);
|
|
602
663
|
if (regexBrand !== undefined) {
|
|
603
664
|
if (!("value" in regexBrand) || regexBrand.value !== true) {
|
|
@@ -759,6 +820,20 @@ function allocateSandboxValue(value, budget, seen) {
|
|
|
759
820
|
budget.allocateString(value);
|
|
760
821
|
return;
|
|
761
822
|
}
|
|
823
|
+
if (isSandboxBox(value)) {
|
|
824
|
+
if (seen.has(value))
|
|
825
|
+
return;
|
|
826
|
+
seen.add(value);
|
|
827
|
+
const primitive = boxedValue(value);
|
|
828
|
+
if (typeof primitive === "string")
|
|
829
|
+
budget.allocateString(primitive);
|
|
830
|
+
for (const [key, descriptor] of boxedDataProperties(value)) {
|
|
831
|
+
budget.allocateString(key);
|
|
832
|
+
if ("value" in descriptor)
|
|
833
|
+
allocateSandboxValue(descriptor.value, budget, seen);
|
|
834
|
+
}
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
762
837
|
if (isFloat32Array(value)) {
|
|
763
838
|
if (seen.has(value))
|
|
764
839
|
return;
|
|
@@ -13,14 +13,14 @@ export declare function inspectSnapshotMigration(snapshot: SafeJSSnapshot, optio
|
|
|
13
13
|
checkpointDigest: string;
|
|
14
14
|
sourceHash: string;
|
|
15
15
|
executionSemantics: string;
|
|
16
|
-
calls: (Omit<import("./index.js").HostCallRecord, "
|
|
16
|
+
calls: (Omit<import("./index.js").HostCallRecord, "asynchronous" | "outcome"> & {
|
|
17
17
|
asynchronous: boolean;
|
|
18
18
|
outcome?: {
|
|
19
19
|
status: "fulfilled" | "rejected";
|
|
20
20
|
data: import("./snapshot/replay-data.js").ReplayData;
|
|
21
21
|
};
|
|
22
22
|
})[];
|
|
23
|
-
unresolvedCalls: (Omit<import("./index.js").HostCallRecord, "
|
|
23
|
+
unresolvedCalls: (Omit<import("./index.js").HostCallRecord, "asynchronous" | "outcome"> & {
|
|
24
24
|
asynchronous: boolean;
|
|
25
25
|
outcome?: {
|
|
26
26
|
status: "fulfilled" | "rejected";
|
|
@@ -13,14 +13,14 @@ export declare function migrateSnapshotFile(options: SnapshotMigrationFileOption
|
|
|
13
13
|
checkpointDigest: string;
|
|
14
14
|
sourceHash: string;
|
|
15
15
|
executionSemantics: string;
|
|
16
|
-
calls: (Omit<import("./index.js").HostCallRecord, "
|
|
16
|
+
calls: (Omit<import("./index.js").HostCallRecord, "asynchronous" | "outcome"> & {
|
|
17
17
|
asynchronous: boolean;
|
|
18
18
|
outcome?: {
|
|
19
19
|
status: "fulfilled" | "rejected";
|
|
20
20
|
data: import("./snapshot/replay-data.js").ReplayData;
|
|
21
21
|
};
|
|
22
22
|
})[];
|
|
23
|
-
unresolvedCalls: (Omit<import("./index.js").HostCallRecord, "
|
|
23
|
+
unresolvedCalls: (Omit<import("./index.js").HostCallRecord, "asynchronous" | "outcome"> & {
|
|
24
24
|
asynchronous: boolean;
|
|
25
25
|
outcome?: {
|
|
26
26
|
status: "fulfilled" | "rejected";
|
|
@@ -35,14 +35,14 @@ export declare function migrateSnapshotFile(options: SnapshotMigrationFileOption
|
|
|
35
35
|
checkpointDigest: string;
|
|
36
36
|
sourceHash: string;
|
|
37
37
|
executionSemantics: string;
|
|
38
|
-
calls: (Omit<import("./index.js").HostCallRecord, "
|
|
38
|
+
calls: (Omit<import("./index.js").HostCallRecord, "asynchronous" | "outcome"> & {
|
|
39
39
|
asynchronous: boolean;
|
|
40
40
|
outcome?: {
|
|
41
41
|
status: "fulfilled" | "rejected";
|
|
42
42
|
data: import("./snapshot/replay-data.js").ReplayData;
|
|
43
43
|
};
|
|
44
44
|
})[];
|
|
45
|
-
unresolvedCalls: (Omit<import("./index.js").HostCallRecord, "
|
|
45
|
+
unresolvedCalls: (Omit<import("./index.js").HostCallRecord, "asynchronous" | "outcome"> & {
|
|
46
46
|
asynchronous: boolean;
|
|
47
47
|
outcome?: {
|
|
48
48
|
status: "fulfilled" | "rejected";
|
|
@@ -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
|
+
}
|
|
@@ -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 (
|
|
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 =
|
|
204
|
-
?
|
|
205
|
-
:
|
|
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
|
}
|
|
@@ -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,6 +4,8 @@ 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";
|
|
@@ -349,6 +351,12 @@ function restoreHeapValue(id, state) {
|
|
|
349
351
|
if (serialized === undefined) {
|
|
350
352
|
throw new Error(`Snapshot references unknown heap value ${id}.`);
|
|
351
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
|
+
}
|
|
352
360
|
if (serialized.kind === "date") {
|
|
353
361
|
const value = restoreDateTime(serialized.time);
|
|
354
362
|
state.heapValueById.set(id, value);
|
|
@@ -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 = {
|