@hypequery/cli 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +26 -2
  2. package/dist/bin/cli.js +21 -81
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +72 -116
  5. package/dist/commands/dev.d.ts +1 -0
  6. package/dist/commands/dev.d.ts.map +1 -1
  7. package/dist/commands/dev.js +180 -255
  8. package/dist/commands/generate-datasets.d.ts +14 -0
  9. package/dist/commands/generate-datasets.d.ts.map +1 -0
  10. package/dist/commands/generate-datasets.js +96 -0
  11. package/dist/commands/generate.d.ts +2 -0
  12. package/dist/commands/generate.d.ts.map +1 -1
  13. package/dist/commands/generate.js +112 -167
  14. package/dist/commands/init.d.ts +7 -0
  15. package/dist/commands/init.d.ts.map +1 -1
  16. package/dist/commands/init.js +337 -370
  17. package/dist/generators/clickhouse.d.ts +1 -1
  18. package/dist/generators/clickhouse.d.ts.map +1 -1
  19. package/dist/generators/clickhouse.js +10 -269
  20. package/dist/generators/dataset-generator.d.ts +16 -0
  21. package/dist/generators/dataset-generator.d.ts.map +1 -0
  22. package/dist/generators/dataset-generator.js +213 -0
  23. package/dist/generators/index.js +3 -3
  24. package/dist/templates/api.d.ts +9 -0
  25. package/dist/templates/api.d.ts.map +1 -0
  26. package/dist/templates/api.js +26 -0
  27. package/dist/templates/auth-scaffold.d.ts +17 -0
  28. package/dist/templates/auth-scaffold.d.ts.map +1 -0
  29. package/dist/templates/auth-scaffold.js +41 -0
  30. package/dist/templates/client.js +10 -1
  31. package/dist/templates/datasets.d.ts +5 -0
  32. package/dist/templates/datasets.d.ts.map +1 -0
  33. package/dist/templates/datasets.js +23 -0
  34. package/dist/templates/env.js +19 -8
  35. package/dist/templates/gitignore.js +4 -1
  36. package/dist/templates/queries.d.ts +3 -0
  37. package/dist/templates/queries.d.ts.map +1 -1
  38. package/dist/templates/queries.js +66 -10
  39. package/dist/test-utils.d.ts +0 -71
  40. package/dist/test-utils.d.ts.map +1 -1
  41. package/dist/test-utils.js +12 -137
  42. package/dist/utils/clickhouse-client.js +10 -57
  43. package/dist/utils/clickhouse-sql.d.ts +6 -0
  44. package/dist/utils/clickhouse-sql.d.ts.map +1 -0
  45. package/dist/utils/clickhouse-sql.js +18 -0
  46. package/dist/utils/clickhouse-type-utils.d.ts +6 -0
  47. package/dist/utils/clickhouse-type-utils.d.ts.map +1 -0
  48. package/dist/utils/clickhouse-type-utils.js +59 -0
  49. package/dist/utils/dependency-installer.d.ts +4 -2
  50. package/dist/utils/dependency-installer.d.ts.map +1 -1
  51. package/dist/utils/dependency-installer.js +93 -170
  52. package/dist/utils/detect-database.js +84 -195
  53. package/dist/utils/find-files.d.ts +3 -1
  54. package/dist/utils/find-files.d.ts.map +1 -1
  55. package/dist/utils/find-files.js +82 -148
  56. package/dist/utils/load-api.d.ts.map +1 -1
  57. package/dist/utils/load-api.js +207 -396
  58. package/dist/utils/logger.js +38 -42
  59. package/dist/utils/prompts.d.ts +14 -0
  60. package/dist/utils/prompts.d.ts.map +1 -1
  61. package/dist/utils/prompts.js +170 -219
  62. package/dist/utils/runtime-guards.d.ts +4 -0
  63. package/dist/utils/runtime-guards.d.ts.map +1 -0
  64. package/dist/utils/runtime-guards.js +9 -0
  65. package/dist/utils/sha256.d.ts +2 -0
  66. package/dist/utils/sha256.d.ts.map +1 -0
  67. package/dist/utils/sha256.js +4 -0
  68. package/package.json +3 -8
  69. package/dist/utils/error-messages.d.ts +0 -6
  70. package/dist/utils/error-messages.d.ts.map +0 -1
  71. package/dist/utils/error-messages.js +0 -19
  72. package/dist/utils/load-hypequery-config.d.ts +0 -7
  73. package/dist/utils/load-hypequery-config.d.ts.map +0 -1
  74. package/dist/utils/load-hypequery-config.js +0 -89
