@hypequery/cli 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,211 +1,109 @@
1
1
  # @hypequery/cli
2
2
 
3
- Command-line interface for Hypequery - the type-safe analytics layer for ClickHouse.
3
+ CLI for scaffolding and running the main hypequery path.
4
4
 
5
- ## Quick Start
5
+ Use it to:
6
6
 
7
- The CLI scaffolds and runs the main hypequery path:
7
+ - generate schema types from ClickHouse
8
+ - scaffold `analytics/` files
9
+ - run the local dev server with docs
8
10
 
9
- 1. Generate schema types from ClickHouse
10
- 2. Build queries with the typed query builder
11
- 3. Wrap reusable queries with `query({ ... })`
12
- 4. Add `serve({ queries })` when you want HTTP routes and docs
11
+ ## Quick Start
13
12
 
14
- Use `npx` to run commands directly:
13
+ Run it directly:
15
14
 
16
15
  ```bash
17
- # Initialize a new project
18
16
  npx @hypequery/cli init
19
-
20
- # Start development server
21
17
  npx @hypequery/cli dev
22
-
23
- # Generate types from database
24
18
  npx @hypequery/cli generate
25
19
  ```
26
20
 
27
- ## Installation (Optional)
28
-
29
- For frequent use, install as a dev dependency:
21
+ Or install it once:
30
22
 
31
23
  ```bash
32
24
  npm install -D @hypequery/cli
33
- # or
34
- pnpm add -D @hypequery/cli
35
- # or
36
- yarn add -D @hypequery/cli
37
25
  ```
38
26
 
39
- Then use the shorter `hypequery` command:
40
-
41
- ```bash
42
- npx hypequery dev
43
- ```
44
-
45
- Or add to your `package.json` scripts:
46
-
47
- ```json
48
- {
49
- "scripts": {
50
- "hypequery:init": "hypequery init",
51
- "hypequery:dev": "hypequery dev",
52
- "hypequery:generate": "hypequery generate"
53
- }
54
- }
55
- ```
56
-
57
- ## TypeScript Support
58
-
59
- `hypequery dev` bundles a TypeScript runtime (powered by `tsx`), so pointing it at `analytics/queries.ts` or any `.ts/.tsx` file just works—no extra install or custom runner required. If your project already compiles to JavaScript you can keep targeting the generated `.js` file instead.
60
-
61
27
  ## Commands
62
28
 
63
29
  ### `hypequery init`
64
30
 
65
- Interactive setup wizard that scaffolds a new Hypequery project.
31
+ Scaffolds the standard hypequery setup.
66
32
 
67
33
  ```bash
68
- # Without installation
69
- npx @hypequery/cli init
70
-
71
- # With installation
72
34
  npx hypequery init
73
35
  ```
74
36
 
75
- **What it does:**
76
- - Connects to your ClickHouse database
77
- - Generates TypeScript types from your schema
78
- - Creates the client, query, and serve files for the main path
79
- - Sets up `.env` with connection details
80
- - Updates `.gitignore` to protect secrets
81
-
82
- **Options:**
83
- - `--database <type>` - Database type (currently only `clickhouse`)
84
- - `--path <path>` - Output directory (default: `analytics/`)
85
- - `--no-example` - Skip example query generation
86
- - `--no-interactive` - Non-interactive mode (uses environment variables)
87
- - `--force` - Overwrite existing files without confirmation
88
-
89
- **Example:**
90
- ```bash
91
- # Interactive mode (recommended)
92
- npx @hypequery/cli init
37
+ It will:
93
38
 
94
- # Non-interactive with custom path
95
- npx @hypequery/cli init --path src/analytics --no-interactive
96
- ```
39
+ - generate schema types
40
+ - create client and query files
41
+ - write `.env` values
42
+ - update `.gitignore`
43
+ - install scaffold dependencies, including `zod`
44
+
45
+ Options:
46
+
47
+ - `--path <path>`: output directory, default `analytics/`
48
+ - `--no-example`: skip the example query
49
+ - `--no-interactive`: read connection details from env vars
50
+ - `--force`: overwrite existing scaffold files
51
+ - `--skip-connection`: skip testing the ClickHouse connection before scaffolding
97
52
 
98
53
  ### `hypequery dev`
99
54
 
100
- Start development server with live reload and query playground.
55
+ Runs the local serve runtime with docs and hot reload.
101
56
 
102
57
  ```bash
103
- # Without installation
104
- npx @hypequery/cli dev
105
-
106
- # With installation
107
58
  npx hypequery dev
108
-
109
- # With TypeScript file
110
- npx @hypequery/cli dev src/analytics/queries.ts
111
59
  ```
112
60
 
113
- **What it does:**
114
- - Starts a local HTTP server for your queries
115
- - Provides interactive API documentation at `/docs`
116
- - Auto-reloads on file changes
117
- - Displays query execution stats
118
-
119
- **Options:**
120
- - `-p, --port <port>` - Port number (default: `4000`)
121
- - `-h, --hostname <host>` - Hostname to bind (default: `localhost`)
122
- - `--no-watch` - Disable file watching
123
- - `--cache <provider>` - Cache provider (`memory` | `redis` | `none`)
124
- - `--open` - Open browser automatically
125
- - `--cors` - Enable CORS
126
- - `-q, --quiet` - Suppress startup messages
127
-
128
- **Example:**
129
- ```bash
130
- # Basic usage
131
- npx @hypequery/cli dev
61
+ Options:
132
62
 
133
- # Custom port with browser auto-open
134
- npx @hypequery/cli dev --port 3000 --open
63
+ - `--port <port>`: default `4000`
64
+ - `--hostname <host>`: default `localhost`
65
+ - `--no-watch`: disable file watching
66
+ - `--cache <provider>`: `memory`, `redis`, or `none`
67
+ - `--open`: open the browser automatically
68
+ - `--cors`: enable CORS
69
+ - `--quiet`: reduce startup output
135
70
 
136
- # Disable caching for debugging
137
- npx @hypequery/cli dev --cache none
138
- ```
139
-
140
- **Common Issues:**
141
-
142
- If you see "Unexpected token" errors while loading your queries, double-check that you're pointing the CLI at the TypeScript source file (e.g. `analytics/queries.ts`). The CLI bundles the loader and should not require additional dependencies.
71
+ The CLI understands TypeScript entry files directly, so `analytics/queries.ts` works without an extra runner.
143
72
 
144
73
  ### `hypequery generate`
145
74
 
146
- Regenerate TypeScript types from ClickHouse schema.
75
+ Regenerates schema types from ClickHouse.
147
76
 
148
77
  ```bash
149
- # Without installation
150
- npx @hypequery/cli generate
151
-
152
- # With installation
153
78
  npx hypequery generate
154
79
  ```
155
80
 
156
- The CLI bundles the ClickHouse driver directly, so you can run this command without installing `@hypequery/clickhouse`. Specify `--database <type>` once additional drivers become available.
81
+ Options:
157
82
 
158
- **What it does:**
159
- - Connects to ClickHouse
160
- - Introspects your database schema
161
- - Generates TypeScript interfaces for all tables
162
- - Updates your schema file with type-safe definitions
83
+ - `--output <path>`: default `analytics/schema.ts`
84
+ - `--tables <names>`: comma-separated table list
85
+ - `--database <type>`: currently `clickhouse`
163
86
 
164
- **Options:**
165
- - `-o, --output <path>` - Output file (default: `analytics/schema.ts`)
166
- - `--tables <names>` - Only generate for specific tables (comma-separated)
167
- - `--database <type>` - Override detected database (currently only `clickhouse`)
87
+ ## Non-interactive Setup
168
88
 
169
- **Example:**
170
- ```bash
171
- # Generate all tables
172
- npx @hypequery/cli generate
173
-
174
- # Generate specific tables
175
- npx @hypequery/cli generate --tables users,events
176
-
177
- # Custom output path
178
- npx @hypequery/cli generate --output src/schema.ts
179
- ```
89
+ `hypequery init --no-interactive` reads:
180
90
 
