@hypequery/protocol-conformance 0.15.1 → 0.16.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
@@ -85,3 +85,11 @@ fixture family or adding an implementation.
85
85
  ## License
86
86
 
87
87
  Apache-2.0.
88
+
89
+ ## Client contract v1
90
+
91
+ Both protocol adapters announce `client-contract-v1`. The runner resolves the
92
+ family's compact success references and negative JSON Pointer patches before
93
+ sending complete cases over NDJSON. It checks canonical identities, normalized
94
+ HTTP/MCP output and exact safe rejection paths/messages, including boundary
95
+ inputs. Fixture composition neither validates nor repairs runtime artifacts.
@@ -0,0 +1,5 @@
1
+ import type { FixtureRole, HandlerResult } from '../types.js';
2
+ type Data = Record<string, unknown>;
3
+ export declare function handleClientContract(role: FixtureRole, c: Data): HandlerResult;
4
+ export {};
5
+ //# sourceMappingURL=client-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-contract.d.ts","sourceRoot":"","sources":["../../src/adapters/client-contract.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE9D,KAAK,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACpC,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,EAAE,IAAI,GAAG,aAAa,CAqB9E"}
@@ -0,0 +1,29 @@
1
+ import { decodeProtocolClientContract, prepareProtocolClientContract, validateProtocolClientContract, validateProtocolClientQuery, } from '@hypequery/protocol';
2
+ import { clientContractTargets } from './utils/client-contract-targets.js';
3
+ export function handleClientContract(role, c) {
4
+ if (c.mode === 'decode') {
5
+ decodeProtocolClientContract(c.value);
6
+ return { ok: true };
7
+ }
8
+ if (c.mode === 'query') {
9
+ const value = c.value;
10
+ const result = validateProtocolClientQuery(value.contract, value.endpoint, value.input, value.transport);
11
+ return { ok: true, output: { body: result.body, selectedMeasures: result.selectedMeasures, effectiveLimit: result.effectiveLimit } };
12
+ }
13
+ if (c.mode === 'relation') {
14
+ const value = c.value;
15
+ if (value.contract)
16
+ return { ok: true, output: clientContractTargets(validateProtocolClientContract(value.contract)) };
17
+ const left = validateProtocolClientContract(value.left), right = validateProtocolClientContract(value.right);
18
+ return { ok: true, output: {
19
+ sameEndpointSchemas: left.endpoints.filter(e => right.endpoints.some(r => r.id === e.id && r.schemaHash === e.schemaHash)).map(e => e.id),
20
+ sameContentHash: left.contentHash === right.contentHash,
21
+ } };
22
+ }
23
+ const contract = validateProtocolClientContract(c.value);
24
+ if (role !== 'identity')
25
+ return { ok: true };
26
+ const { contentHash: _hash, endpoints, ...root } = contract;
27
+ const prepared = prepareProtocolClientContract({ ...root, endpoints: endpoints.map(({ schemaHash: _schemaHash, ...endpoint }) => endpoint) });
28
+ return { ok: true, output: { canonical: prepared.canonical, sha256: prepared.contentHash } };
29
+ }
@@ -9,7 +9,7 @@ export declare const REFERENCE_HOSTILE_OBJECT_SUITE: {
9
9
  readonly count: 7;
10
10
  readonly mechanisms: readonly ["getter", "toJSON", "proxy", "custom-prototype", "symbol-key", "sparse-array", "cycle"];
11
11
  };
12
- export declare const REFERENCE_FAMILIES: readonly ["cache-keys-v1", "cache-preimages-v1", "tagged-values-v1", "identifiers-v1", "expressions-v1", "expressions-v2", "query-schemas-v1", "query-implementations-v1", "query-events-v1", "query-diagnostics-v1", "deployments-v2", "deployments-v3", "deployment-bundles-v1", "deployment-releases-v1", "semantic-invocations-v1", "semantic-invocations-v2"];
12
+ export declare const REFERENCE_FAMILIES: readonly ["client-contract-v1", "cache-keys-v1", "cache-preimages-v1", "tagged-values-v1", "identifiers-v1", "expressions-v1", "expressions-v2", "query-schemas-v1", "query-implementations-v1", "query-events-v1", "query-diagnostics-v1", "deployments-v2", "deployments-v3", "deployment-bundles-v1", "deployment-releases-v1", "semantic-invocations-v1", "semantic-invocations-v2"];
13
13
  export declare function referenceHandle(family: string, role: FixtureRole, c: Case, section?: string): HandlerResult;
