@auth/drizzle-adapter 0.9.0 → 1.0.1

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/src/lib/sqlite.ts CHANGED
@@ -1,211 +1,421 @@
1
- import { eq, and } from "drizzle-orm"
1
+ import { and, eq, getTableColumns } from "drizzle-orm"
2
2
  import {
3
+ BaseSQLiteDatabase,
4
+ SQLiteColumn,
5
+ SQLiteTableWithColumns,
3
6
  integer,
4
- sqliteTable as defaultSqliteTableFn,
5
- text,
6
7
  primaryKey,
7
- BaseSQLiteDatabase,
8
- SQLiteTableFn,
8
+ sqliteTable,
9
+ text,
9
10
  } from "drizzle-orm/sqlite-core"
10
- import { stripUndefined } from "./utils.js"
11
11
 
12
- import type { Adapter, AdapterAccount } from "@auth/core/adapters"
12
+ import type {
13
+ Adapter,
14
+ AdapterAccount,
15
+ AdapterSession,
16
+ AdapterUser,
17
+ VerificationToken,
18
+ } from "@auth/core/adapters"
13
19
 
14
- export function createTables(sqliteTable: SQLiteTableFn) {
15
- const users = sqliteTable("user", {
16
- id: text("id").notNull().primaryKey(),
17
- name: text("name"),
18
- email: text("email").notNull(),
19
- emailVerified: integer("emailVerified", { mode: "timestamp_ms" }),
20
- image: text("image"),
21
- })
22
-
23
- const accounts = sqliteTable(
24
- "account",
25
- {
26
- userId: text("userId")
27
- .notNull()
28
- .references(() => users.id, { onDelete: "cascade" }),
29
- type: text("type").$type<AdapterAccount["type"]>().notNull(),
30
- provider: text("provider").notNull(),
31
- providerAccountId: text("providerAccountId").notNull(),
32
- refresh_token: text("refresh_token"),
33
- access_token: text("access_token"),
34
- expires_at: integer("expires_at"),
35
- token_type: text("token_type"),
36
- scope: text("scope"),
37
- id_token: text("id_token"),
38
- session_state: text("session_state"),
39
- },
40
- (account) => ({
41
- compoundKey: primaryKey(account.provider, account.providerAccountId),
42
- })
43
- )
20
+ export const sqliteUsersTable = sqliteTable("user", {
21
+ id: text("id")
22
+ .primaryKey()
23
+ .$defaultFn(() => crypto.randomUUID()),
24
+ name: text("name"),
25
+ email: text("email").notNull(),
26
+ emailVerified: integer("emailVerified", { mode: "timestamp_ms" }),
27
+ image: text("image"),
28
+ }) satisfies DefaultSQLiteUsersTable
44
29
 
45
- const sessions = sqliteTable("session", {
46
- sessionToken: text("sessionToken").notNull().primaryKey(),
30
+ export const sqliteAccountsTable = sqliteTable(
31
+ "account",
32
+ {
47
33
  userId: text("userId")
48
34
  .notNull()
49
- .references(() => users.id, { onDelete: "cascade" }),
50
- expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
35
+ .references(() => sqliteUsersTable.id, { onDelete: "cascade" }),
36
+ type: text("type").$type<AdapterAccount["type"]>().notNull(),
37
+ provider: text("provider").notNull(),
38
+ providerAccountId: text("providerAccountId").notNull(),
39
+ refresh_token: text("refresh_token"),
40
+ access_token: text("access_token"),
41
+ expires_at: integer("expires_at"),
42
+ token_type: text("token_type"),
43
+ scope: text("scope"),
44
+ id_token: text("id_token"),
45
+ session_state: text("session_state"),
46
+ },
47
+ (account) => ({
48
+ compositePk: primaryKey({
49
+ columns: [account.provider, account.providerAccountId],
50
+ }),
51
51
  })
52
+ ) satisfies DefaultSQLiteAccountsTable
52
53
 
53
- const verificationTokens = sqliteTable(
54
- "verificationToken",
55
- {
56
- identifier: text("identifier").notNull(),
57
- token: text("token").notNull(),
58
- expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
59
- },
60
- (vt) => ({
61
- compoundKey: primaryKey(vt.identifier, vt.token),
62
- })
63
- )
64
-
65
- return { users, accounts, sessions, verificationTokens }
66
- }
54
+ export const sqliteSessionsTable = sqliteTable("session", {
55
+ sessionToken: text("sessionToken").primaryKey(),
56
+ userId: text("userId")
57
+ .notNull()
58
+ .references(() => sqliteUsersTable.id, { onDelete: "cascade" }),
59
+ expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
60
+ }) satisfies DefaultSQLiteSessionsTable
67
61
 
68
- export type DefaultSchema = ReturnType<typeof createTables>
62
+ export const sqliteVerificationTokensTable = sqliteTable(
63
+ "verificationToken",
64
+ {
65
+ identifier: text("identifier").notNull(),
66
+ token: text("token").notNull(),
67
+ expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
68
+ },
69
+ (vt) => ({
70
+ compositePk: primaryKey({ columns: [vt.identifier, vt.token] }),
71
+ })
72
+ ) satisfies DefaultSQLiteVerificationTokenTable
69
73
 
70
74
  export function SQLiteDrizzleAdapter(
71
- client: InstanceType<typeof BaseSQLiteDatabase>,
72
- tableFn = defaultSqliteTableFn
75
+ client: BaseSQLiteDatabase<"sync" | "async", any, any>,
76
+ schema: DefaultSQLiteSchema = {
77
+ usersTable: sqliteUsersTable,
78
+ accountsTable: sqliteAccountsTable,
79
+ sessionsTable: sqliteSessionsTable,
80
+ verificationTokensTable: sqliteVerificationTokensTable,
81
+ }
73
82
  ): Adapter {
74
- const { users, accounts, sessions, verificationTokens } =
75
- createTables(tableFn)
83
+ const { usersTable, accountsTable, sessionsTable, verificationTokensTable } =
84
+ schema
76
85
 
77
86
  return {
78
- async createUser(data) {
79
- return await client
80
- .insert(users)
81
- .values({ ...data, id: crypto.randomUUID() })
87
+ async createUser(data: AdapterUser) {
88
+ const hasDefaultId = getTableColumns(usersTable)["id"]["hasDefault"]
89
+
90
+ return client
91
+ .insert(usersTable)
92
+ .values(hasDefaultId ? data : { ...data, id: crypto.randomUUID() })
82
93
  .returning()
83
94
  .get()
84
95
  },
85
- async getUser(data) {
96
+ async getUser(userId: string) {
86
97
  const result = await client
87
98
  .select()
88
- .from(users)
89
- .where(eq(users.id, data))
99
+ .from(usersTable)
100
+ .where(eq(usersTable.id, userId))
90
101
  .get()
102
+
91
103
  return result ?? null
92
104
  },
93
- async getUserByEmail(data) {
105
+ async getUserByEmail(email: string) {
94
106
  const result = await client
95
107
  .select()
96
- .from(users)
97
- .where(eq(users.email, data))
108
+ .from(usersTable)
109
+ .where(eq(usersTable.email, email))
98
110
  .get()
111
+
99
112
  return result ?? null
100
113
  },
101
- createSession(data) {
102
- return client.insert(sessions).values(data).returning().get()
114
+ async createSession(data: {
115
+ sessionToken: string
116
+ userId: string
117
+ expires: Date
118
+ }) {
119
+ return await client.insert(sessionsTable).values(data).returning().get()
103
120
  },
104
- async getSessionAndUser(data) {
121
+ async getSessionAndUser(sessionToken: string) {
105
122
  const result = await client
106
- .select({ session: sessions, user: users })
107
- .from(sessions)
108
- .where(eq(sessions.sessionToken, data))
109
- .innerJoin(users, eq(users.id, sessions.userId))
123
+ .select({
124
+ session: sessionsTable,
125
+ user: usersTable,
126
+ })
127
+ .from(sessionsTable)
128
+ .where(eq(sessionsTable.sessionToken, sessionToken))
129
+ .innerJoin(usersTable, eq(usersTable.id, sessionsTable.userId))
110
130
  .get()
131
+
111
132
  return result ?? null
112
133
  },
113
- async updateUser(data) {
134
+ async updateUser(data: Partial<AdapterUser> & Pick<AdapterUser, "id">) {
114
135
  if (!data.id) {
115
136
  throw new Error("No user id.")
116
137
  }
117
138
 
118
139
  const result = await client
119
- .update(users)
140
+ .update(usersTable)
120
141
  .set(data)
121
- .where(eq(users.id, data.id))
142
+ .where(eq(usersTable.id, data.id))
122
143
  .returning()
123
144
  .get()
124
- return result ?? null
145
+
146
+ if (!result) {
147
+ throw new Error("User not found.")
148
+ }
149
+
150
+ return result
125
151
  },
126
- async updateSession(data) {
152
+ async updateSession(
153
+ data: Partial<AdapterSession> & Pick<AdapterSession, "sessionToken">
154
+ ) {
127
155
  const result = await client
128
- .update(sessions)
156
+ .update(sessionsTable)
129
157
  .set(data)
130
- .where(eq(sessions.sessionToken, data.sessionToken))
158
+ .where(eq(sessionsTable.sessionToken, data.sessionToken))
131
159
  .returning()
132
160
  .get()
161
+
133
162
  return result ?? null
134
163
  },
135
- async linkAccount(rawAccount) {
136
- return stripUndefined(
137
- await client.insert(accounts).values(rawAccount).returning().get()
138
- )
164
+ async linkAccount(data: AdapterAccount) {
165
+ await client.insert(accountsTable).values(data).run()
139
166
  },
140
- async getUserByAccount(account) {
141
- const results = await client
142
- .select()
143
- .from(accounts)
144
- .leftJoin(users, eq(users.id, accounts.userId))
167
+ async getUserByAccount(
168
+ account: Pick<AdapterAccount, "provider" | "providerAccountId">
169
+ ) {
170
+ const result = await client
171
+ .select({
172
+ account: accountsTable,
173
+ user: usersTable,
174
+ })
175
+ .from(accountsTable)
176
+ .innerJoin(usersTable, eq(accountsTable.userId, usersTable.id))
145
177
  .where(
146
178
  and(
147
- eq(accounts.provider, account.provider),
148
- eq(accounts.providerAccountId, account.providerAccountId)
179
+ eq(accountsTable.provider, account.provider),
180
+ eq(accountsTable.providerAccountId, account.providerAccountId)
149
181
  )
150
182
  )
151
183
  .get()
152
184
 
153
- if (!results) {
154
- return null
155
- }
156
- return Promise.resolve(results).then((results) => results.user)
185
+ return result?.user ?? null
157
186
  },
158
- async deleteSession(sessionToken) {
159
- const result = await client
160
- .delete(sessions)
161
- .where(eq(sessions.sessionToken, sessionToken))
162
- .returning()
163
- .get()
164
- return result ?? null
187
+ async deleteSession(sessionToken: string) {
188
+ await client
189
+ .delete(sessionsTable)
190
+ .where(eq(sessionsTable.sessionToken, sessionToken))
191
+ .run()
165
192
  },
166
- async createVerificationToken(token) {
167
- const result = await client
168
- .insert(verificationTokens)
169
- .values(token)
193
+ async createVerificationToken(data: VerificationToken) {
194
+ return await client
195
+ .insert(verificationTokensTable)
196
+ .values(data)
170
197
  .returning()
171
198
  .get()
172
- return result ?? null
173
- },
174
- async useVerificationToken(token) {
175
- try {
176
- const result = await client
177
- .delete(verificationTokens)
178
- .where(
179
- and(
180
- eq(verificationTokens.identifier, token.identifier),
181
- eq(verificationTokens.token, token.token)
182
- )
183
- )
184
- .returning()
185
- .get()
186
- return result ?? null
187
- } catch (err) {
188
- throw new Error("No verification token found.")
189
- }
190
199
  },
191
- async deleteUser(id) {
200
+ async useVerificationToken(params: { identifier: string; token: string }) {
192
201
  const result = await client
193
- .delete(users)
194
- .where(eq(users.id, id))
202
+ .delete(verificationTokensTable)
203
+ .where(
204
+ and(
205
+ eq(verificationTokensTable.identifier, params.identifier),
206
+ eq(verificationTokensTable.token, params.token)
207
+ )
208
+ )
195
209
  .returning()
196
210
  .get()
211
+
197
212
  return result ?? null
198
213
  },
199
- async unlinkAccount(account) {
214
+ async deleteUser(id: string) {
215
+ await client.delete(usersTable).where(eq(usersTable.id, id)).run()
216
+ },
217
+ async unlinkAccount(
218
+ params: Pick<AdapterAccount, "provider" | "providerAccountId">
219
+ ) {
200
220
  await client
201
- .delete(accounts)
221
+ .delete(accountsTable)
202
222
  .where(
203
223
  and(
204
- eq(accounts.providerAccountId, account.providerAccountId),
205
- eq(accounts.provider, account.provider)
224
+ eq(accountsTable.provider, params.provider),
225
+ eq(accountsTable.providerAccountId, params.providerAccountId)
206
226
  )
207
227
  )
208
228
  .run()
209
229
  },
210
230
  }
211
231
  }
232
+
233
+ type DefaultSQLiteColumn<
234
+ T extends {
235
+ data: string | number | Date
236
+ dataType: "string" | "number" | "date"
237
+ notNull: boolean
238
+ columnType: "SQLiteText" | "SQLiteTimestamp" | "SQLiteInteger"
239
+ },
240
+ > = SQLiteColumn<{
241
+ name: string
242
+ columnType: T["columnType"]
243
+ data: T["data"]
244
+ driverParam: string | number
245
+ notNull: T["notNull"]
246
+ hasDefault: boolean
247
+ enumValues: any
248
+ dataType: T["dataType"]
249
+ tableName: string
250
+ }>
251
+
252
+ export type DefaultSQLiteUsersTable = SQLiteTableWithColumns<{
253
+ name: string
254
+ columns: {
255
+ id: DefaultSQLiteColumn<{
256
+ columnType: "SQLiteText"
257
+ data: string
258
+ notNull: true
259
+ dataType: "string"
260
+ }>
261
+ name: DefaultSQLiteColumn<{
262
+ columnType: "SQLiteText"
263
+ data: string
264
+ notNull: boolean
265
+ dataType: "string"
266
+ }>
267
+ email: DefaultSQLiteColumn<{
268
+ columnType: "SQLiteText"
269
+ data: string
270
+ notNull: true
271
+ dataType: "string"
272
+ }>
273
+ emailVerified: DefaultSQLiteColumn<{
274
+ dataType: "date"
275
+ columnType: "SQLiteTimestamp"
276
+ data: Date
277
+ notNull: boolean
278
+ }>
279
+ image: DefaultSQLiteColumn<{
280
+ dataType: "string"
281
+ columnType: "SQLiteText"
282
+ data: string
283
+ notNull: boolean
284
+ }>
285
+ }
286
+ dialect: "sqlite"
287
+ schema: string | undefined
288
+ }>
289
+
290
+ export type DefaultSQLiteAccountsTable = SQLiteTableWithColumns<{
291
+ name: string
292
+ columns: {
293
+ userId: DefaultSQLiteColumn<{
294
+ columnType: "SQLiteText"
295
+ data: string
296
+ notNull: true
297
+ dataType: "string"
298
+ }>
299
+ type: DefaultSQLiteColumn<{
300
+ columnType: "SQLiteText"
301
+ data: string
302
+ notNull: true
303
+ dataType: "string"
304
+ }>
305
+ provider: DefaultSQLiteColumn<{
306
+ columnType: "SQLiteText"
307
+ data: string
308
+ notNull: true
309
+ dataType: "string"
310
+ }>
311
+ providerAccountId: DefaultSQLiteColumn<{
312
+ dataType: "string"
313
+ columnType: "SQLiteText"
314
+ data: string
315
+ notNull: true
316
+ }>
317
+ refresh_token: DefaultSQLiteColumn<{
318
+ dataType: "string"
319
+ columnType: "SQLiteText"
320
+ data: string
321
+ notNull: boolean
322
+ }>
323
+ access_token: DefaultSQLiteColumn<{
324
+ dataType: "string"
325
+ columnType: "SQLiteText"
326
+ data: string
327
+ notNull: boolean
328
+ }>
329
+ expires_at: DefaultSQLiteColumn<{
330
+ dataType: "number"
331
+ columnType: "SQLiteInteger"
332
+ data: number
333
+ notNull: boolean
334
+ }>
335
+ token_type: DefaultSQLiteColumn<{
336
+ dataType: "string"
337
+ columnType: "SQLiteText"
338
+ data: string
339
+ notNull: boolean
340
+ }>
341
+ scope: DefaultSQLiteColumn<{
342
+ dataType: "string"
343
+ columnType: "SQLiteText"
344
+ data: string
345
+ notNull: boolean
346
+ }>
347
+ id_token: DefaultSQLiteColumn<{
348
+ dataType: "string"
349
+ columnType: "SQLiteText"
350
+ data: string
351
+ notNull: boolean
352
+ }>
353
+ session_state: DefaultSQLiteColumn<{
354
+ dataType: "string"
355
+ columnType: "SQLiteText"
356
+ data: string
357
+ notNull: boolean
358
+ }>
359
+ }
360
+ dialect: "sqlite"
361
+ schema: string | undefined
362
+ }>
363
+
364
+ export type DefaultSQLiteSessionsTable = SQLiteTableWithColumns<{
365
+ name: string
366
+ columns: {
367
+ sessionToken: DefaultSQLiteColumn<{
368
+ columnType: "SQLiteText"
369
+ data: string
370
+ notNull: true
371
+ dataType: "string"
372
+ }>
373
+ userId: DefaultSQLiteColumn<{
374
+ columnType: "SQLiteText"
375
+ data: string
376
+ notNull: true
377
+ dataType: "string"
378
+ }>
379
+ expires: DefaultSQLiteColumn<{
380
+ dataType: "date"
381
+ columnType: "SQLiteTimestamp"
382
+ data: Date
383
+ notNull: true
384
+ }>
385
+ }
386
+ dialect: "sqlite"
387
+ schema: string | undefined
388
+ }>
389
+
390
+ export type DefaultSQLiteVerificationTokenTable = SQLiteTableWithColumns<{
391
+ name: string
392
+ columns: {
393
+ identifier: DefaultSQLiteColumn<{
394
+ columnType: "SQLiteText"
395
+ data: string
396
+ notNull: true
397
+ dataType: "string"
398
+ }>
399
+ token: DefaultSQLiteColumn<{
400
+ columnType: "SQLiteText"
401
+ data: string
402
+ notNull: true
403
+ dataType: "string"
404
+ }>
405
+ expires: DefaultSQLiteColumn<{
406
+ dataType: "date"
407
+ columnType: "SQLiteTimestamp"
408
+ data: Date
409
+ notNull: true
410
+ }>
411
+ }
412
+ dialect: "sqlite"
413
+ schema: string | undefined
414
+ }>
415
+
416
+ export type DefaultSQLiteSchema = {
417
+ usersTable: DefaultSQLiteUsersTable
418
+ accountsTable: DefaultSQLiteAccountsTable
419
+ sessionsTable: DefaultSQLiteSessionsTable
420
+ verificationTokensTable: DefaultSQLiteVerificationTokenTable
421
+ }
package/src/lib/utils.ts CHANGED
@@ -1,51 +1,33 @@
1
+ import type {
2
+ QueryResultHKT as MySQLQueryResultHKT,
3
+ PreparedQueryHKTBase,
4
+ } from "drizzle-orm/mysql-core"
1
5
  import { MySqlDatabase } from "drizzle-orm/mysql-core"
6
+ import type { QueryResultHKT as PostgresQueryResultHKT } from "drizzle-orm/pg-core"
2
7
  import { PgDatabase } from "drizzle-orm/pg-core"
3
8
  import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core"
4
-
5
- import type { AnyMySqlTable, MySqlTableFn } from "drizzle-orm/mysql-core"
6
- import type { AnyPgTable, PgTableFn } from "drizzle-orm/pg-core"
7
- import type { AnySQLiteTable, SQLiteTableFn } from "drizzle-orm/sqlite-core"
8
- import type { DefaultSchema as PgSchema } from "./pg.js"
9
- import type { DefaultSchema as MySqlSchema } from "./mysql.js"
10
- import type { DefaultSchema as SQLiteSchema } from "./sqlite.js"
11
-
12
- export type AnyMySqlDatabase = MySqlDatabase<any, any>
13
- export type AnyPgDatabase = PgDatabase<any, any, any>
14
- export type AnySQLiteDatabase = BaseSQLiteDatabase<any, any, any, any>
15
-
16
- export interface MinimumSchema {
17
- mysql: MySqlSchema & Record<string, AnyMySqlTable>
18
- pg: PgSchema & Record<string, AnyPgTable>
19
- sqlite: SQLiteSchema & Record<string, AnySQLiteTable>
20
- }
9
+ import { DefaultMySqlSchema } from "./mysql.js"
10
+ import { DefaultPostgresSchema } from "./pg.js"
11
+ import { DefaultSQLiteSchema } from "./sqlite.js"
12
+
13
+ type AnyPostgresDatabase = PgDatabase<PostgresQueryResultHKT, any>
14
+ type AnyMySqlDatabase = MySqlDatabase<
15
+ MySQLQueryResultHKT,
16
+ PreparedQueryHKTBase,
17
+ any
18
+ >
19
+ type AnySQLiteDatabase = BaseSQLiteDatabase<"sync" | "async", any, any>
21
20
 
22
21
  export type SqlFlavorOptions =
22
+ | AnyPostgresDatabase
23
23
  | AnyMySqlDatabase
24
- | AnyPgDatabase
25
24
  | AnySQLiteDatabase
26
25
 
27
- export type ClientFlavors<Flavor> = Flavor extends AnyMySqlDatabase
28
- ? MinimumSchema["mysql"]
29
- : Flavor extends AnyPgDatabase
30
- ? MinimumSchema["pg"]
31
- : Flavor extends AnySQLiteDatabase
32
- ? MinimumSchema["sqlite"]
33
- : never
34
-
35
- export type TableFn<Flavor> = Flavor extends AnyMySqlDatabase
36
- ? MySqlTableFn
37
- : Flavor extends AnyPgDatabase
38
- ? PgTableFn
39
- : Flavor extends AnySQLiteDatabase
40
- ? SQLiteTableFn
41
- : AnySQLiteTable
42
-
43
- type NonNullableProps<T> = {
44
- [P in keyof T]: null extends T[P] ? never : P
45
- }[keyof T]
46
-
47
- export function stripUndefined<T>(obj: T): Pick<T, NonNullableProps<T>> {
48
- const result = {} as T
49
- for (const key in obj) if (obj[key] !== undefined) result[key] = obj[key]
50
- return result
51
- }
26
+ export type DefaultSchema<Flavor extends SqlFlavorOptions> =
27
+ Flavor extends AnyMySqlDatabase
28
+ ? DefaultMySqlSchema
29
+ : Flavor extends AnyPostgresDatabase
30
+ ? DefaultPostgresSchema
31
+ : Flavor extends AnySQLiteDatabase
32
+ ? DefaultSQLiteSchema
33
+ : never