@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.cjs CHANGED
@@ -30,6 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ BigQueryAdapter: () => BigQueryAdapter,
34
+ CallbackApiClient: () => CallbackApiClient,
33
35
  ClickHouseAdapter: () => ClickHouseAdapter,
34
36
  PostgresAdapter: () => PostgresAdapter,
35
37
  QueryErrorCode: () => QueryErrorCode,
@@ -39,6 +41,186 @@ __export(index_exports, {
39
41
  });
40
42
  module.exports = __toCommonJS(index_exports);
41
43
 
44
+ // src/adapters/bigquery.ts
45
+ var BigQueryAdapter = class {
46
+ constructor(clientFn, options) {
47
+ this.clientFn = clientFn;
48
+ this.projectId = options.projectId;
49
+ this.datasetProjectId = options.datasetProjectId ?? options.projectId;
50
+ this.defaultDataset = options.dataset;
51
+ this.databaseName = options.database ?? options.dataset;
52
+ this.kind = options.kind ?? "bigquery";
53
+ if (options.allowedTables) {
54
+ this.allowedTables = normalizeTableFilter(
55
+ options.allowedTables,
56
+ this.defaultDataset
57
+ );
58
+ }
59
+ }
60
+ projectId;
61
+ datasetProjectId;
62
+ defaultDataset;
63
+ databaseName;
64
+ kind;
65
+ allowedTables;
66
+ async execute(sql, params) {
67
+ if (this.allowedTables) {
68
+ this.validateQueryTables(sql);
69
+ }
70
+ const result = await this.clientFn({
71
+ query: sql,
72
+ params,
73
+ dryRun: false
74
+ });
75
+ return { fields: result.fields, rows: result.rows };
76
+ }
77
+ async validate(sql, params) {
78
+ await this.clientFn({
79
+ query: sql,
80
+ params,
81
+ dryRun: true
82
+ });
83
+ }
84
+ getDialect() {
85
+ return "bigquery";
86
+ }
87
+ /**
88
+ * Introspect using BigQuery INFORMATION_SCHEMA (TABLES and COLUMNS).
89
+ * Restricts to the default dataset (or allowedTables) and returns tables/columns only.
90
+ */
91
+ async introspect(options) {
92
+ const tablesToIntrospect = options?.tables ? normalizeTableFilter(options.tables, this.defaultDataset) : this.allowedTables;
93
+ const normalizedTables = tablesToIntrospect ?? [];
94
+ const tablesSql = buildTablesQuery(
95
+ this.datasetProjectId,
96
+ this.defaultDataset,
97
+ normalizedTables
98
+ );
99
+ const tablesResult = await this.clientFn({
100
+ query: tablesSql,
101
+ dryRun: false
102
+ });
103
+ const tableRows = tablesResult.rows;
104
+ const columnsSql = buildColumnsQuery(
105
+ this.datasetProjectId,
106
+ this.defaultDataset,
107
+ normalizedTables
108
+ );
109
+ const columnsResult = await this.clientFn({
110
+ query: columnsSql,
111
+ dryRun: false
112
+ });
113
+ const columnRows = columnsResult.rows;
114
+ const tablesByKey = /* @__PURE__ */ new Map();
115
+ for (const row of tableRows) {
116
+ const key = tableKey(row.table_schema, row.table_name);
117
+ const table = {
118
+ name: row.table_name,
119
+ schema: row.table_schema,
120
+ type: asTableType(row.table_type),
121
+ columns: []
122
+ };
123
+ tablesByKey.set(key, table);
124
+ }
125
+ for (const row of columnRows) {
126
+ const key = tableKey(row.table_schema, row.table_name);
127
+ const table = tablesByKey.get(key);
128
+ if (!table) continue;
129
+ const column = {
130
+ name: row.column_name,
131
+ type: row.data_type,
132
+ isPrimaryKey: false
133
+ };
134
+ table.columns.push(column);
135
+ }
136
+ const tables = Array.from(tablesByKey.values()).sort((a, b) => {
137
+ if (a.schema === b.schema) {
138
+ return a.name.localeCompare(b.name);
139
+ }
140
+ return a.schema.localeCompare(b.schema);
141
+ });
142
+ return {
143
+ db: {
144
+ kind: this.kind,
145
+ name: this.databaseName
146
+ },
147
+ tables,
148
+ introspectedAt: (/* @__PURE__ */ new Date()).toISOString()
149
+ };
150
+ }
151
+ validateQueryTables(sql) {
152
+ if (!this.allowedTables || this.allowedTables.length === 0) {
153
+ return;
154
+ }
155
+ const allowedSet = new Set(
156
+ this.allowedTables.map((t) => tableKey(t.dataset, t.table))
157
+ );
158
+ const tablePattern = /(?:FROM|JOIN)\s+`?(?:[\w-]+\.)?([\w-]+)\.([\w-]+)`?|(?:FROM|JOIN)\s+`?([\w-]+)`?/gi;
159
+ const matches = sql.matchAll(tablePattern);
160
+ for (const match of matches) {
161
+ const dataset = match[1] ?? match[3] ?? this.defaultDataset;
162
+ const table = match[2] ?? match[3];
163
+ if (table) {
164
+ const key = tableKey(dataset, table);
165
+ if (!allowedSet.has(key)) {
166
+ throw new Error(
167
+ `Query references table "${dataset}.${table}" which is not in the allowed tables list`
168
+ );
169
+ }
170
+ }
171
+ }
172
+ }
173
+ };
174
+ function normalizeTableFilter(tables, defaultDataset) {
175
+ if (!tables?.length) return [];
176
+ const normalized = [];
177
+ const seen = /* @__PURE__ */ new Set();
178
+ for (const raw of tables) {
179
+ if (!raw?.trim()) continue;
180
+ const trimmed = raw.trim();
181
+ const parts = trimmed.split(".");
182
+ const table = parts.pop() ?? "";
183
+ const dataset = parts.pop() ?? defaultDataset;
184
+ const key = tableKey(dataset, table);
185
+ if (seen.has(key)) continue;
186
+ seen.add(key);
187
+ normalized.push({ dataset, table });
188
+ }
189
+ return normalized;
190
+ }
191
+ function tableKey(dataset, table) {
192
+ return `${dataset}.${table}`;
193
+ }
194
+ function buildTablesQuery(datasetProjectId, dataset, tables) {
195
+ const qualifier = `\`${datasetProjectId}.${dataset}.INFORMATION_SCHEMA.TABLES\``;
196
+ const filter = tables.length > 0 ? ` AND table_name IN (${tables.map((t) => `'${escapeSql(t.table)}'`).join(", ")})` : "";
197
+ return `SELECT table_name, table_schema, table_type
198
+ FROM ${qualifier}
199
+ WHERE table_schema = '${escapeSql(dataset)}'${filter}
200
+ ORDER BY table_schema, table_name`;
201
+ }
202
+ function buildColumnsQuery(datasetProjectId, dataset, tables) {
203
+ const qualifier = `\`${datasetProjectId}.${dataset}.INFORMATION_SCHEMA.COLUMNS\``;
204
+ const filter = tables.length > 0 ? ` AND table_name IN (${tables.map((t) => `'${escapeSql(t.table)}'`).join(", ")})` : "";
205
+ return `SELECT table_name, table_schema, column_name, data_type, ordinal_position, is_nullable
206
+ FROM ${qualifier}
207
+ WHERE table_schema = '${escapeSql(dataset)}'${filter}
208
+ ORDER BY table_schema, table_name, ordinal_position`;
209
+ }
210
+ function escapeSql(value) {
211
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "''");
212
+ }
213
+ function asTableType(tableType) {
214
+ const normalized = tableType?.toLowerCase() ?? "";
215
+ if (normalized.includes("view")) {
216
+ return normalized.includes("materialized") ? "materialized_view" : "view";
217
+ }
218
+ if (normalized.includes("external") || normalized.includes("snapshot") || normalized.includes("clone")) {
219
+ return "table";
220
+ }
221
+ return "table";
222
+ }
223
+
42
224
  // src/utils/clickhouse.ts