14
14
  export {};
15
15
  //# sourceMappingURL=reference.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reference.d.ts","sourceRoot":"","sources":["../../src/adapters/reference.ts"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAc9D,KAAK,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA0BpC;;;;GAIG;AACH,eAAO,MAAM,8BAA8B;;;CAWjC,CAAC;AAEX,eAAO,MAAM,kBAAkB,oWAiBrB,CAAC;AAEX,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,WAAW,EACjB,CAAC,EAAE,IAAI,EACP,OAAO,CAAC,EAAE,MAAM,GACf,aAAa,CA8Cf"}
1
+ {"version":3,"file":"reference.d.ts","sourceRoot":"","sources":["../../src/adapters/reference.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAc9D,KAAK,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA6BpC;;;;GAIG;AACH,eAAO,MAAM,8BAA8B;;;CAWjC,CAAC;AAEX,eAAO,MAAM,kBAAkB,0XAkBrB,CAAC;AAEX,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,WAAW,EACjB,CAAC,EAAE,IAAI,EACP,OAAO,CAAC,EAAE,MAAM,GACf,aAAa,CAgDf"}
@@ -1,3 +1,4 @@
1
+ import { handleClientContract } from './client-contract.js';
1
2
  // The TypeScript reference adapter. Maps each family/role/case to the public
2
3
  // root export of @hypequery/protocol and returns a wire result. Every operation
3
4
  // goes through the package's supported surface — this is exactly what a second
@@ -8,8 +9,10 @@ const STABLE_CODE = /^HQ_[A-Z0-9_]+$/;
8
9
  /** Turns a thrown protocol error into a wire rejection; rethrows anything else. */
9
10
  function mapError(error) {
10
11
  const code = error?.code;
11
- if (typeof code === 'string' && STABLE_CODE.test(code))
12
- return { ok: false, code };
12
+ if (typeof code === 'string' && STABLE_CODE.test(code)) {
13
+ const safe = error;
14
+ return { ok: false, code, ...(code.startsWith('HQ_CLIENT_') ? { output: { path: safe.path, message: safe.message } } : {}) };
15
+ }
13
16
  throw error;
14
17
  }
15
18
  function attempt(fn) {
@@ -43,6 +46,7 @@ export const REFERENCE_HOSTILE_OBJECT_SUITE = {
43
46
  ],
44
47
  };
45
48
  export const REFERENCE_FAMILIES = [
49
+ 'client-contract-v1',
46
50
  'cache-keys-v1',
47
51
  'cache-preimages-v1',
48
52
  'tagged-values-v1',
@@ -62,6 +66,8 @@ export const REFERENCE_FAMILIES = [
62
66
  ];
63
67
  export function referenceHandle(family, role, c, section) {
64
68
  switch (family) {
69
+ case 'client-contract-v1':
70
+ return attempt(() => handleClientContract(role, c));
65
71
  case 'tagged-values-v1':
66
72
  return handleTaggedValue(role, c);
67
73
  case 'identifiers-v1':
@@ -0,0 +1,4 @@
1
+ import type { ProtocolClientContract } from '@hypequery/protocol';
2
+ /** Callable targets, preserving normalized endpoint order without inventing dataset access. */
3
+ export declare function clientContractTargets(contract: ProtocolClientContract): Record<string, unknown>;
4
+ //# sourceMappingURL=client-contract-targets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-contract-targets.d.ts","sourceRoot":"","sources":["../../../src/adapters/utils/client-contract-targets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAElE,+FAA+F;AAC/F,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAK/F"}
@@ -0,0 +1,7 @@
1
+ /** Callable targets, preserving normalized endpoint order without inventing dataset access. */
2
+ export function clientContractTargets(contract) {
3
+ return {
4
+ queryDatasetTargets: contract.endpoints.filter(e => e.kind === 'dataset').map(e => e.id),
5
+ queryMetricTargets: contract.endpoints.filter(e => e.kind === 'metric').map(e => ({ dataset: e.dataset, metric: e.id })),
6
+ };
7
+ }
@@ -0,0 +1,3 @@
1
+ import type { EnumeratedCase } from './types.js';
2
+ export declare function composeClientContractCases(cases: EnumeratedCase[]): EnumeratedCase[];
3
+ //# sourceMappingURL=client-contract-fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-contract-fixtures.d.ts","sourceRoot":"","sources":["../src/client-contract-fixtures.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAqBjD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAwBpF"}
@@ -0,0 +1,61 @@
1
+ function base(values, id) {
2
+ if (typeof id !== 'string' || !values.has(id))
3
+ throw new Error('Unknown client fixture base');
4
+ return structuredClone(values.get(id));
5
+ }
6
+ function patch(value, changes) {
7
+ for (const change of changes) {
8
+ if (!change.path.startsWith('/'))
9
+ throw new Error('Invalid client fixture pointer');
10
+ const parts = change.path.slice(1).split('/').map(part => part.replaceAll('~1', '/').replaceAll('~0', '~'));
11
+ let target = value;
12
+ for (const part of parts.slice(0, -1)) {
13
+ if (!Object.hasOwn(target, part))
14
+ throw new Error('Missing client fixture parent');
15
+ target = target[part];
16
+ }
17
+ const key = parts.at(-1);
18
+ if (key === '-' && Array.isArray(target))
19
+ target.push(structuredClone(change.value));
20
+ else
21
+ Object.defineProperty(target, key, { value: structuredClone(change.value), enumerable: true, writable: true, configurable: true });
22
+ }
23
+ return value;
24
+ }
25
+ export function composeClientContractCases(cases) {
26
+ const values = new Map(cases.filter(item => item.role === 'success' && item.case.mode === undefined)
27
+ .map(item => [item.id, item.case.value]));
28
+ return cases.map(item => {
29
+ const c = item.case;
30
+ if (c.base !== undefined)
31
+ return { ...item, case: { ...c, value: patch(base(values, c.base), c.set) } };
32
+ if (c.mode === 'query')
33
+ return { ...item, case: { ...c, value: { contract: base(values, c.contract), endpoint: c.endpoint, input: c.input, transport: c.transport ?? 'http' } } };
34
+ if (c.mode === 'relation')
35
+ return { ...item, case: { ...c, value: c.left === undefined ? { contract: base(values, c.contract) }
36
+ : { left: base(values, c.left), right: base(values, c.right) } } };
37
+ if (c.mode === 'boundary') {
38
+ const generator = c.generator;
39
+ let value;
40
+ if (generator.type === 'endpoints')
41
+ value = { ...base(values, 'build'), endpoints: Array.from({ length: generator.count }, () => ({})) };
42
+ else if (generator.type === 'descriptors') {
43
+ const dimensions = Object.fromEntries(Array.from({ length: 256 }, (_, i) => ['field' + i, null]));
44
+ value = { ...base(values, 'build'), endpoints: Array.from({ length: generator.count }, () => ({ dimensions })) };
45
+ }
46
+ else if (generator.type === 'members')
47
+ value = Array.from({ length: generator.count }, () => null);
48
+ else if (generator.type === 'bytes')
49
+ value = { extra: 'x'.repeat(generator.count - 12) };
50
+ else if (generator.type === 'depth') {
51
+ value = 0;
52
+ for (let i = 1; i < generator.count; i++)
53
+ value = [value];
54
+ }
55
+ else
56
+ throw new Error('Unknown client fixture generator');
57
+ return { ...item, case: { ...c, value } };
58
+ }
59
+ return item;
60
+ });
61
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../src/compare.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAwC7E,wBAAgB,WAAW,CAAC,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,GAAG,WAAW,CA2ElF"}
1
+ {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../src/compare.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAwC7E,wBAAgB,WAAW,CAAC,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,GAAG,WAAW,CA+ElF"}
package/dist/compare.js CHANGED
@@ -76,6 +76,11 @@ export function compareCase(ec, result) {
76
76
  if (code !== ec.case.error) {
77
77
  return outcome(ec, 'fail', { expected: String(ec.case.error), actual: String(code) });
78
78
  }
79
+ if (ec.family === 'client-contract-v1') {
80
+ const o = output(result);
81
+ if (o.path !== ec.case.path || o.message !== ec.case.message)
82
+ return outcome(ec, 'fail', { message: 'client failure path/message mismatch' });
83
+ }
79
84
  return outcome(ec, 'pass');
80
85
  }
81
86
  case 'non-portable': {
@@ -108,6 +113,9 @@ export function compareCase(ec, result) {
108
113
  }
109
114
  }
110
115
  function compareSuccessOutput(ec, o) {
116
+ if (ec.family === 'client-contract-v1' && ec.case.expected !== undefined && !deepEqual(o, ec.case.expected)) {
117
+ return outcome(ec, 'fail', { message: 'client normalized output mismatch' });
118
+ }
111
119
  if (ec.family === 'tagged-values-v1') {
112
120
  if (o.canonicalHex !== ec.case.canonicalHex) {
113
121
  return outcome(ec, 'fail', { expected: 'canonicalHex', actual: 'mismatch' });
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,mBAAmB,EACnB,cAAc,EAGf,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,UAAU,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;AAM3D,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAiB1E;AAgDD,sEAAsE;AACtE,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,UAAU,GACnB,cAAc,EAAE,CAmBlB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,UAAU,GACnB,cAAc,EAAE,CA0BlB;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,UAAU,GACnB,cAAc,EAAE,CAElB"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,mBAAmB,EACnB,cAAc,EAGf,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,UAAU,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;AAM3D,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAiB1E;AAgDD,sEAAsE;AACtE,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,UAAU,GACnB,cAAc,EAAE,CAoBlB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,UAAU,GACnB,cAAc,EAAE,CA0BlB;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,UAAU,GACnB,cAAc,EAAE,CAElB"}
package/dist/manifest.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { composeClientContractCases } from './client-contract-fixtures.js';
1
2
  function decodePointerToken(token) {
2
3
  return token.replace(/~1/g, '/').replace(/~0/g, '~');
3
4
  }
@@ -63,7 +64,8 @@ function enumerateFile(family, file, loadJson) {
63
64
  /** Cases derived from the family fixture files, in manifest order. */
64
65
  export function enumerateFamilyCases(manifest, loadJson) {
65
66
  return manifest.families.flatMap((family) => {
66
- const cases = family.files.flatMap((file) => enumerateFile(family, file, loadJson));
67
+ const rawCases = family.files.flatMap((file) => enumerateFile(family, file, loadJson));
68
+ const cases = family.name === 'client-contract-v1' ? composeClientContractCases(rawCases) : rawCases;
67
69
  // An identity case pins the canonical bytes and hash of the success case
68
70
  // that shares its id. Attach that success value so an adapter can derive
69
71
  // the canonical form without loading the success file itself.
@@ -0,0 +1,29 @@
1
+ # Semantic client contract v1
2
+
3
+ Normative vectors for [RFC 0018](../../rfc/0018-semantic-client-contract.md).
4
+ The family is required by the TypeScript/Python conformance gates.
5
+
6
+ - success.json pins build, metric-only, empty and narrowed signed contracts.
7
+ - identities.json pins canonical complete documents and both identity domains.
8
+ - rejections.json pins malformed descriptors, identity failures, safe messages
9
+ and JSON Pointer paths, plus exact size/depth/count boundaries and text decoding.
10
+ - queries.json pins accepted strict HTTP/MCP input and complete normalized output.
11
+ - query-rejections.json pins invalid selections, types, nulls, operators and budgets.
12
+ - relations.json pins cross-view schema stability and query-target visibility.
13
+ - timezone-names.txt is the case-sensitive portable timezone registry. Runtime
14
+ package registries must match it; aliases are retained verbatim in identities.
15
+
16
+ For compact review, cases can reference a success ID via base/contract/left/right.
17
+ The conformance runner composes these references before dispatching the case;
18
+ implementations receive complete inputs and need no fixture-file access. A set
19
+ array adds/replaces fields in a deep-copied base using RFC 6901 JSON Pointers;
20
+ - appends an array element. Parents must exist. It never recomputes identities:
21
+ malformed shape validation must precede identity validation. The runner's boundary
22
+ generators expand only synthetic byte, depth, endpoint, descriptor and array-member counts.
23
+ Identity cases reuse the matching success value, as other fixture families do.
24
+
25
+ The query compiler preserves fixed MCP tool names; empty target sets have required
26
+ never-valued routing fields. JSON Schema pins structural types; call-time semantic
27
+ validation additionally enforces selected fields, uniqueness, portable timezones
28
+ and combined predicate budgets. No artifact identity authenticates a principal or
29
+ certifies an endpoint's execution implementation.
@@ -0,0 +1,36 @@
1
+ [
2
+ {
3
+ "id": "build",
4
+ "canonical": "{\"contentHash\":\"f1a7071cfac3615ef809ce91f14c59b1ee68807b099afe1fe9ead06db1f5ecd1\",\"endpoints\":[{\"dataset\":\"countries\",\"defaultMeasures\":[],\"dimensions\":{\"country\":{\"groupable\":true,\"nullable\":false,\"orderable\":true,\"type\":\"string\",\"wireType\":\"string\"}},\"filters\":{},\"id\":\"countries\",\"kind\":\"dataset\",\"limits\":{\"defaultLimit\":100,\"maxDimensions\":50,\"maxFilterValues\":1000,\"maxLimit\":1000,\"maxMeasures\":0,\"maxOffset\":10000,\"maxOrderBy\":50,\"maxPredicates\":0},\"measures\":{},\"metadata\":false,\"path\":\"/api/v1/datasets/countries/query\",\"schemaHash\":\"9a5f50cd7564f283d2f0ee8f3f1cdc27731683c939e176bd25919a83d28d94ca\",\"time\":null},{\"dataset\":\"orders\",\"defaultMeasures\":[\"revenue\",\"orderCount\",\"latestLabel\"],\"dimensions\":{\"amount\":{\"groupable\":true,\"nullable\":\"unknown\",\"orderable\":true,\"type\":\"number\",\"wireType\":\"number-or-string\"},\"country\":{\"groupable\":true,\"nullable\":false,\"orderable\":true,\"type\":\"string\",\"wireType\":\"string\"},\"customer.active\":{\"groupable\":false,\"nullable\":true,\"orderable\":false,\"type\":\"boolean\",\"wireType\":\"boolean\"},\"customer.tier\":{\"groupable\":true,\"nullable\":true,\"orderable\":true,\"type\":\"string\",\"wireType\":\"string\"}},\"filters\":{\"amount\":{\"operators\":[\"between\",\"eq\",\"gt\",\"gte\",\"in\",\"lt\",\"lte\",\"neq\",\"notIn\"],\"type\":\"number\"},\"country\":{\"operators\":[\"eq\",\"in\",\"like\",\"neq\",\"notIn\"],\"type\":\"string\"},\"customer.active\":{\"operators\":[\"eq\",\"neq\"],\"type\":\"boolean\"},\"customer.tier\":{\"operators\":[\"eq\",\"in\"],\"type\":\"string\"},\"paidStatus\":{\"operators\":[\"eq\"],\"type\":\"string\"}},\"id\":\"orders\",\"kind\":\"dataset\",\"limits\":{\"defaultLimit\":100,\"maxDimensions\":50,\"maxFilterValues\":1000,\"maxLimit\":1000,\"maxMeasures\":50,\"maxOffset\":10000,\"maxOrderBy\":50,\"maxPredicates\":100},\"measures\":{\"averageRevenue\":{\"having\":true,\"nullable\":true,\"wireType\":\"string\"},\"customer.unique\":{\"having\":true,\"nullable\":true,\"wireType\":\"string\"},\"latestLabel\":{\"having\":false,\"nullable\":true,\"wireType\":\"string\"},\"orderCount\":{\"having\":true,\"nullable\":true,\"wireType\":\"string\"},\"revenue\":{\"having\":true,\"nullable\":true,\"wireType\":\"string\"}},\"metadata\":true,\"path\":\"/api/v1/datasets/orders/query\",\"schemaHash\":\"88802c0d4798fd0758b7e7697f582b034afada967bfe0fdcb1cafd957144b1f3\",\"time\":{\"defaultTimezone\":\"UTC\",\"grains\":[\"day\",\"hour\",\"month\",\"week\"],\"periodFormat\":\"calendar-date-or-local-datetime-v1\",\"timezone\":true}},{\"dataset\":\"orders\",\"dimensions\":{\"country\":{\"groupable\":true,\"nullable\":false,\"orderable\":true,\"type\":\"string\",\"wireType\":\"string\"},\"customer.active\":{\"groupable\":false,\"nullable\":true,\"orderable\":false,\"type\":\"boolean\",\"wireType\":\"boolean\"},\"customer.tier\":{\"groupable\":true,\"nullable\":true,\"orderable\":true,\"type\":\"string\",\"wireType\":\"string\"}},\"filters\":{\"amount\":{\"operators\":[\"between\",\"eq\",\"gt\",\"gte\",\"in\",\"lt\",\"lte\",\"neq\",\"notIn\"],\"type\":\"number\"},\"country\":{\"operators\":[\"eq\",\"in\",\"like\",\"neq\",\"notIn\"],\"type\":\"string\"},\"customer.active\":{\"operators\":[\"eq\",\"neq\"],\"type\":\"boolean\"},\"customer.tier\":{\"operators\":[\"eq\",\"in\"],\"type\":\"string\"},\"paidStatus\":{\"operators\":[\"eq\"],\"type\":\"string\"}},\"id\":\"ordersRevenue\",\"kind\":\"metric\",\"limits\":{\"defaultLimit\":20,\"maxDimensions\":50,\"maxFilterValues\":1000,\"maxLimit\":20,\"maxMeasures\":1,\"maxOffset\":10000,\"maxOrderBy\":50,\"maxPredicates\":100},\"measures\":{\"revenue\":{\"having\":false,\"nullable\":true,\"wireType\":\"string\"}},\"metadata\":true,\"path\":\"/api/v1/metrics/revenue\",\"schemaHash\":\"e282fb74777e65328d87cfe5caa0349104630cd88137b1842289e0c83676a98d\",\"time\":{\"defaultTimezone\":\"UTC\",\"grains\":[\"day\",\"hour\",\"month\",\"week\"],\"periodFormat\":\"calendar-date-or-local-datetime-v1\",\"timezone\":true}}],\"kind\":\"hypequery-client-contract\",\"version\":1,\"view\":\"build\"}",
5
+ "endpointHashes": {
6
+ "countries": "9a5f50cd7564f283d2f0ee8f3f1cdc27731683c939e176bd25919a83d28d94ca",
7
+ "orders": "88802c0d4798fd0758b7e7697f582b034afada967bfe0fdcb1cafd957144b1f3",
8
+ "ordersRevenue": "e282fb74777e65328d87cfe5caa0349104630cd88137b1842289e0c83676a98d"
9
+ },
10
+ "sha256": "f1a7071cfac3615ef809ce91f14c59b1ee68807b099afe1fe9ead06db1f5ecd1"
11
+ },
12
+ {
13
+ "id": "metric-only",
14
+ "canonical": "{\"contentHash\":\"3dc2252343be12ec4d2434a24dd86e4a4ec5aede037d646c44d14179d9672e81\",\"endpoints\":[{\"dataset\":\"orders\",\"dimensions\":{\"country\":{\"groupable\":true,\"nullable\":false,\"orderable\":true,\"type\":\"string\",\"wireType\":\"string\"},\"customer.active\":{\"groupable\":false,\"nullable\":true,\"orderable\":false,\"type\":\"boolean\",\"wireType\":\"boolean\"},\"customer.tier\":{\"groupable\":true,\"nullable\":true,\"orderable\":true,\"type\":\"string\",\"wireType\":\"string\"}},\"filters\":{\"amount\":{\"operators\":[\"between\",\"eq\",\"gt\",\"gte\",\"in\",\"lt\",\"lte\",\"neq\",\"notIn\"],\"type\":\"number\"},\"country\":{\"operators\":[\"eq\",\"in\",\"like\",\"neq\",\"notIn\"],\"type\":\"string\"},\"customer.active\":{\"operators\":[\"eq\",\"neq\"],\"type\":\"boolean\"},\"customer.tier\":{\"operators\":[\"eq\",\"in\"],\"type\":\"string\"},\"paidStatus\":{\"operators\":[\"eq\"],\"type\":\"string\"}},\"id\":\"ordersRevenue\",\"kind\":\"metric\",\"limits\":{\"defaultLimit\":20,\"maxDimensions\":50,\"maxFilterValues\":1000,\"maxLimit\":20,\"maxMeasures\":1,\"maxOffset\":10000,\"maxOrderBy\":50,\"maxPredicates\":100},\"measures\":{\"revenue\":{\"having\":false,\"nullable\":true,\"wireType\":\"string\"}},\"metadata\":true,\"path\":\"/api/v1/metrics/revenue\",\"schemaHash\":\"e282fb74777e65328d87cfe5caa0349104630cd88137b1842289e0c83676a98d\",\"time\":{\"defaultTimezone\":\"UTC\",\"grains\":[\"day\",\"hour\",\"month\",\"week\"],\"periodFormat\":\"calendar-date-or-local-datetime-v1\",\"timezone\":true}}],\"kind\":\"hypequery-client-contract\",\"version\":1,\"view\":\"authorized\"}",
15
+ "endpointHashes": {
16
+ "ordersRevenue": "e282fb74777e65328d87cfe5caa0349104630cd88137b1842289e0c83676a98d"
17
+ },
18
+ "sha256": "3dc2252343be12ec4d2434a24dd86e4a4ec5aede037d646c44d14179d9672e81"
19
+ },
20
+ {
21
+ "id": "empty",
22
+ "canonical": "{\"contentHash\":\"7a31724c56ffb78f0e8e2ab022101c422671f6bb4291ae6c09b1cd634180507e\",\"endpoints\":[],\"kind\":\"hypequery-client-contract\",\"version\":1,\"view\":\"authorized\"}",
23
+ "endpointHashes": {},
24
+ "sha256": "7a31724c56ffb78f0e8e2ab022101c422671f6bb4291ae6c09b1cd634180507e"
25
+ },
26
+ {
27
+ "id": "narrowed",
28
+ "canonical": "{\"contentHash\":\"c350b9b6e0351acbbac7509bf5d889afa343b9fb5048d826265d8e9f5ef895d8\",\"endpoints\":[{\"dataset\":\"countries\",\"defaultMeasures\":[],\"dimensions\":{\"country\":{\"groupable\":true,\"nullable\":false,\"orderable\":true,\"type\":\"string\",\"wireType\":\"string\"}},\"filters\":{},\"id\":\"countries\",\"kind\":\"dataset\",\"limits\":{\"defaultLimit\":10,\"maxDimensions\":50,\"maxFilterValues\":1000,\"maxLimit\":10,\"maxMeasures\":0,\"maxOffset\":10000,\"maxOrderBy\":50,\"maxPredicates\":0},\"measures\":{},\"metadata\":false,\"path\":\"/api/v1/datasets/countries/query\",\"schemaHash\":\"9a5f50cd7564f283d2f0ee8f3f1cdc27731683c939e176bd25919a83d28d94ca\",\"time\":null},{\"dataset\":\"orders\",\"defaultMeasures\":[\"revenue\",\"orderCount\",\"latestLabel\"],\"dimensions\":{\"amount\":{\"groupable\":true,\"nullable\":\"unknown\",\"orderable\":true,\"type\":\"number\",\"wireType\":\"number-or-string\"},\"country\":{\"groupable\":true,\"nullable\":false,\"orderable\":true,\"type\":\"string\",\"wireType\":\"string\"},\"customer.active\":{\"groupable\":false,\"nullable\":true,\"orderable\":false,\"type\":\"boolean\",\"wireType\":\"boolean\"},\"customer.tier\":{\"groupable\":true,\"nullable\":true,\"orderable\":true,\"type\":\"string\",\"wireType\":\"string\"}},\"filters\":{\"amount\":{\"operators\":[\"between\",\"eq\",\"gt\",\"gte\",\"in\",\"lt\",\"lte\",\"neq\",\"notIn\"],\"type\":\"number\"},\"country\":{\"operators\":[\"eq\",\"in\",\"like\",\"neq\",\"notIn\"],\"type\":\"string\"},\"customer.active\":{\"operators\":[\"eq\",\"neq\"],\"type\":\"boolean\"},\"customer.tier\":{\"operators\":[\"eq\",\"in\"],\"type\":\"string\"},\"paidStatus\":{\"operators\":[\"eq\"],\"type\":\"string\"}},\"id\":\"orders\",\"kind\":\"dataset\",\"limits\":{\"defaultLimit\":10,\"maxDimensions\":50,\"maxFilterValues\":1000,\"maxLimit\":10,\"maxMeasures\":50,\"maxOffset\":10000,\"maxOrderBy\":50,\"maxPredicates\":100},\"measures\":{\"averageRevenue\":{\"having\":true,\"nullable\":true,\"wireType\":\"string\"},\"customer.unique\":{\"having\":true,\"nullable\":true,\"wireType\":\"string\"},\"latestLabel\":{\"having\":false,\"nullable\":true,\"wireType\":\"string\"},\"orderCount\":{\"having\":true,\"nullable\":true,\"wireType\":\"string\"},\"revenue\":{\"having\":true,\"nullable\":true,\"wireType\":\"string\"}},\"metadata\":true,\"path\":\"/api/v1/datasets/orders/query\",\"schemaHash\":\"88802c0d4798fd0758b7e7697f582b034afada967bfe0fdcb1cafd957144b1f3\",\"time\":{\"defaultTimezone\":\"UTC\",\"grains\":[\"day\",\"hour\",\"month\",\"week\"],\"periodFormat\":\"calendar-date-or-local-datetime-v1\",\"timezone\":true}},{\"dataset\":\"orders\",\"dimensions\":{\"country\":{\"groupable\":true,\"nullable\":false,\"orderable\":true,\"type\":\"string\",\"wireType\":\"string\"},\"customer.active\":{\"groupable\":false,\"nullable\":true,\"orderable\":false,\"type\":\"boolean\",\"wireType\":\"boolean\"},\"customer.tier\":{\"groupable\":true,\"nullable\":true,\"orderable\":true,\"type\":\"string\",\"wireType\":\"string\"}},\"filters\":{\"amount\":{\"operators\":[\"between\",\"eq\",\"gt\",\"gte\",\"in\",\"lt\",\"lte\",\"neq\",\"notIn\"],\"type\":\"number\"},\"country\":{\"operators\":[\"eq\",\"in\",\"like\",\"neq\",\"notIn\"],\"type\":\"string\"},\"customer.active\":{\"operators\":[\"eq\",\"neq\"],\"type\":\"boolean\"},\"customer.tier\":{\"operators\":[\"eq\",\"in\"],\"type\":\"string\"},\"paidStatus\":{\"operators\":[\"eq\"],\"type\":\"string\"}},\"id\":\"ordersRevenue\",\"kind\":\"metric\",\"limits\":{\"defaultLimit\":10,\"maxDimensions\":50,\"maxFilterValues\":1000,\"maxLimit\":10,\"maxMeasures\":1,\"maxOffset\":10000,\"maxOrderBy\":50,\"maxPredicates\":100},\"measures\":{\"revenue\":{\"having\":false,\"nullable\":true,\"wireType\":\"string\"}},\"metadata\":true,\"path\":\"/api/v1/metrics/revenue\",\"schemaHash\":\"e282fb74777e65328d87cfe5caa0349104630cd88137b1842289e0c83676a98d\",\"time\":{\"defaultTimezone\":\"UTC\",\"grains\":[\"day\",\"hour\",\"month\",\"week\"],\"periodFormat\":\"calendar-date-or-local-datetime-v1\",\"timezone\":true}}],\"kind\":\"hypequery-client-contract\",\"version\":1,\"view\":\"authorized\"}",
29
+ "endpointHashes": {
30
+ "countries": "9a5f50cd7564f283d2f0ee8f3f1cdc27731683c939e176bd25919a83d28d94ca",
31
+ "orders": "88802c0d4798fd0758b7e7697f582b034afada967bfe0fdcb1cafd957144b1f3",
32
+ "ordersRevenue": "e282fb74777e65328d87cfe5caa0349104630cd88137b1842289e0c83676a98d"
33
+ },
34
+ "sha256": "c350b9b6e0351acbbac7509bf5d889afa343b9fb5048d826265d8e9f5ef895d8"
35
+ }
36
+ ]