@agent-native/core 0.22.44 → 0.23.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 (114) hide show
  1. package/dist/a2a/artifact-response.js +1 -1
  2. package/dist/a2a/artifact-response.js.map +1 -1
  3. package/dist/agent/production-agent.d.ts.map +1 -1
  4. package/dist/agent/production-agent.js +12 -4
  5. package/dist/agent/production-agent.js.map +1 -1
  6. package/dist/cli/app-skill.d.ts +139 -0
  7. package/dist/cli/app-skill.d.ts.map +1 -0
  8. package/dist/cli/app-skill.js +960 -0
  9. package/dist/cli/app-skill.js.map +1 -0
  10. package/dist/cli/create.d.ts.map +1 -1
  11. package/dist/cli/create.js +13 -4
  12. package/dist/cli/create.js.map +1 -1
  13. package/dist/cli/index.js +24 -0
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/cli/skills.d.ts +39 -0
  16. package/dist/cli/skills.d.ts.map +1 -0
  17. package/dist/cli/skills.js +363 -0
  18. package/dist/cli/skills.js.map +1 -0
  19. package/dist/cli/templates-meta.d.ts.map +1 -1
  20. package/dist/cli/templates-meta.js +9 -6
  21. package/dist/cli/templates-meta.js.map +1 -1
  22. package/dist/cli/workspace-dev.d.ts.map +1 -1
  23. package/dist/cli/workspace-dev.js +2 -0
  24. package/dist/cli/workspace-dev.js.map +1 -1
  25. package/dist/client/AgentPanel.d.ts +2 -0
  26. package/dist/client/AgentPanel.d.ts.map +1 -1
  27. package/dist/client/AgentPanel.js +2 -2
  28. package/dist/client/AgentPanel.js.map +1 -1
  29. package/dist/client/AssistantChat.d.ts +9 -0
  30. package/dist/client/AssistantChat.d.ts.map +1 -1
  31. package/dist/client/AssistantChat.js +15 -7
  32. package/dist/client/AssistantChat.js.map +1 -1
  33. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  34. package/dist/client/MultiTabAssistantChat.js +15 -0
  35. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  36. package/dist/client/index.d.ts +1 -1
  37. package/dist/client/index.d.ts.map +1 -1
  38. package/dist/client/index.js.map +1 -1
  39. package/dist/client/use-chat-threads.d.ts +5 -1
  40. package/dist/client/use-chat-threads.d.ts.map +1 -1
  41. package/dist/client/use-chat-threads.js +14 -3
  42. package/dist/client/use-chat-threads.js.map +1 -1
  43. package/dist/collab/client.d.ts.map +1 -1
  44. package/dist/collab/client.js +20 -1
  45. package/dist/collab/client.js.map +1 -1
  46. package/dist/collab/routes.d.ts.map +1 -1
  47. package/dist/collab/routes.js +16 -2
  48. package/dist/collab/routes.js.map +1 -1
  49. package/dist/collab/storage.d.ts +8 -0
  50. package/dist/collab/storage.d.ts.map +1 -1
  51. package/dist/collab/storage.js +55 -7
  52. package/dist/collab/storage.js.map +1 -1
  53. package/dist/collab/ydoc-manager.d.ts.map +1 -1
  54. package/dist/collab/ydoc-manager.js +121 -69
  55. package/dist/collab/ydoc-manager.js.map +1 -1
  56. package/dist/deploy/workspace-deploy.js +6 -0
  57. package/dist/deploy/workspace-deploy.js.map +1 -1
  58. package/dist/mcp-client/index.d.ts +1 -1
  59. package/dist/mcp-client/index.d.ts.map +1 -1
  60. package/dist/mcp-client/index.js +1 -1
  61. package/dist/mcp-client/index.js.map +1 -1
  62. package/dist/mcp-client/routes.d.ts +1 -0
  63. package/dist/mcp-client/routes.d.ts.map +1 -1
  64. package/dist/mcp-client/routes.js +52 -0
  65. package/dist/mcp-client/routes.js.map +1 -1
  66. package/dist/mcp-client/workspace-servers.d.ts +15 -0
  67. package/dist/mcp-client/workspace-servers.d.ts.map +1 -0
  68. package/dist/mcp-client/workspace-servers.js +297 -0
  69. package/dist/mcp-client/workspace-servers.js.map +1 -0
  70. package/dist/resources/handlers.d.ts.map +1 -1
  71. package/dist/resources/handlers.js +38 -25
  72. package/dist/resources/handlers.js.map +1 -1
  73. package/dist/resources/store.d.ts +11 -3
  74. package/dist/resources/store.d.ts.map +1 -1
  75. package/dist/resources/store.js +220 -9
  76. package/dist/resources/store.js.map +1 -1
  77. package/dist/scripts/call-agent.js +1 -1
  78. package/dist/scripts/call-agent.js.map +1 -1
  79. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  80. package/dist/server/agent-chat-plugin.js +21 -6
  81. package/dist/server/agent-chat-plugin.js.map +1 -1
  82. package/dist/server/agent-discovery.d.ts.map +1 -1
  83. package/dist/server/agent-discovery.js +34 -9
  84. package/dist/server/agent-discovery.js.map +1 -1
  85. package/dist/server/auth-marketing.d.ts.map +1 -1
  86. package/dist/server/auth-marketing.js +8 -5
  87. package/dist/server/auth-marketing.js.map +1 -1
  88. package/dist/templates/default/AGENTS.md +12 -4
  89. package/dist/templates/default/DEVELOPING.md +7 -5
  90. package/dist/templates/workspace-core/AGENTS.md +7 -0
  91. package/dist/templates/workspace-root/AGENTS.md +6 -0
  92. package/docs/content/creating-templates.md +14 -9
  93. package/docs/content/database.md +44 -17
  94. package/docs/content/deployment.md +15 -7
  95. package/docs/content/dispatch.md +7 -1
  96. package/docs/content/embedding-sdk.md +79 -0
  97. package/docs/content/key-concepts.md +15 -17
  98. package/docs/content/mcp-clients.md +30 -0
  99. package/docs/content/multi-app-workspace.md +3 -2
  100. package/docs/content/multi-tenancy.md +4 -4
  101. package/docs/content/server.md +10 -7
  102. package/docs/content/skills-guide.md +75 -0
  103. package/docs/content/template-analytics.md +1 -1
  104. package/docs/content/template-assets.md +130 -0
  105. package/docs/content/template-dispatch.md +3 -2
  106. package/docs/content/template-slides.md +2 -2
  107. package/docs/content/workspace-management.md +2 -2
  108. package/docs/content/workspace.md +11 -9
  109. package/package.json +1 -1
  110. package/src/templates/default/AGENTS.md +12 -4
  111. package/src/templates/default/DEVELOPING.md +7 -5
  112. package/src/templates/workspace-core/AGENTS.md +7 -0
  113. package/src/templates/workspace-root/AGENTS.md +6 -0
  114. package/docs/content/template-images.md +0 -55
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/collab/client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Client-side hook for collaborative document editing via Yjs.\n *\n * Creates a STABLE Y.Doc per docId that never changes identity. This allows\n * TipTap's Collaboration extension to bind once without editor recreation.\n * Server state is applied to the existing doc when it arrives.\n *\n * Also manages Yjs Awareness for cursor positions and user presence,\n * synced via polling to the server's awareness endpoint.\n */\n\nimport { useEffect, useRef, useState, useMemo } from \"react\";\nimport * as Y from \"yjs\";\nimport { Awareness } from \"y-protocols/awareness\";\nimport { agentNativePath } from \"../client/api-path.js\";\n\nexport interface CollabUser {\n name: string;\n email: string;\n color: string;\n}\n\nexport interface UseCollaborativeDocOptions {\n /** Document ID to collaborate on. Pass null to disable. */\n docId: string | null;\n /** Poll interval in ms. Default: 2000 */\n pollInterval?: number;\n /** Pause remote update/presence polling while the tab is hidden. Default: true */\n pauseWhenHidden?: boolean;\n /** Base URL for collab endpoints. Default: \"/_agent-native/collab\" */\n baseUrl?: string;\n /** Request source ID for jitter prevention (e.g., tab ID). */\n requestSource?: string;\n /** Current user info for cursor labels. */\n user?: CollabUser;\n}\n\nexport interface UseCollaborativeDocResult {\n /** The Yjs document instance. Stable per docId — never changes identity. */\n ydoc: Y.Doc | null;\n /** Yjs Awareness instance for cursor/presence sync. */\n awareness: Awareness | null;\n /** Whether the initial state is still loading from the server. */\n isLoading: boolean;\n /** Whether the doc is synced with the server. */\n isSynced: boolean;\n /** Active users on this document (from awareness). */\n activeUsers: CollabUser[];\n /** True briefly when the AI agent makes an edit (for presence indicator). */\n agentActive: boolean;\n /** True when the AI agent has an active awareness entry (durable presence). */\n agentPresent: boolean;\n}\n\n// Consistent color palette for user cursors\nconst CURSOR_COLORS = [\n \"#f87171\",\n \"#fb923c\",\n \"#fbbf24\",\n \"#a3e635\",\n \"#34d399\",\n \"#22d3ee\",\n \"#60a5fa\",\n \"#14b8a6\",\n \"#f472b6\",\n \"#e879f9\",\n];\n\n/** Hash a string to a consistent color from the palette. */\nexport function emailToColor(email: string): string {\n let hash = 0;\n for (let i = 0; i < email.length; i++) {\n hash = ((hash << 5) - hash + email.charCodeAt(i)) | 0;\n }\n return CURSOR_COLORS[Math.abs(hash) % CURSOR_COLORS.length];\n}\n\n/** Derive a display name from an email address. */\nexport function emailToName(email: string): string {\n const local = email.split(\"@\")[0] || email;\n return local.charAt(0).toUpperCase() + local.slice(1);\n}\n\nfunction normalizeCollabEmail(email: string): string {\n return email.trim().toLowerCase();\n}\n\nfunction isDocumentHidden(): boolean {\n return (\n typeof document !== \"undefined\" && document.visibilityState === \"hidden\"\n );\n}\n\nexport function dedupeCollabUsersByEmail(users: CollabUser[]): CollabUser[] {\n const byEmail = new Map<string, CollabUser>();\n for (const user of users) {\n const email = normalizeCollabEmail(user.email);\n if (!email || byEmail.has(email)) continue;\n byEmail.set(email, {\n name: user.name || emailToName(email),\n email,\n color: user.color || emailToColor(email),\n });\n }\n return Array.from(byEmail.values());\n}\n\nexport interface RemoteAwarenessSnapshot {\n clientId: number;\n state: unknown;\n}\n\nexport function reconcileRemoteAwarenessStates(\n states: Map<number, unknown>,\n localClientId: number,\n remoteStates: RemoteAwarenessSnapshot[],\n): { added: number[]; updated: number[]; removed: number[] } {\n const incoming = new Set<number>();\n const added: number[] = [];\n const updated: number[] = [];\n const removed: number[] = [];\n\n for (const remote of remoteStates) {\n if (\n !Number.isFinite(remote.clientId) ||\n remote.clientId === localClientId\n ) {\n continue;\n }\n incoming.add(remote.clientId);\n const hadState = states.has(remote.clientId);\n states.set(remote.clientId, remote.state);\n (hadState ? updated : added).push(remote.clientId);\n }\n\n for (const clientId of Array.from(states.keys())) {\n if (clientId === localClientId) continue;\n if (incoming.has(clientId)) continue;\n states.delete(clientId);\n removed.push(clientId);\n }\n\n return { added, updated, removed };\n}\n\n// Base64 helpers\nfunction uint8ArrayToBase64(arr: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < arr.length; i++) {\n binary += String.fromCharCode(arr[i]);\n }\n return btoa(binary);\n}\n\nfunction base64ToUint8Array(b64: string): Uint8Array {\n const binary = atob(b64);\n const arr = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n arr[i] = binary.charCodeAt(i);\n }\n return arr;\n}\n\nexport function useCollaborativeDoc(\n options: UseCollaborativeDocOptions,\n): UseCollaborativeDocResult {\n const {\n docId,\n pollInterval = 2000,\n pauseWhenHidden = true,\n baseUrl = agentNativePath(\"/_agent-native/collab\"),\n requestSource,\n user,\n } = options;\n\n // Stable Y.Doc per docId\n const ydoc = useMemo(() => {\n if (!docId) return null;\n return new Y.Doc();\n }, [docId]);\n\n // Stable Awareness per ydoc\n const awareness = useMemo(() => {\n if (!ydoc) return null;\n return new Awareness(ydoc);\n }, [ydoc]);\n\n const [isLoading, setIsLoading] = useState(!!docId);\n const [isSynced, setIsSynced] = useState(false);\n const [activeUsers, setActiveUsers] = useState<CollabUser[]>([]);\n const [agentActive, setAgentActive] = useState(false);\n const [agentPresent, setAgentPresent] = useState(false);\n // Set when the initial state fetch returns 404/403 — stops the awareness\n // poll so we don't spam the console with errors against a doc that doesn't\n // exist or isn't accessible.\n const [docMissing, setDocMissing] = useState(false);\n const agentTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pollVersionRef = useRef(0);\n\n // Set local awareness state (user info for cursor labels)\n useEffect(() => {\n if (!awareness || !user) return;\n awareness.setLocalStateField(\"user\", {\n name: user.name,\n email: user.email,\n color: user.color,\n });\n }, [awareness, user?.name, user?.email, user?.color]);\n\n // Track active users from awareness changes\n useEffect(() => {\n if (!awareness) return;\n\n const updateUsers = () => {\n const users: CollabUser[] = [];\n let hasAgent = false;\n awareness.getStates().forEach((state, clientId) => {\n if (clientId === ydoc?.clientID) return; // Skip self\n if (state.user) {\n users.push(state.user as CollabUser);\n if ((state.user as CollabUser).email === \"agent@system\") {\n hasAgent = true;\n }\n }\n });\n setActiveUsers(dedupeCollabUsersByEmail(users));\n setAgentPresent(hasAgent);\n };\n\n awareness.on(\"change\", updateUsers);\n return () => {\n awareness.off(\"change\", updateUsers);\n };\n }, [awareness, ydoc]);\n\n // Clean up on unmount or docId change\n useEffect(() => {\n return () => {\n awareness?.destroy();\n ydoc?.destroy();\n };\n }, [ydoc, awareness]);\n\n // Fetch server state and apply to existing doc\n useEffect(() => {\n if (!ydoc || !docId) {\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n setIsLoading(true);\n setIsSynced(false);\n setDocMissing(false);\n\n fetch(`${baseUrl}/${docId}/state`)\n .then(async (res) => {\n if (cancelled) return;\n if (res.status === 404 || res.status === 403) {\n setDocMissing(true);\n setIsLoading(false);\n setIsSynced(true);\n return;\n }\n const data = (await res.json().catch(() => null)) as {\n state?: string;\n } | null;\n if (data?.state) {\n const binary = base64ToUint8Array(data.state);\n if (binary.length > 4) {\n Y.applyUpdate(ydoc, binary);\n }\n }\n setIsLoading(false);\n setIsSynced(true);\n })\n .catch(() => {\n if (cancelled) return;\n setIsLoading(false);\n setIsSynced(true);\n });\n\n return () => {\n cancelled = true;\n };\n }, [ydoc, docId, baseUrl]);\n\n // Send local updates to server\n useEffect(() => {\n if (!ydoc || !docId || docMissing) return;\n\n const handler = (update: Uint8Array, origin: unknown) => {\n if (origin === \"remote\") return;\n\n fetch(`${baseUrl}/${docId}/update`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n update: uint8ArrayToBase64(update),\n requestSource,\n }),\n });\n };\n\n ydoc.on(\"update\", handler);\n return () => {\n ydoc.off(\"update\", handler);\n };\n }, [ydoc, docId, baseUrl, requestSource, docMissing]);\n\n // Poll for remote doc updates + awareness sync\n useEffect(() => {\n if (!ydoc || !docId || docMissing) return;\n\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n function schedulePoll() {\n if (stopped) return;\n if (pauseWhenHidden && isDocumentHidden()) return;\n timer = setTimeout(poll, pollInterval);\n }\n\n async function poll() {\n if (stopped) return;\n try {\n // Poll for document updates\n const res = await fetch(\n agentNativePath(\n `/_agent-native/poll?since=${pollVersionRef.current}`,\n ),\n );\n if (!res.ok) throw new Error(\"HTTP \" + res.status);\n const data = await res.json();\n const { version, events } = data as {\n version: number;\n events: Array<{\n source: string;\n docId?: string;\n update?: string;\n requestSource?: string;\n }>;\n };\n\n for (const evt of events) {\n if (evt.source === \"collab\" && evt.docId === docId && evt.update) {\n if (requestSource && evt.requestSource === requestSource) continue;\n Y.applyUpdate(ydoc, base64ToUint8Array(evt.update), \"remote\");\n\n // Show agent presence indicator briefly\n if (evt.requestSource === \"agent\") {\n setAgentActive(true);\n if (agentTimerRef.current) clearTimeout(agentTimerRef.current);\n agentTimerRef.current = setTimeout(\n () => setAgentActive(false),\n 3000,\n );\n }\n }\n }\n\n pollVersionRef.current = version;\n\n // Sync awareness (cursor positions)\n if (awareness) {\n const localState = awareness.getLocalState();\n if (localState) {\n const awarenessRes = await fetch(`${baseUrl}/${docId}/awareness`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n clientId: ydoc.clientID,\n state: JSON.stringify(localState),\n }),\n });\n if (awarenessRes.ok) {\n const awarenessData = await awarenessRes.json();\n const remoteStates: RemoteAwarenessSnapshot[] = [];\n for (const remote of awarenessData.states || []) {\n try {\n const remoteState = JSON.parse(remote.state);\n remoteStates.push({\n clientId: Number(remote.clientId),\n state: remoteState,\n });\n } catch {\n // Invalid state — skip\n }\n }\n const changes = reconcileRemoteAwarenessStates(\n awareness.getStates() as Map<number, unknown>,\n ydoc.clientID,\n remoteStates,\n );\n if (\n changes.added.length ||\n changes.updated.length ||\n changes.removed.length\n ) {\n awareness.emit(\"change\", [changes, \"remote\"]);\n }\n }\n }\n }\n } catch {\n // Network error — retry next interval\n }\n schedulePoll();\n }\n\n function pollNow() {\n if (pauseWhenHidden && isDocumentHidden()) return;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n void poll();\n }\n\n function handleVisibilityChange() {\n if (document.visibilityState === \"visible\") {\n pollNow();\n } else if (pauseWhenHidden && timer) {\n clearTimeout(timer);\n timer = null;\n }\n }\n\n if (!pauseWhenHidden || !isDocumentHidden()) {\n void poll();\n }\n window.addEventListener(\"focus\", pollNow);\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n\n return () => {\n stopped = true;\n if (timer) clearTimeout(timer);\n window.removeEventListener(\"focus\", pollNow);\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n };\n }, [\n ydoc,\n awareness,\n docId,\n pollInterval,\n pauseWhenHidden,\n requestSource,\n baseUrl,\n docMissing,\n ]);\n\n return {\n ydoc,\n awareness,\n isLoading,\n isSynced,\n activeUsers,\n agentActive,\n agentPresent,\n };\n}\n"]}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/collab/client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Client-side hook for collaborative document editing via Yjs.\n *\n * Creates a STABLE Y.Doc per docId that never changes identity. This allows\n * TipTap's Collaboration extension to bind once without editor recreation.\n * Server state is applied to the existing doc when it arrives.\n *\n * Also manages Yjs Awareness for cursor positions and user presence,\n * synced via polling to the server's awareness endpoint.\n */\n\nimport { useEffect, useRef, useState, useMemo } from \"react\";\nimport * as Y from \"yjs\";\nimport { Awareness } from \"y-protocols/awareness\";\nimport { agentNativePath } from \"../client/api-path.js\";\n\nexport interface CollabUser {\n name: string;\n email: string;\n color: string;\n}\n\nexport interface UseCollaborativeDocOptions {\n /** Document ID to collaborate on. Pass null to disable. */\n docId: string | null;\n /** Poll interval in ms. Default: 2000 */\n pollInterval?: number;\n /** Pause remote update/presence polling while the tab is hidden. Default: true */\n pauseWhenHidden?: boolean;\n /** Base URL for collab endpoints. Default: \"/_agent-native/collab\" */\n baseUrl?: string;\n /** Request source ID for jitter prevention (e.g., tab ID). */\n requestSource?: string;\n /** Current user info for cursor labels. */\n user?: CollabUser;\n}\n\nexport interface UseCollaborativeDocResult {\n /** The Yjs document instance. Stable per docId — never changes identity. */\n ydoc: Y.Doc | null;\n /** Yjs Awareness instance for cursor/presence sync. */\n awareness: Awareness | null;\n /** Whether the initial state is still loading from the server. */\n isLoading: boolean;\n /** Whether the doc is synced with the server. */\n isSynced: boolean;\n /** Active users on this document (from awareness). */\n activeUsers: CollabUser[];\n /** True briefly when the AI agent makes an edit (for presence indicator). */\n agentActive: boolean;\n /** True when the AI agent has an active awareness entry (durable presence). */\n agentPresent: boolean;\n}\n\n// Consistent color palette for user cursors\nconst CURSOR_COLORS = [\n \"#f87171\",\n \"#fb923c\",\n \"#fbbf24\",\n \"#a3e635\",\n \"#34d399\",\n \"#22d3ee\",\n \"#60a5fa\",\n \"#14b8a6\",\n \"#f472b6\",\n \"#e879f9\",\n];\n\n/** Hash a string to a consistent color from the palette. */\nexport function emailToColor(email: string): string {\n let hash = 0;\n for (let i = 0; i < email.length; i++) {\n hash = ((hash << 5) - hash + email.charCodeAt(i)) | 0;\n }\n return CURSOR_COLORS[Math.abs(hash) % CURSOR_COLORS.length];\n}\n\n/** Derive a display name from an email address. */\nexport function emailToName(email: string): string {\n const local = email.split(\"@\")[0] || email;\n return local.charAt(0).toUpperCase() + local.slice(1);\n}\n\nfunction normalizeCollabEmail(email: string): string {\n return email.trim().toLowerCase();\n}\n\nfunction isDocumentHidden(): boolean {\n return (\n typeof document !== \"undefined\" && document.visibilityState === \"hidden\"\n );\n}\n\nexport function dedupeCollabUsersByEmail(users: CollabUser[]): CollabUser[] {\n const byEmail = new Map<string, CollabUser>();\n for (const user of users) {\n const email = normalizeCollabEmail(user.email);\n if (!email || byEmail.has(email)) continue;\n byEmail.set(email, {\n name: user.name || emailToName(email),\n email,\n color: user.color || emailToColor(email),\n });\n }\n return Array.from(byEmail.values());\n}\n\nexport interface RemoteAwarenessSnapshot {\n clientId: number;\n state: unknown;\n}\n\nexport function reconcileRemoteAwarenessStates(\n states: Map<number, unknown>,\n localClientId: number,\n remoteStates: RemoteAwarenessSnapshot[],\n): { added: number[]; updated: number[]; removed: number[] } {\n const incoming = new Set<number>();\n const added: number[] = [];\n const updated: number[] = [];\n const removed: number[] = [];\n\n for (const remote of remoteStates) {\n if (\n !Number.isFinite(remote.clientId) ||\n remote.clientId === localClientId\n ) {\n continue;\n }\n incoming.add(remote.clientId);\n const hadState = states.has(remote.clientId);\n states.set(remote.clientId, remote.state);\n (hadState ? updated : added).push(remote.clientId);\n }\n\n for (const clientId of Array.from(states.keys())) {\n if (clientId === localClientId) continue;\n if (incoming.has(clientId)) continue;\n states.delete(clientId);\n removed.push(clientId);\n }\n\n return { added, updated, removed };\n}\n\n// Base64 helpers\nfunction uint8ArrayToBase64(arr: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < arr.length; i++) {\n binary += String.fromCharCode(arr[i]);\n }\n return btoa(binary);\n}\n\nfunction base64ToUint8Array(b64: string): Uint8Array {\n const binary = atob(b64);\n const arr = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n arr[i] = binary.charCodeAt(i);\n }\n return arr;\n}\n\nexport function useCollaborativeDoc(\n options: UseCollaborativeDocOptions,\n): UseCollaborativeDocResult {\n const {\n docId,\n pollInterval = 2000,\n pauseWhenHidden = true,\n baseUrl = agentNativePath(\"/_agent-native/collab\"),\n requestSource,\n user,\n } = options;\n\n // Stable Y.Doc per docId\n const ydoc = useMemo(() => {\n if (!docId) return null;\n return new Y.Doc();\n }, [docId]);\n\n // Stable Awareness per ydoc\n const awareness = useMemo(() => {\n if (!ydoc) return null;\n return new Awareness(ydoc);\n }, [ydoc]);\n\n const [isLoading, setIsLoading] = useState(!!docId);\n const [isSynced, setIsSynced] = useState(false);\n const [activeUsers, setActiveUsers] = useState<CollabUser[]>([]);\n const [agentActive, setAgentActive] = useState(false);\n const [agentPresent, setAgentPresent] = useState(false);\n // Set when the initial state fetch returns 404/403 — stops the awareness\n // poll so we don't spam the console with errors against a doc that doesn't\n // exist or isn't accessible.\n const [docMissing, setDocMissing] = useState(false);\n const agentTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pollVersionRef = useRef(0);\n\n // Set local awareness state (user info for cursor labels)\n useEffect(() => {\n if (!awareness || !user) return;\n awareness.setLocalStateField(\"user\", {\n name: user.name,\n email: user.email,\n color: user.color,\n });\n }, [awareness, user?.name, user?.email, user?.color]);\n\n // Track active users from awareness changes\n useEffect(() => {\n if (!awareness) return;\n\n const updateUsers = () => {\n const users: CollabUser[] = [];\n let hasAgent = false;\n awareness.getStates().forEach((state, clientId) => {\n if (clientId === ydoc?.clientID) return; // Skip self\n if (state.user) {\n users.push(state.user as CollabUser);\n if ((state.user as CollabUser).email === \"agent@system\") {\n hasAgent = true;\n }\n }\n });\n setActiveUsers(dedupeCollabUsersByEmail(users));\n setAgentPresent(hasAgent);\n };\n\n awareness.on(\"change\", updateUsers);\n return () => {\n awareness.off(\"change\", updateUsers);\n };\n }, [awareness, ydoc]);\n\n // Clean up on unmount or docId change\n useEffect(() => {\n return () => {\n awareness?.destroy();\n ydoc?.destroy();\n };\n }, [ydoc, awareness]);\n\n // Fetch server state and apply to existing doc\n useEffect(() => {\n if (!ydoc || !docId) {\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n setIsLoading(true);\n setIsSynced(false);\n setDocMissing(false);\n\n fetch(`${baseUrl}/${docId}/state`)\n .then(async (res) => {\n if (cancelled) return;\n if (res.status === 404 || res.status === 403) {\n setDocMissing(true);\n setIsLoading(false);\n setIsSynced(true);\n return;\n }\n const data = (await res.json().catch(() => null)) as {\n state?: string;\n } | null;\n if (data?.state) {\n const binary = base64ToUint8Array(data.state);\n if (binary.length > 4) {\n Y.applyUpdate(ydoc, binary, \"remote\");\n }\n }\n setIsLoading(false);\n setIsSynced(true);\n })\n .catch(() => {\n if (cancelled) return;\n setIsLoading(false);\n setIsSynced(true);\n });\n\n return () => {\n cancelled = true;\n };\n }, [ydoc, docId, baseUrl]);\n\n // Send local updates to server\n useEffect(() => {\n if (!ydoc || !docId || docMissing) return;\n\n const handler = (update: Uint8Array, origin: unknown) => {\n if (origin === \"remote\") return;\n\n fetch(`${baseUrl}/${docId}/update`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n update: uint8ArrayToBase64(update),\n requestSource,\n }),\n });\n };\n\n ydoc.on(\"update\", handler);\n return () => {\n ydoc.off(\"update\", handler);\n };\n }, [ydoc, docId, baseUrl, requestSource, docMissing]);\n\n // Poll for remote doc updates + awareness sync\n useEffect(() => {\n if (!ydoc || !docId || docMissing) return;\n\n let stopped = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n function schedulePoll() {\n if (stopped) return;\n if (pauseWhenHidden && isDocumentHidden()) return;\n timer = setTimeout(poll, pollInterval);\n }\n\n async function poll() {\n if (stopped) return;\n try {\n // Poll for document updates\n const res = await fetch(\n agentNativePath(\n `/_agent-native/poll?since=${pollVersionRef.current}`,\n ),\n );\n if (!res.ok) throw new Error(\"HTTP \" + res.status);\n const data = await res.json();\n const { version, events } = data as {\n version: number;\n events: Array<{\n source: string;\n docId?: string;\n update?: string;\n requestSource?: string;\n }>;\n };\n\n for (const evt of events) {\n if (evt.source === \"collab\" && evt.docId === docId && evt.update) {\n if (requestSource && evt.requestSource === requestSource) continue;\n Y.applyUpdate(ydoc, base64ToUint8Array(evt.update), \"remote\");\n\n // Show agent presence indicator briefly\n if (evt.requestSource === \"agent\") {\n setAgentActive(true);\n if (agentTimerRef.current) clearTimeout(agentTimerRef.current);\n agentTimerRef.current = setTimeout(\n () => setAgentActive(false),\n 3000,\n );\n }\n }\n }\n\n pollVersionRef.current = version;\n\n try {\n // The poll ring buffer is process-local. Fetching a state-vector diff\n // makes collaboration durable across serverless invocations, process\n // restarts, or any missed poll event.\n const stateVector = uint8ArrayToBase64(Y.encodeStateVector(ydoc));\n const stateRes = await fetch(\n `${baseUrl}/${docId}/state?stateVector=${encodeURIComponent(\n stateVector,\n )}`,\n );\n if (stateRes.ok) {\n const stateData = (await stateRes.json().catch(() => null)) as {\n state?: string;\n } | null;\n if (stateData?.state) {\n const binary = base64ToUint8Array(stateData.state);\n if (binary.length > 2) {\n Y.applyUpdate(ydoc, binary, \"remote\");\n }\n }\n }\n } catch {\n // The next poll retries; awareness should still sync below.\n }\n\n // Sync awareness (cursor positions)\n if (awareness) {\n const localState = awareness.getLocalState();\n if (localState) {\n const awarenessRes = await fetch(`${baseUrl}/${docId}/awareness`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n clientId: ydoc.clientID,\n state: JSON.stringify(localState),\n }),\n });\n if (awarenessRes.ok) {\n const awarenessData = await awarenessRes.json();\n const remoteStates: RemoteAwarenessSnapshot[] = [];\n for (const remote of awarenessData.states || []) {\n try {\n const remoteState = JSON.parse(remote.state);\n remoteStates.push({\n clientId: Number(remote.clientId),\n state: remoteState,\n });\n } catch {\n // Invalid state — skip\n }\n }\n const changes = reconcileRemoteAwarenessStates(\n awareness.getStates() as Map<number, unknown>,\n ydoc.clientID,\n remoteStates,\n );\n if (\n changes.added.length ||\n changes.updated.length ||\n changes.removed.length\n ) {\n awareness.emit(\"change\", [changes, \"remote\"]);\n }\n }\n }\n }\n } catch {\n // Network error — retry next interval\n }\n schedulePoll();\n }\n\n function pollNow() {\n if (pauseWhenHidden && isDocumentHidden()) return;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n void poll();\n }\n\n function handleVisibilityChange() {\n if (document.visibilityState === \"visible\") {\n pollNow();\n } else if (pauseWhenHidden && timer) {\n clearTimeout(timer);\n timer = null;\n }\n }\n\n if (!pauseWhenHidden || !isDocumentHidden()) {\n void poll();\n }\n window.addEventListener(\"focus\", pollNow);\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n\n return () => {\n stopped = true;\n if (timer) clearTimeout(timer);\n window.removeEventListener(\"focus\", pollNow);\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n };\n }, [\n ydoc,\n awareness,\n docId,\n pollInterval,\n pauseWhenHidden,\n requestSource,\n baseUrl,\n docMissing,\n ]);\n\n return {\n ydoc,\n awareness,\n isLoading,\n isSynced,\n activeUsers,\n agentActive,\n agentPresent,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/collab/routes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;;;GAYzB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB;;;;;;GAsB3B,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc;;;;;;;;GA2BzB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;GA6BnC,CAAC"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/collab/routes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAcH;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;;;GA4BzB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB;;;;;;GAsB3B,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc;;;;;;;;GA2BzB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;GA6BnC,CAAC"}
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Mounted under /_agent-native/collab/ by the collab plugin.
5
5
  */
