@dudousxd/nestjs-durable-store-mikro-orm 0.17.0 → 0.18.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 CHANGED
@@ -84,11 +84,28 @@ var BufferedSignalEntity = class {
84
84
  token;
85
85
  payload;
86
86
  };
87
+ function namespaceFilterCond(args) {
88
+ if (args.namespace === void 0) return {};
89
+ return {
90
+ namespace: args.namespace
91
+ };
92
+ }
93
+ __name(namespaceFilterCond, "namespaceFilterCond");
87
94
  function durableEntities(options = {}) {
88
95
  const col = columnNamer(options.naming ?? "snake_case");
89
96
  const workflowRuns = new EntitySchema({
90
97
  class: WorkflowRunEntity,
91
98
  tableName: "durable_workflow_runs",
99
+ // Global filter: confines every read to the tenant's namespace when the store is scoped.
100
+ // The `cond` returns `{}` (no-op) when the `namespace` arg is `undefined`, so an unscoped
101
+ // (operator / control-plane) store sees all rows unchanged — existing behaviour is preserved.
102
+ filters: {
103
+ namespace: {
104
+ name: "namespace",
105
+ cond: namespaceFilterCond,
106
+ default: true
107
+ }
108
+ },
92
109
  // Indexes mirror the Prisma adapter so a store swap keeps the same plan. The poller hits these
93
110
  // every tick: `(status, wakeAt)` serves both the due-timer scan (status='suspended' AND wakeAt<=now)
94
111
  // and, via its `status` prefix, the pending/incomplete status lookups; `(workflow, status)` serves
@@ -614,25 +631,51 @@ async function alignDurableCollation(orm) {
614
631
  __name(alignDurableCollation, "alignDurableCollation");
615
632
 
616
633
  // src/mikro-orm-state-store.ts
617
- var MikroOrmStateStore = class {
634
+ var MikroOrmStateStore = class _MikroOrmStateStore {
618
635
  static {
619
636
  __name(this, "MikroOrmStateStore");
620
637
  }
621
638
  orm;
622
- constructor(orm) {
639
+ scopeNamespace;
640
+ constructor(orm, opts) {
623
641
  this.orm = orm;
642
+ this.scopeNamespace = opts?.scope?.namespace;
643
+ }
644
+ /**
645
+ * Derive a store confined to a single tenant `scope.namespace`, sharing this store's ORM (no new
646
+ * connection). Turns the operator (unscoped) store into a tenant-boundary view at wiring time —
647
+ * used by the NestJS module's `scopeReads` option, which receives a pre-built store and can't
648
+ * reconstruct it. Pass `{ namespace: undefined }` for the unscoped operator view.
649
+ */
650
+ withScope(scope) {
651
+ return new _MikroOrmStateStore(this.orm, {
652
+ scope
653
+ });
654
+ }
655
+ /**
656
+ * Create a forked EntityManager and activate the `namespace` global filter with the store's
657
+ * scope (if any). All read operations go through this so the tenant boundary is applied
658
+ * uniformly. Write operations (nativeDelete, nativeUpdate, upsert, insertMany) bypass global
659
+ * filters in MikroORM and are unaffected.
660
+ */
661
+ fork() {
662
+ const em = this.orm.em.fork();
663
+ em.setFilterParams("namespace", {
664
+ namespace: this.scopeNamespace
665
+ });
666
+ return em;
624
667
  }
625
668
  async ensureSchema() {
626
669
  await ensureMikroOrmDurableSchema(this.orm);
627
670
  }
628
671
  async createRun(run) {
629
- const em = this.orm.em.fork();
672
+ const em = this.fork();
630
673
  em.create(WorkflowRunEntity, toRunEntity(run));
631
674
  await em.flush();
632
675
  await this.reindexAttributes(run.id, run.searchAttributes);
633
676
  }
634
677
  async updateRun(runId, patch) {
635
- const em = this.orm.em.fork();
678
+ const em = this.fork();
636
679
  const entity = await em.findOneOrFail(WorkflowRunEntity, {
637
680
  id: runId
638
681
  });
@@ -646,7 +689,7 @@ var MikroOrmStateStore = class {
646
689
  /** Rewrite a run's normalized attribute rows: delete the old set, insert the current one. Mirrors
647
690
  * the in-memory store's reindex so the side-table always reflects the run's live searchAttributes. */
648
691
  async reindexAttributes(runId, attributes, forked) {
649
- const em = forked ?? this.orm.em.fork();
692
+ const em = forked ?? this.fork();
650
693
  await em.nativeDelete(RunAttributeEntity, {
651
694
  runId
652
695
  });
@@ -654,14 +697,14 @@ var MikroOrmStateStore = class {
654
697
  if (rows.length) await em.insertMany(RunAttributeEntity, rows);
655
698
  }
656
699
  async getRun(runId) {
657
- const em = this.orm.em.fork();
700
+ const em = this.fork();
658
701
  const entity = await em.findOne(WorkflowRunEntity, {
659
702
  id: runId
660
703
  });
661
704
  return entity ? fromRunEntity(entity) : null;
662
705
  }
663
706
  async deleteRun(runId) {
664
- const em = this.orm.em.fork();
707
+ const em = this.fork();
665
708
  await em.nativeDelete(StepCheckpointEntity, {
666
709
  runId
667
710
  });
@@ -677,7 +720,7 @@ var MikroOrmStateStore = class {
677
720
  }
678
721
  async pruneTerminalRuns(policy, nowMs, limit) {
679
722
  if (policy.statuses.length === 0 || limit <= 0) return 0;
680
- const em = this.orm.em.fork();
723
+ const em = this.fork();
681
724
  const status = {
682
725
  $in: policy.statuses
683
726
  };
@@ -743,7 +786,7 @@ var MikroOrmStateStore = class {
743
786
  return idList.length;
744
787
  }
745
788
  async getCheckpoint(runId, seq) {
746
- const em = this.orm.em.fork();
789
+ const em = this.fork();
747
790
  const entity = await em.findOne(StepCheckpointEntity, {
748
791
  runId,
749
792
  seq
@@ -751,12 +794,12 @@ var MikroOrmStateStore = class {
751
794
  return entity ? fromCheckpointEntity(entity) : null;
752
795
  }
753
796
  async saveCheckpoint(checkpoint) {
754
- const em = this.orm.em.fork();
797
+ const em = this.fork();
755
798
  await em.upsert(StepCheckpointEntity, toCheckpointEntity(checkpoint));
756
799
  await em.flush();
757
800
  }
758
801
  async transaction(work) {
759
- return this.orm.em.fork().transactional(async (em) => work({
802
+ return this.fork().transactional(async (em) => work({
760
803
  raw: em,
761
804
  saveCheckpoint: /* @__PURE__ */ __name(async (cp) => {
762
805
  await em.upsert(StepCheckpointEntity, toCheckpointEntity(cp));
@@ -764,7 +807,7 @@ var MikroOrmStateStore = class {
764
807
  }));
765
808
  }
766
809
  async listIncompleteRuns(namespace) {
767
- const em = this.orm.em.fork();
810
+ const em = this.fork();
768
811
  const rows = await em.find(WorkflowRunEntity, {
769
812
  status: {
770
813
  $in: [
@@ -779,7 +822,7 @@ var MikroOrmStateStore = class {
779
822
  return rows.map(fromRunEntity);
780
823
  }
781
824
  async listPendingRuns(limit, namespace) {
782
- const em = this.orm.em.fork();
825
+ const em = this.fork();
783
826
  const rows = await em.find(WorkflowRunEntity, {
784
827
  status: "pending",
785
828
  ...namespace !== void 0 ? {
@@ -794,7 +837,7 @@ var MikroOrmStateStore = class {
794
837
  return rows.map(fromRunEntity);
795
838
  }
796
839
  async listDueTimers(nowMs, namespace) {
797
- const em = this.orm.em.fork();
840
+ const em = this.fork();
798
841
  const rows = await em.find(WorkflowRunEntity, {
799
842
  status: "suspended",
800
843
  wakeAt: {
@@ -808,7 +851,7 @@ var MikroOrmStateStore = class {
808
851
  return rows.map(fromRunEntity);
809
852
  }
810
853
  async tryLockRun(runId, owner, leaseUntilMs, nowMs) {
811
- const em = this.orm.em.fork();
854
+ const em = this.fork();
812
855
  const affected = await em.nativeUpdate(WorkflowRunEntity, {
813
856
  id: runId,
814
857
  $or: [
@@ -828,7 +871,7 @@ var MikroOrmStateStore = class {
828
871
  return affected === 1;
829
872
  }
830
873
  async releaseRunLock(runId) {
831
- const em = this.orm.em.fork();
874
+ const em = this.fork();
832
875
  await em.nativeUpdate(WorkflowRunEntity, {
833
876
  id: runId
834
877
  }, {
@@ -837,7 +880,7 @@ var MikroOrmStateStore = class {
837
880
  });
838
881
  }
839
882
  async renewRunLock(runId, owner, leaseUntilMs) {
840
- const em = this.orm.em.fork();
883
+ const em = this.fork();
841
884
  const affected = await em.nativeUpdate(WorkflowRunEntity, {
842
885
  id: runId,
843
886
  lockedBy: owner
@@ -847,7 +890,7 @@ var MikroOrmStateStore = class {
847
890
  return affected === 1;
848
891
  }
849
892
  async listRuns(query) {
850
- const em = this.orm.em.fork();
893
+ const em = this.fork();
851
894
  const where = {};
852
895
  if (query.workflow) where.workflow = query.workflow;
853
896
  if (query.namespace !== void 0) where.namespace = query.namespace;
@@ -873,6 +916,7 @@ var MikroOrmStateStore = class {
873
916
  createdAt: "desc"
874
917
  };
875
918
  if (query.tag || query.attributes?.length) {
919
+ if (this.scopeNamespace !== void 0) where.namespace = this.scopeNamespace;
876
920
  const quote = this.idQuote(em);
877
921
  const cols = this.attributeColumns(em);
878
922
  const qb = em.createQueryBuilder(WorkflowRunEntity, "r").where(where).orderBy(orderBy);
@@ -955,7 +999,7 @@ var MikroOrmStateStore = class {
955
999
  return (id) => `${ch}${id}${ch}`;
956
1000
  }
957
1001
  async listCheckpoints(runId) {
958
- const em = this.orm.em.fork();
1002
+ const em = this.fork();
959
1003
  const rows = await em.find(StepCheckpointEntity, {
960
1004
  runId
961
1005
  }, {
@@ -966,7 +1010,7 @@ var MikroOrmStateStore = class {
966
1010
  return rows.map(fromCheckpointEntity);
967
1011
  }
968
1012
  async getLatestCheckpointByName(runId, name) {
969
- const em = this.orm.em.fork();
1013
+ const em = this.fork();
970
1014
  const entity = await em.findOne(StepCheckpointEntity, {
971
1015
  runId,
972
1016
  name
@@ -979,7 +1023,7 @@ var MikroOrmStateStore = class {
979
1023
  }
980
1024
  async listCheckpointsByNamePrefix(runId, prefixes) {
981
1025
  if (prefixes.length === 0) return [];
982
- const em = this.orm.em.fork();
1026
+ const em = this.fork();
983
1027
  const rows = await em.find(StepCheckpointEntity, {
984
1028
  runId,
985
1029
  $or: prefixes.map((p) => ({
@@ -995,7 +1039,7 @@ var MikroOrmStateStore = class {
995
1039
  return rows.map(fromCheckpointEntity);
996
1040
  }
997
1041
  async putSignalWaiter(waiter) {
998
- const em = this.orm.em.fork();
1042
+ const em = this.fork();
999
1043
  await em.upsert(SignalWaiterEntity, {
1000
1044
  token: waiter.token,
1001
1045
  runId: waiter.runId,
@@ -1005,7 +1049,7 @@ var MikroOrmStateStore = class {
1005
1049
  await em.flush();
1006
1050
  }
1007
1051
  async takeSignalWaiter(token) {
1008
- const em = this.orm.em.fork();
1052
+ const em = this.fork();
1009
1053
  const entity = await em.findOne(SignalWaiterEntity, {
1010
1054
  token
1011
1055
  });
@@ -1020,7 +1064,7 @@ var MikroOrmStateStore = class {
1020
1064
  return waiter;
1021
1065
  }
1022
1066
  async listSignalWaiters(prefix) {
1023
- const em = this.orm.em.fork();
1067
+ const em = this.fork();
1024
1068
  const rows = await em.find(SignalWaiterEntity, {
1025
1069
  token: {
1026
1070
  $like: `${prefix}%`
@@ -1034,7 +1078,7 @@ var MikroOrmStateStore = class {
1034
1078
  }));
1035
1079
  }
1036
1080
  async bufferSignal(token, payload) {
1037
- const em = this.orm.em.fork();
1081
+ const em = this.fork();
1038
1082
  const e = new BufferedSignalEntity();
1039
1083
  e.token = token;
1040
1084
  e.payload = payload ?? null;
@@ -1042,7 +1086,7 @@ var MikroOrmStateStore = class {
1042
1086
  await em.flush();
1043
1087
  }
1044
1088
  async takeBufferedSignal(token) {
1045
- const em = this.orm.em.fork();
1089
+ const em = this.fork();
1046
1090
  const entity = await em.findOne(BufferedSignalEntity, {
1047
1091
  token
1048
1092
  }, {