@hypequery/cli 1.26.0 → 1.28.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.
Files changed (56) hide show
  1. package/README.md +14 -1
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +9 -2
  4. package/dist/commands/deploy.d.ts +6 -0
  5. package/dist/commands/deploy.d.ts.map +1 -1
  6. package/dist/commands/deploy.js +48 -2
  7. package/dist/commands/deployment.d.ts.map +1 -1
  8. package/dist/commands/deployment.js +11 -0
  9. package/dist/commands/generate-client.d.ts +13 -1
  10. package/dist/commands/generate-client.d.ts.map +1 -1
  11. package/dist/commands/generate-client.js +41 -7
  12. package/dist/generators/client-contract/render.d.ts +7 -1
  13. package/dist/generators/client-contract/render.d.ts.map +1 -1
  14. package/dist/generators/client-contract/render.js +5 -3
  15. package/dist/generators/dataset-generator.d.ts.map +1 -1
  16. package/dist/generators/dataset-generator.js +77 -68
  17. package/dist/templates/cloud.d.ts.map +1 -1
  18. package/dist/templates/cloud.js +3 -0
  19. package/dist/utils/clickhouse-dimension-type.d.ts +27 -0
  20. package/dist/utils/clickhouse-dimension-type.d.ts.map +1 -0
  21. package/dist/utils/clickhouse-dimension-type.js +61 -0
  22. package/dist/utils/clickhouse-type-utils.d.ts.map +1 -1
  23. package/dist/utils/clickhouse-type-utils.js +9 -1
  24. package/dist/utils/client-contract-file.d.ts +5 -0
  25. package/dist/utils/client-contract-file.d.ts.map +1 -1
  26. package/dist/utils/client-contract-file.js +49 -5
  27. package/dist/utils/cloud-client-contract.d.ts +20 -0
  28. package/dist/utils/cloud-client-contract.d.ts.map +1 -0
  29. package/dist/utils/cloud-client-contract.js +52 -0
  30. package/dist/utils/deployment-upload.d.ts +6 -2
  31. package/dist/utils/deployment-upload.d.ts.map +1 -1
  32. package/dist/utils/deployment-upload.js +12 -2
  33. package/dist/utils/draft-deployment.d.ts +16 -0
  34. package/dist/utils/draft-deployment.d.ts.map +1 -0
  35. package/dist/utils/draft-deployment.js +35 -0
  36. package/dist/utils/field-namer.d.ts +10 -0
  37. package/dist/utils/field-namer.d.ts.map +1 -0
  38. package/dist/utils/field-namer.js +20 -0
  39. package/dist/utils/json-pointer.d.ts +3 -0
  40. package/dist/utils/json-pointer.d.ts.map +1 -0
  41. package/dist/utils/json-pointer.js +4 -0
  42. package/dist/utils/live-deployment.d.ts +1 -1
  43. package/dist/utils/live-deployment.d.ts.map +1 -1
  44. package/dist/utils/read-capped.d.ts +6 -0
  45. package/dist/utils/read-capped.d.ts.map +1 -0
  46. package/dist/utils/read-capped.js +36 -0
  47. package/dist/utils/shell-quote.d.ts +6 -0
  48. package/dist/utils/shell-quote.d.ts.map +1 -0
  49. package/dist/utils/shell-quote.js +7 -0
  50. package/dist/utils/telemetry/catalog.d.ts +1 -1
  51. package/dist/utils/telemetry/domains.d.ts +6 -6
  52. package/dist/utils/telemetry/domains.js +6 -6
  53. package/dist/utils/tenant-key-candidates.d.ts +19 -0
  54. package/dist/utils/tenant-key-candidates.d.ts.map +1 -0
  55. package/dist/utils/tenant-key-candidates.js +63 -0
  56. package/package.json +7 -7