181
- ## Package Scripts
91
+ - `CLICKHOUSE_URL` or deprecated `CLICKHOUSE_HOST`
92
+ - `CLICKHOUSE_DATABASE`
93
+ - `CLICKHOUSE_USERNAME` or `CLICKHOUSE_USER`
94
+ - `CLICKHOUSE_PASSWORD`
182
95
 
183
- Add these to your `package.json` for easy access:
184
-
185
- ```json
186
- {
187
- "scripts": {
188
- "db:init": "hypequery init",
189
- "db:dev": "hypequery dev",
190
- "db:generate": "hypequery generate"
191
- }
192
- }
193
- ```
194
-
195
- Then run with:
196
- ```bash
197
- npm run db:dev
198
- ```
96
+ ## Notes
199
97
 
200
- ## Documentation
98
+ - generated scaffold files use NodeNext-safe local `.js` imports
99
+ - `CLICKHOUSE_URL` is now the preferred connection variable
100
+ - the CLI bundles the ClickHouse driver for schema generation
201
101
 
202
- Visit the main docs flow:
102
+ ## Docs
203
103
 
204
- - [Quick Start](https://hypequery.com/docs/quick-start)
205
- - [Core Concepts](https://hypequery.com/docs/core-concepts)
206
- - [Query Building](https://hypequery.com/docs/query-building/basics)
207
- - [Serve Runtime Reference](https://hypequery.com/docs/reference/runtime)
104
+ - [Quick start](https://hypequery.com/docs/quick-start)
105
+ - [CLI reference](https://hypequery.com/docs/reference/api/cli)
208
106
 
209
107
  ## License
210
108
 
211
- Apache-2.0
109
+ Apache-2.0.
package/dist/cli.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import { Command } from 'commander';
2
2
  declare const program: Command;
3
+ export declare function normalizeInitOptions(options: Record<string, unknown>): {
4
+ noInteractive: boolean;
5
+ };
3
6
  export { program };
4
7
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AA8F9B,OAAO,EAAE,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AAE9B,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;EAKpE;AA6FD,OAAO,EAAE,OAAO,EAAE,CAAC"}
package/dist/cli.js CHANGED
@@ -1,3 +1,14 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
1
12
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
13
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
14
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -39,6 +50,9 @@ import { initCommand } from './commands/init.js';
39
50
  import { devCommand } from './commands/dev.js';
40
51
  import { generateCommand } from './commands/generate.js';
41
52
  var program = new Command();
53
+ export function normalizeInitOptions(options) {
54
+ return __assign(__assign({}, options), { noInteractive: options.noInteractive === true || options.interactive === false });
55
+ }
42
56
  program
43
57
  .name('hypequery')
44
58
  .description('Type-safe analytics layer for ClickHouse')
@@ -47,7 +61,6 @@ program
47
61
  program
48
62
  .command('init')
49
63
  .description('Initialize a new hypequery project')
50
- .option('--database <type>', 'Database type (clickhouse|bigquery)')
51
64
  .option('--path <path>', 'Output directory (default: analytics/)')
52
65
  .option('--no-example', 'Skip example query generation')
53
66
  .option('--no-interactive', 'Non-interactive mode (use env vars)')
@@ -59,7 +72,7 @@ program
59
72
  switch (_a.label) {
60
73
  case 0:
61
74
  _a.trys.push([0, 2, , 3]);
62
- return [4 /*yield*/, initCommand(options)];
75
+ return [4 /*yield*/, initCommand(normalizeInitOptions(options))];
63
76
  case 1:
64
77
  _a.sent();
65
78
  return [3 /*break*/, 3];
@@ -118,7 +118,7 @@ export function generateCommand() {
118
118
  logger.indent('• Firewall blocking connection');
119
119
  logger.newline();
120
120
  logger.info('Check your configuration:');
121
- logger.indent('CLICKHOUSE_HOST=' + (process.env.CLICKHOUSE_HOST || 'not set'));
121
+ logger.indent('CLICKHOUSE_URL=' + (process.env.CLICKHOUSE_URL || process.env.CLICKHOUSE_HOST || 'not set'));
122
122
  logger.newline();
123
123
  logger.info('Docs: https://hypequery.com/docs/troubleshooting#connection-errors');
124
124
  }
@@ -1,5 +1,4 @@
1
1
  export interface InitOptions {
2
- database?: string;
3
2
  path?: string;
4
3
  noExample?: boolean;
5
4
  noInteractive?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAgFD,wBAAsB,WAAW,CAAC,OAAO,GAAE,WAAgB,iBAoO1D"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAiED,wBAAsB,WAAW,CAAC,OAAO,GAAE,WAAgB,iBAwO1D"}
@@ -38,7 +38,7 @@ import { mkdir, writeFile, readFile, access } from 'node:fs/promises';
38
38
  import path from 'node:path';
39
39
  import ora from 'ora';
40
40
  import { logger } from '../utils/logger.js';
41
- import { promptDatabaseType, promptClickHouseConnection, promptOutputDirectory, promptGenerateExample, promptTableSelection, confirmOverwrite, promptRetry, promptContinueWithoutDb, } from '../utils/prompts.js';
41
+ import { promptClickHouseConnection, promptOutputDirectory, promptGenerateExample, promptTableSelection, confirmOverwrite, promptRetry, promptContinueWithoutDb, } from '../utils/prompts.js';
42
42
  import { validateConnection, getTableCount, getTables, } from '../utils/detect-database.js';
43
43
  import { hasEnvFile, hasGitignore } from '../utils/find-files.js';
44
44
  import { generateEnvTemplate, appendToEnv } from '../templates/env.js';
@@ -46,53 +46,25 @@ import { generateClientTemplate } from '../templates/client.js';
46
46
  import { generateQueriesTemplate } from '../templates/queries.js';
47
47
  import { appendToGitignore } from '../templates/gitignore.js';
48
48
  import { getTypeGenerator } from '../generators/index.js';
49
- import { installServeDependencies } from '../utils/dependency-installer.js';
50
- function determineDatabase(options) {
51
- return __awaiter(this, void 0, void 0, function () {
52
- var dbType, _a;
53
- var _b;
54
- return __generator(this, function (_c) {
55
- switch (_c.label) {
56
- case 0:
57
- if (!((_b = options.database) !== null && _b !== void 0)) return [3 /*break*/, 1];
58
- _a = _b;
59
- return [3 /*break*/, 3];
60
- case 1: return [4 /*yield*/, promptDatabaseType()];
61
- case 2:
62
- _a = (_c.sent());
63
- _c.label = 3;
64
- case 3:
65
- dbType = _a;
66
- if (!dbType) {
67
- logger.info('Setup cancelled');
68
- process.exit(0);
69
- }
70
- if (dbType !== 'clickhouse') {
71
- logger.error("".concat(dbType, " is not yet supported. Only ClickHouse is available."));
72
- process.exit(1);
73
- }
74
- return [2 /*return*/, dbType];
75
- }
76
- });
77
- });
78
- }
49
+ import { installScaffoldDependencies } from '../utils/dependency-installer.js';
79
50
  function resolveConnectionConfig(options) {
80
51
  return __awaiter(this, void 0, void 0, function () {
81
52
  var required;
82
53
  var _a;
83
54
  return __generator(this, function (_b) {
84
55
  if (options.noInteractive) {
85
- required = function (key) {
86
- var value = process.env[key];
56
+ required = function (keys) {
57
+ var values = Array.isArray(keys) ? keys : [keys];
58
+ var value = values.map(function (key) { return process.env[key]; }).find(Boolean);
87
59
  if (!value) {
88
- throw new Error("Missing ".concat(key, ". Provide ClickHouse connection info via environment variables when using --no-interactive."));
60
+ throw new Error("Missing ".concat(values.join(' or '), ". Provide ClickHouse connection info via environment variables when using --no-interactive."));
89
61
  }
90
62
  return value;
91
63
  };
92
64
  return [2 /*return*/, {
93
- host: required('CLICKHOUSE_HOST'),
65
+ host: required(['CLICKHOUSE_URL', 'CLICKHOUSE_HOST']),
94
66
  database: required('CLICKHOUSE_DATABASE'),
95
- username: required('CLICKHOUSE_USERNAME'),
67
+ username: required(['CLICKHOUSE_USERNAME', 'CLICKHOUSE_USER']),
96
68
  password: (_a = process.env.CLICKHOUSE_PASSWORD) !== null && _a !== void 0 ? _a : '',
97
69
  }];
98
70
  }
@@ -100,18 +72,19 @@ function resolveConnectionConfig(options) {
100
72
  });
101
73
  });
102
74
  }
103
- function testConnection(connectionConfig, dbType) {
75
+ function testConnection(connectionConfig) {
104
76
  return __awaiter(this, void 0, void 0, function () {
105
77
  var spinner, isValid, tableCount;
106
78
  return __generator(this, function (_a) {
107
79
  switch (_a.label) {
108
80
  case 0:
109
81
  spinner = ora('Testing connection...').start();
82
+ process.env.CLICKHOUSE_URL = connectionConfig.host;
110
83
  process.env.CLICKHOUSE_HOST = connectionConfig.host;
111
84
  process.env.CLICKHOUSE_DATABASE = connectionConfig.database;
112
85
  process.env.CLICKHOUSE_USERNAME = connectionConfig.username;
113
86
  process.env.CLICKHOUSE_PASSWORD = connectionConfig.password;
114
- return [4 /*yield*/, validateConnection(dbType)];
87
+ return [4 /*yield*/, validateConnection('clickhouse')];
115
88
  case 1:
116
89
  isValid = _a.sent();
117
90
  if (!isValid) {
@@ -127,7 +100,7 @@ function testConnection(connectionConfig, dbType) {
127
100
  logger.newline();
128
101
  return [2 /*return*/, { hasValidConnection: false, tableCount: 0 }];
129
102
  }
130
- return [4 /*yield*/, getTableCount(dbType)];
103
+ return [4 /*yield*/, getTableCount('clickhouse')];
131
104
  case 2:
132
105
  tableCount = _a.sent();
133
106
  spinner.succeed("Connected successfully (".concat(tableCount, " tables found)"));
@@ -139,47 +112,48 @@ function testConnection(connectionConfig, dbType) {
139
112
  }
140
113
  export function initCommand() {
141
114
  return __awaiter(this, arguments, void 0, function (options) {
142
- var dbType, connectionConfig, hasValidConnection, tableCount, _a, valid, count, retry, continueWithout, outputDir, resolvedOutputDir, filesToCreate, existingFiles, _i, filesToCreate_1, file, _b, shouldOverwrite, generateExample, selectedTable, tables, envPath, envExists, existingEnv, newEnv, envPath, envExists, placeholderConfig, schemaPath, typeSpinner, generator, error_1, clientPath, queriesPath, gitignorePath, gitignoreExists, existingGitignore, newGitignore, exampleQueryKey;
115
+ var noInteractive, connectionConfig, hasValidConnection, tableCount, _a, valid, count, retry, continueWithout, outputDir, resolvedOutputDir, filesToCreate, existingFiles, _i, filesToCreate_1, file, _b, shouldOverwrite, _c, generateExample, selectedTable, tables, envPath, envExists, existingEnv, newEnv, envPath, envExists, placeholderConfig, schemaPath, typeSpinner, generator, error_1, clientPath, queriesPath, gitignorePath, gitignoreExists, existingGitignore, newGitignore, exampleQueryKey;
143
116
  if (options === void 0) { options = {}; }
144
- return __generator(this, function (_c) {
145
- switch (_c.label) {
117
+ return __generator(this, function (_d) {
118
+ switch (_d.label) {
146
119
  case 0:
120
+ noInteractive = options.noInteractive === true || options.interactive === false;
147
121
  logger.newline();
148
122
  logger.header('Welcome to hypequery!');
149
123
  logger.info("Let's set up your analytics layer.");
150
124
  logger.newline();
151
- return [4 /*yield*/, determineDatabase(options)];
152
- case 1:
153
- dbType = _c.sent();
154
125
  return [4 /*yield*/, resolveConnectionConfig(options)];
155
- case 2:
156
- connectionConfig = _c.sent();
126
+ case 1:
127
+ connectionConfig = _d.sent();
157
128
  hasValidConnection = false;
158
129
  tableCount = 0;
159
- if (!!connectionConfig) return [3 /*break*/, 3];
130
+ if (!!connectionConfig) return [3 /*break*/, 2];
160
131
  logger.info('Skipping database connection for now.');
161
132
  logger.newline();
162
- return [3 /*break*/, 8];
163
- case 3:
164
- if (!options.skipConnection) return [3 /*break*/, 4];
133
+ return [3 /*break*/, 7];
134
+ case 2:
135
+ if (!options.skipConnection) return [3 /*break*/, 3];
165
136
  logger.info('Skipping database connection test (requested).');
166
137
  logger.newline();
167
- return [3 /*break*/, 8];
168
- case 4: return [4 /*yield*/, testConnection(connectionConfig, dbType)];
169
- case 5:
170
- _a = _c.sent(), valid = _a.hasValidConnection, count = _a.tableCount;
138
+ return [3 /*break*/, 7];
139
+ case 3: return [4 /*yield*/, testConnection(connectionConfig)];
140
+ case 4:
141
+ _a = _d.sent(), valid = _a.hasValidConnection, count = _a.tableCount;
171
142
  hasValidConnection = valid;
172
143
  tableCount = count;
173
- if (!!hasValidConnection) return [3 /*break*/, 8];
144
+ if (!!hasValidConnection) return [3 /*break*/, 7];
145
+ if (noInteractive) {
146
+ throw new Error('Failed to connect to ClickHouse in non-interactive mode. Check your environment variables or use interactive setup.');
147
+ }
174
148
  return [4 /*yield*/, promptRetry('Try again?')];
175
- case 6:
176
- retry = _c.sent();
149
+ case 5:
150
+ retry = _d.sent();
177
151
  if (retry) {
178
152
  return [2 /*return*/, initCommand(options)];
179
153
  }
180
154
  return [4 /*yield*/, promptContinueWithoutDb()];
181
- case 7:
182
- continueWithout = _c.sent();
155
+ case 6:
156
+ continueWithout = _d.sent();
183
157
  if (!continueWithout) {
184
158
  logger.info('Setup cancelled');
185
159
  process.exit(0);
@@ -189,20 +163,20 @@ export function initCommand() {
189
163
  logger.info('You can configure the connection later in .env');
190
164
  logger.newline();
191
165
  connectionConfig = null;
192
- _c.label = 8;
193
- case 8:
166
+ _d.label = 7;
167
+ case 7:
194
168
  outputDir = options.path;
195
- if (!(!outputDir && !options.noInteractive)) return [3 /*break*/, 10];
169
+ if (!(!outputDir && !noInteractive)) return [3 /*break*/, 9];
196
170
  return [4 /*yield*/, promptOutputDirectory()];
171
+ case 8:
172
+ outputDir = _d.sent();
173
+ return [3 /*break*/, 10];
197
174
  case 9:
198
- outputDir = _c.sent();
199
- return [3 /*break*/, 11];
200
- case 10:
201
175
  if (!outputDir) {
202
176
  outputDir = 'analytics';
203
177
  }
204
- _c.label = 11;
205
- case 11:
178
+ _d.label = 10;
179
+ case 10:
206
180
  resolvedOutputDir = path.resolve(process.cwd(), outputDir);
207
181
  filesToCreate = [
208
182
  path.join(resolvedOutputDir, 'client.ts'),
@@ -211,170 +185,176 @@ export function initCommand() {
211
185
  ];
212
186
  existingFiles = [];
213
187
  _i = 0, filesToCreate_1 = filesToCreate;
214
- _c.label = 12;
215
- case 12:
216
- if (!(_i < filesToCreate_1.length)) return [3 /*break*/, 17];
188
+ _d.label = 11;
189
+ case 11:
190
+ if (!(_i < filesToCreate_1.length)) return [3 /*break*/, 16];
217
191
  file = filesToCreate_1[_i];
218
- _c.label = 13;
219
- case 13:
220
- _c.trys.push([13, 15, , 16]);
192
+ _d.label = 12;
193
+ case 12:
194
+ _d.trys.push([12, 14, , 15]);
221
195
  return [4 /*yield*/, access(file)];
222
- case 14:
223
- _c.sent();
196
+ case 13:
197
+ _d.sent();
224
198
  existingFiles.push(path.relative(process.cwd(), file));
225
- return [3 /*break*/, 16];
199
+ return [3 /*break*/, 15];
200
+ case 14:
201
+ _b = _d.sent();
202
+ return [3 /*break*/, 15];
226
203
  case 15:
227
- _b = _c.sent();
228
- return [3 /*break*/, 16];
229
- case 16:
230
204
  _i++;
231
- return [3 /*break*/, 12];
232
- case 17:
233
- if (!(existingFiles.length > 0 && !options.force)) return [3 /*break*/, 19];
205
+ return [3 /*break*/, 11];
206
+ case 16:
207
+ if (!(existingFiles.length > 0 && !options.force)) return [3 /*break*/, 20];
234
208
  logger.warn('Files already exist');
235
209
  logger.newline();
236
- return [4 /*yield*/, confirmOverwrite(existingFiles)];
210
+ if (!noInteractive) return [3 /*break*/, 17];
211
+ _c = false;
212
+ return [3 /*break*/, 19];
213
+ case 17: return [4 /*yield*/, confirmOverwrite(existingFiles)];
237
214
  case 18:
238
- shouldOverwrite = _c.sent();
215
+ _c = _d.sent();
216
+ _d.label = 19;
217
+ case 19:
218
+ shouldOverwrite = _c;
239
219
  if (!shouldOverwrite) {
240
220
  logger.info('Setup cancelled');
241
221
  process.exit(0);
242
222
  }
243
223
  logger.newline();
244
- _c.label = 19;
245
- case 19:
224
+ _d.label = 20;
225
+ case 20:
246
226
  generateExample = !options.noExample && hasValidConnection;
247
227
  selectedTable = null;
248
- if (!(generateExample && !options.noInteractive && hasValidConnection)) return [3 /*break*/, 23];
228
+ if (!(generateExample && !noInteractive && hasValidConnection)) return [3 /*break*/, 24];
249
229
  return [4 /*yield*/, promptGenerateExample()];
250
- case 20:
251
- generateExample = _c.sent();
252
- if (!generateExample) return [3 /*break*/, 23];
253
- return [4 /*yield*/, getTables(dbType)];
254
230
  case 21:
255
- tables = _c.sent();
256
- return [4 /*yield*/, promptTableSelection(tables)];
231
+ generateExample = _d.sent();
232
+ if (!generateExample) return [3 /*break*/, 24];
233
+ return [4 /*yield*/, getTables('clickhouse')];
257
234
  case 22:
258
- selectedTable = _c.sent();
259
- generateExample = selectedTable !== null;
260
- _c.label = 23;
235
+ tables = _d.sent();
236
+ return [4 /*yield*/, promptTableSelection(tables)];
261
237
  case 23:
238
+ selectedTable = _d.sent();
239
+ generateExample = selectedTable !== null;
240
+ _d.label = 24;
241
+ case 24:
262
242
  logger.newline();
263
243
  // Step 7: Create directory
264
244
  return [4 /*yield*/, mkdir(resolvedOutputDir, { recursive: true })];
265
- case 24:
245
+ case 25:
266
246
  // Step 7: Create directory
267
- _c.sent();
268
- if (!connectionConfig) return [3 /*break*/, 31];
247
+ _d.sent();
248
+ if (!connectionConfig) return [3 /*break*/, 32];
269
249
  envPath = path.join(process.cwd(), '.env');
270
250
  return [4 /*yield*/, hasEnvFile()];
271
- case 25:
272
- envExists = _c.sent();
273
- if (!envExists) return [3 /*break*/, 28];
274
- return [4 /*yield*/, readFile(envPath, 'utf-8')];
275
251
  case 26:
276
- existingEnv = _c.sent();
252
+ envExists = _d.sent();
253
+ if (!envExists) return [3 /*break*/, 29];
254
+ return [4 /*yield*/, readFile(envPath, 'utf-8')];
255
+ case 27:
256
+ existingEnv = _d.sent();
277
257
  newEnv = appendToEnv(existingEnv, generateEnvTemplate(connectionConfig));
278
258
  return [4 /*yield*/, writeFile(envPath, newEnv)];
279
- case 27:
280
- _c.sent();
259
+ case 28:
260
+ _d.sent();
281
261
  logger.success('Updated .env');
282
- return [3 /*break*/, 30];
283
- case 28: return [4 /*yield*/, writeFile(envPath, generateEnvTemplate(connectionConfig))];
284
- case 29:
285
- _c.sent();
262
+ return [3 /*break*/, 31];
263
+ case 29: return [4 /*yield*/, writeFile(envPath, generateEnvTemplate(connectionConfig))];
264
+ case 30:
265
+ _d.sent();
286
266
  logger.success('Created .env');
287
- _c.label = 30;
288
- case 30: return [3 /*break*/, 34];
289
- case 31:
267
+ _d.label = 31;
268
+ case 31: return [3 /*break*/, 35];
269
+ case 32:
290
270
  envPath = path.join(process.cwd(), '.env');
291
271
  return [4 /*yield*/, hasEnvFile()];
292
- case 32:
293
- envExists = _c.sent();
272
+ case 33:
273
+ envExists = _d.sent();
294
274
  placeholderConfig = {
295
- host: 'YOUR_CLICKHOUSE_HOST',
275
+ url: 'YOUR_CLICKHOUSE_URL',
296
276
  database: 'YOUR_DATABASE',
297
277
  username: 'YOUR_USERNAME',
298
278
  password: 'YOUR_PASSWORD',
299
279
  };
300
- if (!!envExists) return [3 /*break*/, 34];
280
+ if (!!envExists) return [3 /*break*/, 35];
301
281
  return [4 /*yield*/, writeFile(envPath, generateEnvTemplate(placeholderConfig))];
302
- case 33:
303
- _c.sent();
304
- logger.success('Created .env (configure your credentials)');
305
- _c.label = 34;
306
282
  case 34:
283
+ _d.sent();
284
+ logger.success('Created .env (configure your credentials)');
285
+ _d.label = 35;
286
+ case 35:
307
287
  schemaPath = path.join(resolvedOutputDir, 'schema.ts');
308
- if (!hasValidConnection) return [3 /*break*/, 39];
288
+ if (!hasValidConnection) return [3 /*break*/, 40];
309
289
  typeSpinner = ora('Generating TypeScript types...').start();
310
- _c.label = 35;
311
- case 35:
312
- _c.trys.push([35, 37, , 38]);
290
+ _d.label = 36;
291
+ case 36:
292
+ _d.trys.push([36, 38, , 39]);
313
293
  generator = getTypeGenerator('clickhouse');
314
294
  return [4 /*yield*/, generator({ outputPath: schemaPath })];
315
- case 36:
316
- _c.sent();
317
- typeSpinner.succeed("Generated TypeScript types (".concat(path.relative(process.cwd(), schemaPath), ")"));
318
- return [3 /*break*/, 38];
319
295
  case 37:
320
- error_1 = _c.sent();
296
+ _d.sent();
297
+ typeSpinner.succeed("Generated TypeScript types (".concat(path.relative(process.cwd(), schemaPath), ")"));
298
+ return [3 /*break*/, 39];
299
+ case 38:
300
+ error_1 = _d.sent();
321
301
  typeSpinner.fail('Failed to generate types');
322
302
  logger.error(error_1 instanceof Error ? error_1.message : String(error_1));
323
303
  process.exit(1);
324
- return [3 /*break*/, 38];
325
- case 38: return [3 /*break*/, 41];
326
- case 39:
304
+ return [3 /*break*/, 39];
305
+ case 39: return [3 /*break*/, 42];
306
+ case 40:
327
307
  // Create placeholder schema file
328
308
  return [4 /*yield*/, writeFile(schemaPath, "// Generated by hypequery\n// Run 'npx hypequery generate' after configuring your database connection\n\nexport interface IntrospectedSchema {\n // Your table types will appear here after generation\n}\n")];
329
- case 40:
309
+ case 41:
330
310
  // Create placeholder schema file
331
- _c.sent();
311
+ _d.sent();
332
312
  logger.success("Created placeholder schema (".concat(path.relative(process.cwd(), schemaPath), ")"));
333
- _c.label = 41;
334
- case 41:
313
+ _d.label = 42;
314
+ case 42:
335
315
  clientPath = path.join(resolvedOutputDir, 'client.ts');
336
316
  return [4 /*yield*/, writeFile(clientPath, generateClientTemplate())];
337
- case 42:
338
- _c.sent();
317
+ case 43:
318
+ _d.sent();
339
319
  logger.success("Created ClickHouse client (".concat(path.relative(process.cwd(), clientPath), ")"));
340
320
  queriesPath = path.join(resolvedOutputDir, 'queries.ts');
341
321
  return [4 /*yield*/, writeFile(queriesPath, generateQueriesTemplate({
342
322
  hasExample: generateExample,
343
323
  tableName: selectedTable || undefined,
344
324
  }))];
345
- case 43:
346
- _c.sent();
325
+ case 44:
326
+ _d.sent();
347
327
  logger.success("Created queries file (".concat(path.relative(process.cwd(), queriesPath), ")"));
348
328
  if (generateExample && selectedTable) {
349
329
  logger.success("Created example query using '".concat(selectedTable, "' table"));
350
330
  }
351
331
  gitignorePath = path.join(process.cwd(), '.gitignore');
352
332
  return [4 /*yield*/, hasGitignore()];
353
- case 44:
354
- gitignoreExists = _c.sent();
355
- if (!gitignoreExists) return [3 /*break*/, 48];
356
- return [4 /*yield*/, readFile(gitignorePath, 'utf-8')];
357
333
  case 45:
358
- existingGitignore = _c.sent();
334
+ gitignoreExists = _d.sent();
335
+ if (!gitignoreExists) return [3 /*break*/, 49];
336
+ return [4 /*yield*/, readFile(gitignorePath, 'utf-8')];
337
+ case 46:
338
+ existingGitignore = _d.sent();
359
339
  newGitignore = appendToGitignore(existingGitignore);
360
- if (!(newGitignore !== existingGitignore)) return [3 /*break*/, 47];
340
+ if (!(newGitignore !== existingGitignore)) return [3 /*break*/, 48];
361
341
  return [4 /*yield*/, writeFile(gitignorePath, newGitignore)];
362
- case 46:
363
- _c.sent();
342
+ case 47:
343
+ _d.sent();
364
344
  logger.success('Updated .gitignore');
365
- _c.label = 47;
366
- case 47: return [3 /*break*/, 50];
367
- case 48: return [4 /*yield*/, writeFile(gitignorePath, appendToGitignore(''))];
368
- case 49:
369
- _c.sent();
345
+ _d.label = 48;
346
+ case 48: return [3 /*break*/, 51];
347
+ case 49: return [4 /*yield*/, writeFile(gitignorePath, appendToGitignore(''))];
348
+ case 50:
349
+ _d.sent();
370
350
  logger.success('Created .gitignore');
371
- _c.label = 50;
372
- case 50:
351
+ _d.label = 51;
352
+ case 51:
373
353
  // Step 13: Ensure required hypequery packages are installed
374
- return [4 /*yield*/, installServeDependencies()];
375
- case 51:
354
+ return [4 /*yield*/, installScaffoldDependencies()];
355
+ case 52:
376
356
  // Step 13: Ensure required hypequery packages are installed
377
- _c.sent();
357
+ _d.sent();
378
358
  // Step 14: Success message
379
359
  logger.newline();
380
360
  logger.header('Setup complete!');
@@ -6,7 +6,7 @@ export function getTypeGenerator(dbType) {
6
6
  var generator = generators[dbType];
7
7
  if (!generator) {
8
8
  throw new Error(dbType === 'unknown'
9
- ? 'Unable to detect database type. Re-run `hypequery init --database <type>` or pass `--database` explicitly.'
9
+ ? 'Unable to detect database type. Re-run `hypequery generate --database <type>` or pass `--database` explicitly.'
10
10
  : "Type generation for ".concat(dbType, " is not supported yet."));
11
11
  }
12
12
  return generator;
@@ -2,5 +2,5 @@
2
2
  * Generate client.ts file for ClickHouse
3
3
  */
4
4
  export function generateClientTemplate() {
5
- return "import { createQueryBuilder } from '@hypequery/clickhouse';\nimport type { IntrospectedSchema } from './schema';\n\nexport const db = createQueryBuilder<IntrospectedSchema>({\n host: process.env.CLICKHOUSE_HOST!,\n database: process.env.CLICKHOUSE_DATABASE!,\n username: process.env.CLICKHOUSE_USERNAME!,\n password: process.env.CLICKHOUSE_PASSWORD,\n});\n";
5
+ return "import { createQueryBuilder } from '@hypequery/clickhouse';\nimport type { IntrospectedSchema } from './schema.js';\n\nexport const db = createQueryBuilder<IntrospectedSchema>({\n url: process.env.CLICKHOUSE_URL ?? process.env.CLICKHOUSE_HOST!,\n database: process.env.CLICKHOUSE_DATABASE!,\n username: process.env.CLICKHOUSE_USERNAME!,\n password: process.env.CLICKHOUSE_PASSWORD,\n});\n";
6
6
  }
@@ -2,7 +2,8 @@
2
2
  * Generate .env file content for ClickHouse
3
3
  */
4
4
  export declare function generateEnvTemplate(config: {
5
- host: string;
5
+ host?: string;
6
+ url?: string;
6
7
  database: string;
7
8
  username: string;
8
9
  password: string;
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/templates/env.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,MAAM,CAoBT;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,eAAe,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CA4B/E"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/templates/env.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,MAAM,CAqBT;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,eAAe,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CA4B/E"}
@@ -2,11 +2,13 @@
2
2
  * Generate .env file content for ClickHouse
3
3
  */
4
4
  export function generateEnvTemplate(config) {
5
- var isPlaceholder = config.host.includes('YOUR_');
5
+ var _a, _b;
6
+ var connectionUrl = (_b = (_a = config.url) !== null && _a !== void 0 ? _a : config.host) !== null && _b !== void 0 ? _b : '';
7
+ var isPlaceholder = connectionUrl.includes('YOUR_');
6
8
  if (isPlaceholder) {
7
- return "# Hypequery Configuration\n# Replace these placeholder values with your actual ClickHouse credentials\n\nCLICKHOUSE_HOST=".concat(config.host, "\nCLICKHOUSE_DATABASE=").concat(config.database, "\nCLICKHOUSE_USERNAME=").concat(config.username, "\nCLICKHOUSE_PASSWORD=").concat(config.password, "\n");
9
+ return "# Hypequery Configuration\n# Replace these placeholder values with your actual ClickHouse credentials\n\nCLICKHOUSE_URL=".concat(connectionUrl, "\nCLICKHOUSE_DATABASE=").concat(config.database, "\nCLICKHOUSE_USERNAME=").concat(config.username, "\nCLICKHOUSE_PASSWORD=").concat(config.password, "\n");
8
10
  }
9
- return "# Hypequery Configuration\nCLICKHOUSE_HOST=".concat(config.host, "\nCLICKHOUSE_DATABASE=").concat(config.database, "\nCLICKHOUSE_USERNAME=").concat(config.username, "\nCLICKHOUSE_PASSWORD=").concat(config.password, "\n");
11
+ return "# Hypequery Configuration\nCLICKHOUSE_URL=".concat(connectionUrl, "\nCLICKHOUSE_DATABASE=").concat(config.database, "\nCLICKHOUSE_USERNAME=").concat(config.username, "\nCLICKHOUSE_PASSWORD=").concat(config.password, "\n");
10
12
  }
11
13
  /**
12
14
  * Append to existing .env file
@@ -1 +1 @@
1
- {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/templates/queries.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAC/C,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,MAAM,CA+DT"}
1
+ {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/templates/queries.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAC/C,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,MAAM,CAiET"}
@@ -5,14 +5,15 @@ export function generateQueriesTemplate(options) {
5
5
  var hasExample = options.hasExample, tableName = options.tableName;
6
6
  var metricKey = hasExample && tableName ? "".concat(camelCase(tableName), "Query") : 'exampleMetric';
7
7
  var typeAlias = "".concat(pascalCase(metricKey), "Result");
8
- var template = "import { initServe } from '@hypequery/serve';\nimport type { InferApiType } from '@hypequery/serve';\nimport { z } from 'zod';\nimport { db } from './client';\n\nconst { query, serve } = initServe({\n context: () => ({ db }),\n});\n\n";
8
+ var routePath = "/metrics/".concat(metricKey);
9
+ var template = "import { initServe } from '@hypequery/serve';\nimport type { InferApiType } from '@hypequery/serve';\nimport { z } from 'zod';\nimport { db } from './client.js';\n\nconst { query, serve } = initServe({\n context: () => ({ db }),\n});\n\n";
9
10
  if (hasExample && tableName) {
10
11
  template += "\nconst ".concat(camelCase(tableName), "Query = query({\n description: 'Example query using the ").concat(tableName, " table',\n query: async ({ ctx }) =>\n ctx.db\n .table('").concat(tableName, "')\n .select('*')\n .limit(10)\n .execute(),\n});");
11
12
  }
12
13
  else {
13
14
  template += "\nconst exampleMetric = query({\n description: 'Example metric that returns a simple value',\n output: z.object({ ok: z.boolean() }),\n query: async () => ({ ok: true }),\n});";
14
15
  }
15
- template += "\nexport const api = serve({\n queries: {\n ".concat(metricKey, ",\n },\n});\n\nexport type ApiDefinition = InferApiType<typeof api>;\n\n/**\n * Inline usage example:\n *\n * const result = await api.execute('").concat(metricKey, "');\n * console.log(result);\n *\n * // import type { InferQueryResult } from '@hypequery/serve';\n * type ").concat(typeAlias, " = InferQueryResult<typeof api, '").concat(metricKey, "'>;\n *\n * // Register HTTP route:\n * api.route('/metrics/").concat(metricKey, "', api.queries.").concat(metricKey, ");\n *\n * Dev server:\n * npx hypequery dev analytics/queries.ts\n */\n");
16
+ template += "\nexport const api = serve({\n queries: {\n ".concat(metricKey, ",\n },\n});\napi.route('").concat(routePath, "', api.queries.").concat(metricKey, ");\n\nexport type ApiDefinition = InferApiType<typeof api>;\n\n/**\n * Inline usage example:\n *\n * const result = await api.execute('").concat(metricKey, "');\n * console.log(result);\n *\n * // import type { InferQueryResult } from '@hypequery/serve';\n * type ").concat(typeAlias, " = InferQueryResult<typeof api, '").concat(metricKey, "'>;\n *\n * HTTP route:\n * ").concat(routePath, "\n *\n * Dev server:\n * npx hypequery dev analytics/queries.ts\n */\n");
16
17
  return template;
17
18
  }
18
19
  /**
@@ -3,7 +3,6 @@ import type { Mock } from 'vitest';
3
3
  * Mock prompts module for testing
4
4
  */
5
5
  export declare function createMockPrompts(): {
6
- promptDatabaseType: Mock<(...args: any[]) => any>;
7
6
  promptClickHouseConnection: Mock<(...args: any[]) => any>;
8
7
  promptOutputDirectory: Mock<(...args: any[]) => any>;
9
8
  promptGenerateExample: Mock<(...args: any[]) => any>;
@@ -1 +1 @@
1
- {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC;;GAEG;AACH,wBAAgB,iBAAiB;;;;;;;;;EAWhC;AAED;;GAEG;AACH,wBAAgB,uBAAuB;;;;EAMtC;AAED;;GAEG;AACH,wBAAgB,mBAAmB;;;;;EAOlC;AAED;;GAEG;AACH,wBAAgB,gBAAgB;;;;;;;;;;;;EAc/B;AAED;;GAEG;AACH,wBAAgB,iBAAiB;;;;;GAQhC;AAED;;GAEG;AACH,wBAAgB,YAAY;;;;;EAO3B;AAED;;GAEG;AACH,wBAAgB,cAAc;;;;EAuB7B;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACtB,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;CAIhC;AAED,wBAAgB,eAAe;;;EAc9B"}
1
+ {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC;;GAEG;AACH,wBAAgB,iBAAiB;;;;;;;;EAUhC;AAED;;GAEG;AACH,wBAAgB,uBAAuB;;;;EAMtC;AAED;;GAEG;AACH,wBAAgB,mBAAmB;;;;;EAOlC;AAED;;GAEG;AACH,wBAAgB,gBAAgB;;;;;;;;;;;;EAc/B;AAED;;GAEG;AACH,wBAAgB,iBAAiB;;;;;GAQhC;AAED;;GAEG;AACH,wBAAgB,YAAY;;;;;EAO3B;AAED;;GAEG;AACH,wBAAgB,cAAc;;;;EAuB7B;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACtB,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;CAIhC;AAED,wBAAgB,eAAe;;;EAc9B"}
@@ -19,7 +19,6 @@ import { vi } from 'vitest';
19
19
  */
20
20
  export function createMockPrompts() {
21
21
  return {
22
- promptDatabaseType: vi.fn(),
23
22
  promptClickHouseConnection: vi.fn(),
24
23
  promptOutputDirectory: vi.fn(),
25
24
  promptGenerateExample: vi.fn(),
@@ -57,7 +57,7 @@ export function getClickHouseClient() {
57
57
  if (!client) {
58
58
  var config = getClickHouseConfigFromEnv();
59
59
  if (!config) {
60
- throw new Error('ClickHouse connection details are missing. Set CLICKHOUSE_HOST (or CLICKHOUSE_URL), CLICKHOUSE_DATABASE, CLICKHOUSE_USERNAME, and CLICKHOUSE_PASSWORD.');
60
+ throw new Error('ClickHouse connection details are missing. Set CLICKHOUSE_URL (or CLICKHOUSE_HOST), CLICKHOUSE_DATABASE, CLICKHOUSE_USERNAME, and CLICKHOUSE_PASSWORD.');
61
61
  }
62
62
  client = createClient({
63
63
  url: config.url,
@@ -1,2 +1,4 @@
1
- export declare function installServeDependencies(): Promise<void>;
1
+ export declare function resolveScaffoldPackages(cliVersion: string | undefined): string[];
2
+ export declare function installScaffoldDependencies(): Promise<void>;
3
+ export declare const installServeDependencies: typeof installScaffoldDependencies;
2
4
  //# 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":"AAyEA,wBAAsB,wBAAwB,kBA4C7C"}
1
+ {"version":3,"file":"dependency-installer.d.ts","sourceRoot":"","sources":["../../src/utils/dependency-installer.ts"],"names":[],"mappings":"AAkGA,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAUhF;AAsBD,wBAAsB,2BAA2B,kBAgDhD;AAED,eAAO,MAAM,wBAAwB,oCAA8B,CAAC"}
@@ -47,17 +47,28 @@ import { existsSync } from 'node:fs';
47
47
  import { readFile } from 'node:fs/promises';
48
48
  import path from 'node:path';
49
49
  import { spawn } from 'node:child_process';
50
+ import { fileURLToPath } from 'node:url';
50
51
  import { logger } from './logger.js';
51
- var REQUIRED_PACKAGES = ['@hypequery/clickhouse', '@hypequery/serve'];
52
+ var ZOD_SCAFFOLD_VERSION = '^3.23.8';
53
+ var STABLE_SCAFFOLD_PACKAGES = ['@hypequery/clickhouse', '@hypequery/serve', "zod@".concat(ZOD_SCAFFOLD_VERSION)];
54
+ var CLI_PACKAGE_PATH = fileURLToPath(new URL('../../package.json', import.meta.url));
52
55
  var MANUAL_COMMANDS = {
53
56
  pnpm: 'pnpm add',
54
57
  yarn: 'yarn add',
55
58
  npm: 'npm install',
56
59
  bun: 'bun add',
57
60
  };
58
- function hasDependency(pkg, name) {
61
+ function getDependencyVersion(pkg, name) {
59
62
  var _a, _b, _c;
60
- return Boolean((_b = (_a = pkg.dependencies) === null || _a === void 0 ? void 0 : _a[name]) !== null && _b !== void 0 ? _b : (_c = pkg.devDependencies) === null || _c === void 0 ? void 0 : _c[name]);
63
+ return (_b = (_a = pkg.dependencies) === null || _a === void 0 ? void 0 : _a[name]) !== null && _b !== void 0 ? _b : (_c = pkg.devDependencies) === null || _c === void 0 ? void 0 : _c[name];
64
+ }
65
+ function packageNameFromSpecifier(specifier) {
66
+ if (!specifier.startsWith('@')) {
67
+ var atIndex = specifier.lastIndexOf('@');
68
+ return atIndex > 0 ? specifier.slice(0, atIndex) : specifier;
69
+ }
70
+ var separatorIndex = specifier.indexOf('@', specifier.indexOf('/') + 1);
71
+ return separatorIndex === -1 ? specifier : specifier.slice(0, separatorIndex);
61
72
  }
62
73
  function readProjectPackageJson() {
63
74
  return __awaiter(this, void 0, void 0, function () {
@@ -78,6 +89,25 @@ function readProjectPackageJson() {
78
89
  });
79
90
  });
80
91
  }
92
+ function readCliPackageJson() {
93
+ return __awaiter(this, void 0, void 0, function () {
94
+ var file, _a;
95
+ return __generator(this, function (_b) {
96
+ switch (_b.label) {
97
+ case 0:
98
+ _b.trys.push([0, 2, , 3]);
99
+ return [4 /*yield*/, readFile(CLI_PACKAGE_PATH, 'utf8')];
100
+ case 1:
101
+ file = _b.sent();
102
+ return [2 /*return*/, JSON.parse(file)];
103
+ case 2:
104
+ _a = _b.sent();
105
+ return [2 /*return*/, null];
106
+ case 3: return [2 /*return*/];
107
+ }
108
+ });
109
+ });
110
+ }
81
111
  function detectPackageManager(pkgJson) {
82
112
  var _a, _b;
83
113
  var userAgent = (_a = process.env.npm_config_user_agent) !== null && _a !== void 0 ? _a : '';
@@ -119,23 +149,52 @@ function getInstallArgs(manager, packages) {
119
149
  function formatManualCommand(manager, packages) {
120
150
  return "".concat(MANUAL_COMMANDS[manager], " ").concat(packages.join(' '));
121
151
  }
122
- export function installServeDependencies() {
152
+ export function resolveScaffoldPackages(cliVersion) {
153
+ if (cliVersion === null || cliVersion === void 0 ? void 0 : cliVersion.includes('canary')) {
154
+ return [
155
+ "@hypequery/clickhouse@".concat(cliVersion),
156
+ "@hypequery/serve@".concat(cliVersion),
157
+ "zod@".concat(ZOD_SCAFFOLD_VERSION),
158
+ ];
159
+ }
160
+ return __spreadArray([], STABLE_SCAFFOLD_PACKAGES, true);
161
+ }
162
+ function shouldInstallPackage(pkgJson, specifier) {
163
+ var name = packageNameFromSpecifier(specifier);
164
+ var existingVersion = getDependencyVersion(pkgJson, name);
165
+ if (!existingVersion) {
166
+ return true;
167
+ }
168
+ var desiredHasPinnedVersion = specifier.startsWith('@')
169
+ ? specifier.indexOf('@', specifier.indexOf('/') + 1) !== -1
170
+ : specifier.includes('@');
171
+ if (!desiredHasPinnedVersion) {
172
+ return false;
173
+ }
174
+ var desiredVersion = specifier.slice(name.length + 1);
175
+ return existingVersion !== desiredVersion;
176
+ }
177
+ export function installScaffoldDependencies() {
123
178
  return __awaiter(this, void 0, void 0, function () {
124
- var pkgJson, missing, manager, command, args, error_1;
125
- return __generator(this, function (_a) {
126
- switch (_a.label) {
179
+ var _a, pkgJson, cliPkgJson, requestedPackages, missing, manager, command, args, error_1;
180
+ return __generator(this, function (_b) {
181
+ switch (_b.label) {
127
182
  case 0:
128
183
  if (process.env.HYPEQUERY_SKIP_INSTALL === '1') {
129
184
  return [2 /*return*/];
130
185
  }
131
- return [4 /*yield*/, readProjectPackageJson()];
186
+ return [4 /*yield*/, Promise.all([
187
+ readProjectPackageJson(),
188
+ readCliPackageJson(),
189
+ ])];
132
190
  case 1:
133
- pkgJson = _a.sent();
191
+ _a = _b.sent(), pkgJson = _a[0], cliPkgJson = _a[1];
134
192
  if (!pkgJson) {
135
- logger.warn('package.json not found. Install @hypequery/clickhouse and @hypequery/serve manually.');
193
+ logger.warn('package.json not found. Install @hypequery/clickhouse, @hypequery/serve, and zod manually.');
136
194
  return [2 /*return*/];
137
195
  }
138
- missing = REQUIRED_PACKAGES.filter(function (pkg) { return !hasDependency(pkgJson, pkg); });
196
+ requestedPackages = resolveScaffoldPackages(cliPkgJson === null || cliPkgJson === void 0 ? void 0 : cliPkgJson.version);
197
+ missing = requestedPackages.filter(function (pkg) { return shouldInstallPackage(pkgJson, pkg); });
139
198
  if (missing.length === 0) {
140
199
  return [2 /*return*/];
141
200
  }
@@ -143,9 +202,9 @@ export function installServeDependencies() {
143
202
  command = manager === 'npm' ? 'npm' : manager;
144
203
  args = getInstallArgs(manager, missing);
145
204
  logger.info("Installing ".concat(missing.join(', '), " with ").concat(manager, "..."));
146
- _a.label = 2;
205
+ _b.label = 2;
147
206
  case 2:
148
- _a.trys.push([2, 4, , 5]);
207
+ _b.trys.push([2, 4, , 5]);
149
208
  return [4 /*yield*/, new Promise(function (resolve, reject) {
150
209
  var child = spawn(command, args, {
151
210
  cwd: process.cwd(),
@@ -162,11 +221,11 @@ export function installServeDependencies() {
162
221
  });
163
222
  })];
164
223
  case 3:
165
- _a.sent();
224
+ _b.sent();
166
225
  logger.success("Installed ".concat(missing.join(', ')));
167
226
  return [3 /*break*/, 5];
168
227
  case 4:
169
- error_1 = _a.sent();
228
+ error_1 = _b.sent();
170
229
  logger.warn('Failed to install hypequery packages automatically.');
171
230
  logger.info("Run manually: ".concat(formatManualCommand(manager, missing)));
172
231
  if (error_1 instanceof Error && error_1.message) {
@@ -178,3 +237,4 @@ export function installServeDependencies() {
178
237
  });
179
238
  });
180
239
  }
240
+ export var installServeDependencies = installScaffoldDependencies;
@@ -1,4 +1,4 @@
1
- import type { ResolvedHypequeryClickHouseConfig } from '@hypequery/clickhouse';
1
+ import type { ResolvedHypequeryClickHouseConfig } from '@hypequery/schema';
2
2
  export declare const DEFAULT_HYPEQUERY_CONFIG_PATH = "hypequery.config.ts";
3
3
  export declare const DEFAULT_MIGRATIONS_OUT_DIR = "./migrations";
4
4
  export declare const DEFAULT_MIGRATIONS_TABLE = "_hypequery_migrations";
@@ -1 +1 @@
1
- {"version":3,"file":"load-hypequery-config.d.ts","sourceRoot":"","sources":["../../src/utils/load-hypequery-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iCAAiC,EAClC,MAAM,uBAAuB,CAAC;AAG/B,eAAO,MAAM,6BAA6B,wBAAwB,CAAC;AACnE,eAAO,MAAM,0BAA0B,iBAAiB,CAAC;AACzD,eAAO,MAAM,wBAAwB,0BAA0B,CAAC;AAChE,eAAO,MAAM,yBAAyB,EAAG,WAAoB,CAAC;AAE9D,wBAAsB,mBAAmB,CACvC,UAAU,SAAgC,GACzC,OAAO,CAAC,iCAAiC,CAAC,CA0C5C"}
1
+ {"version":3,"file":"load-hypequery-config.d.ts","sourceRoot":"","sources":["../../src/utils/load-hypequery-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iCAAiC,EAClC,MAAM,mBAAmB,CAAC;AAG3B,eAAO,MAAM,6BAA6B,wBAAwB,CAAC;AACnE,eAAO,MAAM,0BAA0B,iBAAiB,CAAC;AACzD,eAAO,MAAM,wBAAwB,0BAA0B,CAAC;AAChE,eAAO,MAAM,yBAAyB,EAAG,WAAoB,CAAC;AAE9D,wBAAsB,mBAAmB,CACvC,UAAU,SAAgC,GACzC,OAAO,CAAC,iCAAiC,CAAC,CA0C5C"}
@@ -1,8 +1,3 @@
1
- import type { DatabaseType } from './detect-database.js';
2
- /**
3
- * Prompt for database type selection
4
- */
5
- export declare function promptDatabaseType(): Promise<DatabaseType | null>;
6
1
  /**
7
2
  * Prompt for ClickHouse connection details
8
3
  */
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAQzD;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAavE;AAED;;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;;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,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"}
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;;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,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"}
@@ -48,68 +48,43 @@ import { logger } from './logger.js';
48
48
  var noop = function () { return undefined; };
49
49
  // Configure prompts to not exit on cancel
50
50
  prompts.override({ onCancel: noop });
51
- /**
52
- * Prompt for database type selection
53
- */
54
- export function promptDatabaseType() {
55
- return __awaiter(this, void 0, void 0, function () {
56
- var response;
57
- return __generator(this, function (_a) {
58
- switch (_a.label) {
59
- case 0: return [4 /*yield*/, prompts({
60
- type: 'select',
61
- name: 'database',
62
- message: 'Which database are you using?',
63
- choices: [
64
- { title: 'ClickHouse', value: 'clickhouse' },
65
- { title: 'BigQuery (coming soon)', value: 'bigquery', disabled: true },
66
- ],
67
- initial: 0,
68
- })];
69
- case 1:
70
- response = _a.sent();
71
- return [2 /*return*/, response.database || null];
72
- }
73
- });
74
- });
75
- }
76
51
  /**
77
52
  * Prompt for ClickHouse connection details
78
53
  */
79
54
  export function promptClickHouseConnection() {
80
55
  return __awaiter(this, void 0, void 0, function () {
81
56
  var response;
82
- var _a, _b, _c, _d;
83
- return __generator(this, function (_e) {
84
- switch (_e.label) {
57
+ var _a, _b, _c, _d, _e;
58
+ return __generator(this, function (_f) {
59
+ switch (_f.label) {
85
60
  case 0: return [4 /*yield*/, prompts([
86
61
  {
87
62
  type: 'text',
88
63
  name: 'host',
89
- message: 'ClickHouse host (or skip to configure later):',
90
- initial: (_a = process.env.CLICKHOUSE_HOST) !== null && _a !== void 0 ? _a : '',
64
+ message: 'ClickHouse URL (or skip to configure later):',
65
+ initial: (_b = (_a = process.env.CLICKHOUSE_URL) !== null && _a !== void 0 ? _a : process.env.CLICKHOUSE_HOST) !== null && _b !== void 0 ? _b : '',
91
66
  },
92
67
  {
93
68
  type: 'text',
94
69
  name: 'database',
95
70
  message: 'Database:',
96
- initial: (_b = process.env.CLICKHOUSE_DATABASE) !== null && _b !== void 0 ? _b : '',
71
+ initial: (_c = process.env.CLICKHOUSE_DATABASE) !== null && _c !== void 0 ? _c : '',
97
72
  },
98
73
  {
99
74
  type: 'text',
100
75
  name: 'username',
101
76
  message: 'Username:',
102
- initial: (_c = process.env.CLICKHOUSE_USERNAME) !== null && _c !== void 0 ? _c : '',
77
+ initial: (_d = process.env.CLICKHOUSE_USERNAME) !== null && _d !== void 0 ? _d : '',
103
78
  },
104
79
  {
105
80
  type: 'password',
106
81
  name: 'password',
107
82
  message: 'Password:',
108
- initial: (_d = process.env.CLICKHOUSE_PASSWORD) !== null && _d !== void 0 ? _d : '',
83
+ initial: (_e = process.env.CLICKHOUSE_PASSWORD) !== null && _e !== void 0 ? _e : '',
109
84
  },
110
85
  ])];
111
86
  case 1:
112
- response = _e.sent();
87
+ response = _f.sent();
113
88
  // If user cancelled or skipped
114
89
  if (!response.host) {
115
90
  return [2 /*return*/, null];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypequery/cli",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Command-line interface for hypequery",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -11,7 +11,7 @@
11
11
  "dist"
12
12
  ],
13
13
  "dependencies": {
14
- "@clickhouse/client": "^1.16.0",
14
+ "@clickhouse/client": "^1.18.3",
15
15
  "chalk": "^5.3.0",
16
16
  "commander": "^12.0.0",
17
17
  "dotenv": "^16.4.7",
@@ -22,11 +22,15 @@
22
22
  },
23
23
  "peerDependencies": {
24
24
  "@hypequery/clickhouse": "*",
25
+ "@hypequery/schema": "*",
25
26
  "@hypequery/serve": "*"
26
27
  },
27
28
  "peerDependenciesMeta": {
28
29
  "@hypequery/clickhouse": {
29
30
  "optional": true
31
+ },
32
+ "@hypequery/schema": {
33
+ "optional": true
30
34
  }
31
35
  },
32
36
  "devDependencies": {
@@ -35,8 +39,9 @@
35
39
  "@vitest/coverage-v8": "^2.1.6",
36
40
  "typescript": "^5.7.3",
37
41
  "vitest": "^2.1.6",
38
- "@hypequery/clickhouse": "1.6.2",
39
- "@hypequery/serve": "0.2.0"
42
+ "@hypequery/schema": "0.0.0",
43
+ "@hypequery/serve": "0.2.0",
44
+ "@hypequery/clickhouse": "2.0.0"
40
45
  },
41
46
  "repository": {
42
47
  "type": "git",