@hypequery/cli 1.3.0 → 1.4.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 (45) hide show
  1. package/README.md +27 -10
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +4 -1
  4. package/dist/commands/generate-datasets.d.ts.map +1 -1
  5. package/dist/commands/generate-datasets.js +2 -1
  6. package/dist/commands/generate.d.ts +1 -0
  7. package/dist/commands/generate.d.ts.map +1 -1
  8. package/dist/commands/generate.js +24 -4
  9. package/dist/commands/init.d.ts +2 -0
  10. package/dist/commands/init.d.ts.map +1 -1
  11. package/dist/commands/init.js +180 -41
  12. package/dist/generators/chdb.d.ts +13 -0
  13. package/dist/generators/chdb.d.ts.map +1 -0
  14. package/dist/generators/chdb.js +17 -0
  15. package/dist/generators/dataset-generator.d.ts +2 -0
  16. package/dist/generators/dataset-generator.d.ts.map +1 -1
  17. package/dist/generators/dataset-generator.js +1 -1
  18. package/dist/generators/index.d.ts +2 -1
  19. package/dist/generators/index.d.ts.map +1 -1
  20. package/dist/generators/index.js +2 -0
  21. package/dist/templates/client.d.ts +7 -2
  22. package/dist/templates/client.d.ts.map +1 -1
  23. package/dist/templates/client.js +23 -2
  24. package/dist/templates/gitignore.d.ts +1 -1
  25. package/dist/templates/gitignore.d.ts.map +1 -1
  26. package/dist/templates/gitignore.js +20 -6
  27. package/dist/utils/chdb-client.d.ts +36 -0
  28. package/dist/utils/chdb-client.d.ts.map +1 -0
  29. package/dist/utils/chdb-client.js +122 -0
  30. package/dist/utils/dependency-installer.d.ts +3 -2
  31. package/dist/utils/dependency-installer.d.ts.map +1 -1
  32. package/dist/utils/dependency-installer.js +8 -3
  33. package/dist/utils/detect-database.d.ts +11 -4
  34. package/dist/utils/detect-database.d.ts.map +1 -1
  35. package/dist/utils/detect-database.js +23 -5
  36. package/dist/utils/prompts.d.ts +6 -8
  37. package/dist/utils/prompts.d.ts.map +1 -1
  38. package/dist/utils/prompts.js +31 -24
  39. package/dist/utils/redact-connection-url.d.ts +6 -0
  40. package/dist/utils/redact-connection-url.d.ts.map +1 -0
  41. package/dist/utils/redact-connection-url.js +18 -0
  42. package/package.json +3 -3
  43. package/dist/utils/clickhouse-sql.d.ts +0 -6
  44. package/dist/utils/clickhouse-sql.d.ts.map +0 -1
  45. package/dist/utils/clickhouse-sql.js +0 -18
