@dudousxd/nestjs-durable-store-drizzle 0.22.0 → 0.24.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.
@@ -1,6 +1,6 @@
1
1
  import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
2
2
  import { PgDatabase } from 'drizzle-orm/pg-core';
3
- import { StateStore, WorkflowRun, RunStatus, RetentionPolicy, StepCheckpoint, StoreTransaction, RunQuery, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, ScheduleRecord, ScheduleQuery, CheckpointStat, CheckpointQuery, SignalWaiter, ChildWait } from '@dudousxd/nestjs-durable-core';
3
+ import { StateStore, WorkflowRun, RunStatus, RetentionPolicy, StepCheckpoint, StoreTransaction, OwnedLeaseReclaim, RunQuery, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, ScheduleRecord, ScheduleQuery, CheckpointStat, CheckpointQuery, SignalWaiter, ChildWait } from '@dudousxd/nestjs-durable-core';
4
4
  import { SQL } from 'drizzle-orm';
5
5
 
6
6
  /**
@@ -22,12 +22,40 @@ import { SQL } from 'drizzle-orm';
22
22
  * an existing database heals on the next boot exactly like the MikroORM / TypeORM auto-schema does.
23
23
  */
24
24
  declare const DURABLE_PG_DDL: readonly string[];
25
+ /**
26
+ * Where {@link ensureDrizzlePgDurableSchema} reports a schema repair it actually applied. `console`
27
+ * (the default) satisfies it, and so does NestJS's `Logger`, so an app can route the warning into
28
+ * its own log stream.
29
+ */
30
+ interface DurableSchemaLogger {
31
+ warn(message: string): void;
32
+ }
33
+ interface EnsureDrizzlePgDurableSchemaOptions {
34
+ /** Sink for the schema-repair warning. Defaults to `console`. */
35
+ logger?: DurableSchemaLogger | undefined;
36
+ }
37
+ /**
38
+ * The warning logged when the boot-time check changed an EXISTING durable schema: created a table
39
+ * that was missing next to ones that exist, added a column, or created an index. Never logged when
40
+ * the schema was already current, and never on a fresh install (no durable table existed, so
41
+ * creating them all is the expected first boot, not a drift).
42
+ *
43
+ * The discriminator matters more than the wording: a warning on every boot would be filtered as
44
+ * noise, and a silent repair hides a drizzle-kit-managed schema that has drifted from the library.
45
+ */
46
+ declare function drizzlePgSchemaRepairWarning(repairs: readonly string[]): string;
25
47
  /**
26
48
  * Idempotently create the durable tables + indexes on `db` (any drizzle Postgres driver). Runs in one
27
49
  * transaction under a `pg_advisory_xact_lock`, so N pods booting together heal the schema once and
28
50
  * the rest see it done. Only ever touches the `durable_*` tables.
51
+ *
52
+ * The DDL is all `IF NOT EXISTS`, which does not say whether it created anything, so the current
53
+ * durable tables, columns and indexes are read from the catalog first (inside the lock) and each
54
+ * statement that will create something is recorded. When anything was created on an EXISTING durable
55
+ * schema, ONE warning listing it goes to `options.logger` (default `console`) after the transaction
56
+ * commits; see {@link drizzlePgSchemaRepairWarning}. A current schema and a fresh install log nothing.
29
57
  */
30
- declare function ensureDrizzlePgDurableSchema(db: PgDatabase<any, any, any>): Promise<void>;
58
+ declare function ensureDrizzlePgDurableSchema(db: PgDatabase<any, any, any>, options?: EnsureDrizzlePgDurableSchemaOptions): Promise<void>;
31
59
 
32
60
  /**
33
61
  * Any drizzle Postgres database — `drizzle-orm/node-postgres`, `drizzle-orm/postgres-js`,
@@ -45,6 +73,12 @@ interface DrizzlePgStateStoreOptions {
45
73
  scope?: {
46
74
  namespace?: string | undefined;
47
75
  } | undefined;
76
+ /**
77
+ * Where `ensureSchema()` (the boot-time auto-schema) reports a schema repair it actually applied
78
+ * — a missing table or index it had to create. Defaults to `console`. NestJS's `Logger`
79
+ * satisfies it.
80
+ */
81
+ schemaLogger?: DurableSchemaLogger | undefined;
48
82
  }
