@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.cjs CHANGED
@@ -116,11 +116,28 @@ var BufferedSignalEntity = class {
116
116
  token;
117
117
  payload;
118
118
  };
119
+ function namespaceFilterCond(args) {
120
+ if (args.namespace === void 0) return {};
121
+ return {
122
+ namespace: args.namespace
123
+ };
124
+ }
125
+ __name(namespaceFilterCond, "namespaceFilterCond");
119
126
  function durableEntities(options = {}) {
120
127
  const col = columnNamer(options.naming ?? "snake_case");
121
128
  const workflowRuns = new import_core.EntitySchema({
122
129
  class: WorkflowRunEntity,
123
130
  tableName: "durable_workflow_runs",
131
+ // Global filter: confines every read to the tenant's namespace when the store is scoped.
132
+ // The `cond` returns `{}` (no-op) when the `namespace` arg is `undefined`, so an unscoped
133
+ // (operator / control-plane) store sees all rows unchanged — existing behaviour is preserved.
134
+ filters: {
135
+ namespace: {
136
+ name: "namespace",
137
+ cond: namespaceFilterCond,
138
+ default: true
139
+ }
140
+ },
124
141
  // Indexes mirror the Prisma adapter so a store swap keeps the same plan. The poller hits these
125
142
  // every tick: `(status, wakeAt)` serves both the due-timer scan (status='suspended' AND wakeAt<=now)
126
143
  // and, via its `status` prefix, the pending/incomplete status lookups; `(workflow, status)` serves
@@ -646,25 +663,51 @@ async function alignDurableCollation(orm) {
646
663
  __name(alignDurableCollation, "alignDurableCollation");
647
664
 
648
665
  // src/mikro-orm-state-store.ts
649
- var MikroOrmStateStore = class {
666
+ var MikroOrmStateStore = class _MikroOrmStateStore {
650
667
  static {
651
668
  __name(this, "MikroOrmStateStore");
652
669
  }
653
670
  orm;
654
- constructor(orm) {
671
+ scopeNamespace;
672
+ constructor(orm, opts) {
655
673
  this.orm = orm;
674
+ this.scopeNamespace = opts?.scope?.namespace;
675
+ }
676
+ /**
677
+ * Derive a store confined to a single tenant `scope.namespace`, sharing this store's ORM (no new
678
+ * connection). Turns the operator (unscoped) store into a tenant-boundary view at wiring time —
679
+ * used by the NestJS module's `scopeReads` option, which receives a pre-built store and can't
680
+ * reconstruct it. Pass `{ namespace: undefined }` for the unscoped operator view.
681
+ */
682
+ withScope(scope) {
683
+ return new _MikroOrmStateStore(this.orm, {
684
+ scope
685
+ });
686
+ }
687
+ /**
688
+ * Create a forked EntityManager and activate the `namespace` global filter with the store's
689
+ * scope (if any). All read operations go through this so the tenant boundary is applied
690
+ * uniformly. Write operations (nativeDelete, nativeUpdate, upsert, insertMany) bypass global
691
+ * filters in MikroORM and are unaffected.
692
+ */
693
+ fork() {
694
+ const em = this.orm.em.fork();
695
+ em.setFilterParams("namespace", {
696
+ namespace: this.scopeNamespace
697
+ });
698
+ return em;
656
699
  }
657
700
  async ensureSchema() {
658
701
  await ensureMikroOrmDurableSchema(this.orm);
659
702
  }
660
703
  async createRun(run) {
661
- const em = this.orm.em.fork();
704
+ const em = this.fork();
662
705
  em.create(WorkflowRunEntity, toRunEntity(run));
663
706
  await em.flush();
664
707
  await this.reindexAttributes(run.id, run.searchAttributes);
665
708
  }
666
709
  async updateRun(runId, patch) {
667
- const em = this.orm.em.fork();
710
+ const em = this.fork();
668
711
  const entity = await em.findOneOrFail(WorkflowRunEntity, {
669
712
  id: runId
670
713
  });
@@ -678,7 +721,7 @@ var MikroOrmStateStore = class {
678
721
  /** Rewrite a run's normalized attribute rows: delete the old set, insert the current one. Mirrors
679
722
  * the in-memory store's reindex so the side-table always reflects the run's live searchAttributes. */
680
723
  async reindexAttributes(runId, attributes, forked) {
681
- const em = forked ?? this.orm.em.fork();
724
+ const em = forked ?? this.fork();
682
725
  await em.nativeDelete(RunAttributeEntity, {
683
726
  runId
684
727
  });
@@ -686,14 +729,14 @@ var MikroOrmStateStore = class {
686
729
  if (rows.length) await em.insertMany(RunAttributeEntity, rows);
687
730
  }
688
731
  async getRun(runId) {
689
- const em = this.orm.em.fork();
732
+ const em = this.fork();
690
733
  const entity = await em.findOne(WorkflowRunEntity, {
691
734
  id: runId
692
735
  });
693
736
  return entity ? fromRunEntity(entity) : null;
694
737
  }
695
738
  async deleteRun(runId) {
696
- const em = this.orm.em.fork();
739
+ const em = this.fork();
697
740
  await em.nativeDelete(StepCheckpointEntity, {
698
741
  runId
699
742
  });
@@ -709,7 +752,7 @@ var MikroOrmStateStore = class {
709
752
  }
710
753
  async pruneTerminalRuns(policy, nowMs, limit) {
711
754
  if (policy.statuses.length === 0 || limit <= 0) return 0;
712
- const em = this.orm.em.fork();
755
+ const em = this.fork();
713
756
  const status = {
714
757
  $in: policy.statuses
715
758
  };
@@ -775,7 +818,7 @@ var MikroOrmStateStore = class {
775
818
  return idList.length;
776
819
  }
777
820
  async getCheckpoint(runId, seq) {
778
- const em = this.orm.em.fork();
821
+ const em = this.fork();
779
822
  const entity = await em.findOne(StepCheckpointEntity, {
780
823
  runId,
781
824
  seq
@@ -783,12 +826,12 @@ var MikroOrmStateStore = class {
783
826
  return entity ? fromCheckpointEntity(entity) : null;
784
827
  }
785
828
  async saveCheckpoint(checkpoint) {
786
- const em = this.orm.em.fork();
829
+ const em = this.fork();
787
830
  await em.upsert(StepCheckpointEntity, toCheckpointEntity(checkpoint));
788
831
  await em.flush();
789
832
  }
790
833
  async transaction(work) {
791
- return this.orm.em.fork().transactional(async (em) => work({
834
+ return this.fork().transactional(async (em) => work({
792
835
  raw: em,
793
836
  saveCheckpoint: /* @__PURE__ */ __name(async (cp) => {
794
837
  await em.upsert(StepCheckpointEntity, toCheckpointEntity(cp));
@@ -796,7 +839,7 @@ var MikroOrmStateStore = class {
796
839
  }));
797
840
  }
798
841
  async listIncompleteRuns(namespace) {
799
- const em = this.orm.em.fork();
842
+ const em = this.fork();
800
843
  const rows = await em.find(WorkflowRunEntity, {
801
844
  status: {
802
845
  $in: [
@@ -811,7 +854,7 @@ var MikroOrmStateStore = class {
811
854
  return rows.map(fromRunEntity);
812
855
  }
813
856
  async listPendingRuns(limit, namespace) {
814
- const em = this.orm.em.fork();
857
+ const em = this.fork();
815
858
  const rows = await em.find(WorkflowRunEntity, {
816
859
  status: "pending",
817
860
  ...namespace !== void 0 ? {
@@ -826,7 +869,7 @@ var MikroOrmStateStore = class {
826
869
  return rows.map(fromRunEntity);
827
870
  }
828
871
  async listDueTimers(nowMs, namespace) {
829
- const em = this.orm.em.fork();
872
+ const em = this.fork();
830
873
  const rows = await em.find(WorkflowRunEntity, {
831
874
  status: "suspended",
832
875
  wakeAt: {
@@ -840,7 +883,7 @@ var MikroOrmStateStore = class {
840
883
  return rows.map(fromRunEntity);
841
884
  }
842
885
  async tryLockRun(runId, owner, leaseUntilMs, nowMs) {
843
- const em = this.orm.em.fork();
886
+ const em = this.fork();
844
887
  const affected = await em.nativeUpdate(WorkflowRunEntity, {
845
888
  id: runId,
846
889
  $or: [
@@ -860,7 +903,7 @@ var MikroOrmStateStore = class {
860
903
  return affected === 1;
861
904
  }
862
905
  async releaseRunLock(runId) {
863
- const em = this.orm.em.fork();
906
+ const em = this.fork();
864
907
  await em.nativeUpdate(WorkflowRunEntity, {
865
908
  id: runId
866
909
  }, {
@@ -869,7 +912,7 @@ var MikroOrmStateStore = class {
869
912
  });
870
913
  }
871
914
  async renewRunLock(runId, owner, leaseUntilMs) {
872
- const em = this.orm.em.fork();
915
+ const em = this.fork();
873
916
  const affected = await em.nativeUpdate(WorkflowRunEntity, {
874
917
  id: runId,
875
918
  lockedBy: owner
@@ -879,7 +922,7 @@ var MikroOrmStateStore = class {
879
922
  return affected === 1;
880
923
  }
881
924
  async listRuns(query) {
882
- const em = this.orm.em.fork();
925
+ const em = this.fork();
883
926
  const where = {};
884
927
  if (query.workflow) where.workflow = query.workflow;
885
928
  if (query.namespace !== void 0) where.namespace = query.namespace;
@@ -905,6 +948,7 @@ var MikroOrmStateStore = class {
905
948
  createdAt: "desc"
906
949
  };
907
950
  if (query.tag || query.attributes?.length) {
951
+ if (this.scopeNamespace !== void 0) where.namespace = this.scopeNamespace;
908
952
  const quote = this.idQuote(em);
909
953
  const cols = this.attributeColumns(em);
910
954
  const qb = em.createQueryBuilder(WorkflowRunEntity, "r").where(where).orderBy(orderBy);
@@ -987,7 +1031,7 @@ var MikroOrmStateStore = class {
987
1031
  return (id) => `${ch}${id}${ch}`;
988
1032
  }
989
1033
  async listCheckpoints(runId) {
990
- const em = this.orm.em.fork();
1034
+ const em = this.fork();
991
1035
  const rows = await em.find(StepCheckpointEntity, {
992
1036
  runId
993
1037
  }, {
@@ -998,7 +1042,7 @@ var MikroOrmStateStore = class {
998
1042
  return rows.map(fromCheckpointEntity);
999
1043
  }
1000
1044
  async getLatestCheckpointByName(runId, name) {
1001
- const em = this.orm.em.fork();
1045
+ const em = this.fork();
1002
1046
  const entity = await em.findOne(StepCheckpointEntity, {
1003
1047
  runId,
1004
1048
  name
@@ -1011,7 +1055,7 @@ var MikroOrmStateStore = class {
1011
1055
  }
1012
1056
  async listCheckpointsByNamePrefix(runId, prefixes) {
1013
1057
  if (prefixes.length === 0) return [];
1014
- const em = this.orm.em.fork();
1058
+ const em = this.fork();
1015
1059
  const rows = await em.find(StepCheckpointEntity, {
1016
1060
  runId,
1017
1061
  $or: prefixes.map((p) => ({
@@ -1027,7 +1071,7 @@ var MikroOrmStateStore = class {
1027
1071
  return rows.map(fromCheckpointEntity);
1028
1072
  }
1029
1073
  async putSignalWaiter(waiter) {
1030
- const em = this.orm.em.fork();
1074
+ const em = this.fork();
1031
1075
  await em.upsert(SignalWaiterEntity, {
1032
1076
  token: waiter.token,
1033
1077
  runId: waiter.runId,
@@ -1037,7 +1081,7 @@ var MikroOrmStateStore = class {
1037
1081
  await em.flush();
1038
1082
  }
1039
1083
  async takeSignalWaiter(token) {
1040
- const em = this.orm.em.fork();
1084
+ const em = this.fork();
1041
1085
  const entity = await em.findOne(SignalWaiterEntity, {
1042
1086
  token
1043
1087
  });
@@ -1052,7 +1096,7 @@ var MikroOrmStateStore = class {
1052
1096
  return waiter;
1053
1097
  }
1054
1098
  async listSignalWaiters(prefix) {
1055
- const em = this.orm.em.fork();
1099
+ const em = this.fork();
1056
1100
  const rows = await em.find(SignalWaiterEntity, {
1057
1101
  token: {
1058
1102
  $like: `${prefix}%`
@@ -1066,7 +1110,7 @@ var MikroOrmStateStore = class {
1066
1110
  }));
1067
1111
  }
1068
1112
  async bufferSignal(token, payload) {
1069
- const em = this.orm.em.fork();
1113
+ const em = this.fork();
1070
1114
  const e = new BufferedSignalEntity();
1071
1115
  e.token = token;
1072
1116
  e.payload = payload ?? null;
@@ -1074,7 +1118,7 @@ var MikroOrmStateStore = class {
1074
1118
  await em.flush();
1075
1119
  }
1076
1120
  async takeBufferedSignal(token) {
1077
- const em = this.orm.em.fork();
1121
+ const em = this.fork();
1078
1122
  const entity = await em.findOne(BufferedSignalEntity, {
1079
1123
  token
1080
1124
  }, {