@@ -1,388 +1,355 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- var __generator = (this && this.__generator) || function (thisArg, body) {
11
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
12
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
13
- function verb(n) { return function (v) { return step([n, v]); }; }
14
- function step(op) {
15
- if (f) throw new TypeError("Generator is already executing.");
16
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
17
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
18
- if (y = 0, t) op = [op[0] & 2, t.value];
19
- switch (op[0]) {
20
- case 0: case 1: t = op; break;
21
- case 4: _.label++; return { value: op[1], done: false };
22
- case 5: _.label++; y = op[1]; op = [0]; continue;
23
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
24
- default:
25
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
26
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
27
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
28
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
29
- if (t[2]) _.ops.pop();
30
- _.trys.pop(); continue;
31
- }
32
- op = body.call(thisArg, _);
33
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
34
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35
- }
36
- };
37
1
  import { mkdir, writeFile, readFile, access } from 'node:fs/promises';
38
2
  import path from 'node:path';
39
3
  import ora from 'ora';
40
4
  import { logger } from '../utils/logger.js';
41
- import { promptClickHouseConnection, promptOutputDirectory, promptGenerateExample, promptTableSelection, confirmOverwrite, promptRetry, promptContinueWithoutDb, } from '../utils/prompts.js';
5
+ import { promptClickHouseConnection, promptOutputDirectory, promptInitStyle, promptGenerateExample, promptTableSelection, promptDatasetTableSelection, confirmOverwrite, promptRetry, promptContinueWithoutDb, } from '../utils/prompts.js';
42
6
  import { validateConnection, getTableCount, getTables, } from '../utils/detect-database.js';
43
7
  import { hasEnvFile, hasGitignore } from '../utils/find-files.js';
44
8
  import { generateEnvTemplate, appendToEnv } from '../templates/env.js';
45
9
  import { generateClientTemplate } from '../templates/client.js';
46
10
  import { generateQueriesTemplate } from '../templates/queries.js';
11
+ import { generateApiTemplate } from '../templates/api.js';
12
+ import { generateDatasetsPlaceholderTemplate } from '../templates/datasets.js';
47
13
  import { appendToGitignore } from '../templates/gitignore.js';
48
14
  import { getTypeGenerator } from '../generators/index.js';
15
+ import { generateDatasets } from '../generators/dataset-generator.js';
49
16
  import { installScaffoldDependencies } from '../utils/dependency-installer.js';
50
- function resolveConnectionConfig(options) {
51
- return __awaiter(this, void 0, void 0, function () {
52
- var required;
53
- var _a;
54
- return __generator(this, function (_b) {
55
- if (options.noInteractive) {
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);
59
- if (!value) {
60
- throw new Error("Missing ".concat(values.join(' or '), ". Provide ClickHouse connection info via environment variables when using --no-interactive."));
61
- }
62
- return value;
63
- };
64
- return [2 /*return*/, {
65
- host: required(['CLICKHOUSE_URL', 'CLICKHOUSE_HOST']),
66
- database: required('CLICKHOUSE_DATABASE'),
67
- username: required(['CLICKHOUSE_USERNAME', 'CLICKHOUSE_USER']),
68
- password: (_a = process.env.CLICKHOUSE_PASSWORD) !== null && _a !== void 0 ? _a : '',
69
- }];
17
+ function normalizeInitStyle(style) {
18
+ return style === 'datasets' ? 'datasets' : 'queries';
19
+ }
20
+ function normalizeAuthMode(auth) {
21
+ if (!auth || auth === 'none') {
22
+ return 'none';
23
+ }
24
+ if (auth === 'context') {
25
+ return 'context';
26
+ }
27
+ throw new Error(`Unsupported auth mode "${auth}". Use "none" or "context".`);
28
+ }
29
+ function parseTableList(value) {
30
+ const parsed = value
31
+ ?.split(',')
32
+ .map((table) => table.trim())
33
+ .filter(Boolean);
34
+ return parsed && parsed.length > 0 ? parsed : undefined;
35
+ }
36
+ async function resolveConnectionConfig(options) {
37
+ if (options.noInteractive) {
38
+ const required = (keys) => {
39
+ const values = Array.isArray(keys) ? keys : [keys];
40
+ const value = values.map((key) => process.env[key]).find(Boolean);
41
+ if (!value) {
42
+ throw new Error(`Missing ${values.join(' or ')}. Provide ClickHouse connection info via environment variables when using --no-interactive.`);
70
43
  }
71
- return [2 /*return*/, promptClickHouseConnection()];
72
- });
73
- });
44
+ return value;
45
+ };
46
+ return {
47
+ host: required(['CLICKHOUSE_URL', 'CLICKHOUSE_HOST']),
48
+ database: required('CLICKHOUSE_DATABASE'),
49
+ username: required(['CLICKHOUSE_USERNAME', 'CLICKHOUSE_USER']),
50
+ password: process.env.CLICKHOUSE_PASSWORD ?? '',
51
+ };
52
+ }
53
+ return promptClickHouseConnection();
54
+ }
55
+ async function testConnection(connectionConfig) {
56
+ const spinner = ora('Testing connection...').start();
57
+ process.env.CLICKHOUSE_URL = connectionConfig.host;
58
+ process.env.CLICKHOUSE_HOST = connectionConfig.host;
59
+ process.env.CLICKHOUSE_DATABASE = connectionConfig.database;
60
+ process.env.CLICKHOUSE_USERNAME = connectionConfig.username;
61
+ process.env.CLICKHOUSE_PASSWORD = connectionConfig.password;
62
+ const isValid = await validateConnection('clickhouse');
63
+ if (!isValid) {
64
+ spinner.fail('Connection failed');
65
+ logger.newline();
66
+ logger.error(`Could not connect to ClickHouse at ${connectionConfig.host}`);
67
+ logger.newline();
68
+ logger.info('Common issues:');
69
+ logger.indent('• Check your host URL includes http:// or https://');
70
+ logger.indent('• Verify username and password');
71
+ logger.indent('• Ensure database exists');
72
+ logger.indent('• Check firewall/network access');
73
+ logger.newline();
74
+ return { hasValidConnection: false, tableCount: 0 };
75
+ }
76
+ const tableCount = await getTableCount('clickhouse');
77
+ spinner.succeed(`Connected successfully (${tableCount} tables found)`);
78
+ logger.newline();
79
+ return { hasValidConnection: true, tableCount };
74
80
  }