49
83
  /**
50
84
  * Drizzle `StateStore` for **PostgreSQL**. Pass any drizzle Postgres db (see {@link DrizzlePgDatabase});
@@ -91,13 +125,13 @@ declare class DrizzlePgStateStore implements StateStore {
91
125
  transaction<T>(work: (tx: StoreTransaction) => Promise<T>): Promise<T>;
92
126
  /** Crash recovery. Namespace-scoped: a worker re-drives only its own tenant's runs. */
93
127
  listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
94
- listOrphanedRuns(nowMs: number, limit: number, namespace?: string): Promise<WorkflowRun[]>;
128
+ listOrphanedRuns(nowMs: number, limit: number, namespace?: string, ownedBy?: OwnedLeaseReclaim): Promise<WorkflowRun[]>;
95
129
  /** FIFO pick-up, served by `durable_workflow_runs_namespace_status_idx`. The engine then leases
96
130
  * each run with {@link tryLockRun}; the listing itself takes no locks (it runs outside a tx). */
97
131
  listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
98
132
  listDueTimers(nowMs: number, namespace?: string, limit?: number): Promise<WorkflowRun[]>;
99
133
  tryLockRun(runId: string, owner: string, leaseUntilMs: number, nowMs: number): Promise<boolean>;
100
- releaseRunLock(runId: string): Promise<void>;
134
+ releaseRunLock(runId: string, owner?: string): Promise<void>;
101
135
  renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean>;
102
136
  /** The predicates every run query shares — {@link listRuns} pages them, the facets group them. */
103
137
  private runFilters;
@@ -2397,4 +2431,4 @@ declare const durablePgSchema: {
2397
2431
  */
2398
2432
  declare function durablePgManagedTables(): string[];
2399
2433
 
2400
- export { DEFAULT_NAMESPACE, DURABLE_PG_DDL, type DrizzlePgDatabase, type DrizzlePgSqlExecutor, DrizzlePgStateStore, type DrizzlePgStateStoreOptions, drizzlePgExecutor, durableBufferedEvents, durableBufferedSignals, durablePgManagedTables, durablePgSchema, durableRunAttributes, durableSchedules, durableSignalWaiters, durableStepCheckpoints, durableWorkflowRuns, ensureDrizzlePgDurableSchema, rowsOf, toDrizzleSql };
2434
+ export { DEFAULT_NAMESPACE, DURABLE_PG_DDL, type DrizzlePgDatabase, type DrizzlePgSqlExecutor, DrizzlePgStateStore, type DrizzlePgStateStoreOptions, type DurableSchemaLogger, type EnsureDrizzlePgDurableSchemaOptions, drizzlePgExecutor, drizzlePgSchemaRepairWarning, durableBufferedEvents, durableBufferedSignals, durablePgManagedTables, durablePgSchema, durableRunAttributes, durableSchedules, durableSignalWaiters, durableStepCheckpoints, durableWorkflowRuns, ensureDrizzlePgDurableSchema, rowsOf, toDrizzleSql };
@@ -1,6 +1,6 @@
1
1
  import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
2
2
  import { PgDatabase } from 'drizzle-orm/pg-core';
3
- import { StateStore, WorkflowRun, RunStatus, RetentionPolicy, StepCheckpoint, StoreTransaction, RunQuery, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, ScheduleRecord, ScheduleQuery, CheckpointStat, CheckpointQuery, SignalWaiter, ChildWait } from '@dudousxd/nestjs-durable-core';
3
+ import { StateStore, WorkflowRun, RunStatus, RetentionPolicy, StepCheckpoint, StoreTransaction, OwnedLeaseReclaim, RunQuery, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, ScheduleRecord, ScheduleQuery, CheckpointStat, CheckpointQuery, SignalWaiter, ChildWait } from '@dudousxd/nestjs-durable-core';
4
4
  import { SQL } from 'drizzle-orm';
5
5
 
6
6
  /**
@@ -22,12 +22,40 @@ import { SQL } from 'drizzle-orm';
22
22
  * an existing database heals on the next boot exactly like the MikroORM / TypeORM auto-schema does.
23
23
  */
24
24
  declare const DURABLE_PG_DDL: readonly string[];
25
+ /**
26
+ * Where {@link ensureDrizzlePgDurableSchema} reports a schema repair it actually applied. `console`
27
+ * (the default) satisfies it, and so does NestJS's `Logger`, so an app can route the warning into
28
+ * its own log stream.
29
+ */
30
+ interface DurableSchemaLogger {
31
+ warn(message: string): void;
32
+ }
33
+ interface EnsureDrizzlePgDurableSchemaOptions {
34
+ /** Sink for the schema-repair warning. Defaults to `console`. */
35
+ logger?: DurableSchemaLogger | undefined;
36
+ }
37
+ /**
38
+ * The warning logged when the boot-time check changed an EXISTING durable schema: created a table
39
+ * that was missing next to ones that exist, added a column, or created an index. Never logged when
40
+ * the schema was already current, and never on a fresh install (no durable table existed, so
41
+ * creating them all is the expected first boot, not a drift).
42
+ *
43
+ * The discriminator matters more than the wording: a warning on every boot would be filtered as
44
+ * noise, and a silent repair hides a drizzle-kit-managed schema that has drifted from the library.
45
+ */
46
+ declare function drizzlePgSchemaRepairWarning(repairs: readonly string[]): string;
25
47
  /**
26
48
  * Idempotently create the durable tables + indexes on `db` (any drizzle Postgres driver). Runs in one
27
49
  * transaction under a `pg_advisory_xact_lock`, so N pods booting together heal the schema once and
28
50
  * the rest see it done. Only ever touches the `durable_*` tables.
51
+ *
52
+ * The DDL is all `IF NOT EXISTS`, which does not say whether it created anything, so the current
53
+ * durable tables, columns and indexes are read from the catalog first (inside the lock) and each
54
+ * statement that will create something is recorded. When anything was created on an EXISTING durable
55
+ * schema, ONE warning listing it goes to `options.logger` (default `console`) after the transaction
56
+ * commits; see {@link drizzlePgSchemaRepairWarning}. A current schema and a fresh install log nothing.
29
57
  */
30
- declare function ensureDrizzlePgDurableSchema(db: PgDatabase<any, any, any>): Promise<void>;
58
+ declare function ensureDrizzlePgDurableSchema(db: PgDatabase<any, any, any>, options?: EnsureDrizzlePgDurableSchemaOptions): Promise<void>;
31
59
 
32
60
  /**
33
61
  * Any drizzle Postgres database — `drizzle-orm/node-postgres`, `drizzle-orm/postgres-js`,
@@ -45,6 +73,12 @@ interface DrizzlePgStateStoreOptions {
45
73
  scope?: {
46
74
  namespace?: string | undefined;
47
75
  } | undefined;
76
+ /**
77
+ * Where `ensureSchema()` (the boot-time auto-schema) reports a schema repair it actually applied
78
+ * — a missing table or index it had to create. Defaults to `console`. NestJS's `Logger`
79
+ * satisfies it.
80
+ */
81
+ schemaLogger?: DurableSchemaLogger | undefined;
48
82
  }
