@querypanel/node-sdk 1.0.56 → 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
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ CallbackApiClient: () => CallbackApiClient,
33
34
  ClickHouseAdapter: () => ClickHouseAdapter,
34
35
  PostgresAdapter: () => PostgresAdapter,
35
36
  QueryErrorCode: () => QueryErrorCode,
@@ -367,29 +368,31 @@ var PostgresAdapter = class {
367
368
  }
368
369
  }
369
370
  /**
370
- * Convert named params to positional array for PostgreSQL
371
- * 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.
372
376
  */
373
377
  convertNamedToPositionalParams(params) {
374
378
  const numericKeys = Object.keys(params).filter((k) => /^\d+$/.test(k)).map((k) => Number.parseInt(k, 10)).sort((a, b) => a - b);
375
- const namedKeys = Object.keys(params).filter((k) => !/^\d+$/.test(k)).sort();
376
- const positionalParams = [];
377
- for (const key of numericKeys) {
378
- let val = params[String(key)];
379
- if (typeof val === "string") {
380
- const match = val.match(/^<([a-zA-Z0-9_]+)>$/);
381
- const namedKey = match?.[1];
382
- if (namedKey && namedKey in params) {
383
- 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
+ }
384
389
  }
390
+ positionalParams.push(val);
385
391
  }
386
- positionalParams.push(val);
387
- }
388
- for (const key of namedKeys) {
389
- const val = params[key];
390
- positionalParams.push(val);
392
+ return positionalParams;
391
393
  }
392
- return positionalParams;
394
+ const namedKeys = Object.keys(params).filter((k) => !/^\d+$/.test(k)).sort();
395
+ return namedKeys.map((key) => params[key]);
393
396
  }
