@lightdash/query-sdk 1.142.1 → 1.144.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/LightdashProvider.d.ts +3 -0
- package/dist/LightdashProvider.js +6 -0
- package/dist/apiTransport.js +32 -0
- package/dist/features.js +6 -0
- package/dist/generated/sdkVersion.d.ts +1 -1
- package/dist/generated/sdkVersion.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +31 -0
- package/dist/types.js +9 -1
- package/dist/vizContext.d.ts +33 -0
- package/dist/vizContext.js +42 -1
- package/package.json +2 -2
|
@@ -10,6 +10,9 @@ import { type LightdashClient } from './client';
|
|
|
10
10
|
import type { Transport } from './types';
|
|
11
11
|
export declare function useTransport(): Transport;
|
|
12
12
|
export declare function useLightdashClient(): LightdashClient | null;
|
|
13
|
+
/** Non-throwing transport lookup for hooks that must keep working without a
|
|
14
|
+
* provider (`useVizContext` supports standalone self-subscription). */
|
|
15
|
+
export declare function useOptionalTransport(): Transport | null;
|
|
13
16
|
type LightdashProviderProps = {
|
|
14
17
|
children: ReactNode;
|
|
15
18
|
} & ({
|
|
@@ -20,6 +20,12 @@ export function useLightdashClient() {
|
|
|
20
20
|
const ctx = useContext(LightdashContext);
|
|
21
21
|
return ctx?.client ?? null;
|
|
22
22
|
}
|
|
23
|
+
/** Non-throwing transport lookup for hooks that must keep working without a
|
|
24
|
+
* provider (`useVizContext` supports standalone self-subscription). */
|
|
25
|
+
export function useOptionalTransport() {
|
|
26
|
+
const ctx = useContext(LightdashContext);
|
|
27
|
+
return ctx?.transport ?? null;
|
|
28
|
+
}
|
|
23
29
|
export function LightdashProvider({ children, ...props }) {
|
|
24
30
|
const client = ('client' in props ? props.client : null) ?? null;
|
|
25
31
|
const transport = client?.transport ??
|
package/dist/apiTransport.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* 2. Poll GET /api/v2/projects/{projectUuid}/query/{queryUuid}
|
|
8
8
|
* → returns results when status is 'ready'
|
|
9
9
|
*/
|
|
10
|
+
import { VIZ_UNDERLYING_DATA_PATH } from './types';
|
|
10
11
|
// Mirrors the explorer's `useInfiniteQueryResults` polling rhythm so the
|
|
11
12
|
// SDK behaves like a normal Lightdash chart: 500-row pages, exponential
|
|
12
13
|
// backoff starting at 250ms, capped at 1000ms.
|
|
@@ -653,5 +654,36 @@ export function createApiTransport(config, adapter) {
|
|
|
653
654
|
// to resolve. Only the in-iframe postMessage transport supports it.
|
|
654
655
|
throw new Error('externalFetch is only available inside a data app preview');
|
|
655
656
|
},
|
|
657
|
+
async getVizUnderlyingData(intent) {
|
|
658
|
+
const execResult = await fetchFn('POST', VIZ_UNDERLYING_DATA_PATH, intent);
|
|
659
|
+
const { firstReadyPage, apiRows } = await pollQueryRows(fetchFn, config.projectUuid, execResult.queryUuid);
|
|
660
|
+
const fieldIds = [
|
|
661
|
+
...Object.keys(execResult.fields),
|
|
662
|
+
...Object.keys(firstReadyPage.columns).filter((fieldId) => !(fieldId in execResult.fields)),
|
|
663
|
+
];
|
|
664
|
+
return {
|
|
665
|
+
...mapApiRowsToQueryResult({
|
|
666
|
+
apiRows,
|
|
667
|
+
columns: firstReadyPage.columns,
|
|
668
|
+
fields: execResult.fields,
|
|
669
|
+
fieldIds,
|
|
670
|
+
fieldNameForId: (fieldId) => fieldId,
|
|
671
|
+
}),
|
|
672
|
+
queryUuid: execResult.queryUuid,
|
|
673
|
+
};
|
|
674
|
+
},
|
|
675
|
+
async downloadVizUnderlyingData(intent, options = {}) {
|
|
676
|
+
const execResult = await fetchFn('POST', VIZ_UNDERLYING_DATA_PATH, {
|
|
677
|
+
...intent,
|
|
678
|
+
limit: getUnderlyingDownloadLimit(options.limit),
|
|
679
|
+
});
|
|
680
|
+
await pollQueryReady(fetchFn, config.projectUuid, execResult.queryUuid, 1);
|
|
681
|
+
return scheduleDownloadForQuery({
|
|
682
|
+
fetchFn,
|
|
683
|
+
projectUuid: config.projectUuid,
|
|
684
|
+
queryUuid: execResult.queryUuid,
|
|
685
|
+
options,
|
|
686
|
+
});
|
|
687
|
+
},
|
|
656
688
|
};
|
|
657
689
|
}
|
package/dist/features.js
CHANGED
|
@@ -83,6 +83,12 @@ export const SDK_FEATURES = [
|
|
|
83
83
|
description: "Scheduled deliveries and their preview render every tab or slide's data, not just the one currently visible.",
|
|
84
84
|
wiring: "Gate tab/slide content so DATA components for all tabs mount when useDeliveryRender() is true — tabs switch what's shown, not what's fetched. Never mount all tabs unconditionally.",
|
|
85
85
|
},
|
|
86
|
+
{
|
|
87
|
+
key: 'viz-underlying-data',
|
|
88
|
+
label: 'View underlying data',
|
|
89
|
+
description: 'Open the raw result rows behind a clicked data point in a reusable visualization, with CSV/XLSX download.',
|
|
90
|
+
wiring: 'In the viz, keep the untransformed source row on each interactive datum, show a data-point action menu only when useVizContext().underlyingData.enabled and the mark maps to exactly one source row, render underlyingData.get({ row, metric }) in a themed dialog, and wire its Download button to underlyingData.download.',
|
|
91
|
+
},
|
|
86
92
|
];
|
|
87
93
|
export const SDK_FEATURE_KEYS = SDK_FEATURES.map((f) => f.key);
|
|
88
94
|
export const SDK_MANIFEST_MESSAGE_TYPE = 'lightdash:sdk:manifest';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "1.
|
|
1
|
+
export declare const SDK_VERSION = "1.144.1";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by scripts/generateSdkVersion.mjs (prebuild) — do not edit.
|
|
2
|
-
export const SDK_VERSION = '1.
|
|
2
|
+
export const SDK_VERSION = '1.144.1';
|
package/dist/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export type { DeliveryQuery } from './delivery';
|
|
|
15
15
|
export { exportToSheets } from './exportToSheets';
|
|
16
16
|
export type { ExportToSheetsOptions, ExportToSheetsResult, } from './exportToSheets';
|
|
17
17
|
export { VizContextProvider, useVizContext, getFormatted, getRaw, } from './vizContext';
|
|
18
|
-
export type { VizContext, VizContextCell, VizContextOptionValue, VizContextRow, DataAppVizContextMessage, VizContextRequestMessage, } from './vizContext';
|
|
18
|
+
export type { VizContext, VizContextCell, VizContextOptionValue, VizContextRow, VizUnderlyingData, DataAppVizContextMessage, VizContextRequestMessage, } from './vizContext';
|
|
19
19
|
export { useColorScheme } from './colorScheme';
|
|
20
20
|
export type { HostColorScheme, HostColorSchemeMessage, HostColorSchemeRequestMessage, } from './colorScheme';
|
|
21
21
|
export { isDeliveryRender, useDeliveryRender } from './deliveryRender';
|
package/dist/types.d.ts
CHANGED
|
@@ -162,6 +162,29 @@ export type UnderlyingDataResult = {
|
|
|
162
162
|
format: FormatFunction;
|
|
163
163
|
queryUuid: string;
|
|
164
164
|
};
|
|
165
|
+
/**
|
|
166
|
+
* Bridge-only virtual route for viz underlying-data click intents. Duplicated
|
|
167
|
+
* from `@lightdash/common` (`APP_SDK_VIZ_UNDERLYING_DATA_PATH`) — this package
|
|
168
|
+
* must not depend on common. The path only resolves behind the host's
|
|
169
|
+
* postMessage bridge, which rewrites it into the real underlying-data request;
|
|
170
|
+
* on a direct-API transport it fails with a plain HTTP error.
|
|
171
|
+
*/
|
|
172
|
+
export declare const VIZ_UNDERLYING_DATA_PATH = "/__sdk/viz/underlying-data";
|
|
173
|
+
/**
|
|
174
|
+
* Semantic click intent a viz sends to the host: the untransformed source row
|
|
175
|
+
* (as received from `useVizContext().rows`) and the declared field NAME bound
|
|
176
|
+
* to the clicked metric slot. The host resolves everything else.
|
|
177
|
+
*/
|
|
178
|
+
export type VizUnderlyingDataIntent = {
|
|
179
|
+
row: Record<string, {
|
|
180
|
+
value?: {
|
|
181
|
+
raw?: unknown;
|
|
182
|
+
formatted?: string;
|
|
183
|
+
};
|
|
184
|
+
} | undefined>;
|
|
185
|
+
metric: string;
|
|
186
|
+
limit?: number | null;
|
|
187
|
+
};
|
|
165
188
|
export type LightdashClientConfig = {
|
|
166
189
|
/** Lightdash instance URL */
|
|
167
190
|
baseUrl: string;
|
|
@@ -223,4 +246,12 @@ export type Transport = {
|
|
|
223
246
|
}) => Promise<QueryResult>;
|
|
224
247
|
getUser: () => Promise<LightdashUser>;
|
|
225
248
|
externalFetch: (alias: string, opts: ExternalFetchOptions) => Promise<ExternalFetchResult>;
|
|
249
|
+
/**
|
|
250
|
+
* Fetch the raw rows behind a viz data point via the host bridge.
|
|
251
|
+
* Optional so custom transports predating the capability stay valid —
|
|
252
|
+
* `useVizContext().underlyingData.enabled` is false when absent.
|
|
253
|
+
*/
|
|
254
|
+
getVizUnderlyingData?: (intent: VizUnderlyingDataIntent) => Promise<UnderlyingDataResult>;
|
|
255
|
+
/** Schedule a CSV/XLSX export of the rows behind a viz data point. */
|
|
256
|
+
downloadVizUnderlyingData?: (intent: Omit<VizUnderlyingDataIntent, 'limit'>, options?: DownloadResultsOptions) => Promise<DownloadResultsResult>;
|
|
226
257
|
};
|
package/dist/types.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Core types for the Lightdash SDK.
|
|
3
3
|
*/
|
|
4
|
-
|
|
4
|
+
// --- Viz underlying data ---
|
|
5
|
+
/**
|
|
6
|
+
* Bridge-only virtual route for viz underlying-data click intents. Duplicated
|
|
7
|
+
* from `@lightdash/common` (`APP_SDK_VIZ_UNDERLYING_DATA_PATH`) — this package
|
|
8
|
+
* must not depend on common. The path only resolves behind the host's
|
|
9
|
+
* postMessage bridge, which rewrites it into the real underlying-data request;
|
|
10
|
+
* on a direct-API transport it fails with a plain HTTP error.
|
|
11
|
+
*/
|
|
12
|
+
export const VIZ_UNDERLYING_DATA_PATH = '/__sdk/viz/underlying-data';
|
package/dist/vizContext.d.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* host's reply therefore can't be missed. No timers, no races.
|
|
15
15
|
*/
|
|
16
16
|
import { type ReactNode } from 'react';
|
|
17
|
+
import type { DownloadResultsOptions, DownloadResultsResult, Transport, UnderlyingDataResult } from './types';
|
|
17
18
|
/** A single cell of a Lightdash result row: `{ value: { raw, formatted } }`. */
|
|
18
19
|
export type VizContextCell = {
|
|
19
20
|
value?: {
|
|
@@ -45,6 +46,10 @@ export type DataAppVizContextMessage = {
|
|
|
45
46
|
options?: Record<string, VizContextOptionValue>;
|
|
46
47
|
/** Absent when the installed host predates palette delivery. */
|
|
47
48
|
colorPalette?: string[];
|
|
49
|
+
/** Absent when the installed host predates underlying-data delivery. */
|
|
50
|
+
underlyingData?: {
|
|
51
|
+
enabled?: boolean;
|
|
52
|
+
};
|
|
48
53
|
};
|
|
49
54
|
/** Posted by the iframe on mount so the host pushes the current context. */
|
|
50
55
|
export type VizContextRequestMessage = {
|
|
@@ -54,6 +59,25 @@ export type VizContextRequestMessage = {
|
|
|
54
59
|
export declare const getFormatted: (row: VizContextRow | undefined, fieldId: string | undefined) => string;
|
|
55
60
|
/** Raw value for a field's cell in a row (number/string/etc.), or null when unset. */
|
|
56
61
|
export declare const getRaw: (row: VizContextRow | undefined, fieldId: string | undefined) => unknown;
|
|
62
|
+
/**
|
|
63
|
+
* Host-mediated access to the raw rows behind a clicked data point. `enabled`
|
|
64
|
+
* is false when the host predates the capability, the viewer lacks permission,
|
|
65
|
+
* or no transport is mounted — render no menu item in that case (never a
|
|
66
|
+
* disabled one). `row` is the untransformed source row from `rows`; `metric`
|
|
67
|
+
* is the declared field NAME bound to the clicked metric slot.
|
|
68
|
+
*/
|
|
69
|
+
export type VizUnderlyingData = {
|
|
70
|
+
enabled: boolean;
|
|
71
|
+
get: (opts: {
|
|
72
|
+
row: VizContextRow;
|
|
73
|
+
metric: string;
|
|
74
|
+
limit?: number;
|
|
75
|
+
}) => Promise<UnderlyingDataResult>;
|
|
76
|
+
download: (opts: {
|
|
77
|
+
row: VizContextRow;
|
|
78
|
+
metric: string;
|
|
79
|
+
} & DownloadResultsOptions) => Promise<DownloadResultsResult>;
|
|
80
|
+
};
|
|
57
81
|
export type VizContext = {
|
|
58
82
|
/** field name → query field id, as bound in the host field mapping UI. */
|
|
59
83
|
fieldMapping: Record<string, string>;
|
|
@@ -70,12 +94,15 @@ export type VizContext = {
|
|
|
70
94
|
colorPalette: string[];
|
|
71
95
|
/** False until the first context arrives — render a placeholder while false. */
|
|
72
96
|
ready: boolean;
|
|
97
|
+
/** Fetch/export the raw rows behind a clicked data point via the host. */
|
|
98
|
+
underlyingData: VizUnderlyingData;
|
|
73
99
|
};
|
|
74
100
|
type VizContextValue = {
|
|
75
101
|
fieldMapping: Record<string, string>;
|
|
76
102
|
rows: VizContextRow[];
|
|
77
103
|
options: Record<string, VizContextOptionValue>;
|
|
78
104
|
colorPalette: string[];
|
|
105
|
+
underlyingDataEnabled: boolean;
|
|
79
106
|
};
|
|
80
107
|
type VizContextState = VizContextValue | null;
|
|
81
108
|
/**
|
|
@@ -85,6 +112,12 @@ type VizContextState = VizContextValue | null;
|
|
|
85
112
|
* `{}` / `[]`.
|
|
86
113
|
*/
|
|
87
114
|
export declare function toVizContextState(message: DataAppVizContextMessage): VizContextValue;
|
|
115
|
+
/**
|
|
116
|
+
* Builds the `underlyingData` surface from the host's availability flag and
|
|
117
|
+
* the mounted transport (null when no `LightdashProvider` is present, e.g.
|
|
118
|
+
* standalone `useVizContext` usage). Exported for tests.
|
|
119
|
+
*/
|
|
120
|
+
export declare function buildVizUnderlyingData(hostEnabled: boolean, transport: Transport | null): VizUnderlyingData;
|
|
88
121
|
declare const NO_PROVIDER: unique symbol;
|
|
89
122
|
/**
|
|
90
123
|
* Owns the single listener + handshake for a data app viz. Mount it in the
|
package/dist/vizContext.js
CHANGED
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
* request is guaranteed to be sent after any listener the app registered. The
|
|
14
14
|
* host's reply therefore can't be missed. No timers, no races.
|
|
15
15
|
*/
|
|
16
|
-
import { createContext, createElement, useContext, useEffect, useState, } from 'react';
|
|
16
|
+
import { createContext, createElement, useContext, useEffect, useMemo, useState, } from 'react';
|
|
17
|
+
import { useOptionalTransport } from './LightdashProvider';
|
|
17
18
|
const DATA_APP_VIZ_CONTEXT_MESSAGE = 'lightdash:sdk:data-app-viz-context';
|
|
18
19
|
const VIZ_CONTEXT_REQUEST_MESSAGE = 'lightdash:sdk:viz-context-request';
|
|
19
20
|
/** Display string for a field's cell in a row, e.g. `"$1,234"`. Empty when unset. */
|
|
@@ -53,6 +54,40 @@ export function toVizContextState(message) {
|
|
|
53
54
|
colorPalette: Array.isArray(message.colorPalette)
|
|
54
55
|
? message.colorPalette.filter((color) => typeof color === 'string')
|
|
55
56
|
: [],
|
|
57
|
+
// Strict boolean check — non-boolean payloads read as disabled.
|
|
58
|
+
underlyingDataEnabled: message.underlyingData?.enabled === true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Builds the `underlyingData` surface from the host's availability flag and
|
|
63
|
+
* the mounted transport (null when no `LightdashProvider` is present, e.g.
|
|
64
|
+
* standalone `useVizContext` usage). Exported for tests.
|
|
65
|
+
*/
|
|
66
|
+
export function buildVizUnderlyingData(hostEnabled, transport) {
|
|
67
|
+
// Atomic capability: the generated menu promises Download whenever
|
|
68
|
+
// `enabled` is true, so a transport must implement both methods.
|
|
69
|
+
const supported = typeof transport?.getVizUnderlyingData === 'function' &&
|
|
70
|
+
typeof transport?.downloadVizUnderlyingData === 'function';
|
|
71
|
+
return {
|
|
72
|
+
enabled: hostEnabled && supported,
|
|
73
|
+
get: async ({ row, metric, limit }) => {
|
|
74
|
+
if (!hostEnabled) {
|
|
75
|
+
throw new Error('Underlying data is not enabled for this visualization.');
|
|
76
|
+
}
|
|
77
|
+
if (!transport?.getVizUnderlyingData) {
|
|
78
|
+
throw new Error('This SDK build predates underlying data. Rebuild the app on the current template.');
|
|
79
|
+
}
|
|
80
|
+
return transport.getVizUnderlyingData({ row, metric, limit });
|
|
81
|
+
},
|
|
82
|
+
download: async ({ row, metric, ...options }) => {
|
|
83
|
+
if (!hostEnabled) {
|
|
84
|
+
throw new Error('Underlying data is not enabled for this visualization.');
|
|
85
|
+
}
|
|
86
|
+
if (!transport?.downloadVizUnderlyingData) {
|
|
87
|
+
throw new Error('This SDK build predates underlying data. Rebuild the app on the current template.');
|
|
88
|
+
}
|
|
89
|
+
return transport.downloadVizUnderlyingData({ row, metric }, options);
|
|
90
|
+
},
|
|
56
91
|
};
|
|
57
92
|
}
|
|
58
93
|
// Distinguishes "no provider mounted" from "provider present, no context yet".
|
|
@@ -113,11 +148,17 @@ export function useVizContext() {
|
|
|
113
148
|
const context = hasProvider
|
|
114
149
|
? fromProvider
|
|
115
150
|
: selfSubscribed;
|
|
151
|
+
// Null-returning lookup — standalone usage without LightdashProvider keeps
|
|
152
|
+
// working, with underlying data reported as unavailable.
|
|
153
|
+
const transport = useOptionalTransport();
|
|
154
|
+
const hostEnabled = context?.underlyingDataEnabled === true;
|
|
155
|
+
const underlyingData = useMemo(() => buildVizUnderlyingData(hostEnabled, transport), [hostEnabled, transport]);
|
|
116
156
|
return {
|
|
117
157
|
fieldMapping: context?.fieldMapping ?? {},
|
|
118
158
|
rows: context?.rows ?? [],
|
|
119
159
|
options: context?.options ?? {},
|
|
120
160
|
colorPalette: context?.colorPalette ?? [],
|
|
121
161
|
ready: context !== null,
|
|
162
|
+
underlyingData,
|
|
122
163
|
};
|
|
123
164
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightdash/query-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.144.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "SDK for building custom data apps against the Lightdash semantic layer",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"jsdom": "26.1.0",
|
|
35
35
|
"typescript": "7.0.2",
|
|
36
36
|
"vitest": "4.1.6",
|
|
37
|
-
"@lightdash/common": "1.
|
|
37
|
+
"@lightdash/common": "1.144.1"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"prebuild": "node ./scripts/generateSdkVersion.mjs",
|