@dudousxd/nestjs-durable-store-mikro-orm 0.17.0 → 0.18.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/index.cjs CHANGED
@@ -116,11 +116,35 @@ 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
+ // Optional args: the store's own forks set the scope via `setFilterParams` (undefined =
140
+ // operator, sees all), but an external consumer that shares this ORM (an operator app reading
141
+ // WorkflowRunEntity through its own EntityManager) never sets it. Without this, MikroORM
142
+ // throws "No arguments provided for filter 'namespace'" on those queries. `args: false` makes
143
+ // the param optional — cond then receives `undefined` and no-ops (sees all), which is exactly
144
+ // the operator view, while a scoped store still passes its namespace through.
145
+ args: false
146
+ }
147
+ },
124
148
  // Indexes mirror the Prisma adapter so a store swap keeps the same plan. The poller hits these
125
149
  // every tick: `(status, wakeAt)` serves both the due-timer scan (status='suspended' AND wakeAt<=now)
126
150
  // and, via its `status` prefix, the pending/incomplete status lookups; `(workflow, status)` serves
@@ -646,25 +670,51 @@ async function alignDurableCollation(orm) {
646
670
  __name(alignDurableCollation, "alignDurableCollation");
647
671
 
648
672
  // src/mikro-orm-state-store.ts
649
- var MikroOrmStateStore = class {
673
+ var MikroOrmStateStore = class _MikroOrmStateStore {
650
674
  static {
651
675
  __name(this, "MikroOrmStateStore");
652
676
  }
653
677
  orm;
654
- constructor(orm) {
678
+ scopeNamespace;
679
+ constructor(orm, opts) {
655
680
  this.orm = orm;
681
+ this.scopeNamespace = opts?.scope?.namespace;
682
+ }
683
+ /**
684
+ * Derive a store confined to a single tenant `scope.namespace`, sharing this store's ORM (no new
685
+ * connection). Turns the operator (unscoped) store into a tenant-boundary view at wiring time —
686
+ * used by the NestJS module's `scopeReads` option, which receives a pre-built store and can't
687
+ * reconstruct it. Pass `{ namespace: undefined }` for the unscoped operator view.
688
+ */
689
+ withScope(scope) {
690
+ return new _MikroOrmStateStore(this.orm, {
691
+ scope
692
+ });
693
+ }
694
+ /**
695
+ * Create a forked EntityManager and activate the `namespace` global filter with the store's
696
+ * scope (if any). All read operations go through this so the tenant boundary is applied
697
+ * uniformly. Write operations (nativeDelete, nativeUpdate, upsert, insertMany) bypass global
698
+ * filters in MikroORM and are unaffected.
699
+ */
700
+ fork() {
701
+ const em = this.orm.em.fork();
702
+ em.setFilterParams("namespace", {
703
+ namespace: this.scopeNamespace
704
+ });
705
+ return em;
656
706
  }
657
707
  async ensureSchema() {
658
708
  await ensureMikroOrmDurableSchema(this.orm);
659
709
  }
660
710
  async createRun(run) {
661
- const em = this.orm.em.fork();
711
+ const em = this.fork();
662
712
  em.create(WorkflowRunEntity, toRunEntity(run));
663
713
  await em.flush();
664
714
  await this.reindexAttributes(run.id, run.searchAttributes);
665
715
  }
666
716
  async updateRun(runId, patch) {
667
- const em = this.orm.em.fork();
717
+ const em = this.fork();
668
718
  const entity = await em.findOneOrFail(WorkflowRunEntity, {
669
719
  id: runId
670
720
  });
@@ -678,7 +728,7 @@ var MikroOrmStateStore = class {
678
728
  /** Rewrite a run's normalized attribute rows: delete the old set, insert the current one. Mirrors
679
729
  * the in-memory store's reindex so the side-table always reflects the run's live searchAttributes. */
680
730
  async reindexAttributes(runId, attributes, forked) {
681
- const em = forked ?? this.orm.em.fork();
731
+ const em = forked ?? this.fork();
682
732
  await em.nativeDelete(RunAttributeEntity, {
683
733
  runId
684
734
  });
@@ -686,14 +736,14 @@ var MikroOrmStateStore = class {
686
736
  if (rows.length) await em.insertMany(RunAttributeEntity, rows);
687
737
  }
688
738
  async getRun(runId) {
689
- const em = this.orm.em.fork();
739
+ const em = this.fork();
690
740
  const entity = await em.findOne(WorkflowRunEntity, {
691
741
  id: runId
692
742
  });
693
743
  return entity ? fromRunEntity(entity) : null;
694
744
  }
695
745
  async deleteRun(runId) {
696
- const em = this.orm.em.fork();
746
+ const em = this.fork();
697
747
  await em.nativeDelete(StepCheckpointEntity, {
698
748
  runId
699
749
  });
@@ -709,7 +759,7 @@ var MikroOrmStateStore = class {
709
759
  }
710
760
  async pruneTerminalRuns(policy, nowMs, limit) {
711
761
  if (policy.statuses.length === 0 || limit <= 0) return 0;
712
- const em = this.orm.em.fork();
762
+ const em = this.fork();
713
763
  const status = {
714
764
  $in: policy.statuses
715
765
  };
@@ -775,7 +825,7 @@ var MikroOrmStateStore = class {
775
825
  return idList.length;
776
826
  }
777
827
  async getCheckpoint(runId, seq) {
778
- const em = this.orm.em.fork();
828
+ const em = this.fork();
779
829
  const entity = await em.findOne(StepCheckpointEntity, {
780
830
  runId,
781
831
  seq
@@ -783,12 +833,12 @@ var MikroOrmStateStore = class {
783
833
  return entity ? fromCheckpointEntity(entity) : null;
784
834
  }
785
835
  async saveCheckpoint(checkpoint) {
786
- const em = this.orm.em.fork();
836
+ const em = this.fork();
787
837
  await em.upsert(StepCheckpointEntity, toCheckpointEntity(checkpoint));
788
838
  await em.flush();
789
839
  }
790
840
  async transaction(work) {
791
- return this.orm.em.fork().transactional(async (em) => work({
841
+ return this.fork().transactional(async (em) => work({
792
842
  raw: em,
793
843
  saveCheckpoint: /* @__PURE__ */ __name(async (cp) => {
794
844
  await em.upsert(StepCheckpointEntity, toCheckpointEntity(cp));
@@ -796,7 +846,7 @@ var MikroOrmStateStore = class {
796
846
  }));
797
847
  }
798
848
  async listIncompleteRuns(namespace) {
799
- const em = this.orm.em.fork();
849
+ const em = this.fork();
800
850
  const rows = await em.find(WorkflowRunEntity, {
801
851
  status: {
802
852
  $in: [
@@ -811,7 +861,7 @@ var MikroOrmStateStore = class {
811
861
  return rows.map(fromRunEntity);
812
862
  }
813
863
  async listPendingRuns(limit, namespace) {
814
- const em = this.orm.em.fork();
864
+ const em = this.fork();
815
865
  const rows = await em.find(WorkflowRunEntity, {
816
866
  status: "pending",
817
867
  ...namespace !== void 0 ? {
@@ -826,7 +876,7 @@ var MikroOrmStateStore = class {
826
876
  return rows.map(fromRunEntity);
827
877
  }
828
878
  async listDueTimers(nowMs, namespace) {
829
- const em = this.orm.em.fork();
879
+ const em = this.fork();
830
880
  const rows = await em.find(WorkflowRunEntity, {
831
881
  status: "suspended",
832
882
  wakeAt: {
@@ -840,7 +890,7 @@ var MikroOrmStateStore = class {
840
890
  return rows.map(fromRunEntity);
841
891
  }
842
892
  async tryLockRun(runId, owner, leaseUntilMs, nowMs) {
843
- const em = this.orm.em.fork();
893
+ const em = this.fork();
844
894
  const affected = await em.nativeUpdate(WorkflowRunEntity, {
845
895
  id: runId,
846
896
  $or: [
@@ -860,7 +910,7 @@ var MikroOrmStateStore = class {
860
910
  return affected === 1;
861
911
  }
862
912
  async releaseRunLock(runId) {
863
- const em = this.orm.em.fork();
913
+ const em = this.fork();
864
914
  await em.nativeUpdate(WorkflowRunEntity, {
865
915
  id: runId
866
916
  }, {
@@ -869,7 +919,7 @@ var MikroOrmStateStore = class {
869
919
  });
870
920
  }
871
921
  async renewRunLock(runId, owner, leaseUntilMs) {
872
- const em = this.orm.em.fork();
922
+ const em = this.fork();
873
923
  const affected = await em.nativeUpdate(WorkflowRunEntity, {
874
924
  id: runId,
875
925
  lockedBy: owner
@@ -879,7 +929,7 @@ var MikroOrmStateStore = class {
879
929
  return affected === 1;
880
930
  }
881
931
  async listRuns(query) {
882
- const em = this.orm.em.fork();
932
+ const em = this.fork();
883
933
  const where = {};
884
934
  if (query.workflow) where.workflow = query.workflow;
885
935
  if (query.namespace !== void 0) where.namespace = query.namespace;
@@ -905,6 +955,7 @@ var MikroOrmStateStore = class {
905
955
  createdAt: "desc"
906
956
  };
907
957
  if (query.tag || query.attributes?.length) {
958
+ if (this.scopeNamespace !== void 0) where.namespace = this.scopeNamespace;
908
959
  const quote = this.idQuote(em);
909
960
  const cols = this.attributeColumns(em);
910
961
  const qb = em.createQueryBuilder(WorkflowRunEntity, "r").where(where).orderBy(orderBy);
@@ -987,7 +1038,7 @@ var MikroOrmStateStore = class {
987
1038
  return (id) => `${ch}${id}${ch}`;
988
1039
  }
989
1040
  async listCheckpoints(runId) {
990
- const em = this.orm.em.fork();
1041
+ const em = this.fork();
991
1042
  const rows = await em.find(StepCheckpointEntity, {
992
1043
  runId
993
1044
  }, {
@@ -998,7 +1049,7 @@ var MikroOrmStateStore = class {
998
1049
  return rows.map(fromCheckpointEntity);
999
1050
  }
1000
1051
  async getLatestCheckpointByName(runId, name) {
1001
- const em = this.orm.em.fork();
1052
+ const em = this.fork();
1002
1053
  const entity = await em.findOne(StepCheckpointEntity, {
1003
1054
  runId,
1004
1055
  name
@@ -1011,7 +1062,7 @@ var MikroOrmStateStore = class {
1011
1062
  }
1012
1063
  async listCheckpointsByNamePrefix(runId, prefixes) {
1013
1064
  if (prefixes.length === 0) return [];
1014
- const em = this.orm.em.fork();
1065
+ const em = this.fork();
1015
1066
  const rows = await em.find(StepCheckpointEntity, {
1016
1067
  runId,
1017
1068
  $or: prefixes.map((p) => ({
@@ -1027,7 +1078,7 @@ var MikroOrmStateStore = class {
1027
1078
  return rows.map(fromCheckpointEntity);
1028
1079
  }
1029
1080
  async putSignalWaiter(waiter) {
1030
- const em = this.orm.em.fork();
1081
+ const em = this.fork();
1031
1082
  await em.upsert(SignalWaiterEntity, {
1032
1083
  token: waiter.token,
1033
1084
  runId: waiter.runId,
@@ -1037,7 +1088,7 @@ var MikroOrmStateStore = class {
1037
1088
  await em.flush();
1038
1089
  }
1039
1090
  async takeSignalWaiter(token) {
1040
- const em = this.orm.em.fork();
1091
+ const em = this.fork();
1041
1092
  const entity = await em.findOne(SignalWaiterEntity, {
1042
1093
  token
1043
1094
  });
@@ -1052,7 +1103,7 @@ var MikroOrmStateStore = class {
1052
1103
  return waiter;
1053
1104
  }
1054
1105
  async listSignalWaiters(prefix) {
1055
- const em = this.orm.em.fork();
1106
+ const em = this.fork();
1056
1107
  const rows = await em.find(SignalWaiterEntity, {
1057
1108
  token: {
1058
1109
  $like: `${prefix}%`
@@ -1066,7 +1117,7 @@ var MikroOrmStateStore = class {
1066
1117
  }));
1067
1118
  }
1068
1119
  async bufferSignal(token, payload) {
1069
- const em = this.orm.em.fork();
1120
+ const em = this.fork();
1070
1121
  const e = new BufferedSignalEntity();
1071
1122
  e.token = token;
1072
1123
  e.payload = payload ?? null;
@@ -1074,7 +1125,7 @@ var MikroOrmStateStore = class {
1074
1125
  await em.flush();
1075
1126
  }
1076
1127
  async takeBufferedSignal(token) {
1077
- const em = this.orm.em.fork();
1128
+ const em = this.fork();
1078
1129
  const entity = await em.findOne(BufferedSignalEntity, {
1079
1130
  token
1080
1131
  }, {