@nextlyhq/adapter-drizzle 0.0.2-alpha.4 → 0.0.2-alpha.41

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.mjs CHANGED
@@ -1,8 +1,8 @@
1
- import { getTableColumns, desc, asc, and, or, not, like, notBetween, between, isNotNull, isNull, notInArray, inArray, ilike, lte, gte, lt, gt, ne, eq } from 'drizzle-orm';
1
+ import { getColumns, desc, asc, and, or, not, like, notBetween, between, isNotNull, isNull, notInArray, inArray, ilike, lte, gte, lt, gt, ne, eq } from 'drizzle-orm';
2
2
 
3
3
  // src/adapter.ts
4
4
  function buildDrizzleWhere(table, where) {
5
- const columns = getTableColumns(table);
5
+ const columns = getColumns(table);
6
6
  return processWhereClause(columns, where);
7
7
  }
8
8
  function processWhereClause(columns, where) {
@@ -106,6 +106,21 @@ function createDatabaseError(options) {
106
106
  }
107
107
 
108
108
  // src/adapter.ts
109
+ function affectedRowCount(result) {
110
+ if (Array.isArray(result)) {
111
+ const header = result[0];
112
+ if (header && typeof header.affectedRows === "number") {
113
+ return header.affectedRows;
114
+ }
115
+ return result.length;
116
+ }
117
+ const record = result;
118
+ const rowCount = record?.rowCount;
119
+ if (typeof rowCount === "number") return rowCount;
120
+ const changes = record?.changes;
121
+ if (typeof changes === "number") return changes;
122
+ return 0;
123
+ }
109
124
  var DrizzleAdapter = class {
110
125
  // ============================================================
111
126
  // Drizzle Query API Support
@@ -173,6 +188,107 @@ var DrizzleAdapter = class {
173
188
  }
174
189
  return mapped;
175
190
  }
191
+ /**
192
+ * Map data keys from Drizzle JS property names to SQL column names for the
193
+ * raw-SQL transaction insert path. The transaction context builds INSERT
194
+ * statements from Object.keys(data) used directly as column identifiers, so a
195
+ * table whose Drizzle property names differ from its SQL column names
196
+ * (camelCase core tables like nextly_versions) needs its keys translated
197
+ * first. For tables whose property names already equal their column names
198
+ * (the dynamic dc_/single_/comp_ tables) every lookup is identity, so
199
+ * existing callers are unaffected.
200
+ */
201
+ mapKeysToSqlColumns(tableObj, data) {
202
+ if (!tableObj || typeof tableObj !== "object") return data;
203
+ const jsToSql = /* @__PURE__ */ new Map();
204
+ for (const [jsName, colDef] of Object.entries(
205
+ tableObj
206
+ )) {
207
+ if (colDef && typeof colDef === "object" && "name" in colDef && typeof colDef.name === "string") {
208
+ jsToSql.set(jsName, colDef.name);
209
+ }
210
+ }
211
+ if (jsToSql.size === 0) return data;
212
+ const out = {};
213
+ for (const [key, value] of Object.entries(data)) {
214
+ out[jsToSql.get(key) ?? key] = value;
215
+ }
216
+ return out;
217
+ }
218
+ /**
219
+ * Map a list of column identifiers (Drizzle property names) to their SQL
220
+ * column names, for the raw-SQL transaction insert paths that build a
221
+ * RETURNING clause from `options.returning`. Same identity behavior as
222
+ * `mapKeysToSqlColumns`: names that are already SQL columns (the dynamic
223
+ * dc_/single_/comp_ tables) pass through unchanged.
224
+ */
225
+ mapColumnNamesToSql(tableObj, names) {
226
+ if (!tableObj || typeof tableObj !== "object") return names;
227
+ const jsToSql = /* @__PURE__ */ new Map();
228
+ for (const [jsName, colDef] of Object.entries(
229
+ tableObj
230
+ )) {
231
+ if (colDef && typeof colDef === "object" && "name" in colDef && typeof colDef.name === "string") {
232
+ jsToSql.set(jsName, colDef.name);
233
+ }
234
+ }
235
+ if (jsToSql.size === 0) return names;
236
+ return names.map((n) => jsToSql.get(n) ?? n);
237
+ }
238
+ /**
239
+ * Remap a raw-SQL result row's KEYS from SQL column names to Drizzle property
240
+ * names, so the raw-SQL transaction insert paths return the same key casing
241
+ * as the non-transactional (Drizzle) insert. Keys only - values are left
242
+ * untouched, so this does not change how JSON/date columns are decoded. For
243
+ * tables whose property names already equal their SQL columns (the dynamic
244
+ * dc_/single_/comp_ tables) every lookup is identity, so existing callers see
245
+ * no change.
246
+ */
247
+ mapRowKeysToJs(tableObj, row) {
248
+ if (!tableObj || typeof tableObj !== "object" || !row || typeof row !== "object") {
249
+ return row;
250
+ }
251
+ const sqlToJs = /* @__PURE__ */ new Map();
252
+ for (const [jsName, colDef] of Object.entries(
253
+ tableObj
254
+ )) {
255
+ if (colDef && typeof colDef === "object" && "name" in colDef && typeof colDef.name === "string") {
256
+ sqlToJs.set(colDef.name, jsName);
257
+ }
258
+ }
259
+ if (sqlToJs.size === 0) return row;
260
+ const out = {};
261
+ for (const [key, value] of Object.entries(row)) {
262
+ out[sqlToJs.get(key) ?? key] = value;
263
+ }
264
+ return out;
265
+ }
266
+ /**
267
+ * Build a Drizzle column projection object (`{ propertyName: column }`) from a
268
+ * requested column list, used by `select` (columns) and `insert` (returning).
269
+ * A requested name resolves against either the Drizzle property name
270
+ * (camelCase) or the SQL column name (snake_case); the projection is keyed by
271
+ * the property name so the row shape matches a full select. Returns undefined
272
+ * for `"*"` or when nothing resolves, so callers fall back to all columns.
273
+ */
274
+ buildColumnProjection(tableObj, names) {
275
+ if (names == null || names === "*" || !tableObj || typeof tableObj !== "object") {
276
+ return void 0;
277
+ }
278
+ const cols = getColumns(tableObj);
279
+ const byAnyName = {};
280
+ for (const [jsName, col] of Object.entries(cols)) {
281
+ byAnyName[jsName] = { jsName, col };
282
+ const sqlName = col?.name;
283
+ if (typeof sqlName === "string") byAnyName[sqlName] = { jsName, col };
284
+ }
285
+ const projection = {};
286
+ for (const name of names) {
287
+ const hit = byAnyName[name];
288
+ if (hit) projection[hit.jsName] = hit.col;
289
+ }
290
+ return Object.keys(projection).length ? projection : void 0;
291
+ }
176
292
  // ============================================================
177
293
  // Connection Status (Default implementations, can override)
178
294
  // ============================================================
@@ -326,12 +442,13 @@ var DrizzleAdapter = class {
326
442
  * });
