@hypequery/protocol 0.10.0 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,99 +1,68 @@
1
1
  # @hypequery/protocol
2
2
 
3
- Public contracts and the TypeScript reference implementation for portable
4
- Hypequery artifacts.
5
-
6
- ## Current status
7
-
8
- This package contains proposed version 1 tagged values, identifiers,
9
- expressions, query schemas and implementations, Dataset deployment contracts,
10
- and deployment bundle manifests. The API remains pre-stable while the
11
- language-neutral specifications and conformance fixtures are reviewed; these
12
- drafts do not yet establish a stable Cloud protocol.
13
-
14
- The normative source is
15
- [`specs/security-protocol`](../../specs/security-protocol/README.md).
16
-
17
- ## Scope
18
-
19
- The package contains deterministic, framework-independent implementations of
20
- accepted protocol rules. The current implementation provides strict tagged
21
- value validation, RFC 8785 canonical encoding, duplicate-aware decoding,
22
- portable logical identifiers, closed expression and schema trees, query
23
- implementation artifacts, and a validated deployment envelope with a
24
- domain-separated identity.
25
-
26
- It will not connect to ClickHouse, execute queries, load project source, access
27
- credentials or the environment, perform network or filesystem I/O, implement
28
- authentication or tenancy, or contain CLI, HTTP, UI, and Cloud operations.
29
-
30
- Self-hosted Serve continues to run project source directly. Deployment bundles
31
- are required only by Cloud deployment and may be generated ephemerally for
32
- local compatibility and security diagnostics.
33
-
34
- ## Public API
35
-
36
- Only the root package export is public. Deep imports from `src` or `dist` are
37
- unsupported. Package SemVer and protocol/artifact versions are separate; an
38
- installed npm version never determines an artifact's identity.
39
-
40
- The proposed tagged-value surface exports:
41
-
42
- - `validateCanonicalValue`
43
- - `encodeCanonicalValue` and `encodeCanonicalValueToString`
44
- - `decodeCanonicalValue`
45
- - `hashCanonicalValue`
46
- - `ProtocolValueError` and stable error-code types
47
- - tagged-value, option, and limit types
48
-
49
- The raw conformance digest is not a deployment identity or shared cache key.
50
- Those domains require separate, versioned, domain-separated contracts.
51
-
52
- The proposed identifier surface exports strict parse, guard, split, and join
53
- helpers for simple and dot-qualified logical identifiers. Identifiers are
54
- ASCII, case-sensitive, preserved exactly, and are not SQL identifiers.
55
-
56
- The proposed expression surface exports strict validators and immutable types
57
- for derived formulas, comparisons, filtered aggregations, all current dataset
58
- aggregations, and dataset/metric query envelopes. It intentionally excludes
59
- caller-supplied SQL and tenant identity; consumers validate names and policy
60
- against a dataset contract before execution.
61
-
62
- The proposed schema surface exports strict types and validation for portable
63
- query input/output schemas. It covers the current declarative Serve/Zod schema
64
- features without depending on Zod or embedding executable transforms and
65
- refinements. The reusable schema-value parser applies defaults and unknown
66
- property policy to bounded plain wire values and returns detached immutable
67
- values for execution adapters.
68
-
69
- The proposed query-implementation surface keeps trusted implementation details
70
- separate from public query intent. It covers Dataset SQL expressions, fixed
71
- semantic plans, compiled read-only ClickHouse statements with bound input or
72
- tenant parameters, and hashed Node/Python runtime references for Serve handlers
73
- that cannot be lowered portably. Validation does not execute or authorize SQL.
74
-
75
- The proposed deployment surface combines complete Dataset definitions, named
76
- Serve queries, endpoint policy, and runtime artifact identities into one strict
77
- versioned envelope. Dataset and Serve adapters live in their owning packages;
78
- the protocol package remains deterministic and framework-independent.
79
- Validated envelopes can be encoded as canonical RFC 8785 bytes and identified
80
- with the deployment-v1 domain-separated SHA-256 digest.
81
-
82
- The proposed deployment-bundle surface validates the portable manifest that
83
- binds a deployment identity to exact deployment and runtime artifact files. It
84
- provides canonical encoding and a separate bundle-v1 identity. Filesystem-safe
85
- writing remains in the CLI, while reusable filesystem verification and
86
- receiving-side intake live in `@hypequery/deployment`; this package performs no
87
- I/O.
88
-
89
- The proposed deployment-release surface binds one verified bundle identity to
90
- an explicit project and environment. Its deterministic identity serves as the
91
- idempotency key for authenticated deployment submission without putting
92
- credentials, timestamps, release state, or provider behavior into the envelope.
93
-
94
- ## Runtime compatibility
95
-
96
- This package is ESM-only. Consumers must load it with `import`; CommonJS
97
- `require()` and a dual ESM/CommonJS build are intentionally out of scope.
98
- Older tools that ignore the package `exports` map and attempt to require the
99
- `main` entry may fail with `ERR_REQUIRE_ESM`.
3
+ `@hypequery/protocol` is the deterministic TypeScript reference implementation for Hypequery’s portable, language-neutral artifacts. It validates and canonically encodes semantic expressions, query schemas, deployment contracts, bundle manifests, release envelopes, query events, and diagnostics without connecting to a database or executing user code.
4
+
5
+ The normative source is [`specs/security-protocol`](../../specs/security-protocol/README.md). The package is pre-stable while the draft wire contracts and conformance fixtures are reviewed; the npm version is not an artifact version.
6
+
7
+ ## What the protocol solves
8
+
9
+ Hypequery authoring tools, deployment systems, runtimes, and implementations in different languages need to agree on four things:
10
+
11
+ 1. which values and structures are accepted;
12
+ 2. the exact canonical bytes for an accepted artifact;
13
+ 3. the stable identity derived from those bytes;
14
+ 4. the failure code returned for rejected input.
15
+
16
+ This package implements those rules in TypeScript. Python and other implementations are tested against the same fixtures with `@hypequery/protocol-conformance`.
17
+
18
+ ## Main surfaces
19
+
20
+ - canonical tagged values and RFC 8785 encoding
21
+ - strict simple and qualified logical identifiers
22
+ - closed semantic expression and query envelopes
23
+ - portable input and output schemas
24
+ - trusted query implementation artifacts
25
+ - dataset and named-query deployment contracts
26
+ - closed deployment bundle manifests
27
+ - project/environment release envelopes
28
+ - compiled query settings, cancellation, events, and diagnostics
29
+
30
+ The package performs no filesystem or network I/O. It does not load project source, choose credentials, connect to ClickHouse, authenticate users, resolve tenants, or host HTTP routes. Those jobs remain with the CLI, deployment package, and runtime adapters.
31
+
32
+ ## Canonical values
33
+
34
+ The root export includes validation, encoding, decoding, hashing, errors, and the related immutable types:
35
+
36
+ ```ts
37
+ import {
38
+ validateCanonicalValue,
39
+ encodeCanonicalValueToString,
40
+ decodeCanonicalValue,
41
+ hashCanonicalValue,
42
+ ProtocolValueError,
43
+ } from '@hypequery/protocol';
44
+ ```
45
+
46
+ Domain-specific identities remain separate. A raw canonical-value digest is not automatically a deployment, bundle, release, or cache identity.
47
+
48
+ ## Portable analytics definitions
49
+
50
+ Expression validators cover derived formulas, comparisons, filtered aggregates, the full dataset aggregation surface, and metric/dataset query envelopes. Runtime callers provide semantic names and values; they cannot embed SQL or tenant identity in these portable query structures.
51
+
52
+ Schema validators cover the declarative Serve/Zod features that can travel between runtimes without importing Zod or executable refinements. Schema application handles defaults and unknown properties on bounded wire values.
53
+
54
+ ## Deployment artifacts
55
+
56
+ A deployment combines dataset definitions, named Serve queries, endpoint policy, and runtime artifact identities in one strict versioned envelope. Bundle manifests bind that deployment and every runtime file by path, byte length, and hash. Release envelopes bind a verified bundle to one explicit project and environment.
57
+
58
+ Canonical encoders and domain-separated SHA-256 identities make these artifacts reproducible across language implementations. Filesystem-safe construction and verification live in `@hypequery/deployment` and the CLI.
59
+
60
+ ## Package use
61
+
62
+ Only the root package export is public. Package SemVer and protocol versions are deliberately separate. The package is ESM-only and must be loaded with `import`.
63
+
64
+ Self-hosted `@hypequery/serve` can continue to run application source directly. Portable deployment bundles are needed for deployment, compatibility checks, and cross-runtime handoff—not for ordinary local query execution.
65
+
66
+ ## License
67
+
68
+ Apache-2.0.
@@ -13,6 +13,8 @@ export interface ProtocolDeploymentBundleSourceRevision {
13
13
  readonly kind: 'git';
14
14
  readonly commit: string;
15
15
  readonly dirty: boolean;
16
+ /** Checked-out branch at build time; absent for detached HEADs. */
17
+ readonly branch?: string;
16
18
  }
