@dudousxd/nestjs-durable-store-prisma 0.19.1 → 0.21.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 +241 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -3
- package/dist/index.d.ts +25 -3
- package/dist/index.js +241 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/prisma/schema.prisma +2 -0
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StateStore, WorkflowRun, RetentionPolicy, StepCheckpoint, RunQuery, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, ScheduleRecord, ScheduleQuery, SignalWaiter, ChildWait } from '@dudousxd/nestjs-durable-core';
|
|
1
|
+
import { StateStore, WorkflowRun, RetentionPolicy, StepCheckpoint, RunQuery, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, ScheduleRecord, ScheduleQuery, CheckpointStat, CheckpointQuery, SignalWaiter, ChildWait } from '@dudousxd/nestjs-durable-core';
|
|
2
2
|
|
|
3
3
|
interface RunRow {
|
|
4
4
|
id: string;
|
|
@@ -83,8 +83,8 @@ interface Delegate<Row> {
|
|
|
83
83
|
count: number;
|
|
84
84
|
}>;
|
|
85
85
|
count(args?: Args): Promise<number>;
|
|
86
|
-
/** Aggregate form — used
|
|
87
|
-
* model rows, so the result type is stated at the call site rather than derived from `Row`. */
|
|
86
|
+
/** Aggregate form — used by the run facets and `checkpointStats`, whose rows are aggregate cells,
|
|
87
|
+
* not model rows, so the result type is stated at the call site rather than derived from `Row`. */
|
|
88
88
|
groupBy(args: Args): Promise<Record<string, unknown>[]>;
|
|
89
89
|
}
|
|
90
90
|
interface ScheduleRow {
|
|
@@ -138,6 +138,8 @@ declare class PrismaStateStore implements StateStore {
|
|
|
138
138
|
pruneTerminalRuns(policy: RetentionPolicy, nowMs: number, limit: number): Promise<number>;
|
|
139
139
|
getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null>;
|
|
140
140
|
saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
|
|
141
|
+
saveCheckpointIfPending(checkpoint: StepCheckpoint): Promise<boolean>;
|
|
142
|
+
saveCheckpointsIfPending(checkpoints: StepCheckpoint[]): Promise<boolean[]>;
|
|
141
143
|
transaction<T>(work: (tx: {
|
|
142
144
|
raw: unknown;
|
|
143
145
|
saveCheckpoint: (cp: StepCheckpoint) => Promise<void>;
|
|
@@ -170,7 +172,27 @@ declare class PrismaStateStore implements StateStore {
|
|
|
170
172
|
listSchedules(query: ScheduleQuery): Promise<ScheduleRecord[]>;
|
|
171
173
|
listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
|
|
172
174
|
getLatestCheckpointByName(runId: string, name: string): Promise<StepCheckpoint | undefined>;
|
|
175
|
+
countCheckpoints(runId: string, fromSeq: number, toSeq: number, statuses: StepCheckpoint['status'][]): Promise<number>;
|
|
173
176
|
listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
|
|
177
|
+
/**
|
|
178
|
+
* One `groupBy(parallelGroup, status)` per {@link CheckpointNameKind}, each narrowed to its kind by
|
|
179
|
+
* a name-prefix predicate, run concurrently — Prisma cannot group by a `CASE`, so the kind is the
|
|
180
|
+
* `where` instead. `minEnqueuedAt` reads a NULL `enqueuedAt` as `startedAt` (as
|
|
181
|
+
* {@link fromCheckpointRow} does): a bucket whose non-null `enqueuedAt` count falls short of its
|
|
182
|
+
* row count has such rows, and only for those kinds is the NULL subset's `min(startedAt)` asked for.
|
|
183
|
+
*
|
|
184
|
+
* On SQLite `startsWith` is a case-insensitive `LIKE`, as in {@link listCheckpointsByNamePrefix};
|
|
185
|
+
* the kind prefixes are lower-case and wildcard-free, so only a step whose name starts with one of
|
|
186
|
+
* them in another case would be classified differently there.
|
|
187
|
+
*/
|
|
188
|
+
checkpointStats(runId: string, opts?: {
|
|
189
|
+
group?: string | undefined;
|
|
190
|
+
}): Promise<CheckpointStat[]>;
|
|
191
|
+
/** `min(startedAt)` per `(parallelGroup, status)` over the rows whose `enqueuedAt` is NULL. */
|
|
192
|
+
private unenqueuedStartedAt;
|
|
193
|
+
/** {@link CheckpointQuery} as one `findMany`, in `seq` order, paged by `skip`/`take`. Ungrouped rows
|
|
194
|
+
* are kept past `excludeGroups` explicitly: `NOT IN` alone drops a NULL `parallelGroup`. */
|
|
195
|
+
queryCheckpoints(runId: string, query: CheckpointQuery): Promise<StepCheckpoint[]>;
|
|
174
196
|
putSignalWaiter(waiter: SignalWaiter): Promise<void>;
|
|
175
197
|
takeSignalWaiter(token: string): Promise<SignalWaiter | null>;
|
|
176
198
|
listSignalWaiters(prefix: string): Promise<SignalWaiter[]>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StateStore, WorkflowRun, RetentionPolicy, StepCheckpoint, RunQuery, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, ScheduleRecord, ScheduleQuery, SignalWaiter, ChildWait } from '@dudousxd/nestjs-durable-core';
|
|
1
|
+
import { StateStore, WorkflowRun, RetentionPolicy, StepCheckpoint, RunQuery, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, ScheduleRecord, ScheduleQuery, CheckpointStat, CheckpointQuery, SignalWaiter, ChildWait } from '@dudousxd/nestjs-durable-core';
|
|
2
2
|
|
|
3
3
|
interface RunRow {
|
|
4
4
|
id: string;
|
|
@@ -83,8 +83,8 @@ interface Delegate<Row> {
|
|
|
83
83
|
count: number;
|
|
84
84
|
}>;
|
|
85
85
|
count(args?: Args): Promise<number>;
|
|
86
|
-
/** Aggregate form — used
|
|
87
|
-
* model rows, so the result type is stated at the call site rather than derived from `Row`. */
|
|
86
|
+
/** Aggregate form — used by the run facets and `checkpointStats`, whose rows are aggregate cells,
|
|
87
|
+
* not model rows, so the result type is stated at the call site rather than derived from `Row`. */
|
|
88
88
|
groupBy(args: Args): Promise<Record<string, unknown>[]>;
|
|
89
89
|
}
|
|
90
90
|
interface ScheduleRow {
|
|
@@ -138,6 +138,8 @@ declare class PrismaStateStore implements StateStore {
|
|
|
138
138
|
pruneTerminalRuns(policy: RetentionPolicy, nowMs: number, limit: number): Promise<number>;
|
|
139
139
|
getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null>;
|
|
140
140
|
saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
|
|
141
|
+
saveCheckpointIfPending(checkpoint: StepCheckpoint): Promise<boolean>;
|
|
142
|
+
saveCheckpointsIfPending(checkpoints: StepCheckpoint[]): Promise<boolean[]>;
|
|
141
143
|
transaction<T>(work: (tx: {
|
|
142
144
|
raw: unknown;
|
|
143
145
|
saveCheckpoint: (cp: StepCheckpoint) => Promise<void>;
|
|
@@ -170,7 +172,27 @@ declare class PrismaStateStore implements StateStore {
|
|
|
170
172
|
listSchedules(query: ScheduleQuery): Promise<ScheduleRecord[]>;
|
|
171
173
|
listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
|
|
172
174
|
getLatestCheckpointByName(runId: string, name: string): Promise<StepCheckpoint | undefined>;
|
|
175
|
+
countCheckpoints(runId: string, fromSeq: number, toSeq: number, statuses: StepCheckpoint['status'][]): Promise<number>;
|
|
173
176
|
listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
|
|
177
|
+
/**
|
|
178
|
+
* One `groupBy(parallelGroup, status)` per {@link CheckpointNameKind}, each narrowed to its kind by
|
|
179
|
+
* a name-prefix predicate, run concurrently — Prisma cannot group by a `CASE`, so the kind is the
|
|
180
|
+
* `where` instead. `minEnqueuedAt` reads a NULL `enqueuedAt` as `startedAt` (as
|
|
181
|
+
* {@link fromCheckpointRow} does): a bucket whose non-null `enqueuedAt` count falls short of its
|
|
182
|
+
* row count has such rows, and only for those kinds is the NULL subset's `min(startedAt)` asked for.
|
|
183
|
+
*
|
|
184
|
+
* On SQLite `startsWith` is a case-insensitive `LIKE`, as in {@link listCheckpointsByNamePrefix};
|
|
185
|
+
* the kind prefixes are lower-case and wildcard-free, so only a step whose name starts with one of
|
|
186
|
+
* them in another case would be classified differently there.
|
|
187
|
+
*/
|
|
188
|
+
checkpointStats(runId: string, opts?: {
|
|
189
|
+
group?: string | undefined;
|
|
190
|
+
}): Promise<CheckpointStat[]>;
|
|
191
|
+
/** `min(startedAt)` per `(parallelGroup, status)` over the rows whose `enqueuedAt` is NULL. */
|
|
192
|
+
private unenqueuedStartedAt;
|
|
193
|
+
/** {@link CheckpointQuery} as one `findMany`, in `seq` order, paged by `skip`/`take`. Ungrouped rows
|
|
194
|
+
* are kept past `excludeGroups` explicitly: `NOT IN` alone drops a NULL `parallelGroup`. */
|
|
195
|
+
queryCheckpoints(runId: string, query: CheckpointQuery): Promise<StepCheckpoint[]>;
|
|
174
196
|
putSignalWaiter(waiter: SignalWaiter): Promise<void>;
|
|
175
197
|
takeSignalWaiter(token: string): Promise<SignalWaiter | null>;
|
|
176
198
|
listSignalWaiters(prefix: string): Promise<SignalWaiter[]>;
|
package/dist/index.js
CHANGED
|
@@ -234,6 +234,37 @@ var PrismaStateStore = class {
|
|
|
234
234
|
update: data
|
|
235
235
|
});
|
|
236
236
|
}
|
|
237
|
+
async saveCheckpointIfPending(checkpoint) {
|
|
238
|
+
const { runId, seq, ...data } = toCheckpointData(checkpoint);
|
|
239
|
+
const { count } = await this.db.durableStepCheckpoint.updateMany({
|
|
240
|
+
where: {
|
|
241
|
+
runId,
|
|
242
|
+
seq,
|
|
243
|
+
status: "pending"
|
|
244
|
+
},
|
|
245
|
+
data
|
|
246
|
+
});
|
|
247
|
+
return count > 0;
|
|
248
|
+
}
|
|
249
|
+
async saveCheckpointsIfPending(checkpoints) {
|
|
250
|
+
if (checkpoints.length === 0) return [];
|
|
251
|
+
return this.db.$transaction(async (tx) => {
|
|
252
|
+
const wrote = [];
|
|
253
|
+
for (const checkpoint of checkpoints) {
|
|
254
|
+
const { runId, seq, ...data } = toCheckpointData(checkpoint);
|
|
255
|
+
const { count } = await tx.durableStepCheckpoint.updateMany({
|
|
256
|
+
where: {
|
|
257
|
+
runId,
|
|
258
|
+
seq,
|
|
259
|
+
status: "pending"
|
|
260
|
+
},
|
|
261
|
+
data
|
|
262
|
+
});
|
|
263
|
+
wrote.push(count > 0);
|
|
264
|
+
}
|
|
265
|
+
return wrote;
|
|
266
|
+
});
|
|
267
|
+
}
|
|
237
268
|
async transaction(work) {
|
|
238
269
|
return this.db.$transaction(async (tx) => work({
|
|
239
270
|
raw: tx,
|
|
@@ -568,6 +599,21 @@ var PrismaStateStore = class {
|
|
|
568
599
|
});
|
|
569
600
|
return row ? fromCheckpointRow(row) : void 0;
|
|
570
601
|
}
|
|
602
|
+
async countCheckpoints(runId, fromSeq, toSeq, statuses) {
|
|
603
|
+
if (statuses.length === 0) return 0;
|
|
604
|
+
return this.db.durableStepCheckpoint.count({
|
|
605
|
+
where: {
|
|
606
|
+
runId,
|
|
607
|
+
seq: {
|
|
608
|
+
gte: fromSeq,
|
|
609
|
+
lte: toSeq
|
|
610
|
+
},
|
|
611
|
+
status: {
|
|
612
|
+
in: statuses
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
}
|
|
571
617
|
async listCheckpointsByNamePrefix(runId, prefixes) {
|
|
572
618
|
if (prefixes.length === 0) return [];
|
|
573
619
|
const rows = await this.db.durableStepCheckpoint.findMany({
|
|
@@ -583,6 +629,137 @@ var PrismaStateStore = class {
|
|
|
583
629
|
seq: "asc"
|
|
584
630
|
}
|
|
585
631
|
});
|
|
632
|
+
return rows.filter((r) => prefixes.some((p) => r.name.startsWith(p))).map(fromCheckpointRow);
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* One `groupBy(parallelGroup, status)` per {@link CheckpointNameKind}, each narrowed to its kind by
|
|
636
|
+
* a name-prefix predicate, run concurrently — Prisma cannot group by a `CASE`, so the kind is the
|
|
637
|
+
* `where` instead. `minEnqueuedAt` reads a NULL `enqueuedAt` as `startedAt` (as
|
|
638
|
+
* {@link fromCheckpointRow} does): a bucket whose non-null `enqueuedAt` count falls short of its
|
|
639
|
+
* row count has such rows, and only for those kinds is the NULL subset's `min(startedAt)` asked for.
|
|
640
|
+
*
|
|
641
|
+
* On SQLite `startsWith` is a case-insensitive `LIKE`, as in {@link listCheckpointsByNamePrefix};
|
|
642
|
+
* the kind prefixes are lower-case and wildcard-free, so only a step whose name starts with one of
|
|
643
|
+
* them in another case would be classified differently there.
|
|
644
|
+
*/
|
|
645
|
+
async checkpointStats(runId, opts) {
|
|
646
|
+
const scope = opts?.group === void 0 ? {
|
|
647
|
+
runId
|
|
648
|
+
} : {
|
|
649
|
+
runId,
|
|
650
|
+
parallelGroup: opts.group
|
|
651
|
+
};
|
|
652
|
+
const grouped = await Promise.all(NAME_KINDS.map(async ({ kind, where }) => ({
|
|
653
|
+
kind,
|
|
654
|
+
where,
|
|
655
|
+
rows: await this.db.durableStepCheckpoint.groupBy({
|
|
656
|
+
by: [
|
|
657
|
+
"parallelGroup",
|
|
658
|
+
"status"
|
|
659
|
+
],
|
|
660
|
+
where: {
|
|
661
|
+
...scope,
|
|
662
|
+
...where
|
|
663
|
+
},
|
|
664
|
+
_count: {
|
|
665
|
+
_all: true,
|
|
666
|
+
enqueuedAt: true
|
|
667
|
+
},
|
|
668
|
+
_min: {
|
|
669
|
+
seq: true,
|
|
670
|
+
name: true,
|
|
671
|
+
enqueuedAt: true,
|
|
672
|
+
startedAt: true
|
|
673
|
+
},
|
|
674
|
+
_max: {
|
|
675
|
+
seq: true,
|
|
676
|
+
name: true,
|
|
677
|
+
startedAt: true,
|
|
678
|
+
finishedAt: true
|
|
679
|
+
},
|
|
680
|
+
_sum: {
|
|
681
|
+
attempts: true
|
|
682
|
+
}
|
|
683
|
+
})
|
|
684
|
+
})));
|
|
685
|
+
const stats = await Promise.all(grouped.map(async ({ kind, where, rows }) => {
|
|
686
|
+
const unenqueued = rows.some((r) => r._count.enqueuedAt < r._count._all) ? await this.unenqueuedStartedAt({
|
|
687
|
+
...scope,
|
|
688
|
+
...where
|
|
689
|
+
}) : /* @__PURE__ */ new Map();
|
|
690
|
+
return rows.map((r) => toCheckpointStat(kind, r, unenqueued));
|
|
691
|
+
}));
|
|
692
|
+
return stats.flat();
|
|
693
|
+
}
|
|
694
|
+
/** `min(startedAt)` per `(parallelGroup, status)` over the rows whose `enqueuedAt` is NULL. */
|
|
695
|
+
async unenqueuedStartedAt(where) {
|
|
696
|
+
const rows = await this.db.durableStepCheckpoint.groupBy({
|
|
697
|
+
by: [
|
|
698
|
+
"parallelGroup",
|
|
699
|
+
"status"
|
|
700
|
+
],
|
|
701
|
+
where: {
|
|
702
|
+
...where,
|
|
703
|
+
enqueuedAt: null
|
|
704
|
+
},
|
|
705
|
+
_min: {
|
|
706
|
+
startedAt: true
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
return new Map(rows.map((r) => [
|
|
710
|
+
bucketKey(r.parallelGroup, r.status),
|
|
711
|
+
r._min.startedAt
|
|
712
|
+
]));
|
|
713
|
+
}
|
|
714
|
+
/** {@link CheckpointQuery} as one `findMany`, in `seq` order, paged by `skip`/`take`. Ungrouped rows
|
|
715
|
+
* are kept past `excludeGroups` explicitly: `NOT IN` alone drops a NULL `parallelGroup`. */
|
|
716
|
+
async queryCheckpoints(runId, query) {
|
|
717
|
+
if (query.statuses?.length === 0 || query.namePrefixes?.length === 0) return [];
|
|
718
|
+
const and = [];
|
|
719
|
+
if (query.excludeGroups?.length) {
|
|
720
|
+
and.push({
|
|
721
|
+
OR: [
|
|
722
|
+
{
|
|
723
|
+
parallelGroup: null
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
parallelGroup: {
|
|
727
|
+
notIn: query.excludeGroups
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
]
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
if (query.namePrefixes) {
|
|
734
|
+
and.push({
|
|
735
|
+
OR: query.namePrefixes.map((p) => ({
|
|
736
|
+
name: {
|
|
737
|
+
startsWith: p
|
|
738
|
+
}
|
|
739
|
+
}))
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
const rows = await this.db.durableStepCheckpoint.findMany({
|
|
743
|
+
where: {
|
|
744
|
+
runId,
|
|
745
|
+
...query.group === void 0 ? {} : {
|
|
746
|
+
parallelGroup: query.group
|
|
747
|
+
},
|
|
748
|
+
...query.statuses ? {
|
|
749
|
+
status: {
|
|
750
|
+
in: query.statuses
|
|
751
|
+
}
|
|
752
|
+
} : {},
|
|
753
|
+
...and.length ? {
|
|
754
|
+
AND: and
|
|
755
|
+
} : {}
|
|
756
|
+
},
|
|
757
|
+
orderBy: {
|
|
758
|
+
seq: "asc"
|
|
759
|
+
},
|
|
760
|
+
skip: query.offset,
|
|
761
|
+
take: query.limit
|
|
762
|
+
});
|
|
586
763
|
return rows.map(fromCheckpointRow);
|
|
587
764
|
}
|
|
588
765
|
async putSignalWaiter(waiter) {
|
|
@@ -630,7 +807,7 @@ var PrismaStateStore = class {
|
|
|
630
807
|
}
|
|
631
808
|
}
|
|
632
809
|
});
|
|
633
|
-
return rows.map((r) => ({
|
|
810
|
+
return rows.filter((r) => r.token.startsWith(prefix)).map((r) => ({
|
|
634
811
|
token: r.token,
|
|
635
812
|
runId: r.runId,
|
|
636
813
|
seq: r.seq,
|
|
@@ -920,6 +1097,69 @@ function fromRunRow(row) {
|
|
|
920
1097
|
};
|
|
921
1098
|
}
|
|
922
1099
|
__name(fromRunRow, "fromRunRow");
|
|
1100
|
+
var KIND_PREFIXES = [
|
|
1101
|
+
[
|
|
1102
|
+
"child",
|
|
1103
|
+
"signal:child:"
|
|
1104
|
+
],
|
|
1105
|
+
[
|
|
1106
|
+
"folded",
|
|
1107
|
+
"fan:"
|
|
1108
|
+
],
|
|
1109
|
+
[
|
|
1110
|
+
"spawn",
|
|
1111
|
+
"spawn:"
|
|
1112
|
+
]
|
|
1113
|
+
];
|
|
1114
|
+
var NAME_KINDS = [
|
|
1115
|
+
...KIND_PREFIXES.map(([kind, prefix]) => ({
|
|
1116
|
+
kind,
|
|
1117
|
+
where: {
|
|
1118
|
+
name: {
|
|
1119
|
+
startsWith: prefix
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
})),
|
|
1123
|
+
{
|
|
1124
|
+
kind: "step",
|
|
1125
|
+
where: {
|
|
1126
|
+
NOT: KIND_PREFIXES.map(([, prefix]) => ({
|
|
1127
|
+
name: {
|
|
1128
|
+
startsWith: prefix
|
|
1129
|
+
}
|
|
1130
|
+
}))
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
];
|
|
1134
|
+
function bucketKey(group, status) {
|
|
1135
|
+
return JSON.stringify([
|
|
1136
|
+
group,
|
|
1137
|
+
status
|
|
1138
|
+
]);
|
|
1139
|
+
}
|
|
1140
|
+
__name(bucketKey, "bucketKey");
|
|
1141
|
+
function toCheckpointStat(nameKind, r, unenqueued) {
|
|
1142
|
+
const fallback = unenqueued.get(bucketKey(r.parallelGroup, r.status));
|
|
1143
|
+
const enqueued = r._min.enqueuedAt;
|
|
1144
|
+
const minEnqueuedAt = enqueued && fallback ? enqueued < fallback ? enqueued : fallback : enqueued ?? fallback;
|
|
1145
|
+
return {
|
|
1146
|
+
group: r.parallelGroup,
|
|
1147
|
+
status: r.status,
|
|
1148
|
+
nameKind,
|
|
1149
|
+
count: r._count._all,
|
|
1150
|
+
minSeq: r._min.seq,
|
|
1151
|
+
maxSeq: r._max.seq,
|
|
1152
|
+
minName: r._min.name,
|
|
1153
|
+
maxName: r._max.name,
|
|
1154
|
+
// Every row has `enqueuedAt` or is in the NULL subset, so one of the two is set.
|
|
1155
|
+
minEnqueuedAt,
|
|
1156
|
+
minStartedAt: r._min.startedAt,
|
|
1157
|
+
maxStartedAt: r._max.startedAt,
|
|
1158
|
+
maxFinishedAt: r._max.finishedAt,
|
|
1159
|
+
sumAttempts: Number(r._sum.attempts ?? 0)
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
__name(toCheckpointStat, "toCheckpointStat");
|
|
923
1163
|
function toCheckpointData(cp) {
|
|
924
1164
|
return {
|
|
925
1165
|
runId: cp.runId,
|