@hypequery/cli 1.13.1 → 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 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;AA0BpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AAa9B,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;EAKpE;AA4ND,OAAO,EAAE,OAAO,EAAE,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
@@ -142,6 +142,7 @@ program
142
142
  .option('--runtime-output <path>', 'Bundled Node runtime path (default: beside deployment JSON)')
143
143
  .option('--entrypoint-prefix <prefix>', 'Runtime entrypoint prefix (default: queries)')
144
144
  .option('--hash-output <path>', 'Deployment identity sidecar path (default: <output>.sha256)')
145
+ .option('--no-source', 'Exclude project source files from the deployment bundle')
145
146
  .action(runCommand(async (api, options) => {
146
147
  await buildDeploymentCommand(api, options);
147
148
  }));
@@ -176,6 +177,7 @@ program
176
177
  .option('--project <project>', 'Target project identifier (advanced override)')
177
178
  .option('--environment <environment>', 'Target environment identifier (advanced override)')
178
179
  .option('--release <path>', 'Submit a prebuilt bundle with this release (legacy)')
180
+ .option('--no-source', 'Exclude project source files from the deployment bundle')
179
181
  .option('--endpoint <url>', 'HTTPS submission endpoint; requires HYPEQUERY_API_TOKEN')
180
182
  .action(runCommand(async (source, options) => {
181
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;CACxB;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"}
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"}
@@ -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);
@@ -9,6 +9,7 @@ export interface BuildDeploymentOptions {
9
9
  runtimeOutput?: string;
10
10
  entrypointPrefix?: string;
11
11
  hashOutput?: string;
12
+ source?: boolean;
12
13
  }
13
14
  export interface PrepareDeploymentReleaseOptions {
14
15
  project?: string;
@@ -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;AAc7B,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;CACrB;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,CAwIrC;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"}
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 bundle = await writeDeploymentBundle(bundleOutput, prepared, runtimeFiles);
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;
@@ -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;AA+FD,wBAAsB,qBAAqB,CACzC,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,kCAAkC,EAC5C,YAAY,EAAE,SAAS,2BAA2B,EAAE,GACnD,OAAO,CAAC,uBAAuB,CAAC,CA6ElC;AAoCD,wBAAsB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAMrF"}
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
- export async function writeDeploymentBundle(outputDirectory, prepared, runtimeFiles) {
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,3 @@
1
+ import type { DeploymentBundleSourceSnapshot } from './deployment-bundle.js';
2
+ export declare function captureDeploymentSourceSnapshot(apiPath: string): Promise<DeploymentBundleSourceSnapshot>;
3
+ //# sourceMappingURL=deployment-source-snapshot.d.ts.map
@@ -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+VD,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,oCAAoC,GAC5C,yBAAyB,CAsF3B"}
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypequery/cli",
3
- "version": "1.13.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/protocol": "0.9.0",
23
- "@hypequery/deployment": "0.6.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.0"
37
+ "@hypequery/serve": "0.14.1"
38
38
  },
39
39
  "repository": {
40
40
  "type": "git",