@querypanel/node-sdk 1.0.56 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,3 +1,183 @@
1
+ // src/adapters/bigquery.ts
2
+ var BigQueryAdapter = class {
3
+ constructor(clientFn, options) {
4
+ this.clientFn = clientFn;
5
+ this.projectId = options.projectId;
6
+ this.datasetProjectId = options.datasetProjectId ?? options.projectId;
7
+ this.defaultDataset = options.dataset;
8
+ this.databaseName = options.database ?? options.dataset;
9
+ this.kind = options.kind ?? "bigquery";
10
+ if (options.allowedTables) {
11
+ this.allowedTables = normalizeTableFilter(
12
+ options.allowedTables,
13
+ this.defaultDataset
14
+ );
15
+ }
16
+ }
17
+ projectId;
18
+ datasetProjectId;
19
+ defaultDataset;
20
+ databaseName;
21
+ kind;
22
+ allowedTables;
23
+ async execute(sql, params) {
24
+ if (this.allowedTables) {
25
+ this.validateQueryTables(sql);
26
+ }
27
+ const result = await this.clientFn({
28
+ query: sql,
29
+ params,
30
+ dryRun: false
31
+ });
32
+ return { fields: result.fields, rows: result.rows };
33
+ }
34
+ async validate(sql, params) {
35
+ await this.clientFn({
36
+ query: sql,
37
+ params,
38
+ dryRun: true
39
+ });
40
+ }
41
+ getDialect() {
42
+ return "bigquery";
43
+ }
44
+ /**
45
+ * Introspect using BigQuery INFORMATION_SCHEMA (TABLES and COLUMNS).
46
+ * Restricts to the default dataset (or allowedTables) and returns tables/columns only.
47
+ */
48
+ async introspect(options) {
49
+ const tablesToIntrospect = options?.tables ? normalizeTableFilter(options.tables, this.defaultDataset) : this.allowedTables;
50
+ const normalizedTables = tablesToIntrospect ?? [];
51
+ const tablesSql = buildTablesQuery(
52
+ this.datasetProjectId,
53
+ this.defaultDataset,
54
+ normalizedTables
55
+ );
56
+ const tablesResult = await this.clientFn({
57
+ query: tablesSql,
58
+ dryRun: false
59
+ });
60
+ const tableRows = tablesResult.rows;
61
+ const columnsSql = buildColumnsQuery(
62
+ this.datasetProjectId,
63
+ this.defaultDataset,
64
+ normalizedTables
65
+ );
66
+ const columnsResult = await this.clientFn({
67
+ query: columnsSql,
68
+ dryRun: false
69
+ });
70
+ const columnRows = columnsResult.rows;
71
+ const tablesByKey = /* @__PURE__ */ new Map();
72
+ for (const row of tableRows) {
73
+ const key = tableKey(row.table_schema, row.table_name);
74
+ const table = {
75
+ name: row.table_name,
76
+ schema: row.table_schema,
77
+ type: asTableType(row.table_type),
78
+ columns: []
79
+ };
80
+ tablesByKey.set(key, table);
81
+ }
82
+ for (const row of columnRows) {
83
+ const key = tableKey(row.table_schema, row.table_name);
84
+ const table = tablesByKey.get(key);
85
+ if (!table) continue;
86
+ const column = {
87
+ name: row.column_name,
88
+ type: row.data_type,
89
+ isPrimaryKey: false
90
+ };
91
+ table.columns.push(column);
92
+ }
93
+ const tables = Array.from(tablesByKey.values()).sort((a, b) => {
94
+ if (a.schema === b.schema) {
95
+ return a.name.localeCompare(b.name);
96
+ }
97
+ return a.schema.localeCompare(b.schema);
98
+ });
99
+ return {
100
+ db: {
101
+ kind: this.kind,
102
+ name: this.databaseName
103
+ },
104
+ tables,
105
+ introspectedAt: (/* @__PURE__ */ new Date()).toISOString()
106
+ };
107
+ }
108
+ validateQueryTables(sql) {
109
+ if (!this.allowedTables || this.allowedTables.length === 0) {
110
+ return;
111
+ }
112
+ const allowedSet = new Set(
113
+ this.allowedTables.map((t) => tableKey(t.dataset, t.table))
114
+ );
115
+ const tablePattern = /(?:FROM|JOIN)\s+`?(?:[\w-]+\.)?([\w-]+)\.([\w-]+)`?|(?:FROM|JOIN)\s+`?([\w-]+)`?/gi;
116
+ const matches = sql.matchAll(tablePattern);
117
+ for (const match of matches) {
118
+ const dataset = match[1] ?? match[3] ?? this.defaultDataset;
119
+ const table = match[2] ?? match[3];
120
+ if (table) {
121
+ const key = tableKey(dataset, table);
122
+ if (!allowedSet.has(key)) {
123
+ throw new Error(
124
+ `Query references table "${dataset}.${table}" which is not in the allowed tables list`
125
+ );
126
+ }
127
+ }
128
+ }
129
+ }
130
+ };
131
+ function normalizeTableFilter(tables, defaultDataset) {
132
+ if (!tables?.length) return [];
133
+ const normalized = [];
134
+ const seen = /* @__PURE__ */ new Set();
135
+ for (const raw of tables) {
136
+ if (!raw?.trim()) continue;
137
+ const trimmed = raw.trim();
138
+ const parts = trimmed.split(".");
139
+ const table = parts.pop() ?? "";
140
+ const dataset = parts.pop() ?? defaultDataset;
141
+ const key = tableKey(dataset, table);
142
+ if (seen.has(key)) continue;
143
+ seen.add(key);
144
+ normalized.push({ dataset, table });
145
+ }
146
+ return normalized;
147
+ }
148
+ function tableKey(dataset, table) {
149
+ return `${dataset}.${table}`;
150
+ }
151
+ function buildTablesQuery(datasetProjectId, dataset, tables) {
152
+ const qualifier = `\`${datasetProjectId}.${dataset}.INFORMATION_SCHEMA.TABLES\``;
153
+ const filter = tables.length > 0 ? ` AND table_name IN (${tables.map((t) => `'${escapeSql(t.table)}'`).join(", ")})` : "";
154
+ return `SELECT table_name, table_schema, table_type
155
+ FROM ${qualifier}
156
+ WHERE table_schema = '${escapeSql(dataset)}'${filter}
157
+ ORDER BY table_schema, table_name`;
158
+ }
159
+ function buildColumnsQuery(datasetProjectId, dataset, tables) {
160
+ const qualifier = `\`${datasetProjectId}.${dataset}.INFORMATION_SCHEMA.COLUMNS\``;
161
+ const filter = tables.length > 0 ? ` AND table_name IN (${tables.map((t) => `'${escapeSql(t.table)}'`).join(", ")})` : "";
162
+ return `SELECT table_name, table_schema, column_name, data_type, ordinal_position, is_nullable
163
+ FROM ${qualifier}
164
+ WHERE table_schema = '${escapeSql(dataset)}'${filter}
165
+ ORDER BY table_schema, table_name, ordinal_position`;
166
+ }
167
+ function escapeSql(value) {
168
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "''");
169
+ }
170
+ function asTableType(tableType) {
171
+ const normalized = tableType?.toLowerCase() ?? "";
172
+ if (normalized.includes("view")) {
173
+ return normalized.includes("materialized") ? "materialized_view" : "view";
174
+ }
175
+ if (normalized.includes("external") || normalized.includes("snapshot") || normalized.includes("clone")) {
176
+ return "table";
177
+ }
178
+ return "table";
179
+ }
180
+
1
181
  // src/utils/clickhouse.ts
