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 diff --git a/docs/pages/getting-started/adapters/surrealdb.mdx b/docs/pages/getting-started/adapters/surrealdb.mdx index a70d335635..c2b079d169 100644 --- a/docs/pages/getting-started/adapters/surrealdb.mdx +++ b/docs/pages/getting-started/adapters/surrealdb.mdx @@ -13,17 +13,27 @@ import { Code } from "@/components/Code" ### Installation ```bash npm2yarn -npm install @auth/surrealdb-adapter surrealdb.js +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_SURREALDB_CONNECTION -AUTH_SURREALDB_USERNAME -AUTH_SURREALDB_PASSWORD -AUTH_SURREALDB_NS -AUTH_SURREALDB_DB +AUTH_SURREAL_URL (required) +AUTH_SURREAL_NS +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 @@ -97,84 +107,172 @@ 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. +### Utils File + +```ts filename="./lib/surrealdb_utils.ts" +import type { ConnectOptions, AnyAuth } from "surrealdb" +import { Surreal, ConnectionStatus } from "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(): 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(this._url, this._opts) + if (process.env.NODE_ENV === "development") { + const str = this.toConnectionString( + this._surrealdb.status, + this._opts + ) + console.info(str) + } + } catch (error) { + if (error instanceof Error) throw error + throw new Error(error as unknown as string) + } + } + return this._surrealdb + } + + 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 + * 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 +} +``` + ### Authorization -#### Option 1 – Using RPC: +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 { Surreal } from "surrealdb.js" - -const connectionString = process.env.AUTH_SURREALDB_CONNECTION -const username = process.env.AUTH_SURREALDB_USERNAME -const password = process.env.AUTH_SURREALDB_PASSWORD -const namespace = process.env.AUTH_SURREALDB_NAMESPACE -const database = process.env.AUTH_SURREALDB_DATABASE -if (!connectionString || !username || !password || !namespace || !database) { - throw new Error( - "SurrealDB connection string, username, password, namespace, and database are required" - ) -} +import { type Surreal } from "surrealdb" +import { handleDisconnect, MySurreal, toAnyAuth } from "./lib/surrealdb_utils" const clientPromise = new Promise(async (resolve, reject) => { - const db = new Surreal() try { - await db.connect(`${connectionString}/rpc`, { + const { + 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 (!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, - auth: { - username, - password, - }, + auth, }) + const db = await surreal.surrealdb() resolve(db) } catch (e) { reject(e) } }) - -// Export a module-scoped Promise. By doing this in a -// separate module, the client can be shared across functions. -export default clientPromise ``` -#### Option 2 – Using HTTP: +#### HTTP ENGINE -Useful in serverless environments like Vercel. +With this configuration, we can use the database's http endpoint. Thus, the `AUTH_SURREAL_URL` should begin with `http` or `https`. -```ts filename="./lib/surrealdb.ts" -import { ExperimentalSurrealHTTP } from "surrealdb.js" - -const connectionString = process.env.AUTH_SURREALDB_CONNECTION -const username = process.env.AUTH_SURREALDB_USERNAME -const password = process.env.AUTH_SURREALDB_PASSWORD -const namespace = process.env.AUTH_SURREALDB_NAMESPACE -const database = process.env.AUTH_SURREALDB_DATABASE -if (!connectionString || !username || !password || !namespace || !database) { - throw new Error( - "SurrealDB connection string, username, password, namespace, and database are required" - ) -} +#### Websocket ENGINE -const clientPromise = new Promise>( - async (resolve, reject) => { - try { - const db = new ExperimentalSurrealHTTP(connectionString, { - fetch, - namespace, - database, - auth: { - username, - password, - }, - }) - resolve(db) - } catch (e) { - reject(e) - } - } -) - -// Export a module-scoped Promise. By doing this in a -// separate module, the client can be shared across functions. -export default clientPromise -``` +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/package.json b/packages/adapter-surrealdb/package.json index b0d05d95d2..51c993fb7e 100644 --- a/packages/adapter-surrealdb/package.json +++ b/packages/adapter-surrealdb/package.json @@ -1,14 +1,15 @@ { "name": "@auth/surrealdb-adapter", - "version": "1.9.1", + "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, @@ -45,6 +46,6 @@ "@auth/core": "workspace:*" }, "peerDependencies": { - "surrealdb.js": "^0.11.0" + "surrealdb": "^1.3.0" } } diff --git a/packages/adapter-surrealdb/src/index.ts b/packages/adapter-surrealdb/src/index.ts index f0ce26b047..7f117fecfe 100644 --- a/packages/adapter-surrealdb/src/index.ts +++ b/packages/adapter-surrealdb/src/index.ts @@ -14,138 +14,197 @@ * * @module @auth/surrealdb-adapter */ -import Surreal, { ExperimentalSurrealHTTP } from "surrealdb.js" +import { Surreal, RecordId } from "surrealdb" import type { Adapter, AdapterUser, AdapterAccount, + AdapterAccountType, + AdapterAuthenticator, 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> = Document> & { 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 = Document> & { + identifier: string + expires: Date + token: string +} +export type AuthenticatorDoc> = Document< + RecordId<"authenticator"> +> & + Omit & { + userId: T + counter: number + } /** @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 ?? null), }) /** @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.id.toString(), +}) + +/** @internal */ +// Convert DB object to AdapterAccount +export const docToAuthenticator = ( + doc: AuthenticatorDoc +): AdapterAuthenticator => ({ + ...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: VerificationTokenDoc +): VerificationToken => ({ + identifier: doc.identifier, + expires: doc.expires, + token: doc.token, }) /** @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 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 = ( 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 VerificationToken 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 +214,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 +229,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 +314,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 +348,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 +374,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 +408,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 VerificationTokenDoc + ) + } + } catch {} + throw new Error("Verification Token not created") }, async useVerificationToken({ identifier, @@ -386,31 +450,108 @@ 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 VerificationTokenDoc + ) } } else { return null } } 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 {} + throw new Error("Verification Token not found") + }, + 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 67582825a2..44a683291d 100644 --- a/packages/adapter-surrealdb/test/common.ts +++ b/packages/adapter-surrealdb/test/common.ts @@ -1,22 +1,45 @@ -import Surreal, { ExperimentalSurrealHTTP } from "surrealdb.js" +import { RecordId, Surreal } from "surrealdb" import { SurrealDBAdapter, docToUser, docToAccount, docToSession, - toSurrealId, + docToVerificationToken, + docToAuthenticator, +} from "../src/index" +import type { + UserDoc, + AccountDoc, + SessionDoc, + VerificationTokenDoc, + AuthenticatorDoc, } 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), + 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 @@ -25,6 +48,7 @@ export const config = ( surreal.delete("session"), surreal.delete("verification_token"), surreal.delete("user"), + surreal.delete("authenticator"), ]) }, disconnect: async () => { @@ -35,67 +59,68 @@ export const config = ( surreal.delete("session"), surreal.delete("verification_token"), surreal.delete("user"), + surreal.delete("authenticator"), ]) } catch (e) { - console.log(e) + console.error(e) } 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) + 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 }, + 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 dd265e6dab..069e300ea1 100644 --- a/packages/adapter-surrealdb/test/index.test.ts +++ b/packages/adapter-surrealdb/test/index.test.ts @@ -1,12 +1,12 @@ -import Surreal, { ExperimentalSurrealHTTP } from "surrealdb.js" +import { Surreal } from "surrealdb" import { runBasicTests } from "utils/adapter" 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", { namespace: "test", database: "test", auth: { @@ -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)) 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 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, }) 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: