@hypequery/cli 1.1.1 → 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 (77) hide show
  1. package/README.md +71 -149
  2. package/dist/bin/cli.js +21 -81
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +74 -105
  6. package/dist/commands/dev.d.ts +1 -0
  7. package/dist/commands/dev.d.ts.map +1 -1
  8. package/dist/commands/dev.js +180 -255
  9. package/dist/commands/generate-datasets.d.ts +14 -0
  10. package/dist/commands/generate-datasets.d.ts.map +1 -0
  11. package/dist/commands/generate-datasets.js +96 -0
  12. package/dist/commands/generate.d.ts +2 -0
  13. package/dist/commands/generate.d.ts.map +1 -1
  14. package/dist/commands/generate.js +112 -167
  15. package/dist/commands/init.d.ts +7 -1
  16. package/dist/commands/init.d.ts.map +1 -1
  17. package/dist/commands/init.js +337 -390
  18. package/dist/generators/clickhouse.d.ts +1 -1
  19. package/dist/generators/clickhouse.d.ts.map +1 -1
  20. package/dist/generators/clickhouse.js +10 -269
  21. package/dist/generators/dataset-generator.d.ts +16 -0
  22. package/dist/generators/dataset-generator.d.ts.map +1 -0
  23. package/dist/generators/dataset-generator.js +213 -0
  24. package/dist/generators/index.js +4 -4
  25. package/dist/templates/api.d.ts +9 -0
  26. package/dist/templates/api.d.ts.map +1 -0
  27. package/dist/templates/api.js +26 -0
  28. package/dist/templates/auth-scaffold.d.ts +17 -0
  29. package/dist/templates/auth-scaffold.d.ts.map +1 -0
  30. package/dist/templates/auth-scaffold.js +41 -0
  31. package/dist/templates/client.js +10 -1
  32. package/dist/templates/datasets.d.ts +5 -0
  33. package/dist/templates/datasets.d.ts.map +1 -0
  34. package/dist/templates/datasets.js +23 -0
  35. package/dist/templates/env.d.ts +2 -1
  36. package/dist/templates/env.d.ts.map +1 -1
  37. package/dist/templates/env.js +19 -6
  38. package/dist/templates/gitignore.js +4 -1
  39. package/dist/templates/queries.d.ts +3 -0
  40. package/dist/templates/queries.d.ts.map +1 -1
  41. package/dist/templates/queries.js +66 -9
  42. package/dist/test-utils.d.ts +0 -72
  43. package/dist/test-utils.d.ts.map +1 -1
  44. package/dist/test-utils.js +12 -138
  45. package/dist/utils/clickhouse-client.js +11 -58
  46. package/dist/utils/clickhouse-sql.d.ts +6 -0
  47. package/dist/utils/clickhouse-sql.d.ts.map +1 -0
  48. package/dist/utils/clickhouse-sql.js +18 -0
  49. package/dist/utils/clickhouse-type-utils.d.ts +6 -0
  50. package/dist/utils/clickhouse-type-utils.d.ts.map +1 -0
  51. package/dist/utils/clickhouse-type-utils.js +59 -0
  52. package/dist/utils/dependency-installer.d.ts +5 -1
  53. package/dist/utils/dependency-installer.d.ts.map +1 -1
  54. package/dist/utils/dependency-installer.js +117 -134
  55. package/dist/utils/detect-database.js +84 -195
  56. package/dist/utils/find-files.d.ts +3 -1
  57. package/dist/utils/find-files.d.ts.map +1 -1
  58. package/dist/utils/find-files.js +82 -148
  59. package/dist/utils/load-api.d.ts.map +1 -1
  60. package/dist/utils/load-api.js +207 -396
  61. package/dist/utils/logger.js +38 -42
  62. package/dist/utils/prompts.d.ts +14 -5
  63. package/dist/utils/prompts.d.ts.map +1 -1
  64. package/dist/utils/prompts.js +170 -244
  65. package/dist/utils/runtime-guards.d.ts +4 -0
  66. package/dist/utils/runtime-guards.d.ts.map +1 -0
  67. package/dist/utils/runtime-guards.js +9 -0
  68. package/dist/utils/sha256.d.ts +2 -0
  69. package/dist/utils/sha256.d.ts.map +1 -0
  70. package/dist/utils/sha256.js +4 -0
  71. package/package.json +4 -4
  72. package/dist/utils/error-messages.d.ts +0 -6
  73. package/dist/utils/error-messages.d.ts.map +0 -1
  74. package/dist/utils/error-messages.js +0 -19
  75. package/dist/utils/load-hypequery-config.d.ts +0 -7
  76. package/dist/utils/load-hypequery-config.d.ts.map +0 -1
  77. package/dist/utils/load-hypequery-config.js +0 -89
