{"version":3,"sources":["../../src/db/chat-store.ts","../../src/db/index.ts","../../src/db/schema.ts","../../src/server/chat-store.ts","../../src/api/index.ts"],"sourcesContent":["import { generateId, UIMessage } from 'ai';\nimport { db, conversations, messages } from './index';\nimport { eq, and, desc, asc, sql } from 'drizzle-orm';\nimport { ConversationOwnershipError } from '../server/chat-store';\n\n/**\n * ⚠️ DEPRECATED LEGACY DATA LAYER ⚠️\n *\n * These standalone functions predate the user-bound `ChatStore` design and are\n * retained only for migration. They are exported via the `@mordn/chat-widget/db`\n * and `@mordn/chat-widget/api` subpaths.\n *\n * Historically several of them queried by raw `conversationId`/`chatId` with NO\n * `userId` and NO ownership check, which made the IDOR class of bug (cross-user\n * read/write/delete) representable — directly contradicting the headline\n * \"IDOR is unrepresentable\" guarantee. Every function below now REQUIRES a\n * verified `userId` (derived server-side from the session, never client input)\n * and scopes/validates ownership on every read, write, and delete, mirroring\n * {@link DrizzleChatStore} (src/server/stores/drizzle/store.ts).\n *\n * @deprecated Prefer the secure persistence path: build routes with\n * `createChatHandler` and the user-bound `DrizzleChatStore`\n * (`createDrizzleChatStore`) from `@mordn/chat-widget/server` /\n * `@mordn/chat-widget/server/drizzle`. The bound store cannot be made to cross\n * users because no method accepts a `userId`. These legacy functions will be\n * removed in a future release.\n */\n\n/**\n * Create a new conversation owned by `userId`.\n *\n * @deprecated Use the user-bound `DrizzleChatStore.ensureConversation` via\n * `createChatHandler` instead. See the module-level note above.\n */\nexport async function createChat(userId: string): Promise<string> {\n  if (!userId) {\n    throw new Error('userId is required for createChat');\n  }\n\n  const id = generateId();\n\n  await db.insert(conversations).values({\n    id,\n    userId,\n    title: 'New Chat',\n    metadata: {},\n  });\n\n  return id;\n}\n\n/**\n * Load messages for a conversation owned by `userId`.\n *\n * Ownership is asserted FIRST: a conversation id that does not exist OR belongs\n * to a different user yields `[]` — never another user's messages, and the two\n * cases are intentionally indistinguishable to the caller (mirrors\n * `DrizzleChatStore.listMessages`).\n *\n * @param userId Verified, server-derived id of the user that must own the\n *   conversation. Required — never pass a client-supplied value.\n * @param conversationId Conversation whose messages to load.\n * @deprecated Use the user-bound `DrizzleChatStore.listMessages` via\n * `createChatHandler` instead. See the module-level note above.\n */\nexport async function loadChat(\n  userId: string,\n  conversationId: string\n): Promise<UIMessage[]> {\n  if (!userId) {\n    throw new Error('userId is required for loadChat');\n  }\n\n  try {\n    // Scope to the user FIRST: confirm ownership before reading any messages,\n    // so a foreign/forged conversationId yields [] rather than someone else's\n    // history.\n    const owned = await db\n      .select({ id: conversations.id })\n      .from(conversations)\n      .where(\n        and(\n          eq(conversations.id, conversationId),\n          eq(conversations.userId, userId)\n        )\n      )\n      .limit(1);\n\n    if (!owned.length) return [];\n\n    const dbMessages = await db\n      .select()\n      .from(messages)\n      .where(eq(messages.conversationId, conversationId))\n      .orderBy(asc(messages.createdAt));\n\n    if (!dbMessages.length) return [];\n\n    // Convert database messages to UIMessage format\n    return dbMessages.map((msg) => {\n      // If we have metadata with parts, use those (includes reasoning)\n      const metadata = msg.metadata as { parts?: any[] } | null;\n      if (metadata?.parts && Array.isArray(metadata.parts)) {\n        return {\n          id: msg.id,\n          role: msg.role as 'user' | 'assistant' | 'system',\n          parts: metadata.parts,\n          createdAt: msg.createdAt,\n        };\n      }\n\n      // Fallback to simple text message\n      return {\n        id: msg.id,\n        role: msg.role as 'user' | 'assistant' | 'system',\n        parts: [{ type: 'text', text: msg.content }],\n        createdAt: msg.createdAt,\n      };\n    });\n  } catch (error) {\n    console.error('Error loading chat:', error);\n    return [];\n  }\n}\n\n/**\n * Update a conversation's title, scoped to its owner.\n *\n * The `WHERE` clause includes `userId`, so a foreign/forged `chatId` matches no\n * row and the update is a silent no-op (renaming is not security-sensitive;\n * mirrors `DrizzleChatStore.renameConversation`).\n *\n * @param userId Verified, server-derived owner id. Required.\n * @param chatId Conversation to rename.\n * @param title New title.\n * @deprecated Use the user-bound `DrizzleChatStore.renameConversation` via\n * `createChatHandler` instead. See the module-level note above.\n */\nexport async function updateConversationTitle(\n  userId: string,\n  chatId: string,\n  title: string\n): Promise<void> {\n  if (!userId) {\n    throw new Error('userId is required for updateConversationTitle');\n  }\n\n  try {\n    await db\n      .update(conversations)\n      .set({ title, updatedAt: new Date() })\n      .where(\n        and(eq(conversations.id, chatId), eq(conversations.userId, userId))\n      );\n  } catch (error) {\n    console.error('Error updating conversation title:', error);\n  }\n}\n\n/**\n * Save messages to a conversation owned by `userId`.\n *\n * Ownership is VERIFIED before any mutation: the conversation must exist AND\n * belong to `userId`, otherwise a `ConversationOwnershipError` is thrown and\n * nothing is written (callers should map this to HTTP 403). This closes the\n * write-IDOR where the previous implementation only checked the conversation\n * existed, letting a caller inject messages into a victim's thread. Mirrors\n * `DrizzleChatStore.saveTurn`.\n *\n * @param userId Verified, server-derived owner id. Required.\n * @throws ConversationOwnershipError if the conversation exists under a\n *   different user (or cannot be confirmed owned).\n * @deprecated Use the user-bound `DrizzleChatStore.saveTurn` via\n * `createChatHandler` instead. See the module-level note above.\n */\nexport async function saveChat({\n  chatId,\n  messages: chatMessages,\n  model,\n  userId,\n}: {\n  chatId: string;\n  messages: UIMessage[];\n  model?: string;\n  userId: string;\n}): Promise<void> {\n  if (!userId) {\n    throw new Error('userId is required for saveChat');\n  }\n\n  try {\n    // Verify the conversation exists AND is owned by this user. Scoping the\n    // lookup by userId means a foreign/forged chatId returns no row, so we\n    // refuse rather than writing into someone else's conversation.\n    const existingConv = await db\n      .select({ id: conversations.id, title: conversations.title })\n      .from(conversations)\n      .where(\n        and(eq(conversations.id, chatId), eq(conversations.userId, userId))\n      )\n      .limit(1);\n\n    if (!existingConv.length) {\n      // Either the conversation doesn't exist or it's owned by another user.\n      // Both must refuse the write; throw so the route returns 403 and never\n      // mutates a foreign thread.\n      throw new ConversationOwnershipError(chatId);\n    }\n\n    const conv = existingConv[0];\n\n    // No prefix auto-title here (removed): the title stays 'New Chat' until the\n    // handler's generated thread title lands. The default title doubles as the\n    // \"still needs a title\" signal, enabling retry on the thread's next turn.\n\n    // Get existing message IDs from database\n    const existingMessages = await db\n      .select({ id: messages.id })\n      .from(messages)\n      .where(eq(messages.conversationId, chatId));\n\n    const existingIds = new Set(existingMessages.map((m) => m.id));\n\n    // Insert only new messages\n    const newMessages = chatMessages.filter((msg) => !existingIds.has(msg.id));\n\n    if (newMessages.length > 0) {\n      for (const msg of newMessages) {\n        const textPart = msg.parts?.find((p) => p.type === 'text') as { text: string } | undefined;\n        const fileParts = msg.parts?.filter((p) => p.type === 'file') || [];\n\n        // Generate ID for assistant messages, use existing ID for user messages\n        const messageId = msg.role === 'assistant' ? generateId() : msg.id;\n\n        await db.insert(messages).values({\n          id: messageId,\n          conversationId: chatId,\n          role: msg.role,\n          content: textPart?.text || '',\n          files: fileParts,\n          model: model || 'openai/gpt-4o-mini',\n          metadata: { parts: msg.parts || [] },\n        });\n      }\n\n      // Update conversation's updatedAt (scoped to owner)\n      await db\n        .update(conversations)\n        .set({ updatedAt: new Date() })\n        .where(\n          and(eq(conversations.id, chatId), eq(conversations.userId, userId))\n        );\n    }\n  } catch (error) {\n    // Re-throw ownership violations so callers can return 403; the bare\n    // function must never silently succeed on a foreign conversation.\n    if (error instanceof ConversationOwnershipError) {\n      throw error;\n    }\n    console.error('Error saving chat:', error);\n  }\n}\n\n/**\n * Get all conversations for a user.\n *\n * Already scoped by `userId` in the original implementation; the parameter is\n * now explicitly required and validated. Trust only a server-derived `userId`.\n *\n * @param userId Verified, server-derived owner id. Required.\n * @deprecated Use the user-bound `DrizzleChatStore.listConversations` via\n * `createChatHandler` instead. See the module-level note above.\n */\nexport async function getConversations(userId: string) {\n  if (!userId) {\n    throw new Error('userId is required for getConversations');\n  }\n\n  try {\n    const result = await db\n      .select({\n        id: conversations.id,\n        title: conversations.title,\n        createdAt: conversations.createdAt,\n        updatedAt: conversations.updatedAt,\n        metadata: conversations.metadata,\n        messageCount: sql<number>`(\n          SELECT COUNT(*) FROM ${messages}\n          WHERE ${messages.conversationId} = ${conversations.id}\n        )`,\n      })\n      .from(conversations)\n      .where(eq(conversations.userId, userId))\n      .orderBy(desc(conversations.updatedAt));\n\n    return result;\n  } catch (error) {\n    console.error('Error getting conversations:', error);\n    return [];\n  }\n}\n\n/**\n * Delete a conversation (and cascade its messages) owned by `userId`.\n *\n * The `DELETE` is scoped by `userId`, so a foreign/forged `chatId` matches no\n * row and nothing is deleted. Returns `true` only when a row owned by the user\n * was actually removed (mirrors `DrizzleChatStore.deleteConversation`), letting\n * the route distinguish 404 from 200 without a separate ownership check.\n *\n * @param userId Verified, server-derived owner id. Required.\n * @param chatId Conversation to delete.\n * @returns `true` if a conversation owned by `userId` was deleted, else `false`.\n * @deprecated Use the user-bound `DrizzleChatStore.deleteConversation` via\n * `createChatHandler` instead. See the module-level note above.\n */\nexport async function deleteConversation(\n  userId: string,\n  chatId: string\n): Promise<boolean> {\n  if (!userId) {\n    throw new Error('userId is required for deleteConversation');\n  }\n\n  try {\n    // Messages are deleted automatically due to cascade. Scoping the DELETE by\n    // userId means a caller cannot destroy another user's conversation.\n    const deleted = await db\n      .delete(conversations)\n      .where(\n        and(eq(conversations.id, chatId), eq(conversations.userId, userId))\n      )\n      .returning({ id: conversations.id });\n    return deleted.length > 0;\n  } catch (error) {\n    console.error('Error deleting conversation:', error);\n    return false;\n  }\n}\n","import 'server-only';\nimport { drizzle } from 'drizzle-orm/postgres-js';\nimport postgres from 'postgres';\nimport * as schema from './schema';\n\n// Cache the postgres client on globalThis so Next.js dev (HMR) doesn't\n// leak a fresh pool on every code reload — without this, each hot\n// reload accumulates open sockets against Supabase's pooler and you\n// eventually hit \"max clients reached in session mode\". In production\n// the module is loaded once per server instance so the cache is a\n// no-op there.\nconst connectionString = process.env.DATABASE_URL!;\n\nconst globalForDb = globalThis as unknown as {\n  __mordnChatWidgetPg?: ReturnType<typeof postgres>;\n};\n\nconst client =\n  globalForDb.__mordnChatWidgetPg ??\n  postgres(connectionString, {\n    // Disable prefetch — required for Supabase Transaction-mode pooler.\n    prepare: false,\n    // Keep dev's connection footprint small. Override at the env level\n    // (DATABASE_URL or equivalent) for prod.\n    max: 5,\n    idle_timeout: 20,\n    connect_timeout: 10,\n  });\n\nif (process.env.NODE_ENV !== 'production') {\n  globalForDb.__mordnChatWidgetPg = client;\n}\n\n// Create drizzle database instance\nexport const db = drizzle(client, { schema });\n\n// Export schema for convenience\nexport * from './schema';\n\n// Export chat store functions\nexport * from './chat-store';\n","import { pgTable, text, timestamp, jsonb, index } from 'drizzle-orm/pg-core';\n\n/**\n * Conversations table\n * Stores chat conversation metadata\n */\nexport const conversations = pgTable('conversations', {\n  id: text('id').primaryKey(),\n  userId: text('user_id').notNull(),\n  title: text('title').notNull().default('New Chat'),\n  metadata: jsonb('metadata'),\n  createdAt: timestamp('created_at').defaultNow().notNull(),\n  updatedAt: timestamp('updated_at').defaultNow().notNull(),\n}, (table) => [\n  index('conversations_user_id_idx').on(table.userId),\n  index('conversations_updated_at_idx').on(table.updatedAt),\n]);\n\n/**\n * Messages table\n * Stores individual chat messages\n */\nexport const messages = pgTable('messages', {\n  id: text('id').primaryKey(),\n  conversationId: text('conversation_id').notNull().references(() => conversations.id, { onDelete: 'cascade' }),\n  role: text('role').notNull(), // 'user' | 'assistant' | 'system'\n  content: text('content').notNull(),\n  files: jsonb('files'), // Array of file attachments\n  model: text('model'), // AI model used\n  metadata: jsonb('metadata'), // Additional data (parts, reasoning, etc.)\n  createdAt: timestamp('created_at').defaultNow().notNull(),\n}, (table) => [\n  index('messages_conversation_id_idx').on(table.conversationId),\n  index('messages_created_at_idx').on(table.createdAt),\n]);\n\n// Type exports for use in application code\nexport type Conversation = typeof conversations.$inferSelect;\nexport type NewConversation = typeof conversations.$inferInsert;\nexport type Message = typeof messages.$inferSelect;\nexport type NewMessage = typeof messages.$inferInsert;\n","/**\n * ChatStore — the persistence contract for chat conversations and messages.\n *\n * This is one of the two pluggable backends of the widget (the other is\n * `StorageAdapter` for attachments). The package ships a Drizzle/Postgres\n * implementation as the default; a hosted backend or a BYO store (Prisma,\n * raw SQL, DynamoDB, a test double) is simply another implementation of this\n * same interface.\n *\n * ──────────────────────────────────────────────────────────────────────────\n * The security model is in the shape of this API, not in its callers.\n * ──────────────────────────────────────────────────────────────────────────\n *\n * A `ChatStore` is *bound to one verified user* at construction time (see\n * `ChatStoreFactory`). None of its methods accept a `userId`. This is\n * deliberate and it is the core defence against the IDOR class of bug:\n *\n *   - You cannot ask the store for \"conversation X belonging to user Y\",\n *     because there is no parameter through which a foreign `userId` could\n *     enter. The only user the store will ever read or write is the one it\n *     was constructed with.\n *\n *   - Every method is therefore *implicitly scoped*. `listConversations()`\n *     returns only the bound user's conversations. `getConversation(id)`\n *     returns `null` — not someone else's row — when `id` exists but belongs\n *     to a different user. `saveTurn(...)` refuses (throws\n *     `ConversationOwnershipError`) if `conversationId` exists under another\n *     user.\n *\n * The route layer's job shrinks to: authenticate the request, derive the\n * real `userId` from the *server* session, construct a store bound to it,\n * and call methods. There is no per-route ownership check to forget, because\n * the store cannot be made to cross users.\n *\n * Implementations MUST uphold the contract documented on each method. The\n * Drizzle default does; if you write your own, these invariants are the\n * security boundary — treat them as load-bearing, not advisory.\n */\n\nimport type {\n  ListMessagesOptions,\n  SaveTurnInput,\n  StoredConversation,\n  StoredMessage,\n} from './types';\n\n/**\n * Thrown by mutating methods when the target conversation exists but is owned\n * by a different user than the one this store is bound to. Callers should map\n * this to an HTTP 403. (Read methods don't throw — they return `null`/`[]` —\n * so that probing for existence can't distinguish \"not found\" from\n * \"forbidden\", which would itself leak information.)\n */\nexport class ConversationOwnershipError extends Error {\n  constructor(public readonly conversationId: string) {\n    super(`Conversation ${conversationId} is not owned by the current user`);\n    this.name = 'ConversationOwnershipError';\n  }\n}\n\nexport interface ChatStore {\n  /**\n   * The user this store instance is bound to. Read-only; set at construction.\n   * Exposed so the router can stamp it onto storage paths, logs, etc. — never\n   * as something a caller can change.\n   */\n  readonly userId: string;\n\n  // ── Conversations ──────────────────────────────────────────────────────\n\n  /**\n   * List the bound user's conversations, most-recently-updated first.\n   * Returns `messageCount` on each row for the history list. Returns `[]`\n   * (never throws) when the user has none.\n   */\n  listConversations(): Promise<StoredConversation[]>;\n\n  /**\n   * Fetch a single conversation by id, scoped to the bound user.\n   *\n   * Returns `null` when the conversation does not exist OR exists but belongs\n   * to another user — the two cases are intentionally indistinguishable to\n   * the caller (and thus to an attacker). Never returns another user's row.\n   */\n  getConversation(id: string): Promise<StoredConversation | null>;\n\n  /**\n   * Ensure a conversation row exists for `id`, owned by the bound user.\n   *\n   * - If no row exists for `id`: creates it, owned by the bound user, and\n   *   returns it.\n   * - If a row exists and is owned by the bound user: returns it unchanged\n   *   (idempotent — safe to call at the top of every request).\n   * - If a row exists but is owned by a *different* user: throws\n   *   `ConversationOwnershipError` and writes nothing.\n   *\n   * This is the single chokepoint that makes \"write into someone else's\n   * conversation\" impossible: the router calls it before persisting any\n   * message, so a forged conversation id is rejected before any data lands.\n   */\n  ensureConversation(id: string, init?: { title?: string }): Promise<StoredConversation>;\n\n  /**\n   * Rename a conversation owned by the bound user. No-op (does not throw) if\n   * the conversation doesn't exist or isn't owned by the user — renaming is\n   * not security-sensitive and silent failure is friendlier here.\n   */\n  renameConversation(id: string, title: string): Promise<void>;\n\n  /**\n   * Delete a conversation (and cascade its messages + attachment rows) owned\n   * by the bound user. No-op if it doesn't exist or isn't owned by the user.\n   * Returns `true` if a row was actually deleted, `false` otherwise — lets\n   * the route return 404 vs 200 honestly without a separate existence check.\n   *\n   * Note: this deletes message *rows*. Purging the underlying attachment\n   * blobs from storage is the router's job (it has the `StorageAdapter`),\n   * driven off the attachments this method returns having referenced.\n   */\n  deleteConversation(id: string): Promise<boolean>;\n\n  // ── Messages ───────────────────────────────────────────────────────────\n\n  /**\n   * Load messages for a conversation, scoped to the bound user, newest-first\n   * internally but returned in chronological order (oldest → newest) ready to\n   * render. Returns `[]` if the conversation doesn't exist or isn't owned by\n   * the user — same non-distinguishing contract as `getConversation`.\n   *\n   * Honours `ListMessagesOptions` for pagination. Implementations MUST:\n   *\n   *  - Apply `before` as a strict composite cursor when `beforeId` is present:\n   *    `(createdAt < before) OR (createdAt = before AND id < beforeId)`.\n   *    Timestamp-only callers retain strict `createdAt < before` behavior.\n   *  - Return CHRONOLOGICAL order (oldest → newest), with `id` as the stable\n   *    tiebreaker for equal timestamps.\n   *  - Clamp `limit` so a hostile client cannot request an unbounded page —\n   *    but clamp to **101, not 100**. The router asks for `limit + 1` to detect\n   *    whether an older page exists without a second COUNT query; a ceiling of\n   *    exactly 100 eats that probe row at the maximum page size, so `hasMore`\n   *    silently reports `false` and the conversation appears to end at message\n   *    100 with no way to scroll further (#55). One row of headroom keeps the\n   *    probe working while the ceiling still bounds the query.\n   *\n   * A store that ignores these options is not merely slower — it breaks the\n   * router's `hasMore` contract and ships the whole transcript on every load.\n   */\n  listMessages(conversationId: string, opts?: ListMessagesOptions): Promise<StoredMessage[]>;\n\n  /**\n   * Persist the final messages of a completed turn.\n   *\n   * Contract:\n   *  - MUST verify the conversation is owned by the bound user first; throws\n   *    `ConversationOwnershipError` otherwise (defence in depth — the router\n   *    already called `ensureConversation`, but `saveTurn` must not trust\n   *    that).\n   *  - MUST be idempotent on message id: a message whose id already exists is\n   *    skipped, not duplicated. (The AI SDK delivers stable ids; replays and\n   *    retries re-deliver them.)\n   *  - MUST persist each message's full `parts` array as the source of truth,\n   *    plus a denormalised text projection for previews.\n   *  - MUST bump the conversation's `updatedAt`.\n   *\n   * Errors other than ownership (e.g. a transient DB failure) propagate so\n   * the router can log them loudly — a silently-dropped assistant turn is\n   * exactly the bug we're trying to design out.\n   */\n  saveTurn(input: SaveTurnInput): Promise<void>;\n}\n\n/**\n * Constructs a `ChatStore` bound to a specific, already-verified user.\n *\n * The router calls this *after* it has authenticated the request and derived\n * `userId` from the server session — never from anything client-supplied.\n * Passing a client-controlled value here would reintroduce the very IDOR the\n * bound-store design exists to prevent, so implementations should treat\n * `userId` as a trusted server secret, not as request input.\n *\n * Construction is intended to be cheap (the underlying DB pool/connection is\n * shared across instances) so a fresh store per request is the norm.\n */\nexport type ChatStoreFactory = (userId: string) => ChatStore;\n","/**\n * API Helpers for Chat Widget\n *\n * ⚠️ DEPRECATED LEGACY SURFACE ⚠️\n *\n * These utilities (exported as `@mordn/chat-widget/api`) re-export the legacy\n * standalone data layer from `../db/chat-store`. They predate the user-bound\n * `ChatStore` design and are kept only for migration.\n *\n * Every re-exported function now REQUIRES a verified `userId` and scopes/\n * validates ownership on every read, write, and delete (see the security notes\n * on each function in `../db/chat-store`). Even so, this surface puts ownership\n * enforcement back in the caller's hands — exactly the footgun the bound-store\n * design removes.\n *\n * @deprecated Build routes with `createChatHandler` and the user-bound\n * `DrizzleChatStore` (`createDrizzleChatStore`) from `@mordn/chat-widget/server`\n * / `@mordn/chat-widget/server/drizzle` instead. The bound store makes\n * cross-user access (IDOR) unrepresentable because no method accepts a\n * `userId`. This subpath will be removed in a future release.\n */\n\nexport {\n  createChat,\n  loadChat,\n  saveChat,\n  getConversations,\n  updateConversationTitle,\n  deleteConversation\n} from '../db/chat-store';\n\nexport { db, conversations, messages } from '../db';\nexport type { Conversation, Message, NewConversation, NewMessage } from '../db/schema';\n\n// Re-export drizzle utilities for convenience\nexport { eq, and, or, desc, asc, sql } from 'drizzle-orm';\n"],"mappings":"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","names":["eq","and","desc","asc","sql"]}