@dudousxd/nestjs-durable-store-drizzle 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/pg/index.cjs CHANGED
@@ -35,6 +35,7 @@ __export(pg_exports, {
35
35
  durableStepCheckpoints: () => durableStepCheckpoints,
36
36
  durableWorkflowRuns: () => durableWorkflowRuns,
37
37
  ensureDrizzlePgDurableSchema: () => ensureDrizzlePgDurableSchema,
38
+ rowsOf: () => rowsOf,
38
39
  toDrizzleSql: () => toDrizzleSql
39
40
  });
40
41
  module.exports = __toCommonJS(pg_exports);
@@ -124,6 +125,7 @@ var DURABLE_PG_DDL = [
124
125
  `CREATE INDEX IF NOT EXISTS "durable_workflow_runs_namespace_status_idx" ON "durable_workflow_runs" USING btree ("namespace","status","created_at")`,
125
126
  `CREATE INDEX IF NOT EXISTS "durable_runs_tags_gin_idx" ON "durable_workflow_runs" USING gin ("tags" jsonb_path_ops)`,
126
127
  `CREATE INDEX IF NOT EXISTS "durable_runs_status_updated_idx" ON "durable_workflow_runs" USING btree ("status","updated_at")`,
128
+ `CREATE INDEX IF NOT EXISTS "durable_step_checkpoints_group_idx" ON "durable_step_checkpoints" USING btree ("run_id","parallel_group","seq")`,
127
129
  `CREATE INDEX IF NOT EXISTS "durable_run_attributes_num_idx" ON "durable_run_attributes" USING btree ("key","num_value")`,
128
130
  `CREATE INDEX IF NOT EXISTS "durable_run_attributes_str_idx" ON "durable_run_attributes" USING btree ("key","str_value")`,
129
131
  `CREATE INDEX IF NOT EXISTS "durable_signal_waiters_run_id_idx" ON "durable_signal_waiters" USING btree ("run_id")`,
@@ -142,10 +144,42 @@ __name(ensureDrizzlePgDurableSchema, "ensureDrizzlePgDurableSchema");
142
144
 
143
145
  // src/pg/drizzle-pg-state-store.ts
144
146
  var import_nestjs_durable_core = require("@dudousxd/nestjs-durable-core");
145
- var import_drizzle_orm3 = require("drizzle-orm");
147
+ var import_drizzle_orm4 = require("drizzle-orm");
146
148
 
147
- // src/pg/schema.ts
149
+ // src/pg/executor.ts
148
150
  var import_drizzle_orm2 = require("drizzle-orm");
151
+ function toDrizzleSql(text2, params = []) {
152
+ const chunks = [];
153
+ let last = 0;
154
+ for (const match of text2.matchAll(/\$(\d+)/g)) {
155
+ const index2 = Number(match[1]) - 1;
156
+ if (index2 < 0 || index2 >= params.length) throw new Error(`placeholder ${match[0]} has no bound parameter`);
157
+ chunks.push(import_drizzle_orm2.sql.raw(text2.slice(last, match.index)), import_drizzle_orm2.sql`${params[index2]}`);
158
+ last = (match.index ?? 0) + match[0].length;
159
+ }
160
+ chunks.push(import_drizzle_orm2.sql.raw(text2.slice(last)));
161
+ return import_drizzle_orm2.sql.join(chunks);
162
+ }
163
+ __name(toDrizzleSql, "toDrizzleSql");
164
+ function rowsOf(result) {
165
+ if (Array.isArray(result)) return result;
166
+ return result?.rows ?? [];
167
+ }
168
+ __name(rowsOf, "rowsOf");
169
+ function drizzlePgExecutor(db) {
170
+ return {
171
+ dialect: "postgres",
172
+ escapeId: /* @__PURE__ */ __name((id) => `"${id.replace(/"/g, '""')}"`, "escapeId"),
173
+ query: /* @__PURE__ */ __name(async (text2, params) => rowsOf(await db.execute(toDrizzleSql(text2, params))), "query"),
174
+ transaction: /* @__PURE__ */ __name((fn) => db.transaction((tx) => fn({
175
+ query: /* @__PURE__ */ __name(async (text2, params) => rowsOf(await tx.execute(toDrizzleSql(text2, params))), "query")
176
+ })), "transaction")
177
+ };
178
+ }
179
+ __name(drizzlePgExecutor, "drizzlePgExecutor");
180
+
181
+ // src/pg/schema.ts
182
+ var import_drizzle_orm3 = require("drizzle-orm");
149
183
  var import_pg_core = require("drizzle-orm/pg-core");
150
184
  var DEFAULT_NAMESPACE = "default";
151
185
  var tstz = /* @__PURE__ */ __name((name) => (0, import_pg_core.timestamp)(name, {
@@ -220,7 +254,9 @@ var durableStepCheckpoints = (0, import_pg_core.pgTable)(
220
254
  t.runId,
221
255
  t.seq
222
256
  ]
223
- })
257
+ }),
258
+ // A run detail reads one fan at a time: checkpointStats / queryCheckpoints by group, seq-paged.
259
+ (0, import_pg_core.index)("durable_step_checkpoints_group_idx").on(t.runId, t.parallelGroup, t.seq)
224
260
  ]
225
261
  );
