@dudousxd/nestjs-durable-store-mikro-orm 0.23.0 → 0.24.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
@@ -1071,20 +1071,47 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
1071
1071
  * applied by the QueryBuilder path in both callers. */
1072
1072
  runWhere(query) {
1073
1073
  const where = {};
1074
+ const and = [];
1074
1075
  if (query.workflow) where.workflow = query.workflow;
1076
+ if (query.workflows) and.push({
1077
+ workflow: {
1078
+ $in: query.workflows
1079
+ }
1080
+ });
1075
1081
  if (query.namespace !== void 0) where.namespace = query.namespace;
1082
+ if (query.namespaces) and.push({
1083
+ namespace: {
1084
+ $in: query.namespaces
1085
+ }
1086
+ });
1076
1087
  if (query.origin !== void 0) where.origin = query.origin;
1088
+ if (query.origins) {
1089
+ const named = query.origins.filter((o) => o !== null);
1090
+ const branches = [];
1091
+ if (named.length) branches.push({
1092
+ origin: {
1093
+ $in: named
1094
+ }
1095
+ });
1096
+ if (query.origins.some((o) => o === null)) branches.push({
1097
+ origin: null
1098
+ });
1099
+ and.push(branches.length ? {
1100
+ $or: branches
1101
+ } : {
1102
+ origin: {
1103
+ $in: []
1104
+ }
1105
+ });
1106
+ }
1077
1107
  if (query.status && query.statuses) {
1078
- where.$and = [
1079
- {
1080
- status: query.status
1081
- },
1082
- {
1083
- status: {
1084
- $in: query.statuses
1085
- }
1108
+ and.push({
1109
+ status: query.status
1110
+ }, {
1111
+ status: {
1112
+ $in: query.statuses
1086
1113
  }
1087
- ];
1114
+ });
1088
1115
  } else if (query.status) {
1089
1116
  where.status = query.status;
1090
1117
  } else if (query.statuses) {
@@ -1092,6 +1119,7 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
1092
1119
  $in: query.statuses
1093
1120
  };
1094
1121
  }
1122
+ if (and.length) where.$and = and;
1095
1123
  return where;
1096
1124
  }
1097
1125
  async listRuns(query) {
@@ -1100,7 +1128,7 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
1100
1128
  const orderBy = {
1101
1129
  createdAt: "desc"
1102
1130
  };
1103
- if (query.tag || query.attributes?.length) {
1131
+ if (query.tag || query.tags || query.attributes?.length) {
1104
1132
  const qb = this.runQueryBuilder(em, query, where).orderBy(orderBy);
1105
1133
  if (query.limit != null) qb.limit(query.limit);
1106
1134
  if (query.offset != null) qb.offset(query.offset);
@@ -1126,17 +1154,23 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
1126
1154
  const quote = this.idQuote(em);
1127
1155
  const cols = this.attributeColumns(em);
1128
1156
  const qb = em.createQueryBuilder(WorkflowRunEntity, "r").where(where);
1129
- if (query.tag) {
1130
- const tagsCol = this.tagsColumn(em);
1131
- const pattern = `%"${query.tag.replace(/'/g, "''")}"%`;
1132
- const colExpr = this.jsonAsText(em, `${quote("r")}.${quote(tagsCol)}`);
1133
- qb.andWhere(`${colExpr} LIKE '${pattern}'`);
1157
+ if (query.tag) qb.andWhere(this.tagLikeSql(em, query.tag, quote));
1158
+ if (query.tags) {
1159
+ const branches = query.tags.map((t) => this.tagLikeSql(em, t, quote));
1160
+ qb.andWhere(branches.length ? `(${branches.join(" OR ")})` : "1 = 0");
1134
1161
  }
1135
1162
  for (const f of query.attributes ?? []) {
1136
1163
  qb.andWhere(this.attributeExistsSql(f, quote, "r", cols));
1137
1164
  }
1138
1165
  return qb;
1139
1166
  }
1167
+ /** `tags LIKE '%"etl"%'` — the quoted token, so `etl` doesn't match `etl-foo`. See the note in
1168
+ * {@link listRuns} for why this is raw SQL rather than a MikroORM condition. */
1169
+ tagLikeSql(em, tag, quote) {
1170
+ const pattern = `%"${tag.replace(/'/g, "''")}"%`;
1171
+ const colExpr = this.jsonAsText(em, `${quote("r")}.${quote(this.tagsColumn(em))}`);
1172
+ return `${colExpr} LIKE '${pattern}'`;
1173
+ }
1140
1174
  /** `GROUP BY status, origin` over the same predicates {@link listRuns} pages — one aggregate, so a
1141
1175
  * console can show whole-set counts next to a bounded page instead of downloading every run to
1142
1176
  * count them in the browser. */
@@ -1157,20 +1191,50 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
1157
1191
  count: Number(r.count)
1158
1192
  })));
