@hypequery/cli 1.3.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +61 -10
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +24 -1
  4. package/dist/commands/deployment.d.ts +11 -0
  5. package/dist/commands/deployment.d.ts.map +1 -0
  6. package/dist/commands/deployment.js +84 -0
  7. package/dist/commands/generate.d.ts +1 -0
  8. package/dist/commands/generate.d.ts.map +1 -1
  9. package/dist/commands/generate.js +22 -3
  10. package/dist/commands/init.d.ts +2 -0
  11. package/dist/commands/init.d.ts.map +1 -1
  12. package/dist/commands/init.js +180 -41
  13. package/dist/generators/chdb.d.ts +13 -0
  14. package/dist/generators/chdb.d.ts.map +1 -0
  15. package/dist/generators/chdb.js +17 -0
  16. package/dist/generators/dataset-generator.d.ts +2 -0
  17. package/dist/generators/dataset-generator.d.ts.map +1 -1
  18. package/dist/generators/dataset-generator.js +1 -1
  19. package/dist/generators/index.d.ts +2 -1
  20. package/dist/generators/index.d.ts.map +1 -1
  21. package/dist/generators/index.js +2 -0
  22. package/dist/templates/client.d.ts +7 -2
  23. package/dist/templates/client.d.ts.map +1 -1
  24. package/dist/templates/client.js +23 -2
  25. package/dist/templates/gitignore.d.ts +1 -1
  26. package/dist/templates/gitignore.d.ts.map +1 -1
  27. package/dist/templates/gitignore.js +20 -6
  28. package/dist/utils/chdb-client.d.ts +36 -0
  29. package/dist/utils/chdb-client.d.ts.map +1 -0
  30. package/dist/utils/chdb-client.js +122 -0
  31. package/dist/utils/dependency-installer.d.ts +3 -2
  32. package/dist/utils/dependency-installer.d.ts.map +1 -1
  33. package/dist/utils/dependency-installer.js +8 -3
  34. package/dist/utils/detect-database.d.ts +11 -4
  35. package/dist/utils/detect-database.d.ts.map +1 -1
  36. package/dist/utils/detect-database.js +23 -5
  37. package/dist/utils/prompts.d.ts +6 -8
  38. package/dist/utils/prompts.d.ts.map +1 -1
  39. package/dist/utils/prompts.js +31 -24
  40. package/package.json +5 -4
  41. package/dist/utils/clickhouse-sql.d.ts +0 -6
  42. package/dist/utils/clickhouse-sql.d.ts.map +0 -1
  43. package/dist/utils/clickhouse-sql.js +0 -18
@@ -2,8 +2,9 @@ import { mkdir, writeFile, readFile, access } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import ora from 'ora';
4
4
  import { logger } from '../utils/logger.js';
5
- import { promptClickHouseConnection, promptOutputDirectory, promptInitStyle, promptGenerateExample, promptTableSelection, promptDatasetTableSelection, confirmOverwrite, promptRetry, promptContinueWithoutDb, } from '../utils/prompts.js';
5
+ import { promptClickHouseConnection, promptChdbStorage, promptOutputDirectory, promptInitStyle, promptGenerateExample, promptTableSelection, promptDatasetTableSelection, confirmOverwrite, promptRetry, promptContinueWithoutDb, } from '../utils/prompts.js';
6
6
  import { validateConnection, getTableCount, getTables, } from '../utils/detect-database.js';
7
+ import { ChdbNotInstalledError, ensureChdbInstalled, getChdbTypeGenerationClient, } from '../utils/chdb-client.js';
7
8
  import { hasEnvFile, hasGitignore } from '../utils/find-files.js';
8
9
  import { generateEnvTemplate, appendToEnv } from '../templates/env.js';
9
10
  import { generateClientTemplate } from '../templates/client.js';
@@ -14,6 +15,15 @@ import { appendToGitignore } from '../templates/gitignore.js';
14
15
  import { getTypeGenerator } from '../generators/index.js';
15
16
  import { generateDatasets } from '../generators/dataset-generator.js';
16
17
  import { installScaffoldDependencies } from '../utils/dependency-installer.js';
