mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
feat(adapters): add Kysely adapter (#5464)
* feat: kysely-adapter with PostgreSQL and MySQL support * feat: kysely-adapter with SQLite support * docs: add docs for kysely-adapter * chore: cleanup * chore: update adapter lists * chore: update column types * chore: remove pgcrypto install * chore: add indexes * chore: Object.assign and cleanup * feat: add AuthedKysely wrapper * docs: add Naming Conventions section * chore: add coerceReturnData to reduce repitition * chore: add coerceInputData to reduce repitition * chore: move AuthedKysely export to end * chore: cleanup * docs: remove unused import * feat: add support for using AuthedKysely with generated types from kysely-codegen * docs: formatting * chore: CodeGen --> Codegen * docs: wording update, ts Co-authored-by: Julius Marminge <julius0216@outlook.com> * chore: use latest kysely version, update model * docs: move content to source code * chore: update deps * chore: update logo location, add link in overview * chore: bump kysely version Co-authored-by: Igal Klebanov <igalklebanov@gmail.com> * chore: update docs Co-authored-by: Igal Klebanov <igalklebanov@gmail.com> * chore: update docs with links to new Kysely docs Co-authored-by: Jie Peng <dean.leehom@gmail.com> * feat: emailVerified shouldn't have a default Co-authored-by: Lars Graubner <lgraubner@users.noreply.github.com> * simplify, update code * add README.md * clean up docs * fix adapter name * add to turbo * fix test * revert some changes * test fixes --------- Co-authored-by: Julius Marminge <julius0216@outlook.com> Co-authored-by: Igal Klebanov <igalklebanov@gmail.com> Co-authored-by: Jie Peng <dean.leehom@gmail.com> Co-authored-by: Lars Graubner <lgraubner@users.noreply.github.com> Co-authored-by: Balázs Orbán <info@balazsorban.com>
This commit is contained in:
2
.github/ISSUE_TEMPLATE/3_bug_adapter.yml
vendored
2
.github/ISSUE_TEMPLATE/3_bug_adapter.yml
vendored
@@ -24,8 +24,10 @@ body:
|
||||
- "@auth/dgraph-adapter"
|
||||
- "@auth/drizzle-adapter"
|
||||
- "@auth/dynamodb-adapter"
|
||||
- "@auth/drizzle-adapter"
|
||||
- "@auth/fauna-adapter"
|
||||
- "@auth/firebase-adapter"
|
||||
- "@auth/kysely-adapter"
|
||||
- "@auth/mikro-orm-adapter"
|
||||
- "@auth/mongodb-adapter"
|
||||
- "@auth/neo4j-adapter"
|
||||
|
||||
3
.github/issue-labeler.yml
vendored
3
.github/issue-labeler.yml
vendored
@@ -15,6 +15,9 @@ fauna:
|
||||
firebase:
|
||||
- "@auth/firebase-adapter"
|
||||
|
||||
kysely:
|
||||
- "@auth/kysely-adapter"
|
||||
|
||||
mikro-orm:
|
||||
- "@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/**/*"]
|
||||
pouchdb: ["packages/adapter-pouchdb/**/*"]
|
||||
prisma: ["packages/adapter-prisma/**/*"]
|
||||
kysely: ["packages/adapter-kysely/**/*"]
|
||||
providers: ["packages/core/src/providers/**/*"]
|
||||
sequelize: ["packages/adapter-sequelize/**/*"]
|
||||
solidjs: ["packages/frameworks-solid-start/**/*"]
|
||||
|
||||
@@ -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" />
|
||||
<h4 class="adapter-card__title">Firebase Adapter</h4>
|
||||
</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">
|
||||
<img src="/img/adapters/mikro-orm.png" width="30" />
|
||||
<h4 class="adapter-card__title">Mikro ORM Adapter</h4>
|
||||
|
||||
@@ -269,6 +269,7 @@ const docusaurusConfig = {
|
||||
typedocAdapter("DynamoDB"),
|
||||
typedocAdapter("Fauna"),
|
||||
typedocAdapter("Firebase"),
|
||||
typedocAdapter("Kysely"),
|
||||
typedocAdapter("Mikro ORM"),
|
||||
typedocAdapter("MongoDB"),
|
||||
typedocAdapter("Neo4j"),
|
||||
|
||||
@@ -59,6 +59,7 @@ module.exports = {
|
||||
{ type: "doc", id: "reference/adapter/dynamodb/index" },
|
||||
{ type: "doc", id: "reference/adapter/fauna/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/mongodb/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 |
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.0.0",
|
||||
"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",
|
||||
]
|
||||
}
|
||||
124
pnpm-lock.yaml
generated
124
pnpm-lock.yaml
generated
@@ -318,6 +318,31 @@ importers:
|
||||
firebase-tools: 11.16.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:
|
||||
specifiers:
|
||||
'@auth/core': workspace:*
|
||||
@@ -10139,6 +10164,14 @@ packages:
|
||||
resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==}
|
||||
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:
|
||||
resolution: {integrity: sha512-L5eZmzw89eXBKkiqVBcJfU1QGx9y+wurRIEgt0cuLH0hwNtVUxtx+6cu0R2STwWj468sjXyBYPYDtGclUd1kjQ==}
|
||||
|
||||
@@ -21590,6 +21623,11 @@ packages:
|
||||
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
|
||||
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:
|
||||
resolution: {integrity: sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -23801,15 +23839,30 @@ packages:
|
||||
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
|
||||
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:
|
||||
resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==}
|
||||
dev: true
|
||||
|
||||
/pg-connection-string/2.6.2:
|
||||
resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==}
|
||||
dev: true
|
||||
|
||||
/pg-int8/1.0.1:
|
||||
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
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:
|
||||
resolution: {integrity: sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ==}
|
||||
peerDependencies:
|
||||
@@ -23818,10 +23871,22 @@ packages:
|
||||
pg: 8.7.3
|
||||
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:
|
||||
resolution: {integrity: sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==}
|
||||
dev: true
|
||||
|
||||
/pg-protocol/1.6.0:
|
||||
resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==}
|
||||
dev: true
|
||||
|
||||
/pg-types/2.2.0:
|
||||
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -23833,6 +23898,39 @@ packages:
|
||||
postgres-interval: 1.2.0
|
||||
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:
|
||||
resolution: {integrity: sha512-HPmH4GH4H3AOprDJOazoIcpI49XFsHCe8xlrjHkWiapdbHK+HLtbm/GQzXYAZwmPju/kzKhjaSfMACG+8cgJcw==}
|
||||
engines: {node: '>= 8.0.0'}
|
||||
@@ -25032,16 +25130,33 @@ packages:
|
||||
engines: {node: '>=4'}
|
||||
dev: true
|
||||
|
||||
/postgres-array/3.0.2:
|
||||
resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==}
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
/postgres-bytea/1.0.0:
|
||||
resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
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:
|
||||
resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/postgres-date/2.0.1:
|
||||
resolution: {integrity: sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==}
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
/postgres-interval/1.2.0:
|
||||
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -25049,6 +25164,15 @@ packages:
|
||||
xtend: 4.0.2
|
||||
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:
|
||||
resolution: {integrity: sha512-+JD93VELV9gHkqpV5gdL5/70HdGtEw4/XE1S4BC8f1mcPmdib3K5XsKVbnR1XcAyC41zOnifJ+9YRKxdIsXiUw==}
|
||||
dev: true
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
"@auth/dynamodb-adapter#build",
|
||||
"@auth/fauna-adapter#build",
|
||||
"@auth/firebase-adapter#build",
|
||||
"@auth/kysely-adapter#build",
|
||||
"@auth/mikro-orm-adapter#build",
|
||||
"@auth/mongodb-adapter#build",
|
||||
"@auth/neo4j-adapter#build",
|
||||
@@ -88,6 +89,7 @@
|
||||
"@auth/dynamodb-adapter#build",
|
||||
"@auth/fauna-adapter#build",
|
||||
"@auth/firebase-adapter#build",
|
||||
"@auth/kysely-adapter#build",
|
||||
"@auth/mikro-orm-adapter#build",
|
||||
"@auth/mongodb-adapter#build",
|
||||
"@auth/neo4j-adapter#build",
|
||||
@@ -109,4 +111,4 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user