@lightdash/query-sdk 0.2675.3 → 0.2676.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/apiTransport.d.ts +17 -2
- package/dist/apiTransport.js +41 -32
- package/dist/client.d.ts +6 -17
- package/dist/client.js +23 -28
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -0
- package/dist/postMessageTransport.d.ts +41 -0
- package/dist/postMessageTransport.js +91 -0
- package/package.json +1 -1
package/dist/apiTransport.d.ts
CHANGED
|
@@ -7,5 +7,20 @@
|
|
|
7
7
|
* 2. Poll GET /api/v2/projects/{projectUuid}/query/{queryUuid}
|
|
8
8
|
* → returns results when status is 'ready'
|
|
9
9
|
*/
|
|
10
|
-
import type { LightdashClientConfig, Transport } from './types';
|
|
11
|
-
|
|
10
|
+
import type { Column, LightdashClientConfig, Transport } from './types';
|
|
11
|
+
/**
|
|
12
|
+
* A function that performs HTTP requests and returns parsed JSON results.
|
|
13
|
+
* The default implementation uses `Authorization: ApiKey` header.
|
|
14
|
+
* Supply a custom adapter to use session cookies or other auth mechanisms.
|
|
15
|
+
*/
|
|
16
|
+
export type FetchAdapter = <T>(method: string, path: string, body?: unknown) => Promise<T>;
|
|
17
|
+
export declare function mapColumnType(type: string): Column['type'];
|
|
18
|
+
/**
|
|
19
|
+
* Creates a transport that executes queries via the Lightdash REST API.
|
|
20
|
+
*
|
|
21
|
+
* @param config - Client configuration (projectUuid is required; apiKey/baseUrl
|
|
22
|
+
* are used by the default fetch adapter but ignored when a custom adapter is provided)
|
|
23
|
+
* @param adapter - Optional custom fetch adapter. When omitted, uses the default
|
|
24
|
+
* adapter that authenticates via `Authorization: ApiKey` header.
|
|
25
|
+
*/
|
|
26
|
+
export declare function createApiTransport(config: LightdashClientConfig, adapter?: FetchAdapter): Transport;
|
package/dist/apiTransport.js
CHANGED
|
@@ -33,36 +33,36 @@ function buildApiFilters(filters) {
|
|
|
33
33
|
},
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
36
|
+
function createDefaultFetchAdapter(config) {
|
|
37
|
+
return async (method, path, body) => {
|
|
38
|
+
const useProxy = config.useProxy ?? false;
|
|
39
|
+
const baseUrl = useProxy ? '' : config.baseUrl.replace(/\/$/, '');
|
|
40
|
+
const url = `${baseUrl}${path}`;
|
|
41
|
+
const res = await fetch(url, {
|
|
42
|
+
method,
|
|
43
|
+
headers: {
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
Authorization: `ApiKey ${config.apiKey}`,
|
|
46
|
+
},
|
|
47
|
+
...(body ? { body: JSON.stringify(body) } : {}),
|
|
48
|
+
});
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
const text = await res.text();
|
|
51
|
+
let message;
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(text);
|
|
54
|
+
message = parsed.error?.message ?? parsed.message ?? text;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
message = text;
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`Lightdash API error (${res.status}): ${message}`);
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return json.results;
|
|
61
|
+
const json = (await res.json());
|
|
62
|
+
return json.results;
|
|
63
|
+
};
|
|
64
64
|
}
|
|
65
|
-
function mapColumnType(type) {
|
|
65
|
+
export function mapColumnType(type) {
|
|
66
66
|
if (/timestamp/i.test(type))
|
|
67
67
|
return 'timestamp';
|
|
68
68
|
if (/date/i.test(type))
|
|
@@ -73,7 +73,16 @@ function mapColumnType(type) {
|
|
|
73
73
|
return 'boolean';
|
|
74
74
|
return 'string';
|
|
75
75
|
}
|
|
76
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Creates a transport that executes queries via the Lightdash REST API.
|
|
78
|
+
*
|
|
79
|
+
* @param config - Client configuration (projectUuid is required; apiKey/baseUrl
|
|
80
|
+
* are used by the default fetch adapter but ignored when a custom adapter is provided)
|
|
81
|
+
* @param adapter - Optional custom fetch adapter. When omitted, uses the default
|
|
82
|
+
* adapter that authenticates via `Authorization: ApiKey` header.
|
|
83
|
+
*/
|
|
84
|
+
export function createApiTransport(config, adapter) {
|
|
85
|
+
const fetchFn = adapter ?? createDefaultFetchAdapter(config);
|
|
77
86
|
return {
|
|
78
87
|
async executeQuery(query) {
|
|
79
88
|
const table = query.exploreName;
|
|
@@ -103,12 +112,12 @@ export function createApiTransport(config) {
|
|
|
103
112
|
tableCalculations: [],
|
|
104
113
|
},
|
|
105
114
|
};
|
|
106
|
-
const execResult = await
|
|
115
|
+
const execResult = await fetchFn('POST', `/api/v2/projects/${config.projectUuid}/query/metric-query`, body);
|
|
107
116
|
const { queryUuid, fields } = execResult;
|
|
108
117
|
// Step 2: Poll for results
|
|
109
118
|
let attempts = 0;
|
|
110
119
|
while (attempts < MAX_POLL_ATTEMPTS) {
|
|
111
|
-
const pollResult = await
|
|
120
|
+
const pollResult = await fetchFn('GET', `/api/v2/projects/${config.projectUuid}/query/${queryUuid}`);
|
|
112
121
|
if (pollResult.status === 'ready') {
|
|
113
122
|
// Build a mapping from qualified → short field names
|
|
114
123
|
// so app code uses row.driver_name, not row.fct_race_results_driver_name
|
|
@@ -192,7 +201,7 @@ export function createApiTransport(config) {
|
|
|
192
201
|
throw new Error('Query timed out waiting for results');
|
|
193
202
|
},
|
|
194
203
|
async getUser() {
|
|
195
|
-
const user = await
|
|
204
|
+
const user = await fetchFn('GET', '/api/v1/user');
|
|
196
205
|
return {
|
|
197
206
|
name: `${user.firstName} ${user.lastName}`.trim(),
|
|
198
207
|
email: user.email,
|
package/dist/client.d.ts
CHANGED
|
@@ -2,15 +2,7 @@
|
|
|
2
2
|
* Lightdash client.
|
|
3
3
|
*
|
|
4
4
|
* Usage:
|
|
5
|
-
*
|
|
6
|
-
* const lightdash = createClient()
|
|
7
|
-
*
|
|
8
|
-
* // Or explicit config
|
|
9
|
-
* const lightdash = createClient({
|
|
10
|
-
* apiKey: 'pat_xxx',
|
|
11
|
-
* baseUrl: 'https://app.lightdash.cloud',
|
|
12
|
-
* projectUuid: 'uuid',
|
|
13
|
-
* })
|
|
5
|
+
* const lightdash = createClient() // auto-detects from environment
|
|
14
6
|
*/
|
|
15
7
|
import { QueryBuilder } from './query';
|
|
16
8
|
import type { LightdashClientConfig, LightdashUser, Transport } from './types';
|
|
@@ -20,17 +12,14 @@ export declare class LightdashClient {
|
|
|
20
12
|
readonly auth: {
|
|
21
13
|
getUser: () => Promise<LightdashUser>;
|
|
22
14
|
};
|
|
23
|
-
constructor(config: LightdashClientConfig, transport
|
|
15
|
+
constructor(config: LightdashClientConfig, transport: Transport);
|
|
24
16
|
/** Start building a query against a model */
|
|
25
17
|
model(exploreName: string): QueryBuilder;
|
|
26
18
|
}
|
|
27
19
|
/**
|
|
28
|
-
* Create a Lightdash client.
|
|
29
|
-
*
|
|
30
|
-
* With no args, reads from env vars:
|
|
31
|
-
* const lightdash = createClient()
|
|
20
|
+
* Create a Lightdash client. Auto-detects the transport from the environment:
|
|
32
21
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
22
|
+
* 1. Hash fragment #transport=postMessage → postMessage bridge (Lightdash iframe)
|
|
23
|
+
* 2. Env vars (VITE_LIGHTDASH_* or LIGHTDASH_*) → direct API calls (local dev)
|
|
35
24
|
*/
|
|
36
|
-
export declare function createClient(
|
|
25
|
+
export declare function createClient(): LightdashClient;
|
package/dist/client.js
CHANGED
|
@@ -2,22 +2,15 @@
|
|
|
2
2
|
* Lightdash client.
|
|
3
3
|
*
|
|
4
4
|
* Usage:
|
|
5
|
-
*
|
|
6
|
-
* const lightdash = createClient()
|
|
7
|
-
*
|
|
8
|
-
* // Or explicit config
|
|
9
|
-
* const lightdash = createClient({
|
|
10
|
-
* apiKey: 'pat_xxx',
|
|
11
|
-
* baseUrl: 'https://app.lightdash.cloud',
|
|
12
|
-
* projectUuid: 'uuid',
|
|
13
|
-
* })
|
|
5
|
+
* const lightdash = createClient() // auto-detects from environment
|
|
14
6
|
*/
|
|
15
7
|
import { createApiTransport } from './apiTransport';
|
|
8
|
+
import { createPostMessageTransport } from './postMessageTransport';
|
|
16
9
|
import { QueryBuilder } from './query';
|
|
17
10
|
export class LightdashClient {
|
|
18
11
|
constructor(config, transport) {
|
|
19
12
|
this.config = config;
|
|
20
|
-
this.transport = transport
|
|
13
|
+
this.transport = transport;
|
|
21
14
|
this.auth = {
|
|
22
15
|
getUser: () => this.transport.getUser(),
|
|
23
16
|
};
|
|
@@ -31,14 +24,10 @@ export class LightdashClient {
|
|
|
31
24
|
* Resolve config from env vars.
|
|
32
25
|
*
|
|
33
26
|
* Vite (.env file, statically replaced at build time):
|
|
34
|
-
* VITE_LIGHTDASH_API_KEY
|
|
35
|
-
* VITE_LIGHTDASH_URL=https://app.lightdash.cloud
|
|
36
|
-
* VITE_LIGHTDASH_PROJECT_UUID=uuid
|
|
27
|
+
* VITE_LIGHTDASH_API_KEY, VITE_LIGHTDASH_URL, VITE_LIGHTDASH_PROJECT_UUID
|
|
37
28
|
*
|
|
38
29
|
* Node/E2B (runtime):
|
|
39
|
-
* LIGHTDASH_API_KEY
|
|
40
|
-
* LIGHTDASH_URL=https://app.lightdash.cloud
|
|
41
|
-
* LIGHTDASH_PROJECT_UUID=uuid
|
|
30
|
+
* LIGHTDASH_API_KEY, LIGHTDASH_URL, LIGHTDASH_PROJECT_UUID
|
|
42
31
|
*/
|
|
43
32
|
function configFromEnv() {
|
|
44
33
|
// Vite statically replaces import.meta.env.VITE_X at build time.
|
|
@@ -60,19 +49,25 @@ function configFromEnv() {
|
|
|
60
49
|
return { apiKey, baseUrl, projectUuid, useProxy };
|
|
61
50
|
}
|
|
62
51
|
/**
|
|
63
|
-
* Create a Lightdash client.
|
|
64
|
-
*
|
|
65
|
-
* With no args, reads from env vars:
|
|
66
|
-
* const lightdash = createClient()
|
|
52
|
+
* Create a Lightdash client. Auto-detects the transport from the environment:
|
|
67
53
|
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
54
|
+
* 1. Hash fragment #transport=postMessage → postMessage bridge (Lightdash iframe)
|
|
55
|
+
* 2. Env vars (VITE_LIGHTDASH_* or LIGHTDASH_*) → direct API calls (local dev)
|
|
70
56
|
*/
|
|
71
|
-
export function createClient(
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
|
|
57
|
+
export function createClient() {
|
|
58
|
+
// 1. postMessage transport (iframe hosted by Lightdash parent)
|
|
59
|
+
if (typeof window !== 'undefined' && window.location.hash) {
|
|
60
|
+
const params = new URLSearchParams(window.location.hash.replace(/^#/, ''));
|
|
61
|
+
if (params.get('transport') === 'postMessage') {
|
|
62
|
+
const projectUuid = params.get('projectUuid') ?? '';
|
|
63
|
+
return new LightdashClient({ apiKey: '', baseUrl: '', projectUuid }, createPostMessageTransport({ targetWindow: window.parent, projectUuid }));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// 2. Env vars → API transport
|
|
67
|
+
const config = configFromEnv();
|
|
68
|
+
if (!config) {
|
|
69
|
+
throw new Error('Missing Lightdash client config. ' +
|
|
70
|
+
'Set env vars: VITE_LIGHTDASH_API_KEY, VITE_LIGHTDASH_URL, VITE_LIGHTDASH_PROJECT_UUID');
|
|
76
71
|
}
|
|
77
|
-
return new LightdashClient(
|
|
72
|
+
return new LightdashClient(config, createApiTransport(config));
|
|
78
73
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,4 +2,7 @@ export { query } from './query';
|
|
|
2
2
|
export { createClient, LightdashClient } from './client';
|
|
3
3
|
export { useLightdash } from './useLightdash';
|
|
4
4
|
export { LightdashProvider, useLightdashClient } from './LightdashProvider';
|
|
5
|
-
export
|
|
5
|
+
export { createApiTransport, type FetchAdapter } from './apiTransport';
|
|
6
|
+
export { createPostMessageTransport } from './postMessageTransport';
|
|
7
|
+
export type { Column, Filter, FilterOperator, FilterValue, FormatFunction, LightdashClientConfig, LightdashUser, QueryDefinition, QueryResult, Row, Sort, Transport, UnitOfTime, } from './types';
|
|
8
|
+
export type { SdkFetchRequest, SdkFetchResponse, SdkReadyMessage, } from './postMessageTransport';
|
package/dist/index.js
CHANGED
|
@@ -6,3 +6,6 @@ export { createClient, LightdashClient } from './client';
|
|
|
6
6
|
export { useLightdash } from './useLightdash';
|
|
7
7
|
// Provider
|
|
8
8
|
export { LightdashProvider, useLightdashClient } from './LightdashProvider';
|
|
9
|
+
// Transports
|
|
10
|
+
export { createApiTransport } from './apiTransport';
|
|
11
|
+
export { createPostMessageTransport } from './postMessageTransport';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* postMessage transport — routes HTTP requests through the parent window
|
|
3
|
+
* via postMessage instead of calling the API directly.
|
|
4
|
+
*
|
|
5
|
+
* Used when the SDK runs inside a sandboxed iframe (sandbox="allow-scripts")
|
|
6
|
+
* that cannot make direct API calls. The parent receives fetch requests,
|
|
7
|
+
* executes them with its own session cookies, and sends the raw API
|
|
8
|
+
* response back. All query logic (field qualification, polling, result
|
|
9
|
+
* mapping) stays in the SDK's apiTransport.
|
|
10
|
+
*/
|
|
11
|
+
import type { Transport } from './types';
|
|
12
|
+
export type SdkFetchRequest = {
|
|
13
|
+
type: 'lightdash:sdk:fetch';
|
|
14
|
+
id: string;
|
|
15
|
+
method: string;
|
|
16
|
+
path: string;
|
|
17
|
+
body?: unknown;
|
|
18
|
+
};
|
|
19
|
+
export type SdkFetchResponse = {
|
|
20
|
+
type: 'lightdash:sdk:fetch-response';
|
|
21
|
+
id: string;
|
|
22
|
+
result?: unknown;
|
|
23
|
+
error?: string;
|
|
24
|
+
};
|
|
25
|
+
export type SdkReadyMessage = {
|
|
26
|
+
type: 'lightdash:sdk:ready';
|
|
27
|
+
};
|
|
28
|
+
type PostMessageTransportConfig = {
|
|
29
|
+
targetWindow: Window;
|
|
30
|
+
projectUuid: string;
|
|
31
|
+
timeoutMs?: number;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Creates a Transport that routes all API calls through the parent window
|
|
35
|
+
* via postMessage. The parent acts as a fetch proxy using session cookies.
|
|
36
|
+
*
|
|
37
|
+
* All query logic (field qualification, polling, result mapping) is handled
|
|
38
|
+
* by the SDK's apiTransport — the postMessage layer is just the HTTP adapter.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createPostMessageTransport(config: PostMessageTransportConfig): Transport;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* postMessage transport — routes HTTP requests through the parent window
|
|
3
|
+
* via postMessage instead of calling the API directly.
|
|
4
|
+
*
|
|
5
|
+
* Used when the SDK runs inside a sandboxed iframe (sandbox="allow-scripts")
|
|
6
|
+
* that cannot make direct API calls. The parent receives fetch requests,
|
|
7
|
+
* executes them with its own session cookies, and sends the raw API
|
|
8
|
+
* response back. All query logic (field qualification, polling, result
|
|
9
|
+
* mapping) stays in the SDK's apiTransport.
|
|
10
|
+
*/
|
|
11
|
+
import { createApiTransport } from './apiTransport';
|
|
12
|
+
const DEFAULT_TIMEOUT_MS = 120000;
|
|
13
|
+
const READY_TIMEOUT_MS = 10000;
|
|
14
|
+
/**
|
|
15
|
+
* Creates a FetchAdapter that sends HTTP requests to the parent window
|
|
16
|
+
* via postMessage and waits for responses.
|
|
17
|
+
*/
|
|
18
|
+
function createPostMessageFetchAdapter(config) {
|
|
19
|
+
const { targetWindow, timeoutMs = DEFAULT_TIMEOUT_MS } = config;
|
|
20
|
+
const pending = new Map();
|
|
21
|
+
let readyResolve = null;
|
|
22
|
+
const readyPromise = new Promise((resolve) => {
|
|
23
|
+
readyResolve = resolve;
|
|
24
|
+
});
|
|
25
|
+
const readyTimer = setTimeout(() => {
|
|
26
|
+
readyResolve?.();
|
|
27
|
+
}, READY_TIMEOUT_MS);
|
|
28
|
+
window.addEventListener('message', (event) => {
|
|
29
|
+
const { data } = event;
|
|
30
|
+
if (!data || typeof data !== 'object' || typeof data.type !== 'string') {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (data.type === 'lightdash:sdk:ready') {
|
|
34
|
+
clearTimeout(readyTimer);
|
|
35
|
+
readyResolve?.();
|
|
36
|
+
readyResolve = null;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (data.type === 'lightdash:sdk:fetch-response') {
|
|
40
|
+
const msg = data;
|
|
41
|
+
const req = pending.get(msg.id);
|
|
42
|
+
if (!req)
|
|
43
|
+
return;
|
|
44
|
+
clearTimeout(req.timer);
|
|
45
|
+
pending.delete(msg.id);
|
|
46
|
+
if (msg.error) {
|
|
47
|
+
req.reject(new Error(msg.error));
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
req.resolve(msg.result);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return async (method, path, body) => {
|
|
55
|
+
await readyPromise;
|
|
56
|
+
const id = crypto.randomUUID();
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const timer = setTimeout(() => {
|
|
59
|
+
pending.delete(id);
|
|
60
|
+
reject(new Error(`SDK fetch timed out after ${timeoutMs}ms: ${method} ${path}`));
|
|
61
|
+
}, timeoutMs);
|
|
62
|
+
pending.set(id, {
|
|
63
|
+
resolve: resolve,
|
|
64
|
+
reject,
|
|
65
|
+
timer,
|
|
66
|
+
});
|
|
67
|
+
const message = {
|
|
68
|
+
type: 'lightdash:sdk:fetch',
|
|
69
|
+
id,
|
|
70
|
+
method,
|
|
71
|
+
path,
|
|
72
|
+
body,
|
|
73
|
+
};
|
|
74
|
+
targetWindow.postMessage(message, '*');
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Creates a Transport that routes all API calls through the parent window
|
|
80
|
+
* via postMessage. The parent acts as a fetch proxy using session cookies.
|
|
81
|
+
*
|
|
82
|
+
* All query logic (field qualification, polling, result mapping) is handled
|
|
83
|
+
* by the SDK's apiTransport — the postMessage layer is just the HTTP adapter.
|
|
84
|
+
*/
|
|
85
|
+
export function createPostMessageTransport(config) {
|
|
86
|
+
const adapter = createPostMessageFetchAdapter({
|
|
87
|
+
targetWindow: config.targetWindow,
|
|
88
|
+
timeoutMs: config.timeoutMs,
|
|
89
|
+
});
|
|
90
|
+
return createApiTransport({ apiKey: '', baseUrl: '', projectUuid: config.projectUuid }, adapter);
|
|
91
|
+
}
|