deepspace 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +19 -8
  3. package/dist/{chunk-SF6GNPYL.js → chunk-652TIGI4.js} +2 -2
  4. package/dist/{chunk-SF6GNPYL.js.map → chunk-652TIGI4.js.map} +1 -1
  5. package/dist/cli.js +1819 -1068
  6. package/dist/cli.js.map +1 -1
  7. package/dist/documentation-client-core.js +1 -1
  8. package/dist/documentation-default-renderer.cjs +1 -1
  9. package/dist/documentation-react.js +1 -1
  10. package/dist/documentation-runtime.js +1 -1
  11. package/dist/documentation-server-core.js +1 -1
  12. package/dist/documentation.d.ts +0 -1
  13. package/dist/documentation.js +41 -21
  14. package/dist/documentation.js.map +1 -1
  15. package/dist/feature-installer.js +27 -61
  16. package/dist/feature-installer.js.map +1 -1
  17. package/dist/index.d.ts +6 -55
  18. package/dist/index.js +4 -20
  19. package/dist/index.js.map +1 -1
  20. package/dist/schema.js.map +1 -1
  21. package/dist/server.d.ts +8 -39
  22. package/dist/server.js +16 -64
  23. package/dist/server.js.map +1 -1
  24. package/dist/testing.js +2 -2
  25. package/dist/testing.js.map +1 -1
  26. package/dist/worker.d.ts +7 -80
  27. package/dist/worker.js +17 -69
  28. package/dist/worker.js.map +1 -1
  29. package/features/admin-page/feature.json +0 -1
  30. package/features/ai-chat/feature.json +0 -1
  31. package/features/canvas/feature.json +0 -1
  32. package/features/cron/feature.json +0 -1
  33. package/features/documents/feature.json +0 -1
  34. package/features/file-attachments/feature.json +0 -1
  35. package/features/file-manager/feature.json +0 -1
  36. package/features/integration-test/feature.json +0 -1
  37. package/features/items/feature.json +0 -1
  38. package/features/kanban/feature.json +0 -1
  39. package/features/landing/feature.json +0 -1
  40. package/features/leaderboard/feature.json +0 -1
  41. package/features/messaging/feature.json +0 -1
  42. package/features/search-bar/feature.json +0 -1
  43. package/features/tasks/feature.json +0 -1
  44. package/features/testing/feature.json +0 -1
  45. package/features/topbar/feature.json +1 -4
  46. package/features/tree/feature.json +1 -4
  47. package/package.json +1 -1
  48. package/dist/chunk-Q5YJGF5Z.js +0 -769
  49. package/dist/chunk-Q5YJGF5Z.js.map +0 -1
  50. package/dist/repo-api-7GDPVSFV.js +0 -14
  51. package/dist/repo-api-7GDPVSFV.js.map +0 -1
  52. package/features/presence-test/feature.json +0 -22
  53. package/features/presence-test/src/PresenceTestPage.tsx +0 -120
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/env/index.ts","../src/shared/roles.ts","../src/shared/ai-models.ts","../src/shared/protocol/yjs.ts","../src/shared/protocol/constants.ts","../src/shared/protocol/messages.ts","../src/shared/protocol/app-migrations.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/ScopeRegistry.tsx","../src/client/storage/RecordScope.tsx","../src/client/storage/store.ts","../src/client/storage/record-matching.ts","../src/client/storage/errors.ts","../src/client/storage/serverErrors.ts","../src/client/storage/record-socket.ts","../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/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/shared/app-files.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/theme/DeepSpaceThemeProvider.tsx","../src/client/theme/applyTheme.ts","../src/client/theme/useIsDarkTheme.ts","../src/shared/api-error.ts","../src/client/integration.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/client/authed-json.ts","../src/shared/subscription.ts","../src/client/subscriptions/PricingTable.tsx","../src/client/charges/useCheckout.ts","../src/shared/client-errors.ts","../src/client/errors.ts","../src/shared/log-events.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' | 'staging' | '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\n// Mirrors the CLI's staging preset (cli/env.ts) and the staging wrangler\n// routes: services and the dashboard on deepspacesites.com, user apps on\n// spacestest.com. This module had NO staging tier — a staging app calling\n// getApiUrl() silently targeted production.\nconst STAGING_CONFIG: EnvironmentConfig = {\n name: 'staging',\n apiUrl: 'https://api.deepspacesites.com',\n platformWorkerUrl: 'https://platform.deepspacesites.com',\n authUrl: 'https://auth.deepspacesites.com',\n authSignInUrl: 'https://auth.deepspacesites.com/login/social',\n authSignUpUrl: 'https://auth.deepspacesites.com/login/social',\n mainAppUrl: 'https://spacestest.com',\n dashboardUrl: 'https://dashboard.deepspacesites.com',\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 === 'staging') return 'staging'\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 // Staging: apps on *.spacestest.com, services on *.deepspacesites.com.\n if (\n hostname === 'spacestest.com' ||\n hostname.endsWith('.spacestest.com') ||\n hostname === 'deepspacesites.com' ||\n hostname.endsWith('.deepspacesites.com')\n ) {\n cachedEnvironment = 'staging'\n return 'staging'\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 : env === 'staging' ? STAGING_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\n/**\n * Whether a role may write app content: member and admin can, viewer is\n * read-only. The one chokepoint for every client-side write gate — an app\n * that defines custom write-capable roles extends this predicate once. The\n * server always re-checks writes against the schema's permission table; this\n * only controls what the UI offers.\n */\nexport function isWriterRole(role: unknown): role is Exclude<Role, typeof ROLES.VIEWER> {\n return role === ROLES.ADMIN || role === ROLES.MEMBER\n}\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 * Canonical DeepSpace model catalog and agent policy.\n *\n * Provider catalogs are discovery inputs, not a safe runtime allowlist: a\n * model is promoted here only after its tool loop, transport, pricing, and\n * streaming contract have been verified end to end through the DeepSpace\n * proxy. Every client picker and server agent profile consumes this module.\n */\n\nexport type DeepSpaceAIProvider = 'anthropic' | 'openai' | 'cerebras'\nexport type DeepSpaceAgentProfileId = 'application' | 'documentation'\nexport type DeepSpaceAgentSupport = 'multi-step' | 'single-step' | 'none'\n\nexport interface DeepSpaceAIModel {\n id: string\n label: string\n provider: DeepSpaceAIProvider\n providerLabel: string\n family: string\n /** Whether the model is eligible for DeepSpace's server-owned agent loop. */\n agentSupport: DeepSpaceAgentSupport\n /** Profiles in which the model has passed the complete runtime contract. */\n agentProfiles: readonly DeepSpaceAgentProfileId[]\n /** Provider transport currently used by the DeepSpace proxy adapter. */\n transport: 'messages' | 'chat-completions'\n recommendation: 'frontier' | 'balanced' | 'fast' | 'available' | 'limited'\n note?: string\n}\n\nexport interface DeepSpaceAgentProfile {\n id: DeepSpaceAgentProfileId\n defaultModel: string\n maxSteps: number\n maxToolCalls?: number\n allowedTools: 'application-defined' | readonly string[]\n}\n\n/**\n * Versioned provenance for the provider catalogs used during the latest\n * promotion review. Keeping this in the shipped artifact makes selection\n * policy auditable without allowing a mutable upstream list to change a\n * deployed app underneath a commit.\n */\nexport const DEEPSPACE_MODEL_CATALOG_PROVENANCE = {\n version: '2026-08-04',\n verifiedAt: '2026-08-04',\n sources: {\n anthropic: 'https://platform.claude.com/docs/en/api/beta/models/list',\n openai: 'https://developers.openai.com/api/docs/models/all',\n cerebras: 'https://inference-docs.cerebras.ai/models/overview',\n },\n} as const\n\nconst MULTI_STEP_AGENT_PROFILES = ['application', 'documentation'] as const\n\ntype MultiStepModelInput = Pick<DeepSpaceAIModel, 'id' | 'label' | 'family' | 'recommendation'>\ntype MultiStepProvider = Pick<DeepSpaceAIModel, 'provider' | 'providerLabel' | 'transport'>\n\nfunction createMultiStepModelFactory<const Provider extends MultiStepProvider>(provider: Provider) {\n return <const Model extends MultiStepModelInput>(model: Model) => ({\n ...model,\n ...provider,\n agentSupport: 'multi-step' as const,\n agentProfiles: MULTI_STEP_AGENT_PROFILES,\n })\n}\n\nconst anthropicModel = createMultiStepModelFactory({\n provider: 'anthropic',\n providerLabel: 'Anthropic',\n transport: 'messages',\n})\n\nconst openAIModel = createMultiStepModelFactory({\n provider: 'openai',\n providerLabel: 'OpenAI',\n transport: 'chat-completions',\n})\n\nexport const DEEPSPACE_AI_MODELS = [\n anthropicModel({\n id: 'claude-fable-5',\n label: 'Claude Fable 5',\n family: 'Claude 5',\n recommendation: 'frontier',\n }),\n anthropicModel({\n id: 'claude-opus-5',\n label: 'Claude Opus 5',\n family: 'Claude 5',\n recommendation: 'frontier',\n }),\n anthropicModel({\n id: 'claude-sonnet-5',\n label: 'Claude Sonnet 5',\n family: 'Claude 5',\n recommendation: 'balanced',\n }),\n anthropicModel({\n id: 'claude-haiku-4-5',\n label: 'Claude Haiku 4.5',\n family: 'Claude 4.5',\n recommendation: 'fast',\n }),\n openAIModel({\n id: 'gpt-5.6-sol',\n label: 'GPT-5.6 Sol',\n family: 'GPT-5.6',\n recommendation: 'frontier',\n }),\n openAIModel({\n id: 'gpt-5.6-terra',\n label: 'GPT-5.6 Terra',\n family: 'GPT-5.6',\n recommendation: 'balanced',\n }),\n openAIModel({\n id: 'gpt-5.6-luna',\n label: 'GPT-5.6 Luna',\n family: 'GPT-5.6',\n recommendation: 'fast',\n }),\n {\n id: 'gpt-oss-120b',\n label: 'GPT-OSS 120B',\n provider: 'cerebras',\n providerLabel: 'Cerebras',\n family: 'GPT-OSS',\n agentSupport: 'single-step',\n agentProfiles: [],\n transport: 'chat-completions',\n recommendation: 'limited',\n note: 'Available for direct generation; the current proxy adapter has not passed multi-step tool-result continuation.',\n },\n] as const satisfies readonly DeepSpaceAIModel[]\n\nexport const DEEPSPACE_AI_DEFAULTS = {\n agent: 'claude-sonnet-5',\n directGeneration: 'claude-sonnet-5',\n summarization: 'claude-haiku-4-5',\n} as const satisfies Record<string, DeepSpaceAIModelId>\n\nexport const DEEPSPACE_AGENT_PROFILES = {\n application: {\n id: 'application',\n defaultModel: DEEPSPACE_AI_DEFAULTS.agent,\n maxSteps: 21,\n maxToolCalls: 20,\n allowedTools: 'application-defined',\n },\n documentation: {\n id: 'documentation',\n defaultModel: DEEPSPACE_AI_DEFAULTS.agent,\n // Permit up to twenty completed read-only tools plus one final synthesis\n // step. The runner disables tools once the tool-call budget is exhausted.\n maxSteps: 21,\n maxToolCalls: 20,\n allowedTools: ['documentation_search', 'documentation_read'],\n },\n} as const satisfies Record<DeepSpaceAgentProfileId, DeepSpaceAgentProfile>\n\nexport type DeepSpaceAIModelId = (typeof DEEPSPACE_AI_MODELS)[number]['id']\n\nexport function getDeepSpaceAIModel(modelId: unknown): DeepSpaceAIModel | null {\n if (typeof modelId !== 'string') return null\n return DEEPSPACE_AI_MODELS.find((model) => model.id === modelId) ?? null\n}\n\nexport function listDeepSpaceAgentModels(\n profileId: DeepSpaceAgentProfileId = 'application',\n): readonly DeepSpaceAIModel[] {\n const defaultModel = DEEPSPACE_AGENT_PROFILES[profileId].defaultModel\n return DEEPSPACE_AI_MODELS.filter((model) =>\n (model.agentProfiles as readonly DeepSpaceAgentProfileId[]).includes(profileId),\n ).sort((left, right) => Number(right.id === defaultModel) - Number(left.id === defaultModel))\n}\n\nexport interface ResolvedDeepSpaceAgentModel {\n modelId: string\n provider: DeepSpaceAIProvider\n model: DeepSpaceAIModel\n profile: DeepSpaceAgentProfile\n}\n\nexport function resolveDeepSpaceAgentModel(\n modelId: unknown,\n profileId: DeepSpaceAgentProfileId = 'application',\n): ResolvedDeepSpaceAgentModel | null {\n const profile = DEEPSPACE_AGENT_PROFILES[profileId]\n const resolvedId = modelId === undefined ? profile.defaultModel : modelId\n const model = getDeepSpaceAIModel(resolvedId)\n if (!model || !model.agentProfiles.includes(profileId)) return null\n return { modelId: model.id, provider: model.provider, model, profile }\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 /** Release all state and listeners owned by this awareness instance. */\n destroy(): void {\n this.setLocalState(null)\n this.states.clear()\n this.meta.clear()\n this.updateListeners.clear()\n this.changeListeners.clear()\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 * Wire protocol constants.\n *\n * The JSON WebSocket protocol uses dotted string identifiers (e.g.\n * `\"records.query\"`) 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 * })\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\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 // Per-mutation receipt, sent only to the socket whose trigger/pause/\n // resume carried a `requestId` — frames without one stay fire-and-forget.\n CRON_ACK: 'cron.ack',\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/** Prefix `base-room` mints anonymous socket ids with. */\nexport const ANONYMOUS_USER_ID_PREFIX = 'anon-'\n/** The one test for an anonymous socket identity — `ROLE_ANONYMOUS` cannot be\n * it, since authenticated users can legitimately hold the same role name. */\nexport function isAnonymousUserId(userId: string): boolean {\n return userId.startsWith(ANONYMOUS_USER_ID_PREFIX)\n}\n/** Default role for newly registered authenticated users */\nexport const ROLE_DEFAULT = 'member'\n/** Admin role */\nexport const ROLE_ADMIN = 'admin'\n\n// ---------------------------------------------------------------------------\n// Error strings that cross a room boundary and are matched by the caller\n// ---------------------------------------------------------------------------\n\n/**\n * Reported when a record id resolves to nothing.\n *\n * This crosses the DO boundary as a response body and callers branch on it,\n * so it is a contract rather than prose. `getChat` in particular treats it as\n * \"no such chat\" and returns null — the same answer it gives for a chat owned\n * by someone else, which is what stops chat ids being enumerable. If a room\n * reworded this independently, a miss would start throwing while a cross-user\n * hit kept returning null, and that difference is an existence oracle.\n */\nexport const RECORD_NOT_FOUND = 'Record not found'\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.RECORDS_RESUBSCRIBE`, for example, is `{}` when the client asks to\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 // ---- 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 // Optional `requestId` opts the mutation into a `CRON_ACK` receipt;\n // frames without one keep the fire-and-forget contract.\n | BaseMessage<typeof MSG.CRON_TRIGGER, { taskName: string; requestId?: string }>\n | BaseMessage<typeof MSG.CRON_PAUSE, { taskName: string; requestId?: string }>\n | BaseMessage<typeof MSG.CRON_RESUME, { taskName: string; requestId?: 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 /\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 // ---- 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 // `CRON_ACK` is the per-mutation receipt for a trigger/pause/resume that\n // carried a `requestId`. Triggers execute synchronously in the DO before\n // the ack is sent, so an ok trigger receipt means the run completed\n // (execution detail rides the history broadcasts). Failures carry a\n // machine-readable `reason` so clients never parse error strings —\n // `read_only` covers the role-downgrade race the client-side gate\n // cannot see.\n | BaseMessage<\n typeof MSG.CRON_ACK,\n | { requestId: string; taskName: string; ok: true }\n | {\n requestId: string\n taskName?: string\n ok: false\n reason: 'read_only' | 'unknown_task' | 'failed'\n error?: string\n }\n >\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 // 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, requestId?: string) =>\n ({ type: MSG.CRON_TRIGGER, payload: { taskName, requestId } }) as const,\n cronPause: (taskName: string, requestId?: string) =>\n ({ type: MSG.CRON_PAUSE, payload: { taskName, requestId } }) as const,\n cronResume: (taskName: string, requestId?: string) =>\n ({ type: MSG.CRON_RESUME, payload: { taskName, requestId } }) 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 // 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 cronAckSuccess: (requestId: string, taskName: string) =>\n ({\n type: MSG.CRON_ACK,\n payload: { requestId, taskName, ok: true as const },\n }) as const,\n cronAckFailure: (\n requestId: string,\n reason: 'read_only' | 'unknown_task' | 'failed',\n error?: string,\n taskName?: string,\n ) =>\n ({\n type: MSG.CRON_ACK,\n payload: { requestId, taskName, ok: false as const, reason, error },\n }) 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// })\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","const APP_MIGRATION_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/\nconst MAX_APP_MIGRATION_IDS = 256\n\n/** Asset-layer misses became real 404s, and the worker owns the fallback. The\n * config and the worker's fallback path have to move together. */\nexport const WORKER_OWNED_NOT_FOUND_MIGRATION_ID = '2026-08-worker-owned-not-found'\n\n/** Room identity moved to internal headers and privileged room routes gained app-role checks. */\nexport const SECURE_ROOM_BOUNDARIES_MIGRATION_ID = '2026-08-secure-room-boundaries'\n\n/** `ActionTools` gained `deleteWhere`, so the app's own tools factory — which\n * declares that interface as its return type — has to supply it. */\nexport const ACTION_TOOLS_DELETE_WHERE_MIGRATION_ID = '2026-08-action-tools-delete-where'\n\n/** The browser's app id moved off a scaffold-time literal onto a build-time\n * define that `deepspace/build` reads from the wrangler config being built. */\nexport const BUILD_INJECTED_APP_ID_MIGRATION_ID = '2026-08-build-injected-app-id'\n\n/** The action route refuses a call without a bearer token as 401 (a cookie\n * session has none) instead of crashing on a non-null assertion, and the\n * file states the trust model actions run under. */\nexport const ACTION_ROUTES_BEARER_GUARD_MIGRATION_ID = '2026-08-action-routes-bearer-guard'\n\nexport type AppMigrationIdsValidation =\n | { valid: true; ids: string[] }\n | { valid: false; reason: string }\n\n/** Validate the small, provider-neutral migration manifest shipped with an app bundle. */\nexport function validateAppMigrationIds(value: unknown): AppMigrationIdsValidation {\n if (!Array.isArray(value)) {\n return { valid: false, reason: 'app migrations must be a JSON string array' }\n }\n if (value.length > MAX_APP_MIGRATION_IDS) {\n return {\n valid: false,\n reason: `app migrations cannot contain more than ${MAX_APP_MIGRATION_IDS} entries`,\n }\n }\n\n const ids: string[] = []\n const seen = new Set<string>()\n for (const valueId of value) {\n if (typeof valueId !== 'string' || !APP_MIGRATION_ID_RE.test(valueId)) {\n return { valid: false, reason: `invalid app migration id: ${String(valueId)}` }\n }\n if (seen.has(valueId)) {\n return { valid: false, reason: `duplicate app migration id: ${valueId}` }\n }\n seen.add(valueId)\n ids.push(valueId)\n }\n return { valid: true, ids }\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 start` 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 * Hosts can brand the card via `title` / `description` / `logo` and re-tint\n * the backdrop by defining `--ds-auth-backdrop` (defaults to black/55).\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 /** Card heading. Defaults to \"Sign in to DeepSpace\". */\n title?: string\n /** Line under the heading. Defaults to \"Sync your data across devices\". */\n description?: string\n /** Optional brand mark rendered above the heading. Defaults to none (the current look). */\n logo?: React.ReactNode\n}\n\nexport function AuthOverlay({\n onClose,\n providers = ['github', 'google'],\n title = 'Sign in to DeepSpace',\n description = 'Sync your data across devices',\n logo,\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-[var(--ds-auth-backdrop,#0000008c)] 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 {logo && <div className=\"mb-4 flex justify-center\">{logo}</div>}\n\n <h2 className=\"mb-2 text-[22px] font-bold leading-snug text-foreground\">\n {title}\n </h2>\n\n <p className=\"mb-6 text-sm leading-snug text-muted-foreground\">\n {description}\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 shared authentication state and scope registration for RecordRoom.\n * RecordScope components own 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 { isLocalDevHost } from '../debug'\nimport { RecordStore } from './store'\nimport { ScopeRegistryProvider } from './ScopeRegistry'\nimport type {\n UserProfile,\n RoomUser,\n RoomConnectionState,\n RecordProviderProps,\n WriteError,\n} from './types'\n\ntype FetchUserProfile = () => Promise<UserProfile | null>\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 RecordScope')\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 /**\n * The one write-failure surface. RecordScope adapts socket rejections onto\n * it (permission/validation) and useMutations dispatches writes it refuses\n * before the room is ready (not_ready), so every rejected write reaches the\n * app's single `onWriteError` prop.\n */\n onWriteError?: (error: WriteError) => 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// Shared provider state\n// ============================================================================\n\ninterface RecordProviderStateProps {\n children: ReactNode\n fetchUser: FetchUserProfile\n allowAnonymous?: boolean\n getAuthToken?: () => Promise<string | null>\n onWriteError: (error: WriteError) => void\n}\n\nfunction RecordProviderState({\n children,\n fetchUser,\n allowAnonymous = false,\n getAuthToken: getAuthTokenProp,\n onWriteError: onWriteErrorProp,\n}: RecordProviderStateProps): React.ReactElement {\n const [userProfile, setUserProfile] = useState<UserProfile | null>(null)\n const [userProfileLoading, setUserProfileLoading] = useState(true)\n\n // Hold the latest app callback in a ref and expose a stable wrapper, so an\n // inline arrow prop doesn't churn authValue (and every RecordScope under\n // it) on each render.\n const onWriteErrorRef = useRef(onWriteErrorProp)\n onWriteErrorRef.current = onWriteErrorProp\n\n const onWriteError = useCallback((error: WriteError) => {\n onWriteErrorRef.current(error)\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 onWriteError,\n }),\n [\n userProfile,\n userProfileLoading,\n refetchUserProfile,\n allowAnonymous,\n getAuthTokenProp,\n onWriteError,\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 * Default write-error handler. Fire-and-forget 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 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 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 supplies shared authentication and scope registration.\n * Use `<RecordScope>` for every RecordRoom connection.\n *\n * @example\n * ```tsx\n * <RecordProvider>\n * <RecordScope roomId=\"app:slack-clone\" schemas={appSchemas}>\n * <RecordScope roomId={`chat:${channelId}`} schemas={messagingSchemas}>\n * <ChannelView />\n * </RecordScope>\n * </RecordScope>\n * </RecordProvider>\n * ```\n */\nexport function RecordProvider({\n children,\n allowAnonymous = false,\n getAuthToken: getAuthTokenProp,\n onWriteError = defaultOnWriteError,\n}: RecordProviderProps): 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 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 // The public callback travels down unchanged; every producer (socket\n // rejections, not-ready mutations) shapes its own WriteError. Passing the\n // raw prop is safe: RecordProviderState holds it in a ref behind a stable\n // wrapper, so its identity never reaches a dependency array.\n return (\n <RecordProviderState\n fetchUser={fetchUser}\n allowAnonymous={allowAnonymous}\n getAuthToken={getAuthTokenFn}\n onWriteError={onWriteError}\n >\n {children}\n </RecordProviderState>\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 * sharedScopes={[\n * { roomId: 'chat:general', schemas: messagingSchemas },\n * ]}\n * >\n * <App />\n * </RecordScope>\n * </RecordProvider>\n * ```\n */\n\nimport { 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 { RecordRoomNotReadyError } from './errors'\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// 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 /** 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 wsUrl,\n wsPathPrefix,\n}: {\n roomId: string\n schemas: CollectionSchema[]\n wsUrl?: string\n wsPathPrefix?: string\n}) {\n return (\n <ScopeConnection roomId={roomId} schemas={schemas} wsUrl={wsUrl} wsPathPrefix={wsPathPrefix} />\n )\n}\n\n// ============================================================================\n// Core WebSocket connection logic (shared by primary + headless scopes)\n// ============================================================================\n\nfunction ScopeConnection({\n roomId,\n schemas,\n children,\n wsUrl,\n wsPathPrefix = '/ws',\n isolated = false,\n}: {\n roomId: string\n schemas: CollectionSchema[]\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 onWriteErrorRef = useRef(auth?.onWriteError)\n onWriteErrorRef.current = auth?.onWriteError\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, 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 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 onWriteErrorRef.current?.({ kind: 'permission', title, detail }),\n onValidationError: (title, detail) =>\n onWriteErrorRef.current?.({ kind: 'validation', 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, 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 RecordRoomNotReadyError())\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 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 wsUrl={wsUrl}\n wsPathPrefix={wsPathPrefix}\n />\n ))}\n <ScopeConnection\n roomId={roomId}\n schemas={schemas}\n wsUrl={wsUrl}\n wsPathPrefix={wsPathPrefix}\n isolated={isolated}\n >\n {children}\n </ScopeConnection>\n </>\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","/** A write was attempted before its RecordRoom connection became ready. */\nexport class RecordRoomNotReadyError extends Error {\n readonly code = 'not_ready'\n\n constructor(collection?: string) {\n super(\n collection\n ? `RecordRoom is not ready for \"${collection}\" writes.`\n : 'RecordRoom is not ready for writes.',\n )\n this.name = 'RecordRoomNotReadyError'\n }\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 // The shape `checkFieldPermissions` actually emits for a `writableFields`\n // refusal. This branch spelled it \"Cannot update field '<f>' … role '<r>'\",\n // a sentence the server has never sent, so every one of those refusals fell\n // through to the fieldless \"Field not editable\" below.\n const fieldMatch = error.match(/^FIELD ERROR: Role '(\\w+)' cannot modify field '(\\w+)'/)\n if (fieldMatch) {\n return {\n title: `${humanizeRole(fieldMatch[1])}s can't edit \"${fieldMatch[2]}\"`,\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 * used by 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 { RecordRoomNotReadyError } from './errors'\nimport { parseServerError } from './serverErrors'\nimport type { CollectionSchema, Query } 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/**\n * Liveness probe interval and response deadline. Ordinary quiet sockets send\n * a probe after 15 seconds and close only if that probe is unanswered for a\n * further 30 seconds.\n *\n * A peer that stops answering without closing leaves the connection\n * ESTABLISHED: no packets are lost, so nothing retransmits, so nothing ever\n * times out. Measured against a peer frozen mid-conversation, an unprobed\n * socket stayed open indefinitely — this is unbounded, not slow. That is a hung\n * Durable Object, a broken relay, or a blackholed path; `offline` does not fire\n * for any of them, because the machine's own network is fine.\n *\n * The probe costs nothing on the server: BaseRoom registers a\n * `WebSocketRequestResponsePair('ping','pong')`, so the Cloudflare runtime\n * answers it while the DO stays hibernated.\n */\nconst PING_EVERY_MS = 15_000\nconst PONG_TIMEOUT_MS = 30_000\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(\n queryKey: string,\n record: RecordData,\n changeType: 'create' | 'update' | 'delete',\n ): 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 for scopes that support schema discovery. */\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 /** 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 private heartbeat: ReturnType<typeof setInterval> | null = null\n /** Any inbound frame proves the peer is answering. */\n private lastInboundAt = 0\n /** Monotonic time of the one probe awaiting an inbound answer. */\n private outstandingPingAt: number | null = null\n /** Monotonic time of the prior heartbeat callback. A long gap means the\n * scheduler was suspended, so a pre-suspension probe cannot be judged. */\n private lastHeartbeatCheckAt = 0\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 // 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 this.startHeartbeat(ws)\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 // Code-agnostic on purpose: EVERY close reconnects, including clean/1000\n // and server-initiated 1012 ('state-refresh', sent by\n // BaseRoom.disconnectAllSockets after an out-of-band server-side write).\n // We must NOT special-case \"clean\" closes as terminal — that would strand\n // a client on stale data after the server kicked it to force a resync.\n // The reconnect's onopen re-subscribes every active query, so fresh\n // QUERY_RESULTs replace the stale store contents (full resync).\n ws.onclose = () => {\n this.stopHeartbeat()\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 /**\n * Turn a peer that stopped answering into a `close` event. That is the whole\n * job — `onclose` already reports 'disconnected', marks every query stale,\n * and reconnects with backoff. Without this it is never called, because\n * nothing about the connection is actually broken.\n */\n private startHeartbeat(ws: WebSocket): void {\n this.stopHeartbeat()\n this.lastInboundAt = performance.now()\n this.lastHeartbeatCheckAt = this.lastInboundAt\n this.outstandingPingAt = null\n this.heartbeat = setInterval(() => {\n if (ws.readyState !== WS_OPEN) return\n const now = performance.now()\n const callbackGap = now - this.lastHeartbeatCheckAt\n this.lastHeartbeatCheckAt = now\n if (this.outstandingPingAt !== null) {\n if (now - this.outstandingPingAt >= PONG_TIMEOUT_MS) {\n // The event loop can freeze after the runtime queued a pong but\n // before our message task runs. A heartbeat callback delayed by a\n // full response window cannot safely judge that old probe: send a\n // fresh one and give it the ordinary deadline after the scheduler\n // resumes. Normal 15-second callbacks still close a silent peer at\n // the original deadline.\n if (callbackGap >= PONG_TIMEOUT_MS) {\n ws.send('ping')\n this.outstandingPingAt = now\n } else {\n ws.close()\n }\n }\n return\n }\n // Only probe once the connection has actually gone quiet: an app\n // receiving live updates has already proved the peer is answering.\n if (now - this.lastInboundAt >= PING_EVERY_MS) {\n ws.send('ping')\n this.outstandingPingAt = now\n }\n }, PING_EVERY_MS)\n // A suspended timer's first resumed callback has no outstanding probe, so\n // it sends one instead of closing a healthy backgrounded connection.\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeat) clearInterval(this.heartbeat)\n this.heartbeat = null\n this.outstandingPingAt = null\n this.lastHeartbeatCheckAt = 0\n }\n\n /** Close without firing onclose — prevents the zombie-reconnect. */\n private teardownSocket(): void {\n this.stopHeartbeat()\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 RecordRoomNotReadyError())\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 this.lastInboundAt = performance.now()\n this.outstandingPingAt = null\n // The runtime's auto-response to our probe. It carries no payload — its\n // only meaning is the timestamp above — and it is not JSON, so it must\n // return before the parse below.\n if (event.data === 'pong') return\n\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 [subscriptionId, queryKey] of this.subscriptions) {\n try {\n const query = JSON.parse(queryKey) as Query\n if (query.collection !== collection) continue\n if (query.orderBy !== undefined || query.limit !== undefined) {\n this.sendMessage({\n type: MSG.SUBSCRIBE,\n payload: { subscriptionId, query },\n })\n continue\n }\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 * 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 uses the nearest RecordScope context during registry transitions.\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 // Resolve through the registry and retain the nearest scope during navigation.\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 this hook in a RecordScope that registers the 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 uses the nearest RecordScope context during registry transitions.\n */\n\nimport { useCallback, useContext, useMemo } from 'react'\nimport { RecordContext, useRecordAuth } from '../context'\nimport { useScopeRegistry } from '../ScopeRegistry'\nimport { RecordRoomNotReadyError } from '../errors'\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 /** True once the collection's RecordRoom can accept writes. Disable the\n * control on it (`disabled={!ready}`) rather than early-returning from\n * the handler: an early return drops the user's write silently, while a\n * write attempted before `ready` at least raises a `not_ready` error. */\n ready: boolean\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 // Resolve through the registry and retain the nearest scope during navigation.\n const registry = useScopeRegistry()\n const recordCtx = useContext(RecordContext)\n // Stable wrapper from RecordProvider — safe in a dependency array.\n const onWriteError = useRecordAuth()?.onWriteError\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 const ready = preferLocal ? recordCtx!.ready : (scopeEntry?.ready ?? recordCtx?.ready ?? false)\n\n if (!sendMessage || !sendConfirmed) {\n throw new Error(\n `useMutations('${collection}'): No scope found. Wrap this hook in a RecordScope that registers the collection.`,\n )\n }\n\n // The single readiness gate for every mutation below. A write refused here\n // never touches the socket, so `onWriteError` — the one surface an app can\n // observe a rejected fire-and-forget write on — is the only place it can show\n // up; it reports *and* throws, so callers that await keep their contract\n // and callers that don't still get a visible failure instead of silence.\n const assertReady = useCallback(() => {\n if (ready) return\n onWriteError?.({\n kind: 'not_ready',\n title: 'Not saved — still connecting',\n detail:\n `The room backing \"${collection}\" was not ready to accept writes yet, so the change was dropped. ` +\n `Gate the action on the \\`ready\\` flag from useMutations('${collection}') (e.g. \\`disabled={!ready}\\`) and retry once it is true.`,\n })\n throw new RecordRoomNotReadyError(collection)\n }, [ready, collection, onWriteError])\n\n const create = useCallback(\n async (data: T): Promise<string> => {\n assertReady()\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 [assertReady, sendMessage, collection],\n )\n\n const put = useCallback(\n async (recordId: string, data: Partial<T>): Promise<void> => {\n assertReady()\n sendMessage({ type: MSG.PUT, payload: { collection, recordId, data } })\n },\n [assertReady, sendMessage, collection],\n )\n\n const remove = useCallback(\n async (recordId: string): Promise<void> => {\n assertReady()\n sendMessage({ type: MSG.DELETE, payload: { collection, recordId } })\n },\n [assertReady, sendMessage, collection],\n )\n\n const createConfirmed = useCallback(\n async (data: T): Promise<string> => {\n assertReady()\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 [assertReady, sendConfirmed, collection],\n )\n\n const putConfirmed = useCallback(\n async (recordId: string, data: Partial<T>): Promise<void> => {\n assertReady()\n await sendConfirmed({\n type: MSG.PUT,\n payload: { collection, recordId, data: data as Record<string, unknown> },\n })\n },\n [assertReady, sendConfirmed, collection],\n )\n\n const removeConfirmed = useCallback(\n async (recordId: string): Promise<void> => {\n assertReady()\n await sendConfirmed({ type: MSG.DELETE, payload: { collection, recordId } })\n },\n [assertReady, 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 () => ({ ready, create, put, remove, createConfirmed, putConfirmed, removeConfirmed }),\n [ready, 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, useLayoutEffect, useCallback, useRef, useMemo } 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 const docKey = `${collection}:${recordId}:${fieldName}`\n\n const { doc, awareness } = useMemo(() => {\n const nextDoc = new Y.Doc()\n return { doc: nextDoc, awareness: new Awareness(nextDoc) }\n // The wire-level field key is the Yjs document identity. Reusing either\n // resource across keys can answer B's sync handshake with A's state.\n // eslint-disable-next-line react-hooks/exhaustive-deps -- docKey is the Yjs document identity\n }, [docKey])\n\n useEffect(() => {\n return () => {\n awareness.destroy()\n doc.destroy()\n }\n }, [awareness, doc])\n\n useLayoutEffect(() => {\n setSynced(false)\n setCanWrite(false)\n setUpdateCount(0)\n canWriteRef.current = false\n }, [docKey])\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 yText = useMemo(() => doc.getText(fieldName), [doc, fieldName])\n const [textState, setTextState] = useState(() => ({ doc, value: '' }))\n const text = textState.doc === doc ? textState.value : ''\n\n useEffect(() => {\n if (!synced) return\n const value = yText.toString()\n setTextState((current) =>\n current.doc === doc && current.value === value ? current : { doc, value },\n )\n }, [doc, yText, synced, updateCount])\n\n const setText = useCallback(\n (value: string) => {\n setTextState((current) =>\n current.doc === doc && current.value === value ? current : { doc, value },\n )\n\n if (canWrite) {\n doc.transact(() => {\n yText.delete(0, yText.length)\n yText.insert(0, value)\n })\n }\n },\n [doc, yText, 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 /** Available to admins; ordinary collaborators receive public identity only. */\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, connected, synced, canWrite } = useYjsRoom(docId, 'content')\n */\n\nimport { useState, useEffect, useLayoutEffect, 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\nconst UPDATE_BATCH_DELAY_MS = 16\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 the current WebSocket transport is open */\n connected: boolean\n /** Whether initial sync is complete */\n synced: boolean\n /** Whether user has write access */\n canWrite: boolean\n /** Whether the server has resolved this connection's write permission */\n writeAuthResolved: 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 [connected, setConnected] = useState(false)\n const [syncedDocId, setSyncedDocId] = useState<string | null>(null)\n const [canWrite, setCanWrite] = useState(false)\n const [writeAuthResolved, setWriteAuthResolved] = useState(false)\n const [text, setTextState] = useState('')\n const [, setUpdateCount] = useState(0)\n const synced = syncedDocId === docId\n\n const { doc, awareness } = useMemo(() => {\n const nextDoc = new Y.Doc()\n return { doc: nextDoc, awareness: new Awareness(nextDoc) }\n // docId deliberately owns this resource pair. Reusing either resource\n // across room ids can answer B's sync handshake with A's document state.\n // eslint-disable-next-line react-hooks/exhaustive-deps -- docId is the Yjs document identity\n }, [docId])\n\n const wsRef = useRef<WebSocket | null>(null)\n const isLocalRef = useRef(false)\n const applyingRemoteAwarenessRef = useRef(false)\n const pendingUpdateRef = useRef<Uint8Array | null>(null)\n const updateBatchTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n const yText = useMemo(() => doc.getText(fieldName), [doc, fieldName])\n\n const flushPendingUpdate = useCallback((socket = wsRef.current) => {\n if (updateBatchTimerRef.current) clearTimeout(updateBatchTimerRef.current)\n updateBatchTimerRef.current = null\n const update = pendingUpdateRef.current\n pendingUpdateRef.current = null\n if (!update || !socket || socket.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 socket.send(toUint8Array(enc).buffer)\n }, [])\n\n useEffect(() => {\n return () => {\n awareness.destroy()\n doc.destroy()\n }\n }, [awareness, doc])\n\n useLayoutEffect(() => {\n setConnected(false)\n setSyncedDocId(null)\n setCanWrite(false)\n setWriteAuthResolved(false)\n setTextState('')\n }, [docId])\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 let browserOnline = typeof navigator === 'undefined' || navigator.onLine\n let connecting = false\n\n const renewLocalAwareness = () => {\n const localState = awareness.getLocalState()\n if (localState) awareness.setLocalState(localState)\n }\n\n const connect = async () => {\n if (!alive || !browserOnline) return\n if (connecting) return\n if (ws?.readyState === WebSocket.CONNECTING || ws?.readyState === WebSocket.OPEN) return\n connecting = true\n\n // Permissions belong to one socket handshake. Every replacement socket\n // starts closed, including reconnects and same-room field changes.\n setCanWrite(false)\n setWriteAuthResolved(false)\n\n // The server's disconnect tombstone advances the old awareness clock\n // once. Renew while offline and again on open so the one transmitted\n // replay is strictly newer and peers accept it immediately.\n renewLocalAwareness()\n\n let token: string | null\n try {\n token = await getAuthToken()\n } finally {\n connecting = false\n }\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 || !browserOnline) 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 const socket = new WebSocket(url.toString())\n ws = socket\n socket.binaryType = 'arraybuffer'\n wsRef.current = socket\n\n socket.onopen = () => {\n if (ws !== socket) return socket.close()\n wsLog('connected', `yjs:${docId}`)\n setConnected(true)\n setSyncedDocId(null)\n renewLocalAwareness()\n }\n\n socket.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') {\n setCanWrite(msg.canWrite)\n setWriteAuthResolved(true)\n }\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 socket.send(toUint8Array(enc).buffer)\n setSyncedDocId(docId)\n break\n }\n case MSG_SYNC_STEP2: {\n Y.applyUpdate(doc, payload, 'server')\n setTextState(yText.toString())\n setSyncedDocId(docId)\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 } catch {\n // Awareness is ephemeral. Ignore a malformed peer frame without\n // breaking subsequent document sync messages on this socket.\n } finally {\n applyingRemoteAwarenessRef.current = false\n }\n }\n }\n\n socket.onclose = () => {\n if (ws !== socket) return\n wsLog('disconnected', `yjs:${docId}`)\n ws = null\n wsRef.current = null\n setConnected(false)\n setSyncedDocId(null)\n setCanWrite(false)\n setWriteAuthResolved(false)\n if (alive && browserOnline) reconnectTimer = setTimeout(connect, 1000)\n }\n\n socket.onerror = () => socket.close()\n }\n\n const handleOffline = () => {\n browserOnline = false\n setConnected(false)\n setSyncedDocId(null)\n setCanWrite(false)\n setWriteAuthResolved(false)\n ws?.close()\n }\n const handleOnline = () => {\n browserOnline = true\n void connect()\n }\n\n window.addEventListener('offline', handleOffline)\n window.addEventListener('online', handleOnline)\n void connect()\n\n return () => {\n wsLog('closing', `yjs:${docId}`)\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n window.removeEventListener('offline', handleOffline)\n window.removeEventListener('online', handleOnline)\n // This effect owns the socket, so it must drain a pending local batch\n // before closing it. A later effect cleanup cannot safely do so.\n flushPendingUpdate(ws)\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, flushPendingUpdate, 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 pendingUpdateRef.current = pendingUpdateRef.current\n ? Y.mergeUpdates([pendingUpdateRef.current, update])\n : update\n updateBatchTimerRef.current ??= setTimeout(flushPendingUpdate, UPDATE_BATCH_DELAY_MS)\n }\n\n doc.on('update', handler)\n return () => {\n doc.off('update', handler)\n flushPendingUpdate()\n }\n }, [doc, flushPendingUpdate])\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, connected, synced, canWrite, writeAuthResolved }\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 * async function onRunNow() {\n * const receipt = await trigger('daily-digest')\n * if (!receipt.ok) toast(receipt.error ?? receipt.reason)\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\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\n/**\n * Outcome of a cron mutation (trigger / pause / resume). Failures are\n * typed by origin: `read_only` and `not_connected` short-circuit locally\n * without sending a frame; `unknown_task` and `failed` come back in the\n * server's CRON_ACK receipt. Triggers execute synchronously in the DO\n * before the receipt is sent, so an ok trigger result means the run\n * completed — its execution record (duration, errors) arrives via\n * `history`.\n */\nexport type CronMutationResult =\n | { ok: true; taskName: string; requestId: string }\n | {\n ok: false\n reason: 'read_only' | 'not_connected' | 'unknown_task' | 'failed'\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 resolve `{ ok: false, reason: 'read_only' }`\n * and UIs should disable those controls. Reading tasks/history stays\n * available either way.\n */\n canWrite: boolean\n /**\n * Most recent ERROR frame from the room (e.g. a write rejected by a\n * concurrent role change). Null until one arrives.\n */\n lastError: string | null\n /** Manually trigger a task; resolves once the run completes. */\n trigger: (taskName: string) => Promise<CronMutationResult>\n /** Pause a task; resolves once the server applies it. */\n pause: (taskName: string) => Promise<CronMutationResult>\n /** Resume a paused task; resolves once the server applies it. */\n resume: (taskName: string) => Promise<CronMutationResult>\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 [lastError, setLastError] = useState<string | null>(null)\n const wsRef = useRef<WebSocket | null>(null)\n /**\n * Pending mutation resolvers keyed by requestId. No per-entry timeout:\n * triggers run to completion inline in the DO, so a long task's receipt\n * is legitimately slow; the socket-close drain below settles anything\n * the server can no longer answer.\n */\n const pendingRef = useRef<Map<string, (result: CronMutationResult) => void>>(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 drainPending = (result: CronMutationResult) => {\n for (const resolve of pendingRef.current.values()) resolve(result)\n pendingRef.current.clear()\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/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 [MSG.CRON_ACK]: (p) => {\n const resolve = pendingRef.current.get(p.requestId)\n if (!resolve) return\n pendingRef.current.delete(p.requestId)\n resolve(\n p.ok\n ? { ok: true, taskName: p.taskName, requestId: p.requestId }\n : { ok: false, reason: p.reason, error: p.error },\n )\n },\n [MSG.ERROR]: (p) => {\n setLastError(p.error)\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 drainPending({ ok: false, reason: 'not_connected' })\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 drainPending({ ok: false, reason: 'not_connected' })\n ws?.close()\n wsRef.current = null\n }\n }, [roomId])\n\n // Local write-gate. Mirrors the server check in CronRoom.onMessage so a\n // viewer's click resolves `read_only` locally instead of round-tripping\n // to ERROR. Connected sends register a resolver under a fresh requestId;\n // the room's CRON_ACK receipt settles the promise.\n const sendWrite = useCallback(\n <M extends { type: string; payload: unknown }>(\n build: (requestId: string) => M,\n ): Promise<CronMutationResult> => {\n // Each new mutation starts with a clean slate so a surfaced ERROR\n // always belongs to the latest attempt, not a stale earlier one.\n setLastError(null)\n if (!canWrite) return Promise.resolve({ ok: false, reason: 'read_only' })\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n return Promise.resolve({ ok: false, reason: 'not_connected' })\n }\n return new Promise((resolve) => {\n const requestId = crypto.randomUUID()\n pendingRef.current.set(requestId, resolve)\n // ws.send can throw synchronously if the socket transitions between\n // the readyState check and the call; without this guard the pending\n // entry would only settle on the next socket close.\n try {\n ws.send(encode(build(requestId)))\n } catch (e) {\n pendingRef.current.delete(requestId)\n resolve({\n ok: false,\n reason: 'not_connected',\n error: e instanceof Error ? e.message : String(e),\n })\n }\n })\n },\n [canWrite],\n )\n\n const trigger = useCallback(\n (taskName: string) => sendWrite((requestId) => clientBuild.cronTrigger(taskName, requestId)),\n [sendWrite],\n )\n const pause = useCallback(\n (taskName: string) => sendWrite((requestId) => clientBuild.cronPause(taskName, requestId)),\n [sendWrite],\n )\n const resume = useCallback(\n (taskName: string) => sendWrite((requestId) => clientBuild.cronResume(taskName, requestId)),\n [sendWrite],\n )\n\n return { tasks, history, connected, canWrite, lastError, 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.AUTH]: (p) => {\n if (!p.canWrite) rejectAllPending(new Error('JobRoom write access denied'))\n },\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 small ephemeral 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 { useRecordAuth } from '../context'\nimport { wsLog } from '../ws-log'\nimport { MSG } from '@/shared/protocol/constants'\nimport { clientBuild, dispatch, encode, type ServerMessage } from '@/shared/protocol/messages'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface PresencePeerClient {\n userId: string\n userName: 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 auth = useRecordAuth()\n const tokenProvider = auth?.getAuthToken ?? getAuthToken\n const [peers, setPeers] = useState<PresencePeerClient[]>([])\n const [connected, setConnected] = useState(false)\n const wsRef = useRef<WebSocket | null>(null)\n\n useEffect(() => {\n setConnected(false)\n setPeers([])\n let ws: WebSocket | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let alive = true\n let browserOnline = typeof navigator === 'undefined' || navigator.onLine\n let connecting = false\n\n const connect = async () => {\n if (!alive || !browserOnline || connecting) return\n if (ws?.readyState === WebSocket.CONNECTING || ws?.readyState === WebSocket.OPEN) return\n connecting = true\n\n let token: string | null\n try {\n token = await tokenProvider()\n } finally {\n connecting = false\n }\n if (!alive || !browserOnline) return\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 const socket = new WebSocket(url.toString())\n ws = socket\n wsRef.current = socket\n\n socket.onopen = () => {\n if (ws !== socket) return socket.close()\n wsLog('connected', `presence:${scopeId}`)\n setConnected(true)\n }\n\n socket.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 socket.onclose = () => {\n if (ws !== socket) return\n wsLog('disconnected', `presence:${scopeId}`)\n ws = null\n wsRef.current = null\n setConnected(false)\n if (alive && browserOnline) reconnectTimer = setTimeout(connect, 1000)\n }\n\n socket.onerror = () => socket.close()\n }\n\n const handleOffline = () => {\n browserOnline = false\n setConnected(false)\n ws?.close()\n }\n const handleOnline = () => {\n browserOnline = true\n void connect()\n }\n\n window.addEventListener('offline', handleOffline)\n window.addEventListener('online', handleOnline)\n void connect()\n\n return () => {\n wsLog('closing', `presence:${scopeId}`)\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n window.removeEventListener('offline', handleOffline)\n window.removeEventListener('online', handleOnline)\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, tokenProvider])\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 * Uploads pick their own transport. A small file goes in one request; a large\n * one is chunked into parts with `Blob.slice`, so peak memory is bounded by\n * the PART SIZE rather than by the file — a 1 GiB video costs one part, not a\n * gibibyte — and the file ceiling becomes a product decision rather than a\n * request-size accident. Callers do nothing differently either way.\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'\nimport {\n MAX_APP_FILE_BYTES,\n MAX_BASE64_UPLOAD_BYTES,\n UPLOAD_PART_BYTES,\n describeFilesFailure,\n encodeKeyPath,\n oversizeMessage,\n planUploadParts,\n} from '../../shared/app-files'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface R2UploadResult {\n success: boolean\n key?: string\n /**\n * `key` minus the scope prefix — the spelling that works with `list(prefix)`\n * filters and `upload(..., { key })` upserts, which never see the prefix.\n * Older servers omit it.\n */\n relativeKey?: string\n url?: string\n name?: string\n error?: string\n}\n\n/**\n * Read an upload response without assuming it is JSON.\n *\n * An over-limit upload is refused by the edge, not the app, and comes back as\n * an HTML error page — `response.json()` on that throws `Unexpected token '<'`,\n * which is what users saw instead of \"your file is too big\".\n */\n/** The one client-side size check, for both upload shapes. */\nfunction oversize(bytes: number, limit = MAX_APP_FILE_BYTES): R2UploadResult | null {\n return bytes > limit ? { success: false, error: oversizeMessage(bytes, limit) } : null\n}\n\nasync function readUploadResult(response: Response): Promise<R2UploadResult> {\n const text = await response.text()\n if (response.ok) {\n try {\n return JSON.parse(text) as R2UploadResult\n } catch {\n return { success: false, error: 'The files service returned a malformed response.' }\n }\n }\n return { success: false, error: describeFilesFailure(response.status, text) }\n}\n\nexport interface R2FileInfo {\n key: string\n /** `key` minus the scope prefix (see {@link R2UploadResult.relativeKey}). Older servers omit it. */\n relativeKey?: string\n size: number\n uploaded: string\n url: string\n originalName?: string\n uploadedBy?: string\n}\n\n/** One page of a listing: the entries plus the continuation state. */\nexport interface R2FileListPage {\n files: R2FileInfo[]\n /**\n * Opaque continuation token — pass it back as `cursor` to fetch the next\n * page. Present only while `truncated`; older servers never send one.\n */\n cursor?: string\n /** Whether entries exist past this page. */\n truncated: boolean\n}\n\n/** Told after each part lands, so a long upload can drive a progress bar. */\nexport type UploadProgress = (sent: number, total: number) => void\n\nexport interface UploadOptions {\n onProgress?: UploadProgress\n /**\n * Exact key for the file within the scope (slashes allowed), so\n * `upload(file, 'report.csv', { key: 'showcase/report.csv' })` is found by\n * `list('showcase')` and uploading to the same key again replaces the file.\n * Without it the server generates a unique key per upload — a location no\n * prefix you choose can address. `name` stays display metadata either way.\n */\n key?: string\n}\n\nexport interface UseR2FilesReturn {\n /**\n * Upload a File or Blob. Optionally provide a display name.\n *\n * A file above the single-request bound is chunked automatically and sent as\n * parts — nothing to opt into, and nothing held in memory beyond one part.\n * Pass `onProgress` to follow a large one.\n */\n upload: (file: File | Blob, name?: string, options?: UploadOptions) => Promise<R2UploadResult>\n /**\n * Upload raw base64 data.\n *\n * Single-request only: base64 has to be materialized whole to be decoded, so\n * this path keeps the one-request bound rather than the file ceiling — and\n * because the request carries the ENCODED string, its real budget is\n * `MAX_BASE64_UPLOAD_BYTES` (~18.75 MiB decoded), not the full 25 MiB. Send a\n * Blob through `upload` for anything larger; it chunks.\n */\n uploadBase64: (\n base64Data: string,\n name: string,\n mimeType?: string,\n options?: Pick<UploadOptions, 'key'>,\n ) => 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 /**\n * List files, optionally filtered by a sub-prefix within the scope.\n * `limit` caps how many entries come back; `cursor` continues from an\n * earlier page (use `listPage` to obtain one).\n *\n * Throws on a failed response — \"not authorized\" is an error, not an\n * empty account.\n */\n list: (prefix?: string, options?: { limit?: number; cursor?: string }) => Promise<R2FileInfo[]>\n /**\n * Like `list`, but returns the page envelope `list` flattens away:\n * `truncated` says whether more entries exist, and `cursor` (present only\n * while `truncated`) feeds the next call to fetch them.\n *\n * @example\n * ```tsx\n * let cursor: string | undefined\n * do {\n * const page = await listPage('showcase', { cursor })\n * render(page.files)\n * cursor = page.cursor\n * } while (cursor)\n * ```\n */\n listPage: (\n prefix?: string,\n options?: { limit?: number; cursor?: string },\n ) => Promise<R2FileListPage>\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: (\n fileOrKey: R2FileInfo | string,\n fileName?: string,\n ) => 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/** Append the caller-named upsert key, on whichever upload shape carries it. */\nfunction withUploadKey(params: string, key: string | undefined): string {\n if (!key) return params\n const pair = `key=${encodeURIComponent(key)}`\n return params ? `${params}&${pair}` : pair\n}\n\ninterface MultipartSession {\n uploadId: string\n uploadKey: string\n reservationId: string\n partSize: number\n}\n\n/**\n * Upload a file too large for one request, as a sequence of parts.\n *\n * `Blob.slice` is a lazy view rather than a copy, so the range is materialized\n * only while it is being sent: peak memory is one part, not one file, and a\n * 1 GiB video never becomes 1 GiB of tab memory. Its exact size becomes the\n * Content-Length the server requires. Parts go one at a time: they must,\n * because R2 sizes non-final parts uniformly and a half-uploaded object must be\n * abandonable in one call.\n *\n * The server names the key and confirms the fixed part size. A mismatched\n * reply means the client and server speak different protocol versions, so it\n * is refused before any content moves.\n */\nasync function uploadInParts(\n file: File | Blob,\n name: string,\n key: string | undefined,\n scopeParams: string,\n authHeaders: () => Promise<Record<string, string>>,\n onProgress?: UploadProgress,\n): Promise<R2UploadResult> {\n const headers = await authHeaders()\n const json = { ...headers, 'Content-Type': 'application/json' }\n const requestId = crypto.randomUUID()\n const initBody = JSON.stringify({\n requestId,\n name,\n mimeType: file.type || 'application/octet-stream',\n size: file.size,\n })\n // The caller-named key matters only at init: the server anchors it there\n // and answers with the session's `uploadKey`, which is what parts carry.\n const sendInit = (): Promise<Response> =>\n fetch(`/api/files/multipart?${withUploadKey(scopeParams, key)}`, {\n method: 'POST',\n headers: json,\n body: initBody,\n })\n let initResponse = await sendInit().catch(() => null)\n if (!initResponse || initResponse.status === 429 || initResponse.status >= 500) {\n initResponse = await sendInit().catch(() => null)\n }\n if (!initResponse) {\n return { success: false, error: 'The files service could not be reached.' }\n }\n if (!initResponse.ok) return await readUploadResult(initResponse)\n const session = (await initResponse.json()) as MultipartSession\n if (\n !session.uploadId ||\n !session.uploadKey ||\n !session.reservationId ||\n session.partSize !== UPLOAD_PART_BYTES\n ) {\n return {\n success: false,\n error: `The files service returned an unusable part size or multipart session (${session.partSize}).`,\n }\n }\n\n const query =\n `${scopeParams}&uploadKey=${encodeKeyPath(session.uploadKey)}` +\n `&uploadId=${encodeURIComponent(session.uploadId)}` +\n `&reservationId=${encodeURIComponent(session.reservationId)}`\n const plan = planUploadParts(file.size)\n const parts: Array<{ partNumber: number; etag: string }> = []\n let assembled = false\n try {\n for (const part of plan) {\n // The slice is rebuilt per attempt: a body is consumed once, so a retry\n // needs its own.\n const send = (): Promise<Response> =>\n fetch(`/api/files/multipart/part?${query}&partNumber=${part.partNumber}`, {\n method: 'PUT',\n headers,\n body: file.slice(part.start, part.end),\n })\n let response = await send().catch(() => null)\n // ONE retry, only for the answers the platform marks retryable —\n // otherwise a transient on part 40 of 52 throws away everything already\n // uploaded. Not a loop: a second failure is the answer.\n if (!response || response.status === 429 || response.status >= 500) {\n const retried = await send().catch(() => null)\n if (retried) response = retried\n }\n if (!response) return { success: false, error: 'The files service could not be reached.' }\n if (!response.ok) return await readUploadResult(response)\n parts.push((await response.json()) as { partNumber: number; etag: string })\n onProgress?.(part.end, file.size)\n }\n const completed = await fetch(`/api/files/multipart/complete?${query}`, {\n method: 'POST',\n headers: json,\n body: JSON.stringify({ parts }),\n })\n assembled = completed.ok\n return await readUploadResult(completed)\n } finally {\n // Anything short of an assembled object leaves parts holding space in the\n // customer's allocation. R2 would reap the session in 7 days; releasing it\n // now is the difference between a retry loop that costs nothing and one\n // that fills the app up.\n if (!assembled) {\n await fetch(`/api/files/multipart?${query}`, { method: 'DELETE', headers }).catch(\n () => undefined,\n )\n }\n }\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(\n async (file: File | Blob, name?: string, options?: UploadOptions): Promise<R2UploadResult> => {\n setIsUploading(true)\n try {\n const tooBig = oversize(file.size)\n if (tooBig) return tooBig\n const fileName = name ?? (file instanceof File ? file.name : 'upload')\n if (file.size > UPLOAD_PART_BYTES) {\n return await uploadInParts(\n file,\n fileName,\n options?.key,\n scopeParams,\n authHeaders,\n options?.onProgress,\n )\n }\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(\n `/api/files/upload?${withUploadKey(scopeParams, options?.key)}`,\n {\n method: 'POST',\n headers,\n body: formData,\n },\n )\n options?.onProgress?.(file.size, file.size)\n return await readUploadResult(response)\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : 'Upload failed' }\n } finally {\n setIsUploading(false)\n }\n },\n [scopeParams, authHeaders],\n )\n\n const uploadBase64 = useCallback(\n async (\n base64Data: string,\n name: string,\n mimeType?: string,\n options?: Pick<UploadOptions, 'key'>,\n ): Promise<R2UploadResult> => {\n setIsUploading(true)\n try {\n // The REQUEST is the encoded string, and that is what has to fit:\n // base64 spends 4 characters per 3 bytes, so measuring only the\n // decoded payload against the request bound passes files whose bodies\n // are a third larger than the server accepts — a check that green-lights\n // uploads the server then always 413s. Measured decoded, compared\n // against the decoded budget the request bound actually leaves.\n const tooBig = oversize(Math.floor((base64Data.length * 3) / 4), MAX_BASE64_UPLOAD_BYTES)\n if (tooBig) return tooBig\n const headers = await authHeaders()\n const response = await fetch(\n `/api/files/upload?${withUploadKey(scopeParams, options?.key)}`,\n {\n method: 'POST',\n headers: { ...headers, 'Content-Type': 'application/json' },\n body: JSON.stringify({ data: base64Data, name, mimeType }),\n },\n )\n return await readUploadResult(response)\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : 'Upload failed' }\n } finally {\n setIsUploading(false)\n }\n },\n [scopeParams, authHeaders],\n )\n\n const getUrl = useCallback(\n (fileOrKey: R2FileInfo | string): string => {\n const { key } = resolveFile(fileOrKey)\n return `/api/files/${encodeKeyPath(key)}?${scopeParams}`\n },\n [scopeParams],\n )\n\n const deleteFile = useCallback(\n async (fileOrKey: R2FileInfo | string): Promise<{ success: boolean; error?: string }> => {\n try {\n const headers = await authHeaders()\n const response = await fetch(getUrl(fileOrKey), {\n method: 'DELETE',\n headers,\n })\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 },\n [getUrl, authHeaders],\n )\n\n const listPage = useCallback(\n async (\n prefix?: string,\n options?: { limit?: number; cursor?: string },\n ): Promise<R2FileListPage> => {\n const headers = await authHeaders()\n const params = new URLSearchParams(scopeParams)\n if (prefix) params.set('prefix', prefix)\n if (options?.limit !== undefined) params.set('limit', String(options.limit))\n if (options?.cursor) params.set('cursor', options.cursor)\n const response = await fetch(`/api/files?${params.toString()}`, { headers })\n // A failure throws instead of flattening to [] — an expired session or\n // unreachable service used to read as \"no files\", which callers then\n // rendered as an empty account.\n if (!response.ok) {\n throw new Error(describeFilesFailure(response.status, await response.text()))\n }\n const result = (await response.json()) as {\n files?: R2FileInfo[]\n cursor?: string\n truncated?: boolean\n }\n // Older servers omit cursor/truncated; both read as \"nothing more\".\n return {\n files: result.files || [],\n cursor: result.cursor,\n truncated: result.truncated ?? false,\n }\n },\n [scopeParams, authHeaders],\n )\n\n const list = useCallback(\n async (prefix?: string, options?: { limit?: number; cursor?: string }): Promise<R2FileInfo[]> =>\n (await listPage(prefix, options)).files,\n [listPage],\n )\n\n const downloadFile = useCallback(\n async (\n fileOrKey: R2FileInfo | string,\n fileName?: string,\n ): Promise<{ success: boolean; error?: string }> => {\n const { key, name: infoName } = resolveFile(fileOrKey)\n try {\n const headers = await authHeaders()\n const response = await fetch(getUrl(key), { headers })\n\n if (!response.ok) {\n return { success: false, error: describeFilesFailure(response.status, await response.text()) }\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 },\n [getUrl, authHeaders],\n )\n\n const readFile = useCallback(\n async (fileOrKey: R2FileInfo | string): Promise<Response> => {\n const headers = await authHeaders()\n const response = await fetch(getUrl(fileOrKey), { headers })\n if (!response.ok) {\n throw new Error(describeFilesFailure(response.status, await response.text()))\n }\n return response\n },\n [getUrl, authHeaders],\n )\n\n return {\n upload,\n uploadBase64,\n deleteFile,\n downloadFile,\n readFile,\n list,\n listPage,\n getUrl,\n isUploading,\n }\n}\n","/**\n * App-files limits, chunk planning, and failure reading — shared by every\n * caller of the files API: the browser hook (`client/storage/useR2Files.ts`),\n * the CLI (`cli/lib/app-files-api.ts`), the server handler\n * (`server/utils/scoped-r2-files.ts`), and the docs that quote them.\n *\n * There are three numbers here and they answer three different questions.\n * Conflating them is what produced the old 25 MiB per-file ceiling: that was\n * never a product decision, it was the size of one HTTP request leaking out as\n * a rule about files. A request is bounded by physics — Cloudflare rejects an\n * oversized body before any worker runs, and the worker's isolate is ~128 MiB.\n * A FILE is not, once it travels as a sequence of bounded requests.\n *\n * The other half of this module is failure reading. An upload refused at the\n * edge answers with a Cloudflare HTML page, which every caller used to feed\n * straight into `JSON.parse` and report as \"Unexpected token '<' … is not\n * valid JSON\". `describeFilesFailure` exists to make that impossible.\n */\n\n/**\n * The largest single file the app-files API accepts.\n *\n * Reachable only through the multipart transport: a file above\n * {@link UPLOAD_PART_BYTES} is uploaded as a sequence of parts, so no request\n * and no isolate ever holds more than one part. Enforced by the shared handler\n * (`server/utils/scoped-r2-files.ts`) twice — once at init against the\n * declared total, so an impossible upload fails before a byte moves, and again\n * at complete against the assembled object, because the declared total is\n * client-supplied. Callers check it locally too, but only to fail early;\n * skipping the client check cannot widen what is accepted.\n */\nexport const MAX_APP_FILE_BYTES = 1024 * 1024 * 1024\n\n/**\n * The most any ONE request body may carry — a whole small file on the\n * single-request path, or one part on the multipart path.\n *\n * This is the transport's real physical bound, and the only thing 25 MiB ever\n * meant. Above it the worker risks exhausting its isolate holding the body;\n * well above it (~100 MB) Cloudflare refuses the request before the worker\n * runs at all, which is why callers must read failures as text first.\n */\nexport const MAX_UPLOAD_REQUEST_BYTES = 25 * 1024 * 1024\n\n/**\n * The part size clients chunk at, and the threshold above which they must.\n *\n * Below the {@link MAX_UPLOAD_REQUEST_BYTES} bound with headroom for the\n * request envelope, and comfortably inside R2's own part rules: parts must be\n * at least 5 MiB and every non-final part must be exactly the same size\n * (R2 errors 10011 / 10048). The server advertises this at init — a client\n * chunks at the size it is told, not at this constant, so the number can move\n * without stranding older callers mid-upload.\n */\nexport const UPLOAD_PART_BYTES = 20 * 1024 * 1024\n\n/**\n * The highest part number the server accepts.\n *\n * The server derives the only valid part number and byte length from the\n * declared total and {@link UPLOAD_PART_BYTES}. The maximum part count is\n * therefore the exact number needed to reach the file ceiling.\n *\n * R2 allows 10,000 parts, so this is far inside what the API permits.\n */\nexport const MAX_UPLOAD_PARTS = Math.ceil(MAX_APP_FILE_BYTES / UPLOAD_PART_BYTES)\n\n/**\n * The largest base64 payload the single-request JSON shape can carry.\n *\n * Derived from the ENCODED size, because that is what the request body is:\n * base64 spends 4 characters per 3 bytes, so a payload measured only by its\n * decoded size can sit under {@link MAX_UPLOAD_REQUEST_BYTES} and still\n * produce a body a third larger than the server will accept — a client check\n * that passes everything the server then refuses. Roughly 18.75 MiB.\n */\nexport const MAX_BASE64_UPLOAD_BYTES = Math.floor((MAX_UPLOAD_REQUEST_BYTES * 3) / 4)\n\n/** Byte ranges for a chunked upload: `[start, end)`, `end` exclusive. */\nexport interface UploadPart {\n partNumber: number\n start: number\n end: number\n}\n\n/**\n * Split a file into the parts the multipart transport expects.\n *\n * Every part is exactly `partSize` except the last, which is what R2 requires\n * of non-final parts. Both chunking clients plan with this, so \"which bytes\n * are part N\" is answered in one place.\n */\nexport function planUploadParts(totalBytes: number): UploadPart[] {\n const parts: UploadPart[] = []\n for (let start = 0; start < totalBytes; start += UPLOAD_PART_BYTES) {\n parts.push({\n partNumber: parts.length + 1,\n start,\n end: Math.min(start + UPLOAD_PART_BYTES, totalBytes),\n })\n }\n return parts\n}\n\nconst GIB = 1024 * 1024 * 1024\n\n/**\n * The customer-visible storage schedule, in bytes, PER ACCOUNT — the total an\n * owner's uploaded files may occupy across every app they own, not a budget\n * each app gets separately. This is the number the billing page advertises.\n *\n * Admitted by the shared files handler (`server/utils/scoped-r2-files.ts`),\n * which sums the owner's apps rather than the one being written to.\n *\n * It deliberately does NOT govern the repo store — see\n * `REPO_STORE_LIMIT_BYTES` below, and docs/platform/r2-storage.md.\n */\nexport const ACCOUNT_STORAGE_LIMIT_BYTES = {\n test: 0,\n free: 1 * GIB,\n starter: 10 * GIB,\n premium: 30 * GIB,\n admin: 100 * GIB,\n} as const\n\n/** The account storage limit for a billing tier; unknown or missing tiers get free. */\nexport function storageLimitForTier(tier: string | null | undefined): number {\n if (tier && Object.hasOwn(ACCOUNT_STORAGE_LIMIT_BYTES, tier)) {\n return ACCOUNT_STORAGE_LIMIT_BYTES[tier as keyof typeof ACCOUNT_STORAGE_LIMIT_BYTES]\n }\n return ACCOUNT_STORAGE_LIMIT_BYTES.free\n}\n\n/**\n * The repo store's budget, per app, flat across every tier.\n *\n * Git packs, retained rollback bundles, and stored deploy assets are platform\n * bookkeeping, not something a customer buys — so this is a working ceiling,\n * not an entitlement, and it is deliberately small. An app that cannot fit its\n * SOURCE plus a few rollback bundles in this has something in the wrong place:\n * user uploads belong in the app-files allocation above, which is what the\n * account schedule sizes.\n *\n * Being flat is the point. Tying it to the tier table meant every raise to the\n * customer-facing number silently multiplied platform-side storage per app,\n * and made a push depend on a billing lookup that could refuse it.\n */\nexport const REPO_STORE_LIMIT_BYTES = 256 * 1024 * 1024\n\n/** The one storage-quota refusal sentence. `usedBytes` is net of any object\n * the upload replaces, so an upsert is charged only for its growth. */\nexport function storageQuotaMessage(\n incomingBytes: number,\n usedBytes: number,\n limitBytes: number,\n): string {\n const free = Math.max(0, limitBytes - usedBytes)\n return (\n `This upload needs ${formatBytes(incomingBytes)} but the app's file storage has ` +\n `${formatBytes(free)} free (${formatBytes(usedBytes)} of ${formatBytes(limitBytes)} used). ` +\n `Delete files with \\`deepspace app files rm <key>\\`, or upgrade the owner's plan.`\n )\n}\n\n/**\n * The largest single file a deploy may ship as a static asset.\n *\n * Cloudflare Static Assets refuses anything larger, so this is a hard edge of\n * the platform rather than a policy dial — which is why the CLI may check it\n * locally. It lives here so the deploy worker and the CLI cannot drift; the\n * per-DEPLOY total is deliberately NOT here, because it is env-configurable\n * per environment and only the server can know it.\n */\nexport const MAX_DEPLOY_ASSET_FILE_BYTES = 25 * 1024 * 1024\n\n/** MIME types that must download rather than render in the app's origin. */\nexport const DANGEROUS_MIME_TYPES = new Set<string>([\n 'text/html',\n 'application/xhtml+xml',\n 'image/svg+xml',\n 'application/xml',\n 'text/xml',\n 'application/javascript',\n 'text/javascript',\n 'application/ecmascript',\n 'text/ecmascript',\n])\n\nexport function formatBytes(bytes: number): string {\n if (bytes >= 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GiB`\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`\n if (bytes >= 1024) return `${Math.round(bytes / 1024)} KiB`\n return `${bytes} B`\n}\n\n/** Encode an R2 key as URL path segments while preserving its hierarchy. */\nexport function encodeKeyPath(key: string): string {\n return key.split('/').map(encodeURIComponent).join('/')\n}\n\n/** The one oversize sentence, for whichever caller notices first. */\nexport function oversizeMessage(bytes: number, limit = MAX_APP_FILE_BYTES): string {\n return `That file is ${formatBytes(bytes)}; the limit is ${formatBytes(limit)} per file.`\n}\n\n/**\n * The one over-the-request-bound sentence. Distinct from\n * {@link oversizeMessage} because it names a fixable mistake: the file is fine,\n * the caller just sent it in one piece instead of in parts.\n */\nexport function oversizeRequestMessage(bytes: number): string {\n return (\n `This request carries ${formatBytes(bytes)}; one request may carry ` +\n `${formatBytes(MAX_UPLOAD_REQUEST_BYTES)}. Files above ` +\n `${formatBytes(UPLOAD_PART_BYTES)} upload in parts.`\n )\n}\n\n/** A part is bounded tighter than a request; say which number was crossed. */\nexport function oversizePartMessage(bytes: number): string {\n return (\n `This part carries ${formatBytes(bytes)}; a part may carry ` +\n `${formatBytes(UPLOAD_PART_BYTES)} — the size the upload advertised.`\n )\n}\n\n/**\n * Turn a failed files-API response into a sentence worth showing.\n *\n * `body` is the raw response text. A JSON `{ error }` is the app's own\n * refusal and is passed through; anything else came from the edge (an HTML\n * error page, an empty body, a proxy) and is described by its status instead\n * of being echoed, because echoing it is what produced the JSON-parse noise.\n */\nexport function describeFilesFailure(status: number, body: string): string {\n const trimmed = body.trim()\n if (trimmed.startsWith('{')) {\n try {\n const parsed = JSON.parse(trimmed) as { error?: string }\n if (parsed.error) return parsed.error\n } catch {\n // Not the app's JSON after all — fall through to the status description.\n }\n }\n if (status === 413) {\n return (\n `The upload is too large. Files are limited to ${formatBytes(MAX_APP_FILE_BYTES)}, ` +\n `and one request may carry ${formatBytes(MAX_UPLOAD_REQUEST_BYTES)} — a larger request ` +\n `is rejected by the network before the app receives it.`\n )\n }\n if (status === 411) return 'The upload did not declare its length.'\n if (status === 401 || status === 403) return 'Not authorized to write these files.'\n if (status === 415) return 'That file type is not accepted.'\n if (status >= 500) return `The files service failed (HTTP ${status}). Retry shortly.`\n return `Upload failed (HTTP ${status}).`\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; description?: string }) => {\n return createMutation({\n name: data.name,\n type: 'public',\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 { GroupedReaction, Reaction } from './channel-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) =>\n 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 {\n reactions: records as RecordData<Reaction>[],\n status,\n error,\n getReactionsForMessage,\n toggle,\n }\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 { createConfirmed, removeConfirmed } = useMutations<ChannelMember>('channel-members')\n\n const join = useCallback(async () => {\n if (!channelId || !user) return\n const existing = records.find((r) => r.data.userId === user.id)\n if (existing) return\n await createConfirmed({\n channelId,\n userId: user.id,\n joinedAt: new Date().toISOString(),\n } as unknown as ChannelMember)\n }, [channelId, user, records, createConfirmed])\n\n const leave = useCallback(async () => {\n if (!channelId || !user) return\n const membership = records.find((r) => r.data.userId === user.id)\n if (membership) await removeConfirmed(membership.recordId)\n }, [channelId, user, records, removeConfirmed])\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","'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 * Platform API error normalization — the one chokepoint that turns the\n * api-worker's error envelope into a render-safe + branch-safe shape.\n *\n * The platform emits errors as `{ error: <machine slug>, message?: <human>,\n * ...details }` (e.g. 402 `{ error: 'insufficient_credits', message:\n * 'Insufficient credits.', availableCredits, requiredCredits }`; some routes\n * send the bare slug with no `message` at all, e.g. 409\n * `{ error: 'owner_connect_not_ready' }`). Consumers must never show the slug\n * to a person or make an agent parse the sentence — this module separates the\n * two once, for the integration client, the billing hooks, and the CLI.\n */\n\n/** One structured validation issue (Zod shape) from a `validation_failed` response. */\nexport interface ApiErrorIssue {\n path?: string[]\n message: string\n code?: string\n}\n\n/** A platform error response, normalized. */\nexport interface NormalizedApiError {\n /**\n * Human-readable text, safe to render:\n * `message ?? non-slug error ?? humanized(slug) ?? slug`.\n */\n error: string\n /** The machine slug (e.g. 'insufficient_credits') for branching — never render it. */\n code?: string\n /** HTTP status of the response; 0 when no HTTP response arrived (transport failure). */\n status: number\n /** Structured fields the server sent alongside the error (e.g. `availableCredits`). */\n details?: Record<string, unknown>\n /** Zod issues accompanying a `validation_failed` response. */\n issues?: ApiErrorIssue[]\n}\n\n// A machine slug: lowercase alphanumeric segments joined by `_`, `.`, or `-`.\n// Rejects human sentences (\"Unknown integration: openai/foo\") so they pass\n// through as the error text rather than masquerading as a code.\nconst SLUG_RE = /^[a-z0-9]+(?:[_.-][a-z0-9]+)*$/\n\n// Human text for the slugs the platform emits without (or before) a `message`\n// of their own. Unknown slugs fall through to the raw message/slug unchanged.\nconst HUMAN_MESSAGES: Record<string, string> = {\n owner_connect_not_ready:\n \"This app isn't ready to accept payments yet — the app owner needs to finish payment setup.\",\n invalid_json: 'The request body was not valid JSON.',\n invalid_body: 'The request body is invalid.',\n invalid_product_id: 'Invalid product id.',\n duplicate_product_id: 'Duplicate product id.',\n unknown_product: 'Unknown product.',\n too_many_products: 'Too many products declared.',\n invalid_amount: 'Invalid amount.',\n invalid_name: 'Invalid product name.',\n invalid_description: 'Invalid product description.',\n unsupported_currency: 'Unsupported currency.',\n unknown_app: 'Unknown app.',\n not_app_owner: 'Only the app owner (or a collaborator) can do this.',\n invalid_return_url: 'Invalid return URL.',\n invalid_cancel_url: 'Invalid cancel URL.',\n unknown_plan: 'Unknown subscription plan.',\n unknown_price: 'No price is configured for that billing interval.',\n no_subscription: 'No subscription found for this account.',\n}\n\n/**\n * Normalize a platform error body (parsed JSON, or anything else) plus its\n * HTTP status into a {@link NormalizedApiError}. Pass status 0 for failures\n * that never produced an HTTP response.\n */\nexport function normalizeApiError(status: number, body: unknown): NormalizedApiError {\n const payload = (body !== null && typeof body === 'object' ? body : {}) as Record<\n string,\n unknown\n >\n const rawError = typeof payload.error === 'string' ? payload.error : undefined\n const message = typeof payload.message === 'string' ? payload.message : undefined\n // In the `{ code: slug, error: human }` convention the server's `error` IS\n // the specific human sentence — it must beat the generic slug mapping.\n const humanError = rawError !== undefined && !SLUG_RE.test(rawError) ? rawError : undefined\n const code =\n rawError !== undefined && SLUG_RE.test(rawError)\n ? rawError\n : typeof payload.code === 'string' && SLUG_RE.test(payload.code)\n ? payload.code\n : undefined\n\n const details: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(payload)) {\n if (\n key === 'success' ||\n key === 'error' ||\n key === 'message' ||\n key === 'issues' ||\n key === 'code'\n )\n continue\n details[key] = value\n }\n\n return {\n error:\n message ??\n humanError ??\n (code !== undefined ? HUMAN_MESSAGES[code] : undefined) ??\n rawError ??\n `Request failed (${status})`,\n ...(code !== undefined ? { code } : {}),\n status,\n ...(Object.keys(details).length > 0 ? { details } : {}),\n ...(Array.isArray(payload.issues) ? { issues: payload.issues as ApiErrorIssue[] } : {}),\n }\n}\n\n/**\n * The error the billing hooks (`useCheckout`, `useSubscription`) throw for a\n * platform refusal. `message` is human text safe to render; branch on `code`:\n *\n * try {\n * await subscribe('pro')\n * } catch (e) {\n * if (e instanceof PlatformApiError && e.code === 'owner_connect_not_ready') {\n * // owner hasn't finished payment onboarding\n * }\n * }\n */\nexport class PlatformApiError extends Error {\n /** The machine slug (e.g. 'owner_connect_not_ready'), when the server sent one. */\n readonly code?: string\n /** HTTP status of the response; 0 when no HTTP response arrived. */\n readonly status: number\n /** Structured fields the server sent alongside the error. */\n readonly details?: Record<string, unknown>\n /** Zod issues accompanying a `validation_failed` response. */\n readonly issues?: ApiErrorIssue[]\n\n constructor(normalized: NormalizedApiError) {\n super(normalized.error)\n this.name = 'PlatformApiError'\n this.code = normalized.code\n this.status = normalized.status\n this.details = normalized.details\n this.issues = normalized.issues\n }\n}\n\n/**\n * The machine slug from a thrown {@link PlatformApiError}, or null for any\n * other thrown value — the chokepoint for \"which refusal was this?\" checks.\n */\nexport const apiErrorCode = (e: unknown): string | null =>\n e instanceof PlatformApiError ? (e.code ?? null) : null\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'\nimport { normalizeApiError, type ApiErrorIssue } from '../shared/api-error'\n\nconst ENDPOINT_PREFIX = '/api/integrations'\nconst DEFAULT_TIMEOUT_MS = 120_000\nconst ENDPOINT_RE = /^[a-z0-9][a-z0-9._~-]*(?:\\/[a-z0-9][a-z0-9._~-]*)*$/i\n\ninterface IntegrationResponse<T = unknown> {\n success: boolean\n data?: T\n /** Human-readable error, safe to render directly. */\n error?: string\n /** Machine slug (e.g. 'insufficient_credits') for branching — never render it. */\n code?: string\n /** HTTP status of the failed response; 0 when no response arrived (timeout, abort, network). */\n status?: number\n /** Structured fields the server sent alongside the error (e.g. `availableCredits`). */\n details?: Record<string, unknown>\n issues?: ApiErrorIssue[]\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 !== '' && !ENDPOINT_RE.test(endpoint)) {\n throw new TypeError(\n 'Integration endpoints must be relative route segments, for example \"openai/chat-completion\".',\n )\n }\n const path = 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 query?: URLSearchParams,\n): Promise<IntegrationResponse<T>> {\n const queryString = query?.toString()\n const url = `${resolveUrl(endpoint)}${queryString ? `?${queryString}` : ''}`\n const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS\n\n if (options?.signal?.aborted) {\n return { success: false, error: 'Request cancelled', status: 0 }\n }\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 options?.signal?.addEventListener('abort', abortFromCaller, { once: true })\n let timedOut = false\n const timeout = setTimeout(() => {\n timedOut = true\n controller.abort()\n }, 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})`, status: res.status }\n }\n\n if (!res.ok || payload.success === false) {\n // The api-worker's envelope is { error: <machine slug>, message: <human>,\n // ...details } — normalize so `error` is always the human text and the\n // slug rides on `code` for branching.\n return { success: false, ...normalizeApiError(res.status, payload) }\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 {\n success: false,\n error: timedOut ? `Request timed out after ${timeoutMs}ms` : 'Request cancelled',\n status: 0,\n }\n }\n return {\n success: false,\n error: err instanceof Error ? err.message : 'Request failed',\n status: 0,\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>(\n endpoint: string,\n params?: Record<string, string | number | boolean | null | undefined>,\n options?: RequestOptions,\n ) {\n let searchParams: URLSearchParams | undefined\n if (params) {\n searchParams = new URLSearchParams()\n for (const [k, v] of Object.entries(params)) {\n if (v != null) searchParams.set(k, String(v))\n }\n }\n return request<T>('GET', endpoint, undefined, options, searchParams)\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\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\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 { authedJson } from '../authed-json'\nimport { apiErrorCode } from '../../shared/api-error'\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 /** Human-readable text of the last load failure, safe to render. */\n error: string | null\n /**\n * Machine slug of the last load failure (e.g. 'owner_connect_not_ready')\n * for branching — never render it. Null when there is no error or the\n * failure carried no slug (e.g. a network drop).\n */\n errorCode: 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; PricingTable also requires an onSelect callback. */\n plans: PlanInfo[]\n subscribe: (planSlug: string, opts?: SubscribeOpts) => Promise<SubscribeResult>\n openPortal: (returnUrl?: string) => Promise<{ url: string }>\n refresh: () => Promise<void>\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 const [errorCode, setErrorCode] = 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 setErrorCode(null)\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to load subscription')\n setErrorCode(apiErrorCode(e))\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 errorCode,\n hasTier,\n isAtLeast,\n plans: state.plans,\n subscribe,\n openPortal,\n refresh,\n }\n}\n","/**\n * Authenticated JSON fetch against the app's same-origin `/_deepspace` proxy.\n *\n * Shared by the billing hooks (`useCheckout`, `useSubscription`). On a non-2xx\n * response it throws a {@link PlatformApiError} built by the shared error\n * normalizer, so callers render `.message` (human text) and branch on `.code`\n * (the machine slug, e.g. 'owner_connect_not_ready') instead of string-sniffing.\n */\n\nimport { getAuthToken } from './auth/token'\nimport { normalizeApiError, PlatformApiError } from '../shared/api-error'\n\nexport async 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 PlatformApiError(normalizeApiError(res.status, data))\n }\n return data as T\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 by\n * `<PricingTable plans={sub.plans} onSelect={...}>`. Fields beyond slug/rank\n * are optional so a free tier (no prices, no trial) doesn't need to fabricate\n * 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 { authedJson } from '../authed-json'\nimport { apiErrorCode } from '../../shared/api-error'\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 /** Human-readable text of the last failure, safe to render. */\n error: string | null\n /**\n * Machine slug of the last failure (e.g. 'owner_connect_not_ready') for\n * branching — never render it. Null when there is no error or the\n * failure carried no slug (e.g. a network drop).\n */\n errorCode: 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\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 const [errorCode, setErrorCode] = 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 setErrorCode(null)\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to load purchases')\n setErrorCode(apiErrorCode(e))\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 setErrorCode(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 setErrorCode(apiErrorCode(e))\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 errorCode,\n purchases,\n owned,\n ownsProduct,\n refresh,\n }\n}\n","/**\n * Client-side error reporting — shared contract.\n *\n * Browser JS errors never invoke the app's Worker, so they never reach\n * Workers Logs and are invisible to `deepspace logs`. The opt-in reporter\n * (`installClientErrorReporter`, client entry) forwards them to the app's own\n * Worker via `registerClientErrorRoute` (worker entry), which `console.error`s\n * a marker-prefixed envelope. The platform log reader recognises the marker\n * and tags the event `source: 'client'`.\n *\n * This module is isomorphic — no DOM, no Hono — so both halves import the\n * exact same marker, caps, and normaliser and can never drift.\n */\n\n/**\n * Prefix the Worker route prepends before JSON-stringifying a report into a\n * single `console.error` line. The platform mapper\n * (platform/deploy-worker/src/lib/workers-logs.ts) detects this prefix to set\n * `source: 'client'`. Deliberately distinctive so an ordinary app log is\n * vanishingly unlikely to collide. The marker is added SERVER-SIDE by the app\n * Worker — a browser cannot forge it onto a genuine server log.\n *\n * The platform copy is kept in sync by a drift test; if you change this\n * string, that test fails until the platform const matches.\n */\nexport const CLIENT_LOG_MARKER = '[deepspace:client-error]'\n\n/** Same-origin path the reporter POSTs to and the route registers. */\nexport const CLIENT_ERROR_PATH = '/_deepspace/client-errors'\n\n/**\n * Per-field caps on the browser-supplied report. These bound the payload the\n * reporter sends and the route logs — they are NOT a hard serialized-size\n * guarantee: JSON escaping (a control char → `\\u00XX`, 6×) and multibyte text\n * can inflate the serialized line several-fold (a maxed adversarial envelope is\n * ~24 KB). That's fine — Cloudflare Workers Logs truncates a log event at\n * ~256 KB (bytes), so even the worst case survives intact and re-parses on\n * read. The platform reader (`workers-logs.ts`) then caps each surfaced field\n * (message/stack) at 8 KB *after* parsing.\n */\nexport const CLIENT_ERROR_CAPS = {\n message: 1024,\n stack: 4096,\n name: 256,\n url: 512,\n} as const\n\nexport type ClientErrorKind = 'error' | 'unhandledrejection' | 'react'\n\n/** A sanitized, capped browser-error report — the wire + log-line payload. */\nexport interface ClientErrorReport {\n kind: ClientErrorKind\n message: string\n name?: string\n stack?: string\n /** Page URL where it fired (href or the script filename). */\n url?: string\n line?: number\n col?: number\n}\n\nfunction capText(value: unknown, max: number): string | undefined {\n if (typeof value !== 'string') return undefined\n return value.length > max ? value.slice(0, max) : value\n}\n\nfunction finiteNumber(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined\n}\n\n/**\n * Validate + cap an untrusted report shape into a `ClientErrorReport`, or\n * `null` when there's nothing useful to log. Used on BOTH ends: the browser\n * caps before sending, and the Worker route re-normalises defensively — it\n * never trusts the posted body, mirroring the platform reader's field-by-field\n * whitelist philosophy.\n */\nexport function normalizeClientErrorReport(raw: unknown): ClientErrorReport | null {\n if (typeof raw !== 'object' || raw === null) return null\n const o = raw as Record<string, unknown>\n\n const kind: ClientErrorKind =\n o.kind === 'unhandledrejection' || o.kind === 'react' ? o.kind : 'error'\n const message = capText(o.message, CLIENT_ERROR_CAPS.message) ?? ''\n const name = capText(o.name, CLIENT_ERROR_CAPS.name)\n const stack = capText(o.stack, CLIENT_ERROR_CAPS.stack)\n const url = capText(o.url, CLIENT_ERROR_CAPS.url)\n const line = finiteNumber(o.line)\n const col = finiteNumber(o.col)\n\n // Nothing actionable — drop it (Worker route → 400).\n if (!message && !stack) return null\n\n const report: ClientErrorReport = { kind, message: message || (name ?? 'Error') }\n if (name) report.name = name\n if (stack) report.stack = stack\n if (url) report.url = url\n if (line !== undefined) report.line = line\n if (col !== undefined) report.col = col\n return report\n}\n\n/** The exact single-string line the Worker route writes to Workers Logs. */\nexport function clientErrorToLogLine(report: ClientErrorReport): string {\n return `${CLIENT_LOG_MARKER} ${JSON.stringify(report)}`\n}\n","/**\n * Client-side error reporter (opt-in) — the browser half of client-side error\n * reporting.\n *\n * // src/main.tsx (or your root)\n * import { installClientErrorReporter } from 'deepspace'\n * installClientErrorReporter()\n *\n * Hooks `window` 'error' + 'unhandledrejection' and forwards each to the app's\n * own Worker (see `registerClientErrorRoute` in 'deepspace/worker'), where it\n * lands in Workers Logs and shows up in `deepspace logs` tagged CLIENT.\n *\n * Best-effort by contract: it dedupes, caps how many it sends per page load,\n * and NEVER throws — a broken reporter must not break the app it watches.\n */\n\nimport { CLIENT_ERROR_PATH, normalizeClientErrorReport } from '../shared/client-errors'\nimport type { ClientErrorReport } from '../shared/client-errors'\n\nexport interface ClientErrorReporterOptions {\n /** Override the ingestion path (default `/_deepspace/client-errors`). */\n endpoint?: string\n /** Max reports sent per page load — a runaway loop can't flood (default 50). */\n maxReports?: number\n}\n\n// Strip query + fragment from a URL before it leaves the browser — a page href\n// or script filename may carry tokens the app put in its own URL, and those\n// should never be persisted in the platform's account-wide log store. Returns\n// origin + pathname only. Never throws.\nfunction stripQuery(u: string | undefined): string | undefined {\n if (!u) return undefined\n try {\n const parsed = new URL(u, window.location?.href)\n return parsed.origin + parsed.pathname\n } catch {\n return undefined\n }\n}\n\n// Fire-and-forget POST. Same-origin, so the session cookie rides along for\n// logged-in users; anonymous visitors are accepted too. keepalive lets a\n// report survive the page unloading right after the error.\nfunction post(endpoint: string, report: ClientErrorReport): void {\n try {\n void fetch(endpoint, {\n method: 'POST',\n keepalive: true,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(report),\n }).catch(() => {})\n } catch {\n // A reporter must never throw — swallow everything.\n }\n}\n\n// Module-global so a repeated install() call (against the same cached module\n// instance) doesn't double-report. Note: a full Vite HMR reload that\n// re-evaluates this module resets it — wire `import.meta.hot.dispose(uninstall)`\n// if you install inside an HMR-reloaded module.\nlet installed = false\n\n/**\n * Install global handlers that forward uncaught browser errors to the app's\n * Worker. No-op outside a browser. Returns an uninstall function.\n */\nexport function installClientErrorReporter(\n options: ClientErrorReporterOptions = {},\n): () => void {\n if (typeof window === 'undefined' || installed) return () => {}\n installed = true\n\n const endpoint = options.endpoint ?? CLIENT_ERROR_PATH\n const maxReports = options.maxReports ?? 50\n const seen = new Set<string>()\n let sent = 0\n\n const send = (raw: Record<string, unknown>): void => {\n const report = normalizeClientErrorReport(raw)\n if (!report) return\n const key = `${report.kind}:${report.name ?? ''}:${report.message}`\n if (seen.has(key)) return\n // Cap BEFORE recording, so `seen` can't grow past the cap on a loop that\n // throws distinct errors (network is capped either way; this bounds memory).\n if (sent >= maxReports) return\n seen.add(key)\n sent += 1\n post(endpoint, report)\n }\n\n // The field extraction itself must not throw (a hostile `reason` can have a\n // throwing `toString`/getter or a Proxy trap) — the whole handler is guarded.\n const onError = (event: ErrorEvent): void => {\n try {\n const err = event.error as Error | undefined\n send({\n kind: 'error',\n name: err?.name,\n message: err?.message ?? event.message,\n stack: err?.stack,\n url: stripQuery(event.filename || window.location?.href),\n line: event.lineno,\n col: event.colno,\n })\n } catch {\n // never throw\n }\n }\n\n const onRejection = (event: PromiseRejectionEvent): void => {\n try {\n const reason: unknown = event.reason\n const isError = reason instanceof Error\n send({\n kind: 'unhandledrejection',\n name: isError ? reason.name : undefined,\n message: isError ? reason.message : String(reason),\n stack: isError ? reason.stack : undefined,\n url: stripQuery(window.location?.href),\n })\n } catch {\n // never throw\n }\n }\n\n window.addEventListener('error', onError)\n window.addEventListener('unhandledrejection', onRejection)\n\n return () => {\n window.removeEventListener('error', onError)\n window.removeEventListener('unhandledrejection', onRejection)\n installed = false\n }\n}\n\n/**\n * Manually report a caught error — e.g. from a React error boundary's\n * `componentDidCatch`. No-op outside a browser; never throws. Unlike the\n * auto-installed handlers this has no client-side dedupe/cap — it relies on the\n * ingestion route's per-isolate throttle — so call it from boundaries (which\n * unmount rather than loop), not a hot path.\n */\nexport function reportClientError(\n error: unknown,\n options: ClientErrorReporterOptions & { componentStack?: string } = {},\n): void {\n if (typeof window === 'undefined') return\n try {\n const isError = error instanceof Error\n const report = normalizeClientErrorReport({\n kind: 'react',\n name: isError ? error.name : undefined,\n message: isError ? error.message : String(error),\n stack: (isError ? error.stack : undefined) ?? options.componentStack,\n url: stripQuery(window.location?.href),\n })\n if (!report) return\n post(options.endpoint ?? CLIENT_ERROR_PATH, report)\n } catch {\n // never throw\n }\n}\n","/**\n * `deepspace logs` wire DTO — the single source of truth for the whitelisted\n * shape the platform telemetry reader emits and every consumer reads.\n *\n * The producer is `platform/deploy-worker/src/lib/workers-logs.ts` (which\n * rebuilds each event field-by-field from a raw Cloudflare row — see the\n * security invariant there). The consumers are the CLI tail\n * (`cli/commands/logs.ts`), the dashboard Logs panel\n * (`apps/dashboard/src/lib/apps.ts`), and the e2e whitelist assertion\n * (`tests/e2e/tests/logs.spec.ts`). This module used to be hand-mirrored in\n * all four places; `source` was added to three of them and missed in the\n * e2e whitelist, so it now lives here once and they import it.\n *\n * Isomorphic — no DOM, no Hono, no platform types — so it re-exports cleanly\n * from both `deepspace` (client) and `deepspace/worker` and is safe to import\n * from the private deploy-worker.\n */\n\nexport const LOG_LEVELS = ['debug', 'log', 'info', 'warn', 'error'] as const\nexport type LogLevel = (typeof LOG_LEVELS)[number]\n\n/** One log / request / exception event, rebuilt field-by-field from a CF row. */\nexport interface AppLogEvent {\n /** `$metadata.id` — unique per event; the client's dedupe key. */\n id: string\n /** Unix ms. */\n timestamp: number\n level: LogLevel\n eventType: 'log' | 'request' | 'exception' | 'other'\n message: string\n /**\n * Present (= `'client'`) only for a browser-forwarded error the app's own\n * Worker re-logged via `registerClientErrorRoute`. Absent means the log was\n * emitted server-side — there is no explicit `'server'` value, so consumers\n * branch on `source === 'client'`, never on a truthy/other check.\n */\n source?: 'client'\n /**\n * The runtime's verdict on the invocation (`ok`, `exception`, `exceededCpu`,\n * `canceled`, …) — present on `request` and other invocation-summary events\n * only, never on a `log` line. It says whether the Worker RETURNED, not\n * whether the app succeeded: an action that throws inside the app's own\n * error handling and answers 500 is `ok` here. Triage failures on\n * `request.status`, `level`, and `eventType === 'exception'` (an exception\n * that escaped the Worker), not on `outcome !== 'ok'` alone.\n */\n outcome?: string\n request?: { method: string; path: string; status?: number }\n /**\n * The exception that ESCAPED the Worker (`eventType: 'exception'`), or a\n * browser-reported one (`source: 'client'`). An error the app catches and\n * `console.error`s is a plain `log` line — and the Workers runtime renders a\n * logged Error object as its stack frames without the message, so log the\n * message text yourself (`console.error(\\`… ${err.message}\\`, err.stack)`).\n */\n exception?: { name: string; message: string; stack?: string }\n}\n\nexport interface AppLogsResponse {\n /** Ascending by timestamp (CF returns newest-first; the reader flips). */\n events: AppLogEvent[]\n /** Max event timestamp in ms — pass back as `since` to poll; null when empty. */\n cursor: number | null\n /** True when the window returned `limit` events (older events were cut). */\n truncated: boolean\n pollIntervalMs: number\n}\n\n/**\n * Runtime tuple of every key a whitelisted `AppLogEvent` may carry — the\n * canonical whitelist the e2e suite mirrors when asserting no event leaks a key\n * outside this set. The two type-level assertions below fail compilation if\n * this tuple and the `AppLogEvent` interface disagree in either direction, so\n * adding an interface field forces a matching key here.\n *\n * NOTE: the e2e spec keeps its OWN copy of this set (it can't import the SDK —\n * `@deepspace/e2e` has no `deepspace` dependency), kept in step by a comment,\n * not by these compile-time guards. So this tuple hardens the code copies\n * (deploy-worker/CLI/dashboard, which import it), while the e2e mirror remains a\n * knowingly-accepted duplicate.\n */\nexport const APP_LOG_EVENT_KEYS = [\n 'id',\n 'timestamp',\n 'level',\n 'eventType',\n 'message',\n 'source',\n 'outcome',\n 'request',\n 'exception',\n] as const\n\nexport type AppLogEventKey = (typeof APP_LOG_EVENT_KEYS)[number]\n\n// Compile-time guard: `APP_LOG_EVENT_KEYS` and `keyof AppLogEvent` list the\n// same keys. If either drifts, one of these aliases resolves to `never` and\n// `tsc` errors on the assignment.\ntype _KeysCoverInterface = AppLogEventKey extends keyof AppLogEvent ? true : never\ntype _InterfaceCoversKeys = keyof AppLogEvent extends AppLogEventKey ? true : never\nconst _assertKeysMatch: [_KeysCoverInterface, _InterfaceCoversKeys] = [true, true]\nvoid _assertKeysMatch\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","useMemo","Y","useState","useRef","useMemo","useEffect","msgDocKey","payload","useCallback","useMemo","useCallback","useMemo","useCallback","useEffect","useCallback","useState","useState","useEffect","useCallback","u","useState","useEffect","useLayoutEffect","useCallback","useRef","useMemo","Y","useState","useMemo","useRef","useCallback","useEffect","useLayoutEffect","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","options","useCallback","useMemo","useMemo","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useEffect","r","g","b","Fragment","jsx","useEffect","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","useState","useCallback","useEffect","useMemo"]}
1
+ {"version":3,"sources":["../src/shared/env/index.ts","../src/shared/roles.ts","../src/shared/ai-models.ts","../src/shared/protocol/yjs.ts","../src/shared/protocol/constants.ts","../src/shared/protocol/messages.ts","../src/shared/protocol/app-migrations.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/ScopeRegistry.tsx","../src/client/storage/RecordScope.tsx","../src/client/storage/store.ts","../src/client/storage/record-matching.ts","../src/client/storage/errors.ts","../src/client/storage/serverErrors.ts","../src/client/storage/record-socket.ts","../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/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/shared/app-files.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/theme/DeepSpaceThemeProvider.tsx","../src/client/theme/applyTheme.ts","../src/client/theme/useIsDarkTheme.ts","../src/shared/api-error.ts","../src/client/integration.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/client/authed-json.ts","../src/shared/subscription.ts","../src/client/subscriptions/PricingTable.tsx","../src/client/charges/useCheckout.ts","../src/shared/client-errors.ts","../src/client/errors.ts","../src/shared/log-events.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' | 'staging' | '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\n// Mirrors the CLI's staging preset (cli/env.ts) and the staging wrangler\n// routes: services and the dashboard on deepspacesites.com, user apps on\n// spacestest.com. This module had NO staging tier — a staging app calling\n// getApiUrl() silently targeted production.\nconst STAGING_CONFIG: EnvironmentConfig = {\n name: 'staging',\n apiUrl: 'https://api.deepspacesites.com',\n platformWorkerUrl: 'https://platform.deepspacesites.com',\n authUrl: 'https://auth.deepspacesites.com',\n authSignInUrl: 'https://auth.deepspacesites.com/login/social',\n authSignUpUrl: 'https://auth.deepspacesites.com/login/social',\n mainAppUrl: 'https://spacestest.com',\n dashboardUrl: 'https://dashboard.deepspacesites.com',\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 === 'staging') return 'staging'\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 // Staging: apps on *.spacestest.com, services on *.deepspacesites.com.\n if (\n hostname === 'spacestest.com' ||\n hostname.endsWith('.spacestest.com') ||\n hostname === 'deepspacesites.com' ||\n hostname.endsWith('.deepspacesites.com')\n ) {\n cachedEnvironment = 'staging'\n return 'staging'\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 : env === 'staging' ? STAGING_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\n/**\n * Whether a role may write app content: member and admin can, viewer is\n * read-only. The one chokepoint for every client-side write gate — an app\n * that defines custom write-capable roles extends this predicate once. The\n * server always re-checks writes against the schema's permission table; this\n * only controls what the UI offers.\n */\nexport function isWriterRole(role: unknown): role is Exclude<Role, typeof ROLES.VIEWER> {\n return role === ROLES.ADMIN || role === ROLES.MEMBER\n}\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 * Canonical DeepSpace model catalog and agent policy.\n *\n * Provider catalogs are discovery inputs, not a safe runtime allowlist: a\n * model is promoted here only after its tool loop, transport, pricing, and\n * streaming contract have been verified end to end through the DeepSpace\n * proxy. Every client picker and server agent profile consumes this module.\n */\n\nexport type DeepSpaceAIProvider = 'anthropic' | 'openai' | 'cerebras'\nexport type DeepSpaceAgentProfileId = 'application' | 'documentation'\nexport type DeepSpaceAgentSupport = 'multi-step' | 'single-step' | 'none'\n\nexport interface DeepSpaceAIModel {\n id: string\n label: string\n provider: DeepSpaceAIProvider\n providerLabel: string\n family: string\n /** Whether the model is eligible for DeepSpace's server-owned agent loop. */\n agentSupport: DeepSpaceAgentSupport\n /** Profiles in which the model has passed the complete runtime contract. */\n agentProfiles: readonly DeepSpaceAgentProfileId[]\n /** Provider transport currently used by the DeepSpace proxy adapter. */\n transport: 'messages' | 'chat-completions'\n recommendation: 'frontier' | 'balanced' | 'fast' | 'available' | 'limited'\n note?: string\n}\n\nexport interface DeepSpaceAgentProfile {\n id: DeepSpaceAgentProfileId\n defaultModel: string\n maxSteps: number\n maxToolCalls?: number\n allowedTools: 'application-defined' | readonly string[]\n}\n\n/**\n * Versioned provenance for the provider catalogs used during the latest\n * promotion review. Keeping this in the shipped artifact makes selection\n * policy auditable without allowing a mutable upstream list to change a\n * deployed app underneath a commit.\n */\nexport const DEEPSPACE_MODEL_CATALOG_PROVENANCE = {\n version: '2026-08-04',\n verifiedAt: '2026-08-04',\n sources: {\n anthropic: 'https://platform.claude.com/docs/en/api/beta/models/list',\n openai: 'https://developers.openai.com/api/docs/models/all',\n cerebras: 'https://inference-docs.cerebras.ai/models/overview',\n },\n} as const\n\nconst MULTI_STEP_AGENT_PROFILES = ['application', 'documentation'] as const\n\ntype MultiStepModelInput = Pick<DeepSpaceAIModel, 'id' | 'label' | 'family' | 'recommendation'>\ntype MultiStepProvider = Pick<DeepSpaceAIModel, 'provider' | 'providerLabel' | 'transport'>\n\nfunction createMultiStepModelFactory<const Provider extends MultiStepProvider>(provider: Provider) {\n return <const Model extends MultiStepModelInput>(model: Model) => ({\n ...model,\n ...provider,\n agentSupport: 'multi-step' as const,\n agentProfiles: MULTI_STEP_AGENT_PROFILES,\n })\n}\n\nconst anthropicModel = createMultiStepModelFactory({\n provider: 'anthropic',\n providerLabel: 'Anthropic',\n transport: 'messages',\n})\n\nconst openAIModel = createMultiStepModelFactory({\n provider: 'openai',\n providerLabel: 'OpenAI',\n transport: 'chat-completions',\n})\n\nexport const DEEPSPACE_AI_MODELS = [\n anthropicModel({\n id: 'claude-fable-5',\n label: 'Claude Fable 5',\n family: 'Claude 5',\n recommendation: 'frontier',\n }),\n anthropicModel({\n id: 'claude-opus-5',\n label: 'Claude Opus 5',\n family: 'Claude 5',\n recommendation: 'frontier',\n }),\n anthropicModel({\n id: 'claude-sonnet-5',\n label: 'Claude Sonnet 5',\n family: 'Claude 5',\n recommendation: 'balanced',\n }),\n anthropicModel({\n id: 'claude-haiku-4-5',\n label: 'Claude Haiku 4.5',\n family: 'Claude 4.5',\n recommendation: 'fast',\n }),\n openAIModel({\n id: 'gpt-5.6-sol',\n label: 'GPT-5.6 Sol',\n family: 'GPT-5.6',\n recommendation: 'frontier',\n }),\n openAIModel({\n id: 'gpt-5.6-terra',\n label: 'GPT-5.6 Terra',\n family: 'GPT-5.6',\n recommendation: 'balanced',\n }),\n openAIModel({\n id: 'gpt-5.6-luna',\n label: 'GPT-5.6 Luna',\n family: 'GPT-5.6',\n recommendation: 'fast',\n }),\n {\n id: 'gpt-oss-120b',\n label: 'GPT-OSS 120B',\n provider: 'cerebras',\n providerLabel: 'Cerebras',\n family: 'GPT-OSS',\n agentSupport: 'single-step',\n agentProfiles: [],\n transport: 'chat-completions',\n recommendation: 'limited',\n note: 'Available for direct generation; the current proxy adapter has not passed multi-step tool-result continuation.',\n },\n] as const satisfies readonly DeepSpaceAIModel[]\n\nexport const DEEPSPACE_AI_DEFAULTS = {\n agent: 'claude-sonnet-5',\n directGeneration: 'claude-sonnet-5',\n summarization: 'claude-haiku-4-5',\n} as const satisfies Record<string, DeepSpaceAIModelId>\n\nexport const DEEPSPACE_AGENT_PROFILES = {\n application: {\n id: 'application',\n defaultModel: DEEPSPACE_AI_DEFAULTS.agent,\n maxSteps: 21,\n maxToolCalls: 20,\n allowedTools: 'application-defined',\n },\n documentation: {\n id: 'documentation',\n defaultModel: DEEPSPACE_AI_DEFAULTS.agent,\n // Permit up to twenty completed read-only tools plus one final synthesis\n // step. The runner disables tools once the tool-call budget is exhausted.\n maxSteps: 21,\n maxToolCalls: 20,\n allowedTools: ['documentation_search', 'documentation_read'],\n },\n} as const satisfies Record<DeepSpaceAgentProfileId, DeepSpaceAgentProfile>\n\nexport type DeepSpaceAIModelId = (typeof DEEPSPACE_AI_MODELS)[number]['id']\n\nexport function getDeepSpaceAIModel(modelId: unknown): DeepSpaceAIModel | null {\n if (typeof modelId !== 'string') return null\n return DEEPSPACE_AI_MODELS.find((model) => model.id === modelId) ?? null\n}\n\nexport function listDeepSpaceAgentModels(\n profileId: DeepSpaceAgentProfileId = 'application',\n): readonly DeepSpaceAIModel[] {\n const defaultModel = DEEPSPACE_AGENT_PROFILES[profileId].defaultModel\n return DEEPSPACE_AI_MODELS.filter((model) =>\n (model.agentProfiles as readonly DeepSpaceAgentProfileId[]).includes(profileId),\n ).sort((left, right) => Number(right.id === defaultModel) - Number(left.id === defaultModel))\n}\n\nexport interface ResolvedDeepSpaceAgentModel {\n modelId: string\n provider: DeepSpaceAIProvider\n model: DeepSpaceAIModel\n profile: DeepSpaceAgentProfile\n}\n\nexport function resolveDeepSpaceAgentModel(\n modelId: unknown,\n profileId: DeepSpaceAgentProfileId = 'application',\n): ResolvedDeepSpaceAgentModel | null {\n const profile = DEEPSPACE_AGENT_PROFILES[profileId]\n const resolvedId = modelId === undefined ? profile.defaultModel : modelId\n const model = getDeepSpaceAIModel(resolvedId)\n if (!model || !model.agentProfiles.includes(profileId)) return null\n return { modelId: model.id, provider: model.provider, model, profile }\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 /** Release all state and listeners owned by this awareness instance. */\n destroy(): void {\n this.setLocalState(null)\n this.states.clear()\n this.meta.clear()\n this.updateListeners.clear()\n this.changeListeners.clear()\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 * Wire protocol constants.\n *\n * The JSON WebSocket protocol uses dotted string identifiers (e.g.\n * `\"records.query\"`) 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 * })\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, 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 // ---- 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 // Per-mutation receipt, sent only to the socket whose trigger/pause/\n // resume carried a `requestId` — frames without one stay fire-and-forget.\n CRON_ACK: 'cron.ack',\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} 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/** Prefix `base-room` mints anonymous socket ids with. */\nexport const ANONYMOUS_USER_ID_PREFIX = 'anon-'\n/** The one test for an anonymous socket identity — `ROLE_ANONYMOUS` cannot be\n * it, since authenticated users can legitimately hold the same role name. */\nexport function isAnonymousUserId(userId: string): boolean {\n return userId.startsWith(ANONYMOUS_USER_ID_PREFIX)\n}\n/** Default role for newly registered authenticated users */\nexport const ROLE_DEFAULT = 'member'\n/** Admin role */\nexport const ROLE_ADMIN = 'admin'\n\n// ---------------------------------------------------------------------------\n// Error strings that cross a room boundary and are matched by the caller\n// ---------------------------------------------------------------------------\n\n/**\n * Reported when a record id resolves to nothing.\n *\n * This crosses the DO boundary as a response body and callers branch on it,\n * so it is a contract rather than prose. `getChat` in particular treats it as\n * \"no such chat\" and returns null — the same answer it gives for a chat owned\n * by someone else, which is what stops chat ids being enumerable. If a room\n * reworded this independently, a miss would start throwing while a cross-user\n * hit kept returning null, and that difference is an existence oracle.\n */\nexport const RECORD_NOT_FOUND = 'Record not found'\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.canvasAdd(...)` 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.RECORDS_RESUBSCRIBE`, for example, is `{}` when the client asks to\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, canvas shapes), 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. `\"canvas.add\"`) — 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<typeof MSG.DELETE, { collection: string; recordId: string; requestId?: string }>\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 // ---- 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<typeof MSG.CANVAS_UPDATE, { shapeId: string; props: Record<string, unknown> }>\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 // Optional `requestId` opts the mutation into a `CRON_ACK` receipt;\n // frames without one keep the fire-and-forget contract.\n | BaseMessage<typeof MSG.CRON_TRIGGER, { taskName: string; requestId?: string }>\n | BaseMessage<typeof MSG.CRON_PAUSE, { taskName: string; requestId?: string }>\n | BaseMessage<typeof MSG.CRON_RESUME, { taskName: string; requestId?: 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\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<typeof MSG.QUERY_RESULT, { subscriptionId: string; records: unknown[] }>\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, Cron). Client hooks store `canWrite` in state and\n // disable write APIs when false. See useCanvas /\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 // ---- 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<typeof MSG.CANVAS_SHAPES, { shapes: unknown[]; viewports: unknown[] }>\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<typeof MSG.CANVAS_UPDATE, { shapeId: string; props: Record<string, unknown> }>\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 // `CRON_ACK` is the per-mutation receipt for a trigger/pause/resume that\n // carried a `requestId`. Triggers execute synchronously in the DO before\n // the ack is sent, so an ok trigger receipt means the run completed\n // (execution detail rides the history broadcasts). Failures carry a\n // machine-readable `reason` so clients never parse error strings —\n // `read_only` covers the role-downgrade race the client-side gate\n // cannot see.\n | BaseMessage<\n typeof MSG.CRON_ACK,\n | { requestId: string; taskName: string; ok: true }\n | {\n requestId: string\n taskName?: string\n ok: false\n reason: 'read_only' | 'unknown_task' | 'failed'\n error?: string\n }\n >\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<typeof MSG.PRESENCE_UPDATE, { userId: string; state: Record<string, 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. `\"canvas.add\"`), 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: (collection: string, recordId: string, data: Record<string, unknown>, requestId?: string) =>\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 // 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: (shapeId: string, width: number, height: number, x?: number, y?: number) =>\n ({ type: MSG.CANVAS_RESIZE, payload: { shapeId, width, height, x, y } }) as const,\n canvasDelete: (shapeId: string) => ({ 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, requestId?: string) =>\n ({ type: MSG.CRON_TRIGGER, payload: { taskName, requestId } }) as const,\n cronPause: (taskName: string, requestId?: string) =>\n ({ type: MSG.CRON_PAUSE, payload: { taskName, requestId } }) as const,\n cronResume: (taskName: string, requestId?: string) =>\n ({ type: MSG.CRON_RESUME, payload: { taskName, requestId } }) as const,\n // Jobs\n jobEnqueue: (requestId: string, type: string, payload?: unknown, maxAttempts?: number) =>\n ({\n type: MSG.JOB_ENQUEUE,\n payload: { requestId, type, payload, maxAttempts },\n }) as const,\n jobCancel: (jobId: string) => ({ type: MSG.JOB_CANCEL, payload: { jobId } }) as const,\n jobRetry: (jobId: string) => ({ 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}\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: (collection: string, record: unknown, changeType: 'create' | 'update' | 'delete') =>\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) => ({ type: MSG.LIST_SCHEMAS, payload: { schemas } }) as const,\n // Users\n userInfo: (user: unknown) => ({ type: MSG.USER_INFO, payload: user }) as const,\n userList: (users: unknown[]) => ({ 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 // Canvas\n canvasShapes: (shapes: unknown[], viewports: unknown[]) =>\n ({ type: MSG.CANVAS_SHAPES, payload: { shapes, viewports } }) as const,\n canvasAdd: (shape: unknown) => ({ 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: (shapeId: string, width: number, height: number, x?: number, y?: number) =>\n ({ type: MSG.CANVAS_RESIZE, payload: { shapeId, width, height, x, y } }) as const,\n canvasDelete: (shapeId: string) => ({ 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) => ({ type: MSG.CRON_TASKS, payload: { tasks } }) as const,\n cronHistory: (history: unknown) => ({ 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 cronAckSuccess: (requestId: string, taskName: string) =>\n ({\n type: MSG.CRON_ACK,\n payload: { requestId, taskName, ok: true as const },\n }) as const,\n cronAckFailure: (\n requestId: string,\n reason: 'read_only' | 'unknown_task' | 'failed',\n error?: string,\n taskName?: string,\n ) =>\n ({\n type: MSG.CRON_ACK,\n payload: { requestId, taskName, ok: false as const, reason, error },\n }) 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[]) => ({ type: MSG.PRESENCE_SYNC, payload: { peers } }) as const,\n presenceJoin: (peer: unknown) => ({ type: MSG.PRESENCE_JOIN, payload: { peer } }) as const,\n presenceLeave: (userId: string) => ({ 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}\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// })\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>)[msg.type]\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.canvasAdd(...)))` 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","const APP_MIGRATION_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/\nconst MAX_APP_MIGRATION_IDS = 256\n\n/** Asset-layer misses became real 404s, and the worker owns the fallback. The\n * config and the worker's fallback path have to move together. */\nexport const WORKER_OWNED_NOT_FOUND_MIGRATION_ID = '2026-08-worker-owned-not-found'\n\n/** Room identity moved to internal headers and privileged room routes gained app-role checks. */\nexport const SECURE_ROOM_BOUNDARIES_MIGRATION_ID = '2026-08-secure-room-boundaries'\n\n/** `ActionTools` gained `deleteWhere`, so the app's own tools factory — which\n * declares that interface as its return type — has to supply it. */\nexport const ACTION_TOOLS_DELETE_WHERE_MIGRATION_ID = '2026-08-action-tools-delete-where'\n\n/** The browser's app id moved off a scaffold-time literal onto a build-time\n * define that `deepspace/build` reads from the wrangler config being built. */\nexport const BUILD_INJECTED_APP_ID_MIGRATION_ID = '2026-08-build-injected-app-id'\n\n/** The action route refuses a call without a bearer token as 401 (a cookie\n * session has none) instead of crashing on a non-null assertion, and the\n * file states the trust model actions run under. */\nexport const ACTION_ROUTES_BEARER_GUARD_MIGRATION_ID = '2026-08-action-routes-bearer-guard'\n\nexport type AppMigrationIdsValidation =\n | { valid: true; ids: string[] }\n | { valid: false; reason: string }\n\n/** Validate the small, provider-neutral migration manifest shipped with an app bundle. */\nexport function validateAppMigrationIds(value: unknown): AppMigrationIdsValidation {\n if (!Array.isArray(value)) {\n return { valid: false, reason: 'app migrations must be a JSON string array' }\n }\n if (value.length > MAX_APP_MIGRATION_IDS) {\n return {\n valid: false,\n reason: `app migrations cannot contain more than ${MAX_APP_MIGRATION_IDS} entries`,\n }\n }\n\n const ids: string[] = []\n const seen = new Set<string>()\n for (const valueId of value) {\n if (typeof valueId !== 'string' || !APP_MIGRATION_ID_RE.test(valueId)) {\n return { valid: false, reason: `invalid app migration id: ${String(valueId)}` }\n }\n if (seen.has(valueId)) {\n return { valid: false, reason: `duplicate app migration id: ${valueId}` }\n }\n seen.add(valueId)\n ids.push(valueId)\n }\n return { valid: true, ids }\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 start` 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 * Hosts can brand the card via `title` / `description` / `logo` and re-tint\n * the backdrop by defining `--ds-auth-backdrop` (defaults to black/55).\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 /** Card heading. Defaults to \"Sign in to DeepSpace\". */\n title?: string\n /** Line under the heading. Defaults to \"Sync your data across devices\". */\n description?: string\n /** Optional brand mark rendered above the heading. Defaults to none (the current look). */\n logo?: React.ReactNode\n}\n\nexport function AuthOverlay({\n onClose,\n providers = ['github', 'google'],\n title = 'Sign in to DeepSpace',\n description = 'Sync your data across devices',\n logo,\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-[var(--ds-auth-backdrop,#0000008c)] 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 {logo && <div className=\"mb-4 flex justify-center\">{logo}</div>}\n\n <h2 className=\"mb-2 text-[22px] font-bold leading-snug text-foreground\">\n {title}\n </h2>\n\n <p className=\"mb-6 text-sm leading-snug text-muted-foreground\">\n {description}\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 shared authentication state and scope registration for RecordRoom.\n * RecordScope components own 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 { isLocalDevHost } from '../debug'\nimport { RecordStore } from './store'\nimport { ScopeRegistryProvider } from './ScopeRegistry'\nimport type {\n UserProfile,\n RoomUser,\n RoomConnectionState,\n RecordProviderProps,\n WriteError,\n} from './types'\n\ntype FetchUserProfile = () => Promise<UserProfile | null>\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 RecordScope')\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 /**\n * The one write-failure surface. RecordScope adapts socket rejections onto\n * it (permission/validation) and useMutations dispatches writes it refuses\n * before the room is ready (not_ready), so every rejected write reaches the\n * app's single `onWriteError` prop.\n */\n onWriteError?: (error: WriteError) => 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// Shared provider state\n// ============================================================================\n\ninterface RecordProviderStateProps {\n children: ReactNode\n fetchUser: FetchUserProfile\n allowAnonymous?: boolean\n getAuthToken?: () => Promise<string | null>\n onWriteError: (error: WriteError) => void\n}\n\nfunction RecordProviderState({\n children,\n fetchUser,\n allowAnonymous = false,\n getAuthToken: getAuthTokenProp,\n onWriteError: onWriteErrorProp,\n}: RecordProviderStateProps): React.ReactElement {\n const [userProfile, setUserProfile] = useState<UserProfile | null>(null)\n const [userProfileLoading, setUserProfileLoading] = useState(true)\n\n // Hold the latest app callback in a ref and expose a stable wrapper, so an\n // inline arrow prop doesn't churn authValue (and every RecordScope under\n // it) on each render.\n const onWriteErrorRef = useRef(onWriteErrorProp)\n onWriteErrorRef.current = onWriteErrorProp\n\n const onWriteError = useCallback((error: WriteError) => {\n onWriteErrorRef.current(error)\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 onWriteError,\n }),\n [\n userProfile,\n userProfileLoading,\n refetchUserProfile,\n allowAnonymous,\n getAuthTokenProp,\n onWriteError,\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 * Default write-error handler. Fire-and-forget 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 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 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 supplies shared authentication and scope registration.\n * Use `<RecordScope>` for every RecordRoom connection.\n *\n * @example\n * ```tsx\n * <RecordProvider>\n * <RecordScope roomId=\"app:slack-clone\" schemas={appSchemas}>\n * <RecordScope roomId={`chat:${channelId}`} schemas={messagingSchemas}>\n * <ChannelView />\n * </RecordScope>\n * </RecordScope>\n * </RecordProvider>\n * ```\n */\nexport function RecordProvider({\n children,\n allowAnonymous = false,\n getAuthToken: getAuthTokenProp,\n onWriteError = defaultOnWriteError,\n}: RecordProviderProps): 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 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 // The public callback travels down unchanged; every producer (socket\n // rejections, not-ready mutations) shapes its own WriteError. Passing the\n // raw prop is safe: RecordProviderState holds it in a ref behind a stable\n // wrapper, so its identity never reaches a dependency array.\n return (\n <RecordProviderState\n fetchUser={fetchUser}\n allowAnonymous={allowAnonymous}\n getAuthToken={getAuthTokenFn}\n onWriteError={onWriteError}\n >\n {children}\n </RecordProviderState>\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 * sharedScopes={[\n * { roomId: 'chat:general', schemas: messagingSchemas },\n * ]}\n * >\n * <App />\n * </RecordScope>\n * </RecordProvider>\n * ```\n */\n\nimport { 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 { RecordRoomNotReadyError } from './errors'\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// 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 /** 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 wsUrl,\n wsPathPrefix,\n}: {\n roomId: string\n schemas: CollectionSchema[]\n wsUrl?: string\n wsPathPrefix?: string\n}) {\n return (\n <ScopeConnection roomId={roomId} schemas={schemas} wsUrl={wsUrl} wsPathPrefix={wsPathPrefix} />\n )\n}\n\n// ============================================================================\n// Core WebSocket connection logic (shared by primary + headless scopes)\n// ============================================================================\n\nfunction ScopeConnection({\n roomId,\n schemas,\n children,\n wsUrl,\n wsPathPrefix = '/ws',\n isolated = false,\n}: {\n roomId: string\n schemas: CollectionSchema[]\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 onWriteErrorRef = useRef(auth?.onWriteError)\n onWriteErrorRef.current = auth?.onWriteError\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, 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 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 onWriteErrorRef.current?.({ kind: 'permission', title, detail }),\n onValidationError: (title, detail) =>\n onWriteErrorRef.current?.({ kind: 'validation', 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, 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 RecordRoomNotReadyError())\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 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 wsUrl={wsUrl}\n wsPathPrefix={wsPathPrefix}\n />\n ))}\n <ScopeConnection\n roomId={roomId}\n schemas={schemas}\n wsUrl={wsUrl}\n wsPathPrefix={wsPathPrefix}\n isolated={isolated}\n >\n {children}\n </ScopeConnection>\n </>\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","/** A write was attempted before its RecordRoom connection became ready. */\nexport class RecordRoomNotReadyError extends Error {\n readonly code = 'not_ready'\n\n constructor(collection?: string) {\n super(\n collection\n ? `RecordRoom is not ready for \"${collection}\" writes.`\n : 'RecordRoom is not ready for writes.',\n )\n this.name = 'RecordRoomNotReadyError'\n }\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 // The shape `checkFieldPermissions` actually emits for a `writableFields`\n // refusal. This branch spelled it \"Cannot update field '<f>' … role '<r>'\",\n // a sentence the server has never sent, so every one of those refusals fell\n // through to the fieldless \"Field not editable\" below.\n const fieldMatch = error.match(/^FIELD ERROR: Role '(\\w+)' cannot modify field '(\\w+)'/)\n if (fieldMatch) {\n return {\n title: `${humanizeRole(fieldMatch[1])}s can't edit \"${fieldMatch[2]}\"`,\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 * used by 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 { RecordRoomNotReadyError } from './errors'\nimport { parseServerError } from './serverErrors'\nimport type { CollectionSchema, Query } 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/**\n * Liveness probe interval and response deadline. Ordinary quiet sockets send\n * a probe after 15 seconds and close only if that probe is unanswered for a\n * further 30 seconds.\n *\n * A peer that stops answering without closing leaves the connection\n * ESTABLISHED: no packets are lost, so nothing retransmits, so nothing ever\n * times out. Measured against a peer frozen mid-conversation, an unprobed\n * socket stayed open indefinitely — this is unbounded, not slow. That is a hung\n * Durable Object, a broken relay, or a blackholed path; `offline` does not fire\n * for any of them, because the machine's own network is fine.\n *\n * The probe costs nothing on the server: BaseRoom registers a\n * `WebSocketRequestResponsePair('ping','pong')`, so the Cloudflare runtime\n * answers it while the DO stays hibernated.\n */\nconst PING_EVERY_MS = 15_000\nconst PONG_TIMEOUT_MS = 30_000\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(\n queryKey: string,\n record: RecordData,\n changeType: 'create' | 'update' | 'delete',\n ): 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 for scopes that support schema discovery. */\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 /** 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 private heartbeat: ReturnType<typeof setInterval> | null = null\n /** Any inbound frame proves the peer is answering. */\n private lastInboundAt = 0\n /** Monotonic time of the one probe awaiting an inbound answer. */\n private outstandingPingAt: number | null = null\n /** Monotonic time of the prior heartbeat callback. A long gap means the\n * scheduler was suspended, so a pre-suspension probe cannot be judged. */\n private lastHeartbeatCheckAt = 0\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 // 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 this.startHeartbeat(ws)\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 // Code-agnostic on purpose: EVERY close reconnects, including clean/1000\n // and server-initiated 1012 ('state-refresh', sent by\n // BaseRoom.disconnectAllSockets after an out-of-band server-side write).\n // We must NOT special-case \"clean\" closes as terminal — that would strand\n // a client on stale data after the server kicked it to force a resync.\n // The reconnect's onopen re-subscribes every active query, so fresh\n // QUERY_RESULTs replace the stale store contents (full resync).\n ws.onclose = () => {\n this.stopHeartbeat()\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 /**\n * Turn a peer that stopped answering into a `close` event. That is the whole\n * job — `onclose` already reports 'disconnected', marks every query stale,\n * and reconnects with backoff. Without this it is never called, because\n * nothing about the connection is actually broken.\n */\n private startHeartbeat(ws: WebSocket): void {\n this.stopHeartbeat()\n this.lastInboundAt = performance.now()\n this.lastHeartbeatCheckAt = this.lastInboundAt\n this.outstandingPingAt = null\n this.heartbeat = setInterval(() => {\n if (ws.readyState !== WS_OPEN) return\n const now = performance.now()\n const callbackGap = now - this.lastHeartbeatCheckAt\n this.lastHeartbeatCheckAt = now\n if (this.outstandingPingAt !== null) {\n if (now - this.outstandingPingAt >= PONG_TIMEOUT_MS) {\n // The event loop can freeze after the runtime queued a pong but\n // before our message task runs. A heartbeat callback delayed by a\n // full response window cannot safely judge that old probe: send a\n // fresh one and give it the ordinary deadline after the scheduler\n // resumes. Normal 15-second callbacks still close a silent peer at\n // the original deadline.\n if (callbackGap >= PONG_TIMEOUT_MS) {\n ws.send('ping')\n this.outstandingPingAt = now\n } else {\n ws.close()\n }\n }\n return\n }\n // Only probe once the connection has actually gone quiet: an app\n // receiving live updates has already proved the peer is answering.\n if (now - this.lastInboundAt >= PING_EVERY_MS) {\n ws.send('ping')\n this.outstandingPingAt = now\n }\n }, PING_EVERY_MS)\n // A suspended timer's first resumed callback has no outstanding probe, so\n // it sends one instead of closing a healthy backgrounded connection.\n }\n\n private stopHeartbeat(): void {\n if (this.heartbeat) clearInterval(this.heartbeat)\n this.heartbeat = null\n this.outstandingPingAt = null\n this.lastHeartbeatCheckAt = 0\n }\n\n /** Close without firing onclose — prevents the zombie-reconnect. */\n private teardownSocket(): void {\n this.stopHeartbeat()\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 RecordRoomNotReadyError())\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 this.lastInboundAt = performance.now()\n this.outstandingPingAt = null\n // The runtime's auto-response to our probe. It carries no payload — its\n // only meaning is the timestamp above — and it is not JSON, so it must\n // return before the parse below.\n if (event.data === 'pong') return\n\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 [subscriptionId, queryKey] of this.subscriptions) {\n try {\n const query = JSON.parse(queryKey) as Query\n if (query.collection !== collection) continue\n if (query.orderBy !== undefined || query.limit !== undefined) {\n this.sendMessage({\n type: MSG.SUBSCRIBE,\n payload: { subscriptionId, query },\n })\n continue\n }\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 * 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 uses the nearest RecordScope context during registry transitions.\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 // Resolve through the registry and retain the nearest scope during navigation.\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 this hook in a RecordScope that registers the 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 uses the nearest RecordScope context during registry transitions.\n */\n\nimport { useCallback, useContext, useMemo } from 'react'\nimport { RecordContext, useRecordAuth } from '../context'\nimport { useScopeRegistry } from '../ScopeRegistry'\nimport { RecordRoomNotReadyError } from '../errors'\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 /** True once the collection's RecordRoom can accept writes. Disable the\n * control on it (`disabled={!ready}`) rather than early-returning from\n * the handler: an early return drops the user's write silently, while a\n * write attempted before `ready` at least raises a `not_ready` error. */\n ready: boolean\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 // Resolve through the registry and retain the nearest scope during navigation.\n const registry = useScopeRegistry()\n const recordCtx = useContext(RecordContext)\n // Stable wrapper from RecordProvider — safe in a dependency array.\n const onWriteError = useRecordAuth()?.onWriteError\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 const ready = preferLocal ? recordCtx!.ready : (scopeEntry?.ready ?? recordCtx?.ready ?? false)\n\n if (!sendMessage || !sendConfirmed) {\n throw new Error(\n `useMutations('${collection}'): No scope found. Wrap this hook in a RecordScope that registers the collection.`,\n )\n }\n\n // The single readiness gate for every mutation below. A write refused here\n // never touches the socket, so `onWriteError` — the one surface an app can\n // observe a rejected fire-and-forget write on — is the only place it can show\n // up; it reports *and* throws, so callers that await keep their contract\n // and callers that don't still get a visible failure instead of silence.\n const assertReady = useCallback(() => {\n if (ready) return\n onWriteError?.({\n kind: 'not_ready',\n title: 'Not saved — still connecting',\n detail:\n `The room backing \"${collection}\" was not ready to accept writes yet, so the change was dropped. ` +\n `Gate the action on the \\`ready\\` flag from useMutations('${collection}') (e.g. \\`disabled={!ready}\\`) and retry once it is true.`,\n })\n throw new RecordRoomNotReadyError(collection)\n }, [ready, collection, onWriteError])\n\n const create = useCallback(\n async (data: T): Promise<string> => {\n assertReady()\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 [assertReady, sendMessage, collection],\n )\n\n const put = useCallback(\n async (recordId: string, data: Partial<T>): Promise<void> => {\n assertReady()\n sendMessage({ type: MSG.PUT, payload: { collection, recordId, data } })\n },\n [assertReady, sendMessage, collection],\n )\n\n const remove = useCallback(\n async (recordId: string): Promise<void> => {\n assertReady()\n sendMessage({ type: MSG.DELETE, payload: { collection, recordId } })\n },\n [assertReady, sendMessage, collection],\n )\n\n const createConfirmed = useCallback(\n async (data: T): Promise<string> => {\n assertReady()\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 [assertReady, sendConfirmed, collection],\n )\n\n const putConfirmed = useCallback(\n async (recordId: string, data: Partial<T>): Promise<void> => {\n assertReady()\n await sendConfirmed({\n type: MSG.PUT,\n payload: { collection, recordId, data: data as Record<string, unknown> },\n })\n },\n [assertReady, sendConfirmed, collection],\n )\n\n const removeConfirmed = useCallback(\n async (recordId: string): Promise<void> => {\n assertReady()\n await sendConfirmed({ type: MSG.DELETE, payload: { collection, recordId } })\n },\n [assertReady, 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 () => ({ ready, create, put, remove, createConfirmed, putConfirmed, removeConfirmed }),\n [ready, 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, useLayoutEffect, useCallback, useRef, useMemo } 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 const docKey = `${collection}:${recordId}:${fieldName}`\n\n const { doc, awareness } = useMemo(() => {\n const nextDoc = new Y.Doc()\n return { doc: nextDoc, awareness: new Awareness(nextDoc) }\n // The wire-level field key is the Yjs document identity. Reusing either\n // resource across keys can answer B's sync handshake with A's state.\n // eslint-disable-next-line react-hooks/exhaustive-deps -- docKey is the Yjs document identity\n }, [docKey])\n\n useEffect(() => {\n return () => {\n awareness.destroy()\n doc.destroy()\n }\n }, [awareness, doc])\n\n useLayoutEffect(() => {\n setSynced(false)\n setCanWrite(false)\n setUpdateCount(0)\n canWriteRef.current = false\n }, [docKey])\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 yText = useMemo(() => doc.getText(fieldName), [doc, fieldName])\n const [textState, setTextState] = useState(() => ({ doc, value: '' }))\n const text = textState.doc === doc ? textState.value : ''\n\n useEffect(() => {\n if (!synced) return\n const value = yText.toString()\n setTextState((current) =>\n current.doc === doc && current.value === value ? current : { doc, value },\n )\n }, [doc, yText, synced, updateCount])\n\n const setText = useCallback(\n (value: string) => {\n setTextState((current) =>\n current.doc === doc && current.value === value ? current : { doc, value },\n )\n\n if (canWrite) {\n doc.transact(() => {\n yText.delete(0, yText.length)\n yText.insert(0, value)\n })\n }\n },\n [doc, yText, 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 /** Available to admins; ordinary collaborators receive public identity only. */\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, connected, synced, canWrite } = useYjsRoom(docId, 'content')\n */\n\nimport { useState, useEffect, useLayoutEffect, 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\nconst UPDATE_BATCH_DELAY_MS = 16\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 the current WebSocket transport is open */\n connected: boolean\n /** Whether initial sync is complete */\n synced: boolean\n /** Whether user has write access */\n canWrite: boolean\n /** Whether the server has resolved this connection's write permission */\n writeAuthResolved: 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 [connected, setConnected] = useState(false)\n const [syncedDocId, setSyncedDocId] = useState<string | null>(null)\n const [canWrite, setCanWrite] = useState(false)\n const [writeAuthResolved, setWriteAuthResolved] = useState(false)\n const [text, setTextState] = useState('')\n const [, setUpdateCount] = useState(0)\n const synced = syncedDocId === docId\n\n const { doc, awareness } = useMemo(() => {\n const nextDoc = new Y.Doc()\n return { doc: nextDoc, awareness: new Awareness(nextDoc) }\n // docId deliberately owns this resource pair. Reusing either resource\n // across room ids can answer B's sync handshake with A's document state.\n // eslint-disable-next-line react-hooks/exhaustive-deps -- docId is the Yjs document identity\n }, [docId])\n\n const wsRef = useRef<WebSocket | null>(null)\n const isLocalRef = useRef(false)\n const applyingRemoteAwarenessRef = useRef(false)\n const pendingUpdateRef = useRef<Uint8Array | null>(null)\n const updateBatchTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n const yText = useMemo(() => doc.getText(fieldName), [doc, fieldName])\n\n const flushPendingUpdate = useCallback((socket = wsRef.current) => {\n if (updateBatchTimerRef.current) clearTimeout(updateBatchTimerRef.current)\n updateBatchTimerRef.current = null\n const update = pendingUpdateRef.current\n pendingUpdateRef.current = null\n if (!update || !socket || socket.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 socket.send(toUint8Array(enc).buffer)\n }, [])\n\n useEffect(() => {\n return () => {\n awareness.destroy()\n doc.destroy()\n }\n }, [awareness, doc])\n\n useLayoutEffect(() => {\n setConnected(false)\n setSyncedDocId(null)\n setCanWrite(false)\n setWriteAuthResolved(false)\n setTextState('')\n }, [docId])\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 let browserOnline = typeof navigator === 'undefined' || navigator.onLine\n let connecting = false\n\n const renewLocalAwareness = () => {\n const localState = awareness.getLocalState()\n if (localState) awareness.setLocalState(localState)\n }\n\n const connect = async () => {\n if (!alive || !browserOnline) return\n if (connecting) return\n if (ws?.readyState === WebSocket.CONNECTING || ws?.readyState === WebSocket.OPEN) return\n connecting = true\n\n // Permissions belong to one socket handshake. Every replacement socket\n // starts closed, including reconnects and same-room field changes.\n setCanWrite(false)\n setWriteAuthResolved(false)\n\n // The server's disconnect tombstone advances the old awareness clock\n // once. Renew while offline and again on open so the one transmitted\n // replay is strictly newer and peers accept it immediately.\n renewLocalAwareness()\n\n let token: string | null\n try {\n token = await getAuthToken()\n } finally {\n connecting = false\n }\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 || !browserOnline) 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 const socket = new WebSocket(url.toString())\n ws = socket\n socket.binaryType = 'arraybuffer'\n wsRef.current = socket\n\n socket.onopen = () => {\n if (ws !== socket) return socket.close()\n wsLog('connected', `yjs:${docId}`)\n setConnected(true)\n setSyncedDocId(null)\n renewLocalAwareness()\n }\n\n socket.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') {\n setCanWrite(msg.canWrite)\n setWriteAuthResolved(true)\n }\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 socket.send(toUint8Array(enc).buffer)\n setSyncedDocId(docId)\n break\n }\n case MSG_SYNC_STEP2: {\n Y.applyUpdate(doc, payload, 'server')\n setTextState(yText.toString())\n setSyncedDocId(docId)\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 } catch {\n // Awareness is ephemeral. Ignore a malformed peer frame without\n // breaking subsequent document sync messages on this socket.\n } finally {\n applyingRemoteAwarenessRef.current = false\n }\n }\n }\n\n socket.onclose = () => {\n if (ws !== socket) return\n wsLog('disconnected', `yjs:${docId}`)\n ws = null\n wsRef.current = null\n setConnected(false)\n setSyncedDocId(null)\n setCanWrite(false)\n setWriteAuthResolved(false)\n if (alive && browserOnline) reconnectTimer = setTimeout(connect, 1000)\n }\n\n socket.onerror = () => socket.close()\n }\n\n const handleOffline = () => {\n browserOnline = false\n setConnected(false)\n setSyncedDocId(null)\n setCanWrite(false)\n setWriteAuthResolved(false)\n ws?.close()\n }\n const handleOnline = () => {\n browserOnline = true\n void connect()\n }\n\n window.addEventListener('offline', handleOffline)\n window.addEventListener('online', handleOnline)\n void connect()\n\n return () => {\n wsLog('closing', `yjs:${docId}`)\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n window.removeEventListener('offline', handleOffline)\n window.removeEventListener('online', handleOnline)\n // This effect owns the socket, so it must drain a pending local batch\n // before closing it. A later effect cleanup cannot safely do so.\n flushPendingUpdate(ws)\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, flushPendingUpdate, 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 pendingUpdateRef.current = pendingUpdateRef.current\n ? Y.mergeUpdates([pendingUpdateRef.current, update])\n : update\n updateBatchTimerRef.current ??= setTimeout(flushPendingUpdate, UPDATE_BATCH_DELAY_MS)\n }\n\n doc.on('update', handler)\n return () => {\n doc.off('update', handler)\n flushPendingUpdate()\n }\n }, [doc, flushPendingUpdate])\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, connected, synced, canWrite, writeAuthResolved }\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 * async function onRunNow() {\n * const receipt = await trigger('daily-digest')\n * if (!receipt.ok) toast(receipt.error ?? receipt.reason)\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\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\n/**\n * Outcome of a cron mutation (trigger / pause / resume). Failures are\n * typed by origin: `read_only` and `not_connected` short-circuit locally\n * without sending a frame; `unknown_task` and `failed` come back in the\n * server's CRON_ACK receipt. Triggers execute synchronously in the DO\n * before the receipt is sent, so an ok trigger result means the run\n * completed — its execution record (duration, errors) arrives via\n * `history`.\n */\nexport type CronMutationResult =\n | { ok: true; taskName: string; requestId: string }\n | {\n ok: false\n reason: 'read_only' | 'not_connected' | 'unknown_task' | 'failed'\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 resolve `{ ok: false, reason: 'read_only' }`\n * and UIs should disable those controls. Reading tasks/history stays\n * available either way.\n */\n canWrite: boolean\n /**\n * Most recent ERROR frame from the room (e.g. a write rejected by a\n * concurrent role change). Null until one arrives.\n */\n lastError: string | null\n /** Manually trigger a task; resolves once the run completes. */\n trigger: (taskName: string) => Promise<CronMutationResult>\n /** Pause a task; resolves once the server applies it. */\n pause: (taskName: string) => Promise<CronMutationResult>\n /** Resume a paused task; resolves once the server applies it. */\n resume: (taskName: string) => Promise<CronMutationResult>\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 [lastError, setLastError] = useState<string | null>(null)\n const wsRef = useRef<WebSocket | null>(null)\n /**\n * Pending mutation resolvers keyed by requestId. No per-entry timeout:\n * triggers run to completion inline in the DO, so a long task's receipt\n * is legitimately slow; the socket-close drain below settles anything\n * the server can no longer answer.\n */\n const pendingRef = useRef<Map<string, (result: CronMutationResult) => void>>(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 drainPending = (result: CronMutationResult) => {\n for (const resolve of pendingRef.current.values()) resolve(result)\n pendingRef.current.clear()\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/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 [MSG.CRON_ACK]: (p) => {\n const resolve = pendingRef.current.get(p.requestId)\n if (!resolve) return\n pendingRef.current.delete(p.requestId)\n resolve(\n p.ok\n ? { ok: true, taskName: p.taskName, requestId: p.requestId }\n : { ok: false, reason: p.reason, error: p.error },\n )\n },\n [MSG.ERROR]: (p) => {\n setLastError(p.error)\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 drainPending({ ok: false, reason: 'not_connected' })\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 drainPending({ ok: false, reason: 'not_connected' })\n ws?.close()\n wsRef.current = null\n }\n }, [roomId])\n\n // Local write-gate. Mirrors the server check in CronRoom.onMessage so a\n // viewer's click resolves `read_only` locally instead of round-tripping\n // to ERROR. Connected sends register a resolver under a fresh requestId;\n // the room's CRON_ACK receipt settles the promise.\n const sendWrite = useCallback(\n <M extends { type: string; payload: unknown }>(\n build: (requestId: string) => M,\n ): Promise<CronMutationResult> => {\n // Each new mutation starts with a clean slate so a surfaced ERROR\n // always belongs to the latest attempt, not a stale earlier one.\n setLastError(null)\n if (!canWrite) return Promise.resolve({ ok: false, reason: 'read_only' })\n const ws = wsRef.current\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n return Promise.resolve({ ok: false, reason: 'not_connected' })\n }\n return new Promise((resolve) => {\n const requestId = crypto.randomUUID()\n pendingRef.current.set(requestId, resolve)\n // ws.send can throw synchronously if the socket transitions between\n // the readyState check and the call; without this guard the pending\n // entry would only settle on the next socket close.\n try {\n ws.send(encode(build(requestId)))\n } catch (e) {\n pendingRef.current.delete(requestId)\n resolve({\n ok: false,\n reason: 'not_connected',\n error: e instanceof Error ? e.message : String(e),\n })\n }\n })\n },\n [canWrite],\n )\n\n const trigger = useCallback(\n (taskName: string) => sendWrite((requestId) => clientBuild.cronTrigger(taskName, requestId)),\n [sendWrite],\n )\n const pause = useCallback(\n (taskName: string) => sendWrite((requestId) => clientBuild.cronPause(taskName, requestId)),\n [sendWrite],\n )\n const resume = useCallback(\n (taskName: string) => sendWrite((requestId) => clientBuild.cronResume(taskName, requestId)),\n [sendWrite],\n )\n\n return { tasks, history, connected, canWrite, lastError, 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.AUTH]: (p) => {\n if (!p.canWrite) rejectAllPending(new Error('JobRoom write access denied'))\n },\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 small ephemeral 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 { useRecordAuth } from '../context'\nimport { wsLog } from '../ws-log'\nimport { MSG } from '@/shared/protocol/constants'\nimport { clientBuild, dispatch, encode, type ServerMessage } from '@/shared/protocol/messages'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface PresencePeerClient {\n userId: string\n userName: 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 auth = useRecordAuth()\n const tokenProvider = auth?.getAuthToken ?? getAuthToken\n const [peers, setPeers] = useState<PresencePeerClient[]>([])\n const [connected, setConnected] = useState(false)\n const wsRef = useRef<WebSocket | null>(null)\n\n useEffect(() => {\n setConnected(false)\n setPeers([])\n let ws: WebSocket | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n let alive = true\n let browserOnline = typeof navigator === 'undefined' || navigator.onLine\n let connecting = false\n\n const connect = async () => {\n if (!alive || !browserOnline || connecting) return\n if (ws?.readyState === WebSocket.CONNECTING || ws?.readyState === WebSocket.OPEN) return\n connecting = true\n\n let token: string | null\n try {\n token = await tokenProvider()\n } finally {\n connecting = false\n }\n if (!alive || !browserOnline) return\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 const socket = new WebSocket(url.toString())\n ws = socket\n wsRef.current = socket\n\n socket.onopen = () => {\n if (ws !== socket) return socket.close()\n wsLog('connected', `presence:${scopeId}`)\n setConnected(true)\n }\n\n socket.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 socket.onclose = () => {\n if (ws !== socket) return\n wsLog('disconnected', `presence:${scopeId}`)\n ws = null\n wsRef.current = null\n setConnected(false)\n if (alive && browserOnline) reconnectTimer = setTimeout(connect, 1000)\n }\n\n socket.onerror = () => socket.close()\n }\n\n const handleOffline = () => {\n browserOnline = false\n setConnected(false)\n ws?.close()\n }\n const handleOnline = () => {\n browserOnline = true\n void connect()\n }\n\n window.addEventListener('offline', handleOffline)\n window.addEventListener('online', handleOnline)\n void connect()\n\n return () => {\n wsLog('closing', `presence:${scopeId}`)\n alive = false\n if (reconnectTimer) clearTimeout(reconnectTimer)\n window.removeEventListener('offline', handleOffline)\n window.removeEventListener('online', handleOnline)\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, tokenProvider])\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 * Uploads pick their own transport. A small file goes in one request; a large\n * one is chunked into parts with `Blob.slice`, so peak memory is bounded by\n * the PART SIZE rather than by the file — a 1 GiB video costs one part, not a\n * gibibyte — and the file ceiling becomes a product decision rather than a\n * request-size accident. Callers do nothing differently either way.\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'\nimport {\n MAX_APP_FILE_BYTES,\n MAX_BASE64_UPLOAD_BYTES,\n UPLOAD_PART_BYTES,\n describeFilesFailure,\n encodeKeyPath,\n oversizeMessage,\n planUploadParts,\n} from '../../shared/app-files'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface R2UploadResult {\n success: boolean\n key?: string\n /**\n * `key` minus the scope prefix — the spelling that works with `list(prefix)`\n * filters and `upload(..., { key })` upserts, which never see the prefix.\n * Older servers omit it.\n */\n relativeKey?: string\n url?: string\n name?: string\n error?: string\n}\n\n/**\n * Read an upload response without assuming it is JSON.\n *\n * An over-limit upload is refused by the edge, not the app, and comes back as\n * an HTML error page — `response.json()` on that throws `Unexpected token '<'`,\n * which is what users saw instead of \"your file is too big\".\n */\n/** The one client-side size check, for both upload shapes. */\nfunction oversize(bytes: number, limit = MAX_APP_FILE_BYTES): R2UploadResult | null {\n return bytes > limit ? { success: false, error: oversizeMessage(bytes, limit) } : null\n}\n\nasync function readUploadResult(response: Response): Promise<R2UploadResult> {\n const text = await response.text()\n if (response.ok) {\n try {\n return JSON.parse(text) as R2UploadResult\n } catch {\n return { success: false, error: 'The files service returned a malformed response.' }\n }\n }\n return { success: false, error: describeFilesFailure(response.status, text) }\n}\n\nexport interface R2FileInfo {\n key: string\n /** `key` minus the scope prefix (see {@link R2UploadResult.relativeKey}). Older servers omit it. */\n relativeKey?: string\n size: number\n uploaded: string\n url: string\n originalName?: string\n uploadedBy?: string\n}\n\n/** One page of a listing: the entries plus the continuation state. */\nexport interface R2FileListPage {\n files: R2FileInfo[]\n /**\n * Opaque continuation token — pass it back as `cursor` to fetch the next\n * page. Present only while `truncated`; older servers never send one.\n */\n cursor?: string\n /** Whether entries exist past this page. */\n truncated: boolean\n}\n\n/** Told after each part lands, so a long upload can drive a progress bar. */\nexport type UploadProgress = (sent: number, total: number) => void\n\nexport interface UploadOptions {\n onProgress?: UploadProgress\n /**\n * Exact key for the file within the scope (slashes allowed), so\n * `upload(file, 'report.csv', { key: 'showcase/report.csv' })` is found by\n * `list('showcase')` and uploading to the same key again replaces the file.\n * Without it the server generates a unique key per upload — a location no\n * prefix you choose can address. `name` stays display metadata either way.\n */\n key?: string\n}\n\nexport interface UseR2FilesReturn {\n /**\n * Upload a File or Blob. Optionally provide a display name.\n *\n * A file above the single-request bound is chunked automatically and sent as\n * parts — nothing to opt into, and nothing held in memory beyond one part.\n * Pass `onProgress` to follow a large one.\n */\n upload: (file: File | Blob, name?: string, options?: UploadOptions) => Promise<R2UploadResult>\n /**\n * Upload raw base64 data.\n *\n * Single-request only: base64 has to be materialized whole to be decoded, so\n * this path keeps the one-request bound rather than the file ceiling — and\n * because the request carries the ENCODED string, its real budget is\n * `MAX_BASE64_UPLOAD_BYTES` (~18.75 MiB decoded), not the full 25 MiB. Send a\n * Blob through `upload` for anything larger; it chunks.\n */\n uploadBase64: (\n base64Data: string,\n name: string,\n mimeType?: string,\n options?: Pick<UploadOptions, 'key'>,\n ) => 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 /**\n * List files, optionally filtered by a sub-prefix within the scope.\n * `limit` caps how many entries come back; `cursor` continues from an\n * earlier page (use `listPage` to obtain one).\n *\n * Throws on a failed response — \"not authorized\" is an error, not an\n * empty account.\n */\n list: (prefix?: string, options?: { limit?: number; cursor?: string }) => Promise<R2FileInfo[]>\n /**\n * Like `list`, but returns the page envelope `list` flattens away:\n * `truncated` says whether more entries exist, and `cursor` (present only\n * while `truncated`) feeds the next call to fetch them.\n *\n * @example\n * ```tsx\n * let cursor: string | undefined\n * do {\n * const page = await listPage('showcase', { cursor })\n * render(page.files)\n * cursor = page.cursor\n * } while (cursor)\n * ```\n */\n listPage: (\n prefix?: string,\n options?: { limit?: number; cursor?: string },\n ) => Promise<R2FileListPage>\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: (\n fileOrKey: R2FileInfo | string,\n fileName?: string,\n ) => 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/** Append the caller-named upsert key, on whichever upload shape carries it. */\nfunction withUploadKey(params: string, key: string | undefined): string {\n if (!key) return params\n const pair = `key=${encodeURIComponent(key)}`\n return params ? `${params}&${pair}` : pair\n}\n\ninterface MultipartSession {\n uploadId: string\n uploadKey: string\n reservationId: string\n partSize: number\n}\n\n/**\n * Upload a file too large for one request, as a sequence of parts.\n *\n * `Blob.slice` is a lazy view rather than a copy, so the range is materialized\n * only while it is being sent: peak memory is one part, not one file, and a\n * 1 GiB video never becomes 1 GiB of tab memory. Its exact size becomes the\n * Content-Length the server requires. Parts go one at a time: they must,\n * because R2 sizes non-final parts uniformly and a half-uploaded object must be\n * abandonable in one call.\n *\n * The server names the key and confirms the fixed part size. A mismatched\n * reply means the client and server speak different protocol versions, so it\n * is refused before any content moves.\n */\nasync function uploadInParts(\n file: File | Blob,\n name: string,\n key: string | undefined,\n scopeParams: string,\n authHeaders: () => Promise<Record<string, string>>,\n onProgress?: UploadProgress,\n): Promise<R2UploadResult> {\n const headers = await authHeaders()\n const json = { ...headers, 'Content-Type': 'application/json' }\n const requestId = crypto.randomUUID()\n const initBody = JSON.stringify({\n requestId,\n name,\n mimeType: file.type || 'application/octet-stream',\n size: file.size,\n })\n // The caller-named key matters only at init: the server anchors it there\n // and answers with the session's `uploadKey`, which is what parts carry.\n const sendInit = (): Promise<Response> =>\n fetch(`/api/files/multipart?${withUploadKey(scopeParams, key)}`, {\n method: 'POST',\n headers: json,\n body: initBody,\n })\n let initResponse = await sendInit().catch(() => null)\n if (!initResponse || initResponse.status === 429 || initResponse.status >= 500) {\n initResponse = await sendInit().catch(() => null)\n }\n if (!initResponse) {\n return { success: false, error: 'The files service could not be reached.' }\n }\n if (!initResponse.ok) return await readUploadResult(initResponse)\n const session = (await initResponse.json()) as MultipartSession\n if (\n !session.uploadId ||\n !session.uploadKey ||\n !session.reservationId ||\n session.partSize !== UPLOAD_PART_BYTES\n ) {\n return {\n success: false,\n error: `The files service returned an unusable part size or multipart session (${session.partSize}).`,\n }\n }\n\n const query =\n `${scopeParams}&uploadKey=${encodeKeyPath(session.uploadKey)}` +\n `&uploadId=${encodeURIComponent(session.uploadId)}` +\n `&reservationId=${encodeURIComponent(session.reservationId)}`\n const plan = planUploadParts(file.size)\n const parts: Array<{ partNumber: number; etag: string }> = []\n let assembled = false\n try {\n for (const part of plan) {\n // The slice is rebuilt per attempt: a body is consumed once, so a retry\n // needs its own.\n const send = (): Promise<Response> =>\n fetch(`/api/files/multipart/part?${query}&partNumber=${part.partNumber}`, {\n method: 'PUT',\n headers,\n body: file.slice(part.start, part.end),\n })\n let response = await send().catch(() => null)\n // ONE retry, only for the answers the platform marks retryable —\n // otherwise a transient on part 40 of 52 throws away everything already\n // uploaded. Not a loop: a second failure is the answer.\n if (!response || response.status === 429 || response.status >= 500) {\n const retried = await send().catch(() => null)\n if (retried) response = retried\n }\n if (!response) return { success: false, error: 'The files service could not be reached.' }\n if (!response.ok) return await readUploadResult(response)\n parts.push((await response.json()) as { partNumber: number; etag: string })\n onProgress?.(part.end, file.size)\n }\n const completed = await fetch(`/api/files/multipart/complete?${query}`, {\n method: 'POST',\n headers: json,\n body: JSON.stringify({ parts }),\n })\n assembled = completed.ok\n return await readUploadResult(completed)\n } finally {\n // Anything short of an assembled object leaves parts holding space in the\n // customer's allocation. R2 would reap the session in 7 days; releasing it\n // now is the difference between a retry loop that costs nothing and one\n // that fills the app up.\n if (!assembled) {\n await fetch(`/api/files/multipart?${query}`, { method: 'DELETE', headers }).catch(\n () => undefined,\n )\n }\n }\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(\n async (file: File | Blob, name?: string, options?: UploadOptions): Promise<R2UploadResult> => {\n setIsUploading(true)\n try {\n const tooBig = oversize(file.size)\n if (tooBig) return tooBig\n const fileName = name ?? (file instanceof File ? file.name : 'upload')\n if (file.size > UPLOAD_PART_BYTES) {\n return await uploadInParts(\n file,\n fileName,\n options?.key,\n scopeParams,\n authHeaders,\n options?.onProgress,\n )\n }\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(\n `/api/files/upload?${withUploadKey(scopeParams, options?.key)}`,\n {\n method: 'POST',\n headers,\n body: formData,\n },\n )\n options?.onProgress?.(file.size, file.size)\n return await readUploadResult(response)\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : 'Upload failed' }\n } finally {\n setIsUploading(false)\n }\n },\n [scopeParams, authHeaders],\n )\n\n const uploadBase64 = useCallback(\n async (\n base64Data: string,\n name: string,\n mimeType?: string,\n options?: Pick<UploadOptions, 'key'>,\n ): Promise<R2UploadResult> => {\n setIsUploading(true)\n try {\n // The REQUEST is the encoded string, and that is what has to fit:\n // base64 spends 4 characters per 3 bytes, so measuring only the\n // decoded payload against the request bound passes files whose bodies\n // are a third larger than the server accepts — a check that green-lights\n // uploads the server then always 413s. Measured decoded, compared\n // against the decoded budget the request bound actually leaves.\n const tooBig = oversize(Math.floor((base64Data.length * 3) / 4), MAX_BASE64_UPLOAD_BYTES)\n if (tooBig) return tooBig\n const headers = await authHeaders()\n const response = await fetch(\n `/api/files/upload?${withUploadKey(scopeParams, options?.key)}`,\n {\n method: 'POST',\n headers: { ...headers, 'Content-Type': 'application/json' },\n body: JSON.stringify({ data: base64Data, name, mimeType }),\n },\n )\n return await readUploadResult(response)\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : 'Upload failed' }\n } finally {\n setIsUploading(false)\n }\n },\n [scopeParams, authHeaders],\n )\n\n const getUrl = useCallback(\n (fileOrKey: R2FileInfo | string): string => {\n const { key } = resolveFile(fileOrKey)\n return `/api/files/${encodeKeyPath(key)}?${scopeParams}`\n },\n [scopeParams],\n )\n\n const deleteFile = useCallback(\n async (fileOrKey: R2FileInfo | string): Promise<{ success: boolean; error?: string }> => {\n try {\n const headers = await authHeaders()\n const response = await fetch(getUrl(fileOrKey), {\n method: 'DELETE',\n headers,\n })\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 },\n [getUrl, authHeaders],\n )\n\n const listPage = useCallback(\n async (\n prefix?: string,\n options?: { limit?: number; cursor?: string },\n ): Promise<R2FileListPage> => {\n const headers = await authHeaders()\n const params = new URLSearchParams(scopeParams)\n if (prefix) params.set('prefix', prefix)\n if (options?.limit !== undefined) params.set('limit', String(options.limit))\n if (options?.cursor) params.set('cursor', options.cursor)\n const response = await fetch(`/api/files?${params.toString()}`, { headers })\n // A failure throws instead of flattening to [] — an expired session or\n // unreachable service used to read as \"no files\", which callers then\n // rendered as an empty account.\n if (!response.ok) {\n throw new Error(describeFilesFailure(response.status, await response.text()))\n }\n const result = (await response.json()) as {\n files?: R2FileInfo[]\n cursor?: string\n truncated?: boolean\n }\n // Older servers omit cursor/truncated; both read as \"nothing more\".\n return {\n files: result.files || [],\n cursor: result.cursor,\n truncated: result.truncated ?? false,\n }\n },\n [scopeParams, authHeaders],\n )\n\n const list = useCallback(\n async (prefix?: string, options?: { limit?: number; cursor?: string }): Promise<R2FileInfo[]> =>\n (await listPage(prefix, options)).files,\n [listPage],\n )\n\n const downloadFile = useCallback(\n async (\n fileOrKey: R2FileInfo | string,\n fileName?: string,\n ): Promise<{ success: boolean; error?: string }> => {\n const { key, name: infoName } = resolveFile(fileOrKey)\n try {\n const headers = await authHeaders()\n const response = await fetch(getUrl(key), { headers })\n\n if (!response.ok) {\n return { success: false, error: describeFilesFailure(response.status, await response.text()) }\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 },\n [getUrl, authHeaders],\n )\n\n const readFile = useCallback(\n async (fileOrKey: R2FileInfo | string): Promise<Response> => {\n const headers = await authHeaders()\n const response = await fetch(getUrl(fileOrKey), { headers })\n if (!response.ok) {\n throw new Error(describeFilesFailure(response.status, await response.text()))\n }\n return response\n },\n [getUrl, authHeaders],\n )\n\n return {\n upload,\n uploadBase64,\n deleteFile,\n downloadFile,\n readFile,\n list,\n listPage,\n getUrl,\n isUploading,\n }\n}\n","/**\n * App-files limits, chunk planning, and failure reading — shared by every\n * caller of the files API: the browser hook (`client/storage/useR2Files.ts`),\n * the CLI (`cli/lib/app-files-api.ts`), the server handler\n * (`server/utils/scoped-r2-files.ts`), and the docs that quote them.\n *\n * There are three numbers here and they answer three different questions.\n * Conflating them is what produced the old 25 MiB per-file ceiling: that was\n * never a product decision, it was the size of one HTTP request leaking out as\n * a rule about files. A request is bounded by physics — Cloudflare rejects an\n * oversized body before any worker runs, and the worker's isolate is ~128 MiB.\n * A FILE is not, once it travels as a sequence of bounded requests.\n *\n * The other half of this module is failure reading. An upload refused at the\n * edge answers with a Cloudflare HTML page, which every caller used to feed\n * straight into `JSON.parse` and report as \"Unexpected token '<' … is not\n * valid JSON\". `describeFilesFailure` exists to make that impossible.\n */\n\n/**\n * The largest single file the app-files API accepts.\n *\n * Reachable only through the multipart transport: a file above\n * {@link UPLOAD_PART_BYTES} is uploaded as a sequence of parts, so no request\n * and no isolate ever holds more than one part. Enforced by the shared handler\n * (`server/utils/scoped-r2-files.ts`) twice — once at init against the\n * declared total, so an impossible upload fails before a byte moves, and again\n * at complete against the assembled object, because the declared total is\n * client-supplied. Callers check it locally too, but only to fail early;\n * skipping the client check cannot widen what is accepted.\n */\nexport const MAX_APP_FILE_BYTES = 1024 * 1024 * 1024\n\n/**\n * The most any ONE request body may carry — a whole small file on the\n * single-request path, or one part on the multipart path.\n *\n * This is the transport's real physical bound, and the only thing 25 MiB ever\n * meant. Above it the worker risks exhausting its isolate holding the body;\n * well above it (~100 MB) Cloudflare refuses the request before the worker\n * runs at all, which is why callers must read failures as text first.\n */\nexport const MAX_UPLOAD_REQUEST_BYTES = 25 * 1024 * 1024\n\n/**\n * The part size clients chunk at, and the threshold above which they must.\n *\n * Below the {@link MAX_UPLOAD_REQUEST_BYTES} bound with headroom for the\n * request envelope, and comfortably inside R2's own part rules: parts must be\n * at least 5 MiB and every non-final part must be exactly the same size\n * (R2 errors 10011 / 10048). The server advertises this at init — a client\n * chunks at the size it is told, not at this constant, so the number can move\n * without stranding older callers mid-upload.\n */\nexport const UPLOAD_PART_BYTES = 20 * 1024 * 1024\n\n/**\n * The highest part number the server accepts.\n *\n * The server derives the only valid part number and byte length from the\n * declared total and {@link UPLOAD_PART_BYTES}. The maximum part count is\n * therefore the exact number needed to reach the file ceiling.\n *\n * R2 allows 10,000 parts, so this is far inside what the API permits.\n */\nexport const MAX_UPLOAD_PARTS = Math.ceil(MAX_APP_FILE_BYTES / UPLOAD_PART_BYTES)\n\n/**\n * The largest base64 payload the single-request JSON shape can carry.\n *\n * Derived from the ENCODED size, because that is what the request body is:\n * base64 spends 4 characters per 3 bytes, so a payload measured only by its\n * decoded size can sit under {@link MAX_UPLOAD_REQUEST_BYTES} and still\n * produce a body a third larger than the server will accept — a client check\n * that passes everything the server then refuses. Roughly 18.75 MiB.\n */\nexport const MAX_BASE64_UPLOAD_BYTES = Math.floor((MAX_UPLOAD_REQUEST_BYTES * 3) / 4)\n\n/** Byte ranges for a chunked upload: `[start, end)`, `end` exclusive. */\nexport interface UploadPart {\n partNumber: number\n start: number\n end: number\n}\n\n/**\n * Split a file into the parts the multipart transport expects.\n *\n * Every part is exactly `partSize` except the last, which is what R2 requires\n * of non-final parts. Both chunking clients plan with this, so \"which bytes\n * are part N\" is answered in one place.\n */\nexport function planUploadParts(totalBytes: number): UploadPart[] {\n const parts: UploadPart[] = []\n for (let start = 0; start < totalBytes; start += UPLOAD_PART_BYTES) {\n parts.push({\n partNumber: parts.length + 1,\n start,\n end: Math.min(start + UPLOAD_PART_BYTES, totalBytes),\n })\n }\n return parts\n}\n\nconst GIB = 1024 * 1024 * 1024\n\n/**\n * The customer-visible storage schedule, in bytes, PER ACCOUNT — the total an\n * owner's uploaded files may occupy across every app they own, not a budget\n * each app gets separately. This is the number the billing page advertises.\n *\n * Admitted by the shared files handler (`server/utils/scoped-r2-files.ts`),\n * which sums the owner's apps rather than the one being written to.\n *\n * It deliberately does NOT govern the repo store — see\n * `REPO_STORE_LIMIT_BYTES` below, and docs/platform/r2-storage.md.\n */\nexport const ACCOUNT_STORAGE_LIMIT_BYTES = {\n test: 0,\n free: 1 * GIB,\n starter: 10 * GIB,\n premium: 30 * GIB,\n admin: 100 * GIB,\n} as const\n\n/** The account storage limit for a billing tier; unknown or missing tiers get free. */\nexport function storageLimitForTier(tier: string | null | undefined): number {\n if (tier && Object.hasOwn(ACCOUNT_STORAGE_LIMIT_BYTES, tier)) {\n return ACCOUNT_STORAGE_LIMIT_BYTES[tier as keyof typeof ACCOUNT_STORAGE_LIMIT_BYTES]\n }\n return ACCOUNT_STORAGE_LIMIT_BYTES.free\n}\n\n/**\n * The repo store's budget, per app, flat across every tier.\n *\n * Git packs, retained rollback bundles, and stored deploy assets are platform\n * bookkeeping, not something a customer buys — so this is a working ceiling,\n * not an entitlement, and it is deliberately small. An app that cannot fit its\n * SOURCE plus a few rollback bundles in this has something in the wrong place:\n * user uploads belong in the app-files allocation above, which is what the\n * account schedule sizes.\n *\n * Being flat is the point. Tying it to the tier table meant every raise to the\n * customer-facing number silently multiplied platform-side storage per app,\n * and made a push depend on a billing lookup that could refuse it.\n */\nexport const REPO_STORE_LIMIT_BYTES = 256 * 1024 * 1024\n\n/** The one storage-quota refusal sentence. `usedBytes` is net of any object\n * the upload replaces, so an upsert is charged only for its growth. */\nexport function storageQuotaMessage(\n incomingBytes: number,\n usedBytes: number,\n limitBytes: number,\n): string {\n const free = Math.max(0, limitBytes - usedBytes)\n return (\n `This upload needs ${formatBytes(incomingBytes)} but the app's file storage has ` +\n `${formatBytes(free)} free (${formatBytes(usedBytes)} of ${formatBytes(limitBytes)} used). ` +\n `Delete files with \\`deepspace app files rm <key>\\`, or upgrade the owner's plan.`\n )\n}\n\n/**\n * The largest single file a deploy may ship as a static asset.\n *\n * Cloudflare Static Assets refuses anything larger, so this is a hard edge of\n * the platform rather than a policy dial — which is why the CLI may check it\n * locally. It lives here so the deploy worker and the CLI cannot drift; the\n * per-DEPLOY total is deliberately NOT here, because it is env-configurable\n * per environment and only the server can know it.\n */\nexport const MAX_DEPLOY_ASSET_FILE_BYTES = 25 * 1024 * 1024\n\n/** MIME types that must download rather than render in the app's origin. */\nexport const DANGEROUS_MIME_TYPES = new Set<string>([\n 'text/html',\n 'application/xhtml+xml',\n 'image/svg+xml',\n 'application/xml',\n 'text/xml',\n 'application/javascript',\n 'text/javascript',\n 'application/ecmascript',\n 'text/ecmascript',\n])\n\nexport function formatBytes(bytes: number): string {\n if (bytes >= 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GiB`\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`\n if (bytes >= 1024) return `${Math.round(bytes / 1024)} KiB`\n return `${bytes} B`\n}\n\n/** Encode an R2 key as URL path segments while preserving its hierarchy. */\nexport function encodeKeyPath(key: string): string {\n return key.split('/').map(encodeURIComponent).join('/')\n}\n\n/** The one oversize sentence, for whichever caller notices first. */\nexport function oversizeMessage(bytes: number, limit = MAX_APP_FILE_BYTES): string {\n return `That file is ${formatBytes(bytes)}; the limit is ${formatBytes(limit)} per file.`\n}\n\n/**\n * The one over-the-request-bound sentence. Distinct from\n * {@link oversizeMessage} because it names a fixable mistake: the file is fine,\n * the caller just sent it in one piece instead of in parts.\n */\nexport function oversizeRequestMessage(bytes: number): string {\n return (\n `This request carries ${formatBytes(bytes)}; one request may carry ` +\n `${formatBytes(MAX_UPLOAD_REQUEST_BYTES)}. Files above ` +\n `${formatBytes(UPLOAD_PART_BYTES)} upload in parts.`\n )\n}\n\n/** A part is bounded tighter than a request; say which number was crossed. */\nexport function oversizePartMessage(bytes: number): string {\n return (\n `This part carries ${formatBytes(bytes)}; a part may carry ` +\n `${formatBytes(UPLOAD_PART_BYTES)} — the size the upload advertised.`\n )\n}\n\n/**\n * Turn a failed files-API response into a sentence worth showing.\n *\n * `body` is the raw response text. A JSON `{ error }` is the app's own\n * refusal and is passed through; anything else came from the edge (an HTML\n * error page, an empty body, a proxy) and is described by its status instead\n * of being echoed, because echoing it is what produced the JSON-parse noise.\n */\nexport function describeFilesFailure(status: number, body: string): string {\n const trimmed = body.trim()\n if (trimmed.startsWith('{')) {\n try {\n const parsed = JSON.parse(trimmed) as { error?: string }\n if (parsed.error) return parsed.error\n } catch {\n // Not the app's JSON after all — fall through to the status description.\n }\n }\n if (status === 413) {\n return (\n `The upload is too large. Files are limited to ${formatBytes(MAX_APP_FILE_BYTES)}, ` +\n `and one request may carry ${formatBytes(MAX_UPLOAD_REQUEST_BYTES)} — a larger request ` +\n `is rejected by the network before the app receives it.`\n )\n }\n if (status === 411) return 'The upload did not declare its length.'\n if (status === 401 || status === 403) return 'Not authorized to write these files.'\n if (status === 415) return 'That file type is not accepted.'\n if (status >= 500) return `The files service failed (HTTP ${status}). Retry shortly.`\n return `Upload failed (HTTP ${status}).`\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; description?: string }) => {\n return createMutation({\n name: data.name,\n type: 'public',\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 { GroupedReaction, Reaction } from './channel-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) =>\n 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 {\n reactions: records as RecordData<Reaction>[],\n status,\n error,\n getReactionsForMessage,\n toggle,\n }\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 { createConfirmed, removeConfirmed } = useMutations<ChannelMember>('channel-members')\n\n const join = useCallback(async () => {\n if (!channelId || !user) return\n const existing = records.find((r) => r.data.userId === user.id)\n if (existing) return\n await createConfirmed({\n channelId,\n userId: user.id,\n joinedAt: new Date().toISOString(),\n } as unknown as ChannelMember)\n }, [channelId, user, records, createConfirmed])\n\n const leave = useCallback(async () => {\n if (!channelId || !user) return\n const membership = records.find((r) => r.data.userId === user.id)\n if (membership) await removeConfirmed(membership.recordId)\n }, [channelId, user, records, removeConfirmed])\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","'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 * Platform API error normalization — the one chokepoint that turns the\n * api-worker's error envelope into a render-safe + branch-safe shape.\n *\n * The platform emits errors as `{ error: <machine slug>, message?: <human>,\n * ...details }` (e.g. 402 `{ error: 'insufficient_credits', message:\n * 'Insufficient credits.', availableCredits, requiredCredits }`; some routes\n * send the bare slug with no `message` at all, e.g. 409\n * `{ error: 'owner_connect_not_ready' }`). Consumers must never show the slug\n * to a person or make an agent parse the sentence — this module separates the\n * two once, for the integration client, the billing hooks, and the CLI.\n */\n\n/** One structured validation issue (Zod shape) from a `validation_failed` response. */\nexport interface ApiErrorIssue {\n path?: string[]\n message: string\n code?: string\n}\n\n/** A platform error response, normalized. */\nexport interface NormalizedApiError {\n /**\n * Human-readable text, safe to render:\n * `message ?? non-slug error ?? humanized(slug) ?? slug`.\n */\n error: string\n /** The machine slug (e.g. 'insufficient_credits') for branching — never render it. */\n code?: string\n /** HTTP status of the response; 0 when no HTTP response arrived (transport failure). */\n status: number\n /** Structured fields the server sent alongside the error (e.g. `availableCredits`). */\n details?: Record<string, unknown>\n /** Zod issues accompanying a `validation_failed` response. */\n issues?: ApiErrorIssue[]\n}\n\n// A machine slug: lowercase alphanumeric segments joined by `_`, `.`, or `-`.\n// Rejects human sentences (\"Unknown integration: openai/foo\") so they pass\n// through as the error text rather than masquerading as a code.\nconst SLUG_RE = /^[a-z0-9]+(?:[_.-][a-z0-9]+)*$/\n\n// Human text for the slugs the platform emits without (or before) a `message`\n// of their own. Unknown slugs fall through to the raw message/slug unchanged.\nconst HUMAN_MESSAGES: Record<string, string> = {\n owner_connect_not_ready:\n \"This app isn't ready to accept payments yet — the app owner needs to finish payment setup.\",\n invalid_json: 'The request body was not valid JSON.',\n invalid_body: 'The request body is invalid.',\n invalid_product_id: 'Invalid product id.',\n duplicate_product_id: 'Duplicate product id.',\n unknown_product: 'Unknown product.',\n too_many_products: 'Too many products declared.',\n invalid_amount: 'Invalid amount.',\n invalid_name: 'Invalid product name.',\n invalid_description: 'Invalid product description.',\n unsupported_currency: 'Unsupported currency.',\n unknown_app: 'Unknown app.',\n not_app_owner: 'Only the app owner (or a collaborator) can do this.',\n invalid_return_url: 'Invalid return URL.',\n invalid_cancel_url: 'Invalid cancel URL.',\n unknown_plan: 'Unknown subscription plan.',\n unknown_price: 'No price is configured for that billing interval.',\n no_subscription: 'No subscription found for this account.',\n}\n\n/**\n * Normalize a platform error body (parsed JSON, or anything else) plus its\n * HTTP status into a {@link NormalizedApiError}. Pass status 0 for failures\n * that never produced an HTTP response.\n */\nexport function normalizeApiError(status: number, body: unknown): NormalizedApiError {\n const payload = (body !== null && typeof body === 'object' ? body : {}) as Record<\n string,\n unknown\n >\n const rawError = typeof payload.error === 'string' ? payload.error : undefined\n const message = typeof payload.message === 'string' ? payload.message : undefined\n // In the `{ code: slug, error: human }` convention the server's `error` IS\n // the specific human sentence — it must beat the generic slug mapping.\n const humanError = rawError !== undefined && !SLUG_RE.test(rawError) ? rawError : undefined\n const code =\n rawError !== undefined && SLUG_RE.test(rawError)\n ? rawError\n : typeof payload.code === 'string' && SLUG_RE.test(payload.code)\n ? payload.code\n : undefined\n\n const details: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(payload)) {\n if (\n key === 'success' ||\n key === 'error' ||\n key === 'message' ||\n key === 'issues' ||\n key === 'code'\n )\n continue\n details[key] = value\n }\n\n return {\n error:\n message ??\n humanError ??\n (code !== undefined ? HUMAN_MESSAGES[code] : undefined) ??\n rawError ??\n `Request failed (${status})`,\n ...(code !== undefined ? { code } : {}),\n status,\n ...(Object.keys(details).length > 0 ? { details } : {}),\n ...(Array.isArray(payload.issues) ? { issues: payload.issues as ApiErrorIssue[] } : {}),\n }\n}\n\n/**\n * The error the billing hooks (`useCheckout`, `useSubscription`) throw for a\n * platform refusal. `message` is human text safe to render; branch on `code`:\n *\n * try {\n * await subscribe('pro')\n * } catch (e) {\n * if (e instanceof PlatformApiError && e.code === 'owner_connect_not_ready') {\n * // owner hasn't finished payment onboarding\n * }\n * }\n */\nexport class PlatformApiError extends Error {\n /** The machine slug (e.g. 'owner_connect_not_ready'), when the server sent one. */\n readonly code?: string\n /** HTTP status of the response; 0 when no HTTP response arrived. */\n readonly status: number\n /** Structured fields the server sent alongside the error. */\n readonly details?: Record<string, unknown>\n /** Zod issues accompanying a `validation_failed` response. */\n readonly issues?: ApiErrorIssue[]\n\n constructor(normalized: NormalizedApiError) {\n super(normalized.error)\n this.name = 'PlatformApiError'\n this.code = normalized.code\n this.status = normalized.status\n this.details = normalized.details\n this.issues = normalized.issues\n }\n}\n\n/**\n * The machine slug from a thrown {@link PlatformApiError}, or null for any\n * other thrown value — the chokepoint for \"which refusal was this?\" checks.\n */\nexport const apiErrorCode = (e: unknown): string | null =>\n e instanceof PlatformApiError ? (e.code ?? null) : null\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'\nimport { normalizeApiError, type ApiErrorIssue } from '../shared/api-error'\n\nconst ENDPOINT_PREFIX = '/api/integrations'\nconst DEFAULT_TIMEOUT_MS = 120_000\nconst ENDPOINT_RE = /^[a-z0-9][a-z0-9._~-]*(?:\\/[a-z0-9][a-z0-9._~-]*)*$/i\n\ninterface IntegrationResponse<T = unknown> {\n success: boolean\n data?: T\n /** Human-readable error, safe to render directly. */\n error?: string\n /** Machine slug (e.g. 'insufficient_credits') for branching — never render it. */\n code?: string\n /** HTTP status of the failed response; 0 when no response arrived (timeout, abort, network). */\n status?: number\n /** Structured fields the server sent alongside the error (e.g. `availableCredits`). */\n details?: Record<string, unknown>\n issues?: ApiErrorIssue[]\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 !== '' && !ENDPOINT_RE.test(endpoint)) {\n throw new TypeError(\n 'Integration endpoints must be relative route segments, for example \"openai/chat-completion\".',\n )\n }\n const path = 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 query?: URLSearchParams,\n): Promise<IntegrationResponse<T>> {\n const queryString = query?.toString()\n const url = `${resolveUrl(endpoint)}${queryString ? `?${queryString}` : ''}`\n const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS\n\n if (options?.signal?.aborted) {\n return { success: false, error: 'Request cancelled', status: 0 }\n }\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 options?.signal?.addEventListener('abort', abortFromCaller, { once: true })\n let timedOut = false\n const timeout = setTimeout(() => {\n timedOut = true\n controller.abort()\n }, 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})`, status: res.status }\n }\n\n if (!res.ok || payload.success === false) {\n // The api-worker's envelope is { error: <machine slug>, message: <human>,\n // ...details } — normalize so `error` is always the human text and the\n // slug rides on `code` for branching.\n return { success: false, ...normalizeApiError(res.status, payload) }\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 {\n success: false,\n error: timedOut ? `Request timed out after ${timeoutMs}ms` : 'Request cancelled',\n status: 0,\n }\n }\n return {\n success: false,\n error: err instanceof Error ? err.message : 'Request failed',\n status: 0,\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>(\n endpoint: string,\n params?: Record<string, string | number | boolean | null | undefined>,\n options?: RequestOptions,\n ) {\n let searchParams: URLSearchParams | undefined\n if (params) {\n searchParams = new URLSearchParams()\n for (const [k, v] of Object.entries(params)) {\n if (v != null) searchParams.set(k, String(v))\n }\n }\n return request<T>('GET', endpoint, undefined, options, searchParams)\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\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\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 { authedJson } from '../authed-json'\nimport { apiErrorCode } from '../../shared/api-error'\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 /** Human-readable text of the last load failure, safe to render. */\n error: string | null\n /**\n * Machine slug of the last load failure (e.g. 'owner_connect_not_ready')\n * for branching — never render it. Null when there is no error or the\n * failure carried no slug (e.g. a network drop).\n */\n errorCode: 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; PricingTable also requires an onSelect callback. */\n plans: PlanInfo[]\n subscribe: (planSlug: string, opts?: SubscribeOpts) => Promise<SubscribeResult>\n openPortal: (returnUrl?: string) => Promise<{ url: string }>\n refresh: () => Promise<void>\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 const [errorCode, setErrorCode] = 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 setErrorCode(null)\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to load subscription')\n setErrorCode(apiErrorCode(e))\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 errorCode,\n hasTier,\n isAtLeast,\n plans: state.plans,\n subscribe,\n openPortal,\n refresh,\n }\n}\n","/**\n * Authenticated JSON fetch against the app's same-origin `/_deepspace` proxy.\n *\n * Shared by the billing hooks (`useCheckout`, `useSubscription`). On a non-2xx\n * response it throws a {@link PlatformApiError} built by the shared error\n * normalizer, so callers render `.message` (human text) and branch on `.code`\n * (the machine slug, e.g. 'owner_connect_not_ready') instead of string-sniffing.\n */\n\nimport { getAuthToken } from './auth/token'\nimport { normalizeApiError, PlatformApiError } from '../shared/api-error'\n\nexport async 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 PlatformApiError(normalizeApiError(res.status, data))\n }\n return data as T\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 by\n * `<PricingTable plans={sub.plans} onSelect={...}>`. Fields beyond slug/rank\n * are optional so a free tier (no prices, no trial) doesn't need to fabricate\n * 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 { authedJson } from '../authed-json'\nimport { apiErrorCode } from '../../shared/api-error'\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 /** Human-readable text of the last failure, safe to render. */\n error: string | null\n /**\n * Machine slug of the last failure (e.g. 'owner_connect_not_ready') for\n * branching — never render it. Null when there is no error or the\n * failure carried no slug (e.g. a network drop).\n */\n errorCode: 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\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 const [errorCode, setErrorCode] = 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 setErrorCode(null)\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to load purchases')\n setErrorCode(apiErrorCode(e))\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 setErrorCode(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 setErrorCode(apiErrorCode(e))\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 errorCode,\n purchases,\n owned,\n ownsProduct,\n refresh,\n }\n}\n","/**\n * Client-side error reporting — shared contract.\n *\n * Browser JS errors never invoke the app's Worker, so they never reach\n * Workers Logs and are invisible to `deepspace logs`. The opt-in reporter\n * (`installClientErrorReporter`, client entry) forwards them to the app's own\n * Worker via `registerClientErrorRoute` (worker entry), which `console.error`s\n * a marker-prefixed envelope. The platform log reader recognises the marker\n * and tags the event `source: 'client'`.\n *\n * This module is isomorphic — no DOM, no Hono — so both halves import the\n * exact same marker, caps, and normaliser and can never drift.\n */\n\n/**\n * Prefix the Worker route prepends before JSON-stringifying a report into a\n * single `console.error` line. The platform mapper\n * (platform/deploy-worker/src/lib/workers-logs.ts) detects this prefix to set\n * `source: 'client'`. Deliberately distinctive so an ordinary app log is\n * vanishingly unlikely to collide. The marker is added SERVER-SIDE by the app\n * Worker — a browser cannot forge it onto a genuine server log.\n *\n * The platform copy is kept in sync by a drift test; if you change this\n * string, that test fails until the platform const matches.\n */\nexport const CLIENT_LOG_MARKER = '[deepspace:client-error]'\n\n/** Same-origin path the reporter POSTs to and the route registers. */\nexport const CLIENT_ERROR_PATH = '/_deepspace/client-errors'\n\n/**\n * Per-field caps on the browser-supplied report. These bound the payload the\n * reporter sends and the route logs — they are NOT a hard serialized-size\n * guarantee: JSON escaping (a control char → `\\u00XX`, 6×) and multibyte text\n * can inflate the serialized line several-fold (a maxed adversarial envelope is\n * ~24 KB). That's fine — Cloudflare Workers Logs truncates a log event at\n * ~256 KB (bytes), so even the worst case survives intact and re-parses on\n * read. The platform reader (`workers-logs.ts`) then caps each surfaced field\n * (message/stack) at 8 KB *after* parsing.\n */\nexport const CLIENT_ERROR_CAPS = {\n message: 1024,\n stack: 4096,\n name: 256,\n url: 512,\n} as const\n\nexport type ClientErrorKind = 'error' | 'unhandledrejection' | 'react'\n\n/** A sanitized, capped browser-error report — the wire + log-line payload. */\nexport interface ClientErrorReport {\n kind: ClientErrorKind\n message: string\n name?: string\n stack?: string\n /** Page URL where it fired (href or the script filename). */\n url?: string\n line?: number\n col?: number\n}\n\nfunction capText(value: unknown, max: number): string | undefined {\n if (typeof value !== 'string') return undefined\n return value.length > max ? value.slice(0, max) : value\n}\n\nfunction finiteNumber(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined\n}\n\n/**\n * Validate + cap an untrusted report shape into a `ClientErrorReport`, or\n * `null` when there's nothing useful to log. Used on BOTH ends: the browser\n * caps before sending, and the Worker route re-normalises defensively — it\n * never trusts the posted body, mirroring the platform reader's field-by-field\n * whitelist philosophy.\n */\nexport function normalizeClientErrorReport(raw: unknown): ClientErrorReport | null {\n if (typeof raw !== 'object' || raw === null) return null\n const o = raw as Record<string, unknown>\n\n const kind: ClientErrorKind =\n o.kind === 'unhandledrejection' || o.kind === 'react' ? o.kind : 'error'\n const message = capText(o.message, CLIENT_ERROR_CAPS.message) ?? ''\n const name = capText(o.name, CLIENT_ERROR_CAPS.name)\n const stack = capText(o.stack, CLIENT_ERROR_CAPS.stack)\n const url = capText(o.url, CLIENT_ERROR_CAPS.url)\n const line = finiteNumber(o.line)\n const col = finiteNumber(o.col)\n\n // Nothing actionable — drop it (Worker route → 400).\n if (!message && !stack) return null\n\n const report: ClientErrorReport = { kind, message: message || (name ?? 'Error') }\n if (name) report.name = name\n if (stack) report.stack = stack\n if (url) report.url = url\n if (line !== undefined) report.line = line\n if (col !== undefined) report.col = col\n return report\n}\n\n/** The exact single-string line the Worker route writes to Workers Logs. */\nexport function clientErrorToLogLine(report: ClientErrorReport): string {\n return `${CLIENT_LOG_MARKER} ${JSON.stringify(report)}`\n}\n","/**\n * Client-side error reporter (opt-in) — the browser half of client-side error\n * reporting.\n *\n * // src/main.tsx (or your root)\n * import { installClientErrorReporter } from 'deepspace'\n * installClientErrorReporter()\n *\n * Hooks `window` 'error' + 'unhandledrejection' and forwards each to the app's\n * own Worker (see `registerClientErrorRoute` in 'deepspace/worker'), where it\n * lands in Workers Logs and shows up in `deepspace logs` tagged CLIENT.\n *\n * Best-effort by contract: it dedupes, caps how many it sends per page load,\n * and NEVER throws — a broken reporter must not break the app it watches.\n */\n\nimport { CLIENT_ERROR_PATH, normalizeClientErrorReport } from '../shared/client-errors'\nimport type { ClientErrorReport } from '../shared/client-errors'\n\nexport interface ClientErrorReporterOptions {\n /** Override the ingestion path (default `/_deepspace/client-errors`). */\n endpoint?: string\n /** Max reports sent per page load — a runaway loop can't flood (default 50). */\n maxReports?: number\n}\n\n// Strip query + fragment from a URL before it leaves the browser — a page href\n// or script filename may carry tokens the app put in its own URL, and those\n// should never be persisted in the platform's account-wide log store. Returns\n// origin + pathname only. Never throws.\nfunction stripQuery(u: string | undefined): string | undefined {\n if (!u) return undefined\n try {\n const parsed = new URL(u, window.location?.href)\n return parsed.origin + parsed.pathname\n } catch {\n return undefined\n }\n}\n\n// Fire-and-forget POST. Same-origin, so the session cookie rides along for\n// logged-in users; anonymous visitors are accepted too. keepalive lets a\n// report survive the page unloading right after the error.\nfunction post(endpoint: string, report: ClientErrorReport): void {\n try {\n void fetch(endpoint, {\n method: 'POST',\n keepalive: true,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(report),\n }).catch(() => {})\n } catch {\n // A reporter must never throw — swallow everything.\n }\n}\n\n// Module-global so a repeated install() call (against the same cached module\n// instance) doesn't double-report. Note: a full Vite HMR reload that\n// re-evaluates this module resets it — wire `import.meta.hot.dispose(uninstall)`\n// if you install inside an HMR-reloaded module.\nlet installed = false\n\n/**\n * Install global handlers that forward uncaught browser errors to the app's\n * Worker. No-op outside a browser. Returns an uninstall function.\n */\nexport function installClientErrorReporter(\n options: ClientErrorReporterOptions = {},\n): () => void {\n if (typeof window === 'undefined' || installed) return () => {}\n installed = true\n\n const endpoint = options.endpoint ?? CLIENT_ERROR_PATH\n const maxReports = options.maxReports ?? 50\n const seen = new Set<string>()\n let sent = 0\n\n const send = (raw: Record<string, unknown>): void => {\n const report = normalizeClientErrorReport(raw)\n if (!report) return\n const key = `${report.kind}:${report.name ?? ''}:${report.message}`\n if (seen.has(key)) return\n // Cap BEFORE recording, so `seen` can't grow past the cap on a loop that\n // throws distinct errors (network is capped either way; this bounds memory).\n if (sent >= maxReports) return\n seen.add(key)\n sent += 1\n post(endpoint, report)\n }\n\n // The field extraction itself must not throw (a hostile `reason` can have a\n // throwing `toString`/getter or a Proxy trap) — the whole handler is guarded.\n const onError = (event: ErrorEvent): void => {\n try {\n const err = event.error as Error | undefined\n send({\n kind: 'error',\n name: err?.name,\n message: err?.message ?? event.message,\n stack: err?.stack,\n url: stripQuery(event.filename || window.location?.href),\n line: event.lineno,\n col: event.colno,\n })\n } catch {\n // never throw\n }\n }\n\n const onRejection = (event: PromiseRejectionEvent): void => {\n try {\n const reason: unknown = event.reason\n const isError = reason instanceof Error\n send({\n kind: 'unhandledrejection',\n name: isError ? reason.name : undefined,\n message: isError ? reason.message : String(reason),\n stack: isError ? reason.stack : undefined,\n url: stripQuery(window.location?.href),\n })\n } catch {\n // never throw\n }\n }\n\n window.addEventListener('error', onError)\n window.addEventListener('unhandledrejection', onRejection)\n\n return () => {\n window.removeEventListener('error', onError)\n window.removeEventListener('unhandledrejection', onRejection)\n installed = false\n }\n}\n\n/**\n * Manually report a caught error — e.g. from a React error boundary's\n * `componentDidCatch`. No-op outside a browser; never throws. Unlike the\n * auto-installed handlers this has no client-side dedupe/cap — it relies on the\n * ingestion route's per-isolate throttle — so call it from boundaries (which\n * unmount rather than loop), not a hot path.\n */\nexport function reportClientError(\n error: unknown,\n options: ClientErrorReporterOptions & { componentStack?: string } = {},\n): void {\n if (typeof window === 'undefined') return\n try {\n const isError = error instanceof Error\n const report = normalizeClientErrorReport({\n kind: 'react',\n name: isError ? error.name : undefined,\n message: isError ? error.message : String(error),\n stack: (isError ? error.stack : undefined) ?? options.componentStack,\n url: stripQuery(window.location?.href),\n })\n if (!report) return\n post(options.endpoint ?? CLIENT_ERROR_PATH, report)\n } catch {\n // never throw\n }\n}\n","/**\n * `deepspace logs` wire DTO — the single source of truth for the whitelisted\n * shape the platform telemetry reader emits and every consumer reads.\n *\n * The producer is `platform/deploy-worker/src/lib/workers-logs.ts` (which\n * rebuilds each event field-by-field from a raw Cloudflare row — see the\n * security invariant there). The consumers are the CLI tail\n * (`cli/commands/logs.ts`), the dashboard Logs panel\n * (`apps/dashboard/src/lib/apps.ts`), and the e2e whitelist assertion\n * (`tests/e2e/tests/logs.spec.ts`). This module used to be hand-mirrored in\n * all four places; `source` was added to three of them and missed in the\n * e2e whitelist, so it now lives here once and they import it.\n *\n * Isomorphic — no DOM, no Hono, no platform types — so it re-exports cleanly\n * from both `deepspace` (client) and `deepspace/worker` and is safe to import\n * from the private deploy-worker.\n */\n\nexport const LOG_LEVELS = ['debug', 'log', 'info', 'warn', 'error'] as const\nexport type LogLevel = (typeof LOG_LEVELS)[number]\n\n/** One log / request / exception event, rebuilt field-by-field from a CF row. */\nexport interface AppLogEvent {\n /** `$metadata.id` — unique per event; the client's dedupe key. */\n id: string\n /** Unix ms. */\n timestamp: number\n level: LogLevel\n eventType: 'log' | 'request' | 'exception' | 'other'\n message: string\n /**\n * Present (= `'client'`) only for a browser-forwarded error the app's own\n * Worker re-logged via `registerClientErrorRoute`. Absent means the log was\n * emitted server-side — there is no explicit `'server'` value, so consumers\n * branch on `source === 'client'`, never on a truthy/other check.\n */\n source?: 'client'\n /**\n * The runtime's verdict on the invocation (`ok`, `exception`, `exceededCpu`,\n * `canceled`, …) — present on `request` and other invocation-summary events\n * only, never on a `log` line. It says whether the Worker RETURNED, not\n * whether the app succeeded: an action that throws inside the app's own\n * error handling and answers 500 is `ok` here. Triage failures on\n * `request.status`, `level`, and `eventType === 'exception'` (an exception\n * that escaped the Worker), not on `outcome !== 'ok'` alone.\n */\n outcome?: string\n request?: { method: string; path: string; status?: number }\n /**\n * The exception that ESCAPED the Worker (`eventType: 'exception'`), or a\n * browser-reported one (`source: 'client'`). An error the app catches and\n * `console.error`s is a plain `log` line — and the Workers runtime renders a\n * logged Error object as its stack frames without the message, so log the\n * message text yourself (`console.error(\\`… ${err.message}\\`, err.stack)`).\n */\n exception?: { name: string; message: string; stack?: string }\n}\n\nexport interface AppLogsResponse {\n /** Ascending by timestamp (CF returns newest-first; the reader flips). */\n events: AppLogEvent[]\n /** Max event timestamp in ms — pass back as `since` to poll; null when empty. */\n cursor: number | null\n /** True when the window returned `limit` events (older events were cut). */\n truncated: boolean\n pollIntervalMs: number\n}\n\n/**\n * Runtime tuple of every key a whitelisted `AppLogEvent` may carry — the\n * canonical whitelist the e2e suite mirrors when asserting no event leaks a key\n * outside this set. The two type-level assertions below fail compilation if\n * this tuple and the `AppLogEvent` interface disagree in either direction, so\n * adding an interface field forces a matching key here.\n *\n * NOTE: the e2e spec keeps its OWN copy of this set (it can't import the SDK —\n * `@deepspace/e2e` has no `deepspace` dependency), kept in step by a comment,\n * not by these compile-time guards. So this tuple hardens the code copies\n * (deploy-worker/CLI/dashboard, which import it), while the e2e mirror remains a\n * knowingly-accepted duplicate.\n */\nexport const APP_LOG_EVENT_KEYS = [\n 'id',\n 'timestamp',\n 'level',\n 'eventType',\n 'message',\n 'source',\n 'outcome',\n 'request',\n 'exception',\n] as const\n\nexport type AppLogEventKey = (typeof APP_LOG_EVENT_KEYS)[number]\n\n// Compile-time guard: `APP_LOG_EVENT_KEYS` and `keyof AppLogEvent` list the\n// same keys. If either drifts, one of these aliases resolves to `never` and\n// `tsc` errors on the assignment.\ntype _KeysCoverInterface = AppLogEventKey extends keyof AppLogEvent ? true : never\ntype _InterfaceCoversKeys = keyof AppLogEvent extends AppLogEventKey ? true : never\nconst _assertKeysMatch: [_KeysCoverInterface, _InterfaceCoversKeys] = [true, true]\nvoid _assertKeysMatch\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","useMemo","Y","useState","useRef","useMemo","useEffect","msgDocKey","payload","useCallback","useMemo","useCallback","useMemo","useCallback","useEffect","useCallback","useState","useState","useEffect","useCallback","u","useState","useEffect","useLayoutEffect","useCallback","useRef","useMemo","Y","useState","useMemo","useRef","useCallback","useEffect","useLayoutEffect","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","options","useCallback","useMemo","useMemo","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useEffect","r","g","b","Fragment","jsx","useEffect","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","useState","useCallback","useEffect","useMemo"]}