@dudousxd/nestjs-durable-store-mikro-orm 0.32.0 → 0.33.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
@@ -39,7 +39,8 @@ __export(index_exports, {
39
39
  WorkflowRunRepository: () => WorkflowRunRepository,
40
40
  durableEntities: () => durableEntities,
41
41
  durableManagedTables: () => durableManagedTables,
42
- ensureMikroOrmDurableSchema: () => ensureMikroOrmDurableSchema
42
+ ensureMikroOrmDurableSchema: () => ensureMikroOrmDurableSchema,
43
+ mikroOrmSchemaRepairWarning: () => mikroOrmSchemaRepairWarning
43
44
  });
44
45
  module.exports = __toCommonJS(index_exports);
45
46
 
@@ -721,6 +722,78 @@ var MARKER_TABLE = "durable_schema_meta";
721
722
  var MARKER_ROW_ID = "durable";
722
723
  var SCHEMA_LOCK_NAME = "durable_schema";
723
724
  var SCHEMA_REVISION = 1;
725
+ function mikroOrmSchemaRepairWarning(repairs) {
726
+ return [
727
+ `[nestjs-durable] store-mikro-orm repaired the durable schema at boot: ${repairs.join("; ")}.`,
728
+ "The database had drifted from the schema this library version expects (it was created by an",
729
+ "older version, or a migration left these out), so they were applied at runtime and nothing",
730
+ "recorded them in your migration history. To make them permanent, add a migration that calls",
731
+ "`ensureMikroOrmDurableSchema(orm)`. This warning appears only when a repair was applied; a",
732
+ "schema that is already current logs nothing."
733
+ ].join(" ");
734
+ }
735
+ __name(mikroOrmSchemaRepairWarning, "mikroOrmSchemaRepairWarning");
736
+ function unquote(identifier) {
737
+ return identifier.replace(/^[`"']|[`"']$/g, "");
738
+ }
739
+ __name(unquote, "unquote");
740
+ function describeDurableStatement(statement) {
741
+ const table = targetTable(statement) ?? "?";
742
+ if (/^create\s+table\b/i.test(statement)) return [
743
+ `created table ${table}`
744
+ ];
745
+ const createIndex = statement.match(/^create\s+(?:unique\s+)?index\s+(?:if\s+not\s+exists\s+)?([`"']?[\w$]+[`"']?)/i);
746
+ if (createIndex?.[1]) return [
747
+ `created index ${unquote(createIndex[1])} on ${table}`
748
+ ];
749
+ if (/^alter\s+table\b/i.test(statement)) {
750
+ const out = [];
751
+ let other = false;
752
+ for (const action of splitTopLevel(statement.replace(/^alter\s+table\s+[`"']?[\w$]+[`"']?\s*/i, ""))) {
753
+ const index = action.match(/^add\s+(?:unique\s+|fulltext\s+)?(?:index|key)\s+([`"']?[\w$]+[`"']?)/i);
754
+ const constraint = action.match(/^add\s+constraint\s+([`"']?[\w$]+[`"']?)/i);
755
+ const column = action.match(/^add\s+(?:column\s+)?([`"']?[\w$]+[`"']?)/i);
756
+ if (index?.[1]) out.push(`created index ${unquote(index[1])} on ${table}`);
757
+ else if (constraint?.[1]) out.push(`added constraint ${unquote(constraint[1])} on ${table}`);
758
+ else if (column?.[1] && !/^add\s+(?:primary|unique|fulltext|foreign)\b/i.test(action)) out.push(`added column ${table}.${unquote(column[1])}`);
759
+ else other = true;
760
+ }
761
+ if (other || out.length === 0) {
762
+ const flat = statement.replace(/\s+/g, " ");
763
+ out.push(`altered ${table} (${flat.length > 160 ? `${flat.slice(0, 157)}...` : flat})`);
764
+ }
765
+ return out;
766
+ }
767
+ return [
768
+ `ran ${statement.replace(/\s+/g, " ").slice(0, 160)}`
769
+ ];
770
+ }
771
+ __name(describeDurableStatement, "describeDurableStatement");
772
+ function splitTopLevel(text) {
773
+ const parts = [];
774
+ let depth = 0;
775
+ let quote;
776
+ let current = "";
777
+ for (const ch of text) {
778
+ if (quote) {
779
+ if (ch === quote) quote = void 0;
780
+ } else if (ch === "'" || ch === '"' || ch === "`") {
781
+ quote = ch;
782
+ } else if (ch === "(") {
783
+ depth++;
784
+ } else if (ch === ")") {
785
+ depth--;
786
+ } else if (ch === "," && depth === 0) {
787
+ parts.push(current.trim());
788
+ current = "";
789
+ continue;
790
+ }
791
+ current += ch;
792
+ }
793
+ if (current.trim()) parts.push(current.trim());
794
+ return parts;
795
+ }
796
+ __name(splitTopLevel, "splitTopLevel");
724
797
  function compareStrings(a, b) {
725
798
  return a < b ? -1 : a > b ? 1 : 0;
726
799
  }
@@ -839,7 +912,8 @@ async function releaseSchemaLock(orm, dialect) {
839
912
  }
840
913
  }
841
914
  __name(releaseSchemaLock, "releaseSchemaLock");
842
- async function ensureMikroOrmDurableSchema(orm) {
915
+ async function ensureMikroOrmDurableSchema(orm, options = {}) {
916
+ const logger = options.logger ?? console;
843
917
  const dialect = detectSqlDialect(orm);
844
918
  await orm.em.getConnection().execute(createMarkerTableSql());
845
919
  const expected = computeExpectedFingerprint(orm, DURABLE_TABLE_NAMES);
@@ -852,8 +926,9 @@ async function ensureMikroOrmDurableSchema(orm) {
852
926
  if (await readStoredFingerprint(orm) === expected) {
853
927
  return;
854
928
  }
855
- await healDurableSchema(orm);
929
+ const { repairs, freshInstall } = await healDurableSchema(orm);
856
930
  await writeFingerprint(orm, dialect, expected);
931
+ if (repairs.length > 0 && !freshInstall) logger.warn(mikroOrmSchemaRepairWarning(repairs));
857
932
  } finally {
858
933
  await releaseSchemaLock(orm, dialect);
859
934
  }
@@ -868,9 +943,14 @@ async function healDurableSchema(orm) {
868
943
  return table !== void 0 && DURABLE_TABLE_NAMES.has(table);
869
944
  });
870
945
  const connection = orm.em.getConnection();
946
+ const created = new Set(ours.filter((statement) => /^create\s+table\b/i.test(statement)).map((statement) => targetTable(statement)));
947
+ const repairs = [];
871
948
  for (const statement of ours) {
872
949
  try {
873
950
  await connection.execute(statement);
951
+ const table = targetTable(statement);
952
+ const isCreateTable = /^create\s+table\b/i.test(statement);
953
+ if (isCreateTable || !created.has(table)) repairs.push(...describeDurableStatement(statement));
874
954
  } catch (err) {
875
955
  if (isRequiredStructure(statement)) {
876
956
  throw err;
@@ -879,7 +959,17 @@ async function healDurableSchema(orm) {
879
959
  console.warn(`[nestjs-durable-store-mikro-orm] skipped a non-structural durable-schema statement that failed; the column is left as-is (functional) \u2014 repair the data out of band to converge its type. Statement: ${statement} \u2014 ${message}`);
880
960
  }
881
961
  }
882
- await alignDurableCollation(orm);
962
+ const converted = await alignDurableCollation(orm);
963
+ for (const { table, from, to } of converted) {
964
+ if (!created.has(table)) repairs.push(`converted ${table} collation from ${from} to ${to}`);
965
+ }
966
+ const freshInstall = [
967
+ ...DURABLE_TABLE_NAMES
968
+ ].every((table) => created.has(table));
969
+ return {
970
+ repairs,
971
+ freshInstall
972
+ };
883
973
  }
884
974
  __name(healDurableSchema, "healDurableSchema");
885
975
  function isSafeSqlIdentifier(value) {
@@ -887,17 +977,18 @@ function isSafeSqlIdentifier(value) {
887
977
  }
888
978
  __name(isSafeSqlIdentifier, "isSafeSqlIdentifier");
889
979
  async function alignDurableCollation(orm) {
980
+ const converted = [];
890
981
  const platform = String(orm.em.getPlatform().constructor.name).toLowerCase();
891
982
  if (!platform.includes("mysql") && !platform.includes("maria")) {
892
- return;
983
+ return converted;
893
984
  }
894
985
  const collate = orm.config.get("collate");
895
986
  if (typeof collate !== "string" || !isSafeSqlIdentifier(collate)) {
896
- return;
987
+ return converted;
897
988
  }
898
989
  const charset = collate.split("_")[0];
899
990
  if (!charset || !isSafeSqlIdentifier(charset)) {
900
- return;
991
+ return converted;
901
992
  }
902
993
  const connection = orm.em.getConnection();
903
994
  for (const table of DURABLE_TABLE_NAMES) {
@@ -910,11 +1001,17 @@ async function alignDurableCollation(orm) {
910
1001
  continue;
911
1002
  }
912
1003
  await connection.execute(`alter table \`${table}\` convert to character set ${charset} collate ${collate}`);
1004
+ converted.push({
1005
+ table,
1006
+ from: current,
1007
+ to: collate
1008
+ });
913
1009
  } catch (err) {
914
1010
  const message = err instanceof Error ? err.message : String(err);
915
1011
  console.warn(`[nestjs-durable-store-mikro-orm] could not align collation for \`${table}\` to ${collate} (left as-is): ${message}`);
916
1012
  }
917
1013
  }
1014
+ return converted;
918
1015
  }
919
1016
  __name(alignDurableCollation, "alignDurableCollation");
920
1017
 
@@ -924,10 +1021,12 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
924
1021
  __name(this, "MikroOrmStateStore");
925
1022
  }
926
1023
  orm;
1024
+ opts;
927
1025
  scopeNamespace;
928
- constructor(orm, opts) {
1026
+ constructor(orm, opts = {}) {
929
1027
  this.orm = orm;
930
- this.scopeNamespace = opts?.scope?.namespace;
1028
+ this.opts = opts;
1029
+ this.scopeNamespace = opts.scope?.namespace;
931
1030
  }
932
1031
  /**
933
1032
  * Derive a store confined to a single tenant `scope.namespace`, sharing this store's ORM (no new
@@ -937,6 +1036,7 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
937
1036
  */
938
1037
  withScope(scope) {
939
1038
  return new _MikroOrmStateStore(this.orm, {
1039
+ ...this.opts,
940
1040
  scope
941
1041
  });
942
1042
  }
@@ -954,7 +1054,9 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
954
1054
  return em;
955
1055
  }
956
1056
  async ensureSchema() {
957
- await ensureMikroOrmDurableSchema(this.orm);
1057
+ await ensureMikroOrmDurableSchema(this.orm, {
1058
+ logger: this.opts.schemaLogger
1059
+ });
958
1060
  }
959
1061
  async tryAdmitSingleton(runId, tag, workflow, limit, retryWakeAt) {
960
1062
  return this.fork().transactional(async (em) => {
@@ -1053,6 +1155,17 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
1053
1155
  });
1054
1156
  return entity ? fromRunEntity(entity) : null;
1055
1157
  }
1158
+ async getRuns(runIds) {
1159
+ if (runIds.length === 0) return [];
1160
+ const rows = await this.fork().find(WorkflowRunEntity, {
1161
+ id: {
1162
+ $in: [
1163
+ ...new Set(runIds)
1164
+ ]
1165
+ }
1166
+ });
1167
+ return rows.map(fromRunEntity);
1168
+ }
1056
1169
  async deleteRun(runId) {
1057
1170
  const em = this.fork();
1058
1171
  await em.nativeDelete(StepCheckpointEntity, {
@@ -1296,10 +1409,13 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
1296
1409
  });
1297
1410
  return affected === 1;
1298
1411
  }
1299
- async releaseRunLock(runId) {
1412
+ async releaseRunLock(runId, owner) {
1300
1413
  const em = this.fork();
1301
- await em.nativeUpdate(WorkflowRunEntity, {
1414
+ await em.nativeUpdate(WorkflowRunEntity, owner === void 0 ? {
1302
1415
  id: runId
1416
+ } : {
1417
+ id: runId,
1418
+ lockedBy: owner
1303
1419
  }, {
1304
1420
  lockedBy: null,
1305
1421
  lockedUntil: null
@@ -2184,6 +2300,7 @@ __name(fromScheduleEntity, "fromScheduleEntity");
2184
2300
  WorkflowRunRepository,
2185
2301
  durableEntities,
2186
2302
  durableManagedTables,
2187
- ensureMikroOrmDurableSchema
2303
+ ensureMikroOrmDurableSchema,
2304
+ mikroOrmSchemaRepairWarning
2188
2305
  });
2189
2306
  //# sourceMappingURL=index.cjs.map