@dudousxd/nestjs-durable-store-mikro-orm 0.29.0 → 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 +145 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +145 -1
- 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,
|
|
@@ -1640,6 +1672,103 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1640
1672
|
seq: "asc"
|
|
1641
1673
|
}
|
|
1642
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
|
+
});
|
|
1643
1772
|
return rows.map(fromCheckpointEntity);
|
|
1644
1773
|
}
|
|
1645
1774
|
async putSignalWaiter(waiter) {
|
|
@@ -1674,7 +1803,7 @@ var MikroOrmStateStore = class _MikroOrmStateStore {
|
|
|
1674
1803
|
$like: `${prefix}%`
|
|
1675
1804
|
}
|
|
1676
1805
|
});
|
|
1677
|
-
return rows.map((e) => ({
|
|
1806
|
+
return rows.filter((e) => e.token.startsWith(prefix)).map((e) => ({
|
|
1678
1807
|
token: e.token,
|
|
1679
1808
|
runId: e.runId,
|
|
1680
1809
|
seq: e.seq,
|
|
@@ -1856,6 +1985,21 @@ function fromRunEntity(e) {
|
|
|
1856
1985
|
};
|
|
1857
1986
|
}
|
|
1858
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");
|
|
1859
2003
|
function toCheckpointEntity(cp) {
|
|
1860
2004
|
const e = new StepCheckpointEntity();
|
|
1861
2005
|
e.runId = cp.runId;
|