diff --git a/packages/adapter-node-redis/.env.test b/packages/adapter-node-redis/.env.test new file mode 100644 index 0000000000..b715203c98 --- /dev/null +++ b/packages/adapter-node-redis/.env.test @@ -0,0 +1,2 @@ +NODE_REDIS_URL=redis://localhost:6379 +NODE_REDIS_PASSWORD= \ No newline at end of file diff --git a/packages/adapter-node-redis/README.md b/packages/adapter-node-redis/README.md new file mode 100644 index 0000000000..bc47758ce8 --- /dev/null +++ b/packages/adapter-node-redis/README.md @@ -0,0 +1,28 @@ +

+
+ + + + + + +

Node Redis Adapter - NextAuth.js / Auth.js

+

+ + TypeScript + + + npm + + + Downloads + + + GitHub Stars + +

+

+ +--- + +Check out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/node-redis). diff --git a/packages/adapter-node-redis/docker-compose.yml b/packages/adapter-node-redis/docker-compose.yml new file mode 100644 index 0000000000..e738f9144d --- /dev/null +++ b/packages/adapter-node-redis/docker-compose.yml @@ -0,0 +1,5 @@ +services: + redis: + image: redis + ports: + - "6379:6379" diff --git a/packages/adapter-node-redis/package.json b/packages/adapter-node-redis/package.json new file mode 100644 index 0000000000..f86439c77a --- /dev/null +++ b/packages/adapter-node-redis/package.json @@ -0,0 +1,53 @@ +{ + "name": "@auth/node-redis-adapter", + "version": "1.0.0", + "description": "Node Redis adapter for Auth.js.", + "homepage": "https://authjs.dev", + "repository": "https://github.com/nextauthjs/next-auth", + "bugs": { + "url": "https://github.com/nextauthjs/next-auth/issues" + }, + "author": "github.com/lengerrong", + "type": "module", + "types": "./index.d.ts", + "files": [ + "*.js", + "*.d.ts*", + "src" + ], + "exports": { + ".": { + "types": "./index.d.ts", + "import": "./index.js" + } + }, + "license": "ISC", + "keywords": [ + "next-auth", + "next.js", + "oauth", + "node", + "redis" + ], + "private": false, + "publishConfig": { + "access": "public" + }, + "scripts": { + "test": "./test/test.sh", + "build": "tsc", + "clean": "rm -rf *.js *.d.ts*" + }, + "dependencies": { + "@auth/core": "workspace:*" + }, + "peerDependencies": { + "redis": "^5.7.0", + "uuid": "^11.1.0" + }, + "devDependencies": { + "@types/uuid": "^8.3.3", + "redis": "^5.7.0", + "dotenv": "^10.0.0" + } +} diff --git a/packages/adapter-node-redis/src/index.ts b/packages/adapter-node-redis/src/index.ts new file mode 100644 index 0000000000..10ebe96477 --- /dev/null +++ b/packages/adapter-node-redis/src/index.ts @@ -0,0 +1,394 @@ +/** + *
+ *

Official Node Redis adapter for Auth.js / NextAuth.js.

+ * + * + * + *
+ * + * ## Installation + * + * ```bash npm2yarn + * npm install redis @auth/node-redis-adapter + * ``` + * + * @module @auth/node-redis-adapter + */ +import { Account } from "@auth/core" + +import type { + Adapter, + AdapterUser, + AdapterSession, + VerificationToken, +} from "@auth/core/adapters" + +import { RedisClientType } from "redis" + +import { v4 as uuid } from "uuid" + +import { withRedisClient } from "./utils" + +export interface RedisAdapterOptions { + baseKeyPrefix?: string + accountKeyPrefix?: string + accountByUserIdPrefix?: string + emailKeyPrefix?: string + sessionKeyPrefix?: string + sessionByUserIdKeyPrefix?: string + userKeyPrefix?: string + verificationTokenKeyPrefix?: string +} + +export const defaultOptions: RedisAdapterOptions = { + baseKeyPrefix: "", + accountKeyPrefix: "user:account:", + accountByUserIdPrefix: "user:account:by-user-id:", + emailKeyPrefix: "user:email:", + sessionKeyPrefix: "user:session:", + sessionByUserIdKeyPrefix: "user:session:by-user-id:", + userKeyPrefix: "user:", + verificationTokenKeyPrefix: "user:token:", +} + +const isoDateRE = + /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/ +function isDate(value: any) { + return value && isoDateRE.test(value) && !isNaN(Date.parse(value)) +} + +export const hydrateDates = (json: object) => { + return Object.entries(json).reduce((acc, [key, val]) => { + acc[key] = isDate(val) ? new Date(val as string) : val + return acc + }, {} as any) +} + +export function NodeRedisAdapter(options: RedisAdapterOptions = {}): Adapter { + const mergedOptions = Object.entries({ + ...defaultOptions, + ...options, + }).reduce( + (acc, [key, val]) => { + if (key !== "baseKeyPrefix") { + acc[key as keyof RedisAdapterOptions] = acc["baseKeyPrefix"] + val + } + return acc + }, + { + baseKeyPrefix: options.baseKeyPrefix || defaultOptions.baseKeyPrefix, + } as RedisAdapterOptions + ) + + const { + accountKeyPrefix, + accountByUserIdPrefix, + emailKeyPrefix, + sessionKeyPrefix, + sessionByUserIdKeyPrefix, + userKeyPrefix, + verificationTokenKeyPrefix, + } = mergedOptions + + const getObjectFromRedis = ( + client: RedisClientType, + key: string + ) => { + return client.get(key).then((value) => { + if (!value) return null + try { + const t = JSON.parse(value) as T + return hydrateDates(t) as T + } catch (error: unknown) { + console.error("RedisAdapter.getObjectFromRedis", error) + return null + } + }) + } + + const saveObject = async ( + client: RedisClientType, + key: string, + obj: T + ) => { + await client.set(key, JSON.stringify(obj)) + return obj + } + + const getObject = async ( + client: RedisClientType, + key: string + ) => { + return getObjectFromRedis(client, key) + } + + const getUser = (client: RedisClientType, id: string) => { + return getObject(client, userKeyPrefix + id) + } + + const setUser = async ( + client: RedisClientType, + user: Omit + ) => { + const id = uuid() + if (user["email"]) { + await client.set(`${emailKeyPrefix}${user["email"]}`, id) + } + const savedUser = { + ...user, + id, + } as AdapterUser + await saveObject(client, userKeyPrefix + id, savedUser) + return savedUser + } + + const deleteUser = async (client: RedisClientType, user: AdapterUser) => { + if (user["email"]) { + await client.del(`${emailKeyPrefix}${user["email"]}`) + } + await client.del(`${userKeyPrefix}${user.id}`) + const sessionByUserIdKey = `${sessionByUserIdKeyPrefix}${user.id}` + const sessionKey = await client.get(sessionByUserIdKey) + await client.del(sessionByUserIdKey) + if (sessionKey) { + await client.del(sessionKey) + } + const accountByUserIdKey = accountByUserIdPrefix + user.id + const accountKey = await client.get(accountByUserIdKey) + await client.del(accountByUserIdKey) + if (accountKey) { + await client.del(accountKey) + } + return user + } + + const updateUser = (client: RedisClientType, user: AdapterUser) => { + return getObject(client, userKeyPrefix + user.id).then( + async (olduser) => { + // delete olduser email to prevent email duplication + if (olduser && user["email"] !== olduser["email"]) { + await client.del(`${emailKeyPrefix}${olduser["email"]}`) + await client.set(`${emailKeyPrefix}${user["email"]}`, user.id) + } + await saveObject(client, userKeyPrefix + user.id, { + ...olduser, + ...user, + } as AdapterUser) + return user + } + ) + } + + return { + createUser: async (user: Omit) => { + return withRedisClient(async (client: RedisClientType) => { + return setUser(client, user) + }) + }, + getUser: async (id: string) => { + return withRedisClient(async (client: RedisClientType) => { + return getUser(client, id) + }) + }, + getUserByEmail: async (email: string) => { + return withRedisClient(async (client: RedisClientType) => { + const id = await client.get(emailKeyPrefix + email) + if (!id) return null + return getObject(client, userKeyPrefix + id) + }) + }, + updateUser: async (user: Partial) => { + return withRedisClient(async (client: RedisClientType) => { + if (!user.id) { + return setUser(client, { + ...user, + email: user.email || "", + emailVerified: user.emailVerified || null, + }) + } + return updateUser(client, user as AdapterUser) + }) + }, + deleteUser: (userId: string) => { + return withRedisClient(async (client: RedisClientType) => { + return getObject(client, userKeyPrefix + userId) + .then((user) => { + if (user) { + return deleteUser(client, user) + } + return Promise.resolve(null) + }) + .catch(() => Promise.resolve(null)) + }) + }, + /** Using the provider id and the id of the user for a specific account, get the user. */ + getUserByAccount: ( + providerAccountId: Pick + ) => { + const id = `${accountKeyPrefix}${providerAccountId.provider}:${providerAccountId.providerAccountId}` + return withRedisClient(async (client: RedisClientType) => { + return getObject(client, id).then((account) => { + if (account) { + return getObject( + client, + `${userKeyPrefix}${account.userId}` + ) + } + return Promise.resolve(null) + }) + }) + }, + linkAccount: (account: Account) => { + const id = `${accountKeyPrefix}${account.provider}:${account.providerAccountId}` + return withRedisClient(async (client: RedisClientType) => { + return saveObject(client, id, account) + .then((account) => { + client + .set(accountByUserIdPrefix + account.userId, id) + .catch(console.error) + return account + }) + .catch(() => Promise.resolve(undefined)) + }) + }, + unlinkAccount: ( + providerAccountId: Pick + ) => { + const id = `${accountKeyPrefix}${providerAccountId.provider}:${providerAccountId.providerAccountId}` + return withRedisClient(async (client: RedisClientType) => { + return getObject(client, id) + .then((account) => { + if (account) { + client + .del(accountByUserIdPrefix + account.userId) + .catch(console.error) + return client.del(id).then(() => account) + } + return Promise.resolve(undefined) + }) + .catch(() => Promise.resolve(undefined)) + }) + }, + /** Creates a session for the user and returns it. */ + createSession: (session: { + sessionToken: string + userId: string + expires: Date + }) => { + const key = `${sessionKeyPrefix}${session.sessionToken}` + return withRedisClient(async (client: RedisClientType) => { + return saveObject(client, key, session).then(() => { + client + .expireAt(key, Math.floor(session.expires.getTime() / 1000)) + .catch(console.error) + client + .set(`${sessionByUserIdKeyPrefix}${session.userId}`, key) + .catch(console.error) + return session as AdapterSession + }) + }) + }, + getSessionAndUser: (sessionToken: string) => { + return withRedisClient(async (client: RedisClientType) => { + return getObject( + client, + `${sessionKeyPrefix}${sessionToken}` + ) + .then((session) => { + if (session) { + return getObject( + client, + userKeyPrefix + session.userId + ).then((user) => { + if (user) { + return { + session, + user, + } + } + return { + session, + user: {} as AdapterUser, + } + }) + } + return Promise.resolve(null) + }) + .catch(() => Promise.resolve(null)) + }) + }, + updateSession: ( + session: Partial & Pick + ) => { + return withRedisClient(async (client: RedisClientType) => { + return getObject( + client, + `${sessionKeyPrefix}${session.sessionToken}` + ) + .then((oldsession) => + saveObject(client, `${sessionKeyPrefix}${session?.sessionToken}`, { + ...oldsession, + ...session, + } as AdapterSession) + ) + .catch(() => Promise.resolve(session as AdapterSession)) + }) + }, + /** + * Deletes a session from the database. + * It is preferred that this method also returns the session + * that is being deleted for logging purposes. + */ + deleteSession: (sessionToken: string) => { + return withRedisClient(async (client: RedisClientType) => { + return getObject( + client, + `${sessionKeyPrefix}${sessionToken}` + ) + .then((session) => { + if (session) { + client + .del(`${sessionByUserIdKeyPrefix}${session.userId}`) + .catch(console.error) + return client + .del(`${sessionKeyPrefix}${sessionToken}`) + .then(() => session) + } + return Promise.resolve(null) + }) + .catch(() => Promise.resolve(null)) + }) + }, + createVerificationToken: (verificationToken: VerificationToken) => { + return withRedisClient(async (client: RedisClientType) => { + return saveObject( + client, + `${verificationTokenKeyPrefix}${verificationToken.identifier}:${verificationToken.token}`, + verificationToken + ) + }) + }, + /** + * Return verification token from the database + * and delete it so it cannot be used again. + */ + useVerificationToken: ({ + identifier, + token, + }: { + identifier: string + token: string + }) => { + return withRedisClient(async (client: RedisClientType) => { + return getObject( + client, + `${verificationTokenKeyPrefix}${identifier}:${token}` + ).then((verificationToken) => + client + .del(`${verificationTokenKeyPrefix}${identifier}:${token}`) + .then(() => verificationToken) + ) + }) + }, + } +} diff --git a/packages/adapter-node-redis/src/utils.ts b/packages/adapter-node-redis/src/utils.ts new file mode 100644 index 0000000000..e464643464 --- /dev/null +++ b/packages/adapter-node-redis/src/utils.ts @@ -0,0 +1,60 @@ +import { createClient, RedisClientType } from "redis" + +// Singleton Redis client +let redisClient: RedisClientType | null = null +let initializing = false +let initializedPromise: Promise | null = null + +export const getRedisClient = async (): Promise => { + if (redisClient && redisClient.isOpen) { + return redisClient + } + + // Prevent duplicate initializations + if (initializing && initializedPromise) { + return initializedPromise + } + + initializing = true + + initializedPromise = (async () => { + redisClient = createClient({ + url: process.env.NODE_REDIS_URL, + password: process.env.NODE_REDIS_PASSWORD, + }) + + redisClient.on("error", (err) => { + console.error("Redis error:", err) + }) + + await redisClient.connect() + console.log("Connected to Redis") + + return redisClient + })() + + try { + return await initializedPromise + } finally { + initializing = false + } +} + +/** + * Helper function to perform operations with Redis client + * Automatically handles getting the client and proper error handling + * @param operation Function that receives Redis client and performs operations + * @returns Result of the operation + */ + +export const withRedisClient = async ( + operation: (client: RedisClientType) => Promise +): Promise => { + const client = await getRedisClient() + try { + return await operation(client) + } catch (error) { + console.error("Redis operation failed:", error) + throw error + } +} diff --git a/packages/adapter-node-redis/test/index.test.ts b/packages/adapter-node-redis/test/index.test.ts new file mode 100644 index 0000000000..e5c34589af --- /dev/null +++ b/packages/adapter-node-redis/test/index.test.ts @@ -0,0 +1,42 @@ +import { runBasicTests } from "utils/adapter" +import { hydrateDates, NodeRedisAdapter } from "../src" +import { getRedisClient } from "../src/utils" + +import "dotenv/config" + +const client = await getRedisClient() + +runBasicTests({ + adapter: NodeRedisAdapter({ baseKeyPrefix: "testNodeRedis:" }), + db: { + disconnect: async () => { + await client.quit() + }, + async user(id: string) { + const data = await client.get(`testNodeRedis:user:${id}`) + if (!data) return null + return hydrateDates(JSON.parse(data)) + }, + async account({ provider, providerAccountId }) { + const data = await client.get( + `testNodeRedis:user:account:${provider}:${providerAccountId}` + ) + if (!data) return null + return hydrateDates(JSON.parse(data)) + }, + async session(sessionToken) { + const data = await client.get( + `testNodeRedis:user:session:${sessionToken}` + ) + if (!data) return null + return hydrateDates(JSON.parse(data)) + }, + async verificationToken(where) { + const data = await client.get( + `testNodeRedis:user:token:${where.identifier}:${where.token}` + ) + if (!data) return null + return hydrateDates(JSON.parse(data)) + }, + }, +}) diff --git a/packages/adapter-node-redis/test/test.sh b/packages/adapter-node-redis/test/test.sh new file mode 100755 index 0000000000..de7ca8c009 --- /dev/null +++ b/packages/adapter-node-redis/test/test.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +echo "Initializing container for Node-Redis tests" + +# Init Redis containers +docker compose up -d + +echo "Waiting 5s for db to start..." +sleep 5 + +# Always stop container, but exit with 1 when tests are failing +if vitest run -c ../utils/vitest.config.ts; then + docker compose down -v +else + docker compose down -v && exit 1 +fi diff --git a/packages/adapter-node-redis/tsconfig.json b/packages/adapter-node-redis/tsconfig.json new file mode 100644 index 0000000000..6f3b51d36f --- /dev/null +++ b/packages/adapter-node-redis/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../utils/tsconfig.json", + "compilerOptions": { + "outDir": ".", + "rootDir": "src" + }, + "exclude": ["*.js", "*.d.ts"], + "include": ["src/**/*"] +} diff --git a/packages/adapter-node-redis/typedoc.config.cjs b/packages/adapter-node-redis/typedoc.config.cjs new file mode 100644 index 0000000000..2edb8a5046 --- /dev/null +++ b/packages/adapter-node-redis/typedoc.config.cjs @@ -0,0 +1,14 @@ +// @ts-check + +/** + * @type {import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').MarkdownTheme} + */ +module.exports = { + entryPoints: ["src/index.ts"], + entryPointStrategy: "expand", + tsconfig: "./tsconfig.json", + entryModule: "@auth/node-redis-adapter", + entryFileName: "../node-redis-adapter.mdx", + includeVersion: true, + readme: 'none', +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f6fe6e4264..78b306b82d 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.2)(@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.6)(@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) @@ -527,6 +527,25 @@ importers: specifier: ^8.18.0 version: 8.18.0 + packages/adapter-node-redis: + dependencies: + '@auth/core': + specifier: workspace:* + version: link:../core + uuid: + specifier: ^11.1.0 + version: 11.1.0 + devDependencies: + '@types/uuid': + specifier: ^8.3.3 + version: 8.3.4 + dotenv: + specifier: ^10.0.0 + version: 10.0.0 + redis: + specifier: ^5.7.0 + version: 5.8.0 + packages/adapter-pg: dependencies: '@auth/core': @@ -4495,10 +4514,20 @@ packages: peerDependencies: '@redis/client': ^1.0.0 + '@redis/bloom@5.8.0': + resolution: {integrity: sha512-kpKZzAAjGiGYn88Bqq6+ozxPg6kGYWRZH9vnOwGcoSCbrW14SZpZVMYMFSio8FH9ZJUdUcmT/RLGlA1W1t0UWQ==} + engines: {node: '>= 18'} + peerDependencies: + '@redis/client': ^5.8.0 + '@redis/client@1.5.13': resolution: {integrity: sha512-epkUM9D0Sdmt93/8Ozk43PNjLi36RZzG+d/T1Gdu5AI8jvghonTeLYV69WVWdilvFo+PYxbP0TZ0saMvr6nscQ==} engines: {node: '>=14'} + '@redis/client@5.8.0': + resolution: {integrity: sha512-ywZjKGoSSAECGYOd9bJpws6d4867SN686obUWT/sRmo1c/Q8V+jWyInvlqwKa0BOvTHHwYeB2WFUEvd6PADeOQ==} + engines: {node: '>= 18'} + '@redis/graph@1.1.1': resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} peerDependencies: @@ -4509,16 +4538,34 @@ packages: peerDependencies: '@redis/client': ^1.0.0 + '@redis/json@5.8.0': + resolution: {integrity: sha512-xPBpwY6aKoRzMSu67MpwrBiSliON9bfHo9Y/pSPBjW8/KoOm1MzGqwJUO20qdjXpFoKJsDWwxIE1LHdBNzcImw==} + engines: {node: '>= 18'} + peerDependencies: + '@redis/client': ^5.8.0 + '@redis/search@1.1.6': resolution: {integrity: sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==} peerDependencies: '@redis/client': ^1.0.0 + '@redis/search@5.8.0': + resolution: {integrity: sha512-lF9pNv9vySmirm1EzCH6YeRjhvH6lLT7tvebYHEM7WTkEQ/7kZWb4athliKESHpxzTQ36U9UbzuedSywHV6OhQ==} + engines: {node: '>= 18'} + peerDependencies: + '@redis/client': ^5.8.0 + '@redis/time-series@1.0.5': resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==} peerDependencies: '@redis/client': ^1.0.0 + '@redis/time-series@5.8.0': + resolution: {integrity: sha512-kPTlW2ACXokjQNXjCD8Pw9mHDoB94AHUlHFahyjxz9lUJUVwiva2Dgfrd2k3JxHhSBqyY2PREIj9YwIUSTSSqQ==} + engines: {node: '>= 18'} + peerDependencies: + '@redis/client': ^5.8.0 + '@repeaterjs/repeater@3.0.5': resolution: {integrity: sha512-l3YHBLAol6d/IKnB9LhpD0cEZWAoe3eFKUyTYWmFmCO2Q/WOckxLQAUyMZWwZV2M/m3+4vgRoaolFqaII82/TA==} @@ -11656,6 +11703,10 @@ packages: redis@4.6.12: resolution: {integrity: sha512-41Xuuko6P4uH4VPe5nE3BqXHB7a9lkFL0J29AlxKaIfD6eWO8VO/5PDF9ad2oS+mswMsfFxaM5DlE3tnXT+P8Q==} + redis@5.8.0: + resolution: {integrity: sha512-re0MHm1KHbiVIUPDGoUM3jldvjH5EM/wGZ3A33gyUYoC/UnVNKNnZHM5hcJVry7L2O2eJU3nflSXTliv10BTKg==} + engines: {node: '>= 18'} + reflect-metadata@0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} @@ -13427,6 +13478,10 @@ packages: resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} hasBin: true + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + hasBin: true + uuid@3.4.0: resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. @@ -14213,7 +14268,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.2)': + '@ark-ui/anatomy@0.1.0(@internationalized/date@3.5.6)': dependencies: '@zag-js/accordion': 0.20.0 '@zag-js/anatomy': 0.20.0 @@ -14224,7 +14279,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.2) + '@zag-js/date-utils': 0.20.0(@internationalized/date@3.5.6) '@zag-js/dialog': 0.20.0 '@zag-js/editable': 0.20.0 '@zag-js/hover-card': 0.20.0 @@ -14250,7 +14305,7 @@ snapshots: transitivePeerDependencies: - '@internationalized/date' - '@ark-ui/react@0.15.0(@internationalized/date@3.5.2)(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)': dependencies: '@zag-js/accordion': 0.19.1 '@zag-js/anatomy': 0.19.1 @@ -14262,7 +14317,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.2) + '@zag-js/date-utils': 0.19.1(@internationalized/date@3.5.6) '@zag-js/dialog': 0.19.1 '@zag-js/editable': 0.19.1 '@zag-js/hover-card': 0.19.1 @@ -17141,11 +17196,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.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/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)': dependencies: - '@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) + '@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) '@inkeep/shared': 0.0.25 '@inkeep/styled-system': 0.0.44 '@pandacss/dev': 0.22.1(typescript@5.6.3) @@ -17157,9 +17212,9 @@ snapshots: - jsdom - typescript - '@inkeep/preset-chakra@0.0.24(@internationalized/date@3.5.2)(typescript@5.6.3)': + '@inkeep/preset-chakra@0.0.24(@internationalized/date@3.5.6)(typescript@5.6.3)': dependencies: - '@ark-ui/anatomy': 0.1.0(@internationalized/date@3.5.2) + '@ark-ui/anatomy': 0.1.0(@internationalized/date@3.5.6) '@inkeep/shared': 0.0.25 '@pandacss/dev': 0.22.1(typescript@5.6.3) transitivePeerDependencies: @@ -17167,10 +17222,10 @@ snapshots: - jsdom - typescript - '@inkeep/preset@0.0.24(@internationalized/date@3.5.2)(typescript@5.6.3)': + '@inkeep/preset@0.0.24(@internationalized/date@3.5.6)(typescript@5.6.3)': dependencies: - '@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) + '@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) '@inkeep/shared': 0.0.25 '@pandacss/dev': 0.22.1(typescript@5.6.3) colorjs.io: 0.4.5 @@ -17185,14 +17240,14 @@ snapshots: '@inkeep/styled-system@0.0.46': {} - '@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)': + '@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)': 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.2)(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) '@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.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/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/shared': 0.0.25 '@inkeep/styled-system': 0.0.46 '@types/lodash.isequal': 4.5.8 @@ -18485,12 +18540,20 @@ snapshots: dependencies: '@redis/client': 1.5.13 + '@redis/bloom@5.8.0(@redis/client@5.8.0)': + dependencies: + '@redis/client': 5.8.0 + '@redis/client@1.5.13': dependencies: cluster-key-slot: 1.1.2 generic-pool: 3.9.0 yallist: 4.0.0 + '@redis/client@5.8.0': + dependencies: + cluster-key-slot: 1.1.2 + '@redis/graph@1.1.1(@redis/client@1.5.13)': dependencies: '@redis/client': 1.5.13 @@ -18499,14 +18562,26 @@ snapshots: dependencies: '@redis/client': 1.5.13 + '@redis/json@5.8.0(@redis/client@5.8.0)': + dependencies: + '@redis/client': 5.8.0 + '@redis/search@1.1.6(@redis/client@1.5.13)': dependencies: '@redis/client': 1.5.13 + '@redis/search@5.8.0(@redis/client@5.8.0)': + dependencies: + '@redis/client': 5.8.0 + '@redis/time-series@1.0.5(@redis/client@1.5.13)': dependencies: '@redis/client': 1.5.13 + '@redis/time-series@5.8.0(@redis/client@5.8.0)': + dependencies: + '@redis/client': 5.8.0 + '@repeaterjs/repeater@3.0.5': {} '@rollup/pluginutils@4.2.1': @@ -20539,9 +20614,9 @@ snapshots: dependencies: '@internationalized/date': 3.5.2 - '@zag-js/date-utils@0.20.0(@internationalized/date@3.5.2)': + '@zag-js/date-utils@0.19.1(@internationalized/date@3.5.6)': dependencies: - '@internationalized/date': 3.5.2 + '@internationalized/date': 3.5.6 '@zag-js/date-utils@0.20.0(@internationalized/date@3.5.6)': dependencies: @@ -27997,6 +28072,14 @@ snapshots: '@redis/search': 1.1.6(@redis/client@1.5.13) '@redis/time-series': 1.0.5(@redis/client@1.5.13) + redis@5.8.0: + dependencies: + '@redis/bloom': 5.8.0(@redis/client@5.8.0) + '@redis/client': 5.8.0 + '@redis/json': 5.8.0(@redis/client@5.8.0) + '@redis/search': 5.8.0(@redis/client@5.8.0) + '@redis/time-series': 5.8.0(@redis/client@5.8.0) + reflect-metadata@0.1.13: {} reflect-metadata@0.1.14: {} @@ -30013,6 +30096,8 @@ snapshots: uuid@10.0.0: optional: true + uuid@11.1.0: {} + uuid@3.4.0: {} uuid@8.3.2: {}