@hypequery/cli 1.3.1 → 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.
- package/README.md +27 -10
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -1
- package/dist/commands/generate.d.ts +1 -0
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +22 -3
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +180 -41
- package/dist/generators/chdb.d.ts +13 -0
- package/dist/generators/chdb.d.ts.map +1 -0
- package/dist/generators/chdb.js +17 -0
- package/dist/generators/dataset-generator.d.ts +2 -0
- package/dist/generators/dataset-generator.d.ts.map +1 -1
- package/dist/generators/dataset-generator.js +1 -1
- package/dist/generators/index.d.ts +2 -1
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +2 -0
- package/dist/templates/client.d.ts +7 -2
- package/dist/templates/client.d.ts.map +1 -1
- package/dist/templates/client.js +23 -2
- package/dist/templates/gitignore.d.ts +1 -1
- package/dist/templates/gitignore.d.ts.map +1 -1
- package/dist/templates/gitignore.js +20 -6
- package/dist/utils/chdb-client.d.ts +36 -0
- package/dist/utils/chdb-client.d.ts.map +1 -0
- package/dist/utils/chdb-client.js +122 -0
- package/dist/utils/dependency-installer.d.ts +3 -2
- package/dist/utils/dependency-installer.d.ts.map +1 -1
- package/dist/utils/dependency-installer.js +8 -3
- package/dist/utils/detect-database.d.ts +11 -4
- package/dist/utils/detect-database.d.ts.map +1 -1
- package/dist/utils/detect-database.js +23 -5
- package/dist/utils/prompts.d.ts +6 -8
- package/dist/utils/prompts.d.ts.map +1 -1
- package/dist/utils/prompts.js +31 -24
- package/package.json +3 -3
- package/dist/utils/clickhouse-sql.d.ts +0 -6
- package/dist/utils/clickhouse-sql.d.ts.map +0 -1
- package/dist/utils/clickhouse-sql.js +0 -18
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ CLI for scaffolding and running the main hypequery path.
|
|
|
4
4
|
|
|
5
5
|
Use it to:
|
|
6
6
|
|
|
7
|
-
- generate schema types from ClickHouse
|
|
7
|
+
- generate schema types from ClickHouse or embedded chDB
|
|
8
8
|
- scaffold `analytics/` files
|
|
9
9
|
- run the local dev server with docs
|
|
10
10
|
|
|
@@ -36,27 +36,36 @@ npx hypequery init
|
|
|
36
36
|
|
|
37
37
|
It will:
|
|
38
38
|
|
|
39
|
-
-
|
|
39
|
+
- connect to ClickHouse or start an embedded chDB session
|
|
40
|
+
- generate schema types when the database is available
|
|
40
41
|
- create client and query files
|
|
41
|
-
- write `.env` values
|
|
42
|
-
- update `.gitignore
|
|
43
|
-
- install scaffold dependencies, including `zod`
|
|
42
|
+
- write `.env` values for ClickHouse connections
|
|
43
|
+
- update `.gitignore`, including a project-local persistent chDB directory
|
|
44
|
+
- install scaffold dependencies, including `zod` and the selected database adapter
|
|
44
45
|
|
|
45
46
|
Options:
|
|
46
47
|
|
|
47
48
|
- `--path <path>`: output directory, default `analytics/`
|
|
48
49
|
- `--style <style>`: `queries` (default) or `datasets`
|
|
50
|
+
- `--database <type>`: `clickhouse` (default) or `chdb`
|
|
51
|
+
- `--chdb-path <path>`: persistent chDB data directory; omit for an in-memory session
|
|
49
52
|
- `--auth <mode>`: `none` (default) or `context`
|
|
50
53
|
- `--all-tables`: with `--style datasets`, scaffold every table
|
|
51
54
|
- `--tables <names>`: with `--style datasets`, scaffold these comma-separated tables
|
|
52
55
|
- `--exclude-tables <names>`: with `--style datasets`, exclude these comma-separated tables
|
|
53
56
|
- `--no-example`: skip the example query
|
|
54
|
-
- `--no-interactive`:
|
|
57
|
+
- `--no-interactive`: skip prompts; ClickHouse connection details come from env vars
|
|
55
58
|
- `--force`: overwrite existing scaffold files
|
|
56
|
-
- `--skip-connection`: skip testing the
|
|
59
|
+
- `--skip-connection`: skip testing the selected database before scaffolding
|
|
57
60
|
|
|
58
61
|
Set `HYPEQUERY_SKIP_INSTALL=1` to skip the automatic dependency install.
|
|
59
62
|
|
|
63
|
+
To scaffold against persistent embedded chDB without server credentials:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx hypequery init --database chdb --chdb-path ./analytics.chdb --no-interactive
|
|
67
|
+
```
|
|
68
|
+
|
|
60
69
|
### `hypequery dev`
|
|
61
70
|
|
|
62
71
|
Runs the local serve runtime with docs and hot reload.
|
|
@@ -78,7 +87,7 @@ The CLI understands TypeScript entry files directly, so `analytics/queries.ts` w
|
|
|
78
87
|
|
|
79
88
|
### `hypequery generate`
|
|
80
89
|
|
|
81
|
-
Regenerates schema types from ClickHouse.
|
|
90
|
+
Regenerates schema types from ClickHouse or embedded chDB.
|
|
82
91
|
|
|
83
92
|
```bash
|
|
84
93
|
npx hypequery generate
|
|
@@ -89,7 +98,14 @@ Options:
|
|
|
89
98
|
- `--output <path>`: default `analytics/schema.ts`
|
|
90
99
|
- `--path <path>`: analytics directory (derives `<path>/schema.ts`)
|
|
91
100
|
- `--tables <names>`: comma-separated table list
|
|
92
|
-
- `--database <type>`:
|
|
101
|
+
- `--database <type>`: `clickhouse` or `chdb`; chDB generation must be selected explicitly
|
|
102
|
+
- `--chdb-path <path>`: persistent chDB data directory; omit for an in-memory session
|
|
103
|
+
|
|
104
|
+
For a persistent chDB scaffold, pass the same path used by `init`:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npx hypequery generate --database chdb --chdb-path ./analytics.chdb
|
|
108
|
+
```
|
|
93
109
|
|
|
94
110
|
`hypequery generate:types` is an alias for `hypequery generate`.
|
|
95
111
|
|
|
@@ -121,7 +137,7 @@ semantic keys such as `dataset:orders`.
|
|
|
121
137
|
|
|
122
138
|
## Non-interactive Setup
|
|
123
139
|
|
|
124
|
-
`hypequery init --no-interactive` reads:
|
|
140
|
+
For ClickHouse, `hypequery init --no-interactive` reads:
|
|
125
141
|
|
|
126
142
|
- `CLICKHOUSE_URL` or deprecated `CLICKHOUSE_HOST`
|
|
127
143
|
- `CLICKHOUSE_DATABASE`
|
|
@@ -133,6 +149,7 @@ semantic keys such as `dataset:orders`.
|
|
|
133
149
|
- generated scaffold files use NodeNext-safe local `.js` imports
|
|
134
150
|
- `CLICKHOUSE_URL` is now the preferred connection variable
|
|
135
151
|
- the CLI bundles the ClickHouse driver for schema generation
|
|
152
|
+
- chDB runs in memory unless `--chdb-path` is provided; persistent paths must be reused by later `generate` commands
|
|
136
153
|
|
|
137
154
|
## Docs
|
|
138
155
|
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AAa9B,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;EAKpE;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AAa9B,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;EAKpE;AAmID,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -43,7 +43,8 @@ function addTypeGenerationOptions(command) {
|
|
|
43
43
|
.option('-o, --output <path>', 'Output file (default: analytics/schema.ts)')
|
|
44
44
|
.option('--path <path>', 'Analytics directory (derives <path>/schema.ts)')
|
|
45
45
|
.option('--tables <names>', 'Only generate for specific tables (comma-separated)')
|
|
46
|
-
.option('--database <type>', 'Database driver to use (default: auto-detect)')
|
|
46
|
+
.option('--database <type>', 'Database driver to use: clickhouse or chdb (default: auto-detect)')
|
|
47
|
+
.option('--chdb-path <path>', 'Embedded chDB data directory (with --database chdb; omit for in-memory)');
|
|
47
48
|
}
|
|
48
49
|
// Init command
|
|
49
50
|
program
|
|
@@ -51,6 +52,8 @@ program
|
|
|
51
52
|
.description('Initialize a new hypequery project')
|
|
52
53
|
.option('--path <path>', 'Output directory (default: analytics/)')
|
|
53
54
|
.option('--style <style>', 'Scaffold style: queries or datasets')
|
|
55
|
+
.option('--database <type>', 'Database driver: clickhouse (default) or chdb for embedded, zero-server ClickHouse')
|
|
56
|
+
.option('--chdb-path <path>', 'Embedded chDB data directory (with --database chdb; omit for in-memory)')
|
|
54
57
|
.option('--auth <mode>', 'Auth scaffold mode: none or context')
|
|
55
58
|
.option('--all-tables', 'Generate datasets for all discovered tables when using --style datasets')
|
|
56
59
|
.option('--tables <names>', 'Generate datasets for specific tables when using --style datasets (comma-separated)')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiC,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiC,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAK/F,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,eAAe,CAAC,OAAO,GAAE,eAAoB,iBAoJlE"}
|
|
@@ -3,6 +3,7 @@ import ora from 'ora';
|
|
|
3
3
|
import { logger } from '../utils/logger.js';
|
|
4
4
|
import { findSchemaFile } from '../utils/find-files.js';
|
|
5
5
|
import { detectDatabase, getTableCount } from '../utils/detect-database.js';
|
|
6
|
+
import { ensureChdbInstalled } from '../utils/chdb-client.js';
|
|
6
7
|
import { getTypeGenerator } from '../generators/index.js';
|
|
7
8
|
import { redactConnectionUrl } from '../utils/redact-connection-url.js';
|
|
8
9
|
export async function generateCommand(options = {}) {
|
|
@@ -33,20 +34,38 @@ export async function generateCommand(options = {}) {
|
|
|
33
34
|
: undefined;
|
|
34
35
|
const requestedDbType = options.database;
|
|
35
36
|
const dbType = requestedDbType ?? (await detectDatabase());
|
|
37
|
+
const chdbPath = dbType === 'chdb' ? options.chdbPath : undefined;
|
|
38
|
+
if (dbType === 'chdb' && !requestedDbType) {
|
|
39
|
+
throw new Error('chDB generation must be explicit. Re-run with --database chdb and add --chdb-path <dir> for persistent storage.');
|
|
40
|
+
}
|
|
36
41
|
logger.newline();
|
|
37
42
|
logger.header(options.commandName ?? 'hypequery generate');
|
|
38
43
|
const spinner = ora(`Connecting to ${dbType}...`).start();
|
|
44
|
+
let activeSpinner = spinner;
|
|
45
|
+
let failureMessage = dbType === 'chdb'
|
|
46
|
+
? 'Failed to start embedded chDB'
|
|
47
|
+
: 'Failed to generate types';
|
|
39
48
|
try {
|
|
40
49
|
const generator = getTypeGenerator(dbType);
|
|
50
|
+
// Surface a missing chdb install as the connection failure it is —
|
|
51
|
+
// getTableCount swallows errors, so without this probe the spinner
|
|
52
|
+
// would report a successful connection and 0 tables before type
|
|
53
|
+
// generation fails.
|
|
54
|
+
if (dbType === 'chdb') {
|
|
55
|
+
await ensureChdbInstalled();
|
|
56
|
+
}
|
|
41
57
|
// Get table count
|
|
42
|
-
const tableCount = await getTableCount(dbType);
|
|
43
|
-
spinner.succeed(`Connected to ${dbType === 'clickhouse' ? 'ClickHouse' : dbType}`);
|
|
58
|
+
const tableCount = await getTableCount(dbType, { chdbPath });
|
|
59
|
+
spinner.succeed(`Connected to ${dbType === 'clickhouse' ? 'ClickHouse' : dbType === 'chdb' ? 'embedded chDB' : dbType}`);
|
|
44
60
|
logger.success(`Found ${tableCount} tables`);
|
|
45
61
|
// Generate types
|
|
46
62
|
const typeSpinner = ora('Generating types...').start();
|
|
63
|
+
activeSpinner = typeSpinner;
|
|
64
|
+
failureMessage = 'Failed to generate types';
|
|
47
65
|
await generator({
|
|
48
66
|
outputPath,
|
|
49
67
|
includeTables: parsedTables,
|
|
68
|
+
chdbPath,
|
|
50
69
|
});
|
|
51
70
|
typeSpinner.succeed(`Generated types for ${tableCount} tables`);
|
|
52
71
|
logger.success(`Updated ${path.relative(process.cwd(), outputPath)}`);
|
|
@@ -55,7 +74,7 @@ export async function generateCommand(options = {}) {
|
|
|
55
74
|
logger.newline();
|
|
56
75
|
}
|
|
57
76
|
catch (error) {
|
|
58
|
-
|
|
77
|
+
activeSpinner.fail(failureMessage);
|
|
59
78
|
logger.newline();
|
|
60
79
|
if (error instanceof Error) {
|
|
61
80
|
logger.error(error.message);
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAIA,OAAO,EAWL,KAAK,SAAS,EACf,MAAM,qBAAqB,CAAC;AAe7B,OAAO,EAA2B,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAQzF,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AA4JD,wBAAsB,WAAW,CAAC,OAAO,GAAE,WAAgB,iBAyZ1D"}
|
package/dist/commands/init.js
CHANGED
|
@@ -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(
|
|
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 =
|
|
145
|
+
let connectionConfig = null;
|
|
89
146
|
let hasValidConnection = false;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
if (!
|
|
103
|
-
|
|
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
|
-
|
|
117
|
-
|
|
162
|
+
}
|
|
163
|
+
else if (options.skipConnection) {
|
|
164
|
+
logger.info('Skipping database connection test (requested).');
|
|
118
165
|
logger.newline();
|
|
119
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
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;
|
|
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"}
|
package/dist/generators/index.js
CHANGED
|
@@ -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];
|