@hypequery/protocol 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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.
@@ -0,0 +1,19 @@
1
+ import { type DeriveProtocolCacheKeyOptions, type ProtocolCacheKeyErrorCode } from './types.js';
2
+ export declare class ProtocolCacheKeyError extends Error {
3
+ readonly code: ProtocolCacheKeyErrorCode;
4
+ constructor(code: ProtocolCacheKeyErrorCode);
5
+ }
6
+ /**
7
+ * Derives the opaque namespace prefix. Truncated to 16 bytes because it is a
8
+ * grouping label for prefix operations, not an authentication tag.
9
+ */
10
+ export declare function deriveProtocolCacheNamespaceToken(secret: Uint8Array, project: string, environment: string): string;
11
+ /**
12
+ * Derives the opaque store key for one canonical preimage (RFC 0013).
13
+ *
14
+ * The preimage never appears in the result. An unkeyed digest would be
15
+ * offline-guessable — the schema is public and identifier value spaces are
16
+ * small — so the derivation is an HMAC under a per-namespace secret.
17
+ */
18
+ export declare function deriveProtocolCacheKey(options: DeriveProtocolCacheKeyOptions): string;
19
+ //# sourceMappingURL=cache-keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-keys.d.ts","sourceRoot":"","sources":["../../src/cache-keys/cache-keys.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,6BAA6B,EAClC,KAAK,yBAAyB,EAC/B,MAAM,YAAY,CAAC;AAUpB,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,EAAE,yBAAyB,CAAC;gBAE7B,IAAI,EAAE,yBAAyB;CAO5C;AA8FD;;;GAGG;AACH,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GAClB,MAAM,CAUR;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG,MAAM,CA8BrF"}
@@ -0,0 +1,141 @@
1
+ import { hmac } from '@noble/hashes/hmac';
2
+ import { sha256 } from '@noble/hashes/sha2';
3
+ import { validateProtocolDeploymentReleaseTarget } from '../releases/validate.js';
4
+ import { PROTOCOL_CACHE_KEY_LIMITS, } from './types.js';
5
+ const SCHEME = 'hq1';
6
+ const NAMESPACE_DOMAIN = 'hypequery.cache.namespace.v1';
7
+ const ENTRY_DOMAIN = 'hypequery.cache.entry.v1';
8
+ const textEncoder = new TextEncoder();
9
+ const BASE64URL = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
10
+ export class ProtocolCacheKeyError extends Error {
11
+ code;
12
+ constructor(code) {
13
+ // The message is the code and nothing else: an error here has the secret
14
+ // and the preimage in scope, and neither may reach a log.
15
+ super(code);
16
+ this.name = 'ProtocolCacheKeyError';
17
+ this.code = code;
18
+ }
19
+ }
20
+ function cacheKeyError(code) {
21
+ throw new ProtocolCacheKeyError(code);
22
+ }
23
+ /** RFC 4648 base64url without padding. */
24
+ function base64url(bytes) {
25
+ let out = '';
26
+ for (let index = 0; index < bytes.length; index += 3) {
27
+ const b0 = bytes[index];
28
+ const b1 = bytes[index + 1];
29
+ const b2 = bytes[index + 2];
30
+ out += BASE64URL[b0 >> 2];
31
+ out += BASE64URL[((b0 & 0x03) << 4) | ((b1 ?? 0) >> 4)];
32
+ if (b1 === undefined)
33
+ break;
34
+ out += BASE64URL[((b1 & 0x0f) << 2) | ((b2 ?? 0) >> 6)];
35
+ if (b2 === undefined)
36
+ break;
37
+ out += BASE64URL[b2 & 0x3f];
38
+ }
39
+ return out;
40
+ }
41
+ /**
42
+ * Joins parts with a single 0x00 byte. RFC 0008 restricts deployment target
43
+ * tokens to an ASCII grammar that excludes 0x00, so the concatenation is
44
+ * injective — which is why the namespace must be validated before this runs.
45
+ */
46
+ function joinNulSeparated(parts) {
47
+ const total = parts.reduce((sum, part) => sum + part.byteLength, 0) + parts.length - 1;
48
+ const out = new Uint8Array(total);
49
+ let offset = 0;
50
+ parts.forEach((part, index) => {
51
+ if (index > 0) {
52
+ out[offset] = 0x00;
53
+ offset += 1;
54
+ }
55
+ out.set(part, offset);
56
+ offset += part.byteLength;
57
+ });
58
+ return out;
59
+ }
60
+ function requireSecret(secret) {
61
+ if (!secret || secret.byteLength === 0)
62
+ cacheKeyError('HQ_CACHE_KEY_SECRET_MISSING');
63
+ if (secret.byteLength < PROTOCOL_CACHE_KEY_LIMITS.minSecretBytes) {
64
+ cacheKeyError('HQ_CACHE_KEY_SECRET_TOO_SHORT');
65
+ }
66
+ return secret;
67
+ }
68
+ function requireNamespace(namespace) {
69
+ try {
70
+ const target = validateProtocolDeploymentReleaseTarget(namespace);
71
+ return {
72
+ project: textEncoder.encode(target.project),
73
+ environment: textEncoder.encode(target.environment),
74
+ };
75
+ }
76
+ catch {
77
+ // Deliberately does not forward release-target validation details: a
78
+ // single code keeps callers from branching on namespace internals.
79
+ cacheKeyError('HQ_CACHE_KEY_INVALID_NAMESPACE');
80
+ }
81
+ }
82
+ function requireKeyVersion(keyVersion) {
83
+ if (typeof keyVersion !== 'number'
84
+ || !Number.isSafeInteger(keyVersion)
85
+ || keyVersion < PROTOCOL_CACHE_KEY_LIMITS.minKeyVersion
86
+ || keyVersion > PROTOCOL_CACHE_KEY_LIMITS.maxKeyVersion) {
87
+ cacheKeyError('HQ_CACHE_KEY_INVALID_VERSION');
88
+ }
89
+ return keyVersion;
90
+ }
91
+ function requirePreimage(preimage) {
92
+ if (typeof preimage === 'string'
93
+ && preimage.length > PROTOCOL_CACHE_KEY_LIMITS.maxPreimageBytes) {
94
+ cacheKeyError('HQ_CACHE_KEY_PREIMAGE_TOO_LARGE');
95
+ }
96
+ const bytes = typeof preimage === 'string' ? textEncoder.encode(preimage) : preimage;
97
+ if (bytes.byteLength > PROTOCOL_CACHE_KEY_LIMITS.maxPreimageBytes) {
98
+ cacheKeyError('HQ_CACHE_KEY_PREIMAGE_TOO_LARGE');
99
+ }
100
+ return bytes;
101
+ }
102
+ /**
103
+ * Derives the opaque namespace prefix. Truncated to 16 bytes because it is a
104
+ * grouping label for prefix operations, not an authentication tag.
105
+ */
106
+ export function deriveProtocolCacheNamespaceToken(secret, project, environment) {
107
+ const key = requireSecret(secret);
108
+ const namespace = requireNamespace({ project, environment });
109
+ const input = joinNulSeparated([
110
+ textEncoder.encode(NAMESPACE_DOMAIN),
111
+ namespace.project,
112
+ namespace.environment,
113
+ ]);
114
+ const mac = hmac(sha256, key, input);
115
+ return base64url(mac.slice(0, PROTOCOL_CACHE_KEY_LIMITS.namespaceTokenBytes));
116
+ }
117
+ /**
118
+ * Derives the opaque store key for one canonical preimage (RFC 0013).
119
+ *
120
+ * The preimage never appears in the result. An unkeyed digest would be
121
+ * offline-guessable — the schema is public and identifier value spaces are
122
+ * small — so the derivation is an HMAC under a per-namespace secret.
123
+ */
124
+ export function deriveProtocolCacheKey(options) {
125
+ const secret = requireSecret(options.secret);
126
+ const { project, environment } = requireNamespace(options.namespace);
127
+ const keyVersion = requireKeyVersion(options.keyVersion);
128
+ const preimage = requirePreimage(options.preimage);
129
+ const namespaceToken = base64url(hmac(sha256, secret, joinNulSeparated([textEncoder.encode(NAMESPACE_DOMAIN), project, environment])).slice(0, PROTOCOL_CACHE_KEY_LIMITS.namespaceTokenBytes));
130
+ // The namespace participates in the entry MAC directly, not only through the
131
+ // prefix, so two namespaces cannot collide even in a store that ignores
132
+ // prefixes.
133
+ const entryMac = hmac(sha256, secret, joinNulSeparated([textEncoder.encode(ENTRY_DOMAIN), project, environment, preimage]));
134
+ const key = `${SCHEME}.${keyVersion}.${namespaceToken}.${base64url(entryMac)}`;
135
+ /* c8 ignore next 3 -- unreachable with version-1 limits; a guard against a
136
+ future format change silently exceeding what stores accept. */
137
+ if (textEncoder.encode(key).byteLength > PROTOCOL_CACHE_KEY_LIMITS.maxStoreKeyBytes) {
138
+ cacheKeyError('HQ_CACHE_KEY_INVALID_VERSION');
139
+ }
140
+ return key;
141
+ }
@@ -0,0 +1,4 @@
1
+ export { ProtocolCacheKeyError, deriveProtocolCacheKey, deriveProtocolCacheNamespaceToken, } from './cache-keys.js';
2
+ export { PROTOCOL_CACHE_KEY_LIMITS } from './types.js';
3
+ export type { DeriveProtocolCacheKeyOptions, ProtocolCacheKeyErrorCode, ProtocolCacheKeyNamespace, } from './types.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache-keys/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,iCAAiC,GAClC,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AACvD,YAAY,EACV,6BAA6B,EAC7B,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { ProtocolCacheKeyError, deriveProtocolCacheKey, deriveProtocolCacheNamespaceToken, } from './cache-keys.js';
2
+ export { PROTOCOL_CACHE_KEY_LIMITS } from './types.js';
@@ -0,0 +1,26 @@
1
+ /** Absolute limits for cache key version 1 (RFC 0013). */
2
+ export declare const PROTOCOL_CACHE_KEY_LIMITS: {
3
+ readonly minSecretBytes: 32;
4
+ readonly maxPreimageBytes: 1048576;
5
+ readonly namespaceTokenBytes: 16;
6
+ readonly entryMacBytes: 32;
7
+ readonly maxStoreKeyBytes: 128;
8
+ readonly minKeyVersion: 1;
9
+ readonly maxKeyVersion: 2147483647;
10
+ };
11
+ export type ProtocolCacheKeyErrorCode = 'HQ_CACHE_KEY_SECRET_MISSING' | 'HQ_CACHE_KEY_SECRET_TOO_SHORT' | 'HQ_CACHE_KEY_INVALID_NAMESPACE' | 'HQ_CACHE_KEY_INVALID_VERSION' | 'HQ_CACHE_KEY_PREIMAGE_TOO_LARGE';
12
+ /** The RFC 0008 deployment target a cache entry belongs to. */
13
+ export interface ProtocolCacheKeyNamespace {
14
+ readonly project: string;
15
+ readonly environment: string;
16
+ }
17
+ export interface DeriveProtocolCacheKeyOptions {
18
+ /** At least 32 bytes, distinct per namespace, never derived from a name. */
19
+ readonly secret: Uint8Array;
20
+ readonly namespace: ProtocolCacheKeyNamespace;
21
+ /** Increments whenever the namespace secret is rotated. */
22
+ readonly keyVersion: number;
23
+ /** Canonical bytes identifying the query. Stays in memory. */
24
+ readonly preimage: Uint8Array | string;
25
+ }
26
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cache-keys/types.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,eAAO,MAAM,yBAAyB;;;;;;;;CAQ5B,CAAC;AAEX,MAAM,MAAM,yBAAyB,GACjC,6BAA6B,GAC7B,+BAA+B,GAC/B,gCAAgC,GAChC,8BAA8B,GAC9B,iCAAiC,CAAC;AAEtC,+DAA+D;AAC/D,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,6BAA6B;IAC5C,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,yBAAyB,CAAC;IAC9C,2DAA2D;IAC3D,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAAC;CACxC"}
@@ -0,0 +1,10 @@
1
+ /** Absolute limits for cache key version 1 (RFC 0013). */
2
+ export const PROTOCOL_CACHE_KEY_LIMITS = {
3
+ minSecretBytes: 32,
4
+ maxPreimageBytes: 1_048_576,
5
+ namespaceTokenBytes: 16,
6
+ entryMacBytes: 32,
7
+ maxStoreKeyBytes: 128,
8
+ minKeyVersion: 1,
9
+ maxKeyVersion: 2_147_483_647,
10
+ };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  export { DEFAULT_CANONICAL_VALUE_LIMITS, ProtocolValueError, decodeCanonicalValue, encodeCanonicalValue, encodeCanonicalValueToString, hashCanonicalValue, validateCanonicalValue, } from './values/index.js';
2
2
  export type { ArrayTaggedValue, BytesTaggedValue, CanonicalValue, CanonicalValueLimits, CanonicalValueOptions, DateTaggedValue, DatetimeTaggedValue, DecimalTaggedValue, EnumTaggedValue, IntegerTaggedValue, MapTaggedValue, ProtocolValueErrorCode, TaggedValue, TupleTaggedValue, UuidTaggedValue, } from './values/index.js';
