@absolutejs/sync 1.7.3 → 1.7.5
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 +32 -59
- package/dist/engine/index.js.map +3 -3
- package/dist/engine/sandbox.d.ts +30 -25
- package/dist/index.js +32 -59
- package/dist/index.js.map +3 -3
- package/package.json +3 -3
package/dist/engine/sandbox.d.ts
CHANGED
|
@@ -8,30 +8,30 @@
|
|
|
8
8
|
*
|
|
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
|
-
* `args` / `ctx` clones and the `actions`
|
|
12
|
-
* - First call per mutation pays
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* `args` / `ctx` clones and the `actions` Reference we pass in.
|
|
12
|
+
* - First call per mutation pays an isolate spawn + compile (~3–25 ms
|
|
13
|
+
* depending on backend). Every subsequent call is a single
|
|
14
|
+
* `JSObjectCallAsFunction` (FFI) or one postMessage (Worker) — no
|
|
15
|
+
* per-call eval, no per-call `setGlobal`.
|
|
15
16
|
* - Timeout terminates the isolate (the sandbox runner detects this and
|
|
16
17
|
* lazily re-spawns on the next call). On the FFI backend timeouts throw
|
|
17
18
|
* a TerminationException without killing the isolate; sync's runner
|
|
18
19
|
* treats both shapes the same.
|
|
19
|
-
* - Each per-call context retains some JSC metadata until the isolate's
|
|
20
|
-
* next GC sweep. Empirically ~2 MB residual per call (Worker backend).
|
|
21
|
-
* For long-lived mutations choose `memoryLimit` ≥ 128 (the default 32
|
|
22
|
-
* trips after a few dozen calls without pressure for GC).
|
|
23
20
|
*
|
|
24
|
-
* **Backend default: `'auto'`** —
|
|
25
|
-
* pump on the FFI backend (alternates Bun event-loop yields with JSC
|
|
26
|
-
* microtask drains, bounded by `Script.run`'s `timeout`), so the
|
|
27
|
-
* `actions.insert/update/delete/change` async References settle on FFI
|
|
28
|
-
* just like they do on Worker. `'auto'` picks FFI when libJSC is reachable
|
|
21
|
+
* **Backend default: `'auto'`** — `'auto'` picks FFI when libJSC is reachable
|
|
29
22
|
* (~300 KB cold heap, interrupt-driven CPU timeouts) and falls back to
|
|
30
23
|
* Worker (~46 MB cold heap, postMessage round-trips) otherwise. Pin to
|
|
31
24
|
* `'worker'` if you specifically need Web APIs (`URL`, `TextEncoder`,
|
|
32
25
|
* `WebSocket`) inside your handler — those live in the Bun-Worker
|
|
33
26
|
* environment, not the bare JSC C API.
|
|
34
27
|
*
|
|
28
|
+
* **Per-call hot path (since 1.7.4 / isolated-jsc 0.6).** Each mutation is
|
|
29
|
+
* compiled to a {@link Callable} once — a precompiled function expression
|
|
30
|
+
* the sandbox owns by reference. Per call we invoke
|
|
31
|
+
* `callable.call([args, ctx, dispatch])` where `dispatch` is a Reference
|
|
32
|
+
* that bridges `actions.*` back to the host. No globals, no eval per call,
|
|
33
|
+
* no shared-slot serialization machinery.
|
|
34
|
+
*
|
|
35
35
|
* The runner is built lazily per-mutation: nothing is spawned until the
|
|
36
36
|
* mutation actually runs for the first time. No engine teardown hook is
|
|
37
37
|
* needed — the OS reaps the workers when the engine's host process exits.
|
|
@@ -45,27 +45,32 @@ export type SandboxConfig = {
|
|
|
45
45
|
timeout?: number;
|
|
46
46
|
/**
|
|
47
47
|
* isolated-jsc backend. Defaults to `'auto'` (FFI when libJSC is
|
|
48
|
-
* reachable, Worker otherwise)
|
|
49
|
-
*
|
|
50
|
-
*
|
|
48
|
+
* reachable, Worker otherwise). Both backends now run the same
|
|
49
|
+
* `Context.compileCallable`-based hot path; the choice trades cold
|
|
50
|
+
* spawn (FFI wins ~6×) against Web API availability (Worker only).
|
|
51
51
|
*
|
|
52
52
|
* Pin to `'worker'` if your handler needs Web APIs (`URL`,
|
|
53
53
|
* `TextEncoder`, `WebSocket`) — those live in the Bun-Worker
|
|
54
54
|
* environment, not the bare JSC C API.
|
|
55
55
|
*
|
|
56
|
-
* Pin to `'ffi'`
|
|
57
|
-
*
|
|
56
|
+
* Pin to `'ffi'` to bypass the auto-probe when you know libJSC is
|
|
57
|
+
* reachable (e.g. CI with a known image).
|
|
58
58
|
*/
|
|
59
59
|
backend?: 'auto' | 'ffi' | 'worker';
|
|
60
60
|
};
|
|
61
61
|
/**
|
|
62
62
|
* Build a lazy runner for one mutation's sandboxed source. The first call
|
|
63
|
-
* compiles
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
63
|
+
* compiles the isolate + context + dispatch Reference + callable;
|
|
64
|
+
* subsequent calls only generate a fresh callId, register the per-call
|
|
65
|
+
* `actions` in the callMap, and invoke `callable.call([callId, args,
|
|
66
|
+
* ctx])`. Per-call cost on FFI: one JSObjectCallAsFunction + three
|
|
67
|
+
* cheap primitive packings. No per-call Reference allocation, no
|
|
68
|
+
* setGlobal, no eval.
|
|
69
|
+
*
|
|
70
|
+
* Concurrency-safe by construction: each call has its own callId →
|
|
71
|
+
* its own actions slot in the callMap.
|
|
72
|
+
*
|
|
73
|
+
* If the isolate has been disposed (timeout, memory cap), the next
|
|
74
|
+
* call re-spawns transparently.
|
|
70
75
|
*/
|
|
71
76
|
export declare const makeSandboxedHandler: (source: string, config?: SandboxConfig) => ((args: unknown, ctx: unknown, actions: MutationActions) => Promise<unknown>);
|
package/dist/index.js
CHANGED
|
@@ -725,7 +725,7 @@ var loadIsolatedJsc = async () => {
|
|
|
725
725
|
}
|
|
726
726
|
};
|
|
727
727
|
var wrap = (source) => `
|
|
728
|
-
(
|
|
728
|
+
function (__callId, args, ctx) {
|
|
729
729
|
const userFn = (${source});
|
|
730
730
|
if (typeof userFn !== 'function') {
|
|
731
731
|
throw new Error(
|
|
@@ -734,20 +734,26 @@ var wrap = (source) => `
|
|
|
734
734
|
);
|
|
735
735
|
}
|
|
736
736
|
const actions = {
|
|
737
|
-
insert: (table, data) =>
|
|
738
|
-
update: (table, data) =>
|
|
739
|
-
delete: (table, row) =>
|
|
740
|
-
change: (collection, change) =>
|
|
737
|
+
insert: (table, data) => __dispatch(__callId, 'insert', table, data),
|
|
738
|
+
update: (table, data) => __dispatch(__callId, 'update', table, data),
|
|
739
|
+
delete: (table, row) => __dispatch(__callId, 'delete', table, row),
|
|
740
|
+
change: (collection, change) => __dispatch(__callId, 'change', collection, change)
|
|
741
741
|
};
|
|
742
742
|
return userFn(args, ctx, actions);
|
|
743
|
-
}
|
|
743
|
+
}
|
|
744
744
|
`;
|
|
745
|
-
var
|
|
746
|
-
|
|
747
|
-
const
|
|
748
|
-
|
|
745
|
+
var compile = async (source, config) => {
|
|
746
|
+
const { createIsolate, Reference } = await loadIsolatedJsc();
|
|
747
|
+
const isolate = await createIsolate({
|
|
748
|
+
backend: config.backend ?? "auto",
|
|
749
|
+
memoryLimit: config.memoryLimit ?? 32
|
|
750
|
+
});
|
|
751
|
+
const context = await isolate.createContext();
|
|
752
|
+
const callMap = new Map;
|
|
753
|
+
const dispatch = new Reference((callId, op, ...rest) => {
|
|
754
|
+
const a = callMap.get(callId);
|
|
749
755
|
if (a === undefined) {
|
|
750
|
-
throw new Error(
|
|
756
|
+
throw new Error(`__dispatch invoked for orphan callId ${String(callId)}`);
|
|
751
757
|
}
|
|
752
758
|
switch (op) {
|
|
753
759
|
case "insert":
|
|
@@ -762,27 +768,14 @@ var installRouter = async (context, currentActions, Reference) => {
|
|
|
762
768
|
throw new Error(`unknown sandbox action op: ${String(op)}`);
|
|
763
769
|
}
|
|
764
770
|
});
|
|
765
|
-
await context.setGlobal("
|
|
766
|
-
|
|
767
|
-
var compile = async (source, config) => {
|
|
768
|
-
const { createIsolate, Reference } = await loadIsolatedJsc();
|
|
769
|
-
const isolate = await createIsolate({
|
|
770
|
-
backend: config.backend ?? "auto",
|
|
771
|
-
memoryLimit: config.memoryLimit ?? 32
|
|
772
|
-
});
|
|
773
|
-
const script = await isolate.compileScript(wrap(source));
|
|
774
|
-
const context = await isolate.createContext();
|
|
775
|
-
const currentActions = {
|
|
776
|
-
value: undefined
|
|
777
|
-
};
|
|
778
|
-
await installRouter(context, currentActions, Reference);
|
|
771
|
+
await context.setGlobal("__dispatch", dispatch);
|
|
772
|
+
const callable = await context.compileCallable(wrap(source));
|
|
779
773
|
return {
|
|
774
|
+
callable,
|
|
775
|
+
callMap,
|
|
780
776
|
context,
|
|
781
|
-
currentActions,
|
|
782
777
|
isolate,
|
|
783
|
-
|
|
784
|
-
script,
|
|
785
|
-
servedCalls: 0,
|
|
778
|
+
nextCallId: 1,
|
|
786
779
|
timeoutMs: config.timeout ?? 5000
|
|
787
780
|
};
|
|
788
781
|
};
|
|
@@ -798,37 +791,17 @@ var makeSandboxedHandler = (source, config = {}) => {
|
|
|
798
791
|
pending = compile(source, config);
|
|
799
792
|
return pending;
|
|
800
793
|
};
|
|
801
|
-
const recycleContextIfNeeded = async (compiled) => {
|
|
802
|
-
if (compiled.servedCalls < DEFAULT_RECYCLE_CONTEXT_AFTER)
|
|
803
|
-
return;
|
|
804
|
-
const { Reference } = await loadIsolatedJsc();
|
|
805
|
-
await compiled.context.dispose().catch(() => {});
|
|
806
|
-
compiled.context = await compiled.isolate.createContext();
|
|
807
|
-
await installRouter(compiled.context, compiled.currentActions, Reference);
|
|
808
|
-
compiled.servedCalls = 0;
|
|
809
|
-
};
|
|
810
794
|
return async (args, ctx, actions) => {
|
|
811
795
|
const compiled = await getCompiled();
|
|
812
|
-
const
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
compiled.
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
});
|
|
822
|
-
return result;
|
|
823
|
-
} finally {
|
|
824
|
-
compiled.currentActions.value = undefined;
|
|
825
|
-
compiled.servedCalls += 1;
|
|
826
|
-
}
|
|
827
|
-
});
|
|
828
|
-
compiled.runQueue = turn.catch(() => {
|
|
829
|
-
return;
|
|
830
|
-
});
|
|
831
|
-
return turn;
|
|
796
|
+
const callId = compiled.nextCallId++;
|
|
797
|
+
compiled.callMap.set(callId, actions);
|
|
798
|
+
try {
|
|
799
|
+
return await compiled.callable.call([callId, args, ctx], {
|
|
800
|
+
timeout: compiled.timeoutMs
|
|
801
|
+
});
|
|
802
|
+
} finally {
|
|
803
|
+
compiled.callMap.delete(callId);
|
|
804
|
+
}
|
|
832
805
|
};
|
|
833
806
|
};
|
|
834
807
|
|
|
@@ -2350,5 +2323,5 @@ export {
|
|
|
2350
2323
|
createPresenceHub
|
|
2351
2324
|
};
|
|
2352
2325
|
|
|
2353
|
-
//# debugId=
|
|
2326
|
+
//# debugId=B9056CA0A3DAE81164756E2164756E21
|
|
2354
2327
|
//# sourceMappingURL=index.js.map
|