327
443
  * ```
328
444
  */
329
- async select(table, options) {
445
+ async select(table, options, executor) {
330
446
  const tableObj = this.getTableObject(table);
331
447
  if (tableObj) {
332
448
  try {
333
- const db = this.getDrizzle();
334
- let query = db.select().from(tableObj);
449
+ const db = executor ?? this.getDrizzle();
450
+ const projection = options?.columns?.length ? this.buildColumnProjection(tableObj, options.columns) : void 0;
451
+ let query = projection ? db.select(projection).from(tableObj) : db.select().from(tableObj);
335
452
  if (options?.where) {
336
453
  const whereCondition = buildDrizzleWhere(
337
454
  tableObj,
@@ -342,7 +459,7 @@ var DrizzleAdapter = class {
342
459
  }
343
460
  }
344
461
  if (options?.orderBy?.length) {
345
- const columns = getTableColumns(tableObj);
462
+ const columns = getColumns(tableObj);
346
463
  const orderClauses = options.orderBy.map((o) => {
347
464
  const col = columns[o.column];
348
465
  if (!col) return void 0;
@@ -358,6 +475,16 @@ var DrizzleAdapter = class {
358
475
  if (options?.offset !== void 0) {
359
476
  query = query.offset(options.offset);
360
477
  }
478
+ if (options?.forUpdate && !executor) {
479
+ throw this.createDatabaseError(
480
+ "query",
481
+ "forUpdate requires a transaction executor: a lock request on the pooled connection takes no durable lock and cannot prevent a concurrent write.",
482
+ void 0
483
+ );
484
+ }
485
+ if (options?.forUpdate && this.dialect !== "sqlite") {
486
+ query = query.for("update");
487
+ }
361
488
  return await query;
362
489
  } catch (error) {
363
490
  throw this.handleQueryError(error, "select", table);
@@ -389,8 +516,12 @@ var DrizzleAdapter = class {
389
516
  * });
390
517
  * ```
391
518
  */
