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.
- package/dist/index.js +1147 -574
- 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-VAYQP2VZ.js → chunk-KMTTD3BL.js} +7 -3
- package/packages/safe-js/dist/chunks/chunk-KMTTD3BL.js.map +7 -0
- package/packages/safe-js/dist/chunks/{chunk-ZTGKRKQH.js → chunk-RU2BCN4G.js} +1069 -577
- 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/async.js +3 -0
- 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/function-string.d.ts +2 -0
- package/packages/safe-js/dist/interp/function-string.js +21 -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 +12 -13
- package/packages/safe-js/dist/interp/globals/numeric-parsers.d.ts +5 -0
- package/packages/safe-js/dist/interp/globals/numeric-parsers.js +47 -0
- package/packages/safe-js/dist/interp/globals/object-array.js +21 -24
- 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 +173 -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/function.d.ts +2 -0
- package/packages/safe-js/dist/interp/methods/function.js +10 -1
- 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/resources.d.ts +1 -0
- package/packages/safe-js/dist/interp/string-coercion.js +7 -1
- package/packages/safe-js/dist/interp/values.d.ts +4 -0
- package/packages/safe-js/dist/interp/values.js +81 -3
- package/packages/safe-js/dist/migrate.d.ts +2 -2
- package/packages/safe-js/dist/migrate.js +1 -1
- package/packages/safe-js/dist/migration-file.d.ts +4 -4
- package/packages/safe-js/dist/parse/function-source.d.ts +7 -0
- package/packages/safe-js/dist/parse/function-source.js +1 -0
- package/packages/safe-js/dist/parse/hash.d.ts +2 -2
- package/packages/safe-js/dist/parse/hash.js +23 -9
- package/packages/safe-js/dist/parse/parser.js +36 -23
- package/packages/safe-js/dist/restore.js +2 -1
- package/packages/safe-js/dist/run.js +16 -5
- 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.d.ts +1 -1
- package/packages/safe-js/dist/snapshot/dump-format.js +19 -6
- package/packages/safe-js/dist/snapshot/migration.js +2 -2
- 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 +10 -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-VAYQP2VZ.js.map +0 -7
- 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(
|
|
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(
|
|
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(
|
|
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(), {
|
|
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, {
|
|
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"
|
|
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({
|
|
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 const DUMP_FORMAT_VERSION = 1;
|
|
2
|
-
export const EXECUTION_SEMANTICS = "jobs-
|
|
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 (
|
|
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
|
}
|
|
@@ -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-
|
|
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 =
|
|
380
|
-
?
|
|
381
|
-
:
|
|
382
|
-
?
|
|
383
|
-
:
|
|
384
|
-
?
|
|
385
|
-
:
|
|
386
|
-
? [...value.
|
|
387
|
-
:
|
|
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");
|