@@ -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"}
@@ -0,0 +1,122 @@
1
+ import { createRequire } from 'node:module';
2
+ import path from 'node:path';
3
+ let session = null;
4
+ let sessionPath;
5
+ export class ChdbNotInstalledError extends Error {
6
+ code = 'CHDB_NOT_INSTALLED';
7
+ constructor() {
8
+ super('The embedded driver needs the `chdb` package. Install it with `npm install chdb` (or your package manager\'s equivalent) and re-run.');
9
+ this.name = 'ChdbNotInstalledError';
10
+ }
11
+ }
12
+ function isModuleNotFound(error) {
13
+ return (error instanceof Error &&
14
+ 'code' in error &&
15
+ (error.code === 'MODULE_NOT_FOUND' ||
16
+ error.code === 'ERR_MODULE_NOT_FOUND'));
17
+ }
18
+ function resolveChdb(requireFn) {
19
+ try {
20
+ return requireFn.resolve('chdb');
21
+ }
22
+ catch (error) {
23
+ if (isModuleNotFound(error)) {
24
+ return undefined;
25
+ }
26
+ throw error;
27
+ }
28
+ }
29
+ async function loadChdb() {
30
+ // Resolve chdb from the USER'S project, not from where the CLI happens to
31
+ // be installed — under `npx` the CLI runs out of the npx cache, where a
32
+ // bare import('chdb') would never see the project's node_modules.
33
+ const requireFromProject = createRequire(path.join(process.cwd(), 'package.json'));
34
+ const projectChdb = resolveChdb(requireFromProject);
35
+ if (projectChdb) {
36
+ // Resolving first lets us distinguish an absent package from failures
37
+ // while loading an installed package (for example, a native ABI error).
38
+ return requireFromProject(projectChdb);
39
+ }
40
+ // Retain the fallback for installations where chdb is visible beside the
41
+ // CLI rather than from the project package.json.
42
+ const requireFromCli = createRequire(import.meta.url);
43
+ const cliChdb = resolveChdb(requireFromCli);
44
+ if (cliChdb) {
45
+ return requireFromCli(cliChdb);
46
+ }
47
+ throw new ChdbNotInstalledError();
48
+ }
49
+ /**
50
+ * Get (or create) the embedded chDB session. `path` selects the on-disk
51
+ * database directory; omit it for an ephemeral in-memory session. The session
52
+ * is cached per path — chDB allows one active data directory per process.
53
+ */
54
+ export async function getChdbSession(dbPath) {
55
+ if (session && sessionPath === dbPath) {
56
+ return session;
57
+ }
58
+ if (session) {
59
+ session.close();
60
+ session = null;
61
+ }
62
+ const chdb = await loadChdb();
63
+ session = new chdb.Session(dbPath);
64
+ sessionPath = dbPath;
65
+ return session;
66
+ }
67
+ async function queryJsonRows(sql, dbPath) {
68
+ const s = await getChdbSession(dbPath);
69
+ const result = await s.queryAsync(sql, { format: 'JSONEachRow' });
70
+ return result
71
+ .text()
72
+ .split('\n')
73
+ .filter((line) => line.trim() !== '')
74
+ .map((line) => JSON.parse(line));
75
+ }
76
+ /**
77
+ * The type generator only needs `query({ query, format }) → { json() }` —
78
+ * the `TypeGenerationClickHouseClient` seam — so the embedded session can
79
+ * stand in for an HTTP client without touching the generator itself.
80
+ */
81
+ export function getChdbTypeGenerationClient(dbPath) {
82
+ return {
83
+ async query({ query }) {
84
+ return {
85
+ json: async () => queryJsonRows(query, dbPath),
86
+ };
87
+ },
88
+ };
89
+ }
90
+ /**
91
+ * Throws the actionable install-hint error when `chdb` is missing. Init calls
92
+ * this before the connection test so "package not installed" surfaces as its
93
+ * own message instead of a generic connection failure.
94
+ */
95
+ export async function ensureChdbInstalled() {
96
+ await loadChdb();
97
+ }
98
+ export async function validateChdb(dbPath) {
99
+ try {
100
+ const rows = await queryJsonRows('SELECT 1 AS ok', dbPath);
101
+ return rows.length === 1;
102
+ }
103
+ catch {
104
+ return false;
105
+ }
106
+ }
107
+ export async function getChdbTables(dbPath) {
108
+ try {
109
+ const rows = await queryJsonRows('SHOW TABLES', dbPath);
110
+ return rows.map((row) => row.name);
111
+ }
112
+ catch {
113
+ return [];
114
+ }
115
+ }
116
+ export async function closeChdbSessionForTesting() {
117
+ if (session) {
118
+ session.close();
119
+ session = null;
120
+ sessionPath = undefined;
121
+ }
122
+ }
@@ -1,6 +1,7 @@
1
1
  type ScaffoldStyle = 'queries' | 'datasets';
2
- export declare function resolveScaffoldPackages(cliVersion: string | undefined, style?: ScaffoldStyle): string[];
3
- export declare function installScaffoldDependencies(style?: ScaffoldStyle): Promise<void>;
2
+ type ScaffoldDatabase = 'clickhouse' | 'chdb';
3
+ export declare function resolveScaffoldPackages(cliVersion: string | undefined, style?: ScaffoldStyle, database?: ScaffoldDatabase): string[];
4
+ export declare function installScaffoldDependencies(style?: ScaffoldStyle, database?: ScaffoldDatabase): Promise<void>;
4
5
  export declare const installServeDependencies: typeof installScaffoldDependencies;
5
6
  export {};
