@querypanel/node-sdk 1.0.44 → 1.0.45

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
@@ -587,6 +587,22 @@ var ApiClient = class {
587
587
  signal
588
588
  });
589
589
  }
590
+ async postWithHeaders(path, body, tenantId, userId, scopes, signal, sessionId) {
591
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
592
+ method: "POST",
593
+ headers: await this.buildHeaders(
594
+ tenantId,
595
+ userId,
596
+ scopes,
597
+ true,
598
+ sessionId
599
+ ),
600
+ body: JSON.stringify(body ?? {}),
601
+ signal
602
+ });
603
+ const data = await this.parseResponse(response);
604
+ return { data, headers: response.headers };
605
+ }
590
606
  async put(path, body, tenantId, userId, scopes, signal, sessionId) {
591
607
  return await this.request(path, {
592
608
  method: "PUT",
@@ -601,6 +617,20 @@ var ApiClient = class {
601
617
  signal
602
618
  });
603
619
  }
620
+ async patch(path, body, tenantId, userId, scopes, signal, sessionId) {
621
+ return await this.request(path, {
622
+ method: "PATCH",
623
+ headers: await this.buildHeaders(
624
+ tenantId,
625
+ userId,
626
+ scopes,
627
+ true,
628
+ sessionId
629
+ ),
630
+ body: JSON.stringify(body ?? {}),
631
+ signal
632
+ });
633
+ }
604
634
  async delete(path, tenantId, userId, scopes, signal, sessionId) {
605
635
  return await this.request(path, {
606
636
  method: "DELETE",
@@ -616,6 +646,9 @@ var ApiClient = class {
616
646
  }
617
647
  async request(path, init) {
618
648
  const response = await this.fetchImpl(`${this.baseUrl}${path}`, init);
649
+ return await this.parseResponse(response);
650
+ }
651
+ async parseResponse(response) {
619
652
  const text = await response.text();
620
653
  let json;
621
654
  try {
@@ -1197,6 +1230,7 @@ import crypto3 from "crypto";
1197
1230
  async function ask(client, queryEngine, question, options, signal) {
1198
1231
  const tenantId = resolveTenantId4(client, options.tenantId);
1199
1232
  const sessionId = crypto3.randomUUID();
1233
+ const querypanelSessionId = options.querypanelSessionId ?? sessionId;
1200
1234
  const maxRetry = options.maxRetry ?? 0;
1201
1235
  let attempt = 0;
1202
1236
  let lastError = options.lastError;
@@ -1213,10 +1247,11 @@ async function ask(client, queryEngine, question, options, signal) {
1213
1247
  enforceTenantIsolation: metadata.enforceTenantIsolation
1214
1248
  };
1215
1249
  }
1216
- const queryResponse = await client.post(
1250
+ const queryResponse = await client.postWithHeaders(
1217
1251
  "/query",
1218
1252
  {
1219
1253
  question,
1254
+ ...querypanelSessionId ? { session_id: querypanelSessionId } : {},
1220
1255
  ...lastError ? { last_error: lastError } : {},
1221
1256
  ...previousSql ? { previous_sql: previousSql } : {},
1222
1257
  ...options.maxRetry ? { max_retry: options.maxRetry } : {},
@@ -1230,24 +1265,30 @@ async function ask(client, queryEngine, question, options, signal) {
1230
1265
  signal,
1231
1266
  sessionId
1232
1267
  );
1233
- if (!queryResponse.success) {
1268
+ const responseSessionId = queryResponse.headers.get("x-querypanel-session-id") ?? querypanelSessionId;
1269
+ if (!queryResponse.data.success) {
1234
1270
  throw new QueryPipelineError(
1235
- queryResponse.error || "Query generation failed",
1236
- queryResponse.code || "INTERNAL_ERROR",
1237
- queryResponse.details
1271
+ queryResponse.data.error || "Query generation failed",
1272
+ queryResponse.data.code || "INTERNAL_ERROR",
1273
+ queryResponse.data.details
1238
1274
  );
1239
1275
  }
1240
- const dbName = queryResponse.database ?? options.database ?? queryEngine.getDefaultDatabase();
1276
+ const sql = queryResponse.data.sql;
1277
+ const dialect = queryResponse.data.dialect;
1278
+ if (!sql || !dialect) {
1279
+ throw new Error("Query response missing required SQL or dialect");
1280
+ }
1281
+ const dbName = queryResponse.data.database ?? options.database ?? queryEngine.getDefaultDatabase();
1241
1282
  if (!dbName) {
1242
1283
  throw new Error(
1243
1284
  "No database attached. Call attachPostgres/attachClickhouse first."
1244
1285
  );
1245
1286
  }
1246
- const paramMetadata = Array.isArray(queryResponse.params) ? queryResponse.params : [];
1287
+ const paramMetadata = Array.isArray(queryResponse.data.params) ? queryResponse.data.params : [];
1247
1288
  const paramValues = queryEngine.mapGeneratedParams(paramMetadata);
1248
1289
  try {
1249
1290
  const execution = await queryEngine.validateAndExecute(
1250
- queryResponse.sql,
1291
+ sql,
1251
1292
  paramValues,
1252
1293
  dbName,
1253
1294
  tenantId
@@ -1264,12 +1305,12 @@ async function ask(client, queryEngine, question, options, signal) {
1264
1305
  "/vizspec",
1265
1306
  {
1266
1307
  question,
1267
- sql: queryResponse.sql,
1268
- rationale: queryResponse.rationale,
1308
+ sql,
1309
+ rationale: queryResponse.data.rationale,
1269
1310
  fields: execution.fields,
1270
1311
  rows: anonymizeResults(rows),
1271
1312
  max_retries: options.chartMaxRetries ?? 3,
1272
- query_id: queryResponse.queryId
1313
+ query_id: queryResponse.data.queryId
1273
1314
  },
1274
1315
  tenantId,
1275
1316
  options.userId,
@@ -1287,12 +1328,12 @@ async function ask(client, queryEngine, question, options, signal) {
1287
1328
  "/chart",
1288
1329
  {
1289
1330
  question,
1290
- sql: queryResponse.sql,
1291
- rationale: queryResponse.rationale,
1331
+ sql,
1332
+ rationale: queryResponse.data.rationale,
1292
1333
  fields: execution.fields,
1293
1334
  rows: anonymizeResults(rows),
1294
1335
  max_retries: options.chartMaxRetries ?? 3,
1295
- query_id: queryResponse.queryId
1336
+ query_id: queryResponse.data.queryId
1296
1337
  },
1297
1338
  tenantId,
1298
1339
  options.userId,
@@ -1311,18 +1352,19 @@ async function ask(client, queryEngine, question, options, signal) {
1311
1352
  }
1312
1353
  }
1313
1354
  return {
1314
- sql: queryResponse.sql,
1355
+ sql,
1315
1356
  params: paramValues,
1316
1357
  paramMetadata,
1317
- rationale: queryResponse.rationale,
1318
- dialect: queryResponse.dialect,
1319
- queryId: queryResponse.queryId,
1358
+ rationale: queryResponse.data.rationale,
1359
+ dialect,
1360
+ queryId: queryResponse.data.queryId,
1320
1361
  rows,
1321
1362
  fields: execution.fields,
1322
1363
  chart,
1323
- context: queryResponse.context,
1364
+ context: queryResponse.data.context,
1324
1365
  attempts: attempt + 1,
1325
- target_db: dbName
1366
+ target_db: dbName,
1367
+ querypanelSessionId: responseSessionId ?? void 0
1326
1368
  };
1327
1369
  } catch (error) {
1328
1370
  attempt++;
@@ -1330,7 +1372,7 @@ async function ask(client, queryEngine, question, options, signal) {
1330
1372
  throw error;
1331
1373
  }
1332
1374
  lastError = error instanceof Error ? error.message : String(error);
1333
- previousSql = queryResponse.sql;
1375
+ previousSql = queryResponse.data.sql ?? previousSql;
1334
1376
  console.warn(
1335
1377
  `SQL execution failed (attempt ${attempt}/${maxRetry + 1}): ${lastError}. Retrying...`
1336
1378
  );
@@ -1565,10 +1607,79 @@ async function modifyChart(client, queryEngine, input, options, signal) {
1565
1607
  };
1566
1608
  }
1567
1609
 
1610
+ // src/routes/sessions.ts
1611
+ async function listSessions(client, options, signal) {
1612
+ const tenantId = resolveTenantId6(client, options?.tenantId);
1613
+ const params = new URLSearchParams();
1614
+ if (options?.pagination?.page)
1615
+ params.set("page", `${options.pagination.page}`);
1616
+ if (options?.pagination?.limit)
1617
+ params.set("limit", `${options.pagination.limit}`);
1618
+ if (options?.sortBy) params.set("sort_by", options.sortBy);
1619
+ if (options?.sortDir) params.set("sort_dir", options.sortDir);
1620
+ if (options?.title) params.set("title", options.title);
1621
+ if (options?.userFilter) params.set("user_id", options.userFilter);
1622
+ if (options?.createdFrom) params.set("created_from", options.createdFrom);
1623
+ if (options?.createdTo) params.set("created_to", options.createdTo);
1624
+ if (options?.updatedFrom) params.set("updated_from", options.updatedFrom);
1625
+ if (options?.updatedTo) params.set("updated_to", options.updatedTo);
1626
+ return await client.get(
1627
+ `/sessions${params.toString() ? `?${params.toString()}` : ""}`,
1628
+ tenantId,
1629
+ options?.userId,
1630
+ options?.scopes,
1631
+ signal
1632
+ );
1633
+ }
1634
+ async function getSession(client, sessionId, options, signal) {
1635
+ const tenantId = resolveTenantId6(client, options?.tenantId);
1636
+ const params = new URLSearchParams();
1637
+ if (options?.includeTurns !== void 0) {
1638
+ params.set("include_turns", `${options.includeTurns}`);
1639
+ }
1640
+ return await client.get(
1641
+ `/sessions/${encodeURIComponent(sessionId)}${params.toString() ? `?${params.toString()}` : ""}`,
1642
+ tenantId,
1643
+ options?.userId,
1644
+ options?.scopes,
1645
+ signal
1646
+ );
1647
+ }
1648
+ async function updateSession(client, sessionId, body, options, signal) {
1649
+ const tenantId = resolveTenantId6(client, options?.tenantId);
1650
+ return await client.patch(
1651
+ `/sessions/${encodeURIComponent(sessionId)}`,
1652
+ body,
1653
+ tenantId,
1654
+ options?.userId,
1655
+ options?.scopes,
1656
+ signal
1657
+ );
1658
+ }
1659
+ async function deleteSession(client, sessionId, options, signal) {
1660
+ const tenantId = resolveTenantId6(client, options?.tenantId);
1661
+ await client.delete(
1662
+ `/sessions/${encodeURIComponent(sessionId)}`,
1663
+ tenantId,
1664
+ options?.userId,
1665
+ options?.scopes,
1666
+ signal
1667
+ );
1668
+ }
1669
+ function resolveTenantId6(client, tenantId) {
1670
+ const resolved = tenantId ?? client.getDefaultTenantId();
1671
+ if (!resolved) {
1672
+ throw new Error(
1673
+ "tenantId is required. Provide it per request or via defaultTenantId option."
1674
+ );
1675
+ }
1676
+ return resolved;
1677
+ }
1678
+
1568
1679
  // src/routes/vizspec.ts
1569
1680
  import crypto5 from "crypto";
1570
1681
  async function generateVizSpec(client, input, options, signal) {
1571
- const tenantId = resolveTenantId6(client, options?.tenantId);
1682
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1572
1683
  const sessionId = crypto5.randomUUID();
1573
1684
  const response = await client.post(
1574
1685
  "/vizspec",
@@ -1589,7 +1700,7 @@ async function generateVizSpec(client, input, options, signal) {
1589
1700
  );
1590
1701
  return response;
1591
1702
  }
1592
- function resolveTenantId6(client, tenantId) {
1703
+ function resolveTenantId7(client, tenantId) {
1593
1704
  const resolved = tenantId ?? client.getDefaultTenantId();
1594
1705
  if (!resolved) {
1595
1706
  throw new Error(
@@ -1702,6 +1813,7 @@ var QueryPanelSdkAPI = class {
1702
1813
  * console.log(result.sql); // Generated SQL
1703
1814
  * console.log(result.rows); // Query results
1704
1815
  * console.log(result.chart); // Vega-Lite chart spec
1816
+ * console.log(result.querypanelSessionId); // Use for follow-ups
1705
1817
  *
1706
1818
  * // With automatic SQL repair on failure
1707
1819
  * const result = await qp.ask("Show monthly trends", {
@@ -1877,6 +1989,92 @@ var QueryPanelSdkAPI = class {
1877
1989
  signal
1878
1990
  );
1879
1991
  }
1992
+ // Session history CRUD operations
1993
+ /**
1994
+ * Lists query sessions with pagination and filtering.
1995
+ *
1996
+ * @param options - Filtering, pagination, and sort options
1997
+ * @param signal - Optional AbortSignal for cancellation
1998
+ * @returns Paginated list of sessions
1999
+ *
2000
+ * @example
2001
+ * ```typescript
2002
+ * const sessions = await qp.listSessions({
2003
+ * tenantId: "tenant_123",
2004
+ * pagination: { page: 1, limit: 20 },
2005
+ * sortBy: "updated_at",
2006
+ * });
2007
+ * ```
2008
+ */
2009
+ async listSessions(options, signal) {
2010
+ return await listSessions(this.client, options, signal);
2011
+ }
2012
+ /**
2013
+ * Retrieves a session by session_id with optional turn history.
2014
+ *
2015
+ * @param sessionId - QueryPanel session identifier used in ask()
2016
+ * @param options - Tenant, user, scopes, and includeTurns flag
2017
+ * @param signal - Optional AbortSignal for cancellation
2018
+ * @returns Session metadata with optional turns
2019
+ *
2020
+ * @example
2021
+ * ```typescript
2022
+ * const session = await qp.getSession("session_123", {
2023
+ * tenantId: "tenant_123",
2024
+ * includeTurns: true,
2025
+ * });
2026
+ * ```
2027
+ */
2028
+ async getSession(sessionId, options, signal) {
2029
+ return await getSession(
2030
+ this.client,
2031
+ sessionId,
2032
+ options,
2033
+ signal
2034
+ );
2035
+ }
2036
+ /**
2037
+ * Updates session metadata (title).
2038
+ *
2039
+ * @param sessionId - QueryPanel session identifier to update
2040
+ * @param body - Fields to update
2041
+ * @param options - Tenant, user, and scope options
2042
+ * @param signal - Optional AbortSignal for cancellation
2043
+ * @returns Updated session
2044
+ *
2045
+ * @example
2046
+ * ```typescript
2047
+ * const updated = await qp.updateSession(
2048
+ * "session_123",
2049
+ * { title: "Q4 Revenue Analysis" },
2050
+ * { tenantId: "tenant_123" },
2051
+ * );
2052
+ * ```
2053
+ */
2054
+ async updateSession(sessionId, body, options, signal) {
2055
+ return await updateSession(
2056
+ this.client,
2057
+ sessionId,
2058
+ body,
2059
+ options,
2060
+ signal
2061
+ );
2062
+ }
2063
+ /**
2064
+ * Deletes a session and its turn history.
2065
+ *
2066
+ * @param sessionId - QueryPanel session identifier to delete
2067
+ * @param options - Tenant, user, and scope options
2068
+ * @param signal - Optional AbortSignal for cancellation
2069
+ *
2070
+ * @example
2071
+ * ```typescript
2072
+ * await qp.deleteSession("session_123", { tenantId: "tenant_123" });
2073
+ * ```
2074
+ */
2075
+ async deleteSession(sessionId, options, signal) {
2076
+ await deleteSession(this.client, sessionId, options, signal);
2077
+ }
1880
2078
  /**
1881
2079
  * Retrieves a single chart by ID with live data.
1882
2080
  *