43
225
  var WRAPPER_REGEX = /^(Nullable|LowCardinality|SimpleAggregateFunction)\((.+)\)$/i;
44
226
  function unwrapTypeModifiers(type) {
@@ -108,7 +290,7 @@ var ClickHouseAdapter = class {
108
290
  this.defaultFormat = options.defaultFormat ?? "JSONEachRow";
109
291
  this.kind = options.kind ?? "clickhouse";
110
292
  if (options.allowedTables) {
111
- this.allowedTables = normalizeTableFilter(options.allowedTables);
293
+ this.allowedTables = normalizeTableFilter2(options.allowedTables);
112
294
  }
113
295
  }
114
296
  databaseName;
@@ -146,7 +328,7 @@ var ClickHouseAdapter = class {
146
328
  * No indexes, constraints, or statistics
147
329
  */
148
330
  async introspect(options) {
149
- const tablesToIntrospect = options?.tables ? normalizeTableFilter(options.tables) : this.allowedTables;
331
+ const tablesToIntrospect = options?.tables ? normalizeTableFilter2(options.tables) : this.allowedTables;
150
332
  const allowTables = tablesToIntrospect ?? [];
151
333
  const hasFilter = allowTables.length > 0;
152
334
  const queryParams = {
@@ -186,7 +368,7 @@ var ClickHouseAdapter = class {
186
368
  const base = {
187
369
  name: table.name,
188
370
  schema: this.databaseName,
189
- type: asTableType(table.engine),
371
+ type: asTableType2(table.engine),
190
372
  columns: tableColumns
191
373
  };
192
374
  const comment = sanitize(table.comment);
@@ -264,7 +446,7 @@ function normalizePayload(payload) {
264
446
  }
265
447
  return [];
266
448
  }
267
- function normalizeTableFilter(tables) {
449
+ function normalizeTableFilter2(tables) {
268
450
  if (!tables?.length) return [];
269
451
  const seen = /* @__PURE__ */ new Set();
270
452
  const normalized = [];
@@ -292,7 +474,7 @@ function transformColumnRow(row) {
292
474
  if (comment !== void 0) column.comment = comment;
293
475
  return column;
294
476
  }
295
- function asTableType(engine) {
477
+ function asTableType2(engine) {
296
478
  if (typeof engine === "string") {
297
479
  const normalized = engine.toLowerCase();
298
480
  if (normalized.includes("view")) {
@@ -321,7 +503,7 @@ var PostgresAdapter = class {
321
503
  this.defaultSchema = options.defaultSchema ?? "public";
322
504
  this.kind = options.kind ?? "postgres";
323
505
  if (options.allowedTables) {
324
- this.allowedTables = normalizeTableFilter2(
506
+ this.allowedTables = normalizeTableFilter3(
325
507
  options.allowedTables,
326
508
  this.defaultSchema
327
509
  );
@@ -348,7 +530,7 @@ var PostgresAdapter = class {
348
530
  return;
349
531
  }
350
532
  const allowedSet = new Set(
351
- this.allowedTables.map((t) => tableKey(t.schema, t.table))
533
+ this.allowedTables.map((t) => tableKey2(t.schema, t.table))
352
534
  );
353
535
  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*/)");
354
536
  const tablePattern = /(?:FROM|JOIN)\s+(?:ONLY\s+)?(?:([a-zA-Z_][a-zA-Z0-9_]*)\.)?(["']?[a-zA-Z_][a-zA-Z0-9_]*["']?)/gi;
@@ -357,7 +539,7 @@ var PostgresAdapter = class {
357
539
  const schema = match[1] ?? this.defaultSchema;
358
540
  const table = match[2]?.replace(/['"]/g, "");
359
541
  if (table) {
360
- const key = tableKey(schema, table);
542
+ const key = tableKey2(schema, table);
361
543
  if (!allowedSet.has(key)) {
362
544
  throw new Error(
363
545
  `Query references table "${schema}.${table}" which is not in the allowed tables list`
@@ -367,29 +549,31 @@ var PostgresAdapter = class {
367
549
  }
368
550
  }
369
551
  /**
370
- * Convert named params to positional array for PostgreSQL
371
- * PostgreSQL expects $1, $2, $3 in SQL and an array of values [val1, val2, val3]
552
+ * Convert params to positional array for PostgreSQL.
553
+ * PostgreSQL expects $1, $2, $3 in SQL and an array of values [val1, val2, val3].
554
+ * When the record has numeric keys ("1", "2", "3") — e.g. from mapGeneratedParams —
555
+ * use only those in order so placeholder order matches SQL. Otherwise fall back to
556
+ * named keys in alphabetical order for backward compatibility.
372
557
  */
373
558
  convertNamedToPositionalParams(params) {
374
559
  const numericKeys = Object.keys(params).filter((k) => /^\d+$/.test(k)).map((k) => Number.parseInt(k, 10)).sort((a, b) => a - b);
375
- const namedKeys = Object.keys(params).filter((k) => !/^\d+$/.test(k)).sort();
376
- const positionalParams = [];
377
- for (const key of numericKeys) {
378
- let val = params[String(key)];
379
- if (typeof val === "string") {
380
- const match = val.match(/^<([a-zA-Z0-9_]+)>$/);
381
- const namedKey = match?.[1];
382
- if (namedKey && namedKey in params) {
383
- val = params[namedKey];
560
+ if (numericKeys.length > 0) {
561
+ const positionalParams = [];
562
+ for (const key of numericKeys) {
563
+ let val = params[String(key)];
564
+ if (typeof val === "string") {
565
+ const match = val.match(/^<([a-zA-Z0-9_]+)>$/);
566
+ const namedKey = match?.[1];
567
+ if (namedKey && namedKey in params) {
568
+ val = params[namedKey];
569
+ }
384
570
  }
571
+ positionalParams.push(val);
385
572
  }
386
- positionalParams.push(val);
387
- }
388
- for (const key of namedKeys) {
389
- const val = params[key];
390
- positionalParams.push(val);
573
+ return positionalParams;
391
574
  }
392
- return positionalParams;
575
+ const namedKeys = Object.keys(params).filter((k) => !/^\d+$/.test(k)).sort();
576
+ return namedKeys.map((key) => params[key]);
393
577
  }
394
578
  async validate(sql, params) {
395
579
  let paramArray;
@@ -406,23 +590,23 @@ var PostgresAdapter = class {
406
590
  * No indexes, constraints, or statistics
407
591
  */
408
592
  async introspect(options) {
409
- const tablesToIntrospect = options?.tables ? normalizeTableFilter2(options.tables, this.defaultSchema) : this.allowedTables;
593
+ const tablesToIntrospect = options?.tables ? normalizeTableFilter3(options.tables, this.defaultSchema) : this.allowedTables;
410
594
  const normalizedTables = tablesToIntrospect ?? [];
411
595
  const tablesResult = await this.clientFn(
412
- buildTablesQuery(normalizedTables)
596
+ buildTablesQuery2(normalizedTables)
413
597
  );
414
598
  const tableRows = tablesResult.rows;
415
599
  const columnsResult = await this.clientFn(
416
- buildColumnsQuery(normalizedTables)
600
+ buildColumnsQuery2(normalizedTables)
417
601
  );
418
602
  const columnRows = columnsResult.rows;
419
603
  const tablesByKey = /* @__PURE__ */ new Map();
420
604
  for (const row of tableRows) {
421
- const key = tableKey(row.schema_name, row.table_name);
605
+ const key = tableKey2(row.schema_name, row.table_name);
422
606
  const table = {
423
607
  name: row.table_name,
424
608
  schema: row.schema_name,
425
- type: asTableType2(row.table_type),
609
+ type: asTableType3(row.table_type),
426
610
  columns: []
427
611
  };
428
612
  const comment = sanitize2(row.comment);
@@ -432,7 +616,7 @@ var PostgresAdapter = class {
432
616
  tablesByKey.set(key, table);
433
617
  }
434
618
  for (const row of columnRows) {
435
- const key = tableKey(row.table_schema, row.table_name);
619
+ const key = tableKey2(row.table_schema, row.table_name);
436
620
  const table = tablesByKey.get(key);
437
621
  if (!table) continue;
438
622
  const column = {
@@ -462,7 +646,7 @@ var PostgresAdapter = class {
462
646
  };
463
647
  }
464
648
  };
465
- function normalizeTableFilter2(tables, defaultSchema) {
649
+ function normalizeTableFilter3(tables, defaultSchema) {
466
650
  if (!tables?.length) return [];
467
651
  const normalized = [];
468
652
  const seen = /* @__PURE__ */ new Set();
@@ -476,14 +660,14 @@ function normalizeTableFilter2(tables, defaultSchema) {
476
660
  if (!isSafeIdentifier(schema) || !isSafeIdentifier(table)) {
477
661
  continue;
478
662
  }
479
- const key = tableKey(schema, table);
663
+ const key = tableKey2(schema, table);
480
664
  if (seen.has(key)) continue;
481
665
  seen.add(key);
482
666
  normalized.push({ schema, table });
483
667
  }
484
668
  return normalized;
485
669
  }
486
- function buildTablesQuery(tables) {
670
+ function buildTablesQuery2(tables) {
487
671
  const filter = buildFilterClause(tables, "n.nspname", "c.relname");
488
672
  return `SELECT
489
673
  c.relname AS table_name,
@@ -502,7 +686,7 @@ function buildTablesQuery(tables) {
502
686
  ${filter}
503
687
  ORDER BY n.nspname, c.relname;`;
504
688
  }
505
- function buildColumnsQuery(tables) {
689
+ function buildColumnsQuery2(tables) {
506
690
  const filter = buildFilterClause(
507
691
  tables,
508
692
  "cols.table_schema",
@@ -547,13 +731,13 @@ function buildFilterClause(tables, schemaExpr, tableExpr) {
547
731
  });
548
732
  return `AND (${clauses.join(" OR ")})`;
549
733
  }
550
- function tableKey(schema, table) {
734
+ function tableKey2(schema, table) {
551
735
  return `${schema}.${table}`;
552
736
  }
553
737
  function isSafeIdentifier(value) {
554
738
  return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
555
739
  }
556
- function asTableType2(value) {
740
+ function asTableType3(value) {
557
741
  const normalized = value.toLowerCase();
558
742
  if (normalized.includes("view")) {
559
743
  return normalized.includes("materialized") ? "materialized_view" : "view";
@@ -601,6 +785,13 @@ var ApiClient = class {
601
785
  getDefaultTenantId() {
602
786
  return this.defaultTenantId;
603
787
  }
788
+ /**
789
+ * Create a JWT for the given tenant (and optional userId, scopes).
790
+ * Use this when you need to pass a token to the embed (e.g. frontend or demo).
791
+ */
792
+ async createJwt(tenantId, userId, scopes) {
793
+ return await this.generateJWT(tenantId, userId, scopes);
794
+ }
604
795
  async get(path, tenantId, userId, scopes, signal, sessionId) {
605
796
  return await this.request(path, {
606
797
  method: "GET",
@@ -813,6 +1004,111 @@ var ApiClient = class {
813
1004
  }
814
1005
  };
815
1006
 
1007
+ // src/core/callback-client.ts
1008
+ var CallbackApiClient = class {
1009
+ requestHandler;
1010
+ defaultTenantId;
1011
+ constructor(requestHandler, options) {
1012
+ this.requestHandler = requestHandler;
1013
+ this.defaultTenantId = options?.defaultTenantId;
1014
+ }
1015
+ getDefaultTenantId() {
1016
+ return this.defaultTenantId;
1017
+ }
1018
+ async request(opts) {
1019
+ const { data, headers } = await this.requestHandler({
1020
+ method: opts.method,
1021
+ path: opts.path,
1022
+ body: opts.body,
1023
+ tenantId: opts.tenantId,
1024
+ userId: opts.userId,
1025
+ scopes: opts.scopes,
1026
+ signal: opts.signal,
1027
+ sessionId: opts.sessionId
1028
+ });
1029
+ return { data, headers };
1030
+ }
1031
+ async get(path, tenantId, userId, scopes, signal, sessionId) {
1032
+ const { data } = await this.request({
1033
+ method: "GET",
1034
+ path,
1035
+ tenantId,
1036
+ userId,
1037
+ scopes,
1038
+ signal,
1039
+ sessionId
1040
+ });
1041
+ return data;
1042
+ }
1043
+ async post(path, body, tenantId, userId, scopes, signal, sessionId) {
1044
+ const { data } = await this.request({
1045
+ method: "POST",
1046
+ path,
1047
+ body,
1048
+ tenantId,
1049
+ userId,
1050
+ scopes,
1051
+ signal,
1052
+ sessionId
1053
+ });
1054
+ return data;
1055
+ }
1056
+ async postWithHeaders(path, body, tenantId, userId, scopes, signal, sessionId) {
1057
+ const { data, headers } = await this.request({
1058
+ method: "POST",
1059
+ path,
1060
+ body,
1061
+ tenantId,
1062
+ userId,
1063
+ scopes,
1064
+ signal,
1065
+ sessionId
1066
+ });
1067
+ return {
1068
+ data,
1069
+ headers: headers ?? new Headers()
1070
+ };
1071
+ }
1072
+ async put(path, body, tenantId, userId, scopes, signal, sessionId) {
1073
+ const { data } = await this.request({
1074
+ method: "PUT",
1075
+ path,
1076
+ body,
1077
+ tenantId,
1078
+ userId,
1079
+ scopes,
1080
+ signal,
1081
+ sessionId
1082
+ });
1083
+ return data;
1084
+ }
1085
+ async patch(path, body, tenantId, userId, scopes, signal, sessionId) {
1086
+ const { data } = await this.request({
1087
+ method: "PATCH",
1088
+ path,
1089
+ body,
1090
+ tenantId,
1091
+ userId,
1092
+ scopes,
1093
+ signal,
1094
+ sessionId
1095
+ });
1096
+ return data;
1097
+ }
1098
+ async delete(path, tenantId, userId, scopes, signal, sessionId) {
1099
+ const { data } = await this.request({
1100
+ method: "DELETE",
1101
+ path,
1102
+ tenantId,
1103
+ userId,
1104
+ scopes,
1105
+ signal,
1106
+ sessionId
1107
+ });
1108
+ return data;
1109
+ }
1110
+ };
1111
+
816
1112
  // src/core/query-engine.ts
817
1113
  var QueryEngine = class {
818
1114
  databases = /* @__PURE__ */ new Map();
@@ -875,6 +1171,12 @@ var QueryEngine = class {
875
1171
  return [];
876
1172
  }
877
1173
  }
1174
+ /**
1175
+ * Maps API params (array in SQL placeholder order $1, $2, $3) to ParamRecord.
1176
+ * Sets both numeric keys ("1", "2", "3") and named keys so that:
1177
+ * - Postgres adapter uses numeric order for positional params (matches SQL).
1178
+ * - Named keys remain for tenant isolation / other lookups.
1179
+ */
878
1180
  mapGeneratedParams(params) {
879
1181
  const record = {};
880
1182
  params.forEach((param, index) => {
@@ -882,6 +1184,7 @@ var QueryEngine = class {
882
1184
  if (value === void 0) {
883
1185
  return;
884
1186
  }
1187
+ record[String(index + 1)] = value;
885
1188
  const nameCandidate = typeof param.name === "string" && param.name.trim() || typeof param.placeholder === "string" && param.placeholder.trim() || typeof param.position === "number" && String(param.position) || String(index + 1);
886
1189
  const key = nameCandidate.replace(/[{}:$]/g, "").trim();
887
1190
  record[key] = value;
@@ -902,6 +1205,9 @@ var QueryEngine = class {
902
1205
  const paramKey = tenantField;
903
1206
  params[paramKey] = tenantId;
904
1207
  tenantPredicate = `${tenantField} = {${tenantField}:${metadata.tenantFieldType ?? "String"}}`;
1208
+ } else if (metadata.dialect === "bigquery") {
1209
+ params[tenantField] = tenantId;
1210
+ tenantPredicate = `${tenantField} = @${tenantField}`;
905
1211
  } else {
906
1212
  const escapedId = tenantId.replace(/'/g, "''");
907
1213
  tenantPredicate = `${tenantField} = '${escapedId}'`;
@@ -1200,20 +1506,171 @@ async function deleteActiveChart(client, id, options, signal) {
1200
1506
  signal
1201
1507
  );
1202
1508
  }
1203
- function resolveTenantId2(client, tenantId) {
1204
- const resolved = tenantId ?? client.getDefaultTenantId();
1205
- if (!resolved) {
1206
- throw new Error(
1207
- "tenantId is required. Provide it per request or via defaultTenantId option."
1208
- );
1209
- }
1210
- return resolved;
1211
- }
1509
+ function resolveTenantId2(client, tenantId) {
1510
+ const resolved = tenantId ?? client.getDefaultTenantId();
1511
+ if (!resolved) {
1512
+ throw new Error(
1513
+ "tenantId is required. Provide it per request or via defaultTenantId option."
1514
+ );
1515
+ }
1516
+ return resolved;
1517
+ }
1518
+
1519
+ // src/routes/dashboards.ts
1520
+ function resolveTenantId3(client, tenantId) {
1521
+ const resolved = tenantId ?? client.getDefaultTenantId();
1522
+ if (resolved === void 0) {
1523
+ throw new Error("tenantId is required (option or client default)");
1524
+ }
1525
+ return resolved;
1526
+ }
1527
+ async function createDashboard(client, body, options, signal) {
1528
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1529
+ return await client.post(
1530
+ "/dashboards",
1531
+ body,
1532
+ tenantId,
1533
+ options?.userId,
1534
+ options?.scopes,
1535
+ signal
1536
+ );
1537
+ }
1538
+ async function listDashboards(client, options, signal) {
1539
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1540
+ const params = new URLSearchParams();
1541
+ if (options?.pagination?.page) {
1542
+ params.set("page", options.pagination.page.toString());
1543
+ }
1544
+ if (options?.pagination?.limit) {
1545
+ params.set("limit", options.pagination.limit.toString());
1546
+ }
1547
+ if (options?.status) {
1548
+ params.set("status", options.status);
1549
+ }
1550
+ if (options?.sortBy) {
1551
+ params.set("sort_by", options.sortBy);
1552
+ }
1553
+ if (options?.sortDir) {
1554
+ params.set("sort_dir", options.sortDir);
1555
+ }
1556
+ return await client.get(
1557
+ `/dashboards?${params.toString()}`,
1558
+ tenantId,
1559
+ options?.userId,
1560
+ options?.scopes,
1561
+ signal
1562
+ );
1563
+ }
1564
+ async function getDashboard(client, id, options, signal) {
1565
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1566
+ return await client.get(
1567
+ `/dashboards/${id}`,
1568
+ tenantId,
1569
+ options?.userId,
1570
+ options?.scopes,
1571
+ signal
1572
+ );
1573
+ }
1574
+ async function getDashboardForTenant(client, id, tenantId, options, signal) {
1575
+ const params = new URLSearchParams();
1576
+ params.set("tenant_id", tenantId);
1577
+ return await client.get(
1578
+ `/dashboards/${id}/for-tenant?${params.toString()}`,
1579
+ tenantId,
1580
+ options?.userId,
1581
+ options?.scopes,
1582
+ signal
1583
+ );
1584
+ }
1585
+ async function updateDashboard(client, id, body, options, signal) {
1586
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1587
+ return await client.put(
1588
+ `/dashboards/${id}`,
1589
+ body,
1590
+ tenantId,
1591
+ options?.userId,
1592
+ options?.scopes,
1593
+ signal
1594
+ );
1595
+ }
1596
+ async function updateDashboardStatus(client, id, status, options, signal) {
1597
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1598
+ return await client.patch(
1599
+ `/dashboards/${id}/status`,
1600
+ { status },
1601
+ tenantId,
1602
+ options?.userId,
1603
+ options?.scopes,
1604
+ signal
1605
+ );
1606
+ }
1607
+ async function deleteDashboard(client, id, options, signal) {
1608
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1609
+ await client.delete(
1610
+ `/dashboards/${id}`,
1611
+ tenantId,
1612
+ options?.userId,
1613
+ options?.scopes,
1614
+ signal
1615
+ );
1616
+ }
1617
+ async function forkDashboard(client, id, input, options, signal) {
1618
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1619
+ return await client.post(
1620
+ `/dashboards/${id}/fork`,
1621
+ input,
1622
+ tenantId,
1623
+ options?.userId,
1624
+ options?.scopes,
1625
+ signal
1626
+ );
1627
+ }
1628
+ async function updateFork(client, forkId, input, options, signal) {
1629
+ const tenantId = resolveTenantId3(client, options?.tenantId);
1630
+ return await client.put(
1631
+ `/dashboards/forks/${forkId}`,
1632
+ input,
1633
+ tenantId,
1634
+ options?.userId,
1635
+ options?.scopes,
1636
+ signal
1637
+ );
1638
+ }
1639
+ async function rollbackFork(client, forkId, tenantId, options, signal) {
1640
+ return await client.post(
1641
+ `/dashboards/forks/${forkId}/rollback`,
1642
+ { tenant_id: tenantId },
1643
+ tenantId,
1644
+ options?.userId,
1645
+ options?.scopes,
1646
+ signal
1647
+ );
1648
+ }
1649
+ async function deleteFork(client, forkId, tenantId, options, signal) {
1650
+ const params = new URLSearchParams();
1651
+ params.set("tenant_id", tenantId);
1652
+ await client.delete(
1653
+ `/dashboards/forks/${forkId}?${params.toString()}`,
1654
+ tenantId,
1655
+ options?.userId,
1656
+ options?.scopes,
1657
+ signal
1658
+ );
1659
+ }
1660
+ async function listForksForTenant(client, tenantId, options, signal) {
1661
+ return await client.get(
1662
+ `/dashboards/customer/${tenantId}`,
1663
+ tenantId,
1664
+ options?.userId,
1665
+ options?.scopes,
1666
+ signal
1667
+ );
1668
+ }
1212
1669
 
1213
1670
  // src/routes/ingest.ts
1214
1671
  var import_node_crypto2 = __toESM(require("crypto"), 1);
1215
1672
  async function syncSchema(client, queryEngine, databaseName, options, signal) {
1216
- const tenantId = resolveTenantId3(client, options.tenantId);
1673
+ const tenantId = resolveTenantId4(client, options.tenantId);
1217
1674
  const adapter = queryEngine.getDatabase(databaseName);
1218
1675
  const metadata = queryEngine.getDatabaseMetadata(databaseName);
1219
1676
  const introspection = await adapter.introspect(
@@ -1235,7 +1692,7 @@ async function syncSchema(client, queryEngine, databaseName, options, signal) {
1235
1692
  );
1236
1693
  return response;
1237
1694
  }
1238
- function resolveTenantId3(client, tenantId) {
1695
+ function resolveTenantId4(client, tenantId) {
1239
1696
  const resolved = tenantId ?? client.getDefaultTenantId();
1240
1697
  if (!resolved) {
1241
1698
  throw new Error(
@@ -1277,7 +1734,7 @@ var import_node_crypto4 = __toESM(require("crypto"), 1);
1277
1734
  // src/routes/query.ts
1278
1735
  var import_node_crypto3 = __toESM(require("crypto"), 1);
1279
1736
  async function ask(client, queryEngine, question, options, signal) {
1280
- const tenantId = resolveTenantId4(client, options.tenantId);
1737
+ const tenantId = resolveTenantId5(client, options.tenantId);
1281
1738
  const sessionId = import_node_crypto3.default.randomUUID();
1282
1739
  const querypanelSessionId = options.querypanelSessionId ?? sessionId;
1283
1740
  const maxRetry = options.maxRetry ?? 0;
@@ -1307,7 +1764,8 @@ async function ask(client, queryEngine, question, options, signal) {
1307
1764
  ...options.maxRetry ? { max_retry: options.maxRetry } : {},
1308
1765
  ...tenantSettings ? { tenant_settings: tenantSettings } : {},
1309
1766
  ...databaseName ? { database: databaseName } : {},
1310
- ...metadata?.dialect ? { dialect: metadata.dialect } : {}
1767
+ ...metadata?.dialect ? { dialect: metadata.dialect } : {},
1768
+ ...options.model?.trim() ? { model: options.model.trim() } : {}
1311
1769
  },
1312
1770
  tenantId,
1313
1771
  options.userId,
@@ -1331,7 +1789,7 @@ async function ask(client, queryEngine, question, options, signal) {
1331
1789
  const dbName = queryResponse.data.database ?? options.database ?? queryEngine.getDefaultDatabase();
1332
1790
  if (!dbName) {
1333
1791
  throw new Error(
1334
- "No database attached. Call attachPostgres/attachClickhouse first."
1792
+ "No database attached. Call attachPostgres, attachClickhouse, or attachBigQuery first."
1335
1793
  );
1336
1794
  }
1337
1795
  const paramMetadata = Array.isArray(queryResponse.data.params) ? queryResponse.data.params : [];
@@ -1432,7 +1890,7 @@ async function ask(client, queryEngine, question, options, signal) {
1432
1890
  }
1433
1891
  throw new Error("Unexpected error in ask retry loop");
1434
1892
  }
1435
- function resolveTenantId4(client, tenantId) {
1893
+ function resolveTenantId5(client, tenantId) {
1436
1894
  const resolved = tenantId ?? client.getDefaultTenantId();
1437
1895
  if (!resolved) {
1438
1896
  throw new Error(
@@ -1588,7 +2046,7 @@ function stripVizSpecOnlyHints(hints) {
1588
2046
  const { kind: _kind, ...rest } = hints;
1589
2047
  return rest;
1590
2048
  }
1591
- function resolveTenantId5(client, tenantId) {
2049
+ function resolveTenantId6(client, tenantId) {
1592
2050
  const resolved = tenantId ?? client.getDefaultTenantId();
1593
2051
  if (!resolved) {
1594
2052
  throw new Error(
@@ -1598,7 +2056,7 @@ function resolveTenantId5(client, tenantId) {
1598
2056
  return resolved;
1599
2057
  }
1600
2058
  async function modifyChart(client, queryEngine, input, options, signal) {
1601
- const tenantId = resolveTenantId5(client, options?.tenantId);
2059
+ const tenantId = resolveTenantId6(client, options?.tenantId);
1602
2060
  const sessionId = import_node_crypto4.default.randomUUID();
1603
2061
  const querypanelSessionId = options?.querypanelSessionId ?? sessionId;
1604
2062
  const hasQuerypanelSessionContext = !!options?.querypanelSessionId;
@@ -1766,7 +2224,7 @@ async function modifyChart(client, queryEngine, input, options, signal) {
1766
2224
 
1767
2225
  // src/routes/sessions.ts
1768
2226
  async function listSessions(client, options, signal) {
1769
- const tenantId = resolveTenantId6(client, options?.tenantId);
2227
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1770
2228
  const params = new URLSearchParams();
1771
2229
  if (options?.pagination?.page)
1772
2230
  params.set("page", `${options.pagination.page}`);
@@ -1789,7 +2247,7 @@ async function listSessions(client, options, signal) {
1789
2247
  );
1790
2248
  }
1791
2249
  async function getSession(client, sessionId, options, signal) {
1792
- const tenantId = resolveTenantId6(client, options?.tenantId);
2250
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1793
2251
  const params = new URLSearchParams();
1794
2252
  if (options?.includeTurns !== void 0) {
1795
2253
  params.set("include_turns", `${options.includeTurns}`);
@@ -1803,7 +2261,7 @@ async function getSession(client, sessionId, options, signal) {
1803
2261
  );
1804
2262
  }
1805
2263
  async function updateSession(client, sessionId, body, options, signal) {
1806
- const tenantId = resolveTenantId6(client, options?.tenantId);
2264
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1807
2265
  return await client.patch(
1808
2266
  `/sessions/${encodeURIComponent(sessionId)}`,
1809
2267
  body,
@@ -1814,7 +2272,7 @@ async function updateSession(client, sessionId, body, options, signal) {
1814
2272
  );
1815
2273
  }
1816
2274
  async function deleteSession(client, sessionId, options, signal) {
1817
- const tenantId = resolveTenantId6(client, options?.tenantId);
2275
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1818
2276
  await client.delete(
1819
2277
  `/sessions/${encodeURIComponent(sessionId)}`,
1820
2278
  tenantId,
@@ -1823,7 +2281,7 @@ async function deleteSession(client, sessionId, options, signal) {
1823
2281
  signal
1824
2282
  );
1825
2283
  }
1826
- function resolveTenantId6(client, tenantId) {
2284
+ function resolveTenantId7(client, tenantId) {
1827
2285
  const resolved = tenantId ?? client.getDefaultTenantId();
1828
2286
  if (!resolved) {
1829
2287
  throw new Error(
@@ -1836,7 +2294,7 @@ function resolveTenantId6(client, tenantId) {
1836
2294
  // src/routes/vizspec.ts
1837
2295
  var import_node_crypto5 = __toESM(require("crypto"), 1);
1838
2296
  async function generateVizSpec(client, input, options, signal) {
1839
- const tenantId = resolveTenantId7(client, options?.tenantId);
2297
+ const tenantId = resolveTenantId8(client, options?.tenantId);
1840
2298
  const sessionId = import_node_crypto5.default.randomUUID();
1841
2299
  const response = await client.post(
1842
2300
  "/vizspec",
@@ -1858,7 +2316,7 @@ async function generateVizSpec(client, input, options, signal) {
1858
2316
  );
1859
2317
  return response;
1860
2318
  }
1861
- function resolveTenantId7(client, tenantId) {
2319
+ function resolveTenantId8(client, tenantId) {
1862
2320
  const resolved = tenantId ?? client.getDefaultTenantId();
1863
2321
  if (!resolved) {
1864
2322
  throw new Error(
@@ -1869,13 +2327,64 @@ function resolveTenantId7(client, tenantId) {
1869
2327
  }
1870
2328
 
1871
2329
  // src/index.ts
1872
- var QueryPanelSdkAPI = class {
2330
+ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
1873
2331
  client;
1874
2332
  queryEngine;
1875
- constructor(baseUrl, privateKey, organizationId, options) {
1876
- this.client = new ApiClient(baseUrl, privateKey, organizationId, options);
2333
+ /**
2334
+ * @param workspaceId - Workspace UUID (same value as your org in the dashboard). Passed to the API as JWT claim `organizationId`.
2335
+ */
2336
+ constructor(baseUrl, privateKey, workspaceId, options) {
2337
+ if (options?.api) {
2338
+ this.client = options.api;
2339
+ } else {
2340
+ if (!baseUrl) throw new Error("Base URL is required");
2341
+ if (!privateKey) throw new Error("Private key is required");
2342
+ if (!workspaceId) throw new Error("Workspace ID is required");
2343
+ this.client = new ApiClient(baseUrl, privateKey, workspaceId, {
2344
+ defaultTenantId: options?.defaultTenantId,
2345
+ additionalHeaders: options?.additionalHeaders,
2346
+ fetch: options?.fetch
2347
+ });
2348
+ }
1877
2349
  this.queryEngine = new QueryEngine();
1878
2350
  }
2351
+ /**
2352
+ * Create an SDK instance that uses a request callback instead of HTTP.
2353
+ * Use this when calling the SDK from within your own API so that "API" calls
2354
+ * are handled in-process (e.g. by invoking querypanel-sdk services directly)
2355
+ * and do not cause recursion.
2356
+ *
2357
+ * @param workspaceId - Workspace identifier (sent as `organizationId` in JWTs and API auth)
2358
+ * @param requestHandler - Callback invoked for each logical API request (method, path, body, tenantId, etc.)
2359
+ * @param options - Optional defaultTenantId
2360
+ * @returns QueryPanelSdkAPI instance; attach databases and call ask(), syncSchema(), etc. as usual
2361
+ *
2362
+ * @example
2363
+ * ```ts
2364
+ * const qp = QueryPanelSdkAPI.withCallbacks(
2365
+ * process.env.QUERYPANEL_WORKSPACE_ID!,
2366
+ * async (opts) => {
2367
+ * // Call your in-process services (e.g. querypanel-sdk logic) instead of HTTP
2368
+ * if (opts.path === '/query' || opts.path === '/v2/query') {
2369
+ * const result = await yourSqlGenerator.generate(opts.body, opts.tenantId);
2370
+ * return { data: result, headers: new Headers({ 'x-querypanel-session-id': result.sessionId }) };
2371
+ * }
2372
+ * if (opts.path === '/chart') return { data: await yourChartService.generate(opts.body) };
2373
+ * // ... other paths
2374
+ * return { data: await yourProxy(opts) };
2375
+ * },
2376
+ * { defaultTenantId: process.env.DEFAULT_TENANT_ID }
2377
+ * );
2378
+ * qp.attachPostgres('db', createClient, { tenantFieldName: 'tenant_id' });
2379
+ * const result = await qp.ask('Top 10 orders', { tenantId: 't1', database: 'db' });
2380
+ * ```
2381
+ */
2382
+ static withCallbacks(workspaceId, requestHandler, options) {
2383
+ const api = new CallbackApiClient(requestHandler, {
2384
+ defaultTenantId: options?.defaultTenantId
2385
+ });
2386
+ return new _QueryPanelSdkAPI("", "", workspaceId, { api });
2387
+ }
1879
2388
  // Database attachment methods
1880
2389
  attachClickhouse(name, clientFn, options) {
1881
2390
  const adapter = new ClickHouseAdapter(clientFn, options);
@@ -1903,6 +2412,19 @@ var QueryPanelSdkAPI = class {
1903
2412
  };
1904
2413
  this.queryEngine.attachDatabase(name, adapter, metadata);
1905
2414
  }
2415
+ attachBigQuery(name, clientFn, options) {
2416
+ const adapter = new BigQueryAdapter(clientFn, options);
2417
+ const metadata = {
2418
+ name,
2419
+ dialect: "bigquery",
2420
+ description: options?.description,
2421
+ tags: options?.tags,
2422
+ tenantFieldName: options?.tenantFieldName,
2423
+ tenantFieldType: options?.tenantFieldType ?? "String",
2424
+ enforceTenantIsolation: options?.tenantFieldName ? options?.enforceTenantIsolation ?? true : void 0
2425
+ };
2426
+ this.queryEngine.attachDatabase(name, adapter, metadata);
2427
+ }
1906
2428
  attachDatabase(name, adapter) {
1907
2429
  const metadata = {
1908
2430
  name,
@@ -1989,6 +2511,48 @@ var QueryPanelSdkAPI = class {
1989
2511
  signal
1990
2512
  );
1991
2513
  }
2514
+ // Embedded dashboard / JWT
2515
+ /**
2516
+ * Creates a JWT for the given tenant (and optional userId, scopes).
2517
+ * Use this when you need to pass a token to the embed (e.g. frontend or demo).
2518
+ * Only available when using the HTTP client (ApiClient); not available when using withCallbacks.
2519
+ */
2520
+ async createJwt(options) {
2521
+ const tenantId = options.tenantId?.trim();
2522
+ if (!tenantId) {
2523
+ throw new Error("tenantId is required");
2524
+ }
2525
+ if (!(this.client instanceof ApiClient)) {
2526
+ throw new Error(
2527
+ "createJwt is not available when using withCallbacks. Use the SDK with baseUrl + privateKey to create JWTs."
2528
+ );
2529
+ }
2530
+ return await this.client.createJwt(
2531
+ tenantId,
2532
+ options.userId,
2533
+ options.scopes
2534
+ );
2535
+ }
2536
+ /**
2537
+ * Runs SQL against an attached database with tenant isolation applied.
2538
+ * Used by embed run-sql (e.g. dashboard chart execution). Requires a database
2539
+ * to be attached (e.g. via attachPostgres) and options.tenantId.
2540
+ */
2541
+ async runSqlForDashboard(input, options) {
2542
+ const tenantId = options.tenantId?.trim();
2543
+ if (!tenantId) {
2544
+ throw new Error("tenantId is required");
2545
+ }
2546
+ const databaseName = input.database ?? "db";
2547
+ const params = input.params ?? {};
2548
+ const result = await this.queryEngine.validateAndExecute(
2549
+ input.sql,
2550
+ params,
2551
+ databaseName,
2552
+ tenantId
2553
+ );
2554
+ return { rows: result.rows, fields: result.fields };
2555
+ }
1992
2556
  // VizSpec generation
1993
2557
  /**
1994
2558
  * Generates a VizSpec visualization specification from query results.
@@ -2432,9 +2996,298 @@ var QueryPanelSdkAPI = class {
2432
2996
  async deleteActiveChart(id, options, signal) {
2433
2997
  await deleteActiveChart(this.client, id, options, signal);
2434
2998
  }
2999
+ // Dashboard CRUD operations
3000
+ /**
3001
+ * Creates a new dashboard with BlockNote content.
3002
+ *
3003
+ * @param body - Dashboard configuration including name and BlockNote content
3004
+ * @param options - Tenant, user, and scope options
3005
+ * @param signal - Optional AbortSignal for cancellation
3006
+ * @returns Created dashboard
3007
+ *
3008
+ * @example
3009
+ * ```typescript
3010
+ * const dashboard = await qp.createDashboard({
3011
+ * name: "Sales Dashboard",
3012
+ * description: "Monthly sales metrics",
3013
+ * content_json: blockNoteContent,
3014
+ * editor_type: "blocknote",
3015
+ * }, { tenantId: "tenant_123" });
3016
+ * ```
3017
+ */
3018
+ async createDashboard(body, options, signal) {
3019
+ return await createDashboard(
3020
+ this.client,
3021
+ body,
3022
+ options,
3023
+ signal
3024
+ );
3025
+ }
3026
+ /**
3027
+ * Lists dashboards with pagination and filtering.
3028
+ *
3029
+ * @param options - Filtering and pagination options
3030
+ * @param signal - Optional AbortSignal for cancellation
3031
+ * @returns Paginated list of dashboards
3032
+ *
3033
+ * @example
3034
+ * ```typescript
3035
+ * const dashboards = await qp.listDashboards({
3036
+ * tenantId: "tenant_123",
3037
+ * status: "deployed",
3038
+ * pagination: { page: 1, limit: 10 },
3039
+ * });
3040
+ * ```
3041
+ */
3042
+ async listDashboards(options, signal) {
3043
+ return await listDashboards(this.client, options, signal);
3044
+ }
3045
+ /**
3046
+ * Gets a dashboard by ID.
3047
+ *
3048
+ * @param id - Dashboard ID
3049
+ * @param options - Tenant, user, and scope options
3050
+ * @param signal - Optional AbortSignal for cancellation
3051
+ * @returns Dashboard details
3052
+ *
3053
+ * @example
3054
+ * ```typescript
3055
+ * const dashboard = await qp.getDashboard("dash_123", {
3056
+ * tenantId: "tenant_123",
3057
+ * });
3058
+ * ```
3059
+ */
3060
+ async getDashboard(id, options, signal) {
3061
+ return await getDashboard(
3062
+ this.client,
3063
+ id,
3064
+ options,
3065
+ signal
3066
+ );
3067
+ }
3068
+ /**
3069
+ * Gets a dashboard for a specific tenant.
3070
+ * Returns customer fork if exists, otherwise returns the original dashboard.
3071
+ *
3072
+ * @param id - Dashboard ID
3073
+ * @param tenantId - Tenant ID to check for fork
3074
+ * @param options - Additional auth options
3075
+ * @param signal - Optional AbortSignal for cancellation
3076
+ * @returns Dashboard (fork if exists, original otherwise)
3077
+ *
3078
+ * @example
3079
+ * ```typescript
3080
+ * const dashboard = await qp.getDashboardForTenant(
3081
+ * "dash_123",
3082
+ * "tenant_456",
3083
+ * );
3084
+ * ```
3085
+ */
3086
+ async getDashboardForTenant(id, tenantId, options, signal) {
3087
+ return await getDashboardForTenant(
3088
+ this.client,
3089
+ id,
3090
+ tenantId,
3091
+ options,
3092
+ signal
3093
+ );
3094
+ }
3095
+ /**
3096
+ * Updates a dashboard.
3097
+ *
3098
+ * @param id - Dashboard ID to update
3099
+ * @param body - Fields to update
3100
+ * @param options - Tenant, user, and scope options
3101
+ * @param signal - Optional AbortSignal for cancellation
3102
+ * @returns Updated dashboard
3103
+ *
3104
+ * @example
3105
+ * ```typescript
3106
+ * const updated = await qp.updateDashboard("dash_123", {
3107
+ * name: "Updated Dashboard",
3108
+ * content_json: newBlockNoteContent,
3109
+ * }, { tenantId: "tenant_123" });
3110
+ * ```
3111
+ */
3112
+ async updateDashboard(id, body, options, signal) {
3113
+ return await updateDashboard(
3114
+ this.client,
3115
+ id,
3116
+ body,
3117
+ options,
3118
+ signal
3119
+ );
3120
+ }
3121
+ /**
3122
+ * Updates dashboard status (deploy/undeploy).
3123
+ *
3124
+ * @param id - Dashboard ID
3125
+ * @param status - New status
3126
+ * @param options - Tenant, user, and scope options
3127
+ * @param signal - Optional AbortSignal for cancellation
3128
+ * @returns Updated dashboard
3129
+ *
3130
+ * @example
3131
+ * ```typescript
3132
+ * const deployed = await qp.updateDashboardStatus(
3133
+ * "dash_123",
3134
+ * "deployed",
3135
+ * { tenantId: "tenant_123" },
3136
+ * );
3137
+ * ```
3138
+ */
3139
+ async updateDashboardStatus(id, status, options, signal) {
3140
+ return await updateDashboardStatus(
3141
+ this.client,
3142
+ id,
3143
+ status,
3144
+ options,
3145
+ signal
3146
+ );
3147
+ }
3148
+ /**
3149
+ * Deletes a dashboard.
3150
+ *
3151
+ * @param id - Dashboard ID to delete
3152
+ * @param options - Tenant, user, and scope options
3153
+ * @param signal - Optional AbortSignal for cancellation
3154
+ *
3155
+ * @example
3156
+ * ```typescript
3157
+ * await qp.deleteDashboard("dash_123", { tenantId: "tenant_123" });
3158
+ * ```
3159
+ */
3160
+ async deleteDashboard(id, options, signal) {
3161
+ await deleteDashboard(this.client, id, options, signal);
3162
+ }
3163
+ // Dashboard Fork operations (Customer Customization)
3164
+ /**
3165
+ * Forks a dashboard for customer customization (copy-on-write).
3166
+ *
3167
+ * Creates a full copy of the dashboard that the customer can edit independently.
3168
+ * The original dashboard remains unchanged.
3169
+ *
3170
+ * @param id - Dashboard ID to fork
3171
+ * @param input - Fork configuration with tenant_id
3172
+ * @param options - Additional auth options
3173
+ * @param signal - Optional AbortSignal for cancellation
3174
+ * @returns Forked dashboard
3175
+ *
3176
+ * @example
3177
+ * ```typescript
3178
+ * const fork = await qp.forkDashboard("dash_123", {
3179
+ * tenant_id: "tenant_456",
3180
+ * name: "Customer's Custom Dashboard",
3181
+ * });
3182
+ * ```
3183
+ */
3184
+ async forkDashboard(id, input, options, signal) {
3185
+ return await forkDashboard(
3186
+ this.client,
3187
+ id,
3188
+ input,
3189
+ options,
3190
+ signal
3191
+ );
3192
+ }
3193
+ /**
3194
+ * Updates a customer fork.
3195
+ *
3196
+ * @param forkId - Fork ID to update
3197
+ * @param input - Update data including tenant_id
3198
+ * @param options - Additional auth options
3199
+ * @param signal - Optional AbortSignal for cancellation
3200
+ * @returns Updated fork
3201
+ *
3202
+ * @example
3203
+ * ```typescript
3204
+ * const updated = await qp.updateFork("fork_123", {
3205
+ * tenant_id: "tenant_456",
3206
+ * content_json: newBlockNoteContent,
3207
+ * });
3208
+ * ```
3209
+ */
3210
+ async updateFork(forkId, input, options, signal) {
3211
+ return await updateFork(
3212
+ this.client,
3213
+ forkId,
3214
+ input,
3215
+ options,
3216
+ signal
3217
+ );
3218
+ }
3219
+ /**
3220
+ * Rollbacks a fork to the original dashboard.
3221
+ * This deletes the fork and the customer will see the original again.
3222
+ *
3223
+ * @param forkId - Fork ID to rollback
3224
+ * @param tenantId - Tenant ID
3225
+ * @param options - Additional auth options
3226
+ * @param signal - Optional AbortSignal for cancellation
3227
+ * @returns Original dashboard
3228
+ *
3229
+ * @example
3230
+ * ```typescript
3231
+ * const original = await qp.rollbackFork("fork_123", "tenant_456");
3232
+ * ```
3233
+ */
3234
+ async rollbackFork(forkId, tenantId, options, signal) {
3235
+ return await rollbackFork(
3236
+ this.client,
3237
+ forkId,
3238
+ tenantId,
3239
+ options,
3240
+ signal
3241
+ );
3242
+ }
3243
+ /**
3244
+ * Deletes a customer fork.
3245
+ *
3246
+ * @param forkId - Fork ID to delete
3247
+ * @param tenantId - Tenant ID
3248
+ * @param options - Additional auth options
3249
+ * @param signal - Optional AbortSignal for cancellation
3250
+ *
3251
+ * @example
3252
+ * ```typescript
3253
+ * await qp.deleteFork("fork_123", "tenant_456");
3254
+ * ```
3255
+ */
3256
+ async deleteFork(forkId, tenantId, options, signal) {
3257
+ await deleteFork(
3258
+ this.client,
3259
+ forkId,
3260
+ tenantId,
3261
+ options,
3262
+ signal
3263
+ );
3264
+ }
3265
+ /**
3266
+ * Lists all customer forks for a tenant.
3267
+ *
3268
+ * @param tenantId - Tenant ID
3269
+ * @param options - Additional auth options
3270
+ * @param signal - Optional AbortSignal for cancellation
3271
+ * @returns Array of forks
3272
+ *
3273
+ * @example
3274
+ * ```typescript
3275
+ * const forks = await qp.listForksForTenant("tenant_456");
3276
+ * ```
3277
+ */
3278
+ async listForksForTenant(tenantId, options, signal) {
3279
+ return await listForksForTenant(
3280
+ this.client,
3281
+ tenantId,
3282
+ options,
3283
+ signal
3284
+ );
3285
+ }
2435
3286
  };
2436
3287
  // Annotate the CommonJS export names for ESM import in node:
2437
3288
  0 && (module.exports = {
3289
+ BigQueryAdapter,
3290
+ CallbackApiClient,
2438
3291
  ClickHouseAdapter,
2439
3292
  PostgresAdapter,
2440
3293
  QueryErrorCode,