poe-code 14.0.6 → 14.0.8
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 +944 -143
- 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-2M6MNQBY.js → chunk-MO3YD4ID.js} +888 -133
- package/packages/safe-js/dist/chunks/chunk-MO3YD4ID.js.map +7 -0
- package/packages/safe-js/dist/chunks/{chunk-QSSQJNKE.js → chunk-RXLDJ6LQ.js} +2 -2
- package/packages/safe-js/dist/cli.js +2 -2
- package/packages/safe-js/dist/core.d.ts +1 -1
- package/packages/safe-js/dist/core.js +1 -1
- package/packages/safe-js/dist/extensions.d.ts +2 -0
- package/packages/safe-js/dist/index.d.ts +1 -1
- package/packages/safe-js/dist/index.js +2 -2
- package/packages/safe-js/dist/index.js.map +1 -1
- package/packages/safe-js/dist/interp/async.js +1 -0
- package/packages/safe-js/dist/interp/date.d.ts +19 -0
- package/packages/safe-js/dist/interp/date.js +129 -0
- package/packages/safe-js/dist/interp/globals/console-json.js +5 -1
- package/packages/safe-js/dist/interp/globals/date.d.ts +10 -0
- package/packages/safe-js/dist/interp/globals/date.js +95 -0
- package/packages/safe-js/dist/interp/globals/object-array.d.ts +4 -3
- package/packages/safe-js/dist/interp/globals/object-array.js +42 -5
- package/packages/safe-js/dist/interp/globals/object.d.ts +4 -0
- package/packages/safe-js/dist/interp/globals/object.js +153 -0
- package/packages/safe-js/dist/interp/globals.d.ts +4 -1
- package/packages/safe-js/dist/interp/globals.js +2 -0
- package/packages/safe-js/dist/interp/host-bridge.d.ts +4 -0
- package/packages/safe-js/dist/interp/host-bridge.js +21 -2
- package/packages/safe-js/dist/interp/host-call.js +4 -0
- package/packages/safe-js/dist/interp/host-capabilities.d.ts +28 -1
- package/packages/safe-js/dist/interp/host-capabilities.js +220 -5
- package/packages/safe-js/dist/interp/interpreter.js +46 -11
- package/packages/safe-js/dist/interp/iteration.js +3 -0
- package/packages/safe-js/dist/interp/object-model.d.ts +3 -1
- package/packages/safe-js/dist/interp/object-model.js +39 -9
- package/packages/safe-js/dist/interp/string-coercion.js +3 -0
- package/packages/safe-js/dist/interp/values.d.ts +3 -1
- package/packages/safe-js/dist/interp/values.js +34 -6
- package/packages/safe-js/dist/lint/rules/known-globals.d.ts +1 -1
- package/packages/safe-js/dist/lint/rules/known-globals.js +1 -0
- package/packages/safe-js/dist/parse/parser.js +4 -2
- package/packages/safe-js/dist/realm.d.ts +4 -1
- package/packages/safe-js/dist/realm.js +89 -10
- package/packages/safe-js/dist/run.d.ts +2 -0
- package/packages/safe-js/dist/run.js +3 -1
- package/packages/safe-js/dist/snapshot/dump-format.js +8 -3
- package/packages/safe-js/dist/snapshot/replay-data.d.ts +4 -1
- package/packages/safe-js/dist/snapshot/replay-data.js +11 -0
- package/packages/safe-js/dist/snapshot/restore.js +6 -0
- package/packages/safe-js/dist/snapshot/serialize.d.ts +5 -2
- package/packages/safe-js/dist/snapshot/serialize.js +8 -2
- package/packages/safe-js/dist/snapshot/validation.js +18 -1
- package/packages/safe-js/dist/chunks/chunk-2M6MNQBY.js.map +0 -7
- /package/packages/safe-js/dist/chunks/{chunk-QSSQJNKE.js.map → chunk-RXLDJ6LQ.js.map} +0 -0
|
@@ -3,6 +3,7 @@ import { types } from "node:util";
|
|
|
3
3
|
import { Budget, SandboxError } from "./interp/budget.js";
|
|
4
4
|
import { CompileScope } from "./interp/regex/compile-guard.js";
|
|
5
5
|
import { createBuiltinBindings } from "./interp/globals.js";
|
|
6
|
+
import { releaseObjectPrototype } from "./interp/object-model.js";
|
|
6
7
|
import { interpret, Scope } from "./interp/interpreter.js";
|
|
7
8
|
import { SandboxJobQueue, runAsyncPrefix, suspendJob } from "./interp/jobs.js";
|
|
8
9
|
import { withCancellationSignal, awaitSandboxValue } from "./interp/cancel.js";
|
|
@@ -10,7 +11,7 @@ import { enterRunningState } from "./interp/running-state.js";
|
|
|
10
11
|
import { runResources } from "./interp/resources.js";
|
|
11
12
|
import { createSandboxPromiseRejectionTracker, withSandboxPromiseRejectionTracker } from "./interp/promise-tracker.js";
|
|
12
13
|
import { copyHostValueToSandbox, wrapCallerInjectedBindings } from "./interp/host-bridge.js";
|
|
13
|
-
import { createLiveHostObject, exportHostCapability, readGuestCallback, registerGuestCallback, revokeGuestCallback, revokeHostObject } from "./interp/host-capabilities.js";
|
|
14
|
+
import { createLiveHostObject, createGuestReference, exportHostCapability, readGuestReference, readGuestCallback, registerGuestCallback, revokeGuestCallback, revokeHostObject, revokeGuestReference } from "./interp/host-capabilities.js";
|
|
14
15
|
import { deepCopyFromSandbox, isSandboxClosure, measureSandboxData, reconcileCompiledValues } from "./interp/values.js";
|
|
15
16
|
import { getExtensionSetup, readDataRecord, readStringList } from "./extensions.js";
|
|
16
17
|
import { resolveModuleImports } from "./modules/registry.js";
|
|
@@ -36,6 +37,8 @@ class RealmState {
|
|
|
36
37
|
pendingCallbacks = new Set();
|
|
37
38
|
callbackCache = new WeakMap();
|
|
38
39
|
hostObjects = new Set();
|
|
40
|
+
guestReferences = new Map();
|
|
41
|
+
retainedOperations = new WeakMap();
|
|
39
42
|
nestedOperations = new WeakMap();
|
|
40
43
|
convertedModules = new Map();
|
|
41
44
|
nativeConversions = { seen: new WeakMap() };
|
|
@@ -56,6 +59,7 @@ class RealmState {
|
|
|
56
59
|
extensions: 32,
|
|
57
60
|
hostObjects: 1024,
|
|
58
61
|
callbacks: 1024,
|
|
62
|
+
guestReferences: 1024,
|
|
59
63
|
cleanups: 1024,
|
|
60
64
|
nestedEvaluations: 16
|
|
61
65
|
};
|
|
@@ -94,6 +98,7 @@ class RealmState {
|
|
|
94
98
|
owner: this,
|
|
95
99
|
assertActive: this.assertOpen,
|
|
96
100
|
wrapCallback: this.wrapCallback,
|
|
101
|
+
captureArguments: this.captureArguments,
|
|
97
102
|
invoke: this.invokeHost,
|
|
98
103
|
awaitResult: (operation) => this.nestedOperations.has(operation)
|
|
99
104
|
};
|
|
@@ -102,6 +107,7 @@ class RealmState {
|
|
|
102
107
|
budget: this.budget,
|
|
103
108
|
compileOwner: this.lease.owner,
|
|
104
109
|
sink: options.sink,
|
|
110
|
+
clock: options.clock,
|
|
105
111
|
random: createReplayableRandom({ seed: options.randomSeed }).next
|
|
106
112
|
});
|
|
107
113
|
const names = new Set();
|
|
@@ -134,10 +140,11 @@ class RealmState {
|
|
|
134
140
|
options.signal?.addEventListener("abort", this.abort, { once: true });
|
|
135
141
|
if (options.signal?.aborted)
|
|
136
142
|
this.abort();
|
|
137
|
-
this.budget.setRetainedValues(this, this.
|
|
143
|
+
this.budget.setRetainedValues(this, this.retainedRoots);
|
|
138
144
|
this.tracker.onFatalRejection((error) => this.poison(error));
|
|
139
145
|
}
|
|
140
146
|
catch (error) {
|
|
147
|
+
releaseObjectPrototype(this.budget);
|
|
141
148
|
this.compilation.dispose();
|
|
142
149
|
this.lease.release();
|
|
143
150
|
throw error;
|
|
@@ -149,10 +156,48 @@ class RealmState {
|
|
|
149
156
|
signal: this.controller.signal,
|
|
150
157
|
realm: this.bridge
|
|
151
158
|
});
|
|
152
|
-
|
|
159
|
+
retainedRoots = () => [
|
|
153
160
|
...this.callbacks.values(),
|
|
154
|
-
...Array.from(this.pendingCallbacks, (pending) => pending.closure)
|
|
161
|
+
...Array.from(this.pendingCallbacks, (pending) => pending.closure),
|
|
162
|
+
...this.guestReferences.values()
|
|
155
163
|
];
|
|
164
|
+
captureArguments = (operation, args, copy) => {
|
|
165
|
+
const from = this.retainedOperations.get(operation)?.from ?? args.length;
|
|
166
|
+
const values = copy(args.slice(0, from));
|
|
167
|
+
const captured = [];
|
|
168
|
+
const rollback = () => {
|
|
169
|
+
for (const reference of captured) {
|
|
170
|
+
revokeGuestReference(reference, this);
|
|
171
|
+
this.guestReferences.delete(reference);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
try {
|
|
175
|
+
for (const value of args.slice(from)) {
|
|
176
|
+
this.checkCollection(this.guestReferences.size + 1, this.limits.guestReferences, "guest reference");
|
|
177
|
+
const root = [value];
|
|
178
|
+
const reference = createGuestReference(root, this, this.assertOpen);
|
|
179
|
+
this.guestReferences.set(reference, root);
|
|
180
|
+
captured.push(reference);
|
|
181
|
+
values.push(reference);
|
|
182
|
+
}
|
|
183
|
+
if (captured.length > 0)
|
|
184
|
+
this.budget.reconcileDataUsage(measureSandboxData([...(this.scope?.retainedValues() ?? []), ...this.retainedRoots()]));
|
|
185
|
+
return { args: values, rollback };
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
rollback();
|
|
189
|
+
if (error instanceof SandboxError)
|
|
190
|
+
this.poison(error);
|
|
191
|
+
throw error;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
releaseGuestReference = (reference) => {
|
|
195
|
+
readGuestReference(reference, this);
|
|
196
|
+
revokeGuestReference(reference, this);
|
|
197
|
+
this.guestReferences.delete(reference);
|
|
198
|
+
if (this.active === undefined)
|
|
199
|
+
reconcileCompiledValues(this.budget, [...(this.scope?.retainedValues() ?? []), ...this.retainedRoots()], this.compilation);
|
|
200
|
+
};
|
|
156
201
|
assertOpen = () => {
|
|
157
202
|
if (this.failure !== undefined)
|
|
158
203
|
throw this.failure.reason;
|
|
@@ -267,13 +312,23 @@ class RealmState {
|
|
|
267
312
|
owner: this,
|
|
268
313
|
assertActive: this.assertOpen,
|
|
269
314
|
chargeWork: this.chargeWork,
|
|
270
|
-
|
|
315
|
+
checkLength: (length) => this.budget.allocateArrayLength(length),
|
|
316
|
+
checkTemporaryDataSize: (size) => {
|
|
317
|
+
const temporary = {};
|
|
318
|
+
try {
|
|
319
|
+
this.budget.setRetainedDataUsage(temporary, size);
|
|
320
|
+
}
|
|
321
|
+
finally {
|
|
322
|
+
this.budget.setRetainedDataUsage(temporary, 0);
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
read: (operation, validate) => {
|
|
271
326
|
const value = this.invokeHost(operation, operation);
|
|
272
327
|
if (types.isPromise(value)) {
|
|
273
328
|
void Promise.resolve(value).catch(() => undefined);
|
|
274
329
|
throw new TypeError("Live property getters must be synchronous.");
|
|
275
330
|
}
|
|
276
|
-
return this.importValue(value);
|
|
331
|
+
return this.importValue(validate === undefined ? value : validate(value));
|
|
277
332
|
},
|
|
278
333
|
write: (operation, value) => {
|
|
279
334
|
const result = this.invokeHost(operation, () => operation(this.exportValue(value)));
|
|
@@ -321,7 +376,7 @@ class RealmState {
|
|
|
321
376
|
}
|
|
322
377
|
});
|
|
323
378
|
try {
|
|
324
|
-
this.budget.reconcileDataUsage(measureSandboxData([...(this.scope?.retainedValues() ?? []), ...this.
|
|
379
|
+
this.budget.reconcileDataUsage(measureSandboxData([...(this.scope?.retainedValues() ?? []), ...this.retainedRoots()]));
|
|
325
380
|
}
|
|
326
381
|
catch (error) {
|
|
327
382
|
this.poison(error);
|
|
@@ -334,7 +389,7 @@ class RealmState {
|
|
|
334
389
|
revokeGuestCallback(callback, this);
|
|
335
390
|
this.callbacks.delete(callback);
|
|
336
391
|
if (this.active === undefined)
|
|
337
|
-
reconcileCompiledValues(this.budget, [...(this.scope?.retainedValues() ?? []), ...this.
|
|
392
|
+
reconcileCompiledValues(this.budget, [...(this.scope?.retainedValues() ?? []), ...this.retainedRoots()], this.compilation);
|
|
338
393
|
};
|
|
339
394
|
invokeCallback = async (callback, options = {}) => {
|
|
340
395
|
if (this.closed || this.failure !== undefined)
|
|
@@ -384,7 +439,7 @@ class RealmState {
|
|
|
384
439
|
finally {
|
|
385
440
|
this.pendingCallbacks.delete(record);
|
|
386
441
|
if (!this.closed && this.active === undefined)
|
|
387
|
-
reconcileCompiledValues(this.budget, [...(this.scope?.retainedValues() ?? []), ...this.
|
|
442
|
+
reconcileCompiledValues(this.budget, [...(this.scope?.retainedValues() ?? []), ...this.retainedRoots()], this.compilation);
|
|
388
443
|
}
|
|
389
444
|
};
|
|
390
445
|
initialize() {
|
|
@@ -400,6 +455,24 @@ class RealmState {
|
|
|
400
455
|
createHostObject: this.createHostObject,
|
|
401
456
|
invokeCallback: this.invokeCallback,
|
|
402
457
|
releaseCallback: this.releaseCallback,
|
|
458
|
+
releaseGuestReference: this.releaseGuestReference,
|
|
459
|
+
retainGuestArguments: (operation, from) => {
|
|
460
|
+
this.assertOpen();
|
|
461
|
+
if (!extension.manifest.capabilities?.includes("guest:retain"))
|
|
462
|
+
throw new TypeError("Retaining arguments requires the guest:retain grant.");
|
|
463
|
+
if (typeof operation !== "function")
|
|
464
|
+
throw new TypeError("Retained operation must be a function.");
|
|
465
|
+
if (!Number.isSafeInteger(from) || from < 0)
|
|
466
|
+
throw new TypeError("Argument index must be a non-negative safe integer.");
|
|
467
|
+
if (this.scope !== undefined)
|
|
468
|
+
throw new TypeError("Retained operations must be registered during setup.");
|
|
469
|
+
const previous = this.retainedOperations.get(operation);
|
|
470
|
+
if (previous !== undefined &&
|
|
471
|
+
(previous.extension !== extension || previous.from !== from))
|
|
472
|
+
throw new TypeError("Conflicting retained operation declaration.");
|
|
473
|
+
this.retainedOperations.set(operation, { extension, from });
|
|
474
|
+
return operation;
|
|
475
|
+
},
|
|
403
476
|
nestedOperation: (operation) => {
|
|
404
477
|
this.assertOpen();
|
|
405
478
|
if (!extension.manifest.capabilities?.includes("source:nested"))
|
|
@@ -552,7 +625,7 @@ class RealmState {
|
|
|
552
625
|
if (this.failure !== undefined)
|
|
553
626
|
throw this.failure.reason;
|
|
554
627
|
if (!this.closed)
|
|
555
|
-
reconcileCompiledValues(this.budget, [...(this.scope?.retainedValues() ?? []), ...this.
|
|
628
|
+
reconcileCompiledValues(this.budget, [...(this.scope?.retainedValues() ?? []), ...this.retainedRoots()], this.compilation);
|
|
556
629
|
return result;
|
|
557
630
|
}
|
|
558
631
|
catch (error) {
|
|
@@ -591,7 +664,11 @@ class RealmState {
|
|
|
591
664
|
for (const object of this.hostObjects)
|
|
592
665
|
revokeHostObject(object, this);
|
|
593
666
|
this.hostObjects.clear();
|
|
667
|
+
for (const reference of this.guestReferences.keys())
|
|
668
|
+
revokeGuestReference(reference, this);
|
|
669
|
+
this.guestReferences.clear();
|
|
594
670
|
this.budget.setRetainedValues(this, undefined);
|
|
671
|
+
releaseObjectPrototype(this.budget);
|
|
595
672
|
this.disposal = (async () => {
|
|
596
673
|
const errors = [];
|
|
597
674
|
for (const cleanup of this.cleanups.splice(0).reverse()) {
|
|
@@ -651,6 +728,7 @@ export function createRealm(options = {}) {
|
|
|
651
728
|
evaluate: state.evaluate,
|
|
652
729
|
invokeCallback: state.invokeCallback,
|
|
653
730
|
releaseCallback: state.releaseCallback,
|
|
731
|
+
releaseGuestReference: state.releaseGuestReference,
|
|
654
732
|
close: state.close
|
|
655
733
|
});
|
|
656
734
|
}
|
|
@@ -702,6 +780,7 @@ function readRealmOptions(value, oneShot = false) {
|
|
|
702
780
|
"signal",
|
|
703
781
|
"sink",
|
|
704
782
|
"randomSeed",
|
|
783
|
+
"clock",
|
|
705
784
|
"limits"
|
|
706
785
|
]);
|
|
707
786
|
for (const [key, entry] of Object.entries(options)) {
|
|
@@ -14,6 +14,7 @@ import { enterSnapshotRun } from "./interp/running-state.js";
|
|
|
14
14
|
import { createSandboxPromiseRejectionTracker, observeSandboxPromise, withSandboxPromiseRejectionTracker } from "./interp/promise-tracker.js";
|
|
15
15
|
import { HostCallJournal } from "./interp/host-call.js";
|
|
16
16
|
import { createBuiltinBindings } from "./interp/globals.js";
|
|
17
|
+
import { releaseObjectPrototype } from "./interp/object-model.js";
|
|
17
18
|
import { declareHostOperation, wrapCallerInjectedBindings } from "./interp/host-bridge.js";
|
|
18
19
|
import { interpret, Scope } from "./interp/interpreter.js";
|
|
19
20
|
import { consumeSettledHostCall } from "./interp/promise.js";
|
|
@@ -109,7 +110,7 @@ export function run(source, options = {}) {
|
|
|
109
110
|
signal: options.signal,
|
|
110
111
|
lifecycle
|
|
111
112
|
}));
|
|
112
|
-
const builtinBindings = createBuiltinBindings({ compileOwner: operation.owner, budget, hostCalls, sink: options.sink, random: random?.generator.next });
|
|
113
|
+
const builtinBindings = createBuiltinBindings({ compileOwner: operation.owner, budget, hostCalls, sink: options.sink, random: random?.generator.next, clock: options.clock });
|
|
113
114
|
const importMeta = convertInitialInput(() => deepCopyToSandbox(options.importMeta ?? {}));
|
|
114
115
|
if (restoredSnapshot?.migration !== undefined) {
|
|
115
116
|
if (Object.hasOwn(importMeta, "migration"))
|
|
@@ -349,6 +350,7 @@ export function run(source, options = {}) {
|
|
|
349
350
|
});
|
|
350
351
|
}
|
|
351
352
|
finally {
|
|
353
|
+
releaseObjectPrototype(budget);
|
|
352
354
|
compilation.dispose();
|
|
353
355
|
operation.release();
|
|
354
356
|
}
|
|
@@ -8,6 +8,7 @@ import { serializeArguments } from "./arguments.js";
|
|
|
8
8
|
import { requiresArrayEntries, serializeArray } from "./arrays.js";
|
|
9
9
|
import { float32DataProperties, isFloat32Array } from "../interp/float32.js";
|
|
10
10
|
import { encodeFloat32Storage } from "./float32array.js";
|
|
11
|
+
import { isSandboxDate, serializedDateTime } from "../interp/date.js";
|
|
11
12
|
const SKIP_VALUE = Symbol("SafeJS.skip-dump-value");
|
|
12
13
|
export function serializeSafeJSSnapshot(snapshot) {
|
|
13
14
|
const replayError = Object.getOwnPropertyDescriptor(snapshot, "replayError");
|
|
@@ -72,7 +73,7 @@ function serializeDumpValue(value, path, state) {
|
|
|
72
73
|
if (hasGuestObjectState(value)) {
|
|
73
74
|
throw new TypeError("Guest function properties and prototype links cannot be serialized.");
|
|
74
75
|
}
|
|
75
|
-
if (isFloat32Array(value))
|
|
76
|
+
if (isSandboxDate(value) || isFloat32Array(value))
|
|
76
77
|
return serializeHeapReference(value, path, state);
|
|
77
78
|
if (Array.isArray(value)) {
|
|
78
79
|
const reference = serializeHeapReference(value, path, state);
|
|
@@ -97,7 +98,10 @@ function serializeHeapReference(value, path, state) {
|
|
|
97
98
|
}
|
|
98
99
|
if (!state.serializedHeapIds.has(id)) {
|
|
99
100
|
state.serializedHeapIds.add(id);
|
|
100
|
-
if (
|
|
101
|
+
if (isSandboxDate(value)) {
|
|
102
|
+
state.heap[String(id)] = { kind: "date", time: serializedDateTime(value) };
|
|
103
|
+
}
|
|
104
|
+
else if (isFloat32Array(value)) {
|
|
101
105
|
const storage = encodeFloat32Storage(value, id, state.float32Buffers, (id) => ({
|
|
102
106
|
kind: "ref",
|
|
103
107
|
id
|
|
@@ -159,6 +163,7 @@ function indexHeapContainers(snapshot) {
|
|
|
159
163
|
for (const [value, stat] of stats.entries()) {
|
|
160
164
|
if (stat.count > 1 ||
|
|
161
165
|
stat.cyclic ||
|
|
166
|
+
isSandboxDate(value) ||
|
|
162
167
|
isFloat32Array(value) ||
|
|
163
168
|
(Array.isArray(value) && requiresArrayEntries(value)) ||
|
|
164
169
|
isSandboxArguments(value) ||
|
|
@@ -173,7 +178,7 @@ function collectContainerStats(value, stats, ancestors) {
|
|
|
173
178
|
if (value === null || typeof value !== "object") {
|
|
174
179
|
return;
|
|
175
180
|
}
|
|
176
|
-
if (!Array.isArray(value) && !isPlainObject(value) && !isFloat32Array(value)) {
|
|
181
|
+
if (!Array.isArray(value) && !isPlainObject(value) && !isFloat32Array(value) && !isSandboxDate(value)) {
|
|
177
182
|
return;
|
|
178
183
|
}
|
|
179
184
|
let stat = stats.get(value);
|
|
@@ -24,7 +24,10 @@ type Properties = Record<string, {
|
|
|
24
24
|
enumerable: boolean;
|
|
25
25
|
writable: boolean;
|
|
26
26
|
}>;
|
|
27
|
-
type DataNode =
|
|
27
|
+
type DataNode = {
|
|
28
|
+
kind: "date";
|
|
29
|
+
time: number | null;
|
|
30
|
+
} | (Float32Data<Atom> & {
|
|
28
31
|
properties: Properties;
|
|
29
32
|
extensible: boolean;
|
|
30
33
|
}) | {
|
|
@@ -3,6 +3,7 @@ import { hasGuestObjectState } from "../interp/object-model.js";
|
|
|
3
3
|
import { CompileScope } from "../interp/regex/compile-guard.js";
|
|
4
4
|
import { float32DataProperties, isFloat32Array } from "../interp/float32.js";
|
|
5
5
|
import { decodeFloat32Storage, encodeFloat32Storage } from "./float32array.js";
|
|
6
|
+
import { isSandboxDate, restoreDateTime, serializedDateTime } from "../interp/date.js";
|
|
6
7
|
import { sandboxErrorNames, sandboxErrorTypes } from "../error/shape.js";
|
|
7
8
|
import { createSandboxArguments, createSandboxClosure, createSandboxMap, createSandboxRegex, createSandboxSet, isSandboxArguments, isSandboxClosure, isSandboxGenerator, isSandboxMap, isSandboxPromise, isSandboxRegex, isSandboxSet } from "../interp/values.js";
|
|
8
9
|
import { serializeArguments } from "./arguments.js";
|
|
@@ -63,6 +64,9 @@ export function encodeReplayData(value, options = {}) {
|
|
|
63
64
|
properties: child(entry.properties, "properties")
|
|
64
65
|
};
|
|
65
66
|
}
|
|
67
|
+
else if (isSandboxDate(entry)) {
|
|
68
|
+
nodes[id] = { kind: "date", time: serializedDateTime(entry) };
|
|
69
|
+
}
|
|
66
70
|
else if (isFloat32Array(entry)) {
|
|
67
71
|
const storage = encodeFloat32Storage(entry, id, float32Buffers, (id) => ({
|
|
68
72
|
tag: "ref",
|
|
@@ -231,6 +235,13 @@ export function decodeReplayData(input, options = {}, parent) {
|
|
|
231
235
|
options.onCapabilityRestored?.(capability, copy);
|
|
232
236
|
return copy;
|
|
233
237
|
}
|
|
238
|
+
if (kind === "date") {
|
|
239
|
+
if (Object.keys(node).length !== 2)
|
|
240
|
+
throw new TypeError("Invalid serialized Date fields.");
|
|
241
|
+
const result = restoreDateTime(own(node, "time"));
|
|
242
|
+
restored.set(id, result);
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
234
245
|
if (kind === "float32array") {
|
|
235
246
|
if (typeof node.extensible !== "boolean")
|
|
236
247
|
throw new TypeError("Invalid Float32Array extensibility.");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Budget, SandboxError } from "../interp/budget.js";
|
|
2
2
|
import { CompileScope } from "../interp/regex/compile-guard.js";
|
|
3
3
|
import { decodeFloat32Storage } from "./float32array.js";
|
|
4
|
+
import { restoreDateTime } from "../interp/date.js";
|
|
4
5
|
import { sandboxErrorTypes } from "../error/shape.js";
|
|
5
6
|
import { SnapshotMismatchError } from "../restore.js";
|
|
6
7
|
import { Scope, setSandboxProperty } from "../interp/interpreter.js";
|
|
@@ -339,6 +340,11 @@ function restoreHeapValue(id, state) {
|
|
|
339
340
|
if (serialized === undefined) {
|
|
340
341
|
throw new Error(`Snapshot references unknown heap value ${id}.`);
|
|
341
342
|
}
|
|
343
|
+
if (serialized.kind === "date") {
|
|
344
|
+
const value = restoreDateTime(serialized.time);
|
|
345
|
+
state.heapValueById.set(id, value);
|
|
346
|
+
return value;
|
|
347
|
+
}
|
|
342
348
|
if (serialized.kind === "float32array") {
|
|
343
349
|
const value = decodeFloat32Storage(serialized, (reference) => deserializeValue(reference, state));
|
|
344
350
|
state.heapValueById.set(id, value);
|
|
@@ -40,7 +40,10 @@ export type SerializedReferenceValue = {
|
|
|
40
40
|
kind: "ref";
|
|
41
41
|
id: number;
|
|
42
42
|
};
|
|
43
|
-
export type SerializedHeapValue =
|
|
43
|
+
export type SerializedHeapValue = {
|
|
44
|
+
kind: "date";
|
|
45
|
+
time: number | null;
|
|
46
|
+
} | (Float32Data<SerializedReferenceValue> & {
|
|
44
47
|
entries: Record<string, SerializedSnapshotValue>;
|
|
45
48
|
}) | SerializedArguments<SerializedSnapshotValue> | SerializedArray<SerializedSnapshotValue> | {
|
|
46
49
|
kind: "object";
|
|
@@ -72,7 +75,7 @@ export type RuntimePromiseValue = {
|
|
|
72
75
|
id: SnapshotId;
|
|
73
76
|
promise?: Promise<unknown>;
|
|
74
77
|
};
|
|
75
|
-
export type RuntimeSnapshotValue = Float32Array | boolean | null | number | string | undefined | RuntimeClosureValue | SandboxGenerator | RuntimePromiseValue | SandboxMap | SandboxRegex | SandboxSet | RuntimeSnapshotValue[] | {
|
|
78
|
+
export type RuntimeSnapshotValue = Date | Float32Array | boolean | null | number | string | undefined | RuntimeClosureValue | SandboxGenerator | RuntimePromiseValue | SandboxMap | SandboxRegex | SandboxSet | RuntimeSnapshotValue[] | {
|
|
76
79
|
[key: string]: RuntimeSnapshotValue;
|
|
77
80
|
};
|
|
78
81
|
export type RuntimeScopeFrame = {
|
|
@@ -6,6 +6,7 @@ import { serializeArguments } from "./arguments.js";
|
|
|
6
6
|
import { requiresArrayEntries, serializeArray } from "./arrays.js";
|
|
7
7
|
import { float32DataProperties, isFloat32Array } from "../interp/float32.js";
|
|
8
8
|
import { encodeFloat32Storage } from "./float32array.js";
|
|
9
|
+
import { isSandboxDate, serializedDateTime } from "../interp/date.js";
|
|
9
10
|
import { isSandboxArguments, isSandboxGenerator, isSandboxMap, isSandboxRegex, isSandboxSet } from "../interp/values.js";
|
|
10
11
|
export class UnsnapshotableValueError extends Error {
|
|
11
12
|
path;
|
|
@@ -161,7 +162,7 @@ function serializeValue(value, path, state) {
|
|
|
161
162
|
if (isSandboxRegex(value)) {
|
|
162
163
|
return { kind: "regex", source: value.source, flags: value.flags, lastIndex: value.lastIndex };
|
|
163
164
|
}
|
|
164
|
-
if (isSandboxMap(value) || isSandboxSet(value) || isFloat32Array(value)) {
|
|
165
|
+
if (isSandboxDate(value) || isSandboxMap(value) || isSandboxSet(value) || isFloat32Array(value)) {
|
|
165
166
|
const reference = serializeHeapReference(value, path, state);
|
|
166
167
|
if (reference === undefined) {
|
|
167
168
|
throw new TypeError(`Cannot serialize collection without a heap reference at ${path}.`);
|
|
@@ -190,7 +191,10 @@ function serializeHeapReference(value, path, state) {
|
|
|
190
191
|
}
|
|
191
192
|
if (!state.serializedHeapIds.has(id)) {
|
|
192
193
|
state.serializedHeapIds.add(id);
|
|
193
|
-
if (
|
|
194
|
+
if (isSandboxDate(value)) {
|
|
195
|
+
state.heap[String(id)] = { kind: "date", time: serializedDateTime(value) };
|
|
196
|
+
}
|
|
197
|
+
else if (isFloat32Array(value)) {
|
|
194
198
|
const storage = encodeFloat32Storage(value, id, state.float32Buffers, (id) => ({
|
|
195
199
|
kind: "ref",
|
|
196
200
|
id
|
|
@@ -297,6 +301,7 @@ function indexHeapContainers(input) {
|
|
|
297
301
|
for (const [value, stat] of stats.entries()) {
|
|
298
302
|
if (stat.count > 1 ||
|
|
299
303
|
stat.cyclic ||
|
|
304
|
+
isSandboxDate(value) ||
|
|
300
305
|
isFloat32Array(value) ||
|
|
301
306
|
(Array.isArray(value) && requiresArrayEntries(value)) ||
|
|
302
307
|
sandboxErrorTypes.has(value) ||
|
|
@@ -319,6 +324,7 @@ function collectContainerStats(value, stats, ancestors) {
|
|
|
319
324
|
}
|
|
320
325
|
if (!Array.isArray(value) &&
|
|
321
326
|
!isPlainObject(value) &&
|
|
327
|
+
!isSandboxDate(value) &&
|
|
322
328
|
!isFloat32Array(value) &&
|
|
323
329
|
!isSandboxMap(value) &&
|
|
324
330
|
!isSandboxSet(value)) {
|
|
@@ -3,6 +3,7 @@ import { types } from "node:util";
|
|
|
3
3
|
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
|
+
import { restoreDateTime } from "../interp/date.js";
|
|
6
7
|
import { hasGuestObjectState } from "../interp/object-model.js";
|
|
7
8
|
const DEFAULT_MAX_DEPTH = MAX_DATA_DEPTH;
|
|
8
9
|
const DEFAULT_MAX_ENTRIES = 100_000;
|
|
@@ -80,6 +81,10 @@ function validateDumpHeap(root, state) {
|
|
|
80
81
|
addUnique(heapIds, id, path);
|
|
81
82
|
const entry = requireRecord(value, path);
|
|
82
83
|
validateErrorType(entry, path);
|
|
84
|
+
if (entry.kind === "date") {
|
|
85
|
+
validateDateRecord(entry, path);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
83
88
|
if (entry.kind === "float32array") {
|
|
84
89
|
validateFloat32Storage(entry);
|
|
85
90
|
requireRecord(entry.entries, `${path}.entries`);
|
|
@@ -402,7 +407,7 @@ function validateGeneratorShape(record, path, state) {
|
|
|
402
407
|
function validateHeapValue(value, path, state) {
|
|
403
408
|
const record = requireRecord(value, path);
|
|
404
409
|
validateErrorType(record, path);
|
|
405
|
-
if (!["arguments", "array", "object", "map", "set", "float32array"].includes(String(record.kind)))
|
|
410
|
+
if (!["arguments", "array", "object", "map", "set", "float32array", "date"].includes(String(record.kind)))
|
|
406
411
|
fail("unknownTag", `${path}.kind`, "unknown heap tag");
|
|
407
412
|
validateValue(record, path, 1, state);
|
|
408
413
|
if (record.kind === "arguments")
|
|
@@ -411,6 +416,8 @@ function validateHeapValue(value, path, state) {
|
|
|
411
416
|
validateArrayHeap(record, path, state);
|
|
412
417
|
if (record.kind === "object")
|
|
413
418
|
requireRecord(record.entries, `${path}.entries`);
|
|
419
|
+
if (record.kind === "date")
|
|
420
|
+
validateDateRecord(record, path);
|
|
414
421
|
if (record.kind === "float32array") {
|
|
415
422
|
validateFloat32Storage(record);
|
|
416
423
|
requireRecord(record.entries, `${path}.entries`);
|
|
@@ -425,6 +432,16 @@ function validateHeapValue(value, path, state) {
|
|
|
425
432
|
if (record.kind === "set")
|
|
426
433
|
requireArray(record.values, `${path}.values`, state);
|
|
427
434
|
}
|
|
435
|
+
function validateDateRecord(record, path) {
|
|
436
|
+
if (Object.keys(record).length !== 2)
|
|
437
|
+
fail("invalidValue", path, "invalid Date fields");
|
|
438
|
+
try {
|
|
439
|
+
restoreDateTime(record.time);
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
fail("invalidValue", `${path}.time`, "invalid Date epoch");
|
|
443
|
+
}
|
|
444
|
+
}
|
|
428
445
|
function validateArrayHeap(record, path, state) {
|
|
429
446
|
if (Object.hasOwn(record, "items")) {
|
|
430
447
|
requireArray(record.items, `${path}.items`, state);
|