2
182
  var WRAPPER_REGEX = /^(Nullable|LowCardinality|SimpleAggregateFunction)\((.+)\)$/i;
3
183
  function unwrapTypeModifiers(type) {
@@ -67,7 +247,7 @@ var ClickHouseAdapter = class {
67
247
  this.defaultFormat = options.defaultFormat ?? "JSONEachRow";
68
248
  this.kind = options.kind ?? "clickhouse";
69
249
  if (options.allowedTables) {
70
- this.allowedTables = normalizeTableFilter(options.allowedTables);
250
+ this.allowedTables = normalizeTableFilter2(options.allowedTables);
71
251
  }
72
252
  }
73
253
  databaseName;
@@ -105,7 +285,7 @@ var ClickHouseAdapter = class {
105
285
  * No indexes, constraints, or statistics
106
286
  */
107
287
  async introspect(options) {
108
- const tablesToIntrospect = options?.tables ? normalizeTableFilter(options.tables) : this.allowedTables;
288
+ const tablesToIntrospect = options?.tables ? normalizeTableFilter2(options.tables) : this.allowedTables;
109
289
  const allowTables = tablesToIntrospect ?? [];
110
290
  const hasFilter = allowTables.length > 0;
111
291
  const queryParams = {
@@ -145,7 +325,7 @@ var ClickHouseAdapter = class {
145
325
  const base = {
146
326
  name: table.name,
147
327
  schema: this.databaseName,
148
- type: asTableType(table.engine),
328
+ type: asTableType2(table.engine),
149
329
  columns: tableColumns
150
330
  };
151
331
  const comment = sanitize(table.comment);
@@ -223,7 +403,7 @@ function normalizePayload(payload) {
223
403
  }
224
404
  return [];
225
405
  }
226
- function normalizeTableFilter(tables) {
406
+ function normalizeTableFilter2(tables) {
227
407
  if (!tables?.length) return [];
228
408
  const seen = /* @__PURE__ */ new Set();
229
409
  const normalized = [];
@@ -251,7 +431,7 @@ function transformColumnRow(row) {
251
431
  if (comment !== void 0) column.comment = comment;
252
432
  return column;
253
433
  }
254
- function asTableType(engine) {
434
+ function asTableType2(engine) {
255
435
  if (typeof engine === "string") {
256
436
  const normalized = engine.toLowerCase();
257
437
  if (normalized.includes("view")) {
@@ -280,7 +460,7 @@ var PostgresAdapter = class {
280
460
  this.defaultSchema = options.defaultSchema ?? "public";
281
461
  this.kind = options.kind ?? "postgres";
282
462
  if (options.allowedTables) {
283
- this.allowedTables = normalizeTableFilter2(
463
+ this.allowedTables = normalizeTableFilter3(
284
464
  options.allowedTables,
285
465
  this.defaultSchema
286
466
  );
@@ -307,7 +487,7 @@ var PostgresAdapter = class {
307
487
  return;
308
488
  }
309
489
  const allowedSet = new Set(
310
- this.allowedTables.map((t) => tableKey(t.schema, t.table))
490
+ this.allowedTables.map((t) => tableKey2(t.schema, t.table))
311
491
  );
312
492
  const neutralizedSql = sql.replace(/EXTRACT\s*\([^)]*FROM\s+[^)]+\)/gi, "EXTRACT(/*neutralized*/)").replace(/SUBSTRING\s*\([^)]*FROM\s+[^)]+\)/gi, "SUBSTRING(/*neutralized*/)").replace(/TRIM\s*\([^)]*FROM\s+[^)]+\)/gi, "TRIM(/*neutralized*/)").replace(/POSITION\s*\([^)]*FROM\s+[^)]+\)/gi, "POSITION(/*neutralized*/)");
313
493
  const tablePattern = /(?:FROM|JOIN)\s+(?:ONLY\s+)?(?:([a-zA-Z_][a-zA-Z0-9_]*)\.)?(["']?[a-zA-Z_][a-zA-Z0-9_]*["']?)/gi;
@@ -316,7 +496,7 @@ var PostgresAdapter = class {
316
496
  const schema = match[1] ?? this.defaultSchema;
317
497
  const table = match[2]?.replace(/['"]/g, "");
318
498
  if (table) {
319
- const key = tableKey(schema, table);
499
+ const key = tableKey2(schema, table);
320
500
  if (!allowedSet.has(key)) {
321
501
  throw new Error(
322
502
  `Query references table "${schema}.${table}" which is not in the allowed tables list`
@@ -326,29 +506,31 @@ var PostgresAdapter = class {
326
506
  }
327
507
  }
328
508
  /**
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]
509
+ * Convert params to positional array for PostgreSQL.
510
+ * PostgreSQL expects $1, $2, $3 in SQL and an array of values [val1, val2, val3].
511
+ * When the record has numeric keys ("1", "2", "3") — e.g. from mapGeneratedParams —
512
+ * use only those in order so placeholder order matches SQL. Otherwise fall back to
513
+ * named keys in alphabetical order for backward compatibility.
331
514
  */
332
515
  convertNamedToPositionalParams(params) {
333
516
  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];
517
+ if (numericKeys.length > 0) {
518
+ const positionalParams = [];
519
+ for (const key of numericKeys) {
520
+ let val = params[String(key)];
521
+ if (typeof val === "string") {
522
+ const match = val.match(/^<([a-zA-Z0-9_]+)>$/);
523
+ const namedKey = match?.[1];
524
+ if (namedKey && namedKey in params) {
525
+ val = params[namedKey];
526
+ }
343
527
  }
528
+ positionalParams.push(val);
344
529
  }
345
- positionalParams.push(val);
530
+ return positionalParams;
346
531
  }
347
- for (const key of namedKeys) {
348
- const val = params[key];
349
- positionalParams.push(val);
350
- }
351
- return positionalParams;
532
+ const namedKeys = Object.keys(params).filter((k) => !/^\d+$/.test(k)).sort();
533
+ return namedKeys.map((key) => params[key]);
352
534
  }
353
535
  async validate(sql, params) {
354
536
  let paramArray;
@@ -365,23 +547,23 @@ var PostgresAdapter = class {
365
547
  * No indexes, constraints, or statistics
366
548
  */
367
549
  async introspect(options) {
368
- const tablesToIntrospect = options?.tables ? normalizeTableFilter2(options.tables, this.defaultSchema) : this.allowedTables;
550
+ const tablesToIntrospect = options?.tables ? normalizeTableFilter3(options.tables, this.defaultSchema) : this.allowedTables;
369
551
  const normalizedTables = tablesToIntrospect ?? [];
370
552
  const tablesResult = await this.clientFn(
371
- buildTablesQuery(normalizedTables)
553
+ buildTablesQuery2(normalizedTables)
372
554
  );
373
555
  const tableRows = tablesResult.rows;
374
556
  const columnsResult = await this.clientFn(
375
- buildColumnsQuery(normalizedTables)
557
+ buildColumnsQuery2(normalizedTables)
376
558
  );
377
559
  const columnRows = columnsResult.rows;
378
560
  const tablesByKey = /* @__PURE__ */ new Map();
379
561
  for (const row of tableRows) {
380
- const key = tableKey(row.schema_name, row.table_name);
562
+ const key = tableKey2(row.schema_name, row.table_name);
381
563
  const table = {
382
564
  name: row.table_name,
383
565
  schema: row.schema_name,
384
- type: asTableType2(row.table_type),
566
+ type: asTableType3(row.table_type),
385
567
  columns: []
386
568
  };
387
569
  const comment = sanitize2(row.comment);
@@ -391,7 +573,7 @@ var PostgresAdapter = class {
391
573
  tablesByKey.set(key, table);
392
574
  }
393
575
  for (const row of columnRows) {
394
- const key = tableKey(row.table_schema, row.table_name);
576
+ const key = tableKey2(row.table_schema, row.table_name);
395
577
  const table = tablesByKey.get(key);
396
578
  if (!table) continue;
397
579
  const column = {
@@ -421,7 +603,7 @@ var PostgresAdapter = class {
421
603
  };
422
604
  }
423
605
  };
424
- function normalizeTableFilter2(tables, defaultSchema) {
606
+ function normalizeTableFilter3(tables, defaultSchema) {
425
607
  if (!tables?.length) return [];
426
608
  const normalized = [];
427
609
  const seen = /* @__PURE__ */ new Set();
@@ -435,14 +617,14 @@ function normalizeTableFilter2(tables, defaultSchema) {
435
617
  if (!isSafeIdentifier(schema) || !isSafeIdentifier(table)) {
436
618
  continue;
437
619
  }
438
- const key = tableKey(schema, table);
620
+ const key = tableKey2(schema, table);
439
621
  if (seen.has(key)) continue;
440
622
  seen.add(key);
441
623
  normalized.push({ schema, table });
442
624
  }
443
625
  return normalized;
444
626
  }
445
- function buildTablesQuery(tables) {
627
+ function buildTablesQuery2(tables) {
446
628
  const filter = buildFilterClause(tables, "n.nspname", "c.relname");
447
629
  return `SELECT
448
630
  c.relname AS table_name,
@@ -461,7 +643,7 @@ function buildTablesQuery(tables) {
461
643
  ${filter}
462
644
  ORDER BY n.nspname, c.relname;`;
463
645
  }
464
- function buildColumnsQuery(tables) {
646
+ function buildColumnsQuery2(tables) {
465
647
  const filter = buildFilterClause(
466
648
  tables,
467
649
  "cols.table_schema",
@@ -506,13 +688,13 @@ function buildFilterClause(tables, schemaExpr, tableExpr) {
506
688
  });
507
689
  return `AND (${clauses.join(" OR ")})`;
508
690
  }
509
- function tableKey(schema, table) {
691
+ function tableKey2(schema, table) {
510
692
  return `${schema}.${table}`;
511
693
  }
512
694
  function isSafeIdentifier(value) {
513
695
  return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
514
696
  }
515
- function asTableType2(value) {
697
+ function asTableType3(value) {
516
698
  const normalized = value.toLowerCase();
517
699
  if (normalized.includes("view")) {
518
700
  return normalized.includes("materialized") ? "materialized_view" : "view";
@@ -560,6 +742,13 @@ var ApiClient = class {
560
742
  getDefaultTenantId() {
561
743
  return this.defaultTenantId;
562
744
  }
745
+ /**
746
+ * Create a JWT for the given tenant (and optional userId, scopes).
747
+ * Use this when you need to pass a token to the embed (e.g. frontend or demo).
748
+ */
749
+ async createJwt(tenantId, userId, scopes) {
750
+ return await this.generateJWT(tenantId, userId, scopes);
751
+ }
563
752
  async get(path, tenantId, userId, scopes, signal, sessionId) {
564
753
  return await this.request(path, {
565
754
  method: "GET",
@@ -772,6 +961,111 @@ var ApiClient = class {
772
961
  }
773
962
  };
774
963
 
964
+ // src/core/callback-client.ts
965
+ var CallbackApiClient = class {
966
+ requestHandler;
967
+ defaultTenantId;
968
+ constructor(requestHandler, options) {
969
+ this.requestHandler = requestHandler;
970
+ this.defaultTenantId = options?.defaultTenantId;
971
+ }
972
+ getDefaultTenantId() {
973
+ return this.defaultTenantId;
974
+ }
975
+ async request(opts) {
976
+ const { data, headers } = await this.requestHandler({
977
+ method: opts.method,
978
+ path: opts.path,
979
+ body: opts.body,
980
+ tenantId: opts.tenantId,
981
+ userId: opts.userId,
982
+ scopes: opts.scopes,
983
+ signal: opts.signal,
984
+ sessionId: opts.sessionId
985
+ });
986
+ return { data, headers };
987
+ }
988
+ async get(path, tenantId, userId, scopes, signal, sessionId) {
989
+ const { data } = await this.request({
990
+ method: "GET",
991
+ path,
992
+ tenantId,
993
+ userId,
994
+ scopes,
995
+ signal,
996
+ sessionId
997
+ });
998
+ return data;
999
+ }
1000
+ async post(path, body, tenantId, userId, scopes, signal, sessionId) {
1001
+ const { data } = await this.request({
1002
+ method: "POST",
1003
+ path,
1004
+ body,
1005
+ tenantId,
1006
+ userId,
1007
+ scopes,
1008
+ signal,
1009
+ sessionId
1010
+ });
1011
+ return data;
1012
+ }
1013
+ async postWithHeaders(path, body, tenantId, userId, scopes, signal, sessionId) {
1014
+ const { data, headers } = await this.request({
1015
+ method: "POST",
1016
+ path,
1017
+ body,
1018
+ tenantId,
1019
+ userId,
1020
+ scopes,
1021
+ signal,
1022
+ sessionId
1023
+ });
1024
+ return {
1025
+ data,
1026
+ headers: headers ?? new Headers()
1027
+ };
1028
+ }
1029
+ async put(path, body, tenantId, userId, scopes, signal, sessionId) {
1030
+ const { data } = await this.request({
1031
+ method: "PUT",
1032
+ path,
1033
+ body,
1034
+ tenantId,
1035
+ userId,
1036
+ scopes,
1037
+ signal,
1038
+ sessionId
1039
+ });
1040
+ return data;
1041
+ }
1042
+ async patch(path, body, tenantId, userId, scopes, signal, sessionId) {
1043
+ const { data } = await this.request({
1044
+ method: "PATCH",
1045
+ path,
1046
+ body,
1047
+ tenantId,
1048
+ userId,
1049
+ scopes,
1050
+ signal,
1051
+ sessionId
1052
+ });
1053
+ return data;
1054
+ }
1055
+ async delete(path, tenantId, userId, scopes, signal, sessionId) {
1056
+ const { data } = await this.request({
1057
+ method: "DELETE",
1058
+ path,
1059
+ tenantId,
1060
+ userId,
1061
+ scopes,
1062
+ signal,
1063
+ sessionId
1064
+ });
1065
+ return data;
1066
+ }
1067
+ };
1068
+
775
1069
  // src/core/query-engine.ts
776
1070
  var QueryEngine = class {
777
1071
  databases = /* @__PURE__ */ new Map();
@@ -834,6 +1128,12 @@ var QueryEngine = class {
834
1128
  return [];
835
1129
  }
836
1130
  }
1131
+ /**
1132
+ * Maps API params (array in SQL placeholder order $1, $2, $3) to ParamRecord.
1133
+ * Sets both numeric keys ("1", "2", "3") and named keys so that:
1134
+ * - Postgres adapter uses numeric order for positional params (matches SQL).
1135
+ * - Named keys remain for tenant isolation / other lookups.
1136
+ */
837
1137
  mapGeneratedParams(params) {
838
1138
  const record = {};
839
1139
  params.forEach((param, index) => {
@@ -841,6 +1141,7 @@ var QueryEngine = class {
841
1141
  if (value === void 0) {
842
1142
  return;
843
1143
  }
1144
+ record[String(index + 1)] = value;
844
1145
  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
1146
  const key = nameCandidate.replace(/[{}:$]/g, "").trim();
846
1147
  record[key] = value;
@@ -861,6 +1162,9 @@ var QueryEngine = class {
861
1162
  const paramKey = tenantField;
862
1163
  params[paramKey] = tenantId;
863
1164
  tenantPredicate = `${tenantField} = {${tenantField}:${metadata.tenantFieldType ?? "String"}}`;
1165
+ } else if (metadata.dialect === "bigquery") {
1166
+ params[tenantField] = tenantId;
1167
+ tenantPredicate = `${tenantField} = @${tenantField}`;
864
1168
  } else {
865
1169
  const escapedId = tenantId.replace(/'/g, "''");
866
1170
  tenantPredicate = `${tenantField} = '${escapedId}'`;
@@ -1149,30 +1453,181 @@ async function updateActiveChart(client, id, body, options, signal) {
1149
1453
  signal
1150
1454
  );
1151
1455
  }
1152
- async function deleteActiveChart(client, id, options, signal) {
1153
- const tenantId = resolveTenantId2(client, options?.tenantId);
1154
- await client.delete(
1155
- `/active-charts/${encodeURIComponent(id)}`,
1456
+ async function deleteActiveChart(client, id, options, signal) {
1457
+ const tenantId = resolveTenantId2(client, options?.tenantId);
1458
+ await client.delete(
1459
+ `/active-charts/${encodeURIComponent(id)}`,
1460
+ tenantId,
1461
+ options?.userId,
1462
+ options?.scopes,
1463
+ signal
1464
+ );
1465
+ }
1466
+ function resolveTenantId2(client, tenantId) {
1467
+ const resolved = tenantId ?? client.getDefaultTenantId();
1468
+ if (!resolved) {
1469
+ throw new Error(
1470
+ "tenantId is required. Provide it per request or via defaultTenantId option."
1471
+ );
1472
+ }
1473
+ return resolved;
1474
+ }
1475
+
1476
+ // src/routes/dashboards.ts
1477
+ function resolveTenantId3(client, tenantId) {
1478
+ const resolved = tenantId ?? client.getDefaultTenantId();
1479
+ if (resolved === void 0) {
1480
+ throw new Error("tenantId is required (option or client default)");
1481
+ }
1482
+ return resolved;
1483
+ }
1484
+ async function createDashboard(client, body, options, signal) {
1485
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1486
+ return await client.post(
1487
+ "/dashboards",
1488
+ body,
1489
+ tenantId,
1490
+ options?.userId,
1491
+ options?.scopes,
1492
+ signal
1493
+ );
1494
+ }
1495
+ async function listDashboards(client, options, signal) {
1496
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1497
+ const params = new URLSearchParams();
1498
+ if (options?.pagination?.page) {
1499
+ params.set("page", options.pagination.page.toString());
1500
+ }
1501
+ if (options?.pagination?.limit) {
1502
+ params.set("limit", options.pagination.limit.toString());
1503
+ }
1504
+ if (options?.status) {
1505
+ params.set("status", options.status);
1506
+ }
1507
+ if (options?.sortBy) {
1508
+ params.set("sort_by", options.sortBy);
1509
+ }
1510
+ if (options?.sortDir) {
1511
+ params.set("sort_dir", options.sortDir);
1512
+ }
1513
+ return await client.get(
1514
+ `/dashboards?${params.toString()}`,
1515
+ tenantId,
1516
+ options?.userId,
1517
+ options?.scopes,
1518
+ signal
1519
+ );
1520
+ }
1521
+ async function getDashboard(client, id, options, signal) {
1522
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1523
+ return await client.get(
1524
+ `/dashboards/${id}`,
1525
+ tenantId,
1526
+ options?.userId,
1527
+ options?.scopes,
1528
+ signal
1529
+ );
1530
+ }
1531
+ async function getDashboardForTenant(client, id, tenantId, options, signal) {
1532
+ const params = new URLSearchParams();
1533
+ params.set("tenant_id", tenantId);
1534
+ return await client.get(
1535
+ `/dashboards/${id}/for-tenant?${params.toString()}`,
1536
+ tenantId,
1537
+ options?.userId,
1538
+ options?.scopes,
1539
+ signal
1540
+ );
1541
+ }
1542
+ async function updateDashboard(client, id, body, options, signal) {
1543
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1544
+ return await client.put(
1545
+ `/dashboards/${id}`,
1546
+ body,
1547
+ tenantId,
1548
+ options?.userId,
1549
+ options?.scopes,
1550
+ signal
1551
+ );
1552
+ }
1553
+ async function updateDashboardStatus(client, id, status, options, signal) {
1554
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1555
+ return await client.patch(
1556
+ `/dashboards/${id}/status`,
1557
+ { status },
1558
+ tenantId,
1559
+ options?.userId,
1560
+ options?.scopes,
1561
+ signal
1562
+ );
1563
+ }
1564
+ async function deleteDashboard(client, id, options, signal) {
1565
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1566
+ await client.delete(
1567
+ `/dashboards/${id}`,
1568
+ tenantId,
1569
+ options?.userId,
1570
+ options?.scopes,
1571
+ signal
1572
+ );
1573
+ }
1574
+ async function forkDashboard(client, id, input, options, signal) {
1575
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1576
+ return await client.post(
1577
+ `/dashboards/${id}/fork`,
1578
+ input,
1579
+ tenantId,
1580
+ options?.userId,
1581
+ options?.scopes,
1582
+ signal
1583
+ );
1584
+ }
1585
+ async function updateFork(client, forkId, input, options, signal) {
1586
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1587
+ return await client.put(
1588
+ `/dashboards/forks/${forkId}`,
1589
+ input,
1590
+ tenantId,
1591
+ options?.userId,
1592
+ options?.scopes,
1593
+ signal
1594
+ );
1595
+ }
1596
+ async function rollbackFork(client, forkId, tenantId, options, signal) {
1597
+ return await client.post(
1598
+ `/dashboards/forks/${forkId}/rollback`,
1599
+ { tenant_id: tenantId },
1600
+ tenantId,
1601
+ options?.userId,
1602
+ options?.scopes,
1603
+ signal
1604
+ );
1605
+ }
1606
+ async function deleteFork(client, forkId, tenantId, options, signal) {
1607
+ const params = new URLSearchParams();
1608
+ params.set("tenant_id", tenantId);
1609
+ await client.delete(
1610
+ `/dashboards/forks/${forkId}?${params.toString()}`,
1611
+ tenantId,
1612
+ options?.userId,
1613
+ options?.scopes,
1614
+ signal
1615
+ );
1616
+ }
1617
+ async function listForksForTenant(client, tenantId, options, signal) {
1618
+ return await client.get(
1619
+ `/dashboards/customer/${tenantId}`,
1156
1620
  tenantId,
1157
1621
  options?.userId,
1158
1622
  options?.scopes,
1159
1623
  signal
1160
1624
  );
1161
1625
  }
1162
- function resolveTenantId2(client, tenantId) {
1163
- const resolved = tenantId ?? client.getDefaultTenantId();
1164
- if (!resolved) {
1165
- throw new Error(
1166
- "tenantId is required. Provide it per request or via defaultTenantId option."
1167
- );
1168
- }
1169
- return resolved;
1170
- }
1171
1626
 
1172
1627
  // src/routes/ingest.ts
1173
1628
  import crypto2 from "crypto";
1174
1629
  async function syncSchema(client, queryEngine, databaseName, options, signal) {
1175
- const tenantId = resolveTenantId3(client, options.tenantId);
1630
+ const tenantId = resolveTenantId4(client, options.tenantId);
1176
1631
  const adapter = queryEngine.getDatabase(databaseName);
1177
1632
  const metadata = queryEngine.getDatabaseMetadata(databaseName);
1178
1633
  const introspection = await adapter.introspect(
@@ -1194,7 +1649,7 @@ async function syncSchema(client, queryEngine, databaseName, options, signal) {
1194
1649
  );
1195
1650
  return response;
1196
1651
  }
1197
- function resolveTenantId3(client, tenantId) {
1652
+ function resolveTenantId4(client, tenantId) {
1198
1653
  const resolved = tenantId ?? client.getDefaultTenantId();
1199
1654
  if (!resolved) {
1200
1655
  throw new Error(
@@ -1236,7 +1691,7 @@ import crypto4 from "crypto";
1236
1691
  // src/routes/query.ts
1237
1692
  import crypto3 from "crypto";
1238
1693
  async function ask(client, queryEngine, question, options, signal) {
1239
- const tenantId = resolveTenantId4(client, options.tenantId);
1694
+ const tenantId = resolveTenantId5(client, options.tenantId);
1240
1695
  const sessionId = crypto3.randomUUID();
1241
1696
  const querypanelSessionId = options.querypanelSessionId ?? sessionId;
1242
1697
  const maxRetry = options.maxRetry ?? 0;
@@ -1290,7 +1745,7 @@ async function ask(client, queryEngine, question, options, signal) {
1290
1745
  const dbName = queryResponse.data.database ?? options.database ?? queryEngine.getDefaultDatabase();
1291
1746
  if (!dbName) {
1292
1747
  throw new Error(
1293
- "No database attached. Call attachPostgres/attachClickhouse first."
1748
+ "No database attached. Call attachPostgres, attachClickhouse, or attachBigQuery first."
1294
1749
  );
1295
1750
  }
1296
1751
  const paramMetadata = Array.isArray(queryResponse.data.params) ? queryResponse.data.params : [];
@@ -1391,7 +1846,7 @@ async function ask(client, queryEngine, question, options, signal) {
1391
1846
  }
1392
1847
  throw new Error("Unexpected error in ask retry loop");
1393
1848
  }
1394
- function resolveTenantId4(client, tenantId) {
1849
+ function resolveTenantId5(client, tenantId) {
1395
1850
  const resolved = tenantId ?? client.getDefaultTenantId();
1396
1851
  if (!resolved) {
1397
1852
  throw new Error(
@@ -1547,7 +2002,7 @@ function stripVizSpecOnlyHints(hints) {
1547
2002
  const { kind: _kind, ...rest } = hints;
1548
2003
  return rest;
1549
2004
  }
1550
- function resolveTenantId5(client, tenantId) {
2005
+ function resolveTenantId6(client, tenantId) {
1551
2006
  const resolved = tenantId ?? client.getDefaultTenantId();
1552
2007
  if (!resolved) {
1553
2008
  throw new Error(
@@ -1557,7 +2012,7 @@ function resolveTenantId5(client, tenantId) {
1557
2012
  return resolved;
1558
2013
  }
1559
2014
  async function modifyChart(client, queryEngine, input, options, signal) {
1560
- const tenantId = resolveTenantId5(client, options?.tenantId);
2015
+ const tenantId = resolveTenantId6(client, options?.tenantId);
1561
2016
  const sessionId = crypto4.randomUUID();
1562
2017
  const querypanelSessionId = options?.querypanelSessionId ?? sessionId;
1563
2018
  const hasQuerypanelSessionContext = !!options?.querypanelSessionId;
@@ -1725,7 +2180,7 @@ async function modifyChart(client, queryEngine, input, options, signal) {
1725
2180
 
1726
2181
  // src/routes/sessions.ts
1727
2182
  async function listSessions(client, options, signal) {
1728
- const tenantId = resolveTenantId6(client, options?.tenantId);
2183
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1729
2184
  const params = new URLSearchParams();
1730
2185
  if (options?.pagination?.page)
1731
2186
  params.set("page", `${options.pagination.page}`);
@@ -1748,7 +2203,7 @@ async function listSessions(client, options, signal) {
1748
2203
  );
1749
2204
  }
1750
2205
  async function getSession(client, sessionId, options, signal) {
1751
- const tenantId = resolveTenantId6(client, options?.tenantId);
2206
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1752
2207
  const params = new URLSearchParams();
1753
2208
  if (options?.includeTurns !== void 0) {
1754
2209
  params.set("include_turns", `${options.includeTurns}`);
@@ -1762,7 +2217,7 @@ async function getSession(client, sessionId, options, signal) {
1762
2217
  );
1763
2218
  }
1764
2219
  async function updateSession(client, sessionId, body, options, signal) {
1765
- const tenantId = resolveTenantId6(client, options?.tenantId);
2220
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1766
2221
  return await client.patch(
1767
2222
  `/sessions/${encodeURIComponent(sessionId)}`,
1768
2223
  body,
@@ -1773,7 +2228,7 @@ async function updateSession(client, sessionId, body, options, signal) {
1773
2228
  );
1774
2229
  }
1775
2230
  async function deleteSession(client, sessionId, options, signal) {
1776
- const tenantId = resolveTenantId6(client, options?.tenantId);
2231
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1777
2232
  await client.delete(
1778
2233
  `/sessions/${encodeURIComponent(sessionId)}`,
1779
2234
  tenantId,
@@ -1782,7 +2237,7 @@ async function deleteSession(client, sessionId, options, signal) {
1782
2237
  signal
1783
2238
  );
1784
2239
  }
1785
- function resolveTenantId6(client, tenantId) {
2240
+ function resolveTenantId7(client, tenantId) {
1786
2241
  const resolved = tenantId ?? client.getDefaultTenantId();
1787
2242
  if (!resolved) {
1788
2243
  throw new Error(
@@ -1795,7 +2250,7 @@ function resolveTenantId6(client, tenantId) {
1795
2250
  // src/routes/vizspec.ts
1796
2251
  import crypto5 from "crypto";
1797
2252
  async function generateVizSpec(client, input, options, signal) {
1798
- const tenantId = resolveTenantId7(client, options?.tenantId);
2253
+ const tenantId = resolveTenantId8(client, options?.tenantId);
1799
2254
  const sessionId = crypto5.randomUUID();
1800
2255
  const response = await client.post(
1801
2256
  "/vizspec",
@@ -1817,7 +2272,7 @@ async function generateVizSpec(client, input, options, signal) {
1817
2272
  );
1818
2273
  return response;
1819
2274
  }
1820
- function resolveTenantId7(client, tenantId) {
2275
+ function resolveTenantId8(client, tenantId) {
1821
2276
  const resolved = tenantId ?? client.getDefaultTenantId();
1822
2277
  if (!resolved) {
1823
2278
  throw new Error(
@@ -1828,13 +2283,64 @@ function resolveTenantId7(client, tenantId) {
1828
2283
  }
1829
2284
 
1830
2285
  // src/index.ts
1831
- var QueryPanelSdkAPI = class {
2286
+ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
1832
2287
  client;
1833
2288
  queryEngine;
1834
- constructor(baseUrl, privateKey, organizationId, options) {
1835
- this.client = new ApiClient(baseUrl, privateKey, organizationId, options);
2289
+ /**
2290
+ * @param workspaceId - Workspace UUID (same value as your org in the dashboard). Passed to the API as JWT claim `organizationId`.
2291
+ */
2292
+ constructor(baseUrl, privateKey, workspaceId, options) {
2293
+ if (options?.api) {
2294
+ this.client = options.api;
2295
+ } else {
2296
+ if (!baseUrl) throw new Error("Base URL is required");
2297
+ if (!privateKey) throw new Error("Private key is required");
2298
+ if (!workspaceId) throw new Error("Workspace ID is required");
2299
+ this.client = new ApiClient(baseUrl, privateKey, workspaceId, {
2300
+ defaultTenantId: options?.defaultTenantId,
2301
+ additionalHeaders: options?.additionalHeaders,
2302
+ fetch: options?.fetch
2303
+ });
2304
+ }
1836
2305
  this.queryEngine = new QueryEngine();
1837
2306
  }
2307
+ /**
2308
+ * Create an SDK instance that uses a request callback instead of HTTP.
2309
+ * Use this when calling the SDK from within your own API so that "API" calls
2310
+ * are handled in-process (e.g. by invoking querypanel-sdk services directly)
2311
+ * and do not cause recursion.
2312
+ *
2313
+ * @param workspaceId - Workspace identifier (sent as `organizationId` in JWTs and API auth)
2314
+ * @param requestHandler - Callback invoked for each logical API request (method, path, body, tenantId, etc.)
2315
+ * @param options - Optional defaultTenantId
2316
+ * @returns QueryPanelSdkAPI instance; attach databases and call ask(), syncSchema(), etc. as usual
2317
+ *
2318
+ * @example
2319
+ * ```ts
2320
+ * const qp = QueryPanelSdkAPI.withCallbacks(
2321
+ * process.env.QUERYPANEL_WORKSPACE_ID!,
2322
+ * async (opts) => {
2323
+ * // Call your in-process services (e.g. querypanel-sdk logic) instead of HTTP
2324
+ * if (opts.path === '/query' || opts.path === '/v2/query') {
2325
+ * const result = await yourSqlGenerator.generate(opts.body, opts.tenantId);
2326
+ * return { data: result, headers: new Headers({ 'x-querypanel-session-id': result.sessionId }) };
2327
+ * }
2328
+ * if (opts.path === '/chart') return { data: await yourChartService.generate(opts.body) };
2329
+ * // ... other paths
2330
+ * return { data: await yourProxy(opts) };
2331
+ * },
2332
+ * { defaultTenantId: process.env.DEFAULT_TENANT_ID }
2333
+ * );
2334
+ * qp.attachPostgres('db', createClient, { tenantFieldName: 'tenant_id' });
2335
+ * const result = await qp.ask('Top 10 orders', { tenantId: 't1', database: 'db' });
2336
+ * ```
2337
+ */
2338
+ static withCallbacks(workspaceId, requestHandler, options) {
2339
+ const api = new CallbackApiClient(requestHandler, {
2340
+ defaultTenantId: options?.defaultTenantId
2341
+ });
2342
+ return new _QueryPanelSdkAPI("", "", workspaceId, { api });
2343
+ }
1838
2344
  // Database attachment methods
1839
2345
  attachClickhouse(name, clientFn, options) {
1840
2346
  const adapter = new ClickHouseAdapter(clientFn, options);
@@ -1862,6 +2368,19 @@ var QueryPanelSdkAPI = class {
1862
2368
  };
1863
2369
  this.queryEngine.attachDatabase(name, adapter, metadata);
1864
2370
  }
2371
+ attachBigQuery(name, clientFn, options) {
2372
+ const adapter = new BigQueryAdapter(clientFn, options);
2373
+ const metadata = {
2374
+ name,
2375
+ dialect: "bigquery",
2376
+ description: options?.description,
2377
+ tags: options?.tags,
2378
+ tenantFieldName: options?.tenantFieldName,
2379
+ tenantFieldType: options?.tenantFieldType ?? "String",
2380
+ enforceTenantIsolation: options?.tenantFieldName ? options?.enforceTenantIsolation ?? true : void 0
2381
+ };
2382
+ this.queryEngine.attachDatabase(name, adapter, metadata);
2383
+ }
1865
2384
  attachDatabase(name, adapter) {
1866
2385
  const metadata = {
1867
2386
  name,
@@ -1948,6 +2467,48 @@ var QueryPanelSdkAPI = class {
1948
2467
  signal
1949
2468
  );
1950
2469
  }
2470
+ // Embedded dashboard / JWT
2471
+ /**
2472
+ * Creates a JWT for the given tenant (and optional userId, scopes).
2473
+ * Use this when you need to pass a token to the embed (e.g. frontend or demo).
2474
+ * Only available when using the HTTP client (ApiClient); not available when using withCallbacks.
2475
+ */
2476
+ async createJwt(options) {
2477
+ const tenantId = options.tenantId?.trim();
2478
+ if (!tenantId) {
2479
+ throw new Error("tenantId is required");
2480
+ }
2481
+ if (!(this.client instanceof ApiClient)) {
2482
+ throw new Error(
2483
+ "createJwt is not available when using withCallbacks. Use the SDK with baseUrl + privateKey to create JWTs."
2484
+ );
2485
+ }
2486
+ return await this.client.createJwt(
2487
+ tenantId,
2488
+ options.userId,
2489
+ options.scopes
2490
+ );
2491
+ }
2492
+ /**
2493
+ * Runs SQL against an attached database with tenant isolation applied.
2494
+ * Used by embed run-sql (e.g. dashboard chart execution). Requires a database
2495
+ * to be attached (e.g. via attachPostgres) and options.tenantId.
2496
+ */
2497
+ async runSqlForDashboard(input, options) {
2498
+ const tenantId = options.tenantId?.trim();
2499
+ if (!tenantId) {
2500
+ throw new Error("tenantId is required");
2501
+ }
2502
+ const databaseName = input.database ?? "db";
2503
+ const params = input.params ?? {};
2504
+ const result = await this.queryEngine.validateAndExecute(
2505
+ input.sql,
2506
+ params,
2507
+ databaseName,
2508
+ tenantId
2509
+ );
2510
+ return { rows: result.rows, fields: result.fields };
2511
+ }
1951
2512
  // VizSpec generation
1952
2513
  /**
1953
2514
  * Generates a VizSpec visualization specification from query results.
@@ -2391,8 +2952,297 @@ var QueryPanelSdkAPI = class {
2391
2952
  async deleteActiveChart(id, options, signal) {
2392
2953
  await deleteActiveChart(this.client, id, options, signal);
2393
2954
  }
2955
+ // Dashboard CRUD operations
2956
+ /**
2957
+ * Creates a new dashboard with BlockNote content.
2958
+ *
2959
+ * @param body - Dashboard configuration including name and BlockNote content
2960
+ * @param options - Tenant, user, and scope options
2961
+ * @param signal - Optional AbortSignal for cancellation
2962
+ * @returns Created dashboard
2963
+ *
2964
+ * @example
2965
+ * ```typescript
2966
+ * const dashboard = await qp.createDashboard({
2967
+ * name: "Sales Dashboard",
2968
+ * description: "Monthly sales metrics",
2969
+ * content_json: blockNoteContent,
2970
+ * editor_type: "blocknote",
2971
+ * }, { tenantId: "tenant_123" });
2972
+ * ```
2973
+ */
2974
+ async createDashboard(body, options, signal) {
2975
+ return await createDashboard(
2976
+ this.client,
2977
+ body,
2978
+ options,
2979
+ signal
2980
+ );
2981
+ }
2982
+ /**
2983
+ * Lists dashboards with pagination and filtering.
2984
+ *
2985
+ * @param options - Filtering and pagination options
2986
+ * @param signal - Optional AbortSignal for cancellation
2987
+ * @returns Paginated list of dashboards
2988
+ *
2989
+ * @example
2990
+ * ```typescript
2991
+ * const dashboards = await qp.listDashboards({
2992
+ * tenantId: "tenant_123",
2993
+ * status: "deployed",
2994
+ * pagination: { page: 1, limit: 10 },
2995
+ * });
2996
+ * ```
2997
+ */
2998
+ async listDashboards(options, signal) {
2999
+ return await listDashboards(this.client, options, signal);
3000
+ }
3001
+ /**
3002
+ * Gets a dashboard by ID.
3003
+ *
3004
+ * @param id - Dashboard ID
3005
+ * @param options - Tenant, user, and scope options
3006
+ * @param signal - Optional AbortSignal for cancellation
3007
+ * @returns Dashboard details
3008
+ *
3009
+ * @example
3010
+ * ```typescript
3011
+ * const dashboard = await qp.getDashboard("dash_123", {
3012
+ * tenantId: "tenant_123",
3013
+ * });
3014
+ * ```
3015
+ */
3016
+ async getDashboard(id, options, signal) {
3017
+ return await getDashboard(
3018
+ this.client,
3019
+ id,
3020
+ options,
3021
+ signal
3022
+ );
3023
+ }
3024
+ /**
3025
+ * Gets a dashboard for a specific tenant.
3026
+ * Returns customer fork if exists, otherwise returns the original dashboard.
3027
+ *
3028
+ * @param id - Dashboard ID
3029
+ * @param tenantId - Tenant ID to check for fork
3030
+ * @param options - Additional auth options
3031
+ * @param signal - Optional AbortSignal for cancellation
3032
+ * @returns Dashboard (fork if exists, original otherwise)
3033
+ *
3034
+ * @example
3035
+ * ```typescript
3036
+ * const dashboard = await qp.getDashboardForTenant(
3037
+ * "dash_123",
3038
+ * "tenant_456",
3039
+ * );
3040
+ * ```
3041
+ */
3042
+ async getDashboardForTenant(id, tenantId, options, signal) {
3043
+ return await getDashboardForTenant(
3044
+ this.client,
3045
+ id,
3046
+ tenantId,
3047
+ options,
3048
+ signal
3049
+ );
3050
+ }
3051
+ /**
3052
+ * Updates a dashboard.
3053
+ *
3054
+ * @param id - Dashboard ID to update
3055
+ * @param body - Fields to update
3056
+ * @param options - Tenant, user, and scope options
3057
+ * @param signal - Optional AbortSignal for cancellation
3058
+ * @returns Updated dashboard
3059
+ *
3060
+ * @example
3061
+ * ```typescript
3062
+ * const updated = await qp.updateDashboard("dash_123", {
3063
+ * name: "Updated Dashboard",
3064
+ * content_json: newBlockNoteContent,
3065
+ * }, { tenantId: "tenant_123" });
3066
+ * ```
3067
+ */
3068
+ async updateDashboard(id, body, options, signal) {
3069
+ return await updateDashboard(
3070
+ this.client,
3071
+ id,
3072
+ body,
3073
+ options,
3074
+ signal
3075
+ );
3076
+ }
3077
+ /**
3078
+ * Updates dashboard status (deploy/undeploy).
3079
+ *
3080
+ * @param id - Dashboard ID
3081
+ * @param status - New status
3082
+ * @param options - Tenant, user, and scope options
3083
+ * @param signal - Optional AbortSignal for cancellation
3084
+ * @returns Updated dashboard
3085
+ *
3086
+ * @example
3087
+ * ```typescript
3088
+ * const deployed = await qp.updateDashboardStatus(
3089
+ * "dash_123",
3090
+ * "deployed",
3091
+ * { tenantId: "tenant_123" },
3092
+ * );
3093
+ * ```
3094
+ */
3095
+ async updateDashboardStatus(id, status, options, signal) {
3096
+ return await updateDashboardStatus(
3097
+ this.client,
3098
+ id,
3099
+ status,
3100
+ options,
3101
+ signal
3102
+ );
3103
+ }
3104
+ /**
3105
+ * Deletes a dashboard.
3106
+ *
3107
+ * @param id - Dashboard ID to delete
3108
+ * @param options - Tenant, user, and scope options
3109
+ * @param signal - Optional AbortSignal for cancellation
3110
+ *
3111
+ * @example
3112
+ * ```typescript
3113
+ * await qp.deleteDashboard("dash_123", { tenantId: "tenant_123" });
3114
+ * ```
3115
+ */
3116
+ async deleteDashboard(id, options, signal) {
3117
+ await deleteDashboard(this.client, id, options, signal);
3118
+ }
3119
+ // Dashboard Fork operations (Customer Customization)
3120
+ /**
3121
+ * Forks a dashboard for customer customization (copy-on-write).
3122
+ *
3123
+ * Creates a full copy of the dashboard that the customer can edit independently.
3124
+ * The original dashboard remains unchanged.
3125
+ *
3126
+ * @param id - Dashboard ID to fork
3127
+ * @param input - Fork configuration with tenant_id
3128
+ * @param options - Additional auth options
3129
+ * @param signal - Optional AbortSignal for cancellation
3130
+ * @returns Forked dashboard
3131
+ *
3132
+ * @example
3133
+ * ```typescript
3134
+ * const fork = await qp.forkDashboard("dash_123", {
3135
+ * tenant_id: "tenant_456",
3136
+ * name: "Customer's Custom Dashboard",
3137
+ * });
3138
+ * ```
3139
+ */
3140
+ async forkDashboard(id, input, options, signal) {
3141
+ return await forkDashboard(
3142
+ this.client,
3143
+ id,
3144
+ input,
3145
+ options,
3146
+ signal
3147
+ );
3148
+ }
3149
+ /**
3150
+ * Updates a customer fork.
3151
+ *
3152
+ * @param forkId - Fork ID to update
3153
+ * @param input - Update data including tenant_id
3154
+ * @param options - Additional auth options
3155
+ * @param signal - Optional AbortSignal for cancellation
3156
+ * @returns Updated fork
3157
+ *
3158
+ * @example
3159
+ * ```typescript
3160
+ * const updated = await qp.updateFork("fork_123", {
3161
+ * tenant_id: "tenant_456",
3162
+ * content_json: newBlockNoteContent,
3163
+ * });
3164
+ * ```
3165
+ */
3166
+ async updateFork(forkId, input, options, signal) {
3167
+ return await updateFork(
3168
+ this.client,
3169
+ forkId,
3170
+ input,
3171
+ options,
3172
+ signal
3173
+ );
3174
+ }
3175
+ /**
3176
+ * Rollbacks a fork to the original dashboard.
3177
+ * This deletes the fork and the customer will see the original again.
3178
+ *
3179
+ * @param forkId - Fork ID to rollback
3180
+ * @param tenantId - Tenant ID
3181
+ * @param options - Additional auth options
3182
+ * @param signal - Optional AbortSignal for cancellation
3183
+ * @returns Original dashboard
3184
+ *
3185
+ * @example
3186
+ * ```typescript
3187
+ * const original = await qp.rollbackFork("fork_123", "tenant_456");
3188
+ * ```
3189
+ */
3190
+ async rollbackFork(forkId, tenantId, options, signal) {
3191
+ return await rollbackFork(
3192
+ this.client,
3193
+ forkId,
3194
+ tenantId,
3195
+ options,
3196
+ signal
3197
+ );
3198
+ }
3199
+ /**
3200
+ * Deletes a customer fork.
3201
+ *
3202
+ * @param forkId - Fork ID to delete
3203
+ * @param tenantId - Tenant ID
3204
+ * @param options - Additional auth options
3205
+ * @param signal - Optional AbortSignal for cancellation
3206
+ *
3207
+ * @example
3208
+ * ```typescript
3209
+ * await qp.deleteFork("fork_123", "tenant_456");
3210
+ * ```
3211
+ */
3212
+ async deleteFork(forkId, tenantId, options, signal) {
3213
+ await deleteFork(
3214
+ this.client,
3215
+ forkId,
3216
+ tenantId,
3217
+ options,
3218
+ signal
3219
+ );
3220
+ }
3221
+ /**
3222
+ * Lists all customer forks for a tenant.
3223
+ *
3224
+ * @param tenantId - Tenant ID
3225
+ * @param options - Additional auth options
3226
+ * @param signal - Optional AbortSignal for cancellation
3227
+ * @returns Array of forks
3228
+ *
3229
+ * @example
3230
+ * ```typescript
3231
+ * const forks = await qp.listForksForTenant("tenant_456");
3232
+ * ```
3233
+ */
3234
+ async listForksForTenant(tenantId, options, signal) {
3235
+ return await listForksForTenant(
3236
+ this.client,
3237
+ tenantId,
3238
+ options,
3239
+ signal
3240
+ );
3241
+ }
2394
3242
  };
2395
3243
  export {
3244
+ BigQueryAdapter,
3245
+ CallbackApiClient,
2396
3246
  ClickHouseAdapter,
2397
3247
  PostgresAdapter,
2398
3248
  QueryErrorCode,