75
- function testConnection(connectionConfig) {
76
- return __awaiter(this, void 0, void 0, function () {
77
- var spinner, isValid, tableCount;
78
- return __generator(this, function (_a) {
79
- switch (_a.label) {
80
- case 0:
81
- spinner = ora('Testing connection...').start();
82
- process.env.CLICKHOUSE_URL = connectionConfig.host;
83
- process.env.CLICKHOUSE_HOST = connectionConfig.host;
84
- process.env.CLICKHOUSE_DATABASE = connectionConfig.database;
85
- process.env.CLICKHOUSE_USERNAME = connectionConfig.username;
86
- process.env.CLICKHOUSE_PASSWORD = connectionConfig.password;
87
- return [4 /*yield*/, validateConnection('clickhouse')];
88
- case 1:
89
- isValid = _a.sent();
90
- if (!isValid) {
91
- spinner.fail('Connection failed');
92
- logger.newline();
93
- logger.error("Could not connect to ClickHouse at ".concat(connectionConfig.host));
94
- logger.newline();
95
- logger.info('Common issues:');
96
- logger.indent('• Check your host URL includes http:// or https://');
97
- logger.indent('• Verify username and password');
98
- logger.indent(' Ensure database exists');
99
- logger.indent('• Check firewall/network access');
100
- logger.newline();
101
- return [2 /*return*/, { hasValidConnection: false, tableCount: 0 }];
102
- }
103
- return [4 /*yield*/, getTableCount('clickhouse')];
104
- case 2:
105
- tableCount = _a.sent();
106
- spinner.succeed("Connected successfully (".concat(tableCount, " tables found)"));
107
- logger.newline();
108
- return [2 /*return*/, { hasValidConnection: true, tableCount: tableCount }];
81
+ export async function initCommand(options = {}) {
82
+ const noInteractive = options.noInteractive === true || options.interactive === false;
83
+ logger.newline();
84
+ logger.header('Welcome to hypequery!');
85
+ logger.info("Let's set up your analytics layer.");
86
+ logger.newline();
87
+ // Step 2: Get connection details
88
+ let connectionConfig = await resolveConnectionConfig(options);
89
+ let hasValidConnection = false;
90
+ // Handle user skipping connection details
91
+ if (!connectionConfig) {
92
+ logger.info('Skipping database connection for now.');
93
+ logger.newline();
94
+ }
95
+ else if (options.skipConnection) {
96
+ logger.info('Skipping database connection test (requested).');
97
+ logger.newline();
98
+ }
99
+ else {
100
+ const { hasValidConnection: valid } = await testConnection(connectionConfig);
101
+ hasValidConnection = valid;
102
+ if (!hasValidConnection) {
103
+ if (noInteractive) {
104
+ throw new Error('Failed to connect to ClickHouse in non-interactive mode. Check your environment variables or use interactive setup.');
105
+ }
106
+ const retry = await promptRetry('Try again?');
107
+ if (retry) {
108
+ return initCommand(options);
109
+ }
110
+ const continueWithout = await promptContinueWithoutDb();
111
+ if (!continueWithout) {
112
+ logger.info('Setup cancelled');
113
+ process.exit(0);
109
114
  }
110
- });
111
- });
115
+ logger.newline();
116
+ logger.info('Continuing without database connection.');
117
+ logger.info('You can configure the connection later in .env');
118
+ logger.newline();
119
+ connectionConfig = null;
120
+ }
121
+ }
122
+ // Step 4: Get output directory
123
+ let outputDir = options.path;
124
+ if (!outputDir && !noInteractive) {
125
+ outputDir = await promptOutputDirectory();
126
+ }
127
+ else if (!outputDir) {
128
+ outputDir = 'analytics';
129
+ }
130
+ const resolvedOutputDir = path.resolve(process.cwd(), outputDir);
131
+ let style = normalizeInitStyle(options.style);
132
+ const auth = normalizeAuthMode(options.auth);
133
+ if (!options.style && !noInteractive) {
134
+ style = await promptInitStyle();
135
+ }
136
+ // Step 5: Check for existing files
137
+ const filesToCreate = [
138
+ path.join(resolvedOutputDir, 'client.ts'),
139
+ path.join(resolvedOutputDir, 'schema.ts'),
140
+ ...(style === 'datasets'
141
+ ? [
142
+ path.join(resolvedOutputDir, 'datasets.ts'),
143
+ path.join(resolvedOutputDir, 'api.ts'),
144
+ ]
145
+ : [
146
+ path.join(resolvedOutputDir, 'queries.ts'),
147
+ ]),
148
+ ];
149
+ const existingFiles = [];
150
+ for (const file of filesToCreate) {
151
+ try {
152
+ await access(file);
153
+ existingFiles.push(path.relative(process.cwd(), file));
154
+ }
155
+ catch {
156
+ // File doesn't exist, continue
157
+ }
158
+ }
159
+ if (existingFiles.length > 0 && !options.force) {
160
+ logger.warn('Files already exist');
161
+ logger.newline();
162
+ const shouldOverwrite = noInteractive ? false : await confirmOverwrite(existingFiles);
163
+ if (!shouldOverwrite) {
164
+ logger.info('Setup cancelled');
165
+ process.exit(0);
166
+ }
167
+ logger.newline();
168
+ }
169
+ // Step 6: Ask about example query (only if we have a valid connection)
170
+ let generateExample = !options.noExample && hasValidConnection;
171
+ let selectedTable = null;
172
+ let discoveredTables = null;
173
+ if (generateExample && !noInteractive && hasValidConnection) {
174
+ generateExample = await promptGenerateExample();
175
+ if (generateExample) {
176
+ discoveredTables = await getTables('clickhouse');
177
+ selectedTable = await promptTableSelection(discoveredTables);
178
+ generateExample = selectedTable !== null;
179
+ }
180
+ }
181
+ let datasetTables = parseTableList(options.tables);
182
+ const excludedDatasetTables = parseTableList(options.excludeTables);
183
+ if (style === 'datasets' &&
184
+ hasValidConnection &&
185
+ !options.allTables &&
186
+ !datasetTables &&
187
+ !noInteractive) {
188
+ discoveredTables ??= await getTables('clickhouse');
189
+ datasetTables = await promptDatasetTableSelection(discoveredTables, selectedTable ? [selectedTable] : []);
190
+ }
191
+ logger.newline();
192
+ // Step 7: Create directory
193
+ await mkdir(resolvedOutputDir, { recursive: true });
194
+ // Step 8: Save credentials to .env (if we have connection config)
195
+ if (connectionConfig) {
196
+ const envPath = path.join(process.cwd(), '.env');
197
+ const envExists = await hasEnvFile();
198
+ if (envExists) {
199
+ const existingEnv = await readFile(envPath, 'utf-8');
200
+ const newEnv = appendToEnv(existingEnv, generateEnvTemplate(connectionConfig));
201
+ await writeFile(envPath, newEnv);
202
+ logger.success('Updated .env');
203
+ }
204
+ else {
205
+ await writeFile(envPath, generateEnvTemplate(connectionConfig));
206
+ logger.success('Created .env');
207
+ }
208
+ }
209
+ else {
210
+ // Create placeholder .env
211
+ const envPath = path.join(process.cwd(), '.env');
212
+ const envExists = await hasEnvFile();
213
+ const placeholderConfig = {
214
+ url: 'YOUR_CLICKHOUSE_URL',
215
+ database: 'YOUR_DATABASE',
216
+ username: 'YOUR_USERNAME',
217
+ password: 'YOUR_PASSWORD',
218
+ };
219
+ if (!envExists) {
220
+ await writeFile(envPath, generateEnvTemplate(placeholderConfig));
221
+ logger.success('Created .env (configure your credentials)');
222
+ }
223
+ }
224
+ // Step 9: Generate types from schema (only if we have a valid connection)
225
+ const schemaPath = path.join(resolvedOutputDir, 'schema.ts');
226
+ if (hasValidConnection) {
227
+ const typeSpinner = ora('Generating TypeScript types...').start();
228
+ try {
229
+ const generator = getTypeGenerator('clickhouse');
230
+ await generator({ outputPath: schemaPath });
231
+ typeSpinner.succeed(`Generated TypeScript types (${path.relative(process.cwd(), schemaPath)})`);
232
+ }
233
+ catch (error) {
234
+ typeSpinner.fail('Failed to generate types');
235
+ logger.error(error instanceof Error ? error.message : String(error));
236
+ process.exit(1);
237
+ }
238
+ }
239
+ else {
240
+ // Create placeholder schema file
241
+ await writeFile(schemaPath, `// Generated by hypequery
242
+ // Run 'npx hypequery generate' after configuring your database connection
243
+
244
+ export interface IntrospectedSchema {
245
+ // Your table types will appear here after generation
112
246
  }
113
- export function initCommand() {
114
- return __awaiter(this, arguments, void 0, function (options) {
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;
116
- if (options === void 0) { options = {}; }
117
- return __generator(this, function (_d) {
118
- switch (_d.label) {
119
- case 0:
120
- noInteractive = options.noInteractive === true || options.interactive === false;
121
- logger.newline();
122
- logger.header('Welcome to hypequery!');
123
- logger.info("Let's set up your analytics layer.");
124
- logger.newline();
125
- return [4 /*yield*/, resolveConnectionConfig(options)];
126
- case 1:
127
- connectionConfig = _d.sent();
128
- hasValidConnection = false;
129
- tableCount = 0;
130
- if (!!connectionConfig) return [3 /*break*/, 2];
131
- logger.info('Skipping database connection for now.');
132
- logger.newline();
133
- return [3 /*break*/, 7];
134
- case 2:
135
- if (!options.skipConnection) return [3 /*break*/, 3];
136
- logger.info('Skipping database connection test (requested).');
137
- logger.newline();
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;
142
- hasValidConnection = valid;
143
- tableCount = count;
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
- }
148
- return [4 /*yield*/, promptRetry('Try again?')];
149
- case 5:
150
- retry = _d.sent();
151
- if (retry) {
152
- return [2 /*return*/, initCommand(options)];
153
- }
154
- return [4 /*yield*/, promptContinueWithoutDb()];
155
- case 6:
156
- continueWithout = _d.sent();
157
- if (!continueWithout) {
158
- logger.info('Setup cancelled');
159
- process.exit(0);
160
- }
161
- logger.newline();
162
- logger.info('Continuing without database connection.');
163
- logger.info('You can configure the connection later in .env');
164
- logger.newline();
165
- connectionConfig = null;
166
- _d.label = 7;
167
- case 7:
168
- outputDir = options.path;
169
- if (!(!outputDir && !noInteractive)) return [3 /*break*/, 9];
170
- return [4 /*yield*/, promptOutputDirectory()];
171
- case 8:
172
- outputDir = _d.sent();
173
- return [3 /*break*/, 10];
174
- case 9:
175
- if (!outputDir) {
176
- outputDir = 'analytics';
177
- }
178
- _d.label = 10;
179
- case 10:
180
- resolvedOutputDir = path.resolve(process.cwd(), outputDir);
181
- filesToCreate = [
182
- path.join(resolvedOutputDir, 'client.ts'),
183
- path.join(resolvedOutputDir, 'schema.ts'),
184
- path.join(resolvedOutputDir, 'queries.ts'),
185
- ];
186
- existingFiles = [];
187
- _i = 0, filesToCreate_1 = filesToCreate;
188
- _d.label = 11;
189
- case 11:
190
- if (!(_i < filesToCreate_1.length)) return [3 /*break*/, 16];
191
- file = filesToCreate_1[_i];
192
- _d.label = 12;
193
- case 12:
194
- _d.trys.push([12, 14, , 15]);
195
- return [4 /*yield*/, access(file)];
196
- case 13:
197
- _d.sent();
198
- existingFiles.push(path.relative(process.cwd(), file));
199
- return [3 /*break*/, 15];
200
- case 14:
201
- _b = _d.sent();
202
- return [3 /*break*/, 15];
203
- case 15:
204
- _i++;
205
- return [3 /*break*/, 11];
206
- case 16:
207
- if (!(existingFiles.length > 0 && !options.force)) return [3 /*break*/, 20];
208
- logger.warn('Files already exist');
209
- logger.newline();
210
- if (!noInteractive) return [3 /*break*/, 17];
211
- _c = false;
212
- return [3 /*break*/, 19];
213
- case 17: return [4 /*yield*/, confirmOverwrite(existingFiles)];
214
- case 18:
215
- _c = _d.sent();
216
- _d.label = 19;
217
- case 19:
218
- shouldOverwrite = _c;
219
- if (!shouldOverwrite) {
220
- logger.info('Setup cancelled');
221
- process.exit(0);
222
- }
223
- logger.newline();
224
- _d.label = 20;
225
- case 20:
226
- generateExample = !options.noExample && hasValidConnection;
227
- selectedTable = null;
228
- if (!(generateExample && !noInteractive && hasValidConnection)) return [3 /*break*/, 24];
229
- return [4 /*yield*/, promptGenerateExample()];
230
- case 21:
231
- generateExample = _d.sent();
232
- if (!generateExample) return [3 /*break*/, 24];
233
- return [4 /*yield*/, getTables('clickhouse')];
234
- case 22:
235
- tables = _d.sent();
236
- return [4 /*yield*/, promptTableSelection(tables)];
237
- case 23:
238
- selectedTable = _d.sent();
239
- generateExample = selectedTable !== null;
240
- _d.label = 24;
241
- case 24:
242
- logger.newline();
243
- // Step 7: Create directory
244
- return [4 /*yield*/, mkdir(resolvedOutputDir, { recursive: true })];
245
- case 25:
246
- // Step 7: Create directory
247
- _d.sent();
248
- if (!connectionConfig) return [3 /*break*/, 32];
249
- envPath = path.join(process.cwd(), '.env');
250
- return [4 /*yield*/, hasEnvFile()];
251
- case 26:
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();
257
- newEnv = appendToEnv(existingEnv, generateEnvTemplate(connectionConfig));
258
- return [4 /*yield*/, writeFile(envPath, newEnv)];
259
- case 28:
260
- _d.sent();
261
- logger.success('Updated .env');
262
- return [3 /*break*/, 31];
263
- case 29: return [4 /*yield*/, writeFile(envPath, generateEnvTemplate(connectionConfig))];
264
- case 30:
265
- _d.sent();
266
- logger.success('Created .env');
267
- _d.label = 31;
268
- case 31: return [3 /*break*/, 35];
269
- case 32:
270
- envPath = path.join(process.cwd(), '.env');
271
- return [4 /*yield*/, hasEnvFile()];
272
- case 33:
273
- envExists = _d.sent();
274
- placeholderConfig = {
275
- url: 'YOUR_CLICKHOUSE_URL',
276
- database: 'YOUR_DATABASE',
277
- username: 'YOUR_USERNAME',
278
- password: 'YOUR_PASSWORD',
279
- };
280
- if (!!envExists) return [3 /*break*/, 35];
281
- return [4 /*yield*/, writeFile(envPath, generateEnvTemplate(placeholderConfig))];
282
- case 34:
283
- _d.sent();
284
- logger.success('Created .env (configure your credentials)');
285
- _d.label = 35;
286
- case 35:
287
- schemaPath = path.join(resolvedOutputDir, 'schema.ts');
288
- if (!hasValidConnection) return [3 /*break*/, 40];
289
- typeSpinner = ora('Generating TypeScript types...').start();
290
- _d.label = 36;
291
- case 36:
292
- _d.trys.push([36, 38, , 39]);
293
- generator = getTypeGenerator('clickhouse');
294
- return [4 /*yield*/, generator({ outputPath: schemaPath })];
295
- case 37:
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();
301
- typeSpinner.fail('Failed to generate types');
302
- logger.error(error_1 instanceof Error ? error_1.message : String(error_1));
303
- process.exit(1);
304
- return [3 /*break*/, 39];
305
- case 39: return [3 /*break*/, 42];
306
- case 40:
307
- // Create placeholder schema file
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")];
309
- case 41:
310
- // Create placeholder schema file
311
- _d.sent();
312
- logger.success("Created placeholder schema (".concat(path.relative(process.cwd(), schemaPath), ")"));
313
- _d.label = 42;
314
- case 42:
315
- clientPath = path.join(resolvedOutputDir, 'client.ts');
316
- return [4 /*yield*/, writeFile(clientPath, generateClientTemplate())];
317
- case 43:
318
- _d.sent();
319
- logger.success("Created ClickHouse client (".concat(path.relative(process.cwd(), clientPath), ")"));
320
- queriesPath = path.join(resolvedOutputDir, 'queries.ts');
321
- return [4 /*yield*/, writeFile(queriesPath, generateQueriesTemplate({
322
- hasExample: generateExample,
323
- tableName: selectedTable || undefined,
324
- }))];
325
- case 44:
326
- _d.sent();
327
- logger.success("Created queries file (".concat(path.relative(process.cwd(), queriesPath), ")"));
328
- if (generateExample && selectedTable) {
329
- logger.success("Created example query using '".concat(selectedTable, "' table"));
330
- }
331
- gitignorePath = path.join(process.cwd(), '.gitignore');
332
- return [4 /*yield*/, hasGitignore()];
333
- case 45:
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();
339
- newGitignore = appendToGitignore(existingGitignore);
340
- if (!(newGitignore !== existingGitignore)) return [3 /*break*/, 48];
341
- return [4 /*yield*/, writeFile(gitignorePath, newGitignore)];
342
- case 47:
343
- _d.sent();
344
- logger.success('Updated .gitignore');
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();
350
- logger.success('Created .gitignore');
351
- _d.label = 51;
352
- case 51:
353
- // Step 13: Ensure required hypequery packages are installed
354
- return [4 /*yield*/, installScaffoldDependencies()];
355
- case 52:
356
- // Step 13: Ensure required hypequery packages are installed
357
- _d.sent();
358
- // Step 14: Success message
359
- logger.newline();
360
- logger.header('Setup complete!');
361
- if (hasValidConnection) {
362
- logger.info('Try your first query:');
363
- logger.newline();
364
- logger.indent("import { api } from './".concat(path.relative(process.cwd(), queriesPath).replace(/\.ts$/, '.js'), "'"));
365
- exampleQueryKey = generateExample && selectedTable
366
- ? "".concat(selectedTable.replace(/_([a-z])/g, function (_, l) { return l.toUpperCase(); }), "Query")
367
- : 'exampleMetric';
368
- logger.indent("const result = await api.execute('".concat(exampleQueryKey, "')"));
369
- logger.newline();
370
- logger.info('Next:');
371
- logger.indent('npx hypequery dev Start development server');
372
- logger.newline();
373
- }
374
- else {
375
- logger.info('Next steps:');
376
- logger.newline();
377
- logger.indent('1. Configure your database connection in .env');
378
- logger.indent('2. Run: npx hypequery generate (to generate types)');
379
- logger.indent('3. Run: npx hypequery dev (to start dev server)');
380
- logger.newline();
381
- }
382
- logger.info('Docs: https://hypequery.com/docs');
383
- logger.newline();
384
- return [2 /*return*/];
247
+ `);
248
+ logger.success(`Created placeholder schema (${path.relative(process.cwd(), schemaPath)})`);
249
+ }
250
+ // Step 10: Create client.ts
251
+ const clientPath = path.join(resolvedOutputDir, 'client.ts');
252
+ await writeFile(clientPath, generateClientTemplate());
253
+ logger.success(`Created ClickHouse client (${path.relative(process.cwd(), clientPath)})`);
254
+ // Step 11: Create API entrypoint
255
+ let apiPath;
256
+ let generatedAnyDatasets = false;
257
+ let generatedSelectedDataset = false;
258
+ if (style === 'datasets') {
259
+ const datasetsPath = path.join(resolvedOutputDir, 'datasets.ts');
260
+ const shouldGenerateDatasets = hasValidConnection && (options.allTables === true ||
261
+ (datasetTables !== undefined && datasetTables.length > 0));
262
+ if (shouldGenerateDatasets) {
263
+ await generateDatasets({
264
+ outputPath: datasetsPath,
265
+ includeTables: options.allTables ? undefined : datasetTables,
266
+ excludeTables: excludedDatasetTables,
267
+ });
268
+ generatedAnyDatasets = true;
269
+ generatedSelectedDataset = selectedTable !== null && (options.allTables === true ||
270
+ datasetTables?.includes(selectedTable) === true);
271
+ }
272
+ else {
273
+ await writeFile(datasetsPath, generateDatasetsPlaceholderTemplate());
274
+ if (hasValidConnection) {
275
+ logger.info('Skipped dataset generation. Run `hypequery generate:datasets --path ' + outputDir + ' --tables table1,table2` when ready.');
385
276
  }
386
- });
387
- });
277
+ }
278
+ logger.success(`Created datasets file (${path.relative(process.cwd(), datasetsPath)})`);
279
+ apiPath = path.join(resolvedOutputDir, 'api.ts');
280
+ await writeFile(apiPath, generateApiTemplate({ auth }));
281
+ logger.success(`Created API file (${path.relative(process.cwd(), apiPath)})`);
282
+ }
283
+ else {
284
+ apiPath = path.join(resolvedOutputDir, 'queries.ts');
285
+ await writeFile(apiPath, generateQueriesTemplate({
286
+ hasExample: generateExample,
287
+ tableName: selectedTable || undefined,
288
+ auth,
289
+ }));
290
+ logger.success(`Created queries file (${path.relative(process.cwd(), apiPath)})`);
291
+ }
292
+ if (generateExample && selectedTable && (style === 'queries' || generatedSelectedDataset)) {
293
+ logger.success(`Created example ${style === 'datasets' ? 'dataset' : 'query'} using '${selectedTable}' table`);
294
+ }
295
+ // Step 12: Update .gitignore
296
+ const gitignorePath = path.join(process.cwd(), '.gitignore');
297
+ const gitignoreExists = await hasGitignore();
298
+ if (gitignoreExists) {
299
+ const existingGitignore = await readFile(gitignorePath, 'utf-8');
300
+ const newGitignore = appendToGitignore(existingGitignore);
301
+ if (newGitignore !== existingGitignore) {
302
+ await writeFile(gitignorePath, newGitignore);
303
+ logger.success('Updated .gitignore');
304
+ }
305
+ }
306
+ else {
307
+ await writeFile(gitignorePath, appendToGitignore(''));
308
+ logger.success('Created .gitignore');
309
+ }
310
+ // Step 13: Ensure required hypequery packages are installed
311
+ await installScaffoldDependencies(style);
312
+ // Step 14: Success message
313
+ logger.newline();
314
+ logger.header('Setup complete!');
315
+ if (hasValidConnection) {
316
+ if (style === 'datasets' && !generatedAnyDatasets) {
317
+ logger.info('Next:');
318
+ logger.indent(`hypequery generate:datasets --path ${outputDir} --tables table1,table2`);
319
+ logger.newline();
320
+ }
321
+ else if (style === 'datasets' && !generatedSelectedDataset) {
322
+ logger.info('Next:');
323
+ logger.indent('npx hypequery dev Start development server');
324
+ logger.newline();
325
+ }
326
+ else {
327
+ logger.info('Try your first query:');
328
+ logger.newline();
329
+ logger.indent(`import { api } from './${path.relative(process.cwd(), apiPath).replace(/\.ts$/, '.js')}'`);
330
+ const exampleQueryKey = generateExample && selectedTable
331
+ ? `${selectedTable.replace(/_([a-z])/g, (_, l) => l.toUpperCase())}Query`
332
+ : 'exampleMetric';
333
+ if (style === 'datasets' && selectedTable) {
334
+ logger.indent(`const result = await api.execute('dataset:${selectedTable}', { input: {} })`);
335
+ }
336
+ else {
337
+ logger.indent(`const result = await api.execute('${exampleQueryKey}')`);
338
+ }
339
+ logger.newline();
340
+ logger.info('Next:');
341
+ logger.indent('npx hypequery dev Start development server');
342
+ logger.newline();
343
+ }
344
+ }
345
+ else {
346
+ logger.info('Next steps:');
347
+ logger.newline();
348
+ logger.indent('1. Configure your database connection in .env');
349
+ logger.indent('2. Run: npx hypequery generate (to generate types)');
350
+ logger.indent('3. Run: npx hypequery dev (to start dev server)');
351
+ logger.newline();
352
+ }
353
+ logger.info('Docs: https://hypequery.com/docs');
354
+ logger.newline();
388
355
  }