@better-auth/drizzle-adapter 1.5.0-beta.9 → 1.5.0

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/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # Better Auth Drizzle Adapter
2
+
3
+ Drizzle ORM adapter for [Better Auth](https://www.better-auth.com).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @better-auth/drizzle-adapter
9
+ ```
10
+
11
+ ## Documentation
12
+
13
+ For full documentation, visit [better-auth.com/docs/adapters/drizzle](https://www.better-auth.com/docs/adapters/drizzle).
14
+
15
+ ## License
16
+
17
+ MIT
package/dist/index.d.mts CHANGED
@@ -44,4 +44,5 @@ interface DrizzleAdapterConfig {
44
44
  }
45
45
  declare const drizzleAdapter: (db: DB, config: DrizzleAdapterConfig) => (options: BetterAuthOptions) => DBAdapter<BetterAuthOptions>;
46
46
  //#endregion
47
- export { DB, DrizzleAdapterConfig, drizzleAdapter };
47
+ export { DB, DrizzleAdapterConfig, drizzleAdapter };
48
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -6,9 +6,9 @@ import { and, asc, count, desc, eq, gt, gte, inArray, like, lt, lte, ne, notInAr
6
6
  //#region src/drizzle-adapter.ts
7
7
  const drizzleAdapter = (db, config) => {
8
8
  let lazyOptions = null;
9
- const createCustomAdapter = (db$1) => ({ getFieldName, options }) => {
9
+ const createCustomAdapter = (db) => ({ getFieldName, getDefaultFieldName, options }) => {
10
10
  function getSchema(model) {
11
- const schema = config.schema || db$1._.fullSchema;
11
+ const schema = config.schema || db._.fullSchema;
12
12
  if (!schema) throw new BetterAuthError("Drizzle adapter failed to initialize. Schema not found. Please provide a schema object in the adapter options object.");
13
13
  const schemaModel = schema[model];
14
14
  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.`);
@@ -27,15 +27,15 @@ const drizzleAdapter = (db, config) => {
27
27
  };
28
28
  return w;
29
29
  }), model);
