@mastra/playground-ui 68.0.0-alpha.3 → 68.0.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.
- package/dist/{FilterBar-CQl04O6t.cjs → FilterBar-BSP2UTjc.cjs} +2 -2
- package/dist/{FilterBar-CQl04O6t.cjs.map → FilterBar-BSP2UTjc.cjs.map} +1 -1
- package/dist/{FilterBar-BgW2dqRQ.js → FilterBar-PSQ1OwqH.js} +2 -2
- package/dist/{FilterBar-BgW2dqRQ.js.map → FilterBar-PSQ1OwqH.js.map} +1 -1
- package/dist/{MetricsCard-CEB-f2sv.cjs → MetricsCard-BEwiS7Pe.cjs} +8 -8
- package/dist/MetricsCard-BEwiS7Pe.cjs.map +1 -0
- package/dist/{MetricsCard-M8awJ2B_.js → MetricsCard-ua6C6b8a.js} +10 -10
- package/dist/MetricsCard-ua6C6b8a.js.map +1 -0
- package/dist/{MetricsLineChart-BhHoduO0.js → MetricsLineChart-4SwxBbQx.js} +14 -3
- package/dist/MetricsLineChart-4SwxBbQx.js.map +1 -0
- package/dist/{MetricsLineChart-CjBm4IXu.cjs → MetricsLineChart-tZxsauau.cjs} +13 -2
- package/dist/MetricsLineChart-tZxsauau.cjs.map +1 -0
- package/dist/{MetricsStackedBarChart-BdMlKv6G.cjs → MetricsStackedBarChart-CDLE76IB.cjs} +2 -2
- package/dist/{MetricsStackedBarChart-BdMlKv6G.cjs.map → MetricsStackedBarChart-CDLE76IB.cjs.map} +1 -1
- package/dist/{MetricsStackedBarChart-BRuA6MTE.js → MetricsStackedBarChart-D2QyS4-z.js} +2 -2
- package/dist/{MetricsStackedBarChart-BRuA6MTE.js.map → MetricsStackedBarChart-D2QyS4-z.js.map} +1 -1
- package/dist/components/DateTimeRangePicker.cjs.js +1 -1
- package/dist/components/DateTimeRangePicker.es.js +1 -1
- package/dist/components/FilterBar.cjs.js +2 -2
- package/dist/components/FilterBar.es.js +2 -2
- package/dist/components/MetricsCard.cjs.js +1 -1
- package/dist/components/MetricsCard.es.js +1 -1
- package/dist/components/MetricsLineChart.cjs.js +1 -1
- package/dist/components/MetricsLineChart.es.js +1 -1
- package/dist/components/MetricsStackedBarChart.cjs.js +1 -1
- package/dist/components/MetricsStackedBarChart.es.js +1 -1
- package/dist/{date-time-range-picker-CTt7Od0P.cjs → date-time-range-picker-CWGjoR3j.cjs} +40 -13
- package/dist/date-time-range-picker-CWGjoR3j.cjs.map +1 -0
- package/dist/{date-time-range-picker-DKh1K9Iy.js → date-time-range-picker-CdSiXwB4.js} +40 -13
- package/dist/date-time-range-picker-CdSiXwB4.js.map +1 -0
- package/dist/domains/chat/messages/message-row.cjs.js +1 -1
- package/dist/domains/chat/messages/message-row.es.js +1 -1
- package/dist/domains/chat/tools/badges/agent-badge-wrapper.cjs.js +1 -1
- package/dist/domains/chat/tools/badges/agent-badge-wrapper.es.js +1 -1
- package/dist/domains/chat/tools/badges/agent-badge.cjs.js +1 -1
- package/dist/domains/chat/tools/badges/agent-badge.es.js +1 -1
- package/dist/domains/chat/tools/connect-prompt-tool.cjs.js +79 -4
- package/dist/domains/chat/tools/connect-prompt-tool.cjs.js.map +1 -1
- package/dist/domains/chat/tools/connect-prompt-tool.es.js +79 -4
- package/dist/domains/chat/tools/connect-prompt-tool.es.js.map +1 -1
- package/dist/domains/chat/tools/mcp-input-request-tool.cjs.js +10 -12
- package/dist/domains/chat/tools/mcp-input-request-tool.cjs.js.map +1 -1
- package/dist/domains/chat/tools/mcp-input-request-tool.es.js +10 -12
- package/dist/domains/chat/tools/mcp-input-request-tool.es.js.map +1 -1
- package/dist/domains/chat/tools/tool-card-kind.cjs.js +1 -1
- package/dist/domains/chat/tools/tool-card-kind.cjs.js.map +1 -1
- package/dist/domains/chat/tools/tool-card-kind.es.js +1 -1
- package/dist/domains/chat/tools/tool-card-kind.es.js.map +1 -1
- package/dist/domains/chat/tools/tool-card.cjs.js +1 -1
- package/dist/domains/chat/tools/tool-card.es.js +1 -1
- package/dist/domains/connect/types.cjs.js.map +1 -1
- package/dist/domains/connect/types.es.js.map +1 -1
- package/dist/domains/connect/use-user-connect.cjs.js +19 -2
- package/dist/domains/connect/use-user-connect.cjs.js.map +1 -1
- package/dist/domains/connect/use-user-connect.es.js +19 -2
- package/dist/domains/connect/use-user-connect.es.js.map +1 -1
- package/dist/domains/dashboards/components/dashboard-renderer.cjs.js +4 -3
- package/dist/domains/dashboards/components/dashboard-renderer.cjs.js.map +1 -1
- package/dist/domains/dashboards/components/dashboard-renderer.es.js +4 -3
- package/dist/domains/dashboards/components/dashboard-renderer.es.js.map +1 -1
- package/dist/domains/dashboards/components/widget-chrome.cjs.js +1 -1
- package/dist/domains/dashboards/components/widget-chrome.es.js +1 -1
- package/dist/domains/dashboards/components/widget-error.cjs.js +1 -1
- package/dist/domains/dashboards/components/widget-error.es.js +1 -1
- package/dist/domains/dashboards/hooks/use-widget-views.cjs.js +4 -3
- package/dist/domains/dashboards/hooks/use-widget-views.cjs.js.map +1 -1
- package/dist/domains/dashboards/hooks/use-widget-views.es.js +4 -3
- package/dist/domains/dashboards/hooks/use-widget-views.es.js.map +1 -1
- package/dist/domains/dashboards/kinds/scores/scores-widget.cjs.js +9 -6
- package/dist/domains/dashboards/kinds/scores/scores-widget.cjs.js.map +1 -1
- package/dist/domains/dashboards/kinds/scores/scores-widget.es.js +9 -6
- package/dist/domains/dashboards/kinds/scores/scores-widget.es.js.map +1 -1
- package/dist/domains/dashboards/kinds/table/table-tabs.cjs.js +2 -2
- package/dist/domains/dashboards/kinds/table/table-tabs.es.js +2 -2
- package/dist/domains/dashboards/kinds/table/table-widget.cjs.js +1 -1
- package/dist/domains/dashboards/kinds/table/table-widget.es.js +1 -1
- package/dist/domains/dashboards/kinds/timeseries/timeseries-widget.cjs.js +6 -5
- package/dist/domains/dashboards/kinds/timeseries/timeseries-widget.cjs.js.map +1 -1
- package/dist/domains/dashboards/kinds/timeseries/timeseries-widget.es.js +6 -5
- package/dist/domains/dashboards/kinds/timeseries/timeseries-widget.es.js.map +1 -1
- package/dist/domains/dashboards/lib/chrome.cjs.js +9 -2
- package/dist/domains/dashboards/lib/chrome.cjs.js.map +1 -1
- package/dist/domains/dashboards/lib/chrome.es.js +9 -2
- package/dist/domains/dashboards/lib/chrome.es.js.map +1 -1
- package/dist/domains/dashboards/widget-kind.cjs.js.map +1 -1
- package/dist/domains/dashboards/widget-kind.es.js.map +1 -1
- package/dist/domains/metrics/components/agent-runs-card.cjs.js +3 -3
- package/dist/domains/metrics/components/agent-runs-card.es.js +3 -3
- package/dist/domains/metrics/components/breakdown/breakdown-tab-body.cjs.js +1 -1
- package/dist/domains/metrics/components/breakdown/breakdown-tab-body.es.js +1 -1
- package/dist/domains/metrics/components/breakdown-card.cjs.js +1 -1
- package/dist/domains/metrics/components/breakdown-card.es.js +1 -1
- package/dist/domains/metrics/components/chart-area.cjs.js +1 -1
- package/dist/domains/metrics/components/chart-area.es.js +1 -1
- package/dist/domains/metrics/components/chart-card-error.cjs.js +1 -1
- package/dist/domains/metrics/components/chart-card-error.es.js +1 -1
- package/dist/domains/metrics/components/chart-card.cjs.js +1 -1
- package/dist/domains/metrics/components/chart-card.es.js +1 -1
- package/dist/domains/metrics/components/date-range-selector.cjs.js +1 -1
- package/dist/domains/metrics/components/date-range-selector.es.js +1 -1
- package/dist/domains/metrics/components/distinct-ids/distinct-ids-chart.cjs.js +3 -2
- package/dist/domains/metrics/components/distinct-ids/distinct-ids-chart.cjs.js.map +1 -1
- package/dist/domains/metrics/components/distinct-ids/distinct-ids-chart.es.js +3 -2
- package/dist/domains/metrics/components/distinct-ids/distinct-ids-chart.es.js.map +1 -1
- package/dist/domains/metrics/components/failure-rate-card.cjs.js +3 -2
- package/dist/domains/metrics/components/failure-rate-card.cjs.js.map +1 -1
- package/dist/domains/metrics/components/failure-rate-card.es.js +3 -2
- package/dist/domains/metrics/components/failure-rate-card.es.js.map +1 -1
- package/dist/domains/metrics/components/latency-card.cjs.js +3 -2
- package/dist/domains/metrics/components/latency-card.cjs.js.map +1 -1
- package/dist/domains/metrics/components/latency-card.es.js +3 -2
- package/dist/domains/metrics/components/latency-card.es.js.map +1 -1
- package/dist/domains/metrics/components/metrics-time-range-chip.cjs.js +1 -1
- package/dist/domains/metrics/components/metrics-time-range-chip.es.js +1 -1
- package/dist/domains/metrics/components/scores-card.cjs.js +2 -1
- package/dist/domains/metrics/components/scores-card.cjs.js.map +1 -1
- package/dist/domains/metrics/components/scores-card.es.js +2 -1
- package/dist/domains/metrics/components/scores-card.es.js.map +1 -1
- package/dist/domains/metrics/components/token-usage-card.cjs.js +3 -3
- package/dist/domains/metrics/components/token-usage-card.es.js +3 -3
- package/dist/domains/metrics/components/trace-aggregate-card.cjs.js +1 -1
- package/dist/domains/metrics/components/trace-aggregate-card.es.js +1 -1
- package/dist/domains/observability/components/observability-histogram.cjs.js +1 -1
- package/dist/domains/observability/components/observability-histogram.es.js +1 -1
- package/dist/domains/spans/components/spans-filter-bar.cjs.js +2 -2
- package/dist/domains/spans/components/spans-filter-bar.es.js +2 -2
- package/dist/domains/spans/hooks/use-spans-filter-bar.cjs.js +1 -1
- package/dist/domains/spans/hooks/use-spans-filter-bar.es.js +1 -1
- package/dist/domains/threads/components/threads-filter-bar.cjs.js +2 -2
- package/dist/domains/threads/components/threads-filter-bar.es.js +2 -2
- package/dist/domains/threads/hooks/use-threads-filter-bar.cjs.js +1 -1
- package/dist/domains/threads/hooks/use-threads-filter-bar.es.js +1 -1
- package/dist/domains/traces/components/traces-explorer/traces-explorer-filter-bar.cjs.js +2 -2
- package/dist/domains/traces/components/traces-explorer/traces-explorer-filter-bar.es.js +2 -2
- package/dist/domains/traces/components/traces-explorer/use-traces-filter-bar.cjs.js +1 -1
- package/dist/domains/traces/components/traces-explorer/use-traces-filter-bar.es.js +1 -1
- package/dist/{filter-bar-time-range-mg9JeYue.js → filter-bar-time-range-qlV8p7hp.js} +2 -2
- package/dist/{filter-bar-time-range-mg9JeYue.js.map → filter-bar-time-range-qlV8p7hp.js.map} +1 -1
- package/dist/{filter-bar-time-range-BA-pXMlo.cjs → filter-bar-time-range-zdM024JD.cjs} +2 -2
- package/dist/{filter-bar-time-range-BA-pXMlo.cjs.map → filter-bar-time-range-zdM024JD.cjs.map} +1 -1
- package/dist/src/domains/chat/tools/connect-prompt-tool.d.ts +10 -1
- package/dist/src/domains/connect/types.d.ts +16 -0
- package/dist/src/domains/dashboards/components/dashboard-renderer.d.ts +4 -2
- package/dist/src/domains/dashboards/hooks/use-widget-views.d.ts +4 -3
- package/dist/src/domains/dashboards/index.d.ts +1 -1
- package/dist/src/domains/dashboards/kinds/scores/scores-widget.d.ts +1 -1
- package/dist/src/domains/dashboards/lib/chrome.d.ts +6 -4
- package/dist/src/domains/dashboards/widget-kind.d.ts +12 -0
- package/dist/src/ds/components/DateTimeRangePicker/date-time-range-picker.d.ts +12 -1
- package/dist/src/ds/components/MetricsCard/metrics-card-headline.d.ts +1 -1
- package/dist/src/ds/components/MetricsLineChart/metrics-line-chart.d.ts +8 -0
- package/dist/style.css +4 -0
- package/dist/{tool-card-rxhTDpk_.cjs → tool-card-BOLXeKIp.cjs} +4 -1
- package/dist/{tool-card-rxhTDpk_.cjs.map → tool-card-BOLXeKIp.cjs.map} +1 -1
- package/dist/{tool-card-toDgoZzJ.js → tool-card-HZ5bm7ue.js} +4 -1
- package/dist/{tool-card-toDgoZzJ.js.map → tool-card-HZ5bm7ue.js.map} +1 -1
- package/package.json +9 -9
- package/dist/MetricsCard-CEB-f2sv.cjs.map +0 -1
- package/dist/MetricsCard-M8awJ2B_.js.map +0 -1
- package/dist/MetricsLineChart-BhHoduO0.js.map +0 -1
- package/dist/MetricsLineChart-CjBm4IXu.cjs.map +0 -1
- package/dist/date-time-range-picker-CTt7Od0P.cjs.map +0 -1
- package/dist/date-time-range-picker-DKh1K9Iy.js.map +0 -1
|
@@ -115,6 +115,23 @@ function useConnectSurface() {
|
|
|
115
115
|
});
|
|
116
116
|
}
|
|
117
117
|
var connectionsQueryKey = ["user-connect", "connections"];
|
|
118
|
+
/**
|
|
119
|
+
* A connection that activates or goes away changes which tools resolve for
|
|
120
|
+
* this user, so the connection list and every agent / tool listing are
|
|
121
|
+
* refetched. The server drops its cached tool snapshot when it reports the
|
|
122
|
+
* change, so the refetch already sees the new tools.
|
|
123
|
+
*/
|
|
124
|
+
function invalidateAfterConnectionChange(queryClient) {
|
|
125
|
+
return Promise.all([
|
|
126
|
+
connectionsQueryKey,
|
|
127
|
+
["agent"],
|
|
128
|
+
["agents"],
|
|
129
|
+
["tools"]
|
|
130
|
+
].map(async (queryKey) => {
|
|
131
|
+
await queryClient.cancelQueries({ queryKey });
|
|
132
|
+
await queryClient.invalidateQueries({ queryKey });
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
118
135
|
/** Lists the caller's visible connections (global + their own). */
|
|
119
136
|
function useUserConnections({ enabled }) {
|
|
120
137
|
const connectFetch = useConnectFetch();
|
|
@@ -170,7 +187,7 @@ function useConnectionStatus({ connectionId }) {
|
|
|
170
187
|
};
|
|
171
188
|
throw error;
|
|
172
189
|
}
|
|
173
|
-
if (status.status !== "pending") queryClient
|
|
190
|
+
if (status.status !== "pending") invalidateAfterConnectionChange(queryClient);
|
|
174
191
|
return status;
|
|
175
192
|
}
|
|
176
193
|
});
|
|
@@ -374,7 +391,7 @@ function useDisconnectUserConnection() {
|
|
|
374
391
|
const queryClient = useQueryClient();
|
|
375
392
|
return useMutation({
|
|
376
393
|
mutationFn: ({ connectionId }) => connectFetch(`/connections/${encodeURIComponent(connectionId)}`, { method: "DELETE" }),
|
|
377
|
-
onSuccess: () => queryClient
|
|
394
|
+
onSuccess: () => invalidateAfterConnectionChange(queryClient)
|
|
378
395
|
});
|
|
379
396
|
}
|
|
380
397
|
export { CONNECT_BASE_PATH, UserConnectRequestError, useConnectLaunchHref, useConnectSurface, useConnectionStatus, useCreateConnectSession, useDisconnectUserConnection, usePendingConnect, useUserConnections };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-user-connect.es.js","names":[],"sources":["../../../src/domains/connect/use-user-connect.ts"],"sourcesContent":["import { useMastraClient } from '@mastra/react';\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nimport {\n HeadlessAuthError,\n headlessAuthFields,\n headlessAuthRequiresForm,\n splitHeadlessAuthValues,\n startHeadlessAuth,\n supportsHeadlessAuth,\n} from './headless-connect';\nimport type { HeadlessAuthHandle } from './headless-connect';\nimport type {\n UserConnectErrorBody,\n UserConnectLaunchTokenResponse,\n UserConnectProvider,\n UserConnectProvidersResponse,\n UserConnectSessionResponse,\n UserConnectionStatusResponse,\n UserConnectionsResponse,\n} from './types';\n\n/** Default mount prefix of `userConnectRoutes()` on the dev server. */\nexport const CONNECT_BASE_PATH = '/connect';\n\nexport class UserConnectRequestError extends Error {\n status: number;\n code: string | null;\n\n constructor(status: number, code: string | null, message: string) {\n super(message);\n this.name = 'UserConnectRequestError';\n this.status = status;\n this.code = code;\n }\n}\n\ntype ConnectFetch = <T>(path: string, init?: RequestInit) => Promise<T>;\n\n/**\n * Fetches against the dev server's mounted connect routes. Custom `apiRoutes`\n * live outside the `/api` prefix, so this goes around the SDK while reusing\n * its base URL, auth headers, credentials mode, and refresh-aware fetch.\n */\nfunction useConnectFetch(): ConnectFetch {\n const client = useMastraClient();\n const { baseUrl, headers, credentials, fetch: customFetch } = client.options;\n\n return useCallback<ConnectFetch>(\n async (path, init) => {\n const fetchFn = customFetch ?? fetch;\n const response = await fetchFn(`${baseUrl}${CONNECT_BASE_PATH}${path}`, {\n ...init,\n headers: { ...headers, ...init?.headers },\n credentials,\n });\n if (!response.ok) {\n let code: string | null = null;\n let message = `Request failed with status ${response.status}`;\n try {\n const body = (await response.json()) as Partial<UserConnectErrorBody>;\n if (body?.error) {\n code = body.error.code ?? null;\n message = body.error.message ?? message;\n }\n } catch {\n // Non-JSON error body (e.g. a plain 404 for unmounted routes).\n }\n throw new UserConnectRequestError(response.status, code, message);\n }\n if (response.status === 204) return undefined as never;\n return (await response.json()) as never;\n },\n [baseUrl, headers, credentials, customFetch],\n );\n}\n\n/**\n * Resolves a server-relative launch-gate path (returned by the session route\n * as `launchUrl`) against the SDK's base URL for top-level popup navigation.\n * The launch route is authenticated by the browser's session cookie — the\n * server re-verifies that this browser owns the pending session before\n * redirecting into the vendor OAuth flow.\n */\nexport function useConnectLaunchHref(): (launchUrl: string) => string {\n const client = useMastraClient();\n const { baseUrl } = client.options;\n return useCallback((launchUrl: string) => (baseUrl ? `${baseUrl}${launchUrl}` : launchUrl), [baseUrl]);\n}\n\n/**\n * Redeems the launch gate for a headless session token (`Accept:\n * application/json`) instead of following its hosted 302. The gate runs the\n * same ownership assertion in both modes; a `null` token means the server\n * could not extract one and the caller falls back to the hosted popup.\n */\nfunction useRedeemLaunchToken(): (launchUrl: string) => Promise<UserConnectLaunchTokenResponse> {\n const client = useMastraClient();\n const { baseUrl, headers, credentials, fetch: customFetch } = client.options;\n\n return useCallback(\n async launchUrl => {\n const fetchFn = customFetch ?? fetch;\n // `launchUrl` is the full server-relative gate path (it already carries\n // the connect mount prefix), so it resolves against the bare base URL.\n const response = await fetchFn(`${baseUrl ?? ''}${launchUrl}`, {\n headers: { ...headers, accept: 'application/json' },\n credentials,\n });\n if (!response.ok) {\n throw new UserConnectRequestError(response.status, null, `Request failed with status ${response.status}`);\n }\n return (await response.json()) as UserConnectLaunchTokenResponse;\n },\n [baseUrl, headers, credentials, customFetch],\n );\n}\n\nexport type ConnectSurface =\n | { status: 'ready'; providers: UserConnectProvidersResponse['providers'] }\n | { status: 'not-mounted' }\n | { status: 'unauthenticated' };\n\n/**\n * Capability detection for the end-user connect surface: probes\n * `GET /connect/providers` and distinguishes a working surface from the two\n * setup gaps (routes not mounted → 404, no end-user identity → 401).\n */\nexport function useConnectSurface() {\n const connectFetch = useConnectFetch();\n\n return useQuery<ConnectSurface, UserConnectRequestError>({\n queryKey: ['user-connect', 'surface'],\n retry: false,\n queryFn: async () => {\n try {\n const body = await connectFetch<UserConnectProvidersResponse>('/providers');\n return { status: 'ready', providers: body.providers };\n } catch (error) {\n if (error instanceof UserConnectRequestError) {\n if (error.status === 404) return { status: 'not-mounted' };\n if (error.status === 401) return { status: 'unauthenticated' };\n }\n throw error;\n }\n },\n });\n}\n\nconst connectionsQueryKey = ['user-connect', 'connections'] as const;\n\n/** Lists the caller's visible connections (global + their own). */\nexport function useUserConnections({ enabled }: { enabled: boolean }) {\n const connectFetch = useConnectFetch();\n\n return useQuery<UserConnectionsResponse['connections'], UserConnectRequestError>({\n queryKey: connectionsQueryKey,\n enabled,\n retry: false,\n queryFn: async () => {\n const body = await connectFetch<UserConnectionsResponse>('/connections');\n return body.connections;\n },\n });\n}\n\n/** Creates a connect session for a provider; the caller drives the popup. */\nexport function useCreateConnectSession() {\n const connectFetch = useConnectFetch();\n\n return useMutation<UserConnectSessionResponse, UserConnectRequestError, { provider: string }>({\n mutationFn: ({ provider }) =>\n connectFetch<UserConnectSessionResponse>(`/${encodeURIComponent(provider)}/sessions`, { method: 'POST' }),\n });\n}\n\n/** 2s interval with a 5 minute cap, mirroring the hosted connect page. */\nconst STATUS_POLL_INTERVAL_MS = 2_000;\nconst STATUS_MAX_POLLS = 150;\n\n/**\n * Polls a pending connection until it leaves `pending`. A 404 counts as still\n * pending — the platform row may not be visible until the provider handshake\n * lands. Polling stops once the status settles (or after the 5 minute cap);\n * the connections list is refreshed when it does.\n */\nexport function useConnectionStatus({ connectionId }: { connectionId: string | null }) {\n const connectFetch = useConnectFetch();\n const queryClient = useQueryClient();\n\n return useQuery<UserConnectionStatusResponse, UserConnectRequestError>({\n queryKey: ['user-connect', 'connection-status', connectionId],\n enabled: connectionId !== null,\n retry: false,\n refetchInterval: query => {\n const status = query.state.data?.status;\n if (status !== undefined && status !== 'pending') return false;\n if (query.state.dataUpdateCount >= STATUS_MAX_POLLS) return false;\n return STATUS_POLL_INTERVAL_MS;\n },\n queryFn: async () => {\n let status: UserConnectionStatusResponse;\n try {\n status = await connectFetch<UserConnectionStatusResponse>(\n `/connections/${encodeURIComponent(connectionId ?? '')}/status`,\n );\n } catch (error) {\n if (error instanceof UserConnectRequestError && error.status === 404) {\n return { id: connectionId ?? '', status: 'pending', scope: 'user' };\n }\n throw error;\n }\n if (status.status !== 'pending') {\n void queryClient.invalidateQueries({ queryKey: connectionsQueryKey });\n }\n return status;\n },\n });\n}\n\nexport interface PendingConnect {\n provider: string;\n connectionId: string;\n /**\n * `headless` — the Nango SDK drives the provider's own consent popup (or no\n * popup at all for collected credentials). `hosted` — a popup navigated\n * through the launch gate's 302 into the hosted connect page.\n */\n mode: 'headless' | 'hosted';\n /** Shown when the popup was blocked so the user can finish the flow manually. */\n fallbackUrl: string | null;\n}\n\n/**\n * What a caller passes to `connect()`: the provider's id plus the auth shape\n * when known. Callers that only know the id (older payloads) always get the\n * hosted flow.\n */\nexport type ConnectTarget = Pick<UserConnectProvider, 'id'> &\n Partial<Pick<UserConnectProvider, 'authType' | 'authFields'>>;\n\n/**\n * Grace period after the vendor popup closes before an unsettled flow is\n * abandoned — the activation webhook often lands a few seconds after the\n * vendor window closes itself.\n */\nconst POPUP_CLOSE_GRACE_MS = 15_000;\n/** Wall-clock cap matching the status poll's 5 minute budget. */\nconst PENDING_TIMEOUT_MS = STATUS_POLL_INTERVAL_MS * STATUS_MAX_POLLS;\n\nexport interface UsePendingConnectOptions {\n /** Overrides for the abandonment timings (primarily for tests). */\n popupCloseGraceMs?: number;\n pendingTimeoutMs?: number;\n}\n\n/**\n * Outcome of the most recently settled flow: `active` when the connection\n * activated, `failed` for everything else (non-active settle, poll failure,\n * abandoned popup, poll budget). Cleared when a new flow starts.\n */\nexport interface SettledConnect {\n provider: string;\n status: 'active' | 'failed';\n /** Human-readable failure detail (blocked popup, closed window, rejected credentials). */\n message?: string;\n}\n\n/** Window features for the hosted connect popup. */\nconst POPUP_FEATURES = 'popup,width=600,height=720';\n\n/**\n * Drives one add-connection flow end to end. Providers whose auth the browser\n * can complete headlessly go through the launch gate's JSON redemption: the\n * released session token keys the Nango SDK, which opens the provider's own\n * consent popup (or submits collected credentials with no popup). Everything\n * else gets the hosted flow — a popup opened synchronously in the click\n * gesture (so popup blockers allow it) and navigated through the launch\n * gate's 302. Either way the connection status is polled until it settles.\n * The pending state always resolves — on settle, on a poll or auth failure,\n * when the popup closes without settling (after a grace period), or at the\n * poll budget — so the Connect action is never stuck disabled.\n */\nexport function usePendingConnect(options: UsePendingConnectOptions = {}) {\n const { popupCloseGraceMs = POPUP_CLOSE_GRACE_MS, pendingTimeoutMs = PENDING_TIMEOUT_MS } = options;\n const session = useCreateConnectSession();\n const resolveLaunchHref = useConnectLaunchHref();\n const redeemLaunchToken = useRedeemLaunchToken();\n const [pending, setPending] = useState<PendingConnect | null>(null);\n const [settled, setSettled] = useState<SettledConnect | null>(null);\n const [connectedProviders, setConnectedProviders] = useState<ReadonlySet<string>>(new Set());\n const popupRef = useRef<Pick<Window, 'closed'> | null>(null);\n const headlessRef = useRef<HeadlessAuthHandle | null>(null);\n // Mirrors `pending` for async guards (the SDK promise settles outside the\n // render cycle); written directly at every transition so a cancel is never\n // raced by a trailing rejection.\n const pendingRef = useRef<PendingConnect | null>(null);\n const status = useConnectionStatus({ connectionId: pending?.connectionId ?? null });\n\n useEffect(() => {\n pendingRef.current = pending;\n }, [pending]);\n\n // Close the provider popup if the surface unmounts mid-flow.\n useEffect(\n () => () => {\n headlessRef.current?.close();\n headlessRef.current = null;\n },\n [],\n );\n\n // Settle only on a status response for the connection currently pending —\n // a stale result from a previous flow must not clear or mark a newer one.\n const statusData = status.data;\n useEffect(() => {\n if (!pending || statusData === undefined) return;\n if (statusData.id !== pending.connectionId || statusData.status === 'pending') return;\n const { provider } = pending;\n if (statusData.status === 'active') {\n setConnectedProviders(previous => (previous.has(provider) ? previous : new Set([...previous, provider])));\n }\n // The provider window closes itself on active settles; drop the handle so\n // a trailing SDK rejection cannot re-settle this flow.\n headlessRef.current = null;\n pendingRef.current = null;\n setSettled({ provider, status: statusData.status === 'active' ? 'active' : 'failed' });\n setPending(null);\n }, [pending, statusData]);\n\n // A failed poll hands the Connect action back instead of leaving it stuck.\n useEffect(() => {\n if (!status.isError || !pending) return;\n // The ref is cleared before close() so the rejection it triggers no-ops.\n pendingRef.current = null;\n headlessRef.current?.close();\n headlessRef.current = null;\n setSettled({ provider: pending.provider, status: 'failed' });\n setPending(null);\n }, [status.isError, pending]);\n\n // Abandonment: the popup closed without the flow settling (grace period for\n // the trailing webhook) or the poll budget ran out.\n useEffect(() => {\n if (!pending) return;\n const startedAt = Date.now();\n let closedAt: number | null = null;\n const tickMs = Math.min(1_000, popupCloseGraceMs);\n const timer = window.setInterval(() => {\n if (popupRef.current?.closed) closedAt ??= Date.now();\n const abandoned = closedAt !== null && Date.now() - closedAt >= popupCloseGraceMs;\n const timedOut = Date.now() - startedAt >= pendingTimeoutMs;\n if (abandoned || timedOut) {\n // The ref is cleared before close() so the rejection it triggers no-ops.\n pendingRef.current = null;\n headlessRef.current?.close();\n headlessRef.current = null;\n setSettled({ provider: pending.provider, status: 'failed' });\n setPending(null);\n }\n }, tickMs);\n return () => window.clearInterval(timer);\n }, [pending, popupCloseGraceMs, pendingTimeoutMs]);\n\n const beginHeadless = useCallback(\n async (provider: ConnectTarget, created: UserConnectSessionResponse, values?: Record<string, string>) => {\n let sessionToken: string | null = null;\n try {\n sessionToken = (await redeemLaunchToken(created.launchUrl)).sessionToken;\n } catch {\n // Redemption failure (older platform, transient error) is not fatal —\n // the hosted flow below still works through the gate's 302.\n }\n if (!sessionToken) {\n // Opened after an await, so a blocker may stop it; the fallback link\n // in the pending state covers that.\n const launchHref = resolveLaunchHref(created.launchUrl);\n const popup = window.open(launchHref, 'mastra-connect', POPUP_FEATURES);\n popupRef.current = popup;\n const next: PendingConnect = {\n provider: provider.id,\n connectionId: created.connectionId,\n mode: 'hosted',\n fallbackUrl: popup ? null : launchHref,\n };\n pendingRef.current = next;\n setPending(next);\n return;\n }\n\n const split = values ? splitHeadlessAuthValues(headlessAuthFields(provider), values) : {};\n const handle = startHeadlessAuth({\n sessionToken,\n integrationId: created.integrationId,\n authType: provider.authType,\n credentials: split.credentials,\n params: split.params,\n });\n headlessRef.current = handle;\n const next: PendingConnect = {\n provider: provider.id,\n connectionId: created.connectionId,\n mode: 'headless',\n fallbackUrl: null,\n };\n pendingRef.current = next;\n setPending(next);\n // Success needs no action here — the activation webhook lands on the\n // platform and the status poll settles the flow. Failures settle it now.\n handle.result.catch((error: unknown) => {\n if (pendingRef.current?.connectionId !== created.connectionId) return;\n headlessRef.current = null;\n pendingRef.current = null;\n const message = error instanceof HeadlessAuthError ? error.message : 'Could not authorize this connection.';\n setSettled({ provider: provider.id, status: 'failed', message });\n setPending(null);\n });\n },\n [redeemLaunchToken, resolveLaunchHref],\n );\n\n const connect = useCallback(\n (provider: ConnectTarget, values?: Record<string, string>) => {\n setSettled(null);\n headlessRef.current?.close();\n headlessRef.current = null;\n\n // Headless needs both the capability and the inputs: a caller without a\n // form (no `values`) can only run flows whose fields are all optional —\n // anything else falls back to the hosted page, which renders the form.\n const headless =\n supportsHeadlessAuth(provider.authType, provider.authFields) &&\n (values !== undefined || !headlessAuthRequiresForm(provider));\n // Hosted flows open the popup synchronously in the click gesture so\n // popup blockers allow it; headless flows never navigate to a hosted\n // page, so no window opens here.\n const popup = headless ? null : window.open('', 'mastra-connect', POPUP_FEATURES);\n popupRef.current = popup;\n session.mutate(\n { provider: provider.id },\n {\n onSuccess: created => {\n if (headless) {\n void beginHeadless(provider, created, values);\n return;\n }\n // The popup enters OAuth through the server's launch gate, which\n // verifies this browser owns the pending session before\n // redirecting — the raw vendor URL never reaches the page.\n const launchHref = resolveLaunchHref(created.launchUrl);\n if (popup) {\n popup.location.href = launchHref;\n }\n const next: PendingConnect = {\n provider: provider.id,\n connectionId: created.connectionId,\n mode: 'hosted',\n fallbackUrl: popup ? null : launchHref,\n };\n pendingRef.current = next;\n setPending(next);\n },\n onError: () => {\n popup?.close();\n popupRef.current = null;\n },\n },\n );\n },\n [session, resolveLaunchHref, beginHeadless],\n );\n\n /** Abandons the in-flight flow: closes the provider window, hands the Connect action back. */\n const cancel = useCallback(() => {\n headlessRef.current?.close();\n headlessRef.current = null;\n popupRef.current = null;\n pendingRef.current = null;\n setPending(null);\n setSettled(null);\n }, []);\n\n return {\n pending,\n connect,\n cancel,\n /** Outcome of the most recently settled flow; null while none settled or a flow is in progress. */\n settled,\n /** Providers whose connection settled `active` during this hook's lifetime. */\n connectedProviders,\n isCreating: session.isPending,\n createError: session.isError ? session.error : null,\n };\n}\n\n/** Disconnects one of the caller's own connections. */\nexport function useDisconnectUserConnection() {\n const connectFetch = useConnectFetch();\n const queryClient = useQueryClient();\n\n return useMutation<void, UserConnectRequestError, { connectionId: string }>({\n mutationFn: ({ connectionId }) =>\n connectFetch<void>(`/connections/${encodeURIComponent(connectionId)}`, { method: 'DELETE' }),\n onSuccess: () => queryClient.invalidateQueries({ queryKey: connectionsQueryKey }),\n });\n}\n"],"mappings":";;;;;;AAwBA,IAAa,oBAAoB;AAEjC,IAAa,0BAAb,cAA6C,MAAM;CACjD;CACA;CAEA,YAAY,QAAgB,MAAqB,SAAiB;EAChE,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,KAAK,OAAO;CACd;AACF;;;;;;AASA,SAAS,kBAAgC;CAEvC,MAAM,EAAE,SAAS,SAAS,aAAa,OAAO,gBAD/B,gBAC+C,CAAA,CAAO;CAErE,OAAO,YACL,OAAO,MAAM,SAAS;EAEpB,MAAM,WAAW,OADD,eAAe,MAAA,CACA,GAAG,UAAU,oBAAoB,QAAQ;GACtE,GAAG;GACH,SAAS;IAAE,GAAG;IAAS,GAAG,MAAM;GAAQ;GACxC;EACF,CAAC;EACD,IAAI,CAAC,SAAS,IAAI;GAChB,IAAI,OAAsB;GAC1B,IAAI,UAAU,8BAA8B,SAAS;GACrD,IAAI;IACF,MAAM,OAAQ,MAAM,SAAS,KAAK;IAClC,IAAI,MAAM,OAAO;KACf,OAAO,KAAK,MAAM,QAAQ;KAC1B,UAAU,KAAK,MAAM,WAAW;IAClC;GACF,QAAQ,CAER;GACA,MAAM,IAAI,wBAAwB,SAAS,QAAQ,MAAM,OAAO;EAClE;EACA,IAAI,SAAS,WAAW,KAAK,OAAO,KAAA;EACpC,OAAQ,MAAM,SAAS,KAAK;CAC9B,GACA;EAAC;EAAS;EAAS;EAAa;CAAW,CAC7C;AACF;;;;;;;;AASA,SAAgB,uBAAsD;CAEpE,MAAM,EAAE,YADO,gBACK,CAAA,CAAO;CAC3B,OAAO,aAAa,cAAuB,UAAU,GAAG,UAAU,cAAc,WAAY,CAAC,OAAO,CAAC;AACvG;;;;;;;AAQA,SAAS,uBAAuF;CAE9F,MAAM,EAAE,SAAS,SAAS,aAAa,OAAO,gBAD/B,gBAC+C,CAAA,CAAO;CAErE,OAAO,YACL,OAAM,cAAa;EAIjB,MAAM,WAAW,OAHD,eAAe,MAAA,CAGA,GAAG,WAAW,KAAK,aAAa;GAC7D,SAAS;IAAE,GAAG;IAAS,QAAQ;GAAmB;GAClD;EACF,CAAC;EACD,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,wBAAwB,SAAS,QAAQ,MAAM,8BAA8B,SAAS,QAAQ;EAE1G,OAAQ,MAAM,SAAS,KAAK;CAC9B,GACA;EAAC;EAAS;EAAS;EAAa;CAAW,CAC7C;AACF;;;;;;AAYA,SAAgB,oBAAoB;CAClC,MAAM,eAAe,gBAAgB;CAErC,OAAO,SAAkD;EACvD,UAAU,CAAC,gBAAgB,SAAS;EACpC,OAAO;EACP,SAAS,YAAY;GACnB,IAAI;IAEF,OAAO;KAAE,QAAQ;KAAS,YAAW,MADlB,aAA2C,YAAY,EAAA,CAChC;IAAU;GACtD,SAAS,OAAO;IACd,IAAI,iBAAiB,yBAAyB;KAC5C,IAAI,MAAM,WAAW,KAAK,OAAO,EAAE,QAAQ,cAAc;KACzD,IAAI,MAAM,WAAW,KAAK,OAAO,EAAE,QAAQ,kBAAkB;IAC/D;IACA,MAAM;GACR;EACF;CACF,CAAC;AACH;AAEA,IAAM,sBAAsB,CAAC,gBAAgB,aAAa;;AAG1D,SAAgB,mBAAmB,EAAE,WAAiC;CACpE,MAAM,eAAe,gBAAgB;CAErC,OAAO,SAA0E;EAC/E,UAAU;EACV;EACA,OAAO;EACP,SAAS,YAAY;GAEnB,QAAO,MADY,aAAsC,cAAc,EAAA,CAC3D;EACd;CACF,CAAC;AACH;;AAGA,SAAgB,0BAA0B;CACxC,MAAM,eAAe,gBAAgB;CAErC,OAAO,YAAuF,EAC5F,aAAa,EAAE,eACb,aAAyC,IAAI,mBAAmB,QAAQ,EAAE,YAAY,EAAE,QAAQ,OAAO,CAAC,EAC5G,CAAC;AACH;;AAGA,IAAM,0BAA0B;AAChC,IAAM,mBAAmB;;;;;;;AAQzB,SAAgB,oBAAoB,EAAE,gBAAiD;CACrF,MAAM,eAAe,gBAAgB;CACrC,MAAM,cAAc,eAAe;CAEnC,OAAO,SAAgE;EACrE,UAAU;GAAC;GAAgB;GAAqB;EAAY;EAC5D,SAAS,iBAAiB;EAC1B,OAAO;EACP,kBAAiB,UAAS;GACxB,MAAM,SAAS,MAAM,MAAM,MAAM;GACjC,IAAI,WAAW,KAAA,KAAa,WAAW,WAAW,OAAO;GACzD,IAAI,MAAM,MAAM,mBAAmB,kBAAkB,OAAO;GAC5D,OAAO;EACT;EACA,SAAS,YAAY;GACnB,IAAI;GACJ,IAAI;IACF,SAAS,MAAM,aACb,gBAAgB,mBAAmB,gBAAgB,EAAE,EAAE,QACzD;GACF,SAAS,OAAO;IACd,IAAI,iBAAiB,2BAA2B,MAAM,WAAW,KAC/D,OAAO;KAAE,IAAI,gBAAgB;KAAI,QAAQ;KAAW,OAAO;IAAO;IAEpE,MAAM;GACR;GACA,IAAI,OAAO,WAAW,WACpB,YAAiB,kBAAkB,EAAE,UAAU,oBAAoB,CAAC;GAEtE,OAAO;EACT;CACF,CAAC;AACH;;;;;;AA4BA,IAAM,uBAAuB;;AAE7B,IAAM,qBAAqB,0BAA0B;;AAqBrD,IAAM,iBAAiB;;;;;;;;;;;;;AAcvB,SAAgB,kBAAkB,UAAoC,CAAC,GAAG;CACxE,MAAM,EAAE,oBAAoB,sBAAsB,mBAAmB,uBAAuB;CAC5F,MAAM,UAAU,wBAAwB;CACxC,MAAM,oBAAoB,qBAAqB;CAC/C,MAAM,oBAAoB,qBAAqB;CAC/C,MAAM,CAAC,SAAS,cAAc,SAAgC,IAAI;CAClE,MAAM,CAAC,SAAS,cAAc,SAAgC,IAAI;CAClE,MAAM,CAAC,oBAAoB,yBAAyB,yBAA8B,IAAI,IAAI,CAAC;CAC3F,MAAM,WAAW,OAAsC,IAAI;CAC3D,MAAM,cAAc,OAAkC,IAAI;CAI1D,MAAM,aAAa,OAA8B,IAAI;CACrD,MAAM,SAAS,oBAAoB,EAAE,cAAc,SAAS,gBAAgB,KAAK,CAAC;CAElF,gBAAgB;EACd,WAAW,UAAU;CACvB,GAAG,CAAC,OAAO,CAAC;CAGZ,sBACc;EACV,YAAY,SAAS,MAAM;EAC3B,YAAY,UAAU;CACxB,GACA,CAAC,CACH;CAIA,MAAM,aAAa,OAAO;CAC1B,gBAAgB;EACd,IAAI,CAAC,WAAW,eAAe,KAAA,GAAW;EAC1C,IAAI,WAAW,OAAO,QAAQ,gBAAgB,WAAW,WAAW,WAAW;EAC/E,MAAM,EAAE,aAAa;EACrB,IAAI,WAAW,WAAW,UACxB,uBAAsB,aAAa,SAAS,IAAI,QAAQ,IAAI,2BAAW,IAAI,IAAI,CAAC,GAAG,UAAU,QAAQ,CAAC,CAAE;EAI1G,YAAY,UAAU;EACtB,WAAW,UAAU;EACrB,WAAW;GAAE;GAAU,QAAQ,WAAW,WAAW,WAAW,WAAW;EAAS,CAAC;EACrF,WAAW,IAAI;CACjB,GAAG,CAAC,SAAS,UAAU,CAAC;CAGxB,gBAAgB;EACd,IAAI,CAAC,OAAO,WAAW,CAAC,SAAS;EAEjC,WAAW,UAAU;EACrB,YAAY,SAAS,MAAM;EAC3B,YAAY,UAAU;EACtB,WAAW;GAAE,UAAU,QAAQ;GAAU,QAAQ;EAAS,CAAC;EAC3D,WAAW,IAAI;CACjB,GAAG,CAAC,OAAO,SAAS,OAAO,CAAC;CAI5B,gBAAgB;EACd,IAAI,CAAC,SAAS;EACd,MAAM,YAAY,KAAK,IAAI;EAC3B,IAAI,WAA0B;EAC9B,MAAM,SAAS,KAAK,IAAI,KAAO,iBAAiB;EAChD,MAAM,QAAQ,OAAO,kBAAkB;GACrC,IAAI,SAAS,SAAS,QAAQ,aAAa,KAAK,IAAI;GACpD,MAAM,YAAY,aAAa,QAAQ,KAAK,IAAI,IAAI,YAAY;GAChE,MAAM,WAAW,KAAK,IAAI,IAAI,aAAa;GAC3C,IAAI,aAAa,UAAU;IAEzB,WAAW,UAAU;IACrB,YAAY,SAAS,MAAM;IAC3B,YAAY,UAAU;IACtB,WAAW;KAAE,UAAU,QAAQ;KAAU,QAAQ;IAAS,CAAC;IAC3D,WAAW,IAAI;GACjB;EACF,GAAG,MAAM;EACT,aAAa,OAAO,cAAc,KAAK;CACzC,GAAG;EAAC;EAAS;EAAmB;CAAgB,CAAC;CAEjD,MAAM,gBAAgB,YACpB,OAAO,UAAyB,SAAqC,WAAoC;EACvG,IAAI,eAA8B;EAClC,IAAI;GACF,gBAAgB,MAAM,kBAAkB,QAAQ,SAAS,EAAA,CAAG;EAC9D,QAAQ,CAGR;EACA,IAAI,CAAC,cAAc;GAGjB,MAAM,aAAa,kBAAkB,QAAQ,SAAS;GACtD,MAAM,QAAQ,OAAO,KAAK,YAAY,kBAAkB,cAAc;GACtE,SAAS,UAAU;GACnB,MAAM,OAAuB;IAC3B,UAAU,SAAS;IACnB,cAAc,QAAQ;IACtB,MAAM;IACN,aAAa,QAAQ,OAAO;GAC9B;GACA,WAAW,UAAU;GACrB,WAAW,IAAI;GACf;EACF;EAEA,MAAM,QAAQ,SAAS,wBAAwB,mBAAmB,QAAQ,GAAG,MAAM,IAAI,CAAC;EACxF,MAAM,SAAS,kBAAkB;GAC/B;GACA,eAAe,QAAQ;GACvB,UAAU,SAAS;GACnB,aAAa,MAAM;GACnB,QAAQ,MAAM;EAChB,CAAC;EACD,YAAY,UAAU;EACtB,MAAM,OAAuB;GAC3B,UAAU,SAAS;GACnB,cAAc,QAAQ;GACtB,MAAM;GACN,aAAa;EACf;EACA,WAAW,UAAU;EACrB,WAAW,IAAI;EAGf,OAAO,OAAO,OAAO,UAAmB;GACtC,IAAI,WAAW,SAAS,iBAAiB,QAAQ,cAAc;GAC/D,YAAY,UAAU;GACtB,WAAW,UAAU;GACrB,MAAM,UAAU,iBAAiB,oBAAoB,MAAM,UAAU;GACrE,WAAW;IAAE,UAAU,SAAS;IAAI,QAAQ;IAAU;GAAQ,CAAC;GAC/D,WAAW,IAAI;EACjB,CAAC;CACH,GACA,CAAC,mBAAmB,iBAAiB,CACvC;CA+DA,OAAO;EACL;EACA,SA/Dc,aACb,UAAyB,WAAoC;GAC5D,WAAW,IAAI;GACf,YAAY,SAAS,MAAM;GAC3B,YAAY,UAAU;GAKtB,MAAM,WACJ,qBAAqB,SAAS,UAAU,SAAS,UAAU,MAC1D,WAAW,KAAA,KAAa,CAAC,yBAAyB,QAAQ;GAI7D,MAAM,QAAQ,WAAW,OAAO,OAAO,KAAK,IAAI,kBAAkB,cAAc;GAChF,SAAS,UAAU;GACnB,QAAQ,OACN,EAAE,UAAU,SAAS,GAAG,GACxB;IACE,YAAW,YAAW;KACpB,IAAI,UAAU;MACZ,cAAmB,UAAU,SAAS,MAAM;MAC5C;KACF;KAIA,MAAM,aAAa,kBAAkB,QAAQ,SAAS;KACtD,IAAI,OACF,MAAM,SAAS,OAAO;KAExB,MAAM,OAAuB;MAC3B,UAAU,SAAS;MACnB,cAAc,QAAQ;MACtB,MAAM;MACN,aAAa,QAAQ,OAAO;KAC9B;KACA,WAAW,UAAU;KACrB,WAAW,IAAI;IACjB;IACA,eAAe;KACb,OAAO,MAAM;KACb,SAAS,UAAU;IACrB;GACF,CACF;EACF,GACA;GAAC;GAAS;GAAmB;EAAa,CAe1C;EACA,QAZa,kBAAkB;GAC/B,YAAY,SAAS,MAAM;GAC3B,YAAY,UAAU;GACtB,SAAS,UAAU;GACnB,WAAW,UAAU;GACrB,WAAW,IAAI;GACf,WAAW,IAAI;EACjB,GAAG,CAAC,CAKF;;EAEA;;EAEA;EACA,YAAY,QAAQ;EACpB,aAAa,QAAQ,UAAU,QAAQ,QAAQ;CACjD;AACF;;AAGA,SAAgB,8BAA8B;CAC5C,MAAM,eAAe,gBAAgB;CACrC,MAAM,cAAc,eAAe;CAEnC,OAAO,YAAqE;EAC1E,aAAa,EAAE,mBACb,aAAmB,gBAAgB,mBAAmB,YAAY,KAAK,EAAE,QAAQ,SAAS,CAAC;EAC7F,iBAAiB,YAAY,kBAAkB,EAAE,UAAU,oBAAoB,CAAC;CAClF,CAAC;AACH"}
|
|
1
|
+
{"version":3,"file":"use-user-connect.es.js","names":[],"sources":["../../../src/domains/connect/use-user-connect.ts"],"sourcesContent":["import { useMastraClient } from '@mastra/react';\nimport type { QueryClient } from '@tanstack/react-query';\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nimport {\n HeadlessAuthError,\n headlessAuthFields,\n headlessAuthRequiresForm,\n splitHeadlessAuthValues,\n startHeadlessAuth,\n supportsHeadlessAuth,\n} from './headless-connect';\nimport type { HeadlessAuthHandle } from './headless-connect';\nimport type {\n UserConnectErrorBody,\n UserConnectLaunchTokenResponse,\n UserConnectProvider,\n UserConnectProvidersResponse,\n UserConnectSessionResponse,\n UserConnectionStatusResponse,\n UserConnectionsResponse,\n} from './types';\n\n/** Default mount prefix of `userConnectRoutes()` on the dev server. */\nexport const CONNECT_BASE_PATH = '/connect';\n\nexport class UserConnectRequestError extends Error {\n status: number;\n code: string | null;\n\n constructor(status: number, code: string | null, message: string) {\n super(message);\n this.name = 'UserConnectRequestError';\n this.status = status;\n this.code = code;\n }\n}\n\ntype ConnectFetch = <T>(path: string, init?: RequestInit) => Promise<T>;\n\n/**\n * Fetches against the dev server's mounted connect routes. Custom `apiRoutes`\n * live outside the `/api` prefix, so this goes around the SDK while reusing\n * its base URL, auth headers, credentials mode, and refresh-aware fetch.\n */\nfunction useConnectFetch(): ConnectFetch {\n const client = useMastraClient();\n const { baseUrl, headers, credentials, fetch: customFetch } = client.options;\n\n return useCallback<ConnectFetch>(\n async (path, init) => {\n const fetchFn = customFetch ?? fetch;\n const response = await fetchFn(`${baseUrl}${CONNECT_BASE_PATH}${path}`, {\n ...init,\n headers: { ...headers, ...init?.headers },\n credentials,\n });\n if (!response.ok) {\n let code: string | null = null;\n let message = `Request failed with status ${response.status}`;\n try {\n const body = (await response.json()) as Partial<UserConnectErrorBody>;\n if (body?.error) {\n code = body.error.code ?? null;\n message = body.error.message ?? message;\n }\n } catch {\n // Non-JSON error body (e.g. a plain 404 for unmounted routes).\n }\n throw new UserConnectRequestError(response.status, code, message);\n }\n if (response.status === 204) return undefined as never;\n return (await response.json()) as never;\n },\n [baseUrl, headers, credentials, customFetch],\n );\n}\n\n/**\n * Resolves a server-relative launch-gate path (returned by the session route\n * as `launchUrl`) against the SDK's base URL for top-level popup navigation.\n * The launch route is authenticated by the browser's session cookie — the\n * server re-verifies that this browser owns the pending session before\n * redirecting into the vendor OAuth flow.\n */\nexport function useConnectLaunchHref(): (launchUrl: string) => string {\n const client = useMastraClient();\n const { baseUrl } = client.options;\n return useCallback((launchUrl: string) => (baseUrl ? `${baseUrl}${launchUrl}` : launchUrl), [baseUrl]);\n}\n\n/**\n * Redeems the launch gate for a headless session token (`Accept:\n * application/json`) instead of following its hosted 302. The gate runs the\n * same ownership assertion in both modes; a `null` token means the server\n * could not extract one and the caller falls back to the hosted popup.\n */\nfunction useRedeemLaunchToken(): (launchUrl: string) => Promise<UserConnectLaunchTokenResponse> {\n const client = useMastraClient();\n const { baseUrl, headers, credentials, fetch: customFetch } = client.options;\n\n return useCallback(\n async launchUrl => {\n const fetchFn = customFetch ?? fetch;\n // `launchUrl` is the full server-relative gate path (it already carries\n // the connect mount prefix), so it resolves against the bare base URL.\n const response = await fetchFn(`${baseUrl ?? ''}${launchUrl}`, {\n headers: { ...headers, accept: 'application/json' },\n credentials,\n });\n if (!response.ok) {\n throw new UserConnectRequestError(response.status, null, `Request failed with status ${response.status}`);\n }\n return (await response.json()) as UserConnectLaunchTokenResponse;\n },\n [baseUrl, headers, credentials, customFetch],\n );\n}\n\nexport type ConnectSurface =\n | { status: 'ready'; providers: UserConnectProvidersResponse['providers'] }\n | { status: 'not-mounted' }\n | { status: 'unauthenticated' };\n\n/**\n * Capability detection for the end-user connect surface: probes\n * `GET /connect/providers` and distinguishes a working surface from the two\n * setup gaps (routes not mounted → 404, no end-user identity → 401).\n */\nexport function useConnectSurface() {\n const connectFetch = useConnectFetch();\n\n return useQuery<ConnectSurface, UserConnectRequestError>({\n queryKey: ['user-connect', 'surface'],\n retry: false,\n queryFn: async () => {\n try {\n const body = await connectFetch<UserConnectProvidersResponse>('/providers');\n return { status: 'ready', providers: body.providers };\n } catch (error) {\n if (error instanceof UserConnectRequestError) {\n if (error.status === 404) return { status: 'not-mounted' };\n if (error.status === 401) return { status: 'unauthenticated' };\n }\n throw error;\n }\n },\n });\n}\n\nconst connectionsQueryKey = ['user-connect', 'connections'] as const;\n\n/**\n * A connection that activates or goes away changes which tools resolve for\n * this user, so the connection list and every agent / tool listing are\n * refetched. The server drops its cached tool snapshot when it reports the\n * change, so the refetch already sees the new tools.\n */\nfunction invalidateAfterConnectionChange(queryClient: QueryClient) {\n return Promise.all(\n [connectionsQueryKey, ['agent'], ['agents'], ['tools']].map(async queryKey => {\n // A first fetch still in flight would otherwise be reused by the\n // invalidation, and its pre-change answer would land as fresh data.\n await queryClient.cancelQueries({ queryKey });\n await queryClient.invalidateQueries({ queryKey });\n }),\n );\n}\n\n/** Lists the caller's visible connections (global + their own). */\nexport function useUserConnections({ enabled }: { enabled: boolean }) {\n const connectFetch = useConnectFetch();\n\n return useQuery<UserConnectionsResponse['connections'], UserConnectRequestError>({\n queryKey: connectionsQueryKey,\n enabled,\n retry: false,\n queryFn: async () => {\n const body = await connectFetch<UserConnectionsResponse>('/connections');\n return body.connections;\n },\n });\n}\n\n/** Creates a connect session for a provider; the caller drives the popup. */\nexport function useCreateConnectSession() {\n const connectFetch = useConnectFetch();\n\n return useMutation<UserConnectSessionResponse, UserConnectRequestError, { provider: string }>({\n mutationFn: ({ provider }) =>\n connectFetch<UserConnectSessionResponse>(`/${encodeURIComponent(provider)}/sessions`, { method: 'POST' }),\n });\n}\n\n/** 2s interval with a 5 minute cap, mirroring the hosted connect page. */\nconst STATUS_POLL_INTERVAL_MS = 2_000;\nconst STATUS_MAX_POLLS = 150;\n\n/**\n * Polls a pending connection until it leaves `pending`. A 404 counts as still\n * pending — the platform row may not be visible until the provider handshake\n * lands. Polling stops once the status settles (or after the 5 minute cap);\n * the connections list is refreshed when it does.\n */\nexport function useConnectionStatus({ connectionId }: { connectionId: string | null }) {\n const connectFetch = useConnectFetch();\n const queryClient = useQueryClient();\n\n return useQuery<UserConnectionStatusResponse, UserConnectRequestError>({\n queryKey: ['user-connect', 'connection-status', connectionId],\n enabled: connectionId !== null,\n retry: false,\n refetchInterval: query => {\n const status = query.state.data?.status;\n if (status !== undefined && status !== 'pending') return false;\n if (query.state.dataUpdateCount >= STATUS_MAX_POLLS) return false;\n return STATUS_POLL_INTERVAL_MS;\n },\n queryFn: async () => {\n let status: UserConnectionStatusResponse;\n try {\n status = await connectFetch<UserConnectionStatusResponse>(\n `/connections/${encodeURIComponent(connectionId ?? '')}/status`,\n );\n } catch (error) {\n if (error instanceof UserConnectRequestError && error.status === 404) {\n return { id: connectionId ?? '', status: 'pending', scope: 'user' };\n }\n throw error;\n }\n if (status.status !== 'pending') {\n void invalidateAfterConnectionChange(queryClient);\n }\n return status;\n },\n });\n}\n\nexport interface PendingConnect {\n provider: string;\n connectionId: string;\n /**\n * `headless` — the Nango SDK drives the provider's own consent popup (or no\n * popup at all for collected credentials). `hosted` — a popup navigated\n * through the launch gate's 302 into the hosted connect page.\n */\n mode: 'headless' | 'hosted';\n /** Shown when the popup was blocked so the user can finish the flow manually. */\n fallbackUrl: string | null;\n}\n\n/**\n * What a caller passes to `connect()`: the provider's id plus the auth shape\n * when known. Callers that only know the id (older payloads) always get the\n * hosted flow.\n */\nexport type ConnectTarget = Pick<UserConnectProvider, 'id'> &\n Partial<Pick<UserConnectProvider, 'authType' | 'authFields'>>;\n\n/**\n * Grace period after the vendor popup closes before an unsettled flow is\n * abandoned — the activation webhook often lands a few seconds after the\n * vendor window closes itself.\n */\nconst POPUP_CLOSE_GRACE_MS = 15_000;\n/** Wall-clock cap matching the status poll's 5 minute budget. */\nconst PENDING_TIMEOUT_MS = STATUS_POLL_INTERVAL_MS * STATUS_MAX_POLLS;\n\nexport interface UsePendingConnectOptions {\n /** Overrides for the abandonment timings (primarily for tests). */\n popupCloseGraceMs?: number;\n pendingTimeoutMs?: number;\n}\n\n/**\n * Outcome of the most recently settled flow: `active` when the connection\n * activated, `failed` for everything else (non-active settle, poll failure,\n * abandoned popup, poll budget). Cleared when a new flow starts.\n */\nexport interface SettledConnect {\n provider: string;\n status: 'active' | 'failed';\n /** Human-readable failure detail (blocked popup, closed window, rejected credentials). */\n message?: string;\n}\n\n/** Window features for the hosted connect popup. */\nconst POPUP_FEATURES = 'popup,width=600,height=720';\n\n/**\n * Drives one add-connection flow end to end. Providers whose auth the browser\n * can complete headlessly go through the launch gate's JSON redemption: the\n * released session token keys the Nango SDK, which opens the provider's own\n * consent popup (or submits collected credentials with no popup). Everything\n * else gets the hosted flow — a popup opened synchronously in the click\n * gesture (so popup blockers allow it) and navigated through the launch\n * gate's 302. Either way the connection status is polled until it settles.\n * The pending state always resolves — on settle, on a poll or auth failure,\n * when the popup closes without settling (after a grace period), or at the\n * poll budget — so the Connect action is never stuck disabled.\n */\nexport function usePendingConnect(options: UsePendingConnectOptions = {}) {\n const { popupCloseGraceMs = POPUP_CLOSE_GRACE_MS, pendingTimeoutMs = PENDING_TIMEOUT_MS } = options;\n const session = useCreateConnectSession();\n const resolveLaunchHref = useConnectLaunchHref();\n const redeemLaunchToken = useRedeemLaunchToken();\n const [pending, setPending] = useState<PendingConnect | null>(null);\n const [settled, setSettled] = useState<SettledConnect | null>(null);\n const [connectedProviders, setConnectedProviders] = useState<ReadonlySet<string>>(new Set());\n const popupRef = useRef<Pick<Window, 'closed'> | null>(null);\n const headlessRef = useRef<HeadlessAuthHandle | null>(null);\n // Mirrors `pending` for async guards (the SDK promise settles outside the\n // render cycle); written directly at every transition so a cancel is never\n // raced by a trailing rejection.\n const pendingRef = useRef<PendingConnect | null>(null);\n const status = useConnectionStatus({ connectionId: pending?.connectionId ?? null });\n\n useEffect(() => {\n pendingRef.current = pending;\n }, [pending]);\n\n // Close the provider popup if the surface unmounts mid-flow.\n useEffect(\n () => () => {\n headlessRef.current?.close();\n headlessRef.current = null;\n },\n [],\n );\n\n // Settle only on a status response for the connection currently pending —\n // a stale result from a previous flow must not clear or mark a newer one.\n const statusData = status.data;\n useEffect(() => {\n if (!pending || statusData === undefined) return;\n if (statusData.id !== pending.connectionId || statusData.status === 'pending') return;\n const { provider } = pending;\n if (statusData.status === 'active') {\n setConnectedProviders(previous => (previous.has(provider) ? previous : new Set([...previous, provider])));\n }\n // The provider window closes itself on active settles; drop the handle so\n // a trailing SDK rejection cannot re-settle this flow.\n headlessRef.current = null;\n pendingRef.current = null;\n setSettled({ provider, status: statusData.status === 'active' ? 'active' : 'failed' });\n setPending(null);\n }, [pending, statusData]);\n\n // A failed poll hands the Connect action back instead of leaving it stuck.\n useEffect(() => {\n if (!status.isError || !pending) return;\n // The ref is cleared before close() so the rejection it triggers no-ops.\n pendingRef.current = null;\n headlessRef.current?.close();\n headlessRef.current = null;\n setSettled({ provider: pending.provider, status: 'failed' });\n setPending(null);\n }, [status.isError, pending]);\n\n // Abandonment: the popup closed without the flow settling (grace period for\n // the trailing webhook) or the poll budget ran out.\n useEffect(() => {\n if (!pending) return;\n const startedAt = Date.now();\n let closedAt: number | null = null;\n const tickMs = Math.min(1_000, popupCloseGraceMs);\n const timer = window.setInterval(() => {\n if (popupRef.current?.closed) closedAt ??= Date.now();\n const abandoned = closedAt !== null && Date.now() - closedAt >= popupCloseGraceMs;\n const timedOut = Date.now() - startedAt >= pendingTimeoutMs;\n if (abandoned || timedOut) {\n // The ref is cleared before close() so the rejection it triggers no-ops.\n pendingRef.current = null;\n headlessRef.current?.close();\n headlessRef.current = null;\n setSettled({ provider: pending.provider, status: 'failed' });\n setPending(null);\n }\n }, tickMs);\n return () => window.clearInterval(timer);\n }, [pending, popupCloseGraceMs, pendingTimeoutMs]);\n\n const beginHeadless = useCallback(\n async (provider: ConnectTarget, created: UserConnectSessionResponse, values?: Record<string, string>) => {\n let sessionToken: string | null = null;\n try {\n sessionToken = (await redeemLaunchToken(created.launchUrl)).sessionToken;\n } catch {\n // Redemption failure (older platform, transient error) is not fatal —\n // the hosted flow below still works through the gate's 302.\n }\n if (!sessionToken) {\n // Opened after an await, so a blocker may stop it; the fallback link\n // in the pending state covers that.\n const launchHref = resolveLaunchHref(created.launchUrl);\n const popup = window.open(launchHref, 'mastra-connect', POPUP_FEATURES);\n popupRef.current = popup;\n const next: PendingConnect = {\n provider: provider.id,\n connectionId: created.connectionId,\n mode: 'hosted',\n fallbackUrl: popup ? null : launchHref,\n };\n pendingRef.current = next;\n setPending(next);\n return;\n }\n\n const split = values ? splitHeadlessAuthValues(headlessAuthFields(provider), values) : {};\n const handle = startHeadlessAuth({\n sessionToken,\n integrationId: created.integrationId,\n authType: provider.authType,\n credentials: split.credentials,\n params: split.params,\n });\n headlessRef.current = handle;\n const next: PendingConnect = {\n provider: provider.id,\n connectionId: created.connectionId,\n mode: 'headless',\n fallbackUrl: null,\n };\n pendingRef.current = next;\n setPending(next);\n // Success needs no action here — the activation webhook lands on the\n // platform and the status poll settles the flow. Failures settle it now.\n handle.result.catch((error: unknown) => {\n if (pendingRef.current?.connectionId !== created.connectionId) return;\n headlessRef.current = null;\n pendingRef.current = null;\n const message = error instanceof HeadlessAuthError ? error.message : 'Could not authorize this connection.';\n setSettled({ provider: provider.id, status: 'failed', message });\n setPending(null);\n });\n },\n [redeemLaunchToken, resolveLaunchHref],\n );\n\n const connect = useCallback(\n (provider: ConnectTarget, values?: Record<string, string>) => {\n setSettled(null);\n headlessRef.current?.close();\n headlessRef.current = null;\n\n // Headless needs both the capability and the inputs: a caller without a\n // form (no `values`) can only run flows whose fields are all optional —\n // anything else falls back to the hosted page, which renders the form.\n const headless =\n supportsHeadlessAuth(provider.authType, provider.authFields) &&\n (values !== undefined || !headlessAuthRequiresForm(provider));\n // Hosted flows open the popup synchronously in the click gesture so\n // popup blockers allow it; headless flows never navigate to a hosted\n // page, so no window opens here.\n const popup = headless ? null : window.open('', 'mastra-connect', POPUP_FEATURES);\n popupRef.current = popup;\n session.mutate(\n { provider: provider.id },\n {\n onSuccess: created => {\n if (headless) {\n void beginHeadless(provider, created, values);\n return;\n }\n // The popup enters OAuth through the server's launch gate, which\n // verifies this browser owns the pending session before\n // redirecting — the raw vendor URL never reaches the page.\n const launchHref = resolveLaunchHref(created.launchUrl);\n if (popup) {\n popup.location.href = launchHref;\n }\n const next: PendingConnect = {\n provider: provider.id,\n connectionId: created.connectionId,\n mode: 'hosted',\n fallbackUrl: popup ? null : launchHref,\n };\n pendingRef.current = next;\n setPending(next);\n },\n onError: () => {\n popup?.close();\n popupRef.current = null;\n },\n },\n );\n },\n [session, resolveLaunchHref, beginHeadless],\n );\n\n /** Abandons the in-flight flow: closes the provider window, hands the Connect action back. */\n const cancel = useCallback(() => {\n headlessRef.current?.close();\n headlessRef.current = null;\n popupRef.current = null;\n pendingRef.current = null;\n setPending(null);\n setSettled(null);\n }, []);\n\n return {\n pending,\n connect,\n cancel,\n /** Outcome of the most recently settled flow; null while none settled or a flow is in progress. */\n settled,\n /** Providers whose connection settled `active` during this hook's lifetime. */\n connectedProviders,\n isCreating: session.isPending,\n createError: session.isError ? session.error : null,\n };\n}\n\n/** Disconnects one of the caller's own connections. */\nexport function useDisconnectUserConnection() {\n const connectFetch = useConnectFetch();\n const queryClient = useQueryClient();\n\n return useMutation<void, UserConnectRequestError, { connectionId: string }>({\n mutationFn: ({ connectionId }) =>\n connectFetch<void>(`/connections/${encodeURIComponent(connectionId)}`, { method: 'DELETE' }),\n onSuccess: () => invalidateAfterConnectionChange(queryClient),\n });\n}\n"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_ErrorBoundary = require("../../../ErrorBoundary-B5GjmA6V.cjs");
|
|
3
3
|
const require_domains_dashboards_components_dashboard_grid = require("./dashboard-grid.cjs.js");
|
|
4
4
|
const require_domains_metrics_hooks_use_metrics = require("../../metrics/hooks/use-metrics.cjs.js");
|
|
5
|
-
const require_MetricsCard = require("../../../MetricsCard-
|
|
5
|
+
const require_MetricsCard = require("../../../MetricsCard-BEwiS7Pe.cjs");
|
|
6
6
|
const require_domains_dashboards_kinds = require("../kinds.cjs.js");
|
|
7
7
|
const require_domains_dashboards_lib_prepare_widget = require("../lib/prepare-widget.cjs.js");
|
|
8
8
|
const require_domains_dashboards_components_widget_error = require("./widget-error.cjs.js");
|
|
@@ -18,7 +18,7 @@ var spanOf = (widget) => isDashboardSpan(widget.span) ? widget.span : 12;
|
|
|
18
18
|
* owns the range and filters. Each widget is checked and rendered on its own: one that fails to
|
|
19
19
|
* parse, query or render shows its own error, and the rest of the dashboard still renders.
|
|
20
20
|
*/
|
|
21
|
-
function DashboardRenderer({ definition, scope, onDrilldown, kinds = [], className }) {
|
|
21
|
+
function DashboardRenderer({ definition, scope, onDrilldown, expand, kinds = [], className }) {
|
|
22
22
|
const { datePreset, customRange, filterTokens } = require_domains_metrics_hooks_use_metrics.useMetrics();
|
|
23
23
|
const pageKey = JSON.stringify([
|
|
24
24
|
datePreset,
|
|
@@ -57,7 +57,8 @@ function DashboardRenderer({ definition, scope, onDrilldown, kinds = [], classNa
|
|
|
57
57
|
widget: prepared.widget,
|
|
58
58
|
views: prepared.views,
|
|
59
59
|
scope,
|
|
60
|
-
onDrilldown
|
|
60
|
+
onDrilldown,
|
|
61
|
+
expand
|
|
61
62
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_domains_dashboards_components_widget_error.WidgetError, {
|
|
62
63
|
widget: heading,
|
|
63
64
|
message: prepared.message
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard-renderer.cjs.js","names":["DASHBOARD_WIDGET_SPANS","useMetrics","BUILT_IN_WIDGET_KINDS","dashboardDefinitionEnvelopeSchema","MetricsCard","describeIssue","migrateDashboardDefinition","prepareWidget","DashboardGrid","DashboardGridRow","DashboardGridCell","ErrorBoundary","WidgetError"],"sources":["../../../../src/domains/dashboards/components/dashboard-renderer.tsx"],"sourcesContent":["import type { DashboardDefinition } from '@mastra/client-js';\nimport {\n DASHBOARD_WIDGET_SPANS,\n dashboardDefinitionEnvelopeSchema,\n migrateDashboardDefinition,\n} from '@mastra/core/storage/dashboards';\nimport { useMetrics } from '../../metrics/hooks/use-metrics';\nimport { BUILT_IN_WIDGET_KINDS } from '../kinds';\nimport type { DashboardScope, OnDashboardDrilldown } from '../lib/definition';\nimport { describeIssue, prepareWidget } from '../lib/prepare-widget';\nimport type { PreparedWidget, StoredWidget } from '../lib/prepare-widget';\nimport type { AnyWidgetKind } from '../widget-kind';\nimport { DashboardGrid, DashboardGridCell, DashboardGridRow } from './dashboard-grid';\nimport type { DashboardSpan } from './dashboard-grid';\nimport { WidgetError } from './widget-error';\nimport { ErrorBoundary } from '@/ds/components/ErrorBoundary';\nimport { MetricsCard } from '@/ds/components/MetricsCard';\n\nexport type DashboardRendererProps = {\n definition: DashboardDefinition;\n /** Which widgets, views and columns show: `org` spans several projects, `project` is one. */\n scope: DashboardScope;\n /** Opens a drilldown; each host routes to its own Traces or Logs page. */\n onDrilldown?: OnDashboardDrilldown;\n /** Kinds beyond the built-in ones; a kind of the same name replaces the built-in. */\n kinds?: AnyWidgetKind[];\n className?: string;\n};\n\n/** Whether a stored widget's `span` is one the grid lays out. */\nconst isDashboardSpan = (span: unknown): span is DashboardSpan => DASHBOARD_WIDGET_SPANS.some(s => s === span);\ntype ShownCell = { widget: StoredWidget; prepared: Exclude<PreparedWidget, { ok: 'hidden' }> };\nconst isShown = (cell: { widget: StoredWidget; prepared: PreparedWidget }): cell is ShownCell =>\n cell.prepared.ok !== 'hidden';\n\nconst spanOf = (widget: StoredWidget) => (isDashboardSpan(widget.span) ? widget.span : 12);\n\n/**\n * Lays a dashboard definition out on a 12-column grid inside the page's `MetricsProvider`, which\n * owns the range and filters. Each widget is checked and rendered on its own: one that fails to\n * parse, query or render shows its own error, and the rest of the dashboard still renders.\n */\nexport function DashboardRenderer({
|
|
1
|
+
{"version":3,"file":"dashboard-renderer.cjs.js","names":["DASHBOARD_WIDGET_SPANS","useMetrics","BUILT_IN_WIDGET_KINDS","dashboardDefinitionEnvelopeSchema","MetricsCard","describeIssue","migrateDashboardDefinition","prepareWidget","DashboardGrid","DashboardGridRow","DashboardGridCell","ErrorBoundary","WidgetError"],"sources":["../../../../src/domains/dashboards/components/dashboard-renderer.tsx"],"sourcesContent":["import type { DashboardDefinition } from '@mastra/client-js';\nimport {\n DASHBOARD_WIDGET_SPANS,\n dashboardDefinitionEnvelopeSchema,\n migrateDashboardDefinition,\n} from '@mastra/core/storage/dashboards';\nimport { useMetrics } from '../../metrics/hooks/use-metrics';\nimport { BUILT_IN_WIDGET_KINDS } from '../kinds';\nimport type { DashboardScope, OnDashboardDrilldown } from '../lib/definition';\nimport { describeIssue, prepareWidget } from '../lib/prepare-widget';\nimport type { PreparedWidget, StoredWidget } from '../lib/prepare-widget';\nimport type { AnyWidgetKind, DashboardExpand } from '../widget-kind';\nimport { DashboardGrid, DashboardGridCell, DashboardGridRow } from './dashboard-grid';\nimport type { DashboardSpan } from './dashboard-grid';\nimport { WidgetError } from './widget-error';\nimport { ErrorBoundary } from '@/ds/components/ErrorBoundary';\nimport { MetricsCard } from '@/ds/components/MetricsCard';\n\nexport type DashboardRendererProps = {\n definition: DashboardDefinition;\n /** Which widgets, views and columns show: `org` spans several projects, `project` is one. */\n scope: DashboardScope;\n /** Opens a drilldown; each host routes to its own Traces or Logs page. */\n onDrilldown?: OnDashboardDrilldown;\n /** Where chart cards expand to, e.g. the host's query page; see `DashboardExpand`. */\n expand?: DashboardExpand;\n /** Kinds beyond the built-in ones; a kind of the same name replaces the built-in. */\n kinds?: AnyWidgetKind[];\n className?: string;\n};\n\n/** Whether a stored widget's `span` is one the grid lays out. */\nconst isDashboardSpan = (span: unknown): span is DashboardSpan => DASHBOARD_WIDGET_SPANS.some(s => s === span);\ntype ShownCell = { widget: StoredWidget; prepared: Exclude<PreparedWidget, { ok: 'hidden' }> };\nconst isShown = (cell: { widget: StoredWidget; prepared: PreparedWidget }): cell is ShownCell =>\n cell.prepared.ok !== 'hidden';\n\nconst spanOf = (widget: StoredWidget) => (isDashboardSpan(widget.span) ? widget.span : 12);\n\n/**\n * Lays a dashboard definition out on a 12-column grid inside the page's `MetricsProvider`, which\n * owns the range and filters. Each widget is checked and rendered on its own: one that fails to\n * parse, query or render shows its own error, and the rest of the dashboard still renders.\n */\nexport function DashboardRenderer({\n definition,\n scope,\n onDrilldown,\n expand,\n kinds = [],\n className,\n}: DashboardRendererProps) {\n // A widget that threw renders again once the page's range or filters change, or the widget does.\n const { datePreset, customRange, filterTokens } = useMetrics();\n const pageKey = JSON.stringify([datePreset, customRange, filterTokens]);\n const registry = new Map([...BUILT_IN_WIDGET_KINDS, ...kinds].map(k => [k.kind, k]));\n\n const envelope = dashboardDefinitionEnvelopeSchema.safeParse(definition);\n if (!envelope.success) {\n return <MetricsCard.Error message={`This dashboard can't be shown. ${describeIssue(envelope.error.issues[0])}`} />;\n }\n // Each widget is parsed on its own below, so one that doesn't fit the current version shows its own error.\n const { rows } = migrateDashboardDefinition(envelope.data);\n\n // A row whose widgets are all hidden on this page isn't drawn, so it leaves no gap in the grid.\n const shown = rows\n .map((row, rowIndex) => ({\n rowIndex,\n widgets: row.widgets\n .map(widget => ({ widget, prepared: prepareWidget(widget, registry, scope) }))\n .filter(isShown),\n }))\n .filter(row => row.widgets.length > 0);\n\n return (\n <DashboardGrid className={className}>\n {shown.map(({ rowIndex, widgets }) => (\n <DashboardGridRow key={rowIndex}>\n {widgets.map(({ widget, prepared }) => {\n // Unparsed, the title may be anything; the error card shows it only when it's text.\n const heading = { id: widget.id, title: typeof widget.title === 'string' ? widget.title : undefined };\n return (\n <DashboardGridCell key={widget.id} id={widget.id} span={spanOf(widget)}>\n <ErrorBoundary\n resetKeys={[pageKey, JSON.stringify(widget)]}\n variant=\"inline\"\n fallback={({ error }) => <WidgetError widget={heading} message={error.message} />}\n >\n {prepared.ok ? (\n <prepared.kind.component\n widget={prepared.widget}\n views={prepared.views}\n scope={scope}\n onDrilldown={onDrilldown}\n expand={expand}\n />\n ) : (\n <WidgetError widget={heading} message={prepared.message} />\n )}\n </ErrorBoundary>\n </DashboardGridCell>\n );\n })}\n </DashboardGridRow>\n ))}\n </DashboardGrid>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAgCA,IAAM,mBAAmB,SAAyCA,gCAAAA,uBAAuB,MAAK,MAAK,MAAM,IAAI;AAE7G,IAAM,WAAW,SACf,KAAK,SAAS,OAAO;AAEvB,IAAM,UAAU,WAA0B,gBAAgB,OAAO,IAAI,IAAI,OAAO,OAAO;;;;;;AAOvF,SAAgB,kBAAkB,EAChC,YACA,OACA,aACA,QACA,QAAQ,CAAC,GACT,aACyB;CAEzB,MAAM,EAAE,YAAY,aAAa,iBAAiBC,0CAAAA,WAAW;CAC7D,MAAM,UAAU,KAAK,UAAU;EAAC;EAAY;EAAa;CAAY,CAAC;CACtE,MAAM,WAAW,IAAI,IAAI,CAAC,GAAGC,iCAAAA,uBAAuB,GAAG,KAAK,CAAC,CAAC,KAAI,MAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;CAEnF,MAAM,WAAWC,gCAAAA,kCAAkC,UAAU,UAAU;CACvE,IAAI,CAAC,SAAS,SACZ,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAACC,oBAAAA,YAAY,OAAb,EAAmB,SAAS,kCAAkCC,8CAAAA,cAAc,SAAS,MAAM,OAAO,EAAE,IAAM,CAAA;CAGnH,MAAM,EAAE,UAAA,GAASC,gCAAAA,2BAAAA,CAA2B,SAAS,IAAI;CAGzD,MAAM,QAAQ,KACX,KAAK,KAAK,cAAc;EACvB;EACA,SAAS,IAAI,QACV,KAAI,YAAW;GAAE;GAAQ,UAAUC,8CAAAA,cAAc,QAAQ,UAAU,KAAK;EAAE,EAAE,CAAC,CAC7E,OAAO,OAAO;CACnB,EAAE,CAAC,CACF,QAAO,QAAO,IAAI,QAAQ,SAAS,CAAC;CAEvC,OACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,qDAAAA,eAAD;EAA0B;EACvB,UAAA,MAAM,KAAK,EAAE,UAAU,cACtB,iBAAA,GAAA,kBAAA,IAAA,CAACC,qDAAAA,kBAAD,EAAA,UACG,QAAQ,KAAK,EAAE,QAAQ,eAAe;GAErC,MAAM,UAAU;IAAE,IAAI,OAAO;IAAI,OAAO,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ,KAAA;GAAU;GACpG,OACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,qDAAAA,mBAAD;IAAmC,IAAI,OAAO;IAAI,MAAM,OAAO,MAAM;IACnE,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACC,sBAAAA,eAAD;KACE,WAAW,CAAC,SAAS,KAAK,UAAU,MAAM,CAAC;KAC3C,SAAQ;KACR,WAAW,EAAE,YAAY,iBAAA,GAAA,kBAAA,IAAA,CAACC,mDAAAA,aAAD;MAAa,QAAQ;MAAS,SAAS,MAAM;KAAU,CAAA;KAE/E,UAAA,SAAS,KACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAS,KAAK,WAAf;MACE,QAAQ,SAAS;MACjB,OAAO,SAAS;MACT;MACM;MACL;KACT,CAAA,IAED,iBAAA,GAAA,kBAAA,IAAA,CAACA,mDAAAA,aAAD;MAAa,QAAQ;MAAS,SAAS,SAAS;KAAU,CAAA;IAE/C,CAAA;GACE,GAlBK,OAAO,EAkBZ;EAEvB,CAAC,EACe,GA1BK,QA0BL,CACnB;CACY,CAAA;AAEnB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as ErrorBoundary } from "../../../ErrorBoundary-Cat3KU86.js";
|
|
2
2
|
import { DashboardGrid, DashboardGridCell, DashboardGridRow } from "./dashboard-grid.es.js";
|
|
3
3
|
import { useMetrics } from "../../metrics/hooks/use-metrics.es.js";
|
|
4
|
-
import { t as MetricsCard } from "../../../MetricsCard-
|
|
4
|
+
import { t as MetricsCard } from "../../../MetricsCard-ua6C6b8a.js";
|
|
5
5
|
import { BUILT_IN_WIDGET_KINDS } from "../kinds.es.js";
|
|
6
6
|
import { describeIssue, prepareWidget } from "../lib/prepare-widget.es.js";
|
|
7
7
|
import { WidgetError } from "./widget-error.es.js";
|
|
@@ -17,7 +17,7 @@ var spanOf = (widget) => isDashboardSpan(widget.span) ? widget.span : 12;
|
|
|
17
17
|
* owns the range and filters. Each widget is checked and rendered on its own: one that fails to
|
|
18
18
|
* parse, query or render shows its own error, and the rest of the dashboard still renders.
|
|
19
19
|
*/
|
|
20
|
-
function DashboardRenderer({ definition, scope, onDrilldown, kinds = [], className }) {
|
|
20
|
+
function DashboardRenderer({ definition, scope, onDrilldown, expand, kinds = [], className }) {
|
|
21
21
|
const { datePreset, customRange, filterTokens } = useMetrics();
|
|
22
22
|
const pageKey = JSON.stringify([
|
|
23
23
|
datePreset,
|
|
@@ -56,7 +56,8 @@ function DashboardRenderer({ definition, scope, onDrilldown, kinds = [], classNa
|
|
|
56
56
|
widget: prepared.widget,
|
|
57
57
|
views: prepared.views,
|
|
58
58
|
scope,
|
|
59
|
-
onDrilldown
|
|
59
|
+
onDrilldown,
|
|
60
|
+
expand
|
|
60
61
|
}) : /* @__PURE__ */ jsx(WidgetError, {
|
|
61
62
|
widget: heading,
|
|
62
63
|
message: prepared.message
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard-renderer.es.js","names":[],"sources":["../../../../src/domains/dashboards/components/dashboard-renderer.tsx"],"sourcesContent":["import type { DashboardDefinition } from '@mastra/client-js';\nimport {\n DASHBOARD_WIDGET_SPANS,\n dashboardDefinitionEnvelopeSchema,\n migrateDashboardDefinition,\n} from '@mastra/core/storage/dashboards';\nimport { useMetrics } from '../../metrics/hooks/use-metrics';\nimport { BUILT_IN_WIDGET_KINDS } from '../kinds';\nimport type { DashboardScope, OnDashboardDrilldown } from '../lib/definition';\nimport { describeIssue, prepareWidget } from '../lib/prepare-widget';\nimport type { PreparedWidget, StoredWidget } from '../lib/prepare-widget';\nimport type { AnyWidgetKind } from '../widget-kind';\nimport { DashboardGrid, DashboardGridCell, DashboardGridRow } from './dashboard-grid';\nimport type { DashboardSpan } from './dashboard-grid';\nimport { WidgetError } from './widget-error';\nimport { ErrorBoundary } from '@/ds/components/ErrorBoundary';\nimport { MetricsCard } from '@/ds/components/MetricsCard';\n\nexport type DashboardRendererProps = {\n definition: DashboardDefinition;\n /** Which widgets, views and columns show: `org` spans several projects, `project` is one. */\n scope: DashboardScope;\n /** Opens a drilldown; each host routes to its own Traces or Logs page. */\n onDrilldown?: OnDashboardDrilldown;\n /** Kinds beyond the built-in ones; a kind of the same name replaces the built-in. */\n kinds?: AnyWidgetKind[];\n className?: string;\n};\n\n/** Whether a stored widget's `span` is one the grid lays out. */\nconst isDashboardSpan = (span: unknown): span is DashboardSpan => DASHBOARD_WIDGET_SPANS.some(s => s === span);\ntype ShownCell = { widget: StoredWidget; prepared: Exclude<PreparedWidget, { ok: 'hidden' }> };\nconst isShown = (cell: { widget: StoredWidget; prepared: PreparedWidget }): cell is ShownCell =>\n cell.prepared.ok !== 'hidden';\n\nconst spanOf = (widget: StoredWidget) => (isDashboardSpan(widget.span) ? widget.span : 12);\n\n/**\n * Lays a dashboard definition out on a 12-column grid inside the page's `MetricsProvider`, which\n * owns the range and filters. Each widget is checked and rendered on its own: one that fails to\n * parse, query or render shows its own error, and the rest of the dashboard still renders.\n */\nexport function DashboardRenderer({
|
|
1
|
+
{"version":3,"file":"dashboard-renderer.es.js","names":[],"sources":["../../../../src/domains/dashboards/components/dashboard-renderer.tsx"],"sourcesContent":["import type { DashboardDefinition } from '@mastra/client-js';\nimport {\n DASHBOARD_WIDGET_SPANS,\n dashboardDefinitionEnvelopeSchema,\n migrateDashboardDefinition,\n} from '@mastra/core/storage/dashboards';\nimport { useMetrics } from '../../metrics/hooks/use-metrics';\nimport { BUILT_IN_WIDGET_KINDS } from '../kinds';\nimport type { DashboardScope, OnDashboardDrilldown } from '../lib/definition';\nimport { describeIssue, prepareWidget } from '../lib/prepare-widget';\nimport type { PreparedWidget, StoredWidget } from '../lib/prepare-widget';\nimport type { AnyWidgetKind, DashboardExpand } from '../widget-kind';\nimport { DashboardGrid, DashboardGridCell, DashboardGridRow } from './dashboard-grid';\nimport type { DashboardSpan } from './dashboard-grid';\nimport { WidgetError } from './widget-error';\nimport { ErrorBoundary } from '@/ds/components/ErrorBoundary';\nimport { MetricsCard } from '@/ds/components/MetricsCard';\n\nexport type DashboardRendererProps = {\n definition: DashboardDefinition;\n /** Which widgets, views and columns show: `org` spans several projects, `project` is one. */\n scope: DashboardScope;\n /** Opens a drilldown; each host routes to its own Traces or Logs page. */\n onDrilldown?: OnDashboardDrilldown;\n /** Where chart cards expand to, e.g. the host's query page; see `DashboardExpand`. */\n expand?: DashboardExpand;\n /** Kinds beyond the built-in ones; a kind of the same name replaces the built-in. */\n kinds?: AnyWidgetKind[];\n className?: string;\n};\n\n/** Whether a stored widget's `span` is one the grid lays out. */\nconst isDashboardSpan = (span: unknown): span is DashboardSpan => DASHBOARD_WIDGET_SPANS.some(s => s === span);\ntype ShownCell = { widget: StoredWidget; prepared: Exclude<PreparedWidget, { ok: 'hidden' }> };\nconst isShown = (cell: { widget: StoredWidget; prepared: PreparedWidget }): cell is ShownCell =>\n cell.prepared.ok !== 'hidden';\n\nconst spanOf = (widget: StoredWidget) => (isDashboardSpan(widget.span) ? widget.span : 12);\n\n/**\n * Lays a dashboard definition out on a 12-column grid inside the page's `MetricsProvider`, which\n * owns the range and filters. Each widget is checked and rendered on its own: one that fails to\n * parse, query or render shows its own error, and the rest of the dashboard still renders.\n */\nexport function DashboardRenderer({\n definition,\n scope,\n onDrilldown,\n expand,\n kinds = [],\n className,\n}: DashboardRendererProps) {\n // A widget that threw renders again once the page's range or filters change, or the widget does.\n const { datePreset, customRange, filterTokens } = useMetrics();\n const pageKey = JSON.stringify([datePreset, customRange, filterTokens]);\n const registry = new Map([...BUILT_IN_WIDGET_KINDS, ...kinds].map(k => [k.kind, k]));\n\n const envelope = dashboardDefinitionEnvelopeSchema.safeParse(definition);\n if (!envelope.success) {\n return <MetricsCard.Error message={`This dashboard can't be shown. ${describeIssue(envelope.error.issues[0])}`} />;\n }\n // Each widget is parsed on its own below, so one that doesn't fit the current version shows its own error.\n const { rows } = migrateDashboardDefinition(envelope.data);\n\n // A row whose widgets are all hidden on this page isn't drawn, so it leaves no gap in the grid.\n const shown = rows\n .map((row, rowIndex) => ({\n rowIndex,\n widgets: row.widgets\n .map(widget => ({ widget, prepared: prepareWidget(widget, registry, scope) }))\n .filter(isShown),\n }))\n .filter(row => row.widgets.length > 0);\n\n return (\n <DashboardGrid className={className}>\n {shown.map(({ rowIndex, widgets }) => (\n <DashboardGridRow key={rowIndex}>\n {widgets.map(({ widget, prepared }) => {\n // Unparsed, the title may be anything; the error card shows it only when it's text.\n const heading = { id: widget.id, title: typeof widget.title === 'string' ? widget.title : undefined };\n return (\n <DashboardGridCell key={widget.id} id={widget.id} span={spanOf(widget)}>\n <ErrorBoundary\n resetKeys={[pageKey, JSON.stringify(widget)]}\n variant=\"inline\"\n fallback={({ error }) => <WidgetError widget={heading} message={error.message} />}\n >\n {prepared.ok ? (\n <prepared.kind.component\n widget={prepared.widget}\n views={prepared.views}\n scope={scope}\n onDrilldown={onDrilldown}\n expand={expand}\n />\n ) : (\n <WidgetError widget={heading} message={prepared.message} />\n )}\n </ErrorBoundary>\n </DashboardGridCell>\n );\n })}\n </DashboardGridRow>\n ))}\n </DashboardGrid>\n );\n}\n"],"mappings":";;;;;;;;;;;AAgCA,IAAM,mBAAmB,SAAyC,uBAAuB,MAAK,MAAK,MAAM,IAAI;AAE7G,IAAM,WAAW,SACf,KAAK,SAAS,OAAO;AAEvB,IAAM,UAAU,WAA0B,gBAAgB,OAAO,IAAI,IAAI,OAAO,OAAO;;;;;;AAOvF,SAAgB,kBAAkB,EAChC,YACA,OACA,aACA,QACA,QAAQ,CAAC,GACT,aACyB;CAEzB,MAAM,EAAE,YAAY,aAAa,iBAAiB,WAAW;CAC7D,MAAM,UAAU,KAAK,UAAU;EAAC;EAAY;EAAa;CAAY,CAAC;CACtE,MAAM,WAAW,IAAI,IAAI,CAAC,GAAG,uBAAuB,GAAG,KAAK,CAAC,CAAC,KAAI,MAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;CAEnF,MAAM,WAAW,kCAAkC,UAAU,UAAU;CACvE,IAAI,CAAC,SAAS,SACZ,OAAO,oBAAC,YAAY,OAAb,EAAmB,SAAS,kCAAkC,cAAc,SAAS,MAAM,OAAO,EAAE,IAAM,CAAA;CAGnH,MAAM,EAAE,SAAS,2BAA2B,SAAS,IAAI;CAGzD,MAAM,QAAQ,KACX,KAAK,KAAK,cAAc;EACvB;EACA,SAAS,IAAI,QACV,KAAI,YAAW;GAAE;GAAQ,UAAU,cAAc,QAAQ,UAAU,KAAK;EAAE,EAAE,CAAC,CAC7E,OAAO,OAAO;CACnB,EAAE,CAAC,CACF,QAAO,QAAO,IAAI,QAAQ,SAAS,CAAC;CAEvC,OACE,oBAAC,eAAD;EAA0B;EACvB,UAAA,MAAM,KAAK,EAAE,UAAU,cACtB,oBAAC,kBAAD,EAAA,UACG,QAAQ,KAAK,EAAE,QAAQ,eAAe;GAErC,MAAM,UAAU;IAAE,IAAI,OAAO;IAAI,OAAO,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ,KAAA;GAAU;GACpG,OACE,oBAAC,mBAAD;IAAmC,IAAI,OAAO;IAAI,MAAM,OAAO,MAAM;IACnE,UAAA,oBAAC,eAAD;KACE,WAAW,CAAC,SAAS,KAAK,UAAU,MAAM,CAAC;KAC3C,SAAQ;KACR,WAAW,EAAE,YAAY,oBAAC,aAAD;MAAa,QAAQ;MAAS,SAAS,MAAM;KAAU,CAAA;KAE/E,UAAA,SAAS,KACR,oBAAC,SAAS,KAAK,WAAf;MACE,QAAQ,SAAS;MACjB,OAAO,SAAS;MACT;MACM;MACL;KACT,CAAA,IAED,oBAAC,aAAD;MAAa,QAAQ;MAAS,SAAS,SAAS;KAAU,CAAA;IAE/C,CAAA;GACE,GAlBK,OAAO,EAkBZ;EAEvB,CAAC,EACe,GA1BK,QA0BL,CACnB;CACY,CAAA;AAEnB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_MetricsCard = require("../../../MetricsCard-
|
|
2
|
+
const require_MetricsCard = require("../../../MetricsCard-BEwiS7Pe.cjs");
|
|
3
3
|
const require_domains_metrics_components_chart_card = require("../../metrics/components/chart-card.cjs.js");
|
|
4
4
|
const require_domains_dashboards_lib_chrome = require("../lib/chrome.cjs.js");
|
|
5
5
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as MetricsCard } from "../../../MetricsCard-
|
|
1
|
+
import { t as MetricsCard } from "../../../MetricsCard-ua6C6b8a.js";
|
|
2
2
|
import { ChartCard } from "../../metrics/components/chart-card.es.js";
|
|
3
3
|
import { unsupportedMessage } from "../lib/chrome.es.js";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_MetricsCard = require("../../../MetricsCard-
|
|
2
|
+
const require_MetricsCard = require("../../../MetricsCard-BEwiS7Pe.cjs");
|
|
3
3
|
const require_domains_metrics_components_chart_card = require("../../metrics/components/chart-card.cjs.js");
|
|
4
4
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
5
5
|
// src/domains/dashboards/components/widget-error.tsx
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as MetricsCard } from "../../../MetricsCard-
|
|
1
|
+
import { t as MetricsCard } from "../../../MetricsCard-ua6C6b8a.js";
|
|
2
2
|
import { ChartCard } from "../../metrics/components/chart-card.es.js";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
// src/domains/dashboards/components/widget-error.tsx
|
|
@@ -5,16 +5,17 @@ let react = require("react");
|
|
|
5
5
|
// src/domains/dashboards/hooks/use-widget-views.ts
|
|
6
6
|
/**
|
|
7
7
|
* The open view of a widget, `defaultIndex` until one is picked, and the card title for it
|
|
8
|
-
* (`title`, and `titleMenu` with several views) to spread into
|
|
8
|
+
* (`title`, and `titleMenu` with several views, or an expand link with `expand`) to spread into
|
|
9
|
+
* `ChartCard`.
|
|
9
10
|
*/
|
|
10
|
-
function useViewMenu(widget, views, defaultIndex = 0) {
|
|
11
|
+
function useViewMenu(widget, views, defaultIndex = 0, expand) {
|
|
11
12
|
const [chosen, choose] = (0, react.useState)();
|
|
12
13
|
const wanted = chosen ?? defaultIndex;
|
|
13
14
|
const index = views[wanted] ? wanted : 0;
|
|
14
15
|
return {
|
|
15
16
|
view: views[index] ?? views[0],
|
|
16
17
|
index,
|
|
17
|
-
heading: require_domains_dashboards_lib_chrome.viewTitle(widget, views, index, choose)
|
|
18
|
+
heading: require_domains_dashboards_lib_chrome.viewTitle(widget, views, index, choose, expand)
|
|
18
19
|
};
|
|
19
20
|
}
|
|
20
21
|
/** What clicking a bucket or row opens, with what was clicked; `undefined` when the view has no click target. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-widget-views.cjs.js","names":["useState","viewTitle","viewDrilldown","targetScope"],"sources":["../../../../src/domains/dashboards/hooks/use-widget-views.ts"],"sourcesContent":["import type { DashboardWidget, DashboardWidgetView } from '@mastra/client-js';\nimport { useState } from 'react';\nimport type { DrilldownScope } from '../../metrics/drilldown';\nimport { viewTitle } from '../lib/chrome';\nimport { viewDrilldown, targetScope } from '../lib/definition';\nimport type { OnDashboardDrilldown } from '../lib/definition';\nimport type { ResolvedView } from '../widget-kind';\n\nexport type Views<Options> = readonly [ResolvedView<Options>, ...ResolvedView<Options>[]];\n\n/**\n * The open view of a widget, `defaultIndex` until one is picked, and the card title for it\n * (`title`, and `titleMenu` with several views) to spread into `ChartCard`.\n */\nexport function useViewMenu<Options>(widget: DashboardWidget
|
|
1
|
+
{"version":3,"file":"use-widget-views.cjs.js","names":["useState","viewTitle","viewDrilldown","targetScope"],"sources":["../../../../src/domains/dashboards/hooks/use-widget-views.ts"],"sourcesContent":["import type { DashboardWidget, DashboardWidgetView } from '@mastra/client-js';\nimport { useState } from 'react';\nimport type { DrilldownScope } from '../../metrics/drilldown';\nimport { viewTitle } from '../lib/chrome';\nimport { viewDrilldown, targetScope } from '../lib/definition';\nimport type { OnDashboardDrilldown } from '../lib/definition';\nimport type { DashboardExpand, ResolvedView } from '../widget-kind';\n\nexport type Views<Options> = readonly [ResolvedView<Options>, ...ResolvedView<Options>[]];\n\n/**\n * The open view of a widget, `defaultIndex` until one is picked, and the card title for it\n * (`title`, and `titleMenu` with several views, or an expand link with `expand`) to spread into\n * `ChartCard`.\n */\nexport function useViewMenu<Options>(\n widget: DashboardWidget,\n views: Views<Options>,\n defaultIndex = 0,\n expand?: DashboardExpand,\n) {\n const [chosen, choose] = useState<number>();\n const wanted = chosen ?? defaultIndex;\n const index = views[wanted] ? wanted : 0;\n return { view: views[index] ?? views[0], index, heading: viewTitle(widget, views, index, choose, expand) };\n}\n\n/** What clicking a bucket or row opens, with what was clicked; `undefined` when the view has no click target. */\nexport function useDrilldown(\n widget: DashboardWidget,\n view: DashboardWidgetView,\n onDrilldown: OnDashboardDrilldown | undefined,\n) {\n const { click } = viewDrilldown(widget, view);\n if (!onDrilldown || !click) return undefined;\n return (clicked: DrilldownScope) => onDrilldown(click.page, { ...targetScope(click), ...clicked });\n}\n\n/** Whether the view has an action button to show (`DrilldownAction`). */\nexport const hasDrilldownAction = (\n widget: DashboardWidget,\n view: DashboardWidgetView,\n onDrilldown: OnDashboardDrilldown | undefined,\n) => onDrilldown !== undefined && viewDrilldown(widget, view).action !== undefined;\n"],"mappings":";;;;;;;;;;AAeA,SAAgB,YACd,QACA,OACA,eAAe,GACf,QACA;CACA,MAAM,CAAC,QAAQ,WAAA,GAAUA,MAAAA,SAAAA,CAAiB;CAC1C,MAAM,SAAS,UAAU;CACzB,MAAM,QAAQ,MAAM,UAAU,SAAS;CACvC,OAAO;EAAE,MAAM,MAAM,UAAU,MAAM;EAAI;EAAO,SAASC,sCAAAA,UAAU,QAAQ,OAAO,OAAO,QAAQ,MAAM;CAAE;AAC3G;;AAGA,SAAgB,aACd,QACA,MACA,aACA;CACA,MAAM,EAAE,UAAUC,0CAAAA,cAAc,QAAQ,IAAI;CAC5C,IAAI,CAAC,eAAe,CAAC,OAAO,OAAO,KAAA;CACnC,QAAQ,YAA4B,YAAY,MAAM,MAAM;EAAE,GAAGC,0CAAAA,YAAY,KAAK;EAAG,GAAG;CAAQ,CAAC;AACnG;;AAGA,IAAa,sBACX,QACA,MACA,gBACG,gBAAgB,KAAA,KAAaD,0CAAAA,cAAc,QAAQ,IAAI,CAAC,CAAC,WAAW,KAAA"}
|
|
@@ -4,16 +4,17 @@ import { useState } from "react";
|
|
|
4
4
|
// src/domains/dashboards/hooks/use-widget-views.ts
|
|
5
5
|
/**
|
|
6
6
|
* The open view of a widget, `defaultIndex` until one is picked, and the card title for it
|
|
7
|
-
* (`title`, and `titleMenu` with several views) to spread into
|
|
7
|
+
* (`title`, and `titleMenu` with several views, or an expand link with `expand`) to spread into
|
|
8
|
+
* `ChartCard`.
|
|
8
9
|
*/
|
|
9
|
-
function useViewMenu(widget, views, defaultIndex = 0) {
|
|
10
|
+
function useViewMenu(widget, views, defaultIndex = 0, expand) {
|
|
10
11
|
const [chosen, choose] = useState();
|
|
11
12
|
const wanted = chosen ?? defaultIndex;
|
|
12
13
|
const index = views[wanted] ? wanted : 0;
|
|
13
14
|
return {
|
|
14
15
|
view: views[index] ?? views[0],
|
|
15
16
|
index,
|
|
16
|
-
heading: viewTitle(widget, views, index, choose)
|
|
17
|
+
heading: viewTitle(widget, views, index, choose, expand)
|
|
17
18
|
};
|
|
18
19
|
}
|
|
19
20
|
/** What clicking a bucket or row opens, with what was clicked; `undefined` when the view has no click target. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-widget-views.es.js","names":[],"sources":["../../../../src/domains/dashboards/hooks/use-widget-views.ts"],"sourcesContent":["import type { DashboardWidget, DashboardWidgetView } from '@mastra/client-js';\nimport { useState } from 'react';\nimport type { DrilldownScope } from '../../metrics/drilldown';\nimport { viewTitle } from '../lib/chrome';\nimport { viewDrilldown, targetScope } from '../lib/definition';\nimport type { OnDashboardDrilldown } from '../lib/definition';\nimport type { ResolvedView } from '../widget-kind';\n\nexport type Views<Options> = readonly [ResolvedView<Options>, ...ResolvedView<Options>[]];\n\n/**\n * The open view of a widget, `defaultIndex` until one is picked, and the card title for it\n * (`title`, and `titleMenu` with several views) to spread into `ChartCard`.\n */\nexport function useViewMenu<Options>(widget: DashboardWidget
|
|
1
|
+
{"version":3,"file":"use-widget-views.es.js","names":[],"sources":["../../../../src/domains/dashboards/hooks/use-widget-views.ts"],"sourcesContent":["import type { DashboardWidget, DashboardWidgetView } from '@mastra/client-js';\nimport { useState } from 'react';\nimport type { DrilldownScope } from '../../metrics/drilldown';\nimport { viewTitle } from '../lib/chrome';\nimport { viewDrilldown, targetScope } from '../lib/definition';\nimport type { OnDashboardDrilldown } from '../lib/definition';\nimport type { DashboardExpand, ResolvedView } from '../widget-kind';\n\nexport type Views<Options> = readonly [ResolvedView<Options>, ...ResolvedView<Options>[]];\n\n/**\n * The open view of a widget, `defaultIndex` until one is picked, and the card title for it\n * (`title`, and `titleMenu` with several views, or an expand link with `expand`) to spread into\n * `ChartCard`.\n */\nexport function useViewMenu<Options>(\n widget: DashboardWidget,\n views: Views<Options>,\n defaultIndex = 0,\n expand?: DashboardExpand,\n) {\n const [chosen, choose] = useState<number>();\n const wanted = chosen ?? defaultIndex;\n const index = views[wanted] ? wanted : 0;\n return { view: views[index] ?? views[0], index, heading: viewTitle(widget, views, index, choose, expand) };\n}\n\n/** What clicking a bucket or row opens, with what was clicked; `undefined` when the view has no click target. */\nexport function useDrilldown(\n widget: DashboardWidget,\n view: DashboardWidgetView,\n onDrilldown: OnDashboardDrilldown | undefined,\n) {\n const { click } = viewDrilldown(widget, view);\n if (!onDrilldown || !click) return undefined;\n return (clicked: DrilldownScope) => onDrilldown(click.page, { ...targetScope(click), ...clicked });\n}\n\n/** Whether the view has an action button to show (`DrilldownAction`). */\nexport const hasDrilldownAction = (\n widget: DashboardWidget,\n view: DashboardWidgetView,\n onDrilldown: OnDashboardDrilldown | undefined,\n) => onDrilldown !== undefined && viewDrilldown(widget, view).action !== undefined;\n"],"mappings":";;;;;;;;;AAeA,SAAgB,YACd,QACA,OACA,eAAe,GACf,QACA;CACA,MAAM,CAAC,QAAQ,UAAU,SAAiB;CAC1C,MAAM,SAAS,UAAU;CACzB,MAAM,QAAQ,MAAM,UAAU,SAAS;CACvC,OAAO;EAAE,MAAM,MAAM,UAAU,MAAM;EAAI;EAAO,SAAS,UAAU,QAAQ,OAAO,OAAO,QAAQ,MAAM;CAAE;AAC3G;;AAGA,SAAgB,aACd,QACA,MACA,aACA;CACA,MAAM,EAAE,UAAU,cAAc,QAAQ,IAAI;CAC5C,IAAI,CAAC,eAAe,CAAC,OAAO,OAAO,KAAA;CACnC,QAAQ,YAA4B,YAAY,MAAM,MAAM;EAAE,GAAG,YAAY,KAAK;EAAG,GAAG;CAAQ,CAAC;AACnG;;AAGA,IAAa,sBACX,QACA,MACA,gBACG,gBAAgB,KAAA,KAAa,cAAc,QAAQ,IAAI,CAAC,CAAC,WAAW,KAAA"}
|
|
@@ -10,8 +10,9 @@ const require_domains_metrics_lib_chart_axis = require("../../../metrics/lib/cha
|
|
|
10
10
|
const require_domains_metrics_lib_chart_colors = require("../../../metrics/lib/chart-colors.cjs.js");
|
|
11
11
|
const require_domains_dashboards_lib_scores = require("../../lib/scores.cjs.js");
|
|
12
12
|
const require_domains_dashboards_hooks_use_scorers = require("../../hooks/use-scorers.cjs.js");
|
|
13
|
+
const require_domains_dashboards_lib_chrome = require("../../lib/chrome.cjs.js");
|
|
13
14
|
const require_domains_dashboards_lib_format = require("../../lib/format.cjs.js");
|
|
14
|
-
const require_MetricsLineChart = require("../../../../MetricsLineChart-
|
|
15
|
+
const require_MetricsLineChart = require("../../../../MetricsLineChart-tZxsauau.cjs");
|
|
15
16
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
16
17
|
// src/domains/dashboards/kinds/scores/scores-widget.tsx
|
|
17
18
|
/** Scores aren't problems, so no warm hues; past five scorers the colors repeat. */
|
|
@@ -22,19 +23,20 @@ var SCORER_COLORS = [
|
|
|
22
23
|
require_domains_metrics_lib_chart_colors.CHART_COLORS.teal,
|
|
23
24
|
require_domains_metrics_lib_chart_colors.CHART_COLORS.indigo
|
|
24
25
|
];
|
|
25
|
-
function ScoresWidget({ widget, views }) {
|
|
26
|
+
function ScoresWidget({ widget, views, expand }) {
|
|
26
27
|
const [view] = views;
|
|
28
|
+
const heading = require_domains_dashboards_lib_chrome.viewTitle(widget, [view], 0, () => {}, expand);
|
|
27
29
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_domains_dashboards_components_view_gate.ViewGate, {
|
|
28
30
|
queries: view.queries,
|
|
29
31
|
pending: () => null,
|
|
30
32
|
children: (page) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ScoresView, {
|
|
31
|
-
|
|
33
|
+
heading,
|
|
32
34
|
view,
|
|
33
35
|
page
|
|
34
36
|
})
|
|
35
37
|
});
|
|
36
38
|
}
|
|
37
|
-
function ScoresView({
|
|
39
|
+
function ScoresView({ heading, view, page }) {
|
|
38
40
|
const { options } = view;
|
|
39
41
|
const { interval } = require_domains_metrics_lib_metrics_buckets.bucketPlan(page.window.start, page.window.end);
|
|
40
42
|
const stepHours = require_domains_metrics_lib_metrics_buckets.intervalHours(interval);
|
|
@@ -54,7 +56,7 @@ function ScoresView({ title, view, page }) {
|
|
|
54
56
|
].filter((r) => r !== void 0);
|
|
55
57
|
const failed = all.filter((r) => r.isError);
|
|
56
58
|
if (failed.length > 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_domains_metrics_components_chart_card.ChartCard, {
|
|
57
|
-
|
|
59
|
+
...heading,
|
|
58
60
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_domains_metrics_components_chart_card_error.ChartCardError, {
|
|
59
61
|
error: failed[0]?.error,
|
|
60
62
|
onRetry: () => failed.forEach((r) => void r.refetch())
|
|
@@ -68,12 +70,13 @@ function ScoresView({ title, view, page }) {
|
|
|
68
70
|
color: SCORER_COLORS[i % SCORER_COLORS.length] ?? require_domains_metrics_lib_chart_colors.CHART_COLORS.neutral
|
|
69
71
|
}));
|
|
70
72
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_domains_metrics_components_chart_card.ChartCard, {
|
|
71
|
-
|
|
73
|
+
...heading,
|
|
72
74
|
isUpdating: all.some((r) => r.isRefetching),
|
|
73
75
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_domains_metrics_components_chart_area.ChartArea, {
|
|
74
76
|
isEmpty: !isLoading && lines.length === 0,
|
|
75
77
|
emptyMessage: options.emptyMessage,
|
|
76
78
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_MetricsLineChart.MetricsLineChart, {
|
|
79
|
+
lineStyle: "thin",
|
|
77
80
|
data: isLoading ? [] : buckets,
|
|
78
81
|
series,
|
|
79
82
|
height: "fill",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scores-widget.cjs.js","names":["CHART_COLORS","ViewGate","bucketPlan","intervalHours","useScorers","withScorer","buildRequest","useDashboardRequests","ChartCard","ChartCardError","shapeScores","ChartArea","MetricsLineChart","FORMATTERS","BUCKET_AXIS"],"sources":["../../../../../src/domains/dashboards/kinds/scores/scores-widget.tsx"],"sourcesContent":["import type { DashboardQuery } from '@mastra/client-js';\nimport { ChartArea } from '../../../metrics/components/chart-area';\nimport { ChartCard } from '../../../metrics/components/chart-card';\nimport { ChartCardError } from '../../../metrics/components/chart-card-error';\nimport { BUCKET_AXIS } from '../../../metrics/lib/chart-axis';\nimport { CHART_COLORS } from '../../../metrics/lib/chart-colors';\nimport { bucketPlan, intervalHours } from '../../../metrics/lib/metrics-buckets';\nimport { ViewGate } from '../../components/view-gate';\nimport { useDashboardRequests } from '../../hooks/use-dashboard-requests';\nimport { useScorers } from '../../hooks/use-scorers';\nimport { FORMATTERS } from '../../lib/format';\nimport { buildRequest } from '../../lib/requests';\nimport type { DashboardPageContext } from '../../lib/requests';\nimport { shapeScores, withScorer } from '../../lib/scores';\nimport type { ResolvedView, WidgetKindProps } from '../../widget-kind';\nimport type { ScoresOptions } from './options';\nimport type { MetricsLineChartSeries } from '@/ds/components/MetricsLineChart';\nimport { MetricsLineChart } from '@/ds/components/MetricsLineChart';\n\n/** Scores aren't problems, so no warm hues; past five scorers the colors repeat. */\nconst SCORER_COLORS = [\n CHART_COLORS.green,\n CHART_COLORS.sky,\n CHART_COLORS.violet,\n CHART_COLORS.teal,\n CHART_COLORS.indigo,\n];\n\nexport function ScoresWidget({ widget, views }: WidgetKindProps<ScoresOptions>) {\n const [view] = views;\n return (\n <ViewGate queries={view.queries} pending={() => null}>\n {page => <ScoresView
|
|
1
|
+
{"version":3,"file":"scores-widget.cjs.js","names":["CHART_COLORS","viewTitle","ViewGate","bucketPlan","intervalHours","useScorers","withScorer","buildRequest","useDashboardRequests","ChartCard","ChartCardError","shapeScores","ChartArea","MetricsLineChart","FORMATTERS","BUCKET_AXIS"],"sources":["../../../../../src/domains/dashboards/kinds/scores/scores-widget.tsx"],"sourcesContent":["import type { DashboardQuery } from '@mastra/client-js';\nimport { ChartArea } from '../../../metrics/components/chart-area';\nimport { ChartCard } from '../../../metrics/components/chart-card';\nimport { ChartCardError } from '../../../metrics/components/chart-card-error';\nimport { BUCKET_AXIS } from '../../../metrics/lib/chart-axis';\nimport { CHART_COLORS } from '../../../metrics/lib/chart-colors';\nimport { bucketPlan, intervalHours } from '../../../metrics/lib/metrics-buckets';\nimport { ViewGate } from '../../components/view-gate';\nimport { useDashboardRequests } from '../../hooks/use-dashboard-requests';\nimport { useScorers } from '../../hooks/use-scorers';\nimport { viewTitle } from '../../lib/chrome';\nimport type { ViewHeading } from '../../lib/chrome';\nimport { FORMATTERS } from '../../lib/format';\nimport { buildRequest } from '../../lib/requests';\nimport type { DashboardPageContext } from '../../lib/requests';\nimport { shapeScores, withScorer } from '../../lib/scores';\nimport type { ResolvedView, WidgetKindProps } from '../../widget-kind';\nimport type { ScoresOptions } from './options';\nimport type { MetricsLineChartSeries } from '@/ds/components/MetricsLineChart';\nimport { MetricsLineChart } from '@/ds/components/MetricsLineChart';\n\n/** Scores aren't problems, so no warm hues; past five scorers the colors repeat. */\nconst SCORER_COLORS = [\n CHART_COLORS.green,\n CHART_COLORS.sky,\n CHART_COLORS.violet,\n CHART_COLORS.teal,\n CHART_COLORS.indigo,\n];\n\nexport function ScoresWidget({ widget, views, expand }: WidgetKindProps<ScoresOptions>) {\n const [view] = views;\n // Scores show their first view only, so the title is plain or, with somewhere to expand to, a link.\n const heading = viewTitle(widget, [view], 0, () => {}, expand);\n return (\n <ViewGate queries={view.queries} pending={() => null}>\n {page => <ScoresView heading={heading} view={view} page={page} />}\n </ViewGate>\n );\n}\n\nfunction ScoresView({\n heading,\n view,\n page,\n}: {\n heading: ViewHeading;\n view: ResolvedView<ScoresOptions>;\n page: DashboardPageContext;\n}) {\n const { options } = view;\n const { interval } = bucketPlan(page.window.start, page.window.end);\n const stepHours = intervalHours(interval);\n const { recent, scorers } = useScorers(view.queries, page);\n const averageQuery = view.queries.find(q => q.id === options.average);\n const seriesQuery = view.queries.find(q => q.id === options.series);\n const send = (query: DashboardQuery | undefined, scorerId: string, bucket?: typeof interval) => {\n const own = withScorer(query, scorerId);\n return own && buildRequest(own, page, { interval: bucket });\n };\n // One answer per scorer in each list, in `scorers`' order.\n const means = useDashboardRequests(scorers.map(({ scorerId }) => send(averageQuery, scorerId)));\n const points = useDashboardRequests(scorers.map(({ scorerId }) => send(seriesQuery, scorerId, interval)));\n\n const all = [recent, ...means, ...points].filter(r => r !== undefined);\n const failed = all.filter(r => r.isError);\n if (failed.length > 0) {\n return (\n <ChartCard {...heading}>\n <ChartCardError error={failed[0]?.error} onRetry={() => failed.forEach(r => void r.refetch())} />\n </ChartCard>\n );\n }\n\n const isLoading = all.some(r => r.isLoading);\n const { buckets, lines } = shapeScores(\n isLoading ? [] : scorers,\n means.map(r => r?.data),\n points.map(r => r?.data),\n page.window,\n stepHours,\n );\n const series: MetricsLineChartSeries[] = lines.map(({ scorerId, name }, i) => ({\n dataKey: scorerId,\n label: name,\n color: SCORER_COLORS[i % SCORER_COLORS.length] ?? CHART_COLORS.neutral,\n }));\n\n return (\n <ChartCard {...heading} isUpdating={all.some(r => r.isRefetching)}>\n <ChartArea isEmpty={!isLoading && lines.length === 0} emptyMessage={options.emptyMessage}>\n <MetricsLineChart\n lineStyle=\"thin\"\n data={isLoading ? [] : buckets}\n series={series}\n height=\"fill\"\n yDomain={[0, 1]}\n valueFormatter={FORMATTERS.score.value}\n axisFormatter={FORMATTERS.score.axis}\n isLoading={isLoading}\n {...BUCKET_AXIS}\n />\n </ChartArea>\n </ChartCard>\n );\n}\n"],"mappings":"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"}
|
|
@@ -9,8 +9,9 @@ import { BUCKET_AXIS } from "../../../metrics/lib/chart-axis.es.js";
|
|
|
9
9
|
import { CHART_COLORS } from "../../../metrics/lib/chart-colors.es.js";
|
|
10
10
|
import { shapeScores, withScorer } from "../../lib/scores.es.js";
|
|
11
11
|
import { useScorers } from "../../hooks/use-scorers.es.js";
|
|
12
|
+
import { viewTitle } from "../../lib/chrome.es.js";
|
|
12
13
|
import { FORMATTERS } from "../../lib/format.es.js";
|
|
13
|
-
import { t as MetricsLineChart } from "../../../../MetricsLineChart-
|
|
14
|
+
import { t as MetricsLineChart } from "../../../../MetricsLineChart-4SwxBbQx.js";
|
|
14
15
|
import { jsx } from "react/jsx-runtime";
|
|
15
16
|
// src/domains/dashboards/kinds/scores/scores-widget.tsx
|
|
16
17
|
/** Scores aren't problems, so no warm hues; past five scorers the colors repeat. */
|
|
@@ -21,19 +22,20 @@ var SCORER_COLORS = [
|
|
|
21
22
|
CHART_COLORS.teal,
|
|
22
23
|
CHART_COLORS.indigo
|
|
23
24
|
];
|
|
24
|
-
function ScoresWidget({ widget, views }) {
|
|
25
|
+
function ScoresWidget({ widget, views, expand }) {
|
|
25
26
|
const [view] = views;
|
|
27
|
+
const heading = viewTitle(widget, [view], 0, () => {}, expand);
|
|
26
28
|
return /* @__PURE__ */ jsx(ViewGate, {
|
|
27
29
|
queries: view.queries,
|
|
28
30
|
pending: () => null,
|
|
29
31
|
children: (page) => /* @__PURE__ */ jsx(ScoresView, {
|
|
30
|
-
|
|
32
|
+
heading,
|
|
31
33
|
view,
|
|
32
34
|
page
|
|
33
35
|
})
|
|
34
36
|
});
|
|
35
37
|
}
|
|
36
|
-
function ScoresView({
|
|
38
|
+
function ScoresView({ heading, view, page }) {
|
|
37
39
|
const { options } = view;
|
|
38
40
|
const { interval } = bucketPlan(page.window.start, page.window.end);
|
|
39
41
|
const stepHours = intervalHours(interval);
|
|
@@ -53,7 +55,7 @@ function ScoresView({ title, view, page }) {
|
|
|
53
55
|
].filter((r) => r !== void 0);
|
|
54
56
|
const failed = all.filter((r) => r.isError);
|
|
55
57
|
if (failed.length > 0) return /* @__PURE__ */ jsx(ChartCard, {
|
|
56
|
-
|
|
58
|
+
...heading,
|
|
57
59
|
children: /* @__PURE__ */ jsx(ChartCardError, {
|
|
58
60
|
error: failed[0]?.error,
|
|
59
61
|
onRetry: () => failed.forEach((r) => void r.refetch())
|
|
@@ -67,12 +69,13 @@ function ScoresView({ title, view, page }) {
|
|
|
67
69
|
color: SCORER_COLORS[i % SCORER_COLORS.length] ?? CHART_COLORS.neutral
|
|
68
70
|
}));
|
|
69
71
|
return /* @__PURE__ */ jsx(ChartCard, {
|
|
70
|
-
|
|
72
|
+
...heading,
|
|
71
73
|
isUpdating: all.some((r) => r.isRefetching),
|
|
72
74
|
children: /* @__PURE__ */ jsx(ChartArea, {
|
|
73
75
|
isEmpty: !isLoading && lines.length === 0,
|
|
74
76
|
emptyMessage: options.emptyMessage,
|
|
75
77
|
children: /* @__PURE__ */ jsx(MetricsLineChart, {
|
|
78
|
+
lineStyle: "thin",
|
|
76
79
|
data: isLoading ? [] : buckets,
|
|
77
80
|
series,
|
|
78
81
|
height: "fill",
|