392
- async selectOne(table, options) {
393
- const results = await this.select(table, { ...options, limit: 1 });
519
+ async selectOne(table, options, executor) {
520
+ const results = await this.select(
521
+ table,
522
+ { ...options, limit: 1 },
523
+ executor
524
+ );
394
525
  return results.length > 0 ? results[0] : null;
395
526
  }
396
527
  /**
@@ -422,19 +553,22 @@ var DrizzleAdapter = class {
422
553
  const mappedData = this.mapDataToColumnNames(tableObj, data);
423
554
  const db = this.getDrizzle();
424
555
  const caps = this.getCapabilities();
425
- if (caps.supportsReturning && options?.returning) {
426
- const result2 = await db.insert(tableObj).values(mappedData).returning();
556
+ const returning = options?.returning;
557
+ const wantsReturning = returning != null && !(Array.isArray(returning) && returning.length === 0);
558
+ if (caps.supportsReturning && wantsReturning) {
559
+ const projection = this.buildColumnProjection(tableObj, returning);
560
+ const insertQuery = db.insert(tableObj).values(mappedData);
561
+ const result2 = await (projection ? insertQuery.returning(projection) : insertQuery.returning());
427
562
  return Array.isArray(result2) ? result2[0] : result2;
428
563
  }
429
564
  const result = await db.insert(tableObj).values(mappedData);
430
- if (!caps.supportsReturning && options?.returning) {
431
- if (data.id !== void 0) {
432
- return await this.selectOne(table, {
433
- where: {
434
- and: [{ column: "id", op: "=", value: data.id }]
435
- }
436
- });
437
- }
565
+ if (!caps.supportsReturning && wantsReturning && data.id !== void 0) {
566
+ return await this.selectOne(table, {
567
+ columns: returning === "*" ? void 0 : returning,
568
+ where: {
569
+ and: [{ column: "id", op: "=", value: data.id }]
570
+ }
571
+ });
438
572
  }
439
573
  return Array.isArray(result) ? result[0] : result;
440
574
  } catch (error) {
@@ -504,11 +638,11 @@ var DrizzleAdapter = class {
504
638
  * );
505
639
  * ```
506
640
  */
507
- async update(table, data, where, options) {
641
+ async update(table, data, where, options, executor) {
508
642
  const tableObj = this.getTableObject(table);
509
643
  if (tableObj) {
510
644
  try {
511
- const db = this.getDrizzle();
645
+ const db = executor ?? this.getDrizzle();
512
646
  const caps = this.getCapabilities();
513
647
  const mappedData = this.mapDataToColumnNames(tableObj, data);
514
648
  let query = db.update(tableObj).set(mappedData);
@@ -521,7 +655,7 @@ var DrizzleAdapter = class {
521
655
  }
522
656
  await query;
523
657
  if (!caps.supportsReturning && options?.returning) {
524
- return await this.select(table, { where });
658
+ return await this.select(table, { where }, executor);
525
659
  }
526
660
  return [];
527
661
  } catch (error) {
@@ -556,18 +690,18 @@ var DrizzleAdapter = class {
556
690
  * console.log(`Deleted ${count} users`);
557
691
  * ```
558
692
  */
559
- async delete(table, where, _options) {
693
+ async delete(table, where, _options, executor) {
560
694
  const tableObj = this.getTableObject(table);
561
695
  if (tableObj) {
562
696
  try {
563
- const db = this.getDrizzle();
697
+ const db = executor ?? this.getDrizzle();
564
698
  let query = db.delete(tableObj);
565
699
  const whereCondition = buildDrizzleWhere(tableObj, where);
566
700
  if (whereCondition) {
567
701
  query = query.where(whereCondition);
568
702
  }
569
703
  const result = await query;
570
- return Array.isArray(result) ? result.length : result?.rowCount ?? result?.changes ?? 0;
704
+ return affectedRowCount(result);
571
705
  } catch (error) {
572
706
  throw this.handleQueryError(error, "delete", table);
573
707
  }
@@ -605,13 +739,13 @@ var DrizzleAdapter = class {
605
739
  * });
606
740
  * ```
607
741
  */
608
- async upsert(table, data, options) {
742
+ async upsert(table, data, options, executor) {
609
743
  const tableObj = this.getTableObject(table);
610
744
  if (tableObj) {
611
745
  try {
612
- const db = this.getDrizzle();
746
+ const db = executor ?? this.getDrizzle();
613
747
  const caps = this.getCapabilities();
614
- const columns = getTableColumns(tableObj);
748
+ const columns = getColumns(tableObj);
615
749
  const conflictTarget = options.conflictColumns.map((col) => columns[col]).filter(Boolean);
616
750
  const conflictSet = new Set(options.conflictColumns);
617
751
  const updateData = {};
@@ -636,17 +770,21 @@ var DrizzleAdapter = class {
636
770
  }
637
771
  await query;
638
772
  if (options.conflictColumns.length && data[options.conflictColumns[0]] !== void 0) {
639
- return await this.selectOne(table, {
640
- where: {
641
- and: [
642
- {
643
- column: options.conflictColumns[0],
644
- op: "=",
645
- value: data[options.conflictColumns[0]]
646
- }
647
- ]
648
- }
649
- });
773
+ return await this.selectOne(
774
+ table,
775
+ {
776
+ where: {
777
+ and: [
778
+ {
779
+ column: options.conflictColumns[0],
780
+ op: "=",
781
+ value: data[options.conflictColumns[0]]
782
+ }
783
+ ]
784
+ }
785
+ },
786
+ executor
787
+ );
650
788
  }
651
789
  return data;
652
790
  } catch (error) {
@@ -972,7 +1110,7 @@ var DrizzleAdapter = class {
972
1110
  break;
973
1111
  case "mysql":
974
1112
  sql = `
975
- SELECT table_name
1113
+ SELECT table_name AS table_name
976
1114
  FROM information_schema.tables
977
1115
  WHERE table_schema = DATABASE()
978
1116
  AND table_type = 'BASE TABLE'