@databricks/appkit-ui 0.61.1 → 0.63.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 (123) hide show
  1. package/CLAUDE.md +1 -0
  2. package/dist/cli/commands/codemod/index.js.map +1 -1
  3. package/dist/cli/commands/codemod/on-plugins-ready.js.map +1 -1
  4. package/dist/cli/commands/docs.js.map +1 -1
  5. package/dist/cli/commands/doctor/bundle.js.map +1 -1
  6. package/dist/cli/commands/doctor/index.js.map +1 -1
  7. package/dist/cli/commands/doctor/report.js.map +1 -1
  8. package/dist/cli/commands/doctor/resolve-targets.js.map +1 -1
  9. package/dist/cli/commands/doctor/run.js.map +1 -1
  10. package/dist/cli/commands/generate-types.js.map +1 -1
  11. package/dist/cli/commands/lint.js.map +1 -1
  12. package/dist/cli/commands/plugin/add-resource/add-resource.js.map +1 -1
  13. package/dist/cli/commands/plugin/create/create.js.map +1 -1
  14. package/dist/cli/commands/plugin/create/prompt-resource.js.map +1 -1
  15. package/dist/cli/commands/plugin/create/scaffold.js.map +1 -1
  16. package/dist/cli/commands/plugin/index.js.map +1 -1
  17. package/dist/cli/commands/plugin/list/list.js.map +1 -1
  18. package/dist/cli/commands/plugin/promote/promote.js.map +1 -1
  19. package/dist/cli/commands/plugin/sync/sync.js.map +1 -1
  20. package/dist/cli/commands/plugin/validate/validate-manifest.js.map +1 -1
  21. package/dist/cli/commands/plugin/validate/validate.js.map +1 -1
  22. package/dist/cli/commands/registry/add.js.map +1 -1
  23. package/dist/cli/commands/registry/client.js.map +1 -1
  24. package/dist/cli/commands/registry/config-writer.js.map +1 -1
  25. package/dist/cli/commands/registry/env-reconcile.js.map +1 -1
  26. package/dist/cli/commands/registry/env-writer.js.map +1 -1
  27. package/dist/cli/commands/registry/index.js.map +1 -1
  28. package/dist/cli/commands/registry/info.js.map +1 -1
  29. package/dist/cli/commands/registry/list.js.map +1 -1
  30. package/dist/cli/commands/registry/requirements.js.map +1 -1
  31. package/dist/cli/commands/registry/server-register.js.map +1 -1
  32. package/dist/cli/commands/registry/workspace-picker.js.map +1 -1
  33. package/dist/cli/commands/setup.js.map +1 -1
  34. package/dist/cli/index.js.map +1 -1
  35. package/dist/js/arrow/arrow-client.d.ts.map +1 -1
  36. package/dist/js/arrow/arrow-client.js.map +1 -1
  37. package/dist/react/charts/area/index.d.ts.map +1 -1
  38. package/dist/react/charts/area/index.js.map +1 -1
  39. package/dist/react/charts/bar/index.d.ts.map +1 -1
  40. package/dist/react/charts/bar/index.js.map +1 -1
  41. package/dist/react/charts/base.d.ts.map +1 -1
  42. package/dist/react/charts/base.js +1 -1
  43. package/dist/react/charts/base.js.map +1 -1
  44. package/dist/react/charts/heatmap/index.d.ts.map +1 -1
  45. package/dist/react/charts/heatmap/index.js.map +1 -1
  46. package/dist/react/charts/line/index.d.ts.map +1 -1
  47. package/dist/react/charts/line/index.js.map +1 -1
  48. package/dist/react/charts/normalize.d.ts.map +1 -1
  49. package/dist/react/charts/normalize.js.map +1 -1
  50. package/dist/react/charts/pie/index.d.ts.map +1 -1
  51. package/dist/react/charts/pie/index.js.map +1 -1
  52. package/dist/react/charts/radar/index.d.ts.map +1 -1
  53. package/dist/react/charts/radar/index.js.map +1 -1
  54. package/dist/react/charts/scatter/index.d.ts.map +1 -1
  55. package/dist/react/charts/scatter/index.js.map +1 -1
  56. package/dist/react/charts/theme.d.ts.map +1 -1
  57. package/dist/react/charts/theme.js.map +1 -1
  58. package/dist/react/charts/wrapper.d.ts.map +1 -1
  59. package/dist/react/charts/wrapper.js.map +1 -1
  60. package/dist/react/file-browser/directory-list.d.ts.map +1 -1
  61. package/dist/react/file-browser/directory-list.js.map +1 -1
  62. package/dist/react/file-browser/file-breadcrumb.d.ts.map +1 -1
  63. package/dist/react/file-browser/file-breadcrumb.js.map +1 -1
  64. package/dist/react/file-browser/file-entry.d.ts.map +1 -1
  65. package/dist/react/file-browser/file-entry.js.map +1 -1
  66. package/dist/react/file-browser/file-preview-panel.d.ts.map +1 -1
  67. package/dist/react/file-browser/file-preview-panel.js.map +1 -1
  68. package/dist/react/file-browser/new-folder-input.d.ts.map +1 -1
  69. package/dist/react/file-browser/new-folder-input.js.map +1 -1
  70. package/dist/react/genie/genie-chat-input.d.ts.map +1 -1
  71. package/dist/react/genie/genie-chat-input.js.map +1 -1
  72. package/dist/react/genie/genie-chat-message-list.d.ts.map +1 -1
  73. package/dist/react/genie/genie-chat-message-list.js.map +1 -1
  74. package/dist/react/genie/genie-chat-message.d.ts.map +1 -1
  75. package/dist/react/genie/genie-chat-message.js.map +1 -1
  76. package/dist/react/genie/genie-query-visualization.d.ts.map +1 -1
  77. package/dist/react/genie/genie-query-visualization.js.map +1 -1
  78. package/dist/react/genie/use-genie-chat.d.ts.map +1 -1
  79. package/dist/react/genie/use-genie-chat.js.map +1 -1
  80. package/dist/react/hooks/analytics-request-store.js +144 -0
  81. package/dist/react/hooks/analytics-request-store.js.map +1 -0
  82. package/dist/react/hooks/request-store.js +92 -0
  83. package/dist/react/hooks/request-store.js.map +1 -0
  84. package/dist/react/hooks/types.d.ts.map +1 -1
  85. package/dist/react/hooks/use-agent-chat.d.ts.map +1 -1
  86. package/dist/react/hooks/use-agent-chat.js.map +1 -1
  87. package/dist/react/hooks/use-ai-search-query.d.ts.map +1 -1
  88. package/dist/react/hooks/use-ai-search-query.js.map +1 -1
  89. package/dist/react/hooks/use-analytics-query.d.ts +6 -0
  90. package/dist/react/hooks/use-analytics-query.d.ts.map +1 -1
  91. package/dist/react/hooks/use-analytics-query.js +34 -143
  92. package/dist/react/hooks/use-analytics-query.js.map +1 -1
  93. package/dist/react/hooks/use-analytics-warehouse-status.js.map +1 -1
  94. package/dist/react/hooks/use-chart-data.d.ts.map +1 -1
  95. package/dist/react/hooks/use-chart-data.js.map +1 -1
  96. package/dist/react/hooks/use-metric-view.d.ts.map +1 -1
  97. package/dist/react/hooks/use-metric-view.js.map +1 -1
  98. package/dist/react/hooks/use-plugin-config.d.ts.map +1 -1
  99. package/dist/react/hooks/use-plugin-config.js.map +1 -1
  100. package/dist/react/hooks/use-serving-invoke.d.ts.map +1 -1
  101. package/dist/react/hooks/use-serving-invoke.js.map +1 -1
  102. package/dist/react/hooks/use-serving-stream.d.ts.map +1 -1
  103. package/dist/react/hooks/use-serving-stream.js.map +1 -1
  104. package/dist/react/resource-status-indicator.d.ts.map +1 -1
  105. package/dist/react/resource-status-indicator.js.map +1 -1
  106. package/dist/react/table/data-table.d.ts.map +1 -1
  107. package/dist/react/table/data-table.js.map +1 -1
  108. package/dist/react/table/table-wrapper.js.map +1 -1
  109. package/dist/react/ui/button.d.ts +1 -1
  110. package/dist/react/ui/input-group.d.ts +1 -1
  111. package/dist/react/ui-variants/variants.d.ts.map +1 -1
  112. package/dist/react/ui-variants/variants.js.map +1 -1
  113. package/dist/schemas/manifest.d.ts.map +1 -1
  114. package/dist/schemas/manifest.js.map +1 -1
  115. package/dist/schemas/metric-fqn.js.map +1 -1
  116. package/dist/shared/src/plugin.d.ts.map +1 -1
  117. package/dist/styles.css +0 -3
  118. package/dist/workspace-client/legacy.js.map +1 -1
  119. package/dist/workspace-client/types.d.ts.map +1 -1
  120. package/docs/plugins/testing.md +245 -0
  121. package/llms.txt +1 -0
  122. package/package.json +1 -1
  123. package/sbom.cdx.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/react/hooks/types.ts"],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/react/hooks/types.ts"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-agent-chat.d.ts","names":[],"sources":["../../../src/react/hooks/use-agent-chat.ts"],"mappings":";;AAeA;;;;;;;;;;;UAAiB,cAAA;EACf,IAAA;EACA,KAAA;EACA,OAAA;EACA,IAAA;IACE,IAAA;IACA,EAAA;IACA,OAAA;IACA,IAAA;IACA,SAAA;IACA,MAAA;IACA,MAAA;EAAA;EAEF,OAAA;EACA,IAAA,GAAO,MAAA;EACP,KAAA;EACA,eAAA;EACA,YAAA;EAEA,WAAA;EACA,SAAA;EACA,SAAA;EACA,IAAA;EACA,WAAA,GAAc,MAAA;AAAA;AAAA,UAGC,mBAAA;EAmBG;;;;AAGpB;EAhBE,KAAA;;;;;;EAMA,QAAA;EA2BA;;;;;;EApBA,OAAA,IAAW,KAAA,EAAO,cAAA;AAAA;AAAA,UAGH,kBAAA;EA+DW;EA7D1B,OAAA;EA8DA;;;;;;EAvDA,MAAA,EAAQ,cAAA;EAuDR;;;;;EAjDA,QAAA;EAoDC;EAlDD,WAAA;EAkDyC;EAhDzC,KAAA;;;;;EAKA,IAAA,GAAO,OAAA,aAAoB,OAAA;;;;;;EAM3B,KAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiCc,YAAA,CAAA;EACd,KAAA;EACA,QAAA;EACA;AAAA,GACC,mBAAA,GAAsB,kBAAA"}
