@absolutejs/sync 1.11.0 → 1.12.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 +25 -4
- package/dist/engine/index.js.map +4 -4
- package/dist/engine/sandbox.d.ts +46 -0
- package/dist/index.js +29 -6
- package/dist/index.js.map +6 -6
- package/dist/testing.js +23 -3
- package/dist/testing.js.map +3 -3
- package/package.json +1 -1
package/dist/engine/sandbox.d.ts
CHANGED
|
@@ -171,6 +171,52 @@ export type SandboxConfig = {
|
|
|
171
171
|
* reachable (e.g. CI with a known image).
|
|
172
172
|
*/
|
|
173
173
|
backend?: 'auto' | 'ffi' | 'worker';
|
|
174
|
+
/**
|
|
175
|
+
* **Escape hatch.** Map of host functions the sandboxed handler may
|
|
176
|
+
* call as `unsafeHost.fnName(...args)`. The name is deliberately
|
|
177
|
+
* loud: anyone reading the source must see immediately that a
|
|
178
|
+
* sandboxed mutation is reaching through to non-deterministic host
|
|
179
|
+
* code (third-party API, queue push, email send, anything that
|
|
180
|
+
* touches the outside world).
|
|
181
|
+
*
|
|
182
|
+
* Without this option, the sandbox is hermetic — only `args`,
|
|
183
|
+
* `ctx`, and `actions` are reachable. Declare an entry here, name
|
|
184
|
+
* it visibly (e.g. `chargeStripe`, `sendSlackPing`), and the engine
|
|
185
|
+
* exposes it on the sandbox-side `unsafeHost` Proxy. The fn runs on
|
|
186
|
+
* the host; its return value is structured-cloned back across the
|
|
187
|
+
* isolate boundary; thrown errors propagate into the sandbox as
|
|
188
|
+
* normal JS errors the handler can catch.
|
|
189
|
+
*
|
|
190
|
+
* The deterministic-mutation guarantees stop at the call site —
|
|
191
|
+
* retries WILL re-fire these host fns (they're outside the
|
|
192
|
+
* transaction). Treat them as side effects and either make them
|
|
193
|
+
* idempotent or pair them with explicit compensation in the
|
|
194
|
+
* handler. Convex's actions are the same model; this is the same
|
|
195
|
+
* trade.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```ts
|
|
199
|
+
* defineMutation({
|
|
200
|
+
* name: 'payments:checkout',
|
|
201
|
+
* sandboxedHandler: `async (args, ctx, actions, unsafeHost) => {
|
|
202
|
+
* const order = await actions.insert('orders', { ...args, status: 'pending' });
|
|
203
|
+
* const receipt = await unsafeHost.chargeStripe({
|
|
204
|
+
* amount: order.amount,
|
|
205
|
+
* token: args.token,
|
|
206
|
+
* });
|
|
207
|
+
* await actions.update('orders', { id: order.id, status: 'paid', receipt });
|
|
208
|
+
* return order;
|
|
209
|
+
* }`,
|
|
210
|
+
* sandbox: {
|
|
211
|
+
* unsafeHost: {
|
|
212
|
+
* chargeStripe: ({ amount, token }) =>
|
|
213
|
+
* stripe.charges.create({ amount, source: token }),
|
|
214
|
+
* },
|
|
215
|
+
* },
|
|
216
|
+
* });
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
unsafeHost?: Record<string, (...args: any[]) => unknown | Promise<unknown>>;
|
|
174
220
|
};
|
|
175
221
|
/**
|
|
176
222
|
* Build a lazy runner for one mutation's sandboxed source. The first call
|
package/dist/index.js
CHANGED
|
@@ -179,7 +179,8 @@ var sync = ({
|
|
|
179
179
|
headers: {
|
|
180
180
|
"cache-control": "no-cache, no-transform",
|
|
181
181
|
connection: "keep-alive",
|
|
182
|
-
"content-type": "text/event-stream"
|
|
182
|
+
"content-type": "text/event-stream",
|
|
183
|
+
"x-accel-buffering": "no"
|
|
183
184
|
}
|
|
184
185
|
});
|
|
185
186
|
});
|
|
@@ -740,7 +741,7 @@ var wrap = (source) => `
|
|
|
740
741
|
const userFn = (${source});
|
|
741
742
|
if (typeof userFn !== 'function') {
|
|
742
743
|
throw new Error(
|
|
743
|
-
'sandboxedHandler must evaluate to (args, ctx, actions) => result; got ' +
|
|
744
|
+
'sandboxedHandler must evaluate to (args, ctx, actions, unsafeHost) => result; got ' +
|
|
744
745
|
typeof userFn
|
|
745
746
|
);
|
|
746
747
|
}
|
|
@@ -752,12 +753,24 @@ var wrap = (source) => `
|
|
|
752
753
|
now: () => __dispatch(__callId, 'now'),
|
|
753
754
|
fetch: (url, init) => __dispatch(__callId, 'fetch', url, init)
|
|
754
755
|
};
|
|
755
|
-
|
|
756
|
+
// Escape hatch \u2014 host fns the mutation explicitly opted in to.
|
|
757
|
+
// The Proxy means every property access is a host call; the
|
|
758
|
+
// engine throws if the property name isn't declared in the
|
|
759
|
+
// mutation's sandbox.unsafeHost map.
|
|
760
|
+
const unsafeHost = new Proxy({}, {
|
|
761
|
+
get: (_target, fnName) => {
|
|
762
|
+
if (typeof fnName !== 'string') return undefined;
|
|
763
|
+
return (...callArgs) =>
|
|
764
|
+
__dispatch(__callId, 'unsafeHost', fnName, callArgs);
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
return userFn(args, ctx, actions, unsafeHost);
|
|
756
768
|
}
|
|
757
769
|
`;
|
|
758
770
|
var compile = async (source, config, bridgeFetch) => {
|
|
759
771
|
const { Reference, createIsolatedRunner, resolveIsolatePolicy } = await loadIsolatedJsc();
|
|
760
772
|
const callMap = new Map;
|
|
773
|
+
const unsafeHost = config.unsafeHost;
|
|
761
774
|
const dispatch = new Reference((callId, op, ...rest) => {
|
|
762
775
|
const a = callMap.get(callId);
|
|
763
776
|
if (a === undefined) {
|
|
@@ -776,6 +789,14 @@ var compile = async (source, config, bridgeFetch) => {
|
|
|
776
789
|
return a.now();
|
|
777
790
|
case "fetch":
|
|
778
791
|
return runBridgeFetch(bridgeFetch, rest[0], rest[1]);
|
|
792
|
+
case "unsafeHost": {
|
|
793
|
+
const fnName = rest[0];
|
|
794
|
+
const callArgs = rest[1] ?? [];
|
|
795
|
+
if (unsafeHost === undefined || typeof unsafeHost[fnName] !== "function") {
|
|
796
|
+
throw new Error(`sandboxedHandler called unsafeHost.${fnName}() but it was not declared in the mutation's sandbox.unsafeHost config. Declare it (and only the host fns you intend to expose) to opt in to the escape hatch.`);
|
|
797
|
+
}
|
|
798
|
+
return unsafeHost[fnName](...callArgs);
|
|
799
|
+
}
|
|
779
800
|
default:
|
|
780
801
|
throw new Error(`unknown sandbox action op: ${String(op)}`);
|
|
781
802
|
}
|
|
@@ -2478,7 +2499,8 @@ var syncCdc = ({
|
|
|
2478
2499
|
headers: {
|
|
2479
2500
|
"cache-control": "no-cache, no-transform",
|
|
2480
2501
|
connection: "keep-alive",
|
|
2481
|
-
"content-type": "text/event-stream"
|
|
2502
|
+
"content-type": "text/event-stream",
|
|
2503
|
+
"x-accel-buffering": "no"
|
|
2482
2504
|
}
|
|
2483
2505
|
});
|
|
2484
2506
|
});
|
|
@@ -2593,7 +2615,8 @@ data: ${JSON.stringify(event)}
|
|
|
2593
2615
|
headers: {
|
|
2594
2616
|
"cache-control": "no-cache, no-transform",
|
|
2595
2617
|
connection: "keep-alive",
|
|
2596
|
-
"content-type": "text/event-stream"
|
|
2618
|
+
"content-type": "text/event-stream",
|
|
2619
|
+
"x-accel-buffering": "no"
|
|
2597
2620
|
}
|
|
2598
2621
|
});
|
|
2599
2622
|
});
|
|
@@ -2675,5 +2698,5 @@ export {
|
|
|
2675
2698
|
createPresenceHub
|
|
2676
2699
|
};
|
|
2677
2700
|
|
|
2678
|
-
//# debugId=
|
|
2701
|
+
//# debugId=D6679B754081BCC764756E2164756E21
|
|
2679
2702
|
//# sourceMappingURL=index.js.map
|