@absolutejs/sync 1.8.0 → 1.8.1
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/engine/index.js +38 -21
- package/dist/engine/index.js.map +3 -3
- package/dist/engine/sandbox.d.ts +20 -20
- package/dist/index.js +38 -21
- package/dist/index.js.map +3 -3
- package/package.json +4 -4
package/dist/engine/sandbox.d.ts
CHANGED
|
@@ -9,10 +9,9 @@
|
|
|
9
9
|
* - Handler must be a string. It evaluates inside the isolate's JSC VM, with
|
|
10
10
|
* no access to the host's modules, closures, or globals — only the
|
|
11
11
|
* `args` / `ctx` clones and the `actions` Reference we pass in.
|
|
12
|
-
* - First call per mutation pays an
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* per-call eval, no per-call `setGlobal`.
|
|
12
|
+
* - First call per mutation pays an isolated-jsc runner warmup + callable
|
|
13
|
+
* compile. Every subsequent call is served from the runner's keyed
|
|
14
|
+
* callable cache — no per-call eval, no per-call `setGlobal`.
|
|
16
15
|
* - Timeout terminates the isolate (the sandbox runner detects this and
|
|
17
16
|
* lazily re-spawns on the next call). On the FFI backend timeouts throw
|
|
18
17
|
* a TerminationException without killing the isolate; sync's runner
|
|
@@ -25,12 +24,11 @@
|
|
|
25
24
|
* `WebSocket`) inside your handler — those live in the Bun-Worker
|
|
26
25
|
* environment, not the bare JSC C API.
|
|
27
26
|
*
|
|
28
|
-
* **Per-call hot path (since
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* that
|
|
33
|
-
* no shared-slot serialization machinery.
|
|
27
|
+
* **Per-call hot path (since isolated-jsc 0.8).** Each mutation owns a
|
|
28
|
+
* `createIsolatedRunner()` instance. The runner applies the `tenant-script`
|
|
29
|
+
* policy, keeps a keyed isolate pool, and caches the wrapped mutation as a
|
|
30
|
+
* precompiled callable. Per call we invoke `runner.call(name, source, args)`
|
|
31
|
+
* with a call id that routes `actions.*` back through a host Reference.
|
|
34
32
|
*
|
|
35
33
|
* The runner is built lazily per-mutation: nothing is spawned until the
|
|
36
34
|
* mutation actually runs for the first time. No engine teardown hook is
|
|
@@ -66,10 +64,12 @@ export type HandlerMetricsRecord = {
|
|
|
66
64
|
durationMs: number;
|
|
67
65
|
/**
|
|
68
66
|
* CPU time spent inside the JSC sandbox (ms). Comes from
|
|
69
|
-
*
|
|
67
|
+
* isolated-jsc runner metrics — does NOT include host-side message-passing
|
|
70
68
|
* overhead on the Worker backend. Sub-millisecond runs round to 0.
|
|
71
69
|
*/
|
|
72
70
|
cpuMs: number;
|
|
71
|
+
/** isolated-jsc backend that executed the call. */
|
|
72
|
+
backend?: 'ffi' | 'worker';
|
|
73
73
|
/**
|
|
74
74
|
* Heap size (bytes) measured immediately after the script returned.
|
|
75
75
|
* Not the run's peak — a true peak needs continuous polling.
|
|
@@ -159,9 +159,9 @@ export type SandboxConfig = {
|
|
|
159
159
|
timeout?: number;
|
|
160
160
|
/**
|
|
161
161
|
* isolated-jsc backend. Defaults to `'auto'` (FFI when libJSC is
|
|
162
|
-
* reachable, Worker otherwise). Both backends now run the same
|
|
163
|
-
* `
|
|
164
|
-
*
|
|
162
|
+
* reachable, Worker otherwise). Both backends now run through the same
|
|
163
|
+
* `createIsolatedRunner().call()` hot path; the choice trades cold spawn
|
|
164
|
+
* (FFI wins ~6×) against Web API availability (Worker only).
|
|
165
165
|
*
|
|
166
166
|
* Pin to `'worker'` if your handler needs Web APIs (`URL`,
|
|
167
167
|
* `TextEncoder`, `WebSocket`) — those live in the Bun-Worker
|
|
@@ -174,12 +174,11 @@ export type SandboxConfig = {
|
|
|
174
174
|
};
|
|
175
175
|
/**
|
|
176
176
|
* Build a lazy runner for one mutation's sandboxed source. The first call
|
|
177
|
-
* compiles the
|
|
177
|
+
* compiles the runner + dispatch Reference + callable;
|
|
178
178
|
* subsequent calls only generate a fresh callId, register the per-call
|
|
179
|
-
* `actions` in the callMap, and invoke `
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
* setGlobal, no eval.
|
|
179
|
+
* `actions` in the callMap, and invoke `runner.call(...)`. Per-call cost on
|
|
180
|
+
* FFI: one JSObjectCallAsFunction + three cheap primitive packings. No
|
|
181
|
+
* per-call Reference allocation, no setGlobal, no eval.
|
|
183
182
|
*
|
|
184
183
|
* Concurrency-safe by construction: each call has its own callId →
|
|
185
184
|
* its own actions slot in the callMap.
|
|
@@ -191,7 +190,8 @@ export declare const makeSandboxedHandler: (source: string, config?: SandboxConf
|
|
|
191
190
|
/**
|
|
192
191
|
* Engine-level extras the per-mutation config doesn't carry:
|
|
193
192
|
* - `metricsHook` enables per-call telemetry via
|
|
194
|
-
* `
|
|
193
|
+
* `runner.call(..., { withMetrics: true })` (small cost; off without
|
|
194
|
+
* the hook).
|
|
195
195
|
* - `bridgeFetch` enables `actions.fetch(url, init)` inside the
|
|
196
196
|
* sandbox with host-side allowlist + auth injection.
|
|
197
197
|
*/
|
package/dist/index.js
CHANGED
|
@@ -759,12 +759,7 @@ var wrap = (source) => `
|
|
|
759
759
|
}
|
|
760
760
|
`;
|
|
761
761
|
var compile = async (source, config, bridgeFetch) => {
|
|
762
|
-
const {
|
|
763
|
-
const isolate = await createIsolate({
|
|
764
|
-
backend: config.backend ?? "auto",
|
|
765
|
-
memoryLimit: config.memoryLimit ?? 32
|
|
766
|
-
});
|
|
767
|
-
const context = await isolate.createContext();
|
|
762
|
+
const { Reference, createIsolatedRunner, resolveIsolatePolicy } = await loadIsolatedJsc();
|
|
768
763
|
const callMap = new Map;
|
|
769
764
|
const dispatch = new Reference((callId, op, ...rest) => {
|
|
770
765
|
const a = callMap.get(callId);
|
|
@@ -788,15 +783,25 @@ var compile = async (source, config, bridgeFetch) => {
|
|
|
788
783
|
throw new Error(`unknown sandbox action op: ${String(op)}`);
|
|
789
784
|
}
|
|
790
785
|
});
|
|
791
|
-
|
|
792
|
-
const
|
|
786
|
+
const timeoutMs = config.timeout ?? 5000;
|
|
787
|
+
const sourceToCall = wrap(source);
|
|
788
|
+
const policy = resolveIsolatePolicy("tenant-script", {
|
|
789
|
+
allowWorkerFallback: true,
|
|
790
|
+
backend: config.backend ?? "auto",
|
|
791
|
+
memoryLimit: config.memoryLimit ?? 32,
|
|
792
|
+
timeout: timeoutMs
|
|
793
|
+
});
|
|
794
|
+
const runner = createIsolatedRunner({
|
|
795
|
+
globals: { __dispatch: dispatch },
|
|
796
|
+
policy
|
|
797
|
+
});
|
|
798
|
+
await runner.precompile("sandboxedHandler", sourceToCall);
|
|
793
799
|
return {
|
|
794
|
-
callable,
|
|
795
800
|
callMap,
|
|
796
|
-
context,
|
|
797
|
-
isolate,
|
|
798
801
|
nextCallId: 1,
|
|
799
|
-
|
|
802
|
+
runner,
|
|
803
|
+
source: sourceToCall,
|
|
804
|
+
timeoutMs
|
|
800
805
|
};
|
|
801
806
|
};
|
|
802
807
|
var runBridgeFetch = async (config, url, init) => {
|
|
@@ -852,10 +857,7 @@ var makeSandboxedHandler = (source, config = {}, engineExtras) => {
|
|
|
852
857
|
const bridgeFetch = engineExtras?.bridgeFetch;
|
|
853
858
|
const getCompiled = async () => {
|
|
854
859
|
if (pending !== undefined) {
|
|
855
|
-
|
|
856
|
-
if (!compiled.isolate.isDisposed)
|
|
857
|
-
return compiled;
|
|
858
|
-
pending = undefined;
|
|
860
|
+
return pending;
|
|
859
861
|
}
|
|
860
862
|
pending = compile(source, config, bridgeFetch);
|
|
861
863
|
return pending;
|
|
@@ -866,9 +868,13 @@ var makeSandboxedHandler = (source, config = {}, engineExtras) => {
|
|
|
866
868
|
compiled.callMap.set(callId, actions);
|
|
867
869
|
if (metricsHook === undefined) {
|
|
868
870
|
try {
|
|
869
|
-
return await compiled.
|
|
870
|
-
|
|
871
|
-
|
|
871
|
+
return await compiled.runner.call("sandboxedHandler", compiled.source, [callId, args, ctx], { run: { timeout: compiled.timeoutMs } });
|
|
872
|
+
} catch (error) {
|
|
873
|
+
if (isIsolateDisposalError(error)) {
|
|
874
|
+
pending = undefined;
|
|
875
|
+
await disposeCompiled(compiled);
|
|
876
|
+
}
|
|
877
|
+
throw error;
|
|
872
878
|
} finally {
|
|
873
879
|
compiled.callMap.delete(callId);
|
|
874
880
|
}
|
|
@@ -876,8 +882,9 @@ var makeSandboxedHandler = (source, config = {}, engineExtras) => {
|
|
|
876
882
|
const startedAt = performance.now();
|
|
877
883
|
const id = makeRandomId();
|
|
878
884
|
try {
|
|
879
|
-
const { result, metrics } = await compiled.
|
|
885
|
+
const { result, metrics } = await compiled.runner.call("sandboxedHandler", compiled.source, [callId, args, ctx], { run: { timeout: compiled.timeoutMs }, withMetrics: true });
|
|
880
886
|
fireMetrics(metricsHook.onMetrics, {
|
|
887
|
+
backend: metrics.backend,
|
|
881
888
|
cpuMs: metrics.cpuMs,
|
|
882
889
|
durationMs: performance.now() - startedAt,
|
|
883
890
|
heapBytes: metrics.heapBytes,
|
|
@@ -888,6 +895,10 @@ var makeSandboxedHandler = (source, config = {}, engineExtras) => {
|
|
|
888
895
|
});
|
|
889
896
|
return result;
|
|
890
897
|
} catch (error) {
|
|
898
|
+
if (isIsolateDisposalError(error)) {
|
|
899
|
+
pending = undefined;
|
|
900
|
+
await disposeCompiled(compiled);
|
|
901
|
+
}
|
|
891
902
|
fireMetrics(metricsHook.onMetrics, {
|
|
892
903
|
cpuMs: 0,
|
|
893
904
|
durationMs: performance.now() - startedAt,
|
|
@@ -906,6 +917,12 @@ var makeSandboxedHandler = (source, config = {}, engineExtras) => {
|
|
|
906
917
|
};
|
|
907
918
|
};
|
|
908
919
|
var makeRandomId = () => `hm_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
920
|
+
var isIsolateDisposalError = (error) => error instanceof Error && (error.name === "TimeoutError" || error.name === "MemoryLimitError" || error.name === "IsolateDisposedError");
|
|
921
|
+
var disposeCompiled = async (compiled) => {
|
|
922
|
+
try {
|
|
923
|
+
await compiled.runner.dispose();
|
|
924
|
+
} catch {}
|
|
925
|
+
};
|
|
909
926
|
var fireMetrics = (hook, record) => {
|
|
910
927
|
let outcome;
|
|
911
928
|
try {
|
|
@@ -2443,5 +2460,5 @@ export {
|
|
|
2443
2460
|
createPresenceHub
|
|
2444
2461
|
};
|
|
2445
2462
|
|
|
2446
|
-
//# debugId=
|
|
2463
|
+
//# debugId=DDC1D4CC66583DD464756E2164756E21
|
|
2447
2464
|
//# sourceMappingURL=index.js.map
|