diff --git a/.auri/$160srass.md b/.auri/$160srass.md
new file mode 100644
index 000000000..871132ce2
--- /dev/null
+++ b/.auri/$160srass.md
@@ -0,0 +1,8 @@
+---
+package: "@lucia-auth/oauth" # package name
+type: "minor" # "major", "minor", "patch"
+---
+
+Require `lucia@^2.0.0`
+ - Export `useAuth()`
+ - Remove `provider()`
\ No newline at end of file
diff --git a/.auri/$1gd2c9ca.md b/.auri/$1gd2c9ca.md
new file mode 100644
index 000000000..c7fca6210
--- /dev/null
+++ b/.auri/$1gd2c9ca.md
@@ -0,0 +1,15 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Update configuration
+ - Remove `autoDatabaseCleanup`
+ - Remove `transformDatabaseUser()` (see `transformUserAttributes()`)
+ - Replace `generateCustomUserId()` with `generateUserId()`
+ - Replace `hash` with `passwordHash`
+ - Replace `origin` with `requestOrigins`
+ - Replace `sessionCookie` with `sessionCookie.attributes`
+ - Add `sessionCookie.name` for setting session cookie name
+ - Add `transformUserAttributes()` for defining user attributes (**`userId` is automatically included**)
+ - Add `transformSessionAttributes()` for defining session attributes
\ No newline at end of file
diff --git a/.auri/$3i5r5bib.md b/.auri/$3i5r5bib.md
new file mode 100644
index 000000000..6e1990ac4
--- /dev/null
+++ b/.auri/$3i5r5bib.md
@@ -0,0 +1,11 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Update `Auth` methods:
+ - Remove `getSessionUser()`
+ - Remove `validateSessionUser()`
+ - Remove `parseRequestHeaders()`
+ - Add `readSessionCookie()`
+ - Add `validateRequestOrigin()`
\ No newline at end of file
diff --git a/.auri/$4g93h2e1.md b/.auri/$4g93h2e1.md
new file mode 100644
index 000000000..0067e4ee8
--- /dev/null
+++ b/.auri/$4g93h2e1.md
@@ -0,0 +1,8 @@
+---
+package: "lucia" # package name
+type: "minor" # "major", "minor", "patch"
+---
+
+Support bearer tokens
+ - Add `Auth.readBearerToken()`
+ - Add `AuthRequest.validateBearerToken()`
\ No newline at end of file
diff --git a/.auri/$4omphity.md b/.auri/$4omphity.md
new file mode 100644
index 000000000..fcd9f02ab
--- /dev/null
+++ b/.auri/$4omphity.md
@@ -0,0 +1,9 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Remove primary keys
+ - Remove `Key.primary`
+ - Rename `Auth.createUser()` params `options.primaryKey` to `options.key`
+ - Remove column `key(primary_key)`
\ No newline at end of file
diff --git a/.auri/$5gbqip09.md b/.auri/$5gbqip09.md
new file mode 100644
index 000000000..b6b35ab43
--- /dev/null
+++ b/.auri/$5gbqip09.md
@@ -0,0 +1,6 @@
+---
+package: "@lucia-auth/adapter-sqlite" # package name
+type: "minor" # "major", "minor", "patch"
+---
+
+Add option to configure table names in `betterSqlite3()` and `d1()`
\ No newline at end of file
diff --git a/.auri/$5xnm1vze.md b/.auri/$5xnm1vze.md
new file mode 100644
index 000000000..9c631befd
--- /dev/null
+++ b/.auri/$5xnm1vze.md
@@ -0,0 +1,6 @@
+---
+package: "@lucia-auth/adapter-mysql" # package name
+type: "minor" # "major", "minor", "patch"
+---
+
+Add option to configure table names in `mysql2()` and `planetscale()`
\ No newline at end of file
diff --git a/.auri/$8fe25uuj.md b/.auri/$8fe25uuj.md
new file mode 100644
index 000000000..bc261f60c
--- /dev/null
+++ b/.auri/$8fe25uuj.md
@@ -0,0 +1,6 @@
+---
+package: "@lucia-auth/adapter-postgresql" # package name
+type: "minor" # "major", "minor", "patch"
+---
+
+Add option to configure table names in `pg()`
\ No newline at end of file
diff --git a/.auri/$97z0unep.md b/.auri/$97z0unep.md
new file mode 100644
index 000000000..7e5d360d1
--- /dev/null
+++ b/.auri/$97z0unep.md
@@ -0,0 +1,6 @@
+---
+package: "@lucia-auth/oauth" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Remove `redirectUri` from `getAuthorizationUrl()`
\ No newline at end of file
diff --git a/.auri/$awt53nzt.md b/.auri/$awt53nzt.md
new file mode 100644
index 000000000..c6fdf2b0e
--- /dev/null
+++ b/.auri/$awt53nzt.md
@@ -0,0 +1,7 @@
+---
+package: "@lucia-auth/adapter-test" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Require `lucia@^2.0.0`
+ - Update tests
\ No newline at end of file
diff --git a/.auri/$buyqgbd1.md b/.auri/$buyqgbd1.md
new file mode 100644
index 000000000..bad6d1478
--- /dev/null
+++ b/.auri/$buyqgbd1.md
@@ -0,0 +1,9 @@
+---
+package: "@lucia-auth/adapter-test" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Update `testAdapter()` and `testSessionAdapter()`
+ - Rename type `QueryHandler` to `LuciaQueryHandler`
+ - Remove `testUserAdapter()`
+ - Test modules no longer end process by default
\ No newline at end of file
diff --git a/.auri/$cadjkp1x.md b/.auri/$cadjkp1x.md
new file mode 100644
index 000000000..7c6510ba7
--- /dev/null
+++ b/.auri/$cadjkp1x.md
@@ -0,0 +1,10 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Remove single use keys
+ - **Lucia v2 no longer supports `@lucia-auth/tokens`**
+ - Remove `Session.type`
+ - Update `Auth.createKey()` params
+ - Remove column `key(expires)`
\ No newline at end of file
diff --git a/.auri/$cvfrkv1s.md b/.auri/$cvfrkv1s.md
new file mode 100644
index 000000000..5e6dd7010
--- /dev/null
+++ b/.auri/$cvfrkv1s.md
@@ -0,0 +1,8 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Update `Session`
+ - Remove `Session.userId`
+ - Add `Session.user`
\ No newline at end of file
diff --git a/.auri/$ecvf1yna.md b/.auri/$ecvf1yna.md
new file mode 100644
index 000000000..8d35d5588
--- /dev/null
+++ b/.auri/$ecvf1yna.md
@@ -0,0 +1,6 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Remove `AuthRequest.validateUser()`
\ No newline at end of file
diff --git a/.auri/$eyl1lq81.md b/.auri/$eyl1lq81.md
new file mode 100644
index 000000000..c3968318c
--- /dev/null
+++ b/.auri/$eyl1lq81.md
@@ -0,0 +1,7 @@
+---
+package: "@lucia-auth/adapter-session-redis" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Require `lucia@^2.0.0`
+ - `redis()` expects az single Redis instance instead of 2
\ No newline at end of file
diff --git a/.auri/$fgtw8yla.md b/.auri/$fgtw8yla.md
new file mode 100644
index 000000000..1d769b099
--- /dev/null
+++ b/.auri/$fgtw8yla.md
@@ -0,0 +1,8 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Introduce custom session attributes
+ - Update `Auth.createSession()` params
+ - Update behavior of `Auth.renewSession()` to include attributes of old session to renewed session automatically
\ No newline at end of file
diff --git a/.auri/$fozb3rpo.md b/.auri/$fozb3rpo.md
new file mode 100644
index 000000000..2cc79de5b
--- /dev/null
+++ b/.auri/$fozb3rpo.md
@@ -0,0 +1,18 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Overhaul adapter API
+ - Remove `UserAdapter.updateUserAttributes()`
+ - Remove `UserAdapter.deleteNonPrimaryKey()`
+ - Remove `UserAdapter.updateKeyPassword()`
+ - Remove `Adapter?.getSessionAndUserBySessionId()`
+ - Update `UserAdapter.setUser()` params
+ - Remove `UserAdapter.getKey()` params `shouldDataBeDeleted()`
+ - Add `UserAdapter.updateUser()`
+ - Add `UserAdapter.deleteKey()`
+ - Add `UserAdapter.updateKey()`
+ - Add `SessionAdapter.updateSession()`
+ - Add `Adapter.getSessionAndUser()`
+ - Rename type `AdapterFunction` to `InitializeAdapter`
\ No newline at end of file
diff --git a/.auri/$jey1qi9j.md b/.auri/$jey1qi9j.md
new file mode 100644
index 000000000..8f04f1719
--- /dev/null
+++ b/.auri/$jey1qi9j.md
@@ -0,0 +1,9 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Update adapter specifications
+ - Insert and update methods do not return anything
+ - Insert and update methods for sessions and keys may optionally throw a Lucia error on invalid user id
+ - Insert methods do not throw Lucia errors on duplicate session and user ids
\ No newline at end of file
diff --git a/.auri/$jhfpjptb.md b/.auri/$jhfpjptb.md
new file mode 100644
index 000000000..3d985b57c
--- /dev/null
+++ b/.auri/$jhfpjptb.md
@@ -0,0 +1,9 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Remove errors:
+ - `AUTH_DUPLICATE_SESSION_ID`
+ - `AUTO_USER_ID_GENERATION_NOT_SUPPORTED`
+ - `AUTH_EXPIRED_KEY`
\ No newline at end of file
diff --git a/.auri/$msq2k30f.md b/.auri/$msq2k30f.md
new file mode 100644
index 000000000..a3ce54797
--- /dev/null
+++ b/.auri/$msq2k30f.md
@@ -0,0 +1,6 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Remove auto database clean up functionality
\ No newline at end of file
diff --git a/.auri/$oba8uk7e.md b/.auri/$oba8uk7e.md
new file mode 100644
index 000000000..ad70a6028
--- /dev/null
+++ b/.auri/$oba8uk7e.md
@@ -0,0 +1,6 @@
+---
+package: "@lucia-auth/adapter-sqlite" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Require `lucia@^2.0.0`
\ No newline at end of file
diff --git a/.auri/$osupufo1.md b/.auri/$osupufo1.md
new file mode 100644
index 000000000..3b7972859
--- /dev/null
+++ b/.auri/$osupufo1.md
@@ -0,0 +1,8 @@
+---
+package: "major" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Update `Lucia` namespace
+ - Rename `Lucia.UserAttributes` to `Lucia.DatabaseUserAttributes`
+ - Add `Lucia.DatabaseSessionAttributes`
\ No newline at end of file
diff --git a/.auri/$oto9r3vz.md b/.auri/$oto9r3vz.md
new file mode 100644
index 000000000..52d0cbbdf
--- /dev/null
+++ b/.auri/$oto9r3vz.md
@@ -0,0 +1,6 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Update `Middleware` takes a new `Context` params
\ No newline at end of file
diff --git a/.auri/$q972t1ak.md b/.auri/$q972t1ak.md
new file mode 100644
index 000000000..30e579a34
--- /dev/null
+++ b/.auri/$q972t1ak.md
@@ -0,0 +1,6 @@
+---
+package: "@lucia-auth/adapter-mysql" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Require `lucia@^2.0.0`
\ No newline at end of file
diff --git a/.auri/$qcdnw329.md b/.auri/$qcdnw329.md
new file mode 100644
index 000000000..e3aeeceb3
--- /dev/null
+++ b/.auri/$qcdnw329.md
@@ -0,0 +1,10 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Update exports:
+ - **Replace default export with named `lucia()`**
+ - Removed `generateRandomString()`
+ - Removed `serializeCookie()`
+ - Removed `Cookie`
\ No newline at end of file
diff --git a/.auri/$rlqdhur2.md b/.auri/$rlqdhur2.md
new file mode 100644
index 000000000..753af4f36
--- /dev/null
+++ b/.auri/$rlqdhur2.md
@@ -0,0 +1,6 @@
+---
+package: "@lucia-auth/adapter-postgresql" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Require `lucia@^2.0.0`
\ No newline at end of file
diff --git a/.auri/$sbxeojc3.md b/.auri/$sbxeojc3.md
new file mode 100644
index 000000000..753c67549
--- /dev/null
+++ b/.auri/$sbxeojc3.md
@@ -0,0 +1,6 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Rename `SESSION_COOKIE_NAME` to `DEFAULT_SESSION_COOKIE_NAME`
\ No newline at end of file
diff --git a/.auri/$sc875rn3.md b/.auri/$sc875rn3.md
new file mode 100644
index 000000000..e7ed40170
--- /dev/null
+++ b/.auri/$sc875rn3.md
@@ -0,0 +1,9 @@
+---
+package: "lucia" # package name
+type: "minor" # "major", "minor", "patch"
+---
+
+New `lucia/utils` export:
+ - `generateRandomString()`
+ - `serializeCookie()`
+ - `isWithinExpiration()`
\ No newline at end of file
diff --git a/.auri/$tt1sakae.md b/.auri/$tt1sakae.md
new file mode 100644
index 000000000..7e7e09f99
--- /dev/null
+++ b/.auri/$tt1sakae.md
@@ -0,0 +1,8 @@
+---
+package: "@lucia-auth/adapter-mongoose" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Require `lucia@^2.0.0`
+ - Export adapter as named exports (`mongoose()`)
+ - Update adapter params
\ No newline at end of file
diff --git a/.auri/$w6gqe7de.md b/.auri/$w6gqe7de.md
new file mode 100644
index 000000000..07329ad88
--- /dev/null
+++ b/.auri/$w6gqe7de.md
@@ -0,0 +1,6 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+**NPM package `lucia-auth` is renamed to `lucia`**
\ No newline at end of file
diff --git a/.auri/$xwujxvj7.md b/.auri/$xwujxvj7.md
new file mode 100644
index 000000000..0174015e2
--- /dev/null
+++ b/.auri/$xwujxvj7.md
@@ -0,0 +1,8 @@
+---
+package: "@lucia-auth/adapter-prisma" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Require `lucia@^2.0.0`
+ - Export adapter as named exports (`prisma()`)
+ - Update adapter params
\ No newline at end of file
diff --git a/.auri/$yk3hou53.md b/.auri/$yk3hou53.md
new file mode 100644
index 000000000..53508436f
--- /dev/null
+++ b/.auri/$yk3hou53.md
@@ -0,0 +1,8 @@
+---
+package: "lucia" # package name
+type: "major" # "major", "minor", "patch"
+---
+
+Update `RequestContext`:
+ - Add `RequestContext.headers.authorization`
+ - Add optional `RequestContext.storedSessionCookie`
\ No newline at end of file
diff --git a/.auri/release.config.json b/.auri/release.config.json
new file mode 100644
index 000000000..f73f6bd53
--- /dev/null
+++ b/.auri/release.config.json
@@ -0,0 +1,3 @@
+{
+ "stage": "beta"
+}
\ No newline at end of file
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 50b837a23..c03491287 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -6,7 +6,8 @@ module.exports = {
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-empty-interface": "off",
- "no-async-promise-executor": "off"
+ "no-async-promise-executor": "off",
+ "no-useless-catch": "off"
},
parser: "@typescript-eslint/parser",
extends: [
diff --git a/documentation/content/main/adapters/d1.md b/documentation/content/main/adapters/d1.md
index b1453d340..5e182569f 100644
--- a/documentation/content/main/adapters/d1.md
+++ b/documentation/content/main/adapters/d1.md
@@ -31,7 +31,7 @@ yarn add @lucia-auth/adapter-sqlite
Since the database instance is bound to the request, Lucia and the adapter must be initialized on a per-request basis.
```ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { d1 } from "@lucia-auth/adapter-sqlite";
import type { D1Database } from "@cloudflare/workers-types";
diff --git a/documentation/content/main/adapters/drizzle.md b/documentation/content/main/adapters/drizzle.md
index c9a178e97..1f2c21f3f 100644
--- a/documentation/content/main/adapters/drizzle.md
+++ b/documentation/content/main/adapters/drizzle.md
@@ -71,7 +71,7 @@ Refer to the [`mysql2`](/adapters/mysql#mysql2) section.
```ts
import mysql from "mysql2/promise";
import { drizzle } from "drizzle-orm/mysql2";
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { mysql2 } from "@lucia-auth/adapter-mysql";
const connectionPool = mysql.createPool({
@@ -93,7 +93,7 @@ Refer to the [`planetscale`](/adapters/mysql#planetscale) section.
```ts
import { connect } from "@planetscale/database";
import { drizzle } from "drizzle-orm/planetscale";
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { planetscale } from "@lucia-auth/adapter-mysql";
const connection = connect({
@@ -163,7 +163,7 @@ Refer to the [`pg`](/adapters/postgresql#pg) section.
```ts
import postgres from "pg";
import { drizzle } from "drizzle-orm/node-postgres";
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { pg } from "@lucia-auth/adapter-postgresql";
const connectionPool = new postgres.Pool({
@@ -216,7 +216,7 @@ Refer to the [`better-sqlite3`](/adapters/sqlite#better-sqlite3) section.
```ts
import sqlite from "better-sqlite3";
import { drizzle } from "drizzle-orm/better-sqlite3";
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { betterSqlite3 } from "@lucia-auth/adapter-sqlite";
const database = sqlite(pathToDbFile);
diff --git a/documentation/content/main/adapters/kysely.md b/documentation/content/main/adapters/kysely.md
index 8f507eac9..a9b2b3bb7 100644
--- a/documentation/content/main/adapters/kysely.md
+++ b/documentation/content/main/adapters/kysely.md
@@ -47,7 +47,7 @@ Refer to the [MySQL](/adapters/mysql) page for the schema and other details.
```ts
import mysql from "mysql2/promise";
import { Kysely, MysqlDialect } from "kysely";
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { mysql2 } from "@lucia-auth/adapter-mysql";
const connectionPool = mysql.createPool({
@@ -81,7 +81,7 @@ yarn add @planetscale/database kysely-planetscale
import { connect } from "@planetscale/database";
import { Kysely } from "kysely";
import { PlanetScaleDialect } from "kysely-planetscale";
-import { lucia } from "lucia-auth";
+import { lucia } from "lucia";
import { planetscale } from "@lucia-auth/adapter-mysql";
const dbConfig = {
@@ -132,7 +132,7 @@ Refer to the [PostgreSQL](/adapters/postgresql) page for the schema and other de
```ts
import postgres from "pg";
import { Kysely, PostgresDialect } from "kysely";
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { pg } from "@lucia-auth/adapter-postgresql";
const connectionPool = new postgres.Pool({
@@ -183,7 +183,7 @@ Refer to the [SQLite](/adapters/sqlite) page for the schema and other details.
```ts
import sqlite from "better-sqlite3";
import { Kysely, SqliteDialect } from "kysely";
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { betterSqlite3 } from "@lucia-auth/adapter-sqlite";
const database = sqlite(pathToDbFile);
diff --git a/documentation/content/main/adapters/redis.md b/documentation/content/main/adapters/redis.md
index 71e04755e..336b67dfc 100644
--- a/documentation/content/main/adapters/redis.md
+++ b/documentation/content/main/adapters/redis.md
@@ -38,7 +38,7 @@ You will need to set up a different adapter for storing users.
```ts
// lucia.js
-import lucia from "lucia-auth";
+import lucia from "lucia";
import redis from "@lucia-auth/adapter-session-redis";
import prisma from "@lucia-auth/adapter-prisma";
import { createClient } from "redis";
diff --git a/documentation/content/main/basics/error-handling.md b/documentation/content/main/basics/error-handling.md
index cc9c88eb5..f7baca6b5 100644
--- a/documentation/content/main/basics/error-handling.md
+++ b/documentation/content/main/basics/error-handling.md
@@ -9,7 +9,7 @@ Errors are handled by throwing [`LuciaError`](/reference/lucia-auth/luciaerror)
Using a try-catch block, the error message can be read like so:
```ts
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
import { auth } from "./lucia.js";
try {
diff --git a/documentation/content/main/basics/handle-requests.astro.md b/documentation/content/main/basics/handle-requests.astro.md
index 1399d4385..be8f6dbb9 100644
--- a/documentation/content/main/basics/handle-requests.astro.md
+++ b/documentation/content/main/basics/handle-requests.astro.md
@@ -45,7 +45,7 @@ The middleware can be configured with the [`middleware`](/basics/configuration#m
```ts
import { astro } from "lucia-auth/middleware";
-import lucia from "lucia-auth";
+import lucia from "lucia";
const auth = lucia({
middleware: astro()
diff --git a/documentation/content/main/basics/handle-requests.md b/documentation/content/main/basics/handle-requests.md
index a7eeffbbf..56f331031 100644
--- a/documentation/content/main/basics/handle-requests.md
+++ b/documentation/content/main/basics/handle-requests.md
@@ -35,7 +35,7 @@ The middleware can be configured with the [`middleware`](/basics/configuration#m
```ts
import { lucia as luciaMiddleware } from "lucia-auth/middleware";
-import lucia from "lucia-auth";
+import lucia from "lucia";
const auth = lucia({
middleware: luciaMiddleware()
diff --git a/documentation/content/main/basics/handle-requests.nextjs.md b/documentation/content/main/basics/handle-requests.nextjs.md
index 4a7809bb1..29694d937 100644
--- a/documentation/content/main/basics/handle-requests.nextjs.md
+++ b/documentation/content/main/basics/handle-requests.nextjs.md
@@ -75,7 +75,7 @@ The middleware can be configured with the [`middleware`](/basics/configuration#m
```ts
import { nextjs } from "lucia-auth/middleware";
-import lucia from "lucia-auth";
+import lucia from "lucia";
const auth = lucia({
middleware: nextjs()
diff --git a/documentation/content/main/basics/handle-requests.nuxt.md b/documentation/content/main/basics/handle-requests.nuxt.md
index 285eb2cd3..5090055b9 100644
--- a/documentation/content/main/basics/handle-requests.nuxt.md
+++ b/documentation/content/main/basics/handle-requests.nuxt.md
@@ -36,7 +36,7 @@ The middleware can be configured with the [`middleware`](/basics/configuration#m
```ts
import { h3 } from "lucia-auth/middleware";
-import lucia from "lucia-auth";
+import lucia from "lucia";
const auth = lucia({
middleware: h3()
diff --git a/documentation/content/main/basics/handle-requests.qwik.md b/documentation/content/main/basics/handle-requests.qwik.md
index 9f2a2c7cd..dbc953cfb 100644
--- a/documentation/content/main/basics/handle-requests.qwik.md
+++ b/documentation/content/main/basics/handle-requests.qwik.md
@@ -49,7 +49,7 @@ The middleware can be configured with the [`middleware`](/basics/configuration#m
```ts
import { qwik } from "lucia-auth/middleware";
-import lucia from "lucia-auth";
+import lucia from "lucia";
const auth = lucia({
middleware: qwik()
diff --git a/documentation/content/main/basics/handle-requests.remix.md b/documentation/content/main/basics/handle-requests.remix.md
index 6e3773367..d0b9cbd83 100644
--- a/documentation/content/main/basics/handle-requests.remix.md
+++ b/documentation/content/main/basics/handle-requests.remix.md
@@ -41,7 +41,7 @@ The middleware can be configured with the [`middleware`](/basics/configuration#m
```ts
import { web } from "lucia-auth/middleware";
-import lucia from "lucia-auth";
+import lucia from "lucia";
const auth = lucia({
middleware: web()
diff --git a/documentation/content/main/basics/handle-requests.sveltekit.md b/documentation/content/main/basics/handle-requests.sveltekit.md
index 89fa147ba..2283dd894 100644
--- a/documentation/content/main/basics/handle-requests.sveltekit.md
+++ b/documentation/content/main/basics/handle-requests.sveltekit.md
@@ -61,7 +61,7 @@ The middleware can be configured with the [`middleware`](/basics/configuration#m
```ts
import { sveltekit } from "lucia-auth/middleware";
-import lucia from "lucia-auth";
+import lucia from "lucia";
const auth = lucia({
middleware: sveltekit()
diff --git a/documentation/content/main/basics/sessions.md b/documentation/content/main/basics/sessions.md
index 79b61fa46..91fd84ef8 100644
--- a/documentation/content/main/basics/sessions.md
+++ b/documentation/content/main/basics/sessions.md
@@ -96,7 +96,7 @@ try {
Alternatively, you can read the cookie directly. The cookie name is provided as a `SESSION_COOKIE_NAME` constant. Make sure to implement your own CSRF protection in this case.
```ts
-import { SESSION_COOKIE_NAME } from "lucia-auth";
+import { SESSION_COOKIE_NAME } from "lucia";
import { auth } from "./lucia.js";
const sessionId = getCookie(SESSION_COOKIE_NAME);
diff --git a/documentation/content/main/basics/user-attributes.md b/documentation/content/main/basics/user-attributes.md
index c946b7608..bf9d11562 100644
--- a/documentation/content/main/basics/user-attributes.md
+++ b/documentation/content/main/basics/user-attributes.md
@@ -40,7 +40,7 @@ Add the column names and the value type inside `Lucia.UserAttributes`:
```ts
// app.d.ts
-///
+///
declare namespace Lucia {
// ...
type UserAttributes = {
@@ -89,7 +89,7 @@ This should be typed in `Lucia.UserAttributes`:
```ts
// app.d.ts
-///
+///
declare namespace Lucia {
// ...
interface UserAttributes {
diff --git a/documentation/content/main/basics/users.md b/documentation/content/main/basics/users.md
index a7c23e5e6..8fe7c7b08 100644
--- a/documentation/content/main/basics/users.md
+++ b/documentation/content/main/basics/users.md
@@ -179,7 +179,7 @@ You can generate your own user ids by setting [`generateCustomUserId()`](/basics
If you need to generate a cryptographically random alphanumeric string, Lucia provides [`generateRandomString()`](/reference/lucia-auth/lucia-auth#generaterandomstring). This function is based on the [`nanoid`](https://github.com/ai/nanoid) package.
```ts
-import { generateCustomUserId } from "lucia-auth";
+import { generateCustomUserId } from "lucia";
lucia({
generateCustomUserId: () => {
diff --git a/documentation/content/main/custom-adapters/testing-adapters.md b/documentation/content/main/custom-adapters/testing-adapters.md
index c636a76aa..3d74aecb5 100644
--- a/documentation/content/main/custom-adapters/testing-adapters.md
+++ b/documentation/content/main/custom-adapters/testing-adapters.md
@@ -86,7 +86,7 @@ Import one of the three testing function and provide both the adapter and query
```ts
import { testAdapter } from "@lucia-auth/adapter-test";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
const adapter = adapterKysely()(LuciaError);
await testAdapter(adapter, queryHandler);
diff --git a/documentation/content/main/nextjs.nextjs/username-password-example-app-router.md b/documentation/content/main/nextjs.nextjs/username-password-example-app-router.md
index 469b768a8..3a5d8af1b 100644
--- a/documentation/content/main/nextjs.nextjs/username-password-example-app-router.md
+++ b/documentation/content/main/nextjs.nextjs/username-password-example-app-router.md
@@ -31,7 +31,7 @@ In `lucia.d.ts`, add `username` in `UserAttributes` since we added `username` co
```ts
// lucia.d.ts
-///
+///
declare namespace Lucia {
type Auth = import("$lib/server/lucia.js").Auth;
type UserAttributes = {
@@ -160,7 +160,7 @@ Users can be created with `createUser()`. This will create a new primary key tha
```ts
// app/api/signup/route.ts
import { auth } from "@/auth/lucia";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
import { Prisma } from "@prisma/client";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
@@ -300,7 +300,7 @@ We’ll use the key created in the previous section to reference the user and au
import { auth } from "@/auth/lucia";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
export const POST = async (request: Request) => {
const { username, password } = await request.json();
diff --git a/documentation/content/main/start-here/getting-started.astro.md b/documentation/content/main/start-here/getting-started.astro.md
index 61cb4562a..025d3a73f 100644
--- a/documentation/content/main/start-here/getting-started.astro.md
+++ b/documentation/content/main/start-here/getting-started.astro.md
@@ -34,7 +34,7 @@ In `src/lib/lucia.ts`, import [`lucia`](/reference/lucia-auth/auth) from `lucia-
```ts
// src/lib/lucia.ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import prisma from "@lucia-auth/adapter-prisma";
import { PrismaClient } from "@prisma/client";
import { astro } from "lucia-auth/middleware";
@@ -71,7 +71,7 @@ Create `src/lucia.d.ts`, and inside it configure your types. The path in `import
```ts
// src/lucia.d.ts
-///
+///
declare namespace Lucia {
type Auth = import("./lib/lucia.js").Auth;
type UserAttributes = {};
@@ -84,7 +84,7 @@ declare namespace Lucia {
```ts
// auth/lucia.ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import "lucia-auth/polyfill/node";
// ...
diff --git a/documentation/content/main/start-here/getting-started.md b/documentation/content/main/start-here/getting-started.md
index a9df5ca5a..cc85a3561 100644
--- a/documentation/content/main/start-here/getting-started.md
+++ b/documentation/content/main/start-here/getting-started.md
@@ -44,7 +44,7 @@ In a TypeScript file, import [`lucia`](/reference/lucia-auth/auth) and an adapte
```ts
// lucia.ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import prisma from "@lucia-auth/adapter-prisma";
import { PrismaClient } from "@prisma/client";
@@ -63,7 +63,7 @@ This module **should NOT be imported from the client**.
If you're using Node as is for handling requests, use the [Node middleware](/reference/lucia-auth/middleware#node):
```ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { node } from "lucia-auth/middleware";
// ...
@@ -78,7 +78,7 @@ export const auth = lucia({
If you are using Express for handling requests, use the [Express middleware](/reference/lucia-auth/middleware#express):
```ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { express } from "lucia-auth/middleware";
// ...
@@ -93,7 +93,7 @@ export const auth = lucia({
And, if you're dealing with the standard `Request`/`Response`, use the [Web middleware](/reference/lucia-auth/middleware#web):
```ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { web } from "lucia-auth/middleware";
// ...
@@ -111,7 +111,7 @@ In a TypeScript declaration file (`.d.ts`), declare a `Lucia` namespace. The pat
```ts
// app.d.ts
-///
+///
declare namespace Lucia {
type Auth = import("./lucia.js").Auth;
type UserAttributes = {};
@@ -124,7 +124,7 @@ declare namespace Lucia {
```ts
// auth/lucia.ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import "lucia-auth/polyfill/node";
// ...
diff --git a/documentation/content/main/start-here/getting-started.nextjs.md b/documentation/content/main/start-here/getting-started.nextjs.md
index d2b41b597..10353722b 100644
--- a/documentation/content/main/start-here/getting-started.nextjs.md
+++ b/documentation/content/main/start-here/getting-started.nextjs.md
@@ -38,7 +38,7 @@ In `auth/lucia.ts`, import [`lucia`](/reference/lucia-auth/auth) from `lucia-aut
```ts
// auth/lucia.ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { nextjs } from "lucia-auth/middleware";
import prisma from "@lucia-auth/adapter-prisma";
import { PrismaClient } from "@prisma/client";
@@ -61,7 +61,7 @@ Create `lucia.d.ts`, and inside it configure your types. The path in `import('./
```ts
// lucia.d.ts
-///
+///
declare namespace Lucia {
type Auth = import("./auth/lucia").Auth;
type UserAttributes = {};
@@ -74,7 +74,7 @@ declare namespace Lucia {
```ts
// auth/lucia.ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import "lucia-auth/polyfill/node";
// ...
diff --git a/documentation/content/main/start-here/getting-started.nuxt.md b/documentation/content/main/start-here/getting-started.nuxt.md
index 817d957b2..e31847686 100644
--- a/documentation/content/main/start-here/getting-started.nuxt.md
+++ b/documentation/content/main/start-here/getting-started.nuxt.md
@@ -34,7 +34,7 @@ In `server/utils/auth.ts`, import [`lucia`](/reference/lucia-auth/auth) from `lu
```ts
// server/utils/auth.ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { h3 } from "lucia-auth/middleware";
import prisma from "@lucia-auth/adapter-prisma";
import { PrismaClient } from "@prisma/client";
@@ -55,7 +55,7 @@ Create `server/lucia.d.ts`, and inside it configure your types. The path in `imp
```ts
// server/lucia.d.ts
-///
+///
declare namespace Lucia {
type Auth = import("./utils/auth.js").Auth;
type UserAttributes = {
@@ -70,7 +70,7 @@ declare namespace Lucia {
```ts
// server/utils/auth.ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import "lucia-auth/polyfill/node";
// ...
diff --git a/documentation/content/main/start-here/getting-started.qwik.md b/documentation/content/main/start-here/getting-started.qwik.md
index 5eb5c7f00..e289650aa 100644
--- a/documentation/content/main/start-here/getting-started.qwik.md
+++ b/documentation/content/main/start-here/getting-started.qwik.md
@@ -34,7 +34,7 @@ In `$lib/server/lucia.ts`, import [`lucia`](/reference/lucia-auth/auth) from `lu
```ts
// src/lib/lucia.ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { qwik } from "lucia-auth/middleware";
import prismaAdapter from "@lucia-auth/adapter-prisma";
import { PrismaClient } from "@prisma/client";
@@ -56,7 +56,7 @@ Create lucia.d.ts, and inside it configure your types. The path in import('./lib
```ts
// src/lucia.d.ts
-///
+///
declare namespace Lucia {
type Auth = import("./lib/lucia.js").Auth;
type UserAttributes = {};
diff --git a/documentation/content/main/start-here/getting-started.remix.md b/documentation/content/main/start-here/getting-started.remix.md
index 507524507..6cefdd3c2 100644
--- a/documentation/content/main/start-here/getting-started.remix.md
+++ b/documentation/content/main/start-here/getting-started.remix.md
@@ -34,7 +34,7 @@ In `auth/lucia.server.ts`, import [`lucia`](/reference/lucia-auth/auth) from `lu
```ts
// auth/lucia.server.ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { web } from "lucia-auth/middleware";
import prisma from "@lucia-auth/adapter-prisma";
import { PrismaClient } from "@prisma/client";
@@ -56,7 +56,7 @@ Create `lucia.d.ts`, and inside it configure your types. The path in `import('./
```ts
// lucia.d.ts
-///
+///
declare namespace Lucia {
type Auth = import("./auth/lucia.server.js").Auth;
type UserAttributes = {};
@@ -74,7 +74,7 @@ Lucia is an ESM package and you must define all modules in `serverDependenciesTo
module.exports = {
// ...
serverDependenciesToBundle: [
- "lucia-auth",
+ "lucia",
"lucia-auth/middleware",
"@lucia-auth/adapter-prisma",
"lucia-auth/polyfill/node",
@@ -91,7 +91,7 @@ module.exports = {
```ts
// auth/lucia.ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import "lucia-auth/polyfill/node";
// ...
diff --git a/documentation/content/main/start-here/getting-started.sveltekit.md b/documentation/content/main/start-here/getting-started.sveltekit.md
index ead513149..6885f3850 100644
--- a/documentation/content/main/start-here/getting-started.sveltekit.md
+++ b/documentation/content/main/start-here/getting-started.sveltekit.md
@@ -34,7 +34,7 @@ In `$lib/server/lucia.ts`, import [`lucia`](/reference/lucia-auth/auth) from `lu
```ts
// lib/server/lucia.ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { sveltekit } from "lucia-auth/middleware";
import prisma from "@lucia-auth/adapter-prisma";
import { PrismaClient } from "@prisma/client";
@@ -83,12 +83,12 @@ In `src/app.d.ts`, configure your types. The path in `import('$lib/server/lucia.
declare global {
namespace App {
interface Locals {
- auth: import("lucia-auth").AuthRequest;
+ auth: import("lucia").AuthRequest;
}
}
}
-///
+///
declare global {
namespace Lucia {
type Auth = import("$lib/server/lucia").Auth;
diff --git a/documentation/content/main/start-here/migrate-to-version-1.md b/documentation/content/main/start-here/migrate-to-version-1.md
index 721407367..8002c3a21 100644
--- a/documentation/content/main/start-here/migrate-to-version-1.md
+++ b/documentation/content/main/start-here/migrate-to-version-1.md
@@ -31,7 +31,7 @@ Middlewares are similar to database adapters but for frameworks, and they are al
```ts
import { node } from "lucia-auth/middleware";
-import lucia from "lucia-auth";
+import lucia from "lucia";
export const auth = lucia({
// ...
@@ -52,7 +52,7 @@ With this update, `getUser()` and other SvelteKit specific functions are no long
```ts
import { sveltekit } from "lucia-auth/middleware";
-import lucia from "lucia-auth";
+import lucia from "lucia";
export const auth = lucia({
// ...
@@ -75,7 +75,7 @@ export const handle: Handle = async ({ event, resolve }) => {
// app.d.ts
///
declare namespace App {
- type AuthRequest = import("lucia-auth").AuthRequest;
+ type AuthRequest = import("lucia").AuthRequest;
// Locals must be an interface and not a type
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface Locals extends AuthRequest {}
@@ -89,7 +89,7 @@ Add Node middleware (**new in v1.0**):
```ts
// astro
import { node } from "lucia-auth/middleware";
-import lucia from "lucia-auth";
+import lucia from "lucia";
export const auth = lucia({
// ...
@@ -110,7 +110,7 @@ Add Astro middleware:
```ts
// astro
import { astro } from "lucia-auth/middleware";
-import lucia from "lucia-auth";
+import lucia from "lucia";
export const auth = lucia({
// ...
@@ -162,7 +162,7 @@ TypeScript should be able to detect most, if not all, of these changes.
```ts
// auth/lucia.ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
import "lucia-auth/polyfill/node";
// ...
diff --git a/documentation/content/main/start-here/username-password.astro.md b/documentation/content/main/start-here/username-password.astro.md
index af012a7ab..aba6c2646 100644
--- a/documentation/content/main/start-here/username-password.astro.md
+++ b/documentation/content/main/start-here/username-password.astro.md
@@ -32,7 +32,7 @@ In `src/lucia.d.ts`, add `username` in `UserAttributes` since we added `username
```ts
// src/lucia.d.ts
-///
+///
declare namespace Lucia {
type Auth = import("$lib/server/lucia.js").Auth;
type UserAttributes = {
diff --git a/documentation/content/main/start-here/username-password.md b/documentation/content/main/start-here/username-password.md
index 8a334572c..6155d3ab1 100644
--- a/documentation/content/main/start-here/username-password.md
+++ b/documentation/content/main/start-here/username-password.md
@@ -22,7 +22,7 @@ In `src/lucia.d.ts`, add `username` in `UserAttributes` since we added `username
```ts
// src/lucia.d.ts
-///
+///
declare namespace Lucia {
type Auth = import("$lib/server/lucia.js").Auth;
type UserAttributes = {
diff --git a/documentation/content/main/start-here/username-password.nextjs.md b/documentation/content/main/start-here/username-password.nextjs.md
index d88bd7668..cfa01c9a3 100644
--- a/documentation/content/main/start-here/username-password.nextjs.md
+++ b/documentation/content/main/start-here/username-password.nextjs.md
@@ -32,7 +32,7 @@ In `lucia.d.ts`, add `username` in `UserAttributes` since we added `username` co
```ts
// lucia.d.ts
-///
+///
declare namespace Lucia {
type Auth = import("$lib/server/lucia.js").Auth;
type UserAttributes = {
@@ -355,7 +355,7 @@ import type {
GetServerSidePropsResult,
InferGetServerSidePropsType
} from "next";
-import type { User } from "lucia-auth";
+import type { User } from "lucia";
export const getServerSideProps = async (
context: GetServerSidePropsContext
diff --git a/documentation/content/main/start-here/username-password.nuxt.md b/documentation/content/main/start-here/username-password.nuxt.md
index 497976f2a..036a38399 100644
--- a/documentation/content/main/start-here/username-password.nuxt.md
+++ b/documentation/content/main/start-here/username-password.nuxt.md
@@ -32,7 +32,7 @@ In `server/lucia.d.ts`, add `username` in `UserAttributes` since we added `usern
```ts
// server/lucia.d.ts
-///
+///
declare namespace Lucia {
type Auth = import("./utils/auth.js").Auth;
type UserAttributes = {
@@ -111,7 +111,7 @@ Users can be created with `createUser()`. This will create a new primary key tha
```ts
// server/api/signup.post.ts
import { Prisma } from "@prisma/client";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
export default defineEventHandler(async (event) => {
const { username, password } = (await readBody(event)) ?? {};
@@ -224,7 +224,7 @@ We’ll use the key created in the previous section to reference the user and au
```ts
// server/api/login.post.ts
import { Prisma } from "@prisma/client";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
export default defineEventHandler(async (event) => {
const { username, password } = (await readBody(event)) ?? {};
diff --git a/documentation/content/main/start-here/username-password.qwik.md b/documentation/content/main/start-here/username-password.qwik.md
index a3cd3e730..a8986647a 100644
--- a/documentation/content/main/start-here/username-password.qwik.md
+++ b/documentation/content/main/start-here/username-password.qwik.md
@@ -32,7 +32,7 @@ In `lucia.d.ts`, add `username` in `UserAttributes` since we added `username` co
```ts
// src/lucia.d.ts
-///
+///
declare namespace Lucia {
type Auth = import("./lib/lucia.js").Auth;
type UserAttributes = {
@@ -112,7 +112,7 @@ import {
} from "@builder.io/qwik-city";
import { auth } from "~/lib/lucia";
import { Prisma } from "@prisma/client";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
// create an action to handle the form submission
export const useSignupAction = routeAction$(
@@ -224,7 +224,7 @@ import {
} from "@builder.io/qwik-city";
import { auth } from "~/lib/lucia";
import { Prisma } from "@prisma/client";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
export const useUserLoader = routeLoader$(async (event) => {
const authRequest = auth.handleRequest(event);
@@ -289,7 +289,7 @@ import {
} from "@builder.io/qwik-city";
import { auth } from "~/lib/lucia";
import { Prisma } from "@prisma/client";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
// create an action to handle the form submission
export const useLoginAction = routeAction$(
@@ -347,7 +347,7 @@ import {
} from "@builder.io/qwik-city";
import { auth } from "~/lib/lucia";
import { Prisma } from "@prisma/client";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
// .... the action hook shown above
@@ -391,7 +391,7 @@ import {
routeAction$
} from "@builder.io/qwik-city";
import { auth } from "~/lib/lucia";
-import type { LuciaError } from "lucia-auth";
+import type { LuciaError } from "lucia";
export const useUserLoader = routeLoader$(async (event) => {
const authRequest = auth.handleRequest(event);
diff --git a/documentation/content/main/start-here/username-password.remix.md b/documentation/content/main/start-here/username-password.remix.md
index 6de509865..3e7baf27d 100644
--- a/documentation/content/main/start-here/username-password.remix.md
+++ b/documentation/content/main/start-here/username-password.remix.md
@@ -32,7 +32,7 @@ In `lucia.d.ts`, add `username` in `UserAttributes` since we added `username` co
```ts
// lucia.d.ts
-///
+///
declare namespace Lucia {
type Auth = import("$lib/server/lucia.js").Auth;
type UserAttributes = {
@@ -101,7 +101,7 @@ Users can be created with `createUser()`. This will create a new primary key tha
// app/routes/signup.tsx
import { Form } from "@remix-run/react";
import { auth } from "@auth/lucia.server";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
import { redirect, json } from "@remix-run/node";
import { Prisma } from "@prisma/client";
@@ -193,7 +193,7 @@ Define a `loader()`.
// app/routes/signup.tsx
import { Form } from "@remix-run/react";
import { auth } from "@auth/lucia.server";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
import { redirect, json } from "@remix-run/node";
import type { LoaderArgs, ActionArgs } from "@remix-run/node";
@@ -259,7 +259,7 @@ We’ll use the key created in the previous section to reference the user and au
// pages/api/login.ts
import { Form } from "@remix-run/react";
import { auth } from "@auth/lucia.server";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
import { redirect, json } from "@remix-run/node";
import { Prisma } from "@prisma/client";
@@ -319,7 +319,7 @@ If the session exists, redirect authenticated users to the profile page.
// app/routes/login.tsx
import { Form } from "@remix-run/react";
import { auth } from "@auth/lucia.server";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
import { redirect, json } from "@remix-run/node";
import type { LoaderArgs, ActionArgs } from "@remix-run/node";
diff --git a/documentation/content/main/start-here/username-password.sveltekit.md b/documentation/content/main/start-here/username-password.sveltekit.md
index 9e1329046..74ca562dd 100644
--- a/documentation/content/main/start-here/username-password.sveltekit.md
+++ b/documentation/content/main/start-here/username-password.sveltekit.md
@@ -32,7 +32,7 @@ In `src/app.d.ts`, add `username` in `UserAttributes` since we added a `username
```ts
// src/app.d.ts
-///
+///
declare global {
namespace Lucia {
type Auth = import("$lib/lucia").Auth;
diff --git a/documentation/content/reference/lucia-auth/auth.md b/documentation/content/reference/lucia-auth/auth.md
index 002447f40..5482912bf 100644
--- a/documentation/content/reference/lucia-auth/auth.md
+++ b/documentation/content/reference/lucia-auth/auth.md
@@ -230,7 +230,7 @@ const deleteDeadUserSessions: (userId: string) => Promise;
#### Example
```ts
-import { auth } from "lucia-auth";
+import { auth } from "lucia";
try {
await auth.deleteExpiredUserSession(userId);
@@ -257,7 +257,7 @@ const deleteKey: (providerId: string, providerUserId: string) => Promise;
#### Example
```ts
-import { auth } from "lucia-auth";
+import { auth } from "lucia";
try {
await auth.deleteKey("username", "user@example.com");
@@ -621,7 +621,7 @@ const invalidateSession: (sessionId: string) => Promise;
#### Example
```ts
-import { auth } from "lucia-auth";
+import { auth } from "lucia";
try {
await auth.invalidateSession(sessionId);
@@ -661,7 +661,7 @@ const renewSession: (sessionId: string) => Promise;
#### Example
```ts
-import { auth } from "lucia-auth";
+import { auth } from "lucia";
try {
const renewedSession = await auth.renewSession(session.sessionId);
@@ -741,7 +741,7 @@ const updateUserAttributes: (
#### Example
```ts
-import { auth } from "lucia-auth";
+import { auth } from "lucia";
try {
await auth.updateUserAttributes(userId, {
@@ -886,7 +886,7 @@ const validateSession: (sessionId: string) => Promise;
#### Example
```ts
-import { auth } from "lucia-auth";
+import { auth } from "lucia";
try {
const session = await auth.validateSession(sessionId);
@@ -932,7 +932,7 @@ const validateSessionUser: (
#### Example
```ts
-import { auth } from "lucia-auth";
+import { auth } from "lucia";
try {
const { session, user } = await auth.validateSessionUser(sessionId);
diff --git a/documentation/content/reference/lucia-auth/lucia-auth.md b/documentation/content/reference/lucia-auth/lucia-auth.md
index acb8cb7e6..9269f244d 100644
--- a/documentation/content/reference/lucia-auth/lucia-auth.md
+++ b/documentation/content/reference/lucia-auth/lucia-auth.md
@@ -6,7 +6,7 @@ title: "Main (/)"
These can be imported from `lucia-auth` and should only be used inside a server context.
```ts
-import { generateRandomString } from "lucia-auth";
+import { generateRandomString } from "lucia";
```
For exported types, refer to [Public types](/reference/lucia-auth/types).
@@ -62,8 +62,9 @@ const lucia: (config: Configuration) => Auth;
This is exported as default:
```ts
-import lucia from "lucia-auth";
-import { default as lucia } from "lucia-auth";
+import lucia from "lucia";
+// or
+import { default as lucia } from "lucia";
```
#### Parameter
diff --git a/documentation/content/reference/lucia-auth/types.md b/documentation/content/reference/lucia-auth/types.md
index d069fd378..f90d4b4a0 100644
--- a/documentation/content/reference/lucia-auth/types.md
+++ b/documentation/content/reference/lucia-auth/types.md
@@ -6,7 +6,7 @@ _order: 2
These types can be imported from `lucia-auth`:
```ts
-import type { Adapter } from "lucia-auth";
+import type { Adapter } from "lucia";
```
## `Adapter`
@@ -93,9 +93,9 @@ A namespace.
```ts
// lucia.d.ts
-///
+///
declare namespace Lucia {
- type Auth = import("lucia-auth").Auth;
+ type Auth = import("lucia").Auth;
type UserAttributes = {};
}
```
@@ -108,7 +108,7 @@ Should be set to [`Auth`](/reference/lucia-auth/auth).
```ts
// lucia.ts
-import lucia from "lucia-auth";
+import lucia from "lucia";
const auth = lucia();
export type Auth = typeof auth;
diff --git a/documentation/content/tokens/guides/email-verification-links.md b/documentation/content/tokens/guides/email-verification-links.md
index bee248305..40f11d71c 100644
--- a/documentation/content/tokens/guides/email-verification-links.md
+++ b/documentation/content/tokens/guides/email-verification-links.md
@@ -56,7 +56,7 @@ export const auth = lucia({
```ts
// src/app.d.ts
-///
+///
declare global {
namespace Lucia {
type Auth = import("$lib/lucia").Auth;
diff --git a/documentation/content/tokens/guides/email-verification-links.sveltekit.md b/documentation/content/tokens/guides/email-verification-links.sveltekit.md
index 7029dd7c2..44fba95cd 100644
--- a/documentation/content/tokens/guides/email-verification-links.sveltekit.md
+++ b/documentation/content/tokens/guides/email-verification-links.sveltekit.md
@@ -55,7 +55,7 @@ export const auth = lucia({
```ts
// src/app.d.ts
-///
+///
declare global {
namespace Lucia {
type Auth = import("$lib/lucia").Auth;
diff --git a/examples/astro-email/package.json b/examples/astro-email/package.json
index b4d3a6a19..b96a33729 100644
--- a/examples/astro-email/package.json
+++ b/examples/astro-email/package.json
@@ -16,7 +16,7 @@
"@lucia-auth/tokens": "latest",
"@prisma/client": "^4.12.0",
"astro": "^2.1.3",
- "lucia-auth": "latest",
+ "lucia": "latest",
"tailwindcss": "^3.0.24"
},
"devDependencies": {
diff --git a/examples/astro-email/src/auth/email.ts b/examples/astro-email/src/auth/email.ts
index 4df4c8704..75d5d38ab 100644
--- a/examples/astro-email/src/auth/email.ts
+++ b/examples/astro-email/src/auth/email.ts
@@ -1,5 +1,5 @@
import { prismaClient } from "src/db";
-import { generateRandomString } from "lucia-auth";
+import { generateRandomString } from "lucia";
import type { Email as DatabaseEmail } from "@prisma/client";
const sendEmail = async (
diff --git a/examples/astro-email/src/auth/lucia.ts b/examples/astro-email/src/auth/lucia.ts
index 6c1d4514a..a11423dc3 100644
--- a/examples/astro-email/src/auth/lucia.ts
+++ b/examples/astro-email/src/auth/lucia.ts
@@ -1,4 +1,4 @@
-import lucia from "lucia-auth";
+import lucia from "lucia";
import prisma from "@lucia-auth/adapter-prisma";
import { astro } from "lucia-auth/middleware";
import { idToken } from "@lucia-auth/tokens";
diff --git a/examples/astro-email/src/lucia.d.ts b/examples/astro-email/src/lucia.d.ts
index 2b3f51d7f..fb1ff8834 100644
--- a/examples/astro-email/src/lucia.d.ts
+++ b/examples/astro-email/src/lucia.d.ts
@@ -1,4 +1,4 @@
-///
+///
declare namespace Lucia {
type Auth = import("@auth/lucia").Auth;
type UserAttributes = Omit;
diff --git a/examples/astro-email/src/pages/login.astro b/examples/astro-email/src/pages/login.astro
index 382260575..6c53900fa 100644
--- a/examples/astro-email/src/pages/login.astro
+++ b/examples/astro-email/src/pages/login.astro
@@ -1,6 +1,6 @@
---
import { auth } from "@auth/lucia";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
import { emailRegex, isValidFormSubmission } from "src/forms/submission";
import MainLayout from "src/layouts/MainLayout.astro";
diff --git a/examples/astro-email/src/pages/signup.astro b/examples/astro-email/src/pages/signup.astro
index f16828eb8..981f9b48c 100644
--- a/examples/astro-email/src/pages/signup.astro
+++ b/examples/astro-email/src/pages/signup.astro
@@ -2,7 +2,7 @@
import { sendEmailVerificationEmail } from "@auth/email";
import { auth, emailVerificationToken } from "@auth/lucia";
import { Prisma } from "@prisma/client";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
import { emailRegex, isValidFormSubmission } from "src/forms/submission";
import MainLayout from "src/layouts/MainLayout.astro";
diff --git a/examples/astro/package.json b/examples/astro/package.json
index 4fa757c03..2a0e39236 100644
--- a/examples/astro/package.json
+++ b/examples/astro/package.json
@@ -17,7 +17,7 @@
"@lucia-auth/oauth": "latest",
"@prisma/client": "^4.7.0",
"astro": "^1.6.0",
- "lucia-auth": "latest",
+ "lucia": "latest",
"svelte": "^3.46.4",
"tailwindcss": "^3.0.24"
},
diff --git a/examples/astro/src/lib/lucia.ts b/examples/astro/src/lib/lucia.ts
index e038b46ce..c2a3bdc5f 100644
--- a/examples/astro/src/lib/lucia.ts
+++ b/examples/astro/src/lib/lucia.ts
@@ -1,4 +1,4 @@
-import lucia from "lucia-auth";
+import lucia from "lucia";
import "lucia-auth/polyfill/node";
import { astro } from "lucia-auth/middleware";
import prisma from "@lucia-auth/adapter-prisma";
diff --git a/examples/astro/src/lucia.d.ts b/examples/astro/src/lucia.d.ts
index 0d87ba134..6e862713e 100644
--- a/examples/astro/src/lucia.d.ts
+++ b/examples/astro/src/lucia.d.ts
@@ -1,4 +1,4 @@
-///
+///
declare namespace Lucia {
type Auth = import("./lib/lucia").Auth;
type UserAttributes = {
diff --git a/examples/astro/src/pages/login.astro b/examples/astro/src/pages/login.astro
index 4c7f06d67..7f228bc81 100644
--- a/examples/astro/src/pages/login.astro
+++ b/examples/astro/src/pages/login.astro
@@ -1,7 +1,7 @@
---
import MainLayout from "../layouts/MainLayout.astro";
import { auth } from "../lib/lucia";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
const authRequest = auth.handleRequest(Astro);
const { session } = await authRequest.validateUser();
diff --git a/examples/astro/src/pages/signup.astro b/examples/astro/src/pages/signup.astro
index 58086f88a..2b8179530 100644
--- a/examples/astro/src/pages/signup.astro
+++ b/examples/astro/src/pages/signup.astro
@@ -3,7 +3,7 @@
import { auth } from "../lib/lucia";
import MainLayout from "../layouts/MainLayout.astro";
import { Prisma } from "@prisma/client";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
const authRequest = auth.handleRequest(Astro);
const { session } = await authRequest.validateUser();
diff --git a/examples/nextjs-app/app/api/login/route.ts b/examples/nextjs-app/app/api/login/route.ts
index cecb91d6a..6c27a522d 100644
--- a/examples/nextjs-app/app/api/login/route.ts
+++ b/examples/nextjs-app/app/api/login/route.ts
@@ -1,7 +1,7 @@
import { auth } from "@/auth/lucia";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
export const POST = async (request: Request) => {
const { username, password } = (await request.json()) as Partial<{
diff --git a/examples/nextjs-app/app/api/signup/route.ts b/examples/nextjs-app/app/api/signup/route.ts
index 67fca2992..55d365802 100644
--- a/examples/nextjs-app/app/api/signup/route.ts
+++ b/examples/nextjs-app/app/api/signup/route.ts
@@ -1,5 +1,5 @@
import { auth } from "@/auth/lucia";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
import { Prisma } from "@prisma/client";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
diff --git a/examples/nextjs-app/auth/lucia.ts b/examples/nextjs-app/auth/lucia.ts
index 5dc90f397..e24b34b77 100644
--- a/examples/nextjs-app/auth/lucia.ts
+++ b/examples/nextjs-app/auth/lucia.ts
@@ -1,4 +1,4 @@
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { nextjs } from "lucia-auth/middleware";
import prisma from "@lucia-auth/adapter-prisma";
import { PrismaClient } from "@prisma/client";
diff --git a/examples/nextjs-app/lucia.d.ts b/examples/nextjs-app/lucia.d.ts
index 2dcc45d78..ee2987842 100644
--- a/examples/nextjs-app/lucia.d.ts
+++ b/examples/nextjs-app/lucia.d.ts
@@ -1,4 +1,4 @@
-///
+///
declare namespace Lucia {
type Auth = import("./auth/lucia").Auth;
type UserAttributes = {
diff --git a/examples/nextjs-app/package.json b/examples/nextjs-app/package.json
index a4e747132..c903db41e 100644
--- a/examples/nextjs-app/package.json
+++ b/examples/nextjs-app/package.json
@@ -9,7 +9,7 @@
"lint": "next lint"
},
"dependencies": {
- "lucia-auth": "latest",
+ "lucia": "latest",
"@lucia-auth/adapter-prisma": "latest",
"@lucia-auth/oauth": "latest",
"@prisma/client": "^4.7.0",
diff --git a/examples/nextjs/auth/lucia.ts b/examples/nextjs/auth/lucia.ts
index 5dc90f397..e24b34b77 100644
--- a/examples/nextjs/auth/lucia.ts
+++ b/examples/nextjs/auth/lucia.ts
@@ -1,4 +1,4 @@
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { nextjs } from "lucia-auth/middleware";
import prisma from "@lucia-auth/adapter-prisma";
import { PrismaClient } from "@prisma/client";
diff --git a/examples/nextjs/lucia.d.ts b/examples/nextjs/lucia.d.ts
index 0d87ba134..6e862713e 100644
--- a/examples/nextjs/lucia.d.ts
+++ b/examples/nextjs/lucia.d.ts
@@ -1,4 +1,4 @@
-///
+///
declare namespace Lucia {
type Auth = import("./lib/lucia").Auth;
type UserAttributes = {
diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json
index d43484284..a395ad83e 100644
--- a/examples/nextjs/package.json
+++ b/examples/nextjs/package.json
@@ -18,7 +18,7 @@
"encoding": "^0.1.13",
"eslint": "8.26.0",
"eslint-config-next": "13.0.1",
- "lucia-auth": "latest",
+ "lucia": "latest",
"next": "13.0.1",
"react": "18.2.0",
"react-dom": "18.2.0",
diff --git a/examples/nextjs/pages/api/login.ts b/examples/nextjs/pages/api/login.ts
index 2ce5cde5a..794b02e50 100644
--- a/examples/nextjs/pages/api/login.ts
+++ b/examples/nextjs/pages/api/login.ts
@@ -1,7 +1,7 @@
import { auth } from "../../auth/lucia";
import type { NextApiRequest, NextApiResponse } from "next";
-import type { LuciaError } from "lucia-auth";
+import type { LuciaError } from "lucia";
type Data = {
error?: string;
diff --git a/examples/nextjs/pages/api/signup.ts b/examples/nextjs/pages/api/signup.ts
index a6b78ff04..b50643100 100644
--- a/examples/nextjs/pages/api/signup.ts
+++ b/examples/nextjs/pages/api/signup.ts
@@ -1,5 +1,5 @@
import { auth } from "../../auth/lucia";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
import { Prisma } from "@prisma/client";
import type { NextApiRequest, NextApiResponse } from "next";
diff --git a/examples/nextjs/pages/index.tsx b/examples/nextjs/pages/index.tsx
index 3813704dc..a6f44274f 100644
--- a/examples/nextjs/pages/index.tsx
+++ b/examples/nextjs/pages/index.tsx
@@ -6,7 +6,7 @@ import type {
GetServerSidePropsResult,
InferGetServerSidePropsType
} from "next";
-import type { User } from "lucia-auth";
+import type { User } from "lucia";
export const getServerSideProps = async (
context: GetServerSidePropsContext
diff --git a/examples/nuxt/package.json b/examples/nuxt/package.json
index 52bf39f1b..d144e6544 100644
--- a/examples/nuxt/package.json
+++ b/examples/nuxt/package.json
@@ -9,7 +9,7 @@
"postinstall": "nuxt prepare"
},
"devDependencies": {
- "lucia-auth": "latest",
+ "lucia": "latest",
"@lucia-auth/adapter-prisma": "latest",
"@lucia-auth/oauth": "latest",
"@nuxtjs/tailwindcss": "^6.7.0",
diff --git a/examples/nuxt/server/api/login.post.ts b/examples/nuxt/server/api/login.post.ts
index be4c0be3e..2a8d84b2b 100644
--- a/examples/nuxt/server/api/login.post.ts
+++ b/examples/nuxt/server/api/login.post.ts
@@ -1,4 +1,4 @@
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
export default defineEventHandler(async (event) => {
const { username, password } = (await readBody(event)) ?? {};
diff --git a/examples/nuxt/server/api/signup.post.ts b/examples/nuxt/server/api/signup.post.ts
index 1f8565d37..dddcd50c2 100644
--- a/examples/nuxt/server/api/signup.post.ts
+++ b/examples/nuxt/server/api/signup.post.ts
@@ -1,5 +1,5 @@
import { Prisma } from "@prisma/client";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
export default defineEventHandler(async (event) => {
const { username, password } = (await readBody(event)) ?? {};
diff --git a/examples/nuxt/server/lucia.d.ts b/examples/nuxt/server/lucia.d.ts
index 768a60430..6ee497b16 100644
--- a/examples/nuxt/server/lucia.d.ts
+++ b/examples/nuxt/server/lucia.d.ts
@@ -1,4 +1,4 @@
-///
+///
declare namespace Lucia {
type Auth = import("./utils/auth.js").Auth;
type UserAttributes = {
diff --git a/examples/nuxt/server/utils/auth.ts b/examples/nuxt/server/utils/auth.ts
index 7f1f59e7c..56cdc3be3 100644
--- a/examples/nuxt/server/utils/auth.ts
+++ b/examples/nuxt/server/utils/auth.ts
@@ -1,4 +1,4 @@
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { h3 } from "lucia-auth/middleware";
import prisma from "@lucia-auth/adapter-prisma";
import { PrismaClient } from "@prisma/client";
diff --git a/examples/qwik/package.json b/examples/qwik/package.json
index 7247493db..f3400930d 100644
--- a/examples/qwik/package.json
+++ b/examples/qwik/package.json
@@ -42,6 +42,6 @@
"dependencies": {
"@lucia-auth/adapter-prisma": "latest",
"@prisma/client": "^4.13.0",
- "lucia-auth": "latest"
+ "lucia": "latest"
}
}
diff --git a/examples/qwik/src/lib/lucia.ts b/examples/qwik/src/lib/lucia.ts
index 0463bf794..42fdf6859 100644
--- a/examples/qwik/src/lib/lucia.ts
+++ b/examples/qwik/src/lib/lucia.ts
@@ -1,5 +1,5 @@
import prismaAdapter from "@lucia-auth/adapter-prisma";
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { qwik } from "lucia-auth/middleware";
import { prisma } from "./prisma";
diff --git a/examples/qwik/src/lucia.d.ts b/examples/qwik/src/lucia.d.ts
index b89990856..476d80b8e 100644
--- a/examples/qwik/src/lucia.d.ts
+++ b/examples/qwik/src/lucia.d.ts
@@ -1,4 +1,4 @@
-///
+///
declare namespace Lucia {
type Auth = import("./lib/lucia.js").Auth;
type UserAttributes = {
diff --git a/examples/qwik/src/routes/login/index.tsx b/examples/qwik/src/routes/login/index.tsx
index d44cf04e8..7cca9ab05 100644
--- a/examples/qwik/src/routes/login/index.tsx
+++ b/examples/qwik/src/routes/login/index.tsx
@@ -8,7 +8,7 @@ import {
routeAction$
} from "@builder.io/qwik-city";
import { auth } from "~/lib/lucia";
-import type { LuciaError } from "lucia-auth";
+import type { LuciaError } from "lucia";
export const useUserLoader = routeLoader$(async (event) => {
const authRequest = auth.handleRequest(event);
diff --git a/examples/qwik/src/routes/signup/index.tsx b/examples/qwik/src/routes/signup/index.tsx
index a6889ef07..f5ed7ac7a 100644
--- a/examples/qwik/src/routes/signup/index.tsx
+++ b/examples/qwik/src/routes/signup/index.tsx
@@ -9,7 +9,7 @@ import {
} from "@builder.io/qwik-city";
import { auth } from "~/lib/lucia";
import { Prisma } from "@prisma/client";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
export const useUserLoader = routeLoader$(async (event) => {
const authRequest = auth.handleRequest(event);
diff --git a/examples/remix/app/routes/login.tsx b/examples/remix/app/routes/login.tsx
index 291467e3b..1c09a5926 100644
--- a/examples/remix/app/routes/login.tsx
+++ b/examples/remix/app/routes/login.tsx
@@ -1,5 +1,5 @@
import { auth } from "@auth/lucia.server";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
import { Form, useActionData } from "@remix-run/react";
import { redirect, json } from "@remix-run/node";
diff --git a/examples/remix/app/routes/signup.tsx b/examples/remix/app/routes/signup.tsx
index 702391d8e..c5fd1d8a4 100644
--- a/examples/remix/app/routes/signup.tsx
+++ b/examples/remix/app/routes/signup.tsx
@@ -1,5 +1,5 @@
import { auth } from "@auth/lucia.server";
-import { LuciaError } from "lucia-auth";
+import { LuciaError } from "lucia";
import { Form, useActionData } from "@remix-run/react";
import { redirect, json } from "@remix-run/node";
import { Prisma } from "@prisma/client";
diff --git a/examples/remix/auth/lucia.server.ts b/examples/remix/auth/lucia.server.ts
index 343871a91..f544ff9d1 100644
--- a/examples/remix/auth/lucia.server.ts
+++ b/examples/remix/auth/lucia.server.ts
@@ -1,4 +1,4 @@
-import lucia from "lucia-auth";
+import lucia from "lucia";
import { web } from "lucia-auth/middleware";
import prisma from "@lucia-auth/adapter-prisma";
import { PrismaClient } from "@prisma/client";
diff --git a/examples/remix/lucia.d.ts b/examples/remix/lucia.d.ts
index 428073af1..1cb4f79dd 100644
--- a/examples/remix/lucia.d.ts
+++ b/examples/remix/lucia.d.ts
@@ -1,4 +1,4 @@
-///
+///
declare namespace Lucia {
type Auth = import("@auth/lucia.server.js").Auth;
type UserAttributes = {
diff --git a/examples/remix/package.json b/examples/remix/package.json
index 975b03647..456a94c84 100644
--- a/examples/remix/package.json
+++ b/examples/remix/package.json
@@ -16,15 +16,15 @@
"@remix-run/react": "^1.16.1",
"@remix-run/serve": "^1.16.1",
"isbot": "^3.6.8",
- "lucia-auth": "latest",
+ "lucia": "latest",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@remix-run/dev": "^1.16.1",
"@remix-run/eslint-config": "^1.16.1",
- "@types/react": "^18.0.35",
- "@types/react-dom": "^18.0.11",
+ "@types/react": "^18.0.24",
+ "@types/react-dom": "^18.0.8",
"eslint": "^8.38.0",
"prisma": "^4.7.0",
"tailwindcss": "^3.3.2",
diff --git a/examples/remix/remix.config.js b/examples/remix/remix.config.js
index 676bc4b22..6831ebbf2 100644
--- a/examples/remix/remix.config.js
+++ b/examples/remix/remix.config.js
@@ -10,7 +10,7 @@ module.exports = {
v2_routeConvention: true
},
serverDependenciesToBundle: [
- "lucia-auth",
+ "lucia",
"lucia-auth/middleware",
"@lucia-auth/adapter-prisma",
"@lucia-auth/oauth",
diff --git a/examples/sveltekit-email/package.json b/examples/sveltekit-email/package.json
index 24159c193..b19c4728f 100644
--- a/examples/sveltekit-email/package.json
+++ b/examples/sveltekit-email/package.json
@@ -36,6 +36,6 @@
"@lucia-auth/adapter-prisma": "^1.0.0",
"@lucia-auth/tokens": "^1.0.0",
"@prisma/client": "^4.12.0",
- "lucia-auth": "^1.0.0"
+ "lucia": "^1.0.0"
}
}
diff --git a/examples/sveltekit-email/src/app.d.ts b/examples/sveltekit-email/src/app.d.ts
index 4770ac147..a77388211 100644
--- a/examples/sveltekit-email/src/app.d.ts
+++ b/examples/sveltekit-email/src/app.d.ts
@@ -3,12 +3,12 @@
declare global {
namespace App {
interface Locals {
- auth: import('lucia-auth').AuthRequest;
+ auth: import('lucia').AuthRequest;
}
}
}
-///
+///
declare global {
namespace Lucia {
type Auth = import('$lib/lucia').Auth;
diff --git a/examples/sveltekit-email/src/lib/email.ts b/examples/sveltekit-email/src/lib/email.ts
index 00b479046..067ea3209 100644
--- a/examples/sveltekit-email/src/lib/email.ts
+++ b/examples/sveltekit-email/src/lib/email.ts
@@ -1,6 +1,6 @@
import { prismaClient } from '$lib/db';
import type { Email as DatabaseEmail } from '@prisma/client';
-import { generateRandomString } from 'lucia-auth';
+import { generateRandomString } from 'lucia';
const sendEmail = async (emailAddress: string, subject: string, content: string) => {
await prismaClient.email.create({
diff --git a/examples/sveltekit-email/src/lib/lucia.ts b/examples/sveltekit-email/src/lib/lucia.ts
index 27e2c01fb..d52c09888 100644
--- a/examples/sveltekit-email/src/lib/lucia.ts
+++ b/examples/sveltekit-email/src/lib/lucia.ts
@@ -1,4 +1,4 @@
-import lucia from 'lucia-auth';
+import lucia from 'lucia';
import prisma from '@lucia-auth/adapter-prisma';
import { sveltekit } from 'lucia-auth/middleware';
import { idToken } from '@lucia-auth/tokens';
diff --git a/examples/sveltekit-email/src/routes/(main)/login/+page.server.ts b/examples/sveltekit-email/src/routes/(main)/login/+page.server.ts
index c65c12763..bd49d7b62 100644
--- a/examples/sveltekit-email/src/routes/(main)/login/+page.server.ts
+++ b/examples/sveltekit-email/src/routes/(main)/login/+page.server.ts
@@ -1,6 +1,6 @@
import { auth } from '$lib/lucia';
import { emailRegex } from '$lib/form-submission';
-import { LuciaError } from 'lucia-auth';
+import { LuciaError } from 'lucia';
import { fail, redirect } from '@sveltejs/kit';
import type { PageServerLoad, Actions } from './$types';
diff --git a/examples/sveltekit-email/src/routes/(main)/signup/+page.server.ts b/examples/sveltekit-email/src/routes/(main)/signup/+page.server.ts
index c43dac2d2..6e0112eb3 100644
--- a/examples/sveltekit-email/src/routes/(main)/signup/+page.server.ts
+++ b/examples/sveltekit-email/src/routes/(main)/signup/+page.server.ts
@@ -2,7 +2,7 @@ import { emailRegex } from '$lib/form-submission';
import { fail, redirect } from '@sveltejs/kit';
import { auth, emailVerificationToken } from '$lib/lucia';
import { sendEmailVerificationEmail } from '$lib/email';
-import { LuciaError } from 'lucia-auth';
+import { LuciaError } from 'lucia';
import { Prisma } from '@prisma/client';
import type { PageServerLoad, Actions } from './$types';
diff --git a/examples/sveltekit/package.json b/examples/sveltekit/package.json
index ea50d003b..00b8ce29d 100644
--- a/examples/sveltekit/package.json
+++ b/examples/sveltekit/package.json
@@ -30,7 +30,7 @@
"svelte-preprocess": "^4.10.7",
"tailwindcss": "^3.1.8",
"tslib": "^2.4.0",
- "typescript": "5.x",
+ "typescript": "latest",
"vite": "4.x"
},
"type": "module",
@@ -39,6 +39,6 @@
"@lucia-auth/oauth": "latest",
"@prisma/client": "~4.7.0",
"devalue": "^4.0.1",
- "lucia-auth": "latest"
+ "lucia": "latest"
}
}
diff --git a/examples/sveltekit/src/app.d.ts b/examples/sveltekit/src/app.d.ts
index 3f358efd9..15800e441 100644
--- a/examples/sveltekit/src/app.d.ts
+++ b/examples/sveltekit/src/app.d.ts
@@ -1,4 +1,4 @@
-///
+///
declare namespace Lucia {
type Auth = import('$lib/server/lucia.js').Auth;
type UserAttributes = {
@@ -9,6 +9,6 @@ declare namespace Lucia {
///
declare namespace App {
interface Locals {
- auth: import('lucia-auth').AuthRequest;
+ auth: import('lucia').AuthRequest;
}
}
diff --git a/examples/sveltekit/src/lib/server/lucia.ts b/examples/sveltekit/src/lib/server/lucia.ts
index 095029f1c..87d6b0f7e 100644
--- a/examples/sveltekit/src/lib/server/lucia.ts
+++ b/examples/sveltekit/src/lib/server/lucia.ts
@@ -1,5 +1,5 @@
-import lucia from 'lucia-auth';
-import { sveltekit } from 'lucia-auth/middleware';
+import lucia from 'lucia';
+import { sveltekit } from 'lucia/middleware';
import prisma from '@lucia-auth/adapter-prisma';
import { dev } from '$app/environment';
import { PrismaClient } from '@prisma/client';
diff --git a/examples/sveltekit/src/routes/login/+page.server.ts b/examples/sveltekit/src/routes/login/+page.server.ts
index fff3c17bb..f544be999 100644
--- a/examples/sveltekit/src/routes/login/+page.server.ts
+++ b/examples/sveltekit/src/routes/login/+page.server.ts
@@ -2,7 +2,7 @@ import { fail, type Actions } from '@sveltejs/kit';
import { auth } from '$lib/server/lucia';
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
-import { LuciaError } from 'lucia-auth';
+import { LuciaError } from 'lucia';
export const load: PageServerLoad = async ({ locals }) => {
const session = await locals.auth.validate();
diff --git a/examples/sveltekit/src/routes/signup/+page.server.ts b/examples/sveltekit/src/routes/signup/+page.server.ts
index 854ce00b5..e0cb87030 100644
--- a/examples/sveltekit/src/routes/signup/+page.server.ts
+++ b/examples/sveltekit/src/routes/signup/+page.server.ts
@@ -2,7 +2,7 @@ import { auth } from '$lib/server/lucia';
import { fail, type Actions } from '@sveltejs/kit';
import { Prisma } from '@prisma/client';
import { redirect } from '@sveltejs/kit';
-import { LuciaError } from 'lucia-auth';
+import { LuciaError } from 'lucia';
import type { PageServerLoad } from './$types';
export const actions: Actions = {
diff --git a/package.json b/package.json
index ee25db50c..b378de7cf 100644
--- a/package.json
+++ b/package.json
@@ -1,10 +1,10 @@
{
"name": "lucia",
"version": "0.0.1",
- "description": "monorepo for lucia-auth",
+ "description": "Authentication, simple and clean",
"scripts": {
- "ready": "pnpm i && cd packages/lucia-auth && pnpm build && pnpm i && cd ../adapter-test && pnpm build && pnpm i && cd ../integration-oauth && pnpm build && cd ../adapter-prisma && pnpm build && cd ../integration-tokens && pnpm build && cd .. && pnpm install --no-frozen-lockfile",
- "publish-setup": "cd packages/lucia-auth && pnpm i --no-frozen-lockfile && pnpm build && cd ../../ && cd packages/adapter-test && pnpm i --no-frozen-lockfile && pnpm build && cd ../../ && pnpm i --no-frozen-lockfile",
+ "ready": "pnpm i && cd packages/lucia && pnpm build && cd ../adapter-test && pnpm build && cd ../oauth && pnpm build && cd ../adapter-prisma && pnpm build && cd ../../",
+ "publish-setup": "pnpm i --no-frozen-lockfile && cd packages/lucia && pnpm build && cd ../adapter-test && pnpm build && cd ../../",
"format": "pnpm exec prettier --write .",
"preinstall": "npx only-allow pnpm",
"auri.format": "pnpm format",
@@ -21,7 +21,7 @@
"@types/node": "~18.15.13",
"@typescript-eslint/eslint-plugin": "^5.59.6",
"@typescript-eslint/parser": "^5.59.6",
- "auri": "^0.5.4",
+ "auri": "^0.6.0",
"eslint": "^8.40.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-svelte3": "^4.0.0",
@@ -29,15 +29,10 @@
"prettier-plugin-svelte": "^2.10.0",
"prettier-plugin-tailwindcss": "^0.2.8",
"shx": "^0.3.4",
- "typescript": "~5.0.4"
- },
- "resolutions": {
- "lucia-auth": "workspace:*",
- "@lucia-auth/adapter-prisma": "workspace:*",
- "@lucia-auth/oauth": "workspace:*"
+ "typescript": "latest"
},
"engines": {
- "node": "16.x - 20.x",
+ "node": "20.x",
"pnpm": "*"
}
}
diff --git a/packages/adapter-kysely/CHANGELOG.md b/packages/adapter-kysely/CHANGELOG.md
deleted file mode 100644
index 14abfbba2..000000000
--- a/packages/adapter-kysely/CHANGELOG.md
+++ /dev/null
@@ -1,95 +0,0 @@
-# @lucia-auth/adapter-kysely
-
-## 1.0.1
-
-### Patch changes
-
-- [#463](https://github.com/pilcrowOnPaper/lucia/pull/463) by [@pilcrowOnPaper](https://github.com/pilcrowOnPaper) : Fix `deleteSession()`
-
-## 1.0.0
-
-### Major changes
-
-- [#443](https://github.com/pilcrowOnPaper/lucia/pull/443) by [@pilcrowOnPaper](https://github.com/pilcrowOnPaper) : Release version 1.0!
-
-## 0.8.0
-
-### Minor changes
-
-- [#430](https://github.com/pilcrowOnPaper/lucia/pull/430) by [@pilcrowOnPaper](https://github.com/pilcrowOnPaper) : [Breaking] Require `lucia-auth` 0.11.0
-
- - Update schema
-
-## 0.7.1
-
-### Patch changes
-
-- [#424](https://github.com/pilcrowOnPaper/lucia/pull/424) by [@pilcrowOnPaper](https://github.com/pilcrowOnPaper) : - Update dependencies
-
-## 0.7.0
-
-### Minor changes
-
-- [#398](https://github.com/pilcrowOnPaper/lucia/pull/398) by [@pilcrowOnPaper](https://github.com/pilcrowOnPaper) : Require `lucia-auth@0.9.0`
-
-## 0.6.3
-
-### Patch changes
-
-- [#392](https://github.com/pilcrowOnPaper/lucia/pull/392) by [@pilcrowOnPaper](https://github.com/pilcrowOnPaper) : Update peer dependency
-
-## 0.6.2
-
-### Patch changes
-
-- [#388](https://github.com/pilcrowOnPaper/lucia/pull/388) by [@pilcrowOnPaper](https://github.com/pilcrowOnPaper) : remove unnecessary code
-
-## 0.6.1
-
-### Patch changes
-
-- [#381](https://github.com/pilcrowOnPaper/lucia/pull/381) by [@pilcrowOnPaper](https://github.com/pilcrowOnPaper) : Update links in README and package.json
-
-## 0.6.0
-
-- [Breaking] Require minimum `lucia-auth` 0.7.0
-
-## 0.5.0
-
-- [Breaking] Require minimum `lucia-auth` 0.6.0
-
-## 0.4.1
-
-- Fix `lucia-auth` peer dependency
-
-## 0.4.0
-
-- [Breaking] Require minimum `lucia-auth` 0.5.0
-
-## 0.3.1
-
-- [Fix] Proper type checking #297
-
-- Support `kysely@0.23.0`
-
-- Move `kysely` to dev dependencies
-
-## 0.3.0
-
-- [Breaking] Require `dialect` parameter
-
-- Support MySQL
-
-- Support SQLite
-
-- Export type `KyselyLuciaDatabase`, `KyselyUser`, `KyselySession`
-
-## 0.2.0
-
-- [Breaking] Require minimum `lucia-auth` 0.4.0
-
-- [Breaking] Remove global error handler
-
-## 0.1.1
-
-- Update peer dependency
diff --git a/packages/adapter-kysely/README.md b/packages/adapter-kysely/README.md
deleted file mode 100644
index a6ca0ee6a..000000000
--- a/packages/adapter-kysely/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# `@lucia-auth/adapter-kysely`
-
-This has been deprecated in favor of the [MySQL adapter](https://lucia-auth.com/database/mysql), [PostgreSQL adapter](https://lucia-auth.com/database/postgresql), and [SQLite adapter](https://lucia-auth.com/database/sqlite). Please refer to [Kysely](https://lucia-auth.com/database/kysely) to learn how to use these adapters with Kyseky.
diff --git a/packages/adapter-mongoose/.env.example b/packages/adapter-mongoose/.env.example
new file mode 100644
index 000000000..cc3e1ef61
--- /dev/null
+++ b/packages/adapter-mongoose/.env.example
@@ -0,0 +1 @@
+MONGODB_URL=""
\ No newline at end of file
diff --git a/packages/adapter-mongoose/.gitignore b/packages/adapter-mongoose/.gitignore
index e30934148..7a0ae98eb 100644
--- a/packages/adapter-mongoose/.gitignore
+++ b/packages/adapter-mongoose/.gitignore
@@ -1,4 +1,5 @@
/node_modules
/dist
.DS_Store
-.env
\ No newline at end of file
+.env
+*.tgz
\ No newline at end of file
diff --git a/packages/adapter-mongoose/.npmignore b/packages/adapter-mongoose/.npmignore
deleted file mode 100644
index 3c3629e64..000000000
--- a/packages/adapter-mongoose/.npmignore
+++ /dev/null
@@ -1 +0,0 @@
-node_modules
diff --git a/packages/adapter-mongoose/package.json b/packages/adapter-mongoose/package.json
index eb60efa6a..3c5762660 100644
--- a/packages/adapter-mongoose/package.json
+++ b/packages/adapter-mongoose/package.json
@@ -2,21 +2,22 @@
"name": "@lucia-auth/adapter-mongoose",
"version": "2.0.0",
"description": "Mongoose (MongoDB) adapter for Lucia",
- "main": "index.js",
- "types": "index.d.ts",
- "module": "index.js",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "module": "dist/index.js",
"type": "module",
"files": [
- "**/*"
+ "/dist/",
+ "CHANGELOG.md"
],
"scripts": {
- "build": "shx rm -rf ./dist/* && tsc && shx cp ./package.json ./dist && shx cp ./README.md ./dist && shx cp .npmignore dist",
+ "build": "shx rm -rf ./dist/* && tsc",
"test": "tsx test/index.ts",
- "auri.publish": "pnpm build && cd dist && pnpm install --no-frozen-lockfile && pnpm publish --no-git-checks --access public && cd ../"
+ "auri.publish": "pnpm build && pnpm publish --no-git-checks --access public"
},
"keywords": [
"lucia",
- "lucia-auth",
+ "lucia",
"auth",
"authentication",
"adapter",
@@ -32,17 +33,17 @@
"author": "pilcrowonpaper",
"license": "MIT",
"exports": {
- ".": "./index.js"
+ ".": "./dist/index.js"
},
"peerDependencies": {
- "lucia-auth": "^1.3.0",
+ "lucia": "^2.0.0",
"mongoose": "6.x - 7.x"
},
"devDependencies": {
- "@lucia-auth/adapter-test": "workspace:*",
+ "@lucia-auth/adapter-test": "latest",
"dotenv": "^16.0.3",
"tsx": "^3.12.6",
"mongoose": "^6.6.1",
- "lucia-auth": "workspace:*"
+ "lucia": "latest"
}
}
diff --git a/packages/adapter-mongoose/src/docs.ts b/packages/adapter-mongoose/src/docs.ts
index 2dcae0be0..bb199f31e 100644
--- a/packages/adapter-mongoose/src/docs.ts
+++ b/packages/adapter-mongoose/src/docs.ts
@@ -1,10 +1,12 @@
+import type {
+ GlobalDatabaseUserAttributes,
+ GlobalDatabaseSessionAttributes
+} from "lucia";
+
export type UserDoc = {
_id: string;
__v?: any;
- _doc?: any;
- $__?: any;
- username: string;
-};
+} & GlobalDatabaseUserAttributes;
export type SessionDoc = {
_id: string;
@@ -12,13 +14,11 @@ export type SessionDoc = {
active_expires: number;
user_id: string;
idle_expires: number;
-};
+} & GlobalDatabaseSessionAttributes;
export type KeyDoc = {
_id: string;
__v?: any;
user_id: string;
- hashed_password?: string | null;
- primary_key: boolean;
- expires?: number | null;
+ hashed_password?: string;
};
diff --git a/packages/adapter-mongoose/src/index.ts b/packages/adapter-mongoose/src/index.ts
index 218797177..1c21fefc6 100644
--- a/packages/adapter-mongoose/src/index.ts
+++ b/packages/adapter-mongoose/src/index.ts
@@ -1,199 +1 @@
-import type { Mongoose } from "mongoose";
-import {
- transformKeyDoc,
- transformSessionDoc,
- transformUserDoc
-} from "./utils.js";
-import type { Adapter, AdapterFunction } from "lucia-auth";
-import type { UserDoc, SessionDoc, KeyDoc } from "./docs.js";
-
-const createMongoValues = (object: Record) => {
- return Object.fromEntries(
- Object.entries(object).map(([key, value]) => {
- if (key === "id") return ["_id", value];
- return [key, value];
- })
- );
-};
-
-const DEFAULT_PROJECTION = {
- $__: 0,
- __v: 0,
- _doc: 0
-};
-
-const adapter = (mongoose: Mongoose): AdapterFunction => {
- const User = mongoose.model("auth_user");
- const Session = mongoose.model("auth_session");
- const Key = mongoose.model("auth_key");
- return (LuciaError) => {
- return {
- getUser: async (userId: string) => {
- const userDoc = await User.findById(userId, DEFAULT_PROJECTION).lean();
- if (!userDoc) return null;
- return transformUserDoc(userDoc);
- },
- getSessionAndUserBySessionId: async (sessionId) => {
- const session = await Session.findById(
- sessionId,
- DEFAULT_PROJECTION
- ).lean();
- if (!session) return null;
- const user = await User.findById(
- session.user_id,
- DEFAULT_PROJECTION
- ).lean();
- if (!user) return null;
- return {
- user: transformUserDoc(user),
- session: transformSessionDoc(session)
- };
- },
- getSession: async (sessionId) => {
- const session = await Session.findById(
- sessionId,
- DEFAULT_PROJECTION
- ).lean();
- if (!session) return null;
- return transformSessionDoc(session);
- },
- getSessionsByUserId: async (userId) => {
- const sessions = await Session.find(
- {
- user_id: userId
- },
- DEFAULT_PROJECTION
- ).lean();
- return sessions.map((val) => transformSessionDoc(val));
- },
- setUser: async (userId, userAttributes, key) => {
- if (key) {
- const refKeyDoc = await Key.findById(key.id, DEFAULT_PROJECTION);
- if (refKeyDoc) throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
- }
- const userDoc = new User(
- createMongoValues({
- id: userId,
- ...userAttributes
- })
- );
- await userDoc.save();
- try {
- if (key) {
- const keyDoc = new Key(createMongoValues(key));
- await keyDoc.save();
- }
- return transformUserDoc(userDoc.toObject());
- } catch (error) {
- await Key.findByIdAndDelete(userId);
- if (
- error instanceof Error &&
- error.message.includes("E11000") &&
- error.message.includes("id")
- ) {
- throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
- }
- throw error;
- }
- },
- deleteUser: async (userId: string) => {
- await User.findOneAndDelete({
- _id: userId
- });
- },
- setSession: async (session) => {
- const userDoc = await User.findById(
- session.user_id,
- DEFAULT_PROJECTION
- ).lean();
- if (!userDoc) throw new LuciaError("AUTH_INVALID_USER_ID");
- try {
- const sessionDoc = new Session(createMongoValues(session));
- await Session.create(sessionDoc);
- } catch (error) {
- if (
- error instanceof Error &&
- error.message.includes("E11000") &&
- error.message.includes("id")
- )
- throw new LuciaError("AUTH_DUPLICATE_SESSION_ID");
- throw error;
- }
- },
- deleteSession: async (sessionId) => {
- await Session.findByIdAndDelete(sessionId);
- },
- deleteSessionsByUserId: async (userId) => {
- await Session.deleteMany({
- user_id: userId
- });
- },
- updateUserAttributes: async (userId, attributes) => {
- const userDoc = await User.findByIdAndUpdate(userId, attributes, {
- new: true,
- projection: DEFAULT_PROJECTION
- }).lean();
- if (!userDoc) throw new LuciaError("AUTH_INVALID_USER_ID");
- return transformUserDoc(userDoc);
- },
- getKey: async (keyId) => {
- const keyDoc = await Key.findById(keyId, DEFAULT_PROJECTION).lean();
- if (!keyDoc) return null;
- const transformedKeyData = transformKeyDoc(keyDoc);
- return transformedKeyData;
- },
- setKey: async (key) => {
- const userDoc = await User.findById(key.user_id, DEFAULT_PROJECTION);
- if (!userDoc) throw new LuciaError("AUTH_INVALID_USER_ID");
- try {
- const keyDoc = new Key(createMongoValues(key));
- await Key.create(keyDoc);
- } catch (error) {
- if (
- error instanceof Error &&
- error.message.includes("E11000") &&
- error.message.includes("id")
- )
- throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
- throw error;
- }
- },
- getKeysByUserId: async (userId) => {
- const keyDocs = await Key.find(
- {
- user_id: userId
- },
- DEFAULT_PROJECTION
- ).lean();
- return keyDocs.map((val) => transformKeyDoc(val));
- },
- updateKeyPassword: async (key, hashedPassword) => {
- const keyDoc = await Key.findByIdAndUpdate(
- key,
- {
- hashed_password: hashedPassword
- },
- {
- new: true,
- projection: DEFAULT_PROJECTION
- }
- ).lean();
- if (!keyDoc) throw new LuciaError("AUTH_INVALID_KEY_ID");
- return transformKeyDoc(keyDoc);
- },
- deleteKeysByUserId: async (userId) => {
- await Key.deleteMany({
- user_id: userId
- });
- },
- deleteNonPrimaryKey: async (keyId) => {
- await Key.deleteOne({
- _id: keyId,
- primary_key: false
- });
- }
- };
- };
-};
-
-export default adapter;
+export { mongooseAdapter as mongoose } from "./mongoose.js";
diff --git a/packages/adapter-mongoose/src/lucia.d.ts b/packages/adapter-mongoose/src/lucia.d.ts
index f61de1891..8026ca988 100644
--- a/packages/adapter-mongoose/src/lucia.d.ts
+++ b/packages/adapter-mongoose/src/lucia.d.ts
@@ -1,5 +1,6 @@
-///
+///
declare namespace Lucia {
type Auth = any;
- type UserAttributes = {};
+ type DatabaseUserAttributes = any;
+ type DatabaseSessionAttributes = any;
}
diff --git a/packages/adapter-mongoose/src/mongoose.ts b/packages/adapter-mongoose/src/mongoose.ts
new file mode 100644
index 000000000..2def0ec16
--- /dev/null
+++ b/packages/adapter-mongoose/src/mongoose.ts
@@ -0,0 +1,179 @@
+import type { Model } from "mongoose";
+import type {
+ Adapter,
+ InitializeAdapter,
+ KeySchema,
+ UserSchema,
+ SessionSchema
+} from "lucia";
+import type { UserDoc, SessionDoc, KeyDoc } from "./docs.js";
+
+export const DEFAULT_PROJECTION = {
+ $__: 0,
+ __v: 0,
+ _doc: 0
+};
+
+export const mongooseAdapter = (models: {
+ User: Model;
+ Session: Model;
+ Key: Model;
+}): InitializeAdapter => {
+ const { User, Session, Key } = models;
+ return (LuciaError) => {
+ return {
+ getUser: async (userId: string) => {
+ const userDoc = await User.findById(userId, DEFAULT_PROJECTION).lean();
+ if (!userDoc) return null;
+ return transformUserDoc(userDoc);
+ },
+ setUser: async (user, key) => {
+ if (key) {
+ const refKeyDoc = await Key.findById(key.id, DEFAULT_PROJECTION);
+ if (refKeyDoc) throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
+ }
+ const userDoc = new User(createMongoValues(user));
+ await userDoc.save();
+ if (!key) return;
+ try {
+ const keyDoc = new Key(createMongoValues(key));
+ await keyDoc.save();
+ } catch (error) {
+ await Key.findByIdAndDelete(user.id);
+ if (
+ error instanceof Error &&
+ error.message.includes("E11000") &&
+ error.message.includes("id")
+ ) {
+ throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
+ }
+ throw error;
+ }
+ },
+ deleteUser: async (userId: string) => {
+ await User.findByIdAndDelete(userId);
+ },
+ updateUser: async (userId, partialUser) => {
+ await User.findByIdAndUpdate(userId, partialUser, {
+ new: true,
+ projection: DEFAULT_PROJECTION
+ }).lean();
+ },
+
+ getSession: async (sessionId) => {
+ const session = await Session.findById(
+ sessionId,
+ DEFAULT_PROJECTION
+ ).lean();
+ if (!session) return null;
+ return transformSessionDoc(session);
+ },
+ getSessionsByUserId: async (userId) => {
+ const sessions = await Session.find(
+ {
+ user_id: userId
+ },
+ DEFAULT_PROJECTION
+ ).lean();
+ return sessions.map((val) => transformSessionDoc(val));
+ },
+ setSession: async (session) => {
+ const sessionDoc = new Session(createMongoValues(session));
+ await sessionDoc.save();
+ },
+ deleteSession: async (sessionId) => {
+ await Session.findByIdAndDelete(sessionId);
+ },
+ deleteSessionsByUserId: async (userId) => {
+ await Session.deleteMany({
+ user_id: userId
+ });
+ },
+ updateSession: async (sessionId, partialUser) => {
+ await Session.findByIdAndUpdate(sessionId, partialUser, {
+ new: true,
+ projection: DEFAULT_PROJECTION
+ }).lean();
+ },
+
+ getKey: async (keyId) => {
+ const keyDoc = await Key.findById(keyId, DEFAULT_PROJECTION).lean();
+ if (!keyDoc) return null;
+ return transformKeyDoc(keyDoc);
+ },
+ setKey: async (key) => {
+ try {
+ const keyDoc = new Key(createMongoValues(key));
+ await Key.create(keyDoc);
+ } catch (error) {
+ if (
+ error instanceof Error &&
+ error.message.includes("E11000") &&
+ error.message.includes("id")
+ ) {
+ throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
+ }
+ throw error;
+ }
+ },
+ getKeysByUserId: async (userId) => {
+ const keyDocs = await Key.find(
+ {
+ user_id: userId
+ },
+ DEFAULT_PROJECTION
+ ).lean();
+ return keyDocs.map((val) => transformKeyDoc(val));
+ },
+ deleteKey: async (keyId) => {
+ await Key.findByIdAndDelete(keyId);
+ },
+ deleteKeysByUserId: async (userId) => {
+ await Key.deleteMany({
+ user_id: userId
+ });
+ },
+ updateKey: async (keyId, partialKey) => {
+ await Key.findByIdAndUpdate(keyId, partialKey, {
+ new: true,
+ projection: DEFAULT_PROJECTION
+ }).lean();
+ }
+ };
+ };
+};
+
+export const createMongoValues = (object: Record) => {
+ return Object.fromEntries(
+ Object.entries(object).map(([key, value]) => {
+ if (key === "id") return ["_id", value];
+ return [key, value];
+ })
+ );
+};
+
+export const transformUserDoc = (row: UserDoc): UserSchema => {
+ delete row.__v;
+ const { _id: id, ...attributes } = row;
+ return {
+ id,
+ ...attributes
+ };
+};
+
+export const transformSessionDoc = (row: SessionDoc): SessionSchema => {
+ delete row.__v;
+ const { _id: id, ...attributes } = row;
+ return {
+ id,
+ ...attributes
+ };
+};
+
+export const transformKeyDoc = (row: KeyDoc): KeySchema => {
+ return {
+ id: row._id,
+ user_id: row.user_id,
+ hashed_password: row.hashed_password ?? null
+ };
+};
diff --git a/packages/adapter-mongoose/src/utils.ts b/packages/adapter-mongoose/src/utils.ts
deleted file mode 100644
index 6d4e1feaa..000000000
--- a/packages/adapter-mongoose/src/utils.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import type { KeySchema, SessionSchema, UserSchema } from "lucia-auth";
-import type { UserDoc, SessionDoc, KeyDoc } from "./docs.js";
-
-export const transformUserDoc = (row: UserDoc): UserSchema => {
- delete row.$__;
- delete row.__v;
- delete row._doc;
- const { _id: id, ...attributes } = row;
- return {
- id,
- ...attributes
- };
-};
-
-export const transformSessionDoc = (row: SessionDoc): SessionSchema => {
- return {
- id: row._id,
- user_id: row.user_id,
- active_expires: row.active_expires,
- idle_expires: row.idle_expires
- };
-};
-
-export const transformKeyDoc = (row: KeyDoc): KeySchema => {
- return {
- id: row._id,
- user_id: row.user_id,
- hashed_password: row.hashed_password ?? null,
- primary_key: row.primary_key,
- expires: row.expires ?? null
- };
-};
diff --git a/packages/adapter-mongoose/test/db.ts b/packages/adapter-mongoose/test/db.ts
index afd81e2b1..1d0b87657 100644
--- a/packages/adapter-mongoose/test/db.ts
+++ b/packages/adapter-mongoose/test/db.ts
@@ -1,46 +1,36 @@
-import mongoose from "mongoose";
-import type {
- LuciaQueryHandler,
- TestUserSchema
-} from "@lucia-auth/adapter-test";
-import mongodb from "../src/index.js";
-
+import mongodb from "mongoose";
import dotenv from "dotenv";
import { resolve } from "path";
-import { transformKeyDoc, transformSessionDoc } from "../src/utils.js";
-import { LuciaError } from "lucia-auth";
dotenv.config({
path: `${resolve()}/.env`
});
-const url = process.env.MONGODB_URL;
-
-if (!url) throw new Error(".env is not set up");
-
-const User = mongoose.model(
- "auth_user",
- new mongoose.Schema(
+export const User = mongodb.model(
+ "User",
+ new mongodb.Schema(
{
_id: {
- type: String
+ type: String,
+ required: true
},
username: {
unique: true,
type: String,
required: true
}
- },
+ } as const,
{ _id: false }
)
);
-const Session = mongoose.model(
- "auth_session",
- new mongoose.Schema(
+export const Session = mongodb.model(
+ "Session",
+ new mongodb.Schema(
{
_id: {
- type: String
+ type: String,
+ required: true
},
user_id: {
type: String,
@@ -53,99 +43,34 @@ const Session = mongoose.model(
idle_expires: {
type: Number,
required: true
+ },
+ country: {
+ type: String,
+ required: true
}
- },
+ } as const,
{ _id: false }
)
);
-const Key = mongoose.model(
- "auth_key",
- new mongoose.Schema(
+export const Key = mongodb.model(
+ "Key",
+ new mongodb.Schema(
{
_id: {
- type: String
- },
- user_id: {
type: String,
required: true
},
- hashed_password: String,
- primary_key: {
- type: Boolean,
+ user_id: {
+ type: String,
required: true
},
- expires: Number
- },
+ hashed_password: String
+ } as const,
{ _id: false }
)
);
-const clientPromise = mongoose.connect(url);
-
-export const adapter = mongodb(mongoose)(LuciaError);
-
-const inputToMongooseDoc = (obj: Record) => {
- if (obj.id === undefined) return obj;
- const { id, ...data } = obj;
- return {
- _id: id,
- ...data
- };
-};
-export const queryHandler: LuciaQueryHandler = {
- user: {
- get: async () => {
- await clientPromise;
- const userDocs = await User.find().lean();
- return userDocs.map((doc) => {
- const { _id: id, ...attributes } = doc;
- return {
- id,
- ...attributes
- } as TestUserSchema;
- });
- },
- insert: async (user) => {
- await clientPromise;
- const userDoc = new User(inputToMongooseDoc(user));
- await userDoc.save();
- },
- clear: async () => {
- await clientPromise;
- await User.deleteMany().lean();
- }
- },
- session: {
- get: async () => {
- await clientPromise;
- const sessionDocs = await Session.find().lean();
- return sessionDocs.map((doc) => transformSessionDoc(doc));
- },
- insert: async (session) => {
- await clientPromise;
- const sessionDoc = new Session(inputToMongooseDoc(session));
- await sessionDoc.save();
- },
- clear: async () => {
- await clientPromise;
- await Session.deleteMany().lean();
- }
- },
- key: {
- get: async () => {
- await clientPromise;
- const keyDocs = await Key.find().lean();
- return keyDocs.map((doc) => transformKeyDoc(doc));
- },
- insert: async (key) => {
- await clientPromise;
- const keyDoc = new Key(inputToMongooseDoc(key));
- await keyDoc.save();
- },
- clear: async () => {
- await clientPromise;
- await Key.deleteMany().lean();
- }
- }
+export const connect = async () => {
+ await mongodb.connect(process.env.MONGODB_URL as any);
};
diff --git a/packages/adapter-mongoose/test/index.ts b/packages/adapter-mongoose/test/index.ts
index b90f131ca..e2125469c 100644
--- a/packages/adapter-mongoose/test/index.ts
+++ b/packages/adapter-mongoose/test/index.ts
@@ -1,4 +1,63 @@
-import { testAdapter } from "@lucia-auth/adapter-test";
-import { queryHandler, adapter } from "./db.js";
+import { Model } from "mongoose";
+import { testAdapter, Database } from "@lucia-auth/adapter-test";
+import { LuciaError } from "lucia";
-testAdapter(adapter, queryHandler);
+import { mongoose } from "../src/index.js";
+import {
+ createMongoValues,
+ transformKeyDoc,
+ transformSessionDoc,
+ transformUserDoc
+} from "../src/mongoose.js";
+import { User, Key, Session, connect } from "./db.js";
+
+import type { QueryHandler, TableQueryHandler } from "@lucia-auth/adapter-test";
+
+const createPartialTableQueryHandler = (
+ Model: Model
+): Pick => {
+ return {
+ insert: async (value) => {
+ const sessionDoc = new Model(createMongoValues(value));
+ await sessionDoc.save();
+ },
+ clear: async () => {
+ await Model.deleteMany();
+ }
+ };
+};
+
+const queryHandler: QueryHandler = {
+ user: {
+ get: async () => {
+ const userDocs = await User.find().lean();
+ return userDocs.map((doc) => transformUserDoc(doc) as any);
+ },
+ ...createPartialTableQueryHandler(User)
+ },
+ session: {
+ get: async () => {
+ const sessionDocs = await Session.find().lean();
+ return sessionDocs.map((doc) => transformSessionDoc(doc));
+ },
+ ...createPartialTableQueryHandler(Session)
+ },
+ key: {
+ get: async () => {
+ const keyDocs = await Key.find().lean();
+ return keyDocs.map((doc) => transformKeyDoc(doc));
+ },
+ ...createPartialTableQueryHandler(Key)
+ }
+};
+
+const adapter = mongoose({
+ User,
+ Session,
+ Key
+})(LuciaError);
+
+await connect();
+await testAdapter(adapter, new Database(queryHandler));
+
+process.exit(0);
diff --git a/packages/adapter-mysql/.env.example b/packages/adapter-mysql/.env.example
new file mode 100644
index 000000000..f79d76db0
--- /dev/null
+++ b/packages/adapter-mysql/.env.example
@@ -0,0 +1,6 @@
+MYSQL2_DATABASE=""
+MYSQL2_PASSWORD=""
+
+PLANETSCALE_HOST=""
+PLANETSCALE_USERNAME=""
+PLANETSCALE_PASSWORD=""
\ No newline at end of file
diff --git a/packages/adapter-mysql/.gitignore b/packages/adapter-mysql/.gitignore
index 27a3b5d40..7a0ae98eb 100644
--- a/packages/adapter-mysql/.gitignore
+++ b/packages/adapter-mysql/.gitignore
@@ -1,7 +1,5 @@
/node_modules
/dist
.DS_Store
-/prisma/migrations
-/prisma/dev.db
-/prisma/dev.db-journal
.env
+*.tgz
\ No newline at end of file
diff --git a/packages/adapter-mysql/.npmignore b/packages/adapter-mysql/.npmignore
deleted file mode 100644
index e1ac86668..000000000
--- a/packages/adapter-mysql/.npmignore
+++ /dev/null
@@ -1,8 +0,0 @@
-/node_modules
-.DS_Store
-/src
-/tsconfig.json
-.gitignore
-.env
-/prisma
-/test
\ No newline at end of file
diff --git a/packages/adapter-mysql/README.md b/packages/adapter-mysql/README.md
index 61b35b6c5..3a58770f2 100644
--- a/packages/adapter-mysql/README.md
+++ b/packages/adapter-mysql/README.md
@@ -48,7 +48,7 @@ Set up env var:
```bash
PLANETSCALE_HOST=""
PLANETSCALE_USERNAME=""
-PLANETSCALE_PASSWORD= ""
+PLANETSCALE_PASSWORD=""
```
Run:
diff --git a/packages/adapter-mysql/package.json b/packages/adapter-mysql/package.json
index 83026f237..98f304132 100644
--- a/packages/adapter-mysql/package.json
+++ b/packages/adapter-mysql/package.json
@@ -2,43 +2,44 @@
"name": "@lucia-auth/adapter-mysql",
"version": "1.1.1",
"description": "MySQL adapter for Lucia",
- "main": "index.js",
- "types": "index.d.ts",
- "module": "index.js",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "module": "dist/index.js",
"type": "module",
"files": [
- "**/*"
+ "/dist/",
+ "CHANGELOG.md"
],
"scripts": {
- "build": "shx rm -rf ./dist/* && tsc && shx cp ./package.json ./dist && shx cp ./README.md ./dist && shx cp .npmignore dist",
- "test.mysql2": " tsx test/mysql2/index.ts",
- "test.planetscale": " tsx test/planetscale/index.ts",
- "auri.publish": "pnpm build && cd dist && pnpm install --no-frozen-lockfile && pnpm publish --no-git-checks --access public && cd ../"
+ "build": "shx rm -rf ./dist/* && tsc",
+ "test.mysql2": "tsx test/mysql2/index.ts",
+ "test.planetscale": "tsx test/planetscale/index.ts",
+ "test-setup.planetscale": "tsx test/planetscale/setup.ts",
+ "test-setup.mysql2": "tsx test/mysql2/setup.ts",
+ "auri.publish": "pnpm build && pnpm publish --no-git-checks --access public"
},
"keywords": [
"lucia",
- "lucia-auth",
"auth",
+ "mysql2",
"mysql",
- "mysql",
+ "planetscale",
"authentication",
"adapter",
- "sql",
- "kysely",
- "drizzle"
+ "sql"
],
"repository": {
"type": "git",
"url": "https://github.com/pilcrowOnPaper/lucia",
- "directory": "packages/adapter-prisma"
+ "directory": "packages/adapter-mysql"
},
"author": "pilcrowonpaper",
"license": "MIT",
"exports": {
- ".": "./index.js"
+ ".": "./dist/index.js"
},
"peerDependencies": {
- "lucia-auth": "^1.4.0",
+ "lucia": "^2.0.0",
"mysql2": "^3.0.0",
"@planetscale/database": "^1.0.0"
},
@@ -51,10 +52,10 @@
}
},
"devDependencies": {
- "@lucia-auth/adapter-test": "workspace:*",
+ "@lucia-auth/adapter-test": "latest",
"@planetscale/database": "^1.7.0",
"dotenv": "^16.0.3",
- "lucia-auth": "workspace:*",
+ "lucia": "latest",
"mysql2": "^3.2.3",
"tsx": "^3.12.6"
}
diff --git a/packages/adapter-mysql/src/core.ts b/packages/adapter-mysql/src/core.ts
deleted file mode 100644
index c54b776a9..000000000
--- a/packages/adapter-mysql/src/core.ts
+++ /dev/null
@@ -1,177 +0,0 @@
-import { transformDatabaseSession, transformDatabaseKey } from "./utils.js";
-
-import type {
- MySQLUserSchema,
- MySQLSessionSchema,
- MySQLKeySchema
-} from "./utils.js";
-import type { Adapter, KeySchema, SessionSchema } from "lucia-auth";
-import type { Operator } from "./query.js";
-
-export const createCoreAdapter = (operator: Operator) => {
- return {
- getUser: async (userId) => {
- return operator.get((ctx) => [
- ctx.selectFrom("auth_user", "*"),
- ctx.where("id", "=", userId)
- ]);
- },
- getSessionAndUserBySessionId: async (sessionId) => {
- const data = await operator.get<
- MySQLUserSchema & {
- _session_active_expires: number;
- _session_id: string;
- _session_idle_expires: number;
- _session_user_id: string;
- }
- >((ctx) => [
- ctx.selectFrom(
- "auth_session",
- "auth_user.*",
- "auth_session.id as _session_id",
- "auth_session.active_expires as _session_active_expires",
- "auth_session.idle_expires as _session_idle_expires",
- "auth_session.user_id as _session_user_id"
- ),
- ctx.innerJoin("auth_user", "auth_user.id", "auth_session.user_id"),
- ctx.where("auth_session.id", "=", sessionId)
- ]);
- if (!data) return null;
- const {
- _session_active_expires,
- _session_id,
- _session_idle_expires,
- _session_user_id,
- ...user
- } = data;
- return {
- user,
- session: transformDatabaseSession({
- id: _session_id,
- user_id: _session_user_id,
- active_expires: _session_active_expires,
- idle_expires: _session_idle_expires
- })
- };
- },
- getSession: async (sessionId) => {
- const databaseSession = await operator.get((ctx) => [
- ctx.selectFrom("auth_session", "*"),
- ctx.where("id", "=", sessionId)
- ]);
- if (!databaseSession) return null;
- return transformDatabaseSession(databaseSession);
- },
- getSessionsByUserId: async (userId) => {
- const databaseSessions = await operator.getAll(
- (ctx) => [
- ctx.selectFrom("auth_session", "*"),
- ctx.where("user_id", "=", userId)
- ]
- );
- return databaseSessions.map((val) => transformDatabaseSession(val));
- },
- deleteUser: async (userId) => {
- await operator.run((ctx) => [
- ctx.deleteFrom("auth_user"),
- ctx.where("id", "=", userId)
- ]);
- },
- setSession: async (session) => {
- await operator.run((ctx) => [ctx.insertInto("auth_session", session)]);
- },
- deleteSession: async (sessionId) => {
- await operator.run((ctx) => [
- ctx.deleteFrom("auth_session"),
- ctx.where("id", "=", sessionId)
- ]);
- },
- deleteSessionsByUserId: async (userId) => {
- await operator.run((ctx) => [
- ctx.deleteFrom("auth_session"),
- ctx.where("user_id", "=", userId)
- ]);
- },
- updateUserAttributes: async (userId, attributes) => {
- if (Object.keys(attributes).length === 0) {
- await operator.run((ctx) => [
- ctx.selectFrom("auth_user", "*"),
- ctx.where("id", "=", userId)
- ]);
- return;
- }
- await operator.run((ctx) => [
- ctx.update("auth_user", attributes),
- ctx.where("id", "=", userId)
- ]);
- },
- setKey: async (key) => {
- await operator.run((ctx) => [ctx.insertInto("auth_key", key)]);
- },
- getKey: async (keyId) => {
- const databaseKey = await operator.get((ctx) => [
- ctx.selectFrom("auth_key", "*"),
- ctx.where("id", "=", keyId)
- ]);
- if (!databaseKey) return null;
- const transformedDatabaseKey = transformDatabaseKey(databaseKey);
- return transformedDatabaseKey;
- },
- getKeysByUserId: async (userId) => {
- const databaseKeys = await operator.getAll((ctx) => [
- ctx.selectFrom("auth_key", "*"),
- ctx.where("user_id", "=", userId)
- ]);
- return databaseKeys.map((val) => transformDatabaseKey(val));
- },
- updateKeyPassword: async (key, hashedPassword) => {
- await operator.run((ctx) => [
- ctx.update("auth_key", {
- hashed_password: hashedPassword
- }),
- ctx.where("id", "=", key)
- ]);
- },
- deleteKeysByUserId: async (userId) => {
- await operator.run((ctx) => [
- ctx.deleteFrom("auth_key"),
- ctx.where("user_id", "=", userId)
- ]);
- },
- deleteNonPrimaryKey: async (keyId) => {
- await operator.run((ctx) => [
- ctx.deleteFrom("auth_key"),
- ctx.and(
- ctx.where("id", "=", keyId),
- ctx.where("primary_key", "=", false)
- )
- ]);
- }
- } satisfies Partial;
-};
-
-export const createQueryHelper = (operator: Operator) => {
- return {
- getUser: async (userId: string) => {
- return await operator.get((ctx) => [
- ctx.selectFrom("auth_user", "*"),
- ctx.where("id", "=", userId)
- ]);
- },
- insertUser: async (userId: string, attributes: Record) => {
- const user = {
- id: userId,
- ...attributes
- };
- return await operator.run((ctx) => [
- ctx.insertInto("auth_user", user)
- ]);
- },
- insertSession: async (session: SessionSchema) => {
- await operator.run((ctx) => [ctx.insertInto("auth_session", session)]);
- },
- insertKey: async (key: KeySchema) => {
- await operator.run((ctx) => [ctx.insertInto("auth_key", key)]);
- }
- };
-};
diff --git a/packages/adapter-mysql/src/drivers/mysql2.ts b/packages/adapter-mysql/src/drivers/mysql2.ts
new file mode 100644
index 000000000..3bef5fe93
--- /dev/null
+++ b/packages/adapter-mysql/src/drivers/mysql2.ts
@@ -0,0 +1,298 @@
+import { helper, getSetArgs, escapeName } from "../utils.js";
+
+import type {
+ SessionSchema,
+ Adapter,
+ InitializeAdapter,
+ UserSchema,
+ KeySchema
+} from "lucia";
+import type {
+ Pool,
+ QueryError,
+ RowDataPacket,
+ OkPacket,
+ ResultSetHeader,
+ PoolConnection
+} from "mysql2/promise";
+
+export const mysql2Adapter = (
+ db: Pool,
+ tables: {
+ user: string;
+ session: string;
+ key: string;
+ }
+): InitializeAdapter => {
+ const ESCAPED_USER_TABLE_NAME = escapeName(tables.user);
+ const ESCAPED_SESSION_TABLE_NAME = escapeName(tables.session);
+ const ESCAPED_KEY_TABLE_NAME = escapeName(tables.key);
+
+ const transaction = async <
+ _Execute extends (connection: PoolConnection) => Promise
+ >(
+ execute: _Execute
+ ) => {
+ const connection = await db.getConnection();
+ try {
+ await connection.beginTransaction();
+ await execute(connection);
+ await connection.commit();
+ return;
+ } catch (e) {
+ await connection.rollback();
+ throw e;
+ }
+ };
+ return (LuciaError) => {
+ return {
+ getUser: async (userId) => {
+ const result = await get(
+ db.query(`SELECT * FROM ${ESCAPED_USER_TABLE_NAME} WHERE id = ?`, [
+ userId
+ ])
+ );
+ return result;
+ },
+ setUser: async (user, key) => {
+ if (!key) {
+ const [userFields, userValues, userArgs] = helper(user);
+ await db.execute(
+ `INSERT INTO ${ESCAPED_USER_TABLE_NAME} ( ${userFields} ) VALUES ( ${userValues} )`,
+ userArgs
+ );
+ return;
+ }
+ try {
+ await transaction(async (connection) => {
+ const [userFields, userValues, userArgs] = helper(user);
+ await connection.execute(
+ `INSERT INTO ${ESCAPED_USER_TABLE_NAME} ( ${userFields} ) VALUES ( ${userValues} )`,
+ userArgs
+ );
+ const [keyFields, keyValues, keyArgs] = helper(key);
+ await connection.execute(
+ `INSERT INTO ${ESCAPED_KEY_TABLE_NAME} ( ${keyFields} ) VALUES ( ${keyValues} )`,
+ keyArgs
+ );
+ });
+ } catch (e) {
+ const error = e as Partial;
+ if (
+ error.code === "ER_DUP_ENTRY" &&
+ error.message?.includes("PRIMARY") &&
+ error.message?.includes(tables.key)
+ ) {
+ throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
+ }
+ throw e;
+ }
+ },
+ deleteUser: async (userId) => {
+ await db.query(`DELETE FROM ${ESCAPED_USER_TABLE_NAME} WHERE id = ?`, [
+ userId
+ ]);
+ },
+ updateUser: async (userId, partialUser) => {
+ const [fields, values, args] = helper(partialUser);
+ await db.execute(
+ `UPDATE ${ESCAPED_USER_TABLE_NAME} SET ${getSetArgs(
+ fields,
+ values
+ )} WHERE id = ?`,
+ [...args, userId]
+ );
+ },
+
+ getSession: async (sessionId) => {
+ const result = await get(
+ db.query(`SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE id = ?`, [
+ sessionId
+ ])
+ );
+ return result;
+ },
+ getSessionsByUserId: async (userId) => {
+ const result = await getAll(
+ db.query(
+ `SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE user_id = ?`,
+ [userId]
+ )
+ );
+ return result;
+ },
+ setSession: async (session) => {
+ try {
+ const [fields, values, args] = helper(session);
+ await db.execute(
+ `INSERT INTO ${ESCAPED_SESSION_TABLE_NAME} ( ${fields} ) VALUES ( ${values} )`,
+ args
+ );
+ } catch (e) {
+ const error = e as Partial;
+ if (error.errno === 1452 && error.message?.includes("(`user_id`)")) {
+ throw new LuciaError("AUTH_INVALID_USER_ID");
+ }
+ throw e;
+ }
+ },
+ deleteSession: async (sessionId) => {
+ await db.execute(
+ `DELETE FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE id = ?`,
+ [sessionId]
+ );
+ },
+ deleteSessionsByUserId: async (userId) => {
+ await db.execute(
+ `DELETE FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE user_id = ?`,
+ [userId]
+ );
+ },
+ updateSession: async (sessionId, partialSession) => {
+ const [fields, values, args] = helper(partialSession);
+ await db.execute(
+ `UPDATE ${ESCAPED_SESSION_TABLE_NAME} SET ${getSetArgs(
+ fields,
+ values
+ )} WHERE id = ?`,
+ [...args, sessionId]
+ );
+ },
+
+ getKey: async (keyId) => {
+ const result = await get(
+ db.query(`SELECT * FROM ${ESCAPED_KEY_TABLE_NAME} WHERE id = ?`, [
+ keyId
+ ])
+ );
+ return result;
+ },
+ getKeysByUserId: async (userId) => {
+ const result = getAll(
+ db.execute(
+ `SELECT * FROM ${ESCAPED_KEY_TABLE_NAME} WHERE user_id = ?`,
+ [userId]
+ )
+ );
+ return result;
+ },
+ setKey: async (key) => {
+ try {
+ const [fields, values, args] = helper(key);
+ await db.execute(
+ `INSERT INTO ${ESCAPED_KEY_TABLE_NAME} ( ${fields} ) VALUES ( ${values} )`,
+ args
+ );
+ } catch (e) {
+ const error = e as Partial;
+ if (error.errno === 1452 && error.message?.includes("(`user_id`)")) {
+ throw new LuciaError("AUTH_INVALID_USER_ID");
+ }
+ if (
+ error.code === "ER_DUP_ENTRY" &&
+ error.message?.includes("PRIMARY") &&
+ error.message?.includes(tables.key)
+ ) {
+ throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
+ }
+ throw e;
+ }
+ },
+ deleteKey: async (keyId) => {
+ await db.execute(`DELETE FROM ${ESCAPED_KEY_TABLE_NAME} WHERE id = ?`, [
+ keyId
+ ]);
+ },
+ deleteKeysByUserId: async (userId) => {
+ await db.execute(
+ `DELETE FROM ${ESCAPED_KEY_TABLE_NAME} WHERE user_id = ?`,
+ [userId]
+ );
+ },
+ updateKey: async (keyId, partialKey) => {
+ const [fields, values, args] = helper(partialKey);
+ await db.execute(
+ `UPDATE ${ESCAPED_KEY_TABLE_NAME} SET ${getSetArgs(
+ fields,
+ values
+ )} WHERE id = ?`,
+ [...args, keyId]
+ );
+ },
+
+ getSessionAndUser: async (sessionId) => {
+ const getSessionPromise = get(
+ db.query(`SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE id = ?`, [
+ sessionId
+ ])
+ );
+ const getUserFromJoinPromise = get<
+ UserSchema & {
+ __session_id: string;
+ }
+ >(
+ db.query(
+ `SELECT ${ESCAPED_USER_TABLE_NAME}.*, ${ESCAPED_SESSION_TABLE_NAME}.id as __session_id FROM ${ESCAPED_SESSION_TABLE_NAME} INNER JOIN ${ESCAPED_USER_TABLE_NAME} ON ${ESCAPED_USER_TABLE_NAME}.id = ${ESCAPED_SESSION_TABLE_NAME}.user_id WHERE ${ESCAPED_SESSION_TABLE_NAME}.id = ?`,
+ [sessionId]
+ )
+ );
+ const [sessionResult, userFromJoinResult] = await Promise.all([
+ getSessionPromise,
+ getUserFromJoinPromise
+ ]);
+ if (!sessionResult || !userFromJoinResult) return [null, null];
+ const { __session_id: _, ...userResult } = userFromJoinResult;
+ return [sessionResult, userResult];
+ }
+ };
+ };
+};
+
+const isPacketArray = (
+ maybeRowDataPacketArray: RowDataPacket[] | RowDataPacket[][] | OkPacket[]
+): maybeRowDataPacketArray is RowDataPacket[] => {
+ const firstVal = maybeRowDataPacketArray.at(0) ?? null;
+ if (!firstVal) return true;
+ if (!Array.isArray(firstVal)) return true;
+ return false;
+};
+export const get = async (
+ queryPromise: Promise<
+ [
+ (
+ | RowDataPacket[]
+ | RowDataPacket[][]
+ | OkPacket
+ | OkPacket[]
+ | ResultSetHeader
+ ),
+ any
+ ]
+ >
+): Promise => {
+ const [rows] = await queryPromise;
+ if (!Array.isArray(rows)) return null;
+ const result = rows.at(0) ?? null;
+ if (!result || Array.isArray(result)) return null;
+ return result as any;
+};
+
+export const getAll = async (
+ queryPromise: Promise<
+ [
+ (
+ | RowDataPacket[]
+ | RowDataPacket[][]
+ | OkPacket
+ | OkPacket[]
+ | ResultSetHeader
+ ),
+ any
+ ]
+ >
+): Promise => {
+ const [rows] = await queryPromise;
+ if (!Array.isArray(rows)) return [];
+ if (!isPacketArray(rows)) return [];
+ return rows as any;
+};
diff --git a/packages/adapter-mysql/src/drivers/planetscale.ts b/packages/adapter-mysql/src/drivers/planetscale.ts
new file mode 100644
index 000000000..9965ec260
--- /dev/null
+++ b/packages/adapter-mysql/src/drivers/planetscale.ts
@@ -0,0 +1,256 @@
+import { escapeName, getSetArgs, helper } from "../utils.js";
+
+import type {
+ Connection,
+ DatabaseError,
+ ExecutedQuery
+} from "@planetscale/database";
+import type {
+ Adapter,
+ InitializeAdapter,
+ UserSchema,
+ SessionSchema,
+ KeySchema
+} from "lucia";
+
+export const planetscaleAdapter = (
+ connection: Connection,
+ tables: {
+ user: string;
+ session: string;
+ key: string;
+ }
+): InitializeAdapter => {
+ const ESCAPED_USER_TABLE_NAME = escapeName(tables.user);
+ const ESCAPED_SESSION_TABLE_NAME = escapeName(tables.session);
+ const ESCAPED_KEY_TABLE_NAME = escapeName(tables.key);
+
+ return (LuciaError) => {
+ return {
+ getUser: async (userId) => {
+ const result = await get(
+ connection.execute(
+ `SELECT * FROM ${ESCAPED_USER_TABLE_NAME} WHERE id = ?`,
+ [userId]
+ )
+ );
+ return result;
+ },
+ setUser: async (user, key) => {
+ if (!key) {
+ const [userFields, userValues, userArgs] = helper(user);
+ await connection.execute(
+ `INSERT INTO ${ESCAPED_USER_TABLE_NAME} ( ${userFields} ) VALUES ( ${userValues} )`,
+ userArgs
+ );
+ return;
+ }
+ try {
+ await connection.transaction(async (tx) => {
+ const [userFields, userValues, userArgs] = helper(user);
+ await tx.execute(
+ `INSERT INTO ${ESCAPED_USER_TABLE_NAME} ( ${userFields} ) VALUES ( ${userValues} )`,
+ userArgs
+ );
+ const [keyFields, keyValues, keyArgs] = helper(key);
+ await tx.execute(
+ `INSERT INTO ${ESCAPED_KEY_TABLE_NAME} ( ${keyFields} ) VALUES ( ${keyValues} )`,
+ keyArgs
+ );
+ });
+ } catch (e) {
+ const error = e as Partial;
+ if (
+ error.body?.message.includes("AlreadyExists") &&
+ error.body?.message.includes("PRIMARY") &&
+ error.body?.message.includes(`${tables.key}`)
+ ) {
+ throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
+ }
+ throw e;
+ }
+ },
+ deleteUser: async (userId) => {
+ await connection.execute(
+ `DELETE FROM ${ESCAPED_USER_TABLE_NAME} WHERE id = ?`,
+ [userId]
+ );
+ },
+ updateUser: async (userId, partialUser) => {
+ const [fields, values, args] = helper(partialUser);
+ await connection.execute(
+ `UPDATE ${ESCAPED_USER_TABLE_NAME} SET ${getSetArgs(
+ fields,
+ values
+ )} WHERE id = ?`,
+ [...args, userId]
+ );
+ },
+
+ getSession: async (sessionId) => {
+ const result = await get(
+ connection.execute(
+ `SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE id = ?`,
+ [sessionId]
+ )
+ );
+ return result ? transformPlanetscaleSession(result) : null;
+ },
+ getSessionsByUserId: async (userId) => {
+ const result = await getAll(
+ connection.execute(
+ `SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE user_id = ?`,
+ [userId]
+ )
+ );
+ return result.map((val) => transformPlanetscaleSession(val));
+ },
+ setSession: async (session) => {
+ const [fields, values, args] = helper(session);
+ await connection.execute(
+ `INSERT INTO ${ESCAPED_SESSION_TABLE_NAME} ( ${fields} ) VALUES ( ${values} )`,
+ args
+ );
+ },
+ deleteSession: async (sessionId) => {
+ await connection.execute(
+ `DELETE FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE id = ?`,
+ [sessionId]
+ );
+ },
+ deleteSessionsByUserId: async (userId) => {
+ await connection.execute(
+ `DELETE FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE user_id = ?`,
+ [userId]
+ );
+ },
+ updateSession: async (sessionId, partialSession) => {
+ const [fields, values, args] = helper(partialSession);
+ await connection.execute(
+ `UPDATE ${ESCAPED_SESSION_TABLE_NAME} SET ${getSetArgs(
+ fields,
+ values
+ )} WHERE id = ?`,
+ [...args, sessionId]
+ );
+ },
+
+ getKey: async (keyId) => {
+ const result = await get(
+ connection.execute(
+ `SELECT * FROM ${ESCAPED_KEY_TABLE_NAME} WHERE id = ?`,
+ [keyId]
+ )
+ );
+ return result;
+ },
+ getKeysByUserId: async (userId) => {
+ const result = getAll(
+ connection.execute(
+ `SELECT * FROM ${ESCAPED_KEY_TABLE_NAME} WHERE user_id = ?`,
+ [userId]
+ )
+ );
+ return result;
+ },
+ setKey: async (key) => {
+ try {
+ const [fields, values, args] = helper(key);
+ await connection.execute(
+ `INSERT INTO ${ESCAPED_KEY_TABLE_NAME} ( ${fields} ) VALUES ( ${values} )`,
+ args
+ );
+ } catch (e) {
+ const error = e as Partial;
+ if (
+ error.body?.message.includes("AlreadyExists") &&
+ error.body?.message.includes("PRIMARY") &&
+ error.body?.message.includes(`${tables.key}`)
+ ) {
+ throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
+ }
+ throw e;
+ }
+ },
+ deleteKey: async (keyId) => {
+ await connection.execute(
+ `DELETE FROM ${ESCAPED_KEY_TABLE_NAME} WHERE id = ?`,
+ [keyId]
+ );
+ },
+ deleteKeysByUserId: async (userId) => {
+ await connection.execute(
+ `DELETE FROM ${ESCAPED_KEY_TABLE_NAME} WHERE user_id = ?`,
+ [userId]
+ );
+ },
+ updateKey: async (keyId, partialKey) => {
+ const [fields, values, args] = helper(partialKey);
+ await connection.execute(
+ `UPDATE ${ESCAPED_KEY_TABLE_NAME} SET ${getSetArgs(
+ fields,
+ values
+ )} WHERE id = ?`,
+ [...args, keyId]
+ );
+ },
+
+ getSessionAndUser: async (sessionId) => {
+ const [sessionResult, userFromJoinResult] = await Promise.all([
+ get(
+ connection.execute(
+ `SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE id = ?`,
+ [sessionId]
+ )
+ ),
+ get<
+ UserSchema & {
+ __session_id: string;
+ }
+ >(
+ connection.execute(
+ `SELECT ${ESCAPED_USER_TABLE_NAME}.*, ${ESCAPED_SESSION_TABLE_NAME}.id as __session_id FROM ${ESCAPED_SESSION_TABLE_NAME} INNER JOIN ${ESCAPED_USER_TABLE_NAME} ON ${ESCAPED_USER_TABLE_NAME}.id = ${ESCAPED_SESSION_TABLE_NAME}.user_id WHERE ${ESCAPED_SESSION_TABLE_NAME}.id = ?`,
+ [sessionId]
+ )
+ )
+ ]);
+ if (!sessionResult || !userFromJoinResult) return [null, null];
+ const { __session_id: _, ...userResult } = userFromJoinResult;
+ return [transformPlanetscaleSession(sessionResult), userResult];
+ }
+ };
+ };
+};
+
+export const get = async (
+ queryPromise: Promise
+): Promise => {
+ const { rows } = await queryPromise;
+ const result = rows.at(0) ?? null;
+ return result as any;
+};
+
+export const getAll = async (
+ queryPromise: Promise
+): Promise => {
+ const { rows } = await queryPromise;
+ return rows as any;
+};
+
+export type PlanetscaleSession = Omit<
+ SessionSchema,
+ "active_expires" | "idle_expires"
+> & {
+ active_expires: BigInt;
+ idle_expires: BigInt;
+};
+
+export const transformPlanetscaleSession = (
+ session: PlanetscaleSession
+): SessionSchema => {
+ return {
+ ...session,
+ active_expires: Number(session.active_expires),
+ idle_expires: Number(session.idle_expires)
+ };
+};
diff --git a/packages/adapter-mysql/src/index.ts b/packages/adapter-mysql/src/index.ts
index 297f5d791..dda5e9380 100644
--- a/packages/adapter-mysql/src/index.ts
+++ b/packages/adapter-mysql/src/index.ts
@@ -1,2 +1,2 @@
-export { mysql2Adapter as mysql2 } from "./mysql2/index.js";
-export { planetscaleAdapter as planetscale } from "./planetscale/index.js";
+export { mysql2Adapter as mysql2 } from "./drivers/mysql2.js";
+// export { planetscaleAdapter as planetscale } from "./planetscale/index.js";
diff --git a/packages/adapter-mysql/src/lucia.d.ts b/packages/adapter-mysql/src/lucia.d.ts
index f61de1891..8026ca988 100644
--- a/packages/adapter-mysql/src/lucia.d.ts
+++ b/packages/adapter-mysql/src/lucia.d.ts
@@ -1,5 +1,6 @@
-///
+///
declare namespace Lucia {
type Auth = any;
- type UserAttributes = {};
+ type DatabaseUserAttributes = any;
+ type DatabaseSessionAttributes = any;
}
diff --git a/packages/adapter-mysql/src/mysql2/index.ts b/packages/adapter-mysql/src/mysql2/index.ts
deleted file mode 100644
index d0196f9be..000000000
--- a/packages/adapter-mysql/src/mysql2/index.ts
+++ /dev/null
@@ -1,91 +0,0 @@
-import { mysql2Runner } from "./runner.js";
-import { createCoreAdapter, createQueryHelper } from "../core.js";
-import { createOperator } from "../query.js";
-
-import type { Adapter, AdapterFunction } from "lucia-auth";
-import type { Pool, QueryError } from "mysql2/promise";
-
-export const mysql2Adapter = (db: Pool): AdapterFunction => {
- const transaction = async <_Execute extends () => Promise>(
- execute: _Execute
- ) => {
- const connection = await db.getConnection();
- try {
- await connection.beginTransaction();
- await execute();
- await connection.commit();
- return;
- } catch (e) {
- await connection.rollback();
- throw e;
- }
- };
-
- return (LuciaError) => {
- const operator = createOperator(mysql2Runner(db));
- const coreAdapter = createCoreAdapter(operator);
- const helper = createQueryHelper(operator);
- return {
- ...coreAdapter,
- setUser: async (userId, attributes, key) => {
- try {
- if (key) {
- await transaction(async () => {
- await helper.insertUser(userId, attributes);
- await helper.insertKey(key);
- });
- return;
- }
- await helper.insertUser(userId, attributes);
- } catch (e) {
- const error = e as Partial;
- if (
- error.code === "ER_DUP_ENTRY" &&
- error.message?.includes("PRIMARY")
- ) {
- throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
- }
- throw e;
- }
- },
- setSession: async (session) => {
- try {
- const user = await helper.getUser(session.user_id);
- if (!user) throw new LuciaError("AUTH_INVALID_USER_ID");
- await helper.insertSession(session);
- } catch (e) {
- const error = e as Partial;
- if (error.errno === 1452 && error.message?.includes("(`user_id`)")) {
- throw new LuciaError("AUTH_INVALID_USER_ID");
- }
- if (
- error.code === "ER_DUP_ENTRY" &&
- error.message?.includes("PRIMARY")
- ) {
- throw new LuciaError("AUTH_DUPLICATE_SESSION_ID");
- }
- throw e;
- }
- },
- setKey: async (key) => {
- try {
- const user = await helper.getUser(key.user_id);
- if (!user) throw new LuciaError("AUTH_INVALID_USER_ID");
- await helper.insertKey(key);
- } catch (e) {
- const error = e as Partial;
- if (error.errno === 1452 && error.message?.includes("(`user_id`)")) {
- throw new LuciaError("AUTH_INVALID_USER_ID");
- }
- if (
- error.code === "ER_DUP_ENTRY" &&
- error.message?.includes("PRIMARY")
- ) {
- throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
- }
- throw e;
- }
- }
- };
- };
-};
diff --git a/packages/adapter-mysql/src/mysql2/runner.ts b/packages/adapter-mysql/src/mysql2/runner.ts
deleted file mode 100644
index e1b7bf06e..000000000
--- a/packages/adapter-mysql/src/mysql2/runner.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import type { Pool } from "mysql2/promise";
-import type { Runner } from "../query.js";
-
-export const mysql2Runner = (pool: Pool): Runner => {
- return {
- get: async (query, params) => {
- const [rows] = await pool.query(query, params);
- return rows;
- },
- run: async (query, params) => {
- await pool.query(query, params);
- }
- };
-};
diff --git a/packages/adapter-mysql/src/planetscale/index.ts b/packages/adapter-mysql/src/planetscale/index.ts
deleted file mode 100644
index fc854134a..000000000
--- a/packages/adapter-mysql/src/planetscale/index.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import { createCoreAdapter, createQueryHelper } from "../core.js";
-import { planetscaleRunner } from "./runner.js";
-import { createOperator } from "../query.js";
-
-import type { Connection, DatabaseError } from "@planetscale/database";
-import type { Adapter, AdapterFunction } from "lucia-auth";
-
-export const planetscaleAdapter = (
- connection: Connection
-): AdapterFunction => {
- return (LuciaError) => {
- const operator = createOperator(planetscaleRunner(connection));
- const coreAdapter = createCoreAdapter(operator);
- const helper = createQueryHelper(operator);
- return {
- ...coreAdapter,
- setUser: async (userId, attributes, key) => {
- try {
- if (key) {
- await connection.transaction(async (trx) => {
- const trxOperator = createOperator(planetscaleRunner(trx));
- const trxHelper = createQueryHelper(trxOperator);
- await trxHelper.insertUser(userId, attributes);
- await trxHelper.insertKey(key);
- });
- return;
- }
- await helper.insertUser(userId, attributes);
- return;
- } catch (e) {
- const error = e as Partial;
- if (
- error.body?.message.includes("AlreadyExists") &&
- error.body?.message.includes("PRIMARY")
- ) {
- throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
- }
- throw e;
- }
- },
- setSession: async (session) => {
- try {
- const user = await helper.getUser(session.user_id);
- if (!user) throw new LuciaError("AUTH_INVALID_USER_ID");
- await helper.insertSession(session);
- } catch (e) {
- const error = e as Partial;
- if (
- error.body?.message.includes("AlreadyExists") &&
- error.body?.message.includes("PRIMARY")
- ) {
- throw new LuciaError("AUTH_DUPLICATE_SESSION_ID");
- }
- throw e;
- }
- },
- setKey: async (key) => {
- try {
- const user = await helper.getUser(key.user_id);
- if (!user) throw new LuciaError("AUTH_INVALID_USER_ID");
- await helper.insertKey(key);
- } catch (e) {
- const error = e as Partial;
- if (
- error.body?.message.includes("AlreadyExists") &&
- error.body?.message.includes("PRIMARY")
- ) {
- throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
- }
- throw e;
- }
- }
- };
- };
-};
diff --git a/packages/adapter-mysql/src/planetscale/runner.ts b/packages/adapter-mysql/src/planetscale/runner.ts
deleted file mode 100644
index ba773d1ea..000000000
--- a/packages/adapter-mysql/src/planetscale/runner.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import type { Connection } from "@planetscale/database";
-import type { Runner } from "../query.js";
-
-export const planetscaleRunner = (
- connection: Pick
-): Runner => {
- return {
- get: async (query, params) => {
- const { rows } = await connection.execute(query, params);
- return rows;
- },
- run: async (query, params) => {
- await connection.execute(query, params);
- }
- };
-};
diff --git a/packages/adapter-mysql/src/query.ts b/packages/adapter-mysql/src/query.ts
deleted file mode 100644
index 91e523023..000000000
--- a/packages/adapter-mysql/src/query.ts
+++ /dev/null
@@ -1,243 +0,0 @@
-const resolveQueryBlock = (block: Block): ResolvedBlock => {
- const escapeName = (val: string) => {
- if (val === "*") return val;
- return `\`${val}\``;
- };
- if (block.type === "DELETE_FROM") {
- return {
- queryChunk: `DELETE FROM ${escapeName(block.table)}`,
- params: []
- };
- }
- if (block.type === "INNER_JOIN") {
- return {
- queryChunk: `INNER JOIN ${escapeName(block.targetTable)} ON ${
- block.targetColumn
- } = ${block.column}`,
- params: []
- };
- }
- if (block.type === "INSERT_INTO") {
- const keys = Object.keys(block.values);
- return {
- queryChunk: `INSERT INTO ${escapeName(block.table)} (${keys.map((k) =>
- escapeName(k)
- )}) VALUES (${Array(keys.length).fill("?")})`,
- params: keys.map((k) => block.values[k])
- };
- }
- if (block.type === "SELECT") {
- return {
- queryChunk: `SELECT ${block.columns} FROM ${escapeName(block.table)}`,
- params: []
- };
- }
- if (block.type === "WHERE") {
- return {
- queryChunk: `WHERE ${block.column} ${block.comparator} ?`,
- params: [block.value]
- };
- }
- if (block.type === "UPDATE") {
- const keys = Object.keys(block.values);
- return {
- queryChunk: `UPDATE ${escapeName(block.table)} SET ${keys.map((k) => {
- return `${escapeName(k)} = ?`;
- })}`,
- params: keys.map((k) => block.values[k])
- };
- }
- if (block.type === "AND") {
- const resolvedConditionQueryBlocks = block.whereBlocks.map((whereBlock) => {
- return {
- queryChunk: `${whereBlock.column} ${whereBlock.comparator} ?`,
- params: [whereBlock.value]
- };
- });
- const conditionQueryChunk = resolvedConditionQueryBlocks
- .map((resolvedBlock) => resolvedBlock.queryChunk)
- .join(" AND ");
- return {
- queryChunk: `WHERE ${conditionQueryChunk}`,
- params: resolvedConditionQueryBlocks.reduce(
- (acc, curr) => [...acc, ...curr.params],
- [] as ColumnValue[]
- )
- };
- }
- throw new TypeError(`Invalid block type`);
-};
-
-const ctx = {
- innerJoin: (targetTable: string, targetColumn: string, column: string) => {
- return {
- type: "INNER_JOIN",
- targetTable,
- targetColumn,
- column
- };
- },
- selectFrom: (table: string, ...columns: [string, ...string[]]) => {
- return {
- type: "SELECT",
- table,
- columns
- };
- },
- insertInto: (table: string, values: Record) => {
- return {
- type: "INSERT_INTO",
- table,
- values
- };
- },
- where: (column: string, comparator: string, value: ColumnValue) => {
- return {
- type: "WHERE",
- column,
- comparator,
- value
- };
- },
- deleteFrom: (table: string) => {
- return {
- type: "DELETE_FROM",
- table
- };
- },
- update: (table: string, values: Record) => {
- return {
- type: "UPDATE",
- table,
- values
- };
- },
- and: (...whereBlocks: WhereBlock[]) => {
- return {
- type: "AND",
- whereBlocks
- };
- }
-} satisfies Record Block>;
-
-export const createOperator = <_Runner extends Runner>(runner: _Runner) => {
- const resolveQueryBlocks = (blocks: Block[]) => {
- const resolvedBlocks = blocks.map(resolveQueryBlock);
- const statement = resolvedBlocks.map((block) => block.queryChunk).join(" ");
- const params = resolvedBlocks.reduce((result, block) => {
- result.push(...block.params);
- return result;
- }, [] as ColumnValue[]);
- return {
- statement,
- params
- };
- };
-
- const write = <_Selection extends Record>(
- createQueryBlocks: CreateQueryBlocks
- ) => {
- const blocks = createQueryBlocks(ctx);
- return resolveQueryBlocks(blocks);
- };
- const get = async <_Selection extends Record>(
- createQueryBlocks: CreateQueryBlocks
- ): Promise<_Selection | null> => {
- const query = write(createQueryBlocks);
- const result = await runner.get(query.statement, query.params);
- if (Array.isArray(result)) return result.at(0) ?? null;
- return result ?? null;
- };
- const getAll = <_Selection extends Record>(
- createQueryBlocks: CreateQueryBlocks
- ): Promise<_Selection[]> => {
- const query = write(createQueryBlocks);
- const result = runner.get(query.statement, query.params);
- if (result instanceof Promise) {
- return new Promise(async (resolve) => {
- const awaitedResult = await result;
- if (!awaitedResult) return resolve([]);
- if (!Array.isArray(awaitedResult)) return resolve([awaitedResult]);
- return resolve(awaitedResult);
- }) as any;
- }
- if (!result) return [] as any;
- if (!Array.isArray(result)) return [result] as any;
- return result as any;
- };
- const run = <_Selection extends Record>(
- createQueryBlocks: CreateQueryBlocks
- ): Promise => {
- const query = write(createQueryBlocks);
- return runner.run(query.statement, query.params) as any;
- };
- return {
- write,
- get,
- getAll,
- run
- } as const;
-};
-
-export type Operator = ReturnType;
-
-export type Context = typeof ctx;
-
-type CreateQueryBlocks = (context: Context) => Block[];
-
-type ResolvedBlock = {
- queryChunk: string;
- params: ColumnValue[];
-};
-
-export type Runner = {
- get: (statement: string, params: ColumnValue[]) => Promise;
- run: (statement: string, params: ColumnValue[]) => Promise;
-};
-
-type ColumnValue = string | number | null | bigint | boolean;
-
-type Block =
- | {
- type: "INNER_JOIN";
- targetTable: string;
- targetColumn: string;
- column: string;
- }
- | {
- type: "SELECT";
- table: string;
- columns: string[];
- }
- | {
- type: "INSERT_INTO";
- table: string;
- values: Record;
- }
- | {
- type: "WHERE";
- column: string;
- comparator: string;
- value: ColumnValue;
- }
- | {
- type: "AND";
- whereBlocks: WhereBlock[];
- }
- | {
- type: "DELETE_FROM";
- table: string;
- }
- | {
- type: "UPDATE";
- table: string;
- values: Record;
- }
- | WhereBlock;
-
-type WhereBlock = {
- type: "WHERE";
- column: string;
- comparator: string;
- value: ColumnValue;
-};
diff --git a/packages/adapter-mysql/src/utils.ts b/packages/adapter-mysql/src/utils.ts
index 424c7cb8a..7a1d88d73 100644
--- a/packages/adapter-mysql/src/utils.ts
+++ b/packages/adapter-mysql/src/utils.ts
@@ -1,34 +1,29 @@
-import type { KeySchema, SessionSchema, UserSchema } from "lucia-auth";
-
-export const transformDatabaseSession = (
- session: MySQLSessionSchema
-): SessionSchema => {
- return {
- id: session.id,
- user_id: session.user_id,
- active_expires: Number(session.active_expires),
- idle_expires: Number(session.idle_expires)
+const createPreparedStatementHelper = (
+ placeholder: (index: number) => string
+) => {
+ const helper = (
+ values: Record
+ ): readonly [fields: string[], placeholders: string[], arguments: any[]] => {
+ const keys = Object.keys(values);
+ return [
+ keys.map((k) => escapeName(k)),
+ keys.map((_, i) => placeholder(i)),
+ keys.map((k) => values[k])
+ ] as const;
};
+ return helper;
};
-export const transformDatabaseKey = (key: MySQLKeySchema): KeySchema => {
- return {
- id: key.id,
- user_id: key.user_id,
- primary_key: Boolean(key.primary_key),
- hashed_password: key.hashed_password,
- expires: key.expires === null ? null : Number(key.expires)
- };
-};
+const ESCAPE_CHAR = "`";
-export type MySQLUserSchema = UserSchema;
-export type MySQLSessionSchema = SessionSchema;
-export type MySQLKeySchema = TransformToMySQLSchema;
+export const escapeName = (val: string) => {
+ return `${ESCAPE_CHAR}${val}${ESCAPE_CHAR}`;
+};
-export type ReplaceBooleanWithNumber = Extract extends never
- ? T
- : Exclude | number;
+export const helper = createPreparedStatementHelper(() => "?");
-export type TransformToMySQLSchema<_Schema extends {}> = {
- [K in keyof _Schema]: ReplaceBooleanWithNumber<_Schema[K]>;
+export const getSetArgs = (fields: string[], placeholders: string[]) => {
+ return fields
+ .map((field, i) => [field, placeholders[i]].join(" = "))
+ .join(",");
};
diff --git a/packages/adapter-mysql/test/index.ts b/packages/adapter-mysql/test/index.ts
deleted file mode 100644
index 09235c751..000000000
--- a/packages/adapter-mysql/test/index.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import { LuciaQueryHandler } from "@lucia-auth/adapter-test";
-import { Runner, createOperator } from "../src/query.js";
-import {
- transformDatabaseKey,
- transformDatabaseSession
-} from "../src/utils.js";
-
-import type { MySQLKeySchema, MySQLSessionSchema } from "../src/utils.js";
-import type { TestUserSchema } from "@lucia-auth/adapter-test";
-
-export const createQueryHandler = (runner: Runner) => {
- const operator = createOperator(runner);
- return {
- user: {
- get: async () => {
- return operator.getAll((ctx) => [
- ctx.selectFrom("auth_user", "*")
- ]);
- },
- insert: async (user) => {
- await operator.run((ctx) => [ctx.insertInto("auth_user", user)]);
- },
- clear: async () => {
- await operator.run((ctx) => [ctx.deleteFrom("auth_user")]);
- }
- },
- session: {
- get: async () => {
- const databaseSessions = await operator.getAll(
- (ctx) => [ctx.selectFrom("auth_session", "*")]
- );
- return databaseSessions.map((val) => transformDatabaseSession(val));
- },
- insert: async (key) => {
- await operator.run((ctx) => [ctx.insertInto("auth_session", key)]);
- },
- clear: async () => {
- await operator.run((ctx) => [ctx.deleteFrom("auth_session")]);
- }
- },
- key: {
- get: async () => {
- const databaseKeys = await operator.getAll((ctx) => [
- ctx.selectFrom("auth_key", "*")
- ]);
- return databaseKeys.map((val) => transformDatabaseKey(val));
- },
- insert: async (key) => {
- await operator.run((ctx) => [ctx.insertInto("auth_key", key)]);
- },
- clear: async () => {
- await operator.run((ctx) => [ctx.deleteFrom("auth_key")]);
- }
- }
- } satisfies LuciaQueryHandler;
-};
diff --git a/packages/adapter-mysql/test/mysql2/db.ts b/packages/adapter-mysql/test/mysql2/db.ts
index 961705c71..1ac9963aa 100644
--- a/packages/adapter-mysql/test/mysql2/db.ts
+++ b/packages/adapter-mysql/test/mysql2/db.ts
@@ -1,20 +1,14 @@
import mysql from "mysql2/promise";
import dotenv from "dotenv";
import { resolve } from "path";
-import { LuciaError } from "lucia-auth";
-import { mysql2 as mysql2Adapter } from "../../src/index.js";
-import { mysql2Runner } from "../../src/mysql2/runner.js";
-import { createQueryHandler } from "../index.js";
dotenv.config({
path: `${resolve()}/.env`
});
-const pool = mysql.createPool({
+export const pool = mysql.createPool({
host: "localhost",
user: "root",
database: process.env.MYSQL2_DATABASE,
password: process.env.MYSQL2_PASSWORD
});
-export const adapter = mysql2Adapter(pool)(LuciaError);
-export const queryHandler = createQueryHandler(mysql2Runner(pool));
diff --git a/packages/adapter-mysql/test/mysql2/index.ts b/packages/adapter-mysql/test/mysql2/index.ts
index ee388e4f5..50cbd85aa 100644
--- a/packages/adapter-mysql/test/mysql2/index.ts
+++ b/packages/adapter-mysql/test/mysql2/index.ts
@@ -1,4 +1,40 @@
-import { testAdapter } from "@lucia-auth/adapter-test";
-import { adapter, queryHandler } from "./db.js";
+import { testAdapter, Database } from "@lucia-auth/adapter-test";
+import { LuciaError } from "lucia";
-testAdapter(adapter, queryHandler);
+import { pool } from "./db.js";
+import { escapeName, helper } from "../../src/utils.js";
+import { getAll, mysql2Adapter } from "../../src/drivers/mysql2.js";
+import { TABLE_NAMES } from "../shared.js";
+
+import type { QueryHandler, TableQueryHandler } from "@lucia-auth/adapter-test";
+
+const createTableQueryHandler = (tableName: string): TableQueryHandler => {
+ const ESCAPED_TABLE_NAME = escapeName(tableName);
+ return {
+ get: async () => {
+ return await getAll(pool.query(`SELECT * FROM ${ESCAPED_TABLE_NAME}`));
+ },
+ insert: async (value: any) => {
+ const [fields, placeholders, args] = helper(value);
+ await pool.execute(
+ `INSERT INTO ${ESCAPED_TABLE_NAME} ( ${fields} ) VALUES ( ${placeholders} )`,
+ args
+ );
+ },
+ clear: async () => {
+ await pool.execute(`DELETE FROM ${ESCAPED_TABLE_NAME}`);
+ }
+ };
+};
+
+const queryHandler: QueryHandler = {
+ user: createTableQueryHandler(TABLE_NAMES.user),
+ session: createTableQueryHandler(TABLE_NAMES.session),
+ key: createTableQueryHandler(TABLE_NAMES.key)
+};
+
+const adapter = mysql2Adapter(pool, TABLE_NAMES)(LuciaError);
+
+await testAdapter(adapter, new Database(queryHandler));
+
+process.exit(0);
diff --git a/packages/adapter-mysql/test/mysql2/setup.ts b/packages/adapter-mysql/test/mysql2/setup.ts
new file mode 100644
index 000000000..3da560c0d
--- /dev/null
+++ b/packages/adapter-mysql/test/mysql2/setup.ts
@@ -0,0 +1,37 @@
+import { pool } from "./db.js";
+import {
+ ESCAPED_USER_TABLE_NAME,
+ ESCAPED_SESSION_TABLE_NAME,
+ ESCAPED_KEY_TABLE_NAME
+} from "../shared.js";
+
+await pool.execute(`
+CREATE TABLE IF NOT EXISTS ${ESCAPED_USER_TABLE_NAME} (
+ id VARCHAR(15) PRIMARY KEY,
+ username VARCHAR(15) NOT NULL UNIQUE
+)
+`);
+
+await pool.execute(`
+CREATE TABLE IF NOT EXISTS ${ESCAPED_SESSION_TABLE_NAME} (
+ id VARCHAR(127) PRIMARY KEY,
+ user_id VARCHAR(15) NOT NULL,
+ active_expires BIGINT UNSIGNED NOT NULL,
+ idle_expires BIGINT UNSIGNED NOT NULL,
+ country VARCHAR(2) NOT NULL,
+
+ FOREIGN KEY (user_id) REFERENCES ${ESCAPED_USER_TABLE_NAME}(id)
+)
+`);
+
+await pool.execute(`
+CREATE TABLE IF NOT EXISTS ${ESCAPED_KEY_TABLE_NAME} (
+ id VARCHAR(255) PRIMARY KEY,
+ user_id VARCHAR(15) NOT NULL,
+ hashed_password VARCHAR(255),
+
+ FOREIGN KEY (user_id) REFERENCES ${ESCAPED_USER_TABLE_NAME}(id)
+)
+`);
+
+process.exit(0);
diff --git a/packages/adapter-mysql/test/planetscale/db.ts b/packages/adapter-mysql/test/planetscale/db.ts
index 6b972899e..a14e38f5c 100644
--- a/packages/adapter-mysql/test/planetscale/db.ts
+++ b/packages/adapter-mysql/test/planetscale/db.ts
@@ -1,19 +1,13 @@
+import { connect } from "@planetscale/database";
import dotenv from "dotenv";
import { resolve } from "path";
-import { LuciaError } from "lucia-auth";
-import { planetscale as planetscaleAdapter } from "../../src/index.js";
-import { planetscaleRunner } from "../../src/planetscale/runner.js";
-import { createQueryHandler } from "../index.js";
-import { connect } from "@planetscale/database";
dotenv.config({
path: `${resolve()}/.env`
});
-const connection = connect({
+export const connection = connect({
host: process.env.PLANETSCALE_HOST,
username: process.env.PLANETSCALE_USERNAME,
password: process.env.PLANETSCALE_PASSWORD
});
-export const adapter = planetscaleAdapter(connection)(LuciaError);
-export const queryHandler = createQueryHandler(planetscaleRunner(connection));
diff --git a/packages/adapter-mysql/test/planetscale/index.ts b/packages/adapter-mysql/test/planetscale/index.ts
index ee388e4f5..c09b34d57 100644
--- a/packages/adapter-mysql/test/planetscale/index.ts
+++ b/packages/adapter-mysql/test/planetscale/index.ts
@@ -1,4 +1,55 @@
-import { testAdapter } from "@lucia-auth/adapter-test";
-import { adapter, queryHandler } from "./db.js";
+import { testAdapter, Database } from "@lucia-auth/adapter-test";
+import { LuciaError } from "lucia";
-testAdapter(adapter, queryHandler);
+import { connection } from "./db.js";
+import { helper, escapeName } from "../../src/utils.js";
+import {
+ getAll,
+ planetscaleAdapter,
+ transformPlanetscaleSession
+} from "../../src/drivers/planetscale.js";
+import { TABLE_NAMES, ESCAPED_SESSION_TABLE_NAME } from "../shared.js";
+
+import type { QueryHandler, TableQueryHandler } from "@lucia-auth/adapter-test";
+import type { PlanetscaleSession } from "../../src/drivers/planetscale.js";
+
+const createTableQueryHandler = (tableName: string): TableQueryHandler => {
+ const ESCAPED_TABLE_NAME = escapeName(tableName);
+ return {
+ get: async () => {
+ return await getAll(
+ connection.execute(`SELECT * FROM ${ESCAPED_TABLE_NAME}`)
+ );
+ },
+ insert: async (value: any) => {
+ const [fields, placeholders, args] = helper(value);
+ await connection.execute(
+ `INSERT INTO ${ESCAPED_TABLE_NAME} ( ${fields} ) VALUES ( ${placeholders} )`,
+ args
+ );
+ },
+ clear: async () => {
+ await connection.execute(`DELETE FROM ${ESCAPED_TABLE_NAME}`);
+ }
+ };
+};
+
+const queryHandler: QueryHandler = {
+ user: createTableQueryHandler(TABLE_NAMES.user),
+ session: {
+ ...createTableQueryHandler(TABLE_NAMES.session),
+ get: async () => {
+ const result = await getAll(
+ connection.execute(`SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME}`)
+ );
+ return result.map((val) => transformPlanetscaleSession(val));
+ }
+ },
+ key: createTableQueryHandler(TABLE_NAMES.key)
+};
+
+const adapter = planetscaleAdapter(connection, TABLE_NAMES)(LuciaError);
+
+await testAdapter(adapter, new Database(queryHandler));
+
+process.exit(0);
diff --git a/packages/adapter-mysql/test/planetscale/setup.ts b/packages/adapter-mysql/test/planetscale/setup.ts
new file mode 100644
index 000000000..19206d889
--- /dev/null
+++ b/packages/adapter-mysql/test/planetscale/setup.ts
@@ -0,0 +1,33 @@
+import { connection } from "./db.js";
+import {
+ ESCAPED_USER_TABLE_NAME,
+ ESCAPED_SESSION_TABLE_NAME,
+ ESCAPED_KEY_TABLE_NAME
+} from "../shared.js";
+
+await connection.execute(`
+CREATE TABLE IF NOT EXISTS ${ESCAPED_USER_TABLE_NAME} (
+ id VARCHAR(15) PRIMARY KEY,
+ username VARCHAR(15) NOT NULL UNIQUE
+)
+`);
+
+await connection.execute(`
+CREATE TABLE IF NOT EXISTS ${ESCAPED_SESSION_TABLE_NAME} (
+ id VARCHAR(127) PRIMARY KEY,
+ user_id VARCHAR(15) NOT NULL,
+ active_expires BIGINT UNSIGNED NOT NULL,
+ idle_expires BIGINT UNSIGNED NOT NULL,
+ country VARCHAR(2) NOT NULL
+)
+`);
+
+await connection.execute(`
+CREATE TABLE IF NOT EXISTS ${ESCAPED_KEY_TABLE_NAME} (
+ id VARCHAR(255) PRIMARY KEY,
+ user_id VARCHAR(15) NOT NULL,
+ hashed_password VARCHAR(255)
+)
+`);
+
+process.exit(0);
diff --git a/packages/adapter-mysql/test/shared.ts b/packages/adapter-mysql/test/shared.ts
new file mode 100644
index 000000000..b745122fc
--- /dev/null
+++ b/packages/adapter-mysql/test/shared.ts
@@ -0,0 +1,11 @@
+import { escapeName } from "../src/utils";
+
+export const TABLE_NAMES = {
+ user: "test_user",
+ session: "user_session",
+ key: "user_key"
+};
+
+export const ESCAPED_USER_TABLE_NAME = escapeName(TABLE_NAMES.user);
+export const ESCAPED_SESSION_TABLE_NAME = escapeName(TABLE_NAMES.session);
+export const ESCAPED_KEY_TABLE_NAME = escapeName(TABLE_NAMES.key);
diff --git a/packages/adapter-postgresql/.env.example b/packages/adapter-postgresql/.env.example
new file mode 100644
index 000000000..203c24137
--- /dev/null
+++ b/packages/adapter-postgresql/.env.example
@@ -0,0 +1 @@
+PSQL_DATABASE_URL=""
\ No newline at end of file
diff --git a/packages/adapter-postgresql/.gitignore b/packages/adapter-postgresql/.gitignore
index 27a3b5d40..7a0ae98eb 100644
--- a/packages/adapter-postgresql/.gitignore
+++ b/packages/adapter-postgresql/.gitignore
@@ -1,7 +1,5 @@
/node_modules
/dist
.DS_Store
-/prisma/migrations
-/prisma/dev.db
-/prisma/dev.db-journal
.env
+*.tgz
\ No newline at end of file
diff --git a/packages/adapter-postgresql/.npmignore b/packages/adapter-postgresql/.npmignore
deleted file mode 100644
index e1ac86668..000000000
--- a/packages/adapter-postgresql/.npmignore
+++ /dev/null
@@ -1,8 +0,0 @@
-/node_modules
-.DS_Store
-/src
-/tsconfig.json
-.gitignore
-.env
-/prisma
-/test
\ No newline at end of file
diff --git a/packages/adapter-postgresql/package.json b/packages/adapter-postgresql/package.json
index 73aa43635..bd1776b34 100644
--- a/packages/adapter-postgresql/package.json
+++ b/packages/adapter-postgresql/package.json
@@ -2,21 +2,23 @@
"name": "@lucia-auth/adapter-postgresql",
"version": "1.0.1",
"description": "PostgreSQL adapter for Lucia",
- "main": "index.js",
- "types": "index.d.ts",
- "module": "index.js",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "module": "dist/index.js",
"type": "module",
"files": [
- "**/*"
+ "/dist/",
+ "CHANGELOG.md"
],
"scripts": {
- "build": "shx rm -rf ./dist/* && tsc && shx cp ./package.json ./dist && shx cp ./README.md ./dist && shx cp .npmignore dist",
- "test.pg": " tsx test/pg/index.ts",
- "auri.publish": "pnpm build && cd dist && pnpm install --no-frozen-lockfile && pnpm publish --no-git-checks --access public && cd ../"
+ "build": "shx rm -rf ./dist/* && tsc",
+ "test.pg": "tsx test/pg/index.ts",
+ "test-setup.pg": "tsx test/pg/setup.ts",
+ "auri.publish": "pnpm build && pnpm publish --no-git-checks --access public"
},
"keywords": [
"lucia",
- "lucia-auth",
+ "lucia",
"auth",
"pg",
"postgresql",
@@ -29,15 +31,15 @@
"repository": {
"type": "git",
"url": "https://github.com/pilcrowOnPaper/lucia",
- "directory": "packages/adapter-prisma"
+ "directory": "packages/adapter-postgresql"
},
"author": "pilcrowonpaper",
"license": "MIT",
"exports": {
- ".": "./index.js"
+ ".": "./dist/index.js"
},
"peerDependencies": {
- "lucia-auth": "^1.4.0",
+ "lucia": "^2.0.0",
"pg": "^8.0.0"
},
"peerDependenciesMeta": {
@@ -46,10 +48,10 @@
}
},
"devDependencies": {
- "@lucia-auth/adapter-test": "workspace:*",
+ "@lucia-auth/adapter-test": "latest",
"@types/pg": "^8.6.5",
"dotenv": "^16.0.3",
- "lucia-auth": "workspace:*",
+ "lucia": "latest",
"tsx": "^3.12.6"
},
"dependencies": {
diff --git a/packages/adapter-postgresql/src/core.ts b/packages/adapter-postgresql/src/core.ts
deleted file mode 100644
index 4d7893f6a..000000000
--- a/packages/adapter-postgresql/src/core.ts
+++ /dev/null
@@ -1,181 +0,0 @@
-import { transformDatabaseSession, transformDatabaseKey } from "./utils.js";
-
-import type {
- PostgresKeySchema,
- PostgresUserSchema,
- PostgresSessionSchema
-} from "./utils.js";
-import { Adapter, KeySchema, SessionSchema } from "lucia-auth";
-import type { ColumnValue, Operator } from "./query.js";
-
-export const createCoreAdapter = (operator: Operator) => {
- const helper = createQueryHelper(operator);
- return {
- getUser: async (userId) => {
- return await helper.getUser(userId);
- },
- getSessionAndUserBySessionId: async (sessionId) => {
- const data = await operator.get<
- PostgresUserSchema & {
- _session_active_expires: number;
- _session_id: string;
- _session_idle_expires: number;
- _session_user_id: string;
- }
- >((ctx) => [
- ctx.selectFrom(
- "auth_session",
- "auth_user.*",
- "auth_session.id as _session_id",
- "auth_session.active_expires as _session_active_expires",
- "auth_session.idle_expires as _session_idle_expires",
- "auth_session.user_id as _session_user_id"
- ),
- ctx.innerJoin("auth_user", "auth_user.id", "auth_session.user_id"),
- ctx.where("auth_session.id", "=", sessionId)
- ]);
- if (!data) return null;
- const {
- _session_active_expires,
- _session_id,
- _session_idle_expires,
- _session_user_id,
- ...user
- } = data;
- return {
- user,
- session: transformDatabaseSession({
- id: _session_id,
- user_id: _session_user_id,
- active_expires: _session_active_expires,
- idle_expires: _session_idle_expires
- })
- };
- },
- getSession: async (sessionId) => {
- const databaseSession = await operator.get(
- (ctx) => [
- ctx.selectFrom("auth_session", "*"),
- ctx.where("id", "=", sessionId)
- ]
- );
- if (!databaseSession) return null;
- return transformDatabaseSession(databaseSession);
- },
- getSessionsByUserId: async (userId) => {
- const databaseSessions = await operator.getAll(
- (ctx) => [
- ctx.selectFrom("auth_session", "*"),
- ctx.where("user_id", "=", userId)
- ]
- );
- return databaseSessions.map((val) => transformDatabaseSession(val));
- },
- deleteUser: async (userId) => {
- await operator.run((ctx) => [
- ctx.deleteFrom("auth_user"),
- ctx.where("id", "=", userId)
- ]);
- },
- setSession: async (session) => {
- await helper.insertSession(session);
- },
- deleteSession: async (sessionId) => {
- await operator.run((ctx) => [
- ctx.deleteFrom("auth_session"),
- ctx.where("id", "=", sessionId)
- ]);
- },
- deleteSessionsByUserId: async (userId) => {
- await operator.run((ctx) => [
- ctx.deleteFrom("auth_session"),
- ctx.where("user_id", "=", userId)
- ]);
- },
- setKey: async (key) => {
- await helper.insertKey(key);
- },
- getKey: async (keyId) => {
- const databaseKey = await operator.get((ctx) => [
- ctx.selectFrom("auth_key", "*"),
- ctx.where("id", "=", keyId)
- ]);
- if (!databaseKey) return null;
- const transformedDatabaseKey = transformDatabaseKey(databaseKey);
- return transformedDatabaseKey;
- },
- getKeysByUserId: async (userId) => {
- const databaseKeys = await operator.getAll((ctx) => [
- ctx.selectFrom("auth_key", "*"),
- ctx.where("user_id", "=", userId)
- ]);
- return databaseKeys.map((val) => transformDatabaseKey(val));
- },
- deleteKeysByUserId: async (userId) => {
- await operator.run((ctx) => [
- ctx.deleteFrom("auth_key"),
- ctx.where("user_id", "=", userId)
- ]);
- },
- deleteNonPrimaryKey: async (keyId) => {
- await operator.run((ctx) => [
- ctx.deleteFrom("auth_key"),
- ctx.and(
- ctx.where("id", "=", keyId),
- ctx.where("primary_key", "=", false)
- )
- ]);
- }
- } satisfies Partial;
-};
-
-export const createQueryHelper = (operator: Operator) => {
- return {
- getUser: async (userId: string) => {
- return await operator.get((ctx) => [
- ctx.selectFrom("auth_user", "*"),
- ctx.where("id", "=", userId)
- ]);
- },
- updateUserAttributes: async (
- userId: string,
- attributes: Record
- ) => {
- return await operator.get((ctx) => [
- ctx.update("auth_user", attributes),
- ctx.where("id", "=", userId),
- ctx.returning("*")
- ]);
- },
- updateKeyPassword: async (keyId: string, hashedPassword: string | null) => {
- const databaseKey = await operator.get((ctx) => [
- ctx.update("auth_key", {
- hashed_password: hashedPassword
- }),
- ctx.where("id", "=", keyId),
- ctx.returning("*")
- ]);
- if (!databaseKey) return null;
- return transformDatabaseKey(databaseKey);
- },
- insertUser: async (
- userId: string,
- attributes: Record
- ) => {
- const user = {
- id: userId,
- ...attributes
- };
- return await operator.get((ctx) => [
- ctx.insertInto("auth_user", user),
- ctx.returning("*")
- ]);
- },
- insertSession: async (session: SessionSchema) => {
- await operator.run((ctx) => [ctx.insertInto("auth_session", session)]);
- },
- insertKey: async (key: KeySchema) => {
- await operator.run((ctx) => [ctx.insertInto("auth_key", key)]);
- }
- };
-};
diff --git a/packages/adapter-postgresql/src/drivers/pg.ts b/packages/adapter-postgresql/src/drivers/pg.ts
new file mode 100644
index 000000000..5c52ff403
--- /dev/null
+++ b/packages/adapter-postgresql/src/drivers/pg.ts
@@ -0,0 +1,280 @@
+import { helper, getSetArgs, escapeName } from "../utils.js";
+
+import type {
+ Adapter,
+ InitializeAdapter,
+ UserSchema,
+ SessionSchema,
+ KeySchema
+} from "lucia";
+import type {
+ QueryResult,
+ DatabaseError,
+ Pool,
+ PoolClient,
+ QueryResultRow
+} from "pg";
+
+export const pgAdapter = (
+ pool: Pool,
+ tables: {
+ user: string;
+ session: string;
+ key: string;
+ }
+): InitializeAdapter => {
+ const transaction = async (
+ execute: (connection: PoolClient) => Promise
+ ): Promise => {
+ const connection = await pool.connect();
+ try {
+ await connection.query("BEGIN");
+ await execute(connection);
+ await connection.query("COMMIT");
+ } catch (e) {
+ connection.query("ROLLBACK");
+ throw e;
+ }
+ };
+
+ const ESCAPED_USER_TABLE_NAME = escapeName(tables.user);
+ const ESCAPED_SESSION_TABLE_NAME = escapeName(tables.session);
+ const ESCAPED_KEY_TABLE_NAME = escapeName(tables.key);
+
+ return (LuciaError) => {
+ return {
+ getUser: async (userId) => {
+ const result = await get(
+ pool.query(`SELECT * FROM ${ESCAPED_USER_TABLE_NAME} WHERE id = $1`, [
+ userId
+ ])
+ );
+ return result;
+ },
+ setUser: async (user, key) => {
+ if (!key) {
+ const [userFields, userValues, userArgs] = helper(user);
+ await pool.query(
+ `INSERT INTO ${ESCAPED_USER_TABLE_NAME} ( ${userFields} ) VALUES ( ${userValues} )`,
+ userArgs
+ );
+ return;
+ }
+ try {
+ await transaction(async (tx) => {
+ const [userFields, userValues, userArgs] = helper(user);
+ await tx.query(
+ `INSERT INTO ${ESCAPED_USER_TABLE_NAME} ( ${userFields} ) VALUES ( ${userValues} )`,
+ userArgs
+ );
+ const [keyFields, keyValues, keyArgs] = helper(key);
+ await tx.query(
+ `INSERT INTO ${ESCAPED_KEY_TABLE_NAME} ( ${keyFields} ) VALUES ( ${keyValues} )`,
+ keyArgs
+ );
+ });
+ } catch (e) {
+ const error = e as Partial;
+ if (error.code === "23505" && error.detail?.includes("Key (id)")) {
+ throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
+ }
+ throw e;
+ }
+ },
+ deleteUser: async (userId) => {
+ await pool.query(
+ `DELETE FROM ${ESCAPED_USER_TABLE_NAME} WHERE id = $1`,
+ [userId]
+ );
+ },
+ updateUser: async (userId, partialUser) => {
+ const [fields, values, args] = helper(partialUser);
+ await pool.query(
+ `UPDATE ${ESCAPED_USER_TABLE_NAME} SET ${getSetArgs(
+ fields,
+ values
+ )} WHERE id = $${fields.length + 1}`,
+ [...args, userId]
+ );
+ },
+
+ getSession: async (sessionId) => {
+ const result = await get(
+ pool.query(
+ `SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE id = $1`,
+ [sessionId]
+ )
+ );
+ return result ? transformPgSession(result) : null;
+ },
+ getSessionsByUserId: async (userId) => {
+ const result = await getAll(
+ pool.query(
+ `SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE user_id = $1`,
+ [userId]
+ )
+ );
+ return result.map((val) => transformPgSession(val));
+ },
+ setSession: async (session) => {
+ try {
+ const [fields, values, args] = helper(session);
+ await pool.query(
+ `INSERT INTO ${ESCAPED_SESSION_TABLE_NAME} ( ${fields} ) VALUES ( ${values} )`,
+ args
+ );
+ } catch (e) {
+ const error = e as Partial;
+ if (
+ error.code === "23503" &&
+ error.detail?.includes("Key (user_id)")
+ ) {
+ throw new LuciaError("AUTH_INVALID_USER_ID");
+ }
+ throw e;
+ }
+ },
+ deleteSession: async (sessionId) => {
+ await pool.query(
+ `DELETE FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE id = $1`,
+ [sessionId]
+ );
+ },
+ deleteSessionsByUserId: async (userId) => {
+ await pool.query(
+ `DELETE FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE user_id = $1`,
+ [userId]
+ );
+ },
+ updateSession: async (sessionId, partialSession) => {
+ const [fields, values, args] = helper(partialSession);
+ await pool.query(
+ `UPDATE ${ESCAPED_SESSION_TABLE_NAME} SET ${getSetArgs(
+ fields,
+ values
+ )} WHERE id = $${fields.length + 1}`,
+ [...args, sessionId]
+ );
+ },
+
+ getKey: async (keyId) => {
+ const result = await get(
+ pool.query(
+ `SELECT * FROM ${ESCAPED_KEY_TABLE_NAME} WHERE id = $1`,
+ [keyId]
+ )
+ );
+ return result;
+ },
+ getKeysByUserId: async (userId) => {
+ const result = getAll(
+ pool.query(
+ `SELECT * FROM ${ESCAPED_KEY_TABLE_NAME} WHERE user_id = $1`,
+ [userId]
+ )
+ );
+ return result;
+ },
+ setKey: async (key) => {
+ try {
+ const [fields, values, args] = helper(key);
+ await pool.query(
+ `INSERT INTO ${ESCAPED_KEY_TABLE_NAME} ( ${fields} ) VALUES ( ${values} )`,
+ args
+ );
+ } catch (e) {
+ const error = e as Partial;
+ if (
+ error.code === "23503" &&
+ error.detail?.includes("Key (user_id)")
+ ) {
+ throw new LuciaError("AUTH_INVALID_USER_ID");
+ }
+ if (error.code === "23505" && error.detail?.includes("Key (id)")) {
+ throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
+ }
+ throw e;
+ }
+ },
+ deleteKey: async (keyId) => {
+ await pool.query(
+ `DELETE FROM ${ESCAPED_KEY_TABLE_NAME} WHERE id = $1`,
+ [keyId]
+ );
+ },
+ deleteKeysByUserId: async (userId) => {
+ await pool.query(
+ `DELETE FROM ${ESCAPED_KEY_TABLE_NAME} WHERE user_id = $1`,
+ [userId]
+ );
+ },
+ updateKey: async (keyId, partialKey) => {
+ const [fields, values, args] = helper(partialKey);
+ await pool.query(
+ `UPDATE ${ESCAPED_KEY_TABLE_NAME} SET ${getSetArgs(
+ fields,
+ values
+ )} WHERE id = $${fields.length + 1}`,
+ [...args, keyId]
+ );
+ },
+
+ getSessionAndUser: async (sessionId) => {
+ const getSessionPromise = get(
+ pool.query(
+ `SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE id = $1`,
+ [sessionId]
+ )
+ );
+ const getUserFromJoinPromise = get(
+ pool.query<
+ UserSchema & {
+ __session_id: string;
+ }
+ >(
+ `SELECT ${ESCAPED_USER_TABLE_NAME}.*, ${ESCAPED_SESSION_TABLE_NAME}.id as __session_id FROM ${ESCAPED_SESSION_TABLE_NAME} INNER JOIN ${ESCAPED_USER_TABLE_NAME} ON ${ESCAPED_USER_TABLE_NAME}.id = ${ESCAPED_SESSION_TABLE_NAME}.user_id WHERE ${ESCAPED_SESSION_TABLE_NAME}.id = $1`,
+ [sessionId]
+ )
+ );
+ const [sessionResult, userFromJoinResult] = await Promise.all([
+ getSessionPromise,
+ getUserFromJoinPromise
+ ]);
+ if (!sessionResult || !userFromJoinResult) return [null, null];
+ const { __session_id: _, ...userResult } = userFromJoinResult;
+ return [transformPgSession(sessionResult), userResult];
+ }
+ };
+ };
+};
+
+export const get = async <_Schema extends QueryResultRow>(
+ queryPromise: Promise>
+): Promise<_Schema | null> => {
+ const { rows } = await queryPromise;
+ const result = rows.at(0) ?? null;
+ return result;
+};
+
+export const getAll = async <_Schema extends QueryResultRow>(
+ queryPromise: Promise>
+): Promise<_Schema[]> => {
+ const { rows } = await queryPromise;
+ return rows;
+};
+
+export type PgSession = Omit<
+ SessionSchema,
+ "active_expires" | "idle_expires"
+> & {
+ active_expires: BigInt;
+ idle_expires: BigInt;
+};
+
+export const transformPgSession = (session: PgSession): SessionSchema => {
+ return {
+ ...session,
+ active_expires: Number(session.active_expires),
+ idle_expires: Number(session.idle_expires)
+ };
+};
diff --git a/packages/adapter-postgresql/src/index.ts b/packages/adapter-postgresql/src/index.ts
index 2f04c80cb..ae5e531ae 100644
--- a/packages/adapter-postgresql/src/index.ts
+++ b/packages/adapter-postgresql/src/index.ts
@@ -1 +1 @@
-export { pgAdapter as pg } from "./pg/index.js";
+export { pgAdapter as pg } from "./drivers/pg.js";
diff --git a/packages/adapter-postgresql/src/lucia.d.ts b/packages/adapter-postgresql/src/lucia.d.ts
index f61de1891..8026ca988 100644
--- a/packages/adapter-postgresql/src/lucia.d.ts
+++ b/packages/adapter-postgresql/src/lucia.d.ts
@@ -1,5 +1,6 @@
-///
+///
declare namespace Lucia {
type Auth = any;
- type UserAttributes = {};
+ type DatabaseUserAttributes = any;
+ type DatabaseSessionAttributes = any;
}
diff --git a/packages/adapter-postgresql/src/pg/index.ts b/packages/adapter-postgresql/src/pg/index.ts
deleted file mode 100644
index 00a51db76..000000000
--- a/packages/adapter-postgresql/src/pg/index.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import { createOperator } from "../query.js";
-import { pgRunner } from "./runner.js";
-import { createCoreAdapter, createQueryHelper } from "../core.js";
-
-import type { Adapter, AdapterFunction } from "lucia-auth";
-import type { Pool, DatabaseError } from "./types.js";
-
-export const pgAdapter = (pool: Pool): AdapterFunction => {
- const transaction = async <_Execute extends () => Promise>(
- execute: _Execute
- ): Promise>> => {
- const connection = await pool.connect();
- try {
- await connection.query("BEGIN");
- const result = await execute();
- await connection.query("COMMIT");
- return result;
- } catch (e) {
- connection.query("ROLLBACK");
- throw e;
- }
- };
-
- return (LuciaError) => {
- const operator = createOperator(pgRunner(pool));
- const coreAdapter = createCoreAdapter(operator);
- const helper = createQueryHelper(operator);
- return {
- ...coreAdapter,
- setUser: async (userId, attributes, key) => {
- try {
- if (key) {
- const insertedUser = await transaction(async () => {
- const databaseUser = await helper.insertUser(userId, attributes);
- if (!databaseUser) throw new TypeError("Unexpected query result");
- await helper.insertKey(key);
- return databaseUser;
- });
- return insertedUser;
- }
- const insertedUser = await helper.insertUser(userId, attributes);
- if (!insertedUser) throw new TypeError("Unexpected type");
- return insertedUser;
- } catch (e) {
- const error = e as Partial;
- if (error.code === "23505" && error.detail?.includes("Key (id)")) {
- throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
- }
- throw e;
- }
- },
- setSession: async (session) => {
- try {
- await helper.insertSession(session);
- } catch (e) {
- const error = e as Partial;
- if (
- error.code === "23503" &&
- error.detail?.includes("Key (user_id)")
- ) {
- throw new LuciaError("AUTH_INVALID_USER_ID");
- }
- if (error.code === "23505" && error.detail?.includes("Key (id)")) {
- throw new LuciaError("AUTH_DUPLICATE_SESSION_ID");
- }
- throw e;
- }
- },
- updateUserAttributes: async (userId, attributes) => {
- if (Object.keys(attributes).length === 0) {
- const databaseUser = await helper.getUser(userId);
- if (!databaseUser) throw new LuciaError("AUTH_INVALID_USER_ID");
- return databaseUser;
- }
- const updatedUser = await helper.updateUserAttributes(
- userId,
- attributes
- );
- if (!updatedUser) throw new LuciaError("AUTH_INVALID_USER_ID");
- return updatedUser;
- },
- setKey: async (key) => {
- try {
- await helper.insertKey(key);
- } catch (e) {
- const error = e as Partial;
- if (
- error.code === "23503" &&
- error.detail?.includes("Key (user_id)")
- ) {
- throw new LuciaError("AUTH_INVALID_USER_ID");
- }
- if (error.code === "23505" && error.detail?.includes("Key (id)")) {
- throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
- }
- throw error;
- }
- },
- updateKeyPassword: async (keyId, hashedPassword) => {
- const updatedKey = await helper.updateKeyPassword(
- keyId,
- hashedPassword
- );
- if (!updatedKey) throw new LuciaError("AUTH_INVALID_KEY_ID");
- return updatedKey;
- }
- };
- };
-};
diff --git a/packages/adapter-postgresql/src/pg/runner.ts b/packages/adapter-postgresql/src/pg/runner.ts
deleted file mode 100644
index 770c184bb..000000000
--- a/packages/adapter-postgresql/src/pg/runner.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import type { Runner } from "../query.js";
-import type { Pool } from "./types.js";
-
-export const pgRunner = (pool: Pool): Runner => {
- return {
- get: async (query, params) => {
- const result = await pool.query(query, params);
- return result.rows;
- },
- run: async (query, params) => {
- await pool.query(query, params);
- }
- };
-};
diff --git a/packages/adapter-postgresql/src/pg/types.ts b/packages/adapter-postgresql/src/pg/types.ts
deleted file mode 100644
index 047493fc4..000000000
--- a/packages/adapter-postgresql/src/pg/types.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import pg from "pg";
-
-export type Pool = InstanceType;
-export type DatabaseError = InstanceType;
diff --git a/packages/adapter-postgresql/src/query.ts b/packages/adapter-postgresql/src/query.ts
deleted file mode 100644
index 0b2b9652c..000000000
--- a/packages/adapter-postgresql/src/query.ts
+++ /dev/null
@@ -1,261 +0,0 @@
-const resolveQueryBlock = (block: Block, paramCount: number): ResolvedBlock => {
- const escapeName = (val: string) => {
- if (val === "*") return val;
- return `"${val}"`;
- };
- if (block.type === "DELETE_FROM") {
- return {
- queryChunk: `DELETE FROM ${escapeName(block.table)}`,
- params: []
- };
- }
- if (block.type === "INNER_JOIN") {
- return {
- queryChunk: `INNER JOIN ${escapeName(block.targetTable)} ON ${
- block.targetColumn
- } = ${block.column}`,
- params: []
- };
- }
- if (block.type === "INSERT_INTO") {
- const keys = Object.keys(block.values);
- return {
- queryChunk: `INSERT INTO ${escapeName(block.table)} (${keys.map((k) =>
- escapeName(k)
- )}) VALUES (${Array(keys.length)
- .fill("")
- .map((_, i) => `$${paramCount + i + 1}`)})`,
- params: keys.map((k) => block.values[k])
- };
- }
- if (block.type === "RETURNING") {
- return {
- queryChunk: `RETURNING ${block.columns}`,
- params: []
- };
- }
- if (block.type === "SELECT") {
- return {
- queryChunk: `SELECT ${block.columns} FROM ${escapeName(block.table)}`,
- params: []
- };
- }
- if (block.type === "WHERE") {
- return {
- queryChunk: `WHERE ${block.column} ${block.comparator} $${
- paramCount + 1
- }`,
- params: [block.value]
- };
- }
- if (block.type === "UPDATE") {
- const keys = Object.keys(block.values);
- return {
- queryChunk: `UPDATE ${escapeName(block.table)} SET ${keys.map((k, i) => {
- return `${escapeName(k)} = $${paramCount + i + 1}`;
- })}`,
- params: keys.map((k) => block.values[k])
- };
- }
- if (block.type === "AND") {
- const resolvedConditionQueryBlocks = block.whereBlocks.map(
- (whereBlock, i) => {
- return {
- queryChunk: `${whereBlock.column} ${whereBlock.comparator} $${
- paramCount + 1 + i
- }`,
- params: [whereBlock.value]
- };
- }
- );
- const conditionQueryChunk = resolvedConditionQueryBlocks
- .map((resolvedBlock) => resolvedBlock.queryChunk)
- .join(" AND ");
- return {
- queryChunk: `WHERE ${conditionQueryChunk}`,
- params: resolvedConditionQueryBlocks.reduce(
- (acc, curr) => [...acc, ...curr.params],
- [] as ColumnValue[]
- )
- };
- }
- throw new TypeError(`Invalid block type`);
-};
-
-const ctx = {
- innerJoin: (targetTable: string, targetColumn: string, column: string) => {
- return {
- type: "INNER_JOIN",
- targetTable,
- targetColumn,
- column
- };
- },
- selectFrom: (table: string, ...columns: [string, ...string[]]) => {
- return {
- type: "SELECT",
- table,
- columns
- };
- },
- returning: (...columns: [string, ...string[]]) => {
- return {
- type: "RETURNING",
- columns
- };
- },
- insertInto: (table: string, values: Record) => {
- return {
- type: "INSERT_INTO",
- table,
- values
- };
- },
- where: (column: string, comparator: string, value: ColumnValue) => {
- return {
- type: "WHERE",
- column,
- comparator,
- value
- };
- },
- deleteFrom: (table: string) => {
- return {
- type: "DELETE_FROM",
- table
- };
- },
- update: (table: string, values: Record) => {
- return {
- type: "UPDATE",
- table,
- values
- };
- },
- and: (...whereBlocks: WhereBlock[]) => {
- return {
- type: "AND",
- whereBlocks
- };
- }
-} satisfies Record Block>;
-
-export const createOperator = <_Runner extends Runner>(runner: _Runner) => {
- const resolveQueryBlocks = (queryBlocks: Block[]) => {
- const queryChunks: string[] = [];
- const params: ColumnValue[] = [];
- for (const queryBlock of queryBlocks) {
- const resolvedBlock = resolveQueryBlock(queryBlock, params.length);
- queryChunks.push(resolvedBlock.queryChunk);
- params.push(...resolvedBlock.params);
- }
- const statement = queryChunks.join(" ");
- return {
- statement,
- params
- };
- };
-
- const write = <_Selection extends Record>(
- createQueryBlocks: CreateQueryBlocks
- ) => {
- const blocks = createQueryBlocks(ctx);
- return resolveQueryBlocks(blocks);
- };
- const get = async <_Selection extends Record>(
- createQueryBlocks: CreateQueryBlocks
- ): Promise<_Selection | null> => {
- const query = write(createQueryBlocks);
- const result = await runner.get(query.statement, query.params);
- if (Array.isArray(result)) return result.at(0) ?? null;
- return result ?? null;
- };
- const getAll = async <_Selection extends Record>(
- createQueryBlocks: CreateQueryBlocks
- ): Promise<_Selection[]> => {
- const query = write(createQueryBlocks);
- const result = await runner.get(query.statement, query.params);
- if (!result) return [] as any;
- if (!Array.isArray(result)) return [result] as any;
- return result as any;
- };
- const run = <_Selection extends Record>(
- createQueryBlocks: CreateQueryBlocks
- ): Promise => {
- const query = write(createQueryBlocks);
- return runner.run(query.statement, query.params) as any;
- };
- return {
- write,
- get,
- getAll,
- run
- } as const;
-};
-
-export type Operator = ReturnType;
-
-export type Context = typeof ctx;
-
-type CreateQueryBlocks = (context: Context) => Block[];
-
-type ResolvedBlock = {
- queryChunk: string;
- params: ColumnValue[];
-};
-
-export type Runner = {
- get: (statement: string, params: ColumnValue[]) => Promise;
- run: (statement: string, params: ColumnValue[]) => Promise;
-};
-
-export type ColumnValue = string | number | null | bigint | boolean;
-
-type Block =
- | {
- type: "INNER_JOIN";
- targetTable: string;
- targetColumn: string;
- column: string;
- }
- | {
- type: "SELECT";
- table: string;
- columns: string[];
- }
- | {
- type: "RETURNING";
- columns: string[];
- }
- | {
- type: "INSERT_INTO";
- table: string;
- values: Record;
- }
- | {
- type: "WHERE";
- column: string;
- comparator: string;
- value: ColumnValue;
- }
- | {
- type: "AND";
- whereBlocks: WhereBlock[];
- }
- | {
- type: "DELETE_FROM";
- table: string;
- }
- | {
- type: "UPDATE";
- table: string;
- values: Record;
- }
- | WhereBlock;
-
-type WhereBlock = {
- type: "WHERE";
- column: string;
- comparator: string;
- value: ColumnValue;
-};
diff --git a/packages/adapter-postgresql/src/utils.ts b/packages/adapter-postgresql/src/utils.ts
index 5f7be7ae0..e28415a7e 100644
--- a/packages/adapter-postgresql/src/utils.ts
+++ b/packages/adapter-postgresql/src/utils.ts
@@ -1,33 +1,29 @@
-import type { SessionSchema, UserSchema, KeySchema } from "lucia-auth";
-import { ColumnValue } from "./query.js";
-
-export const transformDatabaseSession = (
- session: PostgresSessionSchema
-): SessionSchema => {
- return {
- id: session.id,
- user_id: session.user_id,
- active_expires: Number(session.active_expires),
- idle_expires: Number(session.idle_expires)
+const createPreparedStatementHelper = (
+ placeholder: (index: number) => string
+) => {
+ const helper = (
+ values: Record
+ ): readonly [fields: string[], placeholders: string[], arguments: any[]] => {
+ const keys = Object.keys(values);
+ return [
+ keys.map((k) => escapeName(k)),
+ keys.map((_, i) => placeholder(i)),
+ keys.map((k) => values[k])
+ ] as const;
};
+ return helper;
};
-export const transformDatabaseKey = (key: PostgresKeySchema): KeySchema => {
- return {
- id: key.id,
- user_id: key.user_id,
- primary_key: Boolean(key.primary_key),
- hashed_password: key.hashed_password,
- expires: key.expires === null ? null : Number(key.expires)
- };
-};
+const ESCAPE_CHAR = `"`;
-type PgSchema<_Schema extends Record> = {
- [K in keyof _Schema]: Extract<_Schema[K], number> extends never
- ? _Schema[K]
- : _Schema[K] | string;
+export const escapeName = (val: string) => {
+ return `${ESCAPE_CHAR}${val}${ESCAPE_CHAR}`;
};
-export type PostgresSessionSchema = PgSchema;
-export type PostgresKeySchema = PgSchema;
-export type PostgresUserSchema = PgSchema;
+export const helper = createPreparedStatementHelper((i) => `$${i + 1}`);
+
+export const getSetArgs = (fields: string[], placeholders: string[]) => {
+ return fields
+ .map((field, i) => [field, placeholders[i]].join(" = "))
+ .join(",");
+};
diff --git a/packages/adapter-postgresql/test/index.ts b/packages/adapter-postgresql/test/index.ts
deleted file mode 100644
index a808b47be..000000000
--- a/packages/adapter-postgresql/test/index.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import { LuciaQueryHandler } from "@lucia-auth/adapter-test";
-import { Runner, createOperator } from "../src/query.js";
-import {
- transformDatabaseKey,
- transformDatabaseSession
-} from "../src/utils.js";
-
-import type { PostgresKeySchema, PostgresSessionSchema } from "../src/utils.js";
-import type { TestUserSchema } from "@lucia-auth/adapter-test";
-
-export const createQueryHandler = (runner: Runner) => {
- const operator = createOperator(runner);
- return {
- user: {
- get: async () => {
- return await operator.getAll((ctx) => [
- ctx.selectFrom("auth_user", "*")
- ]);
- },
- insert: async (user) => {
- await operator.run((ctx) => [ctx.insertInto("auth_user", user)]);
- },
- clear: async () => {
- await operator.run((ctx) => [ctx.deleteFrom("auth_user")]);
- }
- },
- session: {
- get: async () => {
- const result = await operator.getAll((ctx) => [
- ctx.selectFrom("auth_session", "*")
- ]);
- return result.map((val) => transformDatabaseSession(val));
- },
- insert: async (key) => {
- await operator.run((ctx) => [ctx.insertInto("auth_session", key)]);
- },
- clear: async () => {
- await operator.run((ctx) => [ctx.deleteFrom("auth_session")]);
- }
- },
- key: {
- get: async () => {
- const result = await operator.getAll((ctx) => [
- ctx.selectFrom("auth_key", "*")
- ]);
- return result.map((val) => transformDatabaseKey(val));
- },
- insert: async (key) => {
- await operator.run((ctx) => [ctx.insertInto("auth_key", key)]);
- },
- clear: async () => {
- await operator.run((ctx) => [ctx.deleteFrom("auth_key")]);
- }
- }
- } satisfies LuciaQueryHandler;
-};
diff --git a/packages/adapter-postgresql/test/pg/db.ts b/packages/adapter-postgresql/test/pg/db.ts
index ec1d05329..e68ff2f66 100644
--- a/packages/adapter-postgresql/test/pg/db.ts
+++ b/packages/adapter-postgresql/test/pg/db.ts
@@ -1,19 +1,11 @@
import dotenv from "dotenv";
import { resolve } from "path";
-import { LuciaError } from "lucia-auth";
-
-import { pgAdapter } from "../../src/pg/index.js";
-import { pgRunner } from "../../src/pg/runner.js";
import pg from "pg";
-import { createQueryHandler } from "../index.js";
dotenv.config({
path: `${resolve()}/.env`
});
-const pool = new pg.Pool({
+export const pool = new pg.Pool({
connectionString: process.env.PSQL_DATABASE_URL
});
-
-export const adapter = pgAdapter(pool)(LuciaError);
-export const queryHandler = createQueryHandler(pgRunner(pool));
diff --git a/packages/adapter-postgresql/test/pg/index.ts b/packages/adapter-postgresql/test/pg/index.ts
index ee388e4f5..dd2578cdf 100644
--- a/packages/adapter-postgresql/test/pg/index.ts
+++ b/packages/adapter-postgresql/test/pg/index.ts
@@ -1,4 +1,49 @@
-import { testAdapter } from "@lucia-auth/adapter-test";
-import { adapter, queryHandler } from "./db.js";
+import { testAdapter, Database } from "@lucia-auth/adapter-test";
+import { LuciaError } from "lucia";
-testAdapter(adapter, queryHandler);
+import { pool } from "./db.js";
+import { escapeName, helper } from "../../src/utils.js";
+import { getAll, pgAdapter, transformPgSession } from "../../src/drivers/pg.js";
+import { ESCAPED_SESSION_TABLE_NAME, TABLE_NAMES } from "../shared.js";
+
+import type { QueryHandler, TableQueryHandler } from "@lucia-auth/adapter-test";
+import type { PgSession } from "../../src/drivers/pg.js";
+
+const createTableQueryHandler = (tableName: string): TableQueryHandler => {
+ const ESCAPED_TABLE_NAME = escapeName(tableName);
+ return {
+ get: async () => {
+ return await getAll(pool.query(`SELECT * FROM ${ESCAPED_TABLE_NAME}`));
+ },
+ insert: async (value: any) => {
+ const [fields, placeholders, args] = helper(value);
+ await pool.query(
+ `INSERT INTO ${ESCAPED_TABLE_NAME} ( ${fields} ) VALUES ( ${placeholders} )`,
+ args
+ );
+ },
+ clear: async () => {
+ await pool.query(`DELETE FROM ${ESCAPED_TABLE_NAME}`);
+ }
+ };
+};
+
+const queryHandler: QueryHandler = {
+ user: createTableQueryHandler(TABLE_NAMES.user),
+ session: {
+ ...createTableQueryHandler(TABLE_NAMES.session),
+ get: async () => {
+ const result = await getAll(
+ pool.query(`SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME}`)
+ );
+ return result.map((val) => transformPgSession(val));
+ }
+ },
+ key: createTableQueryHandler(TABLE_NAMES.key)
+};
+
+const adapter = pgAdapter(pool, TABLE_NAMES)(LuciaError);
+
+await testAdapter(adapter, new Database(queryHandler));
+
+process.exit(0);
diff --git a/packages/adapter-postgresql/test/pg/setup.ts b/packages/adapter-postgresql/test/pg/setup.ts
new file mode 100644
index 000000000..923868311
--- /dev/null
+++ b/packages/adapter-postgresql/test/pg/setup.ts
@@ -0,0 +1,33 @@
+import {
+ ESCAPED_KEY_TABLE_NAME,
+ ESCAPED_SESSION_TABLE_NAME,
+ ESCAPED_USER_TABLE_NAME
+} from "../shared.js";
+import { pool } from "./db.js";
+
+await pool.query(`
+CREATE TABLE ${ESCAPED_USER_TABLE_NAME} (
+ id TEXT PRIMARY KEY,
+ username TEXT NOT NULL UNIQUE
+)
+`);
+
+await pool.query(`
+CREATE TABLE ${ESCAPED_SESSION_TABLE_NAME} (
+ id TEXT PRIMARY KEY,
+ user_id TEXT NOT NULL REFERENCES ${ESCAPED_USER_TABLE_NAME}(id),
+ active_expires BIGINT NOT NULL,
+ idle_expires BIGINT NOT NULL,
+ country TEXT NOT NULL
+)
+`);
+
+await pool.query(`
+CREATE TABLE ${ESCAPED_KEY_TABLE_NAME} (
+ id TEXT PRIMARY KEY,
+ user_id TEXT NOT NULL REFERENCES ${ESCAPED_USER_TABLE_NAME}(id),
+ hashed_password VARCHAR(255)
+)
+`);
+
+process.exit(0);
diff --git a/packages/adapter-postgresql/test/shared.ts b/packages/adapter-postgresql/test/shared.ts
new file mode 100644
index 000000000..a13fb1dd1
--- /dev/null
+++ b/packages/adapter-postgresql/test/shared.ts
@@ -0,0 +1,11 @@
+import { escapeName } from "../src/utils.js";
+
+export const TABLE_NAMES = {
+ user: "test_user",
+ session: "user_session",
+ key: "user_key"
+};
+
+export const ESCAPED_USER_TABLE_NAME = escapeName(TABLE_NAMES.user);
+export const ESCAPED_SESSION_TABLE_NAME = escapeName(TABLE_NAMES.session);
+export const ESCAPED_KEY_TABLE_NAME = escapeName(TABLE_NAMES.key);
diff --git a/packages/adapter-prisma/.gitignore b/packages/adapter-prisma/.gitignore
index 27a3b5d40..7a0ae98eb 100644
--- a/packages/adapter-prisma/.gitignore
+++ b/packages/adapter-prisma/.gitignore
@@ -1,7 +1,5 @@
/node_modules
/dist
.DS_Store
-/prisma/migrations
-/prisma/dev.db
-/prisma/dev.db-journal
.env
+*.tgz
\ No newline at end of file
diff --git a/packages/adapter-prisma/.npmignore b/packages/adapter-prisma/.npmignore
deleted file mode 100644
index e1ac86668..000000000
--- a/packages/adapter-prisma/.npmignore
+++ /dev/null
@@ -1,8 +0,0 @@
-/node_modules
-.DS_Store
-/src
-/tsconfig.json
-.gitignore
-.env
-/prisma
-/test
\ No newline at end of file
diff --git a/packages/adapter-prisma/README.md b/packages/adapter-prisma/README.md
index 3af843a4d..364329278 100644
--- a/packages/adapter-prisma/README.md
+++ b/packages/adapter-prisma/README.md
@@ -20,10 +20,7 @@ Requires `lucia-auth@0.11.0`.
## Testing
-```
-pnpm exec prisma migrate dev --name init
-```
-
-```
+```bash
+pnpm test-setup
pnpm test
```
diff --git a/packages/adapter-prisma/package.json b/packages/adapter-prisma/package.json
index 223c78c1b..e0867d99b 100644
--- a/packages/adapter-prisma/package.json
+++ b/packages/adapter-prisma/package.json
@@ -2,22 +2,24 @@
"name": "@lucia-auth/adapter-prisma",
"version": "2.0.0",
"description": "Prisma adapter for Lucia",
- "main": "index.js",
- "types": "index.d.ts",
- "module": "index.js",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "module": "dist/index.js",
"type": "module",
"files": [
- "**/*"
+ "/dist/",
+ "CHANGELOG.md"
],
"scripts": {
- "build": "shx rm -rf ./dist/* && tsc && shx cp ./package.json ./dist && shx cp ./README.md ./dist && shx cp .npmignore dist",
+ "build": "shx rm -rf ./dist/* && tsc",
"test": "tsx test/index.ts",
- "auri.publish": "pnpm build && cd dist && pnpm install --no-frozen-lockfile && pnpm publish --no-git-checks --access public && cd ../"
+ "test-setup": "prisma db push",
+ "auri.publish": "pnpm build && pnpm publish --no-git-checks --access public"
},
"keywords": [
"lucia",
"prisma",
- "lucia-auth",
+ "lucia",
"auth",
"postgres",
"mysql",
@@ -35,15 +37,15 @@
"author": "pilcrowonpaper",
"license": "MIT",
"exports": {
- ".": "./index.js"
+ ".": "./dist/index.js"
},
"peerDependencies": {
"@prisma/client": "^4.2.0",
- "lucia-auth": "^1.3.0"
+ "lucia": "^2.0.0"
},
"devDependencies": {
- "lucia-auth": "workspace:*",
- "@lucia-auth/adapter-test": "workspace:*",
+ "lucia": "latest",
+ "@lucia-auth/adapter-test": "latest",
"@prisma/client": "^4.9.0",
"prisma": "^4.9.0",
"tsx": "^3.12.6"
diff --git a/packages/adapter-sqlite/test/d1/.wrangler/state/d1/DB.sqlite3 b/packages/adapter-prisma/prisma/dev.db
similarity index 57%
rename from packages/adapter-sqlite/test/d1/.wrangler/state/d1/DB.sqlite3
rename to packages/adapter-prisma/prisma/dev.db
index 3049118f4..91707f19a 100644
Binary files a/packages/adapter-sqlite/test/d1/.wrangler/state/d1/DB.sqlite3 and b/packages/adapter-prisma/prisma/dev.db differ
diff --git a/packages/adapter-prisma/prisma/schema.prisma b/packages/adapter-prisma/prisma/schema.prisma
index 5a849fe5c..235d6f168 100644
--- a/packages/adapter-prisma/prisma/schema.prisma
+++ b/packages/adapter-prisma/prisma/schema.prisma
@@ -10,34 +10,33 @@ datasource db {
url = "file:./dev.db"
}
-model AuthUser {
- id String @id @unique
- username String @unique
- auth_session AuthSession[]
- auth_key AuthKey[]
+model User {
+ id String @id @unique
+ username String @unique
+ auth_session Session[]
+ auth_key Key[]
- @@map("auth_user")
+ @@map("test_user")
}
-model AuthSession {
- id String @id @unique
+model Session {
+ id String @id @unique
user_id String
active_expires BigInt
idle_expires BigInt
- auth_user AuthUser @relation(references: [id], fields: [user_id], onDelete: Cascade)
+ country String
+ test_user User @relation(references: [id], fields: [user_id], onDelete: Cascade)
@@index([user_id])
- @@map("auth_session")
+ @@map("user_session")
}
-model AuthKey {
- id String @id @unique
+model Key {
+ id String @id @unique
hashed_password String?
user_id String
- primary_key Boolean
- expires BigInt?
- auth_user AuthUser @relation(references: [id], fields: [user_id], onDelete: Cascade)
+ test_user User @relation(references: [id], fields: [user_id], onDelete: Cascade)
@@index([user_id])
- @@map("auth_key")
+ @@map("user_key")
}
diff --git a/packages/adapter-prisma/src/index.ts b/packages/adapter-prisma/src/index.ts
index fd5a9783c..2fbb7db38 100644
--- a/packages/adapter-prisma/src/index.ts
+++ b/packages/adapter-prisma/src/index.ts
@@ -1,226 +1 @@
-import type {
- Adapter,
- AdapterFunction,
- KeySchema,
- SessionSchema,
- UserSchema
-} from "lucia-auth";
-import { transformDatabaseKey, transformDatabaseSession } from "./utils.js";
-import { PrismaClient, SmartPrismaClient } from "./prisma.js";
-
-interface PossiblePrismaError {
- code: string;
- message: string;
-}
-
-type Models = {
- authUser: {
- schema: UserSchema;
- relations: {};
- };
- authSession: {
- schema: SessionSchema;
- relations: {
- auth_user: UserSchema;
- };
- };
- authKey: {
- schema: KeySchema;
- relations: {
- auth_user: UserSchema;
- };
- };
-};
-
-const adapter =
- (prismaClient: PrismaClient): AdapterFunction =>
- (LuciaError) => {
- const prisma = prismaClient as any as SmartPrismaClient;
- return {
- getUser: async (userId) => {
- return await prisma.authUser.findUnique({
- where: {
- id: userId
- }
- });
- },
- getSessionAndUserBySessionId: async (sessionId) => {
- const data = await prisma.authSession.findUnique({
- where: {
- id: sessionId
- },
- include: {
- auth_user: true
- }
- });
- if (!data) return null;
- const { auth_user: user, ...session } = data;
- return {
- user,
- session: transformDatabaseSession(session)
- };
- },
- getSession: async (sessionId) => {
- const session = await prisma.authSession.findUnique({
- where: {
- id: sessionId
- }
- });
- if (!session) return null;
- return transformDatabaseSession(session);
- },
- getSessionsByUserId: async (userId) => {
- const sessions = await prisma.authSession.findMany({
- where: {
- user_id: userId
- }
- });
- return sessions.map((session) => transformDatabaseSession(session));
- },
- setUser: async (userId, attributes, key) => {
- if (!key) {
- return await prisma.authUser.create({
- data: {
- id: userId,
- ...attributes
- }
- });
- }
- try {
- const [databaseUser] = await prisma.$transaction([
- prisma.authUser.create({
- data: {
- id: userId,
- ...attributes
- }
- }),
- prisma.authKey.create({
- data: key
- })
- ] as const);
- return databaseUser;
- } catch (e) {
- const error = e as Partial;
- if (error.code === "P2002" && error.message?.includes("`id`"))
- throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
- throw error;
- }
- },
- deleteUser: async (userId) => {
- await prisma.authUser.deleteMany({
- where: {
- id: userId
- }
- });
- },
- setSession: async (session) => {
- try {
- await prisma.authSession.create({
- data: session
- });
- } catch (e) {
- const error = e as Partial;
- if (error.code === "P2003")
- throw new LuciaError("AUTH_INVALID_USER_ID");
- if (error.code === "P2002" && error.message?.includes("`id`"))
- throw new LuciaError("AUTH_DUPLICATE_SESSION_ID");
- throw error;
- }
- },
- deleteSession: async (sessionId) => {
- await prisma.authSession.delete({
- where: {
- id: sessionId
- }
- });
- },
- deleteSessionsByUserId: async (userId) => {
- await prisma.authSession.deleteMany({
- where: {
- user_id: userId
- }
- });
- },
- updateUserAttributes: async (userId, attributes) => {
- try {
- const databaseUser = await prisma.authUser.update({
- data: attributes,
- where: {
- id: userId
- }
- });
- return databaseUser;
- } catch (e) {
- const error = e as Partial;
- if (error.code === "P2025")
- throw new LuciaError("AUTH_INVALID_USER_ID");
- throw error;
- }
- },
- setKey: async (key) => {
- try {
- await prisma.authKey.create({
- data: key
- });
- } catch (e) {
- const error = e as Partial;
- if (error.code === "P2003")
- throw new LuciaError("AUTH_INVALID_USER_ID");
- if (error.code === "P2002" && error.message?.includes("`id`"))
- throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
- throw error;
- }
- },
- getKey: async (keyId) => {
- const databaseKey = await prisma.authKey.findUnique({
- where: {
- id: keyId
- }
- });
- if (!databaseKey) return null;
- return transformDatabaseKey(databaseKey);
- },
- getKeysByUserId: async (userId) => {
- const keys = await prisma.authKey.findMany({
- where: {
- user_id: userId
- }
- });
- return keys.map((val) => transformDatabaseKey(val));
- },
- updateKeyPassword: async (keyId, hashedPassword) => {
- try {
- return await prisma.authKey.update({
- data: {
- hashed_password: hashedPassword
- },
- where: {
- id: keyId
- }
- });
- } catch (e) {
- const error = e as Partial;
- if (error.code === "P2025")
- throw new LuciaError("AUTH_INVALID_KEY_ID");
- throw error;
- }
- },
- deleteKeysByUserId: async (userId) => {
- await prisma.authKey.deleteMany({
- where: {
- user_id: userId
- }
- });
- },
- deleteNonPrimaryKey: async (keyId) => {
- await prisma.authKey.deleteMany({
- where: {
- id: keyId,
- primary_key: false
- }
- });
- }
- };
- };
-
-export default adapter;
+export { prismaAdapter } from "./prisma.js";
diff --git a/packages/adapter-prisma/src/lucia.d.ts b/packages/adapter-prisma/src/lucia.d.ts
index f61de1891..8026ca988 100644
--- a/packages/adapter-prisma/src/lucia.d.ts
+++ b/packages/adapter-prisma/src/lucia.d.ts
@@ -1,5 +1,6 @@
-///
+///
declare namespace Lucia {
type Auth = any;
- type UserAttributes = {};
+ type DatabaseUserAttributes = any;
+ type DatabaseSessionAttributes = any;
}
diff --git a/packages/adapter-prisma/src/prisma.ts b/packages/adapter-prisma/src/prisma.ts
index c0cd0caf3..8c8952133 100644
--- a/packages/adapter-prisma/src/prisma.ts
+++ b/packages/adapter-prisma/src/prisma.ts
@@ -1,61 +1,256 @@
-type PayloadResult = {
- count: number;
-};
+import type {
+ Adapter,
+ InitializeAdapter,
+ KeySchema,
+ SessionSchema,
+ UserSchema
+} from "lucia";
-type Model = {
- schema: Record;
- relations: Record>;
+type PossiblePrismaError = {
+ code: string;
+ message: string;
};
-export type PrismaClient> = {
- [K in keyof Models]: {
- findUnique: (options: {
- where: Partial;
- include?: any;
- }) => Promise;
- } & { [K: string]: any };
-} & { [K: string]: any };
+type ExtractModelNames<_PrismaClient extends PrismaClient> = Exclude<
+ keyof _PrismaClient,
+ `$${string}`
+>;
+
+export const prismaAdapter = <_PrismaClient extends PrismaClient>(options: {
+ client: _PrismaClient;
+ models: {
+ user: ExtractModelNames<_PrismaClient>;
+ session: ExtractModelNames<_PrismaClient>;
+ key: ExtractModelNames<_PrismaClient>;
+ };
+ tables?: {
+ user: string;
+ };
+}): InitializeAdapter => {
+ const User = options.client[
+ options.models.user
+ ] as SmartPrismaModel;
+ const Session = options.client[
+ options.models.session
+ ] as SmartPrismaModel;
+ const Key = options.client[options.models.key] as SmartPrismaModel;
+
+ return (LuciaError) => {
+ return {
+ getUser: async (userId) => {
+ return await User.findUnique({
+ where: {
+ id: userId
+ }
+ });
+ },
+ setUser: async (user, key) => {
+ if (!key) {
+ await User.create({
+ data: user
+ });
+ return;
+ }
+ try {
+ await options.client.$transaction([
+ User.create({
+ data: user
+ }),
+ Key.create({
+ data: key
+ })
+ ]);
+ } catch (e) {
+ const error = e as Partial;
+ if (error.code === "P2002" && error.message?.includes("`id`"))
+ throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
+ throw error;
+ }
+ },
+ deleteUser: async (userId) => {
+ await User.deleteMany({
+ where: {
+ id: userId
+ }
+ });
+ },
+ updateUser: async (userId, partialUser) => {
+ await User.update({
+ data: partialUser,
+ where: {
+ id: userId
+ }
+ });
+ },
+ getSession: async (sessionId) => {
+ const result = await Session.findUnique({
+ where: {
+ id: sessionId
+ }
+ });
+ if (!result) return null;
+ return transformPrismaSession(result);
+ },
+ getSessionsByUserId: async (userId) => {
+ const sessions = await Session.findMany({
+ where: {
+ user_id: userId
+ }
+ });
+ return sessions.map((session) => transformPrismaSession(session));
+ },
+ setSession: async (session) => {
+ try {
+ await Session.create({
+ data: session
+ });
+ } catch (e) {
+ const error = e as Partial;
+ if (error.code === "P2003") {
+ throw new LuciaError("AUTH_INVALID_USER_ID");
+ }
+
+ if (error.code === "P2002" && error.message?.includes("`id`")) {
+ throw new LuciaError("AUTH_DUPLICATE_SESSION_ID");
+ }
+
+ throw error;
+ }
+ },
+ deleteSession: async (sessionId) => {
+ await Session.delete({
+ where: {
+ id: sessionId
+ }
+ });
+ },
+ deleteSessionsByUserId: async (userId) => {
+ await Session.deleteMany({
+ where: {
+ user_id: userId
+ }
+ });
+ },
+ updateSession: async (userId, partialSession) => {
+ await Session.update({
+ data: partialSession,
+ where: {
+ id: userId
+ }
+ });
+ },
+
+ getKey: async (keyId) => {
+ return await Key.findUnique({
+ where: {
+ id: keyId
+ }
+ });
+ },
+ getKeysByUserId: async (userId) => {
+ return await Key.findMany({
+ where: {
+ user_id: userId
+ }
+ });
+ },
+
+ setKey: async (key) => {
+ try {
+ await Key.create({
+ data: key
+ });
+ } catch (e) {
+ const error = e as Partial;
+ if (error.code === "P2003") {
+ throw new LuciaError("AUTH_INVALID_USER_ID");
+ }
+ if (error.code === "P2002" && error.message?.includes("`id`")) {
+ throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
+ }
+ throw error;
+ }
+ },
+ deleteKey: async (keyId) => {
+ await Key.delete({
+ where: {
+ id: keyId
+ }
+ });
+ },
+ deleteKeysByUserId: async (userId) => {
+ await Key.deleteMany({
+ where: {
+ user_id: userId
+ }
+ });
+ },
+ updateKey: async (userId, partialKey) => {
+ await Key.update({
+ data: partialKey,
+ where: {
+ id: userId
+ }
+ });
+ },
-export type SmartPrismaClient> = {
- [K in keyof Models]: {
- findUnique: <
- Options extends {
- where: Partial;
- include?: Partial>;
+ getSessionAndUser: async (sessionId) => {
+ const result = await Session.findUnique({
+ where: {
+ id: sessionId
+ },
+ include: {
+ [options.tables?.user ?? options.models.user]: true
+ }
+ });
+ if (!result) return [null, null];
+ const {
+ [options.tables?.user ?? options.models.user]: userResult,
+ ...sessionResult
+ } = result;
+ return [
+ transformPrismaSession(sessionResult as PrismaSession),
+ userResult as UserSchema
+ ];
}
- >(
- options: Options
- ) => Options["include"] extends undefined
- ? Promise
- : Promise<
- | null
- | (Models[K]["schema"] & {
- [L in keyof Options["include"]]: L extends keyof Models[K]["relations"]
- ? Models[K]["relations"][L]
- : never;
- })
- >;
- findMany: (options: {
- where: Partial;
- }) => Promise;
- create: (options: {
- data: Models[K]["schema"];
- }) => Promise;
- delete: (options: {
- where: Partial;
- }) => Promise;
- deleteMany: (options: {
- where: Partial;
- }) => Promise;
- update: (options: {
- data: Partial;
- where: Partial;
- }) => Promise;
+ };
};
-} & {
- $transaction: []>(
- queries: Queries
- ) => Promise<{
- [I in keyof Queries]: Awaited;
- }>;
+};
+
+export const transformPrismaSession = (
+ sessionData: PrismaSession
+): SessionSchema => {
+ const { active_expires, idle_expires: idleExpires, ...data } = sessionData;
+ return {
+ ...data,
+ active_expires: Number(active_expires),
+ idle_expires: Number(idleExpires)
+ };
+};
+
+type PrismaClient = {
+ $transaction: (...args: any) => any;
+} & { [K: string]: any };
+
+export type PrismaSession = Omit<
+ SessionSchema,
+ "active_expires" | "idle_expires"
+> & {
+ active_expires: BigInt | number;
+ idle_expires: BigInt | number;
+};
+
+export type SmartPrismaModel<_Schema = any> = {
+ findUnique: <_Included = {}>(options: {
+ where: Partial<_Schema>;
+ include?: Partial>;
+ }) => Promise & _Included;
+ findMany: (options?: { where: Partial<_Schema> }) => Promise<_Schema[]>;
+ create: (options: { data: _Schema }) => Promise<_Schema>;
+ delete: (options: { where: Partial<_Schema> }) => Promise;
+ deleteMany: (options?: { where: Partial<_Schema> }) => Promise;
+ update: (options: {
+ data: Partial<_Schema>;
+ where: Partial<_Schema>;
+ }) => Promise<_Schema>;
};
diff --git a/packages/adapter-prisma/src/utils.ts b/packages/adapter-prisma/src/utils.ts
deleted file mode 100644
index 93af1504e..000000000
--- a/packages/adapter-prisma/src/utils.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import type { KeySchema, SessionSchema } from "lucia-auth";
-
-export const transformDatabaseSession = (
- sessionData: SessionSchema
-): SessionSchema => {
- const { active_expires, idle_expires: idleExpires, ...data } = sessionData;
- return {
- active_expires: Number(active_expires),
- idle_expires: Number(idleExpires),
- ...data
- };
-};
-
-export const transformDatabaseKey = (keyData: KeySchema): KeySchema => {
- const { expires, ...data } = keyData;
- return {
- expires: expires === null ? null : Number(expires),
- ...data
- };
-};
diff --git a/packages/adapter-prisma/test/db.ts b/packages/adapter-prisma/test/db.ts
deleted file mode 100644
index 067e27653..000000000
--- a/packages/adapter-prisma/test/db.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-import { PrismaClient } from "@prisma/client";
-import prisma from "../src/index.js";
-import {
- transformDatabaseKey,
- transformDatabaseSession
-} from "../src/utils.js";
-import { KeySchema, LuciaError } from "lucia-auth";
-import type { LuciaQueryHandler } from "@lucia-auth/adapter-test";
-
-const client = new PrismaClient();
-export const adapter = prisma(client)(LuciaError);
-
-export const db: LuciaQueryHandler = {
- user: {
- get: async () => {
- return await client.authUser.findMany();
- },
- insert: async (user) => {
- await client.authUser.create({
- data: user
- });
- },
- clear: async () => {
- await client.authUser.deleteMany();
- }
- },
- session: {
- get: async () => {
- const sessions = await client.authSession.findMany();
- return sessions.map((session) => transformDatabaseSession(session));
- },
- insert: async (session) => {
- await client.authSession.create({
- data: session
- });
- },
- clear: async () => {
- await client.authSession.deleteMany();
- }
- },
- key: {
- get: async () => {
- const keys = (await client.authKey.findMany()) as unknown as KeySchema[];
- return keys.map((key) => transformDatabaseKey(key));
- },
- insert: async (key) => {
- await client.authKey.create({
- data: key
- });
- },
- clear: async () => {
- await client.authKey.deleteMany();
- }
- }
-};
diff --git a/packages/adapter-prisma/test/index.ts b/packages/adapter-prisma/test/index.ts
index febd175fb..0989c7bab 100644
--- a/packages/adapter-prisma/test/index.ts
+++ b/packages/adapter-prisma/test/index.ts
@@ -1,4 +1,56 @@
-import { testAdapter } from "@lucia-auth/adapter-test";
-import { db, adapter } from "./db.js";
+import { testAdapter, Database } from "@lucia-auth/adapter-test";
+import { LuciaError } from "lucia";
+import { PrismaClient } from "@prisma/client";
-testAdapter(adapter, db);
+import { prismaAdapter, transformPrismaSession } from "../src/prisma.js";
+
+import type { QueryHandler, TableQueryHandler } from "@lucia-auth/adapter-test";
+import type { SmartPrismaModel } from "../src/prisma.js";
+
+const client = new PrismaClient();
+
+const createTableQueryHandler = (model: any): TableQueryHandler => {
+ const Model = model as SmartPrismaModel;
+ return {
+ get: async () => {
+ return await Model.findMany();
+ },
+ insert: async (value: any) => {
+ await Model.create({
+ data: value
+ });
+ },
+ clear: async () => {
+ await Model.deleteMany();
+ }
+ };
+};
+
+const queryHandler: QueryHandler = {
+ user: createTableQueryHandler(client.user),
+ session: {
+ ...createTableQueryHandler(client.session),
+ get: async () => {
+ const Session = client.session as any as SmartPrismaModel;
+ const result = await Session.findMany();
+ return result.map((val) => transformPrismaSession(val));
+ }
+ },
+ key: createTableQueryHandler(client.key)
+};
+
+const adapter = prismaAdapter({
+ client,
+ models: {
+ user: "user",
+ session: "session",
+ key: "key"
+ },
+ tables: {
+ user: "test_user"
+ }
+})(LuciaError);
+
+await testAdapter(adapter, new Database(queryHandler));
+
+process.exit(0);
diff --git a/packages/adapter-session-redis/.env.example b/packages/adapter-session-redis/.env.example
new file mode 100644
index 000000000..634f46e4c
--- /dev/null
+++ b/packages/adapter-session-redis/.env.example
@@ -0,0 +1 @@
+REDIS_PORT=""
\ No newline at end of file
diff --git a/packages/adapter-session-redis/.gitignore b/packages/adapter-session-redis/.gitignore
index e30934148..7a0ae98eb 100644
--- a/packages/adapter-session-redis/.gitignore
+++ b/packages/adapter-session-redis/.gitignore
@@ -1,4 +1,5 @@
/node_modules
/dist
.DS_Store
-.env
\ No newline at end of file
+.env
+*.tgz
\ No newline at end of file
diff --git a/packages/adapter-session-redis/.npmignore b/packages/adapter-session-redis/.npmignore
deleted file mode 100644
index b512c09d4..000000000
--- a/packages/adapter-session-redis/.npmignore
+++ /dev/null
@@ -1 +0,0 @@
-node_modules
\ No newline at end of file
diff --git a/packages/adapter-session-redis/package.json b/packages/adapter-session-redis/package.json
index c763d1bc7..ab7d3bad8 100644
--- a/packages/adapter-session-redis/package.json
+++ b/packages/adapter-session-redis/package.json
@@ -2,21 +2,22 @@
"name": "@lucia-auth/adapter-session-redis",
"version": "1.0.0",
"description": "Redis session adapter for Lucia",
- "main": "index.js",
- "types": "index.d.ts",
- "module": "index.js",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "module": "dist/index.js",
"type": "module",
"files": [
- "**/*"
+ "/dist/",
+ "CHANGELOG.md"
],
"scripts": {
- "build": "shx rm -rf ./dist/* && tsc && shx cp ./package.json ./dist && shx cp ./README.md ./dist && shx cp .npmignore dist",
+ "build": "shx rm -rf ./dist/* && tsc",
"test": "tsx test/index.ts",
- "auri.publish": "pnpm build && cd dist && pnpm install --no-frozen-lockfile && pnpm publish --no-git-checks --access public && cd ../"
+ "auri.publish": "pnpm build && pnpm publish --no-git-checks --access public"
},
"keywords": [
"lucia",
- "lucia-auth",
+ "lucia",
"auth",
"authentication",
"adapter",
@@ -26,22 +27,22 @@
"repository": {
"type": "git",
"url": "https://github.com/pilcrowOnPaper/lucia",
- "directory": "packages/session-adapter-redis"
+ "directory": "packages/adapter-session-redis"
},
"author": "pilcrowonpaper",
"license": "MIT",
"exports": {
- ".": "./index.js"
+ ".": "./dist/index.js"
},
"peerDependencies": {
- "lucia-auth": "1.x",
- "redis": "4.x"
+ "lucia": "^2.0.0",
+ "redis": "^4.0.0"
},
"devDependencies": {
- "@lucia-auth/adapter-test": "workspace:*",
+ "@lucia-auth/adapter-test": "latest",
"dotenv": "^16.0.3",
"redis": "^4.3.1",
"tsx": "^3.12.6",
- "lucia-auth": "workspace:*"
+ "lucia": "latest"
}
}
diff --git a/packages/adapter-session-redis/src/index.ts b/packages/adapter-session-redis/src/index.ts
index 59c127277..23c8cabc4 100644
--- a/packages/adapter-session-redis/src/index.ts
+++ b/packages/adapter-session-redis/src/index.ts
@@ -1,65 +1 @@
-import type {
- SessionSchema,
- SessionAdapter,
- AdapterFunction
-} from "lucia-auth";
-import type { RedisClientType } from "redis";
-
-const adapter =
- (redisClient: {
- session: RedisClientType;
- userSession: RedisClientType;
- }): AdapterFunction =>
- () => {
- const { session: sessionRedis, userSession: userSessionRedis } =
- redisClient;
- return {
- getSession: async (sessionId) => {
- const sessionData = await sessionRedis.get(sessionId);
- if (!sessionData) return null;
- const session = JSON.parse(sessionData) as SessionSchema;
- return session;
- },
- getSessionsByUserId: async (userId) => {
- const sessionIds = await userSessionRedis.lRange(userId, 0, -1);
- const sessionData = await Promise.all(
- sessionIds.map((id) => sessionRedis.get(id))
- );
- const sessions = sessionData
- .filter((val): val is string => val !== null)
- .map((val) => JSON.parse(val) as SessionSchema);
- return sessions;
- },
- setSession: async (session) => {
- await Promise.all([
- userSessionRedis.lPush(session.user_id, session.id),
- sessionRedis.set(session.id, JSON.stringify(session), {
- EX: Math.floor(Number(session.idle_expires) / 1000)
- })
- ]);
- },
- deleteSession: async (...sessionIds) => {
- const targetSessionData = await Promise.all(
- sessionIds.map((id) => sessionRedis.get(id))
- );
- const sessions = targetSessionData
- .filter((val): val is string => val !== null)
- .map((val) => JSON.parse(val) as SessionSchema);
- await Promise.all([
- ...sessionIds.map((id) => sessionRedis.del(id)),
- ...sessions.map((session) =>
- userSessionRedis.lRem(session.user_id, 1, session.id)
- )
- ]);
- },
- deleteSessionsByUserId: async (userId) => {
- const sessionIds = await userSessionRedis.lRange(userId, 0, -1);
- await Promise.all([
- ...sessionIds.map((id) => sessionRedis.del(id)),
- userSessionRedis.del(userId)
- ]);
- }
- };
- };
-
-export default adapter;
+export { redisSessionAdapter as redis } from "./redis.js";
diff --git a/packages/adapter-session-redis/src/lucia.d.ts b/packages/adapter-session-redis/src/lucia.d.ts
index 48a75f8ab..8026ca988 100644
--- a/packages/adapter-session-redis/src/lucia.d.ts
+++ b/packages/adapter-session-redis/src/lucia.d.ts
@@ -1,9 +1,6 @@
-///
+///
declare namespace Lucia {
type Auth = any;
- type UserAttributes = {};
-}
-
-declare namespace App {
- interface Locals {}
+ type DatabaseUserAttributes = any;
+ type DatabaseSessionAttributes = any;
}
diff --git a/packages/adapter-session-redis/src/redis.ts b/packages/adapter-session-redis/src/redis.ts
new file mode 100644
index 000000000..0344600f7
--- /dev/null
+++ b/packages/adapter-session-redis/src/redis.ts
@@ -0,0 +1,84 @@
+import type { SessionSchema, SessionAdapter, InitializeAdapter } from "lucia";
+import type { RedisClientType } from "redis";
+
+export const DEFAULT_SESSION_PREFIX = "session";
+export const DEFAULT_USER_SESSIONS_PREFIX = "user_sessions";
+
+export const redisSessionAdapter = (
+ client: RedisClientType,
+ prefixes?: {
+ session: string;
+ userSessions: string;
+ }
+): InitializeAdapter => {
+ return () => {
+ const sessionKey = (sessionId: string) => {
+ return [prefixes?.session ?? DEFAULT_SESSION_PREFIX, sessionId].join(":");
+ };
+ const userSessionsKey = (userId: string) => {
+ return [
+ prefixes?.userSessions ?? DEFAULT_USER_SESSIONS_PREFIX,
+ userId
+ ].join(":");
+ };
+
+ return {
+ getSession: async (sessionId) => {
+ const sessionData = await client.get(sessionKey(sessionId));
+ if (!sessionData) return null;
+ const session = JSON.parse(sessionData) as SessionSchema;
+ return session;
+ },
+ getSessionsByUserId: async (userId) => {
+ const sessionIds = await client.sMembers(userSessionsKey(userId));
+ const sessionData = await Promise.all(
+ sessionIds.map((sessionId) => client.get(sessionKey(sessionId)))
+ );
+ const sessions = sessionData
+ .filter((val): val is string => val !== null)
+ .map((val) => JSON.parse(val) as SessionSchema);
+ return sessions;
+ },
+ setSession: async (session) => {
+ await Promise.all([
+ client.sAdd(userSessionsKey(session.user_id), session.id),
+ client.set(sessionKey(session.id), JSON.stringify(session), {
+ EX: Math.floor(Number(session.idle_expires) / 1000)
+ })
+ ]);
+ },
+ deleteSession: async (sessionId) => {
+ const sessionData = await client.get(sessionKey(sessionId));
+ if (!sessionData) return;
+ const session = JSON.parse(sessionData) as SessionSchema;
+ await Promise.all([
+ client.del(sessionKey(sessionId)),
+ client.sRem(userSessionsKey(session.user_id), sessionId)
+ ]);
+ },
+ deleteSessionsByUserId: async (userId) => {
+ const sessionIds = await client.sMembers(userSessionsKey(userId));
+ await Promise.all([
+ ...sessionIds.map((sessionId) => client.del(sessionKey(sessionId))),
+ client.del(userSessionsKey(userId))
+ ]);
+ },
+ updateSession: async (sessionId, partialSession) => {
+ const sessionData = await client.get(sessionKey(sessionId));
+ if (!sessionData) return;
+ const session = JSON.parse(sessionData) as SessionSchema;
+ const updatedSession = {
+ ...session,
+ ...partialSession
+ };
+ await client.set(
+ sessionKey(sessionId),
+ JSON.stringify(updatedSession),
+ {
+ EX: Math.floor(Number(updatedSession.idle_expires) / 1000)
+ }
+ );
+ }
+ };
+ };
+};
diff --git a/packages/adapter-session-redis/test/db.ts b/packages/adapter-session-redis/test/db.ts
deleted file mode 100644
index 30d099410..000000000
--- a/packages/adapter-session-redis/test/db.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import type { LuciaQueryHandler } from "@lucia-auth/adapter-test";
-import type { SessionSchema } from "lucia-auth/types.js";
-
-import { createClient } from "redis";
-import redis from "../src/index.js";
-import { LuciaError } from "lucia-auth";
-
-const sessionInstance = createClient({
- socket: {
- port: 6379
- }
-});
-
-const userSessionInstance = createClient({
- socket: {
- port: 6380
- }
-});
-
-await sessionInstance.connect();
-await userSessionInstance.connect();
-
-export const adapter = redis({
- session: sessionInstance,
- userSession: userSessionInstance
-})(LuciaError);
-
-export const queryHandler: LuciaQueryHandler = {
- session: {
- get: async () => {
- const sessionIds = await sessionInstance.keys("*");
- const sessionData = await Promise.all(
- sessionIds.map((id) => sessionInstance.get(id))
- );
- const sessions = sessionData
- .filter((val): val is string => val !== null)
- .map((data) => JSON.parse(data) as SessionSchema);
-
- return sessions;
- },
- insert: async (session) => {
- await Promise.all([
- sessionInstance.set(session.id, JSON.stringify(session)),
- userSessionInstance.lPush(session.user_id, session.id)
- ]);
- },
- clear: async () => {
- await Promise.all([
- sessionInstance.flushAll(),
- userSessionInstance.flushAll()
- ]);
- }
- }
-};
diff --git a/packages/adapter-session-redis/test/index.ts b/packages/adapter-session-redis/test/index.ts
index 85c39b9e5..cbd55491d 100644
--- a/packages/adapter-session-redis/test/index.ts
+++ b/packages/adapter-session-redis/test/index.ts
@@ -1,4 +1,63 @@
-import { testSessionAdapter } from "@lucia-auth/adapter-test";
-import { queryHandler, adapter } from "./db.js";
+import { testSessionAdapter, Database } from "@lucia-auth/adapter-test";
+import { LuciaError } from "lucia";
+import dotenv from "dotenv";
+import { resolve } from "path";
-testSessionAdapter(adapter, queryHandler);
+import { createClient } from "redis";
+import {
+ redisSessionAdapter,
+ DEFAULT_SESSION_PREFIX,
+ DEFAULT_USER_SESSIONS_PREFIX
+} from "../src/redis.js";
+
+import type { QueryHandler } from "@lucia-auth/adapter-test";
+import type { SessionSchema } from "lucia";
+
+dotenv.config({
+ path: `${resolve()}/.env`
+});
+
+const redisClient = createClient({
+ socket: {
+ port: Number(process.env.REDIS_PORT)
+ }
+});
+
+const sessionKey = (sessionId: string) => {
+ return [DEFAULT_SESSION_PREFIX, sessionId].join(":");
+};
+const userSessionsKey = (userId: string) => {
+ return [DEFAULT_USER_SESSIONS_PREFIX, userId].join(":");
+};
+
+const adapter = redisSessionAdapter(redisClient)(LuciaError);
+
+const queryHandler: QueryHandler = {
+ session: {
+ get: async () => {
+ const keys = await redisClient.keys(sessionKey("*"));
+ const sessionData = await Promise.all(
+ keys.map((key) => redisClient.get(key))
+ );
+ const sessions = sessionData
+ .filter((val): val is string => val !== null)
+ .map((data) => JSON.parse(data) as SessionSchema);
+ return sessions;
+ },
+ insert: async (session) => {
+ await Promise.all([
+ redisClient.set(sessionKey(session.id), JSON.stringify(session)),
+ redisClient.sAdd(userSessionsKey(session.user_id), session.id)
+ ]);
+ },
+ clear: async () => {
+ await redisClient.flushAll();
+ }
+ }
+};
+
+await redisClient.connect();
+
+await testSessionAdapter(adapter, new Database(queryHandler));
+
+process.exit(0);
diff --git a/packages/adapter-sqlite/.gitignore b/packages/adapter-sqlite/.gitignore
index ad2955eb0..9b5a48152 100644
--- a/packages/adapter-sqlite/.gitignore
+++ b/packages/adapter-sqlite/.gitignore
@@ -1,8 +1,6 @@
/node_modules
/dist
.DS_Store
-/prisma/migrations
-/prisma/dev.db
-/prisma/dev.db-journal
.env
-/test/d1/wrangler.toml
+*.tgz
+*.tgz
\ No newline at end of file
diff --git a/packages/adapter-sqlite/.npmignore b/packages/adapter-sqlite/.npmignore
deleted file mode 100644
index e1ac86668..000000000
--- a/packages/adapter-sqlite/.npmignore
+++ /dev/null
@@ -1,8 +0,0 @@
-/node_modules
-.DS_Store
-/src
-/tsconfig.json
-.gitignore
-.env
-/prisma
-/test
\ No newline at end of file
diff --git a/packages/adapter-sqlite/.wrangler/state/d1/DB.sqlite3 b/packages/adapter-sqlite/.wrangler/state/d1/DB.sqlite3
deleted file mode 100644
index e69de29bb..000000000
diff --git a/packages/adapter-sqlite/package.json b/packages/adapter-sqlite/package.json
index 205878937..e6dec229e 100644
--- a/packages/adapter-sqlite/package.json
+++ b/packages/adapter-sqlite/package.json
@@ -2,22 +2,23 @@
"name": "@lucia-auth/adapter-sqlite",
"version": "1.1.1",
"description": "SQLite adapter for Lucia",
- "main": "index.js",
- "types": "index.d.ts",
- "module": "index.js",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "module": "dist/index.js",
"type": "module",
"files": [
- "**/*"
+ "/dist/",
+ "CHANGELOG.md"
],
"scripts": {
- "build": "shx rm -rf ./dist/* && tsc && shx cp ./package.json ./dist && shx cp ./README.md ./dist && shx cp .npmignore dist",
+ "build": "shx rm -rf ./dist/* && tsc",
+ "auri.publish": "pnpm build && pnpm publish --no-git-checks --access public",
"test.better-sqlite3": "tsx test/better-sqlite3/index.ts",
- "test.d1": "tsx test/d1/generate.ts && wrangler dev test/d1/index.ts --local --persist",
- "auri.publish": "pnpm build && cd dist && pnpm install --no-frozen-lockfile && pnpm publish --no-git-checks --access public && cd ../"
+ "test.d1": "tsx test/d1/index.ts"
},
"keywords": [
"lucia",
- "lucia-auth",
+ "lucia",
"auth",
"better-sqlite3",
"sqlite",
@@ -30,16 +31,16 @@
"repository": {
"type": "git",
"url": "https://github.com/pilcrowOnPaper/lucia",
- "directory": "packages/adapter-prisma"
+ "directory": "packages/adapter-sqlite"
},
"author": "pilcrowonpaper",
"license": "MIT",
"exports": {
- ".": "./index.js"
+ ".": "./dist/index.js"
},
"peerDependencies": {
"better-sqlite3": "^8.0.0",
- "lucia-auth": "^1.4.0"
+ "lucia": "^2.0.0"
},
"peerDependenciesMeta": {
"better-sqlite3": {
@@ -47,12 +48,12 @@
}
},
"devDependencies": {
- "@cloudflare/workers-types": "^4.20230404.0",
- "@lucia-auth/adapter-test": "workspace:*",
+ "@cloudflare/workers-types": "^4.20230518.0",
+ "@lucia-auth/adapter-test": "latest",
+ "@miniflare/d1": "^2.14.0",
"@types/better-sqlite3": "^7.6.3",
- "better-sqlite3": "^8.0.1",
- "dotenv": "^16.0.3",
- "lucia-auth": "workspace:*",
+ "better-sqlite3": "^8.4.0",
+ "lucia": "latest",
"tsx": "^3.12.6"
}
}
diff --git a/packages/adapter-sqlite/src/better-sqlite3/index.ts b/packages/adapter-sqlite/src/better-sqlite3/index.ts
deleted file mode 100644
index 6b468f523..000000000
--- a/packages/adapter-sqlite/src/better-sqlite3/index.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-import { createOperator } from "../query.js";
-import { betterSqliteRunner } from "./runner.js";
-import { transformToSqliteValue } from "../utils.js";
-import { createCoreAdapter } from "../core.js";
-
-import type { Adapter, AdapterFunction } from "lucia-auth";
-import type { Database, SqliteError } from "better-sqlite3";
-import type { SQLiteUserSchema } from "../utils.js";
-
-type BetterSQLiteError = SqliteError["prototype"];
-
-export const betterSqlite3 = (db: Database): AdapterFunction => {
- const transaction = async <_Execute extends () => Promise>(
- execute: _Execute
- ): Promise>> => {
- try {
- db.exec("BEGIN TRANSACTION");
- const result = execute();
- db.exec("COMMIT");
- return result;
- } catch (e) {
- if (db.inTransaction) {
- db.exec("ROLLBACK");
- }
- throw e;
- }
- };
-
- return (LuciaError) => {
- const operator = createOperator(betterSqliteRunner(db));
- const coreAdapter = createCoreAdapter(operator);
- return {
- getUser: coreAdapter.getUser,
- getSessionAndUserBySessionId: coreAdapter.getSessionAndUserBySessionId,
- getSession: coreAdapter.getSession,
- getSessionsByUserId: coreAdapter.getSessionsByUserId,
- setUser: async (userId, attributes, key) => {
- const user = {
- id: userId,
- ...attributes
- };
- try {
- if (key) {
- const databaseUser = await transaction(async () => {
- const databaseUser = await operator.get(
- (ctx) => [ctx.insertInto("auth_user", user), ctx.returning("*")]
- );
- if (!databaseUser) throw new TypeError("Unexpected query result");
- await operator.run((ctx) => [
- ctx.insertInto("auth_key", transformToSqliteValue(key))
- ]);
- return databaseUser;
- });
- return databaseUser;
- }
- const databaseUser = await operator.get((ctx) => [
- ctx.insertInto("auth_user", user),
- ctx.returning("*")
- ]);
- if (!databaseUser) throw new TypeError("Unexpected type");
- return databaseUser;
- } catch (e) {
- const error = e as Partial;
- if (
- error.code === "SQLITE_CONSTRAINT_PRIMARYKEY" &&
- error.message?.includes(".id")
- ) {
- throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
- }
- throw e;
- }
- },
- deleteUser: coreAdapter.deleteUser,
- setSession: async (session) => {
- try {
- return await coreAdapter.setSession(session);
- } catch (e) {
- const error = e as Partial;
- if (error.code === "SQLITE_CONSTRAINT_FOREIGNKEY") {
- throw new LuciaError("AUTH_INVALID_USER_ID");
- }
- if (
- error.code === "SQLITE_CONSTRAINT_PRIMARYKEY" &&
- error.message?.includes(".id")
- ) {
- throw new LuciaError("AUTH_DUPLICATE_SESSION_ID");
- }
- throw e;
- }
- },
- deleteSession: coreAdapter.deleteSession,
- deleteSessionsByUserId: coreAdapter.deleteSessionsByUserId,
- updateUserAttributes: coreAdapter.updateUserAttributes,
- setKey: async (key) => {
- try {
- return await coreAdapter.setKey(key);
- } catch (e) {
- const error = e as Partial;
- if (error.code === "SQLITE_CONSTRAINT_FOREIGNKEY") {
- throw new LuciaError("AUTH_INVALID_USER_ID");
- }
- if (
- error.code === "SQLITE_CONSTRAINT_PRIMARYKEY" &&
- error.message?.includes(".id")
- ) {
- throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
- }
- throw e;
- }
- },
- getKey: coreAdapter.getKey,
- getKeysByUserId: coreAdapter.getKeysByUserId,
- updateKeyPassword: coreAdapter.updateKeyPassword,
- deleteKeysByUserId: coreAdapter.deleteKeysByUserId,
- deleteNonPrimaryKey: coreAdapter.deleteNonPrimaryKey
- };
- };
-};
diff --git a/packages/adapter-sqlite/src/better-sqlite3/runner.ts b/packages/adapter-sqlite/src/better-sqlite3/runner.ts
deleted file mode 100644
index aff9a1cd3..000000000
--- a/packages/adapter-sqlite/src/better-sqlite3/runner.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { Runner } from "../query.js";
-import type { Database } from "better-sqlite3";
-
-export const betterSqliteRunner = (db: Database): Runner => {
- return {
- get: async (query, params) => {
- return db.prepare(query).all(params);
- },
- run: async (query, params) => {
- db.prepare(query).run(params);
- }
- };
-};
diff --git a/packages/adapter-sqlite/src/core.ts b/packages/adapter-sqlite/src/core.ts
deleted file mode 100644
index 56a1fae04..000000000
--- a/packages/adapter-sqlite/src/core.ts
+++ /dev/null
@@ -1,157 +0,0 @@
-import {
- transformDatabaseSession,
- transformDatabaseKey,
- transformToSqliteValue
-} from "./utils.js";
-
-import type {
- SQLiteKeySchema,
- SQLiteSessionSchema,
- SQLiteUserSchema
-} from "./utils.js";
-import type { Adapter } from "lucia-auth";
-import type { Operator } from "./query.js";
-
-export const createCoreAdapter = (operator: Operator) => {
- return {
- getUser: async (userId) => {
- return operator.get((ctx) => [
- ctx.selectFrom("auth_user", "*"),
- ctx.where("id", "=", userId)
- ]);
- },
- getSessionAndUserBySessionId: async (sessionId) => {
- const data = await operator.get<
- SQLiteUserSchema & {
- _session_active_expires: number;
- _session_id: string;
- _session_idle_expires: number;
- _session_user_id: string;
- }
- >((ctx) => [
- ctx.selectFrom(
- "auth_session",
- "auth_user.*",
- "auth_session.id as _session_id",
- "auth_session.active_expires as _session_active_expires",
- "auth_session.idle_expires as _session_idle_expires",
- "auth_session.user_id as _session_user_id"
- ),
- ctx.innerJoin("auth_user", "auth_user.id", "auth_session.user_id"),
- ctx.where("auth_session.id", "=", sessionId)
- ]);
- if (!data) return null;
- const {
- _session_active_expires,
- _session_id,
- _session_idle_expires,
- _session_user_id,
- ...user
- } = data;
- return {
- user,
- session: transformDatabaseSession({
- id: _session_id,
- user_id: _session_user_id,
- active_expires: _session_active_expires,
- idle_expires: _session_idle_expires
- })
- };
- },
- getSession: async (sessionId) => {
- const databaseSession = await operator.get((ctx) => [
- ctx.selectFrom("auth_session", "*"),
- ctx.where("id", "=", sessionId)
- ]);
- if (!databaseSession) return null;
- return transformDatabaseSession(databaseSession);
- },
- getSessionsByUserId: async (userId) => {
- const databaseSessions = await operator.getAll(
- (ctx) => [
- ctx.selectFrom("auth_session", "*"),
- ctx.where("user_id", "=", userId)
- ]
- );
- return databaseSessions.map((val) => transformDatabaseSession(val));
- },
- deleteUser: async (userId) => {
- await operator.run((ctx) => [
- ctx.deleteFrom("auth_user"),
- ctx.where("id", "=", userId)
- ]);
- },
- setSession: async (session) => {
- await operator.run((ctx) => [ctx.insertInto("auth_session", session)]);
- },
- deleteSession: async (sessionId) => {
- await operator.run((ctx) => [
- ctx.deleteFrom("auth_session"),
- ctx.where("id", "=", sessionId)
- ]);
- },
- deleteSessionsByUserId: async (userId) => {
- await operator.run((ctx) => [
- ctx.deleteFrom("auth_session"),
- ctx.where("user_id", "=", userId)
- ]);
- },
- updateUserAttributes: async (userId, attributes) => {
- if (Object.keys(attributes).length === 0) {
- operator.run((ctx) => [
- ctx.selectFrom("auth_user", "*"),
- ctx.where("id", "=", userId)
- ]);
- return;
- }
- await operator.run((ctx) => [
- ctx.update("auth_user", attributes),
- ctx.where("id", "=", userId)
- ]);
- },
- setKey: async (key) => {
- await operator.run((ctx) => [
- ctx.insertInto("auth_key", transformToSqliteValue(key))
- ]);
- },
- getKey: async (keyId) => {
- const databaseKey = await operator.get((ctx) => [
- ctx.selectFrom("auth_key", "*"),
- ctx.where("id", "=", keyId)
- ]);
- if (!databaseKey) return null;
- const transformedDatabaseKey = transformDatabaseKey(databaseKey);
- return transformedDatabaseKey;
- },
- getKeysByUserId: async (userId) => {
- const databaseKeys = await operator.getAll((ctx) => [
- ctx.selectFrom("auth_key", "*"),
- ctx.where("user_id", "=", userId)
- ]);
- return databaseKeys.map((val) => transformDatabaseKey(val));
- },
- updateKeyPassword: async (key, hashedPassword) => {
- await operator.run((ctx) => [
- ctx.update("auth_key", {
- hashed_password: hashedPassword
- }),
- ctx.where("id", "=", key)
- ]);
- },
- deleteKeysByUserId: async (userId) => {
- await operator.run((ctx) => [
- ctx.deleteFrom("auth_key"),
- ctx.where("user_id", "=", userId)
- ]);
- },
- deleteNonPrimaryKey: async (keyId) => {
- await operator.run((ctx) => [
- ctx.deleteFrom("auth_key"),
- ctx.and(
- ctx.where("id", "=", keyId),
- ctx.where("primary_key", "=", Number(false))
- )
- ]);
- }
- } satisfies Partial;
-};
diff --git a/packages/adapter-sqlite/src/d1/index.ts b/packages/adapter-sqlite/src/d1/index.ts
deleted file mode 100644
index 8060c44b0..000000000
--- a/packages/adapter-sqlite/src/d1/index.ts
+++ /dev/null
@@ -1,98 +0,0 @@
-import { createOperator } from "../query.js";
-import { d1Runner } from "./runner.js";
-import { transformToSqliteValue } from "../utils.js";
-import { createCoreAdapter } from "../core.js";
-
-import type { Adapter, AdapterFunction, UserSchema } from "lucia-auth";
-import type { SQLiteUserSchema } from "../utils.js";
-import type { D1Database } from "@cloudflare/workers-types";
-
-export const d1 = (db: D1Database): AdapterFunction => {
- return (LuciaError) => {
- const operator = createOperator(d1Runner(db));
- const coreAdapter = createCoreAdapter(operator);
- return {
- ...coreAdapter,
- setUser: async (userId, attributes, key) => {
- const user = {
- id: userId,
- ...attributes
- };
- try {
- if (key) {
- const setUserQuery = operator.write((ctx) => [
- ctx.insertInto("auth_user", user),
- ctx.returning("*")
- ]);
- const setKeyQuery = operator.write((ctx) => [
- ctx.insertInto("auth_key", transformToSqliteValue(key))
- ]);
- const [setUserResult] = await db.batch([
- db.prepare(setUserQuery.statement).bind(...setUserQuery.params),
- db.prepare(setKeyQuery.statement).bind(...setKeyQuery.params)
- ]);
- if (setUserResult.error) throw setUserResult.error;
- if (!setUserResult.results || setUserResult.results.length < 1)
- throw new Error("Unexpected value");
- return setUserResult.results[0] as UserSchema;
- }
- const databaseUser = await operator.get((ctx) => [
- ctx.insertInto("auth_user", user),
- ctx.returning("*")
- ]);
- if (!databaseUser) throw new TypeError("Unexpected type");
- return databaseUser;
- } catch (e) {
- const error = e as Partial<{
- cause: Partial;
- }>;
- if (
- error.cause?.message?.includes("UNIQUE constraint failed") &&
- error.cause?.message?.includes("auth_key.id")
- ) {
- throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
- }
- throw e;
- }
- },
- setSession: async (session) => {
- try {
- return await coreAdapter.setSession(session);
- } catch (e) {
- const error = e as Partial<{
- cause: Partial;
- }>;
- if (error.cause?.message?.includes("FOREIGN KEY constraint failed")) {
- throw new LuciaError("AUTH_INVALID_USER_ID");
- }
- if (
- error.cause?.message?.includes("UNIQUE constraint failed") &&
- error.cause?.message?.includes("auth_session.id")
- ) {
- throw new LuciaError("AUTH_DUPLICATE_SESSION_ID");
- }
- throw e;
- }
- },
- setKey: async (key) => {
- try {
- return await coreAdapter.setKey(key);
- } catch (e) {
- const error = e as Partial<{
- cause: Partial;
- }>;
- if (error.cause?.message?.includes("FOREIGN KEY constraint failed")) {
- throw new LuciaError("AUTH_INVALID_USER_ID");
- }
- if (
- error.cause?.message?.includes("UNIQUE constraint failed") &&
- error.cause?.message?.includes("auth_key.id")
- ) {
- throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
- }
- throw e;
- }
- }
- };
- };
-};
diff --git a/packages/adapter-sqlite/src/d1/runner.ts b/packages/adapter-sqlite/src/d1/runner.ts
deleted file mode 100644
index 65327b772..000000000
--- a/packages/adapter-sqlite/src/d1/runner.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import type { Runner } from "../query.js";
-import type { D1Database } from "@cloudflare/workers-types";
-
-export const d1Runner = (db: D1Database): Runner => {
- return {
- get: async (query, params) => {
- const result = await db
- .prepare(query)
- .bind(...params)
- .all();
- if (result.error) throw result.error;
- return result.results ?? [];
- },
- run: async (query, params) => {
- const result = await db
- .prepare(query)
- .bind(...params)
- .run();
- if (result.error) throw result.error;
- }
- };
-};
diff --git a/packages/adapter-sqlite/src/drivers/better-sqlite3.ts b/packages/adapter-sqlite/src/drivers/better-sqlite3.ts
new file mode 100644
index 000000000..361c71cdb
--- /dev/null
+++ b/packages/adapter-sqlite/src/drivers/better-sqlite3.ts
@@ -0,0 +1,191 @@
+import { helper, getSetArgs, escapeName } from "../utils.js";
+
+import type {
+ SessionSchema,
+ Adapter,
+ InitializeAdapter,
+ UserSchema,
+ KeySchema
+} from "lucia";
+import type { Database, SqliteError } from "better-sqlite3";
+
+type BetterSQLiteError = InstanceType;
+
+export const betterSqlite3Adapter = (
+ db: Database,
+ tables: {
+ user: string;
+ session: string;
+ key: string;
+ }
+): InitializeAdapter => {
+ const transaction = <_Query extends () => any>(query: _Query): void => {
+ try {
+ db.exec("BEGIN TRANSACTION");
+ const result = query();
+ db.exec("COMMIT");
+ return result;
+ } catch (e) {
+ if (db.inTransaction) {
+ db.exec("ROLLBACK");
+ }
+ throw e;
+ }
+ };
+
+ const ESCAPED_USER_TABLE_NAME = escapeName(tables.user);
+ const ESCAPED_SESSION_TABLE_NAME = escapeName(tables.session);
+ const ESCAPED_KEY_TABLE_NAME = escapeName(tables.key);
+
+ return (LuciaError) => {
+ return {
+ getUser: async (userId) => {
+ const result: UserSchema | undefined = db
+ .prepare(`SELECT * FROM ${ESCAPED_USER_TABLE_NAME} WHERE id = ?`)
+ .get(userId);
+ return result ?? null;
+ },
+ setUser: async (user, key) => {
+ const insertUser = () => {
+ const [userFields, userValues, userArgs] = helper(user);
+ db.prepare(
+ `INSERT INTO ${ESCAPED_USER_TABLE_NAME} ( ${userFields} ) VALUES ( ${userValues} )`
+ ).run(...userArgs);
+ };
+ if (!key) return insertUser();
+ try {
+ transaction(() => {
+ insertUser();
+ const [keyFields, keyValues, keyArgs] = helper(key);
+ db.prepare(
+ `INSERT INTO ${ESCAPED_KEY_TABLE_NAME} ( ${keyFields} ) VALUES ( ${keyValues} )`
+ ).run(...keyArgs);
+ });
+ } catch (e) {
+ const error = e as Partial;
+ if (
+ error.code === "SQLITE_CONSTRAINT_PRIMARYKEY" &&
+ error.message?.includes(".id")
+ ) {
+ throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
+ }
+ throw e;
+ }
+ },
+ deleteUser: async (userId) => {
+ db.prepare(`DELETE FROM ${ESCAPED_USER_TABLE_NAME} WHERE id = ?`).run(
+ userId
+ );
+ },
+ updateUser: async (userId, partialUser) => {
+ const [fields, values, args] = helper(partialUser);
+ db.prepare(
+ `UPDATE ${ESCAPED_USER_TABLE_NAME} SET ${getSetArgs(
+ fields,
+ values
+ )} WHERE id = ?`
+ ).run(...args, userId);
+ },
+
+ getSession: async (sessionId) => {
+ const result: SessionSchema | undefined = db
+ .prepare(`SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE id = ?`)
+ .get(sessionId);
+ return result ?? null;
+ },
+ getSessionsByUserId: async (userId) => {
+ const result: SessionSchema[] = db
+ .prepare(
+ `SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE user_id = ?`
+ )
+ .all(userId);
+ return result;
+ },
+ setSession: async (session) => {
+ try {
+ const [fields, values, args] = helper(session);
+ db.prepare(
+ `INSERT INTO ${ESCAPED_SESSION_TABLE_NAME} ( ${fields} ) VALUES ( ${values} )`
+ ).run(...args);
+ } catch (e) {
+ const error = e as Partial;
+ if (error.code === "SQLITE_CONSTRAINT_FOREIGNKEY") {
+ throw new LuciaError("AUTH_INVALID_USER_ID");
+ }
+ throw e;
+ }
+ },
+ deleteSession: async (sessionId) => {
+ db.prepare(
+ `DELETE FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE id = ?`
+ ).run(sessionId);
+ },
+ deleteSessionsByUserId: async (userId) => {
+ db.prepare(
+ `DELETE FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE user_id = ?`
+ ).run(userId);
+ },
+ updateSession: async (sessionId, partialSession) => {
+ const [fields, values, args] = helper(partialSession);
+ db.prepare(
+ `UPDATE ${ESCAPED_SESSION_TABLE_NAME} SET ${getSetArgs(
+ fields,
+ values
+ )} WHERE id = ?`
+ ).run(...args, sessionId);
+ },
+
+ getKey: async (keyId) => {
+ const result: KeySchema | undefined = db
+ .prepare(`SELECT * FROM ${ESCAPED_KEY_TABLE_NAME} WHERE id = ?`)
+ .get(keyId);
+ return result ?? null;
+ },
+ getKeysByUserId: async (userId) => {
+ const result: KeySchema[] = db
+ .prepare(`SELECT * FROM ${ESCAPED_KEY_TABLE_NAME} WHERE user_id = ?`)
+ .all(userId);
+ return result;
+ },
+ setKey: async (key) => {
+ try {
+ const [fields, values, args] = helper(key);
+ db.prepare(
+ `INSERT INTO ${ESCAPED_KEY_TABLE_NAME} ( ${fields} ) VALUES ( ${values} )`
+ ).run(...args);
+ } catch (e) {
+ const error = e as Partial;
+ if (error.code === "SQLITE_CONSTRAINT_FOREIGNKEY") {
+ throw new LuciaError("AUTH_INVALID_USER_ID");
+ }
+ if (
+ error.code === "SQLITE_CONSTRAINT_PRIMARYKEY" &&
+ error.message?.includes(".id")
+ ) {
+ throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
+ }
+ throw e;
+ }
+ },
+ deleteKey: async (keyId) => {
+ db.prepare(`DELETE FROM ${ESCAPED_KEY_TABLE_NAME} WHERE id = ?`).run(
+ keyId
+ );
+ },
+ deleteKeysByUserId: async (userId) => {
+ db.prepare(
+ `DELETE FROM ${ESCAPED_KEY_TABLE_NAME} WHERE user_id = ?`
+ ).run(userId);
+ },
+ updateKey: async (keyId, partialKey) => {
+ const [fields, values, args] = helper(partialKey);
+ db.prepare(
+ `UPDATE ${ESCAPED_KEY_TABLE_NAME} SET ${getSetArgs(
+ fields,
+ values
+ )} WHERE id = ?`
+ ).run(...args, keyId);
+ }
+ };
+ };
+};
diff --git a/packages/adapter-sqlite/src/drivers/d1.ts b/packages/adapter-sqlite/src/drivers/d1.ts
new file mode 100644
index 000000000..c3f96c5d2
--- /dev/null
+++ b/packages/adapter-sqlite/src/drivers/d1.ts
@@ -0,0 +1,243 @@
+import { helper, getSetArgs, escapeName } from "../utils.js";
+
+import type {
+ SessionSchema,
+ Adapter,
+ InitializeAdapter,
+ UserSchema,
+ KeySchema
+} from "lucia";
+import type { D1Database } from "@cloudflare/workers-types";
+
+export const d1Adapter = (
+ db: D1Database,
+ tables: {
+ user: string;
+ session: string;
+ key: string;
+ }
+): InitializeAdapter => {
+ const ESCAPED_USER_TABLE_NAME = escapeName(tables.user);
+ const ESCAPED_SESSION_TABLE_NAME = escapeName(tables.session);
+ const ESCAPED_KEY_TABLE_NAME = escapeName(tables.key);
+
+ return (LuciaError) => {
+ return {
+ getUser: async (userId) => {
+ const user = await db
+ .prepare(`SELECT * FROM ${ESCAPED_USER_TABLE_NAME} WHERE id = ?`)
+ .bind(userId)
+ .first();
+ return user;
+ },
+ setUser: async (user, key) => {
+ const [userFields, userValues, userArgs] = helper(user);
+ const insertUserStatement = db
+ .prepare(
+ `INSERT INTO ${ESCAPED_USER_TABLE_NAME} ( ${userFields} ) VALUES ( ${userValues} )`
+ )
+ .bind(...userArgs);
+ if (!key) {
+ await insertUserStatement.run();
+ return;
+ }
+ try {
+ const [keyFields, keyValues, keyArgs] = helper(key);
+ const insertKeyStatement = db
+ .prepare(
+ `INSERT INTO ${ESCAPED_KEY_TABLE_NAME} ( ${keyFields} ) VALUES ( ${keyValues} )`
+ )
+ .bind(...keyArgs);
+ await db.batch([insertUserStatement, insertKeyStatement]);
+ } catch (e) {
+ const error = e as Partial<{
+ cause: Partial;
+ }>;
+ if (
+ error.cause?.message?.includes("UNIQUE constraint failed") &&
+ error.cause?.message?.includes(`${tables.key}.id`)
+ ) {
+ throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
+ }
+ throw e;
+ }
+ },
+ deleteUser: async (userId) => {
+ await db
+ .prepare(`DELETE FROM ${ESCAPED_USER_TABLE_NAME} WHERE id = ?`)
+ .bind(userId)
+ .run();
+ },
+ updateUser: async (userId, partialUser) => {
+ const [fields, values, args] = helper(partialUser);
+ await db
+ .prepare(
+ `UPDATE ${ESCAPED_USER_TABLE_NAME} SET ${getSetArgs(
+ fields,
+ values
+ )} WHERE id = ?`
+ )
+ .bind(...args, userId)
+ .run();
+ },
+
+ getSession: async (sessionId) => {
+ const session = await db
+ .prepare(`SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE id = ?`)
+ .bind(sessionId)
+ .first();
+ return session;
+ },
+ getSessionsByUserId: async (userId) => {
+ const { results: sessionResults } = await db
+ .prepare(
+ `SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE user_id = ?`
+ )
+ .bind(userId)
+ .all();
+ return sessionResults ?? [];
+ },
+ setSession: async (session) => {
+ try {
+ const [fields, values, args] = helper(session);
+ await db
+ .prepare(
+ `INSERT INTO ${ESCAPED_SESSION_TABLE_NAME} ( ${fields} ) VALUES ( ${values} )`
+ )
+ .bind(...args)
+ .run();
+ } catch (e) {
+ const error = e as Partial<{
+ cause: Partial;
+ }>;
+ if (error.cause?.message?.includes("FOREIGN KEY constraint failed")) {
+ throw new LuciaError("AUTH_INVALID_USER_ID");
+ }
+ throw e;
+ }
+ },
+ deleteSession: async (sessionId) => {
+ await db
+ .prepare(`DELETE FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE id = ?`)
+ .bind(sessionId)
+ .run();
+ },
+ deleteSessionsByUserId: async (userId) => {
+ await db
+ .prepare(
+ `DELETE FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE user_id = ?`
+ )
+ .bind(userId)
+ .run();
+ },
+ updateSession: async (sessionId, partialSession) => {
+ const [fields, values, args] = helper(partialSession);
+ await db
+ .prepare(
+ `UPDATE ${ESCAPED_SESSION_TABLE_NAME} SET ${getSetArgs(
+ fields,
+ values
+ )} WHERE id = ?`
+ )
+ .bind(...args, sessionId)
+ .run();
+ },
+
+ getKey: async (keyId) => {
+ const key = await db
+ .prepare(`SELECT * FROM ${ESCAPED_KEY_TABLE_NAME} WHERE id = ?`)
+ .bind(keyId)
+ .first();
+ return key;
+ },
+ getKeysByUserId: async (userId) => {
+ const { results: keyResults } = await db
+ .prepare(`SELECT * FROM ${ESCAPED_KEY_TABLE_NAME} WHERE user_id = ?`)
+ .bind(userId)
+ .all();
+ return keyResults ?? [];
+ },
+ setKey: async (key) => {
+ try {
+ const [fields, values, args] = helper(key);
+ await db
+ .prepare(
+ `INSERT INTO ${ESCAPED_KEY_TABLE_NAME} ( ${fields} ) VALUES ( ${values} )`
+ )
+ .bind(...args)
+ .run();
+ } catch (e) {
+ const error = e as Partial<{
+ cause: Partial;
+ }>;
+ if (error.cause?.message?.includes("FOREIGN KEY constraint failed")) {
+ throw new LuciaError("AUTH_INVALID_USER_ID");
+ }
+ if (
+ error.cause?.message?.includes("UNIQUE constraint failed") &&
+ error.cause?.message?.includes(`${tables.key}.id`)
+ ) {
+ throw new LuciaError("AUTH_DUPLICATE_KEY_ID");
+ }
+ throw e;
+ }
+ },
+ deleteKey: async (keyId) => {
+ await db
+ .prepare(`DELETE FROM ${ESCAPED_KEY_TABLE_NAME} WHERE id = ?`)
+ .bind(keyId)
+ .run();
+ },
+ deleteKeysByUserId: async (userId) => {
+ await db
+ .prepare(`DELETE FROM ${ESCAPED_KEY_TABLE_NAME} WHERE user_id = ?`)
+ .bind(userId)
+ .run();
+ },
+ updateKey: async (keyId, partialKey) => {
+ const [fields, values, args] = helper(partialKey);
+ await db
+ .prepare(
+ `UPDATE ${ESCAPED_KEY_TABLE_NAME} SET ${getSetArgs(
+ fields,
+ values
+ )} WHERE id = ?`
+ )
+ .bind(...args, keyId)
+ .run();
+ },
+
+ getSessionAndUser: async (sessionId) => {
+ const getSessionStatement = db
+ .prepare(`SELECT * FROM ${ESCAPED_SESSION_TABLE_NAME} WHERE id = ?`)
+ .bind(sessionId);
+ const getUserFromJoinStatement = db
+ .prepare(
+ `SELECT ${ESCAPED_USER_TABLE_NAME}.*, ${ESCAPED_SESSION_TABLE_NAME}.id as __session_id FROM ${ESCAPED_SESSION_TABLE_NAME} INNER JOIN ${ESCAPED_USER_TABLE_NAME} ON ${ESCAPED_USER_TABLE_NAME}.id = ${ESCAPED_SESSION_TABLE_NAME}.user_id WHERE ${ESCAPED_SESSION_TABLE_NAME}.id = ?`
+ )
+ .bind(sessionId);
+ type BatchQueryResult = {
+ error: any;
+ results?: Schema[];
+ };
+ const [{ results: sessionResults }, { results: userFromJoinResults }] =
+ (await db.batch([
+ getSessionStatement,
+ getUserFromJoinStatement
+ ])) as any as [
+ BatchQueryResult,
+ BatchQueryResult<
+ UserSchema & {
+ __session_id: string;
+ }
+ >
+ ];
+ const sessionResult = sessionResults?.at(0) ?? null;
+ const userFromJoinResult = userFromJoinResults?.at(0) ?? null;
+ if (!sessionResult || !userFromJoinResult) return [null, null];
+ const { __session_id: _, ...userResult } = userFromJoinResult;
+ return [sessionResult, userResult];
+ }
+ };
+ };
+};
diff --git a/packages/adapter-sqlite/src/index.ts b/packages/adapter-sqlite/src/index.ts
index b36f2927d..21a65d4de 100644
--- a/packages/adapter-sqlite/src/index.ts
+++ b/packages/adapter-sqlite/src/index.ts
@@ -1,2 +1,2 @@
-export { betterSqlite3 } from "./better-sqlite3/index.js";
-export { d1 } from "./d1/index.js";
+export { betterSqlite3Adapter as betterSqlite3 } from "./drivers/better-sqlite3.js";
+export { d1Adapter as d1 } from "./drivers/d1.js";
diff --git a/packages/adapter-sqlite/src/lucia.d.ts b/packages/adapter-sqlite/src/lucia.d.ts
index f61de1891..8026ca988 100644
--- a/packages/adapter-sqlite/src/lucia.d.ts
+++ b/packages/adapter-sqlite/src/lucia.d.ts
@@ -1,5 +1,6 @@
-///
+///
declare namespace Lucia {
type Auth = any;
- type UserAttributes = {};
+ type DatabaseUserAttributes = any;
+ type DatabaseSessionAttributes = any;
}
diff --git a/packages/adapter-sqlite/src/query.ts b/packages/adapter-sqlite/src/query.ts
deleted file mode 100644
index 4978dbfba..000000000
--- a/packages/adapter-sqlite/src/query.ts
+++ /dev/null
@@ -1,250 +0,0 @@
-const resolveQueryBlock = (block: Block): ResolvedBlock => {
- const escapeName = (val: string) => {
- if (val === "*") return val;
- return `\`${val}\``;
- };
- if (block.type === "DELETE_FROM") {
- return {
- queryChunk: `DELETE FROM ${escapeName(block.table)}`,
- params: []
- };
- }
- if (block.type === "INNER_JOIN") {
- return {
- queryChunk: `INNER JOIN ${escapeName(block.targetTable)} ON ${
- block.targetColumn
- } = ${block.column}`,
- params: []
- };
- }
- if (block.type === "RETURNING") {
- return {
- queryChunk: `RETURNING ${block.columns}`,
- params: []
- };
- }
- if (block.type === "INSERT_INTO") {
- const keys = Object.keys(block.values);
- return {
- queryChunk: `INSERT INTO ${escapeName(block.table)} (${keys.map((k) =>
- escapeName(k)
- )}) VALUES (${Array(keys.length).fill("?")})`,
- params: keys.map((k) => block.values[k])
- };
- }
- if (block.type === "SELECT") {
- return {
- queryChunk: `SELECT ${block.columns} FROM ${escapeName(block.table)}`,
- params: []
- };
- }
- if (block.type === "WHERE") {
- return {
- queryChunk: `WHERE ${block.column} ${block.comparator} ?`,
- params: [block.value]
- };
- }
- if (block.type === "UPDATE") {
- const keys = Object.keys(block.values);
- return {
- queryChunk: `UPDATE ${escapeName(block.table)} SET ${keys.map((k) => {
- return `${escapeName(k)} = ?`;
- })}`,
- params: keys.map((k) => block.values[k])
- };
- }
- if (block.type === "AND") {
- const resolvedConditionQueryBlocks = block.whereBlocks.map((whereBlock) => {
- return {
- queryChunk: `${whereBlock.column} ${whereBlock.comparator} ?`,
- params: [whereBlock.value]
- };
- });
- const conditionQueryChunk = resolvedConditionQueryBlocks
- .map((resolvedBlock) => resolvedBlock.queryChunk)
- .join(" AND ");
- return {
- queryChunk: `WHERE ${conditionQueryChunk}`,
- params: resolvedConditionQueryBlocks.reduce(
- (acc, curr) => [...acc, ...curr.params],
- [] as ColumnValue[]
- )
- };
- }
- throw new TypeError(`Invalid block type`);
-};
-
-const ctx = {
- innerJoin: (targetTable: string, targetColumn: string, column: string) => {
- return {
- type: "INNER_JOIN",
- targetTable,
- targetColumn,
- column
- };
- },
- returning: (...columns: [string, ...string[]]) => {
- return {
- type: "RETURNING",
- columns
- };
- },
- selectFrom: (table: string, ...columns: [string, ...string[]]) => {
- return {
- type: "SELECT",
- table,
- columns
- };
- },
- insertInto: (table: string, values: Record) => {
- return {
- type: "INSERT_INTO",
- table,
- values
- };
- },
- where: (column: string, comparator: string, value: ColumnValue) => {
- return {
- type: "WHERE",
- column,
- comparator,
- value
- };
- },
- deleteFrom: (table: string) => {
- return {
- type: "DELETE_FROM",
- table
- };
- },
- update: (table: string, values: Record) => {
- return {
- type: "UPDATE",
- table,
- values
- };
- },
- and: (...whereBlocks: WhereBlock[]) => {
- return {
- type: "AND",
- whereBlocks
- };
- }
-} satisfies Record Block>;
-
-export const createOperator = <_Runner extends Runner>(runner: _Runner) => {
- const resolveQueryBlocks = (blocks: Block[]) => {
- const resolvedBlocks = blocks.map(resolveQueryBlock);
- const statement = resolvedBlocks.map((block) => block.queryChunk).join(" ");
- const params = resolvedBlocks.reduce((result, block) => {
- result.push(...block.params);
- return result;
- }, [] as ColumnValue[]);
- return {
- statement,
- params
- };
- };
-
- const write = <_Selection extends Record>(
- createQueryBlocks: CreateQueryBlocks
- ) => {
- const blocks = createQueryBlocks(ctx);
- return resolveQueryBlocks(blocks);
- };
- const get = async <_Selection extends Record>(
- createQueryBlocks: CreateQueryBlocks
- ): Promise<_Selection | null> => {
- const query = write(createQueryBlocks);
- const result = await runner.get(query.statement, query.params);
- if (Array.isArray(result)) return result.at(0) ?? null;
- return result ?? null;
- };
- const getAll = async <_Selection extends Record>(
- createQueryBlocks: CreateQueryBlocks
- ): Promise<_Selection[]> => {
- const query = write(createQueryBlocks);
- const result = await runner.get(query.statement, query.params);
- if (!result) return [] as any;
- if (!Array.isArray(result)) return [result] as any;
- return result as any;
- };
- const run = <_Selection extends Record>(
- createQueryBlocks: CreateQueryBlocks
- ): Promise => {
- const query = write(createQueryBlocks);
- return runner.run(query.statement, query.params) as any;
- };
- return {
- write,
- get,
- getAll,
- run
- } as const;
-};
-
-export type Operator = ReturnType;
-
-export type Context = typeof ctx;
-
-type CreateQueryBlocks = (context: Context) => Block[];
-
-type ResolvedBlock = {
- queryChunk: string;
- params: ColumnValue[];
-};
-
-export type Runner = {
- get: (statement: string, params: ColumnValue[]) => Promise;
- run: (statement: string, params: ColumnValue[]) => Promise;
-};
-type ColumnValue = string | number | null | bigint;
-
-type Block =
- | {
- type: "INNER_JOIN";
- targetTable: string;
- targetColumn: string;
- column: string;
- }
- | {
- type: "SELECT";
- table: string;
- columns: string[];
- }
- | {
- type: "INSERT_INTO";
- table: string;
- values: Record;
- }
- | {
- type: "WHERE";
- column: string;
- comparator: string;
- value: ColumnValue;
- }
- | {
- type: "AND";
- whereBlocks: WhereBlock[];
- }
- | {
- type: "DELETE_FROM";
- table: string;
- }
- | {
- type: "UPDATE";
- table: string;
- values: Record;
- }
- | {
- type: "RETURNING";
- columns: string[];
- }
- | WhereBlock;
-
-type WhereBlock = {
- type: "WHERE";
- column: string;
- comparator: string;
- value: ColumnValue;
-};
diff --git a/packages/adapter-sqlite/src/utils.ts b/packages/adapter-sqlite/src/utils.ts
index b630bb75f..76b7007c2 100644
--- a/packages/adapter-sqlite/src/utils.ts
+++ b/packages/adapter-sqlite/src/utils.ts
@@ -1,49 +1,29 @@
-import type { KeySchema, SessionSchema, UserSchema } from "lucia-auth";
-
-export const transformToSqliteValue = <_Obj extends Record>(
- obj: _Obj
-): {
- [K in keyof _Obj]: _Obj[K] extends boolean
- ? number | Exclude<_Obj[K], boolean>
- : _Obj[K];
-} => {
- return Object.fromEntries(
- Object.entries(obj).map(([key, val]) => {
- if (typeof val !== "boolean") return [key, val];
- return [key, Number(val)];
- })
- ) as any;
-};
-
-export const transformDatabaseSession = (
- session: SQLiteSessionSchema
-): SessionSchema => {
- return {
- id: session.id,
- user_id: session.user_id,
- active_expires: Number(session.active_expires),
- idle_expires: Number(session.idle_expires)
+const createPreparedStatementHelper = (
+ placeholder: (index: number) => string
+) => {
+ const helper = (
+ values: Record
+ ): readonly [fields: string[], placeholders: string[], arguments: any[]] => {
+ const keys = Object.keys(values);
+ return [
+ keys.map((k) => escapeName(k)),
+ keys.map((_, i) => placeholder(i)),
+ keys.map((k) => values[k])
+ ] as const;
};
+ return helper;
};
-export const transformDatabaseKey = (key: SQLiteKeySchema): KeySchema => {
- return {
- id: key.id,
- user_id: key.user_id,
- primary_key: Boolean(key.primary_key),
- hashed_password: key.hashed_password,
- expires: key.expires === null ? null : Number(key.expires)
- };
+export const escapeName = (val: string) => {
+ return `${ESCAPE_CHAR}${val}${ESCAPE_CHAR}`;
};
-export type SQLiteUserSchema = UserSchema;
-export type SQLiteSessionSchema = SessionSchema;
-export type SQLiteKeySchema = TransformToSQLiteSchema;
+const ESCAPE_CHAR = "`";
-export type ReplaceBooleanWithNumber = Extract extends never
- ? T
- : Exclude | number;
+export const helper = createPreparedStatementHelper(() => "?");
-export type TransformToSQLiteSchema<_Schema extends {}> = {
- [K in keyof _Schema]: ReplaceBooleanWithNumber<_Schema[K]>;
+export const getSetArgs = (fields: string[], placeholders: string[]) => {
+ return fields
+ .map((field, i) => [field, placeholders[i]].join(" = "))
+ .join(",");
};
diff --git a/packages/adapter-sqlite/test/better-sqlite3/db.ts b/packages/adapter-sqlite/test/better-sqlite3/db.ts
deleted file mode 100644
index 486f10bdc..000000000
--- a/packages/adapter-sqlite/test/better-sqlite3/db.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import sqlite from "better-sqlite3";
-import dotenv from "dotenv";
-import { resolve } from "path";
-import { LuciaError } from "lucia-auth";
-
-import { betterSqlite3 as betterSqlite3Adapter } from "../../src/index.js";
-import { betterSqliteRunner } from "../../src/better-sqlite3/runner.js";
-import { createQueryHandler } from "../index.js";
-
-dotenv.config({
- path: `${resolve()}/.env`
-});
-
-const db = sqlite("test/main.db");
-
-export const adapter = betterSqlite3Adapter(db)(LuciaError);
-export const queryHandler = createQueryHandler(betterSqliteRunner(db));
diff --git a/packages/adapter-sqlite/test/better-sqlite3/index.ts b/packages/adapter-sqlite/test/better-sqlite3/index.ts
index ee388e4f5..24fb4913c 100644
--- a/packages/adapter-sqlite/test/better-sqlite3/index.ts
+++ b/packages/adapter-sqlite/test/better-sqlite3/index.ts
@@ -1,4 +1,36 @@
-import { testAdapter } from "@lucia-auth/adapter-test";
-import { adapter, queryHandler } from "./db.js";
+import { testAdapter, Database } from "@lucia-auth/adapter-test";
+import { LuciaError } from "lucia";
-testAdapter(adapter, queryHandler);
+import { TABLE_NAMES, db } from "../db.js";
+import { betterSqlite3Adapter } from "../../src/drivers/better-sqlite3.js";
+import { escapeName, helper } from "../../src/utils.js";
+
+import type { QueryHandler, TableQueryHandler } from "@lucia-auth/adapter-test";
+
+const createTableQueryHandler = (tableName: string): TableQueryHandler => {
+ const ESCAPED_TABLE_NAME = escapeName(tableName);
+ return {
+ get: async () => {
+ return db.prepare(`SELECT * FROM ${ESCAPED_TABLE_NAME}`).all();
+ },
+ insert: async (value: any) => {
+ const [fields, placeholders, args] = helper(value);
+ db.prepare(
+ `INSERT INTO ${ESCAPED_TABLE_NAME} ( ${fields} ) VALUES ( ${placeholders} )`
+ ).run(...args);
+ },
+ clear: async () => {
+ db.exec(`DELETE FROM ${ESCAPED_TABLE_NAME}`);
+ }
+ };
+};
+
+const queryHandler: QueryHandler = {
+ user: createTableQueryHandler(TABLE_NAMES.user),
+ session: createTableQueryHandler(TABLE_NAMES.session),
+ key: createTableQueryHandler(TABLE_NAMES.key)
+};
+
+const adapter = betterSqlite3Adapter(db, TABLE_NAMES)(LuciaError);
+
+testAdapter(adapter, new Database(queryHandler));
diff --git a/packages/adapter-sqlite/test/d1/generate.ts b/packages/adapter-sqlite/test/d1/generate.ts
deleted file mode 100644
index 2471174f5..000000000
--- a/packages/adapter-sqlite/test/d1/generate.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import dotenv from "dotenv";
-import fs from "fs";
-import path from "path";
-import { resolve } from "path";
-
-dotenv.config({
- path: `${resolve()}/.env`
-});
-
-const tomlFile = `
-node_compat = true
-
-[[d1_databases]]
-binding = "${process.env.D1_DATABASE_BINDING}"
-database_name = "${process.env.D1_DATABASE_NAME}"
-database_id = "${process.env.D1_DATABASE_ID}"
-`;
-
-fs.writeFileSync(path.resolve("./test/d1/wrangler.toml"), tomlFile);
diff --git a/packages/adapter-sqlite/test/d1/index.ts b/packages/adapter-sqlite/test/d1/index.ts
index 1d3f2c2a2..74215bdbb 100644
--- a/packages/adapter-sqlite/test/d1/index.ts
+++ b/packages/adapter-sqlite/test/d1/index.ts
@@ -1,19 +1,47 @@
-import { testAdapter } from "@lucia-auth/adapter-test";
-import { LuciaError } from "lucia-auth";
-import { d1 as d1Adapter } from "../../src/index.js";
-import { d1Runner } from "../../src/d1/runner.js";
-import { createQueryHandler } from "../index.js";
-import { D1Database } from "@cloudflare/workers-types";
-
-type Env = {
- DB: D1Database;
+import { testAdapter, Database } from "@lucia-auth/adapter-test";
+import { LuciaError } from "lucia";
+import { D1Database, D1DatabaseAPI } from "@miniflare/d1";
+
+import { d1Adapter } from "../../src/drivers/d1.js";
+import { escapeName, helper } from "../../src/utils.js";
+import { TABLE_NAMES, db } from "../db.js";
+
+import type { D1Database as WorkerD1Database } from "@cloudflare/workers-types";
+import type { QueryHandler, TableQueryHandler } from "@lucia-auth/adapter-test";
+
+const D1 = new D1Database(new D1DatabaseAPI(db)) as any as WorkerD1Database;
+
+const createTableQueryHandler = (tableName: string): TableQueryHandler => {
+ const ESCAPED_TABLE_NAME = escapeName(tableName);
+ return {
+ get: async () => {
+ const { results } = await D1.prepare(
+ `SELECT * FROM ${ESCAPED_TABLE_NAME}`
+ ).all();
+ return results ?? [];
+ },
+ insert: async (value: any) => {
+ const [fields, placeholders, args] = helper(value);
+ await D1.prepare(
+ `INSERT INTO ${ESCAPED_TABLE_NAME} ( ${fields} ) VALUES ( ${placeholders} )`
+ )
+ .bind(...args)
+ .run();
+ },
+ clear: async () => {
+ await D1.exec(`DELETE FROM ${ESCAPED_TABLE_NAME}`);
+ }
+ };
};
-export default {
- fetch: async (_: Request, env: Env) => {
- const adapter = d1Adapter(env.DB)(LuciaError);
- const queryHandler = createQueryHandler(d1Runner(env.DB));
- await testAdapter(adapter, queryHandler, false);
- return new Response("Test successful");
- }
+const queryHandler: QueryHandler = {
+ user: createTableQueryHandler(TABLE_NAMES.user),
+ session: createTableQueryHandler(TABLE_NAMES.session),
+ key: createTableQueryHandler(TABLE_NAMES.key)
};
+
+const adapter = d1Adapter(D1, TABLE_NAMES)(LuciaError);
+
+await testAdapter(adapter, new Database(queryHandler));
+
+process.exit(0);
diff --git a/packages/adapter-sqlite/test/db.ts b/packages/adapter-sqlite/test/db.ts
new file mode 100644
index 000000000..4bb8bd093
--- /dev/null
+++ b/packages/adapter-sqlite/test/db.ts
@@ -0,0 +1,9 @@
+import sqlite from "better-sqlite3";
+
+export const db = sqlite("test/main.db");
+
+export const TABLE_NAMES = {
+ user: "test_user",
+ session: "user_session",
+ key: "user_key"
+};
diff --git a/packages/adapter-sqlite/test/index.ts b/packages/adapter-sqlite/test/index.ts
deleted file mode 100644
index cd66f84f7..000000000
--- a/packages/adapter-sqlite/test/index.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { LuciaQueryHandler } from "@lucia-auth/adapter-test";
-import { createOperator, Runner } from "../src/query.js";
-import {
- transformDatabaseKey,
- transformDatabaseSession,
- transformToSqliteValue
-} from "../src/utils.js";
-
-import type { SQLiteKeySchema, SQLiteSessionSchema } from "../src/utils.js";
-import type { TestUserSchema } from "@lucia-auth/adapter-test";
-
-export const createQueryHandler = (runner: Runner) => {
- const operator = createOperator(runner);
- return {
- user: {
- get: async () => {
- return operator.getAll((ctx) => [
- ctx.selectFrom("auth_user", "*")
- ]);
- },
- insert: async (user) => {
- await operator.run((ctx) => [ctx.insertInto("auth_user", user)]);
- },
- clear: async () => {
- await operator.run((ctx) => [ctx.deleteFrom("auth_user")]);
- }
- },
- session: {
- get: async () => {
- const databaseSessions = await operator.getAll(
- (ctx) => [ctx.selectFrom("auth_session", "*")]
- );
- return databaseSessions.map((val) => transformDatabaseSession(val));
- },
- insert: async (key) => {
- await operator.run((ctx) => [ctx.insertInto("auth_session", key)]);
- },
- clear: async () => {
- await operator.run((ctx) => [ctx.deleteFrom("auth_session")]);
- }
- },
- key: {
- get: async () => {
- const databaseKeys = await operator.getAll((ctx) => [
- ctx.selectFrom("auth_key", "*")
- ]);
- return databaseKeys.map((val) => transformDatabaseKey(val));
- },
- insert: async (key) => {
- await operator.run((ctx) => [
- ctx.insertInto("auth_key", transformToSqliteValue(key))
- ]);
- },
- clear: async () => {
- await operator.run((ctx) => [ctx.deleteFrom("auth_key")]);
- }
- }
- } satisfies LuciaQueryHandler;
-};
diff --git a/packages/adapter-sqlite/test/main.db b/packages/adapter-sqlite/test/main.db
index ebaa015a2..4e504bb6a 100644
Binary files a/packages/adapter-sqlite/test/main.db and b/packages/adapter-sqlite/test/main.db differ
diff --git a/packages/adapter-sqlite/tsconfig.json b/packages/adapter-sqlite/tsconfig.json
index 0bf7d70b9..56358e496 100644
--- a/packages/adapter-sqlite/tsconfig.json
+++ b/packages/adapter-sqlite/tsconfig.json
@@ -11,6 +11,5 @@
"outDir": "./dist",
"strict": true
},
- "include": ["src"],
- "exclude": ["node_modules/", "**/__tests__/*"]
+ "include": ["src"]
}
diff --git a/packages/adapter-test/.gitignore b/packages/adapter-test/.gitignore
index 4fe613cbf..7a0ae98eb 100644
--- a/packages/adapter-test/.gitignore
+++ b/packages/adapter-test/.gitignore
@@ -1,3 +1,5 @@
/node_modules
/dist
-.DS_Store
\ No newline at end of file
+.DS_Store
+.env
+*.tgz
\ No newline at end of file
diff --git a/packages/adapter-test/.npmignore b/packages/adapter-test/.npmignore
deleted file mode 100644
index b512c09d4..000000000
--- a/packages/adapter-test/.npmignore
+++ /dev/null
@@ -1 +0,0 @@
-node_modules
\ No newline at end of file
diff --git a/packages/adapter-test/README.md b/packages/adapter-test/README.md
index c91093f94..b4b43345e 100644
--- a/packages/adapter-test/README.md
+++ b/packages/adapter-test/README.md
@@ -1,6 +1,6 @@
# Tests for Lucia adapters
-Testing package for adapters for Lucia
+Testing module for Lucia database adapters.
**[Documentation](https://lucia-auth.com/adapters/testing-adapters)**
@@ -15,5 +15,3 @@ npm i -D @lucia-auth/adapter-test
pnpm add -D @lucia-auth/adapter-test
yarn add -D @lucia-auth/adapter-test
```
-
-Requires `lucia-auth@0.11.0`.
diff --git a/packages/adapter-test/package.json b/packages/adapter-test/package.json
index 1c905ff6a..60148e505 100644
--- a/packages/adapter-test/package.json
+++ b/packages/adapter-test/package.json
@@ -1,21 +1,22 @@
{
"name": "@lucia-auth/adapter-test",
"version": "3.0.1",
- "description": "Tests for database adapters for Lucia",
- "main": "index.js",
- "types": "index.d.ts",
- "module": "index.js",
+ "description": "Testing module for Lucia database adapters",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "module": "dist/index.js",
"type": "module",
"files": [
- "**/*"
+ "/dist/",
+ "CHANGELOG.md"
],
"scripts": {
- "build": "shx rm -rf ./dist/* && tsc && shx cp ./package.json ./dist && shx cp ./README.md ./dist && shx cp .npmignore dist",
- "auri.publish": "pnpm build && cd dist && pnpm install --no-frozen-lockfile && pnpm publish --no-git-checks --access public && cd ../"
+ "build": "shx rm -rf ./dist/* && tsc",
+ "auri.publish": "pnpm build && pnpm publish --no-git-checks --access public"
},
"keywords": [
"lucia",
- "lucia-auth",
+ "lucia",
"auth",
"authentication",
"adapter",
@@ -29,16 +30,16 @@
"author": "pilcrowonpaper",
"license": "MIT",
"exports": {
- ".": "./index.js"
+ ".": "./dist/index.js"
},
"devDependencies": {
- "@types/cli-color": "^2.0.2",
- "lucia-auth": "workspace:*"
- },
- "dependencies": {
- "cli-color": "^2.0.3"
+ "@types/mocha": "^10.0.1",
+ "lucia": "latest"
},
"peerDependencies": {
- "lucia-auth": "^1.3.0"
+ "lucia": "^2.0.0"
+ },
+ "dependencies": {
+ "mocha": "^10.2.0"
}
}
diff --git a/packages/adapter-test/src/database.ts b/packages/adapter-test/src/database.ts
index 579607b2f..b9a9bd6de 100644
--- a/packages/adapter-test/src/database.ts
+++ b/packages/adapter-test/src/database.ts
@@ -1,212 +1,125 @@
-import { generateRandomString } from "lucia-auth";
-import { typeError, valueError } from "./validate.js";
-import type { KeySchema, SessionSchema } from "lucia-auth";
+import { generateRandomString } from "lucia/utils";
+import type { KeySchema, SessionSchema, UserSchema } from "lucia";
-export type TestUserSchema = {
- id: string;
+export type TestUserSchema = UserSchema & {
username: string;
};
-type QueryHandler