226
262
  var durableRunAttributes = (0, import_pg_core.pgTable)("durable_run_attributes", {
@@ -302,22 +338,22 @@ var durablePgSchema = {
302
338
  durableSchedules
303
339
  };
304
340
  function durablePgManagedTables() {
305
- return Object.values(durablePgSchema).map((table) => (0, import_drizzle_orm2.getTableName)(table));
341
+ return Object.values(durablePgSchema).map((table) => (0, import_drizzle_orm3.getTableName)(table));
306
342
  }
307
343
  __name(durablePgManagedTables, "durablePgManagedTables");
308
344
 
309
345
  // src/pg/drizzle-pg-state-store.ts
310
346
  function anyOf(conditions) {
311
347
  const present = conditions.filter((c) => c !== void 0);
312
- return present.length ? (0, import_drizzle_orm3.or)(...present) : import_drizzle_orm3.sql`1 = 0`;
348
+ return present.length ? (0, import_drizzle_orm4.or)(...present) : import_drizzle_orm4.sql`1 = 0`;
313
349
  }
314
350
  __name(anyOf, "anyOf");
315
351
  function namespaceFilter(namespace) {
316
- return namespace === void 0 ? void 0 : (0, import_drizzle_orm3.eq)(durableWorkflowRuns.namespace, namespace);
352
+ return namespace === void 0 ? void 0 : (0, import_drizzle_orm4.eq)(durableWorkflowRuns.namespace, namespace);
317
353
  }
318
354
  __name(namespaceFilter, "namespaceFilter");
319
355
  function hasTag(tag) {
320
- return import_drizzle_orm3.sql`${durableWorkflowRuns.tags} @> ${JSON.stringify([
356
+ return import_drizzle_orm4.sql`${durableWorkflowRuns.tags} @> ${JSON.stringify([
321
357
  tag
322
358
  ])}::jsonb`;
323
359
  }
@@ -355,7 +391,7 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
355
391
  }
356
392
  async tryAdmitSingleton(runId, tag, workflow, limit, retryWakeAt) {
357
393
  return this.db.transaction(async (tx) => {
358
- const where = (0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableWorkflowRuns.workflow, workflow), (0, import_drizzle_orm3.inArray)(durableWorkflowRuns.status, import_nestjs_durable_core.SINGLETON_ACTIVE_STATUSES), this.scope());
394
+ const where = (0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableWorkflowRuns.workflow, workflow), (0, import_drizzle_orm4.inArray)(durableWorkflowRuns.status, import_nestjs_durable_core.SINGLETON_ACTIVE_STATUSES), this.scope());
359
395
  await tx.update(durableWorkflowRuns).set({
360
396
  workflow
361
397
  }).where(where);
@@ -364,12 +400,12 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
364
400
  const tags = (0, import_nestjs_durable_core.singletonAdmissionTags)(runs, runId, tag, workflow, limit);
365
401
  if (!tags) {
366
402
  const patch = (0, import_nestjs_durable_core.singletonAdmissionDenialPatch)(runs, runId, tag, workflow, retryWakeAt);
367
- if (patch) await tx.update(durableWorkflowRuns).set(toRunPatch(patch)).where((0, import_drizzle_orm3.eq)(durableWorkflowRuns.id, runId));
403
+ if (patch) await tx.update(durableWorkflowRuns).set(toRunPatch(patch)).where((0, import_drizzle_orm4.eq)(durableWorkflowRuns.id, runId));
368
404
  return false;
369
405
  }
370
406
  await tx.update(durableWorkflowRuns).set({
371
407
  tags
372
- }).where((0, import_drizzle_orm3.eq)(durableWorkflowRuns.id, runId));
408
+ }).where((0, import_drizzle_orm4.eq)(durableWorkflowRuns.id, runId));
373
409
  return true;
374
410
  });
375
411
  }
@@ -385,12 +421,28 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
385
421
  const reindex = "searchAttributes" in patch;
386
422
  if (!Object.keys(row).length && !reindex) return;
387
423
  await this.db.transaction(async (tx) => {
388
- if (Object.keys(row).length) await tx.update(durableWorkflowRuns).set(row).where((0, import_drizzle_orm3.eq)(durableWorkflowRuns.id, runId));
424
+ if (Object.keys(row).length) await tx.update(durableWorkflowRuns).set(row).where((0, import_drizzle_orm4.eq)(durableWorkflowRuns.id, runId));
389
425
  if (reindex) await reindexAttributes(tx, runId, patch.searchAttributes);
390
426
  });
391
427
  }
428
+ async updateRunIf(runId, expectedStatuses, inputPatch) {
429
+ if (expectedStatuses.length === 0) return false;
430
+ const patch = inputPatch.status && import_nestjs_durable_core.TERMINAL_RUN_STATUSES.includes(inputPatch.status) ? await (0, import_nestjs_durable_core.clearTerminalSingletonAdmission)(runId, inputPatch, (id) => this.withScope({}).getRun(id)) : inputPatch;
431
+ const row = toRunPatch(patch);
432
+ const reindex = "searchAttributes" in patch;
433
+ const matches = (0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableWorkflowRuns.id, runId), (0, import_drizzle_orm4.inArray)(durableWorkflowRuns.status, expectedStatuses));
434
+ return this.db.transaction(async (tx) => {
435
+ const applied = Object.keys(row).length ? (await tx.update(durableWorkflowRuns).set(row).where(matches).returning({
436
+ id: durableWorkflowRuns.id
437
+ })).length === 1 : (await tx.select({
438
+ id: durableWorkflowRuns.id
439
+ }).from(durableWorkflowRuns).where(matches).for("update")).length === 1;
440
+ if (applied && reindex) await reindexAttributes(tx, runId, patch.searchAttributes);
441
+ return applied;
442
+ });
443
+ }
392
444
  async getRun(runId) {
393
- 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);
445
+ const rows = await this.db.select().from(durableWorkflowRuns).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableWorkflowRuns.id, runId), this.scope())).limit(1);
394
446
  return rows[0] ? fromRunRow(rows[0]) : null;
395
447
  }
396
448
  async deleteRun(runId) {
@@ -420,7 +472,7 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
420
472
  const cutoff = new Date(nowMs - (0, import_nestjs_durable_core.parseDuration)(policy.maxAge));
421
473
  const rows = await tx.select({
422
474
  id: durableWorkflowRuns.id
423
- }).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", {
475
+ }).from(durableWorkflowRuns).where((0, import_drizzle_orm4.and)(...scoped, (0, import_drizzle_orm4.lt)(durableWorkflowRuns.updatedAt, cutoff))).orderBy((0, import_drizzle_orm4.asc)(durableWorkflowRuns.updatedAt)).limit(limit).for("update", {
424
476
  skipLocked: true
425
477
  });
426
478
  for (const r of rows) ids.add(r.id);
@@ -428,7 +480,7 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
428
480
  if (policy.maxCount != null && ids.size < limit) {
429
481
  const rows = await tx.select({
430
482
  id: durableWorkflowRuns.id
431
- }).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", {
483
+ }).from(durableWorkflowRuns).where((0, import_drizzle_orm4.and)(...scoped)).orderBy((0, import_drizzle_orm4.desc)(durableWorkflowRuns.updatedAt), (0, import_drizzle_orm4.desc)(durableWorkflowRuns.id)).limit(limit).offset(policy.maxCount).for("update", {
432
484
  skipLocked: true
433
485
  });
434
486
  for (const r of rows) {
@@ -445,7 +497,7 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
445
497
  });
446
498
  }
447
499
  async getCheckpoint(runId, seq) {
448
- 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);
500
+ const rows = await this.db.select().from(durableStepCheckpoints).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableStepCheckpoints.runId, runId), (0, import_drizzle_orm4.eq)(durableStepCheckpoints.seq, seq))).limit(1);
449
501
  return rows[0] ? fromCheckpointRow(rows[0]) : null;
450
502
  }
451
503
  async saveCheckpoint(checkpoint) {
@@ -453,11 +505,35 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
453
505
  }
454
506
  async saveCheckpointIfPending(checkpoint) {
455
507
  const { runId, seq, ...set } = toCheckpointRow(checkpoint);
456
- const rows = await this.db.update(durableStepCheckpoints).set(set).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableStepCheckpoints.runId, runId), (0, import_drizzle_orm3.eq)(durableStepCheckpoints.seq, seq), (0, import_drizzle_orm3.eq)(durableStepCheckpoints.status, "pending"))).returning({
508
+ const rows = await this.db.update(durableStepCheckpoints).set(set).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableStepCheckpoints.runId, runId), (0, import_drizzle_orm4.eq)(durableStepCheckpoints.seq, seq), (0, import_drizzle_orm4.eq)(durableStepCheckpoints.status, "pending"))).returning({
457
509
  seq: durableStepCheckpoints.seq
458
510
  });
459
511
  return rows.length === 1;
460
512
  }
513
+ async saveCheckpointsIfPending(checkpoints) {
514
+ const wrote = checkpoints.map(() => false);
515
+ if (checkpoints.length === 0) return wrote;
516
+ await this.db.transaction(async (tx) => {
517
+ for (const round of distinctRounds(checkpoints)) {
518
+ for (let at = 0; at < round.length; at += CHECKPOINT_BATCH_ROWS) {
519
+ const chunk = round.slice(at, at + CHECKPOINT_BATCH_ROWS);
520
+ const updated = rowsOf(await tx.execute(updateIfPendingFrom(chunk.map((i) => checkpoints[i]))));
521
+ const byKey = new Map(chunk.map((i) => [
522
+ rowKey(checkpoints[i]),
523
+ i
524
+ ]));
525
+ for (const row of updated) {
526
+ const i = byKey.get(rowKey({
527
+ runId: row.run_id,
528
+ seq: Number(row.seq)
529
+ }));
530
+ if (i !== void 0) wrote[i] = true;
531
+ }
532
+ }
533
+ }
534
+ });
535
+ return wrote;
536
+ }
461
537
  async transaction(work) {
462
538
  return this.db.transaction(async (tx) => work({
463
539
  raw: tx,
@@ -466,20 +542,28 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
466
542
  }
467
543
  /** Crash recovery. Namespace-scoped: a worker re-drives only its own tenant's runs. */
468
544
  async listIncompleteRuns(namespace) {
469
- const rows = await this.db.select().from(durableWorkflowRuns).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.inArray)(durableWorkflowRuns.status, [
545
+ const rows = await this.db.select().from(durableWorkflowRuns).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.inArray)(durableWorkflowRuns.status, [
470
546
  "running",
471
547
  "cancelling"
472
548
  ]), namespaceFilter(namespace), this.scope()));
473
549
  return rows.map(fromRunRow);
474
550
  }
551
+ async listOrphanedRuns(nowMs, limit, namespace) {
552
+ if (limit <= 0) return [];
553
+ const rows = await this.db.select().from(durableWorkflowRuns).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.inArray)(durableWorkflowRuns.status, [
554
+ "running",
555
+ "cancelling"
556
+ ]), (0, import_drizzle_orm4.or)((0, import_drizzle_orm4.isNull)(durableWorkflowRuns.lockedUntil), (0, import_drizzle_orm4.lte)(durableWorkflowRuns.lockedUntil, new Date(nowMs))), namespaceFilter(namespace), this.scope())).orderBy((0, import_drizzle_orm4.asc)(durableWorkflowRuns.createdAt)).limit(limit);
557
+ return rows.map(fromRunRow);
558
+ }
475
559
  /** FIFO pick-up, served by `durable_workflow_runs_namespace_status_idx`. The engine then leases
476
560
  * each run with {@link tryLockRun}; the listing itself takes no locks (it runs outside a tx). */
477
561
  async listPendingRuns(limit, namespace) {
478
- 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);
562
+ const rows = await this.db.select().from(durableWorkflowRuns).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableWorkflowRuns.status, "pending"), namespaceFilter(namespace), this.scope())).orderBy((0, import_drizzle_orm4.asc)(durableWorkflowRuns.createdAt)).limit(limit);
479
563
  return rows.map(fromRunRow);
480
564
  }
481
565
  async listDueTimers(nowMs, namespace, limit) {
482
- const query = 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())).orderBy((0, import_drizzle_orm3.asc)(durableWorkflowRuns.wakeAt));
566
+ const query = this.db.select().from(durableWorkflowRuns).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableWorkflowRuns.status, "suspended"), (0, import_drizzle_orm4.isNotNull)(durableWorkflowRuns.wakeAt), (0, import_drizzle_orm4.lte)(durableWorkflowRuns.wakeAt, new Date(nowMs)), namespaceFilter(namespace), this.scope())).orderBy((0, import_drizzle_orm4.asc)(durableWorkflowRuns.wakeAt));
483
567
  const rows = limit !== void 0 ? await query.limit(limit) : await query;
484
568
  return rows.map(fromRunRow);
485
569
  }
@@ -487,7 +571,7 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
487
571
  const rows = await this.db.update(durableWorkflowRuns).set({
488
572
  lockedBy: owner,
489
573
  lockedUntil: new Date(leaseUntilMs)
490
- }).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({
574
+ }).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableWorkflowRuns.id, runId), (0, import_drizzle_orm4.or)((0, import_drizzle_orm4.isNull)(durableWorkflowRuns.lockedUntil), (0, import_drizzle_orm4.lte)(durableWorkflowRuns.lockedUntil, new Date(nowMs))))).returning({
491
575
  id: durableWorkflowRuns.id
492
576
  });
493
577
  return rows.length === 1;
@@ -496,12 +580,12 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
496
580
  await this.db.update(durableWorkflowRuns).set({
497
581
  lockedBy: null,
498
582
  lockedUntil: null
499
- }).where((0, import_drizzle_orm3.eq)(durableWorkflowRuns.id, runId));
583
+ }).where((0, import_drizzle_orm4.eq)(durableWorkflowRuns.id, runId));
500
584
  }
501
585
  async renewRunLock(runId, owner, leaseUntilMs) {
502
586
  const rows = await this.db.update(durableWorkflowRuns).set({
503
587
  lockedUntil: new Date(leaseUntilMs)
504
- }).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableWorkflowRuns.id, runId), (0, import_drizzle_orm3.eq)(durableWorkflowRuns.lockedBy, owner))).returning({
588
+ }).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableWorkflowRuns.id, runId), (0, import_drizzle_orm4.eq)(durableWorkflowRuns.lockedBy, owner))).returning({
505
589
  id: durableWorkflowRuns.id
506
590
  });
507
591
  return rows.length === 1;
@@ -510,17 +594,17 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
510
594
  runFilters(query) {
511
595
  return [
512
596
  this.scope(),
513
- query.workflow ? (0, import_drizzle_orm3.eq)(durableWorkflowRuns.workflow, query.workflow) : void 0,
514
- query.workflows ? query.workflows.length ? (0, import_drizzle_orm3.inArray)(durableWorkflowRuns.workflow, query.workflows) : import_drizzle_orm3.sql`1 = 0` : void 0,
597
+ query.workflow ? (0, import_drizzle_orm4.eq)(durableWorkflowRuns.workflow, query.workflow) : void 0,
598
+ query.workflows ? query.workflows.length ? (0, import_drizzle_orm4.inArray)(durableWorkflowRuns.workflow, query.workflows) : import_drizzle_orm4.sql`1 = 0` : void 0,
515
599
  // The tenant boundary. Also the engine's execution-timeout sweep path
516
600
  // (`listRuns({ workflow, status, namespace })`) — unscoped, it would cancel another tenant's runs.
517
601
  namespaceFilter(query.namespace),
518
- query.namespaces ? query.namespaces.length ? (0, import_drizzle_orm3.inArray)(durableWorkflowRuns.namespace, query.namespaces) : import_drizzle_orm3.sql`1 = 0` : void 0,
602
+ query.namespaces ? query.namespaces.length ? (0, import_drizzle_orm4.inArray)(durableWorkflowRuns.namespace, query.namespaces) : import_drizzle_orm4.sql`1 = 0` : void 0,
519
603
  // `null` selects the unattributed bucket; a value never matches a NULL origin.
520
- 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,
521
- 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,
522
- query.status ? (0, import_drizzle_orm3.eq)(durableWorkflowRuns.status, query.status) : void 0,
523
- query.statuses ? query.statuses.length ? (0, import_drizzle_orm3.inArray)(durableWorkflowRuns.status, query.statuses) : import_drizzle_orm3.sql`1 = 0` : void 0,
604
+ query.origin === null ? (0, import_drizzle_orm4.isNull)(durableWorkflowRuns.origin) : query.origin !== void 0 ? (0, import_drizzle_orm4.eq)(durableWorkflowRuns.origin, query.origin) : void 0,
605
+ query.origins ? anyOf(query.origins.map((o) => o === null ? (0, import_drizzle_orm4.isNull)(durableWorkflowRuns.origin) : (0, import_drizzle_orm4.eq)(durableWorkflowRuns.origin, o))) : void 0,
606
+ query.status ? (0, import_drizzle_orm4.eq)(durableWorkflowRuns.status, query.status) : void 0,
607
+ query.statuses ? query.statuses.length ? (0, import_drizzle_orm4.inArray)(durableWorkflowRuns.status, query.statuses) : import_drizzle_orm4.sql`1 = 0` : void 0,
524
608
  query.tag ? hasTag(query.tag) : void 0,
525
609
  query.tags ? anyOf(query.tags.map(hasTag)) : void 0,
526
610
  // One EXISTS per attribute predicate against the normalized side table (ANDed).
@@ -529,7 +613,7 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
529
613
  }
530
614
  async listRuns(query) {
531
615
  const filters = this.runFilters(query);
532
- 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();
616
+ const base = this.db.select().from(durableWorkflowRuns).where(filters.length ? (0, import_drizzle_orm4.and)(...filters) : void 0).orderBy((0, import_drizzle_orm4.desc)(durableWorkflowRuns.createdAt), (0, import_drizzle_orm4.desc)(durableWorkflowRuns.id)).$dynamic();
533
617
  if (query.limit != null) base.limit(query.limit);
534
618
  if (query.offset != null) base.offset(query.offset);
535
619
  const rows = await base;
@@ -538,8 +622,8 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
538
622
  async countRuns(query) {
539
623
  const filters = this.runFilters(query);
540
624
  const rows = await this.db.select({
541
- count: import_drizzle_orm3.sql`count(*)`.mapWith(Number)
542
- }).from(durableWorkflowRuns).where(filters.length ? (0, import_drizzle_orm3.and)(...filters) : void 0);
625
+ count: import_drizzle_orm4.sql`count(*)`.mapWith(Number)
626
+ }).from(durableWorkflowRuns).where(filters.length ? (0, import_drizzle_orm4.and)(...filters) : void 0);
543
627
  return rows[0]?.count ?? 0;
544
628
  }
545
629
  async runFacets(query) {
@@ -547,8 +631,8 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
547
631
  const rows = await this.db.select({
548
632
  status: durableWorkflowRuns.status,
549
633
  origin: durableWorkflowRuns.origin,
550
- count: import_drizzle_orm3.sql`count(*)`.mapWith(Number)
551
- }).from(durableWorkflowRuns).where(filters.length ? (0, import_drizzle_orm3.and)(...filters) : void 0).groupBy(durableWorkflowRuns.status, durableWorkflowRuns.origin);
634
+ count: import_drizzle_orm4.sql`count(*)`.mapWith(Number)
635
+ }).from(durableWorkflowRuns).where(filters.length ? (0, import_drizzle_orm4.and)(...filters) : void 0).groupBy(durableWorkflowRuns.status, durableWorkflowRuns.origin);
552
636
  return (0, import_nestjs_durable_core.mergeRunFacetRows)(rows.map((r) => ({
553
637
  status: r.status,
554
638
  origin: r.origin,
@@ -569,8 +653,8 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
569
653
  const filters = this.runFilters(query);
570
654
  const rows = await this.db.select({
571
655
  value: column,
572
- count: import_drizzle_orm3.sql`count(*)`.mapWith(Number)
573
- }).from(durableWorkflowRuns).where(filters.length ? (0, import_drizzle_orm3.and)(...filters) : void 0).groupBy(column);
656
+ count: import_drizzle_orm4.sql`count(*)`.mapWith(Number)
657
+ }).from(durableWorkflowRuns).where(filters.length ? (0, import_drizzle_orm4.and)(...filters) : void 0).groupBy(column);
574
658
  return (0, import_nestjs_durable_core.mergeRunValueFacetRows)(rows.map((r) => ({
575
659
  value: r.value ?? null,
576
660
  count: r.count
@@ -580,10 +664,10 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
580
664
  * excludes runs lacking the key (EXISTS requires the row). Numbers compare `num_value`, strings
581
665
  * and booleans `str_value`; an `in` set ORs its members inside the one EXISTS. */
582
666
  attributeExists(f) {
583
- 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}`);
584
- return (0, import_drizzle_orm3.exists)(this.db.select({
585
- one: import_drizzle_orm3.sql`1`
586
- }).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))));
667
+ const comparisons = (0, import_nestjs_durable_core.attributePredicateOperands)(f).map(({ column, comparator, operand }) => import_drizzle_orm4.sql`${column === "numValue" ? durableRunAttributes.numValue : durableRunAttributes.strValue} ${import_drizzle_orm4.sql.raw(comparator)} ${operand}`);
668
+ return (0, import_drizzle_orm4.exists)(this.db.select({
669
+ one: import_drizzle_orm4.sql`1`
670
+ }).from(durableRunAttributes).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableRunAttributes.runId, durableWorkflowRuns.id), (0, import_drizzle_orm4.eq)(durableRunAttributes.key, f.key), anyOf(comparisons))));
587
671
  }
