@hypequery/protocol 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +26 -0
  2. package/dist/bundles/codec.js +2 -2
  3. package/dist/cache-keys/cache-keys.js +2 -2
  4. package/dist/cache-preimages/cache-preimages.js +3 -3
  5. package/dist/client-contract/bounds.d.ts +3 -0
  6. package/dist/client-contract/bounds.d.ts.map +1 -0
  7. package/dist/client-contract/bounds.js +24 -0
  8. package/dist/client-contract/codec.d.ts +4 -0
  9. package/dist/client-contract/codec.d.ts.map +1 -0
  10. package/dist/client-contract/codec.js +8 -0
  11. package/dist/client-contract/decode.d.ts +4 -0
  12. package/dist/client-contract/decode.d.ts.map +1 -0
  13. package/dist/client-contract/decode.js +100 -0
  14. package/dist/client-contract/errors.d.ts +21 -0
  15. package/dist/client-contract/errors.d.ts.map +1 -0
  16. package/dist/client-contract/errors.js +26 -0
  17. package/dist/client-contract/index.d.ts +8 -0
  18. package/dist/client-contract/index.d.ts.map +1 -0
  19. package/dist/client-contract/index.js +4 -0
  20. package/dist/client-contract/nodes.d.ts +8 -0
  21. package/dist/client-contract/nodes.d.ts.map +1 -0
  22. package/dist/client-contract/nodes.js +68 -0
  23. package/dist/client-contract/primitives.d.ts +15 -0
  24. package/dist/client-contract/primitives.d.ts.map +1 -0
  25. package/dist/client-contract/primitives.js +74 -0
  26. package/dist/client-contract/query.d.ts +10 -0
  27. package/dist/client-contract/query.d.ts.map +1 -0
  28. package/dist/client-contract/query.js +121 -0
  29. package/dist/client-contract/safe-data.d.ts +6 -0
  30. package/dist/client-contract/safe-data.d.ts.map +1 -0
  31. package/dist/client-contract/safe-data.js +124 -0
  32. package/dist/client-contract/timezone-names.d.ts +2 -0
  33. package/dist/client-contract/timezone-names.d.ts.map +1 -0
  34. package/dist/client-contract/timezone-names.js +600 -0
  35. package/dist/client-contract/types.d.ts +68 -0
  36. package/dist/client-contract/types.d.ts.map +1 -0
  37. package/dist/client-contract/types.js +1 -0
  38. package/dist/client-contract/validate.d.ts +6 -0
  39. package/dist/client-contract/validate.d.ts.map +1 -0
  40. package/dist/client-contract/validate.js +95 -0
  41. package/dist/deployments/codec.js +2 -2
  42. package/dist/deployments/collect.d.ts +22 -0
  43. package/dist/deployments/collect.d.ts.map +1 -0
  44. package/dist/deployments/collect.js +69 -0
  45. package/dist/deployments/index.d.ts +2 -2
  46. package/dist/deployments/index.d.ts.map +1 -1
  47. package/dist/deployments/index.js +1 -1
  48. package/dist/deployments/types.d.ts +20 -1
  49. package/dist/deployments/types.d.ts.map +1 -1
  50. package/dist/deployments/validate.d.ts +22 -1
  51. package/dist/deployments/validate.d.ts.map +1 -1
  52. package/dist/deployments/validate.js +171 -79
  53. package/dist/index.d.ts +5 -2
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +3 -1
  56. package/dist/releases/codec.js +2 -2
  57. package/dist/values/codec.js +2 -2
  58. package/dist/visibility/errors.d.ts +8 -0
  59. package/dist/visibility/errors.d.ts.map +1 -0
  60. package/dist/visibility/errors.js +13 -0
  61. package/dist/visibility/index.d.ts +5 -0
  62. package/dist/visibility/index.d.ts.map +1 -0
  63. package/dist/visibility/index.js +3 -0
  64. package/dist/visibility/resolve.d.ts +30 -0
  65. package/dist/visibility/resolve.d.ts.map +1 -0
  66. package/dist/visibility/resolve.js +108 -0
  67. package/dist/visibility/types.d.ts +32 -0
  68. package/dist/visibility/types.d.ts.map +1 -0
  69. package/dist/visibility/types.js +1 -0
  70. package/dist/visibility/validate.d.ts +11 -0
  71. package/dist/visibility/validate.d.ts.map +1 -0
  72. package/dist/visibility/validate.js +78 -0
  73. package/package.json +3 -3
@@ -1,6 +1,8 @@
1
1
  import { ProtocolExpressionError, validateProtocolExpression } from '../expressions/index.js';
2
2
  import { ProtocolIdentifierError, parseProtocolIdentifier, parseProtocolQualifiedIdentifier, } from '../identifiers/index.js';
3
+ import { ProtocolQueryImplementationError } from '../query-implementations/errors.js';
3
4
  import { validateProtocolSqlExpression } from '../sql-expressions/validate.js';
5
+ import { attempt, collectErrors } from './collect.js';
4
6
  import { deploymentError } from './errors.js';
5
7
  import { expressionReferences, isSegmentPredicate, isSegmentReference } from './segments.js';
6
8
  import { resolveDeploymentLimits } from './limits.js';
@@ -62,15 +64,15 @@ function requireArray(input, path, maxItems) {
62
64
  }
63
65
  return input;
64
66
  }
