@querypanel/node-sdk 1.0.56 → 1.0.58

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);
346
- }
347
- for (const key of namedKeys) {
348
- const val = params[key];
349
- positionalParams.push(val);
530
+ return positionalParams;
350
531
  }
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}'`;
@@ -1159,20 +1463,171 @@ async function deleteActiveChart(client, id, options, signal) {
1159
1463
  signal
1160
1464
  );
1161
1465
  }
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
- }
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}`,
1620
+ tenantId,
1621
+ options?.userId,
1622
+ options?.scopes,
1623
+ signal
1624
+ );
1625
+ }
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;
@@ -1266,7 +1721,8 @@ async function ask(client, queryEngine, question, options, signal) {
1266
1721
  ...options.maxRetry ? { max_retry: options.maxRetry } : {},
1267
1722
  ...tenantSettings ? { tenant_settings: tenantSettings } : {},
1268
1723
  ...databaseName ? { database: databaseName } : {},
1269
- ...metadata?.dialect ? { dialect: metadata.dialect } : {}
1724
+ ...metadata?.dialect ? { dialect: metadata.dialect } : {},
1725
+ ...options.model?.trim() ? { model: options.model.trim() } : {}
1270
1726
  },
1271
1727
  tenantId,
1272
1728
  options.userId,
@@ -1290,7 +1746,7 @@ async function ask(client, queryEngine, question, options, signal) {
1290
1746
  const dbName = queryResponse.data.database ?? options.database ?? queryEngine.getDefaultDatabase();
1291
1747
  if (!dbName) {
1292
1748
  throw new Error(
1293
- "No database attached. Call attachPostgres/attachClickhouse first."
1749
+ "No database attached. Call attachPostgres, attachClickhouse, or attachBigQuery first."
1294
1750
  );
1295
1751
  }
1296
1752
  const paramMetadata = Array.isArray(queryResponse.data.params) ? queryResponse.data.params : [];
@@ -1391,7 +1847,7 @@ async function ask(client, queryEngine, question, options, signal) {
1391
1847
  }
1392
1848
  throw new Error("Unexpected error in ask retry loop");
1393
1849
  }
1394
- function resolveTenantId4(client, tenantId) {
1850
+ function resolveTenantId5(client, tenantId) {
1395
1851
  const resolved = tenantId ?? client.getDefaultTenantId();
1396
1852
  if (!resolved) {
1397
1853
  throw new Error(
@@ -1547,7 +2003,7 @@ function stripVizSpecOnlyHints(hints) {
1547
2003
  const { kind: _kind, ...rest } = hints;
1548
2004
  return rest;
1549
2005
  }
1550
- function resolveTenantId5(client, tenantId) {
2006
+ function resolveTenantId6(client, tenantId) {
1551
2007
  const resolved = tenantId ?? client.getDefaultTenantId();
1552
2008
  if (!resolved) {
1553
2009
  throw new Error(
@@ -1557,7 +2013,7 @@ function resolveTenantId5(client, tenantId) {
1557
2013
  return resolved;
1558
2014
  }
1559
2015
  async function modifyChart(client, queryEngine, input, options, signal) {
1560
- const tenantId = resolveTenantId5(client, options?.tenantId);
2016
+ const tenantId = resolveTenantId6(client, options?.tenantId);
1561
2017
  const sessionId = crypto4.randomUUID();
1562
2018
  const querypanelSessionId = options?.querypanelSessionId ?? sessionId;
1563
2019
  const hasQuerypanelSessionContext = !!options?.querypanelSessionId;
@@ -1725,7 +2181,7 @@ async function modifyChart(client, queryEngine, input, options, signal) {
1725
2181
 
1726
2182
  // src/routes/sessions.ts
1727
2183
  async function listSessions(client, options, signal) {
1728
- const tenantId = resolveTenantId6(client, options?.tenantId);
2184
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1729
2185
  const params = new URLSearchParams();
1730
2186
  if (options?.pagination?.page)
1731
2187
  params.set("page", `${options.pagination.page}`);
@@ -1748,7 +2204,7 @@ async function listSessions(client, options, signal) {
1748
2204
  );
1749
2205
  }
1750
2206
  async function getSession(client, sessionId, options, signal) {
1751
- const tenantId = resolveTenantId6(client, options?.tenantId);
2207
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1752
2208
  const params = new URLSearchParams();
1753
2209
  if (options?.includeTurns !== void 0) {
1754
2210
  params.set("include_turns", `${options.includeTurns}`);
@@ -1762,7 +2218,7 @@ async function getSession(client, sessionId, options, signal) {
1762
2218
  );
1763
2219
  }
1764
2220
  async function updateSession(client, sessionId, body, options, signal) {
1765
- const tenantId = resolveTenantId6(client, options?.tenantId);
2221
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1766
2222
  return await client.patch(
1767
2223
  `/sessions/${encodeURIComponent(sessionId)}`,
1768
2224
  body,
@@ -1773,7 +2229,7 @@ async function updateSession(client, sessionId, body, options, signal) {
1773
2229
  );
1774
2230
  }
1775
2231
  async function deleteSession(client, sessionId, options, signal) {
1776
- const tenantId = resolveTenantId6(client, options?.tenantId);
2232
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1777
2233
  await client.delete(
1778
2234
  `/sessions/${encodeURIComponent(sessionId)}`,
1779
2235
  tenantId,
@@ -1782,7 +2238,7 @@ async function deleteSession(client, sessionId, options, signal) {
1782
2238
  signal
1783
2239
  );
1784
2240
  }
1785
- function resolveTenantId6(client, tenantId) {
2241
+ function resolveTenantId7(client, tenantId) {
1786
2242
  const resolved = tenantId ?? client.getDefaultTenantId();
1787
2243
  if (!resolved) {
1788
2244
  throw new Error(
@@ -1795,7 +2251,7 @@ function resolveTenantId6(client, tenantId) {
1795
2251
  // src/routes/vizspec.ts
1796
2252
  import crypto5 from "crypto";
1797
2253
  async function generateVizSpec(client, input, options, signal) {
1798
- const tenantId = resolveTenantId7(client, options?.tenantId);
2254
+ const tenantId = resolveTenantId8(client, options?.tenantId);
1799
2255
  const sessionId = crypto5.randomUUID();
1800
2256
  const response = await client.post(
1801
2257
  "/vizspec",
@@ -1817,7 +2273,7 @@ async function generateVizSpec(client, input, options, signal) {
1817
2273
  );
1818
2274
  return response;
1819
2275
  }
1820
- function resolveTenantId7(client, tenantId) {
2276
+ function resolveTenantId8(client, tenantId) {
1821
2277
  const resolved = tenantId ?? client.getDefaultTenantId();
1822
2278
  if (!resolved) {
1823
2279
  throw new Error(
@@ -1828,13 +2284,64 @@ function resolveTenantId7(client, tenantId) {
1828
2284
  }
1829
2285
 
1830
2286
  // src/index.ts
1831
- var QueryPanelSdkAPI = class {
2287
+ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
1832
2288
  client;
1833
2289
  queryEngine;
1834
- constructor(baseUrl, privateKey, organizationId, options) {
1835
- this.client = new ApiClient(baseUrl, privateKey, organizationId, options);
2290
+ /**
2291
+ * @param workspaceId - Workspace UUID (same value as your org in the dashboard). Passed to the API as JWT claim `organizationId`.
2292
+ */
2293
+ constructor(baseUrl, privateKey, workspaceId, options) {
2294
+ if (options?.api) {
2295
+ this.client = options.api;
2296
+ } else {
2297
+ if (!baseUrl) throw new Error("Base URL is required");
2298
+ if (!privateKey) throw new Error("Private key is required");
2299
+ if (!workspaceId) throw new Error("Workspace ID is required");
2300
+ this.client = new ApiClient(baseUrl, privateKey, workspaceId, {
2301
+ defaultTenantId: options?.defaultTenantId,
2302
+ additionalHeaders: options?.additionalHeaders,
2303
+ fetch: options?.fetch
2304
+ });
2305
+ }
1836
2306
  this.queryEngine = new QueryEngine();
1837
2307
  }
2308
+ /**
2309
+ * Create an SDK instance that uses a request callback instead of HTTP.
2310
+ * Use this when calling the SDK from within your own API so that "API" calls
2311
+ * are handled in-process (e.g. by invoking querypanel-sdk services directly)
2312
+ * and do not cause recursion.
2313
+ *
2314
+ * @param workspaceId - Workspace identifier (sent as `organizationId` in JWTs and API auth)
2315
+ * @param requestHandler - Callback invoked for each logical API request (method, path, body, tenantId, etc.)
2316
+ * @param options - Optional defaultTenantId
2317
+ * @returns QueryPanelSdkAPI instance; attach databases and call ask(), syncSchema(), etc. as usual
2318
+ *
2319
+ * @example
2320
+ * ```ts
2321
+ * const qp = QueryPanelSdkAPI.withCallbacks(
2322
+ * process.env.QUERYPANEL_WORKSPACE_ID!,
2323
+ * async (opts) => {
2324
+ * // Call your in-process services (e.g. querypanel-sdk logic) instead of HTTP
2325
+ * if (opts.path === '/query' || opts.path === '/v2/query') {
2326
+ * const result = await yourSqlGenerator.generate(opts.body, opts.tenantId);
2327
+ * return { data: result, headers: new Headers({ 'x-querypanel-session-id': result.sessionId }) };
2328
+ * }
2329
+ * if (opts.path === '/chart') return { data: await yourChartService.generate(opts.body) };
2330
+ * // ... other paths
2331
+ * return { data: await yourProxy(opts) };
2332
+ * },
2333
+ * { defaultTenantId: process.env.DEFAULT_TENANT_ID }
2334
+ * );
2335
+ * qp.attachPostgres('db', createClient, { tenantFieldName: 'tenant_id' });
2336
+ * const result = await qp.ask('Top 10 orders', { tenantId: 't1', database: 'db' });
2337
+ * ```
2338
+ */
2339
+ static withCallbacks(workspaceId, requestHandler, options) {
2340
+ const api = new CallbackApiClient(requestHandler, {
2341
+ defaultTenantId: options?.defaultTenantId
2342
+ });
2343
+ return new _QueryPanelSdkAPI("", "", workspaceId, { api });
2344
+ }
1838
2345
  // Database attachment methods
1839
2346
  attachClickhouse(name, clientFn, options) {
1840
2347
  const adapter = new ClickHouseAdapter(clientFn, options);
@@ -1862,6 +2369,19 @@ var QueryPanelSdkAPI = class {
1862
2369
  };
1863
2370
  this.queryEngine.attachDatabase(name, adapter, metadata);
1864
2371
  }
2372
+ attachBigQuery(name, clientFn, options) {
2373
+ const adapter = new BigQueryAdapter(clientFn, options);
2374
+ const metadata = {
2375
+ name,
2376
+ dialect: "bigquery",
2377
+ description: options?.description,
2378
+ tags: options?.tags,
2379
+ tenantFieldName: options?.tenantFieldName,
2380
+ tenantFieldType: options?.tenantFieldType ?? "String",
2381
+ enforceTenantIsolation: options?.tenantFieldName ? options?.enforceTenantIsolation ?? true : void 0
2382
+ };
2383
+ this.queryEngine.attachDatabase(name, adapter, metadata);
2384
+ }
1865
2385
  attachDatabase(name, adapter) {
1866
2386
  const metadata = {
1867
2387
  name,
@@ -1948,6 +2468,48 @@ var QueryPanelSdkAPI = class {
1948
2468
  signal
1949
2469
  );
1950
2470
  }
2471
+ // Embedded dashboard / JWT
2472
+ /**
2473
+ * Creates a JWT for the given tenant (and optional userId, scopes).
2474
+ * Use this when you need to pass a token to the embed (e.g. frontend or demo).
2475
+ * Only available when using the HTTP client (ApiClient); not available when using withCallbacks.
2476
+ */
2477
+ async createJwt(options) {
2478
+ const tenantId = options.tenantId?.trim();
2479
+ if (!tenantId) {
2480
+ throw new Error("tenantId is required");
2481
+ }
2482
+ if (!(this.client instanceof ApiClient)) {
2483
+ throw new Error(
2484
+ "createJwt is not available when using withCallbacks. Use the SDK with baseUrl + privateKey to create JWTs."
2485
+ );
2486
+ }
2487
+ return await this.client.createJwt(
2488
+ tenantId,
2489
+ options.userId,
2490
+ options.scopes
2491
+ );
2492
+ }
2493
+ /**
2494
+ * Runs SQL against an attached database with tenant isolation applied.
2495
+ * Used by embed run-sql (e.g. dashboard chart execution). Requires a database
2496
+ * to be attached (e.g. via attachPostgres) and options.tenantId.
2497
+ */
2498
+ async runSqlForDashboard(input, options) {
2499
+ const tenantId = options.tenantId?.trim();
2500
+ if (!tenantId) {
2501
+ throw new Error("tenantId is required");
2502
+ }
2503
+ const databaseName = input.database ?? "db";
2504
+ const params = input.params ?? {};
2505
+ const result = await this.queryEngine.validateAndExecute(
2506
+ input.sql,
2507
+ params,
2508
+ databaseName,
2509
+ tenantId
2510
+ );
2511
+ return { rows: result.rows, fields: result.fields };
2512
+ }
1951
2513
  // VizSpec generation
1952
2514
  /**
1953
2515
  * Generates a VizSpec visualization specification from query results.
@@ -2391,8 +2953,297 @@ var QueryPanelSdkAPI = class {
2391
2953
  async deleteActiveChart(id, options, signal) {
2392
2954
  await deleteActiveChart(this.client, id, options, signal);
2393
2955
  }
2956
+ // Dashboard CRUD operations
2957
+ /**
2958
+ * Creates a new dashboard with BlockNote content.
2959
+ *
2960
+ * @param body - Dashboard configuration including name and BlockNote content
2961
+ * @param options - Tenant, user, and scope options
2962
+ * @param signal - Optional AbortSignal for cancellation
2963
+ * @returns Created dashboard
2964
+ *
2965
+ * @example
2966
+ * ```typescript
2967
+ * const dashboard = await qp.createDashboard({
2968
+ * name: "Sales Dashboard",
2969
+ * description: "Monthly sales metrics",
2970
+ * content_json: blockNoteContent,
2971
+ * editor_type: "blocknote",
2972
+ * }, { tenantId: "tenant_123" });
2973
+ * ```
2974
+ */
2975
+ async createDashboard(body, options, signal) {
2976
+ return await createDashboard(
2977
+ this.client,
2978
+ body,
2979
+ options,
2980
+ signal
2981
+ );
2982
+ }
2983
+ /**
2984
+ * Lists dashboards with pagination and filtering.
2985
+ *
2986
+ * @param options - Filtering and pagination options
2987
+ * @param signal - Optional AbortSignal for cancellation
2988
+ * @returns Paginated list of dashboards
2989
+ *
2990
+ * @example
2991
+ * ```typescript
2992
+ * const dashboards = await qp.listDashboards({
2993
+ * tenantId: "tenant_123",
2994
+ * status: "deployed",
2995
+ * pagination: { page: 1, limit: 10 },
2996
+ * });
2997
+ * ```
2998
+ */
2999
+ async listDashboards(options, signal) {
3000
+ return await listDashboards(this.client, options, signal);
3001
+ }
3002
+ /**
3003
+ * Gets a dashboard by ID.
3004
+ *
3005
+ * @param id - Dashboard ID
3006
+ * @param options - Tenant, user, and scope options
3007
+ * @param signal - Optional AbortSignal for cancellation
3008
+ * @returns Dashboard details
3009
+ *
3010
+ * @example
3011
+ * ```typescript
3012
+ * const dashboard = await qp.getDashboard("dash_123", {
3013
+ * tenantId: "tenant_123",
3014
+ * });
3015
+ * ```
3016
+ */
3017
+ async getDashboard(id, options, signal) {
3018
+ return await getDashboard(
3019
+ this.client,
3020
+ id,
3021
+ options,
3022
+ signal
3023
+ );
3024
+ }
3025
+ /**
3026
+ * Gets a dashboard for a specific tenant.
3027
+ * Returns customer fork if exists, otherwise returns the original dashboard.
3028
+ *
3029
+ * @param id - Dashboard ID
3030
+ * @param tenantId - Tenant ID to check for fork
3031
+ * @param options - Additional auth options
3032
+ * @param signal - Optional AbortSignal for cancellation
3033
+ * @returns Dashboard (fork if exists, original otherwise)
3034
+ *
3035
+ * @example
3036
+ * ```typescript
3037
+ * const dashboard = await qp.getDashboardForTenant(
3038
+ * "dash_123",
3039
+ * "tenant_456",
3040
+ * );
3041
+ * ```
3042
+ */
3043
+ async getDashboardForTenant(id, tenantId, options, signal) {
3044
+ return await getDashboardForTenant(
3045
+ this.client,
3046
+ id,
3047
+ tenantId,
3048
+ options,
3049
+ signal
3050
+ );
3051
+ }
3052
+ /**
3053
+ * Updates a dashboard.
3054
+ *
3055
+ * @param id - Dashboard ID to update
3056
+ * @param body - Fields to update
3057
+ * @param options - Tenant, user, and scope options
3058
+ * @param signal - Optional AbortSignal for cancellation
3059
+ * @returns Updated dashboard
3060
+ *
3061
+ * @example
3062
+ * ```typescript
3063
+ * const updated = await qp.updateDashboard("dash_123", {
3064
+ * name: "Updated Dashboard",
3065
+ * content_json: newBlockNoteContent,
3066
+ * }, { tenantId: "tenant_123" });
3067
+ * ```
3068
+ */
3069
+ async updateDashboard(id, body, options, signal) {
3070
+ return await updateDashboard(
3071
+ this.client,
3072
+ id,
3073
+ body,
3074
+ options,
3075
+ signal
3076
+ );
3077
+ }
3078
+ /**
3079
+ * Updates dashboard status (deploy/undeploy).
3080
+ *
3081
+ * @param id - Dashboard ID
3082
+ * @param status - New status
3083
+ * @param options - Tenant, user, and scope options
3084
+ * @param signal - Optional AbortSignal for cancellation
3085
+ * @returns Updated dashboard
3086
+ *
3087
+ * @example
3088
+ * ```typescript
3089
+ * const deployed = await qp.updateDashboardStatus(
3090
+ * "dash_123",
3091
+ * "deployed",
3092
+ * { tenantId: "tenant_123" },
3093
+ * );
3094
+ * ```
3095
+ */
3096
+ async updateDashboardStatus(id, status, options, signal) {
3097
+ return await updateDashboardStatus(
3098
+ this.client,
3099
+ id,
3100
+ status,
3101
+ options,
3102
+ signal
3103
+ );
3104
+ }
3105
+ /**
3106
+ * Deletes a dashboard.
3107
+ *
3108
+ * @param id - Dashboard ID to delete
3109
+ * @param options - Tenant, user, and scope options
3110
+ * @param signal - Optional AbortSignal for cancellation
3111
+ *
3112
+ * @example
3113
+ * ```typescript
3114
+ * await qp.deleteDashboard("dash_123", { tenantId: "tenant_123" });
3115
+ * ```
3116
+ */
3117
+ async deleteDashboard(id, options, signal) {
3118
+ await deleteDashboard(this.client, id, options, signal);
3119
+ }
3120
+ // Dashboard Fork operations (Customer Customization)
3121
+ /**
3122
+ * Forks a dashboard for customer customization (copy-on-write).
3123
+ *
3124
+ * Creates a full copy of the dashboard that the customer can edit independently.
3125
+ * The original dashboard remains unchanged.
3126
+ *
3127
+ * @param id - Dashboard ID to fork
3128
+ * @param input - Fork configuration with tenant_id
3129
+ * @param options - Additional auth options
3130
+ * @param signal - Optional AbortSignal for cancellation
3131
+ * @returns Forked dashboard
3132
+ *
3133
+ * @example
3134
+ * ```typescript
3135
+ * const fork = await qp.forkDashboard("dash_123", {
3136
+ * tenant_id: "tenant_456",
3137
+ * name: "Customer's Custom Dashboard",
3138
+ * });
3139
+ * ```
3140
+ */
3141
+ async forkDashboard(id, input, options, signal) {
3142
+ return await forkDashboard(
3143
+ this.client,
3144
+ id,
3145
+ input,
3146
+ options,
3147
+ signal
3148
+ );
3149
+ }
3150
+ /**
3151
+ * Updates a customer fork.
3152
+ *
3153
+ * @param forkId - Fork ID to update
3154
+ * @param input - Update data including tenant_id
3155
+ * @param options - Additional auth options
3156
+ * @param signal - Optional AbortSignal for cancellation
3157
+ * @returns Updated fork
3158
+ *
3159
+ * @example
3160
+ * ```typescript
3161
+ * const updated = await qp.updateFork("fork_123", {
3162
+ * tenant_id: "tenant_456",
3163
+ * content_json: newBlockNoteContent,
3164
+ * });
3165
+ * ```
3166
+ */
3167
+ async updateFork(forkId, input, options, signal) {
3168
+ return await updateFork(
3169
+ this.client,
3170
+ forkId,
3171
+ input,
3172
+ options,
3173
+ signal
3174
+ );
3175
+ }
3176
+ /**
3177
+ * Rollbacks a fork to the original dashboard.
3178
+ * This deletes the fork and the customer will see the original again.
3179
+ *
3180
+ * @param forkId - Fork ID to rollback
3181
+ * @param tenantId - Tenant ID
3182
+ * @param options - Additional auth options
3183
+ * @param signal - Optional AbortSignal for cancellation
3184
+ * @returns Original dashboard
3185
+ *
3186
+ * @example
3187
+ * ```typescript
3188
+ * const original = await qp.rollbackFork("fork_123", "tenant_456");
3189
+ * ```
3190
+ */
3191
+ async rollbackFork(forkId, tenantId, options, signal) {
3192
+ return await rollbackFork(
3193
+ this.client,
3194
+ forkId,
3195
+ tenantId,
3196
+ options,
3197
+ signal
3198
+ );
3199
+ }
3200
+ /**
3201
+ * Deletes a customer fork.
3202
+ *
3203
+ * @param forkId - Fork ID to delete
3204
+ * @param tenantId - Tenant ID
3205
+ * @param options - Additional auth options
3206
+ * @param signal - Optional AbortSignal for cancellation
3207
+ *
3208
+ * @example
3209
+ * ```typescript
3210
+ * await qp.deleteFork("fork_123", "tenant_456");
3211
+ * ```
3212
+ */
3213
+ async deleteFork(forkId, tenantId, options, signal) {
3214
+ await deleteFork(
3215
+ this.client,
3216
+ forkId,
3217
+ tenantId,
3218
+ options,
3219
+ signal
3220
+ );
3221
+ }
3222
+ /**
3223
+ * Lists all customer forks for a tenant.
3224
+ *
3225
+ * @param tenantId - Tenant ID
3226
+ * @param options - Additional auth options
3227
+ * @param signal - Optional AbortSignal for cancellation
3228
+ * @returns Array of forks
3229
+ *
3230
+ * @example
3231
+ * ```typescript
3232
+ * const forks = await qp.listForksForTenant("tenant_456");
3233
+ * ```
3234
+ */
3235
+ async listForksForTenant(tenantId, options, signal) {
3236
+ return await listForksForTenant(
3237
+ this.client,
3238
+ tenantId,
3239
+ options,
3240
+ signal
3241
+ );
3242
+ }
2394
3243
  };
2395
3244
  export {
3245
+ BigQueryAdapter,
3246
+ CallbackApiClient,
2396
3247
  ClickHouseAdapter,
2397
3248
  PostgresAdapter,
2398
3249
  QueryErrorCode,