588
672
  // ---- persisted schedules ----------------------------------------------------------------------
589
673
  async saveSchedule(record) {
@@ -594,52 +678,99 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
594
678
  });
595
679
  }
596
680
  async getSchedule(id) {
597
- const rows = await this.db.select().from(durableSchedules).where((0, import_drizzle_orm3.eq)(durableSchedules.id, id)).limit(1);
681
+ const rows = await this.db.select().from(durableSchedules).where((0, import_drizzle_orm4.eq)(durableSchedules.id, id)).limit(1);
598
682
  return rows[0] ? fromScheduleRow(rows[0]) : null;
599
683
  }
600
684
  /** A single conditional `UPDATE … RETURNING`: the compare-and-set on `next_fire_at` is atomic. */
601
685
  async updateSchedule(id, patch, expectedNextFireAt) {
602
686
  const set = toSchedulePatch(patch);
603
687
  if (!Object.keys(set).length) return await this.getSchedule(id) !== null;
604
- 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({
688
+ const rows = await this.db.update(durableSchedules).set(set).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableSchedules.id, id), expectedNextFireAt === void 0 ? void 0 : expectedNextFireAt === null ? (0, import_drizzle_orm4.isNull)(durableSchedules.nextFireAt) : (0, import_drizzle_orm4.eq)(durableSchedules.nextFireAt, new Date(expectedNextFireAt)))).returning({
605
689
  id: durableSchedules.id
606
690
  });
