@hypequery/cli 1.2.0 → 1.3.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 +11 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +9 -0
- package/dist/commands/generate-manifest.d.ts +5 -0
- package/dist/commands/generate-manifest.d.ts.map +1 -0
- package/dist/commands/generate-manifest.js +21 -0
- package/dist/generators/dataset-generator.d.ts.map +1 -1
- package/dist/generators/dataset-generator.js +35 -2
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -108,6 +108,17 @@ Options:
|
|
|
108
108
|
- `--tables <names>`: comma-separated table list
|
|
109
109
|
- `--exclude-tables <names>`: comma-separated tables to exclude
|
|
110
110
|
|
|
111
|
+
### `hypequery generate:manifest`
|
|
112
|
+
|
|
113
|
+
Generates a static React hook route manifest from an exported HypeQuery API.
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npx hypequery generate:manifest analytics/api.ts --output analytics/hypequery-manifest.json
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The output is the exact serializable JSON returned by `api.manifest()`, including
|
|
120
|
+
semantic keys such as `dataset:orders`.
|
|
121
|
+
|
|
111
122
|
## Non-interactive Setup
|
|
112
123
|
|
|
113
124
|
`hypequery init --no-interactive` reads:
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AAa9B,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;EAKpE;AAgID,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -5,6 +5,7 @@ import { initCommand } from './commands/init.js';
|
|
|
5
5
|
import { devCommand } from './commands/dev.js';
|
|
6
6
|
import { generateCommand } from './commands/generate.js';
|
|
7
7
|
import { generateDatasetsCommand } from './commands/generate-datasets.js';
|
|
8
|
+
import { generateManifestCommand } from './commands/generate-manifest.js';
|
|
8
9
|
const program = new Command();
|
|
9
10
|
function getCliVersion() {
|
|
10
11
|
try {
|
|
@@ -97,6 +98,13 @@ program
|
|
|
97
98
|
.action(runCommand(async (options) => {
|
|
98
99
|
await generateDatasetsCommand(options);
|
|
99
100
|
}));
|
|
101
|
+
program
|
|
102
|
+
.command('generate:manifest <api>')
|
|
103
|
+
.description('Generate a static React hook manifest from a hypequery API module')
|
|
104
|
+
.option('-o, --output <path>', 'Output JSON file (default: analytics/hypequery-manifest.json)')
|
|
105
|
+
.action(runCommand(async (api, options) => {
|
|
106
|
+
await generateManifestCommand(api, options);
|
|
107
|
+
}));
|
|
100
108
|
// Help command
|
|
101
109
|
program
|
|
102
110
|
.command('help [command]')
|
|
@@ -126,6 +134,7 @@ program.on('--help', () => {
|
|
|
126
134
|
console.log(' hypequery generate --output analytics/schema.ts');
|
|
127
135
|
console.log(' hypequery generate:types --output analytics/schema.ts');
|
|
128
136
|
console.log(' hypequery generate:datasets');
|
|
137
|
+
console.log(' hypequery generate:manifest analytics/api.ts --output analytics/hypequery-manifest.json');
|
|
129
138
|
console.log('');
|
|
130
139
|
console.log('Docs: https://hypequery.com/docs');
|
|
131
140
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-manifest.d.ts","sourceRoot":"","sources":["../../src/commands/generate-manifest.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,GAAE,uBAA4B,iBAyBtC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { loadApiModule } from '../utils/load-api.js';
|
|
4
|
+
import { logger } from '../utils/logger.js';
|
|
5
|
+
export async function generateManifestCommand(apiPath, options = {}) {
|
|
6
|
+
if (!apiPath) {
|
|
7
|
+
throw new Error('Missing API module path.\n\n' +
|
|
8
|
+
'Usage: hypequery generate:manifest analytics/api.ts --output analytics/hypequery-manifest.json');
|
|
9
|
+
}
|
|
10
|
+
const outputPath = options.output ?? 'analytics/hypequery-manifest.json';
|
|
11
|
+
const api = await loadApiModule(apiPath);
|
|
12
|
+
if (!api || typeof api.manifest !== 'function') {
|
|
13
|
+
throw new Error(`Invalid API module: ${apiPath}\n\n` +
|
|
14
|
+
`The exported API must provide a manifest() method. ` +
|
|
15
|
+
`Export the value returned by createAPI() or initServe(...).serve(...).`);
|
|
16
|
+
}
|
|
17
|
+
const manifest = api.manifest();
|
|
18
|
+
await mkdir(path.dirname(outputPath), { recursive: true });
|
|
19
|
+
await writeFile(outputPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
|
|
20
|
+
logger.success(`Manifest written to ${outputPath}`);
|
|
21
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataset-generator.d.ts","sourceRoot":"","sources":["../../src/generators/dataset-generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;
|
|
1
|
+
{"version":3,"file":"dataset-generator.d.ts","sourceRoot":"","sources":["../../src/generators/dataset-generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAuPD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,iBAyDtE"}
|
|
@@ -68,6 +68,38 @@ function isNumericColumn(column) {
|
|
|
68
68
|
type.includes('double') ||
|
|
69
69
|
type.includes('decimal'));
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Check if a numeric column is more likely an identifier than an additive value.
|
|
73
|
+
*/
|
|
74
|
+
function isIdentifierColumn(column) {
|
|
75
|
+
const originalName = column.name;
|
|
76
|
+
const name = column.name.toLowerCase();
|
|
77
|
+
return (name === 'id' ||
|
|
78
|
+
name === 'uuid' ||
|
|
79
|
+
name.endsWith('_id') ||
|
|
80
|
+
originalName.endsWith('Id') ||
|
|
81
|
+
name.includes('uuid') ||
|
|
82
|
+
isTenantColumn(column));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Check if a numeric column is a coordinate that should not be summed or averaged.
|
|
86
|
+
*/
|
|
87
|
+
function isCoordinateColumn(column) {
|
|
88
|
+
const name = column.name.toLowerCase();
|
|
89
|
+
return (name === 'lat' ||
|
|
90
|
+
name === 'lng' ||
|
|
91
|
+
name === 'lon' ||
|
|
92
|
+
name === 'latitude' ||
|
|
93
|
+
name === 'longitude' ||
|
|
94
|
+
name.endsWith('_lat') ||
|
|
95
|
+
name.endsWith('_lng') ||
|
|
96
|
+
name.endsWith('_lon') ||
|
|
97
|
+
name.endsWith('_latitude') ||
|
|
98
|
+
name.endsWith('_longitude'));
|
|
99
|
+
}
|
|
100
|
+
function isMeasureCandidate(column) {
|
|
101
|
+
return isNumericColumn(column) && !isIdentifierColumn(column) && !isCoordinateColumn(column);
|
|
102
|
+
}
|
|
71
103
|
/**
|
|
72
104
|
* Convert table name to PascalCase for dataset variable name
|
|
73
105
|
*/
|
|
@@ -122,8 +154,9 @@ async function generateDatasetForTable(client, tableName) {
|
|
|
122
154
|
dimensionLines.push(` ${fieldName}: dimension.${dimensionType}({ column: '${column.name}', label: '${label}' }),`);
|
|
123
155
|
}
|
|
124
156
|
}
|
|
125
|
-
// Generate measures
|
|
126
|
-
|
|
157
|
+
// Generate measures for numeric value columns only. IDs and coordinates stay
|
|
158
|
+
// dimensions because summing or averaging them is usually noise.
|
|
159
|
+
const numericColumns = columns.filter(isMeasureCandidate);
|
|
127
160
|
const measureLines = [];
|
|
128
161
|
if (numericColumns.length > 0) {
|
|
129
162
|
measureLines.push(` // Count measures`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypequery/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Command-line interface for hypequery",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^20.11.30",
|
|
34
34
|
"@types/prompts": "^2.4.9",
|
|
35
|
-
"@vitest/coverage-v8": "^2.
|
|
35
|
+
"@vitest/coverage-v8": "^3.2.6",
|
|
36
36
|
"typescript": "^5.7.3",
|
|
37
|
-
"vitest": "^2.
|
|
38
|
-
"@hypequery/clickhouse": "2.1.
|
|
39
|
-
"@hypequery/serve": "0.
|
|
37
|
+
"vitest": "^3.2.6",
|
|
38
|
+
"@hypequery/clickhouse": "2.1.2",
|
|
39
|
+
"@hypequery/serve": "0.6.0"
|
|
40
40
|
},
|
|
41
41
|
"repository": {
|
|
42
42
|
"type": "git",
|