@dudousxd/nestjs-durable-store-mikro-orm 0.28.1 → 0.30.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 +166 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -2
- package/dist/index.d.ts +17 -2
- package/dist/index.js +166 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -379,6 +379,19 @@ function durableEntities(options = {}) {
|
|
|
379
379
|
class: StepCheckpointEntity,
|
|
380
380
|
tableName: "durable_step_checkpoints",
|
|
381
381
|
repository: /* @__PURE__ */ __name(() => StepCheckpointRepository, "repository"),
|
|
382
|
+
// `(runId, parallelGroup, seq)` serves the run detail's per-group reads — `checkpointStats` with a
|
|
383
|
+
// group and `queryCheckpoints` paging one fan in `seq` order — without scanning the whole run.
|
|
384
|
+
// Mirrors the Prisma adapter's index of the same name.
|
|
385
|
+
indexes: [
|
|
386
|
+
{
|
|
387
|
+
name: "durable_step_checkpoints_group_idx",
|
|
388
|
+
properties: [
|
|
389
|
+
"runId",
|
|
390
|
+
"parallelGroup",
|
|
391
|
+
"seq"
|
|
392
|
+
]
|
|
393
|
+
}
|
|
394
|
+
],
|
|
382
395
|
properties: {
|
|
383
396
|
runId: {
|
|
384
397
|
type: "string",
|
|
@@ -1120,6 +1133,25 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1120
1133
|
});
|
|
1121
1134
|
return affected > 0;
|
|
1122
1135
|
}
|
|
1136
|
+
async saveCheckpointsIfPending(checkpoints) {
|
|
1137
|
+
if (checkpoints.length === 0) return [];
|
|
1138
|
+
return this.fork().transactional(async (em) => {
|
|
1139
|
+
const wrote = [];
|
|
1140
|
+
for (const checkpoint of checkpoints) {
|
|
1141
|
+
const { runId: _runId, seq: _seq, ...data } = toCheckpointEntity(checkpoint);
|
|
1142
|
+
const affected = await em.nativeUpdate(StepCheckpointEntity, {
|
|
1143
|
+
runId: checkpoint.runId,
|
|
1144
|
+
seq: checkpoint.seq,
|
|
1145
|
+
status: "pending"
|
|
1146
|
+
}, {
|
|
1147
|
+
...data,
|
|
1148
|
+
wakeAt: checkpoint.wakeAt != null ? new Date(checkpoint.wakeAt) : null
|
|
1149
|
+
});
|
|
1150
|
+
wrote.push(affected > 0);
|
|
1151
|
+
}
|
|
1152
|
+
return wrote;
|
|
1153
|
+
});
|
|
1154
|
+
}
|
|
1123
1155
|
async transaction(work) {
|
|
1124
1156
|
return this.fork().transactional(async (em) => work({
|
|
1125
1157
|
raw: em,
|
|
@@ -1158,7 +1190,7 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1158
1190
|
});
|
|
1159
1191
|
return rows.map(fromRunEntity);
|
|
1160
1192
|
}
|
|
1161
|
-
async listDueTimers(nowMs, namespace) {
|
|
1193
|
+
async listDueTimers(nowMs, namespace, limit) {
|
|
1162
1194
|
const em = this.fork();
|
|
1163
1195
|
const rows = await em.find(WorkflowRunEntity, {
|
|
1164
1196
|
status: "suspended",
|
|
@@ -1169,6 +1201,13 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1169
1201
|
...namespace !== void 0 ? {
|
|
1170
1202
|
namespace
|
|
1171
1203
|
} : {}
|
|
1204
|
+
}, {
|
|
1205
|
+
orderBy: {
|
|
1206
|
+
wakeAt: "asc"
|
|
1207
|
+
},
|
|
1208
|
+
...limit !== void 0 ? {
|
|
1209
|
+
limit
|
|
1210
|
+
} : {}
|
|
1172
1211
|
});
|
|
1173
1212
|
return rows.map(fromRunEntity);
|
|
1174
1213
|
}
|
|
@@ -1605,6 +1644,19 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1605
1644
|
});
|
|
1606
1645
|
return entity ? fromCheckpointEntity(entity) : void 0;
|
|
1607
1646
|
}
|
|
1647
|
+
async countCheckpoints(runId, fromSeq, toSeq, statuses) {
|
|
1648
|
+
if (statuses.length === 0) return 0;
|
|
1649
|
+
return this.fork().count(StepCheckpointEntity, {
|
|
1650
|
+
runId,
|
|
1651
|
+
seq: {
|
|
1652
|
+
$gte: fromSeq,
|
|
1653
|
+
$lte: toSeq
|
|
1654
|
+
},
|
|
1655
|
+
status: {
|
|
1656
|
+
$in: statuses
|
|
1657
|
+
}
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1608
1660
|
async listCheckpointsByNamePrefix(runId, prefixes) {
|
|
1609
1661
|
if (prefixes.length === 0) return [];
|
|
1610
1662
|
const em = this.fork();
|
|
@@ -1620,6 +1672,103 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1620
1672
|
seq: "asc"
|
|
1621
1673
|
}
|
|
1622
1674
|
});
|
|
1675
|
+
return rows.filter((r) => prefixes.some((p) => r.name.startsWith(p))).map(fromCheckpointEntity);
|
|
1676
|
+
}
|
|
1677
|
+
/** One `GROUP BY parallel_group, status, <name kind>` over the run's checkpoints — the buckets a
|
|
1678
|
+
* run detail summarizes a fan from, without loading its rows. The name-kind `CASE` is the SQL of
|
|
1679
|
+
* `checkpointNameKind`, and `min(coalesce(enqueued_at, started_at))` reads `enqueuedAt` the way
|
|
1680
|
+
* {@link listCheckpoints} does. Raw SQL on metadata-resolved names, so a custom column naming
|
|
1681
|
+
* strategy is honoured. */
|
|
1682
|
+
async checkpointStats(runId, opts) {
|
|
1683
|
+
const em = this.fork();
|
|
1684
|
+
const quote = this.idQuote(em);
|
|
1685
|
+
const meta = em.getMetadata().get(StepCheckpointEntity);
|
|
1686
|
+
const c = /* @__PURE__ */ __name((prop) => quote(meta.properties[prop]?.fieldNames?.[0] ?? prop), "c");
|
|
1687
|
+
const nameKind = `case when ${c("name")} like 'signal:child:%' then 'child' when ${c("name")} like 'fan:%' then 'folded' when ${c("name")} like 'spawn:%' then 'spawn' else 'step' end`;
|
|
1688
|
+
const params = [
|
|
1689
|
+
runId
|
|
1690
|
+
];
|
|
1691
|
+
let where = `${c("runId")} = ?`;
|
|
1692
|
+
if (opts?.group !== void 0) {
|
|
1693
|
+
where += ` and ${c("parallelGroup")} = ?`;
|
|
1694
|
+
params.push(opts.group);
|
|
1695
|
+
}
|
|
1696
|
+
const sql = `select ${c("parallelGroup")} as ${quote("grp")}, ${c("status")} as ${quote("status")}, ${nameKind} as ${quote("name_kind")}, count(*) as ${quote("n")}, min(${c("seq")}) as ${quote("min_seq")}, max(${c("seq")}) as ${quote("max_seq")}, min(${c("name")}) as ${quote("min_name")}, max(${c("name")}) as ${quote("max_name")}, min(coalesce(${c("enqueuedAt")}, ${c("startedAt")})) as ${quote("min_enqueued_at")}, min(${c("startedAt")}) as ${quote("min_started_at")}, max(${c("startedAt")}) as ${quote("max_started_at")}, max(${c("finishedAt")}) as ${quote("max_finished_at")}, sum(${c("attempts")}) as ${quote("sum_attempts")} from ${quote(meta.tableName)} where ${where} group by ${c("parallelGroup")}, ${c("status")}, ${nameKind}`;
|
|
1697
|
+
const rows = await em.getConnection().execute(sql, params);
|
|
1698
|
+
const date = aggregateDateReader(em);
|
|
1699
|
+
return rows.map((row) => ({
|
|
1700
|
+
group: row.grp ?? null,
|
|
1701
|
+
status: row.status,
|
|
1702
|
+
nameKind: row.name_kind,
|
|
1703
|
+
count: Number(row.n),
|
|
1704
|
+
minSeq: Number(row.min_seq),
|
|
1705
|
+
maxSeq: Number(row.max_seq),
|
|
1706
|
+
minName: String(row.min_name),
|
|
1707
|
+
maxName: String(row.max_name),
|
|
1708
|
+
minEnqueuedAt: date(row.min_enqueued_at),
|
|
1709
|
+
minStartedAt: date(row.min_started_at),
|
|
1710
|
+
maxStartedAt: date(row.max_started_at),
|
|
1711
|
+
maxFinishedAt: date(row.max_finished_at),
|
|
1712
|
+
sumAttempts: Number(row.sum_attempts)
|
|
1713
|
+
}));
|
|
1714
|
+
}
|
|
1715
|
+
/** The run's checkpoints matching `query`, in `seq` order and paged in the database — a fan's
|
|
1716
|
+
* failures or one page of its members without the rest of the run. An excluded group keeps the
|
|
1717
|
+
* ungrouped rows (`parallel_group is null or not in (...)`); an empty `statuses`/`namePrefixes`
|
|
1718
|
+
* matches nothing, so it returns before querying. */
|
|
1719
|
+
async queryCheckpoints(runId, query) {
|
|
1720
|
+
if (query.statuses?.length === 0 || query.namePrefixes?.length === 0) return [];
|
|
1721
|
+
const and = [
|
|
1722
|
+
{
|
|
1723
|
+
runId
|
|
1724
|
+
}
|
|
1725
|
+
];
|
|
1726
|
+
if (query.group !== void 0) and.push({
|
|
1727
|
+
parallelGroup: query.group
|
|
1728
|
+
});
|
|
1729
|
+
if (query.excludeGroups?.length) {
|
|
1730
|
+
and.push({
|
|
1731
|
+
$or: [
|
|
1732
|
+
{
|
|
1733
|
+
parallelGroup: null
|
|
1734
|
+
},
|
|
1735
|
+
{
|
|
1736
|
+
parallelGroup: {
|
|
1737
|
+
$nin: query.excludeGroups
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
]
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1743
|
+
if (query.statuses) and.push({
|
|
1744
|
+
status: {
|
|
1745
|
+
$in: query.statuses
|
|
1746
|
+
}
|
|
1747
|
+
});
|
|
1748
|
+
if (query.namePrefixes) {
|
|
1749
|
+
and.push({
|
|
1750
|
+
$or: query.namePrefixes.map((p) => ({
|
|
1751
|
+
name: {
|
|
1752
|
+
$like: `${p}%`
|
|
1753
|
+
}
|
|
1754
|
+
}))
|
|
1755
|
+
});
|
|
1756
|
+
}
|
|
1757
|
+
const rows = await this.fork().find(StepCheckpointEntity, {
|
|
1758
|
+
$and: and
|
|
1759
|
+
}, {
|
|
1760
|
+
orderBy: {
|
|
1761
|
+
seq: "asc"
|
|
1762
|
+
},
|
|
1763
|
+
// An offset alone is not valid SQL on SQLite/MySQL, so an unbounded page gets a limit no
|
|
1764
|
+
// run reaches.
|
|
1765
|
+
...query.limit !== void 0 || query.offset ? {
|
|
1766
|
+
limit: query.limit ?? Number.MAX_SAFE_INTEGER
|
|
1767
|
+
} : {},
|
|
1768
|
+
...query.offset ? {
|
|
1769
|
+
offset: query.offset
|
|
1770
|
+
} : {}
|
|
1771
|
+
});
|
|
1623
1772
|
return rows.map(fromCheckpointEntity);
|
|
1624
1773
|
}
|
|
1625
1774
|
async putSignalWaiter(waiter) {
|
|
@@ -1654,7 +1803,7 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1654
1803
|
$like: `${prefix}%`
|
|
1655
1804
|
}
|
|
1656
1805
|
});
|
|
1657
|
-
return rows.map((e) => ({
|
|
1806
|
+
return rows.filter((e) => e.token.startsWith(prefix)).map((e) => ({
|
|
1658
1807
|
token: e.token,
|
|
1659
1808
|
runId: e.runId,
|
|
1660
1809
|
seq: e.seq,
|
|
@@ -1836,6 +1985,21 @@ function fromRunEntity(e) {
|
|
|
1836
1985
|
};
|
|
1837
1986
|
}
|
|
1838
1987
|
__name(fromRunEntity, "fromRunEntity");
|
|
1988
|
+
function aggregateDateReader(em) {
|
|
1989
|
+
const platform = em.getPlatform();
|
|
1990
|
+
const tz = platform.getTimezone();
|
|
1991
|
+
return (value) => {
|
|
1992
|
+
if (value instanceof Date) return value;
|
|
1993
|
+
if (typeof value === "bigint") return platform.parseDate(Number(value));
|
|
1994
|
+
if (typeof value === "number" || !tz || tz === "local") {
|
|
1995
|
+
return platform.parseDate(value);
|
|
1996
|
+
}
|
|
1997
|
+
const text = String(value);
|
|
1998
|
+
const zoned = text.includes("+") || text.lastIndexOf("-") > 10 || text.endsWith("Z");
|
|
1999
|
+
return platform.parseDate(zoned ? text : `${text}${tz}`);
|
|
2000
|
+
};
|
|
2001
|
+
}
|
|
2002
|
+
__name(aggregateDateReader, "aggregateDateReader");
|
|
1839
2003
|
function toCheckpointEntity(cp) {
|
|
1840
2004
|
const e = new StepCheckpointEntity();
|
|
1841
2005
|
e.runId = cp.runId;
|