394
397
  async validate(sql, params) {
395
398
  let paramArray;
@@ -601,6 +604,13 @@ var ApiClient = class {
601
604
  getDefaultTenantId() {
602
605
  return this.defaultTenantId;
603
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
+ }
604
614
  async get(path, tenantId, userId, scopes, signal, sessionId) {
605
615
  return await this.request(path, {
606
616
  method: "GET",
@@ -813,6 +823,111 @@ var ApiClient = class {
813
823
  }
814
824
  };
815
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
+
816
931
  // src/core/query-engine.ts
817
932
  var QueryEngine = class {
818
933
  databases = /* @__PURE__ */ new Map();
@@ -875,6 +990,12 @@ var QueryEngine = class {
875
990
  return [];
876
991
  }
877
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
+ */
878
999
  mapGeneratedParams(params) {
879
1000
  const record = {};
880
1001
  params.forEach((param, index) => {
@@ -882,6 +1003,7 @@ var QueryEngine = class {
882
1003
  if (value === void 0) {
883
1004
  return;
884
1005
  }
1006
+ record[String(index + 1)] = value;
885
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);
886
1008
  const key = nameCandidate.replace(/[{}:$]/g, "").trim();
887
1009
  record[key] = value;
@@ -1210,10 +1332,161 @@ function resolveTenantId2(client, tenantId) {
1210
1332
  return resolved;
1211
1333
  }
1212
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
+
1213
1486
  // src/routes/ingest.ts
1214
1487
  var import_node_crypto2 = __toESM(require("crypto"), 1);
1215
1488
  async function syncSchema(client, queryEngine, databaseName, options, signal) {
1216
- const tenantId = resolveTenantId3(client, options.tenantId);
1489
+ const tenantId = resolveTenantId4(client, options.tenantId);
1217
1490
  const adapter = queryEngine.getDatabase(databaseName);
1218
1491
  const metadata = queryEngine.getDatabaseMetadata(databaseName);
1219
1492
  const introspection = await adapter.introspect(
@@ -1235,7 +1508,7 @@ async function syncSchema(client, queryEngine, databaseName, options, signal) {
1235
1508
  );
1236
1509
  return response;
1237
1510
  }
1238
- function resolveTenantId3(client, tenantId) {
1511
+ function resolveTenantId4(client, tenantId) {
1239
1512
  const resolved = tenantId ?? client.getDefaultTenantId();
1240
1513
  if (!resolved) {
1241
1514
  throw new Error(
@@ -1277,7 +1550,7 @@ var import_node_crypto4 = __toESM(require("crypto"), 1);
1277
1550
  // src/routes/query.ts
1278
1551
  var import_node_crypto3 = __toESM(require("crypto"), 1);
1279
1552
  async function ask(client, queryEngine, question, options, signal) {
1280
- const tenantId = resolveTenantId4(client, options.tenantId);
1553
+ const tenantId = resolveTenantId5(client, options.tenantId);
1281
1554
  const sessionId = import_node_crypto3.default.randomUUID();
1282
1555
  const querypanelSessionId = options.querypanelSessionId ?? sessionId;
1283
1556
  const maxRetry = options.maxRetry ?? 0;
@@ -1432,7 +1705,7 @@ async function ask(client, queryEngine, question, options, signal) {
1432
1705
  }
1433
1706
  throw new Error("Unexpected error in ask retry loop");
1434
1707
  }
1435
- function resolveTenantId4(client, tenantId) {
1708
+ function resolveTenantId5(client, tenantId) {
1436
1709
  const resolved = tenantId ?? client.getDefaultTenantId();
1437
1710
  if (!resolved) {
1438
1711
  throw new Error(
@@ -1588,7 +1861,7 @@ function stripVizSpecOnlyHints(hints) {
1588
1861
  const { kind: _kind, ...rest } = hints;
1589
1862
  return rest;
1590
1863
  }
1591
- function resolveTenantId5(client, tenantId) {
1864
+ function resolveTenantId6(client, tenantId) {
1592
1865
  const resolved = tenantId ?? client.getDefaultTenantId();
1593
1866
  if (!resolved) {
1594
1867
  throw new Error(
@@ -1598,7 +1871,7 @@ function resolveTenantId5(client, tenantId) {
1598
1871
  return resolved;
1599
1872
  }
1600
1873
  async function modifyChart(client, queryEngine, input, options, signal) {
1601
- const tenantId = resolveTenantId5(client, options?.tenantId);
1874
+ const tenantId = resolveTenantId6(client, options?.tenantId);
1602
1875
  const sessionId = import_node_crypto4.default.randomUUID();
1603
1876
  const querypanelSessionId = options?.querypanelSessionId ?? sessionId;
1604
1877
  const hasQuerypanelSessionContext = !!options?.querypanelSessionId;
@@ -1766,7 +2039,7 @@ async function modifyChart(client, queryEngine, input, options, signal) {
1766
2039
 
1767
2040
  // src/routes/sessions.ts
1768
2041
  async function listSessions(client, options, signal) {
1769
- const tenantId = resolveTenantId6(client, options?.tenantId);
2042
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1770
2043
  const params = new URLSearchParams();
1771
2044
  if (options?.pagination?.page)
1772
2045
  params.set("page", `${options.pagination.page}`);
@@ -1789,7 +2062,7 @@ async function listSessions(client, options, signal) {
1789
2062
  );
1790
2063
  }
1791
2064
  async function getSession(client, sessionId, options, signal) {
1792
- const tenantId = resolveTenantId6(client, options?.tenantId);
2065
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1793
2066
  const params = new URLSearchParams();
1794
2067
  if (options?.includeTurns !== void 0) {
1795
2068
  params.set("include_turns", `${options.includeTurns}`);
@@ -1803,7 +2076,7 @@ async function getSession(client, sessionId, options, signal) {
1803
2076
  );
1804
2077
  }
1805
2078
  async function updateSession(client, sessionId, body, options, signal) {
1806
- const tenantId = resolveTenantId6(client, options?.tenantId);
2079
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1807
2080
  return await client.patch(
1808
2081
  `/sessions/${encodeURIComponent(sessionId)}`,
1809
2082
  body,
@@ -1814,7 +2087,7 @@ async function updateSession(client, sessionId, body, options, signal) {
1814
2087
  );
1815
2088
  }
1816
2089
  async function deleteSession(client, sessionId, options, signal) {
1817
- const tenantId = resolveTenantId6(client, options?.tenantId);
2090
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1818
2091
  await client.delete(
1819
2092
  `/sessions/${encodeURIComponent(sessionId)}`,
1820
2093
  tenantId,
@@ -1823,7 +2096,7 @@ async function deleteSession(client, sessionId, options, signal) {
1823
2096
  signal
1824
2097
  );
1825
2098
  }
1826
- function resolveTenantId6(client, tenantId) {
2099
+ function resolveTenantId7(client, tenantId) {
1827
2100
  const resolved = tenantId ?? client.getDefaultTenantId();
1828
2101
  if (!resolved) {
1829
2102
  throw new Error(
@@ -1836,7 +2109,7 @@ function resolveTenantId6(client, tenantId) {
1836
2109
  // src/routes/vizspec.ts
1837
2110
  var import_node_crypto5 = __toESM(require("crypto"), 1);
1838
2111
  async function generateVizSpec(client, input, options, signal) {
1839
- const tenantId = resolveTenantId7(client, options?.tenantId);
2112
+ const tenantId = resolveTenantId8(client, options?.tenantId);
1840
2113
  const sessionId = import_node_crypto5.default.randomUUID();
1841
2114
  const response = await client.post(
1842
2115
  "/vizspec",
@@ -1858,7 +2131,7 @@ async function generateVizSpec(client, input, options, signal) {
1858
2131
  );
1859
2132
  return response;
1860
2133
  }
1861
- function resolveTenantId7(client, tenantId) {
2134
+ function resolveTenantId8(client, tenantId) {
1862
2135
  const resolved = tenantId ?? client.getDefaultTenantId();
1863
2136
  if (!resolved) {
1864
2137
  throw new Error(
@@ -1869,13 +2142,62 @@ function resolveTenantId7(client, tenantId) {
1869
2142
  }
1870
2143
 
1871
2144
  // src/index.ts
1872
- var QueryPanelSdkAPI = class {
2145
+ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
1873
2146
  client;
1874
2147
  queryEngine;
1875
2148
  constructor(baseUrl, privateKey, organizationId, options) {
1876
- 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");
1877
2162
  this.queryEngine = new QueryEngine();
1878
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
+ }
1879
2201
  // Database attachment methods
1880
2202
  attachClickhouse(name, clientFn, options) {
1881
2203
  const adapter = new ClickHouseAdapter(clientFn, options);
@@ -1989,6 +2311,48 @@ var QueryPanelSdkAPI = class {
1989
2311
  signal
1990
2312
  );
1991
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
+ }
1992
2356
  // VizSpec generation
1993
2357
  /**
1994
2358
  * Generates a VizSpec visualization specification from query results.
@@ -2432,9 +2796,297 @@ var QueryPanelSdkAPI = class {
2432
2796
  async deleteActiveChart(id, options, signal) {
2433
2797
  await deleteActiveChart(this.client, id, options, signal);
2434
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
+ }
2435
3086
  };
2436
3087
  // Annotate the CommonJS export names for ESM import in node:
2437
3088
  0 && (module.exports = {
3089
+ CallbackApiClient,
2438
3090
  ClickHouseAdapter,
2439
3091
  PostgresAdapter,
2440
3092
  QueryErrorCode,