49
83
  /**
50
84
  * Drizzle `StateStore` for **PostgreSQL**. Pass any drizzle Postgres db (see {@link DrizzlePgDatabase});
@@ -91,13 +125,13 @@ declare class DrizzlePgStateStore implements StateStore {
91
125
  transaction<T>(work: (tx: StoreTransaction) => Promise<T>): Promise<T>;
92
126
  /** Crash recovery. Namespace-scoped: a worker re-drives only its own tenant's runs. */
93
127
  listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
94
- listOrphanedRuns(nowMs: number, limit: number, namespace?: string): Promise<WorkflowRun[]>;
128
+ listOrphanedRuns(nowMs: number, limit: number, namespace?: string, ownedBy?: OwnedLeaseReclaim): Promise<WorkflowRun[]>;
95
129
  /** FIFO pick-up, served by `durable_workflow_runs_namespace_status_idx`. The engine then leases
96
130
  * each run with {@link tryLockRun}; the listing itself takes no locks (it runs outside a tx). */
97
131
  listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
98
132
  listDueTimers(nowMs: number, namespace?: string, limit?: number): Promise<WorkflowRun[]>;
99
133
  tryLockRun(runId: string, owner: string, leaseUntilMs: number, nowMs: number): Promise<boolean>;
100
- releaseRunLock(runId: string): Promise<void>;
134
+ releaseRunLock(runId: string, owner?: string): Promise<void>;
101
135
  renewRunLock(runId: string, owner: string, leaseUntilMs: number): Promise<boolean>;
102
136
  /** The predicates every run query shares — {@link listRuns} pages them, the facets group them. */
103
137
  private runFilters;