607
691
  return rows.length > 0;
608
692
  }
609
693
  async deleteSchedule(id) {
610
- const rows = await this.db.delete(durableSchedules).where((0, import_drizzle_orm3.eq)(durableSchedules.id, id)).returning({
694
+ const rows = await this.db.delete(durableSchedules).where((0, import_drizzle_orm4.eq)(durableSchedules.id, id)).returning({
611
695
  id: durableSchedules.id
612
696
  });
613
697
  return rows.length > 0;
614
698
  }
615
699
  async listSchedules(query) {
616
- 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([
700
+ const base = this.db.select().from(durableSchedules).where((0, import_drizzle_orm4.and)(query.namespace !== void 0 ? (0, import_drizzle_orm4.eq)(durableSchedules.namespace, query.namespace) : void 0, query.workflow !== void 0 ? (0, import_drizzle_orm4.eq)(durableSchedules.workflow, query.workflow) : void 0, query.paused !== void 0 ? (0, import_drizzle_orm4.eq)(durableSchedules.paused, query.paused) : void 0, query.tag !== void 0 ? import_drizzle_orm4.sql`${durableSchedules.tags} @> ${JSON.stringify([
617
701
  query.tag
618
- ])}::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();
702
+ ])}::jsonb` : void 0, query.dueBy !== void 0 ? (0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableSchedules.paused, false), (0, import_drizzle_orm4.lte)(durableSchedules.nextFireAt, new Date(query.dueBy))) : void 0)).orderBy(import_drizzle_orm4.sql`${durableSchedules.nextFireAt} ASC NULLS LAST`, (0, import_drizzle_orm4.asc)(durableSchedules.id)).$dynamic();
619
703
  if (query.limit != null) base.limit(query.limit);
620
704
  if (query.offset != null) base.offset(query.offset);
621
705
  return (await base).map(fromScheduleRow);
622
706
  }
623
707
  async listCheckpoints(runId) {
624
- 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));
708
+ const rows = await this.db.select().from(durableStepCheckpoints).where((0, import_drizzle_orm4.eq)(durableStepCheckpoints.runId, runId)).orderBy((0, import_drizzle_orm4.asc)(durableStepCheckpoints.seq));
625
709
  return rows.map(fromCheckpointRow);
626
710
  }
627
711
  async getLatestCheckpointByName(runId, name) {
628
- 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);
712
+ const rows = await this.db.select().from(durableStepCheckpoints).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableStepCheckpoints.runId, runId), (0, import_drizzle_orm4.eq)(durableStepCheckpoints.name, name))).orderBy((0, import_drizzle_orm4.desc)(durableStepCheckpoints.seq)).limit(1);
629
713
  return rows[0] ? fromCheckpointRow(rows[0]) : void 0;
