From ccefb64c39ad9f009686171932b31783a03ce44e Mon Sep 17 00:00:00 2001 From: Dylan Vanmali Date: Wed, 25 Sep 2024 17:22:51 -0700 Subject: [PATCH 01/14] feat: transition from surrealdb.js beta to surrealdb@1.0.0 --- .../getting-started/adapters/surrealdb.mdx | 85 ++-- packages/adapter-surrealdb/package.json | 9 +- packages/adapter-surrealdb/src/index.ts | 448 ++++++++++-------- packages/adapter-surrealdb/test/common.ts | 68 ++- packages/adapter-surrealdb/test/index.test.ts | 34 +- 5 files changed, 311 insertions(+), 333 deletions(-) diff --git a/docs/pages/getting-started/adapters/surrealdb.mdx b/docs/pages/getting-started/adapters/surrealdb.mdx index 4411e84206..b2a1f77f94 100644 --- a/docs/pages/getting-started/adapters/surrealdb.mdx +++ b/docs/pages/getting-started/adapters/surrealdb.mdx @@ -13,17 +13,17 @@ import { Code } from "@/components/Code" ### Installation ```bash npm2yarn -npm install @auth/surrealdb-adapter surrealdb.js +npm install @auth/surrealdb-adapter surrealdb ``` ### Environment Variables ```sh -AUTH_SURREALDB_CONNECTION -AUTH_SURREALDB_USERNAME -AUTH_SURREALDB_PASSWORD -AUTH_SURREALDB_NS -AUTH_SURREALDB_DB +AUTH_SURREAL_URL, +AUTH_SURREAL_NS, +AUTH_SURREAL_DB, +AUTH_SURREAL_USER, +AUTH_SURREAL_PW, ``` ### Configuration @@ -99,66 +99,35 @@ The SurrealDB adapter does not handle connections automatically, so you will hav ### Authorization -#### Option 1 – Using RPC: +#### Using WS: -```ts filename="./lib/surrealdb.ts" -import { Surreal } from "surrealdb.js" +With this configuration, we can use the websocket endpoint. Thus, the `AUTH_SURREAL_URL` should begin with `ws` or `wss`. -const connectionString = process.env.AUTH_SURREALDB_CONNECTION -const user = process.env.AUTH_SURREALDB_USERNAME -const pass = process.env.AUTH_SURREALDB_PASSWORD -const ns = process.env.AUTH_SURREALDB_NS -const db = process.env.AUTH_SURREALDB_DB +```ts filename="./lib/surrealdb.ts" +import { Surreal } from "surrealdb" const clientPromise = new Promise(async (resolve, reject) => { - const db = new Surreal() try { - await db.connect(`${connectionString}/rpc`, { - ns, - db, - auth: { user, pass }, - }) + const db = new Surreal() + const { + AUTH_SURREAL_URL: url, + AUTH_SURREAL_NS: namespace, + AUTH_SURREAL_DB: database, + AUTH_SURREAL_USER: username, + AUTH_SURREAL_PW: password, + } = process.env + if (url && namespace && database && username && password) { + await db.connect(url) + await db.signin({ + namespace, + database, + username, + password, + }) + } resolve(db) } catch (e) { reject(e) } }) - -// Export a module-scoped MongoClient promise. By doing this in a -// separate module, the client can be shared across functions. -export default clientPromise -``` - -#### Option 2 – Using HTTP: - -Useful in serverless environments like Vercel. - -```ts filename="./lib/surrealdb.ts" -import { ExperimentalSurrealHTTP } from "surrealdb.js" - -const connectionString = process.env.AUTH_SURREALDB_CONNECTION -const user = process.env.AUTH_SURREALDB_USERNAME -const pass = process.env.AUTH_SURREALDB_PASSWORD -const ns = process.env.AUTH_SURREALDB_NS -const db = process.env.AUTH_SURREALDB_DB - -const clientPromise = new Promise>( - async (resolve, reject) => { - try { - const db = new ExperimentalSurrealHTTP(connectionString, { - fetch, - ns, - db, - auth: { user, pass }, - }) - resolve(db) - } catch (e) { - reject(e) - } - } -) - -// Export a module-scoped MongoClient promise. By doing this in a -// separate module, the client can be shared across functions. -export default clientPromise ``` diff --git a/packages/adapter-surrealdb/package.json b/packages/adapter-surrealdb/package.json index 56e6f5794e..1fc04d32af 100644 --- a/packages/adapter-surrealdb/package.json +++ b/packages/adapter-surrealdb/package.json @@ -1,14 +1,15 @@ { "name": "@auth/surrealdb-adapter", - "version": "1.5.2", + "version": "2.0.0", "description": "SurrealDB adapter for next-auth.", "homepage": "https://authjs.dev", "repository": "https://github.com/nextauthjs/next-auth", "bugs": { "url": "https://github.com/nextauthjs/next-auth/issues" }, - "author": "Martin Schaer ", + "author": "Dylan Vanmali", "contributors": [ + "Martin Schaer ", "Thang Huu Vu " ], "type": "module", @@ -29,7 +30,7 @@ "next-auth", "next.js", "oauth", - "mongodb", + "surrealdb", "adapter" ], "private": false, @@ -44,6 +45,6 @@ "@auth/core": "workspace:*" }, "peerDependencies": { - "surrealdb.js": "^0.11.0" + "surrealdb": "^1.0.0" } } diff --git a/packages/adapter-surrealdb/src/index.ts b/packages/adapter-surrealdb/src/index.ts index f0ce26b047..f5e5c783b7 100644 --- a/packages/adapter-surrealdb/src/index.ts +++ b/packages/adapter-surrealdb/src/index.ts @@ -14,138 +14,175 @@ * * @module @auth/surrealdb-adapter */ -import Surreal, { ExperimentalSurrealHTTP } from "surrealdb.js" +import { Surreal, RecordId } from "surrealdb" import type { Adapter, AdapterUser, AdapterAccount, + AdapterAccountType, AdapterSession, VerificationToken, } from "@auth/core/adapters" -import type { ProviderType } from "@auth/core/providers" -type Document = Record & { id: string } -export type UserDoc = Document & { email: string } -export type AccountDoc = { - id: string +type Document> = { + id: T + [key: string]: unknown +} +export type UserDoc = Document & { + email: string + emailVerified?: string | Date +} +export type AccountDoc> = { + id: RecordId<"account"> userId: T refresh_token?: string access_token?: string - type: Extract + type: AdapterAccountType provider: string providerAccountId: string expires_at?: number } -export type SessionDoc = Document & { userId: T } - -const extractId = (surrealId: string) => - toId(surrealId.split(":")[1]) ?? surrealId +export type SessionDoc> = Document & { + userId: T + expires: string | Date + sessionToken: string +} +export type VerificationTokenDoc = { + id: RecordId<"verification_token"> + identifier: string + expires: Date + token: string +} /** @internal */ // Convert DB object to AdapterUser export const docToUser = (doc: UserDoc): AdapterUser => ({ ...doc, - id: extractId(doc.id), - emailVerified: doc.emailVerified ? new Date(doc.emailVerified) : null, + id: doc.id.id.toString(), + emailVerified: + typeof doc?.emailVerified === "string" + ? new Date(Date.parse(doc.emailVerified)) + : doc?.emailVerified, }) /** @internal */ // Convert DB object to AdapterAccount -export const docToAccount = (doc: AccountDoc) => { - const account: AdapterAccount = { - ...doc, - id: extractId(doc.id), - userId: doc.userId ? extractId(doc.userId) : "", - } - return account -} +export const docToAccount = (doc: AccountDoc): AdapterAccount => ({ + ...doc, + id: doc.id.id.toString(), + userId: doc?.userId ? doc.userId.id.toString() : "", +}) /** @internal */ // Convert DB object to AdapterSession export const docToSession = ( - doc: SessionDoc + doc: SessionDoc | UserDoc> ): AdapterSession => ({ - userId: extractId( - typeof doc.userId === "string" ? doc.userId : doc.userId.id - ), - expires: new Date(doc.expires ?? ""), - sessionToken: doc.sessionToken ?? "", + userId: + doc.userId instanceof RecordId + ? doc.userId.id.toString() + : doc.userId.id.id.toString(), + expires: + typeof doc?.expires === "string" + ? new Date(Date.parse(doc.expires)) + : (doc?.expires ?? null), + sessionToken: doc.sessionToken, +}) + +/** @internal */ +// Convert DB object to Verification Token +export const docToVerificationToken = ( + doc: Omit +): VerificationToken => ({ + ...doc, + expires: + typeof doc?.expires === "string" + ? new Date(Date.parse(doc.expires)) + : (doc?.expires ?? null), }) /** @internal */ // Convert AdapterUser to DB object -const userToDoc = ( - user: Omit | Partial -): Omit => { - const doc = { - ...user, - emailVerified: user.emailVerified?.toISOString(), - } - return doc -} +const userToDoc = (user: Partial): Partial => ({ + ...user, + id: user?.id ? new RecordId("user", user.id) : undefined, + emailVerified: user.emailVerified ?? undefined, +}) /** @internal */ // Convert AdapterAccount to DB object -const accountToDoc = (account: AdapterAccount): Omit => { - const doc = { - ...account, - userId: `user:${toSurrealId(account.userId)}`, - } - return doc -} +const accountToDoc = (account: AdapterAccount): Omit => ({ + ...account, + userId: new RecordId("user", account.userId.replace("user:", "")), +}) /** @internal */ // Convert AdapterSession to DB object export const sessionToDoc = ( session: AdapterSession -): Omit => { - const doc = { - ...session, - expires: session.expires.toISOString(), - } - return doc -} +): Partial>> => ({ + ...session, + userId: new RecordId("user", session.userId.replace("user:", "")), + expires: session.expires, +}) -export const toSurrealId = (id: string) => { - if (/^⟨.+⟩$/.test(id)) { - return id - } else { - return `⟨${id}⟩` - } -} +/** @internal */ +// Convert AdapterAccount to DB object +const verificationTokenToDoc = ( + account: VerificationToken +): Omit => ({ + ...account, +}) -export const toId = (surrealId: string) => { - return surrealId.replace(/^⟨(.+)⟩$/, "$1") +/** @internal */ +/** + * Removes all undefined fields in an object + * @param obj + * @returns + */ +function removeUndefinedFields(obj: T): Partial { + if (typeof obj !== "object" || obj === null) { + return obj + } + for (const key in obj) { + if (obj[key] === undefined) { + delete obj[key] + } else if (typeof obj[key] === "object" && obj[key] !== null) { + removeUndefinedFields(obj[key]) + } + } + return obj } -export function SurrealDBAdapter( - client: Promise> +export function SurrealDBAdapter( + client: Promise // options = {} ): Adapter { return { - async createUser(user: Omit) { - const surreal = await client - const doc = userToDoc(user) - const userDoc = await surreal.create>( - "user", - doc - ) - if (userDoc.length) { - return docToUser(userDoc[0]) - } + async createUser(user: Partial) { + try { + const surreal = await client + const doc = userToDoc(user) + const userDoc = await surreal.create>( + "user", + doc + ) + if (userDoc.length) { + return docToUser(userDoc[0]) + } + } catch {} throw new Error("User not created") }, async getUser(id: string) { const surreal = await client try { - const surrealId = toSurrealId(id) - const queryResult = await surreal.query<[UserDoc[]]>( + const [userDoc] = await surreal.query<[UserDoc[]]>( "SELECT * FROM $user", { - user: `user:${surrealId}`, + user: new RecordId("user", id), } ) - const doc = queryResult[0]?.[0] + const doc = userDoc.at(0) if (doc) { return docToUser(doc) } @@ -155,11 +192,11 @@ export function SurrealDBAdapter( async getUserByEmail(email: string) { const surreal = await client try { - const users = await surreal.query<[UserDoc[]]>( + const [users] = await surreal.query<[UserDoc[]]>( `SELECT * FROM user WHERE email = $email`, { email } ) - const doc = users[0]?.[0] + const doc = users.at(0) if (doc) return docToUser(doc) } catch {} return null @@ -170,83 +207,84 @@ export function SurrealDBAdapter( }: Pick) { const surreal = await client try { - const users = await surreal.query<[AccountDoc[]]>( - `SELECT userId - FROM account - WHERE providerAccountId = $providerAccountId - AND provider = $provider - FETCH userId`, - { providerAccountId, provider } + const [accounts] = await surreal.query<[AccountDoc[]]>( + `SELECT userId FROM account WHERE providerAccountId = $providerAccountId AND provider = $provider FETCH userId`, + { + providerAccountId, + provider, + } ) - - const user = users[0]?.[0]?.userId + const user = accounts.at(0)?.userId if (user) return docToUser(user) } catch {} return null }, async updateUser(user: Partial) { - if (!user.id) throw new Error("User id is required") - const surreal = await client - const doc = { - ...user, - emailVerified: user.emailVerified?.toISOString(), - id: undefined, - } - const updatedUser = await surreal.merge>( - `user:${toSurrealId(user.id)}`, - doc - ) - if (updatedUser.length) { - return docToUser(updatedUser[0]) - } else { - throw new Error("User not updated") - } + try { + if (!user.id) throw new Error("User id is required") + const surreal = await client + const doc: Partial | null = removeUndefinedFields( + userToDoc({ + ...user, + id: undefined, + }) + ) + if (doc) { + const updatedUser = await surreal.merge>( + new RecordId("user", user.id), + doc + ) + if (updatedUser) { + return docToUser(updatedUser) + } + } + } catch {} + throw new Error("User not updated") }, async deleteUser(userId: string) { const surreal = await client - const surrealId = toSurrealId(userId) // delete account try { - const accounts = await surreal.query<[AccountDoc[]]>( - `SELECT * - FROM account - WHERE userId = $userId - LIMIT 1`, - { userId: `user:${surrealId}` } + const [accounts] = await surreal.query<[AccountDoc[]]>( + `SELECT * FROM account WHERE userId = $userId LIMIT 1`, + { userId: new RecordId("user", userId) } ) - const account = accounts[0]?.[0] + const account = accounts.at(0) if (account) { - const accountId = extractId(account.id) - await surreal.delete(`account:${accountId}`) + await surreal.delete(account.id) } } catch {} // delete session try { - const sessions = await surreal.query<[SessionDoc[]]>( - `SELECT * - FROM session - WHERE userId = $userId - LIMIT 1`, - { userId: `user:${surrealId}` } + const [sessions] = await surreal.query<[SessionDoc[]]>( + `SELECT * FROM session WHERE userId = $userId LIMIT 1`, + { userId: new RecordId("user", userId) } ) - const session = sessions[0]?.[0] + const session = sessions.at(0) if (session) { - const sessionId = extractId(session.id) - await surreal.delete(`session:${sessionId}`) + await surreal.delete(session.id) } } catch {} // delete user - await surreal.delete(`user:${surrealId}`) + await surreal.delete(new RecordId("user", userId)) // TODO: put all 3 deletes inside a Promise all }, async linkAccount(account: AdapterAccount) { - const surreal = await client - const doc = await surreal.create("account", accountToDoc(account)) - return docToAccount(doc[0]) + try { + const surreal = await client + const accountDoc = await surreal.create< + AccountDoc, + Omit + >("account", accountToDoc(account)) + if (accountDoc.length) { + return docToAccount(accountDoc[0]) + } + } catch {} + throw new Error("Account not created") }, async unlinkAccount({ providerAccountId, @@ -254,26 +292,29 @@ export function SurrealDBAdapter( }: Pick) { const surreal = await client try { - const accounts = await surreal.query<[AccountDoc[]]>( - `SELECT * - FROM account - WHERE providerAccountId = $providerAccountId - AND provider = $provider - LIMIT 1`, + const [accounts] = await surreal.query<[AccountDoc[]]>( + `SELECT * FROM account WHERE providerAccountId = $providerAccountId AND provider = $provider LIMIT 1`, { providerAccountId, provider } ) - const account = accounts[0]?.[0] + const account = accounts.at(0) if (account) { - const accountId = extractId(account.id) - await surreal.delete(`account:${accountId}`) + await surreal.delete(account.id) } } catch {} }, - async createSession({ sessionToken, userId, expires }) { + async createSession({ + sessionToken, + userId, + expires, + }: { + sessionToken: string + userId: string + expires: Date + }) { const surreal = await client const doc = sessionToDoc({ sessionToken, - userId: `user:${toSurrealId(userId)}`, + userId, expires, }) const result = await surreal.create>( @@ -285,16 +326,13 @@ export function SurrealDBAdapter( async getSessionAndUser(sessionToken: string) { const surreal = await client try { - // Can't use limit 1 because it prevent userId to be fetched. + // Can't use limit 1 because it prevents userId from being fetched. // Works setting limit to 2 - const sessions = await surreal.query<[SessionDoc[]]>( - `SELECT * - FROM session - WHERE sessionToken = $sessionToken - FETCH userId`, + const [sessions] = await surreal.query<[SessionDoc[]]>( + `SELECT * FROM session WHERE sessionToken = $sessionToken FETCH userId`, { sessionToken } ) - const session = sessions[0]?.[0] + const session = sessions.at(0) if (session) { const userDoc = session.userId if (!userDoc) return null @@ -314,32 +352,32 @@ export function SurrealDBAdapter( ) { const surreal = await client try { - const sessions = await surreal.query<[SessionDoc[]]>( - `SELECT * - FROM session - WHERE sessionToken = $sessionToken - LIMIT 1`, - { sessionToken: session.sessionToken } + const [sessions] = await surreal.query<[SessionDoc[]]>( + `SELECT * FROM session WHERE sessionToken = $sessionToken LIMIT 1`, + { + sessionToken: session.sessionToken, + } ) - const sessionDoc = sessions[0]?.[0] + const sessionDoc = sessions.at(0) if (sessionDoc && session.expires) { - const sessionId = extractId(sessionDoc.id) - const updatedSession = await surreal.merge< - SessionDoc, - Omit - >( - `session:${sessionId}`, - sessionToDoc({ - ...sessionDoc, - ...session, - userId: sessionDoc.userId, - expires: session.expires, - }) - ) - if (updatedSession.length) { - return docToSession(updatedSession[0]) - } else { - return null + const sessionId = sessionDoc.id + const doc: Partial>> | null = + removeUndefinedFields( + sessionToDoc({ + ...sessionDoc, + ...session, + userId: sessionDoc.userId.toString(), + expires: session.expires, + }) + ) + if (doc) { + const updatedSession = await surreal.merge< + SessionDoc, + Partial>> + >(sessionId, doc) + if (updatedSession?.id) { + return docToSession(updatedSession) + } } } } catch {} @@ -348,34 +386,38 @@ export function SurrealDBAdapter( async deleteSession(sessionToken: string) { const surreal = await client try { - const sessions = await surreal.query<[SessionDoc[]]>( - `SELECT * - FROM session - WHERE sessionToken = $sessionToken - LIMIT 1`, - { sessionToken } + const [sessions] = await surreal.query<[SessionDoc[]]>( + `SELECT * FROM session WHERE sessionToken = $sessionToken LIMIT 1`, + { + sessionToken, + } ) - const session = sessions[0]?.[0] + const session = sessions.at(0) if (session) { - const sessionId = extractId(session.id) - await surreal.delete(`session:${sessionId}`) + await surreal.delete(session.id) return } } catch {} }, - async createVerificationToken({ - identifier, - expires, - token, - }: VerificationToken) { - const surreal = await client - const doc = { - identifier, - expires, - token, - } - const result = await surreal.create("verification_token", doc) - return result[0] ?? null + async createVerificationToken(verificationToken: VerificationToken) { + try { + const surreal = await client + const doc = verificationTokenToDoc(verificationToken) + + const verificationTokenDocs = await surreal.create< + VerificationTokenDoc, + Omit + >("verification_token", doc) + if (verificationTokenDocs.length) { + const verificationTokenDoc: Partial = + verificationTokenDocs[0] + if (verificationTokenDoc.id) delete verificationTokenDoc.id + return docToVerificationToken( + verificationTokenDoc as Omit + ) + } + } catch {} + throw new Error("Verification Token not created") }, async useVerificationToken({ identifier, @@ -386,31 +428,25 @@ export function SurrealDBAdapter( }) { const surreal = await client try { - const tokens = await surreal.query< - [{ identifier: string; expires: string; token: string; id: string }[]] - >( - `SELECT * - FROM verification_token - WHERE identifier = $identifier - AND token = $verificationToken - LIMIT 1`, - { identifier, verificationToken: token } + const [tokens] = await surreal.query<[VerificationTokenDoc[]]>( + `SELECT * FROM verification_token WHERE identifier = $identifier AND token = $vt LIMIT 1`, + { identifier, vt: token } ) - if (tokens.length && tokens[0]) { - const vt = tokens[0][0] + if (tokens.length && tokens.at(0)) { + const vt = tokens[0] if (vt) { await surreal.delete(vt.id) - return { - identifier: vt.identifier, - expires: new Date(vt.expires), - token: vt.token, - } + const verificationTokenDoc: Partial = vt + if (verificationTokenDoc.id) delete verificationTokenDoc.id + return docToVerificationToken( + verificationTokenDoc as Omit + ) } } else { return null } } catch {} - return null + throw new Error("Verification Token not used") }, } } diff --git a/packages/adapter-surrealdb/test/common.ts b/packages/adapter-surrealdb/test/common.ts index 67582825a2..1b2f30c312 100644 --- a/packages/adapter-surrealdb/test/common.ts +++ b/packages/adapter-surrealdb/test/common.ts @@ -1,18 +1,21 @@ -import Surreal, { ExperimentalSurrealHTTP } from "surrealdb.js" +import { RecordId, Surreal } from "surrealdb" import { SurrealDBAdapter, docToUser, docToAccount, docToSession, - toSurrealId, + docToVerificationToken, +} from "../src/index" +import type { + UserDoc, + AccountDoc, + SessionDoc, + VerificationTokenDoc, } from "../src/index" -import type { UserDoc, AccountDoc, SessionDoc } from "../src/index" import { randomUUID } from "utils/adapter" -export const config = ( - clientPromise: Promise> -) => ({ +export const config = (clientPromise: Promise) => ({ adapter: SurrealDBAdapter(clientPromise), db: { id() { @@ -42,58 +45,49 @@ export const config = ( if (surreal.close) surreal.close() }, async user(id: string) { - const surrealId = toSurrealId(id) + const userId = new RecordId("user", id) const surreal = await clientPromise - try { - const users = await surreal.query<[UserDoc[]]>("SELECT * FROM $user", { - user: `user:${surrealId}`, - }) - const user = users[0][0] - if (user !== undefined) return docToUser(user) - } catch (e) {} + const [users] = await surreal.query<[UserDoc[]]>(`SELECT * FROM $user`, { + user: userId, + }) + const user = users.at(0) + console.log(user) + if (user) return docToUser(user) return null }, async account({ provider, providerAccountId }) { const surreal = await clientPromise - const accounts = await surreal.query<[AccountDoc[]]>( + const [accounts] = await surreal.query<[AccountDoc[]]>( `SELECT * FROM account WHERE provider = $provider AND providerAccountId = $providerAccountId`, { provider, providerAccountId } ) - const account = accounts[0] - if (account?.[0] !== undefined) return docToAccount(account[0]) + const account = accounts.at(0) + if (account) return docToAccount(account) return null }, async session(sessionToken: string) { const surreal = await clientPromise - const sessions = await surreal.query<[SessionDoc[]]>( + const [sessions] = await surreal.query<[SessionDoc[]]>( `SELECT * FROM session WHERE sessionToken = $sessionToken`, { sessionToken } ) - const session = sessions[0]?.[0] - if (session !== undefined) { - return docToSession(session) - } + const session = sessions.at(0) + if (session) return docToSession(session) return null }, async verificationToken({ identifier, token }) { const surreal = await clientPromise - const tokens = await surreal.query< - [{ identifier: string; expires: string; token: string; id: string }[]] - >( - `SELECT * - FROM verification_token - WHERE identifier = $identifier - AND token = $verificationToken - LIMIT 1`, - { identifier, verificationToken: token } + const [tokens] = await surreal.query<[VerificationTokenDoc[]]>( + `SELECT * FROM verification_token WHERE identifier = $identifier AND token = $vt LIMIT 1`, + { identifier, vt: token } ) - const verificationToken = tokens[0]?.[0] + const verificationToken: Partial | undefined = + tokens.at(0) if (verificationToken) { - return { - identifier: verificationToken.identifier, - expires: new Date(verificationToken.expires), - token: verificationToken.token, - } + if (verificationToken.id) delete verificationToken.id + return docToVerificationToken( + verificationToken as Omit + ) } return null }, diff --git a/packages/adapter-surrealdb/test/index.test.ts b/packages/adapter-surrealdb/test/index.test.ts index dd265e6dab..a3e754342d 100644 --- a/packages/adapter-surrealdb/test/index.test.ts +++ b/packages/adapter-surrealdb/test/index.test.ts @@ -1,4 +1,4 @@ -import Surreal, { ExperimentalSurrealHTTP } from "surrealdb.js" +import { Surreal } from "surrealdb" import { runBasicTests } from "utils/adapter" import { config } from "./common" @@ -6,13 +6,13 @@ import { config } from "./common" const clientPromise = new Promise(async (resolve, reject) => { const db = new Surreal() try { - await db.connect("http://0.0.0.0:8000/rpc", { + const db = new Surreal() + await db.connect("ws://0.0.0.0:8000") + await db.signin({ namespace: "test", database: "test", - auth: { - username: "test", - password: "test", - }, + username: "test", + password: "test", }) resolve(db) } catch (e) { @@ -21,25 +21,3 @@ const clientPromise = new Promise(async (resolve, reject) => { }) runBasicTests(config(clientPromise)) - -// const clientPromiseRest = new Promise>( -// async (resolve, reject) => { -// try { -// const db = new ExperimentalSurrealHTTP("http://0.0.0.0:8000", { -// fetch, -// auth: { -// user: "test", -// pass: "test", -// }, -// ns: "test", -// db: "test", -// }) -// resolve(db) -// } catch (e) { -// reject(e) -// } -// } -// ) - -// TODO: Revisit and fix this test - currently updateUser and deleteUser are failing. -// runBasicTests(config(clientPromiseRest)) From b0a7bf9c48858e74c9d6db83d208198cffc3ebf4 Mon Sep 17 00:00:00 2001 From: Dylan Vanmali Date: Wed, 25 Sep 2024 17:28:22 -0700 Subject: [PATCH 02/14] fix: ensure correct adapter type is returned with verified is nulled if not provided --- packages/adapter-surrealdb/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-surrealdb/src/index.ts b/packages/adapter-surrealdb/src/index.ts index f5e5c783b7..f3a88fb21c 100644 --- a/packages/adapter-surrealdb/src/index.ts +++ b/packages/adapter-surrealdb/src/index.ts @@ -62,7 +62,7 @@ export const docToUser = (doc: UserDoc): AdapterUser => ({ emailVerified: typeof doc?.emailVerified === "string" ? new Date(Date.parse(doc.emailVerified)) - : doc?.emailVerified, + : (doc?.emailVerified ?? null), }) /** @internal */ From 9756ad1075983154a29cca76deefe3b40abef3c8 Mon Sep 17 00:00:00 2001 From: Dylan Vanmali Date: Wed, 2 Oct 2024 12:33:20 -0700 Subject: [PATCH 03/14] fix: remove log print on test --- packages/adapter-surrealdb/test/common.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/adapter-surrealdb/test/common.ts b/packages/adapter-surrealdb/test/common.ts index 1b2f30c312..5979e00ce8 100644 --- a/packages/adapter-surrealdb/test/common.ts +++ b/packages/adapter-surrealdb/test/common.ts @@ -40,7 +40,7 @@ export const config = (clientPromise: Promise) => ({ surreal.delete("user"), ]) } catch (e) { - console.log(e) + console.error(e) } if (surreal.close) surreal.close() }, @@ -51,7 +51,6 @@ export const config = (clientPromise: Promise) => ({ user: userId, }) const user = users.at(0) - console.log(user) if (user) return docToUser(user) return null }, From 14efdfe7c368e590eeb117973ee44475bffe6347 Mon Sep 17 00:00:00 2001 From: Dylan Vanmali Date: Wed, 2 Oct 2024 12:44:24 -0700 Subject: [PATCH 04/14] feat: more AnyAuth login variables, support HTTP rpc connection, add exponential backoff for disconnection events to example --- .../getting-started/adapters/surrealdb.mdx | 177 ++++++++++++++++-- packages/adapter-surrealdb/src/index.ts | 2 +- packages/adapter-surrealdb/test/index.test.ts | 13 +- 3 files changed, 171 insertions(+), 21 deletions(-) diff --git a/docs/pages/getting-started/adapters/surrealdb.mdx b/docs/pages/getting-started/adapters/surrealdb.mdx index 09914d0186..a5d2751815 100644 --- a/docs/pages/getting-started/adapters/surrealdb.mdx +++ b/docs/pages/getting-started/adapters/surrealdb.mdx @@ -97,37 +97,186 @@ app.use( The SurrealDB adapter does not handle connections automatically, so you will have to make sure that you pass the Adapter a `SurrealDBClient` that is connected already. Below you can see an example how to do this. -### Authorization +### Utils File + +```ts filename="./lib/surrealdb_utils.ts" +import { Surreal, ConnectionStatus, type AnyAuth } from "surrealdb" + +/** + * Maintains a single instance of surrealdb + */ +export class MySurreal { + // A single instantiation of a surreal connection + private _surrealdb: Surreal | undefined + + async surrealdb(url: string | URL, auth: AnyAuth | string): Promise { + // Init Surreal + if (!this._surrealdb) { + this._surrealdb = new Surreal() + } + + if (this._surrealdb.status == ConnectionStatus.Connected) { + return this._surrealdb + } else if (this._surrealdb.status == ConnectionStatus.Disconnected) { + try { + // Connect as a database user + await this._surrealdb.connect(url, { + auth, + }) + if (process.env.NODE_ENV === "development") { + let str = `${this._surrealdb.status}` + if (typeof auth !== "string") { + if ("username" in auth) str += ` as ${auth.username}` + if ("database" in auth && "namespace" in auth) + str += ` for ${auth.namespace} ${auth.database}` + else if ("namespace" in auth) str += ` for ${auth.namespace}` + else if ("database" in auth) str += ` for ${auth.database}` + } + console.info(str) + } + } catch (error) { + if (error instanceof Error) throw error + throw new Error(error as unknown as string) + } + } + return this._surrealdb + } +} + +export const surrealdb = new MySurreal() + +/** + * Converts environment variables to an AnyAuth type + * to connect with the database + * @param param0 - environment variables + * @returns {RootAuth | NamespaceAuth | DatabaseAuth | ScopeAuth} + */ +export function toAnyAuth({ + username, + password, + namespace, + database, + scope, +}: Record) { + let auth: AnyAuth + if (username && password && namespace && database) { + auth = { + database, + namespace, + username, + password, + } + } else if (username && password && namespace) { + auth = { + namespace, + username, + password, + } + } else if (username && password) { + auth = { + username, + password, + } + } else if (scope) { + auth = { + namespace, + database, + username, + password, + scope, + } + } else { + throw new Error("unsupported any auth configuration") + } + return auth +} -#### Websocket Method: +/** + * Handles a disconnection with the database with exponential backoff + * @param surreal - the single instance of surreal + * @param url - the database url + * @param auth - auth credentials + * @param {number} [reconnectDelay = 1000] - initial delay amount + * @param {number} [max_retries = 5] - maximum number of retries + * @param {number} [retry = 0] - current retry + */ +export async function handleDisconnect( + surreal: MySurreal, + url: string | URL, + auth: AnyAuth, + reconnectDelay: number = 1000, + max_retries: number = 5, + retry: number = 0 +) { + if (retry >= max_retries) { + console.error("Shutting down.") + process.exit(1) // Graceful exit or handle gracefully in a production environment. + } -With this configuration, we can use the websocket endpoint. Thus, the `AUTH_SURREAL_URL` should begin with `ws` or `wss`. + retry++ + console.log(`Reconnect in ${reconnectDelay}ms...`) + setTimeout(async () => { + try { + await surreal.surrealdb(url, auth) + console.log("reconnected") + } catch (err) { + console.error("Reconnection attempt failed") + handleDisconnect( + surreal, + url, + auth, + retry, + max_retries, + reconnectDelay * 2 + ) // Recursively try to reconnect. + } + }, reconnectDelay) +} +``` + +### Authorization + +The clientPromise provides a connection to the database. ```ts filename="./lib/surrealdb.ts" -import { Surreal } from "surrealdb" +import { type Surreal } from "surrealdb" +import { surrealdb, toAnyAuth } from "../lib/surrealdb" +import { handleDisconnect, MySurreal, toAnyAuth } from "./lib/surrealdb_utils" const clientPromise = new Promise(async (resolve, reject) => { try { - const db = new Surreal() const { AUTH_SURREAL_URL: url, AUTH_SURREAL_NS: namespace, AUTH_SURREAL_DB: database, AUTH_SURREAL_USER: username, AUTH_SURREAL_PW: password, + AUTH_SURREAL_SCOPE: scope, } = process.env - if (url && namespace && database && username && password) { - await db.connect(url) - await db.signin({ - namespace, - database, - username, - password, - }) - } + if (!url) throw "required auth url" + const auth = toAnyAuth({ + namespace, + database, + username, + password, + scope, + }) + const surreal = new MySurreal() + const db = await surreal.surrealdb(url, auth) + db.emitter.subscribe("disconnected", async () => { + handleDisconnect(surreal, url, auth) + }) resolve(db) } catch (e) { reject(e) } }) ``` + +#### HTTP ENGINE + +With this configuration, we can use the database's http endpoint. Thus, the `AUTH_SURREAL_URL` should begin with `http` or `https`. + +#### Websocket ENGINE + +With this configuration, we can use the database's websocket endpoint. Thus, the `AUTH_SURREAL_URL` should begin with `ws` or `wss`. diff --git a/packages/adapter-surrealdb/src/index.ts b/packages/adapter-surrealdb/src/index.ts index f3a88fb21c..97ed051f1c 100644 --- a/packages/adapter-surrealdb/src/index.ts +++ b/packages/adapter-surrealdb/src/index.ts @@ -127,7 +127,7 @@ export const sessionToDoc = ( }) /** @internal */ -// Convert AdapterAccount to DB object +// Convert VerificationToken to DB object const verificationTokenToDoc = ( account: VerificationToken ): Omit => ({ diff --git a/packages/adapter-surrealdb/test/index.test.ts b/packages/adapter-surrealdb/test/index.test.ts index a3e754342d..c3e213b216 100644 --- a/packages/adapter-surrealdb/test/index.test.ts +++ b/packages/adapter-surrealdb/test/index.test.ts @@ -7,12 +7,13 @@ const clientPromise = new Promise(async (resolve, reject) => { const db = new Surreal() try { const db = new Surreal() - await db.connect("ws://0.0.0.0:8000") - await db.signin({ - namespace: "test", - database: "test", - username: "test", - password: "test", + await db.connect("ws://0.0.0.0:8000", { + auth: { + namespace: "test", + database: "test", + username: "test", + password: "test", + }, }) resolve(db) } catch (e) { From d3aabc91c244b9d2e28188699eae1f52690ea0ac Mon Sep 17 00:00:00 2001 From: Dylan Vanmali Date: Wed, 2 Oct 2024 13:45:13 -0700 Subject: [PATCH 05/14] restore: changes --- docs/pages/guides/extending-the-session.mdx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/pages/guides/extending-the-session.mdx b/docs/pages/guides/extending-the-session.mdx index 2b27518385..01f3a14363 100644 --- a/docs/pages/guides/extending-the-session.mdx +++ b/docs/pages/guides/extending-the-session.mdx @@ -12,12 +12,17 @@ This is `name`, `email`, and `image`. A common use case is to add the user's id to the session. Below it is shown how to do this based on the session strategy you are using. + + By default, the `id` property does not exist on `token` or `session`. See the + [TypeScript](https://authjs.dev/getting-started/typescript) on how to add it. + + ## With JWT To have access to the user id, add the following to your Auth.js configuration: ```ts filename="auth.ts" - providers, + // By default, the `id` property does not exist on `token` or `session`. See the [TypeScript](https://authjs.dev/getting-started/typescript) on how to add it. callbacks: { jwt({ token, user }) { if (user) { // User is available during sign-in @@ -44,7 +49,7 @@ Calls to `auth()` or `useSession()` will now have access to the user's id. If you are using a database session strategy, you can add the user's id to the session by modifying the `session` callback: ```ts filename="auth.ts" - providers, + // By default, the `id` property does not exist on `session`. See the [TypeScript](https://authjs.dev/getting-started/typescript) on how to add it. callbacks: { session({ session, user }) { session.user.id = user.id From 34ebb2cf61b85a15db024c82aaa8fd5777bc5772 Mon Sep 17 00:00:00 2001 From: Dylan Vanmali Date: Wed, 2 Oct 2024 13:46:44 -0700 Subject: [PATCH 06/14] restore: changes --- docs/pages/guides/extending-the-session.mdx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/pages/guides/extending-the-session.mdx b/docs/pages/guides/extending-the-session.mdx index 01f3a14363..734cc6b3bc 100644 --- a/docs/pages/guides/extending-the-session.mdx +++ b/docs/pages/guides/extending-the-session.mdx @@ -12,11 +12,6 @@ This is `name`, `email`, and `image`. A common use case is to add the user's id to the session. Below it is shown how to do this based on the session strategy you are using. - - By default, the `id` property does not exist on `token` or `session`. See the - [TypeScript](https://authjs.dev/getting-started/typescript) on how to add it. - - ## With JWT To have access to the user id, add the following to your Auth.js configuration: From bd1116b1cf0e28ae3ddc174920ea7b6405ab69ab Mon Sep 17 00:00:00 2001 From: Dylan Vanmali Date: Wed, 2 Oct 2024 13:57:58 -0700 Subject: [PATCH 07/14] doc: add all env variables available --- .../getting-started/adapters/surrealdb.mdx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/pages/getting-started/adapters/surrealdb.mdx b/docs/pages/getting-started/adapters/surrealdb.mdx index a5d2751815..af8a9656c2 100644 --- a/docs/pages/getting-started/adapters/surrealdb.mdx +++ b/docs/pages/getting-started/adapters/surrealdb.mdx @@ -18,12 +18,20 @@ npm install @auth/surrealdb-adapter surrealdb ### Environment Variables +A valid authentication combination must be provided. The following authentication combinations are supported: + +- RootAuth +- NamespaceAuth +- DatabaseAuth +- ScopeAuth + ```sh -AUTH_SURREAL_URL, -AUTH_SURREAL_NS, -AUTH_SURREAL_DB, -AUTH_SURREAL_USER, -AUTH_SURREAL_PW, +AUTH_SURREAL_URL (required) +AUTH_SURREAL_NS +AUTH_SURREAL_DB +AUTH_SURREAL_USER +AUTH_SURREAL_PW +AUTH_SURREAL_SCOPE ``` ### Configuration From bc6b85cadb212c361deeb6f06d454ce14e0eda0f Mon Sep 17 00:00:00 2001 From: Dylan Vanmali Date: Wed, 2 Oct 2024 14:42:32 -0700 Subject: [PATCH 08/14] fix: throw error not string --- docs/pages/getting-started/adapters/surrealdb.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/getting-started/adapters/surrealdb.mdx b/docs/pages/getting-started/adapters/surrealdb.mdx index af8a9656c2..8d285b8a3b 100644 --- a/docs/pages/getting-started/adapters/surrealdb.mdx +++ b/docs/pages/getting-started/adapters/surrealdb.mdx @@ -261,7 +261,7 @@ const clientPromise = new Promise(async (resolve, reject) => { AUTH_SURREAL_PW: password, AUTH_SURREAL_SCOPE: scope, } = process.env - if (!url) throw "required auth url" + if (!url) throw new Error('required auth url') const auth = toAnyAuth({ namespace, database, From 89f94671a475cb1fb28faa071c552db00193464b Mon Sep 17 00:00:00 2001 From: Dylan Vanmali Date: Mon, 12 May 2025 17:00:53 -0700 Subject: [PATCH 09/14] fix: uses surrealdb:^v1.3.0 for native reconnectivity, feat: add webauthn funcs to adapter --- .../getting-started/adapters/surrealdb.mdx | 138 ++++++++---------- packages/adapter-surrealdb/package.json | 2 +- packages/adapter-surrealdb/src/index.ts | 125 +++++++++++++++- packages/adapter-surrealdb/test/common.ts | 36 ++++- packages/adapter-surrealdb/test/index.test.ts | 5 +- packages/adapter-surrealdb/test/test.sh | 9 +- 6 files changed, 226 insertions(+), 89 deletions(-) diff --git a/docs/pages/getting-started/adapters/surrealdb.mdx b/docs/pages/getting-started/adapters/surrealdb.mdx index 8d285b8a3b..c2b079d169 100644 --- a/docs/pages/getting-started/adapters/surrealdb.mdx +++ b/docs/pages/getting-started/adapters/surrealdb.mdx @@ -32,6 +32,8 @@ AUTH_SURREAL_DB AUTH_SURREAL_USER AUTH_SURREAL_PW AUTH_SURREAL_SCOPE +SURREAL_NS (required when using RootAuth or NamespaceAuth) +SURREAL_DB (required when using RootAuth or NamespaceAuth) ``` ### Configuration @@ -108,16 +110,31 @@ The SurrealDB adapter does not handle connections automatically, so you will hav ### Utils File ```ts filename="./lib/surrealdb_utils.ts" -import { Surreal, ConnectionStatus, type AnyAuth } from "surrealdb" +import type { ConnectOptions, AnyAuth } from "surrealdb" +import { Surreal, ConnectionStatus } from "surrealdb" /** - * Maintains a single instance of surrealdb + * Maintains a single instance of surrealdb. + * Automatically reconnects unless options.reconnect is set to false manually. */ export class MySurreal { // A single instantiation of a surreal connection private _surrealdb: Surreal | undefined + private _url: string | URL + private _opts: ConnectOptions | undefined + + constructor( + url: Parameters["connect"]>[0], + opts?: ConnectOptions + ) { + this._url = url + if (opts && opts.reconnect === undefined) { + opts.reconnect = true + } + this._opts = opts + } - async surrealdb(url: string | URL, auth: AnyAuth | string): Promise { + async surrealdb(): Promise { // Init Surreal if (!this._surrealdb) { this._surrealdb = new Surreal() @@ -128,18 +145,12 @@ export class MySurreal { } else if (this._surrealdb.status == ConnectionStatus.Disconnected) { try { // Connect as a database user - await this._surrealdb.connect(url, { - auth, - }) + await this._surrealdb.connect(this._url, this._opts) if (process.env.NODE_ENV === "development") { - let str = `${this._surrealdb.status}` - if (typeof auth !== "string") { - if ("username" in auth) str += ` as ${auth.username}` - if ("database" in auth && "namespace" in auth) - str += ` for ${auth.namespace} ${auth.database}` - else if ("namespace" in auth) str += ` for ${auth.namespace}` - else if ("database" in auth) str += ` for ${auth.database}` - } + const str = this.toConnectionString( + this._surrealdb.status, + this._opts + ) console.info(str) } } catch (error) { @@ -149,9 +160,26 @@ export class MySurreal { } return this._surrealdb } -} -export const surrealdb = new MySurreal() + private toConnectionString(status: ConnectionStatus, opts?: ConnectOptions) { + let str = `${status}` + const auth = opts?.auth + + if (auth && typeof auth !== "string") { + if ("username" in auth) { + str += ` as ${auth.username}` + } + if ("database" in auth && "namespace" in auth) { + str += ` for ${auth.namespace} ${auth.database}` + } else if ("namespace" in auth) { + str += ` for ${auth.namespace}` + } else if ("database" in auth) { + str += ` for ${auth.database}` + } + } + return str + } +} /** * Converts environment variables to an AnyAuth type @@ -198,82 +226,42 @@ export function toAnyAuth({ } return auth } - -/** - * Handles a disconnection with the database with exponential backoff - * @param surreal - the single instance of surreal - * @param url - the database url - * @param auth - auth credentials - * @param {number} [reconnectDelay = 1000] - initial delay amount - * @param {number} [max_retries = 5] - maximum number of retries - * @param {number} [retry = 0] - current retry - */ -export async function handleDisconnect( - surreal: MySurreal, - url: string | URL, - auth: AnyAuth, - reconnectDelay: number = 1000, - max_retries: number = 5, - retry: number = 0 -) { - if (retry >= max_retries) { - console.error("Shutting down.") - process.exit(1) // Graceful exit or handle gracefully in a production environment. - } - - retry++ - console.log(`Reconnect in ${reconnectDelay}ms...`) - setTimeout(async () => { - try { - await surreal.surrealdb(url, auth) - console.log("reconnected") - } catch (err) { - console.error("Reconnection attempt failed") - handleDisconnect( - surreal, - url, - auth, - retry, - max_retries, - reconnectDelay * 2 - ) // Recursively try to reconnect. - } - }, reconnectDelay) -} ``` ### Authorization -The clientPromise provides a connection to the database. +The clientPromise provides a connection to the database. You could use any connect option you wish. For quick setup, use the DatabaseAuth method. For best security, we recommend creating a Record Access method if you know how to properly setup access table permissions. ```ts filename="./lib/surrealdb.ts" import { type Surreal } from "surrealdb" -import { surrealdb, toAnyAuth } from "../lib/surrealdb" import { handleDisconnect, MySurreal, toAnyAuth } from "./lib/surrealdb_utils" const clientPromise = new Promise(async (resolve, reject) => { try { const { - AUTH_SURREAL_URL: url, - AUTH_SURREAL_NS: namespace, - AUTH_SURREAL_DB: database, - AUTH_SURREAL_USER: username, - AUTH_SURREAL_PW: password, - AUTH_SURREAL_SCOPE: scope, + AUTH_SURREAL_URL: auth_url, + AUTH_SURREAL_NS: auth_ns, + AUTH_SURREAL_DB: auth_db, + AUTH_SURREAL_USER: auth_user, + AUTH_SURREAL_PW: auth_pw, + AUTH_SURREAL_SCOPE: auth_scope, + SURREAL_NS: namespace, + SURREAL_DB: database, } = process.env - if (!url) throw new Error('required auth url') + if (!auth_url) throw new Error("required auth_url") const auth = toAnyAuth({ + namespace: auth_ns, + database: auth_db, + username: auth_user, + password: auth_pw, + scope: auth_scope, + }) + const surreal = new MySurreal(auth_url, { namespace, database, - username, - password, - scope, - }) - const surreal = new MySurreal() - const db = await surreal.surrealdb(url, auth) - db.emitter.subscribe("disconnected", async () => { - handleDisconnect(surreal, url, auth) + auth, }) + const db = await surreal.surrealdb() resolve(db) } catch (e) { reject(e) diff --git a/packages/adapter-surrealdb/package.json b/packages/adapter-surrealdb/package.json index 3436ebd692..51c993fb7e 100644 --- a/packages/adapter-surrealdb/package.json +++ b/packages/adapter-surrealdb/package.json @@ -46,6 +46,6 @@ "@auth/core": "workspace:*" }, "peerDependencies": { - "surrealdb": "^1.0.0" + "surrealdb": "^1.3.0" } } diff --git a/packages/adapter-surrealdb/src/index.ts b/packages/adapter-surrealdb/src/index.ts index 97ed051f1c..06bdafcaa9 100644 --- a/packages/adapter-surrealdb/src/index.ts +++ b/packages/adapter-surrealdb/src/index.ts @@ -20,6 +20,7 @@ import type { AdapterUser, AdapterAccount, AdapterAccountType, + AdapterAuthenticator, AdapterSession, VerificationToken, } from "@auth/core/adapters" @@ -28,12 +29,11 @@ type Document> = { id: T [key: string]: unknown } -export type UserDoc = Document & { +export type UserDoc = Document> & { email: string emailVerified?: string | Date } -export type AccountDoc> = { - id: RecordId<"account"> +export type AccountDoc> = Document> & { userId: T refresh_token?: string access_token?: string @@ -42,17 +42,23 @@ export type AccountDoc> = { providerAccountId: string expires_at?: number } -export type SessionDoc> = Document & { +export type SessionDoc> = Document> & { userId: T expires: string | Date sessionToken: string } -export type VerificationTokenDoc = { - id: RecordId<"verification_token"> +export type VerificationTokenDoc = Document> & { identifier: string expires: Date token: string } +export type AuthenticatorDoc> = Document< + RecordId<"authenticator"> +> & + AdapterAuthenticator & { + userId: T + counter: number + } /** @internal */ // Convert DB object to AdapterUser @@ -70,7 +76,20 @@ export const docToUser = (doc: UserDoc): AdapterUser => ({ export const docToAccount = (doc: AccountDoc): AdapterAccount => ({ ...doc, id: doc.id.id.toString(), - userId: doc?.userId ? doc.userId.id.toString() : "", + userId: doc.userId instanceof RecordId ? doc.userId.id.toString() : "", +}) + +/** @internal */ +// Convert DB object to AdapterAccount +export const docToAuthenticator = ( + doc: AuthenticatorDoc +): AdapterAuthenticator => ({ + ...doc, + id: doc.id.id.toString(), + userId: + doc.userId instanceof RecordId + ? doc.userId.id.toString() + : doc.userId.id.id.toString(), }) /** @internal */ @@ -116,6 +135,15 @@ const accountToDoc = (account: AdapterAccount): Omit => ({ userId: new RecordId("user", account.userId.replace("user:", "")), }) +/** @internal */ +// Convert AdapterAuthenticator to DB object +const authenticatorToDoc = ( + authenticator: AdapterAuthenticator +): Partial => ({ + ...authenticator, + userId: new RecordId("user", authenticator.userId.replace("user:", "")), +}) + /** @internal */ // Convert AdapterSession to DB object export const sessionToDoc = ( @@ -448,5 +476,88 @@ export function SurrealDBAdapter( } catch {} throw new Error("Verification Token not used") }, + async getAccount( + providerAccountId: AdapterAccount["providerAccountId"], + provider: AdapterAccount["provider"] + ) { + const surreal = await client + try { + const [accountsDoc] = await surreal.query<[AccountDoc[]]>( + `SELECT * FROM account WHERE providerAccountId = $pid AND provider = $provider LIMIT 1`, + { + pid: providerAccountId, + provider, + } + ) + if (accountsDoc.length) { + return docToAccount(accountsDoc[0]) + } + } catch {} + return null + }, + async createAuthenticator(authenticator: AdapterAuthenticator) { + try { + const surreal = await client + const authenticatorDoc = await surreal.create< + AuthenticatorDoc, + Omit + >("authenticator", authenticatorToDoc(authenticator)) + if (authenticatorDoc.length) { + return docToAuthenticator(authenticatorDoc[0]) + } + } catch {} + throw new Error("Authenticator not created") + }, + async getAuthenticator(credentialId: AdapterAuthenticator["credentialID"]) { + const surreal = await client + try { + const [authenticatorDoc] = await surreal.query<[AuthenticatorDoc[]]>( + `SELECT * FROM authenticator WHERE credentialID = $cid LIMIT 1`, + { + cid: credentialId, + } + ) + if (authenticatorDoc.length) { + return docToAuthenticator(authenticatorDoc[0]) + } + } catch {} + return null + }, + async listAuthenticatorsByUserId(userId: AdapterAuthenticator["userId"]) { + const surreal = await client + try { + const [authenticatorDocs] = await surreal.query<[AuthenticatorDoc[]]>( + `SELECT * FROM authenticator WHERE userId = $userId LIMIT 1`, + { + userId, + } + ) + + return authenticatorDocs.map((v) => docToAuthenticator(v)) + } catch {} + return null + }, + async updateAuthenticatorCounter( + credentialId: AdapterAuthenticator["credentialID"], + newCounter: AdapterAuthenticator["counter"] + ) { + try { + if (!credentialId) throw new Error("credential id is required") + const surreal = await client + const [authenticatorDoc] = await surreal.query<[AuthenticatorDoc]>( + `UPDATE ONLY authenticator MERGE $doc WHERE credentialID = $cid`, + { + cid: credentialId, + doc: { + counter: newCounter, + }, + } + ) + return docToAuthenticator(authenticatorDoc) + } catch {} + throw Error( + `Unable to update authenticator with credential ${credentialId}` + ) + }, } } diff --git a/packages/adapter-surrealdb/test/common.ts b/packages/adapter-surrealdb/test/common.ts index 5979e00ce8..44a683291d 100644 --- a/packages/adapter-surrealdb/test/common.ts +++ b/packages/adapter-surrealdb/test/common.ts @@ -6,20 +6,40 @@ import { docToAccount, docToSession, docToVerificationToken, + docToAuthenticator, } from "../src/index" import type { UserDoc, AccountDoc, SessionDoc, VerificationTokenDoc, + AuthenticatorDoc, } from "../src/index" -import { randomUUID } from "utils/adapter" export const config = (clientPromise: Promise) => ({ adapter: SurrealDBAdapter(clientPromise), + testWebAuthnMethods: true, db: { + // Generates a guid like surrealdb id() { - return randomUUID() + const length = 20 + const charset = + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + const charsetLength = charset.length + const result: string[] = [] + const byteRange = 256 // [0, 255) + const maxUsable = Math.floor(byteRange / charsetLength) * charsetLength + + while (result.length < length) { + const randomBytes = new Uint8Array(length - result.length) + crypto.getRandomValues(randomBytes) + for (let i = 0; i < randomBytes.length && result.length < length; i++) { + if (randomBytes[i] < maxUsable) { + result.push(charset[randomBytes[i] % charsetLength]) + } + } + } + return result.join("") }, connect: async () => { const surreal = await clientPromise @@ -28,6 +48,7 @@ export const config = (clientPromise: Promise) => ({ surreal.delete("session"), surreal.delete("verification_token"), surreal.delete("user"), + surreal.delete("authenticator"), ]) }, disconnect: async () => { @@ -38,6 +59,7 @@ export const config = (clientPromise: Promise) => ({ surreal.delete("session"), surreal.delete("verification_token"), surreal.delete("user"), + surreal.delete("authenticator"), ]) } catch (e) { console.error(e) @@ -90,5 +112,15 @@ export const config = (clientPromise: Promise) => ({ } return null }, + async authenticator(credentialID: string) { + const surreal = await clientPromise + const [authenticators] = await surreal.query<[AuthenticatorDoc[]]>( + `SELECT * FROM authenticator WHERE credentialID = $credentialID`, + { credentialID } + ) + const authenticator = authenticators.at(0) + if (authenticator) return docToAuthenticator(authenticator) + return null + }, }, }) diff --git a/packages/adapter-surrealdb/test/index.test.ts b/packages/adapter-surrealdb/test/index.test.ts index c3e213b216..069e300ea1 100644 --- a/packages/adapter-surrealdb/test/index.test.ts +++ b/packages/adapter-surrealdb/test/index.test.ts @@ -4,13 +4,12 @@ import { runBasicTests } from "utils/adapter" import { config } from "./common" const clientPromise = new Promise(async (resolve, reject) => { - const db = new Surreal() try { const db = new Surreal() await db.connect("ws://0.0.0.0:8000", { + namespace: "test", + database: "test", auth: { - namespace: "test", - database: "test", username: "test", password: "test", }, diff --git a/packages/adapter-surrealdb/test/test.sh b/packages/adapter-surrealdb/test/test.sh index e133ee054e..9383e45789 100755 --- a/packages/adapter-surrealdb/test/test.sh +++ b/packages/adapter-surrealdb/test/test.sh @@ -2,11 +2,18 @@ CONTAINER_NAME=authjs-surrealdb-test +# Get the latest 2.x container since 3.x-alpha is not yet fully supported by js library +TAG=$(curl -s "https://registry.hub.docker.com/v2/repositories/surrealdb/surrealdb/tags?page_size=100" \ +| jq -r '.results[].name' \ +| grep -E '^v2\.[0-9]+(\.[0-9]+)?$' \ +| sort -V \ +| tail -n1) + # Start db docker run -d --rm \ -p 8000:8000 \ --name ${CONTAINER_NAME} \ - surrealdb/surrealdb:latest start --log debug --user test --pass test memory + surrealdb/surrealdb:${TAG} start --log debug --user test --pass test memory echo "Waiting 5s for db to start..." sleep 5 From 293c64bdb95cce209330b0517f227f30eb7f573d Mon Sep 17 00:00:00 2001 From: Dylan Vanmali Date: Mon, 12 May 2025 17:02:07 -0700 Subject: [PATCH 10/14] fix: creating user should use id() not randomUUID() --- packages/utils/adapter.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/utils/adapter.ts b/packages/utils/adapter.ts index 33de4c1187..ed6b672848 100644 --- a/packages/utils/adapter.ts +++ b/packages/utils/adapter.ts @@ -385,7 +385,7 @@ export async function runBasicTests(options: TestOptions) { const providerAccountId = randomUUID() const provider = "auth0" const localUser = await adapter.createUser({ - id: randomUUID(), + id: id(), email: "getAccount@example.com", emailVerified: null, }) @@ -425,7 +425,7 @@ export async function runBasicTests(options: TestOptions) { // Setup const credentialID = randomUUID() const localUser = await adapter.createUser({ - id: randomUUID(), + id: id(), email: "createAuthenticator@example.com", emailVerified: null, }) @@ -461,7 +461,7 @@ export async function runBasicTests(options: TestOptions) { // Setup const credentialID = randomUUID() const localUser = await adapter.createUser({ - id: randomUUID(), + id: id(), email: "getAuthenticator@example.com", emailVerified: null, }) @@ -498,12 +498,12 @@ export async function runBasicTests(options: TestOptions) { maybeTest("listAuthenticatorsByUserId", async () => { // Setup const user1 = await adapter.createUser({ - id: randomUUID(), + id: id(), email: "listAuthenticatorsByUserId1@example.com", emailVerified: null, }) const user2 = await adapter.createUser({ - id: randomUUID(), + id: id(), email: "listAuthenticatorsByUserId2@example.com", emailVerified: null, }) @@ -580,7 +580,7 @@ export async function runBasicTests(options: TestOptions) { // Setup const credentialID = randomUUID() const localUser = await adapter.createUser({ - id: randomUUID(), + id: id(), email: "updateAuthenticatorCounter@example.com", emailVerified: null, }) From fe650ddebc50e8d37bf0d88008e899c7674bf42b Mon Sep 17 00:00:00 2001 From: Dylan Vanmali Date: Mon, 12 May 2025 17:03:36 -0700 Subject: [PATCH 11/14] fix: ignored files from prettier --- .prettierignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.prettierignore b/.prettierignore index 5838858cf7..c6856beab2 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,4 @@ +.prettierignore .cache-loader .DS_Store .pnpm-debug.log @@ -11,6 +12,7 @@ pnpm-lock.yaml .github/actions/issue-validator/index.mjs *.d.ts *.d.ts.map +**/*.sh .svelte-kit .next From 12c90ab58d233f19ca3d794f1686e3fd12ccc1d3 Mon Sep 17 00:00:00 2001 From: Dylan Vanmali Date: Mon, 12 May 2025 17:04:45 -0700 Subject: [PATCH 12/14] chore: upgrade lockfile as requested --- pnpm-lock.yaml | 98 +++++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e8dc49879f..f6fe6e4264 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -249,7 +249,7 @@ importers: version: 3.8.3(@algolia/client-search@5.20.0)(@types/react@18.2.78)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.2) '@inkeep/widgets': specifier: ^0.2.289 - version: 0.2.289(@internationalized/date@3.5.6)(@types/react@18.2.78)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3) + version: 0.2.289(@internationalized/date@3.5.2)(@types/react@18.2.78)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3) '@next/third-parties': specifier: ^14.2.15 version: 14.2.15(next@14.2.21(@opentelemetry/api@1.7.0)(@playwright/test@1.41.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.70.0))(react@18.3.1) @@ -606,9 +606,9 @@ importers: '@auth/core': specifier: workspace:* version: link:../core - surrealdb.js: - specifier: ^0.11.0 - version: 0.11.0 + surrealdb: + specifier: ^1.3.0 + version: 1.3.2(tslib@2.8.1)(typescript@5.6.3)(ws@8.18.1) packages/adapter-typeorm: dependencies: @@ -4189,6 +4189,7 @@ packages: '@playwright/test@1.41.2': resolution: {integrity: sha512-qQB9h7KbibJzrDpkXkYvsmiDJK14FULCCZgEcoe2AvFAS64oCirWTwzTlAYEbKaRxWs5TFesE1Na6izMv3HfGg==} engines: {node: '>=16'} + deprecated: Please update to the latest version of Playwright to test up-to-date browsers. hasBin: true '@polka/url@1.0.0-next.24': @@ -9207,6 +9208,11 @@ packages: peerDependencies: ws: '*' + isows@1.0.6: + resolution: {integrity: sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==} + peerDependencies: + ws: '*' + istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -12547,9 +12553,12 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - surrealdb.js@0.11.0: - resolution: {integrity: sha512-x/Qd0UYyNilwY27JZlWgP8NsCdBzDtextRzP9oIm7xO27qsZvE5Rh4wnYN0xD68zgRAE4W8Jsufbm+hKbaGsTg==} - deprecated: Please visit https://www.npmjs.com/package/surrealdb for the updated SurrealDB SDK. + surrealdb@1.3.2: + resolution: {integrity: sha512-mL7nij33iuon3IQP72F46fgX3p2LAxFCWCBDbZB7IohZ13RTEwJVNq7nZeP1eMSceQUpKzS6OHIWOuF9LYAkNw==} + engines: {node: '>=18.0.0'} + peerDependencies: + tslib: ^2.6.3 + typescript: ^5.0.0 svelte-check@2.10.2: resolution: {integrity: sha512-h1Tuiir0m8J5yqN+Vx6qgKKk1L871e6a9o7rMwVWfu8Qs6Wg7x2R+wcxS3SO3VpW5JCxCat90rxPsZMYgz+HaQ==} @@ -13343,12 +13352,6 @@ packages: resolution: {integrity: sha512-4luGP9LMYszMRZwsvyUd9MrxgEGZdZuZgpVQHEEX0lCYFESasVRvZd0EYpCkOIbJKHMuv0LskpXc/8Un+MJzEQ==} hasBin: true - unws@0.2.4: - resolution: {integrity: sha512-/N1ajiqrSp0A/26/LBg7r10fOcPtGXCqJRJ61sijUFoGZMr6ESWGYn7i0cwr7fR7eEECY5HsitqtjGHDZLAu2w==} - engines: {node: '>=16.14.0'} - peerDependencies: - ws: '*' - update-browserslist-db@1.0.13: resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} hasBin: true @@ -13437,6 +13440,10 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true + uuidv7@1.0.2: + resolution: {integrity: sha512-8JQkH4ooXnm1JCIhqTMbtmdnYEn6oKukBxHn1Ic9878jMkL7daTI7anTExfY18VRCX7tcdn5quzvCb6EWrR8PA==} + hasBin: true + uvu@0.5.6: resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} engines: {node: '>=8'} @@ -14206,7 +14213,7 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@ark-ui/anatomy@0.1.0(@internationalized/date@3.5.6)': + '@ark-ui/anatomy@0.1.0(@internationalized/date@3.5.2)': dependencies: '@zag-js/accordion': 0.20.0 '@zag-js/anatomy': 0.20.0 @@ -14217,7 +14224,7 @@ snapshots: '@zag-js/color-utils': 0.20.0 '@zag-js/combobox': 0.20.0 '@zag-js/date-picker': 0.20.0 - '@zag-js/date-utils': 0.20.0(@internationalized/date@3.5.6) + '@zag-js/date-utils': 0.20.0(@internationalized/date@3.5.2) '@zag-js/dialog': 0.20.0 '@zag-js/editable': 0.20.0 '@zag-js/hover-card': 0.20.0 @@ -14243,7 +14250,7 @@ snapshots: transitivePeerDependencies: - '@internationalized/date' - '@ark-ui/react@0.15.0(@internationalized/date@3.5.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@ark-ui/react@0.15.0(@internationalized/date@3.5.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@zag-js/accordion': 0.19.1 '@zag-js/anatomy': 0.19.1 @@ -14255,7 +14262,7 @@ snapshots: '@zag-js/combobox': 0.19.1 '@zag-js/core': 0.19.1 '@zag-js/date-picker': 0.19.1 - '@zag-js/date-utils': 0.19.1(@internationalized/date@3.5.6) + '@zag-js/date-utils': 0.19.1(@internationalized/date@3.5.2) '@zag-js/dialog': 0.19.1 '@zag-js/editable': 0.19.1 '@zag-js/hover-card': 0.19.1 @@ -17134,11 +17141,11 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@inkeep/components@0.0.24(@ark-ui/react@0.15.0(@internationalized/date@3.5.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@internationalized/date@3.5.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)': + '@inkeep/components@0.0.24(@ark-ui/react@0.15.0(@internationalized/date@3.5.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@internationalized/date@3.5.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)': dependencies: - '@ark-ui/react': 0.15.0(@internationalized/date@3.5.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@inkeep/preset': 0.0.24(@internationalized/date@3.5.6)(typescript@5.6.3) - '@inkeep/preset-chakra': 0.0.24(@internationalized/date@3.5.6)(typescript@5.6.3) + '@ark-ui/react': 0.15.0(@internationalized/date@3.5.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@inkeep/preset': 0.0.24(@internationalized/date@3.5.2)(typescript@5.6.3) + '@inkeep/preset-chakra': 0.0.24(@internationalized/date@3.5.2)(typescript@5.6.3) '@inkeep/shared': 0.0.25 '@inkeep/styled-system': 0.0.44 '@pandacss/dev': 0.22.1(typescript@5.6.3) @@ -17150,9 +17157,9 @@ snapshots: - jsdom - typescript - '@inkeep/preset-chakra@0.0.24(@internationalized/date@3.5.6)(typescript@5.6.3)': + '@inkeep/preset-chakra@0.0.24(@internationalized/date@3.5.2)(typescript@5.6.3)': dependencies: - '@ark-ui/anatomy': 0.1.0(@internationalized/date@3.5.6) + '@ark-ui/anatomy': 0.1.0(@internationalized/date@3.5.2) '@inkeep/shared': 0.0.25 '@pandacss/dev': 0.22.1(typescript@5.6.3) transitivePeerDependencies: @@ -17160,10 +17167,10 @@ snapshots: - jsdom - typescript - '@inkeep/preset@0.0.24(@internationalized/date@3.5.6)(typescript@5.6.3)': + '@inkeep/preset@0.0.24(@internationalized/date@3.5.2)(typescript@5.6.3)': dependencies: - '@ark-ui/anatomy': 0.1.0(@internationalized/date@3.5.6) - '@inkeep/preset-chakra': 0.0.24(@internationalized/date@3.5.6)(typescript@5.6.3) + '@ark-ui/anatomy': 0.1.0(@internationalized/date@3.5.2) + '@inkeep/preset-chakra': 0.0.24(@internationalized/date@3.5.2)(typescript@5.6.3) '@inkeep/shared': 0.0.25 '@pandacss/dev': 0.22.1(typescript@5.6.3) colorjs.io: 0.4.5 @@ -17178,14 +17185,14 @@ snapshots: '@inkeep/styled-system@0.0.46': {} - '@inkeep/widgets@0.2.289(@internationalized/date@3.5.6)(@types/react@18.2.78)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)': + '@inkeep/widgets@0.2.289(@internationalized/date@3.5.2)(@types/react@18.2.78)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3)': dependencies: '@apollo/client': 3.9.5(@types/react@18.2.78)(graphql-ws@5.14.3(graphql@16.8.1))(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@ark-ui/react': 0.15.0(@internationalized/date@3.5.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ark-ui/react': 0.15.0(@internationalized/date@3.5.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@inkeep/color-mode': 0.0.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@inkeep/components': 0.0.24(@ark-ui/react@0.15.0(@internationalized/date@3.5.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@internationalized/date@3.5.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3) - '@inkeep/preset': 0.0.24(@internationalized/date@3.5.6)(typescript@5.6.3) - '@inkeep/preset-chakra': 0.0.24(@internationalized/date@3.5.6)(typescript@5.6.3) + '@inkeep/components': 0.0.24(@ark-ui/react@0.15.0(@internationalized/date@3.5.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@internationalized/date@3.5.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.6.3) + '@inkeep/preset': 0.0.24(@internationalized/date@3.5.2)(typescript@5.6.3) + '@inkeep/preset-chakra': 0.0.24(@internationalized/date@3.5.2)(typescript@5.6.3) '@inkeep/shared': 0.0.25 '@inkeep/styled-system': 0.0.46 '@types/lodash.isequal': 4.5.8 @@ -20532,9 +20539,9 @@ snapshots: dependencies: '@internationalized/date': 3.5.2 - '@zag-js/date-utils@0.19.1(@internationalized/date@3.5.6)': + '@zag-js/date-utils@0.20.0(@internationalized/date@3.5.2)': dependencies: - '@internationalized/date': 3.5.6 + '@internationalized/date': 3.5.2 '@zag-js/date-utils@0.20.0(@internationalized/date@3.5.6)': dependencies: @@ -24823,6 +24830,10 @@ snapshots: dependencies: ws: 8.18.0 + isows@1.0.6(ws@8.18.1): + dependencies: + ws: 8.18.1 + istanbul-lib-coverage@3.2.2: {} istanbul-lib-report@3.0.1: @@ -29091,14 +29102,14 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - surrealdb.js@0.11.0: + surrealdb@1.3.2(tslib@2.8.1)(typescript@5.6.3)(ws@8.18.1): dependencies: - unws: 0.2.4(ws@8.18.0) - ws: 8.18.0 - zod: 3.22.4 + isows: 1.0.6(ws@8.18.1) + tslib: 2.8.1 + typescript: 5.6.3 + uuidv7: 1.0.2 transitivePeerDependencies: - - bufferutil - - utf-8-validate + - ws svelte-check@2.10.2(@babel/core@7.23.9)(postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.3.3)))(postcss@8.4.47)(pug@3.0.2)(sass@1.70.0)(svelte@4.2.19): dependencies: @@ -29937,10 +29948,6 @@ snapshots: consola: 3.2.3 pathe: 1.1.2 - unws@0.2.4(ws@8.18.0): - dependencies: - ws: 8.18.0 - update-browserslist-db@1.0.13(browserslist@4.23.0): dependencies: browserslist: 4.23.0 @@ -30012,6 +30019,8 @@ snapshots: uuid@9.0.1: {} + uuidv7@1.0.2: {} + uvu@0.5.6: dependencies: dequal: 2.0.3 @@ -30449,8 +30458,7 @@ snapshots: ws@8.18.0: {} - ws@8.18.1: - optional: true + ws@8.18.1: {} xml2js@0.5.0: dependencies: From e0786c92f67474ecb39e9d2c73785dddba892c57 Mon Sep 17 00:00:00 2001 From: Dylan Vanmali Date: Mon, 19 May 2025 09:25:34 -0700 Subject: [PATCH 13/14] fix: tsc build errors --- packages/adapter-surrealdb/src/index.ts | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/packages/adapter-surrealdb/src/index.ts b/packages/adapter-surrealdb/src/index.ts index 06bdafcaa9..1ecd82f37e 100644 --- a/packages/adapter-surrealdb/src/index.ts +++ b/packages/adapter-surrealdb/src/index.ts @@ -55,7 +55,7 @@ export type VerificationTokenDoc = Document> & { export type AuthenticatorDoc> = Document< RecordId<"authenticator"> > & - AdapterAuthenticator & { + Omit & { userId: T counter: number } @@ -85,11 +85,7 @@ export const docToAuthenticator = ( doc: AuthenticatorDoc ): AdapterAuthenticator => ({ ...doc, - id: doc.id.id.toString(), - userId: - doc.userId instanceof RecordId - ? doc.userId.id.toString() - : doc.userId.id.id.toString(), + userId: doc.userId.id.toString(), }) /** @internal */ @@ -111,13 +107,11 @@ export const docToSession = ( /** @internal */ // Convert DB object to Verification Token export const docToVerificationToken = ( - doc: Omit + doc: VerificationTokenDoc ): VerificationToken => ({ - ...doc, - expires: - typeof doc?.expires === "string" - ? new Date(Date.parse(doc.expires)) - : (doc?.expires ?? null), + identifier: doc.identifier, + expires: doc.expires, + token: doc.token, }) /** @internal */ @@ -441,7 +435,7 @@ export function SurrealDBAdapter( verificationTokenDocs[0] if (verificationTokenDoc.id) delete verificationTokenDoc.id return docToVerificationToken( - verificationTokenDoc as Omit + verificationTokenDoc as VerificationTokenDoc ) } } catch {} @@ -467,7 +461,7 @@ export function SurrealDBAdapter( const verificationTokenDoc: Partial = vt if (verificationTokenDoc.id) delete verificationTokenDoc.id return docToVerificationToken( - verificationTokenDoc as Omit + verificationTokenDoc as VerificationTokenDoc ) } } else { @@ -535,7 +529,7 @@ export function SurrealDBAdapter( return authenticatorDocs.map((v) => docToAuthenticator(v)) } catch {} - return null + throw new Error("Verification Token not found") }, async updateAuthenticatorCounter( credentialId: AdapterAuthenticator["credentialID"], From 241426ce0853de42866f10f6b01a631322be7c3b Mon Sep 17 00:00:00 2001 From: Dylan Vanmali Date: Mon, 19 May 2025 09:27:19 -0700 Subject: [PATCH 14/14] fix: tsc build errors --- packages/adapter-surrealdb/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/adapter-surrealdb/src/index.ts b/packages/adapter-surrealdb/src/index.ts index 1ecd82f37e..7f117fecfe 100644 --- a/packages/adapter-surrealdb/src/index.ts +++ b/packages/adapter-surrealdb/src/index.ts @@ -76,7 +76,7 @@ export const docToUser = (doc: UserDoc): AdapterUser => ({ export const docToAccount = (doc: AccountDoc): AdapterAccount => ({ ...doc, id: doc.id.id.toString(), - userId: doc.userId instanceof RecordId ? doc.userId.id.toString() : "", + userId: doc.userId.id.toString(), }) /** @internal */