@hypequery/cli 1.4.0 → 1.5.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 +34 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +20 -0
- package/dist/commands/deployment.d.ts +11 -0
- package/dist/commands/deployment.d.ts.map +1 -0
- package/dist/commands/deployment.js +84 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ Use it to:
|
|
|
7
7
|
- generate schema types from ClickHouse or embedded chDB
|
|
8
8
|
- scaffold `analytics/` files
|
|
9
9
|
- run the local dev server with docs
|
|
10
|
+
- build and validate portable deployment contracts
|
|
10
11
|
|
|
11
12
|
## Quick Start
|
|
12
13
|
|
|
@@ -135,6 +136,39 @@ npx hypequery generate:manifest analytics/api.ts --output analytics/hypequery-ma
|
|
|
135
136
|
The output is the exact serializable JSON returned by `api.manifest()`, including
|
|
136
137
|
semantic keys such as `dataset:orders`.
|
|
137
138
|
|
|
139
|
+
### `hypequery deployment:build`
|
|
140
|
+
|
|
141
|
+
Builds deployment metadata for an exported HypeQuery API and writes the
|
|
142
|
+
artifact with its identity sidecar.
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npx hypequery deployment:build analytics/api.ts \
|
|
146
|
+
--runtime-artifact 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The runtime artifact digest is required when named Serve handlers cannot be
|
|
150
|
+
lowered to a portable implementation. Dataset-only APIs can omit it.
|
|
151
|
+
|
|
152
|
+
Options:
|
|
153
|
+
|
|
154
|
+
- `--output <path>`: default `analytics/hypequery-deployment.json`
|
|
155
|
+
- `--runtime <runtime>`: `node` (default) or `python`
|
|
156
|
+
- `--runtime-artifact <sha256>`: lowercase SHA-256 of the built runtime artifact
|
|
157
|
+
- `--entrypoint-prefix <prefix>`: default `queries`
|
|
158
|
+
- `--hash-output <path>`: default `<output>.sha256`
|
|
159
|
+
|
|
160
|
+
The identity sidecar is not compatible with `sha256sum -c`. Use
|
|
161
|
+
`deployment:validate` to validate the artifact and report its identity.
|
|
162
|
+
|
|
163
|
+
### `hypequery deployment:validate`
|
|
164
|
+
|
|
165
|
+
Validates an existing deployment artifact and reports its datasets, queries,
|
|
166
|
+
runtime artifacts, and identity.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
npx hypequery deployment:validate analytics/hypequery-deployment.json
|
|
170
|
+
```
|
|
171
|
+
|
|
138
172
|
## Non-interactive Setup
|
|
139
173
|
|
|
140
174
|
For ClickHouse, `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;AAYpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AAa9B,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;EAKpE;AAwJD,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import { devCommand } from './commands/dev.js';
|
|
|
6
6
|
import { generateCommand } from './commands/generate.js';
|
|
7
7
|
import { generateDatasetsCommand } from './commands/generate-datasets.js';
|
|
8
8
|
import { generateManifestCommand } from './commands/generate-manifest.js';
|
|
9
|
+
import { buildDeploymentCommand, validateDeploymentCommand, } from './commands/deployment.js';
|
|
9
10
|
const program = new Command();
|
|
10
11
|
function getCliVersion() {
|
|
11
12
|
try {
|
|
@@ -108,6 +109,23 @@ program
|
|
|
108
109
|
.action(runCommand(async (api, options) => {
|
|
109
110
|
await generateManifestCommand(api, options);
|
|
110
111
|
}));
|
|
112
|
+
program
|
|
113
|
+
.command('deployment:build <api>')
|
|
114
|
+
.description('Build a validated, canonical deployment contract and identity')
|
|
115
|
+
.option('-o, --output <path>', 'Output JSON file (default: analytics/hypequery-deployment.json)')
|
|
116
|
+
.option('--runtime <runtime>', 'Runtime for non-portable handlers: node or python (default: node)')
|
|
117
|
+
.option('--runtime-artifact <sha256>', 'SHA-256 identity of the built runtime artifact')
|
|
118
|
+
.option('--entrypoint-prefix <prefix>', 'Runtime entrypoint prefix (default: queries)')
|
|
119
|
+
.option('--hash-output <path>', 'Deployment identity sidecar path (default: <output>.sha256)')
|
|
120
|
+
.action(runCommand(async (api, options) => {
|
|
121
|
+
await buildDeploymentCommand(api, options);
|
|
122
|
+
}));
|
|
123
|
+
program
|
|
124
|
+
.command('deployment:validate <artifact>')
|
|
125
|
+
.description('Validate a deployment contract and report its identity')
|
|
126
|
+
.action(runCommand(async (artifact) => {
|
|
127
|
+
await validateDeploymentCommand(artifact);
|
|
128
|
+
}));
|
|
111
129
|
// Help command
|
|
112
130
|
program
|
|
113
131
|
.command('help [command]')
|
|
@@ -138,6 +156,8 @@ program.on('--help', () => {
|
|
|
138
156
|
console.log(' hypequery generate:types --output analytics/schema.ts');
|
|
139
157
|
console.log(' hypequery generate:datasets');
|
|
140
158
|
console.log(' hypequery generate:manifest analytics/api.ts --output analytics/hypequery-manifest.json');
|
|
159
|
+
console.log(' hypequery deployment:build analytics/api.ts --runtime-artifact <sha256>');
|
|
160
|
+
console.log(' hypequery deployment:validate analytics/hypequery-deployment.json');
|
|
141
161
|
console.log('');
|
|
142
162
|
console.log('Docs: https://hypequery.com/docs');
|
|
143
163
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ProtocolDeploymentContract } from '@hypequery/protocol';
|
|
2
|
+
export interface BuildDeploymentOptions {
|
|
3
|
+
output?: string;
|
|
4
|
+
runtime?: 'node' | 'python';
|
|
5
|
+
runtimeArtifact?: string;
|
|
6
|
+
entrypointPrefix?: string;
|
|
7
|
+
hashOutput?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function buildDeploymentCommand(apiPath: string | undefined, options?: BuildDeploymentOptions): Promise<ProtocolDeploymentContract>;
|
|
10
|
+
export declare function validateDeploymentCommand(artifactPath: string | undefined): Promise<ProtocolDeploymentContract>;
|
|
11
|
+
//# sourceMappingURL=deployment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/commands/deployment.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,qBAAqB,CAAC;AAM7B,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA8BD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,0BAA0B,CAAC,CAuCrC;AAED,wBAAsB,yBAAyB,CAC7C,YAAY,EAAE,MAAM,GAAG,SAAS,GAC/B,OAAO,CAAC,0BAA0B,CAAC,CAoCrC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { prepareProtocolDeploymentContract, } from '@hypequery/protocol';
|
|
4
|
+
import { loadApiModule } from '../utils/load-api.js';
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
6
|
+
const SHA256_PATTERN = /^[0-9a-f]{64}$/;
|
|
7
|
+
function runtimeArtifact(options) {
|
|
8
|
+
if (options.runtimeArtifact === undefined)
|
|
9
|
+
return undefined;
|
|
10
|
+
if (!SHA256_PATTERN.test(options.runtimeArtifact)) {
|
|
11
|
+
throw new Error('--runtime-artifact must be a lowercase 64-character SHA-256 digest.');
|
|
12
|
+
}
|
|
13
|
+
const runtime = options.runtime ?? 'node';
|
|
14
|
+
if (runtime !== 'node' && runtime !== 'python') {
|
|
15
|
+
throw new Error('--runtime must be either node or python.');
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
runtime,
|
|
19
|
+
artifactSha256: options.runtimeArtifact,
|
|
20
|
+
...(options.entrypointPrefix !== undefined
|
|
21
|
+
? { entrypointPrefix: options.entrypointPrefix }
|
|
22
|
+
: {}),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export async function buildDeploymentCommand(apiPath, options = {}) {
|
|
26
|
+
if (!apiPath) {
|
|
27
|
+
throw new Error('Missing API module path.\n\n'
|
|
28
|
+
+ 'Usage: hypequery deployment:build analytics/api.ts');
|
|
29
|
+
}
|
|
30
|
+
const artifact = runtimeArtifact(options);
|
|
31
|
+
const api = await loadApiModule(apiPath);
|
|
32
|
+
if (typeof api.deploymentContract !== 'function') {
|
|
33
|
+
throw new Error(`Invalid API module: ${apiPath}\n\n`
|
|
34
|
+
+ 'The exported API must provide deploymentContract(). '
|
|
35
|
+
+ 'Upgrade @hypequery/serve and export the value returned by createAPI() or serve().');
|
|
36
|
+
}
|
|
37
|
+
const contract = api.deploymentContract(artifact ? { runtimeArtifact: artifact } : {});
|
|
38
|
+
const prepared = prepareProtocolDeploymentContract(contract);
|
|
39
|
+
const { canonical, contract: validated, identity: digest } = prepared;
|
|
40
|
+
const outputPath = options.output ?? 'analytics/hypequery-deployment.json';
|
|
41
|
+
const hashOutputPath = options.hashOutput ?? `${outputPath}.sha256`;
|
|
42
|
+
const identitySidecar = [
|
|
43
|
+
'# Hypequery deployment identity v1; not a file checksum or sha256sum input.',
|
|
44
|
+
'# SHA-256(UTF-8("hypequery:deployment:v1") || 0x00 || RFC 8785 canonical bytes); '
|
|
45
|
+
+ 'the output newline is excluded.',
|
|
46
|
+
`${digest} ${path.basename(outputPath)}`,
|
|
47
|
+
'',
|
|
48
|
+
].join('\n');
|
|
49
|
+
await mkdir(path.dirname(outputPath), { recursive: true });
|
|
50
|
+
await mkdir(path.dirname(hashOutputPath), { recursive: true });
|
|
51
|
+
await writeFile(outputPath, `${canonical}\n`, 'utf8');
|
|
52
|
+
await writeFile(hashOutputPath, identitySidecar, 'utf8');
|
|
53
|
+
logger.success(`Deployment contract written to ${outputPath}`);
|
|
54
|
+
logger.info(`Identity: ${digest}`);
|
|
55
|
+
return validated;
|
|
56
|
+
}
|
|
57
|
+
export async function validateDeploymentCommand(artifactPath) {
|
|
58
|
+
if (!artifactPath) {
|
|
59
|
+
throw new Error('Missing deployment artifact path.\n\n'
|
|
60
|
+
+ 'Usage: hypequery deployment:validate analytics/hypequery-deployment.json');
|
|
61
|
+
}
|
|
62
|
+
let input;
|
|
63
|
+
try {
|
|
64
|
+
input = JSON.parse(await readFile(artifactPath, 'utf8'));
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw new Error(`Invalid deployment JSON: ${artifactPath}\n\n`
|
|
68
|
+
+ (error instanceof Error ? error.message : String(error)));
|
|
69
|
+
}
|
|
70
|
+
let prepared;
|
|
71
|
+
try {
|
|
72
|
+
prepared = prepareProtocolDeploymentContract(input);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
throw new Error(`Invalid deployment contract: ${artifactPath}\n\n`
|
|
76
|
+
+ (error instanceof Error ? error.message : String(error)));
|
|
77
|
+
}
|
|
78
|
+
const { contract, identity: digest } = prepared;
|
|
79
|
+
logger.success(`Valid deployment contract: ${artifactPath}`);
|
|
80
|
+
logger.info(`${contract.datasets.length} datasets, ${contract.queries.length} queries, `
|
|
81
|
+
+ `${contract.artifacts.length} runtime artifacts`);
|
|
82
|
+
logger.info(`Identity: ${digest}`);
|
|
83
|
+
return contract;
|
|
84
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypequery/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Command-line interface for hypequery",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"esbuild": "^0.25.0",
|
|
19
19
|
"open": "^10.0.0",
|
|
20
20
|
"ora": "^8.0.1",
|
|
21
|
-
"prompts": "^2.4.2"
|
|
21
|
+
"prompts": "^2.4.2",
|
|
22
|
+
"@hypequery/protocol": "0.3.0"
|
|
22
23
|
},
|
|
23
24
|
"peerDependencies": {
|
|
24
25
|
"@hypequery/clickhouse": "*",
|
|
@@ -36,7 +37,7 @@
|
|
|
36
37
|
"typescript": "^5.7.3",
|
|
37
38
|
"vitest": "^3.2.6",
|
|
38
39
|
"@hypequery/clickhouse": "2.4.0",
|
|
39
|
-
"@hypequery/serve": "0.
|
|
40
|
+
"@hypequery/serve": "0.12.0"
|
|
40
41
|
},
|
|
41
42
|
"repository": {
|
|
42
43
|
"type": "git",
|