@querypanel/node-sdk 1.0.46 → 1.0.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/clickhouse.ts","../src/adapters/clickhouse.ts","../src/adapters/postgres.ts","../src/core/client.ts","../src/core/query-engine.ts","../src/errors.ts","../src/routes/charts.ts","../src/routes/active-charts.ts","../src/routes/ingest.ts","../src/routes/modify.ts","../src/routes/query.ts","../src/routes/sessions.ts","../src/routes/vizspec.ts","../src/index.ts"],"sourcesContent":["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 type { DatabaseAdapter, DatabaseExecutionResult } 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 */\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(options.allowedTables);\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)\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\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/(?: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 table = match[1]?.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\nfunction normalizeTableFilter(tables?: string[] | null): 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\tconst parts = trimmed.split(\".\");\n\t\tconst tableName = parts[parts.length - 1];\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 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 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 type { DatabaseAdapter, DatabaseExecutionResult } 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 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 \"postgres\". */\n\tkind?: SchemaIntrospection[\"db\"][\"kind\"];\n\t/**\n\t * Optional allow-list of table names (schema-qualified or bare).\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\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\ninterface NormalizedTable {\n\tschema: string;\n\ttable: string;\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 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.kind = options.kind ?? \"postgres\";\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\t// Convert named params to positional array for PostgreSQL\n\t\tlet paramArray: unknown[] | undefined;\n\t\tif (params) {\n\t\t\tparamArray = this.convertNamedToPositionalParams(params);\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\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)) {\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 named params to positional array for PostgreSQL\n\t * PostgreSQL expects $1, $2, $3 in SQL and an array of values [val1, val2, val3]\n\t */\n\tprivate convertNamedToPositionalParams(\n\t\tparams: Record<string, string | number | boolean | string[] | number[]>,\n\t): unknown[] {\n\t\t// Separate numeric and named keys\n\t\tconst numericKeys = Object.keys(params)\n\t\t\t.filter((k) => /^\\d+$/.test(k))\n\t\t\t.map((k) => Number.parseInt(k, 10))\n\t\t\t.sort((a, b) => a - b);\n\n\t\tconst namedKeys = Object.keys(params)\n\t\t\t.filter((k) => !/^\\d+$/.test(k))\n\t\t\t.sort(); // Alphabetical order for consistency\n\n\t\t// Build positional array\n\t\tconst positionalParams: unknown[] = [];\n\n\t\t// First, add values from numeric keys (in sorted order)\n\t\tfor (const key of numericKeys) {\n\t\t\tlet val: unknown = params[String(key)];\n\t\t\tif (typeof val === \"string\") {\n\t\t\t\t// Resolve placeholder tokens like `<tenant_id>` to their named values\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\t\t\tpositionalParams.push(val);\n\t\t}\n\n\t\t// Then, add values from named keys (in alphabetical order)\n\t\tfor (const key of namedKeys) {\n\t\t\tconst val = params[key];\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\tlet paramArray: unknown[] | undefined;\n\t\tif (params) {\n\t\t\tparamArray = this.convertNamedToPositionalParams(params);\n\t\t}\n\n\t\tawait this.clientFn(`EXPLAIN ${sql}`, paramArray);\n\t}\n\n\tgetDialect() {\n\t\treturn \"postgres\" 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.defaultSchema)\n\t\t\t: this.allowedTables;\n\t\tconst normalizedTables = tablesToIntrospect ?? [];\n\n\t\tconst tablesResult = await this.clientFn(\n\t\t\tbuildTablesQuery(normalizedTables),\n\t\t);\n\t\tconst tableRows = tablesResult.rows as TableRow[];\n\n\t\tconst columnsResult = await this.clientFn(\n\t\t\tbuildColumnsQuery(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 (!isSafeIdentifier(schema) || !isSafeIdentifier(table)) {\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_class c\n ON c.relname = cols.table_name\n AND c.relkind IN ('r', 'v', 'm')\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\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\nfunction buildFilterClause(\n\ttables: NormalizedTable[],\n\tschemaExpr: string,\n\ttableExpr: string,\n): string {\n\tif (!tables.length) return \"\";\n\tconst clauses = tables.map(({ schema, table }) => {\n\t\treturn `(${schemaExpr} = '${schema}' AND ${tableExpr} = '${table}')`;\n\t});\n\treturn `AND (${clauses.join(\" OR \")})`;\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): TableSchema[\"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';\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\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: typeof fetch;\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?: typeof fetch;\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\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 error = new Error(\n\t\t\t\tjson?.error || response.statusText || \"Request failed\",\n\t\t\t);\n\t\t\t(error as any).status = response.status;\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","import type { DatabaseAdapter, DatabaseDialect } from \"../adapters/types\";\n\nexport type ParamValue = string | number | boolean | string[] | number[];\nexport type ParamRecord = Record<string, ParamValue>;\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\tmapGeneratedParams(params: Array<Record<string, unknown>>): ParamRecord {\n\t\tconst record: ParamRecord = {};\n\n\t\tparams.forEach((param, index) => {\n\t\t\tconst value = param.value as ParamValue | undefined;\n\t\t\tif (value === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst nameCandidate =\n\t\t\t\t(typeof param.name === \"string\" && param.name.trim()) ||\n\t\t\t\t(typeof param.placeholder === \"string\" && param.placeholder.trim()) ||\n\t\t\t\t(typeof param.position === \"number\" && String(param.position)) ||\n\t\t\t\tString(index + 1);\n\t\t\tconst key = nameCandidate.replace(/[{}:$]/g, \"\").trim();\n\t\t\trecord[key] = value;\n\t\t});\n\n\t\treturn record;\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 {\n\t\t\t// Postgres (and others): Use literal to avoid modifying 'params' object.\n\t\t\t// Modifying 'params' can break positional parameter mapping (e.g. $1, $2)\n\t\t\t// because PostgresAdapter sorts named keys alphabetically to assign positions.\n\t\t\tconst escapedId = tenantId.replace(/'/g, \"''\");\n\t\t\ttenantPredicate = `${tenantField} = '${escapedId}'`;\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","/**\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// 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\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) {\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","import type { ApiClient } from \"../core/client\";\nimport type { ParamRecord, QueryEngine } from \"../core/query-engine\";\n\nexport interface SdkChart {\n\tid: string;\n\ttitle: string;\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\tdescription?: string;\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\tquery_id?: string;\n\ttarget_db?: string;\n}\n\nexport interface ChartUpdateInput {\n\ttitle?: string;\n\tdescription?: string;\n\tsql?: string;\n\tsql_params?: Record<string, unknown>;\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\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: ApiClient,\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: ApiClient,\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 getChart(\n\tclient: ApiClient,\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: ApiClient,\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: ApiClient,\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: ApiClient, 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 { ApiClient } from \"../core/client\";\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\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: ApiClient,\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: ApiClient,\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(\"name\", 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 getActiveChart(\n\tclient: ApiClient,\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: ApiClient,\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: ApiClient,\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: ApiClient, 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 { ApiClient } from \"../core/client\";\nimport type { QueryEngine } from \"../core/query-engine\";\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\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: ApiClient,\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\nfunction resolveTenantId(client: ApiClient, 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\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 (\n\t\tmetadata?.tenantFieldName &&\n\t\tmetadata?.tenantFieldType &&\n\t\tmetadata?.enforceTenantIsolation !== undefined\n\t) {\n\t\trequest.tenant_settings = {\n\t\t\ttenantFieldName: metadata.tenantFieldName,\n\t\t\ttenantFieldType: metadata.tenantFieldType,\n\t\t\tenforceTenantIsolation: metadata.enforceTenantIsolation,\n\t\t};\n\t}\n\n\treturn request;\n}\n","import crypto from \"node:crypto\";\nimport type { ApiClient } from \"../core/client\";\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} from \"../types/vizspec\";\nimport {\n\ttype AskResponse,\n\tanonymizeResults,\n\ttype ChartEnvelope,\n} from \"./query\";\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 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 * The SQL query to modify or re-execute.\n\t * From ask() response: response.sql\n\t * From saved chart: chart.sql\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}\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): 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 parts: string[] = [];\n\t\tif (modifications.dateRange.from) {\n\t\t\tparts.push(`from ${modifications.dateRange.from}`);\n\t\t}\n\t\tif (modifications.dateRange.to) {\n\t\t\tparts.push(`to ${modifications.dateRange.to}`);\n\t\t}\n\t\tif (parts.length > 0) {\n\t\t\thints.push(`filter date range ${parts.join(\" \")}`);\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\treturn `${originalQuestion} (${hints.join(\", \")})`;\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.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 * Resolves tenant ID from options or client default.\n */\nfunction resolveTenantId(client: ApiClient, 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: ApiClient,\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 chartType = options?.chartType ?? \"vega-lite\";\n\n\tconst hasSqlMods = !!input.sqlModifications;\n\tconst hasVizMods = !!input.vizModifications;\n\tconst hasCustomSql = !!input.sqlModifications?.customSql;\n\n\t// Determine which SQL to use\n\tlet finalSql = 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\tlet sqlChanged = false;\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?.tenantFieldName) {\n\t\ttenantSettings = {\n\t\t\ttenantFieldName: metadata.tenantFieldName,\n\t\t\ttenantFieldType: metadata.tenantFieldType,\n\t\t\tenforceTenantIsolation: metadata.enforceTenantIsolation,\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\tsqlChanged = true;\n\t}\n\t// Path 2: SQL modifications (non-custom) - regenerate via query endpoint\n\telse if (hasSqlMods && !hasCustomSql) {\n\t\tconst modifiedQuestion = buildModifiedQuestion(\n\t\t\tinput.question,\n\t\t\tinput.sqlModifications!,\n\t\t);\n\n\t\tconst queryResponse = await client.post<ServerQueryResponse>(\n\t\t\t\"/query\",\n\t\t\t{\n\t\t\t\tquestion: modifiedQuestion,\n\t\t\t\tprevious_sql: input.sql,\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},\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\trationale = queryResponse.rationale;\n\t\tqueryId = queryResponse.queryId;\n\t\tsqlChanged = finalSql !== input.sql;\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\n\tlet chart: ChartEnvelope = {\n\t\tspecType: chartType,\n\t\tnotes: rows.length === 0 ? \"Query returned no rows.\" : null,\n\t};\n\n\tif (rows.length > 0) {\n\t\t// Build viz hints if modifications provided\n\t\tconst vizHints = hasVizMods ? buildVizHints(input.vizModifications!) : {};\n\n\t\tif (chartType === \"vizspec\") {\n\t\t\tconst vizspecResponse = await client.post<ServerVizSpecResponse>(\n\t\t\t\t\"/vizspec\",\n\t\t\t\t{\n\t\t\t\t\tquestion: input.question,\n\t\t\t\t\tsql: finalSql,\n\t\t\t\t\trationale,\n\t\t\t\t\tfields: execution.fields,\n\t\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\t\tmax_retries: options?.chartMaxRetries ?? 3,\n\t\t\t\t\tquery_id: queryId,\n\t\t\t\t\t// Include viz hints for the chart generator\n\t\t\t\t\t...(hasVizMods ? { encoding_hints: vizHints } : {}),\n\t\t\t\t},\n\t\t\t\ttenantId,\n\t\t\t\toptions?.userId,\n\t\t\t\toptions?.scopes,\n\t\t\t\tsignal,\n\t\t\t\tsessionId,\n\t\t\t);\n\n\t\t\tchart = {\n\t\t\t\tvizSpec: vizspecResponse.spec,\n\t\t\t\tspecType: \"vizspec\",\n\t\t\t\tnotes: vizspecResponse.notes,\n\t\t\t};\n\t\t} else {\n\t\t\tconst chartResponse = await client.post<ServerChartResponse>(\n\t\t\t\t\"/chart\",\n\t\t\t\t{\n\t\t\t\t\tquestion: input.question,\n\t\t\t\t\tsql: finalSql,\n\t\t\t\t\trationale,\n\t\t\t\t\tfields: execution.fields,\n\t\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\t\tmax_retries: options?.chartMaxRetries ?? 3,\n\t\t\t\t\tquery_id: queryId,\n\t\t\t\t\t// Include viz hints for the chart generator\n\t\t\t\t\t...(hasVizMods ? { encoding_hints: vizHints } : {}),\n\t\t\t\t},\n\t\t\t\ttenantId,\n\t\t\t\toptions?.userId,\n\t\t\t\toptions?.scopes,\n\t\t\t\tsignal,\n\t\t\t\tsessionId,\n\t\t\t);\n\n\t\t\tchart = {\n\t\t\t\tvegaLiteSpec: chartResponse.chart\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...chartResponse.chart,\n\t\t\t\t\t\t\tdata: { values: rows },\n\t\t\t\t\t\t}\n\t\t\t\t\t: null,\n\t\t\t\tspecType: \"vega-lite\",\n\t\t\t\tnotes: chartResponse.notes,\n\t\t\t};\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\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 { ApiClient } from \"../core/client\";\nimport type { ParamRecord, QueryEngine } from \"../core/query-engine\";\nimport { type QueryErrorCode, QueryPipelineError } from \"../errors\";\nimport type { 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}\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}\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// 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 * Route module for natural language query generation\n * Simple orchestration following Ousterhout's principle\n */\nexport async function ask(\n\tclient: ApiClient,\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\n\twhile (attempt <= maxRetry) {\n\t\t// Step 1: Get SQL from backend\n\t\tconsole.log({ lastError, previousSql });\n\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\t// Include tenant settings if available in metadata\n\t\tlet tenantSettings: Record<string, unknown> | undefined;\n\t\tif (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: metadata.enforceTenantIsolation,\n\t\t\t};\n\t\t}\n\n\t\tconst queryResponse = await client.postWithHeaders<ServerQueryResponse>(\n\t\t\t\"/query\",\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},\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\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 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 if we have data\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 (rows.length > 0) {\n\t\t\t\tif (chartType === \"vizspec\") {\n\t\t\t\t\t// Use new VizSpec generation\n\t\t\t\t\tconst vizspecResponse = await client.post<ServerVizSpecResponse>(\n\t\t\t\t\t\t\"/vizspec\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tquestion,\n\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\trationale: queryResponse.data.rationale,\n\t\t\t\t\t\t\tfields: execution.fields,\n\t\t\t\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\t\t\t\tmax_retries: options.chartMaxRetries ?? 3,\n\t\t\t\t\t\t\tquery_id: queryResponse.data.queryId,\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttenantId,\n\t\t\t\t\t\toptions.userId,\n\t\t\t\t\t\toptions.scopes,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t);\n\n\t\t\t\t\tchart = {\n\t\t\t\t\t\tvizSpec: vizspecResponse.spec,\n\t\t\t\t\t\tspecType: \"vizspec\",\n\t\t\t\t\t\tnotes: vizspecResponse.notes,\n\t\t\t\t\t};\n\t\t\t\t} else {\n\t\t\t\t\t// Use traditional Vega-Lite chart generation\n\t\t\t\t\tconst chartResponse = await client.post<ServerChartResponse>(\n\t\t\t\t\t\t\"/chart\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tquestion,\n\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\trationale: queryResponse.data.rationale,\n\t\t\t\t\t\t\tfields: execution.fields,\n\t\t\t\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\t\t\t\tmax_retries: options.chartMaxRetries ?? 3,\n\t\t\t\t\t\t\tquery_id: queryResponse.data.queryId,\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttenantId,\n\t\t\t\t\t\toptions.userId,\n\t\t\t\t\t\toptions.scopes,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t);\n\n\t\t\t\t\tchart = {\n\t\t\t\t\t\tvegaLiteSpec: chartResponse.chart\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t...chartResponse.chart,\n\t\t\t\t\t\t\t\t\tdata: { values: rows },\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\tspecType: \"vega-lite\",\n\t\t\t\t\t\tnotes: chartResponse.notes,\n\t\t\t\t\t};\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};\n\t\t} catch (error) {\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(\n\t\t\t\t`SQL execution failed (attempt ${attempt}/${maxRetry + 1}): ${lastError}. Retrying...`,\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: ApiClient, 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 { ApiClient } from \"../core/client\";\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: ApiClient,\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: ApiClient,\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: ApiClient,\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: ApiClient,\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: ApiClient, 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 { ApiClient } from \"../core/client\";\nimport type { 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\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: ApiClient,\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 },\n tenantId,\n options?.userId,\n options?.scopes,\n signal,\n sessionId,\n );\n\n return response;\n}\n\nfunction resolveTenantId(client: ApiClient, 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","import {\n\tClickHouseAdapter,\n\ttype ClickHouseAdapterOptions,\n\ttype ClickHouseClientFn,\n} from \"./adapters/clickhouse\";\nimport {\n\tPostgresAdapter,\n\ttype PostgresAdapterOptions,\n\ttype PostgresClientFn,\n} from \"./adapters/postgres\";\nimport type { DatabaseAdapter, DatabaseDialect } from \"./adapters/types\";\nimport { ApiClient } from \"./core/client\";\nimport { type DatabaseMetadata, QueryEngine } from \"./core/query-engine\";\nimport { QueryErrorCode, QueryPipelineError } from \"./errors\";\nimport * as activeChartsRoute from \"./routes/active-charts\";\nimport * as chartsRoute from \"./routes/charts\";\nimport * as ingestRoute from \"./routes/ingest\";\nimport * as modifyRoute from \"./routes/modify\";\nimport * as queryRoute from \"./routes/query\";\nimport * as sessionsRoute from \"./routes/sessions\";\nimport * as vizspecRoute from \"./routes/vizspec\";\nimport type { SchemaIntrospection } from \"./schema/types\";\n\n// Re-export all public types\nexport { ClickHouseAdapter, PostgresAdapter };\n\n// Re-export error types\nexport type { QueryErrorCode as QueryErrorCodeType } from \"./errors\";\nexport { QueryErrorCode, QueryPipelineError };\n\nexport type {\n\tClickHouseAdapterOptions,\n\tClickHouseClientFn,\n\tDatabaseAdapter,\n\tDatabaseDialect,\n\tPostgresAdapterOptions,\n\tPostgresClientFn,\n\tSchemaIntrospection,\n};\n\n// Re-export from query-engine\nexport type { ParamRecord, ParamValue } from \"./core/query-engine\";\nexport type {\n\tActiveChartCreateInput,\n\tActiveChartListOptions,\n\tActiveChartUpdateInput,\n\tSdkActiveChart,\n} from \"./routes/active-charts\";\n\nexport type {\n\tChartCreateInput,\n\tChartListOptions,\n\tChartUpdateInput,\n\tPaginatedResponse,\n\tPaginationInfo,\n\tPaginationQuery,\n\tSdkChart,\n} from \"./routes/charts\";\n// Re-export route types\nexport type {\n\tIngestResponse,\n\tSchemaSyncOptions,\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} from \"./routes/query\";\n// Re-export anonymizeResults utility\nexport { anonymizeResults } 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 type {\n\tAggregateOp,\n\tAxisField,\n\tChartEncoding,\n\tChartSpec,\n\tChartType,\n\tFieldRef,\n\tFieldType,\n\tMetricEncoding,\n\tMetricField,\n\tMetricSpec,\n\tStackingMode,\n\tTableColumn,\n\tTableEncoding,\n\tTableSpec,\n\tTimeUnit,\n\tVizSpec,\n} from \"./types/vizspec\";\n\n/**\n * Main SDK class - Thin orchestrator\n * Delegates to deep modules (ApiClient, QueryEngine, route modules)\n * Following Ousterhout's principle: \"Simple interface hiding complexity\"\n */\nexport class QueryPanelSdkAPI {\n\tprivate readonly client: ApiClient;\n\tprivate readonly queryEngine: QueryEngine;\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?: typeof fetch;\n\t\t},\n\t) {\n\t\tthis.client = new ApiClient(baseUrl, privateKey, organizationId, options);\n\t\tthis.queryEngine = new QueryEngine();\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: options?.tenantFieldName\n\t\t\t\t? (options?.enforceTenantIsolation ?? true)\n\t\t\t\t: undefined,\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: \"postgres\",\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: options?.tenantFieldName\n\t\t\t\t? (options?.enforceTenantIsolation ?? true)\n\t\t\t\t: undefined,\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 * 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// 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, and retry settings\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\toptions,\n\t\t\tsignal,\n\t\t);\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\tinput,\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\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// 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 * 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"],"mappings":"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","names":["normalizeTableFilter","asTableType","sanitize","resolveTenantId","crypto","resolveTenantId","crypto","crypto","resolveTenantId","crypto","resolveTenantId","crypto","resolveTenantId","crypto","resolveTenantId"]}
1
+ {"version":3,"sources":["../src/utils/clickhouse.ts","../src/adapters/clickhouse.ts","../src/adapters/postgres.ts","../src/core/client.ts","../src/core/query-engine.ts","../src/errors.ts","../src/routes/charts.ts","../src/routes/active-charts.ts","../src/routes/ingest.ts","../src/routes/modify.ts","../src/routes/query.ts","../src/routes/sessions.ts","../src/routes/vizspec.ts","../src/index.ts"],"sourcesContent":["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 type { DatabaseAdapter, DatabaseExecutionResult } 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 */\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(options.allowedTables);\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)\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\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/(?: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 table = match[1]?.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\nfunction normalizeTableFilter(tables?: string[] | null): 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\tconst parts = trimmed.split(\".\");\n\t\tconst tableName = parts[parts.length - 1];\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 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 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 type { DatabaseAdapter, DatabaseExecutionResult } 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 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 \"postgres\". */\n\tkind?: SchemaIntrospection[\"db\"][\"kind\"];\n\t/**\n\t * Optional allow-list of table names (schema-qualified or bare).\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\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\ninterface NormalizedTable {\n\tschema: string;\n\ttable: string;\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 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.kind = options.kind ?? \"postgres\";\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\t// Convert named params to positional array for PostgreSQL\n\t\tlet paramArray: unknown[] | undefined;\n\t\tif (params) {\n\t\t\tparamArray = this.convertNamedToPositionalParams(params);\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\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)) {\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 named params to positional array for PostgreSQL\n\t * PostgreSQL expects $1, $2, $3 in SQL and an array of values [val1, val2, val3]\n\t */\n\tprivate convertNamedToPositionalParams(\n\t\tparams: Record<string, string | number | boolean | string[] | number[]>,\n\t): unknown[] {\n\t\t// Separate numeric and named keys\n\t\tconst numericKeys = Object.keys(params)\n\t\t\t.filter((k) => /^\\d+$/.test(k))\n\t\t\t.map((k) => Number.parseInt(k, 10))\n\t\t\t.sort((a, b) => a - b);\n\n\t\tconst namedKeys = Object.keys(params)\n\t\t\t.filter((k) => !/^\\d+$/.test(k))\n\t\t\t.sort(); // Alphabetical order for consistency\n\n\t\t// Build positional array\n\t\tconst positionalParams: unknown[] = [];\n\n\t\t// First, add values from numeric keys (in sorted order)\n\t\tfor (const key of numericKeys) {\n\t\t\tlet val: unknown = params[String(key)];\n\t\t\tif (typeof val === \"string\") {\n\t\t\t\t// Resolve placeholder tokens like `<tenant_id>` to their named values\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\t\t\tpositionalParams.push(val);\n\t\t}\n\n\t\t// Then, add values from named keys (in alphabetical order)\n\t\tfor (const key of namedKeys) {\n\t\t\tconst val = params[key];\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\tlet paramArray: unknown[] | undefined;\n\t\tif (params) {\n\t\t\tparamArray = this.convertNamedToPositionalParams(params);\n\t\t}\n\n\t\tawait this.clientFn(`EXPLAIN ${sql}`, paramArray);\n\t}\n\n\tgetDialect() {\n\t\treturn \"postgres\" 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.defaultSchema)\n\t\t\t: this.allowedTables;\n\t\tconst normalizedTables = tablesToIntrospect ?? [];\n\n\t\tconst tablesResult = await this.clientFn(\n\t\t\tbuildTablesQuery(normalizedTables),\n\t\t);\n\t\tconst tableRows = tablesResult.rows as TableRow[];\n\n\t\tconst columnsResult = await this.clientFn(\n\t\t\tbuildColumnsQuery(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 (!isSafeIdentifier(schema) || !isSafeIdentifier(table)) {\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_class c\n ON c.relname = cols.table_name\n AND c.relkind IN ('r', 'v', 'm')\n LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\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\nfunction buildFilterClause(\n\ttables: NormalizedTable[],\n\tschemaExpr: string,\n\ttableExpr: string,\n): string {\n\tif (!tables.length) return \"\";\n\tconst clauses = tables.map(({ schema, table }) => {\n\t\treturn `(${schemaExpr} = '${schema}' AND ${tableExpr} = '${table}')`;\n\t});\n\treturn `AND (${clauses.join(\" OR \")})`;\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): TableSchema[\"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';\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\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: typeof fetch;\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?: typeof fetch;\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\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 error = new Error(\n\t\t\t\tjson?.error || response.statusText || \"Request failed\",\n\t\t\t);\n\t\t\t(error as any).status = response.status;\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","import type { DatabaseAdapter, DatabaseDialect } from \"../adapters/types\";\n\nexport type ParamValue = string | number | boolean | string[] | number[];\nexport type ParamRecord = Record<string, ParamValue>;\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\tmapGeneratedParams(params: Array<Record<string, unknown>>): ParamRecord {\n\t\tconst record: ParamRecord = {};\n\n\t\tparams.forEach((param, index) => {\n\t\t\tconst value = param.value as ParamValue | undefined;\n\t\t\tif (value === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst nameCandidate =\n\t\t\t\t(typeof param.name === \"string\" && param.name.trim()) ||\n\t\t\t\t(typeof param.placeholder === \"string\" && param.placeholder.trim()) ||\n\t\t\t\t(typeof param.position === \"number\" && String(param.position)) ||\n\t\t\t\tString(index + 1);\n\t\t\tconst key = nameCandidate.replace(/[{}:$]/g, \"\").trim();\n\t\t\trecord[key] = value;\n\t\t});\n\n\t\treturn record;\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 {\n\t\t\t// Postgres (and others): Use literal to avoid modifying 'params' object.\n\t\t\t// Modifying 'params' can break positional parameter mapping (e.g. $1, $2)\n\t\t\t// because PostgresAdapter sorts named keys alphabetically to assign positions.\n\t\t\tconst escapedId = tenantId.replace(/'/g, \"''\");\n\t\t\ttenantPredicate = `${tenantField} = '${escapedId}'`;\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","/**\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// 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\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) {\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","import type { ApiClient } from \"../core/client\";\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\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\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\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\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: ApiClient,\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: ApiClient,\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 getChart(\n\tclient: ApiClient,\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: ApiClient,\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: ApiClient,\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: ApiClient, 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 { ApiClient } from \"../core/client\";\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\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: ApiClient,\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: ApiClient,\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(\"name\", 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 getActiveChart(\n\tclient: ApiClient,\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: ApiClient,\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: ApiClient,\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: ApiClient, 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 { ApiClient } from \"../core/client\";\nimport type { QueryEngine } from \"../core/query-engine\";\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\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: ApiClient,\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\nfunction resolveTenantId(client: ApiClient, 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\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 (\n\t\tmetadata?.tenantFieldName &&\n\t\tmetadata?.tenantFieldType &&\n\t\tmetadata?.enforceTenantIsolation !== undefined\n\t) {\n\t\trequest.tenant_settings = {\n\t\t\ttenantFieldName: metadata.tenantFieldName,\n\t\t\ttenantFieldType: metadata.tenantFieldType,\n\t\t\tenforceTenantIsolation: metadata.enforceTenantIsolation,\n\t\t};\n\t}\n\n\treturn request;\n}\n","import crypto from \"node:crypto\";\nimport type { ApiClient } from \"../core/client\";\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} from \"../types/vizspec\";\nimport {\n\ttype AskResponse,\n\tanonymizeResults,\n\ttype ChartEnvelope,\n} from \"./query\";\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 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 * The SQL query to modify or re-execute.\n\t * From ask() response: response.sql\n\t * From saved chart: chart.sql\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}\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): 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 parts: string[] = [];\n\t\tif (modifications.dateRange.from) {\n\t\t\tparts.push(`from ${modifications.dateRange.from}`);\n\t\t}\n\t\tif (modifications.dateRange.to) {\n\t\t\tparts.push(`to ${modifications.dateRange.to}`);\n\t\t}\n\t\tif (parts.length > 0) {\n\t\t\thints.push(`filter date range ${parts.join(\" \")}`);\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\treturn `${originalQuestion} (${hints.join(\", \")})`;\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.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 * Resolves tenant ID from options or client default.\n */\nfunction resolveTenantId(client: ApiClient, 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: ApiClient,\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 chartType = options?.chartType ?? \"vega-lite\";\n\n\tconst hasSqlMods = !!input.sqlModifications;\n\tconst hasVizMods = !!input.vizModifications;\n\tconst hasCustomSql = !!input.sqlModifications?.customSql;\n\n\t// Determine which SQL to use\n\tlet finalSql = 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\tlet sqlChanged = false;\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?.tenantFieldName) {\n\t\ttenantSettings = {\n\t\t\ttenantFieldName: metadata.tenantFieldName,\n\t\t\ttenantFieldType: metadata.tenantFieldType,\n\t\t\tenforceTenantIsolation: metadata.enforceTenantIsolation,\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\tsqlChanged = true;\n\t}\n\t// Path 2: SQL modifications (non-custom) - regenerate via query endpoint\n\telse if (hasSqlMods && !hasCustomSql) {\n\t\tconst modifiedQuestion = buildModifiedQuestion(\n\t\t\tinput.question,\n\t\t\tinput.sqlModifications!,\n\t\t);\n\n\t\tconst queryResponse = await client.post<ServerQueryResponse>(\n\t\t\t\"/query\",\n\t\t\t{\n\t\t\t\tquestion: modifiedQuestion,\n\t\t\t\tprevious_sql: input.sql,\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},\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\trationale = queryResponse.rationale;\n\t\tqueryId = queryResponse.queryId;\n\t\tsqlChanged = finalSql !== input.sql;\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\n\tlet chart: ChartEnvelope = {\n\t\tspecType: chartType,\n\t\tnotes: rows.length === 0 ? \"Query returned no rows.\" : null,\n\t};\n\n\tif (rows.length > 0) {\n\t\t// Build viz hints if modifications provided\n\t\tconst vizHints = hasVizMods ? buildVizHints(input.vizModifications!) : {};\n\n\t\tif (chartType === \"vizspec\") {\n\t\t\tconst vizspecResponse = await client.post<ServerVizSpecResponse>(\n\t\t\t\t\"/vizspec\",\n\t\t\t\t{\n\t\t\t\t\tquestion: input.question,\n\t\t\t\t\tsql: finalSql,\n\t\t\t\t\trationale,\n\t\t\t\t\tfields: execution.fields,\n\t\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\t\tmax_retries: options?.chartMaxRetries ?? 3,\n\t\t\t\t\tquery_id: queryId,\n\t\t\t\t\t// Include viz hints for the chart generator\n\t\t\t\t\t...(hasVizMods ? { encoding_hints: vizHints } : {}),\n\t\t\t\t},\n\t\t\t\ttenantId,\n\t\t\t\toptions?.userId,\n\t\t\t\toptions?.scopes,\n\t\t\t\tsignal,\n\t\t\t\tsessionId,\n\t\t\t);\n\n\t\t\tchart = {\n\t\t\t\tvizSpec: vizspecResponse.spec,\n\t\t\t\tspecType: \"vizspec\",\n\t\t\t\tnotes: vizspecResponse.notes,\n\t\t\t};\n\t\t} else {\n\t\t\tconst chartResponse = await client.post<ServerChartResponse>(\n\t\t\t\t\"/chart\",\n\t\t\t\t{\n\t\t\t\t\tquestion: input.question,\n\t\t\t\t\tsql: finalSql,\n\t\t\t\t\trationale,\n\t\t\t\t\tfields: execution.fields,\n\t\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\t\tmax_retries: options?.chartMaxRetries ?? 3,\n\t\t\t\t\tquery_id: queryId,\n\t\t\t\t\t// Include viz hints for the chart generator\n\t\t\t\t\t...(hasVizMods ? { encoding_hints: vizHints } : {}),\n\t\t\t\t},\n\t\t\t\ttenantId,\n\t\t\t\toptions?.userId,\n\t\t\t\toptions?.scopes,\n\t\t\t\tsignal,\n\t\t\t\tsessionId,\n\t\t\t);\n\n\t\t\tchart = {\n\t\t\t\tvegaLiteSpec: chartResponse.chart\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...chartResponse.chart,\n\t\t\t\t\t\t\tdata: { values: rows },\n\t\t\t\t\t\t}\n\t\t\t\t\t: null,\n\t\t\t\tspecType: \"vega-lite\",\n\t\t\t\tnotes: chartResponse.notes,\n\t\t\t};\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\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 { ApiClient } from \"../core/client\";\nimport type { ParamRecord, QueryEngine } from \"../core/query-engine\";\nimport { type QueryErrorCode, QueryPipelineError } from \"../errors\";\nimport type { 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}\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}\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// 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 * Route module for natural language query generation\n * Simple orchestration following Ousterhout's principle\n */\nexport async function ask(\n\tclient: ApiClient,\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\n\twhile (attempt <= maxRetry) {\n\t\t// Step 1: Get SQL from backend\n\t\tconsole.log({ lastError, previousSql });\n\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\t// Include tenant settings if available in metadata\n\t\tlet tenantSettings: Record<string, unknown> | undefined;\n\t\tif (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: metadata.enforceTenantIsolation,\n\t\t\t};\n\t\t}\n\n\t\tconst queryResponse = await client.postWithHeaders<ServerQueryResponse>(\n\t\t\t\"/query\",\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},\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\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 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 if we have data\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 (rows.length > 0) {\n\t\t\t\tif (chartType === \"vizspec\") {\n\t\t\t\t\t// Use new VizSpec generation\n\t\t\t\t\tconst vizspecResponse = await client.post<ServerVizSpecResponse>(\n\t\t\t\t\t\t\"/vizspec\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tquestion,\n\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\trationale: queryResponse.data.rationale,\n\t\t\t\t\t\t\tfields: execution.fields,\n\t\t\t\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\t\t\t\tmax_retries: options.chartMaxRetries ?? 3,\n\t\t\t\t\t\t\tquery_id: queryResponse.data.queryId,\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttenantId,\n\t\t\t\t\t\toptions.userId,\n\t\t\t\t\t\toptions.scopes,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t);\n\n\t\t\t\t\tchart = {\n\t\t\t\t\t\tvizSpec: vizspecResponse.spec,\n\t\t\t\t\t\tspecType: \"vizspec\",\n\t\t\t\t\t\tnotes: vizspecResponse.notes,\n\t\t\t\t\t};\n\t\t\t\t} else {\n\t\t\t\t\t// Use traditional Vega-Lite chart generation\n\t\t\t\t\tconst chartResponse = await client.post<ServerChartResponse>(\n\t\t\t\t\t\t\"/chart\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tquestion,\n\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\trationale: queryResponse.data.rationale,\n\t\t\t\t\t\t\tfields: execution.fields,\n\t\t\t\t\t\t\trows: anonymizeResults(rows),\n\t\t\t\t\t\t\tmax_retries: options.chartMaxRetries ?? 3,\n\t\t\t\t\t\t\tquery_id: queryResponse.data.queryId,\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttenantId,\n\t\t\t\t\t\toptions.userId,\n\t\t\t\t\t\toptions.scopes,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\tsessionId,\n\t\t\t\t\t);\n\n\t\t\t\t\tchart = {\n\t\t\t\t\t\tvegaLiteSpec: chartResponse.chart\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t...chartResponse.chart,\n\t\t\t\t\t\t\t\t\tdata: { values: rows },\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t\tspecType: \"vega-lite\",\n\t\t\t\t\t\tnotes: chartResponse.notes,\n\t\t\t\t\t};\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};\n\t\t} catch (error) {\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(\n\t\t\t\t`SQL execution failed (attempt ${attempt}/${maxRetry + 1}): ${lastError}. Retrying...`,\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: ApiClient, 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 { ApiClient } from \"../core/client\";\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: ApiClient,\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: ApiClient,\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: ApiClient,\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: ApiClient,\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: ApiClient, 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 { ApiClient } from \"../core/client\";\nimport type { 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\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: ApiClient,\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 },\n tenantId,\n options?.userId,\n options?.scopes,\n signal,\n sessionId,\n );\n\n return response;\n}\n\nfunction resolveTenantId(client: ApiClient, 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","import {\n\tClickHouseAdapter,\n\ttype ClickHouseAdapterOptions,\n\ttype ClickHouseClientFn,\n} from \"./adapters/clickhouse\";\nimport {\n\tPostgresAdapter,\n\ttype PostgresAdapterOptions,\n\ttype PostgresClientFn,\n} from \"./adapters/postgres\";\nimport type { DatabaseAdapter, DatabaseDialect } from \"./adapters/types\";\nimport { ApiClient } from \"./core/client\";\nimport { type DatabaseMetadata, QueryEngine } from \"./core/query-engine\";\nimport { QueryErrorCode, QueryPipelineError } from \"./errors\";\nimport * as activeChartsRoute from \"./routes/active-charts\";\nimport * as chartsRoute from \"./routes/charts\";\nimport * as ingestRoute from \"./routes/ingest\";\nimport * as modifyRoute from \"./routes/modify\";\nimport * as queryRoute from \"./routes/query\";\nimport * as sessionsRoute from \"./routes/sessions\";\nimport * as vizspecRoute from \"./routes/vizspec\";\nimport type { SchemaIntrospection } from \"./schema/types\";\n\n// Re-export all public types\nexport { ClickHouseAdapter, PostgresAdapter };\n\n// Re-export error types\nexport type { QueryErrorCode as QueryErrorCodeType } from \"./errors\";\nexport { QueryErrorCode, QueryPipelineError };\n\nexport type {\n\tClickHouseAdapterOptions,\n\tClickHouseClientFn,\n\tDatabaseAdapter,\n\tDatabaseDialect,\n\tPostgresAdapterOptions,\n\tPostgresClientFn,\n\tSchemaIntrospection,\n};\n\n// Re-export from query-engine\nexport type { ParamRecord, ParamValue } from \"./core/query-engine\";\nexport type {\n\tActiveChartCreateInput,\n\tActiveChartListOptions,\n\tActiveChartUpdateInput,\n\tSdkActiveChart,\n} from \"./routes/active-charts\";\n\nexport type {\n\tChartCreateInput,\n\tChartListOptions,\n\tChartUpdateInput,\n\tPaginatedResponse,\n\tPaginationInfo,\n\tPaginationQuery,\n\tSdkChart,\n} from \"./routes/charts\";\n// Re-export route types\nexport type {\n\tIngestResponse,\n\tSchemaSyncOptions,\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} from \"./routes/query\";\n// Re-export anonymizeResults utility\nexport { anonymizeResults } 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 type {\n\tAggregateOp,\n\tAxisField,\n\tChartEncoding,\n\tChartSpec,\n\tChartType,\n\tFieldRef,\n\tFieldType,\n\tMetricEncoding,\n\tMetricField,\n\tMetricSpec,\n\tStackingMode,\n\tTableColumn,\n\tTableEncoding,\n\tTableSpec,\n\tTimeUnit,\n\tVizSpec,\n} from \"./types/vizspec\";\n\n/**\n * Main SDK class - Thin orchestrator\n * Delegates to deep modules (ApiClient, QueryEngine, route modules)\n * Following Ousterhout's principle: \"Simple interface hiding complexity\"\n */\nexport class QueryPanelSdkAPI {\n\tprivate readonly client: ApiClient;\n\tprivate readonly queryEngine: QueryEngine;\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?: typeof fetch;\n\t\t},\n\t) {\n\t\tthis.client = new ApiClient(baseUrl, privateKey, organizationId, options);\n\t\tthis.queryEngine = new QueryEngine();\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: options?.tenantFieldName\n\t\t\t\t? (options?.enforceTenantIsolation ?? true)\n\t\t\t\t: undefined,\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: \"postgres\",\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: options?.tenantFieldName\n\t\t\t\t? (options?.enforceTenantIsolation ?? true)\n\t\t\t\t: undefined,\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 * 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// 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, and retry settings\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\toptions,\n\t\t\tsignal,\n\t\t);\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\tinput,\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\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// 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 * 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"],"mappings":"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","names":["normalizeTableFilter","asTableType","sanitize","resolveTenantId","crypto","resolveTenantId","crypto","crypto","resolveTenantId","crypto","resolveTenantId","crypto","resolveTenantId","crypto","resolveTenantId"]}