6
- import { defineEventHandler, setResponseStatus, getRouterParam } from "h3";
6
+ import { defineEventHandler, setResponseStatus, getRouterParam, getQuery, } from "h3";
7
7
  import * as manager from "./ydoc-manager.js";
8
8
  import { searchAndReplace as doSearchAndReplace } from "./ydoc-manager.js";
9
9
  import { uint8ArrayToBase64, base64ToUint8Array } from "./storage.js";
@@ -19,7 +19,21 @@ export const getCollabState = defineEventHandler(async (event) => {
19
19
  setResponseStatus(event, 400);
20
20
  return { error: "docId required" };
21
21
  }
22
- const state = await manager.getState(docId);
22
+ const query = getQuery(event);
23
+ const encodedStateVector = typeof query.stateVector === "string" ? query.stateVector : null;
24
+ let state;
25
+ if (encodedStateVector) {
26
+ try {
27
+ state = await manager.getIncUpdate(docId, base64ToUint8Array(encodedStateVector));
28
+ }
29
+ catch {
30
+ setResponseStatus(event, 400);
31
+ return { error: "stateVector must be base64-encoded" };
32
+ }
33
+ }
34
+ else {
35
+ state = await manager.getState(docId);
36
+ }
23
37
  return {
24
38
  docId,
25
39
  state: uint8ArrayToBase64(state),
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/collab/routes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * HTTP route handlers for collaborative editing.\n *\n * Mounted under /_agent-native/collab/ by the collab plugin.\n */\n\nimport { defineEventHandler, setResponseStatus, getRouterParam } from \"h3\";\nimport type { H3Event } from \"h3\";\nimport * as manager from \"./ydoc-manager.js\";\nimport { searchAndReplace as doSearchAndReplace } from \"./ydoc-manager.js\";\nimport { uint8ArrayToBase64, base64ToUint8Array } from \"./storage.js\";\nimport { readBody } from \"../server/h3-helpers.js\";\n\n/**\n * GET /_agent-native/collab/:docId/state\n *\n * Returns full Yjs document state as base64 for initial client load.\n */\nexport const getCollabState = defineEventHandler(async (event: H3Event) => {\n const docId = getRouterParam(event, \"docId\");\n if (!docId) {\n setResponseStatus(event, 400);\n return { error: \"docId required\" };\n }\n\n const state = await manager.getState(docId);\n return {\n docId,\n state: uint8ArrayToBase64(state),\n };\n});\n\n/**\n * POST /_agent-native/collab/:docId/update\n *\n * Client sends a Yjs update (base64). Server applies it, persists, and\n * emits a change event so other clients pick it up via polling.\n *\n * Body: { update: string (base64), requestSource?: string }\n */\nexport const postCollabUpdate = defineEventHandler(async (event: H3Event) => {\n const docId = getRouterParam(event, \"docId\");\n if (!docId) {\n setResponseStatus(event, 400);\n return { error: \"docId required\" };\n }\n\n const body = await readBody(event);\n const { update, requestSource } = body as {\n update?: string;\n requestSource?: string;\n };\n\n if (!update) {\n setResponseStatus(event, 400);\n return { error: \"update (base64) required\" };\n }\n\n const binary = base64ToUint8Array(update);\n await manager.applyUpdate(docId, binary, requestSource);\n\n return { ok: true };\n});\n\n/**\n * POST /_agent-native/collab/:docId/text\n *\n * Agent sends full text content. Server computes diff against current\n * Yjs state and applies minimal operations.\n *\n * Body: { text: string, fieldName?: string, requestSource?: string }\n */\nexport const postCollabText = defineEventHandler(async (event: H3Event) => {\n const docId = getRouterParam(event, \"docId\");\n if (!docId) {\n setResponseStatus(event, 400);\n return { error: \"docId required\" };\n }\n\n const body = await readBody(event);\n const { text, fieldName, requestSource } = body as {\n text?: string;\n fieldName?: string;\n requestSource?: string;\n };\n\n if (text === undefined) {\n setResponseStatus(event, 400);\n return { error: \"text required\" };\n }\n\n const result = await manager.applyText(\n docId,\n text,\n fieldName ?? \"content\",\n requestSource ?? \"agent\",\n );\n\n return { ok: true, text: result };\n});\n\n/**\n * POST /_agent-native/collab/:docId/search-replace\n *\n * Search-and-replace text in the Y.XmlFragment (ProseMirror tree).\n * Produces minimal Yjs operations for cursor-preserving updates.\n *\n * Body: { find: string, replace: string, requestSource?: string }\n */\nexport const postCollabSearchReplace = defineEventHandler(\n async (event: H3Event) => {\n const docId = getRouterParam(event, \"docId\");\n if (!docId) {\n setResponseStatus(event, 400);\n return { error: \"docId required\" };\n }\n\n const body = await readBody(event);\n const { find, replace, requestSource } = body as {\n find?: string;\n replace?: string;\n requestSource?: string;\n };\n\n if (!find) {\n setResponseStatus(event, 400);\n return { error: \"find required\" };\n }\n\n const result = await doSearchAndReplace(\n docId,\n find,\n replace ?? \"\",\n requestSource ?? \"agent\",\n );\n\n return { ok: true, found: result.found };\n },\n);\n"]}
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/collab/routes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * HTTP route handlers for collaborative editing.\n *\n * Mounted under /_agent-native/collab/ by the collab plugin.\n */\n\nimport {\n defineEventHandler,\n setResponseStatus,\n getRouterParam,\n getQuery,\n} from \"h3\";\nimport type { H3Event } from \"h3\";\nimport * as manager from \"./ydoc-manager.js\";\nimport { searchAndReplace as doSearchAndReplace } from \"./ydoc-manager.js\";\nimport { uint8ArrayToBase64, base64ToUint8Array } from \"./storage.js\";\nimport { readBody } from \"../server/h3-helpers.js\";\n\n/**\n * GET /_agent-native/collab/:docId/state\n *\n * Returns full Yjs document state as base64 for initial client load.\n */\nexport const getCollabState = defineEventHandler(async (event: H3Event) => {\n const docId = getRouterParam(event, \"docId\");\n if (!docId) {\n setResponseStatus(event, 400);\n return { error: \"docId required\" };\n }\n\n const query = getQuery(event);\n const encodedStateVector =\n typeof query.stateVector === \"string\" ? query.stateVector : null;\n let state: Uint8Array;\n if (encodedStateVector) {\n try {\n state = await manager.getIncUpdate(\n docId,\n base64ToUint8Array(encodedStateVector),\n );\n } catch {\n setResponseStatus(event, 400);\n return { error: \"stateVector must be base64-encoded\" };\n }\n } else {\n state = await manager.getState(docId);\n }\n return {\n docId,\n state: uint8ArrayToBase64(state),\n };\n});\n\n/**\n * POST /_agent-native/collab/:docId/update\n *\n * Client sends a Yjs update (base64). Server applies it, persists, and\n * emits a change event so other clients pick it up via polling.\n *\n * Body: { update: string (base64), requestSource?: string }\n */\nexport const postCollabUpdate = defineEventHandler(async (event: H3Event) => {\n const docId = getRouterParam(event, \"docId\");\n if (!docId) {\n setResponseStatus(event, 400);\n return { error: \"docId required\" };\n }\n\n const body = await readBody(event);\n const { update, requestSource } = body as {\n update?: string;\n requestSource?: string;\n };\n\n if (!update) {\n setResponseStatus(event, 400);\n return { error: \"update (base64) required\" };\n }\n\n const binary = base64ToUint8Array(update);\n await manager.applyUpdate(docId, binary, requestSource);\n\n return { ok: true };\n});\n\n/**\n * POST /_agent-native/collab/:docId/text\n *\n * Agent sends full text content. Server computes diff against current\n * Yjs state and applies minimal operations.\n *\n * Body: { text: string, fieldName?: string, requestSource?: string }\n */\nexport const postCollabText = defineEventHandler(async (event: H3Event) => {\n const docId = getRouterParam(event, \"docId\");\n if (!docId) {\n setResponseStatus(event, 400);\n return { error: \"docId required\" };\n }\n\n const body = await readBody(event);\n const { text, fieldName, requestSource } = body as {\n text?: string;\n fieldName?: string;\n requestSource?: string;\n };\n\n if (text === undefined) {\n setResponseStatus(event, 400);\n return { error: \"text required\" };\n }\n\n const result = await manager.applyText(\n docId,\n text,\n fieldName ?? \"content\",\n requestSource ?? \"agent\",\n );\n\n return { ok: true, text: result };\n});\n\n/**\n * POST /_agent-native/collab/:docId/search-replace\n *\n * Search-and-replace text in the Y.XmlFragment (ProseMirror tree).\n * Produces minimal Yjs operations for cursor-preserving updates.\n *\n * Body: { find: string, replace: string, requestSource?: string }\n */\nexport const postCollabSearchReplace = defineEventHandler(\n async (event: H3Event) => {\n const docId = getRouterParam(event, \"docId\");\n if (!docId) {\n setResponseStatus(event, 400);\n return { error: \"docId required\" };\n }\n\n const body = await readBody(event);\n const { find, replace, requestSource } = body as {\n find?: string;\n replace?: string;\n requestSource?: string;\n };\n\n if (!find) {\n setResponseStatus(event, 400);\n return { error: \"find required\" };\n }\n\n const result = await doSearchAndReplace(\n docId,\n find,\n replace ?? \"\",\n requestSource ?? \"agent\",\n );\n\n return { ok: true, found: result.found };\n },\n);\n"]}
@@ -4,8 +4,16 @@
4
4
  * Uses a framework-level `_collab_docs` table (TEXT columns with base64
5
5
  * encoding for binary Yjs state) that works across SQLite and Postgres.
