@querypanel/node-sdk 1.0.24 → 1.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,323 +1,118 @@
1
1
  # QueryPanel Node SDK
2
2
 
3
- A lightweight Node.js client for QueryPanel's NL-to-SQL APIs. The SDK manages JWT auth, exposes helper methods for common endpoints, and provides database adapters with execution, validation, and schema introspection utilities for ClickHouse and Postgres.
3
+ A TypeScript-first client for the QueryPanel Bun/Hono API. It signs JWTs with your service private key, syncs database schemas, enforces tenant isolation, and wraps every public route under `src/routes/` (query, ingest, charts, active charts, and knowledge base).
4
4
 
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install @querypanel/node-sdk
8
+ bun add @querypanel/sdk
9
+ # or
10
+ npm install @querypanel/sdk
9
11
  ```
10
12
 
11
- - Requires **Node 18+**.
12
- - `@clickhouse/client` is a peer dependency for ClickHouse integrations.
13
+ > **Runtime:** Node.js 18+ (or Bun). The SDK relies on the native `fetch` API.
13
14
 
14
- ## Quick start
15
+ ## Quickstart
15
16
 
16
17
  ```ts
17
- import { QueryPanelSdkAPI } from "@querypanel/node-sdk";
18
- import { createClient } from "@clickhouse/client";
18
+ import { QueryPanelSdkAPI } from "@querypanel/sdk";
19
+ import { Pool } from "pg";
19
20
 
20
21
  const qp = new QueryPanelSdkAPI(
21
22
  process.env.QUERYPANEL_URL!,
22
- process.env.QUERYPANEL_SERVICE_TOKEN!,
23
- process.env.PRIVATE_KEY!
24
- );
25
-
26
- // Attach a ClickHouse database (use the SDK-supplied adapter)
27
- const clickhouse = createClient({
28
- url: process.env.CLICKHOUSE_URL!,
29
- username: process.env.CLICKHOUSE_USER,
30
- password: process.env.CLICKHOUSE_PASSWORD,
31
- database: "analytics",
32
- });
33
-
34
- qp.attachClickhouse(
35
- "analytics",
36
- (params) => clickhouse.query(params),
23
+ process.env.PRIVATE_KEY!,
24
+ process.env.ORGANIZATION_ID!,
37
25
  {
38
- database: "analytics",
39
- tenantFieldName: "customer_id", // Enable tenant isolation
40
- tenantFieldType: "String", // ClickHouse type
26
+ defaultTenantId: process.env.DEFAULT_TENANT_ID,
41
27
  },
42
28
  );
43
29
 