630
714
  }
631
715
  async countCheckpoints(runId, fromSeq, toSeq, statuses) {
632
716
  if (statuses.length === 0) return 0;
633
717
  const rows = await this.db.select({
634
- count: import_drizzle_orm3.sql`count(*)`.mapWith(Number)
635
- }).from(durableStepCheckpoints).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableStepCheckpoints.runId, runId), (0, import_drizzle_orm3.gte)(durableStepCheckpoints.seq, fromSeq), (0, import_drizzle_orm3.lte)(durableStepCheckpoints.seq, toSeq), (0, import_drizzle_orm3.inArray)(durableStepCheckpoints.status, statuses)));
718
+ count: import_drizzle_orm4.sql`count(*)`.mapWith(Number)
719
+ }).from(durableStepCheckpoints).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableStepCheckpoints.runId, runId), (0, import_drizzle_orm4.gte)(durableStepCheckpoints.seq, fromSeq), (0, import_drizzle_orm4.lte)(durableStepCheckpoints.seq, toSeq), (0, import_drizzle_orm4.inArray)(durableStepCheckpoints.status, statuses)));
636
720
  return rows[0]?.count ?? 0;
637
721
  }
638
722
  async listCheckpointsByNamePrefix(runId, prefixes) {
639
723
  if (prefixes.length === 0) return [];
640
- 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));
724
+ const rows = await this.db.select().from(durableStepCheckpoints).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableStepCheckpoints.runId, runId), (0, import_drizzle_orm4.or)(...prefixes.map((p) => (0, import_drizzle_orm4.like)(durableStepCheckpoints.name, `${escapeLike(p)}%`))))).orderBy((0, import_drizzle_orm4.asc)(durableStepCheckpoints.seq));
641
725
  return rows.map(fromCheckpointRow);