6
6
  */
7
+ export interface YDocStateRecord {
8
+ state: Uint8Array;
9
+ version: number;
10
+ }
11
+ /** Load Yjs state plus optimistic concurrency version. */
12
+ export declare function loadYDocRecord(docId: string): Promise<YDocStateRecord | null>;
7
13
  /** Load Yjs state as Uint8Array, or null if not found. */
8
14
  export declare function loadYDocState(docId: string): Promise<Uint8Array | null>;
15
+ /** Save only if the stored row still has the version the caller merged from. */
16
+ export declare function trySaveYDocState(docId: string, state: Uint8Array, textSnapshot: string, expectedVersion: number | null): Promise<boolean>;
9
17
  /** Save Yjs state (Uint8Array) and a plain-text snapshot. */
10
18
  export declare function saveYDocState(docId: string, state: Uint8Array, textSnapshot: string): Promise<void>;
11
19
  /** Check if a document has collaborative state. */
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/collab/storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAwBH,0DAA0D;AAC1D,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAS7E;AAED,6DAA6D;AAC7D,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,UAAU,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,mDAAmD;AACnD,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQpE;AAED,iDAAiD;AACjD,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOpE;AAID,iBAAS,kBAAkB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAUnD;AAED,iBAAS,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAUnD;AAED,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/collab/storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgCH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,0DAA0D;AAC1D,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAYjC;AAED,0DAA0D;AAC1D,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAG7E;AAED,gFAAgF;AAChF,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,UAAU,EACjB,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,MAAM,GAAG,IAAI,GAC7B,OAAO,CAAC,OAAO,CAAC,CAoBlB;AAED,6DAA6D;AAC7D,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,UAAU,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED,mDAAmD;AACnD,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQpE;AAED,iDAAiD;AACjD,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOpE;AAID,iBAAS,kBAAkB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAUnD;AAED,iBAAS,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAUnD;AAED,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,CAAC"}
@@ -16,24 +16,60 @@ async function ensureTable() {
16
16
  doc_id TEXT PRIMARY KEY,
17
17
  yjs_state TEXT NOT NULL,
18
18
  text_snapshot TEXT NOT NULL DEFAULT '',
19
+ version INTEGER NOT NULL DEFAULT 0,
19
20
  updated_at TEXT NOT NULL DEFAULT (${nowDefault})
20
21
  )