@@ -2397,4 +2431,4 @@ declare const durablePgSchema: {
2397
2431
  */
2398
2432
  declare function durablePgManagedTables(): string[];
2399
2433
 
2400
- export { DEFAULT_NAMESPACE, DURABLE_PG_DDL, type DrizzlePgDatabase, type DrizzlePgSqlExecutor, DrizzlePgStateStore, type DrizzlePgStateStoreOptions, drizzlePgExecutor, durableBufferedEvents, durableBufferedSignals, durablePgManagedTables, durablePgSchema, durableRunAttributes, durableSchedules, durableSignalWaiters, durableStepCheckpoints, durableWorkflowRuns, ensureDrizzlePgDurableSchema, rowsOf, toDrizzleSql };
2434
+ export { DEFAULT_NAMESPACE, DURABLE_PG_DDL, type DrizzlePgDatabase, type DrizzlePgSqlExecutor, DrizzlePgStateStore, type DrizzlePgStateStoreOptions, type DurableSchemaLogger, type EnsureDrizzlePgDurableSchemaOptions, drizzlePgExecutor, drizzlePgSchemaRepairWarning, durableBufferedEvents, durableBufferedSignals, durablePgManagedTables, durablePgSchema, durableRunAttributes, durableSchedules, durableSignalWaiters, durableStepCheckpoints, durableWorkflowRuns, ensureDrizzlePgDurableSchema, rowsOf, toDrizzleSql };
package/dist/pg/index.js CHANGED
@@ -2,7 +2,41 @@ var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
4
  // src/pg/ddl.ts
5
+ import { sql as sql2 } from "drizzle-orm";
6
+
7
+ // src/pg/executor.ts
5
8
  import { sql } from "drizzle-orm";
9
+ function toDrizzleSql(text2, params = []) {
10
+ const chunks = [];
11
+ let last = 0;
12
+ for (const match of text2.matchAll(/\$(\d+)/g)) {
13
+ const index2 = Number(match[1]) - 1;
14
+ if (index2 < 0 || index2 >= params.length) throw new Error(`placeholder ${match[0]} has no bound parameter`);
15
+ chunks.push(sql.raw(text2.slice(last, match.index)), sql`${params[index2]}`);
16
+ last = (match.index ?? 0) + match[0].length;
17
+ }
18
+ chunks.push(sql.raw(text2.slice(last)));
19
+ return sql.join(chunks);
20
+ }
21
+ __name(toDrizzleSql, "toDrizzleSql");
22
+ function rowsOf(result) {
23
+ if (Array.isArray(result)) return result;
24
+ return result?.rows ?? [];
25
+ }
26
+ __name(rowsOf, "rowsOf");
27
+ function drizzlePgExecutor(db) {
28
+ return {
29
+ dialect: "postgres",
30
+ escapeId: /* @__PURE__ */ __name((id) => `"${id.replace(/"/g, '""')}"`, "escapeId"),
31
+ query: /* @__PURE__ */ __name(async (text2, params) => rowsOf(await db.execute(toDrizzleSql(text2, params))), "query"),
32
+ transaction: /* @__PURE__ */ __name((fn) => db.transaction((tx) => fn({
33
+ query: /* @__PURE__ */ __name(async (text2, params) => rowsOf(await tx.execute(toDrizzleSql(text2, params))), "query")
34
+ })), "transaction")
35
+ };
36
+ }
37
+ __name(drizzlePgExecutor, "drizzlePgExecutor");
38
+
39
+ // src/pg/ddl.ts
6
40
  var DURABLE_PG_DDL = [
7
41
  `CREATE TABLE IF NOT EXISTS "durable_workflow_runs" (
8
42
  "id" text PRIMARY KEY NOT NULL,
@@ -95,11 +129,71 @@ var DURABLE_PG_DDL = [
95
129
  `CREATE INDEX IF NOT EXISTS "durable_schedules_due_idx" ON "durable_schedules" USING btree ("paused","next_fire_at")`
96
130
  ];
97
131
  var SCHEMA_LOCK_KEY = "durable_schema";
98
- async function ensureDrizzlePgDurableSchema(db) {
99
- await db.transaction(async (tx) => {
100
- await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${SCHEMA_LOCK_KEY}))`);
101
- for (const statement of DURABLE_PG_DDL) await tx.execute(sql.raw(statement));
132
+ function drizzlePgSchemaRepairWarning(repairs) {
133
+ return [
134
+ `[nestjs-durable] store-drizzle repaired the durable schema at boot: ${repairs.join("; ")}.`,
135
+ "The database had drifted from the schema this library version expects (it was created by an",
136
+ "older version, or a migration left these out), so they were applied at runtime and nothing",
137
+ "recorded them in your migration history. To make them permanent, generate a drizzle-kit",
138
+ "migration from `durablePgSchema` (or apply `DURABLE_PG_DDL` in a migration). This warning",
139
+ "appears only when a repair was applied; a schema that is already current logs nothing."
140
+ ].join(" ");
141
+ }
142
+ __name(drizzlePgSchemaRepairWarning, "drizzlePgSchemaRepairWarning");
143
+ function ddlTarget(statement) {
144
+ const table = statement.match(/^CREATE TABLE IF NOT EXISTS "([^"]+)"/i);
145
+ if (table?.[1]) return {
146
+ kind: "table",
147
+ table: table[1]
148
+ };
149
+ const index2 = statement.match(/^CREATE (?:UNIQUE )?INDEX (?:CONCURRENTLY )?IF NOT EXISTS "([^"]+)" ON "([^"]+)"/i);
150
+ if (index2?.[1] && index2[2]) return {
151
+ kind: "index",
152
+ index: index2[1],
153
+ table: index2[2]
154
+ };
155
+ const column = statement.match(/^ALTER TABLE "([^"]+)" ADD COLUMN IF NOT EXISTS "([^"]+)"/i);
156
+ if (column?.[1] && column[2]) return {
157
+ kind: "column",
158
+ table: column[1],
159
+ column: column[2]
160
+ };
161
+ return void 0;
162
+ }
163
+ __name(ddlTarget, "ddlTarget");
164
+ async function ensureDrizzlePgDurableSchema(db, options = {}) {
165
+ const logger = options.logger ?? console;
166
+ const { repairs, freshInstall } = await db.transaction(async (tx) => {
167
+ await tx.execute(sql2`SELECT pg_advisory_xact_lock(hashtext(${SCHEMA_LOCK_KEY}))`);
168
+ const tables = new Set(rowsOf(await tx.execute(sql2.raw("SELECT tablename AS name FROM pg_tables WHERE schemaname = current_schema() AND left(tablename, 8) = 'durable_'"))).map((row) => row.name));
169
+ const indexes = new Set(rowsOf(await tx.execute(sql2.raw("SELECT indexname AS name FROM pg_indexes WHERE schemaname = current_schema() AND left(tablename, 8) = 'durable_'"))).map((row) => row.name));
170
+ const columns = new Set(rowsOf(await tx.execute(sql2.raw("SELECT table_name || '.' || column_name AS name FROM information_schema.columns WHERE table_schema = current_schema() AND left(table_name, 8) = 'durable_'"))).map((row) => row.name));
171
+ const created = /* @__PURE__ */ new Set();
172
+ const ddlTables = /* @__PURE__ */ new Set();
173
+ const applied = [];
174
+ for (const statement of DURABLE_PG_DDL) {
175
+ const target = ddlTarget(statement);
176
+ await tx.execute(sql2.raw(statement));
177
+ if (!target) continue;
178
+ if (target.kind === "table") {
179
+ ddlTables.add(target.table);
180
+ if (!tables.has(target.table)) {
181
+ created.add(target.table);
182
+ applied.push(`created table ${target.table}`);
183
+ }
184
+ } else if (created.has(target.table)) {
185
+ } else if (target.kind === "index" && !indexes.has(target.index)) {
186
+ applied.push(`created index ${target.index} on ${target.table}`);
187
+ } else if (target.kind === "column" && !columns.has(`${target.table}.${target.column}`)) {
188
+ applied.push(`added column ${target.table}.${target.column}`);
189
+ }
190
+ }
191
+ return {
192
+ repairs: applied,
193
+ freshInstall: created.size === ddlTables.size
194
+ };
102
195
  });
196
+ if (repairs.length > 0 && !freshInstall) logger.warn(drizzlePgSchemaRepairWarning(repairs));
103
197
  }
