@better-auth/drizzle-adapter 1.6.10 → 1.6.12

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.
Files changed (2) hide show
  1. package/dist/index.mjs +75 -15
  2. package/package.json +5 -5
package/dist/index.mjs CHANGED
@@ -40,7 +40,12 @@ function insensitiveNe(column, value) {
40
40
  //#region src/drizzle-adapter.ts
41
41
  const drizzleAdapter = (db, config) => {
42
42
  let lazyOptions = null;
43
- const createCustomAdapter = (db) => ({ getFieldName, getDefaultFieldName, options }) => {
43
+ let mysqlNoIdWarned = false;
44
+ const createCustomAdapter = (db, inTransaction = false) => ({ getFieldName, getDefaultFieldName, getDefaultModelName, options, schema: baSchema }) => {
45
+ if (config.provider === "mysql" && options.advanced?.database?.generateId === false && !mysqlNoIdWarned) {
46
+ mysqlNoIdWarned = true;
47
+ logger.warn("[Drizzle Adapter] MySQL does not support INSERT...RETURNING. With generateId set to false, the adapter uses best-effort fallback strategies (unique columns, full-field match) to retrieve inserted rows. For reliable behavior, use Better Auth's default ID generation, a custom generateId function, or generateId: \"serial\" for auto-increment.");
48
+ }
44
49
  function getSchema(model) {
45
50
  const schema = config.schema || db._.fullSchema;
46
51
  if (!schema) throw new BetterAuthError("Drizzle adapter failed to initialize. Schema not found. Please provide a schema object in the adapter options object.");
@@ -62,15 +67,44 @@ const drizzleAdapter = (db, config) => {
62
67
  return w;
63
68
  }), model);
64
69
  return (await db.select().from(schemaModel).where(...clause))[0];
65
- } else if (builderVal && builderVal[0]?.id?.value) {
66
- let tId = builderVal[0]?.id?.value;
67
- if (!tId) tId = (await db.select({ id: sql`LAST_INSERT_ID()` }).from(schemaModel).orderBy(desc(schemaModel.id)).limit(1))[0].id;
68
- return (await db.select().from(schemaModel).where(eq(schemaModel.id, tId)).limit(1).execute())[0];
69
- } else if (data.id) return (await db.select().from(schemaModel).where(eq(schemaModel.id, data.id)).limit(1).execute())[0];
70
- else {
71
- if (!("id" in schemaModel)) throw new BetterAuthError(`The model "${model}" does not have an "id" field. Please use the "id" field as your primary key.`);
72
- return (await db.select().from(schemaModel).orderBy(desc(schemaModel.id)).limit(1).execute())[0];
73
70
  }
71
+ const fetchInserted = async (tx) => {
72
+ const builderId = builderVal?.[0]?.id?.value;
73
+ if (builderId) return (await tx.select().from(schemaModel).where(eq(schemaModel.id, builderId)).limit(1).execute())[0] ?? null;
74
+ if (data.id) return (await tx.select().from(schemaModel).where(eq(schemaModel.id, data.id)).limit(1).execute())[0] ?? null;
75
+ if (options.advanced?.database?.generateId === "serial" && schemaModel.id) {
76
+ const lastId = (await tx.select({ id: sql`LAST_INSERT_ID()` }).from(schemaModel).limit(1).execute())[0]?.id;
77
+ if (lastId) return (await tx.select().from(schemaModel).where(eq(schemaModel.id, lastId)).limit(1).execute())[0] ?? null;
78
+ }
79
+ const modelSchema = baSchema[getDefaultModelName(model)]?.fields;
80
+ if (modelSchema) for (const [fieldKey, fieldAttr] of Object.entries(modelSchema)) {
81
+ if (!fieldAttr.unique) continue;
82
+ const dbFieldName = getFieldName({
83
+ model,
84
+ field: fieldKey
85
+ });
86
+ const val = data[dbFieldName];
87
+ if (val === void 0 || val === null) continue;
88
+ if (!schemaModel[dbFieldName]) continue;
89
+ const res = await tx.select().from(schemaModel).where(eq(schemaModel[dbFieldName], val)).limit(1).execute();
90
+ if (res[0]) return res[0];
91
+ }
92
+ const conditions = [];
93
+ for (const [key, val] of Object.entries(data)) {
94
+ if (val === void 0 || !schemaModel[key]) continue;
95
+ conditions.push(val === null ? isNull(schemaModel[key]) : eq(schemaModel[key], val));
96
+ }
97
+ if (conditions.length > 0) {
98
+ const combined = and(...conditions);
99
+ if (combined) {
100
+ const res = await tx.select().from(schemaModel).where(combined).limit(2).execute();
101
+ if (res.length === 1) return res[0];
102
+ }
103
+ }
104
+ logger.warn(`[Drizzle Adapter] Unable to safely identify the inserted "${model}" row on MySQL. Enable Better Auth ID generation or use generateId: "serial" for reliable behavior.`);
105
+ return null;
106
+ };
107
+ return inTransaction ? fetchInserted(db) : db.transaction(fetchInserted);
74
108
  };
75
109
  function convertWhereClause(where, model) {
76
110
  const schemaModel = getSchema(model);
@@ -204,10 +238,10 @@ const drizzleAdapter = (db, config) => {
204
238
  if (isInsensitive && typeof w.value === "string") return insensitiveEq(schemaModel[field], w.value);
205
239
  return eq(schemaModel[field], w.value);
206
240
  }));