6
7
  //# sourceMappingURL=dependency-installer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dependency-installer.d.ts","sourceRoot":"","sources":["../../src/utils/dependency-installer.ts"],"names":[],"mappings":"AASA,KAAK,aAAa,GAAG,SAAS,GAAG,UAAU,CAAC;AA0F5C,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,GAAE,aAAyB,GAAG,MAAM,EAAE,CAgBlH;AAsBD,wBAAsB,2BAA2B,CAAC,KAAK,GAAE,aAAyB,iBAiDjF;AAED,eAAO,MAAM,wBAAwB,oCAA8B,CAAC"}
1
+ {"version":3,"file":"dependency-installer.d.ts","sourceRoot":"","sources":["../../src/utils/dependency-installer.ts"],"names":[],"mappings":"AAWA,KAAK,aAAa,GAAG,SAAS,GAAG,UAAU,CAAC;AAC5C,KAAK,gBAAgB,GAAG,YAAY,GAAG,MAAM,CAAC;AA0F9C,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,KAAK,GAAE,aAAyB,EAChC,QAAQ,GAAE,gBAA+B,GACxC,MAAM,EAAE,CAmBV;AAsBD,wBAAsB,2BAA2B,CAC/C,KAAK,GAAE,aAAyB,EAChC,QAAQ,GAAE,gBAA+B,iBAkD1C;AAED,eAAO,MAAM,wBAAwB,oCAA8B,CAAC"}
@@ -5,6 +5,8 @@ import { spawn } from 'node:child_process';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { logger } from './logger.js';
7
7
  const ZOD_SCAFFOLD_VERSION = '^3.23.8';
8
+ // chdb/hypequery (the embedded adapter subpath) ships from chdb 3.2.0.
9
+ const CHDB_SCAFFOLD_VERSION = '^3.2.0';
8
10
  const STABLE_SCAFFOLD_PACKAGES = ['@hypequery/clickhouse', '@hypequery/serve', `zod@${ZOD_SCAFFOLD_VERSION}`];
9
11
  const CLI_PACKAGE_PATH = fileURLToPath(new URL('../../package.json', import.meta.url));
