@dudousxd/nestjs-durable-store-drizzle 0.23.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.
package/dist/pg/index.cjs CHANGED
@@ -25,6 +25,7 @@ __export(pg_exports, {
25
25
  DURABLE_PG_DDL: () => DURABLE_PG_DDL,
26
26
  DrizzlePgStateStore: () => DrizzlePgStateStore,
27
27
  drizzlePgExecutor: () => drizzlePgExecutor,
28
+ drizzlePgSchemaRepairWarning: () => drizzlePgSchemaRepairWarning,
28
29
  durableBufferedEvents: () => durableBufferedEvents,
29
30
  durableBufferedSignals: () => durableBufferedSignals,
30
31
  durablePgManagedTables: () => durablePgManagedTables,
@@ -41,7 +42,41 @@ __export(pg_exports, {
41
42
  module.exports = __toCommonJS(pg_exports);
42
43
 
43
44
  // src/pg/ddl.ts
45
+ var import_drizzle_orm2 = require("drizzle-orm");
46
+
47
+ // src/pg/executor.ts
44
48
  var import_drizzle_orm = require("drizzle-orm");
49
+ function toDrizzleSql(text2, params = []) {
50
+ const chunks = [];
51
+ let last = 0;
52
+ for (const match of text2.matchAll(/\$(\d+)/g)) {
53
+ const index2 = Number(match[1]) - 1;
54
+ if (index2 < 0 || index2 >= params.length) throw new Error(`placeholder ${match[0]} has no bound parameter`);
55
+ chunks.push(import_drizzle_orm.sql.raw(text2.slice(last, match.index)), import_drizzle_orm.sql`${params[index2]}`);
56
+ last = (match.index ?? 0) + match[0].length;
57
+ }
58
+ chunks.push(import_drizzle_orm.sql.raw(text2.slice(last)));
59
+ return import_drizzle_orm.sql.join(chunks);
60
+ }
61
+ __name(toDrizzleSql, "toDrizzleSql");
62
+ function rowsOf(result) {
63
+ if (Array.isArray(result)) return result;
64
+ return result?.rows ?? [];
65
+ }
66
+ __name(rowsOf, "rowsOf");
67
+ function drizzlePgExecutor(db) {
68
+ return {
69
+ dialect: "postgres",
70
+ escapeId: /* @__PURE__ */ __name((id) => `"${id.replace(/"/g, '""')}"`, "escapeId"),
71
+ query: /* @__PURE__ */ __name(async (text2, params) => rowsOf(await db.execute(toDrizzleSql(text2, params))), "query"),
72
+ transaction: /* @__PURE__ */ __name((fn) => db.transaction((tx) => fn({
73
+ query: /* @__PURE__ */ __name(async (text2, params) => rowsOf(await tx.execute(toDrizzleSql(text2, params))), "query")
74
+ })), "transaction")
75
+ };
76
+ }
77
+ __name(drizzlePgExecutor, "drizzlePgExecutor");
78
+
79
+ // src/pg/ddl.ts
45
80
  var DURABLE_PG_DDL = [
46
81
  `CREATE TABLE IF NOT EXISTS "durable_workflow_runs" (
47
82
  "id" text PRIMARY KEY NOT NULL,
@@ -134,11 +169,71 @@ var DURABLE_PG_DDL = [
134
169
  `CREATE INDEX IF NOT EXISTS "durable_schedules_due_idx" ON "durable_schedules" USING btree ("paused","next_fire_at")`
135
170
  ];
136
171
  var SCHEMA_LOCK_KEY = "durable_schema";
137
- async function ensureDrizzlePgDurableSchema(db) {
138
- await db.transaction(async (tx) => {
139
- await tx.execute(import_drizzle_orm.sql`SELECT pg_advisory_xact_lock(hashtext(${SCHEMA_LOCK_KEY}))`);
140
- for (const statement of DURABLE_PG_DDL) await tx.execute(import_drizzle_orm.sql.raw(statement));
172
+ function drizzlePgSchemaRepairWarning(repairs) {
173
+ return [
174
+ `[nestjs-durable] store-drizzle repaired the durable schema at boot: ${repairs.join("; ")}.`,
175
+ "The database had drifted from the schema this library version expects (it was created by an",
176
+ "older version, or a migration left these out), so they were applied at runtime and nothing",
177
+ "recorded them in your migration history. To make them permanent, generate a drizzle-kit",
178
+ "migration from `durablePgSchema` (or apply `DURABLE_PG_DDL` in a migration). This warning",
179
+ "appears only when a repair was applied; a schema that is already current logs nothing."
180
+ ].join(" ");
181
+ }
182
+ __name(drizzlePgSchemaRepairWarning, "drizzlePgSchemaRepairWarning");
183
+ function ddlTarget(statement) {
184
+ const table = statement.match(/^CREATE TABLE IF NOT EXISTS "([^"]+)"/i);
185
+ if (table?.[1]) return {
186
+ kind: "table",
187
+ table: table[1]
188
+ };
189
+ const index2 = statement.match(/^CREATE (?:UNIQUE )?INDEX (?:CONCURRENTLY )?IF NOT EXISTS "([^"]+)" ON "([^"]+)"/i);
190
+ if (index2?.[1] && index2[2]) return {
191
+ kind: "index",
192
+ index: index2[1],
193
+ table: index2[2]
194
+ };
195
+ const column = statement.match(/^ALTER TABLE "([^"]+)" ADD COLUMN IF NOT EXISTS "([^"]+)"/i);
196
+ if (column?.[1] && column[2]) return {
197
+ kind: "column",
198
+ table: column[1],
199
+ column: column[2]
200
+ };
201
+ return void 0;
202
+ }
203
+ __name(ddlTarget, "ddlTarget");
204
+ async function ensureDrizzlePgDurableSchema(db, options = {}) {
205
+ const logger = options.logger ?? console;
206
+ const { repairs, freshInstall } = await db.transaction(async (tx) => {
207
+ await tx.execute(import_drizzle_orm2.sql`SELECT pg_advisory_xact_lock(hashtext(${SCHEMA_LOCK_KEY}))`);
208
+ const tables = new Set(rowsOf(await tx.execute(import_drizzle_orm2.sql.raw("SELECT tablename AS name FROM pg_tables WHERE schemaname = current_schema() AND left(tablename, 8) = 'durable_'"))).map((row) => row.name));
209
+ const indexes = new Set(rowsOf(await tx.execute(import_drizzle_orm2.sql.raw("SELECT indexname AS name FROM pg_indexes WHERE schemaname = current_schema() AND left(tablename, 8) = 'durable_'"))).map((row) => row.name));
210
+ const columns = new Set(rowsOf(await tx.execute(import_drizzle_orm2.sql.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));
211
+ const created = /* @__PURE__ */ new Set();
212
+ const ddlTables = /* @__PURE__ */ new Set();
213
+ const applied = [];
214
+ for (const statement of DURABLE_PG_DDL) {
215
+ const target = ddlTarget(statement);
216
+ await tx.execute(import_drizzle_orm2.sql.raw(statement));
217
+ if (!target) continue;
218
+ if (target.kind === "table") {
219
+ ddlTables.add(target.table);
220
+ if (!tables.has(target.table)) {
221
+ created.add(target.table);
222
+ applied.push(`created table ${target.table}`);
223
+ }
224
+ } else if (created.has(target.table)) {
225
+ } else if (target.kind === "index" && !indexes.has(target.index)) {
226
+ applied.push(`created index ${target.index} on ${target.table}`);
227
+ } else if (target.kind === "column" && !columns.has(`${target.table}.${target.column}`)) {
228
+ applied.push(`added column ${target.table}.${target.column}`);
229
+ }
230
+ }
231
+ return {
232
+ repairs: applied,
233
+ freshInstall: created.size === ddlTables.size
234
+ };
141
235
  });
236
+ if (repairs.length > 0 && !freshInstall) logger.warn(drizzlePgSchemaRepairWarning(repairs));
142
237
  }
143
238
  __name(ensureDrizzlePgDurableSchema, "ensureDrizzlePgDurableSchema");
144
239
 
@@ -146,38 +241,6 @@ __name(ensureDrizzlePgDurableSchema, "ensureDrizzlePgDurableSchema");
146
241
  var import_nestjs_durable_core = require("@dudousxd/nestjs-durable-core");
147
242
  var import_drizzle_orm4 = require("drizzle-orm");
148
243
 
149
- // src/pg/executor.ts
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
244
  // src/pg/schema.ts
182
245
  var import_drizzle_orm3 = require("drizzle-orm");
183
246
  var import_pg_core = require("drizzle-orm/pg-core");
@@ -390,7 +453,9 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
390
453
  /** Idempotent CREATE TABLE / INDEX IF NOT EXISTS under an advisory lock — called on boot when the
391
454
  * module's `autoSchema` is on. Turn `autoSchema` off if drizzle-kit owns these tables. */
392
455
  async ensureSchema() {
393
- await ensureDrizzlePgDurableSchema(this.db);
456
+ await ensureDrizzlePgDurableSchema(this.db, {
457
+ logger: this.options.schemaLogger
458
+ });
394
459
  }
395
460
  /** The read-scope predicate on the runs table (undefined when the store is unscoped). */
396
461
  scope() {
@@ -588,11 +653,11 @@ var DrizzlePgStateStore = class _DrizzlePgStateStore {
588
653
  });
589
654
  return rows.length === 1;
590
655
  }
591
- async releaseRunLock(runId) {
656
+ async releaseRunLock(runId, owner) {
592
657
  await this.db.update(durableWorkflowRuns).set({
593
658
  lockedBy: null,
594
659
  lockedUntil: null
595
- }).where((0, import_drizzle_orm4.eq)(durableWorkflowRuns.id, runId));
660
+ }).where(owner === void 0 ? (0, import_drizzle_orm4.eq)(durableWorkflowRuns.id, runId) : (0, import_drizzle_orm4.and)((0, import_drizzle_orm4.eq)(durableWorkflowRuns.id, runId), (0, import_drizzle_orm4.eq)(durableWorkflowRuns.lockedBy, owner)));
596
661
  }
597
662
  async renewRunLock(runId, owner, leaseUntilMs) {
598
663
  const rows = await this.db.update(durableWorkflowRuns).set({
@@ -1140,6 +1205,7 @@ __name(excludedSet, "excludedSet");
1140
1205
  DURABLE_PG_DDL,
1141
1206
  DrizzlePgStateStore,
1142
1207
  drizzlePgExecutor,
1208
+ drizzlePgSchemaRepairWarning,
1143
1209
  durableBufferedEvents,
1144
1210
  durableBufferedSignals,
1145
1211
  durablePgManagedTables,