3
3
  export { PROTOCOL_IDENTIFIER_LIMITS, ProtocolIdentifierError, isProtocolIdentifier, isProtocolQualifiedIdentifier, joinProtocolQualifiedIdentifier, parseProtocolIdentifier, parseProtocolQualifiedIdentifier, splitProtocolQualifiedIdentifier, } from './identifiers/index.js';
4
+ export { PROTOCOL_CACHE_KEY_LIMITS, ProtocolCacheKeyError, deriveProtocolCacheKey, deriveProtocolCacheNamespaceToken, } from './cache-keys/index.js';
4
5
  export { DEFAULT_PROTOCOL_EXPRESSION_LIMITS, ProtocolExpressionError, validateProtocolExpression, validateProtocolSemanticQuery, } from './expressions/index.js';
5
6
  export { DEFAULT_PROTOCOL_SCHEMA_LIMITS, DEFAULT_PROTOCOL_SCHEMA_VALUE_LIMITS, ProtocolSchemaError, ProtocolSchemaValueError, applyProtocolSchemaValue, createProtocolSchemaValueParser, resolveProtocolSchemaValueLimits, validateProtocolSchema, } from './schemas/index.js';
6
7
  export type { ProtocolSchema, ProtocolSchemaErrorCode, ProtocolSchemaLimits, ProtocolSchemaOptions, ProtocolSchemaValueLimits, ProtocolSchemaValueOptions, ProtocolSchemaValueParser, } from './schemas/index.js';