1159
1193
  }
1194
+ /** Distinct values of one filter axis over the same predicates {@link listRuns} pages. A run-table
1195
+ * column is a `GROUP BY` like {@link runFacets}, exact over the whole matching set; the tag and
1196
+ * attribute axes live outside the row (json array / side table) and are counted from a bounded
1197
+ * page of runs instead — see {@link RunValueFacetOptions.scan}. */
1198
+ async runValueFacets(axis, query, opts) {
1199
+ if (!(0, import_nestjs_durable_core.axisIsRunColumn)(axis)) {
1200
+ const runs = await this.listRuns({
1201
+ ...query,
1202
+ limit: opts?.scan ?? import_nestjs_durable_core.RUN_VALUE_FACET_SCAN
1203
+ });
1204
+ return (0, import_nestjs_durable_core.runValueFacetsFromRuns)(runs, axis, opts);
1205
+ }
1206
+ const em = this.fork();
1207
+ const meta = em.getMetadata().get(WorkflowRunEntity);
1208
+ const col = meta.properties[axis.field]?.fieldNames?.[0] ?? axis.field;
1209
+ const rows = await this.runQueryBuilder(em, query, this.runWhere(query)).select([
1210
+ `r.${col}`
1211
+ ]).addSelect((0, import_core3.raw)("count(*) as count")).groupBy([
1212
+ `r.${col}`
1213
+ ]).execute();
1214
+ return (0, import_nestjs_durable_core.mergeRunValueFacetRows)(rows.map((row) => ({
1215
+ value: row[axis.field] ?? null,
1216
+ count: Number(row.count)
1217
+ })), opts);
1218
+ }
1160
1219
  /** One attribute predicate as a raw EXISTS subquery on the side-table, correlated to the outer run
1161
1220
  * alias. `<>` (ne) also excludes runs where the attribute is absent (missing-key-never-matches):
1162
1221
  * EXISTS already requires a present row, so EXISTS(... <> ...) is exactly ne-with-present. Numeric
1163
- * operands compare the num column, everything else the str column. Identifiers are quoted per
1164
- * driver and operands are inlined as literals (string operands are escaped by doubling quotes). */
1222
+ * operands compare the num column, everything else the str column. `in` contributes one comparison
1223
+ * per member, ORed inside the single EXISTS — one subquery either way, and an `in` over no values
1224
+ * yields no comparison and so matches nothing. Identifiers are quoted per driver and operands are
1225
+ * inlined as literals (string operands are escaped by doubling quotes). */
1165
1226
  attributeExistsSql(f, quote, alias, cols) {
1166
- const col = (0, import_nestjs_durable_core.attributeColumnFor)(f) === "numValue" ? cols.numValue : cols.strValue;
1167
- const cmp = (0, import_nestjs_durable_core.sqlComparator)(f.op);
1168
- const operand = (0, import_nestjs_durable_core.attributeOperand)(f);
1169
- const literal = typeof operand === "number" ? String(operand) : `'${String(operand).replace(/'/g, "''")}'`;
1227
+ const comparisons = (0, import_nestjs_durable_core.attributePredicateOperands)(f).map(({ column, comparator, operand }) => {
1228
+ const col = column === "numValue" ? cols.numValue : cols.strValue;
1229
+ const literal = typeof operand === "number" ? String(operand) : `'${String(operand).replace(/'/g, "''")}'`;
1230
+ return `${quote("a")}.${quote(col)} ${comparator} ${literal}`;
1231
+ });
1232
+ if (comparisons.length === 0) return "1 = 0";
1170
1233
  const a = quote(cols.table);
1171
1234
  const sub = quote("a");
1172
1235
  const outerId = `${quote(alias)}.${quote(cols.runPk)}`;
1173
- return `EXISTS (SELECT 1 FROM ${a} ${sub} WHERE ${sub}.${quote(cols.runId)} = ${outerId} AND ${sub}.${quote(cols.key)} = '${f.key.replace(/'/g, "''")}' AND ${sub}.${quote(col)} ${cmp} ${literal})`;
1236
+ const match = comparisons.length === 1 ? comparisons[0] : `(${comparisons.join(" OR ")})`;
1237
+ return `EXISTS (SELECT 1 FROM ${a} ${sub} WHERE ${sub}.${quote(cols.runId)} = ${outerId} AND ${sub}.${quote(cols.key)} = '${f.key.replace(/'/g, "''")}' AND ${match})`;
1174
1238
  }
1175
1239
  /** Resolve the side-table's actual column names from MikroORM metadata, so the raw EXISTS matches
1176
1240
  * the active naming strategy (underscore vs camelCase) rather than guessing. The run PK column is