207
- const clause = [];
208
- if (andGroup.length) clause.push(andClause);
209
- if (orGroup.length) clause.push(orClause);
210
- return clause;
241
+ if (andGroup.length && orGroup.length) return [and(andClause, orClause)];
242
+ if (andGroup.length) return [andClause];
243
+ if (orGroup.length) return [orClause];
244
+ return [];
211
245
  }
212
246
  function checkMissingFields(schema, model, values) {
213
247
  if (!schema) throw new BetterAuthError("Drizzle adapter failed to initialize. Drizzle Schema not found. Please provide a schema object in the adapter options object.");
@@ -419,6 +453,29 @@ const drizzleAdapter = (db, config) => {
419
453
  });
420
454
  return count;
421
455
  },
456
+ async consumeOne({ model, where }) {
457
+ const schemaModel = getSchema(model);
458
+ const clause = convertWhereClause(where, model);
459
+ const idField = getFieldName({
460
+ model,
461
+ field: "id"
462
+ });
463
+ const idColumn = schemaModel[idField];
464
+ if (config.provider === "mysql") {
465
+ const claimFromTransaction = async (tx) => {
466
+ const target = (await tx.select().from(schemaModel).where(...clause).for("update").limit(1))[0];
467
+ if (!target) return null;
468
+ const targetId = target[idField] ?? target.id;
469
+ if (targetId === void 0 || targetId === null || !idColumn) return null;
470
+ const delRes = await tx.delete(schemaModel).where(eq(idColumn, targetId)).execute();
471
+ return ((delRes && (delRes.rowsAffected ?? delRes.affectedRows ?? delRes.changes)) ?? 0) > 0 ? target : null;
472
+ };
473
+ return inTransaction ? claimFromTransaction(db) : db.transaction(claimFromTransaction);
474
+ }
475
+ if (!idColumn) return null;
476
+ const targetIds = db.select({ id: idColumn }).from(schemaModel).where(...clause).limit(1);
477
+ return (await db.delete(schemaModel).where(inArray(idColumn, targetIds)).returning())[0] ?? null;
478
+ },
422
479
  options: config
423
480
  };
424
481
  };
@@ -441,8 +498,11 @@ const drizzleAdapter = (db, config) => {
441
498
  },
442
499
  transaction: config.transaction ?? false ? (cb) => db.transaction((tx) => {
443
500
  return cb(createAdapterFactory({
444
- config: adapterOptions.config,
445
- adapter: createCustomAdapter(tx)
501
+ config: {
502
+ ...adapterOptions.config,
503
+ transaction: false
504
+ },
505
+ adapter: createCustomAdapter(tx, true)
446
506
  })(lazyOptions));
447
507
  }) : false
448
508
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/drizzle-adapter",
3
- "version": "1.6.10",
3
+ "version": "1.6.12",
4
4
  "description": "Drizzle adapter for Better Auth",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -35,9 +35,9 @@
35
35
  }
36
36
  },
37
37
  "peerDependencies": {
38
- "@better-auth/utils": "0.4.0",
38
+ "@better-auth/utils": "0.4.1",
39
39
  "drizzle-orm": "^0.45.2",
40
- "@better-auth/core": "^1.6.10"
40
+ "@better-auth/core": "^1.6.12"
41
41
  },
42
42
  "peerDependenciesMeta": {
43
43
  "drizzle-orm": {
@@ -45,11 +45,11 @@
45
45
  }
46
46
  },
47
47
  "devDependencies": {
48
- "@better-auth/utils": "0.4.0",
48
+ "@better-auth/utils": "0.4.1",
49
49
  "drizzle-orm": "^0.45.2",
50
50
  "tsdown": "0.21.1",
51
51
  "typescript": "^5.9.3",
52
- "@better-auth/core": "1.6.10"
52
+ "@better-auth/core": "1.6.12"
53
53
  },
54
54
  "scripts": {
55
55
  "build": "tsdown",