65
- function exactFields(value, required, optional, path) {
67
+ function exactFields(value, required, optional, path, issues) {
66
68
  const allowed = new Set([...required, ...optional]);
67
69
  for (const key of Object.keys(value)) {
68
70
  if (!allowed.has(key))
69
- deploymentError('HQ_DEPLOYMENT_UNKNOWN_FIELD', `${path}.${key}`);
71
+ attempt(issues, () => deploymentError('HQ_DEPLOYMENT_UNKNOWN_FIELD', `${path}.${key}`));
70
72
  }
71
73
  for (const key of required) {
72
74
  if (!Object.hasOwn(value, key))
73
- deploymentError('HQ_DEPLOYMENT_TYPE', `${path}.${key}`);
75
+ attempt(issues, () => deploymentError('HQ_DEPLOYMENT_TYPE', `${path}.${key}`));
74
76
  }
75
77
  }
76
78
  function freezeRecord(entries) {
@@ -119,28 +121,35 @@ function uniqueStrings(input, path, maxItems, parse) {
119
121
  deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', path);
120
122
  return Object.freeze(result);
121
123
  }
122
- function validateSemanticMetadata(value, result, path, limits) {
124
+ function validateSemanticMetadata(value, result, path, limits, issues) {
123
125
  const parseText = (item, itemPath) => (boundedText(item, itemPath, limits.maxTextBytes));
126
+ // Each metadata field is independent, so each is its own recovery point.
124
127
  for (const key of ['examples', 'synonyms']) {
125
128
  if (value[key] !== undefined) {
126
- result[key] = uniqueStrings(value[key], `${path}.${key}`, limits.maxSemanticMetadataItems, parseText);
129
+ attempt(issues, () => {
130
+ result[key] = uniqueStrings(value[key], `${path}.${key}`, limits.maxSemanticMetadataItems, parseText);
131
+ });
127
132
  }
128
133
  }
129
134
  for (const key of ['format', 'unit', 'timezone']) {
130
- optionalText(value[key], key, result, path, limits);
135
+ attempt(issues, () => optionalText(value[key], key, result, path, limits));
131
136
  }
132
137
  if (value.currency !== undefined) {
133
- const currency = boundedText(value.currency, `${path}.currency`, limits.maxTextBytes);
134
- if (!/^[A-Z]{3}$/.test(currency)) {
135
- deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.currency`);
136
- }
137
- result.currency = currency;
138
+ attempt(issues, () => {
139
+ const currency = boundedText(value.currency, `${path}.currency`, limits.maxTextBytes);
140
+ if (!/^[A-Z]{3}$/.test(currency)) {
141
+ deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.currency`);
142
+ }
143
+ result.currency = currency;
144
+ });
138
145
  }
139
146
  if (value.sensitivity !== undefined) {
140
- if (typeof value.sensitivity !== 'string' || !SENSITIVITIES.has(value.sensitivity)) {
141
- deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.sensitivity`);
142
- }
143
- result.sensitivity = value.sensitivity;
147
+ attempt(issues, () => {
148
+ if (typeof value.sensitivity !== 'string' || !SENSITIVITIES.has(value.sensitivity)) {
149
+ deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.sensitivity`);
150
+ }
151
+ result.sensitivity = value.sensitivity;
152
+ });
144
153
  }
145
154
  }
146
155
  function validateAccess(input, path, limits) {
@@ -230,6 +239,11 @@ function nested(action, path) {
230
239
  if (error instanceof ProtocolExpressionError) {
231
240
  deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', path);
232
241
  }
242
+ // RFC 0006 keeps the SQL envelope's own code; only the location is rebased
243
+ // from the envelope root onto the contract.
244
+ if (error instanceof ProtocolQueryImplementationError) {
245
+ throw new ProtocolQueryImplementationError(error.code, `${path}${error.path.slice(1)}`);
246
+ }
233
247
  throw error;
234
248
  }
235
249
  }
@@ -601,8 +615,12 @@ function validateDefaults(input, path, limits, version) {
601
615
  }
602
616
  return freezeRecord(result);
603
617
  }