@@ -0,0 +1,27 @@
1
+ /** A dataset dimension type the scaffolder can emit. */
2
+ export type ScaffoldDimensionType = 'string' | 'number' | 'boolean' | 'timestamp';
3
+ export interface NamedTupleElement {
4
+ name: string;
5
+ type: ScaffoldDimensionType;
6
+ }
7
+ /**
8
+ * The value type behind `Nullable`, `LowCardinality` and
9
+ * `SimpleAggregateFunction(fn, T)` wrappers; none changes what a value is.
10
+ */
11
+ export declare function unwrapClickHouseType(type: string): string;
12
+ /**
13
+ * Maps a ClickHouse column type to a dataset dimension type, or null when no
14
+ * scalar dimension type describes its values (Array, Tuple, Map, geo, JSON,
15
+ * Interval, ...). The outer type name is matched exactly: a substring match
16
+ * reads `Point` or `Array(Int32)` as numbers. Mirrors Cloud's `protocolTypeOf`
17
+ * so a table scaffolds with the same field types in both.
18
+ */
19
+ export declare function clickHouseDimensionType(type: string): ScaffoldDimensionType | null;
20
+ /**
21
+ * The scalar elements of a named `Tuple(name T, ...)` column, each of which can
22
+ * be its own dimension through `tupleElement`. Null for any other type,
23
+ * including unnamed tuples, whose positions carry no meaning to name a field
24
+ * after. Elements without a scalar type or a plain identifier name are left out.
25
+ */
26
+ export declare function namedTupleElements(type: string): NamedTupleElement[] | null;
27
+ //# sourceMappingURL=clickhouse-dimension-type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clickhouse-dimension-type.d.ts","sourceRoot":"","sources":["../../src/utils/clickhouse-dimension-type.ts"],"names":[],"mappings":"AAEA,wDAAwD;AACxD,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;AAElF,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,qBAAqB,CAAC;CAC7B;AAID;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASzD;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAOlF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,GAAG,IAAI,CAgB3E"}
@@ -0,0 +1,61 @@
1
+ import { matchTypeCall } from './clickhouse-type-utils.js';
2
+ const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
3
+ /**
4
+ * The value type behind `Nullable`, `LowCardinality` and
5
+ * `SimpleAggregateFunction(fn, T)` wrappers; none changes what a value is.
6
+ */
7
+ export function unwrapClickHouseType(type) {
8
+ let current = type.trim();
9
+ const simple = /^SimpleAggregateFunction\(\s*[A-Za-z_][A-Za-z0-9_]*\s*,\s*(.+)\)$/s.exec(current);
10
+ if (simple)
11
+ current = simple[1].trim();
12
+ for (;;) {
13
+ const wrapped = /^(?:Nullable|LowCardinality)\((.*)\)$/s.exec(current);
14
+ if (!wrapped)
15
+ return current;
16
+ current = wrapped[1].trim();
17
+ }
18
+ }
19
+ /**
20
+ * Maps a ClickHouse column type to a dataset dimension type, or null when no
21
+ * scalar dimension type describes its values (Array, Tuple, Map, geo, JSON,
22
+ * Interval, ...). The outer type name is matched exactly: a substring match
23
+ * reads `Point` or `Array(Int32)` as numbers. Mirrors Cloud's `protocolTypeOf`
24
+ * so a table scaffolds with the same field types in both.
25
+ */
26
+ export function clickHouseDimensionType(type) {
27
+ const value = unwrapClickHouseType(type);
28
+ if (/^(?:U?Int\d+|Float\d+|Decimal\d*)(?:\(.*\))?$/s.test(value))
29
+ return 'number';
30
+ if (/^(?:Date|Date32|DateTime|DateTime64)(?:\(.*\))?$/s.test(value))
31
+ return 'timestamp';
32
+ if (value === 'Bool')
33
+ return 'boolean';
34
+ if (/^(?:String|FixedString|UUID|Enum\d*|IPv4|IPv6)(?:\(.*\))?$/s.test(value))
35
+ return 'string';
36
+ return null;
37
+ }
38
+ /**
39
+ * The scalar elements of a named `Tuple(name T, ...)` column, each of which can
40
+ * be its own dimension through `tupleElement`. Null for any other type,
41
+ * including unnamed tuples, whose positions carry no meaning to name a field
42
+ * after. Elements without a scalar type or a plain identifier name are left out.
43
+ */
44
+ export function namedTupleElements(type) {
45
+ const parts = matchTypeCall(unwrapClickHouseType(type), 'Tuple');
46
+ if (!parts?.length)
47
+ return null;
48
+ const elements = [];
49
+ for (const part of parts) {
50
+ const named = /^(\S+)\s+(.+)$/s.exec(part);
51
+ // Element types never contain top-level whitespace before their first
52
+ // `(`, so a part without a name-then-space prefix is an unnamed element.
53
+ if (!named || named[1].includes('('))
54
+ return null;
55
+ const elementType = clickHouseDimensionType(named[2]);
56
+ if (IDENTIFIER.test(named[1]) && elementType) {
57
+ elements.push({ name: named[1], type: elementType });
58
+ }
59
+ }
60
+ return elements;
61
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"clickhouse-type-utils.d.ts","sourceRoot":"","sources":["../../src/utils/clickhouse-type-utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA+CzD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG3E;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAGzE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,UASpD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,WAE7C"}
1
+ {"version":3,"file":"clickhouse-type-utils.d.ts","sourceRoot":"","sources":["../../src/utils/clickhouse-type-utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAqDzD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG3E;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAGzE;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,UASpD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,WAE7C"}
@@ -3,10 +3,18 @@ export function splitTopLevelArgs(value) {
3
3
  let current = '';
4
4
  let depth = 0;
5
5
  let quote = null;
6
+ let escaped = false;
6
7
  for (const char of value) {
7
8
  if (quote) {
8
9
  current += char;
9
- if (char === quote) {
10
+ // ClickHouse escapes a quote inside a literal with a backslash (`'can\'t'`).
11
+ if (escaped) {
12
+ escaped = false;
13
+ }
14
+ else if (char === '\\') {
15
+ escaped = true;
16
+ }
17
+ else if (char === quote) {
10
18
  quote = null;
11
19
  }
12
20
  continue;
@@ -3,4 +3,9 @@ export declare function checkClientOutputPath(filePath: string): Promise<void>;
3
3
  /** Bound allocation before reading; O_NOFOLLOW also refuses a swapped final symlink. */
4
4
  export declare function readClientArtifact(filePath: string, maxBytes?: number): Promise<string>;
5
5
  export declare function readClientOutput(filePath: string): Promise<string | undefined>;
6
+ /**
7
+ * Say where a client contract is invalid. An unknown field usually means the
8
+ * contract was exported by a newer hypequery than this CLI understands.
9
+ */
10
+ export declare function describeClientContractError(error: unknown): unknown;
6
11
  //# sourceMappingURL=client-contract-file.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client-contract-file.d.ts","sourceRoot":"","sources":["../../src/utils/client-contract-file.ts"],"names":[],"mappings":"AAIA,qFAAqF;AACrF,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAe3E;AAED,wFAAwF;AACxF,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,SAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBhG;AAED,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAMpF"}
1
+ {"version":3,"file":"client-contract-file.d.ts","sourceRoot":"","sources":["../../src/utils/client-contract-file.ts"],"names":[],"mappings":"AAMA,qFAAqF;AACrF,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB3E;AAsBD,wFAAwF;AACxF,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,SAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBhG;AAED,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAMpF;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQnE"}
@@ -1,18 +1,27 @@
1
1
  import { constants } from 'node:fs';
2
- import { lstat, open } from 'node:fs/promises';
2
+ import { lstat, open, realpath, stat } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
+ import { ProtocolClientContractError } from '@hypequery/protocol';
5
+ import { pointer } from './json-pointer.js';
4
6
  /** Refuse filesystem indirection for generated output and its existing ancestors. */
5
7
  export async function checkClientOutputPath(filePath) {
6
- let current = path.resolve(filePath);
8
+ const target = path.resolve(filePath);
9
+ let current = target;
7
10
  while (true) {
8
11
  try {
9
12
  const info = await lstat(current);
10
- if (info.isSymbolicLink() || (current === path.resolve(filePath) ? !info.isFile() : !info.isDirectory())) {
11
- throw new Error('Client output must be a regular file under ordinary directories.');
13
+ if (info.isSymbolicLink())
14
+ throw await throughLink(filePath, target, current);
15
+ if (current === target ? !info.isFile() : !info.isDirectory()) {
16
+ throw new Error(current === target
17
+ ? `Client output ${filePath} must be a regular file.`
18
+ : `Client output ${filePath} must be a regular file under ordinary directories: ${current} is not a directory.`);
12
19
  }
13
20
  }
14
21
  catch (error) {
15
- if (error.code !== 'ENOENT')
22
+ // Missing, or below a file: the ancestor that exists is checked next.
23
+ const code = error.code;
24
+ if (code !== 'ENOENT' && code !== 'ENOTDIR')
16
25
  throw error;
17
26
  }
18
27
  const parent = path.dirname(current);
@@ -21,6 +30,27 @@ export async function checkClientOutputPath(filePath) {
21
30
  current = parent;
22
31
  }
23
32
  }
33
+ /**
34
+ * Name the link, and the same output without it, e.g. /tmp → /private/tmp on macOS.
35
+ * The path is suggested only when the link resolves to what that position needs:
36
+ * a directory above the output, a regular file at the output itself.
37
+ */
38
+ async function throughLink(filePath, target, link) {
39
+ const message = link === target
40
+ ? `Client output ${filePath} must be a regular file, not a symbolic link.`
41
+ : `Client output ${filePath} must be a regular file under ordinary directories: ${link} is a symbolic link.`;
42
+ try {
43
+ const real = await realpath(link);
44
+ const info = await stat(real);
45
+ if (link === target ? !info.isFile() : !info.isDirectory())
46
+ return new Error(message);
47
+ const resolved = path.join(real, path.relative(link, target));
48
+ return new Error(`${message} Use the path it points to instead: ${resolved}`);
49
+ }
50
+ catch {
51
+ return new Error(message);
52
+ }
53
+ }
24
54
  /** Bound allocation before reading; O_NOFOLLOW also refuses a swapped final symlink. */
25
55
  export async function readClientArtifact(filePath, maxBytes = 1_048_576) {
26
56
  const handle = await open(filePath, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
@@ -56,3 +86,17 @@ export async function readClientOutput(filePath) {
56
86
  throw error;
57
87
  }
58
88
  }
89
+ /**
90
+ * Say where a client contract is invalid. An unknown field usually means the
91
+ * contract was exported by a newer hypequery than this CLI understands.
92
+ */
93
+ export function describeClientContractError(error) {
94
+ if (!(error instanceof ProtocolClientContractError))
95
+ return error;
96
+ const at = error.field === undefined ? error.path : pointer(error.path, error.field);
97
+ const where = at ? ` at ${at}` : '';
98
+ const hint = error.code === 'HQ_CLIENT_UNKNOWN_FIELD'
99
+ ? ' The contract may come from a newer hypequery than this CLI; upgrade @hypequery/cli.'
100
+ : '';
101
+ return new Error(`${error.message.replace(/\.$/, '')}${where} (${error.code}).${hint}`, { cause: error });
102
+ }
@@ -0,0 +1,20 @@
1
+ import type { ProtocolDeploymentReleaseTarget } from '@hypequery/protocol';
2
+ import { type LiveDeploymentFetch } from './live-deployment.js';
3
+ /**
4
+ * The origin a generated client calls. Refused when Cloud did not say, when it
5
+ * is not https (loopback aside), or when it carries a path: the client resolves
6
+ * its absolute endpoint paths against the origin, so a prefix would be dropped.
7
+ */
8
+ export declare function clientOrigin(baseUrl: string | undefined): string;
9
+ /**
10
+ * The client contract of the target's live release, as Cloud serves it beside
11
+ * the deployment endpoint. Returned as text: the caller decodes and verifies it
12
+ * exactly as it would a contract file.
13
+ */
14
+ export declare function fetchCloudClientContract(input: {
15
+ readonly endpoint: string;
16
+ readonly token: string;
17
+ readonly target: ProtocolDeploymentReleaseTarget;
18
+ readonly fetch?: LiveDeploymentFetch;
19
+ }): Promise<string>;
20
+ //# sourceMappingURL=cloud-client-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-client-contract.d.ts","sourceRoot":"","sources":["../../src/utils/cloud-client-contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAW,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAOzE;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAehE;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,KAAK,EAAE;IACpD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,+BAA+B,CAAC;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,mBAAmB,CAAC;CACtC,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBlB"}
@@ -0,0 +1,52 @@
1
+ import { liveUrl } from './live-deployment.js';
2
+ import { readCapped } from './read-capped.js';
3
+ const MAX_CONTRACT_BYTES = 1_048_576;
4
+ const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]']);
5
+ /**
6
+ * The origin a generated client calls. Refused when Cloud did not say, when it
7
+ * is not https (loopback aside), or when it carries a path: the client resolves
8
+ * its absolute endpoint paths against the origin, so a prefix would be dropped.
9
+ */
10
+ export function clientOrigin(baseUrl) {
11
+ if (baseUrl === undefined) {
12
+ throw new Error('Cloud did not say where this release is served, so the client would have no base URL. Retry, or update Cloud.');
13
+ }
14
+ const url = new URL(baseUrl);
15
+ if (url.protocol !== 'https:' && !(url.protocol === 'http:' && LOOPBACK_HOSTS.has(url.hostname))) {
16
+ throw new Error(`Cloud serves this release from ${url.origin}, which is not https.`);
17
+ }
18
+ if (url.pathname !== '/' || url.search || url.hash) {
19
+ throw new Error(`Cloud serves this release under ${baseUrl} because an endpoint path collides with a reserved Cloud path (such as /mcp). `
20
+ + 'Rename that endpoint path and deploy again; generated clients call the customer origin directly.');
21
+ }
22
+ return url.origin;
23
+ }
24
+ /**
25
+ * The client contract of the target's live release, as Cloud serves it beside
26
+ * the deployment endpoint. Returned as text: the caller decodes and verifies it
27
+ * exactly as it would a contract file.
28
+ */
29
+ export async function fetchCloudClientContract(input) {
30
+ const url = liveUrl(input.endpoint, input.target, 'client-contract');
31
+ if (!url) {
32
+ throw new Error('The deployment endpoint is not a Cloud submissions URL, so it serves no client contract.');
33
+ }
34
+ const response = await (input.fetch ?? fetch)(url, {
35
+ method: 'GET',
36
+ headers: { Accept: 'application/json', Authorization: `Bearer ${input.token}` },
37
+ redirect: 'error',
38
+ signal: AbortSignal.timeout(30_000),
39
+ });
40
+ const target = `${input.target.project} / ${input.target.environment}`;
41
+ if (response.status === 401 || response.status === 403) {
42
+ throw new Error(`Cloud refused the client contract for ${target}. Run \`hypequery login\` again.`);
43
+ }
44
+ if (response.status === 404) {
45
+ throw new Error(`Cloud has no client contract for ${target}: publish a release first, `
46
+ + 'or this Cloud version does not serve client contracts yet.');
47
+ }
48
+ if (!response.ok) {
49
+ throw new Error(`Cloud could not return the client contract for ${target} (HTTP ${response.status}).`);
50
+ }
51
+ return new TextDecoder('utf-8', { fatal: true }).decode(await readCapped(response, MAX_CONTRACT_BYTES));
52
+ }
@@ -1,8 +1,11 @@
1
1
  import type { ReadableStream } from 'node:stream/web';
2
2
  import { type PreparedProtocolDeploymentReleaseEnvelope } from '@hypequery/protocol';
3
3
  import { type VerifiedDeploymentBundle } from './deployment-bundle.js';
4
- import type { DeploymentSubmissionResponse } from '@hypequery/deployment';
5
- export type { DeploymentSubmissionResponse } from '@hypequery/deployment';
4
+ import type { DeploymentSubmissionResponse as BaseDeploymentSubmissionResponse } from '@hypequery/deployment';
5
+ import { type DraftDeploymentResult } from './draft-deployment.js';
6
+ export type DeploymentSubmissionResponse = BaseDeploymentSubmissionResponse & {
7
+ readonly draft?: DraftDeploymentResult;
8
+ };
6
9
  export type DeploymentUploadErrorCode = 'HQ_UPLOAD_CONFIGURATION' | 'HQ_UPLOAD_IDENTITY_MISMATCH' | 'HQ_UPLOAD_BUNDLE_CHANGED' | 'HQ_UPLOAD_NETWORK' | 'HQ_UPLOAD_REJECTED' | 'HQ_UPLOAD_INVALID_RESPONSE';
7
10
  export declare class DeploymentUploadError extends Error {
8
11
  readonly code: DeploymentUploadErrorCode;
@@ -29,6 +32,7 @@ export interface HttpDeploymentUploadTransportOptions {
29
32
  readonly token: string;
30
33
  readonly expectedActivationRevision?: string | null;
31
34
  readonly replaceRestored?: boolean;
35
+ readonly draft?: string;
32
36
  readonly timeoutMs?: number;
33
37
  readonly fetch?: DeploymentFetch;
34
38
  }
@@ -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,0BAA0B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpD,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC,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,CA+F3B"}
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,IAAI,gCAAgC,EAAE,MAAM,uBAAuB,CAAC;AAC9G,OAAO,EAA8D,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAE/H,MAAM,MAAM,4BAA4B,GAAG,gCAAgC,GAAG;IAC5E,QAAQ,CAAC,KAAK,CAAC,EAAE,qBAAqB,CAAC;CACxC,CAAC;AAQF,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,0BAA0B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpD,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,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;AAoXD,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,oCAAoC,GAC5C,yBAAyB,CAqG3B"}
@@ -4,6 +4,7 @@ import path from 'node:path';
4
4
  import { prepareProtocolDeploymentBundleManifest, prepareProtocolDeploymentReleaseEnvelope, } from '@hypequery/protocol';
5
5
  import { DEPLOYMENT_BUNDLE_MANIFEST, } from './deployment-bundle.js';
6
6
  import { logger } from './logger.js';
7
+ import { parseDraftDeploymentResult, validateDraftDeploymentOptions } from './draft-deployment.js';
7
8
  const SHA256_PATTERN = /^[0-9a-f]{64}$/;
8
9
  const MAX_RESPONSE_BYTES = 64 * 1024;
9
10
  const DEFAULT_TIMEOUT_MS = 120_000;
@@ -249,15 +250,18 @@ function exactRecord(value, fields) {
249
250
  }
250
251
  return record;
251
252
  }
252
- function validateSubmissionResponse(input, bundleIdentity, releaseIdentity, status) {
253
+ function validateSubmissionResponse(input, bundleIdentity, releaseIdentity, status, expectedDraft) {
253
254
  const value = exactRecord(input, [
254
255
  'kind',
255
256
  'version',
256
257
  'status',
257
258
  'releaseIdentity',
258
259
  'bundleIdentity',
260
+ ...(expectedDraft ? ['draft'] : []),
259
261
  ]);
262
+ const draft = expectedDraft && value ? parseDraftDeploymentResult(value.draft, expectedDraft) : undefined;
260
263
  if (!value
264
+ || (expectedDraft && !draft)
261
265
  || value.kind !== 'hypequery-deployment-submission'
262
266
  || value.version !== 1
263
267
  || (value.status !== 'accepted' && value.status !== 'already-exists')
@@ -275,6 +279,7 @@ function validateSubmissionResponse(input, bundleIdentity, releaseIdentity, stat
275
279
  status: value.status,
276
280
  releaseIdentity: value.releaseIdentity,
277
281
  bundleIdentity: value.bundleIdentity,
282
+ ...(draft ? { draft } : {}),
278
283
  });
279
284
  }
280
285
  function rejectedMessage(response, body) {
@@ -319,6 +324,10 @@ function nestedUploadError(input) {
319
324
  }
320
325
  const defaultFetch = async (input, init) => fetch(input, init);
321
326
  export function createHttpDeploymentUploadTransport(options) {
327
+ validateDraftDeploymentOptions(options);
328
+ if (options.draft !== undefined && options.expectedActivationRevision !== undefined) {
329
+ configurationError('Draft uploads cannot specify an expected Production activation revision.');
330
+ }
322
331
  const endpoint = endpointUrl(options.endpoint);
323
332
  const token = bearerToken(options.token);
324
333
  const timeoutMs = timeout(options.timeoutMs);
@@ -347,6 +356,7 @@ export function createHttpDeploymentUploadTransport(options) {
347
356
  'Idempotency-Key': preparedRelease.identity,
348
357
  'X-HypeQuery-Bundle-Identity': manifest.identity,
349
358
  'X-HypeQuery-Release-Identity': preparedRelease.identity,
359
+ ...(options.draft !== undefined ? { 'X-HypeQuery-Draft-Name': options.draft } : {}),
350
360
  ...(options.expectedActivationRevision !== undefined
351
361
  ? {
352
362
  'X-HypeQuery-Expected-Activation-Revision': options.expectedActivationRevision ?? 'none',
@@ -387,7 +397,7 @@ export function createHttpDeploymentUploadTransport(options) {
387
397
  catch {
388
398
  throw new DeploymentUploadError('HQ_UPLOAD_INVALID_RESPONSE', 'Deployment service returned invalid JSON.', response.status);
389
399
  }
390
- return validateSubmissionResponse(input, manifest.identity, preparedRelease.identity, response.status);
400
+ return validateSubmissionResponse(input, manifest.identity, preparedRelease.identity, response.status, options.draft !== undefined ? { name: options.draft, endpoint, project: preparedRelease.release.target.project } : undefined);
391
401
  },
392
402
  };
393
403
  return Object.freeze(transport);
@@ -0,0 +1,16 @@
1
+ export interface DraftDeploymentResult {
2
+ readonly draftId: string;
3
+ readonly name: string;
4
+ readonly stagingUrl: string;
5
+ }
6
+ export declare function validateDraftDeploymentOptions(options: {
7
+ draft?: string;
8
+ replaceRestored?: boolean;
9
+ }): void;
10
+ /** Accept only the requested draft and a staging endpoint on the deployment service. */
11
+ export declare function parseDraftDeploymentResult(input: unknown, expected: {
12
+ name: string;
13
+ endpoint: string;
14
+ project: string;
15
+ }): DraftDeploymentResult | undefined;
16
+ //# sourceMappingURL=draft-deployment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draft-deployment.d.ts","sourceRoot":"","sources":["../../src/utils/draft-deployment.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,GAAG,IAAI,CAQP;AAED,wFAAwF;AACxF,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC5D,qBAAqB,GAAG,SAAS,CAmBnC"}
@@ -0,0 +1,35 @@
1
+ export function validateDraftDeploymentOptions(options) {
2
+ if (options.draft === undefined)
3
+ return;
4
+ if (options.draft.length > 64 || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(options.draft)) {
5
+ throw new Error('--draft must be a lowercase name with letters, numbers and single hyphens (max 64 characters).');
6
+ }
7
+ if (options.replaceRestored) {
8
+ throw new Error('--draft cannot be combined with --replace-restored; draft uploads do not replace Production.');
9
+ }
10
+ }
11
+ /** Accept only the requested draft and a staging endpoint on the deployment service. */
12
+ export function parseDraftDeploymentResult(input, expected) {
13
+ if (typeof input !== 'object' || input === null || Array.isArray(input)
14
+ || Object.getPrototypeOf(input) !== Object.prototype)
15
+ return undefined;
16
+ const value = input;
17
+ if (Object.keys(value).sort().join(',') !== 'draftId,name,stagingUrl'
18
+ || typeof value.draftId !== 'string'
19
+ || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(value.draftId)
20
+ || value.name !== expected.name
21
+ || typeof value.stagingUrl !== 'string')
22
+ return undefined;
23
+ let url;
24
+ try {
25
+ url = new URL(value.stagingUrl, expected.endpoint);
26
+ }
27
+ catch {
28
+ return undefined;
29
+ }
30
+ if (url.origin !== new URL(expected.endpoint).origin || url.username || url.password
31
+ || url.search || url.hash
32
+ || url.pathname !== `/api/gateway/${encodeURIComponent(expected.project)}/draft/${value.draftId}`)
33
+ return undefined;
34
+ return Object.freeze({ draftId: value.draftId, name: expected.name, stagingUrl: url.href });
35
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Hands out the field names of one generated dataset. A field's name is also
3
+ * its SQL alias, and ClickHouse resolves an alias before a column of the same
4
+ * name, so a field may take a source column's name only when it reads exactly
5
+ * that column; otherwise every expression reading that column would read the
6
+ * aliased field instead. A name that is taken, or is another column's name,
7
+ * gets the lowest free numeric suffix (`fareTotal2`).
8
+ */
9
+ export declare function createFieldNamer(columnNames: readonly string[]): (base: string, ownColumn?: string) => string;
10
+ //# sourceMappingURL=field-namer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-namer.d.ts","sourceRoot":"","sources":["../../src/utils/field-namer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,IAGrD,MAAM,MAAM,EAAE,YAAY,MAAM,KAAG,MAAM,CAQlD"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Hands out the field names of one generated dataset. A field's name is also
3
+ * its SQL alias, and ClickHouse resolves an alias before a column of the same
4
+ * name, so a field may take a source column's name only when it reads exactly
5
+ * that column; otherwise every expression reading that column would read the
6
+ * aliased field instead. A name that is taken, or is another column's name,
7
+ * gets the lowest free numeric suffix (`fareTotal2`).
8
+ */
9
+ export function createFieldNamer(columnNames) {
10
+ const columns = new Set(columnNames);
11
+ const taken = new Set();
12
+ return (base, ownColumn) => {
13
+ let name = base;
14
+ for (let suffix = 2; taken.has(name) || (columns.has(name) && name !== ownColumn); suffix += 1) {
15
+ name = `${base}${suffix}`;
16
+ }
17
+ taken.add(name);
18
+ return name;
19
+ };
20
+ }
@@ -0,0 +1,3 @@
1
+ /** RFC 6901: append *key* to the JSON pointer *path*. */
2
+ export declare function pointer(path: string, key: string): string;
3
+ //# sourceMappingURL=json-pointer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-pointer.d.ts","sourceRoot":"","sources":["../../src/utils/json-pointer.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEzD"}
@@ -0,0 +1,4 @@
1
+ /** RFC 6901: append *key* to the JSON pointer *path*. */
2
+ export function pointer(path, key) {
3
+ return `${path}/${key.replaceAll('~', '~0').replaceAll('/', '~1')}`;
4
+ }
@@ -31,7 +31,7 @@ export type LiveDeploymentFetch = typeof fetch;
31
31
  * the endpoint is not a Cloud submissions URL. Refuses anything but https (or
32
32
  * loopback http) so a credential is never sent in the clear.
33
33
  */
34
- export declare function liveUrl(endpoint: string, target: ProtocolDeploymentReleaseTarget, resource: 'state' | 'source' | 'endpoints'): string | undefined;
34
+ export declare function liveUrl(endpoint: string, target: ProtocolDeploymentReleaseTarget, resource: 'state' | 'source' | 'endpoints' | 'client-contract'): string | undefined;
35
35
  export declare function fetchLiveDeployment(input: {
36
36
  readonly endpoint: string;
37
37
  readonly token: string;
@@ -1 +1 @@
1
- {"version":3,"file":"live-deployment.d.ts","sourceRoot":"","sources":["../../src/utils/live-deployment.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,+BAA+B,EACrC,MAAM,qBAAqB,CAAC;AAO7B,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,wBAAwB,EAAE,CAAC;IACpD,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAClB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;QACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;KACzB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,MAAM,EAAE,+BAA+B,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG;QACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;QACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;QAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,oBAAoB,CAAC;KACxC,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,OAAO,KAAK,CAAC;AAE/C;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,+BAA+B,EACvC,QAAQ,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GACzC,MAAM,GAAG,SAAS,CAkBpB;AAsJD,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAC/C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,+BAA+B,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,QAAQ,CAAC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,mBAAmB,CAAC;CACtC,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAuCtC"}
1
+ {"version":3,"file":"live-deployment.d.ts","sourceRoot":"","sources":["../../src/utils/live-deployment.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,+BAA+B,EACrC,MAAM,qBAAqB,CAAC;AAO7B,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,wBAAwB,EAAE,CAAC;IACpD,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAClB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;QACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;KACzB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,MAAM,EAAE,+BAA+B,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG;QACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;QACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;QAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,oBAAoB,CAAC;KACxC,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,OAAO,KAAK,CAAC;AAE/C;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,+BAA+B,EACvC,QAAQ,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,iBAAiB,GAC7D,MAAM,GAAG,SAAS,CAkBpB;AAsJD,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAC/C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,+BAA+B,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,QAAQ,CAAC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,mBAAmB,CAAC;CACtC,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAuCtC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * A response body, refused once it passes `maxBytes`: by the declared length
3
+ * before reading, and while streaming when no length is declared.
4
+ */
5
+ export declare function readCapped(response: Response, maxBytes: number): Promise<Uint8Array>;
6
+ //# sourceMappingURL=read-capped.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-capped.d.ts","sourceRoot":"","sources":["../../src/utils/read-capped.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA6B1F"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * A response body, refused once it passes `maxBytes`: by the declared length
3
+ * before reading, and while streaming when no length is declared.
4
+ */
5
+ export async function readCapped(response, maxBytes) {
6
+ const tooLarge = () => new Error(`The response exceeds ${maxBytes} bytes.`);
7
+ const declared = Number(response.headers.get('content-length'));
8
+ if (Number.isFinite(declared) && declared > maxBytes) {
9
+ // Release the connection rather than leave the body unread.
10
+ await response.body?.cancel();
11
+ throw tooLarge();
12
+ }
13
+ if (!response.body)
14
+ return new Uint8Array(await response.arrayBuffer());
15
+ const reader = response.body.getReader();
16
+ const chunks = [];
17
+ let total = 0;
18
+ for (;;) {
19
+ const { done, value } = await reader.read();
20
+ if (done)
21
+ break;
22
+ total += value.byteLength;
23
+ if (total > maxBytes) {
24
+ await reader.cancel();
25
+ throw tooLarge();
26
+ }
27
+ chunks.push(value);
28
+ }
29
+ const bytes = new Uint8Array(total);
30
+ let offset = 0;
31
+ for (const chunk of chunks) {
32
+ bytes.set(chunk, offset);
33
+ offset += chunk.byteLength;
34
+ }
35
+ return bytes;
36
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * One argument as a POSIX shell word, for commands printed for the user to
3
+ * copy. Plain words stay as they are; anything else is single-quoted.
4
+ */
5
+ export declare function shellQuote(value: string): string;
6
+ //# sourceMappingURL=shell-quote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-quote.d.ts","sourceRoot":"","sources":["../../src/utils/shell-quote.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * One argument as a POSIX shell word, for commands printed for the user to
3
+ * copy. Plain words stay as they are; anything else is single-quoted.
4
+ */
5
+ export function shellQuote(value) {
6
+ return /^[A-Za-z0-9_./:=@%+-]+$/.test(value) ? value : `'${value.replaceAll("'", `'\\''`)}'`;
7
+ }
@@ -625,7 +625,7 @@ export declare const EVENT_CATALOG: {
625
625
  readonly flags_used: {
626
626
  readonly kind: "enum_list";
627
627
  readonly description: string;
628
- readonly values: readonly ("--path" | "--style" | "--database" | "--chdb-path" | "--auth" | "--all-tables" | "--tables" | "--exclude-tables" | "--no-example" | "--no-interactive" | "--force" | "--skip-connection" | "--port" | "--hostname" | "--no-watch" | "--no-cache" | "--cache" | "--redis-url" | "--open" | "--cors" | "--quiet" | "--tenant" | "--self-test" | "--url" | "--client-contract-url" | "--output" | "--tenant-column" | "--check" | "--diff" | "--cloud-url" | "--environment" | "--bundle-output" | "--release-output" | "--project" | "--no-source" | "--endpoint" | "--replace-restored" | "--mcp-config" | "--release" | "--no-telemetry" | "--help" | "--version")[];
628
+ readonly values: readonly ("--path" | "--style" | "--database" | "--chdb-path" | "--auth" | "--all-tables" | "--tables" | "--exclude-tables" | "--no-example" | "--no-interactive" | "--force" | "--skip-connection" | "--port" | "--hostname" | "--no-watch" | "--no-cache" | "--cache" | "--redis-url" | "--open" | "--cors" | "--quiet" | "--tenant" | "--self-test" | "--url" | "--client-contract-url" | "--output" | "--tenant-column" | "--check" | "--diff" | "--cloud" | "--project" | "--environment" | "--endpoint" | "--cloud-url" | "--bundle-output" | "--release-output" | "--no-source" | "--replace-restored" | "--mcp-config" | "--draft" | "--client" | "--release" | "--no-telemetry" | "--help" | "--version")[];
629
629
  };
630
630
  readonly outcome: {
631
631
  readonly kind: "enum";