44
- // Ask a question - tenant isolation is automatic
45
- const response = await qp.ask("Top countries by revenue", { tenantId: "tenant_123" });
46
- console.log(response.sql); // Includes WHERE customer_id = {customer_id:String}
47
- console.log(response.params); // { customer_id: "tenant_123", ... }
48
- console.table(response.rows);
49
- ```
50
-
51
- ## Authentication modes
52
-
53
- The constructor accepts a private key/organization pair for on-the-fly token generation:
54
-
55
- ```ts
56
-
57
- // provide an RSA private key and organization ID (recommended)
58
- // and save the public key on the querypanel ui
59
- const qp = new QueryPanelSdkAPI(baseUrl, privateKeyPem, organizationId);
60
- ```
61
-
62
- ## V3 Bedrock Integration
63
-
64
- The SDK provides methods to generate schema exports compatible with AWS Bedrock knowledge bases:
65
-
66
- ### introspectV3
67
-
68
- Generate a simplified schema export format suitable for Bedrock ingestion:
69
-
70
- ```ts
71
- const schema = await qp.introspectV3("analytics", {
72
- tenantId: "tenant-123",
73
- tables: ["orders", "customers"], // Optional: filter specific tables
74
- });
75
-
76
- // Output matches schema_export.json format
77
- console.log(JSON.stringify(schema, null, 2));
78
- ```
79
-
80
- ### ingestSchemaV3
30
+ const pool = new Pool({ connectionString: process.env.POSTGRES_URL });
81
31
 
82
- Introspect and ingest directly to Bedrock knowledge base:
83
-
84
- ```ts
85
- const result = await qp.ingestSchemaV3("analytics", {
86
- tenantId: "tenant-123",
87
- tables: ["orders", "customers"], // Optional
88
- });
89
-
90
- console.log(`Ingested ${result.total_documents} documents`);
91
- console.log(`Failed: ${result.failed.length}`);
92
- ```
93
-
94
- The V3 format includes:
95
- - Table overviews with column summaries and statistics
96
- - Individual column metadata documents
97
- - Foreign key relationships
98
- - Primary key indicators
99
-
100
- This data powers the Bedrock-backed `/v3/generate-sql` endpoint using Claude Haiku 4.5.
101
-
102
- ## Working with databases
103
-
104
- Adapters mediate between the SDK and your data sources. Two helpers are bundled:
105
-
106
- ### ClickHouse
107
-
108
- ```ts
109
- import {
110
- ClickHouseAdapter,
111
- type ClickHouseClientFn,
112
- } from "@querypanel/node-sdk";
113
- import { createClient } from "@clickhouse/client";
114
-
115
- const clickhouseClient = createClient({ url: "https://ch.example.com", database: "analytics" });
116
- const clickhouseFn: ClickHouseClientFn = (params) => clickhouseClient.query(params);
117
- const clickhouseAdapter = new ClickHouseAdapter(clickhouseFn, { database: "analytics" });
118
-
119
- // Optional introspection before attaching
120
- const schema = await clickhouseAdapter.introspect();
121
- console.log(schema.tables.map((t) => t.name));
122
-
123
- qp.attachDatabase("analytics", clickhouseAdapter);
124
- ```
125
-
126
- ### Postgres
127
-
128
- ```ts
129
- import { Pool } from "pg";
130
- import {
131
- PostgresAdapter,
132
- type PostgresClientFn,
133
- } from "@querypanel/node-sdk";
134
-
135
- const pool = new Pool({ connectionString: process.env.DATABASE_URL });
136
- const pgFn: PostgresClientFn = async (sql) => {
32
+ const createPostgresClient = () => async (sql: string, params?: unknown[]) => {
137
33
  const client = await pool.connect();
138
34
  try {
139
- const result = await client.query(sql);
35
+ const result = await client.query(sql, params);
140
36
  return {
141
37
  rows: result.rows,
142
- fields: result.fields.map((f) => ({ name: f.name })),
38
+ fields: result.fields.map((field) => ({ name: field.name })),
143
39
  };
144
40
  } finally {
145
41
  client.release();
146
42
  }
147
43
  };
148
44
 
149
- const pgAdapter = new PostgresAdapter(pgFn, {
150
- database: "app",
151
- defaultSchema: "public",
45
+ qp.attachPostgres("analytics", createPostgresClient(), {
46
+ description: "Primary analytics warehouse",
47
+ tenantFieldName: "tenant_id",
152
48
  });
153
49
 
154
- await pgAdapter.introspect({ tables: ["public.users"] });
155
- qp.attachDatabase("app", pgAdapter);
156
- ```
157
-
158
- Adapters expose three core methods:
159
-
160
- - `execute(sql)` – runs the query and returns `{ fields, rows }`.
161
- - `validate(sql)` – runs an `EXPLAIN` to verify syntax before execution.
162
- - `introspect(options?)` – returns structured schema metadata (`SchemaIntrospection`).
163
-
164
- ## SDK helpers
165
-
166
- Once at least one adapter is attached you can call:
167
-
168
- - `ask(question, options)` – generate SQL, validate it, execute through the adapter, and retrieve a matching Vega-Lite chart spec.
169
- - `train(payload, { tenantId, userId, scopes })` – submit curated glossary entries, metric docs, and gold SQL examples.
170
- - `stats({ tenantId, userId, scopes })` – fetch ingestion statistics.
171
- - Chart helpers: `listCharts`, `getChart`, `createChart`, `updateChart`, `deleteChart`, `listActiveCharts`, `getActiveChart`, `createActiveChart`, `updateActiveChart`, `deleteActiveChart`.
172
-
173
- Every SDK call automatically attaches the correct authentication headers and forwards optional `tenantId`, `userId`, and `scopes` when provided.
174
-
175
- ### Using V3 SQL generation
50
+ qp.attachClickhouse(
51
+ "clicks",
52
+ (params) => clickhouse.query(params),
53
+ {
54
+ database: "analytics",
55
+ tenantFieldName: "customer_id",
56
+ tenantFieldType: "String",
57
+ },
58
+ );
176
59
 
177
- By default, `ask()` uses the `/v2/generate-sql` endpoint (OpenAI + pgvector). To use the Bedrock-powered `/v3/generate-sql` endpoint (Claude Haiku 4.5), set `useV3: true`:
60
+ await qp.syncSchema("analytics", { tenantId: "tenant_123" });
178
61
 
179
- ```ts
180
62
  const response = await qp.ask("Top countries by revenue", {
181
63
  tenantId: "tenant_123",
182
- useV3: true, // Use Bedrock + Claude instead of OpenAI
183
- });
184
- ```
185
-
186
- **Important**: The `useV3` flag controls both:
187
- 1. **SQL generation endpoint**: Uses `/v3/generate-sql` instead of `/v2/generate-sql`
188
- 2. **Schema introspection**: On first use, auto-syncs schema via `/v3/ingest` (Bedrock knowledge base) instead of `/v2/vectorize-schema` (pgvector)
189
-
190
- Both endpoints support the same request/response format, making migration seamless.
191
-
192
- ### Manual sync control
193
-
194
- By default, the SDK automatically syncs your database schema on the first `ask()` call. You can disable this and sync manually:
195
-
196
- ```ts
197
- // Manual sync workflow
198
- await qp.ingestSchemaV3("analytics", {
199
- tenantId: "tenant_123",
200
- tables: ["orders", "customers"], // Optional: sync specific tables
201
- });
202
-
203
- // Now ask questions with auto-sync disabled
204
- const response = await qp.ask("Top revenue by country", {
205
- tenantId: "tenant_123",
206
- useV3: true,
207
- disableAutoSync: true, // ⚠️ Requires manual sync first
208
- });
209
- ```
210
-
211
- **Why disable auto-sync?**
212
- - 🎯 Control when schema updates happen
213
- - ⚡ Faster `ask()` calls (no sync overhead)
214
- - 💰 Reduce API calls to Bedrock
215
- - 🔧 Better for production deployments (sync on app startup, not on every request)
216
-
217
- **Recommended pattern for production:**
218
-
219
- ```ts
220
- // On app startup (once)
221
- await qp.ingestSchemaV3("analytics", { tenantId: "..." });
222
-
223
- // In request handlers (many times)
224
- app.post("/query", async (req, res) => {
225
- const result = await qp.ask(req.body.question, {
226
- tenantId: req.user.tenantId,
227
- useV3: true,
228
- disableAutoSync: true, // Already synced at startup
229
- });
230
- res.json(result);
231
- });
232
- ```
233
-
234
- ## Multi-database support
235
-
236
- The SDK keeps a registry of adapters. The first attached database becomes the default, but you can switch per request:
237
-
238
- ```ts
239
- qp.attachPostgres("users", pgFn, { database: "users" });
240
- qp.attachClickhouse("analytics", clickhouseFn);
241
-
242
- await qp.ask("Top spenders", {
243
- tenantId: "tenant_123",
244
- database: "analytics", // override default database for this request
64
+ database: "analytics",
245
65
  });
246
- ```
247
-
248
- The `available_databases` array is forwarded to the `/v2/generate-sql` endpoint so the service can pick the right dialect when it suggests queries.
249
-
250
- ## Schema introspection
251
-
252
- Both adapters can produce consistent metadata that matches `node-sdk/src/schema/types.ts`:
253
66
 
254
- ```ts
255
- const snapshot = await pgAdapter.introspect({ tables: ["public.orders"] });
256
- for (const table of snapshot.tables) {
257
- console.log(table.name, table.columns.length);
258
- }
67
+ console.log(response.sql);
68
+ console.log(response.params);
69
+ console.table(response.rows);
70
+ console.log(response.chart.vegaLiteSpec);
259
71
  ```
260
72
 
261
- Use introspection to feed the training endpoint or to drive documentation for the AI planner.
262
-
263
- ## Local introspection script
264
-
265
- Test V3 introspection locally without calling the API:
73
+ ## Building locally
266
74
 
267
75
  ```bash
268
- # Quick start
269
- npm run introspect:local
270
-
271
- # With custom database
272
- CLICKHOUSE_URL=http://localhost:8123 \
273
- CLICKHOUSE_DATABASE=analytics \
274
- TENANT_ID=my-tenant \
275
- npm run introspect:local
276
-
277
- # Filter specific tables
278
- TABLES="orders,customers" npm run introspect:local
76
+ cd node-sdk
77
+ bun install
78
+ bun run build
279
79
  ```
280
80
 
281
- This generates `schema_export.json` that you can:
282
- 1. Review locally before ingestion
283
- 2. Upload manually via `/v3/ingest`
284
- 3. Use for testing and CI/CD
81
+ This runs `tsup` which emits dual ESM/CJS bundles plus type declarations to `dist/`.
285
82
 
286
- See `node-sdk/scripts/README.md` for full documentation.
83
+ ## Authentication model
287
84
 
288
- ## Testing and building
85
+ Every request is signed with `RS256` using the private key you pass to the constructor. The payload always includes `organizationId` and `tenantId`; `userId` and `scopes` are added when provided per call. If you still need service tokens or custom middleware, pass additional headers via the constructor.
289
86
 
290
- ```bash
291
- # run unit tests
292
- npm --prefix node-sdk test
87
+ ## Error handling
293
88
 
294
- # build CommonJS and ESM artifacts
295
- npm --prefix node-sdk run build
296
- ```
297
-
298
- ## Tenant Isolation
89
+ - HTTP errors propagate as thrown `Error` instances that include `status` (and `details` when available).
90
+ - Schema ingestion failures are logged to `console.warn` during auto-sync, but you can call `syncSchema(..., { force: true })` to surface them directly.
91
+ - `ask()` raises immediately for guardrail/moderation errors because `/query` responds with 4xx/5xx.
299
92
 
300
- The SDK provides automatic tenant isolation enforcement to prevent data leaks in multi-tenant environments. When configured, the SDK will automatically:
93
+ ### Automatic SQL repair and retry
301
94
 
302
- - Inject tenant parameters into all queries
303
- - ✅ Validate that generated SQL includes tenant filters
304
- - ✅ Auto-fix SQL missing tenant isolation by adding WHERE clauses
305
- - ✅ Support ClickHouse typed parameters (`{customer_id:String}`)
95
+ When SQL execution fails (e.g., invalid column name, syntax error), the SDK can automatically retry with a repaired query:
306
96
 
307
- ```typescript
308
- sdk.attachClickhouse('analytics', clickhouseFn, {
309
- tenantFieldName: 'customer_id', // Required field in your schema
310
- tenantFieldType: 'String', // ClickHouse type (String, UUID, Int64, etc.)
311
- enforceTenantIsolation: true, // Default: true (auto-fix missing filters)
97
+ ```ts
98
+ const response = await qp.ask("Show revenue by country", {
99
+ tenantId: "tenant_123",
100
+ maxRetry: 3, // Automatically retry up to 3 times on execution error
312
101
  });
102
+
103
+ console.log(`Query succeeded after ${response.attempts} attempt(s)`);
104
+ console.table(response.rows);
313
105
  ```
314
106
 
315
- See [TENANT_ISOLATION.md](./TENANT_ISOLATION.md) for complete documentation.
107
+ The SDK will:
108
+ 1. Execute the generated SQL
109
+ 2. If execution fails, send the error back to the server with the failed SQL
110
+ 3. Get a repaired SQL query from the server
111
+ 4. Execute the repaired query
112
+ 5. Repeat up to `maxRetry` times
316
113
 
317
- ## Additional resources
114
+ Without `maxRetry`, execution errors throw immediately (default behavior).
318
115
 
319
- - `TENANT_ISOLATION.md` – Complete guide to automatic tenant isolation enforcement
320
- - `SDK-API.md` – REST endpoint reference used by the SDK
321
- - `MULTI_DATABASE.md` – Notes on attaching multiple adapters
116
+ ## Need more?
322
117
 
323
- If you hit an integration bug or need support for an additional database engine, open an issue or reach out to the QueryPanel team.
118
+ Open an issue or extend `node-sdk/src/index.ts`—every route lives in one file. Pull requests are welcome for additional adapters, richer param coercion, or convenience helpers around charts/annotations.
@@ -0,0 +1,5 @@
1
+ declare global {
2
+ var fetch: typeof globalThis.fetch;
3
+ }
4
+ export {};
5
+ //# sourceMappingURL=ingest.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/ingest.test.ts"],"names":[],"mappings":"AAKA,OAAO,CAAC,MAAM,CAAC;IAEX,IAAI,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACtC"}
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const index_js_1 = require("../index.js");
5
+ const noop = () => { };
6
+ class StubConnector {
7
+ constructor(introspection) {
8
+ this.introspection = introspection;
9
+ this.connect = vitest_1.vi.fn(async () => { });
10
+ this.close = vitest_1.vi.fn(async () => { });
11
+ this.executeQuery = vitest_1.vi.fn(async () => []);
12
+ this.introspect = vitest_1.vi.fn(async (_options) => this.introspection);
13
+ }
14
+ }
15
+ (0, vitest_1.describe)('Ingestion API', () => {
16
+ const originalFetch = globalThis.fetch;
17
+ const fakeResponse = {
18
+ ok: true,
19
+ status: 200,
20
+ text: async () => JSON.stringify({ message: 'ok', ingestedTables: 1 }),
21
+ };
22
+ let fetchMock;
23
+ (0, vitest_1.beforeEach)(() => {
24
+ fetchMock = vitest_1.vi.fn().mockResolvedValue(fakeResponse);
25
+ globalThis.fetch = fetchMock;
26
+ });
27
+ (0, vitest_1.afterEach)(() => {
28
+ fetchMock.mockReset();
29
+ if (originalFetch) {
30
+ globalThis.fetch = originalFetch;
31
+ }
32
+ else {
33
+ delete globalThis.fetch;
34
+ }
35
+ });
36
+ (0, vitest_1.it)('posts schema introspection payloads with merged metadata', async () => {
37
+ const sdk = new index_js_1.QueryPanelSdkAPI('https://example.com', 'token');
38
+ const introspection = {
39
+ db: { kind: 'clickhouse', name: 'analytics' },
40
+ tables: [],
41
+ introspectedAt: new Date().toISOString(),
42
+ metadata: { existing: true },
43
+ };
44
+ const response = await sdk.ingestSchema(introspection, {
45
+ tenantId: 'tenant-1',
46
+ metadata: { triggeredBy: 'test' },
47
+ });
48
+ (0, vitest_1.expect)(response).toMatchObject({ message: 'ok', ingestedTables: 1 });
49
+ (0, vitest_1.expect)(fetchMock).toHaveBeenCalledTimes(1);
50
+ const [url, init] = fetchMock.mock.calls[0];
51
+ (0, vitest_1.expect)(url).toBe('https://example.com/ingest/schema');
52
+ (0, vitest_1.expect)(init.method).toBe('POST');
53
+ (0, vitest_1.expect)(init.headers).toMatchObject({
54
+ Authorization: 'Bearer token',
55
+ 'Content-Type': 'application/json',
56
+ });
57
+ const body = JSON.parse(init.body);
58
+ (0, vitest_1.expect)(body.metadata).toEqual({ existing: true, triggeredBy: 'test' });
59
+ });
60
+ (0, vitest_1.it)('introspects via connector and closes connections by default', async () => {
61
+ const sdk = new index_js_1.QueryPanelSdkAPI('https://example.com', 'token');
62
+ const introspection = {
63
+ db: { kind: 'clickhouse', name: 'analytics' },
64
+ tables: [],
65
+ introspectedAt: new Date().toISOString(),
66
+ };
67
+ const connector = new StubConnector(introspection);
68
+ const response = await sdk.introspectAndIngest(connector, {
69
+ tenantId: 'tenant-1',
70
+ metadata: { flow: 'batch' },
71
+ tables: ['events'],
72
+ });
73
+ (0, vitest_1.expect)(response.message).toBe('ok');
74
+ (0, vitest_1.expect)(connector.connect).toHaveBeenCalledTimes(1);
75
+ (0, vitest_1.expect)(connector.introspect).toHaveBeenCalledWith({ tables: ['events'] });
76
+ (0, vitest_1.expect)(connector.close).toHaveBeenCalledTimes(1);
77
+ const [, init] = fetchMock.mock.calls[0];
78
+ const body = JSON.parse(init.body);
79
+ (0, vitest_1.expect)(body.metadata).toEqual({ flow: 'batch' });
80
+ });
81
+ (0, vitest_1.it)('respects keepConnectionOpen option', async () => {
82
+ const sdk = new index_js_1.QueryPanelSdkAPI('https://example.com', 'token');
83
+ const connector = new StubConnector({
84
+ db: { kind: 'clickhouse', name: 'analytics' },
85
+ tables: [],
86
+ introspectedAt: new Date().toISOString(),
87
+ });
88
+ await sdk.introspectAndIngest(connector, {
89
+ tenantId: 'tenant-1',
90
+ keepConnectionOpen: true,
91
+ });
92
+ (0, vitest_1.expect)(connector.close).not.toHaveBeenCalled();
93
+ });
94
+ });
95
+ //# sourceMappingURL=ingest.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest.test.js","sourceRoot":"","sources":["../../../src/__tests__/ingest.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=clickhouse.introspect.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clickhouse.introspect.test.d.ts","sourceRoot":"","sources":["../../../../src/connectors/__tests__/clickhouse.introspect.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const clickhouse_js_1 = require("../clickhouse.js");
5
+ class StubResultSet {
6
+ constructor(payload) {
7
+ this.payload = payload;
8
+ }
9
+ async json() {
10
+ return this.payload;
11
+ }
12
+ }
13
+ class StubClickHouseClient {
14
+ constructor(responses) {
15
+ this.queries = [];
16
+ this.responses = responses;
17
+ }
18
+ async query(params) {
19
+ this.queries.push({ query: params.query, params: params.query_params });
20
+ const responseKey = params.query.includes('system.tables') ? 'tables' : 'columns';
21
+ const payload = this.responses.get(responseKey);
22
+ if (!payload) {
23
+ throw new Error(`No stub response registered for ${responseKey}`);
24
+ }
25
+ return new StubResultSet(payload);
26
+ }
27
+ async ping() {
28
+ return { success: true };
29
+ }
30
+ async close() { }
31
+ }
32
+ (0, vitest_1.describe)('ClickHouseConnector', () => {
33
+ (0, vitest_1.it)('introspects tables with large column counts', async () => {
34
+ const tableRows = [
35
+ {
36
+ name: 'events',
37
+ engine: 'MergeTree',
38
+ comment: 'User event stream',
39
+ total_rows: '424242',
40
+ total_bytes: '1024',
41
+ is_view: 0,
42
+ primary_key: 'tuple(event_id, occurred_at)',
43
+ sorting_key: 'tuple(occurred_at)',
44
+ },
45
+ ];
46
+ const columnRows = Array.from({ length: 120 }, (_, index) => ({
47
+ table: 'events',
48
+ name: `column_${index}`,
49
+ type: index % 2 === 0 ? 'Nullable(String)' : 'UInt32',
50
+ position: index + 1,
51
+ default_kind: index % 10 === 0 ? 'DEFAULT' : null,
52
+ default_expression: index % 10 === 0 ? `'value_${index}'` : null,
53
+ comment: index % 15 === 0 ? `Column ${index}` : null,
54
+ codec_expression: index % 20 === 0 ? 'ZSTD(3)' : null,
55
+ ttl_expression: null,
56
+ is_in_primary_key: index === 0 ? 1 : 0,
57
+ data_compressed_bytes: '128',
58
+ data_uncompressed_bytes: '256',
59
+ }));
60
+ const responses = new Map([
61
+ ['tables', tableRows],
62
+ ['columns', columnRows],
63
+ ]);
64
+ const stubClient = new StubClickHouseClient(responses);
65
+ const connector = new clickhouse_js_1.ClickHouseConnector({
66
+ database: 'analytics',
67
+ }, { client: stubClient });
68
+ const introspection = await connector.introspect();
69
+ (0, vitest_1.expect)(introspection.db).toEqual({ kind: 'clickhouse', name: 'analytics' });
70
+ (0, vitest_1.expect)(introspection.tables).toHaveLength(1);
71
+ const [table] = introspection.tables;
72
+ (0, vitest_1.expect)(table.name).toBe('events');
73
+ (0, vitest_1.expect)(table.columns).toHaveLength(120);
74
+ (0, vitest_1.expect)(table.columns[0]).toMatchObject({
75
+ name: 'column_0',
76
+ nullable: true,
77
+ type: 'String',
78
+ rawType: 'Nullable(String)',
79
+ defaultKind: 'DEFAULT',
80
+ defaultExpression: `'value_0'`,
81
+ statistics: { compressedBytes: 128, uncompressedBytes: 256 },
82
+ });
83
+ (0, vitest_1.expect)(table.columns.at(-1)).toMatchObject({
84
+ nullable: false,
85
+ type: 'UInt32',
86
+ });
87
+ (0, vitest_1.expect)(table.statistics).toEqual({ totalRows: 424242, totalBytes: 1024 });
88
+ (0, vitest_1.expect)(table.indexes).toEqual([
89
+ {
90
+ name: 'primary_key',
91
+ columns: ['event_id', 'occurred_at'],
92
+ unique: true,
93
+ type: 'PRIMARY KEY',
94
+ definition: 'tuple(event_id, occurred_at)',
95
+ },
96
+ ]);
97
+ (0, vitest_1.expect)(table.constraints[0]).toMatchObject({
98
+ type: 'PRIMARY KEY',
99
+ columns: ['event_id', 'occurred_at'],
100
+ });
101
+ (0, vitest_1.expect)(new Date(introspection.introspectedAt).toISOString()).toBe(introspection.introspectedAt);
102
+ (0, vitest_1.expect)(stubClient.queries).toHaveLength(2);
103
+ (0, vitest_1.expect)(stubClient.queries[0].params).toEqual({ db: 'analytics' });
104
+ });
105
+ (0, vitest_1.it)('applies table filters when provided', async () => {
106
+ const responses = new Map([
107
+ ['tables', [{ name: 'events', engine: 'MergeTree', is_view: 0 }]],
108
+ ['columns', [{ table: 'events', name: 'column_1', type: 'UInt64', position: 1 }]],
109
+ ]);
110
+ const stubClient = new StubClickHouseClient(responses);
111
+ const connector = new clickhouse_js_1.ClickHouseConnector({ database: 'analytics' }, { client: stubClient });
112
+ await connector.introspect({ tables: ['analytics.events', 'events'] });
113
+ (0, vitest_1.expect)(stubClient.queries).toHaveLength(2);
114
+ for (const query of stubClient.queries) {
115
+ (0, vitest_1.expect)(query.params).toEqual({ db: 'analytics', tables: ['events'] });
116
+ }
117
+ });
118
+ });
119
+ //# sourceMappingURL=clickhouse.introspect.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clickhouse.introspect.test.js","sourceRoot":"","sources":["../../../../src/connectors/__tests__/clickhouse.introspect.test.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,12 @@
1
- import type { DataFormat } from '@clickhouse/client';
2
1
  import type { IntrospectOptions, SchemaIntrospection } from '../schema/types.js';
3
2
  export interface QueryOptions {
4
3
  params?: Record<string, unknown>;
5
- format?: DataFormat;
4
+ format?: string;
6
5
  settings?: Record<string, unknown>;
7
6
  }
8
7
  export interface DatabaseConnector {
9
8
  connect(): Promise<void>;
10
- query<T = Record<string, unknown>>(sql: string, options?: QueryOptions): Promise<T[]>;
9
+ executeQuery<T = Record<string, unknown>>(query: string, options?: QueryOptions): Promise<T[]>;
11
10
  introspect(options?: IntrospectOptions): Promise<SchemaIntrospection>;
12
11
  close(): Promise<void>;
13
12
  }