@dudousxd/nestjs-durable-store-drizzle 0.16.0 → 0.18.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.
@@ -0,0 +1,924 @@
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, desc2) => {
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: !(desc2 = __getOwnPropDesc(from, key)) || desc2.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/pg/index.ts
22
+ var pg_exports = {};
23
+ __export(pg_exports, {
24
+ DEFAULT_NAMESPACE: () => DEFAULT_NAMESPACE,
25
+ DURABLE_PG_DDL: () => DURABLE_PG_DDL,
26
+ DrizzlePgStateStore: () => DrizzlePgStateStore,
27
+ drizzlePgExecutor: () => drizzlePgExecutor,
28
+ durableBufferedEvents: () => durableBufferedEvents,
29
+ durableBufferedSignals: () => durableBufferedSignals,
30
+ durablePgManagedTables: () => durablePgManagedTables,
31
+ durablePgSchema: () => durablePgSchema,
32
+ durableRunAttributes: () => durableRunAttributes,
33
+ durableSchedules: () => durableSchedules,
34
+ durableSignalWaiters: () => durableSignalWaiters,
35
+ durableStepCheckpoints: () => durableStepCheckpoints,
36
+ durableWorkflowRuns: () => durableWorkflowRuns,
37
+ ensureDrizzlePgDurableSchema: () => ensureDrizzlePgDurableSchema,
38
+ toDrizzleSql: () => toDrizzleSql
39
+ });
40
+ module.exports = __toCommonJS(pg_exports);
41
+
42
+ // src/pg/ddl.ts
43
+ var import_drizzle_orm = require("drizzle-orm");
44
+ var DURABLE_PG_DDL = [
45
+ `CREATE TABLE IF NOT EXISTS "durable_workflow_runs" (
46
+ "id" text PRIMARY KEY NOT NULL,
47
+ "workflow" text NOT NULL,
48
+ "workflow_version" text NOT NULL,
49
+ "status" text NOT NULL,
50
+ "input" jsonb,
51
+ "output" jsonb,
52
+ "error" jsonb,
53
+ "wake_at" timestamp with time zone,
54
+ "locked_by" text,
55
+ "locked_until" timestamp with time zone,
56
+ "awaiting_decision_task_id" text,
57
+ "recovery_attempts" integer,
58
+ "tags" jsonb,
59
+ "search_attributes" jsonb,
60
+ "priority" integer,
61
+ "namespace" text DEFAULT 'default' NOT NULL,
62
+ "origin" text,
63
+ "created_at" timestamp with time zone NOT NULL,
64
+ "updated_at" timestamp with time zone NOT NULL
65
+ )`,
66
+ `CREATE TABLE IF NOT EXISTS "durable_step_checkpoints" (
67
+ "run_id" text NOT NULL,
68
+ "seq" integer NOT NULL,
69
+ "name" text NOT NULL,
70
+ "kind" text NOT NULL,
71
+ "step_id" text NOT NULL,
72
+ "status" text NOT NULL,
73
+ "input" jsonb,
74
+ "output" jsonb,
75
+ "error" jsonb,
76
+ "events" jsonb,
77
+ "attempts" integer NOT NULL,
78
+ "worker_group" text,
79
+ "parallel_group" text,
80
+ "wake_at" timestamp with time zone,
81
+ "enqueued_at" timestamp with time zone,
82
+ "started_at" timestamp with time zone NOT NULL,
83
+ "finished_at" timestamp with time zone NOT NULL,
84
+ CONSTRAINT "durable_step_checkpoints_run_id_seq_pk" PRIMARY KEY("run_id","seq")
85
+ )`,
86
+ `CREATE TABLE IF NOT EXISTS "durable_run_attributes" (
87
+ "run_id" text NOT NULL,
88
+ "key" text NOT NULL,
89
+ "str_value" text,
90
+ "num_value" double precision,
91
+ CONSTRAINT "durable_run_attributes_run_id_key_pk" PRIMARY KEY("run_id","key")
92
+ )`,
93
+ `CREATE TABLE IF NOT EXISTS "durable_signal_waiters" (
94
+ "token" text PRIMARY KEY NOT NULL,
95
+ "run_id" text NOT NULL,
96
+ "seq" integer NOT NULL,
97
+ "parallel_group" text
98
+ )`,
99
+ `CREATE TABLE IF NOT EXISTS "durable_buffered_signals" (
100
+ "id" bigserial PRIMARY KEY NOT NULL,
101
+ "token" text NOT NULL,
102
+ "payload" jsonb
103
+ )`,
104
+ `CREATE TABLE IF NOT EXISTS "durable_buffered_events" (
105
+ "id" text PRIMARY KEY NOT NULL,
106
+ "name" text NOT NULL,
107
+ "payload" jsonb,
108
+ "published_at" timestamp with time zone NOT NULL
109
+ )`,
110
+ `CREATE TABLE IF NOT EXISTS "durable_schedules" (
111
+ "id" text PRIMARY KEY NOT NULL,
112
+ "namespace" text DEFAULT 'default' NOT NULL,
113
+ "workflow" text NOT NULL,
114
+ "paused" boolean DEFAULT false NOT NULL,
115
+ "next_fire_at" timestamp with time zone,
116
+ "tags" jsonb,
117
+ "spec" jsonb NOT NULL,
118
+ "state" jsonb NOT NULL,
119
+ "created_at" timestamp with time zone NOT NULL,
120
+ "updated_at" timestamp with time zone NOT NULL
121
+ )`,
122
+ `CREATE INDEX IF NOT EXISTS "durable_runs_status_idx" ON "durable_workflow_runs" USING btree ("status","wake_at")`,
123
+ `CREATE INDEX IF NOT EXISTS "durable_runs_workflow_status_idx" ON "durable_workflow_runs" USING btree ("workflow","status")`,
124
+ `CREATE INDEX IF NOT EXISTS "durable_workflow_runs_namespace_status_idx" ON "durable_workflow_runs" USING btree ("namespace","status","created_at")`,
125
+ `CREATE INDEX IF NOT EXISTS "durable_runs_tags_gin_idx" ON "durable_workflow_runs" USING gin ("tags" jsonb_path_ops)`,
126
+ `CREATE INDEX IF NOT EXISTS "durable_runs_status_updated_idx" ON "durable_workflow_runs" USING btree ("status","updated_at")`,
127
+ `CREATE INDEX IF NOT EXISTS "durable_run_attributes_num_idx" ON "durable_run_attributes" USING btree ("key","num_value")`,
128
+ `CREATE INDEX IF NOT EXISTS "durable_run_attributes_str_idx" ON "durable_run_attributes" USING btree ("key","str_value")`,
129
+ `CREATE INDEX IF NOT EXISTS "durable_signal_waiters_run_id_idx" ON "durable_signal_waiters" USING btree ("run_id")`,
130
+ `CREATE INDEX IF NOT EXISTS "durable_buffered_signals_token_idx" ON "durable_buffered_signals" USING btree ("token","id")`,
131
+ `CREATE INDEX IF NOT EXISTS "durable_buffered_events_name_published_at_idx" ON "durable_buffered_events" USING btree ("name","published_at")`,
132
+ `CREATE INDEX IF NOT EXISTS "durable_schedules_due_idx" ON "durable_schedules" USING btree ("paused","next_fire_at")`
133
+ ];
134
+ var SCHEMA_LOCK_KEY = "durable_schema";
135
+ async function ensureDrizzlePgDurableSchema(db) {
136
+ await db.transaction(async (tx) => {
137
+ await tx.execute(import_drizzle_orm.sql`SELECT pg_advisory_xact_lock(hashtext(${SCHEMA_LOCK_KEY}))`);
138
+ for (const statement of DURABLE_PG_DDL) await tx.execute(import_drizzle_orm.sql.raw(statement));
139
+ });
140
+ }
141
+ __name(ensureDrizzlePgDurableSchema, "ensureDrizzlePgDurableSchema");
142
+
143
+ // src/pg/drizzle-pg-state-store.ts
144
+ var import_nestjs_durable_core = require("@dudousxd/nestjs-durable-core");
145
+ var import_drizzle_orm3 = require("drizzle-orm");
146
+
147
+ // src/pg/schema.ts
148
+ var import_drizzle_orm2 = require("drizzle-orm");
149
+ var import_pg_core = require("drizzle-orm/pg-core");
150
+ var DEFAULT_NAMESPACE = "default";
151
+ var tstz = /* @__PURE__ */ __name((name) => (0, import_pg_core.timestamp)(name, {
152
+ withTimezone: true,
153
+ mode: "date"
154
+ }), "tstz");
155
+ var durableWorkflowRuns = (0, import_pg_core.pgTable)("durable_workflow_runs", {
156
+ id: (0, import_pg_core.text)("id").primaryKey(),
157
+ workflow: (0, import_pg_core.text)("workflow").notNull(),
158
+ workflowVersion: (0, import_pg_core.text)("workflow_version").notNull(),
159
+ status: (0, import_pg_core.text)("status").notNull(),
160
+ input: (0, import_pg_core.jsonb)("input"),
161
+ output: (0, import_pg_core.jsonb)("output"),
162
+ error: (0, import_pg_core.jsonb)("error"),
163
+ wakeAt: tstz("wake_at"),
164
+ lockedBy: (0, import_pg_core.text)("locked_by"),
165
+ lockedUntil: tstz("locked_until"),
166
+ // REMOTE turn the engine suspended on awaiting a decision; matched by completeRemoteDecision so
167
+ // only the currently-awaited turn's decision is applied. Nullable: cleared when a decision lands.
168
+ awaitingDecisionTaskId: (0, import_pg_core.text)("awaiting_decision_task_id"),
169
+ recoveryAttempts: (0, import_pg_core.integer)("recovery_attempts"),
170
+ tags: (0, import_pg_core.jsonb)("tags").$type(),
171
+ searchAttributes: (0, import_pg_core.jsonb)("search_attributes").$type(),
172
+ priority: (0, import_pg_core.integer)("priority"),
173
+ // The worker-pool partition (tenant) — THE isolation boundary every poll predicate filters on.
174
+ // NOT NULL DEFAULT 'default' so a row can never fall outside every worker's namespace.
175
+ namespace: (0, import_pg_core.text)("namespace").notNull().default(DEFAULT_NAMESPACE),
176
+ // Which package declared the workflow. Nullable, no default: NULL = unknown, never guessed.
177
+ origin: (0, import_pg_core.text)("origin"),
178
+ createdAt: tstz("created_at").notNull(),
179
+ updatedAt: tstz("updated_at").notNull()
180
+ }, (t) => [
181
+ // Timer poller (`listDueTimers`) + recovery (`listIncompleteRuns`). Same name as the TypeORM DDL.
182
+ (0, import_pg_core.index)("durable_runs_status_idx").on(t.status, t.wakeAt),
183
+ // Dashboard `listRuns({ workflow, status })` and the singleton gate's per-workflow scans.
184
+ (0, import_pg_core.index)("durable_runs_workflow_status_idx").on(t.workflow, t.status),
185
+ // Every namespaced poll tick: `namespace = $1 AND status = $2 ORDER BY created_at` (FIFO pick-up).
186
+ // Name matches the MikroORM adapter + the SQLite drizzle schema so a store swap keeps the index.
187
+ (0, import_pg_core.index)("durable_workflow_runs_namespace_status_idx").on(t.namespace, t.status, t.createdAt),
188
+ // `listRuns({ tag })` compiles to `tags @> '["tag"]'`, which a `jsonb_path_ops` GIN index serves —
189
+ // this is what keeps the singleton gate (`tag: singleton:<key>`, e.g. a per-tenant concurrency
190
+ // key) an index probe instead of a scan of every run the workflow ever had.
191
+ (0, import_pg_core.index)("durable_runs_tags_gin_idx").using("gin", t.tags.op("jsonb_path_ops")),
192
+ // Retention (`pruneTerminalRuns`) orders a status set by `updated_at`.
193
+ (0, import_pg_core.index)("durable_runs_status_updated_idx").on(t.status, t.updatedAt)
194
+ ]);
195
+ var durableStepCheckpoints = (0, import_pg_core.pgTable)(
196
+ "durable_step_checkpoints",
197
+ {
198
+ runId: (0, import_pg_core.text)("run_id").notNull(),
199
+ seq: (0, import_pg_core.integer)("seq").notNull(),
200
+ name: (0, import_pg_core.text)("name").notNull(),
201
+ kind: (0, import_pg_core.text)("kind").notNull(),
202
+ stepId: (0, import_pg_core.text)("step_id").notNull(),
203
+ status: (0, import_pg_core.text)("status").notNull(),
204
+ input: (0, import_pg_core.jsonb)("input"),
205
+ output: (0, import_pg_core.jsonb)("output"),
206
+ error: (0, import_pg_core.jsonb)("error"),
207
+ events: (0, import_pg_core.jsonb)("events"),
208
+ attempts: (0, import_pg_core.integer)("attempts").notNull(),
209
+ workerGroup: (0, import_pg_core.text)("worker_group"),
210
+ parallelGroup: (0, import_pg_core.text)("parallel_group"),
211
+ wakeAt: tstz("wake_at"),
212
+ enqueuedAt: tstz("enqueued_at"),
213
+ startedAt: tstz("started_at").notNull(),
214
+ finishedAt: tstz("finished_at").notNull()
215
+ },
216
+ // (run_id, seq) is the primary key, so listCheckpoints / getEvent already hit an index.
217
+ (t) => [
218
+ (0, import_pg_core.primaryKey)({
219
+ columns: [
220
+ t.runId,
221
+ t.seq
222
+ ]
223
+ })
224
+ ]
225
+ );
226
+ var durableRunAttributes = (0, import_pg_core.pgTable)("durable_run_attributes", {
227
+ runId: (0, import_pg_core.text)("run_id").notNull(),
228
+ key: (0, import_pg_core.text)("key").notNull(),
229
+ strValue: (0, import_pg_core.text)("str_value"),
230
+ numValue: (0, import_pg_core.doublePrecision)("num_value")
231
+ }, (t) => [
232
+ (0, import_pg_core.primaryKey)({
233
+ columns: [
234
+ t.runId,
235
+ t.key
236
+ ]
237
+ }),
238
+ (0, import_pg_core.index)("durable_run_attributes_num_idx").on(t.key, t.numValue),
239
+ (0, import_pg_core.index)("durable_run_attributes_str_idx").on(t.key, t.strValue)
240
+ ]);
241
+ var durableSignalWaiters = (0, import_pg_core.pgTable)(
242
+ "durable_signal_waiters",
243
+ {
244
+ token: (0, import_pg_core.text)("token").primaryKey(),
245
+ runId: (0, import_pg_core.text)("run_id").notNull(),
246
+ seq: (0, import_pg_core.integer)("seq").notNull(),
247
+ parallelGroup: (0, import_pg_core.text)("parallel_group")
248
+ },
249
+ // deleteRun / pruneTerminalRuns delete a run's waiters by run_id.
250
+ (t) => [
251
+ (0, import_pg_core.index)("durable_signal_waiters_run_id_idx").on(t.runId)
252
+ ]
253
+ );
254
+ var durableBufferedSignals = (0, import_pg_core.pgTable)(
255
+ "durable_buffered_signals",
256
+ {
257
+ id: (0, import_pg_core.bigserial)("id", {
258
+ mode: "number"
259
+ }).primaryKey(),
260
+ token: (0, import_pg_core.text)("token").notNull(),
261
+ payload: (0, import_pg_core.jsonb)("payload")
262
+ },
263
+ // Taken FIFO per token (smallest id first).
264
+ (t) => [
265
+ (0, import_pg_core.index)("durable_buffered_signals_token_idx").on(t.token, t.id)
266
+ ]
267
+ );
268
+ var durableBufferedEvents = (0, import_pg_core.pgTable)("durable_buffered_events", {
269
+ id: (0, import_pg_core.text)("id").primaryKey(),
270
+ name: (0, import_pg_core.text)("name").notNull(),
271
+ payload: (0, import_pg_core.jsonb)("payload"),
272
+ publishedAt: tstz("published_at").notNull()
273
+ }, (t) => [
274
+ (0, import_pg_core.index)("durable_buffered_events_name_published_at_idx").on(t.name, t.publishedAt)
275
+ ]);
276
+ var durableSchedules = (0, import_pg_core.pgTable)(
277
+ "durable_schedules",
278
+ {
279
+ id: (0, import_pg_core.text)("id").primaryKey(),
280
+ namespace: (0, import_pg_core.text)("namespace").notNull().default(DEFAULT_NAMESPACE),
281
+ workflow: (0, import_pg_core.text)("workflow").notNull(),
282
+ paused: (0, import_pg_core.boolean)("paused").notNull().default(false),
283
+ nextFireAt: tstz("next_fire_at"),
284
+ tags: (0, import_pg_core.jsonb)("tags").$type(),
285
+ spec: (0, import_pg_core.jsonb)("spec").$type().notNull(),
286
+ state: (0, import_pg_core.jsonb)("state").$type().notNull(),
287
+ createdAt: tstz("created_at").notNull(),
288
+ updatedAt: tstz("updated_at").notNull()
289
+ },
290
+ // The poll's "due by T" scan: `paused = false AND next_fire_at <= T ORDER BY next_fire_at`.
291
+ (t) => [
292
+ (0, import_pg_core.index)("durable_schedules_due_idx").on(t.paused, t.nextFireAt)
293
+ ]
294
+ );
295
+ var durablePgSchema = {
296
+ durableWorkflowRuns,
297
+ durableStepCheckpoints,
298
+ durableRunAttributes,
299
+ durableSignalWaiters,
300
+ durableBufferedSignals,
301
+ durableBufferedEvents,
302
+ durableSchedules
303
+ };
304
+ function durablePgManagedTables() {
305
+ return Object.values(durablePgSchema).map((table) => (0, import_drizzle_orm2.getTableName)(table));
306
+ }
307
+ __name(durablePgManagedTables, "durablePgManagedTables");
308
+
309
+ // src/pg/drizzle-pg-state-store.ts
310
+ function anyOf(conditions) {
311
+ const present = conditions.filter((c) => c !== void 0);
312
+ return present.length ? (0, import_drizzle_orm3.or)(...present) : import_drizzle_orm3.sql`1 = 0`;
313
+ }
314
+ __name(anyOf, "anyOf");
315
+ function namespaceFilter(namespace) {
316
+ return namespace === void 0 ? void 0 : (0, import_drizzle_orm3.eq)(durableWorkflowRuns.namespace, namespace);
317
+ }
318
+ __name(namespaceFilter, "namespaceFilter");
319
+ function hasTag(tag) {
320
+ return import_drizzle_orm3.sql`${durableWorkflowRuns.tags} @> ${JSON.stringify([
321
+ tag
322
+ ])}::jsonb`;
323
+ }
324
+ __name(hasTag, "hasTag");
325
+ var date = /* @__PURE__ */ __name((ms2) => ms2 == null ? null : new Date(ms2), "date");
326
+ var ms = /* @__PURE__ */ __name((d) => d == null ? void 0 : d.getTime(), "ms");
327
+ var DrizzlePgStateStore = class _DrizzlePgStateStore {
328
+ static {
329
+ __name(this, "DrizzlePgStateStore");
330
+ }
331
+ db;
332
+ options;
333
+ scopeNamespace;
334
+ constructor(db, options = {}) {
335
+ this.db = db;
336
+ this.options = options;
337
+ this.scopeNamespace = options.scope?.namespace;
338
+ }
339
+ /** A store confined to `scope.namespace`, sharing this store's db (no new connection). Used by the
340
+ * NestJS module's `scopeReads`; `{ namespace: undefined }` is the unscoped operator view. */
341
+ withScope(scope) {
342
+ return new _DrizzlePgStateStore(this.db, {
343
+ ...this.options,
344
+ scope
345
+ });
346
+ }
347
+ /** Idempotent CREATE TABLE / INDEX IF NOT EXISTS under an advisory lock — called on boot when the
348
+ * module's `autoSchema` is on. Turn `autoSchema` off if drizzle-kit owns these tables. */
349
+ async ensureSchema() {
350
+ await ensureDrizzlePgDurableSchema(this.db);
351
+ }
352
+ /** The read-scope predicate on the runs table (undefined when the store is unscoped). */
353
+ scope() {
354
+ return namespaceFilter(this.scopeNamespace);
355
+ }
356
+ async createRun(run) {
357
+ await this.db.transaction(async (tx) => {
358
+ await tx.insert(durableWorkflowRuns).values(toRunRow(run));
359
+ await reindexAttributes(tx, run.id, run.searchAttributes);
360
+ });
361
+ }
362
+ async updateRun(runId, patch) {
363
+ const row = toRunPatch(patch);
364
+ const reindex = "searchAttributes" in patch;
365
+ if (!Object.keys(row).length && !reindex) return;
366
+ await this.db.transaction(async (tx) => {
367
+ if (Object.keys(row).length) await tx.update(durableWorkflowRuns).set(row).where((0, import_drizzle_orm3.eq)(durableWorkflowRuns.id, runId));
368
+ if (reindex) await reindexAttributes(tx, runId, patch.searchAttributes);
369
+ });
370
+ }
371
+ async getRun(runId) {
372
+ const rows = await this.db.select().from(durableWorkflowRuns).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableWorkflowRuns.id, runId), this.scope())).limit(1);
373
+ return rows[0] ? fromRunRow(rows[0]) : null;
374
+ }
375
+ async deleteRun(runId) {
376
+ await this.db.transaction(async (tx) => {
377
+ await deleteRunsCascade(tx, [
378
+ runId
379
+ ]);
380
+ });
381
+ }
382
+ async deleteRuns(runIds) {
383
+ if (runIds.length === 0) return;
384
+ await this.db.transaction(async (tx) => {
385
+ await deleteRunsCascade(tx, runIds);
386
+ });
387
+ }
388
+ async pruneTerminalRuns(policy, nowMs, limit) {
389
+ const statuses = policy.statuses.filter((s) => import_nestjs_durable_core.TERMINAL_RUN_STATUSES.includes(s));
390
+ if (statuses.length === 0 || limit <= 0) return 0;
391
+ if (policy.maxAge == null && policy.maxCount == null) return 0;
392
+ const scoped = this.runFilters({
393
+ ...policy.scope ?? {},
394
+ statuses
395
+ });
396
+ return this.db.transaction(async (tx) => {
397
+ const ids = /* @__PURE__ */ new Set();
398
+ if (policy.maxAge != null) {
399
+ const cutoff = new Date(nowMs - (0, import_nestjs_durable_core.parseDuration)(policy.maxAge));
400
+ const rows = await tx.select({
401
+ id: durableWorkflowRuns.id
402
+ }).from(durableWorkflowRuns).where((0, import_drizzle_orm3.and)(...scoped, (0, import_drizzle_orm3.lt)(durableWorkflowRuns.updatedAt, cutoff))).orderBy((0, import_drizzle_orm3.asc)(durableWorkflowRuns.updatedAt)).limit(limit).for("update", {
403
+ skipLocked: true
404
+ });
405
+ for (const r of rows) ids.add(r.id);
406
+ }
407
+ if (policy.maxCount != null && ids.size < limit) {
408
+ const rows = await tx.select({
409
+ id: durableWorkflowRuns.id
410
+ }).from(durableWorkflowRuns).where((0, import_drizzle_orm3.and)(...scoped)).orderBy((0, import_drizzle_orm3.desc)(durableWorkflowRuns.updatedAt), (0, import_drizzle_orm3.desc)(durableWorkflowRuns.id)).limit(limit).offset(policy.maxCount).for("update", {
411
+ skipLocked: true
412
+ });
413
+ for (const r of rows) {
414
+ if (ids.size >= limit) break;
415
+ ids.add(r.id);
416
+ }
417
+ }
418
+ if (ids.size === 0) return 0;
419
+ const idList = [
420
+ ...ids
421
+ ].slice(0, limit);
422
+ await deleteRunsCascade(tx, idList);
423
+ return idList.length;
424
+ });
425
+ }
426
+ async getCheckpoint(runId, seq) {
427
+ const rows = await this.db.select().from(durableStepCheckpoints).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableStepCheckpoints.runId, runId), (0, import_drizzle_orm3.eq)(durableStepCheckpoints.seq, seq))).limit(1);
428
+ return rows[0] ? fromCheckpointRow(rows[0]) : null;
429
+ }
430
+ async saveCheckpoint(checkpoint) {
431
+ await upsertCheckpoint(this.db, checkpoint);
432
+ }
433
+ async transaction(work) {
434
+ return this.db.transaction(async (tx) => work({
435
+ raw: tx,
436
+ saveCheckpoint: /* @__PURE__ */ __name((cp) => upsertCheckpoint(tx, cp), "saveCheckpoint")
437
+ }));
438
+ }
439
+ /** Crash recovery. Namespace-scoped: a worker re-drives only its own tenant's runs. */
440
+ async listIncompleteRuns(namespace) {
441
+ const rows = await this.db.select().from(durableWorkflowRuns).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.inArray)(durableWorkflowRuns.status, [
442
+ "running",
443
+ "cancelling"
444
+ ]), namespaceFilter(namespace), this.scope()));
445
+ return rows.map(fromRunRow);
446
+ }
447
+ /** FIFO pick-up, served by `durable_workflow_runs_namespace_status_idx`. The engine then leases
448
+ * each run with {@link tryLockRun}; the listing itself takes no locks (it runs outside a tx). */
449
+ async listPendingRuns(limit, namespace) {
450
+ const rows = await this.db.select().from(durableWorkflowRuns).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableWorkflowRuns.status, "pending"), namespaceFilter(namespace), this.scope())).orderBy((0, import_drizzle_orm3.asc)(durableWorkflowRuns.createdAt)).limit(limit);
451
+ return rows.map(fromRunRow);
452
+ }
453
+ async listDueTimers(nowMs, namespace) {
454
+ const rows = await this.db.select().from(durableWorkflowRuns).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableWorkflowRuns.status, "suspended"), (0, import_drizzle_orm3.isNotNull)(durableWorkflowRuns.wakeAt), (0, import_drizzle_orm3.lte)(durableWorkflowRuns.wakeAt, new Date(nowMs)), namespaceFilter(namespace), this.scope()));
455
+ return rows.map(fromRunRow);
456
+ }
457
+ async tryLockRun(runId, owner, leaseUntilMs, nowMs) {
458
+ const rows = await this.db.update(durableWorkflowRuns).set({
459
+ lockedBy: owner,
460
+ lockedUntil: new Date(leaseUntilMs)
461
+ }).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableWorkflowRuns.id, runId), (0, import_drizzle_orm3.or)((0, import_drizzle_orm3.isNull)(durableWorkflowRuns.lockedUntil), (0, import_drizzle_orm3.lte)(durableWorkflowRuns.lockedUntil, new Date(nowMs))))).returning({
462
+ id: durableWorkflowRuns.id
463
+ });
464
+ return rows.length === 1;
465
+ }
466
+ async releaseRunLock(runId) {
467
+ await this.db.update(durableWorkflowRuns).set({
468
+ lockedBy: null,
469
+ lockedUntil: null
470
+ }).where((0, import_drizzle_orm3.eq)(durableWorkflowRuns.id, runId));
471
+ }
472
+ async renewRunLock(runId, owner, leaseUntilMs) {
473
+ const rows = await this.db.update(durableWorkflowRuns).set({
474
+ lockedUntil: new Date(leaseUntilMs)
475
+ }).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableWorkflowRuns.id, runId), (0, import_drizzle_orm3.eq)(durableWorkflowRuns.lockedBy, owner))).returning({
476
+ id: durableWorkflowRuns.id
477
+ });
478
+ return rows.length === 1;
479
+ }
480
+ /** The predicates every run query shares — {@link listRuns} pages them, the facets group them. */
481
+ runFilters(query) {
482
+ return [
483
+ this.scope(),
484
+ query.workflow ? (0, import_drizzle_orm3.eq)(durableWorkflowRuns.workflow, query.workflow) : void 0,
485
+ query.workflows ? query.workflows.length ? (0, import_drizzle_orm3.inArray)(durableWorkflowRuns.workflow, query.workflows) : import_drizzle_orm3.sql`1 = 0` : void 0,
486
+ // The tenant boundary. Also the engine's execution-timeout sweep path
487
+ // (`listRuns({ workflow, status, namespace })`) — unscoped, it would cancel another tenant's runs.
488
+ namespaceFilter(query.namespace),
489
+ query.namespaces ? query.namespaces.length ? (0, import_drizzle_orm3.inArray)(durableWorkflowRuns.namespace, query.namespaces) : import_drizzle_orm3.sql`1 = 0` : void 0,
490
+ // `null` selects the unattributed bucket; a value never matches a NULL origin.
491
+ query.origin === null ? (0, import_drizzle_orm3.isNull)(durableWorkflowRuns.origin) : query.origin !== void 0 ? (0, import_drizzle_orm3.eq)(durableWorkflowRuns.origin, query.origin) : void 0,
492
+ query.origins ? anyOf(query.origins.map((o) => o === null ? (0, import_drizzle_orm3.isNull)(durableWorkflowRuns.origin) : (0, import_drizzle_orm3.eq)(durableWorkflowRuns.origin, o))) : void 0,
493
+ query.status ? (0, import_drizzle_orm3.eq)(durableWorkflowRuns.status, query.status) : void 0,
494
+ query.statuses ? query.statuses.length ? (0, import_drizzle_orm3.inArray)(durableWorkflowRuns.status, query.statuses) : import_drizzle_orm3.sql`1 = 0` : void 0,
495
+ query.tag ? hasTag(query.tag) : void 0,
496
+ query.tags ? anyOf(query.tags.map(hasTag)) : void 0,
497
+ // One EXISTS per attribute predicate against the normalized side table (ANDed).
498
+ ...query.attributes?.map((f) => this.attributeExists(f)) ?? []
499
+ ].filter((f) => f !== void 0);
500
+ }
501
+ async listRuns(query) {
502
+ const filters = this.runFilters(query);
503
+ const base = this.db.select().from(durableWorkflowRuns).where(filters.length ? (0, import_drizzle_orm3.and)(...filters) : void 0).orderBy((0, import_drizzle_orm3.desc)(durableWorkflowRuns.createdAt), (0, import_drizzle_orm3.desc)(durableWorkflowRuns.id)).$dynamic();
504
+ if (query.limit != null) base.limit(query.limit);
505
+ if (query.offset != null) base.offset(query.offset);
506
+ const rows = await base;
507
+ return rows.map(fromRunRow);
508
+ }
509
+ async countRuns(query) {
510
+ const filters = this.runFilters(query);
511
+ const rows = await this.db.select({
512
+ count: import_drizzle_orm3.sql`count(*)`.mapWith(Number)
513
+ }).from(durableWorkflowRuns).where(filters.length ? (0, import_drizzle_orm3.and)(...filters) : void 0);
514
+ return rows[0]?.count ?? 0;
515
+ }
516
+ async runFacets(query) {
517
+ const filters = this.runFilters(query);
518
+ const rows = await this.db.select({
519
+ status: durableWorkflowRuns.status,
520
+ origin: durableWorkflowRuns.origin,
521
+ count: import_drizzle_orm3.sql`count(*)`.mapWith(Number)
522
+ }).from(durableWorkflowRuns).where(filters.length ? (0, import_drizzle_orm3.and)(...filters) : void 0).groupBy(durableWorkflowRuns.status, durableWorkflowRuns.origin);
523
+ return (0, import_nestjs_durable_core.mergeRunFacetRows)(rows.map((r) => ({
524
+ status: r.status,
525
+ origin: r.origin,
526
+ count: r.count
527
+ })));
528
+ }
529
+ /** Run-table axes are an exact `GROUP BY`; the tag / attribute axes are counted over a bounded
530
+ * page of runs (see `RunValueFacetOptions.scan`), like the TypeORM and SQLite Drizzle adapters. */
531
+ async runValueFacets(axis, query, opts) {
532
+ if (!(0, import_nestjs_durable_core.axisIsRunColumn)(axis)) {
533
+ const runs = await this.listRuns({
534
+ ...query,
535
+ limit: opts?.scan ?? import_nestjs_durable_core.RUN_VALUE_FACET_SCAN
536
+ });
537
+ return (0, import_nestjs_durable_core.runValueFacetsFromRuns)(runs, axis, opts);
538
+ }
539
+ const column = durableWorkflowRuns[axis.field];
540
+ const filters = this.runFilters(query);
541
+ const rows = await this.db.select({
542
+ value: column,
543
+ count: import_drizzle_orm3.sql`count(*)`.mapWith(Number)
544
+ }).from(durableWorkflowRuns).where(filters.length ? (0, import_drizzle_orm3.and)(...filters) : void 0).groupBy(column);
545
+ return (0, import_nestjs_durable_core.mergeRunValueFacetRows)(rows.map((r) => ({
546
+ value: r.value ?? null,
547
+ count: r.count
548
+ })), opts);
549
+ }
550
+ /** One attribute predicate as an EXISTS on the side table, correlated to the outer run. `ne` also
551
+ * excludes runs lacking the key (EXISTS requires the row). Numbers compare `num_value`, strings
552
+ * and booleans `str_value`; an `in` set ORs its members inside the one EXISTS. */
553
+ attributeExists(f) {
554
+ const comparisons = (0, import_nestjs_durable_core.attributePredicateOperands)(f).map(({ column, comparator, operand }) => import_drizzle_orm3.sql`${column === "numValue" ? durableRunAttributes.numValue : durableRunAttributes.strValue} ${import_drizzle_orm3.sql.raw(comparator)} ${operand}`);
555
+ return (0, import_drizzle_orm3.exists)(this.db.select({
556
+ one: import_drizzle_orm3.sql`1`
557
+ }).from(durableRunAttributes).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableRunAttributes.runId, durableWorkflowRuns.id), (0, import_drizzle_orm3.eq)(durableRunAttributes.key, f.key), anyOf(comparisons))));
558
+ }
559
+ // ---- persisted schedules ----------------------------------------------------------------------
560
+ async saveSchedule(record) {
561
+ const row = toScheduleRow(record);
562
+ await this.db.insert(durableSchedules).values(row).onConflictDoUpdate({
563
+ target: durableSchedules.id,
564
+ set: row
565
+ });
566
+ }
567
+ async getSchedule(id) {
568
+ const rows = await this.db.select().from(durableSchedules).where((0, import_drizzle_orm3.eq)(durableSchedules.id, id)).limit(1);
569
+ return rows[0] ? fromScheduleRow(rows[0]) : null;
570
+ }
571
+ /** A single conditional `UPDATE … RETURNING`: the compare-and-set on `next_fire_at` is atomic. */
572
+ async updateSchedule(id, patch, expectedNextFireAt) {
573
+ const set = toSchedulePatch(patch);
574
+ if (!Object.keys(set).length) return await this.getSchedule(id) !== null;
575
+ const rows = await this.db.update(durableSchedules).set(set).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableSchedules.id, id), expectedNextFireAt === void 0 ? void 0 : expectedNextFireAt === null ? (0, import_drizzle_orm3.isNull)(durableSchedules.nextFireAt) : (0, import_drizzle_orm3.eq)(durableSchedules.nextFireAt, new Date(expectedNextFireAt)))).returning({
576
+ id: durableSchedules.id
577
+ });
578
+ return rows.length > 0;
579
+ }
580
+ async deleteSchedule(id) {
581
+ const rows = await this.db.delete(durableSchedules).where((0, import_drizzle_orm3.eq)(durableSchedules.id, id)).returning({
582
+ id: durableSchedules.id
583
+ });
584
+ return rows.length > 0;
585
+ }
586
+ async listSchedules(query) {
587
+ const base = this.db.select().from(durableSchedules).where((0, import_drizzle_orm3.and)(query.namespace !== void 0 ? (0, import_drizzle_orm3.eq)(durableSchedules.namespace, query.namespace) : void 0, query.workflow !== void 0 ? (0, import_drizzle_orm3.eq)(durableSchedules.workflow, query.workflow) : void 0, query.paused !== void 0 ? (0, import_drizzle_orm3.eq)(durableSchedules.paused, query.paused) : void 0, query.tag !== void 0 ? import_drizzle_orm3.sql`${durableSchedules.tags} @> ${JSON.stringify([
588
+ query.tag
589
+ ])}::jsonb` : void 0, query.dueBy !== void 0 ? (0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableSchedules.paused, false), (0, import_drizzle_orm3.lte)(durableSchedules.nextFireAt, new Date(query.dueBy))) : void 0)).orderBy(import_drizzle_orm3.sql`${durableSchedules.nextFireAt} ASC NULLS LAST`, (0, import_drizzle_orm3.asc)(durableSchedules.id)).$dynamic();
590
+ if (query.limit != null) base.limit(query.limit);
591
+ if (query.offset != null) base.offset(query.offset);
592
+ return (await base).map(fromScheduleRow);
593
+ }
594
+ async listCheckpoints(runId) {
595
+ const rows = await this.db.select().from(durableStepCheckpoints).where((0, import_drizzle_orm3.eq)(durableStepCheckpoints.runId, runId)).orderBy((0, import_drizzle_orm3.asc)(durableStepCheckpoints.seq));
596
+ return rows.map(fromCheckpointRow);
597
+ }
598
+ async getLatestCheckpointByName(runId, name) {
599
+ const rows = await this.db.select().from(durableStepCheckpoints).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableStepCheckpoints.runId, runId), (0, import_drizzle_orm3.eq)(durableStepCheckpoints.name, name))).orderBy((0, import_drizzle_orm3.desc)(durableStepCheckpoints.seq)).limit(1);
600
+ return rows[0] ? fromCheckpointRow(rows[0]) : void 0;
601
+ }
602
+ async listCheckpointsByNamePrefix(runId, prefixes) {
603
+ if (prefixes.length === 0) return [];
604
+ const rows = await this.db.select().from(durableStepCheckpoints).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableStepCheckpoints.runId, runId), (0, import_drizzle_orm3.or)(...prefixes.map((p) => (0, import_drizzle_orm3.like)(durableStepCheckpoints.name, `${escapeLike(p)}%`))))).orderBy((0, import_drizzle_orm3.asc)(durableStepCheckpoints.seq));
605
+ return rows.map(fromCheckpointRow);
606
+ }
607
+ async putSignalWaiter(waiter) {
608
+ const set = {
609
+ runId: waiter.runId,
610
+ seq: waiter.seq,
611
+ parallelGroup: waiter.parallelGroup ?? null
612
+ };
613
+ await this.db.insert(durableSignalWaiters).values({
614
+ token: waiter.token,
615
+ ...set
616
+ }).onConflictDoUpdate({
617
+ target: durableSignalWaiters.token,
618
+ set
619
+ });
620
+ }
621
+ async takeSignalWaiter(token) {
622
+ const rows = await this.db.delete(durableSignalWaiters).where((0, import_drizzle_orm3.eq)(durableSignalWaiters.token, token)).returning();
623
+ const row = rows[0];
624
+ return row ? fromWaiterRow(row) : null;
625
+ }
626
+ async listSignalWaiters(prefix) {
627
+ const rows = await this.db.select().from(durableSignalWaiters).where((0, import_drizzle_orm3.like)(durableSignalWaiters.token, `${escapeLike(prefix)}%`));
628
+ return rows.map(fromWaiterRow);
629
+ }
630
+ async removeSignalWaiter(waiter) {
631
+ await this.db.delete(durableSignalWaiters).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableSignalWaiters.token, waiter.token), (0, import_drizzle_orm3.eq)(durableSignalWaiters.runId, waiter.runId), (0, import_drizzle_orm3.eq)(durableSignalWaiters.seq, waiter.seq)));
632
+ }
633
+ async bufferSignal(token, payload) {
634
+ await this.db.insert(durableBufferedSignals).values({
635
+ token,
636
+ payload: payload ?? null
637
+ });
638
+ }
639
+ async takeBufferedSignal(token) {
640
+ const oldest = this.db.select({
641
+ id: durableBufferedSignals.id
642
+ }).from(durableBufferedSignals).where((0, import_drizzle_orm3.eq)(durableBufferedSignals.token, token)).orderBy((0, import_drizzle_orm3.asc)(durableBufferedSignals.id)).limit(1).for("update", {
643
+ skipLocked: true
644
+ });
645
+ const rows = await this.db.delete(durableBufferedSignals).where((0, import_drizzle_orm3.inArray)(durableBufferedSignals.id, oldest)).returning({
646
+ payload: durableBufferedSignals.payload
647
+ });
648
+ const row = rows[0];
649
+ return row ? {
650
+ payload: row.payload ?? void 0
651
+ } : null;
652
+ }
653
+ async bufferEvent(input) {
654
+ await this.db.insert(durableBufferedEvents).values({
655
+ id: input.id,
656
+ name: input.name,
657
+ payload: input.payload ?? null,
658
+ publishedAt: new Date(input.publishedAt)
659
+ });
660
+ }
661
+ async listBufferedEvents(name, limit) {
662
+ const rows = await this.db.select().from(durableBufferedEvents).where((0, import_drizzle_orm3.eq)(durableBufferedEvents.name, name)).orderBy((0, import_drizzle_orm3.asc)(durableBufferedEvents.publishedAt), (0, import_drizzle_orm3.asc)(durableBufferedEvents.id)).limit(limit);
663
+ return rows.map((r) => ({
664
+ id: r.id,
665
+ payload: r.payload ?? void 0,
666
+ publishedAt: r.publishedAt.getTime()
667
+ }));
668
+ }
669
+ async removeBufferedEvent(id) {
670
+ const rows = await this.db.delete(durableBufferedEvents).where((0, import_drizzle_orm3.eq)(durableBufferedEvents.id, id)).returning({
671
+ id: durableBufferedEvents.id
672
+ });
673
+ return rows.length === 1;
674
+ }
675
+ };
676
+ function escapeLike(value) {
677
+ return value.replace(/[\\%_]/g, (c) => `\\${c}`);
678
+ }
679
+ __name(escapeLike, "escapeLike");
680
+ async function upsertCheckpoint(db, cp) {
681
+ const row = toCheckpointRow(cp);
682
+ await db.insert(durableStepCheckpoints).values(row).onConflictDoUpdate({
683
+ target: [
684
+ durableStepCheckpoints.runId,
685
+ durableStepCheckpoints.seq
686
+ ],
687
+ set: row
688
+ });
689
+ }
690
+ __name(upsertCheckpoint, "upsertCheckpoint");
691
+ async function reindexAttributes(db, runId, attributes) {
692
+ await db.delete(durableRunAttributes).where((0, import_drizzle_orm3.eq)(durableRunAttributes.runId, runId));
693
+ const rows = (0, import_nestjs_durable_core.normalizeAttributeRows)(runId, attributes);
694
+ if (rows.length) await db.insert(durableRunAttributes).values(rows);
695
+ }
696
+ __name(reindexAttributes, "reindexAttributes");
697
+ async function deleteRunsCascade(db, runIds) {
698
+ await db.delete(durableStepCheckpoints).where((0, import_drizzle_orm3.inArray)(durableStepCheckpoints.runId, runIds));
699
+ await db.delete(durableSignalWaiters).where((0, import_drizzle_orm3.inArray)(durableSignalWaiters.runId, runIds));
700
+ await db.delete(durableRunAttributes).where((0, import_drizzle_orm3.inArray)(durableRunAttributes.runId, runIds));
701
+ await db.delete(durableWorkflowRuns).where((0, import_drizzle_orm3.inArray)(durableWorkflowRuns.id, runIds));
702
+ }
703
+ __name(deleteRunsCascade, "deleteRunsCascade");
704
+ function fromWaiterRow(row) {
705
+ return {
706
+ token: row.token,
707
+ runId: row.runId,
708
+ seq: row.seq,
709
+ parallelGroup: row.parallelGroup ?? void 0
710
+ };
711
+ }
712
+ __name(fromWaiterRow, "fromWaiterRow");
713
+ function toRunRow(run) {
714
+ return {
715
+ id: run.id,
716
+ workflow: run.workflow,
717
+ workflowVersion: run.workflowVersion,
718
+ status: run.status,
719
+ input: run.input ?? null,
720
+ output: run.output ?? null,
721
+ error: run.error ?? null,
722
+ wakeAt: date(run.wakeAt),
723
+ lockedBy: run.lockedBy ?? null,
724
+ lockedUntil: date(run.lockedUntil),
725
+ awaitingDecisionTaskId: run.awaitingDecisionTaskId ?? null,
726
+ recoveryAttempts: run.recoveryAttempts ?? null,
727
+ tags: run.tags ?? null,
728
+ searchAttributes: run.searchAttributes ?? null,
729
+ priority: run.priority ?? null,
730
+ namespace: run.namespace ?? DEFAULT_NAMESPACE,
731
+ origin: run.origin ?? null,
732
+ createdAt: run.createdAt,
733
+ updatedAt: run.updatedAt
734
+ };
735
+ }
736
+ __name(toRunRow, "toRunRow");
737
+ function toRunPatch(patch) {
738
+ const row = {};
739
+ if (patch.workflow !== void 0) row.workflow = patch.workflow;
740
+ if (patch.workflowVersion !== void 0) row.workflowVersion = patch.workflowVersion;
741
+ if (patch.status !== void 0) row.status = patch.status;
742
+ if ("input" in patch) row.input = patch.input ?? null;
743
+ if ("output" in patch) row.output = patch.output ?? null;
744
+ if ("error" in patch) row.error = patch.error ?? null;
745
+ if ("wakeAt" in patch) row.wakeAt = date(patch.wakeAt);
746
+ if ("lockedBy" in patch) row.lockedBy = patch.lockedBy ?? null;
747
+ if ("lockedUntil" in patch) row.lockedUntil = date(patch.lockedUntil);
748
+ if ("awaitingDecisionTaskId" in patch) row.awaitingDecisionTaskId = patch.awaitingDecisionTaskId ?? null;
749
+ if ("recoveryAttempts" in patch) row.recoveryAttempts = patch.recoveryAttempts ?? null;
750
+ if ("tags" in patch) row.tags = patch.tags ?? null;
751
+ if ("searchAttributes" in patch) row.searchAttributes = patch.searchAttributes ?? null;
752
+ if ("priority" in patch) row.priority = patch.priority ?? null;
753
+ if ("namespace" in patch) row.namespace = patch.namespace ?? DEFAULT_NAMESPACE;
754
+ if ("origin" in patch) row.origin = patch.origin ?? null;
755
+ if (patch.createdAt != null) row.createdAt = patch.createdAt;
756
+ if (patch.updatedAt != null) row.updatedAt = patch.updatedAt;
757
+ return row;
758
+ }
759
+ __name(toRunPatch, "toRunPatch");
760
+ function fromRunRow(row) {
761
+ return {
762
+ id: row.id,
763
+ workflow: row.workflow,
764
+ workflowVersion: row.workflowVersion,
765
+ status: row.status,
766
+ input: row.input ?? void 0,
767
+ output: row.output ?? void 0,
768
+ error: row.error ?? void 0,
769
+ wakeAt: ms(row.wakeAt),
770
+ lockedBy: row.lockedBy ?? void 0,
771
+ lockedUntil: ms(row.lockedUntil),
772
+ awaitingDecisionTaskId: row.awaitingDecisionTaskId ?? void 0,
773
+ recoveryAttempts: row.recoveryAttempts ?? void 0,
774
+ tags: row.tags ?? void 0,
775
+ searchAttributes: row.searchAttributes ?? void 0,
776
+ priority: row.priority ?? void 0,
777
+ namespace: row.namespace,
778
+ origin: row.origin ?? void 0,
779
+ createdAt: row.createdAt,
780
+ updatedAt: row.updatedAt
781
+ };
782
+ }
783
+ __name(fromRunRow, "fromRunRow");
784
+ function toCheckpointRow(cp) {
785
+ return {
786
+ runId: cp.runId,
787
+ seq: cp.seq,
788
+ name: cp.name,
789
+ kind: cp.kind,
790
+ stepId: cp.stepId,
791
+ status: cp.status,
792
+ input: cp.input ?? null,
793
+ output: cp.output ?? null,
794
+ error: cp.error ?? null,
795
+ events: cp.events ?? null,
796
+ attempts: cp.attempts,
797
+ workerGroup: cp.workerGroup ?? null,
798
+ parallelGroup: cp.parallelGroup ?? null,
799
+ wakeAt: date(cp.wakeAt),
800
+ enqueuedAt: cp.enqueuedAt ?? cp.startedAt,
801
+ startedAt: cp.startedAt,
802
+ finishedAt: cp.finishedAt
803
+ };
804
+ }
805
+ __name(toCheckpointRow, "toCheckpointRow");
806
+ function fromCheckpointRow(row) {
807
+ return {
808
+ runId: row.runId,
809
+ seq: row.seq,
810
+ name: row.name,
811
+ kind: row.kind,
812
+ stepId: row.stepId,
813
+ status: row.status,
814
+ input: row.input ?? void 0,
815
+ output: row.output ?? void 0,
816
+ error: row.error ?? void 0,
817
+ events: row.events ?? void 0,
818
+ attempts: row.attempts,
819
+ workerGroup: row.workerGroup ?? void 0,
820
+ parallelGroup: row.parallelGroup ?? void 0,
821
+ wakeAt: ms(row.wakeAt),
822
+ // Older rows may lack enqueued_at; treat the start as enqueue time (queue-wait reads zero).
823
+ enqueuedAt: row.enqueuedAt ?? row.startedAt,
824
+ startedAt: row.startedAt,
825
+ finishedAt: row.finishedAt
826
+ };
827
+ }
828
+ __name(fromCheckpointRow, "fromCheckpointRow");
829
+ function toScheduleRow(r) {
830
+ return {
831
+ id: r.id,
832
+ namespace: r.namespace,
833
+ workflow: r.workflow,
834
+ paused: r.paused,
835
+ nextFireAt: r.nextFireAt == null ? null : new Date(r.nextFireAt),
836
+ tags: r.tags ?? null,
837
+ spec: r.spec,
838
+ state: r.state,
839
+ createdAt: r.createdAt,
840
+ updatedAt: r.updatedAt
841
+ };
842
+ }
843
+ __name(toScheduleRow, "toScheduleRow");
844
+ function toSchedulePatch(patch) {
845
+ const set = {};
846
+ if (patch.namespace !== void 0) set.namespace = patch.namespace;
847
+ if (patch.workflow !== void 0) set.workflow = patch.workflow;
848
+ if (patch.paused !== void 0) set.paused = patch.paused;
849
+ if ("nextFireAt" in patch) set.nextFireAt = patch.nextFireAt == null ? null : new Date(patch.nextFireAt);
850
+ if ("tags" in patch) set.tags = patch.tags ?? null;
851
+ if (patch.spec !== void 0) set.spec = patch.spec;
852
+ if (patch.state !== void 0) set.state = patch.state;
853
+ if (patch.updatedAt !== void 0) set.updatedAt = patch.updatedAt;
854
+ return set;
855
+ }
856
+ __name(toSchedulePatch, "toSchedulePatch");
857
+ function fromScheduleRow(row) {
858
+ return {
859
+ id: row.id,
860
+ namespace: row.namespace,
861
+ workflow: row.workflow,
862
+ paused: row.paused,
863
+ nextFireAt: row.nextFireAt ? row.nextFireAt.getTime() : null,
864
+ ...row.tags ? {
865
+ tags: row.tags
866
+ } : {},
867
+ spec: row.spec ?? {},
868
+ state: row.state ?? {},
869
+ createdAt: row.createdAt,
870
+ updatedAt: row.updatedAt
871
+ };
872
+ }
873
+ __name(fromScheduleRow, "fromScheduleRow");
874
+
875
+ // src/pg/executor.ts
876
+ var import_drizzle_orm4 = require("drizzle-orm");
877
+ function toDrizzleSql(text2, params = []) {
878
+ const chunks = [];
879
+ let last = 0;
880
+ for (const match of text2.matchAll(/\$(\d+)/g)) {
881
+ const index2 = Number(match[1]) - 1;
882
+ if (index2 < 0 || index2 >= params.length) throw new Error(`placeholder ${match[0]} has no bound parameter`);
883
+ chunks.push(import_drizzle_orm4.sql.raw(text2.slice(last, match.index)), import_drizzle_orm4.sql`${params[index2]}`);
884
+ last = (match.index ?? 0) + match[0].length;
885
+ }
886
+ chunks.push(import_drizzle_orm4.sql.raw(text2.slice(last)));
887
+ return import_drizzle_orm4.sql.join(chunks);
888
+ }
889
+ __name(toDrizzleSql, "toDrizzleSql");
890
+ function rowsOf(result) {
891
+ if (Array.isArray(result)) return result;
892
+ return result?.rows ?? [];
893
+ }
894
+ __name(rowsOf, "rowsOf");
895
+ function drizzlePgExecutor(db) {
896
+ return {
897
+ dialect: "postgres",
898
+ escapeId: /* @__PURE__ */ __name((id) => `"${id.replace(/"/g, '""')}"`, "escapeId"),
899
+ query: /* @__PURE__ */ __name(async (text2, params) => rowsOf(await db.execute(toDrizzleSql(text2, params))), "query"),
900
+ transaction: /* @__PURE__ */ __name((fn) => db.transaction((tx) => fn({
901
+ query: /* @__PURE__ */ __name(async (text2, params) => rowsOf(await tx.execute(toDrizzleSql(text2, params))), "query")
902
+ })), "transaction")
903
+ };
904
+ }
905
+ __name(drizzlePgExecutor, "drizzlePgExecutor");
906
+ // Annotate the CommonJS export names for ESM import in node:
907
+ 0 && (module.exports = {
908
+ DEFAULT_NAMESPACE,
909
+ DURABLE_PG_DDL,
910
+ DrizzlePgStateStore,
911
+ drizzlePgExecutor,
912
+ durableBufferedEvents,
913
+ durableBufferedSignals,
914
+ durablePgManagedTables,
915
+ durablePgSchema,
916
+ durableRunAttributes,
917
+ durableSchedules,
918
+ durableSignalWaiters,
919
+ durableStepCheckpoints,
920
+ durableWorkflowRuns,
921
+ ensureDrizzlePgDurableSchema,
922
+ toDrizzleSql
923
+ });
924
+ //# sourceMappingURL=index.cjs.map