@querypanel/node-sdk 1.0.56 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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);
573
+ return positionalParams;
387
574
  }
388
- for (const key of namedKeys) {
389
- const val = params[key];
390
- positionalParams.push(val);
391
- }
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}'`;
@@ -1190,30 +1496,181 @@ async function updateActiveChart(client, id, body, options, signal) {
1190
1496
  signal
1191
1497
  );
1192
1498
  }
1193
- async function deleteActiveChart(client, id, options, signal) {
1194
- const tenantId = resolveTenantId2(client, options?.tenantId);
1195
- await client.delete(
1196
- `/active-charts/${encodeURIComponent(id)}`,
1499
+ async function deleteActiveChart(client, id, options, signal) {
1500
+ const tenantId = resolveTenantId2(client, options?.tenantId);
1501
+ await client.delete(
1502
+ `/active-charts/${encodeURIComponent(id)}`,
1503
+ tenantId,
1504
+ options?.userId,
1505
+ options?.scopes,
1506
+ signal
1507
+ );
1508
+ }
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}`,
1197
1663
  tenantId,
1198
1664
  options?.userId,
1199
1665
  options?.scopes,
1200
1666
  signal
1201
1667
  );
1202
1668
  }
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
- }
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;
@@ -1331,7 +1788,7 @@ async function ask(client, queryEngine, question, options, signal) {
1331
1788
  const dbName = queryResponse.data.database ?? options.database ?? queryEngine.getDefaultDatabase();
1332
1789
  if (!dbName) {
1333
1790
  throw new Error(
1334
- "No database attached. Call attachPostgres/attachClickhouse first."
1791
+ "No database attached. Call attachPostgres, attachClickhouse, or attachBigQuery first."
1335
1792
  );
1336
1793
  }
1337
1794
  const paramMetadata = Array.isArray(queryResponse.data.params) ? queryResponse.data.params : [];
@@ -1432,7 +1889,7 @@ async function ask(client, queryEngine, question, options, signal) {
1432
1889
  }
1433
1890
  throw new Error("Unexpected error in ask retry loop");
1434
1891
  }
1435
- function resolveTenantId4(client, tenantId) {
1892
+ function resolveTenantId5(client, tenantId) {
1436
1893
  const resolved = tenantId ?? client.getDefaultTenantId();
1437
1894
  if (!resolved) {
1438
1895
  throw new Error(
@@ -1588,7 +2045,7 @@ function stripVizSpecOnlyHints(hints) {
1588
2045
  const { kind: _kind, ...rest } = hints;
1589
2046
  return rest;
1590
2047
  }
1591
- function resolveTenantId5(client, tenantId) {
2048
+ function resolveTenantId6(client, tenantId) {
1592
2049
  const resolved = tenantId ?? client.getDefaultTenantId();
1593
2050
  if (!resolved) {
1594
2051
  throw new Error(
@@ -1598,7 +2055,7 @@ function resolveTenantId5(client, tenantId) {
1598
2055
  return resolved;
1599
2056
  }
1600
2057
  async function modifyChart(client, queryEngine, input, options, signal) {
1601
- const tenantId = resolveTenantId5(client, options?.tenantId);
2058
+ const tenantId = resolveTenantId6(client, options?.tenantId);
1602
2059
  const sessionId = import_node_crypto4.default.randomUUID();
1603
2060
  const querypanelSessionId = options?.querypanelSessionId ?? sessionId;
1604
2061
  const hasQuerypanelSessionContext = !!options?.querypanelSessionId;
@@ -1766,7 +2223,7 @@ async function modifyChart(client, queryEngine, input, options, signal) {
1766
2223
 
1767
2224
  // src/routes/sessions.ts
1768
2225
  async function listSessions(client, options, signal) {
1769
- const tenantId = resolveTenantId6(client, options?.tenantId);
2226
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1770
2227
  const params = new URLSearchParams();
1771
2228
  if (options?.pagination?.page)
1772
2229
  params.set("page", `${options.pagination.page}`);
@@ -1789,7 +2246,7 @@ async function listSessions(client, options, signal) {
1789
2246
  );
1790
2247
  }
1791
2248
  async function getSession(client, sessionId, options, signal) {
1792
- const tenantId = resolveTenantId6(client, options?.tenantId);
2249
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1793
2250
  const params = new URLSearchParams();
1794
2251
  if (options?.includeTurns !== void 0) {
1795
2252
  params.set("include_turns", `${options.includeTurns}`);
@@ -1803,7 +2260,7 @@ async function getSession(client, sessionId, options, signal) {
1803
2260
  );
1804
2261
  }
1805
2262
  async function updateSession(client, sessionId, body, options, signal) {
1806
- const tenantId = resolveTenantId6(client, options?.tenantId);
2263
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1807
2264
  return await client.patch(
1808
2265
  `/sessions/${encodeURIComponent(sessionId)}`,
1809
2266
  body,
@@ -1814,7 +2271,7 @@ async function updateSession(client, sessionId, body, options, signal) {
1814
2271
  );
1815
2272
  }
1816
2273
  async function deleteSession(client, sessionId, options, signal) {
1817
- const tenantId = resolveTenantId6(client, options?.tenantId);
2274
+ const tenantId = resolveTenantId7(client, options?.tenantId);
1818
2275
  await client.delete(
1819
2276
  `/sessions/${encodeURIComponent(sessionId)}`,
1820
2277
  tenantId,
@@ -1823,7 +2280,7 @@ async function deleteSession(client, sessionId, options, signal) {
1823
2280
  signal
1824
2281
  );
1825
2282
  }
1826
- function resolveTenantId6(client, tenantId) {
2283
+ function resolveTenantId7(client, tenantId) {
1827
2284
  const resolved = tenantId ?? client.getDefaultTenantId();
1828
2285
  if (!resolved) {
1829
2286
  throw new Error(
@@ -1836,7 +2293,7 @@ function resolveTenantId6(client, tenantId) {
1836
2293
  // src/routes/vizspec.ts
1837
2294
  var import_node_crypto5 = __toESM(require("crypto"), 1);
1838
2295
  async function generateVizSpec(client, input, options, signal) {
1839
- const tenantId = resolveTenantId7(client, options?.tenantId);
2296
+ const tenantId = resolveTenantId8(client, options?.tenantId);
1840
2297
  const sessionId = import_node_crypto5.default.randomUUID();
1841
2298
  const response = await client.post(
1842
2299
  "/vizspec",
@@ -1858,7 +2315,7 @@ async function generateVizSpec(client, input, options, signal) {
1858
2315
  );
1859
2316
  return response;
1860
2317
  }
1861
- function resolveTenantId7(client, tenantId) {
2318
+ function resolveTenantId8(client, tenantId) {
1862
2319
  const resolved = tenantId ?? client.getDefaultTenantId();
1863
2320
  if (!resolved) {
1864
2321
  throw new Error(
@@ -1869,13 +2326,64 @@ function resolveTenantId7(client, tenantId) {
1869
2326
  }
1870
2327
 
1871
2328
  // src/index.ts
1872
- var QueryPanelSdkAPI = class {
2329
+ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
1873
2330
  client;
1874
2331
  queryEngine;
1875
- constructor(baseUrl, privateKey, organizationId, options) {
1876
- this.client = new ApiClient(baseUrl, privateKey, organizationId, options);
2332
+ /**
2333
+ * @param workspaceId - Workspace UUID (same value as your org in the dashboard). Passed to the API as JWT claim `organizationId`.
2334
+ */
2335
+ constructor(baseUrl, privateKey, workspaceId, options) {
2336
+ if (options?.api) {
2337
+ this.client = options.api;
2338
+ } else {
2339
+ if (!baseUrl) throw new Error("Base URL is required");
2340
+ if (!privateKey) throw new Error("Private key is required");
2341
+ if (!workspaceId) throw new Error("Workspace ID is required");
2342
+ this.client = new ApiClient(baseUrl, privateKey, workspaceId, {
2343
+ defaultTenantId: options?.defaultTenantId,
2344
+ additionalHeaders: options?.additionalHeaders,
2345
+ fetch: options?.fetch
2346
+ });
2347
+ }
1877
2348
  this.queryEngine = new QueryEngine();
1878
2349
  }
2350
+ /**
2351
+ * Create an SDK instance that uses a request callback instead of HTTP.
2352
+ * Use this when calling the SDK from within your own API so that "API" calls
2353
+ * are handled in-process (e.g. by invoking querypanel-sdk services directly)
2354
+ * and do not cause recursion.
2355
+ *
2356
+ * @param workspaceId - Workspace identifier (sent as `organizationId` in JWTs and API auth)
2357
+ * @param requestHandler - Callback invoked for each logical API request (method, path, body, tenantId, etc.)
2358
+ * @param options - Optional defaultTenantId
2359
+ * @returns QueryPanelSdkAPI instance; attach databases and call ask(), syncSchema(), etc. as usual
2360
+ *
2361
+ * @example
2362
+ * ```ts
2363
+ * const qp = QueryPanelSdkAPI.withCallbacks(
2364
+ * process.env.QUERYPANEL_WORKSPACE_ID!,
2365
+ * async (opts) => {
2366
+ * // Call your in-process services (e.g. querypanel-sdk logic) instead of HTTP
2367
+ * if (opts.path === '/query' || opts.path === '/v2/query') {
2368
+ * const result = await yourSqlGenerator.generate(opts.body, opts.tenantId);
2369
+ * return { data: result, headers: new Headers({ 'x-querypanel-session-id': result.sessionId }) };
2370
+ * }
2371
+ * if (opts.path === '/chart') return { data: await yourChartService.generate(opts.body) };
2372
+ * // ... other paths
2373
+ * return { data: await yourProxy(opts) };
2374
+ * },
2375
+ * { defaultTenantId: process.env.DEFAULT_TENANT_ID }
2376
+ * );
2377
+ * qp.attachPostgres('db', createClient, { tenantFieldName: 'tenant_id' });
2378
+ * const result = await qp.ask('Top 10 orders', { tenantId: 't1', database: 'db' });
2379
+ * ```
2380
+ */
2381
+ static withCallbacks(workspaceId, requestHandler, options) {
2382
+ const api = new CallbackApiClient(requestHandler, {
2383
+ defaultTenantId: options?.defaultTenantId
2384
+ });
2385
+ return new _QueryPanelSdkAPI("", "", workspaceId, { api });
2386
+ }
1879
2387
  // Database attachment methods
1880
2388
  attachClickhouse(name, clientFn, options) {
1881
2389
  const adapter = new ClickHouseAdapter(clientFn, options);
@@ -1903,6 +2411,19 @@ var QueryPanelSdkAPI = class {
1903
2411
  };
1904
2412
  this.queryEngine.attachDatabase(name, adapter, metadata);
1905
2413
  }
2414
+ attachBigQuery(name, clientFn, options) {
2415
+ const adapter = new BigQueryAdapter(clientFn, options);
2416
+ const metadata = {
2417
+ name,
2418
+ dialect: "bigquery",
2419
+ description: options?.description,
2420
+ tags: options?.tags,
2421
+ tenantFieldName: options?.tenantFieldName,
2422
+ tenantFieldType: options?.tenantFieldType ?? "String",
2423
+ enforceTenantIsolation: options?.tenantFieldName ? options?.enforceTenantIsolation ?? true : void 0
2424
+ };
2425
+ this.queryEngine.attachDatabase(name, adapter, metadata);
2426
+ }
1906
2427
  attachDatabase(name, adapter) {
1907
2428
  const metadata = {
1908
2429
  name,
@@ -1989,6 +2510,48 @@ var QueryPanelSdkAPI = class {
1989
2510
  signal
1990
2511
  );
1991
2512
  }
2513
+ // Embedded dashboard / JWT
2514
+ /**
2515
+ * Creates a JWT for the given tenant (and optional userId, scopes).
2516
+ * Use this when you need to pass a token to the embed (e.g. frontend or demo).
2517
+ * Only available when using the HTTP client (ApiClient); not available when using withCallbacks.
2518
+ */
2519
+ async createJwt(options) {
2520
+ const tenantId = options.tenantId?.trim();
2521
+ if (!tenantId) {
2522
+ throw new Error("tenantId is required");
2523
+ }
2524
+ if (!(this.client instanceof ApiClient)) {
2525
+ throw new Error(
2526
+ "createJwt is not available when using withCallbacks. Use the SDK with baseUrl + privateKey to create JWTs."
2527
+ );
2528
+ }
2529
+ return await this.client.createJwt(
2530
+ tenantId,
2531
+ options.userId,
2532
+ options.scopes
2533
+ );
2534
+ }
2535
+ /**
2536
+ * Runs SQL against an attached database with tenant isolation applied.
2537
+ * Used by embed run-sql (e.g. dashboard chart execution). Requires a database
2538
+ * to be attached (e.g. via attachPostgres) and options.tenantId.
2539
+ */
2540
+ async runSqlForDashboard(input, options) {
2541
+ const tenantId = options.tenantId?.trim();
2542
+ if (!tenantId) {
2543
+ throw new Error("tenantId is required");
2544
+ }
2545
+ const databaseName = input.database ?? "db";
2546
+ const params = input.params ?? {};
2547
+ const result = await this.queryEngine.validateAndExecute(
2548
+ input.sql,
2549
+ params,
2550
+ databaseName,
2551
+ tenantId
2552
+ );
2553
+ return { rows: result.rows, fields: result.fields };
2554
+ }
1992
2555
  // VizSpec generation
1993
2556
  /**
1994
2557
  * Generates a VizSpec visualization specification from query results.
@@ -2432,9 +2995,298 @@ var QueryPanelSdkAPI = class {
2432
2995
  async deleteActiveChart(id, options, signal) {
2433
2996
  await deleteActiveChart(this.client, id, options, signal);
2434
2997
  }
2998
+ // Dashboard CRUD operations
2999
+ /**
3000
+ * Creates a new dashboard with BlockNote content.
3001
+ *
3002
+ * @param body - Dashboard configuration including name and BlockNote content
3003
+ * @param options - Tenant, user, and scope options
3004
+ * @param signal - Optional AbortSignal for cancellation
3005
+ * @returns Created dashboard
3006
+ *
3007
+ * @example
3008
+ * ```typescript
3009
+ * const dashboard = await qp.createDashboard({
3010
+ * name: "Sales Dashboard",
3011
+ * description: "Monthly sales metrics",
3012
+ * content_json: blockNoteContent,
3013
+ * editor_type: "blocknote",
3014
+ * }, { tenantId: "tenant_123" });
3015
+ * ```
3016
+ */
3017
+ async createDashboard(body, options, signal) {
3018
+ return await createDashboard(
3019
+ this.client,
3020
+ body,
3021
+ options,
3022
+ signal
3023
+ );
3024
+ }
3025
+ /**
3026
+ * Lists dashboards with pagination and filtering.
3027
+ *
3028
+ * @param options - Filtering and pagination options
3029
+ * @param signal - Optional AbortSignal for cancellation
3030
+ * @returns Paginated list of dashboards
3031
+ *
3032
+ * @example
3033
+ * ```typescript
3034
+ * const dashboards = await qp.listDashboards({
3035
+ * tenantId: "tenant_123",
3036
+ * status: "deployed",
3037
+ * pagination: { page: 1, limit: 10 },
3038
+ * });
3039
+ * ```
3040
+ */
3041
+ async listDashboards(options, signal) {
3042
+ return await listDashboards(this.client, options, signal);
3043
+ }
3044
+ /**
3045
+ * Gets a dashboard by ID.
3046
+ *
3047
+ * @param id - Dashboard ID
3048
+ * @param options - Tenant, user, and scope options
3049
+ * @param signal - Optional AbortSignal for cancellation
3050
+ * @returns Dashboard details
3051
+ *
3052
+ * @example
3053
+ * ```typescript
3054
+ * const dashboard = await qp.getDashboard("dash_123", {
3055
+ * tenantId: "tenant_123",
3056
+ * });
3057
+ * ```
3058
+ */
3059
+ async getDashboard(id, options, signal) {
3060
+ return await getDashboard(
3061
+ this.client,
3062
+ id,
3063
+ options,
3064
+ signal
3065
+ );
3066
+ }
3067
+ /**
3068
+ * Gets a dashboard for a specific tenant.
3069
+ * Returns customer fork if exists, otherwise returns the original dashboard.
3070
+ *
3071
+ * @param id - Dashboard ID
3072
+ * @param tenantId - Tenant ID to check for fork
3073
+ * @param options - Additional auth options
3074
+ * @param signal - Optional AbortSignal for cancellation
3075
+ * @returns Dashboard (fork if exists, original otherwise)
3076
+ *
3077
+ * @example
3078
+ * ```typescript
3079
+ * const dashboard = await qp.getDashboardForTenant(
3080
+ * "dash_123",
3081
+ * "tenant_456",
3082
+ * );
3083
+ * ```
3084
+ */
3085
+ async getDashboardForTenant(id, tenantId, options, signal) {
3086
+ return await getDashboardForTenant(
3087
+ this.client,
3088
+ id,
3089
+ tenantId,
3090
+ options,
3091
+ signal
3092
+ );
3093
+ }
3094
+ /**
3095
+ * Updates a dashboard.
3096
+ *
3097
+ * @param id - Dashboard ID to update
3098
+ * @param body - Fields to update
3099
+ * @param options - Tenant, user, and scope options
3100
+ * @param signal - Optional AbortSignal for cancellation
3101
+ * @returns Updated dashboard
3102
+ *
3103
+ * @example
3104
+ * ```typescript
3105
+ * const updated = await qp.updateDashboard("dash_123", {
3106
+ * name: "Updated Dashboard",
3107
+ * content_json: newBlockNoteContent,
3108
+ * }, { tenantId: "tenant_123" });
3109
+ * ```
3110
+ */
3111
+ async updateDashboard(id, body, options, signal) {
3112
+ return await updateDashboard(
3113
+ this.client,
3114
+ id,
3115
+ body,
3116
+ options,
3117
+ signal
3118
+ );
3119
+ }
3120
+ /**
3121
+ * Updates dashboard status (deploy/undeploy).
3122
+ *
3123
+ * @param id - Dashboard ID
3124
+ * @param status - New status
3125
+ * @param options - Tenant, user, and scope options
3126
+ * @param signal - Optional AbortSignal for cancellation
3127
+ * @returns Updated dashboard
3128
+ *
3129
+ * @example
3130
+ * ```typescript
3131
+ * const deployed = await qp.updateDashboardStatus(
3132
+ * "dash_123",
3133
+ * "deployed",
3134
+ * { tenantId: "tenant_123" },
3135
+ * );
3136
+ * ```
3137
+ */
3138
+ async updateDashboardStatus(id, status, options, signal) {
3139
+ return await updateDashboardStatus(
3140
+ this.client,
3141
+ id,
3142
+ status,
3143
+ options,
3144
+ signal
3145
+ );
3146
+ }
3147
+ /**
3148
+ * Deletes a dashboard.
3149
+ *
3150
+ * @param id - Dashboard ID to delete
3151
+ * @param options - Tenant, user, and scope options
3152
+ * @param signal - Optional AbortSignal for cancellation
3153
+ *
3154
+ * @example
3155
+ * ```typescript
3156
+ * await qp.deleteDashboard("dash_123", { tenantId: "tenant_123" });
3157
+ * ```
3158
+ */
3159
+ async deleteDashboard(id, options, signal) {
3160
+ await deleteDashboard(this.client, id, options, signal);
3161
+ }
3162
+ // Dashboard Fork operations (Customer Customization)
3163
+ /**
3164
+ * Forks a dashboard for customer customization (copy-on-write).
3165
+ *
3166
+ * Creates a full copy of the dashboard that the customer can edit independently.
3167
+ * The original dashboard remains unchanged.
3168
+ *
3169
+ * @param id - Dashboard ID to fork
3170
+ * @param input - Fork configuration with tenant_id
3171
+ * @param options - Additional auth options
3172
+ * @param signal - Optional AbortSignal for cancellation
3173
+ * @returns Forked dashboard
3174
+ *
3175
+ * @example
3176
+ * ```typescript
3177
+ * const fork = await qp.forkDashboard("dash_123", {
3178
+ * tenant_id: "tenant_456",
3179
+ * name: "Customer's Custom Dashboard",
3180
+ * });
3181
+ * ```
3182
+ */
3183
+ async forkDashboard(id, input, options, signal) {
3184
+ return await forkDashboard(
3185
+ this.client,
3186
+ id,
3187
+ input,
3188
+ options,
3189
+ signal
3190
+ );
3191
+ }
3192
+ /**
3193
+ * Updates a customer fork.
3194
+ *
3195
+ * @param forkId - Fork ID to update
3196
+ * @param input - Update data including tenant_id
3197
+ * @param options - Additional auth options
3198
+ * @param signal - Optional AbortSignal for cancellation
3199
+ * @returns Updated fork
3200
+ *
3201
+ * @example
3202
+ * ```typescript
3203
+ * const updated = await qp.updateFork("fork_123", {
3204
+ * tenant_id: "tenant_456",
3205
+ * content_json: newBlockNoteContent,
3206
+ * });
3207
+ * ```
3208
+ */
3209
+ async updateFork(forkId, input, options, signal) {
3210
+ return await updateFork(
3211
+ this.client,
3212
+ forkId,
3213
+ input,
3214
+ options,
3215
+ signal
3216
+ );
3217
+ }
3218
+ /**
3219
+ * Rollbacks a fork to the original dashboard.
3220
+ * This deletes the fork and the customer will see the original again.
3221
+ *
3222
+ * @param forkId - Fork ID to rollback
3223
+ * @param tenantId - Tenant ID
3224
+ * @param options - Additional auth options
3225
+ * @param signal - Optional AbortSignal for cancellation
3226
+ * @returns Original dashboard
3227
+ *
3228
+ * @example
3229
+ * ```typescript
3230
+ * const original = await qp.rollbackFork("fork_123", "tenant_456");
3231
+ * ```
3232
+ */
3233
+ async rollbackFork(forkId, tenantId, options, signal) {
3234
+ return await rollbackFork(
3235
+ this.client,
3236
+ forkId,
3237
+ tenantId,
3238
+ options,
3239
+ signal
3240
+ );
3241
+ }
3242
+ /**
3243
+ * Deletes a customer fork.
3244
+ *
3245
+ * @param forkId - Fork ID to delete
3246
+ * @param tenantId - Tenant ID
3247
+ * @param options - Additional auth options
3248
+ * @param signal - Optional AbortSignal for cancellation
3249
+ *
3250
+ * @example
3251
+ * ```typescript
3252
+ * await qp.deleteFork("fork_123", "tenant_456");
3253
+ * ```
3254
+ */
3255
+ async deleteFork(forkId, tenantId, options, signal) {
3256
+ await deleteFork(
3257
+ this.client,
3258
+ forkId,
3259
+ tenantId,
3260
+ options,
3261
+ signal
3262
+ );
3263
+ }
3264
+ /**
3265
+ * Lists all customer forks for a tenant.
3266
+ *
3267
+ * @param tenantId - Tenant ID
3268
+ * @param options - Additional auth options
3269
+ * @param signal - Optional AbortSignal for cancellation
3270
+ * @returns Array of forks
3271
+ *
3272
+ * @example
3273
+ * ```typescript
3274
+ * const forks = await qp.listForksForTenant("tenant_456");
3275
+ * ```
3276
+ */
3277
+ async listForksForTenant(tenantId, options, signal) {
3278
+ return await listForksForTenant(
3279
+ this.client,
3280
+ tenantId,
3281
+ options,
3282
+ signal
3283
+ );
3284
+ }
2435
3285
  };
2436
3286
  // Annotate the CommonJS export names for ESM import in node:
2437
3287
  0 && (module.exports = {
3288
+ BigQueryAdapter,
3289
+ CallbackApiClient,
2438
3290
  ClickHouseAdapter,
2439
3291
  PostgresAdapter,
2440
3292
  QueryErrorCode,