642
726
  }
727
+ /** {@link StateStore.checkpointStats}: one `GROUP BY parallel_group, status, name_kind` over the
728
+ * run's checkpoints, so a wide fan comes back as a few bucket rows instead of every member. */
729
+ async checkpointStats(runId, opts) {
730
+ const c = durableStepCheckpoints;
731
+ const nameKind = import_drizzle_orm4.sql`CASE
732
+ WHEN ${c.name} LIKE 'signal:child:%' THEN 'child'
733
+ WHEN ${c.name} LIKE 'fan:%' THEN 'folded'
734
+ WHEN ${c.name} LIKE 'spawn:%' THEN 'spawn'
735
+ ELSE 'step' END`.as("name_kind");
736
+ const rows = await this.db.select({
737
+ group: c.parallelGroup,
738
+ status: c.status,
739
+ nameKind,
740
+ count: import_drizzle_orm4.sql`count(*)`.mapWith(Number),
741
+ minSeq: import_drizzle_orm4.sql`min(${c.seq})`.mapWith(Number),
742
+ maxSeq: import_drizzle_orm4.sql`max(${c.seq})`.mapWith(Number),
743
+ minName: import_drizzle_orm4.sql`min(${c.name})`,
744
+ maxName: import_drizzle_orm4.sql`max(${c.name})`,
745
+ minEnqueuedAt: import_drizzle_orm4.sql`min(coalesce(${c.enqueuedAt}, ${c.startedAt}))`.mapWith(c.startedAt),
746
+ minStartedAt: import_drizzle_orm4.sql`min(${c.startedAt})`.mapWith(c.startedAt),
747
+ maxStartedAt: import_drizzle_orm4.sql`max(${c.startedAt})`.mapWith(c.startedAt),
748
+ maxFinishedAt: import_drizzle_orm4.sql`max(${c.finishedAt})`.mapWith(c.finishedAt),
749
+ sumAttempts: import_drizzle_orm4.sql`sum(${c.attempts})`.mapWith(Number)
750
+ }).from(c).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(c.runId, runId), opts?.group !== void 0 ? (0, import_drizzle_orm4.eq)(c.parallelGroup, opts.group) : void 0)).groupBy(c.parallelGroup, c.status, import_drizzle_orm4.sql`name_kind`);
751
+ return rows.map((r) => ({
752
+ ...r,
753
+ group: r.group ?? null,
754
+ status: r.status
755
+ }));
756
+ }
757
+ /** {@link StateStore.queryCheckpoints}: the run's checkpoints narrowed in SQL, in `seq` order and
758
+ * paged there, so reading one page of a wide fan never loads the rest of it. */
759
+ async queryCheckpoints(runId, query) {
760
+ if (query.statuses?.length === 0 || query.namePrefixes?.length === 0) return [];
761
+ const c = durableStepCheckpoints;
762
+ const base = this.db.select().from(c).where((0, import_drizzle_orm4.and)(
763
+ (0, import_drizzle_orm4.eq)(c.runId, runId),
764
+ query.group !== void 0 ? (0, import_drizzle_orm4.eq)(c.parallelGroup, query.group) : void 0,
765
+ // `NOT IN` is never true for a NULL group, so ungrouped rows are kept explicitly.
766
+ query.excludeGroups?.length ? (0, import_drizzle_orm4.or)((0, import_drizzle_orm4.isNull)(c.parallelGroup), (0, import_drizzle_orm4.notInArray)(c.parallelGroup, query.excludeGroups)) : void 0,
767
+ query.statuses ? (0, import_drizzle_orm4.inArray)(c.status, query.statuses) : void 0,
768
+ query.namePrefixes ? (0, import_drizzle_orm4.or)(...query.namePrefixes.map((p) => (0, import_drizzle_orm4.like)(c.name, `${escapeLike(p)}%`))) : void 0
769
+ )).orderBy((0, import_drizzle_orm4.asc)(c.seq)).$dynamic();
770
+ if (query.limit != null) base.limit(query.limit);
771
+ if (query.offset != null) base.offset(query.offset);
772
+ return (await base).map(fromCheckpointRow);
773
+ }
643
774
  async putSignalWaiter(waiter) {
644
775
  const set = {
645
776
  runId: waiter.runId,
@@ -655,12 +786,12 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
655
786
  });
656
787
  }
657
788
  async takeSignalWaiter(token) {
658
- const rows = await this.db.delete(durableSignalWaiters).where((0, import_drizzle_orm3.eq)(durableSignalWaiters.token, token)).returning();
789
+ const rows = await this.db.delete(durableSignalWaiters).where((0, import_drizzle_orm4.eq)(durableSignalWaiters.token, token)).returning();
659
790
  const row = rows[0];
660
791
  return row ? fromWaiterRow(row) : null;
661
792
  }
662
793
  async listSignalWaiters(prefix) {
663
- const rows = await this.db.select().from(durableSignalWaiters).where((0, import_drizzle_orm3.like)(durableSignalWaiters.token, `${escapeLike(prefix)}%`));
794
+ const rows = await this.db.select().from(durableSignalWaiters).where((0, import_drizzle_orm4.like)(durableSignalWaiters.token, `${escapeLike(prefix)}%`));
664
795
  return rows.map(fromWaiterRow);
665
796
  }
666
797
  async registerChildWaits(entries) {
@@ -690,23 +821,23 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
690
821
  }
691
822
  async compactCheckpoints(runId, fromSeq, toSeq, into) {
692
823
  await this.db.transaction(async (tx) => {
693
- await tx.delete(durableStepCheckpoints).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableStepCheckpoints.runId, runId), (0, import_drizzle_orm3.gte)(durableStepCheckpoints.seq, fromSeq), (0, import_drizzle_orm3.lte)(durableStepCheckpoints.seq, toSeq)));
824
+ await tx.delete(durableStepCheckpoints).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableStepCheckpoints.runId, runId), (0, import_drizzle_orm4.gte)(durableStepCheckpoints.seq, fromSeq), (0, import_drizzle_orm4.lte)(durableStepCheckpoints.seq, toSeq)));
694
825
  await upsertCheckpoint(tx, into);
