@querypanel/node-sdk 1.0.57 → 1.0.59

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/README.md CHANGED
@@ -21,7 +21,7 @@ import { Pool } from "pg";
21
21
  const qp = new QueryPanelSdkAPI(
22
22
  process.env.QUERYPANEL_URL!,
23
23
  process.env.PRIVATE_KEY!,
24
- process.env.ORGANIZATION_ID!,
24
+ process.env.QUERYPANEL_WORKSPACE_ID!,
25
25
  {
26
26
  defaultTenantId: process.env.DEFAULT_TENANT_ID,
27
27
  },
@@ -80,6 +80,19 @@ console.table(response.rows);
80
80
  console.log(response.chart.vegaLiteSpec);
81
81
  ```
82
82
 
83
+ ### Custom system instructions (v2 pipeline)
84
+
85
+ If you need to enforce tenant-specific policies that should apply to every query (for example, data retention windows), you can inject additional **system prompt** instructions into the v2 pipeline:
86
+
87
+ ```ts
88
+ const response = await qp.ask("Revenue by product", {
89
+ tenantId: "tenant_123",
90
+ database: "analytics",
91
+ pipeline: "v2",
92
+ systemPrompt: "Retention policy: only query data from the last 30 days.",
93
+ });
94
+ ```
95
+
83
96
  ## Session History & Context-Aware Queries
84
97
 
85
98
  The SDK can link related questions into a session so follow-ups like “filter that to Europe” use prior context. The backend generates a QueryPanel session ID for every query and returns it in the response so you can reuse it.
@@ -338,7 +351,7 @@ import { QueryPanelSdkAPI } from "https://esm.sh/@querypanel/sdk";
338
351
  const qp = new QueryPanelSdkAPI(
339
352
  Deno.env.get("QUERYPANEL_URL")!,
340
353
  Deno.env.get("PRIVATE_KEY")!,
341
- Deno.env.get("ORGANIZATION_ID")!,
354
+ Deno.env.get("QUERYPANEL_WORKSPACE_ID")!,
342
355
  );
343
356
 
344
357
  // Use the SDK as normal - JWT signing works automatically
package/dist/index.cjs CHANGED
@@ -30,6 +30,7 @@ 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,
33
34
  CallbackApiClient: () => CallbackApiClient,
34
35
  ClickHouseAdapter: () => ClickHouseAdapter,
35
36
  PostgresAdapter: () => PostgresAdapter,
@@ -40,6 +41,186 @@ __export(index_exports, {
40
41
  });
41
42
  module.exports = __toCommonJS(index_exports);
42
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
+
43
224
  // src/utils/clickhouse.ts
44
225
  var WRAPPER_REGEX = /^(Nullable|LowCardinality|SimpleAggregateFunction)\((.+)\)$/i;
45
226
  function unwrapTypeModifiers(type) {
@@ -109,7 +290,7 @@ var ClickHouseAdapter = class {
109
290
  this.defaultFormat = options.defaultFormat ?? "JSONEachRow";
110
291
  this.kind = options.kind ?? "clickhouse";
111
292
  if (options.allowedTables) {
112
- this.allowedTables = normalizeTableFilter(options.allowedTables);
293
+ this.allowedTables = normalizeTableFilter2(options.allowedTables);
113
294
  }
114
295
  }
115
296
  databaseName;
@@ -147,7 +328,7 @@ var ClickHouseAdapter = class {
147
328
  * No indexes, constraints, or statistics
148
329
  */
149
330
  async introspect(options) {
150
- const tablesToIntrospect = options?.tables ? normalizeTableFilter(options.tables) : this.allowedTables;
331
+ const tablesToIntrospect = options?.tables ? normalizeTableFilter2(options.tables) : this.allowedTables;
151
332
  const allowTables = tablesToIntrospect ?? [];
152
333
  const hasFilter = allowTables.length > 0;
153
334
  const queryParams = {
@@ -187,7 +368,7 @@ var ClickHouseAdapter = class {
187
368
  const base = {
188
369
  name: table.name,
189
370
  schema: this.databaseName,
190
- type: asTableType(table.engine),
371
+ type: asTableType2(table.engine),
191
372
  columns: tableColumns
192
373
  };
193
374
  const comment = sanitize(table.comment);
@@ -265,7 +446,7 @@ function normalizePayload(payload) {
265
446
  }
266
447
  return [];
267
448
  }
268
- function normalizeTableFilter(tables) {
449
+ function normalizeTableFilter2(tables) {
269
450
  if (!tables?.length) return [];
270
451
  const seen = /* @__PURE__ */ new Set();
271
452
  const normalized = [];
@@ -293,7 +474,7 @@ function transformColumnRow(row) {
293
474
  if (comment !== void 0) column.comment = comment;
294
475
  return column;
295
476
  }
296
- function asTableType(engine) {
477
+ function asTableType2(engine) {
297
478
  if (typeof engine === "string") {
298
479
  const normalized = engine.toLowerCase();
299
480
  if (normalized.includes("view")) {
@@ -322,7 +503,7 @@ var PostgresAdapter = class {
322
503
  this.defaultSchema = options.defaultSchema ?? "public";
323
504
  this.kind = options.kind ?? "postgres";
324
505
  if (options.allowedTables) {
325
- this.allowedTables = normalizeTableFilter2(
506
+ this.allowedTables = normalizeTableFilter3(
326
507
  options.allowedTables,
327
508
  this.defaultSchema
328
509
  );
@@ -349,7 +530,7 @@ var PostgresAdapter = class {
349
530
  return;
350
531
  }
351
532
  const allowedSet = new Set(
352
- this.allowedTables.map((t) => tableKey(t.schema, t.table))
533
+ this.allowedTables.map((t) => tableKey2(t.schema, t.table))
353
534
  );
354
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*/)");
355
536
  const tablePattern = /(?:FROM|JOIN)\s+(?:ONLY\s+)?(?:([a-zA-Z_][a-zA-Z0-9_]*)\.)?(["']?[a-zA-Z_][a-zA-Z0-9_]*["']?)/gi;
@@ -358,7 +539,7 @@ var PostgresAdapter = class {
358
539
  const schema = match[1] ?? this.defaultSchema;
359
540
  const table = match[2]?.replace(/['"]/g, "");
360
541
  if (table) {
361
- const key = tableKey(schema, table);
542
+ const key = tableKey2(schema, table);
362
543
  if (!allowedSet.has(key)) {
363
544
  throw new Error(
364
545
  `Query references table "${schema}.${table}" which is not in the allowed tables list`
@@ -409,23 +590,23 @@ var PostgresAdapter = class {
409
590
  * No indexes, constraints, or statistics
410
591
  */
411
592
  async introspect(options) {
412
- const tablesToIntrospect = options?.tables ? normalizeTableFilter2(options.tables, this.defaultSchema) : this.allowedTables;
593
+ const tablesToIntrospect = options?.tables ? normalizeTableFilter3(options.tables, this.defaultSchema) : this.allowedTables;
413
594
  const normalizedTables = tablesToIntrospect ?? [];
414
595
  const tablesResult = await this.clientFn(
415
- buildTablesQuery(normalizedTables)
596
+ buildTablesQuery2(normalizedTables)
416
597
  );
417
598
  const tableRows = tablesResult.rows;
418
599
  const columnsResult = await this.clientFn(
419
- buildColumnsQuery(normalizedTables)
600
+ buildColumnsQuery2(normalizedTables)
420
601
  );
421
602
  const columnRows = columnsResult.rows;
422
603
  const tablesByKey = /* @__PURE__ */ new Map();
423
604
  for (const row of tableRows) {
424
- const key = tableKey(row.schema_name, row.table_name);
605
+ const key = tableKey2(row.schema_name, row.table_name);
425
606
  const table = {
426
607
  name: row.table_name,
427
608
  schema: row.schema_name,
428
- type: asTableType2(row.table_type),
609
+ type: asTableType3(row.table_type),
429
610
  columns: []
430
611
  };
431
612
  const comment = sanitize2(row.comment);
@@ -435,7 +616,7 @@ var PostgresAdapter = class {
435
616
  tablesByKey.set(key, table);
436
617
  }
437
618
  for (const row of columnRows) {
438
- const key = tableKey(row.table_schema, row.table_name);
619
+ const key = tableKey2(row.table_schema, row.table_name);
439
620
  const table = tablesByKey.get(key);
440
621
  if (!table) continue;
441
622
  const column = {
@@ -465,7 +646,7 @@ var PostgresAdapter = class {
465
646
  };
466
647
  }
467
648
  };
468
- function normalizeTableFilter2(tables, defaultSchema) {
649
+ function normalizeTableFilter3(tables, defaultSchema) {
469
650
  if (!tables?.length) return [];
470
651
  const normalized = [];
471
652
  const seen = /* @__PURE__ */ new Set();
@@ -479,14 +660,14 @@ function normalizeTableFilter2(tables, defaultSchema) {
479
660
  if (!isSafeIdentifier(schema) || !isSafeIdentifier(table)) {
480
661
  continue;
481
662
  }
482
- const key = tableKey(schema, table);
663
+ const key = tableKey2(schema, table);
483
664
  if (seen.has(key)) continue;
484
665
  seen.add(key);
485
666
  normalized.push({ schema, table });
486
667
  }
487
668
  return normalized;
488
669
  }
489
- function buildTablesQuery(tables) {
670
+ function buildTablesQuery2(tables) {
490
671
  const filter = buildFilterClause(tables, "n.nspname", "c.relname");
491
672
  return `SELECT
492
673
  c.relname AS table_name,
@@ -505,7 +686,7 @@ function buildTablesQuery(tables) {
505
686
  ${filter}
506
687
  ORDER BY n.nspname, c.relname;`;
507
688
  }
508
- function buildColumnsQuery(tables) {
689
+ function buildColumnsQuery2(tables) {
509
690
  const filter = buildFilterClause(
510
691
  tables,
511
692
  "cols.table_schema",
@@ -550,13 +731,13 @@ function buildFilterClause(tables, schemaExpr, tableExpr) {
550
731
  });
551
732
  return `AND (${clauses.join(" OR ")})`;
552
733
  }
553
- function tableKey(schema, table) {
734
+ function tableKey2(schema, table) {
554
735
  return `${schema}.${table}`;
555
736
  }
556
737
  function isSafeIdentifier(value) {
557
738
  return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
558
739
  }
559
- function asTableType2(value) {
740
+ function asTableType3(value) {
560
741
  const normalized = value.toLowerCase();
561
742
  if (normalized.includes("view")) {
562
743
  return normalized.includes("materialized") ? "materialized_view" : "view";
@@ -1024,6 +1205,9 @@ var QueryEngine = class {
1024
1205
  const paramKey = tenantField;
1025
1206
  params[paramKey] = tenantId;
1026
1207
  tenantPredicate = `${tenantField} = {${tenantField}:${metadata.tenantFieldType ?? "String"}}`;
1208
+ } else if (metadata.dialect === "bigquery") {
1209
+ params[tenantField] = tenantId;
1210
+ tenantPredicate = `${tenantField} = @${tenantField}`;
1027
1211
  } else {
1028
1212
  const escapedId = tenantId.replace(/'/g, "''");
1029
1213
  tenantPredicate = `${tenantField} = '${escapedId}'`;
@@ -1580,7 +1764,9 @@ async function ask(client, queryEngine, question, options, signal) {
1580
1764
  ...options.maxRetry ? { max_retry: options.maxRetry } : {},
1581
1765
  ...tenantSettings ? { tenant_settings: tenantSettings } : {},
1582
1766
  ...databaseName ? { database: databaseName } : {},
1583
- ...metadata?.dialect ? { dialect: metadata.dialect } : {}
1767
+ ...metadata?.dialect ? { dialect: metadata.dialect } : {},
1768
+ ...options.model?.trim() ? { model: options.model.trim() } : {},
1769
+ ...options.pipeline === "v2" && options.systemPrompt?.trim() ? { system_prompt: options.systemPrompt.trim() } : {}
1584
1770
  },
1585
1771
  tenantId,
1586
1772
  options.userId,
@@ -1604,7 +1790,7 @@ async function ask(client, queryEngine, question, options, signal) {
1604
1790
  const dbName = queryResponse.data.database ?? options.database ?? queryEngine.getDefaultDatabase();
1605
1791
  if (!dbName) {
1606
1792
  throw new Error(
1607
- "No database attached. Call attachPostgres/attachClickhouse first."
1793
+ "No database attached. Call attachPostgres, attachClickhouse, or attachBigQuery first."
1608
1794
  );
1609
1795
  }
1610
1796
  const paramMetadata = Array.isArray(queryResponse.data.params) ? queryResponse.data.params : [];
@@ -2145,20 +2331,22 @@ function resolveTenantId8(client, tenantId) {
2145
2331
  var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
2146
2332
  client;
2147
2333
  queryEngine;
2148
- constructor(baseUrl, privateKey, organizationId, options) {
2334
+ /**
2335
+ * @param workspaceId - Workspace UUID (same value as your org in the dashboard). Passed to the API as JWT claim `organizationId`.
2336
+ */
2337
+ constructor(baseUrl, privateKey, workspaceId, options) {
2149
2338
  if (options?.api) {
2150
2339
  this.client = options.api;
2151
2340
  } else {
2152
2341
  if (!baseUrl) throw new Error("Base URL is required");
2153
2342
  if (!privateKey) throw new Error("Private key is required");
2154
- if (!organizationId) throw new Error("Organization ID is required");
2155
- this.client = new ApiClient(baseUrl, privateKey, organizationId, {
2343
+ if (!workspaceId) throw new Error("Workspace ID is required");
2344
+ this.client = new ApiClient(baseUrl, privateKey, workspaceId, {
2156
2345
  defaultTenantId: options?.defaultTenantId,
2157
2346
  additionalHeaders: options?.additionalHeaders,
2158
2347
  fetch: options?.fetch
2159
2348
  });
2160
2349
  }
2161
- if (!organizationId) throw new Error("Organization ID is required");
2162
2350
  this.queryEngine = new QueryEngine();
2163
2351
  }
2164
2352
  /**
@@ -2167,7 +2355,7 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
2167
2355
  * are handled in-process (e.g. by invoking querypanel-sdk services directly)
2168
2356
  * and do not cause recursion.
2169
2357
  *
2170
- * @param organizationId - Organization identifier
2358
+ * @param workspaceId - Workspace identifier (sent as `organizationId` in JWTs and API auth)
2171
2359
  * @param requestHandler - Callback invoked for each logical API request (method, path, body, tenantId, etc.)
2172
2360
  * @param options - Optional defaultTenantId
2173
2361
  * @returns QueryPanelSdkAPI instance; attach databases and call ask(), syncSchema(), etc. as usual
@@ -2175,7 +2363,7 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
2175
2363
  * @example
2176
2364
  * ```ts
2177
2365
  * const qp = QueryPanelSdkAPI.withCallbacks(
2178
- * process.env.ORGANIZATION_ID!,
2366
+ * process.env.QUERYPANEL_WORKSPACE_ID!,
2179
2367
  * async (opts) => {
2180
2368
  * // Call your in-process services (e.g. querypanel-sdk logic) instead of HTTP
2181
2369
  * if (opts.path === '/query' || opts.path === '/v2/query') {
@@ -2192,11 +2380,11 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
2192
2380
  * const result = await qp.ask('Top 10 orders', { tenantId: 't1', database: 'db' });
2193
2381
  * ```
2194
2382
  */
2195
- static withCallbacks(organizationId, requestHandler, options) {
2383
+ static withCallbacks(workspaceId, requestHandler, options) {
2196
2384
  const api = new CallbackApiClient(requestHandler, {
2197
2385
  defaultTenantId: options?.defaultTenantId
2198
2386
  });
2199
- return new _QueryPanelSdkAPI("", "", organizationId, { api });
2387
+ return new _QueryPanelSdkAPI("", "", workspaceId, { api });
2200
2388
  }
2201
2389
  // Database attachment methods
2202
2390
  attachClickhouse(name, clientFn, options) {
@@ -2225,6 +2413,19 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
2225
2413
  };
2226
2414
  this.queryEngine.attachDatabase(name, adapter, metadata);
2227
2415
  }
2416
+ attachBigQuery(name, clientFn, options) {
2417
+ const adapter = new BigQueryAdapter(clientFn, options);
2418
+ const metadata = {
2419
+ name,
2420
+ dialect: "bigquery",
2421
+ description: options?.description,
2422
+ tags: options?.tags,
2423
+ tenantFieldName: options?.tenantFieldName,
2424
+ tenantFieldType: options?.tenantFieldType ?? "String",
2425
+ enforceTenantIsolation: options?.tenantFieldName ? options?.enforceTenantIsolation ?? true : void 0
2426
+ };
2427
+ this.queryEngine.attachDatabase(name, adapter, metadata);
2428
+ }
2228
2429
  attachDatabase(name, adapter) {
2229
2430
  const metadata = {
2230
2431
  name,
@@ -3086,6 +3287,7 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
3086
3287
  };
3087
3288
  // Annotate the CommonJS export names for ESM import in node:
3088
3289
  0 && (module.exports = {
3290
+ BigQueryAdapter,
3089
3291
  CallbackApiClient,
3090
3292
  ClickHouseAdapter,
3091
3293
  PostgresAdapter,