@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.
- package/README.md +71 -149
- package/dist/bin/cli.js +21 -81
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +74 -105
- package/dist/commands/dev.d.ts +1 -0
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +180 -255
- package/dist/commands/generate-datasets.d.ts +14 -0
- package/dist/commands/generate-datasets.d.ts.map +1 -0
- package/dist/commands/generate-datasets.js +96 -0
- package/dist/commands/generate.d.ts +2 -0
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +112 -167
- package/dist/commands/init.d.ts +7 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +337 -390
- package/dist/generators/clickhouse.d.ts +1 -1
- package/dist/generators/clickhouse.d.ts.map +1 -1
- package/dist/generators/clickhouse.js +10 -269
- package/dist/generators/dataset-generator.d.ts +16 -0
- package/dist/generators/dataset-generator.d.ts.map +1 -0
- package/dist/generators/dataset-generator.js +213 -0
- package/dist/generators/index.js +4 -4
- package/dist/templates/api.d.ts +9 -0
- package/dist/templates/api.d.ts.map +1 -0
- package/dist/templates/api.js +26 -0
- package/dist/templates/auth-scaffold.d.ts +17 -0
- package/dist/templates/auth-scaffold.d.ts.map +1 -0
- package/dist/templates/auth-scaffold.js +41 -0
- package/dist/templates/client.js +10 -1
- package/dist/templates/datasets.d.ts +5 -0
- package/dist/templates/datasets.d.ts.map +1 -0
- package/dist/templates/datasets.js +23 -0
- package/dist/templates/env.d.ts +2 -1
- package/dist/templates/env.d.ts.map +1 -1
- package/dist/templates/env.js +19 -6
- package/dist/templates/gitignore.js +4 -1
- package/dist/templates/queries.d.ts +3 -0
- package/dist/templates/queries.d.ts.map +1 -1
- package/dist/templates/queries.js +66 -9
- package/dist/test-utils.d.ts +0 -72
- package/dist/test-utils.d.ts.map +1 -1
- package/dist/test-utils.js +12 -138
- package/dist/utils/clickhouse-client.js +11 -58
- package/dist/utils/clickhouse-sql.d.ts +6 -0
- package/dist/utils/clickhouse-sql.d.ts.map +1 -0
- package/dist/utils/clickhouse-sql.js +18 -0
- package/dist/utils/clickhouse-type-utils.d.ts +6 -0
- package/dist/utils/clickhouse-type-utils.d.ts.map +1 -0
- package/dist/utils/clickhouse-type-utils.js +59 -0
- package/dist/utils/dependency-installer.d.ts +5 -1
- package/dist/utils/dependency-installer.d.ts.map +1 -1
- package/dist/utils/dependency-installer.js +117 -134
- package/dist/utils/detect-database.js +84 -195
- package/dist/utils/find-files.d.ts +3 -1
- package/dist/utils/find-files.d.ts.map +1 -1
- package/dist/utils/find-files.js +82 -148
- package/dist/utils/load-api.d.ts.map +1 -1
- package/dist/utils/load-api.js +207 -396
- package/dist/utils/logger.js +38 -42
- package/dist/utils/prompts.d.ts +14 -5
- package/dist/utils/prompts.d.ts.map +1 -1
- package/dist/utils/prompts.js +170 -244
- package/dist/utils/runtime-guards.d.ts +4 -0
- package/dist/utils/runtime-guards.d.ts.map +1 -0
- package/dist/utils/runtime-guards.js +9 -0
- package/dist/utils/sha256.d.ts +2 -0
- package/dist/utils/sha256.d.ts.map +1 -0
- package/dist/utils/sha256.js +4 -0
- package/package.json +4 -4
- package/dist/utils/error-messages.d.ts +0 -6
- package/dist/utils/error-messages.d.ts.map +0 -1
- package/dist/utils/error-messages.js +0 -19
- package/dist/utils/load-hypequery-config.d.ts +0 -7
- package/dist/utils/load-hypequery-config.d.ts.map +0 -1
- package/dist/utils/load-hypequery-config.js +0 -89
package/dist/templates/client.js
CHANGED
|
@@ -2,5 +2,14 @@
|
|
|
2
2
|
* Generate client.ts file for ClickHouse
|
|
3
3
|
*/
|
|
4
4
|
export function generateClientTemplate() {
|
|
5
|
-
return
|
|
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 @@
|
|
|
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
|
+
}
|
package/dist/templates/env.d.ts
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/templates/env.js
CHANGED
|
@@ -2,11 +2,24 @@
|
|
|
2
2
|
* Generate .env file content for ClickHouse
|
|
3
3
|
*/
|
|
4
4
|
export function generateEnvTemplate(config) {
|
|
5
|
-
|
|
5
|
+
const connectionUrl = config.url ?? config.host ?? '';
|
|
6
|
+
const isPlaceholder = connectionUrl.includes('YOUR_');
|
|
6
7
|
if (isPlaceholder) {
|
|
7
|
-
return
|
|
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
|
|
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
|
-
|
|
19
|
-
|
|
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
|
-
|
|
35
|
+
let endIndex = startIndex + 1;
|
|
23
36
|
while (endIndex < lines.length &&
|
|
24
37
|
(lines[endIndex].startsWith('CLICKHOUSE_') || lines[endIndex].trim() === '')) {
|
|
25
38
|
endIndex++;
|
|
@@ -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;
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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 +=
|
|
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 +=
|
|
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 +=
|
|
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,
|
|
79
|
+
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
23
80
|
}
|
|
24
81
|
function pascalCase(str) {
|
|
25
|
-
|
|
82
|
+
const camel = camelCase(str);
|
|
26
83
|
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
27
84
|
}
|
package/dist/test-utils.d.ts
CHANGED
|
@@ -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
|
package/dist/test-utils.d.ts.map
CHANGED
|
@@ -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
|
|
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"}
|
package/dist/test-utils.js
CHANGED
|
@@ -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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
138
|
-
}(Error));
|
|
139
|
-
export { ProcessExitError };
|
|
13
|
+
}
|
|
140
14
|
export function mockProcessExit() {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
throw new ProcessExitError(code
|
|
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
|
|
148
|
-
restore:
|
|
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
|
-
|
|
2
|
+
let client = null;
|
|
39
3
|
function readEnv(name, fallback) {
|
|
40
|
-
|
|
4
|
+
const value = process.env[name];
|
|
41
5
|
return value === undefined || value === '' ? fallback : value;
|
|
42
6
|
}
|
|
43
7
|
export function getClickHouseConfigFromEnv() {
|
|
44
|
-
|
|
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
|
|
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
|
-
|
|
21
|
+
const config = getClickHouseConfigFromEnv();
|
|
59
22
|
if (!config) {
|
|
60
|
-
throw new Error('ClickHouse connection details are missing. Set
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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"}
|