@hypequery/cli 0.0.9 → 1.1.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 CHANGED
@@ -4,7 +4,14 @@ Command-line interface for Hypequery - the type-safe analytics layer for ClickHo
4
4
 
5
5
  ## Quick Start
6
6
 
7
- **No installation required!** Use `npx` to run commands directly:
7
+ The CLI scaffolds and runs the main hypequery path:
8
+
9
+ 1. Generate schema types from ClickHouse
10
+ 2. Build queries with the typed query builder
11
+ 3. Wrap reusable queries with `query({ ... })`
12
+ 4. Add `serve({ queries })` when you want HTTP routes and docs
13
+
14
+ Use `npx` to run commands directly:
8
15
 
9
16
  ```bash
10
17
  # Initialize a new project
@@ -68,7 +75,7 @@ npx hypequery init
68
75
  **What it does:**
69
76
  - Connects to your ClickHouse database
70
77
  - Generates TypeScript types from your schema
71
- - Creates client, queries, and config files
78
+ - Creates the client, query, and serve files for the main path
72
79
  - Sets up `.env` with connection details
73
80
  - Updates `.gitignore` to protect secrets
74
81
 
@@ -192,7 +199,12 @@ npm run db:dev
192
199
 
193
200
  ## Documentation
194
201
 
195
- Visit [hypequery.com/docs](https://hypequery.com/docs) for full documentation.
202
+ Visit the main docs flow:
203
+
204
+ - [Quick Start](https://hypequery.com/docs/quick-start)
205
+ - [Core Concepts](https://hypequery.com/docs/core-concepts)
206
+ - [Query Building](https://hypequery.com/docs/query-building/basics)
207
+ - [Serve Runtime Reference](https://hypequery.com/docs/reference/runtime)
196
208
 
197
209
  ## License
198
210
 
@@ -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,CA6DT"}
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"}
@@ -5,14 +5,14 @@ export function generateQueriesTemplate(options) {
5
5
  var hasExample = options.hasExample, tableName = options.tableName;
6
6
  var metricKey = hasExample && tableName ? "".concat(camelCase(tableName), "Query") : 'exampleMetric';
7
7
  var typeAlias = "".concat(pascalCase(metricKey), "Result");
8
- var template = "import { initServe } from '@hypequery/serve';\nimport type { InferApiType } from '@hypequery/serve';\nimport { z } from 'zod';\nimport { db } from './client';\n\nconst serve = initServe({\n context: () => ({ db }),\n});\nconst { query } = serve;\n\nexport const api = serve.define({\n queries: serve.queries({";
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";
9
9
  if (hasExample && tableName) {
10
- template += "\n ".concat(camelCase(tableName), "Query: query\n .describe('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 ),");
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});");
11
11
  }
12
12
  else {
13
- template += "\n exampleMetric: query\n .describe('Example metric that returns a simple value')\n .output(z.object({ ok: z.boolean() }))\n .query(async () => ({ ok: true })),";
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});";
14
14
  }
15
- template += "\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\n */\n");
15
+ template += "\nexport const api = serve({\n queries: {\n ".concat(metricKey, ",\n },\n});\n\nexport type ApiDefinition = InferApiType<typeof api>;\n\n/**\n * Inline usage example:\n *\n * const result = await api.execute('").concat(metricKey, "');\n * console.log(result);\n *\n * // import type { InferQueryResult } from '@hypequery/serve';\n * type ").concat(typeAlias, " = InferQueryResult<typeof api, '").concat(metricKey, "'>;\n *\n * // Register HTTP route:\n * api.route('/metrics/").concat(metricKey, "', api.queries.").concat(metricKey, ");\n *\n * Dev server:\n * npx hypequery dev analytics/queries.ts\n */\n");
16
16
  return template;
17
17
  }
18
18
  /**
@@ -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;;;EAe9B"}
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"}
@@ -142,7 +142,6 @@ export function mockProcessExit() {
142
142
  var exitMock = vi.fn(function (code) {
143
143
  throw new ProcessExitError(code !== null && code !== void 0 ? code : 0);
144
144
  });
145
- // @ts-ignore
146
145
  process.exit = exitMock;
147
146
  return {
148
147
  exitMock: exitMock,
@@ -1 +1 @@
1
- {"version":3,"file":"load-api.d.ts","sourceRoot":"","sources":["../../src/utils/load-api.ts"],"names":[],"mappings":"AAaA,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,gBAuErD"}
1
+ {"version":3,"file":"load-api.d.ts","sourceRoot":"","sources":["../../src/utils/load-api.ts"],"names":[],"mappings":"AAaA,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,gBA0ErD"}
@@ -36,7 +36,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
36
36
  };
37
37
  var _a, _b, _c, _d;
38
38
  import { pathToFileURL } from 'node:url';
39
- import { access, mkdtemp, writeFile, rm, mkdir } from 'node:fs/promises';
39
+ import { access, mkdtemp, rm, mkdir, writeFile } from 'node:fs/promises';
40
40
  import os from 'node:os';
41
41
  import path from 'node:path';
42
42
  import { build } from 'esbuild';
@@ -47,21 +47,21 @@ var TYPESCRIPT_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts']);
47
47
  var tsconfigCache = new Map();
48
48
  export function loadApiModule(modulePath) {
49
49
  return __awaiter(this, void 0, void 0, function () {
50
- var resolved, _a, relativePath, extension, isTypeScript, moduleUrl, _b, mod, error_1, relativePath, api, relativePath, availableExports;
51
- var _c;
52
- return __generator(this, function (_d) {
53
- switch (_d.label) {
50
+ var resolved, _a, relativePath, extension, isTypeScript, moduleUrl, _b, mod, importOverride, _c, error_1, relativePath, api, relativePath, availableExports;
51
+ var _d;
52
+ return __generator(this, function (_e) {
53
+ switch (_e.label) {
54
54
  case 0:
55
55
  resolved = path.resolve(process.cwd(), modulePath);
56
- _d.label = 1;
56
+ _e.label = 1;
57
57
  case 1:
58
- _d.trys.push([1, 3, , 4]);
58
+ _e.trys.push([1, 3, , 4]);
59
59
  return [4 /*yield*/, access(resolved)];
60
60
  case 2:
61
- _d.sent();
61
+ _e.sent();
62
62
  return [3 /*break*/, 4];
63
63
  case 3:
64
- _a = _d.sent();
64
+ _a = _e.sent();
65
65
  relativePath = path.relative(process.cwd(), resolved);
66
66
  throw new Error("File not found: ".concat(relativePath, "\n\n") +
67
67
  "Make sure the file exists and the path is correct.\n" +
@@ -73,22 +73,31 @@ export function loadApiModule(modulePath) {
73
73
  if (!isTypeScript) return [3 /*break*/, 6];
74
74
  return [4 /*yield*/, bundleTypeScriptModule(resolved)];
75
75
  case 5:
76
- _b = _d.sent();
76
+ _b = _e.sent();
77
77
  return [3 /*break*/, 7];
78
78
  case 6:
79
79
  _b = "".concat(pathToFileURL(resolved).href, "?t=").concat(Date.now());
80
- _d.label = 7;
80
+ _e.label = 7;
81
81
  case 7:
82
82
  moduleUrl = _b;
83
- _d.label = 8;
83
+ _e.label = 8;
84
84
  case 8:
85
- _d.trys.push([8, 10, , 11]);
86
- return [4 /*yield*/, import(moduleUrl)];
85
+ _e.trys.push([8, 13, , 14]);
86
+ importOverride = globalState.__hypequeryCliImportOverride;
87
+ if (!importOverride) return [3 /*break*/, 10];
88
+ return [4 /*yield*/, importOverride(moduleUrl)];
87
89
  case 9:
88
- mod = _d.sent();
89
- return [3 /*break*/, 11];
90
- case 10:
91
- error_1 = _d.sent();
90
+ _c = _e.sent();
91
+ return [3 /*break*/, 12];
92
+ case 10: return [4 /*yield*/, import(/* @vite-ignore */ moduleUrl)];
93
+ case 11:
94
+ _c = _e.sent();
95
+ _e.label = 12;
96
+ case 12:
97
+ mod = _c;
98
+ return [3 /*break*/, 14];
99
+ case 13:
100
+ error_1 = _e.sent();
92
101
  relativePath = path.relative(process.cwd(), resolved);
93
102
  throw new Error("Failed to load module: ".concat(relativePath, "\n\n") +
94
103
  "Error: ".concat(error_1.message, "\n\n") +
@@ -98,8 +107,8 @@ export function loadApiModule(modulePath) {
98
107
  " \u2022 An import path is incorrect\n"
99
108
  : "") +
100
109
  (error_1.stack ? "\nStack trace:\n".concat(error_1.stack, "\n") : ''));
101
- case 11:
102
- api = (_c = mod.api) !== null && _c !== void 0 ? _c : mod.default;
110
+ case 14:
111
+ api = (_d = mod.api) !== null && _d !== void 0 ? _d : mod.default;
103
112
  if (!api || typeof api.handler !== 'function') {
104
113
  relativePath = path.relative(process.cwd(), resolved);
105
114
  availableExports = Object.keys(mod).filter(function (key) { return key !== '__esModule'; });
@@ -285,7 +294,7 @@ function installCleanupHooks() {
285
294
  }
286
295
  function bundleTypeScriptModule(entryPath) {
287
296
  return __awaiter(this, void 0, void 0, function () {
288
- var relativePath, tsconfigPath, result, output, contents, tempDir, tempFile, error_2;
297
+ var relativePath, tsconfigPath, result, output, tempDir, timestamp, tempFile, contents, error_2;
289
298
  var _a, _b, _c, _d;
290
299
  return __generator(this, function (_e) {
291
300
  switch (_e.label) {
@@ -322,16 +331,19 @@ function bundleTypeScriptModule(entryPath) {
322
331
  if (!output) {
323
332
  throw new Error('esbuild produced no output');
324
333
  }
325
- contents = "".concat(output.text, "\n//# sourceURL=").concat(pathToFileURL(entryPath).href);
326
334
  return [4 /*yield*/, ensureTempDir()];
327
335
  case 4:
328
336
  tempDir = _e.sent();
329
- tempFile = path.join(tempDir, "".concat(path.basename(entryPath).replace(/[^a-zA-Z0-9_-]/g, '_'), "-").concat(Date.now(), "-").concat(Math.random().toString(36).slice(2), ".mjs"));
337
+ timestamp = Date.now();
338
+ tempFile = path.join(tempDir, "".concat(path.basename(entryPath, path.extname(entryPath)), "-").concat(timestamp, ".mjs"));
339
+ contents = "".concat(output.text, "\n") +
340
+ "//# sourceURL=".concat(pathToFileURL(entryPath).href, "\n") +
341
+ "//# hypequery-ts-bundle=".concat(timestamp);
330
342
  return [4 /*yield*/, writeFile(tempFile, contents, 'utf8')];
331
343
  case 5:
332
344
  _e.sent();
333
345
  tempFiles.add(tempFile);
334
- return [2 /*return*/, "".concat(pathToFileURL(tempFile).href, "?t=").concat(Date.now())];
346
+ return [2 /*return*/, "".concat(pathToFileURL(tempFile).href, "?t=").concat(timestamp)];
335
347
  case 6:
336
348
  error_2 = _e.sent();
337
349
  throw new Error("Failed to compile ".concat(relativePath, " with esbuild.\n") +
@@ -386,14 +398,14 @@ function findNearestTsconfig(filePath) {
386
398
  };
387
399
  _b.label = 1;
388
400
  case 1:
389
- if (!true) return [3 /*break*/, 3];
401
+ if (!dir) return [3 /*break*/, 3];
390
402
  return [5 /*yield**/, _loop_1()];
391
403
  case 2:
392
404
  state_1 = _b.sent();
393
405
  if (typeof state_1 === "object")
394
406
  return [2 /*return*/, state_1.value];
395
407
  return [3 /*break*/, 1];
396
- case 3: return [2 /*return*/];
408
+ case 3: return [2 /*return*/, null];
397
409
  }
398
410
  });
399
411
  });
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAMzD;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAavE;AAED;;GAEG;AACH,wBAAsB,0BAA0B,IAAI,OAAO,CAAC;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,CAAC,CAkCR;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CA6B7D;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAS9D;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0BnF;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CASxE;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASnE;AAED;;GAEG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC,CAShE"}
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAQzD;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAavE;AAED;;GAEG;AACH,wBAAsB,0BAA0B,IAAI,OAAO,CAAC;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,CAAC,CAkCR;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CA6B7D;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAS9D;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0BnF;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CASxE;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASnE;AAED;;GAEG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC,CAShE"}
@@ -45,8 +45,9 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
45
45
  };
46
46
  import prompts from 'prompts';
47
47
  import { logger } from './logger.js';
48
+ var noop = function () { return undefined; };
48
49
  // Configure prompts to not exit on cancel
49
- prompts.override({ onCancel: function () { } });
50
+ prompts.override({ onCancel: noop });
50
51
  /**
51
52
  * Prompt for database type selection
52
53
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypequery/cli",
3
- "version": "0.0.9",
3
+ "version": "1.1.0",
4
4
  "description": "Command-line interface for hypequery",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -15,7 +15,7 @@
15
15
  "chalk": "^5.3.0",
16
16
  "commander": "^12.0.0",
17
17
  "dotenv": "^16.4.7",
18
- "esbuild": "^0.24.0",
18
+ "esbuild": "^0.25.0",
19
19
  "open": "^10.0.0",
20
20
  "ora": "^8.0.1",
21
21
  "prompts": "^2.4.2"
@@ -35,8 +35,16 @@
35
35
  "@vitest/coverage-v8": "^2.1.6",
36
36
  "typescript": "^5.7.3",
37
37
  "vitest": "^2.1.6",
38
- "@hypequery/clickhouse": "1.5.0",
39
- "@hypequery/serve": "0.0.9"
38
+ "@hypequery/clickhouse": "1.6.0",
39
+ "@hypequery/serve": "0.2.0"
40
+ },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/hypequery/hypequery.git"
44
+ },
45
+ "homepage": "https://www.hypequery.com",
46
+ "bugs": {
47
+ "url": "https://github.com/hypequery/hypequery/issues"
40
48
  },
41
49
  "publishConfig": {
42
50
  "access": "public"