@dudousxd/nestjs-durable-store-mikro-orm 0.22.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 +155 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -5
- package/dist/index.d.ts +49 -5
- package/dist/index.js +143 -24
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -22,13 +22,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
22
22
|
var index_exports = {};
|
|
23
23
|
__export(index_exports, {
|
|
24
24
|
BufferedEventEntity: () => BufferedEventEntity,
|
|
25
|
+
BufferedEventRepository: () => BufferedEventRepository,
|
|
25
26
|
BufferedSignalEntity: () => BufferedSignalEntity,
|
|
27
|
+
BufferedSignalRepository: () => BufferedSignalRepository,
|
|
26
28
|
ENTITIES: () => ENTITIES,
|
|
27
29
|
MikroOrmStateStore: () => MikroOrmStateStore,
|
|
28
30
|
RunAttributeEntity: () => RunAttributeEntity,
|
|
31
|
+
RunAttributeRepository: () => RunAttributeRepository,
|
|
29
32
|
SignalWaiterEntity: () => SignalWaiterEntity,
|
|
33
|
+
SignalWaiterRepository: () => SignalWaiterRepository,
|
|
30
34
|
StepCheckpointEntity: () => StepCheckpointEntity,
|
|
35
|
+
StepCheckpointRepository: () => StepCheckpointRepository,
|
|
31
36
|
WorkflowRunEntity: () => WorkflowRunEntity,
|
|
37
|
+
WorkflowRunRepository: () => WorkflowRunRepository,
|
|
32
38
|
durableEntities: () => durableEntities,
|
|
33
39
|
durableManagedTables: () => durableManagedTables,
|
|
34
40
|
ensureMikroOrmDurableSchema: () => ensureMikroOrmDurableSchema
|
|
@@ -36,7 +42,42 @@ __export(index_exports, {
|
|
|
36
42
|
module.exports = __toCommonJS(index_exports);
|
|
37
43
|
|
|
38
44
|
// src/entities.ts
|
|
45
|
+
var import_core2 = require("@mikro-orm/core");
|
|
46
|
+
|
|
47
|
+
// src/repositories.ts
|
|
39
48
|
var import_core = require("@mikro-orm/core");
|
|
49
|
+
var WorkflowRunRepository = class extends import_core.EntityRepository {
|
|
50
|
+
static {
|
|
51
|
+
__name(this, "WorkflowRunRepository");
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var StepCheckpointRepository = class extends import_core.EntityRepository {
|
|
55
|
+
static {
|
|
56
|
+
__name(this, "StepCheckpointRepository");
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var RunAttributeRepository = class extends import_core.EntityRepository {
|
|
60
|
+
static {
|
|
61
|
+
__name(this, "RunAttributeRepository");
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var SignalWaiterRepository = class extends import_core.EntityRepository {
|
|
65
|
+
static {
|
|
66
|
+
__name(this, "SignalWaiterRepository");
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var BufferedSignalRepository = class extends import_core.EntityRepository {
|
|
70
|
+
static {
|
|
71
|
+
__name(this, "BufferedSignalRepository");
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var BufferedEventRepository = class extends import_core.EntityRepository {
|
|
75
|
+
static {
|
|
76
|
+
__name(this, "BufferedEventRepository");
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// src/entities.ts
|
|
40
81
|
function snakeCase(property) {
|
|
41
82
|
return property.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
|
|
42
83
|
}
|
|
@@ -51,6 +92,7 @@ var WorkflowRunEntity = class {
|
|
|
51
92
|
static {
|
|
52
93
|
__name(this, "WorkflowRunEntity");
|
|
53
94
|
}
|
|
95
|
+
[import_core2.EntityRepositoryType];
|
|
54
96
|
id;
|
|
55
97
|
workflow;
|
|
56
98
|
workflowVersion;
|
|
@@ -75,6 +117,7 @@ var StepCheckpointEntity = class {
|
|
|
75
117
|
static {
|
|
76
118
|
__name(this, "StepCheckpointEntity");
|
|
77
119
|
}
|
|
120
|
+
[import_core2.EntityRepositoryType];
|
|
78
121
|
runId;
|
|
79
122
|
seq;
|
|
80
123
|
name;
|
|
@@ -97,6 +140,7 @@ var RunAttributeEntity = class {
|
|
|
97
140
|
static {
|
|
98
141
|
__name(this, "RunAttributeEntity");
|
|
99
142
|
}
|
|
143
|
+
[import_core2.EntityRepositoryType];
|
|
100
144
|
runId;
|
|
101
145
|
key;
|
|
102
146
|
strValue;
|
|
@@ -106,6 +150,7 @@ var SignalWaiterEntity = class {
|
|
|
106
150
|
static {
|
|
107
151
|
__name(this, "SignalWaiterEntity");
|
|
108
152
|
}
|
|
153
|
+
[import_core2.EntityRepositoryType];
|
|
109
154
|
token;
|
|
110
155
|
runId;
|
|
111
156
|
seq;
|
|
@@ -115,6 +160,7 @@ var BufferedSignalEntity = class {
|
|
|
115
160
|
static {
|
|
116
161
|
__name(this, "BufferedSignalEntity");
|
|
117
162
|
}
|
|
163
|
+
[import_core2.EntityRepositoryType];
|
|
118
164
|
id;
|
|
119
165
|
token;
|
|
120
166
|
payload;
|
|
@@ -123,6 +169,7 @@ var BufferedEventEntity = class {
|
|
|
123
169
|
static {
|
|
124
170
|
__name(this, "BufferedEventEntity");
|
|
125
171
|
}
|
|
172
|
+
[import_core2.EntityRepositoryType];
|
|
126
173
|
id;
|
|
127
174
|
name;
|
|
128
175
|
payload;
|
|
@@ -137,9 +184,12 @@ function namespaceFilterCond(args) {
|
|
|
137
184
|
__name(namespaceFilterCond, "namespaceFilterCond");
|
|
138
185
|
function durableEntities(options = {}) {
|
|
139
186
|
const col = columnNamer(options.naming ?? "snake_case");
|
|
140
|
-
const workflowRuns = new
|
|
187
|
+
const workflowRuns = new import_core2.EntitySchema({
|
|
141
188
|
class: WorkflowRunEntity,
|
|
142
189
|
tableName: "durable_workflow_runs",
|
|
190
|
+
// Wired per schema rather than once on the class: `durableEntities` can be called again with a
|
|
191
|
+
// different `naming`, and each call builds a distinct EntitySchema over the same class.
|
|
192
|
+
repository: /* @__PURE__ */ __name(() => WorkflowRunRepository, "repository"),
|
|
143
193
|
// Global filter: confines every read to the tenant's namespace when the store is scoped.
|
|
144
194
|
// The `cond` returns `{}` (no-op) when the `namespace` arg is `undefined`, so an unscoped
|
|
145
195
|
// (operator / control-plane) store sees all rows unchanged — existing behaviour is preserved.
|
|
@@ -300,9 +350,10 @@ function durableEntities(options = {}) {
|
|
|
300
350
|
}
|
|
301
351
|
}
|
|
302
352
|
});
|
|
303
|
-
const stepCheckpoints = new
|
|
353
|
+
const stepCheckpoints = new import_core2.EntitySchema({
|
|
304
354
|
class: StepCheckpointEntity,
|
|
305
355
|
tableName: "durable_step_checkpoints",
|
|
356
|
+
repository: /* @__PURE__ */ __name(() => StepCheckpointRepository, "repository"),
|
|
306
357
|
properties: {
|
|
307
358
|
runId: {
|
|
308
359
|
type: "string",
|
|
@@ -388,9 +439,10 @@ function durableEntities(options = {}) {
|
|
|
388
439
|
}
|
|
389
440
|
}
|
|
390
441
|
});
|
|
391
|
-
const runAttributes = new
|
|
442
|
+
const runAttributes = new import_core2.EntitySchema({
|
|
392
443
|
class: RunAttributeEntity,
|
|
393
444
|
tableName: "durable_run_attributes",
|
|
445
|
+
repository: /* @__PURE__ */ __name(() => RunAttributeRepository, "repository"),
|
|
394
446
|
// The search-attribute EXISTS join (see the class docstring) pushes equality/range predicates down
|
|
395
447
|
// onto `(key, numValue)` / `(key, strValue)`; mirror the Prisma adapter so those scans stay indexed.
|
|
396
448
|
indexes: [
|
|
@@ -433,9 +485,10 @@ function durableEntities(options = {}) {
|
|
|
433
485
|
}
|
|
434
486
|
}
|
|
435
487
|
});
|
|
436
|
-
const signalWaiters = new
|
|
488
|
+
const signalWaiters = new import_core2.EntitySchema({
|
|
437
489
|
class: SignalWaiterEntity,
|
|
438
490
|
tableName: "durable_signal_waiters",
|
|
491
|
+
repository: /* @__PURE__ */ __name(() => SignalWaiterRepository, "repository"),
|
|
439
492
|
properties: {
|
|
440
493
|
token: {
|
|
441
494
|
type: "string",
|
|
@@ -460,9 +513,10 @@ function durableEntities(options = {}) {
|
|
|
460
513
|
}
|
|
461
514
|
}
|
|
462
515
|
});
|
|
463
|
-
const bufferedSignals = new
|
|
516
|
+
const bufferedSignals = new import_core2.EntitySchema({
|
|
464
517
|
class: BufferedSignalEntity,
|
|
465
518
|
tableName: "durable_buffered_signals",
|
|
519
|
+
repository: /* @__PURE__ */ __name(() => BufferedSignalRepository, "repository"),
|
|
466
520
|
properties: {
|
|
467
521
|
id: {
|
|
468
522
|
type: "integer",
|
|
@@ -482,9 +536,10 @@ function durableEntities(options = {}) {
|
|
|
482
536
|
}
|
|
483
537
|
}
|
|
484
538
|
});
|
|
485
|
-
const bufferedEvents = new
|
|
539
|
+
const bufferedEvents = new import_core2.EntitySchema({
|
|
486
540
|
class: BufferedEventEntity,
|
|
487
541
|
tableName: "durable_buffered_events",
|
|
542
|
+
repository: /* @__PURE__ */ __name(() => BufferedEventRepository, "repository"),
|
|
488
543
|
// `(name, publishedAt)` serves both `listBufferedEvents`'s name-scoped scan and its oldest-first
|
|
489
544
|
// ordering in one index.
|
|
490
545
|
indexes: [
|
|
@@ -533,7 +588,7 @@ var ENTITIES = durableEntities();
|
|
|
533
588
|
|
|
534
589
|
// src/mikro-orm-state-store.ts
|
|
535
590
|
var import_nestjs_durable_core = require("@dudousxd/nestjs-durable-core");
|
|
536
|
-
var
|
|
591
|
+
var import_core3 = require("@mikro-orm/core");
|
|
537
592
|
|
|
538
593
|
// src/schema.ts
|
|
539
594
|
var import_node_crypto = require("crypto");
|
|
@@ -1016,20 +1071,47 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1016
1071
|
* applied by the QueryBuilder path in both callers. */
|
|
1017
1072
|
runWhere(query) {
|
|
1018
1073
|
const where = {};
|
|
1074
|
+
const and = [];
|
|
1019
1075
|
if (query.workflow) where.workflow = query.workflow;
|
|
1076
|
+
if (query.workflows) and.push({
|
|
1077
|
+
workflow: {
|
|
1078
|
+
$in: query.workflows
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1020
1081
|
if (query.namespace !== void 0) where.namespace = query.namespace;
|
|
1082
|
+
if (query.namespaces) and.push({
|
|
1083
|
+
namespace: {
|
|
1084
|
+
$in: query.namespaces
|
|
1085
|
+
}
|
|
1086
|
+
});
|
|
1021
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
|
+
}
|
|
1022
1107
|
if (query.status && query.statuses) {
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
status: {
|
|
1029
|
-
$in: query.statuses
|
|
1030
|
-
}
|
|
1108
|
+
and.push({
|
|
1109
|
+
status: query.status
|
|
1110
|
+
}, {
|
|
1111
|
+
status: {
|
|
1112
|
+
$in: query.statuses
|
|
1031
1113
|
}
|
|
1032
|
-
|
|
1114
|
+
});
|
|
1033
1115
|
} else if (query.status) {
|
|
1034
1116
|
where.status = query.status;
|
|
1035
1117
|
} else if (query.statuses) {
|
|
@@ -1037,6 +1119,7 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1037
1119
|
$in: query.statuses
|
|
1038
1120
|
};
|
|
1039
1121
|
}
|
|
1122
|
+
if (and.length) where.$and = and;
|
|
1040
1123
|
return where;
|
|
1041
1124
|
}
|
|
1042
1125
|
async listRuns(query) {
|
|
@@ -1045,7 +1128,7 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1045
1128
|
const orderBy = {
|
|
1046
1129
|
createdAt: "desc"
|
|
1047
1130
|
};
|
|
1048
|
-
if (query.tag || query.attributes?.length) {
|
|
1131
|
+
if (query.tag || query.tags || query.attributes?.length) {
|
|
1049
1132
|
const qb = this.runQueryBuilder(em, query, where).orderBy(orderBy);
|
|
1050
1133
|
if (query.limit != null) qb.limit(query.limit);
|
|
1051
1134
|
if (query.offset != null) qb.offset(query.offset);
|
|
@@ -1071,17 +1154,23 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1071
1154
|
const quote = this.idQuote(em);
|
|
1072
1155
|
const cols = this.attributeColumns(em);
|
|
1073
1156
|
const qb = em.createQueryBuilder(WorkflowRunEntity, "r").where(where);
|
|
1074
|
-
if (query.tag)
|
|
1075
|
-
|
|
1076
|
-
const
|
|
1077
|
-
|
|
1078
|
-
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");
|
|
1079
1161
|
}
|
|
1080
1162
|
for (const f of query.attributes ?? []) {
|
|
1081
1163
|
qb.andWhere(this.attributeExistsSql(f, quote, "r", cols));
|
|
1082
1164
|
}
|
|
1083
1165
|
return qb;
|
|
1084
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
|
+
}
|
|
1085
1174
|
/** `GROUP BY status, origin` over the same predicates {@link listRuns} pages — one aggregate, so a
|
|
1086
1175
|
* console can show whole-set counts next to a bounded page instead of downloading every run to
|
|
1087
1176
|
* count them in the browser. */
|
|
@@ -1093,7 +1182,7 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1093
1182
|
const rows = await this.runQueryBuilder(em, query, this.runWhere(query)).select([
|
|
1094
1183
|
`r.${statusCol}`,
|
|
1095
1184
|
`r.${originCol}`
|
|
1096
|
-
]).addSelect((0,
|
|
1185
|
+
]).addSelect((0, import_core3.raw)("count(*) as count")).groupBy([
|
|
1097
1186
|
`r.${statusCol}`,
|
|
1098
1187
|
`r.${originCol}`
|
|
1099
1188
|
]).execute();
|
|
@@ -1102,20 +1191,50 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1102
1191
|
count: Number(r.count)
|
|
1103
1192
|
})));
|
|
1104
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
|
+
}
|
|
1105
1219
|
/** One attribute predicate as a raw EXISTS subquery on the side-table, correlated to the outer run
|
|
1106
1220
|
* alias. `<>` (ne) also excludes runs where the attribute is absent (missing-key-never-matches):
|
|
1107
1221
|
* EXISTS already requires a present row, so EXISTS(... <> ...) is exactly ne-with-present. Numeric
|
|
1108
|
-
* operands compare the num column, everything else the str column.
|
|
1109
|
-
*
|
|
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). */
|
|
1110
1226
|
attributeExistsSql(f, quote, alias, cols) {
|
|
1111
|
-
const
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
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";
|
|
1115
1233
|
const a = quote(cols.table);
|
|
1116
1234
|
const sub = quote("a");
|
|
1117
1235
|
const outerId = `${quote(alias)}.${quote(cols.runPk)}`;
|
|
1118
|
-
|
|
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})`;
|
|
1119
1238
|
}
|
|
1120
1239
|
/** Resolve the side-table's actual column names from MikroORM metadata, so the raw EXISTS matches
|
|
1121
1240
|
* the active naming strategy (underscore vs camelCase) rather than guessing. The run PK column is
|
|
@@ -1396,13 +1515,19 @@ __name(fromCheckpointEntity, "fromCheckpointEntity");
|
|
|
1396
1515
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1397
1516
|
0 && (module.exports = {
|
|
1398
1517
|
BufferedEventEntity,
|
|
1518
|
+
BufferedEventRepository,
|
|
1399
1519
|
BufferedSignalEntity,
|
|
1520
|
+
BufferedSignalRepository,
|
|
1400
1521
|
ENTITIES,
|
|
1401
1522
|
MikroOrmStateStore,
|
|
1402
1523
|
RunAttributeEntity,
|
|
1524
|
+
RunAttributeRepository,
|
|
1403
1525
|
SignalWaiterEntity,
|
|
1526
|
+
SignalWaiterRepository,
|
|
1404
1527
|
StepCheckpointEntity,
|
|
1528
|
+
StepCheckpointRepository,
|
|
1405
1529
|
WorkflowRunEntity,
|
|
1530
|
+
WorkflowRunRepository,
|
|
1406
1531
|
durableEntities,
|
|
1407
1532
|
durableManagedTables,
|
|
1408
1533
|
ensureMikroOrmDurableSchema
|