From 439f0ae20be07528ff130e19dee0f8d698d06ecd Mon Sep 17 00:00:00 2001
From: Leiv Fredrik Berge
Date: Thu, 14 Aug 2025 12:49:24 +0200
Subject: [PATCH 1/4] feat: add nats kv adapter
---
.github/pr-labeler.yml | 1 +
.github/workflows/release.yml | 1 +
docs/public/img/adapters/nats.svg | 1 +
packages/adapter-nats-kv/README.md | 28 ++
packages/adapter-nats-kv/docker-compose.yml | 6 +
packages/adapter-nats-kv/package.json | 56 ++++
packages/adapter-nats-kv/src/index.ts | 323 ++++++++++++++++++++
packages/adapter-nats-kv/test/index.test.ts | 112 +++++++
packages/adapter-nats-kv/test/test.sh | 16 +
packages/adapter-nats-kv/tsconfig.json | 9 +
packages/adapter-nats-kv/typedoc.config.cjs | 14 +
pnpm-lock.yaml | 119 ++++++--
12 files changed, 665 insertions(+), 21 deletions(-)
create mode 100644 docs/public/img/adapters/nats.svg
create mode 100644 packages/adapter-nats-kv/README.md
create mode 100644 packages/adapter-nats-kv/docker-compose.yml
create mode 100644 packages/adapter-nats-kv/package.json
create mode 100644 packages/adapter-nats-kv/src/index.ts
create mode 100644 packages/adapter-nats-kv/test/index.test.ts
create mode 100755 packages/adapter-nats-kv/test/test.sh
create mode 100644 packages/adapter-nats-kv/tsconfig.json
create mode 100644 packages/adapter-nats-kv/typedoc.config.cjs
diff --git a/.github/pr-labeler.yml b/.github/pr-labeler.yml
index 89af700064..f99f2741ca 100644
--- a/.github/pr-labeler.yml
+++ b/.github/pr-labeler.yml
@@ -16,6 +16,7 @@ hasura: ["packages/adapter-hasura/**/*"]
frameworks: ["packages/frameworks-*/**/*"]
mikro-orm: ["packages/adapter-mikro-orm/**/*"]
mongodb: ["packages/adapter-mongodb/**/*"]
+nats-kv: ["packages/adapter-nats-kv/**/*"]
neo4j: ["packages/adapter-neo4j/**/*"]
next-auth: ["packages/next-auth/**/*"]
pg: ["packages/adapter-pg/**/*"]
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 1ee78afa19..70e630e2e3 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -34,6 +34,7 @@ on:
- "@auth/kysely-adapter"
- "@auth/mikro-orm-adapter"
- "@auth/mongodb-adapter"
+ - "@auth/nats-kv-adapter"
- "@auth/neo4j-adapter"
- "@auth/pg-adapter"
- "@auth/pouchdb-adapter"
diff --git a/docs/public/img/adapters/nats.svg b/docs/public/img/adapters/nats.svg
new file mode 100644
index 0000000000..4a44b41a78
--- /dev/null
+++ b/docs/public/img/adapters/nats.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/adapter-nats-kv/README.md b/packages/adapter-nats-kv/README.md
new file mode 100644
index 0000000000..10a8211f86
--- /dev/null
+++ b/packages/adapter-nats-kv/README.md
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
NATS KeyValue-store Adapter - NextAuth.js / Auth.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+---
+
+Check out the documentation at [authjs.dev](https://authjs.dev/reference/adapter/nats-kv).
diff --git a/packages/adapter-nats-kv/docker-compose.yml b/packages/adapter-nats-kv/docker-compose.yml
new file mode 100644
index 0000000000..c605bca47a
--- /dev/null
+++ b/packages/adapter-nats-kv/docker-compose.yml
@@ -0,0 +1,6 @@
+services:
+ nats:
+ image: nats:latest
+ command: -js -p 5222
+ ports:
+ - "5222:5222"
diff --git a/packages/adapter-nats-kv/package.json b/packages/adapter-nats-kv/package.json
new file mode 100644
index 0000000000..2e19b2937d
--- /dev/null
+++ b/packages/adapter-nats-kv/package.json
@@ -0,0 +1,56 @@
+{
+ "name": "@auth/nats-kv-adapter",
+ "version": "1.0.0",
+ "description": "NATS KeyValue-store 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": "Team Kjøttkontroll @ Mattilsynet",
+ "contributors": [
+ "github.com/eljarh",
+ "github.com/lfbergee"
+ ],
+ "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",
+ "NATS",
+ "nats.io"
+ ],
+ "private": false,
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "test": "./test/test.sh",
+ "build": "tsc",
+ "clean": "rm -rf *.js *.d.ts*"
+ },
+ "dependencies": {
+ "@auth/core": "workspace:*"
+ },
+ "peerDependencies": {
+ "@nats-io/kv": "^3.1.0",
+ "@nats-io/transport-node": "^3.1.0"
+ },
+ "devDependencies": {
+ "@types/uuid": "^8.3.3",
+ "dotenv": "^17.0.0"
+ }
+}
diff --git a/packages/adapter-nats-kv/src/index.ts b/packages/adapter-nats-kv/src/index.ts
new file mode 100644
index 0000000000..29d2df14e5
--- /dev/null
+++ b/packages/adapter-nats-kv/src/index.ts
@@ -0,0 +1,323 @@
+/**
+ *
+ *
+ * ## Installation
+ *
+ * ```bash npm2yarn
+ * npm install @nats-io/kv @nats-io/transport-node @auth/nats-kv-adapter
+ * ```
+ *
+ * @module @auth/nats-kv-adapter
+ */
+import {
+ type Adapter,
+ type AdapterUser,
+ type AdapterAccount,
+ type AdapterSession,
+ type VerificationToken,
+ isDate,
+} from "@auth/core/adapters"
+import { KV } from "@nats-io/kv"
+
+/**
+ *
+ * You can either use this with Symbol.asyncDispose or handle the disposal yourself.
+ * https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management
+ * If you do choose asyncDispose, make sure you environment is configured to handled that
+ * by targeting at least es2022 and the `lib` option to include `esnext` or `esnext.disposable`,
+ * or by providing a polyfill. Using this pattern the adapter will call the cleanup function when the adapter is after NATS operations.
+ *
+ * You can instead provide the adapter with a KV instance, and handle the connection and disposal yourself.
+ *
+ * Usage:
+ * const kvm = new Kvm(client);
+ * const authKV = await kvm.create("authKV");
+ * export const { handlers, auth, signIn, signOut } = NextAuth({
+ * adapter: NatsKVAdapter(authKV),
+ * providers: [],
+ * })
+ */
+
+/** This is the interface of the Nats KV adapter options. */
+export interface NatsKVAdapterOptions {
+ /**
+ * The base prefix for your keys
+ */
+ baseKeyPrefix?: string
+ /**
+ * The prefix for the `account` key
+ */
+ accountKeyPrefix?: string
+ /**
+ * The prefix for the `accountByUserId` key
+ */
+ accountByUserIdPrefix?: string
+ /**
+ * The prefix for the `emailKey` key
+ */
+ emailKeyPrefix?: string
+ /**
+ * The prefix for the `sessionKey` key
+ */
+ sessionKeyPrefix?: string
+ /**
+ * The prefix for the `sessionByUserId` key
+ */
+ sessionByUserIdKeyPrefix?: string
+ /**
+ * The prefix for the `user` key
+ */
+ userKeyPrefix?: string
+ /**
+ * The prefix for the `verificationToken` key
+ */
+ verificationTokenKeyPrefix?: string
+}
+
+export const defaultOptions = {
+ 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.",
+}
+
+export function 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)
+}
+
+// replace symbols that are not allowed in keys
+export function natsKey(identifier: string) {
+ return identifier
+ .replace(/@/g, "_at_")
+ .replace(/:/g, "_colon_")
+ .replace(/ /g, "_") as string
+}
+
+export function nats2json(value: any) {
+ return JSON.parse(value.toString())
+}
+
+export function NatsKVAdapter(
+ natsConnect:
+ | (() => Promise<
+ { kv: KV } & {
+ [Symbol.asyncDispose]: () => Promise
+ }
+ >)
+ | KV,
+ options: NatsKVAdapterOptions = {}
+): Adapter {
+ const [disposableConnection, kv] =
+ typeof natsConnect === "function"
+ ? [natsConnect, null]
+ : [null, natsConnect]
+
+ const mergedOptions = {
+ ...defaultOptions,
+ ...options,
+ }
+
+ const { baseKeyPrefix } = mergedOptions
+ const accountKeyPrefix = baseKeyPrefix + mergedOptions.accountKeyPrefix
+ const accountByUserIdPrefix =
+ baseKeyPrefix + mergedOptions.accountByUserIdPrefix
+ const emailKeyPrefix = baseKeyPrefix + mergedOptions.emailKeyPrefix
+ const sessionKeyPrefix = baseKeyPrefix + mergedOptions.sessionKeyPrefix
+ const sessionByUserIdKeyPrefix =
+ baseKeyPrefix + mergedOptions.sessionByUserIdKeyPrefix
+ const userKeyPrefix = baseKeyPrefix + mergedOptions.userKeyPrefix
+ const verificationTokenKeyPrefix =
+ baseKeyPrefix + mergedOptions.verificationTokenKeyPrefix
+
+ const natsPutJson = async (key: string, obj: any) => {
+ return await natsPut(key, JSON.stringify(obj))
+ }
+
+ const natsPut = async (key: string, obj: any) => {
+ if (disposableConnection) {
+ await using nc = await disposableConnection()
+ return await nc.kv.put(key, obj)
+ } else {
+ return await kv.put(key, obj)
+ }
+ }
+
+ const natsPurge = async (key: string) => {
+ if (disposableConnection) {
+ await using nc = await disposableConnection()
+ return await nc.kv.purge(key)
+ } else {
+ return await kv.purge(key)
+ }
+ }
+
+ const natsGet = async (key: string) => {
+ if (disposableConnection) {
+ await using nc = await disposableConnection()
+ return await nc.kv.get(key)
+ } else {
+ return await kv.get(key)
+ }
+ }
+
+ const setAccount = async (id: string, account: AdapterAccount) => {
+ const accountKey = accountKeyPrefix + natsKey(id)
+ await natsPutJson(accountKey, account)
+ await natsPut(accountByUserIdPrefix + natsKey(account.userId), accountKey)
+ return account
+ }
+
+ const getAccount = async (id: string) => {
+ const data = await natsGet(accountKeyPrefix + natsKey(id))
+ if (!data || data.length == 0) return null
+ const account = data.json()
+ return hydrateDates(account)
+ }
+
+ const setSession = async (
+ id: string,
+ session: AdapterSession
+ ): Promise => {
+ const sessionKey = sessionKeyPrefix + natsKey(id)
+ await natsPutJson(sessionKey, session)
+ await natsPut(
+ sessionByUserIdKeyPrefix + natsKey(session.userId),
+ sessionKey
+ )
+ return session
+ }
+
+ const getSession = async (id: string) => {
+ const data = await natsGet(sessionKeyPrefix + natsKey(id))
+ if (!data || data.length == 0) return null
+ const session = data.json()
+ return hydrateDates(session)
+ }
+
+ const setUser = async (
+ id: string,
+ user: AdapterUser
+ ): Promise => {
+ await natsPutJson(userKeyPrefix + natsKey(id), user)
+ await natsPut(`${emailKeyPrefix}${natsKey(user.email)}`, id)
+ return user
+ }
+
+ const getUser = async (id: string) => {
+ const data = await natsGet(userKeyPrefix + natsKey(id))
+ if (!data || data.length == 0) return null
+ const user = data.json()
+
+ return hydrateDates(user)
+ }
+
+ return {
+ async createUser(user) {
+ const id = crypto.randomUUID()
+ return await setUser(id, { ...user, id })
+ },
+ getUser,
+ async getUserByEmail(email) {
+ const data = await natsGet(emailKeyPrefix + natsKey(email))
+
+ if (!data || data.length == 0) return null
+ const userId = data.string()
+ return await getUser(userId)
+ },
+ async getUserByAccount(account) {
+ const dbAccount = await getAccount(
+ `${account.provider}.${account.providerAccountId}`
+ )
+ if (!dbAccount) return null
+ return await getUser(dbAccount.userId)
+ },
+ async updateUser(updates) {
+ const userId = updates.id as string
+ const user = await getUser(userId)
+ return await setUser(userId, { ...(user as AdapterUser), ...updates })
+ },
+ async linkAccount(account) {
+ const id = `${account.provider}.${account.providerAccountId}`
+ return await setAccount(id, { ...account, id })
+ },
+ createSession: (session) => setSession(session.sessionToken, session),
+ async getSessionAndUser(sessionToken) {
+ const session = await getSession(sessionToken)
+ if (!session) return null
+ const user = await getUser(session.userId)
+ if (!user) return null
+ return { session, user }
+ },
+ async updateSession(updates) {
+ const session = await getSession(updates.sessionToken)
+ if (!session) return null
+ return await setSession(updates.sessionToken, { ...session, ...updates })
+ },
+ async deleteSession(sessionToken) {
+ await natsPurge(sessionKeyPrefix + sessionToken)
+ },
+ async createVerificationToken(verificationToken) {
+ await natsPutJson(
+ verificationTokenKeyPrefix +
+ natsKey(verificationToken.identifier) +
+ "." +
+ natsKey(verificationToken.token),
+ verificationToken
+ )
+ return verificationToken
+ },
+ async useVerificationToken(verificationToken) {
+ const tokenKey =
+ verificationTokenKeyPrefix +
+ natsKey(verificationToken.identifier) +
+ "." +
+ natsKey(verificationToken.token)
+ const data = await natsGet(tokenKey)
+ if (!data || data.length == 0) return null
+ const token = data.json()
+ await natsPurge(tokenKey)
+ return hydrateDates(token)
+ // return reviveFromJson(token)
+ },
+ async unlinkAccount(account) {
+ const id = `${account.provider}.${account.providerAccountId}`
+ const dbAccount = await getAccount(natsKey(id))
+ if (!dbAccount) return
+ const accountKey = `${accountKeyPrefix}${natsKey(id)}`
+ await natsPurge(accountKey)
+ await natsPurge(`${(accountByUserIdPrefix + dbAccount.userId) as string}`)
+ },
+ async deleteUser(userId) {
+ const user = await getUser(natsKey(userId))
+ if (!user) return
+ const accountByUserKey = accountByUserIdPrefix + natsKey(userId)
+ const accountKey = await natsGet(accountByUserKey).then((data) =>
+ data?.string()
+ )
+ const sessionByUserIdKey = sessionByUserIdKeyPrefix + natsKey(userId)
+ const sessionKey = await natsGet(sessionByUserIdKey).then((data) =>
+ data?.string()
+ )
+ await Promise.all([
+ natsPurge(userKeyPrefix + natsKey(userId)),
+ natsPurge(`${emailKeyPrefix}${natsKey(user.email)}`),
+ natsPurge(accountKey as string),
+ natsPurge(accountByUserKey),
+ natsPurge(sessionKey as string),
+ natsPurge(sessionByUserIdKey),
+ ])
+ },
+ }
+}
diff --git a/packages/adapter-nats-kv/test/index.test.ts b/packages/adapter-nats-kv/test/index.test.ts
new file mode 100644
index 0000000000..b548b05213
--- /dev/null
+++ b/packages/adapter-nats-kv/test/index.test.ts
@@ -0,0 +1,112 @@
+import { runBasicTests } from "../../utils/adapter"
+import { hydrateDates, NatsKVAdapter, natsKey } from "../src"
+import "dotenv/config"
+import { connect } from "@nats-io/transport-node"
+import { Kvm, KV } from "@nats-io/kv"
+
+// This functions allows us to open a connection without having to worry about closing it
+async function getNextAuthKVandCloseConnection(): Promise<
+ { kv: KV } & {
+ [Symbol.asyncDispose]: () => Promise
+ }
+> {
+ const nc = await connect({
+ servers: "nats://localhost:5222",
+ authenticator: undefined,
+ })
+ const kvm = new Kvm(nc)
+ const kv = await kvm.create("authKV")
+
+ return {
+ kv: kv,
+ [Symbol.asyncDispose]: async () => {
+ await nc.drain()
+ await nc.close()
+ },
+ }
+}
+
+await runBasicTests({
+ adapter: NatsKVAdapter(getNextAuthKVandCloseConnection, {
+ baseKeyPrefix: "testApp.",
+ }),
+ db: {
+ disconnect: async () => {
+ //do nothing - since the connection itself handles this (was: await nc.close())
+ },
+ async account({ provider, providerAccountId }) {
+ await using nc = await getNextAuthKVandCloseConnection()
+ const data = await nc.kv.get(
+ `testApp.user.account.${natsKey(provider)}.${natsKey(providerAccountId)}`
+ )
+ if (!data || data.length == 0) return null
+ return hydrateDates(data.json())
+ },
+ async user(id: string) {
+ await using nc = await getNextAuthKVandCloseConnection()
+ const data = await nc.kv.get(`testApp.user.${natsKey(id)}`)
+ if (!data || data.length == 0) return null
+ return hydrateDates(data.json())
+ },
+ async session(sessionToken) {
+ await using nc = await getNextAuthKVandCloseConnection()
+ const data = await nc.kv.get(
+ `testApp.user.session.${natsKey(sessionToken)}`
+ )
+ if (!data || data.length == 0) return null
+ return hydrateDates(data.json())
+ },
+ async verificationToken(where) {
+ await using nc = await getNextAuthKVandCloseConnection()
+ const data = await nc.kv.get(
+ `testApp.user.token.${natsKey(where.identifier)}.${natsKey(where.token)}`
+ )
+ if (!data || data.length == 0) return null
+ return hydrateDates(data.json())
+ },
+ },
+})
+
+// Running the same tests again with a static KV natsconnection
+const nc = await connect({
+ servers: "nats://localhost:5222",
+ authenticator: undefined,
+})
+const kvm = new Kvm(nc)
+const kv = await kvm.open("authKV")
+
+await runBasicTests({
+ adapter: NatsKVAdapter(kv, {
+ baseKeyPrefix: "testApp.",
+ }),
+ db: {
+ disconnect: async () => {
+ await nc.drain()
+ await nc.close()
+ },
+ async account({ provider, providerAccountId }) {
+ const data = await kv.get(
+ `testApp.user.account.${natsKey(provider)}.${natsKey(providerAccountId)}`
+ )
+ if (!data || data.length == 0) return null
+ return hydrateDates(data.json())
+ },
+ async user(id: string) {
+ const data = await kv.get(`testApp.user.${natsKey(id)}`)
+ if (!data || data.length == 0) return null
+ return hydrateDates(data.json())
+ },
+ async session(sessionToken) {
+ const data = await kv.get(`testApp.user.session.${natsKey(sessionToken)}`)
+ if (!data || data.length == 0) return null
+ return hydrateDates(data.json())
+ },
+ async verificationToken(where) {
+ const data = await kv.get(
+ `testApp.user.token.${natsKey(where.identifier)}.${natsKey(where.token)}`
+ )
+ if (!data || data.length == 0) return null
+ return hydrateDates(data.json())
+ },
+ },
+})
diff --git a/packages/adapter-nats-kv/test/test.sh b/packages/adapter-nats-kv/test/test.sh
new file mode 100755
index 0000000000..6d2c479ec5
--- /dev/null
+++ b/packages/adapter-nats-kv/test/test.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+echo "Initializing container for NATS KV (nats:latest)..."
+
+# Init Redis + serverless-redis-http containers
+docker compose up -d
+
+echo "Waiting 5s for nats 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-nats-kv/tsconfig.json b/packages/adapter-nats-kv/tsconfig.json
new file mode 100644
index 0000000000..6f3b51d36f
--- /dev/null
+++ b/packages/adapter-nats-kv/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-nats-kv/typedoc.config.cjs b/packages/adapter-nats-kv/typedoc.config.cjs
new file mode 100644
index 0000000000..f2aa735adc
--- /dev/null
+++ b/packages/adapter-nats-kv/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/nats-kv-adapter",
+ entryFileName: "../nats-kv-adapter.mdx",
+ includeVersion: true,
+ readme: 'none',
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f6fe6e4264..4bfef91981 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)
@@ -498,6 +498,25 @@ importers:
specifier: ^6.0.0
version: 6.9.0(@aws-sdk/credential-providers@3.499.0)(gcp-metadata@5.3.0)(socks@2.7.1)
+ packages/adapter-nats-kv:
+ dependencies:
+ '@auth/core':
+ specifier: workspace:*
+ version: link:../core
+ '@nats-io/kv':
+ specifier: ^3.1.0
+ version: 3.1.0
+ '@nats-io/transport-node':
+ specifier: ^3.1.0
+ version: 3.1.0
+ devDependencies:
+ '@types/uuid':
+ specifier: ^8.3.3
+ version: 8.3.4
+ dotenv:
+ specifier: ^17.0.0
+ version: 17.2.1
+
packages/adapter-neo4j:
dependencies:
'@auth/core':
@@ -3843,6 +3862,27 @@ packages:
resolution: {integrity: sha512-C5wRPw9waqL2jk3jEDeJv+f7ScuO3N0a39HVdyFLkwKxHH4Sya4ZbzZsu2JLi6eEqe7RuHipHL6mC7B2OfYZZw==}
engines: {node: '>= 10'}
+ '@nats-io/jetstream@3.1.0':
+ resolution: {integrity: sha512-L+IEqEo2Bb8533tGNCsfsFW1kArYrQIkq3YMz8KDzBXJUjH3e5pFNaL2j7xeN/klToYFFLmuhsM6FwRYarkR0w==}
+
+ '@nats-io/kv@3.1.0':
+ resolution: {integrity: sha512-PqtJMF8vKqnnDngN8ITYSChSchUHGdFZOO6pV3lxSS/SxXgvhaJ8FTAoC/70mSZNOmHu3ISB5SbH86gyQIugXQ==}
+
+ '@nats-io/nats-core@3.1.0':
+ resolution: {integrity: sha512-xsSkLEGGcqNF+Ru8dMjPmKtfbBeq/U4meuJJX4Zi+5TBHpjpjNjs4YkCBC/pGYWnEum1/vdNPizjE1RdNHCyBg==}
+
+ '@nats-io/nkeys@2.0.3':
+ resolution: {integrity: sha512-JVt56GuE6Z89KUkI4TXUbSI9fmIfAmk6PMPknijmuL72GcD+UgIomTcRWiNvvJKxA01sBbmIPStqJs5cMRBC3A==}
+ engines: {node: '>=18.0.0'}
+
+ '@nats-io/nuid@2.0.3':
+ resolution: {integrity: sha512-TpA3HEBna/qMVudy+3HZr5M3mo/L1JPofpVT4t0HkFGkz2Cn9wrlrQC8tvR8Md5Oa9//GtGG26eN0qEWF5Vqew==}
+ engines: {node: '>= 18.x'}
+
+ '@nats-io/transport-node@3.1.0':
+ resolution: {integrity: sha512-k5pH7IOKUetwXOMraVgcB5zG0wibcHOwJJuyuY1/5Q4K0XfBJDnb/IbczP5/JJWwMYfxSL9O+46ojtdBHvHRSw==}
+ engines: {node: '>= 18.0.0'}
+
'@neon-rs/load@0.0.4':
resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==}
@@ -7593,6 +7633,10 @@ packages:
resolution: {integrity: sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==}
engines: {node: '>=12'}
+ dotenv@17.2.1:
+ resolution: {integrity: sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==}
+ engines: {node: '>=12'}
+
dotenv@8.6.0:
resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==}
engines: {node: '>=10'}
@@ -12951,6 +12995,9 @@ packages:
resolution: {integrity: sha512-u9gUDkmR9dFS8b5kAYqIETK4OnzsS4l2ragJ0+soSMHh6VEeNHjTfSjk1tKxCqLyziCrPogadxP680J+v6yGHw==}
hasBin: true
+ tweetnacl@1.0.3:
+ resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==}
+
twoslash-protocol@0.2.5:
resolution: {integrity: sha512-oUr5ZAn37CgNa6p1mrCuuR/pINffsnGCee2aS170Uj1IObxCjsHzu6sgdPUdxGLLn6++gd/qjNH1/iR6RrfLeg==}
@@ -14213,7 +14260,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 +14271,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 +14297,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 +14309,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 +17188,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 +17204,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 +17214,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 +17232,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
@@ -17710,6 +17757,32 @@ snapshots:
'@napi-rs/simple-git-win32-arm64-msvc': 0.1.16
'@napi-rs/simple-git-win32-x64-msvc': 0.1.16
+ '@nats-io/jetstream@3.1.0':
+ dependencies:
+ '@nats-io/nats-core': 3.1.0
+
+ '@nats-io/kv@3.1.0':
+ dependencies:
+ '@nats-io/jetstream': 3.1.0
+ '@nats-io/nats-core': 3.1.0
+
+ '@nats-io/nats-core@3.1.0':
+ dependencies:
+ '@nats-io/nkeys': 2.0.3
+ '@nats-io/nuid': 2.0.3
+
+ '@nats-io/nkeys@2.0.3':
+ dependencies:
+ tweetnacl: 1.0.3
+
+ '@nats-io/nuid@2.0.3': {}
+
+ '@nats-io/transport-node@3.1.0':
+ dependencies:
+ '@nats-io/nats-core': 3.1.0
+ '@nats-io/nkeys': 2.0.3
+ '@nats-io/nuid': 2.0.3
+
'@neon-rs/load@0.0.4': {}
'@neondatabase/serverless@0.10.4':
@@ -20539,9 +20612,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:
@@ -22715,6 +22788,8 @@ snapshots:
dotenv@16.4.1: {}
+ dotenv@17.2.1: {}
+
dotenv@8.6.0: {}
dottie@2.0.6: {}
@@ -29575,6 +29650,8 @@ snapshots:
turbo-windows-64: 2.1.1
turbo-windows-arm64: 2.1.1
+ tweetnacl@1.0.3: {}
+
twoslash-protocol@0.2.5: {}
twoslash@0.2.5(typescript@5.6.3):
From ed8b98f0dcad706979010b81683768e877cbe05e Mon Sep 17 00:00:00 2001
From: Leiv Fredrik Berge
Date: Thu, 14 Aug 2025 13:13:59 +0200
Subject: [PATCH 2/4] feat: add nats kv adapter
---
docs/pages/getting-started/adapters/_meta.js | 1 +
docs/pages/getting-started/adapters/nats.mdx | 319 +++++++++++++++++++
turbo.json | 1 +
3 files changed, 321 insertions(+)
create mode 100644 docs/pages/getting-started/adapters/nats.mdx
diff --git a/docs/pages/getting-started/adapters/_meta.js b/docs/pages/getting-started/adapters/_meta.js
index 153672f783..0dfd9b515a 100644
--- a/docs/pages/getting-started/adapters/_meta.js
+++ b/docs/pages/getting-started/adapters/_meta.js
@@ -11,6 +11,7 @@ export default {
kysely: "Kysely",
"mikro-orm": "MikroORM",
mongodb: "MongoDB",
+ "nats-kv": "NATS KV",
neo4j: "Neo4j",
neon: "Neon",
pg: "PostgreSQL",
diff --git a/docs/pages/getting-started/adapters/nats.mdx b/docs/pages/getting-started/adapters/nats.mdx
new file mode 100644
index 0000000000..a6f0563e34
--- /dev/null
+++ b/docs/pages/getting-started/adapters/nats.mdx
@@ -0,0 +1,319 @@
+import { Code } from "@/components/Code"
+
+
+
+# NATS KV Adapter
+
+## Resources
+
+- [NATS documentation](https://docs.nats.io/)
+
+## Setup
+
+### Installation
+
+```bash npm2yarn
+npm install @nats-io/transport-node @nats-io/kv @auth/nats-kv-adapter
+```
+
+### Environment Variables
+
+```sh
+NATS_SERVERS,
+NATS_CREDS
+```
+
+### Configuration
+
+You can either use this with Symbol.asyncDispose or handle the disposal yourself.
+
+#### With explicit resource management
+
+If you do choose asyncDispose, make sure you environment is configured to handled that by targeting at least es2022 and the `lib` option to include `esnext` or `esnext.disposable`, or by providing a polyfill. Using this pattern the adapter will call the cleanup function when the adapter is after NATS operations. [https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management)
+
+
+
+
+```ts filename="./auth.ts"
+import NextAuth from "next-auth"
+import { NatsKVAdapter } from "@auth/nats-kv-adapter"
+import { connect } from "@nats-io/transport-node"
+import { Kvm, KV } from "@nats-io/kv"
+
+async function getNats(): Promise<
+ { kv: KV } & {
+ [Symbol.asyncDispose]: () => Promise
+ }
+> {
+ const nc = await connect({
+ servers: process.env.NATS_SERVERS,
+ authenticator: process.env.NATS_CREDS,
+ })
+ const kvm = new Kvm(nc)
+ const kv = await kvm.create("name-of-auth-bucket")
+
+ return {
+ kv: kv,
+ [Symbol.asyncDispose]: async () => {
+ await nc.drain()
+ await nc.close()
+ },
+ }
+}
+
+export const { handlers, auth, signIn, signOut } = NextAuth({
+ adapter: NatsKVAdapter(getNats),
+ providers: [],
+})
+```
+
+
+
+
+```ts filename="/src/routes/plugin@auth.ts"
+import { QwikAuth$ } from "@auth/qwik"
+import { NatsKVAdapter } from "@auth/nats-kv-adapter"
+import { connect } from "@nats-io/transport-node"
+import { Kvm, KV } from "@nats-io/kv"
+
+async function getNats(): Promise<
+ { kv: KV } & {
+ [Symbol.asyncDispose]: () => Promise
+ }
+> {
+ const nc = await connect({
+ servers: process.env.NATS_SERVERS,
+ authenticator: process.env.NATS_CREDS,
+ })
+ const kvm = new Kvm(nc)
+ const kv = await kvm.create("name-of-auth-bucket")
+
+ return {
+ kv: kv,
+ [Symbol.asyncDispose]: async () => {
+ await nc.drain()
+ await nc.close()
+ },
+ }
+}
+
+export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
+ () => ({
+ providers: [],
+ adapter: NatsKVAdapter(getNats),
+ })
+)
+```
+
+
+
+
+```ts filename="./src/auth.ts"
+import { SvelteKitAuth } from "@auth/sveltekit"
+import { NatsKVAdapter } from "@auth/nats-kv-adapter"
+import { connect } from "@nats-io/transport-node"
+import { Kvm, KV } from "@nats-io/kv"
+
+async function getNats(): Promise<
+ { kv: KV } & {
+ [Symbol.asyncDispose]: () => Promise
+ }
+> {
+ const nc = await connect({
+ servers: process.env.NATS_SERVERS,
+ authenticator: process.env.NATS_CREDS,
+ })
+ const kvm = new Kvm(nc)
+ const kv = await kvm.create("name-of-auth-bucket")
+
+ return {
+ kv: kv,
+ [Symbol.asyncDispose]: async () => {
+ await nc.drain()
+ await nc.close()
+ },
+ }
+}
+
+export const { handle, signIn, signOut } = SvelteKitAuth({
+ adapter: NatsKVAdapter(getNats),
+ providers: [],
+})
+```
+
+
+
+
+```ts filename="./src/routes/auth.route.ts"
+import { ExpressAuth } from "@auth/express"
+import { NatsKVAdapter } from "@auth/nats-kv-adapter"
+import { connect } from "@nats-io/transport-node"
+import { Kvm, KV } from "@nats-io/kv"
+
+async function getNats(): Promise<
+ { kv: KV } & {
+ [Symbol.asyncDispose]: () => Promise
+ }
+> {
+ const nc = await connect({
+ servers: process.env.NATS_SERVERS,
+ authenticator: process.env.NATS_CREDS,
+ })
+ const kvm = new Kvm(nc)
+ const kv = await kvm.create("name-of-auth-bucket")
+
+ return {
+ kv: kv,
+ [Symbol.asyncDispose]: async () => {
+ await nc.drain()
+ await nc.close()
+ },
+ }
+}
+
+const app = express()
+
+app.set("trust proxy", true)
+app.use(
+ "/auth/*",
+ ExpressAuth({
+ providers: [],
+ adapter: NatsKVAdapter(getNats),
+ })
+)
+```
+
+
+
+
+#### Without explicit resource management
+
+You can instead provide the adapter with a KV instance, and handle the connection and disposal yourself. Useful if you want to keep the connection alive, or have explicit control over the connection.
+
+
+
+
+```ts filename="./auth.ts"
+import NextAuth from "next-auth"
+import { NatsKVAdapter } from "@auth/nats-kv-adapter"
+import { connect } from "@nats-io/transport-node"
+import { Kvm, KV } from "@nats-io/kv"
+
+const nc = await connect({
+ servers: process.env.NATS_SERVERS,
+ authenticator: process.env.NATS_CREDS,
+})
+const kvm = new Kvm(nc)
+const kv = await kvm.create("name-of-auth-bucket")
+
+export const { handlers, auth, signIn, signOut } = NextAuth({
+ adapter: NatsKVAdapter(kv),
+ providers: [],
+})
+```
+
+
+
+
+```ts filename="/src/routes/plugin@auth.ts"
+import { QwikAuth$ } from "@auth/qwik"
+import { NatsKVAdapter } from "@auth/nats-kv-adapter"
+import { connect } from "@nats-io/transport-node"
+import { Kvm, KV } from "@nats-io/kv"
+
+const nc = await connect({
+ servers: process.env.NATS_SERVERS,
+ authenticator: process.env.NATS_CREDS,
+})
+const kvm = new Kvm(nc)
+const kv = await kvm.create("name-of-auth-bucket")
+
+export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
+ () => ({
+ providers: [],
+ adapter: NatsKVAdapter(kv),
+ })
+)
+```
+
+
+
+
+```ts filename="./src/auth.ts"
+import { SvelteKitAuth } from "@auth/sveltekit"
+import { NatsKVAdapter } from "@auth/nats-kv-adapter"
+import { connect } from "@nats-io/transport-node"
+import { Kvm, KV } from "@nats-io/kv"
+
+const nc = await connect({
+ servers: process.env.NATS_SERVERS,
+ authenticator: process.env.NATS_CREDS,
+})
+const kvm = new Kvm(nc)
+const kv = await kvm.create("name-of-auth-bucket")
+
+export const { handle, signIn, signOut } = SvelteKitAuth({
+ adapter: NatsKVAdapter(kv),
+ providers: [],
+})
+```
+
+
+
+
+```ts filename="./src/routes/auth.route.ts"
+import { ExpressAuth } from "@auth/express"
+import { NatsKVAdapter } from "@auth/nats-kv-adapter"
+import { connect } from "@nats-io/transport-node"
+import { Kvm, KV } from "@nats-io/kv"
+
+const nc = await connect({
+ servers: process.env.NATS_SERVERS,
+ authenticator: process.env.NATS_CREDS,
+})
+const kvm = new Kvm(nc)
+const kv = await kvm.create("name-of-auth-bucket")
+
+const app = express()
+
+app.set("trust proxy", true)
+app.use(
+ "/auth/*",
+ ExpressAuth({
+ providers: [],
+ adapter: NatsKVAdapter(kv),
+ })
+)
+```
+
+
+
+
+### Advanced usage
+
+If you have multiple Auth.js connected apps using this instance, you need different key prefixes for every app.
+
+You can change the prefixes by passing an `options` object as the second argument to the adapter factory function.
+
+The default values for this object are:
+
+```ts
+const defaultOptions = {
+ 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:",
+}
+```
+
+Usually changing the `baseKeyPrefix` should be enough for this scenario, but for more custom setups, you can also change the prefixes of every single key.
+
+```ts
+export const { handlers, auth, signIn, signOut } = NextAuth({
+ adapter: NatsKVAdapter(kv, { baseKeyPrefix: "app2:" }),
+})
+```
diff --git a/turbo.json b/turbo.json
index 240427e652..105507fb29 100644
--- a/turbo.json
+++ b/turbo.json
@@ -105,6 +105,7 @@
"@auth/kysely-adapter#build",
"@auth/mikro-orm-adapter#build",
"@auth/mongodb-adapter#build",
+ "@auth/nats-kv-adapter#build",
"@auth/neo4j-adapter#build",
"@auth/pg-adapter#build",
"@auth/pouchdb-adapter#build",
From abb3073f2f46982d7de463a427ad64a11b88ec1a Mon Sep 17 00:00:00 2001
From: Leiv Fredrik Berge
Date: Thu, 14 Aug 2025 13:22:46 +0200
Subject: [PATCH 3/4] fix: remove commented code
---
packages/adapter-nats-kv/src/index.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/packages/adapter-nats-kv/src/index.ts b/packages/adapter-nats-kv/src/index.ts
index 29d2df14e5..8fc9759b17 100644
--- a/packages/adapter-nats-kv/src/index.ts
+++ b/packages/adapter-nats-kv/src/index.ts
@@ -289,7 +289,6 @@ export function NatsKVAdapter(
const token = data.json()
await natsPurge(tokenKey)
return hydrateDates(token)
- // return reviveFromJson(token)
},
async unlinkAccount(account) {
const id = `${account.provider}.${account.providerAccountId}`
From 094919b735403666e2862bf42b40b2fcc132ad30 Mon Sep 17 00:00:00 2001
From: Leiv Fredrik Berge
Date: Thu, 14 Aug 2025 13:29:15 +0200
Subject: [PATCH 4/4] fix: broken link
---
docs/pages/getting-started/adapters/_meta.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/getting-started/adapters/_meta.js b/docs/pages/getting-started/adapters/_meta.js
index 0dfd9b515a..a790aac41c 100644
--- a/docs/pages/getting-started/adapters/_meta.js
+++ b/docs/pages/getting-started/adapters/_meta.js
@@ -11,7 +11,7 @@ export default {
kysely: "Kysely",
"mikro-orm": "MikroORM",
mongodb: "MongoDB",
- "nats-kv": "NATS KV",
+ nats: "NATS KV",
neo4j: "Neo4j",
neon: "Neon",
pg: "PostgreSQL",