21
22
  `);
23
+ try {
24
+ await client.execute(`ALTER TABLE _collab_docs ADD COLUMN version INTEGER NOT NULL DEFAULT 0`);
25
+ }
26
+ catch {
27
+ // Existing deployments already have the column after the first run.
28
+ }
22
29
  })();
23
30
  }
24
31
  return _initPromise;
25
32
  }
26
- /** Load Yjs state as Uint8Array, or null if not found. */
27
- export async function loadYDocState(docId) {
33
+ /** Load Yjs state plus optimistic concurrency version. */
34
+ export async function loadYDocRecord(docId) {
28
35
  await ensureTable();
29
36
  const client = getDbExec();
30
37
  const { rows } = await client.execute({
31
- sql: `SELECT yjs_state FROM _collab_docs WHERE doc_id = ?`,
38
+ sql: `SELECT yjs_state, version FROM _collab_docs WHERE doc_id = ?`,
32
39
  args: [docId],
33
40
  });
34
41
  if (rows.length === 0)
35
42
  return null;
36
- return base64ToUint8Array(rows[0].yjs_state);
43
+ return {
44
+ state: base64ToUint8Array(rows[0].yjs_state),
45
+ version: Number(rows[0].version ?? 0),
46
+ };
47
+ }
48
+ /** Load Yjs state as Uint8Array, or null if not found. */
49
+ export async function loadYDocState(docId) {
50
+ const record = await loadYDocRecord(docId);
51
+ return record?.state ?? null;
52
+ }
53
+ /** Save only if the stored row still has the version the caller merged from. */
54
+ export async function trySaveYDocState(docId, state, textSnapshot, expectedVersion) {
55
+ await ensureTable();
56
+ const client = getDbExec();
57
+ const b64 = uint8ArrayToBase64(state);
58
+ const nowExpr = isPostgres() ? "NOW()::text" : "datetime('now')";
59
+ if (expectedVersion === null) {
60
+ const result = await client.execute({
61
+ sql: isPostgres()
62
+ ? `INSERT INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr}) ON CONFLICT (doc_id) DO NOTHING`
63
+ : `INSERT OR IGNORE INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr})`,
64
+ args: [docId, b64, textSnapshot],
65
+ });
66
+ return result.rowsAffected > 0;
67
+ }
68
+ const result = await client.execute({
69
+ sql: `UPDATE _collab_docs SET yjs_state = ?, text_snapshot = ?, version = version + 1, updated_at = ${nowExpr} WHERE doc_id = ? AND version = ?`,
70
+ args: [b64, textSnapshot, docId, expectedVersion],
71
+ });
72
+ return result.rowsAffected > 0;
37
73
  }
38
74
  /** Save Yjs state (Uint8Array) and a plain-text snapshot. */
39
75
  export async function saveYDocState(docId, state, textSnapshot) {
@@ -41,12 +77,24 @@ export async function saveYDocState(docId, state, textSnapshot) {
41
77
  const client = getDbExec();
42
78
  const b64 = uint8ArrayToBase64(state);
43
79
  const nowExpr = isPostgres() ? "NOW()::text" : "datetime('now')";
44
- await client.execute({
80
+ const updated = await client.execute({
81
+ sql: `UPDATE _collab_docs SET yjs_state = ?, text_snapshot = ?, version = version + 1, updated_at = ${nowExpr} WHERE doc_id = ?`,
82
+ args: [b64, textSnapshot, docId],
83
+ });
84
+ if (updated.rowsAffected > 0)
85
+ return;
86
+ const inserted = await client.execute({
45
87
  sql: isPostgres()
46
- ? `INSERT INTO _collab_docs (doc_id, yjs_state, text_snapshot, updated_at) VALUES (?, ?, ?, ${nowExpr}) ON CONFLICT (doc_id) DO UPDATE SET yjs_state = EXCLUDED.yjs_state, text_snapshot = EXCLUDED.text_snapshot, updated_at = EXCLUDED.updated_at`
47
- : `INSERT OR REPLACE INTO _collab_docs (doc_id, yjs_state, text_snapshot, updated_at) VALUES (?, ?, ?, ${nowExpr})`,
88
+ ? `INSERT INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr}) ON CONFLICT (doc_id) DO NOTHING`
89
+ : `INSERT OR IGNORE INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr})`,
48
90
  args: [docId, b64, textSnapshot],
49
91
  });
92
+ if (inserted.rowsAffected > 0)
93
+ return;
94
+ await client.execute({
95
+ sql: `UPDATE _collab_docs SET yjs_state = ?, text_snapshot = ?, version = version + 1, updated_at = ${nowExpr} WHERE doc_id = ?`,
96
+ args: [b64, textSnapshot, docId],
97
+ });
50
98
  }
51
99
  /** Check if a document has collaborative state. */
52
100
  export async function hasCollabState(docId) {
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/collab/storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SQL storage for Yjs collaborative document state.\n *\n * Uses a framework-level `_collab_docs` table (TEXT columns with base64\n * encoding for binary Yjs state) that works across SQLite and Postgres.\n */\n\nimport { getDbExec, isPostgres } from \"../db/client.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const nowDefault = isPostgres() ? \"NOW()::text\" : \"datetime('now')\";\n await client.execute(`\n CREATE TABLE IF NOT EXISTS _collab_docs (\n doc_id TEXT PRIMARY KEY,\n yjs_state TEXT NOT NULL,\n text_snapshot TEXT NOT NULL DEFAULT '',\n updated_at TEXT NOT NULL DEFAULT (${nowDefault})\n )\n `);\n })();\n }\n return _initPromise;\n}\n\n/** Load Yjs state as Uint8Array, or null if not found. */\nexport async function loadYDocState(docId: string): Promise<Uint8Array | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT yjs_state FROM _collab_docs WHERE doc_id = ?`,\n args: [docId],\n });\n if (rows.length === 0) return null;\n return base64ToUint8Array(rows[0].yjs_state as string);\n}\n\n/** Save Yjs state (Uint8Array) and a plain-text snapshot. */\nexport async function saveYDocState(\n docId: string,\n state: Uint8Array,\n textSnapshot: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const b64 = uint8ArrayToBase64(state);\n const nowExpr = isPostgres() ? \"NOW()::text\" : \"datetime('now')\";\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO _collab_docs (doc_id, yjs_state, text_snapshot, updated_at) VALUES (?, ?, ?, ${nowExpr}) ON CONFLICT (doc_id) DO UPDATE SET yjs_state = EXCLUDED.yjs_state, text_snapshot = EXCLUDED.text_snapshot, updated_at = EXCLUDED.updated_at`\n : `INSERT OR REPLACE INTO _collab_docs (doc_id, yjs_state, text_snapshot, updated_at) VALUES (?, ?, ?, ${nowExpr})`,\n args: [docId, b64, textSnapshot],\n });\n}\n\n/** Check if a document has collaborative state. */\nexport async function hasCollabState(docId: string): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT 1 FROM _collab_docs WHERE doc_id = ?`,\n args: [docId],\n });\n return rows.length > 0;\n}\n\n/** Delete collaborative state for a document. */\nexport async function deleteCollabState(docId: string): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n await client.execute({\n sql: `DELETE FROM _collab_docs WHERE doc_id = ?`,\n args: [docId],\n });\n}\n\n// ─── Base64 helpers ──────────────────────────────────────────────────\n\nfunction uint8ArrayToBase64(arr: Uint8Array): string {\n // Works in both Node.js and edge runtimes\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(arr).toString(\"base64\");\n }\n let binary = \"\";\n for (let i = 0; i < arr.length; i++) {\n binary += String.fromCharCode(arr[i]);\n }\n return btoa(binary);\n}\n\nfunction base64ToUint8Array(b64: string): Uint8Array {\n if (typeof Buffer !== \"undefined\") {\n return new Uint8Array(Buffer.from(b64, \"base64\"));\n }\n const binary = atob(b64);\n const arr = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n arr[i] = binary.charCodeAt(i);\n }\n return arr;\n}\n\nexport { uint8ArrayToBase64, base64ToUint8Array };\n"]}
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/collab/storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SQL storage for Yjs collaborative document state.\n *\n * Uses a framework-level `_collab_docs` table (TEXT columns with base64\n * encoding for binary Yjs state) that works across SQLite and Postgres.\n */\n\nimport { getDbExec, isPostgres } from \"../db/client.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const nowDefault = isPostgres() ? \"NOW()::text\" : \"datetime('now')\";\n await client.execute(`\n CREATE TABLE IF NOT EXISTS _collab_docs (\n doc_id TEXT PRIMARY KEY,\n yjs_state TEXT NOT NULL,\n text_snapshot TEXT NOT NULL DEFAULT '',\n version INTEGER NOT NULL DEFAULT 0,\n updated_at TEXT NOT NULL DEFAULT (${nowDefault})\n )\n `);\n try {\n await client.execute(\n `ALTER TABLE _collab_docs ADD COLUMN version INTEGER NOT NULL DEFAULT 0`,\n );\n } catch {\n // Existing deployments already have the column after the first run.\n }\n })();\n }\n return _initPromise;\n}\n\nexport interface YDocStateRecord {\n state: Uint8Array;\n version: number;\n}\n\n/** Load Yjs state plus optimistic concurrency version. */\nexport async function loadYDocRecord(\n docId: string,\n): Promise<YDocStateRecord | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT yjs_state, version FROM _collab_docs WHERE doc_id = ?`,\n args: [docId],\n });\n if (rows.length === 0) return null;\n return {\n state: base64ToUint8Array(rows[0].yjs_state as string),\n version: Number(rows[0].version ?? 0),\n };\n}\n\n/** Load Yjs state as Uint8Array, or null if not found. */\nexport async function loadYDocState(docId: string): Promise<Uint8Array | null> {\n const record = await loadYDocRecord(docId);\n return record?.state ?? null;\n}\n\n/** Save only if the stored row still has the version the caller merged from. */\nexport async function trySaveYDocState(\n docId: string,\n state: Uint8Array,\n textSnapshot: string,\n expectedVersion: number | null,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const b64 = uint8ArrayToBase64(state);\n const nowExpr = isPostgres() ? \"NOW()::text\" : \"datetime('now')\";\n if (expectedVersion === null) {\n const result = await client.execute({\n sql: isPostgres()\n ? `INSERT INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr}) ON CONFLICT (doc_id) DO NOTHING`\n : `INSERT OR IGNORE INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr})`,\n args: [docId, b64, textSnapshot],\n });\n return result.rowsAffected > 0;\n }\n\n const result = await client.execute({\n sql: `UPDATE _collab_docs SET yjs_state = ?, text_snapshot = ?, version = version + 1, updated_at = ${nowExpr} WHERE doc_id = ? AND version = ?`,\n args: [b64, textSnapshot, docId, expectedVersion],\n });\n return result.rowsAffected > 0;\n}\n\n/** Save Yjs state (Uint8Array) and a plain-text snapshot. */\nexport async function saveYDocState(\n docId: string,\n state: Uint8Array,\n textSnapshot: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const b64 = uint8ArrayToBase64(state);\n const nowExpr = isPostgres() ? \"NOW()::text\" : \"datetime('now')\";\n const updated = await client.execute({\n sql: `UPDATE _collab_docs SET yjs_state = ?, text_snapshot = ?, version = version + 1, updated_at = ${nowExpr} WHERE doc_id = ?`,\n args: [b64, textSnapshot, docId],\n });\n if (updated.rowsAffected > 0) return;\n\n const inserted = await client.execute({\n sql: isPostgres()\n ? `INSERT INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr}) ON CONFLICT (doc_id) DO NOTHING`\n : `INSERT OR IGNORE INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr})`,\n args: [docId, b64, textSnapshot],\n });\n if (inserted.rowsAffected > 0) return;\n\n await client.execute({\n sql: `UPDATE _collab_docs SET yjs_state = ?, text_snapshot = ?, version = version + 1, updated_at = ${nowExpr} WHERE doc_id = ?`,\n args: [b64, textSnapshot, docId],\n });\n}\n\n/** Check if a document has collaborative state. */\nexport async function hasCollabState(docId: string): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT 1 FROM _collab_docs WHERE doc_id = ?`,\n args: [docId],\n });\n return rows.length > 0;\n}\n\n/** Delete collaborative state for a document. */\nexport async function deleteCollabState(docId: string): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n await client.execute({\n sql: `DELETE FROM _collab_docs WHERE doc_id = ?`,\n args: [docId],\n });\n}\n\n// ─── Base64 helpers ──────────────────────────────────────────────────\n\nfunction uint8ArrayToBase64(arr: Uint8Array): string {\n // Works in both Node.js and edge runtimes\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(arr).toString(\"base64\");\n }\n let binary = \"\";\n for (let i = 0; i < arr.length; i++) {\n binary += String.fromCharCode(arr[i]);\n }\n return btoa(binary);\n}\n\nfunction base64ToUint8Array(b64: string): Uint8Array {\n if (typeof Buffer !== \"undefined\") {\n return new Uint8Array(Buffer.from(b64, \"base64\"));\n }\n const binary = atob(b64);\n const arr = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n arr[i] = binary.charCodeAt(i);\n }\n return arr;\n}\n\nexport { uint8ArrayToBase64, base64ToUint8Array };\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ydoc-manager.d.ts","sourceRoot":"","sources":["../../src/collab/ydoc-manager.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAIzB,OAAO,EAKL,KAAK,OAAO,EACb,MAAM,kBAAkB,CAAC;AAgC1B;;GAEG;AACH,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAgB1D;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,UAAU,EAClB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,MAAsB,EACjC,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,CAcjB;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC,CA6BjD;AAED;;GAEG;AACH,wBAAsB,OAAO,CAC3B,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAAsB,GAChC,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAGjE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,iBAAiB,EAAE,UAAU,GAC5B,OAAO,CAAC,UAAU,CAAC,CAGrB;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,MAAsB,GAChC,OAAO,CAAC,IAAI,CAAC,CAUf;AAID;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,GAAG,EACZ,SAAS,GAAE,MAAe,EAC1B,IAAI,GAAE,KAAK,GAAG,OAAe,EAC7B,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,OAAO,EAAE,EACd,SAAS,GAAE,MAAe,EAC1B,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;GAEG;AACH,wBAAsB,OAAO,CAC3B,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAAe,GACzB,OAAO,CAAC,GAAG,CAAC,CAGd;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,GAAG,EACT,SAAS,GAAE,MAAe,EAC1B,IAAI,GAAE,KAAK,GAAG,OAAe,GAC5B,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAM9C"}
1
+ {"version":3,"file":"ydoc-manager.d.ts","sourceRoot":"","sources":["../../src/collab/ydoc-manager.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AASzB,OAAO,EAKL,KAAK,OAAO,EACb,MAAM,kBAAkB,CAAC;AAuF1B;;GAEG;AACH,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAgB1D;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,UAAU,EAClB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CAYf;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,MAAsB,EACjC,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC,CA6BjD;AAED;;GAEG;AACH,wBAAsB,OAAO,CAC3B,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAAsB,GAChC,OAAO,CAAC,MAAM,CAAC,CAIjB;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAIjE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,iBAAiB,EAAE,UAAU,GAC5B,OAAO,CAAC,UAAU,CAAC,CAIrB;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,MAAsB,GAChC,OAAO,CAAC,IAAI,CAAC,CAYf;AAID;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,GAAG,EACZ,SAAS,GAAE,MAAe,EAC1B,IAAI,GAAE,KAAK,GAAG,OAAe,EAC7B,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CAYf;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,OAAO,EAAE,EACd,SAAS,GAAE,MAAe,EAC1B,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CAcf;AAED;;GAEG;AACH,wBAAsB,OAAO,CAC3B,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAAe,GACzB,OAAO,CAAC,GAAG,CAAC,CAId;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,GAAG,EACT,SAAS,GAAE,MAAe,EAC1B,IAAI,GAAE,KAAK,GAAG,OAAe,GAC5B,OAAO,CAAC,IAAI,CAAC,CAYf;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAM9C"}