@hypequery/cli 1.13.0 → 1.14.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/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +9 -0
- package/dist/commands/deploy.d.ts +1 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +1 -1
- package/dist/commands/deployment.d.ts +1 -0
- package/dist/commands/deployment.d.ts.map +1 -1
- package/dist/commands/deployment.js +9 -1
- package/dist/utils/dependency-installer.d.ts +1 -0
- package/dist/utils/dependency-installer.d.ts.map +1 -1
- package/dist/utils/dependency-installer.js +29 -10
- package/dist/utils/deployment-bundle.d.ts +14 -1
- package/dist/utils/deployment-bundle.d.ts.map +1 -1
- package/dist/utils/deployment-bundle.js +58 -1
- package/dist/utils/deployment-source-snapshot.d.ts +3 -0
- package/dist/utils/deployment-source-snapshot.d.ts.map +1 -0
- package/dist/utils/deployment-source-snapshot.js +110 -0
- package/dist/utils/deployment-upload.d.ts.map +1 -1
- package/dist/utils/deployment-upload.js +16 -0
- package/dist/utils/prompts.d.ts +7 -0
- package/dist/utils/prompts.d.ts.map +1 -1
- package/dist/utils/prompts.js +50 -19
- package/package.json +4 -4
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;AA0BpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AAa9B,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;EAKpE;AA8ND,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ import { generateManifestCommand } from './commands/generate-manifest.js';
|
|
|
9
9
|
import { buildDeploymentCommand, prepareDeploymentReleaseCommand, validateDeploymentCommand, } from './commands/deployment.js';
|
|
10
10
|
import { deployCommand, submitDeploymentCommand, } from './commands/deploy.js';
|
|
11
11
|
import { loginCommand, logoutCommand, } from './commands/login.js';
|
|
12
|
+
import { isPromptCancelled } from './utils/prompts.js';
|
|
12
13
|
const program = new Command();
|
|
13
14
|
function getCliVersion() {
|
|
14
15
|
try {
|
|
@@ -48,6 +49,12 @@ function runCommand(action) {
|
|
|
48
49
|
await action(...args);
|
|
49
50
|
}
|
|
50
51
|
catch (error) {
|
|
52
|
+
// Ctrl+C at a prompt is a deliberate abort, not a failure: report it as
|
|
53
|
+
// the interrupt it is instead of dumping an error and exiting 1.
|
|
54
|
+
if (isPromptCancelled(error)) {
|
|
55
|
+
console.log('\nCancelled.');
|
|
56
|
+
process.exit(130);
|
|
57
|
+
}
|
|
51
58
|
console.error(error instanceof Error ? error.message : error);
|
|
52
59
|
process.exit(1);
|
|
53
60
|
}
|
|
@@ -135,6 +142,7 @@ program
|
|
|
135
142
|
.option('--runtime-output <path>', 'Bundled Node runtime path (default: beside deployment JSON)')
|
|
136
143
|
.option('--entrypoint-prefix <prefix>', 'Runtime entrypoint prefix (default: queries)')
|
|
137
144
|
.option('--hash-output <path>', 'Deployment identity sidecar path (default: <output>.sha256)')
|
|
145
|
+
.option('--no-source', 'Exclude project source files from the deployment bundle')
|
|
138
146
|
.action(runCommand(async (api, options) => {
|
|
139
147
|
await buildDeploymentCommand(api, options);
|
|
140
148
|
}));
|
|
@@ -169,6 +177,7 @@ program
|
|
|
169
177
|
.option('--project <project>', 'Target project identifier (advanced override)')
|
|
170
178
|
.option('--environment <environment>', 'Target environment identifier (advanced override)')
|
|
171
179
|
.option('--release <path>', 'Submit a prebuilt bundle with this release (legacy)')
|
|
180
|
+
.option('--no-source', 'Exclude project source files from the deployment bundle')
|
|
172
181
|
.option('--endpoint <url>', 'HTTPS submission endpoint; requires HYPEQUERY_API_TOKEN')
|
|
173
182
|
.action(runCommand(async (source, options) => {
|
|
174
183
|
await deployCommand(source, options);
|
|
@@ -10,6 +10,7 @@ export interface DeployOptions extends SubmitDeploymentOptions {
|
|
|
10
10
|
environment?: string;
|
|
11
11
|
bundleOutput?: string;
|
|
12
12
|
releaseOutput?: string;
|
|
13
|
+
source?: boolean;
|
|
13
14
|
}
|
|
14
15
|
export interface SubmitDeploymentDependencies {
|
|
15
16
|
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AASA,OAAO,EACL,mCAAmC,EAEnC,KAAK,4BAA4B,EAElC,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,sBAAsB,EACtB,+BAA+B,EAChC,MAAM,iBAAiB,CAAC;AAMzB,MAAM,WAAW,uBAAuB;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAc,SAAQ,uBAAuB;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AASA,OAAO,EACL,mCAAmC,EAEnC,KAAK,4BAA4B,EAElC,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,sBAAsB,EACtB,+BAA+B,EAChC,MAAM,iBAAiB,CAAC;AAMzB,MAAM,WAAW,uBAAuB;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAc,SAAQ,uBAAuB;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC5D,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,mCAAmC,CAAC;IACtE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;CACvE;AAED,MAAM,WAAW,kBAAmB,SAAQ,4BAA4B;IACtE,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,sBAAsB,CAAC;IACzD,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,+BAA+B,CAAC;IAC3E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,uBAAuB,CAAC;CAC5D;AA8GD,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,OAAO,GAAE,uBAA4B,EACrC,YAAY,GAAE,4BAAiC,GAC9C,OAAO,CAAC,4BAA4B,CAAC,CAwDvC;AAmDD,wBAAsB,aAAa,CACjC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,OAAO,GAAE,aAAkB,EAC3B,YAAY,GAAE,kBAAuB,GACpC,OAAO,CAAC,4BAA4B,CAAC,CAmDvC"}
|
package/dist/commands/deploy.js
CHANGED
|
@@ -204,7 +204,7 @@ export async function deployCommand(sourcePath, options = {}, dependencies = {})
|
|
|
204
204
|
// a missing or expired login only surfaces after a full bundle build.
|
|
205
205
|
await resolveDeploymentCredential(options.endpoint, dependencies);
|
|
206
206
|
try {
|
|
207
|
-
await build(sourcePath, { bundleOutput: bundlePath });
|
|
207
|
+
await build(sourcePath, { bundleOutput: bundlePath, source: options.source });
|
|
208
208
|
}
|
|
209
209
|
catch (error) {
|
|
210
210
|
throw withRuntimeFlagHint(error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/commands/deployment.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,0BAA0B,EAC/B,KAAK,iCAAiC,EACvC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/commands/deployment.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,0BAA0B,EAC/B,KAAK,iCAAiC,EACvC,MAAM,qBAAqB,CAAC;AAe7B,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,oCAAoC,CAAC;AAI5C,MAAM,WAAW,sBAAsB;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,+BAA+B;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oCAAoC;IACnD,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;IAC7D;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAoDD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,0BAA0B,CAAC,CAsJrC;AAED,wBAAsB,yBAAyB,CAC7C,YAAY,EAAE,MAAM,GAAG,SAAS,GAC/B,OAAO,CAAC,0BAA0B,CAAC,CA2ErC;AAiED,wBAAsB,+BAA+B,CACnD,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,OAAO,GAAE,+BAAoC,EAC7C,YAAY,GAAE,oCAAyC,GACtD,OAAO,CAAC,iCAAiC,CAAC,CAkE5C"}
|
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { prepareProtocolDeploymentReleaseEnvelope, prepareProtocolDeploymentContract, } from '@hypequery/protocol';
|
|
5
5
|
import { writeDeploymentBundle, verifyDeploymentBundle, readDeploymentRuntimeFile, } from '../utils/deployment-bundle.js';
|
|
6
6
|
import { buildNodeRuntimeArtifact, getDeploymentRuntimeEntrypoints, } from '../utils/deployment-runtime-artifact.js';
|
|
7
|
+
import { captureDeploymentSourceSnapshot } from '../utils/deployment-source-snapshot.js';
|
|
7
8
|
import { loadApiModule } from '../utils/load-api.js';
|
|
8
9
|
import { logger } from '../utils/logger.js';
|
|
9
10
|
import { loadCloudCredential, } from '../utils/cloud-credential-store.js';
|
|
@@ -123,8 +124,15 @@ export async function buildDeploymentCommand(apiPath, options = {}) {
|
|
|
123
124
|
}
|
|
124
125
|
runtimeFiles.push({ runtime, sha256: actualSha256, bytes });
|
|
125
126
|
}
|
|
126
|
-
const
|
|
127
|
+
const sourceSnapshot = options.source === false
|
|
128
|
+
? undefined
|
|
129
|
+
: await captureDeploymentSourceSnapshot(apiPath);
|
|
130
|
+
const bundle = await writeDeploymentBundle(bundleOutput, prepared, runtimeFiles, sourceSnapshot);
|
|
127
131
|
logger.success(`Deployment bundle written to ${bundle.directory}`);
|
|
132
|
+
if (bundle.manifest.source) {
|
|
133
|
+
logger.info(`Captured ${bundle.manifest.source.files.length} source `
|
|
134
|
+
+ `${bundle.manifest.source.files.length === 1 ? 'file' : 'files'}`);
|
|
135
|
+
}
|
|
128
136
|
logger.info(`Bundle identity: ${bundle.identity}`);
|
|
129
137
|
logger.info(`Deployment identity: ${digest}`);
|
|
130
138
|
return validated;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
type ScaffoldStyle = 'queries' | 'datasets';
|
|
2
2
|
type ScaffoldDatabase = 'clickhouse' | 'chdb';
|
|
3
|
+
export declare function isCanaryVersion(version: string | undefined): boolean;
|
|
3
4
|
export declare function resolveScaffoldPackages(cliVersion: string | undefined, style?: ScaffoldStyle, database?: ScaffoldDatabase): string[];
|
|
4
5
|
export declare function installScaffoldDependencies(style?: ScaffoldStyle, database?: ScaffoldDatabase): Promise<void>;
|
|
5
6
|
export declare const installServeDependencies: typeof installScaffoldDependencies;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dependency-installer.d.ts","sourceRoot":"","sources":["../../src/utils/dependency-installer.ts"],"names":[],"mappings":"AAWA,KAAK,aAAa,GAAG,SAAS,GAAG,UAAU,CAAC;AAC5C,KAAK,gBAAgB,GAAG,YAAY,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"dependency-installer.d.ts","sourceRoot":"","sources":["../../src/utils/dependency-installer.ts"],"names":[],"mappings":"AAWA,KAAK,aAAa,GAAG,SAAS,GAAG,UAAU,CAAC;AAC5C,KAAK,gBAAgB,GAAG,YAAY,GAAG,MAAM,CAAC;AAwE9C,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAEpE;AAqCD,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,KAAK,GAAE,aAAyB,EAChC,QAAQ,GAAE,gBAA+B,GACxC,MAAM,EAAE,CAmBV;AAsBD,wBAAsB,2BAA2B,CAC/C,KAAK,GAAE,aAAyB,EAChC,QAAQ,GAAE,gBAA+B,iBAyD1C;AAED,eAAO,MAAM,wBAAwB,oCAA8B,CAAC"}
|
|
@@ -68,26 +68,44 @@ function detectPackageManager(pkgJson) {
|
|
|
68
68
|
return 'bun';
|
|
69
69
|
return 'npm';
|
|
70
70
|
}
|
|
71
|
-
function
|
|
71
|
+
export function isCanaryVersion(version) {
|
|
72
|
+
return version?.includes('canary') === true;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Canary builds pin every hypequery package to a `0.0.0-canary-*` version, which
|
|
76
|
+
* sorts below every published semver range. Any third-party peer range on a
|
|
77
|
+
* hypequery package is therefore unsatisfiable — `chdb` declares
|
|
78
|
+
* `peerOptional @hypequery/clickhouse@">=2.1.2"` — and npm aborts the whole
|
|
79
|
+
* install with ERESOLVE, leaving the scaffold without its dependencies.
|
|
80
|
+
*
|
|
81
|
+
* Only npm needs this: pnpm (strict-peer-dependencies defaults to false), yarn
|
|
82
|
+
* and bun all install through peer conflicts rather than failing. Stable
|
|
83
|
+
* installs keep strict peer checking on every manager.
|
|
84
|
+
*/
|
|
85
|
+
function getPeerRelaxFlags(manager, canary) {
|
|
86
|
+
return canary && manager === 'npm' ? ['--legacy-peer-deps'] : [];
|
|
87
|
+
}
|
|
88
|
+
function getInstallArgs(manager, packages, canary = false) {
|
|
89
|
+
const relaxFlags = getPeerRelaxFlags(manager, canary);
|
|
72
90
|
switch (manager) {
|
|
73
91
|
case 'pnpm':
|
|
74
|
-
return ['add', ...packages];
|
|
92
|
+
return ['add', ...relaxFlags, ...packages];
|
|
75
93
|
case 'yarn':
|
|
76
|
-
return ['add', ...packages];
|
|
94
|
+
return ['add', ...relaxFlags, ...packages];
|
|
77
95
|
case 'bun':
|
|
78
|
-
return ['add', ...packages];
|
|
96
|
+
return ['add', ...relaxFlags, ...packages];
|
|
79
97
|
case 'npm':
|
|
80
98
|
default:
|
|
81
|
-
return ['install', ...packages];
|
|
99
|
+
return ['install', ...relaxFlags, ...packages];
|
|
82
100
|
}
|
|
83
101
|
}
|
|
84
|
-
function formatManualCommand(manager, packages) {
|
|
85
|
-
return
|
|
102
|
+
function formatManualCommand(manager, packages, canary = false) {
|
|
103
|
+
return [MANUAL_COMMANDS[manager], ...getPeerRelaxFlags(manager, canary), ...packages].join(' ');
|
|
86
104
|
}
|
|
87
105
|
export function resolveScaffoldPackages(cliVersion, style = 'queries', database = 'clickhouse') {
|
|
88
106
|
const includeDatasets = style === 'datasets';
|
|
89
107
|
const includeChdb = database === 'chdb';
|
|
90
|
-
if (cliVersion
|
|
108
|
+
if (isCanaryVersion(cliVersion)) {
|
|
91
109
|
return [
|
|
92
110
|
`@hypequery/clickhouse@${cliVersion}`,
|
|
93
111
|
`@hypequery/serve@${cliVersion}`,
|
|
@@ -141,9 +159,10 @@ export async function installScaffoldDependencies(style = 'queries', database =
|
|
|
141
159
|
if (missing.length === 0) {
|
|
142
160
|
return;
|
|
143
161
|
}
|
|
162
|
+
const canary = isCanaryVersion(cliPkgJson?.version);
|
|
144
163
|
const manager = detectPackageManager(pkgJson);
|
|
145
164
|
const command = manager === 'npm' ? 'npm' : manager;
|
|
146
|
-
const args = getInstallArgs(manager, missing);
|
|
165
|
+
const args = getInstallArgs(manager, missing, canary);
|
|
147
166
|
logger.info(`Installing ${missing.join(', ')} with ${manager}...`);
|
|
148
167
|
try {
|
|
149
168
|
await new Promise((resolve, reject) => {
|
|
@@ -165,7 +184,7 @@ export async function installScaffoldDependencies(style = 'queries', database =
|
|
|
165
184
|
}
|
|
166
185
|
catch (error) {
|
|
167
186
|
logger.warn('Failed to install hypequery packages automatically.');
|
|
168
|
-
logger.info(`Run manually: ${formatManualCommand(manager, missing)}`);
|
|
187
|
+
logger.info(`Run manually: ${formatManualCommand(manager, missing, canary)}`);
|
|
169
188
|
if (error instanceof Error && error.message) {
|
|
170
189
|
logger.info(error.message);
|
|
171
190
|
}
|
|
@@ -8,12 +8,25 @@ export interface DeploymentBundleRuntimeFile {
|
|
|
8
8
|
readonly sha256: string;
|
|
9
9
|
readonly bytes: Uint8Array;
|
|
10
10
|
}
|
|
11
|
+
export interface DeploymentBundleSourceFile {
|
|
12
|
+
readonly path: string;
|
|
13
|
+
readonly bytes: Uint8Array;
|
|
14
|
+
}
|
|
15
|
+
export interface DeploymentBundleSourceSnapshot {
|
|
16
|
+
readonly entrypoint: string;
|
|
17
|
+
readonly files: readonly DeploymentBundleSourceFile[];
|
|
18
|
+
readonly revision?: {
|
|
19
|
+
readonly kind: 'git';
|
|
20
|
+
readonly commit: string;
|
|
21
|
+
readonly dirty: boolean;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
11
24
|
export interface WrittenDeploymentBundle {
|
|
12
25
|
readonly directory: string;
|
|
13
26
|
readonly manifest: ProtocolDeploymentBundleManifest;
|
|
14
27
|
readonly identity: string;
|
|
15
28
|
readonly contract: ProtocolDeploymentContract;
|
|
16
29
|
}
|
|
17
|
-
export declare function writeDeploymentBundle(outputDirectory: string, prepared: PreparedProtocolDeploymentContract, runtimeFiles: readonly DeploymentBundleRuntimeFile[]): Promise<WrittenDeploymentBundle>;
|
|
30
|
+
export declare function writeDeploymentBundle(outputDirectory: string, prepared: PreparedProtocolDeploymentContract, runtimeFiles: readonly DeploymentBundleRuntimeFile[], sourceSnapshot?: DeploymentBundleSourceSnapshot): Promise<WrittenDeploymentBundle>;
|
|
18
31
|
export declare function readDeploymentRuntimeFile(filePath: string): Promise<Uint8Array>;
|
|
19
32
|
//# sourceMappingURL=deployment-bundle.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment-bundle.d.ts","sourceRoot":"","sources":["../../src/utils/deployment-bundle.ts"],"names":[],"mappings":"AAYA,OAAO,EAGL,KAAK,kCAAkC,EACvC,KAAK,gCAAgC,EACrC,KAAK,0BAA0B,EAChC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,sBAAsB,EACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAGtE,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,sBAAsB,GACvB,CAAC;AACF,YAAY,EAAE,wBAAwB,EAAE,CAAC;AAEzC,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,gCAAgC,CAAC;IACpD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,0BAA0B,CAAC;CAC/C;
|
|
1
|
+
{"version":3,"file":"deployment-bundle.d.ts","sourceRoot":"","sources":["../../src/utils/deployment-bundle.ts"],"names":[],"mappings":"AAYA,OAAO,EAGL,KAAK,kCAAkC,EACvC,KAAK,gCAAgC,EACrC,KAAK,0BAA0B,EAChC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,sBAAsB,EACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAGtE,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,sBAAsB,GACvB,CAAC;AACF,YAAY,EAAE,wBAAwB,EAAE,CAAC;AAEzC,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,0BAA0B,EAAE,CAAC;IACtD,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAClB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;QACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;KACzB,CAAC;CACH;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,gCAAgC,CAAC;IACpD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,0BAA0B,CAAC;CAC/C;AA0ID,wBAAsB,qBAAqB,CACzC,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,kCAAkC,EAC5C,YAAY,EAAE,SAAS,2BAA2B,EAAE,EACpD,cAAc,CAAC,EAAE,8BAA8B,GAC9C,OAAO,CAAC,uBAAuB,CAAC,CA+FlC;AAoCD,wBAAsB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAMrF"}
|
|
@@ -12,6 +12,7 @@ function sha256(bytes) {
|
|
|
12
12
|
function runtimeArtifactPath(runtime, digest) {
|
|
13
13
|
return `artifacts/${digest}.${runtime === 'node' ? 'mjs' : 'pyz'}`;
|
|
14
14
|
}
|
|
15
|
+
const SOURCE_ROOT = 'source';
|
|
15
16
|
function errorCode(error) {
|
|
16
17
|
return typeof error === 'object' && error !== null && 'code' in error
|
|
17
18
|
? String(error.code)
|
|
@@ -83,12 +84,51 @@ function requireClosedContractArtifactSet(contract) {
|
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
|
-
|
|
87
|
+
function validateSourceSnapshot(source) {
|
|
88
|
+
if (!source)
|
|
89
|
+
return undefined;
|
|
90
|
+
const files = [...source.files].sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
91
|
+
if (files.length < 1
|
|
92
|
+
|| files.length > DEFAULT_PROTOCOL_DEPLOYMENT_BUNDLE_LIMITS.maxSourceFiles) {
|
|
93
|
+
throw new Error('Deployment source snapshot exceeds the bundle file limits.');
|
|
94
|
+
}
|
|
95
|
+
let totalBytes = 0;
|
|
96
|
+
const seen = new Set();
|
|
97
|
+
for (const file of files) {
|
|
98
|
+
const portable = file.path.split(path.sep).join('/');
|
|
99
|
+
if (portable !== file.path || path.isAbsolute(file.path)
|
|
100
|
+
|| file.path.split('/').some(segment => !segment || segment === '.' || segment === '..')) {
|
|
101
|
+
throw new Error(`Deployment source path must be project-relative: ${file.path}`);
|
|
102
|
+
}
|
|
103
|
+
const caseFolded = file.path.toLowerCase();
|
|
104
|
+
if (seen.has(caseFolded)) {
|
|
105
|
+
throw new Error(`Duplicate deployment source path: ${file.path}`);
|
|
106
|
+
}
|
|
107
|
+
seen.add(caseFolded);
|
|
108
|
+
if (file.bytes.byteLength > DEFAULT_PROTOCOL_DEPLOYMENT_BUNDLE_LIMITS.maxSourceFileBytes) {
|
|
109
|
+
throw new Error(`Deployment source file exceeds its byte limit: ${file.path}`);
|
|
110
|
+
}
|
|
111
|
+
totalBytes += file.bytes.byteLength;
|
|
112
|
+
}
|
|
113
|
+
if (totalBytes > DEFAULT_PROTOCOL_DEPLOYMENT_BUNDLE_LIMITS.maxSourceBytes) {
|
|
114
|
+
throw new Error('Deployment source snapshot exceeds its total byte limit.');
|
|
115
|
+
}
|
|
116
|
+
if (!files.some(file => file.path === source.entrypoint)) {
|
|
117
|
+
throw new Error('Deployment source snapshot does not contain its API entrypoint.');
|
|
118
|
+
}
|
|
119
|
+
return Object.freeze({
|
|
120
|
+
entrypoint: source.entrypoint,
|
|
121
|
+
files: Object.freeze(files),
|
|
122
|
+
...(source.revision ? { revision: Object.freeze({ ...source.revision }) } : {}),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
export async function writeDeploymentBundle(outputDirectory, prepared, runtimeFiles, sourceSnapshot) {
|
|
87
126
|
const destination = path.resolve(outputDirectory);
|
|
88
127
|
if (destination === path.parse(destination).root) {
|
|
89
128
|
throw new Error('The deployment bundle output cannot be a filesystem root.');
|
|
90
129
|
}
|
|
91
130
|
const files = validateRuntimeFiles(prepared, runtimeFiles);
|
|
131
|
+
const source = validateSourceSnapshot(sourceSnapshot);
|
|
92
132
|
const deploymentBytes = utf8Encoder.encode(`${prepared.canonical}\n`);
|
|
93
133
|
const manifestInput = {
|
|
94
134
|
kind: 'hypequery-deployment-bundle',
|
|
@@ -105,6 +145,18 @@ export async function writeDeploymentBundle(outputDirectory, prepared, runtimeFi
|
|
|
105
145
|
sha256: file.sha256,
|
|
106
146
|
byteLength: file.bytes.byteLength,
|
|
107
147
|
})),
|
|
148
|
+
...(source ? {
|
|
149
|
+
source: {
|
|
150
|
+
root: SOURCE_ROOT,
|
|
151
|
+
entrypoint: source.entrypoint,
|
|
152
|
+
files: source.files.map(file => ({
|
|
153
|
+
path: file.path,
|
|
154
|
+
sha256: sha256(file.bytes),
|
|
155
|
+
byteLength: file.bytes.byteLength,
|
|
156
|
+
})),
|
|
157
|
+
...(source.revision ? { revision: source.revision } : {}),
|
|
158
|
+
},
|
|
159
|
+
} : {}),
|
|
108
160
|
};
|
|
109
161
|
const preparedManifest = prepareProtocolDeploymentBundleManifest(manifestInput);
|
|
110
162
|
const replaceExisting = await existingVerifiedBundle(destination);
|
|
@@ -118,6 +170,11 @@ export async function writeDeploymentBundle(outputDirectory, prepared, runtimeFi
|
|
|
118
170
|
for (const file of files) {
|
|
119
171
|
await writeFile(path.join(staging, runtimeArtifactPath(file.runtime, file.sha256)), file.bytes, { flag: 'wx' });
|
|
120
172
|
}
|
|
173
|
+
for (const file of source?.files ?? []) {
|
|
174
|
+
const outputPath = path.join(staging, SOURCE_ROOT, ...file.path.split('/'));
|
|
175
|
+
await mkdir(path.dirname(outputPath), { recursive: true });
|
|
176
|
+
await writeFile(outputPath, file.bytes, { flag: 'wx' });
|
|
177
|
+
}
|
|
121
178
|
await writeFile(path.join(staging, DEPLOYMENT_BUNDLE_MANIFEST), `${preparedManifest.canonical}\n`, { encoding: 'utf8', flag: 'wx' });
|
|
122
179
|
if (!replaceExisting) {
|
|
123
180
|
await rename(staging, destination);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployment-source-snapshot.d.ts","sourceRoot":"","sources":["../../src/utils/deployment-source-snapshot.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAEV,8BAA8B,EAC/B,MAAM,wBAAwB,CAAC;AAqEhC,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,8BAA8B,CAAC,CAkDzC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { constants, lstat, open, realpath } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { DEFAULT_PROTOCOL_DEPLOYMENT_BUNDLE_LIMITS, } from '@hypequery/protocol';
|
|
5
|
+
import { build } from 'esbuild';
|
|
6
|
+
import { findNearestTsconfig } from './load-api.js';
|
|
7
|
+
function portableProjectPath(projectRoot, absolutePath) {
|
|
8
|
+
const relative = path.relative(projectRoot, absolutePath);
|
|
9
|
+
if (!relative || relative === '..' || relative.startsWith(`..${path.sep}`)
|
|
10
|
+
|| path.isAbsolute(relative)) {
|
|
11
|
+
throw new Error('Deployment source files must remain inside the current project.');
|
|
12
|
+
}
|
|
13
|
+
return relative.split(path.sep).join('/');
|
|
14
|
+
}
|
|
15
|
+
async function readSourceFile(projectRoot, inputPath) {
|
|
16
|
+
const absolutePath = path.resolve(projectRoot, inputPath);
|
|
17
|
+
const displayPath = portableProjectPath(projectRoot, absolutePath);
|
|
18
|
+
const resolvedPath = await realpath(absolutePath);
|
|
19
|
+
if (resolvedPath !== absolutePath) {
|
|
20
|
+
throw new Error(`Deployment source files must not be symbolic links: ${displayPath}`);
|
|
21
|
+
}
|
|
22
|
+
const initial = await lstat(absolutePath);
|
|
23
|
+
if (!initial.isFile() || initial.isSymbolicLink()) {
|
|
24
|
+
throw new Error(`Deployment source entry is not a regular file: ${displayPath}`);
|
|
25
|
+
}
|
|
26
|
+
if (initial.size > DEFAULT_PROTOCOL_DEPLOYMENT_BUNDLE_LIMITS.maxSourceFileBytes) {
|
|
27
|
+
throw new Error(`Deployment source file exceeds its byte limit: ${displayPath}`);
|
|
28
|
+
}
|
|
29
|
+
const handle = await open(absolutePath, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
30
|
+
try {
|
|
31
|
+
const stat = await handle.stat();
|
|
32
|
+
if (!stat.isFile() || stat.size !== initial.size) {
|
|
33
|
+
throw new Error(`Deployment source file changed while being read: ${displayPath}`);
|
|
34
|
+
}
|
|
35
|
+
return Object.freeze({
|
|
36
|
+
path: displayPath,
|
|
37
|
+
bytes: new Uint8Array(await handle.readFile()),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
await handle.close();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function gitOutput(projectRoot, arguments_) {
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
execFile('git', [...arguments_], { cwd: projectRoot, encoding: 'utf8', maxBuffer: 1024 * 1024 }, (error, stdout) => resolve(error ? undefined : stdout.trim()));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async function sourceRevision(projectRoot) {
|
|
50
|
+
const commit = await gitOutput(projectRoot, ['rev-parse', 'HEAD']);
|
|
51
|
+
if (!commit || !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(commit))
|
|
52
|
+
return undefined;
|
|
53
|
+
const status = await gitOutput(projectRoot, ['status', '--porcelain', '--untracked-files=all']);
|
|
54
|
+
if (status === undefined)
|
|
55
|
+
return undefined;
|
|
56
|
+
return Object.freeze({
|
|
57
|
+
kind: 'git',
|
|
58
|
+
commit,
|
|
59
|
+
dirty: status.length > 0,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export async function captureDeploymentSourceSnapshot(apiPath) {
|
|
63
|
+
const projectRoot = await realpath(process.cwd());
|
|
64
|
+
const entrypointAbsolute = await realpath(path.resolve(projectRoot, apiPath));
|
|
65
|
+
const entrypoint = portableProjectPath(projectRoot, entrypointAbsolute);
|
|
66
|
+
let inputs;
|
|
67
|
+
try {
|
|
68
|
+
const result = await build({
|
|
69
|
+
absWorkingDir: projectRoot,
|
|
70
|
+
bundle: true,
|
|
71
|
+
entryPoints: [entrypointAbsolute],
|
|
72
|
+
logLevel: 'silent',
|
|
73
|
+
metafile: true,
|
|
74
|
+
packages: 'external',
|
|
75
|
+
platform: 'node',
|
|
76
|
+
sourcemap: false,
|
|
77
|
+
tsconfig: await findNearestTsconfig(entrypointAbsolute) ?? undefined,
|
|
78
|
+
write: false,
|
|
79
|
+
});
|
|
80
|
+
inputs = Object.keys(result.metafile.inputs);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
throw new Error(`Could not resolve the deployment source graph from ${apiPath}.\n`
|
|
84
|
+
+ `Original error: ${error instanceof Error ? error.message : String(error)}`);
|
|
85
|
+
}
|
|
86
|
+
const paths = [...new Set(inputs.map(input => {
|
|
87
|
+
const absolute = path.resolve(projectRoot, input);
|
|
88
|
+
const relative = portableProjectPath(projectRoot, absolute);
|
|
89
|
+
if (relative.split('/').includes('node_modules')) {
|
|
90
|
+
throw new Error(`Deployment source graph unexpectedly included a dependency: ${relative}`);
|
|
91
|
+
}
|
|
92
|
+
return relative;
|
|
93
|
+
}))].sort();
|
|
94
|
+
if (!paths.includes(entrypoint))
|
|
95
|
+
paths.unshift(entrypoint);
|
|
96
|
+
if (paths.length > DEFAULT_PROTOCOL_DEPLOYMENT_BUNDLE_LIMITS.maxSourceFiles) {
|
|
97
|
+
throw new Error('Deployment source graph exceeds the bundle file limit.');
|
|
98
|
+
}
|
|
99
|
+
const files = await Promise.all(paths.map(file => readSourceFile(projectRoot, file)));
|
|
100
|
+
const totalBytes = files.reduce((total, file) => total + file.bytes.byteLength, 0);
|
|
101
|
+
if (totalBytes > DEFAULT_PROTOCOL_DEPLOYMENT_BUNDLE_LIMITS.maxSourceBytes) {
|
|
102
|
+
throw new Error('Deployment source graph exceeds the bundle byte limit.');
|
|
103
|
+
}
|
|
104
|
+
const revision = await sourceRevision(projectRoot);
|
|
105
|
+
return Object.freeze({
|
|
106
|
+
entrypoint,
|
|
107
|
+
files: Object.freeze(files),
|
|
108
|
+
...(revision ? { revision } : {}),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment-upload.d.ts","sourceRoot":"","sources":["../../src/utils/deployment-upload.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAIL,KAAK,yCAAyC,EAC/C,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAE1E,YAAY,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAQ1E,MAAM,MAAM,yBAAyB,GACjC,yBAAyB,GACzB,6BAA6B,GAC7B,0BAA0B,GAC1B,mBAAmB,GACnB,oBAAoB,GACpB,4BAA4B,CAAC;AAEjC,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,EAAE,yBAAyB,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEb,IAAI,EAAE,yBAAyB,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAM9E;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;CAClD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B;AAED,MAAM,MAAM,eAAe,GAAG,CAC5B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,mBAAmB,KACtB,OAAO,CAAC,sBAAsB,CAAC,CAAC;AAErC,MAAM,WAAW,oCAAoC;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC;CAClC;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,CACJ,MAAM,EAAE,wBAAwB,EAChC,OAAO,EAAE,yCAAyC,GACjD,OAAO,CAAC,4BAA4B,CAAC,CAAC;CAC1C;AA+
|
|
1
|
+
{"version":3,"file":"deployment-upload.d.ts","sourceRoot":"","sources":["../../src/utils/deployment-upload.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAIL,KAAK,yCAAyC,EAC/C,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAE1E,YAAY,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAQ1E,MAAM,MAAM,yBAAyB,GACjC,yBAAyB,GACzB,6BAA6B,GAC7B,0BAA0B,GAC1B,mBAAmB,GACnB,oBAAoB,GACpB,4BAA4B,CAAC;AAEjC,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,EAAE,yBAAyB,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEb,IAAI,EAAE,yBAAyB,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAM9E;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;CAClD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B;AAED,MAAM,MAAM,eAAe,GAAG,CAC5B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,mBAAmB,KACtB,OAAO,CAAC,sBAAsB,CAAC,CAAC;AAErC,MAAM,WAAW,oCAAoC;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC;CAClC;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,CACJ,MAAM,EAAE,wBAAwB,EAChC,OAAO,EAAE,yCAAyC,GACjD,OAAO,CAAC,4BAA4B,CAAC,CAAC;CAC1C;AA+WD,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,oCAAoC,GAC5C,yBAAyB,CAsF3B"}
|
|
@@ -78,6 +78,9 @@ function validateMultipartBundlePaths(bundle) {
|
|
|
78
78
|
multipartBundlePath(bundle.manifest.deployment.path);
|
|
79
79
|
for (const artifact of bundle.manifest.artifacts)
|
|
80
80
|
multipartBundlePath(artifact.path);
|
|
81
|
+
for (const source of bundle.manifest.source?.files ?? []) {
|
|
82
|
+
multipartBundlePath(`${bundle.manifest.source.root}/${source.path}`);
|
|
83
|
+
}
|
|
81
84
|
}
|
|
82
85
|
function partHeader(boundary, part) {
|
|
83
86
|
const headers = [
|
|
@@ -130,6 +133,19 @@ function uploadParts(bundle, release, manifest) {
|
|
|
130
133
|
byteLength: artifact.byteLength,
|
|
131
134
|
sha256: artifact.sha256,
|
|
132
135
|
})),
|
|
136
|
+
...(manifest.manifest.source?.files.map(source => {
|
|
137
|
+
const bundlePath = `${manifest.manifest.source.root}/${source.path}`;
|
|
138
|
+
return {
|
|
139
|
+
kind: 'file',
|
|
140
|
+
name: 'bundle',
|
|
141
|
+
filename: path.basename(source.path),
|
|
142
|
+
contentType: 'application/octet-stream',
|
|
143
|
+
absolutePath: path.join(root, ...bundlePath.split('/')),
|
|
144
|
+
bundlePath,
|
|
145
|
+
byteLength: source.byteLength,
|
|
146
|
+
sha256: source.sha256,
|
|
147
|
+
};
|
|
148
|
+
}) ?? []),
|
|
133
149
|
]);
|
|
134
150
|
}
|
|
135
151
|
async function* streamVerifiedFile(part) {
|
package/dist/utils/prompts.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raised when the user aborts a prompt with Ctrl+C or Esc.
|
|
3
|
+
*/
|
|
4
|
+
export declare class PromptCancelledError extends Error {
|
|
5
|
+
constructor();
|
|
6
|
+
}
|
|
7
|
+
export declare function isPromptCancelled(error: unknown): error is PromptCancelledError;
|
|
1
8
|
/**
|
|
2
9
|
* Prompt for ClickHouse connection details
|
|
3
10
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;;CAK9C;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,oBAAoB,CAE/E;AA8BD;;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,CAyCR;AAED,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,MAAM,CAAC;AAEjD;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,YAAY,CAAC,CAahE;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA4BrE;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CA6B7D;AAED,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAE/C,wBAAsB,eAAe,IAAI,OAAO,CAAC,SAAS,CAAC,CAa1D;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,CAAC;AAE9C;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,YAAY,CAAC,CAahE;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASnF;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,2BAA2B,CAC/C,MAAM,EAAE,MAAM,EAAE,EAChB,aAAa,GAAE,MAAM,EAAO,GAC3B,OAAO,CAAC,MAAM,EAAE,CAAC,CA2BnB;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"}
|
package/dist/utils/prompts.js
CHANGED
|
@@ -1,13 +1,44 @@
|
|
|
1
1
|
import prompts from 'prompts';
|
|
2
2
|
import { logger } from './logger.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Raised when the user aborts a prompt with Ctrl+C or Esc.
|
|
5
|
+
*/
|
|
6
|
+
export class PromptCancelledError extends Error {
|
|
7
|
+
constructor() {
|
|
8
|
+
super('Prompt cancelled');
|
|
9
|
+
this.name = 'PromptCancelledError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function isPromptCancelled(error) {
|
|
13
|
+
return error instanceof PromptCancelledError;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Ask a question and turn an aborted prompt into a thrown PromptCancelledError.
|
|
17
|
+
*
|
|
18
|
+
* `prompts` only reports cancellation through the `onCancel` callback passed as
|
|
19
|
+
* its second argument — the resolved answers simply omit the cancelled key. So
|
|
20
|
+
* without this wrapper Ctrl+C is indistinguishable from an unanswered question,
|
|
21
|
+
* and each caller's `?? default` silently answers on the user's behalf.
|
|
22
|
+
*/
|
|
23
|
+
async function ask(questions) {
|
|
24
|
+
let cancelled = false;
|
|
25
|
+
const answers = await prompts(questions, {
|
|
26
|
+
onCancel: () => {
|
|
27
|
+
cancelled = true;
|
|
28
|
+
// Returning false stops any remaining questions in the same chain.
|
|
29
|
+
return false;
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
if (cancelled) {
|
|
33
|
+
throw new PromptCancelledError();
|
|
34
|
+
}
|
|
35
|
+
return answers;
|
|
36
|
+
}
|
|
6
37
|
/**
|
|
7
38
|
* Prompt for ClickHouse connection details
|
|
8
39
|
*/
|
|
9
40
|
export async function promptClickHouseConnection() {
|
|
10
|
-
const hostResponse = await
|
|
41
|
+
const hostResponse = await ask({
|
|
11
42
|
type: 'text',
|
|
12
43
|
name: 'host',
|
|
13
44
|
message: 'ClickHouse URL (or skip to configure later):',
|
|
@@ -18,7 +49,7 @@ export async function promptClickHouseConnection() {
|
|
|
18
49
|
if (!hostResponse.host) {
|
|
19
50
|
return null;
|
|
20
51
|
}
|
|
21
|
-
const credentials = await
|
|
52
|
+
const credentials = await ask([
|
|
22
53
|
{
|
|
23
54
|
type: 'text',
|
|
24
55
|
name: 'database',
|
|
@@ -49,7 +80,7 @@ export async function promptClickHouseConnection() {
|
|
|
49
80
|
* Choose between a remote ClickHouse server and embedded chDB.
|
|
50
81
|
*/
|
|
51
82
|
export async function promptInitDatabase() {
|
|
52
|
-
const response = await
|
|
83
|
+
const response = await ask({
|
|
53
84
|
type: 'select',
|
|
54
85
|
name: 'database',
|
|
55
86
|
message: 'Choose your database',
|
|
@@ -67,7 +98,7 @@ export async function promptInitDatabase() {
|
|
|
67
98
|
* or undefined for in-memory.
|
|
68
99
|
*/
|
|
69
100
|
export async function promptChdbStorage() {
|
|
70
|
-
const response = await
|
|
101
|
+
const response = await ask({
|
|
71
102
|
type: 'select',
|
|
72
103
|
name: 'storage',
|
|
73
104
|
message: 'Where should embedded chDB store data?',
|
|
@@ -82,7 +113,7 @@ export async function promptChdbStorage() {
|
|
|
82
113
|
return undefined;
|
|
83
114
|
}
|
|
84
115
|
if (response.storage === 'custom') {
|
|
85
|
-
const customResponse = await
|
|
116
|
+
const customResponse = await ask({
|
|
86
117
|
type: 'text',
|
|
87
118
|
name: 'path',
|
|
88
119
|
message: 'Enter chDB data directory:',
|
|
@@ -96,7 +127,7 @@ export async function promptChdbStorage() {
|
|
|
96
127
|
* Prompt for output directory
|
|
97
128
|
*/
|
|
98
129
|
export async function promptOutputDirectory() {
|
|
99
|
-
const response = await
|
|
130
|
+
const response = await ask({
|
|
100
131
|
type: 'select',
|
|
101
132
|
name: 'directory',
|
|
102
133
|
message: 'Where should we create your analytics files?',
|
|
@@ -111,7 +142,7 @@ export async function promptOutputDirectory() {
|
|
|
111
142
|
return 'analytics'; // Default fallback
|
|
112
143
|
}
|
|
113
144
|
if (response.directory === 'custom') {
|
|
114
|
-
const customResponse = await
|
|
145
|
+
const customResponse = await ask({
|
|
115
146
|
type: 'text',
|
|
116
147
|
name: 'path',
|
|
117
148
|
message: 'Enter custom path:',
|
|
@@ -122,7 +153,7 @@ export async function promptOutputDirectory() {
|
|
|
122
153
|
return response.directory;
|
|
123
154
|
}
|
|
124
155
|
export async function promptInitStyle() {
|
|
125
|
-
const response = await
|
|
156
|
+
const response = await ask({
|
|
126
157
|
type: 'select',
|
|
127
158
|
name: 'style',
|
|
128
159
|
message: 'Choose your dev API style',
|
|
@@ -138,7 +169,7 @@ export async function promptInitStyle() {
|
|
|
138
169
|
* Choose whether generated routes include request-context auth scaffolding.
|
|
139
170
|
*/
|
|
140
171
|
export async function promptInitAuthMode() {
|
|
141
|
-
const response = await
|
|
172
|
+
const response = await ask({
|
|
142
173
|
type: 'select',
|
|
143
174
|
name: 'auth',
|
|
144
175
|
message: 'Choose authentication scaffolding',
|
|
@@ -154,7 +185,7 @@ export async function promptInitAuthMode() {
|
|
|
154
185
|
* Confirm scaffolding when init is run outside a Node project.
|
|
155
186
|
*/
|
|
156
187
|
export async function confirmWithoutPackageJson(directory) {
|
|
157
|
-
const response = await
|
|
188
|
+
const response = await ask({
|
|
158
189
|
type: 'confirm',
|
|
159
190
|
name: 'continue',
|
|
160
191
|
message: `package.json was not found in ${directory}. Continue scaffolding here?`,
|
|
@@ -166,7 +197,7 @@ export async function confirmWithoutPackageJson(directory) {
|
|
|
166
197
|
* Prompt for example query generation
|
|
167
198
|
*/
|
|
168
199
|
export async function promptGenerateExample() {
|
|
169
|
-
const response = await
|
|
200
|
+
const response = await ask({
|
|
170
201
|
type: 'confirm',
|
|
171
202
|
name: 'generate',
|
|
172
203
|
message: 'Generate an example query?',
|
|
@@ -191,7 +222,7 @@ export async function promptTableSelection(tables) {
|
|
|
191
222
|
...tables.slice(0, 10).map(table => ({ title: table, value: table })),
|
|
192
223
|
{ title: 'Skip example', value: null },
|
|
193
224
|
];
|
|
194
|
-
const response = await
|
|
225
|
+
const response = await ask({
|
|
195
226
|
type: 'select',
|
|
196
227
|
name: 'table',
|
|
197
228
|
message: 'Which table should we use for the example?',
|
|
@@ -214,7 +245,7 @@ export async function promptDatasetTableSelection(tables, defaultTables = []) {
|
|
|
214
245
|
logger.newline();
|
|
215
246
|
}
|
|
216
247
|
const defaults = new Set(defaultTables);
|
|
217
|
-
const response = await
|
|
248
|
+
const response = await ask({
|
|
218
249
|
type: 'multiselect',
|
|
219
250
|
name: 'tables',
|
|
220
251
|
message: 'Which tables should we scaffold as datasets?',
|
|
@@ -232,7 +263,7 @@ export async function promptDatasetTableSelection(tables, defaultTables = []) {
|
|
|
232
263
|
* Confirm overwrite of existing files
|
|
233
264
|
*/
|
|
234
265
|
export async function confirmOverwrite(files) {
|
|
235
|
-
const response = await
|
|
266
|
+
const response = await ask({
|
|
236
267
|
type: 'confirm',
|
|
237
268
|
name: 'overwrite',
|
|
238
269
|
message: `The following files will be overwritten:\n${files.map(f => ` • ${f}`).join('\n')}\n\nContinue?`,
|
|
@@ -244,7 +275,7 @@ export async function confirmOverwrite(files) {
|
|
|
244
275
|
* Retry prompt for failed operations
|
|
245
276
|
*/
|
|
246
277
|
export async function promptRetry(message) {
|
|
247
|
-
const response = await
|
|
278
|
+
const response = await ask({
|
|
248
279
|
type: 'confirm',
|
|
249
280
|
name: 'retry',
|
|
250
281
|
message,
|
|
@@ -256,7 +287,7 @@ export async function promptRetry(message) {
|
|
|
256
287
|
* Ask if user wants to continue without DB connection
|
|
257
288
|
*/
|
|
258
289
|
export async function promptContinueWithoutDb() {
|
|
259
|
-
const response = await
|
|
290
|
+
const response = await ask({
|
|
260
291
|
type: 'confirm',
|
|
261
292
|
name: 'continue',
|
|
262
293
|
message: 'Continue setup without database connection?',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypequery/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "Command-line interface for hypequery",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"open": "^10.0.0",
|
|
20
20
|
"ora": "^8.0.1",
|
|
21
21
|
"prompts": "^2.4.2",
|
|
22
|
-
"@hypequery/deployment": "0.
|
|
23
|
-
"@hypequery/protocol": "0.
|
|
22
|
+
"@hypequery/deployment": "0.7.0",
|
|
23
|
+
"@hypequery/protocol": "0.10.0"
|
|
24
24
|
},
|
|
25
25
|
"optionalDependencies": {
|
|
26
26
|
"@napi-rs/keyring": "1.3.0"
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@vitest/coverage-v8": "^3.2.6",
|
|
35
35
|
"typescript": "^5.7.3",
|
|
36
36
|
"vitest": "^3.2.6",
|
|
37
|
-
"@hypequery/serve": "0.14.
|
|
37
|
+
"@hypequery/serve": "0.14.1"
|
|
38
38
|
},
|
|
39
39
|
"repository": {
|
|
40
40
|
"type": "git",
|