104
198
  __name(ensureDrizzlePgDurableSchema, "ensureDrizzlePgDurableSchema");
105
199
 
@@ -107,38 +201,6 @@ __name(ensureDrizzlePgDurableSchema, "ensureDrizzlePgDurableSchema");
107
201
  import { RUN_VALUE_FACET_SCAN, SINGLETON_ACTIVE_STATUSES, TERMINAL_RUN_STATUSES, attributePredicateOperands, axisIsRunColumn, clearTerminalSingletonAdmission, mergeRunFacetRows, mergeRunValueFacetRows, normalizeAttributeRows, parseDuration, runValueFacetsFromRuns, singletonAdmissionDenialPatch, singletonAdmissionTags } from "@dudousxd/nestjs-durable-core";
108
202
  import { and, asc, desc, eq, exists, getTableColumns, gte, inArray, isNotNull, isNull, like, lt, lte, notInArray, or, sql as sql3 } from "drizzle-orm";
109
203
 
110
- // src/pg/executor.ts
111
- import { sql as sql2 } from "drizzle-orm";
112
- function toDrizzleSql(text2, params = []) {
113
- const chunks = [];
114
- let last = 0;
115
- for (const match of text2.matchAll(/\$(\d+)/g)) {
116
- const index2 = Number(match[1]) - 1;
117
- if (index2 < 0 || index2 >= params.length) throw new Error(`placeholder ${match[0]} has no bound parameter`);
118
- chunks.push(sql2.raw(text2.slice(last, match.index)), sql2`${params[index2]}`);
119
- last = (match.index ?? 0) + match[0].length;
120
- }
121
- chunks.push(sql2.raw(text2.slice(last)));
122
- return sql2.join(chunks);
123
- }
124
- __name(toDrizzleSql, "toDrizzleSql");
125
- function rowsOf(result) {
126
- if (Array.isArray(result)) return result;
127
- return result?.rows ?? [];
128
- }
129
- __name(rowsOf, "rowsOf");
130
- function drizzlePgExecutor(db) {
131
- return {
132
- dialect: "postgres",
133
- escapeId: /* @__PURE__ */ __name((id) => `"${id.replace(/"/g, '""')}"`, "escapeId"),
134
- query: /* @__PURE__ */ __name(async (text2, params) => rowsOf(await db.execute(toDrizzleSql(text2, params))), "query"),
135
- transaction: /* @__PURE__ */ __name((fn) => db.transaction((tx) => fn({
136
- query: /* @__PURE__ */ __name(async (text2, params) => rowsOf(await tx.execute(toDrizzleSql(text2, params))), "query")
137
- })), "transaction")
138
- };
139
- }
140
- __name(drizzlePgExecutor, "drizzlePgExecutor");
141
-
142
204
  // src/pg/schema.ts
