@nextlyhq/adapter-drizzle 0.0.2-alpha.6 → 0.0.2-alpha.60

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,7 +1,8 @@
1
+ import { AnyRelations, SQL } from 'drizzle-orm';
1
2
  import { S as SupportedDialect, a as SqlParam, T as TableResolver } from './core-CVO7WYDj.cjs';
2
- import { T as TransactionContext, e as TransactionOptions, D as DatabaseCapabilities, P as PoolStats, S as SelectOptions, I as InsertOptions, W as WhereClause, U as UpdateOptions, f as DeleteOptions, g as UpsertOptions, a as Migration, d as MigrationResult } from './migration-BbO5meEV.cjs';
3
+ import { T as TransactionContext, e as TransactionOptions, D as DatabaseCapabilities, P as PoolStats, S as SelectOptions, I as InsertOptions, W as WhereClause, U as UpdateOptions, f as DeleteOptions, g as UpsertOptions, a as Migration, d as MigrationResult } from './migration-BUc56kip.cjs';
3
4
  import { T as TableDefinition, C as CreateTableOptions, D as DropTableOptions, A as AlterTableOperation, a as AlterTableOptions } from './schema-BDn8WfSL.cjs';
4
- import { D as DatabaseErrorKind, a as DatabaseError } from './error-um1d_3Uo.cjs';
5
+ import { D as DatabaseErrorKind, a as DatabaseError } from './error-BrdknH2s.cjs';
5
6
 
6
7
  /**
7
8
  * Base database adapter abstract class.
@@ -16,53 +17,6 @@ import { D as DatabaseErrorKind, a as DatabaseError } from './error-um1d_3Uo.cjs
16
17
  * @packageDocumentation
17
18
  */
18
19
 