1
+ {"version":3,"file":"use-agent-chat.d.ts","names":[],"sources":["../../../src/react/hooks/use-agent-chat.ts"],"mappings":";;AAgBA;;;;;;;;;;;UAAiB,cAAA;EACf,IAAA;EACA,KAAA;EACA,OAAA;EACA,IAAA;IACE,IAAA;IACA,EAAA;IACA,OAAA;IACA,IAAA;IACA,SAAA;IACA,MAAA;IACA,MAAA;EAAA;EAEF,OAAA;EACA,IAAA,GAAO,MAAA;EACP,KAAA;EACA,eAAA;EACA,YAAA;EAEA,WAAA;EACA,SAAA;EACA,SAAA;EACA,IAAA;EACA,WAAA,GAAc,MAAA;AAAA;AAAA,UAGC,mBAAA;EAmBG;;;;AAGpB;EAhBE,KAAA;;;;;;EAMA,QAAA;EA2BA;;;;;;EApBA,OAAA,IAAW,KAAA,EAAO,cAAA;AAAA;AAAA,UAGH,kBAAA;EA+DW;EA7D1B,OAAA;EA8DA;;;;;;EAvDA,MAAA,EAAQ,cAAA;EAuDR;;;;;EAjDA,QAAA;EAoDC;EAlDD,WAAA;EAkDyC;EAhDzC,KAAA;;;;;EAKA,IAAA,GAAO,OAAA,aAAoB,OAAA;;;;;;EAM3B,KAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiCc,YAAA,CAAA;EACd,KAAA;EACA,QAAA;EACA;AAAA,GACC,mBAAA,GAAsB,kBAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-agent-chat.js","names":[],"sources":["../../../src/react/hooks/use-agent-chat.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { connectSSE } from \"@/js\";\n\n/**\n * One Responses-API-shaped event yielded by the agents plugin SSE stream.\n *\n * The hook handles the two paths every chat UI needs — accumulating\n * `content` from `response.output_text.delta` and capturing `threadId`\n * from `appkit.metadata` — and surfaces everything else (tool calls,\n * approval gates, status events, etc.) through {@link UseAgentChatOptions.onEvent}.\n *\n * Fields beyond `type` are intentionally loose because the agents plugin\n * forwards adapter-specific shapes verbatim. Treat unknown fields as\n * opaque pass-through.\n */\nexport interface AgentChatEvent {\n type: string;\n delta?: string;\n item_id?: string;\n item?: {\n type?: string;\n id?: string;\n call_id?: string;\n name?: string;\n arguments?: string;\n output?: string;\n status?: string;\n };\n content?: string;\n data?: Record<string, unknown>;\n error?: string;\n sequence_number?: number;\n output_index?: number;\n // `appkit.approval_pending` payload\n approval_id?: string;\n stream_id?: string;\n tool_name?: string;\n args?: unknown;\n annotations?: Record<string, unknown>;\n}\n\nexport interface UseAgentChatOptions {\n /**\n * Agent name registered with the `agents()` plugin (e.g. `\"assistant\"`,\n * `\"helper\"`). Send-time payload includes this so the plugin routes the\n * turn to the right `AgentDefinition`.\n */\n agent: string;\n /**\n * Override the chat endpoint. Default `\"/api/agents/chat\"` matches the\n * route the agents plugin mounts under its prefix. Useful when the\n * server mounts under a non-default base path or when proxying.\n */\n endpoint?: string;\n /**\n * Called for every parsed SSE event before any state update. Use this\n * to drive tool-call rows, approval cards, inspectors, or anything\n * beyond the streaming text content. Errors thrown here are swallowed\n * so a buggy handler can't kill the stream.\n */\n onEvent?: (event: AgentChatEvent) => void;\n}\n\nexport interface UseAgentChatResult {\n /** Accumulated assistant text from `response.output_text.delta` events. */\n content: string;\n /**\n * Every parsed event, in order. Provided for components that need to\n * render historical tool calls or replay state after a remount —\n * lighter than re-deriving from message history. For one-off side\n * effects prefer {@link UseAgentChatOptions.onEvent}.\n */\n events: AgentChatEvent[];\n /**\n * Thread id captured from the first `appkit.metadata` event of the\n * stream. Subsequent `send()` calls automatically forward this so the\n * server reuses the same thread.\n */\n threadId: string | null;\n /** True while an SSE stream is open. */\n isStreaming: boolean;\n /** Last error message (cleared on next successful `send()`). */\n error: string | null;\n /**\n * Send a user turn and stream the response. Aborts any in-flight\n * stream. Resolves when the stream completes (success or error).\n */\n send: (message: string) => Promise<void>;\n /**\n * Discard accumulated content, events, and threadId. Aborts any\n * in-flight stream. Use when switching agents or starting a fresh\n * conversation.\n */\n reset: () => void;\n}\n\n/**\n * React hook for chatting with an agent registered via the `agents()`\n * plugin. Wraps {@link connectSSE} (which owns the buffer cap, abort\n * composition, retry/backoff, and frame parsing) with the small amount\n * of stateful glue every chat UI needs: accumulated assistant text,\n * thread id, streaming flag, and an event callback.\n *\n * The hook is intentionally lower-level than a full chat component —\n * it owns one stream at a time, not a multi-turn message history. The\n * caller composes its own messages array (typically a `useState`) and\n * appends to it via the `onEvent` callback for tool calls and via the\n * `content` field for assistant text.\n *\n * @example\n * ```tsx\n * function Chat({ agent }: { agent: string }) {\n * const [messages, setMessages] = useState<Message[]>([]);\n * const { content, threadId, isStreaming, send, reset } = useAgentChat({\n * agent,\n * onEvent(ev) {\n * if (ev.type === \"response.output_item.added\" && ev.item?.type === \"function_call\") {\n * setMessages((m) => [...m, { role: \"tool\", name: ev.item?.name, args: ev.item?.arguments }]);\n * }\n * },\n * });\n * // `content` reflects the latest assistant turn; reset() between conversations.\n * // ...\n * }\n * ```\n */\nexport function useAgentChat({\n agent,\n endpoint = \"/api/agents/chat\",\n onEvent,\n}: UseAgentChatOptions): UseAgentChatResult {\n const [content, setContent] = useState(\"\");\n const [events, setEvents] = useState<AgentChatEvent[]>([]);\n const [threadId, setThreadId] = useState<string | null>(null);\n const [isStreaming, setIsStreaming] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Refs avoid the standard \"stale closure\" problem with `send` and\n // `onEvent`: `send` is a stable callback that reads the latest\n // threadId/onEvent without re-mounting connectSSE on every render.\n const threadIdRef = useRef<string | null>(null);\n const contentRef = useRef(\"\");\n const onEventRef = useRef(onEvent);\n onEventRef.current = onEvent;\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const reset = useCallback(() => {\n abortControllerRef.current?.abort();\n abortControllerRef.current = null;\n threadIdRef.current = null;\n contentRef.current = \"\";\n setContent(\"\");\n setEvents([]);\n setThreadId(null);\n setIsStreaming(false);\n setError(null);\n }, []);\n\n const send = useCallback(\n async (message: string) => {\n // Abort any previous stream — only one chat turn in flight per hook.\n abortControllerRef.current?.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n\n contentRef.current = \"\";\n setContent(\"\");\n setEvents([]);\n setError(null);\n setIsStreaming(true);\n\n const payload = {\n message,\n agent,\n ...(threadIdRef.current ? { threadId: threadIdRef.current } : {}),\n };\n\n try {\n await connectSSE({\n url: endpoint,\n payload,\n signal: controller.signal,\n // Chat turns aren't idempotent — re-sending the payload after a\n // transient failure would either duplicate the user message or\n // depend on server-side Last-Event-ID resumption (the agents\n // plugin's StreamManager supports it, but failure-mode auditing\n // is easier with retries off by default; callers can re-enable\n // via the underlying connectSSE once they understand the\n // resumption contract on their endpoint).\n maxRetries: 0,\n onMessage: async ({ data }) => {\n if (controller.signal.aborted) return;\n if (!data || data === \"[DONE]\") return;\n let event: AgentChatEvent;\n try {\n event = JSON.parse(data) as AgentChatEvent;\n } catch {\n // Skip malformed payloads — the rest of the stream is\n // still useful and the agents plugin recovers on the\n // next event boundary.\n return;\n }\n if (!event.type) return;\n\n // Best-effort: never let an onEvent throw break the stream.\n try {\n onEventRef.current?.(event);\n } catch {\n // swallow\n }\n\n setEvents((prev) => [...prev, event]);\n\n if (event.type === \"appkit.metadata\") {\n const tid = event.data?.threadId;\n if (typeof tid === \"string\") {\n threadIdRef.current = tid;\n setThreadId(tid);\n }\n } else if (\n event.type === \"response.output_text.delta\" &&\n typeof event.delta === \"string\"\n ) {\n contentRef.current += event.delta;\n setContent(contentRef.current);\n }\n },\n onError: (err) => {\n if (controller.signal.aborted) return;\n setError(err instanceof Error ? err.message : \"Chat stream failed\");\n },\n });\n } catch (err) {\n if (!controller.signal.aborted) {\n setError(err instanceof Error ? err.message : \"Chat stream failed\");\n }\n } finally {\n if (abortControllerRef.current === controller) {\n abortControllerRef.current = null;\n }\n setIsStreaming(false);\n }\n },\n [agent, endpoint],\n );\n\n // Abort any in-flight stream when the component unmounts.\n useEffect(() => {\n return () => {\n abortControllerRef.current?.abort();\n };\n }, []);\n\n return {\n content,\n events,\n threadId,\n isStreaming,\n error,\n send,\n reset,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8HA,SAAgB,aAAa,EAC3B,OACA,WAAW,oBACX,WAC0C;CAC1C,MAAM,CAAC,SAAS,cAAc,SAAS,GAAG;CAC1C,MAAM,CAAC,QAAQ,aAAa,SAA2B,EAAE,CAAC;CAC1D,MAAM,CAAC,UAAU,eAAe,SAAwB,KAAK;CAC7D,MAAM,CAAC,aAAa,kBAAkB,SAAS,MAAM;CACrD,MAAM,CAAC,OAAO,YAAY,SAAwB,KAAK;CAKvD,MAAM,cAAc,OAAsB,KAAK;CAC/C,MAAM,aAAa,OAAO,GAAG;CAC7B,MAAM,aAAa,OAAO,QAAQ;AAClC,YAAW,UAAU;CACrB,MAAM,qBAAqB,OAA+B,KAAK;CAE/D,MAAM,QAAQ,kBAAkB;AAC9B,qBAAmB,SAAS,OAAO;AACnC,qBAAmB,UAAU;AAC7B,cAAY,UAAU;AACtB,aAAW,UAAU;AACrB,aAAW,GAAG;AACd,YAAU,EAAE,CAAC;AACb,cAAY,KAAK;AACjB,iBAAe,MAAM;AACrB,WAAS,KAAK;IACb,EAAE,CAAC;CAEN,MAAM,OAAO,YACX,OAAO,YAAoB;AAEzB,qBAAmB,SAAS,OAAO;EACnC,MAAM,aAAa,IAAI,iBAAiB;AACxC,qBAAmB,UAAU;AAE7B,aAAW,UAAU;AACrB,aAAW,GAAG;AACd,YAAU,EAAE,CAAC;AACb,WAAS,KAAK;AACd,iBAAe,KAAK;EAEpB,MAAM,UAAU;GACd;GACA;GACA,GAAI,YAAY,UAAU,EAAE,UAAU,YAAY,SAAS,GAAG,EAAE;GACjE;AAED,MAAI;AACF,SAAM,WAAW;IACf,KAAK;IACL;IACA,QAAQ,WAAW;IAQnB,YAAY;IACZ,WAAW,OAAO,EAAE,WAAW;AAC7B,SAAI,WAAW,OAAO,QAAS;AAC/B,SAAI,CAAC,QAAQ,SAAS,SAAU;KAChC,IAAI;AACJ,SAAI;AACF,cAAQ,KAAK,MAAM,KAAK;aAClB;AAIN;;AAEF,SAAI,CAAC,MAAM,KAAM;AAGjB,SAAI;AACF,iBAAW,UAAU,MAAM;aACrB;AAIR,gBAAW,SAAS,CAAC,GAAG,MAAM,MAAM,CAAC;AAErC,SAAI,MAAM,SAAS,mBAAmB;MACpC,MAAM,MAAM,MAAM,MAAM;AACxB,UAAI,OAAO,QAAQ,UAAU;AAC3B,mBAAY,UAAU;AACtB,mBAAY,IAAI;;gBAGlB,MAAM,SAAS,gCACf,OAAO,MAAM,UAAU,UACvB;AACA,iBAAW,WAAW,MAAM;AAC5B,iBAAW,WAAW,QAAQ;;;IAGlC,UAAU,QAAQ;AAChB,SAAI,WAAW,OAAO,QAAS;AAC/B,cAAS,eAAe,QAAQ,IAAI,UAAU,qBAAqB;;IAEtE,CAAC;WACK,KAAK;AACZ,OAAI,CAAC,WAAW,OAAO,QACrB,UAAS,eAAe,QAAQ,IAAI,UAAU,qBAAqB;YAE7D;AACR,OAAI,mBAAmB,YAAY,WACjC,oBAAmB,UAAU;AAE/B,kBAAe,MAAM;;IAGzB,CAAC,OAAO,SAAS,CAClB;AAGD,iBAAgB;AACd,eAAa;AACX,sBAAmB,SAAS,OAAO;;IAEpC,EAAE,CAAC;AAEN,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACD"}
1
+ {"version":3,"file":"use-agent-chat.js","names":[],"sources":["../../../src/react/hooks/use-agent-chat.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { connectSSE } from \"@/js\";\n\n/**\n * One Responses-API-shaped event yielded by the agents plugin SSE stream.\n *\n * The hook handles the two paths every chat UI needs — accumulating\n * `content` from `response.output_text.delta` and capturing `threadId`\n * from `appkit.metadata` — and surfaces everything else (tool calls,\n * approval gates, status events, etc.) through {@link UseAgentChatOptions.onEvent}.\n *\n * Fields beyond `type` are intentionally loose because the agents plugin\n * forwards adapter-specific shapes verbatim. Treat unknown fields as\n * opaque pass-through.\n */\nexport interface AgentChatEvent {\n type: string;\n delta?: string;\n item_id?: string;\n item?: {\n type?: string;\n id?: string;\n call_id?: string;\n name?: string;\n arguments?: string;\n output?: string;\n status?: string;\n };\n content?: string;\n data?: Record<string, unknown>;\n error?: string;\n sequence_number?: number;\n output_index?: number;\n // `appkit.approval_pending` payload\n approval_id?: string;\n stream_id?: string;\n tool_name?: string;\n args?: unknown;\n annotations?: Record<string, unknown>;\n}\n\nexport interface UseAgentChatOptions {\n /**\n * Agent name registered with the `agents()` plugin (e.g. `\"assistant\"`,\n * `\"helper\"`). Send-time payload includes this so the plugin routes the\n * turn to the right `AgentDefinition`.\n */\n agent: string;\n /**\n * Override the chat endpoint. Default `\"/api/agents/chat\"` matches the\n * route the agents plugin mounts under its prefix. Useful when the\n * server mounts under a non-default base path or when proxying.\n */\n endpoint?: string;\n /**\n * Called for every parsed SSE event before any state update. Use this\n * to drive tool-call rows, approval cards, inspectors, or anything\n * beyond the streaming text content. Errors thrown here are swallowed\n * so a buggy handler can't kill the stream.\n */\n onEvent?: (event: AgentChatEvent) => void;\n}\n\nexport interface UseAgentChatResult {\n /** Accumulated assistant text from `response.output_text.delta` events. */\n content: string;\n /**\n * Every parsed event, in order. Provided for components that need to\n * render historical tool calls or replay state after a remount —\n * lighter than re-deriving from message history. For one-off side\n * effects prefer {@link UseAgentChatOptions.onEvent}.\n */\n events: AgentChatEvent[];\n /**\n * Thread id captured from the first `appkit.metadata` event of the\n * stream. Subsequent `send()` calls automatically forward this so the\n * server reuses the same thread.\n */\n threadId: string | null;\n /** True while an SSE stream is open. */\n isStreaming: boolean;\n /** Last error message (cleared on next successful `send()`). */\n error: string | null;\n /**\n * Send a user turn and stream the response. Aborts any in-flight\n * stream. Resolves when the stream completes (success or error).\n */\n send: (message: string) => Promise<void>;\n /**\n * Discard accumulated content, events, and threadId. Aborts any\n * in-flight stream. Use when switching agents or starting a fresh\n * conversation.\n */\n reset: () => void;\n}\n\n/**\n * React hook for chatting with an agent registered via the `agents()`\n * plugin. Wraps {@link connectSSE} (which owns the buffer cap, abort\n * composition, retry/backoff, and frame parsing) with the small amount\n * of stateful glue every chat UI needs: accumulated assistant text,\n * thread id, streaming flag, and an event callback.\n *\n * The hook is intentionally lower-level than a full chat component —\n * it owns one stream at a time, not a multi-turn message history. The\n * caller composes its own messages array (typically a `useState`) and\n * appends to it via the `onEvent` callback for tool calls and via the\n * `content` field for assistant text.\n *\n * @example\n * ```tsx\n * function Chat({ agent }: { agent: string }) {\n * const [messages, setMessages] = useState<Message[]>([]);\n * const { content, threadId, isStreaming, send, reset } = useAgentChat({\n * agent,\n * onEvent(ev) {\n * if (ev.type === \"response.output_item.added\" && ev.item?.type === \"function_call\") {\n * setMessages((m) => [...m, { role: \"tool\", name: ev.item?.name, args: ev.item?.arguments }]);\n * }\n * },\n * });\n * // `content` reflects the latest assistant turn; reset() between conversations.\n * // ...\n * }\n * ```\n */\nexport function useAgentChat({\n agent,\n endpoint = \"/api/agents/chat\",\n onEvent,\n}: UseAgentChatOptions): UseAgentChatResult {\n const [content, setContent] = useState(\"\");\n const [events, setEvents] = useState<AgentChatEvent[]>([]);\n const [threadId, setThreadId] = useState<string | null>(null);\n const [isStreaming, setIsStreaming] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Refs avoid the standard \"stale closure\" problem with `send` and\n // `onEvent`: `send` is a stable callback that reads the latest\n // threadId/onEvent without re-mounting connectSSE on every render.\n const threadIdRef = useRef<string | null>(null);\n const contentRef = useRef(\"\");\n const onEventRef = useRef(onEvent);\n onEventRef.current = onEvent;\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const reset = useCallback(() => {\n abortControllerRef.current?.abort();\n abortControllerRef.current = null;\n threadIdRef.current = null;\n contentRef.current = \"\";\n setContent(\"\");\n setEvents([]);\n setThreadId(null);\n setIsStreaming(false);\n setError(null);\n }, []);\n\n const send = useCallback(\n async (message: string) => {\n // Abort any previous stream — only one chat turn in flight per hook.\n abortControllerRef.current?.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n\n contentRef.current = \"\";\n setContent(\"\");\n setEvents([]);\n setError(null);\n setIsStreaming(true);\n\n const payload = {\n message,\n agent,\n ...(threadIdRef.current ? { threadId: threadIdRef.current } : {}),\n };\n\n try {\n await connectSSE({\n url: endpoint,\n payload,\n signal: controller.signal,\n // Chat turns aren't idempotent — re-sending the payload after a\n // transient failure would either duplicate the user message or\n // depend on server-side Last-Event-ID resumption (the agents\n // plugin's StreamManager supports it, but failure-mode auditing\n // is easier with retries off by default; callers can re-enable\n // via the underlying connectSSE once they understand the\n // resumption contract on their endpoint).\n maxRetries: 0,\n onMessage: async ({ data }) => {\n if (controller.signal.aborted) return;\n if (!data || data === \"[DONE]\") return;\n let event: AgentChatEvent;\n try {\n event = JSON.parse(data) as AgentChatEvent;\n } catch {\n // Skip malformed payloads — the rest of the stream is\n // still useful and the agents plugin recovers on the\n // next event boundary.\n return;\n }\n if (!event.type) return;\n\n // Best-effort: never let an onEvent throw break the stream.\n try {\n onEventRef.current?.(event);\n } catch {\n // swallow\n }\n\n setEvents((prev) => [...prev, event]);\n\n if (event.type === \"appkit.metadata\") {\n const tid = event.data?.threadId;\n if (typeof tid === \"string\") {\n threadIdRef.current = tid;\n setThreadId(tid);\n }\n } else if (\n event.type === \"response.output_text.delta\" &&\n typeof event.delta === \"string\"\n ) {\n contentRef.current += event.delta;\n setContent(contentRef.current);\n }\n },\n onError: (err) => {\n if (controller.signal.aborted) return;\n setError(err instanceof Error ? err.message : \"Chat stream failed\");\n },\n });\n } catch (err) {\n if (!controller.signal.aborted) {\n setError(err instanceof Error ? err.message : \"Chat stream failed\");\n }\n } finally {\n if (abortControllerRef.current === controller) {\n abortControllerRef.current = null;\n }\n setIsStreaming(false);\n }\n },\n [agent, endpoint],\n );\n\n // Abort any in-flight stream when the component unmounts.\n useEffect(() => {\n return () => {\n abortControllerRef.current?.abort();\n };\n }, []);\n\n return {\n content,\n events,\n threadId,\n isStreaming,\n error,\n send,\n reset,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+HA,SAAgB,aAAa,EAC3B,OACA,WAAW,oBACX,WAC0C;CAC1C,MAAM,CAAC,SAAS,cAAc,SAAS,GAAG;CAC1C,MAAM,CAAC,QAAQ,aAAa,SAA2B,EAAE,CAAC;CAC1D,MAAM,CAAC,UAAU,eAAe,SAAwB,KAAK;CAC7D,MAAM,CAAC,aAAa,kBAAkB,SAAS,MAAM;CACrD,MAAM,CAAC,OAAO,YAAY,SAAwB,KAAK;CAKvD,MAAM,cAAc,OAAsB,KAAK;CAC/C,MAAM,aAAa,OAAO,GAAG;CAC7B,MAAM,aAAa,OAAO,QAAQ;AAClC,YAAW,UAAU;CACrB,MAAM,qBAAqB,OAA+B,KAAK;CAE/D,MAAM,QAAQ,kBAAkB;AAC9B,qBAAmB,SAAS,OAAO;AACnC,qBAAmB,UAAU;AAC7B,cAAY,UAAU;AACtB,aAAW,UAAU;AACrB,aAAW,GAAG;AACd,YAAU,EAAE,CAAC;AACb,cAAY,KAAK;AACjB,iBAAe,MAAM;AACrB,WAAS,KAAK;IACb,EAAE,CAAC;CAEN,MAAM,OAAO,YACX,OAAO,YAAoB;AAEzB,qBAAmB,SAAS,OAAO;EACnC,MAAM,aAAa,IAAI,iBAAiB;AACxC,qBAAmB,UAAU;AAE7B,aAAW,UAAU;AACrB,aAAW,GAAG;AACd,YAAU,EAAE,CAAC;AACb,WAAS,KAAK;AACd,iBAAe,KAAK;EAEpB,MAAM,UAAU;GACd;GACA;GACA,GAAI,YAAY,UAAU,EAAE,UAAU,YAAY,SAAS,GAAG,EAAE;GACjE;AAED,MAAI;AACF,SAAM,WAAW;IACf,KAAK;IACL;IACA,QAAQ,WAAW;IAQnB,YAAY;IACZ,WAAW,OAAO,EAAE,WAAW;AAC7B,SAAI,WAAW,OAAO,QAAS;AAC/B,SAAI,CAAC,QAAQ,SAAS,SAAU;KAChC,IAAI;AACJ,SAAI;AACF,cAAQ,KAAK,MAAM,KAAK;aAClB;AAIN;;AAEF,SAAI,CAAC,MAAM,KAAM;AAGjB,SAAI;AACF,iBAAW,UAAU,MAAM;aACrB;AAIR,gBAAW,SAAS,CAAC,GAAG,MAAM,MAAM,CAAC;AAErC,SAAI,MAAM,SAAS,mBAAmB;MACpC,MAAM,MAAM,MAAM,MAAM;AACxB,UAAI,OAAO,QAAQ,UAAU;AAC3B,mBAAY,UAAU;AACtB,mBAAY,IAAI;;gBAGlB,MAAM,SAAS,gCACf,OAAO,MAAM,UAAU,UACvB;AACA,iBAAW,WAAW,MAAM;AAC5B,iBAAW,WAAW,QAAQ;;;IAGlC,UAAU,QAAQ;AAChB,SAAI,WAAW,OAAO,QAAS;AAC/B,cAAS,eAAe,QAAQ,IAAI,UAAU,qBAAqB;;IAEtE,CAAC;WACK,KAAK;AACZ,OAAI,CAAC,WAAW,OAAO,QACrB,UAAS,eAAe,QAAQ,IAAI,UAAU,qBAAqB;YAE7D;AACR,OAAI,mBAAmB,YAAY,WACjC,oBAAmB,UAAU;AAE/B,kBAAe,MAAM;;IAGzB,CAAC,OAAO,SAAS,CAClB;AAGD,iBAAgB;AACd,eAAa;AACX,sBAAmB,SAAS,OAAO;;IAEpC,EAAE,CAAC;AAEN,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-ai-search-query.d.ts","names":[],"sources":["../../../src/react/hooks/use-ai-search-query.ts"],"mappings":";;;UASiB,uBAAA;;AAAjB;;;EAKE,KAAA;AAAA;AAAA,UAGe,sBAAA,WACL,MAAA,oBAA0B,MAAA;EADC;EAIrC,MAAA,GACE,KAAA,WAAgB,eAAA,KACb,OAAA,CAAQ,gBAAA,CAAiB,CAAA;EALpB;EAOV,IAAA,EAAM,gBAAA,CAAiB,CAAA;EAHL;EAKlB,OAAA;EAJa;EAMb,KAAA;EAJuB;EAMvB,KAAA;EAES;EAAT,OAAA,EAAS,oBAAA;AAAA;;;;;;iBAQK,gBAAA,WACJ,MAAA,oBAA0B,MAAA,kBAAA,CACpC,OAAA,GAAS,uBAAA,GAA+B,sBAAA,CAAuB,CAAA"}
1
+ {"version":3,"file":"use-ai-search-query.d.ts","names":[],"sources":["../../../src/react/hooks/use-ai-search-query.ts"],"mappings":";;;UAUiB,uBAAA;;AAAjB;;;EAKE,KAAA;AAAA;AAAA,UAGe,sBAAA,WACL,MAAA,oBAA0B,MAAA;EADC;EAIrC,MAAA,GACE,KAAA,WAAgB,eAAA,KACb,OAAA,CAAQ,gBAAA,CAAiB,CAAA;EALpB;EAOV,IAAA,EAAM,gBAAA,CAAiB,CAAA;EAHL;EAKlB,OAAA;EAJa;EAMb,KAAA;EAJuB;EAMvB,KAAA;EAES;EAAT,OAAA,EAAS,oBAAA;AAAA;;;;;;iBAQK,gBAAA,WACJ,MAAA,oBAA0B,MAAA,kBAAA,CACpC,OAAA,GAAS,uBAAA,GAA+B,sBAAA,CAAuB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-ai-search-query.js","names":[],"sources":["../../../src/react/hooks/use-ai-search-query.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type {\n AiSearchClientConfig,\n AiSearchIndexSummary,\n AiSearchRequest,\n AiSearchResponse,\n} from \"./types\";\nimport { usePluginClientConfig } from \"./use-plugin-config\";\n\nexport interface UseAiSearchQueryOptions {\n /**\n * Index alias to query. Defaults to the first index exposed by the plugin's\n * `clientConfig()`, so a single-index app needs no alias.\n */\n alias?: string;\n}\n\nexport interface UseAiSearchQueryResult<\n T extends Record<string, unknown> = Record<string, unknown>,\n> {\n /** Run a search. Pass query text, or a full request for filters/paging control. */\n search: (\n query: string | AiSearchRequest,\n ) => Promise<AiSearchResponse<T> | null>;\n /** Latest response, null until the first successful search. */\n data: AiSearchResponse<T> | null;\n /** Whether a search is in progress. */\n loading: boolean;\n /** Error message, if any. */\n error: string | null;\n /** The resolved alias this hook queries. */\n alias: string | null;\n /** All configured indexes, for building a selector. */\n indexes: AiSearchIndexSummary[];\n}\n\n/**\n * Hook for querying a Databricks AI Search index. Reads the available indexes\n * from the ai-search plugin's `clientConfig()` and POSTs to\n * `/api/ai-search/{alias}/query`, so the UI never hardcodes an endpoint alias.\n */\nexport function useAiSearchQuery<\n T extends Record<string, unknown> = Record<string, unknown>,\n>(options: UseAiSearchQueryOptions = {}): UseAiSearchQueryResult<T> {\n const config = usePluginClientConfig<AiSearchClientConfig>(\"aiSearch\");\n const indexes = config.indexes ?? [];\n\n const alias = options.alias ?? indexes[0]?.alias ?? null;\n\n // Config validation error; null for a valid alias.\n let aliasError: string | null = null;\n if (!alias) {\n aliasError = \"No AI Search indexes are configured.\";\n } else if (options.alias && !indexes.some((i) => i.alias === options.alias)) {\n const available = indexes.map((i) => i.alias).join(\", \") || \"none\";\n aliasError = `Unknown AI Search index \"${options.alias}\". Available: ${available}`;\n }\n\n const [data, setData] = useState<AiSearchResponse<T> | null>(null);\n const [loading, setLoading] = useState(false);\n const [fetchError, setFetchError] = useState<string | null>(null);\n const error = fetchError ?? aliasError;\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const search = useCallback(\n (query: string | AiSearchRequest): Promise<AiSearchResponse<T> | null> => {\n if (aliasError || !alias) {\n return Promise.resolve(null);\n }\n\n abortControllerRef.current?.abort();\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n setLoading(true);\n setFetchError(null);\n setData(null);\n\n const body: AiSearchRequest =\n typeof query === \"string\" ? { queryText: query } : query;\n\n return fetch(`/api/ai-search/${encodeURIComponent(alias)}/query`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: abortController.signal,\n })\n .then(async (res) => {\n if (!res.ok) {\n const errorBody = await res.json().catch(() => null);\n throw new Error(errorBody?.error || `HTTP ${res.status}`);\n }\n return res.json();\n })\n .then((result: AiSearchResponse<T>) => {\n if (abortController.signal.aborted) return null;\n setData(result);\n setLoading(false);\n return result;\n })\n .catch((err: Error) => {\n if (abortController.signal.aborted) return null;\n setFetchError(err.message || \"Search failed\");\n setLoading(false);\n return null;\n });\n },\n [alias, aliasError],\n );\n\n // Reset when the target alias changes: abort any in-flight request (its\n // result would otherwise land under the new alias) and clear stale data +\n // fetch error. `error` re-derives from the new alias's validation state.\n // biome-ignore lint/correctness/useExhaustiveDependencies: `alias` is the intended trigger — the effect runs to reset on every alias change, not because it reads alias.\n useEffect(() => {\n abortControllerRef.current?.abort();\n setData(null);\n setLoading(false);\n setFetchError(null);\n }, [alias]);\n\n useEffect(() => () => abortControllerRef.current?.abort(), []);\n\n return { search, data, loading, error, alias, indexes };\n}\n"],"mappings":";;;;;;;;;AAyCA,SAAgB,iBAEd,UAAmC,EAAE,EAA6B;CAElE,MAAM,UADS,sBAA4C,WAAW,CAC/C,WAAW,EAAE;CAEpC,MAAM,QAAQ,QAAQ,SAAS,QAAQ,IAAI,SAAS;CAGpD,IAAI,aAA4B;AAChC,KAAI,CAAC,MACH,cAAa;UACJ,QAAQ,SAAS,CAAC,QAAQ,MAAM,MAAM,EAAE,UAAU,QAAQ,MAAM,EAAE;EAC3E,MAAM,YAAY,QAAQ,KAAK,MAAM,EAAE,MAAM,CAAC,KAAK,KAAK,IAAI;AAC5D,eAAa,4BAA4B,QAAQ,MAAM,gBAAgB;;CAGzE,MAAM,CAAC,MAAM,WAAW,SAAqC,KAAK;CAClE,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;CAC7C,MAAM,CAAC,YAAY,iBAAiB,SAAwB,KAAK;CACjE,MAAM,QAAQ,cAAc;CAC5B,MAAM,qBAAqB,OAA+B,KAAK;CAE/D,MAAM,SAAS,aACZ,UAAyE;AACxE,MAAI,cAAc,CAAC,MACjB,QAAO,QAAQ,QAAQ,KAAK;AAG9B,qBAAmB,SAAS,OAAO;EACnC,MAAM,kBAAkB,IAAI,iBAAiB;AAC7C,qBAAmB,UAAU;AAE7B,aAAW,KAAK;AAChB,gBAAc,KAAK;AACnB,UAAQ,KAAK;EAEb,MAAM,OACJ,OAAO,UAAU,WAAW,EAAE,WAAW,OAAO,GAAG;AAErD,SAAO,MAAM,kBAAkB,mBAAmB,MAAM,CAAC,SAAS;GAChE,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAC/C,MAAM,KAAK,UAAU,KAAK;GAC1B,QAAQ,gBAAgB;GACzB,CAAC,CACC,KAAK,OAAO,QAAQ;AACnB,OAAI,CAAC,IAAI,IAAI;IACX,MAAM,YAAY,MAAM,IAAI,MAAM,CAAC,YAAY,KAAK;AACpD,UAAM,IAAI,MAAM,WAAW,SAAS,QAAQ,IAAI,SAAS;;AAE3D,UAAO,IAAI,MAAM;IACjB,CACD,MAAM,WAAgC;AACrC,OAAI,gBAAgB,OAAO,QAAS,QAAO;AAC3C,WAAQ,OAAO;AACf,cAAW,MAAM;AACjB,UAAO;IACP,CACD,OAAO,QAAe;AACrB,OAAI,gBAAgB,OAAO,QAAS,QAAO;AAC3C,iBAAc,IAAI,WAAW,gBAAgB;AAC7C,cAAW,MAAM;AACjB,UAAO;IACP;IAEN,CAAC,OAAO,WAAW,CACpB;AAMD,iBAAgB;AACd,qBAAmB,SAAS,OAAO;AACnC,UAAQ,KAAK;AACb,aAAW,MAAM;AACjB,gBAAc,KAAK;IAClB,CAAC,MAAM,CAAC;AAEX,uBAAsB,mBAAmB,SAAS,OAAO,EAAE,EAAE,CAAC;AAE9D,QAAO;EAAE;EAAQ;EAAM;EAAS;EAAO;EAAO;EAAS"}
1
+ {"version":3,"file":"use-ai-search-query.js","names":[],"sources":["../../../src/react/hooks/use-ai-search-query.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport type {\n AiSearchClientConfig,\n AiSearchIndexSummary,\n AiSearchRequest,\n AiSearchResponse,\n} from \"./types\";\nimport { usePluginClientConfig } from \"./use-plugin-config\";\n\nexport interface UseAiSearchQueryOptions {\n /**\n * Index alias to query. Defaults to the first index exposed by the plugin's\n * `clientConfig()`, so a single-index app needs no alias.\n */\n alias?: string;\n}\n\nexport interface UseAiSearchQueryResult<\n T extends Record<string, unknown> = Record<string, unknown>,\n> {\n /** Run a search. Pass query text, or a full request for filters/paging control. */\n search: (\n query: string | AiSearchRequest,\n ) => Promise<AiSearchResponse<T> | null>;\n /** Latest response, null until the first successful search. */\n data: AiSearchResponse<T> | null;\n /** Whether a search is in progress. */\n loading: boolean;\n /** Error message, if any. */\n error: string | null;\n /** The resolved alias this hook queries. */\n alias: string | null;\n /** All configured indexes, for building a selector. */\n indexes: AiSearchIndexSummary[];\n}\n\n/**\n * Hook for querying a Databricks AI Search index. Reads the available indexes\n * from the ai-search plugin's `clientConfig()` and POSTs to\n * `/api/ai-search/{alias}/query`, so the UI never hardcodes an endpoint alias.\n */\nexport function useAiSearchQuery<\n T extends Record<string, unknown> = Record<string, unknown>,\n>(options: UseAiSearchQueryOptions = {}): UseAiSearchQueryResult<T> {\n const config = usePluginClientConfig<AiSearchClientConfig>(\"aiSearch\");\n const indexes = config.indexes ?? [];\n\n const alias = options.alias ?? indexes[0]?.alias ?? null;\n\n // Config validation error; null for a valid alias.\n let aliasError: string | null = null;\n if (!alias) {\n aliasError = \"No AI Search indexes are configured.\";\n } else if (options.alias && !indexes.some((i) => i.alias === options.alias)) {\n const available = indexes.map((i) => i.alias).join(\", \") || \"none\";\n aliasError = `Unknown AI Search index \"${options.alias}\". Available: ${available}`;\n }\n\n const [data, setData] = useState<AiSearchResponse<T> | null>(null);\n const [loading, setLoading] = useState(false);\n const [fetchError, setFetchError] = useState<string | null>(null);\n const error = fetchError ?? aliasError;\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const search = useCallback(\n (query: string | AiSearchRequest): Promise<AiSearchResponse<T> | null> => {\n if (aliasError || !alias) {\n return Promise.resolve(null);\n }\n\n abortControllerRef.current?.abort();\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n setLoading(true);\n setFetchError(null);\n setData(null);\n\n const body: AiSearchRequest =\n typeof query === \"string\" ? { queryText: query } : query;\n\n return fetch(`/api/ai-search/${encodeURIComponent(alias)}/query`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: abortController.signal,\n })\n .then(async (res) => {\n if (!res.ok) {\n const errorBody = await res.json().catch(() => null);\n throw new Error(errorBody?.error || `HTTP ${res.status}`);\n }\n return res.json();\n })\n .then((result: AiSearchResponse<T>) => {\n if (abortController.signal.aborted) return null;\n setData(result);\n setLoading(false);\n return result;\n })\n .catch((err: Error) => {\n if (abortController.signal.aborted) return null;\n setFetchError(err.message || \"Search failed\");\n setLoading(false);\n return null;\n });\n },\n [alias, aliasError],\n );\n\n // Reset when the target alias changes: abort any in-flight request (its\n // result would otherwise land under the new alias) and clear stale data +\n // fetch error. `error` re-derives from the new alias's validation state.\n useEffect(() => {\n abortControllerRef.current?.abort();\n setData(null);\n setLoading(false);\n setFetchError(null);\n }, [alias]);\n\n useEffect(() => () => abortControllerRef.current?.abort(), []);\n\n return { search, data, loading, error, alias, indexes };\n}\n"],"mappings":";;;;;;;;;AA0CA,SAAgB,iBAEd,UAAmC,EAAE,EAA6B;CAElE,MAAM,UADS,sBAA4C,WAAW,CAC/C,WAAW,EAAE;CAEpC,MAAM,QAAQ,QAAQ,SAAS,QAAQ,IAAI,SAAS;CAGpD,IAAI,aAA4B;AAChC,KAAI,CAAC,MACH,cAAa;UACJ,QAAQ,SAAS,CAAC,QAAQ,MAAM,MAAM,EAAE,UAAU,QAAQ,MAAM,EAAE;EAC3E,MAAM,YAAY,QAAQ,KAAK,MAAM,EAAE,MAAM,CAAC,KAAK,KAAK,IAAI;AAC5D,eAAa,4BAA4B,QAAQ,MAAM,gBAAgB;;CAGzE,MAAM,CAAC,MAAM,WAAW,SAAqC,KAAK;CAClE,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;CAC7C,MAAM,CAAC,YAAY,iBAAiB,SAAwB,KAAK;CACjE,MAAM,QAAQ,cAAc;CAC5B,MAAM,qBAAqB,OAA+B,KAAK;CAE/D,MAAM,SAAS,aACZ,UAAyE;AACxE,MAAI,cAAc,CAAC,MACjB,QAAO,QAAQ,QAAQ,KAAK;AAG9B,qBAAmB,SAAS,OAAO;EACnC,MAAM,kBAAkB,IAAI,iBAAiB;AAC7C,qBAAmB,UAAU;AAE7B,aAAW,KAAK;AAChB,gBAAc,KAAK;AACnB,UAAQ,KAAK;EAEb,MAAM,OACJ,OAAO,UAAU,WAAW,EAAE,WAAW,OAAO,GAAG;AAErD,SAAO,MAAM,kBAAkB,mBAAmB,MAAM,CAAC,SAAS;GAChE,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAC/C,MAAM,KAAK,UAAU,KAAK;GAC1B,QAAQ,gBAAgB;GACzB,CAAC,CACC,KAAK,OAAO,QAAQ;AACnB,OAAI,CAAC,IAAI,IAAI;IACX,MAAM,YAAY,MAAM,IAAI,MAAM,CAAC,YAAY,KAAK;AACpD,UAAM,IAAI,MAAM,WAAW,SAAS,QAAQ,IAAI,SAAS;;AAE3D,UAAO,IAAI,MAAM;IACjB,CACD,MAAM,WAAgC;AACrC,OAAI,gBAAgB,OAAO,QAAS,QAAO;AAC3C,WAAQ,OAAO;AACf,cAAW,MAAM;AACjB,UAAO;IACP,CACD,OAAO,QAAe;AACrB,OAAI,gBAAgB,OAAO,QAAS,QAAO;AAC3C,iBAAc,IAAI,WAAW,gBAAgB;AAC7C,cAAW,MAAM;AACjB,UAAO;IACP;IAEN,CAAC,OAAO,WAAW,CACpB;AAKD,iBAAgB;AACd,qBAAmB,SAAS,OAAO;AACnC,UAAQ,KAAK;AACb,aAAW,MAAM;AACjB,gBAAc,KAAK;IAClB,CAAC,MAAM,CAAC;AAEX,uBAAsB,mBAAmB,SAAS,OAAO,EAAE,EAAE,CAAC;AAE9D,QAAO;EAAE;EAAQ;EAAM;EAAS;EAAO;EAAO;EAAS"}
@@ -7,6 +7,12 @@ import { AnalyticsFormat, InferParams, InferResultByFormat, QueryKey, UseAnalyti
7
7
  * results are fetched as raw Arrow bytes directly from the query endpoint.
8
8
  * Integration hook between client and analytics plugin.
9
9
  *
10
+ * Identical requests (same query key, parameters, format, and dev mode) share
11
+ * a single in-flight network request: the first mounting instance starts it,
12
+ * later instances subscribe to the same {@link store} entry and see the same
13
+ * result and warehouse-status updates. The request is torn down once its last
14
+ * subscriber unmounts.
15
+ *
10
16
  * The return type is automatically inferred based on the format:
11
17
  * - `format: "JSON_ARRAY"` (default): Returns typed array from QueryRegistry
12
18
  * - `format: "ARROW_STREAM"`: Returns TypedArrowTable with row type preserved
@@ -1 +1 @@
1
- {"version":3,"file":"use-analytics-query.d.ts","names":[],"sources":["../../../src/react/hooks/use-analytics-query.ts"],"mappings":";;;;;AA2MA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,iBAAA,wBAEJ,QAAA,GAAW,QAAA,YACX,eAAA,gBAAA,CAEV,QAAA,EAAU,CAAA,EACV,UAAA,GAAa,WAAA,CAAY,CAAA,UACzB,OAAA,GAAS,wBAAA,CAAyB,CAAA,IACjC,uBAAA,CAAwB,mBAAA,CAAoB,CAAA,EAAG,CAAA,EAAG,CAAA"}
1
+ {"version":3,"file":"use-analytics-query.d.ts","names":[],"sources":["../../../src/react/hooks/use-analytics-query.ts"],"mappings":";;;;;AAgGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,iBAAA,wBAEJ,QAAA,GAAW,QAAA,YACX,eAAA,gBAAA,CAEV,QAAA,EAAU,CAAA,EACV,UAAA,GAAa,WAAA,CAAY,CAAA,UACzB,OAAA,GAAS,wBAAA,CAAyB,CAAA,IACjC,uBAAA,CAAwB,mBAAA,CAAoB,CAAA,EAAG,CAAA,EAAG,CAAA"}
@@ -1,9 +1,8 @@
1
- import { ArrowClient } from "../../js/arrow/arrow-client.js";
2
- import { connectSSE } from "../../js/sse/connect-sse.js";
3
- import { GENERIC_LOAD_ERROR, getDevMode, handleAnalyticsSseError, handleAnalyticsSseMessage, userFacingFetchError } from "./analytics-sse.js";
1
+ import { getDevMode } from "./analytics-sse.js";
2
+ import { getSnapshot, retain, start, subscribe } from "./analytics-request-store.js";
4
3
  import { useAnalyticsWarehousePublisher } from "./use-analytics-warehouse-status.js";
5
4
  import { useQueryHMR } from "./use-query-hmr.js";
6
- import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
5
+ import { useCallback, useEffect, useId, useMemo, useRef, useSyncExternalStore } from "react";
7
6
 
8
7
  //#region src/react/hooks/use-analytics-query.ts
9
8
  /** Shallow equality for plain-object query parameters (primitive values only). */
@@ -26,78 +25,17 @@ function useStableParams(value) {
26
25
  return ref.current;
27
26
  }
28
27
  /**
29
- * Fetch the real column names for a statement from the fallback endpoint,
30
- * used when a very wide schema's names didn't fit in the response header.
31
- * Returns undefined on any failure so decoding falls back to the raw Arrow
32
- * schema names.
33
- */
34
- async function fetchArrowColumns(statementId, signal) {
35
- try {
36
- const res = await fetch(`/api/analytics/columns/${encodeURIComponent(statementId)}`, { signal });
37
- if (!res.ok) return void 0;
38
- const body = await res.json();
39
- return Array.isArray(body.columns) ? body.columns : void 0;
40
- } catch {
41
- return;
42
- }
43
- }
44
- /**
45
- * Fetch an ARROW_STREAM query result as raw Arrow IPC bytes directly from
46
- * the query endpoint (no SSE, no second /arrow-result request) and decode
47
- * it into a Table. The server streams the bytes back as the POST response
48
- * body; errors before the first byte arrive as a JSON `{ error, errorCode }`.
49
- */
50
- async function fetchArrowDirect(ctx) {
51
- try {
52
- const response = await fetch(ctx.url, {
53
- method: "POST",
54
- headers: { "Content-Type": "application/json" },
55
- body: ctx.payload,
56
- signal: ctx.signal
57
- });
58
- if (ctx.signal.aborted) return;
59
- if (!response.ok) {
60
- let message = GENERIC_LOAD_ERROR;
61
- let code = null;
62
- try {
63
- const body = await response.json();
64
- if (body.error) message = body.error;
65
- if (typeof body.errorCode === "string") code = body.errorCode;
66
- } catch {}
67
- ctx.setLoading(false);
68
- ctx.setError(message);
69
- if (code) ctx.setErrorCode(code);
70
- ctx.unpublishWarehouseStatus();
71
- return;
72
- }
73
- const buffer = await response.arrayBuffer();
74
- if (ctx.signal.aborted) return;
75
- let columnNames;
76
- const header = response.headers.get("X-Appkit-Arrow-Columns");
77
- if (header) try {
78
- columnNames = JSON.parse(decodeURIComponent(header));
79
- } catch {}
80
- else {
81
- const ref = response.headers.get("X-Appkit-Arrow-Columns-Ref");
82
- if (ref) columnNames = await fetchArrowColumns(ref, ctx.signal);
83
- }
84
- const table = await ArrowClient.processArrowBuffer(new Uint8Array(buffer), columnNames);
85
- ctx.setData(table);
86
- ctx.setLoading(false);
87
- ctx.unpublishWarehouseStatus();
88
- } catch (error) {
89
- if (ctx.signal.aborted) return;
90
- ctx.setLoading(false);
91
- ctx.unpublishWarehouseStatus();
92
- ctx.setError(userFacingFetchError(error));
93
- }
94
- }
95
- /**
96
28
  * Subscribe to an analytics query and return its latest result. JSON_ARRAY
97
29
  * results stream over SSE (with warehouse-readiness progress); ARROW_STREAM
98
30
  * results are fetched as raw Arrow bytes directly from the query endpoint.
99
31
  * Integration hook between client and analytics plugin.
100
32
  *
33
+ * Identical requests (same query key, parameters, format, and dev mode) share
34
+ * a single in-flight network request: the first mounting instance starts it,
35
+ * later instances subscribe to the same {@link store} entry and see the same
36
+ * result and warehouse-status updates. The request is torn down once its last
37
+ * subscriber unmounts.
38
+ *
101
39
  * The return type is automatically inferred based on the format:
102
40
  * - `format: "JSON_ARRAY"` (default): Returns typed array from QueryRegistry
103
41
  * - `format: "ARROW_STREAM"`: Returns TypedArrowTable with row type preserved
@@ -129,12 +67,6 @@ function useAnalyticsQuery(queryKey, parameters, options = {}) {
129
67
  const autoStart = options?.autoStart ?? true;
130
68
  const devMode = getDevMode();
131
69
  const urlSuffix = `/api/analytics/query/${encodeURIComponent(queryKey)}${devMode}`;
132
- const [data, setData] = useState(null);
133
- const [loading, setLoading] = useState(false);
134
- const [error, setError] = useState(null);
135
- const [errorCode, setErrorCode] = useState(null);
136
- const [warehouseStatus, setWarehouseStatus] = useState(null);
137
- const abortControllerRef = useRef(null);
138
70
  const { publish: publishWarehouseStatus, unpublish: unpublishWarehouseStatus } = useAnalyticsWarehousePublisher(useId(), queryKey);
139
71
  if (!queryKey || queryKey.trim().length === 0) throw new Error("useAnalyticsQuery: 'queryKey' must be a non-empty string.");
140
72
  const stableParameters = useStableParams(parameters);
@@ -155,83 +87,42 @@ function useAnalyticsQuery(queryKey, parameters, options = {}) {
155
87
  format,
156
88
  maxParametersSize
157
89
  ]);
158
- const start = useCallback(() => {
159
- if (payload === null) {
160
- setError("Failed to serialize query parameters");
161
- return;
162
- }
163
- abortControllerRef.current?.abort();
164
- setLoading(true);
165
- setError(null);
166
- setErrorCode(null);
167
- setData(null);
168
- setWarehouseStatus(null);
169
- publishWarehouseStatus(null);
170
- const abortController = new AbortController();
171
- abortControllerRef.current = abortController;
172
- if (format === "ARROW_STREAM") {
173
- fetchArrowDirect({
174
- url: urlSuffix,
175
- payload,
176
- signal: abortController.signal,
177
- setLoading,
178
- setError,
179
- setErrorCode,
180
- setData: (table) => setData(table),
181
- unpublishWarehouseStatus
182
- });
183
- return;
184
- }
185
- const sseContext = {
186
- source: "useAnalyticsQuery",
187
- resource: { queryKey },
188
- defaultExecutionError: "Unable to execute query",
189
- unpublishOnMalformedMessage: false,
190
- signal: abortController.signal,
191
- abort: () => abortController.abort(),
192
- setLoading,
193
- setError,
194
- setErrorCode,
195
- onWarehouseStatus: (status) => {
196
- setWarehouseStatus(status);
197
- publishWarehouseStatus(status);
198
- },
199
- onResult: (message) => setData(message.data),
200
- unpublishWarehouseStatus
201
- };
202
- connectSSE({
90
+ const cacheKey = `${urlSuffix}::${payload}`;
91
+ const subscribe$1 = useCallback((listener) => subscribe(cacheKey, listener), [cacheKey]);
92
+ const getSnapshot$1 = useCallback(() => getSnapshot(cacheKey), [cacheKey]);
93
+ const snapshot = useSyncExternalStore(subscribe$1, getSnapshot$1, getSnapshot$1);
94
+ const start$1 = useCallback(() => start(cacheKey), [cacheKey]);
95
+ useEffect(() => {
96
+ if (payload === null) return;
97
+ return retain(cacheKey, {
203
98
  url: urlSuffix,
204
99
  payload,
205
- signal: abortController.signal,
206
- onMessage: (message) => handleAnalyticsSseMessage(message.data, sseContext),
207
- onError: (error) => handleAnalyticsSseError(error, sseContext)
208
- });
100
+ format
101
+ }, autoStart);
209
102
  }, [
210
- queryKey,
211
- payload,
103
+ cacheKey,
212
104
  urlSuffix,
105
+ payload,
213
106
  format,
214
- publishWarehouseStatus,
215
- unpublishWarehouseStatus
107
+ autoStart
216
108
  ]);
217
109
  useEffect(() => {
218
- if (autoStart) start();
219
- return () => {
220
- abortControllerRef.current?.abort();
221
- unpublishWarehouseStatus();
222
- };
110
+ if (snapshot.loading) publishWarehouseStatus(snapshot.warehouseStatus);
111
+ else unpublishWarehouseStatus();
223
112
  }, [
224
- start,
225
- autoStart,
113
+ snapshot.loading,
114
+ snapshot.warehouseStatus,
115
+ publishWarehouseStatus,
226
116
  unpublishWarehouseStatus
227
117
  ]);
228
- useQueryHMR(queryKey, start);
118
+ useEffect(() => unpublishWarehouseStatus, [unpublishWarehouseStatus]);
119
+ useQueryHMR(queryKey, start$1);
229
120
  return {
230
- data,
231
- loading,
232
- error,
233
- errorCode,
234
- warehouseStatus
121
+ data: snapshot.data,
122
+ loading: snapshot.loading,
123
+ error: payload === null ? "Failed to serialize query parameters" : snapshot.error,
124
+ errorCode: snapshot.errorCode,
125
+ warehouseStatus: snapshot.warehouseStatus
235
126
  };
236
127
  }
237
128
 
@@ -1 +1 @@
1
- {"version":3,"file":"use-analytics-query.js","names":[],"sources":["../../../src/react/hooks/use-analytics-query.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { ArrowClient, connectSSE } from \"@/js\";\nimport {\n type AnalyticsSseHandlerContext,\n GENERIC_LOAD_ERROR,\n getDevMode,\n handleAnalyticsSseError,\n handleAnalyticsSseMessage,\n userFacingFetchError,\n} from \"./analytics-sse\";\nimport type {\n AnalyticsFormat,\n InferParams,\n InferResultByFormat,\n QueryKey,\n UseAnalyticsQueryOptions,\n UseAnalyticsQueryResult,\n WarehouseStatus,\n} from \"./types\";\nimport { useAnalyticsWarehousePublisher } from \"./use-analytics-warehouse-status\";\nimport { useQueryHMR } from \"./use-query-hmr\";\n\n/** Shallow equality for plain-object query parameters (primitive values only). */\nfunction shallowEqualParams(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (\n a === null ||\n b === null ||\n typeof a !== \"object\" ||\n typeof b !== \"object\"\n ) {\n return false;\n }\n const aKeys = Object.keys(a as Record<string, unknown>);\n const bKeys = Object.keys(b as Record<string, unknown>);\n if (aKeys.length !== bKeys.length) return false;\n for (const key of aKeys) {\n if (!Object.hasOwn(b, key)) return false;\n if (\n !Object.is(\n (a as Record<string, unknown>)[key],\n (b as Record<string, unknown>)[key],\n )\n ) {\n return false;\n }\n }\n return true;\n}\n\n/** Keep structurally-equal params referentially stable across renders. */\nfunction useStableParams<T>(value: T): T {\n const ref = useRef<T>(value);\n if (!shallowEqualParams(ref.current, value)) {\n ref.current = value;\n }\n return ref.current;\n}\n\ninterface ArrowDirectContext {\n url: string;\n payload: string;\n signal: AbortSignal;\n setLoading: (loading: boolean) => void;\n setError: (error: string | null) => void;\n setErrorCode: (code: string | null) => void;\n setData: (data: unknown) => void;\n unpublishWarehouseStatus: () => void;\n}\n\n/**\n * Fetch the real column names for a statement from the fallback endpoint,\n * used when a very wide schema's names didn't fit in the response header.\n * Returns undefined on any failure so decoding falls back to the raw Arrow\n * schema names.\n */\nasync function fetchArrowColumns(\n statementId: string,\n signal: AbortSignal,\n): Promise<string[] | undefined> {\n try {\n const res = await fetch(\n `/api/analytics/columns/${encodeURIComponent(statementId)}`,\n { signal },\n );\n if (!res.ok) return undefined;\n const body = (await res.json()) as { columns?: unknown };\n return Array.isArray(body.columns) ? (body.columns as string[]) : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Fetch an ARROW_STREAM query result as raw Arrow IPC bytes directly from\n * the query endpoint (no SSE, no second /arrow-result request) and decode\n * it into a Table. The server streams the bytes back as the POST response\n * body; errors before the first byte arrive as a JSON `{ error, errorCode }`.\n */\nasync function fetchArrowDirect(ctx: ArrowDirectContext): Promise<void> {\n try {\n const response = await fetch(ctx.url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: ctx.payload,\n signal: ctx.signal,\n });\n if (ctx.signal.aborted) return;\n\n if (!response.ok) {\n let message = GENERIC_LOAD_ERROR;\n let code: string | null = null;\n try {\n const body = (await response.json()) as {\n error?: string;\n errorCode?: string;\n };\n if (body.error) message = body.error;\n if (typeof body.errorCode === \"string\") code = body.errorCode;\n } catch {\n // Non-JSON error body — keep the generic message.\n }\n ctx.setLoading(false);\n ctx.setError(message);\n if (code) ctx.setErrorCode(code);\n ctx.unpublishWarehouseStatus();\n return;\n }\n\n const buffer = await response.arrayBuffer();\n if (ctx.signal.aborted) return;\n // Databricks encodes ARROW_STREAM columns positionally (col_0, …); the\n // server sends the real manifest names so we can relabel the decoded\n // Table (charts look columns up by name). Normally inline in the\n // `X-Appkit-Arrow-Columns` header; for very wide schemas the header\n // carries only a statement-id reference and we fetch the names.\n let columnNames: string[] | undefined;\n const header = response.headers.get(\"X-Appkit-Arrow-Columns\");\n if (header) {\n try {\n columnNames = JSON.parse(decodeURIComponent(header));\n } catch {\n // Malformed header — fall back to the raw Arrow schema names.\n }\n } else {\n const ref = response.headers.get(\"X-Appkit-Arrow-Columns-Ref\");\n if (ref) {\n columnNames = await fetchArrowColumns(ref, ctx.signal);\n }\n }\n const table = await ArrowClient.processArrowBuffer(\n new Uint8Array(buffer),\n columnNames,\n );\n ctx.setData(table);\n ctx.setLoading(false);\n ctx.unpublishWarehouseStatus();\n } catch (error) {\n if (ctx.signal.aborted) return;\n ctx.setLoading(false);\n ctx.unpublishWarehouseStatus();\n ctx.setError(userFacingFetchError(error));\n }\n}\n\n/**\n * Subscribe to an analytics query and return its latest result. JSON_ARRAY\n * results stream over SSE (with warehouse-readiness progress); ARROW_STREAM\n * results are fetched as raw Arrow bytes directly from the query endpoint.\n * Integration hook between client and analytics plugin.\n *\n * The return type is automatically inferred based on the format:\n * - `format: \"JSON_ARRAY\"` (default): Returns typed array from QueryRegistry\n * - `format: \"ARROW_STREAM\"`: Returns TypedArrowTable with row type preserved\n *\n * Note: User context execution is determined by query file naming:\n * - `queryKey.obo.sql`: Executes as user (OBO = on-behalf-of / user delegation)\n * - `queryKey.sql`: Executes as service principal\n *\n * @param queryKey - Analytics query identifier\n * @param parameters - Query parameters (type-safe based on QueryRegistry)\n * @param options - Analytics query settings including format\n * @returns Query result state with format-appropriate data type\n *\n * @example JSON_ARRAY format (default)\n * ```typescript\n * const { data } = useAnalyticsQuery(\"spend_data\", params);\n * // data: Array<{ group_key: string; cost_usd: number; ... }> | null\n * ```\n *\n * @example ARROW_STREAM format\n * ```typescript\n * const { data } = useAnalyticsQuery(\"spend_data\", params, { format: \"ARROW_STREAM\" });\n * // data: TypedArrowTable<{ group_key: string; cost_usd: number; ... }> | null\n * ```\n */\nexport function useAnalyticsQuery<\n T = unknown,\n K extends QueryKey = QueryKey,\n F extends AnalyticsFormat = \"JSON_ARRAY\",\n>(\n queryKey: K,\n parameters?: InferParams<K> | null,\n options: UseAnalyticsQueryOptions<F> = {} as UseAnalyticsQueryOptions<F>,\n): UseAnalyticsQueryResult<InferResultByFormat<T, K, F>> {\n const format = options?.format ?? \"JSON_ARRAY\";\n const maxParametersSize = options?.maxParametersSize ?? 100 * 1024;\n const autoStart = options?.autoStart ?? true;\n\n const devMode = getDevMode();\n const urlSuffix = `/api/analytics/query/${encodeURIComponent(queryKey)}${devMode}`;\n\n type ResultType = InferResultByFormat<T, K, F>;\n const [data, setData] = useState<ResultType | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [errorCode, setErrorCode] = useState<string | null>(null);\n const [warehouseStatus, setWarehouseStatus] =\n useState<WarehouseStatus | null>(null);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const publisherId = useId();\n const {\n publish: publishWarehouseStatus,\n unpublish: unpublishWarehouseStatus,\n } = useAnalyticsWarehousePublisher(publisherId, queryKey);\n\n if (!queryKey || queryKey.trim().length === 0) {\n throw new Error(\n \"useAnalyticsQuery: 'queryKey' must be a non-empty string.\",\n );\n }\n\n const stableParameters = useStableParams(parameters);\n\n const payload = useMemo(() => {\n try {\n const serialized = JSON.stringify({\n parameters: stableParameters,\n format,\n });\n const sizeInBytes = new Blob([serialized]).size;\n if (sizeInBytes > maxParametersSize) {\n throw new Error(\n \"useAnalyticsQuery: Parameters size exceeds the maximum allowed size\",\n );\n }\n\n return serialized;\n } catch (error) {\n console.error(\"useAnalyticsQuery: Failed to serialize parameters\", error);\n return null;\n }\n }, [stableParameters, format, maxParametersSize]);\n\n const start = useCallback(() => {\n if (payload === null) {\n setError(\"Failed to serialize query parameters\");\n return;\n }\n\n abortControllerRef.current?.abort();\n\n setLoading(true);\n setError(null);\n setErrorCode(null);\n setData(null);\n setWarehouseStatus(null);\n publishWarehouseStatus(null);\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n // ARROW_STREAM: the server streams raw Arrow IPC bytes back on the query\n // response body (no SSE). Fetch and decode directly.\n if (format === \"ARROW_STREAM\") {\n void fetchArrowDirect({\n url: urlSuffix,\n payload,\n signal: abortController.signal,\n setLoading,\n setError,\n setErrorCode,\n setData: (table) => setData(table as ResultType),\n unpublishWarehouseStatus,\n });\n return;\n }\n\n const sseContext: AnalyticsSseHandlerContext = {\n source: \"useAnalyticsQuery\",\n resource: { queryKey },\n defaultExecutionError: \"Unable to execute query\",\n unpublishOnMalformedMessage: false,\n signal: abortController.signal,\n abort: () => abortController.abort(),\n setLoading,\n setError,\n setErrorCode,\n onWarehouseStatus: (status) => {\n setWarehouseStatus(status);\n publishWarehouseStatus(status);\n },\n onResult: (message) => setData(message.data as ResultType),\n unpublishWarehouseStatus,\n };\n\n connectSSE({\n url: urlSuffix,\n payload,\n signal: abortController.signal,\n onMessage: (message) =>\n handleAnalyticsSseMessage(message.data, sseContext),\n onError: (error) => handleAnalyticsSseError(error, sseContext),\n });\n }, [\n queryKey,\n payload,\n urlSuffix,\n format,\n publishWarehouseStatus,\n unpublishWarehouseStatus,\n ]);\n\n useEffect(() => {\n if (autoStart) {\n start();\n }\n\n return () => {\n abortControllerRef.current?.abort();\n unpublishWarehouseStatus();\n };\n }, [start, autoStart, unpublishWarehouseStatus]);\n\n useQueryHMR(queryKey, start);\n\n return { data, loading, error, errorCode, warehouseStatus };\n}\n"],"mappings":";;;;;;;;;AA8BA,SAAS,mBAAmB,GAAY,GAAqB;AAC3D,KAAI,OAAO,GAAG,GAAG,EAAE,CAAE,QAAO;AAC5B,KACE,MAAM,QACN,MAAM,QACN,OAAO,MAAM,YACb,OAAO,MAAM,SAEb,QAAO;CAET,MAAM,QAAQ,OAAO,KAAK,EAA6B;CACvD,MAAM,QAAQ,OAAO,KAAK,EAA6B;AACvD,KAAI,MAAM,WAAW,MAAM,OAAQ,QAAO;AAC1C,MAAK,MAAM,OAAO,OAAO;AACvB,MAAI,CAAC,OAAO,OAAO,GAAG,IAAI,CAAE,QAAO;AACnC,MACE,CAAC,OAAO,GACL,EAA8B,MAC9B,EAA8B,KAChC,CAED,QAAO;;AAGX,QAAO;;;AAIT,SAAS,gBAAmB,OAAa;CACvC,MAAM,MAAM,OAAU,MAAM;AAC5B,KAAI,CAAC,mBAAmB,IAAI,SAAS,MAAM,CACzC,KAAI,UAAU;AAEhB,QAAO,IAAI;;;;;;;;AAoBb,eAAe,kBACb,aACA,QAC+B;AAC/B,KAAI;EACF,MAAM,MAAM,MAAM,MAChB,0BAA0B,mBAAmB,YAAY,IACzD,EAAE,QAAQ,CACX;AACD,MAAI,CAAC,IAAI,GAAI,QAAO;EACpB,MAAM,OAAQ,MAAM,IAAI,MAAM;AAC9B,SAAO,MAAM,QAAQ,KAAK,QAAQ,GAAI,KAAK,UAAuB;SAC5D;AACN;;;;;;;;;AAUJ,eAAe,iBAAiB,KAAwC;AACtE,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,IAAI,KAAK;GACpC,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAC/C,MAAM,IAAI;GACV,QAAQ,IAAI;GACb,CAAC;AACF,MAAI,IAAI,OAAO,QAAS;AAExB,MAAI,CAAC,SAAS,IAAI;GAChB,IAAI,UAAU;GACd,IAAI,OAAsB;AAC1B,OAAI;IACF,MAAM,OAAQ,MAAM,SAAS,MAAM;AAInC,QAAI,KAAK,MAAO,WAAU,KAAK;AAC/B,QAAI,OAAO,KAAK,cAAc,SAAU,QAAO,KAAK;WAC9C;AAGR,OAAI,WAAW,MAAM;AACrB,OAAI,SAAS,QAAQ;AACrB,OAAI,KAAM,KAAI,aAAa,KAAK;AAChC,OAAI,0BAA0B;AAC9B;;EAGF,MAAM,SAAS,MAAM,SAAS,aAAa;AAC3C,MAAI,IAAI,OAAO,QAAS;EAMxB,IAAI;EACJ,MAAM,SAAS,SAAS,QAAQ,IAAI,yBAAyB;AAC7D,MAAI,OACF,KAAI;AACF,iBAAc,KAAK,MAAM,mBAAmB,OAAO,CAAC;UAC9C;OAGH;GACL,MAAM,MAAM,SAAS,QAAQ,IAAI,6BAA6B;AAC9D,OAAI,IACF,eAAc,MAAM,kBAAkB,KAAK,IAAI,OAAO;;EAG1D,MAAM,QAAQ,MAAM,YAAY,mBAC9B,IAAI,WAAW,OAAO,EACtB,YACD;AACD,MAAI,QAAQ,MAAM;AAClB,MAAI,WAAW,MAAM;AACrB,MAAI,0BAA0B;UACvB,OAAO;AACd,MAAI,IAAI,OAAO,QAAS;AACxB,MAAI,WAAW,MAAM;AACrB,MAAI,0BAA0B;AAC9B,MAAI,SAAS,qBAAqB,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmC7C,SAAgB,kBAKd,UACA,YACA,UAAuC,EAAE,EACc;CACvD,MAAM,SAAS,SAAS,UAAU;CAClC,MAAM,oBAAoB,SAAS,qBAAqB,MAAM;CAC9D,MAAM,YAAY,SAAS,aAAa;CAExC,MAAM,UAAU,YAAY;CAC5B,MAAM,YAAY,wBAAwB,mBAAmB,SAAS,GAAG;CAGzE,MAAM,CAAC,MAAM,WAAW,SAA4B,KAAK;CACzD,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;CAC7C,MAAM,CAAC,OAAO,YAAY,SAAwB,KAAK;CACvD,MAAM,CAAC,WAAW,gBAAgB,SAAwB,KAAK;CAC/D,MAAM,CAAC,iBAAiB,sBACtB,SAAiC,KAAK;CACxC,MAAM,qBAAqB,OAA+B,KAAK;CAG/D,MAAM,EACJ,SAAS,wBACT,WAAW,6BACT,+BAJgB,OAAO,EAIqB,SAAS;AAEzD,KAAI,CAAC,YAAY,SAAS,MAAM,CAAC,WAAW,EAC1C,OAAM,IAAI,MACR,4DACD;CAGH,MAAM,mBAAmB,gBAAgB,WAAW;CAEpD,MAAM,UAAU,cAAc;AAC5B,MAAI;GACF,MAAM,aAAa,KAAK,UAAU;IAChC,YAAY;IACZ;IACD,CAAC;AAEF,OADoB,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC,OACzB,kBAChB,OAAM,IAAI,MACR,sEACD;AAGH,UAAO;WACA,OAAO;AACd,WAAQ,MAAM,qDAAqD,MAAM;AACzE,UAAO;;IAER;EAAC;EAAkB;EAAQ;EAAkB,CAAC;CAEjD,MAAM,QAAQ,kBAAkB;AAC9B,MAAI,YAAY,MAAM;AACpB,YAAS,uCAAuC;AAChD;;AAGF,qBAAmB,SAAS,OAAO;AAEnC,aAAW,KAAK;AAChB,WAAS,KAAK;AACd,eAAa,KAAK;AAClB,UAAQ,KAAK;AACb,qBAAmB,KAAK;AACxB,yBAAuB,KAAK;EAE5B,MAAM,kBAAkB,IAAI,iBAAiB;AAC7C,qBAAmB,UAAU;AAI7B,MAAI,WAAW,gBAAgB;AAC7B,GAAK,iBAAiB;IACpB,KAAK;IACL;IACA,QAAQ,gBAAgB;IACxB;IACA;IACA;IACA,UAAU,UAAU,QAAQ,MAAoB;IAChD;IACD,CAAC;AACF;;EAGF,MAAM,aAAyC;GAC7C,QAAQ;GACR,UAAU,EAAE,UAAU;GACtB,uBAAuB;GACvB,6BAA6B;GAC7B,QAAQ,gBAAgB;GACxB,aAAa,gBAAgB,OAAO;GACpC;GACA;GACA;GACA,oBAAoB,WAAW;AAC7B,uBAAmB,OAAO;AAC1B,2BAAuB,OAAO;;GAEhC,WAAW,YAAY,QAAQ,QAAQ,KAAmB;GAC1D;GACD;AAED,aAAW;GACT,KAAK;GACL;GACA,QAAQ,gBAAgB;GACxB,YAAY,YACV,0BAA0B,QAAQ,MAAM,WAAW;GACrD,UAAU,UAAU,wBAAwB,OAAO,WAAW;GAC/D,CAAC;IACD;EACD;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;AAEF,iBAAgB;AACd,MAAI,UACF,QAAO;AAGT,eAAa;AACX,sBAAmB,SAAS,OAAO;AACnC,6BAA0B;;IAE3B;EAAC;EAAO;EAAW;EAAyB,CAAC;AAEhD,aAAY,UAAU,MAAM;AAE5B,QAAO;EAAE;EAAM;EAAS;EAAO;EAAW;EAAiB"}
1
+ {"version":3,"file":"use-analytics-query.js","names":["subscribe","store.subscribe","getSnapshot","store.getSnapshot","start","store.start","store.retain"],"sources":["../../../src/react/hooks/use-analytics-query.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\n\nimport * as store from \"./analytics-request-store\";\nimport { getDevMode } from \"./analytics-sse\";\nimport type {\n AnalyticsFormat,\n InferParams,\n InferResultByFormat,\n QueryKey,\n UseAnalyticsQueryOptions,\n UseAnalyticsQueryResult,\n} from \"./types\";\nimport { useAnalyticsWarehousePublisher } from \"./use-analytics-warehouse-status\";\nimport { useQueryHMR } from \"./use-query-hmr\";\n\n/** Shallow equality for plain-object query parameters (primitive values only). */\nfunction shallowEqualParams(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (\n a === null ||\n b === null ||\n typeof a !== \"object\" ||\n typeof b !== \"object\"\n ) {\n return false;\n }\n const aKeys = Object.keys(a as Record<string, unknown>);\n const bKeys = Object.keys(b as Record<string, unknown>);\n if (aKeys.length !== bKeys.length) return false;\n for (const key of aKeys) {\n if (!Object.hasOwn(b, key)) return false;\n if (\n !Object.is(\n (a as Record<string, unknown>)[key],\n (b as Record<string, unknown>)[key],\n )\n ) {\n return false;\n }\n }\n return true;\n}\n\n/** Keep structurally-equal params referentially stable across renders. */\nfunction useStableParams<T>(value: T): T {\n const ref = useRef<T>(value);\n if (!shallowEqualParams(ref.current, value)) {\n ref.current = value;\n }\n return ref.current;\n}\n\n/**\n * Subscribe to an analytics query and return its latest result. JSON_ARRAY\n * results stream over SSE (with warehouse-readiness progress); ARROW_STREAM\n * results are fetched as raw Arrow bytes directly from the query endpoint.\n * Integration hook between client and analytics plugin.\n *\n * Identical requests (same query key, parameters, format, and dev mode) share\n * a single in-flight network request: the first mounting instance starts it,\n * later instances subscribe to the same {@link store} entry and see the same\n * result and warehouse-status updates. The request is torn down once its last\n * subscriber unmounts.\n *\n * The return type is automatically inferred based on the format:\n * - `format: \"JSON_ARRAY\"` (default): Returns typed array from QueryRegistry\n * - `format: \"ARROW_STREAM\"`: Returns TypedArrowTable with row type preserved\n *\n * Note: User context execution is determined by query file naming:\n * - `queryKey.obo.sql`: Executes as user (OBO = on-behalf-of / user delegation)\n * - `queryKey.sql`: Executes as service principal\n *\n * @param queryKey - Analytics query identifier\n * @param parameters - Query parameters (type-safe based on QueryRegistry)\n * @param options - Analytics query settings including format\n * @returns Query result state with format-appropriate data type\n *\n * @example JSON_ARRAY format (default)\n * ```typescript\n * const { data } = useAnalyticsQuery(\"spend_data\", params);\n * // data: Array<{ group_key: string; cost_usd: number; ... }> | null\n * ```\n *\n * @example ARROW_STREAM format\n * ```typescript\n * const { data } = useAnalyticsQuery(\"spend_data\", params, { format: \"ARROW_STREAM\" });\n * // data: TypedArrowTable<{ group_key: string; cost_usd: number; ... }> | null\n * ```\n */\nexport function useAnalyticsQuery<\n T = unknown,\n K extends QueryKey = QueryKey,\n F extends AnalyticsFormat = \"JSON_ARRAY\",\n>(\n queryKey: K,\n parameters?: InferParams<K> | null,\n options: UseAnalyticsQueryOptions<F> = {} as UseAnalyticsQueryOptions<F>,\n): UseAnalyticsQueryResult<InferResultByFormat<T, K, F>> {\n const format = options?.format ?? \"JSON_ARRAY\";\n const maxParametersSize = options?.maxParametersSize ?? 100 * 1024;\n const autoStart = options?.autoStart ?? true;\n\n const devMode = getDevMode();\n const urlSuffix = `/api/analytics/query/${encodeURIComponent(queryKey)}${devMode}`;\n\n type ResultType = InferResultByFormat<T, K, F>;\n\n const publisherId = useId();\n const {\n publish: publishWarehouseStatus,\n unpublish: unpublishWarehouseStatus,\n } = useAnalyticsWarehousePublisher(publisherId, queryKey);\n\n if (!queryKey || queryKey.trim().length === 0) {\n throw new Error(\n \"useAnalyticsQuery: 'queryKey' must be a non-empty string.\",\n );\n }\n\n const stableParameters = useStableParams(parameters);\n\n const payload = useMemo(() => {\n try {\n const serialized = JSON.stringify({\n parameters: stableParameters,\n format,\n });\n const sizeInBytes = new Blob([serialized]).size;\n if (sizeInBytes > maxParametersSize) {\n throw new Error(\n \"useAnalyticsQuery: Parameters size exceeds the maximum allowed size\",\n );\n }\n\n return serialized;\n } catch (error) {\n console.error(\"useAnalyticsQuery: Failed to serialize parameters\", error);\n return null;\n }\n }, [stableParameters, format, maxParametersSize]);\n\n // Cache key shared across hook instances. `payload` already serializes\n // `{ parameters, format }`, so identical requests collapse to one key.\n // On a serialization failure (`payload === null`) the key stays unused: no\n // request is retained and the store reports the stable idle snapshot.\n const cacheKey = `${urlSuffix}::${payload}`;\n\n const subscribe = useCallback(\n (listener: () => void) => store.subscribe(cacheKey, listener),\n [cacheKey],\n );\n const getSnapshot = useCallback(\n () => store.getSnapshot(cacheKey),\n [cacheKey],\n );\n const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n const start = useCallback(() => store.start(cacheKey), [cacheKey]);\n\n // Register with the shared store on mount / key change; release on cleanup.\n // The store starts the request on first retain of a key and reuses the\n // in-flight request for later subscribers.\n useEffect(() => {\n if (payload === null) return;\n return store.retain(\n cacheKey,\n { url: urlSuffix, payload, format },\n autoStart,\n );\n }, [cacheKey, urlSuffix, payload, format, autoStart]);\n\n // Mirror this instance's warehouse status into the nearest resource-status\n // provider while the request is in flight; clear the slot once it settles.\n useEffect(() => {\n if (snapshot.loading) {\n publishWarehouseStatus(snapshot.warehouseStatus);\n } else {\n unpublishWarehouseStatus();\n }\n }, [\n snapshot.loading,\n snapshot.warehouseStatus,\n publishWarehouseStatus,\n unpublishWarehouseStatus,\n ]);\n\n useEffect(() => unpublishWarehouseStatus, [unpublishWarehouseStatus]);\n\n useQueryHMR(queryKey, start);\n\n return {\n data: snapshot.data as ResultType | null,\n loading: snapshot.loading,\n // A serialization failure never creates a store entry, so surface it here.\n error:\n payload === null\n ? \"Failed to serialize query parameters\"\n : snapshot.error,\n errorCode: snapshot.errorCode,\n warehouseStatus: snapshot.warehouseStatus,\n };\n}\n"],"mappings":";;;;;;;;AAuBA,SAAS,mBAAmB,GAAY,GAAqB;AAC3D,KAAI,OAAO,GAAG,GAAG,EAAE,CAAE,QAAO;AAC5B,KACE,MAAM,QACN,MAAM,QACN,OAAO,MAAM,YACb,OAAO,MAAM,SAEb,QAAO;CAET,MAAM,QAAQ,OAAO,KAAK,EAA6B;CACvD,MAAM,QAAQ,OAAO,KAAK,EAA6B;AACvD,KAAI,MAAM,WAAW,MAAM,OAAQ,QAAO;AAC1C,MAAK,MAAM,OAAO,OAAO;AACvB,MAAI,CAAC,OAAO,OAAO,GAAG,IAAI,CAAE,QAAO;AACnC,MACE,CAAC,OAAO,GACL,EAA8B,MAC9B,EAA8B,KAChC,CAED,QAAO;;AAGX,QAAO;;;AAIT,SAAS,gBAAmB,OAAa;CACvC,MAAM,MAAM,OAAU,MAAM;AAC5B,KAAI,CAAC,mBAAmB,IAAI,SAAS,MAAM,CACzC,KAAI,UAAU;AAEhB,QAAO,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCb,SAAgB,kBAKd,UACA,YACA,UAAuC,EAAE,EACc;CACvD,MAAM,SAAS,SAAS,UAAU;CAClC,MAAM,oBAAoB,SAAS,qBAAqB,MAAM;CAC9D,MAAM,YAAY,SAAS,aAAa;CAExC,MAAM,UAAU,YAAY;CAC5B,MAAM,YAAY,wBAAwB,mBAAmB,SAAS,GAAG;CAKzE,MAAM,EACJ,SAAS,wBACT,WAAW,6BACT,+BAJgB,OAAO,EAIqB,SAAS;AAEzD,KAAI,CAAC,YAAY,SAAS,MAAM,CAAC,WAAW,EAC1C,OAAM,IAAI,MACR,4DACD;CAGH,MAAM,mBAAmB,gBAAgB,WAAW;CAEpD,MAAM,UAAU,cAAc;AAC5B,MAAI;GACF,MAAM,aAAa,KAAK,UAAU;IAChC,YAAY;IACZ;IACD,CAAC;AAEF,OADoB,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC,OACzB,kBAChB,OAAM,IAAI,MACR,sEACD;AAGH,UAAO;WACA,OAAO;AACd,WAAQ,MAAM,qDAAqD,MAAM;AACzE,UAAO;;IAER;EAAC;EAAkB;EAAQ;EAAkB,CAAC;CAMjD,MAAM,WAAW,GAAG,UAAU,IAAI;CAElC,MAAMA,cAAY,aACf,aAAyBC,UAAgB,UAAU,SAAS,EAC7D,CAAC,SAAS,CACX;CACD,MAAMC,gBAAc,kBACZC,YAAkB,SAAS,EACjC,CAAC,SAAS,CACX;CACD,MAAM,WAAW,qBAAqBH,aAAWE,eAAaA,cAAY;CAE1E,MAAME,UAAQ,kBAAkBC,MAAY,SAAS,EAAE,CAAC,SAAS,CAAC;AAKlE,iBAAgB;AACd,MAAI,YAAY,KAAM;AACtB,SAAOC,OACL,UACA;GAAE,KAAK;GAAW;GAAS;GAAQ,EACnC,UACD;IACA;EAAC;EAAU;EAAW;EAAS;EAAQ;EAAU,CAAC;AAIrD,iBAAgB;AACd,MAAI,SAAS,QACX,wBAAuB,SAAS,gBAAgB;MAEhD,2BAA0B;IAE3B;EACD,SAAS;EACT,SAAS;EACT;EACA;EACD,CAAC;AAEF,iBAAgB,0BAA0B,CAAC,yBAAyB,CAAC;AAErE,aAAY,UAAUF,QAAM;AAE5B,QAAO;EACL,MAAM,SAAS;EACf,SAAS,SAAS;EAElB,OACE,YAAY,OACR,yCACA,SAAS;EACf,WAAW,SAAS;EACpB,iBAAiB,SAAS;EAC3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-analytics-warehouse-status.js","names":[],"sources":["../../../src/react/hooks/use-analytics-warehouse-status.ts"],"sourcesContent":["import { useCallback, useRef } from \"react\";\nimport type { WarehouseStatus } from \"./types\";\nimport {\n type ResourceSeverity,\n useResourceStatusPublisher,\n} from \"./use-resource-status\";\n\nconst ANALYTICS_WAREHOUSE_RESOURCE_KIND = \"warehouse\";\n\n/**\n * - `RUNNING` → `null`; callers `unpublish` instead.\n * - `STARTING` / `STOPPED` / `STOPPING` → `pending`.\n * - `DELETED` / `DELETING` → `error` (config change required).\n */\nfunction severityForWarehouseState(\n state: WarehouseStatus[\"state\"],\n): ResourceSeverity | null {\n switch (state) {\n case \"RUNNING\":\n return null;\n case \"DELETED\":\n case \"DELETING\":\n return \"error\";\n default:\n return \"pending\";\n }\n}\n\n/**\n * Internal hook used by `useAnalyticsQuery` to mirror its current warehouse\n * status into the nearest provider. No-op when no provider is mounted.\n */\nexport function useAnalyticsWarehousePublisher(\n id: string,\n queryKey: string,\n): {\n publish: (status: WarehouseStatus | null) => void;\n unpublish: () => void;\n} {\n const { publish: publishGeneric, unpublish } = useResourceStatusPublisher(\n id,\n queryKey,\n { kindHint: ANALYTICS_WAREHOUSE_RESOURCE_KIND },\n );\n\n // Anchor `startedAt` to the first non-null status so `elapsedMs`\n // advances monotonically across successive `warehouse_status` events.\n const startedAtRef = useRef<number | null>(null);\n\n const publish = useCallback(\n (status: WarehouseStatus | null) => {\n // null covers \"register with no status yet\" *and* RUNNING — both\n // keep the slot registered without contributing to the aggregate.\n const severity = status && severityForWarehouseState(status.state);\n if (!status || !severity) {\n startedAtRef.current = null;\n publishGeneric(null);\n return;\n }\n if (startedAtRef.current === null) {\n startedAtRef.current = Date.now() - Math.max(0, status.elapsedMs);\n }\n publishGeneric({\n kind: ANALYTICS_WAREHOUSE_RESOURCE_KIND,\n state: status.state,\n severity,\n startedAt: startedAtRef.current,\n });\n },\n [publishGeneric],\n );\n\n return { publish, unpublish };\n}\n"],"mappings":";;;;AAOA,MAAM,oCAAoC;;;;;;AAO1C,SAAS,0BACP,OACyB;AACzB,SAAQ,OAAR;EACE,KAAK,UACH,QAAO;EACT,KAAK;EACL,KAAK,WACH,QAAO;EACT,QACE,QAAO;;;;;;;AAQb,SAAgB,+BACd,IACA,UAIA;CACA,MAAM,EAAE,SAAS,gBAAgB,cAAc,2BAC7C,IACA,UACA,EAAE,UAAU,mCAAmC,CAChD;CAID,MAAM,eAAe,OAAsB,KAAK;AAyBhD,QAAO;EAAE,SAvBO,aACb,WAAmC;GAGlC,MAAM,WAAW,UAAU,0BAA0B,OAAO,MAAM;AAClE,OAAI,CAAC,UAAU,CAAC,UAAU;AACxB,iBAAa,UAAU;AACvB,mBAAe,KAAK;AACpB;;AAEF,OAAI,aAAa,YAAY,KAC3B,cAAa,UAAU,KAAK,KAAK,GAAG,KAAK,IAAI,GAAG,OAAO,UAAU;AAEnE,kBAAe;IACb,MAAM;IACN,OAAO,OAAO;IACd;IACA,WAAW,aAAa;IACzB,CAAC;KAEJ,CAAC,eAAe,CACjB;EAEiB;EAAW"}
1
+ {"version":3,"file":"use-analytics-warehouse-status.js","names":[],"sources":["../../../src/react/hooks/use-analytics-warehouse-status.ts"],"sourcesContent":["import { useCallback, useRef } from \"react\";\n\nimport type { WarehouseStatus } from \"./types\";\nimport {\n type ResourceSeverity,\n useResourceStatusPublisher,\n} from \"./use-resource-status\";\n\nconst ANALYTICS_WAREHOUSE_RESOURCE_KIND = \"warehouse\";\n\n/**\n * - `RUNNING` → `null`; callers `unpublish` instead.\n * - `STARTING` / `STOPPED` / `STOPPING` → `pending`.\n * - `DELETED` / `DELETING` → `error` (config change required).\n */\nfunction severityForWarehouseState(\n state: WarehouseStatus[\"state\"],\n): ResourceSeverity | null {\n switch (state) {\n case \"RUNNING\":\n return null;\n case \"DELETED\":\n case \"DELETING\":\n return \"error\";\n default:\n return \"pending\";\n }\n}\n\n/**\n * Internal hook used by `useAnalyticsQuery` to mirror its current warehouse\n * status into the nearest provider. No-op when no provider is mounted.\n */\nexport function useAnalyticsWarehousePublisher(\n id: string,\n queryKey: string,\n): {\n publish: (status: WarehouseStatus | null) => void;\n unpublish: () => void;\n} {\n const { publish: publishGeneric, unpublish } = useResourceStatusPublisher(\n id,\n queryKey,\n { kindHint: ANALYTICS_WAREHOUSE_RESOURCE_KIND },\n );\n\n // Anchor `startedAt` to the first non-null status so `elapsedMs`\n // advances monotonically across successive `warehouse_status` events.\n const startedAtRef = useRef<number | null>(null);\n\n const publish = useCallback(\n (status: WarehouseStatus | null) => {\n // null covers \"register with no status yet\" *and* RUNNING — both\n // keep the slot registered without contributing to the aggregate.\n const severity = status && severityForWarehouseState(status.state);\n if (!status || !severity) {\n startedAtRef.current = null;\n publishGeneric(null);\n return;\n }\n if (startedAtRef.current === null) {\n startedAtRef.current = Date.now() - Math.max(0, status.elapsedMs);\n }\n publishGeneric({\n kind: ANALYTICS_WAREHOUSE_RESOURCE_KIND,\n state: status.state,\n severity,\n startedAt: startedAtRef.current,\n });\n },\n [publishGeneric],\n );\n\n return { publish, unpublish };\n}\n"],"mappings":";;;;AAQA,MAAM,oCAAoC;;;;;;AAO1C,SAAS,0BACP,OACyB;AACzB,SAAQ,OAAR;EACE,KAAK,UACH,QAAO;EACT,KAAK;EACL,KAAK,WACH,QAAO;EACT,QACE,QAAO;;;;;;;AAQb,SAAgB,+BACd,IACA,UAIA;CACA,MAAM,EAAE,SAAS,gBAAgB,cAAc,2BAC7C,IACA,UACA,EAAE,UAAU,mCAAmC,CAChD;CAID,MAAM,eAAe,OAAsB,KAAK;AAyBhD,QAAO;EAAE,SAvBO,aACb,WAAmC;GAGlC,MAAM,WAAW,UAAU,0BAA0B,OAAO,MAAM;AAClE,OAAI,CAAC,UAAU,CAAC,UAAU;AACxB,iBAAa,UAAU;AACvB,mBAAe,KAAK;AACpB;;AAEF,OAAI,aAAa,YAAY,KAC3B,cAAa,UAAU,KAAK,KAAK,GAAG,KAAK,IAAI,GAAG,OAAO,UAAU;AAEnE,kBAAe;IACb,MAAM;IACN,OAAO,OAAO;IACd;IACA,WAAW,aAAa;IACzB,CAAC;KAEJ,CAAC,eAAe,CACjB;EAEiB;EAAW"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-chart-data.d.ts","names":[],"sources":["../../../src/react/hooks/use-chart-data.ts"],"mappings":";;;;UAaiB,mBAAA;;EAEf,QAAA;EAFkC;EAIlC,UAAA,GAAa,MAAA;EAAA;;;;;;;EAQb,MAAA,GAAS,UAAA;EARI;EAUb,WAAA,OAAkB,IAAA,EAAM,CAAA,KAAM,CAAA;AAAA;AAAA,UAGf,kBAAA;EAHA;EAKf,IAAA,EAAM,SAAA;EALY;EAOlB,OAAA;EAP+B;EAS/B,OAAA;EANe;EAQf,KAAA;;EAEA,OAAA;EARA;;;;;;EAeA,eAAA,EAAiB,eAAA;AAAA;;;AAiEnB;;;;;;;;;;;;;;;;;iBAAgB,YAAA,CAAa,OAAA,EAAS,mBAAA,GAAsB,kBAAA"}
1
+ {"version":3,"file":"use-chart-data.d.ts","names":[],"sources":["../../../src/react/hooks/use-chart-data.ts"],"mappings":";;;;UAciB,mBAAA;;EAEf,QAAA;EAFkC;EAIlC,UAAA,GAAa,MAAA;EAAA;;;;;;;EAQb,MAAA,GAAS,UAAA;EARI;EAUb,WAAA,OAAkB,IAAA,EAAM,CAAA,KAAM,CAAA;AAAA;AAAA,UAGf,kBAAA;EAHA;EAKf,IAAA,EAAM,SAAA;EALY;EAOlB,OAAA;EAP+B;EAS/B,OAAA;EANe;EAQf,KAAA;;EAEA,OAAA;EARA;;;;;;EAeA,eAAA,EAAiB,eAAA;AAAA;;;AAiEnB;;;;;;;;;;;;;;;;;iBAAgB,YAAA,CAAa,OAAA,EAAS,mBAAA,GAAsB,kBAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-chart-data.js","names":[],"sources":["../../../src/react/hooks/use-chart-data.ts"],"sourcesContent":["import type { Table } from \"apache-arrow\";\nimport { useMemo } from \"react\";\nimport type { ChartData, DataFormat } from \"../charts/types\";\nimport type { WarehouseStatus } from \"./types\";\nimport { useAnalyticsQuery } from \"./use-analytics-query\";\n\n/** Threshold for auto-selecting Arrow format (row count hint) */\nconst ARROW_THRESHOLD = 500;\n\n// ============================================================================\n// Hook Options & Result Types\n// ============================================================================\n\nexport interface UseChartDataOptions {\n /** Analytics query key */\n queryKey: string;\n /** Query parameters */\n parameters?: Record<string, unknown>;\n /**\n * Data format preference\n * - \"json_array\": Force JSON format\n * - \"arrow_stream\": Force Arrow format\n * - \"auto\": Auto-select based on heuristics\n * @default \"auto\"\n */\n format?: DataFormat;\n /** Transform data after fetching */\n transformer?: <T>(data: T) => T;\n}\n\nexport interface UseChartDataResult {\n /** The fetched data (Arrow Table or JSON array) */\n data: ChartData | null;\n /** Whether the data is in Arrow format */\n isArrow: boolean;\n /** Loading state */\n loading: boolean;\n /** Error message if any */\n error: string | null;\n /** Whether the data is empty */\n isEmpty: boolean;\n /**\n * Latest warehouse readiness status from SSE. Retains the last value\n * (including `RUNNING`) until the next `start()`; `null` only before\n * the first event. Use with `loading` to distinguish warehouse warm-up\n * from in-flight SQL fetch.\n */\n warehouseStatus: WarehouseStatus | null;\n}\n\n// ============================================================================\n// Format Resolution\n// ============================================================================\n\n/**\n * Resolves the data format based on hints and preferences\n */\nfunction resolveFormat(\n format: DataFormat,\n parameters?: Record<string, unknown>,\n): \"JSON_ARRAY\" | \"ARROW_STREAM\" {\n // Explicit format selection (legacy \"json\"/\"arrow\" accepted for back-compat\n // with appkit-ui < 0.33.0 — see DataFormat in ../charts/types.ts).\n if (format === \"json_array\" || format === \"json\") return \"JSON_ARRAY\";\n if (format === \"arrow_stream\" || format === \"arrow\") return \"ARROW_STREAM\";\n\n // Auto-selection heuristics\n if (format === \"auto\") {\n // Check for explicit hint in parameters\n if (parameters?._preferArrow === true) return \"ARROW_STREAM\";\n if (parameters?._preferJson === true) return \"JSON_ARRAY\";\n\n // Check limit parameter as data size hint\n const limit = parameters?.limit;\n if (typeof limit === \"number\" && limit > ARROW_THRESHOLD) {\n return \"ARROW_STREAM\";\n }\n\n // Check for date range queries (often large)\n if (parameters?.startDate && parameters?.endDate) {\n return \"ARROW_STREAM\";\n }\n\n return \"JSON_ARRAY\";\n }\n\n return \"JSON_ARRAY\";\n}\n\n// ============================================================================\n// Main Hook\n// ============================================================================\n\n/**\n * Hook for fetching chart data in either JSON or Arrow format.\n * Automatically selects the best format based on query hints.\n *\n * @example\n * ```tsx\n * // Auto-select format\n * const { data, isArrow, loading } = useChartData({\n * queryKey: \"spend_data\",\n * parameters: { limit: 1000 }\n * });\n *\n * // Force Arrow format\n * const { data } = useChartData({\n * queryKey: \"big_query\",\n * format: \"arrow_stream\"\n * });\n * ```\n */\nexport function useChartData(options: UseChartDataOptions): UseChartDataResult {\n const { queryKey, parameters, format = \"auto\", transformer } = options;\n\n // Resolve the format to use\n const resolvedFormat = useMemo(\n () => resolveFormat(format, parameters),\n [format, parameters],\n );\n\n const isArrowFormat = resolvedFormat === \"ARROW_STREAM\";\n\n // Fetch data using the analytics query hook\n const {\n data: rawData,\n loading,\n error,\n warehouseStatus,\n } = useAnalyticsQuery(queryKey, parameters, {\n autoStart: true,\n format: resolvedFormat,\n });\n\n // Process and transform data\n const processedData = useMemo(() => {\n if (!rawData) return null;\n\n // Apply transformer if provided\n if (transformer) {\n try {\n return transformer(rawData);\n } catch (err) {\n console.error(\"[useChartData] Transformer error:\", err);\n return rawData;\n }\n }\n\n return rawData;\n }, [rawData, transformer]);\n\n // Determine if data is empty\n const isEmpty = useMemo(() => {\n if (!processedData) return true;\n\n // Arrow Table - check using duck typing\n if (\n typeof processedData === \"object\" &&\n \"numRows\" in processedData &&\n typeof (processedData as Table).numRows === \"number\"\n ) {\n return (processedData as Table).numRows === 0;\n }\n\n // JSON Array\n if (Array.isArray(processedData)) {\n return processedData.length === 0;\n }\n\n return true;\n }, [processedData]);\n\n // Detect actual data type (may differ from requested if server doesn't support format)\n const isArrow = useMemo(() => {\n if (!processedData) return isArrowFormat;\n // Duck type check for Arrow Table\n return (\n typeof processedData === \"object\" &&\n processedData !== null &&\n \"schema\" in processedData &&\n \"numRows\" in processedData &&\n typeof (processedData as Table).getChild === \"function\"\n );\n }, [processedData, isArrowFormat]);\n\n return {\n data: processedData as ChartData | null,\n isArrow,\n loading,\n error,\n isEmpty,\n warehouseStatus,\n };\n}\n"],"mappings":";;;;;AAOA,MAAM,kBAAkB;;;;AAkDxB,SAAS,cACP,QACA,YAC+B;AAG/B,KAAI,WAAW,gBAAgB,WAAW,OAAQ,QAAO;AACzD,KAAI,WAAW,kBAAkB,WAAW,QAAS,QAAO;AAG5D,KAAI,WAAW,QAAQ;AAErB,MAAI,YAAY,iBAAiB,KAAM,QAAO;AAC9C,MAAI,YAAY,gBAAgB,KAAM,QAAO;EAG7C,MAAM,QAAQ,YAAY;AAC1B,MAAI,OAAO,UAAU,YAAY,QAAQ,gBACvC,QAAO;AAIT,MAAI,YAAY,aAAa,YAAY,QACvC,QAAO;AAGT,SAAO;;AAGT,QAAO;;;;;;;;;;;;;;;;;;;;;AA0BT,SAAgB,aAAa,SAAkD;CAC7E,MAAM,EAAE,UAAU,YAAY,SAAS,QAAQ,gBAAgB;CAG/D,MAAM,iBAAiB,cACf,cAAc,QAAQ,WAAW,EACvC,CAAC,QAAQ,WAAW,CACrB;CAED,MAAM,gBAAgB,mBAAmB;CAGzC,MAAM,EACJ,MAAM,SACN,SACA,OACA,oBACE,kBAAkB,UAAU,YAAY;EAC1C,WAAW;EACX,QAAQ;EACT,CAAC;CAGF,MAAM,gBAAgB,cAAc;AAClC,MAAI,CAAC,QAAS,QAAO;AAGrB,MAAI,YACF,KAAI;AACF,UAAO,YAAY,QAAQ;WACpB,KAAK;AACZ,WAAQ,MAAM,qCAAqC,IAAI;AACvD,UAAO;;AAIX,SAAO;IACN,CAAC,SAAS,YAAY,CAAC;CAG1B,MAAM,UAAU,cAAc;AAC5B,MAAI,CAAC,cAAe,QAAO;AAG3B,MACE,OAAO,kBAAkB,YACzB,aAAa,iBACb,OAAQ,cAAwB,YAAY,SAE5C,QAAQ,cAAwB,YAAY;AAI9C,MAAI,MAAM,QAAQ,cAAc,CAC9B,QAAO,cAAc,WAAW;AAGlC,SAAO;IACN,CAAC,cAAc,CAAC;AAenB,QAAO;EACL,MAAM;EACN,SAdc,cAAc;AAC5B,OAAI,CAAC,cAAe,QAAO;AAE3B,UACE,OAAO,kBAAkB,YACzB,kBAAkB,QAClB,YAAY,iBACZ,aAAa,iBACb,OAAQ,cAAwB,aAAa;KAE9C,CAAC,eAAe,cAAc,CAAC;EAKhC;EACA;EACA;EACA;EACD"}
1
+ {"version":3,"file":"use-chart-data.js","names":[],"sources":["../../../src/react/hooks/use-chart-data.ts"],"sourcesContent":["import type { Table } from \"apache-arrow\";\nimport { useMemo } from \"react\";\n\nimport type { ChartData, DataFormat } from \"../charts/types\";\nimport type { WarehouseStatus } from \"./types\";\nimport { useAnalyticsQuery } from \"./use-analytics-query\";\n\n/** Threshold for auto-selecting Arrow format (row count hint) */\nconst ARROW_THRESHOLD = 500;\n\n// ============================================================================\n// Hook Options & Result Types\n// ============================================================================\n\nexport interface UseChartDataOptions {\n /** Analytics query key */\n queryKey: string;\n /** Query parameters */\n parameters?: Record<string, unknown>;\n /**\n * Data format preference\n * - \"json_array\": Force JSON format\n * - \"arrow_stream\": Force Arrow format\n * - \"auto\": Auto-select based on heuristics\n * @default \"auto\"\n */\n format?: DataFormat;\n /** Transform data after fetching */\n transformer?: <T>(data: T) => T;\n}\n\nexport interface UseChartDataResult {\n /** The fetched data (Arrow Table or JSON array) */\n data: ChartData | null;\n /** Whether the data is in Arrow format */\n isArrow: boolean;\n /** Loading state */\n loading: boolean;\n /** Error message if any */\n error: string | null;\n /** Whether the data is empty */\n isEmpty: boolean;\n /**\n * Latest warehouse readiness status from SSE. Retains the last value\n * (including `RUNNING`) until the next `start()`; `null` only before\n * the first event. Use with `loading` to distinguish warehouse warm-up\n * from in-flight SQL fetch.\n */\n warehouseStatus: WarehouseStatus | null;\n}\n\n// ============================================================================\n// Format Resolution\n// ============================================================================\n\n/**\n * Resolves the data format based on hints and preferences\n */\nfunction resolveFormat(\n format: DataFormat,\n parameters?: Record<string, unknown>,\n): \"JSON_ARRAY\" | \"ARROW_STREAM\" {\n // Explicit format selection (legacy \"json\"/\"arrow\" accepted for back-compat\n // with appkit-ui < 0.33.0 — see DataFormat in ../charts/types.ts).\n if (format === \"json_array\" || format === \"json\") return \"JSON_ARRAY\";\n if (format === \"arrow_stream\" || format === \"arrow\") return \"ARROW_STREAM\";\n\n // Auto-selection heuristics\n if (format === \"auto\") {\n // Check for explicit hint in parameters\n if (parameters?._preferArrow === true) return \"ARROW_STREAM\";\n if (parameters?._preferJson === true) return \"JSON_ARRAY\";\n\n // Check limit parameter as data size hint\n const limit = parameters?.limit;\n if (typeof limit === \"number\" && limit > ARROW_THRESHOLD) {\n return \"ARROW_STREAM\";\n }\n\n // Check for date range queries (often large)\n if (parameters?.startDate && parameters?.endDate) {\n return \"ARROW_STREAM\";\n }\n\n return \"JSON_ARRAY\";\n }\n\n return \"JSON_ARRAY\";\n}\n\n// ============================================================================\n// Main Hook\n// ============================================================================\n\n/**\n * Hook for fetching chart data in either JSON or Arrow format.\n * Automatically selects the best format based on query hints.\n *\n * @example\n * ```tsx\n * // Auto-select format\n * const { data, isArrow, loading } = useChartData({\n * queryKey: \"spend_data\",\n * parameters: { limit: 1000 }\n * });\n *\n * // Force Arrow format\n * const { data } = useChartData({\n * queryKey: \"big_query\",\n * format: \"arrow_stream\"\n * });\n * ```\n */\nexport function useChartData(options: UseChartDataOptions): UseChartDataResult {\n const { queryKey, parameters, format = \"auto\", transformer } = options;\n\n // Resolve the format to use\n const resolvedFormat = useMemo(\n () => resolveFormat(format, parameters),\n [format, parameters],\n );\n\n const isArrowFormat = resolvedFormat === \"ARROW_STREAM\";\n\n // Fetch data using the analytics query hook\n const {\n data: rawData,\n loading,\n error,\n warehouseStatus,\n } = useAnalyticsQuery(queryKey, parameters, {\n autoStart: true,\n format: resolvedFormat,\n });\n\n // Process and transform data\n const processedData = useMemo(() => {\n if (!rawData) return null;\n\n // Apply transformer if provided\n if (transformer) {\n try {\n return transformer(rawData);\n } catch (err) {\n console.error(\"[useChartData] Transformer error:\", err);\n return rawData;\n }\n }\n\n return rawData;\n }, [rawData, transformer]);\n\n // Determine if data is empty\n const isEmpty = useMemo(() => {\n if (!processedData) return true;\n\n // Arrow Table - check using duck typing\n if (\n typeof processedData === \"object\" &&\n \"numRows\" in processedData &&\n typeof (processedData as Table).numRows === \"number\"\n ) {\n return (processedData as Table).numRows === 0;\n }\n\n // JSON Array\n if (Array.isArray(processedData)) {\n return processedData.length === 0;\n }\n\n return true;\n }, [processedData]);\n\n // Detect actual data type (may differ from requested if server doesn't support format)\n const isArrow = useMemo(() => {\n if (!processedData) return isArrowFormat;\n // Duck type check for Arrow Table\n return (\n typeof processedData === \"object\" &&\n processedData !== null &&\n \"schema\" in processedData &&\n \"numRows\" in processedData &&\n typeof (processedData as Table).getChild === \"function\"\n );\n }, [processedData, isArrowFormat]);\n\n return {\n data: processedData as ChartData | null,\n isArrow,\n loading,\n error,\n isEmpty,\n warehouseStatus,\n };\n}\n"],"mappings":";;;;;AAQA,MAAM,kBAAkB;;;;AAkDxB,SAAS,cACP,QACA,YAC+B;AAG/B,KAAI,WAAW,gBAAgB,WAAW,OAAQ,QAAO;AACzD,KAAI,WAAW,kBAAkB,WAAW,QAAS,QAAO;AAG5D,KAAI,WAAW,QAAQ;AAErB,MAAI,YAAY,iBAAiB,KAAM,QAAO;AAC9C,MAAI,YAAY,gBAAgB,KAAM,QAAO;EAG7C,MAAM,QAAQ,YAAY;AAC1B,MAAI,OAAO,UAAU,YAAY,QAAQ,gBACvC,QAAO;AAIT,MAAI,YAAY,aAAa,YAAY,QACvC,QAAO;AAGT,SAAO;;AAGT,QAAO;;;;;;;;;;;;;;;;;;;;;AA0BT,SAAgB,aAAa,SAAkD;CAC7E,MAAM,EAAE,UAAU,YAAY,SAAS,QAAQ,gBAAgB;CAG/D,MAAM,iBAAiB,cACf,cAAc,QAAQ,WAAW,EACvC,CAAC,QAAQ,WAAW,CACrB;CAED,MAAM,gBAAgB,mBAAmB;CAGzC,MAAM,EACJ,MAAM,SACN,SACA,OACA,oBACE,kBAAkB,UAAU,YAAY;EAC1C,WAAW;EACX,QAAQ;EACT,CAAC;CAGF,MAAM,gBAAgB,cAAc;AAClC,MAAI,CAAC,QAAS,QAAO;AAGrB,MAAI,YACF,KAAI;AACF,UAAO,YAAY,QAAQ;WACpB,KAAK;AACZ,WAAQ,MAAM,qCAAqC,IAAI;AACvD,UAAO;;AAIX,SAAO;IACN,CAAC,SAAS,YAAY,CAAC;CAG1B,MAAM,UAAU,cAAc;AAC5B,MAAI,CAAC,cAAe,QAAO;AAG3B,MACE,OAAO,kBAAkB,YACzB,aAAa,iBACb,OAAQ,cAAwB,YAAY,SAE5C,QAAQ,cAAwB,YAAY;AAI9C,MAAI,MAAM,QAAQ,cAAc,CAC9B,QAAO,cAAc,WAAW;AAGlC,SAAO;IACN,CAAC,cAAc,CAAC;AAenB,QAAO;EACL,MAAM;EACN,SAdc,cAAc;AAC5B,OAAI,CAAC,cAAe,QAAO;AAE3B,UACE,OAAO,kBAAkB,YACzB,kBAAkB,QAClB,YAAY,iBACZ,aAAa,iBACb,OAAQ,cAAwB,aAAa;KAE9C,CAAC,eAAe,cAAc,CAAC;EAKhC;EACA;EACA;EACA;EACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-metric-view.d.ts","names":[],"sources":["../../../src/react/hooks/use-metric-view.ts"],"mappings":";;;;;AAqEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,aAAA,WACJ,SAAA,GAAY,SAAA,kBACN,aAAA,CAAc,gBAAA,CAAiB,CAAA,KAAM,aAAA,CACnD,gBAAA,CAAiB,CAAA,oBAEH,aAAA,CAAc,kBAAA,CAAmB,CAAA,iBAAA,CAEjD,GAAA,EAAK,CAAA,EACL,OAAA,EAAS,oBAAA,CAAqB,CAAA,EAAG,CAAA,EAAG,CAAA,IACnC,mBAAA,CAAoB,aAAA,CAAc,CAAA,EAAG,CAAA,EAAG,CAAA"}
1
+ {"version":3,"file":"use-metric-view.d.ts","names":[],"sources":["../../../src/react/hooks/use-metric-view.ts"],"mappings":";;;;;AAuEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,aAAA,WACJ,SAAA,GAAY,SAAA,kBACN,aAAA,CAAc,gBAAA,CAAiB,CAAA,KAAM,aAAA,CACnD,gBAAA,CAAiB,CAAA,oBAEH,aAAA,CAAc,kBAAA,CAAmB,CAAA,iBAAA,CAEjD,GAAA,EAAK,CAAA,EACL,OAAA,EAAS,oBAAA,CAAqB,CAAA,EAAG,CAAA,EAAG,CAAA,IACnC,mBAAA,CAAoB,aAAA,CAAc,CAAA,EAAG,CAAA,EAAG,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-metric-view.js","names":[],"sources":["../../../src/react/hooks/use-metric-view.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { MetricViewColumnDisplay } from \"shared\";\nimport { connectSSE } from \"@/js\";\nimport {\n type AnalyticsSseHandlerContext,\n getDevMode,\n handleAnalyticsSseError,\n handleAnalyticsSseMessage,\n} from \"./analytics-sse\";\nimport type {\n InferDimensionKeys,\n InferMeasureKeys,\n MetricKey,\n PickMetricRow,\n UseMetricViewOptions,\n UseMetricViewResult,\n WarehouseStatus,\n} from \"./types\";\nimport { useAnalyticsWarehousePublisher } from \"./use-analytics-warehouse-status\";\nimport { useQueryHMR } from \"./use-query-hmr\";\n\nfunction asMetricMetadata(\n value: unknown,\n): Record<string, MetricViewColumnDisplay> | undefined {\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n return value as Record<string, MetricViewColumnDisplay>;\n }\n return undefined;\n}\n\n/**\n * Subscribe to a Unity Catalog Metric View and return its latest result.\n *\n * @param key - Metric view identifier\n * @param options - Measures (required) plus optional dimensions, filter,\n * orderBy, timeGrain/timeDimension, and limit\n * @returns Metric result state with typed rows, display metadata, and warehouse readiness\n *\n * @remarks\n * `orderBy` and `limit` interact. With `limit`, the route completes the ordering\n * with the remaining dimensions so the capped rows are the *same* rows on every\n * run — pass `orderBy` to choose WHICH rows (top-N), since the completion only\n * makes the result stable, not ranked. Without `limit`, `orderBy` is presentation\n * ordering over the full result and gets no completion.\n *\n * When a request refetches with the same metric key, measures, and dimensions,\n * the previous `data` and `metadata` remain available while `loading` is true.\n * Changing any of those result-shaping fields clears the previous result.\n *\n * @example\n * ```typescript\n * const { data, metadata } = useMetricView(\"orders\", {\n * measures: [\"revenue\"],\n * dimensions: [\"region\"],\n * filter: { member: \"region\", operator: \"in\", values: [\"EMEA\", \"APAC\"] },\n * orderBy: [{ field: \"revenue\", direction: \"DESC\" }],\n * limit: 10,\n * });\n * // JSON_ARRAY preserves SQL scalar cells as strings and nullable columns as null:\n * // data: Array<{ revenue: string | null; region: string | null }> | null\n * ```\n */\nexport function useMetricView<\n K extends MetricKey = MetricKey,\n const M extends ReadonlyArray<InferMeasureKeys<K>> = ReadonlyArray<\n InferMeasureKeys<K>\n >,\n const D extends ReadonlyArray<InferDimensionKeys<K>> = readonly [],\n>(\n key: K,\n options: UseMetricViewOptions<K, M, D>,\n): UseMetricViewResult<PickMetricRow<K, M, D>[]> {\n const autoStart = options.autoStart ?? true;\n const devMode = getDevMode();\n const urlSuffix = `/api/analytics/metric/${encodeURIComponent(key)}${devMode}`;\n\n type Rows = PickMetricRow<K, M, D>[];\n const [result, setResult] = useState<{\n shape: string;\n data: Rows;\n metadata: Record<string, MetricViewColumnDisplay> | undefined;\n } | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [errorCode, setErrorCode] = useState<string | null>(null);\n const [warehouseStatus, setWarehouseStatus] =\n useState<WarehouseStatus | null>(null);\n const abortControllerRef = useRef<AbortController | null>(null);\n const activeRequestKeyRef = useRef<string | null>(null);\n const effectLeaseRef = useRef(0);\n const requestedShapeRef = useRef<string | null>(null);\n\n const publisherId = useId();\n const {\n publish: publishWarehouseStatus,\n unpublish: unpublishWarehouseStatus,\n } = useAnalyticsWarehousePublisher(publisherId, key);\n\n if (!key || key.trim().length === 0) {\n throw new Error(\"useMetricView: 'key' must be a non-empty string.\");\n }\n\n // Stringify to compare request by value; prevent re-firing on every render.\n const payload = useMemo(() => {\n const body: {\n measures: ReadonlyArray<unknown>;\n dimensions?: ReadonlyArray<unknown>;\n filter?: unknown;\n timeGrain?: unknown;\n timeDimension?: unknown;\n orderBy?: ReadonlyArray<unknown>;\n limit?: number;\n } = { measures: options.measures };\n if (options.dimensions !== undefined) body.dimensions = options.dimensions;\n if (options.filter !== undefined) body.filter = options.filter;\n if (options.timeGrain !== undefined) body.timeGrain = options.timeGrain;\n if (options.timeDimension !== undefined)\n body.timeDimension = options.timeDimension;\n if (options.orderBy !== undefined) body.orderBy = options.orderBy;\n if (options.limit !== undefined) body.limit = options.limit;\n return JSON.stringify(body);\n }, [\n options.measures,\n options.dimensions,\n options.filter,\n options.timeGrain,\n options.timeDimension,\n options.orderBy,\n options.limit,\n ]);\n\n // Detect shape changes: clear rows when metric/measures/dimensions change,\n // but keep stale rows during filter/order/limit/time-grain revalidations.\n const resultShape = JSON.stringify({\n key,\n measures: options.measures,\n dimensions: options.dimensions ?? [],\n });\n const requestKey = `${urlSuffix}\\0${payload}`;\n\n // Return stale rows only if shape is current; hide during shape transitions.\n const isCurrentShape = result?.shape === resultShape;\n const data = isCurrentShape ? result.data : null;\n const metadata = isCurrentShape ? result.metadata : undefined;\n\n const start = useCallback(() => {\n abortControllerRef.current?.abort();\n\n setLoading(true);\n setError(null);\n setErrorCode(null);\n setWarehouseStatus(null);\n if (requestedShapeRef.current !== resultShape) {\n requestedShapeRef.current = resultShape;\n setResult(null);\n }\n // Register an empty slot to clear stale status from the prior run.\n publishWarehouseStatus(null);\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n activeRequestKeyRef.current = requestKey;\n\n const sseContext: AnalyticsSseHandlerContext = {\n source: \"useMetricView\",\n resource: { key },\n defaultExecutionError: \"Unable to execute metric query\",\n unpublishOnMalformedMessage: true,\n signal: abortController.signal,\n abort: () => abortController.abort(),\n setLoading,\n setError,\n setErrorCode,\n onWarehouseStatus: (status) => {\n setWarehouseStatus(status);\n publishWarehouseStatus(status);\n },\n onResult: (message) => {\n setError(null);\n setErrorCode(null);\n setResult({\n shape: resultShape,\n data: message.data as Rows,\n metadata: asMetricMetadata(message.payload.metadata),\n });\n },\n unpublishWarehouseStatus,\n };\n\n connectSSE({\n url: urlSuffix,\n payload,\n signal: abortController.signal,\n onMessage: (message) =>\n handleAnalyticsSseMessage(message.data, sseContext),\n onError: (error) => handleAnalyticsSseError(error, sseContext),\n });\n }, [\n key,\n payload,\n requestKey,\n resultShape,\n urlSuffix,\n publishWarehouseStatus,\n unpublishWarehouseStatus,\n ]);\n\n useEffect(() => {\n const lease = ++effectLeaseRef.current;\n\n if (autoStart) {\n // React Strict Mode runs an effect's setup/cleanup/setup sequence on\n // mount. Keep the still-active identical stream for the second setup so\n // development sends one POST, not an immediately-aborted duplicate.\n // A real request change still starts synchronously and aborts the prior\n // controller in start().\n const activeController = abortControllerRef.current;\n if (\n activeRequestKeyRef.current !== requestKey ||\n !activeController ||\n activeController.signal.aborted\n ) {\n start();\n }\n } else {\n abortControllerRef.current?.abort();\n activeRequestKeyRef.current = null;\n unpublishWarehouseStatus();\n }\n\n return () => {\n // Defer teardown by one microtask so Strict Mode's immediate second\n // setup can claim the same request. On a genuine unmount there is no new\n // lease, so the stream is still cancelled before later async work runs.\n const controller = abortControllerRef.current;\n queueMicrotask(() => {\n if (effectLeaseRef.current !== lease) return;\n controller?.abort();\n if (abortControllerRef.current === controller) {\n activeRequestKeyRef.current = null;\n }\n unpublishWarehouseStatus();\n });\n };\n }, [start, autoStart, requestKey, unpublishWarehouseStatus]);\n\n useQueryHMR(key, start);\n\n return { data, loading, error, errorCode, metadata, warehouseStatus };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-metric-view.js","names":[],"sources":["../../../src/react/hooks/use-metric-view.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { MetricViewColumnDisplay } from \"shared\";\n\nimport { connectSSE } from \"@/js\";\n\nimport {\n type AnalyticsSseHandlerContext,\n getDevMode,\n handleAnalyticsSseError,\n handleAnalyticsSseMessage,\n} from \"./analytics-sse\";\nimport type {\n InferDimensionKeys,\n InferMeasureKeys,\n MetricKey,\n PickMetricRow,\n UseMetricViewOptions,\n UseMetricViewResult,\n WarehouseStatus,\n} from \"./types\";\nimport { useAnalyticsWarehousePublisher } from \"./use-analytics-warehouse-status\";\nimport { useQueryHMR } from \"./use-query-hmr\";\n\nfunction asMetricMetadata(\n value: unknown,\n): Record<string, MetricViewColumnDisplay> | undefined {\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n return value as Record<string, MetricViewColumnDisplay>;\n }\n return undefined;\n}\n\n/**\n * Subscribe to a Unity Catalog Metric View and return its latest result.\n *\n * @param key - Metric view identifier\n * @param options - Measures (required) plus optional dimensions, filter,\n * orderBy, timeGrain/timeDimension, and limit\n * @returns Metric result state with typed rows, display metadata, and warehouse readiness\n *\n * @remarks\n * `orderBy` and `limit` interact. With `limit`, the route completes the ordering\n * with the remaining dimensions so the capped rows are the *same* rows on every\n * run — pass `orderBy` to choose WHICH rows (top-N), since the completion only\n * makes the result stable, not ranked. Without `limit`, `orderBy` is presentation\n * ordering over the full result and gets no completion.\n *\n * When a request refetches with the same metric key, measures, and dimensions,\n * the previous `data` and `metadata` remain available while `loading` is true.\n * Changing any of those result-shaping fields clears the previous result.\n *\n * @example\n * ```typescript\n * const { data, metadata } = useMetricView(\"orders\", {\n * measures: [\"revenue\"],\n * dimensions: [\"region\"],\n * filter: { member: \"region\", operator: \"in\", values: [\"EMEA\", \"APAC\"] },\n * orderBy: [{ field: \"revenue\", direction: \"DESC\" }],\n * limit: 10,\n * });\n * // JSON_ARRAY preserves SQL scalar cells as strings and nullable columns as null:\n * // data: Array<{ revenue: string | null; region: string | null }> | null\n * ```\n */\nexport function useMetricView<\n K extends MetricKey = MetricKey,\n const M extends ReadonlyArray<InferMeasureKeys<K>> = ReadonlyArray<\n InferMeasureKeys<K>\n >,\n const D extends ReadonlyArray<InferDimensionKeys<K>> = readonly [],\n>(\n key: K,\n options: UseMetricViewOptions<K, M, D>,\n): UseMetricViewResult<PickMetricRow<K, M, D>[]> {\n const autoStart = options.autoStart ?? true;\n const devMode = getDevMode();\n const urlSuffix = `/api/analytics/metric/${encodeURIComponent(key)}${devMode}`;\n\n type Rows = PickMetricRow<K, M, D>[];\n const [result, setResult] = useState<{\n shape: string;\n data: Rows;\n metadata: Record<string, MetricViewColumnDisplay> | undefined;\n } | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [errorCode, setErrorCode] = useState<string | null>(null);\n const [warehouseStatus, setWarehouseStatus] =\n useState<WarehouseStatus | null>(null);\n const abortControllerRef = useRef<AbortController | null>(null);\n const activeRequestKeyRef = useRef<string | null>(null);\n const effectLeaseRef = useRef(0);\n const requestedShapeRef = useRef<string | null>(null);\n\n const publisherId = useId();\n const {\n publish: publishWarehouseStatus,\n unpublish: unpublishWarehouseStatus,\n } = useAnalyticsWarehousePublisher(publisherId, key);\n\n if (!key || key.trim().length === 0) {\n throw new Error(\"useMetricView: 'key' must be a non-empty string.\");\n }\n\n // Stringify to compare request by value; prevent re-firing on every render.\n const payload = useMemo(() => {\n const body: {\n measures: ReadonlyArray<unknown>;\n dimensions?: ReadonlyArray<unknown>;\n filter?: unknown;\n timeGrain?: unknown;\n timeDimension?: unknown;\n orderBy?: ReadonlyArray<unknown>;\n limit?: number;\n } = { measures: options.measures };\n if (options.dimensions !== undefined) body.dimensions = options.dimensions;\n if (options.filter !== undefined) body.filter = options.filter;\n if (options.timeGrain !== undefined) body.timeGrain = options.timeGrain;\n if (options.timeDimension !== undefined)\n body.timeDimension = options.timeDimension;\n if (options.orderBy !== undefined) body.orderBy = options.orderBy;\n if (options.limit !== undefined) body.limit = options.limit;\n return JSON.stringify(body);\n }, [\n options.measures,\n options.dimensions,\n options.filter,\n options.timeGrain,\n options.timeDimension,\n options.orderBy,\n options.limit,\n ]);\n\n // Detect shape changes: clear rows when metric/measures/dimensions change,\n // but keep stale rows during filter/order/limit/time-grain revalidations.\n const resultShape = JSON.stringify({\n key,\n measures: options.measures,\n dimensions: options.dimensions ?? [],\n });\n const requestKey = `${urlSuffix}\\0${payload}`;\n\n // Return stale rows only if shape is current; hide during shape transitions.\n const isCurrentShape = result?.shape === resultShape;\n const data = isCurrentShape ? result.data : null;\n const metadata = isCurrentShape ? result.metadata : undefined;\n\n const start = useCallback(() => {\n abortControllerRef.current?.abort();\n\n setLoading(true);\n setError(null);\n setErrorCode(null);\n setWarehouseStatus(null);\n if (requestedShapeRef.current !== resultShape) {\n requestedShapeRef.current = resultShape;\n setResult(null);\n }\n // Register an empty slot to clear stale status from the prior run.\n publishWarehouseStatus(null);\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n activeRequestKeyRef.current = requestKey;\n\n const sseContext: AnalyticsSseHandlerContext = {\n source: \"useMetricView\",\n resource: { key },\n defaultExecutionError: \"Unable to execute metric query\",\n unpublishOnMalformedMessage: true,\n signal: abortController.signal,\n abort: () => abortController.abort(),\n setLoading,\n setError,\n setErrorCode,\n onWarehouseStatus: (status) => {\n setWarehouseStatus(status);\n publishWarehouseStatus(status);\n },\n onResult: (message) => {\n setError(null);\n setErrorCode(null);\n setResult({\n shape: resultShape,\n data: message.data as Rows,\n metadata: asMetricMetadata(message.payload.metadata),\n });\n },\n unpublishWarehouseStatus,\n };\n\n connectSSE({\n url: urlSuffix,\n payload,\n signal: abortController.signal,\n onMessage: (message) =>\n handleAnalyticsSseMessage(message.data, sseContext),\n onError: (error) => handleAnalyticsSseError(error, sseContext),\n });\n }, [\n key,\n payload,\n requestKey,\n resultShape,\n urlSuffix,\n publishWarehouseStatus,\n unpublishWarehouseStatus,\n ]);\n\n useEffect(() => {\n const lease = ++effectLeaseRef.current;\n\n if (autoStart) {\n // React Strict Mode runs an effect's setup/cleanup/setup sequence on\n // mount. Keep the still-active identical stream for the second setup so\n // development sends one POST, not an immediately-aborted duplicate.\n // A real request change still starts synchronously and aborts the prior\n // controller in start().\n const activeController = abortControllerRef.current;\n if (\n activeRequestKeyRef.current !== requestKey ||\n !activeController ||\n activeController.signal.aborted\n ) {\n start();\n }\n } else {\n abortControllerRef.current?.abort();\n activeRequestKeyRef.current = null;\n unpublishWarehouseStatus();\n }\n\n return () => {\n // Defer teardown by one microtask so Strict Mode's immediate second\n // setup can claim the same request. On a genuine unmount there is no new\n // lease, so the stream is still cancelled before later async work runs.\n const controller = abortControllerRef.current;\n queueMicrotask(() => {\n if (effectLeaseRef.current !== lease) return;\n controller?.abort();\n if (abortControllerRef.current === controller) {\n activeRequestKeyRef.current = null;\n }\n unpublishWarehouseStatus();\n });\n };\n }, [start, autoStart, requestKey, unpublishWarehouseStatus]);\n\n useQueryHMR(key, start);\n\n return { data, loading, error, errorCode, metadata, warehouseStatus };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-plugin-config.d.ts","names":[],"sources":["../../../src/react/hooks/use-plugin-config.ts"],"mappings":";;AAuBA;;;;;;;;;;;;;;;;;;;iBAAgB,qBAAA,KAA0B,MAAA,kBAAA,CACxC,UAAA,WACC,CAAA"}
1
+ {"version":3,"file":"use-plugin-config.d.ts","names":[],"sources":["../../../src/react/hooks/use-plugin-config.ts"],"mappings":";;AAwBA;;;;;;;;;;;;;;;;;;;iBAAgB,qBAAA,KAA0B,MAAA,kBAAA,CACxC,UAAA,WACC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-plugin-config.js","names":[],"sources":["../../../src/react/hooks/use-plugin-config.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport { getPluginClientConfig } from \"@/js\";\n\n/**\n * Returns the client-side config exposed by a plugin's `clientConfig()` method.\n *\n * The value is read once from the boot-time `<script id=\"__appkit__\">` payload\n * and cached for the lifetime of the component. Because `clientConfig()` runs\n * at server startup, the data is static and never changes during a session.\n *\n * @param pluginName - The plugin name as registered in AppKit (e.g. `\"files\"`)\n * @returns The plugin's client config object, typed via the generic parameter\n *\n * @example\n * ```tsx\n * interface FilesConfig { volumes: string[] }\n *\n * function MyComponent() {\n * const { volumes } = usePluginClientConfig<FilesConfig>(\"files\");\n * return <ul>{volumes.map(v => <li key={v}>{v}</li>)}</ul>;\n * }\n * ```\n */\nexport function usePluginClientConfig<T = Record<string, unknown>>(\n pluginName: string,\n): T {\n return useMemo(() => getPluginClientConfig<T>(pluginName), [pluginName]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,sBACd,YACG;AACH,QAAO,cAAc,sBAAyB,WAAW,EAAE,CAAC,WAAW,CAAC"}
1
+ {"version":3,"file":"use-plugin-config.js","names":[],"sources":["../../../src/react/hooks/use-plugin-config.ts"],"sourcesContent":["import { useMemo } from \"react\";\n\nimport { getPluginClientConfig } from \"@/js\";\n\n/**\n * Returns the client-side config exposed by a plugin's `clientConfig()` method.\n *\n * The value is read once from the boot-time `<script id=\"__appkit__\">` payload\n * and cached for the lifetime of the component. Because `clientConfig()` runs\n * at server startup, the data is static and never changes during a session.\n *\n * @param pluginName - The plugin name as registered in AppKit (e.g. `\"files\"`)\n * @returns The plugin's client config object, typed via the generic parameter\n *\n * @example\n * ```tsx\n * interface FilesConfig { volumes: string[] }\n *\n * function MyComponent() {\n * const { volumes } = usePluginClientConfig<FilesConfig>(\"files\");\n * return <ul>{volumes.map(v => <li key={v}>{v}</li>)}</ul>;\n * }\n * ```\n */\nexport function usePluginClientConfig<T = Record<string, unknown>>(\n pluginName: string,\n): T {\n return useMemo(() => getPluginClientConfig<T>(pluginName), [pluginName]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,sBACd,YACG;AACH,QAAO,cAAc,sBAAyB,WAAW,EAAE,CAAC,WAAW,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-serving-invoke.d.ts","names":[],"sources":["../../../src/react/hooks/use-serving-invoke.ts"],"mappings":";;;UASiB,uBAAA,WACL,YAAA,GAAe,YAAA;;EAGzB,KAAA,GAAQ,CAAA;EAJ8B;EAMtC,SAAA;AAAA;AAAA,UAGe,sBAAA,sBAEP,MAAA;EAPA;EAUR,MAAA,GAAS,YAAA,GAAe,KAAA,KAAU,OAAA,CAAQ,CAAA;EAVjC;EAYT,IAAA,EAAM,CAAA;EAfI;EAiBV,OAAA;EAdA;EAgBA,KAAA;AAAA;;;AAXF;;;;;iBAqBgB,gBAAA,WAA2B,YAAA,GAAe,YAAA,CAAA,CACxD,IAAA,EAAM,mBAAA,CAAoB,CAAA,GAC1B,OAAA,GAAS,uBAAA,CAAwB,CAAA,IAChC,sBAAA,CAAuB,oBAAA,CAAqB,CAAA,GAAI,mBAAA,CAAoB,CAAA"}
1
+ {"version":3,"file":"use-serving-invoke.d.ts","names":[],"sources":["../../../src/react/hooks/use-serving-invoke.ts"],"mappings":";;;UAUiB,uBAAA,WACL,YAAA,GAAe,YAAA;;EAGzB,KAAA,GAAQ,CAAA;EAJ8B;EAMtC,SAAA;AAAA;AAAA,UAGe,sBAAA,sBAEP,MAAA;EAPA;EAUR,MAAA,GAAS,YAAA,GAAe,KAAA,KAAU,OAAA,CAAQ,CAAA;EAVjC;EAYT,IAAA,EAAM,CAAA;EAfI;EAiBV,OAAA;EAdA;EAgBA,KAAA;AAAA;;;AAXF;;;;;iBAqBgB,gBAAA,WAA2B,YAAA,GAAe,YAAA,CAAA,CACxD,IAAA,EAAM,mBAAA,CAAoB,CAAA,GAC1B,OAAA,GAAS,uBAAA,CAAwB,CAAA,IAChC,sBAAA,CAAuB,oBAAA,CAAqB,CAAA,GAAI,mBAAA,CAAoB,CAAA"}