143
205
  import { getTableName } from "drizzle-orm";
144
206
  import { bigserial, boolean, doublePrecision, index, integer, jsonb, pgTable, primaryKey, text, timestamp } from "drizzle-orm/pg-core";
@@ -310,7 +372,14 @@ function anyOf(conditions) {
310
372
  }
311
373
  __name(anyOf, "anyOf");
312
374
  function namespaceFilter(namespace) {
313
- return namespace === void 0 ? void 0 : eq(durableWorkflowRuns.namespace, namespace);
375
+ if (namespace === void 0) return void 0;
376
+ if (namespace === DEFAULT_NAMESPACE || namespace === "") {
377
+ return inArray(durableWorkflowRuns.namespace, [
378
+ DEFAULT_NAMESPACE,
379
+ ""
380
+ ]);
381
+ }
382
+ return eq(durableWorkflowRuns.namespace, namespace);
314
383
  }
315
384
  __name(namespaceFilter, "namespaceFilter");
316
385
  function hasTag(tag) {
@@ -344,7 +413,9 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
344
413
  /** Idempotent CREATE TABLE / INDEX IF NOT EXISTS under an advisory lock — called on boot when the
345
414
  * module's `autoSchema` is on. Turn `autoSchema` off if drizzle-kit owns these tables. */
346
415
  async ensureSchema() {
347
- await ensureDrizzlePgDurableSchema(this.db);
416
+ await ensureDrizzlePgDurableSchema(this.db, {
417
+ logger: this.options.schemaLogger
418
+ });
348
419
  }
349
420
  /** The read-scope predicate on the runs table (undefined when the store is unscoped). */
350
421
  scope() {
@@ -509,12 +580,17 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
509
580
  ]), namespaceFilter(namespace), this.scope()));
510
581
  return rows.map(fromRunRow);
511
582
  }
