poe-code 4.0.72 → 5.0.0
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 +1687 -1589
- package/dist/index.js.map +4 -4
- package/dist/metafile.json +1 -1
- package/package.json +1 -1
- package/packages/safejs/dist/interp/async.d.ts +2 -1
- package/packages/safejs/dist/interp/async.js +20 -12
- package/packages/safejs/dist/interp/cancel.d.ts +1 -0
- package/packages/safejs/dist/interp/cancel.js +25 -2
- package/packages/safejs/dist/interp/globals/console-json.js +12 -7
- package/packages/safejs/dist/interp/globals/object-array.js +4 -1
- package/packages/safejs/dist/interp/interpreter.d.ts +1 -0
- package/packages/safejs/dist/interp/interpreter.js +12 -33
- package/packages/safejs/dist/interp/jobs.d.ts +20 -0
- package/packages/safejs/dist/interp/jobs.js +96 -0
- package/packages/safejs/dist/interp/methods/map.js +2 -5
- package/packages/safejs/dist/interp/methods/set.js +2 -5
- package/packages/safejs/dist/interp/promise.d.ts +3 -2
- package/packages/safejs/dist/interp/promise.js +66 -74
- package/packages/safejs/dist/interp/values.d.ts +2 -0
- package/packages/safejs/dist/interp/values.js +6 -0
- package/packages/safejs/dist/restore.js +9 -1
- package/packages/safejs/dist/run.js +6 -3
- package/packages/safejs/dist/snapshot/dump-format.d.ts +1 -0
- package/packages/safejs/dist/snapshot/dump-format.js +1 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createSubsetErrorValue } from "./exceptions.js";
|
|
2
|
+
import { runPromiseJob } from "./jobs.js";
|
|
2
3
|
import { observeSandboxPromise } from "./promise-tracker.js";
|
|
3
4
|
import { createSandboxClosure, createSandboxPromise, isSandboxClosure, isSandboxPromise } from "./values.js";
|
|
4
5
|
export function createPromiseGlobals(options) {
|
|
@@ -29,11 +30,13 @@ export function createPromiseGlobals(options) {
|
|
|
29
30
|
}),
|
|
30
31
|
any: createSandboxClosure({
|
|
31
32
|
async: true,
|
|
32
|
-
call: ([values]) => createSandboxPromise(settleIterable(values, (entries) => Promise.any(entries).catch((error) =>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
call: ([values]) => createSandboxPromise(settleIterable(values, (entries) => Promise.any(entries).catch((error) => {
|
|
34
|
+
const aggregate = createSubsetErrorValue("AggregateError", "All promises were rejected", [], options.budget);
|
|
35
|
+
aggregate.errors = Array.isArray(error.errors)
|
|
36
|
+
? error.errors
|
|
37
|
+
: [];
|
|
38
|
+
throw budgetSandboxValue(aggregate, options.budget);
|
|
39
|
+
}), options.budget)),
|
|
37
40
|
name: "any"
|
|
38
41
|
}),
|
|
39
42
|
resolve: createSandboxClosure({
|
|
@@ -51,13 +54,13 @@ export function createPromiseGlobals(options) {
|
|
|
51
54
|
}
|
|
52
55
|
};
|
|
53
56
|
}
|
|
54
|
-
export function getPromiseMember(target, property, budget
|
|
57
|
+
export function getPromiseMember(target, property, budget) {
|
|
55
58
|
if (property === "then") {
|
|
56
59
|
return createSandboxClosure({
|
|
57
60
|
async: true,
|
|
58
61
|
call: ([onFulfilled, onRejected]) => {
|
|
59
62
|
observeSandboxPromise(target);
|
|
60
|
-
const chained = createSandboxPromise(
|
|
63
|
+
const chained = createSandboxPromise(target.promise.then((value) => runPromiseReaction(onFulfilled, value, "fulfilled", budget, chained), (reason) => runPromiseReaction(onRejected, reason, "rejected", budget, chained)));
|
|
61
64
|
return chained;
|
|
62
65
|
},
|
|
63
66
|
name: "then"
|
|
@@ -68,7 +71,7 @@ export function getPromiseMember(target, property, budget, enqueue = (task) => P
|
|
|
68
71
|
async: true,
|
|
69
72
|
call: ([onRejected]) => {
|
|
70
73
|
observeSandboxPromise(target);
|
|
71
|
-
const chained = createSandboxPromise(
|
|
74
|
+
const chained = createSandboxPromise(target.promise.then((value) => runPromiseReaction(undefined, value, "fulfilled", budget, chained), (reason) => runPromiseReaction(onRejected, reason, "rejected", budget, chained)));
|
|
72
75
|
return chained;
|
|
73
76
|
},
|
|
74
77
|
name: "catch"
|
|
@@ -79,7 +82,7 @@ export function getPromiseMember(target, property, budget, enqueue = (task) => P
|
|
|
79
82
|
async: true,
|
|
80
83
|
call: ([onFinally]) => {
|
|
81
84
|
observeSandboxPromise(target);
|
|
82
|
-
const chained = createSandboxPromise(
|
|
85
|
+
const chained = createSandboxPromise(target.promise.then((value) => runPromiseFinally(onFinally, value, "fulfilled", budget, chained), (reason) => runPromiseFinally(onFinally, reason, "rejected", budget, chained)));
|
|
83
86
|
return chained;
|
|
84
87
|
},
|
|
85
88
|
name: "finally"
|
|
@@ -190,62 +193,53 @@ function resolveThenable(value, then, options, seenThenables) {
|
|
|
190
193
|
return new Promise((resolve, reject) => {
|
|
191
194
|
let settlement;
|
|
192
195
|
let completed = false;
|
|
196
|
+
let invocationPending = true;
|
|
193
197
|
const complete = () => {
|
|
194
|
-
if (completed || settlement === undefined)
|
|
198
|
+
if (completed || invocationPending || settlement === undefined)
|
|
195
199
|
return;
|
|
196
200
|
completed = true;
|
|
197
|
-
|
|
198
|
-
|
|
201
|
+
try {
|
|
202
|
+
if (settlement.state === "fulfilled") {
|
|
203
|
+
resolve(resolveSandboxValueNow(settlement.value, options, seenThenables));
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
reject(budgetIfNeeded(settlement.value, options.budget));
|
|
207
|
+
}
|
|
199
208
|
}
|
|
200
|
-
|
|
201
|
-
reject(
|
|
209
|
+
catch (error) {
|
|
210
|
+
reject(error);
|
|
202
211
|
}
|
|
203
212
|
};
|
|
204
213
|
const recordSettlement = (state, settledValue) => {
|
|
205
214
|
if (settlement !== undefined) {
|
|
206
|
-
|
|
215
|
+
return;
|
|
207
216
|
}
|
|
208
217
|
settlement = { state, value: settledValue };
|
|
209
218
|
queueMicrotask(complete);
|
|
210
219
|
};
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if (isPromiseLike(result)) {
|
|
235
|
-
Promise.resolve(result).catch((reason) => {
|
|
236
|
-
rejectOnce(reason);
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
catch (error) {
|
|
241
|
-
if (settlement !== undefined) {
|
|
242
|
-
settlement = { state: "rejected", value: error };
|
|
243
|
-
queueMicrotask(complete);
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
rejectOnce(error);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
220
|
+
callInPromiseJob(then, [
|
|
221
|
+
createSandboxClosure({
|
|
222
|
+
call: ([resolved]) => {
|
|
223
|
+
recordSettlement("fulfilled", resolved);
|
|
224
|
+
return undefined;
|
|
225
|
+
},
|
|
226
|
+
name: "resolve"
|
|
227
|
+
}),
|
|
228
|
+
createSandboxClosure({
|
|
229
|
+
call: ([reason]) => {
|
|
230
|
+
recordSettlement("rejected", reason);
|
|
231
|
+
return undefined;
|
|
232
|
+
},
|
|
233
|
+
name: "reject"
|
|
234
|
+
})
|
|
235
|
+
], value).then(() => {
|
|
236
|
+
invocationPending = false;
|
|
237
|
+
complete();
|
|
238
|
+
}, (error) => {
|
|
239
|
+
invocationPending = false;
|
|
240
|
+
recordSettlement("rejected", error);
|
|
241
|
+
complete();
|
|
242
|
+
});
|
|
249
243
|
}).then((resolved) => budgetIfNeeded(resolved, options.budget));
|
|
250
244
|
}
|
|
251
245
|
function runPromiseReaction(handler, value, state, budget, self) {
|
|
@@ -254,25 +248,24 @@ function runPromiseReaction(handler, value, state, budget, self) {
|
|
|
254
248
|
? Promise.resolve(value)
|
|
255
249
|
: Promise.reject(budgetSandboxValue(value, budget));
|
|
256
250
|
}
|
|
257
|
-
|
|
258
|
-
const result = handler.call([value]);
|
|
259
|
-
return resolveReactionResult(result, budget, self);
|
|
260
|
-
}
|
|
261
|
-
catch (error) {
|
|
262
|
-
return Promise.reject(error);
|
|
263
|
-
}
|
|
251
|
+
return callInPromiseJob(handler, [value]).then(({ value: result }) => resolveReactionResult(result, budget, self));
|
|
264
252
|
}
|
|
265
253
|
function runPromiseFinally(handler, value, state, budget, self) {
|
|
266
254
|
if (!isSandboxClosure(handler)) {
|
|
267
255
|
return runPromiseReaction(undefined, value, state, budget);
|
|
268
256
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
257
|
+
return callInPromiseJob(handler, []).then(({ value: result }) => resolveReactionResult(result, budget, self).then(() => runPromiseReaction(undefined, value, state, budget)));
|
|
258
|
+
}
|
|
259
|
+
function callInPromiseJob(handler, args, thisValue = undefined) {
|
|
260
|
+
return runPromiseJob(async () => {
|
|
261
|
+
let result = handler.call(args, { stack: [], thisValue });
|
|
262
|
+
if (handler.async !== true)
|
|
263
|
+
result = await result;
|
|
264
|
+
if (isSandboxPromise(result) && result.synchronousPrefix !== undefined) {
|
|
265
|
+
await result.synchronousPrefix;
|
|
266
|
+
}
|
|
267
|
+
return { value: result };
|
|
268
|
+
});
|
|
276
269
|
}
|
|
277
270
|
function resolveReactionResult(result, budget, self) {
|
|
278
271
|
return Promise.resolve(result).then((resolved) => {
|
|
@@ -289,14 +282,13 @@ function isSelfResolution(result, self) {
|
|
|
289
282
|
return (self !== undefined &&
|
|
290
283
|
(result === self || (isSandboxPromise(result) && result.promise === self.promise)));
|
|
291
284
|
}
|
|
292
|
-
function getThenable(value) {
|
|
293
|
-
if (typeof value !== "object" ||
|
|
294
|
-
value === null ||
|
|
295
|
-
isSandboxClosure(value) ||
|
|
296
|
-
isSandboxPromise(value)) {
|
|
285
|
+
export function getThenable(value) {
|
|
286
|
+
if (typeof value !== "object" || value === null || isSandboxPromise(value)) {
|
|
297
287
|
return undefined;
|
|
298
288
|
}
|
|
299
|
-
const then = value
|
|
289
|
+
const then = isSandboxClosure(value)
|
|
290
|
+
? value.properties?.then
|
|
291
|
+
: value.then;
|
|
300
292
|
return isSandboxClosure(then) ? then : undefined;
|
|
301
293
|
}
|
|
302
294
|
function budgetIfNeeded(value, budget) {
|
|
@@ -62,6 +62,7 @@ export type SandboxClosure = {
|
|
|
62
62
|
};
|
|
63
63
|
export type SandboxPromise = {
|
|
64
64
|
readonly kind: "promise";
|
|
65
|
+
readonly synchronousPrefix?: Promise<void>;
|
|
65
66
|
readonly promise: Promise<SandboxValue>;
|
|
66
67
|
readonly hostCall?: import("./host-call.js").HostCallRecord;
|
|
67
68
|
readonly hostCallJournal?: import("./host-call.js").HostCallJournal;
|
|
@@ -89,6 +90,7 @@ export declare function createSandboxClosure(input: {
|
|
|
89
90
|
}): SandboxClosure;
|
|
90
91
|
export declare function createSandboxPromise(promise: Promise<SandboxValue>, metadata?: {
|
|
91
92
|
trackReplay?: boolean;
|
|
93
|
+
synchronousPrefix?: Promise<void>;
|
|
92
94
|
hostCall?: import("./host-call.js").HostCallRecord;
|
|
93
95
|
hostCallJournal?: import("./host-call.js").HostCallJournal;
|
|
94
96
|
span?: SandboxCallContext["span"];
|
|
@@ -50,11 +50,17 @@ export function createSandboxPromise(promise, metadata = {}) {
|
|
|
50
50
|
enumerable: false,
|
|
51
51
|
value: true
|
|
52
52
|
});
|
|
53
|
+
Object.defineProperty(sandboxPromise, Symbol.toStringTag, { value: "Promise" });
|
|
53
54
|
if (metadata.span !== undefined) {
|
|
54
55
|
Object.defineProperty(sandboxPromise, "span", {
|
|
55
56
|
value: metadata.span
|
|
56
57
|
});
|
|
57
58
|
}
|
|
59
|
+
if (metadata.synchronousPrefix !== undefined) {
|
|
60
|
+
Object.defineProperty(sandboxPromise, "synchronousPrefix", {
|
|
61
|
+
value: metadata.synchronousPrefix
|
|
62
|
+
});
|
|
63
|
+
}
|
|
58
64
|
if (metadata.hostCall !== undefined) {
|
|
59
65
|
Object.defineProperty(sandboxPromise, "hostCall", { value: metadata.hostCall });
|
|
60
66
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { hashSource } from "./parse/hash.js";
|
|
2
2
|
import { replaceErrorStack } from "./error/shape.js";
|
|
3
|
-
import { validateDumpEnvelope } from "./snapshot/validation.js";
|
|
3
|
+
import { SnapshotValidationError, validateDumpEnvelope } from "./snapshot/validation.js";
|
|
4
|
+
import { EXECUTION_SEMANTICS } from "./snapshot/dump-format.js";
|
|
4
5
|
import { assertSnapshotInactive } from "./interp/running-state.js";
|
|
5
6
|
export class SnapshotMismatchError extends Error {
|
|
6
7
|
actualHash;
|
|
@@ -16,6 +17,13 @@ export class SnapshotMismatchError extends Error {
|
|
|
16
17
|
export function restore(snapshot, options) {
|
|
17
18
|
assertSnapshotInactive(snapshot);
|
|
18
19
|
validateDumpEnvelope(snapshot);
|
|
20
|
+
if (snapshot.executionSemantics !== EXECUTION_SEMANTICS &&
|
|
21
|
+
(snapshot.executionSemantics !== undefined ||
|
|
22
|
+
snapshot.promiseReplay !== undefined ||
|
|
23
|
+
snapshot.replay !== undefined ||
|
|
24
|
+
snapshot.initialInputs !== undefined)) {
|
|
25
|
+
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.");
|
|
26
|
+
}
|
|
19
27
|
const currentSourceHash = hashSource(options.source);
|
|
20
28
|
if (snapshot.sourceHash !== currentSourceHash) {
|
|
21
29
|
throw new SnapshotMismatchError(snapshot.sourceHash, currentSourceHash);
|
|
@@ -23,7 +23,7 @@ import { deepCopyToSandbox, isSandboxClosure, isSandboxPromise, measureSandboxDa
|
|
|
23
23
|
import { resolveModuleImports } from "./modules/registry.js";
|
|
24
24
|
import { activateOtelSink, getActiveOtelSpan, safeAddEvent } from "./observability/otel.js";
|
|
25
25
|
import { attachDumpController, createDumpController } from "./snapshot/dump.js";
|
|
26
|
-
import { DUMP_FORMAT_VERSION } from "./snapshot/dump-format.js";
|
|
26
|
+
import { DUMP_FORMAT_VERSION, EXECUTION_SEMANTICS } from "./snapshot/dump-format.js";
|
|
27
27
|
import { createSnapshotScheduler } from "./snapshot/scheduler.js";
|
|
28
28
|
import { UnsnapshotableValueError } from "./snapshot/serialize.js";
|
|
29
29
|
import { prepareReplayInputs } from "./snapshot/replay-inputs.js";
|
|
@@ -110,7 +110,6 @@ export function run(source, options = {}) {
|
|
|
110
110
|
}),
|
|
111
111
|
...createRegexGlobals()
|
|
112
112
|
};
|
|
113
|
-
const bindings = wrapCancelableBindings(builtinBindings, options.signal);
|
|
114
113
|
const initialInputs = prepareReplayInputs({
|
|
115
114
|
bindings: callerBindings,
|
|
116
115
|
imports: resolveModuleImports(module, options.modules, {
|
|
@@ -151,7 +150,7 @@ export function run(source, options = {}) {
|
|
|
151
150
|
leaveInputReplay = () => budget.setRetainedDataUsage(initialInputs, 0);
|
|
152
151
|
const entryPointArgs = initialInputs.values.entryPointArgs;
|
|
153
152
|
const cancelableCallerBindings = wrapCancelableBindings(initialInputs.values.bindings, options.signal);
|
|
154
|
-
const scope = new Scope(
|
|
153
|
+
const scope = new Scope(builtinBindings, undefined, undefined, { chargeData: false }, interpreterSnapshot?.bindings);
|
|
155
154
|
const callerScope = scope.child(cancelableCallerBindings);
|
|
156
155
|
const executionScope = new Scope(initialInputs.values.imports, callerScope, initialInputs.values.importMeta, { functionBoundary: true });
|
|
157
156
|
const activeSnapshotScheduler = createSnapshotScheduler({
|
|
@@ -202,6 +201,7 @@ export function run(source, options = {}) {
|
|
|
202
201
|
dumpController.onYield(createSnapshot);
|
|
203
202
|
},
|
|
204
203
|
scope: executionScope,
|
|
204
|
+
signal: options.signal,
|
|
205
205
|
snapshot: interpreterSnapshot,
|
|
206
206
|
surfaceUnhandledThrows: true,
|
|
207
207
|
useScopeDirectly: true
|
|
@@ -247,6 +247,7 @@ export function run(source, options = {}) {
|
|
|
247
247
|
dumpController.onYield(createSnapshot);
|
|
248
248
|
},
|
|
249
249
|
scope: executionScope,
|
|
250
|
+
signal: options.signal,
|
|
250
251
|
snapshot: interpreterSnapshot
|
|
251
252
|
});
|
|
252
253
|
await throwIfReturnedPromiseRejected(result);
|
|
@@ -350,6 +351,7 @@ async function callEntryPoint(input) {
|
|
|
350
351
|
...argumentBindings
|
|
351
352
|
});
|
|
352
353
|
return interpret(createEntryPointAwait(input.args.length, input.module.span), {
|
|
354
|
+
signal: input.signal,
|
|
353
355
|
budget: input.budget,
|
|
354
356
|
captureReplayState: input.captureReplayState,
|
|
355
357
|
onYield: input.onYield,
|
|
@@ -407,6 +409,7 @@ function createExecutableNode(module) {
|
|
|
407
409
|
function createRunSnapshot(input) {
|
|
408
410
|
return {
|
|
409
411
|
version: DUMP_FORMAT_VERSION,
|
|
412
|
+
executionSemantics: EXECUTION_SEMANTICS,
|
|
410
413
|
sourceHash: input.sourceHash,
|
|
411
414
|
bindings: input.bindings,
|
|
412
415
|
clock: input.clock?.snapshot(),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export const DUMP_FORMAT_VERSION = 1;
|
|
2
|
+
export const EXECUTION_SEMANTICS = "jobs-v1";
|
|
2
3
|
import { assertSnapshotGraphDepth } from "../graph-depth.js";
|
|
3
4
|
import { getSandboxArgumentEntries, isSandboxArguments } from "../interp/arguments.js";
|
|
4
5
|
import { serializeArguments } from "./arguments.js";
|