7
8
  export type { ProtocolAggregation, ProtocolBinaryOperator, ProtocolComparisonOperator, ProtocolDatasetQuery, ProtocolExpression, ProtocolExpressionErrorCode, ProtocolExpressionLimits, ProtocolExpressionOptions, ProtocolFunctionName, ProtocolMetricQuery, ProtocolOrderBy, ProtocolSemanticQuery, ProtocolTimeGrain, } from './expressions/index.js';
9
+ export type { DeriveProtocolCacheKeyOptions, ProtocolCacheKeyErrorCode, ProtocolCacheKeyNamespace, } from './cache-keys/index.js';
8
10
  export type { ProtocolIdentifier, ProtocolIdentifierErrorCode, ProtocolQualifiedIdentifier, } from './identifiers/index.js';
9
11
  export { DEFAULT_PROTOCOL_QUERY_IMPLEMENTATION_LIMITS, ProtocolQueryImplementationError, validateProtocolQueryImplementation, validateProtocolSqlExpression, } from './query-implementations/index.js';
10
12
  export { DEFAULT_PROTOCOL_QUERY_EVENT_LIMITS, ProtocolQueryDiagnosticsError, ProtocolQueryEventError, validateProtocolQueryDiagnostics, validateProtocolQueryEvent, } from './events/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { DEFAULT_CANONICAL_VALUE_LIMITS, ProtocolValueError, decodeCanonicalValue, encodeCanonicalValue, encodeCanonicalValueToString, hashCanonicalValue, validateCanonicalValue, } from './values/index.js';
