@querypanel/node-sdk 1.3.6 → 1.3.8
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 +14 -0
- package/dist/index.cjs +21 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -7
- package/dist/index.d.ts +32 -7
- package/dist/index.js +21 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/adapters/types.ts","../src/adapters/bigquery.ts","../src/utils/clickhouse.ts","../src/adapters/clickhouse.ts","../src/adapters/postgres.ts","../src/core/client.ts","../src/errors.ts","../src/core/callback-client.ts","../src/core/map-generated-params.ts","../src/core/query-engine.ts","../src/routes/charts.ts","../src/routes/active-charts.ts","../src/routes/dashboards.ts","../src/routes/ingest.ts","../src/routes/modify.ts","../src/routes/query.ts","../src/routes/queries.ts","../src/routes/sessions.ts","../src/routes/vizspec.ts","../src/types/vizspec.ts"],"sourcesContent":["import {\n\tBigQueryAdapter,\n\ttype BigQueryAdapterOptions,\n\ttype BigQueryClientFn,\n\ttype BigQueryQueryRequest,\n\ttype BigQueryQueryResult,\n} from \"./adapters/bigquery\";\nimport {\n\tClickHouseAdapter,\n\ttype ClickHouseAdapterOptions,\n\ttype ClickHouseClientFn,\n} from \"./adapters/clickhouse\";\nimport {\n\tPostgresAdapter,\n\ttype PostgresAdapterOptions,\n\ttype PostgresClientFn,\n\ttype PostgresWireDialect,\n} from \"./adapters/postgres\";\nimport type {\n\tCatalogSchema,\n\tCatalogTable,\n\tCatalogTableType,\n\tDatabaseAdapter,\n\tDatabaseDialect,\n\tListTablesOptions,\n} from \"./adapters/types\";\nimport type { IQueryPanelApi, RequestHandler } from \"./core/api-types\";\nimport { ApiClient } from \"./core/client\";\nimport { CallbackApiClient } from \"./core/callback-client\";\nimport {\n\ttype DatabaseMetadata,\n\ttype ParamRecord,\n\tQueryEngine,\n} from \"./core/query-engine\";\nimport {\n\tQueryErrorCode,\n\tQueryPipelineError,\n\tSchemaTooLargeError,\n} from \"./errors\";\nimport * as activeChartsRoute from \"./routes/active-charts\";\nimport * as chartsRoute from \"./routes/charts\";\nimport * as dashboardsRoute from \"./routes/dashboards\";\nimport * as ingestRoute from \"./routes/ingest\";\nimport * as modifyRoute from \"./routes/modify\";\nimport * as queriesRoute from \"./routes/queries\";\nimport * as queryRoute from \"./routes/query\";\nimport * as sessionsRoute from \"./routes/sessions\";\nimport * as vizspecRoute from \"./routes/vizspec\";\nimport type { SchemaIntrospection } from \"./schema/types\";\nimport type { ChartType } from \"./types/vizspec\";\n\n// Re-export all public types\nexport { BigQueryAdapter, ClickHouseAdapter, PostgresAdapter };\n\n// Re-export error types\nexport type { QueryErrorCode as QueryErrorCodeType } from \"./errors\";\nexport { QueryErrorCode, QueryPipelineError, SchemaTooLargeError };\n\nexport type {\n\tBigQueryAdapterOptions,\n\tBigQueryClientFn,\n\tBigQueryQueryRequest,\n\tBigQueryQueryResult,\n\tCatalogSchema,\n\tCatalogTable,\n\tCatalogTableType,\n\tClickHouseAdapterOptions,\n\tClickHouseClientFn,\n\tDatabaseAdapter,\n\tDatabaseDialect,\n\tListTablesOptions,\n\tPostgresAdapterOptions,\n\tPostgresClientFn,\n\tPostgresWireDialect,\n\tSchemaIntrospection,\n};\n\n// Re-export callback API types for in-process usage\nexport type {\n\tIQueryPanelApi,\n\tRequestHandler,\n\tRequestHandlerOptions,\n\tRequestHandlerResult,\n} from \"./core/api-types\";\nexport { CallbackApiClient } from \"./core/callback-client\";\n\n// Re-export from query-engine\nexport type { ParamRecord, ParamValue } from \"./core/query-engine\";\nexport {\n\tmapGeneratedParams,\n\tnormalizeSqlParamsInput,\n} from \"./core/map-generated-params\";\nexport type {\n\tActiveChartBulkGetResponse,\n\tActiveChartCreateInput,\n\tActiveChartListOptions,\n\tActiveChartUpdateInput,\n\tSdkActiveChart,\n} from \"./routes/active-charts\";\n\nexport type {\n\tChartBulkGetResponse,\n\tChartCreateInput,\n\tChartListAllOptions,\n\tChartListOptions,\n\tChartUpdateInput,\n\tPaginatedResponse,\n\tPaginationInfo,\n\tPaginationQuery,\n\tSdkChart,\n} from \"./routes/charts\";\n\nexport type {\n\tDashboardCreateInput,\n\tDashboardForkInput,\n\tDashboardForkUpdateInput,\n\tDashboardListOptions,\n\tDashboardUpdateInput,\n\tSdkDashboard,\n} from \"./routes/dashboards\";\n// Re-export route types\nexport type {\n\tIngestResponse,\n\tSchemaIngestBatchEmbedResponse,\n\tSchemaIngestRequestOptions,\n\tSchemaSyncOptions,\n\tSchemaSyncProgressEvent,\n\tStageSchemaIngestOptions,\n\tStageSchemaIngestResult,\n\tSyncSchemaInBatchesOptions,\n} from \"./routes/ingest\";\nexport type {\n\tAxisFieldInput,\n\tChartModifyInput,\n\tChartModifyOptions,\n\tChartModifyResponse,\n\tDateRangeInput,\n\tFieldRefInput,\n\tSqlModifications,\n\tVizModifications,\n} from \"./routes/modify\";\nexport type {\n\tAskOptions,\n\tAskResponse,\n\tChartEnvelope,\n\tContextDocument,\n\tIntentResult,\n\tPipelineTrace,\n} from \"./routes/query\";\n// Re-export anonymizeResults utility\nexport { anonymizeResults, resolveQueryPipeline } from \"./routes/query\";\nexport type {\n\tSdkSession,\n\tSdkSessionTurn,\n\tSessionGetOptions,\n\tSessionListOptions,\n\tSessionUpdateInput,\n} from \"./routes/sessions\";\nexport type {\n\tVizSpecGenerateInput,\n\tVizSpecGenerateOptions,\n\tVizSpecResponse,\n} from \"./routes/vizspec\";\n// Re-export VizSpec types\nexport {\n\tALL_VIZ_CHART_TYPES,\n} from \"./types/vizspec\";\nexport type {\n\tAggregateOp,\n\tAxisField,\n\tChartEncoding,\n\tChartSpec,\n\tChartType,\n\tEncodingHints,\n\tFieldRef,\n\tFieldType,\n\tMetricEncoding,\n\tMetricField,\n\tMetricSpec,\n\tStackingMode,\n\tTableColumn,\n\tTableEncoding,\n\tTableSpec,\n\tTimeUnit,\n\tValueFormat,\n\tVizSpec,\n\tVizSpecGeneratorInput,\n\tVizSpecKind,\n\tVizSpecResult,\n} from \"./types/vizspec\";\n\n/** Options when constructing the SDK with HTTP (default) or with a custom API implementation. */\nexport interface QueryPanelSdkAPIOptions {\n\tdefaultTenantId?: string;\n\tadditionalHeaders?: Record<string, string>;\n\tfetch?: typeof fetch;\n\t/**\n\t * Use a custom API implementation (e.g. CallbackApiClient) instead of HTTP.\n\t * When set, baseUrl and privateKey are ignored and no requests are sent to querypanel-sdk;\n\t * use this when calling the SDK from within your own API to avoid recursion.\n\t */\n\tapi?: IQueryPanelApi;\n\t/**\n\t * Restrict VizSpec chart kinds (when using `chartType: \"vizspec\"`).\n\t * Omitted means all types: line, bar, column, area, scatter, pie.\n\t * Per-request `supportedChartTypes` on `ask` / `modifyChart` overrides this default.\n\t */\n\tsupportedChartTypes?: ChartType[];\n}\n\nfunction resolveAttachEnforceTenantIsolation(\n\ttenantFieldName: string | undefined,\n\tenforceTenantIsolation: boolean | undefined,\n): boolean | undefined {\n\tif (enforceTenantIsolation !== undefined) {\n\t\treturn enforceTenantIsolation;\n\t}\n\treturn tenantFieldName ? true : undefined;\n}\n\n/**\n * Main SDK class - Thin orchestrator\n * Delegates to deep modules (ApiClient or custom API, QueryEngine, route modules)\n * Following Ousterhout's principle: \"Simple interface hiding complexity\"\n */\nexport class QueryPanelSdkAPI {\n\tprivate readonly client: IQueryPanelApi;\n\tprivate readonly queryEngine: QueryEngine;\n\tprivate readonly supportedChartTypes?: ChartType[];\n\n\t/**\n\t * @param workspaceId - Workspace UUID (same value as your org in the dashboard). Passed to the API as JWT claim `organizationId`.\n\t */\n\tconstructor(\n\t\tbaseUrl: string,\n\t\tprivateKey: string,\n\t\tworkspaceId: string,\n\t\toptions?: QueryPanelSdkAPIOptions,\n\t) {\n\t\tif (options?.api) {\n\t\t\tthis.client = options.api;\n\t\t} else {\n\t\t\tif (!baseUrl) throw new Error(\"Base URL is required\");\n\t\t\tif (!privateKey) throw new Error(\"Private key is required\");\n\t\t\tif (!workspaceId) throw new Error(\"Workspace ID is required\");\n\t\t\tthis.client = new ApiClient(baseUrl, privateKey, workspaceId, {\n\t\t\t\tdefaultTenantId: options?.defaultTenantId,\n\t\t\t\tadditionalHeaders: options?.additionalHeaders,\n\t\t\t\tfetch: options?.fetch,\n\t\t\t});\n\t\t}\n\t\tthis.queryEngine = new QueryEngine();\n\t\tthis.supportedChartTypes = options?.supportedChartTypes;\n\t}\n\n\t/**\n\t * Create an SDK instance that uses a request callback instead of HTTP.\n\t * Use this when calling the SDK from within your own API so that \"API\" calls\n\t * are handled in-process (e.g. by invoking querypanel-sdk services directly)\n\t * and do not cause recursion.\n\t *\n\t * @param workspaceId - Workspace identifier (sent as `organizationId` in JWTs and API auth)\n\t * @param requestHandler - Callback invoked for each logical API request (method, path, body, tenantId, etc.)\n\t * @param options - Optional defaultTenantId\n\t * @returns QueryPanelSdkAPI instance; attach databases and call ask(), syncSchema(), etc. as usual\n\t *\n\t * @example\n\t * ```ts\n\t * const qp = QueryPanelSdkAPI.withCallbacks(\n\t * process.env.QUERYPANEL_WORKSPACE_ID!,\n\t * async (opts) => {\n\t * // Call your in-process services (e.g. querypanel-sdk logic) instead of HTTP\n\t * if (opts.path === '/query' || opts.path === '/v2/query') {\n\t * const result = await yourSqlGenerator.generate(opts.body, opts.tenantId);\n\t * return { data: result, headers: new Headers({ 'x-querypanel-session-id': result.sessionId }) };\n\t * }\n\t * if (opts.path === '/chart') return { data: await yourChartService.generate(opts.body) };\n\t * // ... other paths\n\t * return { data: await yourProxy(opts) };\n\t * },\n\t * { defaultTenantId: process.env.DEFAULT_TENANT_ID }\n\t * );\n\t * qp.attachPostgres('db', createClient, { tenantFieldName: 'tenant_id' });\n\t * const result = await qp.ask('Top 10 orders', { tenantId: 't1', database: 'db' });\n\t * ```\n\t */\n\tstatic withCallbacks(\n\t\tworkspaceId: string,\n\t\trequestHandler: RequestHandler,\n\t\toptions?: { defaultTenantId?: string; supportedChartTypes?: ChartType[] },\n\t): QueryPanelSdkAPI {\n\t\tconst api = new CallbackApiClient(requestHandler, {\n\t\t\tdefaultTenantId: options?.defaultTenantId,\n\t\t});\n\t\treturn new QueryPanelSdkAPI(\"\", \"\", workspaceId, {\n\t\t\tapi,\n\t\t\tsupportedChartTypes: options?.supportedChartTypes,\n\t\t});\n\t}\n\n\t// Database attachment methods\n\n\tattachClickhouse(\n\t\tname: string,\n\t\tclientFn: ClickHouseClientFn,\n\t\toptions?: ClickHouseAdapterOptions & {\n\t\t\tdescription?: string;\n\t\t\ttags?: string[];\n\t\t\ttenantFieldName?: string;\n\t\t\ttenantFieldType?: string;\n\t\t\tenforceTenantIsolation?: boolean;\n\t\t},\n\t): void {\n\t\tconst adapter = new ClickHouseAdapter(clientFn, options);\n\n\t\tconst metadata: DatabaseMetadata = {\n\t\t\tname,\n\t\t\tdialect: \"clickhouse\",\n\t\t\tdescription: options?.description,\n\t\t\ttags: options?.tags,\n\t\t\ttenantFieldName: options?.tenantFieldName,\n\t\t\ttenantFieldType: options?.tenantFieldType ?? \"String\",\n\t\t\tenforceTenantIsolation: resolveAttachEnforceTenantIsolation(\n\t\t\t\toptions?.tenantFieldName,\n\t\t\t\toptions?.enforceTenantIsolation,\n\t\t\t),\n\t\t};\n\n\t\tthis.queryEngine.attachDatabase(name, adapter, metadata);\n\t}\n\n\tattachPostgres(\n\t\tname: string,\n\t\tclientFn: PostgresClientFn,\n\t\toptions?: PostgresAdapterOptions & {\n\t\t\tdescription?: string;\n\t\t\ttags?: string[];\n\t\t\ttenantFieldName?: string;\n\t\t\ttenantFieldType?: string;\n\t\t\tenforceTenantIsolation?: boolean;\n\t\t},\n\t): void {\n\t\tconst adapter = new PostgresAdapter(clientFn, options);\n\n\t\tconst metadata: DatabaseMetadata = {\n\t\t\tname,\n\t\t\tdialect: adapter.getDialect(),\n\t\t\tdescription: options?.description,\n\t\t\ttags: options?.tags,\n\t\t\ttenantFieldName: options?.tenantFieldName,\n\t\t\ttenantFieldType: options?.tenantFieldType ?? \"String\",\n\t\t\tenforceTenantIsolation: resolveAttachEnforceTenantIsolation(\n\t\t\t\toptions?.tenantFieldName,\n\t\t\t\toptions?.enforceTenantIsolation,\n\t\t\t),\n\t\t};\n\n\t\tthis.queryEngine.attachDatabase(name, adapter, metadata);\n\t}\n\n\t/**\n\t * Attach an Amazon Redshift database. Redshift speaks the Postgres wire\n\t * protocol, so `clientFn` can wrap a `pg` client; introspection uses\n\t * Redshift's svv_* catalog views and SQL is generated for Redshift.\n\t */\n\tattachRedshift(\n\t\tname: string,\n\t\tclientFn: PostgresClientFn,\n\t\toptions?: Omit<PostgresAdapterOptions, \"dialect\"> & {\n\t\t\tdescription?: string;\n\t\t\ttags?: string[];\n\t\t\ttenantFieldName?: string;\n\t\t\ttenantFieldType?: string;\n\t\t\tenforceTenantIsolation?: boolean;\n\t\t},\n\t): void {\n\t\tthis.attachPostgres(name, clientFn, { ...options, dialect: \"redshift\" });\n\t}\n\n\tattachBigQuery(\n\t\tname: string,\n\t\tclientFn: BigQueryClientFn,\n\t\toptions: BigQueryAdapterOptions & {\n\t\t\tdescription?: string;\n\t\t\ttags?: string[];\n\t\t\ttenantFieldName?: string;\n\t\t\ttenantFieldType?: string;\n\t\t\tenforceTenantIsolation?: boolean;\n\t\t},\n\t): void {\n\t\tconst adapter = new BigQueryAdapter(clientFn, options);\n\n\t\tconst metadata: DatabaseMetadata = {\n\t\t\tname,\n\t\t\tdialect: \"bigquery\",\n\t\t\tdescription: options?.description,\n\t\t\ttags: options?.tags,\n\t\t\ttenantFieldName: options?.tenantFieldName,\n\t\t\ttenantFieldType: options?.tenantFieldType ?? \"String\",\n\t\t\tenforceTenantIsolation: resolveAttachEnforceTenantIsolation(\n\t\t\t\toptions?.tenantFieldName,\n\t\t\t\toptions?.enforceTenantIsolation,\n\t\t\t),\n\t\t};\n\n\t\tthis.queryEngine.attachDatabase(name, adapter, metadata);\n\t}\n\n\tattachDatabase(name: string, adapter: DatabaseAdapter): void {\n\t\tconst metadata: DatabaseMetadata = {\n\t\t\tname,\n\t\t\tdialect: adapter.getDialect(),\n\t\t};\n\t\tthis.queryEngine.attachDatabase(name, adapter, metadata);\n\t}\n\n\t// Schema introspection and sync\n\n\tasync introspect(\n\t\tdatabaseName: string,\n\t\ttables?: string[],\n\t): Promise<SchemaIntrospection> {\n\t\tconst adapter = this.queryEngine.getDatabase(databaseName);\n\t\treturn await adapter.introspect(tables ? { tables } : undefined);\n\t}\n\n\t/**\n\t * Lists schemas (ClickHouse: the database, BigQuery: the dataset) with table\n\t * counts. Cheap catalog read for building an allowed-tables picker; not\n\t * restricted by `allowedTables`.\n\t */\n\tasync listSchemas(databaseName: string): Promise<CatalogSchema[]> {\n\t\tconst adapter = this.queryEngine.getDatabase(databaseName);\n\t\tif (!adapter.listSchemas) {\n\t\t\tthrow new Error(\n\t\t\t\t`Database \"${databaseName}\" (${adapter.getDialect()}) does not support listSchemas`,\n\t\t\t);\n\t\t}\n\t\treturn await adapter.listSchemas();\n\t}\n\n\t/**\n\t * Lists tables/views (no columns) filtered by schema and a case-insensitive\n\t * name substring. Default limit 500, max 5000. Not restricted by `allowedTables`.\n\t */\n\tasync listTables(\n\t\tdatabaseName: string,\n\t\toptions?: ListTablesOptions,\n\t): Promise<CatalogTable[]> {\n\t\tconst adapter = this.queryEngine.getDatabase(databaseName);\n\t\tif (!adapter.listTables) {\n\t\t\tthrow new Error(\n\t\t\t\t`Database \"${databaseName}\" (${adapter.getDialect()}) does not support listTables`,\n\t\t\t);\n\t\t}\n\t\treturn await adapter.listTables(options);\n\t}\n\n\t/**\n\t * Syncs the database schema to QueryPanel for natural language query generation.\n\t *\n\t * This method introspects your database schema and uploads it to QueryPanel's\n\t * vector store. The schema is used by the LLM to generate accurate SQL queries.\n\t * Schema embedding is skipped if no changes are detected (drift detection).\n\t *\n\t * @param databaseName - Name of the attached database to sync\n\t * @param options - Sync options including tenantId and forceReindex\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Response with sync status and chunk counts\n\t *\n\t * @example\n\t * ```typescript\n\t * // Basic schema sync (skips if no changes)\n\t * await qp.syncSchema(\"analytics\", { tenantId: \"tenant_123\" });\n\t *\n\t * // Force re-embedding even if schema hasn't changed\n\t * await qp.syncSchema(\"analytics\", {\n\t * tenantId: \"tenant_123\",\n\t * forceReindex: true,\n\t * });\n\t * ```\n\t */\n\tasync syncSchema(\n\t\tdatabaseName: string,\n\t\toptions: ingestRoute.SchemaSyncOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<ingestRoute.IngestResponse> {\n\t\treturn await ingestRoute.syncSchema(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tdatabaseName,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Introspects the schema, uploads it in batches to a new ingest session and\n\t * commits it. Returns `status: \"skipped\"` (with `skippedResult`) when the schema\n\t * is unchanged, otherwise `status: \"embedding\"`: call embedSchemaIngestBatch for\n\t * every index in [0, totalBatches) and then finalizeSchemaIngest.\n\t * Throws SchemaTooLargeError before uploading if `maxColumns` is exceeded.\n\t */\n\tasync stageSchemaIngest(\n\t\tdatabaseName: string,\n\t\toptions: ingestRoute.StageSchemaIngestOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<ingestRoute.StageSchemaIngestResult> {\n\t\treturn await ingestRoute.stageSchemaIngest(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tdatabaseName,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/** Embeds one batch of a staged ingest session. */\n\tasync embedSchemaIngestBatch(\n\t\tsessionId: string,\n\t\tindex: number,\n\t\toptions: ingestRoute.SchemaIngestRequestOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<ingestRoute.SchemaIngestBatchEmbedResponse> {\n\t\treturn await ingestRoute.embedSchemaIngestBatch(\n\t\t\tthis.client,\n\t\t\tsessionId,\n\t\t\tindex,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/** Finalizes a staged ingest session after all batches are embedded. */\n\tasync finalizeSchemaIngest(\n\t\tsessionId: string,\n\t\toptions: ingestRoute.SchemaIngestRequestOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<ingestRoute.IngestResponse> {\n\t\treturn await ingestRoute.finalizeSchemaIngest(\n\t\t\tthis.client,\n\t\t\tsessionId,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Batched alternative to syncSchema for large databases: stage, embed every\n\t * batch, and finalize in-process, reporting progress via `onProgress`.\n\t *\n\t * @example\n\t * ```typescript\n\t * await qp.syncSchemaInBatches(\"warehouse\", {\n\t * tenantId: \"tenant_123\",\n\t * onProgress: (e) => console.log(e.phase, e),\n\t * });\n\t * ```\n\t */\n\tasync syncSchemaInBatches(\n\t\tdatabaseName: string,\n\t\toptions: ingestRoute.SyncSchemaInBatchesOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<ingestRoute.IngestResponse> {\n\t\treturn await ingestRoute.syncSchemaInBatches(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tdatabaseName,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t// Natural language query\n\n\t/**\n\t * Generates SQL from a natural language question and executes it.\n\t *\n\t * This is the primary method for converting user questions into data.\n\t * It handles the complete flow: SQL generation → validation → execution → chart generation.\n\t *\n\t * @param question - Natural language question (e.g., \"Show revenue by country\")\n\t * @param options - Query options including tenantId, database, retry settings, and optional `debug` for engineer-style rationale\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Response with SQL, executed data rows, and generated chart\n\t * @throws {Error} When SQL generation or execution fails after all retries\n\t *\n\t * @example\n\t * ```typescript\n\t * // Basic query\n\t * const result = await qp.ask(\"Top 10 customers by revenue\", {\n\t * tenantId: \"tenant_123\",\n\t * database: \"analytics\",\n\t * });\n\t * console.log(result.sql); // Generated SQL\n\t * console.log(result.rows); // Query results\n\t * console.log(result.chart); // Vega-Lite chart spec\n\t * console.log(result.querypanelSessionId); // Use for follow-ups\n\t *\n\t * // With automatic SQL repair on failure\n\t * const result = await qp.ask(\"Show monthly trends\", {\n\t * tenantId: \"tenant_123\",\n\t * maxRetry: 3, // Retry up to 3 times if SQL fails\n\t * });\n\t * ```\n\t */\n\tasync ask(\n\t\tquestion: string,\n\t\toptions: queryRoute.AskOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<queryRoute.AskResponse> {\n\t\treturn await queryRoute.ask(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tquestion,\n\t\t\t{\n\t\t\t\t...options,\n\t\t\t\tsupportedChartTypes:\n\t\t\t\t\toptions.supportedChartTypes ?? this.supportedChartTypes,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t// Embedded dashboard / JWT\n\n\t/**\n\t * Creates a JWT for the given tenant (and optional userId, scopes).\n\t * Use this when you need to pass a token to the embed (e.g. frontend or demo).\n\t * Only available when using the HTTP client (ApiClient); not available when using withCallbacks.\n\t */\n\tasync createJwt(options: {\n\t\ttenantId: string;\n\t\tuserId?: string;\n\t\tscopes?: string[];\n\t}): Promise<string> {\n\t\tconst tenantId = options.tenantId?.trim();\n\t\tif (!tenantId) {\n\t\t\tthrow new Error(\"tenantId is required\");\n\t\t}\n\t\tif (!(this.client instanceof ApiClient)) {\n\t\t\tthrow new Error(\n\t\t\t\t\"createJwt is not available when using withCallbacks. Use the SDK with baseUrl + privateKey to create JWTs.\",\n\t\t\t);\n\t\t}\n\t\treturn await this.client.createJwt(\n\t\t\ttenantId,\n\t\t\toptions.userId,\n\t\t\toptions.scopes,\n\t\t);\n\t}\n\n\t/**\n\t * Runs SQL against an attached database with tenant isolation applied.\n\t * Used by embed run-sql (e.g. dashboard chart execution). Requires a database\n\t * to be attached (e.g. via attachPostgres) and options.tenantId.\n\t */\n\tasync runSqlForDashboard(\n\t\tinput: {\n\t\t\t/**\n\t\t\t * Server-issued id from ask() / modifyChart(). Takes priority: the stored SQL\n\t\t\t * and params are executed and `sql` / `params` are ignored.\n\t\t\t */\n\t\t\tqueryId?: string;\n\t\t\t/** Required when `queryId` is not provided (backward compatible raw SQL). */\n\t\t\tsql?: string;\n\t\t\tparams?: Record<string, unknown>;\n\t\t\tdatabase?: string;\n\t\t},\n\t\toptions: { tenantId: string },\n\t): Promise<{ rows: unknown[]; fields: string[] }> {\n\t\tconst tenantId = options.tenantId?.trim();\n\t\tif (!tenantId) {\n\t\t\tthrow new Error(\"tenantId is required\");\n\t\t}\n\t\tconst databaseName = input.database ?? \"db\";\n\t\tlet sql = input.sql;\n\t\tlet params = (input.params ?? {}) as ParamRecord;\n\t\tif (input.queryId) {\n\t\t\tconst resolved = await queriesRoute.resolveQuery(\n\t\t\t\tthis.client,\n\t\t\t\tthis.queryEngine,\n\t\t\t\tinput.queryId,\n\t\t\t\t{ tenantId },\n\t\t\t);\n\t\t\tsql = resolved.sql;\n\t\t\tparams = resolved.params;\n\t\t}\n\t\tif (!sql) {\n\t\t\tthrow new Error(\"Either queryId or sql is required\");\n\t\t}\n\t\tconst result = await this.queryEngine.validateAndExecute(\n\t\t\tsql,\n\t\t\tparams,\n\t\t\tdatabaseName,\n\t\t\ttenantId,\n\t\t);\n\t\tif (input.queryId) {\n\t\t\tawait queriesRoute.markQueryExecuted(this.client, input.queryId, {\n\t\t\t\ttenantId,\n\t\t\t});\n\t\t}\n\t\treturn { rows: result.rows, fields: result.fields };\n\t}\n\n\t// VizSpec generation\n\n\t/**\n\t * Generates a VizSpec visualization specification from query results.\n\t *\n\t * Use this when you have raw SQL results and want to generate a chart\n\t * specification without going through the full ask() flow. Useful for\n\t * re-generating charts with different settings.\n\t *\n\t * @param input - VizSpec generation input with question, SQL, and result data\n\t * @param options - Optional settings for tenant and retries\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns VizSpec specification for chart, table, or metric visualization\n\t *\n\t * @example\n\t * ```typescript\n\t * const vizspec = await qp.generateVizSpec({\n\t * question: \"Revenue by country\",\n\t * sql: \"SELECT country, SUM(revenue) FROM orders GROUP BY country\",\n\t * fields: [\"country\", \"revenue\"],\n\t * rows: queryResults,\n\t * }, { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync generateVizSpec(\n\t\tinput: vizspecRoute.VizSpecGenerateInput,\n\t\toptions?: vizspecRoute.VizSpecGenerateOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<vizspecRoute.VizSpecResponse> {\n\t\treturn await vizspecRoute.generateVizSpec(\n\t\t\tthis.client,\n\t\t\t{\n\t\t\t\t...input,\n\t\t\t\tsupported_chart_types:\n\t\t\t\t\tinput.supported_chart_types ?? this.supportedChartTypes,\n\t\t\t},\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t// Chart modification\n\n\t/**\n\t * Modifies a chart by regenerating SQL and/or applying visualization changes.\n\t *\n\t * This method supports three modes of operation:\n\t *\n\t * 1. **SQL Modifications**: When `sqlModifications` is provided, the SQL is\n\t * regenerated using the query endpoint with modification hints. If `customSql`\n\t * is set, it's used directly without regeneration.\n\t *\n\t * 2. **Visualization Modifications**: When only `vizModifications` is provided,\n\t * the existing SQL is re-executed and a new chart is generated with the\n\t * specified encoding preferences.\n\t *\n\t * 3. **Combined**: Both SQL and visualization modifications can be applied\n\t * together. SQL is regenerated first, then viz modifications are applied.\n\t *\n\t * @param input - Chart modification input with source data and modifications\n\t * @param options - Optional settings for tenant, user, and chart generation\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Modified chart response with SQL, data, and chart specification\n\t *\n\t * @example\n\t * ```typescript\n\t * // Change chart type and axis from an ask() response\n\t * const modified = await qp.modifyChart({\n\t * sql: response.sql,\n\t * question: \"revenue by country\",\n\t * database: \"analytics\",\n\t * vizModifications: {\n\t * chartType: \"bar\",\n\t * xAxis: { field: \"country\" },\n\t * yAxis: { field: \"revenue\", aggregate: \"sum\" },\n\t * },\n\t * }, { tenantId: \"tenant_123\" });\n\t *\n\t * // Change time granularity (triggers SQL regeneration)\n\t * const monthly = await qp.modifyChart({\n\t * sql: response.sql,\n\t * question: \"revenue over time\",\n\t * database: \"analytics\",\n\t * sqlModifications: {\n\t * timeGranularity: \"month\",\n\t * dateRange: { from: \"2024-01-01\", to: \"2024-12-31\" },\n\t * },\n\t * }, { tenantId: \"tenant_123\" });\n\t *\n\t * // Direct SQL edit with chart regeneration\n\t * const customized = await qp.modifyChart({\n\t * sql: response.sql,\n\t * question: \"revenue by country\",\n\t * database: \"analytics\",\n\t * sqlModifications: {\n\t * customSql: \"SELECT country, SUM(revenue) FROM orders GROUP BY country\",\n\t * },\n\t * }, { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync modifyChart(\n\t\tinput: modifyRoute.ChartModifyInput,\n\t\toptions?: modifyRoute.ChartModifyOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<modifyRoute.ChartModifyResponse> {\n\t\treturn await modifyRoute.modifyChart(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tinput,\n\t\t\toptions\n\t\t\t\t? {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\tsupportedChartTypes:\n\t\t\t\t\t\t\toptions.supportedChartTypes ?? this.supportedChartTypes,\n\t\t\t\t\t}\n\t\t\t\t: this.supportedChartTypes\n\t\t\t\t\t? { supportedChartTypes: this.supportedChartTypes }\n\t\t\t\t\t: undefined,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t// Chart CRUD operations\n\n\t/**\n\t * Saves a chart to the QueryPanel system for later retrieval.\n\t *\n\t * Charts store the SQL query, parameters, and visualization spec - never the actual data.\n\t * Data is fetched live when the chart is rendered or refreshed.\n\t *\n\t * @param body - Chart data including title, SQL, and Vega-Lite spec\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns The saved chart with its generated ID\n\t *\n\t * @example\n\t * ```typescript\n\t * const savedChart = await qp.createChart({\n\t * title: \"Revenue by Country\",\n\t * sql: response.sql,\n\t * sql_params: response.params,\n\t * vega_lite_spec: response.chart.vegaLiteSpec,\n\t * target_db: \"analytics\",\n\t * }, { tenantId: \"tenant_123\", userId: \"user_456\" });\n\t * ```\n\t */\n\tasync createChart(\n\t\tbody: chartsRoute.ChartCreateInput,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.SdkChart> {\n\t\treturn await chartsRoute.createChart(this.client, body, options, signal);\n\t}\n\n\t/**\n\t * Lists saved charts with optional filtering and pagination.\n\t *\n\t * Use `includeData: true` to execute each chart's SQL and include live data.\n\t *\n\t * @param options - Filtering, pagination, and data options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Paginated list of charts\n\t *\n\t * @example\n\t * ```typescript\n\t * // List charts with pagination\n\t * const charts = await qp.listCharts({\n\t * tenantId: \"tenant_123\",\n\t * pagination: { page: 1, limit: 10 },\n\t * sortBy: \"created_at\",\n\t * sortDir: \"desc\",\n\t * });\n\t *\n\t * // List with live data\n\t * const chartsWithData = await qp.listCharts({\n\t * tenantId: \"tenant_123\",\n\t * includeData: true,\n\t * });\n\t * ```\n\t */\n\tasync listCharts(\n\t\toptions?: chartsRoute.ChartListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.PaginatedResponse<chartsRoute.SdkChart>> {\n\t\treturn await chartsRoute.listCharts(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Lists saved charts via `GET /charts/all` (same query, filters, and pagination as {@link listCharts}).\n\t */\n\tasync listAllCharts(\n\t\toptions?: chartsRoute.ChartListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.PaginatedResponse<chartsRoute.SdkChart>> {\n\t\treturn await chartsRoute.listAllCharts(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Bulk gets saved charts by chart IDs (`GET /charts/bulk` with `ids` query params).\n\t */\n\tasync getChartsByIds(\n\t\tids: string[],\n\t\toptions?: chartsRoute.ChartListAllOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.ChartBulkGetResponse> {\n\t\treturn await chartsRoute.getChartsByIds(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tids,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t// Session history CRUD operations\n\n\t/**\n\t * Lists query sessions with pagination and filtering.\n\t *\n\t * @param options - Filtering, pagination, and sort options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Paginated list of sessions\n\t *\n\t * @example\n\t * ```typescript\n\t * const sessions = await qp.listSessions({\n\t * tenantId: \"tenant_123\",\n\t * pagination: { page: 1, limit: 20 },\n\t * sortBy: \"updated_at\",\n\t * });\n\t * ```\n\t */\n\tasync listSessions(\n\t\toptions?: sessionsRoute.SessionListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<sessionsRoute.PaginatedResponse<sessionsRoute.SdkSession>> {\n\t\treturn await sessionsRoute.listSessions(this.client, options, signal);\n\t}\n\n\t/**\n\t * Retrieves a session by session_id with optional turn history.\n\t *\n\t * @param sessionId - QueryPanel session identifier used in ask()\n\t * @param options - Tenant, user, scopes, and includeTurns flag\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Session metadata with optional turns\n\t *\n\t * @example\n\t * ```typescript\n\t * const session = await qp.getSession(\"session_123\", {\n\t * tenantId: \"tenant_123\",\n\t * includeTurns: true,\n\t * });\n\t * ```\n\t */\n\tasync getSession(\n\t\tsessionId: string,\n\t\toptions?: sessionsRoute.SessionGetOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<sessionsRoute.SdkSession> {\n\t\treturn await sessionsRoute.getSession(\n\t\t\tthis.client,\n\t\t\tsessionId,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Updates session metadata (title).\n\t *\n\t * @param sessionId - QueryPanel session identifier to update\n\t * @param body - Fields to update\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Updated session\n\t *\n\t * @example\n\t * ```typescript\n\t * const updated = await qp.updateSession(\n\t * \"session_123\",\n\t * { title: \"Q4 Revenue Analysis\" },\n\t * { tenantId: \"tenant_123\" },\n\t * );\n\t * ```\n\t */\n\tasync updateSession(\n\t\tsessionId: string,\n\t\tbody: sessionsRoute.SessionUpdateInput,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<sessionsRoute.SdkSession> {\n\t\treturn await sessionsRoute.updateSession(\n\t\t\tthis.client,\n\t\t\tsessionId,\n\t\t\tbody,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Deletes a session and its turn history.\n\t *\n\t * @param sessionId - QueryPanel session identifier to delete\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t *\n\t * @example\n\t * ```typescript\n\t * await qp.deleteSession(\"session_123\", { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync deleteSession(\n\t\tsessionId: string,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\tawait sessionsRoute.deleteSession(this.client, sessionId, options, signal);\n\t}\n\n\t/**\n\t * Retrieves a single chart by ID with live data.\n\t *\n\t * The chart's SQL is automatically executed and data is included in the response.\n\t *\n\t * @param id - Chart ID\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Chart with live data populated\n\t *\n\t * @example\n\t * ```typescript\n\t * const chart = await qp.getChart(\"chart_123\", {\n\t * tenantId: \"tenant_123\",\n\t * });\n\t * console.log(chart.vega_lite_spec.data.values); // Live data\n\t * ```\n\t */\n\tasync getChart(\n\t\tid: string,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.SdkChart> {\n\t\treturn await chartsRoute.getChart(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tid,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Updates an existing chart's metadata or configuration.\n\t *\n\t * @param id - Chart ID to update\n\t * @param body - Fields to update (partial update supported)\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Updated chart\n\t *\n\t * @example\n\t * ```typescript\n\t * const updated = await qp.updateChart(\"chart_123\", {\n\t * title: \"Updated Chart Title\",\n\t * description: \"New description\",\n\t * }, { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync updateChart(\n\t\tid: string,\n\t\tbody: chartsRoute.ChartUpdateInput,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.SdkChart> {\n\t\treturn await chartsRoute.updateChart(\n\t\t\tthis.client,\n\t\t\tid,\n\t\t\tbody,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Deletes a chart permanently.\n\t *\n\t * @param id - Chart ID to delete\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t *\n\t * @example\n\t * ```typescript\n\t * await qp.deleteChart(\"chart_123\", { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync deleteChart(\n\t\tid: string,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\tawait chartsRoute.deleteChart(this.client, id, options, signal);\n\t}\n\n\t// Active Chart CRUD operations (Dashboard)\n\n\t/**\n\t * Pins a saved chart to the dashboard (Active Charts).\n\t *\n\t * Active Charts are used for building dashboards. Unlike the chart history,\n\t * active charts are meant to be displayed together with layout metadata.\n\t *\n\t * @param body - Active chart config with chart_id, order, and optional meta\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Created active chart entry\n\t *\n\t * @example\n\t * ```typescript\n\t * const pinned = await qp.createActiveChart({\n\t * chart_id: savedChart.id,\n\t * order: 1,\n\t * meta: { width: \"full\", variant: \"dark\" },\n\t * }, { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync createActiveChart(\n\t\tbody: activeChartsRoute.ActiveChartCreateInput,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<activeChartsRoute.SdkActiveChart> {\n\t\treturn await activeChartsRoute.createActiveChart(\n\t\t\tthis.client,\n\t\t\tbody,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Lists all active charts (dashboard items) with optional live data.\n\t *\n\t * Use `withData: true` to execute each chart's SQL and include results.\n\t * This is the primary method for loading a complete dashboard.\n\t *\n\t * @param options - Filtering and data options including withData\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Paginated list of active charts with optional live data\n\t *\n\t * @example\n\t * ```typescript\n\t * // Load dashboard with live data\n\t * const dashboard = await qp.listActiveCharts({\n\t * tenantId: \"tenant_123\",\n\t * withData: true,\n\t * });\n\t *\n\t * dashboard.data.forEach(item => {\n\t * console.log(item.chart?.title);\n\t * console.log(item.chart?.vega_lite_spec.data.values);\n\t * });\n\t * ```\n\t */\n\tasync listActiveCharts(\n\t\toptions?: activeChartsRoute.ActiveChartListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.PaginatedResponse<activeChartsRoute.SdkActiveChart>> {\n\t\treturn await activeChartsRoute.listActiveCharts(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Lists all active charts without pagination.\n\t */\n\tasync listAllActiveCharts(\n\t\toptions?: activeChartsRoute.ActiveChartListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<activeChartsRoute.SdkActiveChart[]> {\n\t\treturn await activeChartsRoute.listAllActiveCharts(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Bulk gets active charts by IDs.\n\t */\n\tasync getActiveChartsByIds(\n\t\tids: string[],\n\t\toptions?: activeChartsRoute.ActiveChartListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<activeChartsRoute.ActiveChartBulkGetResponse> {\n\t\treturn await activeChartsRoute.getActiveChartsByIds(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tids,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Retrieves a single active chart by ID.\n\t *\n\t * @param id - Active chart ID\n\t * @param options - Options including withData for live data\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Active chart with associated chart data\n\t *\n\t * @example\n\t * ```typescript\n\t * const activeChart = await qp.getActiveChart(\"active_123\", {\n\t * tenantId: \"tenant_123\",\n\t * withData: true,\n\t * });\n\t * ```\n\t */\n\tasync getActiveChart(\n\t\tid: string,\n\t\toptions?: activeChartsRoute.ActiveChartListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<activeChartsRoute.SdkActiveChart> {\n\t\treturn await activeChartsRoute.getActiveChart(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tid,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Updates an active chart's order or metadata.\n\t *\n\t * Use this to reorder dashboard items or update layout hints.\n\t *\n\t * @param id - Active chart ID to update\n\t * @param body - Fields to update (order, meta)\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Updated active chart\n\t *\n\t * @example\n\t * ```typescript\n\t * const updated = await qp.updateActiveChart(\"active_123\", {\n\t * order: 5,\n\t * meta: { width: \"half\" },\n\t * }, { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync updateActiveChart(\n\t\tid: string,\n\t\tbody: activeChartsRoute.ActiveChartUpdateInput,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<activeChartsRoute.SdkActiveChart> {\n\t\treturn await activeChartsRoute.updateActiveChart(\n\t\t\tthis.client,\n\t\t\tid,\n\t\t\tbody,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Removes a chart from the dashboard (unpins it).\n\t *\n\t * This only removes the active chart entry, not the underlying saved chart.\n\t *\n\t * @param id - Active chart ID to delete\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t *\n\t * @example\n\t * ```typescript\n\t * await qp.deleteActiveChart(\"active_123\", { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync deleteActiveChart(\n\t\tid: string,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\tawait activeChartsRoute.deleteActiveChart(this.client, id, options, signal);\n\t}\n\n\t// Dashboard CRUD operations\n\n\t/**\n\t * Creates a new dashboard with BlockNote content.\n\t *\n\t * @param body - Dashboard configuration including name and BlockNote content\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Created dashboard\n\t *\n\t * @example\n\t * ```typescript\n\t * const dashboard = await qp.createDashboard({\n\t * name: \"Sales Dashboard\",\n\t * description: \"Monthly sales metrics\",\n\t * content_json: blockNoteContent,\n\t * editor_type: \"blocknote\",\n\t * }, { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync createDashboard(\n\t\tbody: dashboardsRoute.DashboardCreateInput,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.createDashboard(\n\t\t\tthis.client,\n\t\t\tbody,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Lists dashboards with pagination and filtering.\n\t *\n\t * @param options - Filtering and pagination options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Paginated list of dashboards\n\t *\n\t * @example\n\t * ```typescript\n\t * const dashboards = await qp.listDashboards({\n\t * tenantId: \"tenant_123\",\n\t * status: \"deployed\",\n\t * pagination: { page: 1, limit: 10 },\n\t * });\n\t * ```\n\t */\n\tasync listDashboards(\n\t\toptions?: dashboardsRoute.DashboardListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.PaginatedResponse<dashboardsRoute.SdkDashboard>> {\n\t\treturn await dashboardsRoute.listDashboards(this.client, options, signal);\n\t}\n\n\t/**\n\t * Gets a dashboard by ID.\n\t *\n\t * @param id - Dashboard ID\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Dashboard details\n\t *\n\t * @example\n\t * ```typescript\n\t * const dashboard = await qp.getDashboard(\"dash_123\", {\n\t * tenantId: \"tenant_123\",\n\t * });\n\t * ```\n\t */\n\tasync getDashboard(\n\t\tid: string,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.getDashboard(\n\t\t\tthis.client,\n\t\t\tid,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Gets a dashboard for a specific tenant.\n\t * Returns customer fork if exists, otherwise returns the original dashboard.\n\t *\n\t * @param id - Dashboard ID\n\t * @param tenantId - Tenant ID to check for fork\n\t * @param options - Additional auth options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Dashboard (fork if exists, original otherwise)\n\t *\n\t * @example\n\t * ```typescript\n\t * const dashboard = await qp.getDashboardForTenant(\n\t * \"dash_123\",\n\t * \"tenant_456\",\n\t * );\n\t * ```\n\t */\n\tasync getDashboardForTenant(\n\t\tid: string,\n\t\ttenantId: string,\n\t\toptions?: { userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.getDashboardForTenant(\n\t\t\tthis.client,\n\t\t\tid,\n\t\t\ttenantId,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Updates a dashboard.\n\t *\n\t * @param id - Dashboard ID to update\n\t * @param body - Fields to update\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Updated dashboard\n\t *\n\t * @example\n\t * ```typescript\n\t * const updated = await qp.updateDashboard(\"dash_123\", {\n\t * name: \"Updated Dashboard\",\n\t * content_json: newBlockNoteContent,\n\t * }, { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync updateDashboard(\n\t\tid: string,\n\t\tbody: dashboardsRoute.DashboardUpdateInput,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.updateDashboard(\n\t\t\tthis.client,\n\t\t\tid,\n\t\t\tbody,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Updates dashboard status (deploy/undeploy).\n\t *\n\t * @param id - Dashboard ID\n\t * @param status - New status\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Updated dashboard\n\t *\n\t * @example\n\t * ```typescript\n\t * const deployed = await qp.updateDashboardStatus(\n\t * \"dash_123\",\n\t * \"deployed\",\n\t * { tenantId: \"tenant_123\" },\n\t * );\n\t * ```\n\t */\n\tasync updateDashboardStatus(\n\t\tid: string,\n\t\tstatus: \"draft\" | \"deployed\",\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.updateDashboardStatus(\n\t\t\tthis.client,\n\t\t\tid,\n\t\t\tstatus,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Deletes a dashboard.\n\t *\n\t * @param id - Dashboard ID to delete\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t *\n\t * @example\n\t * ```typescript\n\t * await qp.deleteDashboard(\"dash_123\", { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync deleteDashboard(\n\t\tid: string,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\tawait dashboardsRoute.deleteDashboard(this.client, id, options, signal);\n\t}\n\n\t// Dashboard Fork operations (Customer Customization)\n\n\t/**\n\t * Forks a dashboard for customer customization (copy-on-write).\n\t *\n\t * Creates a full copy of the dashboard that the customer can edit independently.\n\t * The original dashboard remains unchanged.\n\t *\n\t * @param id - Dashboard ID to fork\n\t * @param input - Fork configuration with tenant_id\n\t * @param options - Additional auth options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Forked dashboard\n\t *\n\t * @example\n\t * ```typescript\n\t * const fork = await qp.forkDashboard(\"dash_123\", {\n\t * tenant_id: \"tenant_456\",\n\t * name: \"Customer's Custom Dashboard\",\n\t * });\n\t * ```\n\t */\n\tasync forkDashboard(\n\t\tid: string,\n\t\tinput: dashboardsRoute.DashboardForkInput,\n\t\toptions?: { userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.forkDashboard(\n\t\t\tthis.client,\n\t\t\tid,\n\t\t\tinput,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Updates a customer fork.\n\t *\n\t * @param forkId - Fork ID to update\n\t * @param input - Update data including tenant_id\n\t * @param options - Additional auth options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Updated fork\n\t *\n\t * @example\n\t * ```typescript\n\t * const updated = await qp.updateFork(\"fork_123\", {\n\t * tenant_id: \"tenant_456\",\n\t * content_json: newBlockNoteContent,\n\t * });\n\t * ```\n\t */\n\tasync updateFork(\n\t\tforkId: string,\n\t\tinput: dashboardsRoute.DashboardForkUpdateInput,\n\t\toptions?: { userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.updateFork(\n\t\t\tthis.client,\n\t\t\tforkId,\n\t\t\tinput,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Rollbacks a fork to the original dashboard.\n\t * This deletes the fork and the customer will see the original again.\n\t *\n\t * @param forkId - Fork ID to rollback\n\t * @param tenantId - Tenant ID\n\t * @param options - Additional auth options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Original dashboard\n\t *\n\t * @example\n\t * ```typescript\n\t * const original = await qp.rollbackFork(\"fork_123\", \"tenant_456\");\n\t * ```\n\t */\n\tasync rollbackFork(\n\t\tforkId: string,\n\t\ttenantId: string,\n\t\toptions?: { userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.rollbackFork(\n\t\t\tthis.client,\n\t\t\tforkId,\n\t\t\ttenantId,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Deletes a customer fork.\n\t *\n\t * @param forkId - Fork ID to delete\n\t * @param tenantId - Tenant ID\n\t * @param options - Additional auth options\n\t * @param signal - Optional AbortSignal for cancellation\n\t *\n\t * @example\n\t * ```typescript\n\t * await qp.deleteFork(\"fork_123\", \"tenant_456\");\n\t * ```\n\t */\n\tasync deleteFork(\n\t\tforkId: string,\n\t\ttenantId: string,\n\t\toptions?: { userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\tawait dashboardsRoute.deleteFork(\n\t\t\tthis.client,\n\t\t\tforkId,\n\t\t\ttenantId,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Lists all customer forks for a tenant.\n\t *\n\t * @param tenantId - Tenant ID\n\t * @param options - Additional auth options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Array of forks\n\t *\n\t * @example\n\t * ```typescript\n\t * const forks = await qp.listForksForTenant(\"tenant_456\");\n\t * ```\n\t */\n\tasync listForksForTenant(\n\t\ttenantId: string,\n\t\toptions?: { userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard[]> {\n\t\treturn await dashboardsRoute.listForksForTenant(\n\t\t\tthis.client,\n\t\t\ttenantId,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n}\n","import type {\n IntrospectOptions,\n SchemaIntrospection,\n} from \"../schema/types\";\n\nexport type DatabaseDialect =\n\t| \"bigquery\"\n\t| \"clickhouse\"\n\t| \"postgres\"\n\t| \"redshift\"\n\t| \"mysql\";\n\n/** A schema (or ClickHouse database / BigQuery dataset) in the catalog. */\nexport interface CatalogSchema {\n schema: string;\n tableCount: number;\n}\n\nexport type CatalogTableType = \"table\" | \"view\" | \"materialized_view\";\n\n/** A table or view in the catalog (no columns; cheap to list). */\nexport interface CatalogTable {\n schema: string;\n name: string;\n type: CatalogTableType;\n}\n\nexport interface ListTablesOptions {\n /** Only list tables in this schema. */\n schema?: string;\n /** Case-insensitive substring match on the table name. */\n search?: string;\n /** Max rows to return. Default 500, capped at 5000. */\n limit?: number;\n}\n\nexport const DEFAULT_CATALOG_LIMIT = 500;\nexport const MAX_CATALOG_LIMIT = 5000;\n\n/** Clamp a caller-provided limit to a safe positive integer. */\nexport function resolveCatalogLimit(limit?: number): number {\n if (limit === undefined || !Number.isFinite(limit)) {\n return DEFAULT_CATALOG_LIMIT;\n }\n return Math.min(Math.max(Math.floor(limit), 1), MAX_CATALOG_LIMIT);\n}\n\nexport interface DatabaseExecutionResult {\n fields: string[];\n rows: Array<Record<string, unknown>>;\n}\n\n/**\n * Database adapter interface for abstracting database-specific operations.\n * Allows the SDK to work with multiple database types.\n */\nexport interface DatabaseAdapter {\n /**\n * Execute a SQL query and return results\n * @param sql - The SQL query to execute\n * @param params - Optional query parameters for parameterized queries\n */\n execute(\n sql: string,\n params?: Record<string, string | number | boolean | string[] | number[]>,\n ): Promise<DatabaseExecutionResult>;\n\n /**\n * Validate SQL query (e.g., using EXPLAIN)\n * Throws an error if the SQL is invalid\n * @param sql - The SQL query to validate\n * @param params - Optional query parameters for parameterized queries\n */\n validate(\n sql: string,\n params?: Record<string, string | number | boolean | string[] | number[]>,\n ): Promise<void>;\n\n /**\n * Introspect database schema metadata\n */\n introspect(options?: IntrospectOptions): Promise<SchemaIntrospection>;\n\n /**\n * Get the database dialect/type\n */\n getDialect(): DatabaseDialect;\n\n /**\n * Optional: List schemas with table counts (cheap catalog read for pickers).\n */\n listSchemas?(): Promise<CatalogSchema[]>;\n\n /**\n * Optional: List tables/views without columns (cheap catalog read for pickers).\n */\n listTables?(options?: ListTablesOptions): Promise<CatalogTable[]>;\n\n /**\n * Optional: Close/cleanup database connection\n */\n close?(): Promise<void>;\n}\n","import type {\n\tColumnSchema,\n\tIntrospectOptions,\n\tSchemaIntrospection,\n\tTableSchema,\n} from \"../schema/types\";\nimport {\n\ttype CatalogSchema,\n\ttype CatalogTable,\n\ttype DatabaseAdapter,\n\ttype DatabaseExecutionResult,\n\ttype ListTablesOptions,\n\tresolveCatalogLimit,\n} from \"./types\";\n\n/**\n * Request passed to BigQueryClientFn.\n * dryRun: when true, the client should run the query as a dry run (validation only, no execution).\n */\nexport interface BigQueryQueryRequest {\n\tquery: string;\n\tparams?: Record<string, string | number | boolean | string[] | number[]>;\n\t/** When true, validate the query without executing (e.g. BigQuery dry run). */\n\tdryRun?: boolean;\n}\n\nexport interface BigQueryQueryResult {\n\trows: Array<Record<string, unknown>>;\n\tfields: string[];\n}\n\n/**\n * Client function for the BigQuery adapter.\n * Implement using @google-cloud/bigquery: createQueryJob with dryRun for validation, getQueryResults for execute.\n */\nexport type BigQueryClientFn = (\n\trequest: BigQueryQueryRequest,\n) => Promise<BigQueryQueryResult>;\n\nexport interface BigQueryAdapterOptions {\n\t/** GCP project ID. Used for INFORMATION_SCHEMA queries. */\n\tprojectId: string;\n\t/** Optional project that owns the dataset. Defaults to projectId. */\n\tdatasetProjectId?: string;\n\t/** Default dataset (schema) name. Used for introspection and unqualified table names. */\n\tdataset: string;\n\t/** Optional location for the dataset (e.g. \"US\", \"EU\"). */\n\tlocation?: string;\n\t/** Logical database name used in introspection metadata. */\n\tdatabase?: string;\n\t/** Optional database kind label. Defaults to \"bigquery\". */\n\tkind?: SchemaIntrospection[\"db\"][\"kind\"];\n\t/**\n\t * Optional allow-list of table names (dataset.table or bare table name).\n\t * `*` or `<dataset>.*` allows every table in the dataset (same as no filter).\n\t * When specified, introspection and queries are restricted to these tables only.\n\t */\n\tallowedTables?: string[];\n}\n\ntype TableRow = {\n\ttable_name: string;\n\ttable_schema: string;\n\ttable_type: string;\n};\n\ntype ColumnRow = {\n\ttable_name: string;\n\ttable_schema: string;\n\tcolumn_name: string;\n\tdata_type: string;\n\tordinal_position: number;\n\tis_nullable: string | null;\n};\n\ninterface NormalizedTable {\n\tdataset: string;\n\ttable: string;\n}\n\n/**\n * BigQuery adapter following the same pattern as PostgresAdapter and ClickHouseAdapter.\n * - execute: runs the query via the client function.\n * - validate: uses dry run (no EXPLAIN in BigQuery) to validate SQL without executing.\n * - introspect: uses INFORMATION_SCHEMA.TABLES and INFORMATION_SCHEMA.COLUMNS.\n */\nexport class BigQueryAdapter implements DatabaseAdapter {\n\tprivate readonly projectId: string;\n\tprivate readonly datasetProjectId: string;\n\tprivate readonly defaultDataset: string;\n\tprivate readonly databaseName: string;\n\tprivate readonly kind: SchemaIntrospection[\"db\"][\"kind\"];\n\tprivate readonly allowedTables?: NormalizedTable[];\n\n\tconstructor(\n\t\tprivate readonly clientFn: BigQueryClientFn,\n\t\toptions: BigQueryAdapterOptions,\n\t) {\n\t\tthis.projectId = options.projectId;\n\t\tthis.datasetProjectId = options.datasetProjectId ?? options.projectId;\n\t\tthis.defaultDataset = options.dataset;\n\t\tthis.databaseName = options.database ?? options.dataset;\n\t\tthis.kind = options.kind ?? \"bigquery\";\n\t\tif (options.allowedTables) {\n\t\t\tthis.allowedTables = normalizeTableFilter(\n\t\t\t\toptions.allowedTables,\n\t\t\t\tthis.defaultDataset,\n\t\t\t);\n\t\t}\n\t}\n\n\tasync execute(\n\t\tsql: string,\n\t\tparams?: Record<string, string | number | boolean | string[] | number[]>,\n\t): Promise<DatabaseExecutionResult> {\n\t\tif (this.allowedTables) {\n\t\t\tthis.validateQueryTables(sql);\n\t\t}\n\n\t\tconst result = await this.clientFn({\n\t\t\tquery: sql,\n\t\t\tparams,\n\t\t\tdryRun: false,\n\t\t});\n\t\treturn { fields: result.fields, rows: result.rows };\n\t}\n\n\tasync validate(\n\t\tsql: string,\n\t\tparams?: Record<string, string | number | boolean | string[] | number[]>,\n\t): Promise<void> {\n\t\tawait this.clientFn({\n\t\t\tquery: sql,\n\t\t\tparams,\n\t\t\tdryRun: true,\n\t\t});\n\t}\n\n\tgetDialect() {\n\t\treturn \"bigquery\" as const;\n\t}\n\n\t/**\n\t * Introspect using BigQuery INFORMATION_SCHEMA (TABLES and COLUMNS).\n\t * Restricts to the default dataset (or allowedTables) and returns tables/columns only.\n\t */\n\tasync introspect(options?: IntrospectOptions): Promise<SchemaIntrospection> {\n\t\tconst tablesToIntrospect = options?.tables\n\t\t\t? normalizeTableFilter(options.tables, this.defaultDataset)\n\t\t\t: this.allowedTables;\n\t\tconst normalizedTables = tablesToIntrospect ?? [];\n\n\t\tconst tablesSql = buildTablesQuery(\n\t\t\tthis.datasetProjectId,\n\t\t\tthis.defaultDataset,\n\t\t\tnormalizedTables,\n\t\t);\n\t\tconst tablesResult = await this.clientFn({\n\t\t\tquery: tablesSql,\n\t\t\tdryRun: false,\n\t\t});\n\t\tconst tableRows = tablesResult.rows as unknown as TableRow[];\n\n\t\tconst columnsSql = buildColumnsQuery(\n\t\t\tthis.datasetProjectId,\n\t\t\tthis.defaultDataset,\n\t\t\tnormalizedTables,\n\t\t);\n\t\tconst columnsResult = await this.clientFn({\n\t\t\tquery: columnsSql,\n\t\t\tdryRun: false,\n\t\t});\n\t\tconst columnRows = columnsResult.rows as unknown as ColumnRow[];\n\n\t\tconst tablesByKey = new Map<string, TableSchema>();\n\n\t\tfor (const row of tableRows) {\n\t\t\tconst key = tableKey(row.table_schema, row.table_name);\n\t\t\tconst table: TableSchema = {\n\t\t\t\tname: row.table_name,\n\t\t\t\tschema: row.table_schema,\n\t\t\t\ttype: asTableType(row.table_type),\n\t\t\t\tcolumns: [],\n\t\t\t};\n\t\t\ttablesByKey.set(key, table);\n\t\t}\n\n\t\t// columnRows is ordered by ordinal_position, so columns are added in correct order\n\t\tfor (const row of columnRows) {\n\t\t\tconst key = tableKey(row.table_schema, row.table_name);\n\t\t\tconst table = tablesByKey.get(key);\n\t\t\tif (!table) continue;\n\n\t\t\tconst column: ColumnSchema = {\n\t\t\t\tname: row.column_name,\n\t\t\t\ttype: row.data_type,\n\t\t\t\tisPrimaryKey: false,\n\t\t\t};\n\t\t\ttable.columns.push(column);\n\t\t}\n\n\t\tconst tables = Array.from(tablesByKey.values()).sort((a, b) => {\n\t\t\tif (a.schema === b.schema) {\n\t\t\t\treturn a.name.localeCompare(b.name);\n\t\t\t}\n\t\t\treturn a.schema.localeCompare(b.schema);\n\t\t});\n\n\t\treturn {\n\t\t\tdb: {\n\t\t\t\tkind: this.kind,\n\t\t\t\tname: this.databaseName,\n\t\t\t},\n\t\t\ttables,\n\t\t\tintrospectedAt: new Date().toISOString(),\n\t\t};\n\t}\n\n\t/** The adapter is bound to one dataset, so this returns a single row. */\n\tasync listSchemas(): Promise<CatalogSchema[]> {\n\t\tconst result = await this.clientFn({\n\t\t\tquery: `SELECT COUNT(*) AS table_count\n FROM ${this.informationSchema(\"TABLES\")}`,\n\t\t\tdryRun: false,\n\t\t});\n\t\treturn [\n\t\t\t{\n\t\t\t\tschema: this.defaultDataset,\n\t\t\t\ttableCount: Number(result.rows[0]?.table_count ?? 0),\n\t\t\t},\n\t\t];\n\t}\n\n\t/**\n\t * List tables in the bound dataset. Reads the full catalog (not restricted\n\t * by `allowedTables`). Default limit 500, max 5000.\n\t */\n\tasync listTables(options: ListTablesOptions = {}): Promise<CatalogTable[]> {\n\t\tif (options.schema && options.schema !== this.defaultDataset) {\n\t\t\treturn [];\n\t\t}\n\t\tconst search = options.search?.trim();\n\t\tconst params: Record<string, string> = {};\n\t\tlet searchClause = \"\";\n\t\tif (search) {\n\t\t\tparams.search = `%${search.toLowerCase().replace(/[\\\\%_]/g, (ch) => `\\\\${ch}`)}%`;\n\t\t\tsearchClause = \"\\n WHERE LOWER(table_name) LIKE @search\";\n\t\t}\n\t\t// Integer produced by resolveCatalogLimit, never raw input.\n\t\tconst limit = resolveCatalogLimit(options.limit);\n\t\tconst result = await this.clientFn({\n\t\t\tquery: `SELECT table_schema, table_name, table_type\n FROM ${this.informationSchema(\"TABLES\")}${searchClause}\n ORDER BY table_name\n LIMIT ${limit}`,\n\t\t\t...(search ? { params } : {}),\n\t\t\tdryRun: false,\n\t\t});\n\t\treturn (result.rows as unknown as TableRow[]).map((row) => ({\n\t\t\tschema: row.table_schema,\n\t\t\tname: row.table_name,\n\t\t\ttype: asTableType(row.table_type),\n\t\t}));\n\t}\n\n\tprivate informationSchema(view: \"TABLES\"): string {\n\t\treturn `\\`${this.datasetProjectId}.${this.defaultDataset}.INFORMATION_SCHEMA.${view}\\``;\n\t}\n\n\tprivate validateQueryTables(sql: string): void {\n\t\tif (!this.allowedTables || this.allowedTables.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst allowedSet = new Set(\n\t\t\tthis.allowedTables.map((t) => tableKey(t.dataset, t.table)),\n\t\t);\n\n\t\t// Match FROM `project.dataset.table` or FROM dataset.table or FROM table\n\t\tconst tablePattern =\n\t\t\t/(?:FROM|JOIN)\\s+`?(?:[\\w-]+\\.)?([\\w-]+)\\.([\\w-]+)`?|(?:FROM|JOIN)\\s+`?([\\w-]+)`?/gi;\n\t\tconst matches = sql.matchAll(tablePattern);\n\n\t\tfor (const match of matches) {\n\t\t\tconst dataset = match[1] ?? match[3] ?? this.defaultDataset;\n\t\t\tconst table = match[2] ?? match[3];\n\t\t\tif (table) {\n\t\t\t\tconst key = tableKey(dataset, table);\n\t\t\t\tif (!allowedSet.has(key)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Query references table \"${dataset}.${table}\" which is not in the allowed tables list`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction normalizeTableFilter(\n\ttables: string[] | undefined,\n\tdefaultDataset: string,\n): NormalizedTable[] {\n\tif (!tables?.length) return [];\n\tconst normalized: NormalizedTable[] = [];\n\tconst seen = new Set<string>();\n\n\tfor (const raw of tables) {\n\t\tif (!raw?.trim()) continue;\n\t\tconst trimmed = raw.trim();\n\t\t// `*` / `<dataset>.*` = whole dataset = no filter.\n\t\tif (trimmed === \"*\" || trimmed === `${defaultDataset}.*`) return [];\n\t\tconst parts = trimmed.split(\".\");\n\t\tconst table = parts.pop() ?? \"\";\n\t\tif (table === \"*\") continue;\n\t\tconst dataset = parts.pop() ?? defaultDataset;\n\t\tconst key = tableKey(dataset, table);\n\t\tif (seen.has(key)) continue;\n\t\tseen.add(key);\n\t\tnormalized.push({ dataset, table });\n\t}\n\n\treturn normalized;\n}\n\nfunction tableKey(dataset: string, table: string): string {\n\treturn `${dataset}.${table}`;\n}\n\nfunction buildTablesQuery(\n\tdatasetProjectId: string,\n\tdataset: string,\n\ttables: NormalizedTable[],\n): string {\n\tconst qualifier = `\\`${datasetProjectId}.${dataset}.INFORMATION_SCHEMA.TABLES\\``;\n\tconst filter =\n\t\ttables.length > 0\n\t\t\t? ` AND table_name IN (${tables.map((t) => `'${escapeSql(t.table)}'`).join(\", \")})`\n\t\t\t: \"\";\n\treturn `SELECT table_name, table_schema, table_type\n FROM ${qualifier}\n WHERE table_schema = '${escapeSql(dataset)}'${filter}\n ORDER BY table_schema, table_name`;\n}\n\nfunction buildColumnsQuery(\n\tdatasetProjectId: string,\n\tdataset: string,\n\ttables: NormalizedTable[],\n): string {\n\tconst qualifier = `\\`${datasetProjectId}.${dataset}.INFORMATION_SCHEMA.COLUMNS\\``;\n\tconst filter =\n\t\ttables.length > 0\n\t\t\t? ` AND table_name IN (${tables.map((t) => `'${escapeSql(t.table)}'`).join(\", \")})`\n\t\t\t: \"\";\n\treturn `SELECT table_name, table_schema, column_name, data_type, ordinal_position, is_nullable\n FROM ${qualifier}\n WHERE table_schema = '${escapeSql(dataset)}'${filter}\n ORDER BY table_schema, table_name, ordinal_position`;\n}\n\nfunction escapeSql(value: string): string {\n\treturn value.replace(/\\\\/g, \"\\\\\\\\\").replace(/'/g, \"''\");\n}\n\nfunction asTableType(tableType: string): CatalogTable[\"type\"] {\n\tconst normalized = tableType?.toLowerCase() ?? \"\";\n\tif (normalized.includes(\"view\")) {\n\t\treturn normalized.includes(\"materialized\") ? \"materialized_view\" : \"view\";\n\t}\n\tif (normalized.includes(\"external\") || normalized.includes(\"snapshot\") || normalized.includes(\"clone\")) {\n\t\treturn \"table\";\n\t}\n\treturn \"table\";\n}\n","const WRAPPER_REGEX =\n /^(Nullable|LowCardinality|SimpleAggregateFunction)\\((.+)\\)$/i;\n\nexport function isNullableType(type: string): boolean {\n return /Nullable\\s*\\(/i.test(type);\n}\n\nexport function unwrapTypeModifiers(type: string): string {\n let current = type.trim();\n let match = WRAPPER_REGEX.exec(current);\n while (match) {\n const inner = match[2];\n if (!inner) {\n break;\n }\n current = inner.trim();\n match = WRAPPER_REGEX.exec(current);\n }\n return current;\n}\n\nexport function extractPrecisionScale(type: string): {\n precision?: number;\n scale?: number;\n} {\n const unwrapped = unwrapTypeModifiers(type);\n const decimalMatch = unwrapped.match(/Decimal(?:\\d+)?\\((\\d+)\\s*,\\s*(\\d+)\\)/i);\n if (!decimalMatch) return {};\n const precision = decimalMatch[1];\n const scale = decimalMatch[2];\n if (!precision || !scale) return {};\n return {\n precision: Number.parseInt(precision, 10),\n scale: Number.parseInt(scale, 10),\n };\n}\n\nexport function extractFixedStringLength(type: string): number | undefined {\n const unwrapped = unwrapTypeModifiers(type);\n const match = unwrapped.match(/^(?:FixedString|StringFixed)\\((\\d+)\\)$/i);\n if (!match) return undefined;\n const length = match[1];\n if (!length) return undefined;\n return Number.parseInt(length, 10);\n}\n\nexport function parseKeyExpression(expression?: string | null): string[] {\n if (!expression) return [];\n let value = expression.trim();\n if (!value) return [];\n if (/^tuple\\s*\\(/i.test(value) && value.endsWith(\")\")) {\n value = value.replace(/^tuple\\s*\\(/i, \"\").replace(/\\)$/, \"\");\n }\n\n const columns: string[] = [];\n let depth = 0;\n let token = \"\";\n for (const ch of value) {\n if (ch === \"(\") {\n depth += 1;\n token += ch;\n continue;\n }\n if (ch === \")\") {\n depth = Math.max(0, depth - 1);\n token += ch;\n continue;\n }\n if (ch === \",\" && depth === 0) {\n const col = token.trim();\n if (col) columns.push(stripWrapper(col));\n token = \"\";\n continue;\n }\n token += ch;\n }\n const last = token.trim();\n if (last) columns.push(stripWrapper(last));\n return columns.filter(Boolean);\n}\n\nfunction stripWrapper(value: string): string {\n const noQuotes = stripQuotes(value);\n const withoutTicks = noQuotes.replace(/`/g, \"\").trim();\n const parts = withoutTicks.split(\".\");\n return parts[parts.length - 1]?.trim() ?? \"\";\n}\n\nfunction stripQuotes(value: string): string {\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n return value.slice(1, -1);\n }\n return value;\n}\n","import type {\n\tClickHouseSettings,\n\tDataFormat,\n\tQueryParams,\n} from \"@clickhouse/client\";\nimport type {\n\tColumnSchema,\n\tIntrospectOptions,\n\tSchemaIntrospection,\n\tTableSchema,\n} from \"../schema/types\";\nimport { parseKeyExpression, unwrapTypeModifiers } from \"../utils/clickhouse\";\nimport {\n\ttype CatalogSchema,\n\ttype CatalogTable,\n\ttype DatabaseAdapter,\n\ttype DatabaseExecutionResult,\n\ttype ListTablesOptions,\n\tresolveCatalogLimit,\n} from \"./types\";\n\nexport interface ClickHouseAdapterOptions {\n\t/** Optional logical database name used in introspection metadata. */\n\tdatabase?: string;\n\t/** Override the default response format used for query execution. */\n\tdefaultFormat?: DataFormat;\n\t/**\n\t * Optional database kind label. Defaults to \"clickhouse\" but allows\n\t * sub-classing/custom branding if needed.\n\t */\n\tkind?: SchemaIntrospection[\"db\"][\"kind\"];\n\t/**\n\t * Optional allow-list of table names.\n\t * When specified, introspection and queries are restricted to these tables only.\n\t * ClickHouse tables are not schema-qualified, so just provide table names.\n\t * `*` or `<database>.*` allows every table (same as no filter).\n\t */\n\tallowedTables?: string[];\n}\n\nexport type ClickHouseQueryResult = { json: () => Promise<unknown> };\n\nexport type ClickHouseClientFn = (\n\tparams: QueryParams,\n) => Promise<\n\t| ClickHouseQueryResult\n\t| Array<Record<string, unknown>>\n\t| Record<string, unknown>[]\n>;\n\ninterface QueryOptions {\n\tparams?: Record<string, unknown>;\n\tformat?: DataFormat;\n\tsettings?: ClickHouseSettings;\n}\n\ntype TableRow = {\n\tname: string;\n\tengine: string;\n\tcomment: string | null;\n\tprimary_key: string | null;\n};\n\ntype ColumnRow = {\n\ttable: string;\n\tname: string;\n\ttype: string;\n\tposition: number;\n\tcomment: string | null;\n\tis_in_primary_key: string | number | null;\n};\n\n/**\n * Simplified ClickHouse adapter following IngestRequest format\n * Removed: indexes, constraints, statistics\n * Kept only: tables, columns (name, type, isPrimaryKey, comment)\n */\nexport class ClickHouseAdapter implements DatabaseAdapter {\n\tprivate readonly databaseName: string;\n\tprivate readonly defaultFormat: QueryParams[\"format\"];\n\tprivate readonly kind: SchemaIntrospection[\"db\"][\"kind\"];\n\tprivate readonly allowedTables?: string[];\n\n\tconstructor(\n\t\tprivate readonly clientFn: ClickHouseClientFn,\n\t\toptions: ClickHouseAdapterOptions = {},\n\t) {\n\t\tthis.databaseName = options.database ?? \"default\";\n\t\tthis.defaultFormat = options.defaultFormat ?? \"JSONEachRow\";\n\t\tthis.kind = options.kind ?? \"clickhouse\";\n\t\tif (options.allowedTables) {\n\t\t\tthis.allowedTables = normalizeTableFilter(\n\t\t\t\toptions.allowedTables,\n\t\t\t\tthis.databaseName,\n\t\t\t);\n\t\t}\n\t}\n\n\tasync execute(\n\t\tsql: string,\n\t\tparams?: Record<string, string | number | boolean | string[] | number[]>,\n\t): Promise<DatabaseExecutionResult> {\n\t\t// Validate query against allowed tables if restrictions are in place\n\t\tif (this.allowedTables) {\n\t\t\tthis.validateQueryTables(sql);\n\t\t}\n\n\t\tconst queryOptions: QueryOptions = {\n\t\t\tformat: this.defaultFormat,\n\t\t};\n\t\tif (params) {\n\t\t\tqueryOptions.params = params;\n\t\t}\n\n\t\tconst rows = await this.query<Record<string, unknown>>(sql, queryOptions);\n\t\tconst fields = rows.length > 0 ? Object.keys(rows[0] ?? {}) : [];\n\t\treturn { fields, rows };\n\t}\n\n\tasync validate(\n\t\tsql: string,\n\t\tparams?: Record<string, string | number | boolean | string[] | number[]>,\n\t): Promise<void> {\n\t\tconst queryOptions: QueryOptions = {\n\t\t\tformat: this.defaultFormat,\n\t\t};\n\t\tif (params) {\n\t\t\tqueryOptions.params = params;\n\t\t}\n\n\t\tawait this.query(`EXPLAIN ${sql}`, queryOptions);\n\t}\n\n\tgetDialect() {\n\t\treturn \"clickhouse\" as const;\n\t}\n\n\t/**\n\t * Simplified introspection: only collect table/column metadata for IngestRequest\n\t * No indexes, constraints, or statistics\n\t */\n\tasync introspect(options?: IntrospectOptions): Promise<SchemaIntrospection> {\n\t\t// Use adapter-level allowedTables if no specific tables provided in options\n\t\tconst tablesToIntrospect = options?.tables\n\t\t\t? normalizeTableFilter(options.tables, this.databaseName)\n\t\t\t: this.allowedTables;\n\t\tconst allowTables = tablesToIntrospect ?? [];\n\t\tconst hasFilter = allowTables.length > 0;\n\t\tconst queryParams: Record<string, unknown> = {\n\t\t\tdb: this.databaseName,\n\t\t};\n\t\tif (hasFilter) {\n\t\t\tqueryParams.tables = allowTables;\n\t\t}\n\n\t\tconst filterClause = hasFilter ? \" AND name IN {tables:Array(String)}\" : \"\";\n\t\tconst tables = await this.query<TableRow>(\n\t\t\t`SELECT name, engine, comment, primary_key\n FROM system.tables\n WHERE database = {db:String}${filterClause}\n ORDER BY name`,\n\t\t\t{ params: queryParams },\n\t\t);\n\n\t\tconst columnFilterClause = hasFilter\n\t\t\t? \" AND table IN {tables:Array(String)}\"\n\t\t\t: \"\";\n\t\tconst columns = await this.query<ColumnRow>(\n\t\t\t`SELECT table, name, type, position, comment, is_in_primary_key\n FROM system.columns\n WHERE database = {db:String}${columnFilterClause}\n ORDER BY table, position`,\n\t\t\t{ params: queryParams },\n\t\t);\n\n\t\tconst columnsByTable = new Map<string, ColumnSchema[]>();\n\t\tfor (const rawColumn of columns) {\n\t\t\tconst list = columnsByTable.get(rawColumn.table) ?? [];\n\t\t\tlist.push(transformColumnRow(rawColumn));\n\t\t\tcolumnsByTable.set(rawColumn.table, list);\n\t\t}\n\n\t\tconst tableSchemas: TableSchema[] = tables.map((table) => {\n\t\t\tconst tableColumns = columnsByTable.get(table.name) ?? [];\n\t\t\tconst primaryKeyColumns = parseKeyExpression(table.primary_key);\n\n\t\t\t// Mark columns as primary key\n\t\t\tfor (const column of tableColumns) {\n\t\t\t\tcolumn.isPrimaryKey =\n\t\t\t\t\tcolumn.isPrimaryKey || primaryKeyColumns.includes(column.name);\n\t\t\t}\n\n\t\t\tconst base: TableSchema = {\n\t\t\t\tname: table.name,\n\t\t\t\tschema: this.databaseName,\n\t\t\t\ttype: asTableType(table.engine),\n\t\t\t\tcolumns: tableColumns,\n\t\t\t};\n\n\t\t\tconst comment = sanitize(table.comment);\n\t\t\tif (comment !== undefined) {\n\t\t\t\tbase.comment = comment;\n\t\t\t}\n\n\t\t\treturn base;\n\t\t});\n\n\t\treturn {\n\t\t\tdb: {\n\t\t\t\tkind: this.kind,\n\t\t\t\tname: this.databaseName,\n\t\t\t},\n\t\t\ttables: tableSchemas,\n\t\t\tintrospectedAt: new Date().toISOString(),\n\t\t};\n\t}\n\n\t/** The adapter is bound to one database, so this returns a single row. */\n\tasync listSchemas(): Promise<CatalogSchema[]> {\n\t\tconst rows = await this.query<{ table_count: string | number }>(\n\t\t\t`SELECT count() AS table_count\n FROM system.tables\n WHERE database = {db:String}`,\n\t\t\t{ params: { db: this.databaseName } },\n\t\t);\n\t\treturn [\n\t\t\t{\n\t\t\t\tschema: this.databaseName,\n\t\t\t\ttableCount: toNumber(rows[0]?.table_count) ?? 0,\n\t\t\t},\n\t\t];\n\t}\n\n\t/**\n\t * List tables in the bound database. Reads the full catalog (not restricted\n\t * by `allowedTables`). Default limit 500, max 5000.\n\t */\n\tasync listTables(options: ListTablesOptions = {}): Promise<CatalogTable[]> {\n\t\tif (options.schema && options.schema !== this.databaseName) {\n\t\t\treturn [];\n\t\t}\n\t\tconst params: Record<string, unknown> = {\n\t\t\tdb: this.databaseName,\n\t\t\tlimit: resolveCatalogLimit(options.limit),\n\t\t};\n\t\tconst search = options.search?.trim();\n\t\tlet searchClause = \"\";\n\t\tif (search) {\n\t\t\tparams.search = search;\n\t\t\tsearchClause = \" AND positionCaseInsensitive(name, {search:String}) > 0\";\n\t\t}\n\t\tconst rows = await this.query<{ name: string; engine: string }>(\n\t\t\t`SELECT name, engine\n FROM system.tables\n WHERE database = {db:String}${searchClause}\n ORDER BY name\n LIMIT {limit:UInt32}`,\n\t\t\t{ params },\n\t\t);\n\t\treturn rows.map((row) => ({\n\t\t\tschema: this.databaseName,\n\t\t\tname: row.name,\n\t\t\ttype: asCatalogTableType(row.engine),\n\t\t}));\n\t}\n\n\tprivate validateQueryTables(sql: string): void {\n\t\tif (!this.allowedTables || this.allowedTables.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst allowedSet = new Set(this.allowedTables);\n\n\t\t// Extract potential table references from SQL\n\t\tconst tablePattern =\n\t\t\t/\\b(FROM|JOIN)\\s+(?:FINAL\\s+)?(?:(?:[a-zA-Z_][a-zA-Z0-9_]*)\\.)?([\"'`]?[a-zA-Z_][a-zA-Z0-9_]*[\"'`]?)/gi;\n\t\tconst matches = sql.matchAll(tablePattern);\n\n\t\tfor (const match of matches) {\n\t\t\tconst clause = match[1]?.toUpperCase();\n\t\t\tconst matchIndex = match.index ?? -1;\n\t\t\tif (\n\t\t\t\tclause === \"JOIN\" &&\n\t\t\t\tmatchIndex >= 0 &&\n\t\t\t\tisArrayJoinClause(sql, matchIndex)\n\t\t\t) {\n\t\t\t\t// ClickHouse ARRAY JOIN expands array-typed columns, not tables.\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst table = match[2]?.replace(/[\"'`]/g, \"\");\n\t\t\tif (table) {\n\t\t\t\tif (!allowedSet.has(table)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Query references table \"${table}\" which is not in the allowed tables list`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// No-op: lifecycle of the underlying client is controlled by the caller.\n\t}\n\n\tprivate async query<T>(sql: string, options?: QueryOptions): Promise<T[]> {\n\t\tconst params: QueryParams = {\n\t\t\tquery: sql,\n\t\t};\n\n\t\tconst format = options?.format ?? this.defaultFormat;\n\t\tif (format !== undefined) {\n\t\t\tparams.format = format;\n\t\t}\n\n\t\tif (options?.params) {\n\t\t\tparams.query_params = options.params;\n\t\t}\n\n\t\tif (options?.settings) {\n\t\t\tparams.clickhouse_settings = options.settings;\n\t\t}\n\n\t\tconst result = await this.clientFn(params);\n\t\treturn this.extractRows<T>(result);\n\t}\n\n\tprivate async extractRows<T>(\n\t\tresult:\n\t\t\t| ClickHouseQueryResult\n\t\t\t| Array<Record<string, unknown>>\n\t\t\t| Record<string, unknown>[],\n\t): Promise<T[]> {\n\t\tif (Array.isArray(result)) {\n\t\t\treturn result as T[];\n\t\t}\n\n\t\tif (\n\t\t\tresult &&\n\t\t\ttypeof (result as ClickHouseQueryResult).json === \"function\"\n\t\t) {\n\t\t\tconst payload = await (result as ClickHouseQueryResult).json();\n\t\t\treturn normalizePayload<T>(payload);\n\t\t}\n\n\t\treturn [];\n\t}\n}\n\nfunction normalizePayload<T>(payload: unknown): T[] {\n\tif (Array.isArray(payload)) {\n\t\treturn payload as T[];\n\t}\n\tif (payload && typeof payload === \"object\") {\n\t\tconst maybeData = (payload as { data?: unknown }).data;\n\t\tif (Array.isArray(maybeData)) {\n\t\t\treturn maybeData as T[];\n\t\t}\n\t}\n\treturn [];\n}\n\n/**\n * Normalize to bare table names. `*` or `<database>.*` means \"all tables\" and\n * returns an empty list (no filter); wildcards for other databases are ignored.\n */\nfunction normalizeTableFilter(\n\ttables: string[] | null | undefined,\n\tdatabaseName: string,\n): string[] {\n\tif (!tables?.length) return [];\n\tconst seen = new Set<string>();\n\tconst normalized: string[] = [];\n\tfor (const table of tables) {\n\t\tif (!table) continue;\n\t\tconst trimmed = table.trim();\n\t\tif (!trimmed) continue;\n\t\tif (trimmed === \"*\" || trimmed === `${databaseName}.*`) return [];\n\t\tconst parts = trimmed.split(\".\");\n\t\tconst tableName = parts[parts.length - 1];\n\t\tif (tableName === \"*\") continue;\n\t\tif (!tableName || seen.has(tableName)) continue;\n\t\tseen.add(tableName);\n\t\tnormalized.push(tableName);\n\t}\n\treturn normalized;\n}\n\nfunction isArrayJoinClause(sql: string, joinIndex: number): boolean {\n\tconst beforeJoin = sql.slice(0, joinIndex);\n\tconst previousToken = beforeJoin.trim().split(/\\s+/).pop()?.toUpperCase();\n\treturn previousToken === \"ARRAY\";\n}\n\nfunction transformColumnRow(row: ColumnRow): ColumnSchema {\n\tconst unwrappedType = unwrapTypeModifiers(row.type);\n\n\tconst column: ColumnSchema = {\n\t\tname: row.name,\n\t\ttype: unwrappedType,\n\t\trawType: row.type,\n\t\tisPrimaryKey: Boolean(toNumber(row.is_in_primary_key)),\n\t};\n\n\tconst comment = sanitize(row.comment);\n\tif (comment !== undefined) column.comment = comment;\n\n\treturn column;\n}\n\nfunction asTableType(engine: unknown): TableSchema[\"type\"] {\n\tif (typeof engine === \"string\") {\n\t\tconst normalized = engine.toLowerCase();\n\t\t// ClickHouse view engines: View, MaterializedView, LiveView\n\t\tif (normalized.includes(\"view\")) {\n\t\t\treturn \"view\";\n\t\t}\n\t}\n\treturn \"table\";\n}\n\nfunction asCatalogTableType(engine: unknown): CatalogTable[\"type\"] {\n\tconst normalized = typeof engine === \"string\" ? engine.toLowerCase() : \"\";\n\tif (normalized === \"materializedview\") return \"materialized_view\";\n\tif (normalized.includes(\"view\")) return \"view\";\n\treturn \"table\";\n}\n\nfunction sanitize(value: unknown): string | undefined {\n\tif (value === null || value === undefined) return undefined;\n\tconst trimmed = String(value).trim();\n\treturn trimmed.length ? trimmed : undefined;\n}\n\nfunction toNumber(value: unknown): number | undefined {\n\tif (value === null || value === undefined) return undefined;\n\tif (typeof value === \"number\") return value;\n\tconst parsed = Number.parseFloat(String(value));\n\treturn Number.isNaN(parsed) ? undefined : parsed;\n}\n","import type {\n\tColumnSchema,\n\tIntrospectOptions,\n\tSchemaIntrospection,\n\tTableSchema,\n} from \"../schema/types\";\nimport {\n\ttype CatalogSchema,\n\ttype CatalogTable,\n\ttype DatabaseAdapter,\n\ttype DatabaseExecutionResult,\n\ttype ListTablesOptions,\n\tresolveCatalogLimit,\n} from \"./types\";\n\nexport interface PostgresQueryResult {\n\trows: Array<Record<string, unknown>>;\n\tfields: Array<{ name: string }>;\n}\n\nexport type PostgresClientFn = (\n\tsql: string,\n\tparams?: unknown[],\n) => Promise<PostgresQueryResult>;\n\nexport type PostgresWireDialect = \"postgres\" | \"redshift\";\n\nexport interface PostgresAdapterOptions {\n\t/** Logical database name used in introspection metadata. */\n\tdatabase?: string;\n\t/** Schema to assume when a table is provided without qualification. */\n\tdefaultSchema?: string;\n\t/** Optional database kind label. Defaults to the dialect. */\n\tkind?: SchemaIntrospection[\"db\"][\"kind\"];\n\t/**\n\t * Wire-compatible engine behind the client. \"redshift\" uses Redshift's\n\t * svv_* catalog views for introspection. Defaults to \"postgres\".\n\t */\n\tdialect?: PostgresWireDialect;\n\t/**\n\t * Optional allow-list of table names (schema-qualified or bare).\n\t * Use `schema.*` to allow every table in a schema (a bare `*` means the\n\t * default schema). When specified, introspection and queries are restricted\n\t * to these tables only.\n\t */\n\tallowedTables?: string[];\n}\n\ntype TableRow = {\n\ttable_name: string;\n\tschema_name: string;\n\ttable_type: string;\n\tcomment: string | null;\n};\n\ntype ColumnRow = {\n\ttable_name: string;\n\ttable_schema: string;\n\tcolumn_name: string;\n\tdata_type: string;\n\tudt_name: string | null;\n\tis_primary_key: boolean;\n\tdescription: string | null;\n};\n\n/** `table` is `\"*\"` when every table in `schema` is allowed. */\nexport interface NormalizedTable {\n\tschema: string;\n\ttable: string;\n}\n\nconst WILDCARD = \"*\";\n\n/** Highest $N index referenced in SQL (0 when the query uses literal values only). */\nexport function maxPostgresPlaceholderIndex(sql: string): number {\n\tlet max = 0;\n\tfor (const match of sql.matchAll(/\\$(\\d+)\\b/g)) {\n\t\tconst index = Number.parseInt(match[1], 10);\n\t\tif (Number.isFinite(index) && index > max) {\n\t\t\tmax = index;\n\t\t}\n\t}\n\treturn max;\n}\n\ntype PostgresParamRecord = Record<\n\tstring,\n\tstring | number | boolean | string[] | number[]\n>;\n\n/**\n * Drop bind parameters when SQL has no $1..$N placeholders (common after LLM inlines literals).\n */\nexport function filterParamsForPostgresSql(\n\tsql: string,\n\tparams: PostgresParamRecord | undefined,\n): PostgresParamRecord | undefined {\n\tif (!params || Object.keys(params).length === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst placeholderCount = maxPostgresPlaceholderIndex(sql);\n\tif (placeholderCount === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst hasNumericKeys = Object.keys(params).some((key) => /^\\d+$/.test(key));\n\tif (hasNumericKeys) {\n\t\tconst filtered: PostgresParamRecord = {};\n\t\tfor (let index = 1; index <= placeholderCount; index += 1) {\n\t\t\tconst key = String(index);\n\t\t\tif (key in params) {\n\t\t\t\tfiltered[key] = params[key];\n\t\t\t}\n\t\t}\n\t\treturn Object.keys(filtered).length > 0 ? filtered : undefined;\n\t}\n\n\treturn params;\n}\n\n/**\n * Simplified PostgreSQL adapter following IngestRequest format\n * Removed: indexes, constraints, foreign keys, statistics\n * Kept only: tables, columns (name, type, isPrimaryKey, comment)\n */\nexport class PostgresAdapter implements DatabaseAdapter {\n\tprivate readonly databaseName: string;\n\tprivate readonly defaultSchema: string;\n\tprivate readonly kind: SchemaIntrospection[\"db\"][\"kind\"];\n\tprivate readonly dialect: PostgresWireDialect;\n\tprivate readonly allowedTables?: NormalizedTable[];\n\n\tconstructor(\n\t\tprivate readonly clientFn: PostgresClientFn,\n\t\toptions: PostgresAdapterOptions = {},\n\t) {\n\t\tthis.databaseName = options.database ?? \"postgres\";\n\t\tthis.defaultSchema = options.defaultSchema ?? \"public\";\n\t\tthis.dialect = options.dialect ?? \"postgres\";\n\t\tthis.kind = options.kind ?? this.dialect;\n\t\tif (options.allowedTables) {\n\t\t\tthis.allowedTables = normalizeTableFilter(\n\t\t\t\toptions.allowedTables,\n\t\t\t\tthis.defaultSchema,\n\t\t\t);\n\t\t}\n\t}\n\n\tasync execute(\n\t\tsql: string,\n\t\tparams?: Record<string, string | number | boolean | string[] | number[]>,\n\t): Promise<DatabaseExecutionResult> {\n\t\t// Validate query against allowed tables if restrictions are in place\n\t\tif (this.allowedTables) {\n\t\t\tthis.validateQueryTables(sql);\n\t\t}\n\n\t\tconst boundParams = filterParamsForPostgresSql(sql, params);\n\n\t\t// Convert named params to positional array for PostgreSQL\n\t\tlet paramArray: unknown[] | undefined;\n\t\tif (boundParams) {\n\t\t\tparamArray = this.convertNamedToPositionalParams(sql, boundParams);\n\t\t}\n\n\t\tconst result = await this.clientFn(sql, paramArray);\n\t\tconst fields = result.fields.map((f) => f.name);\n\t\treturn { fields, rows: result.rows };\n\t}\n\n\tprivate validateQueryTables(sql: string): void {\n\t\tif (!this.allowedTables || this.allowedTables.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst allowedSet = new Set(\n\t\t\tthis.allowedTables.map((t) => tableKey(t.schema, t.table)),\n\t\t);\n\t\tconst allowedSchemas = new Set(\n\t\t\tthis.allowedTables\n\t\t\t\t.filter((t) => t.table === WILDCARD)\n\t\t\t\t.map((t) => t.schema),\n\t\t);\n\n\t\t// First, neutralize function calls that use FROM keyword (EXTRACT, SUBSTRING, TRIM, etc.)\n\t\t// Replace their FROM with a placeholder to avoid false positives\n\t\tconst neutralizedSql = sql\n\t\t\t.replace(/EXTRACT\\s*\\([^)]*FROM\\s+[^)]+\\)/gi, \"EXTRACT(/*neutralized*/)\")\n\t\t\t.replace(/SUBSTRING\\s*\\([^)]*FROM\\s+[^)]+\\)/gi, \"SUBSTRING(/*neutralized*/)\")\n\t\t\t.replace(/TRIM\\s*\\([^)]*FROM\\s+[^)]+\\)/gi, \"TRIM(/*neutralized*/)\")\n\t\t\t.replace(/POSITION\\s*\\([^)]*FROM\\s+[^)]+\\)/gi, \"POSITION(/*neutralized*/)\");\n\n\t\t// Extract potential table references from SQL\n\t\tconst tablePattern =\n\t\t\t/(?:FROM|JOIN)\\s+(?:ONLY\\s+)?(?:([a-zA-Z_][a-zA-Z0-9_]*)\\.)?([\"']?[a-zA-Z_][a-zA-Z0-9_]*[\"']?)/gi;\n\t\tconst matches = neutralizedSql.matchAll(tablePattern);\n\n\t\tfor (const match of matches) {\n\t\t\tconst schema = match[1] ?? this.defaultSchema;\n\t\t\tconst table = match[2]?.replace(/['\"]/g, \"\");\n\t\t\tif (table) {\n\t\t\t\tconst key = tableKey(schema, table);\n\t\t\t\tif (!allowedSet.has(key) && !allowedSchemas.has(schema)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Query references table \"${schema}.${table}\" which is not in the allowed tables list`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Convert params to positional array for PostgreSQL ($1, $2, …).\n\t * Values must align with v2 generation order via numeric keys from mapGeneratedParams.\n\t */\n\tprivate convertNamedToPositionalParams(\n\t\tsql: string,\n\t\tparams: Record<string, string | number | boolean | string[] | number[]>,\n\t): unknown[] {\n\t\tconst placeholderCount = maxPostgresPlaceholderIndex(sql);\n\t\tif (placeholderCount === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst positionalParams: unknown[] = [];\n\n\t\tfor (let index = 1; index <= placeholderCount; index += 1) {\n\t\t\tconst key = String(index);\n\t\t\tlet val: unknown = params[key];\n\n\t\t\tif (val === undefined && placeholderCount === 1) {\n\t\t\t\tconst namedKeys = Object.keys(params).filter((k) => !/^\\d+$/.test(k));\n\t\t\t\tif (namedKeys.length === 1) {\n\t\t\t\t\tval = params[namedKeys[0] as keyof typeof params];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (val === undefined) {\n\t\t\t\tthrow new Error(`Missing value for PostgreSQL parameter $${index}`);\n\t\t\t}\n\n\t\t\tif (typeof val === \"string\") {\n\t\t\t\tconst match = val.match(/^<([a-zA-Z0-9_]+)>$/);\n\t\t\t\tconst namedKey = match?.[1];\n\t\t\t\tif (namedKey && namedKey in params) {\n\t\t\t\t\tval = params[namedKey as keyof typeof params];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpositionalParams.push(val);\n\t\t}\n\n\t\treturn positionalParams;\n\t}\n\n\tasync validate(\n\t\tsql: string,\n\t\tparams?: Record<string, string | number | boolean | string[] | number[]>,\n\t): Promise<void> {\n\t\tconst boundParams = filterParamsForPostgresSql(sql, params);\n\t\tlet paramArray: unknown[] | undefined;\n\t\tif (boundParams) {\n\t\t\tparamArray = this.convertNamedToPositionalParams(sql, boundParams);\n\t\t}\n\n\t\tawait this.clientFn(`EXPLAIN ${sql}`, paramArray);\n\t}\n\n\tgetDialect(): PostgresWireDialect {\n\t\treturn this.dialect;\n\t}\n\n\t/**\n\t * List schemas with their table/view counts. Reads the full catalog (not\n\t * restricted by `allowedTables`) so it can back an allowed-tables picker.\n\t */\n\tasync listSchemas(): Promise<CatalogSchema[]> {\n\t\tconst result = await this.clientFn(\n\t\t\tthis.dialect === \"redshift\"\n\t\t\t\t? buildRedshiftListSchemasQuery()\n\t\t\t\t: buildListSchemasQuery(),\n\t\t);\n\t\treturn result.rows.map((row) => ({\n\t\t\tschema: String(row.schema),\n\t\t\ttableCount: Number(row.table_count ?? 0),\n\t\t}));\n\t}\n\n\t/**\n\t * List tables/views, optionally filtered by schema and a case-insensitive\n\t * substring of the table name. Reads the full catalog (not restricted by\n\t * `allowedTables`). Default limit 500, max 5000.\n\t */\n\tasync listTables(options: ListTablesOptions = {}): Promise<CatalogTable[]> {\n\t\tconst { sql, params } = buildListTablesQuery(this.dialect, options);\n\t\tconst result = await this.clientFn(sql, params);\n\t\treturn result.rows.map((row) => ({\n\t\t\tschema: String(row.schema),\n\t\t\tname: String(row.name),\n\t\t\ttype: asTableType(String(row.table_type ?? \"\")),\n\t\t}));\n\t}\n\n\t/**\n\t * Simplified introspection: only collect table/column metadata for IngestRequest\n\t * No indexes, constraints, or statistics\n\t */\n\tasync introspect(options?: IntrospectOptions): Promise<SchemaIntrospection> {\n\t\t// Use adapter-level allowedTables if no specific tables provided in options\n\t\tconst tablesToIntrospect = options?.tables\n\t\t\t? normalizeTableFilter(options.tables, this.defaultSchema)\n\t\t\t: this.allowedTables;\n\t\tconst normalizedTables = tablesToIntrospect ?? [];\n\n\t\tconst isRedshift = this.dialect === \"redshift\";\n\t\tconst tablesResult = await this.clientFn(\n\t\t\tisRedshift\n\t\t\t\t? buildRedshiftTablesQuery(normalizedTables)\n\t\t\t\t: buildTablesQuery(normalizedTables),\n\t\t);\n\t\tconst tableRows = tablesResult.rows as TableRow[];\n\n\t\tconst columnsResult = await this.clientFn(\n\t\t\tisRedshift\n\t\t\t\t? buildRedshiftColumnsQuery(normalizedTables)\n\t\t\t\t: buildColumnsQuery(normalizedTables),\n\t\t);\n\t\tconst columnRows = columnsResult.rows as ColumnRow[];\n\n\t\tconst tablesByKey = new Map<string, TableSchema>();\n\n\t\t// Build tables\n\t\tfor (const row of tableRows) {\n\t\t\tconst key = tableKey(row.schema_name, row.table_name);\n\t\t\tconst table: TableSchema = {\n\t\t\t\tname: row.table_name,\n\t\t\t\tschema: row.schema_name,\n\t\t\t\ttype: asTableType(row.table_type),\n\t\t\t\tcolumns: [],\n\t\t\t};\n\n\t\t\tconst comment = sanitize(row.comment);\n\t\t\tif (comment !== undefined) {\n\t\t\t\ttable.comment = comment;\n\t\t\t}\n\n\t\t\ttablesByKey.set(key, table);\n\t\t}\n\n\t\t// Build columns\n\t\tfor (const row of columnRows) {\n\t\t\tconst key = tableKey(row.table_schema, row.table_name);\n\t\t\tconst table = tablesByKey.get(key);\n\t\t\tif (!table) continue;\n\n\t\t\tconst column: ColumnSchema = {\n\t\t\t\tname: row.column_name,\n\t\t\t\ttype: row.data_type,\n\t\t\t\tisPrimaryKey: row.is_primary_key,\n\t\t\t};\n\n\t\t\tconst rawType = row.udt_name ?? undefined;\n\t\t\tif (rawType !== undefined) column.rawType = rawType;\n\n\t\t\tconst comment = sanitize(row.description);\n\t\t\tif (comment !== undefined) column.comment = comment;\n\n\t\t\ttable.columns.push(column);\n\t\t}\n\n\t\tconst tables = Array.from(tablesByKey.values()).sort((a, b) => {\n\t\t\tif (a.schema === b.schema) {\n\t\t\t\treturn a.name.localeCompare(b.name);\n\t\t\t}\n\t\t\treturn a.schema.localeCompare(b.schema);\n\t\t});\n\n\t\treturn {\n\t\t\tdb: {\n\t\t\t\tkind: this.kind,\n\t\t\t\tname: this.databaseName,\n\t\t\t},\n\t\t\ttables,\n\t\t\tintrospectedAt: new Date().toISOString(),\n\t\t};\n\t}\n}\n\nfunction normalizeTableFilter(\n\ttables: string[] | undefined,\n\tdefaultSchema: string,\n): NormalizedTable[] {\n\tif (!tables?.length) return [];\n\tconst normalized: NormalizedTable[] = [];\n\tconst seen = new Set<string>();\n\n\tfor (const raw of tables) {\n\t\tif (!raw) continue;\n\t\tconst trimmed = raw.trim();\n\t\tif (!trimmed) continue;\n\t\tconst parts = trimmed.split(\".\");\n\t\tconst table = parts.pop() ?? \"\";\n\t\tconst schema = parts.pop() ?? defaultSchema;\n\t\tif (\n\t\t\t!isSafeIdentifier(schema) ||\n\t\t\t(table !== WILDCARD && !isSafeIdentifier(table))\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst key = tableKey(schema, table);\n\t\tif (seen.has(key)) continue;\n\t\tseen.add(key);\n\t\tnormalized.push({ schema, table });\n\t}\n\n\treturn normalized;\n}\n\nfunction buildTablesQuery(tables: NormalizedTable[]): string {\n\tconst filter = buildFilterClause(tables, \"n.nspname\", \"c.relname\");\n\treturn `SELECT\n c.relname AS table_name,\n n.nspname AS schema_name,\n CASE c.relkind\n WHEN 'r' THEN 'table'\n WHEN 'v' THEN 'view'\n WHEN 'm' THEN 'materialized_view'\n ELSE c.relkind::text\n END AS table_type,\n obj_description(c.oid) AS comment\n FROM pg_class c\n JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')\n AND c.relkind IN ('r', 'v', 'm')\n ${filter}\n ORDER BY n.nspname, c.relname;`;\n}\n\nfunction buildColumnsQuery(tables: NormalizedTable[]): string {\n\tconst filter = buildFilterClause(\n\t\ttables,\n\t\t\"cols.table_schema\",\n\t\t\"cols.table_name\",\n\t);\n\treturn `SELECT\n cols.table_name,\n cols.table_schema,\n cols.column_name,\n cols.data_type,\n cols.udt_name,\n pgd.description,\n EXISTS(\n SELECT 1\n FROM information_schema.table_constraints tc\n JOIN information_schema.key_column_usage kcu\n ON tc.constraint_name = kcu.constraint_name\n AND tc.table_schema = kcu.table_schema\n WHERE tc.constraint_type = 'PRIMARY KEY'\n AND tc.table_schema = cols.table_schema\n AND tc.table_name = cols.table_name\n AND kcu.column_name = cols.column_name\n ) AS is_primary_key\n FROM information_schema.columns cols\n LEFT JOIN pg_catalog.pg_namespace n ON n.nspname = cols.table_schema\n LEFT JOIN pg_catalog.pg_class c\n ON c.relname = cols.table_name\n AND c.relnamespace = n.oid\n AND c.relkind IN ('r', 'v', 'm')\n LEFT JOIN pg_catalog.pg_attribute attr\n ON attr.attrelid = c.oid\n AND attr.attname = cols.column_name\n LEFT JOIN pg_catalog.pg_description pgd\n ON pgd.objoid = attr.attrelid AND pgd.objsubid = attr.attnum\n WHERE cols.table_schema NOT IN ('pg_catalog', 'information_schema')\n ${filter}\n ORDER BY cols.table_schema, cols.table_name, cols.ordinal_position;`;\n}\n\n// Redshift's information_schema constraint views are very slow on large\n// clusters, so read the svv_* catalog views instead. Primary keys are\n// informational-only in Redshift and are not reported.\nconst REDSHIFT_EXCLUDED_SCHEMAS =\n\t\"('pg_catalog', 'information_schema', 'pg_internal', 'pg_automv')\";\n\nfunction buildRedshiftTablesQuery(tables: NormalizedTable[]): string {\n\tconst filter = buildFilterClause(tables, \"table_schema\", \"table_name\");\n\treturn `SELECT\n table_name,\n table_schema AS schema_name,\n table_type,\n remarks AS comment\n FROM svv_tables\n WHERE table_catalog = current_database()\n AND table_schema NOT IN ${REDSHIFT_EXCLUDED_SCHEMAS}\n AND table_schema NOT LIKE 'pg_temp%'\n ${filter}\n ORDER BY table_schema, table_name;`;\n}\n\nfunction buildRedshiftColumnsQuery(tables: NormalizedTable[]): string {\n\tconst filter = buildFilterClause(tables, \"table_schema\", \"table_name\");\n\treturn `SELECT\n table_name,\n table_schema,\n column_name,\n data_type,\n NULL AS udt_name,\n remarks AS description,\n FALSE AS is_primary_key\n FROM svv_columns\n WHERE table_catalog = current_database()\n AND table_schema NOT IN ${REDSHIFT_EXCLUDED_SCHEMAS}\n AND table_schema NOT LIKE 'pg_temp%'\n ${filter}\n ORDER BY table_schema, table_name, ordinal_position;`;\n}\n\n/**\n * WHERE fragment restricting introspection to the normalized allow-list.\n * Identifiers are validated by normalizeTableFilter (isSafeIdentifier), so\n * interpolating them is safe. `schema.*` entries match the whole schema.\n */\nexport function buildFilterClause(\n\ttables: NormalizedTable[],\n\tschemaExpr: string,\n\ttableExpr: string,\n): string {\n\tif (!tables.length) return \"\";\n\tconst wildcardSchemas = new Set(\n\t\ttables.filter((t) => t.table === WILDCARD).map((t) => t.schema),\n\t);\n\tconst clauses: string[] = [];\n\tfor (const schema of wildcardSchemas) {\n\t\tclauses.push(`(${schemaExpr} = '${schema}')`);\n\t}\n\tfor (const { schema, table } of tables) {\n\t\tif (table === WILDCARD || wildcardSchemas.has(schema)) continue;\n\t\tclauses.push(`(${schemaExpr} = '${schema}' AND ${tableExpr} = '${table}')`);\n\t}\n\treturn `AND (${clauses.join(\" OR \")})`;\n}\n\nfunction buildListSchemasQuery(): string {\n\treturn `SELECT n.nspname AS schema, COUNT(*) AS table_count\n FROM pg_class c\n JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE c.relkind IN ('r', 'v', 'm')\n AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')\n AND n.nspname NOT LIKE 'pg_temp%'\n AND n.nspname NOT LIKE 'pg_toast_temp%'\n GROUP BY n.nspname\n ORDER BY n.nspname;`;\n}\n\nfunction buildRedshiftListSchemasQuery(): string {\n\treturn `SELECT table_schema AS schema, COUNT(*) AS table_count\n FROM svv_tables\n WHERE table_catalog = current_database()\n AND table_schema NOT IN ${REDSHIFT_EXCLUDED_SCHEMAS}\n AND table_schema NOT LIKE 'pg_temp%'\n GROUP BY table_schema\n ORDER BY table_schema;`;\n}\n\n/** Escape LIKE/ILIKE metacharacters so search is a literal substring match. */\nexport function toLikeSubstringPattern(search: string): string {\n\treturn `%${search.replace(/[\\\\%_]/g, (ch) => `\\\\${ch}`)}%`;\n}\n\nexport function buildListTablesQuery(\n\tdialect: PostgresWireDialect,\n\toptions: ListTablesOptions,\n): { sql: string; params: unknown[] } {\n\tconst isRedshift = dialect === \"redshift\";\n\tconst schemaExpr = isRedshift ? \"table_schema\" : \"n.nspname\";\n\tconst tableExpr = isRedshift ? \"table_name\" : \"c.relname\";\n\tconst params: unknown[] = [];\n\tconst filters: string[] = [];\n\tif (options.schema) {\n\t\tparams.push(options.schema);\n\t\tfilters.push(`AND ${schemaExpr} = $${params.length}`);\n\t}\n\tconst search = options.search?.trim();\n\tif (search) {\n\t\tparams.push(toLikeSubstringPattern(search));\n\t\tfilters.push(`AND ${tableExpr} ILIKE $${params.length}`);\n\t}\n\t// Integer produced by resolveCatalogLimit, never raw input.\n\tconst limit = resolveCatalogLimit(options.limit);\n\tconst filterSql = filters.join(\"\\n \");\n\n\tconst sql = isRedshift\n\t\t? `SELECT table_schema AS schema, table_name AS name, table_type\n FROM svv_tables\n WHERE table_catalog = current_database()\n AND table_schema NOT IN ${REDSHIFT_EXCLUDED_SCHEMAS}\n AND table_schema NOT LIKE 'pg_temp%'\n ${filterSql}\n ORDER BY table_schema, table_name\n LIMIT ${limit};`\n\t\t: `SELECT n.nspname AS schema, c.relname AS name,\n CASE c.relkind\n WHEN 'v' THEN 'view'\n WHEN 'm' THEN 'materialized_view'\n ELSE 'table'\n END AS table_type\n FROM pg_class c\n JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE c.relkind IN ('r', 'v', 'm')\n AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')\n AND n.nspname NOT LIKE 'pg_temp%'\n AND n.nspname NOT LIKE 'pg_toast_temp%'\n ${filterSql}\n ORDER BY n.nspname, c.relname\n LIMIT ${limit};`;\n\n\treturn { sql, params };\n}\n\nfunction tableKey(schema: string, table: string): string {\n\treturn `${schema}.${table}`;\n}\n\nfunction isSafeIdentifier(value: string): boolean {\n\treturn /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);\n}\n\nfunction asTableType(value: string): CatalogTable[\"type\"] {\n\tconst normalized = value.toLowerCase();\n\tif (normalized.includes(\"view\")) {\n\t\treturn normalized.includes(\"materialized\") ? \"materialized_view\" : \"view\";\n\t}\n\treturn \"table\";\n}\n\nfunction sanitize(value: unknown): string | undefined {\n\tif (value === null || value === undefined) return undefined;\n\tconst trimmed = String(value).trim();\n\treturn trimmed.length ? trimmed : undefined;\n}\n","/**\n * Deep module: Hides JWT signing and HTTP complexity behind simple interface\n * Following Ousterhout's principle: \"Pull complexity downward\"\n */\n\nimport crypto from 'node:crypto';\nimport { QueryPipelineError, isQueryErrorCode } from '../errors';\n\n// Web Crypto API type declarations (available in Node.js 18+, Deno, and Bun)\n// Minimal type declaration for server-side use without DOM types\n// This matches the Web Crypto API CryptoKey interface\ninterface CryptoKey {\n\treadonly type: \"public\" | \"private\" | \"secret\";\n\treadonly extractable: boolean;\n\treadonly algorithm: { name: string };\n\treadonly usages: Array<\n\t\t| \"encrypt\"\n\t\t| \"decrypt\"\n\t\t| \"sign\"\n\t\t| \"verify\"\n\t\t| \"deriveKey\"\n\t\t| \"deriveBits\"\n\t\t| \"wrapKey\"\n\t\t| \"unwrapKey\"\n\t>;\n}\n\ntype FetchLike = (\n\tinput: Parameters<typeof fetch>[0],\n\tinit?: Parameters<typeof fetch>[1],\n) => ReturnType<typeof fetch>;\n\nexport class ApiClient {\n\tprivate readonly baseUrl: string;\n\tprivate readonly privateKey: string;\n\tprivate readonly organizationId: string;\n\tprivate readonly defaultTenantId?: string;\n\tprivate readonly additionalHeaders?: Record<string, string>;\n\tprivate readonly fetchImpl: FetchLike;\n\tprivate cryptoKey: CryptoKey | null = null;\n\n\tconstructor(\n\t\tbaseUrl: string,\n\t\tprivateKey: string,\n\t\torganizationId: string,\n\t\toptions?: {\n\t\t\tdefaultTenantId?: string;\n\t\t\tadditionalHeaders?: Record<string, string>;\n\t\t\tfetch?: FetchLike;\n\t\t},\n\t) {\n\t\tif (!baseUrl) {\n\t\t\tthrow new Error(\"Base URL is required\");\n\t\t}\n\t\tif (!privateKey) {\n\t\t\tthrow new Error(\"Private key is required\");\n\t\t}\n\t\tif (!organizationId) {\n\t\t\tthrow new Error(\"Organization ID is required\");\n\t\t}\n\n\t\tthis.baseUrl = baseUrl.replace(/\\/+$/, \"\");\n\t\tthis.privateKey = privateKey;\n\t\tthis.organizationId = organizationId;\n\t\tthis.defaultTenantId = options?.defaultTenantId;\n\t\tthis.additionalHeaders = options?.additionalHeaders;\n\t\tthis.fetchImpl = options?.fetch ?? globalThis.fetch;\n\n\t\tif (!this.fetchImpl) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Fetch implementation not found. Provide options.fetch or use Node 18+.\",\n\t\t\t);\n\t\t}\n\t}\n\n\tgetDefaultTenantId(): string | undefined {\n\t\treturn this.defaultTenantId;\n\t}\n\n\t/**\n\t * Create a JWT for the given tenant (and optional userId, scopes).\n\t * Use this when you need to pass a token to the embed (e.g. frontend or demo).\n\t */\n\tasync createJwt(\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t): Promise<string> {\n\t\treturn await this.generateJWT(tenantId, userId, scopes);\n\t}\n\n\tasync get<T>(\n\t\tpath: string,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\treturn await this.request<T>(path, {\n\t\t\tmethod: \"GET\",\n\t\t\theaders: await this.buildHeaders(\n\t\t\t\ttenantId,\n\t\t\t\tuserId,\n\t\t\t\tscopes,\n\t\t\t\tfalse,\n\t\t\t\tsessionId,\n\t\t\t),\n\t\t\tsignal,\n\t\t});\n\t}\n\n\tasync post<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\treturn await this.request<T>(path, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: await this.buildHeaders(\n\t\t\t\ttenantId,\n\t\t\t\tuserId,\n\t\t\t\tscopes,\n\t\t\t\ttrue,\n\t\t\t\tsessionId,\n\t\t\t),\n\t\t\tbody: JSON.stringify(body ?? {}),\n\t\t\tsignal,\n\t\t});\n\t}\n\n\tasync postWithHeaders<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<{ data: T; headers: Headers }> {\n\t\tconst response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: await this.buildHeaders(\n\t\t\t\ttenantId,\n\t\t\t\tuserId,\n\t\t\t\tscopes,\n\t\t\t\ttrue,\n\t\t\t\tsessionId,\n\t\t\t),\n\t\t\tbody: JSON.stringify(body ?? {}),\n\t\t\tsignal,\n\t\t});\n\t\tconst data = await this.parseResponse<T>(response);\n\t\treturn { data, headers: response.headers };\n\t}\n\n\tasync put<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\treturn await this.request<T>(path, {\n\t\t\tmethod: \"PUT\",\n\t\t\theaders: await this.buildHeaders(\n\t\t\t\ttenantId,\n\t\t\t\tuserId,\n\t\t\t\tscopes,\n\t\t\t\ttrue,\n\t\t\t\tsessionId,\n\t\t\t),\n\t\t\tbody: JSON.stringify(body ?? {}),\n\t\t\tsignal,\n\t\t});\n\t}\n\n\tasync patch<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\treturn await this.request<T>(path, {\n\t\t\tmethod: \"PATCH\",\n\t\t\theaders: await this.buildHeaders(\n\t\t\t\ttenantId,\n\t\t\t\tuserId,\n\t\t\t\tscopes,\n\t\t\t\ttrue,\n\t\t\t\tsessionId,\n\t\t\t),\n\t\t\tbody: JSON.stringify(body ?? {}),\n\t\t\tsignal,\n\t\t});\n\t}\n\n\tasync delete<T = void>(\n\t\tpath: string,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\treturn await this.request<T>(path, {\n\t\t\tmethod: \"DELETE\",\n\t\t\theaders: await this.buildHeaders(\n\t\t\t\ttenantId,\n\t\t\t\tuserId,\n\t\t\t\tscopes,\n\t\t\t\tfalse,\n\t\t\t\tsessionId,\n\t\t\t),\n\t\t\tsignal,\n\t\t});\n\t}\n\n\tprivate async request<T>(path: string, init: RequestInit): Promise<T> {\n\t\tconst response = await this.fetchImpl(`${this.baseUrl}${path}`, init);\n\t\treturn await this.parseResponse<T>(response);\n\t}\n\n\tprivate async parseResponse<T>(response: Response): Promise<T> {\n\t\tconst text = await response.text();\n\t\tlet json: any;\n\t\ttry {\n\t\t\tjson = text ? JSON.parse(text) : undefined;\n\t\t} catch {\n\t\t\tjson = undefined;\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tconst message = errorMessageFromBody(json, response);\n\t\t\tif (isQueryErrorCode(json?.code)) {\n\t\t\t\tthrow new QueryPipelineError(\n\t\t\t\t\tmessage,\n\t\t\t\t\tjson.code,\n\t\t\t\t\tjson?.details,\n\t\t\t\t\tresponse.status,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst error = new Error(message);\n\t\t\t(error as any).status = response.status;\n\t\t\tif (json?.code) (error as any).code = json.code;\n\t\t\tif (json?.details) (error as any).details = json.details;\n\t\t\tthrow error;\n\t\t}\n\n\t\treturn json as T;\n\t}\n\n\tprivate async buildHeaders(\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tincludeJson: boolean = true,\n\t\tsessionId?: string,\n\t): Promise<Record<string, string>> {\n\t\tconst token = await this.generateJWT(tenantId, userId, scopes);\n\t\tconst headers: Record<string, string> = {\n\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\tAccept: \"application/json\",\n\t\t};\n\t\tif (includeJson) {\n\t\t\theaders[\"Content-Type\"] = \"application/json\";\n\t\t}\n\t\tif (sessionId) {\n\t\t\theaders[\"x-session-id\"] = sessionId;\n\t\t}\n\t\tif (this.additionalHeaders) {\n\t\t\tObject.assign(headers, this.additionalHeaders);\n\t\t}\n\t\treturn headers;\n\t}\n\n\t/**\n\t * Base64URL encode a string (works in both Node.js 18+ and Deno)\n\t */\n\tprivate base64UrlEncode(str: string): string {\n\t\t// Convert string to bytes\n\t\tconst bytes = new TextEncoder().encode(str);\n\n\t\t// btoa is available in both Node.js 18+ and Deno\n\t\t// Convert bytes to binary string efficiently (handle large arrays)\n\t\tlet binary = \"\";\n\t\tconst chunkSize = 8192; // Process in chunks to avoid stack overflow\n\t\tfor (let i = 0; i < bytes.length; i += chunkSize) {\n\t\t\tconst chunk = bytes.slice(i, i + chunkSize);\n\t\t\tbinary += String.fromCharCode(...chunk);\n\t\t}\n\n\t\tconst base64 = btoa(binary);\n\n\t\t// Convert to base64url: replace non-url chars and strip padding\n\t\treturn base64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/g, \"\");\n\t}\n\n\t/**\n\t * Base64URL encode from Uint8Array (for binary data like signatures)\n\t */\n\tprivate base64UrlEncodeBytes(bytes: Uint8Array): string {\n\t\t// Convert bytes to binary string efficiently (handle large arrays)\n\t\tlet binary = \"\";\n\t\tconst chunkSize = 8192; // Process in chunks to avoid stack overflow\n\t\tfor (let i = 0; i < bytes.length; i += chunkSize) {\n\t\t\tconst chunk = bytes.slice(i, i + chunkSize);\n\t\t\tbinary += String.fromCharCode(...chunk);\n\t\t}\n\n\t\tconst base64 = btoa(binary);\n\n\t\t// Convert to base64url: replace non-url chars and strip padding\n\t\treturn base64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/g, \"\");\n\t}\n\n\t/**\n\t * Import the private key into Web Crypto API format (cached after first import)\n\t */\n\tprivate async getCryptoKey(): Promise<CryptoKey> {\n\t\tif (this.cryptoKey) {\n\t\t\treturn this.cryptoKey;\n\t\t}\n\n\t\t// Import the private key for Web Crypto API\n\t\t// Works in both Node.js 18+ and Deno\n\t\tthis.cryptoKey = await crypto.subtle.importKey(\n\t\t\t\"pkcs8\",\n\t\t\tthis.privateKeyToArrayBuffer(this.privateKey),\n\t\t\t{\n\t\t\t\tname: \"RSASSA-PKCS1-v1_5\",\n\t\t\t\thash: \"SHA-256\",\n\t\t\t},\n\t\t\tfalse,\n\t\t\t[\"sign\"],\n\t\t);\n\n\t\treturn this.cryptoKey;\n\t}\n\n\t/**\n\t * Convert PEM private key to ArrayBuffer for Web Crypto API\n\t */\n\tprivate privateKeyToArrayBuffer(pem: string): ArrayBuffer {\n\t\t// Remove PEM headers and whitespace\n\t\tconst pemHeader = \"-----BEGIN PRIVATE KEY-----\";\n\t\tconst pemFooter = \"-----END PRIVATE KEY-----\";\n\t\tconst pemContents = pem\n\t\t\t.replace(pemHeader, \"\")\n\t\t\t.replace(pemFooter, \"\")\n\t\t\t.replace(/\\s/g, \"\");\n\n\t\t// Decode base64 to binary string, then to ArrayBuffer\n\t\tconst binaryString = atob(pemContents);\n\t\tconst bytes = new Uint8Array(binaryString.length);\n\t\tfor (let i = 0; i < binaryString.length; i++) {\n\t\t\tbytes[i] = binaryString.charCodeAt(i);\n\t\t}\n\t\treturn bytes.buffer;\n\t}\n\n\tprivate async generateJWT(\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t): Promise<string> {\n\t\tconst header = {\n\t\t\talg: \"RS256\",\n\t\t\ttyp: \"JWT\",\n\t\t};\n\n\t\tconst payload: Record<string, unknown> = {\n\t\t\torganizationId: this.organizationId,\n\t\t\ttenantId,\n\t\t};\n\n\t\tif (userId) payload.userId = userId;\n\t\tif (scopes?.length) payload.scopes = scopes;\n\n\t\tconst encodedHeader = this.base64UrlEncode(JSON.stringify(header));\n\t\tconst encodedPayload = this.base64UrlEncode(JSON.stringify(payload));\n\t\tconst data = `${encodedHeader}.${encodedPayload}`;\n\n\t\t// Sign using Web Crypto API (works in both Node.js 18+ and Deno)\n\t\tconst key = await this.getCryptoKey();\n\t\tconst dataBytes = new TextEncoder().encode(data);\n\t\tconst signature = await crypto.subtle.sign(\n\t\t\t{\n\t\t\t\tname: \"RSASSA-PKCS1-v1_5\",\n\t\t\t},\n\t\t\tkey,\n\t\t\tdataBytes,\n\t\t);\n\n\t\t// Convert signature ArrayBuffer to base64url\n\t\tconst signatureBytes = new Uint8Array(signature);\n\t\tconst encodedSignature = this.base64UrlEncodeBytes(signatureBytes);\n\n\t\treturn `${data}.${encodedSignature}`;\n\t}\n}\n\n/**\n * QueryPanel API errors are `{ error: string }`; platform errors (e.g. Vercel's 413 for\n * oversized bodies) are `{ error: { code, message } }`. Never let an object become the message.\n */\nfunction errorMessageFromBody(json: any, response: Response): string {\n\tconst error = json?.error;\n\tif (typeof error === \"string\" && error) return error;\n\tif (error && typeof error === \"object\") {\n\t\tconst nested = error.message ?? error.code;\n\t\tif (typeof nested === \"string\" && nested) {\n\t\t\treturn `${nested} (HTTP ${response.status})`;\n\t\t}\n\t}\n\treturn response.statusText || `Request failed (HTTP ${response.status})`;\n}\n","/**\n * Error codes for the query pipeline\n * These match the server-side error codes returned in API responses\n */\nexport const QueryErrorCode = {\n\t// Moderation errors\n\tMODERATION_FAILED: \"MODERATION_FAILED\",\n\n\t// Guardrail errors\n\tRELEVANCE_CHECK_FAILED: \"RELEVANCE_CHECK_FAILED\",\n\tSECURITY_CHECK_FAILED: \"SECURITY_CHECK_FAILED\",\n\n\t// SQL generation errors\n\tSQL_GENERATION_FAILED: \"SQL_GENERATION_FAILED\",\n\n\t// SQL validation errors\n\tSQL_VALIDATION_FAILED: \"SQL_VALIDATION_FAILED\",\n\n\t// Context retrieval errors\n\tCONTEXT_RETRIEVAL_FAILED: \"CONTEXT_RETRIEVAL_FAILED\",\n\n\t// Clarification errors (v2)\n\tCLARIFICATION_NEEDED: \"CLARIFICATION_NEEDED\",\n\n\t// Bring-your-own-key / provider errors\n\tAI_PROVIDER_AUTH_FAILED: \"AI_PROVIDER_AUTH_FAILED\",\n\tAI_PROVIDER_RATE_LIMITED: \"AI_PROVIDER_RATE_LIMITED\",\n\tAI_PROVIDER_UNAVAILABLE: \"AI_PROVIDER_UNAVAILABLE\",\n\n\t// General errors\n\tINTERNAL_ERROR: \"INTERNAL_ERROR\",\n\tAUTHENTICATION_REQUIRED: \"AUTHENTICATION_REQUIRED\",\n\tVALIDATION_ERROR: \"VALIDATION_ERROR\",\n} as const;\n\nexport type QueryErrorCode =\n\t(typeof QueryErrorCode)[keyof typeof QueryErrorCode];\n\nexport function isQueryErrorCode(code: unknown): code is QueryErrorCode {\n\treturn (\n\t\ttypeof code === \"string\" &&\n\t\t(Object.values(QueryErrorCode) as string[]).includes(code)\n\t);\n}\n\nexport function isAiProviderErrorCode(code: unknown): boolean {\n\treturn (\n\t\tcode === QueryErrorCode.AI_PROVIDER_AUTH_FAILED ||\n\t\tcode === QueryErrorCode.AI_PROVIDER_RATE_LIMITED ||\n\t\tcode === QueryErrorCode.AI_PROVIDER_UNAVAILABLE\n\t);\n}\n\nexport function isAiProviderPipelineError(error: unknown): boolean {\n\tif (error instanceof QueryPipelineError) {\n\t\treturn isAiProviderErrorCode(error.code);\n\t}\n\tif (error && typeof error === \"object\" && \"code\" in error) {\n\t\treturn isAiProviderErrorCode((error as { code: unknown }).code);\n\t}\n\treturn false;\n}\n\nexport function throwIfGenerationErrorResponse(response: unknown): void {\n\tif (!response || typeof response !== \"object\") {\n\t\treturn;\n\t}\n\tconst record = response as Record<string, unknown>;\n\tif (typeof record.error !== \"string\") {\n\t\treturn;\n\t}\n\tif (isQueryErrorCode(record.code)) {\n\t\tthrow new QueryPipelineError(record.error, record.code);\n\t}\n\tthrow new Error(record.error);\n}\n\n/**\n * Error thrown when the query pipeline fails\n */\nexport class QueryPipelineError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly code: QueryErrorCode,\n\t\tpublic readonly details?: Record<string, unknown>,\n\t\tpublic readonly status?: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"QueryPipelineError\";\n\t}\n\n\t/**\n\t * Check if this is a moderation error\n\t */\n\tisModeration(): boolean {\n\t\treturn this.code === QueryErrorCode.MODERATION_FAILED;\n\t}\n\n\t/**\n\t * Check if this is a relevance error (question not related to database)\n\t */\n\tisRelevanceError(): boolean {\n\t\treturn this.code === QueryErrorCode.RELEVANCE_CHECK_FAILED;\n\t}\n\n\t/**\n\t * Check if this is a security error (SQL injection, prompt injection, etc.)\n\t */\n\tisSecurityError(): boolean {\n\t\treturn this.code === QueryErrorCode.SECURITY_CHECK_FAILED;\n\t}\n\n\t/**\n\t * Check if this is any guardrail error (relevance or security)\n\t */\n\tisGuardrailError(): boolean {\n\t\treturn this.isRelevanceError() || this.isSecurityError();\n\t}\n\n\t/**\n\t * Check if this is a clarification needed error (v2 pipeline)\n\t */\n\tisClarificationNeeded(): boolean {\n\t\treturn this.code === QueryErrorCode.CLARIFICATION_NEEDED;\n\t}\n\n\t/**\n\t * Check if this is a BYOK / provider credential error\n\t */\n\tisAiProviderAuthError(): boolean {\n\t\treturn this.code === QueryErrorCode.AI_PROVIDER_AUTH_FAILED;\n\t}\n\n\t/**\n\t * Check if this is a BYOK / provider rate-limit error\n\t */\n\tisAiProviderRateLimited(): boolean {\n\t\treturn this.code === QueryErrorCode.AI_PROVIDER_RATE_LIMITED;\n\t}\n\n\t/**\n\t * Check if this is any BYOK / provider error (auth, rate limit, or unavailable)\n\t */\n\tisAiProviderError(): boolean {\n\t\treturn isAiProviderErrorCode(this.code);\n\t}\n}\n\n/**\n * Thrown by stageSchemaIngest / syncSchemaInBatches when the introspected schema\n * has more columns than `maxColumns`. Raised before anything is uploaded; narrow\n * the sync with `allowedTables` / `tables` (e.g. `schema.*`) and retry.\n */\nexport class SchemaTooLargeError extends Error {\n\tconstructor(\n\t\tpublic readonly tableCount: number,\n\t\tpublic readonly columnCount: number,\n\t\tpublic readonly maxColumns: number,\n\t) {\n\t\tsuper(\n\t\t\t`Schema too large to sync: ${columnCount} columns across ${tableCount} tables exceeds the limit of ${maxColumns} columns. Restrict the tables to sync (e.g. allowedTables with \"schema.*\" or specific tables).`,\n\t\t);\n\t\tthis.name = \"SchemaTooLargeError\";\n\t}\n}\n","import type {\n\tIQueryPanelApi,\n\tRequestHandler,\n\tRequestHandlerOptions,\n} from \"./api-types\";\n\n/**\n * API client that delegates every request to a callback instead of HTTP.\n * Use this when running the SDK inside your own API so that \"API\" calls are handled in-process (e.g. by calling querypanel-sdk services directly) and do not cause recursion.\n */\nexport class CallbackApiClient implements IQueryPanelApi {\n\tprivate readonly requestHandler: RequestHandler;\n\tprivate readonly defaultTenantId?: string;\n\n\tconstructor(\n\t\trequestHandler: RequestHandler,\n\t\toptions?: { defaultTenantId?: string },\n\t) {\n\t\tthis.requestHandler = requestHandler;\n\t\tthis.defaultTenantId = options?.defaultTenantId;\n\t}\n\n\tgetDefaultTenantId(): string | undefined {\n\t\treturn this.defaultTenantId;\n\t}\n\n\tprivate async request<T>(\n\t\topts: Omit<RequestHandlerOptions, \"method\" | \"body\"> & {\n\t\t\tmethod: RequestHandlerOptions[\"method\"];\n\t\t\tbody?: unknown;\n\t\t},\n\t): Promise<{ data: T; headers?: Headers }> {\n\t\tconst { data, headers } = await this.requestHandler({\n\t\t\tmethod: opts.method,\n\t\t\tpath: opts.path,\n\t\t\tbody: opts.body,\n\t\t\ttenantId: opts.tenantId,\n\t\t\tuserId: opts.userId,\n\t\t\tscopes: opts.scopes,\n\t\t\tsignal: opts.signal,\n\t\t\tsessionId: opts.sessionId,\n\t\t});\n\t\treturn { data: data as T, headers };\n\t}\n\n\tasync get<T>(\n\t\tpath: string,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\tconst { data } = await this.request<T>({\n\t\t\tmethod: \"GET\",\n\t\t\tpath,\n\t\t\ttenantId,\n\t\t\tuserId,\n\t\t\tscopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t});\n\t\treturn data;\n\t}\n\n\tasync post<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\tconst { data } = await this.request<T>({\n\t\t\tmethod: \"POST\",\n\t\t\tpath,\n\t\t\tbody,\n\t\t\ttenantId,\n\t\t\tuserId,\n\t\t\tscopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t});\n\t\treturn data;\n\t}\n\n\tasync postWithHeaders<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<{ data: T; headers: Headers }> {\n\t\tconst { data, headers } = await this.request<T>({\n\t\t\tmethod: \"POST\",\n\t\t\tpath,\n\t\t\tbody,\n\t\t\ttenantId,\n\t\t\tuserId,\n\t\t\tscopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t});\n\t\treturn {\n\t\t\tdata,\n\t\t\theaders: headers ?? new Headers(),\n\t\t};\n\t}\n\n\tasync put<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\tconst { data } = await this.request<T>({\n\t\t\tmethod: \"PUT\",\n\t\t\tpath,\n\t\t\tbody,\n\t\t\ttenantId,\n\t\t\tuserId,\n\t\t\tscopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t});\n\t\treturn data;\n\t}\n\n\tasync patch<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\tconst { data } = await this.request<T>({\n\t\t\tmethod: \"PATCH\",\n\t\t\tpath,\n\t\t\tbody,\n\t\t\ttenantId,\n\t\t\tuserId,\n\t\t\tscopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t});\n\t\treturn data;\n\t}\n\n\tasync delete<T = void>(\n\t\tpath: string,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\tconst { data } = await this.request<T>({\n\t\t\tmethod: \"DELETE\",\n\t\t\tpath,\n\t\t\ttenantId,\n\t\t\tuserId,\n\t\t\tscopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t});\n\t\treturn data;\n\t}\n}\n","import type { ParamRecord, ParamValue } from \"./param-types\";\n\n/**\n * Maps v2-style param metadata (array order = $1, $2, $3 in SQL) to a ParamRecord.\n * Numeric keys preserve placeholder order for PostgreSQL; named keys support tenant lookups.\n */\nexport function mapGeneratedParams(\n\tparams: Array<Record<string, unknown>>,\n): ParamRecord {\n\tconst record: ParamRecord = {};\n\n\tparams.forEach((param, index) => {\n\t\tconst value = param.value as ParamValue | undefined;\n\t\tif (value === undefined) {\n\t\t\treturn;\n\t\t}\n\n\t\trecord[String(index + 1)] = value;\n\n\t\tconst nameCandidate =\n\t\t\t(typeof param.name === \"string\" && param.name.trim()) ||\n\t\t\t(typeof param.placeholder === \"string\" && param.placeholder.trim()) ||\n\t\t\t(typeof param.position === \"number\" && String(param.position)) ||\n\t\t\tString(index + 1);\n\t\tconst key = nameCandidate.replace(/[{}:$]/g, \"\").trim();\n\t\trecord[key] = value;\n\t});\n\n\treturn record;\n}\n\n/**\n * Normalizes run-sql / dashboard request params into a ParamRecord.\n */\nexport function normalizeSqlParamsInput(\n\tparams: unknown[] | Record<string, unknown> | null | undefined,\n): ParamRecord {\n\tif (params == null) {\n\t\treturn {};\n\t}\n\n\tif (Array.isArray(params)) {\n\t\tif (params.length === 0) {\n\t\t\treturn {};\n\t\t}\n\n\t\tconst first = params[0];\n\t\tif (first && typeof first === \"object\" && \"value\" in (first as object)) {\n\t\t\treturn mapGeneratedParams(params as Array<Record<string, unknown>>);\n\t\t}\n\n\t\tconst positional: ParamRecord = {};\n\t\tparams.forEach((value, index) => {\n\t\t\tpositional[String(index + 1)] = value as ParamValue;\n\t\t});\n\t\treturn positional;\n\t}\n\n\tif (typeof params === \"object\") {\n\t\treturn params as ParamRecord;\n\t}\n\n\treturn {};\n}\n","import type { DatabaseAdapter, DatabaseDialect } from \"../adapters/types\";\nimport { mapGeneratedParams as mapGeneratedParamsFromMetadata } from \"./map-generated-params\";\nimport type { ParamRecord } from \"./param-types\";\n\nexport type { ParamRecord, ParamValue } from \"./param-types\";\n\nexport interface DatabaseMetadata {\n\tname: string;\n\tdialect: DatabaseDialect;\n\tdescription?: string;\n\ttags?: string[];\n\ttenantFieldName?: string;\n\ttenantFieldType?: string;\n\tenforceTenantIsolation?: boolean;\n}\n\nexport interface DatabaseExecutionResult {\n\trows: Array<Record<string, unknown>>;\n\tfields: string[];\n}\n\n/**\n * Deep module: Hides SQL execution complexity and tenant isolation logic\n * Following Ousterhout's principle: \"Information hiding\"\n */\nexport class QueryEngine {\n\tprivate databases = new Map<string, DatabaseAdapter>();\n\tprivate databaseMetadata = new Map<string, DatabaseMetadata>();\n\tprivate defaultDatabase?: string;\n\n\tattachDatabase(\n\t\tname: string,\n\t\tadapter: DatabaseAdapter,\n\t\tmetadata: DatabaseMetadata,\n\t): void {\n\t\tthis.databases.set(name, adapter);\n\t\tthis.databaseMetadata.set(name, metadata);\n\t\tif (!this.defaultDatabase) {\n\t\t\tthis.defaultDatabase = name;\n\t\t}\n\t}\n\n\tgetDatabase(name?: string): DatabaseAdapter {\n\t\tconst dbName = name ?? this.defaultDatabase;\n\t\tif (!dbName) {\n\t\t\tthrow new Error(\"No database attached.\");\n\t\t}\n\t\tconst adapter = this.databases.get(dbName);\n\t\tif (!adapter) {\n\t\t\tthrow new Error(\n\t\t\t\t`Database '${dbName}' not found. Attached: ${Array.from(\n\t\t\t\t\tthis.databases.keys(),\n\t\t\t\t).join(\", \")}`,\n\t\t\t);\n\t\t}\n\t\treturn adapter;\n\t}\n\n\tgetDatabaseMetadata(name?: string): DatabaseMetadata | undefined {\n\t\tconst dbName = name ?? this.defaultDatabase;\n\t\tif (!dbName) return undefined;\n\t\treturn this.databaseMetadata.get(dbName);\n\t}\n\n\tgetDefaultDatabase(): string | undefined {\n\t\treturn this.defaultDatabase;\n\t}\n\n\tasync validateAndExecute(\n\t\tsql: string,\n\t\tparams: ParamRecord,\n\t\tdatabaseName: string,\n\t\ttenantId: string,\n\t): Promise<DatabaseExecutionResult> {\n\t\tconst adapter = this.getDatabase(databaseName);\n\t\tconst metadata = this.getDatabaseMetadata(databaseName);\n\n\t\t// Apply tenant isolation if configured\n\t\tlet finalSql = sql;\n\t\tif (metadata) {\n\t\t\tfinalSql = this.ensureTenantIsolation(sql, params, metadata, tenantId);\n\t\t}\n\n\t\t// Validate SQL\n\t\tawait adapter.validate(finalSql, params);\n\n\t\t// Execute\n\t\tconst result = await adapter.execute(finalSql, params);\n\t\treturn {\n\t\t\trows: result.rows,\n\t\t\tfields: result.fields,\n\t\t};\n\t}\n\n\tasync execute(\n\t\tsql: string,\n\t\tparams: ParamRecord | undefined,\n\t\tdatabaseName?: string,\n\t): Promise<Array<Record<string, unknown>>> {\n\t\ttry {\n\t\t\tconst adapter = this.getDatabase(databaseName);\n\t\t\tconst result = await adapter.execute(sql, params);\n\t\t\treturn result.rows;\n\t\t} catch (error) {\n\t\t\tconsole.warn(\n\t\t\t\t`Failed to execute SQL locally for database '${databaseName}':`,\n\t\t\t\terror,\n\t\t\t);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Maps API params (array in SQL placeholder order $1, $2, $3) to ParamRecord.\n\t * Sets both numeric keys (\"1\", \"2\", \"3\") and named keys so that:\n\t * - Postgres adapter uses numeric order for positional params (matches SQL).\n\t * - Named keys remain for tenant isolation / other lookups.\n\t */\n\tmapGeneratedParams(params: Array<Record<string, unknown>>): ParamRecord {\n\t\treturn mapGeneratedParamsFromMetadata(params);\n\t}\n\n\tprivate ensureTenantIsolation(\n\t\tsql: string,\n\t\tparams: ParamRecord,\n\t\tmetadata: DatabaseMetadata,\n\t\ttenantId: string,\n\t): string {\n\t\tif (\n\t\t\t!metadata.tenantFieldName ||\n\t\t\tmetadata.enforceTenantIsolation === false\n\t\t) {\n\t\t\treturn sql;\n\t\t}\n\n\t\tconst tenantField = metadata.tenantFieldName;\n\t\tconst normalizedSql = sql.toLowerCase();\n\t\tif (normalizedSql.includes(tenantField.toLowerCase())) {\n\t\t\treturn sql;\n\t\t}\n\n\t\tlet tenantPredicate: string;\n\n\t\tif (metadata.dialect === \"clickhouse\") {\n\t\t\t// ClickHouse supports named parameters natively\n\t\t\tconst paramKey = tenantField;\n\t\t\tparams[paramKey] = tenantId;\n\t\t\ttenantPredicate = `${tenantField} = {${tenantField}:${metadata.tenantFieldType ?? \"String\"}}`;\n\t\t} else if (metadata.dialect === \"bigquery\") {\n\t\t\t// BigQuery uses @paramName\n\t\t\tparams[tenantField] = tenantId;\n\t\t\ttenantPredicate = `${tenantField} = @${tenantField}`;\n\t\t} else {\n\t\t\tconst paramIndex = this.nextPostgresPlaceholderIndex(sql, params);\n\t\t\tparams[String(paramIndex)] = tenantId;\n\t\t\tparams[tenantField] = tenantId;\n\t\t\ttenantPredicate = `${tenantField} = $${paramIndex}`;\n\t\t}\n\n\t\tif (/\\bwhere\\b/i.test(sql)) {\n\t\t\treturn sql.replace(\n\t\t\t\t/\\bwhere\\b/i,\n\t\t\t\t(match) => `${match} ${tenantPredicate} AND `,\n\t\t\t);\n\t\t}\n\n\t\treturn `${sql} WHERE ${tenantPredicate}`;\n\t}\n\n\tprivate nextPostgresPlaceholderIndex(\n\t\tsql: string,\n\t\tparams: ParamRecord,\n\t): number {\n\t\tlet max = 0;\n\t\tfor (const match of sql.matchAll(/\\$(\\d+)\\b/g)) {\n\t\t\tconst index = Number.parseInt(match[1], 10);\n\t\t\tif (Number.isFinite(index) && index > max) {\n\t\t\t\tmax = index;\n\t\t\t}\n\t\t}\n\n\t\tif (max > 0) {\n\t\t\treturn max + 1;\n\t\t}\n\n\t\tconst numericKeys = Object.keys(params)\n\t\t\t.filter((key) => /^\\d+$/.test(key))\n\t\t\t.map((key) => Number.parseInt(key, 10))\n\t\t\t.filter((index) => Number.isFinite(index));\n\n\t\tif (numericKeys.length > 0) {\n\t\t\treturn Math.max(...numericKeys) + 1;\n\t\t}\n\n\t\treturn 1;\n\t}\n}\n","import type { IQueryPanelApi } from \"../core/api-types\";\nimport type { ParamRecord, QueryEngine } from \"../core/query-engine\";\n\nexport interface SdkChart {\n\tid: string;\n\ttitle: string;\n\tprompt?: string | null;\n\tdescription: string | null;\n\tsql: string;\n\tsql_params: Record<string, unknown> | null;\n\tvega_lite_spec: Record<string, unknown>;\n\tspec_type?: 'vega-lite' | 'vizspec'; // Type discriminator for spec format\n\tquery_id: string | null;\n\torganization_id: string | null;\n\ttenant_id: string | null;\n\tuser_id: string | null;\n\tcreated_at: string | null;\n\tupdated_at: string | null;\n\tactive?: boolean;\n\ttarget_db?: string | null;\n}\n\nexport interface ChartCreateInput {\n\ttitle: string;\n\tprompt?: string;\n\tdescription?: string;\n\t/**\n\t * Required when `query_id` is not provided. Ignored (with `sql_params`) when\n\t * `query_id` is present: the server saves the SQL and params it stored for that query.\n\t */\n\tsql?: string;\n\tsql_params?: Record<string, unknown>;\n\tvega_lite_spec: Record<string, unknown>;\n\tspec_type?: 'vega-lite' | 'vizspec'; // Defaults to 'vega-lite' if not specified\n\t/** Server-issued query id from ask() / modifyChart(). Preferred over `sql`. */\n\tquery_id?: string;\n\ttarget_db?: string;\n}\n\nexport interface ChartUpdateInput {\n\ttitle?: string;\n\tprompt?: string;\n\tdescription?: string;\n\tsql?: string;\n\tsql_params?: Record<string, unknown>;\n\t/** When present, the server replaces sql / sql_params with the stored query. */\n\tquery_id?: string;\n\tvega_lite_spec?: Record<string, unknown>;\n\tspec_type?: 'vega-lite' | 'vizspec';\n\ttarget_db?: string;\n}\n\nexport interface PaginationQuery {\n\tpage?: number;\n\tlimit?: number;\n}\n\nexport interface PaginationInfo {\n\tpage: number;\n\tlimit: number;\n\ttotal: number;\n\ttotalPages: number;\n\thasNext: boolean;\n\thasPrev: boolean;\n}\n\nexport interface PaginatedResponse<T> {\n\tdata: T[];\n\tpagination: PaginationInfo;\n}\n\nexport interface ChartListOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n\tpagination?: PaginationQuery;\n\tsortBy?: \"title\" | \"user_id\" | \"created_at\" | \"updated_at\";\n\tsortDir?: \"asc\" | \"desc\";\n\ttitle?: string;\n\tuserFilter?: string;\n\tcreatedFrom?: string;\n\tcreatedTo?: string;\n\tupdatedFrom?: string;\n\tupdatedTo?: string;\n\tincludeData?: boolean;\n}\n\n/** Options for bulk chart fetch (`GET /charts/bulk`) — no pagination. */\nexport type ChartListAllOptions = Omit<ChartListOptions, \"pagination\">;\n\nexport interface ChartBulkGetResponse {\n\tdata: SdkChart[];\n\tmissingIds: string[];\n}\n\ninterface RequestOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n}\n\n/**\n * Route module for Chart CRUD operations\n * Simple pass-through to backend with optional data hydration\n */\nexport async function createChart(\n\tclient: IQueryPanelApi,\n\tbody: ChartCreateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkChart> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.post<SdkChart>(\n\t\t\"/charts\",\n\t\tbody,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\nexport async function listCharts(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\toptions?: ChartListOptions,\n\tsignal?: AbortSignal,\n): Promise<PaginatedResponse<SdkChart>> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\tif (options?.pagination?.page)\n\t\tparams.set(\"page\", `${options.pagination.page}`);\n\tif (options?.pagination?.limit)\n\t\tparams.set(\"limit\", `${options.pagination.limit}`);\n\tif (options?.sortBy) params.set(\"sort_by\", options.sortBy);\n\tif (options?.sortDir) params.set(\"sort_dir\", options.sortDir);\n\tif (options?.title) params.set(\"title\", options.title);\n\tif (options?.userFilter) params.set(\"user_id\", options.userFilter);\n\tif (options?.createdFrom) params.set(\"created_from\", options.createdFrom);\n\tif (options?.createdTo) params.set(\"created_to\", options.createdTo);\n\tif (options?.updatedFrom) params.set(\"updated_from\", options.updatedFrom);\n\tif (options?.updatedTo) params.set(\"updated_to\", options.updatedTo);\n\n\tconst response = await client.get<PaginatedResponse<SdkChart>>(\n\t\t`/charts${params.toString() ? `?${params.toString()}` : \"\"}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tif (options?.includeData) {\n\t\tresponse.data = await Promise.all(\n\t\t\tresponse.data.map(async (chart) => {\n\t\t\t\tconst rows = await executeChartQuery(queryEngine, chart, tenantId);\n\t\t\t\treturn hydrateChartWithData(chart, rows);\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn response;\n}\n\nexport async function listAllCharts(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\toptions?: ChartListOptions,\n\tsignal?: AbortSignal,\n): Promise<PaginatedResponse<SdkChart>> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\tif (options?.pagination?.page)\n\t\tparams.set(\"page\", `${options.pagination.page}`);\n\tif (options?.pagination?.limit)\n\t\tparams.set(\"limit\", `${options.pagination.limit}`);\n\tif (options?.sortBy) params.set(\"sort_by\", options.sortBy);\n\tif (options?.sortDir) params.set(\"sort_dir\", options.sortDir);\n\tif (options?.title) params.set(\"title\", options.title);\n\tif (options?.userFilter) params.set(\"user_id\", options.userFilter);\n\tif (options?.createdFrom) params.set(\"created_from\", options.createdFrom);\n\tif (options?.createdTo) params.set(\"created_to\", options.createdTo);\n\tif (options?.updatedFrom) params.set(\"updated_from\", options.updatedFrom);\n\tif (options?.updatedTo) params.set(\"updated_to\", options.updatedTo);\n\n\tconst response = await client.get<PaginatedResponse<SdkChart>>(\n\t\t`/charts/all${params.toString() ? `?${params.toString()}` : \"\"}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tif (options?.includeData) {\n\t\tresponse.data = await Promise.all(\n\t\t\tresponse.data.map(async (chart) => {\n\t\t\t\tconst rows = await executeChartQuery(queryEngine, chart, tenantId);\n\t\t\t\treturn hydrateChartWithData(chart, rows);\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn response;\n}\n\nexport async function getChartsByIds(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tids: string[],\n\toptions?: ChartListAllOptions,\n\tsignal?: AbortSignal,\n): Promise<ChartBulkGetResponse> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\tfor (const id of ids) {\n\t\tparams.append(\"ids\", id);\n\t}\n\n\tconst response = await client.get<ChartBulkGetResponse>(\n\t\t`/charts/bulk${params.toString() ? `?${params.toString()}` : \"\"}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tif (options?.includeData) {\n\t\tresponse.data = await Promise.all(\n\t\t\tresponse.data.map(async (chart) => {\n\t\t\t\tconst rows = await executeChartQuery(queryEngine, chart, tenantId);\n\t\t\t\treturn hydrateChartWithData(chart, rows);\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn response;\n}\n\nexport async function getChart(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tid: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkChart> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst chart = await client.get<SdkChart>(\n\t\t`/charts/${encodeURIComponent(id)}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tconst rows = await executeChartQuery(queryEngine, chart, tenantId);\n\treturn hydrateChartWithData(chart, rows);\n}\n\nexport async function updateChart(\n\tclient: IQueryPanelApi,\n\tid: string,\n\tbody: ChartUpdateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkChart> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.put<SdkChart>(\n\t\t`/charts/${encodeURIComponent(id)}`,\n\t\tbody,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\nexport async function deleteChart(\n\tclient: IQueryPanelApi,\n\tid: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<void> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tawait client.delete(\n\t\t`/charts/${encodeURIComponent(id)}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n\tconst resolved = tenantId ?? client.getDefaultTenantId();\n\tif (!resolved) {\n\t\tthrow new Error(\n\t\t\t\"tenantId is required. Provide it per request or via defaultTenantId option.\",\n\t\t);\n\t}\n\treturn resolved;\n}\n\nasync function executeChartQuery(\n\tqueryEngine: QueryEngine,\n\tchart: SdkChart,\n\ttenantId: string,\n): Promise<Record<string, unknown>[]> {\n\tconst databaseName = chart.target_db ?? queryEngine.getDefaultDatabase();\n\tif (!databaseName) {\n\t\tconsole.warn(\"No database available to execute chart query\");\n\t\treturn [];\n\t}\n\ttry {\n\t\tconst result = await queryEngine.validateAndExecute(\n\t\t\tchart.sql,\n\t\t\t(chart.sql_params as ParamRecord | null) ?? {},\n\t\t\tdatabaseName,\n\t\t\ttenantId,\n\t\t);\n\t\treturn result.rows;\n\t} catch (error) {\n\t\tconsole.warn(`Failed to execute chart query: ${error}`);\n\t\treturn [];\n\t}\n}\n\n/**\n * Hydrates a chart with query result data based on its spec type.\n * - For vega-lite: injects data as `data.values`\n * - For vizspec: injects data as `data.values` while preserving `data.sourceId`\n */\nfunction hydrateChartWithData(\n\tchart: SdkChart,\n\trows: Record<string, unknown>[],\n): SdkChart {\n\tconst spec = chart.vega_lite_spec;\n\n\tif (chart.spec_type === \"vizspec\") {\n\t\t// VizSpec format: preserve sourceId and add values\n\t\tconst existingData = (spec.data as Record<string, unknown>) ?? {};\n\t\treturn {\n\t\t\t...chart,\n\t\t\tvega_lite_spec: {\n\t\t\t\t...spec,\n\t\t\t\tdata: {\n\t\t\t\t\t...existingData,\n\t\t\t\t\tvalues: rows,\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\n\t// Vega-Lite format: standard data injection\n\treturn {\n\t\t...chart,\n\t\tvega_lite_spec: {\n\t\t\t...spec,\n\t\t\tdata: {\n\t\t\t\tvalues: rows,\n\t\t\t},\n\t\t},\n\t};\n}\n","import type { IQueryPanelApi } from \"../core/api-types\";\nimport type { QueryEngine } from \"../core/query-engine\";\nimport * as charts from \"./charts\";\n\nexport interface SdkActiveChart {\n\tid: string;\n\tchart_id: string;\n\torder: number | null;\n\tmeta: Record<string, unknown> | null;\n\torganization_id: string | null;\n\ttenant_id: string | null;\n\tuser_id: string | null;\n\tcreated_at: string | null;\n\tupdated_at: string | null;\n\tchart?: charts.SdkChart | null;\n}\n\nexport interface ActiveChartCreateInput {\n\tchart_id: string;\n\torder?: number;\n\tmeta?: Record<string, unknown>;\n}\n\nexport interface ActiveChartUpdateInput {\n\tchart_id?: string;\n\torder?: number;\n\tmeta?: Record<string, unknown>;\n}\n\nexport interface ActiveChartListOptions extends charts.ChartListOptions {\n\twithData?: boolean;\n}\n\nexport interface ActiveChartBulkGetResponse {\n\tdata: SdkActiveChart[];\n\tmissingIds: string[];\n}\n\ninterface RequestOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n}\n\n/**\n * Route module for Active Chart CRUD operations\n * Simple pass-through to backend with optional chart data hydration\n */\nexport async function createActiveChart(\n\tclient: IQueryPanelApi,\n\tbody: ActiveChartCreateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkActiveChart> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.post<SdkActiveChart>(\n\t\t\"/active-charts\",\n\t\tbody,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\nexport async function listActiveCharts(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\toptions?: ActiveChartListOptions,\n\tsignal?: AbortSignal,\n): Promise<charts.PaginatedResponse<SdkActiveChart>> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\tif (options?.pagination?.page)\n\t\tparams.set(\"page\", `${options.pagination.page}`);\n\tif (options?.pagination?.limit)\n\t\tparams.set(\"limit\", `${options.pagination.limit}`);\n\tif (options?.sortBy) params.set(\"sort_by\", options.sortBy);\n\tif (options?.sortDir) params.set(\"sort_dir\", options.sortDir);\n\tif (options?.title) params.set(\"title\", options.title);\n\tif (options?.userFilter) params.set(\"user_id\", options.userFilter);\n\tif (options?.createdFrom) params.set(\"created_from\", options.createdFrom);\n\tif (options?.createdTo) params.set(\"created_to\", options.createdTo);\n\tif (options?.updatedFrom) params.set(\"updated_from\", options.updatedFrom);\n\tif (options?.updatedTo) params.set(\"updated_to\", options.updatedTo);\n\n\tconst response = await client.get<\n\t\tcharts.PaginatedResponse<SdkActiveChart>\n\t>(\n\t\t`/active-charts${params.toString() ? `?${params.toString()}` : \"\"}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tif (options?.withData) {\n\t\tresponse.data = await Promise.all(\n\t\t\tresponse.data.map(async (active) => ({\n\t\t\t\t...active,\n\t\t\t\tchart: active.chart\n\t\t\t\t\t? await charts.getChart(\n\t\t\t\t\t\t\tclient,\n\t\t\t\t\t\t\tqueryEngine,\n\t\t\t\t\t\t\tactive.chart_id,\n\t\t\t\t\t\t\toptions,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t)\n\t\t\t\t\t: null,\n\t\t\t})),\n\t\t);\n\t}\n\n\treturn response;\n}\n\nexport async function listAllActiveCharts(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\toptions?: ActiveChartListOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkActiveChart[]> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst response = await client.get<{ data: SdkActiveChart[] }>(\n\t\t\"/active-charts/all\",\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tif (options?.withData) {\n\t\tresponse.data = await Promise.all(\n\t\t\tresponse.data.map(async (active) => ({\n\t\t\t\t...active,\n\t\t\t\tchart: active.chart\n\t\t\t\t\t? await charts.getChart(\n\t\t\t\t\t\t\tclient,\n\t\t\t\t\t\t\tqueryEngine,\n\t\t\t\t\t\t\tactive.chart_id,\n\t\t\t\t\t\t\toptions,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t)\n\t\t\t\t\t: null,\n\t\t\t})),\n\t\t);\n\t}\n\n\treturn response.data;\n}\n\nexport async function getActiveChartsByIds(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tids: string[],\n\toptions?: ActiveChartListOptions,\n\tsignal?: AbortSignal,\n): Promise<ActiveChartBulkGetResponse> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\tfor (const id of ids) {\n\t\tparams.append(\"ids\", id);\n\t}\n\n\tconst response = await client.get<ActiveChartBulkGetResponse>(\n\t\t`/active-charts/bulk${params.toString() ? `?${params.toString()}` : \"\"}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tif (options?.withData) {\n\t\tresponse.data = await Promise.all(\n\t\t\tresponse.data.map(async (active) => ({\n\t\t\t\t...active,\n\t\t\t\tchart: active.chart\n\t\t\t\t\t? await charts.getChart(\n\t\t\t\t\t\t\tclient,\n\t\t\t\t\t\t\tqueryEngine,\n\t\t\t\t\t\t\tactive.chart_id,\n\t\t\t\t\t\t\toptions,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t)\n\t\t\t\t\t: null,\n\t\t\t})),\n\t\t);\n\t}\n\n\treturn response;\n}\n\nexport async function getActiveChart(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tid: string,\n\toptions?: ActiveChartListOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkActiveChart> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst active = await client.get<SdkActiveChart>(\n\t\t`/active-charts/${encodeURIComponent(id)}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tif (options?.withData && active.chart_id) {\n\t\treturn {\n\t\t\t...active,\n\t\t\tchart: await charts.getChart(\n\t\t\t\tclient,\n\t\t\t\tqueryEngine,\n\t\t\t\tactive.chart_id,\n\t\t\t\toptions,\n\t\t\t\tsignal,\n\t\t\t),\n\t\t};\n\t}\n\n\treturn active;\n}\n\nexport async function updateActiveChart(\n\tclient: IQueryPanelApi,\n\tid: string,\n\tbody: ActiveChartUpdateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkActiveChart> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.put<SdkActiveChart>(\n\t\t`/active-charts/${encodeURIComponent(id)}`,\n\t\tbody,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\nexport async function deleteActiveChart(\n\tclient: IQueryPanelApi,\n\tid: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<void> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tawait client.delete(\n\t\t`/active-charts/${encodeURIComponent(id)}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n\tconst resolved = tenantId ?? client.getDefaultTenantId();\n\tif (!resolved) {\n\t\tthrow new Error(\n\t\t\t\"tenantId is required. Provide it per request or via defaultTenantId option.\",\n\t\t);\n\t}\n\treturn resolved;\n}\n","import type { IQueryPanelApi } from \"../core/api-types\";\nimport type { PaginatedResponse, PaginationQuery } from \"./charts\";\n\n/**\n * Dashboard interface matching querypanel-sdk schema\n */\nexport interface SdkDashboard {\n\tid: string;\n\torganization_id: string;\n\tname: string;\n\tdescription: string | null;\n\tstatus: \"draft\" | \"deployed\";\n\tcontent_json: string | null;\n\twidget_config: Record<string, unknown> | null;\n\teditor_type: \"blocknote\" | \"custom\";\n\tis_customer_fork: boolean;\n\tforked_from_dashboard_id: string | null;\n\ttenant_id: string | null;\n\tdatasource_id: string | null;\n\tversion: number;\n\tdeployed_at: string | null;\n\tcreated_at: string;\n\tupdated_at: string;\n\tcreated_by: string | null;\n}\n\n/**\n * Dashboard creation input\n */\nexport interface DashboardCreateInput {\n\tname: string;\n\tdescription?: string;\n\tcontent_json?: string;\n\twidget_config?: Record<string, unknown>;\n\teditor_type?: \"blocknote\" | \"custom\";\n\tdatasource_id?: string;\n}\n\n/**\n * Dashboard update input\n */\nexport interface DashboardUpdateInput {\n\tname?: string;\n\tdescription?: string;\n\tcontent_json?: string;\n\twidget_config?: Record<string, unknown>;\n\teditor_type?: \"blocknote\" | \"custom\";\n\tdatasource_id?: string | null;\n}\n\n/**\n * Dashboard list options\n */\nexport interface DashboardListOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n\tpagination?: PaginationQuery;\n\tstatus?: \"draft\" | \"deployed\";\n\tsortBy?: \"name\" | \"created_at\" | \"updated_at\" | \"deployed_at\";\n\tsortDir?: \"asc\" | \"desc\";\n}\n\n/**\n * Fork dashboard input\n */\nexport interface DashboardForkInput {\n\ttenant_id: string;\n\tname?: string;\n}\n\n/**\n * Fork update input\n */\nexport interface DashboardForkUpdateInput {\n\ttenant_id: string;\n\tcontent_json?: string;\n\twidget_config?: Record<string, unknown>;\n}\n\ninterface RequestOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n}\n\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n\tconst resolved = tenantId ?? client.getDefaultTenantId();\n\tif (resolved === undefined) {\n\t\tthrow new Error(\"tenantId is required (option or client default)\");\n\t}\n\treturn resolved;\n}\n\n/**\n * Creates a new dashboard\n */\nexport async function createDashboard(\n\tclient: IQueryPanelApi,\n\tbody: DashboardCreateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.post<SdkDashboard>(\n\t\t\"/dashboards\",\n\t\tbody,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Lists dashboards with pagination and filtering\n */\nexport async function listDashboards(\n\tclient: IQueryPanelApi,\n\toptions?: DashboardListOptions,\n\tsignal?: AbortSignal,\n): Promise<PaginatedResponse<SdkDashboard>> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\n\tif (options?.pagination?.page) {\n\t\tparams.set(\"page\", options.pagination.page.toString());\n\t}\n\tif (options?.pagination?.limit) {\n\t\tparams.set(\"limit\", options.pagination.limit.toString());\n\t}\n\tif (options?.status) {\n\t\tparams.set(\"status\", options.status);\n\t}\n\tif (options?.sortBy) {\n\t\tparams.set(\"sort_by\", options.sortBy);\n\t}\n\tif (options?.sortDir) {\n\t\tparams.set(\"sort_dir\", options.sortDir);\n\t}\n\n\treturn await client.get<PaginatedResponse<SdkDashboard>>(\n\t\t`/dashboards?${params.toString()}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Gets a dashboard by ID\n */\nexport async function getDashboard(\n\tclient: IQueryPanelApi,\n\tid: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.get<SdkDashboard>(\n\t\t`/dashboards/${id}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Gets a dashboard for a specific tenant (returns fork if exists, otherwise original)\n */\nexport async function getDashboardForTenant(\n\tclient: IQueryPanelApi,\n\tid: string,\n\ttenantId: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\tconst params = new URLSearchParams();\n\tparams.set(\"tenant_id\", tenantId);\n\n\treturn await client.get<SdkDashboard>(\n\t\t`/dashboards/${id}/for-tenant?${params.toString()}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Updates a dashboard\n */\nexport async function updateDashboard(\n\tclient: IQueryPanelApi,\n\tid: string,\n\tbody: DashboardUpdateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.put<SdkDashboard>(\n\t\t`/dashboards/${id}`,\n\t\tbody,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Updates dashboard status (deploy/undeploy)\n */\nexport async function updateDashboardStatus(\n\tclient: IQueryPanelApi,\n\tid: string,\n\tstatus: \"draft\" | \"deployed\",\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.patch<SdkDashboard>(\n\t\t`/dashboards/${id}/status`,\n\t\t{ status },\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Deletes a dashboard\n */\nexport async function deleteDashboard(\n\tclient: IQueryPanelApi,\n\tid: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<void> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tawait client.delete(\n\t\t`/dashboards/${id}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n// ============================================================================\n// Customer Fork Operations\n// ============================================================================\n\n/**\n * Forks a dashboard for customer customization\n */\nexport async function forkDashboard(\n\tclient: IQueryPanelApi,\n\tid: string,\n\tinput: DashboardForkInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.post<SdkDashboard>(\n\t\t`/dashboards/${id}/fork`,\n\t\tinput,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Updates a customer fork\n */\nexport async function updateFork(\n\tclient: IQueryPanelApi,\n\tforkId: string,\n\tinput: DashboardForkUpdateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.put<SdkDashboard>(\n\t\t`/dashboards/forks/${forkId}`,\n\t\tinput,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Rollbacks a fork to the original dashboard\n */\nexport async function rollbackFork(\n\tclient: IQueryPanelApi,\n\tforkId: string,\n\ttenantId: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\treturn await client.post<SdkDashboard>(\n\t\t`/dashboards/forks/${forkId}/rollback`,\n\t\t{ tenant_id: tenantId },\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Deletes a customer fork\n */\nexport async function deleteFork(\n\tclient: IQueryPanelApi,\n\tforkId: string,\n\ttenantId: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<void> {\n\tconst params = new URLSearchParams();\n\tparams.set(\"tenant_id\", tenantId);\n\n\tawait client.delete(\n\t\t`/dashboards/forks/${forkId}?${params.toString()}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Lists customer forks for a tenant\n */\nexport async function listForksForTenant(\n\tclient: IQueryPanelApi,\n\ttenantId: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard[]> {\n\treturn await client.get<SdkDashboard[]>(\n\t\t`/dashboards/customer/${tenantId}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n","import crypto from 'node:crypto';\nimport type { IQueryPanelApi } from \"../core/api-types\";\nimport type { QueryEngine } from \"../core/query-engine\";\nimport { SchemaTooLargeError } from \"../errors\";\nimport type { SchemaIntrospection } from \"../schema/types\";\n\nexport interface IngestResponse {\n\tsuccess: boolean;\n\tmessage: string;\n\tchunks: number;\n\tchunks_with_annotations: number;\n\tschema_id?: string;\n\tschema_hash?: string;\n\tdrift_detected?: boolean;\n\tskipped?: boolean;\n}\n\nexport interface SchemaSyncOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n\ttables?: string[];\n\tforceReindex?: boolean;\n}\n\ninterface SchemaIngestColumn {\n\tname: string;\n\tdata_type: string;\n\tis_primary_key: boolean;\n\tdescription: string;\n}\n\ninterface SchemaIngestTable {\n\tschema_name?: string;\n\ttable_name: string;\n\tdescription: string;\n\tcolumns: SchemaIngestColumn[];\n}\n\ninterface SchemaIngestRequest {\n\tdatabase: string;\n\tdialect: string;\n\ttables: SchemaIngestTable[];\n\tforce_reindex?: boolean;\n\ttenant_settings?: {\n\t\ttenantFieldName: string;\n\t\ttenantFieldType: string;\n\t\tenforceTenantIsolation: boolean;\n\t};\n}\n\n/**\n * Route module for schema ingestion\n * Handles introspection and sync to backend\n */\nexport async function syncSchema(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tdatabaseName: string,\n\toptions: SchemaSyncOptions,\n\tsignal?: AbortSignal,\n): Promise<IngestResponse> {\n\tconst tenantId = resolveTenantId(client, options.tenantId);\n\tconst adapter = queryEngine.getDatabase(databaseName);\n\tconst metadata = queryEngine.getDatabaseMetadata(databaseName);\n\n\tconst introspection = await adapter.introspect(\n\t\toptions.tables ? { tables: options.tables } : undefined,\n\t);\n\n\tconst payload = buildSchemaRequest(databaseName, adapter, introspection, metadata);\n\tif (options.forceReindex) {\n\t\tpayload.force_reindex = true;\n\t}\n\n\t// Generate a session id so backend telemetry can correlate all work for this sync\n\tconst sessionId = crypto.randomUUID();\n\n\tconst response = await client.post<IngestResponse>(\n\t\t\"/ingest\",\n\t\tpayload,\n\t\ttenantId,\n\t\toptions.userId,\n\t\toptions.scopes,\n\t\tsignal,\n\t\tsessionId,\n\t);\n\n\treturn response;\n}\n\n// ---------------------------------------------------------------------------\n// Batched ingest (session protocol): stage -> embed each batch -> finalize\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_MAX_BATCH_DOCS = 1000;\nexport const DEFAULT_MAX_BATCH_BYTES = 2_000_000;\n\n/** Progress events emitted by stageSchemaIngest / syncSchemaInBatches. */\nexport type SchemaSyncProgressEvent =\n\t| { phase: \"introspecting\" }\n\t| { phase: \"introspected\"; tableCount: number; columnCount: number }\n\t| { phase: \"uploading\"; done: number; total: number }\n\t| { phase: \"committing\" }\n\t| { phase: \"embedding\"; done: number; total: number; chunks: number }\n\t| { phase: \"finalizing\" };\n\nexport interface SchemaIngestRequestOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n}\n\nexport interface StageSchemaIngestOptions extends SchemaSyncOptions {\n\t/** Max docs per batch (1 per table + 1 per column). Default 1000. */\n\tmaxBatchDocs?: number;\n\t/** Max serialized JSON bytes per batch. Default 2_000_000. */\n\tmaxBatchBytes?: number;\n\t/** Throw SchemaTooLargeError (before uploading) above this many columns. */\n\tmaxColumns?: number;\n\tonProgress?: (event: SchemaSyncProgressEvent) => void;\n}\n\nexport type SyncSchemaInBatchesOptions = StageSchemaIngestOptions;\n\nexport interface StageSchemaIngestResult {\n\tsessionId: string;\n\tstatus: \"skipped\" | \"embedding\";\n\ttotalBatches: number;\n\ttableCount: number;\n\tcolumnCount: number;\n\t/** Present when status is \"skipped\" (schema unchanged). */\n\tskippedResult?: IngestResponse;\n\tdriftDetected?: boolean;\n}\n\nexport interface SchemaIngestBatchEmbedResponse {\n\tindex: number;\n\tchunks: number;\n\tchunks_with_annotations: number;\n}\n\ninterface IngestSessionCreateResponse {\n\tsession_id: string;\n}\n\ntype IngestSessionCommitResponse =\n\t| ({ status: \"skipped\" } & IngestResponse)\n\t| { status: \"embedding\"; total_batches: number; drift_detected?: boolean };\n\nconst utf8 = new TextEncoder();\n\nfunction jsonBytes(value: unknown): number {\n\treturn utf8.encode(JSON.stringify(value)).length;\n}\n\nconst BATCH_ENVELOPE_BYTES = jsonBytes({ tables: [] });\n\n/**\n * Split tables (keeping order) into batches bounded by doc count and JSON size.\n * A single table larger than either limit is placed alone in its own batch.\n * Always returns at least one batch (possibly empty) so a session has >= 1 batch.\n */\nexport function splitIngestTablesIntoBatches<T extends { columns: unknown[] }>(\n\ttables: T[],\n\tmaxBatchDocs: number = DEFAULT_MAX_BATCH_DOCS,\n\tmaxBatchBytes: number = DEFAULT_MAX_BATCH_BYTES,\n): T[][] {\n\tconst batches: T[][] = [];\n\tlet current: T[] = [];\n\tlet currentDocs = 0;\n\tlet currentBytes = BATCH_ENVELOPE_BYTES;\n\n\tfor (const table of tables) {\n\t\tconst docs = 1 + table.columns.length;\n\t\tconst bytes = jsonBytes(table);\n\t\t// +1 for the separating comma when appended to a non-empty batch.\n\t\tif (\n\t\t\tcurrent.length > 0 &&\n\t\t\t(currentDocs + docs > maxBatchDocs ||\n\t\t\t\tcurrentBytes + 1 + bytes > maxBatchBytes)\n\t\t) {\n\t\t\tbatches.push(current);\n\t\t\tcurrent = [];\n\t\t\tcurrentDocs = 0;\n\t\t\tcurrentBytes = BATCH_ENVELOPE_BYTES;\n\t\t}\n\t\tcurrentBytes += bytes + (current.length > 0 ? 1 : 0);\n\t\tcurrent.push(table);\n\t\tcurrentDocs += docs;\n\t}\n\n\tif (current.length > 0 || batches.length === 0) {\n\t\tbatches.push(current);\n\t}\n\treturn batches;\n}\n\nfunction sessionPath(sessionId: string, suffix: string): string {\n\treturn `/ingest/sessions/${encodeURIComponent(sessionId)}${suffix}`;\n}\n\n/**\n * Introspect, upload the schema in batches to a new ingest session, and commit it.\n * Embedding is left to embedSchemaIngestBatch / finalizeSchemaIngest so callers\n * (e.g. serverless routes) can spread the work over several requests.\n */\nexport async function stageSchemaIngest(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tdatabaseName: string,\n\toptions: StageSchemaIngestOptions,\n\tsignal?: AbortSignal,\n): Promise<StageSchemaIngestResult> {\n\tconst tenantId = resolveTenantId(client, options.tenantId);\n\tconst adapter = queryEngine.getDatabase(databaseName);\n\tconst metadata = queryEngine.getDatabaseMetadata(databaseName);\n\tconst emit = options.onProgress ?? (() => {});\n\n\temit({ phase: \"introspecting\" });\n\tconst introspection = await adapter.introspect(\n\t\toptions.tables ? { tables: options.tables } : undefined,\n\t);\n\tconst { tables, ...sessionFields } = buildSchemaRequest(\n\t\tdatabaseName,\n\t\tadapter,\n\t\tintrospection,\n\t\tmetadata,\n\t);\n\tconst tableCount = tables.length;\n\tconst columnCount = tables.reduce((sum, t) => sum + t.columns.length, 0);\n\temit({ phase: \"introspected\", tableCount, columnCount });\n\n\tif (options.maxColumns !== undefined && columnCount > options.maxColumns) {\n\t\tthrow new SchemaTooLargeError(tableCount, columnCount, options.maxColumns);\n\t}\n\n\tconst batches = splitIngestTablesIntoBatches(\n\t\ttables,\n\t\toptions.maxBatchDocs ?? DEFAULT_MAX_BATCH_DOCS,\n\t\toptions.maxBatchBytes ?? DEFAULT_MAX_BATCH_BYTES,\n\t);\n\tconst totalBatches = batches.length;\n\n\tconst created = await client.post<IngestSessionCreateResponse>(\n\t\t\"/ingest/sessions\",\n\t\t{\n\t\t\t...sessionFields,\n\t\t\t...(options.forceReindex ? { force_reindex: true } : {}),\n\t\t\ttotal_batches: totalBatches,\n\t\t},\n\t\ttenantId,\n\t\toptions.userId,\n\t\toptions.scopes,\n\t\tsignal,\n\t\tcrypto.randomUUID(),\n\t);\n\tconst sessionId = created.session_id;\n\n\tfor (let index = 0; index < totalBatches; index += 1) {\n\t\tawait client.put(\n\t\t\tsessionPath(sessionId, `/batches/${index}`),\n\t\t\t{ tables: batches[index] },\n\t\t\ttenantId,\n\t\t\toptions.userId,\n\t\t\toptions.scopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t);\n\t\temit({ phase: \"uploading\", done: index + 1, total: totalBatches });\n\t}\n\n\temit({ phase: \"committing\" });\n\tconst committed = await client.post<IngestSessionCommitResponse>(\n\t\tsessionPath(sessionId, \"/commit\"),\n\t\t{},\n\t\ttenantId,\n\t\toptions.userId,\n\t\toptions.scopes,\n\t\tsignal,\n\t\tsessionId,\n\t);\n\n\tif (committed.status === \"skipped\") {\n\t\tconst { status: _status, ...skippedResult } = committed;\n\t\treturn {\n\t\t\tsessionId,\n\t\t\tstatus: \"skipped\",\n\t\t\ttotalBatches,\n\t\t\ttableCount,\n\t\t\tcolumnCount,\n\t\t\tskippedResult,\n\t\t\tdriftDetected: skippedResult.drift_detected,\n\t\t};\n\t}\n\n\treturn {\n\t\tsessionId,\n\t\tstatus: \"embedding\",\n\t\ttotalBatches: committed.total_batches ?? totalBatches,\n\t\ttableCount,\n\t\tcolumnCount,\n\t\tdriftDetected: committed.drift_detected,\n\t};\n}\n\n/** Embed one committed batch of an ingest session. */\nexport async function embedSchemaIngestBatch(\n\tclient: IQueryPanelApi,\n\tsessionId: string,\n\tindex: number,\n\toptions: SchemaIngestRequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SchemaIngestBatchEmbedResponse> {\n\tconst tenantId = resolveTenantId(client, options.tenantId);\n\treturn await client.post<SchemaIngestBatchEmbedResponse>(\n\t\tsessionPath(sessionId, `/batches/${index}/embed`),\n\t\t{},\n\t\ttenantId,\n\t\toptions.userId,\n\t\toptions.scopes,\n\t\tsignal,\n\t\tsessionId,\n\t);\n}\n\n/** Finalize an ingest session once every batch is embedded. */\nexport async function finalizeSchemaIngest(\n\tclient: IQueryPanelApi,\n\tsessionId: string,\n\toptions: SchemaIngestRequestOptions,\n\tsignal?: AbortSignal,\n): Promise<IngestResponse> {\n\tconst tenantId = resolveTenantId(client, options.tenantId);\n\treturn await client.post<IngestResponse>(\n\t\tsessionPath(sessionId, \"/finalize\"),\n\t\t{},\n\t\ttenantId,\n\t\toptions.userId,\n\t\toptions.scopes,\n\t\tsignal,\n\t\tsessionId,\n\t);\n}\n\n/**\n * Stage, embed every batch, and finalize in-process. For server-side callers\n * without per-request time limits; returns the skipped result when unchanged.\n */\nexport async function syncSchemaInBatches(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tdatabaseName: string,\n\toptions: SyncSchemaInBatchesOptions,\n\tsignal?: AbortSignal,\n): Promise<IngestResponse> {\n\tconst staged = await stageSchemaIngest(\n\t\tclient,\n\t\tqueryEngine,\n\t\tdatabaseName,\n\t\toptions,\n\t\tsignal,\n\t);\n\tif (staged.status === \"skipped\") {\n\t\treturn staged.skippedResult as IngestResponse;\n\t}\n\n\tconst emit = options.onProgress ?? (() => {});\n\tconst requestOptions: SchemaIngestRequestOptions = {\n\t\ttenantId: options.tenantId,\n\t\tuserId: options.userId,\n\t\tscopes: options.scopes,\n\t};\n\tlet chunks = 0;\n\tfor (let index = 0; index < staged.totalBatches; index += 1) {\n\t\tconst embedded = await embedSchemaIngestBatch(\n\t\t\tclient,\n\t\t\tstaged.sessionId,\n\t\t\tindex,\n\t\t\trequestOptions,\n\t\t\tsignal,\n\t\t);\n\t\tchunks += embedded.chunks ?? 0;\n\t\temit({\n\t\t\tphase: \"embedding\",\n\t\t\tdone: index + 1,\n\t\t\ttotal: staged.totalBatches,\n\t\t\tchunks,\n\t\t});\n\t}\n\n\temit({ phase: \"finalizing\" });\n\treturn await finalizeSchemaIngest(\n\t\tclient,\n\t\tstaged.sessionId,\n\t\trequestOptions,\n\t\tsignal,\n\t);\n}\n\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n\tconst resolved = tenantId ?? client.getDefaultTenantId();\n\tif (!resolved) {\n\t\tthrow new Error(\n\t\t\t\"tenantId is required. Provide it per request or via defaultTenantId option.\",\n\t\t);\n\t}\n\treturn resolved;\n}\n\nfunction buildSchemaRequest(\n\tdatabaseName: string,\n\tadapter: { getDialect: () => string },\n\tintrospection: SchemaIntrospection,\n\tmetadata?: {\n\t\ttenantFieldName?: string;\n\t\ttenantFieldType?: string;\n\t\tenforceTenantIsolation?: boolean;\n\t},\n): SchemaIngestRequest {\n\tconst dialect = adapter.getDialect();\n\tconst tables: SchemaIngestTable[] = introspection.tables.map((table) => ({\n\t\tschema_name: table.schema,\n\t\ttable_name: table.name,\n\t\tdescription: table.comment ?? `Table ${table.name}`,\n\t\tcolumns: table.columns.map((column) => ({\n\t\t\tname: column.name,\n\t\t\tdata_type: column.rawType ?? column.type,\n\t\t\tis_primary_key: Boolean(column.isPrimaryKey),\n\t\t\tdescription: column.comment ?? \"\",\n\t\t})),\n\t}));\n\n\tconst request: SchemaIngestRequest = {\n\t\tdatabase: databaseName,\n\t\tdialect,\n\t\ttables,\n\t};\n\n\t// Include tenant_settings if configured in the database metadata\n\tif (metadata?.enforceTenantIsolation === false) {\n\t\trequest.tenant_settings = {\n\t\t\ttenantFieldName: metadata.tenantFieldName ?? \"\",\n\t\t\ttenantFieldType: metadata.tenantFieldType ?? \"String\",\n\t\t\tenforceTenantIsolation: false,\n\t\t};\n\t} else if (metadata?.tenantFieldName && metadata?.tenantFieldType) {\n\t\trequest.tenant_settings = {\n\t\t\ttenantFieldName: metadata.tenantFieldName,\n\t\t\ttenantFieldType: metadata.tenantFieldType,\n\t\t\tenforceTenantIsolation: true,\n\t\t};\n\t}\n\n\treturn request;\n}\n","import crypto from \"node:crypto\";\nimport type { IQueryPanelApi } from \"../core/api-types\";\nimport type { ParamRecord, QueryEngine } from \"../core/query-engine\";\nimport type {\n\tAggregateOp,\n\tChartType,\n\tFieldType,\n\tStackingMode,\n\tTimeUnit,\n\tValueFormat,\n\tVizSpec,\n\tVizSpecKind,\n} from \"../types/vizspec\";\nimport {\n\ttype AskResponse,\n\tanonymizeResults,\n\ttype ChartEnvelope,\n\tresolveQueryPipeline,\n} from \"./query\";\nimport { throwIfGenerationErrorResponse } from \"../errors\";\nimport { resolveQuery } from \"./queries\";\n\n// ============================================================================\n// Input Types for Modifications\n// ============================================================================\n\n/**\n * Simplified field reference for modification inputs.\n * More ergonomic than the full AxisField type.\n */\nexport interface AxisFieldInput {\n\t/** Column name from the SQL result */\n\tfield: string;\n\t/** Human-friendly label for the axis */\n\tlabel?: string;\n\t/** Field data type */\n\ttype?: FieldType;\n\t/** Aggregation operation (e.g., 'sum', 'avg') */\n\taggregate?: AggregateOp;\n\t/** Time unit for temporal fields */\n\ttimeUnit?: TimeUnit;\n\t/** Value formatting options */\n\tformat?: ValueFormat;\n}\n\n/**\n * Simplified field reference for series/grouping fields.\n */\nexport interface FieldRefInput {\n\t/** Column name from the SQL result */\n\tfield: string;\n\t/** Human-friendly label */\n\tlabel?: string;\n\t/** Field data type */\n\ttype?: FieldType;\n}\n\n/**\n * Date range specification for SQL modifications.\n */\nexport interface DateRangeInput {\n\t/** Start date in ISO format (e.g., '2024-01-01') */\n\tfrom?: string;\n\t/** End date in ISO format (e.g., '2024-12-31') */\n\tto?: string;\n}\n\n/**\n * SQL modification options that trigger query regeneration.\n * When any of these are provided, a new ask() call is made.\n */\nexport interface SqlModifications {\n\t/**\n\t * Direct SQL override. When provided, this SQL is executed directly\n\t * without calling the query generation endpoint.\n\t */\n\tcustomSql?: string;\n\n\t/**\n\t * Change the time granularity of the query.\n\t * Triggers SQL regeneration with hints about the desired grouping.\n\t */\n\ttimeGranularity?: TimeUnit;\n\n\t/**\n\t * Filter the query to a specific date range.\n\t * Triggers SQL regeneration with date filter hints.\n\t */\n\tdateRange?: DateRangeInput;\n\n\t/**\n\t * Additional natural language instructions to modify the query.\n\t * These are appended to the original question as hints.\n\t * Example: \"exclude cancelled orders\" or \"only show top 10\"\n\t */\n\tadditionalInstructions?: string;\n}\n\n/**\n * Visualization modification options that don't affect the SQL.\n * These changes only affect how the chart is rendered.\n */\nexport interface VizModifications {\n\t/** Change the VizSpec kind (chart, table, metric). Applies to vizspec only. */\n\tkind?: VizSpecKind;\n\t/** Change the chart type (line, bar, area, scatter, pie) */\n\tchartType?: ChartType;\n\n\t/** Configure the X axis field and settings */\n\txAxis?: AxisFieldInput;\n\n\t/** Configure the Y axis field(s) and settings */\n\tyAxis?: AxisFieldInput | AxisFieldInput[];\n\n\t/** Configure the series/grouping field for multi-series charts */\n\tseries?: FieldRefInput;\n\n\t/** Stacking mode for multi-series charts */\n\tstacking?: StackingMode;\n\n\t/** Maximum number of rows to display in the chart */\n\tlimit?: number;\n}\n\n/**\n * Input for the modifyChart() method.\n * Accepts chart data from either ask() responses or saved charts.\n */\nexport interface ChartModifyInput {\n\t/**\n\t * Server-issued id of the query to modify or re-execute.\n\t * From ask() / modifyChart() response: response.queryId\n\t * From saved chart: chart.query_id\n\t *\n\t * Preferred over `sql`: when present the SQL and params are loaded from the server\n\t * (scoped to the authenticated tenant) and `sql` / `params` are ignored.\n\t */\n\tqueryId?: string;\n\n\t/**\n\t * The SQL query to modify or re-execute. Required when `queryId` is not provided.\n\t * From ask() response: response.sql\n\t * From saved chart: chart.sql\n\t *\n\t * Kept for backward compatibility; prefer `queryId`.\n\t */\n\tsql?: string;\n\n\t/**\n\t * The original natural language question.\n\t * Used when regenerating SQL with modifications.\n\t */\n\tquestion: string;\n\n\t/**\n\t * The database to execute the query against.\n\t * From ask() response: response.target_db\n\t * From saved chart: chart.target_db\n\t */\n\tdatabase: string;\n\n\t/**\n\t * Query parameters (optional).\n\t * From ask() response: response.params\n\t * From saved chart: chart.sql_params\n\t */\n\tparams?: ParamRecord;\n\n\t/**\n\t * SQL modifications that trigger query regeneration.\n\t * When provided, a new ask() call is made with modification hints.\n\t */\n\tsqlModifications?: SqlModifications;\n\n\t/**\n\t * Visualization modifications that don't affect the SQL.\n\t * Applied during chart generation.\n\t */\n\tvizModifications?: VizModifications;\n}\n\n/**\n * Options for the modifyChart() method.\n */\nexport interface ChartModifyOptions {\n\t/** Tenant ID for multi-tenant isolation */\n\ttenantId?: string;\n\t/** User ID for audit/tracking */\n\tuserId?: string;\n\t/** Permission scopes */\n\tscopes?: string[];\n\t/** Maximum retry attempts for SQL generation */\n\tmaxRetry?: number;\n\t/** Maximum retry attempts for chart generation */\n\tchartMaxRetries?: number;\n\t/** Chart generation method: 'vega-lite' or 'vizspec' */\n\tchartType?: \"vega-lite\" | \"vizspec\";\n\t/**\n\t * Query pipeline. Only `\"v2\"` is accepted (`POST /v2/query`).\n\t * `\"v1\"` was removed: passing it fails typecheck and throws at runtime.\n\t * Omit this field; chart modifications always use v2.\n\t */\n\tpipeline?: \"v2\";\n\t/**\n\t * Fast path for structured controls (pinned charts): date-only changes\n\t * substitute params or call `/v2/rewrite-datefilter` instead of `/v2/query`,\n\t * and chart generation is skipped when the caller keeps its own encoding\n\t * (`chart.vizSpec` / `vegaLiteSpec` are then omitted). Default: false.\n\t */\n\tlightweight?: boolean;\n\t/**\n\t * QueryPanel session ID for context-aware follow-ups.\n\t * Pass the querypanelSessionId from a previous ask() response\n\t * to preserve conversation history when modifying charts.\n\t * If omitted, modifyChart starts a new QueryPanel session and sends\n\t * the original question with modification hints.\n\t */\n\tquerypanelSessionId?: string;\n\t/**\n\t * Restrict VizSpec chart types for this call (merged with SDK default from constructor when omitted).\n\t */\n\tsupportedChartTypes?: ChartType[];\n\t/**\n\t * When true, rationale includes engineer-style schema detail (tables, columns).\n\t * When false (default), rationale is client-safe for end users.\n\t */\n\tdebug?: boolean;\n}\n\n/**\n * Response from modifyChart(), extending AskResponse with modification metadata.\n */\nexport interface ChartModifyResponse extends AskResponse {\n\t/** Metadata about what was modified */\n\tmodified: {\n\t\t/** Whether the SQL was changed (regenerated or custom) */\n\t\tsqlChanged: boolean;\n\t\t/** Whether visualization settings were applied */\n\t\tvizChanged: boolean;\n\t};\n}\n\n// ============================================================================\n// Server Response Types\n// ============================================================================\n\ninterface ServerQueryResponse {\n\tsuccess: boolean;\n\tsql: string;\n\tparams?: Array<Record<string, unknown>>;\n\tdialect: string;\n\tdatabase?: string;\n\ttable?: string;\n\trationale?: string;\n\tqueryId?: string;\n}\n\ninterface ServerChartResponse {\n\tchart: Record<string, unknown> | null;\n\tnotes: string | null;\n}\n\ninterface ServerVizSpecResponse {\n\tspec: VizSpec;\n\tnotes: string | null;\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Builds a modified question string with SQL modification hints.\n * These hints guide the LLM to generate appropriate SQL.\n */\nfunction buildModifiedQuestion(\n\toriginalQuestion: string,\n\tmodifications: SqlModifications,\n\thasSessionContext = false,\n): string {\n\tconst hints: string[] = [];\n\n\tif (modifications.timeGranularity) {\n\t\thints.push(`group results by ${modifications.timeGranularity}`);\n\t}\n\n\tif (modifications.dateRange) {\n\t\tconst from = normalizeDateInput(modifications.dateRange.from);\n\t\tconst to = normalizeDateInput(modifications.dateRange.to);\n\n\t\tif (from && to) {\n\t\t\thints.push(`change date range to ${from} through ${to}`);\n\t\t} else if (from) {\n\t\t\thints.push(`change start date to ${from}`);\n\t\t} else if (to) {\n\t\t\thints.push(`change end date to ${to}`);\n\t\t}\n\t}\n\n\tif (modifications.additionalInstructions) {\n\t\thints.push(modifications.additionalInstructions);\n\t}\n\n\tif (hints.length === 0) {\n\t\treturn originalQuestion;\n\t}\n\n\t// Hints-only when continuing a QueryPanel session that already has the question.\n\tif (hasSessionContext) {\n\t\treturn hints.join(\", \");\n\t}\n\n\treturn `${originalQuestion} (${hints.join(\", \")})`;\n}\n\nconst REWRITE_DATEFILTER_ENDPOINT = \"/v2/rewrite-datefilter\";\nconst START_PARAM_KEY_REGEX = /(^|_)(start|from)(_|$)/i;\nconst END_PARAM_KEY_REGEX = /(^|_)(end|to)(_|$)/i;\nconst ISO_DATETIME_RE = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z?$/;\nconst SQL_DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\nconst SQL_DATETIME_RE = /^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/;\n\nfunction normalizeDateInput(value?: string): string | undefined {\n\tif (!value) return undefined;\n\tconst trimmed = value.trim();\n\tif (!trimmed) return undefined;\n\n\tif (ISO_DATETIME_RE.test(trimmed)) {\n\t\treturn trimmed\n\t\t\t.replace(\"T\", \" \")\n\t\t\t.replace(/\\.\\d+Z?$/, \"\")\n\t\t\t.replace(/Z$/, \"\");\n\t}\n\n\treturn trimmed;\n}\n\nfunction keyLooksLikeDateBoundary(\n\tkey: string,\n\tboundary: \"start\" | \"end\",\n): boolean {\n\treturn boundary === \"start\"\n\t\t? START_PARAM_KEY_REGEX.test(key)\n\t\t: END_PARAM_KEY_REGEX.test(key);\n}\n\nfunction hasTimeComponent(value: unknown): boolean {\n\treturn typeof value === \"string\" && /\\d{2}:\\d{2}:\\d{2}/.test(value);\n}\n\nfunction formatDateOverride(\n\tdateValue: string,\n\tboundary: \"start\" | \"end\",\n\texistingValue: unknown,\n): string {\n\t// Preserve date-only params as date-only values.\n\tif (SQL_DATE_RE.test(dateValue) && !hasTimeComponent(existingValue)) {\n\t\treturn dateValue;\n\t}\n\n\tif (SQL_DATE_RE.test(dateValue)) {\n\t\treturn `${dateValue} ${boundary === \"start\" ? \"00:00:00\" : \"23:59:59\"}`;\n\t}\n\n\tif (SQL_DATETIME_RE.test(dateValue)) {\n\t\treturn dateValue;\n\t}\n\n\t// Best effort fallback for already-normalized non-standard values.\n\treturn dateValue;\n}\n\nfunction normalizeGeneratedParamKey(\n\tparam: Record<string, unknown>,\n\tindex: number,\n): string {\n\tconst nameCandidate =\n\t\t(typeof param.name === \"string\" && param.name.trim()) ||\n\t\t(typeof param.placeholder === \"string\" && param.placeholder.trim()) ||\n\t\t(typeof param.position === \"number\" && String(param.position)) ||\n\t\tString(index + 1);\n\n\treturn nameCandidate\n\t\t.replace(/[{}]/g, \"\")\n\t\t.replace(/(.+):.*$/, \"$1\")\n\t\t.replace(/^[:$]/, \"\")\n\t\t.trim();\n}\n\nfunction collectParamKeys(\n\tparams: ParamRecord,\n\tparamMetadata: Array<Record<string, unknown>>,\n): string[] {\n\tconst keys = new Set<string>(Object.keys(params));\n\tfor (let i = 0; i < paramMetadata.length; i++) {\n\t\tconst param = paramMetadata[i];\n\t\tif (!param) continue;\n\t\tkeys.add(normalizeGeneratedParamKey(param, i));\n\t}\n\treturn [...keys];\n}\n\n/** `$1` → `1`, `{start_date:Date}` → `start_date`, `@start_date` → `start_date`. */\nfunction placeholderKey(token: string): string {\n\tif (token.startsWith(\"$\") || token.startsWith(\"@\")) return token.slice(1);\n\treturn token.slice(1, -1).replace(/:.+$/, \"\").trim();\n}\n\nfunction isDateLike(value: unknown): boolean {\n\treturn typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}/.test(value.trim());\n}\n\n/**\n * Param keys the SQL uses as a date bound (`created_at >= $1`, `{start_date:Date}`).\n * Postgres stores both the `$n` key it binds and a named alias; only the `$n` key\n * changes the query.\n */\nfunction dateBoundKeysFromSql(sql: string): {\n\tstart: Set<string>;\n\tend: Set<string>;\n} {\n\tconst start = new Set<string>();\n\tconst end = new Set<string>();\n\tconst comparison =\n\t\t/(>=|<=|>|<)\\s*(\\$\\d+|\\{[^}]+\\}|@[A-Za-z_][\\w]*)/g;\n\tconst between =\n\t\t/BETWEEN\\s+(\\$\\d+|\\{[^}]+\\}|@[A-Za-z_][\\w]*)\\s+AND\\s+(\\$\\d+|\\{[^}]+\\}|@[A-Za-z_][\\w]*)/gi;\n\n\tfor (const match of sql.matchAll(comparison)) {\n\t\tconst key = placeholderKey(match[2] ?? \"\");\n\t\tif (!key) continue;\n\t\tif (match[1] === \">=\" || match[1] === \">\") start.add(key);\n\t\telse end.add(key);\n\t}\n\n\tfor (const match of sql.matchAll(between)) {\n\t\tconst startKey = placeholderKey(match[1] ?? \"\");\n\t\tconst endKey = placeholderKey(match[2] ?? \"\");\n\t\tif (startKey) start.add(startKey);\n\t\tif (endKey) end.add(endKey);\n\t}\n\n\treturn { start, end };\n}\n\nfunction paramValueForKey(\n\tkey: string,\n\tparams: ParamRecord,\n\tparamMetadata: Array<Record<string, unknown>>,\n): unknown {\n\tif (key in params) return params[key];\n\tfor (let i = 0; i < paramMetadata.length; i++) {\n\t\tconst param = paramMetadata[i];\n\t\tif (!param) continue;\n\t\tif (normalizeGeneratedParamKey(param, i) === key) return param.value;\n\t}\n\treturn undefined;\n}\n\n/** Date-like params this range can overwrite: SQL placeholders plus named aliases. */\nfunction dateBoundKeys(\n\tsql: string,\n\tparams: ParamRecord,\n\tparamMetadata: Array<Record<string, unknown>>,\n\tboundary: \"start\" | \"end\",\n): string[] {\n\tconst fromSql = dateBoundKeysFromSql(sql)[boundary];\n\tconst keys: string[] = [];\n\tfor (const key of collectParamKeys(params, paramMetadata)) {\n\t\tconst named = keyLooksLikeDateBoundary(key, boundary);\n\t\tif (!named && !fromSql.has(key)) continue;\n\t\tif (!isDateLike(paramValueForKey(key, params, paramMetadata))) continue;\n\t\tkeys.push(key);\n\t}\n\treturn keys;\n}\n\n/**\n * True when date-range-only SQL mods can be applied by substituting existing\n * start/end params (no NL→SQL regeneration).\n */\nfunction canSubstituteDateRangeParams(\n\tdateRange: DateRangeInput | undefined,\n\tsql: string,\n\tparams: ParamRecord,\n\tparamMetadata: Array<Record<string, unknown>> = [],\n): boolean {\n\tif (!dateRange) return false;\n\tconst from = normalizeDateInput(dateRange.from);\n\tconst to = normalizeDateInput(dateRange.to);\n\tif (!from && !to) return false;\n\n\tif (from && dateBoundKeys(sql, params, paramMetadata, \"start\").length === 0) {\n\t\treturn false;\n\t}\n\tif (to && dateBoundKeys(sql, params, paramMetadata, \"end\").length === 0) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n/** Date-range only (no custom SQL, granularity, or extra NL instructions). */\nfunction isDateRangeOnlySqlModification(\n\tmodifications: SqlModifications | undefined,\n): boolean {\n\tif (!modifications?.dateRange) return false;\n\tif (modifications.customSql) return false;\n\tif (modifications.timeGranularity) return false;\n\tif (\n\t\ttypeof modifications.additionalInstructions === \"string\" &&\n\t\tmodifications.additionalInstructions.trim().length > 0\n\t) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nfunction applyDateRangeOverrides(\n\tdateRange: DateRangeInput | undefined,\n\tsql: string,\n\tparams: ParamRecord,\n\tparamMetadata: Array<Record<string, unknown>>,\n): void {\n\tif (!dateRange) return;\n\n\tconst from = normalizeDateInput(dateRange.from);\n\tconst to = normalizeDateInput(dateRange.to);\n\tif (!from && !to) return;\n\n\tconst startKeys = new Set(\n\t\tfrom ? dateBoundKeys(sql, params, paramMetadata, \"start\") : [],\n\t);\n\tconst endKeys = new Set(\n\t\tto ? dateBoundKeys(sql, params, paramMetadata, \"end\") : [],\n\t);\n\n\tfor (const [key, value] of Object.entries(params)) {\n\t\tif (startKeys.has(key)) {\n\t\t\tparams[key] = formatDateOverride(from ?? \"\", \"start\", value);\n\t\t}\n\t\tif (endKeys.has(key)) {\n\t\t\tparams[key] = formatDateOverride(to ?? \"\", \"end\", value);\n\t\t}\n\t}\n\n\tfor (let i = 0; i < paramMetadata.length; i++) {\n\t\tconst param = paramMetadata[i];\n\t\tif (!param) continue;\n\t\tconst key = normalizeGeneratedParamKey(param, i);\n\n\t\tif (startKeys.has(key)) {\n\t\t\tparam.value = formatDateOverride(from ?? \"\", \"start\", param.value);\n\t\t}\n\t\tif (endKeys.has(key)) {\n\t\t\tparam.value = formatDateOverride(to ?? \"\", \"end\", param.value);\n\t\t}\n\t}\n}\n\n/** Previous params for rewrite-datefilter: prefer metadata, else `{name,value}` entries. */\nfunction buildPreviousParams(\n\tparams: ParamRecord,\n\tparamMetadata: Array<Record<string, unknown>>,\n): Array<Record<string, unknown>> {\n\tif (paramMetadata.length > 0) return paramMetadata;\n\treturn Object.entries(params).map(([name, value]) => ({ name, value }));\n}\n\n/**\n * Builds viz modification hints for the chart generation endpoint.\n */\nfunction buildVizHints(\n\tmodifications: VizModifications,\n): Record<string, unknown> {\n\tconst hints: Record<string, unknown> = {};\n\n\tif (modifications.kind) {\n\t\thints.kind = modifications.kind;\n\t}\n\n\tif (modifications.chartType) {\n\t\thints.chartType = modifications.chartType;\n\t}\n\n\tif (modifications.xAxis) {\n\t\thints.xAxis = modifications.xAxis;\n\t}\n\n\tif (modifications.yAxis) {\n\t\thints.yAxis = modifications.yAxis;\n\t}\n\n\tif (modifications.series) {\n\t\thints.series = modifications.series;\n\t}\n\n\tif (modifications.stacking) {\n\t\thints.stacking = modifications.stacking;\n\t}\n\n\tif (modifications.limit !== undefined) {\n\t\thints.limit = modifications.limit;\n\t}\n\n\treturn hints;\n}\n\n/**\n * Remove VizSpec-only hints for Vega-Lite chart generation.\n */\nfunction stripVizSpecOnlyHints(\n\thints: Record<string, unknown>,\n): Record<string, unknown> {\n\tif (!(\"kind\" in hints)) {\n\t\treturn hints;\n\t}\n\n\tconst { kind: _kind, ...rest } = hints as { kind?: unknown };\n\treturn rest;\n}\n\n/**\n * Resolves tenant ID from options or client default.\n */\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n\tconst resolved = tenantId ?? client.getDefaultTenantId();\n\tif (!resolved) {\n\t\tthrow new Error(\n\t\t\t\"tenantId is required. Provide it per request or via defaultTenantId option.\",\n\t\t);\n\t}\n\treturn resolved;\n}\n\n// ============================================================================\n// Main Function\n// ============================================================================\n\n/**\n * Modifies a chart by regenerating SQL and/or applying visualization changes.\n *\n * This method supports three modes of operation:\n *\n * 1. **SQL Modifications**: When `sqlModifications` is provided, the SQL is\n * regenerated using the query endpoint with modification hints. If `customSql`\n * is set, it's used directly without regeneration.\n *\n * 2. **Visualization Modifications**: When only `vizModifications` is provided,\n * the existing SQL is re-executed and a new chart is generated with the\n * specified encoding preferences.\n *\n * 3. **Combined**: Both SQL and visualization modifications can be applied\n * together. SQL is regenerated first, then viz modifications are applied.\n *\n * @param client - The API client for making requests\n * @param queryEngine - The query engine for executing SQL\n * @param input - Chart modification input with source data and modifications\n * @param options - Optional settings for tenant, user, and chart generation\n * @param signal - Optional AbortSignal for cancellation\n * @returns Modified chart response with SQL, data, and chart specification\n *\n * @example\n * ```typescript\n * // Change chart type and axis\n * const modified = await modifyChart(client, engine, {\n * sql: response.sql,\n * question: \"revenue by country\",\n * database: \"analytics\",\n * vizModifications: {\n * chartType: \"bar\",\n * xAxis: { field: \"country\" },\n * yAxis: { field: \"revenue\", aggregate: \"sum\" },\n * },\n * }, { tenantId: \"tenant_123\" });\n *\n * // Change time granularity (triggers SQL regeneration)\n * const monthly = await modifyChart(client, engine, {\n * sql: response.sql,\n * question: \"revenue over time\",\n * database: \"analytics\",\n * sqlModifications: {\n * timeGranularity: \"month\",\n * dateRange: { from: \"2024-01-01\", to: \"2024-12-31\" },\n * },\n * }, { tenantId: \"tenant_123\" });\n * ```\n */\nexport async function modifyChart(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tinput: ChartModifyInput,\n\toptions?: ChartModifyOptions,\n\tsignal?: AbortSignal,\n): Promise<ChartModifyResponse> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst sessionId = crypto.randomUUID();\n\tconst querypanelSessionId = options?.querypanelSessionId ?? sessionId;\n\tconst hasQuerypanelSessionContext = !!options?.querypanelSessionId;\n\tconst chartType = options?.chartType ?? \"vega-lite\";\n\n\tconst hasSqlMods = !!input.sqlModifications;\n\tconst hasVizMods = !!input.vizModifications;\n\tconst hasCustomSql = !!input.sqlModifications?.customSql;\n\t// `lightweight` (pinned chart controls): the caller reapplies the previous\n\t// chart encoding, so date-only changes avoid NL→SQL and no chart LLM runs.\n\tconst lightweight = options?.lightweight === true;\n\tconst dateOnly =\n\t\tlightweight && isDateRangeOnlySqlModification(input.sqlModifications);\n\tconst skipChartLlm =\n\t\tlightweight && !hasCustomSql && (dateOnly || (hasVizMods && !hasSqlMods));\n\n\tresolveQueryPipeline(options?.pipeline);\n\tconst queryEndpoint = \"/v2/query\";\n\n\t// Determine which SQL to use. A queryId takes priority: the server-stored SQL and\n\t// params are used and any client-supplied sql / params are ignored.\n\tlet currentSql = input.sql;\n\tlet finalParams = input.params ?? {};\n\tlet paramMetadata: Array<Record<string, unknown>> = [];\n\tlet rationale: string | undefined;\n\tlet queryId: string | undefined;\n\tif (input.queryId) {\n\t\tconst resolved = await resolveQuery(\n\t\t\tclient,\n\t\t\tqueryEngine,\n\t\t\tinput.queryId,\n\t\t\t{ tenantId, userId: options?.userId, scopes: options?.scopes },\n\t\t\tsignal,\n\t\t);\n\t\tcurrentSql = resolved.sql;\n\t\tfinalParams = resolved.params;\n\t\tparamMetadata = resolved.paramMetadata;\n\t\tqueryId = resolved.queryId;\n\t}\n\tif (!currentSql) {\n\t\tthrow new Error(\"modifyChart requires either queryId or sql.\");\n\t}\n\tlet finalSql = currentSql;\n\tlet sqlChanged = false;\n\tlet finalQuestion = input.question;\n\n\t// Get database metadata for tenant settings\n\tconst databaseName = input.database ?? queryEngine.getDefaultDatabase();\n\tif (!databaseName) {\n\t\tthrow new Error(\n\t\t\t\"No database specified. Provide database in input or attach a default database.\",\n\t\t);\n\t}\n\n\tconst metadata = queryEngine.getDatabaseMetadata(databaseName);\n\tlet tenantSettings: Record<string, unknown> | undefined;\n\tif (metadata?.enforceTenantIsolation === false) {\n\t\ttenantSettings = {\n\t\t\ttenantFieldName: metadata.tenantFieldName ?? \"\",\n\t\t\ttenantFieldType: metadata.tenantFieldType ?? \"String\",\n\t\t\tenforceTenantIsolation: false,\n\t\t};\n\t} else if (metadata?.tenantFieldName) {\n\t\ttenantSettings = {\n\t\t\ttenantFieldName: metadata.tenantFieldName,\n\t\t\ttenantFieldType: metadata.tenantFieldType,\n\t\t\tenforceTenantIsolation: true,\n\t\t};\n\t}\n\n\t// Path 1: Custom SQL provided - use it directly\n\tif (hasCustomSql) {\n\t\tfinalSql = input.sqlModifications!.customSql!;\n\t\tfinalParams = {};\n\t\tparamMetadata = [];\n\t\t// Client-supplied SQL is not a server-generated query, so it has no id.\n\t\tqueryId = undefined;\n\t\tsqlChanged = true;\n\t}\n\t// Path 2a: Date-range only + existing start/end params → substitute, keep SQL\n\telse if (\n\t\tdateOnly &&\n\t\tcanSubstituteDateRangeParams(\n\t\t\tinput.sqlModifications?.dateRange,\n\t\t\tfinalSql,\n\t\t\tfinalParams,\n\t\t\tparamMetadata,\n\t\t)\n\t) {\n\t\tfinalParams = { ...finalParams };\n\t\tapplyDateRangeOverrides(\n\t\t\tinput.sqlModifications?.dateRange,\n\t\t\tfinalSql,\n\t\t\tfinalParams,\n\t\t\tparamMetadata,\n\t\t);\n\t\tsqlChanged = false;\n\t}\n\t// Path 2b: SQL modifications (non-custom) - regenerate via query endpoint,\n\t// or via the cheaper date-filter rewrite when only the date range changed.\n\telse if (hasSqlMods && !hasCustomSql) {\n\t\tconst modifiedQuestion = buildModifiedQuestion(\n\t\t\tinput.question,\n\t\t\tinput.sqlModifications!,\n\t\t\thasQuerypanelSessionContext,\n\t\t);\n\t\tfinalQuestion = modifiedQuestion;\n\n\t\tconst sharedBody = {\n\t\t\tsession_id: querypanelSessionId,\n\t\t\tprevious_sql: currentSql,\n\t\t\t...(tenantSettings ? { tenant_settings: tenantSettings } : {}),\n\t\t\t...(databaseName ? { database: databaseName } : {}),\n\t\t\t...(metadata?.dialect ? { dialect: metadata.dialect } : {}),\n\t\t\tdebug: options?.debug ?? false,\n\t\t};\n\t\tconst [endpoint, body] = dateOnly\n\t\t\t? [\n\t\t\t\t\tREWRITE_DATEFILTER_ENDPOINT,\n\t\t\t\t\t{\n\t\t\t\t\t\t...sharedBody,\n\t\t\t\t\t\tquestion: input.question,\n\t\t\t\t\t\tprevious_params: buildPreviousParams(finalParams, paramMetadata),\n\t\t\t\t\t\tdate_range: {\n\t\t\t\t\t\t\tfrom: normalizeDateInput(input.sqlModifications?.dateRange?.from),\n\t\t\t\t\t\t\tto: normalizeDateInput(input.sqlModifications?.dateRange?.to),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: [\n\t\t\t\t\tqueryEndpoint,\n\t\t\t\t\t{\n\t\t\t\t\t\t...sharedBody,\n\t\t\t\t\t\tquestion: modifiedQuestion,\n\t\t\t\t\t\t...(options?.maxRetry ? { max_retry: options.maxRetry } : {}),\n\t\t\t\t\t},\n\t\t\t\t];\n\n\t\tconst queryResponse = await client.post<ServerQueryResponse>(\n\t\t\tendpoint,\n\t\t\tbody,\n\t\t\ttenantId,\n\t\t\toptions?.userId,\n\t\t\toptions?.scopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t);\n\n\t\tfinalSql = queryResponse.sql;\n\t\tparamMetadata = Array.isArray(queryResponse.params)\n\t\t\t? queryResponse.params\n\t\t\t: [];\n\t\tfinalParams = queryEngine.mapGeneratedParams(paramMetadata);\n\t\tapplyDateRangeOverrides(\n\t\t\tinput.sqlModifications?.dateRange,\n\t\t\tfinalSql,\n\t\t\tfinalParams,\n\t\t\tparamMetadata,\n\t\t);\n\t\trationale = queryResponse.rationale;\n\t\tqueryId = queryResponse.queryId;\n\t\tsqlChanged = finalSql !== currentSql;\n\t}\n\n\t// Execute the SQL\n\tconst execution = await queryEngine.validateAndExecute(\n\t\tfinalSql,\n\t\tfinalParams,\n\t\tdatabaseName,\n\t\ttenantId,\n\t);\n\tconst rows = execution.rows ?? [];\n\n\t// Generate chart encoding even when the query returns no rows,\n\t// so headless clients can show \"No Data\" with the correct chart type.\n\tlet chart: ChartEnvelope = {\n\t\tspecType: chartType,\n\t\tnotes: rows.length === 0 ? \"Query returned no rows.\" : null,\n\t};\n\n\t// Build viz hints if modifications provided\n\tconst vizHints = hasVizMods ? buildVizHints(input.vizModifications!) : {};\n\tconst vizHintsForChart =\n\t\tchartType === \"vizspec\" ? vizHints : stripVizSpecOnlyHints(vizHints);\n\n\tif (!skipChartLlm && chartType === \"vizspec\") {\n\t\tconst vizspecResponse = await client.post<ServerVizSpecResponse>(\n\t\t\t\"/vizspec\",\n\t\t\t{\n\t\t\t\tquestion: finalQuestion,\n\t\t\t\tsql: finalSql,\n\t\t\t\trationale,\n\t\t\t\tfields: execution.fields,\n\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\tmax_retries: options?.chartMaxRetries ?? 3,\n\t\t\t\tquery_id: queryId,\n\t\t\t\t// Include viz hints for the chart generator\n\t\t\t\t...(hasVizMods ? { encoding_hints: vizHintsForChart } : {}),\n\t\t\t\t...(options?.supportedChartTypes?.length\n\t\t\t\t\t? { supported_chart_types: options.supportedChartTypes }\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t\ttenantId,\n\t\t\toptions?.userId,\n\t\t\toptions?.scopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t);\n\t\tthrowIfGenerationErrorResponse(vizspecResponse);\n\n\t\tchart = {\n\t\t\tvizSpec: vizspecResponse.spec,\n\t\t\tspecType: \"vizspec\",\n\t\t\tnotes:\n\t\t\t\trows.length === 0\n\t\t\t\t\t? (vizspecResponse.notes ?? \"Query returned no rows.\")\n\t\t\t\t\t: vizspecResponse.notes,\n\t\t};\n\t} else if (!skipChartLlm) {\n\t\tconst chartResponse = await client.post<ServerChartResponse>(\n\t\t\t\"/chart\",\n\t\t\t{\n\t\t\t\tquestion: finalQuestion,\n\t\t\t\tsql: finalSql,\n\t\t\t\trationale,\n\t\t\t\tfields: execution.fields,\n\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\tmax_retries: options?.chartMaxRetries ?? 3,\n\t\t\t\tquery_id: queryId,\n\t\t\t\t// Include viz hints for the chart generator\n\t\t\t\t...(hasVizMods ? { encoding_hints: vizHintsForChart } : {}),\n\t\t\t},\n\t\t\ttenantId,\n\t\t\toptions?.userId,\n\t\t\toptions?.scopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t);\n\t\tthrowIfGenerationErrorResponse(chartResponse);\n\n\t\tchart = {\n\t\t\tvegaLiteSpec: chartResponse.chart\n\t\t\t\t? {\n\t\t\t\t\t\t...chartResponse.chart,\n\t\t\t\t\t\tdata: { values: rows },\n\t\t\t\t\t}\n\t\t\t\t: null,\n\t\t\tspecType: \"vega-lite\",\n\t\t\tnotes:\n\t\t\t\trows.length === 0\n\t\t\t\t\t? (chartResponse.notes ?? \"Query returned no rows.\")\n\t\t\t\t\t: chartResponse.notes,\n\t\t};\n\t}\n\n\treturn {\n\t\tsql: finalSql,\n\t\tparams: finalParams,\n\t\tparamMetadata,\n\t\trationale,\n\t\tdialect: metadata?.dialect ?? \"unknown\",\n\t\tqueryId,\n\t\tquerypanelSessionId,\n\t\trows,\n\t\tfields: execution.fields,\n\t\tchart,\n\t\tattempts: 1,\n\t\ttarget_db: databaseName,\n\t\tmodified: {\n\t\t\tsqlChanged,\n\t\t\tvizChanged: hasVizMods,\n\t\t},\n\t};\n}\n","import crypto from \"node:crypto\";\nimport type { IQueryPanelApi } from \"../core/api-types\";\nimport type { ParamRecord, QueryEngine } from \"../core/query-engine\";\nimport { type QueryErrorCode, QueryPipelineError, isAiProviderPipelineError, throwIfGenerationErrorResponse } from \"../errors\";\nimport type { ChartType, VizSpec } from \"../types/vizspec\";\n\n/**\n * Context document returned by the query pipeline.\n */\nexport interface ContextDocument {\n\t/** Optional source identifier for the document. */\n\tsource?: string;\n\t/** Raw document content or excerpt. */\n\tpageContent: string;\n\t/** Additional metadata attached to the document. */\n\tmetadata?: Record<string, unknown>;\n\t/** Optional relevance score from retrieval. */\n\tscore?: number;\n}\n\n/**\n * Chart payload returned with query results.\n */\nexport interface ChartEnvelope {\n\t/** Vega-Lite spec when specType is \"vega-lite\". */\n\tvegaLiteSpec?: Record<string, unknown> | null;\n\t/** VizSpec payload when specType is \"vizspec\". */\n\tvizSpec?: VizSpec | null;\n\t/** Chart specification type. */\n\tspecType: \"vega-lite\" | \"vizspec\";\n\t/** Optional chart generation notes or errors. */\n\tnotes: string | null;\n}\n\n/**\n * Configuration options for query generation.\n */\nexport interface AskOptions {\n\t/** Tenant identifier for scoped access. */\n\ttenantId?: string;\n\t/** Optional user identifier for audit/telemetry. */\n\tuserId?: string;\n\t/** Optional scopes to include in the auth token. */\n\tscopes?: string[];\n\t/** Override the default database name. */\n\tdatabase?: string;\n\t/** Previous error message for retry context. */\n\tlastError?: string;\n\t/** Previous SQL statement for retry context. */\n\tpreviousSql?: string;\n\t/** Maximum number of retry attempts on execution failure. */\n\tmaxRetry?: number;\n\t/** Maximum number of retries for chart generation. */\n\tchartMaxRetries?: number;\n\t/** Choose chart generation method. */\n\tchartType?: \"vega-lite\" | \"vizspec\";\n\t/**\n\t * QueryPanel session ID for context-aware follow-ups.\n\t * Use this to reuse a previously returned session for follow-up prompts.\n\t */\n\tquerypanelSessionId?: string;\n\t/**\n\t * Query pipeline. Only `\"v2\"` is accepted (`POST /v2/query`).\n\t * `\"v1\"` was removed: passing it fails typecheck and throws at runtime.\n\t * Omit this field; asks always use v2.\n\t */\n\tpipeline?: \"v2\";\n\t/**\n\t * Optional model id for SQL generation (e.g. OpenAI or `gemini-*` when the API uses Gemini). Omit for server default.\n\t */\n\tmodel?: string;\n\t/**\n\t * Optional additional system prompt text for the v2 pipeline.\n\t *\n\t * This is appended to the backend's SQL generator/reflection system prompts\n\t * (it does not change tenant isolation mechanics; it only guides the LLM).\n\t *\n\t * Use this for tenant-specific policies (e.g. retention windows) that must\n\t * be enforced on every query.\n\t *\n\t * Sent on every ask (`POST /v2/query`).\n\t */\n\tsystemPrompt?: string;\n\t/**\n\t * Restrict VizSpec `chartType` values when `chartType` is `\"vizspec\"`.\n\t * Sent to `POST /vizspec` as `supported_chart_types`. Omitted allows all standard types.\n\t */\n\tsupportedChartTypes?: ChartType[];\n\t/**\n\t * When true, rationale includes engineer-style schema detail (tables, columns).\n\t * When false (default), rationale is client-safe for end users.\n\t */\n\tdebug?: boolean;\n}\n\n/**\n * Intent analysis result from the v2 pipeline.\n */\nexport interface IntentResult {\n\tintent: string;\n\tconfidence: number;\n\tplan: {\n\t\ttables: string[];\n\t\toperations: string[];\n\t\tfilters: string[];\n\t\torderBy?: string;\n\t\tlimit?: number;\n\t};\n\tambiguities: Array<{ issue: string; suggestion: string }>;\n}\n\n/**\n * Pipeline execution trace with step-level timing.\n */\nexport interface PipelineTrace {\n\ttotalDurationMs: number;\n\tsteps: Array<{ step: string; durationMs: number }>;\n}\n\n/**\n * Response returned after executing a query.\n */\nexport interface AskResponse {\n\t/** Generated SQL statement. */\n\tsql: string;\n\t/** Parameter values for the generated SQL. */\n\tparams: ParamRecord;\n\t/** Raw parameter metadata from the backend. */\n\tparamMetadata: Array<Record<string, unknown>>;\n\t/** Optional reasoning for SQL generation. */\n\trationale?: string;\n\t/** SQL dialect selected by the backend. */\n\tdialect: string;\n\t/** Optional query identifier for traceability. */\n\tqueryId?: string;\n\t/** Result rows returned by the query execution. */\n\trows: Array<Record<string, unknown>>;\n\t/** Column names for returned rows. */\n\tfields: string[];\n\t/** Generated chart payload. */\n\tchart: ChartEnvelope;\n\t/** Optional context documents used for query generation. */\n\tcontext?: ContextDocument[];\n\t/** Number of attempts used for execution. */\n\tattempts?: number;\n\t/** Target database name resolved by the backend or engine. */\n\ttarget_db?: string;\n\t/** QueryPanel session ID for follow-up queries. */\n\tquerypanelSessionId?: string;\n\t/** Intent analysis from v2 pipeline. */\n\tintent?: IntentResult;\n\t/** Pipeline execution trace from v2 pipeline. */\n\ttrace?: PipelineTrace;\n}\n\ninterface ServerQueryResponse {\n\tsuccess: boolean;\n\tsql?: string;\n\tparams?: Array<Record<string, unknown>>;\n\tdialect?: string;\n\tdatabase?: string;\n\ttable?: string;\n\trationale?: string;\n\tqueryId?: string;\n\tcontext?: ContextDocument[];\n\t// v2-specific fields\n\tintent?: IntentResult;\n\ttrace?: PipelineTrace;\n\t// Error fields\n\terror?: string;\n\tcode?: QueryErrorCode;\n\tdetails?: Record<string, unknown>;\n}\n\ninterface ServerChartResponse {\n\tchart: Record<string, unknown> | null;\n\tnotes: string | null;\n}\n\ninterface ServerVizSpecResponse {\n\tspec: VizSpec;\n\tnotes: string | null;\n}\n\n/**\n * Resolve ask/modify pipeline. Always `\"v2\"` (`POST /v2/query`).\n * Throws if a removed pipeline such as `\"v1\"` is passed.\n */\nexport function resolveQueryPipeline(pipeline?: \"v2\"): \"v2\" {\n\tconst requested: string | undefined = pipeline;\n\tif (requested != null && requested !== \"v2\") {\n\t\tthrow new Error(\n\t\t\t`Query pipeline \"${requested}\" was removed. ask() and modifyChart() always use POST /v2/query.`,\n\t\t);\n\t}\n\treturn \"v2\";\n}\n\n/**\n * Route module for natural language query generation\n * Simple orchestration following Ousterhout's principle\n */\nexport async function ask(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tquestion: string,\n\toptions: AskOptions,\n\tsignal?: AbortSignal,\n): Promise<AskResponse> {\n\tconst tenantId = resolveTenantId(client, options.tenantId);\n\tconst sessionId = crypto.randomUUID();\n\tconst querypanelSessionId = options.querypanelSessionId ?? sessionId;\n\tconst maxRetry = options.maxRetry ?? 0;\n\tlet attempt = 0;\n\tlet lastError: string | undefined = options.lastError;\n\tlet previousSql: string | undefined = options.previousSql;\n\tlet parentQueryId: string | undefined;\n\n\tresolveQueryPipeline(options.pipeline);\n\tconst queryEndpoint = \"/v2/query\";\n\n\twhile (attempt <= maxRetry) {\n\t\t// Step 1: Get SQL from backend\n\t\tconst databaseName = options.database ?? queryEngine.getDefaultDatabase();\n\t\tconst metadata = databaseName\n\t\t\t? queryEngine.getDatabaseMetadata(databaseName)\n\t\t\t: undefined;\n\n\t\tlet tenantSettings: Record<string, unknown> | undefined;\n\t\tif (metadata?.enforceTenantIsolation === false) {\n\t\t\ttenantSettings = {\n\t\t\t\ttenantFieldName: metadata.tenantFieldName ?? \"\",\n\t\t\t\ttenantFieldType: metadata.tenantFieldType ?? \"String\",\n\t\t\t\tenforceTenantIsolation: false,\n\t\t\t};\n\t\t} else if (metadata?.tenantFieldName) {\n\t\t\ttenantSettings = {\n\t\t\t\ttenantFieldName: metadata.tenantFieldName,\n\t\t\t\ttenantFieldType: metadata.tenantFieldType,\n\t\t\t\tenforceTenantIsolation: true,\n\t\t\t};\n\t\t}\n\n\t\tconst queryResponse = await client.postWithHeaders<ServerQueryResponse>(\n\t\t\tqueryEndpoint,\n\t\t\t{\n\t\t\t\tquestion,\n\t\t\t\t...(querypanelSessionId ? { session_id: querypanelSessionId } : {}),\n\t\t\t\t...(lastError ? { last_error: lastError } : {}),\n\t\t\t\t...(previousSql ? { previous_sql: previousSql } : {}),\n\t\t\t\t...(options.maxRetry ? { max_retry: options.maxRetry } : {}),\n\t\t\t\t...(tenantSettings ? { tenant_settings: tenantSettings } : {}),\n\t\t\t\t...(databaseName ? { database: databaseName } : {}),\n\t\t\t\t...(metadata?.dialect ? { dialect: metadata.dialect } : {}),\n\t\t\t\t...(options.model?.trim() ? { model: options.model.trim() } : {}),\n\t\t\t\t...(options.systemPrompt?.trim()\n\t\t\t\t\t? { system_prompt: options.systemPrompt.trim() }\n\t\t\t\t\t: {}),\n\t\t\t\t...(parentQueryId ? { parent_query_id: parentQueryId } : {}),\n\t\t\t\tdebug: options.debug ?? false,\n\t\t\t},\n\t\t\ttenantId,\n\t\t\toptions.userId,\n\t\t\toptions.scopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t);\n\t\tconst responseSessionId =\n\t\t\tqueryResponse.headers.get(\"x-querypanel-session-id\") ??\n\t\t\tquerypanelSessionId;\n\n\t\t// Handle pipeline errors from server\n\t\tif (!queryResponse.data.success) {\n\t\t\tthrow new QueryPipelineError(\n\t\t\t\tqueryResponse.data.error || \"Query generation failed\",\n\t\t\t\tqueryResponse.data.code || \"INTERNAL_ERROR\",\n\t\t\t\tqueryResponse.data.details,\n\t\t\t);\n\t\t}\n\n\t\tconst sql = queryResponse.data.sql;\n\t\tconst dialect = queryResponse.data.dialect;\n\t\tif (!sql || !dialect) {\n\t\t\tthrow new Error(\"Query response missing required SQL or dialect\");\n\t\t}\n\n\t\tif (queryResponse.data.queryId) {\n\t\t\tparentQueryId = queryResponse.data.queryId;\n\t\t}\n\n\t\tconst dbName =\n\t\t\tqueryResponse.data.database ??\n\t\t\toptions.database ??\n\t\t\tqueryEngine.getDefaultDatabase();\n\t\tif (!dbName) {\n\t\t\tthrow new Error(\n\t\t\t\t\"No database attached. Call attachPostgres, attachClickhouse, or attachBigQuery first.\",\n\t\t\t);\n\t\t}\n\n\t\t// Step 2: Map and validate parameters\n\t\tconst paramMetadata = Array.isArray(queryResponse.data.params)\n\t\t\t? queryResponse.data.params\n\t\t\t: [];\n\t\tconst paramValues = queryEngine.mapGeneratedParams(paramMetadata);\n\n\t\t// Step 3: Execute SQL with tenant isolation\n\t\ttry {\n\t\t\tconst execution = await queryEngine.validateAndExecute(\n\t\t\t\tsql,\n\t\t\t\tparamValues,\n\t\t\t\tdbName,\n\t\t\t\ttenantId,\n\t\t\t);\n\t\t\tconst rows = execution.rows ?? [];\n\n\t\t\t// Step 4: Generate chart encoding even when the query returns no rows,\n\t\t\t// so headless clients can show \"No Data\" with the correct chart type.\n\t\t\tconst chartType = options.chartType ?? \"vega-lite\"; // Default to vega-lite for backward compatibility\n\t\t\tlet chart: ChartEnvelope = {\n\t\t\t\tspecType: chartType,\n\t\t\t\tnotes: rows.length === 0 ? \"Query returned no rows.\" : null,\n\t\t\t};\n\n\t\t\tif (chartType === \"vizspec\") {\n\t\t\t\t// Use new VizSpec generation\n\t\t\t\tconst vizspecResponse = await client.post<ServerVizSpecResponse>(\n\t\t\t\t\t\"/vizspec\",\n\t\t\t\t\t{\n\t\t\t\t\t\tquestion,\n\t\t\t\t\t\tsql,\n\t\t\t\t\t\trationale: queryResponse.data.rationale,\n\t\t\t\t\t\tfields: execution.fields,\n\t\t\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\t\t\tmax_retries: options.chartMaxRetries ?? 3,\n\t\t\t\t\t\tquery_id: queryResponse.data.queryId,\n\t\t\t\t\t\t...(options.supportedChartTypes?.length\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tsupported_chart_types: options.supportedChartTypes,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t},\n\t\t\t\t\ttenantId,\n\t\t\t\t\toptions.userId,\n\t\t\t\t\toptions.scopes,\n\t\t\t\t\tsignal,\n\t\t\t\t\tsessionId,\n\t\t\t\t);\n\t\t\t\tthrowIfGenerationErrorResponse(vizspecResponse);\n\n\t\t\t\tchart = {\n\t\t\t\t\tvizSpec: vizspecResponse.spec,\n\t\t\t\t\tspecType: \"vizspec\",\n\t\t\t\t\tnotes:\n\t\t\t\t\t\trows.length === 0\n\t\t\t\t\t\t\t? (vizspecResponse.notes ?? \"Query returned no rows.\")\n\t\t\t\t\t\t\t: vizspecResponse.notes,\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\t// Use traditional Vega-Lite chart generation\n\t\t\t\tconst chartResponse = await client.post<ServerChartResponse>(\n\t\t\t\t\t\"/chart\",\n\t\t\t\t\t{\n\t\t\t\t\t\tquestion,\n\t\t\t\t\t\tsql,\n\t\t\t\t\t\trationale: queryResponse.data.rationale,\n\t\t\t\t\t\tfields: execution.fields,\n\t\t\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\t\t\tmax_retries: options.chartMaxRetries ?? 3,\n\t\t\t\t\t\tquery_id: queryResponse.data.queryId,\n\t\t\t\t\t},\n\t\t\t\t\ttenantId,\n\t\t\t\t\toptions.userId,\n\t\t\t\t\toptions.scopes,\n\t\t\t\t\tsignal,\n\t\t\t\t\tsessionId,\n\t\t\t\t);\n\t\t\t\tthrowIfGenerationErrorResponse(chartResponse);\n\n\t\t\t\tchart = {\n\t\t\t\t\tvegaLiteSpec: chartResponse.chart\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t...chartResponse.chart,\n\t\t\t\t\t\t\t\tdata: { values: rows },\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: null,\n\t\t\t\t\tspecType: \"vega-lite\",\n\t\t\t\t\tnotes:\n\t\t\t\t\t\trows.length === 0\n\t\t\t\t\t\t\t? (chartResponse.notes ?? \"Query returned no rows.\")\n\t\t\t\t\t\t\t: chartResponse.notes,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsql,\n\t\t\t\tparams: paramValues,\n\t\t\t\tparamMetadata,\n\t\t\t\trationale: queryResponse.data.rationale,\n\t\t\t\tdialect,\n\t\t\t\tqueryId: queryResponse.data.queryId,\n\t\t\t\trows,\n\t\t\t\tfields: execution.fields,\n\t\t\t\tchart,\n\t\t\t\tcontext: queryResponse.data.context,\n\t\t\t\tattempts: attempt + 1,\n\t\t\t\ttarget_db: dbName,\n\t\t\t\tquerypanelSessionId: responseSessionId ?? undefined,\n\t\t\t\tintent: queryResponse.data.intent,\n\t\t\t\ttrace: queryResponse.data.trace,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (isAiProviderPipelineError(error)) {\n\t\t\t\tif (error instanceof QueryPipelineError) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tconst providerError = error as Error & { code: QueryErrorCode };\n\t\t\t\tthrow new QueryPipelineError(providerError.message, providerError.code);\n\t\t\t}\n\n\t\t\tattempt++;\n\n\t\t\t// If we've exhausted all retries, throw the error\n\t\t\tif (attempt > maxRetry) {\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\t// Save error and SQL for next retry\n\t\t\tlastError = error instanceof Error ? error.message : String(error);\n\t\t\tpreviousSql = queryResponse.data.sql ?? previousSql;\n\n\t\t\t// Log retry attempt\n\t\t\tconsole.warn(\"SQL execution failed, retrying query generation\", {\n\t\t\t\tattempt,\n\t\t\t\tmaxAttempts: maxRetry + 1,\n\t\t\t\terror: lastError,\n\t\t\t\tsql: queryResponse.data.sql ?? null,\n\t\t\t\tparamMetadata,\n\t\t\t\tparams: paramValues,\n\t\t\t});\n\t\t}\n\t}\n\n\t// This should never be reached, but TypeScript needs it\n\tthrow new Error(\"Unexpected error in ask retry loop\");\n}\n\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n\tconst resolved = tenantId ?? client.getDefaultTenantId();\n\tif (!resolved) {\n\t\tthrow new Error(\n\t\t\t\"tenantId is required. Provide it per request or via defaultTenantId option.\",\n\t\t);\n\t}\n\treturn resolved;\n}\n\nexport function anonymizeResults(\n\trows: Array<Record<string, unknown>>,\n): Array<Record<string, string>> {\n\tif (!rows?.length) return [];\n\treturn rows.map((row) => {\n\t\tconst masked: Record<string, string> = {};\n\t\tObject.entries(row).forEach(([key, value]) => {\n\t\t\tif (value === null) masked[key] = \"null\";\n\t\t\telse if (Array.isArray(value)) masked[key] = \"array\";\n\t\t\telse masked[key] = typeof value;\n\t\t});\n\t\treturn masked;\n\t});\n}\n","import type { IQueryPanelApi } from \"../core/api-types\";\nimport type { ParamRecord, QueryEngine } from \"../core/query-engine\";\n\n/**\n * A query previously generated by the server (`ask()` / `modifyChart()` return its `queryId`).\n */\nexport interface ResolvedQuery {\n\tqueryId: string;\n\tsql: string;\n\t/** Bind values keyed for the attached adapter (numeric + named keys). */\n\tparams: ParamRecord;\n\t/** Raw parameter metadata as stored by the server. */\n\tparamMetadata: Array<Record<string, unknown>>;\n\tdialect: string;\n}\n\ninterface ServerResolvedQuery {\n\tqueryId: string;\n\tsql: string;\n\tparams?: Array<Record<string, unknown>>;\n\tdialect: string;\n}\n\nexport interface ResolveQueryOptions {\n\ttenantId: string;\n\tuserId?: string;\n\tscopes?: string[];\n}\n\n/**\n * Loads the server-stored SQL and bind params for a `queryId`.\n *\n * The lookup is scoped by the authenticated organization and tenant on the server, so an\n * id from another tenant (or one that never existed) is rejected. Callers should use the\n * returned SQL/params and ignore any client-supplied values.\n */\nexport async function resolveQuery(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tqueryId: string,\n\toptions: ResolveQueryOptions,\n\tsignal?: AbortSignal,\n): Promise<ResolvedQuery> {\n\tconst response = await client.get<ServerResolvedQuery>(\n\t\t`/v2/queries/${encodeURIComponent(queryId)}`,\n\t\toptions.tenantId,\n\t\toptions.userId,\n\t\toptions.scopes,\n\t\tsignal,\n\t);\n\n\tconst paramMetadata = Array.isArray(response.params) ? response.params : [];\n\treturn {\n\t\tqueryId: response.queryId,\n\t\tsql: response.sql,\n\t\tparams: queryEngine.mapGeneratedParams(paramMetadata),\n\t\tparamMetadata,\n\t\tdialect: response.dialect,\n\t};\n}\n\n/**\n * Mark a stored query as EXECUTED after local SQL succeeds.\n * Best-effort: never fails the caller. No-op when the row is already SUCCESS.\n */\nexport async function markQueryExecuted(\n\tclient: IQueryPanelApi,\n\tqueryId: string,\n\toptions: ResolveQueryOptions,\n\tsignal?: AbortSignal,\n): Promise<void> {\n\ttry {\n\t\tawait client.post(\n\t\t\t`/v2/queries/${encodeURIComponent(queryId)}/executed`,\n\t\t\t{},\n\t\t\toptions.tenantId,\n\t\t\toptions.userId,\n\t\t\toptions.scopes,\n\t\t\tsignal,\n\t\t);\n\t} catch {\n\t\t// Headless metrics must not break execution.\n\t}\n}\n","import type { IQueryPanelApi } from \"../core/api-types\";\n\n/**\n * A single session turn containing the question and optional SQL output.\n */\nexport interface SdkSessionTurn {\n\tid: string;\n\tsession_id: string;\n\tturn_index: number;\n\tquestion: string;\n\tsql: string | null;\n\trationale: string | null;\n\trow_count: number | null;\n\tfields: string[] | null;\n\terror: string | null;\n\tcreated_at: string;\n}\n\n/**\n * Session metadata with optional turn history.\n */\nexport interface SdkSession {\n\tid: string;\n\tsession_id: string;\n\torganization_id: string;\n\ttenant_id: string | null;\n\tuser_id: string | null;\n\ttitle: string | null;\n\tcreated_at: string;\n\tupdated_at: string;\n\tturns?: SdkSessionTurn[];\n}\n\n/**\n * Fields allowed when updating a session.\n */\nexport interface SessionUpdateInput {\n\ttitle?: string;\n}\n\n/**\n * Pagination settings for list endpoints.\n */\nexport interface PaginationQuery {\n\tpage?: number;\n\tlimit?: number;\n}\n\n/**\n * Pagination metadata returned by list endpoints.\n */\nexport interface PaginationInfo {\n\tpage: number;\n\tlimit: number;\n\ttotal: number;\n\ttotalPages: number;\n\thasNext: boolean;\n\thasPrev: boolean;\n}\n\n/**\n * Generic paginated response wrapper.\n */\nexport interface PaginatedResponse<T> {\n\tdata: T[];\n\tpagination: PaginationInfo;\n}\n\n/**\n * Options for listing sessions with filters and pagination.\n */\nexport interface SessionListOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n\tpagination?: PaginationQuery;\n\tsortBy?: \"title\" | \"user_id\" | \"created_at\" | \"updated_at\";\n\tsortDir?: \"asc\" | \"desc\";\n\ttitle?: string;\n\tuserFilter?: string;\n\tcreatedFrom?: string;\n\tcreatedTo?: string;\n\tupdatedFrom?: string;\n\tupdatedTo?: string;\n}\n\n/**\n * Options for retrieving a session.\n */\nexport interface SessionGetOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n\tincludeTurns?: boolean;\n}\n\ninterface RequestOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n}\n\n/**\n * Route module for Session history CRUD operations.\n */\n/**\n * Lists sessions with optional filtering and pagination.\n */\nexport async function listSessions(\n\tclient: IQueryPanelApi,\n\toptions?: SessionListOptions,\n\tsignal?: AbortSignal,\n): Promise<PaginatedResponse<SdkSession>> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\tif (options?.pagination?.page)\n\t\tparams.set(\"page\", `${options.pagination.page}`);\n\tif (options?.pagination?.limit)\n\t\tparams.set(\"limit\", `${options.pagination.limit}`);\n\tif (options?.sortBy) params.set(\"sort_by\", options.sortBy);\n\tif (options?.sortDir) params.set(\"sort_dir\", options.sortDir);\n\tif (options?.title) params.set(\"title\", options.title);\n\tif (options?.userFilter) params.set(\"user_id\", options.userFilter);\n\tif (options?.createdFrom) params.set(\"created_from\", options.createdFrom);\n\tif (options?.createdTo) params.set(\"created_to\", options.createdTo);\n\tif (options?.updatedFrom) params.set(\"updated_from\", options.updatedFrom);\n\tif (options?.updatedTo) params.set(\"updated_to\", options.updatedTo);\n\n\treturn await client.get<PaginatedResponse<SdkSession>>(\n\t\t`/sessions${params.toString() ? `?${params.toString()}` : \"\"}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Retrieves a session by session_id, optionally including turns.\n */\nexport async function getSession(\n\tclient: IQueryPanelApi,\n\tsessionId: string,\n\toptions?: SessionGetOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkSession> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\tif (options?.includeTurns !== undefined) {\n\t\tparams.set(\"include_turns\", `${options.includeTurns}`);\n\t}\n\n\treturn await client.get<SdkSession>(\n\t\t`/sessions/${encodeURIComponent(sessionId)}${params.toString() ? `?${params.toString()}` : \"\"}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Updates a session's metadata.\n */\nexport async function updateSession(\n\tclient: IQueryPanelApi,\n\tsessionId: string,\n\tbody: SessionUpdateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkSession> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.patch<SdkSession>(\n\t\t`/sessions/${encodeURIComponent(sessionId)}`,\n\t\tbody,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Deletes a session and its turn history.\n */\nexport async function deleteSession(\n\tclient: IQueryPanelApi,\n\tsessionId: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<void> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tawait client.delete<void>(\n\t\t`/sessions/${encodeURIComponent(sessionId)}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n\tconst resolved = tenantId ?? client.getDefaultTenantId();\n\tif (!resolved) {\n\t\tthrow new Error(\n\t\t\t\"tenantId is required. Provide it per request or via defaultTenantId option.\",\n\t\t);\n\t}\n\treturn resolved;\n}\n","import crypto from 'node:crypto';\nimport type { IQueryPanelApi } from \"../core/api-types\";\nimport type { ChartType, EncodingHints, VizSpec } from \"../types/vizspec\";\n\nexport interface VizSpecGenerateInput {\n question: string;\n sql: string;\n rationale?: string;\n fields: string[];\n rows: Array<Record<string, unknown>>;\n max_retries?: number;\n query_id?: string;\n /**\n * Optional encoding hints for visualization modification.\n * When provided, these guide the LLM to generate specific visualization configurations.\n */\n encoding_hints?: EncodingHints;\n /** When set, VizSpec kind \"chart\" may only use these chart types (API: supported_chart_types). */\n supported_chart_types?: ChartType[];\n}\n\nexport interface VizSpecGenerateOptions {\n tenantId?: string;\n userId?: string;\n scopes?: string[];\n maxRetries?: number;\n}\n\nexport interface VizSpecResponse {\n spec: VizSpec;\n notes: string | null;\n}\n\n/**\n * Route module for VizSpec generation\n * Calls the /vizspec endpoint to generate visualization specifications\n */\nexport async function generateVizSpec(\n client: IQueryPanelApi,\n input: VizSpecGenerateInput,\n options?: VizSpecGenerateOptions,\n signal?: AbortSignal,\n): Promise<VizSpecResponse> {\n const tenantId = resolveTenantId(client, options?.tenantId);\n const sessionId = crypto.randomUUID();\n\n const response = await client.post<VizSpecResponse>(\n \"/vizspec\",\n {\n question: input.question,\n sql: input.sql,\n rationale: input.rationale,\n fields: input.fields,\n rows: input.rows,\n max_retries: options?.maxRetries ?? input.max_retries ?? 3,\n query_id: input.query_id,\n encoding_hints: input.encoding_hints,\n ...(input.supported_chart_types?.length\n ? { supported_chart_types: input.supported_chart_types }\n : {}),\n },\n tenantId,\n options?.userId,\n options?.scopes,\n signal,\n sessionId,\n );\n\n return response;\n}\n\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n const resolved = tenantId ?? client.getDefaultTenantId();\n if (!resolved) {\n throw new Error(\n \"tenantId is required. Provide it per request or via defaultTenantId option.\",\n );\n }\n return resolved;\n}\n","/**\n * VizSpec types for flexible visualization specifications\n * Supports chart, table, and metric visualizations\n */\n\n// ============================================================================\n// Field Types and Formatting\n// ============================================================================\n\nexport type FieldType =\n | 'quantitative' // numbers you sum/avg\n | 'temporal' // dates/times\n | 'ordinal' // ordered categories (1st, 2nd, 3rd)\n | 'nominal' // unordered categories (names, IDs)\n | 'boolean';\n\nexport interface ValueFormat {\n style?: 'number' | 'currency' | 'percent' | 'date' | 'time' | 'datetime';\n currency?: string; // e.g. \"USD\"\n minimumFractionDigits?: number;\n maximumFractionDigits?: number;\n dateStyle?: 'short' | 'medium' | 'long';\n}\n\nexport interface FieldRef {\n field: string; // column name from the SQL result\n label?: string; // human-friendly label\n type?: FieldType;\n format?: ValueFormat; // optional formatting hint\n}\n\nexport type AggregateOp = 'sum' | 'avg' | 'min' | 'max' | 'count' | 'distinct';\n\nexport type TimeUnit = 'year' | 'quarter' | 'month' | 'week' | 'day' | 'hour' | 'minute';\n\nexport interface AxisField extends FieldRef {\n aggregate?: AggregateOp; // usually on y\n timeUnit?: TimeUnit; // usually on temporal x\n}\n\nexport interface MetricField extends FieldRef {\n aggregate?: AggregateOp;\n}\n\nexport interface SortSpec {\n field: string; // column to sort by\n direction?: 'asc' | 'desc'; // default \"asc\"\n}\n\n// ============================================================================\n// Base Spec\n// ============================================================================\n\nexport interface VizSpecBase {\n version: '1.0';\n kind: 'chart' | 'table' | 'metric';\n title?: string;\n description?: string;\n data: {\n sourceId: string; // Identifier for the SQL result this spec refers to\n };\n}\n\nexport type VizSpecKind = VizSpecBase[\"kind\"];\n\n// ============================================================================\n// Chart Spec\n// ============================================================================\n\nexport type ChartType =\n | 'line'\n | 'bar' // horizontal bars (categorical on Y-axis)\n | 'column' // vertical bars (categorical on X-axis)\n | 'area'\n | 'scatter'\n | 'pie';\n\n/** Full VizSpec chart catalog; use when building a custom `supportedChartTypes` list. */\nexport const ALL_VIZ_CHART_TYPES: readonly ChartType[] = [\n 'line',\n 'bar',\n 'column',\n 'area',\n 'scatter',\n 'pie',\n] as const;\n\nexport type StackingMode = 'none' | 'stacked' | 'percent';\n\nexport interface ChartEncoding {\n chartType: ChartType;\n\n // Axes / measure fields\n x?: AxisField; // usually categorical or temporal\n y?: AxisField | AxisField[]; // one or more measures\n\n // Optional: split into multiple series (colors/lines)\n series?: FieldRef; // e.g. \"region\"\n\n // Optional: how to stack if multiple series on same axis\n stacking?: StackingMode;\n\n // Display-only concerns\n sort?: SortSpec; // order of categories / points\n limit?: number; // max rows to display\n\n // Tooltip fields to show on hover\n tooltips?: FieldRef[];\n}\n\nexport interface ChartSpec extends VizSpecBase {\n kind: 'chart';\n encoding: ChartEncoding;\n}\n\n// ============================================================================\n// Table Spec\n// ============================================================================\n\nexport type TextAlign = 'left' | 'right' | 'center';\n\nexport interface TableColumn extends FieldRef {\n width?: number; // optional, in px\n align?: TextAlign; // default: left; numeric often right\n isHidden?: boolean; // if true, don't render this column\n}\n\nexport interface TableEncoding {\n columns: TableColumn[];\n sort?: SortSpec;\n limit?: number; // max rows to show (e.g., top 50)\n}\n\nexport interface TableSpec extends VizSpecBase {\n kind: 'table';\n encoding: TableEncoding;\n}\n\n// ============================================================================\n// Metric Spec\n// ============================================================================\n\nexport type ComparisonMode = 'delta' | 'deltaPercent' | 'ratio';\n\nexport interface MetricTrend {\n timeField: AxisField; // e.g. \"date\"\n valueField: MetricField; // value over time for sparkline\n}\n\nexport interface MetricEncoding {\n valueField: MetricField; // main KPI\n\n // optional comparison (e.g. vs previous period)\n comparisonField?: MetricField;\n comparisonMode?: ComparisonMode; // default could be \"deltaPercent\"\n\n // optional tiny timeseries/sparkline\n trend?: MetricTrend;\n}\n\nexport interface MetricSpec extends VizSpecBase {\n kind: 'metric';\n encoding: MetricEncoding;\n}\n\n// ============================================================================\n// Union Type\n// ============================================================================\n\nexport type VizSpec = ChartSpec | TableSpec | MetricSpec;\n\n// ============================================================================\n// Service Input/Output Types\n// ============================================================================\n\n/**\n * Encoding hints for vizspec modifications.\n * These hints guide the LLM to generate specific visualization configurations.\n */\nexport interface EncodingHints {\n /** Preferred vizspec kind (chart, table, metric) */\n kind?: 'chart' | 'table' | 'metric';\n /** Preferred chart type (bar, line, area, scatter, pie) */\n chartType?: ChartType;\n /** X axis field configuration */\n xAxis?: AxisField;\n /** Y axis field configuration (single or multiple) */\n yAxis?: AxisField | AxisField[];\n /** Series/color field for multi-series charts */\n series?: FieldRef;\n /** Stacking mode for multi-series */\n stacking?: StackingMode;\n /** Maximum rows to display */\n limit?: number;\n}\n\n/**\n * Input for vizspec generator service\n */\nexport interface VizSpecGeneratorInput {\n question: string;\n sql: string;\n rationale?: string;\n fields: string[];\n rows: Array<Record<string, unknown>>;\n maxRetries?: number;\n queryId?: string;\n /**\n * Optional encoding hints for visualization modification.\n * When provided, these guide the LLM to generate specific visualization configurations.\n */\n encodingHints?: EncodingHints;\n}\n\n/**\n * Output from vizspec generator service\n */\nexport interface VizSpecResult {\n spec: VizSpec;\n notes: string | null;\n}\n"],"mappings":"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","names":["normalizeTableFilter","asTableType","normalizeTableFilter","tableKey","asTableType","buildTablesQuery","buildColumnsQuery","sanitize","crypto","resolveTenantId","resolveTenantId","import_node_crypto","resolveTenantId","crypto","import_node_crypto","import_node_crypto","resolveTenantId","crypto","resolveTenantId","crypto","resolveTenantId","import_node_crypto","resolveTenantId","crypto"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/adapters/types.ts","../src/adapters/bigquery.ts","../src/utils/clickhouse.ts","../src/adapters/clickhouse.ts","../src/adapters/postgres.ts","../src/core/client.ts","../src/errors.ts","../src/core/callback-client.ts","../src/core/map-generated-params.ts","../src/core/query-engine.ts","../src/routes/charts.ts","../src/routes/active-charts.ts","../src/routes/dashboards.ts","../src/routes/ingest.ts","../src/routes/modify.ts","../src/routes/query.ts","../src/routes/queries.ts","../src/routes/sessions.ts","../src/routes/vizspec.ts","../src/types/vizspec.ts"],"sourcesContent":["import {\n\tBigQueryAdapter,\n\ttype BigQueryAdapterOptions,\n\ttype BigQueryClientFn,\n\ttype BigQueryQueryRequest,\n\ttype BigQueryQueryResult,\n} from \"./adapters/bigquery\";\nimport {\n\tClickHouseAdapter,\n\ttype ClickHouseAdapterOptions,\n\ttype ClickHouseClientFn,\n} from \"./adapters/clickhouse\";\nimport {\n\tPostgresAdapter,\n\ttype PostgresAdapterOptions,\n\ttype PostgresClientFn,\n\ttype PostgresWireDialect,\n} from \"./adapters/postgres\";\nimport type {\n\tCatalogSchema,\n\tCatalogTable,\n\tCatalogTableType,\n\tDatabaseAdapter,\n\tDatabaseDialect,\n\tListTablesOptions,\n} from \"./adapters/types\";\nimport type { IQueryPanelApi, RequestHandler } from \"./core/api-types\";\nimport { ApiClient } from \"./core/client\";\nimport { CallbackApiClient } from \"./core/callback-client\";\nimport {\n\ttype DatabaseMetadata,\n\ttype ParamRecord,\n\tQueryEngine,\n} from \"./core/query-engine\";\nimport {\n\tQueryErrorCode,\n\tQueryPipelineError,\n\tSchemaTooLargeError,\n} from \"./errors\";\nimport * as activeChartsRoute from \"./routes/active-charts\";\nimport * as chartsRoute from \"./routes/charts\";\nimport * as dashboardsRoute from \"./routes/dashboards\";\nimport * as ingestRoute from \"./routes/ingest\";\nimport * as modifyRoute from \"./routes/modify\";\nimport * as queriesRoute from \"./routes/queries\";\nimport * as queryRoute from \"./routes/query\";\nimport * as sessionsRoute from \"./routes/sessions\";\nimport * as vizspecRoute from \"./routes/vizspec\";\nimport type { SchemaIntrospection } from \"./schema/types\";\nimport type { ChartType } from \"./types/vizspec\";\n\n// Re-export all public types\nexport { BigQueryAdapter, ClickHouseAdapter, PostgresAdapter };\n\n// Re-export error types\nexport type { QueryErrorCode as QueryErrorCodeType } from \"./errors\";\nexport { QueryErrorCode, QueryPipelineError, SchemaTooLargeError };\n\nexport type {\n\tBigQueryAdapterOptions,\n\tBigQueryClientFn,\n\tBigQueryQueryRequest,\n\tBigQueryQueryResult,\n\tCatalogSchema,\n\tCatalogTable,\n\tCatalogTableType,\n\tClickHouseAdapterOptions,\n\tClickHouseClientFn,\n\tDatabaseAdapter,\n\tDatabaseDialect,\n\tListTablesOptions,\n\tPostgresAdapterOptions,\n\tPostgresClientFn,\n\tPostgresWireDialect,\n\tSchemaIntrospection,\n};\n\n// Re-export callback API types for in-process usage\nexport type {\n\tIQueryPanelApi,\n\tRequestHandler,\n\tRequestHandlerOptions,\n\tRequestHandlerResult,\n} from \"./core/api-types\";\nexport { CallbackApiClient } from \"./core/callback-client\";\n\n// Re-export from query-engine\nexport type { ParamRecord, ParamValue } from \"./core/query-engine\";\nexport {\n\tmapGeneratedParams,\n\tnormalizeSqlParamsInput,\n} from \"./core/map-generated-params\";\nexport type {\n\tActiveChartBulkGetResponse,\n\tActiveChartCreateInput,\n\tActiveChartListOptions,\n\tActiveChartUpdateInput,\n\tSdkActiveChart,\n} from \"./routes/active-charts\";\n\nexport type {\n\tChartBulkGetResponse,\n\tChartCreateInput,\n\tChartListAllOptions,\n\tChartListOptions,\n\tChartUpdateInput,\n\tPaginatedResponse,\n\tPaginationInfo,\n\tPaginationQuery,\n\tSdkChart,\n} from \"./routes/charts\";\n\nexport type {\n\tDashboardCreateInput,\n\tDashboardForkInput,\n\tDashboardForkUpdateInput,\n\tDashboardListOptions,\n\tDashboardUpdateInput,\n\tSdkDashboard,\n} from \"./routes/dashboards\";\n// Re-export route types\nexport type {\n\tIngestResponse,\n\tSchemaIngestBatchEmbedResponse,\n\tSchemaIngestRequestOptions,\n\tSchemaSyncOptions,\n\tSchemaSyncProgressEvent,\n\tStageSchemaIngestOptions,\n\tStageSchemaIngestResult,\n\tSyncSchemaInBatchesOptions,\n} from \"./routes/ingest\";\nexport type {\n\tAxisFieldInput,\n\tChartModifyInput,\n\tChartModifyOptions,\n\tChartModifyResponse,\n\tDateRangeInput,\n\tFieldRefInput,\n\tSqlModifications,\n\tVizModifications,\n} from \"./routes/modify\";\nexport type {\n\tAskOptions,\n\tAskResponse,\n\tChartEnvelope,\n\tContextDocument,\n\tIntentResult,\n\tPipelineTrace,\n} from \"./routes/query\";\n// Re-export anonymizeResults utility\nexport { anonymizeResults, resolveQueryPipeline } from \"./routes/query\";\nexport type {\n\tSdkSession,\n\tSdkSessionTurn,\n\tSessionGetOptions,\n\tSessionListOptions,\n\tSessionUpdateInput,\n} from \"./routes/sessions\";\nexport type {\n\tVizSpecGenerateInput,\n\tVizSpecGenerateOptions,\n\tVizSpecResponse,\n} from \"./routes/vizspec\";\n// Re-export VizSpec types\nexport {\n\tALL_VIZ_CHART_TYPES,\n} from \"./types/vizspec\";\nexport type {\n\tAggregateOp,\n\tAxisField,\n\tChartEncoding,\n\tChartSpec,\n\tChartType,\n\tEncodingHints,\n\tFieldRef,\n\tFieldType,\n\tMapEncoding,\n\tMapLocation,\n\tMapLocationMode,\n\tMapSpec,\n\tMetricEncoding,\n\tMetricField,\n\tMetricSpec,\n\tStackingMode,\n\tTableColumn,\n\tTableEncoding,\n\tTableSpec,\n\tTimeUnit,\n\tValueFormat,\n\tVizSpec,\n\tVizSpecGeneratorInput,\n\tVizSpecKind,\n\tVizSpecResult,\n} from \"./types/vizspec\";\n\n/** Options when constructing the SDK with HTTP (default) or with a custom API implementation. */\nexport interface QueryPanelSdkAPIOptions {\n\tdefaultTenantId?: string;\n\tadditionalHeaders?: Record<string, string>;\n\tfetch?: typeof fetch;\n\t/**\n\t * Use a custom API implementation (e.g. CallbackApiClient) instead of HTTP.\n\t * When set, baseUrl and privateKey are ignored and no requests are sent to querypanel-sdk;\n\t * use this when calling the SDK from within your own API to avoid recursion.\n\t */\n\tapi?: IQueryPanelApi;\n\t/**\n\t * Restrict VizSpec chart kinds (when using `chartType: \"vizspec\"`).\n\t * Omitted means all types: line, bar, column, area, scatter, pie.\n\t * Per-request `supportedChartTypes` on `ask` / `modifyChart` overrides this default.\n\t */\n\tsupportedChartTypes?: ChartType[];\n}\n\nfunction resolveAttachEnforceTenantIsolation(\n\ttenantFieldName: string | undefined,\n\tenforceTenantIsolation: boolean | undefined,\n): boolean | undefined {\n\tif (enforceTenantIsolation !== undefined) {\n\t\treturn enforceTenantIsolation;\n\t}\n\treturn tenantFieldName ? true : undefined;\n}\n\n/**\n * Main SDK class - Thin orchestrator\n * Delegates to deep modules (ApiClient or custom API, QueryEngine, route modules)\n * Following Ousterhout's principle: \"Simple interface hiding complexity\"\n */\nexport class QueryPanelSdkAPI {\n\tprivate readonly client: IQueryPanelApi;\n\tprivate readonly queryEngine: QueryEngine;\n\tprivate readonly supportedChartTypes?: ChartType[];\n\n\t/**\n\t * @param workspaceId - Workspace UUID (same value as your org in the dashboard). Passed to the API as JWT claim `organizationId`.\n\t */\n\tconstructor(\n\t\tbaseUrl: string,\n\t\tprivateKey: string,\n\t\tworkspaceId: string,\n\t\toptions?: QueryPanelSdkAPIOptions,\n\t) {\n\t\tif (options?.api) {\n\t\t\tthis.client = options.api;\n\t\t} else {\n\t\t\tif (!baseUrl) throw new Error(\"Base URL is required\");\n\t\t\tif (!privateKey) throw new Error(\"Private key is required\");\n\t\t\tif (!workspaceId) throw new Error(\"Workspace ID is required\");\n\t\t\tthis.client = new ApiClient(baseUrl, privateKey, workspaceId, {\n\t\t\t\tdefaultTenantId: options?.defaultTenantId,\n\t\t\t\tadditionalHeaders: options?.additionalHeaders,\n\t\t\t\tfetch: options?.fetch,\n\t\t\t});\n\t\t}\n\t\tthis.queryEngine = new QueryEngine();\n\t\tthis.supportedChartTypes = options?.supportedChartTypes;\n\t}\n\n\t/**\n\t * Create an SDK instance that uses a request callback instead of HTTP.\n\t * Use this when calling the SDK from within your own API so that \"API\" calls\n\t * are handled in-process (e.g. by invoking querypanel-sdk services directly)\n\t * and do not cause recursion.\n\t *\n\t * @param workspaceId - Workspace identifier (sent as `organizationId` in JWTs and API auth)\n\t * @param requestHandler - Callback invoked for each logical API request (method, path, body, tenantId, etc.)\n\t * @param options - Optional defaultTenantId\n\t * @returns QueryPanelSdkAPI instance; attach databases and call ask(), syncSchema(), etc. as usual\n\t *\n\t * @example\n\t * ```ts\n\t * const qp = QueryPanelSdkAPI.withCallbacks(\n\t * process.env.QUERYPANEL_WORKSPACE_ID!,\n\t * async (opts) => {\n\t * // Call your in-process services (e.g. querypanel-sdk logic) instead of HTTP\n\t * if (opts.path === '/query' || opts.path === '/v2/query') {\n\t * const result = await yourSqlGenerator.generate(opts.body, opts.tenantId);\n\t * return { data: result, headers: new Headers({ 'x-querypanel-session-id': result.sessionId }) };\n\t * }\n\t * if (opts.path === '/chart') return { data: await yourChartService.generate(opts.body) };\n\t * // ... other paths\n\t * return { data: await yourProxy(opts) };\n\t * },\n\t * { defaultTenantId: process.env.DEFAULT_TENANT_ID }\n\t * );\n\t * qp.attachPostgres('db', createClient, { tenantFieldName: 'tenant_id' });\n\t * const result = await qp.ask('Top 10 orders', { tenantId: 't1', database: 'db' });\n\t * ```\n\t */\n\tstatic withCallbacks(\n\t\tworkspaceId: string,\n\t\trequestHandler: RequestHandler,\n\t\toptions?: { defaultTenantId?: string; supportedChartTypes?: ChartType[] },\n\t): QueryPanelSdkAPI {\n\t\tconst api = new CallbackApiClient(requestHandler, {\n\t\t\tdefaultTenantId: options?.defaultTenantId,\n\t\t});\n\t\treturn new QueryPanelSdkAPI(\"\", \"\", workspaceId, {\n\t\t\tapi,\n\t\t\tsupportedChartTypes: options?.supportedChartTypes,\n\t\t});\n\t}\n\n\t// Database attachment methods\n\n\tattachClickhouse(\n\t\tname: string,\n\t\tclientFn: ClickHouseClientFn,\n\t\toptions?: ClickHouseAdapterOptions & {\n\t\t\tdescription?: string;\n\t\t\ttags?: string[];\n\t\t\ttenantFieldName?: string;\n\t\t\ttenantFieldType?: string;\n\t\t\tenforceTenantIsolation?: boolean;\n\t\t},\n\t): void {\n\t\tconst adapter = new ClickHouseAdapter(clientFn, options);\n\n\t\tconst metadata: DatabaseMetadata = {\n\t\t\tname,\n\t\t\tdialect: \"clickhouse\",\n\t\t\tdescription: options?.description,\n\t\t\ttags: options?.tags,\n\t\t\ttenantFieldName: options?.tenantFieldName,\n\t\t\ttenantFieldType: options?.tenantFieldType ?? \"String\",\n\t\t\tenforceTenantIsolation: resolveAttachEnforceTenantIsolation(\n\t\t\t\toptions?.tenantFieldName,\n\t\t\t\toptions?.enforceTenantIsolation,\n\t\t\t),\n\t\t};\n\n\t\tthis.queryEngine.attachDatabase(name, adapter, metadata);\n\t}\n\n\tattachPostgres(\n\t\tname: string,\n\t\tclientFn: PostgresClientFn,\n\t\toptions?: PostgresAdapterOptions & {\n\t\t\tdescription?: string;\n\t\t\ttags?: string[];\n\t\t\ttenantFieldName?: string;\n\t\t\ttenantFieldType?: string;\n\t\t\tenforceTenantIsolation?: boolean;\n\t\t},\n\t): void {\n\t\tconst adapter = new PostgresAdapter(clientFn, options);\n\n\t\tconst metadata: DatabaseMetadata = {\n\t\t\tname,\n\t\t\tdialect: adapter.getDialect(),\n\t\t\tdescription: options?.description,\n\t\t\ttags: options?.tags,\n\t\t\ttenantFieldName: options?.tenantFieldName,\n\t\t\ttenantFieldType: options?.tenantFieldType ?? \"String\",\n\t\t\tenforceTenantIsolation: resolveAttachEnforceTenantIsolation(\n\t\t\t\toptions?.tenantFieldName,\n\t\t\t\toptions?.enforceTenantIsolation,\n\t\t\t),\n\t\t};\n\n\t\tthis.queryEngine.attachDatabase(name, adapter, metadata);\n\t}\n\n\t/**\n\t * Attach an Amazon Redshift database. Redshift speaks the Postgres wire\n\t * protocol, so `clientFn` can wrap a `pg` client; introspection uses\n\t * Redshift's svv_* catalog views and SQL is generated for Redshift.\n\t */\n\tattachRedshift(\n\t\tname: string,\n\t\tclientFn: PostgresClientFn,\n\t\toptions?: Omit<PostgresAdapterOptions, \"dialect\"> & {\n\t\t\tdescription?: string;\n\t\t\ttags?: string[];\n\t\t\ttenantFieldName?: string;\n\t\t\ttenantFieldType?: string;\n\t\t\tenforceTenantIsolation?: boolean;\n\t\t},\n\t): void {\n\t\tthis.attachPostgres(name, clientFn, { ...options, dialect: \"redshift\" });\n\t}\n\n\tattachBigQuery(\n\t\tname: string,\n\t\tclientFn: BigQueryClientFn,\n\t\toptions: BigQueryAdapterOptions & {\n\t\t\tdescription?: string;\n\t\t\ttags?: string[];\n\t\t\ttenantFieldName?: string;\n\t\t\ttenantFieldType?: string;\n\t\t\tenforceTenantIsolation?: boolean;\n\t\t},\n\t): void {\n\t\tconst adapter = new BigQueryAdapter(clientFn, options);\n\n\t\tconst metadata: DatabaseMetadata = {\n\t\t\tname,\n\t\t\tdialect: \"bigquery\",\n\t\t\tdescription: options?.description,\n\t\t\ttags: options?.tags,\n\t\t\ttenantFieldName: options?.tenantFieldName,\n\t\t\ttenantFieldType: options?.tenantFieldType ?? \"String\",\n\t\t\tenforceTenantIsolation: resolveAttachEnforceTenantIsolation(\n\t\t\t\toptions?.tenantFieldName,\n\t\t\t\toptions?.enforceTenantIsolation,\n\t\t\t),\n\t\t};\n\n\t\tthis.queryEngine.attachDatabase(name, adapter, metadata);\n\t}\n\n\tattachDatabase(name: string, adapter: DatabaseAdapter): void {\n\t\tconst metadata: DatabaseMetadata = {\n\t\t\tname,\n\t\t\tdialect: adapter.getDialect(),\n\t\t};\n\t\tthis.queryEngine.attachDatabase(name, adapter, metadata);\n\t}\n\n\t// Schema introspection and sync\n\n\tasync introspect(\n\t\tdatabaseName: string,\n\t\ttables?: string[],\n\t): Promise<SchemaIntrospection> {\n\t\tconst adapter = this.queryEngine.getDatabase(databaseName);\n\t\treturn await adapter.introspect(tables ? { tables } : undefined);\n\t}\n\n\t/**\n\t * Lists schemas (ClickHouse: the database, BigQuery: the dataset) with table\n\t * counts. Cheap catalog read for building an allowed-tables picker; not\n\t * restricted by `allowedTables`.\n\t */\n\tasync listSchemas(databaseName: string): Promise<CatalogSchema[]> {\n\t\tconst adapter = this.queryEngine.getDatabase(databaseName);\n\t\tif (!adapter.listSchemas) {\n\t\t\tthrow new Error(\n\t\t\t\t`Database \"${databaseName}\" (${adapter.getDialect()}) does not support listSchemas`,\n\t\t\t);\n\t\t}\n\t\treturn await adapter.listSchemas();\n\t}\n\n\t/**\n\t * Lists tables/views (no columns) filtered by schema and a case-insensitive\n\t * name substring. Default limit 500, max 5000. Not restricted by `allowedTables`.\n\t */\n\tasync listTables(\n\t\tdatabaseName: string,\n\t\toptions?: ListTablesOptions,\n\t): Promise<CatalogTable[]> {\n\t\tconst adapter = this.queryEngine.getDatabase(databaseName);\n\t\tif (!adapter.listTables) {\n\t\t\tthrow new Error(\n\t\t\t\t`Database \"${databaseName}\" (${adapter.getDialect()}) does not support listTables`,\n\t\t\t);\n\t\t}\n\t\treturn await adapter.listTables(options);\n\t}\n\n\t/**\n\t * Syncs the database schema to QueryPanel for natural language query generation.\n\t *\n\t * This method introspects your database schema and uploads it to QueryPanel's\n\t * vector store. The schema is used by the LLM to generate accurate SQL queries.\n\t * Schema embedding is skipped if no changes are detected (drift detection).\n\t *\n\t * @param databaseName - Name of the attached database to sync\n\t * @param options - Sync options including tenantId and forceReindex\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Response with sync status and chunk counts\n\t *\n\t * @example\n\t * ```typescript\n\t * // Basic schema sync (skips if no changes)\n\t * await qp.syncSchema(\"analytics\", { tenantId: \"tenant_123\" });\n\t *\n\t * // Force re-embedding even if schema hasn't changed\n\t * await qp.syncSchema(\"analytics\", {\n\t * tenantId: \"tenant_123\",\n\t * forceReindex: true,\n\t * });\n\t * ```\n\t */\n\tasync syncSchema(\n\t\tdatabaseName: string,\n\t\toptions: ingestRoute.SchemaSyncOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<ingestRoute.IngestResponse> {\n\t\treturn await ingestRoute.syncSchema(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tdatabaseName,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Introspects the schema, uploads it in batches to a new ingest session and\n\t * commits it. Returns `status: \"skipped\"` (with `skippedResult`) when the schema\n\t * is unchanged, otherwise `status: \"embedding\"`: call embedSchemaIngestBatch for\n\t * every index in [0, totalBatches) and then finalizeSchemaIngest.\n\t * Throws SchemaTooLargeError before uploading if `maxColumns` is exceeded.\n\t */\n\tasync stageSchemaIngest(\n\t\tdatabaseName: string,\n\t\toptions: ingestRoute.StageSchemaIngestOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<ingestRoute.StageSchemaIngestResult> {\n\t\treturn await ingestRoute.stageSchemaIngest(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tdatabaseName,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/** Embeds one batch of a staged ingest session. */\n\tasync embedSchemaIngestBatch(\n\t\tsessionId: string,\n\t\tindex: number,\n\t\toptions: ingestRoute.SchemaIngestRequestOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<ingestRoute.SchemaIngestBatchEmbedResponse> {\n\t\treturn await ingestRoute.embedSchemaIngestBatch(\n\t\t\tthis.client,\n\t\t\tsessionId,\n\t\t\tindex,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/** Finalizes a staged ingest session after all batches are embedded. */\n\tasync finalizeSchemaIngest(\n\t\tsessionId: string,\n\t\toptions: ingestRoute.SchemaIngestRequestOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<ingestRoute.IngestResponse> {\n\t\treturn await ingestRoute.finalizeSchemaIngest(\n\t\t\tthis.client,\n\t\t\tsessionId,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Batched alternative to syncSchema for large databases: stage, embed every\n\t * batch, and finalize in-process, reporting progress via `onProgress`.\n\t *\n\t * @example\n\t * ```typescript\n\t * await qp.syncSchemaInBatches(\"warehouse\", {\n\t * tenantId: \"tenant_123\",\n\t * onProgress: (e) => console.log(e.phase, e),\n\t * });\n\t * ```\n\t */\n\tasync syncSchemaInBatches(\n\t\tdatabaseName: string,\n\t\toptions: ingestRoute.SyncSchemaInBatchesOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<ingestRoute.IngestResponse> {\n\t\treturn await ingestRoute.syncSchemaInBatches(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tdatabaseName,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t// Natural language query\n\n\t/**\n\t * Generates SQL from a natural language question and executes it.\n\t *\n\t * This is the primary method for converting user questions into data.\n\t * It handles the complete flow: SQL generation → validation → execution → chart generation.\n\t *\n\t * @param question - Natural language question (e.g., \"Show revenue by country\")\n\t * @param options - Query options including tenantId, database, retry settings, and optional `debug` for engineer-style rationale\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Response with SQL, executed data rows, and generated chart\n\t * @throws {Error} When SQL generation or execution fails after all retries\n\t *\n\t * @example\n\t * ```typescript\n\t * // Basic query\n\t * const result = await qp.ask(\"Top 10 customers by revenue\", {\n\t * tenantId: \"tenant_123\",\n\t * database: \"analytics\",\n\t * });\n\t * console.log(result.sql); // Generated SQL\n\t * console.log(result.rows); // Query results\n\t * console.log(result.chart); // Vega-Lite chart spec\n\t * console.log(result.querypanelSessionId); // Use for follow-ups\n\t *\n\t * // With automatic SQL repair on failure\n\t * const result = await qp.ask(\"Show monthly trends\", {\n\t * tenantId: \"tenant_123\",\n\t * maxRetry: 3, // Retry up to 3 times if SQL fails\n\t * });\n\t * ```\n\t */\n\tasync ask(\n\t\tquestion: string,\n\t\toptions: queryRoute.AskOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<queryRoute.AskResponse> {\n\t\treturn await queryRoute.ask(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tquestion,\n\t\t\t{\n\t\t\t\t...options,\n\t\t\t\tsupportedChartTypes:\n\t\t\t\t\toptions.supportedChartTypes ?? this.supportedChartTypes,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t// Embedded dashboard / JWT\n\n\t/**\n\t * Creates a JWT for the given tenant (and optional userId, scopes).\n\t * Use this when you need to pass a token to the embed (e.g. frontend or demo).\n\t * Only available when using the HTTP client (ApiClient); not available when using withCallbacks.\n\t */\n\tasync createJwt(options: {\n\t\ttenantId: string;\n\t\tuserId?: string;\n\t\tscopes?: string[];\n\t}): Promise<string> {\n\t\tconst tenantId = options.tenantId?.trim();\n\t\tif (!tenantId) {\n\t\t\tthrow new Error(\"tenantId is required\");\n\t\t}\n\t\tif (!(this.client instanceof ApiClient)) {\n\t\t\tthrow new Error(\n\t\t\t\t\"createJwt is not available when using withCallbacks. Use the SDK with baseUrl + privateKey to create JWTs.\",\n\t\t\t);\n\t\t}\n\t\treturn await this.client.createJwt(\n\t\t\ttenantId,\n\t\t\toptions.userId,\n\t\t\toptions.scopes,\n\t\t);\n\t}\n\n\t/**\n\t * Runs SQL against an attached database with tenant isolation applied.\n\t * Used by embed run-sql (e.g. dashboard chart execution). Requires a database\n\t * to be attached (e.g. via attachPostgres) and options.tenantId.\n\t */\n\tasync runSqlForDashboard(\n\t\tinput: {\n\t\t\t/**\n\t\t\t * Server-issued id from ask() / modifyChart(). Takes priority: the stored SQL\n\t\t\t * and params are executed and `sql` / `params` are ignored.\n\t\t\t */\n\t\t\tqueryId?: string;\n\t\t\t/** Required when `queryId` is not provided (backward compatible raw SQL). */\n\t\t\tsql?: string;\n\t\t\tparams?: Record<string, unknown>;\n\t\t\tdatabase?: string;\n\t\t},\n\t\toptions: { tenantId: string },\n\t): Promise<{ rows: unknown[]; fields: string[] }> {\n\t\tconst tenantId = options.tenantId?.trim();\n\t\tif (!tenantId) {\n\t\t\tthrow new Error(\"tenantId is required\");\n\t\t}\n\t\tconst databaseName = input.database ?? \"db\";\n\t\tlet sql = input.sql;\n\t\tlet params = (input.params ?? {}) as ParamRecord;\n\t\tif (input.queryId) {\n\t\t\tconst resolved = await queriesRoute.resolveQuery(\n\t\t\t\tthis.client,\n\t\t\t\tthis.queryEngine,\n\t\t\t\tinput.queryId,\n\t\t\t\t{ tenantId },\n\t\t\t);\n\t\t\tsql = resolved.sql;\n\t\t\tparams = resolved.params;\n\t\t}\n\t\tif (!sql) {\n\t\t\tthrow new Error(\"Either queryId or sql is required\");\n\t\t}\n\t\tconst result = await this.queryEngine.validateAndExecute(\n\t\t\tsql,\n\t\t\tparams,\n\t\t\tdatabaseName,\n\t\t\ttenantId,\n\t\t);\n\t\tif (input.queryId) {\n\t\t\tawait queriesRoute.markQueryExecuted(this.client, input.queryId, {\n\t\t\t\ttenantId,\n\t\t\t});\n\t\t}\n\t\treturn { rows: result.rows, fields: result.fields };\n\t}\n\n\t// VizSpec generation\n\n\t/**\n\t * Generates a VizSpec visualization specification from query results.\n\t *\n\t * Use this when you have raw SQL results and want to generate a chart\n\t * specification without going through the full ask() flow. Useful for\n\t * re-generating charts with different settings.\n\t *\n\t * @param input - VizSpec generation input with question, SQL, and result data\n\t * @param options - Optional settings for tenant and retries\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns VizSpec specification for chart, table, metric, or map visualization\n\t *\n\t * @example\n\t * ```typescript\n\t * const vizspec = await qp.generateVizSpec({\n\t * question: \"Revenue by country\",\n\t * sql: \"SELECT country, SUM(revenue) FROM orders GROUP BY country\",\n\t * fields: [\"country\", \"revenue\"],\n\t * rows: queryResults,\n\t * }, { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync generateVizSpec(\n\t\tinput: vizspecRoute.VizSpecGenerateInput,\n\t\toptions?: vizspecRoute.VizSpecGenerateOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<vizspecRoute.VizSpecResponse> {\n\t\treturn await vizspecRoute.generateVizSpec(\n\t\t\tthis.client,\n\t\t\t{\n\t\t\t\t...input,\n\t\t\t\tsupported_chart_types:\n\t\t\t\t\tinput.supported_chart_types ?? this.supportedChartTypes,\n\t\t\t},\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t// Chart modification\n\n\t/**\n\t * Modifies a chart by regenerating SQL and/or applying visualization changes.\n\t *\n\t * This method supports three modes of operation:\n\t *\n\t * 1. **SQL Modifications**: When `sqlModifications` is provided, the SQL is\n\t * regenerated using the query endpoint with modification hints. If `customSql`\n\t * is set, it's used directly without regeneration.\n\t *\n\t * 2. **Visualization Modifications**: When only `vizModifications` is provided,\n\t * the existing SQL is re-executed and a new chart is generated with the\n\t * specified encoding preferences.\n\t *\n\t * 3. **Combined**: Both SQL and visualization modifications can be applied\n\t * together. SQL is regenerated first, then viz modifications are applied.\n\t *\n\t * @param input - Chart modification input with source data and modifications\n\t * @param options - Optional settings for tenant, user, and chart generation\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Modified chart response with SQL, data, and chart specification\n\t *\n\t * @example\n\t * ```typescript\n\t * // Change chart type and axis from an ask() response\n\t * const modified = await qp.modifyChart({\n\t * sql: response.sql,\n\t * question: \"revenue by country\",\n\t * database: \"analytics\",\n\t * vizModifications: {\n\t * chartType: \"bar\",\n\t * xAxis: { field: \"country\" },\n\t * yAxis: { field: \"revenue\", aggregate: \"sum\" },\n\t * },\n\t * }, { tenantId: \"tenant_123\" });\n\t *\n\t * // Change time granularity (triggers SQL regeneration)\n\t * const monthly = await qp.modifyChart({\n\t * sql: response.sql,\n\t * question: \"revenue over time\",\n\t * database: \"analytics\",\n\t * sqlModifications: {\n\t * timeGranularity: \"month\",\n\t * dateRange: { from: \"2024-01-01\", to: \"2024-12-31\" },\n\t * },\n\t * }, { tenantId: \"tenant_123\" });\n\t *\n\t * // Direct SQL edit with chart regeneration\n\t * const customized = await qp.modifyChart({\n\t * sql: response.sql,\n\t * question: \"revenue by country\",\n\t * database: \"analytics\",\n\t * sqlModifications: {\n\t * customSql: \"SELECT country, SUM(revenue) FROM orders GROUP BY country\",\n\t * },\n\t * }, { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync modifyChart(\n\t\tinput: modifyRoute.ChartModifyInput,\n\t\toptions?: modifyRoute.ChartModifyOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<modifyRoute.ChartModifyResponse> {\n\t\treturn await modifyRoute.modifyChart(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tinput,\n\t\t\toptions\n\t\t\t\t? {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\tsupportedChartTypes:\n\t\t\t\t\t\t\toptions.supportedChartTypes ?? this.supportedChartTypes,\n\t\t\t\t\t}\n\t\t\t\t: this.supportedChartTypes\n\t\t\t\t\t? { supportedChartTypes: this.supportedChartTypes }\n\t\t\t\t\t: undefined,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t// Chart CRUD operations\n\n\t/**\n\t * Saves a chart to the QueryPanel system for later retrieval.\n\t *\n\t * Charts store the SQL query, parameters, and visualization spec - never the actual data.\n\t * Data is fetched live when the chart is rendered or refreshed.\n\t *\n\t * @param body - Chart data including title, SQL, and Vega-Lite spec\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns The saved chart with its generated ID\n\t *\n\t * @example\n\t * ```typescript\n\t * const savedChart = await qp.createChart({\n\t * title: \"Revenue by Country\",\n\t * sql: response.sql,\n\t * sql_params: response.params,\n\t * vega_lite_spec: response.chart.vegaLiteSpec,\n\t * target_db: \"analytics\",\n\t * }, { tenantId: \"tenant_123\", userId: \"user_456\" });\n\t * ```\n\t */\n\tasync createChart(\n\t\tbody: chartsRoute.ChartCreateInput,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.SdkChart> {\n\t\treturn await chartsRoute.createChart(this.client, body, options, signal);\n\t}\n\n\t/**\n\t * Lists saved charts with optional filtering and pagination.\n\t *\n\t * Use `includeData: true` to execute each chart's SQL and include live data.\n\t *\n\t * @param options - Filtering, pagination, and data options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Paginated list of charts\n\t *\n\t * @example\n\t * ```typescript\n\t * // List charts with pagination\n\t * const charts = await qp.listCharts({\n\t * tenantId: \"tenant_123\",\n\t * pagination: { page: 1, limit: 10 },\n\t * sortBy: \"created_at\",\n\t * sortDir: \"desc\",\n\t * });\n\t *\n\t * // List with live data\n\t * const chartsWithData = await qp.listCharts({\n\t * tenantId: \"tenant_123\",\n\t * includeData: true,\n\t * });\n\t * ```\n\t */\n\tasync listCharts(\n\t\toptions?: chartsRoute.ChartListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.PaginatedResponse<chartsRoute.SdkChart>> {\n\t\treturn await chartsRoute.listCharts(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Lists saved charts via `GET /charts/all` (same query, filters, and pagination as {@link listCharts}).\n\t */\n\tasync listAllCharts(\n\t\toptions?: chartsRoute.ChartListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.PaginatedResponse<chartsRoute.SdkChart>> {\n\t\treturn await chartsRoute.listAllCharts(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Bulk gets saved charts by chart IDs (`GET /charts/bulk` with `ids` query params).\n\t */\n\tasync getChartsByIds(\n\t\tids: string[],\n\t\toptions?: chartsRoute.ChartListAllOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.ChartBulkGetResponse> {\n\t\treturn await chartsRoute.getChartsByIds(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tids,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t// Session history CRUD operations\n\n\t/**\n\t * Lists query sessions with pagination and filtering.\n\t *\n\t * @param options - Filtering, pagination, and sort options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Paginated list of sessions\n\t *\n\t * @example\n\t * ```typescript\n\t * const sessions = await qp.listSessions({\n\t * tenantId: \"tenant_123\",\n\t * pagination: { page: 1, limit: 20 },\n\t * sortBy: \"updated_at\",\n\t * });\n\t * ```\n\t */\n\tasync listSessions(\n\t\toptions?: sessionsRoute.SessionListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<sessionsRoute.PaginatedResponse<sessionsRoute.SdkSession>> {\n\t\treturn await sessionsRoute.listSessions(this.client, options, signal);\n\t}\n\n\t/**\n\t * Retrieves a session by session_id with optional turn history.\n\t *\n\t * @param sessionId - QueryPanel session identifier used in ask()\n\t * @param options - Tenant, user, scopes, and includeTurns flag\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Session metadata with optional turns\n\t *\n\t * @example\n\t * ```typescript\n\t * const session = await qp.getSession(\"session_123\", {\n\t * tenantId: \"tenant_123\",\n\t * includeTurns: true,\n\t * });\n\t * ```\n\t */\n\tasync getSession(\n\t\tsessionId: string,\n\t\toptions?: sessionsRoute.SessionGetOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<sessionsRoute.SdkSession> {\n\t\treturn await sessionsRoute.getSession(\n\t\t\tthis.client,\n\t\t\tsessionId,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Updates session metadata (title).\n\t *\n\t * @param sessionId - QueryPanel session identifier to update\n\t * @param body - Fields to update\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Updated session\n\t *\n\t * @example\n\t * ```typescript\n\t * const updated = await qp.updateSession(\n\t * \"session_123\",\n\t * { title: \"Q4 Revenue Analysis\" },\n\t * { tenantId: \"tenant_123\" },\n\t * );\n\t * ```\n\t */\n\tasync updateSession(\n\t\tsessionId: string,\n\t\tbody: sessionsRoute.SessionUpdateInput,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<sessionsRoute.SdkSession> {\n\t\treturn await sessionsRoute.updateSession(\n\t\t\tthis.client,\n\t\t\tsessionId,\n\t\t\tbody,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Deletes a session and its turn history.\n\t *\n\t * @param sessionId - QueryPanel session identifier to delete\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t *\n\t * @example\n\t * ```typescript\n\t * await qp.deleteSession(\"session_123\", { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync deleteSession(\n\t\tsessionId: string,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\tawait sessionsRoute.deleteSession(this.client, sessionId, options, signal);\n\t}\n\n\t/**\n\t * Retrieves a single chart by ID with live data.\n\t *\n\t * The chart's SQL is automatically executed and data is included in the response.\n\t *\n\t * @param id - Chart ID\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Chart with live data populated\n\t *\n\t * @example\n\t * ```typescript\n\t * const chart = await qp.getChart(\"chart_123\", {\n\t * tenantId: \"tenant_123\",\n\t * });\n\t * console.log(chart.vega_lite_spec.data.values); // Live data\n\t * ```\n\t */\n\tasync getChart(\n\t\tid: string,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.SdkChart> {\n\t\treturn await chartsRoute.getChart(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tid,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Updates an existing chart's metadata or configuration.\n\t *\n\t * @param id - Chart ID to update\n\t * @param body - Fields to update (partial update supported)\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Updated chart\n\t *\n\t * @example\n\t * ```typescript\n\t * const updated = await qp.updateChart(\"chart_123\", {\n\t * title: \"Updated Chart Title\",\n\t * description: \"New description\",\n\t * }, { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync updateChart(\n\t\tid: string,\n\t\tbody: chartsRoute.ChartUpdateInput,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.SdkChart> {\n\t\treturn await chartsRoute.updateChart(\n\t\t\tthis.client,\n\t\t\tid,\n\t\t\tbody,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Deletes a chart permanently.\n\t *\n\t * @param id - Chart ID to delete\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t *\n\t * @example\n\t * ```typescript\n\t * await qp.deleteChart(\"chart_123\", { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync deleteChart(\n\t\tid: string,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\tawait chartsRoute.deleteChart(this.client, id, options, signal);\n\t}\n\n\t// Active Chart CRUD operations (Dashboard)\n\n\t/**\n\t * Pins a saved chart to the dashboard (Active Charts).\n\t *\n\t * Active Charts are used for building dashboards. Unlike the chart history,\n\t * active charts are meant to be displayed together with layout metadata.\n\t *\n\t * @param body - Active chart config with chart_id, order, and optional meta\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Created active chart entry\n\t *\n\t * @example\n\t * ```typescript\n\t * const pinned = await qp.createActiveChart({\n\t * chart_id: savedChart.id,\n\t * order: 1,\n\t * meta: { width: \"full\", variant: \"dark\" },\n\t * }, { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync createActiveChart(\n\t\tbody: activeChartsRoute.ActiveChartCreateInput,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<activeChartsRoute.SdkActiveChart> {\n\t\treturn await activeChartsRoute.createActiveChart(\n\t\t\tthis.client,\n\t\t\tbody,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Lists all active charts (dashboard items) with optional live data.\n\t *\n\t * Use `withData: true` to execute each chart's SQL and include results.\n\t * This is the primary method for loading a complete dashboard.\n\t *\n\t * @param options - Filtering and data options including withData\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Paginated list of active charts with optional live data\n\t *\n\t * @example\n\t * ```typescript\n\t * // Load dashboard with live data\n\t * const dashboard = await qp.listActiveCharts({\n\t * tenantId: \"tenant_123\",\n\t * withData: true,\n\t * });\n\t *\n\t * dashboard.data.forEach(item => {\n\t * console.log(item.chart?.title);\n\t * console.log(item.chart?.vega_lite_spec.data.values);\n\t * });\n\t * ```\n\t */\n\tasync listActiveCharts(\n\t\toptions?: activeChartsRoute.ActiveChartListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.PaginatedResponse<activeChartsRoute.SdkActiveChart>> {\n\t\treturn await activeChartsRoute.listActiveCharts(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Lists all active charts without pagination.\n\t */\n\tasync listAllActiveCharts(\n\t\toptions?: activeChartsRoute.ActiveChartListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<activeChartsRoute.SdkActiveChart[]> {\n\t\treturn await activeChartsRoute.listAllActiveCharts(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Bulk gets active charts by IDs.\n\t */\n\tasync getActiveChartsByIds(\n\t\tids: string[],\n\t\toptions?: activeChartsRoute.ActiveChartListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<activeChartsRoute.ActiveChartBulkGetResponse> {\n\t\treturn await activeChartsRoute.getActiveChartsByIds(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tids,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Retrieves a single active chart by ID.\n\t *\n\t * @param id - Active chart ID\n\t * @param options - Options including withData for live data\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Active chart with associated chart data\n\t *\n\t * @example\n\t * ```typescript\n\t * const activeChart = await qp.getActiveChart(\"active_123\", {\n\t * tenantId: \"tenant_123\",\n\t * withData: true,\n\t * });\n\t * ```\n\t */\n\tasync getActiveChart(\n\t\tid: string,\n\t\toptions?: activeChartsRoute.ActiveChartListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<activeChartsRoute.SdkActiveChart> {\n\t\treturn await activeChartsRoute.getActiveChart(\n\t\t\tthis.client,\n\t\t\tthis.queryEngine,\n\t\t\tid,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Updates an active chart's order or metadata.\n\t *\n\t * Use this to reorder dashboard items or update layout hints.\n\t *\n\t * @param id - Active chart ID to update\n\t * @param body - Fields to update (order, meta)\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Updated active chart\n\t *\n\t * @example\n\t * ```typescript\n\t * const updated = await qp.updateActiveChart(\"active_123\", {\n\t * order: 5,\n\t * meta: { width: \"half\" },\n\t * }, { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync updateActiveChart(\n\t\tid: string,\n\t\tbody: activeChartsRoute.ActiveChartUpdateInput,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<activeChartsRoute.SdkActiveChart> {\n\t\treturn await activeChartsRoute.updateActiveChart(\n\t\t\tthis.client,\n\t\t\tid,\n\t\t\tbody,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Removes a chart from the dashboard (unpins it).\n\t *\n\t * This only removes the active chart entry, not the underlying saved chart.\n\t *\n\t * @param id - Active chart ID to delete\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t *\n\t * @example\n\t * ```typescript\n\t * await qp.deleteActiveChart(\"active_123\", { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync deleteActiveChart(\n\t\tid: string,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\tawait activeChartsRoute.deleteActiveChart(this.client, id, options, signal);\n\t}\n\n\t// Dashboard CRUD operations\n\n\t/**\n\t * Creates a new dashboard with BlockNote content.\n\t *\n\t * @param body - Dashboard configuration including name and BlockNote content\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Created dashboard\n\t *\n\t * @example\n\t * ```typescript\n\t * const dashboard = await qp.createDashboard({\n\t * name: \"Sales Dashboard\",\n\t * description: \"Monthly sales metrics\",\n\t * content_json: blockNoteContent,\n\t * editor_type: \"blocknote\",\n\t * }, { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync createDashboard(\n\t\tbody: dashboardsRoute.DashboardCreateInput,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.createDashboard(\n\t\t\tthis.client,\n\t\t\tbody,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Lists dashboards with pagination and filtering.\n\t *\n\t * @param options - Filtering and pagination options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Paginated list of dashboards\n\t *\n\t * @example\n\t * ```typescript\n\t * const dashboards = await qp.listDashboards({\n\t * tenantId: \"tenant_123\",\n\t * status: \"deployed\",\n\t * pagination: { page: 1, limit: 10 },\n\t * });\n\t * ```\n\t */\n\tasync listDashboards(\n\t\toptions?: dashboardsRoute.DashboardListOptions,\n\t\tsignal?: AbortSignal,\n\t): Promise<chartsRoute.PaginatedResponse<dashboardsRoute.SdkDashboard>> {\n\t\treturn await dashboardsRoute.listDashboards(this.client, options, signal);\n\t}\n\n\t/**\n\t * Gets a dashboard by ID.\n\t *\n\t * @param id - Dashboard ID\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Dashboard details\n\t *\n\t * @example\n\t * ```typescript\n\t * const dashboard = await qp.getDashboard(\"dash_123\", {\n\t * tenantId: \"tenant_123\",\n\t * });\n\t * ```\n\t */\n\tasync getDashboard(\n\t\tid: string,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.getDashboard(\n\t\t\tthis.client,\n\t\t\tid,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Gets a dashboard for a specific tenant.\n\t * Returns customer fork if exists, otherwise returns the original dashboard.\n\t *\n\t * @param id - Dashboard ID\n\t * @param tenantId - Tenant ID to check for fork\n\t * @param options - Additional auth options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Dashboard (fork if exists, original otherwise)\n\t *\n\t * @example\n\t * ```typescript\n\t * const dashboard = await qp.getDashboardForTenant(\n\t * \"dash_123\",\n\t * \"tenant_456\",\n\t * );\n\t * ```\n\t */\n\tasync getDashboardForTenant(\n\t\tid: string,\n\t\ttenantId: string,\n\t\toptions?: { userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.getDashboardForTenant(\n\t\t\tthis.client,\n\t\t\tid,\n\t\t\ttenantId,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Updates a dashboard.\n\t *\n\t * @param id - Dashboard ID to update\n\t * @param body - Fields to update\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Updated dashboard\n\t *\n\t * @example\n\t * ```typescript\n\t * const updated = await qp.updateDashboard(\"dash_123\", {\n\t * name: \"Updated Dashboard\",\n\t * content_json: newBlockNoteContent,\n\t * }, { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync updateDashboard(\n\t\tid: string,\n\t\tbody: dashboardsRoute.DashboardUpdateInput,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.updateDashboard(\n\t\t\tthis.client,\n\t\t\tid,\n\t\t\tbody,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Updates dashboard status (deploy/undeploy).\n\t *\n\t * @param id - Dashboard ID\n\t * @param status - New status\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Updated dashboard\n\t *\n\t * @example\n\t * ```typescript\n\t * const deployed = await qp.updateDashboardStatus(\n\t * \"dash_123\",\n\t * \"deployed\",\n\t * { tenantId: \"tenant_123\" },\n\t * );\n\t * ```\n\t */\n\tasync updateDashboardStatus(\n\t\tid: string,\n\t\tstatus: \"draft\" | \"deployed\",\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.updateDashboardStatus(\n\t\t\tthis.client,\n\t\t\tid,\n\t\t\tstatus,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Deletes a dashboard.\n\t *\n\t * @param id - Dashboard ID to delete\n\t * @param options - Tenant, user, and scope options\n\t * @param signal - Optional AbortSignal for cancellation\n\t *\n\t * @example\n\t * ```typescript\n\t * await qp.deleteDashboard(\"dash_123\", { tenantId: \"tenant_123\" });\n\t * ```\n\t */\n\tasync deleteDashboard(\n\t\tid: string,\n\t\toptions?: { tenantId?: string; userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\tawait dashboardsRoute.deleteDashboard(this.client, id, options, signal);\n\t}\n\n\t// Dashboard Fork operations (Customer Customization)\n\n\t/**\n\t * Forks a dashboard for customer customization (copy-on-write).\n\t *\n\t * Creates a full copy of the dashboard that the customer can edit independently.\n\t * The original dashboard remains unchanged.\n\t *\n\t * @param id - Dashboard ID to fork\n\t * @param input - Fork configuration with tenant_id\n\t * @param options - Additional auth options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Forked dashboard\n\t *\n\t * @example\n\t * ```typescript\n\t * const fork = await qp.forkDashboard(\"dash_123\", {\n\t * tenant_id: \"tenant_456\",\n\t * name: \"Customer's Custom Dashboard\",\n\t * });\n\t * ```\n\t */\n\tasync forkDashboard(\n\t\tid: string,\n\t\tinput: dashboardsRoute.DashboardForkInput,\n\t\toptions?: { userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.forkDashboard(\n\t\t\tthis.client,\n\t\t\tid,\n\t\t\tinput,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Updates a customer fork.\n\t *\n\t * @param forkId - Fork ID to update\n\t * @param input - Update data including tenant_id\n\t * @param options - Additional auth options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Updated fork\n\t *\n\t * @example\n\t * ```typescript\n\t * const updated = await qp.updateFork(\"fork_123\", {\n\t * tenant_id: \"tenant_456\",\n\t * content_json: newBlockNoteContent,\n\t * });\n\t * ```\n\t */\n\tasync updateFork(\n\t\tforkId: string,\n\t\tinput: dashboardsRoute.DashboardForkUpdateInput,\n\t\toptions?: { userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.updateFork(\n\t\t\tthis.client,\n\t\t\tforkId,\n\t\t\tinput,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Rollbacks a fork to the original dashboard.\n\t * This deletes the fork and the customer will see the original again.\n\t *\n\t * @param forkId - Fork ID to rollback\n\t * @param tenantId - Tenant ID\n\t * @param options - Additional auth options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Original dashboard\n\t *\n\t * @example\n\t * ```typescript\n\t * const original = await qp.rollbackFork(\"fork_123\", \"tenant_456\");\n\t * ```\n\t */\n\tasync rollbackFork(\n\t\tforkId: string,\n\t\ttenantId: string,\n\t\toptions?: { userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard> {\n\t\treturn await dashboardsRoute.rollbackFork(\n\t\t\tthis.client,\n\t\t\tforkId,\n\t\t\ttenantId,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Deletes a customer fork.\n\t *\n\t * @param forkId - Fork ID to delete\n\t * @param tenantId - Tenant ID\n\t * @param options - Additional auth options\n\t * @param signal - Optional AbortSignal for cancellation\n\t *\n\t * @example\n\t * ```typescript\n\t * await qp.deleteFork(\"fork_123\", \"tenant_456\");\n\t * ```\n\t */\n\tasync deleteFork(\n\t\tforkId: string,\n\t\ttenantId: string,\n\t\toptions?: { userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\tawait dashboardsRoute.deleteFork(\n\t\t\tthis.client,\n\t\t\tforkId,\n\t\t\ttenantId,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t/**\n\t * Lists all customer forks for a tenant.\n\t *\n\t * @param tenantId - Tenant ID\n\t * @param options - Additional auth options\n\t * @param signal - Optional AbortSignal for cancellation\n\t * @returns Array of forks\n\t *\n\t * @example\n\t * ```typescript\n\t * const forks = await qp.listForksForTenant(\"tenant_456\");\n\t * ```\n\t */\n\tasync listForksForTenant(\n\t\ttenantId: string,\n\t\toptions?: { userId?: string; scopes?: string[] },\n\t\tsignal?: AbortSignal,\n\t): Promise<dashboardsRoute.SdkDashboard[]> {\n\t\treturn await dashboardsRoute.listForksForTenant(\n\t\t\tthis.client,\n\t\t\ttenantId,\n\t\t\toptions,\n\t\t\tsignal,\n\t\t);\n\t}\n}\n","import type {\n IntrospectOptions,\n SchemaIntrospection,\n} from \"../schema/types\";\n\nexport type DatabaseDialect =\n\t| \"bigquery\"\n\t| \"clickhouse\"\n\t| \"postgres\"\n\t| \"redshift\"\n\t| \"mysql\";\n\n/** A schema (or ClickHouse database / BigQuery dataset) in the catalog. */\nexport interface CatalogSchema {\n schema: string;\n tableCount: number;\n}\n\nexport type CatalogTableType = \"table\" | \"view\" | \"materialized_view\";\n\n/** A table or view in the catalog (no columns; cheap to list). */\nexport interface CatalogTable {\n schema: string;\n name: string;\n type: CatalogTableType;\n}\n\nexport interface ListTablesOptions {\n /** Only list tables in this schema. */\n schema?: string;\n /** Case-insensitive substring match on the table name. */\n search?: string;\n /** Max rows to return. Default 500, capped at 5000. */\n limit?: number;\n}\n\nexport const DEFAULT_CATALOG_LIMIT = 500;\nexport const MAX_CATALOG_LIMIT = 5000;\n\n/** Clamp a caller-provided limit to a safe positive integer. */\nexport function resolveCatalogLimit(limit?: number): number {\n if (limit === undefined || !Number.isFinite(limit)) {\n return DEFAULT_CATALOG_LIMIT;\n }\n return Math.min(Math.max(Math.floor(limit), 1), MAX_CATALOG_LIMIT);\n}\n\nexport interface DatabaseExecutionResult {\n fields: string[];\n rows: Array<Record<string, unknown>>;\n}\n\n/**\n * Database adapter interface for abstracting database-specific operations.\n * Allows the SDK to work with multiple database types.\n */\nexport interface DatabaseAdapter {\n /**\n * Execute a SQL query and return results\n * @param sql - The SQL query to execute\n * @param params - Optional query parameters for parameterized queries\n */\n execute(\n sql: string,\n params?: Record<string, string | number | boolean | string[] | number[]>,\n ): Promise<DatabaseExecutionResult>;\n\n /**\n * Validate SQL query (e.g., using EXPLAIN)\n * Throws an error if the SQL is invalid\n * @param sql - The SQL query to validate\n * @param params - Optional query parameters for parameterized queries\n */\n validate(\n sql: string,\n params?: Record<string, string | number | boolean | string[] | number[]>,\n ): Promise<void>;\n\n /**\n * Introspect database schema metadata\n */\n introspect(options?: IntrospectOptions): Promise<SchemaIntrospection>;\n\n /**\n * Get the database dialect/type\n */\n getDialect(): DatabaseDialect;\n\n /**\n * Optional: List schemas with table counts (cheap catalog read for pickers).\n */\n listSchemas?(): Promise<CatalogSchema[]>;\n\n /**\n * Optional: List tables/views without columns (cheap catalog read for pickers).\n */\n listTables?(options?: ListTablesOptions): Promise<CatalogTable[]>;\n\n /**\n * Optional: Close/cleanup database connection\n */\n close?(): Promise<void>;\n}\n","import type {\n\tColumnSchema,\n\tIntrospectOptions,\n\tSchemaIntrospection,\n\tTableSchema,\n} from \"../schema/types\";\nimport {\n\ttype CatalogSchema,\n\ttype CatalogTable,\n\ttype DatabaseAdapter,\n\ttype DatabaseExecutionResult,\n\ttype ListTablesOptions,\n\tresolveCatalogLimit,\n} from \"./types\";\n\n/**\n * Request passed to BigQueryClientFn.\n * dryRun: when true, the client should run the query as a dry run (validation only, no execution).\n */\nexport interface BigQueryQueryRequest {\n\tquery: string;\n\tparams?: Record<string, string | number | boolean | string[] | number[]>;\n\t/** When true, validate the query without executing (e.g. BigQuery dry run). */\n\tdryRun?: boolean;\n}\n\nexport interface BigQueryQueryResult {\n\trows: Array<Record<string, unknown>>;\n\tfields: string[];\n}\n\n/**\n * Client function for the BigQuery adapter.\n * Implement using @google-cloud/bigquery: createQueryJob with dryRun for validation, getQueryResults for execute.\n */\nexport type BigQueryClientFn = (\n\trequest: BigQueryQueryRequest,\n) => Promise<BigQueryQueryResult>;\n\nexport interface BigQueryAdapterOptions {\n\t/** GCP project ID. Used for INFORMATION_SCHEMA queries. */\n\tprojectId: string;\n\t/** Optional project that owns the dataset. Defaults to projectId. */\n\tdatasetProjectId?: string;\n\t/** Default dataset (schema) name. Used for introspection and unqualified table names. */\n\tdataset: string;\n\t/** Optional location for the dataset (e.g. \"US\", \"EU\"). */\n\tlocation?: string;\n\t/** Logical database name used in introspection metadata. */\n\tdatabase?: string;\n\t/** Optional database kind label. Defaults to \"bigquery\". */\n\tkind?: SchemaIntrospection[\"db\"][\"kind\"];\n\t/**\n\t * Optional allow-list of table names (dataset.table or bare table name).\n\t * `*` or `<dataset>.*` allows every table in the dataset (same as no filter).\n\t * When specified, introspection and queries are restricted to these tables only.\n\t */\n\tallowedTables?: string[];\n}\n\ntype TableRow = {\n\ttable_name: string;\n\ttable_schema: string;\n\ttable_type: string;\n};\n\ntype ColumnRow = {\n\ttable_name: string;\n\ttable_schema: string;\n\tcolumn_name: string;\n\tdata_type: string;\n\tordinal_position: number;\n\tis_nullable: string | null;\n};\n\ninterface NormalizedTable {\n\tdataset: string;\n\ttable: string;\n}\n\n/**\n * BigQuery adapter following the same pattern as PostgresAdapter and ClickHouseAdapter.\n * - execute: runs the query via the client function.\n * - validate: uses dry run (no EXPLAIN in BigQuery) to validate SQL without executing.\n * - introspect: uses INFORMATION_SCHEMA.TABLES and INFORMATION_SCHEMA.COLUMNS.\n */\nexport class BigQueryAdapter implements DatabaseAdapter {\n\tprivate readonly projectId: string;\n\tprivate readonly datasetProjectId: string;\n\tprivate readonly defaultDataset: string;\n\tprivate readonly databaseName: string;\n\tprivate readonly kind: SchemaIntrospection[\"db\"][\"kind\"];\n\tprivate readonly allowedTables?: NormalizedTable[];\n\n\tconstructor(\n\t\tprivate readonly clientFn: BigQueryClientFn,\n\t\toptions: BigQueryAdapterOptions,\n\t) {\n\t\tthis.projectId = options.projectId;\n\t\tthis.datasetProjectId = options.datasetProjectId ?? options.projectId;\n\t\tthis.defaultDataset = options.dataset;\n\t\tthis.databaseName = options.database ?? options.dataset;\n\t\tthis.kind = options.kind ?? \"bigquery\";\n\t\tif (options.allowedTables) {\n\t\t\tthis.allowedTables = normalizeTableFilter(\n\t\t\t\toptions.allowedTables,\n\t\t\t\tthis.defaultDataset,\n\t\t\t);\n\t\t}\n\t}\n\n\tasync execute(\n\t\tsql: string,\n\t\tparams?: Record<string, string | number | boolean | string[] | number[]>,\n\t): Promise<DatabaseExecutionResult> {\n\t\tif (this.allowedTables) {\n\t\t\tthis.validateQueryTables(sql);\n\t\t}\n\n\t\tconst result = await this.clientFn({\n\t\t\tquery: sql,\n\t\t\tparams,\n\t\t\tdryRun: false,\n\t\t});\n\t\treturn { fields: result.fields, rows: result.rows };\n\t}\n\n\tasync validate(\n\t\tsql: string,\n\t\tparams?: Record<string, string | number | boolean | string[] | number[]>,\n\t): Promise<void> {\n\t\tawait this.clientFn({\n\t\t\tquery: sql,\n\t\t\tparams,\n\t\t\tdryRun: true,\n\t\t});\n\t}\n\n\tgetDialect() {\n\t\treturn \"bigquery\" as const;\n\t}\n\n\t/**\n\t * Introspect using BigQuery INFORMATION_SCHEMA (TABLES and COLUMNS).\n\t * Restricts to the default dataset (or allowedTables) and returns tables/columns only.\n\t */\n\tasync introspect(options?: IntrospectOptions): Promise<SchemaIntrospection> {\n\t\tconst tablesToIntrospect = options?.tables\n\t\t\t? normalizeTableFilter(options.tables, this.defaultDataset)\n\t\t\t: this.allowedTables;\n\t\tconst normalizedTables = tablesToIntrospect ?? [];\n\n\t\tconst tablesSql = buildTablesQuery(\n\t\t\tthis.datasetProjectId,\n\t\t\tthis.defaultDataset,\n\t\t\tnormalizedTables,\n\t\t);\n\t\tconst tablesResult = await this.clientFn({\n\t\t\tquery: tablesSql,\n\t\t\tdryRun: false,\n\t\t});\n\t\tconst tableRows = tablesResult.rows as unknown as TableRow[];\n\n\t\tconst columnsSql = buildColumnsQuery(\n\t\t\tthis.datasetProjectId,\n\t\t\tthis.defaultDataset,\n\t\t\tnormalizedTables,\n\t\t);\n\t\tconst columnsResult = await this.clientFn({\n\t\t\tquery: columnsSql,\n\t\t\tdryRun: false,\n\t\t});\n\t\tconst columnRows = columnsResult.rows as unknown as ColumnRow[];\n\n\t\tconst tablesByKey = new Map<string, TableSchema>();\n\n\t\tfor (const row of tableRows) {\n\t\t\tconst key = tableKey(row.table_schema, row.table_name);\n\t\t\tconst table: TableSchema = {\n\t\t\t\tname: row.table_name,\n\t\t\t\tschema: row.table_schema,\n\t\t\t\ttype: asTableType(row.table_type),\n\t\t\t\tcolumns: [],\n\t\t\t};\n\t\t\ttablesByKey.set(key, table);\n\t\t}\n\n\t\t// columnRows is ordered by ordinal_position, so columns are added in correct order\n\t\tfor (const row of columnRows) {\n\t\t\tconst key = tableKey(row.table_schema, row.table_name);\n\t\t\tconst table = tablesByKey.get(key);\n\t\t\tif (!table) continue;\n\n\t\t\tconst column: ColumnSchema = {\n\t\t\t\tname: row.column_name,\n\t\t\t\ttype: row.data_type,\n\t\t\t\tisPrimaryKey: false,\n\t\t\t};\n\t\t\ttable.columns.push(column);\n\t\t}\n\n\t\tconst tables = Array.from(tablesByKey.values()).sort((a, b) => {\n\t\t\tif (a.schema === b.schema) {\n\t\t\t\treturn a.name.localeCompare(b.name);\n\t\t\t}\n\t\t\treturn a.schema.localeCompare(b.schema);\n\t\t});\n\n\t\treturn {\n\t\t\tdb: {\n\t\t\t\tkind: this.kind,\n\t\t\t\tname: this.databaseName,\n\t\t\t},\n\t\t\ttables,\n\t\t\tintrospectedAt: new Date().toISOString(),\n\t\t};\n\t}\n\n\t/** The adapter is bound to one dataset, so this returns a single row. */\n\tasync listSchemas(): Promise<CatalogSchema[]> {\n\t\tconst result = await this.clientFn({\n\t\t\tquery: `SELECT COUNT(*) AS table_count\n FROM ${this.informationSchema(\"TABLES\")}`,\n\t\t\tdryRun: false,\n\t\t});\n\t\treturn [\n\t\t\t{\n\t\t\t\tschema: this.defaultDataset,\n\t\t\t\ttableCount: Number(result.rows[0]?.table_count ?? 0),\n\t\t\t},\n\t\t];\n\t}\n\n\t/**\n\t * List tables in the bound dataset. Reads the full catalog (not restricted\n\t * by `allowedTables`). Default limit 500, max 5000.\n\t */\n\tasync listTables(options: ListTablesOptions = {}): Promise<CatalogTable[]> {\n\t\tif (options.schema && options.schema !== this.defaultDataset) {\n\t\t\treturn [];\n\t\t}\n\t\tconst search = options.search?.trim();\n\t\tconst params: Record<string, string> = {};\n\t\tlet searchClause = \"\";\n\t\tif (search) {\n\t\t\tparams.search = `%${search.toLowerCase().replace(/[\\\\%_]/g, (ch) => `\\\\${ch}`)}%`;\n\t\t\tsearchClause = \"\\n WHERE LOWER(table_name) LIKE @search\";\n\t\t}\n\t\t// Integer produced by resolveCatalogLimit, never raw input.\n\t\tconst limit = resolveCatalogLimit(options.limit);\n\t\tconst result = await this.clientFn({\n\t\t\tquery: `SELECT table_schema, table_name, table_type\n FROM ${this.informationSchema(\"TABLES\")}${searchClause}\n ORDER BY table_name\n LIMIT ${limit}`,\n\t\t\t...(search ? { params } : {}),\n\t\t\tdryRun: false,\n\t\t});\n\t\treturn (result.rows as unknown as TableRow[]).map((row) => ({\n\t\t\tschema: row.table_schema,\n\t\t\tname: row.table_name,\n\t\t\ttype: asTableType(row.table_type),\n\t\t}));\n\t}\n\n\tprivate informationSchema(view: \"TABLES\"): string {\n\t\treturn `\\`${this.datasetProjectId}.${this.defaultDataset}.INFORMATION_SCHEMA.${view}\\``;\n\t}\n\n\tprivate validateQueryTables(sql: string): void {\n\t\tif (!this.allowedTables || this.allowedTables.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst allowedSet = new Set(\n\t\t\tthis.allowedTables.map((t) => tableKey(t.dataset, t.table)),\n\t\t);\n\n\t\t// Match FROM `project.dataset.table` or FROM dataset.table or FROM table\n\t\tconst tablePattern =\n\t\t\t/(?:FROM|JOIN)\\s+`?(?:[\\w-]+\\.)?([\\w-]+)\\.([\\w-]+)`?|(?:FROM|JOIN)\\s+`?([\\w-]+)`?/gi;\n\t\tconst matches = sql.matchAll(tablePattern);\n\n\t\tfor (const match of matches) {\n\t\t\tconst dataset = match[1] ?? match[3] ?? this.defaultDataset;\n\t\t\tconst table = match[2] ?? match[3];\n\t\t\tif (table) {\n\t\t\t\tconst key = tableKey(dataset, table);\n\t\t\t\tif (!allowedSet.has(key)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Query references table \"${dataset}.${table}\" which is not in the allowed tables list`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction normalizeTableFilter(\n\ttables: string[] | undefined,\n\tdefaultDataset: string,\n): NormalizedTable[] {\n\tif (!tables?.length) return [];\n\tconst normalized: NormalizedTable[] = [];\n\tconst seen = new Set<string>();\n\n\tfor (const raw of tables) {\n\t\tif (!raw?.trim()) continue;\n\t\tconst trimmed = raw.trim();\n\t\t// `*` / `<dataset>.*` = whole dataset = no filter.\n\t\tif (trimmed === \"*\" || trimmed === `${defaultDataset}.*`) return [];\n\t\tconst parts = trimmed.split(\".\");\n\t\tconst table = parts.pop() ?? \"\";\n\t\tif (table === \"*\") continue;\n\t\tconst dataset = parts.pop() ?? defaultDataset;\n\t\tconst key = tableKey(dataset, table);\n\t\tif (seen.has(key)) continue;\n\t\tseen.add(key);\n\t\tnormalized.push({ dataset, table });\n\t}\n\n\treturn normalized;\n}\n\nfunction tableKey(dataset: string, table: string): string {\n\treturn `${dataset}.${table}`;\n}\n\nfunction buildTablesQuery(\n\tdatasetProjectId: string,\n\tdataset: string,\n\ttables: NormalizedTable[],\n): string {\n\tconst qualifier = `\\`${datasetProjectId}.${dataset}.INFORMATION_SCHEMA.TABLES\\``;\n\tconst filter =\n\t\ttables.length > 0\n\t\t\t? ` AND table_name IN (${tables.map((t) => `'${escapeSql(t.table)}'`).join(\", \")})`\n\t\t\t: \"\";\n\treturn `SELECT table_name, table_schema, table_type\n FROM ${qualifier}\n WHERE table_schema = '${escapeSql(dataset)}'${filter}\n ORDER BY table_schema, table_name`;\n}\n\nfunction buildColumnsQuery(\n\tdatasetProjectId: string,\n\tdataset: string,\n\ttables: NormalizedTable[],\n): string {\n\tconst qualifier = `\\`${datasetProjectId}.${dataset}.INFORMATION_SCHEMA.COLUMNS\\``;\n\tconst filter =\n\t\ttables.length > 0\n\t\t\t? ` AND table_name IN (${tables.map((t) => `'${escapeSql(t.table)}'`).join(\", \")})`\n\t\t\t: \"\";\n\treturn `SELECT table_name, table_schema, column_name, data_type, ordinal_position, is_nullable\n FROM ${qualifier}\n WHERE table_schema = '${escapeSql(dataset)}'${filter}\n ORDER BY table_schema, table_name, ordinal_position`;\n}\n\nfunction escapeSql(value: string): string {\n\treturn value.replace(/\\\\/g, \"\\\\\\\\\").replace(/'/g, \"''\");\n}\n\nfunction asTableType(tableType: string): CatalogTable[\"type\"] {\n\tconst normalized = tableType?.toLowerCase() ?? \"\";\n\tif (normalized.includes(\"view\")) {\n\t\treturn normalized.includes(\"materialized\") ? \"materialized_view\" : \"view\";\n\t}\n\tif (normalized.includes(\"external\") || normalized.includes(\"snapshot\") || normalized.includes(\"clone\")) {\n\t\treturn \"table\";\n\t}\n\treturn \"table\";\n}\n","const WRAPPER_REGEX =\n /^(Nullable|LowCardinality|SimpleAggregateFunction)\\((.+)\\)$/i;\n\nexport function isNullableType(type: string): boolean {\n return /Nullable\\s*\\(/i.test(type);\n}\n\nexport function unwrapTypeModifiers(type: string): string {\n let current = type.trim();\n let match = WRAPPER_REGEX.exec(current);\n while (match) {\n const inner = match[2];\n if (!inner) {\n break;\n }\n current = inner.trim();\n match = WRAPPER_REGEX.exec(current);\n }\n return current;\n}\n\nexport function extractPrecisionScale(type: string): {\n precision?: number;\n scale?: number;\n} {\n const unwrapped = unwrapTypeModifiers(type);\n const decimalMatch = unwrapped.match(/Decimal(?:\\d+)?\\((\\d+)\\s*,\\s*(\\d+)\\)/i);\n if (!decimalMatch) return {};\n const precision = decimalMatch[1];\n const scale = decimalMatch[2];\n if (!precision || !scale) return {};\n return {\n precision: Number.parseInt(precision, 10),\n scale: Number.parseInt(scale, 10),\n };\n}\n\nexport function extractFixedStringLength(type: string): number | undefined {\n const unwrapped = unwrapTypeModifiers(type);\n const match = unwrapped.match(/^(?:FixedString|StringFixed)\\((\\d+)\\)$/i);\n if (!match) return undefined;\n const length = match[1];\n if (!length) return undefined;\n return Number.parseInt(length, 10);\n}\n\nexport function parseKeyExpression(expression?: string | null): string[] {\n if (!expression) return [];\n let value = expression.trim();\n if (!value) return [];\n if (/^tuple\\s*\\(/i.test(value) && value.endsWith(\")\")) {\n value = value.replace(/^tuple\\s*\\(/i, \"\").replace(/\\)$/, \"\");\n }\n\n const columns: string[] = [];\n let depth = 0;\n let token = \"\";\n for (const ch of value) {\n if (ch === \"(\") {\n depth += 1;\n token += ch;\n continue;\n }\n if (ch === \")\") {\n depth = Math.max(0, depth - 1);\n token += ch;\n continue;\n }\n if (ch === \",\" && depth === 0) {\n const col = token.trim();\n if (col) columns.push(stripWrapper(col));\n token = \"\";\n continue;\n }\n token += ch;\n }\n const last = token.trim();\n if (last) columns.push(stripWrapper(last));\n return columns.filter(Boolean);\n}\n\nfunction stripWrapper(value: string): string {\n const noQuotes = stripQuotes(value);\n const withoutTicks = noQuotes.replace(/`/g, \"\").trim();\n const parts = withoutTicks.split(\".\");\n return parts[parts.length - 1]?.trim() ?? \"\";\n}\n\nfunction stripQuotes(value: string): string {\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n return value.slice(1, -1);\n }\n return value;\n}\n","import type {\n\tClickHouseSettings,\n\tDataFormat,\n\tQueryParams,\n} from \"@clickhouse/client\";\nimport type {\n\tColumnSchema,\n\tIntrospectOptions,\n\tSchemaIntrospection,\n\tTableSchema,\n} from \"../schema/types\";\nimport { parseKeyExpression, unwrapTypeModifiers } from \"../utils/clickhouse\";\nimport {\n\ttype CatalogSchema,\n\ttype CatalogTable,\n\ttype DatabaseAdapter,\n\ttype DatabaseExecutionResult,\n\ttype ListTablesOptions,\n\tresolveCatalogLimit,\n} from \"./types\";\n\nexport interface ClickHouseAdapterOptions {\n\t/** Optional logical database name used in introspection metadata. */\n\tdatabase?: string;\n\t/** Override the default response format used for query execution. */\n\tdefaultFormat?: DataFormat;\n\t/**\n\t * Optional database kind label. Defaults to \"clickhouse\" but allows\n\t * sub-classing/custom branding if needed.\n\t */\n\tkind?: SchemaIntrospection[\"db\"][\"kind\"];\n\t/**\n\t * Optional allow-list of table names.\n\t * When specified, introspection and queries are restricted to these tables only.\n\t * ClickHouse tables are not schema-qualified, so just provide table names.\n\t * `*` or `<database>.*` allows every table (same as no filter).\n\t */\n\tallowedTables?: string[];\n}\n\nexport type ClickHouseQueryResult = { json: () => Promise<unknown> };\n\nexport type ClickHouseClientFn = (\n\tparams: QueryParams,\n) => Promise<\n\t| ClickHouseQueryResult\n\t| Array<Record<string, unknown>>\n\t| Record<string, unknown>[]\n>;\n\ninterface QueryOptions {\n\tparams?: Record<string, unknown>;\n\tformat?: DataFormat;\n\tsettings?: ClickHouseSettings;\n}\n\ntype TableRow = {\n\tname: string;\n\tengine: string;\n\tcomment: string | null;\n\tprimary_key: string | null;\n};\n\ntype ColumnRow = {\n\ttable: string;\n\tname: string;\n\ttype: string;\n\tposition: number;\n\tcomment: string | null;\n\tis_in_primary_key: string | number | null;\n};\n\n/**\n * Simplified ClickHouse adapter following IngestRequest format\n * Removed: indexes, constraints, statistics\n * Kept only: tables, columns (name, type, isPrimaryKey, comment)\n */\nexport class ClickHouseAdapter implements DatabaseAdapter {\n\tprivate readonly databaseName: string;\n\tprivate readonly defaultFormat: QueryParams[\"format\"];\n\tprivate readonly kind: SchemaIntrospection[\"db\"][\"kind\"];\n\tprivate readonly allowedTables?: string[];\n\n\tconstructor(\n\t\tprivate readonly clientFn: ClickHouseClientFn,\n\t\toptions: ClickHouseAdapterOptions = {},\n\t) {\n\t\tthis.databaseName = options.database ?? \"default\";\n\t\tthis.defaultFormat = options.defaultFormat ?? \"JSONEachRow\";\n\t\tthis.kind = options.kind ?? \"clickhouse\";\n\t\tif (options.allowedTables) {\n\t\t\tthis.allowedTables = normalizeTableFilter(\n\t\t\t\toptions.allowedTables,\n\t\t\t\tthis.databaseName,\n\t\t\t);\n\t\t}\n\t}\n\n\tasync execute(\n\t\tsql: string,\n\t\tparams?: Record<string, string | number | boolean | string[] | number[]>,\n\t): Promise<DatabaseExecutionResult> {\n\t\t// Validate query against allowed tables if restrictions are in place\n\t\tif (this.allowedTables) {\n\t\t\tthis.validateQueryTables(sql);\n\t\t}\n\n\t\tconst queryOptions: QueryOptions = {\n\t\t\tformat: this.defaultFormat,\n\t\t};\n\t\tif (params) {\n\t\t\tqueryOptions.params = params;\n\t\t}\n\n\t\tconst rows = await this.query<Record<string, unknown>>(sql, queryOptions);\n\t\tconst fields = rows.length > 0 ? Object.keys(rows[0] ?? {}) : [];\n\t\treturn { fields, rows };\n\t}\n\n\tasync validate(\n\t\tsql: string,\n\t\tparams?: Record<string, string | number | boolean | string[] | number[]>,\n\t): Promise<void> {\n\t\tconst queryOptions: QueryOptions = {\n\t\t\tformat: this.defaultFormat,\n\t\t};\n\t\tif (params) {\n\t\t\tqueryOptions.params = params;\n\t\t}\n\n\t\tawait this.query(`EXPLAIN ${sql}`, queryOptions);\n\t}\n\n\tgetDialect() {\n\t\treturn \"clickhouse\" as const;\n\t}\n\n\t/**\n\t * Simplified introspection: only collect table/column metadata for IngestRequest\n\t * No indexes, constraints, or statistics\n\t */\n\tasync introspect(options?: IntrospectOptions): Promise<SchemaIntrospection> {\n\t\t// Use adapter-level allowedTables if no specific tables provided in options\n\t\tconst tablesToIntrospect = options?.tables\n\t\t\t? normalizeTableFilter(options.tables, this.databaseName)\n\t\t\t: this.allowedTables;\n\t\tconst allowTables = tablesToIntrospect ?? [];\n\t\tconst hasFilter = allowTables.length > 0;\n\t\tconst queryParams: Record<string, unknown> = {\n\t\t\tdb: this.databaseName,\n\t\t};\n\t\tif (hasFilter) {\n\t\t\tqueryParams.tables = allowTables;\n\t\t}\n\n\t\tconst filterClause = hasFilter ? \" AND name IN {tables:Array(String)}\" : \"\";\n\t\tconst tables = await this.query<TableRow>(\n\t\t\t`SELECT name, engine, comment, primary_key\n FROM system.tables\n WHERE database = {db:String}${filterClause}\n ORDER BY name`,\n\t\t\t{ params: queryParams },\n\t\t);\n\n\t\tconst columnFilterClause = hasFilter\n\t\t\t? \" AND table IN {tables:Array(String)}\"\n\t\t\t: \"\";\n\t\tconst columns = await this.query<ColumnRow>(\n\t\t\t`SELECT table, name, type, position, comment, is_in_primary_key\n FROM system.columns\n WHERE database = {db:String}${columnFilterClause}\n ORDER BY table, position`,\n\t\t\t{ params: queryParams },\n\t\t);\n\n\t\tconst columnsByTable = new Map<string, ColumnSchema[]>();\n\t\tfor (const rawColumn of columns) {\n\t\t\tconst list = columnsByTable.get(rawColumn.table) ?? [];\n\t\t\tlist.push(transformColumnRow(rawColumn));\n\t\t\tcolumnsByTable.set(rawColumn.table, list);\n\t\t}\n\n\t\tconst tableSchemas: TableSchema[] = tables.map((table) => {\n\t\t\tconst tableColumns = columnsByTable.get(table.name) ?? [];\n\t\t\tconst primaryKeyColumns = parseKeyExpression(table.primary_key);\n\n\t\t\t// Mark columns as primary key\n\t\t\tfor (const column of tableColumns) {\n\t\t\t\tcolumn.isPrimaryKey =\n\t\t\t\t\tcolumn.isPrimaryKey || primaryKeyColumns.includes(column.name);\n\t\t\t}\n\n\t\t\tconst base: TableSchema = {\n\t\t\t\tname: table.name,\n\t\t\t\tschema: this.databaseName,\n\t\t\t\ttype: asTableType(table.engine),\n\t\t\t\tcolumns: tableColumns,\n\t\t\t};\n\n\t\t\tconst comment = sanitize(table.comment);\n\t\t\tif (comment !== undefined) {\n\t\t\t\tbase.comment = comment;\n\t\t\t}\n\n\t\t\treturn base;\n\t\t});\n\n\t\treturn {\n\t\t\tdb: {\n\t\t\t\tkind: this.kind,\n\t\t\t\tname: this.databaseName,\n\t\t\t},\n\t\t\ttables: tableSchemas,\n\t\t\tintrospectedAt: new Date().toISOString(),\n\t\t};\n\t}\n\n\t/** The adapter is bound to one database, so this returns a single row. */\n\tasync listSchemas(): Promise<CatalogSchema[]> {\n\t\tconst rows = await this.query<{ table_count: string | number }>(\n\t\t\t`SELECT count() AS table_count\n FROM system.tables\n WHERE database = {db:String}`,\n\t\t\t{ params: { db: this.databaseName } },\n\t\t);\n\t\treturn [\n\t\t\t{\n\t\t\t\tschema: this.databaseName,\n\t\t\t\ttableCount: toNumber(rows[0]?.table_count) ?? 0,\n\t\t\t},\n\t\t];\n\t}\n\n\t/**\n\t * List tables in the bound database. Reads the full catalog (not restricted\n\t * by `allowedTables`). Default limit 500, max 5000.\n\t */\n\tasync listTables(options: ListTablesOptions = {}): Promise<CatalogTable[]> {\n\t\tif (options.schema && options.schema !== this.databaseName) {\n\t\t\treturn [];\n\t\t}\n\t\tconst params: Record<string, unknown> = {\n\t\t\tdb: this.databaseName,\n\t\t\tlimit: resolveCatalogLimit(options.limit),\n\t\t};\n\t\tconst search = options.search?.trim();\n\t\tlet searchClause = \"\";\n\t\tif (search) {\n\t\t\tparams.search = search;\n\t\t\tsearchClause = \" AND positionCaseInsensitive(name, {search:String}) > 0\";\n\t\t}\n\t\tconst rows = await this.query<{ name: string; engine: string }>(\n\t\t\t`SELECT name, engine\n FROM system.tables\n WHERE database = {db:String}${searchClause}\n ORDER BY name\n LIMIT {limit:UInt32}`,\n\t\t\t{ params },\n\t\t);\n\t\treturn rows.map((row) => ({\n\t\t\tschema: this.databaseName,\n\t\t\tname: row.name,\n\t\t\ttype: asCatalogTableType(row.engine),\n\t\t}));\n\t}\n\n\tprivate validateQueryTables(sql: string): void {\n\t\tif (!this.allowedTables || this.allowedTables.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst allowedSet = new Set(this.allowedTables);\n\n\t\t// Extract potential table references from SQL\n\t\tconst tablePattern =\n\t\t\t/\\b(FROM|JOIN)\\s+(?:FINAL\\s+)?(?:(?:[a-zA-Z_][a-zA-Z0-9_]*)\\.)?([\"'`]?[a-zA-Z_][a-zA-Z0-9_]*[\"'`]?)/gi;\n\t\tconst matches = sql.matchAll(tablePattern);\n\n\t\tfor (const match of matches) {\n\t\t\tconst clause = match[1]?.toUpperCase();\n\t\t\tconst matchIndex = match.index ?? -1;\n\t\t\tif (\n\t\t\t\tclause === \"JOIN\" &&\n\t\t\t\tmatchIndex >= 0 &&\n\t\t\t\tisArrayJoinClause(sql, matchIndex)\n\t\t\t) {\n\t\t\t\t// ClickHouse ARRAY JOIN expands array-typed columns, not tables.\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst table = match[2]?.replace(/[\"'`]/g, \"\");\n\t\t\tif (table) {\n\t\t\t\tif (!allowedSet.has(table)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Query references table \"${table}\" which is not in the allowed tables list`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// No-op: lifecycle of the underlying client is controlled by the caller.\n\t}\n\n\tprivate async query<T>(sql: string, options?: QueryOptions): Promise<T[]> {\n\t\tconst params: QueryParams = {\n\t\t\tquery: sql,\n\t\t};\n\n\t\tconst format = options?.format ?? this.defaultFormat;\n\t\tif (format !== undefined) {\n\t\t\tparams.format = format;\n\t\t}\n\n\t\tif (options?.params) {\n\t\t\tparams.query_params = options.params;\n\t\t}\n\n\t\tif (options?.settings) {\n\t\t\tparams.clickhouse_settings = options.settings;\n\t\t}\n\n\t\tconst result = await this.clientFn(params);\n\t\treturn this.extractRows<T>(result);\n\t}\n\n\tprivate async extractRows<T>(\n\t\tresult:\n\t\t\t| ClickHouseQueryResult\n\t\t\t| Array<Record<string, unknown>>\n\t\t\t| Record<string, unknown>[],\n\t): Promise<T[]> {\n\t\tif (Array.isArray(result)) {\n\t\t\treturn result as T[];\n\t\t}\n\n\t\tif (\n\t\t\tresult &&\n\t\t\ttypeof (result as ClickHouseQueryResult).json === \"function\"\n\t\t) {\n\t\t\tconst payload = await (result as ClickHouseQueryResult).json();\n\t\t\treturn normalizePayload<T>(payload);\n\t\t}\n\n\t\treturn [];\n\t}\n}\n\nfunction normalizePayload<T>(payload: unknown): T[] {\n\tif (Array.isArray(payload)) {\n\t\treturn payload as T[];\n\t}\n\tif (payload && typeof payload === \"object\") {\n\t\tconst maybeData = (payload as { data?: unknown }).data;\n\t\tif (Array.isArray(maybeData)) {\n\t\t\treturn maybeData as T[];\n\t\t}\n\t}\n\treturn [];\n}\n\n/**\n * Normalize to bare table names. `*` or `<database>.*` means \"all tables\" and\n * returns an empty list (no filter); wildcards for other databases are ignored.\n */\nfunction normalizeTableFilter(\n\ttables: string[] | null | undefined,\n\tdatabaseName: string,\n): string[] {\n\tif (!tables?.length) return [];\n\tconst seen = new Set<string>();\n\tconst normalized: string[] = [];\n\tfor (const table of tables) {\n\t\tif (!table) continue;\n\t\tconst trimmed = table.trim();\n\t\tif (!trimmed) continue;\n\t\tif (trimmed === \"*\" || trimmed === `${databaseName}.*`) return [];\n\t\tconst parts = trimmed.split(\".\");\n\t\tconst tableName = parts[parts.length - 1];\n\t\tif (tableName === \"*\") continue;\n\t\tif (!tableName || seen.has(tableName)) continue;\n\t\tseen.add(tableName);\n\t\tnormalized.push(tableName);\n\t}\n\treturn normalized;\n}\n\nfunction isArrayJoinClause(sql: string, joinIndex: number): boolean {\n\tconst beforeJoin = sql.slice(0, joinIndex);\n\tconst previousToken = beforeJoin.trim().split(/\\s+/).pop()?.toUpperCase();\n\treturn previousToken === \"ARRAY\";\n}\n\nfunction transformColumnRow(row: ColumnRow): ColumnSchema {\n\tconst unwrappedType = unwrapTypeModifiers(row.type);\n\n\tconst column: ColumnSchema = {\n\t\tname: row.name,\n\t\ttype: unwrappedType,\n\t\trawType: row.type,\n\t\tisPrimaryKey: Boolean(toNumber(row.is_in_primary_key)),\n\t};\n\n\tconst comment = sanitize(row.comment);\n\tif (comment !== undefined) column.comment = comment;\n\n\treturn column;\n}\n\nfunction asTableType(engine: unknown): TableSchema[\"type\"] {\n\tif (typeof engine === \"string\") {\n\t\tconst normalized = engine.toLowerCase();\n\t\t// ClickHouse view engines: View, MaterializedView, LiveView\n\t\tif (normalized.includes(\"view\")) {\n\t\t\treturn \"view\";\n\t\t}\n\t}\n\treturn \"table\";\n}\n\nfunction asCatalogTableType(engine: unknown): CatalogTable[\"type\"] {\n\tconst normalized = typeof engine === \"string\" ? engine.toLowerCase() : \"\";\n\tif (normalized === \"materializedview\") return \"materialized_view\";\n\tif (normalized.includes(\"view\")) return \"view\";\n\treturn \"table\";\n}\n\nfunction sanitize(value: unknown): string | undefined {\n\tif (value === null || value === undefined) return undefined;\n\tconst trimmed = String(value).trim();\n\treturn trimmed.length ? trimmed : undefined;\n}\n\nfunction toNumber(value: unknown): number | undefined {\n\tif (value === null || value === undefined) return undefined;\n\tif (typeof value === \"number\") return value;\n\tconst parsed = Number.parseFloat(String(value));\n\treturn Number.isNaN(parsed) ? undefined : parsed;\n}\n","import type {\n\tColumnSchema,\n\tIntrospectOptions,\n\tSchemaIntrospection,\n\tTableSchema,\n} from \"../schema/types\";\nimport {\n\ttype CatalogSchema,\n\ttype CatalogTable,\n\ttype DatabaseAdapter,\n\ttype DatabaseExecutionResult,\n\ttype ListTablesOptions,\n\tresolveCatalogLimit,\n} from \"./types\";\n\nexport interface PostgresQueryResult {\n\trows: Array<Record<string, unknown>>;\n\tfields: Array<{ name: string }>;\n}\n\nexport type PostgresClientFn = (\n\tsql: string,\n\tparams?: unknown[],\n) => Promise<PostgresQueryResult>;\n\nexport type PostgresWireDialect = \"postgres\" | \"redshift\";\n\nexport interface PostgresAdapterOptions {\n\t/** Logical database name used in introspection metadata. */\n\tdatabase?: string;\n\t/** Schema to assume when a table is provided without qualification. */\n\tdefaultSchema?: string;\n\t/** Optional database kind label. Defaults to the dialect. */\n\tkind?: SchemaIntrospection[\"db\"][\"kind\"];\n\t/**\n\t * Wire-compatible engine behind the client. \"redshift\" uses Redshift's\n\t * svv_* catalog views for introspection. Defaults to \"postgres\".\n\t */\n\tdialect?: PostgresWireDialect;\n\t/**\n\t * Optional allow-list of table names (schema-qualified or bare).\n\t * Use `schema.*` to allow every table in a schema (a bare `*` means the\n\t * default schema). When specified, introspection and queries are restricted\n\t * to these tables only.\n\t */\n\tallowedTables?: string[];\n}\n\ntype TableRow = {\n\ttable_name: string;\n\tschema_name: string;\n\ttable_type: string;\n\tcomment: string | null;\n};\n\ntype ColumnRow = {\n\ttable_name: string;\n\ttable_schema: string;\n\tcolumn_name: string;\n\tdata_type: string;\n\tudt_name: string | null;\n\tis_primary_key: boolean;\n\tdescription: string | null;\n};\n\n/** `table` is `\"*\"` when every table in `schema` is allowed. */\nexport interface NormalizedTable {\n\tschema: string;\n\ttable: string;\n}\n\nconst WILDCARD = \"*\";\n\n/** Highest $N index referenced in SQL (0 when the query uses literal values only). */\nexport function maxPostgresPlaceholderIndex(sql: string): number {\n\tlet max = 0;\n\tfor (const match of sql.matchAll(/\\$(\\d+)\\b/g)) {\n\t\tconst index = Number.parseInt(match[1], 10);\n\t\tif (Number.isFinite(index) && index > max) {\n\t\t\tmax = index;\n\t\t}\n\t}\n\treturn max;\n}\n\ntype PostgresParamRecord = Record<\n\tstring,\n\tstring | number | boolean | string[] | number[]\n>;\n\n/**\n * Drop bind parameters when SQL has no $1..$N placeholders (common after LLM inlines literals).\n */\nexport function filterParamsForPostgresSql(\n\tsql: string,\n\tparams: PostgresParamRecord | undefined,\n): PostgresParamRecord | undefined {\n\tif (!params || Object.keys(params).length === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst placeholderCount = maxPostgresPlaceholderIndex(sql);\n\tif (placeholderCount === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst hasNumericKeys = Object.keys(params).some((key) => /^\\d+$/.test(key));\n\tif (hasNumericKeys) {\n\t\tconst filtered: PostgresParamRecord = {};\n\t\tfor (let index = 1; index <= placeholderCount; index += 1) {\n\t\t\tconst key = String(index);\n\t\t\tif (key in params) {\n\t\t\t\tfiltered[key] = params[key];\n\t\t\t}\n\t\t}\n\t\treturn Object.keys(filtered).length > 0 ? filtered : undefined;\n\t}\n\n\treturn params;\n}\n\n/**\n * Simplified PostgreSQL adapter following IngestRequest format\n * Removed: indexes, constraints, foreign keys, statistics\n * Kept only: tables, columns (name, type, isPrimaryKey, comment)\n */\nexport class PostgresAdapter implements DatabaseAdapter {\n\tprivate readonly databaseName: string;\n\tprivate readonly defaultSchema: string;\n\tprivate readonly kind: SchemaIntrospection[\"db\"][\"kind\"];\n\tprivate readonly dialect: PostgresWireDialect;\n\tprivate readonly allowedTables?: NormalizedTable[];\n\n\tconstructor(\n\t\tprivate readonly clientFn: PostgresClientFn,\n\t\toptions: PostgresAdapterOptions = {},\n\t) {\n\t\tthis.databaseName = options.database ?? \"postgres\";\n\t\tthis.defaultSchema = options.defaultSchema ?? \"public\";\n\t\tthis.dialect = options.dialect ?? \"postgres\";\n\t\tthis.kind = options.kind ?? this.dialect;\n\t\tif (options.allowedTables) {\n\t\t\tthis.allowedTables = normalizeTableFilter(\n\t\t\t\toptions.allowedTables,\n\t\t\t\tthis.defaultSchema,\n\t\t\t);\n\t\t}\n\t}\n\n\tasync execute(\n\t\tsql: string,\n\t\tparams?: Record<string, string | number | boolean | string[] | number[]>,\n\t): Promise<DatabaseExecutionResult> {\n\t\t// Validate query against allowed tables if restrictions are in place\n\t\tif (this.allowedTables) {\n\t\t\tthis.validateQueryTables(sql);\n\t\t}\n\n\t\tconst boundParams = filterParamsForPostgresSql(sql, params);\n\n\t\t// Convert named params to positional array for PostgreSQL\n\t\tlet paramArray: unknown[] | undefined;\n\t\tif (boundParams) {\n\t\t\tparamArray = this.convertNamedToPositionalParams(sql, boundParams);\n\t\t}\n\n\t\tconst result = await this.clientFn(sql, paramArray);\n\t\tconst fields = result.fields.map((f) => f.name);\n\t\treturn { fields, rows: result.rows };\n\t}\n\n\tprivate validateQueryTables(sql: string): void {\n\t\tif (!this.allowedTables || this.allowedTables.length === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst allowedSet = new Set(\n\t\t\tthis.allowedTables.map((t) => tableKey(t.schema, t.table)),\n\t\t);\n\t\tconst allowedSchemas = new Set(\n\t\t\tthis.allowedTables\n\t\t\t\t.filter((t) => t.table === WILDCARD)\n\t\t\t\t.map((t) => t.schema),\n\t\t);\n\n\t\t// First, neutralize function calls that use FROM keyword (EXTRACT, SUBSTRING, TRIM, etc.)\n\t\t// Replace their FROM with a placeholder to avoid false positives\n\t\tconst neutralizedSql = sql\n\t\t\t.replace(/EXTRACT\\s*\\([^)]*FROM\\s+[^)]+\\)/gi, \"EXTRACT(/*neutralized*/)\")\n\t\t\t.replace(/SUBSTRING\\s*\\([^)]*FROM\\s+[^)]+\\)/gi, \"SUBSTRING(/*neutralized*/)\")\n\t\t\t.replace(/TRIM\\s*\\([^)]*FROM\\s+[^)]+\\)/gi, \"TRIM(/*neutralized*/)\")\n\t\t\t.replace(/POSITION\\s*\\([^)]*FROM\\s+[^)]+\\)/gi, \"POSITION(/*neutralized*/)\");\n\n\t\t// Extract potential table references from SQL\n\t\tconst tablePattern =\n\t\t\t/(?:FROM|JOIN)\\s+(?:ONLY\\s+)?(?:([a-zA-Z_][a-zA-Z0-9_]*)\\.)?([\"']?[a-zA-Z_][a-zA-Z0-9_]*[\"']?)/gi;\n\t\tconst matches = neutralizedSql.matchAll(tablePattern);\n\n\t\tfor (const match of matches) {\n\t\t\tconst schema = match[1] ?? this.defaultSchema;\n\t\t\tconst table = match[2]?.replace(/['\"]/g, \"\");\n\t\t\tif (table) {\n\t\t\t\tconst key = tableKey(schema, table);\n\t\t\t\tif (!allowedSet.has(key) && !allowedSchemas.has(schema)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Query references table \"${schema}.${table}\" which is not in the allowed tables list`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Convert params to positional array for PostgreSQL ($1, $2, …).\n\t * Values must align with v2 generation order via numeric keys from mapGeneratedParams.\n\t */\n\tprivate convertNamedToPositionalParams(\n\t\tsql: string,\n\t\tparams: Record<string, string | number | boolean | string[] | number[]>,\n\t): unknown[] {\n\t\tconst placeholderCount = maxPostgresPlaceholderIndex(sql);\n\t\tif (placeholderCount === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst positionalParams: unknown[] = [];\n\n\t\tfor (let index = 1; index <= placeholderCount; index += 1) {\n\t\t\tconst key = String(index);\n\t\t\tlet val: unknown = params[key];\n\n\t\t\tif (val === undefined && placeholderCount === 1) {\n\t\t\t\tconst namedKeys = Object.keys(params).filter((k) => !/^\\d+$/.test(k));\n\t\t\t\tif (namedKeys.length === 1) {\n\t\t\t\t\tval = params[namedKeys[0] as keyof typeof params];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (val === undefined) {\n\t\t\t\tthrow new Error(`Missing value for PostgreSQL parameter $${index}`);\n\t\t\t}\n\n\t\t\tif (typeof val === \"string\") {\n\t\t\t\tconst match = val.match(/^<([a-zA-Z0-9_]+)>$/);\n\t\t\t\tconst namedKey = match?.[1];\n\t\t\t\tif (namedKey && namedKey in params) {\n\t\t\t\t\tval = params[namedKey as keyof typeof params];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpositionalParams.push(val);\n\t\t}\n\n\t\treturn positionalParams;\n\t}\n\n\tasync validate(\n\t\tsql: string,\n\t\tparams?: Record<string, string | number | boolean | string[] | number[]>,\n\t): Promise<void> {\n\t\tconst boundParams = filterParamsForPostgresSql(sql, params);\n\t\tlet paramArray: unknown[] | undefined;\n\t\tif (boundParams) {\n\t\t\tparamArray = this.convertNamedToPositionalParams(sql, boundParams);\n\t\t}\n\n\t\tawait this.clientFn(`EXPLAIN ${sql}`, paramArray);\n\t}\n\n\tgetDialect(): PostgresWireDialect {\n\t\treturn this.dialect;\n\t}\n\n\t/**\n\t * List schemas with their table/view counts. Reads the full catalog (not\n\t * restricted by `allowedTables`) so it can back an allowed-tables picker.\n\t */\n\tasync listSchemas(): Promise<CatalogSchema[]> {\n\t\tconst result = await this.clientFn(\n\t\t\tthis.dialect === \"redshift\"\n\t\t\t\t? buildRedshiftListSchemasQuery()\n\t\t\t\t: buildListSchemasQuery(),\n\t\t);\n\t\treturn result.rows.map((row) => ({\n\t\t\tschema: String(row.schema),\n\t\t\ttableCount: Number(row.table_count ?? 0),\n\t\t}));\n\t}\n\n\t/**\n\t * List tables/views, optionally filtered by schema and a case-insensitive\n\t * substring of the table name. Reads the full catalog (not restricted by\n\t * `allowedTables`). Default limit 500, max 5000.\n\t */\n\tasync listTables(options: ListTablesOptions = {}): Promise<CatalogTable[]> {\n\t\tconst { sql, params } = buildListTablesQuery(this.dialect, options);\n\t\tconst result = await this.clientFn(sql, params);\n\t\treturn result.rows.map((row) => ({\n\t\t\tschema: String(row.schema),\n\t\t\tname: String(row.name),\n\t\t\ttype: asTableType(String(row.table_type ?? \"\")),\n\t\t}));\n\t}\n\n\t/**\n\t * Simplified introspection: only collect table/column metadata for IngestRequest\n\t * No indexes, constraints, or statistics\n\t */\n\tasync introspect(options?: IntrospectOptions): Promise<SchemaIntrospection> {\n\t\t// Use adapter-level allowedTables if no specific tables provided in options\n\t\tconst tablesToIntrospect = options?.tables\n\t\t\t? normalizeTableFilter(options.tables, this.defaultSchema)\n\t\t\t: this.allowedTables;\n\t\tconst normalizedTables = tablesToIntrospect ?? [];\n\n\t\tconst isRedshift = this.dialect === \"redshift\";\n\t\tconst tablesResult = await this.clientFn(\n\t\t\tisRedshift\n\t\t\t\t? buildRedshiftTablesQuery(normalizedTables)\n\t\t\t\t: buildTablesQuery(normalizedTables),\n\t\t);\n\t\tconst tableRows = tablesResult.rows as TableRow[];\n\n\t\tconst columnsResult = await this.clientFn(\n\t\t\tisRedshift\n\t\t\t\t? buildRedshiftColumnsQuery(normalizedTables)\n\t\t\t\t: buildColumnsQuery(normalizedTables),\n\t\t);\n\t\tconst columnRows = columnsResult.rows as ColumnRow[];\n\n\t\tconst tablesByKey = new Map<string, TableSchema>();\n\n\t\t// Build tables\n\t\tfor (const row of tableRows) {\n\t\t\tconst key = tableKey(row.schema_name, row.table_name);\n\t\t\tconst table: TableSchema = {\n\t\t\t\tname: row.table_name,\n\t\t\t\tschema: row.schema_name,\n\t\t\t\ttype: asTableType(row.table_type),\n\t\t\t\tcolumns: [],\n\t\t\t};\n\n\t\t\tconst comment = sanitize(row.comment);\n\t\t\tif (comment !== undefined) {\n\t\t\t\ttable.comment = comment;\n\t\t\t}\n\n\t\t\ttablesByKey.set(key, table);\n\t\t}\n\n\t\t// Build columns\n\t\tfor (const row of columnRows) {\n\t\t\tconst key = tableKey(row.table_schema, row.table_name);\n\t\t\tconst table = tablesByKey.get(key);\n\t\t\tif (!table) continue;\n\n\t\t\tconst column: ColumnSchema = {\n\t\t\t\tname: row.column_name,\n\t\t\t\ttype: row.data_type,\n\t\t\t\tisPrimaryKey: row.is_primary_key,\n\t\t\t};\n\n\t\t\tconst rawType = row.udt_name ?? undefined;\n\t\t\tif (rawType !== undefined) column.rawType = rawType;\n\n\t\t\tconst comment = sanitize(row.description);\n\t\t\tif (comment !== undefined) column.comment = comment;\n\n\t\t\ttable.columns.push(column);\n\t\t}\n\n\t\tconst tables = Array.from(tablesByKey.values()).sort((a, b) => {\n\t\t\tif (a.schema === b.schema) {\n\t\t\t\treturn a.name.localeCompare(b.name);\n\t\t\t}\n\t\t\treturn a.schema.localeCompare(b.schema);\n\t\t});\n\n\t\treturn {\n\t\t\tdb: {\n\t\t\t\tkind: this.kind,\n\t\t\t\tname: this.databaseName,\n\t\t\t},\n\t\t\ttables,\n\t\t\tintrospectedAt: new Date().toISOString(),\n\t\t};\n\t}\n}\n\nfunction normalizeTableFilter(\n\ttables: string[] | undefined,\n\tdefaultSchema: string,\n): NormalizedTable[] {\n\tif (!tables?.length) return [];\n\tconst normalized: NormalizedTable[] = [];\n\tconst seen = new Set<string>();\n\n\tfor (const raw of tables) {\n\t\tif (!raw) continue;\n\t\tconst trimmed = raw.trim();\n\t\tif (!trimmed) continue;\n\t\tconst parts = trimmed.split(\".\");\n\t\tconst table = parts.pop() ?? \"\";\n\t\tconst schema = parts.pop() ?? defaultSchema;\n\t\tif (\n\t\t\t!isSafeIdentifier(schema) ||\n\t\t\t(table !== WILDCARD && !isSafeIdentifier(table))\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst key = tableKey(schema, table);\n\t\tif (seen.has(key)) continue;\n\t\tseen.add(key);\n\t\tnormalized.push({ schema, table });\n\t}\n\n\treturn normalized;\n}\n\nfunction buildTablesQuery(tables: NormalizedTable[]): string {\n\tconst filter = buildFilterClause(tables, \"n.nspname\", \"c.relname\");\n\treturn `SELECT\n c.relname AS table_name,\n n.nspname AS schema_name,\n CASE c.relkind\n WHEN 'r' THEN 'table'\n WHEN 'v' THEN 'view'\n WHEN 'm' THEN 'materialized_view'\n ELSE c.relkind::text\n END AS table_type,\n obj_description(c.oid) AS comment\n FROM pg_class c\n JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')\n AND c.relkind IN ('r', 'v', 'm')\n ${filter}\n ORDER BY n.nspname, c.relname;`;\n}\n\nfunction buildColumnsQuery(tables: NormalizedTable[]): string {\n\tconst filter = buildFilterClause(\n\t\ttables,\n\t\t\"cols.table_schema\",\n\t\t\"cols.table_name\",\n\t);\n\treturn `SELECT\n cols.table_name,\n cols.table_schema,\n cols.column_name,\n cols.data_type,\n cols.udt_name,\n pgd.description,\n EXISTS(\n SELECT 1\n FROM information_schema.table_constraints tc\n JOIN information_schema.key_column_usage kcu\n ON tc.constraint_name = kcu.constraint_name\n AND tc.table_schema = kcu.table_schema\n WHERE tc.constraint_type = 'PRIMARY KEY'\n AND tc.table_schema = cols.table_schema\n AND tc.table_name = cols.table_name\n AND kcu.column_name = cols.column_name\n ) AS is_primary_key\n FROM information_schema.columns cols\n LEFT JOIN pg_catalog.pg_namespace n ON n.nspname = cols.table_schema\n LEFT JOIN pg_catalog.pg_class c\n ON c.relname = cols.table_name\n AND c.relnamespace = n.oid\n AND c.relkind IN ('r', 'v', 'm')\n LEFT JOIN pg_catalog.pg_attribute attr\n ON attr.attrelid = c.oid\n AND attr.attname = cols.column_name\n LEFT JOIN pg_catalog.pg_description pgd\n ON pgd.objoid = attr.attrelid AND pgd.objsubid = attr.attnum\n WHERE cols.table_schema NOT IN ('pg_catalog', 'information_schema')\n ${filter}\n ORDER BY cols.table_schema, cols.table_name, cols.ordinal_position;`;\n}\n\n// Redshift's information_schema constraint views are very slow on large\n// clusters, so read the svv_* catalog views instead. Primary keys are\n// informational-only in Redshift and are not reported.\nconst REDSHIFT_EXCLUDED_SCHEMAS =\n\t\"('pg_catalog', 'information_schema', 'pg_internal', 'pg_automv')\";\n\nfunction buildRedshiftTablesQuery(tables: NormalizedTable[]): string {\n\tconst filter = buildFilterClause(tables, \"table_schema\", \"table_name\");\n\treturn `SELECT\n table_name,\n table_schema AS schema_name,\n table_type,\n remarks AS comment\n FROM svv_tables\n WHERE table_catalog = current_database()\n AND table_schema NOT IN ${REDSHIFT_EXCLUDED_SCHEMAS}\n AND table_schema NOT LIKE 'pg_temp%'\n ${filter}\n ORDER BY table_schema, table_name;`;\n}\n\nfunction buildRedshiftColumnsQuery(tables: NormalizedTable[]): string {\n\tconst filter = buildFilterClause(tables, \"table_schema\", \"table_name\");\n\treturn `SELECT\n table_name,\n table_schema,\n column_name,\n data_type,\n NULL AS udt_name,\n remarks AS description,\n FALSE AS is_primary_key\n FROM svv_columns\n WHERE table_catalog = current_database()\n AND table_schema NOT IN ${REDSHIFT_EXCLUDED_SCHEMAS}\n AND table_schema NOT LIKE 'pg_temp%'\n ${filter}\n ORDER BY table_schema, table_name, ordinal_position;`;\n}\n\n/**\n * WHERE fragment restricting introspection to the normalized allow-list.\n * Identifiers are validated by normalizeTableFilter (isSafeIdentifier), so\n * interpolating them is safe. `schema.*` entries match the whole schema.\n */\nexport function buildFilterClause(\n\ttables: NormalizedTable[],\n\tschemaExpr: string,\n\ttableExpr: string,\n): string {\n\tif (!tables.length) return \"\";\n\tconst wildcardSchemas = new Set(\n\t\ttables.filter((t) => t.table === WILDCARD).map((t) => t.schema),\n\t);\n\tconst clauses: string[] = [];\n\tfor (const schema of wildcardSchemas) {\n\t\tclauses.push(`(${schemaExpr} = '${schema}')`);\n\t}\n\tfor (const { schema, table } of tables) {\n\t\tif (table === WILDCARD || wildcardSchemas.has(schema)) continue;\n\t\tclauses.push(`(${schemaExpr} = '${schema}' AND ${tableExpr} = '${table}')`);\n\t}\n\treturn `AND (${clauses.join(\" OR \")})`;\n}\n\nfunction buildListSchemasQuery(): string {\n\treturn `SELECT n.nspname AS schema, COUNT(*) AS table_count\n FROM pg_class c\n JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE c.relkind IN ('r', 'v', 'm')\n AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')\n AND n.nspname NOT LIKE 'pg_temp%'\n AND n.nspname NOT LIKE 'pg_toast_temp%'\n GROUP BY n.nspname\n ORDER BY n.nspname;`;\n}\n\nfunction buildRedshiftListSchemasQuery(): string {\n\treturn `SELECT table_schema AS schema, COUNT(*) AS table_count\n FROM svv_tables\n WHERE table_catalog = current_database()\n AND table_schema NOT IN ${REDSHIFT_EXCLUDED_SCHEMAS}\n AND table_schema NOT LIKE 'pg_temp%'\n GROUP BY table_schema\n ORDER BY table_schema;`;\n}\n\n/** Escape LIKE/ILIKE metacharacters so search is a literal substring match. */\nexport function toLikeSubstringPattern(search: string): string {\n\treturn `%${search.replace(/[\\\\%_]/g, (ch) => `\\\\${ch}`)}%`;\n}\n\nexport function buildListTablesQuery(\n\tdialect: PostgresWireDialect,\n\toptions: ListTablesOptions,\n): { sql: string; params: unknown[] } {\n\tconst isRedshift = dialect === \"redshift\";\n\tconst schemaExpr = isRedshift ? \"table_schema\" : \"n.nspname\";\n\tconst tableExpr = isRedshift ? \"table_name\" : \"c.relname\";\n\tconst params: unknown[] = [];\n\tconst filters: string[] = [];\n\tif (options.schema) {\n\t\tparams.push(options.schema);\n\t\tfilters.push(`AND ${schemaExpr} = $${params.length}`);\n\t}\n\tconst search = options.search?.trim();\n\tif (search) {\n\t\tparams.push(toLikeSubstringPattern(search));\n\t\tfilters.push(`AND ${tableExpr} ILIKE $${params.length}`);\n\t}\n\t// Integer produced by resolveCatalogLimit, never raw input.\n\tconst limit = resolveCatalogLimit(options.limit);\n\tconst filterSql = filters.join(\"\\n \");\n\n\tconst sql = isRedshift\n\t\t? `SELECT table_schema AS schema, table_name AS name, table_type\n FROM svv_tables\n WHERE table_catalog = current_database()\n AND table_schema NOT IN ${REDSHIFT_EXCLUDED_SCHEMAS}\n AND table_schema NOT LIKE 'pg_temp%'\n ${filterSql}\n ORDER BY table_schema, table_name\n LIMIT ${limit};`\n\t\t: `SELECT n.nspname AS schema, c.relname AS name,\n CASE c.relkind\n WHEN 'v' THEN 'view'\n WHEN 'm' THEN 'materialized_view'\n ELSE 'table'\n END AS table_type\n FROM pg_class c\n JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE c.relkind IN ('r', 'v', 'm')\n AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')\n AND n.nspname NOT LIKE 'pg_temp%'\n AND n.nspname NOT LIKE 'pg_toast_temp%'\n ${filterSql}\n ORDER BY n.nspname, c.relname\n LIMIT ${limit};`;\n\n\treturn { sql, params };\n}\n\nfunction tableKey(schema: string, table: string): string {\n\treturn `${schema}.${table}`;\n}\n\nfunction isSafeIdentifier(value: string): boolean {\n\treturn /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);\n}\n\nfunction asTableType(value: string): CatalogTable[\"type\"] {\n\tconst normalized = value.toLowerCase();\n\tif (normalized.includes(\"view\")) {\n\t\treturn normalized.includes(\"materialized\") ? \"materialized_view\" : \"view\";\n\t}\n\treturn \"table\";\n}\n\nfunction sanitize(value: unknown): string | undefined {\n\tif (value === null || value === undefined) return undefined;\n\tconst trimmed = String(value).trim();\n\treturn trimmed.length ? trimmed : undefined;\n}\n","/**\n * Deep module: Hides JWT signing and HTTP complexity behind simple interface\n * Following Ousterhout's principle: \"Pull complexity downward\"\n */\n\nimport crypto from 'node:crypto';\nimport { QueryPipelineError, isQueryErrorCode } from '../errors';\n\n// Web Crypto API type declarations (available in Node.js 18+, Deno, and Bun)\n// Minimal type declaration for server-side use without DOM types\n// This matches the Web Crypto API CryptoKey interface\ninterface CryptoKey {\n\treadonly type: \"public\" | \"private\" | \"secret\";\n\treadonly extractable: boolean;\n\treadonly algorithm: { name: string };\n\treadonly usages: Array<\n\t\t| \"encrypt\"\n\t\t| \"decrypt\"\n\t\t| \"sign\"\n\t\t| \"verify\"\n\t\t| \"deriveKey\"\n\t\t| \"deriveBits\"\n\t\t| \"wrapKey\"\n\t\t| \"unwrapKey\"\n\t>;\n}\n\ntype FetchLike = (\n\tinput: Parameters<typeof fetch>[0],\n\tinit?: Parameters<typeof fetch>[1],\n) => ReturnType<typeof fetch>;\n\nexport class ApiClient {\n\tprivate readonly baseUrl: string;\n\tprivate readonly privateKey: string;\n\tprivate readonly organizationId: string;\n\tprivate readonly defaultTenantId?: string;\n\tprivate readonly additionalHeaders?: Record<string, string>;\n\tprivate readonly fetchImpl: FetchLike;\n\tprivate cryptoKey: CryptoKey | null = null;\n\n\tconstructor(\n\t\tbaseUrl: string,\n\t\tprivateKey: string,\n\t\torganizationId: string,\n\t\toptions?: {\n\t\t\tdefaultTenantId?: string;\n\t\t\tadditionalHeaders?: Record<string, string>;\n\t\t\tfetch?: FetchLike;\n\t\t},\n\t) {\n\t\tif (!baseUrl) {\n\t\t\tthrow new Error(\"Base URL is required\");\n\t\t}\n\t\tif (!privateKey) {\n\t\t\tthrow new Error(\"Private key is required\");\n\t\t}\n\t\tif (!organizationId) {\n\t\t\tthrow new Error(\"Organization ID is required\");\n\t\t}\n\n\t\tthis.baseUrl = baseUrl.replace(/\\/+$/, \"\");\n\t\tthis.privateKey = privateKey;\n\t\tthis.organizationId = organizationId;\n\t\tthis.defaultTenantId = options?.defaultTenantId;\n\t\tthis.additionalHeaders = options?.additionalHeaders;\n\t\tthis.fetchImpl = options?.fetch ?? globalThis.fetch;\n\n\t\tif (!this.fetchImpl) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Fetch implementation not found. Provide options.fetch or use Node 18+.\",\n\t\t\t);\n\t\t}\n\t}\n\n\tgetDefaultTenantId(): string | undefined {\n\t\treturn this.defaultTenantId;\n\t}\n\n\t/**\n\t * Create a JWT for the given tenant (and optional userId, scopes).\n\t * Use this when you need to pass a token to the embed (e.g. frontend or demo).\n\t */\n\tasync createJwt(\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t): Promise<string> {\n\t\treturn await this.generateJWT(tenantId, userId, scopes);\n\t}\n\n\tasync get<T>(\n\t\tpath: string,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\treturn await this.request<T>(path, {\n\t\t\tmethod: \"GET\",\n\t\t\theaders: await this.buildHeaders(\n\t\t\t\ttenantId,\n\t\t\t\tuserId,\n\t\t\t\tscopes,\n\t\t\t\tfalse,\n\t\t\t\tsessionId,\n\t\t\t),\n\t\t\tsignal,\n\t\t});\n\t}\n\n\tasync post<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\treturn await this.request<T>(path, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: await this.buildHeaders(\n\t\t\t\ttenantId,\n\t\t\t\tuserId,\n\t\t\t\tscopes,\n\t\t\t\ttrue,\n\t\t\t\tsessionId,\n\t\t\t),\n\t\t\tbody: JSON.stringify(body ?? {}),\n\t\t\tsignal,\n\t\t});\n\t}\n\n\tasync postWithHeaders<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<{ data: T; headers: Headers }> {\n\t\tconst response = await this.fetchImpl(`${this.baseUrl}${path}`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: await this.buildHeaders(\n\t\t\t\ttenantId,\n\t\t\t\tuserId,\n\t\t\t\tscopes,\n\t\t\t\ttrue,\n\t\t\t\tsessionId,\n\t\t\t),\n\t\t\tbody: JSON.stringify(body ?? {}),\n\t\t\tsignal,\n\t\t});\n\t\tconst data = await this.parseResponse<T>(response);\n\t\treturn { data, headers: response.headers };\n\t}\n\n\tasync put<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\treturn await this.request<T>(path, {\n\t\t\tmethod: \"PUT\",\n\t\t\theaders: await this.buildHeaders(\n\t\t\t\ttenantId,\n\t\t\t\tuserId,\n\t\t\t\tscopes,\n\t\t\t\ttrue,\n\t\t\t\tsessionId,\n\t\t\t),\n\t\t\tbody: JSON.stringify(body ?? {}),\n\t\t\tsignal,\n\t\t});\n\t}\n\n\tasync patch<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\treturn await this.request<T>(path, {\n\t\t\tmethod: \"PATCH\",\n\t\t\theaders: await this.buildHeaders(\n\t\t\t\ttenantId,\n\t\t\t\tuserId,\n\t\t\t\tscopes,\n\t\t\t\ttrue,\n\t\t\t\tsessionId,\n\t\t\t),\n\t\t\tbody: JSON.stringify(body ?? {}),\n\t\t\tsignal,\n\t\t});\n\t}\n\n\tasync delete<T = void>(\n\t\tpath: string,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\treturn await this.request<T>(path, {\n\t\t\tmethod: \"DELETE\",\n\t\t\theaders: await this.buildHeaders(\n\t\t\t\ttenantId,\n\t\t\t\tuserId,\n\t\t\t\tscopes,\n\t\t\t\tfalse,\n\t\t\t\tsessionId,\n\t\t\t),\n\t\t\tsignal,\n\t\t});\n\t}\n\n\tprivate async request<T>(path: string, init: RequestInit): Promise<T> {\n\t\tconst response = await this.fetchImpl(`${this.baseUrl}${path}`, init);\n\t\treturn await this.parseResponse<T>(response);\n\t}\n\n\tprivate async parseResponse<T>(response: Response): Promise<T> {\n\t\tconst text = await response.text();\n\t\tlet json: any;\n\t\ttry {\n\t\t\tjson = text ? JSON.parse(text) : undefined;\n\t\t} catch {\n\t\t\tjson = undefined;\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tconst message = errorMessageFromBody(json, response);\n\t\t\tif (isQueryErrorCode(json?.code)) {\n\t\t\t\tthrow new QueryPipelineError(\n\t\t\t\t\tmessage,\n\t\t\t\t\tjson.code,\n\t\t\t\t\tjson?.details,\n\t\t\t\t\tresponse.status,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst error = new Error(message);\n\t\t\t(error as any).status = response.status;\n\t\t\tif (json?.code) (error as any).code = json.code;\n\t\t\tif (json?.details) (error as any).details = json.details;\n\t\t\tthrow error;\n\t\t}\n\n\t\treturn json as T;\n\t}\n\n\tprivate async buildHeaders(\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tincludeJson: boolean = true,\n\t\tsessionId?: string,\n\t): Promise<Record<string, string>> {\n\t\tconst token = await this.generateJWT(tenantId, userId, scopes);\n\t\tconst headers: Record<string, string> = {\n\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\tAccept: \"application/json\",\n\t\t};\n\t\tif (includeJson) {\n\t\t\theaders[\"Content-Type\"] = \"application/json\";\n\t\t}\n\t\tif (sessionId) {\n\t\t\theaders[\"x-session-id\"] = sessionId;\n\t\t}\n\t\tif (this.additionalHeaders) {\n\t\t\tObject.assign(headers, this.additionalHeaders);\n\t\t}\n\t\treturn headers;\n\t}\n\n\t/**\n\t * Base64URL encode a string (works in both Node.js 18+ and Deno)\n\t */\n\tprivate base64UrlEncode(str: string): string {\n\t\t// Convert string to bytes\n\t\tconst bytes = new TextEncoder().encode(str);\n\n\t\t// btoa is available in both Node.js 18+ and Deno\n\t\t// Convert bytes to binary string efficiently (handle large arrays)\n\t\tlet binary = \"\";\n\t\tconst chunkSize = 8192; // Process in chunks to avoid stack overflow\n\t\tfor (let i = 0; i < bytes.length; i += chunkSize) {\n\t\t\tconst chunk = bytes.slice(i, i + chunkSize);\n\t\t\tbinary += String.fromCharCode(...chunk);\n\t\t}\n\n\t\tconst base64 = btoa(binary);\n\n\t\t// Convert to base64url: replace non-url chars and strip padding\n\t\treturn base64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/g, \"\");\n\t}\n\n\t/**\n\t * Base64URL encode from Uint8Array (for binary data like signatures)\n\t */\n\tprivate base64UrlEncodeBytes(bytes: Uint8Array): string {\n\t\t// Convert bytes to binary string efficiently (handle large arrays)\n\t\tlet binary = \"\";\n\t\tconst chunkSize = 8192; // Process in chunks to avoid stack overflow\n\t\tfor (let i = 0; i < bytes.length; i += chunkSize) {\n\t\t\tconst chunk = bytes.slice(i, i + chunkSize);\n\t\t\tbinary += String.fromCharCode(...chunk);\n\t\t}\n\n\t\tconst base64 = btoa(binary);\n\n\t\t// Convert to base64url: replace non-url chars and strip padding\n\t\treturn base64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/g, \"\");\n\t}\n\n\t/**\n\t * Import the private key into Web Crypto API format (cached after first import)\n\t */\n\tprivate async getCryptoKey(): Promise<CryptoKey> {\n\t\tif (this.cryptoKey) {\n\t\t\treturn this.cryptoKey;\n\t\t}\n\n\t\t// Import the private key for Web Crypto API\n\t\t// Works in both Node.js 18+ and Deno\n\t\tthis.cryptoKey = await crypto.subtle.importKey(\n\t\t\t\"pkcs8\",\n\t\t\tthis.privateKeyToArrayBuffer(this.privateKey),\n\t\t\t{\n\t\t\t\tname: \"RSASSA-PKCS1-v1_5\",\n\t\t\t\thash: \"SHA-256\",\n\t\t\t},\n\t\t\tfalse,\n\t\t\t[\"sign\"],\n\t\t);\n\n\t\treturn this.cryptoKey;\n\t}\n\n\t/**\n\t * Convert PEM private key to ArrayBuffer for Web Crypto API\n\t */\n\tprivate privateKeyToArrayBuffer(pem: string): ArrayBuffer {\n\t\t// Remove PEM headers and whitespace\n\t\tconst pemHeader = \"-----BEGIN PRIVATE KEY-----\";\n\t\tconst pemFooter = \"-----END PRIVATE KEY-----\";\n\t\tconst pemContents = pem\n\t\t\t.replace(pemHeader, \"\")\n\t\t\t.replace(pemFooter, \"\")\n\t\t\t.replace(/\\s/g, \"\");\n\n\t\t// Decode base64 to binary string, then to ArrayBuffer\n\t\tconst binaryString = atob(pemContents);\n\t\tconst bytes = new Uint8Array(binaryString.length);\n\t\tfor (let i = 0; i < binaryString.length; i++) {\n\t\t\tbytes[i] = binaryString.charCodeAt(i);\n\t\t}\n\t\treturn bytes.buffer;\n\t}\n\n\tprivate async generateJWT(\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t): Promise<string> {\n\t\tconst header = {\n\t\t\talg: \"RS256\",\n\t\t\ttyp: \"JWT\",\n\t\t};\n\n\t\tconst payload: Record<string, unknown> = {\n\t\t\torganizationId: this.organizationId,\n\t\t\ttenantId,\n\t\t};\n\n\t\tif (userId) payload.userId = userId;\n\t\tif (scopes?.length) payload.scopes = scopes;\n\n\t\tconst encodedHeader = this.base64UrlEncode(JSON.stringify(header));\n\t\tconst encodedPayload = this.base64UrlEncode(JSON.stringify(payload));\n\t\tconst data = `${encodedHeader}.${encodedPayload}`;\n\n\t\t// Sign using Web Crypto API (works in both Node.js 18+ and Deno)\n\t\tconst key = await this.getCryptoKey();\n\t\tconst dataBytes = new TextEncoder().encode(data);\n\t\tconst signature = await crypto.subtle.sign(\n\t\t\t{\n\t\t\t\tname: \"RSASSA-PKCS1-v1_5\",\n\t\t\t},\n\t\t\tkey,\n\t\t\tdataBytes,\n\t\t);\n\n\t\t// Convert signature ArrayBuffer to base64url\n\t\tconst signatureBytes = new Uint8Array(signature);\n\t\tconst encodedSignature = this.base64UrlEncodeBytes(signatureBytes);\n\n\t\treturn `${data}.${encodedSignature}`;\n\t}\n}\n\n/**\n * QueryPanel API errors are `{ error: string }`; platform errors (e.g. Vercel's 413 for\n * oversized bodies) are `{ error: { code, message } }`. Never let an object become the message.\n */\nfunction errorMessageFromBody(json: any, response: Response): string {\n\tconst error = json?.error;\n\tif (typeof error === \"string\" && error) return error;\n\tif (error && typeof error === \"object\") {\n\t\tconst nested = error.message ?? error.code;\n\t\tif (typeof nested === \"string\" && nested) {\n\t\t\treturn `${nested} (HTTP ${response.status})`;\n\t\t}\n\t}\n\treturn response.statusText || `Request failed (HTTP ${response.status})`;\n}\n","/**\n * Error codes for the query pipeline\n * These match the server-side error codes returned in API responses\n */\nexport const QueryErrorCode = {\n\t// Moderation errors\n\tMODERATION_FAILED: \"MODERATION_FAILED\",\n\n\t// Guardrail errors\n\tRELEVANCE_CHECK_FAILED: \"RELEVANCE_CHECK_FAILED\",\n\tSECURITY_CHECK_FAILED: \"SECURITY_CHECK_FAILED\",\n\n\t// SQL generation errors\n\tSQL_GENERATION_FAILED: \"SQL_GENERATION_FAILED\",\n\n\t// SQL validation errors\n\tSQL_VALIDATION_FAILED: \"SQL_VALIDATION_FAILED\",\n\n\t// Context retrieval errors\n\tCONTEXT_RETRIEVAL_FAILED: \"CONTEXT_RETRIEVAL_FAILED\",\n\n\t// Clarification errors (v2)\n\tCLARIFICATION_NEEDED: \"CLARIFICATION_NEEDED\",\n\n\t// Bring-your-own-key / provider errors\n\tAI_PROVIDER_AUTH_FAILED: \"AI_PROVIDER_AUTH_FAILED\",\n\tAI_PROVIDER_RATE_LIMITED: \"AI_PROVIDER_RATE_LIMITED\",\n\tAI_PROVIDER_UNAVAILABLE: \"AI_PROVIDER_UNAVAILABLE\",\n\n\t// General errors\n\tINTERNAL_ERROR: \"INTERNAL_ERROR\",\n\tAUTHENTICATION_REQUIRED: \"AUTHENTICATION_REQUIRED\",\n\tVALIDATION_ERROR: \"VALIDATION_ERROR\",\n} as const;\n\nexport type QueryErrorCode =\n\t(typeof QueryErrorCode)[keyof typeof QueryErrorCode];\n\nexport function isQueryErrorCode(code: unknown): code is QueryErrorCode {\n\treturn (\n\t\ttypeof code === \"string\" &&\n\t\t(Object.values(QueryErrorCode) as string[]).includes(code)\n\t);\n}\n\nexport function isAiProviderErrorCode(code: unknown): boolean {\n\treturn (\n\t\tcode === QueryErrorCode.AI_PROVIDER_AUTH_FAILED ||\n\t\tcode === QueryErrorCode.AI_PROVIDER_RATE_LIMITED ||\n\t\tcode === QueryErrorCode.AI_PROVIDER_UNAVAILABLE\n\t);\n}\n\nexport function isAiProviderPipelineError(error: unknown): boolean {\n\tif (error instanceof QueryPipelineError) {\n\t\treturn isAiProviderErrorCode(error.code);\n\t}\n\tif (error && typeof error === \"object\" && \"code\" in error) {\n\t\treturn isAiProviderErrorCode((error as { code: unknown }).code);\n\t}\n\treturn false;\n}\n\nexport function throwIfGenerationErrorResponse(response: unknown): void {\n\tif (!response || typeof response !== \"object\") {\n\t\treturn;\n\t}\n\tconst record = response as Record<string, unknown>;\n\tif (typeof record.error !== \"string\") {\n\t\treturn;\n\t}\n\tif (isQueryErrorCode(record.code)) {\n\t\tthrow new QueryPipelineError(record.error, record.code);\n\t}\n\tthrow new Error(record.error);\n}\n\n/**\n * Error thrown when the query pipeline fails\n */\nexport class QueryPipelineError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly code: QueryErrorCode,\n\t\tpublic readonly details?: Record<string, unknown>,\n\t\tpublic readonly status?: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"QueryPipelineError\";\n\t}\n\n\t/**\n\t * Check if this is a moderation error\n\t */\n\tisModeration(): boolean {\n\t\treturn this.code === QueryErrorCode.MODERATION_FAILED;\n\t}\n\n\t/**\n\t * Check if this is a relevance error (question not related to database)\n\t */\n\tisRelevanceError(): boolean {\n\t\treturn this.code === QueryErrorCode.RELEVANCE_CHECK_FAILED;\n\t}\n\n\t/**\n\t * Check if this is a security error (SQL injection, prompt injection, etc.)\n\t */\n\tisSecurityError(): boolean {\n\t\treturn this.code === QueryErrorCode.SECURITY_CHECK_FAILED;\n\t}\n\n\t/**\n\t * Check if this is any guardrail error (relevance or security)\n\t */\n\tisGuardrailError(): boolean {\n\t\treturn this.isRelevanceError() || this.isSecurityError();\n\t}\n\n\t/**\n\t * Check if this is a clarification needed error (v2 pipeline)\n\t */\n\tisClarificationNeeded(): boolean {\n\t\treturn this.code === QueryErrorCode.CLARIFICATION_NEEDED;\n\t}\n\n\t/**\n\t * Check if this is a BYOK / provider credential error\n\t */\n\tisAiProviderAuthError(): boolean {\n\t\treturn this.code === QueryErrorCode.AI_PROVIDER_AUTH_FAILED;\n\t}\n\n\t/**\n\t * Check if this is a BYOK / provider rate-limit error\n\t */\n\tisAiProviderRateLimited(): boolean {\n\t\treturn this.code === QueryErrorCode.AI_PROVIDER_RATE_LIMITED;\n\t}\n\n\t/**\n\t * Check if this is any BYOK / provider error (auth, rate limit, or unavailable)\n\t */\n\tisAiProviderError(): boolean {\n\t\treturn isAiProviderErrorCode(this.code);\n\t}\n}\n\n/**\n * Thrown by stageSchemaIngest / syncSchemaInBatches when the introspected schema\n * has more columns than `maxColumns`. Raised before anything is uploaded; narrow\n * the sync with `allowedTables` / `tables` (e.g. `schema.*`) and retry.\n */\nexport class SchemaTooLargeError extends Error {\n\tconstructor(\n\t\tpublic readonly tableCount: number,\n\t\tpublic readonly columnCount: number,\n\t\tpublic readonly maxColumns: number,\n\t) {\n\t\tsuper(\n\t\t\t`Schema too large to sync: ${columnCount} columns across ${tableCount} tables exceeds the limit of ${maxColumns} columns. Restrict the tables to sync (e.g. allowedTables with \"schema.*\" or specific tables).`,\n\t\t);\n\t\tthis.name = \"SchemaTooLargeError\";\n\t}\n}\n","import type {\n\tIQueryPanelApi,\n\tRequestHandler,\n\tRequestHandlerOptions,\n} from \"./api-types\";\n\n/**\n * API client that delegates every request to a callback instead of HTTP.\n * Use this when running the SDK inside your own API so that \"API\" calls are handled in-process (e.g. by calling querypanel-sdk services directly) and do not cause recursion.\n */\nexport class CallbackApiClient implements IQueryPanelApi {\n\tprivate readonly requestHandler: RequestHandler;\n\tprivate readonly defaultTenantId?: string;\n\n\tconstructor(\n\t\trequestHandler: RequestHandler,\n\t\toptions?: { defaultTenantId?: string },\n\t) {\n\t\tthis.requestHandler = requestHandler;\n\t\tthis.defaultTenantId = options?.defaultTenantId;\n\t}\n\n\tgetDefaultTenantId(): string | undefined {\n\t\treturn this.defaultTenantId;\n\t}\n\n\tprivate async request<T>(\n\t\topts: Omit<RequestHandlerOptions, \"method\" | \"body\"> & {\n\t\t\tmethod: RequestHandlerOptions[\"method\"];\n\t\t\tbody?: unknown;\n\t\t},\n\t): Promise<{ data: T; headers?: Headers }> {\n\t\tconst { data, headers } = await this.requestHandler({\n\t\t\tmethod: opts.method,\n\t\t\tpath: opts.path,\n\t\t\tbody: opts.body,\n\t\t\ttenantId: opts.tenantId,\n\t\t\tuserId: opts.userId,\n\t\t\tscopes: opts.scopes,\n\t\t\tsignal: opts.signal,\n\t\t\tsessionId: opts.sessionId,\n\t\t});\n\t\treturn { data: data as T, headers };\n\t}\n\n\tasync get<T>(\n\t\tpath: string,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\tconst { data } = await this.request<T>({\n\t\t\tmethod: \"GET\",\n\t\t\tpath,\n\t\t\ttenantId,\n\t\t\tuserId,\n\t\t\tscopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t});\n\t\treturn data;\n\t}\n\n\tasync post<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\tconst { data } = await this.request<T>({\n\t\t\tmethod: \"POST\",\n\t\t\tpath,\n\t\t\tbody,\n\t\t\ttenantId,\n\t\t\tuserId,\n\t\t\tscopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t});\n\t\treturn data;\n\t}\n\n\tasync postWithHeaders<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<{ data: T; headers: Headers }> {\n\t\tconst { data, headers } = await this.request<T>({\n\t\t\tmethod: \"POST\",\n\t\t\tpath,\n\t\t\tbody,\n\t\t\ttenantId,\n\t\t\tuserId,\n\t\t\tscopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t});\n\t\treturn {\n\t\t\tdata,\n\t\t\theaders: headers ?? new Headers(),\n\t\t};\n\t}\n\n\tasync put<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\tconst { data } = await this.request<T>({\n\t\t\tmethod: \"PUT\",\n\t\t\tpath,\n\t\t\tbody,\n\t\t\ttenantId,\n\t\t\tuserId,\n\t\t\tscopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t});\n\t\treturn data;\n\t}\n\n\tasync patch<T>(\n\t\tpath: string,\n\t\tbody: unknown,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\tconst { data } = await this.request<T>({\n\t\t\tmethod: \"PATCH\",\n\t\t\tpath,\n\t\t\tbody,\n\t\t\ttenantId,\n\t\t\tuserId,\n\t\t\tscopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t});\n\t\treturn data;\n\t}\n\n\tasync delete<T = void>(\n\t\tpath: string,\n\t\ttenantId: string,\n\t\tuserId?: string,\n\t\tscopes?: string[],\n\t\tsignal?: AbortSignal,\n\t\tsessionId?: string,\n\t): Promise<T> {\n\t\tconst { data } = await this.request<T>({\n\t\t\tmethod: \"DELETE\",\n\t\t\tpath,\n\t\t\ttenantId,\n\t\t\tuserId,\n\t\t\tscopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t});\n\t\treturn data;\n\t}\n}\n","import type { ParamRecord, ParamValue } from \"./param-types\";\n\n/**\n * Maps v2-style param metadata (array order = $1, $2, $3 in SQL) to a ParamRecord.\n * Numeric keys preserve placeholder order for PostgreSQL; named keys support tenant lookups.\n */\nexport function mapGeneratedParams(\n\tparams: Array<Record<string, unknown>>,\n): ParamRecord {\n\tconst record: ParamRecord = {};\n\n\tparams.forEach((param, index) => {\n\t\tconst value = param.value as ParamValue | undefined;\n\t\tif (value === undefined) {\n\t\t\treturn;\n\t\t}\n\n\t\trecord[String(index + 1)] = value;\n\n\t\tconst nameCandidate =\n\t\t\t(typeof param.name === \"string\" && param.name.trim()) ||\n\t\t\t(typeof param.placeholder === \"string\" && param.placeholder.trim()) ||\n\t\t\t(typeof param.position === \"number\" && String(param.position)) ||\n\t\t\tString(index + 1);\n\t\tconst key = nameCandidate.replace(/[{}:$]/g, \"\").trim();\n\t\trecord[key] = value;\n\t});\n\n\treturn record;\n}\n\n/**\n * Normalizes run-sql / dashboard request params into a ParamRecord.\n */\nexport function normalizeSqlParamsInput(\n\tparams: unknown[] | Record<string, unknown> | null | undefined,\n): ParamRecord {\n\tif (params == null) {\n\t\treturn {};\n\t}\n\n\tif (Array.isArray(params)) {\n\t\tif (params.length === 0) {\n\t\t\treturn {};\n\t\t}\n\n\t\tconst first = params[0];\n\t\tif (first && typeof first === \"object\" && \"value\" in (first as object)) {\n\t\t\treturn mapGeneratedParams(params as Array<Record<string, unknown>>);\n\t\t}\n\n\t\tconst positional: ParamRecord = {};\n\t\tparams.forEach((value, index) => {\n\t\t\tpositional[String(index + 1)] = value as ParamValue;\n\t\t});\n\t\treturn positional;\n\t}\n\n\tif (typeof params === \"object\") {\n\t\treturn params as ParamRecord;\n\t}\n\n\treturn {};\n}\n","import type { DatabaseAdapter, DatabaseDialect } from \"../adapters/types\";\nimport { mapGeneratedParams as mapGeneratedParamsFromMetadata } from \"./map-generated-params\";\nimport type { ParamRecord } from \"./param-types\";\n\nexport type { ParamRecord, ParamValue } from \"./param-types\";\n\nexport interface DatabaseMetadata {\n\tname: string;\n\tdialect: DatabaseDialect;\n\tdescription?: string;\n\ttags?: string[];\n\ttenantFieldName?: string;\n\ttenantFieldType?: string;\n\tenforceTenantIsolation?: boolean;\n}\n\nexport interface DatabaseExecutionResult {\n\trows: Array<Record<string, unknown>>;\n\tfields: string[];\n}\n\n/**\n * Deep module: Hides SQL execution complexity and tenant isolation logic\n * Following Ousterhout's principle: \"Information hiding\"\n */\nexport class QueryEngine {\n\tprivate databases = new Map<string, DatabaseAdapter>();\n\tprivate databaseMetadata = new Map<string, DatabaseMetadata>();\n\tprivate defaultDatabase?: string;\n\n\tattachDatabase(\n\t\tname: string,\n\t\tadapter: DatabaseAdapter,\n\t\tmetadata: DatabaseMetadata,\n\t): void {\n\t\tthis.databases.set(name, adapter);\n\t\tthis.databaseMetadata.set(name, metadata);\n\t\tif (!this.defaultDatabase) {\n\t\t\tthis.defaultDatabase = name;\n\t\t}\n\t}\n\n\tgetDatabase(name?: string): DatabaseAdapter {\n\t\tconst dbName = name ?? this.defaultDatabase;\n\t\tif (!dbName) {\n\t\t\tthrow new Error(\"No database attached.\");\n\t\t}\n\t\tconst adapter = this.databases.get(dbName);\n\t\tif (!adapter) {\n\t\t\tthrow new Error(\n\t\t\t\t`Database '${dbName}' not found. Attached: ${Array.from(\n\t\t\t\t\tthis.databases.keys(),\n\t\t\t\t).join(\", \")}`,\n\t\t\t);\n\t\t}\n\t\treturn adapter;\n\t}\n\n\tgetDatabaseMetadata(name?: string): DatabaseMetadata | undefined {\n\t\tconst dbName = name ?? this.defaultDatabase;\n\t\tif (!dbName) return undefined;\n\t\treturn this.databaseMetadata.get(dbName);\n\t}\n\n\tgetDefaultDatabase(): string | undefined {\n\t\treturn this.defaultDatabase;\n\t}\n\n\tasync validateAndExecute(\n\t\tsql: string,\n\t\tparams: ParamRecord,\n\t\tdatabaseName: string,\n\t\ttenantId: string,\n\t): Promise<DatabaseExecutionResult> {\n\t\tconst adapter = this.getDatabase(databaseName);\n\t\tconst metadata = this.getDatabaseMetadata(databaseName);\n\n\t\t// Apply tenant isolation if configured\n\t\tlet finalSql = sql;\n\t\tif (metadata) {\n\t\t\tfinalSql = this.ensureTenantIsolation(sql, params, metadata, tenantId);\n\t\t}\n\n\t\t// Validate SQL\n\t\tawait adapter.validate(finalSql, params);\n\n\t\t// Execute\n\t\tconst result = await adapter.execute(finalSql, params);\n\t\treturn {\n\t\t\trows: result.rows,\n\t\t\tfields: result.fields,\n\t\t};\n\t}\n\n\tasync execute(\n\t\tsql: string,\n\t\tparams: ParamRecord | undefined,\n\t\tdatabaseName?: string,\n\t): Promise<Array<Record<string, unknown>>> {\n\t\ttry {\n\t\t\tconst adapter = this.getDatabase(databaseName);\n\t\t\tconst result = await adapter.execute(sql, params);\n\t\t\treturn result.rows;\n\t\t} catch (error) {\n\t\t\tconsole.warn(\n\t\t\t\t`Failed to execute SQL locally for database '${databaseName}':`,\n\t\t\t\terror,\n\t\t\t);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Maps API params (array in SQL placeholder order $1, $2, $3) to ParamRecord.\n\t * Sets both numeric keys (\"1\", \"2\", \"3\") and named keys so that:\n\t * - Postgres adapter uses numeric order for positional params (matches SQL).\n\t * - Named keys remain for tenant isolation / other lookups.\n\t */\n\tmapGeneratedParams(params: Array<Record<string, unknown>>): ParamRecord {\n\t\treturn mapGeneratedParamsFromMetadata(params);\n\t}\n\n\tprivate ensureTenantIsolation(\n\t\tsql: string,\n\t\tparams: ParamRecord,\n\t\tmetadata: DatabaseMetadata,\n\t\ttenantId: string,\n\t): string {\n\t\tif (\n\t\t\t!metadata.tenantFieldName ||\n\t\t\tmetadata.enforceTenantIsolation === false\n\t\t) {\n\t\t\treturn sql;\n\t\t}\n\n\t\tconst tenantField = metadata.tenantFieldName;\n\t\tconst normalizedSql = sql.toLowerCase();\n\t\tif (normalizedSql.includes(tenantField.toLowerCase())) {\n\t\t\treturn sql;\n\t\t}\n\n\t\tlet tenantPredicate: string;\n\n\t\tif (metadata.dialect === \"clickhouse\") {\n\t\t\t// ClickHouse supports named parameters natively\n\t\t\tconst paramKey = tenantField;\n\t\t\tparams[paramKey] = tenantId;\n\t\t\ttenantPredicate = `${tenantField} = {${tenantField}:${metadata.tenantFieldType ?? \"String\"}}`;\n\t\t} else if (metadata.dialect === \"bigquery\") {\n\t\t\t// BigQuery uses @paramName\n\t\t\tparams[tenantField] = tenantId;\n\t\t\ttenantPredicate = `${tenantField} = @${tenantField}`;\n\t\t} else {\n\t\t\tconst paramIndex = this.nextPostgresPlaceholderIndex(sql, params);\n\t\t\tparams[String(paramIndex)] = tenantId;\n\t\t\tparams[tenantField] = tenantId;\n\t\t\ttenantPredicate = `${tenantField} = $${paramIndex}`;\n\t\t}\n\n\t\tif (/\\bwhere\\b/i.test(sql)) {\n\t\t\treturn sql.replace(\n\t\t\t\t/\\bwhere\\b/i,\n\t\t\t\t(match) => `${match} ${tenantPredicate} AND `,\n\t\t\t);\n\t\t}\n\n\t\treturn `${sql} WHERE ${tenantPredicate}`;\n\t}\n\n\tprivate nextPostgresPlaceholderIndex(\n\t\tsql: string,\n\t\tparams: ParamRecord,\n\t): number {\n\t\tlet max = 0;\n\t\tfor (const match of sql.matchAll(/\\$(\\d+)\\b/g)) {\n\t\t\tconst index = Number.parseInt(match[1], 10);\n\t\t\tif (Number.isFinite(index) && index > max) {\n\t\t\t\tmax = index;\n\t\t\t}\n\t\t}\n\n\t\tif (max > 0) {\n\t\t\treturn max + 1;\n\t\t}\n\n\t\tconst numericKeys = Object.keys(params)\n\t\t\t.filter((key) => /^\\d+$/.test(key))\n\t\t\t.map((key) => Number.parseInt(key, 10))\n\t\t\t.filter((index) => Number.isFinite(index));\n\n\t\tif (numericKeys.length > 0) {\n\t\t\treturn Math.max(...numericKeys) + 1;\n\t\t}\n\n\t\treturn 1;\n\t}\n}\n","import type { IQueryPanelApi } from \"../core/api-types\";\nimport type { ParamRecord, QueryEngine } from \"../core/query-engine\";\n\nexport interface SdkChart {\n\tid: string;\n\ttitle: string;\n\tprompt?: string | null;\n\tdescription: string | null;\n\tsql: string;\n\tsql_params: Record<string, unknown> | null;\n\tvega_lite_spec: Record<string, unknown>;\n\tspec_type?: 'vega-lite' | 'vizspec'; // Type discriminator for spec format\n\tquery_id: string | null;\n\torganization_id: string | null;\n\ttenant_id: string | null;\n\tuser_id: string | null;\n\tcreated_at: string | null;\n\tupdated_at: string | null;\n\tactive?: boolean;\n\ttarget_db?: string | null;\n}\n\nexport interface ChartCreateInput {\n\ttitle: string;\n\tprompt?: string;\n\tdescription?: string;\n\t/**\n\t * Required when `query_id` is not provided. Ignored (with `sql_params`) when\n\t * `query_id` is present: the server saves the SQL and params it stored for that query.\n\t */\n\tsql?: string;\n\tsql_params?: Record<string, unknown>;\n\tvega_lite_spec: Record<string, unknown>;\n\tspec_type?: 'vega-lite' | 'vizspec'; // Defaults to 'vega-lite' if not specified\n\t/** Server-issued query id from ask() / modifyChart(). Preferred over `sql`. */\n\tquery_id?: string;\n\ttarget_db?: string;\n}\n\nexport interface ChartUpdateInput {\n\ttitle?: string;\n\tprompt?: string;\n\tdescription?: string;\n\tsql?: string;\n\tsql_params?: Record<string, unknown>;\n\t/** When present, the server replaces sql / sql_params with the stored query. */\n\tquery_id?: string;\n\tvega_lite_spec?: Record<string, unknown>;\n\tspec_type?: 'vega-lite' | 'vizspec';\n\ttarget_db?: string;\n}\n\nexport interface PaginationQuery {\n\tpage?: number;\n\tlimit?: number;\n}\n\nexport interface PaginationInfo {\n\tpage: number;\n\tlimit: number;\n\ttotal: number;\n\ttotalPages: number;\n\thasNext: boolean;\n\thasPrev: boolean;\n}\n\nexport interface PaginatedResponse<T> {\n\tdata: T[];\n\tpagination: PaginationInfo;\n}\n\nexport interface ChartListOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n\tpagination?: PaginationQuery;\n\tsortBy?: \"title\" | \"user_id\" | \"created_at\" | \"updated_at\";\n\tsortDir?: \"asc\" | \"desc\";\n\ttitle?: string;\n\tuserFilter?: string;\n\tcreatedFrom?: string;\n\tcreatedTo?: string;\n\tupdatedFrom?: string;\n\tupdatedTo?: string;\n\tincludeData?: boolean;\n}\n\n/** Options for bulk chart fetch (`GET /charts/bulk`) — no pagination. */\nexport type ChartListAllOptions = Omit<ChartListOptions, \"pagination\">;\n\nexport interface ChartBulkGetResponse {\n\tdata: SdkChart[];\n\tmissingIds: string[];\n}\n\ninterface RequestOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n}\n\n/**\n * Route module for Chart CRUD operations\n * Simple pass-through to backend with optional data hydration\n */\nexport async function createChart(\n\tclient: IQueryPanelApi,\n\tbody: ChartCreateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkChart> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.post<SdkChart>(\n\t\t\"/charts\",\n\t\tbody,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\nexport async function listCharts(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\toptions?: ChartListOptions,\n\tsignal?: AbortSignal,\n): Promise<PaginatedResponse<SdkChart>> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\tif (options?.pagination?.page)\n\t\tparams.set(\"page\", `${options.pagination.page}`);\n\tif (options?.pagination?.limit)\n\t\tparams.set(\"limit\", `${options.pagination.limit}`);\n\tif (options?.sortBy) params.set(\"sort_by\", options.sortBy);\n\tif (options?.sortDir) params.set(\"sort_dir\", options.sortDir);\n\tif (options?.title) params.set(\"title\", options.title);\n\tif (options?.userFilter) params.set(\"user_id\", options.userFilter);\n\tif (options?.createdFrom) params.set(\"created_from\", options.createdFrom);\n\tif (options?.createdTo) params.set(\"created_to\", options.createdTo);\n\tif (options?.updatedFrom) params.set(\"updated_from\", options.updatedFrom);\n\tif (options?.updatedTo) params.set(\"updated_to\", options.updatedTo);\n\n\tconst response = await client.get<PaginatedResponse<SdkChart>>(\n\t\t`/charts${params.toString() ? `?${params.toString()}` : \"\"}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tif (options?.includeData) {\n\t\tresponse.data = await Promise.all(\n\t\t\tresponse.data.map(async (chart) => {\n\t\t\t\tconst rows = await executeChartQuery(queryEngine, chart, tenantId);\n\t\t\t\treturn hydrateChartWithData(chart, rows);\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn response;\n}\n\nexport async function listAllCharts(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\toptions?: ChartListOptions,\n\tsignal?: AbortSignal,\n): Promise<PaginatedResponse<SdkChart>> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\tif (options?.pagination?.page)\n\t\tparams.set(\"page\", `${options.pagination.page}`);\n\tif (options?.pagination?.limit)\n\t\tparams.set(\"limit\", `${options.pagination.limit}`);\n\tif (options?.sortBy) params.set(\"sort_by\", options.sortBy);\n\tif (options?.sortDir) params.set(\"sort_dir\", options.sortDir);\n\tif (options?.title) params.set(\"title\", options.title);\n\tif (options?.userFilter) params.set(\"user_id\", options.userFilter);\n\tif (options?.createdFrom) params.set(\"created_from\", options.createdFrom);\n\tif (options?.createdTo) params.set(\"created_to\", options.createdTo);\n\tif (options?.updatedFrom) params.set(\"updated_from\", options.updatedFrom);\n\tif (options?.updatedTo) params.set(\"updated_to\", options.updatedTo);\n\n\tconst response = await client.get<PaginatedResponse<SdkChart>>(\n\t\t`/charts/all${params.toString() ? `?${params.toString()}` : \"\"}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tif (options?.includeData) {\n\t\tresponse.data = await Promise.all(\n\t\t\tresponse.data.map(async (chart) => {\n\t\t\t\tconst rows = await executeChartQuery(queryEngine, chart, tenantId);\n\t\t\t\treturn hydrateChartWithData(chart, rows);\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn response;\n}\n\nexport async function getChartsByIds(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tids: string[],\n\toptions?: ChartListAllOptions,\n\tsignal?: AbortSignal,\n): Promise<ChartBulkGetResponse> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\tfor (const id of ids) {\n\t\tparams.append(\"ids\", id);\n\t}\n\n\tconst response = await client.get<ChartBulkGetResponse>(\n\t\t`/charts/bulk${params.toString() ? `?${params.toString()}` : \"\"}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tif (options?.includeData) {\n\t\tresponse.data = await Promise.all(\n\t\t\tresponse.data.map(async (chart) => {\n\t\t\t\tconst rows = await executeChartQuery(queryEngine, chart, tenantId);\n\t\t\t\treturn hydrateChartWithData(chart, rows);\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn response;\n}\n\nexport async function getChart(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tid: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkChart> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst chart = await client.get<SdkChart>(\n\t\t`/charts/${encodeURIComponent(id)}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tconst rows = await executeChartQuery(queryEngine, chart, tenantId);\n\treturn hydrateChartWithData(chart, rows);\n}\n\nexport async function updateChart(\n\tclient: IQueryPanelApi,\n\tid: string,\n\tbody: ChartUpdateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkChart> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.put<SdkChart>(\n\t\t`/charts/${encodeURIComponent(id)}`,\n\t\tbody,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\nexport async function deleteChart(\n\tclient: IQueryPanelApi,\n\tid: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<void> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tawait client.delete(\n\t\t`/charts/${encodeURIComponent(id)}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n\tconst resolved = tenantId ?? client.getDefaultTenantId();\n\tif (!resolved) {\n\t\tthrow new Error(\n\t\t\t\"tenantId is required. Provide it per request or via defaultTenantId option.\",\n\t\t);\n\t}\n\treturn resolved;\n}\n\nasync function executeChartQuery(\n\tqueryEngine: QueryEngine,\n\tchart: SdkChart,\n\ttenantId: string,\n): Promise<Record<string, unknown>[]> {\n\tconst databaseName = chart.target_db ?? queryEngine.getDefaultDatabase();\n\tif (!databaseName) {\n\t\tconsole.warn(\"No database available to execute chart query\");\n\t\treturn [];\n\t}\n\ttry {\n\t\tconst result = await queryEngine.validateAndExecute(\n\t\t\tchart.sql,\n\t\t\t(chart.sql_params as ParamRecord | null) ?? {},\n\t\t\tdatabaseName,\n\t\t\ttenantId,\n\t\t);\n\t\treturn result.rows;\n\t} catch (error) {\n\t\tconsole.warn(`Failed to execute chart query: ${error}`);\n\t\treturn [];\n\t}\n}\n\n/**\n * Hydrates a chart with query result data based on its spec type.\n * - For vega-lite: injects data as `data.values`\n * - For vizspec: injects data as `data.values` while preserving `data.sourceId`\n */\nfunction hydrateChartWithData(\n\tchart: SdkChart,\n\trows: Record<string, unknown>[],\n): SdkChart {\n\tconst spec = chart.vega_lite_spec;\n\n\tif (chart.spec_type === \"vizspec\") {\n\t\t// VizSpec format: preserve sourceId and add values\n\t\tconst existingData = (spec.data as Record<string, unknown>) ?? {};\n\t\treturn {\n\t\t\t...chart,\n\t\t\tvega_lite_spec: {\n\t\t\t\t...spec,\n\t\t\t\tdata: {\n\t\t\t\t\t...existingData,\n\t\t\t\t\tvalues: rows,\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\n\t// Vega-Lite format: standard data injection\n\treturn {\n\t\t...chart,\n\t\tvega_lite_spec: {\n\t\t\t...spec,\n\t\t\tdata: {\n\t\t\t\tvalues: rows,\n\t\t\t},\n\t\t},\n\t};\n}\n","import type { IQueryPanelApi } from \"../core/api-types\";\nimport type { QueryEngine } from \"../core/query-engine\";\nimport * as charts from \"./charts\";\n\nexport interface SdkActiveChart {\n\tid: string;\n\tchart_id: string;\n\torder: number | null;\n\tmeta: Record<string, unknown> | null;\n\torganization_id: string | null;\n\ttenant_id: string | null;\n\tuser_id: string | null;\n\tcreated_at: string | null;\n\tupdated_at: string | null;\n\tchart?: charts.SdkChart | null;\n}\n\nexport interface ActiveChartCreateInput {\n\tchart_id: string;\n\torder?: number;\n\tmeta?: Record<string, unknown>;\n}\n\nexport interface ActiveChartUpdateInput {\n\tchart_id?: string;\n\torder?: number;\n\tmeta?: Record<string, unknown>;\n}\n\nexport interface ActiveChartListOptions extends charts.ChartListOptions {\n\twithData?: boolean;\n}\n\nexport interface ActiveChartBulkGetResponse {\n\tdata: SdkActiveChart[];\n\tmissingIds: string[];\n}\n\ninterface RequestOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n}\n\n/**\n * Route module for Active Chart CRUD operations\n * Simple pass-through to backend with optional chart data hydration\n */\nexport async function createActiveChart(\n\tclient: IQueryPanelApi,\n\tbody: ActiveChartCreateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkActiveChart> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.post<SdkActiveChart>(\n\t\t\"/active-charts\",\n\t\tbody,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\nexport async function listActiveCharts(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\toptions?: ActiveChartListOptions,\n\tsignal?: AbortSignal,\n): Promise<charts.PaginatedResponse<SdkActiveChart>> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\tif (options?.pagination?.page)\n\t\tparams.set(\"page\", `${options.pagination.page}`);\n\tif (options?.pagination?.limit)\n\t\tparams.set(\"limit\", `${options.pagination.limit}`);\n\tif (options?.sortBy) params.set(\"sort_by\", options.sortBy);\n\tif (options?.sortDir) params.set(\"sort_dir\", options.sortDir);\n\tif (options?.title) params.set(\"title\", options.title);\n\tif (options?.userFilter) params.set(\"user_id\", options.userFilter);\n\tif (options?.createdFrom) params.set(\"created_from\", options.createdFrom);\n\tif (options?.createdTo) params.set(\"created_to\", options.createdTo);\n\tif (options?.updatedFrom) params.set(\"updated_from\", options.updatedFrom);\n\tif (options?.updatedTo) params.set(\"updated_to\", options.updatedTo);\n\n\tconst response = await client.get<\n\t\tcharts.PaginatedResponse<SdkActiveChart>\n\t>(\n\t\t`/active-charts${params.toString() ? `?${params.toString()}` : \"\"}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tif (options?.withData) {\n\t\tresponse.data = await Promise.all(\n\t\t\tresponse.data.map(async (active) => ({\n\t\t\t\t...active,\n\t\t\t\tchart: active.chart\n\t\t\t\t\t? await charts.getChart(\n\t\t\t\t\t\t\tclient,\n\t\t\t\t\t\t\tqueryEngine,\n\t\t\t\t\t\t\tactive.chart_id,\n\t\t\t\t\t\t\toptions,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t)\n\t\t\t\t\t: null,\n\t\t\t})),\n\t\t);\n\t}\n\n\treturn response;\n}\n\nexport async function listAllActiveCharts(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\toptions?: ActiveChartListOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkActiveChart[]> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst response = await client.get<{ data: SdkActiveChart[] }>(\n\t\t\"/active-charts/all\",\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tif (options?.withData) {\n\t\tresponse.data = await Promise.all(\n\t\t\tresponse.data.map(async (active) => ({\n\t\t\t\t...active,\n\t\t\t\tchart: active.chart\n\t\t\t\t\t? await charts.getChart(\n\t\t\t\t\t\t\tclient,\n\t\t\t\t\t\t\tqueryEngine,\n\t\t\t\t\t\t\tactive.chart_id,\n\t\t\t\t\t\t\toptions,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t)\n\t\t\t\t\t: null,\n\t\t\t})),\n\t\t);\n\t}\n\n\treturn response.data;\n}\n\nexport async function getActiveChartsByIds(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tids: string[],\n\toptions?: ActiveChartListOptions,\n\tsignal?: AbortSignal,\n): Promise<ActiveChartBulkGetResponse> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\tfor (const id of ids) {\n\t\tparams.append(\"ids\", id);\n\t}\n\n\tconst response = await client.get<ActiveChartBulkGetResponse>(\n\t\t`/active-charts/bulk${params.toString() ? `?${params.toString()}` : \"\"}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tif (options?.withData) {\n\t\tresponse.data = await Promise.all(\n\t\t\tresponse.data.map(async (active) => ({\n\t\t\t\t...active,\n\t\t\t\tchart: active.chart\n\t\t\t\t\t? await charts.getChart(\n\t\t\t\t\t\t\tclient,\n\t\t\t\t\t\t\tqueryEngine,\n\t\t\t\t\t\t\tactive.chart_id,\n\t\t\t\t\t\t\toptions,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t)\n\t\t\t\t\t: null,\n\t\t\t})),\n\t\t);\n\t}\n\n\treturn response;\n}\n\nexport async function getActiveChart(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tid: string,\n\toptions?: ActiveChartListOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkActiveChart> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst active = await client.get<SdkActiveChart>(\n\t\t`/active-charts/${encodeURIComponent(id)}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n\n\tif (options?.withData && active.chart_id) {\n\t\treturn {\n\t\t\t...active,\n\t\t\tchart: await charts.getChart(\n\t\t\t\tclient,\n\t\t\t\tqueryEngine,\n\t\t\t\tactive.chart_id,\n\t\t\t\toptions,\n\t\t\t\tsignal,\n\t\t\t),\n\t\t};\n\t}\n\n\treturn active;\n}\n\nexport async function updateActiveChart(\n\tclient: IQueryPanelApi,\n\tid: string,\n\tbody: ActiveChartUpdateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkActiveChart> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.put<SdkActiveChart>(\n\t\t`/active-charts/${encodeURIComponent(id)}`,\n\t\tbody,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\nexport async function deleteActiveChart(\n\tclient: IQueryPanelApi,\n\tid: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<void> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tawait client.delete(\n\t\t`/active-charts/${encodeURIComponent(id)}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n\tconst resolved = tenantId ?? client.getDefaultTenantId();\n\tif (!resolved) {\n\t\tthrow new Error(\n\t\t\t\"tenantId is required. Provide it per request or via defaultTenantId option.\",\n\t\t);\n\t}\n\treturn resolved;\n}\n","import type { IQueryPanelApi } from \"../core/api-types\";\nimport type { PaginatedResponse, PaginationQuery } from \"./charts\";\n\n/**\n * Dashboard interface matching querypanel-sdk schema\n */\nexport interface SdkDashboard {\n\tid: string;\n\torganization_id: string;\n\tname: string;\n\tdescription: string | null;\n\tstatus: \"draft\" | \"deployed\";\n\tcontent_json: string | null;\n\twidget_config: Record<string, unknown> | null;\n\teditor_type: \"blocknote\" | \"custom\";\n\tis_customer_fork: boolean;\n\tforked_from_dashboard_id: string | null;\n\ttenant_id: string | null;\n\tdatasource_id: string | null;\n\tversion: number;\n\tdeployed_at: string | null;\n\tcreated_at: string;\n\tupdated_at: string;\n\tcreated_by: string | null;\n}\n\n/**\n * Dashboard creation input\n */\nexport interface DashboardCreateInput {\n\tname: string;\n\tdescription?: string;\n\tcontent_json?: string;\n\twidget_config?: Record<string, unknown>;\n\teditor_type?: \"blocknote\" | \"custom\";\n\tdatasource_id?: string;\n}\n\n/**\n * Dashboard update input\n */\nexport interface DashboardUpdateInput {\n\tname?: string;\n\tdescription?: string;\n\tcontent_json?: string;\n\twidget_config?: Record<string, unknown>;\n\teditor_type?: \"blocknote\" | \"custom\";\n\tdatasource_id?: string | null;\n}\n\n/**\n * Dashboard list options\n */\nexport interface DashboardListOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n\tpagination?: PaginationQuery;\n\tstatus?: \"draft\" | \"deployed\";\n\tsortBy?: \"name\" | \"created_at\" | \"updated_at\" | \"deployed_at\";\n\tsortDir?: \"asc\" | \"desc\";\n}\n\n/**\n * Fork dashboard input\n */\nexport interface DashboardForkInput {\n\ttenant_id: string;\n\tname?: string;\n}\n\n/**\n * Fork update input\n */\nexport interface DashboardForkUpdateInput {\n\ttenant_id: string;\n\tcontent_json?: string;\n\twidget_config?: Record<string, unknown>;\n}\n\ninterface RequestOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n}\n\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n\tconst resolved = tenantId ?? client.getDefaultTenantId();\n\tif (resolved === undefined) {\n\t\tthrow new Error(\"tenantId is required (option or client default)\");\n\t}\n\treturn resolved;\n}\n\n/**\n * Creates a new dashboard\n */\nexport async function createDashboard(\n\tclient: IQueryPanelApi,\n\tbody: DashboardCreateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.post<SdkDashboard>(\n\t\t\"/dashboards\",\n\t\tbody,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Lists dashboards with pagination and filtering\n */\nexport async function listDashboards(\n\tclient: IQueryPanelApi,\n\toptions?: DashboardListOptions,\n\tsignal?: AbortSignal,\n): Promise<PaginatedResponse<SdkDashboard>> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\n\tif (options?.pagination?.page) {\n\t\tparams.set(\"page\", options.pagination.page.toString());\n\t}\n\tif (options?.pagination?.limit) {\n\t\tparams.set(\"limit\", options.pagination.limit.toString());\n\t}\n\tif (options?.status) {\n\t\tparams.set(\"status\", options.status);\n\t}\n\tif (options?.sortBy) {\n\t\tparams.set(\"sort_by\", options.sortBy);\n\t}\n\tif (options?.sortDir) {\n\t\tparams.set(\"sort_dir\", options.sortDir);\n\t}\n\n\treturn await client.get<PaginatedResponse<SdkDashboard>>(\n\t\t`/dashboards?${params.toString()}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Gets a dashboard by ID\n */\nexport async function getDashboard(\n\tclient: IQueryPanelApi,\n\tid: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.get<SdkDashboard>(\n\t\t`/dashboards/${id}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Gets a dashboard for a specific tenant (returns fork if exists, otherwise original)\n */\nexport async function getDashboardForTenant(\n\tclient: IQueryPanelApi,\n\tid: string,\n\ttenantId: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\tconst params = new URLSearchParams();\n\tparams.set(\"tenant_id\", tenantId);\n\n\treturn await client.get<SdkDashboard>(\n\t\t`/dashboards/${id}/for-tenant?${params.toString()}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Updates a dashboard\n */\nexport async function updateDashboard(\n\tclient: IQueryPanelApi,\n\tid: string,\n\tbody: DashboardUpdateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.put<SdkDashboard>(\n\t\t`/dashboards/${id}`,\n\t\tbody,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Updates dashboard status (deploy/undeploy)\n */\nexport async function updateDashboardStatus(\n\tclient: IQueryPanelApi,\n\tid: string,\n\tstatus: \"draft\" | \"deployed\",\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.patch<SdkDashboard>(\n\t\t`/dashboards/${id}/status`,\n\t\t{ status },\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Deletes a dashboard\n */\nexport async function deleteDashboard(\n\tclient: IQueryPanelApi,\n\tid: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<void> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tawait client.delete(\n\t\t`/dashboards/${id}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n// ============================================================================\n// Customer Fork Operations\n// ============================================================================\n\n/**\n * Forks a dashboard for customer customization\n */\nexport async function forkDashboard(\n\tclient: IQueryPanelApi,\n\tid: string,\n\tinput: DashboardForkInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.post<SdkDashboard>(\n\t\t`/dashboards/${id}/fork`,\n\t\tinput,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Updates a customer fork\n */\nexport async function updateFork(\n\tclient: IQueryPanelApi,\n\tforkId: string,\n\tinput: DashboardForkUpdateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.put<SdkDashboard>(\n\t\t`/dashboards/forks/${forkId}`,\n\t\tinput,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Rollbacks a fork to the original dashboard\n */\nexport async function rollbackFork(\n\tclient: IQueryPanelApi,\n\tforkId: string,\n\ttenantId: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard> {\n\treturn await client.post<SdkDashboard>(\n\t\t`/dashboards/forks/${forkId}/rollback`,\n\t\t{ tenant_id: tenantId },\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Deletes a customer fork\n */\nexport async function deleteFork(\n\tclient: IQueryPanelApi,\n\tforkId: string,\n\ttenantId: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<void> {\n\tconst params = new URLSearchParams();\n\tparams.set(\"tenant_id\", tenantId);\n\n\tawait client.delete(\n\t\t`/dashboards/forks/${forkId}?${params.toString()}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Lists customer forks for a tenant\n */\nexport async function listForksForTenant(\n\tclient: IQueryPanelApi,\n\ttenantId: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkDashboard[]> {\n\treturn await client.get<SdkDashboard[]>(\n\t\t`/dashboards/customer/${tenantId}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n","import crypto from 'node:crypto';\nimport type { IQueryPanelApi } from \"../core/api-types\";\nimport type { QueryEngine } from \"../core/query-engine\";\nimport { SchemaTooLargeError } from \"../errors\";\nimport type { SchemaIntrospection } from \"../schema/types\";\n\nexport interface IngestResponse {\n\tsuccess: boolean;\n\tmessage: string;\n\tchunks: number;\n\tchunks_with_annotations: number;\n\tschema_id?: string;\n\tschema_hash?: string;\n\tdrift_detected?: boolean;\n\tskipped?: boolean;\n}\n\nexport interface SchemaSyncOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n\ttables?: string[];\n\tforceReindex?: boolean;\n}\n\ninterface SchemaIngestColumn {\n\tname: string;\n\tdata_type: string;\n\tis_primary_key: boolean;\n\tdescription: string;\n}\n\ninterface SchemaIngestTable {\n\tschema_name?: string;\n\ttable_name: string;\n\tdescription: string;\n\tcolumns: SchemaIngestColumn[];\n}\n\ninterface SchemaIngestRequest {\n\tdatabase: string;\n\tdialect: string;\n\ttables: SchemaIngestTable[];\n\tforce_reindex?: boolean;\n\ttenant_settings?: {\n\t\ttenantFieldName: string;\n\t\ttenantFieldType: string;\n\t\tenforceTenantIsolation: boolean;\n\t};\n}\n\n/**\n * Route module for schema ingestion\n * Handles introspection and sync to backend\n */\nexport async function syncSchema(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tdatabaseName: string,\n\toptions: SchemaSyncOptions,\n\tsignal?: AbortSignal,\n): Promise<IngestResponse> {\n\tconst tenantId = resolveTenantId(client, options.tenantId);\n\tconst adapter = queryEngine.getDatabase(databaseName);\n\tconst metadata = queryEngine.getDatabaseMetadata(databaseName);\n\n\tconst introspection = await adapter.introspect(\n\t\toptions.tables ? { tables: options.tables } : undefined,\n\t);\n\n\tconst payload = buildSchemaRequest(databaseName, adapter, introspection, metadata);\n\tif (options.forceReindex) {\n\t\tpayload.force_reindex = true;\n\t}\n\n\t// Generate a session id so backend telemetry can correlate all work for this sync\n\tconst sessionId = crypto.randomUUID();\n\n\tconst response = await client.post<IngestResponse>(\n\t\t\"/ingest\",\n\t\tpayload,\n\t\ttenantId,\n\t\toptions.userId,\n\t\toptions.scopes,\n\t\tsignal,\n\t\tsessionId,\n\t);\n\n\treturn response;\n}\n\n// ---------------------------------------------------------------------------\n// Batched ingest (session protocol): stage -> embed each batch -> finalize\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_MAX_BATCH_DOCS = 1000;\nexport const DEFAULT_MAX_BATCH_BYTES = 2_000_000;\n\n/** Progress events emitted by stageSchemaIngest / syncSchemaInBatches. */\nexport type SchemaSyncProgressEvent =\n\t| { phase: \"introspecting\" }\n\t| { phase: \"introspected\"; tableCount: number; columnCount: number }\n\t| { phase: \"uploading\"; done: number; total: number }\n\t| { phase: \"committing\" }\n\t| { phase: \"embedding\"; done: number; total: number; chunks: number }\n\t| { phase: \"finalizing\" };\n\nexport interface SchemaIngestRequestOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n}\n\nexport interface StageSchemaIngestOptions extends SchemaSyncOptions {\n\t/** Max docs per batch (1 per table + 1 per column). Default 1000. */\n\tmaxBatchDocs?: number;\n\t/** Max serialized JSON bytes per batch. Default 2_000_000. */\n\tmaxBatchBytes?: number;\n\t/** Throw SchemaTooLargeError (before uploading) above this many columns. */\n\tmaxColumns?: number;\n\tonProgress?: (event: SchemaSyncProgressEvent) => void;\n}\n\nexport type SyncSchemaInBatchesOptions = StageSchemaIngestOptions;\n\nexport interface StageSchemaIngestResult {\n\tsessionId: string;\n\tstatus: \"skipped\" | \"embedding\";\n\ttotalBatches: number;\n\ttableCount: number;\n\tcolumnCount: number;\n\t/** Present when status is \"skipped\" (schema unchanged). */\n\tskippedResult?: IngestResponse;\n\tdriftDetected?: boolean;\n}\n\nexport interface SchemaIngestBatchEmbedResponse {\n\tindex: number;\n\tchunks: number;\n\tchunks_with_annotations: number;\n}\n\ninterface IngestSessionCreateResponse {\n\tsession_id: string;\n}\n\ntype IngestSessionCommitResponse =\n\t| ({ status: \"skipped\" } & IngestResponse)\n\t| { status: \"embedding\"; total_batches: number; drift_detected?: boolean };\n\nconst utf8 = new TextEncoder();\n\nfunction jsonBytes(value: unknown): number {\n\treturn utf8.encode(JSON.stringify(value)).length;\n}\n\nconst BATCH_ENVELOPE_BYTES = jsonBytes({ tables: [] });\n\n/**\n * Split tables (keeping order) into batches bounded by doc count and JSON size.\n * A single table larger than either limit is placed alone in its own batch.\n * Always returns at least one batch (possibly empty) so a session has >= 1 batch.\n */\nexport function splitIngestTablesIntoBatches<T extends { columns: unknown[] }>(\n\ttables: T[],\n\tmaxBatchDocs: number = DEFAULT_MAX_BATCH_DOCS,\n\tmaxBatchBytes: number = DEFAULT_MAX_BATCH_BYTES,\n): T[][] {\n\tconst batches: T[][] = [];\n\tlet current: T[] = [];\n\tlet currentDocs = 0;\n\tlet currentBytes = BATCH_ENVELOPE_BYTES;\n\n\tfor (const table of tables) {\n\t\tconst docs = 1 + table.columns.length;\n\t\tconst bytes = jsonBytes(table);\n\t\t// +1 for the separating comma when appended to a non-empty batch.\n\t\tif (\n\t\t\tcurrent.length > 0 &&\n\t\t\t(currentDocs + docs > maxBatchDocs ||\n\t\t\t\tcurrentBytes + 1 + bytes > maxBatchBytes)\n\t\t) {\n\t\t\tbatches.push(current);\n\t\t\tcurrent = [];\n\t\t\tcurrentDocs = 0;\n\t\t\tcurrentBytes = BATCH_ENVELOPE_BYTES;\n\t\t}\n\t\tcurrentBytes += bytes + (current.length > 0 ? 1 : 0);\n\t\tcurrent.push(table);\n\t\tcurrentDocs += docs;\n\t}\n\n\tif (current.length > 0 || batches.length === 0) {\n\t\tbatches.push(current);\n\t}\n\treturn batches;\n}\n\nfunction sessionPath(sessionId: string, suffix: string): string {\n\treturn `/ingest/sessions/${encodeURIComponent(sessionId)}${suffix}`;\n}\n\n/**\n * Introspect, upload the schema in batches to a new ingest session, and commit it.\n * Embedding is left to embedSchemaIngestBatch / finalizeSchemaIngest so callers\n * (e.g. serverless routes) can spread the work over several requests.\n */\nexport async function stageSchemaIngest(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tdatabaseName: string,\n\toptions: StageSchemaIngestOptions,\n\tsignal?: AbortSignal,\n): Promise<StageSchemaIngestResult> {\n\tconst tenantId = resolveTenantId(client, options.tenantId);\n\tconst adapter = queryEngine.getDatabase(databaseName);\n\tconst metadata = queryEngine.getDatabaseMetadata(databaseName);\n\tconst emit = options.onProgress ?? (() => {});\n\n\temit({ phase: \"introspecting\" });\n\tconst introspection = await adapter.introspect(\n\t\toptions.tables ? { tables: options.tables } : undefined,\n\t);\n\tconst { tables, ...sessionFields } = buildSchemaRequest(\n\t\tdatabaseName,\n\t\tadapter,\n\t\tintrospection,\n\t\tmetadata,\n\t);\n\tconst tableCount = tables.length;\n\tconst columnCount = tables.reduce((sum, t) => sum + t.columns.length, 0);\n\temit({ phase: \"introspected\", tableCount, columnCount });\n\n\tif (options.maxColumns !== undefined && columnCount > options.maxColumns) {\n\t\tthrow new SchemaTooLargeError(tableCount, columnCount, options.maxColumns);\n\t}\n\n\tconst batches = splitIngestTablesIntoBatches(\n\t\ttables,\n\t\toptions.maxBatchDocs ?? DEFAULT_MAX_BATCH_DOCS,\n\t\toptions.maxBatchBytes ?? DEFAULT_MAX_BATCH_BYTES,\n\t);\n\tconst totalBatches = batches.length;\n\n\tconst created = await client.post<IngestSessionCreateResponse>(\n\t\t\"/ingest/sessions\",\n\t\t{\n\t\t\t...sessionFields,\n\t\t\t...(options.forceReindex ? { force_reindex: true } : {}),\n\t\t\ttotal_batches: totalBatches,\n\t\t},\n\t\ttenantId,\n\t\toptions.userId,\n\t\toptions.scopes,\n\t\tsignal,\n\t\tcrypto.randomUUID(),\n\t);\n\tconst sessionId = created.session_id;\n\n\tfor (let index = 0; index < totalBatches; index += 1) {\n\t\tawait client.put(\n\t\t\tsessionPath(sessionId, `/batches/${index}`),\n\t\t\t{ tables: batches[index] },\n\t\t\ttenantId,\n\t\t\toptions.userId,\n\t\t\toptions.scopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t);\n\t\temit({ phase: \"uploading\", done: index + 1, total: totalBatches });\n\t}\n\n\temit({ phase: \"committing\" });\n\tconst committed = await client.post<IngestSessionCommitResponse>(\n\t\tsessionPath(sessionId, \"/commit\"),\n\t\t{},\n\t\ttenantId,\n\t\toptions.userId,\n\t\toptions.scopes,\n\t\tsignal,\n\t\tsessionId,\n\t);\n\n\tif (committed.status === \"skipped\") {\n\t\tconst { status: _status, ...skippedResult } = committed;\n\t\treturn {\n\t\t\tsessionId,\n\t\t\tstatus: \"skipped\",\n\t\t\ttotalBatches,\n\t\t\ttableCount,\n\t\t\tcolumnCount,\n\t\t\tskippedResult,\n\t\t\tdriftDetected: skippedResult.drift_detected,\n\t\t};\n\t}\n\n\treturn {\n\t\tsessionId,\n\t\tstatus: \"embedding\",\n\t\ttotalBatches: committed.total_batches ?? totalBatches,\n\t\ttableCount,\n\t\tcolumnCount,\n\t\tdriftDetected: committed.drift_detected,\n\t};\n}\n\n/** Embed one committed batch of an ingest session. */\nexport async function embedSchemaIngestBatch(\n\tclient: IQueryPanelApi,\n\tsessionId: string,\n\tindex: number,\n\toptions: SchemaIngestRequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SchemaIngestBatchEmbedResponse> {\n\tconst tenantId = resolveTenantId(client, options.tenantId);\n\treturn await client.post<SchemaIngestBatchEmbedResponse>(\n\t\tsessionPath(sessionId, `/batches/${index}/embed`),\n\t\t{},\n\t\ttenantId,\n\t\toptions.userId,\n\t\toptions.scopes,\n\t\tsignal,\n\t\tsessionId,\n\t);\n}\n\n/** Finalize an ingest session once every batch is embedded. */\nexport async function finalizeSchemaIngest(\n\tclient: IQueryPanelApi,\n\tsessionId: string,\n\toptions: SchemaIngestRequestOptions,\n\tsignal?: AbortSignal,\n): Promise<IngestResponse> {\n\tconst tenantId = resolveTenantId(client, options.tenantId);\n\treturn await client.post<IngestResponse>(\n\t\tsessionPath(sessionId, \"/finalize\"),\n\t\t{},\n\t\ttenantId,\n\t\toptions.userId,\n\t\toptions.scopes,\n\t\tsignal,\n\t\tsessionId,\n\t);\n}\n\n/**\n * Stage, embed every batch, and finalize in-process. For server-side callers\n * without per-request time limits; returns the skipped result when unchanged.\n */\nexport async function syncSchemaInBatches(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tdatabaseName: string,\n\toptions: SyncSchemaInBatchesOptions,\n\tsignal?: AbortSignal,\n): Promise<IngestResponse> {\n\tconst staged = await stageSchemaIngest(\n\t\tclient,\n\t\tqueryEngine,\n\t\tdatabaseName,\n\t\toptions,\n\t\tsignal,\n\t);\n\tif (staged.status === \"skipped\") {\n\t\treturn staged.skippedResult as IngestResponse;\n\t}\n\n\tconst emit = options.onProgress ?? (() => {});\n\tconst requestOptions: SchemaIngestRequestOptions = {\n\t\ttenantId: options.tenantId,\n\t\tuserId: options.userId,\n\t\tscopes: options.scopes,\n\t};\n\tlet chunks = 0;\n\tfor (let index = 0; index < staged.totalBatches; index += 1) {\n\t\tconst embedded = await embedSchemaIngestBatch(\n\t\t\tclient,\n\t\t\tstaged.sessionId,\n\t\t\tindex,\n\t\t\trequestOptions,\n\t\t\tsignal,\n\t\t);\n\t\tchunks += embedded.chunks ?? 0;\n\t\temit({\n\t\t\tphase: \"embedding\",\n\t\t\tdone: index + 1,\n\t\t\ttotal: staged.totalBatches,\n\t\t\tchunks,\n\t\t});\n\t}\n\n\temit({ phase: \"finalizing\" });\n\treturn await finalizeSchemaIngest(\n\t\tclient,\n\t\tstaged.sessionId,\n\t\trequestOptions,\n\t\tsignal,\n\t);\n}\n\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n\tconst resolved = tenantId ?? client.getDefaultTenantId();\n\tif (!resolved) {\n\t\tthrow new Error(\n\t\t\t\"tenantId is required. Provide it per request or via defaultTenantId option.\",\n\t\t);\n\t}\n\treturn resolved;\n}\n\nfunction buildSchemaRequest(\n\tdatabaseName: string,\n\tadapter: { getDialect: () => string },\n\tintrospection: SchemaIntrospection,\n\tmetadata?: {\n\t\ttenantFieldName?: string;\n\t\ttenantFieldType?: string;\n\t\tenforceTenantIsolation?: boolean;\n\t},\n): SchemaIngestRequest {\n\tconst dialect = adapter.getDialect();\n\tconst tables: SchemaIngestTable[] = introspection.tables.map((table) => ({\n\t\tschema_name: table.schema,\n\t\ttable_name: table.name,\n\t\tdescription: table.comment ?? `Table ${table.name}`,\n\t\tcolumns: table.columns.map((column) => ({\n\t\t\tname: column.name,\n\t\t\tdata_type: column.rawType ?? column.type,\n\t\t\tis_primary_key: Boolean(column.isPrimaryKey),\n\t\t\tdescription: column.comment ?? \"\",\n\t\t})),\n\t}));\n\n\tconst request: SchemaIngestRequest = {\n\t\tdatabase: databaseName,\n\t\tdialect,\n\t\ttables,\n\t};\n\n\t// Include tenant_settings if configured in the database metadata\n\tif (metadata?.enforceTenantIsolation === false) {\n\t\trequest.tenant_settings = {\n\t\t\ttenantFieldName: metadata.tenantFieldName ?? \"\",\n\t\t\ttenantFieldType: metadata.tenantFieldType ?? \"String\",\n\t\t\tenforceTenantIsolation: false,\n\t\t};\n\t} else if (metadata?.tenantFieldName && metadata?.tenantFieldType) {\n\t\trequest.tenant_settings = {\n\t\t\ttenantFieldName: metadata.tenantFieldName,\n\t\t\ttenantFieldType: metadata.tenantFieldType,\n\t\t\tenforceTenantIsolation: true,\n\t\t};\n\t}\n\n\treturn request;\n}\n","import crypto from \"node:crypto\";\nimport type { IQueryPanelApi } from \"../core/api-types\";\nimport type { ParamRecord, QueryEngine } from \"../core/query-engine\";\nimport type {\n\tAggregateOp,\n\tChartType,\n\tFieldType,\n\tStackingMode,\n\tTimeUnit,\n\tValueFormat,\n\tVizSpec,\n\tVizSpecKind,\n} from \"../types/vizspec\";\nimport {\n\ttype AskResponse,\n\tanonymizeResults,\n\ttype ChartEnvelope,\n\tresolveQueryPipeline,\n} from \"./query\";\nimport { throwIfGenerationErrorResponse } from \"../errors\";\nimport { resolveQuery } from \"./queries\";\n\n// ============================================================================\n// Input Types for Modifications\n// ============================================================================\n\n/**\n * Simplified field reference for modification inputs.\n * More ergonomic than the full AxisField type.\n */\nexport interface AxisFieldInput {\n\t/** Column name from the SQL result */\n\tfield: string;\n\t/** Human-friendly label for the axis */\n\tlabel?: string;\n\t/** Field data type */\n\ttype?: FieldType;\n\t/** Aggregation operation (e.g., 'sum', 'avg') */\n\taggregate?: AggregateOp;\n\t/** Time unit for temporal fields */\n\ttimeUnit?: TimeUnit;\n\t/** Value formatting options */\n\tformat?: ValueFormat;\n}\n\n/**\n * Simplified field reference for series/grouping fields.\n */\nexport interface FieldRefInput {\n\t/** Column name from the SQL result */\n\tfield: string;\n\t/** Human-friendly label */\n\tlabel?: string;\n\t/** Field data type */\n\ttype?: FieldType;\n}\n\n/**\n * Date range specification for SQL modifications.\n */\nexport interface DateRangeInput {\n\t/** Start date in ISO format (e.g., '2024-01-01') */\n\tfrom?: string;\n\t/** End date in ISO format (e.g., '2024-12-31') */\n\tto?: string;\n}\n\n/**\n * SQL modification options that trigger query regeneration.\n * When any of these are provided, a new ask() call is made.\n */\nexport interface SqlModifications {\n\t/**\n\t * Direct SQL override. When provided, this SQL is executed directly\n\t * without calling the query generation endpoint.\n\t */\n\tcustomSql?: string;\n\n\t/**\n\t * Change the time granularity of the query.\n\t * Triggers SQL regeneration with hints about the desired grouping.\n\t */\n\ttimeGranularity?: TimeUnit;\n\n\t/**\n\t * Filter the query to a specific date range.\n\t * Triggers SQL regeneration with date filter hints.\n\t */\n\tdateRange?: DateRangeInput;\n\n\t/**\n\t * Additional natural language instructions to modify the query.\n\t * These are appended to the original question as hints.\n\t * Example: \"exclude cancelled orders\" or \"only show top 10\"\n\t */\n\tadditionalInstructions?: string;\n}\n\n/**\n * Visualization modification options that don't affect the SQL.\n * These changes only affect how the chart is rendered.\n */\nexport interface VizModifications {\n\t/** Change the VizSpec kind (chart, table, metric; a map cannot be switched to or from). Applies to vizspec only. */\n\tkind?: Exclude<VizSpecKind, \"map\">;\n\t/** Change the chart type (line, bar, area, scatter, pie) */\n\tchartType?: ChartType;\n\n\t/** Configure the X axis field and settings */\n\txAxis?: AxisFieldInput;\n\n\t/** Configure the Y axis field(s) and settings */\n\tyAxis?: AxisFieldInput | AxisFieldInput[];\n\n\t/** Configure the series/grouping field for multi-series charts */\n\tseries?: FieldRefInput;\n\n\t/** Stacking mode for multi-series charts */\n\tstacking?: StackingMode;\n\n\t/** Maximum number of rows to display in the chart */\n\tlimit?: number;\n}\n\n/**\n * Input for the modifyChart() method.\n * Accepts chart data from either ask() responses or saved charts.\n */\nexport interface ChartModifyInput {\n\t/**\n\t * Server-issued id of the query to modify or re-execute.\n\t * From ask() / modifyChart() response: response.queryId\n\t * From saved chart: chart.query_id\n\t *\n\t * Preferred over `sql`: when present the SQL and params are loaded from the server\n\t * (scoped to the authenticated tenant) and `sql` / `params` are ignored.\n\t */\n\tqueryId?: string;\n\n\t/**\n\t * The SQL query to modify or re-execute. Required when `queryId` is not provided.\n\t * From ask() response: response.sql\n\t * From saved chart: chart.sql\n\t *\n\t * Kept for backward compatibility; prefer `queryId`.\n\t */\n\tsql?: string;\n\n\t/**\n\t * The original natural language question.\n\t * Used when regenerating SQL with modifications.\n\t */\n\tquestion: string;\n\n\t/**\n\t * The database to execute the query against.\n\t * From ask() response: response.target_db\n\t * From saved chart: chart.target_db\n\t */\n\tdatabase: string;\n\n\t/**\n\t * Query parameters (optional).\n\t * From ask() response: response.params\n\t * From saved chart: chart.sql_params\n\t */\n\tparams?: ParamRecord;\n\n\t/**\n\t * SQL modifications that trigger query regeneration.\n\t * When provided, a new ask() call is made with modification hints.\n\t */\n\tsqlModifications?: SqlModifications;\n\n\t/**\n\t * Visualization modifications that don't affect the SQL.\n\t * Applied during chart generation.\n\t */\n\tvizModifications?: VizModifications;\n}\n\n/**\n * Options for the modifyChart() method.\n */\nexport interface ChartModifyOptions {\n\t/** Tenant ID for multi-tenant isolation */\n\ttenantId?: string;\n\t/** User ID for audit/tracking */\n\tuserId?: string;\n\t/** Permission scopes */\n\tscopes?: string[];\n\t/** Maximum retry attempts for SQL generation */\n\tmaxRetry?: number;\n\t/** Maximum retry attempts for chart generation */\n\tchartMaxRetries?: number;\n\t/** Chart generation method: 'vega-lite' or 'vizspec' */\n\tchartType?: \"vega-lite\" | \"vizspec\";\n\t/**\n\t * Query pipeline. Only `\"v2\"` is accepted (`POST /v2/query`).\n\t * `\"v1\"` was removed: passing it fails typecheck and throws at runtime.\n\t * Omit this field; chart modifications always use v2.\n\t */\n\tpipeline?: \"v2\";\n\t/**\n\t * Fast path for structured controls (pinned charts): date-only changes\n\t * substitute params or call `/v2/rewrite-datefilter` instead of `/v2/query`,\n\t * and chart generation is skipped when the caller keeps its own encoding\n\t * (`chart.vizSpec` / `vegaLiteSpec` are then omitted). Default: false.\n\t */\n\tlightweight?: boolean;\n\t/**\n\t * QueryPanel session ID for context-aware follow-ups.\n\t * Pass the querypanelSessionId from a previous ask() response\n\t * to preserve conversation history when modifying charts.\n\t * If omitted, modifyChart starts a new QueryPanel session and sends\n\t * the original question with modification hints.\n\t */\n\tquerypanelSessionId?: string;\n\t/**\n\t * Restrict VizSpec chart types for this call (merged with SDK default from constructor when omitted).\n\t */\n\tsupportedChartTypes?: ChartType[];\n\t/**\n\t * When true, rationale includes engineer-style schema detail (tables, columns).\n\t * When false (default), rationale is client-safe for end users.\n\t */\n\tdebug?: boolean;\n}\n\n/**\n * Response from modifyChart(), extending AskResponse with modification metadata.\n */\nexport interface ChartModifyResponse extends AskResponse {\n\t/** Metadata about what was modified */\n\tmodified: {\n\t\t/** Whether the SQL was changed (regenerated or custom) */\n\t\tsqlChanged: boolean;\n\t\t/** Whether visualization settings were applied */\n\t\tvizChanged: boolean;\n\t};\n}\n\n// ============================================================================\n// Server Response Types\n// ============================================================================\n\ninterface ServerQueryResponse {\n\tsuccess: boolean;\n\tsql: string;\n\tparams?: Array<Record<string, unknown>>;\n\tdialect: string;\n\tdatabase?: string;\n\ttable?: string;\n\trationale?: string;\n\tqueryId?: string;\n}\n\ninterface ServerChartResponse {\n\tchart: Record<string, unknown> | null;\n\tnotes: string | null;\n}\n\ninterface ServerVizSpecResponse {\n\tspec: VizSpec;\n\tnotes: string | null;\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Builds a modified question string with SQL modification hints.\n * These hints guide the LLM to generate appropriate SQL.\n */\nfunction buildModifiedQuestion(\n\toriginalQuestion: string,\n\tmodifications: SqlModifications,\n\thasSessionContext = false,\n): string {\n\tconst hints: string[] = [];\n\n\tif (modifications.timeGranularity) {\n\t\thints.push(`group results by ${modifications.timeGranularity}`);\n\t}\n\n\tif (modifications.dateRange) {\n\t\tconst from = normalizeDateInput(modifications.dateRange.from);\n\t\tconst to = normalizeDateInput(modifications.dateRange.to);\n\n\t\tif (from && to) {\n\t\t\thints.push(`change date range to ${from} through ${to}`);\n\t\t} else if (from) {\n\t\t\thints.push(`change start date to ${from}`);\n\t\t} else if (to) {\n\t\t\thints.push(`change end date to ${to}`);\n\t\t}\n\t}\n\n\tif (modifications.additionalInstructions) {\n\t\thints.push(modifications.additionalInstructions);\n\t}\n\n\tif (hints.length === 0) {\n\t\treturn originalQuestion;\n\t}\n\n\t// Hints-only when continuing a QueryPanel session that already has the question.\n\tif (hasSessionContext) {\n\t\treturn hints.join(\", \");\n\t}\n\n\treturn `${originalQuestion} (${hints.join(\", \")})`;\n}\n\nconst REWRITE_DATEFILTER_ENDPOINT = \"/v2/rewrite-datefilter\";\nconst START_PARAM_KEY_REGEX = /(^|_)(start|from)(_|$)/i;\nconst END_PARAM_KEY_REGEX = /(^|_)(end|to)(_|$)/i;\nconst ISO_DATETIME_RE = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z?$/;\nconst SQL_DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\nconst SQL_DATETIME_RE = /^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/;\n\nfunction normalizeDateInput(value?: string): string | undefined {\n\tif (!value) return undefined;\n\tconst trimmed = value.trim();\n\tif (!trimmed) return undefined;\n\n\tif (ISO_DATETIME_RE.test(trimmed)) {\n\t\treturn trimmed\n\t\t\t.replace(\"T\", \" \")\n\t\t\t.replace(/\\.\\d+Z?$/, \"\")\n\t\t\t.replace(/Z$/, \"\");\n\t}\n\n\treturn trimmed;\n}\n\nfunction keyLooksLikeDateBoundary(\n\tkey: string,\n\tboundary: \"start\" | \"end\",\n): boolean {\n\treturn boundary === \"start\"\n\t\t? START_PARAM_KEY_REGEX.test(key)\n\t\t: END_PARAM_KEY_REGEX.test(key);\n}\n\nfunction hasTimeComponent(value: unknown): boolean {\n\treturn typeof value === \"string\" && /\\d{2}:\\d{2}:\\d{2}/.test(value);\n}\n\nfunction formatDateOverride(\n\tdateValue: string,\n\tboundary: \"start\" | \"end\",\n\texistingValue: unknown,\n): string {\n\t// Preserve date-only params as date-only values.\n\tif (SQL_DATE_RE.test(dateValue) && !hasTimeComponent(existingValue)) {\n\t\treturn dateValue;\n\t}\n\n\tif (SQL_DATE_RE.test(dateValue)) {\n\t\treturn `${dateValue} ${boundary === \"start\" ? \"00:00:00\" : \"23:59:59\"}`;\n\t}\n\n\tif (SQL_DATETIME_RE.test(dateValue)) {\n\t\treturn dateValue;\n\t}\n\n\t// Best effort fallback for already-normalized non-standard values.\n\treturn dateValue;\n}\n\nfunction normalizeGeneratedParamKey(\n\tparam: Record<string, unknown>,\n\tindex: number,\n): string {\n\tconst nameCandidate =\n\t\t(typeof param.name === \"string\" && param.name.trim()) ||\n\t\t(typeof param.placeholder === \"string\" && param.placeholder.trim()) ||\n\t\t(typeof param.position === \"number\" && String(param.position)) ||\n\t\tString(index + 1);\n\n\treturn nameCandidate\n\t\t.replace(/[{}]/g, \"\")\n\t\t.replace(/(.+):.*$/, \"$1\")\n\t\t.replace(/^[:$]/, \"\")\n\t\t.trim();\n}\n\nfunction collectParamKeys(\n\tparams: ParamRecord,\n\tparamMetadata: Array<Record<string, unknown>>,\n): string[] {\n\tconst keys = new Set<string>(Object.keys(params));\n\tfor (let i = 0; i < paramMetadata.length; i++) {\n\t\tconst param = paramMetadata[i];\n\t\tif (!param) continue;\n\t\tkeys.add(normalizeGeneratedParamKey(param, i));\n\t}\n\treturn [...keys];\n}\n\n/** `$1` → `1`, `{start_date:Date}` → `start_date`, `@start_date` → `start_date`. */\nfunction placeholderKey(token: string): string {\n\tif (token.startsWith(\"$\") || token.startsWith(\"@\")) return token.slice(1);\n\treturn token.slice(1, -1).replace(/:.+$/, \"\").trim();\n}\n\nfunction isDateLike(value: unknown): boolean {\n\treturn typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}/.test(value.trim());\n}\n\n/**\n * Param keys the SQL uses as a date bound (`created_at >= $1`, `{start_date:Date}`).\n * Postgres stores both the `$n` key it binds and a named alias; only the `$n` key\n * changes the query.\n */\nfunction dateBoundKeysFromSql(sql: string): {\n\tstart: Set<string>;\n\tend: Set<string>;\n} {\n\tconst start = new Set<string>();\n\tconst end = new Set<string>();\n\tconst comparison =\n\t\t/(>=|<=|>|<)\\s*(\\$\\d+|\\{[^}]+\\}|@[A-Za-z_][\\w]*)/g;\n\tconst between =\n\t\t/BETWEEN\\s+(\\$\\d+|\\{[^}]+\\}|@[A-Za-z_][\\w]*)\\s+AND\\s+(\\$\\d+|\\{[^}]+\\}|@[A-Za-z_][\\w]*)/gi;\n\n\tfor (const match of sql.matchAll(comparison)) {\n\t\tconst key = placeholderKey(match[2] ?? \"\");\n\t\tif (!key) continue;\n\t\tif (match[1] === \">=\" || match[1] === \">\") start.add(key);\n\t\telse end.add(key);\n\t}\n\n\tfor (const match of sql.matchAll(between)) {\n\t\tconst startKey = placeholderKey(match[1] ?? \"\");\n\t\tconst endKey = placeholderKey(match[2] ?? \"\");\n\t\tif (startKey) start.add(startKey);\n\t\tif (endKey) end.add(endKey);\n\t}\n\n\treturn { start, end };\n}\n\nfunction paramValueForKey(\n\tkey: string,\n\tparams: ParamRecord,\n\tparamMetadata: Array<Record<string, unknown>>,\n): unknown {\n\tif (key in params) return params[key];\n\tfor (let i = 0; i < paramMetadata.length; i++) {\n\t\tconst param = paramMetadata[i];\n\t\tif (!param) continue;\n\t\tif (normalizeGeneratedParamKey(param, i) === key) return param.value;\n\t}\n\treturn undefined;\n}\n\n/** Date-like params this range can overwrite: SQL placeholders plus named aliases. */\nfunction dateBoundKeys(\n\tsql: string,\n\tparams: ParamRecord,\n\tparamMetadata: Array<Record<string, unknown>>,\n\tboundary: \"start\" | \"end\",\n): string[] {\n\tconst fromSql = dateBoundKeysFromSql(sql)[boundary];\n\tconst keys: string[] = [];\n\tfor (const key of collectParamKeys(params, paramMetadata)) {\n\t\tconst named = keyLooksLikeDateBoundary(key, boundary);\n\t\tif (!named && !fromSql.has(key)) continue;\n\t\tif (!isDateLike(paramValueForKey(key, params, paramMetadata))) continue;\n\t\tkeys.push(key);\n\t}\n\treturn keys;\n}\n\n/**\n * True when date-range-only SQL mods can be applied by substituting existing\n * start/end params (no NL→SQL regeneration).\n */\nfunction canSubstituteDateRangeParams(\n\tdateRange: DateRangeInput | undefined,\n\tsql: string,\n\tparams: ParamRecord,\n\tparamMetadata: Array<Record<string, unknown>> = [],\n): boolean {\n\tif (!dateRange) return false;\n\tconst from = normalizeDateInput(dateRange.from);\n\tconst to = normalizeDateInput(dateRange.to);\n\tif (!from && !to) return false;\n\n\tif (from && dateBoundKeys(sql, params, paramMetadata, \"start\").length === 0) {\n\t\treturn false;\n\t}\n\tif (to && dateBoundKeys(sql, params, paramMetadata, \"end\").length === 0) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\n/** Date-range only (no custom SQL, granularity, or extra NL instructions). */\nfunction isDateRangeOnlySqlModification(\n\tmodifications: SqlModifications | undefined,\n): boolean {\n\tif (!modifications?.dateRange) return false;\n\tif (modifications.customSql) return false;\n\tif (modifications.timeGranularity) return false;\n\tif (\n\t\ttypeof modifications.additionalInstructions === \"string\" &&\n\t\tmodifications.additionalInstructions.trim().length > 0\n\t) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nfunction applyDateRangeOverrides(\n\tdateRange: DateRangeInput | undefined,\n\tsql: string,\n\tparams: ParamRecord,\n\tparamMetadata: Array<Record<string, unknown>>,\n): void {\n\tif (!dateRange) return;\n\n\tconst from = normalizeDateInput(dateRange.from);\n\tconst to = normalizeDateInput(dateRange.to);\n\tif (!from && !to) return;\n\n\tconst startKeys = new Set(\n\t\tfrom ? dateBoundKeys(sql, params, paramMetadata, \"start\") : [],\n\t);\n\tconst endKeys = new Set(\n\t\tto ? dateBoundKeys(sql, params, paramMetadata, \"end\") : [],\n\t);\n\n\tfor (const [key, value] of Object.entries(params)) {\n\t\tif (startKeys.has(key)) {\n\t\t\tparams[key] = formatDateOverride(from ?? \"\", \"start\", value);\n\t\t}\n\t\tif (endKeys.has(key)) {\n\t\t\tparams[key] = formatDateOverride(to ?? \"\", \"end\", value);\n\t\t}\n\t}\n\n\tfor (let i = 0; i < paramMetadata.length; i++) {\n\t\tconst param = paramMetadata[i];\n\t\tif (!param) continue;\n\t\tconst key = normalizeGeneratedParamKey(param, i);\n\n\t\tif (startKeys.has(key)) {\n\t\t\tparam.value = formatDateOverride(from ?? \"\", \"start\", param.value);\n\t\t}\n\t\tif (endKeys.has(key)) {\n\t\t\tparam.value = formatDateOverride(to ?? \"\", \"end\", param.value);\n\t\t}\n\t}\n}\n\n/** Previous params for rewrite-datefilter: prefer metadata, else `{name,value}` entries. */\nfunction buildPreviousParams(\n\tparams: ParamRecord,\n\tparamMetadata: Array<Record<string, unknown>>,\n): Array<Record<string, unknown>> {\n\tif (paramMetadata.length > 0) return paramMetadata;\n\treturn Object.entries(params).map(([name, value]) => ({ name, value }));\n}\n\n/**\n * Builds viz modification hints for the chart generation endpoint.\n */\nfunction buildVizHints(\n\tmodifications: VizModifications,\n): Record<string, unknown> {\n\tconst hints: Record<string, unknown> = {};\n\n\tif (modifications.kind) {\n\t\thints.kind = modifications.kind;\n\t}\n\n\tif (modifications.chartType) {\n\t\thints.chartType = modifications.chartType;\n\t}\n\n\tif (modifications.xAxis) {\n\t\thints.xAxis = modifications.xAxis;\n\t}\n\n\tif (modifications.yAxis) {\n\t\thints.yAxis = modifications.yAxis;\n\t}\n\n\tif (modifications.series) {\n\t\thints.series = modifications.series;\n\t}\n\n\tif (modifications.stacking) {\n\t\thints.stacking = modifications.stacking;\n\t}\n\n\tif (modifications.limit !== undefined) {\n\t\thints.limit = modifications.limit;\n\t}\n\n\treturn hints;\n}\n\n/**\n * Remove VizSpec-only hints for Vega-Lite chart generation.\n */\nfunction stripVizSpecOnlyHints(\n\thints: Record<string, unknown>,\n): Record<string, unknown> {\n\tif (!(\"kind\" in hints)) {\n\t\treturn hints;\n\t}\n\n\tconst { kind: _kind, ...rest } = hints;\n\treturn rest;\n}\n\n/**\n * Resolves tenant ID from options or client default.\n */\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n\tconst resolved = tenantId ?? client.getDefaultTenantId();\n\tif (!resolved) {\n\t\tthrow new Error(\n\t\t\t\"tenantId is required. Provide it per request or via defaultTenantId option.\",\n\t\t);\n\t}\n\treturn resolved;\n}\n\n// ============================================================================\n// Main Function\n// ============================================================================\n\n/**\n * Modifies a chart by regenerating SQL and/or applying visualization changes.\n *\n * This method supports three modes of operation:\n *\n * 1. **SQL Modifications**: When `sqlModifications` is provided, the SQL is\n * regenerated using the query endpoint with modification hints. If `customSql`\n * is set, it's used directly without regeneration.\n *\n * 2. **Visualization Modifications**: When only `vizModifications` is provided,\n * the existing SQL is re-executed and a new chart is generated with the\n * specified encoding preferences.\n *\n * 3. **Combined**: Both SQL and visualization modifications can be applied\n * together. SQL is regenerated first, then viz modifications are applied.\n *\n * @param client - The API client for making requests\n * @param queryEngine - The query engine for executing SQL\n * @param input - Chart modification input with source data and modifications\n * @param options - Optional settings for tenant, user, and chart generation\n * @param signal - Optional AbortSignal for cancellation\n * @returns Modified chart response with SQL, data, and chart specification\n *\n * @example\n * ```typescript\n * // Change chart type and axis\n * const modified = await modifyChart(client, engine, {\n * sql: response.sql,\n * question: \"revenue by country\",\n * database: \"analytics\",\n * vizModifications: {\n * chartType: \"bar\",\n * xAxis: { field: \"country\" },\n * yAxis: { field: \"revenue\", aggregate: \"sum\" },\n * },\n * }, { tenantId: \"tenant_123\" });\n *\n * // Change time granularity (triggers SQL regeneration)\n * const monthly = await modifyChart(client, engine, {\n * sql: response.sql,\n * question: \"revenue over time\",\n * database: \"analytics\",\n * sqlModifications: {\n * timeGranularity: \"month\",\n * dateRange: { from: \"2024-01-01\", to: \"2024-12-31\" },\n * },\n * }, { tenantId: \"tenant_123\" });\n * ```\n */\nexport async function modifyChart(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tinput: ChartModifyInput,\n\toptions?: ChartModifyOptions,\n\tsignal?: AbortSignal,\n): Promise<ChartModifyResponse> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst sessionId = crypto.randomUUID();\n\tconst querypanelSessionId = options?.querypanelSessionId ?? sessionId;\n\tconst hasQuerypanelSessionContext = !!options?.querypanelSessionId;\n\tconst chartType = options?.chartType ?? \"vega-lite\";\n\n\tconst hasSqlMods = !!input.sqlModifications;\n\tconst hasVizMods = !!input.vizModifications;\n\tconst hasCustomSql = !!input.sqlModifications?.customSql;\n\t// `lightweight` (pinned chart controls): the caller reapplies the previous\n\t// chart encoding, so date-only changes avoid NL→SQL and no chart LLM runs.\n\tconst lightweight = options?.lightweight === true;\n\tconst dateOnly =\n\t\tlightweight && isDateRangeOnlySqlModification(input.sqlModifications);\n\tconst skipChartLlm =\n\t\tlightweight && !hasCustomSql && (dateOnly || (hasVizMods && !hasSqlMods));\n\n\tresolveQueryPipeline(options?.pipeline);\n\tconst queryEndpoint = \"/v2/query\";\n\n\t// Determine which SQL to use. A queryId takes priority: the server-stored SQL and\n\t// params are used and any client-supplied sql / params are ignored.\n\tlet currentSql = input.sql;\n\tlet finalParams = input.params ?? {};\n\tlet paramMetadata: Array<Record<string, unknown>> = [];\n\tlet rationale: string | undefined;\n\tlet queryId: string | undefined;\n\tif (input.queryId) {\n\t\tconst resolved = await resolveQuery(\n\t\t\tclient,\n\t\t\tqueryEngine,\n\t\t\tinput.queryId,\n\t\t\t{ tenantId, userId: options?.userId, scopes: options?.scopes },\n\t\t\tsignal,\n\t\t);\n\t\tcurrentSql = resolved.sql;\n\t\tfinalParams = resolved.params;\n\t\tparamMetadata = resolved.paramMetadata;\n\t\tqueryId = resolved.queryId;\n\t}\n\tif (!currentSql) {\n\t\tthrow new Error(\"modifyChart requires either queryId or sql.\");\n\t}\n\tlet finalSql = currentSql;\n\tlet sqlChanged = false;\n\tlet finalQuestion = input.question;\n\n\t// Get database metadata for tenant settings\n\tconst databaseName = input.database ?? queryEngine.getDefaultDatabase();\n\tif (!databaseName) {\n\t\tthrow new Error(\n\t\t\t\"No database specified. Provide database in input or attach a default database.\",\n\t\t);\n\t}\n\n\tconst metadata = queryEngine.getDatabaseMetadata(databaseName);\n\tlet tenantSettings: Record<string, unknown> | undefined;\n\tif (metadata?.enforceTenantIsolation === false) {\n\t\ttenantSettings = {\n\t\t\ttenantFieldName: metadata.tenantFieldName ?? \"\",\n\t\t\ttenantFieldType: metadata.tenantFieldType ?? \"String\",\n\t\t\tenforceTenantIsolation: false,\n\t\t};\n\t} else if (metadata?.tenantFieldName) {\n\t\ttenantSettings = {\n\t\t\ttenantFieldName: metadata.tenantFieldName,\n\t\t\ttenantFieldType: metadata.tenantFieldType,\n\t\t\tenforceTenantIsolation: true,\n\t\t};\n\t}\n\n\t// Path 1: Custom SQL provided - use it directly\n\tif (hasCustomSql) {\n\t\tfinalSql = input.sqlModifications!.customSql!;\n\t\tfinalParams = {};\n\t\tparamMetadata = [];\n\t\t// Client-supplied SQL is not a server-generated query, so it has no id.\n\t\tqueryId = undefined;\n\t\tsqlChanged = true;\n\t}\n\t// Path 2a: Date-range only + existing start/end params → substitute, keep SQL\n\telse if (\n\t\tdateOnly &&\n\t\tcanSubstituteDateRangeParams(\n\t\t\tinput.sqlModifications?.dateRange,\n\t\t\tfinalSql,\n\t\t\tfinalParams,\n\t\t\tparamMetadata,\n\t\t)\n\t) {\n\t\tfinalParams = { ...finalParams };\n\t\tapplyDateRangeOverrides(\n\t\t\tinput.sqlModifications?.dateRange,\n\t\t\tfinalSql,\n\t\t\tfinalParams,\n\t\t\tparamMetadata,\n\t\t);\n\t\tsqlChanged = false;\n\t}\n\t// Path 2b: SQL modifications (non-custom) - regenerate via query endpoint,\n\t// or via the cheaper date-filter rewrite when only the date range changed.\n\telse if (hasSqlMods && !hasCustomSql) {\n\t\tconst modifiedQuestion = buildModifiedQuestion(\n\t\t\tinput.question,\n\t\t\tinput.sqlModifications!,\n\t\t\thasQuerypanelSessionContext,\n\t\t);\n\t\tfinalQuestion = modifiedQuestion;\n\n\t\tconst sharedBody = {\n\t\t\tsession_id: querypanelSessionId,\n\t\t\tprevious_sql: currentSql,\n\t\t\t...(tenantSettings ? { tenant_settings: tenantSettings } : {}),\n\t\t\t...(databaseName ? { database: databaseName } : {}),\n\t\t\t...(metadata?.dialect ? { dialect: metadata.dialect } : {}),\n\t\t\tdebug: options?.debug ?? false,\n\t\t};\n\t\tconst [endpoint, body] = dateOnly\n\t\t\t? [\n\t\t\t\t\tREWRITE_DATEFILTER_ENDPOINT,\n\t\t\t\t\t{\n\t\t\t\t\t\t...sharedBody,\n\t\t\t\t\t\tquestion: input.question,\n\t\t\t\t\t\tprevious_params: buildPreviousParams(finalParams, paramMetadata),\n\t\t\t\t\t\tdate_range: {\n\t\t\t\t\t\t\tfrom: normalizeDateInput(input.sqlModifications?.dateRange?.from),\n\t\t\t\t\t\t\tto: normalizeDateInput(input.sqlModifications?.dateRange?.to),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: [\n\t\t\t\t\tqueryEndpoint,\n\t\t\t\t\t{\n\t\t\t\t\t\t...sharedBody,\n\t\t\t\t\t\tquestion: modifiedQuestion,\n\t\t\t\t\t\t...(options?.maxRetry ? { max_retry: options.maxRetry } : {}),\n\t\t\t\t\t},\n\t\t\t\t];\n\n\t\tconst queryResponse = await client.post<ServerQueryResponse>(\n\t\t\tendpoint,\n\t\t\tbody,\n\t\t\ttenantId,\n\t\t\toptions?.userId,\n\t\t\toptions?.scopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t);\n\n\t\tfinalSql = queryResponse.sql;\n\t\tparamMetadata = Array.isArray(queryResponse.params)\n\t\t\t? queryResponse.params\n\t\t\t: [];\n\t\tfinalParams = queryEngine.mapGeneratedParams(paramMetadata);\n\t\tapplyDateRangeOverrides(\n\t\t\tinput.sqlModifications?.dateRange,\n\t\t\tfinalSql,\n\t\t\tfinalParams,\n\t\t\tparamMetadata,\n\t\t);\n\t\trationale = queryResponse.rationale;\n\t\tqueryId = queryResponse.queryId;\n\t\tsqlChanged = finalSql !== currentSql;\n\t}\n\n\t// Execute the SQL\n\tconst execution = await queryEngine.validateAndExecute(\n\t\tfinalSql,\n\t\tfinalParams,\n\t\tdatabaseName,\n\t\ttenantId,\n\t);\n\tconst rows = execution.rows ?? [];\n\n\t// Generate chart encoding even when the query returns no rows,\n\t// so headless clients can show \"No Data\" with the correct chart type.\n\tlet chart: ChartEnvelope = {\n\t\tspecType: chartType,\n\t\tnotes: rows.length === 0 ? \"Query returned no rows.\" : null,\n\t};\n\n\t// Build viz hints if modifications provided\n\tconst vizHints = hasVizMods ? buildVizHints(input.vizModifications!) : {};\n\tconst vizHintsForChart =\n\t\tchartType === \"vizspec\" ? vizHints : stripVizSpecOnlyHints(vizHints);\n\n\tif (!skipChartLlm && chartType === \"vizspec\") {\n\t\tconst vizspecResponse = await client.post<ServerVizSpecResponse>(\n\t\t\t\"/vizspec\",\n\t\t\t{\n\t\t\t\tquestion: finalQuestion,\n\t\t\t\tsql: finalSql,\n\t\t\t\trationale,\n\t\t\t\tfields: execution.fields,\n\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\tmax_retries: options?.chartMaxRetries ?? 3,\n\t\t\t\tquery_id: queryId,\n\t\t\t\t// Include viz hints for the chart generator\n\t\t\t\t...(hasVizMods ? { encoding_hints: vizHintsForChart } : {}),\n\t\t\t\t...(options?.supportedChartTypes?.length\n\t\t\t\t\t? { supported_chart_types: options.supportedChartTypes }\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t\ttenantId,\n\t\t\toptions?.userId,\n\t\t\toptions?.scopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t);\n\t\tthrowIfGenerationErrorResponse(vizspecResponse);\n\n\t\tchart = {\n\t\t\tvizSpec: vizspecResponse.spec,\n\t\t\tspecType: \"vizspec\",\n\t\t\tnotes:\n\t\t\t\trows.length === 0\n\t\t\t\t\t? (vizspecResponse.notes ?? \"Query returned no rows.\")\n\t\t\t\t\t: vizspecResponse.notes,\n\t\t};\n\t} else if (!skipChartLlm) {\n\t\tconst chartResponse = await client.post<ServerChartResponse>(\n\t\t\t\"/chart\",\n\t\t\t{\n\t\t\t\tquestion: finalQuestion,\n\t\t\t\tsql: finalSql,\n\t\t\t\trationale,\n\t\t\t\tfields: execution.fields,\n\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\tmax_retries: options?.chartMaxRetries ?? 3,\n\t\t\t\tquery_id: queryId,\n\t\t\t\t// Include viz hints for the chart generator\n\t\t\t\t...(hasVizMods ? { encoding_hints: vizHintsForChart } : {}),\n\t\t\t},\n\t\t\ttenantId,\n\t\t\toptions?.userId,\n\t\t\toptions?.scopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t);\n\t\tthrowIfGenerationErrorResponse(chartResponse);\n\n\t\tchart = {\n\t\t\tvegaLiteSpec: chartResponse.chart\n\t\t\t\t? {\n\t\t\t\t\t\t...chartResponse.chart,\n\t\t\t\t\t\tdata: { values: rows },\n\t\t\t\t\t}\n\t\t\t\t: null,\n\t\t\tspecType: \"vega-lite\",\n\t\t\tnotes:\n\t\t\t\trows.length === 0\n\t\t\t\t\t? (chartResponse.notes ?? \"Query returned no rows.\")\n\t\t\t\t\t: chartResponse.notes,\n\t\t};\n\t}\n\n\treturn {\n\t\tsql: finalSql,\n\t\tparams: finalParams,\n\t\tparamMetadata,\n\t\trationale,\n\t\tdialect: metadata?.dialect ?? \"unknown\",\n\t\tqueryId,\n\t\tquerypanelSessionId,\n\t\trows,\n\t\tfields: execution.fields,\n\t\tchart,\n\t\tattempts: 1,\n\t\ttarget_db: databaseName,\n\t\tmodified: {\n\t\t\tsqlChanged,\n\t\t\tvizChanged: hasVizMods,\n\t\t},\n\t};\n}\n","import crypto from \"node:crypto\";\nimport type { IQueryPanelApi } from \"../core/api-types\";\nimport type { ParamRecord, QueryEngine } from \"../core/query-engine\";\nimport { type QueryErrorCode, QueryPipelineError, isAiProviderPipelineError, throwIfGenerationErrorResponse } from \"../errors\";\nimport type { ChartType, VizSpec } from \"../types/vizspec\";\n\n/**\n * Context document returned by the query pipeline.\n */\nexport interface ContextDocument {\n\t/** Optional source identifier for the document. */\n\tsource?: string;\n\t/** Raw document content or excerpt. */\n\tpageContent: string;\n\t/** Additional metadata attached to the document. */\n\tmetadata?: Record<string, unknown>;\n\t/** Optional relevance score from retrieval. */\n\tscore?: number;\n}\n\n/**\n * Chart payload returned with query results.\n */\nexport interface ChartEnvelope {\n\t/** Vega-Lite spec when specType is \"vega-lite\". */\n\tvegaLiteSpec?: Record<string, unknown> | null;\n\t/** VizSpec payload when specType is \"vizspec\". */\n\tvizSpec?: VizSpec | null;\n\t/** Chart specification type. */\n\tspecType: \"vega-lite\" | \"vizspec\";\n\t/** Optional chart generation notes or errors. */\n\tnotes: string | null;\n}\n\n/**\n * Configuration options for query generation.\n */\nexport interface AskOptions {\n\t/** Tenant identifier for scoped access. */\n\ttenantId?: string;\n\t/** Optional user identifier for audit/telemetry. */\n\tuserId?: string;\n\t/** Optional scopes to include in the auth token. */\n\tscopes?: string[];\n\t/** Override the default database name. */\n\tdatabase?: string;\n\t/** Previous error message for retry context. */\n\tlastError?: string;\n\t/** Previous SQL statement for retry context. */\n\tpreviousSql?: string;\n\t/** Maximum number of retry attempts on execution failure. */\n\tmaxRetry?: number;\n\t/**\n\t * Also retry (within `maxRetry`) when the query returns no rows, or one row of only zeros/NULLs\n\t * (what `COUNT(*) ... WHERE status = 'approved'` returns when the data stores 'APPROVE').\n\t * Only that fact is sent to QueryPanel, never rows or values. Default false.\n\t */\n\tretryOnEmptyResult?: boolean;\n\t/** Maximum number of retries for chart generation. */\n\tchartMaxRetries?: number;\n\t/** Choose chart generation method. */\n\tchartType?: \"vega-lite\" | \"vizspec\";\n\t/**\n\t * QueryPanel session ID for context-aware follow-ups.\n\t * Use this to reuse a previously returned session for follow-up prompts.\n\t */\n\tquerypanelSessionId?: string;\n\t/**\n\t * Query pipeline. Only `\"v2\"` is accepted (`POST /v2/query`).\n\t * `\"v1\"` was removed: passing it fails typecheck and throws at runtime.\n\t * Omit this field; asks always use v2.\n\t */\n\tpipeline?: \"v2\";\n\t/**\n\t * Optional model id for SQL generation (e.g. OpenAI or `gemini-*` when the API uses Gemini). Omit for server default.\n\t */\n\tmodel?: string;\n\t/**\n\t * Optional additional system prompt text for the v2 pipeline.\n\t *\n\t * This is appended to the backend's SQL generator/reflection system prompts\n\t * (it does not change tenant isolation mechanics; it only guides the LLM).\n\t *\n\t * Use this for tenant-specific policies (e.g. retention windows) that must\n\t * be enforced on every query.\n\t *\n\t * Sent on every ask (`POST /v2/query`).\n\t */\n\tsystemPrompt?: string;\n\t/**\n\t * Restrict VizSpec `chartType` values when `chartType` is `\"vizspec\"`.\n\t * Sent to `POST /vizspec` as `supported_chart_types`. Omitted allows all standard types.\n\t */\n\tsupportedChartTypes?: ChartType[];\n\t/**\n\t * When true, rationale includes engineer-style schema detail (tables, columns).\n\t * When false (default), rationale is client-safe for end users.\n\t */\n\tdebug?: boolean;\n}\n\n/**\n * Intent analysis result from the v2 pipeline.\n */\nexport interface IntentResult {\n\tintent: string;\n\tconfidence: number;\n\tplan: {\n\t\ttables: string[];\n\t\toperations: string[];\n\t\tfilters: string[];\n\t\torderBy?: string;\n\t\tlimit?: number;\n\t};\n\tambiguities: Array<{ issue: string; suggestion: string }>;\n}\n\n/**\n * Pipeline execution trace with step-level timing.\n */\nexport interface PipelineTrace {\n\ttotalDurationMs: number;\n\tsteps: Array<{ step: string; durationMs: number }>;\n}\n\n/**\n * Response returned after executing a query.\n */\nexport interface AskResponse {\n\t/** Generated SQL statement. */\n\tsql: string;\n\t/** Parameter values for the generated SQL. */\n\tparams: ParamRecord;\n\t/** Raw parameter metadata from the backend. */\n\tparamMetadata: Array<Record<string, unknown>>;\n\t/** Optional reasoning for SQL generation. */\n\trationale?: string;\n\t/** SQL dialect selected by the backend. */\n\tdialect: string;\n\t/** Optional query identifier for traceability. */\n\tqueryId?: string;\n\t/** Result rows returned by the query execution. */\n\trows: Array<Record<string, unknown>>;\n\t/** Column names for returned rows. */\n\tfields: string[];\n\t/** Generated chart payload. */\n\tchart: ChartEnvelope;\n\t/** Optional context documents used for query generation. */\n\tcontext?: ContextDocument[];\n\t/** Number of attempts used for execution. */\n\tattempts?: number;\n\t/** Target database name resolved by the backend or engine. */\n\ttarget_db?: string;\n\t/** QueryPanel session ID for follow-up queries. */\n\tquerypanelSessionId?: string;\n\t/** Intent analysis from v2 pipeline. */\n\tintent?: IntentResult;\n\t/** Pipeline execution trace from v2 pipeline. */\n\ttrace?: PipelineTrace;\n}\n\ninterface ServerQueryResponse {\n\tsuccess: boolean;\n\tsql?: string;\n\tparams?: Array<Record<string, unknown>>;\n\tdialect?: string;\n\tdatabase?: string;\n\ttable?: string;\n\trationale?: string;\n\tqueryId?: string;\n\tcontext?: ContextDocument[];\n\t// v2-specific fields\n\tintent?: IntentResult;\n\ttrace?: PipelineTrace;\n\t// Error fields\n\terror?: string;\n\tcode?: QueryErrorCode;\n\tdetails?: Record<string, unknown>;\n}\n\ninterface ServerChartResponse {\n\tchart: Record<string, unknown> | null;\n\tnotes: string | null;\n}\n\ninterface ServerVizSpecResponse {\n\tspec: VizSpec;\n\tnotes: string | null;\n}\n\n/**\n * Resolve ask/modify pipeline. Always `\"v2\"` (`POST /v2/query`).\n * Throws if a removed pipeline such as `\"v1\"` is passed.\n */\nexport function resolveQueryPipeline(pipeline?: \"v2\"): \"v2\" {\n\tconst requested: string | undefined = pipeline;\n\tif (requested != null && requested !== \"v2\") {\n\t\tthrow new Error(\n\t\t\t`Query pipeline \"${requested}\" was removed. ask() and modifyChart() always use POST /v2/query.`,\n\t\t);\n\t}\n\treturn \"v2\";\n}\n\n/** Sent as `last_error` when retryOnEmptyResult retries an empty result. Carries no data. */\nexport const EMPTY_RESULT_ERROR =\n\t\"The query ran but returned no rows (or only zero/NULL values). A filter value probably does not match how the data stores it (case, spelling, codes such as 'APPROVE' vs 'approved'), or a filter is narrower than the question. Check each filter value against the column's documented values and fix it; keep every filter the question asks for.\";\n\n/** No rows, or one row whose values are all 0 / \"0\" / null (an aggregate over nothing). */\nexport function isEmptyLikeResult(rows: unknown[]): boolean {\n\tif (rows.length === 0) return true;\n\tif (rows.length > 1) return false;\n\tconst row = rows[0];\n\tif (!row || typeof row !== \"object\") return false;\n\tconst values = Object.values(row as Record<string, unknown>);\n\treturn values.length > 0 && values.every((v) => v === null || v === 0 || v === \"0\" || v === 0n);\n}\n\n/**\n * Route module for natural language query generation\n * Simple orchestration following Ousterhout's principle\n */\nexport async function ask(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tquestion: string,\n\toptions: AskOptions,\n\tsignal?: AbortSignal,\n): Promise<AskResponse> {\n\tconst tenantId = resolveTenantId(client, options.tenantId);\n\tconst sessionId = crypto.randomUUID();\n\tconst querypanelSessionId = options.querypanelSessionId ?? sessionId;\n\tconst maxRetry = options.maxRetry ?? 0;\n\tlet attempt = 0;\n\tlet lastError: string | undefined = options.lastError;\n\tlet previousSql: string | undefined = options.previousSql;\n\tlet parentQueryId: string | undefined;\n\n\tresolveQueryPipeline(options.pipeline);\n\tconst queryEndpoint = \"/v2/query\";\n\n\twhile (attempt <= maxRetry) {\n\t\t// Step 1: Get SQL from backend\n\t\tconst databaseName = options.database ?? queryEngine.getDefaultDatabase();\n\t\tconst metadata = databaseName\n\t\t\t? queryEngine.getDatabaseMetadata(databaseName)\n\t\t\t: undefined;\n\n\t\tlet tenantSettings: Record<string, unknown> | undefined;\n\t\tif (metadata?.enforceTenantIsolation === false) {\n\t\t\ttenantSettings = {\n\t\t\t\ttenantFieldName: metadata.tenantFieldName ?? \"\",\n\t\t\t\ttenantFieldType: metadata.tenantFieldType ?? \"String\",\n\t\t\t\tenforceTenantIsolation: false,\n\t\t\t};\n\t\t} else if (metadata?.tenantFieldName) {\n\t\t\ttenantSettings = {\n\t\t\t\ttenantFieldName: metadata.tenantFieldName,\n\t\t\t\ttenantFieldType: metadata.tenantFieldType,\n\t\t\t\tenforceTenantIsolation: true,\n\t\t\t};\n\t\t}\n\n\t\tconst queryResponse = await client.postWithHeaders<ServerQueryResponse>(\n\t\t\tqueryEndpoint,\n\t\t\t{\n\t\t\t\tquestion,\n\t\t\t\t...(querypanelSessionId ? { session_id: querypanelSessionId } : {}),\n\t\t\t\t...(lastError ? { last_error: lastError } : {}),\n\t\t\t\t...(previousSql ? { previous_sql: previousSql } : {}),\n\t\t\t\t...(options.maxRetry ? { max_retry: options.maxRetry } : {}),\n\t\t\t\t...(tenantSettings ? { tenant_settings: tenantSettings } : {}),\n\t\t\t\t...(databaseName ? { database: databaseName } : {}),\n\t\t\t\t...(metadata?.dialect ? { dialect: metadata.dialect } : {}),\n\t\t\t\t...(options.model?.trim() ? { model: options.model.trim() } : {}),\n\t\t\t\t...(options.systemPrompt?.trim()\n\t\t\t\t\t? { system_prompt: options.systemPrompt.trim() }\n\t\t\t\t\t: {}),\n\t\t\t\t...(parentQueryId ? { parent_query_id: parentQueryId } : {}),\n\t\t\t\tdebug: options.debug ?? false,\n\t\t\t},\n\t\t\ttenantId,\n\t\t\toptions.userId,\n\t\t\toptions.scopes,\n\t\t\tsignal,\n\t\t\tsessionId,\n\t\t);\n\t\tconst responseSessionId =\n\t\t\tqueryResponse.headers.get(\"x-querypanel-session-id\") ??\n\t\t\tquerypanelSessionId;\n\n\t\t// Handle pipeline errors from server\n\t\tif (!queryResponse.data.success) {\n\t\t\tthrow new QueryPipelineError(\n\t\t\t\tqueryResponse.data.error || \"Query generation failed\",\n\t\t\t\tqueryResponse.data.code || \"INTERNAL_ERROR\",\n\t\t\t\tqueryResponse.data.details,\n\t\t\t);\n\t\t}\n\n\t\tconst sql = queryResponse.data.sql;\n\t\tconst dialect = queryResponse.data.dialect;\n\t\tif (!sql || !dialect) {\n\t\t\tthrow new Error(\"Query response missing required SQL or dialect\");\n\t\t}\n\n\t\tif (queryResponse.data.queryId) {\n\t\t\tparentQueryId = queryResponse.data.queryId;\n\t\t}\n\n\t\tconst dbName =\n\t\t\tqueryResponse.data.database ??\n\t\t\toptions.database ??\n\t\t\tqueryEngine.getDefaultDatabase();\n\t\tif (!dbName) {\n\t\t\tthrow new Error(\n\t\t\t\t\"No database attached. Call attachPostgres, attachClickhouse, or attachBigQuery first.\",\n\t\t\t);\n\t\t}\n\n\t\t// Step 2: Map and validate parameters\n\t\tconst paramMetadata = Array.isArray(queryResponse.data.params)\n\t\t\t? queryResponse.data.params\n\t\t\t: [];\n\t\tconst paramValues = queryEngine.mapGeneratedParams(paramMetadata);\n\n\t\t// Step 3: Execute SQL with tenant isolation\n\t\ttry {\n\t\t\tconst execution = await queryEngine.validateAndExecute(\n\t\t\t\tsql,\n\t\t\t\tparamValues,\n\t\t\t\tdbName,\n\t\t\t\ttenantId,\n\t\t\t);\n\t\t\tconst rows = execution.rows ?? [];\n\n\t\t\tif (options.retryOnEmptyResult && attempt < maxRetry && isEmptyLikeResult(rows)) {\n\t\t\t\tattempt++;\n\t\t\t\tlastError = EMPTY_RESULT_ERROR;\n\t\t\t\tpreviousSql = sql;\n\t\t\t\tconsole.warn(\"Query returned no data, retrying query generation\", {\n\t\t\t\t\tattempt,\n\t\t\t\t\tmaxAttempts: maxRetry + 1,\n\t\t\t\t\tsql,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Step 4: Generate chart encoding even when the query returns no rows,\n\t\t\t// so headless clients can show \"No Data\" with the correct chart type.\n\t\t\tconst chartType = options.chartType ?? \"vega-lite\"; // Default to vega-lite for backward compatibility\n\t\t\tlet chart: ChartEnvelope = {\n\t\t\t\tspecType: chartType,\n\t\t\t\tnotes: rows.length === 0 ? \"Query returned no rows.\" : null,\n\t\t\t};\n\n\t\t\tif (chartType === \"vizspec\") {\n\t\t\t\t// Use new VizSpec generation\n\t\t\t\tconst vizspecResponse = await client.post<ServerVizSpecResponse>(\n\t\t\t\t\t\"/vizspec\",\n\t\t\t\t\t{\n\t\t\t\t\t\tquestion,\n\t\t\t\t\t\tsql,\n\t\t\t\t\t\trationale: queryResponse.data.rationale,\n\t\t\t\t\t\tfields: execution.fields,\n\t\t\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\t\t\tmax_retries: options.chartMaxRetries ?? 3,\n\t\t\t\t\t\tquery_id: queryResponse.data.queryId,\n\t\t\t\t\t\t...(options.supportedChartTypes?.length\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tsupported_chart_types: options.supportedChartTypes,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t},\n\t\t\t\t\ttenantId,\n\t\t\t\t\toptions.userId,\n\t\t\t\t\toptions.scopes,\n\t\t\t\t\tsignal,\n\t\t\t\t\tsessionId,\n\t\t\t\t);\n\t\t\t\tthrowIfGenerationErrorResponse(vizspecResponse);\n\n\t\t\t\tchart = {\n\t\t\t\t\tvizSpec: vizspecResponse.spec,\n\t\t\t\t\tspecType: \"vizspec\",\n\t\t\t\t\tnotes:\n\t\t\t\t\t\trows.length === 0\n\t\t\t\t\t\t\t? (vizspecResponse.notes ?? \"Query returned no rows.\")\n\t\t\t\t\t\t\t: vizspecResponse.notes,\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\t// Use traditional Vega-Lite chart generation\n\t\t\t\tconst chartResponse = await client.post<ServerChartResponse>(\n\t\t\t\t\t\"/chart\",\n\t\t\t\t\t{\n\t\t\t\t\t\tquestion,\n\t\t\t\t\t\tsql,\n\t\t\t\t\t\trationale: queryResponse.data.rationale,\n\t\t\t\t\t\tfields: execution.fields,\n\t\t\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\t\t\tmax_retries: options.chartMaxRetries ?? 3,\n\t\t\t\t\t\tquery_id: queryResponse.data.queryId,\n\t\t\t\t\t},\n\t\t\t\t\ttenantId,\n\t\t\t\t\toptions.userId,\n\t\t\t\t\toptions.scopes,\n\t\t\t\t\tsignal,\n\t\t\t\t\tsessionId,\n\t\t\t\t);\n\t\t\t\tthrowIfGenerationErrorResponse(chartResponse);\n\n\t\t\t\tchart = {\n\t\t\t\t\tvegaLiteSpec: chartResponse.chart\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t...chartResponse.chart,\n\t\t\t\t\t\t\t\tdata: { values: rows },\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: null,\n\t\t\t\t\tspecType: \"vega-lite\",\n\t\t\t\t\tnotes:\n\t\t\t\t\t\trows.length === 0\n\t\t\t\t\t\t\t? (chartResponse.notes ?? \"Query returned no rows.\")\n\t\t\t\t\t\t\t: chartResponse.notes,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsql,\n\t\t\t\tparams: paramValues,\n\t\t\t\tparamMetadata,\n\t\t\t\trationale: queryResponse.data.rationale,\n\t\t\t\tdialect,\n\t\t\t\tqueryId: queryResponse.data.queryId,\n\t\t\t\trows,\n\t\t\t\tfields: execution.fields,\n\t\t\t\tchart,\n\t\t\t\tcontext: queryResponse.data.context,\n\t\t\t\tattempts: attempt + 1,\n\t\t\t\ttarget_db: dbName,\n\t\t\t\tquerypanelSessionId: responseSessionId ?? undefined,\n\t\t\t\tintent: queryResponse.data.intent,\n\t\t\t\ttrace: queryResponse.data.trace,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (isAiProviderPipelineError(error)) {\n\t\t\t\tif (error instanceof QueryPipelineError) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tconst providerError = error as Error & { code: QueryErrorCode };\n\t\t\t\tthrow new QueryPipelineError(providerError.message, providerError.code);\n\t\t\t}\n\n\t\t\tattempt++;\n\n\t\t\t// If we've exhausted all retries, throw the error\n\t\t\tif (attempt > maxRetry) {\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\t// Save error and SQL for next retry\n\t\t\tlastError = error instanceof Error ? error.message : String(error);\n\t\t\tpreviousSql = queryResponse.data.sql ?? previousSql;\n\n\t\t\t// Log retry attempt\n\t\t\tconsole.warn(\"SQL execution failed, retrying query generation\", {\n\t\t\t\tattempt,\n\t\t\t\tmaxAttempts: maxRetry + 1,\n\t\t\t\terror: lastError,\n\t\t\t\tsql: queryResponse.data.sql ?? null,\n\t\t\t\tparamMetadata,\n\t\t\t\tparams: paramValues,\n\t\t\t});\n\t\t}\n\t}\n\n\t// This should never be reached, but TypeScript needs it\n\tthrow new Error(\"Unexpected error in ask retry loop\");\n}\n\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n\tconst resolved = tenantId ?? client.getDefaultTenantId();\n\tif (!resolved) {\n\t\tthrow new Error(\n\t\t\t\"tenantId is required. Provide it per request or via defaultTenantId option.\",\n\t\t);\n\t}\n\treturn resolved;\n}\n\nexport function anonymizeResults(\n\trows: Array<Record<string, unknown>>,\n): Array<Record<string, string>> {\n\tif (!rows?.length) return [];\n\treturn rows.map((row) => {\n\t\tconst masked: Record<string, string> = {};\n\t\tObject.entries(row).forEach(([key, value]) => {\n\t\t\tif (value === null) masked[key] = \"null\";\n\t\t\telse if (Array.isArray(value)) masked[key] = \"array\";\n\t\t\telse masked[key] = typeof value;\n\t\t});\n\t\treturn masked;\n\t});\n}\n","import type { IQueryPanelApi } from \"../core/api-types\";\nimport type { ParamRecord, QueryEngine } from \"../core/query-engine\";\n\n/**\n * A query previously generated by the server (`ask()` / `modifyChart()` return its `queryId`).\n */\nexport interface ResolvedQuery {\n\tqueryId: string;\n\tsql: string;\n\t/** Bind values keyed for the attached adapter (numeric + named keys). */\n\tparams: ParamRecord;\n\t/** Raw parameter metadata as stored by the server. */\n\tparamMetadata: Array<Record<string, unknown>>;\n\tdialect: string;\n}\n\ninterface ServerResolvedQuery {\n\tqueryId: string;\n\tsql: string;\n\tparams?: Array<Record<string, unknown>>;\n\tdialect: string;\n}\n\nexport interface ResolveQueryOptions {\n\ttenantId: string;\n\tuserId?: string;\n\tscopes?: string[];\n}\n\n/**\n * Loads the server-stored SQL and bind params for a `queryId`.\n *\n * The lookup is scoped by the authenticated organization and tenant on the server, so an\n * id from another tenant (or one that never existed) is rejected. Callers should use the\n * returned SQL/params and ignore any client-supplied values.\n */\nexport async function resolveQuery(\n\tclient: IQueryPanelApi,\n\tqueryEngine: QueryEngine,\n\tqueryId: string,\n\toptions: ResolveQueryOptions,\n\tsignal?: AbortSignal,\n): Promise<ResolvedQuery> {\n\tconst response = await client.get<ServerResolvedQuery>(\n\t\t`/v2/queries/${encodeURIComponent(queryId)}`,\n\t\toptions.tenantId,\n\t\toptions.userId,\n\t\toptions.scopes,\n\t\tsignal,\n\t);\n\n\tconst paramMetadata = Array.isArray(response.params) ? response.params : [];\n\treturn {\n\t\tqueryId: response.queryId,\n\t\tsql: response.sql,\n\t\tparams: queryEngine.mapGeneratedParams(paramMetadata),\n\t\tparamMetadata,\n\t\tdialect: response.dialect,\n\t};\n}\n\n/**\n * Mark a stored query as EXECUTED after local SQL succeeds.\n * Best-effort: never fails the caller. No-op when the row is already SUCCESS.\n */\nexport async function markQueryExecuted(\n\tclient: IQueryPanelApi,\n\tqueryId: string,\n\toptions: ResolveQueryOptions,\n\tsignal?: AbortSignal,\n): Promise<void> {\n\ttry {\n\t\tawait client.post(\n\t\t\t`/v2/queries/${encodeURIComponent(queryId)}/executed`,\n\t\t\t{},\n\t\t\toptions.tenantId,\n\t\t\toptions.userId,\n\t\t\toptions.scopes,\n\t\t\tsignal,\n\t\t);\n\t} catch {\n\t\t// Headless metrics must not break execution.\n\t}\n}\n","import type { IQueryPanelApi } from \"../core/api-types\";\n\n/**\n * A single session turn containing the question and optional SQL output.\n */\nexport interface SdkSessionTurn {\n\tid: string;\n\tsession_id: string;\n\tturn_index: number;\n\tquestion: string;\n\tsql: string | null;\n\trationale: string | null;\n\trow_count: number | null;\n\tfields: string[] | null;\n\terror: string | null;\n\tcreated_at: string;\n}\n\n/**\n * Session metadata with optional turn history.\n */\nexport interface SdkSession {\n\tid: string;\n\tsession_id: string;\n\torganization_id: string;\n\ttenant_id: string | null;\n\tuser_id: string | null;\n\ttitle: string | null;\n\tcreated_at: string;\n\tupdated_at: string;\n\tturns?: SdkSessionTurn[];\n}\n\n/**\n * Fields allowed when updating a session.\n */\nexport interface SessionUpdateInput {\n\ttitle?: string;\n}\n\n/**\n * Pagination settings for list endpoints.\n */\nexport interface PaginationQuery {\n\tpage?: number;\n\tlimit?: number;\n}\n\n/**\n * Pagination metadata returned by list endpoints.\n */\nexport interface PaginationInfo {\n\tpage: number;\n\tlimit: number;\n\ttotal: number;\n\ttotalPages: number;\n\thasNext: boolean;\n\thasPrev: boolean;\n}\n\n/**\n * Generic paginated response wrapper.\n */\nexport interface PaginatedResponse<T> {\n\tdata: T[];\n\tpagination: PaginationInfo;\n}\n\n/**\n * Options for listing sessions with filters and pagination.\n */\nexport interface SessionListOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n\tpagination?: PaginationQuery;\n\tsortBy?: \"title\" | \"user_id\" | \"created_at\" | \"updated_at\";\n\tsortDir?: \"asc\" | \"desc\";\n\ttitle?: string;\n\tuserFilter?: string;\n\tcreatedFrom?: string;\n\tcreatedTo?: string;\n\tupdatedFrom?: string;\n\tupdatedTo?: string;\n}\n\n/**\n * Options for retrieving a session.\n */\nexport interface SessionGetOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n\tincludeTurns?: boolean;\n}\n\ninterface RequestOptions {\n\ttenantId?: string;\n\tuserId?: string;\n\tscopes?: string[];\n}\n\n/**\n * Route module for Session history CRUD operations.\n */\n/**\n * Lists sessions with optional filtering and pagination.\n */\nexport async function listSessions(\n\tclient: IQueryPanelApi,\n\toptions?: SessionListOptions,\n\tsignal?: AbortSignal,\n): Promise<PaginatedResponse<SdkSession>> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\tif (options?.pagination?.page)\n\t\tparams.set(\"page\", `${options.pagination.page}`);\n\tif (options?.pagination?.limit)\n\t\tparams.set(\"limit\", `${options.pagination.limit}`);\n\tif (options?.sortBy) params.set(\"sort_by\", options.sortBy);\n\tif (options?.sortDir) params.set(\"sort_dir\", options.sortDir);\n\tif (options?.title) params.set(\"title\", options.title);\n\tif (options?.userFilter) params.set(\"user_id\", options.userFilter);\n\tif (options?.createdFrom) params.set(\"created_from\", options.createdFrom);\n\tif (options?.createdTo) params.set(\"created_to\", options.createdTo);\n\tif (options?.updatedFrom) params.set(\"updated_from\", options.updatedFrom);\n\tif (options?.updatedTo) params.set(\"updated_to\", options.updatedTo);\n\n\treturn await client.get<PaginatedResponse<SdkSession>>(\n\t\t`/sessions${params.toString() ? `?${params.toString()}` : \"\"}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Retrieves a session by session_id, optionally including turns.\n */\nexport async function getSession(\n\tclient: IQueryPanelApi,\n\tsessionId: string,\n\toptions?: SessionGetOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkSession> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tconst params = new URLSearchParams();\n\tif (options?.includeTurns !== undefined) {\n\t\tparams.set(\"include_turns\", `${options.includeTurns}`);\n\t}\n\n\treturn await client.get<SdkSession>(\n\t\t`/sessions/${encodeURIComponent(sessionId)}${params.toString() ? `?${params.toString()}` : \"\"}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Updates a session's metadata.\n */\nexport async function updateSession(\n\tclient: IQueryPanelApi,\n\tsessionId: string,\n\tbody: SessionUpdateInput,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<SdkSession> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\treturn await client.patch<SdkSession>(\n\t\t`/sessions/${encodeURIComponent(sessionId)}`,\n\t\tbody,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\n/**\n * Deletes a session and its turn history.\n */\nexport async function deleteSession(\n\tclient: IQueryPanelApi,\n\tsessionId: string,\n\toptions?: RequestOptions,\n\tsignal?: AbortSignal,\n): Promise<void> {\n\tconst tenantId = resolveTenantId(client, options?.tenantId);\n\tawait client.delete<void>(\n\t\t`/sessions/${encodeURIComponent(sessionId)}`,\n\t\ttenantId,\n\t\toptions?.userId,\n\t\toptions?.scopes,\n\t\tsignal,\n\t);\n}\n\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n\tconst resolved = tenantId ?? client.getDefaultTenantId();\n\tif (!resolved) {\n\t\tthrow new Error(\n\t\t\t\"tenantId is required. Provide it per request or via defaultTenantId option.\",\n\t\t);\n\t}\n\treturn resolved;\n}\n","import crypto from 'node:crypto';\nimport type { IQueryPanelApi } from \"../core/api-types\";\nimport type { ChartType, EncodingHints, VizSpec } from \"../types/vizspec\";\n\nexport interface VizSpecGenerateInput {\n question: string;\n sql: string;\n rationale?: string;\n fields: string[];\n rows: Array<Record<string, unknown>>;\n max_retries?: number;\n query_id?: string;\n /**\n * Optional encoding hints for visualization modification.\n * When provided, these guide the LLM to generate specific visualization configurations.\n */\n encoding_hints?: EncodingHints;\n /** When set, VizSpec kind \"chart\" may only use these chart types (API: supported_chart_types). */\n supported_chart_types?: ChartType[];\n}\n\nexport interface VizSpecGenerateOptions {\n tenantId?: string;\n userId?: string;\n scopes?: string[];\n maxRetries?: number;\n}\n\nexport interface VizSpecResponse {\n spec: VizSpec;\n notes: string | null;\n}\n\n/**\n * Route module for VizSpec generation\n * Calls the /vizspec endpoint to generate visualization specifications\n */\nexport async function generateVizSpec(\n client: IQueryPanelApi,\n input: VizSpecGenerateInput,\n options?: VizSpecGenerateOptions,\n signal?: AbortSignal,\n): Promise<VizSpecResponse> {\n const tenantId = resolveTenantId(client, options?.tenantId);\n const sessionId = crypto.randomUUID();\n\n const response = await client.post<VizSpecResponse>(\n \"/vizspec\",\n {\n question: input.question,\n sql: input.sql,\n rationale: input.rationale,\n fields: input.fields,\n rows: input.rows,\n max_retries: options?.maxRetries ?? input.max_retries ?? 3,\n query_id: input.query_id,\n encoding_hints: input.encoding_hints,\n ...(input.supported_chart_types?.length\n ? { supported_chart_types: input.supported_chart_types }\n : {}),\n },\n tenantId,\n options?.userId,\n options?.scopes,\n signal,\n sessionId,\n );\n\n return response;\n}\n\nfunction resolveTenantId(client: IQueryPanelApi, tenantId?: string): string {\n const resolved = tenantId ?? client.getDefaultTenantId();\n if (!resolved) {\n throw new Error(\n \"tenantId is required. Provide it per request or via defaultTenantId option.\",\n );\n }\n return resolved;\n}\n","/**\n * VizSpec types for flexible visualization specifications\n * Supports chart, table, and metric visualizations\n */\n\n// ============================================================================\n// Field Types and Formatting\n// ============================================================================\n\nexport type FieldType =\n | 'quantitative' // numbers you sum/avg\n | 'temporal' // dates/times\n | 'ordinal' // ordered categories (1st, 2nd, 3rd)\n | 'nominal' // unordered categories (names, IDs)\n | 'boolean';\n\nexport interface ValueFormat {\n style?: 'number' | 'currency' | 'percent' | 'date' | 'time' | 'datetime';\n currency?: string; // e.g. \"USD\"\n minimumFractionDigits?: number;\n maximumFractionDigits?: number;\n dateStyle?: 'short' | 'medium' | 'long';\n}\n\nexport interface FieldRef {\n field: string; // column name from the SQL result\n label?: string; // human-friendly label\n type?: FieldType;\n format?: ValueFormat; // optional formatting hint\n}\n\nexport type AggregateOp = 'sum' | 'avg' | 'min' | 'max' | 'count' | 'distinct';\n\nexport type TimeUnit = 'year' | 'quarter' | 'month' | 'week' | 'day' | 'hour' | 'minute';\n\nexport interface AxisField extends FieldRef {\n aggregate?: AggregateOp; // usually on y\n timeUnit?: TimeUnit; // usually on temporal x\n}\n\nexport interface MetricField extends FieldRef {\n aggregate?: AggregateOp;\n}\n\nexport interface SortSpec {\n field: string; // column to sort by\n direction?: 'asc' | 'desc'; // default \"asc\"\n}\n\n// ============================================================================\n// Base Spec\n// ============================================================================\n\nexport interface VizSpecBase {\n version: '1.0';\n kind: 'chart' | 'table' | 'metric' | 'map';\n title?: string;\n description?: string;\n data: {\n sourceId: string; // Identifier for the SQL result this spec refers to\n };\n}\n\nexport type VizSpecKind = VizSpecBase[\"kind\"];\n\n// ============================================================================\n// Chart Spec\n// ============================================================================\n\nexport type ChartType =\n | 'line'\n | 'bar' // horizontal bars (categorical on Y-axis)\n | 'column' // vertical bars (categorical on X-axis)\n | 'area'\n | 'scatter'\n | 'pie';\n\n/** Full VizSpec chart catalog; use when building a custom `supportedChartTypes` list. */\nexport const ALL_VIZ_CHART_TYPES: readonly ChartType[] = [\n 'line',\n 'bar',\n 'column',\n 'area',\n 'scatter',\n 'pie',\n] as const;\n\nexport type StackingMode = 'none' | 'stacked' | 'percent';\n\nexport interface ChartEncoding {\n chartType: ChartType;\n\n // Axes / measure fields\n x?: AxisField; // usually categorical or temporal\n y?: AxisField | AxisField[]; // one or more measures\n\n // Optional: split into multiple series (colors/lines)\n series?: FieldRef; // e.g. \"region\"\n\n // Optional: how to stack if multiple series on same axis\n stacking?: StackingMode;\n\n // Display-only concerns\n sort?: SortSpec; // order of categories / points\n limit?: number; // max rows to display\n\n // Tooltip fields to show on hover\n tooltips?: FieldRef[];\n}\n\nexport interface ChartSpec extends VizSpecBase {\n kind: 'chart';\n encoding: ChartEncoding;\n}\n\n// ============================================================================\n// Table Spec\n// ============================================================================\n\nexport type TextAlign = 'left' | 'right' | 'center';\n\nexport interface TableColumn extends FieldRef {\n width?: number; // optional, in px\n align?: TextAlign; // default: left; numeric often right\n isHidden?: boolean; // if true, don't render this column\n}\n\nexport interface TableEncoding {\n columns: TableColumn[];\n sort?: SortSpec;\n limit?: number; // max rows to show (e.g., top 50)\n}\n\nexport interface TableSpec extends VizSpecBase {\n kind: 'table';\n encoding: TableEncoding;\n}\n\n// ============================================================================\n// Metric Spec\n// ============================================================================\n\nexport type ComparisonMode = 'delta' | 'deltaPercent' | 'ratio';\n\nexport interface MetricTrend {\n timeField: AxisField; // e.g. \"date\"\n valueField: MetricField; // value over time for sparkline\n}\n\nexport interface MetricEncoding {\n valueField: MetricField; // main KPI\n\n // optional comparison (e.g. vs previous period)\n comparisonField?: MetricField;\n comparisonMode?: ComparisonMode; // default could be \"deltaPercent\"\n\n // optional tiny timeseries/sparkline\n trend?: MetricTrend;\n}\n\nexport interface MetricSpec extends VizSpecBase {\n kind: 'metric';\n encoding: MetricEncoding;\n}\n\n// ============================================================================\n// Map Spec\n// ============================================================================\n\nexport type MapLocationMode = 'place' | 'latlng';\n\nexport interface MapLocation {\n mode: MapLocationMode;\n postalCodeField?: string | null; // mode=place: postal code column (tried first)\n cityField?: string | null; // mode=place: city/town column (fallback)\n latField?: string | null; // mode=latlng\n lngField?: string | null; // mode=latlng\n}\n\nexport interface MapEncoding {\n location: MapLocation;\n country: string; // ISO 3166-1 alpha-2, e.g. \"HU\"\n colorField?: string | null; // category column, e.g. status\n labelField?: string | null; // device name/id shown in the click list\n tooltips?: string[] | null;\n}\n\nexport interface MapSpec extends VizSpecBase {\n kind: 'map';\n encoding: MapEncoding;\n}\n\n// ============================================================================\n// Union Type\n// ============================================================================\n\nexport type VizSpec = ChartSpec | TableSpec | MetricSpec | MapSpec;\n\n// ============================================================================\n// Service Input/Output Types\n// ============================================================================\n\n/**\n * Encoding hints for vizspec modifications.\n * These hints guide the LLM to generate specific visualization configurations.\n */\nexport interface EncodingHints {\n /** Preferred vizspec kind. A map is chosen by the question (\"show them on a map\"), not by a hint. */\n kind?: 'chart' | 'table' | 'metric';\n /** Preferred chart type (bar, line, area, scatter, pie) */\n chartType?: ChartType;\n /** X axis field configuration */\n xAxis?: AxisField;\n /** Y axis field configuration (single or multiple) */\n yAxis?: AxisField | AxisField[];\n /** Series/color field for multi-series charts */\n series?: FieldRef;\n /** Stacking mode for multi-series */\n stacking?: StackingMode;\n /** Maximum rows to display */\n limit?: number;\n}\n\n/**\n * Input for vizspec generator service\n */\nexport interface VizSpecGeneratorInput {\n question: string;\n sql: string;\n rationale?: string;\n fields: string[];\n rows: Array<Record<string, unknown>>;\n maxRetries?: number;\n queryId?: string;\n /**\n * Optional encoding hints for visualization modification.\n * When provided, these guide the LLM to generate specific visualization configurations.\n */\n encodingHints?: EncodingHints;\n}\n\n/**\n * Output from vizspec generator service\n */\nexport interface VizSpecResult {\n spec: VizSpec;\n notes: string | null;\n}\n"],"mappings":"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","names":["normalizeTableFilter","asTableType","normalizeTableFilter","tableKey","asTableType","buildTablesQuery","buildColumnsQuery","sanitize","crypto","resolveTenantId","resolveTenantId","import_node_crypto","resolveTenantId","crypto","import_node_crypto","import_node_crypto","resolveTenantId","crypto","resolveTenantId","crypto","resolveTenantId","import_node_crypto","resolveTenantId","crypto"]}
|