deepspace 0.6.0 → 0.6.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.
- package/CHANGELOG.md +6 -0
- package/dist/cli.js +169 -55
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +26 -3
- package/dist/index.js +124 -13
- package/dist/index.js.map +1 -1
- package/features/cron/src/CronLogPage.tsx +3 -3
- package/package.json +2 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/env/index.ts","../src/shared/roles.ts","../src/shared/protocol/yjs.ts","../src/shared/protocol/constants.ts","../src/shared/protocol/messages.ts","../src/client/auth/DeepSpaceAuthProvider.tsx","../src/client/auth/client.ts","../src/client/auth/token.ts","../src/client/debug.ts","../src/client/auth/hooks.ts","../src/client/auth/AuthOverlay.tsx","../src/client/auth/helpers.tsx","../src/client/auth/GuestBanner.tsx","../src/client/storage/context.tsx","../src/client/storage/store.ts","../src/client/storage/record-matching.ts","../src/client/storage/serverErrors.ts","../src/client/storage/record-socket.ts","../src/client/storage/ScopeRegistry.tsx","../src/client/storage/RecordScope.tsx","../src/client/storage/ws-log.ts","../src/client/storage/hooks/useUser.ts","../src/client/storage/hooks/useQuery.ts","../src/client/storage/hooks/useMutations.ts","../src/client/storage/hooks/useUsers.ts","../src/client/storage/hooks/useYjs.ts","../src/client/storage/hooks/useUserLookup.ts","../src/client/storage/hooks/usePresence.ts","../src/client/storage/hooks/useYjsRoom.ts","../src/client/storage/hooks/useGameRoom.ts","../src/client/storage/hooks/useCanvas.ts","../src/client/storage/hooks/useCronMonitor.ts","../src/client/storage/hooks/useJobs.ts","../src/client/storage/hooks/usePresenceRoom.ts","../src/client/storage/connection-status.ts","../src/client/storage/useR2Files.ts","../src/client/storage/file-attachment-utils.ts","../src/client/storage/user-color.ts","../src/client/messaging/useMessages.ts","../src/client/messaging/useChannels.ts","../src/client/messaging/useReactions.ts","../src/client/messaging/useChannelMembers.ts","../src/client/messaging/useReadReceipts.ts","../src/client/messaging/useConversation.ts","../src/client/messaging/utils.ts","../src/client/messaging/conversation-utils.ts","../src/client/messaging/message-utils.ts","../src/client/directory/useConversations.ts","../src/client/directory/useCommunities.ts","../src/client/directory/usePosts.ts","../src/client/theme/DeepSpaceThemeProvider.tsx","../src/client/theme/applyTheme.ts","../src/client/theme/useIsDarkTheme.ts","../src/client/platform/PlatformProvider.tsx","../src/client/platform/usePlatformWS.ts","../src/client/integration/index.ts","../src/client/voice/useVoiceAgent.ts","../src/client/ai-stream.ts","../src/client/status.ts","../src/client/status-core.ts","../src/client/subscriptions/useSubscription.ts","../src/shared/subscription.ts","../src/client/subscriptions/PricingTable.tsx","../src/client/charges/useCheckout.ts"],"sourcesContent":["/**\n * Environment detection and URL configuration\n *\n * Determines environment and provides URLs for DeepSpace services.\n *\n * Environment detection priority:\n * 1. Build-time __DEEPSPACE_ENV__ define (set via esbuild/Vite)\n * 2. Runtime window.__DEEPSPACE_ENV__\n * 3. Server-side process.env.DEEPSPACE_ENV\n * 4. Hostname detection (fallback)\n */\n\nexport type Environment = 'dev' | 'prod'\n\ndeclare const __DEEPSPACE_ENV__: string | undefined\n\n// ============================================================================\n// Environment Configuration\n// ============================================================================\n\ninterface EnvironmentConfig {\n name: Environment\n /** Platform API worker URL */\n apiUrl: string\n /** Platform worker URL (RecordRoom, schema registry) */\n platformWorkerUrl: string\n /** Auth worker URL (Better Auth) */\n authUrl: string\n /** Auth sign-in page URL */\n authSignInUrl: string\n /** Auth sign-up page URL */\n authSignUpUrl: string\n /** Main DeepSpace app URL */\n mainAppUrl: string\n /** Builder dashboard URL */\n dashboardUrl: string\n}\n\nconst DEV_CONFIG: EnvironmentConfig = {\n name: 'dev',\n apiUrl: 'http://localhost:8795',\n platformWorkerUrl: 'http://localhost:8792',\n authUrl: 'http://localhost:8794',\n authSignInUrl: 'http://localhost:5173/sign-in',\n authSignUpUrl: 'http://localhost:5173/sign-up',\n mainAppUrl: 'http://localhost:5173',\n dashboardUrl: 'http://localhost:5174',\n}\n\nconst PROD_CONFIG: EnvironmentConfig = {\n name: 'prod',\n apiUrl: 'https://api-worker.deep.space',\n platformWorkerUrl: 'https://platform-worker.deep.space',\n authUrl: 'https://auth.deep.space',\n authSignInUrl: 'https://auth.deep.space/login/social',\n authSignUpUrl: 'https://auth.deep.space/login/social',\n mainAppUrl: 'https://deep.space',\n dashboardUrl: 'https://dashboard.deep.space',\n}\n\n// ============================================================================\n// Environment Detection\n// ============================================================================\n\nlet cachedEnvironment: Environment | null = null\n\nfunction parseEnv(value: string | undefined): Environment | null {\n if (!value) return null\n const v = value.toLowerCase()\n if (v === 'dev' || v === 'development') return 'dev'\n if (v === 'prod' || v === 'production') return 'prod'\n return null\n}\n\nexport function detectEnvironment(): Environment {\n if (cachedEnvironment) return cachedEnvironment\n\n // 1. Build-time define\n if (typeof __DEEPSPACE_ENV__ !== 'undefined') {\n const env = parseEnv(__DEEPSPACE_ENV__)\n if (env) {\n cachedEnvironment = env\n return env\n }\n }\n\n // 2. Runtime window variable\n if (typeof window !== 'undefined') {\n const windowEnv = (window as { __DEEPSPACE_ENV__?: string }).__DEEPSPACE_ENV__\n const env = parseEnv(windowEnv)\n if (env) {\n cachedEnvironment = env\n return env\n }\n }\n\n // 3. Server-side env var\n if (typeof window === 'undefined') {\n try {\n const env = parseEnv(process.env.DEEPSPACE_ENV)\n if (env) {\n cachedEnvironment = env\n return env\n }\n if (process.env.NODE_ENV !== 'production') {\n cachedEnvironment = 'dev'\n return 'dev'\n }\n } catch {\n // process may not exist in Workers\n }\n }\n\n // 4. Hostname detection\n if (typeof window !== 'undefined') {\n const hostname = window.location.hostname\n\n if (\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname.startsWith('192.168.')\n ) {\n cachedEnvironment = 'dev'\n return 'dev'\n }\n\n // Production: app.space, *.app.space, deep.space, *.deep.space\n if (\n hostname === 'app.space' ||\n hostname.endsWith('.app.space') ||\n hostname === 'deep.space' ||\n hostname.endsWith('.deep.space')\n ) {\n cachedEnvironment = 'prod'\n return 'prod'\n }\n }\n\n // Default to prod for unknown domains\n cachedEnvironment = 'prod'\n return 'prod'\n}\n\nexport function getEnvironmentConfig(): EnvironmentConfig {\n const env = detectEnvironment()\n return env === 'dev' ? DEV_CONFIG : PROD_CONFIG\n}\n\n// ============================================================================\n// Convenience Getters\n// ============================================================================\n\nexport function getApiUrl(): string {\n return getEnvironmentConfig().apiUrl\n}\n\nexport function getPlatformWorkerUrl(): string {\n return getEnvironmentConfig().platformWorkerUrl\n}\n\nexport function getAuthUrl(): string {\n return getEnvironmentConfig().authUrl\n}\n\nexport function isLocalDev(): boolean {\n return detectEnvironment() === 'dev'\n}\n\nexport function isProduction(): boolean {\n return detectEnvironment() === 'prod'\n}\n\n/** Reset cached environment (useful for testing) */\nexport function resetEnvironmentCache(): void {\n cachedEnvironment = null\n}\n\n// ============================================================================\n// Convenience Object\n// ============================================================================\n\nexport const ENV = {\n get current() {\n return detectEnvironment()\n },\n get config() {\n return getEnvironmentConfig()\n },\n get apiUrl() {\n return getApiUrl()\n },\n get platformWorkerUrl() {\n return getPlatformWorkerUrl()\n },\n get authUrl() {\n return getAuthUrl()\n },\n get isLocal() {\n return isLocalDev()\n },\n get isProd() {\n return isProduction()\n },\n} as const\n","/**\n * Standard DeepSpace role constants.\n *\n * Every DeepSpace app uses the same three roles.\n * Apps can import these instead of defining them locally.\n */\n\nexport const ROLES = {\n VIEWER: 'viewer',\n MEMBER: 'member',\n ADMIN: 'admin',\n} as const\n\nexport type Role = (typeof ROLES)[keyof typeof ROLES]\n\nexport const ROLE_CONFIG: Record<Role, { title: string; badgeVariant: string; description: string }> = {\n viewer: { title: 'Viewer', badgeVariant: 'secondary', description: 'Read-only access' },\n member: { title: 'Member', badgeVariant: 'default', description: 'Can create and edit own content' },\n admin: { title: 'Admin', badgeVariant: 'warning', description: 'Full access to all features' },\n}\n","/**\n * Yjs Sync Protocol Implementation\n * \n * This implements the y-protocols sync and awareness protocols without\n * depending on y-websocket internal paths. Makes it easier to bundle\n * and template.\n * \n * Protocol spec: https://github.com/yjs/y-protocols\n */\n\nimport * as Y from 'yjs'\n\n// Message type constants\nexport const MSG_SYNC = 0\nexport const MSG_AWARENESS = 1\n\n// Sync message subtypes\nexport const MSG_SYNC_STEP1 = 0 // Send state vector\nexport const MSG_SYNC_STEP2 = 1 // Send diff based on received state vector\nexport const MSG_SYNC_UPDATE = 2 // Send incremental update\n\n// ============================================================================\n// Binary Encoding Utilities (lib0 compatible)\n// ============================================================================\n\nexport function createEncoder(): { data: number[] } {\n return { data: [] }\n}\n\nexport function toUint8Array(encoder: { data: number[] }): Uint8Array {\n return new Uint8Array(encoder.data)\n}\n\nexport function writeVarUint(encoder: { data: number[] }, num: number): void {\n while (num > 0x7f) {\n encoder.data.push((num & 0x7f) | 0x80)\n num = Math.floor(num / 128)\n }\n encoder.data.push(num & 0x7f)\n}\n\nexport function writeVarUint8Array(encoder: { data: number[] }, arr: Uint8Array): void {\n writeVarUint(encoder, arr.length)\n for (let i = 0; i < arr.length; i++) {\n encoder.data.push(arr[i])\n }\n}\n\nexport function createDecoder(data: Uint8Array): { data: Uint8Array; pos: number } {\n return { data, pos: 0 }\n}\n\nexport function readVarUint(decoder: { data: Uint8Array; pos: number }): number {\n let num = 0\n let mult = 1\n const len = decoder.data.length\n while (decoder.pos < len) {\n const byte = decoder.data[decoder.pos++]\n num += (byte & 0x7f) * mult\n mult *= 128\n if (byte < 0x80) {\n return num\n }\n }\n throw new Error('Unexpected end of buffer')\n}\n\nexport function readVarUint8Array(decoder: { data: Uint8Array; pos: number }): Uint8Array {\n const len = readVarUint(decoder)\n const arr = decoder.data.subarray(decoder.pos, decoder.pos + len)\n decoder.pos += len\n return arr\n}\n\nexport function hasContent(decoder: { data: Uint8Array; pos: number }): boolean {\n return decoder.pos < decoder.data.length\n}\n\n// ============================================================================\n// Sync Protocol\n// ============================================================================\n\n/**\n * Encode sync step 1: our state vector\n */\nexport function encodeSyncStep1(doc: Y.Doc): Uint8Array {\n const encoder = createEncoder()\n writeVarUint(encoder, MSG_SYNC)\n writeVarUint(encoder, MSG_SYNC_STEP1)\n writeVarUint8Array(encoder, Y.encodeStateVector(doc))\n return toUint8Array(encoder)\n}\n\n/**\n * Encode sync step 2: diff based on received state vector\n */\nexport function encodeSyncStep2(doc: Y.Doc, stateVector: Uint8Array): Uint8Array {\n const encoder = createEncoder()\n writeVarUint(encoder, MSG_SYNC)\n writeVarUint(encoder, MSG_SYNC_STEP2)\n writeVarUint8Array(encoder, Y.encodeStateAsUpdate(doc, stateVector))\n return toUint8Array(encoder)\n}\n\n/**\n * Encode an incremental update\n */\nexport function encodeUpdate(update: Uint8Array): Uint8Array {\n const encoder = createEncoder()\n writeVarUint(encoder, MSG_SYNC)\n writeVarUint(encoder, MSG_SYNC_UPDATE)\n writeVarUint8Array(encoder, update)\n return toUint8Array(encoder)\n}\n\nexport interface SyncResult {\n type: 'step1' | 'step2' | 'update'\n response?: Uint8Array\n stateVector?: Uint8Array\n update?: Uint8Array\n}\n\n/**\n * Handle an incoming sync message\n * Returns response to send (if any) and whether this was an update\n */\nexport function handleSyncMessage(doc: Y.Doc, data: Uint8Array): SyncResult {\n const decoder = createDecoder(data)\n const messageType = readVarUint(decoder)\n \n if (messageType !== MSG_SYNC) {\n throw new Error(`Expected sync message (${MSG_SYNC}), got ${messageType}`)\n }\n \n const syncType = readVarUint(decoder)\n \n switch (syncType) {\n case MSG_SYNC_STEP1: {\n // Received state vector, send our diff\n const stateVector = readVarUint8Array(decoder)\n const response = encodeSyncStep2(doc, stateVector)\n return { type: 'step1', response, stateVector }\n }\n \n case MSG_SYNC_STEP2: {\n // Received diff, apply it\n const update = readVarUint8Array(decoder)\n Y.applyUpdate(doc, update)\n return { type: 'step2', update }\n }\n \n case MSG_SYNC_UPDATE: {\n // Received incremental update, apply it\n const update = readVarUint8Array(decoder)\n Y.applyUpdate(doc, update)\n return { type: 'update', update }\n }\n \n default:\n throw new Error(`Unknown sync type: ${syncType}`)\n }\n}\n\n// ============================================================================\n// Awareness Protocol\n// ============================================================================\n\nexport interface AwarenessState {\n [key: string]: unknown\n}\n\nexport interface AwarenessStates {\n [clientId: number]: AwarenessState\n}\n\n/**\n * Simple awareness implementation for presence/cursors\n */\nexport class Awareness {\n doc: Y.Doc\n clientID: number\n states: Map<number, AwarenessState> = new Map()\n private meta: Map<number, { clock: number; lastUpdated: number }> = new Map()\n private updateListeners: Set<(changes: { added: number[]; updated: number[]; removed: number[] }) => void> = new Set()\n private changeListeners: Set<(changes: { added: number[]; updated: number[]; removed: number[] }) => void> = new Set()\n\n constructor(doc: Y.Doc) {\n this.doc = doc\n this.clientID = doc.clientID\n }\n \n getStates(): Map<number, AwarenessState> {\n return this.states\n }\n \n getLocalState(): AwarenessState | null {\n return this.states.get(this.clientID) || null\n }\n \n setLocalState(state: AwarenessState | null): void {\n const clientID = this.clientID\n const prevState = this.states.get(clientID)\n \n if (state === null) {\n this.states.delete(clientID)\n this.meta.delete(clientID)\n if (prevState) {\n this.emit({ added: [], updated: [], removed: [clientID] })\n }\n } else {\n const meta = this.meta.get(clientID) || { clock: 0, lastUpdated: Date.now() }\n meta.clock++\n meta.lastUpdated = Date.now()\n this.meta.set(clientID, meta)\n this.states.set(clientID, state)\n this.emit({\n added: prevState ? [] : [clientID],\n updated: prevState ? [clientID] : [],\n removed: [],\n })\n }\n }\n \n setLocalStateField(field: string, value: unknown): void {\n const state = this.getLocalState() || {}\n state[field] = value\n this.setLocalState(state)\n }\n \n on(event: 'update' | 'change', handler: (changes: { added: number[]; updated: number[]; removed: number[] }) => void): void {\n if (event === 'update') {\n this.updateListeners.add(handler)\n } else if (event === 'change') {\n this.changeListeners.add(handler)\n }\n }\n\n off(event: 'update' | 'change', handler: (changes: { added: number[]; updated: number[]; removed: number[] }) => void): void {\n if (event === 'update') {\n this.updateListeners.delete(handler)\n } else if (event === 'change') {\n this.changeListeners.delete(handler)\n }\n }\n\n private emit(changes: { added: number[]; updated: number[]; removed: number[] }): void {\n for (const handler of this.updateListeners) {\n handler(changes)\n }\n if (changes.added.length > 0 || changes.updated.length > 0 || changes.removed.length > 0) {\n for (const handler of this.changeListeners) {\n handler(changes)\n }\n }\n }\n \n /**\n * Encode awareness update for specific clients\n */\n encodeUpdate(clientIds?: number[]): Uint8Array {\n const ids = clientIds || Array.from(this.states.keys())\n const encoder = createEncoder()\n writeVarUint(encoder, ids.length)\n \n for (const clientId of ids) {\n const state = this.states.get(clientId)\n const meta = this.meta.get(clientId) || { clock: 0, lastUpdated: Date.now() }\n \n writeVarUint(encoder, clientId)\n writeVarUint(encoder, meta.clock)\n \n const stateStr = state ? JSON.stringify(state) : 'null'\n const stateBytes = new TextEncoder().encode(stateStr)\n writeVarUint8Array(encoder, stateBytes)\n }\n \n return toUint8Array(encoder)\n }\n \n /**\n * Apply awareness update from another client\n */\n applyUpdate(data: Uint8Array): { added: number[]; updated: number[]; removed: number[] } {\n const decoder = createDecoder(data)\n const len = readVarUint(decoder)\n \n const added: number[] = []\n const updated: number[] = []\n const removed: number[] = []\n \n for (let i = 0; i < len; i++) {\n const clientId = readVarUint(decoder)\n const clock = readVarUint(decoder)\n const stateBytes = readVarUint8Array(decoder)\n const stateStr = new TextDecoder().decode(stateBytes)\n const state = stateStr === 'null' ? null : JSON.parse(stateStr) as AwarenessState\n \n const prevMeta = this.meta.get(clientId)\n const prevState = this.states.get(clientId)\n \n // Only apply if this is a newer update\n if (!prevMeta || prevMeta.clock < clock) {\n this.meta.set(clientId, { clock, lastUpdated: Date.now() })\n \n if (state === null) {\n this.states.delete(clientId)\n if (prevState) {\n removed.push(clientId)\n }\n } else {\n this.states.set(clientId, state)\n if (prevState) {\n updated.push(clientId)\n } else {\n added.push(clientId)\n }\n }\n }\n }\n \n if (added.length > 0 || updated.length > 0 || removed.length > 0) {\n this.emit({ added, updated, removed })\n }\n \n return { added, updated, removed }\n }\n \n /**\n * Remove awareness states for disconnected clients\n */\n removeStates(clientIds: number[]): void {\n const removed: number[] = []\n for (const clientId of clientIds) {\n if (this.states.has(clientId)) {\n this.states.delete(clientId)\n this.meta.delete(clientId)\n removed.push(clientId)\n }\n }\n if (removed.length > 0) {\n this.emit({ added: [], updated: [], removed })\n }\n }\n}\n\n/**\n * Encode a full awareness message\n */\nexport function encodeAwarenessMessage(awareness: Awareness, clientIds?: number[]): Uint8Array {\n const encoder = createEncoder()\n writeVarUint(encoder, MSG_AWARENESS)\n writeVarUint8Array(encoder, awareness.encodeUpdate(clientIds))\n return toUint8Array(encoder)\n}\n\n/**\n * Decode and apply awareness message\n */\nexport function handleAwarenessMessage(\n awareness: Awareness, \n data: Uint8Array\n): { added: number[]; updated: number[]; removed: number[] } {\n const decoder = createDecoder(data)\n const messageType = readVarUint(decoder)\n \n if (messageType !== MSG_AWARENESS) {\n throw new Error(`Expected awareness message (${MSG_AWARENESS}), got ${messageType}`)\n }\n \n const update = readVarUint8Array(decoder)\n return awareness.applyUpdate(update)\n}\n\n/**\n * Get message type from raw data\n */\nexport function getMessageType(data: Uint8Array): number {\n const decoder = createDecoder(data)\n return readVarUint(decoder)\n}\n\n","/**\n * Wire protocol constants.\n *\n * The JSON WebSocket protocol uses dotted string identifiers (e.g.\n * `\"game.input\"`) as the `type` discriminator. All message types are\n * grouped under a single `MSG` object so imports stay tidy:\n *\n * import { MSG, dispatch, clientBuild } from 'deepspace'\n *\n * dispatch<ServerMessage>(raw, {\n * [MSG.GAME_STATE]: (p) => { ... },\n * [MSG.GAME_TICK]: (p) => { ... },\n * })\n *\n * Each key's value is the on-wire string — grep-friendly, self-\n * documenting, and infinite per namespace. Adding a new message is one\n * line here plus one arm in the discriminated union in `./messages.ts`.\n *\n * Yjs binary protocol constants (`MSG_YJS_SYNC`, `MSG_YJS_AWARENESS`) are\n * intentionally kept numeric and separate from `MSG` — they ride a\n * binary WebSocket frame format and aren't part of the JSON dispatcher.\n */\n\nexport const MSG = {\n // ---- Records / CRUD --------------------------------------------------\n SUBSCRIBE: 'core.subscribe',\n UNSUBSCRIBE: 'core.unsubscribe',\n QUERY_RESULT: 'core.query_result',\n RECORD_CHANGE: 'core.record_change',\n PUT: 'core.put',\n DELETE: 'core.delete',\n ERROR: 'core.error',\n\n // ---- Users -----------------------------------------------------------\n USER_INFO: 'user.info',\n USER_LIST: 'user.list',\n SET_ROLE: 'user.set_role',\n USER_UPDATE: 'user.update',\n\n // ---- Auth signal -----------------------------------------------------\n // Server → client one-shot on connect from rooms that enforce role-based\n // writes (Canvas, Game, Cron). Tells the client whether this connection\n // can write so its hook can disable local write APIs and the UI can\n // render controls accordingly. Without this, a viewer's click would\n // round-trip to the server and silently come back as ERROR.\n AUTH: 'auth',\n\n // ---- Yjs JSON control frames (binary sync lives in ./yjs.ts) ---------\n YJS_JOIN: 'yjs.join',\n YJS_LEAVE: 'yjs.leave',\n\n // ---- Records room misc -----------------------------------------------\n ACK: 'records.ack',\n LIST_SCHEMAS: 'records.list_schemas',\n RESUBSCRIBE: 'records.resubscribe',\n\n // ---- GameRoom --------------------------------------------------------\n GAME_STATE: 'game.state',\n GAME_INPUT: 'game.input',\n GAME_PLAYER_JOIN: 'game.player_join',\n GAME_PLAYER_LEAVE: 'game.player_leave',\n GAME_PLAYER_READY: 'game.player_ready',\n GAME_START: 'game.start',\n GAME_END: 'game.end',\n GAME_TICK: 'game.tick',\n\n // ---- CanvasRoom ------------------------------------------------------\n CANVAS_SHAPES: 'canvas.shapes',\n CANVAS_ADD: 'canvas.add',\n CANVAS_MOVE: 'canvas.move',\n CANVAS_RESIZE: 'canvas.resize',\n CANVAS_DELETE: 'canvas.delete',\n CANVAS_UPDATE: 'canvas.update',\n CANVAS_VIEWPORT: 'canvas.viewport',\n CANVAS_UNDO: 'canvas.undo',\n CANVAS_REDO: 'canvas.redo',\n\n // ---- CronRoom --------------------------------------------------------\n CRON_TASKS: 'cron.tasks',\n CRON_HISTORY: 'cron.history',\n CRON_TRIGGER: 'cron.trigger',\n CRON_PAUSE: 'cron.pause',\n CRON_RESUME: 'cron.resume',\n CRON_STATUS: 'cron.status',\n\n // ---- JobRoom ---------------------------------------------------------\n // Background-job queue with durable, alarm-driven execution. See\n // server/rooms/job-room.ts. Clients enqueue and subscribe to updates;\n // the room broadcasts JOB_UPDATE for every status transition.\n JOB_ENQUEUE: 'job.enqueue',\n JOB_CANCEL: 'job.cancel',\n JOB_RETRY: 'job.retry',\n JOB_UPDATE: 'job.update',\n\n // ---- PresenceRoom ----------------------------------------------------\n PRESENCE_SYNC: 'presence.sync',\n PRESENCE_JOIN: 'presence.join',\n PRESENCE_LEAVE: 'presence.leave',\n PRESENCE_UPDATE: 'presence.update',\n\n // ---- Gateway multiplexing --------------------------------------------\n GW_SCOPE_CONNECT: 'gateway.scope_connect',\n GW_SCOPE_DISCONNECT: 'gateway.scope_disconnect',\n GW_SCOPE_ERROR: 'gateway.scope_error',\n GW_TOKEN_REFRESH: 'gateway.token_refresh',\n GW_USER_UPDATE: 'gateway.user_update',\n} as const\n\n/**\n * Type of any message type constant — the union of every string literal\n * stored in `MSG`. Useful when declaring functions that accept \"any known\n * message type\" without enumerating all 54 strings by hand.\n */\nexport type MsgType = (typeof MSG)[keyof typeof MSG]\n\n// ---------------------------------------------------------------------------\n// Yjs binary protocol (NOT part of MSG — see header comment)\n// ---------------------------------------------------------------------------\n\n/** Outer envelope id for binary-framed yjs sync messages. Varuint-encoded. */\nexport const MSG_YJS_SYNC = 22\n/** Outer envelope id for binary-framed yjs awareness messages. Varuint-encoded. */\nexport const MSG_YJS_AWARENESS = 23\n\n// ---------------------------------------------------------------------------\n// Role names — not wire messages, just the string values the auth layer uses\n// as role identifiers.\n// ---------------------------------------------------------------------------\n\n/** Role assigned to unauthenticated WebSocket connections */\nexport const ROLE_ANONYMOUS = 'viewer'\n/** Default role for newly registered authenticated users */\nexport const ROLE_DEFAULT = 'member'\n/** Admin role */\nexport const ROLE_ADMIN = 'admin'\n","/**\n * Typed wire-protocol layer — discriminated unions, typed builders, and a\n * type-safe dispatcher for every `MSG.*` the SDK understands.\n *\n * Why this exists\n * ---------------\n *\n * The string `MSG.*` constants in `./constants.ts` are the authoritative\n * wire protocol, but using them directly is error-prone: a typo picks the\n * wrong message with the wrong payload shape and fails silently at\n * runtime. This module pairs every constant with its payload type, so\n * that:\n *\n * 1. Building a message with `clientBuild.gameInput(...)` is payload-\n * checked at the call site — the compiler refuses to ship a wrong\n * shape.\n *\n * 2. Parsing an inbound message via `dispatch(raw, handlers)` narrows the\n * payload type inside each handler automatically, replacing the\n * unsafe `switch (msg.type) { case MSG.X: (payload as any).foo }`\n * pattern.\n *\n * 3. Tightening `BaseRoom.sendTo` / `BaseRoom.broadcast` /\n * `HandlerContext.send` / `SubscriptionContext.send` to accept\n * `ServerMessage` turns the type layer into enforcement: any room\n * that ships a payload inconsistent with its declared arm fails to\n * compile. Without that, the discriminated union is documentation,\n * not contract.\n *\n * 4. Adding a new `MSG.*` is localized: one entry in the discriminated\n * union, one builder function, one handler key in every dispatcher\n * that cares. No grep-and-fix across the codebase.\n *\n * 5. Apps can extend the SDK protocol without forking: `dispatch<M>` is\n * generic over any `M extends ProtocolMessage`, and builders are\n * plain objects so apps compose via spread (`{ ...clientBuild,\n * myMessage: ... }`).\n *\n * Direction split\n * ---------------\n *\n * Some message types carry different payloads depending on who's sending.\n * `MSG.GAME_START`, for example, is `{}` when the client requests a start\n * but `{ state, tick }` when the server broadcasts the start event.\n * `MSG.CANVAS_ADD` is a flat shape dict on the way in and a `{ shape }`\n * wrapper on the way out. Modelling these with one union would force\n * handlers to juggle a union payload — clunky and error-prone. Instead we\n * split by direction:\n *\n * - `ClientMessage` — what the client sends to the server\n * - `ServerMessage` — what the server sends to the client\n * - `ProtocolMessage = ClientMessage | ServerMessage` (for code that\n * really doesn't care — avoid when possible)\n *\n * Each side gets its own builder (`clientBuild` / `serverBuild`) and each\n * side's dispatcher is parameterised with the union it expects.\n *\n * Payload strictness\n * ------------------\n *\n * Where payload shapes are stable + narrow (ids, flags), we type them\n * precisely. Where they're opaque or escape the protocol layer (record\n * data blobs, Yjs binary frames, game-engine state), we use `unknown` and\n * defer narrowing to the caller. This is intentional: over-typing opaque\n * payloads would require the protocol layer to import application types\n * and defeat the \"thin wire contract\" goal.\n */\n\nimport { MSG } from './constants'\n\n// =============================================================================\n// Envelope + helpers\n// =============================================================================\n\n/**\n * The outer shape of every wire message. `T` is the string discriminator\n * (e.g. `\"game.input\"`) — keeping it as a generic literal type lets the\n * discriminated-union narrowing in `dispatch()` pick the right payload.\n *\n * Callers extending the protocol should pass a string-literal type for\n * `T`, not the widened `string`. `BaseMessage<string, P>` collapses the\n * discriminated union and handler-map key inference falls back to a\n * single untyped `string` key, losing all narrowing.\n */\nexport interface BaseMessage<T extends string, P> {\n type: T\n payload: P\n}\n\n/** Matches when a payload is intentionally empty — `{}` on the wire. */\ntype EmptyPayload = Record<string, never>\n\n/**\n * Frozen empty payload reused by every \"no args\" builder. Frozen so a\n * runaway consumer mutating `.payload` can't corrupt the shared instance\n * that every other call site sees.\n */\nconst EMPTY: EmptyPayload = Object.freeze({}) as EmptyPayload\n\n// =============================================================================\n// Client → Server messages\n// =============================================================================\n\n/**\n * Every message the client can legitimately send. Extend in app code with\n * a string-literal union arm:\n *\n * type MyClientMessage = ClientMessage | BaseMessage<'myapp.foo', { x: number }>\n */\nexport type ClientMessage =\n // ---- Records / CRUD ---------------------------------------------------\n | BaseMessage<typeof MSG.SUBSCRIBE, { subscriptionId: string; query: unknown }>\n | BaseMessage<typeof MSG.UNSUBSCRIBE, { subscriptionId: string }>\n | BaseMessage<\n typeof MSG.PUT,\n { collection: string; recordId: string; data: Record<string, unknown>; requestId?: string }\n >\n | BaseMessage<\n typeof MSG.DELETE,\n { collection: string; recordId: string; requestId?: string }\n >\n // ---- Users ------------------------------------------------------------\n | BaseMessage<typeof MSG.USER_LIST, EmptyPayload>\n | BaseMessage<typeof MSG.SET_ROLE, { userId: string; role: string }>\n | BaseMessage<typeof MSG.USER_UPDATE, EmptyPayload>\n | BaseMessage<typeof MSG.LIST_SCHEMAS, EmptyPayload>\n // ---- Yjs --------------------------------------------------------------\n | BaseMessage<typeof MSG.YJS_JOIN, { collection: string; recordId: string; fieldName: string }>\n | BaseMessage<typeof MSG.YJS_LEAVE, { collection: string; recordId: string; fieldName: string }>\n // Binary yjs sync/awareness frames use numeric `MSG_YJS_SYNC` /\n // `MSG_YJS_AWARENESS` envelope ids from `./constants.ts` and ride the\n // binary WebSocket channel. They bypass this typed layer entirely.\n // ---- Game -------------------------------------------------------------\n | BaseMessage<typeof MSG.GAME_INPUT, { action: string; data: Record<string, unknown> }>\n | BaseMessage<typeof MSG.GAME_PLAYER_READY, EmptyPayload>\n | BaseMessage<typeof MSG.GAME_START, EmptyPayload>\n | BaseMessage<typeof MSG.GAME_END, EmptyPayload>\n // ---- Canvas -----------------------------------------------------------\n // `CANVAS_ADD` sends a flat shape dict as the payload (not `{ shape }`);\n // the server wraps it in `{ shape }` on its rebroadcast. Keep the types\n // aligned with the real wire or `clientBuild` lies. See\n // server/rooms/canvas-room.ts for the server side of this asymmetry.\n // `unknown` here (rather than `Record<string, unknown>`) so closed\n // concrete shape types pass assignability without index signatures.\n | BaseMessage<typeof MSG.CANVAS_ADD, unknown>\n | BaseMessage<typeof MSG.CANVAS_MOVE, { shapeId: string; x: number; y: number }>\n | BaseMessage<\n typeof MSG.CANVAS_RESIZE,\n { shapeId: string; width: number; height: number; x?: number; y?: number }\n >\n | BaseMessage<typeof MSG.CANVAS_DELETE, { shapeId: string }>\n | BaseMessage<\n typeof MSG.CANVAS_UPDATE,\n { shapeId: string; props: Record<string, unknown> }\n >\n // Flat payload in the client→server direction, matching `CANVAS_ADD`.\n | BaseMessage<typeof MSG.CANVAS_VIEWPORT, unknown>\n | BaseMessage<typeof MSG.CANVAS_UNDO, EmptyPayload>\n | BaseMessage<typeof MSG.CANVAS_REDO, EmptyPayload>\n // ---- Cron -------------------------------------------------------------\n | BaseMessage<typeof MSG.CRON_TRIGGER, { taskName: string }>\n | BaseMessage<typeof MSG.CRON_PAUSE, { taskName: string }>\n | BaseMessage<typeof MSG.CRON_RESUME, { taskName: string }>\n // ---- Jobs -------------------------------------------------------------\n // `JOB_ENQUEUE` uses requestId/JOB_UPDATE-ack so the caller learns the\n // newly-assigned jobId.\n | BaseMessage<\n typeof MSG.JOB_ENQUEUE,\n {\n requestId: string\n type: string\n payload?: unknown\n maxAttempts?: number\n }\n >\n | BaseMessage<typeof MSG.JOB_CANCEL, { jobId: string }>\n | BaseMessage<typeof MSG.JOB_RETRY, { jobId: string }>\n // ---- Presence ---------------------------------------------------------\n // Flat state object — the server merges incoming fields into the peer's\n // stored state; there's no `{ state }` wrapper in the client direction.\n | BaseMessage<typeof MSG.PRESENCE_UPDATE, Record<string, unknown>>\n // ---- Gateway ----------------------------------------------------------\n | BaseMessage<typeof MSG.GW_SCOPE_CONNECT, { scopeType: string; scopeId: string }>\n | BaseMessage<typeof MSG.GW_SCOPE_DISCONNECT, { scopeType: string; scopeId: string }>\n | BaseMessage<typeof MSG.GW_TOKEN_REFRESH, { token: string }>\n\n// =============================================================================\n// Server → Client messages\n// =============================================================================\n\n/**\n * Every message the server can send. Room and handler `send` / `broadcast`\n * signatures are tightened to this union so outbound payloads are\n * compile-checked against the wire contract. As with `ClientMessage`,\n * extend via a string-literal union arm in app code when adding new\n * server-side broadcasts.\n */\nexport type ServerMessage =\n // ---- Records / CRUD ---------------------------------------------------\n | BaseMessage<\n typeof MSG.QUERY_RESULT,\n { subscriptionId: string; records: unknown[] }\n >\n | BaseMessage<\n typeof MSG.RECORD_CHANGE,\n {\n collection: string\n record: unknown\n // Real wire values are `create`/`update`/`delete`, not the\n // underlying storage op name. See `server/handlers/subscriptions.ts`.\n changeType: 'create' | 'update' | 'delete'\n }\n >\n // `subscriptionId` is optional because generic transport errors (bad\n // envelope, missing connection) have no subscription context. Specific\n // query failures include the id so the client can correlate.\n | BaseMessage<typeof MSG.ERROR, { error: string; subscriptionId?: string }>\n // ACK is a mutation ack — it carries the correlation `requestId` from\n // the inbound PUT/DELETE plus a success flag. Server/handlers/records.ts\n // is the authoritative send site.\n | BaseMessage<\n typeof MSG.ACK,\n | { requestId: string; success: true; recordId?: string }\n | { requestId: string; success: false; error: string }\n >\n | BaseMessage<typeof MSG.RESUBSCRIBE, EmptyPayload>\n | BaseMessage<typeof MSG.LIST_SCHEMAS, { schemas: unknown }>\n // ---- Auth -------------------------------------------------------------\n // Emitted once per connection by rooms that enforce role-based writes\n // (Canvas, Game, Cron). Client hooks store `canWrite` in state and\n // disable write APIs when false. See useCanvas / useGameRoom /\n // useCronMonitor for the consumer side.\n | BaseMessage<typeof MSG.AUTH, { canWrite: boolean }>\n // ---- Users ------------------------------------------------------------\n // `USER_INFO` carries an app-specific user object (column-shape varies\n // per deployment), so the wire type is opaque. Narrow at the consumer.\n | BaseMessage<typeof MSG.USER_INFO, unknown>\n | BaseMessage<typeof MSG.USER_LIST, { users: unknown[] }>\n // ---- Yjs (JSON frames; binary sync/awareness bypass this layer) -------\n | BaseMessage<\n typeof MSG.YJS_JOIN,\n { collection: string; recordId: string; fieldName: string; canWrite: boolean }\n >\n // ---- Game -------------------------------------------------------------\n | BaseMessage<\n typeof MSG.GAME_STATE,\n { state: unknown; tick: number; players: unknown[]; running: boolean }\n >\n | BaseMessage<typeof MSG.GAME_TICK, { state: unknown; tick: number }>\n | BaseMessage<typeof MSG.GAME_START, { state: unknown; tick: number }>\n | BaseMessage<typeof MSG.GAME_END, { state: unknown; tick: number }>\n | BaseMessage<typeof MSG.GAME_PLAYER_JOIN, { player: unknown }>\n | BaseMessage<typeof MSG.GAME_PLAYER_LEAVE, { userId: string }>\n | BaseMessage<typeof MSG.GAME_PLAYER_READY, { userId: string }>\n // ---- Canvas -----------------------------------------------------------\n // `CANVAS_SHAPES` carries both the shape list and the viewports snapshot\n // so new connections can restore a multi-user view in one message. See\n // server/rooms/canvas-room.ts:onConnect.\n | BaseMessage<\n typeof MSG.CANVAS_SHAPES,\n { shapes: unknown[]; viewports: unknown[] }\n >\n | BaseMessage<typeof MSG.CANVAS_ADD, { shape: unknown }>\n | BaseMessage<typeof MSG.CANVAS_MOVE, { shapeId: string; x: number; y: number }>\n | BaseMessage<\n typeof MSG.CANVAS_RESIZE,\n { shapeId: string; width: number; height: number; x?: number; y?: number }\n >\n | BaseMessage<typeof MSG.CANVAS_DELETE, { shapeId: string }>\n | BaseMessage<\n typeof MSG.CANVAS_UPDATE,\n { shapeId: string; props: Record<string, unknown> }\n >\n | BaseMessage<\n typeof MSG.CANVAS_VIEWPORT,\n { viewport: unknown } | { userId: string; removed: true }\n >\n // ---- Cron -------------------------------------------------------------\n // `CRON_STATUS` server broadcasts carry both tasks and recent history;\n // see server/rooms/cron-room.ts.\n | BaseMessage<typeof MSG.CRON_TASKS, { tasks: unknown }>\n | BaseMessage<typeof MSG.CRON_HISTORY, { history: unknown }>\n | BaseMessage<typeof MSG.CRON_STATUS, { tasks: unknown; recentHistory: unknown }>\n // ---- Jobs -------------------------------------------------------------\n // `JOB_UPDATE` is the single push channel — same payload shape carries\n // initial-snapshot, enqueue, progress, success, failure, cancel, and\n // retry events. `kind` discriminates client-side. See useJobs.\n | BaseMessage<\n typeof MSG.JOB_UPDATE,\n {\n kind: 'snapshot' | 'enqueued' | 'progress' | 'succeeded' | 'failed' | 'canceled' | 'retried'\n job?: unknown\n jobs?: unknown[]\n /** Echoes the enqueue requestId when the update is the enqueue ack. */\n requestId?: string\n }\n >\n // ---- Presence ---------------------------------------------------------\n | BaseMessage<typeof MSG.PRESENCE_SYNC, { peers: unknown[] }>\n | BaseMessage<typeof MSG.PRESENCE_JOIN, { peer: unknown }>\n | BaseMessage<typeof MSG.PRESENCE_LEAVE, { userId: string }>\n | BaseMessage<\n typeof MSG.PRESENCE_UPDATE,\n { userId: string; state: Record<string, unknown> }\n >\n // ---- Gateway ----------------------------------------------------------\n | BaseMessage<\n typeof MSG.GW_SCOPE_ERROR,\n { scopeType: string; scopeId: string; error: string }\n >\n | BaseMessage<typeof MSG.GW_USER_UPDATE, unknown>\n\n/**\n * Any wire message, regardless of direction. Use this only when the code\n * path genuinely doesn't care — otherwise pick `ClientMessage` or\n * `ServerMessage` so the handler's payload type is narrower.\n */\nexport type ProtocolMessage = ClientMessage | ServerMessage\n\n// =============================================================================\n// Typed builders\n// =============================================================================\n//\n// Each builder returns a `{ type, payload }` object typed as a specific arm\n// of `ClientMessage` / `ServerMessage`. Call sites that hand the result to\n// `JSON.stringify` ship exactly what the wire protocol expects.\n//\n// `as const` on every return preserves the literal string `type` value\n// (e.g. `\"game.input\"`), which is what the discriminated-union narrowing\n// in `dispatch` keys on. Without it TS widens to `string` and the handler\n// map collapses to a single untyped key.\n// =============================================================================\n\n/** Factories for every client → server message. */\nexport const clientBuild = {\n // Records / CRUD\n subscribe: (subscriptionId: string, query: unknown) =>\n ({ type: MSG.SUBSCRIBE, payload: { subscriptionId, query } }) as const,\n unsubscribe: (subscriptionId: string) =>\n ({ type: MSG.UNSUBSCRIBE, payload: { subscriptionId } }) as const,\n put: (\n collection: string,\n recordId: string,\n data: Record<string, unknown>,\n requestId?: string,\n ) =>\n ({\n type: MSG.PUT,\n payload: { collection, recordId, data, requestId },\n }) as const,\n remove: (collection: string, recordId: string, requestId?: string) =>\n ({ type: MSG.DELETE, payload: { collection, recordId, requestId } }) as const,\n // Users\n userList: () => ({ type: MSG.USER_LIST, payload: EMPTY }) as const,\n setRole: (userId: string, role: string) =>\n ({ type: MSG.SET_ROLE, payload: { userId, role } }) as const,\n userUpdate: () => ({ type: MSG.USER_UPDATE, payload: EMPTY }) as const,\n listSchemas: () => ({ type: MSG.LIST_SCHEMAS, payload: EMPTY }) as const,\n // Yjs\n yjsJoin: (collection: string, recordId: string, fieldName: string) =>\n ({ type: MSG.YJS_JOIN, payload: { collection, recordId, fieldName } }) as const,\n yjsLeave: (collection: string, recordId: string, fieldName: string) =>\n ({ type: MSG.YJS_LEAVE, payload: { collection, recordId, fieldName } }) as const,\n // Game\n gameInput: (action: string, data: Record<string, unknown> = {}) =>\n ({ type: MSG.GAME_INPUT, payload: { action, data } }) as const,\n gamePlayerReady: () => ({ type: MSG.GAME_PLAYER_READY, payload: EMPTY }) as const,\n gameStart: () => ({ type: MSG.GAME_START, payload: EMPTY }) as const,\n gameEnd: () => ({ type: MSG.GAME_END, payload: EMPTY }) as const,\n // Canvas — `CANVAS_ADD` / `CANVAS_VIEWPORT` payloads are flat (the shape\n // or viewport object is the whole payload).\n canvasAdd: (shape: Record<string, unknown>) =>\n ({ type: MSG.CANVAS_ADD, payload: shape }) as const,\n canvasMove: (shapeId: string, x: number, y: number) =>\n ({ type: MSG.CANVAS_MOVE, payload: { shapeId, x, y } }) as const,\n canvasResize: (\n shapeId: string,\n width: number,\n height: number,\n x?: number,\n y?: number,\n ) =>\n ({ type: MSG.CANVAS_RESIZE, payload: { shapeId, width, height, x, y } }) as const,\n canvasDelete: (shapeId: string) =>\n ({ type: MSG.CANVAS_DELETE, payload: { shapeId } }) as const,\n canvasUpdate: (shapeId: string, props: Record<string, unknown>) =>\n ({ type: MSG.CANVAS_UPDATE, payload: { shapeId, props } }) as const,\n canvasViewport: (viewport: Record<string, unknown>) =>\n ({ type: MSG.CANVAS_VIEWPORT, payload: viewport }) as const,\n canvasUndo: () => ({ type: MSG.CANVAS_UNDO, payload: EMPTY }) as const,\n canvasRedo: () => ({ type: MSG.CANVAS_REDO, payload: EMPTY }) as const,\n // Cron\n cronTrigger: (taskName: string) =>\n ({ type: MSG.CRON_TRIGGER, payload: { taskName } }) as const,\n cronPause: (taskName: string) =>\n ({ type: MSG.CRON_PAUSE, payload: { taskName } }) as const,\n cronResume: (taskName: string) =>\n ({ type: MSG.CRON_RESUME, payload: { taskName } }) as const,\n // Jobs\n jobEnqueue: (\n requestId: string,\n type: string,\n payload?: unknown,\n maxAttempts?: number,\n ) =>\n ({\n type: MSG.JOB_ENQUEUE,\n payload: { requestId, type, payload, maxAttempts },\n }) as const,\n jobCancel: (jobId: string) =>\n ({ type: MSG.JOB_CANCEL, payload: { jobId } }) as const,\n jobRetry: (jobId: string) =>\n ({ type: MSG.JOB_RETRY, payload: { jobId } }) as const,\n // Presence\n presenceUpdate: (state: Record<string, unknown>) =>\n ({ type: MSG.PRESENCE_UPDATE, payload: state }) as const,\n // Gateway\n gwScopeConnect: (scopeType: string, scopeId: string) =>\n ({ type: MSG.GW_SCOPE_CONNECT, payload: { scopeType, scopeId } }) as const,\n gwScopeDisconnect: (scopeType: string, scopeId: string) =>\n ({ type: MSG.GW_SCOPE_DISCONNECT, payload: { scopeType, scopeId } }) as const,\n gwTokenRefresh: (token: string) =>\n ({ type: MSG.GW_TOKEN_REFRESH, payload: { token } }) as const,\n}\n\n/** Factories for every server → client message. Used by room DOs and\n * handler modules so server broadcasts are payload-checked too. Opaque\n * slots (`record`, `shape`, `peer`, etc.) take `unknown` so concrete\n * domain types — `RecordResult`, `CanvasShape`, `MediaPeer` — can be\n * passed without casts. */\nexport const serverBuild = {\n // Records / CRUD\n queryResult: (subscriptionId: string, records: unknown[]) =>\n ({ type: MSG.QUERY_RESULT, payload: { subscriptionId, records } }) as const,\n recordChange: (\n collection: string,\n record: unknown,\n changeType: 'create' | 'update' | 'delete',\n ) =>\n ({ type: MSG.RECORD_CHANGE, payload: { collection, record, changeType } }) as const,\n error: (error: string, subscriptionId?: string) =>\n ({ type: MSG.ERROR, payload: { error, subscriptionId } }) as const,\n ackSuccess: (requestId: string, recordId?: string) =>\n ({\n type: MSG.ACK,\n payload: { requestId, success: true as const, recordId },\n }) as const,\n ackFailure: (requestId: string, error: string) =>\n ({\n type: MSG.ACK,\n payload: { requestId, success: false as const, error },\n }) as const,\n resubscribe: () => ({ type: MSG.RESUBSCRIBE, payload: EMPTY }) as const,\n schemas: (schemas: unknown) =>\n ({ type: MSG.LIST_SCHEMAS, payload: { schemas } }) as const,\n // Users\n userInfo: (user: unknown) =>\n ({ type: MSG.USER_INFO, payload: user }) as const,\n userList: (users: unknown[]) =>\n ({ type: MSG.USER_LIST, payload: { users } }) as const,\n // Yjs\n yjsJoin: (collection: string, recordId: string, fieldName: string, canWrite: boolean) =>\n ({\n type: MSG.YJS_JOIN,\n payload: { collection, recordId, fieldName, canWrite },\n }) as const,\n // Game\n gameState: (state: unknown, tick: number, players: unknown[], running: boolean) =>\n ({ type: MSG.GAME_STATE, payload: { state, tick, players, running } }) as const,\n gameTick: (state: unknown, tick: number) =>\n ({ type: MSG.GAME_TICK, payload: { state, tick } }) as const,\n gameStart: (state: unknown, tick: number) =>\n ({ type: MSG.GAME_START, payload: { state, tick } }) as const,\n gameEnd: (state: unknown, tick: number) =>\n ({ type: MSG.GAME_END, payload: { state, tick } }) as const,\n gamePlayerJoin: (player: unknown) =>\n ({ type: MSG.GAME_PLAYER_JOIN, payload: { player } }) as const,\n gamePlayerLeave: (userId: string) =>\n ({ type: MSG.GAME_PLAYER_LEAVE, payload: { userId } }) as const,\n gamePlayerReady: (userId: string) =>\n ({ type: MSG.GAME_PLAYER_READY, payload: { userId } }) as const,\n // Canvas\n canvasShapes: (shapes: unknown[], viewports: unknown[]) =>\n ({ type: MSG.CANVAS_SHAPES, payload: { shapes, viewports } }) as const,\n canvasAdd: (shape: unknown) =>\n ({ type: MSG.CANVAS_ADD, payload: { shape } }) as const,\n canvasMove: (shapeId: string, x: number, y: number) =>\n ({ type: MSG.CANVAS_MOVE, payload: { shapeId, x, y } }) as const,\n canvasResize: (\n shapeId: string,\n width: number,\n height: number,\n x?: number,\n y?: number,\n ) =>\n ({ type: MSG.CANVAS_RESIZE, payload: { shapeId, width, height, x, y } }) as const,\n canvasDelete: (shapeId: string) =>\n ({ type: MSG.CANVAS_DELETE, payload: { shapeId } }) as const,\n canvasUpdate: (shapeId: string, props: Record<string, unknown>) =>\n ({ type: MSG.CANVAS_UPDATE, payload: { shapeId, props } }) as const,\n canvasViewport: (viewport: unknown) =>\n ({ type: MSG.CANVAS_VIEWPORT, payload: { viewport } }) as const,\n canvasViewportRemoved: (userId: string) =>\n ({\n type: MSG.CANVAS_VIEWPORT,\n payload: { userId, removed: true as const },\n }) as const,\n // Cron\n cronTasks: (tasks: unknown) =>\n ({ type: MSG.CRON_TASKS, payload: { tasks } }) as const,\n cronHistory: (history: unknown) =>\n ({ type: MSG.CRON_HISTORY, payload: { history } }) as const,\n cronStatus: (tasks: unknown, recentHistory: unknown) =>\n ({ type: MSG.CRON_STATUS, payload: { tasks, recentHistory } }) as const,\n // Jobs\n jobSnapshot: (jobs: unknown[]) =>\n ({ type: MSG.JOB_UPDATE, payload: { kind: 'snapshot' as const, jobs } }) as const,\n jobEnqueued: (job: unknown, requestId?: string) =>\n ({\n type: MSG.JOB_UPDATE,\n payload: { kind: 'enqueued' as const, job, requestId },\n }) as const,\n jobProgress: (job: unknown) =>\n ({ type: MSG.JOB_UPDATE, payload: { kind: 'progress' as const, job } }) as const,\n jobSucceeded: (job: unknown) =>\n ({ type: MSG.JOB_UPDATE, payload: { kind: 'succeeded' as const, job } }) as const,\n jobFailed: (job: unknown) =>\n ({ type: MSG.JOB_UPDATE, payload: { kind: 'failed' as const, job } }) as const,\n jobCanceled: (job: unknown) =>\n ({ type: MSG.JOB_UPDATE, payload: { kind: 'canceled' as const, job } }) as const,\n jobRetried: (job: unknown) =>\n ({ type: MSG.JOB_UPDATE, payload: { kind: 'retried' as const, job } }) as const,\n // Presence\n presenceSync: (peers: unknown[]) =>\n ({ type: MSG.PRESENCE_SYNC, payload: { peers } }) as const,\n presenceJoin: (peer: unknown) =>\n ({ type: MSG.PRESENCE_JOIN, payload: { peer } }) as const,\n presenceLeave: (userId: string) =>\n ({ type: MSG.PRESENCE_LEAVE, payload: { userId } }) as const,\n presenceUpdate: (userId: string, state: Record<string, unknown>) =>\n ({ type: MSG.PRESENCE_UPDATE, payload: { userId, state } }) as const,\n // Gateway\n gwScopeError: (scopeType: string, scopeId: string, error: string) =>\n ({\n type: MSG.GW_SCOPE_ERROR,\n payload: { scopeType, scopeId, error },\n }) as const,\n gwUserUpdate: (user: unknown) =>\n ({ type: MSG.GW_USER_UPDATE, payload: user }) as const,\n}\n\n// =============================================================================\n// Typed dispatch\n// =============================================================================\n//\n// `dispatch` parses a raw WebSocket frame (or takes an already-parsed\n// object) and routes it to the right handler. Handlers are indexed by the\n// message's string `type`, and TS narrows the `payload` argument to the\n// correct arm of the message union automatically thanks to the\n// discriminated-union lookup.\n//\n// The generic `M` defaults to `ProtocolMessage` so ad-hoc uses get the\n// full SDK vocabulary for free, but apps can pass a narrower (or extended)\n// union:\n//\n// dispatch<ServerMessage>(raw, {\n// [MSG.GAME_STATE]: (p) => { /* p is narrowed */ },\n// [MSG.GAME_TICK]: (p) => { /* p is narrowed */ },\n// })\n//\n// // With an app-defined message (use a STRING LITERAL for the type\n// // parameter — `string` widens the union and kills narrowing):\n// type MyMsg =\n// | ServerMessage\n// | BaseMessage<'myapp.boss_spawn', { hp: number }>\n// dispatch<MyMsg>(raw, {\n// 'myapp.boss_spawn': (p) => { console.log(p.hp) },\n// })\n//\n// =============================================================================\n\n/**\n * Handler map keyed by a message's string `type`. Every key maps to the\n * payload type of the matching arm in `M`, so handlers get automatic\n * payload narrowing with no runtime checks.\n *\n * Narrowing requires each `M` arm to use a string-literal `T`; widening\n * `M` to `BaseMessage<string, unknown>` collapses all keys to a single\n * untyped `string` and the map becomes a plain `Record<string, (p: unknown)\n * => void>`.\n */\nexport type MessageHandlers<M extends BaseMessage<string, unknown> = ProtocolMessage> = {\n [K in M as K['type']]?: (payload: K['payload']) => void\n}\n\n/**\n * Parse and route an incoming wire message. Accepts either the raw JSON\n * string (directly from `ws.onmessage`'s `event.data`) or an already-\n * parsed object. Returns `true` if a handler ran, `false` otherwise\n * (unrecognised type, parse failure, or malformed envelope).\n *\n * Defaults `M` to `ProtocolMessage` so callers who don't care about\n * direction can just do `dispatch(raw, handlers)`; narrow with\n * `dispatch<ClientMessage>` / `dispatch<ServerMessage>` to restrict which\n * arms the handler map is allowed to cover.\n */\nexport function dispatch<M extends BaseMessage<string, unknown> = ProtocolMessage>(\n raw: unknown,\n handlers: MessageHandlers<M>,\n): boolean {\n let msg: { type?: unknown; payload?: unknown }\n if (typeof raw === 'string') {\n try {\n msg = JSON.parse(raw) as { type?: unknown; payload?: unknown }\n } catch {\n return false\n }\n } else if (raw && typeof raw === 'object') {\n msg = raw as { type?: unknown; payload?: unknown }\n } else {\n return false\n }\n if (typeof msg.type !== 'string') return false\n const handler = (handlers as Record<string, ((payload: unknown) => void) | undefined>)[\n msg.type\n ]\n if (!handler) return false\n handler(msg.payload)\n return true\n}\n\n/**\n * Serialise a typed-built message for `ws.send`. A tiny wrapper around\n * `JSON.stringify` kept around so call sites read as\n * `ws.send(encode(clientBuild.gameInput(...)))` rather than paying\n * attention to stringify arguments.\n */\nexport function encode<M extends BaseMessage<string, unknown>>(message: M): string {\n return JSON.stringify(message)\n}\n","/**\n * DeepSpace Auth Provider.\n *\n * Wraps the app with the Better Auth session hook so children can read\n * the current user via `useAuth` / `useAuthUser`. Sessions ride a\n * same-origin cookie so there's no satellite-domain configuration.\n */\n\nimport React, { useEffect, useRef } from 'react'\nimport { useSession } from './client'\nimport { debugLog } from '../debug'\n\ninterface DeepSpaceAuthProviderProps {\n children: React.ReactNode\n}\n\n/** Logs auth state transitions once. */\nfunction AuthLogger() {\n const session = useSession()\n const isSignedIn = !!session.data?.user\n const prevSignedIn = useRef<boolean | null>(null)\n\n useEffect(() => {\n if (session.isPending) return\n if (prevSignedIn.current !== isSignedIn) {\n if (isSignedIn) {\n debugLog(`[ds:auth] signed in as ${session.data?.user?.name ?? session.data?.user?.id}`)\n } else if (prevSignedIn.current !== null) {\n debugLog('[ds:auth] signed out')\n }\n prevSignedIn.current = isSignedIn\n }\n }, [isSignedIn, session.isPending, session.data?.user?.name, session.data?.user?.id])\n\n return null\n}\n\n/**\n * Auth provider for DeepSpace apps.\n *\n * Better Auth uses cookie-based sessions, so there's no provider\n * state to manage. This component exists for API compatibility\n * and as a place to add auth-related context in the future.\n */\nexport function DeepSpaceAuthProvider({ children }: DeepSpaceAuthProviderProps) {\n return (\n <>\n <AuthLogger />\n {children}\n </>\n )\n}\n","/**\n * Better Auth Client for DeepSpace\n *\n * Creates a typed Better Auth client that communicates via the app's\n * own /api/auth/* proxy (same-origin cookies, no CORS issues).\n * The app's worker proxies these requests to the auth-worker.\n */\n\nimport { createAuthClient } from 'better-auth/react'\nimport { organizationClient, twoFactorClient } from 'better-auth/client/plugins'\nimport { clearAuthToken } from './token'\n\n/**\n * Auth base URL — always same-origin.\n * The app's worker.ts proxies /api/auth/* to the auth-worker.\n */\nfunction getBaseURL(): string {\n if (typeof window !== 'undefined') {\n return window.location.origin\n }\n return ''\n}\n\nexport const authClient = createAuthClient({\n baseURL: getBaseURL(),\n plugins: [organizationClient(), twoFactorClient()],\n})\n\nexport const {\n useSession,\n signIn,\n useActiveOrganization,\n useListOrganizations,\n} = authClient\n\nexport async function signOut(\n ...args: Parameters<typeof authClient.signOut>\n): Promise<Awaited<ReturnType<typeof authClient.signOut>>> {\n try {\n return await authClient.signOut(...args)\n } finally {\n clearAuthToken()\n }\n}\n","/**\n * Short-lived JWT provider for WebSocket and API authentication.\n *\n * Fetches an ES256 token from the same-origin `/api/auth/token` endpoint\n * (routed through to the auth-worker), caches it in module scope, and\n * refreshes ~30s before expiry by reading `exp` out of the payload.\n */\n\n// Token is fetched via same-origin /api/auth/token — the app's worker (or Vite proxy)\n// routes this to the auth-worker. No cross-origin requests needed.\n\nlet cachedToken: string | null = null\nlet tokenExpiry = 0\n\nfunction extractExpiry(token: string): number {\n try {\n const parts = token.split('.')\n if (parts.length < 2) return 0\n const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')))\n return (payload.exp ?? 0) * 1000\n } catch {\n return 0\n }\n}\n\n/**\n * Get a short-lived JWT for WebSocket and API calls.\n *\n * The token is obtained from the auth-worker's /api/auth/token endpoint,\n * authenticated via the Better Auth session cookie. Tokens are cached\n * and refreshed 30s before expiry.\n */\nexport async function getAuthToken(): Promise<string | null> {\n if (typeof window === 'undefined') return null\n\n // Return cached token if still valid (30s buffer)\n if (cachedToken && Date.now() < tokenExpiry - 30_000) {\n return cachedToken\n }\n\n try {\n const res = await fetch('/api/auth/token', {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n })\n\n if (!res.ok) {\n cachedToken = null\n tokenExpiry = 0\n return null\n }\n\n const data = (await res.json()) as { token?: string | null }\n if (!data.token) {\n cachedToken = null\n tokenExpiry = 0\n return null\n }\n\n cachedToken = data.token\n tokenExpiry = extractExpiry(data.token)\n return data.token\n } catch {\n return null\n }\n}\n\n/** Clear cached token (call on sign-out). */\nexport function clearAuthToken(): void {\n cachedToken = null\n tokenExpiry = 0\n}\n","/**\n * Debug gate for the client SDK.\n *\n * The SDK's connection/auth/Yjs logs are useful when developing an app but\n * shouldn't spam every consuming app's production console. They're silent by\n * default and opt-in via either:\n * - `localStorage.DEEPSPACE_DEBUG = '1'` (browser, persists across reloads), or\n * - `globalThis.DEEPSPACE_DEBUG = true` (set before the SDK loads).\n *\n * Read once at module load so the gate is a cheap boolean check per call.\n */\nfunction readDebugFlag(): boolean {\n try {\n const g = globalThis as { DEEPSPACE_DEBUG?: unknown; localStorage?: Storage }\n if (g.DEEPSPACE_DEBUG === true || g.DEEPSPACE_DEBUG === '1') return true\n const ls = g.localStorage?.getItem('DEEPSPACE_DEBUG')\n return ls === '1' || ls === 'true'\n } catch {\n // localStorage can throw (e.g. sandboxed iframes) — treat as disabled.\n return false\n }\n}\n\nexport const DEBUG = readDebugFlag()\n\n/** console.log gated behind the DEEPSPACE_DEBUG flag. No-op by default. */\nexport function debugLog(...args: unknown[]): void {\n if (DEBUG) console.log(...args)\n}\n","/**\n * Auth hooks for the DeepSpace SDK.\n *\n * Thin wrappers over Better Auth's `useSession` that expose the loaded /\n * signed-in / userId / user-profile shape the storage layer reads.\n */\n\nimport { useSession } from './client'\n\n/**\n * Sticky-true flag: becomes true once the session query first resolves\n * (success or error) and stays true for the lifetime of the page.\n *\n * Better Auth's underlying query atom briefly flips `isPending` back to\n * true on every background refetch when `data` is `null` — i.e. for\n * anonymous users on every tab-visibility change. Reporting that as\n * `isLoaded: false` would cause consumers (e.g. `RecordProvider`) to\n * unmount and remount their children on each refocus.\n *\n * `isLoaded` here means \"the first session check has completed,\" which\n * is the contract consumers actually want.\n */\nlet sessionHasResolved = false\n\nfunction stickyIsLoaded(isPending: boolean): boolean {\n if (!isPending) sessionHasResolved = true\n return sessionHasResolved\n}\n\n/**\n * Returns auth state compatible with what storage/context.tsx expects.\n */\nexport function useAuth() {\n const session = useSession()\n\n const isSignedIn = !!session.data?.user\n const userId = session.data?.user?.id ?? null\n\n return {\n isLoaded: stickyIsLoaded(session.isPending),\n isSignedIn,\n userId,\n sessionId: session.data?.session?.id ?? null,\n }\n}\n\n/**\n * Returns user data compatible with what storage/context.tsx expects.\n */\nexport function useAuthUser() {\n const session = useSession()\n const user = session.data?.user ?? null\n\n return {\n isLoaded: stickyIsLoaded(session.isPending),\n isSignedIn: !!user,\n user: user\n ? {\n id: user.id,\n fullName: user.name ?? null,\n firstName: user.name?.split(' ')[0] ?? null,\n lastName: user.name?.split(' ').slice(1).join(' ') || null,\n emailAddresses: user.email\n ? [{ emailAddress: user.email }]\n : [],\n imageUrl: user.image ?? null,\n primaryEmailAddress: user.email\n ? { emailAddress: user.email }\n : null,\n }\n : null,\n }\n}\n","/**\n * Full-page frosted-glass auth overlay for deployed DeepSpace apps.\n *\n * Primary: OAuth (GitHub, Google).\n * Secondary: Email/password sign-in (for test accounts created via CLI/API).\n * Styled with shadcn semantic tokens so it themes along with the host app.\n */\n\nimport React, { useState } from 'react'\nimport { useAuth } from './hooks'\nimport { signIn } from './client'\n\ninterface AuthOverlayProps {\n /** Called when the user clicks the close button. If omitted, overlay is not closeable. */\n onClose?: () => void\n /** Which OAuth providers to show. Defaults to both. */\n providers?: Array<'github' | 'google'>\n}\n\nexport function AuthOverlay({\n onClose,\n providers = ['github', 'google'],\n}: AuthOverlayProps = {}): React.ReactElement | null {\n const { isLoaded, isSignedIn } = useAuth()\n const [showEmailForm, setShowEmailForm] = useState(false)\n const [email, setEmail] = useState('')\n const [password, setPassword] = useState('')\n const [error, setError] = useState('')\n const [loading, setLoading] = useState(false)\n\n if (!isLoaded || isSignedIn) return null\n\n const handleSocial = (provider: 'github' | 'google') => {\n setLoading(true)\n window.location.href = `/api/auth/social-redirect?provider=${provider}`\n }\n\n const handleEmailSignIn = async (e: React.FormEvent) => {\n e.preventDefault()\n setError('')\n setLoading(true)\n\n try {\n const result = await signIn.email({ email, password })\n if (result.error) {\n setError(result.error.message ?? 'Sign in failed')\n setLoading(false)\n }\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : 'Something went wrong')\n setLoading(false)\n }\n }\n\n return (\n <>\n <style>{`\n @keyframes ds-auth-overlay-in {\n from { opacity: 0 }\n to { opacity: 1 }\n }\n @keyframes ds-auth-card-in {\n from { opacity: 0; transform: translateY(16px) scale(0.97) }\n to { opacity: 1; transform: translateY(0) scale(1) }\n }\n `}</style>\n\n <div\n data-testid=\"auth-overlay\"\n onClick={onClose ? (e) => { if (e.target === e.currentTarget) onClose() } : undefined}\n className=\"fixed inset-0 z-[99999] flex items-center justify-center bg-black/55 backdrop-blur-2xl\"\n style={{\n opacity: 0,\n animation: 'ds-auth-overlay-in 0.4s ease forwards',\n }}\n >\n <div\n className=\"relative mx-6 w-full max-w-[380px] rounded-2xl border border-border bg-card p-8 text-center shadow-card-hover sm:p-9\"\n style={{\n opacity: 0,\n animation: 'ds-auth-card-in 0.45s cubic-bezier(0.25, 1, 0.5, 1) 0.08s forwards',\n }}\n >\n {onClose && (\n <button\n data-testid=\"auth-overlay-close\"\n onClick={onClose}\n aria-label=\"Close\"\n className=\"absolute right-3 top-3 inline-flex h-7 w-7 items-center justify-center rounded-full bg-secondary text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n >\n ✕\n </button>\n )}\n\n <h2 className=\"mb-2 text-[22px] font-bold leading-snug text-foreground\">\n Sign in to DeepSpace\n </h2>\n\n <p className=\"mb-6 text-sm leading-snug text-muted-foreground\">\n Sync your data across devices\n </p>\n\n {/* OAuth buttons */}\n <div className=\"flex flex-col gap-2\">\n {providers.includes('github') && (\n <OAuthButton onClick={() => handleSocial('github')} disabled={loading} icon={<GitHubIcon />}>\n Continue with GitHub\n </OAuthButton>\n )}\n {providers.includes('google') && (\n <OAuthButton onClick={() => handleSocial('google')} disabled={loading} icon={<GoogleIcon />}>\n Continue with Google\n </OAuthButton>\n )}\n </div>\n\n <Divider />\n\n {!showEmailForm ? (\n <button\n type=\"button\"\n data-testid=\"auth-email-toggle\"\n onClick={() => setShowEmailForm(true)}\n className=\"flex w-full items-center justify-center gap-2.5 rounded-lg border border-border bg-transparent px-4 py-2.5 text-sm font-semibold text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground\"\n >\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <rect x=\"2\" y=\"4\" width=\"20\" height=\"16\" rx=\"2\" />\n <path d=\"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\" />\n </svg>\n Sign in with email\n </button>\n ) : (\n <form onSubmit={handleEmailSignIn} className=\"text-left\">\n <input\n type=\"email\"\n placeholder=\"Email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n required\n autoComplete=\"email\"\n className=\"mb-3 block w-full rounded-lg border border-border bg-secondary px-3.5 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none\"\n />\n <input\n type=\"password\"\n placeholder=\"Password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n required\n minLength={8}\n autoComplete=\"current-password\"\n className={`block w-full rounded-lg border border-border bg-secondary px-3.5 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none ${error ? 'mb-3' : 'mb-5'}`}\n />\n\n {error && (\n <div className=\"mb-4 text-center text-sm text-destructive\">{error}</div>\n )}\n\n <button\n type=\"submit\"\n disabled={loading}\n className=\"block w-full rounded-lg bg-primary px-5 py-3 text-[15px] font-semibold text-primary-foreground transition-opacity hover:opacity-90 disabled:cursor-wait disabled:opacity-70\"\n >\n {loading ? 'Please wait…' : 'Sign in'}\n </button>\n </form>\n )}\n\n <p className=\"mt-5 text-xs leading-snug text-muted-foreground\">\n By continuing, you agree to the DeepSpace{' '}\n <a\n href=\"https://deep.space/terms\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"underline hover:text-foreground\"\n >\n Terms of Service\n </a>{' '}\n and{' '}\n <a\n href=\"https://deep.space/privacy\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"underline hover:text-foreground\"\n >\n Privacy Policy\n </a>\n .\n </p>\n\n <div className=\"mt-4 flex items-center justify-center gap-1.5 text-[11px] text-muted-foreground\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"currentColor\" opacity=\"0.5\" aria-hidden>\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n </svg>\n Powered by DeepSpace\n </div>\n </div>\n </div>\n </>\n )\n}\n\n// ────────────────────────────────────────────────────────────────────\n// Local components\n// ────────────────────────────────────────────────────────────────────\n\nfunction OAuthButton({\n onClick,\n disabled,\n icon,\n children,\n}: {\n onClick: () => void\n disabled: boolean\n icon: React.ReactNode\n children: React.ReactNode\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n className=\"flex w-full items-center justify-center gap-2.5 rounded-lg border border-border bg-secondary px-4 py-2.5 text-sm font-semibold text-foreground transition-colors hover:bg-accent disabled:cursor-wait disabled:opacity-60\"\n >\n {icon}\n {children}\n </button>\n )\n}\n\nfunction Divider() {\n return (\n <div className=\"my-5 flex items-center gap-3 text-xs text-muted-foreground\">\n <div className=\"h-px flex-1 bg-border\" />\n or\n <div className=\"h-px flex-1 bg-border\" />\n </div>\n )\n}\n\nfunction GitHubIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden>\n <path d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0016 8c0-4.42-3.58-8-8-8z\" />\n </svg>\n )\n}\n\n// Google logo uses Google's brand colors — these are required by Google's\n// brand guidelines and intentionally not theme-reactive.\nfunction GoogleIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" aria-hidden>\n <path fill=\"#4285F4\" d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z\" />\n <path fill=\"#34A853\" d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\" />\n <path fill=\"#FBBC05\" d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\" />\n <path fill=\"#EA4335\" d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\" />\n </svg>\n )\n}\n","/** Conditional renderers, the route gate, and a display-name hook. */\n\nimport { useEffect, useRef, type ReactNode } from 'react'\nimport { useAuth, useAuthUser } from './hooks'\nimport { AuthOverlay } from './AuthOverlay'\n\n/**\n * Renders children only when the user is signed in.\n */\nexport function SignedIn({ children }: { children: ReactNode }): React.ReactElement | null {\n const { isLoaded, isSignedIn } = useAuth()\n if (!isLoaded || !isSignedIn) return null\n return <>{children}</>\n}\n\n/**\n * Renders children only when the user is signed out.\n */\nexport function SignedOut({ children }: { children: ReactNode }): React.ReactElement | null {\n const { isLoaded, isSignedIn } = useAuth()\n if (!isLoaded || isSignedIn) return null\n return <>{children}</>\n}\n\nexport interface AuthGateProps {\n /** Content to show when signed in */\n children: ReactNode\n /**\n * UI for first-visit signed-out users. If omitted, the SDK's sign-in\n * overlay is shown non-closeably. Not used on mid-session sign-out —\n * see `redirectOnSignOut`.\n */\n fallback?: ReactNode\n /** Where to send the user when they sign out from inside the gate. Defaults to '/'. */\n redirectOnSignOut?: string\n}\n\n/**\n * Gates a subtree behind sign-in. Children mount only when signed in (no flash).\n * If the user signs out from inside the gate, redirects out instead of trapping\n * them on the overlay.\n */\nexport function AuthGate({\n children,\n fallback,\n redirectOnSignOut = '/',\n}: AuthGateProps): React.ReactElement | null {\n const { isLoaded, isSignedIn } = useAuth()\n const wasSignedIn = useRef(false)\n if (isSignedIn) wasSignedIn.current = true\n\n if (!isLoaded) return null\n if (isSignedIn) return <>{children}</>\n if (wasSignedIn.current) return <RedirectAway to={redirectOnSignOut} />\n if (fallback) return <>{fallback}</>\n return <AuthOverlay />\n}\n\n// Full page load (not a router navigation) so the SDK stays router-agnostic\n// and the signed-out teardown is clean.\nfunction RedirectAway({ to }: { to: string }): null {\n useEffect(() => {\n if (typeof window !== 'undefined') window.location.replace(to)\n }, [to])\n return null\n}\n\n/**\n * Returns the current user's display name.\n *\n * Resolution order: fullName -> firstName -> email prefix -> 'User'.\n * Returns null when no user is signed in.\n */\nexport function useDisplayName(): string | null {\n const { user } = useAuthUser()\n if (!user) return null\n return (\n user.fullName ||\n user.firstName ||\n user.primaryEmailAddress?.emailAddress?.split('@')[0] ||\n 'User'\n )\n}\n","/**\n * Floating dismissible guest banner for DeepSpace apps.\n *\n * Shows a non-intrusive prompt for signed-out users to sign in or sign up.\n * Dismiss state is persisted in sessionStorage so it stays hidden for the\n * duration of the browsing session.\n *\n * Styled with shadcn semantic tokens (bg-card, text-foreground, etc.) so it\n * themes along with the host app.\n */\n\nimport React, { useEffect, useState } from 'react'\nimport { useAuth } from './hooks'\n\nconst DEFAULT_STORAGE_KEY = 'deepspace-guest-banner-dismissed'\nconst DEFAULT_MESSAGE = \"You're browsing as a guest. Sign in to unlock all features.\"\n\nexport interface GuestBannerProps {\n message?: string\n storageKey?: string\n /** Called when the user clicks \"Sign in\". */\n onSignIn?: () => void\n}\n\nexport function GuestBanner({\n message = DEFAULT_MESSAGE,\n storageKey = DEFAULT_STORAGE_KEY,\n onSignIn,\n}: GuestBannerProps): React.ReactElement | null {\n const { isLoaded, isSignedIn } = useAuth()\n const [mounted, setMounted] = useState(false)\n\n const [dismissed, setDismissed] = useState(() => {\n if (typeof window === 'undefined') return false\n try {\n return sessionStorage.getItem(storageKey) === '1'\n } catch {\n return false\n }\n })\n\n useEffect(() => {\n if (isLoaded && !isSignedIn && !dismissed) {\n requestAnimationFrame(() => setMounted(true))\n }\n }, [dismissed, isLoaded, isSignedIn])\n\n if (!isLoaded || isSignedIn || dismissed) return null\n\n const handleDismiss = (): void => {\n try {\n sessionStorage.setItem(storageKey, '1')\n } catch {\n // best effort\n }\n setDismissed(true)\n }\n\n return (\n <>\n <style>{`\n @keyframes ds-guest-banner-in {\n from { opacity: 0; transform: translateY(-12px); }\n to { opacity: 1; transform: translateY(0); }\n }\n `}</style>\n <div\n role=\"banner\"\n data-testid=\"guest-banner\"\n className=\"fixed top-3 inset-x-3 z-[99998] mx-auto flex max-w-[700px] items-center gap-3.5 rounded-2xl border border-border bg-card/95 px-4 py-3 text-sm leading-snug text-foreground shadow-card-hover backdrop-blur-xl sm:gap-4\"\n style={{\n opacity: mounted ? 1 : 0,\n animation: mounted\n ? 'ds-guest-banner-in 0.35s cubic-bezier(0.25, 1, 0.5, 1) forwards'\n : 'none',\n pointerEvents: mounted ? 'auto' : 'none',\n }}\n >\n <span\n aria-hidden\n className=\"hidden h-2 w-2 shrink-0 rounded-full bg-primary shadow-[0_0_8px_var(--color-primary)] sm:inline-block\"\n />\n\n <span className=\"min-w-0 flex-1 font-medium text-muted-foreground\">\n {message}\n </span>\n\n <div className=\"flex shrink-0 items-center gap-2\">\n <button\n type=\"button\"\n data-testid=\"guest-banner-signin\"\n onClick={onSignIn}\n className=\"inline-flex items-center justify-center whitespace-nowrap rounded-lg bg-primary px-4 py-1.5 text-xs font-semibold text-primary-foreground transition-opacity hover:opacity-90 sm:text-sm\"\n >\n Sign in\n </button>\n\n <button\n type=\"button\"\n data-testid=\"guest-banner-dismiss\"\n onClick={handleDismiss}\n aria-label=\"Dismiss banner\"\n className=\"flex shrink-0 items-center justify-center rounded-md p-1 text-muted-foreground transition-colors hover:text-foreground\"\n >\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </div>\n </div>\n </>\n )\n}\n","/**\n * RecordProvider Context\n *\n * Provides WebSocket connection and state management for RecordRoom.\n *\n * Multi-scope mode: RecordProvider can be used without roomId\n * to provide only auth context + ScopeRegistry. RecordScope components\n * handle individual WebSocket connections.\n */\n\nimport React, {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n useRef,\n useMemo,\n type ReactNode,\n} from 'react'\nimport type { CollectionSchema } from '../../shared/types'\nimport { useAuth, getAuthToken } from '../auth'\nimport { debugLog } from '../debug'\nimport { RecordStore } from './store'\nimport { RecordSocket } from './record-socket'\nimport { ScopeRegistryProvider } from './ScopeRegistry'\nimport type {\n UserProfile,\n RoomUser,\n RoomConnectionState,\n FetchUserProfile,\n} from './types'\nimport { MSG } from '@/shared/protocol/constants'\n\n// ============================================================================\n// RecordContext (per-scope connection state)\n// ============================================================================\n\nexport interface RecordContextValue {\n store: RecordStore\n roomId: string\n /** Collection names registered by this scope's schemas (used for scope resolution priority). */\n registeredCollections?: Set<string>\n userProfile: UserProfile | null\n userProfileLoading: boolean\n refetchUserProfile: () => Promise<void>\n roomRole: string | null\n allUsers: RoomUser[]\n /** True once the first MSG.USER_LIST response has been received. */\n usersLoaded: boolean\n status: RoomConnectionState\n ready: boolean\n /** Schemas discovered via MSG.LIST_SCHEMAS from the server. Available after ready. */\n discoveredSchemas?: CollectionSchema[]\n setUserRole: (userId: string, role: string) => void\n requestUserList: () => void\n registerSubscription: (subscriptionId: string, queryKey: string) => void\n unregisterSubscription: (subscriptionId: string) => void\n sendMessage: (message: { type: string; payload: unknown }) => void\n sendBinary: (data: Uint8Array) => void\n onBinaryMessage: (handler: (data: ArrayBuffer) => void) => () => void\n registerYjsJoinHandler: (docKey: string, handler: (canWrite: boolean) => void) => () => void\n sendConfirmed: (\n message: { type: string; payload: Record<string, unknown> },\n timeoutMs?: number,\n ) => Promise<unknown>\n}\n\nexport const RecordContext = createContext<RecordContextValue | null>(null)\n\nexport function useRecordContext(): RecordContextValue {\n const ctx = useContext(RecordContext)\n if (!ctx) {\n throw new Error('useRecordContext must be used within a RecordProvider')\n }\n return ctx\n}\n\n// ============================================================================\n// RecordAuthContext (shared auth state for RecordScope)\n// ============================================================================\n\nexport interface RecordAuthContextValue {\n userProfile: UserProfile | null\n userProfileLoading: boolean\n refetchUserProfile: () => Promise<void>\n allowAnonymous: boolean\n /** Called by RecordScope when it receives an RBAC permission error */\n onPermissionError?: (title: string, detail: string) => void\n /** Called by RecordScope when it receives a validation/other error */\n onValidationError?: (title: string, detail: string) => void\n /** Get auth token for WebSocket connections. */\n getAuthToken?: () => Promise<string | null>\n}\n\nconst RecordAuthContext = createContext<RecordAuthContextValue | null>(null)\n\nexport function useRecordAuth(): RecordAuthContextValue | null {\n return useContext(RecordAuthContext)\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n\n// ============================================================================\n// Core Provider (handles WebSocket connection — backward compat mode)\n// ============================================================================\n\ninterface RecordProviderCoreProps {\n roomId: string\n wsUrl?: string\n children: ReactNode\n fetchUser: FetchUserProfile\n allowAnonymous?: boolean\n getAuthToken?: () => Promise<string | null>\n}\n\nfunction RecordProviderCore({\n roomId,\n wsUrl,\n children,\n fetchUser,\n allowAnonymous = false,\n getAuthToken: getAuthTokenProp,\n}: RecordProviderCoreProps): React.ReactElement {\n const [userProfile, setUserProfile] = useState<UserProfile | null>(null)\n const [userProfileLoading, setUserProfileLoading] = useState(true)\n\n const [roomRole, setRoomRole] = useState<string | null>(null)\n const [allUsers, setAllUsers] = useState<RoomUser[]>([])\n const [usersLoaded, setUsersLoaded] = useState(false)\n const [status, setStatus] = useState<RoomConnectionState>('connecting')\n const [ready, setReady] = useState(false)\n\n // Callback refs for error handling — the socket's listeners close over\n // these so the latest app callbacks are invoked without socket recreation.\n const onPermissionErrorRef = useRef<((title: string, detail: string) => void) | undefined>(\n undefined,\n )\n const onValidationErrorRef = useRef<((title: string, detail: string) => void) | undefined>(\n undefined,\n )\n\n const storeRef = useRef<RecordStore>(new RecordStore())\n const socketRef = useRef<RecordSocket | null>(null)\n // Persist across socket recreation (auth change) — hooks register these\n // on mount and never re-register for a socket swap.\n const subscriptionsRef = useRef<Map<string, string>>(new Map())\n const binaryHandlersRef = useRef<Set<(data: ArrayBuffer) => void>>(new Set())\n const yjsJoinHandlersRef = useRef<Map<string, Set<(canWrite: boolean) => void>>>(new Map())\n const userProfileRef = useRef<UserProfile | null>(null)\n userProfileRef.current = userProfile\n const getAuthTokenRef = useRef(getAuthTokenProp)\n getAuthTokenRef.current = getAuthTokenProp\n\n const refetchUserProfile = useCallback(async () => {\n try {\n const profile = await fetchUser()\n setUserProfile(profile)\n } catch (err) {\n console.error('[RecordProvider] Failed to refetch user profile:', err)\n }\n }, [fetchUser])\n\n // Fetch user profile on mount and refresh periodically.\n // fetchUser returns null when not signed in — that's fine.\n useEffect(() => {\n let mounted = true\n setUserProfileLoading(true)\n fetchUser()\n .then((profile) => {\n if (mounted) setUserProfile(profile)\n })\n .finally(() => {\n if (mounted) setUserProfileLoading(false)\n })\n\n const interval = setInterval(() => {\n if (mounted)\n fetchUser()\n .then((p) => {\n if (mounted) setUserProfile(p)\n })\n .catch(() => {\n /* a transient profile-refresh failure keeps the last profile */\n })\n }, 30000)\n\n return () => {\n mounted = false\n clearInterval(interval)\n }\n }, [fetchUser])\n\n // ── Socket lifecycle ─────────────────────────────────────────────────\n // One shared engine with RecordScope (record-socket.ts) owns everything\n // race-sensitive. The socket is recreated when the connection identity\n // (room, URL, signed-in user) changes; identity changes therefore always\n // reconnect with the current token — including the anonymous → signed-in\n // upgrade the old inline implementation special-cased.\n\n const userProfileId = userProfile?.id ?? null\n\n useEffect(() => {\n if (userProfileLoading) return\n\n const socket = new RecordSocket({\n roomId,\n store: storeRef.current,\n subscriptions: subscriptionsRef.current,\n binaryHandlers: binaryHandlersRef.current,\n yjsJoinHandlers: yjsJoinHandlersRef.current,\n getToken: () => (getAuthTokenRef.current ?? getAuthToken)(),\n wsUrl,\n log: (event, detail) => debugLog(`[WS] ${event}`, detail),\n listeners: {\n onStatus: setStatus,\n onReady: setReady,\n onRole: setRoomRole,\n onUsers: (users) => {\n setAllUsers(users)\n setUsersLoaded(true)\n },\n onPermissionError: (title, detail) => onPermissionErrorRef.current?.(title, detail),\n onValidationError: (title, detail) => onValidationErrorRef.current?.(title, detail),\n },\n })\n socketRef.current = socket\n\n if (userProfileId || allowAnonymous) {\n void socket.connect(userProfileId ?? '')\n }\n\n return () => {\n socketRef.current = null\n socket.destroy()\n setStatus('connecting')\n setReady(false)\n }\n }, [roomId, wsUrl, userProfileId, userProfileLoading, allowAnonymous])\n\n // Reconnect on tab focus\n useEffect(() => {\n const onVisible = () => {\n if (document.visibilityState === 'visible' && !socketRef.current?.isOpen) {\n debugLog('[WS] Reconnecting after visibility change')\n socketRef.current?.resetBackoff()\n void socketRef.current?.connect()\n }\n }\n document.addEventListener('visibilitychange', onVisible)\n return () => document.removeEventListener('visibilitychange', onVisible)\n }, [])\n\n const sendMessage = useCallback((message: { type: string; payload: unknown }) => {\n socketRef.current?.sendMessage(message)\n }, [])\n\n const setUserRole = useCallback(\n (userId: string, role: string) => {\n sendMessage({ type: MSG.SET_ROLE, payload: { userId, role } })\n },\n [sendMessage],\n )\n\n const requestUserList = useCallback(() => {\n sendMessage({ type: MSG.USER_LIST, payload: {} })\n }, [sendMessage])\n\n const registerSubscription = useCallback((subscriptionId: string, queryKey: string) => {\n subscriptionsRef.current.set(subscriptionId, queryKey)\n }, [])\n\n const unregisterSubscription = useCallback((subscriptionId: string) => {\n subscriptionsRef.current.delete(subscriptionId)\n }, [])\n\n const sendBinary = useCallback((data: Uint8Array) => {\n socketRef.current?.sendBinary(data)\n }, [])\n\n const onBinaryMessage = useCallback((handler: (data: ArrayBuffer) => void) => {\n binaryHandlersRef.current.add(handler)\n return () => {\n binaryHandlersRef.current.delete(handler)\n }\n }, [])\n\n const registerYjsJoinHandler = useCallback(\n (docKey: string, handler: (canWrite: boolean) => void) => {\n const handlers = yjsJoinHandlersRef.current\n if (!handlers.has(docKey)) handlers.set(docKey, new Set())\n handlers.get(docKey)!.add(handler)\n return () => {\n const set = handlers.get(docKey)\n if (set) {\n set.delete(handler)\n if (set.size === 0) handlers.delete(docKey)\n }\n }\n },\n [],\n )\n\n const sendConfirmed = useCallback(\n (\n message: { type: string; payload: Record<string, unknown> },\n timeoutMs = 10000,\n ): Promise<unknown> => {\n const socket = socketRef.current\n if (!socket) return Promise.reject(new Error('WebSocket not connected'))\n return socket.sendConfirmed(message, timeoutMs)\n },\n [],\n )\n\n const value: RecordContextValue = useMemo(\n () => ({\n store: storeRef.current,\n roomId,\n userProfile,\n userProfileLoading,\n refetchUserProfile,\n roomRole,\n allUsers,\n usersLoaded,\n status,\n ready,\n setUserRole,\n requestUserList,\n registerSubscription,\n unregisterSubscription,\n sendMessage,\n sendBinary,\n onBinaryMessage,\n registerYjsJoinHandler,\n sendConfirmed,\n }),\n [\n roomId,\n userProfile,\n userProfileLoading,\n refetchUserProfile,\n roomRole,\n allUsers,\n usersLoaded,\n status,\n ready,\n setUserRole,\n requestUserList,\n registerSubscription,\n unregisterSubscription,\n sendMessage,\n sendBinary,\n onBinaryMessage,\n registerYjsJoinHandler,\n sendConfirmed,\n ],\n )\n\n const onPermissionError = useCallback((title: string, detail: string) => {\n onPermissionErrorRef.current?.(title, detail)\n }, [])\n\n const onValidationError = useCallback((title: string, detail: string) => {\n onValidationErrorRef.current?.(title, detail)\n }, [])\n\n const authValue: RecordAuthContextValue = useMemo(\n () => ({\n userProfile,\n userProfileLoading,\n refetchUserProfile,\n allowAnonymous,\n getAuthToken: getAuthTokenProp,\n onPermissionError,\n onValidationError,\n }),\n [\n userProfile,\n userProfileLoading,\n refetchUserProfile,\n allowAnonymous,\n getAuthTokenProp,\n onPermissionError,\n onValidationError,\n ],\n )\n\n return (\n <RecordAuthContext.Provider value={authValue}>\n <ScopeRegistryProvider>\n <RecordContext.Provider value={value}>{children}</RecordContext.Provider>\n </ScopeRegistryProvider>\n </RecordAuthContext.Provider>\n )\n}\n\n// ============================================================================\n// Auth-Only Provider (multi-scope mode — no roomId, no WS)\n// ============================================================================\n\ninterface RecordProviderAuthOnlyProps {\n children: ReactNode\n fetchUser: FetchUserProfile\n allowAnonymous?: boolean\n getAuthToken?: () => Promise<string | null>\n}\n\nfunction RecordProviderAuthOnly({\n children,\n fetchUser,\n allowAnonymous = false,\n getAuthToken: getAuthTokenProp,\n}: RecordProviderAuthOnlyProps): React.ReactElement {\n const [userProfile, setUserProfile] = useState<UserProfile | null>(null)\n const [userProfileLoading, setUserProfileLoading] = useState(true)\n\n const refetchUserProfile = useCallback(async () => {\n try {\n const profile = await fetchUser()\n setUserProfile(profile)\n } catch (err) {\n console.error('[RecordProvider] Failed to refetch user profile:', err)\n }\n }, [fetchUser])\n\n // Fetch user profile on mount and refresh periodically.\n // fetchUser returns null when not signed in — that's fine, no retry needed.\n useEffect(() => {\n let mounted = true\n setUserProfileLoading(true)\n fetchUser()\n .then((profile) => {\n if (mounted) setUserProfile(profile)\n })\n .finally(() => {\n if (mounted) setUserProfileLoading(false)\n })\n\n const interval = setInterval(() => {\n if (mounted) {\n fetchUser()\n .then((p) => {\n if (mounted) setUserProfile(p)\n })\n .catch(() => {\n /* a transient profile-refresh failure keeps the last profile */\n })\n }\n }, 30000)\n\n return () => {\n mounted = false\n clearInterval(interval)\n }\n }, [fetchUser])\n\n const authValue: RecordAuthContextValue = useMemo(\n () => ({\n userProfile,\n userProfileLoading,\n refetchUserProfile,\n allowAnonymous,\n getAuthToken: getAuthTokenProp,\n }),\n [userProfile, userProfileLoading, refetchUserProfile, allowAnonymous, getAuthTokenProp],\n )\n\n return (\n <RecordAuthContext.Provider value={authValue}>\n <ScopeRegistryProvider>{children}</ScopeRegistryProvider>\n </RecordAuthContext.Provider>\n )\n}\n\n// ============================================================================\n// Main Entry Point\n// ============================================================================\n\n/**\n * RecordProvider - Main entry point for storage.\n *\n * Two modes:\n * 1. **Single-scope (backward compat):** Pass `roomId` + `schemas`. Opens one WS connection.\n * 2. **Multi-scope:** Omit `roomId`. Only provides auth + ScopeRegistry.\n * Use `<RecordScope>` components for individual WS connections.\n *\n * @example\n * ```tsx\n * // Single-scope (backward compat)\n * <RecordProvider roomId=\"my-app\" schemas={schemas}>\n * <App />\n * </RecordProvider>\n *\n * // Multi-scope\n * <RecordProvider>\n * <RecordScope roomId=\"app:slack-clone\" schemas={appSchemas}>\n * <RecordScope roomId={`conv:${channelId}`} schemas={convSchemas}>\n * <ChannelView />\n * </RecordScope>\n * </RecordScope>\n * </RecordProvider>\n * ```\n */\nexport function RecordProvider({\n roomId,\n wsUrl,\n children,\n allowAnonymous = false,\n getAuthToken: getAuthTokenProp,\n}: {\n roomId?: string\n /**\n * Accepted for API compatibility with `RecordScope` and documented usage,\n * but the single-scope backward-compat provider does no local-first\n * registration, so it's ignored. Pass schemas to `RecordScope` instead.\n */\n schemas?: CollectionSchema[]\n wsUrl?: string\n children: ReactNode\n allowAnonymous?: boolean\n getAuthToken?: () => Promise<string | null>\n}): React.ReactElement {\n const { isLoaded, isSignedIn } = useAuth()\n\n // Derive user profile from the JWT — no API call needed.\n // Returns null when not signed in (no error, no console spam).\n const fetchUser = useCallback(async (): Promise<UserProfile | null> => {\n if (!isSignedIn) return null\n const token = await getAuthToken()\n if (!token) return null\n try {\n const parts = token.split('.')\n const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')))\n return {\n id: payload.sub,\n name: payload.name ?? '',\n email: payload.email ?? '',\n imageUrl: payload.image ?? undefined,\n }\n } catch {\n return null\n }\n }, [isSignedIn])\n\n // Build getAuthToken function — use prop override or default to auth module\n const getAuthTokenFn = useCallback(async (): Promise<string | null> => {\n if (getAuthTokenProp) return getAuthTokenProp()\n if (!isSignedIn) return null\n return getAuthToken()\n }, [getAuthTokenProp, isSignedIn])\n\n // Not loaded yet — render nothing (no loading screens — that's the app's job)\n if (!isLoaded) {\n return <>{null}</>\n }\n\n // Not signed in and not allowing anonymous — render nothing\n if (!isSignedIn && !allowAnonymous) {\n return <>{null}</>\n }\n\n if (roomId) {\n return (\n <RecordProviderCore\n roomId={roomId}\n wsUrl={wsUrl}\n fetchUser={fetchUser}\n allowAnonymous={allowAnonymous}\n getAuthToken={getAuthTokenFn}\n >\n {children}\n </RecordProviderCore>\n )\n }\n\n return (\n <RecordProviderAuthOnly\n fetchUser={fetchUser}\n allowAnonymous={allowAnonymous}\n getAuthToken={getAuthTokenFn}\n >\n {children}\n </RecordProviderAuthOnly>\n )\n}\n","/**\n * RecordStore\n *\n * Manages query subscriptions with version-tracked caching.\n * Uses external store pattern for React integration.\n */\n\nimport type { RecordData } from './types'\n\n/**\n * Immutable snapshot handed to `useSyncExternalStore`. A new object is minted\n * on every state transition (see `commit`), so a change to `status`/`error` —\n * not just `records` — flips the snapshot reference and re-renders the hook.\n * Without `status`/`error` in the snapshot, an error that arrives before the\n * first result (records reference unchanged) never propagates and the UI is\n * stuck in \"loading\" forever.\n */\nexport interface QuerySnapshot {\n status: 'loading' | 'ready' | 'error'\n records: RecordData[]\n error?: string\n}\n\ninterface QueryState {\n version: number // Incremented on every change\n snapshot: QuerySnapshot // Stable reference until any field changes\n}\n\n// Stable empty array - never changes reference\nconst EMPTY_RECORDS: RecordData[] = []\n// Stable snapshot for queries that don't exist yet.\nconst EMPTY_SNAPSHOT: QuerySnapshot = { status: 'loading', records: EMPTY_RECORDS }\n\nexport class RecordStore {\n private queries = new Map<string, QueryState>()\n private listeners = new Map<string, Set<() => void>>()\n /** Reference count for each query - only send SUBSCRIBE on first, UNSUBSCRIBE on last */\n private refCounts = new Map<string, number>()\n /** Track the single subscriptionId per queryKey (for deduplication) */\n private activeSubscriptions = new Map<string, string>()\n\n subscribe(queryKey: string, listener: () => void): () => void {\n const set = this.listeners.get(queryKey) ?? new Set()\n set.add(listener)\n this.listeners.set(queryKey, set)\n return () => {\n set.delete(listener)\n if (set.size === 0) {\n this.listeners.delete(queryKey)\n }\n }\n }\n\n /**\n * Snapshot for `useSyncExternalStore`. Bundles records + status + error so a\n * transition in any of them re-renders the hook. Returns a stable reference\n * until the next `commit()`.\n */\n getSnapshot(queryKey: string): QuerySnapshot {\n return this.queries.get(queryKey)?.snapshot ?? EMPTY_SNAPSHOT\n }\n\n /**\n * Initialize a query subscription.\n * Returns true if this is the FIRST subscriber (should send SUBSCRIBE).\n */\n initQuery(queryKey: string): boolean {\n const prevCount = this.refCounts.get(queryKey) ?? 0\n this.refCounts.set(queryKey, prevCount + 1)\n\n if (!this.queries.has(queryKey)) {\n this.queries.set(queryKey, {\n version: 0,\n snapshot: { status: 'loading', records: EMPTY_RECORDS },\n })\n }\n\n // Return true if this is the first subscriber\n return prevCount === 0\n }\n\n /**\n * Release a query subscription.\n * Returns true if this was the LAST subscriber (should send UNSUBSCRIBE).\n */\n releaseQuery(queryKey: string): boolean {\n const count = (this.refCounts.get(queryKey) ?? 1) - 1\n\n if (count <= 0) {\n this.refCounts.delete(queryKey)\n this.activeSubscriptions.delete(queryKey)\n return true // Last subscriber\n }\n\n this.refCounts.set(queryKey, count)\n return false // Still has subscribers\n }\n\n /** Store the subscriptionId for a queryKey (for routing server responses) */\n setSubscriptionId(queryKey: string, subscriptionId: string): void {\n this.activeSubscriptions.set(queryKey, subscriptionId)\n }\n\n /** Get the subscriptionId for a queryKey */\n getSubscriptionId(queryKey: string): string | undefined {\n return this.activeSubscriptions.get(queryKey)\n }\n\n /**\n * Replace the state for a query with a fresh snapshot and bump the version,\n * then notify listeners. Single write-path so every mutator mints a new\n * snapshot reference (which is what drives `useSyncExternalStore` re-renders).\n */\n private commit(queryKey: string, snapshot: QuerySnapshot): void {\n const version = (this.queries.get(queryKey)?.version ?? 0) + 1\n this.queries.set(queryKey, { version, snapshot })\n this.notify(queryKey)\n }\n\n // Handle QUERY_RESULT from server\n setQueryResult(queryKey: string, records: RecordData[]): void {\n this.commit(queryKey, { status: 'ready', records })\n }\n\n /** Check if a record exists in a query's results */\n hasRecord(queryKey: string, recordId: string): boolean {\n const state = this.queries.get(queryKey)\n if (!state) return false\n return state.snapshot.records.some((r) => r.recordId === recordId)\n }\n\n // Handle RECORD_CHANGE from server\n applyChange(\n queryKey: string,\n record: RecordData,\n changeType: 'create' | 'update' | 'delete',\n ): void {\n const state = this.queries.get(queryKey)\n if (!state) return\n const records = state.snapshot.records\n\n let newRecords: RecordData[]\n\n if (changeType === 'delete') {\n newRecords = records.filter((r) => r.recordId !== record.recordId)\n } else if (changeType === 'create') {\n // Check if record already exists (prevent duplicates from race conditions)\n const exists = records.some((r) => r.recordId === record.recordId)\n if (exists) {\n // If it exists, treat as update instead\n newRecords = records.map((r) => (r.recordId === record.recordId ? record : r))\n } else {\n // Append (correct for asc order, which is the common case for real-time data)\n newRecords = [...records, record]\n }\n } else {\n // update\n newRecords = records.map((r) => (r.recordId === record.recordId ? record : r))\n }\n\n // Carry the existing status AND error through: a RECORD_CHANGE can arrive\n // while a query is in `status: 'error'`, and dropping `error` here would\n // leave consumers reading an error state with no message.\n this.commit(queryKey, {\n status: state.snapshot.status,\n records: newRecords,\n error: state.snapshot.error,\n })\n }\n\n /**\n * Reset a query to loading state (used on WebSocket reconnect).\n * Preserves stale records so the UI can show them while reconnecting.\n */\n resetToLoading(queryKey: string): void {\n const current = this.queries.get(queryKey)\n if (!current) return\n this.commit(queryKey, { status: 'loading', records: current.snapshot.records })\n }\n\n setError(queryKey: string, error: string): void {\n const records = this.queries.get(queryKey)?.snapshot.records ?? EMPTY_RECORDS\n this.commit(queryKey, { status: 'error', records, error })\n }\n\n // Cleanup when query is no longer needed\n removeQuery(queryKey: string): void {\n this.queries.delete(queryKey)\n this.listeners.delete(queryKey)\n }\n\n private notify(queryKey: string): void {\n const set = this.listeners.get(queryKey)\n if (set) {\n set.forEach((fn) => fn())\n }\n }\n}\n","import type { RecordData } from './types'\n\n/**\n * Client-side mirror of the server's where-builder (executeTableQuery):\n * schema columns take precedence over envelope fields so initial load and\n * live updates agree when a schema defines a column named 'createdBy'.\n * ONE copy — context.tsx and RecordScope.tsx both filter live changes\n * through this; a divergence shows up as records flickering in one provider\n * and not the other.\n */\nexport function recordMatchesWhere(\n record: RecordData,\n where?: Record<string, unknown>,\n): boolean {\n if (!where) return true\n const r = record as RecordData & { data?: Record<string, unknown> }\n const data = r.data\n if (!data) return false\n return Object.entries(where).every(([key, value]) => {\n if (key in data) return data[key] === value\n if (key === 'recordId') return r.recordId === value\n if (key === 'createdBy') return r.createdBy === value\n return false\n })\n}\n\n/** Shared reconnect backoff: 1s doubling to a 30s ceiling. */\nexport function reconnectDelayMs(attempt: number): number {\n return Math.min(1000 * Math.pow(2, attempt), 30000)\n}\n","/**\n * Server Error Parser\n * \n * Parses server-side error strings into user-friendly messages.\n * Covers RBAC permission errors, validation errors, and generic errors.\n */\n\nexport interface ServerErrorInfo {\n title: string\n detail: string\n /** True for RBAC/permission errors (shown as toasts). False for validation/other (shown in error overlay). */\n isPermissionError: boolean\n}\n\n/**\n * Humanize a collection slug (e.g. \"rbac-notes\" → \"Notes\", \"team-posts\" → \"Team Posts\").\n */\nfunction humanizeCollection(slug: string): string {\n return slug\n .replace(/^rbac-/, '')\n .split(/[-_]/)\n .map(w => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ')\n}\n\n/**\n * Humanize a role name for display (e.g. \"viewer\" → \"Viewer\").\n */\nfunction humanizeRole(role: string): string {\n return role.charAt(0).toUpperCase() + role.slice(1)\n}\n\n/**\n * Parse a server error string into a user-friendly message.\n * Handles RBAC errors, validation errors, and falls back to a generic error.\n */\nexport function parseServerError(error: string): ServerErrorInfo {\n // ── RBAC Errors ──────────────────────────────────────────────────────\n\n // UPDATE DENIED: role=viewer, collection=tasks\n const updateMatch = error.match(/^UPDATE DENIED: role=(\\w+), collection=(.+)/)\n if (updateMatch) {\n return {\n title: `${humanizeRole(updateMatch[1])}s can't edit ${humanizeCollection(updateMatch[2])}`,\n detail: '',\n isPermissionError: true,\n }\n }\n\n const createMatch = error.match(/^CREATE DENIED: role=(\\w+), collection=(.+)/)\n if (createMatch) {\n return {\n title: `${humanizeRole(createMatch[1])}s can't create ${humanizeCollection(createMatch[2])}`,\n detail: '',\n isPermissionError: true,\n }\n }\n\n const deleteMatch = error.match(/^DELETE DENIED: role=(\\w+), collection=(.+)/)\n if (deleteMatch) {\n return {\n title: `${humanizeRole(deleteMatch[1])}s can't delete ${humanizeCollection(deleteMatch[2])}`,\n detail: '',\n isPermissionError: true,\n }\n }\n\n const readMatch = error.match(/^READ DENIED: role=(\\w+), collection=(.+)/)\n if (readMatch) {\n return {\n title: `${humanizeRole(readMatch[1])}s can't view ${humanizeCollection(readMatch[2])}`,\n detail: '',\n isPermissionError: true,\n }\n }\n\n const fieldMatch = error.match(/^FIELD ERROR: Cannot update field '(\\w+)'.*role '(\\w+)'/)\n if (fieldMatch) {\n return {\n title: `${humanizeRole(fieldMatch[2])}s can't edit \"${fieldMatch[1]}\"`,\n detail: '',\n isPermissionError: true,\n }\n }\n\n const fieldGenericMatch = error.match(/^FIELD ERROR: (.+)/)\n if (fieldGenericMatch) {\n return {\n title: 'Field not editable',\n detail: '',\n isPermissionError: true,\n }\n }\n\n const permMatch = error.match(/^Permission denied: (.+)/)\n if (permMatch) {\n return {\n title: permMatch[1].charAt(0).toUpperCase() + permMatch[1].slice(1),\n detail: '',\n isPermissionError: true,\n }\n }\n\n // ── Validation Errors ────────────────────────────────────────────────\n\n // Missing required field: fieldName\n const requiredMatch = error.match(/^Missing required field: (.+)/)\n if (requiredMatch) {\n return {\n title: 'Missing field',\n detail: `\"${requiredMatch[1]}\" is required`,\n isPermissionError: false,\n }\n }\n\n // Cannot change immutable field: fieldName\n const immutableMatch = error.match(/^Cannot change immutable field: (.+)/)\n if (immutableMatch) {\n return {\n title: 'Cannot modify',\n detail: `\"${immutableMatch[1]}\" cannot be changed after creation`,\n isPermissionError: false,\n }\n }\n\n // Field 'x' must be your user ID\n const userBoundMatch = error.match(/^Field '(.+)' must be your user ID/)\n if (userBoundMatch) {\n return {\n title: 'Invalid value',\n detail: `\"${userBoundMatch[1]}\" must be your own user ID`,\n isPermissionError: false,\n }\n }\n\n // ── Fallback ─────────────────────────────────────────────────────────\n\n return {\n title: 'Error',\n detail: error,\n isPermissionError: false,\n }\n}\n","/**\n * RecordSocket — the ONE WebSocket engine behind both providers\n * (context.tsx's backward-compat RecordProvider and RecordScope).\n *\n * Framework-free: React never appears here. The providers own React state\n * and wire it up via the listener callbacks; everything race-sensitive —\n * the connect-token guard around the async auth-token fetch, reconnect\n * backoff, pending-request settlement, resubscribe-on-open, zombie-reconnect\n * prevention on teardown — lives here, under direct unit test\n * (__tests__/record-socket.test.ts).\n *\n * History: this logic used to exist twice (~250 lines each) and the copies\n * drifted three ways before being unified. Do not fork it again.\n */\n\nimport { recordMatchesWhere, reconnectDelayMs } from './record-matching'\nimport { parseServerError } from './serverErrors'\nimport type { CollectionSchema } from '../../shared/types'\nimport type { RoomUser, RoomConnectionState, RecordData } from './types'\nimport { MSG } from '../../shared/protocol/constants'\n\n// WebSocket readyState per spec — avoids touching the global WebSocket\n// object for constants (tests inject WebSocketImpl; node may lack a global).\nconst WS_CONNECTING = 0\nconst WS_OPEN = 1\n\n/** The slice of RecordStore the engine writes to (structural, for tests). */\nexport interface RecordStoreLike {\n setQueryResult(queryKey: string, records: RecordData[]): void\n applyChange(queryKey: string, record: RecordData, changeType: 'create' | 'update' | 'delete'): void\n hasRecord(queryKey: string, recordId: string): boolean\n setError(queryKey: string, error: string): void\n resetToLoading(queryKey: string): void\n}\n\nexport interface RecordSocketListeners {\n onStatus(status: RoomConnectionState): void\n onReady(ready: boolean): void\n onRole(role: string | null): void\n onUsers(users: RoomUser[]): void\n /** MSG.LIST_SCHEMAS — optional; the compat provider has no schemas surface. */\n onSchemas?(schemas: CollectionSchema[]): void\n onPermissionError?(title: string, detail: string): void\n onValidationError?(title: string, detail: string): void\n}\n\nexport interface RecordSocketConfig {\n roomId: string\n store: RecordStoreLike\n /** Fetch the auth token for the connection URL. Failures are swallowed —\n * the socket connects tokenless and the server decides (anonymous rooms\n * work; authed rooms reject). */\n getToken: () => Promise<string | null>\n listeners: RecordSocketListeners\n /** http(s)/ws(s) base; defaults to window.location. */\n wsUrl?: string\n /** Default '/ws'. */\n wsPathPrefix?: string\n /** Extra query params (e.g. { appId }). The token param is added by connect. */\n extraParams?: Record<string, string>\n /** Diagnostics hook (wsLog/debugLog per provider). */\n log?: (event: string, detail?: unknown) => void\n /** Test injection. Defaults to globalThis.WebSocket. */\n WebSocketImpl?: typeof WebSocket\n /**\n * Persistent registries, owned by the CALLER when the socket can be\n * recreated (auth identity change): hooks register subscriptions/handlers\n * once on mount, so they must survive a socket swap. Default to fresh\n * per-instance containers.\n */\n subscriptions?: Map<string, string>\n binaryHandlers?: Set<(data: ArrayBuffer) => void>\n yjsJoinHandlers?: Map<string, Set<(canWrite: boolean) => void>>\n}\n\ninterface PendingRequest {\n resolve: (data?: unknown) => void\n reject: (error: Error) => void\n timer: ReturnType<typeof setTimeout>\n}\n\nexport class RecordSocket {\n private ws: WebSocket | null = null\n private readonly subscriptions: Map<string, string> // subscriptionId → queryKey\n private readonly binaryHandlers: Set<(data: ArrayBuffer) => void>\n private readonly yjsJoinHandlers: Map<string, Set<(canWrite: boolean) => void>>\n private readonly pendingRequests = new Map<string, PendingRequest>()\n private reconnectAttempt = 0\n private reconnectTimeout: ReturnType<typeof setTimeout> | null = null\n // Bumped by connect() (on entry) and disconnect()/destroy(). connect()\n // captures it before awaiting the auth token and re-checks after; a\n // mismatch means a teardown (or a newer connect) raced in during the\n // await, so the resolved connect must bail instead of leaking a socket.\n private connectToken = 0\n // Opaque identity the current socket was opened with (e.g. '' for\n // anonymous, the profile id once signed in). A connect() with a DIFFERENT\n // tag while OPEN forces a reconnect so the server sees the new identity.\n private identityTag: string | undefined\n private destroyed = false\n\n constructor(private readonly config: RecordSocketConfig) {\n this.subscriptions = config.subscriptions ?? new Map()\n this.binaryHandlers = config.binaryHandlers ?? new Set()\n this.yjsJoinHandlers = config.yjsJoinHandlers ?? new Map()\n }\n\n // ── lifecycle ─────────────────────────────────────────────────────────────\n\n async connect(identityTag?: string): Promise<void> {\n if (this.destroyed) return\n const { config } = this\n\n if (this.ws?.readyState === WS_OPEN) {\n if (identityTag !== undefined && identityTag !== this.identityTag) {\n // Identity changed mid-connection (e.g. anonymous → signed-in): the\n // server derives identity from the connection's JWT, so reconnect.\n config.log?.('reconnecting with new identity', config.roomId)\n this.teardownSocket()\n } else {\n return\n }\n }\n if (this.ws?.readyState === WS_CONNECTING) return\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout)\n this.reconnectTimeout = null\n }\n\n const myToken = ++this.connectToken\n\n const params = new URLSearchParams()\n // Identity is derived server-side from the verified JWT — any\n // client-supplied identity params would be stripped at the worker edge,\n // so the token is the only identity we send.\n try {\n const token = await config.getToken()\n if (token) params.set('token', token)\n } catch {\n /* token fetch failed — connect tokenless; the server decides */\n }\n\n // A teardown or newer connect() raced in while we awaited the token —\n // bail so we don't open (and leak) a socket the caller already tore\n // down, whose onclose would then schedule a zombie reconnect.\n if (this.connectToken !== myToken || this.destroyed) return\n\n for (const [key, value] of Object.entries(config.extraParams ?? {})) {\n params.set(key, value)\n }\n\n // Only an explicit tag overwrites the remembered identity — a bare\n // connect() (visibility retry, scheduled reconnect) keeps the last one.\n if (identityTag !== undefined) this.identityTag = identityTag\n\n const protocol =\n typeof window !== 'undefined' && window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const baseUrl =\n config.wsUrl?.replace(/^http/, 'ws') ??\n `${protocol}//${typeof window !== 'undefined' ? window.location.host : ''}`\n const pathPrefix = config.wsPathPrefix ?? '/ws'\n const url = `${baseUrl}${pathPrefix}/${config.roomId}?${params.toString()}`\n\n config.log?.('connecting', config.roomId)\n const WS = config.WebSocketImpl ?? WebSocket\n const ws = new WS(url)\n ws.binaryType = 'arraybuffer'\n this.ws = ws\n\n ws.onopen = () => {\n config.log?.('connected', config.roomId)\n config.listeners.onStatus('connected')\n // Re-subscribe every active query. After a plain socket drop the\n // server has no memory of our subscriptions and sends no RESUBSCRIBE\n // (that only fires on membership changes) — without this, a\n // reconnected client silently stops receiving live updates.\n this.resubscribeAll(ws)\n }\n\n ws.onmessage = (event) => this.handleMessage(event)\n\n ws.onclose = () => {\n config.log?.('disconnected', config.roomId)\n config.listeners.onStatus('disconnected')\n config.listeners.onReady(false)\n this.ws = null\n for (const pending of this.pendingRequests.values()) {\n clearTimeout(pending.timer)\n pending.reject(new Error('WebSocket disconnected'))\n }\n this.pendingRequests.clear()\n // Queries show loading (not silently-stale data) until the reconnect\n // resubscribes and fresh results land.\n for (const queryKey of this.subscriptions.values()) {\n config.store.resetToLoading(queryKey)\n }\n const attempt = this.reconnectAttempt\n const delay = reconnectDelayMs(attempt)\n config.log?.('scheduling reconnect', { roomId: config.roomId, attempt, delayMs: delay })\n this.reconnectAttempt = attempt + 1\n this.reconnectTimeout = setTimeout(() => void this.connect(this.identityTag), delay)\n }\n\n ws.onerror = () => {}\n }\n\n /**\n * Tear down and reset to a connectable state (auth identity change, scope\n * unmount-and-remount). Emits status 'connecting' so the UI shows progress\n * rather than a dead 'disconnected'.\n */\n disconnect(): void {\n this.connectToken++ // invalidate any in-flight connect() awaiting its token\n this.clearReconnect()\n this.teardownSocket()\n this.config.listeners.onStatus('connecting')\n this.config.listeners.onReady(false)\n this.reconnectAttempt = 0\n }\n\n /** Final teardown (unmount): no listener calls — the owner is gone. */\n destroy(): void {\n this.destroyed = true\n this.connectToken++\n this.clearReconnect()\n this.teardownSocket()\n }\n\n /** Zero the backoff (tab became visible, user asked to retry). */\n resetBackoff(): void {\n this.reconnectAttempt = 0\n }\n\n get isOpen(): boolean {\n return this.ws?.readyState === WS_OPEN\n }\n\n private clearReconnect(): void {\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout)\n this.reconnectTimeout = null\n }\n }\n\n /** Close without firing onclose — prevents the zombie-reconnect. */\n private teardownSocket(): void {\n const ws = this.ws\n if (!ws) return\n this.config.log?.('closing', this.config.roomId)\n ws.onclose = null\n ws.onmessage = null\n ws.onerror = null\n ws.onopen = null\n ws.close()\n this.ws = null\n }\n\n // ── sending ───────────────────────────────────────────────────────────────\n\n sendMessage(message: { type: string; payload: unknown }): void {\n if (this.ws?.readyState === WS_OPEN) this.ws.send(JSON.stringify(message))\n else this.config.log?.('send dropped (not connected)', message.type)\n }\n\n sendBinary(data: Uint8Array): void {\n if (this.ws?.readyState === WS_OPEN) this.ws.send(data)\n }\n\n sendConfirmed(\n message: { type: string; payload: Record<string, unknown> },\n timeoutMs = 10000,\n ): Promise<unknown> {\n const ws = this.ws\n if (!ws || ws.readyState !== WS_OPEN) {\n return Promise.reject(new Error('WebSocket not connected'))\n }\n const requestId = `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(requestId)\n reject(new Error('Mutation confirmation timed out'))\n }, timeoutMs)\n this.pendingRequests.set(requestId, { resolve, reject, timer })\n ws.send(JSON.stringify({ ...message, payload: { ...message.payload, requestId } }))\n })\n }\n\n // ── registrations ─────────────────────────────────────────────────────────\n\n registerSubscription(subscriptionId: string, queryKey: string): void {\n this.subscriptions.set(subscriptionId, queryKey)\n }\n\n unregisterSubscription(subscriptionId: string): void {\n this.subscriptions.delete(subscriptionId)\n }\n\n onBinaryMessage(handler: (data: ArrayBuffer) => void): () => void {\n this.binaryHandlers.add(handler)\n return () => {\n this.binaryHandlers.delete(handler)\n }\n }\n\n registerYjsJoinHandler(docKey: string, handler: (canWrite: boolean) => void): () => void {\n if (!this.yjsJoinHandlers.has(docKey)) this.yjsJoinHandlers.set(docKey, new Set())\n this.yjsJoinHandlers.get(docKey)!.add(handler)\n return () => {\n const handlers = this.yjsJoinHandlers.get(docKey)\n if (handlers) {\n handlers.delete(handler)\n if (handlers.size === 0) this.yjsJoinHandlers.delete(docKey)\n }\n }\n }\n\n // ── message dispatch ──────────────────────────────────────────────────────\n\n private resubscribeAll(ws: WebSocket): void {\n for (const [subscriptionId, queryKey] of this.subscriptions) {\n try {\n const query = JSON.parse(queryKey)\n ws.send(JSON.stringify({ type: MSG.SUBSCRIBE, payload: { subscriptionId, query } }))\n } catch {\n /* skip invalid query keys */\n }\n }\n }\n\n private handleMessage(event: MessageEvent): void {\n if (event.data instanceof ArrayBuffer) {\n this.binaryHandlers.forEach((h) => h(event.data as ArrayBuffer))\n return\n }\n\n let msg: { type: string; payload: unknown }\n try {\n msg = JSON.parse(event.data as string)\n } catch {\n console.error('[RecordSocket] Failed to parse message')\n return\n }\n\n const { type, payload } = msg\n const { store, listeners } = this.config\n\n switch (type) {\n case MSG.USER_INFO: {\n const { role } = payload as { role: string }\n listeners.onRole(role)\n listeners.onReady(true)\n this.reconnectAttempt = 0\n // Auto-request the user list as part of the connection handshake.\n this.sendMessage({ type: MSG.USER_LIST, payload: {} })\n break\n }\n\n case MSG.USER_LIST:\n listeners.onUsers((payload as { users: RoomUser[] }).users)\n break\n\n case MSG.QUERY_RESULT: {\n const { subscriptionId, records } = payload as {\n subscriptionId: string\n records: RecordData[]\n }\n const queryKey = this.subscriptions.get(subscriptionId)\n if (queryKey !== undefined) store.setQueryResult(queryKey, records)\n break\n }\n\n case MSG.RECORD_CHANGE: {\n const { collection, record, changeType } = payload as {\n collection: string\n record: RecordData\n changeType: 'create' | 'update' | 'delete'\n }\n for (const queryKey of this.subscriptions.values()) {\n try {\n const query = JSON.parse(queryKey) as {\n collection: string\n where?: Record<string, unknown>\n }\n if (query.collection !== collection) continue\n const matches = recordMatchesWhere(record, query.where)\n const exists = store.hasRecord(queryKey, record.recordId)\n if (changeType === 'delete') {\n if (exists) store.applyChange(queryKey, record, 'delete')\n } else if (changeType === 'create') {\n if (matches) store.applyChange(queryKey, record, 'create')\n } else {\n if (matches && exists) store.applyChange(queryKey, record, 'update')\n else if (matches && !exists) store.applyChange(queryKey, record, 'create')\n else if (!matches && exists) store.applyChange(queryKey, record, 'delete')\n }\n } catch {\n /* skip invalid query keys */\n }\n }\n break\n }\n\n case MSG.ERROR: {\n const { subscriptionId, error } = payload as { subscriptionId?: string; error: string }\n if (subscriptionId) {\n const queryKey = this.subscriptions.get(subscriptionId)\n if (queryKey !== undefined) store.setError(queryKey, error)\n } else {\n const parsed = parseServerError(error)\n if (parsed.isPermissionError) listeners.onPermissionError?.(parsed.title, parsed.detail)\n else listeners.onValidationError?.(parsed.title, parsed.detail)\n }\n break\n }\n\n case MSG.YJS_JOIN: {\n const { collection, recordId, fieldName, canWrite } = payload as {\n collection: string\n recordId: string\n fieldName: string\n canWrite: boolean\n }\n const docKey = `${collection}:${recordId}:${fieldName}`\n this.yjsJoinHandlers.get(docKey)?.forEach((h) => h(canWrite))\n break\n }\n\n case MSG.ACK: {\n const { requestId, success, error, ...rest } = payload as {\n requestId: string\n success: boolean\n error?: string\n [key: string]: unknown\n }\n const pending = this.pendingRequests.get(requestId)\n if (pending) {\n clearTimeout(pending.timer)\n this.pendingRequests.delete(requestId)\n if (success) pending.resolve(rest)\n else pending.reject(new Error(error || 'Mutation rejected'))\n }\n break\n }\n\n case MSG.LIST_SCHEMAS: {\n const { schemas } = payload as { schemas: CollectionSchema[] }\n listeners.onSchemas?.(schemas ?? [])\n break\n }\n\n case MSG.RESUBSCRIBE: {\n // Team membership changed — re-subscribe all active queries.\n if (this.ws?.readyState === WS_OPEN) this.resubscribeAll(this.ws)\n break\n }\n }\n }\n}\n","/**\n * ScopeRegistry\n *\n * React context that maps collection names to scope entries.\n * RecordScope registers its collections on mount, and hooks\n * (useQuery, useMutations) resolve the correct scope by collection name.\n *\n * Each registration is tagged with a scopeId so that unregister only\n * removes entries owned by the calling scope — preventing one scope\n * from breaking another when both register the same collections.\n */\n\nimport { createContext, useContext, useCallback, useRef, useMemo, type ReactNode } from 'react'\nimport type { RecordStore } from './store'\nimport type { RoomConnectionState } from './types'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ScopeEntry {\n store: RecordStore\n sendMessage: (msg: { type: string; payload: unknown }) => void\n sendConfirmed: (\n msg: { type: string; payload: Record<string, unknown> },\n timeoutMs?: number,\n ) => Promise<unknown>\n registerSubscription: (subscriptionId: string, queryKey: string) => void\n unregisterSubscription: (subscriptionId: string) => void\n sendBinary: (data: Uint8Array) => void\n onBinaryMessage: (handler: (data: ArrayBuffer) => void) => () => void\n registerYjsJoinHandler: (\n docKey: string,\n handler: (canWrite: boolean) => void,\n ) => () => void\n ready: boolean\n status: RoomConnectionState\n}\n\ninterface Registration {\n scopeId: string\n entry: ScopeEntry\n}\n\ninterface ScopeRegistryValue {\n register: (scopeId: string, collections: string[], entry: ScopeEntry) => void\n unregister: (scopeId: string, collections: string[]) => void\n resolve: (collection: string) => ScopeEntry | null\n}\n\n// ============================================================================\n// Context\n// ============================================================================\n\nconst ScopeRegistryContext = createContext<ScopeRegistryValue | null>(null)\n\nexport function useScopeRegistry(): ScopeRegistryValue | null {\n return useContext(ScopeRegistryContext)\n}\n\n// ============================================================================\n// Provider\n// ============================================================================\n\nexport function ScopeRegistryProvider({ children }: { children: ReactNode }) {\n const mapRef = useRef(new Map<string, Registration>())\n\n const register = useCallback((scopeId: string, collections: string[], entry: ScopeEntry) => {\n for (const name of collections) {\n mapRef.current.set(name, { scopeId, entry })\n }\n }, [])\n\n const unregister = useCallback((scopeId: string, collections: string[]) => {\n for (const name of collections) {\n const existing = mapRef.current.get(name)\n // Only unregister if this scope owns the registration\n if (existing?.scopeId === scopeId) {\n mapRef.current.delete(name)\n }\n }\n }, [])\n\n const resolve = useCallback((collection: string): ScopeEntry | null => {\n return mapRef.current.get(collection)?.entry ?? null\n }, [])\n\n const value = useMemo(() => ({ register, unregister, resolve }), [register, unregister, resolve])\n\n return (\n <ScopeRegistryContext.Provider value={value}>\n {children}\n </ScopeRegistryContext.Provider>\n )\n}\n","/**\n * RecordScope\n *\n * Opens a direct WebSocket connection to a RecordRoom DO.\n * Manages subscriptions, reconnection, and state via RecordStore.\n * Registers collections in ScopeRegistry so hooks resolve to the right scope.\n *\n * @example\n * ```tsx\n * <RecordProvider>\n * <RecordScope\n * roomId=\"app:my-app\"\n * schemas={appSchemas}\n * appId=\"my-app\"\n * sharedScopes={[\n * { roomId: 'workspace:default', schemas: workspaceSchemas },\n * ]}\n * >\n * <App />\n * </RecordScope>\n * </RecordProvider>\n * ```\n */\n\nimport React, { useEffect, useState, useCallback, useRef, useMemo, type ReactNode } from 'react'\nimport type { CollectionSchema } from '../../shared/types'\nimport { RecordContext, type RecordContextValue } from './context'\nimport { useRecordAuth } from './context'\nimport { RecordStore } from './store'\nimport { RecordSocket } from './record-socket'\nimport { useScopeRegistry, type ScopeEntry } from './ScopeRegistry'\nimport { getAuthToken } from '../auth'\nimport { wsLog } from './ws-log'\nimport type { RoomConnectionState, RoomUser } from './types'\nimport { MSG } from '@/shared/protocol/constants'\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface SharedScopeConfig {\n roomId: string\n schemas: CollectionSchema[]\n}\n\ninterface RecordScopeProps {\n roomId: string\n schemas: CollectionSchema[]\n children?: ReactNode\n /** App ID passed to the server for schema resolution. */\n appId: string\n /** Additional scopes to connect (headless — no children, just register collections). */\n sharedScopes?: SharedScopeConfig[]\n /** WebSocket base URL override. Derived from window.location if omitted. */\n wsUrl?: string\n /** Path prefix for WebSocket route. Default: '/ws'. */\n wsPathPrefix?: string\n /** Don't register collections in ScopeRegistry (prevents name collisions). */\n isolated?: boolean\n}\n\n// ============================================================================\n// Headless scope — opens a WS connection and registers collections only\n// ============================================================================\n\nfunction HeadlessScope({\n roomId,\n schemas,\n appId,\n wsUrl,\n wsPathPrefix,\n}: {\n roomId: string\n schemas: CollectionSchema[]\n appId: string\n wsUrl?: string\n wsPathPrefix?: string\n}) {\n return (\n <ScopeConnection\n roomId={roomId}\n schemas={schemas}\n appId={appId}\n wsUrl={wsUrl}\n wsPathPrefix={wsPathPrefix}\n />\n )\n}\n\n// ============================================================================\n// Core WebSocket connection logic (shared by primary + headless scopes)\n// ============================================================================\n\nfunction ScopeConnection({\n roomId,\n schemas,\n appId,\n children,\n wsUrl,\n wsPathPrefix = '/ws',\n isolated = false,\n}: {\n roomId: string\n schemas: CollectionSchema[]\n appId: string\n children?: ReactNode\n wsUrl?: string\n wsPathPrefix?: string\n isolated?: boolean\n}) {\n const auth = useRecordAuth()\n const registry = useScopeRegistry()\n\n const scopeIdRef = useRef(`scope-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`)\n\n // Room state\n const [roomRole, setRoomRole] = useState<string | null>(null)\n const [allUsers, setAllUsers] = useState<RoomUser[]>([])\n const [usersLoaded, setUsersLoaded] = useState(false)\n const [status, setStatus] = useState<RoomConnectionState>('connecting')\n const [ready, setReady] = useState(false)\n const [discoveredSchemas, setDiscoveredSchemas] = useState<CollectionSchema[]>([])\n\n // Refs\n const storeRef = useRef<RecordStore>(new RecordStore())\n const socketRef = useRef<RecordSocket | null>(null)\n // Persist across socket recreation (auth change) — hooks register these\n // on mount and never re-register for a socket swap.\n const subscriptionsRef = useRef<Map<string, string>>(new Map())\n const binaryHandlersRef = useRef<Set<(data: ArrayBuffer) => void>>(new Set())\n const yjsJoinHandlersRef = useRef<Map<string, Set<(canWrite: boolean) => void>>>(new Map())\n\n // Auth refs (stable across renders)\n const userProfileRef = useRef(auth?.userProfile ?? null)\n userProfileRef.current = auth?.userProfile ?? null\n const getAuthTokenRef = useRef(auth?.getAuthToken ?? null)\n getAuthTokenRef.current = auth?.getAuthToken ?? null\n const authCallbacksRef = useRef({\n onPermissionError: auth?.onPermissionError,\n onValidationError: auth?.onValidationError,\n })\n authCallbacksRef.current.onPermissionError = auth?.onPermissionError\n authCallbacksRef.current.onValidationError = auth?.onValidationError\n const allowAnonymous = auth?.allowAnonymous ?? false\n\n // ── Socket lifecycle ─────────────────────────────────────────────────\n // The engine (RecordSocket) owns everything race-sensitive: connect-token\n // guard, backoff, resubscribe-on-open, pending-request settlement. This\n // component owns React state and recreates the socket when the connection\n // identity (room, URL, app, or signed-in user) changes.\n\n const userProfileId = auth?.userProfile?.id ?? null\n const userProfileLoading = auth?.userProfileLoading ?? false\n\n useEffect(() => {\n // Still loading auth — wait (avoids an anonymous connect that is\n // immediately torn down when the profile lands).\n if (userProfileLoading) return\n\n const socket = new RecordSocket({\n roomId,\n store: storeRef.current,\n subscriptions: subscriptionsRef.current,\n binaryHandlers: binaryHandlersRef.current,\n yjsJoinHandlers: yjsJoinHandlersRef.current,\n getToken: () => (getAuthTokenRef.current ?? getAuthToken)(),\n wsUrl,\n wsPathPrefix,\n extraParams: { appId },\n log: (event) => wsLog(event as Parameters<typeof wsLog>[0], roomId),\n listeners: {\n onStatus: setStatus,\n onReady: setReady,\n onRole: setRoomRole,\n onUsers: (users) => {\n setAllUsers(users)\n setUsersLoaded(true)\n },\n onSchemas: setDiscoveredSchemas,\n onPermissionError: (title, detail) =>\n authCallbacksRef.current.onPermissionError?.(title, detail),\n onValidationError: (title, detail) =>\n authCallbacksRef.current.onValidationError?.(title, detail),\n },\n })\n socketRef.current = socket\n\n if (userProfileId || allowAnonymous) {\n void socket.connect(userProfileId ?? '')\n }\n\n return () => {\n socketRef.current = null\n socket.destroy()\n setStatus('connecting')\n setReady(false)\n }\n }, [roomId, wsUrl, wsPathPrefix, appId, userProfileId, userProfileLoading, allowAnonymous])\n\n // Reconnect on tab focus\n useEffect(() => {\n const onVisible = () => {\n if (document.visibilityState === 'visible' && !socketRef.current?.isOpen) {\n socketRef.current?.resetBackoff()\n void socketRef.current?.connect()\n }\n }\n document.addEventListener('visibilitychange', onVisible)\n return () => document.removeEventListener('visibilitychange', onVisible)\n }, [])\n\n // ── Send helpers (stable delegates to the engine) ────────────────────\n\n const sendMessage = useCallback((message: { type: string; payload: unknown }) => {\n socketRef.current?.sendMessage(message)\n }, [])\n\n const sendBinary = useCallback((data: Uint8Array) => {\n socketRef.current?.sendBinary(data)\n }, [])\n\n const onBinaryMessage = useCallback((handler: (data: ArrayBuffer) => void) => {\n binaryHandlersRef.current.add(handler)\n return () => {\n binaryHandlersRef.current.delete(handler)\n }\n }, [])\n\n const registerYjsJoinHandler = useCallback(\n (docKey: string, handler: (canWrite: boolean) => void) => {\n const handlers = yjsJoinHandlersRef.current\n if (!handlers.has(docKey)) handlers.set(docKey, new Set())\n handlers.get(docKey)!.add(handler)\n return () => {\n const set = handlers.get(docKey)\n if (set) {\n set.delete(handler)\n if (set.size === 0) handlers.delete(docKey)\n }\n }\n },\n [],\n )\n\n const sendConfirmed = useCallback(\n (\n message: { type: string; payload: Record<string, unknown> },\n timeoutMs = 10000,\n ): Promise<unknown> => {\n const socket = socketRef.current\n if (!socket) return Promise.reject(new Error('WebSocket not connected'))\n return socket.sendConfirmed(message, timeoutMs)\n },\n [],\n )\n\n const setUserRole = useCallback(\n (userId: string, role: string) => {\n sendMessage({ type: MSG.SET_ROLE, payload: { userId, role } })\n },\n [sendMessage],\n )\n\n const requestUserList = useCallback(() => {\n sendMessage({ type: MSG.USER_LIST, payload: {} })\n }, [sendMessage])\n\n const registerSubscription = useCallback((subscriptionId: string, queryKey: string) => {\n subscriptionsRef.current.set(subscriptionId, queryKey)\n }, [])\n\n const unregisterSubscription = useCallback((subscriptionId: string) => {\n subscriptionsRef.current.delete(subscriptionId)\n }, [])\n\n // Auto-discover schemas\n useEffect(() => {\n if (ready) sendMessage({ type: MSG.LIST_SCHEMAS, payload: {} })\n }, [ready, sendMessage])\n\n // ── ScopeRegistry ────────────────────────────────────────────────────\n\n const scopeEntryRef = useRef<ScopeEntry | null>(null)\n if (!scopeEntryRef.current) {\n scopeEntryRef.current = {\n store: storeRef.current,\n sendMessage,\n sendConfirmed,\n registerSubscription,\n unregisterSubscription,\n sendBinary,\n onBinaryMessage,\n registerYjsJoinHandler,\n ready,\n status,\n }\n }\n scopeEntryRef.current.ready = ready\n scopeEntryRef.current.status = status\n\n const registeredSchemasRef = useRef<CollectionSchema[] | null>(null)\n if (!isolated && registry && schemas !== registeredSchemasRef.current) {\n if (registeredSchemasRef.current) {\n registry.unregister(\n scopeIdRef.current,\n registeredSchemasRef.current.map((s) => s.name),\n )\n }\n const names = schemas.map((s) => s.name)\n if (names.length > 0) registry.register(scopeIdRef.current, names, scopeEntryRef.current!)\n registeredSchemasRef.current = schemas\n }\n\n useEffect(() => {\n if (isolated || !registry) return\n // scopeId is assigned once at init and never reassigned; capture it so the\n // cleanup doesn't read a possibly-changed ref (it can't here, but the rule\n // can't prove that).\n const scopeId = scopeIdRef.current\n return () => {\n const names = (registeredSchemasRef.current ?? []).map((s) => s.name)\n if (names.length > 0) registry.unregister(scopeId, names)\n }\n }, [isolated, registry, roomId])\n\n // ── Context ──────────────────────────────────────────────────────────\n\n const registeredCollections = useMemo(() => new Set(schemas.map((s) => s.name)), [schemas])\n\n const value: RecordContextValue = useMemo(\n () => ({\n store: storeRef.current,\n roomId,\n registeredCollections,\n userProfile: auth?.userProfile ?? null,\n userProfileLoading: auth?.userProfileLoading ?? false,\n refetchUserProfile: auth?.refetchUserProfile ?? (async () => {}),\n roomRole,\n allUsers,\n usersLoaded,\n status,\n ready,\n discoveredSchemas,\n setUserRole,\n requestUserList,\n registerSubscription,\n unregisterSubscription,\n sendMessage,\n sendBinary,\n onBinaryMessage,\n registerYjsJoinHandler,\n sendConfirmed,\n }),\n [\n roomId,\n registeredCollections,\n auth?.userProfile,\n auth?.userProfileLoading,\n auth?.refetchUserProfile,\n roomRole,\n allUsers,\n usersLoaded,\n status,\n ready,\n discoveredSchemas,\n setUserRole,\n requestUserList,\n registerSubscription,\n unregisterSubscription,\n sendMessage,\n sendBinary,\n onBinaryMessage,\n registerYjsJoinHandler,\n sendConfirmed,\n ],\n )\n\n if (!children) return null\n return <RecordContext.Provider value={value}>{children}</RecordContext.Provider>\n}\n\n// ============================================================================\n// RecordScope (public API)\n// ============================================================================\n\nexport function RecordScope({\n roomId,\n schemas,\n children,\n appId,\n sharedScopes,\n wsUrl,\n wsPathPrefix = '/ws',\n isolated = false,\n}: RecordScopeProps) {\n return (\n <>\n {sharedScopes?.map((shared) => (\n <HeadlessScope\n key={shared.roomId}\n roomId={shared.roomId}\n schemas={shared.schemas}\n appId={appId}\n wsUrl={wsUrl}\n wsPathPrefix={wsPathPrefix}\n />\n ))}\n <ScopeConnection\n roomId={roomId}\n schemas={schemas}\n appId={appId}\n wsUrl={wsUrl}\n wsPathPrefix={wsPathPrefix}\n isolated={isolated}\n >\n {children}\n </ScopeConnection>\n </>\n )\n}\n","/**\n * Shared WebSocket connection logger with active connection count.\n * Silent unless DEEPSPACE_DEBUG is set (see ../debug).\n */\n\nimport { debugLog } from '../debug'\n\nlet activeCount = 0\n\nexport function wsLog(\n event: 'connecting' | 'connected' | 'disconnected' | 'closing',\n label: string,\n) {\n if (event === 'connected') activeCount++\n if (event === 'closing') activeCount--\n\n debugLog(`[ds:ws] ${event} → ${label} (${activeCount} active)`)\n}\n","/**\n * useUser Hook\n * \n * Access current user with profile data and room role.\n */\n\nimport { useMemo } from 'react'\nimport { useRecordContext } from '../context'\nimport type { User } from '../types'\n\n/**\n * Hook to access current user.\n * \n * Returns merged user data:\n * - Profile from API (karma, credits, subscription, isAdmin)\n * - Role from room (derived from user-roles collection)\n * \n * The role is the user's role in THIS miniapp/room, which can be\n * different from their global isAdmin status.\n * \n * @example\n * ```tsx\n * const { user, refetch, isLoading } = useUser()\n * \n * // Check role\n * if (user?.role === 'admin') { ... }\n * if (user?.role === 'intern') { ... }\n * \n * // Access karma/credits\n * const karma = user?.karma?.total ?? 0\n * \n * // Refetch after expensive operations\n * await refetch() // Updates user.credits\n * ```\n */\nexport function useUser(): {\n user: User | null\n isLoading: boolean\n refetch: () => Promise<void>\n} {\n const { userProfile, userProfileLoading, refetchUserProfile, roomRole } = useRecordContext()\n \n // Merge profile + room role into unified User\n const user = useMemo((): User | null => {\n if (!userProfile) return null\n return {\n ...userProfile,\n role: roomRole ?? 'viewer', // Default to 'viewer' if no role set\n }\n }, [userProfile, roomRole])\n \n return { user, isLoading: userProfileLoading, refetch: refetchUserProfile }\n}\n","/**\n * useQuery Hook\n *\n * Subscribe to a query with real-time updates.\n * Resolves the correct scope via ScopeRegistry (multi-scope)\n * or falls back to useRecordContext() (single-scope backward compat).\n */\n\nimport { useEffect, useCallback, useRef, useMemo, useContext, useSyncExternalStore } from 'react'\nimport { RecordContext } from '../context'\nimport { useScopeRegistry } from '../ScopeRegistry'\nimport type { Query, RecordData } from '../types'\nimport { MSG } from '@/shared/protocol/constants'\n\n/**\n * Subscribe to a query with real-time updates.\n *\n * @example\n * ```tsx\n * const { records, status } = useQuery('tasks', {\n * where: { userId: me },\n * orderBy: 'createdAt',\n * orderDir: 'desc',\n * limit: 50\n * })\n * ```\n */\nexport function useQuery<T = unknown>(\n collection: string,\n options?: {\n where?: Record<string, unknown>\n orderBy?: string\n orderDir?: 'asc' | 'desc'\n limit?: number\n },\n): {\n records: RecordData<T>[]\n status: 'loading' | 'ready' | 'error'\n error?: string\n} {\n // Try scope resolution first (multi-scope), then fall back to RecordContext\n const registry = useScopeRegistry()\n const recordCtx = useContext(RecordContext)\n\n const scopeEntry = registry?.resolve(collection) ?? null\n\n // Prefer RecordContext when this scope handles the collection (avoids stale ScopeRegistry during navigation)\n const preferLocal = recordCtx?.registeredCollections?.has(collection) ?? false\n const store = preferLocal ? recordCtx!.store : (scopeEntry?.store ?? recordCtx?.store)\n const sendMessage = preferLocal\n ? recordCtx!.sendMessage\n : (scopeEntry?.sendMessage ?? recordCtx?.sendMessage)\n const registerSub = preferLocal\n ? recordCtx!.registerSubscription\n : (scopeEntry?.registerSubscription ?? recordCtx?.registerSubscription)\n const unregisterSub = preferLocal\n ? recordCtx!.unregisterSubscription\n : (scopeEntry?.unregisterSubscription ?? recordCtx?.unregisterSubscription)\n\n if (!store || !sendMessage || !registerSub || !unregisterSub) {\n throw new Error(\n `useQuery('${collection}'): No scope found. Wrap in a RecordProvider (with roomId) or a RecordScope that registers this collection.`,\n )\n }\n\n const optionsKey = JSON.stringify(options ?? null)\n\n const query: Query = useMemo(\n () => ({\n collection,\n where: options?.where,\n orderBy: options?.orderBy,\n orderDir: options?.orderDir,\n limit: options?.limit,\n // Keyed by the serialized optionsKey rather than the raw options object\n // (a fresh object each render) so the query — and its WS subscription —\n // is only rebuilt when the options actually change.\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- keyed on serialized optionsKey, not raw options.where/orderBy/orderDir/limit objects, to avoid rebuilding the WS subscription every render\n [collection, optionsKey],\n )\n\n const queryKey = useMemo(() => JSON.stringify(query), [query])\n\n const subscriptionIdRef = useRef<string>(\n `sub-${Date.now()}-${Math.random().toString(36).slice(2)}`,\n )\n\n useEffect(() => {\n const subscriptionId = subscriptionIdRef.current\n\n const isFirst = store.initQuery(queryKey)\n\n if (isFirst) {\n store.setSubscriptionId(queryKey, subscriptionId)\n registerSub(subscriptionId, queryKey)\n sendMessage({ type: MSG.SUBSCRIBE, payload: { subscriptionId, query } })\n }\n\n return () => {\n const activeSubId = store.getSubscriptionId(queryKey) ?? subscriptionId\n const isLast = store.releaseQuery(queryKey)\n\n if (isLast) {\n sendMessage({ type: MSG.UNSUBSCRIBE, payload: { subscriptionId: activeSubId } })\n unregisterSub(activeSubId)\n }\n }\n }, [queryKey, query, store, sendMessage, registerSub, unregisterSub, collection])\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => store.subscribe(queryKey, onStoreChange),\n [store, queryKey],\n )\n\n const getSnapshot = useCallback(() => store.getSnapshot(queryKey), [store, queryKey])\n\n // The snapshot bundles records + status + error, so a transition in any of\n // them (e.g. an error before the first result) re-renders. Reading them off\n // the store separately would miss those transitions when `records` is\n // reference-stable.\n const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n const records = snapshot.records as RecordData<T>[]\n const { status, error } = snapshot\n\n // Memoize the return object so consumers get a stable reference\n // (same rationale as useMutations — prevents spurious effect re-runs).\n return useMemo(() => ({ records, status, error }), [records, status, error])\n}\n","/**\n * useMutations Hook\n *\n * Get mutation functions for a collection.\n * Resolves the correct scope via ScopeRegistry (multi-scope)\n * or falls back to useRecordContext() (single-scope backward compat).\n */\n\nimport { useCallback, useContext, useMemo } from 'react'\nimport { RecordContext } from '../context'\nimport { useScopeRegistry } from '../ScopeRegistry'\nimport { MSG } from '@/shared/protocol/constants'\n\n/**\n * Get mutation functions for a collection.\n *\n * Type contract:\n * - `create(data)` requires the **full** record shape `T`. New records\n * have no existing data to merge with, so all fields the schema\n * considers required must be present.\n * - `put(recordId, patch)` accepts a **`Partial<T>`**. The server\n * merges the patch into the existing row (`{...existing, ...patch}`),\n * so callers should send only the fields they want to change.\n *\n * @example\n * ```tsx\n * const { create, put, remove } = useMutations<Task>('tasks')\n *\n * // create needs the full Task shape\n * const id = await create({ title: 'New task', completed: false, priority: 1 })\n *\n * // put is a partial update — only the fields you want to change\n * await put(id, { completed: true })\n * await remove(id)\n * ```\n */\nexport function useMutations<T = unknown>(collection: string): {\n create: (data: T) => Promise<string>\n put: (recordId: string, data: Partial<T>) => Promise<void>\n remove: (recordId: string) => Promise<void>\n createConfirmed: (data: T) => Promise<string>\n putConfirmed: (recordId: string, data: Partial<T>) => Promise<void>\n removeConfirmed: (recordId: string) => Promise<void>\n} {\n // Try scope resolution first (multi-scope), then fall back to RecordContext\n const registry = useScopeRegistry()\n const recordCtx = useContext(RecordContext)\n\n const scopeEntry = registry?.resolve(collection) ?? null\n\n // Prefer RecordContext when this scope handles the collection (avoids stale ScopeRegistry during navigation)\n const preferLocal = recordCtx?.registeredCollections?.has(collection) ?? false\n const sendMessage = preferLocal ? recordCtx!.sendMessage : (scopeEntry?.sendMessage ?? recordCtx?.sendMessage)\n const sendConfirmed = preferLocal ? recordCtx!.sendConfirmed : (scopeEntry?.sendConfirmed ?? recordCtx?.sendConfirmed)\n\n if (!sendMessage || !sendConfirmed) {\n throw new Error(\n `useMutations('${collection}'): No scope found. Wrap in a RecordProvider (with roomId) or a RecordScope that registers this collection.`,\n )\n }\n\n const create = useCallback(\n async (data: T): Promise<string> => {\n const recordId = `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`\n sendMessage({ type: MSG.PUT, payload: { collection, recordId, data } })\n return recordId\n },\n [sendMessage, collection],\n )\n\n const put = useCallback(\n async (recordId: string, data: Partial<T>): Promise<void> => {\n sendMessage({ type: MSG.PUT, payload: { collection, recordId, data } })\n },\n [sendMessage, collection],\n )\n\n const remove = useCallback(\n async (recordId: string): Promise<void> => {\n sendMessage({ type: MSG.DELETE, payload: { collection, recordId } })\n },\n [sendMessage, collection],\n )\n\n const createConfirmed = useCallback(\n async (data: T): Promise<string> => {\n const recordId = `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`\n await sendConfirmed({\n type: MSG.PUT,\n payload: { collection, recordId, data: data as Record<string, unknown> },\n })\n return recordId\n },\n [sendConfirmed, collection],\n )\n\n const putConfirmed = useCallback(\n async (recordId: string, data: Partial<T>): Promise<void> => {\n await sendConfirmed({\n type: MSG.PUT,\n payload: { collection, recordId, data: data as Record<string, unknown> },\n })\n },\n [sendConfirmed, collection],\n )\n\n const removeConfirmed = useCallback(\n async (recordId: string): Promise<void> => {\n await sendConfirmed({ type: MSG.DELETE, payload: { collection, recordId } })\n },\n [sendConfirmed, collection],\n )\n\n // Memoize the return object so consumers get a stable reference.\n // Without this, every render produces a new object, which breaks any\n // useCallback/useEffect that depends on the useMutations() result\n // (e.g., cleanup effects re-fire and delete records they shouldn't).\n return useMemo(\n () => ({ create, put, remove, createConfirmed, putConfirmed, removeConfirmed }),\n [create, put, remove, createConfirmed, putConfirmed, removeConfirmed],\n )\n}\n","/**\n * useUsers Hook\n *\n * Access all users in the current room.\n * User data (name, email, imageUrl, role) comes from the RecordRoom's users collection.\n */\n\nimport { useEffect, useMemo } from 'react'\nimport { useRecordContext } from '../context'\nimport type { RoomUser } from '../types'\n\nexport function useUsers(): {\n users: RoomUser[]\n usersLoaded: boolean\n setRole: (userId: string, role: string) => void\n refresh: () => void\n} {\n const { allUsers, usersLoaded, setUserRole, requestUserList, ready } = useRecordContext()\n\n useEffect(() => {\n if (ready) requestUserList()\n }, [ready, requestUserList])\n\n const users = useMemo(() => allUsers, [allUsers])\n\n return { users, usersLoaded, setRole: setUserRole, refresh: requestUserList }\n}\n","/**\n * Yjs Collaborative Editing Hooks\n *\n * Real-time collaborative editing using Yjs.\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport * as Y from 'yjs'\nimport { useRecordContext } from '../context'\nimport { debugLog } from '../../debug'\nimport { MSG_YJS_SYNC, MSG_YJS_AWARENESS, MSG } from '@/shared/protocol/constants'\nimport {\n MSG_SYNC_STEP1,\n MSG_SYNC_STEP2,\n MSG_SYNC_UPDATE,\n Awareness,\n createEncoder,\n createDecoder,\n toUint8Array,\n writeVarUint,\n writeVarUint8Array,\n readVarUint,\n readVarUint8Array,\n} from '@/shared/protocol/yjs'\n\n// ============================================================================\n// useYjsField\n// ============================================================================\n\nexport interface UseYjsFieldResult {\n /** The Yjs document for this field */\n doc: Y.Doc\n /** Awareness instance for cursor/presence sync */\n awareness: Awareness\n /** Whether initial sync is complete */\n synced: boolean\n /** Whether user has write access */\n canWrite: boolean\n /** Force re-render counter (changes when doc updates) */\n updateCount: number\n}\n\n/**\n * Hook for collaborative editing of a Yjs field.\n *\n * @param collection - Collection name\n * @param recordId - Record ID\n * @param fieldName - Field name (must be type: 'yjs' in schema)\n *\n * @example\n * const { doc, synced, canWrite } = useYjsField('documents', docId, 'content')\n * const text = doc.getText('content')\n */\nexport function useYjsField(\n collection: string,\n recordId: string,\n fieldName: string,\n): UseYjsFieldResult {\n const { sendMessage, sendBinary, onBinaryMessage, registerYjsJoinHandler, ready } =\n useRecordContext()\n const [synced, setSynced] = useState(false)\n const [canWrite, setCanWrite] = useState(false)\n const [updateCount, setUpdateCount] = useState(0)\n const canWriteRef = useRef(false)\n\n // Stable Yjs doc instance\n const docRef = useRef<Y.Doc | null>(null)\n if (!docRef.current) {\n docRef.current = new Y.Doc()\n }\n const doc = docRef.current\n\n // Stable awareness instance (one per doc)\n const awarenessRef = useRef<Awareness | null>(null)\n if (!awarenessRef.current) {\n awarenessRef.current = new Awareness(doc)\n }\n const awareness = awarenessRef.current\n\n const docKey = `${collection}:${recordId}:${fieldName}`\n\n // Keep ref in sync with state for use in callbacks\n useEffect(() => {\n canWriteRef.current = canWrite\n }, [canWrite])\n\n // Handle join response from server\n useEffect(() => {\n return registerYjsJoinHandler(docKey, setCanWrite)\n }, [docKey, registerYjsJoinHandler])\n\n // Handle incoming binary messages (sync + awareness)\n useEffect(() => {\n const handleBinaryMessage = (data: ArrayBuffer) => {\n const decoder = createDecoder(new Uint8Array(data))\n const messageType = readVarUint(decoder)\n\n // Handle awareness messages\n if (messageType === MSG_YJS_AWARENESS) {\n const msgDocKey = new TextDecoder().decode(readVarUint8Array(decoder))\n if (msgDocKey !== docKey) return\n const payload = readVarUint8Array(decoder)\n awareness.applyUpdate(payload)\n return\n }\n\n if (messageType !== MSG_YJS_SYNC) return\n\n const msgDocKey = new TextDecoder().decode(readVarUint8Array(decoder))\n if (msgDocKey !== docKey) return\n\n const syncType = readVarUint(decoder)\n const payload = readVarUint8Array(decoder)\n\n switch (syncType) {\n case MSG_SYNC_STEP1: {\n // Server sent state vector - respond with our diff\n const ourDiff = Y.encodeStateAsUpdate(doc, payload)\n const encoder = createEncoder()\n writeVarUint(encoder, MSG_YJS_SYNC)\n writeVarUint8Array(encoder, new TextEncoder().encode(docKey))\n writeVarUint(encoder, MSG_SYNC_STEP2)\n writeVarUint8Array(encoder, ourDiff)\n sendBinary(toUint8Array(encoder))\n setSynced(true)\n break\n }\n case MSG_SYNC_STEP2: {\n // Server sent full state diff\n Y.applyUpdate(doc, payload, 'server')\n setSynced(true)\n break\n }\n case MSG_SYNC_UPDATE: {\n // Server relayed update from another client\n Y.applyUpdate(doc, payload, 'server')\n break\n }\n }\n }\n\n return onBinaryMessage(handleBinaryMessage)\n }, [doc, awareness, docKey, sendBinary, onBinaryMessage])\n\n // Send local awareness changes to server\n useEffect(() => {\n if (!ready) return\n\n const handleAwarenessUpdate = ({\n added,\n updated,\n removed,\n }: {\n added: number[]\n updated: number[]\n removed: number[]\n }) => {\n const changedClients = added.concat(updated).concat(removed)\n if (changedClients.length === 0) return\n\n const encoder = createEncoder()\n writeVarUint(encoder, MSG_YJS_AWARENESS)\n writeVarUint8Array(encoder, new TextEncoder().encode(docKey))\n writeVarUint8Array(encoder, awareness.encodeUpdate(changedClients))\n sendBinary(toUint8Array(encoder))\n }\n\n awareness.on('update', handleAwarenessUpdate)\n return () => {\n awareness.off('update', handleAwarenessUpdate)\n }\n }, [awareness, docKey, sendBinary, ready])\n\n // Join channel when ready - re-join on reconnect\n useEffect(() => {\n if (!ready) {\n // Reset state when disconnected\n debugLog('[YJS] Not ready, resetting:', docKey)\n setSynced(false)\n setCanWrite(false)\n return\n }\n\n debugLog('[YJS] Joining:', docKey)\n sendMessage({ type: MSG.YJS_JOIN, payload: { collection, recordId, fieldName } })\n\n return () => {\n // Clear local awareness state before leaving\n awareness.setLocalState(null)\n debugLog('[YJS] Leaving:', docKey)\n sendMessage({ type: MSG.YJS_LEAVE, payload: { collection, recordId, fieldName } })\n }\n }, [ready, collection, recordId, fieldName, sendMessage, docKey, awareness])\n\n // Send local updates to server\n useEffect(() => {\n const handleUpdate = (update: Uint8Array, origin: unknown) => {\n setUpdateCount((c) => c + 1)\n\n // Only send local updates when we have write permission\n if (origin === 'server' || !canWriteRef.current) return\n\n const encoder = createEncoder()\n writeVarUint(encoder, MSG_YJS_SYNC)\n writeVarUint8Array(encoder, new TextEncoder().encode(docKey))\n writeVarUint(encoder, MSG_SYNC_UPDATE)\n writeVarUint8Array(encoder, update)\n sendBinary(toUint8Array(encoder))\n }\n\n doc.on('update', handleUpdate)\n return () => {\n doc.off('update', handleUpdate)\n }\n }, [doc, docKey, sendBinary])\n\n // synced is only true when WebSocket is connected AND initial sync is complete\n return { doc, awareness, synced: ready && synced, canWrite, updateCount }\n}\n\n// ============================================================================\n// useYjsText\n// ============================================================================\n\nexport interface UseYjsTextResult {\n /** Current text content */\n text: string\n /** Set text content (replaces entire content) */\n setText: (value: string) => void\n /** Whether initial sync is complete */\n synced: boolean\n /** Whether user has write access */\n canWrite: boolean\n}\n\n/**\n * Simplified hook for collaborative text editing.\n *\n * Provides a string-based API instead of raw Yjs doc access.\n * Perfect for textarea/input fields.\n *\n * @param collection - Collection name\n * @param recordId - Record ID\n * @param fieldName - Field name (must be type: 'yjs' in schema)\n *\n * @example\n * const { text, setText, synced, canWrite } = useYjsText('documents', docId, 'content')\n *\n * <textarea\n * value={text}\n * onChange={(e) => setText(e.target.value)}\n * disabled={!synced || !canWrite}\n * />\n */\nexport function useYjsText(\n collection: string,\n recordId: string,\n fieldName: string,\n): UseYjsTextResult {\n const { doc, synced, canWrite, updateCount } = useYjsField(collection, recordId, fieldName)\n const [text, setTextState] = useState('')\n const yTextRef = useRef<Y.Text | null>(null)\n const isLocalUpdateRef = useRef(false)\n\n // Get Y.Text instance once doc is ready\n useEffect(() => {\n yTextRef.current = doc.getText(fieldName)\n }, [doc, fieldName])\n\n // Sync Y.Text to local state when doc updates\n useEffect(() => {\n if (yTextRef.current && synced && !isLocalUpdateRef.current) {\n setTextState(yTextRef.current.toString())\n }\n isLocalUpdateRef.current = false\n }, [synced, updateCount])\n\n // setText function that updates both local state and Yjs\n const setText = useCallback(\n (value: string) => {\n setTextState(value)\n\n if (yTextRef.current && canWrite) {\n isLocalUpdateRef.current = true\n doc.transact(() => {\n const yText = yTextRef.current!\n yText.delete(0, yText.length)\n yText.insert(0, value)\n })\n }\n },\n [doc, canWrite],\n )\n\n return { text, setText, synced, canWrite }\n}\n","/**\n * useUserLookup Hook\n * \n * Provides helper functions to look up user information by userId.\n * This is a convenience wrapper around useUsers() that provides O(1) lookups.\n * \n * The authoritative user data comes from the users collection.\n */\n\nimport { useMemo, useCallback } from 'react'\nimport { useUsers } from './useUsers'\n\nexport interface UserInfo {\n id: string\n email: string\n name: string\n imageUrl?: string\n role: string\n}\n\n/**\n * Hook for looking up user information by ID.\n * \n * @example\n * ```tsx\n * const { getUser, getEmail, getName } = useUserLookup()\n * \n * // Get full user info\n * const user = getUser(userId)\n * \n * // Get just email\n * const email = getEmail(userId)\n * ```\n */\nexport function useUserLookup() {\n const { users, usersLoaded } = useUsers()\n \n // Build a lookup map for O(1) access\n const userMap = useMemo(() => {\n const map = new Map<string, UserInfo>()\n for (const user of users) {\n map.set(user.id, {\n id: user.id,\n email: user.email,\n name: user.name,\n imageUrl: user.imageUrl,\n role: user.role,\n })\n }\n return map\n }, [users])\n \n /**\n * Get full user info by userId\n */\n const getUser = useCallback((userId: string): UserInfo | null => {\n return userMap.get(userId) ?? null\n }, [userMap])\n \n /**\n * Get user's email by userId\n */\n const getEmail = useCallback((userId: string): string | null => {\n return userMap.get(userId)?.email ?? null\n }, [userMap])\n \n /**\n * Get user's name by userId\n */\n const getName = useCallback((userId: string): string | null => {\n return userMap.get(userId)?.name ?? null\n }, [userMap])\n \n return {\n /** All users from the users collection */\n users,\n /** True once the user list has been received from the server. */\n usersLoaded,\n /** Map of userId -> UserInfo for direct access */\n userMap,\n /** Get full user info by userId */\n getUser,\n /** Get user email by userId */\n getEmail,\n /** Get user name by userId */\n getName,\n }\n}\n","/**\n * usePresence — Derives online/offline from lastSeenAt in users collection.\n *\n * Two mechanisms keep presence working:\n * 1. Heartbeat: Sends MSG.USER_UPDATE every 60s so the server refreshes\n * our lastSeenAt in the c_users table and broadcasts the change.\n * 2. Tick: A re-render trigger every 30s so isOnline() re-evaluates\n * Date.now() and users who stopped heartbeating transition to offline.\n */\n\nimport { useEffect, useCallback, useState } from 'react'\nimport { useUser } from './useUser'\nimport { useUsers } from './useUsers'\nimport { useRecordContext } from '../context'\nimport { MSG } from '@/shared/protocol/constants'\n\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000 // 5 minutes\nconst HEARTBEAT_INTERVAL_MS = 60 * 1000 // 1 minute\nconst TICK_INTERVAL_MS = 30 * 1000 // 30 seconds\n\ninterface UsePresenceOptions {\n timeoutMs?: number\n}\n\nexport function usePresence(opts?: UsePresenceOptions) {\n const { user } = useUser()\n const { users } = useUsers()\n const { sendMessage, ready } = useRecordContext()\n const [tick, setTick] = useState(0)\n\n const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS\n\n // Heartbeat: periodically send MSG.USER_UPDATE to refresh our lastSeenAt\n useEffect(() => {\n if (!user || !ready) return\n const interval = setInterval(() => {\n sendMessage({ type: MSG.USER_UPDATE, payload: {} })\n }, HEARTBEAT_INTERVAL_MS)\n return () => clearInterval(interval)\n }, [user, ready, sendMessage])\n\n // Tick: force re-render so isOnline() re-evaluates stale timestamps\n useEffect(() => {\n const interval = setInterval(() => {\n setTick((t) => t + 1)\n }, TICK_INTERVAL_MS)\n return () => clearInterval(interval)\n }, [])\n\n const isOnline = useCallback(\n (userId: string): boolean => {\n // tick is captured to ensure this callback refreshes on each tick\n void tick\n const u = users.find((u) => u.id === userId)\n if (!u?.lastSeenAt) return false\n return Date.now() - new Date(u.lastSeenAt).getTime() < timeoutMs\n },\n [users, timeoutMs, tick],\n )\n\n const getLastSeen = useCallback(\n (userId: string): string | null => {\n const u = users.find((u) => u.id === userId)\n return u?.lastSeenAt ?? null\n },\n [users],\n )\n\n return { isOnline, getLastSeen, users }\n}\n","/**\n * useYjsRoom — Connect to a dedicated YjsRoom Durable Object.\n *\n * Unlike useYjsField (which piggybacks on RecordRoom's WebSocket),\n * this hook opens a direct WebSocket to a YjsRoom DO at /ws/yjs/:docId.\n * Each document gets its own DO for horizontal scaling.\n *\n * Uses the shared yjs-protocol.ts encoding — no duplication.\n *\n * @example\n * const { doc, text, setText, synced, canWrite } = useYjsRoom(docId, 'content')\n */\n\nimport { useState, useEffect, useCallback, useRef, useMemo } from 'react'\nimport * as Y from 'yjs'\nimport { getAuthToken } from '../../auth'\nimport { wsLog } from '../ws-log'\nimport {\n MSG_AWARENESS,\n MSG_SYNC,\n MSG_SYNC_STEP1,\n MSG_SYNC_STEP2,\n MSG_SYNC_UPDATE,\n Awareness,\n createEncoder,\n createDecoder,\n toUint8Array,\n encodeAwarenessMessage,\n handleAwarenessMessage,\n writeVarUint,\n writeVarUint8Array,\n readVarUint,\n readVarUint8Array,\n} from '@/shared/protocol/yjs'\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport interface UseYjsRoomResult {\n /** The Yjs document */\n doc: Y.Doc\n /** Awareness instance for presence/typing state */\n awareness: Awareness\n /** Current text content (for the specified field) */\n text: string\n /** Set text (replaces full content) */\n setText: (value: string) => void\n /** Whether initial sync is complete */\n synced: boolean\n /** Whether user has write access */\n canWrite: boolean\n}\n\n/**\n * Connect to a YjsRoom DO for collaborative editing.\n *\n * @param docId - Document identifier (maps to DO name)\n * @param fieldName - Y.Text field name within the Y.Doc\n */\nexport function useYjsRoom(docId: string, fieldName: string): UseYjsRoomResult {\n const [synced, setSynced] = useState(false)\n const [canWrite, setCanWrite] = useState(false)\n const [text, setTextState] = useState('')\n const [, setUpdateCount] = useState(0)\n\n const docRef = useRef<Y.Doc | null>(null)\n if (!docRef.current) docRef.current = new Y.Doc()\n const doc = docRef.current\n\n const awarenessRef = useRef<Awareness | null>(null)\n if (!awarenessRef.current) awarenessRef.current = new Awareness(doc)\n const awareness = awarenessRef.current\n\n const wsRef = useRef<WebSocket | null>(null)\n const isLocalRef = useRef(false)\n const applyingRemoteAwarenessRef = useRef(false)\n\n const yText = useMemo(() => doc.getText(fieldName), [doc, fieldName])\n\n // Observe remote Y.Text changes\n useEffect(() => {\n const observer = () => {\n if (isLocalRef.current) return\n setTextState(yText.toString())\n }\n yText.observe(observer)\n return () => yText.unobserve(observer)\n }, [yText])\n\n // WebSocket connection to YjsRoom DO\n useEffect(() => {\n let ws: WebSocket | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let alive = true\n\n const connect = async () => {\n if (!alive) return\n\n const token = await getAuthToken()\n // Unmounted (or reconnect superseded) while awaiting the token — bail so\n // we don't open a socket the cleanup can no longer reach and close.\n if (!alive) return\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const baseUrl = `${protocol}//${window.location.host}`\n const url = new URL(`/ws/yjs/${encodeURIComponent(docId)}`, baseUrl)\n if (token) url.searchParams.set('token', token)\n\n wsLog('connecting', `yjs:${docId}`)\n ws = new WebSocket(url.toString())\n ws.binaryType = 'arraybuffer'\n wsRef.current = ws\n\n ws.onopen = () => {\n wsLog('connected', `yjs:${docId}`)\n setSynced(false)\n }\n\n ws.onmessage = (event) => {\n if (typeof event.data === 'string') {\n try {\n const msg = JSON.parse(event.data) as { type?: string; canWrite?: unknown }\n if (msg.type === 'auth' && typeof msg.canWrite === 'boolean') setCanWrite(msg.canWrite)\n } catch {\n /* ignore */\n }\n return\n }\n\n const data = new Uint8Array(event.data as ArrayBuffer)\n const decoder = createDecoder(data)\n const messageType = readVarUint(decoder)\n\n if (messageType === MSG_SYNC) {\n const syncType = readVarUint(decoder)\n const payload = readVarUint8Array(decoder)\n\n switch (syncType) {\n case MSG_SYNC_STEP1: {\n const diff = Y.encodeStateAsUpdate(doc, payload)\n const enc = createEncoder()\n writeVarUint(enc, MSG_SYNC)\n writeVarUint(enc, MSG_SYNC_STEP2)\n writeVarUint8Array(enc, diff)\n ws?.send(toUint8Array(enc).buffer)\n setSynced(true)\n break\n }\n case MSG_SYNC_STEP2: {\n Y.applyUpdate(doc, payload, 'server')\n setTextState(yText.toString())\n setSynced(true)\n break\n }\n case MSG_SYNC_UPDATE: {\n Y.applyUpdate(doc, payload, 'server')\n setUpdateCount((c) => c + 1)\n break\n }\n }\n return\n }\n\n if (messageType === MSG_AWARENESS) {\n applyingRemoteAwarenessRef.current = true\n try {\n handleAwarenessMessage(awareness, data)\n } finally {\n applyingRemoteAwarenessRef.current = false\n }\n }\n }\n\n ws.onclose = () => {\n wsLog('disconnected', `yjs:${docId}`)\n wsRef.current = null\n setSynced(false)\n if (alive) reconnectTimer = setTimeout(connect, 1000)\n }\n\n ws.onerror = () => ws?.close()\n }\n\n connect()\n\n return () => {\n wsLog('closing', `yjs:${docId}`)\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n if (ws) {\n ws.onclose = null\n ws.onmessage = null\n ws.onerror = null\n ws.close()\n }\n wsRef.current = null\n }\n }, [doc, awareness, docId, yText])\n\n // Relay local awareness updates (presence, typing) through YjsRoom with shared MSG_AWARENESS encoding.\n useEffect(() => {\n const handler = ({\n added,\n updated,\n removed,\n }: {\n added: number[]\n updated: number[]\n removed: number[]\n }) => {\n if (applyingRemoteAwarenessRef.current) return\n const changedClients = added.concat(updated).concat(removed)\n if (changedClients.length === 0) return\n\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) return\n\n ws.send(encodeAwarenessMessage(awareness, changedClients).buffer)\n }\n\n awareness.on('update', handler)\n return () => {\n awareness.off('update', handler)\n awareness.setLocalState(null)\n }\n }, [awareness])\n\n // Send local Y.Doc updates to server\n useEffect(() => {\n const handler = (update: Uint8Array, origin: unknown) => {\n if (origin === 'server') return\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) return\n\n const enc = createEncoder()\n writeVarUint(enc, MSG_SYNC)\n writeVarUint(enc, MSG_SYNC_UPDATE)\n writeVarUint8Array(enc, update)\n ws.send(toUint8Array(enc).buffer)\n }\n\n doc.on('update', handler)\n return () => {\n doc.off('update', handler)\n }\n }, [doc])\n\n // setText: update Y.Text + local state\n const setText = useCallback(\n (value: string) => {\n setTextState(value)\n if (!canWrite) return\n\n isLocalRef.current = true\n doc.transact(() => {\n yText.delete(0, yText.length)\n yText.insert(0, value)\n })\n isLocalRef.current = false\n },\n [doc, yText, canWrite],\n )\n\n return { doc, awareness, text, setText, synced, canWrite }\n}\n","/**\n * useGameRoom — Connect to a GameRoom Durable Object.\n *\n * Opens a WebSocket to /ws/game/:roomId for real-time game state.\n *\n * @example\n * const { state, sendInput, players, connected } = useGameRoom('my-game')\n *\n * Implementation note: this hook uses the typed wire protocol helpers\n * (`clientBuild` for sends, `dispatch` for receives) from\n * `shared/protocol/messages`. Hand-rolled `switch (msg.type)` blocks and\n * raw `JSON.stringify({ type: MSG.X, payload: ... })` sends are a fast\n * route to the \"silent drop\" bug class when a payload shape drifts —\n * prefer the typed helpers instead.\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { getAuthToken } from '../../auth'\nimport { wsLog } from '../ws-log'\nimport { MSG } from '@/shared/protocol/constants'\nimport {\n clientBuild,\n dispatch,\n encode,\n type ServerMessage,\n} from '@/shared/protocol/messages'\n\nexport interface GamePlayer {\n userId: string\n userName: string\n ready: boolean\n connectedAt: string\n data: Record<string, unknown>\n}\n\nexport interface UseGameRoomResult {\n /** Current game state */\n state: Record<string, unknown>\n /** Current tick number */\n tick: number\n /** Connected players */\n players: GamePlayer[]\n /** Whether the game is currently running */\n running: boolean\n /** Whether WebSocket is connected */\n connected: boolean\n /**\n * Whether this connection has write access. False for viewers /\n * unauthenticated spectators — they receive state broadcasts but the\n * send callbacks below no-op. UIs should disable input controls when\n * false so spectators see why their actions don't take effect.\n */\n canWrite: boolean\n /** Send a game input (no-op for spectators) */\n sendInput: (action: string, data?: Record<string, unknown>) => void\n /** Mark self as ready (no-op for spectators) */\n setReady: () => void\n /** Request game start (no-op for spectators) */\n startGame: () => void\n /** Request game end (no-op for spectators) */\n endGame: () => void\n}\n\nexport function useGameRoom(roomId: string): UseGameRoomResult {\n const [gameState, setGameState] = useState<Record<string, unknown>>({})\n const [tick, setTick] = useState(0)\n const [players, setPlayers] = useState<GamePlayer[]>([])\n const [running, setRunning] = useState(false)\n const [connected, setConnected] = useState(false)\n // Default false: assume spectator until the server's AUTH frame\n // confirms otherwise. See GameRoom.onConnect.\n const [canWrite, setCanWrite] = useState(false)\n const wsRef = useRef<WebSocket | null>(null)\n\n useEffect(() => {\n let ws: WebSocket | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let alive = true\n\n const connect = async () => {\n if (!alive) return\n\n const token = await getAuthToken()\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const baseUrl = `${protocol}//${window.location.host}`\n const url = new URL(`/ws/game/${encodeURIComponent(roomId)}`, baseUrl)\n if (token) url.searchParams.set('token', token)\n\n wsLog('connecting', `game:${roomId}`)\n ws = new WebSocket(url.toString())\n wsRef.current = ws\n\n ws.onopen = () => {\n wsLog('connected', `game:${roomId}`)\n setConnected(true)\n }\n\n ws.onmessage = (event) => {\n dispatch<ServerMessage>(event.data, {\n [MSG.AUTH]: (p) => {\n setCanWrite(p.canWrite)\n },\n [MSG.GAME_STATE]: (p) => {\n setGameState(p.state as Record<string, unknown>)\n setTick(p.tick)\n setPlayers(p.players as GamePlayer[])\n setRunning(p.running)\n },\n [MSG.GAME_TICK]: (p) => {\n setGameState(p.state as Record<string, unknown>)\n setTick(p.tick)\n },\n [MSG.GAME_PLAYER_JOIN]: (p) => {\n const player = p.player as GamePlayer\n setPlayers((prev) => [...prev.filter((x) => x.userId !== player.userId), player])\n },\n [MSG.GAME_PLAYER_LEAVE]: (p) => {\n setPlayers((prev) => prev.filter((x) => x.userId !== p.userId))\n },\n [MSG.GAME_PLAYER_READY]: (p) => {\n setPlayers((prev) =>\n prev.map((x) => (x.userId === p.userId ? { ...x, ready: true } : x)),\n )\n },\n [MSG.GAME_START]: (p) => {\n setRunning(true)\n setGameState(p.state as Record<string, unknown>)\n setTick(p.tick)\n },\n [MSG.GAME_END]: (p) => {\n setRunning(false)\n setGameState(p.state as Record<string, unknown>)\n },\n })\n }\n\n ws.onclose = () => {\n wsLog('disconnected', `game:${roomId}`)\n wsRef.current = null\n setConnected(false)\n // Reset to the safe default so a reconnect with a degraded role\n // doesn't leave input/control buttons enabled until the new AUTH\n // frame lands. See useCanvas onclose for the rationale.\n setCanWrite(false)\n if (alive) reconnectTimer = setTimeout(connect, 1000)\n }\n\n ws.onerror = () => ws?.close()\n }\n\n connect()\n\n return () => {\n wsLog('closing', `game:${roomId}`)\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n if (ws) {\n ws.onclose = null\n ws.onmessage = null\n ws.onerror = null\n ws.close()\n }\n wsRef.current = null\n }\n }, [roomId])\n\n // Local write-gate. The server enforces the same rule in\n // GameRoom.onMessage; this is the UX side so spectator clicks don't\n // even leave the browser.\n const send = useCallback(\n <M extends { type: string; payload: unknown }>(message: M) => {\n if (!canWrite) return\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) return\n ws.send(encode(message))\n },\n [canWrite],\n )\n\n const sendInput = useCallback(\n (action: string, data: Record<string, unknown> = {}) => send(clientBuild.gameInput(action, data)),\n [send],\n )\n\n const setReady = useCallback(() => send(clientBuild.gamePlayerReady()), [send])\n const startGame = useCallback(() => send(clientBuild.gameStart()), [send])\n const endGame = useCallback(() => send(clientBuild.gameEnd()), [send])\n\n return { state: gameState, tick, players, running, connected, canWrite, sendInput, setReady, startGame, endGame }\n}\n","/**\n * useCanvas — Connect to a CanvasRoom Durable Object.\n *\n * Opens a WebSocket to /ws/canvas/:roomId for collaborative spatial editing.\n *\n * @example\n * const { shapes, addShape, moveShape, deleteShape, viewports } = useCanvas('my-canvas')\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { getAuthToken } from '../../auth'\nimport { wsLog } from '../ws-log'\nimport { MSG } from '@/shared/protocol/constants'\nimport {\n clientBuild,\n dispatch,\n encode,\n type ServerMessage,\n} from '@/shared/protocol/messages'\n\nexport interface CanvasShapeClient {\n id: string\n type: string\n x: number\n y: number\n width: number\n height: number\n rotation?: number\n props: Record<string, unknown>\n createdBy: string\n createdAt: string\n updatedAt: string\n}\n\nexport interface ViewportClient {\n userId: string\n x: number\n y: number\n width: number\n height: number\n zoom: number\n}\n\nexport interface UseCanvasResult {\n /** All shapes on the canvas */\n shapes: CanvasShapeClient[]\n /** Other users' viewports */\n viewports: ViewportClient[]\n /** Whether WebSocket is connected */\n connected: boolean\n /**\n * Whether this connection has write access (member/admin role). The\n * write callbacks below no-op when this is false; UIs should also use\n * it to disable controls so viewers see why their clicks do nothing.\n * Viewport reporting stays available regardless — it's presence info.\n */\n canWrite: boolean\n /** Add a shape (no-op when canWrite is false) */\n addShape: (shape: Partial<CanvasShapeClient>) => void\n /** Move a shape (no-op when canWrite is false) */\n moveShape: (shapeId: string, x: number, y: number) => void\n /** Resize a shape (no-op when canWrite is false) */\n resizeShape: (shapeId: string, width: number, height: number, x?: number, y?: number) => void\n /** Delete a shape (no-op when canWrite is false) */\n deleteShape: (shapeId: string) => void\n /** Update shape properties (no-op when canWrite is false) */\n updateShape: (shapeId: string, props: Record<string, unknown>) => void\n /** Report local viewport (allowed for viewers — presence-like) */\n setViewport: (viewport: Omit<ViewportClient, 'userId'>) => void\n /** Undo last action (no-op when canWrite is false) */\n undo: () => void\n /** Redo last undone action (no-op when canWrite is false) */\n redo: () => void\n}\n\nexport function useCanvas(roomId: string): UseCanvasResult {\n const [shapes, setShapes] = useState<CanvasShapeClient[]>([])\n const [viewports, setViewports] = useState<ViewportClient[]>([])\n const [connected, setConnected] = useState(false)\n // Defaults to false: until the server's AUTH frame arrives, assume the\n // safer no-write posture so UIs don't briefly enable controls for a\n // viewer mid-handshake.\n const [canWrite, setCanWrite] = useState(false)\n const wsRef = useRef<WebSocket | null>(null)\n\n useEffect(() => {\n let ws: WebSocket | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let alive = true\n\n const connect = async () => {\n if (!alive) return\n\n const token = await getAuthToken()\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const baseUrl = `${protocol}//${window.location.host}`\n const url = new URL(`/ws/canvas/${encodeURIComponent(roomId)}`, baseUrl)\n if (token) url.searchParams.set('token', token)\n\n wsLog('connecting', `canvas:${roomId}`)\n ws = new WebSocket(url.toString())\n wsRef.current = ws\n\n ws.onopen = () => {\n wsLog('connected', `canvas:${roomId}`)\n setConnected(true)\n }\n\n ws.onmessage = (event) => {\n // Typed dispatch — handler payloads are narrowed by the SDK's\n // discriminated `ServerMessage` union. Shape/viewport objects\n // on the wire are typed as `unknown` (the protocol layer\n // doesn't know the app's domain types), so we cast to the\n // local `CanvasShapeClient` / `ViewportClient` where we use\n // them.\n dispatch<ServerMessage>(event.data, {\n [MSG.AUTH]: (p) => {\n setCanWrite(p.canWrite)\n },\n [MSG.CANVAS_SHAPES]: (p) => {\n setShapes(p.shapes as CanvasShapeClient[])\n setViewports(p.viewports as ViewportClient[])\n },\n [MSG.CANVAS_ADD]: (p) => {\n const shape = p.shape as CanvasShapeClient\n setShapes((prev) => [...prev.filter((s) => s.id !== shape.id), shape])\n },\n [MSG.CANVAS_MOVE]: (p) => {\n setShapes((prev) =>\n prev.map((s) => (s.id === p.shapeId ? { ...s, x: p.x, y: p.y } : s)),\n )\n },\n [MSG.CANVAS_RESIZE]: (p) => {\n setShapes((prev) =>\n prev.map((s) => {\n if (s.id !== p.shapeId) return s\n const updated = { ...s, width: p.width, height: p.height }\n if (p.x !== undefined) updated.x = p.x\n if (p.y !== undefined) updated.y = p.y\n return updated\n }),\n )\n },\n [MSG.CANVAS_DELETE]: (p) => {\n setShapes((prev) => prev.filter((s) => s.id !== p.shapeId))\n },\n [MSG.CANVAS_UPDATE]: (p) => {\n setShapes((prev) =>\n prev.map((s) =>\n s.id === p.shapeId ? { ...s, props: { ...s.props, ...p.props } } : s,\n ),\n )\n },\n [MSG.CANVAS_VIEWPORT]: (p) => {\n // The payload is a union of `{ viewport }` and\n // `{ userId, removed: true }` — narrow by presence.\n if ('removed' in p) {\n const removed = p\n setViewports((prev) => prev.filter((v) => v.userId !== removed.userId))\n } else {\n const viewport = p.viewport as ViewportClient\n setViewports((prev) => [\n ...prev.filter((v) => v.userId !== viewport.userId),\n viewport,\n ])\n }\n },\n })\n }\n\n ws.onclose = () => {\n wsLog('disconnected', `canvas:${roomId}`)\n wsRef.current = null\n setConnected(false)\n // Reset to the safe default so a reconnect with a degraded role\n // (e.g. JWT expired, user now anon) doesn't leave write buttons\n // enabled until the new AUTH frame arrives. The fresh server\n // onConnect will re-send AUTH with the current canWrite value.\n setCanWrite(false)\n if (alive) reconnectTimer = setTimeout(connect, 1000)\n }\n\n ws.onerror = () => ws?.close()\n }\n\n connect()\n\n return () => {\n wsLog('closing', `canvas:${roomId}`)\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n if (ws) {\n ws.onclose = null\n ws.onmessage = null\n ws.onerror = null\n ws.close()\n }\n wsRef.current = null\n }\n }, [roomId])\n\n // Typed builders — `clientBuild.canvasX` returns a validated\n // `ClientMessage` that `encode` stringifies. No hand-rolled\n // `JSON.stringify({ type, payload })` calls here.\n const sendBuilt = useCallback(\n <M extends { type: string; payload: unknown }>(message: M) => {\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) return\n ws.send(encode(message))\n },\n [],\n )\n\n // Local write-gate: write callbacks short-circuit when canWrite is\n // false. The server enforces the same rule (see CanvasRoom.onMessage),\n // so this is a UX layer — viewers click a button and get instant\n // \"nothing happened\" instead of a round-trip ERROR.\n const sendWrite = useCallback(\n <M extends { type: string; payload: unknown }>(message: M) => {\n if (!canWrite) return\n sendBuilt(message)\n },\n [sendBuilt, canWrite],\n )\n\n const addShape = useCallback(\n (shape: Partial<CanvasShapeClient>) => sendWrite(clientBuild.canvasAdd(shape)),\n [sendWrite],\n )\n\n const moveShape = useCallback(\n (shapeId: string, x: number, y: number) =>\n sendWrite(clientBuild.canvasMove(shapeId, x, y)),\n [sendWrite],\n )\n\n const resizeShape = useCallback(\n (shapeId: string, width: number, height: number, x?: number, y?: number) =>\n sendWrite(clientBuild.canvasResize(shapeId, width, height, x, y)),\n [sendWrite],\n )\n\n const deleteShape = useCallback(\n (shapeId: string) => sendWrite(clientBuild.canvasDelete(shapeId)),\n [sendWrite],\n )\n\n const updateShape = useCallback(\n (shapeId: string, props: Record<string, unknown>) =>\n sendWrite(clientBuild.canvasUpdate(shapeId, props)),\n [sendWrite],\n )\n\n // Viewport stays on sendBuilt (not sendWrite): broadcasting \"I'm\n // looking here\" is presence-like and intentionally allowed for\n // viewers, matching the server's CanvasRoom gate.\n const setViewport = useCallback(\n (viewport: Omit<ViewportClient, 'userId'>) =>\n sendBuilt(clientBuild.canvasViewport(viewport)),\n [sendBuilt],\n )\n\n const undo = useCallback(() => sendWrite(clientBuild.canvasUndo()), [sendWrite])\n const redo = useCallback(() => sendWrite(clientBuild.canvasRedo()), [sendWrite])\n\n return { shapes, viewports, connected, canWrite, addShape, moveShape, resizeShape, deleteShape, updateShape, setViewport, undo, redo }\n}\n","/**\n * useCronMonitor — Connect to a CronRoom for monitoring scheduled tasks.\n *\n * Opens a WebSocket to /cron/:roomId for real-time task status.\n *\n * @example\n * const { tasks, history, trigger, pause, resume } = useCronMonitor('cron')\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { getAuthToken } from '../../auth'\nimport { MSG } from '@/shared/protocol/constants'\nimport {\n clientBuild,\n dispatch,\n encode,\n type ServerMessage,\n} from '@/shared/protocol/messages'\n\nexport interface CronTaskState {\n name: string\n intervalMinutes: number | null\n schedule: string | null\n timezone: string | null\n paused: boolean\n lastRunAt: string | null\n nextRunAt: string | null\n}\n\nexport interface CronHistoryEntry {\n taskName: string\n startedAt: string\n completedAt: string | null\n success: boolean\n durationMs: number\n error?: string\n}\n\nexport interface UseCronMonitorResult {\n /** Current task states */\n tasks: CronTaskState[]\n /** Execution history */\n history: CronHistoryEntry[]\n /** Whether WebSocket is connected */\n connected: boolean\n /**\n * Whether this connection can mutate cron state (trigger / pause /\n * resume). False for viewers and unauthenticated connections — the\n * mutation callbacks below no-op and UIs should disable those\n * controls. Reading tasks/history stays available either way.\n */\n canWrite: boolean\n /** Manually trigger a task (no-op when canWrite is false) */\n trigger: (taskName: string) => void\n /** Pause a task (no-op when canWrite is false) */\n pause: (taskName: string) => void\n /** Resume a paused task (no-op when canWrite is false) */\n resume: (taskName: string) => void\n}\n\nexport function useCronMonitor(roomId: string): UseCronMonitorResult {\n const [tasks, setTasks] = useState<CronTaskState[]>([])\n const [history, setHistory] = useState<CronHistoryEntry[]>([])\n const [connected, setConnected] = useState(false)\n // Default false: viewers/anon connections can read but not write. The\n // server's CronRoom.onConnect AUTH frame flips this for members/admins.\n const [canWrite, setCanWrite] = useState(false)\n const wsRef = useRef<WebSocket | null>(null)\n\n useEffect(() => {\n let ws: WebSocket | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let alive = true\n\n const connect = async () => {\n if (!alive) return\n\n const token = await getAuthToken()\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const baseUrl = `${protocol}//${window.location.host}`\n const url = new URL(`/ws/cron/${encodeURIComponent(roomId)}`, baseUrl)\n if (token) url.searchParams.set('token', token)\n\n ws = new WebSocket(url.toString())\n wsRef.current = ws\n\n ws.onopen = () => setConnected(true)\n\n ws.onmessage = (event) => {\n dispatch<ServerMessage>(event.data, {\n [MSG.AUTH]: (p) => {\n setCanWrite(p.canWrite)\n },\n [MSG.CRON_TASKS]: (p) => {\n setTasks(p.tasks as CronTaskState[])\n },\n [MSG.CRON_HISTORY]: (p) => {\n setHistory(p.history as CronHistoryEntry[])\n },\n [MSG.CRON_STATUS]: (p) => {\n setTasks(p.tasks as CronTaskState[])\n setHistory(p.recentHistory as CronHistoryEntry[])\n },\n })\n }\n\n ws.onclose = () => {\n wsRef.current = null\n setConnected(false)\n // Reset to the safe default so a reconnect with a degraded role\n // doesn't leave trigger/pause/resume controls enabled until the\n // new AUTH frame lands. See useCanvas onclose for the rationale.\n setCanWrite(false)\n if (alive) reconnectTimer = setTimeout(connect, 1000)\n }\n\n ws.onerror = () => ws?.close()\n }\n\n connect()\n\n return () => {\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n ws?.close()\n wsRef.current = null\n }\n }, [roomId])\n\n // Local write-gate. Mirrors the server check in CronRoom.onMessage so\n // viewer clicks short-circuit instead of round-tripping to ERROR.\n const sendWrite = useCallback(\n <M extends { type: string; payload: unknown }>(message: M) => {\n if (!canWrite) return\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) return\n ws.send(encode(message))\n },\n [canWrite],\n )\n\n const trigger = useCallback(\n (taskName: string) => sendWrite(clientBuild.cronTrigger(taskName)),\n [sendWrite],\n )\n const pause = useCallback(\n (taskName: string) => sendWrite(clientBuild.cronPause(taskName)),\n [sendWrite],\n )\n const resume = useCallback(\n (taskName: string) => sendWrite(clientBuild.cronResume(taskName)),\n [sendWrite],\n )\n\n return { tasks, history, connected, canWrite, trigger, pause, resume }\n}\n","/**\n * useJobs — Connect to a JobRoom for enqueueing and tracking background\n * jobs in real time.\n *\n * Opens a WebSocket to /ws/jobs/:roomId. The DO pushes a snapshot on\n * connect and JOB_UPDATE messages for every state change after that, so\n * `jobs` stays current without polling. `enqueue` returns a promise that\n * resolves with the new jobId once the server acks.\n *\n * Client-side names (`JobView`, `JobStatusView`) differ from the\n * server-side equivalents (`Job`, `JobStatus` in `deepspace/worker`) so\n * apps importing from both ends don't get type collisions.\n *\n * @example\n * const { jobs, enqueue, getJob, cancel } = useJobs(`app:${APP_NAME}`)\n *\n * async function onClick() {\n * const jobId = await enqueue('ai-summarize', { text })\n * // Now `getJob(jobId)` is reactive until the job reaches a terminal state.\n * }\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { getAuthToken } from '../../auth'\nimport { MSG } from '@/shared/protocol/constants'\nimport {\n clientBuild,\n dispatch,\n encode,\n type ServerMessage,\n} from '@/shared/protocol/messages'\n\n/** How long enqueue() waits for a server ack before rejecting. */\nconst ENQUEUE_TIMEOUT_MS = 10_000\n\nexport type JobStatusView =\n | 'queued'\n | 'running'\n | 'succeeded'\n | 'failed'\n | 'canceled'\n\nexport interface JobView<P = unknown, R = unknown> {\n id: string\n type: string\n status: JobStatusView\n payload?: P\n result?: R\n error?: string\n progress?: number\n progressMessage?: string\n attempts: number\n maxAttempts: number\n enqueuedAt: string\n startedAt?: string | null\n completedAt?: string | null\n enqueuedBy?: string | null\n}\n\nexport interface UseJobsResult<P = unknown, R = unknown> {\n /** All jobs the room currently knows about, newest-first. */\n jobs: JobView<P, R>[]\n /** WebSocket connection state. */\n connected: boolean\n /**\n * Enqueue a new job. Resolves with the new jobId once the server acks\n * (typically <100ms). Rejects if:\n * - the WebSocket isn't connected\n * - the WebSocket closes before the ack arrives\n * - no ack arrives within 10s (server dropped the message)\n */\n enqueue(\n type: string,\n payload?: P,\n options?: { maxAttempts?: number },\n ): Promise<string>\n /**\n * Reactive single-job lookup. Returns undefined while the snapshot is\n * loading or if the job has been GC'd by the room's TTL sweep.\n */\n getJob(jobId: string): JobView<P, R> | undefined\n /** Best-effort cancel. The job is marked canceled even if the handler ignores `ctx.signal`. */\n cancel(jobId: string): void\n /** Manually re-queue a failed/canceled job. No-op for other statuses. */\n retry(jobId: string): void\n}\n\ninterface PendingEnqueue {\n resolve: (id: string) => void\n reject: (err: Error) => void\n timer: ReturnType<typeof setTimeout>\n}\n\n/**\n * @param roomId DO room id, typically `app:<APP_NAME>`. Apps with sharded\n * queues pass different ids — each id maps to its own DO instance.\n */\nexport function useJobs<P = unknown, R = unknown>(\n roomId: string,\n): UseJobsResult<P, R> {\n const [jobs, setJobs] = useState<JobView<P, R>[]>([])\n const [connected, setConnected] = useState(false)\n const wsRef = useRef<WebSocket | null>(null)\n /**\n * Pending enqueue promises keyed by requestId. Each entry carries a\n * timeout timer so the caller doesn't hang forever if the server drops\n * the enqueue (validation error swallowed, isolate restart, etc.).\n */\n const pendingEnqueueRef = useRef<Map<string, PendingEnqueue>>(new Map())\n\n useEffect(() => {\n let ws: WebSocket | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let alive = true\n\n const upsert = (next: JobView<P, R>) => {\n setJobs((prev) => {\n const idx = prev.findIndex((j) => j.id === next.id)\n if (idx === -1) return [next, ...prev]\n const copy = prev.slice()\n copy[idx] = next\n return copy\n })\n }\n\n const rejectAllPending = (err: Error) => {\n for (const [id, pending] of pendingEnqueueRef.current.entries()) {\n clearTimeout(pending.timer)\n pending.reject(err)\n pendingEnqueueRef.current.delete(id)\n }\n }\n\n const connect = async () => {\n if (!alive) return\n\n const token = await getAuthToken()\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const baseUrl = `${protocol}//${window.location.host}`\n const url = new URL(`/ws/jobs/${encodeURIComponent(roomId)}`, baseUrl)\n if (token) url.searchParams.set('token', token)\n\n ws = new WebSocket(url.toString())\n wsRef.current = ws\n\n ws.onopen = () => setConnected(true)\n\n ws.onmessage = (event) => {\n dispatch<ServerMessage>(event.data, {\n [MSG.JOB_UPDATE]: (p) => {\n if (p.kind === 'snapshot' && Array.isArray(p.jobs)) {\n setJobs(p.jobs as JobView<P, R>[])\n return\n }\n const job = p.job as JobView<P, R> | undefined\n if (!job) return\n upsert(job)\n // Resolve the enqueue promise once the server acks.\n if (p.kind === 'enqueued' && typeof p.requestId === 'string') {\n const pending = pendingEnqueueRef.current.get(p.requestId)\n if (pending) {\n clearTimeout(pending.timer)\n pendingEnqueueRef.current.delete(p.requestId)\n pending.resolve(job.id)\n }\n }\n },\n })\n }\n\n ws.onclose = () => {\n wsRef.current = null\n setConnected(false)\n rejectAllPending(new Error('JobRoom WebSocket closed'))\n if (alive) reconnectTimer = setTimeout(connect, 1000)\n }\n\n ws.onerror = () => ws?.close()\n }\n\n connect()\n\n return () => {\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n rejectAllPending(new Error('useJobs unmounted'))\n ws?.close()\n wsRef.current = null\n }\n }, [roomId])\n\n const enqueue = useCallback(\n (type: string, payload?: P, options?: { maxAttempts?: number }) => {\n return new Promise<string>((resolve, reject) => {\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n reject(new Error('JobRoom WebSocket not connected'))\n return\n }\n const requestId = crypto.randomUUID()\n const timer = setTimeout(() => {\n pendingEnqueueRef.current.delete(requestId)\n reject(\n new Error(\n `JobRoom enqueue timed out (no server ack within ${ENQUEUE_TIMEOUT_MS}ms)`,\n ),\n )\n }, ENQUEUE_TIMEOUT_MS)\n pendingEnqueueRef.current.set(requestId, { resolve, reject, timer })\n // ws.send can throw synchronously if the socket transitions between\n // the readyState check and the call (browser-specific, quota errors).\n // Without this guard the pending entry would sit until the 10s\n // timeout — fine for safety, bad for caller latency.\n try {\n ws.send(\n encode(clientBuild.jobEnqueue(requestId, type, payload, options?.maxAttempts)),\n )\n } catch (e) {\n clearTimeout(timer)\n pendingEnqueueRef.current.delete(requestId)\n reject(e instanceof Error ? e : new Error(String(e)))\n }\n })\n },\n [],\n )\n\n const sendBuilt = useCallback(\n <M extends { type: string; payload: unknown }>(message: M): void => {\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) return\n ws.send(encode(message))\n },\n [],\n )\n\n const getJob = useCallback(\n (jobId: string) => jobs.find((j) => j.id === jobId),\n [jobs],\n )\n\n const cancel = useCallback(\n (jobId: string) => sendBuilt(clientBuild.jobCancel(jobId)),\n [sendBuilt],\n )\n\n const retry = useCallback(\n (jobId: string) => sendBuilt(clientBuild.jobRetry(jobId)),\n [sendBuilt],\n )\n\n return { jobs, connected, enqueue, getJob, cancel, retry }\n}\n","/**\n * usePresenceRoom — Connect to a PresenceRoom Durable Object.\n *\n * Opens a WebSocket to /ws/presence/:scopeId for real-time presence tracking.\n * Use with any scope: canvas, doc, thread, page, etc.\n *\n * Peers can share arbitrary state (cursor position, typing indicator,\n * viewport, selection) via updateState().\n *\n * @example\n * // Track who's on a canvas\n * const { peers, connected, updateState } = usePresenceRoom(`canvas:${canvasId}`)\n *\n * // Share cursor position\n * updateState({ cursor: { x: 100, y: 200 } })\n *\n * // Track who's viewing a thread\n * const { peers } = usePresenceRoom(`thread:${channelId}`)\n *\n * // Show typing indicator\n * updateState({ typing: true })\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { getAuthToken } from '../../auth'\nimport { wsLog } from '../ws-log'\nimport { MSG } from '@/shared/protocol/constants'\nimport {\n clientBuild,\n dispatch,\n encode,\n type ServerMessage,\n} from '@/shared/protocol/messages'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface PresencePeerClient {\n userId: string\n userName: string\n userEmail: string\n userImageUrl?: string\n joinedAt: string\n state: Record<string, unknown>\n}\n\nexport interface UsePresenceRoomResult {\n /** All peers currently present in this scope (excludes self) */\n peers: PresencePeerClient[]\n /** Whether the WebSocket is connected */\n connected: boolean\n /** Send a state update (cursor, typing, viewport, etc.) — merges with existing state */\n updateState: (state: Record<string, unknown>) => void\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function usePresenceRoom(scopeId: string): UsePresenceRoomResult {\n const [peers, setPeers] = useState<PresencePeerClient[]>([])\n const [connected, setConnected] = useState(false)\n const wsRef = useRef<WebSocket | null>(null)\n\n useEffect(() => {\n let ws: WebSocket | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let alive = true\n\n const connect = async () => {\n if (!alive) return\n\n const token = await getAuthToken()\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const baseUrl = `${protocol}//${window.location.host}`\n const url = new URL(`/ws/presence/${encodeURIComponent(scopeId)}`, baseUrl)\n if (token) url.searchParams.set('token', token)\n\n wsLog('connecting', `presence:${scopeId}`)\n ws = new WebSocket(url.toString())\n wsRef.current = ws\n\n ws.onopen = () => {\n wsLog('connected', `presence:${scopeId}`)\n setConnected(true)\n }\n\n ws.onmessage = (event) => {\n dispatch<ServerMessage>(event.data, {\n [MSG.PRESENCE_SYNC]: (p) => {\n setPeers(p.peers as PresencePeerClient[])\n },\n [MSG.PRESENCE_JOIN]: (p) => {\n const peer = p.peer as PresencePeerClient\n setPeers((prev) => [...prev.filter((x) => x.userId !== peer.userId), peer])\n },\n [MSG.PRESENCE_LEAVE]: (p) => {\n setPeers((prev) => prev.filter((x) => x.userId !== p.userId))\n },\n [MSG.PRESENCE_UPDATE]: (p) => {\n setPeers((prev) =>\n prev.map((x) =>\n x.userId === p.userId ? { ...x, state: { ...x.state, ...p.state } } : x,\n ),\n )\n },\n })\n }\n\n ws.onclose = () => {\n wsLog('disconnected', `presence:${scopeId}`)\n wsRef.current = null\n setConnected(false)\n if (alive) reconnectTimer = setTimeout(connect, 1000)\n }\n\n ws.onerror = () => ws?.close()\n }\n\n connect()\n\n return () => {\n wsLog('closing', `presence:${scopeId}`)\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n if (ws) {\n ws.onclose = null\n ws.onmessage = null\n ws.onerror = null\n ws.close()\n }\n wsRef.current = null\n }\n }, [scopeId])\n\n const updateState = useCallback((state: Record<string, unknown>) => {\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) return\n ws.send(encode(clientBuild.presenceUpdate(state)))\n }, [])\n\n return { peers, connected, updateState }\n}\n","/**\n * ConnectionStatus type and utility for domain hooks.\n *\n * Maps useQuery() status to a simpler connection state\n * used by app-level platform providers.\n */\n\nexport type ConnectionStatus = 'connecting' | 'connected' | 'reconnecting' | 'error'\n\n/**\n * Map a useQuery() status string to a ConnectionStatus.\n *\n * useQuery returns: 'pending' | 'ready' | 'error'\n * This maps to the simplified connection states used by platform providers.\n */\nexport function toConnectionStatus(queryStatus: string): ConnectionStatus {\n if (queryStatus === 'ready') return 'connected'\n if (queryStatus === 'error') return 'error'\n return 'connecting'\n}\n","/**\n * useR2Files — Scoped file storage hook for DeepSpace apps.\n *\n * Provides upload, download, list, and delete for files in R2.\n *\n * Two scopes are supported via the `scope` option:\n * - `'self'` (default): per-user storage. Reads require auth.\n * - `'app'`: app-wide storage. Reads are public — the upload URL works\n * directly as an `<img src>`. Use for avatars, logos, etc.\n *\n * @example\n * ```tsx\n * // Per-user files (default):\n * const { upload, downloadFile } = useR2Files()\n * await upload(myFile, 'photo.png')\n *\n * // App-shared files (public reads — usable in <img src>):\n * const { upload } = useR2Files({ scope: 'app' })\n * const r = await upload(file, `avatars/${userId}.png`)\n * // r.url is a plain, anon-readable URL.\n * ```\n */\n\nimport { useCallback, useState, useMemo } from 'react'\nimport { getAuthToken } from '../auth'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface R2UploadResult {\n success: boolean\n key?: string\n url?: string\n name?: string\n error?: string\n}\n\nexport interface R2FileInfo {\n key: string\n size: number\n uploaded: string\n url: string\n originalName?: string\n uploadedBy?: string\n}\n\nexport interface UseR2FilesReturn {\n /** Upload a File or Blob. Optionally provide a display name. */\n upload: (file: File | Blob, name?: string) => Promise<R2UploadResult>\n /** Upload raw base64 data. */\n uploadBase64: (base64Data: string, name: string, mimeType?: string) => Promise<R2UploadResult>\n /**\n * Delete a file. Accepts an R2FileInfo from `list()` or a raw key string.\n *\n * @example\n * ```tsx\n * const files = await list()\n * await deleteFile(files[0]) // pass the object\n * await deleteFile('widgets/abc/f.pdf') // or a raw key\n * ```\n */\n deleteFile: (fileOrKey: R2FileInfo | string) => Promise<{ success: boolean; error?: string }>\n /** List files, optionally filtered by a sub-prefix within the scope. */\n list: (prefix?: string) => Promise<R2FileInfo[]>\n /**\n * Download a file. Accepts an R2FileInfo from `list()` or a raw key string.\n * Uses an authenticated fetch and triggers a browser download via blob URL.\n *\n * When an R2FileInfo is passed, `originalName` is used as the download\n * filename automatically. When a string key is passed, an optional\n * `fileName` override can be provided.\n *\n * @example\n * ```tsx\n * const files = await list()\n * await downloadFile(files[0]) // uses originalName\n * await downloadFile('widgets/abc/f.pdf', 'report.pdf') // explicit name\n * ```\n */\n downloadFile: (fileOrKey: R2FileInfo | string, fileName?: string) => Promise<{ success: boolean; error?: string }>\n /**\n * Read a file's contents with authentication. Returns the raw Response.\n * Unlike `downloadFile`, this does NOT trigger a browser download —\n * it returns the Response so you can call `.text()`, `.blob()`,\n * `.arrayBuffer()`, `.json()`, etc.\n *\n * @example\n * ```tsx\n * const resp = await readFile('data/cleaned.txt')\n * const text = await resp.text()\n * ```\n */\n readFile: (fileOrKey: R2FileInfo | string) => Promise<Response>\n /**\n * Build a plain URL for a file. Accepts an R2FileInfo or a raw key string.\n *\n * **Note**: This URL has no auth token attached. It works for `'app'`-scope\n * files (which are publicly readable) but not for `'self'`-scope files —\n * those require an Authorization header, so use `readFile` / `downloadFile`.\n */\n getUrl: (fileOrKey: R2FileInfo | string) => string\n /** Whether an upload is currently in progress. */\n isUploading: boolean\n}\n\n/**\n * Scope controls where files live and who can read them:\n *\n * - `'self'` (default) — `apps/<app>/users/<userId>/…`. Per-user storage.\n * Reads require the caller's auth token; not usable from a plain `<img>`\n * or unauthenticated browser request.\n * - `'app'` — `apps/<app>/…`. Shared by everyone in the app. Reads are\n * public (no auth header needed), so the upload URL works directly as\n * an `<img src>`. Use this for avatars, logos, attachments, and other\n * assets meant to be embedded in pages.\n *\n * Uploads under `'app'` still require a signed-in user.\n */\nexport type R2Scope = { scope?: 'self' | 'app' }\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/** Extract key (and optional original name) from an R2FileInfo or raw string. */\nfunction resolveFile(fileOrKey: R2FileInfo | string): { key: string; name: string | undefined } {\n if (typeof fileOrKey === 'string') {\n return { key: fileOrKey, name: undefined }\n }\n return { key: fileOrKey.key, name: fileOrKey.originalName }\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useR2Files(options?: R2Scope): UseR2FilesReturn {\n const [isUploading, setIsUploading] = useState(false)\n const scope = options?.scope ?? 'self'\n\n /** Query string that tells the worker which scope to use. */\n const scopeParams = useMemo(() => {\n const params = new URLSearchParams()\n params.set('scope', scope)\n return params.toString()\n }, [scope])\n\n /** Get auth headers using the DeepSpace auth token. */\n const authHeaders = useCallback(async (): Promise<Record<string, string>> => {\n const token = await getAuthToken()\n if (token) {\n return { Authorization: `Bearer ${token}` }\n }\n return {}\n }, [])\n\n const upload = useCallback(async (file: File | Blob, name?: string): Promise<R2UploadResult> => {\n setIsUploading(true)\n try {\n const headers = await authHeaders()\n const formData = new FormData()\n formData.append('file', file)\n if (name) formData.append('name', name)\n\n const response = await fetch(`/api/files/upload?${scopeParams}`, {\n method: 'POST',\n headers,\n body: formData,\n })\n return await response.json() as R2UploadResult\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : 'Upload failed' }\n } finally {\n setIsUploading(false)\n }\n }, [scopeParams, authHeaders])\n\n const uploadBase64 = useCallback(async (base64Data: string, name: string, mimeType?: string): Promise<R2UploadResult> => {\n setIsUploading(true)\n try {\n const headers = await authHeaders()\n const response = await fetch(`/api/files/upload?${scopeParams}`, {\n method: 'POST',\n headers: { ...headers, 'Content-Type': 'application/json' },\n body: JSON.stringify({ data: base64Data, name, mimeType }),\n })\n return await response.json() as R2UploadResult\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : 'Upload failed' }\n } finally {\n setIsUploading(false)\n }\n }, [scopeParams, authHeaders])\n\n const deleteFile = useCallback(async (fileOrKey: R2FileInfo | string): Promise<{ success: boolean; error?: string }> => {\n const { key } = resolveFile(fileOrKey)\n try {\n const headers = await authHeaders()\n const response = await fetch(`/api/files/${key}?${scopeParams}`, { method: 'DELETE', headers })\n return await response.json() as { success: boolean; error?: string }\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : 'Delete failed' }\n }\n }, [scopeParams, authHeaders])\n\n const list = useCallback(async (prefix?: string): Promise<R2FileInfo[]> => {\n try {\n const headers = await authHeaders()\n const params = new URLSearchParams(scopeParams)\n if (prefix) params.set('prefix', prefix)\n const response = await fetch(`/api/files?${params.toString()}`, { headers })\n const result = await response.json() as { files: R2FileInfo[] }\n return result.files || []\n } catch {\n return []\n }\n }, [scopeParams, authHeaders])\n\n const downloadFile = useCallback(async (fileOrKey: R2FileInfo | string, fileName?: string): Promise<{ success: boolean; error?: string }> => {\n const { key, name: infoName } = resolveFile(fileOrKey)\n try {\n const headers = await authHeaders()\n const response = await fetch(`/api/files/${key}?${scopeParams}`, { headers })\n\n if (!response.ok) {\n const body = await response.json().catch(() => null) as { error?: string } | null\n return { success: false, error: body?.error ?? `Download failed (${response.status})` }\n }\n\n // Derive a display name: explicit arg > R2FileInfo.originalName > Content-Disposition > last key segment\n const disposition = response.headers.get('Content-Disposition')\n const dispositionName = disposition?.match(/filename=\"(.+?)\"/)?.[1]\n const fallbackName = key.split('/').pop() ?? 'download'\n const resolvedName = fileName ?? infoName ?? dispositionName ?? fallbackName\n\n const blob = await response.blob()\n const blobUrl = URL.createObjectURL(blob)\n\n const anchor = document.createElement('a')\n anchor.href = blobUrl\n anchor.download = resolvedName\n document.body.appendChild(anchor)\n anchor.click()\n\n // Clean up\n document.body.removeChild(anchor)\n URL.revokeObjectURL(blobUrl)\n\n return { success: true }\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : 'Download failed' }\n }\n }, [scopeParams, authHeaders])\n\n const readFile = useCallback(async (fileOrKey: R2FileInfo | string): Promise<Response> => {\n const { key } = resolveFile(fileOrKey)\n const headers = await authHeaders()\n const response = await fetch(`/api/files/${key}?${scopeParams}`, { headers })\n if (!response.ok) {\n const body = await response.text().catch(() => '')\n throw new Error(`Failed to read file (${response.status}): ${body}`)\n }\n return response\n }, [scopeParams, authHeaders])\n\n const getUrl = useCallback((fileOrKey: R2FileInfo | string): string => {\n const { key } = resolveFile(fileOrKey)\n return `/api/files/${key}?${scopeParams}`\n }, [scopeParams])\n\n return { upload, uploadBase64, deleteFile, downloadFile, readFile, list, getUrl, isUploading }\n}\n","/**\n * Shared types and utilities for file attachments across miniapps.\n */\n\nexport interface FileAttachment {\n key: string\n url: string\n name: string\n size: number\n mimeType: string\n}\n\nconst IMAGE_MIME_PREFIXES = ['image/']\n\nexport function isImageFile(mimeType: string): boolean {\n return IMAGE_MIME_PREFIXES.some(prefix => mimeType.startsWith(prefix))\n}\n\nexport function formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`\n}\n","/**\n * Deterministic user color assignment.\n * Same user ID always maps to the same color across sessions.\n */\n\nexport const DEFAULT_USER_COLORS = [\n '#f87171', '#fb923c', '#fbbf24', '#34d399',\n '#22d3ee', '#60a5fa', '#a78bfa', '#f472b6',\n '#e879f9', '#4ade80', '#38bdf8', '#facc15',\n] as const\n\nexport function getUserColor(userId: string, palette: readonly string[] = DEFAULT_USER_COLORS): string {\n let hash = 0\n for (let i = 0; i < userId.length; i++) {\n hash = ((hash << 5) - hash + userId.charCodeAt(i)) | 0\n }\n return palette[Math.abs(hash) % palette.length]\n}\n","/**\n * useMessages — subscribe to messages in a channel with send/edit/remove.\n */\n\nimport { useCallback, useMemo } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { Message } from './channel-types'\n\nexport function useMessages(channelId: string | undefined, options?: { parentMessageId?: string }) {\n const where = useMemo(() => {\n if (!channelId) return { channelId: '__none__' }\n const w: Record<string, unknown> = { channelId }\n if (options?.parentMessageId !== undefined) {\n w.parentMessageId = options.parentMessageId\n }\n return w\n }, [channelId, options?.parentMessageId])\n\n const { user } = useUser()\n\n const { records, status, error } = useQuery<Message>('messages', {\n where,\n orderBy: 'createdAt',\n orderDir: 'asc',\n })\n\n const { create, put, remove: removeMutation } = useMutations<Message>('messages')\n\n const send = useCallback(\n (content: string, parentMessageId?: string) => {\n if (!channelId || !user) return\n const data: Record<string, unknown> = { channelId, content, authorId: user.id, edited: false }\n if (parentMessageId) data.parentMessageId = parentMessageId\n return create(data as unknown as Message)\n },\n [channelId, user, create],\n )\n\n const edit = useCallback(\n (messageId: string, newContent: string) => {\n put(messageId, { content: newContent, edited: true } as unknown as Message)\n },\n [put],\n )\n\n /** Hard-delete: drops the row entirely. Use when threads/replies don't matter. */\n const remove = useCallback(\n (messageId: string) => {\n removeMutation(messageId)\n },\n [removeMutation],\n )\n\n /**\n * Soft-delete: sets `deleted: true` on the message. The row stays so threads,\n * read-receipts, and parent links remain coherent. UIs render a tombstone\n * (e.g. \"This message was deleted.\") for messages with `deleted === true`.\n */\n const softDelete = useCallback(\n (messageId: string) => {\n put(messageId, { deleted: true } as unknown as Message)\n },\n [put],\n )\n\n return {\n messages: records as RecordData<Message>[],\n status,\n error,\n send,\n edit,\n remove,\n softDelete,\n }\n}\n","/**\n * useChannels — subscribe to channels with create/archive/update.\n */\n\nimport { useCallback } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { Channel } from './channel-types'\n\nexport function useChannels() {\n const { user } = useUser()\n const { records, status, error } = useQuery<Channel>('channels', {\n orderBy: 'createdAt',\n orderDir: 'asc',\n })\n\n const { create: createMutation, put, remove } = useMutations<Channel>('channels')\n\n const create = useCallback(\n (data: { name: string; type: Channel['type']; description?: string }) => {\n return createMutation({\n name: data.name,\n type: data.type,\n description: data.description ?? '',\n createdBy: user?.id ?? '',\n archived: false,\n } as unknown as Channel)\n },\n [user, createMutation],\n )\n\n const archive = useCallback(\n (channelId: string) => {\n put(channelId, { archived: true } as unknown as Channel)\n },\n [put],\n )\n\n const update = useCallback(\n (channelId: string, data: Partial<Pick<Channel, 'name' | 'description'>>) => {\n put(channelId, data as unknown as Channel)\n },\n [put],\n )\n\n return {\n channels: records as RecordData<Channel>[],\n status,\n error,\n create,\n archive,\n update,\n remove,\n }\n}\n","/**\n * useReactions — subscribe to reactions in a channel with toggle.\n */\n\nimport { useCallback } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { Reaction } from './channel-types'\nimport type { GroupedReaction } from './types'\n\nexport function useReactions(channelId: string | undefined) {\n const { user } = useUser()\n\n const { records, status, error } = useQuery<Reaction>('reactions', {\n where: channelId ? { channelId } : { channelId: '__none__' },\n })\n\n const { create, remove: removeMutation } = useMutations<Reaction>('reactions')\n\n const getReactionsForMessage = useCallback(\n (messageId: string): GroupedReaction[] => {\n const messageReactions = records.filter((r) => r.data.messageId === messageId)\n const grouped = new Map<string, string[]>()\n\n for (const r of messageReactions) {\n if (!grouped.has(r.data.emoji)) grouped.set(r.data.emoji, [])\n grouped.get(r.data.emoji)!.push(r.data.userId)\n }\n\n return Array.from(grouped.entries()).map(([emoji, userIds]) => ({\n emoji,\n count: userIds.length,\n userIds,\n currentUserReacted: user ? userIds.includes(user.id) : false,\n }))\n },\n [records, user],\n )\n\n const toggle = useCallback(\n (messageId: string, emoji: string) => {\n if (!channelId || !user) return\n const existing = records.find(\n (r) => r.data.messageId === messageId && r.data.emoji === emoji && r.data.userId === user.id,\n )\n if (existing) {\n removeMutation(existing.recordId)\n } else {\n create({ messageId, channelId, emoji, userId: user.id } as unknown as Reaction)\n }\n },\n [channelId, user, records, create, removeMutation],\n )\n\n return { reactions: records as RecordData<Reaction>[], status, error, getReactionsForMessage, toggle }\n}\n","/**\n * useChannelMembers — subscribe to channel membership with join/leave.\n */\n\nimport { useCallback } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { ChannelMember } from './channel-types'\n\nexport function useChannelMembers(channelId: string | undefined) {\n const { user } = useUser()\n\n const { records, status, error } = useQuery<ChannelMember>('channel-members', {\n where: channelId ? { channelId } : { channelId: '__none__' },\n })\n\n const { create, remove: removeMutation } = useMutations<ChannelMember>('channel-members')\n\n const join = useCallback(() => {\n if (!channelId || !user) return\n const existing = records.find((r) => r.data.userId === user.id)\n if (existing) return\n create({ channelId, userId: user.id, joinedAt: new Date().toISOString() } as unknown as ChannelMember)\n }, [channelId, user, records, create])\n\n const leave = useCallback(() => {\n if (!channelId || !user) return\n const membership = records.find((r) => r.data.userId === user.id)\n if (membership) removeMutation(membership.recordId)\n }, [channelId, user, records, removeMutation])\n\n const isMember = user ? records.some((r) => r.data.userId === user.id) : false\n\n return { members: records as RecordData<ChannelMember>[], status, error, join, leave, isMember }\n}\n","/**\n * useReadReceipts — track read state per channel for the current user.\n */\n\nimport { useCallback } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { ReadReceipt, Message } from './channel-types'\n\nexport function useReadReceipts() {\n const { user } = useUser()\n\n const { records, status, error } = useQuery<ReadReceipt>('read-receipts', {\n where: user ? { userId: user.id } : { userId: '__none__' },\n })\n\n const { create, put } = useMutations<ReadReceipt>('read-receipts')\n\n const markAsRead = useCallback(\n (channelId: string) => {\n if (!user) return\n const existing = records.find((r) => r.data.channelId === channelId)\n const now = new Date().toISOString()\n if (existing) {\n put(existing.recordId, { lastReadAt: now } as unknown as ReadReceipt)\n } else {\n create({ channelId, userId: user.id, lastReadAt: now } as unknown as ReadReceipt)\n }\n },\n [user, records, create, put],\n )\n\n const getUnreadCount = useCallback(\n (channelId: string, messages: RecordData<Message>[]): number => {\n const receipt = records.find((r) => r.data.channelId === channelId)\n if (!receipt) return messages.length\n const lastReadAt = new Date(receipt.data.lastReadAt).getTime()\n return messages.filter((m) => new Date(m.createdAt).getTime() > lastReadAt).length\n },\n [records],\n )\n\n return { receipts: records as RecordData<ReadReceipt>[], status, error, markAsRead, getUnreadCount }\n}\n","/**\n * useConversation — Shared hook for conversation messaging.\n *\n * Wraps useQuery/useMutations for conv_messages, conv_reactions, conv_members\n * and returns a ConversationObject. Must be used inside a RecordScope for the\n * conversation DO (conv:{channelId}).\n */\n\nimport { useCallback, useMemo } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useRecordContext } from '../storage/context'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { ConvMessageData, ConvReactionData, ConvMemberData } from '../../shared/types'\nimport type { MessageRecord, ReactionRecord, MemberRecord, ConversationObject } from './types'\n\ninterface UseConversationOptions {\n onMessageSent?: (content: string, parentMessageId?: string) => void\n}\n\nexport function useConversation(opts?: UseConversationOptions): ConversationObject {\n const { user } = useUser()\n const { ready } = useRecordContext()\n\n const { records: allMessages } = useQuery<ConvMessageData>('conv_messages', { orderBy: '_created_at' })\n const { records: allReactions } = useQuery<ConvReactionData>('conv_reactions')\n const { records: members } = useQuery<ConvMemberData>('conv_members')\n\n const messageMut = useMutations<ConvMessageData>('conv_messages')\n const reactionMut = useMutations<ConvReactionData>('conv_reactions')\n\n const send = useCallback(\n (content: string, parentMessageId?: string, messageType?: string, metadata?: Record<string, unknown>) => {\n messageMut.create({\n Content: content,\n AuthorId: user?.id ?? '',\n ParentId: parentMessageId ?? '',\n Edited: 0,\n MessageType: messageType ?? 'message',\n Metadata: metadata ? JSON.stringify(metadata) : '',\n })\n opts?.onMessageSent?.(content, parentMessageId)\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps -- depend on the precise opts?.onMessageSent callback, not the whole opts object, to avoid recreating send when unrelated opts fields change\n [messageMut, user?.id, opts?.onMessageSent],\n )\n\n const edit = useCallback(\n (recordId: string, content: string) => {\n const existing = allMessages.find((m) => m.recordId === recordId)\n if (!existing) return\n messageMut.put(recordId, { ...existing.data, Content: content, Edited: 1 })\n },\n [messageMut, allMessages],\n )\n\n const remove = useCallback(\n (recordId: string) => {\n const existing = allMessages.find((m) => m.recordId === recordId)\n if (!existing) return\n messageMut.put(recordId, {\n ...existing.data,\n Content: '',\n MessageType: 'deleted',\n Metadata: '',\n })\n },\n [messageMut, allMessages],\n )\n\n const toggleReaction = useCallback(\n (messageId: string, emoji: string) => {\n const existing = allReactions.find(\n (r) => r.data.MessageId === messageId && r.data.Emoji === emoji && r.data.UserId === user?.id,\n )\n if (existing) {\n reactionMut.remove(existing.recordId)\n } else {\n reactionMut.create({ MessageId: messageId, Emoji: emoji, UserId: user?.id ?? '' })\n }\n },\n [reactionMut, allReactions, user?.id],\n )\n\n return useMemo(\n () => ({\n messages: allMessages as MessageRecord[],\n reactions: allReactions as ReactionRecord[],\n members: members as MemberRecord[],\n status: ready ? ('connected' as const) : ('connecting' as const),\n send,\n edit,\n remove,\n toggleReaction,\n }),\n [allMessages, allReactions, members, ready, send, edit, remove, toggleReaction],\n )\n}\n","/**\n * Shared messaging utility functions.\n */\n\nimport type { ReactionRecord, GroupedReaction, MessageRecord } from './types'\n\nexport function groupReactionsForMessage(\n reactions: ReactionRecord[],\n messageId: string,\n currentUserId: string,\n): GroupedReaction[] {\n const forMessage = reactions.filter((r) => r.data.MessageId === messageId)\n const groups = new Map<string, { count: number; currentUserReacted: boolean; userIds: string[] }>()\n\n for (const r of forMessage) {\n const existing = groups.get(r.data.Emoji) ?? { count: 0, currentUserReacted: false, userIds: [] }\n existing.count++\n existing.userIds.push(r.data.UserId)\n if (r.data.UserId === currentUserId) existing.currentUserReacted = true\n groups.set(r.data.Emoji, existing)\n }\n\n return Array.from(groups.entries()).map(([emoji, data]) => ({\n emoji,\n count: data.count,\n currentUserReacted: data.currentUserReacted,\n userIds: data.userIds,\n }))\n}\n\n/** Default grouping threshold: 5 minutes */\nconst GROUPING_THRESHOLD_MS = 5 * 60 * 1000\n\n/**\n * Determine whether a message should be visually grouped with the previous one\n * (same author, within threshold, no date boundary).\n */\nexport function shouldGroupMessages(\n current: MessageRecord,\n previous: MessageRecord | null | undefined,\n options?: { thresholdMs?: number; dateChanged?: boolean },\n): boolean {\n if (!previous) return false\n if (options?.dateChanged) return false\n if (previous.data.AuthorId !== current.data.AuthorId) return false\n const timeDiff = new Date(current.createdAt).getTime() - new Date(previous.createdAt).getTime()\n return timeDiff < (options?.thresholdMs ?? GROUPING_THRESHOLD_MS)\n}\n\n/**\n * Compute thread reply counts from a flat message array.\n */\nexport function getThreadCounts(messages: MessageRecord[]): Map<string, number> {\n const counts = new Map<string, number>()\n for (const m of messages) {\n if (m.data.ParentId) {\n counts.set(m.data.ParentId, (counts.get(m.data.ParentId) ?? 0) + 1)\n }\n }\n return counts\n}\n\n/**\n * Format a message timestamp for display.\n * Returns \"2:30 PM\" style short time.\n */\nexport function formatMessageTime(dateStr: string): string {\n return new Date(dateStr).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n}\n\n/**\n * Format a full message timestamp.\n * Returns \"Mar 5, 2026 at 2:30 PM\" style.\n */\nexport function formatFullTimestamp(dateStr: string): string {\n const date = new Date(dateStr)\n const datePart = date.toLocaleDateString([], { month: 'short', day: 'numeric', year: 'numeric' })\n const timePart = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n return `${datePart} at ${timePart}`\n}\n","/**\n * Conversation Display Utilities\n *\n * Shared helpers for rendering conversation names, participant lists,\n * and type checks across apps (slack-clone, discord-clone, etc.).\n */\n\nimport type { UserInfo } from '../storage/hooks/useUserLookup'\n\ninterface ConversationData {\n Name: string\n Type: string\n ParticipantIds: string\n ParticipantHash: string\n}\n\n/**\n * Get a display name for a conversation.\n *\n * - Channels: use Name field\n * - DMs: look up the other participant's name\n * - Group DMs: \"User A, User B, +N\"\n */\nexport function getConversationDisplayName(\n conversation: { data: ConversationData },\n currentUserId: string,\n getUser: (id: string) => UserInfo | undefined,\n): string {\n const { Type, Name } = conversation.data\n\n if (Type !== 'dm' && Type !== 'group') {\n return Name || 'Untitled'\n }\n\n const ids = getConversationParticipantIds(conversation, currentUserId)\n\n if (ids.length === 0) return 'Unknown'\n\n if (Type === 'dm') {\n const other = getUser(ids[0])\n return other?.name ?? 'Unknown'\n }\n\n // Group DM\n const MAX_NAMES = 3\n const names: string[] = []\n for (let i = 0; i < Math.min(ids.length, MAX_NAMES); i++) {\n const u = getUser(ids[i])\n names.push(u?.name?.split(' ')[0] ?? 'Unknown')\n }\n const remaining = ids.length - MAX_NAMES\n if (remaining > 0) {\n return `${names.join(', ')}, +${remaining}`\n }\n return names.join(', ')\n}\n\n/**\n * Check if a conversation type is a DM or group DM.\n */\nexport function isDMConversation(type: string): boolean {\n return type === 'dm' || type === 'group'\n}\n\n/**\n * Parse participant IDs from a conversation.\n * Tries ParticipantIds JSON first, falls back to ParticipantHash (colon-separated).\n */\nexport function getConversationParticipantIds(\n conversation: { data: Pick<ConversationData, 'ParticipantIds' | 'ParticipantHash'> },\n excludeUserId?: string,\n): string[] {\n let ids: string[] = []\n\n // Try ParticipantIds JSON first\n try {\n const parsed = JSON.parse(conversation.data.ParticipantIds)\n if (Array.isArray(parsed) && parsed.length > 0) {\n ids = parsed\n }\n } catch {\n // fall through to ParticipantHash\n }\n\n // Fallback to ParticipantHash (colon-separated user IDs)\n if (ids.length === 0 && conversation.data.ParticipantHash) {\n ids = conversation.data.ParticipantHash.split(':')\n }\n\n if (excludeUserId) {\n return ids.filter((id) => id !== excludeUserId)\n }\n return ids\n}\n","/**\n * Safely parse a Metadata field that may be a JSON string, an object, or null/undefined.\n * Returns the parsed object or null on failure.\n */\nexport function parseMessageMetadata(raw: unknown): Record<string, unknown> | null {\n if (!raw) return null\n if (typeof raw === 'object') return raw as Record<string, unknown>\n if (typeof raw === 'string') {\n try { return JSON.parse(raw) } catch { return null }\n }\n return null\n}\n","/**\n * useConversations Hook\n *\n * Standardized conversation directory backed by the `conversations` and\n * `conversation_state` collections in the dir:{appName} DO.\n *\n * Apps that need a sidebar list of channels / DMs / threads should:\n * 1. Add { type: 'dir', instanceId: APP_ID } to SHARED_CONNECTIONS.\n * 2. Call useConversations() inside a RecordProvider.\n */\n\nimport { useCallback, useMemo, useRef } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { DirectoryConversationData, ConversationStateData } from '../../shared/types'\n\n// Module-scoped constant so it's a stable reference across renders (safe to\n// list in useCallback deps without recreating the callback every render).\nconst defaultConvFields = {\n Visibility: 'public',\n Status: 'active',\n AssigneeId: '',\n LinkedRef: '',\n MessageCount: 0,\n}\n\nexport function useConversations() {\n const { records: conversations, status } = useQuery<DirectoryConversationData>('conversations')\n const { create, put } = useMutations<DirectoryConversationData>('conversations')\n const { records: stateRecords } = useQuery<ConversationStateData>('conversation_state')\n const stateMut = useMutations<ConversationStateData>('conversation_state')\n const { user } = useUser()\n\n const ready = status === 'ready'\n\n const recentCreationsRef = useRef(new Map<string, DirectoryConversationData>())\n for (const [id] of recentCreationsRef.current) {\n if (conversations.find((c) => c.recordId === id)) {\n recentCreationsRef.current.delete(id)\n }\n }\n\n const readStateMap = useMemo(\n () => new Map(stateRecords.map((r) => [r.data.ConversationId, r.data.LastReadAt])),\n [stateRecords],\n )\n\n const readMessageCountMap = useMemo(\n () => new Map(stateRecords.map((r) => [r.data.ConversationId, r.data.LastReadMessageCount ?? 0])),\n [stateRecords],\n )\n\n const starredSet = useMemo(\n () => new Set(stateRecords.filter((r) => r.data.Starred).map((r) => r.data.ConversationId)),\n [stateRecords],\n )\n\n const archivedSet = useMemo(\n () => new Set(stateRecords.filter((r) => r.data.Archived).map((r) => r.data.ConversationId)),\n [stateRecords],\n )\n\n\n const getConversationState = useCallback(\n (conversationId: string): ConversationStateData | undefined => {\n return stateRecords.find((r) => r.data.ConversationId === conversationId)?.data\n },\n [stateRecords],\n )\n\n const createChannel = useCallback(\n async (name: string, description?: string) => {\n const existing = conversations.find((r) => r.data.Name === name && r.data.Type !== 'dm')\n if (existing) return existing.recordId\n\n const recordId = await create({\n Name: name,\n Description: description ?? '',\n Type: 'public',\n CreatedBy: user?.id ?? '',\n ParticipantHash: '',\n ParticipantIds: '',\n LastMessageAt: '',\n LastMessagePreview: '',\n LastMessageAuthor: '',\n ...defaultConvFields,\n })\n return recordId\n },\n [create, user?.id, conversations],\n )\n\n const createDM = useCallback(\n async (otherUserId: string) => {\n if (!user?.id) return null\n const hash = [user.id, otherUserId].sort().join(':')\n\n const existing = conversations.find((r) => r.data.ParticipantHash === hash)\n if (existing) return existing.recordId\n\n const data: DirectoryConversationData = {\n Name: '',\n Description: '',\n Type: 'dm',\n Visibility: 'private',\n CreatedBy: user.id,\n ParticipantHash: hash,\n ParticipantIds: JSON.stringify([user.id, otherUserId]),\n Status: 'active',\n AssigneeId: '',\n LinkedRef: '',\n LastMessageAt: '',\n LastMessagePreview: '',\n LastMessageAuthor: '',\n MessageCount: 0,\n }\n const recordId = await create(data)\n recentCreationsRef.current.set(recordId, data)\n return recordId\n },\n [create, user?.id, conversations],\n )\n\n const createGroupDM = useCallback(\n async (participantIds: string[]) => {\n if (!user?.id) return null\n // Add current user + deduplicate\n const allIds = [...new Set([user.id, ...participantIds])]\n if (allIds.length < 3) return null // Use createDM for 2 participants\n\n const hash = allIds.sort().join(':')\n\n const existing = conversations.find((r) => r.data.ParticipantHash === hash)\n if (existing) return existing.recordId\n\n const data: DirectoryConversationData = {\n Name: '',\n Description: '',\n Type: 'group',\n Visibility: 'private',\n CreatedBy: user.id,\n ParticipantHash: hash,\n ParticipantIds: JSON.stringify(allIds),\n Status: 'active',\n AssigneeId: '',\n LinkedRef: '',\n LastMessageAt: '',\n LastMessagePreview: '',\n LastMessageAuthor: '',\n MessageCount: 0,\n }\n const recordId = await create(data)\n recentCreationsRef.current.set(recordId, data)\n return recordId\n },\n [create, user?.id, conversations],\n )\n\n const lookupByName = useCallback(\n (name: string) => {\n const found = conversations.find((r) => r.data.Name === name)\n return found?.recordId ?? null\n },\n [conversations],\n )\n\n const updateLastMessage = useCallback(\n (conversationId: string, preview: string, authorId?: string) => {\n const conv = conversations.find((c) => c.recordId === conversationId)\n const baseData = conv?.data ?? recentCreationsRef.current.get(conversationId)\n if (!baseData) return\n const updatedData = {\n ...baseData,\n LastMessageAt: new Date().toISOString(),\n LastMessagePreview: preview.slice(0, 100),\n LastMessageAuthor: authorId ?? baseData.LastMessageAuthor,\n MessageCount: (baseData.MessageCount ?? 0) + 1,\n }\n put(conversationId, updatedData)\n if (recentCreationsRef.current.has(conversationId)) {\n recentCreationsRef.current.set(conversationId, updatedData)\n }\n },\n [put, conversations],\n )\n\n /** Upsert the user's conversation_state row for a conversation. */\n const upsertState = useCallback(\n (conversationId: string, partial: Partial<ConversationStateData>) => {\n if (!user?.id) return\n const existing = stateRecords.find(\n (r) => r.data.ConversationId === conversationId && r.data.UserId === user.id,\n )\n const base: ConversationStateData = existing?.data ?? {\n ConversationId: conversationId,\n UserId: user.id,\n LastReadAt: '',\n LastReadMessageCount: 0,\n Starred: 0,\n Archived: 0,\n Trashed: 0,\n Labels: '',\n Folder: '',\n }\n const recordId = existing?.recordId ?? `state-${user.id}-${conversationId}`\n stateMut.put(recordId, { ...base, ...partial })\n },\n [stateMut, stateRecords, user?.id],\n )\n\n const markRead = useCallback(\n (conversationId: string) => {\n const conv = conversations.find((c) => c.recordId === conversationId)\n const msgCount = conv?.data.MessageCount ?? recentCreationsRef.current.get(conversationId)?.MessageCount ?? 0\n upsertState(conversationId, {\n LastReadAt: new Date().toISOString(),\n LastReadMessageCount: msgCount,\n })\n },\n [upsertState, conversations],\n )\n\n const toggleStar = useCallback(\n (conversationId: string) => {\n const isStarred = starredSet.has(conversationId)\n upsertState(conversationId, { Starred: isStarred ? 0 : 1 })\n },\n [upsertState, starredSet],\n )\n\n const setArchived = useCallback(\n (conversationId: string, archived: boolean) => {\n upsertState(conversationId, { Archived: archived ? 1 : 0 })\n },\n [upsertState],\n )\n\n const setTrashed = useCallback(\n (conversationId: string, trashed: boolean) => {\n upsertState(conversationId, { Trashed: trashed ? 1 : 0 })\n },\n [upsertState],\n )\n\n const setLabels = useCallback(\n (conversationId: string, labels: string[]) => {\n upsertState(conversationId, { Labels: JSON.stringify(labels) })\n },\n [upsertState],\n )\n\n const setFolder = useCallback(\n (conversationId: string, folder: string) => {\n upsertState(conversationId, { Folder: folder })\n },\n [upsertState],\n )\n\n return {\n conversations: conversations as RecordData<DirectoryConversationData>[],\n ready,\n createChannel,\n createDM,\n createGroupDM,\n lookupByName,\n updateLastMessage,\n readStateMap,\n readMessageCountMap,\n starredSet,\n archivedSet,\n getConversationState,\n upsertState,\n markRead,\n toggleStar,\n setArchived,\n setTrashed,\n setLabels,\n setFolder,\n }\n}\n","/**\n * useCommunities Hook\n *\n * CRUD operations for the `communities` and `memberships` collections\n * in the dir:{appName} DO.\n *\n * Apps that need forums, groups, or boards should:\n * 1. Add { type: 'dir', instanceId: APP_ID } to SHARED_CONNECTIONS.\n * 2. Call useCommunities() inside a RecordProvider.\n */\n\nimport { useCallback, useMemo } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { DirectoryCommunityData, DirectoryMembershipData } from '../../shared/types'\n\nexport function useCommunities() {\n const { records: communities, status } = useQuery<DirectoryCommunityData>('communities')\n const communityMut = useMutations<DirectoryCommunityData>('communities')\n const { records: memberships } = useQuery<DirectoryMembershipData>('memberships')\n const membershipMut = useMutations<DirectoryMembershipData>('memberships')\n const { user } = useUser()\n\n const ready = status === 'ready'\n\n const membershipMap = useMemo(() => {\n const map = new Map<string, DirectoryMembershipData[]>()\n for (const m of memberships) {\n const list = map.get(m.data.CommunityId) ?? []\n list.push(m.data)\n map.set(m.data.CommunityId, list)\n }\n return map\n }, [memberships])\n\n const myMemberships = useMemo(\n () => new Set(\n memberships\n .filter((m) => m.data.UserId === user?.id)\n .map((m) => m.data.CommunityId),\n ),\n [memberships, user?.id],\n )\n\n const createCommunity = useCallback(\n async (name: string, opts?: { description?: string; type?: string; visibility?: string; rules?: string }) => {\n const existing = communities.find((c) => c.data.Name === name)\n if (existing) return existing.recordId\n\n const recordId = await communityMut.create({\n Name: name,\n Description: opts?.description ?? '',\n CreatedBy: user?.id ?? '',\n Type: opts?.type ?? 'community',\n Visibility: opts?.visibility ?? 'public',\n MemberCount: 0,\n Rules: opts?.rules ?? '',\n IconUrl: '',\n CoverUrl: '',\n })\n return recordId\n },\n [communityMut, user?.id, communities],\n )\n\n const updateCommunity = useCallback(\n (communityId: string, updates: Partial<DirectoryCommunityData>) => {\n const existing = communities.find((c) => c.recordId === communityId)\n if (!existing) return\n communityMut.put(communityId, { ...existing.data, ...updates })\n },\n [communityMut, communities],\n )\n\n const joinCommunity = useCallback(\n async (communityId: string, userName: string) => {\n if (!user?.id) return null\n const existing = memberships.find(\n (m) => m.data.CommunityId === communityId && m.data.UserId === user.id,\n )\n if (existing) return existing.recordId\n\n const recordId = await membershipMut.create({\n CommunityId: communityId,\n UserId: user.id,\n UserName: userName,\n Role: 'member',\n JoinedAt: new Date().toISOString(),\n })\n return recordId\n },\n [membershipMut, memberships, user?.id],\n )\n\n const leaveCommunity = useCallback(\n (communityId: string) => {\n if (!user?.id) return\n const membership = memberships.find(\n (m) => m.data.CommunityId === communityId && m.data.UserId === user.id,\n )\n if (membership) {\n membershipMut.remove(membership.recordId)\n }\n },\n [membershipMut, memberships, user?.id],\n )\n\n const getMembersOf = useCallback(\n (communityId: string): DirectoryMembershipData[] => {\n return membershipMap.get(communityId) ?? []\n },\n [membershipMap],\n )\n\n const lookupByName = useCallback(\n (name: string) => {\n const found = communities.find((c) => c.data.Name === name)\n return found?.recordId ?? null\n },\n [communities],\n )\n\n return {\n communities: communities as RecordData<DirectoryCommunityData>[],\n memberships: memberships as RecordData<DirectoryMembershipData>[],\n ready,\n myMemberships,\n createCommunity,\n updateCommunity,\n joinCommunity,\n leaveCommunity,\n getMembersOf,\n lookupByName,\n }\n}\n","/**\n * usePosts Hook\n *\n * CRUD operations for the `posts` collection in the dir:{appName} DO.\n *\n * Apps that need feeds, Q&A, or tweet-style content should:\n * 1. Add { type: 'dir', instanceId: APP_ID } to SHARED_CONNECTIONS.\n * 2. Call usePosts() inside a RecordProvider.\n */\n\nimport { useCallback } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { DirectoryPostData } from '../../shared/types'\n\nexport function usePosts(opts?: { communityId?: string }) {\n const { records: allPosts, status } = useQuery<DirectoryPostData>('posts')\n const postMut = useMutations<DirectoryPostData>('posts')\n const { user } = useUser()\n\n const ready = status === 'ready'\n\n const posts = opts?.communityId\n ? allPosts.filter((p) => p.data.CommunityId === opts.communityId)\n : allPosts\n\n const createPost = useCallback(\n async (data: {\n title: string\n content: string\n type?: string\n communityId?: string\n parentId?: string\n tags?: string[]\n linkUrl?: string\n }) => {\n const recordId = await postMut.create({\n Title: data.title,\n Content: data.content,\n AuthorId: user?.id ?? '',\n Type: data.type ?? 'post',\n CommunityId: data.communityId ?? '',\n ParentId: data.parentId ?? '',\n ConversationId: '',\n Status: 'published',\n Tags: data.tags ? JSON.stringify(data.tags) : '',\n LinkUrl: data.linkUrl ?? '',\n })\n return recordId\n },\n [postMut, user?.id],\n )\n\n const updatePost = useCallback(\n (postId: string, updates: Partial<DirectoryPostData>) => {\n const existing = allPosts.find((p) => p.recordId === postId)\n if (!existing) return\n postMut.put(postId, { ...existing.data, ...updates })\n },\n [postMut, allPosts],\n )\n\n const deletePost = useCallback(\n (postId: string) => {\n postMut.remove(postId)\n },\n [postMut],\n )\n\n const setConversationId = useCallback(\n (postId: string, conversationId: string) => {\n updatePost(postId, { ConversationId: conversationId })\n },\n [updatePost],\n )\n\n return {\n posts: posts as RecordData<DirectoryPostData>[],\n ready,\n createPost,\n updatePost,\n deletePost,\n setConversationId,\n }\n}\n","'use client';\n\nimport React, { useEffect, type ReactNode } from 'react';\nimport type { DeepSpaceThemeConfig } from './types';\nimport { applyDeepSpaceTheme, readThemeFromDOM } from './applyTheme';\n\nexport interface DeepSpaceThemeProviderProps {\n children: ReactNode;\n /**\n * Explicit theme config. If omitted, auto-detects from the widget's\n * --color-* CSS custom properties (defined in the @theme block of styles.css).\n */\n theme?: DeepSpaceThemeConfig;\n}\n\n/**\n * DeepSpaceThemeProvider — sets --theme-* CSS variables for pill,\n * and other DeepSpace UI components on deployed sites and mini-apps.\n *\n * **Zero-config**: reads colors from the widget's @theme CSS block.\n * **Explicit**: pass a `theme` prop to override.\n *\n * @example\n * ```tsx\n * // Zero-config — reads from styles.css @theme block\n * <DeepSpaceThemeProvider>\n * <App />\n * </DeepSpaceThemeProvider>\n *\n * // Explicit theme\n * <DeepSpaceThemeProvider theme={{\n * primaryColor: '#0f172a',\n * secondaryColor: '#1e293b',\n * accentColor: '#8b5cf6',\n * textColor: '#f1f5f9',\n * }}>\n * <App />\n * </DeepSpaceThemeProvider>\n * ```\n */\nexport function DeepSpaceThemeProvider({\n children,\n theme,\n}: DeepSpaceThemeProviderProps): React.ReactElement {\n useEffect(() => {\n const root = document.documentElement;\n const config = theme ?? readThemeFromDOM(root);\n applyDeepSpaceTheme(config, root);\n }, [theme]);\n\n return <>{children}</>;\n}\n","import type { DeepSpaceThemeConfig } from './types';\n\n/**\n * CSS color-mix helper — produces `color-mix(in srgb, color pct%, base)`.\n * Works with any valid CSS color format (hex, rgb, rgba, hsl, named).\n */\nfunction mix(color: string, pct: number, base = 'transparent'): string {\n return `color-mix(in srgb, ${color} ${Math.round(pct * 100)}%, ${base})`;\n}\n\n/**\n * Determine whether a CSS color is \"dark\" using relative luminance.\n */\nexport function isDarkColor(color: string): boolean {\n const c = color.trim();\n\n // getComputedStyle normalises colors to rgb()/rgba() — handle that first.\n const rgbMatch = c.match(/^rgba?\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.]+)/);\n if (rgbMatch) {\n const r = parseFloat(rgbMatch[1]) / 255;\n const g = parseFloat(rgbMatch[2]) / 255;\n const b = parseFloat(rgbMatch[3]) / 255;\n return 0.2126 * r + 0.7152 * g + 0.0722 * b < 0.5;\n }\n\n // #hex (#rgb / #rrggbb)\n if (!c.startsWith('#')) return true; // unrecognised format — assume dark\n let hex = c.slice(1);\n if (hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];\n if (hex.length !== 6) return true;\n const r = parseInt(hex.slice(0, 2), 16) / 255;\n const g = parseInt(hex.slice(2, 4), 16) / 255;\n const b = parseInt(hex.slice(4, 6), 16) / 255;\n return 0.2126 * r + 0.7152 * g + 0.0722 * b < 0.5;\n}\n\n/**\n * Static UI tokens for light themes.\n *\n * KEEP IN SYNC with apps/web/app/styles/tokens.css (:root block)\n *\n * Two delivery paths use these same values:\n * - Web app (SSR): defined in tokens.css (instant, no flash)\n * - Deployed sites: applyDeepSpaceTheme() sets them via JS from these constants\n */\nconst UI_TOKENS_LIGHT: Record<string, string> = {\n '--ui-overlay': 'rgba(0, 0, 0, 0.5)',\n '--ui-bg-primary': '#ffffff',\n '--ui-bg-secondary': '#f9fafb',\n '--ui-bg-tertiary': '#f3f4f6',\n '--ui-bg-elevated': '#ffffff',\n '--ui-border': '#e5e7eb',\n '--ui-border-strong': '#d1d5db',\n '--ui-text-primary': '#111827',\n '--ui-text-secondary': '#6b7280',\n '--ui-text-muted': '#9ca3af',\n '--ui-text-disabled': '#d1d5db',\n '--ui-hover-bg': '#f3f4f6',\n '--ui-success': '#10b981',\n '--ui-warning': '#f59e0b',\n '--ui-error': '#ef4444',\n '--ui-shadow-sm': '0 1px 3px rgba(0, 0, 0, 0.1)',\n '--ui-shadow-md': '0 4px 12px rgba(0, 0, 0, 0.1)',\n '--ui-shadow-lg': '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',\n '--ui-vote-up': '#f97316',\n '--ui-vote-down': '#3b82f6',\n};\n\n/** Static UI tokens for dark themes — KEEP IN SYNC with [data-ui-theme=\"dark\"] in tokens.css */\nconst UI_TOKENS_DARK: Record<string, string> = {\n '--ui-overlay': 'rgba(0, 0, 0, 0.7)',\n '--ui-bg-primary': '#1a1a1b',\n '--ui-bg-secondary': '#1e1f20',\n '--ui-bg-tertiary': '#2d2e30',\n '--ui-bg-elevated': '#1e1f20',\n '--ui-border': '#3c4043',\n '--ui-border-strong': '#5a5d61',\n '--ui-text-primary': '#e8eaed',\n '--ui-text-secondary': '#bdc1c6',\n '--ui-text-muted': '#9aa0a6',\n '--ui-text-disabled': '#5a5d61',\n '--ui-hover-bg': '#3c4043',\n '--ui-success': '#34d399',\n '--ui-warning': '#fbbf24',\n '--ui-error': '#f87171',\n '--ui-shadow-sm': '0 1px 3px rgba(0, 0, 0, 0.25)',\n '--ui-shadow-md': '0 4px 12px rgba(0, 0, 0, 0.3)',\n '--ui-shadow-lg': '0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.3)',\n '--ui-vote-up': '#f97316',\n '--ui-vote-down': '#60a5fa',\n};\n\n/** All --ui-* property names for cleanup */\nconst UI_TOKEN_PROPERTIES = [\n '--ui-overlay', '--ui-bg-primary', '--ui-bg-secondary', '--ui-bg-tertiary', '--ui-bg-elevated',\n '--ui-border', '--ui-border-strong',\n '--ui-text-primary', '--ui-text-secondary', '--ui-text-muted', '--ui-text-disabled',\n '--ui-accent', '--ui-accent-hover', '--ui-accent-secondary',\n '--ui-hover-bg', '--ui-active-bg', '--ui-active-text',\n '--ui-success', '--ui-warning', '--ui-error',\n '--ui-shadow-sm', '--ui-shadow-md', '--ui-shadow-lg',\n '--ui-vote-up', '--ui-vote-down',\n] as const;\n\n/**\n * All CSS custom properties set by applyDeepSpaceTheme.\n * Used by clearDeepSpaceTheme to remove stale values on theme switch.\n */\nexport const DEEPSPACE_THEME_PROPERTIES = [\n // Panel & background\n '--theme-panel-bg', '--theme-panel-bg-opaque', '--theme-panel-bg-secondary',\n '--theme-panel-solid', '--theme-top-panel-bg', '--theme-panel-border',\n '--theme-panel-highlight', '--theme-background',\n // Buttons\n '--theme-button-bg', '--theme-button-border', '--theme-button-hover',\n '--theme-button-border-hover', '--theme-button-active', '--theme-button-fg',\n // Text\n '--theme-text', '--theme-text-hover', '--theme-text-secondary', '--theme-text-tertiary',\n // Accent & colors\n '--theme-accent', '--theme-accent-contrast', '--theme-accent-hover',\n '--theme-secondary', '--theme-secondary-hover', '--theme-primary-hover',\n // Shadows\n '--theme-shadow-primary', '--theme-shadow-secondary', '--theme-shadow-accent',\n '--theme-component-shadow',\n // Surfaces\n '--theme-surface', '--theme-surface-solid',\n '--theme-library-card-bg', '--theme-library-surface', '--theme-library-panel-glass',\n // Borders\n '--theme-border',\n // Status\n '--theme-destructive', '--theme-destructive-hover',\n '--theme-success', '--theme-warning', '--theme-warning-bg', '--theme-warning-fg',\n // Glass\n '--theme-highlight', '--theme-font',\n '--beveled-gradient', '--beveled-glass-shadow',\n // UI component tokens\n ...UI_TOKEN_PROPERTIES,\n] as const;\n\n/**\n * Remove all CSS custom properties previously set by applyDeepSpaceTheme.\n * Call before re-applying a new theme to prevent stale variable bleed.\n */\nexport function clearDeepSpaceTheme(\n root: HTMLElement = document.documentElement,\n): void {\n for (const prop of DEEPSPACE_THEME_PROPERTIES) {\n root.style.removeProperty(prop);\n }\n}\n\n/**\n * Apply the correct set of --ui-* inline styles for the given UI theme.\n *\n * Single source of truth for --ui-* token application. Called by:\n * - `applyDeepSpaceTheme()` (initial theme setup, canvas theme changes)\n * - `useUITheme` toggle (user switches dark <-> light)\n *\n * When `accent` overrides are provided they are used directly (the\n * `applyDeepSpaceTheme` path). Otherwise the current --theme-accent /\n * --theme-secondary values are read from computed styles.\n */\nexport function applyUIThemeTokens(\n theme: 'dark' | 'light',\n root: HTMLElement = document.documentElement,\n accent?: { color: string; hover: string; secondary: string },\n): void {\n const dark = theme === 'dark';\n\n // Base tokens (surfaces, text, borders, etc.)\n const base = dark ? UI_TOKENS_DARK : UI_TOKENS_LIGHT;\n for (const [prop, val] of Object.entries(base)) {\n root.style.setProperty(prop, val);\n }\n\n // Accent-derived tokens\n const accentColor = accent?.color\n ?? (getComputedStyle(root).getPropertyValue('--theme-accent').trim() || (dark ? '#818cf8' : '#3b82f6'));\n const accentHover = accent?.hover\n ?? `color-mix(in srgb, ${accentColor} 90%, black)`;\n const secondary = accent?.secondary\n ?? (getComputedStyle(root).getPropertyValue('--theme-secondary').trim() || accentColor);\n\n root.style.setProperty('--ui-accent', accentColor);\n root.style.setProperty('--ui-accent-hover', accentHover);\n root.style.setProperty('--ui-accent-secondary', secondary);\n root.style.setProperty('--ui-active-bg', `color-mix(in srgb, ${accentColor} ${dark ? 15 : 10}%, transparent)`);\n root.style.setProperty('--ui-active-text', dark ? secondary : accentColor);\n}\n\n/**\n * Apply a DeepSpace theme by setting CSS custom properties on the given root element.\n *\n * Maps a simplified theme config to the full set of --theme-* CSS variables\n * consumed by DeepSpace UI components, pills, and panels.\n *\n * Uses CSS color-mix() for derived colors so any valid CSS color format works\n * (hex, rgb, rgba, hsl, etc.).\n */\nexport function applyDeepSpaceTheme(\n config: DeepSpaceThemeConfig,\n root: HTMLElement = document.documentElement,\n): void {\n const {\n primaryColor,\n panelColor = primaryColor,\n secondaryColor,\n accentColor,\n accentContrastColor = '#ffffff',\n textColor,\n shadowColor = '#000000',\n borderColor = secondaryColor,\n backgroundColor = primaryColor,\n highlightColor = '#ffffff',\n glassmorphism = true,\n } = config;\n\n // ---------- derived colors ----------\n\n // Panel backgrounds\n const panelBg = glassmorphism ? mix(panelColor, 0.4) : panelColor;\n const panelBgOpaque = glassmorphism ? mix(panelColor, 0.85) : panelColor;\n const panelSolid = glassmorphism ? mix(panelColor, 0.6, 'white') : panelColor;\n const panelBgSecondary = glassmorphism ? mix(secondaryColor, 0.5) : secondaryColor;\n const topPanelBg = glassmorphism ? mix(panelColor, 0.6) : panelColor;\n\n // Borders\n const panelBorder = glassmorphism ? mix(borderColor, 0.3) : borderColor;\n\n // Buttons\n const buttonBg = glassmorphism ? mix(secondaryColor, 0.4) : secondaryColor;\n const buttonBorder = glassmorphism ? mix(borderColor, 0.3) : borderColor;\n const buttonBorderHover = glassmorphism ? mix(borderColor, 0.7) : borderColor;\n const buttonHover = mix(accentColor, 0.2);\n const buttonActive = mix(accentColor, 0.3);\n\n // Text\n const textSecondary = mix(textColor, 0.8);\n const textTertiary = mix(textColor, 0.6);\n\n // Accents\n const accentHover = mix(accentColor, 0.9, 'black');\n const secondary = mix(secondaryColor, 0.9);\n const secondaryHover = mix(secondaryColor, 0.9, 'black');\n const primaryHover = mix(primaryColor, 0.9, 'black');\n\n // Shadows\n const shadowPrimary = mix(shadowColor, 0.1);\n const shadowSecondary = mix(shadowColor, 0.2);\n const shadowAccent = mix(shadowColor, 0.01);\n const componentShadow =\n `0 1px 3px ${mix(shadowColor, 0.12)}, 0 1px 2px ${mix(shadowColor, 0.24)}`;\n\n // Surfaces & highlights\n const surface = mix(primaryColor, 0.95);\n const panelHighlight = glassmorphism ? mix(textColor, 0.05) : 'transparent';\n\n // Glass gradients\n const beveledGradient = `linear-gradient(180deg,color-mix(in srgb, ${highlightColor} 40%, transparent) 0%,color-mix(in srgb, ${highlightColor} 15%, transparent) 15%,color-mix(in srgb, ${highlightColor} 5%, transparent) 50%,color-mix(in srgb, ${highlightColor} 8%, transparent) 85%,color-mix(in srgb, ${highlightColor} 25%, transparent) 100%)`;\n\n // Glass shadows\n const beveledGlassShadow = [\n `inset 0 1px 1px color-mix(in srgb, ${highlightColor} 80%, transparent)`,\n `inset 0 2px 4px color-mix(in srgb, ${highlightColor} 40%, transparent)`,\n `inset 0 -1px 2px rgba(0, 0, 0, 0.08)`,\n `inset 0 -0.5px 0.5px rgba(0, 0, 0, 0.12)`,\n `0 4px 2px -2px rgba(0, 0, 0, 0.12)`,\n `0 8px 16px -4px rgba(0, 0, 0, 0.1)`,\n `0 0 0 0.5px color-mix(in srgb, ${highlightColor} 20%, transparent) inset`,\n ].join(', ');\n\n // ---------- dark/light detection for UI tokens ----------\n // Respect the user's explicit UI theme preference (set by useUITheme via\n // localStorage) if one exists. Only auto-detect from background luminance\n // when no preference has been set (e.g. deployed sites / mini-apps).\n const preferredUITheme = root.dataset.uiTheme;\n const dark = (preferredUITheme === 'dark' || preferredUITheme === 'light')\n ? preferredUITheme === 'dark'\n : isDarkColor(backgroundColor);\n\n // ---------- data attributes ----------\n root.dataset.glassmorphism = glassmorphism ? 'enabled' : 'disabled';\n if (!preferredUITheme) {\n root.dataset.uiTheme = dark ? 'dark' : 'light';\n }\n\n // ---------- batch set CSS properties ----------\n const updates: [string, string][] = [\n // Panel & background\n ['--theme-panel-bg', panelBg],\n ['--theme-panel-bg-opaque', panelBgOpaque],\n ['--theme-panel-bg-secondary', panelBgSecondary],\n ['--theme-panel-solid', panelSolid],\n ['--theme-top-panel-bg', topPanelBg],\n ['--theme-panel-border', panelBorder],\n ['--theme-panel-highlight', panelHighlight],\n ['--theme-background', backgroundColor],\n\n // Buttons\n ['--theme-button-bg', buttonBg],\n ['--theme-button-border', buttonBorder],\n ['--theme-button-hover', buttonHover],\n ['--theme-button-border-hover', buttonBorderHover],\n ['--theme-button-active', buttonActive],\n ['--theme-button-fg', textColor],\n\n // Text\n ['--theme-text', textColor],\n ['--theme-text-hover', textColor],\n ['--theme-text-secondary', textSecondary],\n ['--theme-text-tertiary', textTertiary],\n\n // Accent & colors\n ['--theme-accent', accentColor],\n ['--theme-accent-contrast', accentContrastColor],\n ['--theme-accent-hover', accentHover],\n ['--theme-secondary', secondary],\n ['--theme-secondary-hover', secondaryHover],\n ['--theme-primary-hover', primaryHover],\n\n // Shadows\n ['--theme-shadow-primary', shadowPrimary],\n ['--theme-shadow-secondary', shadowSecondary],\n ['--theme-shadow-accent', shadowAccent],\n ['--theme-component-shadow', componentShadow],\n\n // Surfaces\n ['--theme-surface', surface],\n ['--theme-surface-solid', primaryColor],\n ['--theme-library-card-bg', 'rgba(24, 24, 27, 0.95)'],\n ['--theme-library-surface', 'rgba(9, 9, 11, 0.92)'],\n ['--theme-library-panel-glass', 'rgba(24, 24, 27, 0.85)'],\n\n // Borders\n ['--theme-border', mix(borderColor, 0.8)],\n\n // Status\n ['--theme-destructive', '#FF3B30'],\n ['--theme-destructive-hover', 'rgba(255, 59, 48, 0.1)'],\n ['--theme-success', '#34C759'],\n ['--theme-warning', '#FF9500'],\n ['--theme-warning-bg', 'rgba(245, 158, 11, 0.15)'],\n ['--theme-warning-fg', '#f59e0b'],\n\n // Glass highlight\n ['--theme-highlight', highlightColor],\n\n // Font\n ['--theme-font', \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\"],\n\n // Glass gradients & shadows\n ['--beveled-gradient', beveledGradient],\n ['--beveled-glass-shadow', beveledGlassShadow],\n ];\n\n for (const [property, value] of updates) {\n root.style.setProperty(property, value);\n }\n\n // ---------- UI component tokens (modals, profile, library, etc.) ----------\n applyUIThemeTokens(dark ? 'dark' : 'light', root, {\n color: accentColor,\n hover: accentHover,\n secondary,\n });\n}\n\n/**\n * Read a DeepSpace theme config from CSS custom properties on the DOM.\n *\n * Reads the widget template's --color-* variables (from the @theme block)\n * and maps them to a DeepSpaceThemeConfig.\n *\n * Falls back to sensible dark-theme defaults if variables are missing.\n */\nexport function readThemeFromDOM(\n root: HTMLElement = document.documentElement,\n): DeepSpaceThemeConfig {\n const styles = getComputedStyle(root);\n const get = (name: string, fallback: string): string =>\n styles.getPropertyValue(name).trim() || fallback;\n\n return {\n primaryColor: get('--color-surface-elevated', '#0f172a'),\n secondaryColor: get('--color-surface-overlay', '#1e293b'),\n accentColor: get('--color-primary', '#818cf8'),\n textColor: get('--color-content', '#f1f5f9'),\n borderColor: get('--color-border', 'rgba(51, 65, 85, 0.5)'),\n backgroundColor: get('--color-surface', '#0a0f1a'),\n highlightColor: '#ffffff',\n shadowColor: '#000000',\n accentContrastColor: '#ffffff',\n };\n}\n","'use client';\n\nimport { useState, useEffect } from 'react';\n\n/**\n * Reads `data-ui-theme` from the root element (set by `applyDeepSpaceTheme`)\n * and re-evaluates when a `canvas-theme-changed` event fires.\n */\nexport function useIsDarkTheme(): boolean {\n const [isDark, setIsDark] = useState(() =>\n typeof document === 'undefined' || document.documentElement.dataset.uiTheme !== 'light',\n );\n\n useEffect(() => {\n const update = (): void => {\n setIsDark(document.documentElement.dataset.uiTheme !== 'light');\n };\n update();\n window.addEventListener('canvas-theme-changed', update);\n return () => window.removeEventListener('canvas-theme-changed', update);\n }, []);\n\n return isDark;\n}\n","/**\n * PlatformProvider — Zero-cost context for platform worker integration.\n *\n * Always present in the component tree (via template main.tsx), but only\n * activates expensive features (WebSocket connections, polling) when a\n * component subscribes via the corresponding hook.\n *\n * Always available (zero overhead):\n * platformFetch(path, init) — authenticated fetch, prepends /platform\n *\n * Subscription-activated (zero cost until first subscriber):\n * useInbox() — inbox entries from InboxRoom WebSocket\n *\n * Auth uses getAuthToken() from deepspace on demand.\n * The template already provides DeepSpaceAuthProvider.\n *\n * Usage:\n * import { PlatformProvider, usePlatform, useInbox } from 'deepspace'\n *\n * // Already in template main.tsx:\n * <PlatformProvider>\n * <App />\n * </PlatformProvider>\n *\n * // In any component:\n * const { platformFetch } = usePlatform()\n * const inbox = useInbox() // activates WebSocket on mount\n */\n\nimport { createContext, useContext, useState, useEffect, useRef, useCallback } from 'react'\nimport { getAuthToken } from '../auth/token'\n\nexport interface InboxEntry {\n conversationId: string\n scope: {\n type: string\n participants?: string[]\n appId?: string\n contentRef?: string\n ticketNumber?: string\n }\n displayName: string\n muted: boolean\n joinedAt: string\n lastMessageAt: string | null\n lastMessagePreview: string | null\n lastMessageAuthor: string | null\n unreadCount: number\n}\n\nexport interface PlatformContextValue {\n /** Authenticated fetch — prepends /platform, sets Authorization + Content-Type */\n platformFetch: (path: string, init?: RequestInit) => Promise<Response>\n /** Subscribe to inbox — returns unsubscribe. Internal use by useInbox(). */\n subscribeInbox: () => () => void\n /** Current inbox entries (populated when at least one subscriber is active) */\n inbox: InboxEntry[]\n}\n\nexport const PlatformContext = createContext<PlatformContextValue | null>(null)\n\nexport function usePlatform(): PlatformContextValue {\n const ctx = useContext(PlatformContext)\n if (!ctx) throw new Error('usePlatform must be used within a PlatformProvider')\n return ctx\n}\n\n/**\n * Subscribe to inbox entries from the InboxRoom WebSocket.\n * The WebSocket connects when the first component calls useInbox()\n * and disconnects when the last one unmounts.\n */\nexport function useInbox(): InboxEntry[] {\n const { inbox, subscribeInbox } = usePlatform()\n useEffect(() => subscribeInbox(), [subscribeInbox])\n return inbox\n}\n\nexport function PlatformProvider({ children }: { children: React.ReactNode }) {\n const [inbox, setInbox] = useState<InboxEntry[]>([])\n\n // --- Subscription-activated inbox WebSocket ---\n const inboxSubscribers = useRef(0)\n const [inboxActive, setInboxActive] = useState(false)\n\n const subscribeInbox = useCallback(() => {\n inboxSubscribers.current++\n if (inboxSubscribers.current === 1) setInboxActive(true)\n return () => {\n inboxSubscribers.current--\n if (inboxSubscribers.current === 0) setInboxActive(false)\n }\n }, [])\n\n useEffect(() => {\n if (!inboxActive) return\n\n let mounted = true\n let ws: WebSocket | null = null\n let pingInterval: ReturnType<typeof setInterval> | undefined\n let retryTimer: ReturnType<typeof setTimeout>\n\n async function connect() {\n const token = await getAuthToken()\n if (!mounted) return\n if (!token) {\n // Auth not ready yet — retry\n retryTimer = setTimeout(connect, 2_000)\n return\n }\n\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n ws = new WebSocket(\n `${protocol}//${window.location.host}/platform/ws/inbox?token=${encodeURIComponent(token)}`,\n )\n\n ws.onmessage = (e) => {\n if (e.data === 'pong') return\n try {\n const msg = JSON.parse(e.data)\n switch (msg.type) {\n case 'inbox_init':\n setInbox(msg.entries)\n break\n case 'inbox_added':\n setInbox((prev) => [...prev, msg.entry])\n break\n case 'inbox_removed':\n setInbox((prev) => prev.filter((x) => x.conversationId !== msg.conversationId))\n break\n case 'inbox_updated':\n setInbox((prev) =>\n prev.map((x) => (x.conversationId === msg.entry.conversationId ? msg.entry : x)),\n )\n break\n }\n } catch {\n // ignore\n }\n }\n\n ws.onclose = () => {\n // Stop pinging the dead socket — otherwise each reconnect stacks\n // another interval that outlives its WebSocket.\n clearInterval(pingInterval)\n pingInterval = undefined\n if (mounted) {\n // Reconnect after brief delay\n retryTimer = setTimeout(connect, 3_000)\n }\n }\n\n // Clear any prior interval before starting a new one (defensive — a\n // reconnect goes through onclose, but this keeps the single-interval\n // invariant even if connect() is ever called twice).\n clearInterval(pingInterval)\n pingInterval = setInterval(() => {\n if (ws?.readyState === WebSocket.OPEN) ws.send('ping')\n }, 30_000)\n }\n\n connect()\n\n return () => {\n mounted = false\n clearTimeout(retryTimer)\n clearInterval(pingInterval)\n ws?.close()\n setInbox([])\n }\n }, [inboxActive])\n\n // --- platformFetch — always available, zero overhead until called ---\n const platformFetch = useCallback(async (path: string, init?: RequestInit) => {\n const token = await getAuthToken()\n const headers = new Headers(init?.headers)\n if (token) headers.set('Authorization', `Bearer ${token}`)\n if (!headers.has('Content-Type') && init?.body) headers.set('Content-Type', 'application/json')\n return fetch(`/platform${path}`, { ...init, headers })\n }, [])\n\n return (\n <PlatformContext.Provider value={{ platformFetch, subscribeInbox, inbox }}>\n {children}\n </PlatformContext.Provider>\n )\n}\n","/**\n * usePlatformWS — Generic WebSocket hook for platform DO connections.\n *\n * Handles all the boilerplate:\n * - Auth token fetch\n * - WebSocket connection with wss:// protocol detection\n * - Exponential backoff reconnect (500ms -> 10s max)\n * - Ping every 30s\n * - Cleanup on unmount or scopeId change\n *\n * Usage:\n * const state = usePlatformWS({\n * path: 'orders', // -> /platform/ws/orders/{scopeId}\n * scopeId: 'app:demo-corp',\n * initialState: { orders: [], items: {}, status: 'connecting' },\n * onMessage: (msg, prev) => {\n * switch (msg.type) {\n * case 'init': return { orders: msg.orders, items: msg.items, status: 'connected' }\n * case 'order_created': return { ...prev, orders: [...prev.orders, msg.order] }\n * default: return prev\n * }\n * },\n * })\n */\n\nimport { useState, useEffect, useRef } from 'react'\nimport { getAuthToken } from '../auth/token'\nimport type { ConnectionStatus } from '../storage/connection-status'\n\nconst MAX_RECONNECT_DELAY = 10_000\nconst BASE_RECONNECT_DELAY = 500\n\nexport interface PlatformWSOptions<S> {\n /** DO path segment — e.g. 'orders' for /platform/ws/orders/{scopeId} */\n path: string\n /** Scope ID — e.g. 'app:demo-corp'. Passing undefined skips connection. */\n scopeId: string | undefined\n /** Initial state before WS connects */\n initialState: S\n /**\n * Message handler. Receives the parsed message and current state.\n * Return a new state to update, or the same `prev` reference to skip.\n * The 'init' message should reset reconnect (handled automatically)\n * and return state with status: 'connected'.\n */\n onMessage: (msg: Record<string, unknown>, prev: S) => S\n}\n\nexport interface PlatformWSResult<S> {\n state: S\n /** Send a JSON message to the WebSocket */\n send: (msg: Record<string, unknown>) => void\n}\n\nexport function usePlatformWS<S extends { status: ConnectionStatus }>(\n options: PlatformWSOptions<S>,\n): PlatformWSResult<S> {\n const { path, scopeId, initialState, onMessage } = options\n const wsRef = useRef<WebSocket | null>(null)\n const pingIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null)\n const [state, setState] = useState<S>(initialState)\n const reconnectAttemptRef = useRef(0)\n const reconnectTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const mountedRef = useRef(true)\n\n // Keep onMessage ref stable to avoid reconnecting when handler identity changes\n const onMessageRef = useRef(onMessage)\n onMessageRef.current = onMessage\n\n const initialStateRef = useRef(initialState)\n initialStateRef.current = initialState\n\n useEffect(() => {\n mountedRef.current = true\n return () => { mountedRef.current = false }\n }, [])\n\n useEffect(() => {\n if (!scopeId) return\n\n setState(initialStateRef.current)\n reconnectAttemptRef.current = 0\n\n async function connect() {\n if (!mountedRef.current || !scopeId) return\n\n const token = await getAuthToken()\n if (!token || !mountedRef.current) {\n reconnectTimerRef.current = setTimeout(connect, 2_000)\n return\n }\n\n const params = new URLSearchParams()\n params.set('token', token)\n\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const ws = new WebSocket(\n `${protocol}//${window.location.host}/platform/ws/${path}/${scopeId}?${params}`,\n )\n wsRef.current = ws\n\n ws.onmessage = (e) => {\n if (e.data === 'pong') return\n try {\n const msg = JSON.parse(e.data)\n if (msg.type === 'init') {\n reconnectAttemptRef.current = 0\n }\n setState(prev => onMessageRef.current(msg, prev))\n } catch (err) {\n console.error(`[usePlatformWS:${path}] message parse error:`, err)\n }\n }\n\n ws.onclose = () => {\n if (!mountedRef.current) return\n const attempt = reconnectAttemptRef.current++\n const delay = Math.min(BASE_RECONNECT_DELAY * Math.pow(2, attempt), MAX_RECONNECT_DELAY)\n setState(prev => ({\n ...prev,\n status: (attempt === 0 ? 'connecting' : 'reconnecting') as ConnectionStatus,\n }))\n reconnectTimerRef.current = setTimeout(connect, delay)\n }\n\n if (pingIntervalRef.current) clearInterval(pingIntervalRef.current)\n pingIntervalRef.current = setInterval(() => {\n if (ws.readyState === WebSocket.OPEN) ws.send('ping')\n }, 30_000)\n }\n\n connect()\n\n return () => {\n if (reconnectTimerRef.current) {\n clearTimeout(reconnectTimerRef.current)\n reconnectTimerRef.current = null\n }\n if (pingIntervalRef.current) {\n clearInterval(pingIntervalRef.current)\n pingIntervalRef.current = null\n }\n wsRef.current?.close()\n wsRef.current = null\n }\n }, [scopeId, path])\n\n const send = useRef((msg: Record<string, unknown>) => {\n wsRef.current?.send(JSON.stringify(msg))\n }).current\n\n return { state, send }\n}\n","/**\n * Integration API client.\n *\n * Plain functions for calling third-party integrations (OpenAI, weather, etc.)\n * through the DeepSpace API worker. Works anywhere — React components,\n * event handlers, server actions, outside React entirely.\n *\n * Usage:\n * import { integration } from 'deepspace'\n * const result = await integration.post('openai/chat-completion', { messages })\n */\n\nimport { getAuthToken } from '../auth/token'\n\nconst ENDPOINT_PREFIX = '/api/integrations'\nconst DEFAULT_TIMEOUT_MS = 120_000\n\ninterface IntegrationResponse<T = unknown> {\n success: boolean\n data?: T\n error?: string\n issues?: Array<{ path?: string[]; message: string; code?: string }>\n}\n\ninterface RequestOptions {\n headers?: Record<string, string>\n timeoutMs?: number\n signal?: AbortSignal\n}\n\nfunction resolveUrl(endpoint: string): string {\n if (endpoint.startsWith('http://') || endpoint.startsWith('https://')) {\n return endpoint\n }\n const path = endpoint.startsWith('/') ? endpoint : endpoint ? `${ENDPOINT_PREFIX}/${endpoint}` : ENDPOINT_PREFIX\n if (typeof window !== 'undefined') {\n return `${window.location.origin}${path}`\n }\n return path\n}\n\nasync function request<T>(\n method: string,\n endpoint: string,\n body?: unknown,\n options?: RequestOptions,\n): Promise<IntegrationResponse<T>> {\n const url = resolveUrl(endpoint)\n const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...options?.headers,\n }\n\n // Attach auth token if available\n try {\n const token = await getAuthToken()\n if (token) headers['Authorization'] = `Bearer ${token}`\n } catch {\n // Not signed in — continue without auth\n }\n\n const controller = new AbortController()\n const abortFromCaller = () => controller.abort()\n if (options?.signal?.aborted) controller.abort()\n else options?.signal?.addEventListener('abort', abortFromCaller, { once: true })\n const timeout = setTimeout(() => controller.abort(), timeoutMs)\n\n try {\n const res = await fetch(url, {\n method,\n headers,\n body: body != null ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n })\n\n let payload: Record<string, unknown>\n try {\n payload = (await res.json()) as Record<string, unknown>\n } catch {\n return { success: false, error: `Request failed (${res.status})` }\n }\n\n if (!res.ok || payload.success === false) {\n return {\n success: false,\n error: (payload.error as string) ?? (payload.message as string) ?? `Request failed (${res.status})`,\n ...(payload.issues ? { issues: payload.issues as IntegrationResponse['issues'] } : {}),\n }\n }\n\n // Normalize response — handle both { success, data } and { success, ...rest }\n if ('data' in payload) {\n return { success: true, data: payload.data as T }\n }\n const rest = { ...payload }\n delete rest.success\n return { success: true, data: rest as T }\n } catch (err: unknown) {\n if (err instanceof DOMException && err.name === 'AbortError') {\n return { success: false, error: `Request timed out after ${timeoutMs}ms` }\n }\n return {\n success: false,\n error: err instanceof Error ? err.message : 'Request failed',\n }\n } finally {\n clearTimeout(timeout)\n options?.signal?.removeEventListener('abort', abortFromCaller)\n }\n}\n\nexport const integration = {\n post<T = unknown>(endpoint: string, data?: unknown, options?: RequestOptions) {\n return request<T>('POST', endpoint, data, options)\n },\n get<T = unknown>(endpoint: string, params?: Record<string, string | number | boolean | null | undefined>, options?: RequestOptions) {\n let url = endpoint\n if (params) {\n const searchParams = new URLSearchParams()\n for (const [k, v] of Object.entries(params)) {\n if (v != null) searchParams.set(k, String(v))\n }\n const qs = searchParams.toString()\n if (qs) url = `${endpoint}?${qs}`\n }\n return request<T>('GET', url, undefined, options)\n },\n put<T = unknown>(endpoint: string, data?: unknown, options?: RequestOptions) {\n return request<T>('PUT', endpoint, data, options)\n },\n delete<T = unknown>(endpoint: string, data?: unknown, options?: RequestOptions) {\n return request<T>('DELETE', endpoint, data, options)\n },\n}\n\nexport type { IntegrationResponse, RequestOptions }\n","/**\n * useVoiceAgent - managed browser voice on OpenAI Realtime, zero config.\n *\n * The hook mints a credit-gated ephemeral token via our proxy\n * (`openai-realtime/create-session`), connects the browser to OpenAI over raw\n * WebRTC, and on teardown reports the elapsed duration to\n * `openai-realtime/settle-session` so the user is billed for actual minutes. A\n * client timer stops the call at `maxMinutes` (OpenAI also hard-disconnects at\n * 60). The developer holds no key and writes no WebRTC.\n *\n * Billing note: settle is best-effort. If it never lands (crash, offline), the\n * server cron settles the session to the reserved cap - so the platform never\n * loses money; the hook's settle just refines DOWN to actual minutes and\n * releases a never-started session immediately.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { integration } from '../integration'\n\nconst OPENAI_REALTIME_BASE = 'https://api.openai.com/v1'\n\nexport type VoiceStatus = 'idle' | 'connecting' | 'live' | 'ended' | 'error'\n\nexport interface VoiceTranscriptEntry {\n role: 'user' | 'assistant'\n content: string\n}\n\nexport interface UseVoiceAgentOptions {\n instructions?: string\n voice?: string\n /** OpenAI tool definitions, passed through to the session. */\n tools?: Record<string, unknown>[]\n /** Hard cap; the hook stops the call at this many minutes. */\n maxMinutes?: number\n /** Called when the model invokes a tool; the return value is sent back. */\n onToolCall?: (name: string, args: unknown) => Promise<unknown> | unknown\n}\n\nexport interface UseVoiceAgentResult {\n status: VoiceStatus\n start: (overrides?: Partial<UseVoiceAgentOptions>) => Promise<void>\n stop: () => Promise<void>\n isMuted: boolean\n toggleMute: () => void\n isAgentSpeaking: boolean\n transcript: VoiceTranscriptEntry[]\n error: string | null\n /** Escape hatches for advanced use. */\n pc: RTCPeerConnection | null\n dataChannel: RTCDataChannel | null\n}\n\nconst ZERO_USAGE = { inputAudioTokens: 0, outputAudioTokens: 0, inputTextTokens: 0, outputTextTokens: 0 }\n\n/** Token accounting block within a Realtime `response.done` event. */\ninterface RealtimeTokenDetails {\n audio_tokens?: number\n text_tokens?: number\n}\n\n/** A single output item within a Realtime response (e.g. a function call). */\ninterface RealtimeOutputItem {\n type?: string\n name?: string\n call_id?: string\n arguments?: string\n}\n\n/**\n * Loosely-typed OpenAI Realtime wire event. Only the fields this hook reads are\n * modeled; everything else is left off since the protocol is large and dynamic.\n */\ninterface RealtimeEvent {\n type?: string\n delta?: string\n transcript?: string\n response?: {\n usage?: {\n input_token_details?: RealtimeTokenDetails\n output_token_details?: RealtimeTokenDetails\n }\n output?: RealtimeOutputItem[]\n }\n}\n\nexport function useVoiceAgent(options: UseVoiceAgentOptions = {}): UseVoiceAgentResult {\n const [status, setStatus] = useState<VoiceStatus>('idle')\n const [isMuted, setIsMuted] = useState(false)\n const [isAgentSpeaking, setIsAgentSpeaking] = useState(false)\n const [transcript, setTranscript] = useState<VoiceTranscriptEntry[]>([])\n const [error, setError] = useState<string | null>(null)\n\n const pcRef = useRef<RTCPeerConnection | null>(null)\n const dcRef = useRef<RTCDataChannel | null>(null)\n const streamRef = useRef<MediaStream | null>(null)\n const audioRef = useRef<HTMLAudioElement | null>(null)\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const sessionIdRef = useRef<string | null>(null)\n const liveStartRef = useRef<number | null>(null)\n const settledRef = useRef(false)\n const usageRef = useRef({ ...ZERO_USAGE })\n\n // Keep the latest options without re-creating callbacks (onToolCall etc).\n const optsRef = useRef(options)\n optsRef.current = options\n\n const settle = useCallback(async () => {\n const sessionId = sessionIdRef.current\n if (!sessionId || settledRef.current) return\n settledRef.current = true\n const durationSeconds = liveStartRef.current ? Math.max(0, (Date.now() - liveStartRef.current) / 1000) : 0\n try {\n await integration.post('openai-realtime/settle-session', {\n sessionId,\n durationSeconds,\n usage: usageRef.current,\n })\n } catch {\n // Best-effort: the server cron settles to the cap if this never lands.\n }\n }, [])\n\n const cleanup = useCallback(() => {\n if (timerRef.current) {\n clearTimeout(timerRef.current)\n timerRef.current = null\n }\n try {\n dcRef.current?.close()\n } catch {\n // ignore\n }\n try {\n streamRef.current?.getTracks().forEach((t) => t.stop())\n } catch {\n // ignore\n }\n try {\n if (pcRef.current) pcRef.current.onconnectionstatechange = null\n pcRef.current?.close()\n } catch {\n // ignore\n }\n if (audioRef.current) audioRef.current.srcObject = null\n dcRef.current = null\n pcRef.current = null\n streamRef.current = null\n audioRef.current = null\n }, [])\n\n const stop = useCallback(async () => {\n cleanup()\n await settle()\n setIsAgentSpeaking(false)\n setStatus((s) => (s === 'error' ? s : 'ended'))\n }, [cleanup, settle])\n\n const appendTranscript = useCallback((role: 'user' | 'assistant', delta: string) => {\n if (!delta) return\n setTranscript((prev) => {\n const last = prev[prev.length - 1]\n if (last && last.role === role) return [...prev.slice(0, -1), { role, content: last.content + delta }]\n return [...prev, { role, content: delta }]\n })\n }, [])\n\n // Tool-call round-trip: run the dev's handler, send the output back, ask for the\n // next response. Event field names track the current Realtime API; adjust here\n // if OpenAI renames them.\n const handleToolCall = useCallback(async (item: { name?: string; call_id?: string; arguments?: string }) => {\n const onToolCall = optsRef.current.onToolCall\n let output: unknown = null\n if (onToolCall && item.name) {\n let args: unknown = {}\n try {\n args = item.arguments ? JSON.parse(item.arguments) : {}\n } catch {\n args = {}\n }\n try {\n output = await onToolCall(item.name, args)\n } catch (e) {\n output = { error: e instanceof Error ? e.message : String(e) }\n }\n }\n const dc = dcRef.current\n if (!dc || dc.readyState !== 'open') return\n dc.send(\n JSON.stringify({\n type: 'conversation.item.create',\n item: { type: 'function_call_output', call_id: item.call_id, output: JSON.stringify(output ?? null) },\n }),\n )\n dc.send(JSON.stringify({ type: 'response.create' }))\n }, [])\n\n const handleEvent = useCallback(\n (raw: unknown) => {\n if (typeof raw !== 'string') return\n // OpenAI Realtime events are a large, dynamic wire protocol with deeply\n // nested optional fields; model as a loose record and read fields defensively.\n let ev: RealtimeEvent\n try {\n ev = JSON.parse(raw) as RealtimeEvent\n } catch {\n return\n }\n switch (ev.type) {\n case 'response.output_audio_transcript.delta':\n case 'response.audio_transcript.delta':\n appendTranscript('assistant', ev.delta ?? '')\n break\n case 'conversation.item.input_audio_transcription.completed':\n appendTranscript('user', ev.transcript ?? '')\n break\n case 'output_audio_buffer.started':\n setIsAgentSpeaking(true)\n break\n case 'output_audio_buffer.stopped':\n setIsAgentSpeaking(false)\n break\n case 'response.done': {\n const u = ev.response?.usage\n if (u) {\n usageRef.current.inputAudioTokens += u.input_token_details?.audio_tokens ?? 0\n usageRef.current.outputAudioTokens += u.output_token_details?.audio_tokens ?? 0\n usageRef.current.inputTextTokens += u.input_token_details?.text_tokens ?? 0\n usageRef.current.outputTextTokens += u.output_token_details?.text_tokens ?? 0\n }\n for (const item of ev.response?.output ?? []) {\n if (item?.type === 'function_call') void handleToolCall(item)\n }\n break\n }\n }\n },\n [appendTranscript, handleToolCall],\n )\n\n const start = useCallback(\n async (overrides: Partial<UseVoiceAgentOptions> = {}) => {\n // A second start() while a session is in flight (re-click / re-trigger) must\n // not abandon the prior one: settle + tear it down first, else its sessionId\n // is lost (the cron bills it at the full cap) and its mic/peer/timer leak.\n if (sessionIdRef.current) {\n cleanup()\n await settle()\n }\n const o = { ...optsRef.current, ...overrides }\n setError(null)\n setTranscript([])\n setIsAgentSpeaking(false)\n setStatus('connecting')\n settledRef.current = false\n liveStartRef.current = null\n sessionIdRef.current = null\n usageRef.current = { ...ZERO_USAGE }\n\n try {\n const res = await integration.post<{ sessionId: string; clientSecret: string; model: string; maxMinutes: number }>(\n 'openai-realtime/create-session',\n { instructions: o.instructions, voice: o.voice, tools: o.tools, maxMinutes: o.maxMinutes },\n )\n if (!res.success || !res.data) throw new Error(res.error ?? 'Failed to start voice session')\n const { sessionId, clientSecret, model, maxMinutes } = res.data\n sessionIdRef.current = sessionId\n\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true })\n streamRef.current = stream\n\n const pc = new RTCPeerConnection()\n pcRef.current = pc\n // End + settle on a real connection drop (failed/closed) rather than waiting\n // for the maxMinutes timer, so a dropped call bills connected time, not the\n // cap. 'disconnected' is transient (may recover) so it is not acted on.\n pc.onconnectionstatechange = () => {\n if (pc.connectionState === 'failed' || pc.connectionState === 'closed') void stop()\n }\n const audio = new Audio()\n audio.autoplay = true\n audioRef.current = audio\n pc.ontrack = (e) => {\n audio.srcObject = e.streams[0]\n }\n stream.getTracks().forEach((t) => pc.addTrack(t, stream))\n\n const dc = pc.createDataChannel('oai-events')\n dcRef.current = dc\n dc.onmessage = (e) => handleEvent(e.data)\n\n const offer = await pc.createOffer()\n await pc.setLocalDescription(offer)\n const sdpRes = await fetch(`${OPENAI_REALTIME_BASE}/realtime/calls?model=${encodeURIComponent(model)}`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${clientSecret}`, 'Content-Type': 'application/sdp' },\n body: offer.sdp,\n })\n if (!sdpRes.ok) throw new Error(`Realtime connect failed (${sdpRes.status})`)\n const answer = await sdpRes.text()\n await pc.setRemoteDescription({ type: 'answer', sdp: answer })\n\n liveStartRef.current = Date.now()\n setStatus('live')\n timerRef.current = setTimeout(() => void stop(), (maxMinutes ?? 30) * 60_000)\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n setStatus('error')\n cleanup()\n // Release the reservation immediately (durationSeconds = 0 if never live).\n await settle()\n }\n },\n [cleanup, settle, stop, handleEvent],\n )\n\n const toggleMute = useCallback(() => {\n const stream = streamRef.current\n if (!stream) return\n setIsMuted((muted) => {\n const next = !muted\n stream.getAudioTracks().forEach((t) => (t.enabled = !next))\n return next\n })\n }, [])\n\n // Settle + tear down on unmount so a closed tab never leaves a dangling session.\n useEffect(() => {\n return () => {\n cleanup()\n void settle()\n }\n }, [cleanup, settle])\n\n return {\n status,\n start,\n stop,\n isMuted,\n toggleMute,\n isAgentSpeaking,\n transcript,\n error,\n pc: pcRef.current,\n dataChannel: dcRef.current,\n }\n}\n","/**\n * Pure decoders for the Vercel AI SDK v5 `toUIMessageStreamResponse` body.\n *\n * The wire format is SSE: each event is `data: <json>\\n\\n`, terminated by\n * `data: [DONE]\\n\\n`. Each event carries a `type` discriminator\n * (`text-delta`, `tool-input-available`, `tool-output-available`,\n * `tool-input-error`, `tool-output-error`, `error`, `abort`, …) — see the\n * `UIMessageChunk` union in `ai/dist/index.d.ts`.\n *\n * This module decodes those chunks into a small action vocabulary that\n * the React layer then applies to its state. Keeping decode pure means\n * the v5-boundary behavior is fully testable without spinning up a\n * component tree.\n */\n\n/** Raw v5 stream chunk, as parsed off the wire. `type` may be any string. */\nexport type AiStreamChunk = Record<string, unknown> & { type?: unknown }\n\n/**\n * Strip one SSE line's framing. Returns the parsed JSON payload, or `null`\n * for blank lines, comment lines (`:` prefix), non-`data:` SSE fields, the\n * `[DONE]` terminator, or any line whose payload doesn't parse as JSON.\n *\n * Multi-line `data:` events (per the SSE spec, joined by `\\n` before\n * dispatch) are NOT supported — v5's emitter never produces them. If you\n * hit a wire intermediary that re-splits long payloads (e.g. a proxy that\n * caps line length), upgrade this to accumulate consecutive `data:` lines\n * until a blank line.\n */\nexport function parseSseLine(line: string): AiStreamChunk | null {\n if (!line) return null\n if (line.startsWith(':')) return null // SSE comment\n if (!line.startsWith('data:')) return null // we only consume `data:` events\n // SSE allows exactly one space after the colon to be elided. Trim a\n // trailing \\r so CRLF-framed responses still hit the [DONE] short-circuit.\n const raw = line.slice(5).replace(/^ /, '').replace(/\\r$/, '')\n if (raw === '[DONE]') return null\n try {\n const parsed: unknown = JSON.parse(raw)\n if (!parsed || typeof parsed !== 'object') return null\n return parsed as AiStreamChunk\n } catch {\n return null\n }\n}\n\n/**\n * Action vocabulary the React layer applies. Every `decodeAiStreamChunk`\n * output is one of these — `null` means the chunk is intentionally ignored\n * (lifecycle markers, reasoning we don't surface yet, etc.).\n *\n * Wire field renames (v5): chunks carry `input` / `output` directly. We\n * map them to `args` / `result` here so persisted UI shapes stay v4-stable.\n */\nexport type AiStreamAction =\n | { type: 'append-text'; delta: string }\n | { type: 'upsert-tool-call'; toolCallId: string; toolName: string; input: unknown }\n | { type: 'finalize-tool-call'; toolCallId: string; result: unknown }\n /**\n * Schema validation rejected the input before the tool ran; no preceding\n * `upsert-tool-call` was emitted, so the React layer must create the\n * invocation and finalize it as failed in one step.\n */\n | { type: 'fail-tool-input'; toolCallId: string; toolName: string; input: unknown; errorText: string }\n /** Tool execution failed; a previous `upsert-tool-call` exists to finalize. */\n | { type: 'fail-tool-output'; toolCallId: string; errorText: string }\n | { type: 'stream-error'; errorText: string }\n /** Server-side abort with no error chunk to follow. */\n | { type: 'abort' }\n\n/** Decode one v5 UIMessage stream chunk into an action, or `null` to ignore. */\nexport function decodeAiStreamChunk(chunk: AiStreamChunk): AiStreamAction | null {\n if (typeof chunk.type !== 'string') return null\n\n switch (chunk.type) {\n case 'text-delta': {\n const delta = typeof chunk.delta === 'string' ? chunk.delta : ''\n if (!delta) return null\n return { type: 'append-text', delta }\n }\n\n case 'tool-input-available': {\n if (typeof chunk.toolCallId !== 'string' || typeof chunk.toolName !== 'string') return null\n return {\n type: 'upsert-tool-call',\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n input: chunk.input,\n }\n }\n\n case 'tool-output-available': {\n if (typeof chunk.toolCallId !== 'string') return null\n // The wire emits `output: part.output` directly (no `toJSONValue`\n // wrapping at this layer). When `part.output` is `undefined`,\n // `JSON.stringify` drops the key entirely, so `'output' in chunk`\n // reads false on the receiving side. Treat absence as an explicit\n // `null` result — finalizing the invocation is what stops the\n // spinner; ignoring the chunk would hang the UI forever for any\n // tool whose `execute` returns undefined.\n const result = 'output' in chunk ? chunk.output : null\n return { type: 'finalize-tool-call', toolCallId: chunk.toolCallId, result }\n }\n\n case 'tool-input-error': {\n if (typeof chunk.toolCallId !== 'string' || typeof chunk.toolName !== 'string') return null\n const errorText = typeof chunk.errorText === 'string' ? chunk.errorText : 'Tool input rejected'\n return {\n type: 'fail-tool-input',\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n input: chunk.input,\n errorText,\n }\n }\n\n case 'tool-output-error': {\n if (typeof chunk.toolCallId !== 'string') return null\n const errorText = typeof chunk.errorText === 'string' ? chunk.errorText : 'Tool execution failed'\n return { type: 'fail-tool-output', toolCallId: chunk.toolCallId, errorText }\n }\n\n case 'error': {\n const errorText = typeof chunk.errorText === 'string' ? chunk.errorText : 'Stream error'\n return { type: 'stream-error', errorText }\n }\n\n case 'abort':\n return { type: 'abort' }\n\n // Forward-compat: silently ignore lifecycle markers (start/finish,\n // start-step/finish-step), text-start/-end, tool-input-start/-delta,\n // reasoning-*, source-*, message-metadata.\n default:\n return null\n }\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { useAuth } from './auth'\nimport { useUser } from './storage'\nimport {\n ASYNC_RESOURCE_DEFAULTS,\n canAutoLoadPagedResource,\n clampPagedResourceItems,\n deriveLoadState,\n getPagedResourceCooldownMs,\n isAsyncResourceSlow,\n} from './status-core'\n\nexport {\n ASYNC_RESOURCE_DEFAULTS,\n canAutoLoadPagedResource,\n clampPagedResourceItems,\n deriveLoadState,\n getPagedResourceCooldownMs,\n isAsyncResourceSlow,\n}\nexport type {\n AsyncResourceDefaults,\n DeriveLoadStateInput,\n LoadState,\n LoadStateAuthInput,\n LoadStateStatusInput,\n} from './status-core'\n\nexport interface UseAuthStatusOptions {\n requireSignedIn?: boolean\n}\n\nexport interface UseAuthProfileReadyOptions extends UseAuthStatusOptions {\n requireUser?: boolean\n}\n\n/** @deprecated Use `UseAuthProfileReadyOptions` instead. */\nexport type UseAuthReadyOptions = UseAuthProfileReadyOptions\n\nexport function useAuthStatus(options: UseAuthStatusOptions = {}) {\n const auth = useAuth()\n const status = deriveLoadState({\n auth: {\n isLoaded: auth.isLoaded,\n isSignedIn: auth.isSignedIn,\n requireSignedIn: options.requireSignedIn,\n },\n })\n\n return {\n ...auth,\n status,\n isReady: status === 'ready',\n }\n}\n\nexport function useAuthProfileReady(options: UseAuthProfileReadyOptions = {}) {\n const auth = useAuth()\n const { user, isLoading: userLoading, refetch } = useUser()\n const status = deriveLoadState({\n auth: {\n isLoaded: auth.isLoaded,\n isSignedIn: auth.isSignedIn,\n requireSignedIn: options.requireSignedIn,\n requireUser: options.requireUser,\n userLoading,\n },\n })\n\n return {\n ...auth,\n user,\n userLoading,\n refetchUser: refetch,\n status,\n isReady: status === 'ready',\n }\n}\n\n/** @deprecated Use `useAuthStatus` for auth-only UI or `useAuthProfileReady` for profile-backed UI. */\nexport const useAuthReady = useAuthProfileReady\n\nexport type AsyncResourceStatus = 'idle' | 'loading' | 'ready' | 'error'\nexport type PagedResourceStatus = AsyncResourceStatus\n\nexport interface AsyncResourceState<T> {\n status: AsyncResourceStatus\n data: T | null\n error: string | null\n isRefreshing: boolean\n isSlow: boolean\n retryCount: number\n}\n\nexport interface UseAsyncResourceOptions<T> {\n enabled?: boolean\n initialData?: T | null\n keepPreviousData?: boolean\n retry?: number\n retryDelayMs?: number\n slowAfterMs?: number\n}\n\nexport interface PagedResourceFetchArgs {\n page: number\n pageSize: number\n signal: AbortSignal\n}\n\nexport interface PagedResourcePage<T> {\n items: T[]\n hasMore?: boolean\n}\n\nexport interface PagedResourceState<T> {\n status: PagedResourceStatus\n items: T[]\n error: string | null\n warning: string | null\n hasMore: boolean\n isLoadingInitial: boolean\n isLoadingMore: boolean\n isRefreshing: boolean\n}\n\nexport interface UsePagedResourceOptions<T> {\n enabled?: boolean\n initialItems?: T[]\n pageSize?: number\n maxItemsPerPage?: number\n keepPreviousData?: boolean\n autoRetryOnError?: boolean\n retryDelayMs?: number\n maxRetryDelayMs?: number\n}\n\nexport function useAsyncResource<T>(\n fetcher: (signal: AbortSignal) => Promise<T>,\n deps: readonly unknown[],\n options: UseAsyncResourceOptions<T> = {},\n) {\n const enabled = options.enabled ?? true\n const keepPreviousData = options.keepPreviousData ?? true\n const retry = options.retry ?? ASYNC_RESOURCE_DEFAULTS.retry\n const retryDelayMs = options.retryDelayMs ?? ASYNC_RESOURCE_DEFAULTS.retryDelayMs\n const slowAfterMs = options.slowAfterMs ?? ASYNC_RESOURCE_DEFAULTS.slowAfterMs\n const [state, setState] = useState<AsyncResourceState<T>>(() => ({\n status: enabled ? 'loading' : 'idle',\n data: options.initialData ?? null,\n error: null,\n isRefreshing: false,\n isSlow: false,\n retryCount: 0,\n }))\n const requestIdRef = useRef(0)\n const [reloadTick, setReloadTick] = useState(0)\n\n const reload = useCallback(() => {\n setReloadTick((tick) => tick + 1)\n }, [])\n\n useEffect(() => {\n if (!enabled) {\n setState((prev) => ({ ...prev, status: 'idle', isRefreshing: false, isSlow: false, retryCount: 0 }))\n return\n }\n\n const requestId = ++requestIdRef.current\n const controller = new AbortController()\n let retryTimer: ReturnType<typeof setTimeout> | null = null\n let slowTimer: ReturnType<typeof setTimeout> | null = null\n\n setState((prev) => {\n const hasData = keepPreviousData && prev.data != null\n return {\n ...prev,\n status: hasData ? 'ready' : 'loading',\n error: null,\n isRefreshing: hasData,\n isSlow: false,\n data: hasData ? prev.data : (options.initialData ?? null),\n retryCount: 0,\n }\n })\n\n if (slowAfterMs > 0) {\n const startedAt = Date.now()\n slowTimer = setTimeout(() => {\n if (requestIdRef.current !== requestId || controller.signal.aborted) return\n setState((prev) => ({\n ...prev,\n isSlow: isAsyncResourceSlow({ startedAt, now: Date.now(), slowAfterMs }),\n }))\n }, slowAfterMs)\n }\n\n const wait = (ms: number) =>\n new Promise<void>((resolve) => {\n retryTimer = setTimeout(resolve, ms)\n })\n\n const load = async () => {\n let attempt = 0\n while (!controller.signal.aborted) {\n try {\n const data = await fetcher(controller.signal)\n if (requestIdRef.current !== requestId || controller.signal.aborted) return\n if (slowTimer) clearTimeout(slowTimer)\n setState({ status: 'ready', data, error: null, isRefreshing: false, isSlow: false, retryCount: attempt })\n return\n } catch (err: unknown) {\n if (requestIdRef.current !== requestId || controller.signal.aborted) return\n const message = err instanceof Error ? err.message : 'Request failed'\n if (attempt < retry) {\n attempt += 1\n setState((prev) => ({\n ...prev,\n error: message,\n retryCount: attempt,\n isRefreshing: keepPreviousData && prev.data != null,\n }))\n await wait(retryDelayMs)\n continue\n }\n\n if (slowTimer) clearTimeout(slowTimer)\n setState((prev) => ({\n status: keepPreviousData && prev.data != null ? 'ready' : 'error',\n data: keepPreviousData ? prev.data : null,\n error: message,\n isRefreshing: false,\n isSlow: false,\n retryCount: attempt,\n }))\n return\n }\n }\n }\n\n load()\n\n return () => {\n if (retryTimer) clearTimeout(retryTimer)\n if (slowTimer) clearTimeout(slowTimer)\n controller.abort()\n }\n // Deps are the caller-supplied ...deps plus the primitive options; the\n // inline load() closure (and fetcher/options.initialData it captures) is\n // deliberately not listed to avoid re-running the fetch/retry lifecycle on\n // every render (which would cause request storms).\n // eslint-disable-next-line react-hooks/exhaustive-deps -- caller-supplied ...deps is the re-fetch contract; inline load()/fetcher excluded to avoid request storms\n }, [enabled, keepPreviousData, retry, retryDelayMs, slowAfterMs, reloadTick, ...deps])\n\n return useMemo(() => ({ ...state, reload }), [state, reload])\n}\n\nexport function usePagedResource<T>(\n fetchPage: (args: PagedResourceFetchArgs) => Promise<PagedResourcePage<T>>,\n deps: readonly unknown[],\n options: UsePagedResourceOptions<T> = {},\n) {\n const enabled = options.enabled ?? true\n const initialItems = options.initialItems ?? []\n const pageSize = options.pageSize ?? 20\n const maxItemsPerPage = options.maxItemsPerPage ?? pageSize\n const keepPreviousData = options.keepPreviousData ?? true\n const autoRetryOnError = options.autoRetryOnError ?? false\n const retryDelayMs = options.retryDelayMs ?? ASYNC_RESOURCE_DEFAULTS.retryDelayMs\n const maxRetryDelayMs = options.maxRetryDelayMs ?? 30_000\n const hasInitialItems = initialItems.length > 0\n const [state, setState] = useState<PagedResourceState<T>>(() => ({\n status: enabled ? (hasInitialItems ? 'ready' : 'loading') : 'idle',\n items: initialItems,\n error: null,\n warning: null,\n hasMore: true,\n isLoadingInitial: enabled && !hasInitialItems,\n isLoadingMore: false,\n isRefreshing: false,\n }))\n const requestIdRef = useRef(0)\n const controllerRef = useRef<AbortController | null>(null)\n const loadingRef = useRef(false)\n const nextPageRef = useRef(hasInitialItems ? 2 : 1)\n const failedPageRef = useRef<number | null>(null)\n const failureCountRef = useRef(0)\n const cooldownUntilRef = useRef(0)\n const autoRetryTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n const clearAutoRetryTimer = useCallback(() => {\n if (autoRetryTimerRef.current) {\n clearTimeout(autoRetryTimerRef.current)\n autoRetryTimerRef.current = null\n }\n }, [])\n\n const resetCooldown = useCallback(() => {\n failureCountRef.current = 0\n cooldownUntilRef.current = 0\n }, [])\n\n const setFailureCooldown = useCallback(() => {\n failureCountRef.current += 1\n const delay = getPagedResourceCooldownMs({\n failureCount: failureCountRef.current,\n retryDelayMs,\n maxRetryDelayMs,\n })\n cooldownUntilRef.current = Date.now() + delay\n return delay\n }, [maxRetryDelayMs, retryDelayMs])\n\n const startLoad = useCallback(async (page: number, mode: 'auto' | 'manual' | 'refresh') => {\n if (!enabled || loadingRef.current) return\n if (\n mode === 'auto' &&\n !canAutoLoadPagedResource({\n hasMore: true,\n isLoading: false,\n now: Date.now(),\n cooldownUntil: cooldownUntilRef.current,\n })\n ) {\n return\n }\n\n clearAutoRetryTimer()\n loadingRef.current = true\n const requestId = ++requestIdRef.current\n controllerRef.current?.abort()\n const controller = new AbortController()\n controllerRef.current = controller\n\n setState((prev) => {\n const hasItems = prev.items.length > 0\n return {\n ...prev,\n status: hasItems && mode !== 'refresh' ? 'ready' : 'loading',\n error: mode === 'auto' && prev.error ? prev.error : null,\n warning: null,\n isLoadingInitial: !hasItems && mode !== 'refresh',\n isLoadingMore: hasItems && mode !== 'refresh',\n isRefreshing: mode === 'refresh' && hasItems,\n items: mode === 'refresh' && !keepPreviousData ? [] : prev.items,\n }\n })\n\n try {\n const result = await fetchPage({ page, pageSize, signal: controller.signal })\n if (requestIdRef.current !== requestId || controller.signal.aborted) return\n const { items: pageItems, droppedItemCount } = clampPagedResourceItems({\n items: result.items,\n maxItemsPerPage,\n })\n resetCooldown()\n failedPageRef.current = null\n nextPageRef.current = page + 1\n setState((prev) => ({\n status: 'ready',\n items: page === 1 || mode === 'refresh'\n ? pageItems\n : [...prev.items, ...pageItems],\n error: null,\n warning: droppedItemCount > 0\n ? `The last API page returned ${result.items.length} items; only ${pageItems.length} were kept. Request a smaller page from the API instead of fetching the whole feed.`\n : null,\n hasMore: droppedItemCount > 0 || (result.hasMore ?? result.items.length >= pageSize),\n isLoadingInitial: false,\n isLoadingMore: false,\n isRefreshing: false,\n }))\n } catch (err: unknown) {\n if (requestIdRef.current !== requestId || controller.signal.aborted) return\n const message = err instanceof Error ? err.message : 'Request failed'\n failedPageRef.current = page\n const cooldownMs = setFailureCooldown()\n setState((prev) => {\n const hasItems = prev.items.length > 0\n return {\n ...prev,\n status: hasItems ? 'ready' : 'error',\n error: message,\n warning: prev.warning,\n hasMore: hasItems ? prev.hasMore : false,\n isLoadingInitial: false,\n isLoadingMore: false,\n isRefreshing: false,\n }\n })\n if (autoRetryOnError) {\n autoRetryTimerRef.current = setTimeout(() => {\n autoRetryTimerRef.current = null\n void startLoad(page, 'auto')\n }, cooldownMs)\n }\n } finally {\n if (requestIdRef.current === requestId) {\n loadingRef.current = false\n controllerRef.current = null\n }\n }\n }, [autoRetryOnError, clearAutoRetryTimer, enabled, fetchPage, keepPreviousData, maxItemsPerPage, pageSize, resetCooldown, setFailureCooldown])\n\n const loadMore = useCallback(() => {\n if (!state.hasMore || state.isLoadingInitial || state.isLoadingMore || state.isRefreshing) return\n void startLoad(nextPageRef.current, 'auto')\n }, [startLoad, state.hasMore, state.isLoadingInitial, state.isLoadingMore, state.isRefreshing])\n\n const retry = useCallback(() => {\n const page = failedPageRef.current ?? nextPageRef.current\n void startLoad(page, 'manual')\n }, [startLoad])\n\n const refresh = useCallback(() => {\n failedPageRef.current = null\n resetCooldown()\n nextPageRef.current = 1\n void startLoad(1, 'refresh')\n }, [resetCooldown, startLoad])\n\n useEffect(() => {\n requestIdRef.current += 1\n controllerRef.current?.abort()\n clearAutoRetryTimer()\n loadingRef.current = false\n failedPageRef.current = null\n resetCooldown()\n nextPageRef.current = hasInitialItems ? 2 : 1\n\n setState({\n status: enabled ? (hasInitialItems ? 'ready' : 'loading') : 'idle',\n items: initialItems,\n error: null,\n warning: null,\n hasMore: true,\n isLoadingInitial: enabled && !hasInitialItems,\n isLoadingMore: false,\n isRefreshing: false,\n })\n\n if (enabled && !hasInitialItems) {\n void startLoad(1, 'auto')\n }\n\n return () => {\n requestIdRef.current += 1\n controllerRef.current?.abort()\n clearAutoRetryTimer()\n loadingRef.current = false\n }\n // Deps are the caller-supplied ...deps plus the primitive options; the\n // startLoad closure and refs are deliberately excluded so this reset effect\n // only re-runs on real dependency changes, not on every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps -- caller-supplied ...deps is the reset contract; startLoad/refs/initialItems excluded so reset only fires on real dep changes\n }, [enabled, pageSize, maxItemsPerPage, keepPreviousData, autoRetryOnError, retryDelayMs, maxRetryDelayMs, ...deps])\n\n return useMemo(() => ({\n ...state,\n loadMore,\n retry,\n refresh,\n }), [loadMore, refresh, retry, state])\n}\n","export type LoadState = 'loading' | 'ready' | 'empty' | 'error' | 'signedOut'\n\nexport interface LoadStateAuthInput {\n isLoaded: boolean\n isSignedIn: boolean\n requireSignedIn?: boolean\n requireUser?: boolean\n userLoading?: boolean\n}\n\nexport interface LoadStateStatusInput {\n status: 'loading' | 'ready' | 'error' | 'idle' | 'refreshing'\n error?: unknown\n}\n\nexport interface DeriveLoadStateInput {\n auth?: LoadStateAuthInput\n queries?: LoadStateStatusInput[]\n resources?: LoadStateStatusInput[]\n empty?: boolean\n}\n\nexport interface AsyncResourceDefaults {\n retry: number\n retryDelayMs: number\n slowAfterMs: number\n}\n\nexport const ASYNC_RESOURCE_DEFAULTS: AsyncResourceDefaults = {\n retry: 0,\n retryDelayMs: 2_000,\n slowAfterMs: 10_000,\n}\n\nexport function isAsyncResourceSlow(input: {\n startedAt: number\n now: number\n slowAfterMs: number\n}): boolean {\n return input.slowAfterMs > 0 && input.now - input.startedAt >= input.slowAfterMs\n}\n\nexport interface PagedResourceCooldownInput {\n failureCount: number\n retryDelayMs: number\n maxRetryDelayMs: number\n}\n\nexport function getPagedResourceCooldownMs(input: PagedResourceCooldownInput): number {\n const failureCount = Math.max(1, input.failureCount)\n return Math.min(input.maxRetryDelayMs, input.retryDelayMs * 2 ** (failureCount - 1))\n}\n\nexport function canAutoLoadPagedResource(input: {\n hasMore: boolean\n isLoading: boolean\n now: number\n cooldownUntil: number\n}): boolean {\n return input.hasMore && !input.isLoading && input.cooldownUntil <= input.now\n}\n\nexport function clampPagedResourceItems<T>(input: {\n items: T[]\n maxItemsPerPage: number\n}): {\n items: T[]\n droppedItemCount: number\n} {\n if (input.maxItemsPerPage <= 0 || input.items.length <= input.maxItemsPerPage) {\n return { items: input.items, droppedItemCount: 0 }\n }\n return {\n items: input.items.slice(0, input.maxItemsPerPage),\n droppedItemCount: input.items.length - input.maxItemsPerPage,\n }\n}\n\nexport function deriveLoadState(input: DeriveLoadStateInput): LoadState {\n const auth = input.auth\n if (auth) {\n if (!auth.isLoaded) return 'loading'\n if (auth.requireSignedIn && !auth.isSignedIn) return 'signedOut'\n if (auth.requireUser && auth.isSignedIn && auth.userLoading) return 'loading'\n }\n\n const statuses = [...(input.queries ?? []), ...(input.resources ?? [])]\n if (statuses.some((item) => item.status === 'error' || item.error)) return 'error'\n if (statuses.some((item) => item.status === 'loading' || item.status === 'idle')) return 'loading'\n if (input.empty) return 'empty'\n return 'ready'\n}\n","// Per-app subscription state hook. Reads through the starter's same-origin\n// /_deepspace proxy, which signs with the app identity token.\n\nimport { useCallback, useEffect, useState } from 'react'\nimport { getAuthToken } from '../auth/token'\nimport { isEntitled, type PlanInfo, type SubscriptionStatus } from '../../shared/subscription'\n\n// Re-export for SDK consumers — these are public types the hook is generic over.\nexport type { SubscriptionStatus, PlanInfo }\n\nexport interface SubscriptionState {\n tier: string\n status: SubscriptionStatus\n /** Billing interval of the active subscription, or null for free / no sub. */\n interval: 'month' | 'year' | null\n currentPeriodEnd: number | null\n cancelAtPeriodEnd: boolean\n trialEndsAt: number | null\n plans: PlanInfo[]\n}\n\nexport interface SubscribeOpts {\n interval?: 'month' | 'year'\n returnUrl?: string\n cancelUrl?: string\n}\n\nexport interface SubscribeResult {\n url: string | null\n immediate: boolean\n requiresPayment?: boolean\n hostedInvoiceUrl?: string | null\n}\n\ninterface UseSubscriptionReturn {\n tier: string\n status: SubscriptionStatus\n /**\n * True iff status is `active` or `trialing`. `hasTier` and `isAtLeast`\n * already encode this — read `entitled` only when you need the raw boolean\n * without picking a tier.\n */\n entitled: boolean\n /** Current billing interval, or null when on free / no subscription. */\n interval: 'month' | 'year' | null\n currentPeriodEnd: number | null\n cancelAtPeriodEnd: boolean\n trialEndsAt: number | null\n isLoading: boolean\n error: string | null\n /**\n * Returns true only when `tier === slug` AND the subscription is currently\n * entitled (status ∈ {active, trialing}). A `past_due` / `canceled` /\n * `unpaid` subscription that still has a stored paid tier returns false.\n * The free tier is always entitled.\n */\n hasTier: (slug: string) => boolean\n /**\n * Returns true when the tier's rank meets-or-exceeds `slug` AND the\n * subscription is currently entitled. Same entitlement gate as `hasTier`.\n */\n isAtLeast: (slug: string) => boolean\n /** Plan catalog from /me — pass into `<PricingTable plans={...}>` directly. */\n plans: PlanInfo[]\n subscribe: (planSlug: string, opts?: SubscribeOpts) => Promise<SubscribeResult>\n openPortal: (returnUrl?: string) => Promise<{ url: string }>\n refresh: () => Promise<void>\n}\n\nasync function authedJson<T>(path: string, init?: RequestInit): Promise<T> {\n const token = await getAuthToken()\n const res = await fetch(path, {\n ...init,\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...init?.headers,\n },\n })\n const data = (await res.json().catch(() => ({}))) as Record<string, unknown>\n if (!res.ok) {\n throw new Error((data.error as string) ?? `Request failed (${res.status})`)\n }\n return data as T\n}\n\nexport function useSubscription(): UseSubscriptionReturn {\n const [state, setState] = useState<SubscriptionState>({\n tier: 'free',\n status: 'none',\n interval: null,\n currentPeriodEnd: null,\n cancelAtPeriodEnd: false,\n trialEndsAt: null,\n plans: [],\n })\n const [isLoading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n\n const refresh = useCallback(async () => {\n setLoading(true)\n try {\n const r = await authedJson<SubscriptionState>('/_deepspace/subscriptions/me')\n setState({\n tier: r.tier,\n status: r.status,\n interval: r.interval ?? null,\n currentPeriodEnd: r.currentPeriodEnd,\n cancelAtPeriodEnd: r.cancelAtPeriodEnd,\n trialEndsAt: r.trialEndsAt,\n plans: r.plans ?? [],\n })\n setError(null)\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to load subscription')\n } finally {\n setLoading(false)\n }\n }, [])\n\n useEffect(() => {\n refresh()\n }, [refresh])\n\n const entitled = isEntitled(state.tier, state.status)\n const hasTier = useCallback(\n (slug: string) => state.tier === slug && isEntitled(state.tier, state.status),\n [state.tier, state.status],\n )\n const isAtLeast = useCallback(\n (slug: string) => {\n if (!isEntitled(state.tier, state.status)) return false\n const targetRank = state.plans.find((p) => p.slug === slug)?.rank\n const currentRank = state.plans.find((p) => p.slug === state.tier)?.rank\n if (targetRank == null || currentRank == null) return false\n return currentRank >= targetRank\n },\n [state.plans, state.tier, state.status],\n )\n\n const subscribe = useCallback(\n async (planSlug: string, opts?: SubscribeOpts): Promise<SubscribeResult> => {\n const result = await authedJson<SubscribeResult>('/_deepspace/subscriptions/checkout', {\n method: 'POST',\n body: JSON.stringify({\n planSlug,\n interval: opts?.interval ?? 'month',\n returnUrl: opts?.returnUrl ?? window.location.href,\n cancelUrl: opts?.cancelUrl ?? window.location.href,\n requestNonce: crypto.randomUUID(),\n }),\n })\n if (result.immediate) await refresh()\n // Auto-redirect when the server tells us to navigate. `result.url` is\n // populated in every redirect-bound case: Stripe Checkout session URL\n // (new sub), returnUrl (immediate in-place tier/interval change), and\n // hostedInvoiceUrl (SCA-pending proration). Matches useCheckout's\n // chargeOnce behavior so docs that show `await sub.subscribe('pro')`\n // and expect a redirect actually work without extra plumbing.\n if (typeof window !== 'undefined' && result.url) {\n window.location.href = result.url\n }\n return result\n },\n [refresh],\n )\n\n const openPortal = useCallback(\n async (returnUrl?: string) => {\n return authedJson<{ url: string }>('/_deepspace/subscriptions/portal', {\n method: 'POST',\n body: JSON.stringify({ returnUrl: returnUrl ?? window.location.href }),\n })\n },\n [],\n )\n\n return {\n tier: state.tier,\n status: state.status,\n entitled,\n interval: state.interval,\n currentPeriodEnd: state.currentPeriodEnd,\n cancelAtPeriodEnd: state.cancelAtPeriodEnd,\n trialEndsAt: state.trialEndsAt,\n isLoading,\n error,\n hasTier,\n isAtLeast,\n plans: state.plans,\n subscribe,\n openPortal,\n refresh,\n }\n}\n","/**\n * Subscription primitives shared between client (useSubscription hook) and\n * server (requireSubscription helper). Pulled into a shared module so the\n * entitlement gate has a single source of truth — if it ever diverges between\n * client and server, gated features silently disagree about who's allowed in,\n * which is a security bug, not a UX bug.\n */\n\nexport type SubscriptionStatus =\n | 'none'\n | 'trialing'\n | 'active'\n | 'past_due'\n | 'canceled'\n | 'incomplete'\n | 'incomplete_expired'\n | 'unpaid'\n | 'paused'\n\n/** Per-interval price advertised by a plan. */\nexport interface PlanPrice {\n interval: 'month' | 'year'\n priceCents: number\n currency?: string\n}\n\n/**\n * Plan shape returned by `/api/subscriptions/me` and consumed directly by\n * `<PricingTable plans={sub.plans}>`. Fields beyond slug/rank are optional so\n * a free tier (no prices, no trial) doesn't need to fabricate them.\n */\nexport interface PlanInfo {\n slug: string\n rank: number\n name: string\n trialDays?: number | null\n prices: PlanPrice[]\n}\n\n// Statuses that grant access to paid features. Other statuses (past_due,\n// unpaid, incomplete, paused, canceled) keep the user's recorded planSlug so\n// the UI can surface \"your Pro plan is past-due\" — but they MUST NOT unlock.\n//\n// Typed as `ReadonlySet<string>` (not `Set<SubscriptionStatus>`) so callers\n// passing a server-supplied string don't need a runtime cast; unknown statuses\n// fall through as \"not entitled\", which is the safe default.\nexport const ENTITLED_STATUSES: ReadonlySet<string> = new Set([\n 'active',\n 'trialing',\n])\n\n/**\n * True iff the subscription currently grants access to paid features.\n *\n * - Free tier always counts as entitled — no payment required.\n * - Otherwise the status must be in `ENTITLED_STATUSES`.\n *\n * `status` is typed as `string` deliberately: server responses can carry\n * statuses we haven't enumerated yet (e.g. if Stripe adds a new one), and\n * those should fall through as not-entitled rather than crashing the check.\n */\nexport function isEntitled(tier: string, status: string): boolean {\n if (tier === 'free') return true\n return ENTITLED_STATUSES.has(status)\n}\n","// Minimal pre-fab pricing UI. Stateless: pass in `plans` from\n// `useSubscription().plans` and an `onSelect` callback that calls\n// `useSubscription().subscribe()`. Apps that want their own design can ignore\n// this and roll their own; this is just the \"works out of the box\" path for\n// starter apps.\n\nimport type { PlanInfo, PlanPrice } from '../../shared/subscription'\n\n// Re-export shared types so callers can do\n// `import { PricingTablePlan } from 'deepspace'` without reaching into shared.\nexport type PricingTablePrice = PlanPrice\nexport type PricingTablePlan = PlanInfo\n\nexport function PricingTable(props: {\n plans: PricingTablePlan[]\n interval?: 'month' | 'year'\n currentTier?: string\n onSelect: (planSlug: string, interval: 'month' | 'year') => void | Promise<void>\n}) {\n const interval = props.interval ?? 'month'\n return (\n <div className=\"grid gap-4 md:grid-cols-3\">\n {props.plans.map((plan) => {\n const price = plan.prices.find((p) => p.interval === interval)\n const selected = props.currentTier === plan.slug\n // A plan is truly free only if it has no priced interval at all.\n // If `price` is missing but the plan has prices on OTHER intervals,\n // this interval is unavailable — clicking checkout would fail with\n // `unknown_price`, so we render \"Unavailable\" and disable the button\n // instead of mislabeling it as free.\n const isTrueFree = plan.prices.length === 0\n const otherIntervals = plan.prices\n .map((p) => p.interval)\n .filter((i) => i !== interval)\n const intervalUnavailable = !price && !isTrueFree\n const onlyInterval = otherIntervals.length === 1 ? otherIntervals[0] : null\n const unavailableLabel = onlyInterval\n ? onlyInterval === 'month'\n ? 'Monthly only'\n : 'Yearly only'\n : 'Unavailable'\n const priceLabel = price\n ? `$${(price.priceCents / 100).toFixed(0)}`\n : isTrueFree\n ? 'Free'\n : unavailableLabel\n const disabled = selected || intervalUnavailable\n return (\n <section key={plan.slug} className=\"rounded-lg border border-border p-5\">\n <div className=\"space-y-1\">\n <h3 className=\"text-lg font-semibold\">{plan.name}</h3>\n <p className=\"text-3xl font-semibold\">\n {priceLabel}\n <span className=\"text-sm font-normal text-muted-foreground\">\n {price ? `/${interval}` : ''}\n </span>\n </p>\n {plan.trialDays ? (\n <p className=\"text-xs text-muted-foreground\">{plan.trialDays}-day free trial</p>\n ) : null}\n </div>\n <button\n type=\"button\"\n disabled={disabled}\n onClick={() => void props.onSelect(plan.slug, interval)}\n className=\"mt-5 w-full rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {selected\n ? 'Current plan'\n : intervalUnavailable\n ? unavailableLabel\n : 'Select plan'}\n </button>\n </section>\n )\n })}\n </div>\n )\n}\n","// One-time charge hook. Two modes:\n//\n// 1. Server-declared product (`chargeOnce({ productId })`): the platform\n// resolves amount/name from your declared product catalog. The browser\n// can't pick `productId` + `amount` together — only the declared\n// amount is charged. This is the only entitlement-safe mode:\n// `useCheckout({productId}).owned` answers truthfully.\n//\n// 2. Ad-hoc (`chargeOnce({ amount, name })`): tips, donations, dynamic\n// amounts. The resulting invoice has no `productId` so `ownsProduct()`\n// cannot satisfy gating against it. Use for collection, not unlock.\n//\n// To declare products, POST to `/api/charges/products/sync` from a deploy\n// step or admin tool with `{appName, products: [{productId, name,\n// amountCents}]}`. Products dropped from a later sync are deactivated.\n//\n// The hook fetches the customer's purchase history on mount, caches it in\n// state, and exposes `owned` + `ownsProduct(id)` for gating features behind\n// a one-time purchase the same way `useSubscription().tier` gates behind a\n// recurring plan.\n\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport { getAuthToken } from '../auth/token'\n\ninterface ChargeRedirectOpts {\n /** Defaults to window.location.href. */\n returnUrl?: string\n /** Defaults to window.location.href. */\n cancelUrl?: string\n}\n\n/**\n * Product-mode charge: amount, name, and currency are resolved server-side\n * from the developer's declared product catalog (`src/products.ts`). The\n * browser cannot influence the price — this is the only entitlement-safe\n * shape because `useCheckout({productId}).owned` can be trusted afterwards.\n */\nexport interface ProductChargeOpts extends ChargeRedirectOpts {\n productId: string\n}\n\n/**\n * Ad-hoc charge: tips, donations, \"name your price\". Caller supplies the\n * amount and display name; the resulting invoice has `productId=null`, so\n * `ownsProduct()` will never report ownership against it. Use for revenue\n * collection, not for feature gating.\n */\nexport interface AdHocChargeOpts extends ChargeRedirectOpts {\n /** Amount in cents. Minimum 100 (Stripe fees eat smaller amounts). */\n amount: number\n /** Product name shown in Checkout and on the customer's receipt. */\n name: string\n /** Optional description rendered under the product name. */\n description?: string\n /** Must be omitted in ad-hoc mode — the never type makes the union disjoint. */\n productId?: never\n}\n\n/**\n * `chargeOnce` accepts either a `productId` (server-resolves price/name) or\n * an `amount`+`name` pair (ad-hoc). The two are mutually exclusive at the\n * type level so the safe path doesn't require dummy amount/name fields.\n */\nexport type ChargeOnceOpts = ProductChargeOpts | AdHocChargeOpts\n\nexport interface ChargeOnceResult {\n url: string\n}\n\nexport interface Purchase {\n /** Internal invoice row id. Stable across refetches. */\n id: string\n /** Developer-supplied product id, or null for pre-entitlement rows. */\n productId: string | null\n /** Display name captured at checkout time. */\n name: string\n /** Amount paid in cents (post-tax — uses Stripe's `amount_total`). */\n amount: number\n currency: string\n /** ISO timestamp of the payment. */\n paidAt: string\n}\n\nexport interface UseCheckoutOpts {\n /**\n * Product id to derive `owned` against. Without this, `owned` is always\n * false — the hook still surfaces `purchases` and `ownsProduct` for\n * callers that need to ask about more than one product.\n */\n productId?: string\n}\n\ninterface UseCheckoutReturn {\n // Action surface\n chargeOnce: (opts: ChargeOnceOpts) => Promise<ChargeOnceResult>\n isLoading: boolean\n error: string | null\n\n // Read surface\n purchases: Purchase[]\n /** True iff `opts.productId` was provided and a non-fully-refunded row matches. */\n owned: boolean\n /** Pure function over the in-memory `purchases` array. No network. */\n ownsProduct: (productId: string) => boolean\n /** Re-fetch the purchase list. Useful right after a successful checkout. */\n refresh: () => Promise<void>\n}\n\nasync function authedJson<T>(path: string, init?: RequestInit): Promise<T> {\n const token = await getAuthToken()\n const res = await fetch(path, {\n ...init,\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...init?.headers,\n },\n })\n const data = (await res.json().catch(() => ({}))) as Record<string, unknown>\n if (!res.ok) {\n throw new Error((data.error as string) ?? `Request failed (${res.status})`)\n }\n return data as T\n}\n\nexport function useCheckout(opts?: UseCheckoutOpts): UseCheckoutReturn {\n const [purchases, setPurchases] = useState<Purchase[]>([])\n const [isLoading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n\n // No productId param on the fetch — the hook caches the full list and the\n // per-product question is answered in-memory. Cheaper than refetching on\n // every productId change and lets one component check ownership of N\n // products without N requests.\n const refresh = useCallback(async () => {\n setLoading(true)\n try {\n const r = await authedJson<{ purchases: Purchase[] }>(\n '/_deepspace/charges/me',\n )\n setPurchases(r.purchases ?? [])\n setError(null)\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to load purchases')\n } finally {\n setLoading(false)\n }\n }, [])\n\n useEffect(() => {\n refresh()\n }, [refresh])\n\n const chargeOnce = useCallback(\n async (chargeOpts: ChargeOnceOpts): Promise<ChargeOnceResult> => {\n // Note: isLoading intentionally not toggled here — it tracks the\n // fetch lifecycle (initial + refresh), not the chargeOnce redirect.\n // Mirrors the v1 semantics callers already depend on.\n setError(null)\n\n // Build the request body by mode. In product mode we deliberately\n // omit amount/name from the wire — the server resolves them from the\n // declared catalog and would ignore client values anyway, but sending\n // them keeps the dangerous \"browser picks the price\" shape on the\n // wire. In ad-hoc mode we send what the caller supplied.\n const common = {\n returnUrl: chargeOpts.returnUrl ?? window.location.href,\n cancelUrl: chargeOpts.cancelUrl ?? window.location.href,\n requestNonce: crypto.randomUUID(),\n }\n const body =\n chargeOpts.productId !== undefined\n ? { ...common, productId: chargeOpts.productId }\n : {\n ...common,\n amount: chargeOpts.amount,\n name: chargeOpts.name,\n ...(chargeOpts.description ? { description: chargeOpts.description } : {}),\n }\n\n try {\n const result = await authedJson<{ url: string }>(\n '/_deepspace/charges/create',\n {\n method: 'POST',\n body: JSON.stringify(body),\n },\n )\n if (typeof window !== 'undefined' && result.url) {\n window.location.href = result.url\n }\n return result\n } catch (e) {\n const msg = e instanceof Error ? e.message : 'Failed to start checkout'\n setError(msg)\n throw e\n }\n },\n [],\n )\n\n const ownsProduct = useCallback(\n (productId: string) => purchases.some((p) => p.productId === productId),\n [purchases],\n )\n\n // `owned` is the convenience binding for the single-product case. Vacuously\n // false when no productId was passed to the hook — the caller didn't ask a\n // question, so the answer is \"no entitlement\".\n const owned = useMemo(\n () => !!opts?.productId && purchases.some((p) => p.productId === opts.productId),\n [opts?.productId, purchases],\n )\n\n return {\n chargeOnce,\n isLoading,\n error,\n purchases,\n owned,\n ownsProduct,\n refresh,\n }\n}\n"],"mappings":"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","names":["Fragment","jsx","jsxs","useEffect","useRef","Fragment","jsx","useRef","useEffect","useEffect","useState","Fragment","jsx","jsxs","useState","useEffect","createContext","useContext","useEffect","useState","useCallback","useRef","useMemo","useRef","jsx","Fragment","jsx","createContext","useContext","useState","useRef","useCallback","useEffect","useMemo","useEffect","useState","useCallback","useRef","useMemo","Fragment","jsx","jsxs","useRef","useState","useEffect","useCallback","useMemo","useMemo","useMemo","useEffect","useCallback","useRef","useMemo","useContext","useContext","useMemo","useRef","useEffect","useCallback","useCallback","useContext","useMemo","useContext","useCallback","useMemo","useEffect","useMemo","useEffect","useMemo","useState","useEffect","useCallback","useRef","Y","useState","useRef","useEffect","msgDocKey","payload","useCallback","useMemo","useCallback","useMemo","useCallback","useEffect","useCallback","useState","useState","useEffect","useCallback","u","useState","useEffect","useCallback","useRef","useMemo","Y","useState","useRef","useMemo","useEffect","useCallback","useState","useEffect","useCallback","useRef","useState","useRef","useEffect","useCallback","useState","useEffect","useCallback","useRef","useState","useRef","useEffect","useCallback","useState","useEffect","useCallback","useRef","useState","useRef","useEffect","useCallback","useState","useEffect","useCallback","useRef","useState","useRef","useEffect","useCallback","useState","useEffect","useCallback","useRef","useState","useRef","useEffect","useCallback","useCallback","useState","useMemo","useState","useMemo","useCallback","useCallback","useMemo","useMemo","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useMemo","useCallback","useMemo","useCallback","useMemo","useRef","useRef","useMemo","useCallback","useCallback","useMemo","useMemo","useCallback","useCallback","useCallback","useEffect","r","g","b","Fragment","jsx","useEffect","useState","useEffect","createContext","useContext","useState","useEffect","useRef","useCallback","jsx","createContext","useContext","useEffect","useState","useRef","useCallback","useState","useEffect","useRef","useRef","useState","useEffect","DEFAULT_TIMEOUT_MS","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect","useCallback","useEffect","useMemo","useRef","useState","useState","useRef","useCallback","useEffect","useMemo","useCallback","useEffect","useState","useState","useCallback","useEffect","jsx","jsxs","useCallback","useEffect","useMemo","useState","authedJson","useState","useCallback","useEffect","useMemo"]}
|
|
1
|
+
{"version":3,"sources":["../src/shared/env/index.ts","../src/shared/roles.ts","../src/shared/protocol/yjs.ts","../src/shared/protocol/constants.ts","../src/shared/protocol/messages.ts","../src/client/auth/DeepSpaceAuthProvider.tsx","../src/client/auth/client.ts","../src/client/auth/token.ts","../src/client/debug.ts","../src/client/auth/hooks.ts","../src/client/auth/AuthOverlay.tsx","../src/client/auth/helpers.tsx","../src/client/auth/GuestBanner.tsx","../src/client/storage/context.tsx","../src/client/storage/store.ts","../src/client/storage/record-matching.ts","../src/client/storage/serverErrors.ts","../src/client/storage/record-socket.ts","../src/client/storage/ScopeRegistry.tsx","../src/client/storage/RecordScope.tsx","../src/client/storage/ws-log.ts","../src/client/storage/hooks/useUser.ts","../src/client/storage/hooks/useQuery.ts","../src/client/storage/hooks/useMutations.ts","../src/client/storage/hooks/useUsers.ts","../src/client/storage/hooks/useYjs.ts","../src/client/storage/hooks/useUserLookup.ts","../src/client/storage/hooks/usePresence.ts","../src/client/storage/hooks/useYjsRoom.ts","../src/client/storage/hooks/useGameRoom.ts","../src/client/storage/hooks/useCanvas.ts","../src/client/storage/hooks/useCronMonitor.ts","../src/client/storage/hooks/useJobs.ts","../src/client/storage/hooks/usePresenceRoom.ts","../src/client/storage/connection-status.ts","../src/client/storage/useR2Files.ts","../src/client/storage/file-attachment-utils.ts","../src/client/storage/user-color.ts","../src/client/messaging/useMessages.ts","../src/client/messaging/useChannels.ts","../src/client/messaging/useReactions.ts","../src/client/messaging/useChannelMembers.ts","../src/client/messaging/useReadReceipts.ts","../src/client/messaging/useConversation.ts","../src/client/messaging/utils.ts","../src/client/messaging/conversation-utils.ts","../src/client/messaging/message-utils.ts","../src/client/directory/useConversations.ts","../src/client/directory/useCommunities.ts","../src/client/directory/usePosts.ts","../src/client/theme/DeepSpaceThemeProvider.tsx","../src/client/theme/applyTheme.ts","../src/client/theme/useIsDarkTheme.ts","../src/client/platform/PlatformProvider.tsx","../src/client/platform/usePlatformWS.ts","../src/client/integration/index.ts","../src/client/voice/useVoiceAgent.ts","../src/client/ai-stream.ts","../src/client/status.ts","../src/client/status-core.ts","../src/client/subscriptions/useSubscription.ts","../src/shared/subscription.ts","../src/client/subscriptions/PricingTable.tsx","../src/client/charges/useCheckout.ts"],"sourcesContent":["/**\n * Environment detection and URL configuration\n *\n * Determines environment and provides URLs for DeepSpace services.\n *\n * Environment detection priority:\n * 1. Build-time __DEEPSPACE_ENV__ define (set via esbuild/Vite)\n * 2. Runtime window.__DEEPSPACE_ENV__\n * 3. Server-side process.env.DEEPSPACE_ENV\n * 4. Hostname detection (fallback)\n */\n\nexport type Environment = 'dev' | 'prod'\n\ndeclare const __DEEPSPACE_ENV__: string | undefined\n\n// ============================================================================\n// Environment Configuration\n// ============================================================================\n\ninterface EnvironmentConfig {\n name: Environment\n /** Platform API worker URL */\n apiUrl: string\n /** Platform worker URL (RecordRoom, schema registry) */\n platformWorkerUrl: string\n /** Auth worker URL (Better Auth) */\n authUrl: string\n /** Auth sign-in page URL */\n authSignInUrl: string\n /** Auth sign-up page URL */\n authSignUpUrl: string\n /** Main DeepSpace app URL */\n mainAppUrl: string\n /** Builder dashboard URL */\n dashboardUrl: string\n}\n\nconst DEV_CONFIG: EnvironmentConfig = {\n name: 'dev',\n apiUrl: 'http://localhost:8795',\n platformWorkerUrl: 'http://localhost:8792',\n authUrl: 'http://localhost:8794',\n authSignInUrl: 'http://localhost:5173/sign-in',\n authSignUpUrl: 'http://localhost:5173/sign-up',\n mainAppUrl: 'http://localhost:5173',\n dashboardUrl: 'http://localhost:5174',\n}\n\nconst PROD_CONFIG: EnvironmentConfig = {\n name: 'prod',\n apiUrl: 'https://api-worker.deep.space',\n platformWorkerUrl: 'https://platform-worker.deep.space',\n authUrl: 'https://auth.deep.space',\n authSignInUrl: 'https://auth.deep.space/login/social',\n authSignUpUrl: 'https://auth.deep.space/login/social',\n mainAppUrl: 'https://deep.space',\n dashboardUrl: 'https://dashboard.deep.space',\n}\n\n// ============================================================================\n// Environment Detection\n// ============================================================================\n\nlet cachedEnvironment: Environment | null = null\n\nfunction parseEnv(value: string | undefined): Environment | null {\n if (!value) return null\n const v = value.toLowerCase()\n if (v === 'dev' || v === 'development') return 'dev'\n if (v === 'prod' || v === 'production') return 'prod'\n return null\n}\n\nexport function detectEnvironment(): Environment {\n if (cachedEnvironment) return cachedEnvironment\n\n // 1. Build-time define\n if (typeof __DEEPSPACE_ENV__ !== 'undefined') {\n const env = parseEnv(__DEEPSPACE_ENV__)\n if (env) {\n cachedEnvironment = env\n return env\n }\n }\n\n // 2. Runtime window variable\n if (typeof window !== 'undefined') {\n const windowEnv = (window as { __DEEPSPACE_ENV__?: string }).__DEEPSPACE_ENV__\n const env = parseEnv(windowEnv)\n if (env) {\n cachedEnvironment = env\n return env\n }\n }\n\n // 3. Server-side env var\n if (typeof window === 'undefined') {\n try {\n const env = parseEnv(process.env.DEEPSPACE_ENV)\n if (env) {\n cachedEnvironment = env\n return env\n }\n if (process.env.NODE_ENV !== 'production') {\n cachedEnvironment = 'dev'\n return 'dev'\n }\n } catch {\n // process may not exist in Workers\n }\n }\n\n // 4. Hostname detection\n if (typeof window !== 'undefined') {\n const hostname = window.location.hostname\n\n if (\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname.startsWith('192.168.')\n ) {\n cachedEnvironment = 'dev'\n return 'dev'\n }\n\n // Production: app.space, *.app.space, deep.space, *.deep.space\n if (\n hostname === 'app.space' ||\n hostname.endsWith('.app.space') ||\n hostname === 'deep.space' ||\n hostname.endsWith('.deep.space')\n ) {\n cachedEnvironment = 'prod'\n return 'prod'\n }\n }\n\n // Default to prod for unknown domains\n cachedEnvironment = 'prod'\n return 'prod'\n}\n\nexport function getEnvironmentConfig(): EnvironmentConfig {\n const env = detectEnvironment()\n return env === 'dev' ? DEV_CONFIG : PROD_CONFIG\n}\n\n// ============================================================================\n// Convenience Getters\n// ============================================================================\n\nexport function getApiUrl(): string {\n return getEnvironmentConfig().apiUrl\n}\n\nexport function getPlatformWorkerUrl(): string {\n return getEnvironmentConfig().platformWorkerUrl\n}\n\nexport function getAuthUrl(): string {\n return getEnvironmentConfig().authUrl\n}\n\nexport function isLocalDev(): boolean {\n return detectEnvironment() === 'dev'\n}\n\nexport function isProduction(): boolean {\n return detectEnvironment() === 'prod'\n}\n\n/** Reset cached environment (useful for testing) */\nexport function resetEnvironmentCache(): void {\n cachedEnvironment = null\n}\n\n// ============================================================================\n// Convenience Object\n// ============================================================================\n\nexport const ENV = {\n get current() {\n return detectEnvironment()\n },\n get config() {\n return getEnvironmentConfig()\n },\n get apiUrl() {\n return getApiUrl()\n },\n get platformWorkerUrl() {\n return getPlatformWorkerUrl()\n },\n get authUrl() {\n return getAuthUrl()\n },\n get isLocal() {\n return isLocalDev()\n },\n get isProd() {\n return isProduction()\n },\n} as const\n","/**\n * Standard DeepSpace role constants.\n *\n * Every DeepSpace app uses the same three roles.\n * Apps can import these instead of defining them locally.\n */\n\nexport const ROLES = {\n VIEWER: 'viewer',\n MEMBER: 'member',\n ADMIN: 'admin',\n} as const\n\nexport type Role = (typeof ROLES)[keyof typeof ROLES]\n\nexport const ROLE_CONFIG: Record<Role, { title: string; badgeVariant: string; description: string }> = {\n viewer: { title: 'Viewer', badgeVariant: 'secondary', description: 'Read-only access' },\n member: { title: 'Member', badgeVariant: 'default', description: 'Can create and edit own content' },\n admin: { title: 'Admin', badgeVariant: 'warning', description: 'Full access to all features' },\n}\n","/**\n * Yjs Sync Protocol Implementation\n * \n * This implements the y-protocols sync and awareness protocols without\n * depending on y-websocket internal paths. Makes it easier to bundle\n * and template.\n * \n * Protocol spec: https://github.com/yjs/y-protocols\n */\n\nimport * as Y from 'yjs'\n\n// Message type constants\nexport const MSG_SYNC = 0\nexport const MSG_AWARENESS = 1\n\n// Sync message subtypes\nexport const MSG_SYNC_STEP1 = 0 // Send state vector\nexport const MSG_SYNC_STEP2 = 1 // Send diff based on received state vector\nexport const MSG_SYNC_UPDATE = 2 // Send incremental update\n\n// ============================================================================\n// Binary Encoding Utilities (lib0 compatible)\n// ============================================================================\n\nexport function createEncoder(): { data: number[] } {\n return { data: [] }\n}\n\nexport function toUint8Array(encoder: { data: number[] }): Uint8Array {\n return new Uint8Array(encoder.data)\n}\n\nexport function writeVarUint(encoder: { data: number[] }, num: number): void {\n while (num > 0x7f) {\n encoder.data.push((num & 0x7f) | 0x80)\n num = Math.floor(num / 128)\n }\n encoder.data.push(num & 0x7f)\n}\n\nexport function writeVarUint8Array(encoder: { data: number[] }, arr: Uint8Array): void {\n writeVarUint(encoder, arr.length)\n for (let i = 0; i < arr.length; i++) {\n encoder.data.push(arr[i])\n }\n}\n\nexport function createDecoder(data: Uint8Array): { data: Uint8Array; pos: number } {\n return { data, pos: 0 }\n}\n\nexport function readVarUint(decoder: { data: Uint8Array; pos: number }): number {\n let num = 0\n let mult = 1\n const len = decoder.data.length\n while (decoder.pos < len) {\n const byte = decoder.data[decoder.pos++]\n num += (byte & 0x7f) * mult\n mult *= 128\n if (byte < 0x80) {\n return num\n }\n }\n throw new Error('Unexpected end of buffer')\n}\n\nexport function readVarUint8Array(decoder: { data: Uint8Array; pos: number }): Uint8Array {\n const len = readVarUint(decoder)\n const arr = decoder.data.subarray(decoder.pos, decoder.pos + len)\n decoder.pos += len\n return arr\n}\n\nexport function hasContent(decoder: { data: Uint8Array; pos: number }): boolean {\n return decoder.pos < decoder.data.length\n}\n\n// ============================================================================\n// Sync Protocol\n// ============================================================================\n\n/**\n * Encode sync step 1: our state vector\n */\nexport function encodeSyncStep1(doc: Y.Doc): Uint8Array {\n const encoder = createEncoder()\n writeVarUint(encoder, MSG_SYNC)\n writeVarUint(encoder, MSG_SYNC_STEP1)\n writeVarUint8Array(encoder, Y.encodeStateVector(doc))\n return toUint8Array(encoder)\n}\n\n/**\n * Encode sync step 2: diff based on received state vector\n */\nexport function encodeSyncStep2(doc: Y.Doc, stateVector: Uint8Array): Uint8Array {\n const encoder = createEncoder()\n writeVarUint(encoder, MSG_SYNC)\n writeVarUint(encoder, MSG_SYNC_STEP2)\n writeVarUint8Array(encoder, Y.encodeStateAsUpdate(doc, stateVector))\n return toUint8Array(encoder)\n}\n\n/**\n * Encode an incremental update\n */\nexport function encodeUpdate(update: Uint8Array): Uint8Array {\n const encoder = createEncoder()\n writeVarUint(encoder, MSG_SYNC)\n writeVarUint(encoder, MSG_SYNC_UPDATE)\n writeVarUint8Array(encoder, update)\n return toUint8Array(encoder)\n}\n\nexport interface SyncResult {\n type: 'step1' | 'step2' | 'update'\n response?: Uint8Array\n stateVector?: Uint8Array\n update?: Uint8Array\n}\n\n/**\n * Handle an incoming sync message\n * Returns response to send (if any) and whether this was an update\n */\nexport function handleSyncMessage(doc: Y.Doc, data: Uint8Array): SyncResult {\n const decoder = createDecoder(data)\n const messageType = readVarUint(decoder)\n \n if (messageType !== MSG_SYNC) {\n throw new Error(`Expected sync message (${MSG_SYNC}), got ${messageType}`)\n }\n \n const syncType = readVarUint(decoder)\n \n switch (syncType) {\n case MSG_SYNC_STEP1: {\n // Received state vector, send our diff\n const stateVector = readVarUint8Array(decoder)\n const response = encodeSyncStep2(doc, stateVector)\n return { type: 'step1', response, stateVector }\n }\n \n case MSG_SYNC_STEP2: {\n // Received diff, apply it\n const update = readVarUint8Array(decoder)\n Y.applyUpdate(doc, update)\n return { type: 'step2', update }\n }\n \n case MSG_SYNC_UPDATE: {\n // Received incremental update, apply it\n const update = readVarUint8Array(decoder)\n Y.applyUpdate(doc, update)\n return { type: 'update', update }\n }\n \n default:\n throw new Error(`Unknown sync type: ${syncType}`)\n }\n}\n\n// ============================================================================\n// Awareness Protocol\n// ============================================================================\n\nexport interface AwarenessState {\n [key: string]: unknown\n}\n\nexport interface AwarenessStates {\n [clientId: number]: AwarenessState\n}\n\n/**\n * Simple awareness implementation for presence/cursors\n */\nexport class Awareness {\n doc: Y.Doc\n clientID: number\n states: Map<number, AwarenessState> = new Map()\n private meta: Map<number, { clock: number; lastUpdated: number }> = new Map()\n private updateListeners: Set<(changes: { added: number[]; updated: number[]; removed: number[] }) => void> = new Set()\n private changeListeners: Set<(changes: { added: number[]; updated: number[]; removed: number[] }) => void> = new Set()\n\n constructor(doc: Y.Doc) {\n this.doc = doc\n this.clientID = doc.clientID\n }\n \n getStates(): Map<number, AwarenessState> {\n return this.states\n }\n \n getLocalState(): AwarenessState | null {\n return this.states.get(this.clientID) || null\n }\n \n setLocalState(state: AwarenessState | null): void {\n const clientID = this.clientID\n const prevState = this.states.get(clientID)\n \n if (state === null) {\n this.states.delete(clientID)\n this.meta.delete(clientID)\n if (prevState) {\n this.emit({ added: [], updated: [], removed: [clientID] })\n }\n } else {\n const meta = this.meta.get(clientID) || { clock: 0, lastUpdated: Date.now() }\n meta.clock++\n meta.lastUpdated = Date.now()\n this.meta.set(clientID, meta)\n this.states.set(clientID, state)\n this.emit({\n added: prevState ? [] : [clientID],\n updated: prevState ? [clientID] : [],\n removed: [],\n })\n }\n }\n \n setLocalStateField(field: string, value: unknown): void {\n const state = this.getLocalState() || {}\n state[field] = value\n this.setLocalState(state)\n }\n \n on(event: 'update' | 'change', handler: (changes: { added: number[]; updated: number[]; removed: number[] }) => void): void {\n if (event === 'update') {\n this.updateListeners.add(handler)\n } else if (event === 'change') {\n this.changeListeners.add(handler)\n }\n }\n\n off(event: 'update' | 'change', handler: (changes: { added: number[]; updated: number[]; removed: number[] }) => void): void {\n if (event === 'update') {\n this.updateListeners.delete(handler)\n } else if (event === 'change') {\n this.changeListeners.delete(handler)\n }\n }\n\n private emit(changes: { added: number[]; updated: number[]; removed: number[] }): void {\n for (const handler of this.updateListeners) {\n handler(changes)\n }\n if (changes.added.length > 0 || changes.updated.length > 0 || changes.removed.length > 0) {\n for (const handler of this.changeListeners) {\n handler(changes)\n }\n }\n }\n \n /**\n * Encode awareness update for specific clients\n */\n encodeUpdate(clientIds?: number[]): Uint8Array {\n const ids = clientIds || Array.from(this.states.keys())\n const encoder = createEncoder()\n writeVarUint(encoder, ids.length)\n \n for (const clientId of ids) {\n const state = this.states.get(clientId)\n const meta = this.meta.get(clientId) || { clock: 0, lastUpdated: Date.now() }\n \n writeVarUint(encoder, clientId)\n writeVarUint(encoder, meta.clock)\n \n const stateStr = state ? JSON.stringify(state) : 'null'\n const stateBytes = new TextEncoder().encode(stateStr)\n writeVarUint8Array(encoder, stateBytes)\n }\n \n return toUint8Array(encoder)\n }\n \n /**\n * Apply awareness update from another client\n */\n applyUpdate(data: Uint8Array): { added: number[]; updated: number[]; removed: number[] } {\n const decoder = createDecoder(data)\n const len = readVarUint(decoder)\n \n const added: number[] = []\n const updated: number[] = []\n const removed: number[] = []\n \n for (let i = 0; i < len; i++) {\n const clientId = readVarUint(decoder)\n const clock = readVarUint(decoder)\n const stateBytes = readVarUint8Array(decoder)\n const stateStr = new TextDecoder().decode(stateBytes)\n const state = stateStr === 'null' ? null : JSON.parse(stateStr) as AwarenessState\n \n const prevMeta = this.meta.get(clientId)\n const prevState = this.states.get(clientId)\n \n // Only apply if this is a newer update\n if (!prevMeta || prevMeta.clock < clock) {\n this.meta.set(clientId, { clock, lastUpdated: Date.now() })\n \n if (state === null) {\n this.states.delete(clientId)\n if (prevState) {\n removed.push(clientId)\n }\n } else {\n this.states.set(clientId, state)\n if (prevState) {\n updated.push(clientId)\n } else {\n added.push(clientId)\n }\n }\n }\n }\n \n if (added.length > 0 || updated.length > 0 || removed.length > 0) {\n this.emit({ added, updated, removed })\n }\n \n return { added, updated, removed }\n }\n \n /**\n * Remove awareness states for disconnected clients\n */\n removeStates(clientIds: number[]): void {\n const removed: number[] = []\n for (const clientId of clientIds) {\n if (this.states.has(clientId)) {\n this.states.delete(clientId)\n this.meta.delete(clientId)\n removed.push(clientId)\n }\n }\n if (removed.length > 0) {\n this.emit({ added: [], updated: [], removed })\n }\n }\n}\n\n/**\n * Encode a full awareness message\n */\nexport function encodeAwarenessMessage(awareness: Awareness, clientIds?: number[]): Uint8Array {\n const encoder = createEncoder()\n writeVarUint(encoder, MSG_AWARENESS)\n writeVarUint8Array(encoder, awareness.encodeUpdate(clientIds))\n return toUint8Array(encoder)\n}\n\n/**\n * Decode and apply awareness message\n */\nexport function handleAwarenessMessage(\n awareness: Awareness, \n data: Uint8Array\n): { added: number[]; updated: number[]; removed: number[] } {\n const decoder = createDecoder(data)\n const messageType = readVarUint(decoder)\n \n if (messageType !== MSG_AWARENESS) {\n throw new Error(`Expected awareness message (${MSG_AWARENESS}), got ${messageType}`)\n }\n \n const update = readVarUint8Array(decoder)\n return awareness.applyUpdate(update)\n}\n\n/**\n * Get message type from raw data\n */\nexport function getMessageType(data: Uint8Array): number {\n const decoder = createDecoder(data)\n return readVarUint(decoder)\n}\n\n","/**\n * Wire protocol constants.\n *\n * The JSON WebSocket protocol uses dotted string identifiers (e.g.\n * `\"game.input\"`) as the `type` discriminator. All message types are\n * grouped under a single `MSG` object so imports stay tidy:\n *\n * import { MSG, dispatch, clientBuild } from 'deepspace'\n *\n * dispatch<ServerMessage>(raw, {\n * [MSG.GAME_STATE]: (p) => { ... },\n * [MSG.GAME_TICK]: (p) => { ... },\n * })\n *\n * Each key's value is the on-wire string — grep-friendly, self-\n * documenting, and infinite per namespace. Adding a new message is one\n * line here plus one arm in the discriminated union in `./messages.ts`.\n *\n * Yjs binary protocol constants (`MSG_YJS_SYNC`, `MSG_YJS_AWARENESS`) are\n * intentionally kept numeric and separate from `MSG` — they ride a\n * binary WebSocket frame format and aren't part of the JSON dispatcher.\n */\n\nexport const MSG = {\n // ---- Records / CRUD --------------------------------------------------\n SUBSCRIBE: 'core.subscribe',\n UNSUBSCRIBE: 'core.unsubscribe',\n QUERY_RESULT: 'core.query_result',\n RECORD_CHANGE: 'core.record_change',\n PUT: 'core.put',\n DELETE: 'core.delete',\n ERROR: 'core.error',\n\n // ---- Users -----------------------------------------------------------\n USER_INFO: 'user.info',\n USER_LIST: 'user.list',\n SET_ROLE: 'user.set_role',\n USER_UPDATE: 'user.update',\n\n // ---- Auth signal -----------------------------------------------------\n // Server → client one-shot on connect from rooms that enforce role-based\n // writes (Canvas, Game, Cron). Tells the client whether this connection\n // can write so its hook can disable local write APIs and the UI can\n // render controls accordingly. Without this, a viewer's click would\n // round-trip to the server and silently come back as ERROR.\n AUTH: 'auth',\n\n // ---- Yjs JSON control frames (binary sync lives in ./yjs.ts) ---------\n YJS_JOIN: 'yjs.join',\n YJS_LEAVE: 'yjs.leave',\n\n // ---- Records room misc -----------------------------------------------\n ACK: 'records.ack',\n LIST_SCHEMAS: 'records.list_schemas',\n RESUBSCRIBE: 'records.resubscribe',\n\n // ---- GameRoom --------------------------------------------------------\n GAME_STATE: 'game.state',\n GAME_INPUT: 'game.input',\n GAME_PLAYER_JOIN: 'game.player_join',\n GAME_PLAYER_LEAVE: 'game.player_leave',\n GAME_PLAYER_READY: 'game.player_ready',\n GAME_START: 'game.start',\n GAME_END: 'game.end',\n GAME_TICK: 'game.tick',\n\n // ---- CanvasRoom ------------------------------------------------------\n CANVAS_SHAPES: 'canvas.shapes',\n CANVAS_ADD: 'canvas.add',\n CANVAS_MOVE: 'canvas.move',\n CANVAS_RESIZE: 'canvas.resize',\n CANVAS_DELETE: 'canvas.delete',\n CANVAS_UPDATE: 'canvas.update',\n CANVAS_VIEWPORT: 'canvas.viewport',\n CANVAS_UNDO: 'canvas.undo',\n CANVAS_REDO: 'canvas.redo',\n\n // ---- CronRoom --------------------------------------------------------\n CRON_TASKS: 'cron.tasks',\n CRON_HISTORY: 'cron.history',\n CRON_TRIGGER: 'cron.trigger',\n CRON_PAUSE: 'cron.pause',\n CRON_RESUME: 'cron.resume',\n CRON_STATUS: 'cron.status',\n\n // ---- JobRoom ---------------------------------------------------------\n // Background-job queue with durable, alarm-driven execution. See\n // server/rooms/job-room.ts. Clients enqueue and subscribe to updates;\n // the room broadcasts JOB_UPDATE for every status transition.\n JOB_ENQUEUE: 'job.enqueue',\n JOB_CANCEL: 'job.cancel',\n JOB_RETRY: 'job.retry',\n JOB_UPDATE: 'job.update',\n\n // ---- PresenceRoom ----------------------------------------------------\n PRESENCE_SYNC: 'presence.sync',\n PRESENCE_JOIN: 'presence.join',\n PRESENCE_LEAVE: 'presence.leave',\n PRESENCE_UPDATE: 'presence.update',\n\n // ---- Gateway multiplexing --------------------------------------------\n GW_SCOPE_CONNECT: 'gateway.scope_connect',\n GW_SCOPE_DISCONNECT: 'gateway.scope_disconnect',\n GW_SCOPE_ERROR: 'gateway.scope_error',\n GW_TOKEN_REFRESH: 'gateway.token_refresh',\n GW_USER_UPDATE: 'gateway.user_update',\n} as const\n\n/**\n * Type of any message type constant — the union of every string literal\n * stored in `MSG`. Useful when declaring functions that accept \"any known\n * message type\" without enumerating all 54 strings by hand.\n */\nexport type MsgType = (typeof MSG)[keyof typeof MSG]\n\n// ---------------------------------------------------------------------------\n// Yjs binary protocol (NOT part of MSG — see header comment)\n// ---------------------------------------------------------------------------\n\n/** Outer envelope id for binary-framed yjs sync messages. Varuint-encoded. */\nexport const MSG_YJS_SYNC = 22\n/** Outer envelope id for binary-framed yjs awareness messages. Varuint-encoded. */\nexport const MSG_YJS_AWARENESS = 23\n\n// ---------------------------------------------------------------------------\n// Role names — not wire messages, just the string values the auth layer uses\n// as role identifiers.\n// ---------------------------------------------------------------------------\n\n/** Role assigned to unauthenticated WebSocket connections */\nexport const ROLE_ANONYMOUS = 'viewer'\n/** Default role for newly registered authenticated users */\nexport const ROLE_DEFAULT = 'member'\n/** Admin role */\nexport const ROLE_ADMIN = 'admin'\n","/**\n * Typed wire-protocol layer — discriminated unions, typed builders, and a\n * type-safe dispatcher for every `MSG.*` the SDK understands.\n *\n * Why this exists\n * ---------------\n *\n * The string `MSG.*` constants in `./constants.ts` are the authoritative\n * wire protocol, but using them directly is error-prone: a typo picks the\n * wrong message with the wrong payload shape and fails silently at\n * runtime. This module pairs every constant with its payload type, so\n * that:\n *\n * 1. Building a message with `clientBuild.gameInput(...)` is payload-\n * checked at the call site — the compiler refuses to ship a wrong\n * shape.\n *\n * 2. Parsing an inbound message via `dispatch(raw, handlers)` narrows the\n * payload type inside each handler automatically, replacing the\n * unsafe `switch (msg.type) { case MSG.X: (payload as any).foo }`\n * pattern.\n *\n * 3. Tightening `BaseRoom.sendTo` / `BaseRoom.broadcast` /\n * `HandlerContext.send` / `SubscriptionContext.send` to accept\n * `ServerMessage` turns the type layer into enforcement: any room\n * that ships a payload inconsistent with its declared arm fails to\n * compile. Without that, the discriminated union is documentation,\n * not contract.\n *\n * 4. Adding a new `MSG.*` is localized: one entry in the discriminated\n * union, one builder function, one handler key in every dispatcher\n * that cares. No grep-and-fix across the codebase.\n *\n * 5. Apps can extend the SDK protocol without forking: `dispatch<M>` is\n * generic over any `M extends ProtocolMessage`, and builders are\n * plain objects so apps compose via spread (`{ ...clientBuild,\n * myMessage: ... }`).\n *\n * Direction split\n * ---------------\n *\n * Some message types carry different payloads depending on who's sending.\n * `MSG.GAME_START`, for example, is `{}` when the client requests a start\n * but `{ state, tick }` when the server broadcasts the start event.\n * `MSG.CANVAS_ADD` is a flat shape dict on the way in and a `{ shape }`\n * wrapper on the way out. Modelling these with one union would force\n * handlers to juggle a union payload — clunky and error-prone. Instead we\n * split by direction:\n *\n * - `ClientMessage` — what the client sends to the server\n * - `ServerMessage` — what the server sends to the client\n * - `ProtocolMessage = ClientMessage | ServerMessage` (for code that\n * really doesn't care — avoid when possible)\n *\n * Each side gets its own builder (`clientBuild` / `serverBuild`) and each\n * side's dispatcher is parameterised with the union it expects.\n *\n * Payload strictness\n * ------------------\n *\n * Where payload shapes are stable + narrow (ids, flags), we type them\n * precisely. Where they're opaque or escape the protocol layer (record\n * data blobs, Yjs binary frames, game-engine state), we use `unknown` and\n * defer narrowing to the caller. This is intentional: over-typing opaque\n * payloads would require the protocol layer to import application types\n * and defeat the \"thin wire contract\" goal.\n */\n\nimport { MSG } from './constants'\n\n// =============================================================================\n// Envelope + helpers\n// =============================================================================\n\n/**\n * The outer shape of every wire message. `T` is the string discriminator\n * (e.g. `\"game.input\"`) — keeping it as a generic literal type lets the\n * discriminated-union narrowing in `dispatch()` pick the right payload.\n *\n * Callers extending the protocol should pass a string-literal type for\n * `T`, not the widened `string`. `BaseMessage<string, P>` collapses the\n * discriminated union and handler-map key inference falls back to a\n * single untyped `string` key, losing all narrowing.\n */\nexport interface BaseMessage<T extends string, P> {\n type: T\n payload: P\n}\n\n/** Matches when a payload is intentionally empty — `{}` on the wire. */\ntype EmptyPayload = Record<string, never>\n\n/**\n * Frozen empty payload reused by every \"no args\" builder. Frozen so a\n * runaway consumer mutating `.payload` can't corrupt the shared instance\n * that every other call site sees.\n */\nconst EMPTY: EmptyPayload = Object.freeze({}) as EmptyPayload\n\n// =============================================================================\n// Client → Server messages\n// =============================================================================\n\n/**\n * Every message the client can legitimately send. Extend in app code with\n * a string-literal union arm:\n *\n * type MyClientMessage = ClientMessage | BaseMessage<'myapp.foo', { x: number }>\n */\nexport type ClientMessage =\n // ---- Records / CRUD ---------------------------------------------------\n | BaseMessage<typeof MSG.SUBSCRIBE, { subscriptionId: string; query: unknown }>\n | BaseMessage<typeof MSG.UNSUBSCRIBE, { subscriptionId: string }>\n | BaseMessage<\n typeof MSG.PUT,\n { collection: string; recordId: string; data: Record<string, unknown>; requestId?: string }\n >\n | BaseMessage<\n typeof MSG.DELETE,\n { collection: string; recordId: string; requestId?: string }\n >\n // ---- Users ------------------------------------------------------------\n | BaseMessage<typeof MSG.USER_LIST, EmptyPayload>\n | BaseMessage<typeof MSG.SET_ROLE, { userId: string; role: string }>\n | BaseMessage<typeof MSG.USER_UPDATE, EmptyPayload>\n | BaseMessage<typeof MSG.LIST_SCHEMAS, EmptyPayload>\n // ---- Yjs --------------------------------------------------------------\n | BaseMessage<typeof MSG.YJS_JOIN, { collection: string; recordId: string; fieldName: string }>\n | BaseMessage<typeof MSG.YJS_LEAVE, { collection: string; recordId: string; fieldName: string }>\n // Binary yjs sync/awareness frames use numeric `MSG_YJS_SYNC` /\n // `MSG_YJS_AWARENESS` envelope ids from `./constants.ts` and ride the\n // binary WebSocket channel. They bypass this typed layer entirely.\n // ---- Game -------------------------------------------------------------\n | BaseMessage<typeof MSG.GAME_INPUT, { action: string; data: Record<string, unknown> }>\n | BaseMessage<typeof MSG.GAME_PLAYER_READY, EmptyPayload>\n | BaseMessage<typeof MSG.GAME_START, EmptyPayload>\n | BaseMessage<typeof MSG.GAME_END, EmptyPayload>\n // ---- Canvas -----------------------------------------------------------\n // `CANVAS_ADD` sends a flat shape dict as the payload (not `{ shape }`);\n // the server wraps it in `{ shape }` on its rebroadcast. Keep the types\n // aligned with the real wire or `clientBuild` lies. See\n // server/rooms/canvas-room.ts for the server side of this asymmetry.\n // `unknown` here (rather than `Record<string, unknown>`) so closed\n // concrete shape types pass assignability without index signatures.\n | BaseMessage<typeof MSG.CANVAS_ADD, unknown>\n | BaseMessage<typeof MSG.CANVAS_MOVE, { shapeId: string; x: number; y: number }>\n | BaseMessage<\n typeof MSG.CANVAS_RESIZE,\n { shapeId: string; width: number; height: number; x?: number; y?: number }\n >\n | BaseMessage<typeof MSG.CANVAS_DELETE, { shapeId: string }>\n | BaseMessage<\n typeof MSG.CANVAS_UPDATE,\n { shapeId: string; props: Record<string, unknown> }\n >\n // Flat payload in the client→server direction, matching `CANVAS_ADD`.\n | BaseMessage<typeof MSG.CANVAS_VIEWPORT, unknown>\n | BaseMessage<typeof MSG.CANVAS_UNDO, EmptyPayload>\n | BaseMessage<typeof MSG.CANVAS_REDO, EmptyPayload>\n // ---- Cron -------------------------------------------------------------\n | BaseMessage<typeof MSG.CRON_TRIGGER, { taskName: string }>\n | BaseMessage<typeof MSG.CRON_PAUSE, { taskName: string }>\n | BaseMessage<typeof MSG.CRON_RESUME, { taskName: string }>\n // ---- Jobs -------------------------------------------------------------\n // `JOB_ENQUEUE` uses requestId/JOB_UPDATE-ack so the caller learns the\n // newly-assigned jobId.\n | BaseMessage<\n typeof MSG.JOB_ENQUEUE,\n {\n requestId: string\n type: string\n payload?: unknown\n maxAttempts?: number\n }\n >\n | BaseMessage<typeof MSG.JOB_CANCEL, { jobId: string }>\n | BaseMessage<typeof MSG.JOB_RETRY, { jobId: string }>\n // ---- Presence ---------------------------------------------------------\n // Flat state object — the server merges incoming fields into the peer's\n // stored state; there's no `{ state }` wrapper in the client direction.\n | BaseMessage<typeof MSG.PRESENCE_UPDATE, Record<string, unknown>>\n // ---- Gateway ----------------------------------------------------------\n | BaseMessage<typeof MSG.GW_SCOPE_CONNECT, { scopeType: string; scopeId: string }>\n | BaseMessage<typeof MSG.GW_SCOPE_DISCONNECT, { scopeType: string; scopeId: string }>\n | BaseMessage<typeof MSG.GW_TOKEN_REFRESH, { token: string }>\n\n// =============================================================================\n// Server → Client messages\n// =============================================================================\n\n/**\n * Every message the server can send. Room and handler `send` / `broadcast`\n * signatures are tightened to this union so outbound payloads are\n * compile-checked against the wire contract. As with `ClientMessage`,\n * extend via a string-literal union arm in app code when adding new\n * server-side broadcasts.\n */\nexport type ServerMessage =\n // ---- Records / CRUD ---------------------------------------------------\n | BaseMessage<\n typeof MSG.QUERY_RESULT,\n { subscriptionId: string; records: unknown[] }\n >\n | BaseMessage<\n typeof MSG.RECORD_CHANGE,\n {\n collection: string\n record: unknown\n // Real wire values are `create`/`update`/`delete`, not the\n // underlying storage op name. See `server/handlers/subscriptions.ts`.\n changeType: 'create' | 'update' | 'delete'\n }\n >\n // `subscriptionId` is optional because generic transport errors (bad\n // envelope, missing connection) have no subscription context. Specific\n // query failures include the id so the client can correlate.\n | BaseMessage<typeof MSG.ERROR, { error: string; subscriptionId?: string }>\n // ACK is a mutation ack — it carries the correlation `requestId` from\n // the inbound PUT/DELETE plus a success flag. Server/handlers/records.ts\n // is the authoritative send site.\n | BaseMessage<\n typeof MSG.ACK,\n | { requestId: string; success: true; recordId?: string }\n | { requestId: string; success: false; error: string }\n >\n | BaseMessage<typeof MSG.RESUBSCRIBE, EmptyPayload>\n | BaseMessage<typeof MSG.LIST_SCHEMAS, { schemas: unknown }>\n // ---- Auth -------------------------------------------------------------\n // Emitted once per connection by rooms that enforce role-based writes\n // (Canvas, Game, Cron). Client hooks store `canWrite` in state and\n // disable write APIs when false. See useCanvas / useGameRoom /\n // useCronMonitor for the consumer side.\n | BaseMessage<typeof MSG.AUTH, { canWrite: boolean }>\n // ---- Users ------------------------------------------------------------\n // `USER_INFO` carries an app-specific user object (column-shape varies\n // per deployment), so the wire type is opaque. Narrow at the consumer.\n | BaseMessage<typeof MSG.USER_INFO, unknown>\n | BaseMessage<typeof MSG.USER_LIST, { users: unknown[] }>\n // ---- Yjs (JSON frames; binary sync/awareness bypass this layer) -------\n | BaseMessage<\n typeof MSG.YJS_JOIN,\n { collection: string; recordId: string; fieldName: string; canWrite: boolean }\n >\n // ---- Game -------------------------------------------------------------\n | BaseMessage<\n typeof MSG.GAME_STATE,\n { state: unknown; tick: number; players: unknown[]; running: boolean }\n >\n | BaseMessage<typeof MSG.GAME_TICK, { state: unknown; tick: number }>\n | BaseMessage<typeof MSG.GAME_START, { state: unknown; tick: number }>\n | BaseMessage<typeof MSG.GAME_END, { state: unknown; tick: number }>\n | BaseMessage<typeof MSG.GAME_PLAYER_JOIN, { player: unknown }>\n | BaseMessage<typeof MSG.GAME_PLAYER_LEAVE, { userId: string }>\n | BaseMessage<typeof MSG.GAME_PLAYER_READY, { userId: string }>\n // ---- Canvas -----------------------------------------------------------\n // `CANVAS_SHAPES` carries both the shape list and the viewports snapshot\n // so new connections can restore a multi-user view in one message. See\n // server/rooms/canvas-room.ts:onConnect.\n | BaseMessage<\n typeof MSG.CANVAS_SHAPES,\n { shapes: unknown[]; viewports: unknown[] }\n >\n | BaseMessage<typeof MSG.CANVAS_ADD, { shape: unknown }>\n | BaseMessage<typeof MSG.CANVAS_MOVE, { shapeId: string; x: number; y: number }>\n | BaseMessage<\n typeof MSG.CANVAS_RESIZE,\n { shapeId: string; width: number; height: number; x?: number; y?: number }\n >\n | BaseMessage<typeof MSG.CANVAS_DELETE, { shapeId: string }>\n | BaseMessage<\n typeof MSG.CANVAS_UPDATE,\n { shapeId: string; props: Record<string, unknown> }\n >\n | BaseMessage<\n typeof MSG.CANVAS_VIEWPORT,\n { viewport: unknown } | { userId: string; removed: true }\n >\n // ---- Cron -------------------------------------------------------------\n // `CRON_STATUS` server broadcasts carry both tasks and recent history;\n // see server/rooms/cron-room.ts.\n | BaseMessage<typeof MSG.CRON_TASKS, { tasks: unknown }>\n | BaseMessage<typeof MSG.CRON_HISTORY, { history: unknown }>\n | BaseMessage<typeof MSG.CRON_STATUS, { tasks: unknown; recentHistory: unknown }>\n // ---- Jobs -------------------------------------------------------------\n // `JOB_UPDATE` is the single push channel — same payload shape carries\n // initial-snapshot, enqueue, progress, success, failure, cancel, and\n // retry events. `kind` discriminates client-side. See useJobs.\n | BaseMessage<\n typeof MSG.JOB_UPDATE,\n {\n kind: 'snapshot' | 'enqueued' | 'progress' | 'succeeded' | 'failed' | 'canceled' | 'retried'\n job?: unknown\n jobs?: unknown[]\n /** Echoes the enqueue requestId when the update is the enqueue ack. */\n requestId?: string\n }\n >\n // ---- Presence ---------------------------------------------------------\n | BaseMessage<typeof MSG.PRESENCE_SYNC, { peers: unknown[] }>\n | BaseMessage<typeof MSG.PRESENCE_JOIN, { peer: unknown }>\n | BaseMessage<typeof MSG.PRESENCE_LEAVE, { userId: string }>\n | BaseMessage<\n typeof MSG.PRESENCE_UPDATE,\n { userId: string; state: Record<string, unknown> }\n >\n // ---- Gateway ----------------------------------------------------------\n | BaseMessage<\n typeof MSG.GW_SCOPE_ERROR,\n { scopeType: string; scopeId: string; error: string }\n >\n | BaseMessage<typeof MSG.GW_USER_UPDATE, unknown>\n\n/**\n * Any wire message, regardless of direction. Use this only when the code\n * path genuinely doesn't care — otherwise pick `ClientMessage` or\n * `ServerMessage` so the handler's payload type is narrower.\n */\nexport type ProtocolMessage = ClientMessage | ServerMessage\n\n// =============================================================================\n// Typed builders\n// =============================================================================\n//\n// Each builder returns a `{ type, payload }` object typed as a specific arm\n// of `ClientMessage` / `ServerMessage`. Call sites that hand the result to\n// `JSON.stringify` ship exactly what the wire protocol expects.\n//\n// `as const` on every return preserves the literal string `type` value\n// (e.g. `\"game.input\"`), which is what the discriminated-union narrowing\n// in `dispatch` keys on. Without it TS widens to `string` and the handler\n// map collapses to a single untyped key.\n// =============================================================================\n\n/** Factories for every client → server message. */\nexport const clientBuild = {\n // Records / CRUD\n subscribe: (subscriptionId: string, query: unknown) =>\n ({ type: MSG.SUBSCRIBE, payload: { subscriptionId, query } }) as const,\n unsubscribe: (subscriptionId: string) =>\n ({ type: MSG.UNSUBSCRIBE, payload: { subscriptionId } }) as const,\n put: (\n collection: string,\n recordId: string,\n data: Record<string, unknown>,\n requestId?: string,\n ) =>\n ({\n type: MSG.PUT,\n payload: { collection, recordId, data, requestId },\n }) as const,\n remove: (collection: string, recordId: string, requestId?: string) =>\n ({ type: MSG.DELETE, payload: { collection, recordId, requestId } }) as const,\n // Users\n userList: () => ({ type: MSG.USER_LIST, payload: EMPTY }) as const,\n setRole: (userId: string, role: string) =>\n ({ type: MSG.SET_ROLE, payload: { userId, role } }) as const,\n userUpdate: () => ({ type: MSG.USER_UPDATE, payload: EMPTY }) as const,\n listSchemas: () => ({ type: MSG.LIST_SCHEMAS, payload: EMPTY }) as const,\n // Yjs\n yjsJoin: (collection: string, recordId: string, fieldName: string) =>\n ({ type: MSG.YJS_JOIN, payload: { collection, recordId, fieldName } }) as const,\n yjsLeave: (collection: string, recordId: string, fieldName: string) =>\n ({ type: MSG.YJS_LEAVE, payload: { collection, recordId, fieldName } }) as const,\n // Game\n gameInput: (action: string, data: Record<string, unknown> = {}) =>\n ({ type: MSG.GAME_INPUT, payload: { action, data } }) as const,\n gamePlayerReady: () => ({ type: MSG.GAME_PLAYER_READY, payload: EMPTY }) as const,\n gameStart: () => ({ type: MSG.GAME_START, payload: EMPTY }) as const,\n gameEnd: () => ({ type: MSG.GAME_END, payload: EMPTY }) as const,\n // Canvas — `CANVAS_ADD` / `CANVAS_VIEWPORT` payloads are flat (the shape\n // or viewport object is the whole payload).\n canvasAdd: (shape: Record<string, unknown>) =>\n ({ type: MSG.CANVAS_ADD, payload: shape }) as const,\n canvasMove: (shapeId: string, x: number, y: number) =>\n ({ type: MSG.CANVAS_MOVE, payload: { shapeId, x, y } }) as const,\n canvasResize: (\n shapeId: string,\n width: number,\n height: number,\n x?: number,\n y?: number,\n ) =>\n ({ type: MSG.CANVAS_RESIZE, payload: { shapeId, width, height, x, y } }) as const,\n canvasDelete: (shapeId: string) =>\n ({ type: MSG.CANVAS_DELETE, payload: { shapeId } }) as const,\n canvasUpdate: (shapeId: string, props: Record<string, unknown>) =>\n ({ type: MSG.CANVAS_UPDATE, payload: { shapeId, props } }) as const,\n canvasViewport: (viewport: Record<string, unknown>) =>\n ({ type: MSG.CANVAS_VIEWPORT, payload: viewport }) as const,\n canvasUndo: () => ({ type: MSG.CANVAS_UNDO, payload: EMPTY }) as const,\n canvasRedo: () => ({ type: MSG.CANVAS_REDO, payload: EMPTY }) as const,\n // Cron\n cronTrigger: (taskName: string) =>\n ({ type: MSG.CRON_TRIGGER, payload: { taskName } }) as const,\n cronPause: (taskName: string) =>\n ({ type: MSG.CRON_PAUSE, payload: { taskName } }) as const,\n cronResume: (taskName: string) =>\n ({ type: MSG.CRON_RESUME, payload: { taskName } }) as const,\n // Jobs\n jobEnqueue: (\n requestId: string,\n type: string,\n payload?: unknown,\n maxAttempts?: number,\n ) =>\n ({\n type: MSG.JOB_ENQUEUE,\n payload: { requestId, type, payload, maxAttempts },\n }) as const,\n jobCancel: (jobId: string) =>\n ({ type: MSG.JOB_CANCEL, payload: { jobId } }) as const,\n jobRetry: (jobId: string) =>\n ({ type: MSG.JOB_RETRY, payload: { jobId } }) as const,\n // Presence\n presenceUpdate: (state: Record<string, unknown>) =>\n ({ type: MSG.PRESENCE_UPDATE, payload: state }) as const,\n // Gateway\n gwScopeConnect: (scopeType: string, scopeId: string) =>\n ({ type: MSG.GW_SCOPE_CONNECT, payload: { scopeType, scopeId } }) as const,\n gwScopeDisconnect: (scopeType: string, scopeId: string) =>\n ({ type: MSG.GW_SCOPE_DISCONNECT, payload: { scopeType, scopeId } }) as const,\n gwTokenRefresh: (token: string) =>\n ({ type: MSG.GW_TOKEN_REFRESH, payload: { token } }) as const,\n}\n\n/** Factories for every server → client message. Used by room DOs and\n * handler modules so server broadcasts are payload-checked too. Opaque\n * slots (`record`, `shape`, `peer`, etc.) take `unknown` so concrete\n * domain types — `RecordResult`, `CanvasShape`, `MediaPeer` — can be\n * passed without casts. */\nexport const serverBuild = {\n // Records / CRUD\n queryResult: (subscriptionId: string, records: unknown[]) =>\n ({ type: MSG.QUERY_RESULT, payload: { subscriptionId, records } }) as const,\n recordChange: (\n collection: string,\n record: unknown,\n changeType: 'create' | 'update' | 'delete',\n ) =>\n ({ type: MSG.RECORD_CHANGE, payload: { collection, record, changeType } }) as const,\n error: (error: string, subscriptionId?: string) =>\n ({ type: MSG.ERROR, payload: { error, subscriptionId } }) as const,\n ackSuccess: (requestId: string, recordId?: string) =>\n ({\n type: MSG.ACK,\n payload: { requestId, success: true as const, recordId },\n }) as const,\n ackFailure: (requestId: string, error: string) =>\n ({\n type: MSG.ACK,\n payload: { requestId, success: false as const, error },\n }) as const,\n resubscribe: () => ({ type: MSG.RESUBSCRIBE, payload: EMPTY }) as const,\n schemas: (schemas: unknown) =>\n ({ type: MSG.LIST_SCHEMAS, payload: { schemas } }) as const,\n // Users\n userInfo: (user: unknown) =>\n ({ type: MSG.USER_INFO, payload: user }) as const,\n userList: (users: unknown[]) =>\n ({ type: MSG.USER_LIST, payload: { users } }) as const,\n // Yjs\n yjsJoin: (collection: string, recordId: string, fieldName: string, canWrite: boolean) =>\n ({\n type: MSG.YJS_JOIN,\n payload: { collection, recordId, fieldName, canWrite },\n }) as const,\n // Game\n gameState: (state: unknown, tick: number, players: unknown[], running: boolean) =>\n ({ type: MSG.GAME_STATE, payload: { state, tick, players, running } }) as const,\n gameTick: (state: unknown, tick: number) =>\n ({ type: MSG.GAME_TICK, payload: { state, tick } }) as const,\n gameStart: (state: unknown, tick: number) =>\n ({ type: MSG.GAME_START, payload: { state, tick } }) as const,\n gameEnd: (state: unknown, tick: number) =>\n ({ type: MSG.GAME_END, payload: { state, tick } }) as const,\n gamePlayerJoin: (player: unknown) =>\n ({ type: MSG.GAME_PLAYER_JOIN, payload: { player } }) as const,\n gamePlayerLeave: (userId: string) =>\n ({ type: MSG.GAME_PLAYER_LEAVE, payload: { userId } }) as const,\n gamePlayerReady: (userId: string) =>\n ({ type: MSG.GAME_PLAYER_READY, payload: { userId } }) as const,\n // Canvas\n canvasShapes: (shapes: unknown[], viewports: unknown[]) =>\n ({ type: MSG.CANVAS_SHAPES, payload: { shapes, viewports } }) as const,\n canvasAdd: (shape: unknown) =>\n ({ type: MSG.CANVAS_ADD, payload: { shape } }) as const,\n canvasMove: (shapeId: string, x: number, y: number) =>\n ({ type: MSG.CANVAS_MOVE, payload: { shapeId, x, y } }) as const,\n canvasResize: (\n shapeId: string,\n width: number,\n height: number,\n x?: number,\n y?: number,\n ) =>\n ({ type: MSG.CANVAS_RESIZE, payload: { shapeId, width, height, x, y } }) as const,\n canvasDelete: (shapeId: string) =>\n ({ type: MSG.CANVAS_DELETE, payload: { shapeId } }) as const,\n canvasUpdate: (shapeId: string, props: Record<string, unknown>) =>\n ({ type: MSG.CANVAS_UPDATE, payload: { shapeId, props } }) as const,\n canvasViewport: (viewport: unknown) =>\n ({ type: MSG.CANVAS_VIEWPORT, payload: { viewport } }) as const,\n canvasViewportRemoved: (userId: string) =>\n ({\n type: MSG.CANVAS_VIEWPORT,\n payload: { userId, removed: true as const },\n }) as const,\n // Cron\n cronTasks: (tasks: unknown) =>\n ({ type: MSG.CRON_TASKS, payload: { tasks } }) as const,\n cronHistory: (history: unknown) =>\n ({ type: MSG.CRON_HISTORY, payload: { history } }) as const,\n cronStatus: (tasks: unknown, recentHistory: unknown) =>\n ({ type: MSG.CRON_STATUS, payload: { tasks, recentHistory } }) as const,\n // Jobs\n jobSnapshot: (jobs: unknown[]) =>\n ({ type: MSG.JOB_UPDATE, payload: { kind: 'snapshot' as const, jobs } }) as const,\n jobEnqueued: (job: unknown, requestId?: string) =>\n ({\n type: MSG.JOB_UPDATE,\n payload: { kind: 'enqueued' as const, job, requestId },\n }) as const,\n jobProgress: (job: unknown) =>\n ({ type: MSG.JOB_UPDATE, payload: { kind: 'progress' as const, job } }) as const,\n jobSucceeded: (job: unknown) =>\n ({ type: MSG.JOB_UPDATE, payload: { kind: 'succeeded' as const, job } }) as const,\n jobFailed: (job: unknown) =>\n ({ type: MSG.JOB_UPDATE, payload: { kind: 'failed' as const, job } }) as const,\n jobCanceled: (job: unknown) =>\n ({ type: MSG.JOB_UPDATE, payload: { kind: 'canceled' as const, job } }) as const,\n jobRetried: (job: unknown) =>\n ({ type: MSG.JOB_UPDATE, payload: { kind: 'retried' as const, job } }) as const,\n // Presence\n presenceSync: (peers: unknown[]) =>\n ({ type: MSG.PRESENCE_SYNC, payload: { peers } }) as const,\n presenceJoin: (peer: unknown) =>\n ({ type: MSG.PRESENCE_JOIN, payload: { peer } }) as const,\n presenceLeave: (userId: string) =>\n ({ type: MSG.PRESENCE_LEAVE, payload: { userId } }) as const,\n presenceUpdate: (userId: string, state: Record<string, unknown>) =>\n ({ type: MSG.PRESENCE_UPDATE, payload: { userId, state } }) as const,\n // Gateway\n gwScopeError: (scopeType: string, scopeId: string, error: string) =>\n ({\n type: MSG.GW_SCOPE_ERROR,\n payload: { scopeType, scopeId, error },\n }) as const,\n gwUserUpdate: (user: unknown) =>\n ({ type: MSG.GW_USER_UPDATE, payload: user }) as const,\n}\n\n// =============================================================================\n// Typed dispatch\n// =============================================================================\n//\n// `dispatch` parses a raw WebSocket frame (or takes an already-parsed\n// object) and routes it to the right handler. Handlers are indexed by the\n// message's string `type`, and TS narrows the `payload` argument to the\n// correct arm of the message union automatically thanks to the\n// discriminated-union lookup.\n//\n// The generic `M` defaults to `ProtocolMessage` so ad-hoc uses get the\n// full SDK vocabulary for free, but apps can pass a narrower (or extended)\n// union:\n//\n// dispatch<ServerMessage>(raw, {\n// [MSG.GAME_STATE]: (p) => { /* p is narrowed */ },\n// [MSG.GAME_TICK]: (p) => { /* p is narrowed */ },\n// })\n//\n// // With an app-defined message (use a STRING LITERAL for the type\n// // parameter — `string` widens the union and kills narrowing):\n// type MyMsg =\n// | ServerMessage\n// | BaseMessage<'myapp.boss_spawn', { hp: number }>\n// dispatch<MyMsg>(raw, {\n// 'myapp.boss_spawn': (p) => { console.log(p.hp) },\n// })\n//\n// =============================================================================\n\n/**\n * Handler map keyed by a message's string `type`. Every key maps to the\n * payload type of the matching arm in `M`, so handlers get automatic\n * payload narrowing with no runtime checks.\n *\n * Narrowing requires each `M` arm to use a string-literal `T`; widening\n * `M` to `BaseMessage<string, unknown>` collapses all keys to a single\n * untyped `string` and the map becomes a plain `Record<string, (p: unknown)\n * => void>`.\n */\nexport type MessageHandlers<M extends BaseMessage<string, unknown> = ProtocolMessage> = {\n [K in M as K['type']]?: (payload: K['payload']) => void\n}\n\n/**\n * Parse and route an incoming wire message. Accepts either the raw JSON\n * string (directly from `ws.onmessage`'s `event.data`) or an already-\n * parsed object. Returns `true` if a handler ran, `false` otherwise\n * (unrecognised type, parse failure, or malformed envelope).\n *\n * Defaults `M` to `ProtocolMessage` so callers who don't care about\n * direction can just do `dispatch(raw, handlers)`; narrow with\n * `dispatch<ClientMessage>` / `dispatch<ServerMessage>` to restrict which\n * arms the handler map is allowed to cover.\n */\nexport function dispatch<M extends BaseMessage<string, unknown> = ProtocolMessage>(\n raw: unknown,\n handlers: MessageHandlers<M>,\n): boolean {\n let msg: { type?: unknown; payload?: unknown }\n if (typeof raw === 'string') {\n try {\n msg = JSON.parse(raw) as { type?: unknown; payload?: unknown }\n } catch {\n return false\n }\n } else if (raw && typeof raw === 'object') {\n msg = raw as { type?: unknown; payload?: unknown }\n } else {\n return false\n }\n if (typeof msg.type !== 'string') return false\n const handler = (handlers as Record<string, ((payload: unknown) => void) | undefined>)[\n msg.type\n ]\n if (!handler) return false\n handler(msg.payload)\n return true\n}\n\n/**\n * Serialise a typed-built message for `ws.send`. A tiny wrapper around\n * `JSON.stringify` kept around so call sites read as\n * `ws.send(encode(clientBuild.gameInput(...)))` rather than paying\n * attention to stringify arguments.\n */\nexport function encode<M extends BaseMessage<string, unknown>>(message: M): string {\n return JSON.stringify(message)\n}\n","/**\n * DeepSpace Auth Provider.\n *\n * Wraps the app with the Better Auth session hook so children can read\n * the current user via `useAuth` / `useAuthUser`. Sessions ride a\n * same-origin cookie so there's no satellite-domain configuration.\n */\n\nimport React, { useEffect, useRef } from 'react'\nimport { useSession } from './client'\nimport { debugLog } from '../debug'\n\ninterface DeepSpaceAuthProviderProps {\n children: React.ReactNode\n}\n\n/** Logs auth state transitions once. */\nfunction AuthLogger() {\n const session = useSession()\n const isSignedIn = !!session.data?.user\n const prevSignedIn = useRef<boolean | null>(null)\n\n useEffect(() => {\n if (session.isPending) return\n if (prevSignedIn.current !== isSignedIn) {\n if (isSignedIn) {\n debugLog(`[ds:auth] signed in as ${session.data?.user?.name ?? session.data?.user?.id}`)\n } else if (prevSignedIn.current !== null) {\n debugLog('[ds:auth] signed out')\n }\n prevSignedIn.current = isSignedIn\n }\n }, [isSignedIn, session.isPending, session.data?.user?.name, session.data?.user?.id])\n\n return null\n}\n\n/**\n * Auth provider for DeepSpace apps.\n *\n * Better Auth uses cookie-based sessions, so there's no provider\n * state to manage. This component exists for API compatibility\n * and as a place to add auth-related context in the future.\n */\nexport function DeepSpaceAuthProvider({ children }: DeepSpaceAuthProviderProps) {\n return (\n <>\n <AuthLogger />\n {children}\n </>\n )\n}\n","/**\n * Better Auth Client for DeepSpace\n *\n * Creates a typed Better Auth client that communicates via the app's\n * own /api/auth/* proxy (same-origin cookies, no CORS issues).\n * The app's worker proxies these requests to the auth-worker.\n */\n\nimport { createAuthClient } from 'better-auth/react'\nimport { organizationClient, twoFactorClient } from 'better-auth/client/plugins'\nimport { clearAuthToken } from './token'\n\n/**\n * Auth base URL — always same-origin.\n * The app's worker.ts proxies /api/auth/* to the auth-worker.\n */\nfunction getBaseURL(): string {\n if (typeof window !== 'undefined') {\n return window.location.origin\n }\n return ''\n}\n\nexport const authClient = createAuthClient({\n baseURL: getBaseURL(),\n plugins: [organizationClient(), twoFactorClient()],\n})\n\nexport const {\n useSession,\n signIn,\n useActiveOrganization,\n useListOrganizations,\n} = authClient\n\nexport async function signOut(\n ...args: Parameters<typeof authClient.signOut>\n): Promise<Awaited<ReturnType<typeof authClient.signOut>>> {\n try {\n return await authClient.signOut(...args)\n } finally {\n clearAuthToken()\n }\n}\n","/**\n * Short-lived JWT provider for WebSocket and API authentication.\n *\n * Fetches an ES256 token from the same-origin `/api/auth/token` endpoint\n * (routed through to the auth-worker), caches it in module scope, and\n * refreshes ~30s before expiry by reading `exp` out of the payload.\n */\n\n// Token is fetched via same-origin /api/auth/token — the app's worker (or Vite proxy)\n// routes this to the auth-worker. No cross-origin requests needed.\n\nlet cachedToken: string | null = null\nlet tokenExpiry = 0\n\nfunction extractExpiry(token: string): number {\n try {\n const parts = token.split('.')\n if (parts.length < 2) return 0\n const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')))\n return (payload.exp ?? 0) * 1000\n } catch {\n return 0\n }\n}\n\n/**\n * Get a short-lived JWT for WebSocket and API calls.\n *\n * The token is obtained from the auth-worker's /api/auth/token endpoint,\n * authenticated via the Better Auth session cookie. Tokens are cached\n * and refreshed 30s before expiry.\n */\nexport async function getAuthToken(): Promise<string | null> {\n if (typeof window === 'undefined') return null\n\n // Return cached token if still valid (30s buffer)\n if (cachedToken && Date.now() < tokenExpiry - 30_000) {\n return cachedToken\n }\n\n try {\n const res = await fetch('/api/auth/token', {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n })\n\n if (!res.ok) {\n cachedToken = null\n tokenExpiry = 0\n return null\n }\n\n const data = (await res.json()) as { token?: string | null }\n if (!data.token) {\n cachedToken = null\n tokenExpiry = 0\n return null\n }\n\n cachedToken = data.token\n tokenExpiry = extractExpiry(data.token)\n return data.token\n } catch {\n return null\n }\n}\n\n/** Clear cached token (call on sign-out). */\nexport function clearAuthToken(): void {\n cachedToken = null\n tokenExpiry = 0\n}\n","/**\n * Debug gate for the client SDK.\n *\n * The SDK's connection/auth/Yjs logs are useful when developing an app but\n * shouldn't spam every consuming app's production console. They're silent by\n * default and opt-in via either:\n * - `localStorage.DEEPSPACE_DEBUG = '1'` (browser, persists across reloads), or\n * - `globalThis.DEEPSPACE_DEBUG = true` (set before the SDK loads).\n *\n * Read once at module load so the gate is a cheap boolean check per call.\n */\nfunction readDebugFlag(): boolean {\n try {\n const g = globalThis as { DEEPSPACE_DEBUG?: unknown; localStorage?: Storage }\n if (g.DEEPSPACE_DEBUG === true || g.DEEPSPACE_DEBUG === '1') return true\n const ls = g.localStorage?.getItem('DEEPSPACE_DEBUG')\n return ls === '1' || ls === 'true'\n } catch {\n // localStorage can throw (e.g. sandboxed iframes) — treat as disabled.\n return false\n }\n}\n\nexport const DEBUG = readDebugFlag()\n\n/** console.log gated behind the DEEPSPACE_DEBUG flag. No-op by default. */\nexport function debugLog(...args: unknown[]): void {\n if (DEBUG) console.log(...args)\n}\n\n/**\n * True when the page is served from a local dev host (`deepspace dev` binds\n * localhost). Gates developer-facing diagnostics that must never render for\n * real visitors on a deployed app. Hostname-based rather than a build-time\n * env because the SDK is prebuilt — it can't see the consuming app's\n * NODE_ENV or import.meta.env. Named for the hostname check to keep it\n * distinct from `isLocalDev` in shared/env, which answers a different\n * question (which platform environment to talk to) from different signals.\n *\n * Explicit override: set `globalThis.DEEPSPACE_DEV = true` to force the\n * diagnostics on (LAN preview, tunnels), or `= false` to force them off\n * (e.g. a consumer's jsdom test setup, where `location.hostname` defaults\n * to localhost). Mirrors the DEEPSPACE_DEBUG pattern above.\n */\nexport function isLocalDevHost(): boolean {\n try {\n const g = globalThis as { DEEPSPACE_DEV?: unknown; location?: Location }\n // Accept the string/number spellings too — someone typing 'false' and\n // getting diagnostics anyway is the exact opposite of their intent.\n if (\n g.DEEPSPACE_DEV === true ||\n g.DEEPSPACE_DEV === '1' ||\n g.DEEPSPACE_DEV === 'true' ||\n g.DEEPSPACE_DEV === 1\n )\n return true\n if (\n g.DEEPSPACE_DEV === false ||\n g.DEEPSPACE_DEV === '0' ||\n g.DEEPSPACE_DEV === 'false' ||\n g.DEEPSPACE_DEV === 0\n )\n return false\n const host = g.location?.hostname\n if (!host) return false\n return (\n host === 'localhost' ||\n host === '127.0.0.1' ||\n host === '[::1]' ||\n host.endsWith('.localhost')\n )\n } catch {\n return false\n }\n}\n","/**\n * Auth hooks for the DeepSpace SDK.\n *\n * Thin wrappers over Better Auth's `useSession` that expose the loaded /\n * signed-in / userId / user-profile shape the storage layer reads.\n */\n\nimport { useSession } from './client'\n\n/**\n * Sticky-true flag: becomes true once the session query first resolves\n * (success or error) and stays true for the lifetime of the page.\n *\n * Better Auth's underlying query atom briefly flips `isPending` back to\n * true on every background refetch when `data` is `null` — i.e. for\n * anonymous users on every tab-visibility change. Reporting that as\n * `isLoaded: false` would cause consumers (e.g. `RecordProvider`) to\n * unmount and remount their children on each refocus.\n *\n * `isLoaded` here means \"the first session check has completed,\" which\n * is the contract consumers actually want.\n */\nlet sessionHasResolved = false\n\nfunction stickyIsLoaded(isPending: boolean): boolean {\n if (!isPending) sessionHasResolved = true\n return sessionHasResolved\n}\n\n/**\n * Returns auth state compatible with what storage/context.tsx expects.\n */\nexport function useAuth() {\n const session = useSession()\n\n const isSignedIn = !!session.data?.user\n const userId = session.data?.user?.id ?? null\n\n return {\n isLoaded: stickyIsLoaded(session.isPending),\n isSignedIn,\n userId,\n sessionId: session.data?.session?.id ?? null,\n }\n}\n\n/**\n * Returns user data compatible with what storage/context.tsx expects.\n */\nexport function useAuthUser() {\n const session = useSession()\n const user = session.data?.user ?? null\n\n return {\n isLoaded: stickyIsLoaded(session.isPending),\n isSignedIn: !!user,\n user: user\n ? {\n id: user.id,\n fullName: user.name ?? null,\n firstName: user.name?.split(' ')[0] ?? null,\n lastName: user.name?.split(' ').slice(1).join(' ') || null,\n emailAddresses: user.email\n ? [{ emailAddress: user.email }]\n : [],\n imageUrl: user.image ?? null,\n primaryEmailAddress: user.email\n ? { emailAddress: user.email }\n : null,\n }\n : null,\n }\n}\n","/**\n * Full-page frosted-glass auth overlay for deployed DeepSpace apps.\n *\n * Primary: OAuth (GitHub, Google).\n * Secondary: Email/password sign-in (for test accounts created via CLI/API).\n * Styled with shadcn semantic tokens so it themes along with the host app.\n */\n\nimport React, { useState } from 'react'\nimport { useAuth } from './hooks'\nimport { signIn } from './client'\n\ninterface AuthOverlayProps {\n /** Called when the user clicks the close button. If omitted, overlay is not closeable. */\n onClose?: () => void\n /** Which OAuth providers to show. Defaults to both. */\n providers?: Array<'github' | 'google'>\n}\n\nexport function AuthOverlay({\n onClose,\n providers = ['github', 'google'],\n}: AuthOverlayProps = {}): React.ReactElement | null {\n const { isLoaded, isSignedIn } = useAuth()\n const [showEmailForm, setShowEmailForm] = useState(false)\n const [email, setEmail] = useState('')\n const [password, setPassword] = useState('')\n const [error, setError] = useState('')\n const [loading, setLoading] = useState(false)\n\n if (!isLoaded || isSignedIn) return null\n\n const handleSocial = (provider: 'github' | 'google') => {\n setLoading(true)\n window.location.href = `/api/auth/social-redirect?provider=${provider}`\n }\n\n const handleEmailSignIn = async (e: React.FormEvent) => {\n e.preventDefault()\n setError('')\n setLoading(true)\n\n try {\n const result = await signIn.email({ email, password })\n if (result.error) {\n setError(result.error.message ?? 'Sign in failed')\n setLoading(false)\n }\n } catch (err: unknown) {\n setError(err instanceof Error ? err.message : 'Something went wrong')\n setLoading(false)\n }\n }\n\n return (\n <>\n <style>{`\n @keyframes ds-auth-overlay-in {\n from { opacity: 0 }\n to { opacity: 1 }\n }\n @keyframes ds-auth-card-in {\n from { opacity: 0; transform: translateY(16px) scale(0.97) }\n to { opacity: 1; transform: translateY(0) scale(1) }\n }\n `}</style>\n\n <div\n data-testid=\"auth-overlay\"\n onClick={onClose ? (e) => { if (e.target === e.currentTarget) onClose() } : undefined}\n className=\"fixed inset-0 z-[99999] flex items-center justify-center bg-black/55 backdrop-blur-2xl\"\n style={{\n opacity: 0,\n animation: 'ds-auth-overlay-in 0.4s ease forwards',\n }}\n >\n <div\n className=\"relative mx-6 w-full max-w-[380px] rounded-2xl border border-border bg-card p-8 text-center shadow-card-hover sm:p-9\"\n style={{\n opacity: 0,\n animation: 'ds-auth-card-in 0.45s cubic-bezier(0.25, 1, 0.5, 1) 0.08s forwards',\n }}\n >\n {onClose && (\n <button\n data-testid=\"auth-overlay-close\"\n onClick={onClose}\n aria-label=\"Close\"\n className=\"absolute right-3 top-3 inline-flex h-7 w-7 items-center justify-center rounded-full bg-secondary text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n >\n ✕\n </button>\n )}\n\n <h2 className=\"mb-2 text-[22px] font-bold leading-snug text-foreground\">\n Sign in to DeepSpace\n </h2>\n\n <p className=\"mb-6 text-sm leading-snug text-muted-foreground\">\n Sync your data across devices\n </p>\n\n {/* OAuth buttons */}\n <div className=\"flex flex-col gap-2\">\n {providers.includes('github') && (\n <OAuthButton onClick={() => handleSocial('github')} disabled={loading} icon={<GitHubIcon />}>\n Continue with GitHub\n </OAuthButton>\n )}\n {providers.includes('google') && (\n <OAuthButton onClick={() => handleSocial('google')} disabled={loading} icon={<GoogleIcon />}>\n Continue with Google\n </OAuthButton>\n )}\n </div>\n\n <Divider />\n\n {!showEmailForm ? (\n <button\n type=\"button\"\n data-testid=\"auth-email-toggle\"\n onClick={() => setShowEmailForm(true)}\n className=\"flex w-full items-center justify-center gap-2.5 rounded-lg border border-border bg-transparent px-4 py-2.5 text-sm font-semibold text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground\"\n >\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <rect x=\"2\" y=\"4\" width=\"20\" height=\"16\" rx=\"2\" />\n <path d=\"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\" />\n </svg>\n Sign in with email\n </button>\n ) : (\n <form onSubmit={handleEmailSignIn} className=\"text-left\">\n <input\n type=\"email\"\n placeholder=\"Email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n required\n autoComplete=\"email\"\n className=\"mb-3 block w-full rounded-lg border border-border bg-secondary px-3.5 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none\"\n />\n <input\n type=\"password\"\n placeholder=\"Password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n required\n minLength={8}\n autoComplete=\"current-password\"\n className={`block w-full rounded-lg border border-border bg-secondary px-3.5 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-primary focus:outline-none ${error ? 'mb-3' : 'mb-5'}`}\n />\n\n {error && (\n <div className=\"mb-4 text-center text-sm text-destructive\">{error}</div>\n )}\n\n <button\n type=\"submit\"\n disabled={loading}\n className=\"block w-full rounded-lg bg-primary px-5 py-3 text-[15px] font-semibold text-primary-foreground transition-opacity hover:opacity-90 disabled:cursor-wait disabled:opacity-70\"\n >\n {loading ? 'Please wait…' : 'Sign in'}\n </button>\n </form>\n )}\n\n <p className=\"mt-5 text-xs leading-snug text-muted-foreground\">\n By continuing, you agree to the DeepSpace{' '}\n <a\n href=\"https://deep.space/terms\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"underline hover:text-foreground\"\n >\n Terms of Service\n </a>{' '}\n and{' '}\n <a\n href=\"https://deep.space/privacy\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"underline hover:text-foreground\"\n >\n Privacy Policy\n </a>\n .\n </p>\n\n <div className=\"mt-4 flex items-center justify-center gap-1.5 text-[11px] text-muted-foreground\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"currentColor\" opacity=\"0.5\" aria-hidden>\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n </svg>\n Powered by DeepSpace\n </div>\n </div>\n </div>\n </>\n )\n}\n\n// ────────────────────────────────────────────────────────────────────\n// Local components\n// ────────────────────────────────────────────────────────────────────\n\nfunction OAuthButton({\n onClick,\n disabled,\n icon,\n children,\n}: {\n onClick: () => void\n disabled: boolean\n icon: React.ReactNode\n children: React.ReactNode\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n className=\"flex w-full items-center justify-center gap-2.5 rounded-lg border border-border bg-secondary px-4 py-2.5 text-sm font-semibold text-foreground transition-colors hover:bg-accent disabled:cursor-wait disabled:opacity-60\"\n >\n {icon}\n {children}\n </button>\n )\n}\n\nfunction Divider() {\n return (\n <div className=\"my-5 flex items-center gap-3 text-xs text-muted-foreground\">\n <div className=\"h-px flex-1 bg-border\" />\n or\n <div className=\"h-px flex-1 bg-border\" />\n </div>\n )\n}\n\nfunction GitHubIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 16 16\" fill=\"currentColor\" aria-hidden>\n <path d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0016 8c0-4.42-3.58-8-8-8z\" />\n </svg>\n )\n}\n\n// Google logo uses Google's brand colors — these are required by Google's\n// brand guidelines and intentionally not theme-reactive.\nfunction GoogleIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" aria-hidden>\n <path fill=\"#4285F4\" d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z\" />\n <path fill=\"#34A853\" d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\" />\n <path fill=\"#FBBC05\" d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\" />\n <path fill=\"#EA4335\" d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\" />\n </svg>\n )\n}\n","/** Conditional renderers, the route gate, and a display-name hook. */\n\nimport { useEffect, useRef, type ReactNode } from 'react'\nimport { useAuth, useAuthUser } from './hooks'\nimport { AuthOverlay } from './AuthOverlay'\n\n/**\n * Renders children only when the user is signed in.\n */\nexport function SignedIn({ children }: { children: ReactNode }): React.ReactElement | null {\n const { isLoaded, isSignedIn } = useAuth()\n if (!isLoaded || !isSignedIn) return null\n return <>{children}</>\n}\n\n/**\n * Renders children only when the user is signed out.\n */\nexport function SignedOut({ children }: { children: ReactNode }): React.ReactElement | null {\n const { isLoaded, isSignedIn } = useAuth()\n if (!isLoaded || isSignedIn) return null\n return <>{children}</>\n}\n\nexport interface AuthGateProps {\n /** Content to show when signed in */\n children: ReactNode\n /**\n * UI for first-visit signed-out users. If omitted, the SDK's sign-in\n * overlay is shown non-closeably. Not used on mid-session sign-out —\n * see `redirectOnSignOut`.\n */\n fallback?: ReactNode\n /** Where to send the user when they sign out from inside the gate. Defaults to '/'. */\n redirectOnSignOut?: string\n}\n\n/**\n * Gates a subtree behind sign-in. Children mount only when signed in (no flash).\n * If the user signs out from inside the gate, redirects out instead of trapping\n * them on the overlay.\n */\nexport function AuthGate({\n children,\n fallback,\n redirectOnSignOut = '/',\n}: AuthGateProps): React.ReactElement | null {\n const { isLoaded, isSignedIn } = useAuth()\n const wasSignedIn = useRef(false)\n if (isSignedIn) wasSignedIn.current = true\n\n if (!isLoaded) return null\n if (isSignedIn) return <>{children}</>\n if (wasSignedIn.current) return <RedirectAway to={redirectOnSignOut} />\n if (fallback) return <>{fallback}</>\n return <AuthOverlay />\n}\n\n// Full page load (not a router navigation) so the SDK stays router-agnostic\n// and the signed-out teardown is clean.\nfunction RedirectAway({ to }: { to: string }): null {\n useEffect(() => {\n if (typeof window !== 'undefined') window.location.replace(to)\n }, [to])\n return null\n}\n\n/**\n * Returns the current user's display name.\n *\n * Resolution order: fullName -> firstName -> email prefix -> 'User'.\n * Returns null when no user is signed in.\n */\nexport function useDisplayName(): string | null {\n const { user } = useAuthUser()\n if (!user) return null\n return (\n user.fullName ||\n user.firstName ||\n user.primaryEmailAddress?.emailAddress?.split('@')[0] ||\n 'User'\n )\n}\n","/**\n * Floating dismissible guest banner for DeepSpace apps.\n *\n * Shows a non-intrusive prompt for signed-out users to sign in or sign up.\n * Dismiss state is persisted in sessionStorage so it stays hidden for the\n * duration of the browsing session.\n *\n * Styled with shadcn semantic tokens (bg-card, text-foreground, etc.) so it\n * themes along with the host app.\n */\n\nimport React, { useEffect, useState } from 'react'\nimport { useAuth } from './hooks'\n\nconst DEFAULT_STORAGE_KEY = 'deepspace-guest-banner-dismissed'\nconst DEFAULT_MESSAGE = \"You're browsing as a guest. Sign in to unlock all features.\"\n\nexport interface GuestBannerProps {\n message?: string\n storageKey?: string\n /** Called when the user clicks \"Sign in\". */\n onSignIn?: () => void\n}\n\nexport function GuestBanner({\n message = DEFAULT_MESSAGE,\n storageKey = DEFAULT_STORAGE_KEY,\n onSignIn,\n}: GuestBannerProps): React.ReactElement | null {\n const { isLoaded, isSignedIn } = useAuth()\n const [mounted, setMounted] = useState(false)\n\n const [dismissed, setDismissed] = useState(() => {\n if (typeof window === 'undefined') return false\n try {\n return sessionStorage.getItem(storageKey) === '1'\n } catch {\n return false\n }\n })\n\n useEffect(() => {\n if (isLoaded && !isSignedIn && !dismissed) {\n requestAnimationFrame(() => setMounted(true))\n }\n }, [dismissed, isLoaded, isSignedIn])\n\n if (!isLoaded || isSignedIn || dismissed) return null\n\n const handleDismiss = (): void => {\n try {\n sessionStorage.setItem(storageKey, '1')\n } catch {\n // best effort\n }\n setDismissed(true)\n }\n\n return (\n <>\n <style>{`\n @keyframes ds-guest-banner-in {\n from { opacity: 0; transform: translateY(-12px); }\n to { opacity: 1; transform: translateY(0); }\n }\n `}</style>\n <div\n role=\"banner\"\n data-testid=\"guest-banner\"\n className=\"fixed top-3 inset-x-3 z-[99998] mx-auto flex max-w-[700px] items-center gap-3.5 rounded-2xl border border-border bg-card/95 px-4 py-3 text-sm leading-snug text-foreground shadow-card-hover backdrop-blur-xl sm:gap-4\"\n style={{\n opacity: mounted ? 1 : 0,\n animation: mounted\n ? 'ds-guest-banner-in 0.35s cubic-bezier(0.25, 1, 0.5, 1) forwards'\n : 'none',\n pointerEvents: mounted ? 'auto' : 'none',\n }}\n >\n <span\n aria-hidden\n className=\"hidden h-2 w-2 shrink-0 rounded-full bg-primary shadow-[0_0_8px_var(--color-primary)] sm:inline-block\"\n />\n\n <span className=\"min-w-0 flex-1 font-medium text-muted-foreground\">\n {message}\n </span>\n\n <div className=\"flex shrink-0 items-center gap-2\">\n <button\n type=\"button\"\n data-testid=\"guest-banner-signin\"\n onClick={onSignIn}\n className=\"inline-flex items-center justify-center whitespace-nowrap rounded-lg bg-primary px-4 py-1.5 text-xs font-semibold text-primary-foreground transition-opacity hover:opacity-90 sm:text-sm\"\n >\n Sign in\n </button>\n\n <button\n type=\"button\"\n data-testid=\"guest-banner-dismiss\"\n onClick={handleDismiss}\n aria-label=\"Dismiss banner\"\n className=\"flex shrink-0 items-center justify-center rounded-md p-1 text-muted-foreground transition-colors hover:text-foreground\"\n >\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n </button>\n </div>\n </div>\n </>\n )\n}\n","/**\n * RecordProvider Context\n *\n * Provides WebSocket connection and state management for RecordRoom.\n *\n * Multi-scope mode: RecordProvider can be used without roomId\n * to provide only auth context + ScopeRegistry. RecordScope components\n * handle individual WebSocket connections.\n */\n\nimport React, {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n useRef,\n useMemo,\n type ReactNode,\n} from 'react'\nimport type { CollectionSchema } from '../../shared/types'\nimport { useAuth, getAuthToken } from '../auth'\nimport { debugLog, isLocalDevHost } from '../debug'\nimport { RecordStore } from './store'\nimport { RecordSocket } from './record-socket'\nimport { ScopeRegistryProvider } from './ScopeRegistry'\nimport type {\n UserProfile,\n RoomUser,\n RoomConnectionState,\n FetchUserProfile,\n} from './types'\nimport { MSG } from '@/shared/protocol/constants'\n\n// ============================================================================\n// RecordContext (per-scope connection state)\n// ============================================================================\n\nexport interface RecordContextValue {\n store: RecordStore\n roomId: string\n /** Collection names registered by this scope's schemas (used for scope resolution priority). */\n registeredCollections?: Set<string>\n userProfile: UserProfile | null\n userProfileLoading: boolean\n refetchUserProfile: () => Promise<void>\n roomRole: string | null\n allUsers: RoomUser[]\n /** True once the first MSG.USER_LIST response has been received. */\n usersLoaded: boolean\n status: RoomConnectionState\n ready: boolean\n /** Schemas discovered via MSG.LIST_SCHEMAS from the server. Available after ready. */\n discoveredSchemas?: CollectionSchema[]\n setUserRole: (userId: string, role: string) => void\n requestUserList: () => void\n registerSubscription: (subscriptionId: string, queryKey: string) => void\n unregisterSubscription: (subscriptionId: string) => void\n sendMessage: (message: { type: string; payload: unknown }) => void\n sendBinary: (data: Uint8Array) => void\n onBinaryMessage: (handler: (data: ArrayBuffer) => void) => () => void\n registerYjsJoinHandler: (docKey: string, handler: (canWrite: boolean) => void) => () => void\n sendConfirmed: (\n message: { type: string; payload: Record<string, unknown> },\n timeoutMs?: number,\n ) => Promise<unknown>\n}\n\nexport const RecordContext = createContext<RecordContextValue | null>(null)\n\nexport function useRecordContext(): RecordContextValue {\n const ctx = useContext(RecordContext)\n if (!ctx) {\n throw new Error('useRecordContext must be used within a RecordProvider')\n }\n return ctx\n}\n\n// ============================================================================\n// RecordAuthContext (shared auth state for RecordScope)\n// ============================================================================\n\nexport interface RecordAuthContextValue {\n userProfile: UserProfile | null\n userProfileLoading: boolean\n refetchUserProfile: () => Promise<void>\n allowAnonymous: boolean\n /** Called by RecordScope when it receives an RBAC permission error */\n onPermissionError?: (title: string, detail: string) => void\n /** Called by RecordScope when it receives a validation/other error */\n onValidationError?: (title: string, detail: string) => void\n /** Get auth token for WebSocket connections. */\n getAuthToken?: () => Promise<string | null>\n}\n\nconst RecordAuthContext = createContext<RecordAuthContextValue | null>(null)\n\nexport function useRecordAuth(): RecordAuthContextValue | null {\n return useContext(RecordAuthContext)\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n\n// ============================================================================\n// Core Provider (handles WebSocket connection — backward compat mode)\n// ============================================================================\n\ninterface RecordProviderCoreProps {\n roomId: string\n wsUrl?: string\n children: ReactNode\n fetchUser: FetchUserProfile\n allowAnonymous?: boolean\n getAuthToken?: () => Promise<string | null>\n onPermissionError?: (title: string, detail: string) => void\n onValidationError?: (title: string, detail: string) => void\n}\n\nfunction RecordProviderCore({\n roomId,\n wsUrl,\n children,\n fetchUser,\n allowAnonymous = false,\n getAuthToken: getAuthTokenProp,\n onPermissionError: onPermissionErrorProp,\n onValidationError: onValidationErrorProp,\n}: RecordProviderCoreProps): React.ReactElement {\n const [userProfile, setUserProfile] = useState<UserProfile | null>(null)\n const [userProfileLoading, setUserProfileLoading] = useState(true)\n\n const [roomRole, setRoomRole] = useState<string | null>(null)\n const [allUsers, setAllUsers] = useState<RoomUser[]>([])\n const [usersLoaded, setUsersLoaded] = useState(false)\n const [status, setStatus] = useState<RoomConnectionState>('connecting')\n const [ready, setReady] = useState(false)\n\n // Callback refs for error handling — the socket's listeners close over\n // these so the latest app callbacks are invoked without socket recreation.\n const onPermissionErrorRef = useRef<((title: string, detail: string) => void) | undefined>(\n undefined,\n )\n const onValidationErrorRef = useRef<((title: string, detail: string) => void) | undefined>(\n undefined,\n )\n onPermissionErrorRef.current = onPermissionErrorProp\n onValidationErrorRef.current = onValidationErrorProp\n\n const storeRef = useRef<RecordStore>(new RecordStore())\n const socketRef = useRef<RecordSocket | null>(null)\n // Persist across socket recreation (auth change) — hooks register these\n // on mount and never re-register for a socket swap.\n const subscriptionsRef = useRef<Map<string, string>>(new Map())\n const binaryHandlersRef = useRef<Set<(data: ArrayBuffer) => void>>(new Set())\n const yjsJoinHandlersRef = useRef<Map<string, Set<(canWrite: boolean) => void>>>(new Map())\n const userProfileRef = useRef<UserProfile | null>(null)\n userProfileRef.current = userProfile\n const getAuthTokenRef = useRef(getAuthTokenProp)\n getAuthTokenRef.current = getAuthTokenProp\n\n const refetchUserProfile = useCallback(async () => {\n try {\n const profile = await fetchUser()\n setUserProfile(profile)\n } catch (err) {\n console.error('[RecordProvider] Failed to refetch user profile:', err)\n }\n }, [fetchUser])\n\n // Fetch user profile on mount and refresh periodically.\n // fetchUser returns null when not signed in — that's fine.\n useEffect(() => {\n let mounted = true\n setUserProfileLoading(true)\n fetchUser()\n .then((profile) => {\n if (mounted) setUserProfile(profile)\n })\n .finally(() => {\n if (mounted) setUserProfileLoading(false)\n })\n\n const interval = setInterval(() => {\n if (mounted)\n fetchUser()\n .then((p) => {\n if (mounted) setUserProfile(p)\n })\n .catch(() => {\n /* a transient profile-refresh failure keeps the last profile */\n })\n }, 30000)\n\n return () => {\n mounted = false\n clearInterval(interval)\n }\n }, [fetchUser])\n\n // ── Socket lifecycle ─────────────────────────────────────────────────\n // One shared engine with RecordScope (record-socket.ts) owns everything\n // race-sensitive. The socket is recreated when the connection identity\n // (room, URL, signed-in user) changes; identity changes therefore always\n // reconnect with the current token — including the anonymous → signed-in\n // upgrade the old inline implementation special-cased.\n\n const userProfileId = userProfile?.id ?? null\n\n useEffect(() => {\n if (userProfileLoading) return\n\n const socket = new RecordSocket({\n roomId,\n store: storeRef.current,\n subscriptions: subscriptionsRef.current,\n binaryHandlers: binaryHandlersRef.current,\n yjsJoinHandlers: yjsJoinHandlersRef.current,\n getToken: () => (getAuthTokenRef.current ?? getAuthToken)(),\n wsUrl,\n log: (event, detail) => debugLog(`[WS] ${event}`, detail),\n listeners: {\n onStatus: setStatus,\n onReady: setReady,\n onRole: setRoomRole,\n onUsers: (users) => {\n setAllUsers(users)\n setUsersLoaded(true)\n },\n onPermissionError: (title, detail) => onPermissionErrorRef.current?.(title, detail),\n onValidationError: (title, detail) => onValidationErrorRef.current?.(title, detail),\n },\n })\n socketRef.current = socket\n\n if (userProfileId || allowAnonymous) {\n void socket.connect(userProfileId ?? '')\n }\n\n return () => {\n socketRef.current = null\n socket.destroy()\n setStatus('connecting')\n setReady(false)\n }\n }, [roomId, wsUrl, userProfileId, userProfileLoading, allowAnonymous])\n\n // Reconnect on tab focus\n useEffect(() => {\n const onVisible = () => {\n if (document.visibilityState === 'visible' && !socketRef.current?.isOpen) {\n debugLog('[WS] Reconnecting after visibility change')\n socketRef.current?.resetBackoff()\n void socketRef.current?.connect()\n }\n }\n document.addEventListener('visibilitychange', onVisible)\n return () => document.removeEventListener('visibilitychange', onVisible)\n }, [])\n\n const sendMessage = useCallback((message: { type: string; payload: unknown }) => {\n socketRef.current?.sendMessage(message)\n }, [])\n\n const setUserRole = useCallback(\n (userId: string, role: string) => {\n sendMessage({ type: MSG.SET_ROLE, payload: { userId, role } })\n },\n [sendMessage],\n )\n\n const requestUserList = useCallback(() => {\n sendMessage({ type: MSG.USER_LIST, payload: {} })\n }, [sendMessage])\n\n const registerSubscription = useCallback((subscriptionId: string, queryKey: string) => {\n subscriptionsRef.current.set(subscriptionId, queryKey)\n }, [])\n\n const unregisterSubscription = useCallback((subscriptionId: string) => {\n subscriptionsRef.current.delete(subscriptionId)\n }, [])\n\n const sendBinary = useCallback((data: Uint8Array) => {\n socketRef.current?.sendBinary(data)\n }, [])\n\n const onBinaryMessage = useCallback((handler: (data: ArrayBuffer) => void) => {\n binaryHandlersRef.current.add(handler)\n return () => {\n binaryHandlersRef.current.delete(handler)\n }\n }, [])\n\n const registerYjsJoinHandler = useCallback(\n (docKey: string, handler: (canWrite: boolean) => void) => {\n const handlers = yjsJoinHandlersRef.current\n if (!handlers.has(docKey)) handlers.set(docKey, new Set())\n handlers.get(docKey)!.add(handler)\n return () => {\n const set = handlers.get(docKey)\n if (set) {\n set.delete(handler)\n if (set.size === 0) handlers.delete(docKey)\n }\n }\n },\n [],\n )\n\n const sendConfirmed = useCallback(\n (\n message: { type: string; payload: Record<string, unknown> },\n timeoutMs = 10000,\n ): Promise<unknown> => {\n const socket = socketRef.current\n if (!socket) return Promise.reject(new Error('WebSocket not connected'))\n return socket.sendConfirmed(message, timeoutMs)\n },\n [],\n )\n\n const value: RecordContextValue = useMemo(\n () => ({\n store: storeRef.current,\n roomId,\n userProfile,\n userProfileLoading,\n refetchUserProfile,\n roomRole,\n allUsers,\n usersLoaded,\n status,\n ready,\n setUserRole,\n requestUserList,\n registerSubscription,\n unregisterSubscription,\n sendMessage,\n sendBinary,\n onBinaryMessage,\n registerYjsJoinHandler,\n sendConfirmed,\n }),\n [\n roomId,\n userProfile,\n userProfileLoading,\n refetchUserProfile,\n roomRole,\n allUsers,\n usersLoaded,\n status,\n ready,\n setUserRole,\n requestUserList,\n registerSubscription,\n unregisterSubscription,\n sendMessage,\n sendBinary,\n onBinaryMessage,\n registerYjsJoinHandler,\n sendConfirmed,\n ],\n )\n\n const onPermissionError = useCallback((title: string, detail: string) => {\n onPermissionErrorRef.current?.(title, detail)\n }, [])\n\n const onValidationError = useCallback((title: string, detail: string) => {\n onValidationErrorRef.current?.(title, detail)\n }, [])\n\n const authValue: RecordAuthContextValue = useMemo(\n () => ({\n userProfile,\n userProfileLoading,\n refetchUserProfile,\n allowAnonymous,\n getAuthToken: getAuthTokenProp,\n onPermissionError,\n onValidationError,\n }),\n [\n userProfile,\n userProfileLoading,\n refetchUserProfile,\n allowAnonymous,\n getAuthTokenProp,\n onPermissionError,\n onValidationError,\n ],\n )\n\n return (\n <RecordAuthContext.Provider value={authValue}>\n <ScopeRegistryProvider>\n <RecordContext.Provider value={value}>{children}</RecordContext.Provider>\n </ScopeRegistryProvider>\n </RecordAuthContext.Provider>\n )\n}\n\n// ============================================================================\n// Auth-Only Provider (multi-scope mode — no roomId, no WS)\n// ============================================================================\n\ninterface RecordProviderAuthOnlyProps {\n children: ReactNode\n fetchUser: FetchUserProfile\n allowAnonymous?: boolean\n getAuthToken?: () => Promise<string | null>\n onPermissionError?: (title: string, detail: string) => void\n onValidationError?: (title: string, detail: string) => void\n}\n\nfunction RecordProviderAuthOnly({\n children,\n fetchUser,\n allowAnonymous = false,\n getAuthToken: getAuthTokenProp,\n onPermissionError: onPermissionErrorProp,\n onValidationError: onValidationErrorProp,\n}: RecordProviderAuthOnlyProps): React.ReactElement {\n const [userProfile, setUserProfile] = useState<UserProfile | null>(null)\n const [userProfileLoading, setUserProfileLoading] = useState(true)\n\n // Hold the latest app callbacks in refs and expose stable wrappers, so an\n // inline arrow prop doesn't churn authValue (and every RecordScope under\n // it) on each render. Mirrors RecordProviderCore.\n const onPermissionErrorRef = useRef(onPermissionErrorProp)\n onPermissionErrorRef.current = onPermissionErrorProp\n const onValidationErrorRef = useRef(onValidationErrorProp)\n onValidationErrorRef.current = onValidationErrorProp\n\n const onPermissionError = useCallback((title: string, detail: string) => {\n onPermissionErrorRef.current?.(title, detail)\n }, [])\n\n const onValidationError = useCallback((title: string, detail: string) => {\n onValidationErrorRef.current?.(title, detail)\n }, [])\n\n const refetchUserProfile = useCallback(async () => {\n try {\n const profile = await fetchUser()\n setUserProfile(profile)\n } catch (err) {\n console.error('[RecordProvider] Failed to refetch user profile:', err)\n }\n }, [fetchUser])\n\n // Fetch user profile on mount and refresh periodically.\n // fetchUser returns null when not signed in — that's fine, no retry needed.\n useEffect(() => {\n let mounted = true\n setUserProfileLoading(true)\n fetchUser()\n .then((profile) => {\n if (mounted) setUserProfile(profile)\n })\n .finally(() => {\n if (mounted) setUserProfileLoading(false)\n })\n\n const interval = setInterval(() => {\n if (mounted) {\n fetchUser()\n .then((p) => {\n if (mounted) setUserProfile(p)\n })\n .catch(() => {\n /* a transient profile-refresh failure keeps the last profile */\n })\n }\n }, 30000)\n\n return () => {\n mounted = false\n clearInterval(interval)\n }\n }, [fetchUser])\n\n const authValue: RecordAuthContextValue = useMemo(\n () => ({\n userProfile,\n userProfileLoading,\n refetchUserProfile,\n allowAnonymous,\n getAuthToken: getAuthTokenProp,\n onPermissionError,\n onValidationError,\n }),\n [\n userProfile,\n userProfileLoading,\n refetchUserProfile,\n allowAnonymous,\n getAuthTokenProp,\n onPermissionError,\n onValidationError,\n ],\n )\n\n return (\n <RecordAuthContext.Provider value={authValue}>\n <ScopeRegistryProvider>{children}</ScopeRegistryProvider>\n </RecordAuthContext.Provider>\n )\n}\n\n// ============================================================================\n// Main Entry Point\n// ============================================================================\n\n/**\n * A server-rejected write, delivered to `RecordProvider`'s `onWriteError`.\n * One structured object rather than positional strings so future rejection\n * kinds (rate-limit, quota, dropped-while-offline) and correlation fields\n * (collection, recordId, the raw server string) can be added without\n * breaking the callback signature.\n */\nexport interface WriteError {\n /** 'permission' — an RBAC denial; 'validation' — missing/immutable/invalid field or any other rejection. */\n kind: 'permission' | 'validation'\n /** Short human-readable summary, safe to show end users. */\n title: string\n /** Longer human-readable explanation; may be empty. */\n detail: string\n}\n\n/**\n * Default write-error handler. Optimistic mutations (`create`/`put`/`remove`)\n * resolve before the server answers, so a denied or invalid write can only\n * surface through `onWriteError` — if it's unhandled, the app looks like it\n * worked while the server silently rejected. Never let that be fully silent:\n * fall back to a loud console.error that says how to wire real UI.\n *\n * Deduped per unique message: a retry loop hammering the same denied write\n * must not flood the console (or an agent's captured console output) with\n * identical errors — the first occurrence carries all the information.\n */\nconst seenWriteErrors = new Set<string>()\n\nfunction defaultOnWriteError(error: WriteError): void {\n // JSON key rather than string concatenation so delimiter characters in a\n // title/detail can't collide two distinct errors into one dedupe entry.\n const key = JSON.stringify([error.kind, error.title, error.detail])\n if (seenWriteErrors.has(key)) return\n // Bound the set so a pathological stream of distinct errors can't grow it\n // forever; clearing just means an occasional repeat log, which is fine.\n if (seenWriteErrors.size >= 100) seenWriteErrors.clear()\n seenWriteErrors.add(key)\n console.error(\n `[deepspace] Write rejected — ${error.title}: ${error.detail}\\n` +\n `This error reached no UI. Pass onWriteError to <RecordProvider> (e.g. wire it to a toast) so users see rejected writes. (Further repeats of this error are suppressed.)`,\n )\n}\n\nlet warnedIgnoredSchemas = false\nlet warnedSignedOutNull = false\n\n/**\n * Test hook: reset the module-level dev-warning latches and the write-error\n * dedupe set, so tests can assert warnings/errors fire from a clean slate.\n * Not part of the public API surface.\n */\nexport function __resetDevWarningsForTests(): void {\n seenWriteErrors.clear()\n warnedIgnoredSchemas = false\n warnedSignedOutNull = false\n}\n\n/**\n * Rendered on localhost instead of the silent `null` when the user is signed\n * out and `allowAnonymous` is off — the #1 \"my page is blank and nothing says\n * why\" trap. Inline styles only: this must not depend on the app's CSS.\n * Production keeps the old behavior (render nothing).\n */\nfunction SignedOutDiagnostic(): React.ReactElement {\n return (\n <div\n role=\"alert\"\n data-deepspace-diagnostic=\"signed-out\"\n style={{\n margin: '24px auto',\n maxWidth: 560,\n padding: '16px 20px',\n border: '1px solid #d97706',\n borderRadius: 8,\n background: '#fffbeb',\n color: '#78350f',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',\n fontSize: 13,\n lineHeight: 1.5,\n }}\n >\n <strong>DeepSpace: nothing rendered because you're signed out.</strong>\n <p style={{ margin: '8px 0 0' }}>\n This page is inside a {'<RecordProvider>'} without <code>allowAnonymous</code>, which\n renders nothing for signed-out visitors. Either add{' '}\n <code>{'<RecordProvider allowAnonymous>'}</code> to show the page publicly, or require\n sign-in before this point (e.g. a protected route / AuthGate).\n </p>\n <p style={{ margin: '8px 0 0', opacity: 0.8 }}>\n This notice only appears on localhost — a deployed app renders nothing here.\n </p>\n </div>\n )\n}\n\n/**\n * RecordProvider - Main entry point for storage.\n *\n * Two modes:\n * 1. **Single-scope (backward compat):** Pass `roomId` + `schemas`. Opens one WS connection.\n * 2. **Multi-scope:** Omit `roomId`. Only provides auth + ScopeRegistry.\n * Use `<RecordScope>` components for individual WS connections.\n *\n * @example\n * ```tsx\n * // Single-scope (backward compat)\n * <RecordProvider roomId=\"my-app\" schemas={schemas}>\n * <App />\n * </RecordProvider>\n *\n * // Multi-scope\n * <RecordProvider>\n * <RecordScope roomId=\"app:slack-clone\" schemas={appSchemas}>\n * <RecordScope roomId={`conv:${channelId}`} schemas={convSchemas}>\n * <ChannelView />\n * </RecordScope>\n * </RecordScope>\n * </RecordProvider>\n * ```\n */\nexport function RecordProvider({\n roomId,\n schemas,\n wsUrl,\n children,\n allowAnonymous = false,\n getAuthToken: getAuthTokenProp,\n onWriteError = defaultOnWriteError,\n}: {\n roomId?: string\n /**\n * Accepted for API compatibility with `RecordScope` and documented usage,\n * but the single-scope backward-compat provider does no local-first\n * registration, so it's ignored. Pass schemas to `RecordScope` instead.\n */\n schemas?: CollectionSchema[]\n wsUrl?: string\n children: ReactNode\n allowAnonymous?: boolean\n getAuthToken?: () => Promise<string | null>\n /**\n * Called when the server rejects a write. Optimistic mutations\n * (`create`/`put`/`remove`) resolve before the server answers, so this\n * callback is the only place a denied or invalid write surfaces — wire it\n * to your toast/alert system and branch on `error.kind`. Defaults to a\n * deduped console.error so rejections are never fully silent.\n */\n onWriteError?: (error: WriteError) => void\n}): React.ReactElement {\n const { isLoaded, isSignedIn } = useAuth()\n\n if (schemas && roomId && isLocalDevHost() && !warnedIgnoredSchemas) {\n warnedIgnoredSchemas = true\n console.warn(\n '[deepspace] RecordProvider received a `schemas` prop alongside `roomId`, but the single-scope provider ignores it — ' +\n 'schemas are resolved server-side in this mode. Remove the `schemas` prop, or adopt the multi-scope shape and pass schemas to <RecordScope>.',\n )\n }\n\n // Derive user profile from the JWT — no API call needed.\n // Returns null when not signed in (no error, no console spam).\n const fetchUser = useCallback(async (): Promise<UserProfile | null> => {\n if (!isSignedIn) return null\n const token = await getAuthToken()\n if (!token) return null\n try {\n const parts = token.split('.')\n const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')))\n return {\n id: payload.sub,\n name: payload.name ?? '',\n email: payload.email ?? '',\n imageUrl: payload.image ?? undefined,\n }\n } catch {\n return null\n }\n }, [isSignedIn])\n\n // Build getAuthToken function — use prop override or default to auth module\n const getAuthTokenFn = useCallback(async (): Promise<string | null> => {\n if (getAuthTokenProp) return getAuthTokenProp()\n if (!isSignedIn) return null\n return getAuthToken()\n }, [getAuthTokenProp, isSignedIn])\n\n // Not loaded yet — render nothing (no loading screens — that's the app's job)\n if (!isLoaded) {\n return <>{null}</>\n }\n\n // Not signed in and not allowing anonymous — render nothing in production.\n // On localhost this is far more often a missing `allowAnonymous` than a\n // deliberate auth gate, and a bare blank page gives no thread to pull, so\n // dev gets a visible diagnostic instead.\n if (!isSignedIn && !allowAnonymous) {\n if (isLocalDevHost()) {\n if (!warnedSignedOutNull) {\n warnedSignedOutNull = true\n console.warn(\n '[deepspace] RecordProvider rendered nothing: signed out and allowAnonymous is off. ' +\n 'Add `allowAnonymous` to show this page to signed-out visitors, or require sign-in before this point.',\n )\n }\n return <SignedOutDiagnostic />\n }\n return <>{null}</>\n }\n\n // Adapt the public structured callback onto the two internal channels the\n // socket layer dispatches (permission vs validation). Inline arrows are\n // fine here: Core/AuthOnly hold them in refs behind stable wrappers, so\n // their identity never reaches a dependency array.\n const onPermissionError = (title: string, detail: string) =>\n onWriteError({ kind: 'permission', title, detail })\n const onValidationError = (title: string, detail: string) =>\n onWriteError({ kind: 'validation', title, detail })\n\n if (roomId) {\n return (\n <RecordProviderCore\n roomId={roomId}\n wsUrl={wsUrl}\n fetchUser={fetchUser}\n allowAnonymous={allowAnonymous}\n getAuthToken={getAuthTokenFn}\n onPermissionError={onPermissionError}\n onValidationError={onValidationError}\n >\n {children}\n </RecordProviderCore>\n )\n }\n\n return (\n <RecordProviderAuthOnly\n fetchUser={fetchUser}\n allowAnonymous={allowAnonymous}\n getAuthToken={getAuthTokenFn}\n onPermissionError={onPermissionError}\n onValidationError={onValidationError}\n >\n {children}\n </RecordProviderAuthOnly>\n )\n}\n","/**\n * RecordStore\n *\n * Manages query subscriptions with version-tracked caching.\n * Uses external store pattern for React integration.\n */\n\nimport type { RecordData } from './types'\n\n/**\n * Immutable snapshot handed to `useSyncExternalStore`. A new object is minted\n * on every state transition (see `commit`), so a change to `status`/`error` —\n * not just `records` — flips the snapshot reference and re-renders the hook.\n * Without `status`/`error` in the snapshot, an error that arrives before the\n * first result (records reference unchanged) never propagates and the UI is\n * stuck in \"loading\" forever.\n */\nexport interface QuerySnapshot {\n status: 'loading' | 'ready' | 'error'\n records: RecordData[]\n error?: string\n}\n\ninterface QueryState {\n version: number // Incremented on every change\n snapshot: QuerySnapshot // Stable reference until any field changes\n}\n\n// Stable empty array - never changes reference\nconst EMPTY_RECORDS: RecordData[] = []\n// Stable snapshot for queries that don't exist yet.\nconst EMPTY_SNAPSHOT: QuerySnapshot = { status: 'loading', records: EMPTY_RECORDS }\n\nexport class RecordStore {\n private queries = new Map<string, QueryState>()\n private listeners = new Map<string, Set<() => void>>()\n /** Reference count for each query - only send SUBSCRIBE on first, UNSUBSCRIBE on last */\n private refCounts = new Map<string, number>()\n /** Track the single subscriptionId per queryKey (for deduplication) */\n private activeSubscriptions = new Map<string, string>()\n\n subscribe(queryKey: string, listener: () => void): () => void {\n const set = this.listeners.get(queryKey) ?? new Set()\n set.add(listener)\n this.listeners.set(queryKey, set)\n return () => {\n set.delete(listener)\n if (set.size === 0) {\n this.listeners.delete(queryKey)\n }\n }\n }\n\n /**\n * Snapshot for `useSyncExternalStore`. Bundles records + status + error so a\n * transition in any of them re-renders the hook. Returns a stable reference\n * until the next `commit()`.\n */\n getSnapshot(queryKey: string): QuerySnapshot {\n return this.queries.get(queryKey)?.snapshot ?? EMPTY_SNAPSHOT\n }\n\n /**\n * Initialize a query subscription.\n * Returns true if this is the FIRST subscriber (should send SUBSCRIBE).\n */\n initQuery(queryKey: string): boolean {\n const prevCount = this.refCounts.get(queryKey) ?? 0\n this.refCounts.set(queryKey, prevCount + 1)\n\n if (!this.queries.has(queryKey)) {\n this.queries.set(queryKey, {\n version: 0,\n snapshot: { status: 'loading', records: EMPTY_RECORDS },\n })\n }\n\n // Return true if this is the first subscriber\n return prevCount === 0\n }\n\n /**\n * Release a query subscription.\n * Returns true if this was the LAST subscriber (should send UNSUBSCRIBE).\n */\n releaseQuery(queryKey: string): boolean {\n const count = (this.refCounts.get(queryKey) ?? 1) - 1\n\n if (count <= 0) {\n this.refCounts.delete(queryKey)\n this.activeSubscriptions.delete(queryKey)\n return true // Last subscriber\n }\n\n this.refCounts.set(queryKey, count)\n return false // Still has subscribers\n }\n\n /** Store the subscriptionId for a queryKey (for routing server responses) */\n setSubscriptionId(queryKey: string, subscriptionId: string): void {\n this.activeSubscriptions.set(queryKey, subscriptionId)\n }\n\n /** Get the subscriptionId for a queryKey */\n getSubscriptionId(queryKey: string): string | undefined {\n return this.activeSubscriptions.get(queryKey)\n }\n\n /**\n * Replace the state for a query with a fresh snapshot and bump the version,\n * then notify listeners. Single write-path so every mutator mints a new\n * snapshot reference (which is what drives `useSyncExternalStore` re-renders).\n */\n private commit(queryKey: string, snapshot: QuerySnapshot): void {\n const version = (this.queries.get(queryKey)?.version ?? 0) + 1\n this.queries.set(queryKey, { version, snapshot })\n this.notify(queryKey)\n }\n\n // Handle QUERY_RESULT from server\n setQueryResult(queryKey: string, records: RecordData[]): void {\n this.commit(queryKey, { status: 'ready', records })\n }\n\n /** Check if a record exists in a query's results */\n hasRecord(queryKey: string, recordId: string): boolean {\n const state = this.queries.get(queryKey)\n if (!state) return false\n return state.snapshot.records.some((r) => r.recordId === recordId)\n }\n\n // Handle RECORD_CHANGE from server\n applyChange(\n queryKey: string,\n record: RecordData,\n changeType: 'create' | 'update' | 'delete',\n ): void {\n const state = this.queries.get(queryKey)\n if (!state) return\n const records = state.snapshot.records\n\n let newRecords: RecordData[]\n\n if (changeType === 'delete') {\n newRecords = records.filter((r) => r.recordId !== record.recordId)\n } else if (changeType === 'create') {\n // Check if record already exists (prevent duplicates from race conditions)\n const exists = records.some((r) => r.recordId === record.recordId)\n if (exists) {\n // If it exists, treat as update instead\n newRecords = records.map((r) => (r.recordId === record.recordId ? record : r))\n } else {\n // Append (correct for asc order, which is the common case for real-time data)\n newRecords = [...records, record]\n }\n } else {\n // update\n newRecords = records.map((r) => (r.recordId === record.recordId ? record : r))\n }\n\n // Carry the existing status AND error through: a RECORD_CHANGE can arrive\n // while a query is in `status: 'error'`, and dropping `error` here would\n // leave consumers reading an error state with no message.\n this.commit(queryKey, {\n status: state.snapshot.status,\n records: newRecords,\n error: state.snapshot.error,\n })\n }\n\n /**\n * Reset a query to loading state (used on WebSocket reconnect).\n * Preserves stale records so the UI can show them while reconnecting.\n */\n resetToLoading(queryKey: string): void {\n const current = this.queries.get(queryKey)\n if (!current) return\n this.commit(queryKey, { status: 'loading', records: current.snapshot.records })\n }\n\n setError(queryKey: string, error: string): void {\n const records = this.queries.get(queryKey)?.snapshot.records ?? EMPTY_RECORDS\n this.commit(queryKey, { status: 'error', records, error })\n }\n\n // Cleanup when query is no longer needed\n removeQuery(queryKey: string): void {\n this.queries.delete(queryKey)\n this.listeners.delete(queryKey)\n }\n\n private notify(queryKey: string): void {\n const set = this.listeners.get(queryKey)\n if (set) {\n set.forEach((fn) => fn())\n }\n }\n}\n","import type { RecordData } from './types'\n\n/**\n * Client-side mirror of the server's where-builder (executeTableQuery):\n * schema columns take precedence over envelope fields so initial load and\n * live updates agree when a schema defines a column named 'createdBy'.\n * ONE copy — context.tsx and RecordScope.tsx both filter live changes\n * through this; a divergence shows up as records flickering in one provider\n * and not the other.\n */\nexport function recordMatchesWhere(\n record: RecordData,\n where?: Record<string, unknown>,\n): boolean {\n if (!where) return true\n const r = record as RecordData & { data?: Record<string, unknown> }\n const data = r.data\n if (!data) return false\n return Object.entries(where).every(([key, value]) => {\n if (key in data) return data[key] === value\n if (key === 'recordId') return r.recordId === value\n if (key === 'createdBy') return r.createdBy === value\n return false\n })\n}\n\n/** Shared reconnect backoff: 1s doubling to a 30s ceiling. */\nexport function reconnectDelayMs(attempt: number): number {\n return Math.min(1000 * Math.pow(2, attempt), 30000)\n}\n","/**\n * Server Error Parser\n * \n * Parses server-side error strings into user-friendly messages.\n * Covers RBAC permission errors, validation errors, and generic errors.\n */\n\nexport interface ServerErrorInfo {\n title: string\n detail: string\n /** True for RBAC/permission errors (routed to onPermissionError). False for validation/other (routed to onValidationError). */\n isPermissionError: boolean\n}\n\n/**\n * Humanize a collection slug (e.g. \"rbac-notes\" → \"Notes\", \"team-posts\" → \"Team Posts\").\n */\nfunction humanizeCollection(slug: string): string {\n return slug\n .replace(/^rbac-/, '')\n .split(/[-_]/)\n .map(w => w.charAt(0).toUpperCase() + w.slice(1))\n .join(' ')\n}\n\n/**\n * Humanize a role name for display (e.g. \"viewer\" → \"Viewer\").\n */\nfunction humanizeRole(role: string): string {\n return role.charAt(0).toUpperCase() + role.slice(1)\n}\n\n/**\n * Parse a server error string into a user-friendly message.\n * Handles RBAC errors, validation errors, and falls back to a generic error.\n */\nexport function parseServerError(error: string): ServerErrorInfo {\n // ── RBAC Errors ──────────────────────────────────────────────────────\n\n // UPDATE DENIED: role=viewer, collection=tasks\n const updateMatch = error.match(/^UPDATE DENIED: role=(\\w+), collection=(.+)/)\n if (updateMatch) {\n return {\n title: `${humanizeRole(updateMatch[1])}s can't edit ${humanizeCollection(updateMatch[2])}`,\n detail: '',\n isPermissionError: true,\n }\n }\n\n const createMatch = error.match(/^CREATE DENIED: role=(\\w+), collection=(.+)/)\n if (createMatch) {\n return {\n title: `${humanizeRole(createMatch[1])}s can't create ${humanizeCollection(createMatch[2])}`,\n detail: '',\n isPermissionError: true,\n }\n }\n\n const deleteMatch = error.match(/^DELETE DENIED: role=(\\w+), collection=(.+)/)\n if (deleteMatch) {\n return {\n title: `${humanizeRole(deleteMatch[1])}s can't delete ${humanizeCollection(deleteMatch[2])}`,\n detail: '',\n isPermissionError: true,\n }\n }\n\n const readMatch = error.match(/^READ DENIED: role=(\\w+), collection=(.+)/)\n if (readMatch) {\n return {\n title: `${humanizeRole(readMatch[1])}s can't view ${humanizeCollection(readMatch[2])}`,\n detail: '',\n isPermissionError: true,\n }\n }\n\n const fieldMatch = error.match(/^FIELD ERROR: Cannot update field '(\\w+)'.*role '(\\w+)'/)\n if (fieldMatch) {\n return {\n title: `${humanizeRole(fieldMatch[2])}s can't edit \"${fieldMatch[1]}\"`,\n detail: '',\n isPermissionError: true,\n }\n }\n\n const fieldGenericMatch = error.match(/^FIELD ERROR: (.+)/)\n if (fieldGenericMatch) {\n return {\n title: 'Field not editable',\n detail: '',\n isPermissionError: true,\n }\n }\n\n const permMatch = error.match(/^Permission denied: (.+)/)\n if (permMatch) {\n return {\n title: permMatch[1].charAt(0).toUpperCase() + permMatch[1].slice(1),\n detail: '',\n isPermissionError: true,\n }\n }\n\n // ── Validation Errors ────────────────────────────────────────────────\n\n // Missing required field: fieldName\n const requiredMatch = error.match(/^Missing required field: (.+)/)\n if (requiredMatch) {\n return {\n title: 'Missing field',\n detail: `\"${requiredMatch[1]}\" is required`,\n isPermissionError: false,\n }\n }\n\n // Cannot change immutable field: fieldName\n const immutableMatch = error.match(/^Cannot change immutable field: (.+)/)\n if (immutableMatch) {\n return {\n title: 'Cannot modify',\n detail: `\"${immutableMatch[1]}\" cannot be changed after creation`,\n isPermissionError: false,\n }\n }\n\n // Field 'x' must be your user ID\n const userBoundMatch = error.match(/^Field '(.+)' must be your user ID/)\n if (userBoundMatch) {\n return {\n title: 'Invalid value',\n detail: `\"${userBoundMatch[1]}\" must be your own user ID`,\n isPermissionError: false,\n }\n }\n\n // ── Fallback ─────────────────────────────────────────────────────────\n\n return {\n title: 'Error',\n detail: error,\n isPermissionError: false,\n }\n}\n","/**\n * RecordSocket — the ONE WebSocket engine behind both providers\n * (context.tsx's backward-compat RecordProvider and RecordScope).\n *\n * Framework-free: React never appears here. The providers own React state\n * and wire it up via the listener callbacks; everything race-sensitive —\n * the connect-token guard around the async auth-token fetch, reconnect\n * backoff, pending-request settlement, resubscribe-on-open, zombie-reconnect\n * prevention on teardown — lives here, under direct unit test\n * (__tests__/record-socket.test.ts).\n *\n * History: this logic used to exist twice (~250 lines each) and the copies\n * drifted three ways before being unified. Do not fork it again.\n */\n\nimport { recordMatchesWhere, reconnectDelayMs } from './record-matching'\nimport { parseServerError } from './serverErrors'\nimport type { CollectionSchema } from '../../shared/types'\nimport type { RoomUser, RoomConnectionState, RecordData } from './types'\nimport { MSG } from '../../shared/protocol/constants'\n\n// WebSocket readyState per spec — avoids touching the global WebSocket\n// object for constants (tests inject WebSocketImpl; node may lack a global).\nconst WS_CONNECTING = 0\nconst WS_OPEN = 1\n\n/** The slice of RecordStore the engine writes to (structural, for tests). */\nexport interface RecordStoreLike {\n setQueryResult(queryKey: string, records: RecordData[]): void\n applyChange(queryKey: string, record: RecordData, changeType: 'create' | 'update' | 'delete'): void\n hasRecord(queryKey: string, recordId: string): boolean\n setError(queryKey: string, error: string): void\n resetToLoading(queryKey: string): void\n}\n\nexport interface RecordSocketListeners {\n onStatus(status: RoomConnectionState): void\n onReady(ready: boolean): void\n onRole(role: string | null): void\n onUsers(users: RoomUser[]): void\n /** MSG.LIST_SCHEMAS — optional; the compat provider has no schemas surface. */\n onSchemas?(schemas: CollectionSchema[]): void\n onPermissionError?(title: string, detail: string): void\n onValidationError?(title: string, detail: string): void\n}\n\nexport interface RecordSocketConfig {\n roomId: string\n store: RecordStoreLike\n /** Fetch the auth token for the connection URL. Failures are swallowed —\n * the socket connects tokenless and the server decides (anonymous rooms\n * work; authed rooms reject). */\n getToken: () => Promise<string | null>\n listeners: RecordSocketListeners\n /** http(s)/ws(s) base; defaults to window.location. */\n wsUrl?: string\n /** Default '/ws'. */\n wsPathPrefix?: string\n /** Extra query params (e.g. { appId }). The token param is added by connect. */\n extraParams?: Record<string, string>\n /** Diagnostics hook (wsLog/debugLog per provider). */\n log?: (event: string, detail?: unknown) => void\n /** Test injection. Defaults to globalThis.WebSocket. */\n WebSocketImpl?: typeof WebSocket\n /**\n * Persistent registries, owned by the CALLER when the socket can be\n * recreated (auth identity change): hooks register subscriptions/handlers\n * once on mount, so they must survive a socket swap. Default to fresh\n * per-instance containers.\n */\n subscriptions?: Map<string, string>\n binaryHandlers?: Set<(data: ArrayBuffer) => void>\n yjsJoinHandlers?: Map<string, Set<(canWrite: boolean) => void>>\n}\n\ninterface PendingRequest {\n resolve: (data?: unknown) => void\n reject: (error: Error) => void\n timer: ReturnType<typeof setTimeout>\n}\n\nexport class RecordSocket {\n private ws: WebSocket | null = null\n private readonly subscriptions: Map<string, string> // subscriptionId → queryKey\n private readonly binaryHandlers: Set<(data: ArrayBuffer) => void>\n private readonly yjsJoinHandlers: Map<string, Set<(canWrite: boolean) => void>>\n private readonly pendingRequests = new Map<string, PendingRequest>()\n private reconnectAttempt = 0\n private reconnectTimeout: ReturnType<typeof setTimeout> | null = null\n // Bumped by connect() (on entry) and disconnect()/destroy(). connect()\n // captures it before awaiting the auth token and re-checks after; a\n // mismatch means a teardown (or a newer connect) raced in during the\n // await, so the resolved connect must bail instead of leaking a socket.\n private connectToken = 0\n // Opaque identity the current socket was opened with (e.g. '' for\n // anonymous, the profile id once signed in). A connect() with a DIFFERENT\n // tag while OPEN forces a reconnect so the server sees the new identity.\n private identityTag: string | undefined\n private destroyed = false\n\n constructor(private readonly config: RecordSocketConfig) {\n this.subscriptions = config.subscriptions ?? new Map()\n this.binaryHandlers = config.binaryHandlers ?? new Set()\n this.yjsJoinHandlers = config.yjsJoinHandlers ?? new Map()\n }\n\n // ── lifecycle ─────────────────────────────────────────────────────────────\n\n async connect(identityTag?: string): Promise<void> {\n if (this.destroyed) return\n const { config } = this\n\n if (this.ws?.readyState === WS_OPEN) {\n if (identityTag !== undefined && identityTag !== this.identityTag) {\n // Identity changed mid-connection (e.g. anonymous → signed-in): the\n // server derives identity from the connection's JWT, so reconnect.\n config.log?.('reconnecting with new identity', config.roomId)\n this.teardownSocket()\n } else {\n return\n }\n }\n if (this.ws?.readyState === WS_CONNECTING) return\n\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout)\n this.reconnectTimeout = null\n }\n\n const myToken = ++this.connectToken\n\n const params = new URLSearchParams()\n // Identity is derived server-side from the verified JWT — any\n // client-supplied identity params would be stripped at the worker edge,\n // so the token is the only identity we send.\n try {\n const token = await config.getToken()\n if (token) params.set('token', token)\n } catch {\n /* token fetch failed — connect tokenless; the server decides */\n }\n\n // A teardown or newer connect() raced in while we awaited the token —\n // bail so we don't open (and leak) a socket the caller already tore\n // down, whose onclose would then schedule a zombie reconnect.\n if (this.connectToken !== myToken || this.destroyed) return\n\n for (const [key, value] of Object.entries(config.extraParams ?? {})) {\n params.set(key, value)\n }\n\n // Only an explicit tag overwrites the remembered identity — a bare\n // connect() (visibility retry, scheduled reconnect) keeps the last one.\n if (identityTag !== undefined) this.identityTag = identityTag\n\n const protocol =\n typeof window !== 'undefined' && window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const baseUrl =\n config.wsUrl?.replace(/^http/, 'ws') ??\n `${protocol}//${typeof window !== 'undefined' ? window.location.host : ''}`\n const pathPrefix = config.wsPathPrefix ?? '/ws'\n const url = `${baseUrl}${pathPrefix}/${config.roomId}?${params.toString()}`\n\n config.log?.('connecting', config.roomId)\n const WS = config.WebSocketImpl ?? WebSocket\n const ws = new WS(url)\n ws.binaryType = 'arraybuffer'\n this.ws = ws\n\n ws.onopen = () => {\n config.log?.('connected', config.roomId)\n config.listeners.onStatus('connected')\n // Re-subscribe every active query. After a plain socket drop the\n // server has no memory of our subscriptions and sends no RESUBSCRIBE\n // (that only fires on membership changes) — without this, a\n // reconnected client silently stops receiving live updates.\n this.resubscribeAll(ws)\n }\n\n ws.onmessage = (event) => this.handleMessage(event)\n\n ws.onclose = () => {\n config.log?.('disconnected', config.roomId)\n config.listeners.onStatus('disconnected')\n config.listeners.onReady(false)\n this.ws = null\n for (const pending of this.pendingRequests.values()) {\n clearTimeout(pending.timer)\n pending.reject(new Error('WebSocket disconnected'))\n }\n this.pendingRequests.clear()\n // Queries show loading (not silently-stale data) until the reconnect\n // resubscribes and fresh results land.\n for (const queryKey of this.subscriptions.values()) {\n config.store.resetToLoading(queryKey)\n }\n const attempt = this.reconnectAttempt\n const delay = reconnectDelayMs(attempt)\n config.log?.('scheduling reconnect', { roomId: config.roomId, attempt, delayMs: delay })\n this.reconnectAttempt = attempt + 1\n this.reconnectTimeout = setTimeout(() => void this.connect(this.identityTag), delay)\n }\n\n ws.onerror = () => {}\n }\n\n /**\n * Tear down and reset to a connectable state (auth identity change, scope\n * unmount-and-remount). Emits status 'connecting' so the UI shows progress\n * rather than a dead 'disconnected'.\n */\n disconnect(): void {\n this.connectToken++ // invalidate any in-flight connect() awaiting its token\n this.clearReconnect()\n this.teardownSocket()\n this.config.listeners.onStatus('connecting')\n this.config.listeners.onReady(false)\n this.reconnectAttempt = 0\n }\n\n /** Final teardown (unmount): no listener calls — the owner is gone. */\n destroy(): void {\n this.destroyed = true\n this.connectToken++\n this.clearReconnect()\n this.teardownSocket()\n }\n\n /** Zero the backoff (tab became visible, user asked to retry). */\n resetBackoff(): void {\n this.reconnectAttempt = 0\n }\n\n get isOpen(): boolean {\n return this.ws?.readyState === WS_OPEN\n }\n\n private clearReconnect(): void {\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout)\n this.reconnectTimeout = null\n }\n }\n\n /** Close without firing onclose — prevents the zombie-reconnect. */\n private teardownSocket(): void {\n const ws = this.ws\n if (!ws) return\n this.config.log?.('closing', this.config.roomId)\n ws.onclose = null\n ws.onmessage = null\n ws.onerror = null\n ws.onopen = null\n ws.close()\n this.ws = null\n }\n\n // ── sending ───────────────────────────────────────────────────────────────\n\n sendMessage(message: { type: string; payload: unknown }): void {\n if (this.ws?.readyState === WS_OPEN) this.ws.send(JSON.stringify(message))\n else this.config.log?.('send dropped (not connected)', message.type)\n }\n\n sendBinary(data: Uint8Array): void {\n if (this.ws?.readyState === WS_OPEN) this.ws.send(data)\n }\n\n sendConfirmed(\n message: { type: string; payload: Record<string, unknown> },\n timeoutMs = 10000,\n ): Promise<unknown> {\n const ws = this.ws\n if (!ws || ws.readyState !== WS_OPEN) {\n return Promise.reject(new Error('WebSocket not connected'))\n }\n const requestId = `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pendingRequests.delete(requestId)\n reject(new Error('Mutation confirmation timed out'))\n }, timeoutMs)\n this.pendingRequests.set(requestId, { resolve, reject, timer })\n ws.send(JSON.stringify({ ...message, payload: { ...message.payload, requestId } }))\n })\n }\n\n // ── registrations ─────────────────────────────────────────────────────────\n\n registerSubscription(subscriptionId: string, queryKey: string): void {\n this.subscriptions.set(subscriptionId, queryKey)\n }\n\n unregisterSubscription(subscriptionId: string): void {\n this.subscriptions.delete(subscriptionId)\n }\n\n onBinaryMessage(handler: (data: ArrayBuffer) => void): () => void {\n this.binaryHandlers.add(handler)\n return () => {\n this.binaryHandlers.delete(handler)\n }\n }\n\n registerYjsJoinHandler(docKey: string, handler: (canWrite: boolean) => void): () => void {\n if (!this.yjsJoinHandlers.has(docKey)) this.yjsJoinHandlers.set(docKey, new Set())\n this.yjsJoinHandlers.get(docKey)!.add(handler)\n return () => {\n const handlers = this.yjsJoinHandlers.get(docKey)\n if (handlers) {\n handlers.delete(handler)\n if (handlers.size === 0) this.yjsJoinHandlers.delete(docKey)\n }\n }\n }\n\n // ── message dispatch ──────────────────────────────────────────────────────\n\n private resubscribeAll(ws: WebSocket): void {\n for (const [subscriptionId, queryKey] of this.subscriptions) {\n try {\n const query = JSON.parse(queryKey)\n ws.send(JSON.stringify({ type: MSG.SUBSCRIBE, payload: { subscriptionId, query } }))\n } catch {\n /* skip invalid query keys */\n }\n }\n }\n\n private handleMessage(event: MessageEvent): void {\n if (event.data instanceof ArrayBuffer) {\n this.binaryHandlers.forEach((h) => h(event.data as ArrayBuffer))\n return\n }\n\n let msg: { type: string; payload: unknown }\n try {\n msg = JSON.parse(event.data as string)\n } catch {\n console.error('[RecordSocket] Failed to parse message')\n return\n }\n\n const { type, payload } = msg\n const { store, listeners } = this.config\n\n switch (type) {\n case MSG.USER_INFO: {\n const { role } = payload as { role: string }\n listeners.onRole(role)\n listeners.onReady(true)\n this.reconnectAttempt = 0\n // Auto-request the user list as part of the connection handshake.\n this.sendMessage({ type: MSG.USER_LIST, payload: {} })\n break\n }\n\n case MSG.USER_LIST:\n listeners.onUsers((payload as { users: RoomUser[] }).users)\n break\n\n case MSG.QUERY_RESULT: {\n const { subscriptionId, records } = payload as {\n subscriptionId: string\n records: RecordData[]\n }\n const queryKey = this.subscriptions.get(subscriptionId)\n if (queryKey !== undefined) store.setQueryResult(queryKey, records)\n break\n }\n\n case MSG.RECORD_CHANGE: {\n const { collection, record, changeType } = payload as {\n collection: string\n record: RecordData\n changeType: 'create' | 'update' | 'delete'\n }\n for (const queryKey of this.subscriptions.values()) {\n try {\n const query = JSON.parse(queryKey) as {\n collection: string\n where?: Record<string, unknown>\n }\n if (query.collection !== collection) continue\n const matches = recordMatchesWhere(record, query.where)\n const exists = store.hasRecord(queryKey, record.recordId)\n if (changeType === 'delete') {\n if (exists) store.applyChange(queryKey, record, 'delete')\n } else if (changeType === 'create') {\n if (matches) store.applyChange(queryKey, record, 'create')\n } else {\n if (matches && exists) store.applyChange(queryKey, record, 'update')\n else if (matches && !exists) store.applyChange(queryKey, record, 'create')\n else if (!matches && exists) store.applyChange(queryKey, record, 'delete')\n }\n } catch {\n /* skip invalid query keys */\n }\n }\n break\n }\n\n case MSG.ERROR: {\n const { subscriptionId, error } = payload as { subscriptionId?: string; error: string }\n if (subscriptionId) {\n const queryKey = this.subscriptions.get(subscriptionId)\n if (queryKey !== undefined) store.setError(queryKey, error)\n } else {\n const parsed = parseServerError(error)\n if (parsed.isPermissionError) listeners.onPermissionError?.(parsed.title, parsed.detail)\n else listeners.onValidationError?.(parsed.title, parsed.detail)\n }\n break\n }\n\n case MSG.YJS_JOIN: {\n const { collection, recordId, fieldName, canWrite } = payload as {\n collection: string\n recordId: string\n fieldName: string\n canWrite: boolean\n }\n const docKey = `${collection}:${recordId}:${fieldName}`\n this.yjsJoinHandlers.get(docKey)?.forEach((h) => h(canWrite))\n break\n }\n\n case MSG.ACK: {\n const { requestId, success, error, ...rest } = payload as {\n requestId: string\n success: boolean\n error?: string\n [key: string]: unknown\n }\n const pending = this.pendingRequests.get(requestId)\n if (pending) {\n clearTimeout(pending.timer)\n this.pendingRequests.delete(requestId)\n if (success) pending.resolve(rest)\n else pending.reject(new Error(error || 'Mutation rejected'))\n }\n break\n }\n\n case MSG.LIST_SCHEMAS: {\n const { schemas } = payload as { schemas: CollectionSchema[] }\n listeners.onSchemas?.(schemas ?? [])\n break\n }\n\n case MSG.RESUBSCRIBE: {\n // Team membership changed — re-subscribe all active queries.\n if (this.ws?.readyState === WS_OPEN) this.resubscribeAll(this.ws)\n break\n }\n }\n }\n}\n","/**\n * ScopeRegistry\n *\n * React context that maps collection names to scope entries.\n * RecordScope registers its collections on mount, and hooks\n * (useQuery, useMutations) resolve the correct scope by collection name.\n *\n * Each registration is tagged with a scopeId so that unregister only\n * removes entries owned by the calling scope — preventing one scope\n * from breaking another when both register the same collections.\n */\n\nimport { createContext, useContext, useCallback, useRef, useMemo, type ReactNode } from 'react'\nimport type { RecordStore } from './store'\nimport type { RoomConnectionState } from './types'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ScopeEntry {\n store: RecordStore\n sendMessage: (msg: { type: string; payload: unknown }) => void\n sendConfirmed: (\n msg: { type: string; payload: Record<string, unknown> },\n timeoutMs?: number,\n ) => Promise<unknown>\n registerSubscription: (subscriptionId: string, queryKey: string) => void\n unregisterSubscription: (subscriptionId: string) => void\n sendBinary: (data: Uint8Array) => void\n onBinaryMessage: (handler: (data: ArrayBuffer) => void) => () => void\n registerYjsJoinHandler: (\n docKey: string,\n handler: (canWrite: boolean) => void,\n ) => () => void\n ready: boolean\n status: RoomConnectionState\n}\n\ninterface Registration {\n scopeId: string\n entry: ScopeEntry\n}\n\ninterface ScopeRegistryValue {\n register: (scopeId: string, collections: string[], entry: ScopeEntry) => void\n unregister: (scopeId: string, collections: string[]) => void\n resolve: (collection: string) => ScopeEntry | null\n}\n\n// ============================================================================\n// Context\n// ============================================================================\n\nconst ScopeRegistryContext = createContext<ScopeRegistryValue | null>(null)\n\nexport function useScopeRegistry(): ScopeRegistryValue | null {\n return useContext(ScopeRegistryContext)\n}\n\n// ============================================================================\n// Provider\n// ============================================================================\n\nexport function ScopeRegistryProvider({ children }: { children: ReactNode }) {\n const mapRef = useRef(new Map<string, Registration>())\n\n const register = useCallback((scopeId: string, collections: string[], entry: ScopeEntry) => {\n for (const name of collections) {\n mapRef.current.set(name, { scopeId, entry })\n }\n }, [])\n\n const unregister = useCallback((scopeId: string, collections: string[]) => {\n for (const name of collections) {\n const existing = mapRef.current.get(name)\n // Only unregister if this scope owns the registration\n if (existing?.scopeId === scopeId) {\n mapRef.current.delete(name)\n }\n }\n }, [])\n\n const resolve = useCallback((collection: string): ScopeEntry | null => {\n return mapRef.current.get(collection)?.entry ?? null\n }, [])\n\n const value = useMemo(() => ({ register, unregister, resolve }), [register, unregister, resolve])\n\n return (\n <ScopeRegistryContext.Provider value={value}>\n {children}\n </ScopeRegistryContext.Provider>\n )\n}\n","/**\n * RecordScope\n *\n * Opens a direct WebSocket connection to a RecordRoom DO.\n * Manages subscriptions, reconnection, and state via RecordStore.\n * Registers collections in ScopeRegistry so hooks resolve to the right scope.\n *\n * @example\n * ```tsx\n * <RecordProvider>\n * <RecordScope\n * roomId=\"app:my-app\"\n * schemas={appSchemas}\n * appId=\"my-app\"\n * sharedScopes={[\n * { roomId: 'workspace:default', schemas: workspaceSchemas },\n * ]}\n * >\n * <App />\n * </RecordScope>\n * </RecordProvider>\n * ```\n */\n\nimport React, { useEffect, useState, useCallback, useRef, useMemo, type ReactNode } from 'react'\nimport type { CollectionSchema } from '../../shared/types'\nimport { RecordContext, type RecordContextValue } from './context'\nimport { useRecordAuth } from './context'\nimport { RecordStore } from './store'\nimport { RecordSocket } from './record-socket'\nimport { useScopeRegistry, type ScopeEntry } from './ScopeRegistry'\nimport { getAuthToken } from '../auth'\nimport { wsLog } from './ws-log'\nimport type { RoomConnectionState, RoomUser } from './types'\nimport { MSG } from '@/shared/protocol/constants'\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface SharedScopeConfig {\n roomId: string\n schemas: CollectionSchema[]\n}\n\ninterface RecordScopeProps {\n roomId: string\n schemas: CollectionSchema[]\n children?: ReactNode\n /** App ID passed to the server for schema resolution. */\n appId: string\n /** Additional scopes to connect (headless — no children, just register collections). */\n sharedScopes?: SharedScopeConfig[]\n /** WebSocket base URL override. Derived from window.location if omitted. */\n wsUrl?: string\n /** Path prefix for WebSocket route. Default: '/ws'. */\n wsPathPrefix?: string\n /** Don't register collections in ScopeRegistry (prevents name collisions). */\n isolated?: boolean\n}\n\n// ============================================================================\n// Headless scope — opens a WS connection and registers collections only\n// ============================================================================\n\nfunction HeadlessScope({\n roomId,\n schemas,\n appId,\n wsUrl,\n wsPathPrefix,\n}: {\n roomId: string\n schemas: CollectionSchema[]\n appId: string\n wsUrl?: string\n wsPathPrefix?: string\n}) {\n return (\n <ScopeConnection\n roomId={roomId}\n schemas={schemas}\n appId={appId}\n wsUrl={wsUrl}\n wsPathPrefix={wsPathPrefix}\n />\n )\n}\n\n// ============================================================================\n// Core WebSocket connection logic (shared by primary + headless scopes)\n// ============================================================================\n\nfunction ScopeConnection({\n roomId,\n schemas,\n appId,\n children,\n wsUrl,\n wsPathPrefix = '/ws',\n isolated = false,\n}: {\n roomId: string\n schemas: CollectionSchema[]\n appId: string\n children?: ReactNode\n wsUrl?: string\n wsPathPrefix?: string\n isolated?: boolean\n}) {\n const auth = useRecordAuth()\n const registry = useScopeRegistry()\n\n const scopeIdRef = useRef(`scope-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`)\n\n // Room state\n const [roomRole, setRoomRole] = useState<string | null>(null)\n const [allUsers, setAllUsers] = useState<RoomUser[]>([])\n const [usersLoaded, setUsersLoaded] = useState(false)\n const [status, setStatus] = useState<RoomConnectionState>('connecting')\n const [ready, setReady] = useState(false)\n const [discoveredSchemas, setDiscoveredSchemas] = useState<CollectionSchema[]>([])\n\n // Refs\n const storeRef = useRef<RecordStore>(new RecordStore())\n const socketRef = useRef<RecordSocket | null>(null)\n // Persist across socket recreation (auth change) — hooks register these\n // on mount and never re-register for a socket swap.\n const subscriptionsRef = useRef<Map<string, string>>(new Map())\n const binaryHandlersRef = useRef<Set<(data: ArrayBuffer) => void>>(new Set())\n const yjsJoinHandlersRef = useRef<Map<string, Set<(canWrite: boolean) => void>>>(new Map())\n\n // Auth refs (stable across renders)\n const userProfileRef = useRef(auth?.userProfile ?? null)\n userProfileRef.current = auth?.userProfile ?? null\n const getAuthTokenRef = useRef(auth?.getAuthToken ?? null)\n getAuthTokenRef.current = auth?.getAuthToken ?? null\n const authCallbacksRef = useRef({\n onPermissionError: auth?.onPermissionError,\n onValidationError: auth?.onValidationError,\n })\n authCallbacksRef.current.onPermissionError = auth?.onPermissionError\n authCallbacksRef.current.onValidationError = auth?.onValidationError\n const allowAnonymous = auth?.allowAnonymous ?? false\n\n // ── Socket lifecycle ─────────────────────────────────────────────────\n // The engine (RecordSocket) owns everything race-sensitive: connect-token\n // guard, backoff, resubscribe-on-open, pending-request settlement. This\n // component owns React state and recreates the socket when the connection\n // identity (room, URL, app, or signed-in user) changes.\n\n const userProfileId = auth?.userProfile?.id ?? null\n const userProfileLoading = auth?.userProfileLoading ?? false\n\n useEffect(() => {\n // Still loading auth — wait (avoids an anonymous connect that is\n // immediately torn down when the profile lands).\n if (userProfileLoading) return\n\n const socket = new RecordSocket({\n roomId,\n store: storeRef.current,\n subscriptions: subscriptionsRef.current,\n binaryHandlers: binaryHandlersRef.current,\n yjsJoinHandlers: yjsJoinHandlersRef.current,\n getToken: () => (getAuthTokenRef.current ?? getAuthToken)(),\n wsUrl,\n wsPathPrefix,\n extraParams: { appId },\n log: (event) => wsLog(event as Parameters<typeof wsLog>[0], roomId),\n listeners: {\n onStatus: setStatus,\n onReady: setReady,\n onRole: setRoomRole,\n onUsers: (users) => {\n setAllUsers(users)\n setUsersLoaded(true)\n },\n onSchemas: setDiscoveredSchemas,\n onPermissionError: (title, detail) =>\n authCallbacksRef.current.onPermissionError?.(title, detail),\n onValidationError: (title, detail) =>\n authCallbacksRef.current.onValidationError?.(title, detail),\n },\n })\n socketRef.current = socket\n\n if (userProfileId || allowAnonymous) {\n void socket.connect(userProfileId ?? '')\n }\n\n return () => {\n socketRef.current = null\n socket.destroy()\n setStatus('connecting')\n setReady(false)\n }\n }, [roomId, wsUrl, wsPathPrefix, appId, userProfileId, userProfileLoading, allowAnonymous])\n\n // Reconnect on tab focus\n useEffect(() => {\n const onVisible = () => {\n if (document.visibilityState === 'visible' && !socketRef.current?.isOpen) {\n socketRef.current?.resetBackoff()\n void socketRef.current?.connect()\n }\n }\n document.addEventListener('visibilitychange', onVisible)\n return () => document.removeEventListener('visibilitychange', onVisible)\n }, [])\n\n // ── Send helpers (stable delegates to the engine) ────────────────────\n\n const sendMessage = useCallback((message: { type: string; payload: unknown }) => {\n socketRef.current?.sendMessage(message)\n }, [])\n\n const sendBinary = useCallback((data: Uint8Array) => {\n socketRef.current?.sendBinary(data)\n }, [])\n\n const onBinaryMessage = useCallback((handler: (data: ArrayBuffer) => void) => {\n binaryHandlersRef.current.add(handler)\n return () => {\n binaryHandlersRef.current.delete(handler)\n }\n }, [])\n\n const registerYjsJoinHandler = useCallback(\n (docKey: string, handler: (canWrite: boolean) => void) => {\n const handlers = yjsJoinHandlersRef.current\n if (!handlers.has(docKey)) handlers.set(docKey, new Set())\n handlers.get(docKey)!.add(handler)\n return () => {\n const set = handlers.get(docKey)\n if (set) {\n set.delete(handler)\n if (set.size === 0) handlers.delete(docKey)\n }\n }\n },\n [],\n )\n\n const sendConfirmed = useCallback(\n (\n message: { type: string; payload: Record<string, unknown> },\n timeoutMs = 10000,\n ): Promise<unknown> => {\n const socket = socketRef.current\n if (!socket) return Promise.reject(new Error('WebSocket not connected'))\n return socket.sendConfirmed(message, timeoutMs)\n },\n [],\n )\n\n const setUserRole = useCallback(\n (userId: string, role: string) => {\n sendMessage({ type: MSG.SET_ROLE, payload: { userId, role } })\n },\n [sendMessage],\n )\n\n const requestUserList = useCallback(() => {\n sendMessage({ type: MSG.USER_LIST, payload: {} })\n }, [sendMessage])\n\n const registerSubscription = useCallback((subscriptionId: string, queryKey: string) => {\n subscriptionsRef.current.set(subscriptionId, queryKey)\n }, [])\n\n const unregisterSubscription = useCallback((subscriptionId: string) => {\n subscriptionsRef.current.delete(subscriptionId)\n }, [])\n\n // Auto-discover schemas\n useEffect(() => {\n if (ready) sendMessage({ type: MSG.LIST_SCHEMAS, payload: {} })\n }, [ready, sendMessage])\n\n // ── ScopeRegistry ────────────────────────────────────────────────────\n\n const scopeEntryRef = useRef<ScopeEntry | null>(null)\n if (!scopeEntryRef.current) {\n scopeEntryRef.current = {\n store: storeRef.current,\n sendMessage,\n sendConfirmed,\n registerSubscription,\n unregisterSubscription,\n sendBinary,\n onBinaryMessage,\n registerYjsJoinHandler,\n ready,\n status,\n }\n }\n scopeEntryRef.current.ready = ready\n scopeEntryRef.current.status = status\n\n const registeredSchemasRef = useRef<CollectionSchema[] | null>(null)\n if (!isolated && registry && schemas !== registeredSchemasRef.current) {\n if (registeredSchemasRef.current) {\n registry.unregister(\n scopeIdRef.current,\n registeredSchemasRef.current.map((s) => s.name),\n )\n }\n const names = schemas.map((s) => s.name)\n if (names.length > 0) registry.register(scopeIdRef.current, names, scopeEntryRef.current!)\n registeredSchemasRef.current = schemas\n }\n\n useEffect(() => {\n if (isolated || !registry) return\n // scopeId is assigned once at init and never reassigned; capture it so the\n // cleanup doesn't read a possibly-changed ref (it can't here, but the rule\n // can't prove that).\n const scopeId = scopeIdRef.current\n return () => {\n const names = (registeredSchemasRef.current ?? []).map((s) => s.name)\n if (names.length > 0) registry.unregister(scopeId, names)\n }\n }, [isolated, registry, roomId])\n\n // ── Context ──────────────────────────────────────────────────────────\n\n const registeredCollections = useMemo(() => new Set(schemas.map((s) => s.name)), [schemas])\n\n const value: RecordContextValue = useMemo(\n () => ({\n store: storeRef.current,\n roomId,\n registeredCollections,\n userProfile: auth?.userProfile ?? null,\n userProfileLoading: auth?.userProfileLoading ?? false,\n refetchUserProfile: auth?.refetchUserProfile ?? (async () => {}),\n roomRole,\n allUsers,\n usersLoaded,\n status,\n ready,\n discoveredSchemas,\n setUserRole,\n requestUserList,\n registerSubscription,\n unregisterSubscription,\n sendMessage,\n sendBinary,\n onBinaryMessage,\n registerYjsJoinHandler,\n sendConfirmed,\n }),\n [\n roomId,\n registeredCollections,\n auth?.userProfile,\n auth?.userProfileLoading,\n auth?.refetchUserProfile,\n roomRole,\n allUsers,\n usersLoaded,\n status,\n ready,\n discoveredSchemas,\n setUserRole,\n requestUserList,\n registerSubscription,\n unregisterSubscription,\n sendMessage,\n sendBinary,\n onBinaryMessage,\n registerYjsJoinHandler,\n sendConfirmed,\n ],\n )\n\n if (!children) return null\n return <RecordContext.Provider value={value}>{children}</RecordContext.Provider>\n}\n\n// ============================================================================\n// RecordScope (public API)\n// ============================================================================\n\nexport function RecordScope({\n roomId,\n schemas,\n children,\n appId,\n sharedScopes,\n wsUrl,\n wsPathPrefix = '/ws',\n isolated = false,\n}: RecordScopeProps) {\n return (\n <>\n {sharedScopes?.map((shared) => (\n <HeadlessScope\n key={shared.roomId}\n roomId={shared.roomId}\n schemas={shared.schemas}\n appId={appId}\n wsUrl={wsUrl}\n wsPathPrefix={wsPathPrefix}\n />\n ))}\n <ScopeConnection\n roomId={roomId}\n schemas={schemas}\n appId={appId}\n wsUrl={wsUrl}\n wsPathPrefix={wsPathPrefix}\n isolated={isolated}\n >\n {children}\n </ScopeConnection>\n </>\n )\n}\n","/**\n * Shared WebSocket connection logger with active connection count.\n * Silent unless DEEPSPACE_DEBUG is set (see ../debug).\n */\n\nimport { debugLog } from '../debug'\n\nlet activeCount = 0\n\nexport function wsLog(\n event: 'connecting' | 'connected' | 'disconnected' | 'closing',\n label: string,\n) {\n if (event === 'connected') activeCount++\n if (event === 'closing') activeCount--\n\n debugLog(`[ds:ws] ${event} → ${label} (${activeCount} active)`)\n}\n","/**\n * useUser Hook\n * \n * Access current user with profile data and room role.\n */\n\nimport { useMemo } from 'react'\nimport { useRecordContext } from '../context'\nimport type { User } from '../types'\n\n/**\n * Hook to access current user.\n * \n * Returns merged user data:\n * - Profile from API (karma, credits, subscription, isAdmin)\n * - Role from room (derived from user-roles collection)\n * \n * The role is the user's role in THIS miniapp/room, which can be\n * different from their global isAdmin status.\n * \n * @example\n * ```tsx\n * const { user, refetch, isLoading } = useUser()\n * \n * // Check role\n * if (user?.role === 'admin') { ... }\n * if (user?.role === 'intern') { ... }\n * \n * // Access karma/credits\n * const karma = user?.karma?.total ?? 0\n * \n * // Refetch after expensive operations\n * await refetch() // Updates user.credits\n * ```\n */\nexport function useUser(): {\n user: User | null\n isLoading: boolean\n refetch: () => Promise<void>\n} {\n const { userProfile, userProfileLoading, refetchUserProfile, roomRole } = useRecordContext()\n \n // Merge profile + room role into unified User\n const user = useMemo((): User | null => {\n if (!userProfile) return null\n return {\n ...userProfile,\n role: roomRole ?? 'viewer', // Default to 'viewer' if no role set\n }\n }, [userProfile, roomRole])\n \n return { user, isLoading: userProfileLoading, refetch: refetchUserProfile }\n}\n","/**\n * useQuery Hook\n *\n * Subscribe to a query with real-time updates.\n * Resolves the correct scope via ScopeRegistry (multi-scope)\n * or falls back to useRecordContext() (single-scope backward compat).\n */\n\nimport { useEffect, useCallback, useRef, useMemo, useContext, useSyncExternalStore } from 'react'\nimport { RecordContext } from '../context'\nimport { useScopeRegistry } from '../ScopeRegistry'\nimport type { Query, RecordData } from '../types'\nimport { MSG } from '@/shared/protocol/constants'\n\n/**\n * Subscribe to a query with real-time updates.\n *\n * @example\n * ```tsx\n * const { records, status } = useQuery('tasks', {\n * where: { userId: me },\n * orderBy: 'createdAt',\n * orderDir: 'desc',\n * limit: 50\n * })\n * ```\n */\nexport function useQuery<T = unknown>(\n collection: string,\n options?: {\n where?: Record<string, unknown>\n orderBy?: string\n orderDir?: 'asc' | 'desc'\n limit?: number\n },\n): {\n records: RecordData<T>[]\n status: 'loading' | 'ready' | 'error'\n error?: string\n} {\n // Try scope resolution first (multi-scope), then fall back to RecordContext\n const registry = useScopeRegistry()\n const recordCtx = useContext(RecordContext)\n\n const scopeEntry = registry?.resolve(collection) ?? null\n\n // Prefer RecordContext when this scope handles the collection (avoids stale ScopeRegistry during navigation)\n const preferLocal = recordCtx?.registeredCollections?.has(collection) ?? false\n const store = preferLocal ? recordCtx!.store : (scopeEntry?.store ?? recordCtx?.store)\n const sendMessage = preferLocal\n ? recordCtx!.sendMessage\n : (scopeEntry?.sendMessage ?? recordCtx?.sendMessage)\n const registerSub = preferLocal\n ? recordCtx!.registerSubscription\n : (scopeEntry?.registerSubscription ?? recordCtx?.registerSubscription)\n const unregisterSub = preferLocal\n ? recordCtx!.unregisterSubscription\n : (scopeEntry?.unregisterSubscription ?? recordCtx?.unregisterSubscription)\n\n if (!store || !sendMessage || !registerSub || !unregisterSub) {\n throw new Error(\n `useQuery('${collection}'): No scope found. Wrap in a RecordProvider (with roomId) or a RecordScope that registers this collection.`,\n )\n }\n\n const optionsKey = JSON.stringify(options ?? null)\n\n const query: Query = useMemo(\n () => ({\n collection,\n where: options?.where,\n orderBy: options?.orderBy,\n orderDir: options?.orderDir,\n limit: options?.limit,\n // Keyed by the serialized optionsKey rather than the raw options object\n // (a fresh object each render) so the query — and its WS subscription —\n // is only rebuilt when the options actually change.\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- keyed on serialized optionsKey, not raw options.where/orderBy/orderDir/limit objects, to avoid rebuilding the WS subscription every render\n [collection, optionsKey],\n )\n\n const queryKey = useMemo(() => JSON.stringify(query), [query])\n\n const subscriptionIdRef = useRef<string>(\n `sub-${Date.now()}-${Math.random().toString(36).slice(2)}`,\n )\n\n useEffect(() => {\n const subscriptionId = subscriptionIdRef.current\n\n const isFirst = store.initQuery(queryKey)\n\n if (isFirst) {\n store.setSubscriptionId(queryKey, subscriptionId)\n registerSub(subscriptionId, queryKey)\n sendMessage({ type: MSG.SUBSCRIBE, payload: { subscriptionId, query } })\n }\n\n return () => {\n const activeSubId = store.getSubscriptionId(queryKey) ?? subscriptionId\n const isLast = store.releaseQuery(queryKey)\n\n if (isLast) {\n sendMessage({ type: MSG.UNSUBSCRIBE, payload: { subscriptionId: activeSubId } })\n unregisterSub(activeSubId)\n }\n }\n }, [queryKey, query, store, sendMessage, registerSub, unregisterSub, collection])\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => store.subscribe(queryKey, onStoreChange),\n [store, queryKey],\n )\n\n const getSnapshot = useCallback(() => store.getSnapshot(queryKey), [store, queryKey])\n\n // The snapshot bundles records + status + error, so a transition in any of\n // them (e.g. an error before the first result) re-renders. Reading them off\n // the store separately would miss those transitions when `records` is\n // reference-stable.\n const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n const records = snapshot.records as RecordData<T>[]\n const { status, error } = snapshot\n\n // Memoize the return object so consumers get a stable reference\n // (same rationale as useMutations — prevents spurious effect re-runs).\n return useMemo(() => ({ records, status, error }), [records, status, error])\n}\n","/**\n * useMutations Hook\n *\n * Get mutation functions for a collection.\n * Resolves the correct scope via ScopeRegistry (multi-scope)\n * or falls back to useRecordContext() (single-scope backward compat).\n */\n\nimport { useCallback, useContext, useMemo } from 'react'\nimport { RecordContext } from '../context'\nimport { useScopeRegistry } from '../ScopeRegistry'\nimport { MSG } from '@/shared/protocol/constants'\n\n/**\n * Get mutation functions for a collection.\n *\n * Type contract:\n * - `create(data)` requires the **full** record shape `T`. New records\n * have no existing data to merge with, so all fields the schema\n * considers required must be present.\n * - `put(recordId, patch)` accepts a **`Partial<T>`**. The server\n * merges the patch into the existing row (`{...existing, ...patch}`),\n * so callers should send only the fields they want to change.\n *\n * @example\n * ```tsx\n * const { create, put, remove } = useMutations<Task>('tasks')\n *\n * // create needs the full Task shape\n * const id = await create({ title: 'New task', completed: false, priority: 1 })\n *\n * // put is a partial update — only the fields you want to change\n * await put(id, { completed: true })\n * await remove(id)\n * ```\n */\nexport function useMutations<T = unknown>(collection: string): {\n create: (data: T) => Promise<string>\n put: (recordId: string, data: Partial<T>) => Promise<void>\n remove: (recordId: string) => Promise<void>\n createConfirmed: (data: T) => Promise<string>\n putConfirmed: (recordId: string, data: Partial<T>) => Promise<void>\n removeConfirmed: (recordId: string) => Promise<void>\n} {\n // Try scope resolution first (multi-scope), then fall back to RecordContext\n const registry = useScopeRegistry()\n const recordCtx = useContext(RecordContext)\n\n const scopeEntry = registry?.resolve(collection) ?? null\n\n // Prefer RecordContext when this scope handles the collection (avoids stale ScopeRegistry during navigation)\n const preferLocal = recordCtx?.registeredCollections?.has(collection) ?? false\n const sendMessage = preferLocal ? recordCtx!.sendMessage : (scopeEntry?.sendMessage ?? recordCtx?.sendMessage)\n const sendConfirmed = preferLocal ? recordCtx!.sendConfirmed : (scopeEntry?.sendConfirmed ?? recordCtx?.sendConfirmed)\n\n if (!sendMessage || !sendConfirmed) {\n throw new Error(\n `useMutations('${collection}'): No scope found. Wrap in a RecordProvider (with roomId) or a RecordScope that registers this collection.`,\n )\n }\n\n const create = useCallback(\n async (data: T): Promise<string> => {\n const recordId = `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`\n sendMessage({ type: MSG.PUT, payload: { collection, recordId, data } })\n return recordId\n },\n [sendMessage, collection],\n )\n\n const put = useCallback(\n async (recordId: string, data: Partial<T>): Promise<void> => {\n sendMessage({ type: MSG.PUT, payload: { collection, recordId, data } })\n },\n [sendMessage, collection],\n )\n\n const remove = useCallback(\n async (recordId: string): Promise<void> => {\n sendMessage({ type: MSG.DELETE, payload: { collection, recordId } })\n },\n [sendMessage, collection],\n )\n\n const createConfirmed = useCallback(\n async (data: T): Promise<string> => {\n const recordId = `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`\n await sendConfirmed({\n type: MSG.PUT,\n payload: { collection, recordId, data: data as Record<string, unknown> },\n })\n return recordId\n },\n [sendConfirmed, collection],\n )\n\n const putConfirmed = useCallback(\n async (recordId: string, data: Partial<T>): Promise<void> => {\n await sendConfirmed({\n type: MSG.PUT,\n payload: { collection, recordId, data: data as Record<string, unknown> },\n })\n },\n [sendConfirmed, collection],\n )\n\n const removeConfirmed = useCallback(\n async (recordId: string): Promise<void> => {\n await sendConfirmed({ type: MSG.DELETE, payload: { collection, recordId } })\n },\n [sendConfirmed, collection],\n )\n\n // Memoize the return object so consumers get a stable reference.\n // Without this, every render produces a new object, which breaks any\n // useCallback/useEffect that depends on the useMutations() result\n // (e.g., cleanup effects re-fire and delete records they shouldn't).\n return useMemo(\n () => ({ create, put, remove, createConfirmed, putConfirmed, removeConfirmed }),\n [create, put, remove, createConfirmed, putConfirmed, removeConfirmed],\n )\n}\n","/**\n * useUsers Hook\n *\n * Access all users in the current room.\n * User data (name, email, imageUrl, role) comes from the RecordRoom's users collection.\n */\n\nimport { useEffect, useMemo } from 'react'\nimport { useRecordContext } from '../context'\nimport type { RoomUser } from '../types'\n\nexport function useUsers(): {\n users: RoomUser[]\n usersLoaded: boolean\n setRole: (userId: string, role: string) => void\n refresh: () => void\n} {\n const { allUsers, usersLoaded, setUserRole, requestUserList, ready } = useRecordContext()\n\n useEffect(() => {\n if (ready) requestUserList()\n }, [ready, requestUserList])\n\n const users = useMemo(() => allUsers, [allUsers])\n\n return { users, usersLoaded, setRole: setUserRole, refresh: requestUserList }\n}\n","/**\n * Yjs Collaborative Editing Hooks\n *\n * Real-time collaborative editing using Yjs.\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport * as Y from 'yjs'\nimport { useRecordContext } from '../context'\nimport { debugLog } from '../../debug'\nimport { MSG_YJS_SYNC, MSG_YJS_AWARENESS, MSG } from '@/shared/protocol/constants'\nimport {\n MSG_SYNC_STEP1,\n MSG_SYNC_STEP2,\n MSG_SYNC_UPDATE,\n Awareness,\n createEncoder,\n createDecoder,\n toUint8Array,\n writeVarUint,\n writeVarUint8Array,\n readVarUint,\n readVarUint8Array,\n} from '@/shared/protocol/yjs'\n\n// ============================================================================\n// useYjsField\n// ============================================================================\n\nexport interface UseYjsFieldResult {\n /** The Yjs document for this field */\n doc: Y.Doc\n /** Awareness instance for cursor/presence sync */\n awareness: Awareness\n /** Whether initial sync is complete */\n synced: boolean\n /** Whether user has write access */\n canWrite: boolean\n /** Force re-render counter (changes when doc updates) */\n updateCount: number\n}\n\n/**\n * Hook for collaborative editing of a Yjs field.\n *\n * @param collection - Collection name\n * @param recordId - Record ID\n * @param fieldName - Field name (must be type: 'yjs' in schema)\n *\n * @example\n * const { doc, synced, canWrite } = useYjsField('documents', docId, 'content')\n * const text = doc.getText('content')\n */\nexport function useYjsField(\n collection: string,\n recordId: string,\n fieldName: string,\n): UseYjsFieldResult {\n const { sendMessage, sendBinary, onBinaryMessage, registerYjsJoinHandler, ready } =\n useRecordContext()\n const [synced, setSynced] = useState(false)\n const [canWrite, setCanWrite] = useState(false)\n const [updateCount, setUpdateCount] = useState(0)\n const canWriteRef = useRef(false)\n\n // Stable Yjs doc instance\n const docRef = useRef<Y.Doc | null>(null)\n if (!docRef.current) {\n docRef.current = new Y.Doc()\n }\n const doc = docRef.current\n\n // Stable awareness instance (one per doc)\n const awarenessRef = useRef<Awareness | null>(null)\n if (!awarenessRef.current) {\n awarenessRef.current = new Awareness(doc)\n }\n const awareness = awarenessRef.current\n\n const docKey = `${collection}:${recordId}:${fieldName}`\n\n // Keep ref in sync with state for use in callbacks\n useEffect(() => {\n canWriteRef.current = canWrite\n }, [canWrite])\n\n // Handle join response from server\n useEffect(() => {\n return registerYjsJoinHandler(docKey, setCanWrite)\n }, [docKey, registerYjsJoinHandler])\n\n // Handle incoming binary messages (sync + awareness)\n useEffect(() => {\n const handleBinaryMessage = (data: ArrayBuffer) => {\n const decoder = createDecoder(new Uint8Array(data))\n const messageType = readVarUint(decoder)\n\n // Handle awareness messages\n if (messageType === MSG_YJS_AWARENESS) {\n const msgDocKey = new TextDecoder().decode(readVarUint8Array(decoder))\n if (msgDocKey !== docKey) return\n const payload = readVarUint8Array(decoder)\n awareness.applyUpdate(payload)\n return\n }\n\n if (messageType !== MSG_YJS_SYNC) return\n\n const msgDocKey = new TextDecoder().decode(readVarUint8Array(decoder))\n if (msgDocKey !== docKey) return\n\n const syncType = readVarUint(decoder)\n const payload = readVarUint8Array(decoder)\n\n switch (syncType) {\n case MSG_SYNC_STEP1: {\n // Server sent state vector - respond with our diff\n const ourDiff = Y.encodeStateAsUpdate(doc, payload)\n const encoder = createEncoder()\n writeVarUint(encoder, MSG_YJS_SYNC)\n writeVarUint8Array(encoder, new TextEncoder().encode(docKey))\n writeVarUint(encoder, MSG_SYNC_STEP2)\n writeVarUint8Array(encoder, ourDiff)\n sendBinary(toUint8Array(encoder))\n setSynced(true)\n break\n }\n case MSG_SYNC_STEP2: {\n // Server sent full state diff\n Y.applyUpdate(doc, payload, 'server')\n setSynced(true)\n break\n }\n case MSG_SYNC_UPDATE: {\n // Server relayed update from another client\n Y.applyUpdate(doc, payload, 'server')\n break\n }\n }\n }\n\n return onBinaryMessage(handleBinaryMessage)\n }, [doc, awareness, docKey, sendBinary, onBinaryMessage])\n\n // Send local awareness changes to server\n useEffect(() => {\n if (!ready) return\n\n const handleAwarenessUpdate = ({\n added,\n updated,\n removed,\n }: {\n added: number[]\n updated: number[]\n removed: number[]\n }) => {\n const changedClients = added.concat(updated).concat(removed)\n if (changedClients.length === 0) return\n\n const encoder = createEncoder()\n writeVarUint(encoder, MSG_YJS_AWARENESS)\n writeVarUint8Array(encoder, new TextEncoder().encode(docKey))\n writeVarUint8Array(encoder, awareness.encodeUpdate(changedClients))\n sendBinary(toUint8Array(encoder))\n }\n\n awareness.on('update', handleAwarenessUpdate)\n return () => {\n awareness.off('update', handleAwarenessUpdate)\n }\n }, [awareness, docKey, sendBinary, ready])\n\n // Join channel when ready - re-join on reconnect\n useEffect(() => {\n if (!ready) {\n // Reset state when disconnected\n debugLog('[YJS] Not ready, resetting:', docKey)\n setSynced(false)\n setCanWrite(false)\n return\n }\n\n debugLog('[YJS] Joining:', docKey)\n sendMessage({ type: MSG.YJS_JOIN, payload: { collection, recordId, fieldName } })\n\n return () => {\n // Clear local awareness state before leaving\n awareness.setLocalState(null)\n debugLog('[YJS] Leaving:', docKey)\n sendMessage({ type: MSG.YJS_LEAVE, payload: { collection, recordId, fieldName } })\n }\n }, [ready, collection, recordId, fieldName, sendMessage, docKey, awareness])\n\n // Send local updates to server\n useEffect(() => {\n const handleUpdate = (update: Uint8Array, origin: unknown) => {\n setUpdateCount((c) => c + 1)\n\n // Only send local updates when we have write permission\n if (origin === 'server' || !canWriteRef.current) return\n\n const encoder = createEncoder()\n writeVarUint(encoder, MSG_YJS_SYNC)\n writeVarUint8Array(encoder, new TextEncoder().encode(docKey))\n writeVarUint(encoder, MSG_SYNC_UPDATE)\n writeVarUint8Array(encoder, update)\n sendBinary(toUint8Array(encoder))\n }\n\n doc.on('update', handleUpdate)\n return () => {\n doc.off('update', handleUpdate)\n }\n }, [doc, docKey, sendBinary])\n\n // synced is only true when WebSocket is connected AND initial sync is complete\n return { doc, awareness, synced: ready && synced, canWrite, updateCount }\n}\n\n// ============================================================================\n// useYjsText\n// ============================================================================\n\nexport interface UseYjsTextResult {\n /** Current text content */\n text: string\n /** Set text content (replaces entire content) */\n setText: (value: string) => void\n /** Whether initial sync is complete */\n synced: boolean\n /** Whether user has write access */\n canWrite: boolean\n}\n\n/**\n * Simplified hook for collaborative text editing.\n *\n * Provides a string-based API instead of raw Yjs doc access.\n * Perfect for textarea/input fields.\n *\n * @param collection - Collection name\n * @param recordId - Record ID\n * @param fieldName - Field name (must be type: 'yjs' in schema)\n *\n * @example\n * const { text, setText, synced, canWrite } = useYjsText('documents', docId, 'content')\n *\n * <textarea\n * value={text}\n * onChange={(e) => setText(e.target.value)}\n * disabled={!synced || !canWrite}\n * />\n */\nexport function useYjsText(\n collection: string,\n recordId: string,\n fieldName: string,\n): UseYjsTextResult {\n const { doc, synced, canWrite, updateCount } = useYjsField(collection, recordId, fieldName)\n const [text, setTextState] = useState('')\n const yTextRef = useRef<Y.Text | null>(null)\n const isLocalUpdateRef = useRef(false)\n\n // Get Y.Text instance once doc is ready\n useEffect(() => {\n yTextRef.current = doc.getText(fieldName)\n }, [doc, fieldName])\n\n // Sync Y.Text to local state when doc updates\n useEffect(() => {\n if (yTextRef.current && synced && !isLocalUpdateRef.current) {\n setTextState(yTextRef.current.toString())\n }\n isLocalUpdateRef.current = false\n }, [synced, updateCount])\n\n // setText function that updates both local state and Yjs\n const setText = useCallback(\n (value: string) => {\n setTextState(value)\n\n if (yTextRef.current && canWrite) {\n isLocalUpdateRef.current = true\n doc.transact(() => {\n const yText = yTextRef.current!\n yText.delete(0, yText.length)\n yText.insert(0, value)\n })\n }\n },\n [doc, canWrite],\n )\n\n return { text, setText, synced, canWrite }\n}\n","/**\n * useUserLookup Hook\n * \n * Provides helper functions to look up user information by userId.\n * This is a convenience wrapper around useUsers() that provides O(1) lookups.\n * \n * The authoritative user data comes from the users collection.\n */\n\nimport { useMemo, useCallback } from 'react'\nimport { useUsers } from './useUsers'\n\nexport interface UserInfo {\n id: string\n email: string\n name: string\n imageUrl?: string\n role: string\n}\n\n/**\n * Hook for looking up user information by ID.\n * \n * @example\n * ```tsx\n * const { getUser, getEmail, getName } = useUserLookup()\n * \n * // Get full user info\n * const user = getUser(userId)\n * \n * // Get just email\n * const email = getEmail(userId)\n * ```\n */\nexport function useUserLookup() {\n const { users, usersLoaded } = useUsers()\n \n // Build a lookup map for O(1) access\n const userMap = useMemo(() => {\n const map = new Map<string, UserInfo>()\n for (const user of users) {\n map.set(user.id, {\n id: user.id,\n email: user.email,\n name: user.name,\n imageUrl: user.imageUrl,\n role: user.role,\n })\n }\n return map\n }, [users])\n \n /**\n * Get full user info by userId\n */\n const getUser = useCallback((userId: string): UserInfo | null => {\n return userMap.get(userId) ?? null\n }, [userMap])\n \n /**\n * Get user's email by userId\n */\n const getEmail = useCallback((userId: string): string | null => {\n return userMap.get(userId)?.email ?? null\n }, [userMap])\n \n /**\n * Get user's name by userId\n */\n const getName = useCallback((userId: string): string | null => {\n return userMap.get(userId)?.name ?? null\n }, [userMap])\n \n return {\n /** All users from the users collection */\n users,\n /** True once the user list has been received from the server. */\n usersLoaded,\n /** Map of userId -> UserInfo for direct access */\n userMap,\n /** Get full user info by userId */\n getUser,\n /** Get user email by userId */\n getEmail,\n /** Get user name by userId */\n getName,\n }\n}\n","/**\n * usePresence — Derives online/offline from lastSeenAt in users collection.\n *\n * Two mechanisms keep presence working:\n * 1. Heartbeat: Sends MSG.USER_UPDATE every 60s so the server refreshes\n * our lastSeenAt in the c_users table and broadcasts the change.\n * 2. Tick: A re-render trigger every 30s so isOnline() re-evaluates\n * Date.now() and users who stopped heartbeating transition to offline.\n */\n\nimport { useEffect, useCallback, useState } from 'react'\nimport { useUser } from './useUser'\nimport { useUsers } from './useUsers'\nimport { useRecordContext } from '../context'\nimport { MSG } from '@/shared/protocol/constants'\n\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000 // 5 minutes\nconst HEARTBEAT_INTERVAL_MS = 60 * 1000 // 1 minute\nconst TICK_INTERVAL_MS = 30 * 1000 // 30 seconds\n\ninterface UsePresenceOptions {\n timeoutMs?: number\n}\n\nexport function usePresence(opts?: UsePresenceOptions) {\n const { user } = useUser()\n const { users } = useUsers()\n const { sendMessage, ready } = useRecordContext()\n const [tick, setTick] = useState(0)\n\n const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS\n\n // Heartbeat: periodically send MSG.USER_UPDATE to refresh our lastSeenAt\n useEffect(() => {\n if (!user || !ready) return\n const interval = setInterval(() => {\n sendMessage({ type: MSG.USER_UPDATE, payload: {} })\n }, HEARTBEAT_INTERVAL_MS)\n return () => clearInterval(interval)\n }, [user, ready, sendMessage])\n\n // Tick: force re-render so isOnline() re-evaluates stale timestamps\n useEffect(() => {\n const interval = setInterval(() => {\n setTick((t) => t + 1)\n }, TICK_INTERVAL_MS)\n return () => clearInterval(interval)\n }, [])\n\n const isOnline = useCallback(\n (userId: string): boolean => {\n // tick is captured to ensure this callback refreshes on each tick\n void tick\n const u = users.find((u) => u.id === userId)\n if (!u?.lastSeenAt) return false\n return Date.now() - new Date(u.lastSeenAt).getTime() < timeoutMs\n },\n [users, timeoutMs, tick],\n )\n\n const getLastSeen = useCallback(\n (userId: string): string | null => {\n const u = users.find((u) => u.id === userId)\n return u?.lastSeenAt ?? null\n },\n [users],\n )\n\n return { isOnline, getLastSeen, users }\n}\n","/**\n * useYjsRoom — Connect to a dedicated YjsRoom Durable Object.\n *\n * Unlike useYjsField (which piggybacks on RecordRoom's WebSocket),\n * this hook opens a direct WebSocket to a YjsRoom DO at /ws/yjs/:docId.\n * Each document gets its own DO for horizontal scaling.\n *\n * Uses the shared yjs-protocol.ts encoding — no duplication.\n *\n * @example\n * const { doc, text, setText, synced, canWrite } = useYjsRoom(docId, 'content')\n */\n\nimport { useState, useEffect, useCallback, useRef, useMemo } from 'react'\nimport * as Y from 'yjs'\nimport { getAuthToken } from '../../auth'\nimport { wsLog } from '../ws-log'\nimport {\n MSG_AWARENESS,\n MSG_SYNC,\n MSG_SYNC_STEP1,\n MSG_SYNC_STEP2,\n MSG_SYNC_UPDATE,\n Awareness,\n createEncoder,\n createDecoder,\n toUint8Array,\n encodeAwarenessMessage,\n handleAwarenessMessage,\n writeVarUint,\n writeVarUint8Array,\n readVarUint,\n readVarUint8Array,\n} from '@/shared/protocol/yjs'\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport interface UseYjsRoomResult {\n /** The Yjs document */\n doc: Y.Doc\n /** Awareness instance for presence/typing state */\n awareness: Awareness\n /** Current text content (for the specified field) */\n text: string\n /** Set text (replaces full content) */\n setText: (value: string) => void\n /** Whether initial sync is complete */\n synced: boolean\n /** Whether user has write access */\n canWrite: boolean\n}\n\n/**\n * Connect to a YjsRoom DO for collaborative editing.\n *\n * @param docId - Document identifier (maps to DO name)\n * @param fieldName - Y.Text field name within the Y.Doc\n */\nexport function useYjsRoom(docId: string, fieldName: string): UseYjsRoomResult {\n const [synced, setSynced] = useState(false)\n const [canWrite, setCanWrite] = useState(false)\n const [text, setTextState] = useState('')\n const [, setUpdateCount] = useState(0)\n\n const docRef = useRef<Y.Doc | null>(null)\n if (!docRef.current) docRef.current = new Y.Doc()\n const doc = docRef.current\n\n const awarenessRef = useRef<Awareness | null>(null)\n if (!awarenessRef.current) awarenessRef.current = new Awareness(doc)\n const awareness = awarenessRef.current\n\n const wsRef = useRef<WebSocket | null>(null)\n const isLocalRef = useRef(false)\n const applyingRemoteAwarenessRef = useRef(false)\n\n const yText = useMemo(() => doc.getText(fieldName), [doc, fieldName])\n\n // Observe remote Y.Text changes\n useEffect(() => {\n const observer = () => {\n if (isLocalRef.current) return\n setTextState(yText.toString())\n }\n yText.observe(observer)\n return () => yText.unobserve(observer)\n }, [yText])\n\n // WebSocket connection to YjsRoom DO\n useEffect(() => {\n let ws: WebSocket | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let alive = true\n\n const connect = async () => {\n if (!alive) return\n\n const token = await getAuthToken()\n // Unmounted (or reconnect superseded) while awaiting the token — bail so\n // we don't open a socket the cleanup can no longer reach and close.\n if (!alive) return\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const baseUrl = `${protocol}//${window.location.host}`\n const url = new URL(`/ws/yjs/${encodeURIComponent(docId)}`, baseUrl)\n if (token) url.searchParams.set('token', token)\n\n wsLog('connecting', `yjs:${docId}`)\n ws = new WebSocket(url.toString())\n ws.binaryType = 'arraybuffer'\n wsRef.current = ws\n\n ws.onopen = () => {\n wsLog('connected', `yjs:${docId}`)\n setSynced(false)\n }\n\n ws.onmessage = (event) => {\n if (typeof event.data === 'string') {\n try {\n const msg = JSON.parse(event.data) as { type?: string; canWrite?: unknown }\n if (msg.type === 'auth' && typeof msg.canWrite === 'boolean') setCanWrite(msg.canWrite)\n } catch {\n /* ignore */\n }\n return\n }\n\n const data = new Uint8Array(event.data as ArrayBuffer)\n const decoder = createDecoder(data)\n const messageType = readVarUint(decoder)\n\n if (messageType === MSG_SYNC) {\n const syncType = readVarUint(decoder)\n const payload = readVarUint8Array(decoder)\n\n switch (syncType) {\n case MSG_SYNC_STEP1: {\n const diff = Y.encodeStateAsUpdate(doc, payload)\n const enc = createEncoder()\n writeVarUint(enc, MSG_SYNC)\n writeVarUint(enc, MSG_SYNC_STEP2)\n writeVarUint8Array(enc, diff)\n ws?.send(toUint8Array(enc).buffer)\n setSynced(true)\n break\n }\n case MSG_SYNC_STEP2: {\n Y.applyUpdate(doc, payload, 'server')\n setTextState(yText.toString())\n setSynced(true)\n break\n }\n case MSG_SYNC_UPDATE: {\n Y.applyUpdate(doc, payload, 'server')\n setUpdateCount((c) => c + 1)\n break\n }\n }\n return\n }\n\n if (messageType === MSG_AWARENESS) {\n applyingRemoteAwarenessRef.current = true\n try {\n handleAwarenessMessage(awareness, data)\n } finally {\n applyingRemoteAwarenessRef.current = false\n }\n }\n }\n\n ws.onclose = () => {\n wsLog('disconnected', `yjs:${docId}`)\n wsRef.current = null\n setSynced(false)\n if (alive) reconnectTimer = setTimeout(connect, 1000)\n }\n\n ws.onerror = () => ws?.close()\n }\n\n connect()\n\n return () => {\n wsLog('closing', `yjs:${docId}`)\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n if (ws) {\n ws.onclose = null\n ws.onmessage = null\n ws.onerror = null\n ws.close()\n }\n wsRef.current = null\n }\n }, [doc, awareness, docId, yText])\n\n // Relay local awareness updates (presence, typing) through YjsRoom with shared MSG_AWARENESS encoding.\n useEffect(() => {\n const handler = ({\n added,\n updated,\n removed,\n }: {\n added: number[]\n updated: number[]\n removed: number[]\n }) => {\n if (applyingRemoteAwarenessRef.current) return\n const changedClients = added.concat(updated).concat(removed)\n if (changedClients.length === 0) return\n\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) return\n\n ws.send(encodeAwarenessMessage(awareness, changedClients).buffer)\n }\n\n awareness.on('update', handler)\n return () => {\n awareness.off('update', handler)\n awareness.setLocalState(null)\n }\n }, [awareness])\n\n // Send local Y.Doc updates to server\n useEffect(() => {\n const handler = (update: Uint8Array, origin: unknown) => {\n if (origin === 'server') return\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) return\n\n const enc = createEncoder()\n writeVarUint(enc, MSG_SYNC)\n writeVarUint(enc, MSG_SYNC_UPDATE)\n writeVarUint8Array(enc, update)\n ws.send(toUint8Array(enc).buffer)\n }\n\n doc.on('update', handler)\n return () => {\n doc.off('update', handler)\n }\n }, [doc])\n\n // setText: update Y.Text + local state\n const setText = useCallback(\n (value: string) => {\n setTextState(value)\n if (!canWrite) return\n\n isLocalRef.current = true\n doc.transact(() => {\n yText.delete(0, yText.length)\n yText.insert(0, value)\n })\n isLocalRef.current = false\n },\n [doc, yText, canWrite],\n )\n\n return { doc, awareness, text, setText, synced, canWrite }\n}\n","/**\n * useGameRoom — Connect to a GameRoom Durable Object.\n *\n * Opens a WebSocket to /ws/game/:roomId for real-time game state.\n *\n * @example\n * const { state, sendInput, players, connected } = useGameRoom('my-game')\n *\n * Implementation note: this hook uses the typed wire protocol helpers\n * (`clientBuild` for sends, `dispatch` for receives) from\n * `shared/protocol/messages`. Hand-rolled `switch (msg.type)` blocks and\n * raw `JSON.stringify({ type: MSG.X, payload: ... })` sends are a fast\n * route to the \"silent drop\" bug class when a payload shape drifts —\n * prefer the typed helpers instead.\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { getAuthToken } from '../../auth'\nimport { wsLog } from '../ws-log'\nimport { MSG } from '@/shared/protocol/constants'\nimport {\n clientBuild,\n dispatch,\n encode,\n type ServerMessage,\n} from '@/shared/protocol/messages'\n\nexport interface GamePlayer {\n userId: string\n userName: string\n ready: boolean\n connectedAt: string\n data: Record<string, unknown>\n}\n\nexport interface UseGameRoomResult {\n /** Current game state */\n state: Record<string, unknown>\n /** Current tick number */\n tick: number\n /** Connected players */\n players: GamePlayer[]\n /** Whether the game is currently running */\n running: boolean\n /** Whether WebSocket is connected */\n connected: boolean\n /**\n * Whether this connection has write access. False for viewers /\n * unauthenticated spectators — they receive state broadcasts but the\n * send callbacks below no-op. UIs should disable input controls when\n * false so spectators see why their actions don't take effect.\n */\n canWrite: boolean\n /** Send a game input (no-op for spectators) */\n sendInput: (action: string, data?: Record<string, unknown>) => void\n /** Mark self as ready (no-op for spectators) */\n setReady: () => void\n /** Request game start (no-op for spectators) */\n startGame: () => void\n /** Request game end (no-op for spectators) */\n endGame: () => void\n}\n\nexport function useGameRoom(roomId: string): UseGameRoomResult {\n const [gameState, setGameState] = useState<Record<string, unknown>>({})\n const [tick, setTick] = useState(0)\n const [players, setPlayers] = useState<GamePlayer[]>([])\n const [running, setRunning] = useState(false)\n const [connected, setConnected] = useState(false)\n // Default false: assume spectator until the server's AUTH frame\n // confirms otherwise. See GameRoom.onConnect.\n const [canWrite, setCanWrite] = useState(false)\n const wsRef = useRef<WebSocket | null>(null)\n\n useEffect(() => {\n let ws: WebSocket | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let alive = true\n\n const connect = async () => {\n if (!alive) return\n\n const token = await getAuthToken()\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const baseUrl = `${protocol}//${window.location.host}`\n const url = new URL(`/ws/game/${encodeURIComponent(roomId)}`, baseUrl)\n if (token) url.searchParams.set('token', token)\n\n wsLog('connecting', `game:${roomId}`)\n ws = new WebSocket(url.toString())\n wsRef.current = ws\n\n ws.onopen = () => {\n wsLog('connected', `game:${roomId}`)\n setConnected(true)\n }\n\n ws.onmessage = (event) => {\n dispatch<ServerMessage>(event.data, {\n [MSG.AUTH]: (p) => {\n setCanWrite(p.canWrite)\n },\n [MSG.GAME_STATE]: (p) => {\n setGameState(p.state as Record<string, unknown>)\n setTick(p.tick)\n setPlayers(p.players as GamePlayer[])\n setRunning(p.running)\n },\n [MSG.GAME_TICK]: (p) => {\n setGameState(p.state as Record<string, unknown>)\n setTick(p.tick)\n },\n [MSG.GAME_PLAYER_JOIN]: (p) => {\n const player = p.player as GamePlayer\n setPlayers((prev) => [...prev.filter((x) => x.userId !== player.userId), player])\n },\n [MSG.GAME_PLAYER_LEAVE]: (p) => {\n setPlayers((prev) => prev.filter((x) => x.userId !== p.userId))\n },\n [MSG.GAME_PLAYER_READY]: (p) => {\n setPlayers((prev) =>\n prev.map((x) => (x.userId === p.userId ? { ...x, ready: true } : x)),\n )\n },\n [MSG.GAME_START]: (p) => {\n setRunning(true)\n setGameState(p.state as Record<string, unknown>)\n setTick(p.tick)\n },\n [MSG.GAME_END]: (p) => {\n setRunning(false)\n setGameState(p.state as Record<string, unknown>)\n },\n })\n }\n\n ws.onclose = () => {\n wsLog('disconnected', `game:${roomId}`)\n wsRef.current = null\n setConnected(false)\n // Reset to the safe default so a reconnect with a degraded role\n // doesn't leave input/control buttons enabled until the new AUTH\n // frame lands. See useCanvas onclose for the rationale.\n setCanWrite(false)\n if (alive) reconnectTimer = setTimeout(connect, 1000)\n }\n\n ws.onerror = () => ws?.close()\n }\n\n connect()\n\n return () => {\n wsLog('closing', `game:${roomId}`)\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n if (ws) {\n ws.onclose = null\n ws.onmessage = null\n ws.onerror = null\n ws.close()\n }\n wsRef.current = null\n }\n }, [roomId])\n\n // Local write-gate. The server enforces the same rule in\n // GameRoom.onMessage; this is the UX side so spectator clicks don't\n // even leave the browser.\n const send = useCallback(\n <M extends { type: string; payload: unknown }>(message: M) => {\n if (!canWrite) return\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) return\n ws.send(encode(message))\n },\n [canWrite],\n )\n\n const sendInput = useCallback(\n (action: string, data: Record<string, unknown> = {}) => send(clientBuild.gameInput(action, data)),\n [send],\n )\n\n const setReady = useCallback(() => send(clientBuild.gamePlayerReady()), [send])\n const startGame = useCallback(() => send(clientBuild.gameStart()), [send])\n const endGame = useCallback(() => send(clientBuild.gameEnd()), [send])\n\n return { state: gameState, tick, players, running, connected, canWrite, sendInput, setReady, startGame, endGame }\n}\n","/**\n * useCanvas — Connect to a CanvasRoom Durable Object.\n *\n * Opens a WebSocket to /ws/canvas/:roomId for collaborative spatial editing.\n *\n * @example\n * const { shapes, addShape, moveShape, deleteShape, viewports } = useCanvas('my-canvas')\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { getAuthToken } from '../../auth'\nimport { wsLog } from '../ws-log'\nimport { MSG } from '@/shared/protocol/constants'\nimport {\n clientBuild,\n dispatch,\n encode,\n type ServerMessage,\n} from '@/shared/protocol/messages'\n\nexport interface CanvasShapeClient {\n id: string\n type: string\n x: number\n y: number\n width: number\n height: number\n rotation?: number\n props: Record<string, unknown>\n createdBy: string\n createdAt: string\n updatedAt: string\n}\n\nexport interface ViewportClient {\n userId: string\n x: number\n y: number\n width: number\n height: number\n zoom: number\n}\n\nexport interface UseCanvasResult {\n /** All shapes on the canvas */\n shapes: CanvasShapeClient[]\n /** Other users' viewports */\n viewports: ViewportClient[]\n /** Whether WebSocket is connected */\n connected: boolean\n /**\n * Whether this connection has write access (member/admin role). The\n * write callbacks below no-op when this is false; UIs should also use\n * it to disable controls so viewers see why their clicks do nothing.\n * Viewport reporting stays available regardless — it's presence info.\n */\n canWrite: boolean\n /** Add a shape (no-op when canWrite is false) */\n addShape: (shape: Partial<CanvasShapeClient>) => void\n /** Move a shape (no-op when canWrite is false) */\n moveShape: (shapeId: string, x: number, y: number) => void\n /** Resize a shape (no-op when canWrite is false) */\n resizeShape: (shapeId: string, width: number, height: number, x?: number, y?: number) => void\n /** Delete a shape (no-op when canWrite is false) */\n deleteShape: (shapeId: string) => void\n /** Update shape properties (no-op when canWrite is false) */\n updateShape: (shapeId: string, props: Record<string, unknown>) => void\n /** Report local viewport (allowed for viewers — presence-like) */\n setViewport: (viewport: Omit<ViewportClient, 'userId'>) => void\n /** Undo last action (no-op when canWrite is false) */\n undo: () => void\n /** Redo last undone action (no-op when canWrite is false) */\n redo: () => void\n}\n\nexport function useCanvas(roomId: string): UseCanvasResult {\n const [shapes, setShapes] = useState<CanvasShapeClient[]>([])\n const [viewports, setViewports] = useState<ViewportClient[]>([])\n const [connected, setConnected] = useState(false)\n // Defaults to false: until the server's AUTH frame arrives, assume the\n // safer no-write posture so UIs don't briefly enable controls for a\n // viewer mid-handshake.\n const [canWrite, setCanWrite] = useState(false)\n const wsRef = useRef<WebSocket | null>(null)\n\n useEffect(() => {\n let ws: WebSocket | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let alive = true\n\n const connect = async () => {\n if (!alive) return\n\n const token = await getAuthToken()\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const baseUrl = `${protocol}//${window.location.host}`\n const url = new URL(`/ws/canvas/${encodeURIComponent(roomId)}`, baseUrl)\n if (token) url.searchParams.set('token', token)\n\n wsLog('connecting', `canvas:${roomId}`)\n ws = new WebSocket(url.toString())\n wsRef.current = ws\n\n ws.onopen = () => {\n wsLog('connected', `canvas:${roomId}`)\n setConnected(true)\n }\n\n ws.onmessage = (event) => {\n // Typed dispatch — handler payloads are narrowed by the SDK's\n // discriminated `ServerMessage` union. Shape/viewport objects\n // on the wire are typed as `unknown` (the protocol layer\n // doesn't know the app's domain types), so we cast to the\n // local `CanvasShapeClient` / `ViewportClient` where we use\n // them.\n dispatch<ServerMessage>(event.data, {\n [MSG.AUTH]: (p) => {\n setCanWrite(p.canWrite)\n },\n [MSG.CANVAS_SHAPES]: (p) => {\n setShapes(p.shapes as CanvasShapeClient[])\n setViewports(p.viewports as ViewportClient[])\n },\n [MSG.CANVAS_ADD]: (p) => {\n const shape = p.shape as CanvasShapeClient\n setShapes((prev) => [...prev.filter((s) => s.id !== shape.id), shape])\n },\n [MSG.CANVAS_MOVE]: (p) => {\n setShapes((prev) =>\n prev.map((s) => (s.id === p.shapeId ? { ...s, x: p.x, y: p.y } : s)),\n )\n },\n [MSG.CANVAS_RESIZE]: (p) => {\n setShapes((prev) =>\n prev.map((s) => {\n if (s.id !== p.shapeId) return s\n const updated = { ...s, width: p.width, height: p.height }\n if (p.x !== undefined) updated.x = p.x\n if (p.y !== undefined) updated.y = p.y\n return updated\n }),\n )\n },\n [MSG.CANVAS_DELETE]: (p) => {\n setShapes((prev) => prev.filter((s) => s.id !== p.shapeId))\n },\n [MSG.CANVAS_UPDATE]: (p) => {\n setShapes((prev) =>\n prev.map((s) =>\n s.id === p.shapeId ? { ...s, props: { ...s.props, ...p.props } } : s,\n ),\n )\n },\n [MSG.CANVAS_VIEWPORT]: (p) => {\n // The payload is a union of `{ viewport }` and\n // `{ userId, removed: true }` — narrow by presence.\n if ('removed' in p) {\n const removed = p\n setViewports((prev) => prev.filter((v) => v.userId !== removed.userId))\n } else {\n const viewport = p.viewport as ViewportClient\n setViewports((prev) => [\n ...prev.filter((v) => v.userId !== viewport.userId),\n viewport,\n ])\n }\n },\n })\n }\n\n ws.onclose = () => {\n wsLog('disconnected', `canvas:${roomId}`)\n wsRef.current = null\n setConnected(false)\n // Reset to the safe default so a reconnect with a degraded role\n // (e.g. JWT expired, user now anon) doesn't leave write buttons\n // enabled until the new AUTH frame arrives. The fresh server\n // onConnect will re-send AUTH with the current canWrite value.\n setCanWrite(false)\n if (alive) reconnectTimer = setTimeout(connect, 1000)\n }\n\n ws.onerror = () => ws?.close()\n }\n\n connect()\n\n return () => {\n wsLog('closing', `canvas:${roomId}`)\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n if (ws) {\n ws.onclose = null\n ws.onmessage = null\n ws.onerror = null\n ws.close()\n }\n wsRef.current = null\n }\n }, [roomId])\n\n // Typed builders — `clientBuild.canvasX` returns a validated\n // `ClientMessage` that `encode` stringifies. No hand-rolled\n // `JSON.stringify({ type, payload })` calls here.\n const sendBuilt = useCallback(\n <M extends { type: string; payload: unknown }>(message: M) => {\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) return\n ws.send(encode(message))\n },\n [],\n )\n\n // Local write-gate: write callbacks short-circuit when canWrite is\n // false. The server enforces the same rule (see CanvasRoom.onMessage),\n // so this is a UX layer — viewers click a button and get instant\n // \"nothing happened\" instead of a round-trip ERROR.\n const sendWrite = useCallback(\n <M extends { type: string; payload: unknown }>(message: M) => {\n if (!canWrite) return\n sendBuilt(message)\n },\n [sendBuilt, canWrite],\n )\n\n const addShape = useCallback(\n (shape: Partial<CanvasShapeClient>) => sendWrite(clientBuild.canvasAdd(shape)),\n [sendWrite],\n )\n\n const moveShape = useCallback(\n (shapeId: string, x: number, y: number) =>\n sendWrite(clientBuild.canvasMove(shapeId, x, y)),\n [sendWrite],\n )\n\n const resizeShape = useCallback(\n (shapeId: string, width: number, height: number, x?: number, y?: number) =>\n sendWrite(clientBuild.canvasResize(shapeId, width, height, x, y)),\n [sendWrite],\n )\n\n const deleteShape = useCallback(\n (shapeId: string) => sendWrite(clientBuild.canvasDelete(shapeId)),\n [sendWrite],\n )\n\n const updateShape = useCallback(\n (shapeId: string, props: Record<string, unknown>) =>\n sendWrite(clientBuild.canvasUpdate(shapeId, props)),\n [sendWrite],\n )\n\n // Viewport stays on sendBuilt (not sendWrite): broadcasting \"I'm\n // looking here\" is presence-like and intentionally allowed for\n // viewers, matching the server's CanvasRoom gate.\n const setViewport = useCallback(\n (viewport: Omit<ViewportClient, 'userId'>) =>\n sendBuilt(clientBuild.canvasViewport(viewport)),\n [sendBuilt],\n )\n\n const undo = useCallback(() => sendWrite(clientBuild.canvasUndo()), [sendWrite])\n const redo = useCallback(() => sendWrite(clientBuild.canvasRedo()), [sendWrite])\n\n return { shapes, viewports, connected, canWrite, addShape, moveShape, resizeShape, deleteShape, updateShape, setViewport, undo, redo }\n}\n","/**\n * useCronMonitor — Connect to a CronRoom for monitoring scheduled tasks.\n *\n * Opens a WebSocket to /cron/:roomId for real-time task status.\n *\n * @example\n * const { tasks, history, trigger, pause, resume } = useCronMonitor('cron')\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { getAuthToken } from '../../auth'\nimport { MSG } from '@/shared/protocol/constants'\nimport {\n clientBuild,\n dispatch,\n encode,\n type ServerMessage,\n} from '@/shared/protocol/messages'\n\nexport interface CronTaskState {\n name: string\n intervalMinutes: number | null\n schedule: string | null\n timezone: string | null\n paused: boolean\n lastRunAt: string | null\n nextRunAt: string | null\n}\n\nexport interface CronHistoryEntry {\n taskName: string\n startedAt: string\n completedAt: string | null\n success: boolean\n durationMs: number\n error?: string\n}\n\nexport interface UseCronMonitorResult {\n /** Current task states */\n tasks: CronTaskState[]\n /** Execution history */\n history: CronHistoryEntry[]\n /** Whether WebSocket is connected */\n connected: boolean\n /**\n * Whether this connection can mutate cron state (trigger / pause /\n * resume). False for viewers and unauthenticated connections — the\n * mutation callbacks below no-op and UIs should disable those\n * controls. Reading tasks/history stays available either way.\n */\n canWrite: boolean\n /** Manually trigger a task (no-op when canWrite is false) */\n trigger: (taskName: string) => void\n /** Pause a task (no-op when canWrite is false) */\n pause: (taskName: string) => void\n /** Resume a paused task (no-op when canWrite is false) */\n resume: (taskName: string) => void\n}\n\nexport function useCronMonitor(roomId: string): UseCronMonitorResult {\n const [tasks, setTasks] = useState<CronTaskState[]>([])\n const [history, setHistory] = useState<CronHistoryEntry[]>([])\n const [connected, setConnected] = useState(false)\n // Default false: viewers/anon connections can read but not write. The\n // server's CronRoom.onConnect AUTH frame flips this for members/admins.\n const [canWrite, setCanWrite] = useState(false)\n const wsRef = useRef<WebSocket | null>(null)\n\n useEffect(() => {\n let ws: WebSocket | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let alive = true\n\n const connect = async () => {\n if (!alive) return\n\n const token = await getAuthToken()\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const baseUrl = `${protocol}//${window.location.host}`\n const url = new URL(`/ws/cron/${encodeURIComponent(roomId)}`, baseUrl)\n if (token) url.searchParams.set('token', token)\n\n ws = new WebSocket(url.toString())\n wsRef.current = ws\n\n ws.onopen = () => setConnected(true)\n\n ws.onmessage = (event) => {\n dispatch<ServerMessage>(event.data, {\n [MSG.AUTH]: (p) => {\n setCanWrite(p.canWrite)\n },\n [MSG.CRON_TASKS]: (p) => {\n setTasks(p.tasks as CronTaskState[])\n },\n [MSG.CRON_HISTORY]: (p) => {\n setHistory(p.history as CronHistoryEntry[])\n },\n [MSG.CRON_STATUS]: (p) => {\n setTasks(p.tasks as CronTaskState[])\n setHistory(p.recentHistory as CronHistoryEntry[])\n },\n })\n }\n\n ws.onclose = () => {\n wsRef.current = null\n setConnected(false)\n // Reset to the safe default so a reconnect with a degraded role\n // doesn't leave trigger/pause/resume controls enabled until the\n // new AUTH frame lands. See useCanvas onclose for the rationale.\n setCanWrite(false)\n if (alive) reconnectTimer = setTimeout(connect, 1000)\n }\n\n ws.onerror = () => ws?.close()\n }\n\n connect()\n\n return () => {\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n ws?.close()\n wsRef.current = null\n }\n }, [roomId])\n\n // Local write-gate. Mirrors the server check in CronRoom.onMessage so\n // viewer clicks short-circuit instead of round-tripping to ERROR.\n const sendWrite = useCallback(\n <M extends { type: string; payload: unknown }>(message: M) => {\n if (!canWrite) return\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) return\n ws.send(encode(message))\n },\n [canWrite],\n )\n\n const trigger = useCallback(\n (taskName: string) => sendWrite(clientBuild.cronTrigger(taskName)),\n [sendWrite],\n )\n const pause = useCallback(\n (taskName: string) => sendWrite(clientBuild.cronPause(taskName)),\n [sendWrite],\n )\n const resume = useCallback(\n (taskName: string) => sendWrite(clientBuild.cronResume(taskName)),\n [sendWrite],\n )\n\n return { tasks, history, connected, canWrite, trigger, pause, resume }\n}\n","/**\n * useJobs — Connect to a JobRoom for enqueueing and tracking background\n * jobs in real time.\n *\n * Opens a WebSocket to /ws/jobs/:roomId. The DO pushes a snapshot on\n * connect and JOB_UPDATE messages for every state change after that, so\n * `jobs` stays current without polling. `enqueue` returns a promise that\n * resolves with the new jobId once the server acks.\n *\n * Client-side names (`JobView`, `JobStatusView`) differ from the\n * server-side equivalents (`Job`, `JobStatus` in `deepspace/worker`) so\n * apps importing from both ends don't get type collisions.\n *\n * @example\n * const { jobs, enqueue, getJob, cancel } = useJobs(SCOPE_ID) // `app:${APP_ID}`\n *\n * async function onClick() {\n * const jobId = await enqueue('ai-summarize', { text })\n * // Now `getJob(jobId)` is reactive until the job reaches a terminal state.\n * }\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { getAuthToken } from '../../auth'\nimport { MSG } from '@/shared/protocol/constants'\nimport {\n clientBuild,\n dispatch,\n encode,\n type ServerMessage,\n} from '@/shared/protocol/messages'\n\n/** How long enqueue() waits for a server ack before rejecting. */\nconst ENQUEUE_TIMEOUT_MS = 10_000\n\nexport type JobStatusView =\n | 'queued'\n | 'running'\n | 'succeeded'\n | 'failed'\n | 'canceled'\n\nexport interface JobView<P = unknown, R = unknown> {\n id: string\n type: string\n status: JobStatusView\n payload?: P\n result?: R\n error?: string\n progress?: number\n progressMessage?: string\n attempts: number\n maxAttempts: number\n enqueuedAt: string\n startedAt?: string | null\n completedAt?: string | null\n enqueuedBy?: string | null\n}\n\nexport interface UseJobsResult<P = unknown, R = unknown> {\n /** All jobs the room currently knows about, newest-first. */\n jobs: JobView<P, R>[]\n /** WebSocket connection state. */\n connected: boolean\n /**\n * Enqueue a new job. Resolves with the new jobId once the server acks\n * (typically <100ms). Rejects if:\n * - the WebSocket isn't connected\n * - the WebSocket closes before the ack arrives\n * - no ack arrives within 10s (server dropped the message)\n */\n enqueue(\n type: string,\n payload?: P,\n options?: { maxAttempts?: number },\n ): Promise<string>\n /**\n * Reactive single-job lookup. Returns undefined while the snapshot is\n * loading or if the job has been GC'd by the room's TTL sweep.\n */\n getJob(jobId: string): JobView<P, R> | undefined\n /** Best-effort cancel. The job is marked canceled even if the handler ignores `ctx.signal`. */\n cancel(jobId: string): void\n /** Manually re-queue a failed/canceled job. No-op for other statuses. */\n retry(jobId: string): void\n}\n\ninterface PendingEnqueue {\n resolve: (id: string) => void\n reject: (err: Error) => void\n timer: ReturnType<typeof setTimeout>\n}\n\n/**\n * @param roomId DO room id, typically `app:<APP_NAME>`. Apps with sharded\n * queues pass different ids — each id maps to its own DO instance.\n */\nexport function useJobs<P = unknown, R = unknown>(\n roomId: string,\n): UseJobsResult<P, R> {\n const [jobs, setJobs] = useState<JobView<P, R>[]>([])\n const [connected, setConnected] = useState(false)\n const wsRef = useRef<WebSocket | null>(null)\n /**\n * Pending enqueue promises keyed by requestId. Each entry carries a\n * timeout timer so the caller doesn't hang forever if the server drops\n * the enqueue (validation error swallowed, isolate restart, etc.).\n */\n const pendingEnqueueRef = useRef<Map<string, PendingEnqueue>>(new Map())\n\n useEffect(() => {\n let ws: WebSocket | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let alive = true\n\n const upsert = (next: JobView<P, R>) => {\n setJobs((prev) => {\n const idx = prev.findIndex((j) => j.id === next.id)\n if (idx === -1) return [next, ...prev]\n const copy = prev.slice()\n copy[idx] = next\n return copy\n })\n }\n\n const rejectAllPending = (err: Error) => {\n for (const [id, pending] of pendingEnqueueRef.current.entries()) {\n clearTimeout(pending.timer)\n pending.reject(err)\n pendingEnqueueRef.current.delete(id)\n }\n }\n\n const connect = async () => {\n if (!alive) return\n\n const token = await getAuthToken()\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const baseUrl = `${protocol}//${window.location.host}`\n const url = new URL(`/ws/jobs/${encodeURIComponent(roomId)}`, baseUrl)\n if (token) url.searchParams.set('token', token)\n\n ws = new WebSocket(url.toString())\n wsRef.current = ws\n\n ws.onopen = () => setConnected(true)\n\n ws.onmessage = (event) => {\n dispatch<ServerMessage>(event.data, {\n [MSG.JOB_UPDATE]: (p) => {\n if (p.kind === 'snapshot' && Array.isArray(p.jobs)) {\n setJobs(p.jobs as JobView<P, R>[])\n return\n }\n const job = p.job as JobView<P, R> | undefined\n if (!job) return\n upsert(job)\n // Resolve the enqueue promise once the server acks.\n if (p.kind === 'enqueued' && typeof p.requestId === 'string') {\n const pending = pendingEnqueueRef.current.get(p.requestId)\n if (pending) {\n clearTimeout(pending.timer)\n pendingEnqueueRef.current.delete(p.requestId)\n pending.resolve(job.id)\n }\n }\n },\n })\n }\n\n ws.onclose = () => {\n wsRef.current = null\n setConnected(false)\n rejectAllPending(new Error('JobRoom WebSocket closed'))\n if (alive) reconnectTimer = setTimeout(connect, 1000)\n }\n\n ws.onerror = () => ws?.close()\n }\n\n connect()\n\n return () => {\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n rejectAllPending(new Error('useJobs unmounted'))\n ws?.close()\n wsRef.current = null\n }\n }, [roomId])\n\n const enqueue = useCallback(\n (type: string, payload?: P, options?: { maxAttempts?: number }) => {\n return new Promise<string>((resolve, reject) => {\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n reject(new Error('JobRoom WebSocket not connected'))\n return\n }\n const requestId = crypto.randomUUID()\n const timer = setTimeout(() => {\n pendingEnqueueRef.current.delete(requestId)\n reject(\n new Error(\n `JobRoom enqueue timed out (no server ack within ${ENQUEUE_TIMEOUT_MS}ms)`,\n ),\n )\n }, ENQUEUE_TIMEOUT_MS)\n pendingEnqueueRef.current.set(requestId, { resolve, reject, timer })\n // ws.send can throw synchronously if the socket transitions between\n // the readyState check and the call (browser-specific, quota errors).\n // Without this guard the pending entry would sit until the 10s\n // timeout — fine for safety, bad for caller latency.\n try {\n ws.send(\n encode(clientBuild.jobEnqueue(requestId, type, payload, options?.maxAttempts)),\n )\n } catch (e) {\n clearTimeout(timer)\n pendingEnqueueRef.current.delete(requestId)\n reject(e instanceof Error ? e : new Error(String(e)))\n }\n })\n },\n [],\n )\n\n const sendBuilt = useCallback(\n <M extends { type: string; payload: unknown }>(message: M): void => {\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) return\n ws.send(encode(message))\n },\n [],\n )\n\n const getJob = useCallback(\n (jobId: string) => jobs.find((j) => j.id === jobId),\n [jobs],\n )\n\n const cancel = useCallback(\n (jobId: string) => sendBuilt(clientBuild.jobCancel(jobId)),\n [sendBuilt],\n )\n\n const retry = useCallback(\n (jobId: string) => sendBuilt(clientBuild.jobRetry(jobId)),\n [sendBuilt],\n )\n\n return { jobs, connected, enqueue, getJob, cancel, retry }\n}\n","/**\n * usePresenceRoom — Connect to a PresenceRoom Durable Object.\n *\n * Opens a WebSocket to /ws/presence/:scopeId for real-time presence tracking.\n * Use with any scope: canvas, doc, thread, page, etc.\n *\n * Peers can share arbitrary state (cursor position, typing indicator,\n * viewport, selection) via updateState().\n *\n * @example\n * // Track who's on a canvas\n * const { peers, connected, updateState } = usePresenceRoom(`canvas:${canvasId}`)\n *\n * // Share cursor position\n * updateState({ cursor: { x: 100, y: 200 } })\n *\n * // Track who's viewing a thread\n * const { peers } = usePresenceRoom(`thread:${channelId}`)\n *\n * // Show typing indicator\n * updateState({ typing: true })\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { getAuthToken } from '../../auth'\nimport { wsLog } from '../ws-log'\nimport { MSG } from '@/shared/protocol/constants'\nimport {\n clientBuild,\n dispatch,\n encode,\n type ServerMessage,\n} from '@/shared/protocol/messages'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface PresencePeerClient {\n userId: string\n userName: string\n userEmail: string\n userImageUrl?: string\n joinedAt: string\n state: Record<string, unknown>\n}\n\nexport interface UsePresenceRoomResult {\n /** All peers currently present in this scope (excludes self) */\n peers: PresencePeerClient[]\n /** Whether the WebSocket is connected */\n connected: boolean\n /** Send a state update (cursor, typing, viewport, etc.) — merges with existing state */\n updateState: (state: Record<string, unknown>) => void\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function usePresenceRoom(scopeId: string): UsePresenceRoomResult {\n const [peers, setPeers] = useState<PresencePeerClient[]>([])\n const [connected, setConnected] = useState(false)\n const wsRef = useRef<WebSocket | null>(null)\n\n useEffect(() => {\n let ws: WebSocket | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let alive = true\n\n const connect = async () => {\n if (!alive) return\n\n const token = await getAuthToken()\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const baseUrl = `${protocol}//${window.location.host}`\n const url = new URL(`/ws/presence/${encodeURIComponent(scopeId)}`, baseUrl)\n if (token) url.searchParams.set('token', token)\n\n wsLog('connecting', `presence:${scopeId}`)\n ws = new WebSocket(url.toString())\n wsRef.current = ws\n\n ws.onopen = () => {\n wsLog('connected', `presence:${scopeId}`)\n setConnected(true)\n }\n\n ws.onmessage = (event) => {\n dispatch<ServerMessage>(event.data, {\n [MSG.PRESENCE_SYNC]: (p) => {\n setPeers(p.peers as PresencePeerClient[])\n },\n [MSG.PRESENCE_JOIN]: (p) => {\n const peer = p.peer as PresencePeerClient\n setPeers((prev) => [...prev.filter((x) => x.userId !== peer.userId), peer])\n },\n [MSG.PRESENCE_LEAVE]: (p) => {\n setPeers((prev) => prev.filter((x) => x.userId !== p.userId))\n },\n [MSG.PRESENCE_UPDATE]: (p) => {\n setPeers((prev) =>\n prev.map((x) =>\n x.userId === p.userId ? { ...x, state: { ...x.state, ...p.state } } : x,\n ),\n )\n },\n })\n }\n\n ws.onclose = () => {\n wsLog('disconnected', `presence:${scopeId}`)\n wsRef.current = null\n setConnected(false)\n if (alive) reconnectTimer = setTimeout(connect, 1000)\n }\n\n ws.onerror = () => ws?.close()\n }\n\n connect()\n\n return () => {\n wsLog('closing', `presence:${scopeId}`)\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n if (ws) {\n ws.onclose = null\n ws.onmessage = null\n ws.onerror = null\n ws.close()\n }\n wsRef.current = null\n }\n }, [scopeId])\n\n const updateState = useCallback((state: Record<string, unknown>) => {\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) return\n ws.send(encode(clientBuild.presenceUpdate(state)))\n }, [])\n\n return { peers, connected, updateState }\n}\n","/**\n * ConnectionStatus type and utility for domain hooks.\n *\n * Maps useQuery() status to a simpler connection state\n * used by app-level platform providers.\n */\n\nexport type ConnectionStatus = 'connecting' | 'connected' | 'reconnecting' | 'error'\n\n/**\n * Map a useQuery() status string to a ConnectionStatus.\n *\n * useQuery returns: 'pending' | 'ready' | 'error'\n * This maps to the simplified connection states used by platform providers.\n */\nexport function toConnectionStatus(queryStatus: string): ConnectionStatus {\n if (queryStatus === 'ready') return 'connected'\n if (queryStatus === 'error') return 'error'\n return 'connecting'\n}\n","/**\n * useR2Files — Scoped file storage hook for DeepSpace apps.\n *\n * Provides upload, download, list, and delete for files in R2.\n *\n * Two scopes are supported via the `scope` option:\n * - `'self'` (default): per-user storage. Reads require auth.\n * - `'app'`: app-wide storage. Reads are public — the upload URL works\n * directly as an `<img src>`. Use for avatars, logos, etc.\n *\n * @example\n * ```tsx\n * // Per-user files (default):\n * const { upload, downloadFile } = useR2Files()\n * await upload(myFile, 'photo.png')\n *\n * // App-shared files (public reads — usable in <img src>):\n * const { upload } = useR2Files({ scope: 'app' })\n * const r = await upload(file, `avatars/${userId}.png`)\n * // r.url is a plain, anon-readable URL.\n * ```\n */\n\nimport { useCallback, useState, useMemo } from 'react'\nimport { getAuthToken } from '../auth'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface R2UploadResult {\n success: boolean\n key?: string\n url?: string\n name?: string\n error?: string\n}\n\nexport interface R2FileInfo {\n key: string\n size: number\n uploaded: string\n url: string\n originalName?: string\n uploadedBy?: string\n}\n\nexport interface UseR2FilesReturn {\n /** Upload a File or Blob. Optionally provide a display name. */\n upload: (file: File | Blob, name?: string) => Promise<R2UploadResult>\n /** Upload raw base64 data. */\n uploadBase64: (base64Data: string, name: string, mimeType?: string) => Promise<R2UploadResult>\n /**\n * Delete a file. Accepts an R2FileInfo from `list()` or a raw key string.\n *\n * @example\n * ```tsx\n * const files = await list()\n * await deleteFile(files[0]) // pass the object\n * await deleteFile('widgets/abc/f.pdf') // or a raw key\n * ```\n */\n deleteFile: (fileOrKey: R2FileInfo | string) => Promise<{ success: boolean; error?: string }>\n /** List files, optionally filtered by a sub-prefix within the scope. */\n list: (prefix?: string) => Promise<R2FileInfo[]>\n /**\n * Download a file. Accepts an R2FileInfo from `list()` or a raw key string.\n * Uses an authenticated fetch and triggers a browser download via blob URL.\n *\n * When an R2FileInfo is passed, `originalName` is used as the download\n * filename automatically. When a string key is passed, an optional\n * `fileName` override can be provided.\n *\n * @example\n * ```tsx\n * const files = await list()\n * await downloadFile(files[0]) // uses originalName\n * await downloadFile('widgets/abc/f.pdf', 'report.pdf') // explicit name\n * ```\n */\n downloadFile: (fileOrKey: R2FileInfo | string, fileName?: string) => Promise<{ success: boolean; error?: string }>\n /**\n * Read a file's contents with authentication. Returns the raw Response.\n * Unlike `downloadFile`, this does NOT trigger a browser download —\n * it returns the Response so you can call `.text()`, `.blob()`,\n * `.arrayBuffer()`, `.json()`, etc.\n *\n * @example\n * ```tsx\n * const resp = await readFile('data/cleaned.txt')\n * const text = await resp.text()\n * ```\n */\n readFile: (fileOrKey: R2FileInfo | string) => Promise<Response>\n /**\n * Build a plain URL for a file. Accepts an R2FileInfo or a raw key string.\n *\n * **Note**: This URL has no auth token attached. It works for `'app'`-scope\n * files (which are publicly readable) but not for `'self'`-scope files —\n * those require an Authorization header, so use `readFile` / `downloadFile`.\n */\n getUrl: (fileOrKey: R2FileInfo | string) => string\n /** Whether an upload is currently in progress. */\n isUploading: boolean\n}\n\n/**\n * Scope controls where files live and who can read them:\n *\n * - `'self'` (default) — `apps/<app>/users/<userId>/…`. Per-user storage.\n * Reads require the caller's auth token; not usable from a plain `<img>`\n * or unauthenticated browser request.\n * - `'app'` — `apps/<app>/…`. Shared by everyone in the app. Reads are\n * public (no auth header needed), so the upload URL works directly as\n * an `<img src>`. Use this for avatars, logos, attachments, and other\n * assets meant to be embedded in pages.\n *\n * Uploads under `'app'` still require a signed-in user.\n */\nexport type R2Scope = { scope?: 'self' | 'app' }\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/** Extract key (and optional original name) from an R2FileInfo or raw string. */\nfunction resolveFile(fileOrKey: R2FileInfo | string): { key: string; name: string | undefined } {\n if (typeof fileOrKey === 'string') {\n return { key: fileOrKey, name: undefined }\n }\n return { key: fileOrKey.key, name: fileOrKey.originalName }\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useR2Files(options?: R2Scope): UseR2FilesReturn {\n const [isUploading, setIsUploading] = useState(false)\n const scope = options?.scope ?? 'self'\n\n /** Query string that tells the worker which scope to use. */\n const scopeParams = useMemo(() => {\n const params = new URLSearchParams()\n params.set('scope', scope)\n return params.toString()\n }, [scope])\n\n /** Get auth headers using the DeepSpace auth token. */\n const authHeaders = useCallback(async (): Promise<Record<string, string>> => {\n const token = await getAuthToken()\n if (token) {\n return { Authorization: `Bearer ${token}` }\n }\n return {}\n }, [])\n\n const upload = useCallback(async (file: File | Blob, name?: string): Promise<R2UploadResult> => {\n setIsUploading(true)\n try {\n const headers = await authHeaders()\n const formData = new FormData()\n formData.append('file', file)\n if (name) formData.append('name', name)\n\n const response = await fetch(`/api/files/upload?${scopeParams}`, {\n method: 'POST',\n headers,\n body: formData,\n })\n return await response.json() as R2UploadResult\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : 'Upload failed' }\n } finally {\n setIsUploading(false)\n }\n }, [scopeParams, authHeaders])\n\n const uploadBase64 = useCallback(async (base64Data: string, name: string, mimeType?: string): Promise<R2UploadResult> => {\n setIsUploading(true)\n try {\n const headers = await authHeaders()\n const response = await fetch(`/api/files/upload?${scopeParams}`, {\n method: 'POST',\n headers: { ...headers, 'Content-Type': 'application/json' },\n body: JSON.stringify({ data: base64Data, name, mimeType }),\n })\n return await response.json() as R2UploadResult\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : 'Upload failed' }\n } finally {\n setIsUploading(false)\n }\n }, [scopeParams, authHeaders])\n\n const deleteFile = useCallback(async (fileOrKey: R2FileInfo | string): Promise<{ success: boolean; error?: string }> => {\n const { key } = resolveFile(fileOrKey)\n try {\n const headers = await authHeaders()\n const response = await fetch(`/api/files/${key}?${scopeParams}`, { method: 'DELETE', headers })\n return await response.json() as { success: boolean; error?: string }\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : 'Delete failed' }\n }\n }, [scopeParams, authHeaders])\n\n const list = useCallback(async (prefix?: string): Promise<R2FileInfo[]> => {\n try {\n const headers = await authHeaders()\n const params = new URLSearchParams(scopeParams)\n if (prefix) params.set('prefix', prefix)\n const response = await fetch(`/api/files?${params.toString()}`, { headers })\n const result = await response.json() as { files: R2FileInfo[] }\n return result.files || []\n } catch {\n return []\n }\n }, [scopeParams, authHeaders])\n\n const downloadFile = useCallback(async (fileOrKey: R2FileInfo | string, fileName?: string): Promise<{ success: boolean; error?: string }> => {\n const { key, name: infoName } = resolveFile(fileOrKey)\n try {\n const headers = await authHeaders()\n const response = await fetch(`/api/files/${key}?${scopeParams}`, { headers })\n\n if (!response.ok) {\n const body = await response.json().catch(() => null) as { error?: string } | null\n return { success: false, error: body?.error ?? `Download failed (${response.status})` }\n }\n\n // Derive a display name: explicit arg > R2FileInfo.originalName > Content-Disposition > last key segment\n const disposition = response.headers.get('Content-Disposition')\n const dispositionName = disposition?.match(/filename=\"(.+?)\"/)?.[1]\n const fallbackName = key.split('/').pop() ?? 'download'\n const resolvedName = fileName ?? infoName ?? dispositionName ?? fallbackName\n\n const blob = await response.blob()\n const blobUrl = URL.createObjectURL(blob)\n\n const anchor = document.createElement('a')\n anchor.href = blobUrl\n anchor.download = resolvedName\n document.body.appendChild(anchor)\n anchor.click()\n\n // Clean up\n document.body.removeChild(anchor)\n URL.revokeObjectURL(blobUrl)\n\n return { success: true }\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : 'Download failed' }\n }\n }, [scopeParams, authHeaders])\n\n const readFile = useCallback(async (fileOrKey: R2FileInfo | string): Promise<Response> => {\n const { key } = resolveFile(fileOrKey)\n const headers = await authHeaders()\n const response = await fetch(`/api/files/${key}?${scopeParams}`, { headers })\n if (!response.ok) {\n const body = await response.text().catch(() => '')\n throw new Error(`Failed to read file (${response.status}): ${body}`)\n }\n return response\n }, [scopeParams, authHeaders])\n\n const getUrl = useCallback((fileOrKey: R2FileInfo | string): string => {\n const { key } = resolveFile(fileOrKey)\n return `/api/files/${key}?${scopeParams}`\n }, [scopeParams])\n\n return { upload, uploadBase64, deleteFile, downloadFile, readFile, list, getUrl, isUploading }\n}\n","/**\n * Shared types and utilities for file attachments across miniapps.\n */\n\nexport interface FileAttachment {\n key: string\n url: string\n name: string\n size: number\n mimeType: string\n}\n\nconst IMAGE_MIME_PREFIXES = ['image/']\n\nexport function isImageFile(mimeType: string): boolean {\n return IMAGE_MIME_PREFIXES.some(prefix => mimeType.startsWith(prefix))\n}\n\nexport function formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`\n}\n","/**\n * Deterministic user color assignment.\n * Same user ID always maps to the same color across sessions.\n */\n\nexport const DEFAULT_USER_COLORS = [\n '#f87171', '#fb923c', '#fbbf24', '#34d399',\n '#22d3ee', '#60a5fa', '#a78bfa', '#f472b6',\n '#e879f9', '#4ade80', '#38bdf8', '#facc15',\n] as const\n\nexport function getUserColor(userId: string, palette: readonly string[] = DEFAULT_USER_COLORS): string {\n let hash = 0\n for (let i = 0; i < userId.length; i++) {\n hash = ((hash << 5) - hash + userId.charCodeAt(i)) | 0\n }\n return palette[Math.abs(hash) % palette.length]\n}\n","/**\n * useMessages — subscribe to messages in a channel with send/edit/remove.\n */\n\nimport { useCallback, useMemo } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { Message } from './channel-types'\n\nexport function useMessages(channelId: string | undefined, options?: { parentMessageId?: string }) {\n const where = useMemo(() => {\n if (!channelId) return { channelId: '__none__' }\n const w: Record<string, unknown> = { channelId }\n if (options?.parentMessageId !== undefined) {\n w.parentMessageId = options.parentMessageId\n }\n return w\n }, [channelId, options?.parentMessageId])\n\n const { user } = useUser()\n\n const { records, status, error } = useQuery<Message>('messages', {\n where,\n orderBy: 'createdAt',\n orderDir: 'asc',\n })\n\n const { create, put, remove: removeMutation } = useMutations<Message>('messages')\n\n const send = useCallback(\n (content: string, parentMessageId?: string) => {\n if (!channelId || !user) return\n const data: Record<string, unknown> = { channelId, content, authorId: user.id, edited: false }\n if (parentMessageId) data.parentMessageId = parentMessageId\n return create(data as unknown as Message)\n },\n [channelId, user, create],\n )\n\n const edit = useCallback(\n (messageId: string, newContent: string) => {\n put(messageId, { content: newContent, edited: true } as unknown as Message)\n },\n [put],\n )\n\n /** Hard-delete: drops the row entirely. Use when threads/replies don't matter. */\n const remove = useCallback(\n (messageId: string) => {\n removeMutation(messageId)\n },\n [removeMutation],\n )\n\n /**\n * Soft-delete: sets `deleted: true` on the message. The row stays so threads,\n * read-receipts, and parent links remain coherent. UIs render a tombstone\n * (e.g. \"This message was deleted.\") for messages with `deleted === true`.\n */\n const softDelete = useCallback(\n (messageId: string) => {\n put(messageId, { deleted: true } as unknown as Message)\n },\n [put],\n )\n\n return {\n messages: records as RecordData<Message>[],\n status,\n error,\n send,\n edit,\n remove,\n softDelete,\n }\n}\n","/**\n * useChannels — subscribe to channels with create/archive/update.\n */\n\nimport { useCallback } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { Channel } from './channel-types'\n\nexport function useChannels() {\n const { user } = useUser()\n const { records, status, error } = useQuery<Channel>('channels', {\n orderBy: 'createdAt',\n orderDir: 'asc',\n })\n\n const { create: createMutation, put, remove } = useMutations<Channel>('channels')\n\n const create = useCallback(\n (data: { name: string; type: Channel['type']; description?: string }) => {\n return createMutation({\n name: data.name,\n type: data.type,\n description: data.description ?? '',\n createdBy: user?.id ?? '',\n archived: false,\n } as unknown as Channel)\n },\n [user, createMutation],\n )\n\n const archive = useCallback(\n (channelId: string) => {\n put(channelId, { archived: true } as unknown as Channel)\n },\n [put],\n )\n\n const update = useCallback(\n (channelId: string, data: Partial<Pick<Channel, 'name' | 'description'>>) => {\n put(channelId, data as unknown as Channel)\n },\n [put],\n )\n\n return {\n channels: records as RecordData<Channel>[],\n status,\n error,\n create,\n archive,\n update,\n remove,\n }\n}\n","/**\n * useReactions — subscribe to reactions in a channel with toggle.\n */\n\nimport { useCallback } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { Reaction } from './channel-types'\nimport type { GroupedReaction } from './types'\n\nexport function useReactions(channelId: string | undefined) {\n const { user } = useUser()\n\n const { records, status, error } = useQuery<Reaction>('reactions', {\n where: channelId ? { channelId } : { channelId: '__none__' },\n })\n\n const { create, remove: removeMutation } = useMutations<Reaction>('reactions')\n\n const getReactionsForMessage = useCallback(\n (messageId: string): GroupedReaction[] => {\n const messageReactions = records.filter((r) => r.data.messageId === messageId)\n const grouped = new Map<string, string[]>()\n\n for (const r of messageReactions) {\n if (!grouped.has(r.data.emoji)) grouped.set(r.data.emoji, [])\n grouped.get(r.data.emoji)!.push(r.data.userId)\n }\n\n return Array.from(grouped.entries()).map(([emoji, userIds]) => ({\n emoji,\n count: userIds.length,\n userIds,\n currentUserReacted: user ? userIds.includes(user.id) : false,\n }))\n },\n [records, user],\n )\n\n const toggle = useCallback(\n (messageId: string, emoji: string) => {\n if (!channelId || !user) return\n const existing = records.find(\n (r) => r.data.messageId === messageId && r.data.emoji === emoji && r.data.userId === user.id,\n )\n if (existing) {\n removeMutation(existing.recordId)\n } else {\n create({ messageId, channelId, emoji, userId: user.id } as unknown as Reaction)\n }\n },\n [channelId, user, records, create, removeMutation],\n )\n\n return { reactions: records as RecordData<Reaction>[], status, error, getReactionsForMessage, toggle }\n}\n","/**\n * useChannelMembers — subscribe to channel membership with join/leave.\n */\n\nimport { useCallback } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { ChannelMember } from './channel-types'\n\nexport function useChannelMembers(channelId: string | undefined) {\n const { user } = useUser()\n\n const { records, status, error } = useQuery<ChannelMember>('channel-members', {\n where: channelId ? { channelId } : { channelId: '__none__' },\n })\n\n const { create, remove: removeMutation } = useMutations<ChannelMember>('channel-members')\n\n const join = useCallback(() => {\n if (!channelId || !user) return\n const existing = records.find((r) => r.data.userId === user.id)\n if (existing) return\n create({ channelId, userId: user.id, joinedAt: new Date().toISOString() } as unknown as ChannelMember)\n }, [channelId, user, records, create])\n\n const leave = useCallback(() => {\n if (!channelId || !user) return\n const membership = records.find((r) => r.data.userId === user.id)\n if (membership) removeMutation(membership.recordId)\n }, [channelId, user, records, removeMutation])\n\n const isMember = user ? records.some((r) => r.data.userId === user.id) : false\n\n return { members: records as RecordData<ChannelMember>[], status, error, join, leave, isMember }\n}\n","/**\n * useReadReceipts — track read state per channel for the current user.\n */\n\nimport { useCallback } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { ReadReceipt, Message } from './channel-types'\n\nexport function useReadReceipts() {\n const { user } = useUser()\n\n const { records, status, error } = useQuery<ReadReceipt>('read-receipts', {\n where: user ? { userId: user.id } : { userId: '__none__' },\n })\n\n const { create, put } = useMutations<ReadReceipt>('read-receipts')\n\n const markAsRead = useCallback(\n (channelId: string) => {\n if (!user) return\n const existing = records.find((r) => r.data.channelId === channelId)\n const now = new Date().toISOString()\n if (existing) {\n put(existing.recordId, { lastReadAt: now } as unknown as ReadReceipt)\n } else {\n create({ channelId, userId: user.id, lastReadAt: now } as unknown as ReadReceipt)\n }\n },\n [user, records, create, put],\n )\n\n const getUnreadCount = useCallback(\n (channelId: string, messages: RecordData<Message>[]): number => {\n const receipt = records.find((r) => r.data.channelId === channelId)\n if (!receipt) return messages.length\n const lastReadAt = new Date(receipt.data.lastReadAt).getTime()\n return messages.filter((m) => new Date(m.createdAt).getTime() > lastReadAt).length\n },\n [records],\n )\n\n return { receipts: records as RecordData<ReadReceipt>[], status, error, markAsRead, getUnreadCount }\n}\n","/**\n * useConversation — Shared hook for conversation messaging.\n *\n * Wraps useQuery/useMutations for conv_messages, conv_reactions, conv_members\n * and returns a ConversationObject. Must be used inside a RecordScope for the\n * conversation DO (conv:{channelId}).\n */\n\nimport { useCallback, useMemo } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useRecordContext } from '../storage/context'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { ConvMessageData, ConvReactionData, ConvMemberData } from '../../shared/types'\nimport type { MessageRecord, ReactionRecord, MemberRecord, ConversationObject } from './types'\n\ninterface UseConversationOptions {\n onMessageSent?: (content: string, parentMessageId?: string) => void\n}\n\nexport function useConversation(opts?: UseConversationOptions): ConversationObject {\n const { user } = useUser()\n const { ready } = useRecordContext()\n\n const { records: allMessages } = useQuery<ConvMessageData>('conv_messages', { orderBy: '_created_at' })\n const { records: allReactions } = useQuery<ConvReactionData>('conv_reactions')\n const { records: members } = useQuery<ConvMemberData>('conv_members')\n\n const messageMut = useMutations<ConvMessageData>('conv_messages')\n const reactionMut = useMutations<ConvReactionData>('conv_reactions')\n\n const send = useCallback(\n (content: string, parentMessageId?: string, messageType?: string, metadata?: Record<string, unknown>) => {\n messageMut.create({\n Content: content,\n AuthorId: user?.id ?? '',\n ParentId: parentMessageId ?? '',\n Edited: 0,\n MessageType: messageType ?? 'message',\n Metadata: metadata ? JSON.stringify(metadata) : '',\n })\n opts?.onMessageSent?.(content, parentMessageId)\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps -- depend on the precise opts?.onMessageSent callback, not the whole opts object, to avoid recreating send when unrelated opts fields change\n [messageMut, user?.id, opts?.onMessageSent],\n )\n\n const edit = useCallback(\n (recordId: string, content: string) => {\n const existing = allMessages.find((m) => m.recordId === recordId)\n if (!existing) return\n messageMut.put(recordId, { ...existing.data, Content: content, Edited: 1 })\n },\n [messageMut, allMessages],\n )\n\n const remove = useCallback(\n (recordId: string) => {\n const existing = allMessages.find((m) => m.recordId === recordId)\n if (!existing) return\n messageMut.put(recordId, {\n ...existing.data,\n Content: '',\n MessageType: 'deleted',\n Metadata: '',\n })\n },\n [messageMut, allMessages],\n )\n\n const toggleReaction = useCallback(\n (messageId: string, emoji: string) => {\n const existing = allReactions.find(\n (r) => r.data.MessageId === messageId && r.data.Emoji === emoji && r.data.UserId === user?.id,\n )\n if (existing) {\n reactionMut.remove(existing.recordId)\n } else {\n reactionMut.create({ MessageId: messageId, Emoji: emoji, UserId: user?.id ?? '' })\n }\n },\n [reactionMut, allReactions, user?.id],\n )\n\n return useMemo(\n () => ({\n messages: allMessages as MessageRecord[],\n reactions: allReactions as ReactionRecord[],\n members: members as MemberRecord[],\n status: ready ? ('connected' as const) : ('connecting' as const),\n send,\n edit,\n remove,\n toggleReaction,\n }),\n [allMessages, allReactions, members, ready, send, edit, remove, toggleReaction],\n )\n}\n","/**\n * Shared messaging utility functions.\n */\n\nimport type { ReactionRecord, GroupedReaction, MessageRecord } from './types'\n\nexport function groupReactionsForMessage(\n reactions: ReactionRecord[],\n messageId: string,\n currentUserId: string,\n): GroupedReaction[] {\n const forMessage = reactions.filter((r) => r.data.MessageId === messageId)\n const groups = new Map<string, { count: number; currentUserReacted: boolean; userIds: string[] }>()\n\n for (const r of forMessage) {\n const existing = groups.get(r.data.Emoji) ?? { count: 0, currentUserReacted: false, userIds: [] }\n existing.count++\n existing.userIds.push(r.data.UserId)\n if (r.data.UserId === currentUserId) existing.currentUserReacted = true\n groups.set(r.data.Emoji, existing)\n }\n\n return Array.from(groups.entries()).map(([emoji, data]) => ({\n emoji,\n count: data.count,\n currentUserReacted: data.currentUserReacted,\n userIds: data.userIds,\n }))\n}\n\n/** Default grouping threshold: 5 minutes */\nconst GROUPING_THRESHOLD_MS = 5 * 60 * 1000\n\n/**\n * Determine whether a message should be visually grouped with the previous one\n * (same author, within threshold, no date boundary).\n */\nexport function shouldGroupMessages(\n current: MessageRecord,\n previous: MessageRecord | null | undefined,\n options?: { thresholdMs?: number; dateChanged?: boolean },\n): boolean {\n if (!previous) return false\n if (options?.dateChanged) return false\n if (previous.data.AuthorId !== current.data.AuthorId) return false\n const timeDiff = new Date(current.createdAt).getTime() - new Date(previous.createdAt).getTime()\n return timeDiff < (options?.thresholdMs ?? GROUPING_THRESHOLD_MS)\n}\n\n/**\n * Compute thread reply counts from a flat message array.\n */\nexport function getThreadCounts(messages: MessageRecord[]): Map<string, number> {\n const counts = new Map<string, number>()\n for (const m of messages) {\n if (m.data.ParentId) {\n counts.set(m.data.ParentId, (counts.get(m.data.ParentId) ?? 0) + 1)\n }\n }\n return counts\n}\n\n/**\n * Format a message timestamp for display.\n * Returns \"2:30 PM\" style short time.\n */\nexport function formatMessageTime(dateStr: string): string {\n return new Date(dateStr).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n}\n\n/**\n * Format a full message timestamp.\n * Returns \"Mar 5, 2026 at 2:30 PM\" style.\n */\nexport function formatFullTimestamp(dateStr: string): string {\n const date = new Date(dateStr)\n const datePart = date.toLocaleDateString([], { month: 'short', day: 'numeric', year: 'numeric' })\n const timePart = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n return `${datePart} at ${timePart}`\n}\n","/**\n * Conversation Display Utilities\n *\n * Shared helpers for rendering conversation names, participant lists,\n * and type checks across apps (slack-clone, discord-clone, etc.).\n */\n\nimport type { UserInfo } from '../storage/hooks/useUserLookup'\n\ninterface ConversationData {\n Name: string\n Type: string\n ParticipantIds: string\n ParticipantHash: string\n}\n\n/**\n * Get a display name for a conversation.\n *\n * - Channels: use Name field\n * - DMs: look up the other participant's name\n * - Group DMs: \"User A, User B, +N\"\n */\nexport function getConversationDisplayName(\n conversation: { data: ConversationData },\n currentUserId: string,\n getUser: (id: string) => UserInfo | undefined,\n): string {\n const { Type, Name } = conversation.data\n\n if (Type !== 'dm' && Type !== 'group') {\n return Name || 'Untitled'\n }\n\n const ids = getConversationParticipantIds(conversation, currentUserId)\n\n if (ids.length === 0) return 'Unknown'\n\n if (Type === 'dm') {\n const other = getUser(ids[0])\n return other?.name ?? 'Unknown'\n }\n\n // Group DM\n const MAX_NAMES = 3\n const names: string[] = []\n for (let i = 0; i < Math.min(ids.length, MAX_NAMES); i++) {\n const u = getUser(ids[i])\n names.push(u?.name?.split(' ')[0] ?? 'Unknown')\n }\n const remaining = ids.length - MAX_NAMES\n if (remaining > 0) {\n return `${names.join(', ')}, +${remaining}`\n }\n return names.join(', ')\n}\n\n/**\n * Check if a conversation type is a DM or group DM.\n */\nexport function isDMConversation(type: string): boolean {\n return type === 'dm' || type === 'group'\n}\n\n/**\n * Parse participant IDs from a conversation.\n * Tries ParticipantIds JSON first, falls back to ParticipantHash (colon-separated).\n */\nexport function getConversationParticipantIds(\n conversation: { data: Pick<ConversationData, 'ParticipantIds' | 'ParticipantHash'> },\n excludeUserId?: string,\n): string[] {\n let ids: string[] = []\n\n // Try ParticipantIds JSON first\n try {\n const parsed = JSON.parse(conversation.data.ParticipantIds)\n if (Array.isArray(parsed) && parsed.length > 0) {\n ids = parsed\n }\n } catch {\n // fall through to ParticipantHash\n }\n\n // Fallback to ParticipantHash (colon-separated user IDs)\n if (ids.length === 0 && conversation.data.ParticipantHash) {\n ids = conversation.data.ParticipantHash.split(':')\n }\n\n if (excludeUserId) {\n return ids.filter((id) => id !== excludeUserId)\n }\n return ids\n}\n","/**\n * Safely parse a Metadata field that may be a JSON string, an object, or null/undefined.\n * Returns the parsed object or null on failure.\n */\nexport function parseMessageMetadata(raw: unknown): Record<string, unknown> | null {\n if (!raw) return null\n if (typeof raw === 'object') return raw as Record<string, unknown>\n if (typeof raw === 'string') {\n try { return JSON.parse(raw) } catch { return null }\n }\n return null\n}\n","/**\n * useConversations Hook\n *\n * Standardized conversation directory backed by the `conversations` and\n * `conversation_state` collections in the dir:{appName} DO.\n *\n * Apps that need a sidebar list of channels / DMs / threads should:\n * 1. Add { type: 'dir', instanceId: APP_ID } to SHARED_CONNECTIONS.\n * 2. Call useConversations() inside a RecordProvider.\n */\n\nimport { useCallback, useMemo, useRef } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { DirectoryConversationData, ConversationStateData } from '../../shared/types'\n\n// Module-scoped constant so it's a stable reference across renders (safe to\n// list in useCallback deps without recreating the callback every render).\nconst defaultConvFields = {\n Visibility: 'public',\n Status: 'active',\n AssigneeId: '',\n LinkedRef: '',\n MessageCount: 0,\n}\n\nexport function useConversations() {\n const { records: conversations, status } = useQuery<DirectoryConversationData>('conversations')\n const { create, put } = useMutations<DirectoryConversationData>('conversations')\n const { records: stateRecords } = useQuery<ConversationStateData>('conversation_state')\n const stateMut = useMutations<ConversationStateData>('conversation_state')\n const { user } = useUser()\n\n const ready = status === 'ready'\n\n const recentCreationsRef = useRef(new Map<string, DirectoryConversationData>())\n for (const [id] of recentCreationsRef.current) {\n if (conversations.find((c) => c.recordId === id)) {\n recentCreationsRef.current.delete(id)\n }\n }\n\n const readStateMap = useMemo(\n () => new Map(stateRecords.map((r) => [r.data.ConversationId, r.data.LastReadAt])),\n [stateRecords],\n )\n\n const readMessageCountMap = useMemo(\n () => new Map(stateRecords.map((r) => [r.data.ConversationId, r.data.LastReadMessageCount ?? 0])),\n [stateRecords],\n )\n\n const starredSet = useMemo(\n () => new Set(stateRecords.filter((r) => r.data.Starred).map((r) => r.data.ConversationId)),\n [stateRecords],\n )\n\n const archivedSet = useMemo(\n () => new Set(stateRecords.filter((r) => r.data.Archived).map((r) => r.data.ConversationId)),\n [stateRecords],\n )\n\n\n const getConversationState = useCallback(\n (conversationId: string): ConversationStateData | undefined => {\n return stateRecords.find((r) => r.data.ConversationId === conversationId)?.data\n },\n [stateRecords],\n )\n\n const createChannel = useCallback(\n async (name: string, description?: string) => {\n const existing = conversations.find((r) => r.data.Name === name && r.data.Type !== 'dm')\n if (existing) return existing.recordId\n\n const recordId = await create({\n Name: name,\n Description: description ?? '',\n Type: 'public',\n CreatedBy: user?.id ?? '',\n ParticipantHash: '',\n ParticipantIds: '',\n LastMessageAt: '',\n LastMessagePreview: '',\n LastMessageAuthor: '',\n ...defaultConvFields,\n })\n return recordId\n },\n [create, user?.id, conversations],\n )\n\n const createDM = useCallback(\n async (otherUserId: string) => {\n if (!user?.id) return null\n const hash = [user.id, otherUserId].sort().join(':')\n\n const existing = conversations.find((r) => r.data.ParticipantHash === hash)\n if (existing) return existing.recordId\n\n const data: DirectoryConversationData = {\n Name: '',\n Description: '',\n Type: 'dm',\n Visibility: 'private',\n CreatedBy: user.id,\n ParticipantHash: hash,\n ParticipantIds: JSON.stringify([user.id, otherUserId]),\n Status: 'active',\n AssigneeId: '',\n LinkedRef: '',\n LastMessageAt: '',\n LastMessagePreview: '',\n LastMessageAuthor: '',\n MessageCount: 0,\n }\n const recordId = await create(data)\n recentCreationsRef.current.set(recordId, data)\n return recordId\n },\n [create, user?.id, conversations],\n )\n\n const createGroupDM = useCallback(\n async (participantIds: string[]) => {\n if (!user?.id) return null\n // Add current user + deduplicate\n const allIds = [...new Set([user.id, ...participantIds])]\n if (allIds.length < 3) return null // Use createDM for 2 participants\n\n const hash = allIds.sort().join(':')\n\n const existing = conversations.find((r) => r.data.ParticipantHash === hash)\n if (existing) return existing.recordId\n\n const data: DirectoryConversationData = {\n Name: '',\n Description: '',\n Type: 'group',\n Visibility: 'private',\n CreatedBy: user.id,\n ParticipantHash: hash,\n ParticipantIds: JSON.stringify(allIds),\n Status: 'active',\n AssigneeId: '',\n LinkedRef: '',\n LastMessageAt: '',\n LastMessagePreview: '',\n LastMessageAuthor: '',\n MessageCount: 0,\n }\n const recordId = await create(data)\n recentCreationsRef.current.set(recordId, data)\n return recordId\n },\n [create, user?.id, conversations],\n )\n\n const lookupByName = useCallback(\n (name: string) => {\n const found = conversations.find((r) => r.data.Name === name)\n return found?.recordId ?? null\n },\n [conversations],\n )\n\n const updateLastMessage = useCallback(\n (conversationId: string, preview: string, authorId?: string) => {\n const conv = conversations.find((c) => c.recordId === conversationId)\n const baseData = conv?.data ?? recentCreationsRef.current.get(conversationId)\n if (!baseData) return\n const updatedData = {\n ...baseData,\n LastMessageAt: new Date().toISOString(),\n LastMessagePreview: preview.slice(0, 100),\n LastMessageAuthor: authorId ?? baseData.LastMessageAuthor,\n MessageCount: (baseData.MessageCount ?? 0) + 1,\n }\n put(conversationId, updatedData)\n if (recentCreationsRef.current.has(conversationId)) {\n recentCreationsRef.current.set(conversationId, updatedData)\n }\n },\n [put, conversations],\n )\n\n /** Upsert the user's conversation_state row for a conversation. */\n const upsertState = useCallback(\n (conversationId: string, partial: Partial<ConversationStateData>) => {\n if (!user?.id) return\n const existing = stateRecords.find(\n (r) => r.data.ConversationId === conversationId && r.data.UserId === user.id,\n )\n const base: ConversationStateData = existing?.data ?? {\n ConversationId: conversationId,\n UserId: user.id,\n LastReadAt: '',\n LastReadMessageCount: 0,\n Starred: 0,\n Archived: 0,\n Trashed: 0,\n Labels: '',\n Folder: '',\n }\n const recordId = existing?.recordId ?? `state-${user.id}-${conversationId}`\n stateMut.put(recordId, { ...base, ...partial })\n },\n [stateMut, stateRecords, user?.id],\n )\n\n const markRead = useCallback(\n (conversationId: string) => {\n const conv = conversations.find((c) => c.recordId === conversationId)\n const msgCount = conv?.data.MessageCount ?? recentCreationsRef.current.get(conversationId)?.MessageCount ?? 0\n upsertState(conversationId, {\n LastReadAt: new Date().toISOString(),\n LastReadMessageCount: msgCount,\n })\n },\n [upsertState, conversations],\n )\n\n const toggleStar = useCallback(\n (conversationId: string) => {\n const isStarred = starredSet.has(conversationId)\n upsertState(conversationId, { Starred: isStarred ? 0 : 1 })\n },\n [upsertState, starredSet],\n )\n\n const setArchived = useCallback(\n (conversationId: string, archived: boolean) => {\n upsertState(conversationId, { Archived: archived ? 1 : 0 })\n },\n [upsertState],\n )\n\n const setTrashed = useCallback(\n (conversationId: string, trashed: boolean) => {\n upsertState(conversationId, { Trashed: trashed ? 1 : 0 })\n },\n [upsertState],\n )\n\n const setLabels = useCallback(\n (conversationId: string, labels: string[]) => {\n upsertState(conversationId, { Labels: JSON.stringify(labels) })\n },\n [upsertState],\n )\n\n const setFolder = useCallback(\n (conversationId: string, folder: string) => {\n upsertState(conversationId, { Folder: folder })\n },\n [upsertState],\n )\n\n return {\n conversations: conversations as RecordData<DirectoryConversationData>[],\n ready,\n createChannel,\n createDM,\n createGroupDM,\n lookupByName,\n updateLastMessage,\n readStateMap,\n readMessageCountMap,\n starredSet,\n archivedSet,\n getConversationState,\n upsertState,\n markRead,\n toggleStar,\n setArchived,\n setTrashed,\n setLabels,\n setFolder,\n }\n}\n","/**\n * useCommunities Hook\n *\n * CRUD operations for the `communities` and `memberships` collections\n * in the dir:{appName} DO.\n *\n * Apps that need forums, groups, or boards should:\n * 1. Add { type: 'dir', instanceId: APP_ID } to SHARED_CONNECTIONS.\n * 2. Call useCommunities() inside a RecordProvider.\n */\n\nimport { useCallback, useMemo } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { DirectoryCommunityData, DirectoryMembershipData } from '../../shared/types'\n\nexport function useCommunities() {\n const { records: communities, status } = useQuery<DirectoryCommunityData>('communities')\n const communityMut = useMutations<DirectoryCommunityData>('communities')\n const { records: memberships } = useQuery<DirectoryMembershipData>('memberships')\n const membershipMut = useMutations<DirectoryMembershipData>('memberships')\n const { user } = useUser()\n\n const ready = status === 'ready'\n\n const membershipMap = useMemo(() => {\n const map = new Map<string, DirectoryMembershipData[]>()\n for (const m of memberships) {\n const list = map.get(m.data.CommunityId) ?? []\n list.push(m.data)\n map.set(m.data.CommunityId, list)\n }\n return map\n }, [memberships])\n\n const myMemberships = useMemo(\n () => new Set(\n memberships\n .filter((m) => m.data.UserId === user?.id)\n .map((m) => m.data.CommunityId),\n ),\n [memberships, user?.id],\n )\n\n const createCommunity = useCallback(\n async (name: string, opts?: { description?: string; type?: string; visibility?: string; rules?: string }) => {\n const existing = communities.find((c) => c.data.Name === name)\n if (existing) return existing.recordId\n\n const recordId = await communityMut.create({\n Name: name,\n Description: opts?.description ?? '',\n CreatedBy: user?.id ?? '',\n Type: opts?.type ?? 'community',\n Visibility: opts?.visibility ?? 'public',\n MemberCount: 0,\n Rules: opts?.rules ?? '',\n IconUrl: '',\n CoverUrl: '',\n })\n return recordId\n },\n [communityMut, user?.id, communities],\n )\n\n const updateCommunity = useCallback(\n (communityId: string, updates: Partial<DirectoryCommunityData>) => {\n const existing = communities.find((c) => c.recordId === communityId)\n if (!existing) return\n communityMut.put(communityId, { ...existing.data, ...updates })\n },\n [communityMut, communities],\n )\n\n const joinCommunity = useCallback(\n async (communityId: string, userName: string) => {\n if (!user?.id) return null\n const existing = memberships.find(\n (m) => m.data.CommunityId === communityId && m.data.UserId === user.id,\n )\n if (existing) return existing.recordId\n\n const recordId = await membershipMut.create({\n CommunityId: communityId,\n UserId: user.id,\n UserName: userName,\n Role: 'member',\n JoinedAt: new Date().toISOString(),\n })\n return recordId\n },\n [membershipMut, memberships, user?.id],\n )\n\n const leaveCommunity = useCallback(\n (communityId: string) => {\n if (!user?.id) return\n const membership = memberships.find(\n (m) => m.data.CommunityId === communityId && m.data.UserId === user.id,\n )\n if (membership) {\n membershipMut.remove(membership.recordId)\n }\n },\n [membershipMut, memberships, user?.id],\n )\n\n const getMembersOf = useCallback(\n (communityId: string): DirectoryMembershipData[] => {\n return membershipMap.get(communityId) ?? []\n },\n [membershipMap],\n )\n\n const lookupByName = useCallback(\n (name: string) => {\n const found = communities.find((c) => c.data.Name === name)\n return found?.recordId ?? null\n },\n [communities],\n )\n\n return {\n communities: communities as RecordData<DirectoryCommunityData>[],\n memberships: memberships as RecordData<DirectoryMembershipData>[],\n ready,\n myMemberships,\n createCommunity,\n updateCommunity,\n joinCommunity,\n leaveCommunity,\n getMembersOf,\n lookupByName,\n }\n}\n","/**\n * usePosts Hook\n *\n * CRUD operations for the `posts` collection in the dir:{appName} DO.\n *\n * Apps that need feeds, Q&A, or tweet-style content should:\n * 1. Add { type: 'dir', instanceId: APP_ID } to SHARED_CONNECTIONS.\n * 2. Call usePosts() inside a RecordProvider.\n */\n\nimport { useCallback } from 'react'\nimport { useQuery } from '../storage/hooks/useQuery'\nimport { useMutations } from '../storage/hooks/useMutations'\nimport { useUser } from '../storage/hooks/useUser'\nimport type { RecordData } from '../storage/types'\nimport type { DirectoryPostData } from '../../shared/types'\n\nexport function usePosts(opts?: { communityId?: string }) {\n const { records: allPosts, status } = useQuery<DirectoryPostData>('posts')\n const postMut = useMutations<DirectoryPostData>('posts')\n const { user } = useUser()\n\n const ready = status === 'ready'\n\n const posts = opts?.communityId\n ? allPosts.filter((p) => p.data.CommunityId === opts.communityId)\n : allPosts\n\n const createPost = useCallback(\n async (data: {\n title: string\n content: string\n type?: string\n communityId?: string\n parentId?: string\n tags?: string[]\n linkUrl?: string\n }) => {\n const recordId = await postMut.create({\n Title: data.title,\n Content: data.content,\n AuthorId: user?.id ?? '',\n Type: data.type ?? 'post',\n CommunityId: data.communityId ?? '',\n ParentId: data.parentId ?? '',\n ConversationId: '',\n Status: 'published',\n Tags: data.tags ? JSON.stringify(data.tags) : '',\n LinkUrl: data.linkUrl ?? '',\n })\n return recordId\n },\n [postMut, user?.id],\n )\n\n const updatePost = useCallback(\n (postId: string, updates: Partial<DirectoryPostData>) => {\n const existing = allPosts.find((p) => p.recordId === postId)\n if (!existing) return\n postMut.put(postId, { ...existing.data, ...updates })\n },\n [postMut, allPosts],\n )\n\n const deletePost = useCallback(\n (postId: string) => {\n postMut.remove(postId)\n },\n [postMut],\n )\n\n const setConversationId = useCallback(\n (postId: string, conversationId: string) => {\n updatePost(postId, { ConversationId: conversationId })\n },\n [updatePost],\n )\n\n return {\n posts: posts as RecordData<DirectoryPostData>[],\n ready,\n createPost,\n updatePost,\n deletePost,\n setConversationId,\n }\n}\n","'use client';\n\nimport React, { useEffect, type ReactNode } from 'react';\nimport type { DeepSpaceThemeConfig } from './types';\nimport { applyDeepSpaceTheme, readThemeFromDOM } from './applyTheme';\n\nexport interface DeepSpaceThemeProviderProps {\n children: ReactNode;\n /**\n * Explicit theme config. If omitted, auto-detects from the widget's\n * --color-* CSS custom properties (defined in the @theme block of styles.css).\n */\n theme?: DeepSpaceThemeConfig;\n}\n\n/**\n * DeepSpaceThemeProvider — sets --theme-* CSS variables for pill,\n * and other DeepSpace UI components on deployed sites and mini-apps.\n *\n * **Zero-config**: reads colors from the widget's @theme CSS block.\n * **Explicit**: pass a `theme` prop to override.\n *\n * @example\n * ```tsx\n * // Zero-config — reads from styles.css @theme block\n * <DeepSpaceThemeProvider>\n * <App />\n * </DeepSpaceThemeProvider>\n *\n * // Explicit theme\n * <DeepSpaceThemeProvider theme={{\n * primaryColor: '#0f172a',\n * secondaryColor: '#1e293b',\n * accentColor: '#8b5cf6',\n * textColor: '#f1f5f9',\n * }}>\n * <App />\n * </DeepSpaceThemeProvider>\n * ```\n */\nexport function DeepSpaceThemeProvider({\n children,\n theme,\n}: DeepSpaceThemeProviderProps): React.ReactElement {\n useEffect(() => {\n const root = document.documentElement;\n const config = theme ?? readThemeFromDOM(root);\n applyDeepSpaceTheme(config, root);\n }, [theme]);\n\n return <>{children}</>;\n}\n","import type { DeepSpaceThemeConfig } from './types';\n\n/**\n * CSS color-mix helper — produces `color-mix(in srgb, color pct%, base)`.\n * Works with any valid CSS color format (hex, rgb, rgba, hsl, named).\n */\nfunction mix(color: string, pct: number, base = 'transparent'): string {\n return `color-mix(in srgb, ${color} ${Math.round(pct * 100)}%, ${base})`;\n}\n\n/**\n * Determine whether a CSS color is \"dark\" using relative luminance.\n */\nexport function isDarkColor(color: string): boolean {\n const c = color.trim();\n\n // getComputedStyle normalises colors to rgb()/rgba() — handle that first.\n const rgbMatch = c.match(/^rgba?\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.]+)/);\n if (rgbMatch) {\n const r = parseFloat(rgbMatch[1]) / 255;\n const g = parseFloat(rgbMatch[2]) / 255;\n const b = parseFloat(rgbMatch[3]) / 255;\n return 0.2126 * r + 0.7152 * g + 0.0722 * b < 0.5;\n }\n\n // #hex (#rgb / #rrggbb)\n if (!c.startsWith('#')) return true; // unrecognised format — assume dark\n let hex = c.slice(1);\n if (hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];\n if (hex.length !== 6) return true;\n const r = parseInt(hex.slice(0, 2), 16) / 255;\n const g = parseInt(hex.slice(2, 4), 16) / 255;\n const b = parseInt(hex.slice(4, 6), 16) / 255;\n return 0.2126 * r + 0.7152 * g + 0.0722 * b < 0.5;\n}\n\n/**\n * Static UI tokens for light themes.\n *\n * KEEP IN SYNC with apps/web/app/styles/tokens.css (:root block)\n *\n * Two delivery paths use these same values:\n * - Web app (SSR): defined in tokens.css (instant, no flash)\n * - Deployed sites: applyDeepSpaceTheme() sets them via JS from these constants\n */\nconst UI_TOKENS_LIGHT: Record<string, string> = {\n '--ui-overlay': 'rgba(0, 0, 0, 0.5)',\n '--ui-bg-primary': '#ffffff',\n '--ui-bg-secondary': '#f9fafb',\n '--ui-bg-tertiary': '#f3f4f6',\n '--ui-bg-elevated': '#ffffff',\n '--ui-border': '#e5e7eb',\n '--ui-border-strong': '#d1d5db',\n '--ui-text-primary': '#111827',\n '--ui-text-secondary': '#6b7280',\n '--ui-text-muted': '#9ca3af',\n '--ui-text-disabled': '#d1d5db',\n '--ui-hover-bg': '#f3f4f6',\n '--ui-success': '#10b981',\n '--ui-warning': '#f59e0b',\n '--ui-error': '#ef4444',\n '--ui-shadow-sm': '0 1px 3px rgba(0, 0, 0, 0.1)',\n '--ui-shadow-md': '0 4px 12px rgba(0, 0, 0, 0.1)',\n '--ui-shadow-lg': '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',\n '--ui-vote-up': '#f97316',\n '--ui-vote-down': '#3b82f6',\n};\n\n/** Static UI tokens for dark themes — KEEP IN SYNC with [data-ui-theme=\"dark\"] in tokens.css */\nconst UI_TOKENS_DARK: Record<string, string> = {\n '--ui-overlay': 'rgba(0, 0, 0, 0.7)',\n '--ui-bg-primary': '#1a1a1b',\n '--ui-bg-secondary': '#1e1f20',\n '--ui-bg-tertiary': '#2d2e30',\n '--ui-bg-elevated': '#1e1f20',\n '--ui-border': '#3c4043',\n '--ui-border-strong': '#5a5d61',\n '--ui-text-primary': '#e8eaed',\n '--ui-text-secondary': '#bdc1c6',\n '--ui-text-muted': '#9aa0a6',\n '--ui-text-disabled': '#5a5d61',\n '--ui-hover-bg': '#3c4043',\n '--ui-success': '#34d399',\n '--ui-warning': '#fbbf24',\n '--ui-error': '#f87171',\n '--ui-shadow-sm': '0 1px 3px rgba(0, 0, 0, 0.25)',\n '--ui-shadow-md': '0 4px 12px rgba(0, 0, 0, 0.3)',\n '--ui-shadow-lg': '0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.3)',\n '--ui-vote-up': '#f97316',\n '--ui-vote-down': '#60a5fa',\n};\n\n/** All --ui-* property names for cleanup */\nconst UI_TOKEN_PROPERTIES = [\n '--ui-overlay', '--ui-bg-primary', '--ui-bg-secondary', '--ui-bg-tertiary', '--ui-bg-elevated',\n '--ui-border', '--ui-border-strong',\n '--ui-text-primary', '--ui-text-secondary', '--ui-text-muted', '--ui-text-disabled',\n '--ui-accent', '--ui-accent-hover', '--ui-accent-secondary',\n '--ui-hover-bg', '--ui-active-bg', '--ui-active-text',\n '--ui-success', '--ui-warning', '--ui-error',\n '--ui-shadow-sm', '--ui-shadow-md', '--ui-shadow-lg',\n '--ui-vote-up', '--ui-vote-down',\n] as const;\n\n/**\n * All CSS custom properties set by applyDeepSpaceTheme.\n * Used by clearDeepSpaceTheme to remove stale values on theme switch.\n */\nexport const DEEPSPACE_THEME_PROPERTIES = [\n // Panel & background\n '--theme-panel-bg', '--theme-panel-bg-opaque', '--theme-panel-bg-secondary',\n '--theme-panel-solid', '--theme-top-panel-bg', '--theme-panel-border',\n '--theme-panel-highlight', '--theme-background',\n // Buttons\n '--theme-button-bg', '--theme-button-border', '--theme-button-hover',\n '--theme-button-border-hover', '--theme-button-active', '--theme-button-fg',\n // Text\n '--theme-text', '--theme-text-hover', '--theme-text-secondary', '--theme-text-tertiary',\n // Accent & colors\n '--theme-accent', '--theme-accent-contrast', '--theme-accent-hover',\n '--theme-secondary', '--theme-secondary-hover', '--theme-primary-hover',\n // Shadows\n '--theme-shadow-primary', '--theme-shadow-secondary', '--theme-shadow-accent',\n '--theme-component-shadow',\n // Surfaces\n '--theme-surface', '--theme-surface-solid',\n '--theme-library-card-bg', '--theme-library-surface', '--theme-library-panel-glass',\n // Borders\n '--theme-border',\n // Status\n '--theme-destructive', '--theme-destructive-hover',\n '--theme-success', '--theme-warning', '--theme-warning-bg', '--theme-warning-fg',\n // Glass\n '--theme-highlight', '--theme-font',\n '--beveled-gradient', '--beveled-glass-shadow',\n // UI component tokens\n ...UI_TOKEN_PROPERTIES,\n] as const;\n\n/**\n * Remove all CSS custom properties previously set by applyDeepSpaceTheme.\n * Call before re-applying a new theme to prevent stale variable bleed.\n */\nexport function clearDeepSpaceTheme(\n root: HTMLElement = document.documentElement,\n): void {\n for (const prop of DEEPSPACE_THEME_PROPERTIES) {\n root.style.removeProperty(prop);\n }\n}\n\n/**\n * Apply the correct set of --ui-* inline styles for the given UI theme.\n *\n * Single source of truth for --ui-* token application. Called by:\n * - `applyDeepSpaceTheme()` (initial theme setup, canvas theme changes)\n * - `useUITheme` toggle (user switches dark <-> light)\n *\n * When `accent` overrides are provided they are used directly (the\n * `applyDeepSpaceTheme` path). Otherwise the current --theme-accent /\n * --theme-secondary values are read from computed styles.\n */\nexport function applyUIThemeTokens(\n theme: 'dark' | 'light',\n root: HTMLElement = document.documentElement,\n accent?: { color: string; hover: string; secondary: string },\n): void {\n const dark = theme === 'dark';\n\n // Base tokens (surfaces, text, borders, etc.)\n const base = dark ? UI_TOKENS_DARK : UI_TOKENS_LIGHT;\n for (const [prop, val] of Object.entries(base)) {\n root.style.setProperty(prop, val);\n }\n\n // Accent-derived tokens\n const accentColor = accent?.color\n ?? (getComputedStyle(root).getPropertyValue('--theme-accent').trim() || (dark ? '#818cf8' : '#3b82f6'));\n const accentHover = accent?.hover\n ?? `color-mix(in srgb, ${accentColor} 90%, black)`;\n const secondary = accent?.secondary\n ?? (getComputedStyle(root).getPropertyValue('--theme-secondary').trim() || accentColor);\n\n root.style.setProperty('--ui-accent', accentColor);\n root.style.setProperty('--ui-accent-hover', accentHover);\n root.style.setProperty('--ui-accent-secondary', secondary);\n root.style.setProperty('--ui-active-bg', `color-mix(in srgb, ${accentColor} ${dark ? 15 : 10}%, transparent)`);\n root.style.setProperty('--ui-active-text', dark ? secondary : accentColor);\n}\n\n/**\n * Apply a DeepSpace theme by setting CSS custom properties on the given root element.\n *\n * Maps a simplified theme config to the full set of --theme-* CSS variables\n * consumed by DeepSpace UI components, pills, and panels.\n *\n * Uses CSS color-mix() for derived colors so any valid CSS color format works\n * (hex, rgb, rgba, hsl, etc.).\n */\nexport function applyDeepSpaceTheme(\n config: DeepSpaceThemeConfig,\n root: HTMLElement = document.documentElement,\n): void {\n const {\n primaryColor,\n panelColor = primaryColor,\n secondaryColor,\n accentColor,\n accentContrastColor = '#ffffff',\n textColor,\n shadowColor = '#000000',\n borderColor = secondaryColor,\n backgroundColor = primaryColor,\n highlightColor = '#ffffff',\n glassmorphism = true,\n } = config;\n\n // ---------- derived colors ----------\n\n // Panel backgrounds\n const panelBg = glassmorphism ? mix(panelColor, 0.4) : panelColor;\n const panelBgOpaque = glassmorphism ? mix(panelColor, 0.85) : panelColor;\n const panelSolid = glassmorphism ? mix(panelColor, 0.6, 'white') : panelColor;\n const panelBgSecondary = glassmorphism ? mix(secondaryColor, 0.5) : secondaryColor;\n const topPanelBg = glassmorphism ? mix(panelColor, 0.6) : panelColor;\n\n // Borders\n const panelBorder = glassmorphism ? mix(borderColor, 0.3) : borderColor;\n\n // Buttons\n const buttonBg = glassmorphism ? mix(secondaryColor, 0.4) : secondaryColor;\n const buttonBorder = glassmorphism ? mix(borderColor, 0.3) : borderColor;\n const buttonBorderHover = glassmorphism ? mix(borderColor, 0.7) : borderColor;\n const buttonHover = mix(accentColor, 0.2);\n const buttonActive = mix(accentColor, 0.3);\n\n // Text\n const textSecondary = mix(textColor, 0.8);\n const textTertiary = mix(textColor, 0.6);\n\n // Accents\n const accentHover = mix(accentColor, 0.9, 'black');\n const secondary = mix(secondaryColor, 0.9);\n const secondaryHover = mix(secondaryColor, 0.9, 'black');\n const primaryHover = mix(primaryColor, 0.9, 'black');\n\n // Shadows\n const shadowPrimary = mix(shadowColor, 0.1);\n const shadowSecondary = mix(shadowColor, 0.2);\n const shadowAccent = mix(shadowColor, 0.01);\n const componentShadow =\n `0 1px 3px ${mix(shadowColor, 0.12)}, 0 1px 2px ${mix(shadowColor, 0.24)}`;\n\n // Surfaces & highlights\n const surface = mix(primaryColor, 0.95);\n const panelHighlight = glassmorphism ? mix(textColor, 0.05) : 'transparent';\n\n // Glass gradients\n const beveledGradient = `linear-gradient(180deg,color-mix(in srgb, ${highlightColor} 40%, transparent) 0%,color-mix(in srgb, ${highlightColor} 15%, transparent) 15%,color-mix(in srgb, ${highlightColor} 5%, transparent) 50%,color-mix(in srgb, ${highlightColor} 8%, transparent) 85%,color-mix(in srgb, ${highlightColor} 25%, transparent) 100%)`;\n\n // Glass shadows\n const beveledGlassShadow = [\n `inset 0 1px 1px color-mix(in srgb, ${highlightColor} 80%, transparent)`,\n `inset 0 2px 4px color-mix(in srgb, ${highlightColor} 40%, transparent)`,\n `inset 0 -1px 2px rgba(0, 0, 0, 0.08)`,\n `inset 0 -0.5px 0.5px rgba(0, 0, 0, 0.12)`,\n `0 4px 2px -2px rgba(0, 0, 0, 0.12)`,\n `0 8px 16px -4px rgba(0, 0, 0, 0.1)`,\n `0 0 0 0.5px color-mix(in srgb, ${highlightColor} 20%, transparent) inset`,\n ].join(', ');\n\n // ---------- dark/light detection for UI tokens ----------\n // Respect the user's explicit UI theme preference (set by useUITheme via\n // localStorage) if one exists. Only auto-detect from background luminance\n // when no preference has been set (e.g. deployed sites / mini-apps).\n const preferredUITheme = root.dataset.uiTheme;\n const dark = (preferredUITheme === 'dark' || preferredUITheme === 'light')\n ? preferredUITheme === 'dark'\n : isDarkColor(backgroundColor);\n\n // ---------- data attributes ----------\n root.dataset.glassmorphism = glassmorphism ? 'enabled' : 'disabled';\n if (!preferredUITheme) {\n root.dataset.uiTheme = dark ? 'dark' : 'light';\n }\n\n // ---------- batch set CSS properties ----------\n const updates: [string, string][] = [\n // Panel & background\n ['--theme-panel-bg', panelBg],\n ['--theme-panel-bg-opaque', panelBgOpaque],\n ['--theme-panel-bg-secondary', panelBgSecondary],\n ['--theme-panel-solid', panelSolid],\n ['--theme-top-panel-bg', topPanelBg],\n ['--theme-panel-border', panelBorder],\n ['--theme-panel-highlight', panelHighlight],\n ['--theme-background', backgroundColor],\n\n // Buttons\n ['--theme-button-bg', buttonBg],\n ['--theme-button-border', buttonBorder],\n ['--theme-button-hover', buttonHover],\n ['--theme-button-border-hover', buttonBorderHover],\n ['--theme-button-active', buttonActive],\n ['--theme-button-fg', textColor],\n\n // Text\n ['--theme-text', textColor],\n ['--theme-text-hover', textColor],\n ['--theme-text-secondary', textSecondary],\n ['--theme-text-tertiary', textTertiary],\n\n // Accent & colors\n ['--theme-accent', accentColor],\n ['--theme-accent-contrast', accentContrastColor],\n ['--theme-accent-hover', accentHover],\n ['--theme-secondary', secondary],\n ['--theme-secondary-hover', secondaryHover],\n ['--theme-primary-hover', primaryHover],\n\n // Shadows\n ['--theme-shadow-primary', shadowPrimary],\n ['--theme-shadow-secondary', shadowSecondary],\n ['--theme-shadow-accent', shadowAccent],\n ['--theme-component-shadow', componentShadow],\n\n // Surfaces\n ['--theme-surface', surface],\n ['--theme-surface-solid', primaryColor],\n ['--theme-library-card-bg', 'rgba(24, 24, 27, 0.95)'],\n ['--theme-library-surface', 'rgba(9, 9, 11, 0.92)'],\n ['--theme-library-panel-glass', 'rgba(24, 24, 27, 0.85)'],\n\n // Borders\n ['--theme-border', mix(borderColor, 0.8)],\n\n // Status\n ['--theme-destructive', '#FF3B30'],\n ['--theme-destructive-hover', 'rgba(255, 59, 48, 0.1)'],\n ['--theme-success', '#34C759'],\n ['--theme-warning', '#FF9500'],\n ['--theme-warning-bg', 'rgba(245, 158, 11, 0.15)'],\n ['--theme-warning-fg', '#f59e0b'],\n\n // Glass highlight\n ['--theme-highlight', highlightColor],\n\n // Font\n ['--theme-font', \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\"],\n\n // Glass gradients & shadows\n ['--beveled-gradient', beveledGradient],\n ['--beveled-glass-shadow', beveledGlassShadow],\n ];\n\n for (const [property, value] of updates) {\n root.style.setProperty(property, value);\n }\n\n // ---------- UI component tokens (modals, profile, library, etc.) ----------\n applyUIThemeTokens(dark ? 'dark' : 'light', root, {\n color: accentColor,\n hover: accentHover,\n secondary,\n });\n}\n\n/**\n * Read a DeepSpace theme config from CSS custom properties on the DOM.\n *\n * Reads the widget template's --color-* variables (from the @theme block)\n * and maps them to a DeepSpaceThemeConfig.\n *\n * Falls back to sensible dark-theme defaults if variables are missing.\n */\nexport function readThemeFromDOM(\n root: HTMLElement = document.documentElement,\n): DeepSpaceThemeConfig {\n const styles = getComputedStyle(root);\n const get = (name: string, fallback: string): string =>\n styles.getPropertyValue(name).trim() || fallback;\n\n return {\n primaryColor: get('--color-surface-elevated', '#0f172a'),\n secondaryColor: get('--color-surface-overlay', '#1e293b'),\n accentColor: get('--color-primary', '#818cf8'),\n textColor: get('--color-content', '#f1f5f9'),\n borderColor: get('--color-border', 'rgba(51, 65, 85, 0.5)'),\n backgroundColor: get('--color-surface', '#0a0f1a'),\n highlightColor: '#ffffff',\n shadowColor: '#000000',\n accentContrastColor: '#ffffff',\n };\n}\n","'use client';\n\nimport { useState, useEffect } from 'react';\n\n/**\n * Reads `data-ui-theme` from the root element (set by `applyDeepSpaceTheme`)\n * and re-evaluates when a `canvas-theme-changed` event fires.\n */\nexport function useIsDarkTheme(): boolean {\n const [isDark, setIsDark] = useState(() =>\n typeof document === 'undefined' || document.documentElement.dataset.uiTheme !== 'light',\n );\n\n useEffect(() => {\n const update = (): void => {\n setIsDark(document.documentElement.dataset.uiTheme !== 'light');\n };\n update();\n window.addEventListener('canvas-theme-changed', update);\n return () => window.removeEventListener('canvas-theme-changed', update);\n }, []);\n\n return isDark;\n}\n","/**\n * PlatformProvider — Zero-cost context for platform worker integration.\n *\n * Always present in the component tree (via template main.tsx), but only\n * activates expensive features (WebSocket connections, polling) when a\n * component subscribes via the corresponding hook.\n *\n * Always available (zero overhead):\n * platformFetch(path, init) — authenticated fetch, prepends /platform\n *\n * Subscription-activated (zero cost until first subscriber):\n * useInbox() — inbox entries from InboxRoom WebSocket\n *\n * Auth uses getAuthToken() from deepspace on demand.\n * The template already provides DeepSpaceAuthProvider.\n *\n * Usage:\n * import { PlatformProvider, usePlatform, useInbox } from 'deepspace'\n *\n * // Already in template main.tsx:\n * <PlatformProvider>\n * <App />\n * </PlatformProvider>\n *\n * // In any component:\n * const { platformFetch } = usePlatform()\n * const inbox = useInbox() // activates WebSocket on mount\n */\n\nimport { createContext, useContext, useState, useEffect, useRef, useCallback } from 'react'\nimport { getAuthToken } from '../auth/token'\n\nexport interface InboxEntry {\n conversationId: string\n scope: {\n type: string\n participants?: string[]\n appId?: string\n contentRef?: string\n ticketNumber?: string\n }\n displayName: string\n muted: boolean\n joinedAt: string\n lastMessageAt: string | null\n lastMessagePreview: string | null\n lastMessageAuthor: string | null\n unreadCount: number\n}\n\nexport interface PlatformContextValue {\n /** Authenticated fetch — prepends /platform, sets Authorization + Content-Type */\n platformFetch: (path: string, init?: RequestInit) => Promise<Response>\n /** Subscribe to inbox — returns unsubscribe. Internal use by useInbox(). */\n subscribeInbox: () => () => void\n /** Current inbox entries (populated when at least one subscriber is active) */\n inbox: InboxEntry[]\n}\n\nexport const PlatformContext = createContext<PlatformContextValue | null>(null)\n\nexport function usePlatform(): PlatformContextValue {\n const ctx = useContext(PlatformContext)\n if (!ctx) throw new Error('usePlatform must be used within a PlatformProvider')\n return ctx\n}\n\n/**\n * Subscribe to inbox entries from the InboxRoom WebSocket.\n * The WebSocket connects when the first component calls useInbox()\n * and disconnects when the last one unmounts.\n */\nexport function useInbox(): InboxEntry[] {\n const { inbox, subscribeInbox } = usePlatform()\n useEffect(() => subscribeInbox(), [subscribeInbox])\n return inbox\n}\n\nexport function PlatformProvider({ children }: { children: React.ReactNode }) {\n const [inbox, setInbox] = useState<InboxEntry[]>([])\n\n // --- Subscription-activated inbox WebSocket ---\n const inboxSubscribers = useRef(0)\n const [inboxActive, setInboxActive] = useState(false)\n\n const subscribeInbox = useCallback(() => {\n inboxSubscribers.current++\n if (inboxSubscribers.current === 1) setInboxActive(true)\n return () => {\n inboxSubscribers.current--\n if (inboxSubscribers.current === 0) setInboxActive(false)\n }\n }, [])\n\n useEffect(() => {\n if (!inboxActive) return\n\n let mounted = true\n let ws: WebSocket | null = null\n let pingInterval: ReturnType<typeof setInterval> | undefined\n let retryTimer: ReturnType<typeof setTimeout>\n\n async function connect() {\n const token = await getAuthToken()\n if (!mounted) return\n if (!token) {\n // Auth not ready yet — retry\n retryTimer = setTimeout(connect, 2_000)\n return\n }\n\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n ws = new WebSocket(\n `${protocol}//${window.location.host}/platform/ws/inbox?token=${encodeURIComponent(token)}`,\n )\n\n ws.onmessage = (e) => {\n if (e.data === 'pong') return\n try {\n const msg = JSON.parse(e.data)\n switch (msg.type) {\n case 'inbox_init':\n setInbox(msg.entries)\n break\n case 'inbox_added':\n setInbox((prev) => [...prev, msg.entry])\n break\n case 'inbox_removed':\n setInbox((prev) => prev.filter((x) => x.conversationId !== msg.conversationId))\n break\n case 'inbox_updated':\n setInbox((prev) =>\n prev.map((x) => (x.conversationId === msg.entry.conversationId ? msg.entry : x)),\n )\n break\n }\n } catch {\n // ignore\n }\n }\n\n ws.onclose = () => {\n // Stop pinging the dead socket — otherwise each reconnect stacks\n // another interval that outlives its WebSocket.\n clearInterval(pingInterval)\n pingInterval = undefined\n if (mounted) {\n // Reconnect after brief delay\n retryTimer = setTimeout(connect, 3_000)\n }\n }\n\n // Clear any prior interval before starting a new one (defensive — a\n // reconnect goes through onclose, but this keeps the single-interval\n // invariant even if connect() is ever called twice).\n clearInterval(pingInterval)\n pingInterval = setInterval(() => {\n if (ws?.readyState === WebSocket.OPEN) ws.send('ping')\n }, 30_000)\n }\n\n connect()\n\n return () => {\n mounted = false\n clearTimeout(retryTimer)\n clearInterval(pingInterval)\n ws?.close()\n setInbox([])\n }\n }, [inboxActive])\n\n // --- platformFetch — always available, zero overhead until called ---\n const platformFetch = useCallback(async (path: string, init?: RequestInit) => {\n const token = await getAuthToken()\n const headers = new Headers(init?.headers)\n if (token) headers.set('Authorization', `Bearer ${token}`)\n if (!headers.has('Content-Type') && init?.body) headers.set('Content-Type', 'application/json')\n return fetch(`/platform${path}`, { ...init, headers })\n }, [])\n\n return (\n <PlatformContext.Provider value={{ platformFetch, subscribeInbox, inbox }}>\n {children}\n </PlatformContext.Provider>\n )\n}\n","/**\n * usePlatformWS — Generic WebSocket hook for platform DO connections.\n *\n * Handles all the boilerplate:\n * - Auth token fetch\n * - WebSocket connection with wss:// protocol detection\n * - Exponential backoff reconnect (500ms -> 10s max)\n * - Ping every 30s\n * - Cleanup on unmount or scopeId change\n *\n * Usage:\n * const state = usePlatformWS({\n * path: 'orders', // -> /platform/ws/orders/{scopeId}\n * scopeId: 'app:demo-corp',\n * initialState: { orders: [], items: {}, status: 'connecting' },\n * onMessage: (msg, prev) => {\n * switch (msg.type) {\n * case 'init': return { orders: msg.orders, items: msg.items, status: 'connected' }\n * case 'order_created': return { ...prev, orders: [...prev.orders, msg.order] }\n * default: return prev\n * }\n * },\n * })\n */\n\nimport { useState, useEffect, useRef } from 'react'\nimport { getAuthToken } from '../auth/token'\nimport type { ConnectionStatus } from '../storage/connection-status'\n\nconst MAX_RECONNECT_DELAY = 10_000\nconst BASE_RECONNECT_DELAY = 500\n\nexport interface PlatformWSOptions<S> {\n /** DO path segment — e.g. 'orders' for /platform/ws/orders/{scopeId} */\n path: string\n /** Scope ID — e.g. 'app:demo-corp'. Passing undefined skips connection. */\n scopeId: string | undefined\n /** Initial state before WS connects */\n initialState: S\n /**\n * Message handler. Receives the parsed message and current state.\n * Return a new state to update, or the same `prev` reference to skip.\n * The 'init' message should reset reconnect (handled automatically)\n * and return state with status: 'connected'.\n */\n onMessage: (msg: Record<string, unknown>, prev: S) => S\n}\n\nexport interface PlatformWSResult<S> {\n state: S\n /** Send a JSON message to the WebSocket */\n send: (msg: Record<string, unknown>) => void\n}\n\nexport function usePlatformWS<S extends { status: ConnectionStatus }>(\n options: PlatformWSOptions<S>,\n): PlatformWSResult<S> {\n const { path, scopeId, initialState, onMessage } = options\n const wsRef = useRef<WebSocket | null>(null)\n const pingIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null)\n const [state, setState] = useState<S>(initialState)\n const reconnectAttemptRef = useRef(0)\n const reconnectTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const mountedRef = useRef(true)\n\n // Keep onMessage ref stable to avoid reconnecting when handler identity changes\n const onMessageRef = useRef(onMessage)\n onMessageRef.current = onMessage\n\n const initialStateRef = useRef(initialState)\n initialStateRef.current = initialState\n\n useEffect(() => {\n mountedRef.current = true\n return () => { mountedRef.current = false }\n }, [])\n\n useEffect(() => {\n if (!scopeId) return\n\n setState(initialStateRef.current)\n reconnectAttemptRef.current = 0\n\n async function connect() {\n if (!mountedRef.current || !scopeId) return\n\n const token = await getAuthToken()\n if (!token || !mountedRef.current) {\n reconnectTimerRef.current = setTimeout(connect, 2_000)\n return\n }\n\n const params = new URLSearchParams()\n params.set('token', token)\n\n const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'\n const ws = new WebSocket(\n `${protocol}//${window.location.host}/platform/ws/${path}/${scopeId}?${params}`,\n )\n wsRef.current = ws\n\n ws.onmessage = (e) => {\n if (e.data === 'pong') return\n try {\n const msg = JSON.parse(e.data)\n if (msg.type === 'init') {\n reconnectAttemptRef.current = 0\n }\n setState(prev => onMessageRef.current(msg, prev))\n } catch (err) {\n console.error(`[usePlatformWS:${path}] message parse error:`, err)\n }\n }\n\n ws.onclose = () => {\n if (!mountedRef.current) return\n const attempt = reconnectAttemptRef.current++\n const delay = Math.min(BASE_RECONNECT_DELAY * Math.pow(2, attempt), MAX_RECONNECT_DELAY)\n setState(prev => ({\n ...prev,\n status: (attempt === 0 ? 'connecting' : 'reconnecting') as ConnectionStatus,\n }))\n reconnectTimerRef.current = setTimeout(connect, delay)\n }\n\n if (pingIntervalRef.current) clearInterval(pingIntervalRef.current)\n pingIntervalRef.current = setInterval(() => {\n if (ws.readyState === WebSocket.OPEN) ws.send('ping')\n }, 30_000)\n }\n\n connect()\n\n return () => {\n if (reconnectTimerRef.current) {\n clearTimeout(reconnectTimerRef.current)\n reconnectTimerRef.current = null\n }\n if (pingIntervalRef.current) {\n clearInterval(pingIntervalRef.current)\n pingIntervalRef.current = null\n }\n wsRef.current?.close()\n wsRef.current = null\n }\n }, [scopeId, path])\n\n const send = useRef((msg: Record<string, unknown>) => {\n wsRef.current?.send(JSON.stringify(msg))\n }).current\n\n return { state, send }\n}\n","/**\n * Integration API client.\n *\n * Plain functions for calling third-party integrations (OpenAI, weather, etc.)\n * through the DeepSpace API worker. Works anywhere — React components,\n * event handlers, server actions, outside React entirely.\n *\n * Usage:\n * import { integration } from 'deepspace'\n * const result = await integration.post('openai/chat-completion', { messages })\n */\n\nimport { getAuthToken } from '../auth/token'\n\nconst ENDPOINT_PREFIX = '/api/integrations'\nconst DEFAULT_TIMEOUT_MS = 120_000\n\ninterface IntegrationResponse<T = unknown> {\n success: boolean\n data?: T\n error?: string\n issues?: Array<{ path?: string[]; message: string; code?: string }>\n}\n\ninterface RequestOptions {\n headers?: Record<string, string>\n timeoutMs?: number\n signal?: AbortSignal\n}\n\nfunction resolveUrl(endpoint: string): string {\n if (endpoint.startsWith('http://') || endpoint.startsWith('https://')) {\n return endpoint\n }\n const path = endpoint.startsWith('/') ? endpoint : endpoint ? `${ENDPOINT_PREFIX}/${endpoint}` : ENDPOINT_PREFIX\n if (typeof window !== 'undefined') {\n return `${window.location.origin}${path}`\n }\n return path\n}\n\nasync function request<T>(\n method: string,\n endpoint: string,\n body?: unknown,\n options?: RequestOptions,\n): Promise<IntegrationResponse<T>> {\n const url = resolveUrl(endpoint)\n const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...options?.headers,\n }\n\n // Attach auth token if available\n try {\n const token = await getAuthToken()\n if (token) headers['Authorization'] = `Bearer ${token}`\n } catch {\n // Not signed in — continue without auth\n }\n\n const controller = new AbortController()\n const abortFromCaller = () => controller.abort()\n if (options?.signal?.aborted) controller.abort()\n else options?.signal?.addEventListener('abort', abortFromCaller, { once: true })\n const timeout = setTimeout(() => controller.abort(), timeoutMs)\n\n try {\n const res = await fetch(url, {\n method,\n headers,\n body: body != null ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n })\n\n let payload: Record<string, unknown>\n try {\n payload = (await res.json()) as Record<string, unknown>\n } catch {\n return { success: false, error: `Request failed (${res.status})` }\n }\n\n if (!res.ok || payload.success === false) {\n return {\n success: false,\n error: (payload.error as string) ?? (payload.message as string) ?? `Request failed (${res.status})`,\n ...(payload.issues ? { issues: payload.issues as IntegrationResponse['issues'] } : {}),\n }\n }\n\n // Normalize response — handle both { success, data } and { success, ...rest }\n if ('data' in payload) {\n return { success: true, data: payload.data as T }\n }\n const rest = { ...payload }\n delete rest.success\n return { success: true, data: rest as T }\n } catch (err: unknown) {\n if (err instanceof DOMException && err.name === 'AbortError') {\n return { success: false, error: `Request timed out after ${timeoutMs}ms` }\n }\n return {\n success: false,\n error: err instanceof Error ? err.message : 'Request failed',\n }\n } finally {\n clearTimeout(timeout)\n options?.signal?.removeEventListener('abort', abortFromCaller)\n }\n}\n\nexport const integration = {\n post<T = unknown>(endpoint: string, data?: unknown, options?: RequestOptions) {\n return request<T>('POST', endpoint, data, options)\n },\n get<T = unknown>(endpoint: string, params?: Record<string, string | number | boolean | null | undefined>, options?: RequestOptions) {\n let url = endpoint\n if (params) {\n const searchParams = new URLSearchParams()\n for (const [k, v] of Object.entries(params)) {\n if (v != null) searchParams.set(k, String(v))\n }\n const qs = searchParams.toString()\n if (qs) url = `${endpoint}?${qs}`\n }\n return request<T>('GET', url, undefined, options)\n },\n put<T = unknown>(endpoint: string, data?: unknown, options?: RequestOptions) {\n return request<T>('PUT', endpoint, data, options)\n },\n delete<T = unknown>(endpoint: string, data?: unknown, options?: RequestOptions) {\n return request<T>('DELETE', endpoint, data, options)\n },\n}\n\nexport type { IntegrationResponse, RequestOptions }\n","/**\n * useVoiceAgent - managed browser voice on OpenAI Realtime, zero config.\n *\n * The hook mints a credit-gated ephemeral token via our proxy\n * (`openai-realtime/create-session`), connects the browser to OpenAI over raw\n * WebRTC, and on teardown reports the elapsed duration to\n * `openai-realtime/settle-session` so the user is billed for actual minutes. A\n * client timer stops the call at `maxMinutes` (OpenAI also hard-disconnects at\n * 60). The developer holds no key and writes no WebRTC.\n *\n * Billing note: settle is best-effort. If it never lands (crash, offline), the\n * server cron settles the session to the reserved cap - so the platform never\n * loses money; the hook's settle just refines DOWN to actual minutes and\n * releases a never-started session immediately.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { integration } from '../integration'\n\nconst OPENAI_REALTIME_BASE = 'https://api.openai.com/v1'\n\nexport type VoiceStatus = 'idle' | 'connecting' | 'live' | 'ended' | 'error'\n\nexport interface VoiceTranscriptEntry {\n role: 'user' | 'assistant'\n content: string\n}\n\nexport interface UseVoiceAgentOptions {\n instructions?: string\n voice?: string\n /** OpenAI tool definitions, passed through to the session. */\n tools?: Record<string, unknown>[]\n /** Hard cap; the hook stops the call at this many minutes. */\n maxMinutes?: number\n /** Called when the model invokes a tool; the return value is sent back. */\n onToolCall?: (name: string, args: unknown) => Promise<unknown> | unknown\n}\n\nexport interface UseVoiceAgentResult {\n status: VoiceStatus\n start: (overrides?: Partial<UseVoiceAgentOptions>) => Promise<void>\n stop: () => Promise<void>\n isMuted: boolean\n toggleMute: () => void\n isAgentSpeaking: boolean\n transcript: VoiceTranscriptEntry[]\n error: string | null\n /** Escape hatches for advanced use. */\n pc: RTCPeerConnection | null\n dataChannel: RTCDataChannel | null\n}\n\nconst ZERO_USAGE = { inputAudioTokens: 0, outputAudioTokens: 0, inputTextTokens: 0, outputTextTokens: 0 }\n\n/** Token accounting block within a Realtime `response.done` event. */\ninterface RealtimeTokenDetails {\n audio_tokens?: number\n text_tokens?: number\n}\n\n/** A single output item within a Realtime response (e.g. a function call). */\ninterface RealtimeOutputItem {\n type?: string\n name?: string\n call_id?: string\n arguments?: string\n}\n\n/**\n * Loosely-typed OpenAI Realtime wire event. Only the fields this hook reads are\n * modeled; everything else is left off since the protocol is large and dynamic.\n */\ninterface RealtimeEvent {\n type?: string\n delta?: string\n transcript?: string\n response?: {\n usage?: {\n input_token_details?: RealtimeTokenDetails\n output_token_details?: RealtimeTokenDetails\n }\n output?: RealtimeOutputItem[]\n }\n}\n\nexport function useVoiceAgent(options: UseVoiceAgentOptions = {}): UseVoiceAgentResult {\n const [status, setStatus] = useState<VoiceStatus>('idle')\n const [isMuted, setIsMuted] = useState(false)\n const [isAgentSpeaking, setIsAgentSpeaking] = useState(false)\n const [transcript, setTranscript] = useState<VoiceTranscriptEntry[]>([])\n const [error, setError] = useState<string | null>(null)\n\n const pcRef = useRef<RTCPeerConnection | null>(null)\n const dcRef = useRef<RTCDataChannel | null>(null)\n const streamRef = useRef<MediaStream | null>(null)\n const audioRef = useRef<HTMLAudioElement | null>(null)\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const sessionIdRef = useRef<string | null>(null)\n const liveStartRef = useRef<number | null>(null)\n const settledRef = useRef(false)\n const usageRef = useRef({ ...ZERO_USAGE })\n\n // Keep the latest options without re-creating callbacks (onToolCall etc).\n const optsRef = useRef(options)\n optsRef.current = options\n\n const settle = useCallback(async () => {\n const sessionId = sessionIdRef.current\n if (!sessionId || settledRef.current) return\n settledRef.current = true\n const durationSeconds = liveStartRef.current ? Math.max(0, (Date.now() - liveStartRef.current) / 1000) : 0\n try {\n await integration.post('openai-realtime/settle-session', {\n sessionId,\n durationSeconds,\n usage: usageRef.current,\n })\n } catch {\n // Best-effort: the server cron settles to the cap if this never lands.\n }\n }, [])\n\n const cleanup = useCallback(() => {\n if (timerRef.current) {\n clearTimeout(timerRef.current)\n timerRef.current = null\n }\n try {\n dcRef.current?.close()\n } catch {\n // ignore\n }\n try {\n streamRef.current?.getTracks().forEach((t) => t.stop())\n } catch {\n // ignore\n }\n try {\n if (pcRef.current) pcRef.current.onconnectionstatechange = null\n pcRef.current?.close()\n } catch {\n // ignore\n }\n if (audioRef.current) audioRef.current.srcObject = null\n dcRef.current = null\n pcRef.current = null\n streamRef.current = null\n audioRef.current = null\n }, [])\n\n const stop = useCallback(async () => {\n cleanup()\n await settle()\n setIsAgentSpeaking(false)\n setStatus((s) => (s === 'error' ? s : 'ended'))\n }, [cleanup, settle])\n\n const appendTranscript = useCallback((role: 'user' | 'assistant', delta: string) => {\n if (!delta) return\n setTranscript((prev) => {\n const last = prev[prev.length - 1]\n if (last && last.role === role) return [...prev.slice(0, -1), { role, content: last.content + delta }]\n return [...prev, { role, content: delta }]\n })\n }, [])\n\n // Tool-call round-trip: run the dev's handler, send the output back, ask for the\n // next response. Event field names track the current Realtime API; adjust here\n // if OpenAI renames them.\n const handleToolCall = useCallback(async (item: { name?: string; call_id?: string; arguments?: string }) => {\n const onToolCall = optsRef.current.onToolCall\n let output: unknown = null\n if (onToolCall && item.name) {\n let args: unknown = {}\n try {\n args = item.arguments ? JSON.parse(item.arguments) : {}\n } catch {\n args = {}\n }\n try {\n output = await onToolCall(item.name, args)\n } catch (e) {\n output = { error: e instanceof Error ? e.message : String(e) }\n }\n }\n const dc = dcRef.current\n if (!dc || dc.readyState !== 'open') return\n dc.send(\n JSON.stringify({\n type: 'conversation.item.create',\n item: { type: 'function_call_output', call_id: item.call_id, output: JSON.stringify(output ?? null) },\n }),\n )\n dc.send(JSON.stringify({ type: 'response.create' }))\n }, [])\n\n const handleEvent = useCallback(\n (raw: unknown) => {\n if (typeof raw !== 'string') return\n // OpenAI Realtime events are a large, dynamic wire protocol with deeply\n // nested optional fields; model as a loose record and read fields defensively.\n let ev: RealtimeEvent\n try {\n ev = JSON.parse(raw) as RealtimeEvent\n } catch {\n return\n }\n switch (ev.type) {\n case 'response.output_audio_transcript.delta':\n case 'response.audio_transcript.delta':\n appendTranscript('assistant', ev.delta ?? '')\n break\n case 'conversation.item.input_audio_transcription.completed':\n appendTranscript('user', ev.transcript ?? '')\n break\n case 'output_audio_buffer.started':\n setIsAgentSpeaking(true)\n break\n case 'output_audio_buffer.stopped':\n setIsAgentSpeaking(false)\n break\n case 'response.done': {\n const u = ev.response?.usage\n if (u) {\n usageRef.current.inputAudioTokens += u.input_token_details?.audio_tokens ?? 0\n usageRef.current.outputAudioTokens += u.output_token_details?.audio_tokens ?? 0\n usageRef.current.inputTextTokens += u.input_token_details?.text_tokens ?? 0\n usageRef.current.outputTextTokens += u.output_token_details?.text_tokens ?? 0\n }\n for (const item of ev.response?.output ?? []) {\n if (item?.type === 'function_call') void handleToolCall(item)\n }\n break\n }\n }\n },\n [appendTranscript, handleToolCall],\n )\n\n const start = useCallback(\n async (overrides: Partial<UseVoiceAgentOptions> = {}) => {\n // A second start() while a session is in flight (re-click / re-trigger) must\n // not abandon the prior one: settle + tear it down first, else its sessionId\n // is lost (the cron bills it at the full cap) and its mic/peer/timer leak.\n if (sessionIdRef.current) {\n cleanup()\n await settle()\n }\n const o = { ...optsRef.current, ...overrides }\n setError(null)\n setTranscript([])\n setIsAgentSpeaking(false)\n setStatus('connecting')\n settledRef.current = false\n liveStartRef.current = null\n sessionIdRef.current = null\n usageRef.current = { ...ZERO_USAGE }\n\n try {\n const res = await integration.post<{ sessionId: string; clientSecret: string; model: string; maxMinutes: number }>(\n 'openai-realtime/create-session',\n { instructions: o.instructions, voice: o.voice, tools: o.tools, maxMinutes: o.maxMinutes },\n )\n if (!res.success || !res.data) throw new Error(res.error ?? 'Failed to start voice session')\n const { sessionId, clientSecret, model, maxMinutes } = res.data\n sessionIdRef.current = sessionId\n\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true })\n streamRef.current = stream\n\n const pc = new RTCPeerConnection()\n pcRef.current = pc\n // End + settle on a real connection drop (failed/closed) rather than waiting\n // for the maxMinutes timer, so a dropped call bills connected time, not the\n // cap. 'disconnected' is transient (may recover) so it is not acted on.\n pc.onconnectionstatechange = () => {\n if (pc.connectionState === 'failed' || pc.connectionState === 'closed') void stop()\n }\n const audio = new Audio()\n audio.autoplay = true\n audioRef.current = audio\n pc.ontrack = (e) => {\n audio.srcObject = e.streams[0]\n }\n stream.getTracks().forEach((t) => pc.addTrack(t, stream))\n\n const dc = pc.createDataChannel('oai-events')\n dcRef.current = dc\n dc.onmessage = (e) => handleEvent(e.data)\n\n const offer = await pc.createOffer()\n await pc.setLocalDescription(offer)\n const sdpRes = await fetch(`${OPENAI_REALTIME_BASE}/realtime/calls?model=${encodeURIComponent(model)}`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${clientSecret}`, 'Content-Type': 'application/sdp' },\n body: offer.sdp,\n })\n if (!sdpRes.ok) throw new Error(`Realtime connect failed (${sdpRes.status})`)\n const answer = await sdpRes.text()\n await pc.setRemoteDescription({ type: 'answer', sdp: answer })\n\n liveStartRef.current = Date.now()\n setStatus('live')\n timerRef.current = setTimeout(() => void stop(), (maxMinutes ?? 30) * 60_000)\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err))\n setStatus('error')\n cleanup()\n // Release the reservation immediately (durationSeconds = 0 if never live).\n await settle()\n }\n },\n [cleanup, settle, stop, handleEvent],\n )\n\n const toggleMute = useCallback(() => {\n const stream = streamRef.current\n if (!stream) return\n setIsMuted((muted) => {\n const next = !muted\n stream.getAudioTracks().forEach((t) => (t.enabled = !next))\n return next\n })\n }, [])\n\n // Settle + tear down on unmount so a closed tab never leaves a dangling session.\n useEffect(() => {\n return () => {\n cleanup()\n void settle()\n }\n }, [cleanup, settle])\n\n return {\n status,\n start,\n stop,\n isMuted,\n toggleMute,\n isAgentSpeaking,\n transcript,\n error,\n pc: pcRef.current,\n dataChannel: dcRef.current,\n }\n}\n","/**\n * Pure decoders for the Vercel AI SDK v5 `toUIMessageStreamResponse` body.\n *\n * The wire format is SSE: each event is `data: <json>\\n\\n`, terminated by\n * `data: [DONE]\\n\\n`. Each event carries a `type` discriminator\n * (`text-delta`, `tool-input-available`, `tool-output-available`,\n * `tool-input-error`, `tool-output-error`, `error`, `abort`, …) — see the\n * `UIMessageChunk` union in `ai/dist/index.d.ts`.\n *\n * This module decodes those chunks into a small action vocabulary that\n * the React layer then applies to its state. Keeping decode pure means\n * the v5-boundary behavior is fully testable without spinning up a\n * component tree.\n */\n\n/** Raw v5 stream chunk, as parsed off the wire. `type` may be any string. */\nexport type AiStreamChunk = Record<string, unknown> & { type?: unknown }\n\n/**\n * Strip one SSE line's framing. Returns the parsed JSON payload, or `null`\n * for blank lines, comment lines (`:` prefix), non-`data:` SSE fields, the\n * `[DONE]` terminator, or any line whose payload doesn't parse as JSON.\n *\n * Multi-line `data:` events (per the SSE spec, joined by `\\n` before\n * dispatch) are NOT supported — v5's emitter never produces them. If you\n * hit a wire intermediary that re-splits long payloads (e.g. a proxy that\n * caps line length), upgrade this to accumulate consecutive `data:` lines\n * until a blank line.\n */\nexport function parseSseLine(line: string): AiStreamChunk | null {\n if (!line) return null\n if (line.startsWith(':')) return null // SSE comment\n if (!line.startsWith('data:')) return null // we only consume `data:` events\n // SSE allows exactly one space after the colon to be elided. Trim a\n // trailing \\r so CRLF-framed responses still hit the [DONE] short-circuit.\n const raw = line.slice(5).replace(/^ /, '').replace(/\\r$/, '')\n if (raw === '[DONE]') return null\n try {\n const parsed: unknown = JSON.parse(raw)\n if (!parsed || typeof parsed !== 'object') return null\n return parsed as AiStreamChunk\n } catch {\n return null\n }\n}\n\n/**\n * Action vocabulary the React layer applies. Every `decodeAiStreamChunk`\n * output is one of these — `null` means the chunk is intentionally ignored\n * (lifecycle markers, reasoning we don't surface yet, etc.).\n *\n * Wire field renames (v5): chunks carry `input` / `output` directly. We\n * map them to `args` / `result` here so persisted UI shapes stay v4-stable.\n */\nexport type AiStreamAction =\n | { type: 'append-text'; delta: string }\n | { type: 'upsert-tool-call'; toolCallId: string; toolName: string; input: unknown }\n | { type: 'finalize-tool-call'; toolCallId: string; result: unknown }\n /**\n * Schema validation rejected the input before the tool ran; no preceding\n * `upsert-tool-call` was emitted, so the React layer must create the\n * invocation and finalize it as failed in one step.\n */\n | { type: 'fail-tool-input'; toolCallId: string; toolName: string; input: unknown; errorText: string }\n /** Tool execution failed; a previous `upsert-tool-call` exists to finalize. */\n | { type: 'fail-tool-output'; toolCallId: string; errorText: string }\n | { type: 'stream-error'; errorText: string }\n /** Server-side abort with no error chunk to follow. */\n | { type: 'abort' }\n\n/** Decode one v5 UIMessage stream chunk into an action, or `null` to ignore. */\nexport function decodeAiStreamChunk(chunk: AiStreamChunk): AiStreamAction | null {\n if (typeof chunk.type !== 'string') return null\n\n switch (chunk.type) {\n case 'text-delta': {\n const delta = typeof chunk.delta === 'string' ? chunk.delta : ''\n if (!delta) return null\n return { type: 'append-text', delta }\n }\n\n case 'tool-input-available': {\n if (typeof chunk.toolCallId !== 'string' || typeof chunk.toolName !== 'string') return null\n return {\n type: 'upsert-tool-call',\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n input: chunk.input,\n }\n }\n\n case 'tool-output-available': {\n if (typeof chunk.toolCallId !== 'string') return null\n // The wire emits `output: part.output` directly (no `toJSONValue`\n // wrapping at this layer). When `part.output` is `undefined`,\n // `JSON.stringify` drops the key entirely, so `'output' in chunk`\n // reads false on the receiving side. Treat absence as an explicit\n // `null` result — finalizing the invocation is what stops the\n // spinner; ignoring the chunk would hang the UI forever for any\n // tool whose `execute` returns undefined.\n const result = 'output' in chunk ? chunk.output : null\n return { type: 'finalize-tool-call', toolCallId: chunk.toolCallId, result }\n }\n\n case 'tool-input-error': {\n if (typeof chunk.toolCallId !== 'string' || typeof chunk.toolName !== 'string') return null\n const errorText = typeof chunk.errorText === 'string' ? chunk.errorText : 'Tool input rejected'\n return {\n type: 'fail-tool-input',\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n input: chunk.input,\n errorText,\n }\n }\n\n case 'tool-output-error': {\n if (typeof chunk.toolCallId !== 'string') return null\n const errorText = typeof chunk.errorText === 'string' ? chunk.errorText : 'Tool execution failed'\n return { type: 'fail-tool-output', toolCallId: chunk.toolCallId, errorText }\n }\n\n case 'error': {\n const errorText = typeof chunk.errorText === 'string' ? chunk.errorText : 'Stream error'\n return { type: 'stream-error', errorText }\n }\n\n case 'abort':\n return { type: 'abort' }\n\n // Forward-compat: silently ignore lifecycle markers (start/finish,\n // start-step/finish-step), text-start/-end, tool-input-start/-delta,\n // reasoning-*, source-*, message-metadata.\n default:\n return null\n }\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { useAuth } from './auth'\nimport { useUser } from './storage'\nimport {\n ASYNC_RESOURCE_DEFAULTS,\n canAutoLoadPagedResource,\n clampPagedResourceItems,\n deriveLoadState,\n getPagedResourceCooldownMs,\n isAsyncResourceSlow,\n} from './status-core'\n\nexport {\n ASYNC_RESOURCE_DEFAULTS,\n canAutoLoadPagedResource,\n clampPagedResourceItems,\n deriveLoadState,\n getPagedResourceCooldownMs,\n isAsyncResourceSlow,\n}\nexport type {\n AsyncResourceDefaults,\n DeriveLoadStateInput,\n LoadState,\n LoadStateAuthInput,\n LoadStateStatusInput,\n} from './status-core'\n\nexport interface UseAuthStatusOptions {\n requireSignedIn?: boolean\n}\n\nexport interface UseAuthProfileReadyOptions extends UseAuthStatusOptions {\n requireUser?: boolean\n}\n\n/** @deprecated Use `UseAuthProfileReadyOptions` instead. */\nexport type UseAuthReadyOptions = UseAuthProfileReadyOptions\n\nexport function useAuthStatus(options: UseAuthStatusOptions = {}) {\n const auth = useAuth()\n const status = deriveLoadState({\n auth: {\n isLoaded: auth.isLoaded,\n isSignedIn: auth.isSignedIn,\n requireSignedIn: options.requireSignedIn,\n },\n })\n\n return {\n ...auth,\n status,\n isReady: status === 'ready',\n }\n}\n\nexport function useAuthProfileReady(options: UseAuthProfileReadyOptions = {}) {\n const auth = useAuth()\n const { user, isLoading: userLoading, refetch } = useUser()\n const status = deriveLoadState({\n auth: {\n isLoaded: auth.isLoaded,\n isSignedIn: auth.isSignedIn,\n requireSignedIn: options.requireSignedIn,\n requireUser: options.requireUser,\n userLoading,\n },\n })\n\n return {\n ...auth,\n user,\n userLoading,\n refetchUser: refetch,\n status,\n isReady: status === 'ready',\n }\n}\n\n/** @deprecated Use `useAuthStatus` for auth-only UI or `useAuthProfileReady` for profile-backed UI. */\nexport const useAuthReady = useAuthProfileReady\n\nexport type AsyncResourceStatus = 'idle' | 'loading' | 'ready' | 'error'\nexport type PagedResourceStatus = AsyncResourceStatus\n\nexport interface AsyncResourceState<T> {\n status: AsyncResourceStatus\n data: T | null\n error: string | null\n isRefreshing: boolean\n isSlow: boolean\n retryCount: number\n}\n\nexport interface UseAsyncResourceOptions<T> {\n enabled?: boolean\n initialData?: T | null\n keepPreviousData?: boolean\n retry?: number\n retryDelayMs?: number\n slowAfterMs?: number\n}\n\nexport interface PagedResourceFetchArgs {\n page: number\n pageSize: number\n signal: AbortSignal\n}\n\nexport interface PagedResourcePage<T> {\n items: T[]\n hasMore?: boolean\n}\n\nexport interface PagedResourceState<T> {\n status: PagedResourceStatus\n items: T[]\n error: string | null\n warning: string | null\n hasMore: boolean\n isLoadingInitial: boolean\n isLoadingMore: boolean\n isRefreshing: boolean\n}\n\nexport interface UsePagedResourceOptions<T> {\n enabled?: boolean\n initialItems?: T[]\n pageSize?: number\n maxItemsPerPage?: number\n keepPreviousData?: boolean\n autoRetryOnError?: boolean\n retryDelayMs?: number\n maxRetryDelayMs?: number\n}\n\nexport function useAsyncResource<T>(\n fetcher: (signal: AbortSignal) => Promise<T>,\n deps: readonly unknown[],\n options: UseAsyncResourceOptions<T> = {},\n) {\n const enabled = options.enabled ?? true\n const keepPreviousData = options.keepPreviousData ?? true\n const retry = options.retry ?? ASYNC_RESOURCE_DEFAULTS.retry\n const retryDelayMs = options.retryDelayMs ?? ASYNC_RESOURCE_DEFAULTS.retryDelayMs\n const slowAfterMs = options.slowAfterMs ?? ASYNC_RESOURCE_DEFAULTS.slowAfterMs\n const [state, setState] = useState<AsyncResourceState<T>>(() => ({\n status: enabled ? 'loading' : 'idle',\n data: options.initialData ?? null,\n error: null,\n isRefreshing: false,\n isSlow: false,\n retryCount: 0,\n }))\n const requestIdRef = useRef(0)\n const [reloadTick, setReloadTick] = useState(0)\n\n const reload = useCallback(() => {\n setReloadTick((tick) => tick + 1)\n }, [])\n\n useEffect(() => {\n if (!enabled) {\n setState((prev) => ({ ...prev, status: 'idle', isRefreshing: false, isSlow: false, retryCount: 0 }))\n return\n }\n\n const requestId = ++requestIdRef.current\n const controller = new AbortController()\n let retryTimer: ReturnType<typeof setTimeout> | null = null\n let slowTimer: ReturnType<typeof setTimeout> | null = null\n\n setState((prev) => {\n const hasData = keepPreviousData && prev.data != null\n return {\n ...prev,\n status: hasData ? 'ready' : 'loading',\n error: null,\n isRefreshing: hasData,\n isSlow: false,\n data: hasData ? prev.data : (options.initialData ?? null),\n retryCount: 0,\n }\n })\n\n if (slowAfterMs > 0) {\n const startedAt = Date.now()\n slowTimer = setTimeout(() => {\n if (requestIdRef.current !== requestId || controller.signal.aborted) return\n setState((prev) => ({\n ...prev,\n isSlow: isAsyncResourceSlow({ startedAt, now: Date.now(), slowAfterMs }),\n }))\n }, slowAfterMs)\n }\n\n const wait = (ms: number) =>\n new Promise<void>((resolve) => {\n retryTimer = setTimeout(resolve, ms)\n })\n\n const load = async () => {\n let attempt = 0\n while (!controller.signal.aborted) {\n try {\n const data = await fetcher(controller.signal)\n if (requestIdRef.current !== requestId || controller.signal.aborted) return\n if (slowTimer) clearTimeout(slowTimer)\n setState({ status: 'ready', data, error: null, isRefreshing: false, isSlow: false, retryCount: attempt })\n return\n } catch (err: unknown) {\n if (requestIdRef.current !== requestId || controller.signal.aborted) return\n const message = err instanceof Error ? err.message : 'Request failed'\n if (attempt < retry) {\n attempt += 1\n setState((prev) => ({\n ...prev,\n error: message,\n retryCount: attempt,\n isRefreshing: keepPreviousData && prev.data != null,\n }))\n await wait(retryDelayMs)\n continue\n }\n\n if (slowTimer) clearTimeout(slowTimer)\n setState((prev) => ({\n status: keepPreviousData && prev.data != null ? 'ready' : 'error',\n data: keepPreviousData ? prev.data : null,\n error: message,\n isRefreshing: false,\n isSlow: false,\n retryCount: attempt,\n }))\n return\n }\n }\n }\n\n load()\n\n return () => {\n if (retryTimer) clearTimeout(retryTimer)\n if (slowTimer) clearTimeout(slowTimer)\n controller.abort()\n }\n // Deps are the caller-supplied ...deps plus the primitive options; the\n // inline load() closure (and fetcher/options.initialData it captures) is\n // deliberately not listed to avoid re-running the fetch/retry lifecycle on\n // every render (which would cause request storms).\n // eslint-disable-next-line react-hooks/exhaustive-deps -- caller-supplied ...deps is the re-fetch contract; inline load()/fetcher excluded to avoid request storms\n }, [enabled, keepPreviousData, retry, retryDelayMs, slowAfterMs, reloadTick, ...deps])\n\n return useMemo(() => ({ ...state, reload }), [state, reload])\n}\n\nexport function usePagedResource<T>(\n fetchPage: (args: PagedResourceFetchArgs) => Promise<PagedResourcePage<T>>,\n deps: readonly unknown[],\n options: UsePagedResourceOptions<T> = {},\n) {\n const enabled = options.enabled ?? true\n const initialItems = options.initialItems ?? []\n const pageSize = options.pageSize ?? 20\n const maxItemsPerPage = options.maxItemsPerPage ?? pageSize\n const keepPreviousData = options.keepPreviousData ?? true\n const autoRetryOnError = options.autoRetryOnError ?? false\n const retryDelayMs = options.retryDelayMs ?? ASYNC_RESOURCE_DEFAULTS.retryDelayMs\n const maxRetryDelayMs = options.maxRetryDelayMs ?? 30_000\n const hasInitialItems = initialItems.length > 0\n const [state, setState] = useState<PagedResourceState<T>>(() => ({\n status: enabled ? (hasInitialItems ? 'ready' : 'loading') : 'idle',\n items: initialItems,\n error: null,\n warning: null,\n hasMore: true,\n isLoadingInitial: enabled && !hasInitialItems,\n isLoadingMore: false,\n isRefreshing: false,\n }))\n const requestIdRef = useRef(0)\n const controllerRef = useRef<AbortController | null>(null)\n const loadingRef = useRef(false)\n const nextPageRef = useRef(hasInitialItems ? 2 : 1)\n const failedPageRef = useRef<number | null>(null)\n const failureCountRef = useRef(0)\n const cooldownUntilRef = useRef(0)\n const autoRetryTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n const clearAutoRetryTimer = useCallback(() => {\n if (autoRetryTimerRef.current) {\n clearTimeout(autoRetryTimerRef.current)\n autoRetryTimerRef.current = null\n }\n }, [])\n\n const resetCooldown = useCallback(() => {\n failureCountRef.current = 0\n cooldownUntilRef.current = 0\n }, [])\n\n const setFailureCooldown = useCallback(() => {\n failureCountRef.current += 1\n const delay = getPagedResourceCooldownMs({\n failureCount: failureCountRef.current,\n retryDelayMs,\n maxRetryDelayMs,\n })\n cooldownUntilRef.current = Date.now() + delay\n return delay\n }, [maxRetryDelayMs, retryDelayMs])\n\n const startLoad = useCallback(async (page: number, mode: 'auto' | 'manual' | 'refresh') => {\n if (!enabled || loadingRef.current) return\n if (\n mode === 'auto' &&\n !canAutoLoadPagedResource({\n hasMore: true,\n isLoading: false,\n now: Date.now(),\n cooldownUntil: cooldownUntilRef.current,\n })\n ) {\n return\n }\n\n clearAutoRetryTimer()\n loadingRef.current = true\n const requestId = ++requestIdRef.current\n controllerRef.current?.abort()\n const controller = new AbortController()\n controllerRef.current = controller\n\n setState((prev) => {\n const hasItems = prev.items.length > 0\n return {\n ...prev,\n status: hasItems && mode !== 'refresh' ? 'ready' : 'loading',\n error: mode === 'auto' && prev.error ? prev.error : null,\n warning: null,\n isLoadingInitial: !hasItems && mode !== 'refresh',\n isLoadingMore: hasItems && mode !== 'refresh',\n isRefreshing: mode === 'refresh' && hasItems,\n items: mode === 'refresh' && !keepPreviousData ? [] : prev.items,\n }\n })\n\n try {\n const result = await fetchPage({ page, pageSize, signal: controller.signal })\n if (requestIdRef.current !== requestId || controller.signal.aborted) return\n const { items: pageItems, droppedItemCount } = clampPagedResourceItems({\n items: result.items,\n maxItemsPerPage,\n })\n resetCooldown()\n failedPageRef.current = null\n nextPageRef.current = page + 1\n setState((prev) => ({\n status: 'ready',\n items: page === 1 || mode === 'refresh'\n ? pageItems\n : [...prev.items, ...pageItems],\n error: null,\n warning: droppedItemCount > 0\n ? `The last API page returned ${result.items.length} items; only ${pageItems.length} were kept. Request a smaller page from the API instead of fetching the whole feed.`\n : null,\n hasMore: droppedItemCount > 0 || (result.hasMore ?? result.items.length >= pageSize),\n isLoadingInitial: false,\n isLoadingMore: false,\n isRefreshing: false,\n }))\n } catch (err: unknown) {\n if (requestIdRef.current !== requestId || controller.signal.aborted) return\n const message = err instanceof Error ? err.message : 'Request failed'\n failedPageRef.current = page\n const cooldownMs = setFailureCooldown()\n setState((prev) => {\n const hasItems = prev.items.length > 0\n return {\n ...prev,\n status: hasItems ? 'ready' : 'error',\n error: message,\n warning: prev.warning,\n hasMore: hasItems ? prev.hasMore : false,\n isLoadingInitial: false,\n isLoadingMore: false,\n isRefreshing: false,\n }\n })\n if (autoRetryOnError) {\n autoRetryTimerRef.current = setTimeout(() => {\n autoRetryTimerRef.current = null\n void startLoad(page, 'auto')\n }, cooldownMs)\n }\n } finally {\n if (requestIdRef.current === requestId) {\n loadingRef.current = false\n controllerRef.current = null\n }\n }\n }, [autoRetryOnError, clearAutoRetryTimer, enabled, fetchPage, keepPreviousData, maxItemsPerPage, pageSize, resetCooldown, setFailureCooldown])\n\n const loadMore = useCallback(() => {\n if (!state.hasMore || state.isLoadingInitial || state.isLoadingMore || state.isRefreshing) return\n void startLoad(nextPageRef.current, 'auto')\n }, [startLoad, state.hasMore, state.isLoadingInitial, state.isLoadingMore, state.isRefreshing])\n\n const retry = useCallback(() => {\n const page = failedPageRef.current ?? nextPageRef.current\n void startLoad(page, 'manual')\n }, [startLoad])\n\n const refresh = useCallback(() => {\n failedPageRef.current = null\n resetCooldown()\n nextPageRef.current = 1\n void startLoad(1, 'refresh')\n }, [resetCooldown, startLoad])\n\n useEffect(() => {\n requestIdRef.current += 1\n controllerRef.current?.abort()\n clearAutoRetryTimer()\n loadingRef.current = false\n failedPageRef.current = null\n resetCooldown()\n nextPageRef.current = hasInitialItems ? 2 : 1\n\n setState({\n status: enabled ? (hasInitialItems ? 'ready' : 'loading') : 'idle',\n items: initialItems,\n error: null,\n warning: null,\n hasMore: true,\n isLoadingInitial: enabled && !hasInitialItems,\n isLoadingMore: false,\n isRefreshing: false,\n })\n\n if (enabled && !hasInitialItems) {\n void startLoad(1, 'auto')\n }\n\n return () => {\n requestIdRef.current += 1\n controllerRef.current?.abort()\n clearAutoRetryTimer()\n loadingRef.current = false\n }\n // Deps are the caller-supplied ...deps plus the primitive options; the\n // startLoad closure and refs are deliberately excluded so this reset effect\n // only re-runs on real dependency changes, not on every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps -- caller-supplied ...deps is the reset contract; startLoad/refs/initialItems excluded so reset only fires on real dep changes\n }, [enabled, pageSize, maxItemsPerPage, keepPreviousData, autoRetryOnError, retryDelayMs, maxRetryDelayMs, ...deps])\n\n return useMemo(() => ({\n ...state,\n loadMore,\n retry,\n refresh,\n }), [loadMore, refresh, retry, state])\n}\n","export type LoadState = 'loading' | 'ready' | 'empty' | 'error' | 'signedOut'\n\nexport interface LoadStateAuthInput {\n isLoaded: boolean\n isSignedIn: boolean\n requireSignedIn?: boolean\n requireUser?: boolean\n userLoading?: boolean\n}\n\nexport interface LoadStateStatusInput {\n status: 'loading' | 'ready' | 'error' | 'idle' | 'refreshing'\n error?: unknown\n}\n\nexport interface DeriveLoadStateInput {\n auth?: LoadStateAuthInput\n queries?: LoadStateStatusInput[]\n resources?: LoadStateStatusInput[]\n empty?: boolean\n}\n\nexport interface AsyncResourceDefaults {\n retry: number\n retryDelayMs: number\n slowAfterMs: number\n}\n\nexport const ASYNC_RESOURCE_DEFAULTS: AsyncResourceDefaults = {\n retry: 0,\n retryDelayMs: 2_000,\n slowAfterMs: 10_000,\n}\n\nexport function isAsyncResourceSlow(input: {\n startedAt: number\n now: number\n slowAfterMs: number\n}): boolean {\n return input.slowAfterMs > 0 && input.now - input.startedAt >= input.slowAfterMs\n}\n\nexport interface PagedResourceCooldownInput {\n failureCount: number\n retryDelayMs: number\n maxRetryDelayMs: number\n}\n\nexport function getPagedResourceCooldownMs(input: PagedResourceCooldownInput): number {\n const failureCount = Math.max(1, input.failureCount)\n return Math.min(input.maxRetryDelayMs, input.retryDelayMs * 2 ** (failureCount - 1))\n}\n\nexport function canAutoLoadPagedResource(input: {\n hasMore: boolean\n isLoading: boolean\n now: number\n cooldownUntil: number\n}): boolean {\n return input.hasMore && !input.isLoading && input.cooldownUntil <= input.now\n}\n\nexport function clampPagedResourceItems<T>(input: {\n items: T[]\n maxItemsPerPage: number\n}): {\n items: T[]\n droppedItemCount: number\n} {\n if (input.maxItemsPerPage <= 0 || input.items.length <= input.maxItemsPerPage) {\n return { items: input.items, droppedItemCount: 0 }\n }\n return {\n items: input.items.slice(0, input.maxItemsPerPage),\n droppedItemCount: input.items.length - input.maxItemsPerPage,\n }\n}\n\nexport function deriveLoadState(input: DeriveLoadStateInput): LoadState {\n const auth = input.auth\n if (auth) {\n if (!auth.isLoaded) return 'loading'\n if (auth.requireSignedIn && !auth.isSignedIn) return 'signedOut'\n if (auth.requireUser && auth.isSignedIn && auth.userLoading) return 'loading'\n }\n\n const statuses = [...(input.queries ?? []), ...(input.resources ?? [])]\n if (statuses.some((item) => item.status === 'error' || item.error)) return 'error'\n if (statuses.some((item) => item.status === 'loading' || item.status === 'idle')) return 'loading'\n if (input.empty) return 'empty'\n return 'ready'\n}\n","// Per-app subscription state hook. Reads through the starter's same-origin\n// /_deepspace proxy, which signs with the app identity token.\n\nimport { useCallback, useEffect, useState } from 'react'\nimport { getAuthToken } from '../auth/token'\nimport { isEntitled, type PlanInfo, type SubscriptionStatus } from '../../shared/subscription'\n\n// Re-export for SDK consumers — these are public types the hook is generic over.\nexport type { SubscriptionStatus, PlanInfo }\n\nexport interface SubscriptionState {\n tier: string\n status: SubscriptionStatus\n /** Billing interval of the active subscription, or null for free / no sub. */\n interval: 'month' | 'year' | null\n currentPeriodEnd: number | null\n cancelAtPeriodEnd: boolean\n trialEndsAt: number | null\n plans: PlanInfo[]\n}\n\nexport interface SubscribeOpts {\n interval?: 'month' | 'year'\n returnUrl?: string\n cancelUrl?: string\n}\n\nexport interface SubscribeResult {\n url: string | null\n immediate: boolean\n requiresPayment?: boolean\n hostedInvoiceUrl?: string | null\n}\n\ninterface UseSubscriptionReturn {\n tier: string\n status: SubscriptionStatus\n /**\n * True iff status is `active` or `trialing`. `hasTier` and `isAtLeast`\n * already encode this — read `entitled` only when you need the raw boolean\n * without picking a tier.\n */\n entitled: boolean\n /** Current billing interval, or null when on free / no subscription. */\n interval: 'month' | 'year' | null\n currentPeriodEnd: number | null\n cancelAtPeriodEnd: boolean\n trialEndsAt: number | null\n isLoading: boolean\n error: string | null\n /**\n * Returns true only when `tier === slug` AND the subscription is currently\n * entitled (status ∈ {active, trialing}). A `past_due` / `canceled` /\n * `unpaid` subscription that still has a stored paid tier returns false.\n * The free tier is always entitled.\n */\n hasTier: (slug: string) => boolean\n /**\n * Returns true when the tier's rank meets-or-exceeds `slug` AND the\n * subscription is currently entitled. Same entitlement gate as `hasTier`.\n */\n isAtLeast: (slug: string) => boolean\n /** Plan catalog from /me — pass into `<PricingTable plans={...}>` directly. */\n plans: PlanInfo[]\n subscribe: (planSlug: string, opts?: SubscribeOpts) => Promise<SubscribeResult>\n openPortal: (returnUrl?: string) => Promise<{ url: string }>\n refresh: () => Promise<void>\n}\n\nasync function authedJson<T>(path: string, init?: RequestInit): Promise<T> {\n const token = await getAuthToken()\n const res = await fetch(path, {\n ...init,\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...init?.headers,\n },\n })\n const data = (await res.json().catch(() => ({}))) as Record<string, unknown>\n if (!res.ok) {\n throw new Error((data.error as string) ?? `Request failed (${res.status})`)\n }\n return data as T\n}\n\nexport function useSubscription(): UseSubscriptionReturn {\n const [state, setState] = useState<SubscriptionState>({\n tier: 'free',\n status: 'none',\n interval: null,\n currentPeriodEnd: null,\n cancelAtPeriodEnd: false,\n trialEndsAt: null,\n plans: [],\n })\n const [isLoading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n\n const refresh = useCallback(async () => {\n setLoading(true)\n try {\n const r = await authedJson<SubscriptionState>('/_deepspace/subscriptions/me')\n setState({\n tier: r.tier,\n status: r.status,\n interval: r.interval ?? null,\n currentPeriodEnd: r.currentPeriodEnd,\n cancelAtPeriodEnd: r.cancelAtPeriodEnd,\n trialEndsAt: r.trialEndsAt,\n plans: r.plans ?? [],\n })\n setError(null)\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to load subscription')\n } finally {\n setLoading(false)\n }\n }, [])\n\n useEffect(() => {\n refresh()\n }, [refresh])\n\n const entitled = isEntitled(state.tier, state.status)\n const hasTier = useCallback(\n (slug: string) => state.tier === slug && isEntitled(state.tier, state.status),\n [state.tier, state.status],\n )\n const isAtLeast = useCallback(\n (slug: string) => {\n if (!isEntitled(state.tier, state.status)) return false\n const targetRank = state.plans.find((p) => p.slug === slug)?.rank\n const currentRank = state.plans.find((p) => p.slug === state.tier)?.rank\n if (targetRank == null || currentRank == null) return false\n return currentRank >= targetRank\n },\n [state.plans, state.tier, state.status],\n )\n\n const subscribe = useCallback(\n async (planSlug: string, opts?: SubscribeOpts): Promise<SubscribeResult> => {\n const result = await authedJson<SubscribeResult>('/_deepspace/subscriptions/checkout', {\n method: 'POST',\n body: JSON.stringify({\n planSlug,\n interval: opts?.interval ?? 'month',\n returnUrl: opts?.returnUrl ?? window.location.href,\n cancelUrl: opts?.cancelUrl ?? window.location.href,\n requestNonce: crypto.randomUUID(),\n }),\n })\n if (result.immediate) await refresh()\n // Auto-redirect when the server tells us to navigate. `result.url` is\n // populated in every redirect-bound case: Stripe Checkout session URL\n // (new sub), returnUrl (immediate in-place tier/interval change), and\n // hostedInvoiceUrl (SCA-pending proration). Matches useCheckout's\n // chargeOnce behavior so docs that show `await sub.subscribe('pro')`\n // and expect a redirect actually work without extra plumbing.\n if (typeof window !== 'undefined' && result.url) {\n window.location.href = result.url\n }\n return result\n },\n [refresh],\n )\n\n const openPortal = useCallback(\n async (returnUrl?: string) => {\n return authedJson<{ url: string }>('/_deepspace/subscriptions/portal', {\n method: 'POST',\n body: JSON.stringify({ returnUrl: returnUrl ?? window.location.href }),\n })\n },\n [],\n )\n\n return {\n tier: state.tier,\n status: state.status,\n entitled,\n interval: state.interval,\n currentPeriodEnd: state.currentPeriodEnd,\n cancelAtPeriodEnd: state.cancelAtPeriodEnd,\n trialEndsAt: state.trialEndsAt,\n isLoading,\n error,\n hasTier,\n isAtLeast,\n plans: state.plans,\n subscribe,\n openPortal,\n refresh,\n }\n}\n","/**\n * Subscription primitives shared between client (useSubscription hook) and\n * server (requireSubscription helper). Pulled into a shared module so the\n * entitlement gate has a single source of truth — if it ever diverges between\n * client and server, gated features silently disagree about who's allowed in,\n * which is a security bug, not a UX bug.\n */\n\nexport type SubscriptionStatus =\n | 'none'\n | 'trialing'\n | 'active'\n | 'past_due'\n | 'canceled'\n | 'incomplete'\n | 'incomplete_expired'\n | 'unpaid'\n | 'paused'\n\n/** Per-interval price advertised by a plan. */\nexport interface PlanPrice {\n interval: 'month' | 'year'\n priceCents: number\n currency?: string\n}\n\n/**\n * Plan shape returned by `/api/subscriptions/me` and consumed directly by\n * `<PricingTable plans={sub.plans}>`. Fields beyond slug/rank are optional so\n * a free tier (no prices, no trial) doesn't need to fabricate them.\n */\nexport interface PlanInfo {\n slug: string\n rank: number\n name: string\n trialDays?: number | null\n prices: PlanPrice[]\n}\n\n// Statuses that grant access to paid features. Other statuses (past_due,\n// unpaid, incomplete, paused, canceled) keep the user's recorded planSlug so\n// the UI can surface \"your Pro plan is past-due\" — but they MUST NOT unlock.\n//\n// Typed as `ReadonlySet<string>` (not `Set<SubscriptionStatus>`) so callers\n// passing a server-supplied string don't need a runtime cast; unknown statuses\n// fall through as \"not entitled\", which is the safe default.\nexport const ENTITLED_STATUSES: ReadonlySet<string> = new Set([\n 'active',\n 'trialing',\n])\n\n/**\n * True iff the subscription currently grants access to paid features.\n *\n * - Free tier always counts as entitled — no payment required.\n * - Otherwise the status must be in `ENTITLED_STATUSES`.\n *\n * `status` is typed as `string` deliberately: server responses can carry\n * statuses we haven't enumerated yet (e.g. if Stripe adds a new one), and\n * those should fall through as not-entitled rather than crashing the check.\n */\nexport function isEntitled(tier: string, status: string): boolean {\n if (tier === 'free') return true\n return ENTITLED_STATUSES.has(status)\n}\n","// Minimal pre-fab pricing UI. Stateless: pass in `plans` from\n// `useSubscription().plans` and an `onSelect` callback that calls\n// `useSubscription().subscribe()`. Apps that want their own design can ignore\n// this and roll their own; this is just the \"works out of the box\" path for\n// starter apps.\n\nimport type { PlanInfo, PlanPrice } from '../../shared/subscription'\n\n// Re-export shared types so callers can do\n// `import { PricingTablePlan } from 'deepspace'` without reaching into shared.\nexport type PricingTablePrice = PlanPrice\nexport type PricingTablePlan = PlanInfo\n\nexport function PricingTable(props: {\n plans: PricingTablePlan[]\n interval?: 'month' | 'year'\n currentTier?: string\n onSelect: (planSlug: string, interval: 'month' | 'year') => void | Promise<void>\n}) {\n const interval = props.interval ?? 'month'\n return (\n <div className=\"grid gap-4 md:grid-cols-3\">\n {props.plans.map((plan) => {\n const price = plan.prices.find((p) => p.interval === interval)\n const selected = props.currentTier === plan.slug\n // A plan is truly free only if it has no priced interval at all.\n // If `price` is missing but the plan has prices on OTHER intervals,\n // this interval is unavailable — clicking checkout would fail with\n // `unknown_price`, so we render \"Unavailable\" and disable the button\n // instead of mislabeling it as free.\n const isTrueFree = plan.prices.length === 0\n const otherIntervals = plan.prices\n .map((p) => p.interval)\n .filter((i) => i !== interval)\n const intervalUnavailable = !price && !isTrueFree\n const onlyInterval = otherIntervals.length === 1 ? otherIntervals[0] : null\n const unavailableLabel = onlyInterval\n ? onlyInterval === 'month'\n ? 'Monthly only'\n : 'Yearly only'\n : 'Unavailable'\n const priceLabel = price\n ? `$${(price.priceCents / 100).toFixed(0)}`\n : isTrueFree\n ? 'Free'\n : unavailableLabel\n const disabled = selected || intervalUnavailable\n return (\n <section key={plan.slug} className=\"rounded-lg border border-border p-5\">\n <div className=\"space-y-1\">\n <h3 className=\"text-lg font-semibold\">{plan.name}</h3>\n <p className=\"text-3xl font-semibold\">\n {priceLabel}\n <span className=\"text-sm font-normal text-muted-foreground\">\n {price ? `/${interval}` : ''}\n </span>\n </p>\n {plan.trialDays ? (\n <p className=\"text-xs text-muted-foreground\">{plan.trialDays}-day free trial</p>\n ) : null}\n </div>\n <button\n type=\"button\"\n disabled={disabled}\n onClick={() => void props.onSelect(plan.slug, interval)}\n className=\"mt-5 w-full rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {selected\n ? 'Current plan'\n : intervalUnavailable\n ? unavailableLabel\n : 'Select plan'}\n </button>\n </section>\n )\n })}\n </div>\n )\n}\n","// One-time charge hook. Two modes:\n//\n// 1. Server-declared product (`chargeOnce({ productId })`): the platform\n// resolves amount/name from your declared product catalog. The browser\n// can't pick `productId` + `amount` together — only the declared\n// amount is charged. This is the only entitlement-safe mode:\n// `useCheckout({productId}).owned` answers truthfully.\n//\n// 2. Ad-hoc (`chargeOnce({ amount, name })`): tips, donations, dynamic\n// amounts. The resulting invoice has no `productId` so `ownsProduct()`\n// cannot satisfy gating against it. Use for collection, not unlock.\n//\n// To declare products, POST to `/api/charges/products/sync` from a deploy\n// step or admin tool with `{appName, products: [{productId, name,\n// amountCents}]}`. Products dropped from a later sync are deactivated.\n//\n// The hook fetches the customer's purchase history on mount, caches it in\n// state, and exposes `owned` + `ownsProduct(id)` for gating features behind\n// a one-time purchase the same way `useSubscription().tier` gates behind a\n// recurring plan.\n\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport { getAuthToken } from '../auth/token'\n\ninterface ChargeRedirectOpts {\n /** Defaults to window.location.href. */\n returnUrl?: string\n /** Defaults to window.location.href. */\n cancelUrl?: string\n}\n\n/**\n * Product-mode charge: amount, name, and currency are resolved server-side\n * from the developer's declared product catalog (`src/products.ts`). The\n * browser cannot influence the price — this is the only entitlement-safe\n * shape because `useCheckout({productId}).owned` can be trusted afterwards.\n */\nexport interface ProductChargeOpts extends ChargeRedirectOpts {\n productId: string\n}\n\n/**\n * Ad-hoc charge: tips, donations, \"name your price\". Caller supplies the\n * amount and display name; the resulting invoice has `productId=null`, so\n * `ownsProduct()` will never report ownership against it. Use for revenue\n * collection, not for feature gating.\n */\nexport interface AdHocChargeOpts extends ChargeRedirectOpts {\n /** Amount in cents. Minimum 100 (Stripe fees eat smaller amounts). */\n amount: number\n /** Product name shown in Checkout and on the customer's receipt. */\n name: string\n /** Optional description rendered under the product name. */\n description?: string\n /** Must be omitted in ad-hoc mode — the never type makes the union disjoint. */\n productId?: never\n}\n\n/**\n * `chargeOnce` accepts either a `productId` (server-resolves price/name) or\n * an `amount`+`name` pair (ad-hoc). The two are mutually exclusive at the\n * type level so the safe path doesn't require dummy amount/name fields.\n */\nexport type ChargeOnceOpts = ProductChargeOpts | AdHocChargeOpts\n\nexport interface ChargeOnceResult {\n url: string\n}\n\nexport interface Purchase {\n /** Internal invoice row id. Stable across refetches. */\n id: string\n /** Developer-supplied product id, or null for pre-entitlement rows. */\n productId: string | null\n /** Display name captured at checkout time. */\n name: string\n /** Amount paid in cents (post-tax — uses Stripe's `amount_total`). */\n amount: number\n currency: string\n /** ISO timestamp of the payment. */\n paidAt: string\n}\n\nexport interface UseCheckoutOpts {\n /**\n * Product id to derive `owned` against. Without this, `owned` is always\n * false — the hook still surfaces `purchases` and `ownsProduct` for\n * callers that need to ask about more than one product.\n */\n productId?: string\n}\n\ninterface UseCheckoutReturn {\n // Action surface\n chargeOnce: (opts: ChargeOnceOpts) => Promise<ChargeOnceResult>\n isLoading: boolean\n error: string | null\n\n // Read surface\n purchases: Purchase[]\n /** True iff `opts.productId` was provided and a non-fully-refunded row matches. */\n owned: boolean\n /** Pure function over the in-memory `purchases` array. No network. */\n ownsProduct: (productId: string) => boolean\n /** Re-fetch the purchase list. Useful right after a successful checkout. */\n refresh: () => Promise<void>\n}\n\nasync function authedJson<T>(path: string, init?: RequestInit): Promise<T> {\n const token = await getAuthToken()\n const res = await fetch(path, {\n ...init,\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...init?.headers,\n },\n })\n const data = (await res.json().catch(() => ({}))) as Record<string, unknown>\n if (!res.ok) {\n throw new Error((data.error as string) ?? `Request failed (${res.status})`)\n }\n return data as T\n}\n\nexport function useCheckout(opts?: UseCheckoutOpts): UseCheckoutReturn {\n const [purchases, setPurchases] = useState<Purchase[]>([])\n const [isLoading, setLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n\n // No productId param on the fetch — the hook caches the full list and the\n // per-product question is answered in-memory. Cheaper than refetching on\n // every productId change and lets one component check ownership of N\n // products without N requests.\n const refresh = useCallback(async () => {\n setLoading(true)\n try {\n const r = await authedJson<{ purchases: Purchase[] }>(\n '/_deepspace/charges/me',\n )\n setPurchases(r.purchases ?? [])\n setError(null)\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to load purchases')\n } finally {\n setLoading(false)\n }\n }, [])\n\n useEffect(() => {\n refresh()\n }, [refresh])\n\n const chargeOnce = useCallback(\n async (chargeOpts: ChargeOnceOpts): Promise<ChargeOnceResult> => {\n // Note: isLoading intentionally not toggled here — it tracks the\n // fetch lifecycle (initial + refresh), not the chargeOnce redirect.\n // Mirrors the v1 semantics callers already depend on.\n setError(null)\n\n // Build the request body by mode. In product mode we deliberately\n // omit amount/name from the wire — the server resolves them from the\n // declared catalog and would ignore client values anyway, but sending\n // them keeps the dangerous \"browser picks the price\" shape on the\n // wire. In ad-hoc mode we send what the caller supplied.\n const common = {\n returnUrl: chargeOpts.returnUrl ?? window.location.href,\n cancelUrl: chargeOpts.cancelUrl ?? window.location.href,\n requestNonce: crypto.randomUUID(),\n }\n const body =\n chargeOpts.productId !== undefined\n ? { ...common, productId: chargeOpts.productId }\n : {\n ...common,\n amount: chargeOpts.amount,\n name: chargeOpts.name,\n ...(chargeOpts.description ? { description: chargeOpts.description } : {}),\n }\n\n try {\n const result = await authedJson<{ url: string }>(\n '/_deepspace/charges/create',\n {\n method: 'POST',\n body: JSON.stringify(body),\n },\n )\n if (typeof window !== 'undefined' && result.url) {\n window.location.href = result.url\n }\n return result\n } catch (e) {\n const msg = e instanceof Error ? e.message : 'Failed to start checkout'\n setError(msg)\n throw e\n }\n },\n [],\n )\n\n const ownsProduct = useCallback(\n (productId: string) => purchases.some((p) => p.productId === productId),\n [purchases],\n )\n\n // `owned` is the convenience binding for the single-product case. Vacuously\n // false when no productId was passed to the hook — the caller didn't ask a\n // question, so the answer is \"no entitlement\".\n const owned = useMemo(\n () => !!opts?.productId && purchases.some((p) => p.productId === opts.productId),\n [opts?.productId, purchases],\n )\n\n return {\n chargeOnce,\n isLoading,\n error,\n purchases,\n owned,\n ownsProduct,\n refresh,\n }\n}\n"],"mappings":"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","names":["Fragment","jsx","jsxs","useEffect","useRef","Fragment","jsx","useRef","useEffect","useEffect","useState","Fragment","jsx","jsxs","useState","useEffect","createContext","useContext","useEffect","useState","useCallback","useRef","useMemo","useRef","jsx","Fragment","jsx","jsxs","createContext","useContext","useState","useRef","useCallback","useEffect","useMemo","jsxs","jsx","useCallback","Fragment","useEffect","useState","useCallback","useRef","useMemo","Fragment","jsx","jsxs","useRef","useState","useEffect","useCallback","useMemo","useMemo","useMemo","useEffect","useCallback","useRef","useMemo","useContext","useContext","useMemo","useRef","useEffect","useCallback","useCallback","useContext","useMemo","useContext","useCallback","useMemo","useEffect","useMemo","useEffect","useMemo","useState","useEffect","useCallback","useRef","Y","useState","useRef","useEffect","msgDocKey","payload","useCallback","useMemo","useCallback","useMemo","useCallback","useEffect","useCallback","useState","useState","useEffect","useCallback","u","useState","useEffect","useCallback","useRef","useMemo","Y","useState","useRef","useMemo","useEffect","useCallback","useState","useEffect","useCallback","useRef","useState","useRef","useEffect","useCallback","useState","useEffect","useCallback","useRef","useState","useRef","useEffect","useCallback","useState","useEffect","useCallback","useRef","useState","useRef","useEffect","useCallback","useState","useEffect","useCallback","useRef","useState","useRef","useEffect","useCallback","useState","useEffect","useCallback","useRef","useState","useRef","useEffect","useCallback","useCallback","useState","useMemo","useState","useMemo","useCallback","useCallback","useMemo","useMemo","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useMemo","useCallback","useMemo","useCallback","useMemo","useRef","useRef","useMemo","useCallback","useCallback","useMemo","useMemo","useCallback","useCallback","useCallback","useEffect","r","g","b","Fragment","jsx","useEffect","useState","useEffect","createContext","useContext","useState","useEffect","useRef","useCallback","jsx","createContext","useContext","useEffect","useState","useRef","useCallback","useState","useEffect","useRef","useRef","useState","useEffect","DEFAULT_TIMEOUT_MS","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect","useCallback","useEffect","useMemo","useRef","useState","useState","useRef","useCallback","useEffect","useMemo","useCallback","useEffect","useState","useState","useCallback","useEffect","jsx","jsxs","useCallback","useEffect","useMemo","useState","authedJson","useState","useCallback","useEffect","useMemo"]}
|