695
826
  });
696
827
  }
697
828
  async listSignalWaitersByRun(runIds) {
698
829
  if (runIds.length === 0) return [];
699
- const rows = await this.db.select().from(durableSignalWaiters).where((0, import_drizzle_orm3.inArray)(durableSignalWaiters.runId, runIds));
830
+ const rows = await this.db.select().from(durableSignalWaiters).where((0, import_drizzle_orm4.inArray)(durableSignalWaiters.runId, runIds));
700
831
  return rows.map(fromWaiterRow);
701
832
  }
702
833
  async hasSignalWaiter(runId, tokenPrefix) {
703
834
  const rows = await this.db.select({
704
835
  token: durableSignalWaiters.token
705
- }).from(durableSignalWaiters).where((0, import_drizzle_orm3.and)((0, import_drizzle_orm3.eq)(durableSignalWaiters.runId, runId), (0, import_drizzle_orm3.like)(durableSignalWaiters.token, `${escapeLike(tokenPrefix)}%`))).limit(1);
836
+ }).from(durableSignalWaiters).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableSignalWaiters.runId, runId), (0, import_drizzle_orm4.like)(durableSignalWaiters.token, `${escapeLike(tokenPrefix)}%`))).limit(1);
706
837
  return rows.length > 0;
707
838
  }
708
839
  async removeSignalWaiter(waiter) {
709
- 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)));
840
+ await this.db.delete(durableSignalWaiters).where((0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableSignalWaiters.token, waiter.token), (0, import_drizzle_orm4.eq)(durableSignalWaiters.runId, waiter.runId), (0, import_drizzle_orm4.eq)(durableSignalWaiters.seq, waiter.seq)));
710
841
  }
