@dudousxd/nestjs-durable-store-prisma 0.7.0 → 0.8.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Davide Carvalho
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.cjs ADDED
@@ -0,0 +1,468 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ PrismaStateStore: () => PrismaStateStore
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/prisma-state-store.ts
29
+ var import_nestjs_durable_core = require("@dudousxd/nestjs-durable-core");
30
+ var PrismaStateStore = class {
31
+ static {
32
+ __name(this, "PrismaStateStore");
33
+ }
34
+ db;
35
+ constructor(db) {
36
+ this.db = db;
37
+ }
38
+ async createRun(run) {
39
+ await this.db.durableWorkflowRun.create({
40
+ data: toRunData(run)
41
+ });
42
+ await this.reindexAttributes(run.id, run.searchAttributes);
43
+ }
44
+ async updateRun(runId, patch) {
45
+ await this.db.durableWorkflowRun.update({
46
+ where: {
47
+ id: runId
48
+ },
49
+ data: toRunPatch(patch)
50
+ });
51
+ if ("searchAttributes" in patch) await this.reindexAttributes(runId, patch.searchAttributes);
52
+ }
53
+ /** Rewrite a run's normalized attribute rows: delete the old set, insert the current one. Mirrors
54
+ * the in-memory store's reindex so the side-table always reflects the run's live searchAttributes. */
55
+ async reindexAttributes(runId, attributes) {
56
+ await this.db.durableRunAttribute.deleteMany({
57
+ where: {
58
+ runId
59
+ }
60
+ });
61
+ const rows = (0, import_nestjs_durable_core.normalizeAttributeRows)(runId, attributes);
62
+ if (rows.length) await this.db.durableRunAttribute.createMany({
63
+ data: rows
64
+ });
65
+ }
66
+ async getRun(runId) {
67
+ const row = await this.db.durableWorkflowRun.findUnique({
68
+ where: {
69
+ id: runId
70
+ }
71
+ });
72
+ return row ? fromRunRow(row) : null;
73
+ }
74
+ async getCheckpoint(runId, seq) {
75
+ const row = await this.db.durableStepCheckpoint.findUnique({
76
+ where: {
77
+ runId_seq: {
78
+ runId,
79
+ seq
80
+ }
81
+ }
82
+ });
83
+ return row ? fromCheckpointRow(row) : null;
84
+ }
85
+ async saveCheckpoint(checkpoint) {
86
+ const data = toCheckpointData(checkpoint);
87
+ await this.db.durableStepCheckpoint.upsert({
88
+ where: {
89
+ runId_seq: {
90
+ runId: checkpoint.runId,
91
+ seq: checkpoint.seq
92
+ }
93
+ },
94
+ create: data,
95
+ update: data
96
+ });
97
+ }
98
+ async transaction(work) {
99
+ return this.db.$transaction(async (tx) => work({
100
+ raw: tx,
101
+ saveCheckpoint: /* @__PURE__ */ __name(async (cp) => {
102
+ const data = toCheckpointData(cp);
103
+ await tx.durableStepCheckpoint.upsert({
104
+ where: {
105
+ runId_seq: {
106
+ runId: cp.runId,
107
+ seq: cp.seq
108
+ }
109
+ },
110
+ create: data,
111
+ update: data
112
+ });
113
+ }, "saveCheckpoint")
114
+ }));
115
+ }
116
+ async listIncompleteRuns() {
117
+ const rows = await this.db.durableWorkflowRun.findMany({
118
+ where: {
119
+ status: "running"
120
+ }
121
+ });
122
+ return rows.map(fromRunRow);
123
+ }
124
+ async listPendingRuns(limit) {
125
+ const rows = await this.db.durableWorkflowRun.findMany({
126
+ where: {
127
+ status: "pending"
128
+ },
129
+ orderBy: {
130
+ createdAt: "asc"
131
+ },
132
+ take: limit
133
+ });
134
+ return rows.map(fromRunRow);
135
+ }
136
+ async listDueTimers(nowMs) {
137
+ const rows = await this.db.durableWorkflowRun.findMany({
138
+ where: {
139
+ status: "suspended",
140
+ wakeAt: {
141
+ not: null,
142
+ lte: BigInt(nowMs)
143
+ }
144
+ }
145
+ });
146
+ return rows.map(fromRunRow);
147
+ }
148
+ async tryLockRun(runId, owner, leaseUntilMs, nowMs) {
149
+ const result = await this.db.durableWorkflowRun.updateMany({
150
+ where: {
151
+ id: runId,
152
+ OR: [
153
+ {
154
+ lockedUntil: null
155
+ },
156
+ {
157
+ lockedUntil: {
158
+ lte: new Date(nowMs)
159
+ }
160
+ }
161
+ ]
162
+ },
163
+ data: {
164
+ lockedBy: owner,
165
+ lockedUntil: new Date(leaseUntilMs)
166
+ }
167
+ });
168
+ return result.count === 1;
169
+ }
170
+ async releaseRunLock(runId) {
171
+ await this.db.durableWorkflowRun.update({
172
+ where: {
173
+ id: runId
174
+ },
175
+ data: {
176
+ lockedBy: null,
177
+ lockedUntil: null
178
+ }
179
+ });
180
+ }
181
+ async renewRunLock(runId, owner, leaseUntilMs) {
182
+ const result = await this.db.durableWorkflowRun.updateMany({
183
+ where: {
184
+ id: runId,
185
+ lockedBy: owner
186
+ },
187
+ data: {
188
+ lockedUntil: new Date(leaseUntilMs)
189
+ }
190
+ });
191
+ return result.count === 1;
192
+ }
193
+ async listRuns(query) {
194
+ const where = {};
195
+ if (query.workflow) where.workflow = query.workflow;
196
+ if (query.status && query.statuses) {
197
+ where.AND = [
198
+ {
199
+ status: query.status
200
+ },
201
+ {
202
+ status: {
203
+ in: query.statuses
204
+ }
205
+ }
206
+ ];
207
+ } else if (query.status) {
208
+ where.status = query.status;
209
+ } else if (query.statuses) {
210
+ where.status = {
211
+ in: query.statuses
212
+ };
213
+ }
214
+ if (query.tag) where.tags = {
215
+ array_contains: query.tag
216
+ };
217
+ if (query.attributes?.length) {
218
+ const existing = where.AND ?? [];
219
+ where.AND = [
220
+ ...existing,
221
+ ...query.attributes.map((f) => attributeSome(f))
222
+ ];
223
+ }
224
+ const orderBy = {
225
+ createdAt: "desc"
226
+ };
227
+ const rows = await this.db.durableWorkflowRun.findMany({
228
+ where,
229
+ take: query.limit,
230
+ skip: query.offset,
231
+ orderBy
232
+ });
233
+ return rows.map(fromRunRow);
234
+ }
235
+ async listCheckpoints(runId) {
236
+ const rows = await this.db.durableStepCheckpoint.findMany({
237
+ where: {
238
+ runId
239
+ },
240
+ orderBy: {
241
+ seq: "asc"
242
+ }
243
+ });
244
+ return rows.map(fromCheckpointRow);
245
+ }
246
+ async getLatestCheckpointByName(runId, name) {
247
+ const row = await this.db.durableStepCheckpoint.findFirst({
248
+ where: {
249
+ runId,
250
+ name
251
+ },
252
+ orderBy: {
253
+ seq: "desc"
254
+ }
255
+ });
256
+ return row ? fromCheckpointRow(row) : void 0;
257
+ }
258
+ async listCheckpointsByNamePrefix(runId, prefixes) {
259
+ if (prefixes.length === 0) return [];
260
+ const rows = await this.db.durableStepCheckpoint.findMany({
261
+ where: {
262
+ runId,
263
+ OR: prefixes.map((p) => ({
264
+ name: {
265
+ startsWith: p
266
+ }
267
+ }))
268
+ },
269
+ orderBy: {
270
+ seq: "asc"
271
+ }
272
+ });
273
+ return rows.map(fromCheckpointRow);
274
+ }
275
+ async putSignalWaiter(waiter) {
276
+ await this.db.durableSignalWaiter.upsert({
277
+ where: {
278
+ token: waiter.token
279
+ },
280
+ create: {
281
+ ...waiter
282
+ },
283
+ update: {
284
+ runId: waiter.runId,
285
+ seq: waiter.seq
286
+ }
287
+ });
288
+ }
289
+ async takeSignalWaiter(token) {
290
+ const row = await this.db.durableSignalWaiter.findUnique({
291
+ where: {
292
+ token
293
+ }
294
+ });
295
+ if (!row) return null;
296
+ await this.db.durableSignalWaiter.delete({
297
+ where: {
298
+ token
299
+ }
300
+ });
301
+ return {
302
+ token: row.token,
303
+ runId: row.runId,
304
+ seq: row.seq
305
+ };
306
+ }
307
+ async listSignalWaiters(prefix) {
308
+ const rows = await this.db.durableSignalWaiter.findMany({
309
+ where: {
310
+ token: {
311
+ startsWith: prefix
312
+ }
313
+ }
314
+ });
315
+ return rows.map((r) => ({
316
+ token: r.token,
317
+ runId: r.runId,
318
+ seq: r.seq
319
+ }));
320
+ }
321
+ async bufferSignal(token, payload) {
322
+ await this.db.durableBufferedSignal.create({
323
+ data: {
324
+ token,
325
+ payload: jsonOrNull(payload)
326
+ }
327
+ });
328
+ }
329
+ async takeBufferedSignal(token) {
330
+ const row = await this.db.durableBufferedSignal.findFirst({
331
+ where: {
332
+ token
333
+ },
334
+ orderBy: {
335
+ id: "asc"
336
+ }
337
+ });
338
+ if (!row) return null;
339
+ await this.db.durableBufferedSignal.delete({
340
+ where: {
341
+ id: row.id
342
+ }
343
+ });
344
+ return {
345
+ payload: row.payload ?? void 0
346
+ };
347
+ }
348
+ };
349
+ function attributeSome(f) {
350
+ const col = (0, import_nestjs_durable_core.attributeColumnFor)(f);
351
+ const operand = (0, import_nestjs_durable_core.attributeOperand)(f);
352
+ const condition = f.op === "eq" ? operand : f.op === "ne" ? {
353
+ not: operand
354
+ } : {
355
+ [f.op]: operand
356
+ };
357
+ return {
358
+ attributes: {
359
+ some: {
360
+ key: f.key,
361
+ [col]: condition
362
+ }
363
+ }
364
+ };
365
+ }
366
+ __name(attributeSome, "attributeSome");
367
+ var bigOrNull = /* @__PURE__ */ __name((n) => n == null ? null : BigInt(n), "bigOrNull");
368
+ var numOrUndef = /* @__PURE__ */ __name((n) => n == null ? void 0 : Number(n), "numOrUndef");
369
+ var jsonOrNull = /* @__PURE__ */ __name((v) => v ?? null, "jsonOrNull");
370
+ function toRunData(run) {
371
+ return {
372
+ id: run.id,
373
+ workflow: run.workflow,
374
+ workflowVersion: run.workflowVersion,
375
+ status: run.status,
376
+ input: jsonOrNull(run.input),
377
+ output: jsonOrNull(run.output),
378
+ error: jsonOrNull(run.error),
379
+ wakeAt: bigOrNull(run.wakeAt),
380
+ lockedBy: run.lockedBy ?? null,
381
+ lockedUntil: run.lockedUntil == null ? null : new Date(run.lockedUntil),
382
+ recoveryAttempts: run.recoveryAttempts ?? null,
383
+ tags: jsonOrNull(run.tags),
384
+ searchAttributes: jsonOrNull(run.searchAttributes),
385
+ createdAt: run.createdAt,
386
+ updatedAt: run.updatedAt
387
+ };
388
+ }
389
+ __name(toRunData, "toRunData");
390
+ function toRunPatch(patch) {
391
+ const data = {};
392
+ if (patch.status !== void 0) data.status = patch.status;
393
+ if (patch.output !== void 0) data.output = jsonOrNull(patch.output);
394
+ if (patch.error !== void 0) data.error = jsonOrNull(patch.error);
395
+ if (patch.wakeAt !== void 0) data.wakeAt = bigOrNull(patch.wakeAt);
396
+ if (patch.recoveryAttempts !== void 0) data.recoveryAttempts = patch.recoveryAttempts ?? null;
397
+ if ("searchAttributes" in patch) data.searchAttributes = jsonOrNull(patch.searchAttributes);
398
+ if (patch.updatedAt !== void 0) data.updatedAt = patch.updatedAt;
399
+ return data;
400
+ }
401
+ __name(toRunPatch, "toRunPatch");
402
+ function fromRunRow(row) {
403
+ return {
404
+ id: row.id,
405
+ workflow: row.workflow,
406
+ workflowVersion: row.workflowVersion,
407
+ status: row.status,
408
+ input: row.input ?? void 0,
409
+ output: row.output ?? void 0,
410
+ error: row.error ?? void 0,
411
+ wakeAt: numOrUndef(row.wakeAt),
412
+ lockedBy: row.lockedBy ?? void 0,
413
+ lockedUntil: row.lockedUntil == null ? void 0 : row.lockedUntil.getTime(),
414
+ recoveryAttempts: row.recoveryAttempts ?? void 0,
415
+ tags: row.tags ?? void 0,
416
+ searchAttributes: row.searchAttributes ?? void 0,
417
+ createdAt: row.createdAt,
418
+ updatedAt: row.updatedAt
419
+ };
420
+ }
421
+ __name(fromRunRow, "fromRunRow");
422
+ function toCheckpointData(cp) {
423
+ return {
424
+ runId: cp.runId,
425
+ seq: cp.seq,
426
+ name: cp.name,
427
+ kind: cp.kind,
428
+ stepId: cp.stepId,
429
+ status: cp.status,
430
+ input: jsonOrNull(cp.input),
431
+ output: jsonOrNull(cp.output),
432
+ error: jsonOrNull(cp.error),
433
+ events: jsonOrNull(cp.events),
434
+ attempts: cp.attempts,
435
+ workerGroup: cp.workerGroup ?? null,
436
+ wakeAt: bigOrNull(cp.wakeAt),
437
+ enqueuedAt: cp.enqueuedAt,
438
+ startedAt: cp.startedAt,
439
+ finishedAt: cp.finishedAt
440
+ };
441
+ }
442
+ __name(toCheckpointData, "toCheckpointData");
443
+ function fromCheckpointRow(row) {
444
+ return {
445
+ runId: row.runId,
446
+ seq: row.seq,
447
+ name: row.name,
448
+ kind: row.kind,
449
+ stepId: row.stepId,
450
+ status: row.status,
451
+ input: row.input ?? void 0,
452
+ output: row.output ?? void 0,
453
+ error: row.error ?? void 0,
454
+ events: row.events ?? void 0,
455
+ attempts: row.attempts,
456
+ workerGroup: row.workerGroup ?? void 0,
457
+ wakeAt: numOrUndef(row.wakeAt),
458
+ enqueuedAt: row.enqueuedAt ?? row.startedAt,
459
+ startedAt: row.startedAt,
460
+ finishedAt: row.finishedAt
461
+ };
462
+ }
463
+ __name(fromCheckpointRow, "fromCheckpointRow");
464
+ // Annotate the CommonJS export names for ESM import in node:
465
+ 0 && (module.exports = {
466
+ PrismaStateStore
467
+ });
468
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/prisma-state-store.ts"],"sourcesContent":["export * from './prisma-state-store';\n","import {\n type AttributeFilter,\n type RunQuery,\n type SignalWaiter,\n type StateStore,\n type StepCheckpoint,\n type StepError,\n type StepEvent,\n type WorkflowRun,\n attributeColumnFor,\n attributeOperand,\n normalizeAttributeRows,\n} from '@dudousxd/nestjs-durable-core';\n\n/* The Prisma client is generated per-schema, so the adapter can't import a concrete one. Instead\n it depends on this structural surface — the three model delegates and the methods it uses. A\n real `PrismaClient` (with the models from prisma/schema.prisma added) satisfies it. Query-arg\n shapes are Prisma's own generics, so they're left as `any` at this single boundary; the row\n return types are precise, which is what the mapping code below relies on. */\n\ninterface RunRow {\n id: string;\n workflow: string;\n workflowVersion: string;\n status: string;\n input: unknown;\n output: unknown;\n error: unknown;\n wakeAt: bigint | null;\n lockedBy: string | null;\n lockedUntil: Date | null;\n recoveryAttempts: number | null;\n tags: unknown;\n searchAttributes: unknown;\n createdAt: Date;\n updatedAt: Date;\n}\n\ninterface CheckpointRow {\n runId: string;\n seq: number;\n name: string;\n kind: string;\n stepId: string;\n status: string;\n input: unknown;\n output: unknown;\n error: unknown;\n events: unknown;\n attempts: number;\n workerGroup: string | null;\n wakeAt: bigint | null;\n enqueuedAt: Date | null;\n startedAt: Date;\n finishedAt: Date;\n}\n\ninterface WaiterRow {\n token: string;\n runId: string;\n seq: number;\n}\n\ninterface RunAttributeRow {\n runId: string;\n key: string;\n strValue: string | null;\n numValue: number | null;\n}\n\ninterface BufferedSignalRow {\n id: bigint;\n token: string;\n payload: unknown;\n}\n\n// Prisma's per-model query args are generated generics; left as `any` at this single boundary.\ntype Args = any;\n\ninterface Delegate<Row> {\n create(args: Args): Promise<Row>;\n createMany(args: Args): Promise<{ count: number }>;\n findUnique(args: Args): Promise<Row | null>;\n findFirst(args?: Args): Promise<Row | null>;\n findMany(args?: Args): Promise<Row[]>;\n update(args: Args): Promise<Row>;\n updateMany(args: Args): Promise<{ count: number }>;\n upsert(args: Args): Promise<Row>;\n delete(args: Args): Promise<Row>;\n deleteMany(args?: Args): Promise<{ count: number }>;\n}\n\nexport interface DurablePrismaTx {\n durableWorkflowRun: Delegate<RunRow>;\n durableStepCheckpoint: Delegate<CheckpointRow>;\n durableRunAttribute: Delegate<RunAttributeRow>;\n durableSignalWaiter: Delegate<WaiterRow>;\n durableBufferedSignal: Delegate<BufferedSignalRow>;\n}\n\nexport interface DurablePrismaClient extends DurablePrismaTx {\n /** Prisma's interactive-transaction form: runs `fn` with a tx-scoped client and commits on resolve. */\n $transaction<T>(fn: (tx: DurablePrismaTx) => Promise<T>): Promise<T>;\n}\n\n/**\n * Prisma-backed `StateStore`. Pass your `PrismaClient` after adding the models from\n * `prisma/schema.prisma` to your schema. JSON columns carry the run/step payloads directly;\n * `wakeAt` is a `BigInt` (epoch ms).\n */\nexport class PrismaStateStore implements StateStore {\n constructor(private readonly db: DurablePrismaClient) {}\n\n async createRun(run: WorkflowRun): Promise<void> {\n await this.db.durableWorkflowRun.create({ data: toRunData(run) });\n await this.reindexAttributes(run.id, run.searchAttributes);\n }\n\n async updateRun(runId: string, patch: Partial<WorkflowRun>): Promise<void> {\n await this.db.durableWorkflowRun.update({ where: { id: runId }, data: toRunPatch(patch) });\n // Keep the side-table in step with the run's attributes whenever they're patched.\n if ('searchAttributes' in patch) await this.reindexAttributes(runId, patch.searchAttributes);\n }\n\n /** Rewrite a run's normalized attribute rows: delete the old set, insert the current one. Mirrors\n * the in-memory store's reindex so the side-table always reflects the run's live searchAttributes. */\n private async reindexAttributes(\n runId: string,\n attributes: WorkflowRun['searchAttributes'],\n ): Promise<void> {\n await this.db.durableRunAttribute.deleteMany({ where: { runId } });\n const rows = normalizeAttributeRows(runId, attributes);\n if (rows.length) await this.db.durableRunAttribute.createMany({ data: rows });\n }\n\n async getRun(runId: string): Promise<WorkflowRun | null> {\n const row = await this.db.durableWorkflowRun.findUnique({ where: { id: runId } });\n return row ? fromRunRow(row) : null;\n }\n\n async getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null> {\n const row = await this.db.durableStepCheckpoint.findUnique({\n where: { runId_seq: { runId, seq } },\n });\n return row ? fromCheckpointRow(row) : null;\n }\n\n async saveCheckpoint(checkpoint: StepCheckpoint): Promise<void> {\n const data = toCheckpointData(checkpoint);\n await this.db.durableStepCheckpoint.upsert({\n where: { runId_seq: { runId: checkpoint.runId, seq: checkpoint.seq } },\n create: data,\n update: data,\n });\n }\n\n async transaction<T>(\n work: (tx: {\n raw: unknown;\n saveCheckpoint: (cp: StepCheckpoint) => Promise<void>;\n }) => Promise<T>,\n ): Promise<T> {\n return this.db.$transaction(async (tx) =>\n work({\n raw: tx,\n saveCheckpoint: async (cp) => {\n const data = toCheckpointData(cp);\n await tx.durableStepCheckpoint.upsert({\n where: { runId_seq: { runId: cp.runId, seq: cp.seq } },\n create: data,\n update: data,\n });\n },\n }),\n );\n }\n\n async listIncompleteRuns(): Promise<WorkflowRun[]> {\n const rows = await this.db.durableWorkflowRun.findMany({ where: { status: 'running' } });\n return rows.map(fromRunRow);\n }\n\n async listPendingRuns(limit: number): Promise<WorkflowRun[]> {\n const rows = await this.db.durableWorkflowRun.findMany({\n where: { status: 'pending' },\n orderBy: { createdAt: 'asc' }, // FIFO dispatch\n take: limit,\n });\n return rows.map(fromRunRow);\n }\n\n async listDueTimers(nowMs: number): Promise<WorkflowRun[]> {\n const rows = await this.db.durableWorkflowRun.findMany({\n where: { status: 'suspended', wakeAt: { not: null, lte: BigInt(nowMs) } },\n });\n return rows.map(fromRunRow);\n }\n\n async tryLockRun(\n runId: string,\n owner: string,\n leaseUntilMs: number,\n nowMs: number,\n ): Promise<boolean> {\n const result = await this.db.durableWorkflowRun.updateMany({\n where: {\n id: runId,\n OR: [{ lockedUntil: null }, { lockedUntil: { lte: new Date(nowMs) } }],\n },\n data: { lockedBy: owner, lockedUntil: new Date(leaseUntilMs) },\n });\n return result.count === 1;\n }\n\n async releaseRunLock(runId: string): Promise<void> {\n await this.db.durableWorkflowRun.update({\n where: { id: runId },\n data: { lockedBy: null, lockedUntil: null },\n });\n }\n\n async renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean> {\n const result = await this.db.durableWorkflowRun.updateMany({\n where: { id: runId, lockedBy: owner },\n data: { lockedUntil: new Date(leaseUntilMs) },\n });\n return result.count === 1;\n }\n\n async listRuns(query: RunQuery): Promise<WorkflowRun[]> {\n const where: Record<string, unknown> = {};\n if (query.workflow) where.workflow = query.workflow;\n // `status IN (...)`; an empty set matches nothing (mirrors the in-memory store). Combined with the\n // single-value `status` via AND when both are present, so the narrower set wins.\n if (query.status && query.statuses) {\n where.AND = [{ status: query.status }, { status: { in: query.statuses } }];\n } else if (query.status) {\n where.status = query.status;\n } else if (query.statuses) {\n where.status = { in: query.statuses };\n }\n if (query.tag) where.tags = { array_contains: query.tag };\n // Typed/range attribute predicates push DOWN into SQL: each filter becomes a relation `some`\n // (EXISTS) on the normalized `durable_run_attributes` side-table, so the DB filters AND paginates\n // — no full scan + in-process filter. ANDed: a run must match every filter, so one `some` each.\n if (query.attributes?.length) {\n const existing = (where.AND as unknown[] | undefined) ?? [];\n where.AND = [...existing, ...query.attributes.map((f) => attributeSome(f))];\n }\n const orderBy = { createdAt: 'desc' as const }; // newest first — recent runs on top in the dashboard\n const rows = await this.db.durableWorkflowRun.findMany({\n where,\n take: query.limit,\n skip: query.offset,\n orderBy,\n });\n return rows.map(fromRunRow);\n }\n\n async listCheckpoints(runId: string): Promise<StepCheckpoint[]> {\n const rows = await this.db.durableStepCheckpoint.findMany({\n where: { runId },\n orderBy: { seq: 'asc' },\n });\n return rows.map(fromCheckpointRow);\n }\n\n async getLatestCheckpointByName(\n runId: string,\n name: string,\n ): Promise<StepCheckpoint | undefined> {\n const row = await this.db.durableStepCheckpoint.findFirst({\n where: { runId, name },\n orderBy: { seq: 'desc' },\n });\n return row ? fromCheckpointRow(row) : undefined;\n }\n\n async listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]> {\n if (prefixes.length === 0) return [];\n const rows = await this.db.durableStepCheckpoint.findMany({\n where: { runId, OR: prefixes.map((p) => ({ name: { startsWith: p } })) },\n orderBy: { seq: 'asc' },\n });\n return rows.map(fromCheckpointRow);\n }\n\n async putSignalWaiter(waiter: SignalWaiter): Promise<void> {\n await this.db.durableSignalWaiter.upsert({\n where: { token: waiter.token },\n create: { ...waiter },\n update: { runId: waiter.runId, seq: waiter.seq },\n });\n }\n\n async takeSignalWaiter(token: string): Promise<SignalWaiter | null> {\n const row = await this.db.durableSignalWaiter.findUnique({ where: { token } });\n if (!row) return null;\n await this.db.durableSignalWaiter.delete({ where: { token } });\n return { token: row.token, runId: row.runId, seq: row.seq };\n }\n\n async listSignalWaiters(prefix: string): Promise<SignalWaiter[]> {\n const rows = await this.db.durableSignalWaiter.findMany({\n where: { token: { startsWith: prefix } },\n });\n return rows.map((r) => ({ token: r.token, runId: r.runId, seq: r.seq }));\n }\n\n async bufferSignal(token: string, payload: unknown): Promise<void> {\n await this.db.durableBufferedSignal.create({ data: { token, payload: jsonOrNull(payload) } });\n }\n\n async takeBufferedSignal(token: string): Promise<{ payload: unknown } | null> {\n const row = await this.db.durableBufferedSignal.findFirst({\n where: { token },\n orderBy: { id: 'asc' },\n });\n if (!row) return null;\n await this.db.durableBufferedSignal.delete({ where: { id: row.id } });\n return { payload: row.payload ?? undefined };\n }\n}\n\n/**\n * One attribute predicate as a Prisma relation `some` filter on the side-table — compiles to an\n * EXISTS, so the predicate runs in SQL. `<>` (ne) maps to `{ not }`, which under `some` also excludes\n * runs where the attribute is absent (the missing-key-never-matches contract): `some` already\n * requires a matching row to exist. Numeric operands compare `numValue`, everything else `strValue`.\n */\nfunction attributeSome(f: AttributeFilter): { attributes: { some: Record<string, unknown> } } {\n const col = attributeColumnFor(f); // 'numValue' | 'strValue'\n const operand = attributeOperand(f);\n const condition =\n f.op === 'eq' ? operand : f.op === 'ne' ? { not: operand } : { [f.op]: operand }; // gt/gte/lt/lte map 1:1 to Prisma operators\n return { attributes: { some: { key: f.key, [col]: condition } } };\n}\n\nconst bigOrNull = (n: number | undefined) => (n == null ? null : BigInt(n));\nconst numOrUndef = (n: bigint | null) => (n == null ? undefined : Number(n));\nconst jsonOrNull = (v: unknown) => v ?? null;\n\nfunction toRunData(run: WorkflowRun) {\n return {\n id: run.id,\n workflow: run.workflow,\n workflowVersion: run.workflowVersion,\n status: run.status,\n input: jsonOrNull(run.input),\n output: jsonOrNull(run.output),\n error: jsonOrNull(run.error),\n wakeAt: bigOrNull(run.wakeAt),\n lockedBy: run.lockedBy ?? null,\n lockedUntil: run.lockedUntil == null ? null : new Date(run.lockedUntil),\n recoveryAttempts: run.recoveryAttempts ?? null,\n tags: jsonOrNull(run.tags),\n searchAttributes: jsonOrNull(run.searchAttributes),\n createdAt: run.createdAt,\n updatedAt: run.updatedAt,\n };\n}\n\nfunction toRunPatch(patch: Partial<WorkflowRun>) {\n const data: Record<string, unknown> = {};\n if (patch.status !== undefined) data.status = patch.status;\n if (patch.output !== undefined) data.output = jsonOrNull(patch.output);\n if (patch.error !== undefined) data.error = jsonOrNull(patch.error);\n if (patch.wakeAt !== undefined) data.wakeAt = bigOrNull(patch.wakeAt);\n if (patch.recoveryAttempts !== undefined) data.recoveryAttempts = patch.recoveryAttempts ?? null;\n if ('searchAttributes' in patch) data.searchAttributes = jsonOrNull(patch.searchAttributes);\n if (patch.updatedAt !== undefined) data.updatedAt = patch.updatedAt;\n return data;\n}\n\nfunction fromRunRow(row: RunRow): WorkflowRun {\n return {\n id: row.id,\n workflow: row.workflow,\n workflowVersion: row.workflowVersion,\n status: row.status as WorkflowRun['status'],\n input: row.input ?? undefined,\n output: row.output ?? undefined,\n error: (row.error ?? undefined) as StepError | undefined,\n wakeAt: numOrUndef(row.wakeAt),\n lockedBy: row.lockedBy ?? undefined,\n lockedUntil: row.lockedUntil == null ? undefined : row.lockedUntil.getTime(),\n recoveryAttempts: row.recoveryAttempts ?? undefined,\n tags: (row.tags as string[] | null) ?? undefined,\n searchAttributes:\n (row.searchAttributes as Record<string, string | number | boolean> | null) ?? undefined,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n\nfunction toCheckpointData(cp: StepCheckpoint) {\n return {\n runId: cp.runId,\n seq: cp.seq,\n name: cp.name,\n kind: cp.kind,\n stepId: cp.stepId,\n status: cp.status,\n input: jsonOrNull(cp.input),\n output: jsonOrNull(cp.output),\n error: jsonOrNull(cp.error),\n events: jsonOrNull(cp.events),\n attempts: cp.attempts,\n workerGroup: cp.workerGroup ?? null,\n wakeAt: bigOrNull(cp.wakeAt),\n enqueuedAt: cp.enqueuedAt,\n startedAt: cp.startedAt,\n finishedAt: cp.finishedAt,\n };\n}\n\nfunction fromCheckpointRow(row: CheckpointRow): StepCheckpoint {\n return {\n runId: row.runId,\n seq: row.seq,\n name: row.name,\n kind: row.kind as StepCheckpoint['kind'],\n stepId: row.stepId,\n status: row.status as StepCheckpoint['status'],\n input: row.input ?? undefined,\n output: row.output ?? undefined,\n error: (row.error ?? undefined) as StepError | undefined,\n events: (row.events ?? undefined) as StepEvent[] | undefined,\n attempts: row.attempts,\n workerGroup: row.workerGroup ?? undefined,\n wakeAt: numOrUndef(row.wakeAt),\n enqueuedAt: row.enqueuedAt ?? row.startedAt,\n startedAt: row.startedAt,\n finishedAt: row.finishedAt,\n };\n}\n"],"mappings":"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","names":["PrismaStateStore","db","createRun","run","durableWorkflowRun","create","data","toRunData","reindexAttributes","id","searchAttributes","updateRun","runId","patch","update","where","toRunPatch","attributes","durableRunAttribute","deleteMany","rows","normalizeAttributeRows","length","createMany","getRun","row","findUnique","fromRunRow","getCheckpoint","seq","durableStepCheckpoint","runId_seq","fromCheckpointRow","saveCheckpoint","checkpoint","toCheckpointData","upsert","transaction","work","$transaction","tx","raw","cp","listIncompleteRuns","findMany","status","map","listPendingRuns","limit","orderBy","createdAt","take","listDueTimers","nowMs","wakeAt","not","lte","BigInt","tryLockRun","owner","leaseUntilMs","result","updateMany","OR","lockedUntil","Date","lockedBy","count","releaseRunLock","renewRunLock","listRuns","query","workflow","statuses","AND","in","tag","tags","array_contains","existing","f","attributeSome","skip","offset","listCheckpoints","getLatestCheckpointByName","name","findFirst","undefined","listCheckpointsByNamePrefix","prefixes","p","startsWith","putSignalWaiter","waiter","durableSignalWaiter","token","takeSignalWaiter","delete","listSignalWaiters","prefix","r","bufferSignal","payload","durableBufferedSignal","jsonOrNull","takeBufferedSignal","col","attributeColumnFor","operand","attributeOperand","condition","op","some","key","bigOrNull","n","numOrUndef","Number","v","workflowVersion","input","output","error","recoveryAttempts","updatedAt","getTime","kind","stepId","events","attempts","workerGroup","enqueuedAt","startedAt","finishedAt"]}
@@ -1,4 +1,5 @@
1
- import { type RunQuery, type SignalWaiter, type StateStore, type StepCheckpoint, type WorkflowRun } from '@dudousxd/nestjs-durable-core';
1
+ import { StateStore, WorkflowRun, StepCheckpoint, RunQuery, SignalWaiter } from '@dudousxd/nestjs-durable-core';
2
+
2
3
  interface RunRow {
3
4
  id: string;
4
5
  workflow: string;
@@ -39,6 +40,12 @@ interface WaiterRow {
39
40
  runId: string;
40
41
  seq: number;
41
42
  }
43
+ interface RunAttributeRow {
44
+ runId: string;
45
+ key: string;
46
+ strValue: string | null;
47
+ numValue: number | null;
48
+ }
42
49
  interface BufferedSignalRow {
43
50
  id: bigint;
44
51
  token: string;
@@ -47,6 +54,9 @@ interface BufferedSignalRow {
47
54
  type Args = any;
48
55
  interface Delegate<Row> {
49
56
  create(args: Args): Promise<Row>;
57
+ createMany(args: Args): Promise<{
58
+ count: number;
59
+ }>;
50
60
  findUnique(args: Args): Promise<Row | null>;
51
61
  findFirst(args?: Args): Promise<Row | null>;
52
62
  findMany(args?: Args): Promise<Row[]>;
@@ -56,14 +66,18 @@ interface Delegate<Row> {
56
66
  }>;
57
67
  upsert(args: Args): Promise<Row>;
58
68
  delete(args: Args): Promise<Row>;
69
+ deleteMany(args?: Args): Promise<{
70
+ count: number;
71
+ }>;
59
72
  }
60
- export interface DurablePrismaTx {
73
+ interface DurablePrismaTx {
61
74
  durableWorkflowRun: Delegate<RunRow>;
62
75
  durableStepCheckpoint: Delegate<CheckpointRow>;
76
+ durableRunAttribute: Delegate<RunAttributeRow>;
63
77
  durableSignalWaiter: Delegate<WaiterRow>;
64
78
  durableBufferedSignal: Delegate<BufferedSignalRow>;
65
79
  }
66
- export interface DurablePrismaClient extends DurablePrismaTx {
80
+ interface DurablePrismaClient extends DurablePrismaTx {
67
81
  /** Prisma's interactive-transaction form: runs `fn` with a tx-scoped client and commits on resolve. */
68
82
  $transaction<T>(fn: (tx: DurablePrismaTx) => Promise<T>): Promise<T>;
69
83
  }
@@ -72,11 +86,14 @@ export interface DurablePrismaClient extends DurablePrismaTx {
72
86
  * `prisma/schema.prisma` to your schema. JSON columns carry the run/step payloads directly;
73
87
  * `wakeAt` is a `BigInt` (epoch ms).
74
88
  */
75
- export declare class PrismaStateStore implements StateStore {
89
+ declare class PrismaStateStore implements StateStore {
76
90
  private readonly db;
77
91
  constructor(db: DurablePrismaClient);
78
92
  createRun(run: WorkflowRun): Promise<void>;
79
93
  updateRun(runId: string, patch: Partial<WorkflowRun>): Promise<void>;
94
+ /** Rewrite a run's normalized attribute rows: delete the old set, insert the current one. Mirrors
95
+ * the in-memory store's reindex so the side-table always reflects the run's live searchAttributes. */
96
+ private reindexAttributes;
80
97
  getRun(runId: string): Promise<WorkflowRun | null>;
81
98
  getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null>;
82
99
  saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
@@ -92,6 +109,8 @@ export declare class PrismaStateStore implements StateStore {
92
109
  renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean>;
93
110
  listRuns(query: RunQuery): Promise<WorkflowRun[]>;
94
111
  listCheckpoints(runId: string): Promise<StepCheckpoint[]>;
112
+ getLatestCheckpointByName(runId: string, name: string): Promise<StepCheckpoint | undefined>;
113
+ listCheckpointsByNamePrefix(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
95
114
  putSignalWaiter(waiter: SignalWaiter): Promise<void>;
96
115
  takeSignalWaiter(token: string): Promise<SignalWaiter | null>;
97
116
  listSignalWaiters(prefix: string): Promise<SignalWaiter[]>;
@@ -100,5 +119,5 @@ export declare class PrismaStateStore implements StateStore {
100
119
  payload: unknown;
101
120
  } | null>;
102
121
  }
103
- export {};
104
- //# sourceMappingURL=prisma-state-store.d.ts.map
122
+
123
+ export { type DurablePrismaClient, type DurablePrismaTx, PrismaStateStore };