2
2
  export { PROTOCOL_IDENTIFIER_LIMITS, ProtocolIdentifierError, isProtocolIdentifier, isProtocolQualifiedIdentifier, joinProtocolQualifiedIdentifier, parseProtocolIdentifier, parseProtocolQualifiedIdentifier, splitProtocolQualifiedIdentifier, } from './identifiers/index.js';
3
+ export { PROTOCOL_CACHE_KEY_LIMITS, ProtocolCacheKeyError, deriveProtocolCacheKey, deriveProtocolCacheNamespaceToken, } from './cache-keys/index.js';
3
4
  export { DEFAULT_PROTOCOL_EXPRESSION_LIMITS, ProtocolExpressionError, validateProtocolExpression, validateProtocolSemanticQuery, } from './expressions/index.js';
4
5
  export { DEFAULT_PROTOCOL_SCHEMA_LIMITS, DEFAULT_PROTOCOL_SCHEMA_VALUE_LIMITS, ProtocolSchemaError, ProtocolSchemaValueError, applyProtocolSchemaValue, createProtocolSchemaValueParser, resolveProtocolSchemaValueLimits, validateProtocolSchema, } from './schemas/index.js';
5
6
  export { DEFAULT_PROTOCOL_QUERY_IMPLEMENTATION_LIMITS, ProtocolQueryImplementationError, validateProtocolQueryImplementation, validateProtocolSqlExpression, } from './query-implementations/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/values/validate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EACd,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AA0VpB,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,qBAA0B,GAClC,cAAc,CAOhB;AAYD,kFAAkF;AAClF,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,QAAQ,CAAC,oBAAoB,CAAC,EACtC,sBAAsB,CAAC,EAAE,MAAM,GAC9B,cAAc,CAGhB;AAED,6EAA6E;AAC7E,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,QAAQ,CAAC,oBAAoB,CAAC,EACtC,sBAAsB,CAAC,EAAE,MAAM,GAC9B,cAAc,CAEhB"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/values/validate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EACd,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAqWpB,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,qBAA0B,GAClC,cAAc,CAOhB;AAYD,kFAAkF;AAClF,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,QAAQ,CAAC,oBAAoB,CAAC,EACtC,sBAAsB,CAAC,EAAE,MAAM,GAC9B,cAAc,CAGhB;AAED,6EAA6E;AAC7E,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,QAAQ,CAAC,oBAAoB,CAAC,EACtC,sBAAsB,CAAC,EAAE,MAAM,GAC9B,cAAc,CAEhB"}