18
+ function normalizeInitDatabase(database) {
19
+ if (!database || database === 'clickhouse') {
20
+ return 'clickhouse';
21
+ }
22
+ if (database === 'chdb') {
23
+ return 'chdb';
24
+ }
25
+ throw new Error(`Unsupported database "${database}". Use "clickhouse" or "chdb".`);
26
+ }
17
27
  function normalizeInitStyle(style) {
18
28
  return style === 'datasets' ? 'datasets' : 'queries';
19
29
  }
@@ -33,6 +43,22 @@ function parseTableList(value) {
33
43
  .filter(Boolean);
34
44
  return parsed && parsed.length > 0 ? parsed : undefined;
35
45
  }
46
+ function getChdbGitignoreEntry(chdbPath) {
47
+ if (!chdbPath || /[\0\r\n]/.test(chdbPath)) {
48
+ return undefined;
49
+ }
50
+ const cwd = path.resolve(process.cwd());
51
+ const relativePath = path.relative(cwd, path.resolve(cwd, chdbPath));
52
+ if (relativePath.length === 0 ||
53
+ relativePath === '..' ||
54
+ relativePath.startsWith(`..${path.sep}`) ||
55
+ path.isAbsolute(relativePath)) {
56
+ return undefined;
57
+ }
58
+ const normalizedPath = relativePath.split(path.sep).join('/');
59
+ const escapedPath = normalizedPath.replace(/[\\*?[\]]/g, '\\$&');
60
+ return `/${escapedPath}/`;
61
+ }
36
62
  async function resolveConnectionConfig(options) {
37
63
  if (options.noInteractive) {
38
64
  const required = (keys) => {
@@ -52,6 +78,34 @@ async function resolveConnectionConfig(options) {
52
78
  }
53
79
  return promptClickHouseConnection();
54
80
  }
81
+ async function testChdbConnection(chdbPath) {
82
+ const spinner = ora('Starting embedded chDB...').start();
83
+ try {
84
+ await ensureChdbInstalled();
85
+ }
86
+ catch (error) {
87
+ const isNotInstalled = error instanceof ChdbNotInstalledError;
88
+ spinner.fail(isNotInstalled ? 'chdb is not installed' : 'Embedded chDB failed to load');
89
+ logger.newline();
90
+ logger.error(error instanceof Error ? error.message : String(error));
91
+ logger.newline();
92
+ return { ok: false, reason: isNotInstalled ? 'not-installed' : 'engine-error' };
93
+ }
94
+ const isValid = await validateConnection('chdb', { chdbPath });
95
+ if (!isValid) {
96
+ spinner.fail('Embedded chDB failed to run a query');
97
+ logger.newline();
98
+ logger.info('Common issues:');
99
+ logger.indent('• Unsupported platform (chdb ships linux/macOS binaries; Windows needs WSL2)');
100
+ logger.indent(`• The session directory is locked by another process${chdbPath ? ` (${chdbPath})` : ''}`);
101
+ logger.newline();
102
+ return { ok: false, reason: 'engine-error' };
103
+ }
104
+ const tableCount = await getTableCount('chdb', { chdbPath });
105
+ spinner.succeed(`Embedded chDB ready (${chdbPath ? `${tableCount} tables in ${chdbPath}` : 'in-memory session'})`);
106
+ logger.newline();
107
+ return { ok: true };
108
+ }
55
109
  async function testConnection(connectionConfig) {
56
110
  const spinner = ora('Testing connection...').start();
57
111
  process.env.CLICKHOUSE_URL = connectionConfig.host;
@@ -80,43 +134,58 @@ async function testConnection(connectionConfig) {
80
134
  }
81
135
  export async function initCommand(options = {}) {
82
136
  const noInteractive = options.noInteractive === true || options.interactive === false;
137
+ const database = normalizeInitDatabase(options.database);
83
138
  logger.newline();
84
139
  logger.header('Welcome to hypequery!');
85
- logger.info("Let's set up your analytics layer.");
140
+ logger.info(database === 'chdb'
141
+ ? "Let's set up your analytics layer on embedded ClickHouse (chDB)."
142
+ : "Let's set up your analytics layer.");
86
143
  logger.newline();
87
144
  // Step 2: Get connection details
88
- let connectionConfig = await resolveConnectionConfig(options);
145
+ let connectionConfig = null;
89
146
  let hasValidConnection = false;
90
- // Handle user skipping connection details
91
- if (!connectionConfig) {
92
- logger.info('Skipping database connection for now.');
93
- logger.newline();
94
- }
95
- else if (options.skipConnection) {
96
- logger.info('Skipping database connection test (requested).');
97
- logger.newline();
147
+ let chdbPath = options.chdbPath;
148
+ let chdbFailureReason;
149
+ if (database === 'chdb') {
150
+ // No server, no credentials — the only connection question is where the
151
+ // embedded session stores its data.
152
+ if (!chdbPath && !noInteractive) {
153
+ chdbPath = await promptChdbStorage();
154
+ }
98
155
  }
99
156
  else {
100
- const { hasValidConnection: valid } = await testConnection(connectionConfig);
101
- hasValidConnection = valid;
102
- if (!hasValidConnection) {
103
- if (noInteractive) {
104
- throw new Error('Failed to connect to ClickHouse in non-interactive mode. Check your environment variables or use interactive setup.');
105
- }
106
- const retry = await promptRetry('Try again?');
107
- if (retry) {
108
- return initCommand(options);
109
- }
110
- const continueWithout = await promptContinueWithoutDb();
111
- if (!continueWithout) {
112
- logger.info('Setup cancelled');
113
- process.exit(0);
114
- }
157
+ connectionConfig = await resolveConnectionConfig(options);
158
+ // Handle user skipping connection details
159
+ if (!connectionConfig) {
160
+ logger.info('Skipping database connection for now.');
115
161
  logger.newline();
116
- logger.info('Continuing without database connection.');
117
- logger.info('You can configure the connection later in .env');
162
+ }
163
+ else if (options.skipConnection) {
164
+ logger.info('Skipping database connection test (requested).');
118
165
  logger.newline();
119
- connectionConfig = null;
166
+ }
167
+ else {
168
+ const { hasValidConnection: valid } = await testConnection(connectionConfig);
169
+ hasValidConnection = valid;
170
+ if (!hasValidConnection) {
171
+ if (noInteractive) {
172
+ throw new Error('Failed to connect to ClickHouse in non-interactive mode. Check your environment variables or use interactive setup.');
173
+ }
174
+ const retry = await promptRetry('Try again?');
175
+ if (retry) {
176
+ return initCommand(options);
177
+ }
178
+ const continueWithout = await promptContinueWithoutDb();
179
+ if (!continueWithout) {
180
+ logger.info('Setup cancelled');
181
+ process.exit(0);
182
+ }
183
+ logger.newline();
184
+ logger.info('Continuing without database connection.');
185
+ logger.info('You can configure the connection later in .env');
186
+ logger.newline();
187
+ connectionConfig = null;
188
+ }
120
189
  }
121
190
  }
122
191
  // Step 4: Get output directory
@@ -166,6 +235,37 @@ export async function initCommand(options = {}) {
166
235
  }
167
236
  logger.newline();
168
237
  }
238
+ if (database === 'chdb') {
239
+ // All prompts and overwrite checks are complete, so installing packages
240
+ // here cannot leave a cancelled scaffold with unexpected dependencies.
241
+ await installScaffoldDependencies(style, 'chdb');
242
+ if (options.skipConnection) {
243
+ logger.info('Skipping embedded chDB test (requested).');
244
+ logger.newline();
245
+ }
246
+ else {
247
+ const chdbTest = await testChdbConnection(chdbPath);
248
+ hasValidConnection = chdbTest.ok;
249
+ if (!chdbTest.ok) {
250
+ chdbFailureReason = chdbTest.reason;
251
+ }
252
+ if (!hasValidConnection) {
253
+ if (noInteractive) {
254
+ throw new Error(chdbFailureReason === 'not-installed'
255
+ ? 'Embedded chDB failed to start in non-interactive mode. Install the chdb package and re-run.'
256
+ : 'Embedded chDB failed to start in non-interactive mode. Resolve the engine error shown above and re-run.');
257
+ }
258
+ const continueWithout = await promptContinueWithoutDb();
259
+ if (!continueWithout) {
260
+ logger.info('Setup cancelled');
261
+ process.exit(0);
262
+ }
263
+ logger.newline();
264
+ logger.info('Continuing without a working embedded engine.');
265
+ logger.newline();
266
+ }
267
+ }
268
+ }
169
269
  // Step 6: Ask about example query (only if we have a valid connection)
170
270
  let generateExample = !options.noExample && hasValidConnection;
171
271
  let selectedTable = null;
@@ -173,7 +273,7 @@ export async function initCommand(options = {}) {
173
273
  if (generateExample && !noInteractive && hasValidConnection) {
174
274
  generateExample = await promptGenerateExample();
175
275
  if (generateExample) {
176
- discoveredTables = await getTables('clickhouse');
276
+ discoveredTables = await getTables(database, { chdbPath });
177
277
  selectedTable = await promptTableSelection(discoveredTables);
178
278
  generateExample = selectedTable !== null;
179
279
  }
@@ -185,14 +285,19 @@ export async function initCommand(options = {}) {
185
285
  !options.allTables &&
186
286
  !datasetTables &&
187
287
  !noInteractive) {
188
- discoveredTables ??= await getTables('clickhouse');
288
+ discoveredTables ??= await getTables(database, { chdbPath });
189
289
  datasetTables = await promptDatasetTableSelection(discoveredTables, selectedTable ? [selectedTable] : []);
190
290
  }
191
291
  logger.newline();
192
292
  // Step 7: Create directory
193
293
  await mkdir(resolvedOutputDir, { recursive: true });
194
- // Step 8: Save credentials to .env (if we have connection config)
195
- if (connectionConfig) {
294
+ // Step 8: Save credentials to .env (if we have connection config).
295
+ // Embedded chDB has no credentials — the storage path lives in client.ts —
296
+ // so the chdb scaffold writes no .env at all.
297
+ if (database === 'chdb') {
298
+ // nothing to persist
299
+ }
300
+ else if (connectionConfig) {
196
301
  const envPath = path.join(process.cwd(), '.env');
197
302
  const envExists = await hasEnvFile();
198
303
  if (envExists) {
@@ -226,8 +331,8 @@ export async function initCommand(options = {}) {
226
331
  if (hasValidConnection) {
227
332
  const typeSpinner = ora('Generating TypeScript types...').start();
228
333
  try {
229
- const generator = getTypeGenerator('clickhouse');
230
- await generator({ outputPath: schemaPath });
334
+ const generator = getTypeGenerator(database);
335
+ await generator({ outputPath: schemaPath, chdbPath });
231
336
  typeSpinner.succeed(`Generated TypeScript types (${path.relative(process.cwd(), schemaPath)})`);
232
337
  }
233
338
  catch (error) {
@@ -238,8 +343,11 @@ export async function initCommand(options = {}) {
238
343
  }
239
344
  else {
240
345
  // Create placeholder schema file
346
+ const regenerateHint = database === 'chdb'
347
+ ? "// Run 'npx hypequery generate --database chdb --chdb-path <dir>' after creating persistent tables"
348
+ : "// Run 'npx hypequery generate' after configuring your database connection";
241
349
  await writeFile(schemaPath, `// Generated by hypequery
242
- // Run 'npx hypequery generate' after configuring your database connection
350
+ ${regenerateHint}
243
351
 
244
352
  export interface IntrospectedSchema {
245
353
  // Your table types will appear here after generation
@@ -249,8 +357,8 @@ export interface IntrospectedSchema {
249
357
  }
250
358
  // Step 10: Create client.ts
251
359
  const clientPath = path.join(resolvedOutputDir, 'client.ts');
252
- await writeFile(clientPath, generateClientTemplate());
253
- logger.success(`Created ClickHouse client (${path.relative(process.cwd(), clientPath)})`);
360
+ await writeFile(clientPath, generateClientTemplate({ database, chdbPath }));
361
+ logger.success(`Created ${database === 'chdb' ? 'embedded chDB' : 'ClickHouse'} client (${path.relative(process.cwd(), clientPath)})`);
254
362
  // Step 11: Create API entrypoint
255
363
  let apiPath;
256
364
  let generatedAnyDatasets = false;
@@ -264,6 +372,9 @@ export interface IntrospectedSchema {
264
372
  outputPath: datasetsPath,
265
373
  includeTables: options.allTables ? undefined : datasetTables,
266
374
  excludeTables: excludedDatasetTables,
375
+ ...(database === 'chdb'
376
+ ? { client: getChdbTypeGenerationClient(chdbPath) }
377
+ : {}),
267
378
  });
268
379
  generatedAnyDatasets = true;
269
380
  generatedSelectedDataset = selectedTable !== null && (options.allTables === true ||
@@ -295,20 +406,24 @@ export interface IntrospectedSchema {
295
406
  // Step 12: Update .gitignore
296
407
  const gitignorePath = path.join(process.cwd(), '.gitignore');
297
408
  const gitignoreExists = await hasGitignore();
409
+ const chdbGitignoreEntry = database === 'chdb'
410
+ ? getChdbGitignoreEntry(chdbPath)
411
+ : undefined;
412
+ const gitignoreEntries = chdbGitignoreEntry ? [chdbGitignoreEntry] : [];
298
413
  if (gitignoreExists) {
299
414
  const existingGitignore = await readFile(gitignorePath, 'utf-8');
300
- const newGitignore = appendToGitignore(existingGitignore);
415
+ const newGitignore = appendToGitignore(existingGitignore, gitignoreEntries);
301
416
  if (newGitignore !== existingGitignore) {
302
417
  await writeFile(gitignorePath, newGitignore);
303
418
  logger.success('Updated .gitignore');
304
419
  }
305
420
  }
306
421
  else {
307
- await writeFile(gitignorePath, appendToGitignore(''));
422
+ await writeFile(gitignorePath, appendToGitignore('', gitignoreEntries));
308
423
  logger.success('Created .gitignore');
309
424
  }
310
425
  // Step 13: Ensure required hypequery packages are installed
311
- await installScaffoldDependencies(style);
426
+ await installScaffoldDependencies(style, database);
312
427
  // Step 14: Success message
313
428
  logger.newline();
314
429
  logger.header('Setup complete!');
@@ -342,6 +457,24 @@ export interface IntrospectedSchema {
342
457
  logger.newline();
343
458
  }
344
459
  }
460
+ else if (database === 'chdb') {
461
+ logger.info('Next steps:');
462
+ logger.newline();
463
+ // chdb is normally installed by the scaffold itself — only tell the user
464
+ // to install when that is actually what failed, not when an installed
465
+ // engine could not run (unsupported platform, locked session directory).
466
+ const firstStep = options.skipConnection
467
+ ? '1. Verify the embedded engine and create your tables'
468
+ : chdbFailureReason === 'engine-error'
469
+ ? '1. Resolve the engine error shown above'
470
+ : '1. Install the embedded engine: npm install chdb';
471
+ logger.indent(firstStep);
472
+ logger.indent(chdbPath
473
+ ? `2. Run: npx hypequery generate --database chdb --chdb-path ${chdbPath}`
474
+ : '2. Re-run init with --chdb-path <dir> if later generate commands must see your tables');
475
+ logger.indent('3. Run: npx hypequery dev (to start dev server)');
476
+ logger.newline();
477
+ }
345
478
  else {
346
479
  logger.info('Next steps:');
347
480
  logger.newline();
@@ -350,6 +483,12 @@ export interface IntrospectedSchema {
350
483
  logger.indent('3. Run: npx hypequery dev (to start dev server)');
351
484
  logger.newline();
352
485
  }
486
+ if (database === 'chdb' && hasValidConnection) {
487
+ logger.indent(chdbPath
488
+ ? `hypequery generate --database chdb --chdb-path ${chdbPath} Refresh types after creating tables`
489
+ : 'In-memory chDB is process-local; use --chdb-path <dir> for later type generation');
490
+ logger.newline();
491
+ }
353
492
  logger.info('Docs: https://hypequery.com/docs');
354
493
  logger.newline();
355
494
  }
@@ -0,0 +1,13 @@
1
+ export interface ChdbGeneratorOptions {
2
+ outputPath: string;
3
+ includeTables?: string[];
4
+ excludeTables?: string[];
5
+ chdbPath?: string;
6
+ }
7
+ /**
8
+ * Same generator as ClickHouse — the introspection SQL (SHOW TABLES,
9
+ * DESCRIBE TABLE) is identical on the embedded engine — but the client seam
10
+ * is backed by a chDB session instead of an HTTP connection.
11
+ */
12
+ export declare function generateChdbTypes(options: ChdbGeneratorOptions): Promise<void>;
13
+ //# sourceMappingURL=chdb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chdb.d.ts","sourceRoot":"","sources":["../../src/generators/chdb.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,iBAUpE"}
@@ -0,0 +1,17 @@
1
+ import { generateTypes } from '@hypequery/clickhouse/cli';
2
+ import { getChdbTypeGenerationClient } from '../utils/chdb-client.js';
3
+ /**
4
+ * Same generator as ClickHouse — the introspection SQL (SHOW TABLES,
5
+ * DESCRIBE TABLE) is identical on the embedded engine — but the client seam
6
+ * is backed by a chDB session instead of an HTTP connection.
7
+ */
8
+ export async function generateChdbTypes(options) {
9
+ const generatorOptions = {
10
+ client: getChdbTypeGenerationClient(options.chdbPath),
11
+ generatedBy: 'hypequery',
12
+ includeUsageExample: false,
13
+ ...(options.includeTables ? { includeTables: options.includeTables } : {}),
14
+ ...(options.excludeTables ? { excludeTables: options.excludeTables } : {}),
15
+ };
16
+ await generateTypes(options.outputPath, generatorOptions);
17
+ }
@@ -4,10 +4,12 @@
4
4
  * Generates dataset DSL code from ClickHouse schema introspection.
5
5
  * This reduces quickstart friction by auto-scaffolding dataset definitions.
6
6
  */
7
+ import type { TypeGenerationClickHouseClient } from '@hypequery/clickhouse/cli';
7
8
  export interface DatasetGeneratorOptions {
8
9
  outputPath: string;
9
10
  includeTables?: string[];
10
11
  excludeTables?: string[];
12
+ client?: TypeGenerationClickHouseClient;
11
13
  }
12
14
  /**
13
15
  * Generate dataset definitions from ClickHouse schema
@@ -1 +1 @@
1
- {"version":3,"file":"dataset-generator.d.ts","sourceRoot":"","sources":["../../src/generators/dataset-generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAuPD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,iBAyDtE"}
1
+ {"version":3,"file":"dataset-generator.d.ts","sourceRoot":"","sources":["../../src/generators/dataset-generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,2BAA2B,CAAC;AAGhF,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,CAAC,EAAE,8BAA8B,CAAC;CACzC;AAuPD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,iBAyDtE"}
@@ -199,7 +199,7 @@ ${configLines.join('\n')}
199
199
  * Generate dataset definitions from ClickHouse schema
200
200
  */
201
201
  export async function generateDatasets(options) {
202
- const client = getClickHouseClient();
202
+ const client = options.client ?? getClickHouseClient();
203
203
  // Get all tables
204
204
  const tablesQuery = await client.query({
205
205
  query: 'SHOW TABLES',
@@ -1,6 +1,7 @@
1
1
  import type { DatabaseType } from '../utils/detect-database.js';
2
2
  import { type ClickHouseGeneratorOptions } from './clickhouse.js';
3
- export type TypeGeneratorOptions = ClickHouseGeneratorOptions;
3
+ import { type ChdbGeneratorOptions } from './chdb.js';
4
+ export type TypeGeneratorOptions = ClickHouseGeneratorOptions & ChdbGeneratorOptions;
4
5
  type GeneratorFn = (options: TypeGeneratorOptions) => Promise<void>;
5
6
  export declare function getTypeGenerator(dbType: DatabaseType): GeneratorFn;
6
7
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generators/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAA2B,KAAK,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAE3F,MAAM,MAAM,oBAAoB,GAAG,0BAA0B,CAAC;AAE9D,KAAK,WAAW,GAAG,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAMpE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW,CAYlE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generators/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAA2B,KAAK,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAC3F,OAAO,EAAqB,KAAK,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAEzE,MAAM,MAAM,oBAAoB,GAAG,0BAA0B,GAAG,oBAAoB,CAAC;AAErF,KAAK,WAAW,GAAG,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAOpE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW,CAYlE"}
@@ -1,6 +1,8 @@
1
1
  import { generateClickHouseTypes } from './clickhouse.js';
2
+ import { generateChdbTypes } from './chdb.js';
2
3
  const generators = {
3
4
  clickhouse: generateClickHouseTypes,
5
+ chdb: generateChdbTypes,
4
6
  };
5
7
  export function getTypeGenerator(dbType) {
6
8
  const generator = generators[dbType];
@@ -1,5 +1,10 @@
1
+ export interface ClientTemplateOptions {
2
+ database?: 'clickhouse' | 'chdb';
3
+ /** On-disk chDB session directory; omit for an in-memory session. */
4
+ chdbPath?: string;
5
+ }
1
6
  /**
2
- * Generate client.ts file for ClickHouse
7
+ * Generate client.ts file for ClickHouse (HTTP) or embedded chDB
3
8
  */
4
- export declare function generateClientTemplate(): string;
9
+ export declare function generateClientTemplate(options?: ClientTemplateOptions): string;
5
10
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/templates/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAW/C"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/templates/client.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IACjC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,qBAA0B,GAAG,MAAM,CAiClF"}
@@ -1,7 +1,28 @@
1
1
  /**
2
- * Generate client.ts file for ClickHouse
2
+ * Generate client.ts file for ClickHouse (HTTP) or embedded chDB
3
3
  */
4
- export function generateClientTemplate() {
4
+ export function generateClientTemplate(options = {}) {
5
+ if (options.database === 'chdb') {
6
+ // JSON.stringify produces a valid JS string literal for any path
7
+ // (quotes, backslashes) — raw interpolation would break the scaffold.
8
+ const sessionArg = options.chdbPath ? JSON.stringify(options.chdbPath) : '';
9
+ const safePathForComment = options.chdbPath?.replace(/[\r\n\u2028\u2029]/g, '') ?? '';
10
+ const storageComment = options.chdbPath
11
+ ? `// Embedded ClickHouse — data persists in ${safePathForComment}`
12
+ : '// Embedded ClickHouse — in-memory session (data is discarded on exit).\n// Pass a directory path to new Session(...) to persist between runs.';
13
+ return `import { createQueryBuilder } from '@hypequery/clickhouse';
14
+ import { Session } from 'chdb';
15
+ import { chdbAdapter } from 'chdb/hypequery';
16
+ import type { IntrospectedSchema } from './schema.js';
17
+
18
+ ${storageComment}
19
+ export const session = new Session(${sessionArg});
20
+
21
+ export const db = createQueryBuilder<IntrospectedSchema>({
22
+ adapter: chdbAdapter({ session }),
23
+ });
24
+ `;
25
+ }
5
26
  return `import { createQueryBuilder } from '@hypequery/clickhouse';
6
27
  import type { IntrospectedSchema } from './schema.js';
7
28
 
@@ -9,5 +9,5 @@ export declare function hasHypequeryEntries(content: string): boolean;
9
9
  /**
10
10
  * Append hypequery entries to .gitignore
11
11
  */
12
- export declare function appendToGitignore(existingContent: string): string;
12
+ export declare function appendToGitignore(existingContent: string, additionalEntries?: readonly string[]): string;
13
13
  //# sourceMappingURL=gitignore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gitignore.d.ts","sourceRoot":"","sources":["../../src/templates/gitignore.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,iBAAiB,0BAG7B,CAAC;AAEF;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAUjE"}
1
+ {"version":3,"file":"gitignore.d.ts","sourceRoot":"","sources":["../../src/templates/gitignore.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,iBAAiB,0BAG7B,CAAC;AAQF;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,eAAe,EAAE,MAAM,EACvB,iBAAiB,GAAE,SAAS,MAAM,EAAO,GACxC,MAAM,CAuBR"}
@@ -5,21 +5,35 @@ export const GITIGNORE_CONTENT = `
5
5
  # Hypequery
6
6
  .env
7
7
  `;
8
+ function hasEntry(content, entry) {
9
+ return content
10
+ .split(/\r?\n/)
11
+ .some((line) => line.trim() === entry);
12
+ }
8
13
  /**
9
14
  * Check if .gitignore already has hypequery entries
10
15
  */
11
16
  export function hasHypequeryEntries(content) {
12
- return content.includes('# Hypequery') || content.includes('.env');
17
+ return hasEntry(content, '# Hypequery') || hasEntry(content, '.env');
13
18
  }
14
19
  /**
15
20
  * Append hypequery entries to .gitignore
16
21
  */
17
- export function appendToGitignore(existingContent) {
18
- if (hasHypequeryEntries(existingContent)) {
22
+ export function appendToGitignore(existingContent, additionalEntries = []) {
23
+ const entries = ['.env', ...additionalEntries];
24
+ const missingEntries = entries.filter((entry, index) => entry.length > 0 && entries.indexOf(entry) === index && !hasEntry(existingContent, entry));
25
+ if (missingEntries.length === 0) {
19
26
  return existingContent;
20
27
  }
21
- if (!existingContent.endsWith('\n')) {
22
- existingContent += '\n';
28
+ let updatedContent = existingContent;
29
+ if (updatedContent.length > 0 && !updatedContent.endsWith('\n')) {
30
+ updatedContent += '\n';
31
+ }
32
+ if (!hasHypequeryEntries(existingContent)) {
33
+ if (updatedContent.length > 0 && !updatedContent.endsWith('\n\n')) {
34
+ updatedContent += '\n';
35
+ }
36
+ updatedContent += '# Hypequery\n';
23
37
  }
24
- return existingContent + GITIGNORE_CONTENT;
38
+ return `${updatedContent}${missingEntries.join('\n')}\n`;
25
39
  }
@@ -0,0 +1,36 @@
1
+ import type { TypeGenerationClickHouseClient } from '@hypequery/clickhouse/cli';
2
+ interface ChdbSession {
3
+ queryAsync(sql: string, options?: {
4
+ format?: string;
5
+ }): Promise<{
6
+ text(): string;
7
+ }>;
8
+ close(): void;
9
+ }
10
+ export declare class ChdbNotInstalledError extends Error {
11
+ readonly code = "CHDB_NOT_INSTALLED";
12
+ constructor();
13
+ }
14
+ /**
15
+ * Get (or create) the embedded chDB session. `path` selects the on-disk
16
+ * database directory; omit it for an ephemeral in-memory session. The session
17
+ * is cached per path — chDB allows one active data directory per process.
18
+ */
19
+ export declare function getChdbSession(dbPath?: string): Promise<ChdbSession>;
20
+ /**
21
+ * The type generator only needs `query({ query, format }) → { json() }` —
22
+ * the `TypeGenerationClickHouseClient` seam — so the embedded session can
23
+ * stand in for an HTTP client without touching the generator itself.
24
+ */
25
+ export declare function getChdbTypeGenerationClient(dbPath?: string): TypeGenerationClickHouseClient;
26
+ /**
27
+ * Throws the actionable install-hint error when `chdb` is missing. Init calls
28
+ * this before the connection test so "package not installed" surfaces as its
29
+ * own message instead of a generic connection failure.
30
+ */
31
+ export declare function ensureChdbInstalled(): Promise<void>;
32
+ export declare function validateChdb(dbPath?: string): Promise<boolean>;
33
+ export declare function getChdbTables(dbPath?: string): Promise<string[]>;
34
+ export declare function closeChdbSessionForTesting(): Promise<void>;
35
+ export {};
36
+ //# sourceMappingURL=chdb-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chdb-client.d.ts","sourceRoot":"","sources":["../../src/utils/chdb-client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,2BAA2B,CAAC;AAYhF,UAAU,WAAW;IACnB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,IAAI,MAAM,CAAA;KAAE,CAAC,CAAC;IACpF,KAAK,IAAI,IAAI,CAAC;CACf;AAKD,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,wBAAwB;;CAQtC;AA6CD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAY1E;AAYD;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,8BAA8B,CAQ3F;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEzD;AAED,wBAAsB,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOpE;AAED,wBAAsB,aAAa,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAOtE;AAED,wBAAsB,0BAA0B,kBAM/C"}