@raisindb/client 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +313 -70
  2. package/dist/chunk-ASOWGNQZ.mjs +532 -0
  3. package/dist/chunk-ASOWGNQZ.mjs.map +1 -0
  4. package/dist/chunk-FYWYSNQG.mjs +190 -0
  5. package/dist/chunk-FYWYSNQG.mjs.map +1 -0
  6. package/dist/chunk-NOPZAMIV.mjs +1427 -0
  7. package/dist/chunk-NOPZAMIV.mjs.map +1 -0
  8. package/dist/chunk-T2ZSNYFG.mjs +248 -0
  9. package/dist/chunk-T2ZSNYFG.mjs.map +1 -0
  10. package/dist/chunk-TPZNJNZR.mjs +866 -0
  11. package/dist/chunk-TPZNJNZR.mjs.map +1 -0
  12. package/dist/chunk-ZCRYQCXS.mjs +170 -0
  13. package/dist/chunk-ZCRYQCXS.mjs.map +1 -0
  14. package/dist/conversation-list-store-D-W6_nsv.d.mts +8447 -0
  15. package/dist/conversation-list-store-D-W6_nsv.d.ts +8447 -0
  16. package/dist/index-B4CqE3Cb.d.ts +301 -0
  17. package/dist/index-CeBRaSop.d.mts +301 -0
  18. package/dist/index.d.mts +72 -6109
  19. package/dist/index.d.ts +72 -6109
  20. package/dist/index.js +3123 -257
  21. package/dist/index.js.map +1 -0
  22. package/dist/index.mjs +2745 -3185
  23. package/dist/index.mjs.map +1 -0
  24. package/dist/react.d.mts +4 -0
  25. package/dist/react.d.ts +4 -0
  26. package/dist/react.js +2569 -0
  27. package/dist/react.js.map +1 -0
  28. package/dist/react.mjs +9 -0
  29. package/dist/react.mjs.map +1 -0
  30. package/dist/svelte-sql-CQoB4FEm.d.mts +52 -0
  31. package/dist/svelte-sql-CTXjkz0R.d.ts +52 -0
  32. package/dist/svelte.d.mts +288 -0
  33. package/dist/svelte.d.ts +288 -0
  34. package/dist/svelte.js +2482 -0
  35. package/dist/svelte.js.map +1 -0
  36. package/dist/svelte.mjs +25 -0
  37. package/dist/svelte.mjs.map +1 -0
  38. package/dist/vue.d.mts +197 -0
  39. package/dist/vue.d.ts +197 -0
  40. package/dist/vue.js +1510 -0
  41. package/dist/vue.js.map +1 -0
  42. package/dist/vue.mjs +9 -0
  43. package/dist/vue.mjs.map +1 -0
  44. package/package.json +18 -3
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/reconnect.ts","../src/logger.ts","../src/connection.ts","../src/utils/home-path.ts","../src/utils/plan-projection.ts","../src/utils/chat-events.ts","../src/stores/conversation-store.ts","../src/stores/conversation-list-store.ts"],"sourcesContent":["/**\n * Reconnection manager with exponential backoff\n */\n\n/**\n * Reconnection strategy options\n */\nexport interface ReconnectOptions {\n /** Initial delay in milliseconds (default: 1000) */\n initialDelay?: number;\n /** Maximum delay in milliseconds (default: 30000) */\n maxDelay?: number;\n /** Backoff multiplier (default: 2) */\n backoffMultiplier?: number;\n /** Maximum number of reconnection attempts (default: Infinity) */\n maxAttempts?: number;\n /** Add random jitter to delay (default: true). Jitter is +/- 25% of the computed delay. */\n jitter?: boolean;\n}\n\n/**\n * Reconnection manager with exponential backoff\n */\nexport class ReconnectManager {\n private currentDelay: number;\n private attempts = 0;\n private reconnectTimeout?: NodeJS.Timeout;\n private isReconnecting = false;\n\n private readonly initialDelay: number;\n private readonly maxDelay: number;\n private readonly backoffMultiplier: number;\n private readonly maxAttempts: number;\n private readonly jitter: boolean;\n\n constructor(options: ReconnectOptions = {}) {\n this.initialDelay = options.initialDelay ?? 1000;\n this.maxDelay = options.maxDelay ?? 30000;\n this.backoffMultiplier = options.backoffMultiplier ?? 2;\n this.maxAttempts = options.maxAttempts ?? Infinity;\n this.jitter = options.jitter ?? true;\n this.currentDelay = this.initialDelay;\n }\n\n /**\n * Schedule a reconnection attempt\n *\n * @param callback - Function to call when it's time to reconnect\n * @returns true if reconnection was scheduled, false if max attempts reached\n */\n scheduleReconnect(callback: () => void | Promise<void>): boolean {\n // Check if we've exceeded max attempts\n if (this.attempts >= this.maxAttempts) {\n return false;\n }\n\n // Clear any existing timeout\n this.cancelReconnect();\n\n this.isReconnecting = true;\n this.attempts++;\n\n // Apply jitter: +/- 25% of the computed delay\n let delay = this.currentDelay;\n if (this.jitter) {\n const jitterRange = delay * 0.25;\n delay += (Math.random() * 2 - 1) * jitterRange;\n delay = Math.max(0, Math.round(delay));\n }\n\n this.reconnectTimeout = setTimeout(async () => {\n try {\n await callback();\n // If successful, reset the delay\n this.reset();\n } catch (error) {\n // Increase delay for next attempt\n this.currentDelay = Math.min(\n this.currentDelay * this.backoffMultiplier,\n this.maxDelay\n );\n }\n }, delay);\n\n return true;\n }\n\n /**\n * Cancel any pending reconnection attempt\n */\n cancelReconnect(): void {\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = undefined;\n }\n this.isReconnecting = false;\n }\n\n /**\n * Reset the reconnection state (call after successful connection)\n */\n reset(): void {\n this.cancelReconnect();\n this.currentDelay = this.initialDelay;\n this.attempts = 0;\n }\n\n /**\n * Check if currently in reconnection mode\n */\n isActive(): boolean {\n return this.isReconnecting;\n }\n\n /**\n * Get the current number of reconnection attempts\n */\n getAttempts(): number {\n return this.attempts;\n }\n\n /**\n * Get the current delay in milliseconds\n */\n getCurrentDelay(): number {\n return this.currentDelay;\n }\n\n /**\n * Check if max attempts have been reached\n */\n hasReachedMaxAttempts(): boolean {\n return this.attempts >= this.maxAttempts;\n }\n}\n","/**\n * Configurable logger for RaisinDB client\n */\n\n/**\n * Log levels in order of verbosity (lower = less verbose)\n */\nexport enum LogLevel {\n Silent = 0,\n Error = 1,\n Warn = 2,\n Info = 3,\n Debug = 4,\n}\n\n/**\n * Logger configuration\n */\nexport interface LoggerConfig {\n /** Current log level (default: Error) */\n level: LogLevel;\n /** Prefix for all log messages (default: \"[RaisinDB]\") */\n prefix: string;\n}\n\n/**\n * Global logger configuration\n */\nlet config: LoggerConfig = {\n level: LogLevel.Error,\n prefix: '[RaisinDB]',\n};\n\n/**\n * Configure the logger\n *\n * @param options - Logger configuration options\n *\n * @example\n * ```typescript\n * import { configureLogger, LogLevel } from '@raisindb/client';\n *\n * // Enable debug logging\n * configureLogger({ level: LogLevel.Debug });\n *\n * // Disable all logging\n * configureLogger({ level: LogLevel.Silent });\n * ```\n */\nexport function configureLogger(options: Partial<LoggerConfig>): void {\n config = { ...config, ...options };\n}\n\n/**\n * Get current logger configuration\n */\nexport function getLoggerConfig(): LoggerConfig {\n return { ...config };\n}\n\n/**\n * Set log level\n *\n * @param level - Log level to set\n */\nexport function setLogLevel(level: LogLevel): void {\n config.level = level;\n}\n\n/**\n * Get current log level\n */\nexport function getLogLevel(): LogLevel {\n return config.level;\n}\n\n/**\n * Logger instance with level-aware logging methods\n */\nexport const logger = {\n /**\n * Log debug message (only shown when level >= Debug)\n */\n debug(...args: unknown[]): void {\n if (config.level >= LogLevel.Debug) {\n console.log(config.prefix, ...args);\n }\n },\n\n /**\n * Log info message (only shown when level >= Info)\n */\n info(...args: unknown[]): void {\n if (config.level >= LogLevel.Info) {\n console.log(config.prefix, ...args);\n }\n },\n\n /**\n * Log warning message (only shown when level >= Warn)\n */\n warn(...args: unknown[]): void {\n if (config.level >= LogLevel.Warn) {\n console.warn(config.prefix, ...args);\n }\n },\n\n /**\n * Log error message (only shown when level >= Error)\n */\n error(...args: unknown[]): void {\n if (config.level >= LogLevel.Error) {\n console.error(config.prefix, ...args);\n }\n },\n};\n\nif (typeof globalThis !== 'undefined') {\n (globalThis as any).__raisinDebug = (enable = true) => {\n setLogLevel(enable ? LogLevel.Debug : LogLevel.Error);\n console.log(`[RaisinDB] Debug logging ${enable ? 'ENABLED' : 'DISABLED'} (includes server handler logs)`);\n };\n}\n","/**\n * Universal WebSocket connection manager\n *\n * Detects environment (browser vs Node.js) and uses appropriate WebSocket implementation.\n * Handles auto-reconnect, heartbeat, and connection state management.\n */\n\nimport { EventEmitter } from 'events';\nimport { ReconnectManager, ReconnectOptions } from './utils/reconnect';\nimport { logger } from './logger';\n\n/**\n * Connection state\n */\nexport enum ConnectionState {\n Disconnected = 'disconnected',\n Connecting = 'connecting',\n Connected = 'connected',\n Reconnecting = 'reconnecting',\n Closed = 'closed',\n}\n\n/**\n * Connection options\n */\nexport interface ConnectionOptions {\n /** Auto-reconnect on disconnect (default: true) */\n autoReconnect?: boolean;\n /** Reconnection options */\n reconnectOptions?: ReconnectOptions;\n /** Heartbeat interval in milliseconds (default: 30000, 0 to disable) */\n heartbeatInterval?: number;\n /** Heartbeat timeout in milliseconds (default: 5000) */\n heartbeatTimeout?: number;\n /** WebSocket protocols */\n protocols?: string | string[];\n /**\n * Extra headers to send on the WebSocket upgrade request\n * (e.g. `x-tenant-id` for tenant-less `/ws` URLs).\n *\n * Only honored by WebSocket implementations that support custom headers\n * (Node.js `ws` package). Browser WebSockets silently ignore them — the\n * server then resolves the tenant from the request (default: \"default\").\n */\n headers?: Record<string, string>;\n}\n\n/**\n * Connection events\n */\nexport interface ConnectionEvents {\n stateChange: (state: ConnectionState) => void;\n message: (data: ArrayBuffer) => void;\n error: (error: Error) => void;\n close: (event: CloseEvent) => void;\n}\n\n/**\n * Type for WebSocket implementation (browser or Node.js)\n */\ntype WebSocketImpl = typeof WebSocket | typeof import('ws').WebSocket;\n\n/**\n * Get the appropriate WebSocket implementation for the current environment\n */\nfunction getWebSocketImpl(): WebSocketImpl {\n // Browser environment\n if (typeof WebSocket !== 'undefined') {\n return WebSocket as WebSocketImpl;\n }\n\n // Node.js environment\n try {\n // Use dynamic import to avoid bundler issues\n const ws = require('ws');\n return ws.WebSocket || ws.default || ws;\n } catch (error) {\n throw new Error(\n 'WebSocket implementation not found. In Node.js, please install the \"ws\" package: npm install ws'\n );\n }\n}\n\n/**\n * Universal WebSocket connection manager\n */\nexport class Connection extends EventEmitter {\n private ws?: WebSocket;\n private url: string;\n private state: ConnectionState = ConnectionState.Disconnected;\n private reconnectManager: ReconnectManager;\n private heartbeatTimer?: NodeJS.Timeout;\n private heartbeatTimeoutTimer?: NodeJS.Timeout;\n private options: Required<Omit<ConnectionOptions, 'reconnectOptions' | 'protocols' | 'headers'>> & {\n protocols?: string | string[];\n headers?: Record<string, string>;\n };\n private WebSocketImpl: WebSocketImpl;\n private manualClose = false;\n\n constructor(url: string, options: ConnectionOptions = {}) {\n super();\n // Convert raisin:// / raisins:// to ws:// / wss:// for the WebSocket layer\n this.url = url\n .replace(/^raisin:\\/\\//, 'ws://')\n .replace(/^raisins:\\/\\//, 'wss://');\n this.options = {\n autoReconnect: options.autoReconnect ?? true,\n heartbeatInterval: options.heartbeatInterval ?? 30000,\n heartbeatTimeout: options.heartbeatTimeout ?? 5000,\n protocols: options.protocols,\n headers: options.headers,\n };\n this.reconnectManager = new ReconnectManager(options.reconnectOptions);\n this.WebSocketImpl = getWebSocketImpl();\n }\n\n /**\n * Connect to the WebSocket server\n */\n async connect(): Promise<void> {\n if (this.state === ConnectionState.Connected || this.state === ConnectionState.Connecting) {\n return;\n }\n\n this.manualClose = false;\n this.setState(ConnectionState.Connecting);\n\n return new Promise((resolve, reject) => {\n try {\n // Create WebSocket instance.\n // The third argument (upgrade headers) is only understood by the\n // Node.js `ws` package. Browsers do NOT simply ignore it - Chromium\n // fails the handshake when a third argument is passed - so only pass\n // it to non-browser implementations. Browser connections rely on the\n // server-side tenant resolution instead of upgrade headers.\n const isBrowserWebSocket =\n typeof WebSocket !== 'undefined' && this.WebSocketImpl === WebSocket;\n this.ws = (\n isBrowserWebSocket || !this.options.headers\n ? new (this.WebSocketImpl as any)(this.url, this.options.protocols)\n : new (this.WebSocketImpl as any)(this.url, this.options.protocols, {\n headers: this.options.headers,\n })\n ) as WebSocket;\n\n // Set binary type for MessagePack\n this.ws.binaryType = 'arraybuffer';\n\n // Handle open event\n this.ws.onopen = () => {\n this.setState(ConnectionState.Connected);\n this.reconnectManager.reset();\n this.startHeartbeat();\n resolve();\n };\n\n // Handle message event\n this.ws.onmessage = (event: MessageEvent) => {\n logger.debug(`WebSocket onmessage fired - data type: ${typeof event.data}, is ArrayBuffer: ${event.data instanceof ArrayBuffer}`);\n this.resetHeartbeat();\n const data = event.data as ArrayBuffer;\n logger.debug(`Received binary message - size: ${data.byteLength} bytes`);\n logger.debug(`Emitting 'message' event to client handlers`);\n this.emit('message', data);\n };\n\n // Handle error event\n this.ws.onerror = () => {\n const error = new Error('WebSocket error occurred');\n this.emit('error', error);\n if (this.state === ConnectionState.Connecting) {\n reject(error);\n }\n };\n\n // Handle close event\n this.ws.onclose = (event: CloseEvent) => {\n this.stopHeartbeat();\n this.emit('close', event);\n\n if (!this.manualClose && this.options.autoReconnect) {\n this.setState(ConnectionState.Reconnecting);\n this.reconnect();\n } else {\n this.setState(ConnectionState.Disconnected);\n }\n\n if (this.state === ConnectionState.Connecting) {\n reject(new Error(`Connection closed: ${event.reason || 'Unknown reason'}`));\n }\n };\n } catch (error) {\n this.setState(ConnectionState.Disconnected);\n reject(error);\n }\n });\n }\n\n /**\n * Disconnect from the WebSocket server\n */\n disconnect(): void {\n this.manualClose = true;\n this.reconnectManager.cancelReconnect();\n this.stopHeartbeat();\n\n if (this.ws) {\n this.ws.close(1000, 'Client disconnect');\n this.ws = undefined;\n }\n\n this.setState(ConnectionState.Closed);\n }\n\n /**\n * Send data through the WebSocket\n *\n * @param data - Data to send (Uint8Array for MessagePack)\n */\n send(data: Uint8Array): void {\n if (this.state !== ConnectionState.Connected || !this.ws) {\n throw new Error('Cannot send data: not connected');\n }\n\n // Check actual WebSocket readyState\n if (this.ws.readyState !== 1) { // WebSocket.OPEN = 1\n logger.error(`WebSocket not open - readyState: ${this.ws.readyState} (0=CONNECTING, 1=OPEN, 2=CLOSING, 3=CLOSED)`);\n throw new Error(`Cannot send data: WebSocket not open (readyState: ${this.ws.readyState})`);\n }\n\n logger.debug(`Sending message - size: ${data.length} bytes, readyState: ${this.ws.readyState}`);\n\n try {\n this.ws.send(data);\n logger.debug(`Message sent successfully`);\n } catch (error) {\n logger.error(`Failed to send message:`, error);\n throw new Error(`Failed to send data: ${error}`);\n }\n }\n\n /**\n * Get current connection state\n */\n getState(): ConnectionState {\n return this.state;\n }\n\n /**\n * Check if connected\n */\n isConnected(): boolean {\n return this.state === ConnectionState.Connected;\n }\n\n /**\n * Whether auto-reconnect is enabled for this connection.\n *\n * Used by the client to decide whether requests issued while the\n * connection is down can be queued (the connection will come back) or\n * should fail immediately.\n */\n isAutoReconnectEnabled(): boolean {\n return this.options.autoReconnect;\n }\n\n /**\n * Reconnect to the server\n */\n private reconnect(): void {\n const success = this.reconnectManager.scheduleReconnect(async () => {\n try {\n await this.connect();\n } catch (error) {\n // Error is already handled by connect()\n // Reconnection will be scheduled again if autoReconnect is enabled\n }\n });\n\n if (!success) {\n this.setState(ConnectionState.Disconnected);\n this.emit('error', new Error('Max reconnection attempts reached'));\n }\n }\n\n /**\n * Set connection state and emit event\n */\n private setState(state: ConnectionState): void {\n if (this.state !== state) {\n this.state = state;\n this.emit('stateChange', state);\n }\n }\n\n /**\n * Start heartbeat mechanism\n */\n private startHeartbeat(): void {\n if (this.options.heartbeatInterval <= 0) {\n return;\n }\n\n this.stopHeartbeat();\n\n // Note: WebSocket implementations (browser and ws library) automatically handle\n // ping/pong frames at the protocol level, so we don't need application-level heartbeat\n // that sends empty binary messages. The underlying WebSocket will keep the connection alive.\n }\n\n /**\n * Stop heartbeat mechanism\n */\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = undefined;\n }\n if (this.heartbeatTimeoutTimer) {\n clearTimeout(this.heartbeatTimeoutTimer);\n this.heartbeatTimeoutTimer = undefined;\n }\n }\n\n /**\n * Reset heartbeat timeout (called when message received)\n */\n private resetHeartbeat(): void {\n if (this.heartbeatTimeoutTimer) {\n clearTimeout(this.heartbeatTimeoutTimer);\n this.heartbeatTimeoutTimer = undefined;\n }\n }\n}\n","/**\n * User home path normalization.\n *\n * Some server responses (JWT claims, RAISIN_CURRENT_USER(), identity\n * endpoints) return the user home as a workspace-prefixed path like\n * `/raisin:access_control/users/internal/alice`, while others return the\n * workspace-relative form `/users/internal/alice`. SDK code that builds\n * child paths (inbox, chats, outbox) expects the workspace-relative form.\n */\n\n/** Workspace prefix that may leak into user home paths */\nconst ACCESS_CONTROL_PREFIX = '/raisin:access_control';\n\n/**\n * Normalize a user home path to its workspace-relative form.\n *\n * Strips a leading `/raisin:access_control` workspace prefix and trailing\n * slashes, so `/raisin:access_control/users/internal/alice/` becomes\n * `/users/internal/alice`. Paths without the prefix are returned unchanged\n * (modulo trailing-slash trimming). Returns `null` for empty input.\n *\n * @example\n * ```typescript\n * normalizeHomePath('/raisin:access_control/users/internal/alice');\n * // => '/users/internal/alice'\n * normalizeHomePath('/users/internal/alice');\n * // => '/users/internal/alice'\n * ```\n */\nexport function normalizeHomePath(home: string | null | undefined): string | null {\n if (home === null || home === undefined) return null;\n\n let path = home.trim();\n if (path.length === 0) return null;\n\n if (path === ACCESS_CONTROL_PREFIX) return '/';\n if (path.startsWith(`${ACCESS_CONTROL_PREFIX}/`)) {\n path = path.slice(ACCESS_CONTROL_PREFIX.length);\n }\n\n // Trim trailing slashes (but keep root \"/\")\n while (path.length > 1 && path.endsWith('/')) {\n path = path.slice(0, -1);\n }\n\n return path;\n}\n","/**\n * Plan projection utility.\n *\n * Builds deterministic plan/task state from persisted chat messages.\n */\n\nimport type { ChatMessage } from '../types/chat';\n\nexport interface PlanProjectionTask {\n taskId?: string;\n title: string;\n status: string;\n description?: string;\n priority?: string;\n delegation?: {\n id?: string;\n status: string;\n agentRef?: string;\n flowInstanceId?: string;\n branch?: string;\n baseBranch?: string;\n result?: unknown;\n error?: string;\n };\n}\n\nexport interface PlanProjection {\n key: string;\n planPath?: string;\n planId?: string;\n title: string;\n status: string;\n requiresApproval: boolean;\n tasks: PlanProjectionTask[];\n sourceMessagePath?: string;\n updatedAt?: string;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null;\n return value as Record<string, unknown>;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nexport function parsePlanTasks(rawTasks: unknown): PlanProjectionTask[] {\n if (!Array.isArray(rawTasks)) return [];\n return rawTasks\n // The return type is ANNOTATED, not inferred. `satisfies` on each branch\n // checks that branch but does not widen it, so without this the array is a\n // union of two object literals plus null — and `PlanProjectionTask` is not\n // assignable to that union (`taskId` is optional in the interface and\n // required in the inferred literal), which makes the type predicate below\n // illegal and fails the DTS build for the whole package.\n .map((rawTask, index): PlanProjectionTask | null => {\n if (typeof rawTask === 'string') {\n return {\n title: rawTask,\n status: 'pending',\n } satisfies PlanProjectionTask;\n }\n const task = asRecord(rawTask);\n if (!task) return null;\n const delegation = asRecord(task.delegation);\n return {\n taskId: asString(task.task_id) ?? asString(task.id),\n title: asString(task.title) ?? `Task ${index + 1}`,\n status: asString(task.status) ?? 'pending',\n description: asString(task.description),\n priority: asString(task.priority),\n delegation: delegation ? {\n id: asString(delegation.id),\n status: asString(delegation.status) ?? 'unknown',\n agentRef: asString(delegation.agent_ref) ?? asString(delegation.agentRef),\n flowInstanceId: asString(delegation.flow_instance_id) ?? asString(delegation.flowInstanceId),\n branch: asString(delegation.branch),\n baseBranch: asString(delegation.base_branch) ?? asString(delegation.baseBranch),\n result: delegation.result,\n error: asString(delegation.error),\n } : undefined,\n } satisfies PlanProjectionTask;\n })\n .filter((task): task is PlanProjectionTask => task !== null);\n}\n\nconst TERMINAL_TASK_STATUSES = new Set(['completed', 'cancelled']);\nconst TERMINAL_PLAN_STATUSES = new Set(['completed', 'failed', 'cancelled']);\n\n/**\n * Same race as upsertTask: an ai_task_update message carrying a stale\n * plan_status (e.g. \"in_progress\", computed before the final task's\n * completion count caught up) can be persisted and thus replayed here after\n * the message that actually reflects the terminal plan status. Guard it the\n * same way - never regress out of a terminal plan status.\n */\nfunction setPlanStatus(plan: PlanProjection, nextStatus: string | undefined): void {\n if (!nextStatus) return;\n if (TERMINAL_PLAN_STATUSES.has(plan.status) && !TERMINAL_PLAN_STATUSES.has(nextStatus)) {\n return;\n }\n plan.status = nextStatus;\n}\n\nexport function upsertTask(plan: PlanProjection, nextTask: PlanProjectionTask): void {\n const existingIndex = plan.tasks.findIndex((task) =>\n (nextTask.taskId && task.taskId === nextTask.taskId) ||\n (!nextTask.taskId && task.title === nextTask.title)\n );\n if (existingIndex === -1) {\n plan.tasks.push(nextTask);\n return;\n }\n const existing = plan.tasks[existingIndex];\n // Mirrors the server-side monotonic guard in update-task/index.js: tool\n // calls from one model turn can execute in parallel, so an ai_task_update\n // message for an earlier \"in_progress\" transition can be persisted (and\n // thus replayed here) after the \"completed\" message for the same task.\n // Never let that regress an already-terminal status.\n if (TERMINAL_TASK_STATUSES.has(existing.status) && !TERMINAL_TASK_STATUSES.has(nextTask.status)) {\n return;\n }\n plan.tasks[existingIndex] = {\n ...existing,\n ...nextTask,\n };\n}\n\nexport function ensurePlan(\n plans: Map<string, PlanProjection>,\n key: string,\n fallbackTitle: string,\n timestamp: string | undefined,\n): PlanProjection {\n const existing = plans.get(key);\n if (existing) {\n if (timestamp) existing.updatedAt = timestamp;\n return existing;\n }\n const created: PlanProjection = {\n key,\n title: fallbackTitle,\n status: 'pending_approval',\n requiresApproval: true,\n tasks: [],\n updatedAt: timestamp,\n };\n plans.set(key, created);\n return created;\n}\n\n/**\n * Build deterministic plan/task state from persisted chat messages.\n * Uses ai_plan as canonical plan source and ai_task_update for incremental task status changes.\n */\nexport function projectPlansFromMessages(messages: ChatMessage[]): PlanProjection[] {\n const ordered = [...messages].sort((a, b) => {\n const aTime = Date.parse(a.timestamp || '');\n const bTime = Date.parse(b.timestamp || '');\n return (Number.isNaN(aTime) ? 0 : aTime) - (Number.isNaN(bTime) ? 0 : bTime);\n });\n\n const plans = new Map<string, PlanProjection>();\n\n for (const message of ordered) {\n const timestamp = message.timestamp;\n const data = asRecord(message.data);\n\n if (message.messageType === 'ai_plan' && data) {\n const planPath = asString(data.plan_path);\n const planId = asString(data.plan_id);\n const key = planPath || planId || message.path || message.id || `plan:${timestamp}`;\n const plan = ensurePlan(\n plans,\n key,\n asString(data.title) || message.content || 'Plan',\n timestamp\n );\n\n plan.planPath = planPath ?? plan.planPath;\n plan.planId = planId ?? plan.planId;\n plan.title = asString(data.title) || plan.title;\n setPlanStatus(plan, asString(data.status));\n plan.requiresApproval = Boolean(data.requires_approval) || plan.status === 'pending_approval';\n plan.sourceMessagePath = message.path ?? plan.sourceMessagePath;\n\n const parsedTasks = parsePlanTasks(data.tasks);\n if (parsedTasks.length > 0) {\n plan.tasks = parsedTasks;\n }\n }\n\n if (message.messageType === 'ai_task_update' && data) {\n const planPath = asString(data.plan_path);\n const planId = asString(data.plan_id);\n const key = planPath || planId;\n if (!key) continue;\n\n const plan = ensurePlan(plans, key, 'Plan', timestamp);\n plan.planPath = planPath ?? plan.planPath;\n plan.planId = planId ?? plan.planId;\n setPlanStatus(plan, asString(data.plan_status));\n plan.requiresApproval = plan.status === 'pending_approval';\n\n const taskUpdate: PlanProjectionTask = {\n taskId: asString(data.task_id),\n title: asString(data.task_title) || asString(data.title) || 'Task',\n status: asString(data.status) || 'pending',\n };\n upsertTask(plan, taskUpdate);\n }\n\n if (Array.isArray(message.children)) {\n for (const child of message.children) {\n if (child.type !== 'plan') continue;\n const key = child.path || message.path || message.id || `plan-child:${child.id}:${timestamp}`;\n const plan = ensurePlan(plans, key, child.planTitle || message.content || 'Plan', timestamp);\n plan.title = child.planTitle || plan.title;\n plan.status = child.status || plan.status;\n plan.requiresApproval = plan.status === 'pending_approval';\n if (Array.isArray(child.tasks) && child.tasks.length > 0) {\n plan.tasks = child.tasks.map((task) => ({\n taskId: task.id,\n title: task.title,\n status: task.status,\n delegation: task.delegation ? {\n id: task.delegation.id,\n status: task.delegation.status,\n agentRef: task.delegation.agentRef,\n flowInstanceId: task.delegation.flowInstanceId,\n branch: task.delegation.branch,\n baseBranch: task.delegation.baseBranch,\n result: task.delegation.result,\n error: task.delegation.error,\n } : undefined,\n }));\n }\n }\n }\n }\n\n // Belt-and-suspenders: if every task landed in a terminal state but the\n // plan's own status field is stuck non-terminal (the same message-race\n // that setPlanStatus guards against can also leave it simply never\n // updated), derive completion from the tasks - mirrors the server's\n // auto-complete check in update-task/index.js's updatePlanProgress.\n for (const plan of plans.values()) {\n if (\n !TERMINAL_PLAN_STATUSES.has(plan.status) &&\n plan.tasks.length > 0 &&\n plan.tasks.every((task) => TERMINAL_TASK_STATUSES.has(task.status))\n ) {\n plan.status = 'completed';\n plan.requiresApproval = false;\n }\n }\n\n return [...plans.values()].sort((a, b) => {\n const aTime = Date.parse(a.updatedAt || '');\n const bTime = Date.parse(b.updatedAt || '');\n return (Number.isNaN(bTime) ? 0 : bTime) - (Number.isNaN(aTime) ? 0 : aTime);\n });\n}\n","import type { ChatDoneEvent, ChatEvent } from '../types/chat';\n\n/**\n * Detect safety-net done events emitted by backend recovery paths.\n *\n * These events can appear while async tool calls are still running.\n * They carry `recovered: true` and an empty content payload.\n */\nexport function isRecoveredDoneEvent(event: ChatEvent): event is ChatDoneEvent & { recovered: true } {\n if (event.type !== 'done') return false;\n const done = event as ChatDoneEvent & { recovered?: unknown };\n if (done.recovered !== true) return false;\n return typeof done.content !== 'string' || done.content.trim().length === 0;\n}\n\n","/**\n * Framework-agnostic conversation store for RaisinDB.\n *\n * Manages the full conversation lifecycle: creating conversations, sending\n * messages, streaming responses, persistent SSE subscriptions, message\n * history, tool call tracking, and plan projection. Notifies subscribers\n * on every state change so UI frameworks (React, Svelte, Vue) can bind.\n *\n * @example Svelte 5:\n * ```typescript\n * const store = new ConversationStore({ database: db, conversationPath: '/...' });\n * let snapshot = $state(store.getSnapshot());\n * store.subscribe(s => { snapshot = s; });\n * ```\n *\n * @example React (via useConversation hook):\n * ```tsx\n * const chat = useConversation(React, { database: db, conversationPath: '/...' });\n * ```\n */\n\nimport type { ConversationManager, ConversationSubscription } from '../conversations';\nimport type { StartAnonymousOptions } from '../visitor-chat';\nimport type { Database } from '../database';\nimport type { ChatMessage, ChatEvent, ChatLogEvent } from '../types/chat';\nimport { logger, getLogLevel, LogLevel } from '../logger';\nimport { projectPlansFromMessages, type PlanProjection } from '../utils/plan-projection';\nimport { isRecoveredDoneEvent } from '../utils/chat-events';\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Info about an in-flight tool call */\nexport interface ToolCallInfo {\n id: string;\n functionName: string;\n arguments: unknown;\n status: 'running' | 'completed' | 'failed';\n result?: unknown;\n durationMs?: number;\n}\n\n/** Snapshot of the conversation store state */\nexport interface ConversationStoreSnapshot {\n /** The conversation object, if loaded */\n conversation: { conversationPath: string; type: string } | null;\n /** All messages in the conversation */\n messages: ChatMessage[];\n /** Whether the AI is currently generating a response */\n isStreaming: boolean;\n /** Whether the chat is waiting for user input */\n isWaiting: boolean;\n /** Accumulated text from the current streaming response */\n streamingText: string;\n /** Current error, if any */\n error: string | null;\n /** Tracks in-flight tool executions */\n activeToolCalls: ToolCallInfo[];\n /** Deterministic plan/task projection from persisted messages */\n plans: PlanProjection[];\n /** Whether messages are being loaded */\n isLoading: boolean;\n /** Path to the conversation node */\n conversationPath: string | null;\n}\n\n/** Options for creating a ConversationStore */\nexport interface ConversationStoreOptions {\n /** Pre-configured Database instance */\n database: Database;\n /** Resume an existing conversation */\n conversationPath?: string;\n /** Create a new conversation on first message */\n createOptions?: {\n participant: string;\n input?: Record<string, unknown>;\n /**\n * Chat WITHOUT login (a public website): starts or resumes an anonymous\n * visitor conversation (`db.conversations.startAnonymous`). `true`, or the\n * start options. The agent must allow anonymous visitors.\n */\n anonymous?: boolean | StartAnonymousOptions;\n };\n /** Callback for individual chat events */\n onEvent?: (event: ChatEvent) => void;\n /** Streaming inactivity timeout in ms (default: 120000). Auto-recovers when no SSE event arrives within this window. */\n streamingTimeoutMs?: number;\n /** Activity watchdog polling interval in ms (default: 30000). Periodically checks backend turn health while streaming. */\n watchdogIntervalMs?: number;\n}\n\ntype Subscriber = (snapshot: ConversationStoreSnapshot) => void;\n\n// ---------------------------------------------------------------------------\n// ConversationStore\n// ---------------------------------------------------------------------------\n\n/**\n * Framework-agnostic store managing a single conversation's state.\n *\n * Handles the full lifecycle: creating conversations, sending messages,\n * streaming responses, tool call tracking, plan projection, and deferred\n * message reload on turn completion.\n */\nexport class ConversationStore {\n private manager: ConversationManager;\n private subscribers = new Set<Subscriber>();\n private onEvent?: (event: ChatEvent) => void;\n private createOptions?: ConversationStoreOptions['createOptions'];\n\n // Persistent SSE subscription\n private subscription: ConversationSubscription | null = null;\n\n // Internal mutable state\n private _conversationPath: string | null;\n private _messages: ChatMessage[] = [];\n private _isStreaming = false;\n private _isWaiting = false;\n private _streamingText = '';\n private _error: string | null = null;\n private _activeToolCalls: ToolCallInfo[] = [];\n private _isLoading = false;\n private _conversationType: string | null = null;\n\n // Streaming timeout (Fix A)\n private _streamingTimeoutMs: number;\n private _streamingTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Activity watchdog (Fix B)\n private _watchdogIntervalMs: number;\n private _watchdogTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(options: ConversationStoreOptions) {\n this.manager = options.database.conversations;\n this._conversationPath = options.conversationPath ?? null;\n this.createOptions = options.createOptions;\n this.onEvent = options.onEvent;\n this._streamingTimeoutMs = options.streamingTimeoutMs ?? 120_000;\n this._watchdogIntervalMs = options.watchdogIntervalMs ?? 30_000;\n\n // Auto-subscribe to existing conversation\n if (this._conversationPath) {\n this.ensureSubscription();\n } else if (this.createOptions?.anonymous) {\n // An anonymous store resumes the tab's visitor conversation, if any.\n void this.resumeAnonymous();\n }\n }\n\n private anonymousOptions(): StartAnonymousOptions | null {\n const a = this.createOptions?.anonymous;\n if (!a) return null;\n return a === true ? {} : a;\n }\n\n /**\n * Resume the visitor conversation this tab already has (the session secret\n * and conversation id live in sessionStorage), and load its history. A tab\n * without one starts nothing until the first message.\n */\n private async resumeAnonymous(): Promise<void> {\n const opts = this.anonymousOptions();\n if (!opts || !this.createOptions || this._conversationPath) return;\n const agent = this.createOptions.participant;\n try {\n if (!this.manager.hasStoredAnonymous(agent, opts)) return;\n const convo = await this.manager.startAnonymous(agent, opts);\n if (this._conversationPath) return;\n this._conversationPath = convo.conversationPath;\n this._conversationType = convo.type;\n this.ensureSubscription();\n await this.loadMessages();\n } catch (err) {\n logger.debug('[ConversationStore] No visitor conversation to resume', err);\n }\n }\n\n // ==========================================================================\n // Subscription\n // ==========================================================================\n\n /**\n * Subscribe to state changes.\n * Returns an unsubscribe function.\n */\n subscribe(callback: Subscriber): () => void {\n this.subscribers.add(callback);\n callback(this.getSnapshot());\n return () => { this.subscribers.delete(callback); };\n }\n\n /**\n * Get the current state snapshot.\n */\n getSnapshot(): ConversationStoreSnapshot {\n return {\n conversation: this._conversationPath\n ? { conversationPath: this._conversationPath, type: this._conversationType || 'ai_chat' }\n : null,\n messages: [...this._messages],\n isStreaming: this._isStreaming,\n isWaiting: this._isWaiting,\n streamingText: this._streamingText,\n error: this._error,\n activeToolCalls: [...this._activeToolCalls],\n plans: projectPlansFromMessages(this._messages),\n isLoading: this._isLoading,\n conversationPath: this._conversationPath,\n };\n }\n\n // ==========================================================================\n // Actions\n // ==========================================================================\n\n /**\n * Send a user message and stream the AI response.\n *\n * If no conversation exists yet and `createOptions` was provided,\n * the conversation is created automatically on the first call.\n */\n async sendMessage(content: string): Promise<void> {\n if (this._isStreaming) {\n logger.warn('[ConversationStore] Cannot send message while streaming');\n return;\n }\n\n logger.info('[ConversationStore] Sending message', { path: this._conversationPath });\n this._error = null;\n\n // Create conversation if needed\n if (!this._conversationPath) {\n if (!this.createOptions) {\n throw new Error('No conversationPath and no createOptions provided');\n }\n try {\n const anonymous = this.anonymousOptions();\n const convo = anonymous\n ? await this.manager.startAnonymous(this.createOptions.participant, anonymous)\n : await this.manager.create({\n participant: this.createOptions.participant,\n input: this.createOptions.input,\n });\n this._conversationPath = convo.conversationPath;\n this._conversationType = convo.type;\n\n for (const event of convo.initialEvents ?? []) {\n this.onEvent?.(event);\n this.handleEvent(event);\n }\n\n this.notify();\n } catch (err) {\n this._error = err instanceof Error ? err.message : String(err);\n this.notify();\n return;\n }\n }\n\n // Add user message optimistically\n const userMessage: ChatMessage = {\n role: 'user',\n content,\n timestamp: new Date().toISOString(),\n };\n this._messages.push(userMessage);\n\n // Start streaming\n this._isStreaming = true;\n this._isWaiting = false;\n this._streamingText = '';\n this._activeToolCalls = [];\n this.resetStreamingTimer();\n this.startWatchdog();\n this.notify();\n\n // Use persistent SSE subscription + create message node\n try {\n this.ensureSubscription();\n await this.subscription!.waitUntilConnected();\n await this.manager.createUserMessage(this._conversationPath!, content);\n // Events arrive via the persistent subscription → handleEvent\n } catch (err) {\n logger.error('[ConversationStore] Send failed', err);\n this._error = err instanceof Error ? err.message : String(err);\n this._isStreaming = false;\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n }\n }\n\n /**\n * Load messages from the persisted node tree.\n * Useful for loading history on page reload.\n */\n async loadMessages(): Promise<ChatMessage[]> {\n if (!this._conversationPath) return [];\n this._isLoading = true;\n this.notify();\n try {\n const messages = await this.manager.getMessages(this._conversationPath);\n logger.debug(`[ConversationStore] Loaded ${messages.length} messages`);\n if (messages.length > 0) {\n this._messages = messages;\n // Rebuild tool call state from messages\n this._activeToolCalls = [];\n }\n return messages;\n } finally {\n this._isLoading = false;\n this.notify();\n }\n }\n\n /**\n * Approve a pending plan.\n */\n async approvePlan(planPath: string): Promise<void> {\n await this.manager.approvePlan(planPath);\n }\n\n /**\n * Reject a pending plan.\n */\n async rejectPlan(planPath: string, feedback?: string): Promise<void> {\n await this.manager.rejectPlan(planPath, feedback);\n }\n\n /**\n * Mark a single message as read.\n */\n async markMessageAsRead(messagePath: string): Promise<void> {\n await this.manager.markMessageAsRead(messagePath);\n }\n\n /**\n * Stop the current streaming response.\n */\n stop(): void {\n this._isStreaming = false;\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n }\n\n /**\n * Clean up the store and release resources.\n */\n destroy(): void {\n this.stop();\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.subscription?.unsubscribe();\n this.subscription = null;\n this.subscribers.clear();\n }\n\n /**\n * Get the conversation path.\n */\n getConversationPath(): string | null {\n return this._conversationPath;\n }\n\n // ==========================================================================\n // Internal\n // ==========================================================================\n\n private ensureSubscription(): void {\n if (this.subscription) return;\n if (!this._conversationPath) return;\n\n this.subscription = this.manager.subscribe(\n this._conversationPath,\n (event) => {\n this.onEvent?.(event);\n this.handleEvent(event);\n },\n );\n }\n\n private handleEvent(event: ChatEvent): void {\n logger.debug('[ConversationStore] SSE:', event.type);\n // Every SSE event resets the streaming inactivity timer\n if (this._isStreaming) {\n this.resetStreamingTimer();\n }\n switch (event.type) {\n case 'text_chunk':\n this.markStreamingActivity();\n this._streamingText += event.text;\n this.notify();\n break;\n\n case 'thought_chunk':\n this.markStreamingActivity();\n this.notify();\n break;\n\n case 'assistant_message':\n this._messages.push(event.message);\n this._streamingText = '';\n this.notify();\n break;\n\n case 'tool_call_started': {\n this.markStreamingActivity();\n logger.debug('[ConversationStore] Tool started:', event.functionName);\n const info: ToolCallInfo = {\n id: event.toolCallId,\n functionName: event.functionName,\n arguments: event.arguments,\n status: 'running',\n };\n this._activeToolCalls.push(info);\n this.notify();\n break;\n }\n\n case 'tool_call_completed': {\n this.markStreamingActivity();\n logger.debug('[ConversationStore] Tool completed:', event.toolCallId);\n const idx = this._activeToolCalls.findIndex(tc => tc.id === event.toolCallId);\n if (idx >= 0) {\n this._activeToolCalls[idx] = {\n ...this._activeToolCalls[idx],\n status: event.error ? 'failed' : 'completed',\n result: event.result,\n durationMs: event.durationMs,\n };\n }\n this.notify();\n break;\n }\n\n case 'waiting': {\n logger.info('[ConversationStore] Waiting for input');\n this.flushStreamingText(event.timestamp);\n this._isStreaming = false;\n this._isWaiting = true;\n this._activeToolCalls = [];\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n // Deferred reload: pick up canonical messages from node tree\n this.deferredReload();\n break;\n }\n\n case 'done': {\n if (isRecoveredDoneEvent(event)) {\n logger.debug('[ConversationStore] Ignoring recovered done event');\n this.reconcileRecoveredDone();\n break;\n }\n const doneEvent = event as import('../types/chat').ChatDoneEvent;\n if (doneEvent.dispatchPhase && doneEvent.dispatchPhase !== 'terminal') {\n logger.debug('[ConversationStore] Ignoring non-terminal done event');\n break;\n }\n logger.info('[ConversationStore] Turn done');\n this.flushStreamingText(event.timestamp);\n // Safety net: if streaming missed chunks, use the complete response from the event\n if (doneEvent.content && !this.lastMessageIsAssistant()) {\n this._messages.push({\n role: (doneEvent.role as ChatMessage['role']) || 'assistant',\n content: doneEvent.content,\n timestamp: event.timestamp,\n senderDisplayName: doneEvent.senderDisplayName,\n finishReason: doneEvent.finishReason,\n });\n }\n this._isStreaming = false;\n this._isWaiting = true;\n this._activeToolCalls = [];\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n this.deferredReload();\n break;\n }\n\n case 'completed': {\n this.flushStreamingText(event.timestamp);\n if (event.messages && event.messages.length > 0) {\n this._messages = event.messages;\n }\n this._isStreaming = false;\n this._isWaiting = false;\n this._activeToolCalls = [];\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n this.deferredReload();\n break;\n }\n\n case 'failed':\n logger.error('[ConversationStore] Turn failed', event.error);\n this._error = event.error;\n this._isStreaming = false;\n // A failed turn ends any waiting state too - otherwise UIs keyed on\n // isWaiting show a thinking indicator forever after a failure.\n this._isWaiting = false;\n this._streamingText = '';\n this._activeToolCalls = [];\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n break;\n\n case 'conversation_created':\n this._conversationPath = event.conversationPath;\n this.notify();\n break;\n\n case 'message_saved':\n // A message was persisted — reload to pick it up, then notify\n this.silentReload().catch(() => {});\n this.notify();\n break;\n\n case 'message_delivered':\n this._messages.push(event.message);\n this.notify();\n break;\n\n case 'log': {\n const logEvent = event as ChatLogEvent;\n const currentLevel = getLogLevel();\n const levelMap: Record<string, LogLevel> = {\n debug: LogLevel.Debug,\n info: LogLevel.Info,\n warn: LogLevel.Warn,\n error: LogLevel.Error,\n };\n const eventLevel = levelMap[logEvent.level] ?? LogLevel.Debug;\n if (currentLevel >= eventLevel) {\n const fn = logEvent.level === 'error' ? console.error\n : logEvent.level === 'warn' ? console.warn\n : logEvent.level === 'debug' ? console.debug\n : console.info;\n fn('[server]', logEvent.message);\n }\n break;\n }\n }\n }\n\n /**\n * Agent activity observed while the store thought the turn was over.\n *\n * This happens when execution continues WITHOUT a user message — plan\n * approval in approve_then_auto / step_by_step (approvePlan() returns an\n * enqueue receipt and the agent starts executing), or agent-initiated\n * follow-ups. The events arrive over the persistent live subscription\n * (no history replay), so activity is always \"now\": re-enter streaming\n * state so UIs show progress and the end-of-turn handling (done/waiting\n * → deferred reload) runs again for this continuation.\n */\n private markStreamingActivity(): void {\n if (this._isStreaming) return;\n logger.debug('[ConversationStore] Agent activity while idle — re-entering streaming state');\n this._isStreaming = true;\n this._isWaiting = false;\n this._streamingText = '';\n this.resetStreamingTimer();\n this.startWatchdog();\n }\n\n /**\n * If accumulated streaming text hasn't been captured in an assistant_message,\n * synthesize one.\n */\n private flushStreamingText(timestamp: string): void {\n if (this._streamingText && !this.lastMessageIsAssistant()) {\n this._messages.push({\n role: 'assistant',\n content: this._streamingText,\n timestamp,\n });\n }\n this._streamingText = '';\n }\n\n private lastMessageIsAssistant(): boolean {\n const last = this._messages[this._messages.length - 1];\n return last?.role === 'assistant';\n }\n\n /**\n * After a turn completes, reload canonical messages from the node tree\n * to catch any events missed during SSE gaps.\n */\n private deferredReload(): void {\n if (!this._conversationPath) return;\n logger.debug('[ConversationStore] Scheduling deferred reload');\n const attemptReload = (delay: number, retriesLeft: number) => {\n setTimeout(async () => {\n try {\n const prevCount = this._messages.length;\n await this.silentReload();\n // If no new messages appeared and retries remain, try again with exponential backoff\n if (this._messages.length === prevCount && retriesLeft > 0) {\n attemptReload(delay * 2, retriesLeft - 1);\n }\n } catch {}\n }, delay);\n };\n attemptReload(500, 2); // 500ms, 1000ms, 2000ms\n }\n\n private async silentReload(): Promise<void> {\n if (!this._conversationPath) return;\n try {\n const fresh = await this.manager.getMessages(this._conversationPath);\n if (fresh.length === 0) return;\n\n // Try incremental merge: match existing messages by role+content\n // to preserve object references (prevents Svelte DOM recreation)\n let matchCount = 0;\n const limit = Math.min(this._messages.length, fresh.length);\n for (let i = 0; i < limit; i++) {\n if (this._messages[i].role !== fresh[i].role ||\n this._messages[i].content !== fresh[i].content) break;\n matchCount++;\n }\n\n if (matchCount === this._messages.length && fresh.length === matchCount) {\n // Same content, possibly different metadata — update silently\n for (let i = 0; i < matchCount; i++) {\n const f = fresh[i];\n const e = this._messages[i];\n if (f.id) e.id = f.id;\n if (f.path) e.path = f.path;\n if (f.messageType) e.messageType = f.messageType;\n if (f.data) e.data = f.data;\n if (f.children) e.children = f.children;\n if (f.finishReason) e.finishReason = f.finishReason;\n }\n await this.rebuildToolCallState();\n return; // No visible change — don't notify\n }\n\n if (matchCount === this._messages.length && fresh.length > matchCount) {\n // Existing messages match prefix — append new ones\n for (let i = matchCount; i < fresh.length; i++) {\n this._messages.push(fresh[i]);\n }\n // Also update metadata on matched messages\n for (let i = 0; i < matchCount; i++) {\n const f = fresh[i];\n const e = this._messages[i];\n if (f.id) e.id = f.id;\n if (f.path) e.path = f.path;\n if (f.messageType) e.messageType = f.messageType;\n if (f.data) e.data = f.data;\n if (f.children) e.children = f.children;\n }\n await this.rebuildToolCallState();\n this.notify(); // New messages to show\n return;\n }\n\n // Fresh is a prefix subset — outbox delivery pending, keep extras\n if (matchCount === fresh.length && fresh.length < this._messages.length) {\n for (let i = 0; i < matchCount; i++) {\n const f = fresh[i];\n const e = this._messages[i];\n if (f.id) e.id = f.id;\n if (f.path) e.path = f.path;\n if (f.messageType) e.messageType = f.messageType;\n if (f.data) e.data = f.data;\n if (f.children) e.children = f.children;\n }\n await this.rebuildToolCallState();\n return; // Keep streaming-flushed extras — message_saved will trigger next reload\n }\n\n // Content mismatch — full replace\n this._messages = fresh;\n await this.rebuildToolCallState();\n this.notify();\n } catch (err) {\n logger.debug('[ConversationStore] Silent reload failed:', err);\n }\n }\n\n // ==========================================================================\n // Streaming timeout (Fix A) — auto-recover on SSE inactivity\n // ==========================================================================\n\n private resetStreamingTimer(): void {\n this.clearStreamingTimer();\n if (!this._isStreaming) return;\n this._streamingTimer = setTimeout(() => {\n if (this._isStreaming) {\n logger.warn('[ConversationStore] Streaming timeout — no SSE activity for', this._streamingTimeoutMs, 'ms');\n this.recoverFromHang();\n }\n }, this._streamingTimeoutMs);\n }\n\n private clearStreamingTimer(): void {\n if (this._streamingTimer) {\n clearTimeout(this._streamingTimer);\n this._streamingTimer = null;\n }\n }\n\n // ==========================================================================\n // Activity watchdog (Fix B) — periodic backend health check\n // ==========================================================================\n\n private startWatchdog(): void {\n this.stopWatchdog();\n if (!this._conversationPath) return;\n this._watchdogTimer = setInterval(async () => {\n if (!this._isStreaming || !this._conversationPath) {\n this.stopWatchdog();\n return;\n }\n try {\n const health = await this.manager.checkTurnHealth(this._conversationPath);\n if (health === 'done' && this._isStreaming) {\n logger.warn('[ConversationStore] Watchdog detected completed turn while still streaming — recovering');\n this.recoverFromHang();\n }\n } catch {\n // Ignore watchdog errors — it's a best-effort check\n }\n }, this._watchdogIntervalMs);\n }\n\n private stopWatchdog(): void {\n if (this._watchdogTimer) {\n clearInterval(this._watchdogTimer);\n this._watchdogTimer = null;\n }\n }\n\n // ==========================================================================\n // Hang recovery (shared by timeout + watchdog)\n // ==========================================================================\n\n private recoverFromHang(): void {\n logger.info('[ConversationStore] Recovering from hang');\n this.clearStreamingTimer();\n this.stopWatchdog();\n this._isStreaming = false;\n this._isWaiting = true;\n this._streamingText = '';\n this.notify();\n // Reload to pick up any persisted state we missed\n this.silentReload().catch(() => {});\n }\n\n /**\n * Reconcile a recovered done event against persisted state.\n * If the backend turn is actually complete, finalize the local state.\n * Otherwise keep streaming active and let normal events continue.\n */\n private reconcileRecoveredDone(): void {\n if (!this._conversationPath) return;\n void this.manager.checkTurnHealth(this._conversationPath).then((health) => {\n if (health !== 'done') return;\n if (!this._isStreaming) return;\n this._isStreaming = false;\n this._isWaiting = true;\n this._activeToolCalls = [];\n this.clearStreamingTimer();\n this.stopWatchdog();\n this.notify();\n this.deferredReload();\n }).catch(() => {\n // Best effort only; streaming watchdog still protects against hangs.\n });\n }\n\n // ==========================================================================\n // Tool call state rebuild (Fix C) — reconstruct from node tree on reload\n // ==========================================================================\n\n private async rebuildToolCallState(): Promise<void> {\n if (!this._conversationPath) {\n this._activeToolCalls = [];\n return;\n }\n try {\n const pendingCalls = await this.manager.getActiveToolCalls(this._conversationPath);\n if (pendingCalls.length > 0) {\n this._activeToolCalls = pendingCalls.map(tc => ({\n id: tc.id,\n functionName: tc.name,\n arguments: undefined,\n status: 'running' as const,\n }));\n // Keep streaming active if there are pending tool calls\n if (!this._isStreaming) {\n this._isStreaming = true;\n this.resetStreamingTimer();\n this.startWatchdog();\n }\n } else {\n this._activeToolCalls = [];\n // No pending tools — check if we should clear streaming\n if (this._isStreaming) {\n const lastMsg = this._messages[this._messages.length - 1];\n const isLastAssistantDone = lastMsg?.role === 'assistant' &&\n (lastMsg.dispatchPhase\n ? lastMsg.dispatchPhase === 'terminal'\n : (!!lastMsg.finishReason && lastMsg.finishReason !== 'tool_calls'));\n if (isLastAssistantDone) {\n this._isStreaming = false;\n this._isWaiting = true;\n this.clearStreamingTimer();\n this.stopWatchdog();\n }\n }\n }\n } catch {\n this._activeToolCalls = [];\n }\n }\n\n private notify(): void {\n const snapshot = this.getSnapshot();\n for (const sub of this.subscribers) {\n try { sub(snapshot); }\n catch (err) { logger.error('Error in conversation store subscriber:', err); }\n }\n }\n}\n","/**\n * Multi-conversation list store for RaisinDB.\n *\n * Manages the list of conversations: loading, creating, deleting, marking\n * as read, and optional real-time updates via WebSocket events.\n *\n * Provides a factory (`getConversationStore`) that returns cached\n * per-conversation `ConversationStore` instances.\n *\n * @example Svelte 5:\n * ```typescript\n * const listStore = new ConversationListStore({ database: db, realtime: true });\n * let snapshot = $state(listStore.getSnapshot());\n * listStore.subscribe(s => { snapshot = s; });\n * await listStore.load();\n * ```\n */\n\nimport type { Database } from '../database';\nimport type { ConversationType, ConversationListItem } from '../types/chat';\nimport { ConversationStore, type ConversationStoreOptions } from './conversation-store';\nimport { logger } from '../logger';\nimport { normalizeHomePath } from '../utils/home-path';\n\n/**\n * Build the realtime subscription filters for a user's chats folder.\n *\n * The server matches subscription paths literally with glob semantics:\n * `*` matches exactly one path segment, `**` matches recursively, and there\n * is NO implicit prefix matching — a plain path only matches that exact\n * node. To observe conversations created/updated under the chats folder we\n * therefore need the recursive `/**` suffix.\n *\n * Exported for testing.\n * @internal\n */\nexport function buildChatsSubscriptionFilters(userHome: string): {\n workspace: string;\n path: string;\n event_types: string[];\n} {\n const home = normalizeHomePath(userHome) ?? userHome;\n return {\n workspace: 'raisin:access_control',\n path: `${home}/inbox/chats/**`,\n event_types: ['node:created', 'node:updated'],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Snapshot of the conversation list state */\nexport interface ConversationListSnapshot {\n conversations: ConversationListItem[];\n totalUnreadCount: number;\n isLoading: boolean;\n error: string | null;\n}\n\n/** Options for creating a ConversationListStore */\nexport interface ConversationListStoreOptions {\n database: Database;\n /** Filter by conversation type */\n type?: ConversationType;\n /** Subscribe to WebSocket events for new conversations */\n realtime?: boolean;\n}\n\ntype Subscriber = (snapshot: ConversationListSnapshot) => void;\n\n// ---------------------------------------------------------------------------\n// ConversationListStore\n// ---------------------------------------------------------------------------\n\nexport class ConversationListStore {\n private database: Database;\n private type?: ConversationType;\n private realtime: boolean;\n private subscribers = new Set<Subscriber>();\n\n // Internal state\n private _conversations: ConversationListItem[] = [];\n private _isLoading = false;\n private _error: string | null = null;\n\n // Cached ConversationStore instances\n private _stores = new Map<string, ConversationStore>();\n\n // Real-time subscription\n private _realtimeSubscription: { unsubscribe: () => void } | null = null;\n\n constructor(options: ConversationListStoreOptions) {\n this.database = options.database;\n this.type = options.type;\n this.realtime = options.realtime ?? false;\n }\n\n // ==========================================================================\n // Subscription\n // ==========================================================================\n\n subscribe(callback: Subscriber): () => void {\n this.subscribers.add(callback);\n callback(this.getSnapshot());\n return () => { this.subscribers.delete(callback); };\n }\n\n getSnapshot(): ConversationListSnapshot {\n return {\n conversations: [...this._conversations],\n totalUnreadCount: this._conversations.reduce((sum, c) => sum + (c.unreadCount ?? 0), 0),\n isLoading: this._isLoading,\n error: this._error,\n };\n }\n\n // ==========================================================================\n // Actions\n // ==========================================================================\n\n /**\n * Load conversations from the server.\n */\n async load(): Promise<void> {\n this._isLoading = true;\n this._error = null;\n this.notify();\n\n try {\n const conversations = await this.database.conversations.list({\n type: this.type,\n });\n logger.debug(`[ConversationListStore] Loaded ${conversations.length} conversations`);\n this._conversations = conversations;\n\n if (this.realtime) {\n this.startRealtimeSubscription();\n }\n } catch (err) {\n logger.error('[ConversationListStore] Failed to load conversations', err);\n this._error = err instanceof Error ? err.message : String(err);\n } finally {\n this._isLoading = false;\n this.notify();\n }\n }\n\n /**\n * Create a new conversation.\n */\n async createConversation(options: {\n participant: string;\n subject?: string;\n input?: Record<string, unknown>;\n }): Promise<ConversationListItem> {\n const convo = await this.database.conversations.create(options);\n\n const listItem: ConversationListItem = {\n id: convo.id,\n type: convo.type,\n conversationPath: convo.conversationPath,\n conversationWorkspace: convo.conversationWorkspace,\n agentRef: convo.agentRef,\n participants: convo.participants,\n unreadCount: 0,\n updatedAt: new Date().toISOString(),\n };\n\n this._conversations = [listItem, ...this._conversations];\n this.notify();\n return listItem;\n }\n\n /**\n * Delete a conversation.\n */\n async deleteConversation(conversationPath: string): Promise<void> {\n // Destroy cached store\n const store = this._stores.get(conversationPath);\n if (store) {\n store.destroy();\n this._stores.delete(conversationPath);\n }\n\n await this.database.conversations.delete(conversationPath);\n this._conversations = this._conversations.filter(c => c.conversationPath !== conversationPath);\n this.notify();\n }\n\n /**\n * Mark a conversation as read.\n */\n async markAsRead(conversationPath: string): Promise<void> {\n await this.database.conversations.markAsRead(conversationPath);\n this._conversations = this._conversations.map(c =>\n c.conversationPath === conversationPath ? { ...c, unreadCount: 0 } : c\n );\n this.notify();\n }\n\n /**\n * Get or create a ConversationStore for a specific conversation.\n * Caches instances so repeated calls return the same store.\n */\n getConversationStore(\n conversationPath: string,\n overrides?: Partial<ConversationStoreOptions>,\n ): ConversationStore {\n let store = this._stores.get(conversationPath);\n if (!store) {\n store = new ConversationStore({\n database: this.database,\n conversationPath,\n ...overrides,\n });\n this._stores.set(conversationPath, store);\n }\n return store;\n }\n\n /**\n * Clean up all resources.\n */\n destroy(): void {\n this._realtimeSubscription?.unsubscribe();\n this._realtimeSubscription = null;\n\n for (const store of this._stores.values()) {\n store.destroy();\n }\n this._stores.clear();\n this.subscribers.clear();\n }\n\n // ==========================================================================\n // Internal\n // ==========================================================================\n\n private async startRealtimeSubscription(): Promise<void> {\n if (this._realtimeSubscription) return;\n logger.debug('[ConversationListStore] Starting realtime subscription');\n\n try {\n const workspace = this.database.workspace('raisin:access_control');\n const events = workspace.events();\n\n // We need the user home path to subscribe to the right folder\n const result = await this.database.executeSql(\n `SELECT RAISIN_CURRENT_USER()->>'path'::String as home`,\n );\n const rows = result.rows as unknown as Record<string, unknown>[];\n const userHome = rows?.[0]?.home as string;\n if (!userHome) return;\n\n this._realtimeSubscription = await events.subscribe(\n buildChatsSubscriptionFilters(userHome),\n async (event) => {\n const eventData = event as any;\n const nodeId = eventData.payload?.node_id;\n if (!nodeId) return;\n\n try {\n const nodeResult = await this.database.executeSql(\n `SELECT id, path, node_type, properties, created_at, updated_at FROM 'raisin:access_control' WHERE id = $1 LIMIT 1`,\n [nodeId],\n );\n const row = (nodeResult.rows as unknown as Record<string, unknown>[])?.[0];\n if (!row || row.node_type !== 'raisin:Conversation') return;\n\n const convPath = row.path as string;\n const props = (row.properties ?? {}) as Record<string, unknown>;\n const item: ConversationListItem = {\n id: row.id as string,\n type: (props.conversation_type as ConversationType) ?? 'ai_chat',\n conversationPath: convPath,\n conversationWorkspace: 'raisin:access_control',\n agentRef: props.agent_ref as string | undefined,\n participants: props.participants as string[] | undefined,\n unreadCount: (props.unread_count as number) ?? 0,\n lastMessage: props.last_message as { content: string; sender_id: string; created_at: string } | undefined,\n updatedAt: (row.updated_at ?? row.created_at) as string,\n };\n\n const existingIdx = this._conversations.findIndex(c => c.conversationPath === convPath);\n if (existingIdx >= 0) {\n // Update existing conversation and move to top\n this._conversations.splice(existingIdx, 1);\n }\n this._conversations = [item, ...this._conversations];\n this.notify();\n } catch (err) {\n logger.debug('Failed to process conversation event:', err);\n }\n },\n );\n } catch (err) {\n logger.debug('Failed to start realtime subscription:', err);\n }\n }\n\n private notify(): void {\n const snapshot = this.getSnapshot();\n for (const sub of this.subscribers) {\n try { sub(snapshot); }\n catch (err) { logger.error('Error in conversation list store subscriber:', err); }\n }\n }\n}\n"],"mappings":"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","names":["LogLevel","ConnectionState"]}
@@ -0,0 +1,248 @@
1
+ // src/integrations/svelte/svelte-auth.ts
2
+ function createAuthAdapter(client) {
3
+ let snapshot = {
4
+ user: client.getStoredUser(),
5
+ isAuthenticated: client.getStoredUser() !== null,
6
+ isLoading: false
7
+ };
8
+ const listeners = /* @__PURE__ */ new Set();
9
+ function emit() {
10
+ for (const cb of listeners) cb(snapshot);
11
+ }
12
+ function update(partial) {
13
+ snapshot = { ...snapshot, ...partial };
14
+ emit();
15
+ }
16
+ const unsubAuth = client.onAuthStateChange(({ event, session }) => {
17
+ if (event === "SIGNED_IN" || event === "TOKEN_REFRESHED" || event === "USER_UPDATED") {
18
+ update({ user: session.user, isAuthenticated: true });
19
+ } else if (event === "SIGNED_OUT" || event === "SESSION_EXPIRED") {
20
+ update({ user: null, isAuthenticated: false });
21
+ }
22
+ });
23
+ const unsubUser = client.onUserChange(({ node }) => {
24
+ if (!snapshot.user) return;
25
+ update({
26
+ user: {
27
+ ...snapshot.user,
28
+ home: node.path,
29
+ displayName: node.properties.display_name ?? snapshot.user.displayName,
30
+ avatarUrl: node.properties.avatar_url ?? snapshot.user.avatarUrl
31
+ }
32
+ });
33
+ });
34
+ return {
35
+ subscribe(cb) {
36
+ listeners.add(cb);
37
+ return () => {
38
+ listeners.delete(cb);
39
+ };
40
+ },
41
+ getSnapshot: () => snapshot,
42
+ async login(email, password, repository) {
43
+ update({ isLoading: true });
44
+ try {
45
+ return await client.loginWithEmail(email, password, repository);
46
+ } finally {
47
+ update({ isLoading: false });
48
+ }
49
+ },
50
+ async register(email, password, repository, displayName) {
51
+ update({ isLoading: true });
52
+ try {
53
+ return await client.registerWithEmail(email, password, repository, displayName);
54
+ } finally {
55
+ update({ isLoading: false });
56
+ }
57
+ },
58
+ async logout(options) {
59
+ update({ isLoading: true });
60
+ try {
61
+ await client.logout(options);
62
+ } finally {
63
+ update({ isLoading: false });
64
+ }
65
+ },
66
+ async initSession(repository) {
67
+ update({ isLoading: true });
68
+ try {
69
+ return await client.initSession(repository);
70
+ } finally {
71
+ update({ isLoading: false });
72
+ }
73
+ },
74
+ destroy() {
75
+ unsubAuth();
76
+ unsubUser();
77
+ listeners.clear();
78
+ }
79
+ };
80
+ }
81
+
82
+ // src/integrations/svelte/svelte-connection.ts
83
+ function createConnectionAdapter(client) {
84
+ let snapshot = {
85
+ state: client.getConnectionState(),
86
+ isConnected: client.getConnectionState() === "connected" /* Connected */,
87
+ isReady: client.isReady()
88
+ };
89
+ const listeners = /* @__PURE__ */ new Set();
90
+ function emit() {
91
+ for (const cb of listeners) cb(snapshot);
92
+ }
93
+ const unsubConn = client.onConnectionStateChange((newState) => {
94
+ snapshot = {
95
+ state: newState,
96
+ isConnected: newState === "connected" /* Connected */,
97
+ isReady: snapshot.isReady
98
+ };
99
+ emit();
100
+ });
101
+ const unsubReady = client.onReadyStateChange((ready) => {
102
+ snapshot = { ...snapshot, isReady: ready };
103
+ emit();
104
+ });
105
+ return {
106
+ subscribe(cb) {
107
+ listeners.add(cb);
108
+ return () => {
109
+ listeners.delete(cb);
110
+ };
111
+ },
112
+ getSnapshot: () => snapshot,
113
+ async connect() {
114
+ await client.connect();
115
+ },
116
+ disconnect() {
117
+ client.disconnect();
118
+ },
119
+ destroy() {
120
+ unsubConn();
121
+ unsubReady();
122
+ listeners.clear();
123
+ }
124
+ };
125
+ }
126
+
127
+ // src/integrations/svelte/svelte-sql.ts
128
+ function rowsToObjects(result) {
129
+ return result.rows;
130
+ }
131
+ function createSqlAdapter(database, sql, params, options) {
132
+ let snapshot = {
133
+ data: null,
134
+ isLoading: false,
135
+ error: null
136
+ };
137
+ const listeners = /* @__PURE__ */ new Set();
138
+ let destroyed = false;
139
+ function emit() {
140
+ for (const cb of listeners) cb(snapshot);
141
+ }
142
+ function update(partial) {
143
+ snapshot = { ...snapshot, ...partial };
144
+ emit();
145
+ }
146
+ async function fetchData() {
147
+ if (destroyed) return;
148
+ update({ isLoading: true, error: null });
149
+ try {
150
+ const result = await database.executeSql(sql, params);
151
+ if (!destroyed) {
152
+ update({ data: rowsToObjects(result), isLoading: false });
153
+ }
154
+ } catch (err) {
155
+ if (!destroyed) {
156
+ update({
157
+ error: err instanceof Error ? err : new Error(String(err)),
158
+ isLoading: false
159
+ });
160
+ }
161
+ }
162
+ }
163
+ const cleanups = [];
164
+ const enabled = options?.enabled !== false;
165
+ if (enabled) {
166
+ fetchData();
167
+ }
168
+ const refetchOnReconnect = options?.refetchOnReconnect !== false;
169
+ if (enabled && refetchOnReconnect) {
170
+ const client = database._client;
171
+ if (client?.onReconnected) {
172
+ const unsub = client.onReconnected(() => {
173
+ fetchData();
174
+ });
175
+ cleanups.push(unsub);
176
+ }
177
+ }
178
+ const realtime = options?.realtime;
179
+ if (enabled && realtime) {
180
+ let unsubscribe;
181
+ let cancelled = false;
182
+ database.events().subscribe(
183
+ {
184
+ workspace: realtime.workspace,
185
+ event_types: realtime.eventTypes,
186
+ path: realtime.path,
187
+ node_type: realtime.nodeType
188
+ },
189
+ () => {
190
+ fetchData();
191
+ }
192
+ ).then((sub) => {
193
+ if (cancelled) {
194
+ sub.unsubscribe();
195
+ } else {
196
+ unsubscribe = () => sub.unsubscribe();
197
+ }
198
+ });
199
+ cleanups.push(() => {
200
+ cancelled = true;
201
+ unsubscribe?.();
202
+ });
203
+ }
204
+ return {
205
+ subscribe(cb) {
206
+ listeners.add(cb);
207
+ return () => {
208
+ listeners.delete(cb);
209
+ };
210
+ },
211
+ getSnapshot: () => snapshot,
212
+ refetch: fetchData,
213
+ destroy() {
214
+ destroyed = true;
215
+ for (const cleanup of cleanups) cleanup();
216
+ listeners.clear();
217
+ }
218
+ };
219
+ }
220
+
221
+ // src/integrations/svelte/svelte-subscription.ts
222
+ function createSubscriptionAdapter(database, filters, callback) {
223
+ let unsubscribe;
224
+ let cancelled = false;
225
+ database.events().subscribe(filters, (event) => {
226
+ if (!cancelled) callback(event);
227
+ }).then((sub) => {
228
+ if (cancelled) {
229
+ sub.unsubscribe();
230
+ } else {
231
+ unsubscribe = () => sub.unsubscribe();
232
+ }
233
+ });
234
+ return {
235
+ destroy() {
236
+ cancelled = true;
237
+ unsubscribe?.();
238
+ }
239
+ };
240
+ }
241
+
242
+ export {
243
+ createAuthAdapter,
244
+ createConnectionAdapter,
245
+ createSqlAdapter,
246
+ createSubscriptionAdapter
247
+ };
248
+ //# sourceMappingURL=chunk-T2ZSNYFG.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/integrations/svelte/svelte-auth.ts","../src/integrations/svelte/svelte-connection.ts","../src/integrations/svelte/svelte-sql.ts","../src/integrations/svelte/svelte-subscription.ts"],"sourcesContent":["import type { RaisinClient } from '../../client';\nimport type { IdentityUser } from '../../auth';\n\nexport interface AuthSnapshot {\n user: IdentityUser | null;\n isAuthenticated: boolean;\n isLoading: boolean;\n}\n\nexport interface AuthAdapter {\n subscribe: (cb: (snapshot: AuthSnapshot) => void) => () => void;\n getSnapshot: () => AuthSnapshot;\n login: (email: string, password: string, repository: string) => Promise<IdentityUser>;\n register: (email: string, password: string, repository: string, displayName?: string) => Promise<IdentityUser>;\n logout: (options?: { disconnect?: boolean; reconnect?: boolean }) => Promise<void>;\n initSession: (repository: string) => Promise<IdentityUser | null>;\n destroy: () => void;\n}\n\n/**\n * Create an auth adapter for Svelte 5.\n *\n * Returns a plain object with `subscribe`, `getSnapshot`, auth actions, and `destroy`.\n * Bind `getSnapshot()` to `$state` in a `.svelte.ts` file for reactivity.\n *\n * @example\n * ```typescript\n * // lib/auth.svelte.ts\n * import { createAuthAdapter } from '@raisindb/client/svelte';\n * import { client } from '$lib/raisin';\n *\n * const adapter = createAuthAdapter(client);\n * let snapshot = $state(adapter.getSnapshot());\n * adapter.subscribe(s => { snapshot = s; });\n *\n * export const auth = {\n * get user() { return snapshot.user; },\n * get isAuthenticated() { return snapshot.isAuthenticated; },\n * get isLoading() { return snapshot.isLoading; },\n * login: adapter.login,\n * register: adapter.register,\n * logout: adapter.logout,\n * initSession: adapter.initSession,\n * };\n * ```\n */\nexport function createAuthAdapter(client: RaisinClient): AuthAdapter {\n let snapshot: AuthSnapshot = {\n user: client.getStoredUser(),\n isAuthenticated: client.getStoredUser() !== null,\n isLoading: false,\n };\n\n const listeners = new Set<(s: AuthSnapshot) => void>();\n\n function emit() {\n for (const cb of listeners) cb(snapshot);\n }\n\n function update(partial: Partial<AuthSnapshot>) {\n snapshot = { ...snapshot, ...partial };\n emit();\n }\n\n const unsubAuth = client.onAuthStateChange(({ event, session }) => {\n if (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED' || event === 'USER_UPDATED') {\n update({ user: session.user, isAuthenticated: true });\n } else if (event === 'SIGNED_OUT' || event === 'SESSION_EXPIRED') {\n update({ user: null, isAuthenticated: false });\n }\n });\n\n const unsubUser = client.onUserChange(({ node }) => {\n if (!snapshot.user) return;\n update({\n user: {\n ...snapshot.user,\n home: node.path,\n displayName: (node.properties.display_name as string) ?? snapshot.user.displayName,\n avatarUrl: (node.properties.avatar_url as string) ?? snapshot.user.avatarUrl,\n },\n });\n });\n\n return {\n subscribe(cb: (s: AuthSnapshot) => void) {\n listeners.add(cb);\n return () => { listeners.delete(cb); };\n },\n\n getSnapshot: () => snapshot,\n\n async login(email: string, password: string, repository: string) {\n update({ isLoading: true });\n try {\n return await client.loginWithEmail(email, password, repository);\n } finally {\n update({ isLoading: false });\n }\n },\n\n async register(email: string, password: string, repository: string, displayName?: string) {\n update({ isLoading: true });\n try {\n return await client.registerWithEmail(email, password, repository, displayName);\n } finally {\n update({ isLoading: false });\n }\n },\n\n async logout(options?: { disconnect?: boolean; reconnect?: boolean }) {\n update({ isLoading: true });\n try {\n await client.logout(options);\n } finally {\n update({ isLoading: false });\n }\n },\n\n async initSession(repository: string) {\n update({ isLoading: true });\n try {\n return await client.initSession(repository);\n } finally {\n update({ isLoading: false });\n }\n },\n\n destroy() {\n unsubAuth();\n unsubUser();\n listeners.clear();\n },\n };\n}\n","import type { RaisinClient } from '../../client';\nimport { ConnectionState } from '../../connection';\n\nexport interface ConnectionSnapshot {\n state: ConnectionState;\n isConnected: boolean;\n isReady: boolean;\n}\n\nexport interface ConnectionAdapter {\n subscribe: (cb: (snapshot: ConnectionSnapshot) => void) => () => void;\n getSnapshot: () => ConnectionSnapshot;\n connect: () => Promise<void>;\n disconnect: () => void;\n destroy: () => void;\n}\n\n/**\n * Create a connection adapter for Svelte 5.\n *\n * Tracks WebSocket connection state and ready state. Bind to `$state` in\n * a `.svelte.ts` file for reactivity.\n *\n * @example\n * ```typescript\n * // lib/connection.svelte.ts\n * import { createConnectionAdapter } from '@raisindb/client/svelte';\n * import { client } from '$lib/raisin';\n *\n * const adapter = createConnectionAdapter(client);\n * let snapshot = $state(adapter.getSnapshot());\n * adapter.subscribe(s => { snapshot = s; });\n *\n * export const connection = {\n * get state() { return snapshot.state; },\n * get isConnected() { return snapshot.isConnected; },\n * get isReady() { return snapshot.isReady; },\n * connect: adapter.connect,\n * disconnect: adapter.disconnect,\n * };\n * ```\n */\nexport function createConnectionAdapter(client: RaisinClient): ConnectionAdapter {\n let snapshot: ConnectionSnapshot = {\n state: client.getConnectionState(),\n isConnected: client.getConnectionState() === ConnectionState.Connected,\n isReady: client.isReady(),\n };\n\n const listeners = new Set<(s: ConnectionSnapshot) => void>();\n\n function emit() {\n for (const cb of listeners) cb(snapshot);\n }\n\n const unsubConn = client.onConnectionStateChange((newState) => {\n snapshot = {\n state: newState,\n isConnected: newState === ConnectionState.Connected,\n isReady: snapshot.isReady,\n };\n emit();\n });\n\n const unsubReady = client.onReadyStateChange((ready) => {\n snapshot = { ...snapshot, isReady: ready };\n emit();\n });\n\n return {\n subscribe(cb: (s: ConnectionSnapshot) => void) {\n listeners.add(cb);\n return () => { listeners.delete(cb); };\n },\n\n getSnapshot: () => snapshot,\n\n async connect() {\n await client.connect();\n },\n\n disconnect() {\n client.disconnect();\n },\n\n destroy() {\n unsubConn();\n unsubReady();\n listeners.clear();\n },\n };\n}\n","import type { Database } from '../../database';\nimport type { SqlResult } from '../../protocol';\n\nexport interface SqlAdapterOptions {\n enabled?: boolean;\n refetchOnReconnect?: boolean;\n realtime?: {\n workspace: string;\n eventTypes?: string[];\n path?: string;\n nodeType?: string;\n };\n}\n\nexport interface SqlSnapshot<T> {\n data: T[] | null;\n isLoading: boolean;\n error: Error | null;\n}\n\nexport interface SqlAdapter<T> {\n subscribe: (cb: (snapshot: SqlSnapshot<T>) => void) => () => void;\n getSnapshot: () => SqlSnapshot<T>;\n refetch: () => Promise<void>;\n destroy: () => void;\n}\n\nfunction rowsToObjects<T>(result: SqlResult): T[] {\n return result.rows as T[];\n}\n\n/**\n * Create a SQL query adapter for Svelte 5.\n *\n * Executes a SQL query and maintains reactive snapshot state. Supports\n * realtime subscriptions and automatic refetch on reconnect.\n *\n * @example\n * ```typescript\n * // lib/posts.svelte.ts\n * import { createSqlAdapter } from '@raisindb/client/svelte';\n * import { db } from '$lib/raisin';\n *\n * const adapter = createSqlAdapter<{ title: string }>(db, \"SELECT * FROM 'content'\", [], {\n * realtime: { workspace: 'content' },\n * });\n * let snapshot = $state(adapter.getSnapshot());\n * adapter.subscribe(s => { snapshot = s; });\n *\n * export const posts = {\n * get data() { return snapshot.data; },\n * get isLoading() { return snapshot.isLoading; },\n * get error() { return snapshot.error; },\n * refetch: adapter.refetch,\n * };\n * ```\n */\nexport function createSqlAdapter<T = Record<string, unknown>>(\n database: Database,\n sql: string,\n params?: unknown[],\n options?: SqlAdapterOptions,\n): SqlAdapter<T> {\n let snapshot: SqlSnapshot<T> = {\n data: null,\n isLoading: false,\n error: null,\n };\n\n const listeners = new Set<(s: SqlSnapshot<T>) => void>();\n let destroyed = false;\n\n function emit() {\n for (const cb of listeners) cb(snapshot);\n }\n\n function update(partial: Partial<SqlSnapshot<T>>) {\n snapshot = { ...snapshot, ...partial };\n emit();\n }\n\n async function fetchData() {\n if (destroyed) return;\n update({ isLoading: true, error: null });\n try {\n const result = await database.executeSql(sql, params);\n if (!destroyed) {\n update({ data: rowsToObjects<T>(result), isLoading: false });\n }\n } catch (err) {\n if (!destroyed) {\n update({\n error: err instanceof Error ? err : new Error(String(err)),\n isLoading: false,\n });\n }\n }\n }\n\n // Cleanup handles\n const cleanups: (() => void)[] = [];\n\n // Initial fetch\n const enabled = options?.enabled !== false;\n if (enabled) {\n fetchData();\n }\n\n // Refetch on reconnect\n const refetchOnReconnect = options?.refetchOnReconnect !== false;\n if (enabled && refetchOnReconnect) {\n const client = (database as any)._client;\n if (client?.onReconnected) {\n const unsub = client.onReconnected(() => { fetchData(); });\n cleanups.push(unsub);\n }\n }\n\n // Realtime subscription\n const realtime = options?.realtime;\n if (enabled && realtime) {\n let unsubscribe: (() => Promise<void>) | undefined;\n let cancelled = false;\n\n database.events().subscribe(\n {\n workspace: realtime.workspace,\n event_types: realtime.eventTypes,\n path: realtime.path,\n node_type: realtime.nodeType,\n },\n () => { fetchData(); },\n ).then((sub) => {\n if (cancelled) {\n sub.unsubscribe();\n } else {\n unsubscribe = () => sub.unsubscribe();\n }\n });\n\n cleanups.push(() => {\n cancelled = true;\n unsubscribe?.();\n });\n }\n\n return {\n subscribe(cb: (s: SqlSnapshot<T>) => void) {\n listeners.add(cb);\n return () => { listeners.delete(cb); };\n },\n\n getSnapshot: () => snapshot,\n refetch: fetchData,\n\n destroy() {\n destroyed = true;\n for (const cleanup of cleanups) cleanup();\n listeners.clear();\n },\n };\n}\n","import type { Database } from '../../database';\nimport type { EventMessage, SubscriptionFilters } from '../../protocol';\n\nexport interface SubscriptionAdapter {\n destroy: () => void;\n}\n\n/**\n * Create an event subscription adapter for Svelte 5.\n *\n * Pure side-effect adapter — subscribes to database events and invokes the\n * callback on each matching event. Call `destroy()` to unsubscribe.\n *\n * @example\n * ```typescript\n * // In a .svelte component's script block or .svelte.ts file\n * import { createSubscriptionAdapter } from '@raisindb/client/svelte';\n * import { db } from '$lib/raisin';\n *\n * const sub = createSubscriptionAdapter(\n * db,\n * { workspace: 'content', node_type: 'Post' },\n * (event) => { console.log('Post changed:', event); },\n * );\n *\n * // In onDestroy or $effect cleanup:\n * sub.destroy();\n * ```\n */\nexport function createSubscriptionAdapter(\n database: Database,\n filters: SubscriptionFilters,\n callback: (event: EventMessage) => void,\n): SubscriptionAdapter {\n let unsubscribe: (() => Promise<void>) | undefined;\n let cancelled = false;\n\n database.events().subscribe(filters, (event) => {\n if (!cancelled) callback(event);\n }).then((sub) => {\n if (cancelled) {\n sub.unsubscribe();\n } else {\n unsubscribe = () => sub.unsubscribe();\n }\n });\n\n return {\n destroy() {\n cancelled = true;\n unsubscribe?.();\n },\n };\n}\n"],"mappings":"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","names":[]}