711
842
  async bufferSignal(token, payload) {
712
843
  await this.db.insert(durableBufferedSignals).values({
@@ -717,10 +848,10 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
717
848
  async takeBufferedSignal(token) {
718
849
  const oldest = this.db.select({
719
850
  id: durableBufferedSignals.id
720
- }).from(durableBufferedSignals).where((0, import_drizzle_orm3.eq)(durableBufferedSignals.token, token)).orderBy((0, import_drizzle_orm3.asc)(durableBufferedSignals.id)).limit(1).for("update", {
851
+ }).from(durableBufferedSignals).where((0, import_drizzle_orm4.eq)(durableBufferedSignals.token, token)).orderBy((0, import_drizzle_orm4.asc)(durableBufferedSignals.id)).limit(1).for("update", {
721
852
  skipLocked: true
722
853
  });
723
- const rows = await this.db.delete(durableBufferedSignals).where((0, import_drizzle_orm3.inArray)(durableBufferedSignals.id, oldest)).returning({
854
+ const rows = await this.db.delete(durableBufferedSignals).where((0, import_drizzle_orm4.inArray)(durableBufferedSignals.id, oldest)).returning({
724
855
  payload: durableBufferedSignals.payload
725
856
  });
726
857
  const row = rows[0];
@@ -737,7 +868,7 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
737
868
  });
738
869
  }
739
870
  async listBufferedEvents(name, limit) {
740
- 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);
871
+ const rows = await this.db.select().from(durableBufferedEvents).where((0, import_drizzle_orm4.eq)(durableBufferedEvents.name, name)).orderBy((0, import_drizzle_orm4.asc)(durableBufferedEvents.publishedAt), (0, import_drizzle_orm4.asc)(durableBufferedEvents.id)).limit(limit);
741
872
  return rows.map((r) => ({
742
873
  id: r.id,
743
874
  payload: r.payload ?? void 0,
@@ -745,7 +876,7 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
745
876
  }));
746
877
  }
747
878
  async removeBufferedEvent(id) {
748
- const rows = await this.db.delete(durableBufferedEvents).where((0, import_drizzle_orm3.eq)(durableBufferedEvents.id, id)).returning({
879
+ const rows = await this.db.delete(durableBufferedEvents).where((0, import_drizzle_orm4.eq)(durableBufferedEvents.id, id)).returning({
749
880
  id: durableBufferedEvents.id
750
881
  });
751
882
  return rows.length === 1;
@@ -755,6 +886,41 @@ function escapeLike(value) {
755
886
  return value.replace(/[\\%_]/g, (c) => `\\${c}`);
756
887
  }
757
888
  __name(escapeLike, "escapeLike");
889
+ var CHECKPOINT_BATCH_ROWS = 500;
890
+ var rowKey = /* @__PURE__ */ __name((cp) => `${cp.seq}:${cp.runId}`, "rowKey");
891
+ function distinctRounds(checkpoints) {
892
+ const rounds = [];
893
+ const seen = /* @__PURE__ */ new Map();
894
+ checkpoints.forEach((cp, i) => {
895
+ const key = rowKey(cp);
896
+ const round = seen.get(key) ?? 0;
897
+ seen.set(key, round + 1);
898
+ const indexes = rounds[round] ?? [];
899
+ rounds[round] = indexes;
900
+ indexes.push(i);
901
+ });
902
+ return rounds;
903
+ }
904
+ __name(distinctRounds, "distinctRounds");
905
+ function updateIfPendingFrom(checkpoints) {
906
+ const json = /* @__PURE__ */ __name((v) => v == null ? null : JSON.stringify(v), "json");
907
+ const ts = /* @__PURE__ */ __name((v) => v == null ? null : v.toISOString(), "ts");
908
+ const values = checkpoints.map((cp) => {
909
+ const r = toCheckpointRow(cp);
910
+ return import_drizzle_orm4.sql`(${r.runId}::text, ${r.seq}::integer, ${r.name}::text, ${r.kind}::text, ${r.stepId}::text, ${r.status}::text, ${json(r.input)}::jsonb, ${json(r.output)}::jsonb, ${json(r.error)}::jsonb, ${json(r.events)}::jsonb, ${r.attempts}::integer, ${r.workerGroup}::text, ${r.parallelGroup}::text, ${ts(r.wakeAt)}::timestamptz, ${ts(r.enqueuedAt)}::timestamptz, ${ts(r.startedAt)}::timestamptz, ${ts(r.finishedAt)}::timestamptz)`;
911
+ });
912
+ return import_drizzle_orm4.sql`UPDATE ${durableStepCheckpoints} AS c SET
913
+ name = v.name, kind = v.kind, step_id = v.step_id, status = v.status, input = v.input,
914
+ output = v.output, error = v.error, events = v.events, attempts = v.attempts,
915
+ worker_group = v.worker_group, parallel_group = v.parallel_group, wake_at = v.wake_at,
916
+ enqueued_at = v.enqueued_at, started_at = v.started_at, finished_at = v.finished_at
917
+ FROM (VALUES ${import_drizzle_orm4.sql.join(values, import_drizzle_orm4.sql`, `)}) AS v(run_id, seq, name, kind, step_id, status, input,
918
+ output, error, events, attempts, worker_group, parallel_group, wake_at, enqueued_at, started_at,
919
+ finished_at)
920
+ WHERE c.run_id = v.run_id AND c.seq = v.seq AND c.status = 'pending'
921
+ RETURNING c.run_id, c.seq`;
922
+ }
923
+ __name(updateIfPendingFrom, "updateIfPendingFrom");
758
924
  async function upsertCheckpoint(db, cp) {
759
925
  const row = toCheckpointRow(cp);
760
926
  await db.insert(durableStepCheckpoints).values(row).onConflictDoUpdate({
@@ -767,16 +933,16 @@ async function upsertCheckpoint(db, cp) {
767
933
  }
768
934
  __name(upsertCheckpoint, "upsertCheckpoint");
769
935
  async function reindexAttributes(db, runId, attributes) {
770
- await db.delete(durableRunAttributes).where((0, import_drizzle_orm3.eq)(durableRunAttributes.runId, runId));
936
+ await db.delete(durableRunAttributes).where((0, import_drizzle_orm4.eq)(durableRunAttributes.runId, runId));
771
937
  const rows = (0, import_nestjs_durable_core.normalizeAttributeRows)(runId, attributes);
772
938
  if (rows.length) await db.insert(durableRunAttributes).values(rows);
773
939
  }
774
940
  __name(reindexAttributes, "reindexAttributes");
775
941
  async function deleteRunsCascade(db, runIds) {
776
- await db.delete(durableStepCheckpoints).where((0, import_drizzle_orm3.inArray)(durableStepCheckpoints.runId, runIds));
777
- await db.delete(durableSignalWaiters).where((0, import_drizzle_orm3.inArray)(durableSignalWaiters.runId, runIds));
778
- await db.delete(durableRunAttributes).where((0, import_drizzle_orm3.inArray)(durableRunAttributes.runId, runIds));
779
- await db.delete(durableWorkflowRuns).where((0, import_drizzle_orm3.inArray)(durableWorkflowRuns.id, runIds));
942
+ await db.delete(durableStepCheckpoints).where((0, import_drizzle_orm4.inArray)(durableStepCheckpoints.runId, runIds));
943
+ await db.delete(durableSignalWaiters).where((0, import_drizzle_orm4.inArray)(durableSignalWaiters.runId, runIds));
944
+ await db.delete(durableRunAttributes).where((0, import_drizzle_orm4.inArray)(durableRunAttributes.runId, runIds));
945
+ await db.delete(durableWorkflowRuns).where((0, import_drizzle_orm4.inArray)(durableWorkflowRuns.id, runIds));
780
946
  }
781
947
  __name(deleteRunsCascade, "deleteRunsCascade");
782
948
  function fromWaiterRow(row) {
@@ -950,44 +1116,12 @@ function fromScheduleRow(row) {
950
1116
  }
951
1117
  __name(fromScheduleRow, "fromScheduleRow");
952
1118
  function excludedSet(table, only) {
953
- return Object.fromEntries(Object.entries((0, import_drizzle_orm3.getTableColumns)(table)).filter(([key]) => !only || only.includes(key)).map(([key, column]) => [
1119
+ return Object.fromEntries(Object.entries((0, import_drizzle_orm4.getTableColumns)(table)).filter(([key]) => !only || only.includes(key)).map(([key, column]) => [
954
1120
  key,
955
- import_drizzle_orm3.sql.raw(`excluded."${column.name}"`)
1121
+ import_drizzle_orm4.sql.raw(`excluded."${column.name}"`)
956
1122
  ]));
957
1123
  }
958
1124
  __name(excludedSet, "excludedSet");
959
-
960
- // src/pg/executor.ts
961
- var import_drizzle_orm4 = require("drizzle-orm");
962
- function toDrizzleSql(text2, params = []) {
963
- const chunks = [];
964
- let last = 0;
965
- for (const match of text2.matchAll(/\$(\d+)/g)) {
966
- const index2 = Number(match[1]) - 1;
967
- if (index2 < 0 || index2 >= params.length) throw new Error(`placeholder ${match[0]} has no bound parameter`);
968
- chunks.push(import_drizzle_orm4.sql.raw(text2.slice(last, match.index)), import_drizzle_orm4.sql`${params[index2]}`);
969
- last = (match.index ?? 0) + match[0].length;
970
- }
971
- chunks.push(import_drizzle_orm4.sql.raw(text2.slice(last)));
972
- return import_drizzle_orm4.sql.join(chunks);
973
- }
974
- __name(toDrizzleSql, "toDrizzleSql");
975
- function rowsOf(result) {
976
- if (Array.isArray(result)) return result;
977
- return result?.rows ?? [];
978
- }
979
- __name(rowsOf, "rowsOf");
980
- function drizzlePgExecutor(db) {
981
- return {
982
- dialect: "postgres",
983
- escapeId: /* @__PURE__ */ __name((id) => `"${id.replace(/"/g, '""')}"`, "escapeId"),
984
- query: /* @__PURE__ */ __name(async (text2, params) => rowsOf(await db.execute(toDrizzleSql(text2, params))), "query"),
985
- transaction: /* @__PURE__ */ __name((fn) => db.transaction((tx) => fn({
986
- query: /* @__PURE__ */ __name(async (text2, params) => rowsOf(await tx.execute(toDrizzleSql(text2, params))), "query")
987
- })), "transaction")
988
- };
989
- }
990
- __name(drizzlePgExecutor, "drizzlePgExecutor");
991
1125
  // Annotate the CommonJS export names for ESM import in node:
992
1126
  0 && (module.exports = {
993
1127
  DEFAULT_NAMESPACE,
@@ -1004,6 +1138,7 @@ __name(drizzlePgExecutor, "drizzlePgExecutor");
1004
1138
  durableStepCheckpoints,
1005
1139
  durableWorkflowRuns,
1006
1140
  ensureDrizzlePgDurableSchema,
1141
+ rowsOf,
1007
1142
  toDrizzleSql
1008
1143
  });
1009
1144
  //# sourceMappingURL=index.cjs.map