10
12
  const MANUAL_COMMANDS = {
@@ -82,19 +84,22 @@ function getInstallArgs(manager, packages) {
82
84
  function formatManualCommand(manager, packages) {
83
85
  return `${MANUAL_COMMANDS[manager]} ${packages.join(' ')}`;
84
86
  }
85
- export function resolveScaffoldPackages(cliVersion, style = 'queries') {
87
+ export function resolveScaffoldPackages(cliVersion, style = 'queries', database = 'clickhouse') {
86
88
  const includeDatasets = style === 'datasets';
89
+ const includeChdb = database === 'chdb';
87
90
  if (cliVersion?.includes('canary')) {
88
91
  return [
89
92
  `@hypequery/clickhouse@${cliVersion}`,
90
93
  `@hypequery/serve@${cliVersion}`,
91
94
  ...(includeDatasets ? [`@hypequery/datasets@${cliVersion}`] : []),
92
95
  `zod@${ZOD_SCAFFOLD_VERSION}`,
96
+ ...(includeChdb ? [`chdb@${CHDB_SCAFFOLD_VERSION}`] : []),
93
97
  ];
94
98
  }
95
99
  return [
96
100
  ...STABLE_SCAFFOLD_PACKAGES,
97
101
  ...(includeDatasets ? ['@hypequery/datasets'] : []),
102
+ ...(includeChdb ? [`chdb@${CHDB_SCAFFOLD_VERSION}`] : []),
98
103
  ];
99
104
  }
100
105
  function shouldInstallPackage(pkgJson, specifier) {
@@ -112,7 +117,7 @@ function shouldInstallPackage(pkgJson, specifier) {
112
117
  const desiredVersion = specifier.slice(name.length + 1);
113
118
  return existingVersion !== desiredVersion;
114
119
  }
115
- export async function installScaffoldDependencies(style = 'queries') {
120
+ export async function installScaffoldDependencies(style = 'queries', database = 'clickhouse') {
116
121
  if (process.env.HYPEQUERY_SKIP_INSTALL === '1') {
117
122
  return;
118
123
  }
@@ -125,7 +130,7 @@ export async function installScaffoldDependencies(style = 'queries') {
125
130
  logger.warn(`package.json not found. Install @hypequery/clickhouse, @hypequery/serve${extra}, and zod manually.`);
126
131
  return;
127
132
  }
128
- const requestedPackages = resolveScaffoldPackages(cliPkgJson?.version, style);
133
+ const requestedPackages = resolveScaffoldPackages(cliPkgJson?.version, style, database);
129
134
  const missing = requestedPackages.filter(pkg => shouldInstallPackage(pkgJson, pkg));
130
135
  if (missing.length === 0) {
131
136
  return;
@@ -1,7 +1,14 @@
1
1
  /**
2
2
  * Database type detection result
3
3
  */
4
- export type DatabaseType = 'clickhouse' | 'bigquery' | 'unknown';
4
+ export type DatabaseType = 'clickhouse' | 'chdb' | 'bigquery' | 'unknown';
5
+ /**
6
+ * Options threaded through to drivers that need more than env vars —
7
+ * currently only the embedded chDB session directory.
8
+ */
9
+ export interface DatabaseOptions {
10
+ chdbPath?: string;
11
+ }
5
12
  /**
6
13
  * Auto-detect database type from environment or config files
7
14
  */
@@ -9,13 +16,13 @@ export declare function detectDatabase(): Promise<DatabaseType>;
9
16
  /**
10
17
  * Validate database connection
11
18
  */
12
- export declare function validateConnection(dbType: DatabaseType): Promise<boolean>;
19
+ export declare function validateConnection(dbType: DatabaseType, options?: DatabaseOptions): Promise<boolean>;
13
20
  /**
14
21
  * Get table count from database
15
22
  */
16
- export declare function getTableCount(dbType: DatabaseType): Promise<number>;
23
+ export declare function getTableCount(dbType: DatabaseType, options?: DatabaseOptions): Promise<number>;
17
24
  /**
18
25
  * Get list of tables from database
19
26
  */
20
- export declare function getTables(dbType: DatabaseType): Promise<string[]>;
27
+ export declare function getTables(dbType: DatabaseType, options?: DatabaseOptions): Promise<string[]>;
21
28
  //# sourceMappingURL=detect-database.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"detect-database.d.ts","sourceRoot":"","sources":["../../src/utils/detect-database.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,UAAU,GAAG,SAAS,CAAC;AAEjE;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC,CAyC5D;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAS/E;AAuBD;;GAEG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAOzE;AAyBD;;GAEG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAOvE"}
1
+ {"version":3,"file":"detect-database.d.ts","sourceRoot":"","sources":["../../src/utils/detect-database.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;AAE1E;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC,CAuD5D;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,OAAO,CAAC,CAWlB;AAuBD;;GAEG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,MAAM,CAAC,CASjB;AAyBD;;GAEG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,MAAM,EAAE,CAAC,CASnB"}
@@ -1,6 +1,7 @@
1
- import { access } from 'node:fs/promises';
1
+ import { access, readFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { getClickHouseClient } from './clickhouse-client.js';
4
+ import { validateChdb, getChdbTables } from './chdb-client.js';
4
5
  /**
5
6
  * Auto-detect database type from environment or config files
6
7
  */
@@ -18,7 +19,6 @@ export async function detectDatabase() {
18
19
  try {
19
20
  const envPath = path.join(process.cwd(), '.env');
20
21
  await access(envPath);
21
- const { readFile } = await import('node:fs/promises');
22
22
  const envContent = await readFile(envPath, 'utf-8');
23
23
  if (envContent.includes('CLICKHOUSE_') ||
24
24
  envContent.includes('CLICKHOUSE_HOST')) {
@@ -32,15 +32,29 @@ export async function detectDatabase() {
32
32
  catch {
33
33
  // .env doesn't exist, continue
34
34
  }
35
+ // A project that depends on chdb but has no ClickHouse connection config is
36
+ // running on the embedded engine.
37
+ try {
38
+ const pkgContent = await readFile(path.join(process.cwd(), 'package.json'), 'utf-8');
39
+ const pkg = JSON.parse(pkgContent);
40
+ if (pkg.dependencies?.chdb || pkg.devDependencies?.chdb) {
41
+ return 'chdb';
42
+ }
43
+ }
44
+ catch {
45
+ // package.json missing or unparsable, continue
46
+ }
35
47
  return 'unknown';
36
48
  }
37
49
  /**
38
50
  * Validate database connection
39
51
  */
40
- export async function validateConnection(dbType) {
52
+ export async function validateConnection(dbType, options = {}) {
41
53
  switch (dbType) {
42
54
  case 'clickhouse':
43
55
  return validateClickHouse();
56
+ case 'chdb':
57
+ return validateChdb(options.chdbPath);
44
58
  case 'bigquery':
45
59
  return validateBigQuery();
46
60
  default:
@@ -68,10 +82,12 @@ async function validateBigQuery() {
68
82
  /**
69
83
  * Get table count from database
70
84
  */
71
- export async function getTableCount(dbType) {
85
+ export async function getTableCount(dbType, options = {}) {
72
86
  switch (dbType) {
73
87
  case 'clickhouse':
74
88
  return getClickHouseTableCount();
89
+ case 'chdb':
90
+ return (await getChdbTables(options.chdbPath)).length;
75
91
  default:
76
92
  return 0;
77
93
  }
@@ -99,10 +115,12 @@ async function getClickHouseTableCount() {
99
115
  /**
100
116
  * Get list of tables from database
101
117
  */
102
- export async function getTables(dbType) {
118
+ export async function getTables(dbType, options = {}) {
103
119
  switch (dbType) {
104
120
  case 'clickhouse':
105
121
  return getClickHouseTables();
122
+ case 'chdb':
123
+ return getChdbTables(options.chdbPath);
106
124
  default:
107
125
  return [];
108
126
  }
@@ -7,6 +7,12 @@ export declare function promptClickHouseConnection(): Promise<{
7
7
  username: string;
8
8
  password: string;
9
9
  } | null>;
10
+ /**
11
+ * Prompt for embedded chDB storage: ephemeral in-memory session, or an
12
+ * on-disk directory that persists between runs. Returns the session path,
13
+ * or undefined for in-memory.
14
+ */
15
+ export declare function promptChdbStorage(): Promise<string | undefined>;
10
16
  /**
11
17
  * Prompt for output directory
12
18
  */
@@ -37,12 +43,4 @@ export declare function promptRetry(message: string): Promise<boolean>;
37
43
  * Ask if user wants to continue without DB connection
38
44
  */
39
45
  export declare function promptContinueWithoutDb(): Promise<boolean>;
40
- /**
41
- * Confirm destructive operations (DROP TABLE, DROP COLUMN)
42
- */
43
- export declare function confirmDestructiveOperation(operations: string[]): Promise<boolean>;
44
- /**
45
- * Confirm mutation operations (type changes that trigger ClickHouse mutations)
46
- */
47
- export declare function confirmMutationOperation(): Promise<boolean>;
48
46
  //# sourceMappingURL=prompts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,wBAAsB,0BAA0B,IAAI,OAAO,CAAC;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,CAAC,CAkCR;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CA6B7D;AAED,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAE/C,wBAAsB,eAAe,IAAI,OAAO,CAAC,SAAS,CAAC,CAa1D;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAS9D;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0BnF;AAED;;GAEG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,MAAM,EAAE,EAChB,aAAa,GAAE,MAAM,EAAO,GAC3B,OAAO,CAAC,MAAM,EAAE,CAAC,CA2BnB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CASxE;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASnE;AAED;;GAEG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC,CAShE;AAED;;GAEG;AACH,wBAAsB,2BAA2B,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CASxF;AAED;;GAEG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,OAAO,CAAC,CASjE"}
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,wBAAsB,0BAA0B,IAAI,OAAO,CAAC;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,CAAC,CAkCR;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA4BrE;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CA6B7D;AAED,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAE/C,wBAAsB,eAAe,IAAI,OAAO,CAAC,SAAS,CAAC,CAa1D;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAS9D;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0BnF;AAED;;GAEG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,MAAM,EAAE,EAChB,aAAa,GAAE,MAAM,EAAO,GAC3B,OAAO,CAAC,MAAM,EAAE,CAAC,CA2BnB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CASxE;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASnE;AAED;;GAEG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC,CAShE"}
@@ -39,6 +39,37 @@ export async function promptClickHouseConnection() {
39
39
  }
40
40
  return response;
41
41
  }
42
+ /**
43
+ * Prompt for embedded chDB storage: ephemeral in-memory session, or an
44
+ * on-disk directory that persists between runs. Returns the session path,
45
+ * or undefined for in-memory.
46
+ */
47
+ export async function promptChdbStorage() {
48
+ const response = await prompts({
49
+ type: 'select',
50
+ name: 'storage',
51
+ message: 'Where should embedded chDB store data?',
52
+ choices: [
53
+ { title: 'In-memory (discarded on exit)', value: 'memory' },
54
+ { title: 'Local directory (./analytics.chdb)', value: 'file' },
55
+ { title: 'Custom path...', value: 'custom' },
56
+ ],
57
+ initial: 0,
58
+ });
59
+ if (!response.storage || response.storage === 'memory') {
60
+ return undefined;
61
+ }
62
+ if (response.storage === 'custom') {
63
+ const customResponse = await prompts({
64
+ type: 'text',
65
+ name: 'path',
66
+ message: 'Enter chDB data directory:',
67
+ initial: './analytics.chdb',
68
+ });
69
+ return customResponse.path || './analytics.chdb';
70
+ }
71
+ return './analytics.chdb';
72
+ }
42
73
  /**
43
74
  * Prompt for output directory
44
75
  */
@@ -183,27 +214,3 @@ export async function promptContinueWithoutDb() {
183
214
  });
184
215
  return response.continue ?? false;
185
216
  }
186
- /**
187
- * Confirm destructive operations (DROP TABLE, DROP COLUMN)
188
- */
189
- export async function confirmDestructiveOperation(operations) {
190
- const response = await prompts({
191
- type: 'confirm',
192
- name: 'confirm',
193
- message: `This migration includes ${operations.length} destructive operation(s). Continue?`,
194
- initial: false,
195
- });
196
- return response.confirm ?? false;
197
- }
198
- /**
199
- * Confirm mutation operations (type changes that trigger ClickHouse mutations)
200
- */
201
- export async function confirmMutationOperation() {
202
- const response = await prompts({
203
- type: 'confirm',
204
- name: 'confirm',
205
- message: 'This migration will trigger ClickHouse mutations. Continue?',
206
- initial: false,
207
- });
208
- return response.confirm ?? false;
209
- }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Returns enough endpoint information for diagnostics without exposing URL
3
+ * userinfo, query parameters, or fragments that may contain credentials.
4
+ */
5
+ export declare function redactConnectionUrl(value: string | undefined): string;
6
+ //# sourceMappingURL=redact-connection-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact-connection-url.d.ts","sourceRoot":"","sources":["../../src/utils/redact-connection-url.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAarE"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Returns enough endpoint information for diagnostics without exposing URL
3
+ * userinfo, query parameters, or fragments that may contain credentials.
4
+ */
5
+ export function redactConnectionUrl(value) {
6
+ if (!value)
7
+ return 'not set';
8
+ const trimmed = value.trim();
9
+ const hasScheme = /^[a-z][a-z\d+.-]*:\/\//i.test(trimmed);
10
+ try {
11
+ const url = new URL(hasScheme ? trimmed : `http://${trimmed}`);
12
+ const pathname = url.pathname === '/' ? '' : url.pathname;
13
+ return `${hasScheme ? `${url.protocol}//` : ''}${url.host}${pathname}`;
14
+ }
15
+ catch {
16
+ return '[configured connection URL]';
17
+ }
18
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypequery/cli",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Command-line interface for hypequery",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -35,8 +35,8 @@
35
35
  "@vitest/coverage-v8": "^3.2.6",
36
36
  "typescript": "^5.7.3",
37
37
  "vitest": "^3.2.6",
38
- "@hypequery/clickhouse": "2.1.2",
39
- "@hypequery/serve": "0.6.0"
38
+ "@hypequery/clickhouse": "2.4.0",
39
+ "@hypequery/serve": "0.11.0"
40
40
  },
41
41
  "repository": {
42
42
  "type": "git",
@@ -1,6 +0,0 @@
1
- export declare function quoteIdentifier(identifier: string): string;
2
- export declare function assertValidIdentifier(identifier: string): void;
3
- export declare function sqlString(value: string): string;
4
- export declare function sqlStringArray(values: string[]): string;
5
- export declare function sqlDateTime(value: Date): string;
6
- //# sourceMappingURL=clickhouse-sql.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"clickhouse-sql.d.ts","sourceRoot":"","sources":["../../src/utils/clickhouse-sql.ts"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,QAIvD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,UAEtC;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,UAE9C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,IAAI,UAEtC"}