@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.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -29,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
30
  // src/index.ts
30
31
  var index_exports = {};
31
32
  __export(index_exports, {
33
+ CallbackApiClient: () => CallbackApiClient,
32
34
  ClickHouseAdapter: () => ClickHouseAdapter,
33
35
  PostgresAdapter: () => PostgresAdapter,
34
36
  QueryErrorCode: () => QueryErrorCode,
@@ -366,29 +368,31 @@ var PostgresAdapter = class {
366
368
  }
367
369
  }
368
370
  /**
369
- * Convert named params to positional array for PostgreSQL
370
- * PostgreSQL expects $1, $2, $3 in SQL and an array of values [val1, val2, val3]
371
+ * Convert params to positional array for PostgreSQL.
372
+ * PostgreSQL expects $1, $2, $3 in SQL and an array of values [val1, val2, val3].
373
+ * When the record has numeric keys ("1", "2", "3") — e.g. from mapGeneratedParams —
374
+ * use only those in order so placeholder order matches SQL. Otherwise fall back to
375
+ * named keys in alphabetical order for backward compatibility.
371
376
  */
372
377
  convertNamedToPositionalParams(params) {
373
378
  const numericKeys = Object.keys(params).filter((k) => /^\d+$/.test(k)).map((k) => Number.parseInt(k, 10)).sort((a, b) => a - b);
374
- const namedKeys = Object.keys(params).filter((k) => !/^\d+$/.test(k)).sort();
375
- const positionalParams = [];
376
- for (const key of numericKeys) {
377
- let val = params[String(key)];
378
- if (typeof val === "string") {
379
- const match = val.match(/^<([a-zA-Z0-9_]+)>$/);
380
- const namedKey = match?.[1];
381
- if (namedKey && namedKey in params) {
382
- val = params[namedKey];
379
+ if (numericKeys.length > 0) {
380
+ const positionalParams = [];
381
+ for (const key of numericKeys) {
382
+ let val = params[String(key)];
383
+ if (typeof val === "string") {
384
+ const match = val.match(/^<([a-zA-Z0-9_]+)>$/);
385
+ const namedKey = match?.[1];
386
+ if (namedKey && namedKey in params) {
387
+ val = params[namedKey];
388
+ }
383
389
  }
390
+ positionalParams.push(val);
384
391
  }
385
- positionalParams.push(val);
386
- }
387
- for (const key of namedKeys) {
388
- const val = params[key];
389
- positionalParams.push(val);
392
+ return positionalParams;
390
393
  }
391
- return positionalParams;
394
+ const namedKeys = Object.keys(params).filter((k) => !/^\d+$/.test(k)).sort();
395
+ return namedKeys.map((key) => params[key]);
392
396
  }
393
397
  async validate(sql, params) {
394
398
  let paramArray;
@@ -600,6 +604,13 @@ var ApiClient = class {
600
604
  getDefaultTenantId() {
601
605
  return this.defaultTenantId;
602
606
  }
607
+ /**
608
+ * Create a JWT for the given tenant (and optional userId, scopes).
609
+ * Use this when you need to pass a token to the embed (e.g. frontend or demo).
610
+ */
611
+ async createJwt(tenantId, userId, scopes) {
612
+ return await this.generateJWT(tenantId, userId, scopes);
613
+ }
603
614
  async get(path, tenantId, userId, scopes, signal, sessionId) {
604
615
  return await this.request(path, {
605
616
  method: "GET",
@@ -812,6 +823,111 @@ var ApiClient = class {
812
823
  }
813
824
  };
814
825
 
826
+ // src/core/callback-client.ts
827
+ var CallbackApiClient = class {
828
+ requestHandler;
829
+ defaultTenantId;
830
+ constructor(requestHandler, options) {
831
+ this.requestHandler = requestHandler;
832
+ this.defaultTenantId = options?.defaultTenantId;
833
+ }
834
+ getDefaultTenantId() {
835
+ return this.defaultTenantId;
836
+ }
837
+ async request(opts) {
838
+ const { data, headers } = await this.requestHandler({
839
+ method: opts.method,
840
+ path: opts.path,
841
+ body: opts.body,
842
+ tenantId: opts.tenantId,
843
+ userId: opts.userId,
844
+ scopes: opts.scopes,
845
+ signal: opts.signal,
846
+ sessionId: opts.sessionId
847
+ });
848
+ return { data, headers };
849
+ }
850
+ async get(path, tenantId, userId, scopes, signal, sessionId) {
851
+ const { data } = await this.request({
852
+ method: "GET",
853
+ path,
854
+ tenantId,
855
+ userId,
856
+ scopes,
857
+ signal,
858
+ sessionId
859
+ });
860
+ return data;
861
+ }
862
+ async post(path, body, tenantId, userId, scopes, signal, sessionId) {
863
+ const { data } = await this.request({
864
+ method: "POST",
865
+ path,
866
+ body,
867
+ tenantId,
868
+ userId,
869
+ scopes,
870
+ signal,
871
+ sessionId
872
+ });
873
+ return data;
874
+ }
875
+ async postWithHeaders(path, body, tenantId, userId, scopes, signal, sessionId) {
876
+ const { data, headers } = await this.request({
877
+ method: "POST",
878
+ path,
879
+ body,
880
+ tenantId,
881
+ userId,
882
+ scopes,
883
+ signal,
884
+ sessionId
885
+ });
886
+ return {
887
+ data,
888
+ headers: headers ?? new Headers()
889
+ };
890
+ }
891
+ async put(path, body, tenantId, userId, scopes, signal, sessionId) {
892
+ const { data } = await this.request({
893
+ method: "PUT",
894
+ path,
895
+ body,
896
+ tenantId,
897
+ userId,
898
+ scopes,
899
+ signal,
900
+ sessionId
901
+ });
902
+ return data;
903
+ }
904
+ async patch(path, body, tenantId, userId, scopes, signal, sessionId) {
905
+ const { data } = await this.request({
906
+ method: "PATCH",
907
+ path,
908
+ body,
909
+ tenantId,
910
+ userId,
911
+ scopes,
912
+ signal,
913
+ sessionId
914
+ });
915
+ return data;
916
+ }
917
+ async delete(path, tenantId, userId, scopes, signal, sessionId) {
918
+ const { data } = await this.request({
919
+ method: "DELETE",
920
+ path,
921
+ tenantId,
922
+ userId,
923
+ scopes,
924
+ signal,
925
+ sessionId
926
+ });
927
+ return data;
928
+ }
929
+ };
930
+
815
931
  // src/core/query-engine.ts
816
932
  var QueryEngine = class {
817
933
  databases = /* @__PURE__ */ new Map();
@@ -874,6 +990,12 @@ var QueryEngine = class {
874
990
  return [];
875
991
  }
876
992
  }
993
+ /**
994
+ * Maps API params (array in SQL placeholder order $1, $2, $3) to ParamRecord.
995
+ * Sets both numeric keys ("1", "2", "3") and named keys so that:
996
+ * - Postgres adapter uses numeric order for positional params (matches SQL).
997
+ * - Named keys remain for tenant isolation / other lookups.
998
+ */
877
999
  mapGeneratedParams(params) {
878
1000
  const record = {};
879
1001
  params.forEach((param, index) => {
@@ -881,6 +1003,7 @@ var QueryEngine = class {
881
1003
  if (value === void 0) {
882
1004
  return;
883
1005
  }
1006
+ record[String(index + 1)] = value;
884
1007
  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);
885
1008
  const key = nameCandidate.replace(/[{}:$]/g, "").trim();
886
1009
  record[key] = value;
@@ -1209,10 +1332,161 @@ function resolveTenantId2(client, tenantId) {
1209
1332
  return resolved;
1210
1333
  }
1211
1334
 
1335
+ // src/routes/dashboards.ts
1336
+ function resolveTenantId3(client, tenantId) {
1337
+ const resolved = tenantId ?? client.getDefaultTenantId();
1338
+ if (resolved === void 0) {
1339
+ throw new Error("tenantId is required (option or client default)");
1340
+ }
1341
+ return resolved;
1342
+ }
1343
+ async function createDashboard(client, body, options, signal) {
1344
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1345
+ return await client.post(
1346
+ "/dashboards",
1347
+ body,
1348
+ tenantId,
1349
+ options?.userId,
1350
+ options?.scopes,
1351
+ signal
1352
+ );
1353
+ }
1354
+ async function listDashboards(client, options, signal) {
1355
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1356
+ const params = new URLSearchParams();
1357
+ if (options?.pagination?.page) {
1358
+ params.set("page", options.pagination.page.toString());
1359
+ }
1360
+ if (options?.pagination?.limit) {
1361
+ params.set("limit", options.pagination.limit.toString());
1362
+ }
1363
+ if (options?.status) {
1364
+ params.set("status", options.status);
1365
+ }
1366
+ if (options?.sortBy) {
1367
+ params.set("sort_by", options.sortBy);
1368
+ }
1369
+ if (options?.sortDir) {
1370
+ params.set("sort_dir", options.sortDir);
1371
+ }
1372
+ return await client.get(
1373
+ `/dashboards?${params.toString()}`,
1374
+ tenantId,
1375
+ options?.userId,
1376
+ options?.scopes,
1377
+ signal
1378
+ );
1379
+ }
1380
+ async function getDashboard(client, id, options, signal) {
1381
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1382
+ return await client.get(
1383
+ `/dashboards/${id}`,
1384
+ tenantId,
1385
+ options?.userId,
1386
+ options?.scopes,
1387
+ signal
1388
+ );
1389
+ }
1390
+ async function getDashboardForTenant(client, id, tenantId, options, signal) {
1391
+ const params = new URLSearchParams();
1392
+ params.set("tenant_id", tenantId);
1393
+ return await client.get(
1394
+ `/dashboards/${id}/for-tenant?${params.toString()}`,
1395
+ tenantId,
1396
+ options?.userId,
1397
+ options?.scopes,
1398
+ signal
1399
+ );
1400
+ }
1401
+ async function updateDashboard(client, id, body, options, signal) {
1402
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1403
+ return await client.put(
1404
+ `/dashboards/${id}`,
1405
+ body,
1406
+ tenantId,
1407
+ options?.userId,
1408
+ options?.scopes,
1409
+ signal
1410
+ );
1411
+ }
1412
+ async function updateDashboardStatus(client, id, status, options, signal) {
1413
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1414
+ return await client.patch(
1415
+ `/dashboards/${id}/status`,
1416
+ { status },
1417
+ tenantId,
1418
+ options?.userId,
1419
+ options?.scopes,
1420
+ signal
1421
+ );
1422
+ }
1423
+ async function deleteDashboard(client, id, options, signal) {
1424
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1425
+ await client.delete(
1426
+ `/dashboards/${id}`,
1427
+ tenantId,
1428
+ options?.userId,
1429
+ options?.scopes,
1430
+ signal
1431
+ );
1432
+ }
1433
+ async function forkDashboard(client, id, input, options, signal) {
1434
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1435
+ return await client.post(
1436
+ `/dashboards/${id}/fork`,
1437
+ input,
1438
+ tenantId,
1439
+ options?.userId,
1440
+ options?.scopes,
1441
+ signal
1442
+ );
1443
+ }
1444
+ async function updateFork(client, forkId, input, options, signal) {
1445
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1446
+ return await client.put(
1447
+ `/dashboards/forks/${forkId}`,
1448
+ input,
1449
+ tenantId,
1450
+ options?.userId,
1451
+ options?.scopes,
1452
+ signal
1453
+ );
1454
+ }
1455
+ async function rollbackFork(client, forkId, tenantId, options, signal) {
1456
+ return await client.post(
1457
+ `/dashboards/forks/${forkId}/rollback`,
1458
+ { tenant_id: tenantId },
1459
+ tenantId,
1460
+ options?.userId,
1461
+ options?.scopes,
1462
+ signal
1463
+ );
1464
+ }
1465
+ async function deleteFork(client, forkId, tenantId, options, signal) {
1466
+ const params = new URLSearchParams();
1467
+ params.set("tenant_id", tenantId);
1468
+ await client.delete(
1469
+ `/dashboards/forks/${forkId}?${params.toString()}`,
1470
+ tenantId,
1471
+ options?.userId,
1472
+ options?.scopes,
1473
+ signal
1474
+ );
1475
+ }
1476
+ async function listForksForTenant(client, tenantId, options, signal) {
1477
+ return await client.get(
1478
+ `/dashboards/customer/${tenantId}`,
1479
+ tenantId,
1480
+ options?.userId,
1481
+ options?.scopes,
1482
+ signal
1483
+ );
1484
+ }
1485
+
1212
1486
  // src/routes/ingest.ts
1213
1487
  var import_node_crypto2 = __toESM(require("crypto"), 1);
1214
1488
  async function syncSchema(client, queryEngine, databaseName, options, signal) {
1215
- const tenantId = resolveTenantId3(client, options.tenantId);
1489
+ const tenantId = resolveTenantId4(client, options.tenantId);
1216
1490
  const adapter = queryEngine.getDatabase(databaseName);
1217
1491
  const metadata = queryEngine.getDatabaseMetadata(databaseName);
1218
1492
  const introspection = await adapter.introspect(
@@ -1234,7 +1508,7 @@ async function syncSchema(client, queryEngine, databaseName, options, signal) {
1234
1508
  );
1235
1509
  return response;
1236
1510
  }
1237
- function resolveTenantId3(client, tenantId) {
1511
+ function resolveTenantId4(client, tenantId) {
1238
1512
  const resolved = tenantId ?? client.getDefaultTenantId();
1239
1513
  if (!resolved) {
1240
1514
  throw new Error(
@@ -1276,7 +1550,7 @@ var import_node_crypto4 = __toESM(require("crypto"), 1);
1276
1550
  // src/routes/query.ts
1277
1551
  var import_node_crypto3 = __toESM(require("crypto"), 1);
1278
1552
  async function ask(client, queryEngine, question, options, signal) {
1279
- const tenantId = resolveTenantId4(client, options.tenantId);
1553
+ const tenantId = resolveTenantId5(client, options.tenantId);
1280
1554
  const sessionId = import_node_crypto3.default.randomUUID();
1281
1555
  const querypanelSessionId = options.querypanelSessionId ?? sessionId;
1282
1556
  const maxRetry = options.maxRetry ?? 0;
@@ -1431,7 +1705,7 @@ async function ask(client, queryEngine, question, options, signal) {
1431
1705
  }
1432
1706
  throw new Error("Unexpected error in ask retry loop");
1433
1707
  }
1434
- function resolveTenantId4(client, tenantId) {
1708
+ function resolveTenantId5(client, tenantId) {
1435
1709
  const resolved = tenantId ?? client.getDefaultTenantId();
1436
1710
  if (!resolved) {
1437
1711
  throw new Error(
@@ -1454,7 +1728,7 @@ function anonymizeResults(rows) {
1454
1728
  }
1455
1729
 
1456
1730
  // src/routes/modify.ts
1457
- function buildModifiedQuestion(originalQuestion, modifications, pipeline) {
1731
+ function buildModifiedQuestion(originalQuestion, modifications, pipeline, hasSessionContext = false) {
1458
1732
  const hints = [];
1459
1733
  if (modifications.timeGranularity) {
1460
1734
  hints.push(`group results by ${modifications.timeGranularity}`);
@@ -1489,7 +1763,7 @@ function buildModifiedQuestion(originalQuestion, modifications, pipeline) {
1489
1763
  if (hints.length === 0) {
1490
1764
  return originalQuestion;
1491
1765
  }
1492
- if (pipeline === "v2") {
1766
+ if (pipeline === "v2" && hasSessionContext) {
1493
1767
  return hints.join(", ");
1494
1768
  }
1495
1769
  return `${originalQuestion} (${hints.join(", ")})`;
@@ -1587,7 +1861,7 @@ function stripVizSpecOnlyHints(hints) {
1587
1861
  const { kind: _kind, ...rest } = hints;
1588
1862
  return rest;
1589
1863
  }
1590
- function resolveTenantId5(client, tenantId) {
1864
+ function resolveTenantId6(client, tenantId) {
1591
1865
  const resolved = tenantId ?? client.getDefaultTenantId();
1592
1866
  if (!resolved) {
1593
1867
  throw new Error(
@@ -1597,9 +1871,10 @@ function resolveTenantId5(client, tenantId) {
1597
1871
  return resolved;
1598
1872
  }
1599
1873
  async function modifyChart(client, queryEngine, input, options, signal) {
1600
- const tenantId = resolveTenantId5(client, options?.tenantId);
1874
+ const tenantId = resolveTenantId6(client, options?.tenantId);
1601
1875
  const sessionId = import_node_crypto4.default.randomUUID();
1602
1876
  const querypanelSessionId = options?.querypanelSessionId ?? sessionId;
1877
+ const hasQuerypanelSessionContext = !!options?.querypanelSessionId;
1603
1878
  const chartType = options?.chartType ?? "vega-lite";
1604
1879
  const hasSqlMods = !!input.sqlModifications;
1605
1880
  const hasVizMods = !!input.vizModifications;
@@ -1636,7 +1911,8 @@ async function modifyChart(client, queryEngine, input, options, signal) {
1636
1911
  const modifiedQuestion = buildModifiedQuestion(
1637
1912
  input.question,
1638
1913
  input.sqlModifications,
1639
- options?.pipeline
1914
+ options?.pipeline,
1915
+ hasQuerypanelSessionContext
1640
1916
  );
1641
1917
  if (options?.pipeline === "v2") {
1642
1918
  finalQuestion = modifiedQuestion;
@@ -1763,7 +2039,7 @@ async function modifyChart(client, queryEngine, input, options, signal) {
1763
2039
 
1764
2040
  // src/routes/sessions.ts
1765
2041
  async function listSessions(client, options, signal) {
1766
- const tenantId = resolveTenantId6(client, options?.tenantId);
2042
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1767
2043
  const params = new URLSearchParams();
1768
2044
  if (options?.pagination?.page)
1769
2045
  params.set("page", `${options.pagination.page}`);
@@ -1786,7 +2062,7 @@ async function listSessions(client, options, signal) {
1786
2062
  );
1787
2063
  }
1788
2064
  async function getSession(client, sessionId, options, signal) {
1789
- const tenantId = resolveTenantId6(client, options?.tenantId);
2065
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1790
2066
  const params = new URLSearchParams();
1791
2067
  if (options?.includeTurns !== void 0) {
1792
2068
  params.set("include_turns", `${options.includeTurns}`);
@@ -1800,7 +2076,7 @@ async function getSession(client, sessionId, options, signal) {
1800
2076
  );
1801
2077
  }
1802
2078
  async function updateSession(client, sessionId, body, options, signal) {
1803
- const tenantId = resolveTenantId6(client, options?.tenantId);
2079
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1804
2080
  return await client.patch(
1805
2081
  `/sessions/${encodeURIComponent(sessionId)}`,
1806
2082
  body,
@@ -1811,7 +2087,7 @@ async function updateSession(client, sessionId, body, options, signal) {
1811
2087
  );
1812
2088
  }
1813
2089
  async function deleteSession(client, sessionId, options, signal) {
1814
- const tenantId = resolveTenantId6(client, options?.tenantId);
2090
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1815
2091
  await client.delete(
1816
2092
  `/sessions/${encodeURIComponent(sessionId)}`,
1817
2093
  tenantId,
@@ -1820,7 +2096,7 @@ async function deleteSession(client, sessionId, options, signal) {
1820
2096
  signal
1821
2097
  );
1822
2098
  }
1823
- function resolveTenantId6(client, tenantId) {
2099
+ function resolveTenantId7(client, tenantId) {
1824
2100
  const resolved = tenantId ?? client.getDefaultTenantId();
1825
2101
  if (!resolved) {
1826
2102
  throw new Error(
@@ -1833,7 +2109,7 @@ function resolveTenantId6(client, tenantId) {
1833
2109
  // src/routes/vizspec.ts
1834
2110
  var import_node_crypto5 = __toESM(require("crypto"), 1);
1835
2111
  async function generateVizSpec(client, input, options, signal) {
1836
- const tenantId = resolveTenantId7(client, options?.tenantId);
2112
+ const tenantId = resolveTenantId8(client, options?.tenantId);
1837
2113
  const sessionId = import_node_crypto5.default.randomUUID();
1838
2114
  const response = await client.post(
1839
2115
  "/vizspec",
@@ -1855,7 +2131,7 @@ async function generateVizSpec(client, input, options, signal) {
1855
2131
  );
1856
2132
  return response;
1857
2133
  }
1858
- function resolveTenantId7(client, tenantId) {
2134
+ function resolveTenantId8(client, tenantId) {
1859
2135
  const resolved = tenantId ?? client.getDefaultTenantId();
1860
2136
  if (!resolved) {
1861
2137
  throw new Error(
@@ -1866,13 +2142,62 @@ function resolveTenantId7(client, tenantId) {
1866
2142
  }
1867
2143
 
1868
2144
  // src/index.ts
1869
- var QueryPanelSdkAPI = class {
2145
+ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
1870
2146
  client;
1871
2147
  queryEngine;
1872
2148
  constructor(baseUrl, privateKey, organizationId, options) {
1873
- this.client = new ApiClient(baseUrl, privateKey, organizationId, options);
2149
+ if (options?.api) {
2150
+ this.client = options.api;
2151
+ } else {
2152
+ if (!baseUrl) throw new Error("Base URL is required");
2153
+ if (!privateKey) throw new Error("Private key is required");
2154
+ if (!organizationId) throw new Error("Organization ID is required");
2155
+ this.client = new ApiClient(baseUrl, privateKey, organizationId, {
2156
+ defaultTenantId: options?.defaultTenantId,
2157
+ additionalHeaders: options?.additionalHeaders,
2158
+ fetch: options?.fetch
2159
+ });
2160
+ }
2161
+ if (!organizationId) throw new Error("Organization ID is required");
1874
2162
  this.queryEngine = new QueryEngine();
1875
2163
  }
2164
+ /**
2165
+ * Create an SDK instance that uses a request callback instead of HTTP.
2166
+ * Use this when calling the SDK from within your own API so that "API" calls
2167
+ * are handled in-process (e.g. by invoking querypanel-sdk services directly)
2168
+ * and do not cause recursion.
2169
+ *
2170
+ * @param organizationId - Organization identifier
2171
+ * @param requestHandler - Callback invoked for each logical API request (method, path, body, tenantId, etc.)
2172
+ * @param options - Optional defaultTenantId
2173
+ * @returns QueryPanelSdkAPI instance; attach databases and call ask(), syncSchema(), etc. as usual
2174
+ *
2175
+ * @example
2176
+ * ```ts
2177
+ * const qp = QueryPanelSdkAPI.withCallbacks(
2178
+ * process.env.ORGANIZATION_ID!,
2179
+ * async (opts) => {
2180
+ * // Call your in-process services (e.g. querypanel-sdk logic) instead of HTTP
2181
+ * if (opts.path === '/query' || opts.path === '/v2/query') {
2182
+ * const result = await yourSqlGenerator.generate(opts.body, opts.tenantId);
2183
+ * return { data: result, headers: new Headers({ 'x-querypanel-session-id': result.sessionId }) };
2184
+ * }
2185
+ * if (opts.path === '/chart') return { data: await yourChartService.generate(opts.body) };
2186
+ * // ... other paths
2187
+ * return { data: await yourProxy(opts) };
2188
+ * },
2189
+ * { defaultTenantId: process.env.DEFAULT_TENANT_ID }
2190
+ * );
2191
+ * qp.attachPostgres('db', createClient, { tenantFieldName: 'tenant_id' });
2192
+ * const result = await qp.ask('Top 10 orders', { tenantId: 't1', database: 'db' });
2193
+ * ```
2194
+ */
2195
+ static withCallbacks(organizationId, requestHandler, options) {
2196
+ const api = new CallbackApiClient(requestHandler, {
2197
+ defaultTenantId: options?.defaultTenantId
2198
+ });
2199
+ return new _QueryPanelSdkAPI("", "", organizationId, { api });
2200
+ }
1876
2201
  // Database attachment methods
1877
2202
  attachClickhouse(name, clientFn, options) {
1878
2203
  const adapter = new ClickHouseAdapter(clientFn, options);
@@ -1986,6 +2311,48 @@ var QueryPanelSdkAPI = class {
1986
2311
  signal
1987
2312
  );
1988
2313
  }
2314
+ // Embedded dashboard / JWT
2315
+ /**
2316
+ * Creates a JWT for the given tenant (and optional userId, scopes).
2317
+ * Use this when you need to pass a token to the embed (e.g. frontend or demo).
2318
+ * Only available when using the HTTP client (ApiClient); not available when using withCallbacks.
2319
+ */
2320
+ async createJwt(options) {
2321
+ const tenantId = options.tenantId?.trim();
2322
+ if (!tenantId) {
2323
+ throw new Error("tenantId is required");
2324
+ }
2325
+ if (!(this.client instanceof ApiClient)) {
2326
+ throw new Error(
2327
+ "createJwt is not available when using withCallbacks. Use the SDK with baseUrl + privateKey to create JWTs."
2328
+ );
2329
+ }
2330
+ return await this.client.createJwt(
2331
+ tenantId,
2332
+ options.userId,
2333
+ options.scopes
2334
+ );
2335
+ }
2336
+ /**
2337
+ * Runs SQL against an attached database with tenant isolation applied.
2338
+ * Used by embed run-sql (e.g. dashboard chart execution). Requires a database
2339
+ * to be attached (e.g. via attachPostgres) and options.tenantId.
2340
+ */
2341
+ async runSqlForDashboard(input, options) {
2342
+ const tenantId = options.tenantId?.trim();
2343
+ if (!tenantId) {
2344
+ throw new Error("tenantId is required");
2345
+ }
2346
+ const databaseName = input.database ?? "db";
2347
+ const params = input.params ?? {};
2348
+ const result = await this.queryEngine.validateAndExecute(
2349
+ input.sql,
2350
+ params,
2351
+ databaseName,
2352
+ tenantId
2353
+ );
2354
+ return { rows: result.rows, fields: result.fields };
2355
+ }
1989
2356
  // VizSpec generation
1990
2357
  /**
1991
2358
  * Generates a VizSpec visualization specification from query results.
@@ -2429,9 +2796,297 @@ var QueryPanelSdkAPI = class {
2429
2796
  async deleteActiveChart(id, options, signal) {
2430
2797
  await deleteActiveChart(this.client, id, options, signal);
2431
2798
  }
2799
+ // Dashboard CRUD operations
2800
+ /**
2801
+ * Creates a new dashboard with BlockNote content.
2802
+ *
2803
+ * @param body - Dashboard configuration including name and BlockNote content
2804
+ * @param options - Tenant, user, and scope options
2805
+ * @param signal - Optional AbortSignal for cancellation
2806
+ * @returns Created dashboard
2807
+ *
2808
+ * @example
2809
+ * ```typescript
2810
+ * const dashboard = await qp.createDashboard({
2811
+ * name: "Sales Dashboard",
2812
+ * description: "Monthly sales metrics",
2813
+ * content_json: blockNoteContent,
2814
+ * editor_type: "blocknote",
2815
+ * }, { tenantId: "tenant_123" });
2816
+ * ```
2817
+ */
2818
+ async createDashboard(body, options, signal) {
2819
+ return await createDashboard(
2820
+ this.client,
2821
+ body,
2822
+ options,
2823
+ signal
2824
+ );
2825
+ }
2826
+ /**
2827
+ * Lists dashboards with pagination and filtering.
2828
+ *
2829
+ * @param options - Filtering and pagination options
2830
+ * @param signal - Optional AbortSignal for cancellation
2831
+ * @returns Paginated list of dashboards
2832
+ *
2833
+ * @example
2834
+ * ```typescript
2835
+ * const dashboards = await qp.listDashboards({
2836
+ * tenantId: "tenant_123",
2837
+ * status: "deployed",
2838
+ * pagination: { page: 1, limit: 10 },
2839
+ * });
2840
+ * ```
2841
+ */
2842
+ async listDashboards(options, signal) {
2843
+ return await listDashboards(this.client, options, signal);
2844
+ }
2845
+ /**
2846
+ * Gets a dashboard by ID.
2847
+ *
2848
+ * @param id - Dashboard ID
2849
+ * @param options - Tenant, user, and scope options
2850
+ * @param signal - Optional AbortSignal for cancellation
2851
+ * @returns Dashboard details
2852
+ *
2853
+ * @example
2854
+ * ```typescript
2855
+ * const dashboard = await qp.getDashboard("dash_123", {
2856
+ * tenantId: "tenant_123",
2857
+ * });
2858
+ * ```
2859
+ */
2860
+ async getDashboard(id, options, signal) {
2861
+ return await getDashboard(
2862
+ this.client,
2863
+ id,
2864
+ options,
2865
+ signal
2866
+ );
2867
+ }
2868
+ /**
2869
+ * Gets a dashboard for a specific tenant.
2870
+ * Returns customer fork if exists, otherwise returns the original dashboard.
2871
+ *
2872
+ * @param id - Dashboard ID
2873
+ * @param tenantId - Tenant ID to check for fork
2874
+ * @param options - Additional auth options
2875
+ * @param signal - Optional AbortSignal for cancellation
2876
+ * @returns Dashboard (fork if exists, original otherwise)
2877
+ *
2878
+ * @example
2879
+ * ```typescript
2880
+ * const dashboard = await qp.getDashboardForTenant(
2881
+ * "dash_123",
2882
+ * "tenant_456",
2883
+ * );
2884
+ * ```
2885
+ */
2886
+ async getDashboardForTenant(id, tenantId, options, signal) {
2887
+ return await getDashboardForTenant(
2888
+ this.client,
2889
+ id,
2890
+ tenantId,
2891
+ options,
2892
+ signal
2893
+ );
2894
+ }
2895
+ /**
2896
+ * Updates a dashboard.
2897
+ *
2898
+ * @param id - Dashboard ID to update
2899
+ * @param body - Fields to update
2900
+ * @param options - Tenant, user, and scope options
2901
+ * @param signal - Optional AbortSignal for cancellation
2902
+ * @returns Updated dashboard
2903
+ *
2904
+ * @example
2905
+ * ```typescript
2906
+ * const updated = await qp.updateDashboard("dash_123", {
2907
+ * name: "Updated Dashboard",
2908
+ * content_json: newBlockNoteContent,
2909
+ * }, { tenantId: "tenant_123" });
2910
+ * ```
2911
+ */
2912
+ async updateDashboard(id, body, options, signal) {
2913
+ return await updateDashboard(
2914
+ this.client,
2915
+ id,
2916
+ body,
2917
+ options,
2918
+ signal
2919
+ );
2920
+ }
2921
+ /**
2922
+ * Updates dashboard status (deploy/undeploy).
2923
+ *
2924
+ * @param id - Dashboard ID
2925
+ * @param status - New status
2926
+ * @param options - Tenant, user, and scope options
2927
+ * @param signal - Optional AbortSignal for cancellation
2928
+ * @returns Updated dashboard
2929
+ *
2930
+ * @example
2931
+ * ```typescript
2932
+ * const deployed = await qp.updateDashboardStatus(
2933
+ * "dash_123",
2934
+ * "deployed",
2935
+ * { tenantId: "tenant_123" },
2936
+ * );
2937
+ * ```
2938
+ */
2939
+ async updateDashboardStatus(id, status, options, signal) {
2940
+ return await updateDashboardStatus(
2941
+ this.client,
2942
+ id,
2943
+ status,
2944
+ options,
2945
+ signal
2946
+ );
2947
+ }
2948
+ /**
2949
+ * Deletes a dashboard.
2950
+ *
2951
+ * @param id - Dashboard ID to delete
2952
+ * @param options - Tenant, user, and scope options
2953
+ * @param signal - Optional AbortSignal for cancellation
2954
+ *
2955
+ * @example
2956
+ * ```typescript
2957
+ * await qp.deleteDashboard("dash_123", { tenantId: "tenant_123" });
2958
+ * ```
2959
+ */
2960
+ async deleteDashboard(id, options, signal) {
2961
+ await deleteDashboard(this.client, id, options, signal);
2962
+ }
2963
+ // Dashboard Fork operations (Customer Customization)
2964
+ /**
2965
+ * Forks a dashboard for customer customization (copy-on-write).
2966
+ *
2967
+ * Creates a full copy of the dashboard that the customer can edit independently.
2968
+ * The original dashboard remains unchanged.
2969
+ *
2970
+ * @param id - Dashboard ID to fork
2971
+ * @param input - Fork configuration with tenant_id
2972
+ * @param options - Additional auth options
2973
+ * @param signal - Optional AbortSignal for cancellation
2974
+ * @returns Forked dashboard
2975
+ *
2976
+ * @example
2977
+ * ```typescript
2978
+ * const fork = await qp.forkDashboard("dash_123", {
2979
+ * tenant_id: "tenant_456",
2980
+ * name: "Customer's Custom Dashboard",
2981
+ * });
2982
+ * ```
2983
+ */
2984
+ async forkDashboard(id, input, options, signal) {
2985
+ return await forkDashboard(
2986
+ this.client,
2987
+ id,
2988
+ input,
2989
+ options,
2990
+ signal
2991
+ );
2992
+ }
2993
+ /**
2994
+ * Updates a customer fork.
2995
+ *
2996
+ * @param forkId - Fork ID to update
2997
+ * @param input - Update data including tenant_id
2998
+ * @param options - Additional auth options
2999
+ * @param signal - Optional AbortSignal for cancellation
3000
+ * @returns Updated fork
3001
+ *
3002
+ * @example
3003
+ * ```typescript
3004
+ * const updated = await qp.updateFork("fork_123", {
3005
+ * tenant_id: "tenant_456",
3006
+ * content_json: newBlockNoteContent,
3007
+ * });
3008
+ * ```
3009
+ */
3010
+ async updateFork(forkId, input, options, signal) {
3011
+ return await updateFork(
3012
+ this.client,
3013
+ forkId,
3014
+ input,
3015
+ options,
3016
+ signal
3017
+ );
3018
+ }
3019
+ /**
3020
+ * Rollbacks a fork to the original dashboard.
3021
+ * This deletes the fork and the customer will see the original again.
3022
+ *
3023
+ * @param forkId - Fork ID to rollback
3024
+ * @param tenantId - Tenant ID
3025
+ * @param options - Additional auth options
3026
+ * @param signal - Optional AbortSignal for cancellation
3027
+ * @returns Original dashboard
3028
+ *
3029
+ * @example
3030
+ * ```typescript
3031
+ * const original = await qp.rollbackFork("fork_123", "tenant_456");
3032
+ * ```
3033
+ */
3034
+ async rollbackFork(forkId, tenantId, options, signal) {
3035
+ return await rollbackFork(
3036
+ this.client,
3037
+ forkId,
3038
+ tenantId,
3039
+ options,
3040
+ signal
3041
+ );
3042
+ }
3043
+ /**
3044
+ * Deletes a customer fork.
3045
+ *
3046
+ * @param forkId - Fork ID to delete
3047
+ * @param tenantId - Tenant ID
3048
+ * @param options - Additional auth options
3049
+ * @param signal - Optional AbortSignal for cancellation
3050
+ *
3051
+ * @example
3052
+ * ```typescript
3053
+ * await qp.deleteFork("fork_123", "tenant_456");
3054
+ * ```
3055
+ */
3056
+ async deleteFork(forkId, tenantId, options, signal) {
3057
+ await deleteFork(
3058
+ this.client,
3059
+ forkId,
3060
+ tenantId,
3061
+ options,
3062
+ signal
3063
+ );
3064
+ }
3065
+ /**
3066
+ * Lists all customer forks for a tenant.
3067
+ *
3068
+ * @param tenantId - Tenant ID
3069
+ * @param options - Additional auth options
3070
+ * @param signal - Optional AbortSignal for cancellation
3071
+ * @returns Array of forks
3072
+ *
3073
+ * @example
3074
+ * ```typescript
3075
+ * const forks = await qp.listForksForTenant("tenant_456");
3076
+ * ```
3077
+ */
3078
+ async listForksForTenant(tenantId, options, signal) {
3079
+ return await listForksForTenant(
3080
+ this.client,
3081
+ tenantId,
3082
+ options,
3083
+ signal
3084
+ );
3085
+ }
2432
3086
  };
2433
3087
  // Annotate the CommonJS export names for ESM import in node:
2434
3088
  0 && (module.exports = {
3089
+ CallbackApiClient,
2435
3090
  ClickHouseAdapter,
2436
3091
  PostgresAdapter,
2437
3092
  QueryErrorCode,