@better-auth/kysely-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.
@@ -113,7 +113,7 @@ var BunSqliteIntrospector = class {
113
113
  isAutoIncrementing: col.name === autoIncrementCol,
114
114
  hasDefaultValue: col.dflt_value != null
115
115
  })),
116
- isView: true
116
+ isView: false
117
117
  };
118
118
  }
119
119
  };
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Kysely, MssqlDialect, MysqlDialect, PostgresDialect, SqliteDialect, sql } from "kysely";
2
2
  import { createAdapterFactory } from "@better-auth/core/db/adapter";
3
+ import { logger } from "@better-auth/core/env";
3
4
  import { capitalizeFirstLetter } from "@better-auth/core/utils/string";
4
5
  //#region src/dialect.ts
5
6
  function getKyselyDatabaseType(db) {
@@ -43,7 +44,7 @@ const createKyselyAdapter = async (config) => {
43
44
  if ("getConnection" in db) dialect = new MysqlDialect(db);
44
45
  if ("connect" in db) dialect = new PostgresDialect({ pool: db });
45
46
  if ("fileControl" in db) {
46
- const { BunSqliteDialect } = await import("./bun-sqlite-dialect-na--YwnN.mjs");
47
+ const { BunSqliteDialect } = await import("./bun-sqlite-dialect-DzNwOpKv.mjs");
47
48
  dialect = new BunSqliteDialect({ database: db });
48
49
  }
49
50
  if ("createSession" in db) {
@@ -124,8 +125,13 @@ function insensitiveNe(columnRef, value) {
124
125
  //#region src/kysely-adapter.ts
125
126
  const kyselyAdapter = (db, config) => {
126
127
  let lazyOptions = null;
127
- const createCustomAdapter = (db) => {
128
- return ({ getFieldName, schema, getDefaultFieldName, getDefaultModelName, getFieldAttributes, getModelName }) => {
128
+ let mysqlNoIdWarned = false;
129
+ const createCustomAdapter = (db, inTransaction = false) => {
130
+ return ({ getFieldName, schema, getDefaultFieldName, getDefaultModelName, getFieldAttributes, getModelName, options }) => {
131
+ if (config?.type === "mysql" && options.advanced?.database?.generateId === false && !mysqlNoIdWarned) {
132
+ mysqlNoIdWarned = true;
133
+ logger.warn("[Kysely 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.");
134
+ }
129
135
  const selectAllJoins = (join) => {
130
136
  const allSelects = [];
131
137
  const allSelectsStr = [];
@@ -149,33 +155,58 @@ const kyselyAdapter = (db, config) => {
149
155
  };
150
156
  };
151
157
  const withReturning = async (values, builder, model, where) => {
152
- let res;
153
158
  if (config?.type === "mysql") {
154
159
  await builder.execute();
155
- const field = values.id ? "id" : where.length > 0 && where[0]?.field ? where[0].field : "id";
156
- if (!values.id && where.length === 0) {
157
- res = await db.selectFrom(model).selectAll().orderBy(getFieldName({
160
+ if (where.length > 0) {
161
+ const field = values.id ? "id" : where[0]?.field ? where[0].field : "id";
162
+ const value = values[field] !== void 0 ? values[field] : where[0]?.value;
163
+ return await db.selectFrom(model).selectAll().where(getFieldName({
158
164
  model,
159
165
  field
160
- }), "desc").limit(1).executeTakeFirst();
161
- return res;
166
+ }), value === null ? "is" : "=", value).limit(1).executeTakeFirst();
162
167
  }
163
- const value = values[field] !== void 0 ? values[field] : where[0]?.value;
164
- res = await db.selectFrom(model).selectAll().orderBy(getFieldName({
165
- model,
166
- field
167
- }), "desc").where(getFieldName({
168
- model,
169
- field
170
- }), value === null ? "is" : "=", value).limit(1).executeTakeFirst();
171
- return res;
172
- }
173
- if (config?.type === "mssql") {
174
- res = await builder.outputAll("inserted").executeTakeFirst();
175
- return res;
168
+ const fetchInserted = async (trx) => {
169
+ if (values.id) return await trx.selectFrom(model).selectAll().where(getFieldName({
170
+ model,
171
+ field: "id"
172
+ }), "=", values.id).limit(1).executeTakeFirst();
173
+ if (options.advanced?.database?.generateId === "serial") {
174
+ const lastId = (await sql`SELECT LAST_INSERT_ID() as id`.execute(trx)).rows[0]?.id;
175
+ if (lastId) return await trx.selectFrom(model).selectAll().where(getFieldName({
176
+ model,
177
+ field: "id"
178
+ }), "=", lastId).limit(1).executeTakeFirst();
179
+ }
180
+ const modelSchema = schema[getDefaultModelName(model)]?.fields;
181
+ if (modelSchema) for (const [fieldKey, fieldAttr] of Object.entries(modelSchema)) {
182
+ if (!fieldAttr.unique) continue;
183
+ const dbFieldName = getFieldName({
184
+ model,
185
+ field: fieldKey
186
+ });
187
+ const val = values[dbFieldName];
188
+ if (val === void 0 || val === null) continue;
189
+ const row = await trx.selectFrom(model).selectAll().where(dbFieldName, "=", val).limit(1).executeTakeFirst();
190
+ if (row) return row;
191
+ }
192
+ let query = trx.selectFrom(model).selectAll();
193
+ let hasConditions = false;
194
+ for (const [key, val] of Object.entries(values)) {
195
+ if (val === void 0) continue;
196
+ query = query.where(key, val === null ? "is" : "=", val);
197
+ hasConditions = true;
198
+ }
199
+ if (hasConditions) {
200
+ const rows = await query.limit(2).execute();
201
+ if (rows.length === 1) return rows[0];
202
+ }
203
+ logger.warn(`[Kysely Adapter] Unable to safely identify the inserted "${model}" row on MySQL. Enable Better Auth ID generation or use generateId: "serial" for reliable behavior.`);
204
+ return null;
205
+ };
206
+ return inTransaction ? fetchInserted(db) : db.transaction().execute(fetchInserted);
176
207
  }
177
- res = await builder.returningAll().executeTakeFirst();
178
- return res;
208
+ if (config?.type === "mssql") return await builder.outputAll("inserted").executeTakeFirst();
209
+ return await builder.returningAll().executeTakeFirst();
179
210
  };
180
211
  function convertWhereClause(model, w) {
181
212
  if (!w) return {
@@ -425,6 +456,43 @@ const kyselyAdapter = (db, config) => {
425
456
  const res = (await query.executeTakeFirst()).numDeletedRows;
426
457
  return res > Number.MAX_SAFE_INTEGER ? Number.MAX_SAFE_INTEGER : Number(res);
427
458
  },
459
+ async consumeOne({ model, where }) {
460
+ const { and, or } = convertWhereClause(model, where);
461
+ const applyWhere = (query) => {
462
+ if (and) query = query.where((eb) => eb.and(and.map((expr) => expr(eb))));
463
+ if (or) query = query.where((eb) => eb.or(or.map((expr) => expr(eb))));
464
+ return query;
465
+ };
466
+ const idField = getFieldName({
467
+ model,
468
+ field: "id"
469
+ });
470
+ const deleteSelectedRow = async (db, row) => {
471
+ const targetId = row[idField] ?? row.id;
472
+ if (targetId === void 0 || targetId === null) return null;
473
+ const query = db.deleteFrom(model).where(`${model}.${idField}`, "=", targetId);
474
+ if (config?.type === "mysql") {
475
+ const result = await query.executeTakeFirst();
476
+ return Number(result.numDeletedRows) > 0 ? row : null;
477
+ }
478
+ if (config?.type === "mssql") return await query.outputAll("deleted").executeTakeFirst() ?? null;
479
+ return await query.returningAll().executeTakeFirst() ?? null;
480
+ };
481
+ const deleteWithReturning = async (query) => {
482
+ if (config?.type === "mssql") return await query.outputAll("deleted").executeTakeFirst() ?? null;
483
+ return await query.returningAll().executeTakeFirst() ?? null;
484
+ };
485
+ if (config?.type === "mysql") {
486
+ const claimFromTransaction = async (trx) => {
487
+ const row = await applyWhere(trx.selectFrom(model).selectAll().forUpdate()).limit(1).executeTakeFirst();
488
+ if (!row) return null;
489
+ return deleteSelectedRow(trx, row);
490
+ };
491
+ return inTransaction ? claimFromTransaction(db) : db.transaction().execute(claimFromTransaction);
492
+ }
493
+ const targetIds = applyWhere(db.selectFrom(model).select(`${model}.${idField}`)).limit(1);
494
+ return deleteWithReturning(db.deleteFrom(model).where(`${model}.${idField}`, "in", targetIds));
495
+ },
428
496
  options: config
429
497
  };
430
498
  };
@@ -443,8 +511,11 @@ const kyselyAdapter = (db, config) => {
443
511
  supportsUUIDs: config?.type === "postgres" ? true : false,
444
512
  transaction: config?.transaction ? (cb) => db.transaction().execute((trx) => {
445
513
  return cb(createAdapterFactory({
446
- config: adapterOptions.config,
447
- adapter: createCustomAdapter(trx)
514
+ config: {
515
+ ...adapterOptions.config,
516
+ transaction: false
517
+ },
518
+ adapter: createCustomAdapter(trx, true)
448
519
  })(lazyOptions));
449
520
  }) : false
450
521
  },
@@ -113,7 +113,7 @@ var NodeSqliteIntrospector = class {
113
113
  isAutoIncrementing: col.name === autoIncrementCol,
114
114
  hasDefaultValue: col.dflt_value != null
115
115
  })),
116
- isView: true
116
+ isView: false
117
117
  };
118
118
  }
119
119
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/kysely-adapter",
3
- "version": "1.6.10",
3
+ "version": "1.6.12",
4
4
  "description": "Kysely adapter for Better Auth",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -40,9 +40,9 @@
40
40
  }
41
41
  },
42
42
  "peerDependencies": {
43
- "@better-auth/utils": "0.4.0",
44
- "kysely": "^0.28.14",
45
- "@better-auth/core": "^1.6.10"
43
+ "@better-auth/utils": "0.4.1",
44
+ "kysely": "^0.28.17 || ^0.29.0",
45
+ "@better-auth/core": "^1.6.12"
46
46
  },
47
47
  "peerDependenciesMeta": {
48
48
  "kysely": {
@@ -51,11 +51,11 @@
51
51
  },
52
52
  "devDependencies": {
53
53
  "@cloudflare/workers-types": "^4.20250121.0",
54
- "@better-auth/utils": "0.4.0",
55
- "kysely": "^0.28.14",
54
+ "@better-auth/utils": "0.4.1",
55
+ "kysely": "^0.28.17 || ^0.29.0",
56
56
  "tsdown": "0.21.1",
57
57
  "typescript": "^5.9.3",
58
- "@better-auth/core": "1.6.10"
58
+ "@better-auth/core": "1.6.12"
59
59
  },
60
60
  "scripts": {
61
61
  "build": "tsdown",