19
- /**
20
- * Abstract base class for database adapters.
21
- *
22
- * @remarks
23
- * All dialect-specific adapters must extend this class and implement the abstract methods.
24
- * Default implementations are provided for CRUD operations, which can be overridden for
25
- * optimization or dialect-specific behavior.
26
- *
27
- * ## Required Implementations
28
- *
29
- * Subclasses must implement:
30
- * - `dialect` - Database dialect identifier
31
- * - `connect()` - Establish database connection
32
- * - `disconnect()` - Close database connection
33
- * - `executeQuery()` - Execute raw SQL query
34
- * - `transaction()` - Execute operations within a transaction
35
- * - `getCapabilities()` - Report database feature support
36
- *
37
- * ## Optional Overrides
38
- *
39
- * Subclasses can override default CRUD methods for optimization:
40
- * - `select()`, `selectOne()` - Custom query optimization
41
- * - `insert()`, `insertMany()` - Bulk insert optimization
42
- * - `update()`, `delete()` - Custom update/delete logic
43
- * - `upsert()` - Dialect-specific upsert syntax
44
- *
45
- * @example
46
- * ```typescript
47
- * export class PostgresAdapter extends DrizzleAdapter {
48
- * readonly dialect = 'postgresql' as const;
49
- *
50
- * async connect() {
51
- * this.pool = new Pool({ connectionString: this.config.url });
52
- * // ... connection logic
53
- * }
54
- *
55
- * async executeQuery<T>(sql: string, params?: SqlParam[]) {
56
- * const result = await this.pool.query(sql, params);
57
- * return result.rows as T[];
58
- * }
59
- *
60
- * // ... other required methods
61
- * }
62
- * ```
63
- *
64
- * @public
65
- */
66
20
  declare abstract class DrizzleAdapter {
67
21
  /**
68
22
  * Database dialect identifier.
@@ -106,11 +60,29 @@ declare abstract class DrizzleAdapter {
106
60
  * The transaction is automatically committed on success or rolled back on error.
107
61
  * Supports nested transactions via savepoints on databases that support them.
108
62
  *
63
+ * Two kinds of error leave this method, and a caller has to be able to tell
64
+ * them apart. An error carrying the Nextly application brand — see
65
+ * `isApplicationError` — arrives exactly as it was thrown, because that is
66
+ * the application refusing the write rather than the database failing to
67
+ * perform it, and rewriting it would discard the code and payload the caller
68
+ * is meant to act on.
69
+ *
70
+ * Everything else arrives as a `DatabaseError` with its kind classified, and
71
+ * that deliberately includes an unbranded error the callback itself threw:
72
+ * the brand is the only thing distinguishing a deliberate refusal from a
73
+ * driver failure that surfaced through callback code, and guessing from the
74
+ * throw site would put raw driver text on the wire under a caller's own
75
+ * error shape.
76
+ *
77
+ * The rollback is the same either way.
78
+ *
109
79
  * @param callback - Function to execute within transaction
110
80
  * @param options - Transaction options
111
81
  * @returns Result from the callback
112
82
  *
113
- * @throws {DatabaseError} If transaction fails
83
+ * @throws {DatabaseError} If the transaction itself fails, or if the callback
84
+ * threw an error that does not carry the application brand
85
+ * @throws The callback's own error, unchanged, when it carries that brand
114
86
  */
115
87
  abstract transaction<T>(callback: (ctx: TransactionContext) => Promise<T>, options?: TransactionOptions): Promise<T>;
116
88
  /**
@@ -136,17 +108,18 @@ declare abstract class DrizzleAdapter {
136
108
  * - Consistent error handling
137
109
  * - Proper connection pooling
138
110
  *
139
- * @param schema - Optional schema object for typed queries
111
+ * @param relations - Optional drizzle v1 relations config (defineRelations
112
+ * output) that enables the typed relational query API on the instance
140
113
  * @returns Raw Drizzle ORM database instance
141
114
  *
142
115
  * @example
143
116
  * ```typescript
144
117
  * // For legacy code that needs direct Drizzle access
145
- * const db = adapter.getDrizzle(mySchemas);
118
+ * const db = adapter.getDrizzle(myRelations); // defineRelations output
146
119
  * const result = await db.insert(users).values({ ... }).returning();
147
120
  * ```
148
121
  */
149
- abstract getDrizzle<T = unknown>(schema?: Record<string, unknown>): T;
122
+ abstract getDrizzle<T = unknown>(relations?: AnyRelations): T;
150
123
  /**
151
124
  * Table resolver for looking up Drizzle table objects by name.
152
125
  * When set, CRUD methods use Drizzle's query API instead of raw SQL.
@@ -167,6 +140,27 @@ declare abstract class DrizzleAdapter {
167
140
  * Returns null if no resolver is set or table is not found.
168
141
  */
169
142
  protected getTableObject(tableName: string): unknown;
143
+ /**
144
+ * Run a Drizzle-built statement and return its rows.
145
+ *
146
+ * @remarks
147
+ * The CRUD methods resolve their table through the schema registry and reject
148
+ * any name it does not declare, which leaves no way to read from a table the
149
+ * ORM does not know — one mid-rename, above all — except by assembling SQL and
150
+ * quoting identifiers by hand. This takes Drizzle's `sql` template instead, so
151
+ * the dialect in use decides the quoting and the parameter binding.
152
+ *
153
+ * Concrete rather than abstract so existing adapters keep working unchanged.
154
+ * The three drivers disagree about both the call and the result: node-postgres
155
+ * returns `{ rows }`, mysql2 a `[rows, fields]` tuple, and better-sqlite3 has
156
+ * no `execute` at all and answers `all`. Keeping that here rather than at each
157
+ * call site is the point — a caller reasoning about it would be reasoning
158
+ * about a driver it cannot see.
159
+ *
160
+ * @param statement - Drizzle `sql` template to run
161
+ * @returns Rows the statement produced
162
+ */
163
+ queryStatement<T = Record<string, unknown>>(statement: SQL): Promise<T[]>;
170
164
  /**
171
165
  * Map data keys from SQL column names (snake_case) to Drizzle JS property names (camelCase).
172
166
  * Drizzle schemas define columns as e.g. `createdAt: timestamp("created_at")` — the JS
@@ -174,6 +168,131 @@ declare abstract class DrizzleAdapter {
174
168
  * because they match the DB column names. This method maps them to the JS names Drizzle expects.
175
169
  */
176
170
  protected mapDataToColumnNames(tableObj: unknown, data: Record<string, unknown>): Record<string, unknown>;
171
+ /**
172
+ * Map data keys from Drizzle JS property names to SQL column names for the
173
+ * raw-SQL transaction insert path. The transaction context builds INSERT
174
+ * statements from Object.keys(data) used directly as column identifiers, so a
175
+ * table whose Drizzle property names differ from its SQL column names
176
+ * (camelCase core tables like nextly_versions) needs its keys translated
177
+ * first. For tables whose property names already equal their column names
178
+ * (the dynamic dc_/single_/comp_ tables) every lookup is identity, so
179
+ * existing callers are unaffected.
180
+ */
181
+ protected mapKeysToSqlColumns(tableObj: unknown, data: Record<string, unknown>): Record<string, unknown>;
182
+ /**
183
+ * Map a list of column identifiers (Drizzle property names) to their SQL
184
+ * column names, for the raw-SQL transaction insert paths that build a
185
+ * RETURNING clause from `options.returning`. Same identity behavior as
186
+ * `mapKeysToSqlColumns`: names that are already SQL columns (the dynamic
187
+ * dc_/single_/comp_ tables) pass through unchanged.
188
+ */
189
+ protected mapColumnNamesToSql(tableObj: unknown, names: string[]): string[];
190
+ /**
191
+ * Remap a raw-SQL result row's KEYS from SQL column names to Drizzle property
192
+ * names, so the raw-SQL transaction insert paths return the same key casing
193
+ * as the non-transactional (Drizzle) insert. Keys only - values are left
194
+ * untouched, so this does not change how JSON/date columns are decoded. For
195
+ * tables whose property names already equal their SQL columns (the dynamic
196
+ * dc_/single_/comp_ tables) every lookup is identity, so existing callers see
197
+ * no change.
198
+ */
199
+ protected mapRowKeysToJs<T = unknown>(tableObj: unknown, row: T): T;
200
+ /**
201
+ * Decode a raw-SQL result row's DATE column values the way a Drizzle query
202
+ * would, so a write answers with the same representation a read of the same
203
+ * column gives.
204
+ *
205
+ * A Drizzle query path runs every value through its column definition; a
206
+ * raw-SQL path does not, so a dialect that stores a timestamp as a number
207
+ * answers a write with that number while every read answers a `Date`. Only
208
+ * date columns are touched, and a value that is already a `Date` is left
209
+ * alone, so a driver that decodes on its own is unaffected.
210
+ *
211
+ * Runs after {@link mapRowKeysToJs}: keys are Drizzle property names by then,
212
+ * which is what the table definition is keyed by.
213
+ */
214
+ protected mapDateValuesFromDriver<T = unknown>(tableObj: unknown, row: T): T;
215
+ /**
216
+ * Encode a row's DATE values the way a Drizzle query would before binding
217
+ * them, so what lands in the column does not depend on the server's timezone.
218
+ *
219
+ * A column declared without a time zone stores a wall clock and records
220
+ * nothing about which zone it belongs to, so both ends have to agree. Drizzle
221
+ * writes UTC and reads UTC. A driver handed a `Date` writes the LOCAL wall
222
+ * clock instead, and the same row then reads back shifted by the offset --
223
+ * on a UTC server the two agree and nothing is visibly wrong, which is why
224
+ * this survives CI.
225
+ *
226
+ * Only date columns are touched. A value that is not a `Date` is left as it
227
+ * is, so a caller that already encoded one is not encoded twice.
228
+ *
229
+ * Keys may be spelled either way at this point, so both the Drizzle property
230
+ * name and the SQL column name resolve.
231
+ */
232
+ protected mapDateValuesToDriver(tableObj: unknown, data: Record<string, unknown>): Record<string, unknown>;
233
+ /**
234
+ * Bring a row about to be written through a raw-SQL statement into the shape
235
+ * a Drizzle query would have bound: SQL column names for keys, encoded values
236
+ * for date columns.
237
+ *
238
+ * The mirror of {@link mapRowFromRawSql}. The raw-SQL write paths exist
239
+ * because a dialect needs SQL a Drizzle query cannot express, not because
240
+ * their callers want different values in the table.
241
+ */
242
+ protected mapRowToRawSql(tableObj: unknown, data: Record<string, unknown>): Record<string, unknown>;
243
+ /**
244
+ * Bring a raw-SQL result row into the shape a Drizzle query would have
245
+ * produced: Drizzle property names for keys, decoded values for date columns.
246
+ *
247
+ * The raw-SQL write paths exist because a dialect needs SQL a Drizzle query
248
+ * cannot express, not because their callers want a different row shape, so
249
+ * every one of them ends here.
250
+ */
251
+ protected mapRowFromRawSql<T = unknown>(tableObj: unknown, row: T, aliases?: ReadonlyArray<{
252
+ alias: string;
253
+ jsName: string;
254
+ }>): T;
255
+ /**
256
+ * The date columns a statement is about to return, and the alias each one's
257
+ * wall clock is spelled out under.
258
+ *
259
+ * A driver turns a timestamp into a `Date` using ITS zone before any of this
260
+ * code runs, and that conversion is lossy: a wall clock inside a
261
+ * daylight-saving gap is a local time that does not exist, so the driver
262
+ * normalizes it and the original is gone. Asking the database for the wall
263
+ * clock as text alongside the row is the only way to read it exactly.
264
+ *
265
+ * `"*"` covers every date column; a projection covers only the ones it asked
266
+ * for, so a caller still gets back what it requested and nothing more.
267
+ */
268
+ protected dateWallClockAliases(tableObj: unknown, returning: string[] | "*" | undefined): Array<{
269
+ sqlName: string;
270
+ alias: string;
271
+ jsName: string;
272
+ }>;
273
+ /**
274
+ * Replace each date in a row with the wall clock the database spelled out,
275
+ * read as UTC, and drop the aliases that carried it.
276
+ *
277
+ * The statement writes a UTC wall clock, so reading one back as UTC is what
278
+ * makes a write and a later read agree. Whatever the column actually stored
279
+ * is what arrives -- a column keeping only whole seconds reports whole
280
+ * seconds -- because this is the database's own text, not a value
281
+ * reconstructed from the one that was bound.
282
+ */
283
+ protected applyWallClockAliases<T>(row: T, aliases: ReadonlyArray<{
284
+ alias: string;
285
+ jsName: string;
286
+ }>): T;
287
+ /**
288
+ * Build a Drizzle column projection object (`{ propertyName: column }`) from a
289
+ * requested column list, used by `select` (columns) and `insert` (returning).
290
+ * A requested name resolves against either the Drizzle property name
291
+ * (camelCase) or the SQL column name (snake_case); the projection is keyed by
292
+ * the property name so the row shape matches a full select. Returns undefined
293
+ * for `"*"` or when nothing resolves, so callers fall back to all columns.
294
+ */
295
+ protected buildColumnProjection(tableObj: unknown, names: string[] | "*" | undefined): Record<string, unknown> | undefined;
177
296
  /**
178
297
  * Check if the adapter is currently connected.
179
298
  *
@@ -286,7 +405,7 @@ declare abstract class DrizzleAdapter {
286
405
  * });
287
406
  * ```
288
407
  */
289
- select<T = unknown>(table: string, options?: SelectOptions): Promise<T[]>;
408
+ select<T = unknown>(table: string, options?: SelectOptions, executor?: unknown): Promise<T[]>;
290
409
  /**
291
410
  * Select a single record from a table.
292
411
  *
@@ -307,7 +426,7 @@ declare abstract class DrizzleAdapter {
307
426
  * });
308
427
  * ```
309
428
  */
310
- selectOne<T = unknown>(table: string, options?: SelectOptions): Promise<T | null>;
429
+ selectOne<T = unknown>(table: string, options?: SelectOptions, executor?: unknown): Promise<T | null>;
311
430
  /**
312
431
  * Insert a single record into a table.
313
432
  *
@@ -378,7 +497,24 @@ declare abstract class DrizzleAdapter {
378
497
  * );
379
498
  * ```
380
499
  */
381
- update<T = unknown>(table: string, data: Record<string, unknown>, where: WhereClause, options?: UpdateOptions): Promise<T[]>;
500
+ update<T = unknown>(table: string, data: Record<string, unknown>, where: WhereClause, options?: UpdateOptions, executor?: unknown): Promise<T[]>;
501
+ /**
502
+ * Update records in a table and report how many rows the statement affected.
503
+ *
504
+ * The count is the whole return value, mirroring `delete`. `update` cannot answer this: without
505
+ * `returning` it discards the driver's count, and WITH `returning` on a dialect that lacks
506
+ * RETURNING it re-SELECTs using the same WHERE — so a conditional update that just changed a
507
+ * column named in that WHERE reads back zero rows and a write that landed reports as unmatched.
508
+ * Reading the driver's own count has no second query to disagree with the first.
509
+ *
510
+ * 🔴 MySQL reports CHANGED rows, not matched rows: an UPDATE that matches a row but writes values
511
+ * identical to what it holds counts zero. A caller using this as a compare-and-set must therefore
512
+ * include a column the write always moves — a version bump, a timestamp with enough resolution —
513
+ * so that matched implies changed. Postgres (`rowCount`) and SQLite (`changes`) count matched
514
+ * rows and do not need the precaution, which is exactly why it cannot be dropped: the dialect
515
+ * where the distinction exists is the one with no RETURNING to fall back on.
516
+ */
517
+ updateCount(table: string, data: Record<string, unknown>, where: WhereClause, executor?: unknown): Promise<number>;
382
518
  /**
383
519
  * Delete records from a table.
384
520
  *
@@ -401,7 +537,7 @@ declare abstract class DrizzleAdapter {
401
537
  * console.log(`Deleted ${count} users`);
402
538
  * ```
403
539
  */
404
- delete(table: string, where: WhereClause, _options?: DeleteOptions): Promise<number>;
540
+ delete(table: string, where: WhereClause, _options?: DeleteOptions, executor?: unknown): Promise<number>;
405
541
  /**
406
542
  * Upsert (INSERT or UPDATE) a record.
407
543
  *
@@ -429,7 +565,7 @@ declare abstract class DrizzleAdapter {
429
565
  * });
430
566
  * ```
431
567
  */
432
- upsert<T = unknown>(table: string, data: Record<string, unknown>, options: UpsertOptions): Promise<T>;
568
+ upsert<T = unknown>(table: string, data: Record<string, unknown>, options: UpsertOptions, executor?: unknown): Promise<T>;
433
569
  /**
434
570
  * Run pending migrations.
435
571
  *
@@ -1,7 +1,8 @@
1
+ import { AnyRelations, SQL } from 'drizzle-orm';
1
2
  import { S as SupportedDialect, a as SqlParam, T as TableResolver } from './core-CVO7WYDj.js';
2
- import { T as TransactionContext, e as TransactionOptions, D as DatabaseCapabilities, P as PoolStats, S as SelectOptions, I as InsertOptions, W as WhereClause, U as UpdateOptions, f as DeleteOptions, g as UpsertOptions, a as Migration, d as MigrationResult } from './migration-Qe70wDOC.js';
3
+ import { T as TransactionContext, e as TransactionOptions, D as DatabaseCapabilities, P as PoolStats, S as SelectOptions, I as InsertOptions, W as WhereClause, U as UpdateOptions, f as DeleteOptions, g as UpsertOptions, a as Migration, d as MigrationResult } from './migration-Bj84e15B.js';
3
4
  import { T as TableDefinition, C as CreateTableOptions, D as DropTableOptions, A as AlterTableOperation, a as AlterTableOptions } from './schema-BIQ0YQZ_.js';
4
- import { D as DatabaseErrorKind, a as DatabaseError } from './error-um1d_3Uo.js';
5
+ import { D as DatabaseErrorKind, a as DatabaseError } from './error-BrdknH2s.js';
5
6
 
6
7
  /**
7
8
  * Base database adapter abstract class.
@@ -16,53 +17,6 @@ import { D as DatabaseErrorKind, a as DatabaseError } from './error-um1d_3Uo.js'
16
17
  * @packageDocumentation
17
18
  */
18
19
 
19
- /**
20
- * Abstract base class for database adapters.
21
- *
22
- * @remarks
23
- * All dialect-specific adapters must extend this class and implement the abstract methods.
24
- * Default implementations are provided for CRUD operations, which can be overridden for
25
- * optimization or dialect-specific behavior.
26
- *
27
- * ## Required Implementations
28
- *
29
- * Subclasses must implement:
30
- * - `dialect` - Database dialect identifier
31
- * - `connect()` - Establish database connection
32
- * - `disconnect()` - Close database connection
33
- * - `executeQuery()` - Execute raw SQL query
34
- * - `transaction()` - Execute operations within a transaction
35
- * - `getCapabilities()` - Report database feature support
36
- *
37
- * ## Optional Overrides
38
- *
39
- * Subclasses can override default CRUD methods for optimization:
40
- * - `select()`, `selectOne()` - Custom query optimization
41
- * - `insert()`, `insertMany()` - Bulk insert optimization
42
- * - `update()`, `delete()` - Custom update/delete logic
43
- * - `upsert()` - Dialect-specific upsert syntax
44
- *
45
- * @example
46
- * ```typescript
47
- * export class PostgresAdapter extends DrizzleAdapter {
48
- * readonly dialect = 'postgresql' as const;
49
- *
50
- * async connect() {
51
- * this.pool = new Pool({ connectionString: this.config.url });
52
- * // ... connection logic
53
- * }
54
- *
55
- * async executeQuery<T>(sql: string, params?: SqlParam[]) {
56
- * const result = await this.pool.query(sql, params);
57
- * return result.rows as T[];
58
- * }
59
- *
60
- * // ... other required methods
61
- * }
62
- * ```
63
- *
64
- * @public
65
- */
66
20
  declare abstract class DrizzleAdapter {
67
21
  /**
68
22
  * Database dialect identifier.
@@ -106,11 +60,29 @@ declare abstract class DrizzleAdapter {
106
60
  * The transaction is automatically committed on success or rolled back on error.
107
61
  * Supports nested transactions via savepoints on databases that support them.
108
62
  *
63
+ * Two kinds of error leave this method, and a caller has to be able to tell
64
+ * them apart. An error carrying the Nextly application brand — see
65
+ * `isApplicationError` — arrives exactly as it was thrown, because that is
66
+ * the application refusing the write rather than the database failing to
67
+ * perform it, and rewriting it would discard the code and payload the caller
68
+ * is meant to act on.
69
+ *
70
+ * Everything else arrives as a `DatabaseError` with its kind classified, and
71
+ * that deliberately includes an unbranded error the callback itself threw:
72
+ * the brand is the only thing distinguishing a deliberate refusal from a
73
+ * driver failure that surfaced through callback code, and guessing from the
74
+ * throw site would put raw driver text on the wire under a caller's own
75
+ * error shape.
76
+ *
77
+ * The rollback is the same either way.
78
+ *
109
79
  * @param callback - Function to execute within transaction
110
80
  * @param options - Transaction options
111
81
  * @returns Result from the callback
112
82
  *
113
- * @throws {DatabaseError} If transaction fails
83
+ * @throws {DatabaseError} If the transaction itself fails, or if the callback
84
+ * threw an error that does not carry the application brand
85
+ * @throws The callback's own error, unchanged, when it carries that brand
114
86
  */
115
87
  abstract transaction<T>(callback: (ctx: TransactionContext) => Promise<T>, options?: TransactionOptions): Promise<T>;
116
88
  /**
@@ -136,17 +108,18 @@ declare abstract class DrizzleAdapter {
136
108
  * - Consistent error handling
137
109
  * - Proper connection pooling
138
110
  *
139
- * @param schema - Optional schema object for typed queries
111
+ * @param relations - Optional drizzle v1 relations config (defineRelations
112
+ * output) that enables the typed relational query API on the instance
140
113
  * @returns Raw Drizzle ORM database instance
141
114
  *
142
115
  * @example
143
116
  * ```typescript
144
117
  * // For legacy code that needs direct Drizzle access
145
- * const db = adapter.getDrizzle(mySchemas);
118
+ * const db = adapter.getDrizzle(myRelations); // defineRelations output
146
119
  * const result = await db.insert(users).values({ ... }).returning();
147
120
  * ```
148
121
  */
149
- abstract getDrizzle<T = unknown>(schema?: Record<string, unknown>): T;
122
+ abstract getDrizzle<T = unknown>(relations?: AnyRelations): T;
150
123
  /**
151
124
  * Table resolver for looking up Drizzle table objects by name.
152
125
  * When set, CRUD methods use Drizzle's query API instead of raw SQL.
@@ -167,6 +140,27 @@ declare abstract class DrizzleAdapter {
167
140
  * Returns null if no resolver is set or table is not found.
168
141
  */
169
142
  protected getTableObject(tableName: string): unknown;
143
+ /**
144
+ * Run a Drizzle-built statement and return its rows.
145
+ *
146
+ * @remarks
147
+ * The CRUD methods resolve their table through the schema registry and reject
148
+ * any name it does not declare, which leaves no way to read from a table the
149
+ * ORM does not know — one mid-rename, above all — except by assembling SQL and
150
+ * quoting identifiers by hand. This takes Drizzle's `sql` template instead, so
151
+ * the dialect in use decides the quoting and the parameter binding.
152
+ *
153
+ * Concrete rather than abstract so existing adapters keep working unchanged.
154
+ * The three drivers disagree about both the call and the result: node-postgres
155
+ * returns `{ rows }`, mysql2 a `[rows, fields]` tuple, and better-sqlite3 has
156
+ * no `execute` at all and answers `all`. Keeping that here rather than at each
157
+ * call site is the point — a caller reasoning about it would be reasoning
158
+ * about a driver it cannot see.
159
+ *
160
+ * @param statement - Drizzle `sql` template to run
161
+ * @returns Rows the statement produced
162
+ */
163
+ queryStatement<T = Record<string, unknown>>(statement: SQL): Promise<T[]>;
170
164
  /**
171
165
  * Map data keys from SQL column names (snake_case) to Drizzle JS property names (camelCase).
172
166
  * Drizzle schemas define columns as e.g. `createdAt: timestamp("created_at")` — the JS
@@ -174,6 +168,131 @@ declare abstract class DrizzleAdapter {
174
168
  * because they match the DB column names. This method maps them to the JS names Drizzle expects.
175
169
  */
176
170
  protected mapDataToColumnNames(tableObj: unknown, data: Record<string, unknown>): Record<string, unknown>;
171
+ /**
172
+ * Map data keys from Drizzle JS property names to SQL column names for the
173
+ * raw-SQL transaction insert path. The transaction context builds INSERT
174
+ * statements from Object.keys(data) used directly as column identifiers, so a
175
+ * table whose Drizzle property names differ from its SQL column names
176
+ * (camelCase core tables like nextly_versions) needs its keys translated
177
+ * first. For tables whose property names already equal their column names
178
+ * (the dynamic dc_/single_/comp_ tables) every lookup is identity, so
179
+ * existing callers are unaffected.
180
+ */
181
+ protected mapKeysToSqlColumns(tableObj: unknown, data: Record<string, unknown>): Record<string, unknown>;
182
+ /**
183
+ * Map a list of column identifiers (Drizzle property names) to their SQL
184
+ * column names, for the raw-SQL transaction insert paths that build a
185
+ * RETURNING clause from `options.returning`. Same identity behavior as
186
+ * `mapKeysToSqlColumns`: names that are already SQL columns (the dynamic
187
+ * dc_/single_/comp_ tables) pass through unchanged.
188
+ */
189
+ protected mapColumnNamesToSql(tableObj: unknown, names: string[]): string[];
190
+ /**
191
+ * Remap a raw-SQL result row's KEYS from SQL column names to Drizzle property
192
+ * names, so the raw-SQL transaction insert paths return the same key casing
193
+ * as the non-transactional (Drizzle) insert. Keys only - values are left
194
+ * untouched, so this does not change how JSON/date columns are decoded. For
195
+ * tables whose property names already equal their SQL columns (the dynamic
196
+ * dc_/single_/comp_ tables) every lookup is identity, so existing callers see
197
+ * no change.
198
+ */
199
+ protected mapRowKeysToJs<T = unknown>(tableObj: unknown, row: T): T;
200
+ /**
201
+ * Decode a raw-SQL result row's DATE column values the way a Drizzle query
202
+ * would, so a write answers with the same representation a read of the same
203
+ * column gives.
204
+ *
205
+ * A Drizzle query path runs every value through its column definition; a
206
+ * raw-SQL path does not, so a dialect that stores a timestamp as a number
207
+ * answers a write with that number while every read answers a `Date`. Only
208
+ * date columns are touched, and a value that is already a `Date` is left
209
+ * alone, so a driver that decodes on its own is unaffected.
210
+ *
211
+ * Runs after {@link mapRowKeysToJs}: keys are Drizzle property names by then,
212
+ * which is what the table definition is keyed by.
213
+ */
214
+ protected mapDateValuesFromDriver<T = unknown>(tableObj: unknown, row: T): T;
215
+ /**
216
+ * Encode a row's DATE values the way a Drizzle query would before binding
217
+ * them, so what lands in the column does not depend on the server's timezone.
218
+ *
219
+ * A column declared without a time zone stores a wall clock and records
220
+ * nothing about which zone it belongs to, so both ends have to agree. Drizzle
221
+ * writes UTC and reads UTC. A driver handed a `Date` writes the LOCAL wall
222
+ * clock instead, and the same row then reads back shifted by the offset --
223
+ * on a UTC server the two agree and nothing is visibly wrong, which is why
224
+ * this survives CI.
225
+ *
226
+ * Only date columns are touched. A value that is not a `Date` is left as it
227
+ * is, so a caller that already encoded one is not encoded twice.
228
+ *
229
+ * Keys may be spelled either way at this point, so both the Drizzle property
230
+ * name and the SQL column name resolve.
231
+ */
232
+ protected mapDateValuesToDriver(tableObj: unknown, data: Record<string, unknown>): Record<string, unknown>;
233
+ /**
234
+ * Bring a row about to be written through a raw-SQL statement into the shape
235
+ * a Drizzle query would have bound: SQL column names for keys, encoded values
236
+ * for date columns.
237
+ *
238
+ * The mirror of {@link mapRowFromRawSql}. The raw-SQL write paths exist
239
+ * because a dialect needs SQL a Drizzle query cannot express, not because
240
+ * their callers want different values in the table.
241
+ */
242
+ protected mapRowToRawSql(tableObj: unknown, data: Record<string, unknown>): Record<string, unknown>;
243
+ /**
244
+ * Bring a raw-SQL result row into the shape a Drizzle query would have
245
+ * produced: Drizzle property names for keys, decoded values for date columns.
246
+ *
247
+ * The raw-SQL write paths exist because a dialect needs SQL a Drizzle query
248
+ * cannot express, not because their callers want a different row shape, so
249
+ * every one of them ends here.
250
+ */
251
+ protected mapRowFromRawSql<T = unknown>(tableObj: unknown, row: T, aliases?: ReadonlyArray<{
252
+ alias: string;
253
+ jsName: string;
254
+ }>): T;
255
+ /**
256
+ * The date columns a statement is about to return, and the alias each one's
257
+ * wall clock is spelled out under.
258
+ *
259
+ * A driver turns a timestamp into a `Date` using ITS zone before any of this
260
+ * code runs, and that conversion is lossy: a wall clock inside a
261
+ * daylight-saving gap is a local time that does not exist, so the driver
262
+ * normalizes it and the original is gone. Asking the database for the wall
263
+ * clock as text alongside the row is the only way to read it exactly.
264
+ *
265
+ * `"*"` covers every date column; a projection covers only the ones it asked
266
+ * for, so a caller still gets back what it requested and nothing more.
267
+ */
268
+ protected dateWallClockAliases(tableObj: unknown, returning: string[] | "*" | undefined): Array<{
269
+ sqlName: string;
270
+ alias: string;
271
+ jsName: string;
272
+ }>;
273
+ /**
274
+ * Replace each date in a row with the wall clock the database spelled out,
275
+ * read as UTC, and drop the aliases that carried it.
276
+ *
277
+ * The statement writes a UTC wall clock, so reading one back as UTC is what
278
+ * makes a write and a later read agree. Whatever the column actually stored
279
+ * is what arrives -- a column keeping only whole seconds reports whole
280
+ * seconds -- because this is the database's own text, not a value
281
+ * reconstructed from the one that was bound.
282
+ */
283
+ protected applyWallClockAliases<T>(row: T, aliases: ReadonlyArray<{
284
+ alias: string;
285
+ jsName: string;
286
+ }>): T;
287
+ /**
288
+ * Build a Drizzle column projection object (`{ propertyName: column }`) from a
289
+ * requested column list, used by `select` (columns) and `insert` (returning).
290
+ * A requested name resolves against either the Drizzle property name
291
+ * (camelCase) or the SQL column name (snake_case); the projection is keyed by
292
+ * the property name so the row shape matches a full select. Returns undefined
293
+ * for `"*"` or when nothing resolves, so callers fall back to all columns.
294
+ */
295
+ protected buildColumnProjection(tableObj: unknown, names: string[] | "*" | undefined): Record<string, unknown> | undefined;
177
296
  /**
178
297
  * Check if the adapter is currently connected.
179
298
  *
@@ -286,7 +405,7 @@ declare abstract class DrizzleAdapter {
286
405
  * });
287
406
  * ```
288
407
  */
289
- select<T = unknown>(table: string, options?: SelectOptions): Promise<T[]>;
408
+ select<T = unknown>(table: string, options?: SelectOptions, executor?: unknown): Promise<T[]>;
290
409
  /**
291
410
  * Select a single record from a table.
292
411
  *
@@ -307,7 +426,7 @@ declare abstract class DrizzleAdapter {
307
426
  * });
308
427
  * ```
309
428
  */
310
- selectOne<T = unknown>(table: string, options?: SelectOptions): Promise<T | null>;
429
+ selectOne<T = unknown>(table: string, options?: SelectOptions, executor?: unknown): Promise<T | null>;
311
430
  /**
312
431
  * Insert a single record into a table.
313
432
  *
@@ -378,7 +497,24 @@ declare abstract class DrizzleAdapter {
378
497
  * );
379
498
  * ```
380
499
  */
381
- update<T = unknown>(table: string, data: Record<string, unknown>, where: WhereClause, options?: UpdateOptions): Promise<T[]>;
500
+ update<T = unknown>(table: string, data: Record<string, unknown>, where: WhereClause, options?: UpdateOptions, executor?: unknown): Promise<T[]>;
501
+ /**
502
+ * Update records in a table and report how many rows the statement affected.
503
+ *
504
+ * The count is the whole return value, mirroring `delete`. `update` cannot answer this: without
505
+ * `returning` it discards the driver's count, and WITH `returning` on a dialect that lacks
506
+ * RETURNING it re-SELECTs using the same WHERE — so a conditional update that just changed a
507
+ * column named in that WHERE reads back zero rows and a write that landed reports as unmatched.
508
+ * Reading the driver's own count has no second query to disagree with the first.
509
+ *
510
+ * 🔴 MySQL reports CHANGED rows, not matched rows: an UPDATE that matches a row but writes values
511
+ * identical to what it holds counts zero. A caller using this as a compare-and-set must therefore
512
+ * include a column the write always moves — a version bump, a timestamp with enough resolution —
513
+ * so that matched implies changed. Postgres (`rowCount`) and SQLite (`changes`) count matched
514
+ * rows and do not need the precaution, which is exactly why it cannot be dropped: the dialect
515
+ * where the distinction exists is the one with no RETURNING to fall back on.
516
+ */
517
+ updateCount(table: string, data: Record<string, unknown>, where: WhereClause, executor?: unknown): Promise<number>;
382
518
  /**
383
519
  * Delete records from a table.
384
520
  *
@@ -401,7 +537,7 @@ declare abstract class DrizzleAdapter {
401
537
  * console.log(`Deleted ${count} users`);
402
538
  * ```
403
539
  */
404
- delete(table: string, where: WhereClause, _options?: DeleteOptions): Promise<number>;
540
+ delete(table: string, where: WhereClause, _options?: DeleteOptions, executor?: unknown): Promise<number>;
405
541
  /**
406
542
  * Upsert (INSERT or UPDATE) a record.
407
543
  *
@@ -429,7 +565,7 @@ declare abstract class DrizzleAdapter {
429
565
  * });
430
566
  * ```
431
567
  */
432
- upsert<T = unknown>(table: string, data: Record<string, unknown>, options: UpsertOptions): Promise<T>;
568
+ upsert<T = unknown>(table: string, data: Record<string, unknown>, options: UpsertOptions, executor?: unknown): Promise<T>;
433
569
  /**
434
570
  * Run pending migrations.
435
571
  *