@hypequery/protocol 0.10.2 → 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.
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypequery/protocol",
3
- "version": "0.10.2",
3
+ "version": "0.11.0",
4
4
  "description": "Portable analytics contracts and TypeScript reference implementation for Hypequery",
5
5
  "keywords": [
6
6
  "hypequery",