@better-auth/drizzle-adapter 1.7.1 → 1.7.2
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 { a as insensitiveNe, i as insensitiveInArray, n as insensitiveEq, o as insensitiveNotInArray, r as insensitiveIlike } from "./query-builders-
|
|
1
|
+
import { a as insensitiveNe, c as getOneToOneRelationKey, i as insensitiveInArray, n as insensitiveEq, o as insensitiveNotInArray, r as insensitiveIlike, s as buildRelationKeysByModel } from "./query-builders-CBLMSM7v.mjs";
|
|
2
2
|
import { createAdapterFactory } from "@better-auth/core/db/adapter";
|
|
3
3
|
import { logger } from "@better-auth/core/env";
|
|
4
4
|
import { BetterAuthError } from "@better-auth/core/error";
|
|
5
|
-
import { and, asc, count, desc, eq, gt, gte, inArray, isNotNull, isNull, like, lt, lte, ne, notInArray, or, sql } from "drizzle-orm";
|
|
5
|
+
import { Column, and, asc, count, desc, eq, gt, gte, inArray, is, isNotNull, isNull, like, lt, lte, ne, notInArray, or, sql } from "drizzle-orm";
|
|
6
6
|
//#region src/drizzle-adapter.ts
|
|
7
7
|
/**
|
|
8
8
|
* Derive the number of affected rows from a Drizzle write result.
|
|
@@ -48,6 +48,7 @@ function hasDriverRowCount(result) {
|
|
|
48
48
|
const drizzleAdapter = (db, config) => {
|
|
49
49
|
let lazyOptions = null;
|
|
50
50
|
let mysqlNoIdWarned = false;
|
|
51
|
+
const relationKeysByModel = buildRelationKeysByModel(db._?.schema);
|
|
51
52
|
const createCustomAdapter = (db, inTransaction = false) => ({ getFieldName, getDefaultFieldName, getDefaultModelName, options, schema: baSchema }) => {
|
|
52
53
|
if (config.provider === "mysql" && options.advanced?.database?.generateId === false && !mysqlNoIdWarned) {
|
|
53
54
|
mysqlNoIdWarned = true;
|
|
@@ -115,15 +116,19 @@ const drizzleAdapter = (db, config) => {
|
|
|
115
116
|
};
|
|
116
117
|
function convertWhereClause(where, model) {
|
|
117
118
|
const schemaModel = getSchema(model);
|
|
119
|
+
const resolveFieldName = (where) => {
|
|
120
|
+
const field = getFieldName({
|
|
121
|
+
model,
|
|
122
|
+
field: where.field
|
|
123
|
+
});
|
|
124
|
+
if (!is(schemaModel[field], Column)) throw new BetterAuthError(`The field "${where.field}" does not exist in the schema for the model "${model}". Please update your schema.`);
|
|
125
|
+
return field;
|
|
126
|
+
};
|
|
118
127
|
if (!where) return [];
|
|
119
128
|
if (where.length === 1) {
|
|
120
129
|
const w = where[0];
|
|
121
130
|
if (!w) return [];
|
|
122
|
-
const field =
|
|
123
|
-
model,
|
|
124
|
-
field: w.field
|
|
125
|
-
});
|
|
126
|
-
if (!schemaModel[field]) throw new BetterAuthError(`The field "${w.field}" does not exist in the schema for the model "${model}". Please update your schema.`);
|
|
131
|
+
const field = resolveFieldName(w);
|
|
127
132
|
const isInsensitive = (w.mode ?? "sensitive") === "insensitive" && (typeof w.value === "string" || Array.isArray(w.value) && w.value.every((v) => typeof v === "string"));
|
|
128
133
|
if (w.operator === "in") {
|
|
129
134
|
if (!Array.isArray(w.value)) throw new BetterAuthError(`The value for the field "${w.field}" must be an array when using the "in" operator.`);
|
|
@@ -163,10 +168,7 @@ const drizzleAdapter = (db, config) => {
|
|
|
163
168
|
const andGroup = where.filter((w) => w.connector === "AND" || !w.connector);
|
|
164
169
|
const orGroup = where.filter((w) => w.connector === "OR");
|
|
165
170
|
const andClause = and(...andGroup.map((w) => {
|
|
166
|
-
const field =
|
|
167
|
-
model,
|
|
168
|
-
field: w.field
|
|
169
|
-
});
|
|
171
|
+
const field = resolveFieldName(w);
|
|
170
172
|
const isInsensitive = (w.mode ?? "sensitive") === "insensitive" && (typeof w.value === "string" || Array.isArray(w.value) && w.value.every((v) => typeof v === "string"));
|
|
171
173
|
if (w.operator === "in") {
|
|
172
174
|
if (!Array.isArray(w.value)) throw new BetterAuthError(`The value for the field "${w.field}" must be an array when using the "in" operator.`);
|
|
@@ -204,11 +206,7 @@ const drizzleAdapter = (db, config) => {
|
|
|
204
206
|
return eq(schemaModel[field], w.value);
|
|
205
207
|
}));
|
|
206
208
|
const orClause = or(...orGroup.map((w) => {
|
|
207
|
-
const field =
|
|
208
|
-
model,
|
|
209
|
-
field: w.field
|
|
210
|
-
});
|
|
211
|
-
if (!schemaModel[field]) throw new BetterAuthError(`The field "${w.field}" does not exist in the schema for the model "${model}". Please update your schema.`);
|
|
209
|
+
const field = resolveFieldName(w);
|
|
212
210
|
const isInsensitive = (w.mode ?? "sensitive") === "insensitive" && (typeof w.value === "string" || Array.isArray(w.value) && w.value.every((v) => typeof v === "string"));
|
|
213
211
|
if (w.operator === "in") {
|
|
214
212
|
if (!Array.isArray(w.value)) throw new BetterAuthError(`The value for the field "${w.field}" must be an array when using the "in" operator.`);
|
|
@@ -295,6 +293,16 @@ const drizzleAdapter = (db, config) => {
|
|
|
295
293
|
}
|
|
296
294
|
return null;
|
|
297
295
|
}
|
|
296
|
+
function getJoinRelationKey(baseModel, joinModel, relationKeys, isUnique) {
|
|
297
|
+
if (isUnique) return getOneToOneRelationKey({
|
|
298
|
+
baseModel,
|
|
299
|
+
joinModel,
|
|
300
|
+
relationKeys,
|
|
301
|
+
schema: baSchema,
|
|
302
|
+
getDefaultModelName
|
|
303
|
+
});
|
|
304
|
+
return config.usePlural ? joinModel : `${joinModel}s`;
|
|
305
|
+
}
|
|
298
306
|
return {
|
|
299
307
|
async create({ model, data: values }) {
|
|
300
308
|
const schemaModel = getSchema(model);
|
|
@@ -311,15 +319,19 @@ const drizzleAdapter = (db, config) => {
|
|
|
311
319
|
logger.info("Falling back to regular query");
|
|
312
320
|
} else {
|
|
313
321
|
let includes;
|
|
314
|
-
const
|
|
322
|
+
const renamedJoinResults = [];
|
|
323
|
+
const relationKeys = relationKeysByModel.get(queryModel);
|
|
315
324
|
includes = {};
|
|
316
325
|
const joinEntries = Object.entries(join);
|
|
317
|
-
for (const [
|
|
326
|
+
for (const [joinModel, joinAttr] of joinEntries) {
|
|
318
327
|
const limit = joinAttr.limit ?? options.advanced?.database?.defaultFindManyLimit ?? 100;
|
|
319
328
|
const isUnique = joinAttr.relation === "one-to-one";
|
|
320
|
-
const
|
|
321
|
-
includes[
|
|
322
|
-
if (
|
|
329
|
+
const relationKey = getJoinRelationKey(model, joinModel, relationKeys, isUnique);
|
|
330
|
+
includes[relationKey] = isUnique ? true : { limit };
|
|
331
|
+
if (relationKey !== joinModel) renamedJoinResults.push({
|
|
332
|
+
key: relationKey,
|
|
333
|
+
target: joinModel
|
|
334
|
+
});
|
|
323
335
|
}
|
|
324
336
|
const res = await db.query[queryModel].findFirst({
|
|
325
337
|
where: clause[0],
|
|
@@ -332,10 +344,9 @@ const drizzleAdapter = (db, config) => {
|
|
|
332
344
|
}, {}) : void 0,
|
|
333
345
|
with: includes
|
|
334
346
|
});
|
|
335
|
-
if (res) for (const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
if (pluralJoinResult !== singularKey) delete res[pluralJoinResult];
|
|
347
|
+
if (res) for (const { key, target } of renamedJoinResults) {
|
|
348
|
+
res[target] = res[key];
|
|
349
|
+
delete res[key];
|
|
339
350
|
}
|
|
340
351
|
return res;
|
|
341
352
|
}
|
|
@@ -364,15 +375,19 @@ const drizzleAdapter = (db, config) => {
|
|
|
364
375
|
logger.info("Falling back to regular query");
|
|
365
376
|
} else {
|
|
366
377
|
let includes;
|
|
367
|
-
const
|
|
378
|
+
const renamedJoinResults = [];
|
|
379
|
+
const relationKeys = relationKeysByModel.get(queryModel);
|
|
368
380
|
includes = {};
|
|
369
381
|
const joinEntries = Object.entries(join);
|
|
370
|
-
for (const [
|
|
382
|
+
for (const [joinModel, joinAttr] of joinEntries) {
|
|
371
383
|
const isUnique = joinAttr.relation === "one-to-one";
|
|
372
384
|
const limit = joinAttr.limit ?? options.advanced?.database?.defaultFindManyLimit ?? 100;
|
|
373
|
-
const
|
|
374
|
-
includes[
|
|
375
|
-
if (
|
|
385
|
+
const relationKey = getJoinRelationKey(model, joinModel, relationKeys, isUnique);
|
|
386
|
+
includes[relationKey] = isUnique ? true : { limit };
|
|
387
|
+
if (relationKey !== joinModel) renamedJoinResults.push({
|
|
388
|
+
key: relationKey,
|
|
389
|
+
target: joinModel
|
|
390
|
+
});
|
|
376
391
|
}
|
|
377
392
|
let orderBy = void 0;
|
|
378
393
|
if (sortBy?.field) orderBy = [sortFn(schemaModel[getFieldName({
|
|
@@ -393,11 +408,9 @@ const drizzleAdapter = (db, config) => {
|
|
|
393
408
|
offset: offset ?? 0,
|
|
394
409
|
orderBy
|
|
395
410
|
});
|
|
396
|
-
if (res) for (const item of res) for (const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
item[singularKey] = item[pluralJoinResult];
|
|
400
|
-
delete item[pluralJoinResult];
|
|
411
|
+
if (res) for (const item of res) for (const { key, target } of renamedJoinResults) {
|
|
412
|
+
item[target] = item[key];
|
|
413
|
+
delete item[key];
|
|
401
414
|
}
|
|
402
415
|
return res;
|
|
403
416
|
}
|
|
@@ -1,4 +1,30 @@
|
|
|
1
1
|
import { ilike, sql } from "drizzle-orm";
|
|
2
|
+
//#region src/join-relation-key.ts
|
|
3
|
+
/**
|
|
4
|
+
* Reads the relation registry used to build Drizzle relational queries.
|
|
5
|
+
*
|
|
6
|
+
* - Drizzle 0.x (Relations v1): `db._.schema`
|
|
7
|
+
* - Drizzle 1.x (Relations v2): `db._.relations`
|
|
8
|
+
*/
|
|
9
|
+
function buildRelationKeysByModel(relationRegistry) {
|
|
10
|
+
const relationKeysByModel = /* @__PURE__ */ new Map();
|
|
11
|
+
for (const [model, tableMetadata] of Object.entries(relationRegistry ?? {})) {
|
|
12
|
+
if (!tableMetadata.relations) continue;
|
|
13
|
+
relationKeysByModel.set(model, new Set(Object.keys(tableMetadata.relations)));
|
|
14
|
+
}
|
|
15
|
+
return relationKeysByModel;
|
|
16
|
+
}
|
|
17
|
+
function getOneToOneRelationKey({ baseModel, joinModel, relationKeys, schema, getDefaultModelName }) {
|
|
18
|
+
const defaultBaseModelName = getDefaultModelName(baseModel);
|
|
19
|
+
const defaultJoinModelName = getDefaultModelName(joinModel);
|
|
20
|
+
const joinModelFields = schema[defaultJoinModelName]?.fields ?? {};
|
|
21
|
+
const generatedRelationKey = Object.values(joinModelFields).some((field) => field.references && getDefaultModelName(field.references.model) === defaultBaseModelName) ? joinModel : schema[defaultJoinModelName]?.modelName ?? defaultJoinModelName;
|
|
22
|
+
if (!relationKeys?.size) return joinModel;
|
|
23
|
+
if (relationKeys.has(generatedRelationKey)) return generatedRelationKey;
|
|
24
|
+
if (relationKeys.has(joinModel)) return joinModel;
|
|
25
|
+
return generatedRelationKey;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
2
28
|
//#region src/query-builders.ts
|
|
3
29
|
/**
|
|
4
30
|
* Case-insensitive LIKE/ILIKE for pattern matching.
|
|
@@ -49,4 +75,4 @@ function insensitiveNe(column, value) {
|
|
|
49
75
|
return sql`LOWER(${column}) <> LOWER(${value})`;
|
|
50
76
|
}
|
|
51
77
|
//#endregion
|
|
52
|
-
export { insensitiveNe as a, insensitiveInArray as i, insensitiveEq as n, insensitiveNotInArray as o, insensitiveIlike as r, escapedLike as t };
|
|
78
|
+
export { insensitiveNe as a, getOneToOneRelationKey as c, insensitiveInArray as i, insensitiveEq as n, insensitiveNotInArray as o, insensitiveIlike as r, buildRelationKeysByModel as s, escapedLike as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as insensitiveNe, i as insensitiveInArray, n as insensitiveEq, o as insensitiveNotInArray, t as escapedLike } from "../query-builders-
|
|
1
|
+
import { a as insensitiveNe, c as getOneToOneRelationKey, i as insensitiveInArray, n as insensitiveEq, o as insensitiveNotInArray, s as buildRelationKeysByModel, t as escapedLike } from "../query-builders-CBLMSM7v.mjs";
|
|
2
2
|
import { createAdapterFactory } from "@better-auth/core/db/adapter";
|
|
3
3
|
import { logger } from "@better-auth/core/env";
|
|
4
4
|
import { BetterAuthError } from "@better-auth/core/error";
|
|
@@ -72,15 +72,14 @@ function applyWhereOperator(column, w, fieldLabel, provider) {
|
|
|
72
72
|
const drizzleAdapter = (db, config) => {
|
|
73
73
|
let lazyOptions = null;
|
|
74
74
|
let mysqlNoIdWarned = false;
|
|
75
|
+
const relationKeysByModel = buildRelationKeysByModel(db._?.relations);
|
|
75
76
|
const createCustomAdapter = (db, inTransaction = false) => ({ getFieldName, getDefaultModelName, options, schema: baSchema }) => {
|
|
76
77
|
if (config.provider === "mysql" && options.advanced?.database?.generateId === false && !mysqlNoIdWarned) {
|
|
77
78
|
mysqlNoIdWarned = true;
|
|
78
79
|
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.");
|
|
79
80
|
}
|
|
80
81
|
function getSchema(model) {
|
|
81
|
-
const
|
|
82
|
-
if (!schema) throw new BetterAuthError("Drizzle adapter failed to initialize. Schema not found. Please provide a schema object in the adapter options object.");
|
|
83
|
-
const schemaModel = schema[model];
|
|
82
|
+
const schemaModel = config.schema?.[model] ?? db._?.relations?.[model]?.table ?? db._?.fullSchema?.[model];
|
|
84
83
|
if (!schemaModel) throw new BetterAuthError(`[# Drizzle Adapter]: The model "${model}" was not found in the schema object. Please pass the schema directly to the adapter options.`);
|
|
85
84
|
return schemaModel;
|
|
86
85
|
}
|
|
@@ -109,14 +108,16 @@ const drizzleAdapter = (db, config) => {
|
|
|
109
108
|
}
|
|
110
109
|
return null;
|
|
111
110
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
111
|
+
function getJoinRelationKey(baseModel, joinModel, relationKeys, isUnique) {
|
|
112
|
+
if (isUnique) return getOneToOneRelationKey({
|
|
113
|
+
baseModel,
|
|
114
|
+
joinModel,
|
|
115
|
+
relationKeys,
|
|
116
|
+
schema: baSchema,
|
|
117
|
+
getDefaultModelName
|
|
118
|
+
});
|
|
119
|
+
if (config.usePlural || joinModel.endsWith("s")) return joinModel;
|
|
120
|
+
return `${joinModel}s`;
|
|
120
121
|
}
|
|
121
122
|
const withReturning = async (model, builder, data, where) => {
|
|
122
123
|
if (config.provider !== "mysql") return (await builder.returning())[0];
|
|
@@ -290,17 +291,18 @@ const drizzleAdapter = (db, config) => {
|
|
|
290
291
|
logger.info("Falling back to regular query");
|
|
291
292
|
} else {
|
|
292
293
|
let includes;
|
|
293
|
-
const
|
|
294
|
+
const renamedJoinResults = [];
|
|
295
|
+
const relationKeys = relationKeysByModel.get(queryModel);
|
|
294
296
|
includes = {};
|
|
295
297
|
const joinEntries = Object.entries(join);
|
|
296
|
-
for (const [
|
|
298
|
+
for (const [joinModel, joinAttr] of joinEntries) {
|
|
297
299
|
const limit = joinAttr.limit ?? options.advanced?.database?.defaultFindManyLimit ?? 100;
|
|
298
300
|
const isUnique = joinAttr.relation === "one-to-one";
|
|
299
|
-
const relationKey = getJoinRelationKey(model, isUnique);
|
|
301
|
+
const relationKey = getJoinRelationKey(model, joinModel, relationKeys, isUnique);
|
|
300
302
|
includes[relationKey] = isUnique ? true : { limit };
|
|
301
|
-
if (
|
|
303
|
+
if (relationKey !== joinModel) renamedJoinResults.push({
|
|
302
304
|
key: relationKey,
|
|
303
|
-
target:
|
|
305
|
+
target: joinModel
|
|
304
306
|
});
|
|
305
307
|
}
|
|
306
308
|
const clause = convertNewWhereClause(where, model);
|
|
@@ -315,8 +317,7 @@ const drizzleAdapter = (db, config) => {
|
|
|
315
317
|
}, {}) : void 0,
|
|
316
318
|
with: includes
|
|
317
319
|
});
|
|
318
|
-
if (res) for (const { key, target } of
|
|
319
|
-
if (key === target) continue;
|
|
320
|
+
if (res) for (const { key, target } of renamedJoinResults) {
|
|
320
321
|
res[target] = res[key];
|
|
321
322
|
delete res[key];
|
|
322
323
|
}
|
|
@@ -347,17 +348,18 @@ const drizzleAdapter = (db, config) => {
|
|
|
347
348
|
logger.info("Falling back to regular query");
|
|
348
349
|
} else {
|
|
349
350
|
let includes;
|
|
350
|
-
const
|
|
351
|
+
const renamedJoinResults = [];
|
|
352
|
+
const relationKeys = relationKeysByModel.get(queryModel);
|
|
351
353
|
includes = {};
|
|
352
354
|
const joinEntries = Object.entries(join);
|
|
353
|
-
for (const [
|
|
355
|
+
for (const [joinModel, joinAttr] of joinEntries) {
|
|
354
356
|
const isUnique = joinAttr.relation === "one-to-one";
|
|
355
357
|
const limit = joinAttr.limit ?? options.advanced?.database?.defaultFindManyLimit ?? 100;
|
|
356
|
-
const relationKey = getJoinRelationKey(model, isUnique);
|
|
358
|
+
const relationKey = getJoinRelationKey(model, joinModel, relationKeys, isUnique);
|
|
357
359
|
includes[relationKey] = isUnique ? true : { limit };
|
|
358
|
-
if (
|
|
360
|
+
if (relationKey !== joinModel) renamedJoinResults.push({
|
|
359
361
|
key: relationKey,
|
|
360
|
-
target:
|
|
362
|
+
target: joinModel
|
|
361
363
|
});
|
|
362
364
|
}
|
|
363
365
|
let orderBy = void 0;
|
|
@@ -379,8 +381,7 @@ const drizzleAdapter = (db, config) => {
|
|
|
379
381
|
offset: offset ?? 0,
|
|
380
382
|
orderBy
|
|
381
383
|
});
|
|
382
|
-
if (res) for (const item of res) for (const { key, target } of
|
|
383
|
-
if (key === target) continue;
|
|
384
|
+
if (res) for (const item of res) for (const { key, target } of renamedJoinResults) {
|
|
384
385
|
item[target] = item[key];
|
|
385
386
|
delete item[key];
|
|
386
387
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/drizzle-adapter",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.2",
|
|
4
4
|
"bugs": {
|
|
5
5
|
"url": "https://github.com/better-auth/better-auth/issues"
|
|
6
6
|
},
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"@better-auth/utils": "0.4.2",
|
|
47
47
|
"drizzle-orm": "^0.45.2 || >=1.0.0-rc.1 <2.0.0",
|
|
48
|
-
"@better-auth/core": "^1.7.
|
|
48
|
+
"@better-auth/core": "^1.7.2"
|
|
49
49
|
},
|
|
50
50
|
"peerDependenciesMeta": {
|
|
51
51
|
"drizzle-orm": {
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"drizzle-orm": "^0.45.2",
|
|
58
58
|
"tsdown": "0.21.10",
|
|
59
59
|
"typescript": "^6.0.3",
|
|
60
|
-
"@better-auth/core": "1.7.
|
|
60
|
+
"@better-auth/core": "1.7.2"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"build": "tsdown",
|