@absolutejs/sync 1.7.1 → 1.7.3

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.
@@ -60,7 +60,12 @@ export type SandboxConfig = {
60
60
  };
61
61
  /**
62
62
  * Build a lazy runner for one mutation's sandboxed source. The first call
63
- * compiles + spawns; subsequent calls reuse the isolate. If the isolate has
64
- * been disposed (timeout, memory cap), the next call re-spawns transparently.
63
+ * compiles + spawns; subsequent calls reuse the isolate AND the context
64
+ * (the router Reference is installed once on isolate creation; per-call
65
+ * cost is just two `setGlobal`s for `args` + `ctx`). Calls are serialised
66
+ * via a promise queue so the shared-slot router stays coherent. The
67
+ * context is recycled every {@link DEFAULT_RECYCLE_CONTEXT_AFTER} calls
68
+ * to bound JSC per-call metadata accumulation. If the isolate has been
69
+ * disposed (timeout, memory cap), the next call re-spawns transparently.
65
70
  */
66
71
  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
- (async () => {
728
+ (() => {
729
729
  const userFn = (${source});
730
730
  if (typeof userFn !== 'function') {
731
731
  throw new Error(
@@ -734,22 +734,57 @@ var wrap = (source) => `
734
734
  );
735
735
  }
736
736
  const actions = {
737
- insert: __syncActionInsert,
738
- update: __syncActionUpdate,
739
- delete: __syncActionDelete,
740
- change: __syncActionChange
737
+ insert: (table, data) => __syncAction('insert', table, data),
738
+ update: (table, data) => __syncAction('update', table, data),
739
+ delete: (table, row) => __syncAction('delete', table, row),
740
+ change: (collection, change) => __syncAction('change', collection, change)
741
741
  };
742
- return await userFn(args, ctx, actions);
742
+ return userFn(args, ctx, actions);
743
743
  })()
744
744
  `;
745
+ var DEFAULT_RECYCLE_CONTEXT_AFTER = 256;
746
+ var installRouter = async (context, currentActions, Reference) => {
747
+ const router = new Reference((op, ...rest) => {
748
+ const a = currentActions.value;
749
+ if (a === undefined) {
750
+ throw new Error("__syncAction invoked outside an active sandboxed call (shared-slot router)");
751
+ }
752
+ switch (op) {
753
+ case "insert":
754
+ return a.insert(rest[0], rest[1]);
755
+ case "update":
756
+ return a.update(rest[0], rest[1]);
757
+ case "delete":
758
+ return a.delete(rest[0], rest[1]);
759
+ case "change":
760
+ return a.change(rest[0], rest[1]);
761
+ default:
762
+ throw new Error(`unknown sandbox action op: ${String(op)}`);
763
+ }
764
+ });
765
+ await context.setGlobal("__syncAction", router);
766
+ };
745
767
  var compile = async (source, config) => {
746
- const { createIsolate } = await loadIsolatedJsc();
768
+ const { createIsolate, Reference } = await loadIsolatedJsc();
747
769
  const isolate = await createIsolate({
748
770
  backend: config.backend ?? "auto",
749
771
  memoryLimit: config.memoryLimit ?? 32
750
772
  });
751
773
  const script = await isolate.compileScript(wrap(source));
752
- return { isolate, script, timeoutMs: config.timeout ?? 5000 };
774
+ const context = await isolate.createContext();
775
+ const currentActions = {
776
+ value: undefined
777
+ };
778
+ await installRouter(context, currentActions, Reference);
779
+ return {
780
+ context,
781
+ currentActions,
782
+ isolate,
783
+ runQueue: Promise.resolve(undefined),
784
+ script,
785
+ servedCalls: 0,
786
+ timeoutMs: config.timeout ?? 5000
787
+ };
753
788
  };
754
789
  var makeSandboxedHandler = (source, config = {}) => {
755
790
  let pending;
@@ -763,23 +798,37 @@ var makeSandboxedHandler = (source, config = {}) => {
763
798
  pending = compile(source, config);
764
799
  return pending;
765
800
  };
766
- return async (args, ctx, actions) => {
801
+ const recycleContextIfNeeded = async (compiled) => {
802
+ if (compiled.servedCalls < DEFAULT_RECYCLE_CONTEXT_AFTER)
803
+ return;
767
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
+ return async (args, ctx, actions) => {
768
811
  const compiled = await getCompiled();
769
- const context = await compiled.isolate.createContext();
770
- try {
771
- await context.setGlobal("args", args);
772
- await context.setGlobal("ctx", ctx);
773
- await context.setGlobal("__syncActionInsert", new Reference((table, data) => actions.insert(table, data)));
774
- await context.setGlobal("__syncActionUpdate", new Reference((table, data) => actions.update(table, data)));
775
- await context.setGlobal("__syncActionDelete", new Reference((table, row) => actions.delete(table, row)));
776
- await context.setGlobal("__syncActionChange", new Reference((collection, change) => actions.change(collection, change)));
777
- return await compiled.script.run(context, {
778
- timeout: compiled.timeoutMs
779
- });
780
- } finally {
781
- await context.dispose().catch(() => {});
782
- }
812
+ const prev = compiled.runQueue;
813
+ const turn = prev.then(async () => {
814
+ await recycleContextIfNeeded(compiled);
815
+ compiled.currentActions.value = actions;
816
+ try {
817
+ await compiled.context.setGlobal("args", args);
818
+ await compiled.context.setGlobal("ctx", ctx);
819
+ const result = await compiled.script.run(compiled.context, {
820
+ timeout: compiled.timeoutMs
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;
783
832
  };
784
833
  };
785
834
 
@@ -2301,5 +2350,5 @@ export {
2301
2350
  createPresenceHub
2302
2351
  };
2303
2352
 
2304
- //# debugId=A95C21B5786D64AE64756E2164756E21
2353
+ //# debugId=5E4321DCD1FB65A764756E2164756E21
2305
2354
  //# sourceMappingURL=index.js.map