30
- return (await db$1.select().from(schemaModel).where(...clause))[0];
30
+ return (await db.select().from(schemaModel).where(...clause))[0];
31
31
  } else if (builderVal && builderVal[0]?.id?.value) {
32
32
  let tId = builderVal[0]?.id?.value;
33
- if (!tId) tId = (await db$1.select({ id: sql`LAST_INSERT_ID()` }).from(schemaModel).orderBy(desc(schemaModel.id)).limit(1))[0].id;
34
- return (await db$1.select().from(schemaModel).where(eq(schemaModel.id, tId)).limit(1).execute())[0];
35
- } else if (data.id) return (await db$1.select().from(schemaModel).where(eq(schemaModel.id, data.id)).limit(1).execute())[0];
33
+ if (!tId) tId = (await db.select({ id: sql`LAST_INSERT_ID()` }).from(schemaModel).orderBy(desc(schemaModel.id)).limit(1))[0].id;
34
+ return (await db.select().from(schemaModel).where(eq(schemaModel.id, tId)).limit(1).execute())[0];
35
+ } else if (data.id) return (await db.select().from(schemaModel).where(eq(schemaModel.id, data.id)).limit(1).execute())[0];
36
36
  else {
37
37
  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.`);
38
- return (await db$1.select().from(schemaModel).orderBy(desc(schemaModel.id)).limit(1).execute())[0];
38
+ return (await db.select().from(schemaModel).orderBy(desc(schemaModel.id)).limit(1).execute())[0];
39
39
  }
40
40
  };
41
41
  function convertWhereClause(where, model) {
@@ -122,91 +122,169 @@ const drizzleAdapter = (db, config) => {
122
122
  }
123
123
  function checkMissingFields(schema, model, values) {
124
124
  if (!schema) throw new BetterAuthError("Drizzle adapter failed to initialize. Drizzle Schema not found. Please provide a schema object in the adapter options object.");
125
- for (const key in values) if (!schema[key]) throw new BetterAuthError(`The field "${key}" does not exist in the "${model}" Drizzle schema. Please update your drizzle schema or re-generate using "npx @better-auth/cli@latest generate".`);
125
+ for (const key in values) {
126
+ let fieldName;
127
+ try {
128
+ fieldName = getFieldName({
129
+ model,
130
+ field: key
131
+ });
132
+ } catch {
133
+ fieldName = key;
134
+ }
135
+ if (!schema[fieldName]) throw new BetterAuthError(`The field "${key}" does not exist in the "${model}" Drizzle schema. Please update your drizzle schema or re-generate using "npx auth@latest generate".`);
136
+ }
137
+ }
138
+ /**
139
+ * Resolve the db.query key for a model.
140
+ *
141
+ * When `usePlural` is false (default), Better Auth uses singular model
142
+ * names like "user", but Drizzle's db.query is keyed by the schema
143
+ * export names (often plural like "users"). This function:
144
+ *
145
+ * 1. Tries the model name directly (works when schema keys match)
146
+ * 2. If usePlural is set, tries appending "s"
147
+ * 3. Falls back to scanning config.schema to find which db.query key
148
+ * corresponds to the same table object
149
+ */
150
+ function getQueryModel(model) {
151
+ if (db.query[model]) return model;
152
+ if (config.usePlural) {
153
+ const plural = `${model}s`;
154
+ if (db.query[plural]) return plural;
155
+ }
156
+ if (config.schema) {
157
+ const targetTable = config.schema[model];
158
+ if (targetTable) {
159
+ const fullSchema = db._.fullSchema;
160
+ if (fullSchema) {
161
+ for (const key of Object.keys(db.query)) if (fullSchema[key] === targetTable) return key;
162
+ }
163
+ }
164
+ }
165
+ return null;
126
166
  }
127
167
  return {
128
168
  async create({ model, data: values }) {
129
169
  const schemaModel = getSchema(model);
130
170
  checkMissingFields(schemaModel, model, values);
131
- return await withReturning(model, db$1.insert(schemaModel).values(values), values);
171
+ return await withReturning(model, db.insert(schemaModel).values(values), values);
132
172
  },
133
- async findOne({ model, where, join }) {
173
+ async findOne({ model, where, select, join }) {
134
174
  const schemaModel = getSchema(model);
135
175
  const clause = convertWhereClause(where, model);
136
- if (options.experimental?.joins) if (!db$1.query || !db$1.query[model]) {
137
- logger.error(`[# Drizzle Adapter]: The model "${model}" was not found in the query object. Please update your Drizzle schema to include relations or re-generate using "npx @better-auth/cli@latest generate".`);
138
- logger.info("Falling back to regular query");
139
- } else {
140
- let includes;
141
- const pluralJoinResults = [];
142
- if (join) {
143
- includes = {};
144
- const joinEntries = Object.entries(join);
145
- for (const [model$1, joinAttr] of joinEntries) {
146
- const limit = joinAttr.limit ?? options.advanced?.database?.defaultFindManyLimit ?? 100;
147
- const isUnique = joinAttr.relation === "one-to-one";
148
- const pluralSuffix = isUnique || config.usePlural ? "" : "s";
149
- includes[`${model$1}${pluralSuffix}`] = isUnique ? true : { limit };
150
- if (!isUnique) pluralJoinResults.push(`${model$1}${pluralSuffix}`);
176
+ if (options.experimental?.joins) {
177
+ const queryModel = getQueryModel(model);
178
+ if (!db.query || !queryModel) {
179
+ logger.error(`[# Drizzle Adapter]: The model "${model}" was not found in the query object. Please update your Drizzle schema to include relations or re-generate using "npx auth@latest generate".`);
180
+ logger.info("Falling back to regular query");
181
+ } else {
182
+ let includes;
183
+ const pluralJoinResults = [];
184
+ if (join) {
185
+ includes = {};
186
+ const joinEntries = Object.entries(join);
187
+ for (const [model, joinAttr] of joinEntries) {
188
+ const limit = joinAttr.limit ?? options.advanced?.database?.defaultFindManyLimit ?? 100;
189
+ const isUnique = joinAttr.relation === "one-to-one";
190
+ const pluralSuffix = isUnique || config.usePlural ? "" : "s";
191
+ includes[`${model}${pluralSuffix}`] = isUnique ? true : { limit };
192
+ if (!isUnique) pluralJoinResults.push(`${model}${pluralSuffix}`);
193
+ }
151
194
  }
195
+ const res = await db.query[queryModel].findFirst({
196
+ where: clause[0],
197
+ columns: select?.length && select.length > 0 ? select.reduce((acc, field) => {
198
+ acc[getFieldName({
199
+ model,
200
+ field
201
+ })] = true;
202
+ return acc;
203
+ }, {}) : void 0,
204
+ with: includes
205
+ });
206
+ if (res) for (const pluralJoinResult of pluralJoinResults) {
207
+ const singularKey = !config.usePlural ? pluralJoinResult.slice(0, -1) : pluralJoinResult;
208
+ res[singularKey] = res[pluralJoinResult];
209
+ if (pluralJoinResult !== singularKey) delete res[pluralJoinResult];
210
+ }
211
+ return res;
152
212
  }
153
- const res$1 = await db$1.query[model].findFirst({
154
- where: clause[0],
155
- with: includes
156
- });
157
- if (res$1) for (const pluralJoinResult of pluralJoinResults) {
158
- const singularKey = !config.usePlural ? pluralJoinResult.slice(0, -1) : pluralJoinResult;
159
- res$1[singularKey] = res$1[pluralJoinResult];
160
- if (pluralJoinResult !== singularKey) delete res$1[pluralJoinResult];
161
- }
162
- return res$1;
163
213
  }
164
- const res = await db$1.select().from(schemaModel).where(...clause);
214
+ const res = await db.select(select?.length && select.length > 0 ? select.reduce((acc, field) => {
215
+ const fieldName = getFieldName({
216
+ model,
217
+ field
218
+ });
219
+ return {
220
+ ...acc,
221
+ [fieldName]: schemaModel[fieldName]
222
+ };
223
+ }, {}) : void 0).from(schemaModel).where(...clause);
165
224
  if (!res.length) return null;
166
225
  return res[0];
167
226
  },
168
- async findMany({ model, where, sortBy, limit, offset, join }) {
227
+ async findMany({ model, where, sortBy, limit, select, offset, join }) {
169
228
  const schemaModel = getSchema(model);
170
229
  const clause = where ? convertWhereClause(where, model) : [];
171
230
  const sortFn = sortBy?.direction === "desc" ? desc : asc;
172
- if (options.experimental?.joins) if (!db$1.query[model]) {
173
- logger.error(`[# Drizzle Adapter]: The model "${model}" was not found in the query object. Please update your Drizzle schema to include relations or re-generate using "npx @better-auth/cli@latest generate".`);
174
- logger.info("Falling back to regular query");
175
- } else {
176
- let includes;
177
- const pluralJoinResults = [];
178
- if (join) {
179
- includes = {};
180
- const joinEntries = Object.entries(join);
181
- for (const [model$1, joinAttr] of joinEntries) {
182
- const isUnique = joinAttr.relation === "one-to-one";
183
- const limit$1 = joinAttr.limit ?? options.advanced?.database?.defaultFindManyLimit ?? 100;
184
- const pluralSuffix = isUnique || config.usePlural ? "" : "s";
185
- includes[`${model$1}${pluralSuffix}`] = isUnique ? true : { limit: limit$1 };
186
- if (!isUnique) pluralJoinResults.push(`${model$1}${pluralSuffix}`);
231
+ if (options.experimental?.joins) {
232
+ const queryModel = getQueryModel(model);
233
+ if (!queryModel) {
234
+ logger.error(`[# Drizzle Adapter]: The model "${model}" was not found in the query object. Please update your Drizzle schema to include relations or re-generate using "npx auth@latest generate".`);
235
+ logger.info("Falling back to regular query");
236
+ } else {
237
+ let includes;
238
+ const pluralJoinResults = [];
239
+ if (join) {
240
+ includes = {};
241
+ const joinEntries = Object.entries(join);
242
+ for (const [model, joinAttr] of joinEntries) {
243
+ const isUnique = joinAttr.relation === "one-to-one";
244
+ const limit = joinAttr.limit ?? options.advanced?.database?.defaultFindManyLimit ?? 100;
245
+ const pluralSuffix = isUnique || config.usePlural ? "" : "s";
246
+ includes[`${model}${pluralSuffix}`] = isUnique ? true : { limit };
247
+ if (!isUnique) pluralJoinResults.push(`${model}${pluralSuffix}`);
248
+ }
249
+ }
250
+ let orderBy = void 0;
251
+ if (sortBy?.field) orderBy = [sortFn(schemaModel[getFieldName({
252
+ model,
253
+ field: sortBy?.field
254
+ })])];
255
+ const res = await db.query[queryModel].findMany({
256
+ where: clause[0],
257
+ with: includes,
258
+ columns: select?.length && select.length > 0 ? select.reduce((acc, field) => {
259
+ acc[getFieldName({
260
+ model,
261
+ field
262
+ })] = true;
263
+ return acc;
264
+ }, {}) : void 0,
265
+ limit: limit ?? 100,
266
+ offset: offset ?? 0,
267
+ orderBy
268
+ });
269
+ if (res) for (const item of res) for (const pluralJoinResult of pluralJoinResults) {
270
+ const singularKey = !config.usePlural ? pluralJoinResult.slice(0, -1) : pluralJoinResult;
271
+ if (singularKey === pluralJoinResult) continue;
272
+ item[singularKey] = item[pluralJoinResult];
273
+ delete item[pluralJoinResult];
187
274
  }
275
+ return res;
188
276
  }
189
- let orderBy = void 0;
190
- if (sortBy?.field) orderBy = [sortFn(schemaModel[getFieldName({
277
+ }
278
+ let builder = db.select(select?.length && select.length > 0 ? select.reduce((acc, field) => {
279
+ const fieldName = getFieldName({
191
280
  model,
192
- field: sortBy?.field
193
- })])];
194
- const res = await db$1.query[model].findMany({
195
- where: clause[0],
196
- with: includes,
197
- limit: limit ?? 100,
198
- offset: offset ?? 0,
199
- orderBy
281
+ field
200
282
  });
201
- if (res) for (const item of res) for (const pluralJoinResult of pluralJoinResults) {
202
- const singularKey = !config.usePlural ? pluralJoinResult.slice(0, -1) : pluralJoinResult;
203
- if (singularKey === pluralJoinResult) continue;
204
- item[singularKey] = item[pluralJoinResult];
205
- delete item[pluralJoinResult];
206
- }
207
- return res;
208
- }
209
- let builder = db$1.select().from(schemaModel);
283
+ return {
284
+ ...acc,
285
+ [fieldName]: schemaModel[fieldName]
286
+ };
287
+ }, {}) : void 0).from(schemaModel);
210
288
  const effectiveLimit = limit;
211
289
  const effectiveOffset = offset;
212
290
  if (typeof effectiveLimit !== "undefined") builder = builder.limit(effectiveLimit);
@@ -220,37 +298,37 @@ const drizzleAdapter = (db, config) => {
220
298
  async count({ model, where }) {
221
299
  const schemaModel = getSchema(model);
222
300
  const clause = where ? convertWhereClause(where, model) : [];
223
- return (await db$1.select({ count: count() }).from(schemaModel).where(...clause))[0].count;
301
+ return (await db.select({ count: count() }).from(schemaModel).where(...clause))[0].count;
224
302
  },
225
303
  async update({ model, where, update: values }) {
226
304
  const schemaModel = getSchema(model);
227
305
  const clause = convertWhereClause(where, model);
228
- return await withReturning(model, db$1.update(schemaModel).set(values).where(...clause), values, where);
306
+ return await withReturning(model, db.update(schemaModel).set(values).where(...clause), values, where);
229
307
  },
230
308
  async updateMany({ model, where, update: values }) {
231
309
  const schemaModel = getSchema(model);
232
310
  const clause = convertWhereClause(where, model);
233
- return await db$1.update(schemaModel).set(values).where(...clause);
311
+ return await db.update(schemaModel).set(values).where(...clause);
234
312
  },
235
313
  async delete({ model, where }) {
236
314
  const schemaModel = getSchema(model);
237
315
  const clause = convertWhereClause(where, model);
238
- return await db$1.delete(schemaModel).where(...clause);
316
+ return await db.delete(schemaModel).where(...clause);
239
317
  },
240
318
  async deleteMany({ model, where }) {
241
319
  const schemaModel = getSchema(model);
242
320
  const clause = convertWhereClause(where, model);
243
- const res = await db$1.delete(schemaModel).where(...clause);
244
- let count$1 = 0;
245
- if (res && "rowCount" in res) count$1 = res.rowCount;
246
- else if (Array.isArray(res)) count$1 = res.length;
247
- else if (res && ("affectedRows" in res || "rowsAffected" in res || "changes" in res)) count$1 = res.affectedRows ?? res.rowsAffected ?? res.changes;
248
- if (typeof count$1 !== "number") logger.error("[Drizzle Adapter] The result of the deleteMany operation is not a number. This is likely a bug in the adapter. Please report this issue to the Better Auth team.", {
321
+ const res = await db.delete(schemaModel).where(...clause);
322
+ let count = 0;
323
+ if (res && "rowCount" in res) count = res.rowCount;
324
+ else if (Array.isArray(res)) count = res.length;
325
+ else if (res && ("affectedRows" in res || "rowsAffected" in res || "changes" in res)) count = res.affectedRows ?? res.rowsAffected ?? res.changes;
326
+ if (typeof count !== "number") logger.error("[Drizzle Adapter] The result of the deleteMany operation is not a number. This is likely a bug in the adapter. Please report this issue to the Better Auth team.", {
249
327
  res,
250
328
  model,
251
329
  where
252
330
  });
253
- return count$1;
331
+ return count;
254
332
  },
255
333
  options: config
256
334
  };
@@ -265,6 +343,13 @@ const drizzleAdapter = (db, config) => {
265
343
  supportsUUIDs: config.provider === "pg" ? true : false,
266
344
  supportsJSON: config.provider === "pg" ? true : false,
267
345
  supportsArrays: config.provider === "pg" ? true : false,
346
+ customTransformOutput: ({ data, fieldAttributes }) => {
347
+ if (fieldAttributes.type === "date") {
348
+ if (data === null || data === void 0) return data;
349
+ return new Date(data);
350
+ }
351
+ return data;
352
+ },
268
353
  transaction: config.transaction ?? false ? (cb) => db.transaction((tx) => {
269
354
  return cb(createAdapterFactory({
270
355
  config: adapterOptions.config,
@@ -282,4 +367,5 @@ const drizzleAdapter = (db, config) => {
282
367
  };
283
368
 
284
369
  //#endregion
285
- export { drizzleAdapter };
370
+ export { drizzleAdapter };
371
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/drizzle-adapter.ts"],"sourcesContent":["import type { BetterAuthOptions } from \"@better-auth/core\";\nimport type {\n\tAdapterFactoryCustomizeAdapterCreator,\n\tAdapterFactoryOptions,\n\tDBAdapter,\n\tDBAdapterDebugLogOption,\n\tWhere,\n} from \"@better-auth/core/db/adapter\";\nimport { createAdapterFactory } from \"@better-auth/core/db/adapter\";\nimport { logger } from \"@better-auth/core/env\";\nimport { BetterAuthError } from \"@better-auth/core/error\";\nimport type { SQL } from \"drizzle-orm\";\nimport {\n\tand,\n\tasc,\n\tcount,\n\tdesc,\n\teq,\n\tgt,\n\tgte,\n\tinArray,\n\tlike,\n\tlt,\n\tlte,\n\tne,\n\tnotInArray,\n\tor,\n\tsql,\n} from \"drizzle-orm\";\n\nexport interface DB {\n\t[key: string]: any;\n}\n\nexport interface DrizzleAdapterConfig {\n\t/**\n\t * The schema object that defines the tables and fields\n\t */\n\tschema?: Record<string, any> | undefined;\n\t/**\n\t * The database provider\n\t */\n\tprovider: \"pg\" | \"mysql\" | \"sqlite\";\n\t/**\n\t * If the table names in the schema are plural\n\t * set this to true. For example, if the schema\n\t * has an object with a key \"users\" instead of \"user\"\n\t */\n\tusePlural?: boolean | undefined;\n\t/**\n\t * Enable debug logs for the adapter\n\t *\n\t * @default false\n\t */\n\tdebugLogs?: DBAdapterDebugLogOption | undefined;\n\t/**\n\t * By default snake case is used for table and field names\n\t * when the CLI is used to generate the schema. If you want\n\t * to use camel case, set this to true.\n\t * @default false\n\t */\n\tcamelCase?: boolean | undefined;\n\t/**\n\t * Whether to execute multiple operations in a transaction.\n\t *\n\t * If the database doesn't support transactions,\n\t * set this to `false` and operations will be executed sequentially.\n\t * @default false\n\t */\n\ttransaction?: boolean | undefined;\n}\n\nexport const drizzleAdapter = (db: DB, config: DrizzleAdapterConfig) => {\n\tlet lazyOptions: BetterAuthOptions | null = null;\n\tconst createCustomAdapter =\n\t\t(db: DB): AdapterFactoryCustomizeAdapterCreator =>\n\t\t({ getFieldName, getDefaultFieldName, options }) => {\n\t\t\tfunction getSchema(model: string) {\n\t\t\t\tconst schema = config.schema || db._.fullSchema;\n\t\t\t\tif (!schema) {\n\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\"Drizzle adapter failed to initialize. Schema not found. Please provide a schema object in the adapter options object.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst schemaModel = schema[model];\n\t\t\t\tif (!schemaModel) {\n\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t`[# Drizzle Adapter]: The model \"${model}\" was not found in the schema object. Please pass the schema directly to the adapter options.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn schemaModel;\n\t\t\t}\n\t\t\tconst withReturning = async (\n\t\t\t\tmodel: string,\n\t\t\t\tbuilder: any,\n\t\t\t\tdata: Record<string, any>,\n\t\t\t\twhere?: Where[] | undefined,\n\t\t\t) => {\n\t\t\t\tif (config.provider !== \"mysql\") {\n\t\t\t\t\tconst c = await builder.returning();\n\t\t\t\t\treturn c[0];\n\t\t\t\t}\n\t\t\t\tawait builder.execute();\n\t\t\t\tconst schemaModel = getSchema(model);\n\t\t\t\tconst builderVal = builder.config?.values;\n\t\t\t\tif (where?.length) {\n\t\t\t\t\t// If we're updating a field that's in the where clause, use the new value\n\t\t\t\t\tconst updatedWhere = where.map((w) => {\n\t\t\t\t\t\t// If this field was updated, use the new value for lookup\n\t\t\t\t\t\tif (data[w.field] !== undefined) {\n\t\t\t\t\t\t\treturn { ...w, value: data[w.field] };\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn w;\n\t\t\t\t\t});\n\n\t\t\t\t\tconst clause = convertWhereClause(updatedWhere, model);\n\t\t\t\t\tconst res = await db\n\t\t\t\t\t\t.select()\n\t\t\t\t\t\t.from(schemaModel)\n\t\t\t\t\t\t.where(...clause);\n\t\t\t\t\treturn res[0];\n\t\t\t\t} else if (builderVal && builderVal[0]?.id?.value) {\n\t\t\t\t\tlet tId = builderVal[0]?.id?.value;\n\t\t\t\t\tif (!tId) {\n\t\t\t\t\t\t//get last inserted id\n\t\t\t\t\t\tconst lastInsertId = await db\n\t\t\t\t\t\t\t.select({ id: sql`LAST_INSERT_ID()` })\n\t\t\t\t\t\t\t.from(schemaModel)\n\t\t\t\t\t\t\t.orderBy(desc(schemaModel.id))\n\t\t\t\t\t\t\t.limit(1);\n\t\t\t\t\t\ttId = lastInsertId[0].id;\n\t\t\t\t\t}\n\t\t\t\t\tconst res = await db\n\t\t\t\t\t\t.select()\n\t\t\t\t\t\t.from(schemaModel)\n\t\t\t\t\t\t.where(eq(schemaModel.id, tId))\n\t\t\t\t\t\t.limit(1)\n\t\t\t\t\t\t.execute();\n\t\t\t\t\treturn res[0];\n\t\t\t\t} else if (data.id) {\n\t\t\t\t\tconst res = await db\n\t\t\t\t\t\t.select()\n\t\t\t\t\t\t.from(schemaModel)\n\t\t\t\t\t\t.where(eq(schemaModel.id, data.id))\n\t\t\t\t\t\t.limit(1)\n\t\t\t\t\t\t.execute();\n\t\t\t\t\treturn res[0];\n\t\t\t\t} else {\n\t\t\t\t\t// If the user doesn't have `id` as a field, then this will fail.\n\t\t\t\t\t// We expect that they defined `id` in all of their models.\n\t\t\t\t\tif (!(\"id\" in schemaModel)) {\n\t\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\t`The model \"${model}\" does not have an \"id\" field. Please use the \"id\" field as your primary key.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst res = await db\n\t\t\t\t\t\t.select()\n\t\t\t\t\t\t.from(schemaModel)\n\t\t\t\t\t\t.orderBy(desc(schemaModel.id))\n\t\t\t\t\t\t.limit(1)\n\t\t\t\t\t\t.execute();\n\t\t\t\t\treturn res[0];\n\t\t\t\t}\n\t\t\t};\n\t\t\tfunction convertWhereClause(where: Where[], model: string) {\n\t\t\t\tconst schemaModel = getSchema(model);\n\t\t\t\tif (!where) return [];\n\t\t\t\tif (where.length === 1) {\n\t\t\t\t\tconst w = where[0];\n\t\t\t\t\tif (!w) {\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\t\t\t\t\tconst field = getFieldName({ model, field: w.field });\n\t\t\t\t\tif (!schemaModel[field]) {\n\t\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\t`The field \"${w.field}\" does not exist in the schema for the model \"${model}\". Please update your schema.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (w.operator === \"in\") {\n\t\t\t\t\t\tif (!Array.isArray(w.value)) {\n\t\t\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\t\t`The value for the field \"${w.field}\" must be an array when using the \"in\" operator.`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn [inArray(schemaModel[field], w.value)];\n\t\t\t\t\t}\n\n\t\t\t\t\tif (w.operator === \"not_in\") {\n\t\t\t\t\t\tif (!Array.isArray(w.value)) {\n\t\t\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\t\t`The value for the field \"${w.field}\" must be an array when using the \"not_in\" operator.`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn [notInArray(schemaModel[field], w.value)];\n\t\t\t\t\t}\n\n\t\t\t\t\tif (w.operator === \"contains\") {\n\t\t\t\t\t\treturn [like(schemaModel[field], `%${w.value}%`)];\n\t\t\t\t\t}\n\n\t\t\t\t\tif (w.operator === \"starts_with\") {\n\t\t\t\t\t\treturn [like(schemaModel[field], `${w.value}%`)];\n\t\t\t\t\t}\n\n\t\t\t\t\tif (w.operator === \"ends_with\") {\n\t\t\t\t\t\treturn [like(schemaModel[field], `%${w.value}`)];\n\t\t\t\t\t}\n\n\t\t\t\t\tif (w.operator === \"lt\") {\n\t\t\t\t\t\treturn [lt(schemaModel[field], w.value)];\n\t\t\t\t\t}\n\n\t\t\t\t\tif (w.operator === \"lte\") {\n\t\t\t\t\t\treturn [lte(schemaModel[field], w.value)];\n\t\t\t\t\t}\n\n\t\t\t\t\tif (w.operator === \"ne\") {\n\t\t\t\t\t\treturn [ne(schemaModel[field], w.value)];\n\t\t\t\t\t}\n\n\t\t\t\t\tif (w.operator === \"gt\") {\n\t\t\t\t\t\treturn [gt(schemaModel[field], w.value)];\n\t\t\t\t\t}\n\n\t\t\t\t\tif (w.operator === \"gte\") {\n\t\t\t\t\t\treturn [gte(schemaModel[field], w.value)];\n\t\t\t\t\t}\n\n\t\t\t\t\treturn [eq(schemaModel[field], w.value)];\n\t\t\t\t}\n\t\t\t\tconst andGroup = where.filter(\n\t\t\t\t\t(w) => w.connector === \"AND\" || !w.connector,\n\t\t\t\t);\n\t\t\t\tconst orGroup = where.filter((w) => w.connector === \"OR\");\n\n\t\t\t\tconst andClause = and(\n\t\t\t\t\t...andGroup.map((w) => {\n\t\t\t\t\t\tconst field = getFieldName({ model, field: w.field });\n\t\t\t\t\t\tif (w.operator === \"in\") {\n\t\t\t\t\t\t\tif (!Array.isArray(w.value)) {\n\t\t\t\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\t\t\t`The value for the field \"${w.field}\" must be an array when using the \"in\" operator.`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn inArray(schemaModel[field], w.value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"not_in\") {\n\t\t\t\t\t\t\tif (!Array.isArray(w.value)) {\n\t\t\t\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\t\t\t`The value for the field \"${w.field}\" must be an array when using the \"not_in\" operator.`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn notInArray(schemaModel[field], w.value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"contains\") {\n\t\t\t\t\t\t\treturn like(schemaModel[field], `%${w.value}%`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"starts_with\") {\n\t\t\t\t\t\t\treturn like(schemaModel[field], `${w.value}%`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"ends_with\") {\n\t\t\t\t\t\t\treturn like(schemaModel[field], `%${w.value}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"lt\") {\n\t\t\t\t\t\t\treturn lt(schemaModel[field], w.value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"lte\") {\n\t\t\t\t\t\t\treturn lte(schemaModel[field], w.value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"gt\") {\n\t\t\t\t\t\t\treturn gt(schemaModel[field], w.value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"gte\") {\n\t\t\t\t\t\t\treturn gte(schemaModel[field], w.value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"ne\") {\n\t\t\t\t\t\t\treturn ne(schemaModel[field], w.value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn eq(schemaModel[field], w.value);\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tconst orClause = or(\n\t\t\t\t\t...orGroup.map((w) => {\n\t\t\t\t\t\tconst field = getFieldName({ model, field: w.field });\n\t\t\t\t\t\tif (w.operator === \"in\") {\n\t\t\t\t\t\t\tif (!Array.isArray(w.value)) {\n\t\t\t\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\t\t\t`The value for the field \"${w.field}\" must be an array when using the \"in\" operator.`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn inArray(schemaModel[field], w.value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"not_in\") {\n\t\t\t\t\t\t\tif (!Array.isArray(w.value)) {\n\t\t\t\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\t\t\t`The value for the field \"${w.field}\" must be an array when using the \"not_in\" operator.`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn notInArray(schemaModel[field], w.value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"contains\") {\n\t\t\t\t\t\t\treturn like(schemaModel[field], `%${w.value}%`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"starts_with\") {\n\t\t\t\t\t\t\treturn like(schemaModel[field], `${w.value}%`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"ends_with\") {\n\t\t\t\t\t\t\treturn like(schemaModel[field], `%${w.value}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"lt\") {\n\t\t\t\t\t\t\treturn lt(schemaModel[field], w.value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"lte\") {\n\t\t\t\t\t\t\treturn lte(schemaModel[field], w.value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"gt\") {\n\t\t\t\t\t\t\treturn gt(schemaModel[field], w.value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"gte\") {\n\t\t\t\t\t\t\treturn gte(schemaModel[field], w.value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (w.operator === \"ne\") {\n\t\t\t\t\t\t\treturn ne(schemaModel[field], w.value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn eq(schemaModel[field], w.value);\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\t\t\tconst clause: SQL<unknown>[] = [];\n\n\t\t\t\tif (andGroup.length) clause.push(andClause!);\n\t\t\t\tif (orGroup.length) clause.push(orClause!);\n\t\t\t\treturn clause;\n\t\t\t}\n\t\t\tfunction checkMissingFields(\n\t\t\t\tschema: Record<string, any>,\n\t\t\t\tmodel: string,\n\t\t\t\tvalues: Record<string, any>,\n\t\t\t) {\n\t\t\t\tif (!schema) {\n\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\"Drizzle adapter failed to initialize. Drizzle Schema not found. Please provide a schema object in the adapter options object.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tfor (const key in values) {\n\t\t\t\t\tlet fieldName: string;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfieldName = getFieldName({ model, field: key });\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tfieldName = key;\n\t\t\t\t\t}\n\t\t\t\t\tif (!schema[fieldName]) {\n\t\t\t\t\t\tthrow new BetterAuthError(\n\t\t\t\t\t\t\t`The field \"${key}\" does not exist in the \"${model}\" Drizzle schema. Please update your drizzle schema or re-generate using \"npx auth@latest generate\".`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Resolve the db.query key for a model.\n\t\t\t *\n\t\t\t * When `usePlural` is false (default), Better Auth uses singular model\n\t\t\t * names like \"user\", but Drizzle's db.query is keyed by the schema\n\t\t\t * export names (often plural like \"users\"). This function:\n\t\t\t *\n\t\t\t * 1. Tries the model name directly (works when schema keys match)\n\t\t\t * 2. If usePlural is set, tries appending \"s\"\n\t\t\t * 3. Falls back to scanning config.schema to find which db.query key\n\t\t\t * corresponds to the same table object\n\t\t\t */\n\t\t\tfunction getQueryModel(model: string): string | null {\n\t\t\t\tif (db.query[model]) return model;\n\n\t\t\t\tif (config.usePlural) {\n\t\t\t\t\tconst plural = `${model}s`;\n\t\t\t\t\tif (db.query[plural]) return plural;\n\t\t\t\t}\n\n\t\t\t\tif (config.schema) {\n\t\t\t\t\tconst targetTable = config.schema[model];\n\t\t\t\t\tif (targetTable) {\n\t\t\t\t\t\tconst fullSchema = db._.fullSchema;\n\t\t\t\t\t\tif (fullSchema) {\n\t\t\t\t\t\t\tfor (const key of Object.keys(db.query)) {\n\t\t\t\t\t\t\t\tif (fullSchema[key] === targetTable) {\n\t\t\t\t\t\t\t\t\treturn key;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tasync create({ model, data: values }) {\n\t\t\t\t\tconst schemaModel = getSchema(model);\n\t\t\t\t\tcheckMissingFields(schemaModel, model, values);\n\t\t\t\t\tconst builder = db.insert(schemaModel).values(values);\n\t\t\t\t\tconst returned = await withReturning(model, builder, values);\n\t\t\t\t\treturn returned;\n\t\t\t\t},\n\t\t\t\tasync findOne({ model, where, select, join }) {\n\t\t\t\t\tconst schemaModel = getSchema(model);\n\t\t\t\t\tconst clause = convertWhereClause(where, model);\n\n\t\t\t\t\tif (options.experimental?.joins) {\n\t\t\t\t\t\tconst queryModel = getQueryModel(model);\n\t\t\t\t\t\tif (!db.query || !queryModel) {\n\t\t\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t\t\t`[# Drizzle Adapter]: The model \"${model}\" was not found in the query object. Please update your Drizzle schema to include relations or re-generate using \"npx auth@latest generate\".`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tlogger.info(\"Falling back to regular query\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlet includes:\n\t\t\t\t\t\t\t\t| Record<string, { limit: number } | boolean>\n\t\t\t\t\t\t\t\t| undefined;\n\n\t\t\t\t\t\t\tconst pluralJoinResults: string[] = [];\n\t\t\t\t\t\t\tif (join) {\n\t\t\t\t\t\t\t\tincludes = {};\n\t\t\t\t\t\t\t\tconst joinEntries = Object.entries(join);\n\t\t\t\t\t\t\t\tfor (const [model, joinAttr] of joinEntries) {\n\t\t\t\t\t\t\t\t\tconst limit =\n\t\t\t\t\t\t\t\t\t\tjoinAttr.limit ??\n\t\t\t\t\t\t\t\t\t\toptions.advanced?.database?.defaultFindManyLimit ??\n\t\t\t\t\t\t\t\t\t\t100;\n\t\t\t\t\t\t\t\t\tconst isUnique = joinAttr.relation === \"one-to-one\";\n\t\t\t\t\t\t\t\t\tconst pluralSuffix = isUnique || config.usePlural ? \"\" : \"s\";\n\t\t\t\t\t\t\t\t\tincludes[`${model}${pluralSuffix}`] = isUnique\n\t\t\t\t\t\t\t\t\t\t? true\n\t\t\t\t\t\t\t\t\t\t: { limit };\n\t\t\t\t\t\t\t\t\tif (!isUnique) {\n\t\t\t\t\t\t\t\t\t\tpluralJoinResults.push(`${model}${pluralSuffix}`);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst query = db.query[queryModel].findFirst({\n\t\t\t\t\t\t\t\twhere: clause[0],\n\t\t\t\t\t\t\t\tcolumns:\n\t\t\t\t\t\t\t\t\tselect?.length && select.length > 0\n\t\t\t\t\t\t\t\t\t\t? select.reduce(\n\t\t\t\t\t\t\t\t\t\t\t\t(acc, field) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tacc[getFieldName({ model, field })] = true;\n\t\t\t\t\t\t\t\t\t\t\t\t\treturn acc;\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t{} as Record<string, boolean>,\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t\twith: includes,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tconst res = await query;\n\n\t\t\t\t\t\t\tif (res) {\n\t\t\t\t\t\t\t\tfor (const pluralJoinResult of pluralJoinResults) {\n\t\t\t\t\t\t\t\t\tconst singularKey = !config.usePlural\n\t\t\t\t\t\t\t\t\t\t? pluralJoinResult.slice(0, -1)\n\t\t\t\t\t\t\t\t\t\t: pluralJoinResult;\n\t\t\t\t\t\t\t\t\tres[singularKey] = res[pluralJoinResult];\n\t\t\t\t\t\t\t\t\tif (pluralJoinResult !== singularKey) {\n\t\t\t\t\t\t\t\t\t\tdelete res[pluralJoinResult];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn res;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst query = db\n\t\t\t\t\t\t.select(\n\t\t\t\t\t\t\tselect?.length && select.length > 0\n\t\t\t\t\t\t\t\t? select.reduce((acc, field) => {\n\t\t\t\t\t\t\t\t\t\tconst fieldName = getFieldName({ model, field });\n\t\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\t\t...acc,\n\t\t\t\t\t\t\t\t\t\t\t[fieldName]: schemaModel[fieldName],\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t}, {})\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.from(schemaModel)\n\t\t\t\t\t\t.where(...clause);\n\n\t\t\t\t\tconst res = await query;\n\n\t\t\t\t\tif (!res.length) return null;\n\t\t\t\t\treturn res[0];\n\t\t\t\t},\n\t\t\t\tasync findMany({ model, where, sortBy, limit, select, offset, join }) {\n\t\t\t\t\tconst schemaModel = getSchema(model);\n\t\t\t\t\tconst clause = where ? convertWhereClause(where, model) : [];\n\t\t\t\t\tconst sortFn = sortBy?.direction === \"desc\" ? desc : asc;\n\n\t\t\t\t\tif (options.experimental?.joins) {\n\t\t\t\t\t\tconst queryModel = getQueryModel(model);\n\t\t\t\t\t\tif (!queryModel) {\n\t\t\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t\t\t`[# Drizzle Adapter]: The model \"${model}\" was not found in the query object. Please update your Drizzle schema to include relations or re-generate using \"npx auth@latest generate\".`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tlogger.info(\"Falling back to regular query\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlet includes:\n\t\t\t\t\t\t\t\t| Record<string, { limit: number } | boolean>\n\t\t\t\t\t\t\t\t| undefined;\n\n\t\t\t\t\t\t\tconst pluralJoinResults: string[] = [];\n\t\t\t\t\t\t\tif (join) {\n\t\t\t\t\t\t\t\tincludes = {};\n\t\t\t\t\t\t\t\tconst joinEntries = Object.entries(join);\n\t\t\t\t\t\t\t\tfor (const [model, joinAttr] of joinEntries) {\n\t\t\t\t\t\t\t\t\tconst isUnique = joinAttr.relation === \"one-to-one\";\n\t\t\t\t\t\t\t\t\tconst limit =\n\t\t\t\t\t\t\t\t\t\tjoinAttr.limit ??\n\t\t\t\t\t\t\t\t\t\toptions.advanced?.database?.defaultFindManyLimit ??\n\t\t\t\t\t\t\t\t\t\t100;\n\t\t\t\t\t\t\t\t\tconst pluralSuffix = isUnique || config.usePlural ? \"\" : \"s\";\n\t\t\t\t\t\t\t\t\tincludes[`${model}${pluralSuffix}`] = isUnique\n\t\t\t\t\t\t\t\t\t\t? true\n\t\t\t\t\t\t\t\t\t\t: { limit };\n\t\t\t\t\t\t\t\t\tif (!isUnique)\n\t\t\t\t\t\t\t\t\t\tpluralJoinResults.push(`${model}${pluralSuffix}`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlet orderBy: SQL<unknown>[] | undefined = undefined;\n\t\t\t\t\t\t\tif (sortBy?.field) {\n\t\t\t\t\t\t\t\torderBy = [\n\t\t\t\t\t\t\t\t\tsortFn(\n\t\t\t\t\t\t\t\t\t\tschemaModel[getFieldName({ model, field: sortBy?.field })],\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst query = db.query[queryModel].findMany({\n\t\t\t\t\t\t\t\twhere: clause[0],\n\t\t\t\t\t\t\t\twith: includes,\n\t\t\t\t\t\t\t\tcolumns:\n\t\t\t\t\t\t\t\t\tselect?.length && select.length > 0\n\t\t\t\t\t\t\t\t\t\t? select.reduce(\n\t\t\t\t\t\t\t\t\t\t\t\t(acc, field) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tacc[getFieldName({ model, field })] = true;\n\t\t\t\t\t\t\t\t\t\t\t\t\treturn acc;\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t{} as Record<string, boolean>,\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t\t\tlimit: limit ?? 100,\n\t\t\t\t\t\t\t\toffset: offset ?? 0,\n\t\t\t\t\t\t\t\torderBy,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tconst res = await query;\n\t\t\t\t\t\t\tif (res) {\n\t\t\t\t\t\t\t\tfor (const item of res) {\n\t\t\t\t\t\t\t\t\tfor (const pluralJoinResult of pluralJoinResults) {\n\t\t\t\t\t\t\t\t\t\tconst singularKey = !config.usePlural\n\t\t\t\t\t\t\t\t\t\t\t? pluralJoinResult.slice(0, -1)\n\t\t\t\t\t\t\t\t\t\t\t: pluralJoinResult;\n\t\t\t\t\t\t\t\t\t\tif (singularKey === pluralJoinResult) continue;\n\t\t\t\t\t\t\t\t\t\titem[singularKey] = item[pluralJoinResult];\n\t\t\t\t\t\t\t\t\t\tdelete item[pluralJoinResult];\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn res;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tlet builder = db\n\t\t\t\t\t\t.select(\n\t\t\t\t\t\t\tselect?.length && select.length > 0\n\t\t\t\t\t\t\t\t? select.reduce((acc, field) => {\n\t\t\t\t\t\t\t\t\t\tconst fieldName = getFieldName({ model, field });\n\t\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\t\t...acc,\n\t\t\t\t\t\t\t\t\t\t\t[fieldName]: schemaModel[fieldName],\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t}, {})\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.from(schemaModel);\n\n\t\t\t\t\tconst effectiveLimit = limit;\n\t\t\t\t\tconst effectiveOffset = offset;\n\n\t\t\t\t\tif (typeof effectiveLimit !== \"undefined\") {\n\t\t\t\t\t\tbuilder = builder.limit(effectiveLimit);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (typeof effectiveOffset !== \"undefined\") {\n\t\t\t\t\t\tbuilder = builder.offset(effectiveOffset);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (sortBy?.field) {\n\t\t\t\t\t\tbuilder = builder.orderBy(\n\t\t\t\t\t\t\tsortFn(\n\t\t\t\t\t\t\t\tschemaModel[getFieldName({ model, field: sortBy?.field })],\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst res = await builder.where(...clause);\n\t\t\t\t\treturn res;\n\t\t\t\t},\n\t\t\t\tasync count({ model, where }) {\n\t\t\t\t\tconst schemaModel = getSchema(model);\n\t\t\t\t\tconst clause = where ? convertWhereClause(where, model) : [];\n\t\t\t\t\tconst res = await db\n\t\t\t\t\t\t.select({ count: count() })\n\t\t\t\t\t\t.from(schemaModel)\n\t\t\t\t\t\t.where(...clause);\n\t\t\t\t\treturn res[0].count;\n\t\t\t\t},\n\t\t\t\tasync update({ model, where, update: values }) {\n\t\t\t\t\tconst schemaModel = getSchema(model);\n\t\t\t\t\tconst clause = convertWhereClause(where, model);\n\t\t\t\t\tconst builder = db\n\t\t\t\t\t\t.update(schemaModel)\n\t\t\t\t\t\t.set(values)\n\t\t\t\t\t\t.where(...clause);\n\t\t\t\t\treturn await withReturning(model, builder, values as any, where);\n\t\t\t\t},\n\t\t\t\tasync updateMany({ model, where, update: values }) {\n\t\t\t\t\tconst schemaModel = getSchema(model);\n\t\t\t\t\tconst clause = convertWhereClause(where, model);\n\t\t\t\t\tconst builder = db\n\t\t\t\t\t\t.update(schemaModel)\n\t\t\t\t\t\t.set(values)\n\t\t\t\t\t\t.where(...clause);\n\t\t\t\t\treturn await builder;\n\t\t\t\t},\n\t\t\t\tasync delete({ model, where }) {\n\t\t\t\t\tconst schemaModel = getSchema(model);\n\t\t\t\t\tconst clause = convertWhereClause(where, model);\n\t\t\t\t\tconst builder = db.delete(schemaModel).where(...clause);\n\t\t\t\t\treturn await builder;\n\t\t\t\t},\n\t\t\t\tasync deleteMany({ model, where }) {\n\t\t\t\t\tconst schemaModel = getSchema(model);\n\t\t\t\t\tconst clause = convertWhereClause(where, model);\n\t\t\t\t\tconst builder = db.delete(schemaModel).where(...clause);\n\t\t\t\t\tconst res = await builder;\n\t\t\t\t\tlet count = 0;\n\t\t\t\t\tif (res && \"rowCount\" in res) count = res.rowCount;\n\t\t\t\t\telse if (Array.isArray(res)) count = res.length;\n\t\t\t\t\telse if (\n\t\t\t\t\t\tres &&\n\t\t\t\t\t\t(\"affectedRows\" in res || \"rowsAffected\" in res || \"changes\" in res)\n\t\t\t\t\t)\n\t\t\t\t\t\tcount = res.affectedRows ?? res.rowsAffected ?? res.changes;\n\t\t\t\t\tif (typeof count !== \"number\") {\n\t\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t\t\"[Drizzle Adapter] The result of the deleteMany operation is not a number. This is likely a bug in the adapter. Please report this issue to the Better Auth team.\",\n\t\t\t\t\t\t\t{ res, model, where },\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn count;\n\t\t\t\t},\n\t\t\t\toptions: config,\n\t\t\t};\n\t\t};\n\tlet adapterOptions: AdapterFactoryOptions | null = null;\n\tadapterOptions = {\n\t\tconfig: {\n\t\t\tadapterId: \"drizzle\",\n\t\t\tadapterName: \"Drizzle Adapter\",\n\t\t\tusePlural: config.usePlural ?? false,\n\t\t\tdebugLogs: config.debugLogs ?? false,\n\t\t\tsupportsUUIDs: config.provider === \"pg\" ? true : false,\n\t\t\tsupportsJSON:\n\t\t\t\tconfig.provider === \"pg\" // even though mysql also supports it, mysql requires to pass stringified json anyway.\n\t\t\t\t\t? true\n\t\t\t\t\t: false,\n\t\t\tsupportsArrays: config.provider === \"pg\" ? true : false,\n\t\t\tcustomTransformOutput: ({ data, fieldAttributes }) => {\n\t\t\t\t// not all providers support dates\n\t\t\t\t// one such example case is https://github.com/better-auth/better-auth/issues/7819\n\t\t\t\tif (fieldAttributes.type === \"date\") {\n\t\t\t\t\tif (data === null || data === undefined) {\n\t\t\t\t\t\treturn data;\n\t\t\t\t\t}\n\t\t\t\t\treturn new Date(data);\n\t\t\t\t}\n\t\t\t\treturn data;\n\t\t\t},\n\t\t\ttransaction:\n\t\t\t\t(config.transaction ?? false)\n\t\t\t\t\t? (cb) =>\n\t\t\t\t\t\t\tdb.transaction((tx: DB) => {\n\t\t\t\t\t\t\t\tconst adapter = createAdapterFactory({\n\t\t\t\t\t\t\t\t\tconfig: adapterOptions!.config,\n\t\t\t\t\t\t\t\t\tadapter: createCustomAdapter(tx),\n\t\t\t\t\t\t\t\t})(lazyOptions!);\n\t\t\t\t\t\t\t\treturn cb(adapter);\n\t\t\t\t\t\t\t})\n\t\t\t\t\t: false,\n\t\t},\n\t\tadapter: createCustomAdapter(db),\n\t};\n\tconst adapter = createAdapterFactory(adapterOptions);\n\treturn (options: BetterAuthOptions): DBAdapter<BetterAuthOptions> => {\n\t\tlazyOptions = options;\n\t\treturn adapter(options);\n\t};\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,13 +1,28 @@
1
1
  {
2
2
  "name": "@better-auth/drizzle-adapter",
3
- "version": "1.5.0-beta.9",
3
+ "version": "1.5.0",
4
4
  "description": "Drizzle adapter for Better Auth",
5
5
  "type": "module",
6
+ "license": "MIT",
7
+ "homepage": "https://www.better-auth.com/docs/adapters/drizzle",
6
8
  "repository": {
7
9
  "type": "git",
8
10
  "url": "git+https://github.com/better-auth/better-auth.git",
9
11
  "directory": "packages/drizzle-adapter"
10
12
  },
13
+ "keywords": [
14
+ "auth",
15
+ "drizzle",
16
+ "adapter",
17
+ "typescript",
18
+ "better-auth"
19
+ ],
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
11
26
  "main": "./dist/index.mjs",
12
27
  "module": "./dist/index.mjs",
13
28
  "types": "./dist/index.d.mts",
@@ -20,20 +35,22 @@
20
35
  },
21
36
  "peerDependencies": {
22
37
  "@better-auth/utils": "^0.3.0",
23
- "drizzle-orm": "^0.30.0",
24
- "@better-auth/core": "1.5.0-beta.9"
38
+ "drizzle-orm": ">=0.41.0",
39
+ "@better-auth/core": "1.5.0"
25
40
  },
26
41
  "devDependencies": {
27
- "@better-auth/utils": "^0.3.0",
28
- "drizzle-orm": "^0.30.0",
29
- "tsdown": "^0.19.0",
42
+ "@better-auth/utils": "^0.3.1",
43
+ "drizzle-orm": "^0.45.1",
44
+ "tsdown": "^0.20.3",
30
45
  "typescript": "^5.9.3",
31
- "@better-auth/core": "1.5.0-beta.9"
46
+ "@better-auth/core": "1.5.0"
32
47
  },
33
48
  "scripts": {
34
49
  "build": "tsdown",
35
50
  "dev": "tsdown --watch",
36
- "test": "vitest",
37
- "typecheck": "tsc --noEmit"
51
+ "lint:package": "publint run --strict",
52
+ "lint:types": "attw --profile esm-only --pack .",
53
+ "typecheck": "tsc --noEmit",
54
+ "test": "vitest"
38
55
  }
39
56
  }
@@ -1,13 +0,0 @@
1
-
2
- > @better-auth/drizzle-adapter@1.5.0-beta.9 build /home/runner/work/better-auth/better-auth/packages/drizzle-adapter
3
- > tsdown
4
-
5
- ℹ tsdown v0.19.0 powered by rolldown v1.0.0-beta.59
6
- ℹ config file: /home/runner/work/better-auth/better-auth/packages/drizzle-adapter/tsdown.config.ts
7
- ℹ entry: src/index.ts
8
- ℹ tsconfig: tsconfig.json
9
- ℹ Build start
10
- ℹ dist/index.mjs 13.87 kB │ gzip: 2.88 kB
11
- ℹ dist/index.d.mts  1.46 kB │ gzip: 0.65 kB
12
- ℹ 2 files, total: 15.32 kB
13
- ✔ Build complete in 8555ms
@@ -1,17 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { drizzleAdapter } from "./drizzle-adapter";
3
-
4
- describe("drizzle-adapter", () => {
5
- it("should create drizzle adapter", () => {
6
- const db = {
7
- _: {
8
- fullSchema: {},
9
- },
10
- } as any;
11
- const config = {
12
- provider: "sqlite" as const,
13
- };
14
- const adapter = drizzleAdapter(db, config);
15
- expect(adapter).toBeDefined();
16
- });
17
- });
@@ -1,606 +0,0 @@
1
- import type { BetterAuthOptions } from "@better-auth/core";
2
- import type {
3
- AdapterFactoryCustomizeAdapterCreator,
4
- AdapterFactoryOptions,
5
- DBAdapter,
6
- DBAdapterDebugLogOption,
7
- Where,
8
- } from "@better-auth/core/db/adapter";
9
- import { createAdapterFactory } from "@better-auth/core/db/adapter";
10
- import { logger } from "@better-auth/core/env";
11
- import { BetterAuthError } from "@better-auth/core/error";
12
- import type { SQL } from "drizzle-orm";
13
- import {
14
- and,
15
- asc,
16
- count,
17
- desc,
18
- eq,
19
- gt,
20
- gte,
21
- inArray,
22
- like,
23
- lt,
24
- lte,
25
- ne,
26
- notInArray,
27
- or,
28
- sql,
29
- } from "drizzle-orm";
30
-
31
- export interface DB {
32
- [key: string]: any;
33
- }
34
-
35
- export interface DrizzleAdapterConfig {
36
- /**
37
- * The schema object that defines the tables and fields
38
- */
39
- schema?: Record<string, any> | undefined;
40
- /**
41
- * The database provider
42
- */
43
- provider: "pg" | "mysql" | "sqlite";
44
- /**
45
- * If the table names in the schema are plural
46
- * set this to true. For example, if the schema
47
- * has an object with a key "users" instead of "user"
48
- */
49
- usePlural?: boolean | undefined;
50
- /**
51
- * Enable debug logs for the adapter
52
- *
53
- * @default false
54
- */
55
- debugLogs?: DBAdapterDebugLogOption | undefined;
56
- /**
57
- * By default snake case is used for table and field names
58
- * when the CLI is used to generate the schema. If you want
59
- * to use camel case, set this to true.
60
- * @default false
61
- */
62
- camelCase?: boolean | undefined;
63
- /**
64
- * Whether to execute multiple operations in a transaction.
65
- *
66
- * If the database doesn't support transactions,
67
- * set this to `false` and operations will be executed sequentially.
68
- * @default false
69
- */
70
- transaction?: boolean | undefined;
71
- }
72
-
73
- export const drizzleAdapter = (db: DB, config: DrizzleAdapterConfig) => {
74
- let lazyOptions: BetterAuthOptions | null = null;
75
- const createCustomAdapter =
76
- (db: DB): AdapterFactoryCustomizeAdapterCreator =>
77
- ({ getFieldName, options }) => {
78
- function getSchema(model: string) {
79
- const schema = config.schema || db._.fullSchema;
80
- if (!schema) {
81
- throw new BetterAuthError(
82
- "Drizzle adapter failed to initialize. Schema not found. Please provide a schema object in the adapter options object.",
83
- );
84
- }
85
- const schemaModel = schema[model];
86
- if (!schemaModel) {
87
- throw new BetterAuthError(
88
- `[# Drizzle Adapter]: The model "${model}" was not found in the schema object. Please pass the schema directly to the adapter options.`,
89
- );
90
- }
91
- return schemaModel;
92
- }
93
- const withReturning = async (
94
- model: string,
95
- builder: any,
96
- data: Record<string, any>,
97
- where?: Where[] | undefined,
98
- ) => {
99
- if (config.provider !== "mysql") {
100
- const c = await builder.returning();
101
- return c[0];
102
- }
103
- await builder.execute();
104
- const schemaModel = getSchema(model);
105
- const builderVal = builder.config?.values;
106
- if (where?.length) {
107
- // If we're updating a field that's in the where clause, use the new value
108
- const updatedWhere = where.map((w) => {
109
- // If this field was updated, use the new value for lookup
110
- if (data[w.field] !== undefined) {
111
- return { ...w, value: data[w.field] };
112
- }
113
- return w;
114
- });
115
-
116
- const clause = convertWhereClause(updatedWhere, model);
117
- const res = await db
118
- .select()
119
- .from(schemaModel)
120
- .where(...clause);
121
- return res[0];
122
- } else if (builderVal && builderVal[0]?.id?.value) {
123
- let tId = builderVal[0]?.id?.value;
124
- if (!tId) {
125
- //get last inserted id
126
- const lastInsertId = await db
127
- .select({ id: sql`LAST_INSERT_ID()` })
128
- .from(schemaModel)
129
- .orderBy(desc(schemaModel.id))
130
- .limit(1);
131
- tId = lastInsertId[0].id;
132
- }
133
- const res = await db
134
- .select()
135
- .from(schemaModel)
136
- .where(eq(schemaModel.id, tId))
137
- .limit(1)
138
- .execute();
139
- return res[0];
140
- } else if (data.id) {
141
- const res = await db
142
- .select()
143
- .from(schemaModel)
144
- .where(eq(schemaModel.id, data.id))
145
- .limit(1)
146
- .execute();
147
- return res[0];
148
- } else {
149
- // If the user doesn't have `id` as a field, then this will fail.
150
- // We expect that they defined `id` in all of their models.
151
- if (!("id" in schemaModel)) {
152
- throw new BetterAuthError(
153
- `The model "${model}" does not have an "id" field. Please use the "id" field as your primary key.`,
154
- );
155
- }
156
- const res = await db
157
- .select()
158
- .from(schemaModel)
159
- .orderBy(desc(schemaModel.id))
160
- .limit(1)
161
- .execute();
162
- return res[0];
163
- }
164
- };
165
- function convertWhereClause(where: Where[], model: string) {
166
- const schemaModel = getSchema(model);
167
- if (!where) return [];
168
- if (where.length === 1) {
169
- const w = where[0];
170
- if (!w) {
171
- return [];
172
- }
173
- const field = getFieldName({ model, field: w.field });
174
- if (!schemaModel[field]) {
175
- throw new BetterAuthError(
176
- `The field "${w.field}" does not exist in the schema for the model "${model}". Please update your schema.`,
177
- );
178
- }
179
- if (w.operator === "in") {
180
- if (!Array.isArray(w.value)) {
181
- throw new BetterAuthError(
182
- `The value for the field "${w.field}" must be an array when using the "in" operator.`,
183
- );
184
- }
185
- return [inArray(schemaModel[field], w.value)];
186
- }
187
-
188
- if (w.operator === "not_in") {
189
- if (!Array.isArray(w.value)) {
190
- throw new BetterAuthError(
191
- `The value for the field "${w.field}" must be an array when using the "not_in" operator.`,
192
- );
193
- }
194
- return [notInArray(schemaModel[field], w.value)];
195
- }
196
-
197
- if (w.operator === "contains") {
198
- return [like(schemaModel[field], `%${w.value}%`)];
199
- }
200
-
201
- if (w.operator === "starts_with") {
202
- return [like(schemaModel[field], `${w.value}%`)];
203
- }
204
-
205
- if (w.operator === "ends_with") {
206
- return [like(schemaModel[field], `%${w.value}`)];
207
- }
208
-
209
- if (w.operator === "lt") {
210
- return [lt(schemaModel[field], w.value)];
211
- }
212
-
213
- if (w.operator === "lte") {
214
- return [lte(schemaModel[field], w.value)];
215
- }
216
-
217
- if (w.operator === "ne") {
218
- return [ne(schemaModel[field], w.value)];
219
- }
220
-
221
- if (w.operator === "gt") {
222
- return [gt(schemaModel[field], w.value)];
223
- }
224
-
225
- if (w.operator === "gte") {
226
- return [gte(schemaModel[field], w.value)];
227
- }
228
-
229
- return [eq(schemaModel[field], w.value)];
230
- }
231
- const andGroup = where.filter(
232
- (w) => w.connector === "AND" || !w.connector,
233
- );
234
- const orGroup = where.filter((w) => w.connector === "OR");
235
-
236
- const andClause = and(
237
- ...andGroup.map((w) => {
238
- const field = getFieldName({ model, field: w.field });
239
- if (w.operator === "in") {
240
- if (!Array.isArray(w.value)) {
241
- throw new BetterAuthError(
242
- `The value for the field "${w.field}" must be an array when using the "in" operator.`,
243
- );
244
- }
245
- return inArray(schemaModel[field], w.value);
246
- }
247
- if (w.operator === "not_in") {
248
- if (!Array.isArray(w.value)) {
249
- throw new BetterAuthError(
250
- `The value for the field "${w.field}" must be an array when using the "not_in" operator.`,
251
- );
252
- }
253
- return notInArray(schemaModel[field], w.value);
254
- }
255
- if (w.operator === "contains") {
256
- return like(schemaModel[field], `%${w.value}%`);
257
- }
258
- if (w.operator === "starts_with") {
259
- return like(schemaModel[field], `${w.value}%`);
260
- }
261
- if (w.operator === "ends_with") {
262
- return like(schemaModel[field], `%${w.value}`);
263
- }
264
- if (w.operator === "lt") {
265
- return lt(schemaModel[field], w.value);
266
- }
267
- if (w.operator === "lte") {
268
- return lte(schemaModel[field], w.value);
269
- }
270
- if (w.operator === "gt") {
271
- return gt(schemaModel[field], w.value);
272
- }
273
- if (w.operator === "gte") {
274
- return gte(schemaModel[field], w.value);
275
- }
276
- if (w.operator === "ne") {
277
- return ne(schemaModel[field], w.value);
278
- }
279
- return eq(schemaModel[field], w.value);
280
- }),
281
- );
282
- const orClause = or(
283
- ...orGroup.map((w) => {
284
- const field = getFieldName({ model, field: w.field });
285
- if (w.operator === "in") {
286
- if (!Array.isArray(w.value)) {
287
- throw new BetterAuthError(
288
- `The value for the field "${w.field}" must be an array when using the "in" operator.`,
289
- );
290
- }
291
- return inArray(schemaModel[field], w.value);
292
- }
293
- if (w.operator === "not_in") {
294
- if (!Array.isArray(w.value)) {
295
- throw new BetterAuthError(
296
- `The value for the field "${w.field}" must be an array when using the "not_in" operator.`,
297
- );
298
- }
299
- return notInArray(schemaModel[field], w.value);
300
- }
301
- if (w.operator === "contains") {
302
- return like(schemaModel[field], `%${w.value}%`);
303
- }
304
- if (w.operator === "starts_with") {
305
- return like(schemaModel[field], `${w.value}%`);
306
- }
307
- if (w.operator === "ends_with") {
308
- return like(schemaModel[field], `%${w.value}`);
309
- }
310
- if (w.operator === "lt") {
311
- return lt(schemaModel[field], w.value);
312
- }
313
- if (w.operator === "lte") {
314
- return lte(schemaModel[field], w.value);
315
- }
316
- if (w.operator === "gt") {
317
- return gt(schemaModel[field], w.value);
318
- }
319
- if (w.operator === "gte") {
320
- return gte(schemaModel[field], w.value);
321
- }
322
- if (w.operator === "ne") {
323
- return ne(schemaModel[field], w.value);
324
- }
325
- return eq(schemaModel[field], w.value);
326
- }),
327
- );
328
-
329
- const clause: SQL<unknown>[] = [];
330
-
331
- if (andGroup.length) clause.push(andClause!);
332
- if (orGroup.length) clause.push(orClause!);
333
- return clause;
334
- }
335
- function checkMissingFields(
336
- schema: Record<string, any>,
337
- model: string,
338
- values: Record<string, any>,
339
- ) {
340
- if (!schema) {
341
- throw new BetterAuthError(
342
- "Drizzle adapter failed to initialize. Drizzle Schema not found. Please provide a schema object in the adapter options object.",
343
- );
344
- }
345
- for (const key in values) {
346
- if (!schema[key]) {
347
- throw new BetterAuthError(
348
- `The field "${key}" does not exist in the "${model}" Drizzle schema. Please update your drizzle schema or re-generate using "npx @better-auth/cli@latest generate".`,
349
- );
350
- }
351
- }
352
- }
353
-
354
- return {
355
- async create({ model, data: values }) {
356
- const schemaModel = getSchema(model);
357
- checkMissingFields(schemaModel, model, values);
358
- const builder = db.insert(schemaModel).values(values);
359
- const returned = await withReturning(model, builder, values);
360
- return returned;
361
- },
362
- async findOne({ model, where, join }) {
363
- const schemaModel = getSchema(model);
364
- const clause = convertWhereClause(where, model);
365
-
366
- if (options.experimental?.joins) {
367
- if (!db.query || !db.query[model]) {
368
- logger.error(
369
- `[# Drizzle Adapter]: The model "${model}" was not found in the query object. Please update your Drizzle schema to include relations or re-generate using "npx @better-auth/cli@latest generate".`,
370
- );
371
- logger.info("Falling back to regular query");
372
- } else {
373
- let includes:
374
- | Record<string, { limit: number } | boolean>
375
- | undefined;
376
-
377
- const pluralJoinResults: string[] = [];
378
- if (join) {
379
- includes = {};
380
- const joinEntries = Object.entries(join);
381
- for (const [model, joinAttr] of joinEntries) {
382
- const limit =
383
- joinAttr.limit ??
384
- options.advanced?.database?.defaultFindManyLimit ??
385
- 100;
386
- const isUnique = joinAttr.relation === "one-to-one";
387
- const pluralSuffix = isUnique || config.usePlural ? "" : "s";
388
- includes[`${model}${pluralSuffix}`] = isUnique
389
- ? true
390
- : { limit };
391
- if (!isUnique) {
392
- pluralJoinResults.push(`${model}${pluralSuffix}`);
393
- }
394
- }
395
- }
396
- const query = db.query[model].findFirst({
397
- where: clause[0],
398
- with: includes,
399
- });
400
- const res = await query;
401
-
402
- if (res) {
403
- for (const pluralJoinResult of pluralJoinResults) {
404
- const singularKey = !config.usePlural
405
- ? pluralJoinResult.slice(0, -1)
406
- : pluralJoinResult;
407
- res[singularKey] = res[pluralJoinResult];
408
- if (pluralJoinResult !== singularKey) {
409
- delete res[pluralJoinResult];
410
- }
411
- }
412
- }
413
- return res;
414
- }
415
- }
416
-
417
- const query = db
418
- .select()
419
- .from(schemaModel)
420
- .where(...clause);
421
-
422
- const res = await query;
423
-
424
- if (!res.length) return null;
425
- return res[0];
426
- },
427
- async findMany({ model, where, sortBy, limit, offset, join }) {
428
- const schemaModel = getSchema(model);
429
- const clause = where ? convertWhereClause(where, model) : [];
430
- const sortFn = sortBy?.direction === "desc" ? desc : asc;
431
-
432
- if (options.experimental?.joins) {
433
- if (!db.query[model]) {
434
- logger.error(
435
- `[# Drizzle Adapter]: The model "${model}" was not found in the query object. Please update your Drizzle schema to include relations or re-generate using "npx @better-auth/cli@latest generate".`,
436
- );
437
- logger.info("Falling back to regular query");
438
- } else {
439
- let includes:
440
- | Record<string, { limit: number } | boolean>
441
- | undefined;
442
-
443
- const pluralJoinResults: string[] = [];
444
- if (join) {
445
- includes = {};
446
- const joinEntries = Object.entries(join);
447
- for (const [model, joinAttr] of joinEntries) {
448
- const isUnique = joinAttr.relation === "one-to-one";
449
- const limit =
450
- joinAttr.limit ??
451
- options.advanced?.database?.defaultFindManyLimit ??
452
- 100;
453
- const pluralSuffix = isUnique || config.usePlural ? "" : "s";
454
- includes[`${model}${pluralSuffix}`] = isUnique
455
- ? true
456
- : { limit };
457
- if (!isUnique)
458
- pluralJoinResults.push(`${model}${pluralSuffix}`);
459
- }
460
- }
461
- let orderBy: SQL<unknown>[] | undefined = undefined;
462
- if (sortBy?.field) {
463
- orderBy = [
464
- sortFn(
465
- schemaModel[getFieldName({ model, field: sortBy?.field })],
466
- ),
467
- ];
468
- }
469
- const query = db.query[model].findMany({
470
- where: clause[0],
471
- with: includes,
472
- limit: limit ?? 100,
473
- offset: offset ?? 0,
474
- orderBy,
475
- });
476
- const res = await query;
477
- if (res) {
478
- for (const item of res) {
479
- for (const pluralJoinResult of pluralJoinResults) {
480
- const singularKey = !config.usePlural
481
- ? pluralJoinResult.slice(0, -1)
482
- : pluralJoinResult;
483
- if (singularKey === pluralJoinResult) continue;
484
- item[singularKey] = item[pluralJoinResult];
485
- delete item[pluralJoinResult];
486
- }
487
- }
488
- }
489
- return res;
490
- }
491
- }
492
-
493
- let builder = db.select().from(schemaModel);
494
-
495
- const effectiveLimit = limit;
496
- const effectiveOffset = offset;
497
-
498
- if (typeof effectiveLimit !== "undefined") {
499
- builder = builder.limit(effectiveLimit);
500
- }
501
-
502
- if (typeof effectiveOffset !== "undefined") {
503
- builder = builder.offset(effectiveOffset);
504
- }
505
-
506
- if (sortBy?.field) {
507
- builder = builder.orderBy(
508
- sortFn(
509
- schemaModel[getFieldName({ model, field: sortBy?.field })],
510
- ),
511
- );
512
- }
513
-
514
- const res = await builder.where(...clause);
515
- return res;
516
- },
517
- async count({ model, where }) {
518
- const schemaModel = getSchema(model);
519
- const clause = where ? convertWhereClause(where, model) : [];
520
- const res = await db
521
- .select({ count: count() })
522
- .from(schemaModel)
523
- .where(...clause);
524
- return res[0].count;
525
- },
526
- async update({ model, where, update: values }) {
527
- const schemaModel = getSchema(model);
528
- const clause = convertWhereClause(where, model);
529
- const builder = db
530
- .update(schemaModel)
531
- .set(values)
532
- .where(...clause);
533
- return await withReturning(model, builder, values as any, where);
534
- },
535
- async updateMany({ model, where, update: values }) {
536
- const schemaModel = getSchema(model);
537
- const clause = convertWhereClause(where, model);
538
- const builder = db
539
- .update(schemaModel)
540
- .set(values)
541
- .where(...clause);
542
- return await builder;
543
- },
544
- async delete({ model, where }) {
545
- const schemaModel = getSchema(model);
546
- const clause = convertWhereClause(where, model);
547
- const builder = db.delete(schemaModel).where(...clause);
548
- return await builder;
549
- },
550
- async deleteMany({ model, where }) {
551
- const schemaModel = getSchema(model);
552
- const clause = convertWhereClause(where, model);
553
- const builder = db.delete(schemaModel).where(...clause);
554
- const res = await builder;
555
- let count = 0;
556
- if (res && "rowCount" in res) count = res.rowCount;
557
- else if (Array.isArray(res)) count = res.length;
558
- else if (
559
- res &&
560
- ("affectedRows" in res || "rowsAffected" in res || "changes" in res)
561
- )
562
- count = res.affectedRows ?? res.rowsAffected ?? res.changes;
563
- if (typeof count !== "number") {
564
- logger.error(
565
- "[Drizzle Adapter] The result of the deleteMany operation is not a number. This is likely a bug in the adapter. Please report this issue to the Better Auth team.",
566
- { res, model, where },
567
- );
568
- }
569
- return count;
570
- },
571
- options: config,
572
- };
573
- };
574
- let adapterOptions: AdapterFactoryOptions | null = null;
575
- adapterOptions = {
576
- config: {
577
- adapterId: "drizzle",
578
- adapterName: "Drizzle Adapter",
579
- usePlural: config.usePlural ?? false,
580
- debugLogs: config.debugLogs ?? false,
581
- supportsUUIDs: config.provider === "pg" ? true : false,
582
- supportsJSON:
583
- config.provider === "pg" // even though mysql also supports it, mysql requires to pass stringified json anyway.
584
- ? true
585
- : false,
586
- supportsArrays: config.provider === "pg" ? true : false,
587
- transaction:
588
- (config.transaction ?? false)
589
- ? (cb) =>
590
- db.transaction((tx: DB) => {
591
- const adapter = createAdapterFactory({
592
- config: adapterOptions!.config,
593
- adapter: createCustomAdapter(tx),
594
- })(lazyOptions!);
595
- return cb(adapter);
596
- })
597
- : false,
598
- },
599
- adapter: createCustomAdapter(db),
600
- };
601
- const adapter = createAdapterFactory(adapterOptions);
602
- return (options: BetterAuthOptions): DBAdapter<BetterAuthOptions> => {
603
- lazyOptions = options;
604
- return adapter(options);
605
- };
606
- };
package/src/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from "./drizzle-adapter";
package/tsconfig.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "include": ["./src"],
4
- "references": [
5
- {
6
- "path": "../core/tsconfig.json"
7
- }
8
- ]
9
- }
package/tsdown.config.ts DELETED
@@ -1,7 +0,0 @@
1
- import { defineConfig } from "tsdown";
2
-
3
- export default defineConfig({
4
- dts: { build: true, incremental: true },
5
- format: ["esm"],
6
- entry: ["./src/index.ts"],
7
- });
package/vitest.config.ts DELETED
@@ -1,3 +0,0 @@
1
- import { defineProject } from "vitest/config";
2
-
3
- export default defineProject({});