604
- function namedItems(input, path, maxItems, validate) {
605
- const items = requireArray(input, path, maxItems).map((value, index) => validate(value, `${path}[${index}]`, index));
618
+ function namedItems(input, path, maxItems, validate, issues) {
619
+ const since = issues?.count ?? 0;
620
+ const items = requireArray(input, path, maxItems)
621
+ .map((value, index) => attempt(issues, () => validate(value, `${path}[${index}]`, index)));
622
+ // Name checks over a partial list would report the failed items again.
623
+ issues?.checkpoint(since);
606
624
  if (new Set(items.map(item => item.name)).size !== items.length) {
607
625
  deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', path);
608
626
  }
@@ -623,7 +641,9 @@ function validateSegment(input, path, limits, dimensions, tenant) {
623
641
  validateSemanticMetadata(value, result, path, limits);
624
642
  return freezeRecord(result);
625
643
  }
626
- function validateDataset(input, path, limits, measureIndices, version = 2) {
644
+ function validateDataset(input, path, limits, measureIndices, version = 2, issues) {
645
+ const since = issues?.count ?? 0;
646
+ const field = (action) => attempt(issues, action);
627
647
  const value = requireRecord(input, path);
628
648
  // Contract 3 renames the allow-list; the old name is then an unknown field.
629
649
  const filtersKey = version === 3 ? 'allowedFilters' : 'filters';
@@ -634,57 +654,73 @@ function validateDataset(input, path, limits, measureIndices, version = 2) {
634
654
  ...SEMANTIC_METADATA_FIELDS,
635
655
  'timeField', 'limits', 'endpoint',
636
656
  ...(version === 3 ? ['segments'] : []),
637
- ], path);
657
+ ], path, issues);
658
+ // Each field is a recovery point when collecting; without a collector
659
+ // `field` is a plain call and the first failure throws.
638
660
  const result = {
639
- name: identifier(value.name, `${path}.name`),
640
- source: boundedText(value.source, `${path}.source`, limits.maxSourceBytes),
641
- tenant: validateTenant(value.tenant, `${path}.tenant`, limits),
642
- dimensions: namedItems(value.dimensions, `${path}.dimensions`, limits.maxDatasetItems, (item, itemPath) => validateDimension(item, itemPath, limits)),
643
- measures: namedItems(value.measures, `${path}.measures`, limits.maxDatasetItems, (item, itemPath, index) => validateMeasure(item, measureIndices === undefined ? itemPath : `${path}.measures[${measureIndices[index]}]`, limits, version)),
644
- [filtersKey]: namedItems(value[filtersKey], `${path}.${filtersKey}`, limits.maxDatasetItems, (item, itemPath) => validateFilter(item, itemPath, limits)),
645
- metrics: namedItems(value.metrics, `${path}.metrics`, limits.maxDatasetItems, (item, itemPath) => validateMetric(item, itemPath, limits)),
646
- relationships: namedItems(value.relationships, `${path}.relationships`, limits.maxDatasetItems, (item, itemPath) => validateRelationship(item, itemPath, limits)),
661
+ name: field(() => identifier(value.name, `${path}.name`)),
662
+ source: field(() => boundedText(value.source, `${path}.source`, limits.maxSourceBytes)),
663
+ tenant: field(() => validateTenant(value.tenant, `${path}.tenant`, limits)),
664
+ dimensions: field(() => namedItems(value.dimensions, `${path}.dimensions`, limits.maxDatasetItems, (item, itemPath) => validateDimension(item, itemPath, limits), issues)),
665
+ measures: field(() => namedItems(value.measures, `${path}.measures`, limits.maxDatasetItems, (item, itemPath, index) => validateMeasure(item, measureIndices === undefined ? itemPath : `${path}.measures[${measureIndices[index]}]`, limits, version), issues)),
666
+ [filtersKey]: field(() => namedItems(value[filtersKey], `${path}.${filtersKey}`, limits.maxDatasetItems, (item, itemPath) => validateFilter(item, itemPath, limits), issues)),
667
+ metrics: field(() => namedItems(value.metrics, `${path}.metrics`, limits.maxDatasetItems, (item, itemPath) => validateMetric(item, itemPath, limits), issues)),
668
+ relationships: field(() => namedItems(value.relationships, `${path}.relationships`, limits.maxDatasetItems, (item, itemPath) => validateRelationship(item, itemPath, limits), issues)),
647
669
  };
648
- if (version === 3 && value.segments !== undefined) {
649
- result.segments = namedItems(value.segments, `${path}.segments`, limits.maxDatasetItems, (item, itemPath) => validateSegment(item, itemPath, limits, result.dimensions, result.tenant));
670
+ // Segments resolve against the dimensions and tenant, so they wait for both.
671
+ if (version === 3 && value.segments !== undefined
672
+ && result.dimensions !== undefined && result.tenant !== undefined) {
673
+ result.segments = field(() => namedItems(value.segments, `${path}.segments`, limits.maxDatasetItems, (item, itemPath) => validateSegment(item, itemPath, limits, result.dimensions, result.tenant), issues));
650
674
  }
651
- if (value.timeField !== undefined)
652
- result.timeField = identifier(value.timeField, `${path}.timeField`, true);
653
- optionalText(value.description, 'description', result, path, limits);
654
- optionalText(value.owner, 'owner', result, path, limits);
655
- validateSemanticMetadata(value, result, path, limits);
675
+ if (value.timeField !== undefined) {
676
+ result.timeField = field(() => identifier(value.timeField, `${path}.timeField`, true));
677
+ }
678
+ field(() => optionalText(value.description, 'description', result, path, limits));
679
+ field(() => optionalText(value.owner, 'owner', result, path, limits));
680
+ validateSemanticMetadata(value, result, path, limits, issues);
656
681
  if (value.freshness !== undefined) {
657
- result.freshness = validateFreshness(value.freshness, `${path}.freshness`);
682
+ result.freshness = field(() => validateFreshness(value.freshness, `${path}.freshness`));
658
683
  }
659
684
  if (value.defaults !== undefined) {
660
- result.defaults = validateDefaults(value.defaults, `${path}.defaults`, limits, version);
685
+ result.defaults = field(() => validateDefaults(value.defaults, `${path}.defaults`, limits, version));
661
686
  }
662
687
  if (value.limits !== undefined)
663
- result.limits = validateLimits(value.limits, `${path}.limits`);
664
- if (value.endpoint !== undefined)
665
- result.endpoint = validateEndpoint(value.endpoint, `${path}.endpoint`, limits);
688
+ result.limits = field(() => validateLimits(value.limits, `${path}.limits`));
689
+ if (value.endpoint !== undefined) {
690
+ result.endpoint = field(() => validateEndpoint(value.endpoint, `${path}.endpoint`, limits));
691
+ }
692
+ issues?.checkpoint(since);
666
693
  return freezeRecord(result);
667
694
  }
668
695
  export function validateProtocolDatasetContract(input, options = {}) {
669
696
  return validateDataset(input, '$', resolveDeploymentLimits(options));
670
697
  }
671
- function validateDatasetReferences(datasets) {
698
+ /**
699
+ * Check a dataset contract and report every failure found, instead of
700
+ * throwing the first. Accepts exactly what `validateProtocolDatasetContract` accepts.
701
+ */
702
+ export function checkProtocolDatasetContract(input, options = {}) {
703
+ const limits = resolveDeploymentLimits(options);
704
+ return collectErrors(issues => validateDataset(input, '$', limits, undefined, 2, issues));
705
+ }
706
+ function validateDatasetReferences(datasets, issues) {
672
707
  const names = new Set(datasets.map(dataset => dataset.name));
708
+ const reference = (path) => attempt(issues, () => deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', path));
673
709
  for (const [index, dataset] of datasets.entries()) {
674
710
  if (dataset.defaults?.dimensions?.some(name => (!dataset.dimensions.some(dimension => dimension.name === name && dimension.groupable)))) {
675
- deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', `$.datasets[${index}].defaults.dimensions`);
711
+ reference(`$.datasets[${index}].defaults.dimensions`);
676
712
  }
677
713
  if (dataset.defaults?.timeGrain !== undefined && dataset.timeField === undefined) {
678
- deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', `$.datasets[${index}].defaults.timeGrain`);
714
+ reference(`$.datasets[${index}].defaults.timeGrain`);
679
715
  }
680
716
  for (const [relationshipIndex, relationship] of dataset.relationships.entries()) {
681
717
  if (!names.has(relationship.target)) {
682
- deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', `$.datasets[${index}].relationships[${relationshipIndex}].target`);
718
+ reference(`$.datasets[${index}].relationships[${relationshipIndex}].target`);
683
719
  }
684
720
  }
685
721
  if (dataset.endpoint !== undefined
686
722
  && (dataset.tenant.kind === 'required') !== (dataset.endpoint.tenant.kind === 'required')) {
687
- deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', `$.datasets[${index}].endpoint.tenant`);
723
+ reference(`$.datasets[${index}].endpoint.tenant`);
688
724
  }
689
725
  }
690
726
  }
@@ -817,7 +853,8 @@ function validateTimeMeasure(value, path, limits) {
817
853
  validateSemanticMetadata(value, result, path, limits);
818
854
  return freezeRecord(result);
819
855
  }
820
- function validateDeploymentDataset(input, path, limits, version = 2) {
856
+ function validateDeploymentDataset(input, path, limits, version = 2, issues) {
857
+ const since = issues?.count ?? 0;
821
858
  const value = requireRecord(input, path);
822
859
  exactFields(value, [
823
860
  'name', 'source', 'tenant', 'dimensions', 'measures',
@@ -826,80 +863,131 @@ function validateDeploymentDataset(input, path, limits, version = 2) {
826
863
  'description', 'freshness', 'owner', 'defaults',
827
864
  ...SEMANTIC_METADATA_FIELDS, 'timeField', 'limits', 'endpoint',
828
865
  ...(version === 3 ? ['segments'] : []),
829
- ], path);
830
- const measures = requireArray(value.measures, `${path}.measures`, limits.maxDatasetItems)
831
- .map((measure, index) => requireRecord(measure, `${path}.measures[${index}]`));
866
+ ], path, issues);
867
+ // When collecting, a malformed measure list is reported and the rest of the
868
+ // dataset is still checked as if it had no measures; a malformed measure is
869
+ // reported and its siblings are still checked.
870
+ const records = attempt(issues, () => requireArray(value.measures, `${path}.measures`, limits.maxDatasetItems)) ?? [];
871
+ const checked = records.map((measure, index) => (attempt(issues, () => requireRecord(measure, `${path}.measures[${index}]`))));
832
872
  // Base measures are validated by the dataset; composite ones (derived, and in
833
873
  // contract 3 window and shift) reference them and are validated here.
834
874
  const isComposite = (measure) => measure.kind === 'derived'
835
875
  || (version === 3 && (measure.kind === 'window' || measure.kind === 'shift'));
836
876
  // Preserve original indices when validating base measures separately.
837
- const baseEntries = measures.flatMap((measure, index) => (isComposite(measure) ? [] : [{ measure, index }]));
838
- const validated = validateDataset({ ...value, measures: baseEntries.map(entry => entry.measure), metrics: [] }, path, limits, baseEntries.map(entry => entry.index), version);
839
- const composite = measures.map((measure, index) => {
877
+ const baseEntries = checked.flatMap((measure, index) => (measure === undefined || isComposite(measure) ? [] : [{ measure, index }]));
878
+ const validated = attempt(issues, () => validateDataset({ ...value, measures: baseEntries.map(entry => entry.measure), metrics: [] }, path, limits, baseEntries.map(entry => entry.index), version, issues));
879
+ const composite = checked.map((measure, index) => measure && attempt(issues, () => {
840
880
  const measurePath = `${path}.measures[${index}]`;
841
881
  if (measure.kind === 'derived') {
842
882
  return validateDatasetDerivedMeasure(measure, measurePath, limits, version);
843
883
  }
844
884
  return isComposite(measure) ? validateTimeMeasure(measure, measurePath, limits) : undefined;
845
- });
885
+ }));
886
+ // Cross-measure checks below assume every measure validated.
887
+ issues?.checkpoint(since);
888
+ const measures = checked;
889
+ // Independent cross-measure checks each recover; a check that needs a
890
+ // reference that failed is skipped rather than reported again.
891
+ const fail = (code, at) => attempt(issues, () => deploymentError(code, at));
846
892
  const derived = composite.map(item => (item?.kind === 'derived' ? item : undefined));
847
893
  const baseByName = new Map(validated.measures.map(measure => [measure.name, measure]));
848
894
  let baseIndex = 0;
849
895
  const ordered = measures.map((_, index) => (composite[index] ?? validated.measures[baseIndex++]));
850
896
  if (new Set(ordered.map(measure => measure.name)).size !== measures.length) {
851
- deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', `${path}.measures`);
897
+ fail('HQ_DEPLOYMENT_INVALID_REFERENCE', `${path}.measures`);
852
898
  }
899
+ // Checks by name are ambiguous while names repeat.
900
+ issues?.checkpoint(since);
853
901
  const isApproximate = (measure) => measure?.approximate === true;
854
902
  // A time measure wraps exactly one base measure and needs the dataset's time axis.
855
903
  const timeByName = new Map();
904
+ // Time measures that failed a check still resolve by name, but checks that
905
+ // read their properties would only repeat that failure.
906
+ const failedTime = new Set();
856
907
  for (const [index, item] of composite.entries()) {
857
908
  if (!item || item.kind === 'derived')
858
909
  continue;
859
910
  const timeMeasure = item;
911
+ const before = issues?.count ?? 0;
860
912
  const measurePath = `${path}.measures[${index}]`;
861
913
  const wrapped = baseByName.get(timeMeasure.measure);
862
914
  if (!wrapped)
863
- deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', `${measurePath}.measure`);
915
+ fail('HQ_DEPLOYMENT_INVALID_REFERENCE', `${measurePath}.measure`);
864
916
  if (validated.timeField === undefined)
865
- deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', `${measurePath}.kind`);
866
- if (timeMeasure.cumulative === true && !CUMULATIVE_AGGREGATIONS.has(wrapped.aggregation)) {
867
- deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${measurePath}.cumulative`);
868
- }
869
- if (isApproximate(wrapped) !== isApproximate(timeMeasure)) {
870
- deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${measurePath}.approximate`);
917
+ fail('HQ_DEPLOYMENT_INVALID_REFERENCE', `${measurePath}.kind`);
918
+ if (wrapped) {
919
+ if (timeMeasure.cumulative === true && !CUMULATIVE_AGGREGATIONS.has(wrapped.aggregation)) {
920
+ fail('HQ_DEPLOYMENT_INVALID_VALUE', `${measurePath}.cumulative`);
921
+ }
922
+ if (isApproximate(wrapped) !== isApproximate(timeMeasure)) {
923
+ fail('HQ_DEPLOYMENT_INVALID_VALUE', `${measurePath}.approximate`);
924
+ }
871
925
  }
872
926
  timeByName.set(timeMeasure.name, timeMeasure);
927
+ if ((issues?.count ?? 0) > before)
928
+ failedTime.add(timeMeasure.name);
873
929
  }
874
930
  for (const [index, item] of derived.entries()) {
875
931
  if (!item)
876
932
  continue;
933
+ let resolved = true;
877
934
  for (const [useIndex, use] of item.uses.entries()) {
878
935
  if (!baseByName.has(use.measure) && !timeByName.has(use.measure)) {
879
- deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', `${path}.measures[${index}].uses[${useIndex}].measure`);
936
+ resolved = false;
937
+ fail('HQ_DEPLOYMENT_INVALID_REFERENCE', `${path}.measures[${index}].uses[${useIndex}].measure`);
880
938
  }
881
939
  }
940
+ if (!resolved || item.uses.some(use => failedTime.has(use.measure)))
941
+ continue;
882
942
  const approximate = item.uses.some(use => (isApproximate(baseByName.get(use.measure) ?? timeByName.get(use.measure))));
883
943
  if (approximate !== isApproximate(item)) {
884
- deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.measures[${index}].approximate`);
944
+ fail('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.measures[${index}].approximate`);
885
945
  }
886
946
  }
947
+ issues?.checkpoint(since);
887
948
  const { metrics: _metrics, ...dataset } = validated;
888
949
  return freezeRecord({ ...dataset, measures: Object.freeze(ordered) });
889
950
  }
951
+ function validateContract(input, limits, version, issues) {
952
+ const value = requireRecord(input, '$');
953
+ exactFields(value, ['kind', 'version', 'datasets'], [], '$', issues);
954
+ attempt(issues, () => {
955
+ if (value.kind !== 'hypequery-deployment')
956
+ deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', '$.kind');
957
+ });
958
+ // Another version's rules do not apply, so its datasets are not worth checking.
959
+ const since = issues?.count ?? 0;
960
+ attempt(issues, () => {
961
+ if (value.version !== version)
962
+ deploymentError('HQ_DEPLOYMENT_INVALID_VERSION', '$.version');
963
+ });
964
+ issues?.checkpoint(since);
965
+ const datasets = attempt(issues, () => namedItems(value.datasets, '$.datasets', limits.maxDatasets, (item, path) => validateDeploymentDataset(item, path, limits, version, issues), issues));
966
+ // References between datasets need every dataset validated first.
967
+ issues?.checkpoint(0);
968
+ validateDatasetReferences(datasets, issues);
969
+ return datasets;
970
+ }
890
971
  /** Validate the deployment contract. Unsupported fields are forbidden, even when empty. */
891
972
  export function validateProtocolDeploymentContract(input, options = {}) {
892
- const limits = resolveDeploymentLimits(options);
893
- const value = requireRecord(input, '$');
894
- exactFields(value, ['kind', 'version', 'datasets'], [], '$');
895
- if (value.kind !== 'hypequery-deployment')
896
- deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', '$.kind');
897
- if (value.version !== 2)
898
- deploymentError('HQ_DEPLOYMENT_INVALID_VERSION', '$.version');
899
- const datasets = namedItems(value.datasets, '$.datasets', limits.maxDatasets, (item, path) => validateDeploymentDataset(item, path, limits));
900
- validateDatasetReferences(datasets);
973
+ return deploymentContract(validateContract(input, resolveDeploymentLimits(options), 2));
974
+ }
975
+ function deploymentContract(datasets) {
901
976
  return freezeRecord({ kind: 'hypequery-deployment', version: 2, datasets });
902
977
  }
978
+ /**
979
+ * Check a deployment contract and report every failure found, instead of
980
+ * throwing the first. Accepts exactly what `validateProtocolDeploymentContract`
981
+ * accepts, and returns the same value when it does.
982
+ *
983
+ * Failures are reported per independent part of the contract (each dataset,
984
+ * collection item and dataset field). Checks that depend on a part that
985
+ * already failed are skipped so one mistake is not reported several times.
986
+ */
987
+ export function checkProtocolDeploymentContract(input, options = {}) {
988
+ const limits = resolveDeploymentLimits(options);
989
+ return collectErrors(issues => deploymentContract(validateContract(input, limits, 2, issues)));
990
+ }
903
991
  /** True when a contract 3 dataset uses anything contract 2 cannot express. */
904
992
  function usesContract3Feature(dataset) {
905
993
  return (dataset.segments?.length ?? 0) > 0
@@ -917,15 +1005,19 @@ function usesContract3Feature(dataset) {
917
1005
  * function of its content rather than of which producer emitted it.
918
1006
  */
919
1007
  export function validateProtocolDeploymentContractV3(input, options = {}) {
1008
+ return deploymentContractV3(validateContract(input, resolveDeploymentLimits(options), 3));
1009
+ }
1010
+ /**
1011
+ * Check a deployment contract 3 and report every failure found, instead of
1012
+ * throwing the first. Accepts exactly what `validateProtocolDeploymentContractV3`
1013
+ * accepts; see `checkProtocolDeploymentContract` for how failures are grouped.
1014
+ */
1015
+ export function checkProtocolDeploymentContractV3(input, options = {}) {
920
1016
  const limits = resolveDeploymentLimits(options);
921
- const value = requireRecord(input, '$');
922
- exactFields(value, ['kind', 'version', 'datasets'], [], '$');
923
- if (value.kind !== 'hypequery-deployment')
924
- deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', '$.kind');
925
- if (value.version !== 3)
926
- deploymentError('HQ_DEPLOYMENT_INVALID_VERSION', '$.version');
927
- const datasets = namedItems(value.datasets, '$.datasets', limits.maxDatasets, (item, path) => validateDeploymentDataset(item, path, limits, 3));
928
- validateDatasetReferences(datasets);
1017
+ return collectErrors(issues => deploymentContractV3(validateContract(input, limits, 3, issues)));
1018
+ }
1019
+ function deploymentContractV3(validated) {
1020
+ const datasets = validated;
929
1021
  if (!datasets.some(usesContract3Feature)) {
930
1022
  deploymentError('HQ_DEPLOYMENT_INVALID_VERSION', '$.version');
931
1023
  }
package/dist/index.d.ts CHANGED
@@ -19,8 +19,11 @@ export { DEFAULT_PROTOCOL_DEPLOYMENT_BUNDLE_LIMITS, PROTOCOL_DEPLOYMENT_BUNDLE_I
19
19
  export { DEFAULT_PROTOCOL_DEPLOYMENT_RELEASE_LIMITS, PROTOCOL_DEPLOYMENT_RELEASE_IDENTITY_DOMAIN, ProtocolDeploymentReleaseError, encodeProtocolDeploymentReleaseEnvelope, encodeProtocolDeploymentReleaseEnvelopeToString, hashProtocolDeploymentReleaseEnvelope, prepareProtocolDeploymentReleaseEnvelope, validateProtocolDeploymentReleaseEnvelope, validateProtocolDeploymentReleaseTarget, } from './releases/index.js';
20
20
  export type { PreparedProtocolDeploymentReleaseEnvelope, ProtocolDeploymentReleaseEnvelope, ProtocolDeploymentReleaseErrorCode, ProtocolDeploymentReleaseLimits, ProtocolDeploymentReleaseOptions, ProtocolDeploymentReleaseTarget, } from './releases/index.js';
21
21
  export type { PreparedProtocolDeploymentBundleManifest, ProtocolDeploymentBundleArtifact, ProtocolDeploymentBundleDeployment, ProtocolDeploymentBundleErrorCode, ProtocolDeploymentBundleFile, ProtocolDeploymentBundleLimits, ProtocolDeploymentBundleManifest, ProtocolDeploymentBundleOptions, } from './bundles/index.js';
22
- export { DEFAULT_PROTOCOL_DEPLOYMENT_LIMITS, PROTOCOL_DEPLOYMENT_IDENTITY_DOMAIN, PROTOCOL_DEPLOYMENT_V3_IDENTITY_DOMAIN, ProtocolDeploymentError, encodeProtocolDeploymentContract, encodeProtocolDeploymentContractToString, hashProtocolDeploymentContract, prepareProtocolDeploymentContract, prepareProtocolDeploymentContractV3, validateProtocolDatasetContract, validateProtocolDeploymentContract, validateProtocolDeploymentContractV3, } from './deployments/index.js';
23
- export type { ProtocolDatasetDerivedMeasure, ProtocolDeploymentDataset, ProtocolDeploymentMeasure, PreparedProtocolDeploymentContract, PreparedProtocolDeploymentContractV3, ProtocolAccessPolicy, ProtocolDatasetContract, ProtocolDatasetDefaults, ProtocolDatasetDimension, ProtocolDatasetFieldSource, ProtocolDatasetFieldType, ProtocolDatasetFilter, ProtocolDatasetFreshness, ProtocolDatasetLimits, ProtocolDatasetMeasure, ProtocolDatasetMetric, ProtocolDatasetRelationship, ProtocolDatasetTenantPolicy, ProtocolSemanticMetadata, ProtocolSemanticSensitivity, ProtocolDeploymentContract, ProtocolDeploymentContractV3, ProtocolDeploymentDatasetV3, ProtocolDeploymentMeasureV3, ProtocolDatasetDerivedMeasureV3, ProtocolDatasetMeasureV3, ProtocolDatasetSegment, ProtocolDatasetShiftMeasure, ProtocolDatasetWindowMeasure, ProtocolTimeInterval, ProtocolDeploymentErrorCode, ProtocolDeploymentLimits, ProtocolDeploymentOptions, ProtocolEndpointPolicy, ProtocolEndpointTenantPolicy, ProtocolMetricDerivation, ProtocolMetricInput, } from './deployments/index.js';
22
+ export { DEFAULT_PROTOCOL_DEPLOYMENT_LIMITS, PROTOCOL_DEPLOYMENT_IDENTITY_DOMAIN, PROTOCOL_DEPLOYMENT_V3_IDENTITY_DOMAIN, ProtocolDeploymentError, checkProtocolDatasetContract, checkProtocolDeploymentContract, checkProtocolDeploymentContractV3, encodeProtocolDeploymentContract, encodeProtocolDeploymentContractToString, hashProtocolDeploymentContract, prepareProtocolDeploymentContract, prepareProtocolDeploymentContractV3, validateProtocolDatasetContract, validateProtocolDeploymentContract, validateProtocolDeploymentContractV3, } from './deployments/index.js';
23
+ export type { ProtocolDatasetDerivedMeasure, ProtocolDeploymentDataset, ProtocolDeploymentMeasure, PreparedProtocolDeploymentContract, PreparedProtocolDeploymentContractV3, ProtocolAccessPolicy, ProtocolDatasetContract, ProtocolDatasetDefaults, ProtocolDatasetDimension, ProtocolDatasetFieldSource, ProtocolDatasetFieldType, ProtocolDatasetFilter, ProtocolDatasetFreshness, ProtocolDatasetLimits, ProtocolDatasetMeasure, ProtocolDatasetMetric, ProtocolDatasetRelationship, ProtocolDatasetTenantPolicy, ProtocolSemanticMetadata, ProtocolSemanticSensitivity, ProtocolDeploymentContract, ProtocolDeploymentContractV3, ProtocolDeploymentDatasetV3, ProtocolDeploymentMeasureV3, ProtocolDatasetDerivedMeasureV3, ProtocolDatasetMeasureV3, ProtocolDatasetSegment, ProtocolDatasetShiftMeasure, ProtocolDatasetWindowMeasure, ProtocolTimeInterval, ProtocolDeploymentCheckResult, ProtocolDeploymentErrorCode, ProtocolDeploymentValidationError, ProtocolDeploymentLimits, ProtocolDeploymentOptions, ProtocolEndpointPolicy, ProtocolEndpointTenantPolicy, ProtocolMetricDerivation, ProtocolMetricInput, } from './deployments/index.js';
24
24
  export type { ProtocolQueryImplementation, ProtocolQueryImplementationErrorCode, ProtocolQueryImplementationLimits, ProtocolQueryImplementationOptions, ProtocolSqlDialect, ProtocolSqlExpression, ProtocolSqlParameter, ProtocolSqlParameterSource, ProtocolSqlTenantPolicy, } from './query-implementations/index.js';
25
25
  export { PROTOCOL_SEMANTIC_FAILURE_MESSAGES } from './invocations/failure-messages.js';
26
+ export * from './client-contract/index.js';
27
+ export { PROTOCOL_FIELD_VISIBILITY_LIMITS, ProtocolFieldVisibilityError, resolveProtocolVisibleDatasetFields, validateProtocolFieldVisibility, } from './visibility/index.js';
28
+ export type { ProtocolDatasetVisibility, ProtocolFieldVisibility, ProtocolFieldVisibilityErrorCode, ProtocolVisibleDatasetFields, ProtocolVisibleFields, } from './visibility/index.js';
26
29
  //# sourceMappingURL=index.d.ts.map
@@ -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
@@ -9,5 +9,7 @@ export { DEFAULT_PROTOCOL_QUERY_EVENT_LIMITS, ProtocolQueryDiagnosticsError, Pro
9
9
  export { DEFAULT_PROTOCOL_SEMANTIC_INVOCATION_LIMITS, ProtocolSemanticInvocationError, validateProtocolSemanticInvocation, validateProtocolSemanticInvocationFailure, validateProtocolSemanticInvocationResult, validateProtocolSemanticInvocationV2, } from './invocations/index.js';
10
10
  export { DEFAULT_PROTOCOL_DEPLOYMENT_BUNDLE_LIMITS, PROTOCOL_DEPLOYMENT_BUNDLE_IDENTITY_DOMAIN, ProtocolDeploymentBundleError, encodeProtocolDeploymentBundleManifest, encodeProtocolDeploymentBundleManifestToString, hashProtocolDeploymentBundleManifest, prepareProtocolDeploymentBundleManifest, validateProtocolDeploymentBundleManifest, } from './bundles/index.js';
11
11
  export { DEFAULT_PROTOCOL_DEPLOYMENT_RELEASE_LIMITS, PROTOCOL_DEPLOYMENT_RELEASE_IDENTITY_DOMAIN, ProtocolDeploymentReleaseError, encodeProtocolDeploymentReleaseEnvelope, encodeProtocolDeploymentReleaseEnvelopeToString, hashProtocolDeploymentReleaseEnvelope, prepareProtocolDeploymentReleaseEnvelope, validateProtocolDeploymentReleaseEnvelope, validateProtocolDeploymentReleaseTarget, } from './releases/index.js';
12
- export { DEFAULT_PROTOCOL_DEPLOYMENT_LIMITS, PROTOCOL_DEPLOYMENT_IDENTITY_DOMAIN, PROTOCOL_DEPLOYMENT_V3_IDENTITY_DOMAIN, ProtocolDeploymentError, encodeProtocolDeploymentContract, encodeProtocolDeploymentContractToString, hashProtocolDeploymentContract, prepareProtocolDeploymentContract, prepareProtocolDeploymentContractV3, validateProtocolDatasetContract, validateProtocolDeploymentContract, validateProtocolDeploymentContractV3, } from './deployments/index.js';
12
+ export { DEFAULT_PROTOCOL_DEPLOYMENT_LIMITS, PROTOCOL_DEPLOYMENT_IDENTITY_DOMAIN, PROTOCOL_DEPLOYMENT_V3_IDENTITY_DOMAIN, ProtocolDeploymentError, checkProtocolDatasetContract, checkProtocolDeploymentContract, checkProtocolDeploymentContractV3, encodeProtocolDeploymentContract, encodeProtocolDeploymentContractToString, hashProtocolDeploymentContract, prepareProtocolDeploymentContract, prepareProtocolDeploymentContractV3, validateProtocolDatasetContract, validateProtocolDeploymentContract, validateProtocolDeploymentContractV3, } from './deployments/index.js';
13
13
  export { PROTOCOL_SEMANTIC_FAILURE_MESSAGES } from './invocations/failure-messages.js';
14
+ export * from './client-contract/index.js';
15
+ export { PROTOCOL_FIELD_VISIBILITY_LIMITS, ProtocolFieldVisibilityError, resolveProtocolVisibleDatasetFields, validateProtocolFieldVisibility, } from './visibility/index.js';
@@ -1,5 +1,5 @@
1
- import { sha256 } from '@noble/hashes/sha2';
2
- import { bytesToHex } from '@noble/hashes/utils';
1
+ import { sha256 } from '@noble/hashes/sha2.js';
2
+ import { bytesToHex } from '@noble/hashes/utils.js';
3
3
  import { serializeJcs } from '../values/jcs.js';
4
4
  import { validateProtocolDeploymentReleaseEnvelope } from './validate.js';
5
5
  const textEncoder = new TextEncoder();
@@ -1,5 +1,5 @@
1
- import { sha256 } from '@noble/hashes/sha2';
2
- import { bytesToHex } from '@noble/hashes/utils';
1
+ import { sha256 } from '@noble/hashes/sha2.js';
2
+ import { bytesToHex } from '@noble/hashes/utils.js';
3
3
  import { valueError } from './errors.js';
4
4
  import { serializeJcs } from './jcs.js';
5
5
  import { resolveLimits } from './limits.js';
@@ -0,0 +1,8 @@
1
+ import type { ProtocolFieldVisibilityErrorCode } from './types.js';
2
+ export declare class ProtocolFieldVisibilityError extends TypeError {
3
+ readonly code: ProtocolFieldVisibilityErrorCode;
4
+ readonly path: string;
5
+ constructor(code: ProtocolFieldVisibilityErrorCode, path?: string);
6
+ }
7
+ export declare function visibilityError(code: ProtocolFieldVisibilityErrorCode, path?: string): never;
8
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/visibility/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,YAAY,CAAC;AAEnE,qBAAa,4BAA6B,SAAQ,SAAS;IACzD,QAAQ,CAAC,IAAI,EAAE,gCAAgC,CAAC;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,gCAAgC,EAAE,IAAI,SAAM;CAM/D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,gCAAgC,EAAE,IAAI,SAAM,GAAG,KAAK,CAEzF"}
@@ -0,0 +1,13 @@
1
+ export class ProtocolFieldVisibilityError extends TypeError {
2
+ code;
3
+ path;
4
+ constructor(code, path = '$') {
5
+ super(`${code} at ${path}`);
6
+ this.name = 'ProtocolFieldVisibilityError';
7
+ this.code = code;
8
+ this.path = path;
9
+ }
10
+ }
11
+ export function visibilityError(code, path = '$') {
12
+ throw new ProtocolFieldVisibilityError(code, path);
13
+ }
@@ -0,0 +1,5 @@
1
+ export { ProtocolFieldVisibilityError } from './errors.js';
2
+ export { resolveProtocolVisibleDatasetFields } from './resolve.js';
3
+ export { PROTOCOL_FIELD_VISIBILITY_LIMITS, validateProtocolFieldVisibility } from './validate.js';
4
+ export type { ProtocolDatasetVisibility, ProtocolFieldVisibility, ProtocolFieldVisibilityErrorCode, ProtocolVisibleDatasetFields, ProtocolVisibleFields, } from './types.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/visibility/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,mCAAmC,EAAE,MAAM,cAAc,CAAC;AACnE,OAAO,EAAE,gCAAgC,EAAE,+BAA+B,EAAE,MAAM,eAAe,CAAC;AAClG,YAAY,EACV,yBAAyB,EACzB,uBAAuB,EACvB,gCAAgC,EAChC,4BAA4B,EAC5B,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { ProtocolFieldVisibilityError } from './errors.js';
2
+ export { resolveProtocolVisibleDatasetFields } from './resolve.js';
3
+ export { PROTOCOL_FIELD_VISIBILITY_LIMITS, validateProtocolFieldVisibility } from './validate.js';
@@ -0,0 +1,30 @@
1
+ import type { ProtocolDeploymentDataset } from '../deployments/index.js';
2
+ import type { ProtocolFieldVisibility, ProtocolVisibleDatasetFields } from './types.js';
3
+ /**
4
+ * What one dataset exposes under a visibility policy, or null when the policy
5
+ * hides the dataset (RFC 0019).
6
+ *
7
+ * Names the policy lists but the contract doesn't have are ignored, so a
8
+ * policy written against one release keeps working after a later release
9
+ * removes a field. Visibility only narrows: a dimension the contract marks
10
+ * non-groupable or non-filterable stays so.
11
+ *
12
+ * - A one-hop `relationship.dimension` is visible only when the target dataset
13
+ * and that dimension are visible in the target's own entry, and the
14
+ * relationship's local join keys are visible.
15
+ * - A declared filter is visible only when the dimension it filters is.
16
+ * - The time field is visible only as a visible dimension; grouping by period
17
+ * and time measures need it.
18
+ * - A measure is visible only when it is listed and every measure it is
19
+ * computed from is visible too. A derived ratio over a hidden measure would
20
+ * let a caller recover the hidden value.
21
+ * - Measures are authorized by name, independently of the dimensions they
22
+ * aggregate: `revenue = sum(amount)` stays visible with `amount` hidden. A
23
+ * `min`, `max`, `argMax` or `countDistinct` over a hidden dimension reveals
24
+ * its values, so list measures explicitly rather than use `*` when a dataset
25
+ * has any that must stay hidden; `*` also exposes measures added later.
26
+ * - Segments are not narrowed. Deployment contract 2, which this resolves, has
27
+ * none.
28
+ */
29
+ export declare function resolveProtocolVisibleDatasetFields(dataset: ProtocolDeploymentDataset, datasets: ReadonlyMap<string, ProtocolDeploymentDataset>, visibility: ProtocolFieldVisibility): ProtocolVisibleDatasetFields | null;
30
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/visibility/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,KAAK,EACV,uBAAuB,EACvB,4BAA4B,EAE7B,MAAM,YAAY,CAAC;AA6CpB;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,yBAAyB,EAClC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,yBAAyB,CAAC,EACxD,UAAU,EAAE,uBAAuB,GAClC,4BAA4B,GAAG,IAAI,CA+CrC"}