@@ -2,5 +2,14 @@
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';
6
+ import type { IntrospectedSchema } from './schema.js';
7
+
8
+ export const db = createQueryBuilder<IntrospectedSchema>({
9
+ url: process.env.CLICKHOUSE_URL ?? process.env.CLICKHOUSE_HOST!,
10
+ database: process.env.CLICKHOUSE_DATABASE!,
11
+ username: process.env.CLICKHOUSE_USERNAME!,
12
+ password: process.env.CLICKHOUSE_PASSWORD,
13
+ });
14
+ `;
6
15
  }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Generate placeholder datasets.ts file for no-connection init.
3
+ */
4
+ export declare function generateDatasetsPlaceholderTemplate(): string;
5
+ //# sourceMappingURL=datasets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"datasets.d.ts","sourceRoot":"","sources":["../../src/templates/datasets.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,mCAAmC,IAAI,MAAM,CAmB5D"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Generate placeholder datasets.ts file for no-connection init.
3
+ */
4
+ export function generateDatasetsPlaceholderTemplate() {
5
+ return `import { dataset, dimension, measure } from '@hypequery/datasets';
6
+
7
+ const exampleEvents = dataset('example_events', {
8
+ source: 'example_events',
9
+ timeKey: 'created_at',
10
+ dimensions: {
11
+ id: dimension.string(),
12
+ createdAt: dimension.timestamp({ column: 'created_at' }),
13
+ },
14
+ measures: {
15
+ eventCount: measure.count('id', { label: 'Event Count' }),
16
+ },
17
+ });
18
+
19
+ export const datasets = {
20
+ exampleEvents,
21
+ };
22
+ `;
23
+ }
@@ -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,24 @@
2
2
  * Generate .env file content for ClickHouse
3
3
  */
4
4
  export function generateEnvTemplate(config) {
5
- var isPlaceholder = config.host.includes('YOUR_');
5
+ const connectionUrl = config.url ?? config.host ?? '';
6
+ const isPlaceholder = connectionUrl.includes('YOUR_');
6
7
  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");
8
+ return `# Hypequery Configuration
9
+ # Replace these placeholder values with your actual ClickHouse credentials
10
+
11
+ CLICKHOUSE_URL=${connectionUrl}
12
+ CLICKHOUSE_DATABASE=${config.database}
13
+ CLICKHOUSE_USERNAME=${config.username}
14
+ CLICKHOUSE_PASSWORD=${config.password}
15
+ `;
8
16
  }
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");
17
+ return `# Hypequery Configuration
18
+ CLICKHOUSE_URL=${connectionUrl}
19
+ CLICKHOUSE_DATABASE=${config.database}
20
+ CLICKHOUSE_USERNAME=${config.username}
21
+ CLICKHOUSE_PASSWORD=${config.password}
22
+ `;
10
23
  }
11
24
  /**
12
25
  * Append to existing .env file
@@ -15,11 +28,11 @@ export function appendToEnv(existingContent, newContent) {
15
28
  // Check if hypequery section already exists
16
29
  if (existingContent.includes('# Hypequery Configuration')) {
17
30
  // Replace existing section
18
- var lines = existingContent.split('\n');
19
- var startIndex = lines.findIndex(function (l) { return l.includes('# Hypequery Configuration'); });
31
+ const lines = existingContent.split('\n');
32
+ const startIndex = lines.findIndex(l => l.includes('# Hypequery Configuration'));
20
33
  if (startIndex !== -1) {
21
34
  // Find the end of the hypequery section
22
- var endIndex = startIndex + 1;
35
+ let endIndex = startIndex + 1;
23
36
  while (endIndex < lines.length &&
24
37
  (lines[endIndex].startsWith('CLICKHOUSE_') || lines[endIndex].trim() === '')) {
25
38
  endIndex++;
@@ -1,7 +1,10 @@
1
1
  /**
2
2
  * Content to append to .gitignore
3
3
  */
4
- export var GITIGNORE_CONTENT = "\n# Hypequery\n.env\n";
4
+ export const GITIGNORE_CONTENT = `
5
+ # Hypequery
6
+ .env
7
+ `;
5
8
  /**
6
9
  * Check if .gitignore already has hypequery entries
7
10
  */
@@ -1,8 +1,11 @@
1
+ import { type AuthTemplateMode } from './auth-scaffold.js';
2
+ export type { AuthTemplateMode };
1
3
  /**
2
4
  * Generate queries.ts file
3
5
  */
4
6
  export declare function generateQueriesTemplate(options: {
5
7
  hasExample: boolean;
6
8
  tableName?: string;
9
+ auth?: AuthTemplateMode;
7
10
  }): string;
8
11
  //# sourceMappingURL=queries.d.ts.map
@@ -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,OAAO,EACL,KAAK,gBAAgB,EAGtB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAEjC;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAC/C,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB,GAAG,MAAM,CAwET"}
@@ -1,27 +1,84 @@
1
+ import { hostUserHelpers, contextAuthConfig, } from './auth-scaffold.js';
1
2
  /**
2
3
  * Generate queries.ts file
3
4
  */
4
5
  export function generateQueriesTemplate(options) {
5
- var hasExample = options.hasExample, tableName = options.tableName;
6
- var metricKey = hasExample && tableName ? "".concat(camelCase(tableName), "Query") : 'exampleMetric';
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";
6
+ const { hasExample, tableName, auth = 'none' } = options;
7
+ const metricKey = hasExample && tableName ? `${camelCase(tableName)}Query` : 'exampleMetric';
8
+ const typeAlias = `${pascalCase(metricKey)}Result`;
9
+ const routePath = `/metrics/${metricKey}`;
10
+ const serveImports = auth === 'context' ? 'fromContext, initServe' : 'initServe';
11
+ const authHelpers = auth === 'context'
12
+ ? `\n${hostUserHelpers}\n`
13
+ : '';
14
+ const authConfig = auth === 'context'
15
+ ? `${contextAuthConfig}\n`
16
+ : '';
17
+ let template = `import { ${serveImports} } from '@hypequery/serve';
18
+ import type { InferApiType } from '@hypequery/serve';
19
+ import { z } from 'zod';
20
+ import { db } from './client.js';
21
+ ${authHelpers}
22
+ const { query, serve } = initServe({
23
+ context: () => ({ db }),
24
+ ${authConfig}});
25
+
26
+ `;
9
27
  if (hasExample && tableName) {
10
- 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});");
28
+ template += `
29
+ const ${camelCase(tableName)}Query = query({
30
+ description: 'Example query using the ${tableName} table',
31
+ query: async ({ ctx }) =>
32
+ ctx.db
33
+ .table('${tableName}')
34
+ .select('*')
35
+ .limit(10)
36
+ .execute(),
37
+ });`;
11
38
  }
12
39
  else {
13
- 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});";
40
+ template += `
41
+ const exampleMetric = query({
42
+ description: 'Example metric that returns a simple value',
43
+ output: z.object({ ok: z.boolean() }),
44
+ query: async () => ({ ok: true }),
45
+ });`;
14
46
  }
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");
47
+ template += `
48
+ export const api = serve({
49
+ queries: {
50
+ ${metricKey},
51
+ },
52
+ });
53
+ api.route('${routePath}', api.queries.${metricKey});
54
+
55
+ export type ApiDefinition = InferApiType<typeof api>;
56
+
57
+ /**
58
+ * Inline usage example:
59
+ *
60
+ * const result = await api.execute('${metricKey}');
61
+ * console.log(result);
62
+ *
63
+ * // import type { InferQueryResult } from '@hypequery/serve';
64
+ * type ${typeAlias} = InferQueryResult<typeof api, '${metricKey}'>;
65
+ *
66
+ * HTTP route:
67
+ * ${routePath}
68
+ *
69
+ * Dev server:
70
+ * npx hypequery dev analytics/queries.ts
71
+ */
72
+ `;
16
73
  return template;
17
74
  }
18
75
  /**
19
76
  * Convert table name to camelCase
20
77
  */
21
78
  function camelCase(str) {
22
- return str.replace(/_([a-z])/g, function (_, letter) { return letter.toUpperCase(); });
79
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
23
80
  }
24
81
  function pascalCase(str) {
25
- var camel = camelCase(str);
82
+ const camel = camelCase(str);
26
83
  return camel.charAt(0).toUpperCase() + camel.slice(1);
27
84
  }
@@ -1,76 +1,4 @@
1
1
  import type { Mock } from 'vitest';
2
- /**
3
- * Mock prompts module for testing
4
- */
5
- export declare function createMockPrompts(): {
6
- promptDatabaseType: Mock<(...args: any[]) => any>;
7
- promptClickHouseConnection: Mock<(...args: any[]) => any>;
8
- promptOutputDirectory: Mock<(...args: any[]) => any>;
9
- promptGenerateExample: Mock<(...args: any[]) => any>;
10
- promptTableSelection: Mock<(...args: any[]) => any>;
11
- confirmOverwrite: Mock<(...args: any[]) => any>;
12
- promptRetry: Mock<(...args: any[]) => any>;
13
- promptContinueWithoutDb: Mock<(...args: any[]) => any>;
14
- };
15
- /**
16
- * Mock database detection utilities
17
- */
18
- export declare function createMockDatabaseUtils(): {
19
- validateConnection: Mock<(...args: any[]) => any>;
20
- getTableCount: Mock<(...args: any[]) => any>;
21
- getTables: Mock<(...args: any[]) => any>;
22
- };
23
- /**
24
- * Mock file system utilities
25
- */
26
- export declare function createMockFileUtils(): {
27
- hasEnvFile: Mock<(...args: any[]) => any>;
28
- hasGitignore: Mock<(...args: any[]) => any>;
29
- findQueriesFile: Mock<(...args: any[]) => any>;
30
- findSchemaFile: Mock<(...args: any[]) => any>;
31
- };
32
- /**
33
- * Mock logger to capture output
34
- */
35
- export declare function createMockLogger(): {
36
- success: Mock<(...args: any[]) => any>;
37
- error: Mock<(...args: any[]) => any>;
38
- warn: Mock<(...args: any[]) => any>;
39
- info: Mock<(...args: any[]) => any>;
40
- reload: Mock<(...args: any[]) => any>;
41
- header: Mock<(...args: any[]) => any>;
42
- newline: Mock<(...args: any[]) => any>;
43
- indent: Mock<(...args: any[]) => any>;
44
- box: Mock<(...args: any[]) => any>;
45
- table: Mock<(...args: any[]) => any>;
46
- raw: Mock<(...args: any[]) => any>;
47
- };
48
- /**
49
- * Mock ora spinner
50
- */
51
- export declare function createMockSpinner(): Mock<() => {
52
- start: Mock<(...args: any[]) => any>;
53
- succeed: Mock<(...args: any[]) => any>;
54
- fail: Mock<(...args: any[]) => any>;
55
- stop: Mock<(...args: any[]) => any>;
56
- }>;
57
- /**
58
- * Mock fs/promises for file operations
59
- */
60
- export declare function createMockFs(): {
61
- mkdir: Mock<(...args: any[]) => any>;
62
- writeFile: Mock<(...args: any[]) => any>;
63
- readFile: Mock<(...args: any[]) => any>;
64
- access: Mock<(...args: any[]) => any>;
65
- };
66
- /**
67
- * Capture console output
68
- */
69
- export declare function captureConsole(): {
70
- logs: string[];
71
- errors: string[];
72
- restore: () => void;
73
- };
74
2
  /**
75
3
  * Mock process.exit to prevent tests from exiting
76
4
  * Throws a special error to stop execution
@@ -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;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACtB,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;CAIhC;AAED,wBAAgB,eAAe;;;EAc9B"}
@@ -1,151 +1,25 @@
1
- var __extends = (this && this.__extends) || (function () {
2
- var extendStatics = function (d, b) {
3
- extendStatics = Object.setPrototypeOf ||
4
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
5
- function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
6
- return extendStatics(d, b);
7
- };
8
- return function (d, b) {
9
- if (typeof b !== "function" && b !== null)
10
- throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
11
- extendStatics(d, b);
12
- function __() { this.constructor = d; }
13
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
14
- };
15
- })();
16
1
  import { vi } from 'vitest';
17
- /**
18
- * Mock prompts module for testing
19
- */
20
- export function createMockPrompts() {
21
- return {
22
- promptDatabaseType: vi.fn(),
23
- promptClickHouseConnection: vi.fn(),
24
- promptOutputDirectory: vi.fn(),
25
- promptGenerateExample: vi.fn(),
26
- promptTableSelection: vi.fn(),
27
- confirmOverwrite: vi.fn(),
28
- promptRetry: vi.fn(),
29
- promptContinueWithoutDb: vi.fn(),
30
- };
31
- }
32
- /**
33
- * Mock database detection utilities
34
- */
35
- export function createMockDatabaseUtils() {
36
- return {
37
- validateConnection: vi.fn(),
38
- getTableCount: vi.fn(),
39
- getTables: vi.fn(),
40
- };
41
- }
42
- /**
43
- * Mock file system utilities
44
- */
45
- export function createMockFileUtils() {
46
- return {
47
- hasEnvFile: vi.fn(),
48
- hasGitignore: vi.fn(),
49
- findQueriesFile: vi.fn(),
50
- findSchemaFile: vi.fn(),
51
- };
52
- }
53
- /**
54
- * Mock logger to capture output
55
- */
56
- export function createMockLogger() {
57
- return {
58
- success: vi.fn(),
59
- error: vi.fn(),
60
- warn: vi.fn(),
61
- info: vi.fn(),
62
- reload: vi.fn(),
63
- header: vi.fn(),
64
- newline: vi.fn(),
65
- indent: vi.fn(),
66
- box: vi.fn(),
67
- table: vi.fn(),
68
- raw: vi.fn(),
69
- };
70
- }
71
- /**
72
- * Mock ora spinner
73
- */
74
- export function createMockSpinner() {
75
- var spinner = {
76
- start: vi.fn().mockReturnThis(),
77
- succeed: vi.fn().mockReturnThis(),
78
- fail: vi.fn().mockReturnThis(),
79
- stop: vi.fn().mockReturnThis(),
80
- };
81
- return vi.fn(function () { return spinner; });
82
- }
83
- /**
84
- * Mock fs/promises for file operations
85
- */
86
- export function createMockFs() {
87
- return {
88
- mkdir: vi.fn().mockResolvedValue(undefined),
89
- writeFile: vi.fn().mockResolvedValue(undefined),
90
- readFile: vi.fn().mockResolvedValue(''),
91
- access: vi.fn().mockResolvedValue(undefined),
92
- };
93
- }
94
- /**
95
- * Capture console output
96
- */
97
- export function captureConsole() {
98
- var logs = [];
99
- var errors = [];
100
- var originalLog = console.log;
101
- var originalError = console.error;
102
- console.log = vi.fn(function () {
103
- var args = [];
104
- for (var _i = 0; _i < arguments.length; _i++) {
105
- args[_i] = arguments[_i];
106
- }
107
- logs.push(args.join(' '));
108
- });
109
- console.error = vi.fn(function () {
110
- var args = [];
111
- for (var _i = 0; _i < arguments.length; _i++) {
112
- args[_i] = arguments[_i];
113
- }
114
- errors.push(args.join(' '));
115
- });
116
- return {
117
- logs: logs,
118
- errors: errors,
119
- restore: function () {
120
- console.log = originalLog;
121
- console.error = originalError;
122
- },
123
- };
124
- }
125
2
  /**
126
3
  * Mock process.exit to prevent tests from exiting
127
4
  * Throws a special error to stop execution
128
5
  */
129
- var ProcessExitError = /** @class */ (function (_super) {
130
- __extends(ProcessExitError, _super);
131
- function ProcessExitError(code) {
132
- var _this = _super.call(this, "process.exit called with code ".concat(code)) || this;
133
- _this.code = code;
134
- _this.name = 'ProcessExitError';
135
- return _this;
6
+ export class ProcessExitError extends Error {
7
+ code;
8
+ constructor(code) {
9
+ super(`process.exit called with code ${code}`);
10
+ this.code = code;
11
+ this.name = 'ProcessExitError';
136
12
  }
137
- return ProcessExitError;
138
- }(Error));
139
- export { ProcessExitError };
13
+ }
140
14
  export function mockProcessExit() {
141
- var originalExit = process.exit;
142
- var exitMock = vi.fn(function (code) {
143
- throw new ProcessExitError(code !== null && code !== void 0 ? code : 0);
15
+ const originalExit = process.exit;
16
+ const exitMock = vi.fn((code) => {
17
+ throw new ProcessExitError(code ?? 0);
144
18
  });
145
19
  process.exit = exitMock;
146
20
  return {
147
- exitMock: exitMock,
148
- restore: function () {
21
+ exitMock,
22
+ restore: () => {
149
23
  process.exit = originalExit;
150
24
  },
151
25
  };
@@ -1,53 +1,16 @@
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 { createClient } from '@clickhouse/client';
38
- var client = null;
2
+ let client = null;
39
3
  function readEnv(name, fallback) {
40
- var value = process.env[name];
4
+ const value = process.env[name];
41
5
  return value === undefined || value === '' ? fallback : value;
42
6
  }
43
7
  export function getClickHouseConfigFromEnv() {
44
- var _a;
45
- var url = (_a = readEnv('CLICKHOUSE_URL')) !== null && _a !== void 0 ? _a : readEnv('CLICKHOUSE_HOST');
8
+ const url = readEnv('CLICKHOUSE_URL') ?? readEnv('CLICKHOUSE_HOST');
46
9
  if (!url) {
47
10
  return null;
48
11
  }
49
12
  return {
50
- url: url,
13
+ url,
51
14
  username: readEnv('CLICKHOUSE_USERNAME', readEnv('CLICKHOUSE_USER', 'default')),
52
15
  password: readEnv('CLICKHOUSE_PASSWORD', readEnv('CLICKHOUSE_PASS', '')),
53
16
  database: readEnv('CLICKHOUSE_DATABASE', 'default'),
@@ -55,9 +18,9 @@ export function getClickHouseConfigFromEnv() {
55
18
  }
56
19
  export function getClickHouseClient() {
57
20
  if (!client) {
58
- var config = getClickHouseConfigFromEnv();
21
+ const config = getClickHouseConfigFromEnv();
59
22
  if (!config) {
60
- throw new Error('ClickHouse connection details are missing. Set CLICKHOUSE_HOST (or CLICKHOUSE_URL), CLICKHOUSE_DATABASE, CLICKHOUSE_USERNAME, and CLICKHOUSE_PASSWORD.');
23
+ throw new Error('ClickHouse connection details are missing. Set CLICKHOUSE_URL (or CLICKHOUSE_HOST), CLICKHOUSE_DATABASE, CLICKHOUSE_USERNAME, and CLICKHOUSE_PASSWORD.');
61
24
  }
62
25
  client = createClient({
63
26
  url: config.url,
@@ -68,19 +31,9 @@ export function getClickHouseClient() {
68
31
  }
69
32
  return client;
70
33
  }
71
- export function resetClickHouseClientForTesting() {
72
- return __awaiter(this, void 0, void 0, function () {
73
- return __generator(this, function (_a) {
74
- switch (_a.label) {
75
- case 0:
76
- if (!client) return [3 /*break*/, 2];
77
- return [4 /*yield*/, client.close()];
78
- case 1:
79
- _a.sent();
80
- client = null;
81
- _a.label = 2;
82
- case 2: return [2 /*return*/];
83
- }
84
- });
85
- });
34
+ export async function resetClickHouseClientForTesting() {
35
+ if (client) {
36
+ await client.close();
37
+ client = null;
38
+ }
86
39
  }
@@ -0,0 +1,6 @@
1
+ export declare function quoteIdentifier(identifier: string): string;
2
+ export declare function assertValidIdentifier(identifier: string): void;
3
+ export declare function sqlString(value: string): string;
4
+ export declare function sqlStringArray(values: string[]): string;
5
+ export declare function sqlDateTime(value: Date): string;
6
+ //# sourceMappingURL=clickhouse-sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clickhouse-sql.d.ts","sourceRoot":"","sources":["../../src/utils/clickhouse-sql.ts"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,QAIvD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,UAEtC;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,UAE9C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,IAAI,UAEtC"}
@@ -0,0 +1,18 @@
1
+ export function quoteIdentifier(identifier) {
2
+ assertValidIdentifier(identifier);
3
+ return `\`${identifier.replace(/`/g, '``')}\``;
4
+ }
5
+ export function assertValidIdentifier(identifier) {
6
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier)) {
7
+ throw new Error(`Invalid ClickHouse identifier: ${identifier}`);
8
+ }
9
+ }
10
+ export function sqlString(value) {
11
+ return `'${value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
12
+ }
13
+ export function sqlStringArray(values) {
14
+ return `[${values.map(sqlString).join(', ')}]`;
15
+ }
16
+ export function sqlDateTime(value) {
17
+ return `parseDateTime64BestEffort(${sqlString(value.toISOString())}, 3)`;
18
+ }
@@ -0,0 +1,6 @@
1
+ export declare function splitTopLevelArgs(value: string): string[];
2
+ export declare function unwrapType(type: string, wrapperName: string): string | null;
3
+ export declare function matchTypeCall(type: string, name: string): string[] | null;
4
+ export declare function unquoteClickHouseString(value: string): string;
5
+ export declare function isIntegerLiteral(value: string): boolean;
6
+ //# sourceMappingURL=clickhouse-type-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clickhouse-type-utils.d.ts","sourceRoot":"","sources":["../../src/utils/clickhouse-type-utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA+CzD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG3E;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAGzE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,UASpD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,WAE7C"}