@dudousxd/nestjs-durable-store-prisma 0.17.0 → 0.18.1
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 +270 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -2
- package/dist/index.d.ts +28 -2
- package/dist/index.js +271 -3
- package/dist/index.js.map +1 -1
- package/package.json +13 -3
- package/prisma/schema.prisma +20 -0
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StateStore, WorkflowRun, StepCheckpoint, RunQuery, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, SignalWaiter } from '@dudousxd/nestjs-durable-core';
|
|
1
|
+
import { StateStore, WorkflowRun, RetentionPolicy, StepCheckpoint, RunQuery, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, ScheduleRecord, ScheduleQuery, SignalWaiter } from '@dudousxd/nestjs-durable-core';
|
|
2
2
|
|
|
3
3
|
interface RunRow {
|
|
4
4
|
id: string;
|
|
@@ -82,10 +82,23 @@ interface Delegate<Row> {
|
|
|
82
82
|
deleteMany(args?: Args): Promise<{
|
|
83
83
|
count: number;
|
|
84
84
|
}>;
|
|
85
|
+
count(args?: Args): Promise<number>;
|
|
85
86
|
/** Aggregate form — used only by `runFacets`, whose rows are `(status, origin, count)` cells, not
|
|
86
87
|
* model rows, so the result type is stated at the call site rather than derived from `Row`. */
|
|
87
88
|
groupBy(args: Args): Promise<Record<string, unknown>[]>;
|
|
88
89
|
}
|
|
90
|
+
interface ScheduleRow {
|
|
91
|
+
id: string;
|
|
92
|
+
namespace: string;
|
|
93
|
+
workflow: string;
|
|
94
|
+
paused: boolean;
|
|
95
|
+
nextFireAt: bigint | null;
|
|
96
|
+
tags: unknown;
|
|
97
|
+
spec: unknown;
|
|
98
|
+
state: unknown;
|
|
99
|
+
createdAt: Date;
|
|
100
|
+
updatedAt: Date;
|
|
101
|
+
}
|
|
89
102
|
interface DurablePrismaTx {
|
|
90
103
|
durableWorkflowRun: Delegate<RunRow>;
|
|
91
104
|
durableStepCheckpoint: Delegate<CheckpointRow>;
|
|
@@ -93,6 +106,8 @@ interface DurablePrismaTx {
|
|
|
93
106
|
durableSignalWaiter: Delegate<WaiterRow>;
|
|
94
107
|
durableBufferedSignal: Delegate<BufferedSignalRow>;
|
|
95
108
|
durableBufferedEvent: Delegate<BufferedEventRow>;
|
|
109
|
+
/** Only needed for persisted schedules (`engine.schedules`) — add the `DurableSchedule` model. */
|
|
110
|
+
durableSchedule?: Delegate<ScheduleRow>;
|
|
96
111
|
}
|
|
97
112
|
interface DurablePrismaClient extends DurablePrismaTx {
|
|
98
113
|
/** Prisma's interactive-transaction form: runs `fn` with a tx-scoped client and commits on resolve. */
|
|
@@ -111,13 +126,16 @@ interface DurablePrismaClient extends DurablePrismaTx {
|
|
|
111
126
|
declare class PrismaStateStore implements StateStore {
|
|
112
127
|
private readonly db;
|
|
113
128
|
constructor(db: DurablePrismaClient);
|
|
129
|
+
tryAdmitSingleton(runId: string, tag: string, workflow: string, limit: number, retryWakeAt?: number): Promise<boolean>;
|
|
114
130
|
createRun(run: WorkflowRun): Promise<void>;
|
|
115
|
-
updateRun(runId: string,
|
|
131
|
+
updateRun(runId: string, inputPatch: Partial<WorkflowRun>): Promise<void>;
|
|
116
132
|
/** Rewrite a run's normalized attribute rows: delete the old set, insert the current one. Mirrors
|
|
117
133
|
* the in-memory store's reindex so the side-table always reflects the run's live searchAttributes. */
|
|
118
134
|
private reindexAttributes;
|
|
119
135
|
getRun(runId: string): Promise<WorkflowRun | null>;
|
|
120
136
|
deleteRun(runId: string): Promise<void>;
|
|
137
|
+
deleteRuns(runIds: string[]): Promise<void>;
|
|
138
|
+
pruneTerminalRuns(policy: RetentionPolicy, nowMs: number, limit: number): Promise<number>;
|
|
121
139
|
getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null>;
|
|
122
140
|
saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
|
|
123
141
|
transaction<T>(work: (tx: {
|
|
@@ -133,6 +151,7 @@ declare class PrismaStateStore implements StateStore {
|
|
|
133
151
|
/** The `where` every run query shares — {@link listRuns} pages it, {@link runFacets} groups it. */
|
|
134
152
|
private runWhere;
|
|
135
153
|
listRuns(query: RunQuery): Promise<WorkflowRun[]>;
|
|
154
|
+
countRuns(query: Omit<RunQuery, 'limit' | 'offset'>): Promise<number>;
|
|
136
155
|
/** `GROUP BY status, origin` over the same predicates {@link listRuns} pages — one aggregate, so a
|
|
137
156
|
* console can show whole-set counts next to a bounded page instead of downloading every run to
|
|
138
157
|
* count them in the browser. */
|
|
@@ -142,6 +161,13 @@ declare class PrismaStateStore implements StateStore {
|
|
|
142
161
|
* attribute axes live outside the row (json column / side table) and are counted from a bounded
|
|
143
162
|
* page of runs instead — see `RunValueFacetOptions.scan`. */
|
|
144
163
|
runValueFacets(axis: RunValueAxis, query: RunFacetQuery, opts?: RunValueFacetOptions): Promise<RunValueFacetRow[]>;
|
|
164
|
+
private schedules;
|
|
165
|
+
saveSchedule(record: ScheduleRecord): Promise<void>;
|
|
166
|
+
getSchedule(id: string): Promise<ScheduleRecord | null>;
|
|
167
|
+
/** One conditional `updateMany`: the compare-and-set on `next_fire_at` is a single statement. */
|
|
168
|
+
updateSchedule(id: string, patch: Partial<Omit<ScheduleRecord, 'id' | 'createdAt'>>, expectedNextFireAt?: number | null): Promise<boolean>;
|
|
169
|
+
deleteSchedule(id: string): Promise<boolean>;
|
|
170
|
+
listSchedules(query: ScheduleQuery): Promise<ScheduleRecord[]>;
|
|
145
171
|
listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
|
|
146
172
|
getLatestCheckpointByName(runId: string, name: string): Promise<StepCheckpoint | undefined>;
|
|
147
173
|
listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StateStore, WorkflowRun, StepCheckpoint, RunQuery, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, SignalWaiter } from '@dudousxd/nestjs-durable-core';
|
|
1
|
+
import { StateStore, WorkflowRun, RetentionPolicy, StepCheckpoint, RunQuery, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, ScheduleRecord, ScheduleQuery, SignalWaiter } from '@dudousxd/nestjs-durable-core';
|
|
2
2
|
|
|
3
3
|
interface RunRow {
|
|
4
4
|
id: string;
|
|
@@ -82,10 +82,23 @@ interface Delegate<Row> {
|
|
|
82
82
|
deleteMany(args?: Args): Promise<{
|
|
83
83
|
count: number;
|
|
84
84
|
}>;
|
|
85
|
+
count(args?: Args): Promise<number>;
|
|
85
86
|
/** Aggregate form — used only by `runFacets`, whose rows are `(status, origin, count)` cells, not
|
|
86
87
|
* model rows, so the result type is stated at the call site rather than derived from `Row`. */
|
|
87
88
|
groupBy(args: Args): Promise<Record<string, unknown>[]>;
|
|
88
89
|
}
|
|
90
|
+
interface ScheduleRow {
|
|
91
|
+
id: string;
|
|
92
|
+
namespace: string;
|
|
93
|
+
workflow: string;
|
|
94
|
+
paused: boolean;
|
|
95
|
+
nextFireAt: bigint | null;
|
|
96
|
+
tags: unknown;
|
|
97
|
+
spec: unknown;
|
|
98
|
+
state: unknown;
|
|
99
|
+
createdAt: Date;
|
|
100
|
+
updatedAt: Date;
|
|
101
|
+
}
|
|
89
102
|
interface DurablePrismaTx {
|
|
90
103
|
durableWorkflowRun: Delegate<RunRow>;
|
|
91
104
|
durableStepCheckpoint: Delegate<CheckpointRow>;
|
|
@@ -93,6 +106,8 @@ interface DurablePrismaTx {
|
|
|
93
106
|
durableSignalWaiter: Delegate<WaiterRow>;
|
|
94
107
|
durableBufferedSignal: Delegate<BufferedSignalRow>;
|
|
95
108
|
durableBufferedEvent: Delegate<BufferedEventRow>;
|
|
109
|
+
/** Only needed for persisted schedules (`engine.schedules`) — add the `DurableSchedule` model. */
|
|
110
|
+
durableSchedule?: Delegate<ScheduleRow>;
|
|
96
111
|
}
|
|
97
112
|
interface DurablePrismaClient extends DurablePrismaTx {
|
|
98
113
|
/** Prisma's interactive-transaction form: runs `fn` with a tx-scoped client and commits on resolve. */
|
|
@@ -111,13 +126,16 @@ interface DurablePrismaClient extends DurablePrismaTx {
|
|
|
111
126
|
declare class PrismaStateStore implements StateStore {
|
|
112
127
|
private readonly db;
|
|
113
128
|
constructor(db: DurablePrismaClient);
|
|
129
|
+
tryAdmitSingleton(runId: string, tag: string, workflow: string, limit: number, retryWakeAt?: number): Promise<boolean>;
|
|
114
130
|
createRun(run: WorkflowRun): Promise<void>;
|
|
115
|
-
updateRun(runId: string,
|
|
131
|
+
updateRun(runId: string, inputPatch: Partial<WorkflowRun>): Promise<void>;
|
|
116
132
|
/** Rewrite a run's normalized attribute rows: delete the old set, insert the current one. Mirrors
|
|
117
133
|
* the in-memory store's reindex so the side-table always reflects the run's live searchAttributes. */
|
|
118
134
|
private reindexAttributes;
|
|
119
135
|
getRun(runId: string): Promise<WorkflowRun | null>;
|
|
120
136
|
deleteRun(runId: string): Promise<void>;
|
|
137
|
+
deleteRuns(runIds: string[]): Promise<void>;
|
|
138
|
+
pruneTerminalRuns(policy: RetentionPolicy, nowMs: number, limit: number): Promise<number>;
|
|
121
139
|
getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null>;
|
|
122
140
|
saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
|
|
123
141
|
transaction<T>(work: (tx: {
|
|
@@ -133,6 +151,7 @@ declare class PrismaStateStore implements StateStore {
|
|
|
133
151
|
/** The `where` every run query shares — {@link listRuns} pages it, {@link runFacets} groups it. */
|
|
134
152
|
private runWhere;
|
|
135
153
|
listRuns(query: RunQuery): Promise<WorkflowRun[]>;
|
|
154
|
+
countRuns(query: Omit<RunQuery, 'limit' | 'offset'>): Promise<number>;
|
|
136
155
|
/** `GROUP BY status, origin` over the same predicates {@link listRuns} pages — one aggregate, so a
|
|
137
156
|
* console can show whole-set counts next to a bounded page instead of downloading every run to
|
|
138
157
|
* count them in the browser. */
|
|
@@ -142,6 +161,13 @@ declare class PrismaStateStore implements StateStore {
|
|
|
142
161
|
* attribute axes live outside the row (json column / side table) and are counted from a bounded
|
|
143
162
|
* page of runs instead — see `RunValueFacetOptions.scan`. */
|
|
144
163
|
runValueFacets(axis: RunValueAxis, query: RunFacetQuery, opts?: RunValueFacetOptions): Promise<RunValueFacetRow[]>;
|
|
164
|
+
private schedules;
|
|
165
|
+
saveSchedule(record: ScheduleRecord): Promise<void>;
|
|
166
|
+
getSchedule(id: string): Promise<ScheduleRecord | null>;
|
|
167
|
+
/** One conditional `updateMany`: the compare-and-set on `next_fire_at` is a single statement. */
|
|
168
|
+
updateSchedule(id: string, patch: Partial<Omit<ScheduleRecord, 'id' | 'createdAt'>>, expectedNextFireAt?: number | null): Promise<boolean>;
|
|
169
|
+
deleteSchedule(id: string): Promise<boolean>;
|
|
170
|
+
listSchedules(query: ScheduleQuery): Promise<ScheduleRecord[]>;
|
|
145
171
|
listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
|
|
146
172
|
getLatestCheckpointByName(runId: string, name: string): Promise<StepCheckpoint | undefined>;
|
|
147
173
|
listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
|
|
4
4
|
// src/prisma-state-store.ts
|
|
5
|
-
import { RUN_VALUE_FACET_SCAN, attributePredicateOperands, axisIsRunColumn, mergeRunFacetRows, mergeRunValueFacetRows, normalizeAttributeRows, runValueFacetsFromRuns } from "@dudousxd/nestjs-durable-core";
|
|
5
|
+
import { RUN_VALUE_FACET_SCAN, SINGLETON_ACTIVE_STATUSES, TERMINAL_RUN_STATUSES, attributePredicateOperands, axisIsRunColumn, clearTerminalSingletonAdmission, mergeRunFacetRows, mergeRunValueFacetRows, normalizeAttributeRows, parseDuration, runValueFacetsFromRuns, singletonAdmissionDenialPatch, singletonAdmissionTags } from "@dudousxd/nestjs-durable-core";
|
|
6
6
|
function namespaceWhere(namespace) {
|
|
7
7
|
if (namespace === void 0) return {};
|
|
8
8
|
return {
|
|
@@ -18,13 +18,54 @@ var PrismaStateStore = class {
|
|
|
18
18
|
constructor(db) {
|
|
19
19
|
this.db = db;
|
|
20
20
|
}
|
|
21
|
+
async tryAdmitSingleton(runId, tag, workflow, limit, retryWakeAt) {
|
|
22
|
+
return this.db.$transaction(async (tx) => {
|
|
23
|
+
const where = {
|
|
24
|
+
workflow,
|
|
25
|
+
status: {
|
|
26
|
+
in: SINGLETON_ACTIVE_STATUSES
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
await tx.durableWorkflowRun.updateMany({
|
|
30
|
+
where,
|
|
31
|
+
data: {
|
|
32
|
+
workflow
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
const rows = await tx.durableWorkflowRun.findMany({
|
|
36
|
+
where
|
|
37
|
+
});
|
|
38
|
+
const runs = rows.map(fromRunRow);
|
|
39
|
+
const tags = singletonAdmissionTags(runs, runId, tag, workflow, limit);
|
|
40
|
+
if (!tags) {
|
|
41
|
+
const patch = singletonAdmissionDenialPatch(runs, runId, tag, workflow, retryWakeAt);
|
|
42
|
+
if (patch) await tx.durableWorkflowRun.update({
|
|
43
|
+
where: {
|
|
44
|
+
id: runId
|
|
45
|
+
},
|
|
46
|
+
data: toRunPatch(patch)
|
|
47
|
+
});
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
await tx.durableWorkflowRun.update({
|
|
51
|
+
where: {
|
|
52
|
+
id: runId
|
|
53
|
+
},
|
|
54
|
+
data: {
|
|
55
|
+
tags
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
return true;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
21
61
|
async createRun(run) {
|
|
22
62
|
await this.db.durableWorkflowRun.create({
|
|
23
63
|
data: toRunData(run)
|
|
24
64
|
});
|
|
25
65
|
await this.reindexAttributes(run.id, run.searchAttributes);
|
|
26
66
|
}
|
|
27
|
-
async updateRun(runId,
|
|
67
|
+
async updateRun(runId, inputPatch) {
|
|
68
|
+
const patch = inputPatch.status && TERMINAL_RUN_STATUSES.includes(inputPatch.status) ? await clearTerminalSingletonAdmission(runId, inputPatch, (id) => this.getRun(id)) : inputPatch;
|
|
28
69
|
await this.db.durableWorkflowRun.update({
|
|
29
70
|
where: {
|
|
30
71
|
id: runId
|
|
@@ -76,6 +117,99 @@ var PrismaStateStore = class {
|
|
|
76
117
|
}
|
|
77
118
|
});
|
|
78
119
|
}
|
|
120
|
+
async deleteRuns(runIds) {
|
|
121
|
+
if (runIds.length === 0) return;
|
|
122
|
+
await this.db.$transaction(async (tx) => {
|
|
123
|
+
await tx.durableStepCheckpoint.deleteMany({
|
|
124
|
+
where: {
|
|
125
|
+
runId: {
|
|
126
|
+
in: runIds
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
await tx.durableSignalWaiter.deleteMany({
|
|
131
|
+
where: {
|
|
132
|
+
runId: {
|
|
133
|
+
in: runIds
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
await tx.durableRunAttribute.deleteMany({
|
|
138
|
+
where: {
|
|
139
|
+
runId: {
|
|
140
|
+
in: runIds
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
await tx.durableWorkflowRun.deleteMany({
|
|
145
|
+
where: {
|
|
146
|
+
id: {
|
|
147
|
+
in: runIds
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
async pruneTerminalRuns(policy, nowMs, limit) {
|
|
154
|
+
const statuses = policy.statuses.filter((s) => TERMINAL_RUN_STATUSES.includes(s));
|
|
155
|
+
if (statuses.length === 0 || limit <= 0) return 0;
|
|
156
|
+
if (policy.maxAge == null && policy.maxCount == null) return 0;
|
|
157
|
+
const scoped = this.runWhere({
|
|
158
|
+
...policy.scope ?? {},
|
|
159
|
+
statuses
|
|
160
|
+
});
|
|
161
|
+
const ids = /* @__PURE__ */ new Set();
|
|
162
|
+
if (policy.maxAge != null) {
|
|
163
|
+
const cutoff = new Date(nowMs - parseDuration(policy.maxAge));
|
|
164
|
+
const rows = await this.db.durableWorkflowRun.findMany({
|
|
165
|
+
where: {
|
|
166
|
+
AND: [
|
|
167
|
+
scoped,
|
|
168
|
+
{
|
|
169
|
+
updatedAt: {
|
|
170
|
+
lt: cutoff
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
},
|
|
175
|
+
select: {
|
|
176
|
+
id: true
|
|
177
|
+
},
|
|
178
|
+
orderBy: {
|
|
179
|
+
updatedAt: "asc"
|
|
180
|
+
},
|
|
181
|
+
take: limit
|
|
182
|
+
});
|
|
183
|
+
for (const r of rows) ids.add(r.id);
|
|
184
|
+
}
|
|
185
|
+
if (policy.maxCount != null && ids.size < limit) {
|
|
186
|
+
const rows = await this.db.durableWorkflowRun.findMany({
|
|
187
|
+
where: scoped,
|
|
188
|
+
select: {
|
|
189
|
+
id: true
|
|
190
|
+
},
|
|
191
|
+
orderBy: [
|
|
192
|
+
{
|
|
193
|
+
updatedAt: "desc"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
id: "desc"
|
|
197
|
+
}
|
|
198
|
+
],
|
|
199
|
+
skip: policy.maxCount,
|
|
200
|
+
take: limit
|
|
201
|
+
});
|
|
202
|
+
for (const r of rows) {
|
|
203
|
+
if (ids.size >= limit) break;
|
|
204
|
+
ids.add(r.id);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const doomed = [
|
|
208
|
+
...ids
|
|
209
|
+
].slice(0, limit);
|
|
210
|
+
await this.deleteRuns(doomed);
|
|
211
|
+
return doomed.length;
|
|
212
|
+
}
|
|
79
213
|
async getCheckpoint(runId, seq) {
|
|
80
214
|
const row = await this.db.durableStepCheckpoint.findUnique({
|
|
81
215
|
where: {
|
|
@@ -259,6 +393,11 @@ var PrismaStateStore = class {
|
|
|
259
393
|
});
|
|
260
394
|
return rows.map(fromRunRow);
|
|
261
395
|
}
|
|
396
|
+
async countRuns(query) {
|
|
397
|
+
return this.db.durableWorkflowRun.count({
|
|
398
|
+
where: this.runWhere(query)
|
|
399
|
+
});
|
|
400
|
+
}
|
|
262
401
|
/** `GROUP BY status, origin` over the same predicates {@link listRuns} pages — one aggregate, so a
|
|
263
402
|
* console can show whole-set counts next to a bounded page instead of downloading every run to
|
|
264
403
|
* count them in the browser. */
|
|
@@ -305,6 +444,101 @@ var PrismaStateStore = class {
|
|
|
305
444
|
count: r._count._all
|
|
306
445
|
})), opts);
|
|
307
446
|
}
|
|
447
|
+
// ---- persisted schedules ----------------------------------------------------------------------
|
|
448
|
+
schedules() {
|
|
449
|
+
const delegate = this.db.durableSchedule;
|
|
450
|
+
if (!delegate) {
|
|
451
|
+
throw new Error("persisted schedules need the DurableSchedule model \u2014 copy it from prisma/schema.prisma into your schema and re-run `prisma generate`");
|
|
452
|
+
}
|
|
453
|
+
return delegate;
|
|
454
|
+
}
|
|
455
|
+
async saveSchedule(record) {
|
|
456
|
+
const data = toScheduleData(record);
|
|
457
|
+
await this.schedules().upsert({
|
|
458
|
+
where: {
|
|
459
|
+
id: record.id
|
|
460
|
+
},
|
|
461
|
+
create: data,
|
|
462
|
+
update: data
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
async getSchedule(id) {
|
|
466
|
+
const row = await this.schedules().findUnique({
|
|
467
|
+
where: {
|
|
468
|
+
id
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
return row ? fromScheduleRow(row) : null;
|
|
472
|
+
}
|
|
473
|
+
/** One conditional `updateMany`: the compare-and-set on `next_fire_at` is a single statement. */
|
|
474
|
+
async updateSchedule(id, patch, expectedNextFireAt) {
|
|
475
|
+
const data = {};
|
|
476
|
+
if (patch.namespace !== void 0) data.namespace = patch.namespace;
|
|
477
|
+
if (patch.workflow !== void 0) data.workflow = patch.workflow;
|
|
478
|
+
if (patch.paused !== void 0) data.paused = patch.paused;
|
|
479
|
+
if ("nextFireAt" in patch) data.nextFireAt = patch.nextFireAt == null ? null : BigInt(patch.nextFireAt);
|
|
480
|
+
if ("tags" in patch) data.tags = jsonOrNull(patch.tags);
|
|
481
|
+
if (patch.spec !== void 0) data.spec = patch.spec;
|
|
482
|
+
if (patch.state !== void 0) data.state = patch.state;
|
|
483
|
+
if (patch.updatedAt !== void 0) data.updatedAt = patch.updatedAt;
|
|
484
|
+
if (!Object.keys(data).length) return await this.getSchedule(id) !== null;
|
|
485
|
+
const where = {
|
|
486
|
+
id
|
|
487
|
+
};
|
|
488
|
+
if (expectedNextFireAt !== void 0) where.nextFireAt = expectedNextFireAt === null ? null : BigInt(expectedNextFireAt);
|
|
489
|
+
const { count } = await this.schedules().updateMany({
|
|
490
|
+
where,
|
|
491
|
+
data
|
|
492
|
+
});
|
|
493
|
+
return count > 0;
|
|
494
|
+
}
|
|
495
|
+
async deleteSchedule(id) {
|
|
496
|
+
const { count } = await this.schedules().deleteMany({
|
|
497
|
+
where: {
|
|
498
|
+
id
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
return count > 0;
|
|
502
|
+
}
|
|
503
|
+
async listSchedules(query) {
|
|
504
|
+
const where = {};
|
|
505
|
+
if (query.namespace !== void 0) where.namespace = query.namespace;
|
|
506
|
+
if (query.workflow !== void 0) where.workflow = query.workflow;
|
|
507
|
+
if (query.paused !== void 0) where.paused = query.paused;
|
|
508
|
+
if (query.tag !== void 0) where.tags = {
|
|
509
|
+
array_contains: query.tag
|
|
510
|
+
};
|
|
511
|
+
if (query.dueBy !== void 0) {
|
|
512
|
+
where.AND = [
|
|
513
|
+
{
|
|
514
|
+
paused: false
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
nextFireAt: {
|
|
518
|
+
lte: BigInt(query.dueBy)
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
];
|
|
522
|
+
}
|
|
523
|
+
const rows = await this.schedules().findMany({
|
|
524
|
+
where,
|
|
525
|
+
// Soonest-due first, never-firing (NULL) last, id as the stable tie-break.
|
|
526
|
+
orderBy: [
|
|
527
|
+
{
|
|
528
|
+
nextFireAt: {
|
|
529
|
+
sort: "asc",
|
|
530
|
+
nulls: "last"
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
id: "asc"
|
|
535
|
+
}
|
|
536
|
+
],
|
|
537
|
+
take: query.limit,
|
|
538
|
+
skip: query.offset
|
|
539
|
+
});
|
|
540
|
+
return rows.map(fromScheduleRow);
|
|
541
|
+
}
|
|
308
542
|
async listCheckpoints(runId) {
|
|
309
543
|
const rows = await this.db.durableStepCheckpoint.findMany({
|
|
310
544
|
where: {
|
|
@@ -626,6 +860,39 @@ function fromCheckpointRow(row) {
|
|
|
626
860
|
};
|
|
627
861
|
}
|
|
628
862
|
__name(fromCheckpointRow, "fromCheckpointRow");
|
|
863
|
+
function toScheduleData(r) {
|
|
864
|
+
return {
|
|
865
|
+
id: r.id,
|
|
866
|
+
namespace: r.namespace,
|
|
867
|
+
workflow: r.workflow,
|
|
868
|
+
paused: r.paused,
|
|
869
|
+
nextFireAt: r.nextFireAt == null ? null : BigInt(r.nextFireAt),
|
|
870
|
+
tags: jsonOrNull(r.tags),
|
|
871
|
+
spec: r.spec,
|
|
872
|
+
state: r.state,
|
|
873
|
+
createdAt: r.createdAt,
|
|
874
|
+
updatedAt: r.updatedAt
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
__name(toScheduleData, "toScheduleData");
|
|
878
|
+
function fromScheduleRow(row) {
|
|
879
|
+
const tags = Array.isArray(row.tags) ? row.tags : void 0;
|
|
880
|
+
return {
|
|
881
|
+
id: row.id,
|
|
882
|
+
namespace: row.namespace,
|
|
883
|
+
workflow: row.workflow,
|
|
884
|
+
paused: row.paused,
|
|
885
|
+
nextFireAt: row.nextFireAt == null ? null : Number(row.nextFireAt),
|
|
886
|
+
...tags?.length ? {
|
|
887
|
+
tags
|
|
888
|
+
} : {},
|
|
889
|
+
spec: row.spec ?? {},
|
|
890
|
+
state: row.state ?? {},
|
|
891
|
+
createdAt: row.createdAt,
|
|
892
|
+
updatedAt: row.updatedAt
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
__name(fromScheduleRow, "fromScheduleRow");
|
|
629
896
|
|
|
630
897
|
// src/schema.ts
|
|
631
898
|
function durableManagedTables() {
|
|
@@ -635,7 +902,8 @@ function durableManagedTables() {
|
|
|
635
902
|
"durable_run_attributes",
|
|
636
903
|
"durable_signal_waiters",
|
|
637
904
|
"durable_buffered_signals",
|
|
638
|
-
"durable_buffered_events"
|
|
905
|
+
"durable_buffered_events",
|
|
906
|
+
"durable_schedules"
|
|
639
907
|
];
|
|
640
908
|
}
|
|
641
909
|
__name(durableManagedTables, "durableManagedTables");
|