@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 +86 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -3
- package/dist/index.d.ts +13 -3
- package/dist/index.js +87 -23
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -547,7 +547,7 @@ __name(durableEntities, "durableEntities");
|
|
|
547
547
|
var ENTITIES = durableEntities();
|
|
548
548
|
|
|
549
549
|
// src/mikro-orm-state-store.ts
|
|
550
|
-
import {
|
|
550
|
+
import { RUN_VALUE_FACET_SCAN, attributePredicateOperands, axisIsRunColumn, mergeRunFacetRows, mergeRunValueFacetRows, normalizeAttributeRows, parseDuration, runValueFacetsFromRuns } from "@dudousxd/nestjs-durable-core";
|
|
551
551
|
import { raw } from "@mikro-orm/core";
|
|
552
552
|
|
|
553
553
|
// src/schema.ts
|
|
@@ -1031,20 +1031,47 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1031
1031
|
* applied by the QueryBuilder path in both callers. */
|
|
1032
1032
|
runWhere(query) {
|
|
1033
1033
|
const where = {};
|
|
1034
|
+
const and = [];
|
|
1034
1035
|
if (query.workflow) where.workflow = query.workflow;
|
|
1036
|
+
if (query.workflows) and.push({
|
|
1037
|
+
workflow: {
|
|
1038
|
+
$in: query.workflows
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1035
1041
|
if (query.namespace !== void 0) where.namespace = query.namespace;
|
|
1042
|
+
if (query.namespaces) and.push({
|
|
1043
|
+
namespace: {
|
|
1044
|
+
$in: query.namespaces
|
|
1045
|
+
}
|
|
1046
|
+
});
|
|
1036
1047
|
if (query.origin !== void 0) where.origin = query.origin;
|
|
1048
|
+
if (query.origins) {
|
|
1049
|
+
const named = query.origins.filter((o) => o !== null);
|
|
1050
|
+
const branches = [];
|
|
1051
|
+
if (named.length) branches.push({
|
|
1052
|
+
origin: {
|
|
1053
|
+
$in: named
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
if (query.origins.some((o) => o === null)) branches.push({
|
|
1057
|
+
origin: null
|
|
1058
|
+
});
|
|
1059
|
+
and.push(branches.length ? {
|
|
1060
|
+
$or: branches
|
|
1061
|
+
} : {
|
|
1062
|
+
origin: {
|
|
1063
|
+
$in: []
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1037
1067
|
if (query.status && query.statuses) {
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
status: {
|
|
1044
|
-
$in: query.statuses
|
|
1045
|
-
}
|
|
1068
|
+
and.push({
|
|
1069
|
+
status: query.status
|
|
1070
|
+
}, {
|
|
1071
|
+
status: {
|
|
1072
|
+
$in: query.statuses
|
|
1046
1073
|
}
|
|
1047
|
-
|
|
1074
|
+
});
|
|
1048
1075
|
} else if (query.status) {
|
|
1049
1076
|
where.status = query.status;
|
|
1050
1077
|
} else if (query.statuses) {
|
|
@@ -1052,6 +1079,7 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1052
1079
|
$in: query.statuses
|
|
1053
1080
|
};
|
|
1054
1081
|
}
|
|
1082
|
+
if (and.length) where.$and = and;
|
|
1055
1083
|
return where;
|
|
1056
1084
|
}
|
|
1057
1085
|
async listRuns(query) {
|
|
@@ -1060,7 +1088,7 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1060
1088
|
const orderBy = {
|
|
1061
1089
|
createdAt: "desc"
|
|
1062
1090
|
};
|
|
1063
|
-
if (query.tag || query.attributes?.length) {
|
|
1091
|
+
if (query.tag || query.tags || query.attributes?.length) {
|
|
1064
1092
|
const qb = this.runQueryBuilder(em, query, where).orderBy(orderBy);
|
|
1065
1093
|
if (query.limit != null) qb.limit(query.limit);
|
|
1066
1094
|
if (query.offset != null) qb.offset(query.offset);
|
|
@@ -1086,17 +1114,23 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1086
1114
|
const quote = this.idQuote(em);
|
|
1087
1115
|
const cols = this.attributeColumns(em);
|
|
1088
1116
|
const qb = em.createQueryBuilder(WorkflowRunEntity, "r").where(where);
|
|
1089
|
-
if (query.tag)
|
|
1090
|
-
|
|
1091
|
-
const
|
|
1092
|
-
|
|
1093
|
-
qb.andWhere(`${colExpr} LIKE '${pattern}'`);
|
|
1117
|
+
if (query.tag) qb.andWhere(this.tagLikeSql(em, query.tag, quote));
|
|
1118
|
+
if (query.tags) {
|
|
1119
|
+
const branches = query.tags.map((t) => this.tagLikeSql(em, t, quote));
|
|
1120
|
+
qb.andWhere(branches.length ? `(${branches.join(" OR ")})` : "1 = 0");
|
|
1094
1121
|
}
|
|
1095
1122
|
for (const f of query.attributes ?? []) {
|
|
1096
1123
|
qb.andWhere(this.attributeExistsSql(f, quote, "r", cols));
|
|
1097
1124
|
}
|
|
1098
1125
|
return qb;
|
|
1099
1126
|
}
|
|
1127
|
+
/** `tags LIKE '%"etl"%'` — the quoted token, so `etl` doesn't match `etl-foo`. See the note in
|
|
1128
|
+
* {@link listRuns} for why this is raw SQL rather than a MikroORM condition. */
|
|
1129
|
+
tagLikeSql(em, tag, quote) {
|
|
1130
|
+
const pattern = `%"${tag.replace(/'/g, "''")}"%`;
|
|
1131
|
+
const colExpr = this.jsonAsText(em, `${quote("r")}.${quote(this.tagsColumn(em))}`);
|
|
1132
|
+
return `${colExpr} LIKE '${pattern}'`;
|
|
1133
|
+
}
|
|
1100
1134
|
/** `GROUP BY status, origin` over the same predicates {@link listRuns} pages — one aggregate, so a
|
|
1101
1135
|
* console can show whole-set counts next to a bounded page instead of downloading every run to
|
|
1102
1136
|
* count them in the browser. */
|
|
@@ -1117,20 +1151,50 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1117
1151
|
count: Number(r.count)
|
|
1118
1152
|
})));
|
|
1119
1153
|
}
|
|
1154
|
+
/** Distinct values of one filter axis over the same predicates {@link listRuns} pages. A run-table
|
|
1155
|
+
* column is a `GROUP BY` like {@link runFacets}, exact over the whole matching set; the tag and
|
|
1156
|
+
* attribute axes live outside the row (json array / side table) and are counted from a bounded
|
|
1157
|
+
* page of runs instead — see {@link RunValueFacetOptions.scan}. */
|
|
1158
|
+
async runValueFacets(axis, query, opts) {
|
|
1159
|
+
if (!axisIsRunColumn(axis)) {
|
|
1160
|
+
const runs = await this.listRuns({
|
|
1161
|
+
...query,
|
|
1162
|
+
limit: opts?.scan ?? RUN_VALUE_FACET_SCAN
|
|
1163
|
+
});
|
|
1164
|
+
return runValueFacetsFromRuns(runs, axis, opts);
|
|
1165
|
+
}
|
|
1166
|
+
const em = this.fork();
|
|
1167
|
+
const meta = em.getMetadata().get(WorkflowRunEntity);
|
|
1168
|
+
const col = meta.properties[axis.field]?.fieldNames?.[0] ?? axis.field;
|
|
1169
|
+
const rows = await this.runQueryBuilder(em, query, this.runWhere(query)).select([
|
|
1170
|
+
`r.${col}`
|
|
1171
|
+
]).addSelect(raw("count(*) as count")).groupBy([
|
|
1172
|
+
`r.${col}`
|
|
1173
|
+
]).execute();
|
|
1174
|
+
return mergeRunValueFacetRows(rows.map((row) => ({
|
|
1175
|
+
value: row[axis.field] ?? null,
|
|
1176
|
+
count: Number(row.count)
|
|
1177
|
+
})), opts);
|
|
1178
|
+
}
|
|
1120
1179
|
/** One attribute predicate as a raw EXISTS subquery on the side-table, correlated to the outer run
|
|
1121
1180
|
* alias. `<>` (ne) also excludes runs where the attribute is absent (missing-key-never-matches):
|
|
1122
1181
|
* EXISTS already requires a present row, so EXISTS(... <> ...) is exactly ne-with-present. Numeric
|
|
1123
|
-
* operands compare the num column, everything else the str column.
|
|
1124
|
-
*
|
|
1182
|
+
* operands compare the num column, everything else the str column. `in` contributes one comparison
|
|
1183
|
+
* per member, ORed inside the single EXISTS — one subquery either way, and an `in` over no values
|
|
1184
|
+
* yields no comparison and so matches nothing. Identifiers are quoted per driver and operands are
|
|
1185
|
+
* inlined as literals (string operands are escaped by doubling quotes). */
|
|
1125
1186
|
attributeExistsSql(f, quote, alias, cols) {
|
|
1126
|
-
const
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1187
|
+
const comparisons = attributePredicateOperands(f).map(({ column, comparator, operand }) => {
|
|
1188
|
+
const col = column === "numValue" ? cols.numValue : cols.strValue;
|
|
1189
|
+
const literal = typeof operand === "number" ? String(operand) : `'${String(operand).replace(/'/g, "''")}'`;
|
|
1190
|
+
return `${quote("a")}.${quote(col)} ${comparator} ${literal}`;
|
|
1191
|
+
});
|
|
1192
|
+
if (comparisons.length === 0) return "1 = 0";
|
|
1130
1193
|
const a = quote(cols.table);
|
|
1131
1194
|
const sub = quote("a");
|
|
1132
1195
|
const outerId = `${quote(alias)}.${quote(cols.runPk)}`;
|
|
1133
|
-
|
|
1196
|
+
const match = comparisons.length === 1 ? comparisons[0] : `(${comparisons.join(" OR ")})`;
|
|
1197
|
+
return `EXISTS (SELECT 1 FROM ${a} ${sub} WHERE ${sub}.${quote(cols.runId)} = ${outerId} AND ${sub}.${quote(cols.key)} = '${f.key.replace(/'/g, "''")}' AND ${match})`;
|
|
1134
1198
|
}
|
|
1135
1199
|
/** Resolve the side-table's actual column names from MikroORM metadata, so the raw EXISTS matches
|
|
1136
1200
|
* the active naming strategy (underscore vs camelCase) rather than guessing. The run PK column is
|