mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
Compare commits
17 Commits
@auth/driz
...
@auth/core
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5c8a81462 | ||
|
|
61d30f3dcd | ||
|
|
a9180a752b | ||
|
|
6c4180146e | ||
|
|
ec6c4ea2be | ||
|
|
3dfc86334e | ||
|
|
01d6019638 | ||
|
|
4730429a9f | ||
|
|
a49236ef62 | ||
|
|
96ade948ef | ||
|
|
550507b2d1 | ||
|
|
1eddcf643c | ||
|
|
17d71a04d6 | ||
|
|
3c65e264af | ||
|
|
28d8d4894d | ||
|
|
c6b98a8f08 | ||
|
|
d042f933c6 |
3
.github/ISSUE_TEMPLATE/3_bug_adapter.yml
vendored
3
.github/ISSUE_TEMPLATE/3_bug_adapter.yml
vendored
@@ -22,9 +22,12 @@ body:
|
|||||||
options:
|
options:
|
||||||
- "Custom adapter"
|
- "Custom adapter"
|
||||||
- "@auth/dgraph-adapter"
|
- "@auth/dgraph-adapter"
|
||||||
|
- "@auth/drizzle-adapter"
|
||||||
- "@auth/dynamodb-adapter"
|
- "@auth/dynamodb-adapter"
|
||||||
|
- "@auth/drizzle-adapter"
|
||||||
- "@auth/fauna-adapter"
|
- "@auth/fauna-adapter"
|
||||||
- "@auth/firebase-adapter"
|
- "@auth/firebase-adapter"
|
||||||
|
- "@auth/kysely-adapter"
|
||||||
- "@auth/mikro-orm-adapter"
|
- "@auth/mikro-orm-adapter"
|
||||||
- "@auth/mongodb-adapter"
|
- "@auth/mongodb-adapter"
|
||||||
- "@auth/neo4j-adapter"
|
- "@auth/neo4j-adapter"
|
||||||
|
|||||||
6
.github/issue-labeler.yml
vendored
6
.github/issue-labeler.yml
vendored
@@ -3,6 +3,9 @@
|
|||||||
dgraph:
|
dgraph:
|
||||||
- "@auth/dgraph-adapter"
|
- "@auth/dgraph-adapter"
|
||||||
|
|
||||||
|
drizzle:
|
||||||
|
- "@auth/drizzle-adapter"
|
||||||
|
|
||||||
dynamodb:
|
dynamodb:
|
||||||
- "@auth/dynamodb-adapter"
|
- "@auth/dynamodb-adapter"
|
||||||
|
|
||||||
@@ -12,6 +15,9 @@ fauna:
|
|||||||
firebase:
|
firebase:
|
||||||
- "@auth/firebase-adapter"
|
- "@auth/firebase-adapter"
|
||||||
|
|
||||||
|
kysely:
|
||||||
|
- "@auth/kysely-adapter"
|
||||||
|
|
||||||
mikro-orm:
|
mikro-orm:
|
||||||
- "@auth/mikro-orm-adapter"
|
- "@auth/mikro-orm-adapter"
|
||||||
|
|
||||||
|
|||||||
1
.github/pr-labeler.yml
vendored
1
.github/pr-labeler.yml
vendored
@@ -15,6 +15,7 @@ neo4j: ["packages/adapter-neo4j/**/*"]
|
|||||||
playgrounds: ["apps/playgrounds/**/*"]
|
playgrounds: ["apps/playgrounds/**/*"]
|
||||||
pouchdb: ["packages/adapter-pouchdb/**/*"]
|
pouchdb: ["packages/adapter-pouchdb/**/*"]
|
||||||
prisma: ["packages/adapter-prisma/**/*"]
|
prisma: ["packages/adapter-prisma/**/*"]
|
||||||
|
kysely: ["packages/adapter-kysely/**/*"]
|
||||||
providers: ["packages/core/src/providers/**/*"]
|
providers: ["packages/core/src/providers/**/*"]
|
||||||
sequelize: ["packages/adapter-sequelize/**/*"]
|
sequelize: ["packages/adapter-sequelize/**/*"]
|
||||||
solidjs: ["packages/frameworks-solid-start/**/*"]
|
solidjs: ["packages/frameworks-solid-start/**/*"]
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,6 +6,8 @@
|
|||||||
.env.development.local
|
.env.development.local
|
||||||
.env.test.local
|
.env.test.local
|
||||||
.env.production.local
|
.env.production.local
|
||||||
|
packages/*/.npmrc
|
||||||
|
|
||||||
|
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ Using an Auth.js / NextAuth.js adapter you can connect to any database service o
|
|||||||
<img src="/img/adapters/firebase.svg" width="40" />
|
<img src="/img/adapters/firebase.svg" width="40" />
|
||||||
<h4 class="adapter-card__title">Firebase Adapter</h4>
|
<h4 class="adapter-card__title">Firebase Adapter</h4>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="/reference/adapter/kysely" class="adapter-card">
|
||||||
|
<img src="/img/adapters/kysely.svg" width="40" />
|
||||||
|
<h4 class="adapter-card__title">Kysely Adapter</h4>
|
||||||
|
</a>
|
||||||
<a href="/reference/adapter/mikro-orm" class="adapter-card">
|
<a href="/reference/adapter/mikro-orm" class="adapter-card">
|
||||||
<img src="/img/adapters/mikro-orm.png" width="30" />
|
<img src="/img/adapters/mikro-orm.png" width="30" />
|
||||||
<h4 class="adapter-card__title">Mikro ORM Adapter</h4>
|
<h4 class="adapter-card__title">Mikro ORM Adapter</h4>
|
||||||
|
|||||||
@@ -269,6 +269,7 @@ const docusaurusConfig = {
|
|||||||
typedocAdapter("DynamoDB"),
|
typedocAdapter("DynamoDB"),
|
||||||
typedocAdapter("Fauna"),
|
typedocAdapter("Fauna"),
|
||||||
typedocAdapter("Firebase"),
|
typedocAdapter("Firebase"),
|
||||||
|
typedocAdapter("Kysely"),
|
||||||
typedocAdapter("Mikro ORM"),
|
typedocAdapter("Mikro ORM"),
|
||||||
typedocAdapter("MongoDB"),
|
typedocAdapter("MongoDB"),
|
||||||
typedocAdapter("Neo4j"),
|
typedocAdapter("Neo4j"),
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ module.exports = {
|
|||||||
{ type: "doc", id: "reference/adapter/dynamodb/index" },
|
{ type: "doc", id: "reference/adapter/dynamodb/index" },
|
||||||
{ type: "doc", id: "reference/adapter/fauna/index" },
|
{ type: "doc", id: "reference/adapter/fauna/index" },
|
||||||
{ type: "doc", id: "reference/adapter/firebase/index" },
|
{ type: "doc", id: "reference/adapter/firebase/index" },
|
||||||
|
{ type: "doc", id: "reference/adapter/kysely/index" },
|
||||||
{ type: "doc", id: "reference/adapter/mikro-orm/index" },
|
{ type: "doc", id: "reference/adapter/mikro-orm/index" },
|
||||||
{ type: "doc", id: "reference/adapter/mongodb/index" },
|
{ type: "doc", id: "reference/adapter/mongodb/index" },
|
||||||
{ type: "doc", id: "reference/adapter/neo4j/index" },
|
{ type: "doc", id: "reference/adapter/neo4j/index" },
|
||||||
|
|||||||
14
docs/static/img/adapters/kysely.svg
vendored
Normal file
14
docs/static/img/adapters/kysely.svg
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<svg width="132" height="132" viewBox="0 0 132 132" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_8_3)">
|
||||||
|
<rect x="2" y="2" width="128" height="128" rx="16" fill="white" />
|
||||||
|
<path
|
||||||
|
d="M41.2983 109V23.9091H46.4918V73.31H47.0735L91.9457 23.9091H98.8427L61.9062 64.1694L98.5103 109H92.0288L58.5824 67.9087L46.4918 81.2873V109H41.2983Z"
|
||||||
|
fill="black" />
|
||||||
|
</g>
|
||||||
|
<rect x="2" y="2" width="128" height="128" rx="16" stroke="#121212" stroke-width="4" />
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_8_3">
|
||||||
|
<rect x="2" y="2" width="128" height="128" rx="16" fill="white" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 637 B |
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"$schema": "https://openapi.vercel.sh/vercel.json",
|
||||||
"cleanUrls": true,
|
"cleanUrls": true,
|
||||||
"headers": [
|
"headers": [
|
||||||
{
|
{
|
||||||
@@ -76,10 +77,15 @@
|
|||||||
"has": [{ "type": "host", "value": "warnings.authjs.dev" }],
|
"has": [{ "type": "host", "value": "warnings.authjs.dev" }],
|
||||||
"destination": "https://authjs.dev/reference/warnings/:path*"
|
"destination": "https://authjs.dev/reference/warnings/:path*"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"source": "/",
|
||||||
|
"has": [{ "type": "host", "value": "adapters.authjs.dev" }],
|
||||||
|
"destination": "https://authjs.dev/reference/adapters"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"source": "/:path(.*)",
|
"source": "/:path(.*)",
|
||||||
"has": [{ "type": "host", "value": "adapters.authjs.dev" }],
|
"has": [{ "type": "host", "value": "adapters.authjs.dev" }],
|
||||||
"destination": "https://authjs.dev/reference/adapters/:path*"
|
"destination": "https://authjs.dev/reference/adapter/:path*"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "/:path",
|
"source": "/:path",
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<img height="64px" src="https://authjs.dev/img/logo/logo-sm.png" />
|
<img height="64px" src="https://authjs.dev/img/logo/logo-sm.png" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/drizzle-team/drizzle-orm" target="_blank">
|
<a href="https://github.com/drizzle-team/drizzle-orm" target="_blank">
|
||||||
<img height="64px" src="https://pbs.twimg.com/profile_images/1598308842391179266/CtXrfLnk_400x400.jpg"/>
|
<img height="64px" src="https://authjs.dev/img/adapters/drizzle-orm.png"/>
|
||||||
</a>
|
</a>
|
||||||
<h3 align="center"><b>Drizzle ORM Adapter</b> - NextAuth.js / Auth.js</a></h3>
|
<h3 align="center"><b>Drizzle ORM Adapter</b> - NextAuth.js / Auth.js</a></h3>
|
||||||
<p align="center" style="align: center;">
|
<p align="center" style="align: center;">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@auth/drizzle-adapter",
|
"name": "@auth/drizzle-adapter",
|
||||||
"version": "0.1.0",
|
"version": "0.2.1",
|
||||||
"description": "Drizzle adapter for Auth.js.",
|
"description": "Drizzle adapter for Auth.js.",
|
||||||
"homepage": "https://authjs.dev",
|
"homepage": "https://authjs.dev",
|
||||||
"repository": "https://github.com/nextauthjs/next-auth",
|
"repository": "https://github.com/nextauthjs/next-auth",
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
* @module @auth/drizzle-adapter
|
* @module @auth/drizzle-adapter
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { MySqlTableFn } from "drizzle-orm/mysql-core/index.js"
|
||||||
|
import { PgTableFn } from "drizzle-orm/pg-core/index.js"
|
||||||
|
import { SQLiteTableFn } from "drizzle-orm/sqlite-core/index.js"
|
||||||
import { mySqlDrizzleAdapter } from "./lib/mysql.js"
|
import { mySqlDrizzleAdapter } from "./lib/mysql.js"
|
||||||
import { pgDrizzleAdapter } from "./lib/pg.js"
|
import { pgDrizzleAdapter } from "./lib/pg.js"
|
||||||
import { SQLiteDrizzleAdapter } from "./lib/sqlite.js"
|
import { SQLiteDrizzleAdapter } from "./lib/sqlite.js"
|
||||||
@@ -24,12 +27,13 @@ import {
|
|||||||
isPgDatabase,
|
isPgDatabase,
|
||||||
isSQLiteDatabase,
|
isSQLiteDatabase,
|
||||||
SqlFlavorOptions,
|
SqlFlavorOptions,
|
||||||
|
TableFn,
|
||||||
} from "./lib/utils.js"
|
} from "./lib/utils.js"
|
||||||
|
|
||||||
import type { Adapter } from "@auth/core/adapters"
|
import type { Adapter } from "@auth/core/adapters"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the adapter to your `app/api/[...nextauth]/route.js` next-auth configuration object.
|
* Add the adapter to your `pages/api/[...nextauth].ts` next-auth configuration object.
|
||||||
*
|
*
|
||||||
* ```ts title="pages/api/auth/[...nextauth].ts"
|
* ```ts title="pages/api/auth/[...nextauth].ts"
|
||||||
* import NextAuth from "next-auth"
|
* import NextAuth from "next-auth"
|
||||||
@@ -47,6 +51,10 @@ import type { Adapter } from "@auth/core/adapters"
|
|||||||
* ],
|
* ],
|
||||||
* })
|
* })
|
||||||
* ```
|
* ```
|
||||||
|
*
|
||||||
|
* :::info
|
||||||
|
* If you're using multi-project schemas, you can pass your table function as a second argument
|
||||||
|
* :::
|
||||||
*
|
*
|
||||||
* ## Setup
|
* ## Setup
|
||||||
*
|
*
|
||||||
@@ -249,19 +257,20 @@ import type { Adapter } from "@auth/core/adapters"
|
|||||||
*
|
*
|
||||||
**/
|
**/
|
||||||
export function DrizzleAdapter<SqlFlavor extends SqlFlavorOptions>(
|
export function DrizzleAdapter<SqlFlavor extends SqlFlavorOptions>(
|
||||||
db: SqlFlavor
|
db: SqlFlavor,
|
||||||
|
table?: TableFn<SqlFlavor>
|
||||||
): Adapter {
|
): Adapter {
|
||||||
if (isMySqlDatabase(db)) {
|
if (isMySqlDatabase(db)) {
|
||||||
// We need to cast to unknown since the type overlaps (PScale is MySQL based)
|
// We need to cast to unknown since the type overlaps (PScale is MySQL based)
|
||||||
return mySqlDrizzleAdapter(db)
|
return mySqlDrizzleAdapter(db, table as MySqlTableFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPgDatabase(db)) {
|
if (isPgDatabase(db)) {
|
||||||
return pgDrizzleAdapter(db)
|
return pgDrizzleAdapter(db, table as PgTableFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSQLiteDatabase(db)) {
|
if (isSQLiteDatabase(db)) {
|
||||||
return SQLiteDrizzleAdapter(db)
|
return SQLiteDrizzleAdapter(db, table as SQLiteTableFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error("Unsupported database type in Auth.js Drizzle adapter.")
|
throw new Error("Unsupported database type in Auth.js Drizzle adapter.")
|
||||||
|
|||||||
@@ -2,75 +2,87 @@ import { and, eq } from "drizzle-orm"
|
|||||||
import {
|
import {
|
||||||
int,
|
int,
|
||||||
timestamp,
|
timestamp,
|
||||||
mysqlTable,
|
mysqlTable as defaultMySqlTableFn,
|
||||||
primaryKey,
|
primaryKey,
|
||||||
varchar,
|
varchar,
|
||||||
|
MySqlTableFn,
|
||||||
} from "drizzle-orm/mysql-core"
|
} from "drizzle-orm/mysql-core"
|
||||||
|
|
||||||
import type { Adapter, AdapterAccount } from "@auth/core/adapters"
|
import type { Adapter, AdapterAccount } from "@auth/core/adapters"
|
||||||
import type { MySql2Database } from "drizzle-orm/mysql2"
|
import type { MySql2Database } from "drizzle-orm/mysql2"
|
||||||
|
|
||||||
export const users = mysqlTable("users", {
|
export function createTables(mySqlTable: MySqlTableFn) {
|
||||||
id: varchar("id", { length: 255 }).notNull().primaryKey(),
|
const users = mySqlTable("users", {
|
||||||
name: varchar("name", { length: 255 }),
|
id: varchar("id", { length: 255 }).notNull().primaryKey(),
|
||||||
email: varchar("email", { length: 255 }).notNull(),
|
name: varchar("name", { length: 255 }),
|
||||||
emailVerified: timestamp("emailVerified", {
|
email: varchar("email", { length: 255 }).notNull(),
|
||||||
mode: "date",
|
emailVerified: timestamp("emailVerified", {
|
||||||
fsp: 3,
|
mode: "date",
|
||||||
}).defaultNow(),
|
fsp: 3,
|
||||||
image: varchar("image", { length: 255 }),
|
}).defaultNow(),
|
||||||
})
|
image: varchar("image", { length: 255 }),
|
||||||
|
})
|
||||||
|
|
||||||
export const accounts = mysqlTable(
|
const accounts = mySqlTable(
|
||||||
"accounts",
|
"accounts",
|
||||||
{
|
{
|
||||||
|
userId: varchar("userId", { length: 255 })
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
|
type: varchar("type", { length: 255 })
|
||||||
|
.$type<AdapterAccount["type"]>()
|
||||||
|
.notNull(),
|
||||||
|
provider: varchar("provider", { length: 255 }).notNull(),
|
||||||
|
providerAccountId: varchar("providerAccountId", {
|
||||||
|
length: 255,
|
||||||
|
}).notNull(),
|
||||||
|
refresh_token: varchar("refresh_token", { length: 255 }),
|
||||||
|
access_token: varchar("access_token", { length: 255 }),
|
||||||
|
expires_at: int("expires_at"),
|
||||||
|
token_type: varchar("token_type", { length: 255 }),
|
||||||
|
scope: varchar("scope", { length: 255 }),
|
||||||
|
id_token: varchar("id_token", { length: 255 }),
|
||||||
|
session_state: varchar("session_state", { length: 255 }),
|
||||||
|
},
|
||||||
|
(account) => ({
|
||||||
|
compoundKey: primaryKey(account.provider, account.providerAccountId),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const sessions = mySqlTable("sessions", {
|
||||||
|
sessionToken: varchar("sessionToken", { length: 255 })
|
||||||
|
.notNull()
|
||||||
|
.primaryKey(),
|
||||||
userId: varchar("userId", { length: 255 })
|
userId: varchar("userId", { length: 255 })
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
type: varchar("type", { length: 255 })
|
|
||||||
.$type<AdapterAccount["type"]>()
|
|
||||||
.notNull(),
|
|
||||||
provider: varchar("provider", { length: 255 }).notNull(),
|
|
||||||
providerAccountId: varchar("providerAccountId", { length: 255 }).notNull(),
|
|
||||||
refresh_token: varchar("refresh_token", { length: 255 }),
|
|
||||||
access_token: varchar("access_token", { length: 255 }),
|
|
||||||
expires_at: int("expires_at"),
|
|
||||||
token_type: varchar("token_type", { length: 255 }),
|
|
||||||
scope: varchar("scope", { length: 255 }),
|
|
||||||
id_token: varchar("id_token", { length: 255 }),
|
|
||||||
session_state: varchar("session_state", { length: 255 }),
|
|
||||||
},
|
|
||||||
(account) => ({
|
|
||||||
compoundKey: primaryKey(account.provider, account.providerAccountId),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
export const sessions = mysqlTable("sessions", {
|
|
||||||
sessionToken: varchar("sessionToken", { length: 255 }).notNull().primaryKey(),
|
|
||||||
userId: varchar("userId", { length: 255 })
|
|
||||||
.notNull()
|
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
|
||||||
expires: timestamp("expires", { mode: "date" }).notNull(),
|
|
||||||
})
|
|
||||||
|
|
||||||
export const verificationTokens = mysqlTable(
|
|
||||||
"verificationToken",
|
|
||||||
{
|
|
||||||
identifier: varchar("identifier", { length: 255 }).notNull(),
|
|
||||||
token: varchar("token", { length: 255 }).notNull(),
|
|
||||||
expires: timestamp("expires", { mode: "date" }).notNull(),
|
expires: timestamp("expires", { mode: "date" }).notNull(),
|
||||||
},
|
|
||||||
(vt) => ({
|
|
||||||
compoundKey: primaryKey(vt.identifier, vt.token),
|
|
||||||
})
|
})
|
||||||
)
|
|
||||||
|
|
||||||
export const schema = { users, accounts, sessions, verificationTokens }
|
const verificationTokens = mySqlTable(
|
||||||
export type DefaultSchema = typeof schema
|
"verificationToken",
|
||||||
|
{
|
||||||
|
identifier: varchar("identifier", { length: 255 }).notNull(),
|
||||||
|
token: varchar("token", { length: 255 }).notNull(),
|
||||||
|
expires: timestamp("expires", { mode: "date" }).notNull(),
|
||||||
|
},
|
||||||
|
(vt) => ({
|
||||||
|
compoundKey: primaryKey(vt.identifier, vt.token),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
return { users, accounts, sessions, verificationTokens }
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DefaultSchema = ReturnType<typeof createTables>
|
||||||
|
|
||||||
export function mySqlDrizzleAdapter(
|
export function mySqlDrizzleAdapter(
|
||||||
client: MySql2Database<Record<string, never>>
|
client: MySql2Database<Record<string, never>>,
|
||||||
|
tableFn = defaultMySqlTableFn
|
||||||
): Adapter {
|
): Adapter {
|
||||||
|
const { users, accounts, sessions, verificationTokens } =
|
||||||
|
createTables(tableFn)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
async createUser(data) {
|
async createUser(data) {
|
||||||
const id = crypto.randomUUID()
|
const id = crypto.randomUUID()
|
||||||
|
|||||||
@@ -1,71 +1,79 @@
|
|||||||
import { and, eq } from "drizzle-orm"
|
import { and, eq } from "drizzle-orm"
|
||||||
import {
|
import {
|
||||||
timestamp,
|
timestamp,
|
||||||
pgTable,
|
pgTable as defaultPgTableFn,
|
||||||
text,
|
text,
|
||||||
primaryKey,
|
primaryKey,
|
||||||
integer,
|
integer,
|
||||||
|
PgTableFn,
|
||||||
} from "drizzle-orm/pg-core"
|
} from "drizzle-orm/pg-core"
|
||||||
|
|
||||||
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js"
|
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js"
|
||||||
import type { Adapter, AdapterAccount } from "@auth/core/adapters"
|
import type { Adapter, AdapterAccount } from "@auth/core/adapters"
|
||||||
|
|
||||||
export const users = pgTable("users", {
|
export function createTables(pgTable: PgTableFn) {
|
||||||
id: text("id").notNull().primaryKey(),
|
const users = pgTable("users", {
|
||||||
name: text("name"),
|
id: text("id").notNull().primaryKey(),
|
||||||
email: text("email").notNull(),
|
name: text("name"),
|
||||||
emailVerified: timestamp("emailVerified", { mode: "date" }),
|
email: text("email").notNull(),
|
||||||
image: text("image"),
|
emailVerified: timestamp("emailVerified", { mode: "date" }),
|
||||||
})
|
image: text("image"),
|
||||||
|
})
|
||||||
|
|
||||||
export const accounts = pgTable(
|
const accounts = pgTable(
|
||||||
"accounts",
|
"accounts",
|
||||||
{
|
{
|
||||||
|
userId: text("userId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
|
type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
||||||
|
provider: text("provider").notNull(),
|
||||||
|
providerAccountId: text("providerAccountId").notNull(),
|
||||||
|
refresh_token: text("refresh_token"),
|
||||||
|
access_token: text("access_token"),
|
||||||
|
expires_at: integer("expires_at"),
|
||||||
|
token_type: text("token_type"),
|
||||||
|
scope: text("scope"),
|
||||||
|
id_token: text("id_token"),
|
||||||
|
session_state: text("session_state"),
|
||||||
|
},
|
||||||
|
(account) => ({
|
||||||
|
compoundKey: primaryKey(account.provider, account.providerAccountId),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const sessions = pgTable("sessions", {
|
||||||
|
sessionToken: text("sessionToken").notNull().primaryKey(),
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
|
||||||
provider: text("provider").notNull(),
|
|
||||||
providerAccountId: text("providerAccountId").notNull(),
|
|
||||||
refresh_token: text("refresh_token"),
|
|
||||||
access_token: text("access_token"),
|
|
||||||
expires_at: integer("expires_at"),
|
|
||||||
token_type: text("token_type"),
|
|
||||||
scope: text("scope"),
|
|
||||||
id_token: text("id_token"),
|
|
||||||
session_state: text("session_state"),
|
|
||||||
},
|
|
||||||
(account) => ({
|
|
||||||
compoundKey: primaryKey(account.provider, account.providerAccountId),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
export const sessions = pgTable("sessions", {
|
|
||||||
sessionToken: text("sessionToken").notNull().primaryKey(),
|
|
||||||
userId: text("userId")
|
|
||||||
.notNull()
|
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
|
||||||
expires: timestamp("expires", { mode: "date" }).notNull(),
|
|
||||||
})
|
|
||||||
|
|
||||||
export const verificationTokens = pgTable(
|
|
||||||
"verificationToken",
|
|
||||||
{
|
|
||||||
identifier: text("identifier").notNull(),
|
|
||||||
token: text("token").notNull(),
|
|
||||||
expires: timestamp("expires", { mode: "date" }).notNull(),
|
expires: timestamp("expires", { mode: "date" }).notNull(),
|
||||||
},
|
|
||||||
(vt) => ({
|
|
||||||
compoundKey: primaryKey(vt.identifier, vt.token),
|
|
||||||
})
|
})
|
||||||
)
|
|
||||||
|
|
||||||
export const schema = { users, accounts, sessions, verificationTokens }
|
const verificationTokens = pgTable(
|
||||||
export type DefaultSchema = typeof schema
|
"verificationToken",
|
||||||
|
{
|
||||||
|
identifier: text("identifier").notNull(),
|
||||||
|
token: text("token").notNull(),
|
||||||
|
expires: timestamp("expires", { mode: "date" }).notNull(),
|
||||||
|
},
|
||||||
|
(vt) => ({
|
||||||
|
compoundKey: primaryKey(vt.identifier, vt.token),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
return { users, accounts, sessions, verificationTokens }
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DefaultSchema = ReturnType<typeof createTables>
|
||||||
|
|
||||||
export function pgDrizzleAdapter(
|
export function pgDrizzleAdapter(
|
||||||
client: PostgresJsDatabase<Record<string, never>>
|
client: PostgresJsDatabase<Record<string, never>>,
|
||||||
|
tableFn = defaultPgTableFn
|
||||||
): Adapter {
|
): Adapter {
|
||||||
|
const { users, accounts, sessions, verificationTokens } =
|
||||||
|
createTables(tableFn)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
async createUser(data) {
|
async createUser(data) {
|
||||||
return await client
|
return await client
|
||||||
|
|||||||
@@ -1,70 +1,78 @@
|
|||||||
import { eq, and } from "drizzle-orm"
|
import { eq, and } from "drizzle-orm"
|
||||||
import {
|
import {
|
||||||
integer,
|
integer,
|
||||||
sqliteTable,
|
sqliteTable as defaultSqliteTableFn,
|
||||||
text,
|
text,
|
||||||
primaryKey,
|
primaryKey,
|
||||||
BaseSQLiteDatabase,
|
BaseSQLiteDatabase,
|
||||||
|
SQLiteTableFn,
|
||||||
} from "drizzle-orm/sqlite-core"
|
} from "drizzle-orm/sqlite-core"
|
||||||
|
|
||||||
import type { Adapter, AdapterAccount } from "@auth/core/adapters"
|
import type { Adapter, AdapterAccount } from "@auth/core/adapters"
|
||||||
|
|
||||||
export const users = sqliteTable("users", {
|
export function createTables(sqliteTable: SQLiteTableFn) {
|
||||||
id: text("id").notNull().primaryKey(),
|
const users = sqliteTable("users", {
|
||||||
name: text("name"),
|
id: text("id").notNull().primaryKey(),
|
||||||
email: text("email").notNull(),
|
name: text("name"),
|
||||||
emailVerified: integer("emailVerified", { mode: "timestamp_ms" }),
|
email: text("email").notNull(),
|
||||||
image: text("image"),
|
emailVerified: integer("emailVerified", { mode: "timestamp_ms" }),
|
||||||
})
|
image: text("image"),
|
||||||
|
})
|
||||||
|
|
||||||
export const accounts = sqliteTable(
|
const accounts = sqliteTable(
|
||||||
"accounts",
|
"accounts",
|
||||||
{
|
{
|
||||||
|
userId: text("userId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
|
type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
||||||
|
provider: text("provider").notNull(),
|
||||||
|
providerAccountId: text("providerAccountId").notNull(),
|
||||||
|
refresh_token: text("refresh_token"),
|
||||||
|
access_token: text("access_token"),
|
||||||
|
expires_at: integer("expires_at"),
|
||||||
|
token_type: text("token_type"),
|
||||||
|
scope: text("scope"),
|
||||||
|
id_token: text("id_token"),
|
||||||
|
session_state: text("session_state"),
|
||||||
|
},
|
||||||
|
(account) => ({
|
||||||
|
compoundKey: primaryKey(account.provider, account.providerAccountId),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const sessions = sqliteTable("sessions", {
|
||||||
|
sessionToken: text("sessionToken").notNull().primaryKey(),
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
.references(() => users.id, { onDelete: "cascade" }),
|
||||||
type: text("type").$type<AdapterAccount["type"]>().notNull(),
|
|
||||||
provider: text("provider").notNull(),
|
|
||||||
providerAccountId: text("providerAccountId").notNull(),
|
|
||||||
refresh_token: text("refresh_token"),
|
|
||||||
access_token: text("access_token"),
|
|
||||||
expires_at: integer("expires_at"),
|
|
||||||
token_type: text("token_type"),
|
|
||||||
scope: text("scope"),
|
|
||||||
id_token: text("id_token"),
|
|
||||||
session_state: text("session_state"),
|
|
||||||
},
|
|
||||||
(account) => ({
|
|
||||||
compoundKey: primaryKey(account.provider, account.providerAccountId),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
export const sessions = sqliteTable("sessions", {
|
|
||||||
sessionToken: text("sessionToken").notNull().primaryKey(),
|
|
||||||
userId: text("userId")
|
|
||||||
.notNull()
|
|
||||||
.references(() => users.id, { onDelete: "cascade" }),
|
|
||||||
expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
|
|
||||||
})
|
|
||||||
|
|
||||||
export const verificationTokens = sqliteTable(
|
|
||||||
"verificationToken",
|
|
||||||
{
|
|
||||||
identifier: text("identifier").notNull(),
|
|
||||||
token: text("token").notNull(),
|
|
||||||
expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
|
expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
|
||||||
},
|
|
||||||
(vt) => ({
|
|
||||||
compoundKey: primaryKey(vt.identifier, vt.token),
|
|
||||||
})
|
})
|
||||||
)
|
|
||||||
|
|
||||||
export const schema = { users, accounts, sessions, verificationTokens }
|
const verificationTokens = sqliteTable(
|
||||||
export type DefaultSchema = typeof schema
|
"verificationToken",
|
||||||
|
{
|
||||||
|
identifier: text("identifier").notNull(),
|
||||||
|
token: text("token").notNull(),
|
||||||
|
expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
|
||||||
|
},
|
||||||
|
(vt) => ({
|
||||||
|
compoundKey: primaryKey(vt.identifier, vt.token),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
return { users, accounts, sessions, verificationTokens }
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DefaultSchema = ReturnType<typeof createTables>
|
||||||
|
|
||||||
export function SQLiteDrizzleAdapter(
|
export function SQLiteDrizzleAdapter(
|
||||||
client: BaseSQLiteDatabase<any, any>
|
client: BaseSQLiteDatabase<any, any>,
|
||||||
|
tableFn = defaultSqliteTableFn
|
||||||
): Adapter {
|
): Adapter {
|
||||||
|
const { users, accounts, sessions, verificationTokens } =
|
||||||
|
createTables(tableFn)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createUser(data) {
|
createUser(data) {
|
||||||
return client
|
return client
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import { MySqlDatabase } from "drizzle-orm/mysql-core"
|
|||||||
import { PgDatabase } from "drizzle-orm/pg-core"
|
import { PgDatabase } from "drizzle-orm/pg-core"
|
||||||
import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core"
|
import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core"
|
||||||
|
|
||||||
import type { AnyMySqlTable } from "drizzle-orm/mysql-core"
|
import type { AnyMySqlTable, MySqlTableFn } from "drizzle-orm/mysql-core"
|
||||||
import type { AnyPgTable } from "drizzle-orm/pg-core"
|
import type { AnyPgTable, PgTableFn } from "drizzle-orm/pg-core"
|
||||||
import type { AnySQLiteTable } from "drizzle-orm/sqlite-core"
|
import type { AnySQLiteTable, SQLiteTableFn } from "drizzle-orm/sqlite-core"
|
||||||
import type { DefaultSchema as PgSchema } from "./pg.js"
|
import type { DefaultSchema as PgSchema } from "./pg.js"
|
||||||
import type { DefaultSchema as MySqlSchema } from "./mysql.js"
|
import type { DefaultSchema as MySqlSchema } from "./mysql.js"
|
||||||
import type { DefaultSchema as SQLiteSchema } from "./sqlite.js"
|
import type { DefaultSchema as SQLiteSchema } from "./sqlite.js"
|
||||||
@@ -32,6 +32,14 @@ export type ClientFlavors<Flavor> = Flavor extends AnyMySqlDatabase
|
|||||||
? MinimumSchema["sqlite"]
|
? MinimumSchema["sqlite"]
|
||||||
: never
|
: never
|
||||||
|
|
||||||
|
export type TableFn<Flavor> = Flavor extends AnyMySqlDatabase
|
||||||
|
? MySqlTableFn
|
||||||
|
: Flavor extends AnyPgDatabase
|
||||||
|
? PgTableFn
|
||||||
|
: Flavor extends AnySQLiteDatabase
|
||||||
|
? SQLiteTableFn
|
||||||
|
: AnySQLiteTable
|
||||||
|
|
||||||
export function isMySqlDatabase(
|
export function isMySqlDatabase(
|
||||||
db: any
|
db: any
|
||||||
): db is MySqlDatabase<any, any, any, any> {
|
): db is MySqlDatabase<any, any, any, any> {
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import type { Config } from "drizzle-kit"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
schema: "./tests/mysql/schema.ts",
|
||||||
|
out: "./tests/mysql/.drizzle",
|
||||||
|
driver: "mysql2",
|
||||||
|
dbCredentials: {
|
||||||
|
host: "localhost",
|
||||||
|
user: "root",
|
||||||
|
password: "password",
|
||||||
|
database: "next-auth",
|
||||||
|
},
|
||||||
|
} satisfies Config
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { runBasicTests } from "../../../adapter-test"
|
||||||
|
import { DrizzleAdapter } from "../../src"
|
||||||
|
import { db, sessions, verificationTokens, accounts, users } from "./schema"
|
||||||
|
import { eq, and } from "drizzle-orm"
|
||||||
|
import { fixtures } from "../fixtures"
|
||||||
|
|
||||||
|
globalThis.crypto ??= require("node:crypto").webcrypto
|
||||||
|
|
||||||
|
runBasicTests({
|
||||||
|
adapter: DrizzleAdapter(db),
|
||||||
|
fixtures,
|
||||||
|
db: {
|
||||||
|
connect: async () => {
|
||||||
|
await Promise.all([
|
||||||
|
db.delete(sessions),
|
||||||
|
db.delete(accounts),
|
||||||
|
db.delete(verificationTokens),
|
||||||
|
db.delete(users),
|
||||||
|
])
|
||||||
|
},
|
||||||
|
disconnect: async () => {
|
||||||
|
await Promise.all([
|
||||||
|
db.delete(sessions),
|
||||||
|
db.delete(accounts),
|
||||||
|
db.delete(verificationTokens),
|
||||||
|
db.delete(users),
|
||||||
|
])
|
||||||
|
},
|
||||||
|
user: async (id) => {
|
||||||
|
const user = await db
|
||||||
|
.select()
|
||||||
|
.from(users)
|
||||||
|
.where(eq(users.id, id))
|
||||||
|
.then((res) => res[0] ?? null)
|
||||||
|
return user
|
||||||
|
},
|
||||||
|
session: async (sessionToken) => {
|
||||||
|
const session = await db
|
||||||
|
.select()
|
||||||
|
.from(sessions)
|
||||||
|
.where(eq(sessions.sessionToken, sessionToken))
|
||||||
|
.then((res) => res[0] ?? null)
|
||||||
|
|
||||||
|
return session
|
||||||
|
},
|
||||||
|
account: (provider_providerAccountId) => {
|
||||||
|
const account = db
|
||||||
|
.select()
|
||||||
|
.from(accounts)
|
||||||
|
.where(
|
||||||
|
eq(
|
||||||
|
accounts.providerAccountId,
|
||||||
|
provider_providerAccountId.providerAccountId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then((res) => res[0] ?? null)
|
||||||
|
return account
|
||||||
|
},
|
||||||
|
verificationToken: (identifier_token) =>
|
||||||
|
db
|
||||||
|
.select()
|
||||||
|
.from(verificationTokens)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(verificationTokens.token, identifier_token.token),
|
||||||
|
eq(verificationTokens.identifier, identifier_token.identifier)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then((res) => res[0]) ?? null,
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { mysqlTableCreator } from "drizzle-orm/mysql-core"
|
||||||
|
import { drizzle } from "drizzle-orm/mysql2"
|
||||||
|
import { createPool } from "mysql2"
|
||||||
|
import { createTables } from "../../src/lib/mysql"
|
||||||
|
|
||||||
|
const poolConnection = createPool({
|
||||||
|
host: "localhost",
|
||||||
|
user: "root",
|
||||||
|
password: "password",
|
||||||
|
database: "next-auth",
|
||||||
|
})
|
||||||
|
|
||||||
|
const mysqlTable = mysqlTableCreator((name) => `foobar_${name}`)
|
||||||
|
|
||||||
|
export const { users, accounts, sessions, verificationTokens } =
|
||||||
|
createTables(mysqlTable)
|
||||||
|
export const schema = { users, accounts, sessions, verificationTokens }
|
||||||
|
|
||||||
|
export const db = drizzle(poolConnection, { schema })
|
||||||
22
packages/adapter-drizzle/tests/mysql-multi-project-schema/test.sh
Executable file
22
packages/adapter-drizzle/tests/mysql-multi-project-schema/test.sh
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
echo "Initializing container for MySQL tests."
|
||||||
|
|
||||||
|
MYSQL_DATABASE=next-auth
|
||||||
|
MYSQL_ROOT_PASSWORD=password
|
||||||
|
MYSQL_CONTAINER_NAME=next-auth-mysql-test
|
||||||
|
|
||||||
|
docker run -d --rm \
|
||||||
|
-e MYSQL_DATABASE=${MYSQL_DATABASE} \
|
||||||
|
-e MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} \
|
||||||
|
--name "${MYSQL_CONTAINER_NAME}" \
|
||||||
|
-p 3306:3306 \
|
||||||
|
mysql:8 \
|
||||||
|
--default-authentication-plugin=mysql_native_password
|
||||||
|
|
||||||
|
echo "Waiting 15 sec for db to start..." && sleep 15
|
||||||
|
|
||||||
|
drizzle-kit generate:mysql --config=./tests/mysql/drizzle.config.ts
|
||||||
|
drizzle-kit push:mysql --config=./tests/mysql/drizzle.config.ts
|
||||||
|
jest ./tests/mysql/index.test.ts --forceExit
|
||||||
|
docker stop ${MYSQL_CONTAINER_NAME}
|
||||||
@@ -1,20 +1,7 @@
|
|||||||
import type { AdapterAccount } from "@auth/core/adapters"
|
import { mysqlTable } from "drizzle-orm/mysql-core"
|
||||||
import {
|
|
||||||
mysqlTable,
|
|
||||||
varchar,
|
|
||||||
timestamp,
|
|
||||||
int,
|
|
||||||
primaryKey,
|
|
||||||
} from "drizzle-orm/mysql-core"
|
|
||||||
import { drizzle } from "drizzle-orm/mysql2"
|
import { drizzle } from "drizzle-orm/mysql2"
|
||||||
import { createPool } from "mysql2"
|
import { createPool } from "mysql2"
|
||||||
import {
|
import { createTables } from "../../src/lib/mysql"
|
||||||
users,
|
|
||||||
accounts,
|
|
||||||
sessions,
|
|
||||||
verificationTokens,
|
|
||||||
schema,
|
|
||||||
} from "../../src/lib/mysql"
|
|
||||||
|
|
||||||
const poolConnection = createPool({
|
const poolConnection = createPool({
|
||||||
host: "localhost",
|
host: "localhost",
|
||||||
@@ -23,7 +10,8 @@ const poolConnection = createPool({
|
|||||||
database: "next-auth",
|
database: "next-auth",
|
||||||
})
|
})
|
||||||
|
|
||||||
export { users, accounts, sessions, verificationTokens }
|
export const { users, accounts, sessions, verificationTokens } =
|
||||||
export const db = drizzle(poolConnection, {
|
createTables(mysqlTable)
|
||||||
schema: schema,
|
export const schema = { users, accounts, sessions, verificationTokens }
|
||||||
})
|
|
||||||
|
export const db = drizzle(poolConnection, { schema })
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import type { Config } from "drizzle-kit"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
schema: "./tests/pg/schema.ts",
|
||||||
|
out: "./tests/pg/.drizzle",
|
||||||
|
dbCredentials: {
|
||||||
|
database: "nextauth",
|
||||||
|
host: "nextauth",
|
||||||
|
user: "nextauth",
|
||||||
|
password: "nextauth",
|
||||||
|
port: 5432,
|
||||||
|
},
|
||||||
|
} satisfies Config
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { runBasicTests } from "../../../adapter-test"
|
||||||
|
import { DrizzleAdapter } from "../../src"
|
||||||
|
import { db, accounts, sessions, users, verificationTokens } from "./schema"
|
||||||
|
import { eq, and } from "drizzle-orm"
|
||||||
|
import { fixtures } from "../fixtures"
|
||||||
|
|
||||||
|
globalThis.crypto ??= require("node:crypto").webcrypto
|
||||||
|
|
||||||
|
runBasicTests({
|
||||||
|
adapter: DrizzleAdapter(db),
|
||||||
|
fixtures,
|
||||||
|
db: {
|
||||||
|
connect: async () => {
|
||||||
|
await Promise.all([
|
||||||
|
db.delete(sessions),
|
||||||
|
db.delete(accounts),
|
||||||
|
db.delete(verificationTokens),
|
||||||
|
db.delete(users),
|
||||||
|
])
|
||||||
|
},
|
||||||
|
disconnect: async () => {
|
||||||
|
await Promise.all([
|
||||||
|
db.delete(sessions),
|
||||||
|
db.delete(accounts),
|
||||||
|
db.delete(verificationTokens),
|
||||||
|
db.delete(users),
|
||||||
|
])
|
||||||
|
},
|
||||||
|
user: async (id) =>
|
||||||
|
db
|
||||||
|
.select()
|
||||||
|
.from(users)
|
||||||
|
.where(eq(users.id, id))
|
||||||
|
.then((res) => res[0] ?? null),
|
||||||
|
session: (sessionToken) =>
|
||||||
|
db
|
||||||
|
.select()
|
||||||
|
.from(sessions)
|
||||||
|
.where(eq(sessions.sessionToken, sessionToken))
|
||||||
|
.then((res) => res[0] ?? null),
|
||||||
|
account: (provider_providerAccountId) => {
|
||||||
|
return db
|
||||||
|
.select()
|
||||||
|
.from(accounts)
|
||||||
|
.where(
|
||||||
|
eq(
|
||||||
|
accounts.providerAccountId,
|
||||||
|
provider_providerAccountId.providerAccountId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then((res) => res[0] ?? null)
|
||||||
|
},
|
||||||
|
verificationToken: (identifier_token) =>
|
||||||
|
db
|
||||||
|
.select()
|
||||||
|
.from(verificationTokens)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(verificationTokens.token, identifier_token.token),
|
||||||
|
eq(verificationTokens.identifier, identifier_token.identifier)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then((res) => res[0] ?? null),
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { migrate } from "drizzle-orm/postgres-js/migrator"
|
||||||
|
import { db } from "./schema"
|
||||||
|
|
||||||
|
const migrator = async () => {
|
||||||
|
await migrate(db, { migrationsFolder: "./tests/pg/.drizzle" })
|
||||||
|
}
|
||||||
|
|
||||||
|
migrator()
|
||||||
|
.then(() => process.exit(0))
|
||||||
|
.catch(() => process.exit(1))
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { drizzle } from "drizzle-orm/postgres-js"
|
||||||
|
import postgres from "postgres"
|
||||||
|
import { createTables } from "../../src/lib/pg"
|
||||||
|
import { pgTableCreator } from "drizzle-orm/pg-core"
|
||||||
|
|
||||||
|
const connectionString = "postgres://nextauth:nextauth@localhost:5432/nextauth"
|
||||||
|
const sql = postgres(connectionString, { max: 1 })
|
||||||
|
|
||||||
|
const pgTable = pgTableCreator((name) => `foobar_${name}`)
|
||||||
|
|
||||||
|
export const { users, accounts, sessions, verificationTokens } =
|
||||||
|
createTables(pgTable)
|
||||||
|
export const schema = { users, accounts, sessions, verificationTokens }
|
||||||
|
|
||||||
|
export const db = drizzle(sql, {
|
||||||
|
schema,
|
||||||
|
})
|
||||||
25
packages/adapter-drizzle/tests/pg-multi-project-schema/test.sh
Executable file
25
packages/adapter-drizzle/tests/pg-multi-project-schema/test.sh
Executable file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
echo "Initializing container for PostgreSQL tests."
|
||||||
|
|
||||||
|
PGUSER=nextauth
|
||||||
|
PGPASSWORD=nextauth
|
||||||
|
PGDATABASE=nextauth
|
||||||
|
PGPORT=5432
|
||||||
|
PG_CONTAINER_NAME=next-auth-postgres-test
|
||||||
|
|
||||||
|
docker run -d --rm \
|
||||||
|
-e POSTGRES_USER=${PGUSER} \
|
||||||
|
-e POSTGRES_PASSWORD=${PGUSER} \
|
||||||
|
-e POSTGRES_DB=${PGDATABASE} \
|
||||||
|
-e POSTGRES_HOST_AUTH_METHOD=trust \
|
||||||
|
--name "${PG_CONTAINER_NAME}" \
|
||||||
|
-p ${PGPORT}:5432 \
|
||||||
|
postgres:15.3
|
||||||
|
|
||||||
|
echo "Waiting 15 sec for db to start..." && sleep 15
|
||||||
|
|
||||||
|
drizzle-kit generate:pg --config=./tests/pg/drizzle.config.ts
|
||||||
|
npx tsx ./tests/pg/migrator.ts
|
||||||
|
jest ./tests/pg/index.test.ts --forceExit
|
||||||
|
docker stop ${PG_CONTAINER_NAME}
|
||||||
@@ -1,11 +1,15 @@
|
|||||||
import { drizzle } from "drizzle-orm/postgres-js"
|
import { drizzle } from "drizzle-orm/postgres-js"
|
||||||
import postgres from "postgres"
|
import postgres from "postgres"
|
||||||
import { users, accounts, sessions, verificationTokens } from "../../src/lib/pg"
|
import { createTables } from "../../src/lib/pg"
|
||||||
|
import { pgTable } from "drizzle-orm/pg-core"
|
||||||
|
|
||||||
const connectionString = "postgres://nextauth:nextauth@localhost:5432/nextauth"
|
const connectionString = "postgres://nextauth:nextauth@localhost:5432/nextauth"
|
||||||
const sql = postgres(connectionString, { max: 1 })
|
const sql = postgres(connectionString, { max: 1 })
|
||||||
|
|
||||||
|
export const { users, accounts, sessions, verificationTokens } =
|
||||||
|
createTables(pgTable)
|
||||||
|
export const schema = { users, accounts, sessions, verificationTokens }
|
||||||
|
|
||||||
export const db = drizzle(sql, {
|
export const db = drizzle(sql, {
|
||||||
schema: { users, accounts, sessions, verificationTokens },
|
schema,
|
||||||
})
|
})
|
||||||
export { users, accounts, sessions, verificationTokens }
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import type { Config } from "drizzle-kit"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
schema: "./tests/sqlite/schema.ts",
|
||||||
|
out: "./tests/sqlite/.drizzle",
|
||||||
|
driver: "better-sqlite",
|
||||||
|
dbCredentials: {
|
||||||
|
url: "./db.sqlite",
|
||||||
|
},
|
||||||
|
} satisfies Config
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import { runBasicTests } from "../../../adapter-test"
|
||||||
|
import { DrizzleAdapter } from "../../src"
|
||||||
|
import { db, accounts, sessions, users, verificationTokens } from "./schema"
|
||||||
|
import { eq, and } from "drizzle-orm"
|
||||||
|
|
||||||
|
globalThis.crypto ??= require("node:crypto").webcrypto
|
||||||
|
|
||||||
|
runBasicTests({
|
||||||
|
adapter: DrizzleAdapter(db),
|
||||||
|
db: {
|
||||||
|
connect: async () => {
|
||||||
|
await Promise.all([
|
||||||
|
db.delete(sessions),
|
||||||
|
db.delete(accounts),
|
||||||
|
db.delete(verificationTokens),
|
||||||
|
db.delete(users),
|
||||||
|
])
|
||||||
|
},
|
||||||
|
disconnect: async () => {
|
||||||
|
await Promise.all([
|
||||||
|
db.delete(sessions),
|
||||||
|
db.delete(accounts),
|
||||||
|
db.delete(verificationTokens),
|
||||||
|
db.delete(users),
|
||||||
|
])
|
||||||
|
},
|
||||||
|
user: (id) => db.select().from(users).where(eq(users.id, id)).get() ?? null,
|
||||||
|
session: (sessionToken) =>
|
||||||
|
db
|
||||||
|
.select()
|
||||||
|
.from(sessions)
|
||||||
|
.where(eq(sessions.sessionToken, sessionToken))
|
||||||
|
.get() ?? null,
|
||||||
|
account: (provider_providerAccountId) => {
|
||||||
|
return (
|
||||||
|
db
|
||||||
|
.select()
|
||||||
|
.from(accounts)
|
||||||
|
.where(
|
||||||
|
eq(
|
||||||
|
accounts.providerAccountId,
|
||||||
|
provider_providerAccountId.providerAccountId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.get() ?? null
|
||||||
|
)
|
||||||
|
},
|
||||||
|
verificationToken: (identifier_token) =>
|
||||||
|
db
|
||||||
|
.select()
|
||||||
|
.from(verificationTokens)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(verificationTokens.token, identifier_token.token),
|
||||||
|
eq(verificationTokens.identifier, identifier_token.identifier)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.get() ?? null,
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { drizzle } from "drizzle-orm/better-sqlite3"
|
||||||
|
import Database from "better-sqlite3"
|
||||||
|
import { createTables } from "../../src/lib/sqlite"
|
||||||
|
import { sqliteTableCreator } from "drizzle-orm/sqlite-core"
|
||||||
|
|
||||||
|
const sqlite = new Database("db.sqlite")
|
||||||
|
|
||||||
|
const sqliteTable = sqliteTableCreator((name) => `foobar_${name}`)
|
||||||
|
|
||||||
|
export const { users, accounts, sessions, verificationTokens } =
|
||||||
|
createTables(sqliteTable)
|
||||||
|
export const schema = { users, accounts, sessions, verificationTokens }
|
||||||
|
|
||||||
|
export const db = drizzle(sqlite, { schema })
|
||||||
12
packages/adapter-drizzle/tests/sqlite-multi-project-schema/test.sh
Executable file
12
packages/adapter-drizzle/tests/sqlite-multi-project-schema/test.sh
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
|
||||||
|
echo "Running SQLite tests."
|
||||||
|
|
||||||
|
rm -f db.sqlite
|
||||||
|
|
||||||
|
drizzle-kit generate:sqlite --config=./tests/sqlite/drizzle.config.ts
|
||||||
|
drizzle-kit push:sqlite --config=./tests/sqlite/drizzle.config.ts
|
||||||
|
jest ./tests/sqlite/index.test.ts --forceExit
|
||||||
@@ -1,20 +1,12 @@
|
|||||||
import { drizzle } from "drizzle-orm/better-sqlite3"
|
import { drizzle } from "drizzle-orm/better-sqlite3"
|
||||||
import Database from "better-sqlite3"
|
import Database from "better-sqlite3"
|
||||||
import {
|
import { createTables } from "../../src/lib/sqlite"
|
||||||
users,
|
import { sqliteTable } from "drizzle-orm/sqlite-core"
|
||||||
accounts,
|
|
||||||
sessions,
|
|
||||||
verificationTokens,
|
|
||||||
} from "../../src/lib/sqlite"
|
|
||||||
|
|
||||||
const sqlite = new Database("db.sqlite")
|
const sqlite = new Database("db.sqlite")
|
||||||
|
|
||||||
export { users, accounts, sessions, verificationTokens }
|
export const { users, accounts, sessions, verificationTokens } =
|
||||||
export const db = drizzle(sqlite, {
|
createTables(sqliteTable)
|
||||||
schema: {
|
export const schema = { users, accounts, sessions, verificationTokens }
|
||||||
users,
|
|
||||||
accounts,
|
export const db = drizzle(sqlite, { schema })
|
||||||
sessions,
|
|
||||||
verificationTokens,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
28
packages/adapter-kysely/README.md
Normal file
28
packages/adapter-kysely/README.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<p align="center">
|
||||||
|
<br/>
|
||||||
|
<a href="https://authjs.dev" target="_blank">
|
||||||
|
<img height="64px" src="https://authjs.dev/img/logo/logo-sm.png" />
|
||||||
|
</a>
|
||||||
|
<a href="https://kysely.io" target="_blank">
|
||||||
|
<img height="64px" src="https://authjs.dev/img/adapters/kysely.svg"/>
|
||||||
|
</a>
|
||||||
|
<h3 align="center"><b>Kysely Adapter</b> - NextAuth.js / Auth.js</a></h3>
|
||||||
|
<p align="center" style="align: center;">
|
||||||
|
<a href="https://npm.im/@auth/kysely-adapter">
|
||||||
|
<img src="https://img.shields.io/badge/TypeScript-blue?style=flat-square" alt="TypeScript" />
|
||||||
|
</a>
|
||||||
|
<a href="https://npm.im/@auth/kysely-adapter">
|
||||||
|
<img alt="npm" src="https://img.shields.io/npm/v/@auth/kysely-adapter?color=green&label=@auth/kysely-adapter&style=flat-square">
|
||||||
|
</a>
|
||||||
|
<a href="https://www.npmtrends.com/@auth/kysely-adapter">
|
||||||
|
<img src="https://img.shields.io/npm/dm/@auth/kysely-adapter?label=%20downloads&style=flat-square" alt="Downloads" />
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/nextauthjs/next-auth/stargazers">
|
||||||
|
<img src="https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square" alt="Github Stars" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Check out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/kysely).
|
||||||
56
packages/adapter-kysely/package.json
Normal file
56
packages/adapter-kysely/package.json
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"name": "@auth/kysely-adapter",
|
||||||
|
"version": "0.1.1",
|
||||||
|
"description": "Kysely adapter for Auth.js",
|
||||||
|
"homepage": "https://authjs.dev/reference/adapter/kysely",
|
||||||
|
"repository": "https://github.com/nextauthjs/next-auth",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/nextauthjs/next-auth/issues"
|
||||||
|
},
|
||||||
|
"author": "mwojtul <mark.wojtul@gmail.com> (https://github.com/mwojtul)",
|
||||||
|
"license": "ISC",
|
||||||
|
"keywords": [
|
||||||
|
"authjs",
|
||||||
|
"next-auth",
|
||||||
|
"next.js",
|
||||||
|
"oauth",
|
||||||
|
"kysely"
|
||||||
|
],
|
||||||
|
"type": "module",
|
||||||
|
"types": "./index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"*.js",
|
||||||
|
"*.d.ts*",
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./index.d.ts",
|
||||||
|
"import": "./index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"test": "./tests/test.sh"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@auth/core": "workspace:*"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"kysely": "^0.26.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@next-auth/adapter-test": "workspace:*",
|
||||||
|
"@next-auth/tsconfig": "workspace:*",
|
||||||
|
"@types/better-sqlite3": "^7.6.3",
|
||||||
|
"@types/pg": "^8.6.5",
|
||||||
|
"better-sqlite3": "^8.2.0",
|
||||||
|
"jest": "^27.4.3",
|
||||||
|
"kysely": "^0.24.2",
|
||||||
|
"mysql2": "^3.2.0",
|
||||||
|
"pg": "^8.10.0"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"preset": "@next-auth/adapter-test/jest"
|
||||||
|
}
|
||||||
|
}
|
||||||
472
packages/adapter-kysely/src/index.ts
Normal file
472
packages/adapter-kysely/src/index.ts
Normal file
@@ -0,0 +1,472 @@
|
|||||||
|
/**
|
||||||
|
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", padding: 16}}>
|
||||||
|
* <p style={{fontWeight: "normal"}}>Official <a href="https://kysely.dev/">Kysely</a> adapter for Auth.js / NextAuth.js.</p>
|
||||||
|
* <a href="https://kysely.dev/">
|
||||||
|
* <img style={{display: "block"}} src="/img/adapters/kysely.svg" width="38" />
|
||||||
|
* </a>
|
||||||
|
* </div>
|
||||||
|
*
|
||||||
|
* ## Installation
|
||||||
|
*
|
||||||
|
* ```bash npm2yarn2pnpm
|
||||||
|
* npm install kysely @auth/kysely-adapter
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @module @auth/kysely-adapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Kysely, SqliteAdapter } from "kysely"
|
||||||
|
|
||||||
|
import type { Adapter } from "@auth/core/adapters"
|
||||||
|
import type { GeneratedAlways } from "kysely"
|
||||||
|
|
||||||
|
export interface Database {
|
||||||
|
User: {
|
||||||
|
id: GeneratedAlways<string>
|
||||||
|
name: string | null
|
||||||
|
email: string
|
||||||
|
emailVerified: Date | string | null
|
||||||
|
image: string | null
|
||||||
|
}
|
||||||
|
Account: {
|
||||||
|
id: GeneratedAlways<string>
|
||||||
|
userId: string
|
||||||
|
type: string
|
||||||
|
provider: string
|
||||||
|
providerAccountId: string
|
||||||
|
refresh_token: string | null
|
||||||
|
access_token: string | null
|
||||||
|
expires_at: number | null
|
||||||
|
token_type: string | null
|
||||||
|
scope: string | null
|
||||||
|
id_token: string | null
|
||||||
|
session_state: string | null
|
||||||
|
}
|
||||||
|
Session: {
|
||||||
|
id: GeneratedAlways<string>
|
||||||
|
userId: string
|
||||||
|
sessionToken: string
|
||||||
|
expires: Date | string
|
||||||
|
}
|
||||||
|
VerificationToken: {
|
||||||
|
identifier: string
|
||||||
|
token: string
|
||||||
|
expires: Date | string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const format = {
|
||||||
|
/**
|
||||||
|
* Helper function to return the passed in object and its specified prop
|
||||||
|
* as an ISO string if SQLite is being used.
|
||||||
|
*/
|
||||||
|
from<T extends Partial<Record<K, Date | null>>, K extends keyof T>(
|
||||||
|
data: T,
|
||||||
|
key: K,
|
||||||
|
isSqlite: boolean
|
||||||
|
) {
|
||||||
|
const value = data[key]
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
[key]: value && isSqlite ? value.toISOString() : value,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
to,
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReturnData<T = never> = Record<string, Date | string | T>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to return the passed in object and its specified prop as a date.
|
||||||
|
* Necessary because SQLite has no date type so we store dates as ISO strings.
|
||||||
|
*/
|
||||||
|
function to<T extends Partial<ReturnData>, K extends keyof T>(
|
||||||
|
data: T,
|
||||||
|
key: K
|
||||||
|
): Omit<T, K> & Record<K, Date>
|
||||||
|
function to<T extends Partial<ReturnData<null>>, K extends keyof T>(
|
||||||
|
data: T,
|
||||||
|
key: K
|
||||||
|
): Omit<T, K> & Record<K, Date | null>
|
||||||
|
function to<T extends Partial<ReturnData<null>>, K extends keyof T>(
|
||||||
|
data: T,
|
||||||
|
key: K
|
||||||
|
) {
|
||||||
|
const value = data[key]
|
||||||
|
return Object.assign(data, {
|
||||||
|
[key]: value && typeof value === "string" ? new Date(value) : value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* ## Setup
|
||||||
|
*
|
||||||
|
* This adapter supports the same first party dialects that Kysely (as of v0.24.2) supports: PostgreSQL, MySQL, and SQLite. The examples below use PostgreSQL with the [pg](https://www.npmjs.com/package/pg) client.
|
||||||
|
*
|
||||||
|
* ```bash npm2yarn2pnpm
|
||||||
|
* npm install pg
|
||||||
|
* npm install --save-dev @types/pg
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ```typescript title="pages/api/auth/[...nextauth].ts"
|
||||||
|
* import NextAuth from "next-auth"
|
||||||
|
* import GoogleProvider from "next-auth/providers/google"
|
||||||
|
* import { KyselyAdapter } from "@auth/kysely-adapter"
|
||||||
|
* import { db } from "../../../db"
|
||||||
|
*
|
||||||
|
* export default NextAuth({
|
||||||
|
* adapter: KyselyAdapter(db),
|
||||||
|
* providers: [
|
||||||
|
* GoogleProvider({
|
||||||
|
* clientId: process.env.GOOGLE_CLIENT_ID,
|
||||||
|
* clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||||
|
* }),
|
||||||
|
* ],
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Kysely's constructor requires a database interface that contains an entry with an interface for each of your tables. You can define these types manually, or use `kysely-codegen` / `prisma-kysely` to automatically generate them. Check out the default [models](/reference/adapters#models) required by Auth.js.
|
||||||
|
*
|
||||||
|
* ```ts title="db.ts"
|
||||||
|
* import { PostgresDialect } from "kysely"
|
||||||
|
* import { Pool } from "pg"
|
||||||
|
*
|
||||||
|
* // This adapter exports a wrapper of the original `Kysely` class called `KyselyAuth`,
|
||||||
|
* // that can be used to provide additional type-safety.
|
||||||
|
* // While using it isn't required, it is recommended as it will verify
|
||||||
|
* // that the database interface has all the fields that Auth.js expects.
|
||||||
|
* import { KyselyAuth } from "@auth/kysely-adapter"
|
||||||
|
*
|
||||||
|
* import type { GeneratedAlways } from "kysely"
|
||||||
|
*
|
||||||
|
* interface Database {
|
||||||
|
* User: {
|
||||||
|
* id: GeneratedAlways<string>
|
||||||
|
* name: string | null
|
||||||
|
* email: string
|
||||||
|
* emailVerified: Date | null
|
||||||
|
* image: string | null
|
||||||
|
* }
|
||||||
|
* Account: {
|
||||||
|
* id: GeneratedAlways<string>
|
||||||
|
* userId: string
|
||||||
|
* type: string
|
||||||
|
* provider: string
|
||||||
|
* providerAccountId: string
|
||||||
|
* refresh_token: string | null
|
||||||
|
* access_token: string | null
|
||||||
|
* expires_at: number | null
|
||||||
|
* token_type: string | null
|
||||||
|
* scope: string | null
|
||||||
|
* id_token: string | null
|
||||||
|
* session_state: string | null
|
||||||
|
* }
|
||||||
|
* Session: {
|
||||||
|
* id: GeneratedAlways<string>
|
||||||
|
* userId: string
|
||||||
|
* sessionToken: string
|
||||||
|
* expires: Date
|
||||||
|
* }
|
||||||
|
* VerificationToken: {
|
||||||
|
* identifier: string
|
||||||
|
* token: string
|
||||||
|
* expires: Date
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* export const db = new KyselyAuth<Database>({
|
||||||
|
* dialect: new PostgresDialect({
|
||||||
|
* pool: new Pool({
|
||||||
|
* host: process.env.DATABASE_HOST,
|
||||||
|
* database: process.env.DATABASE_NAME,
|
||||||
|
* user: process.env.DATABASE_USER,
|
||||||
|
* password: process.env.DATABASE_PASSWORD,
|
||||||
|
* }),
|
||||||
|
* }),
|
||||||
|
* })
|
||||||
|
```
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* :::note
|
||||||
|
* An alternative to manually defining types is generating them from the database schema using [kysely-codegen](https://github.com/RobinBlomberg/kysely-codegen), or from Prisma schemas using [prisma-kysely](https://github.com/valtyr/prisma-kysely). When using generated types with `KyselyAuth`, import `Codegen` and pass it as the second generic arg:
|
||||||
|
* ```ts
|
||||||
|
* import type { Codegen } from "@auth/kysely-adapter"
|
||||||
|
* new KyselyAuth<Database, Codegen>(...)
|
||||||
|
* ```
|
||||||
|
* :::
|
||||||
|
* ### Schema
|
||||||
|
* ```ts title="db/migrations/001_create_db.ts"
|
||||||
|
* import { Kysely, sql } from "kysely"
|
||||||
|
*
|
||||||
|
* export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
* await db.schema
|
||||||
|
* .createTable("User")
|
||||||
|
* .addColumn("id", "uuid", (col) =>
|
||||||
|
* col.primaryKey().defaultTo(sql`gen_random_uuid()`)
|
||||||
|
* )
|
||||||
|
* .addColumn("name", "text")
|
||||||
|
* .addColumn("email", "text", (col) => col.unique().notNull())
|
||||||
|
* .addColumn("emailVerified", "timestamptz")
|
||||||
|
* .addColumn("image", "text")
|
||||||
|
* .execute()
|
||||||
|
*
|
||||||
|
* await db.schema
|
||||||
|
* .createTable("Account")
|
||||||
|
* .addColumn("id", "uuid", (col) =>
|
||||||
|
* col.primaryKey().defaultTo(sql`gen_random_uuid()`)
|
||||||
|
* )
|
||||||
|
* .addColumn("userId", "uuid", (col) =>
|
||||||
|
* col.references("User.id").onDelete("cascade").notNull()
|
||||||
|
* )
|
||||||
|
* .addColumn("type", "text", (col) => col.notNull())
|
||||||
|
* .addColumn("provider", "text", (col) => col.notNull())
|
||||||
|
* .addColumn("providerAccountId", "text", (col) => col.notNull())
|
||||||
|
* .addColumn("refresh_token", "text")
|
||||||
|
* .addColumn("access_token", "text")
|
||||||
|
* .addColumn("expires_at", "bigint")
|
||||||
|
* .addColumn("token_type", "text")
|
||||||
|
* .addColumn("scope", "text")
|
||||||
|
* .addColumn("id_token", "text")
|
||||||
|
* .addColumn("session_state", "text")
|
||||||
|
* .execute()
|
||||||
|
*
|
||||||
|
* await db.schema
|
||||||
|
* .createTable("Session")
|
||||||
|
* .addColumn("id", "uuid", (col) =>
|
||||||
|
* col.primaryKey().defaultTo(sql`gen_random_uuid()`)
|
||||||
|
* )
|
||||||
|
* .addColumn("userId", "uuid", (col) =>
|
||||||
|
* col.references("User.id").onDelete("cascade").notNull()
|
||||||
|
* )
|
||||||
|
* .addColumn("sessionToken", "text", (col) => col.notNull().unique())
|
||||||
|
* .addColumn("expires", "timestamptz", (col) => col.notNull())
|
||||||
|
* .execute()
|
||||||
|
*
|
||||||
|
* await db.schema
|
||||||
|
* .createTable("VerificationToken")
|
||||||
|
* .addColumn("identifier", "text", (col) => col.notNull())
|
||||||
|
* .addColumn("token", "text", (col) => col.notNull().unique())
|
||||||
|
* .addColumn("expires", "timestamptz", (col) => col.notNull())
|
||||||
|
* .execute()
|
||||||
|
*
|
||||||
|
* await db.schema
|
||||||
|
* .createIndex("Account_userId_index")
|
||||||
|
* .on("Account")
|
||||||
|
* .column("userId")
|
||||||
|
* .execute()
|
||||||
|
*
|
||||||
|
* await db.schema
|
||||||
|
* .createIndex("Session_userId_index")
|
||||||
|
* .on("Session")
|
||||||
|
* .column("userId")
|
||||||
|
* .execute()
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* export async function down(db: Kysely<any>): Promise<void> {
|
||||||
|
* await db.schema.dropTable("Account").ifExists().execute()
|
||||||
|
* await db.schema.dropTable("Session").ifExists().execute()
|
||||||
|
* await db.schema.dropTable("User").ifExists().execute()
|
||||||
|
* await db.schema.dropTable("VerificationToken").ifExists().execute()
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
* > This schema is adapted for use in Kysely and is based upon our main [schema](/reference/adapters#models).
|
||||||
|
*
|
||||||
|
* For more information about creating and running migrations with Kysely, refer to the [Kysely migrations documentation](https://kysely.dev/docs/migrations).
|
||||||
|
*
|
||||||
|
* ### Naming conventions
|
||||||
|
* If mixed snake_case and camelCase column names is an issue for you and/or your underlying database system, we recommend using Kysely's `CamelCasePlugin` ([see the documentation here](https://kysely-org.github.io/kysely/classes/CamelCasePlugin.html)) feature to change the field names. This won't affect NextAuth.js, but will allow you to have consistent casing when using Kysely.
|
||||||
|
*/
|
||||||
|
export function KyselyAdapter(db: Kysely<Database>): Adapter {
|
||||||
|
const { adapter } = db.getExecutor()
|
||||||
|
const supportsReturning = adapter.supportsReturning
|
||||||
|
const isSqlite = adapter instanceof SqliteAdapter
|
||||||
|
|
||||||
|
return {
|
||||||
|
async createUser(data) {
|
||||||
|
const userData = format.from(data, "emailVerified", isSqlite)
|
||||||
|
const query = db.insertInto("User").values(userData)
|
||||||
|
const result = supportsReturning
|
||||||
|
? await query.returningAll().executeTakeFirstOrThrow()
|
||||||
|
: await query.executeTakeFirstOrThrow().then(async () => {
|
||||||
|
return await db
|
||||||
|
.selectFrom("User")
|
||||||
|
.selectAll()
|
||||||
|
.where("email", "=", `${userData.email}`)
|
||||||
|
.executeTakeFirstOrThrow()
|
||||||
|
})
|
||||||
|
return to(result, "emailVerified")
|
||||||
|
},
|
||||||
|
async getUser(id) {
|
||||||
|
const result =
|
||||||
|
(await db
|
||||||
|
.selectFrom("User")
|
||||||
|
.selectAll()
|
||||||
|
.where("id", "=", id)
|
||||||
|
.executeTakeFirst()) ?? null
|
||||||
|
if (!result) return null
|
||||||
|
return to(result, "emailVerified")
|
||||||
|
},
|
||||||
|
async getUserByEmail(email) {
|
||||||
|
const result =
|
||||||
|
(await db
|
||||||
|
.selectFrom("User")
|
||||||
|
.selectAll()
|
||||||
|
.where("email", "=", email)
|
||||||
|
.executeTakeFirst()) ?? null
|
||||||
|
if (!result) return null
|
||||||
|
return to(result, "emailVerified")
|
||||||
|
},
|
||||||
|
async getUserByAccount({ providerAccountId, provider }) {
|
||||||
|
const result =
|
||||||
|
(await db
|
||||||
|
.selectFrom("User")
|
||||||
|
.innerJoin("Account", "User.id", "Account.userId")
|
||||||
|
.selectAll("User")
|
||||||
|
.where("Account.providerAccountId", "=", providerAccountId)
|
||||||
|
.where("Account.provider", "=", provider)
|
||||||
|
.executeTakeFirst()) ?? null
|
||||||
|
if (!result) return null
|
||||||
|
return to(result, "emailVerified")
|
||||||
|
},
|
||||||
|
async updateUser({ id, ...user }) {
|
||||||
|
if (!id) throw new Error("User not found")
|
||||||
|
const userData = format.from(user, "emailVerified", isSqlite)
|
||||||
|
const query = db.updateTable("User").set(userData).where("id", "=", id)
|
||||||
|
const result = supportsReturning
|
||||||
|
? await query.returningAll().executeTakeFirstOrThrow()
|
||||||
|
: await query.executeTakeFirstOrThrow().then(async () => {
|
||||||
|
return await db
|
||||||
|
.selectFrom("User")
|
||||||
|
.selectAll()
|
||||||
|
.where("id", "=", id)
|
||||||
|
.executeTakeFirstOrThrow()
|
||||||
|
})
|
||||||
|
return to(result, "emailVerified")
|
||||||
|
},
|
||||||
|
async deleteUser(userId) {
|
||||||
|
await db.deleteFrom("User").where("User.id", "=", userId).execute()
|
||||||
|
},
|
||||||
|
async linkAccount(account) {
|
||||||
|
await db.insertInto("Account").values(account).executeTakeFirstOrThrow()
|
||||||
|
},
|
||||||
|
async unlinkAccount({ providerAccountId, provider }) {
|
||||||
|
await db
|
||||||
|
.deleteFrom("Account")
|
||||||
|
.where("Account.providerAccountId", "=", providerAccountId)
|
||||||
|
.where("Account.provider", "=", provider)
|
||||||
|
.executeTakeFirstOrThrow()
|
||||||
|
},
|
||||||
|
async createSession(data) {
|
||||||
|
const sessionData = format.from(data, "expires", isSqlite)
|
||||||
|
const query = db.insertInto("Session").values(sessionData)
|
||||||
|
const result = supportsReturning
|
||||||
|
? await query.returningAll().executeTakeFirstOrThrow()
|
||||||
|
: await (async () => {
|
||||||
|
await query.executeTakeFirstOrThrow()
|
||||||
|
return await db
|
||||||
|
.selectFrom("Session")
|
||||||
|
.selectAll()
|
||||||
|
.where("sessionToken", "=", sessionData.sessionToken)
|
||||||
|
.executeTakeFirstOrThrow()
|
||||||
|
})()
|
||||||
|
return to(result, "expires")
|
||||||
|
},
|
||||||
|
async getSessionAndUser(sessionTokenArg) {
|
||||||
|
const result = await db
|
||||||
|
.selectFrom("Session")
|
||||||
|
.innerJoin("User", "User.id", "Session.userId")
|
||||||
|
.selectAll("User")
|
||||||
|
.select([
|
||||||
|
"Session.id as sessionId",
|
||||||
|
"Session.userId",
|
||||||
|
"Session.sessionToken",
|
||||||
|
"Session.expires",
|
||||||
|
])
|
||||||
|
.where("Session.sessionToken", "=", sessionTokenArg)
|
||||||
|
.executeTakeFirst()
|
||||||
|
if (!result) return null
|
||||||
|
const { sessionId: id, userId, sessionToken, expires, ...user } = result
|
||||||
|
return {
|
||||||
|
user: to({ ...user }, "emailVerified"),
|
||||||
|
session: to({ id, userId, sessionToken, expires }, "expires"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async updateSession(session) {
|
||||||
|
const sessionData = format.from(session, "expires", isSqlite)
|
||||||
|
const query = db
|
||||||
|
.updateTable("Session")
|
||||||
|
.set(sessionData)
|
||||||
|
.where("Session.sessionToken", "=", session.sessionToken)
|
||||||
|
const result = supportsReturning
|
||||||
|
? await query.returningAll().executeTakeFirstOrThrow()
|
||||||
|
: await query.executeTakeFirstOrThrow().then(async () => {
|
||||||
|
return await db
|
||||||
|
.selectFrom("Session")
|
||||||
|
.selectAll()
|
||||||
|
.where("Session.sessionToken", "=", sessionData.sessionToken)
|
||||||
|
.executeTakeFirstOrThrow()
|
||||||
|
})
|
||||||
|
return to(result, "expires")
|
||||||
|
},
|
||||||
|
async deleteSession(sessionToken) {
|
||||||
|
await db
|
||||||
|
.deleteFrom("Session")
|
||||||
|
.where("Session.sessionToken", "=", sessionToken)
|
||||||
|
.executeTakeFirstOrThrow()
|
||||||
|
},
|
||||||
|
async createVerificationToken(verificationToken) {
|
||||||
|
const verificationTokenData = format.from(
|
||||||
|
verificationToken,
|
||||||
|
"expires",
|
||||||
|
isSqlite
|
||||||
|
)
|
||||||
|
const query = db
|
||||||
|
.insertInto("VerificationToken")
|
||||||
|
.values(verificationTokenData)
|
||||||
|
const result = supportsReturning
|
||||||
|
? await query.returningAll().executeTakeFirstOrThrow()
|
||||||
|
: await query.executeTakeFirstOrThrow().then(async () => {
|
||||||
|
return await db
|
||||||
|
.selectFrom("VerificationToken")
|
||||||
|
.selectAll()
|
||||||
|
.where("token", "=", verificationTokenData.token)
|
||||||
|
.executeTakeFirstOrThrow()
|
||||||
|
})
|
||||||
|
return to(result, "expires")
|
||||||
|
},
|
||||||
|
async useVerificationToken({ identifier, token }) {
|
||||||
|
const query = db
|
||||||
|
.deleteFrom("VerificationToken")
|
||||||
|
.where("VerificationToken.token", "=", token)
|
||||||
|
.where("VerificationToken.identifier", "=", identifier)
|
||||||
|
const result = supportsReturning
|
||||||
|
? (await query.returningAll().executeTakeFirst()) ?? null
|
||||||
|
: await db
|
||||||
|
.selectFrom("VerificationToken")
|
||||||
|
.selectAll()
|
||||||
|
.where("token", "=", token)
|
||||||
|
.executeTakeFirst()
|
||||||
|
.then(async (res) => {
|
||||||
|
await query.executeTakeFirst()
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
if (!result) return null
|
||||||
|
return to(result, "expires")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper over the original `Kysely` class in order to validate the passed in
|
||||||
|
* database interface. A regular Kysely instance may also be used, but wrapping
|
||||||
|
* it ensures the database interface implements the fields that Auth.js
|
||||||
|
* requires. When used with `kysely-codegen`, the `Codegen` type can be passed as
|
||||||
|
* the second generic argument. The generated types will be used, and
|
||||||
|
* `KyselyAuth` will only verify that the correct fields exist.
|
||||||
|
**/
|
||||||
|
export class KyselyAuth<DB extends T, T = Database> extends Kysely<DB> {}
|
||||||
|
|
||||||
|
export type Codegen = {
|
||||||
|
[K in keyof Database]: { [J in keyof Database[K]]: unknown }
|
||||||
|
}
|
||||||
250
packages/adapter-kysely/tests/index.test.ts
Normal file
250
packages/adapter-kysely/tests/index.test.ts
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
import { runBasicTests } from "@next-auth/adapter-test"
|
||||||
|
import { Pool } from "pg"
|
||||||
|
import {
|
||||||
|
Kysely,
|
||||||
|
MysqlDialect,
|
||||||
|
PostgresDialect,
|
||||||
|
SchemaModule,
|
||||||
|
sql,
|
||||||
|
SqliteAdapter,
|
||||||
|
SqliteDialect,
|
||||||
|
} from "kysely"
|
||||||
|
import { KyselyAdapter, KyselyAuth } from "../src"
|
||||||
|
import { createPool } from "mysql2"
|
||||||
|
import SqliteDatabase from "better-sqlite3"
|
||||||
|
import type { Database } from "../src"
|
||||||
|
import { DataTypeExpression } from "kysely/dist/cjs/parser/data-type-parser"
|
||||||
|
|
||||||
|
type BuiltInDialect = "postgres" | "mysql" | "sqlite"
|
||||||
|
|
||||||
|
const POOL_SIZE = 20
|
||||||
|
const DIALECT_CONFIGS = {
|
||||||
|
postgres: {
|
||||||
|
host: "localhost",
|
||||||
|
database: "kysely_test",
|
||||||
|
user: "kysely",
|
||||||
|
port: 5434,
|
||||||
|
max: POOL_SIZE,
|
||||||
|
},
|
||||||
|
mysql: {
|
||||||
|
database: "kysely_test",
|
||||||
|
host: "localhost",
|
||||||
|
user: "kysely",
|
||||||
|
password: "kysely",
|
||||||
|
port: 3308,
|
||||||
|
supportBigNumbers: true,
|
||||||
|
bigNumberStrings: true,
|
||||||
|
connectionLimit: POOL_SIZE,
|
||||||
|
},
|
||||||
|
sqlite: {
|
||||||
|
databasePath: ":memory:",
|
||||||
|
},
|
||||||
|
} as const
|
||||||
|
|
||||||
|
async function dropDatabase(db: Kysely<Database>): Promise<void> {
|
||||||
|
await Promise.all([
|
||||||
|
db.schema.dropTable("Account").ifExists().execute(),
|
||||||
|
db.schema.dropTable("Session").ifExists().execute(),
|
||||||
|
db.schema.dropTable("User").ifExists().execute(),
|
||||||
|
db.schema.dropTable("VerificationToken").ifExists().execute(),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createTableWithId(
|
||||||
|
schema: SchemaModule,
|
||||||
|
dialect: BuiltInDialect,
|
||||||
|
tableName: string
|
||||||
|
) {
|
||||||
|
const builder = schema.createTable(tableName)
|
||||||
|
|
||||||
|
if (dialect === "postgres") {
|
||||||
|
return builder.addColumn("id", "uuid", (col) =>
|
||||||
|
col.primaryKey().defaultTo(sql`gen_random_uuid()`)
|
||||||
|
)
|
||||||
|
} else if (dialect === "mysql") {
|
||||||
|
return builder.addColumn("id", "varchar(36)", (col) =>
|
||||||
|
col.primaryKey().defaultTo(sql`(UUID())`)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return builder.addColumn("id", "integer", (col) =>
|
||||||
|
col.autoIncrement().primaryKey()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createDatabase(
|
||||||
|
db: Kysely<Database>,
|
||||||
|
dialect: BuiltInDialect
|
||||||
|
): Promise<void> {
|
||||||
|
const defaultTimestamp = {
|
||||||
|
postgres: sql`NOW()`,
|
||||||
|
mysql: sql`NOW(3)`,
|
||||||
|
sqlite: sql`CURRENT_TIMESTAMP`,
|
||||||
|
}[dialect]
|
||||||
|
const uuidColumnType: DataTypeExpression =
|
||||||
|
dialect === "mysql" ? "varchar(36)" : "uuid"
|
||||||
|
const dateColumnType: DataTypeExpression =
|
||||||
|
dialect === "mysql" ? sql`DATETIME(3)` : "timestamptz"
|
||||||
|
const textColumnType: DataTypeExpression =
|
||||||
|
dialect === "mysql" ? "varchar(255)" : "text"
|
||||||
|
|
||||||
|
await dropDatabase(db)
|
||||||
|
|
||||||
|
await createTableWithId(db.schema, dialect, "User")
|
||||||
|
.addColumn("name", textColumnType)
|
||||||
|
.addColumn("email", textColumnType, (col) => col.unique().notNull())
|
||||||
|
.addColumn("emailVerified", dateColumnType, (col) =>
|
||||||
|
col.defaultTo(defaultTimestamp)
|
||||||
|
)
|
||||||
|
.addColumn("image", textColumnType)
|
||||||
|
.execute()
|
||||||
|
|
||||||
|
let createAccountTable = createTableWithId(db.schema, dialect, "Account")
|
||||||
|
.addColumn("userId", uuidColumnType, (col) =>
|
||||||
|
col.references("User.id").onDelete("cascade").notNull()
|
||||||
|
)
|
||||||
|
.addColumn("type", textColumnType, (col) => col.notNull())
|
||||||
|
.addColumn("provider", textColumnType, (col) => col.notNull())
|
||||||
|
.addColumn("providerAccountId", textColumnType, (col) => col.notNull())
|
||||||
|
.addColumn("refresh_token", textColumnType)
|
||||||
|
.addColumn("access_token", textColumnType)
|
||||||
|
.addColumn("expires_at", "bigint")
|
||||||
|
.addColumn("token_type", textColumnType)
|
||||||
|
.addColumn("scope", textColumnType)
|
||||||
|
.addColumn("id_token", textColumnType)
|
||||||
|
.addColumn("session_state", textColumnType)
|
||||||
|
if (dialect === "mysql")
|
||||||
|
createAccountTable = createAccountTable.addForeignKeyConstraint(
|
||||||
|
"Account_userId_fk",
|
||||||
|
["userId"],
|
||||||
|
"User",
|
||||||
|
["id"],
|
||||||
|
(cb) => cb.onDelete("cascade")
|
||||||
|
)
|
||||||
|
await createAccountTable.execute()
|
||||||
|
|
||||||
|
let createSessionTable = createTableWithId(db.schema, dialect, "Session")
|
||||||
|
.addColumn("userId", uuidColumnType, (col) =>
|
||||||
|
col.references("User.id").onDelete("cascade").notNull()
|
||||||
|
)
|
||||||
|
.addColumn("sessionToken", textColumnType, (col) => col.notNull().unique())
|
||||||
|
.addColumn("expires", dateColumnType, (col) => col.notNull())
|
||||||
|
|
||||||
|
if (dialect === "mysql")
|
||||||
|
createSessionTable = createSessionTable.addForeignKeyConstraint(
|
||||||
|
"Session_userId_fk",
|
||||||
|
["userId"],
|
||||||
|
"User",
|
||||||
|
["id"],
|
||||||
|
(cb) => cb.onDelete("cascade")
|
||||||
|
)
|
||||||
|
await createSessionTable.execute()
|
||||||
|
|
||||||
|
await db.schema
|
||||||
|
.createTable("VerificationToken")
|
||||||
|
.addColumn("identifier", textColumnType, (col) => col.notNull())
|
||||||
|
.addColumn("token", textColumnType, (col) => col.notNull().unique())
|
||||||
|
.addColumn("expires", dateColumnType, (col) => col.notNull())
|
||||||
|
.execute()
|
||||||
|
|
||||||
|
await db.schema
|
||||||
|
.createIndex("Account_userId_index")
|
||||||
|
.on("Account")
|
||||||
|
.column("userId")
|
||||||
|
.execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
const runDialectBasicTests = (
|
||||||
|
db: Kysely<Database>,
|
||||||
|
dialect: BuiltInDialect
|
||||||
|
) => {
|
||||||
|
const datesStoredAsISOStrings =
|
||||||
|
db.getExecutor().adapter instanceof SqliteAdapter
|
||||||
|
|
||||||
|
runBasicTests({
|
||||||
|
adapter: KyselyAdapter(db),
|
||||||
|
db: {
|
||||||
|
async connect() {
|
||||||
|
await dropDatabase(db)
|
||||||
|
await createDatabase(db, dialect)
|
||||||
|
},
|
||||||
|
async disconnect() {
|
||||||
|
await db.destroy()
|
||||||
|
},
|
||||||
|
async user(userId) {
|
||||||
|
const user =
|
||||||
|
(await db
|
||||||
|
.selectFrom("User")
|
||||||
|
.selectAll()
|
||||||
|
.where("id", "=", userId)
|
||||||
|
.executeTakeFirst()) ?? null
|
||||||
|
if (datesStoredAsISOStrings && user?.emailVerified)
|
||||||
|
user.emailVerified = new Date(user.emailVerified)
|
||||||
|
return user
|
||||||
|
},
|
||||||
|
async account({ provider, providerAccountId }) {
|
||||||
|
const result = await db
|
||||||
|
.selectFrom("Account")
|
||||||
|
.selectAll()
|
||||||
|
.where("provider", "=", provider)
|
||||||
|
.where("providerAccountId", "=", providerAccountId)
|
||||||
|
.executeTakeFirst()
|
||||||
|
if (!result) return null
|
||||||
|
const { ...account } = result
|
||||||
|
if (typeof account.expires_at === "string")
|
||||||
|
account.expires_at = Number(account.expires_at)
|
||||||
|
return account
|
||||||
|
},
|
||||||
|
async session(sessionToken) {
|
||||||
|
const session =
|
||||||
|
(await db
|
||||||
|
.selectFrom("Session")
|
||||||
|
.selectAll()
|
||||||
|
.where("sessionToken", "=", sessionToken)
|
||||||
|
.executeTakeFirst()) ?? null
|
||||||
|
if (datesStoredAsISOStrings && session?.expires)
|
||||||
|
session.expires = new Date(session.expires)
|
||||||
|
return session
|
||||||
|
},
|
||||||
|
async verificationToken({ identifier, token }) {
|
||||||
|
const verificationToken = await db
|
||||||
|
.selectFrom("VerificationToken")
|
||||||
|
.selectAll()
|
||||||
|
.where("identifier", "=", identifier)
|
||||||
|
.where("token", "=", token)
|
||||||
|
.executeTakeFirstOrThrow()
|
||||||
|
if (datesStoredAsISOStrings)
|
||||||
|
verificationToken.expires = new Date(verificationToken.expires)
|
||||||
|
return verificationToken
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Testing PostgresDialect", () => {
|
||||||
|
const db = new KyselyAuth<Database>({
|
||||||
|
dialect: new PostgresDialect({
|
||||||
|
pool: new Pool(DIALECT_CONFIGS.postgres),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
runDialectBasicTests(db, "postgres")
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Testing MysqlDialect", () => {
|
||||||
|
const db = new KyselyAuth<Database>({
|
||||||
|
dialect: new MysqlDialect({
|
||||||
|
pool: createPool(DIALECT_CONFIGS.mysql),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
runDialectBasicTests(db, "mysql")
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Testing SqliteDialect", () => {
|
||||||
|
const db = new KyselyAuth<Database>({
|
||||||
|
dialect: new SqliteDialect({
|
||||||
|
database: async () =>
|
||||||
|
new SqliteDatabase(DIALECT_CONFIGS.sqlite.databasePath),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
runDialectBasicTests(db, "sqlite")
|
||||||
|
})
|
||||||
3
packages/adapter-kysely/tests/scripts/mysql-init.sql
Normal file
3
packages/adapter-kysely/tests/scripts/mysql-init.sql
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
CREATE USER 'kysely'@'%' IDENTIFIED WITH mysql_native_password BY 'kysely';
|
||||||
|
GRANT ALL ON *.* TO 'kysely'@'%';
|
||||||
|
CREATE DATABASE kysely_test;
|
||||||
30
packages/adapter-kysely/tests/test.sh
Executable file
30
packages/adapter-kysely/tests/test.sh
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
docker run -d \
|
||||||
|
--name mysql \
|
||||||
|
--rm \
|
||||||
|
-e MYSQL_ROOT_PASSWORD=root \
|
||||||
|
-e MYSQL_DATABASE=kysely_test \
|
||||||
|
-p 3308:3306 \
|
||||||
|
-v "$(pwd)"/tests/scripts/mysql-init.sql:/data/application/init.sql \
|
||||||
|
mysql/mysql-server \
|
||||||
|
--init-file /data/application/init.sql
|
||||||
|
|
||||||
|
docker run -d \
|
||||||
|
--name postgres \
|
||||||
|
--rm \
|
||||||
|
-e POSTGRES_DB=kysely_test \
|
||||||
|
-e POSTGRES_USER=kysely \
|
||||||
|
-e POSTGRES_HOST_AUTH_METHOD=trust \
|
||||||
|
-p 5434:5432 \
|
||||||
|
postgres
|
||||||
|
|
||||||
|
echo "waiting 15 seconds for databases to start..."
|
||||||
|
sleep 15
|
||||||
|
|
||||||
|
# Always stop container, but exit with 1 when tests are failing
|
||||||
|
if npx jest tests; then
|
||||||
|
docker stop mysql && docker stop postgres
|
||||||
|
else
|
||||||
|
docker stop mysql && docker stop postgres && exit 1
|
||||||
|
fi
|
||||||
25
packages/adapter-kysely/tsconfig.json
Normal file
25
packages/adapter-kysely/tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"extends": "@next-auth/tsconfig/tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"isolatedModules": true,
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"outDir": ".",
|
||||||
|
"rootDir": "src",
|
||||||
|
"skipDefaultLibCheck": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"stripInternal": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"declaration": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"*.js",
|
||||||
|
"*.d.ts",
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@auth/core",
|
"name": "@auth/core",
|
||||||
"version": "0.10.1",
|
"version": "0.10.2",
|
||||||
"description": "Authentication for the Web.",
|
"description": "Authentication for the Web.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"authentication",
|
"authentication",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
|
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
|
||||||
* <span>Built-in <b>VK</b> integration.</span>
|
* <span>Built-in <b>VK</b> integration.</span>
|
||||||
* <a href="www.vk.com/">
|
* <a href="https://vk.com/">
|
||||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/vk.svg" height="48" />
|
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/vk.svg" height="48" />
|
||||||
* </a>
|
* </a>
|
||||||
* </div>
|
* </div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
|
* <div style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
|
||||||
* <span>Built-in <b> Wikimedia</b> integration.</span>
|
* <span>Built-in <b> Wikimedia</b> integration.</span>
|
||||||
* <a href="www.mediawiki.org/">
|
* <a href="https://mediawiki.org/">
|
||||||
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/wikimedia.svg" height="48" />
|
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/wikimedia.svg" height="48" />
|
||||||
* </a>
|
* </a>
|
||||||
* </div>
|
* </div>
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
||||||
124
pnpm-lock.yaml
generated
124
pnpm-lock.yaml
generated
@@ -318,6 +318,31 @@ importers:
|
|||||||
firebase-tools: 11.16.1
|
firebase-tools: 11.16.1
|
||||||
jest: 29.3.1
|
jest: 29.3.1
|
||||||
|
|
||||||
|
packages/adapter-kysely:
|
||||||
|
specifiers:
|
||||||
|
'@auth/core': workspace:*
|
||||||
|
'@next-auth/adapter-test': workspace:*
|
||||||
|
'@next-auth/tsconfig': workspace:*
|
||||||
|
'@types/better-sqlite3': ^7.6.3
|
||||||
|
'@types/pg': ^8.6.5
|
||||||
|
better-sqlite3: ^8.2.0
|
||||||
|
jest: ^27.4.3
|
||||||
|
kysely: ^0.24.2
|
||||||
|
mysql2: ^3.2.0
|
||||||
|
pg: ^8.10.0
|
||||||
|
dependencies:
|
||||||
|
'@auth/core': link:../core
|
||||||
|
devDependencies:
|
||||||
|
'@next-auth/adapter-test': link:../adapter-test
|
||||||
|
'@next-auth/tsconfig': link:../tsconfig
|
||||||
|
'@types/better-sqlite3': 7.6.4
|
||||||
|
'@types/pg': 8.10.2
|
||||||
|
better-sqlite3: 8.5.0
|
||||||
|
jest: 27.5.1
|
||||||
|
kysely: 0.24.2
|
||||||
|
mysql2: 3.6.0
|
||||||
|
pg: 8.11.2
|
||||||
|
|
||||||
packages/adapter-mikro-orm:
|
packages/adapter-mikro-orm:
|
||||||
specifiers:
|
specifiers:
|
||||||
'@auth/core': workspace:*
|
'@auth/core': workspace:*
|
||||||
@@ -10139,6 +10164,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==}
|
resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/pg/8.10.2:
|
||||||
|
resolution: {integrity: sha512-MKFs9P6nJ+LAeHLU3V0cODEOgyThJ3OAnmOlsZsxux6sfQs3HRXR5bBn7xG5DjckEFhTAxsXi7k7cd0pCMxpJw==}
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 20.4.8
|
||||||
|
pg-protocol: 1.5.0
|
||||||
|
pg-types: 4.0.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/phoenix/1.5.4:
|
/@types/phoenix/1.5.4:
|
||||||
resolution: {integrity: sha512-L5eZmzw89eXBKkiqVBcJfU1QGx9y+wurRIEgt0cuLH0hwNtVUxtx+6cu0R2STwWj468sjXyBYPYDtGclUd1kjQ==}
|
resolution: {integrity: sha512-L5eZmzw89eXBKkiqVBcJfU1QGx9y+wurRIEgt0cuLH0hwNtVUxtx+6cu0R2STwWj468sjXyBYPYDtGclUd1kjQ==}
|
||||||
|
|
||||||
@@ -21590,6 +21623,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
|
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/kysely/0.24.2:
|
||||||
|
resolution: {integrity: sha512-+7eaTJNUYm2yRq1x+lEOZc+78TO35dTZ9b0dh49+Z9CTt2byMSbMiOKpwPlOyCAaHD4kILkAYWYZNywFlmBwRA==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/latest-version/5.1.0:
|
/latest-version/5.1.0:
|
||||||
resolution: {integrity: sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==}
|
resolution: {integrity: sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -23801,15 +23839,30 @@ packages:
|
|||||||
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
|
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/pg-cloudflare/1.1.1:
|
||||||
|
resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==}
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
/pg-connection-string/2.5.0:
|
/pg-connection-string/2.5.0:
|
||||||
resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==}
|
resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/pg-connection-string/2.6.2:
|
||||||
|
resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/pg-int8/1.0.1:
|
/pg-int8/1.0.1:
|
||||||
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
|
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
|
||||||
engines: {node: '>=4.0.0'}
|
engines: {node: '>=4.0.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/pg-numeric/1.0.2:
|
||||||
|
resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/pg-pool/3.5.1_pg@8.7.3:
|
/pg-pool/3.5.1_pg@8.7.3:
|
||||||
resolution: {integrity: sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ==}
|
resolution: {integrity: sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -23818,10 +23871,22 @@ packages:
|
|||||||
pg: 8.7.3
|
pg: 8.7.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/pg-pool/3.6.1_pg@8.11.2:
|
||||||
|
resolution: {integrity: sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==}
|
||||||
|
peerDependencies:
|
||||||
|
pg: '>=8.0'
|
||||||
|
dependencies:
|
||||||
|
pg: 8.11.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
/pg-protocol/1.5.0:
|
/pg-protocol/1.5.0:
|
||||||
resolution: {integrity: sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==}
|
resolution: {integrity: sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/pg-protocol/1.6.0:
|
||||||
|
resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/pg-types/2.2.0:
|
/pg-types/2.2.0:
|
||||||
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
|
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -23833,6 +23898,39 @@ packages:
|
|||||||
postgres-interval: 1.2.0
|
postgres-interval: 1.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/pg-types/4.0.1:
|
||||||
|
resolution: {integrity: sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
pg-int8: 1.0.1
|
||||||
|
pg-numeric: 1.0.2
|
||||||
|
postgres-array: 3.0.2
|
||||||
|
postgres-bytea: 3.0.0
|
||||||
|
postgres-date: 2.0.1
|
||||||
|
postgres-interval: 3.0.0
|
||||||
|
postgres-range: 1.1.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/pg/8.11.2:
|
||||||
|
resolution: {integrity: sha512-l4rmVeV8qTIrrPrIR3kZQqBgSN93331s9i6wiUiLOSk0Q7PmUxZD/m1rQI622l3NfqBby9Ar5PABfS/SulfieQ==}
|
||||||
|
engines: {node: '>= 8.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
pg-native: '>=3.0.1'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
pg-native:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
buffer-writer: 2.0.0
|
||||||
|
packet-reader: 1.0.0
|
||||||
|
pg-connection-string: 2.6.2
|
||||||
|
pg-pool: 3.6.1_pg@8.11.2
|
||||||
|
pg-protocol: 1.6.0
|
||||||
|
pg-types: 2.2.0
|
||||||
|
pgpass: 1.0.5
|
||||||
|
optionalDependencies:
|
||||||
|
pg-cloudflare: 1.1.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/pg/8.7.3:
|
/pg/8.7.3:
|
||||||
resolution: {integrity: sha512-HPmH4GH4H3AOprDJOazoIcpI49XFsHCe8xlrjHkWiapdbHK+HLtbm/GQzXYAZwmPju/kzKhjaSfMACG+8cgJcw==}
|
resolution: {integrity: sha512-HPmH4GH4H3AOprDJOazoIcpI49XFsHCe8xlrjHkWiapdbHK+HLtbm/GQzXYAZwmPju/kzKhjaSfMACG+8cgJcw==}
|
||||||
engines: {node: '>= 8.0.0'}
|
engines: {node: '>= 8.0.0'}
|
||||||
@@ -25032,16 +25130,33 @@ packages:
|
|||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/postgres-array/3.0.2:
|
||||||
|
resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/postgres-bytea/1.0.0:
|
/postgres-bytea/1.0.0:
|
||||||
resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==}
|
resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/postgres-bytea/3.0.0:
|
||||||
|
resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
dependencies:
|
||||||
|
obuf: 1.1.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
/postgres-date/1.0.7:
|
/postgres-date/1.0.7:
|
||||||
resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
|
resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/postgres-date/2.0.1:
|
||||||
|
resolution: {integrity: sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/postgres-interval/1.2.0:
|
/postgres-interval/1.2.0:
|
||||||
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
|
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -25049,6 +25164,15 @@ packages:
|
|||||||
xtend: 4.0.2
|
xtend: 4.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/postgres-interval/3.0.0:
|
||||||
|
resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/postgres-range/1.1.3:
|
||||||
|
resolution: {integrity: sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/postgres/3.3.5:
|
/postgres/3.3.5:
|
||||||
resolution: {integrity: sha512-+JD93VELV9gHkqpV5gdL5/70HdGtEw4/XE1S4BC8f1mcPmdib3K5XsKVbnR1XcAyC41zOnifJ+9YRKxdIsXiUw==}
|
resolution: {integrity: sha512-+JD93VELV9gHkqpV5gdL5/70HdGtEw4/XE1S4BC8f1mcPmdib3K5XsKVbnR1XcAyC41zOnifJ+9YRKxdIsXiUw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|||||||
46
turbo.json
46
turbo.json
@@ -2,12 +2,21 @@
|
|||||||
"$schema": "https://turborepo.org/schema.json",
|
"$schema": "https://turborepo.org/schema.json",
|
||||||
"pipeline": {
|
"pipeline": {
|
||||||
"build": {
|
"build": {
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": [
|
||||||
"outputs": ["dist/**/*", "*.js", "*.d.ts", "*.d.ts.map"],
|
"^build"
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
"dist/**/*",
|
||||||
|
"*.js",
|
||||||
|
"*.d.ts",
|
||||||
|
"*.d.ts.map"
|
||||||
|
],
|
||||||
"outputMode": "new-only"
|
"outputMode": "new-only"
|
||||||
},
|
},
|
||||||
"next-auth#build": {
|
"next-auth#build": {
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": [
|
||||||
|
"^build"
|
||||||
|
],
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"client/**",
|
"client/**",
|
||||||
"core/**",
|
"core/**",
|
||||||
@@ -22,7 +31,9 @@
|
|||||||
"outputMode": "new-only"
|
"outputMode": "new-only"
|
||||||
},
|
},
|
||||||
"@auth/core#build": {
|
"@auth/core#build": {
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": [
|
||||||
|
"^build"
|
||||||
|
],
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"lib/**",
|
"lib/**",
|
||||||
"providers/**",
|
"providers/**",
|
||||||
@@ -35,8 +46,14 @@
|
|||||||
"outputMode": "new-only"
|
"outputMode": "new-only"
|
||||||
},
|
},
|
||||||
"@auth/sveltekit#build": {
|
"@auth/sveltekit#build": {
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": [
|
||||||
"outputs": [".svelte-kit/**", "client.*", "index.*"],
|
"^build"
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
".svelte-kit/**",
|
||||||
|
"client.*",
|
||||||
|
"index.*"
|
||||||
|
],
|
||||||
"outputMode": "new-only"
|
"outputMode": "new-only"
|
||||||
},
|
},
|
||||||
"clean": {
|
"clean": {
|
||||||
@@ -49,20 +66,28 @@
|
|||||||
"outputMode": "new-only"
|
"outputMode": "new-only"
|
||||||
},
|
},
|
||||||
"e2e": {
|
"e2e": {
|
||||||
"outputs": ["playwright-report/**"]
|
"outputs": [
|
||||||
|
"playwright-report/**"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"@auth/upstash-redis-adapter#test": {
|
"@auth/upstash-redis-adapter#test": {
|
||||||
"env": ["UPSTASH_REDIS_KEY", "UPSTASH_REDIS_URL"]
|
"env": [
|
||||||
|
"UPSTASH_REDIS_KEY",
|
||||||
|
"UPSTASH_REDIS_URL"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"docs#dev": {
|
"docs#dev": {
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"@auth/core#build",
|
"@auth/core#build",
|
||||||
"@auth/prisma-adapter#build",
|
"@auth/prisma-adapter#build",
|
||||||
|
"@auth/solid-start#build",
|
||||||
"@auth/sveltekit#build",
|
"@auth/sveltekit#build",
|
||||||
"@auth/dgraph-adapter#build",
|
"@auth/dgraph-adapter#build",
|
||||||
|
"@auth/drizzle-adapter#build",
|
||||||
"@auth/dynamodb-adapter#build",
|
"@auth/dynamodb-adapter#build",
|
||||||
"@auth/fauna-adapter#build",
|
"@auth/fauna-adapter#build",
|
||||||
"@auth/firebase-adapter#build",
|
"@auth/firebase-adapter#build",
|
||||||
|
"@auth/kysely-adapter#build",
|
||||||
"@auth/mikro-orm-adapter#build",
|
"@auth/mikro-orm-adapter#build",
|
||||||
"@auth/mongodb-adapter#build",
|
"@auth/mongodb-adapter#build",
|
||||||
"@auth/neo4j-adapter#build",
|
"@auth/neo4j-adapter#build",
|
||||||
@@ -81,11 +106,14 @@
|
|||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"@auth/core#build",
|
"@auth/core#build",
|
||||||
"@auth/prisma-adapter#build",
|
"@auth/prisma-adapter#build",
|
||||||
|
"@auth/solid-start#build",
|
||||||
"@auth/sveltekit#build",
|
"@auth/sveltekit#build",
|
||||||
"@auth/dgraph-adapter#build",
|
"@auth/dgraph-adapter#build",
|
||||||
|
"@auth/drizzle-adapter#build",
|
||||||
"@auth/dynamodb-adapter#build",
|
"@auth/dynamodb-adapter#build",
|
||||||
"@auth/fauna-adapter#build",
|
"@auth/fauna-adapter#build",
|
||||||
"@auth/firebase-adapter#build",
|
"@auth/firebase-adapter#build",
|
||||||
|
"@auth/kysely-adapter#build",
|
||||||
"@auth/mikro-orm-adapter#build",
|
"@auth/mikro-orm-adapter#build",
|
||||||
"@auth/mongodb-adapter#build",
|
"@auth/mongodb-adapter#build",
|
||||||
"@auth/neo4j-adapter#build",
|
"@auth/neo4j-adapter#build",
|
||||||
@@ -107,4 +135,4 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user