17
19
  export interface ProtocolDeploymentBundleSource {
18
20
  /** Directory within the closed bundle that contains project-relative source files. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/bundles/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,kCAAmC,SAAQ,4BAA4B;IACtF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,gCAAiC,SAAQ,4BAA4B;IACpF,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAAC;CACrC;AAED,MAAM,WAAW,sCAAsC;IACrD,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,8BAA8B;IAC7C,sFAAsF;IACtF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,4BAA4B,EAAE,CAAC;IACxD,QAAQ,CAAC,QAAQ,CAAC,EAAE,sCAAsC,CAAC;CAC5D;AAED,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,IAAI,EAAE,6BAA6B,CAAC;IAC7C,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,QAAQ,CAAC,UAAU,EAAE,kCAAkC,CAAC;IACxD,QAAQ,CAAC,SAAS,EAAE,SAAS,gCAAgC,EAAE,CAAC;IAChE,QAAQ,CAAC,MAAM,CAAC,EAAE,8BAA8B,CAAC;CAClD;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;;;;GAKG;AACH,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,8BAA8B,CAAC,CAAC;CAC3D;AAED,MAAM,MAAM,iCAAiC,GACzC,gBAAgB,GAChB,yBAAyB,GACzB,2BAA2B,GAC3B,yBAAyB,GACzB,wBAAwB,GACxB,6BAA6B,GAC7B,0BAA0B,GAC1B,qBAAqB,GACrB,yBAAyB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/bundles/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,kCAAmC,SAAQ,4BAA4B;IACtF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,gCAAiC,SAAQ,4BAA4B;IACpF,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAAC;CACrC;AAED,MAAM,WAAW,sCAAsC;IACrD,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,mEAAmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,8BAA8B;IAC7C,sFAAsF;IACtF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,4BAA4B,EAAE,CAAC;IACxD,QAAQ,CAAC,QAAQ,CAAC,EAAE,sCAAsC,CAAC;CAC5D;AAED,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,IAAI,EAAE,6BAA6B,CAAC;IAC7C,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,QAAQ,CAAC,UAAU,EAAE,kCAAkC,CAAC;IACxD,QAAQ,CAAC,SAAS,EAAE,SAAS,gCAAgC,EAAE,CAAC;IAChE,QAAQ,CAAC,MAAM,CAAC,EAAE,8BAA8B,CAAC;CAClD;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;;;;GAKG;AACH,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,8BAA8B,CAAC,CAAC;CAC3D;AAED,MAAM,MAAM,iCAAiC,GACzC,gBAAgB,GAChB,yBAAyB,GACzB,2BAA2B,GAC3B,yBAAyB,GACzB,wBAAwB,GACxB,6BAA6B,GAC7B,0BAA0B,GAC1B,qBAAqB,GACrB,yBAAyB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/bundles/validate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAIV,gCAAgC,EAChC,+BAA+B,EAGhC,MAAM,YAAY,CAAC;AA4NpB,wBAAgB,wCAAwC,CACtD,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,+BAAoC,GAC5C,gCAAgC,CA+ClC"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/bundles/validate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAIV,gCAAgC,EAChC,+BAA+B,EAGhC,MAAM,YAAY,CAAC;AAmPpB,wBAAgB,wCAAwC,CACtD,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,+BAAoC,GAC5C,gCAAgC,CA+ClC"}
@@ -113,9 +113,9 @@ function validateArtifact(input, path, limits) {
113
113
  byteLength: byteLength(value.byteLength, `${path}.byteLength`, limits.maxArtifactBytes),
114
114
  });
115
115
  }
116
- function validateSourceRevision(input, path) {
116
+ function validateSourceRevision(input, path, limits) {
117
117
  const value = requireRecord(input, path);
118
- exactFields(value, ['kind', 'commit', 'dirty'], path);
118
+ exactFields(value, ['kind', 'commit', 'dirty'], path, ['branch']);
119
119
  if (value.kind !== 'git') {
120
120
  if (typeof value.kind !== 'string')
121
121
  bundleError('HQ_BUNDLE_TYPE', `${path}.kind`);
@@ -128,10 +128,33 @@ function validateSourceRevision(input, path) {
128
128
  }
129
129
  if (typeof value.dirty !== 'boolean')
130
130
  bundleError('HQ_BUNDLE_TYPE', `${path}.dirty`);
131
+ let branch;
132
+ if (value.branch !== undefined) {
133
+ if (typeof value.branch !== 'string')
134
+ bundleError('HQ_BUNDLE_TYPE', `${path}.branch`);
135
+ if (textEncoder.encode(value.branch).byteLength > limits.maxPathBytes) {
136
+ bundleError('HQ_BUNDLE_TOO_LARGE', `${path}.branch`);
137
+ }
138
+ const invalidCharacter = [...value.branch].some(character => {
139
+ const code = character.charCodeAt(0);
140
+ return code <= 0x20 || code === 0x7f || '~^:?*[\\'.includes(character);
141
+ });
142
+ const segments = value.branch.split('/');
143
+ if (!value.branch || value.branch === '@' || value.branch.startsWith('-')
144
+ || value.branch.startsWith('/') || value.branch.endsWith('/')
145
+ || value.branch.endsWith('.') || value.branch.includes('//')
146
+ || value.branch.includes('..') || value.branch.includes('@{')
147
+ || invalidCharacter
148
+ || segments.some(segment => segment.startsWith('.') || segment.endsWith('.lock'))) {
149
+ bundleError('HQ_BUNDLE_INVALID_VALUE', `${path}.branch`);
150
+ }
151
+ branch = value.branch;
152
+ }
131
153
  return freezeRecord({
132
154
  kind: 'git',
133
155
  commit: value.commit,
134
156
  dirty: value.dirty,
157
+ ...(branch ? { branch } : {}),
135
158
  });
136
159
  }
137
160
  function validateSource(input, path, limits) {
@@ -172,7 +195,7 @@ function validateSource(input, path, limits) {
172
195
  files,
173
196
  ...(value.revision === undefined
174
197
  ? {}
175
- : { revision: validateSourceRevision(value.revision, `${path}.revision`) }),
198
+ : { revision: validateSourceRevision(value.revision, `${path}.revision`, limits) }),
176
199
  });
177
200
  }
178
201
  function hasTreeCollision(paths) {
package/package.json CHANGED
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "name": "@hypequery/protocol",
3
- "version": "0.10.0",
4
- "description": "Public contracts and TypeScript reference implementation for portable Hypequery artifacts",
3
+ "version": "0.10.2",
4
+ "description": "Portable analytics contracts and TypeScript reference implementation for Hypequery",
5
+ "keywords": [
6
+ "hypequery",
7
+ "protocol",
8
+ "analytics",
9
+ "semantic-layer",
10
+ "typescript",
11
+ "canonical-json"
12
+ ],
5
13
  "license": "Apache-2.0",
6
14
  "type": "module",
7
15
  "main": "dist/index.js",
@@ -26,10 +34,10 @@
26
34
  },
27
35
  "repository": {
28
36
  "type": "git",
29
- "url": "https://github.com/hypequery/hypequery.git",
37
+ "url": "git+https://github.com/hypequery/hypequery.git",
30
38
  "directory": "packages/protocol"
31
39
  },
32
- "homepage": "https://github.com/hypequery/hypequery/tree/main/packages/protocol",
40
+ "homepage": "https://hypequery.com",
33
41
  "bugs": {
34
42
  "url": "https://github.com/hypequery/hypequery/issues"
35
43
  },