From 6433369259a489ce4272918d6aac8620b1607ed0 Mon Sep 17 00:00:00 2001 From: Ashwin-3cS Date: Fri, 27 Mar 2026 01:49:28 +0530 Subject: [PATCH 1/2] feat(noter): add server-issued challenge nonce for wallet auth replay protection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace client-generated auth messages with a server-issued one-time challenge nonce. The server generates a random nonce stored in a new walletChallenges table with a 5-minute TTL. The client signs the nonce, and the server deletes it on use — replaying a captured signature fails because the challenge no longer exists. --- apps/noter/package/feature/auth/api/input.ts | 2 +- apps/noter/package/feature/auth/api/route.ts | 67 ++++++++++++++++--- apps/noter/package/feature/auth/constant.ts | 2 +- .../package/feature/auth/hook/use-auth.ts | 3 +- .../package/feature/auth/lib/wallet-client.ts | 14 ---- .../feature/auth/ui/auth-button-group.tsx | 13 ++-- .../package/feature/auth/ui/wallet-button.tsx | 15 +++-- apps/noter/package/shared/db/schema.ts | 21 ++++++ 8 files changed, 98 insertions(+), 39 deletions(-) diff --git a/apps/noter/package/feature/auth/api/input.ts b/apps/noter/package/feature/auth/api/input.ts index 76d0620d..031eca55 100644 --- a/apps/noter/package/feature/auth/api/input.ts +++ b/apps/noter/package/feature/auth/api/input.ts @@ -79,7 +79,7 @@ export const connectWalletInput = z.object({ walletType: walletSessionInsertSchema.shape.walletType.pipe(z.enum(["slush"])), // Subset validation address: walletSessionInsertSchema.shape.walletAddress, // Maps to walletAddress in DB signature: walletSessionInsertSchema.shape.signature, - message: walletSessionInsertSchema.shape.signedMessage, // Maps to signedMessage in DB + challengeId: uuidv7Schema, // Server-issued challenge ID (replaces client message) }); export type ConnectWalletInput = z.infer; diff --git a/apps/noter/package/feature/auth/api/route.ts b/apps/noter/package/feature/auth/api/route.ts index 12b2e948..40615fc7 100644 --- a/apps/noter/package/feature/auth/api/route.ts +++ b/apps/noter/package/feature/auth/api/route.ts @@ -6,6 +6,7 @@ import { router, procedure } from "@/shared/lib/trpc/init"; import { TRPCError } from "@trpc/server"; import { verifyPersonalMessageSignature } from "@mysten/sui/verify"; +import { randomBytes } from "crypto"; import { uuidv7 } from "uuidv7"; import { initiateLoginInput, @@ -32,7 +33,7 @@ import { } from "../lib/zklogin-client"; import { OAUTH_PROVIDERS, OAUTH_SCOPES, AUTH_ERRORS } from "../constant"; import { buildOAuthUrl } from "../domain/zklogin"; -import { zkLoginSessions, walletSessions } from "@/shared/db/schema"; +import { zkLoginSessions, walletSessions, walletChallenges } from "@/shared/db/schema"; import { eq } from "drizzle-orm"; import * as authService from "../domain/service"; @@ -238,35 +239,82 @@ export const authRouter = router({ return { success: true }; }), + /** + * Get a one-time challenge nonce for wallet authentication + * The nonce must be signed by the wallet and returned via connectWallet + */ + getChallenge: procedure + .mutation(async ({ ctx }) => { + const challengeId = uuidv7(); + const nonce = randomBytes(32).toString("hex"); + const expiresAt = new Date(Date.now() + 5 * 60 * 1000); // 5 minutes + + await ctx.db.insert(walletChallenges).values({ + id: challengeId, + nonce, + expiresAt, + }); + + return { challengeId, nonce, expiresAt }; + }), + /** * Connect wallet - authenticate with Sui wallet (Slush, Sui Wallet) - * Verifies signature and creates session + * Verifies signature against a server-issued challenge nonce */ connectWallet: procedure .input(connectWalletInput) .mutation(async ({ ctx, input }) => { - const { walletType, address, signature, message } = input; + const { challengeId, walletType, address, signature } = input; try { - // Verify the wallet signature before creating a session + // 1. Fetch and validate challenge + const [challenge] = await ctx.db + .select() + .from(walletChallenges) + .where(eq(walletChallenges.id, challengeId)) + .limit(1); + + if (!challenge) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Challenge not found or already used", + }); + } + + if (challenge.expiresAt < new Date()) { + await ctx.db.delete(walletChallenges).where(eq(walletChallenges.id, challengeId)); + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "Challenge expired", + }); + } + + // 2. Consume challenge (delete before verify to prevent timing-based replay) + await ctx.db.delete(walletChallenges).where(eq(walletChallenges.id, challengeId)); + + // 3. Verify signature against server-issued nonce const signerAddress = await verifyPersonalMessageSignature( - new TextEncoder().encode(message), + new TextEncoder().encode(challenge.nonce), signature, ).catch(() => { throw new TRPCError({ code: "UNAUTHORIZED", message: "Invalid signature" }); }); if (signerAddress.toSuiAddress() !== address) { - throw new TRPCError({ code: "UNAUTHORIZED", message: "Signature does not match address" }); + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "Signature does not match address", + }); } - // Create or update user via service + // 4. Create or update user const user = await authService.upsertWalletUser(ctx.db, { address, walletType, }); - // Create wallet session + // 5. Create wallet session const sessionId = uuidv7(); const expiresAt = new Date(); expiresAt.setHours(expiresAt.getHours() + 24); // 24 hour session @@ -276,13 +324,12 @@ export const authRouter = router({ userId: user.id, walletAddress: address, walletType, - signedMessage: message, + signedMessage: challenge.nonce, signature, signedAt: new Date(), expiresAt, }); - // Return wallet session data (no ephemeral keys for wallet auth) return { user, sessionId, diff --git a/apps/noter/package/feature/auth/constant.ts b/apps/noter/package/feature/auth/constant.ts index ebd0d059..2def6e91 100644 --- a/apps/noter/package/feature/auth/constant.ts +++ b/apps/noter/package/feature/auth/constant.ts @@ -69,7 +69,7 @@ export const OAUTH_SCOPES = { export const STORAGE_KEYS = { ephemeralPrivateKey: "zklogin:ephemeral:private", ephemeralPublicKey: "zklogin:ephemeral:public", - sessionId: "zklogin:session:id", + sessionId: "auth:session:id", nonce: "zklogin:nonce", maxEpoch: "zklogin:maxEpoch", randomness: "zklogin:randomness", diff --git a/apps/noter/package/feature/auth/hook/use-auth.ts b/apps/noter/package/feature/auth/hook/use-auth.ts index 66611d57..fc7defd9 100644 --- a/apps/noter/package/feature/auth/hook/use-auth.ts +++ b/apps/noter/package/feature/auth/hook/use-auth.ts @@ -27,6 +27,7 @@ export function useAuth() { // tRPC mutations const initiateLoginMutation = trpc.auth.initiateLogin.useMutation(); const completeLoginMutation = trpc.auth.completeLogin.useMutation(); + const getChallengeMutation = trpc.auth.getChallenge.useMutation(); const connectWalletMutation = trpc.auth.connectWallet.useMutation(); const logoutMutation = trpc.auth.logout.useMutation(); @@ -141,7 +142,7 @@ export function useAuth() { walletType: "slush"; address: string; signature: string; - message: string; + challengeId: string; }) => { try { setLoading(true); diff --git a/apps/noter/package/feature/auth/lib/wallet-client.ts b/apps/noter/package/feature/auth/lib/wallet-client.ts index 87e3e086..67325407 100644 --- a/apps/noter/package/feature/auth/lib/wallet-client.ts +++ b/apps/noter/package/feature/auth/lib/wallet-client.ts @@ -180,17 +180,3 @@ export async function disconnectWallet(type: WalletType): Promise { } } -/** - * Generate authentication message - */ -export function generateAuthMessage(): string { - const timestamp = Date.now(); - const nonce = Math.random().toString(36).substring(7); - - return `Sign this message to authenticate with Noter - -Timestamp: ${timestamp} -Nonce: ${nonce} - -This will not trigger any blockchain transaction or cost any gas fees.`; -} diff --git a/apps/noter/package/feature/auth/ui/auth-button-group.tsx b/apps/noter/package/feature/auth/ui/auth-button-group.tsx index 113839b7..5257980e 100644 --- a/apps/noter/package/feature/auth/ui/auth-button-group.tsx +++ b/apps/noter/package/feature/auth/ui/auth-button-group.tsx @@ -20,10 +20,10 @@ import { WALLET_INSTALL_URLS, type WalletType } from "../constant"; import { useAuth } from "../hook/use-auth"; import { connectWallet, - generateAuthMessage, isWalletInstalled, signMessage, } from "../lib/wallet-client"; +import { trpc } from "@/shared/lib/trpc/client"; import { LoginButton } from "./login-button"; export function AuthButtonGroup() { @@ -31,6 +31,7 @@ export function AuthButtonGroup() { const [isWalletConnecting, setIsWalletConnecting] = useState(false); const [walletError, setWalletError] = useState(null); const { connectWalletAuth, isLoginPending } = useAuth(); + const getChallenge = trpc.auth.getChallenge.useMutation(); const slushInstalled = isWalletInstalled("slush"); @@ -50,18 +51,18 @@ export function AuthButtonGroup() { // 1. Connect to wallet const account = await connectWallet(walletType); - // 2. Generate message to sign - const message = generateAuthMessage(); + // 2. Get server-issued challenge nonce + const { challengeId, nonce } = await getChallenge.mutateAsync(); - // 3. Sign message - const { signature } = await signMessage(walletType, message, account); + // 3. Sign the challenge nonce + const { signature } = await signMessage(walletType, nonce, account); // 4. Authenticate with backend await connectWalletAuth({ walletType, address: account.address, signature, - message, + challengeId, }); } catch (err) { console.error(`[AuthButtonGroup] Failed to connect ${walletType}:`, err); diff --git a/apps/noter/package/feature/auth/ui/wallet-button.tsx b/apps/noter/package/feature/auth/ui/wallet-button.tsx index af4322fd..b115bc60 100644 --- a/apps/noter/package/feature/auth/ui/wallet-button.tsx +++ b/apps/noter/package/feature/auth/ui/wallet-button.tsx @@ -12,9 +12,9 @@ import { useState } from "react"; import { connectWallet, signMessage, - generateAuthMessage, isWalletInstalled, } from "../lib/wallet-client"; +import { trpc } from "@/shared/lib/trpc/client"; import { WALLET_NAMES, WALLET_INSTALL_URLS, type WalletType } from "../constant"; export type WalletButtonProps = { @@ -31,6 +31,7 @@ export function WalletButton({ size = "default", }: WalletButtonProps) { const { connectWalletAuth, isLoginPending } = useAuth(); + const getChallenge = trpc.auth.getChallenge.useMutation(); const [isConnecting, setIsConnecting] = useState(false); const [error, setError] = useState(null); @@ -44,17 +45,19 @@ export function WalletButton({ try { // 1. Connect to wallet const account = await connectWallet(wallet); - // 2. Generate message to sign - const message = generateAuthMessage(); - // 3. Sign message - const { signature } = await signMessage(wallet, message, account); + // 2. Get server-issued challenge nonce + const { challengeId, nonce } = await getChallenge.mutateAsync(); + + // 3. Sign the challenge nonce + const { signature } = await signMessage(wallet, nonce, account); + // 4. Authenticate with backend await connectWalletAuth({ walletType: wallet, address: account.address, signature, - message, + challengeId, }); } catch (err) { console.error(`[WalletButton] Failed to connect ${wallet}:`, err); setError(err instanceof Error ? err.message : "Connection failed"); diff --git a/apps/noter/package/shared/db/schema.ts b/apps/noter/package/shared/db/schema.ts index 07b5f876..6655798f 100644 --- a/apps/noter/package/shared/db/schema.ts +++ b/apps/noter/package/shared/db/schema.ts @@ -214,6 +214,27 @@ export const walletSessions = pgTable( (t) => [index().on(t.userId), index().on(t.expiresAt), index().on(t.walletAddress)] ); +// ════════════════════════════════════════════════════════════════ +// WALLET CHALLENGES (one-time nonces for replay protection) +// ════════════════════════════════════════════════════════════════ + +export const walletChallenges = pgTable( + "wallet_challenges", + { + id: uuid() + .primaryKey() + .$defaultFn(() => uuidv7()), + createdAt: timestamp().defaultNow().notNull(), + + // Server-generated random nonce + nonce: text().notNull(), + + // Challenge expiration (5 minutes) + expiresAt: timestamp().notNull(), + }, + (t) => [index().on(t.expiresAt)] +); + // ════════════════════════════════════════════════════════════════ // NOTES (Apple Notes / Notion-like) // ════════════════════════════════════════════════════════════════ From 23b2a058b440e0c4c99ed29f451f2358c22298cd Mon Sep 17 00:00:00 2001 From: Ashwin-3cS Date: Fri, 27 Mar 2026 13:17:32 +0530 Subject: [PATCH 2/2] fix(noter): address review feedback on wallet challenge flow - Use DELETE...RETURNING in connectWallet to eliminate TOCTOU race - Clean up expired challenges in getChallenge to prevent table bloat - Add Drizzle migration for walletChallenges table --- apps/noter/package.json | 10 +- apps/noter/package/feature/auth/api/route.ts | 20 +- .../migrations/0002_conscious_deathbird.sql | 8 + .../db/migrations/meta/0002_snapshot.json | 1103 +++++++++++++++++ .../shared/db/migrations/meta/_journal.json | 7 + pnpm-lock.yaml | 73 +- 6 files changed, 1181 insertions(+), 40 deletions(-) create mode 100644 apps/noter/package/shared/db/migrations/0002_conscious_deathbird.sql create mode 100644 apps/noter/package/shared/db/migrations/meta/0002_snapshot.json diff --git a/apps/noter/package.json b/apps/noter/package.json index 44e92fc0..cf29afd0 100644 --- a/apps/noter/package.json +++ b/apps/noter/package.json @@ -18,7 +18,6 @@ "@ai-sdk/openai": "^3.0.41", "@ai-sdk/react": "3.0.39", "@base-ui/react": "^1.2.0", - "@mysten-incubation/memwal": "workspace:*", "@hookform/resolvers": "^5.2.2", "@lexical/code": "^0.41.0", "@lexical/link": "^0.41.0", @@ -30,6 +29,7 @@ "@lexical/table": "^0.41.0", "@lexical/text": "^0.41.0", "@lexical/utils": "^0.41.0", + "@mysten-incubation/memwal": "workspace:*", "@mysten/sui": "^2.5.0", "@mysten/wallet-standard": "^0.15.0", "@mysten/zklogin": "^0.8.1", @@ -45,7 +45,9 @@ "drizzle-orm": "^0.45.1", "drizzle-zod": "^0.8.3", "embla-carousel-react": "^8.6.0", + "esbuild": "~0.27.4", "framer-motion": "^12.34.3", + "get-tsconfig": "^4.7.5", "input-otp": "^1.4.2", "jotai": "^2.18.0", "jwt-decode": "^4.0.0", @@ -68,9 +70,7 @@ "use-debounce": "^10.1.0", "uuidv7": "^1.1.0", "vaul": "^1.1.2", - "zod": "^4.3.6", - "esbuild": "~0.27.4", - "get-tsconfig": "^4.7.5" + "zod": "^4.3.6" }, "devDependencies": { "@tailwindcss/postcss": "^4", @@ -79,7 +79,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "dotenv": "^16.4.5", - "drizzle-kit": "^0.31.9", + "drizzle-kit": "^0.31.10", "eslint": "^9", "eslint-config-next": "16.1.6", "shadcn": "^3.8.5", diff --git a/apps/noter/package/feature/auth/api/route.ts b/apps/noter/package/feature/auth/api/route.ts index 40615fc7..7114ba88 100644 --- a/apps/noter/package/feature/auth/api/route.ts +++ b/apps/noter/package/feature/auth/api/route.ts @@ -34,7 +34,7 @@ import { import { OAUTH_PROVIDERS, OAUTH_SCOPES, AUTH_ERRORS } from "../constant"; import { buildOAuthUrl } from "../domain/zklogin"; import { zkLoginSessions, walletSessions, walletChallenges } from "@/shared/db/schema"; -import { eq } from "drizzle-orm"; +import { eq, lt } from "drizzle-orm"; import * as authService from "../domain/service"; export const authRouter = router({ @@ -245,6 +245,11 @@ export const authRouter = router({ */ getChallenge: procedure .mutation(async ({ ctx }) => { + // Clean up expired challenges to prevent table bloat + await ctx.db + .delete(walletChallenges) + .where(lt(walletChallenges.expiresAt, new Date())); + const challengeId = uuidv7(); const nonce = randomBytes(32).toString("hex"); const expiresAt = new Date(Date.now() + 5 * 60 * 1000); // 5 minutes @@ -268,12 +273,11 @@ export const authRouter = router({ const { challengeId, walletType, address, signature } = input; try { - // 1. Fetch and validate challenge + // 1. Atomically consume challenge const [challenge] = await ctx.db - .select() - .from(walletChallenges) + .delete(walletChallenges) .where(eq(walletChallenges.id, challengeId)) - .limit(1); + .returning(); if (!challenge) { throw new TRPCError({ @@ -283,17 +287,13 @@ export const authRouter = router({ } if (challenge.expiresAt < new Date()) { - await ctx.db.delete(walletChallenges).where(eq(walletChallenges.id, challengeId)); throw new TRPCError({ code: "UNAUTHORIZED", message: "Challenge expired", }); } - // 2. Consume challenge (delete before verify to prevent timing-based replay) - await ctx.db.delete(walletChallenges).where(eq(walletChallenges.id, challengeId)); - - // 3. Verify signature against server-issued nonce + // 2. Verify signature against server-issued nonce const signerAddress = await verifyPersonalMessageSignature( new TextEncoder().encode(challenge.nonce), signature, diff --git a/apps/noter/package/shared/db/migrations/0002_conscious_deathbird.sql b/apps/noter/package/shared/db/migrations/0002_conscious_deathbird.sql new file mode 100644 index 00000000..bb82e51f --- /dev/null +++ b/apps/noter/package/shared/db/migrations/0002_conscious_deathbird.sql @@ -0,0 +1,8 @@ +CREATE TABLE "wallet_challenges" ( + "id" uuid PRIMARY KEY NOT NULL, + "createdAt" timestamp DEFAULT now() NOT NULL, + "nonce" text NOT NULL, + "expiresAt" timestamp NOT NULL +); +--> statement-breakpoint +CREATE INDEX "wallet_challenges_expiresAt_index" ON "wallet_challenges" USING btree ("expiresAt"); \ No newline at end of file diff --git a/apps/noter/package/shared/db/migrations/meta/0002_snapshot.json b/apps/noter/package/shared/db/migrations/meta/0002_snapshot.json new file mode 100644 index 00000000..2c3c8066 --- /dev/null +++ b/apps/noter/package/shared/db/migrations/meta/0002_snapshot.json @@ -0,0 +1,1103 @@ +{ + "id": "c37d77ab-d057-4524-a8df-e5767fc16d94", + "prevId": "b297cb0c-43b8-4e9b-a620-348289847be8", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.chats": { + "name": "chats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'anthropic/claude-sonnet-4'" + }, + "systemPrompt": { + "name": "systemPrompt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "temperature": { + "name": "temperature", + "type": "real", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "chats_userId_index": { + "name": "chats_userId_index", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "chats_userId_createdAt_index": { + "name": "chats_userId_createdAt_index", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "createdAt", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chats_userId_users_id_fk": { + "name": "chats_userId_users_id_fk", + "tableFrom": "chats", + "tableTo": "users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages": { + "name": "messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "chatId": { + "name": "chatId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "message_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parts": { + "name": "parts", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "ai_message_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "promptTokens": { + "name": "promptTokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "completionTokens": { + "name": "completionTokens", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "agentRunId": { + "name": "agentRunId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "turnIndex": { + "name": "turnIndex", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "messages_chatId_index": { + "name": "messages_chatId_index", + "columns": [ + { + "expression": "chatId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_chatId_createdAt_index": { + "name": "messages_chatId_createdAt_index", + "columns": [ + { + "expression": "chatId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "createdAt", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "messages_agentRunId_index": { + "name": "messages_agentRunId_index", + "columns": [ + { + "expression": "agentRunId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "messages_chatId_chats_id_fk": { + "name": "messages_chatId_chats_id_fk", + "tableFrom": "messages", + "tableTo": "chats", + "columnsFrom": [ + "chatId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.note_memory_highlights": { + "name": "note_memory_highlights", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "highlightedText": { + "name": "highlightedText", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "highlightedHtml": { + "name": "highlightedHtml", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "startOffset": { + "name": "startOffset", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "endOffset": { + "name": "endOffset", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "extractedText": { + "name": "extractedText", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryTitle": { + "name": "memoryTitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryContent": { + "name": "memoryContent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entities": { + "name": "entities", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "relationships": { + "name": "relationships", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "memory_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'preparing'" + }, + "blobId": { + "name": "blobId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "graphObjectId": { + "name": "graphObjectId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "transactionId": { + "name": "transactionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "approvedAt": { + "name": "approvedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "savedAt": { + "name": "savedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "note_memory_highlights_noteId_index": { + "name": "note_memory_highlights_noteId_index", + "columns": [ + { + "expression": "noteId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "note_memory_highlights_userId_index": { + "name": "note_memory_highlights_userId_index", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "note_memory_highlights_status_index": { + "name": "note_memory_highlights_status_index", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "note_memory_highlights_userId_createdAt_index": { + "name": "note_memory_highlights_userId_createdAt_index", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "createdAt", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "note_memory_highlights_noteId_notes_id_fk": { + "name": "note_memory_highlights_noteId_notes_id_fk", + "tableFrom": "note_memory_highlights", + "tableTo": "notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "note_memory_highlights_userId_users_id_fk": { + "name": "note_memory_highlights_userId_users_id_fk", + "tableFrom": "note_memory_highlights", + "tableTo": "users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notes": { + "name": "notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "plainText": { + "name": "plainText", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notes_userId_index": { + "name": "notes_userId_index", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "notes_userId_updatedAt_index": { + "name": "notes_userId_updatedAt_index", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updatedAt", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notes_userId_users_id_fk": { + "name": "notes_userId_users_id_fk", + "tableFrom": "notes", + "tableTo": "users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "suiAddress": { + "name": "suiAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authMethod": { + "name": "authMethod", + "type": "auth_method", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "providerSub": { + "name": "providerSub", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "walletType": { + "name": "walletType", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lastSeenAt": { + "name": "lastSeenAt", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "users_provider_providerSub_index": { + "name": "users_provider_providerSub_index", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "providerSub", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_authMethod_index": { + "name": "users_authMethod_index", + "columns": [ + { + "expression": "authMethod", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_suiAddress_index": { + "name": "users_suiAddress_index", + "columns": [ + { + "expression": "suiAddress", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_suiAddress_unique": { + "name": "users_suiAddress_unique", + "nullsNotDistinct": false, + "columns": [ + "suiAddress" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_challenges": { + "name": "wallet_challenges", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "nonce": { + "name": "nonce", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "wallet_challenges_expiresAt_index": { + "name": "wallet_challenges_expiresAt_index", + "columns": [ + { + "expression": "expiresAt", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_sessions": { + "name": "wallet_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "walletAddress": { + "name": "walletAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "walletType": { + "name": "walletType", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "signedMessage": { + "name": "signedMessage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "signature": { + "name": "signature", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "signedAt": { + "name": "signedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "wallet_sessions_userId_index": { + "name": "wallet_sessions_userId_index", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "wallet_sessions_expiresAt_index": { + "name": "wallet_sessions_expiresAt_index", + "columns": [ + { + "expression": "expiresAt", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "wallet_sessions_walletAddress_index": { + "name": "wallet_sessions_walletAddress_index", + "columns": [ + { + "expression": "walletAddress", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "wallet_sessions_userId_users_id_fk": { + "name": "wallet_sessions_userId_users_id_fk", + "tableFrom": "wallet_sessions", + "tableTo": "users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.zklogin_sessions": { + "name": "zklogin_sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "ephemeralPrivateKey": { + "name": "ephemeralPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ephemeralPublicKey": { + "name": "ephemeralPublicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "maxEpoch": { + "name": "maxEpoch", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "randomness": { + "name": "randomness", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nonce": { + "name": "nonce", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "zkProof": { + "name": "zkProof", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "zklogin_sessions_userId_index": { + "name": "zklogin_sessions_userId_index", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "zklogin_sessions_expiresAt_index": { + "name": "zklogin_sessions_expiresAt_index", + "columns": [ + { + "expression": "expiresAt", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "zklogin_sessions_userId_users_id_fk": { + "name": "zklogin_sessions_userId_users_id_fk", + "tableFrom": "zklogin_sessions", + "tableTo": "users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.ai_message_status": { + "name": "ai_message_status", + "schema": "public", + "values": [ + "streaming", + "awaiting-approval", + "in-progress", + "completed", + "error" + ] + }, + "public.auth_method": { + "name": "auth_method", + "schema": "public", + "values": [ + "zklogin", + "wallet" + ] + }, + "public.memory_status": { + "name": "memory_status", + "schema": "public", + "values": [ + "preparing", + "pending", + "signing", + "uploading", + "indexing", + "saved", + "rejected", + "error" + ] + }, + "public.message_role": { + "name": "message_role", + "schema": "public", + "values": [ + "user", + "assistant" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/noter/package/shared/db/migrations/meta/_journal.json b/apps/noter/package/shared/db/migrations/meta/_journal.json index f0a9c297..6b14e3d0 100644 --- a/apps/noter/package/shared/db/migrations/meta/_journal.json +++ b/apps/noter/package/shared/db/migrations/meta/_journal.json @@ -15,6 +15,13 @@ "when": 1773214559127, "tag": "0001_flat_amazoness", "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1774594396885, + "tag": "0002_conscious_deathbird", + "breakpoints": true } ] } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 23f0458a..fcb3094c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -645,8 +645,8 @@ importers: specifier: ^16.4.5 version: 16.6.1 drizzle-kit: - specifier: ^0.31.9 - version: 0.31.9 + specifier: ^0.31.10 + version: 0.31.10 eslint: specifier: ^9 version: 9.39.4(jiti@2.6.1) @@ -702,8 +702,8 @@ importers: specifier: ^13.7.0 version: 13.12.0(react@19.0.1) '@mysten-incubation/memwal': - specifier: workspace:* - version: link:../../packages/sdk + specifier: ^0.0.1-dev.0 + version: 0.0.1(@mysten/seal@1.1.1(@mysten/sui@2.8.0(typescript@5.9.3)))(@mysten/sui@2.8.0(typescript@5.9.3))(@mysten/walrus@1.0.4(@mysten/sui@2.8.0(typescript@5.9.3)))(ai@6.0.37(zod@3.25.76))(zod@3.25.76) '@noble/ed25519': specifier: ^2.2.3 version: 2.3.0 @@ -3045,6 +3045,26 @@ packages: resolution: {integrity: sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA==} engines: {node: '>=18'} + '@mysten-incubation/memwal@0.0.1': + resolution: {integrity: sha512-kRAFFJBdk3D9XvGHZdPOrnz2x4C7dwCRf0xTaeLFAVTgVwfpk3GmOnJZ1O+pAQyrAhweAzBXNXBWutShnPWgJg==} + peerDependencies: + '@mysten/seal': '>=1.1.0' + '@mysten/sui': '>=2.5.0' + '@mysten/walrus': '>=1.0.3' + ai: '>=4.0.0' + zod: ^3.23.0 + peerDependenciesMeta: + '@mysten/seal': + optional: true + '@mysten/sui': + optional: true + '@mysten/walrus': + optional: true + ai: + optional: true + zod: + optional: true + '@mysten/bcs@1.2.0': resolution: {integrity: sha512-LuKonrGdGW7dq/EM6U2L9/as7dFwnhZnsnINzB/vu08Xfrj0qzWwpLOiXagAa5yZOPLK7anRZydMonczFkUPzA==} @@ -6433,8 +6453,8 @@ packages: resolution: {integrity: sha512-Rcf0nYCAKizwjWQCY+d3zytyuTbDb81NcaPor+8NebESlUz1+9W3uGl0+r9FhU4Qal5Zv9j/7neXCSCe7DHzjA==} hasBin: true - drizzle-kit@0.31.9: - resolution: {integrity: sha512-GViD3IgsXn7trFyBUUHyTFBpH/FsHTxYJ66qdbVggxef4UBPHRYxQaRzYLTuekYnk9i5FIEL9pbBIwMqX/Uwrg==} + drizzle-kit@0.31.10: + resolution: {integrity: sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw==} hasBin: true drizzle-orm@0.34.1: @@ -10269,6 +10289,7 @@ packages: tar@6.1.15: resolution: {integrity: sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==} engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me teeny-request@10.1.0: resolution: {integrity: sha512-3ZnLvgWF29jikg1sAQ1g0o+lr5JX6sVgYvfUJazn7ZjJroDBUTWp44/+cFVX0bULjv4vci+rBD+oGVAkWqhUbw==} @@ -13486,6 +13507,17 @@ snapshots: outvariant: 1.4.3 strict-event-emitter: 0.5.1 + '@mysten-incubation/memwal@0.0.1(@mysten/seal@1.1.1(@mysten/sui@2.8.0(typescript@5.9.3)))(@mysten/sui@2.8.0(typescript@5.9.3))(@mysten/walrus@1.0.4(@mysten/sui@2.8.0(typescript@5.9.3)))(ai@6.0.37(zod@3.25.76))(zod@3.25.76)': + dependencies: + '@noble/ed25519': 2.3.0 + '@noble/hashes': 2.0.1 + optionalDependencies: + '@mysten/seal': 1.1.1(@mysten/sui@2.8.0(typescript@5.9.3)) + '@mysten/sui': 2.8.0(typescript@5.9.3) + '@mysten/walrus': 1.0.4(@mysten/sui@2.8.0(typescript@5.9.3)) + ai: 6.0.37(zod@3.25.76) + zod: 3.25.76 + '@mysten/bcs@1.2.0': dependencies: bs58: 6.0.0 @@ -18543,14 +18575,12 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-kit@0.31.9: + drizzle-kit@0.31.10: dependencies: '@drizzle-team/brocli': 0.10.2 '@esbuild-kit/esm-loader': 2.6.5 esbuild: 0.25.12 - esbuild-register: 3.6.0(esbuild@0.25.12) - transitivePeerDependencies: - - supports-color + tsx: 4.21.0 drizzle-orm@0.34.1(@opentelemetry/api@1.9.0)(@types/react@18.3.28)(postgres@3.4.8)(react@19.0.1): optionalDependencies: @@ -18819,13 +18849,6 @@ snapshots: transitivePeerDependencies: - supports-color - esbuild-register@3.6.0(esbuild@0.25.12): - dependencies: - debug: 4.4.3 - esbuild: 0.25.12 - transitivePeerDependencies: - - supports-color - esbuild@0.18.20: optionalDependencies: '@esbuild/android-arm': 0.18.20 @@ -18984,8 +19007,8 @@ snapshots: '@next/eslint-plugin-next': 16.1.6 eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-react-hooks: 7.0.1(eslint@9.39.4(jiti@2.6.1)) @@ -19007,7 +19030,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -19018,21 +19041,21 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)): + eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)): + eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -19043,7 +19066,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.4(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1)) + eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3