@better-auth/drizzle-adapter 1.5.0-beta.18 → 1.5.0-beta.19
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 +17 -0
- package/package.json +22 -5
- package/.turbo/turbo-build.log +0 -16
- package/src/drizzle-adapter.test.ts +0 -17
- package/src/drizzle-adapter.ts +0 -659
- package/src/index.ts +0 -1
- package/tsconfig.json +0 -9
- package/tsdown.config.ts +0 -8
- package/vitest.config.ts +0 -8
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/package.json
CHANGED
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/drizzle-adapter",
|
|
3
|
-
"version": "1.5.0-beta.
|
|
3
|
+
"version": "1.5.0-beta.19",
|
|
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",
|
|
@@ -21,19 +36,21 @@
|
|
|
21
36
|
"peerDependencies": {
|
|
22
37
|
"@better-auth/utils": "^0.3.0",
|
|
23
38
|
"drizzle-orm": ">=0.41.0",
|
|
24
|
-
"@better-auth/core": "1.5.0-beta.
|
|
39
|
+
"@better-auth/core": "1.5.0-beta.19"
|
|
25
40
|
},
|
|
26
41
|
"devDependencies": {
|
|
27
42
|
"@better-auth/utils": "^0.3.1",
|
|
28
43
|
"drizzle-orm": ">=0.41.0",
|
|
29
44
|
"tsdown": "^0.20.3",
|
|
30
45
|
"typescript": "^5.9.3",
|
|
31
|
-
"@better-auth/core": "1.5.0-beta.
|
|
46
|
+
"@better-auth/core": "1.5.0-beta.19"
|
|
32
47
|
},
|
|
33
48
|
"scripts": {
|
|
34
49
|
"build": "tsdown",
|
|
35
50
|
"dev": "tsdown --watch",
|
|
36
|
-
"
|
|
37
|
-
"
|
|
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
|
}
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @better-auth/drizzle-adapter@1.5.0-beta.18 build /home/runner/work/better-auth/better-auth/packages/drizzle-adapter
|
|
3
|
-
> tsdown
|
|
4
|
-
|
|
5
|
-
[34mℹ[39m tsdown [2mv0.20.3[22m powered by rolldown [2mv1.0.0-rc.3[22m
|
|
6
|
-
[34mℹ[39m config file: [4m/home/runner/work/better-auth/better-auth/packages/drizzle-adapter/tsdown.config.ts[24m
|
|
7
|
-
[34mℹ[39m entry: [34msrc/index.ts[39m
|
|
8
|
-
[34mℹ[39m tsconfig: [34mtsconfig.json[39m
|
|
9
|
-
[34mℹ[39m Build start
|
|
10
|
-
[33m[PLUGIN_TIMINGS] Warning:[0m Your build spent significant time in plugin `rolldown-plugin-dts:generate`. See https://rolldown.rs/options/checks#plugintimings for more details.
|
|
11
|
-
|
|
12
|
-
[34mℹ[39m [2mdist/[22m[1mindex.mjs[22m [2m14.91 kB[22m [2m│ gzip: 3.08 kB[22m
|
|
13
|
-
[34mℹ[39m [2mdist/[22mindex.mjs.map [2m33.91 kB[22m [2m│ gzip: 6.67 kB[22m
|
|
14
|
-
[34mℹ[39m [2mdist/[22m[32m[1mindex.d.mts[22m[39m [2m 1.50 kB[22m [2m│ gzip: 0.67 kB[22m
|
|
15
|
-
[34mℹ[39m 3 files, total: 50.31 kB
|
|
16
|
-
[32m✔[39m Build complete in [32m10310ms[39m
|
|
@@ -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
|
-
});
|
package/src/drizzle-adapter.ts
DELETED
|
@@ -1,659 +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, getDefaultFieldName, 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 auth@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, select, 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 auth@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
|
-
columns:
|
|
399
|
-
select?.length && select.length > 0
|
|
400
|
-
? select.reduce(
|
|
401
|
-
(acc, field) => {
|
|
402
|
-
acc[getFieldName({ model, field })] = true;
|
|
403
|
-
return acc;
|
|
404
|
-
},
|
|
405
|
-
{} as Record<string, boolean>,
|
|
406
|
-
)
|
|
407
|
-
: undefined,
|
|
408
|
-
with: includes,
|
|
409
|
-
});
|
|
410
|
-
const res = await query;
|
|
411
|
-
|
|
412
|
-
if (res) {
|
|
413
|
-
for (const pluralJoinResult of pluralJoinResults) {
|
|
414
|
-
const singularKey = !config.usePlural
|
|
415
|
-
? pluralJoinResult.slice(0, -1)
|
|
416
|
-
: pluralJoinResult;
|
|
417
|
-
res[singularKey] = res[pluralJoinResult];
|
|
418
|
-
if (pluralJoinResult !== singularKey) {
|
|
419
|
-
delete res[pluralJoinResult];
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
return res;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const query = db
|
|
428
|
-
.select(
|
|
429
|
-
select?.length && select.length > 0
|
|
430
|
-
? select.reduce((acc, field) => {
|
|
431
|
-
const fieldName = getFieldName({ model, field });
|
|
432
|
-
return {
|
|
433
|
-
...acc,
|
|
434
|
-
[fieldName]: schemaModel[fieldName],
|
|
435
|
-
};
|
|
436
|
-
}, {})
|
|
437
|
-
: undefined,
|
|
438
|
-
)
|
|
439
|
-
.from(schemaModel)
|
|
440
|
-
.where(...clause);
|
|
441
|
-
|
|
442
|
-
const res = await query;
|
|
443
|
-
|
|
444
|
-
if (!res.length) return null;
|
|
445
|
-
return res[0];
|
|
446
|
-
},
|
|
447
|
-
async findMany({ model, where, sortBy, limit, select, offset, join }) {
|
|
448
|
-
const schemaModel = getSchema(model);
|
|
449
|
-
const clause = where ? convertWhereClause(where, model) : [];
|
|
450
|
-
const sortFn = sortBy?.direction === "desc" ? desc : asc;
|
|
451
|
-
|
|
452
|
-
if (options.experimental?.joins) {
|
|
453
|
-
if (!db.query[model]) {
|
|
454
|
-
logger.error(
|
|
455
|
-
`[# 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".`,
|
|
456
|
-
);
|
|
457
|
-
logger.info("Falling back to regular query");
|
|
458
|
-
} else {
|
|
459
|
-
let includes:
|
|
460
|
-
| Record<string, { limit: number } | boolean>
|
|
461
|
-
| undefined;
|
|
462
|
-
|
|
463
|
-
const pluralJoinResults: string[] = [];
|
|
464
|
-
if (join) {
|
|
465
|
-
includes = {};
|
|
466
|
-
const joinEntries = Object.entries(join);
|
|
467
|
-
for (const [model, joinAttr] of joinEntries) {
|
|
468
|
-
const isUnique = joinAttr.relation === "one-to-one";
|
|
469
|
-
const limit =
|
|
470
|
-
joinAttr.limit ??
|
|
471
|
-
options.advanced?.database?.defaultFindManyLimit ??
|
|
472
|
-
100;
|
|
473
|
-
const pluralSuffix = isUnique || config.usePlural ? "" : "s";
|
|
474
|
-
includes[`${model}${pluralSuffix}`] = isUnique
|
|
475
|
-
? true
|
|
476
|
-
: { limit };
|
|
477
|
-
if (!isUnique)
|
|
478
|
-
pluralJoinResults.push(`${model}${pluralSuffix}`);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
let orderBy: SQL<unknown>[] | undefined = undefined;
|
|
482
|
-
if (sortBy?.field) {
|
|
483
|
-
orderBy = [
|
|
484
|
-
sortFn(
|
|
485
|
-
schemaModel[getFieldName({ model, field: sortBy?.field })],
|
|
486
|
-
),
|
|
487
|
-
];
|
|
488
|
-
}
|
|
489
|
-
const query = db.query[model].findMany({
|
|
490
|
-
where: clause[0],
|
|
491
|
-
with: includes,
|
|
492
|
-
columns:
|
|
493
|
-
select?.length && select.length > 0
|
|
494
|
-
? select.reduce(
|
|
495
|
-
(acc, field) => {
|
|
496
|
-
acc[getFieldName({ model, field })] = true;
|
|
497
|
-
return acc;
|
|
498
|
-
},
|
|
499
|
-
{} as Record<string, boolean>,
|
|
500
|
-
)
|
|
501
|
-
: undefined,
|
|
502
|
-
limit: limit ?? 100,
|
|
503
|
-
offset: offset ?? 0,
|
|
504
|
-
orderBy,
|
|
505
|
-
});
|
|
506
|
-
const res = await query;
|
|
507
|
-
if (res) {
|
|
508
|
-
for (const item of res) {
|
|
509
|
-
for (const pluralJoinResult of pluralJoinResults) {
|
|
510
|
-
const singularKey = !config.usePlural
|
|
511
|
-
? pluralJoinResult.slice(0, -1)
|
|
512
|
-
: pluralJoinResult;
|
|
513
|
-
if (singularKey === pluralJoinResult) continue;
|
|
514
|
-
item[singularKey] = item[pluralJoinResult];
|
|
515
|
-
delete item[pluralJoinResult];
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
return res;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
let builder = db
|
|
524
|
-
.select(
|
|
525
|
-
select?.length && select.length > 0
|
|
526
|
-
? select.reduce((acc, field) => {
|
|
527
|
-
const fieldName = getFieldName({ model, field });
|
|
528
|
-
return {
|
|
529
|
-
...acc,
|
|
530
|
-
[fieldName]: schemaModel[fieldName],
|
|
531
|
-
};
|
|
532
|
-
}, {})
|
|
533
|
-
: undefined,
|
|
534
|
-
)
|
|
535
|
-
.from(schemaModel);
|
|
536
|
-
|
|
537
|
-
const effectiveLimit = limit;
|
|
538
|
-
const effectiveOffset = offset;
|
|
539
|
-
|
|
540
|
-
if (typeof effectiveLimit !== "undefined") {
|
|
541
|
-
builder = builder.limit(effectiveLimit);
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
if (typeof effectiveOffset !== "undefined") {
|
|
545
|
-
builder = builder.offset(effectiveOffset);
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
if (sortBy?.field) {
|
|
549
|
-
builder = builder.orderBy(
|
|
550
|
-
sortFn(
|
|
551
|
-
schemaModel[getFieldName({ model, field: sortBy?.field })],
|
|
552
|
-
),
|
|
553
|
-
);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
const res = await builder.where(...clause);
|
|
557
|
-
return res;
|
|
558
|
-
},
|
|
559
|
-
async count({ model, where }) {
|
|
560
|
-
const schemaModel = getSchema(model);
|
|
561
|
-
const clause = where ? convertWhereClause(where, model) : [];
|
|
562
|
-
const res = await db
|
|
563
|
-
.select({ count: count() })
|
|
564
|
-
.from(schemaModel)
|
|
565
|
-
.where(...clause);
|
|
566
|
-
return res[0].count;
|
|
567
|
-
},
|
|
568
|
-
async update({ model, where, update: values }) {
|
|
569
|
-
const schemaModel = getSchema(model);
|
|
570
|
-
const clause = convertWhereClause(where, model);
|
|
571
|
-
const builder = db
|
|
572
|
-
.update(schemaModel)
|
|
573
|
-
.set(values)
|
|
574
|
-
.where(...clause);
|
|
575
|
-
return await withReturning(model, builder, values as any, where);
|
|
576
|
-
},
|
|
577
|
-
async updateMany({ model, where, update: values }) {
|
|
578
|
-
const schemaModel = getSchema(model);
|
|
579
|
-
const clause = convertWhereClause(where, model);
|
|
580
|
-
const builder = db
|
|
581
|
-
.update(schemaModel)
|
|
582
|
-
.set(values)
|
|
583
|
-
.where(...clause);
|
|
584
|
-
return await builder;
|
|
585
|
-
},
|
|
586
|
-
async delete({ model, where }) {
|
|
587
|
-
const schemaModel = getSchema(model);
|
|
588
|
-
const clause = convertWhereClause(where, model);
|
|
589
|
-
const builder = db.delete(schemaModel).where(...clause);
|
|
590
|
-
return await builder;
|
|
591
|
-
},
|
|
592
|
-
async deleteMany({ model, where }) {
|
|
593
|
-
const schemaModel = getSchema(model);
|
|
594
|
-
const clause = convertWhereClause(where, model);
|
|
595
|
-
const builder = db.delete(schemaModel).where(...clause);
|
|
596
|
-
const res = await builder;
|
|
597
|
-
let count = 0;
|
|
598
|
-
if (res && "rowCount" in res) count = res.rowCount;
|
|
599
|
-
else if (Array.isArray(res)) count = res.length;
|
|
600
|
-
else if (
|
|
601
|
-
res &&
|
|
602
|
-
("affectedRows" in res || "rowsAffected" in res || "changes" in res)
|
|
603
|
-
)
|
|
604
|
-
count = res.affectedRows ?? res.rowsAffected ?? res.changes;
|
|
605
|
-
if (typeof count !== "number") {
|
|
606
|
-
logger.error(
|
|
607
|
-
"[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.",
|
|
608
|
-
{ res, model, where },
|
|
609
|
-
);
|
|
610
|
-
}
|
|
611
|
-
return count;
|
|
612
|
-
},
|
|
613
|
-
options: config,
|
|
614
|
-
};
|
|
615
|
-
};
|
|
616
|
-
let adapterOptions: AdapterFactoryOptions | null = null;
|
|
617
|
-
adapterOptions = {
|
|
618
|
-
config: {
|
|
619
|
-
adapterId: "drizzle",
|
|
620
|
-
adapterName: "Drizzle Adapter",
|
|
621
|
-
usePlural: config.usePlural ?? false,
|
|
622
|
-
debugLogs: config.debugLogs ?? false,
|
|
623
|
-
supportsUUIDs: config.provider === "pg" ? true : false,
|
|
624
|
-
supportsJSON:
|
|
625
|
-
config.provider === "pg" // even though mysql also supports it, mysql requires to pass stringified json anyway.
|
|
626
|
-
? true
|
|
627
|
-
: false,
|
|
628
|
-
supportsArrays: config.provider === "pg" ? true : false,
|
|
629
|
-
customTransformOutput: ({ data, fieldAttributes }) => {
|
|
630
|
-
// not all providers support dates
|
|
631
|
-
// one such example case is https://github.com/better-auth/better-auth/issues/7819
|
|
632
|
-
if (fieldAttributes.type === "date") {
|
|
633
|
-
if (data === null || data === undefined) {
|
|
634
|
-
return data;
|
|
635
|
-
}
|
|
636
|
-
return new Date(data);
|
|
637
|
-
}
|
|
638
|
-
return data;
|
|
639
|
-
},
|
|
640
|
-
transaction:
|
|
641
|
-
(config.transaction ?? false)
|
|
642
|
-
? (cb) =>
|
|
643
|
-
db.transaction((tx: DB) => {
|
|
644
|
-
const adapter = createAdapterFactory({
|
|
645
|
-
config: adapterOptions!.config,
|
|
646
|
-
adapter: createCustomAdapter(tx),
|
|
647
|
-
})(lazyOptions!);
|
|
648
|
-
return cb(adapter);
|
|
649
|
-
})
|
|
650
|
-
: false,
|
|
651
|
-
},
|
|
652
|
-
adapter: createCustomAdapter(db),
|
|
653
|
-
};
|
|
654
|
-
const adapter = createAdapterFactory(adapterOptions);
|
|
655
|
-
return (options: BetterAuthOptions): DBAdapter<BetterAuthOptions> => {
|
|
656
|
-
lazyOptions = options;
|
|
657
|
-
return adapter(options);
|
|
658
|
-
};
|
|
659
|
-
};
|
package/src/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./drizzle-adapter";
|
package/tsconfig.json
DELETED
package/tsdown.config.ts
DELETED