@@ -40,10 +40,15 @@ function requireMetadataInteger(value, path) {
40
40
  }
41
41
  return value;
42
42
  }
43
+ // Tab, line feed, and carriage return are permitted so authored prose can
44
+ // span lines; JCS escapes them deterministically. Every other C0 control, DEL,
45
+ // and the C1 range stay forbidden.
46
+ const ALLOWED_CONTROL_CHARACTERS = new Set([0x09, 0x0a, 0x0d]);
43
47
  function validateUnicode(value, path, maxBytes) {
44
48
  for (let index = 0; index < value.length; index += 1) {
45
49
  const code = value.charCodeAt(index);
46
- if (code <= 0x1f || (code >= 0x7f && code <= 0x9f)) {
50
+ if ((code <= 0x1f && !ALLOWED_CONTROL_CHARACTERS.has(code))
51
+ || (code >= 0x7f && code <= 0x9f)) {
47
52
  valueError('HQ_VALUE_CONTROL_CHARACTER', path);
48
53
  }
49
54
  if (code >= 0xd800 && code <= 0xdbff) {
@@ -139,7 +144,10 @@ function validateDatetimeTag(tag, path) {
139
144
  }
140
145
  const timezone = requireString(tag.timezone, `${path}.timezone`);
141
146
  validateUnicode(timezone, `${path}.timezone`, 64);
142
- if (timezone !== 'UTC' && !/^[A-Za-z_]+(?:\/[A-Za-z0-9_+-]+)+$/.test(timezone)) {
147
+ // Single-component identifiers are valid: UTC, EST, GMT, CET, and MST7MDT
148
+ // are all real tzdb entries. Existence is checked at deployment time
149
+ // against the server's system.time_zones, not here.
150
+ if (!/^[A-Za-z][A-Za-z0-9_+-]*(?:\/[A-Za-z0-9_+-]+)*$/.test(timezone)) {
143
151
  valueError('HQ_VALUE_INVALID_FORMAT', `${path}.timezone`);
144
152
  }
145
153
  const value = requireString(tag.value, `${path}.value`);
package/package.json CHANGED
@@ -1,7 +1,15 @@
1
1
  {
2
2
  "name": "@hypequery/protocol",
3
- "version": "0.10.1",
4
- "description": "Public contracts and TypeScript reference implementation for portable Hypequery artifacts",
3
+ "version": "0.11.0",
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
  },