512
- async listOrphanedRuns(nowMs, limit, namespace) {
583
+ async listOrphanedRuns(nowMs, limit, namespace, ownedBy) {
513
584
  if (limit <= 0) return [];
514
585
  const rows = await this.db.select().from(durableWorkflowRuns).where(and(inArray(durableWorkflowRuns.status, [
515
586
  "running",
516
587
  "cancelling"
517
- ]), or(isNull(durableWorkflowRuns.lockedUntil), lte(durableWorkflowRuns.lockedUntil, new Date(nowMs))), namespaceFilter(namespace), this.scope())).orderBy(asc(durableWorkflowRuns.createdAt)).limit(limit);
588
+ ]), or(
589
+ isNull(durableWorkflowRuns.lockedUntil),
590
+ lte(durableWorkflowRuns.lockedUntil, new Date(nowMs)),
591
+ // A live lease left by a previous process with the caller's own instance id.
592
+ ownedBy ? and(eq(durableWorkflowRuns.lockedBy, ownedBy.owner), lt(durableWorkflowRuns.lockedUntil, new Date(ownedBy.lockedBefore))) : void 0
593
+ ), namespaceFilter(namespace), this.scope())).orderBy(asc(durableWorkflowRuns.createdAt)).limit(limit);
518
594
  return rows.map(fromRunRow);
519
595
  }
520
596
  /** FIFO pick-up, served by `durable_workflow_runs_namespace_status_idx`. The engine then leases
@@ -537,11 +613,11 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
537
613
  });
538
614
  return rows.length === 1;
539
615
  }
540
- async releaseRunLock(runId) {
616
+ async releaseRunLock(runId, owner) {
541
617
  await this.db.update(durableWorkflowRuns).set({
542
618
  lockedBy: null,
543
619
  lockedUntil: null
544
- }).where(eq(durableWorkflowRuns.id, runId));
620
+ }).where(owner === void 0 ? eq(durableWorkflowRuns.id, runId) : and(eq(durableWorkflowRuns.id, runId), eq(durableWorkflowRuns.lockedBy, owner)));
545
621
  }
546
622
  async renewRunLock(runId, owner, leaseUntilMs) {
547
623
  const rows = await this.db.update(durableWorkflowRuns).set({
@@ -932,7 +1008,7 @@ function toRunRow(run) {
932
1008
  tags: run.tags ?? null,
933
1009
  searchAttributes: run.searchAttributes ?? null,
934
1010
  priority: run.priority ?? null,
935
- namespace: run.namespace ?? DEFAULT_NAMESPACE,
1011
+ namespace: run.namespace || DEFAULT_NAMESPACE,
936
1012
  origin: run.origin ?? null,
937
1013
  createdAt: run.createdAt,
938
1014
  updatedAt: run.updatedAt
@@ -955,7 +1031,7 @@ function toRunPatch(patch) {
955
1031
  if ("tags" in patch) row.tags = patch.tags ?? null;
956
1032
  if ("searchAttributes" in patch) row.searchAttributes = patch.searchAttributes ?? null;
957
1033
  if ("priority" in patch) row.priority = patch.priority ?? null;
958
- if ("namespace" in patch) row.namespace = patch.namespace ?? DEFAULT_NAMESPACE;
1034
+ if ("namespace" in patch) row.namespace = patch.namespace || DEFAULT_NAMESPACE;
959
1035
  if ("origin" in patch) row.origin = patch.origin ?? null;
960
1036
  if (patch.createdAt != null) row.createdAt = patch.createdAt;
961
1037
  if (patch.updatedAt != null) row.updatedAt = patch.updatedAt;
@@ -979,7 +1055,7 @@ function fromRunRow(row) {
979
1055
  tags: row.tags ?? void 0,
980
1056
  searchAttributes: row.searchAttributes ?? void 0,
981
1057
  priority: row.priority ?? void 0,
982
- namespace: row.namespace,
1058
+ namespace: row.namespace || DEFAULT_NAMESPACE,
983
1059
  origin: row.origin ?? void 0,
984
1060
  createdAt: row.createdAt,
985
1061
  updatedAt: row.updatedAt
@@ -1088,6 +1164,7 @@ export {
1088
1164
  DURABLE_PG_DDL,
1089
1165
  DrizzlePgStateStore,
1090
1166
  drizzlePgExecutor,
1167
+ drizzlePgSchemaRepairWarning,
1091
1168
  durableBufferedEvents,
1092
1169
  durableBufferedSignals,
1093
1170
  durablePgManagedTables,