@querypanel/node-sdk 1.0.55 → 1.0.57

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/index.js CHANGED
@@ -326,29 +326,31 @@ var PostgresAdapter = class {
326
326
  }
327
327
  }
328
328
  /**
329
- * Convert named params to positional array for PostgreSQL
330
- * PostgreSQL expects $1, $2, $3 in SQL and an array of values [val1, val2, val3]
329
+ * Convert params to positional array for PostgreSQL.
330
+ * PostgreSQL expects $1, $2, $3 in SQL and an array of values [val1, val2, val3].
331
+ * When the record has numeric keys ("1", "2", "3") — e.g. from mapGeneratedParams —
332
+ * use only those in order so placeholder order matches SQL. Otherwise fall back to
333
+ * named keys in alphabetical order for backward compatibility.
331
334
  */
332
335
  convertNamedToPositionalParams(params) {
333
336
  const numericKeys = Object.keys(params).filter((k) => /^\d+$/.test(k)).map((k) => Number.parseInt(k, 10)).sort((a, b) => a - b);
334
- const namedKeys = Object.keys(params).filter((k) => !/^\d+$/.test(k)).sort();
335
- const positionalParams = [];
336
- for (const key of numericKeys) {
337
- let val = params[String(key)];
338
- if (typeof val === "string") {
339
- const match = val.match(/^<([a-zA-Z0-9_]+)>$/);
340
- const namedKey = match?.[1];
341
- if (namedKey && namedKey in params) {
342
- val = params[namedKey];
337
+ if (numericKeys.length > 0) {
338
+ const positionalParams = [];
339
+ for (const key of numericKeys) {
340
+ let val = params[String(key)];
341
+ if (typeof val === "string") {
342
+ const match = val.match(/^<([a-zA-Z0-9_]+)>$/);
343
+ const namedKey = match?.[1];
344
+ if (namedKey && namedKey in params) {
345
+ val = params[namedKey];
346
+ }
343
347
  }
348
+ positionalParams.push(val);
344
349
  }
345
- positionalParams.push(val);
346
- }
347
- for (const key of namedKeys) {
348
- const val = params[key];
349
- positionalParams.push(val);
350
+ return positionalParams;
350
351
  }
351
- return positionalParams;
352
+ const namedKeys = Object.keys(params).filter((k) => !/^\d+$/.test(k)).sort();
353
+ return namedKeys.map((key) => params[key]);
352
354
  }
353
355
  async validate(sql, params) {
354
356
  let paramArray;
@@ -560,6 +562,13 @@ var ApiClient = class {
560
562
  getDefaultTenantId() {
561
563
  return this.defaultTenantId;
562
564
  }
565
+ /**
566
+ * Create a JWT for the given tenant (and optional userId, scopes).
567
+ * Use this when you need to pass a token to the embed (e.g. frontend or demo).
568
+ */
569
+ async createJwt(tenantId, userId, scopes) {
570
+ return await this.generateJWT(tenantId, userId, scopes);
571
+ }
563
572
  async get(path, tenantId, userId, scopes, signal, sessionId) {
564
573
  return await this.request(path, {
565
574
  method: "GET",
@@ -772,6 +781,111 @@ var ApiClient = class {
772
781
  }
773
782
  };
774
783
 
784
+ // src/core/callback-client.ts
785
+ var CallbackApiClient = class {
786
+ requestHandler;
787
+ defaultTenantId;
788
+ constructor(requestHandler, options) {
789
+ this.requestHandler = requestHandler;
790
+ this.defaultTenantId = options?.defaultTenantId;
791
+ }
792
+ getDefaultTenantId() {
793
+ return this.defaultTenantId;
794
+ }
795
+ async request(opts) {
796
+ const { data, headers } = await this.requestHandler({
797
+ method: opts.method,
798
+ path: opts.path,
799
+ body: opts.body,
800
+ tenantId: opts.tenantId,
801
+ userId: opts.userId,
802
+ scopes: opts.scopes,
803
+ signal: opts.signal,
804
+ sessionId: opts.sessionId
805
+ });
806
+ return { data, headers };
807
+ }
808
+ async get(path, tenantId, userId, scopes, signal, sessionId) {
809
+ const { data } = await this.request({
810
+ method: "GET",
811
+ path,
812
+ tenantId,
813
+ userId,
814
+ scopes,
815
+ signal,
816
+ sessionId
817
+ });
818
+ return data;
819
+ }
820
+ async post(path, body, tenantId, userId, scopes, signal, sessionId) {
821
+ const { data } = await this.request({
822
+ method: "POST",
823
+ path,
824
+ body,
825
+ tenantId,
826
+ userId,
827
+ scopes,
828
+ signal,
829
+ sessionId
830
+ });
831
+ return data;
832
+ }
833
+ async postWithHeaders(path, body, tenantId, userId, scopes, signal, sessionId) {
834
+ const { data, headers } = await this.request({
835
+ method: "POST",
836
+ path,
837
+ body,
838
+ tenantId,
839
+ userId,
840
+ scopes,
841
+ signal,
842
+ sessionId
843
+ });
844
+ return {
845
+ data,
846
+ headers: headers ?? new Headers()
847
+ };
848
+ }
849
+ async put(path, body, tenantId, userId, scopes, signal, sessionId) {
850
+ const { data } = await this.request({
851
+ method: "PUT",
852
+ path,
853
+ body,
854
+ tenantId,
855
+ userId,
856
+ scopes,
857
+ signal,
858
+ sessionId
859
+ });
860
+ return data;
861
+ }
862
+ async patch(path, body, tenantId, userId, scopes, signal, sessionId) {
863
+ const { data } = await this.request({
864
+ method: "PATCH",
865
+ path,
866
+ body,
867
+ tenantId,
868
+ userId,
869
+ scopes,
870
+ signal,
871
+ sessionId
872
+ });
873
+ return data;
874
+ }
875
+ async delete(path, tenantId, userId, scopes, signal, sessionId) {
876
+ const { data } = await this.request({
877
+ method: "DELETE",
878
+ path,
879
+ tenantId,
880
+ userId,
881
+ scopes,
882
+ signal,
883
+ sessionId
884
+ });
885
+ return data;
886
+ }
887
+ };
888
+
775
889
  // src/core/query-engine.ts
776
890
  var QueryEngine = class {
777
891
  databases = /* @__PURE__ */ new Map();
@@ -834,6 +948,12 @@ var QueryEngine = class {
834
948
  return [];
835
949
  }
836
950
  }
951
+ /**
952
+ * Maps API params (array in SQL placeholder order $1, $2, $3) to ParamRecord.
953
+ * Sets both numeric keys ("1", "2", "3") and named keys so that:
954
+ * - Postgres adapter uses numeric order for positional params (matches SQL).
955
+ * - Named keys remain for tenant isolation / other lookups.
956
+ */
837
957
  mapGeneratedParams(params) {
838
958
  const record = {};
839
959
  params.forEach((param, index) => {
@@ -841,6 +961,7 @@ var QueryEngine = class {
841
961
  if (value === void 0) {
842
962
  return;
843
963
  }
964
+ record[String(index + 1)] = value;
844
965
  const nameCandidate = typeof param.name === "string" && param.name.trim() || typeof param.placeholder === "string" && param.placeholder.trim() || typeof param.position === "number" && String(param.position) || String(index + 1);
845
966
  const key = nameCandidate.replace(/[{}:$]/g, "").trim();
846
967
  record[key] = value;
@@ -1169,10 +1290,161 @@ function resolveTenantId2(client, tenantId) {
1169
1290
  return resolved;
1170
1291
  }
1171
1292
 
1293
+ // src/routes/dashboards.ts
1294
+ function resolveTenantId3(client, tenantId) {
1295
+ const resolved = tenantId ?? client.getDefaultTenantId();
1296
+ if (resolved === void 0) {
1297
+ throw new Error("tenantId is required (option or client default)");
1298
+ }
1299
+ return resolved;
1300
+ }
1301
+ async function createDashboard(client, body, options, signal) {
1302
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1303
+ return await client.post(
1304
+ "/dashboards",
1305
+ body,
1306
+ tenantId,
1307
+ options?.userId,
1308
+ options?.scopes,
1309
+ signal
1310
+ );
1311
+ }
1312
+ async function listDashboards(client, options, signal) {
1313
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1314
+ const params = new URLSearchParams();
1315
+ if (options?.pagination?.page) {
1316
+ params.set("page", options.pagination.page.toString());
1317
+ }
1318
+ if (options?.pagination?.limit) {
1319
+ params.set("limit", options.pagination.limit.toString());
1320
+ }
1321
+ if (options?.status) {
1322
+ params.set("status", options.status);
1323
+ }
1324
+ if (options?.sortBy) {
1325
+ params.set("sort_by", options.sortBy);
1326
+ }
1327
+ if (options?.sortDir) {
1328
+ params.set("sort_dir", options.sortDir);
1329
+ }
1330
+ return await client.get(
1331
+ `/dashboards?${params.toString()}`,
1332
+ tenantId,
1333
+ options?.userId,
1334
+ options?.scopes,
1335
+ signal
1336
+ );
1337
+ }
1338
+ async function getDashboard(client, id, options, signal) {
1339
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1340
+ return await client.get(
1341
+ `/dashboards/${id}`,
1342
+ tenantId,
1343
+ options?.userId,
1344
+ options?.scopes,
1345
+ signal
1346
+ );
1347
+ }
1348
+ async function getDashboardForTenant(client, id, tenantId, options, signal) {
1349
+ const params = new URLSearchParams();
1350
+ params.set("tenant_id", tenantId);
1351
+ return await client.get(
1352
+ `/dashboards/${id}/for-tenant?${params.toString()}`,
1353
+ tenantId,
1354
+ options?.userId,
1355
+ options?.scopes,
1356
+ signal
1357
+ );
1358
+ }
1359
+ async function updateDashboard(client, id, body, options, signal) {
1360
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1361
+ return await client.put(
1362
+ `/dashboards/${id}`,
1363
+ body,
1364
+ tenantId,
1365
+ options?.userId,
1366
+ options?.scopes,
1367
+ signal
1368
+ );
1369
+ }
1370
+ async function updateDashboardStatus(client, id, status, options, signal) {
1371
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1372
+ return await client.patch(
1373
+ `/dashboards/${id}/status`,
1374
+ { status },
1375
+ tenantId,
1376
+ options?.userId,
1377
+ options?.scopes,
1378
+ signal
1379
+ );
1380
+ }
1381
+ async function deleteDashboard(client, id, options, signal) {
1382
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1383
+ await client.delete(
1384
+ `/dashboards/${id}`,
1385
+ tenantId,
1386
+ options?.userId,
1387
+ options?.scopes,
1388
+ signal
1389
+ );
1390
+ }
1391
+ async function forkDashboard(client, id, input, options, signal) {
1392
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1393
+ return await client.post(
1394
+ `/dashboards/${id}/fork`,
1395
+ input,
1396
+ tenantId,
1397
+ options?.userId,
1398
+ options?.scopes,
1399
+ signal
1400
+ );
1401
+ }
1402
+ async function updateFork(client, forkId, input, options, signal) {
1403
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1404
+ return await client.put(
1405
+ `/dashboards/forks/${forkId}`,
1406
+ input,
1407
+ tenantId,
1408
+ options?.userId,
1409
+ options?.scopes,
1410
+ signal
1411
+ );
1412
+ }
1413
+ async function rollbackFork(client, forkId, tenantId, options, signal) {
1414
+ return await client.post(
1415
+ `/dashboards/forks/${forkId}/rollback`,
1416
+ { tenant_id: tenantId },
1417
+ tenantId,
1418
+ options?.userId,
1419
+ options?.scopes,
1420
+ signal
1421
+ );
1422
+ }
1423
+ async function deleteFork(client, forkId, tenantId, options, signal) {
1424
+ const params = new URLSearchParams();
1425
+ params.set("tenant_id", tenantId);
1426
+ await client.delete(
1427
+ `/dashboards/forks/${forkId}?${params.toString()}`,
1428
+ tenantId,
1429
+ options?.userId,
1430
+ options?.scopes,
1431
+ signal
1432
+ );
1433
+ }
1434
+ async function listForksForTenant(client, tenantId, options, signal) {
1435
+ return await client.get(
1436
+ `/dashboards/customer/${tenantId}`,
1437
+ tenantId,
1438
+ options?.userId,
1439
+ options?.scopes,
1440
+ signal
1441
+ );
1442
+ }
1443
+
1172
1444
  // src/routes/ingest.ts
1173
1445
  import crypto2 from "crypto";
1174
1446
  async function syncSchema(client, queryEngine, databaseName, options, signal) {
1175
- const tenantId = resolveTenantId3(client, options.tenantId);
1447
+ const tenantId = resolveTenantId4(client, options.tenantId);
1176
1448
  const adapter = queryEngine.getDatabase(databaseName);
1177
1449
  const metadata = queryEngine.getDatabaseMetadata(databaseName);
1178
1450
  const introspection = await adapter.introspect(
@@ -1194,7 +1466,7 @@ async function syncSchema(client, queryEngine, databaseName, options, signal) {
1194
1466
  );
1195
1467
  return response;
1196
1468
  }
1197
- function resolveTenantId3(client, tenantId) {
1469
+ function resolveTenantId4(client, tenantId) {
1198
1470
  const resolved = tenantId ?? client.getDefaultTenantId();
1199
1471
  if (!resolved) {
1200
1472
  throw new Error(
@@ -1236,7 +1508,7 @@ import crypto4 from "crypto";
1236
1508
  // src/routes/query.ts
1237
1509
  import crypto3 from "crypto";
1238
1510
  async function ask(client, queryEngine, question, options, signal) {
1239
- const tenantId = resolveTenantId4(client, options.tenantId);
1511
+ const tenantId = resolveTenantId5(client, options.tenantId);
1240
1512
  const sessionId = crypto3.randomUUID();
1241
1513
  const querypanelSessionId = options.querypanelSessionId ?? sessionId;
1242
1514
  const maxRetry = options.maxRetry ?? 0;
@@ -1391,7 +1663,7 @@ async function ask(client, queryEngine, question, options, signal) {
1391
1663
  }
1392
1664
  throw new Error("Unexpected error in ask retry loop");
1393
1665
  }
1394
- function resolveTenantId4(client, tenantId) {
1666
+ function resolveTenantId5(client, tenantId) {
1395
1667
  const resolved = tenantId ?? client.getDefaultTenantId();
1396
1668
  if (!resolved) {
1397
1669
  throw new Error(
@@ -1414,7 +1686,7 @@ function anonymizeResults(rows) {
1414
1686
  }
1415
1687
 
1416
1688
  // src/routes/modify.ts
1417
- function buildModifiedQuestion(originalQuestion, modifications, pipeline) {
1689
+ function buildModifiedQuestion(originalQuestion, modifications, pipeline, hasSessionContext = false) {
1418
1690
  const hints = [];
1419
1691
  if (modifications.timeGranularity) {
1420
1692
  hints.push(`group results by ${modifications.timeGranularity}`);
@@ -1449,7 +1721,7 @@ function buildModifiedQuestion(originalQuestion, modifications, pipeline) {
1449
1721
  if (hints.length === 0) {
1450
1722
  return originalQuestion;
1451
1723
  }
1452
- if (pipeline === "v2") {
1724
+ if (pipeline === "v2" && hasSessionContext) {
1453
1725
  return hints.join(", ");
1454
1726
  }
1455
1727
  return `${originalQuestion} (${hints.join(", ")})`;
@@ -1547,7 +1819,7 @@ function stripVizSpecOnlyHints(hints) {
1547
1819
  const { kind: _kind, ...rest } = hints;
1548
1820
  return rest;
1549
1821
  }
1550
- function resolveTenantId5(client, tenantId) {
1822
+ function resolveTenantId6(client, tenantId) {
1551
1823
  const resolved = tenantId ?? client.getDefaultTenantId();
1552
1824
  if (!resolved) {
1553
1825
  throw new Error(
@@ -1557,9 +1829,10 @@ function resolveTenantId5(client, tenantId) {
1557
1829
  return resolved;
1558
1830
  }
1559
1831
  async function modifyChart(client, queryEngine, input, options, signal) {
1560
- const tenantId = resolveTenantId5(client, options?.tenantId);
1832
+ const tenantId = resolveTenantId6(client, options?.tenantId);
1561
1833
  const sessionId = crypto4.randomUUID();
1562
1834
  const querypanelSessionId = options?.querypanelSessionId ?? sessionId;
1835
+ const hasQuerypanelSessionContext = !!options?.querypanelSessionId;
1563
1836
  const chartType = options?.chartType ?? "vega-lite";
1564
1837
  const hasSqlMods = !!input.sqlModifications;
1565
1838
  const hasVizMods = !!input.vizModifications;
@@ -1596,7 +1869,8 @@ async function modifyChart(client, queryEngine, input, options, signal) {
1596
1869
  const modifiedQuestion = buildModifiedQuestion(
1597
1870
  input.question,
1598
1871
  input.sqlModifications,
1599
- options?.pipeline
1872
+ options?.pipeline,
1873
+ hasQuerypanelSessionContext
1600
1874
  );
1601
1875
  if (options?.pipeline === "v2") {
1602
1876
  finalQuestion = modifiedQuestion;
@@ -1723,7 +1997,7 @@ async function modifyChart(client, queryEngine, input, options, signal) {
1723
1997
 
1724
1998
  // src/routes/sessions.ts
1725
1999
  async function listSessions(client, options, signal) {
1726
- const tenantId = resolveTenantId6(client, options?.tenantId);
2000
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1727
2001
  const params = new URLSearchParams();
1728
2002
  if (options?.pagination?.page)
1729
2003
  params.set("page", `${options.pagination.page}`);
@@ -1746,7 +2020,7 @@ async function listSessions(client, options, signal) {
1746
2020
  );
1747
2021
  }
1748
2022
  async function getSession(client, sessionId, options, signal) {
1749
- const tenantId = resolveTenantId6(client, options?.tenantId);
2023
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1750
2024
  const params = new URLSearchParams();
1751
2025
  if (options?.includeTurns !== void 0) {
1752
2026
  params.set("include_turns", `${options.includeTurns}`);
@@ -1760,7 +2034,7 @@ async function getSession(client, sessionId, options, signal) {
1760
2034
  );
1761
2035
  }
1762
2036
  async function updateSession(client, sessionId, body, options, signal) {
1763
- const tenantId = resolveTenantId6(client, options?.tenantId);
2037
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1764
2038
  return await client.patch(
1765
2039
  `/sessions/${encodeURIComponent(sessionId)}`,
1766
2040
  body,
@@ -1771,7 +2045,7 @@ async function updateSession(client, sessionId, body, options, signal) {
1771
2045
  );
1772
2046
  }
1773
2047
  async function deleteSession(client, sessionId, options, signal) {
1774
- const tenantId = resolveTenantId6(client, options?.tenantId);
2048
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1775
2049
  await client.delete(
1776
2050
  `/sessions/${encodeURIComponent(sessionId)}`,
1777
2051
  tenantId,
@@ -1780,7 +2054,7 @@ async function deleteSession(client, sessionId, options, signal) {
1780
2054
  signal
1781
2055
  );
1782
2056
  }
1783
- function resolveTenantId6(client, tenantId) {
2057
+ function resolveTenantId7(client, tenantId) {
1784
2058
  const resolved = tenantId ?? client.getDefaultTenantId();
1785
2059
  if (!resolved) {
1786
2060
  throw new Error(
@@ -1793,7 +2067,7 @@ function resolveTenantId6(client, tenantId) {
1793
2067
  // src/routes/vizspec.ts
1794
2068
  import crypto5 from "crypto";
1795
2069
  async function generateVizSpec(client, input, options, signal) {
1796
- const tenantId = resolveTenantId7(client, options?.tenantId);
2070
+ const tenantId = resolveTenantId8(client, options?.tenantId);
1797
2071
  const sessionId = crypto5.randomUUID();
1798
2072
  const response = await client.post(
1799
2073
  "/vizspec",
@@ -1815,7 +2089,7 @@ async function generateVizSpec(client, input, options, signal) {
1815
2089
  );
1816
2090
  return response;
1817
2091
  }
1818
- function resolveTenantId7(client, tenantId) {
2092
+ function resolveTenantId8(client, tenantId) {
1819
2093
  const resolved = tenantId ?? client.getDefaultTenantId();
1820
2094
  if (!resolved) {
1821
2095
  throw new Error(
@@ -1826,13 +2100,62 @@ function resolveTenantId7(client, tenantId) {
1826
2100
  }
1827
2101
 
1828
2102
  // src/index.ts
1829
- var QueryPanelSdkAPI = class {
2103
+ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
1830
2104
  client;
1831
2105
  queryEngine;
1832
2106
  constructor(baseUrl, privateKey, organizationId, options) {
1833
- this.client = new ApiClient(baseUrl, privateKey, organizationId, options);
2107
+ if (options?.api) {
2108
+ this.client = options.api;
2109
+ } else {
2110
+ if (!baseUrl) throw new Error("Base URL is required");
2111
+ if (!privateKey) throw new Error("Private key is required");
2112
+ if (!organizationId) throw new Error("Organization ID is required");
2113
+ this.client = new ApiClient(baseUrl, privateKey, organizationId, {
2114
+ defaultTenantId: options?.defaultTenantId,
2115
+ additionalHeaders: options?.additionalHeaders,
2116
+ fetch: options?.fetch
2117
+ });
2118
+ }
2119
+ if (!organizationId) throw new Error("Organization ID is required");
1834
2120
  this.queryEngine = new QueryEngine();
1835
2121
  }
2122
+ /**
2123
+ * Create an SDK instance that uses a request callback instead of HTTP.
2124
+ * Use this when calling the SDK from within your own API so that "API" calls
2125
+ * are handled in-process (e.g. by invoking querypanel-sdk services directly)
2126
+ * and do not cause recursion.
2127
+ *
2128
+ * @param organizationId - Organization identifier
2129
+ * @param requestHandler - Callback invoked for each logical API request (method, path, body, tenantId, etc.)
2130
+ * @param options - Optional defaultTenantId
2131
+ * @returns QueryPanelSdkAPI instance; attach databases and call ask(), syncSchema(), etc. as usual
2132
+ *
2133
+ * @example
2134
+ * ```ts
2135
+ * const qp = QueryPanelSdkAPI.withCallbacks(
2136
+ * process.env.ORGANIZATION_ID!,
2137
+ * async (opts) => {
2138
+ * // Call your in-process services (e.g. querypanel-sdk logic) instead of HTTP
2139
+ * if (opts.path === '/query' || opts.path === '/v2/query') {
2140
+ * const result = await yourSqlGenerator.generate(opts.body, opts.tenantId);
2141
+ * return { data: result, headers: new Headers({ 'x-querypanel-session-id': result.sessionId }) };
2142
+ * }
2143
+ * if (opts.path === '/chart') return { data: await yourChartService.generate(opts.body) };
2144
+ * // ... other paths
2145
+ * return { data: await yourProxy(opts) };
2146
+ * },
2147
+ * { defaultTenantId: process.env.DEFAULT_TENANT_ID }
2148
+ * );
2149
+ * qp.attachPostgres('db', createClient, { tenantFieldName: 'tenant_id' });
2150
+ * const result = await qp.ask('Top 10 orders', { tenantId: 't1', database: 'db' });
2151
+ * ```
2152
+ */
2153
+ static withCallbacks(organizationId, requestHandler, options) {
2154
+ const api = new CallbackApiClient(requestHandler, {
2155
+ defaultTenantId: options?.defaultTenantId
2156
+ });
2157
+ return new _QueryPanelSdkAPI("", "", organizationId, { api });
2158
+ }
1836
2159
  // Database attachment methods
1837
2160
  attachClickhouse(name, clientFn, options) {
1838
2161
  const adapter = new ClickHouseAdapter(clientFn, options);
@@ -1946,6 +2269,48 @@ var QueryPanelSdkAPI = class {
1946
2269
  signal
1947
2270
  );
1948
2271
  }
2272
+ // Embedded dashboard / JWT
2273
+ /**
2274
+ * Creates a JWT for the given tenant (and optional userId, scopes).
2275
+ * Use this when you need to pass a token to the embed (e.g. frontend or demo).
2276
+ * Only available when using the HTTP client (ApiClient); not available when using withCallbacks.
2277
+ */
2278
+ async createJwt(options) {
2279
+ const tenantId = options.tenantId?.trim();
2280
+ if (!tenantId) {
2281
+ throw new Error("tenantId is required");
2282
+ }
2283
+ if (!(this.client instanceof ApiClient)) {
2284
+ throw new Error(
2285
+ "createJwt is not available when using withCallbacks. Use the SDK with baseUrl + privateKey to create JWTs."
2286
+ );
2287
+ }
2288
+ return await this.client.createJwt(
2289
+ tenantId,
2290
+ options.userId,
2291
+ options.scopes
2292
+ );
2293
+ }
2294
+ /**
2295
+ * Runs SQL against an attached database with tenant isolation applied.
2296
+ * Used by embed run-sql (e.g. dashboard chart execution). Requires a database
2297
+ * to be attached (e.g. via attachPostgres) and options.tenantId.
2298
+ */
2299
+ async runSqlForDashboard(input, options) {
2300
+ const tenantId = options.tenantId?.trim();
2301
+ if (!tenantId) {
2302
+ throw new Error("tenantId is required");
2303
+ }
2304
+ const databaseName = input.database ?? "db";
2305
+ const params = input.params ?? {};
2306
+ const result = await this.queryEngine.validateAndExecute(
2307
+ input.sql,
2308
+ params,
2309
+ databaseName,
2310
+ tenantId
2311
+ );
2312
+ return { rows: result.rows, fields: result.fields };
2313
+ }
1949
2314
  // VizSpec generation
1950
2315
  /**
1951
2316
  * Generates a VizSpec visualization specification from query results.
@@ -2389,8 +2754,296 @@ var QueryPanelSdkAPI = class {
2389
2754
  async deleteActiveChart(id, options, signal) {
2390
2755
  await deleteActiveChart(this.client, id, options, signal);
2391
2756
  }
2757
+ // Dashboard CRUD operations
2758
+ /**
2759
+ * Creates a new dashboard with BlockNote content.
2760
+ *
2761
+ * @param body - Dashboard configuration including name and BlockNote content
2762
+ * @param options - Tenant, user, and scope options
2763
+ * @param signal - Optional AbortSignal for cancellation
2764
+ * @returns Created dashboard
2765
+ *
2766
+ * @example
2767
+ * ```typescript
2768
+ * const dashboard = await qp.createDashboard({
2769
+ * name: "Sales Dashboard",
2770
+ * description: "Monthly sales metrics",
2771
+ * content_json: blockNoteContent,
2772
+ * editor_type: "blocknote",
2773
+ * }, { tenantId: "tenant_123" });
2774
+ * ```
2775
+ */
2776
+ async createDashboard(body, options, signal) {
2777
+ return await createDashboard(
2778
+ this.client,
2779
+ body,
2780
+ options,
2781
+ signal
2782
+ );
2783
+ }
2784
+ /**
2785
+ * Lists dashboards with pagination and filtering.
2786
+ *
2787
+ * @param options - Filtering and pagination options
2788
+ * @param signal - Optional AbortSignal for cancellation
2789
+ * @returns Paginated list of dashboards
2790
+ *
2791
+ * @example
2792
+ * ```typescript
2793
+ * const dashboards = await qp.listDashboards({
2794
+ * tenantId: "tenant_123",
2795
+ * status: "deployed",
2796
+ * pagination: { page: 1, limit: 10 },
2797
+ * });
2798
+ * ```
2799
+ */
2800
+ async listDashboards(options, signal) {
2801
+ return await listDashboards(this.client, options, signal);
2802
+ }
2803
+ /**
2804
+ * Gets a dashboard by ID.
2805
+ *
2806
+ * @param id - Dashboard ID
2807
+ * @param options - Tenant, user, and scope options
2808
+ * @param signal - Optional AbortSignal for cancellation
2809
+ * @returns Dashboard details
2810
+ *
2811
+ * @example
2812
+ * ```typescript
2813
+ * const dashboard = await qp.getDashboard("dash_123", {
2814
+ * tenantId: "tenant_123",
2815
+ * });
2816
+ * ```
2817
+ */
2818
+ async getDashboard(id, options, signal) {
2819
+ return await getDashboard(
2820
+ this.client,
2821
+ id,
2822
+ options,
2823
+ signal
2824
+ );
2825
+ }
2826
+ /**
2827
+ * Gets a dashboard for a specific tenant.
2828
+ * Returns customer fork if exists, otherwise returns the original dashboard.
2829
+ *
2830
+ * @param id - Dashboard ID
2831
+ * @param tenantId - Tenant ID to check for fork
2832
+ * @param options - Additional auth options
2833
+ * @param signal - Optional AbortSignal for cancellation
2834
+ * @returns Dashboard (fork if exists, original otherwise)
2835
+ *
2836
+ * @example
2837
+ * ```typescript
2838
+ * const dashboard = await qp.getDashboardForTenant(
2839
+ * "dash_123",
2840
+ * "tenant_456",
2841
+ * );
2842
+ * ```
2843
+ */
2844
+ async getDashboardForTenant(id, tenantId, options, signal) {
2845
+ return await getDashboardForTenant(
2846
+ this.client,
2847
+ id,
2848
+ tenantId,
2849
+ options,
2850
+ signal
2851
+ );
2852
+ }
2853
+ /**
2854
+ * Updates a dashboard.
2855
+ *
2856
+ * @param id - Dashboard ID to update
2857
+ * @param body - Fields to update
2858
+ * @param options - Tenant, user, and scope options
2859
+ * @param signal - Optional AbortSignal for cancellation
2860
+ * @returns Updated dashboard
2861
+ *
2862
+ * @example
2863
+ * ```typescript
2864
+ * const updated = await qp.updateDashboard("dash_123", {
2865
+ * name: "Updated Dashboard",
2866
+ * content_json: newBlockNoteContent,
2867
+ * }, { tenantId: "tenant_123" });
2868
+ * ```
2869
+ */
2870
+ async updateDashboard(id, body, options, signal) {
2871
+ return await updateDashboard(
2872
+ this.client,
2873
+ id,
2874
+ body,
2875
+ options,
2876
+ signal
2877
+ );
2878
+ }
2879
+ /**
2880
+ * Updates dashboard status (deploy/undeploy).
2881
+ *
2882
+ * @param id - Dashboard ID
2883
+ * @param status - New status
2884
+ * @param options - Tenant, user, and scope options
2885
+ * @param signal - Optional AbortSignal for cancellation
2886
+ * @returns Updated dashboard
2887
+ *
2888
+ * @example
2889
+ * ```typescript
2890
+ * const deployed = await qp.updateDashboardStatus(
2891
+ * "dash_123",
2892
+ * "deployed",
2893
+ * { tenantId: "tenant_123" },
2894
+ * );
2895
+ * ```
2896
+ */
2897
+ async updateDashboardStatus(id, status, options, signal) {
2898
+ return await updateDashboardStatus(
2899
+ this.client,
2900
+ id,
2901
+ status,
2902
+ options,
2903
+ signal
2904
+ );
2905
+ }
2906
+ /**
2907
+ * Deletes a dashboard.
2908
+ *
2909
+ * @param id - Dashboard ID to delete
2910
+ * @param options - Tenant, user, and scope options
2911
+ * @param signal - Optional AbortSignal for cancellation
2912
+ *
2913
+ * @example
2914
+ * ```typescript
2915
+ * await qp.deleteDashboard("dash_123", { tenantId: "tenant_123" });
2916
+ * ```
2917
+ */
2918
+ async deleteDashboard(id, options, signal) {
2919
+ await deleteDashboard(this.client, id, options, signal);
2920
+ }
2921
+ // Dashboard Fork operations (Customer Customization)
2922
+ /**
2923
+ * Forks a dashboard for customer customization (copy-on-write).
2924
+ *
2925
+ * Creates a full copy of the dashboard that the customer can edit independently.
2926
+ * The original dashboard remains unchanged.
2927
+ *
2928
+ * @param id - Dashboard ID to fork
2929
+ * @param input - Fork configuration with tenant_id
2930
+ * @param options - Additional auth options
2931
+ * @param signal - Optional AbortSignal for cancellation
2932
+ * @returns Forked dashboard
2933
+ *
2934
+ * @example
2935
+ * ```typescript
2936
+ * const fork = await qp.forkDashboard("dash_123", {
2937
+ * tenant_id: "tenant_456",
2938
+ * name: "Customer's Custom Dashboard",
2939
+ * });
2940
+ * ```
2941
+ */
2942
+ async forkDashboard(id, input, options, signal) {
2943
+ return await forkDashboard(
2944
+ this.client,
2945
+ id,
2946
+ input,
2947
+ options,
2948
+ signal
2949
+ );
2950
+ }
2951
+ /**
2952
+ * Updates a customer fork.
2953
+ *
2954
+ * @param forkId - Fork ID to update
2955
+ * @param input - Update data including tenant_id
2956
+ * @param options - Additional auth options
2957
+ * @param signal - Optional AbortSignal for cancellation
2958
+ * @returns Updated fork
2959
+ *
2960
+ * @example
2961
+ * ```typescript
2962
+ * const updated = await qp.updateFork("fork_123", {
2963
+ * tenant_id: "tenant_456",
2964
+ * content_json: newBlockNoteContent,
2965
+ * });
2966
+ * ```
2967
+ */
2968
+ async updateFork(forkId, input, options, signal) {
2969
+ return await updateFork(
2970
+ this.client,
2971
+ forkId,
2972
+ input,
2973
+ options,
2974
+ signal
2975
+ );
2976
+ }
2977
+ /**
2978
+ * Rollbacks a fork to the original dashboard.
2979
+ * This deletes the fork and the customer will see the original again.
2980
+ *
2981
+ * @param forkId - Fork ID to rollback
2982
+ * @param tenantId - Tenant ID
2983
+ * @param options - Additional auth options
2984
+ * @param signal - Optional AbortSignal for cancellation
2985
+ * @returns Original dashboard
2986
+ *
2987
+ * @example
2988
+ * ```typescript
2989
+ * const original = await qp.rollbackFork("fork_123", "tenant_456");
2990
+ * ```
2991
+ */
2992
+ async rollbackFork(forkId, tenantId, options, signal) {
2993
+ return await rollbackFork(
2994
+ this.client,
2995
+ forkId,
2996
+ tenantId,
2997
+ options,
2998
+ signal
2999
+ );
3000
+ }
3001
+ /**
3002
+ * Deletes a customer fork.
3003
+ *
3004
+ * @param forkId - Fork ID to delete
3005
+ * @param tenantId - Tenant ID
3006
+ * @param options - Additional auth options
3007
+ * @param signal - Optional AbortSignal for cancellation
3008
+ *
3009
+ * @example
3010
+ * ```typescript
3011
+ * await qp.deleteFork("fork_123", "tenant_456");
3012
+ * ```
3013
+ */
3014
+ async deleteFork(forkId, tenantId, options, signal) {
3015
+ await deleteFork(
3016
+ this.client,
3017
+ forkId,
3018
+ tenantId,
3019
+ options,
3020
+ signal
3021
+ );
3022
+ }
3023
+ /**
3024
+ * Lists all customer forks for a tenant.
3025
+ *
3026
+ * @param tenantId - Tenant ID
3027
+ * @param options - Additional auth options
3028
+ * @param signal - Optional AbortSignal for cancellation
3029
+ * @returns Array of forks
3030
+ *
3031
+ * @example
3032
+ * ```typescript
3033
+ * const forks = await qp.listForksForTenant("tenant_456");
3034
+ * ```
3035
+ */
3036
+ async listForksForTenant(tenantId, options, signal) {
3037
+ return await listForksForTenant(
3038
+ this.client,
3039
+ tenantId,
3040
+ options,
3041
+ signal
3042
+ );
3043
+ }
2392
3044
  };
2393
3045
  export {
3046
+ CallbackApiClient,
2394
3047
  ClickHouseAdapter,
2395
3048
  PostgresAdapter,
2396
3049
  QueryErrorCode,