@nextlyhq/adapter-drizzle 0.0.2-alpha.5 → 0.0.2-alpha.51

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/index.cjs CHANGED
@@ -4,7 +4,7 @@ var drizzleOrm = require('drizzle-orm');
4
4
 
5
5
  // src/adapter.ts
6
6
  function buildDrizzleWhere(table, where) {
7
- const columns = drizzleOrm.getTableColumns(table);
7
+ const columns = drizzleOrm.getColumns(table);
8
8
  return processWhereClause(columns, where);
9
9
  }
10
10
  function processWhereClause(columns, where) {
@@ -108,6 +108,21 @@ function createDatabaseError(options) {
108
108
  }
109
109
 
110
110
  // src/adapter.ts
111
+ function affectedRowCount(result) {
112
+ if (Array.isArray(result)) {
113
+ const header = result[0];
114
+ if (header && typeof header.affectedRows === "number") {
115
+ return header.affectedRows;
116
+ }
117
+ return result.length;
118
+ }
119
+ const record = result;
120
+ const rowCount = record?.rowCount;
121
+ if (typeof rowCount === "number") return rowCount;
122
+ const changes = record?.changes;
123
+ if (typeof changes === "number") return changes;
124
+ return 0;
125
+ }
111
126
  var DrizzleAdapter = class {
112
127
  // ============================================================
113
128
  // Drizzle Query API Support
@@ -136,6 +151,45 @@ var DrizzleAdapter = class {
136
151
  getTableObject(tableName) {
137
152
  return this.tableResolver?.getTable(tableName) ?? null;
138
153
  }
154
+ /**
155
+ * Run a Drizzle-built statement and return its rows.
156
+ *
157
+ * @remarks
158
+ * The CRUD methods resolve their table through the schema registry and reject
159
+ * any name it does not declare, which leaves no way to read from a table the
160
+ * ORM does not know — one mid-rename, above all — except by assembling SQL and
161
+ * quoting identifiers by hand. This takes Drizzle's `sql` template instead, so
162
+ * the dialect in use decides the quoting and the parameter binding.
163
+ *
164
+ * Concrete rather than abstract so existing adapters keep working unchanged.
165
+ * The three drivers disagree about both the call and the result: node-postgres
166
+ * returns `{ rows }`, mysql2 a `[rows, fields]` tuple, and better-sqlite3 has
167
+ * no `execute` at all and answers `all`. Keeping that here rather than at each
168
+ * call site is the point — a caller reasoning about it would be reasoning
169
+ * about a driver it cannot see.
170
+ *
171
+ * @param statement - Drizzle `sql` template to run
172
+ * @returns Rows the statement produced
173
+ */
174
+ async queryStatement(statement) {
175
+ if (this.getCapabilities().dialect === "sqlite") {
176
+ const db2 = this.getDrizzle();
177
+ return await db2.all(statement);
178
+ }
179
+ const db = this.getDrizzle();
180
+ const result = await db.execute(statement);
181
+ if (Array.isArray(result)) {
182
+ return Array.isArray(result[0]) ? result[0] : result;
183
+ }
184
+ if (typeof result === "object" && result !== null && Array.isArray(result.rows)) {
185
+ return result.rows;
186
+ }
187
+ throw this.createDatabaseError(
188
+ "query",
189
+ "Drizzle statement returned a result shape this adapter does not recognise; refusing to report it as an empty result.",
190
+ void 0
191
+ );
192
+ }
139
193
  /**
140
194
  * Map data keys from SQL column names (snake_case) to Drizzle JS property names (camelCase).
141
195
  * Drizzle schemas define columns as e.g. `createdAt: timestamp("created_at")` — the JS
@@ -175,6 +229,107 @@ var DrizzleAdapter = class {
175
229
  }
176
230
  return mapped;
177
231
  }
232
+ /**
233
+ * Map data keys from Drizzle JS property names to SQL column names for the
234
+ * raw-SQL transaction insert path. The transaction context builds INSERT
235
+ * statements from Object.keys(data) used directly as column identifiers, so a
236
+ * table whose Drizzle property names differ from its SQL column names
237
+ * (camelCase core tables like nextly_versions) needs its keys translated
238
+ * first. For tables whose property names already equal their column names
239
+ * (the dynamic dc_/single_/comp_ tables) every lookup is identity, so
240
+ * existing callers are unaffected.
241
+ */
242
+ mapKeysToSqlColumns(tableObj, data) {
243
+ if (!tableObj || typeof tableObj !== "object") return data;
244
+ const jsToSql = /* @__PURE__ */ new Map();
245
+ for (const [jsName, colDef] of Object.entries(
246
+ tableObj
247
+ )) {
248
+ if (colDef && typeof colDef === "object" && "name" in colDef && typeof colDef.name === "string") {
249
+ jsToSql.set(jsName, colDef.name);
250
+ }
251
+ }
252
+ if (jsToSql.size === 0) return data;
253
+ const out = {};
254
+ for (const [key, value] of Object.entries(data)) {
255
+ out[jsToSql.get(key) ?? key] = value;
256
+ }
257
+ return out;
258
+ }
259
+ /**
260
+ * Map a list of column identifiers (Drizzle property names) to their SQL
261
+ * column names, for the raw-SQL transaction insert paths that build a
262
+ * RETURNING clause from `options.returning`. Same identity behavior as
263
+ * `mapKeysToSqlColumns`: names that are already SQL columns (the dynamic
264
+ * dc_/single_/comp_ tables) pass through unchanged.
265
+ */
266
+ mapColumnNamesToSql(tableObj, names) {
267
+ if (!tableObj || typeof tableObj !== "object") return names;
268
+ const jsToSql = /* @__PURE__ */ new Map();
269
+ for (const [jsName, colDef] of Object.entries(
270
+ tableObj
271
+ )) {
272
+ if (colDef && typeof colDef === "object" && "name" in colDef && typeof colDef.name === "string") {
273
+ jsToSql.set(jsName, colDef.name);
274
+ }
275
+ }
276
+ if (jsToSql.size === 0) return names;
277
+ return names.map((n) => jsToSql.get(n) ?? n);
278
+ }
279
+ /**
280
+ * Remap a raw-SQL result row's KEYS from SQL column names to Drizzle property
281
+ * names, so the raw-SQL transaction insert paths return the same key casing
282
+ * as the non-transactional (Drizzle) insert. Keys only - values are left
283
+ * untouched, so this does not change how JSON/date columns are decoded. For
284
+ * tables whose property names already equal their SQL columns (the dynamic
285
+ * dc_/single_/comp_ tables) every lookup is identity, so existing callers see
286
+ * no change.
287
+ */
288
+ mapRowKeysToJs(tableObj, row) {
289
+ if (!tableObj || typeof tableObj !== "object" || !row || typeof row !== "object") {
290
+ return row;
291
+ }
292
+ const sqlToJs = /* @__PURE__ */ new Map();
293
+ for (const [jsName, colDef] of Object.entries(
294
+ tableObj
295
+ )) {
296
+ if (colDef && typeof colDef === "object" && "name" in colDef && typeof colDef.name === "string") {
297
+ sqlToJs.set(colDef.name, jsName);
298
+ }
299
+ }
300
+ if (sqlToJs.size === 0) return row;
301
+ const out = {};
302
+ for (const [key, value] of Object.entries(row)) {
303
+ out[sqlToJs.get(key) ?? key] = value;
304
+ }
305
+ return out;
306
+ }
307
+ /**
308
+ * Build a Drizzle column projection object (`{ propertyName: column }`) from a
309
+ * requested column list, used by `select` (columns) and `insert` (returning).
310
+ * A requested name resolves against either the Drizzle property name
311
+ * (camelCase) or the SQL column name (snake_case); the projection is keyed by
312
+ * the property name so the row shape matches a full select. Returns undefined
313
+ * for `"*"` or when nothing resolves, so callers fall back to all columns.
314
+ */
315
+ buildColumnProjection(tableObj, names) {
316
+ if (names == null || names === "*" || !tableObj || typeof tableObj !== "object") {
317
+ return void 0;
318
+ }
319
+ const cols = drizzleOrm.getColumns(tableObj);
320
+ const byAnyName = {};
321
+ for (const [jsName, col] of Object.entries(cols)) {
322
+ byAnyName[jsName] = { jsName, col };
323
+ const sqlName = col?.name;
324
+ if (typeof sqlName === "string") byAnyName[sqlName] = { jsName, col };
325
+ }
326
+ const projection = {};
327
+ for (const name of names) {
328
+ const hit = byAnyName[name];
329
+ if (hit) projection[hit.jsName] = hit.col;
330
+ }
331
+ return Object.keys(projection).length ? projection : void 0;
332
+ }
178
333
  // ============================================================
179
334
  // Connection Status (Default implementations, can override)
180
335
  // ============================================================
@@ -328,12 +483,13 @@ var DrizzleAdapter = class {
328
483
  * });
329
484
  * ```
330
485
  */
331
- async select(table, options) {
486
+ async select(table, options, executor) {
332
487
  const tableObj = this.getTableObject(table);
333
488
  if (tableObj) {
334
489
  try {
335
- const db = this.getDrizzle();
336
- let query = db.select().from(tableObj);
490
+ const db = executor ?? this.getDrizzle();
491
+ const projection = options?.columns?.length ? this.buildColumnProjection(tableObj, options.columns) : void 0;
492
+ let query = projection ? db.select(projection).from(tableObj) : db.select().from(tableObj);
337
493
  if (options?.where) {
338
494
  const whereCondition = buildDrizzleWhere(
339
495
  tableObj,
@@ -344,7 +500,7 @@ var DrizzleAdapter = class {
344
500
  }
345
501
  }
346
502
  if (options?.orderBy?.length) {
347
- const columns = drizzleOrm.getTableColumns(tableObj);
503
+ const columns = drizzleOrm.getColumns(tableObj);
348
504
  const orderClauses = options.orderBy.map((o) => {
349
505
  const col = columns[o.column];
350
506
  if (!col) return void 0;
@@ -360,6 +516,16 @@ var DrizzleAdapter = class {
360
516
  if (options?.offset !== void 0) {
361
517
  query = query.offset(options.offset);
362
518
  }
519
+ if (options?.forUpdate && !executor) {
520
+ throw this.createDatabaseError(
521
+ "query",
522
+ "forUpdate requires a transaction executor: a lock request on the pooled connection takes no durable lock and cannot prevent a concurrent write.",
523
+ void 0
524
+ );
525
+ }
526
+ if (options?.forUpdate && this.dialect !== "sqlite") {
527
+ query = query.for("update");
528
+ }
363
529
  return await query;
364
530
  } catch (error) {
365
531
  throw this.handleQueryError(error, "select", table);
@@ -391,8 +557,12 @@ var DrizzleAdapter = class {
391
557
  * });
392
558
  * ```
393
559
  */
394
- async selectOne(table, options) {
395
- const results = await this.select(table, { ...options, limit: 1 });
560
+ async selectOne(table, options, executor) {
561
+ const results = await this.select(
562
+ table,
563
+ { ...options, limit: 1 },
564
+ executor
565
+ );
396
566
  return results.length > 0 ? results[0] : null;
397
567
  }
398
568
  /**
@@ -424,19 +594,22 @@ var DrizzleAdapter = class {
424
594
  const mappedData = this.mapDataToColumnNames(tableObj, data);
425
595
  const db = this.getDrizzle();
426
596
  const caps = this.getCapabilities();
427
- if (caps.supportsReturning && options?.returning) {
428
- const result2 = await db.insert(tableObj).values(mappedData).returning();
597
+ const returning = options?.returning;
598
+ const wantsReturning = returning != null && !(Array.isArray(returning) && returning.length === 0);
599
+ if (caps.supportsReturning && wantsReturning) {
600
+ const projection = this.buildColumnProjection(tableObj, returning);
601
+ const insertQuery = db.insert(tableObj).values(mappedData);
602
+ const result2 = await (projection ? insertQuery.returning(projection) : insertQuery.returning());
429
603
  return Array.isArray(result2) ? result2[0] : result2;
430
604
  }
431
605
  const result = await db.insert(tableObj).values(mappedData);
432
- if (!caps.supportsReturning && options?.returning) {
433
- if (data.id !== void 0) {
434
- return await this.selectOne(table, {
435
- where: {
436
- and: [{ column: "id", op: "=", value: data.id }]
437
- }
438
- });
439
- }
606
+ if (!caps.supportsReturning && wantsReturning && data.id !== void 0) {
607
+ return await this.selectOne(table, {
608
+ columns: returning === "*" ? void 0 : returning,
609
+ where: {
610
+ and: [{ column: "id", op: "=", value: data.id }]
611
+ }
612
+ });
440
613
  }
441
614
  return Array.isArray(result) ? result[0] : result;
442
615
  } catch (error) {
@@ -506,11 +679,11 @@ var DrizzleAdapter = class {
506
679
  * );
507
680
  * ```
508
681
  */
509
- async update(table, data, where, options) {
682
+ async update(table, data, where, options, executor) {
510
683
  const tableObj = this.getTableObject(table);
511
684
  if (tableObj) {
512
685
  try {
513
- const db = this.getDrizzle();
686
+ const db = executor ?? this.getDrizzle();
514
687
  const caps = this.getCapabilities();
515
688
  const mappedData = this.mapDataToColumnNames(tableObj, data);
516
689
  let query = db.update(tableObj).set(mappedData);
@@ -523,7 +696,7 @@ var DrizzleAdapter = class {
523
696
  }
524
697
  await query;
525
698
  if (!caps.supportsReturning && options?.returning) {
526
- return await this.select(table, { where });
699
+ return await this.select(table, { where }, executor);
527
700
  }
528
701
  return [];
529
702
  } catch (error) {
@@ -558,18 +731,18 @@ var DrizzleAdapter = class {
558
731
  * console.log(`Deleted ${count} users`);
559
732
  * ```
560
733
  */
561
- async delete(table, where, _options) {
734
+ async delete(table, where, _options, executor) {
562
735
  const tableObj = this.getTableObject(table);
563
736
  if (tableObj) {
564
737
  try {
565
- const db = this.getDrizzle();
738
+ const db = executor ?? this.getDrizzle();
566
739
  let query = db.delete(tableObj);
567
740
  const whereCondition = buildDrizzleWhere(tableObj, where);
568
741
  if (whereCondition) {
569
742
  query = query.where(whereCondition);
570
743
  }
571
744
  const result = await query;
572
- return Array.isArray(result) ? result.length : result?.rowCount ?? result?.changes ?? 0;
745
+ return affectedRowCount(result);
573
746
  } catch (error) {
574
747
  throw this.handleQueryError(error, "delete", table);
575
748
  }
@@ -607,13 +780,13 @@ var DrizzleAdapter = class {
607
780
  * });
608
781
  * ```
609
782
  */
610
- async upsert(table, data, options) {
783
+ async upsert(table, data, options, executor) {
611
784
  const tableObj = this.getTableObject(table);
612
785
  if (tableObj) {
613
786
  try {
614
- const db = this.getDrizzle();
787
+ const db = executor ?? this.getDrizzle();
615
788
  const caps = this.getCapabilities();
616
- const columns = drizzleOrm.getTableColumns(tableObj);
789
+ const columns = drizzleOrm.getColumns(tableObj);
617
790
  const conflictTarget = options.conflictColumns.map((col) => columns[col]).filter(Boolean);
618
791
  const conflictSet = new Set(options.conflictColumns);
619
792
  const updateData = {};
@@ -638,17 +811,21 @@ var DrizzleAdapter = class {
638
811
  }
639
812
  await query;
640
813
  if (options.conflictColumns.length && data[options.conflictColumns[0]] !== void 0) {
641
- return await this.selectOne(table, {
642
- where: {
643
- and: [
644
- {
645
- column: options.conflictColumns[0],
646
- op: "=",
647
- value: data[options.conflictColumns[0]]
648
- }
649
- ]
650
- }
651
- });
814
+ return await this.selectOne(
815
+ table,
816
+ {
817
+ where: {
818
+ and: [
819
+ {
820
+ column: options.conflictColumns[0],
821
+ op: "=",
822
+ value: data[options.conflictColumns[0]]
823
+ }
824
+ ]
825
+ }
826
+ },
827
+ executor
828
+ );
652
829
  }
653
830
  return data;
654
831
  } catch (error) {
@@ -974,7 +1151,7 @@ var DrizzleAdapter = class {
974
1151
  break;
975
1152
  case "mysql":
976
1153
  sql = `
977
- SELECT table_name
1154
+ SELECT table_name AS table_name
978
1155
  FROM information_schema.tables
979
1156
  WHERE table_schema = DATABASE()
980
1157
  AND table_type = 'BASE TABLE'