@hypequery/datasets 0.13.5 → 0.14.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 (113) hide show
  1. package/README.md +10 -0
  2. package/dist/agent-catalog.d.ts +97 -0
  3. package/dist/agent-catalog.d.ts.map +1 -0
  4. package/dist/agent-catalog.js +79 -0
  5. package/dist/api.type-test.js +19 -1
  6. package/dist/catalog.d.ts +17 -6
  7. package/dist/catalog.d.ts.map +1 -1
  8. package/dist/catalog.js +18 -1
  9. package/dist/contract.d.ts +11 -7
  10. package/dist/contract.d.ts.map +1 -1
  11. package/dist/contract.js +22 -1
  12. package/dist/dataset-query.d.ts.map +1 -1
  13. package/dist/dataset-query.js +9 -3
  14. package/dist/dataset.d.ts.map +1 -1
  15. package/dist/dataset.js +19 -2
  16. package/dist/executor.d.ts +6 -0
  17. package/dist/executor.d.ts.map +1 -1
  18. package/dist/executor.js +83 -24
  19. package/dist/field.d.ts.map +1 -1
  20. package/dist/field.js +2 -0
  21. package/dist/in-memory-backend.d.ts.map +1 -1
  22. package/dist/in-memory-backend.js +2 -17
  23. package/dist/index.d.ts +10 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +7 -0
  26. package/dist/measure.d.ts.map +1 -1
  27. package/dist/measure.js +4 -0
  28. package/dist/protocol-adapter.d.ts.map +1 -1
  29. package/dist/protocol-adapter.js +21 -0
  30. package/dist/protocol-rehydrate.d.ts +46 -0
  31. package/dist/protocol-rehydrate.d.ts.map +1 -0
  32. package/dist/protocol-rehydrate.js +218 -0
  33. package/dist/publishing/features/dataset-publication.d.ts +10 -0
  34. package/dist/publishing/features/dataset-publication.d.ts.map +1 -0
  35. package/dist/publishing/features/dataset-publication.js +34 -0
  36. package/dist/publishing/node.d.ts +16 -0
  37. package/dist/publishing/node.d.ts.map +1 -0
  38. package/dist/publishing/node.js +35 -0
  39. package/dist/publishing/publisher.d.ts +17 -0
  40. package/dist/publishing/publisher.d.ts.map +1 -0
  41. package/dist/publishing/publisher.js +32 -0
  42. package/dist/publishing/types.d.ts +31 -0
  43. package/dist/publishing/types.d.ts.map +1 -0
  44. package/dist/publishing/types.js +1 -0
  45. package/dist/query-builder-protocol.d.ts +6 -2
  46. package/dist/query-builder-protocol.d.ts.map +1 -1
  47. package/dist/semantic-plan.d.ts +3 -1
  48. package/dist/semantic-plan.d.ts.map +1 -1
  49. package/dist/semantic-planner.d.ts.map +1 -1
  50. package/dist/semantic-planner.js +4 -0
  51. package/dist/semantic-query-schema.d.ts +48 -0
  52. package/dist/semantic-query-schema.d.ts.map +1 -0
  53. package/dist/semantic-query-schema.js +218 -0
  54. package/dist/sql-utils.d.ts +36 -0
  55. package/dist/sql-utils.d.ts.map +1 -1
  56. package/dist/sql-utils.js +76 -0
  57. package/dist/tools.d.ts +5 -0
  58. package/dist/tools.d.ts.map +1 -1
  59. package/dist/tools.js +25 -267
  60. package/dist/types.d.ts +102 -12
  61. package/dist/types.d.ts.map +1 -1
  62. package/dist/utils/agent-catalog-projection.d.ts +78 -0
  63. package/dist/utils/agent-catalog-projection.d.ts.map +1 -0
  64. package/dist/utils/agent-catalog-projection.js +179 -0
  65. package/dist/utils/dataset-cache-policy.d.ts +34 -0
  66. package/dist/utils/dataset-cache-policy.d.ts.map +1 -0
  67. package/dist/utils/dataset-cache-policy.js +50 -0
  68. package/dist/utils/dataset-contract.d.ts +2 -2
  69. package/dist/utils/dataset-contract.d.ts.map +1 -1
  70. package/dist/utils/dataset-contract.js +3 -1
  71. package/dist/utils/dataset-definition-validation.d.ts +35 -0
  72. package/dist/utils/dataset-definition-validation.d.ts.map +1 -0
  73. package/dist/utils/dataset-definition-validation.js +215 -0
  74. package/dist/utils/dataset-metric-ref.d.ts +2 -2
  75. package/dist/utils/dataset-metric-ref.d.ts.map +1 -1
  76. package/dist/utils/dataset-metric-ref.js +5 -3
  77. package/dist/utils/metric-alias.d.ts +4 -0
  78. package/dist/utils/metric-alias.d.ts.map +1 -0
  79. package/dist/utils/metric-alias.js +28 -0
  80. package/dist/utils/protocol-metric-capabilities.d.ts +7 -0
  81. package/dist/utils/protocol-metric-capabilities.d.ts.map +1 -0
  82. package/dist/utils/protocol-metric-capabilities.js +57 -0
  83. package/dist/utils/protocol-rehydrate-filters.d.ts +10 -0
  84. package/dist/utils/protocol-rehydrate-filters.d.ts.map +1 -0
  85. package/dist/utils/protocol-rehydrate-filters.js +36 -0
  86. package/dist/utils/protocol-semantic-metadata.d.ts +5 -0
  87. package/dist/utils/protocol-semantic-metadata.d.ts.map +1 -0
  88. package/dist/utils/protocol-semantic-metadata.js +5 -0
  89. package/dist/utils/published-name.d.ts +3 -0
  90. package/dist/utils/published-name.d.ts.map +1 -0
  91. package/dist/utils/published-name.js +7 -0
  92. package/dist/utils/published-relationships.d.ts +34 -0
  93. package/dist/utils/published-relationships.d.ts.map +1 -0
  94. package/dist/utils/published-relationships.js +35 -0
  95. package/dist/utils/relationship-fields.d.ts +7 -0
  96. package/dist/utils/relationship-fields.d.ts.map +1 -1
  97. package/dist/utils/relationship-fields.js +13 -1
  98. package/dist/utils/result-limits.d.ts +34 -0
  99. package/dist/utils/result-limits.d.ts.map +1 -0
  100. package/dist/utils/result-limits.js +40 -0
  101. package/dist/utils/semantic-metadata-validation.d.ts +4 -0
  102. package/dist/utils/semantic-metadata-validation.d.ts.map +1 -0
  103. package/dist/utils/semantic-metadata-validation.js +103 -0
  104. package/dist/utils/semantic-metadata.d.ts +4 -0
  105. package/dist/utils/semantic-metadata.d.ts.map +1 -0
  106. package/dist/utils/semantic-metadata.js +13 -0
  107. package/dist/utils/semantic-result-serialization.d.ts +9 -0
  108. package/dist/utils/semantic-result-serialization.d.ts.map +1 -0
  109. package/dist/utils/semantic-result-serialization.js +26 -0
  110. package/dist/utils/semantic-tool.d.ts +5 -0
  111. package/dist/utils/semantic-tool.d.ts.map +1 -0
  112. package/dist/utils/semantic-tool.js +29 -0
  113. package/package.json +5 -4
@@ -1 +1 @@
1
- {"version":3,"file":"dataset-metric-ref.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-metric-ref.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EAEnB,iBAAiB,EACjB,SAAS,EACT,sBAAsB,EAEvB,MAAM,aAAa,CAAC;AAGrB,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AACzD,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AACrD,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;AAE/D,wBAAgB,qBAAqB,CACnC,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACnD,YAAY,SAAS,MAAM,EAE3B,MAAM,EAAE,gBAAgB,CAAC,SAAS,CAAC,GAAG,mBAAmB,CAAC,YAAY,CAAC,GACtE,MAAM,IAAI,mBAAmB,CAAC,YAAY,CAAC,CAE7C;AAED,wBAAgB,eAAe,CAC7B,YAAY,SAAS,MAAM,EAC3B,WAAW,SAAS,MAAM,EAC1B,KAAK,SAAS,eAAe,GAAG,iBAAiB,CAAC,YAAY,CAAC,EAC/D,QAAQ,SAAS,eAAe,CAAC,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,CAAC,EAE5F,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,KAAK,EACX,KAAK,CAAC,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,GACnB,SAAS,CAAC,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,CAAC,CAiCvD;AAED,wBAAgB,uBAAuB,CAAC,YAAY,SAAS,MAAM,EACjE,MAAM,EAAE,mBAAmB,CAAC,YAAY,CAAC,GACxC,iBAAiB,CAAC,YAAY,CAAC,CAMjC;AAED,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"dataset-metric-ref.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-metric-ref.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EAEnB,iBAAiB,EACjB,SAAS,EACT,sBAAsB,EACtB,gBAAgB,EAEjB,MAAM,aAAa,CAAC;AAIrB,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AACzD,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AACrD,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;AAE/D,wBAAgB,qBAAqB,CACnC,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACnD,YAAY,SAAS,MAAM,EAE3B,MAAM,EAAE,gBAAgB,CAAC,SAAS,CAAC,GAAG,mBAAmB,CAAC,YAAY,CAAC,GACtE,MAAM,IAAI,mBAAmB,CAAC,YAAY,CAAC,CAE7C;AAED,wBAAgB,eAAe,CAC7B,YAAY,SAAS,MAAM,EAC3B,WAAW,SAAS,MAAM,EAC1B,KAAK,SAAS,eAAe,GAAG,iBAAiB,CAAC,YAAY,CAAC,EAC/D,QAAQ,SAAS,eAAe,CAAC,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,CAAC,EAE5F,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,KAAK,EACX,KAAK,CAAC,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,EACpB,QAAQ,GAAE,gBAAqB,GAC9B,SAAS,CAAC,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,CAAC,CAkCvD;AAED,wBAAgB,uBAAuB,CAAC,YAAY,SAAS,MAAM,EACjE,MAAM,EAAE,mBAAmB,CAAC,YAAY,CAAC,GACxC,iBAAiB,CAAC,YAAY,CAAC,CAMjC;AAED,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,CAAC"}
@@ -1,8 +1,9 @@
1
1
  import { buildMetricContract } from './dataset-contract.js';
2
+ import { snapshotSemanticMetadata } from './semantic-metadata.js';
2
3
  export function isDerivedMetricConfig(config) {
3
4
  return 'uses' in config && 'formula' in config;
4
5
  }
5
- export function createMetricRef(ds, name, spec, label, description) {
6
+ export function createMetricRef(ds, name, spec, label, description, metadata = {}) {
6
7
  const ref = {
7
8
  __type: 'metric_ref',
8
9
  datasetName: ds.name,
@@ -10,6 +11,7 @@ export function createMetricRef(ds, name, spec, label, description) {
10
11
  spec,
11
12
  label,
12
13
  description,
14
+ ...snapshotSemanticMetadata(metadata),
13
15
  dataset: ds,
14
16
  by(grain) {
15
17
  if (!ds.timeKey) {
@@ -20,12 +22,12 @@ export function createMetricRef(ds, name, spec, label, description) {
20
22
  metric: ref,
21
23
  grain,
22
24
  contract() {
23
- return buildMetricContract(name, ds, spec, label, description, grain);
25
+ return buildMetricContract(name, ds, spec, label, description, metadata, grain);
24
26
  },
25
27
  };
26
28
  },
27
29
  contract() {
28
- return buildMetricContract(name, ds, spec, label, description);
30
+ return buildMetricContract(name, ds, spec, label, description, metadata);
29
31
  },
30
32
  };
31
33
  return ref;
@@ -0,0 +1,4 @@
1
+ import type { MetricHandle } from '../types.js';
2
+ /** Clone a metric handle with the public name used by a published registry. */
3
+ export declare function aliasMetricHandle(metric: MetricHandle, alias: string, datasetAlias: string): MetricHandle;
4
+ //# sourceMappingURL=metric-alias.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metric-alias.d.ts","sourceRoot":"","sources":["../../src/utils/metric-alias.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAKV,YAAY,EAEb,MAAM,aAAa,CAAC;AA4BrB,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,GACnB,YAAY,CAYd"}
@@ -0,0 +1,28 @@
1
+ function aliasMetricRef(metric, alias, datasetAlias) {
2
+ const aliased = {
3
+ ...metric,
4
+ datasetName: datasetAlias,
5
+ name: alias,
6
+ by(grain) {
7
+ return aliasMetricHandle(metric.by(grain), alias, datasetAlias);
8
+ },
9
+ contract() {
10
+ return { ...metric.contract(), dataset: datasetAlias, name: alias };
11
+ },
12
+ };
13
+ return aliased;
14
+ }
15
+ /** Clone a metric handle with the public name used by a published registry. */
16
+ export function aliasMetricHandle(metric, alias, datasetAlias) {
17
+ if (metric.__type === 'metric_ref') {
18
+ return aliasMetricRef(metric, alias, datasetAlias);
19
+ }
20
+ const aliasedMetric = aliasMetricRef(metric.metric, alias, datasetAlias);
21
+ return {
22
+ ...metric,
23
+ metric: aliasedMetric,
24
+ contract() {
25
+ return { ...metric.contract(), dataset: datasetAlias, name: alias };
26
+ },
27
+ };
28
+ }
@@ -0,0 +1,7 @@
1
+ import type { ProtocolDatasetMetric } from '@hypequery/protocol';
2
+ import type { MetricHandle, MetricQuery } from '../types.js';
3
+ /** Runtime restrictions travel with a cloned handle, including publication aliases. */
4
+ export declare function protocolMetricCapabilityErrors(handle: MetricHandle, query: MetricQuery): string[];
5
+ /** Pin discovery and execution together, preserving restrictions when graining. */
6
+ export declare function withContractCapabilities(handle: MetricHandle, metric: ProtocolDatasetMetric): MetricHandle;
7
+ //# sourceMappingURL=protocol-metric-capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol-metric-capabilities.d.ts","sourceRoot":"","sources":["../../src/utils/protocol-metric-capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAwB,MAAM,aAAa,CAAC;AAUnF,uFAAuF;AACvF,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE,CAajG;AAED,mFAAmF;AACnF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,qBAAqB,GAAG,YAAY,CAkC1G"}
@@ -0,0 +1,57 @@
1
+ const CAPABILITIES = Symbol('hypequery.protocol-metric-capabilities');
2
+ /** Runtime restrictions travel with a cloned handle, including publication aliases. */
3
+ export function protocolMetricCapabilityErrors(handle, query) {
4
+ const capabilities = handle[CAPABILITIES];
5
+ if (!capabilities)
6
+ return [];
7
+ const errors = [];
8
+ for (const dimension of query.dimensions ?? []) {
9
+ if (!capabilities.dimensions.includes(dimension))
10
+ errors.push(`Dimension "${dimension}" is not published for this metric.`);
11
+ }
12
+ for (const filter of query.filters ?? []) {
13
+ if (!capabilities.filters.includes(filter.field))
14
+ errors.push(`Filter "${filter.field}" is not published for this metric.`);
15
+ }
16
+ const grain = handle.__type === 'grained_metric_ref' ? handle.grain : query.by;
17
+ if (grain && !capabilities.grains.includes(grain))
18
+ errors.push(`Grain "${grain}" is not published for this metric.`);
19
+ return errors;
20
+ }
21
+ /** Pin discovery and execution together, preserving restrictions when graining. */
22
+ export function withContractCapabilities(handle, metric) {
23
+ const capabilities = Object.freeze({
24
+ dimensions: Object.freeze(metric.dimensions.map(String)),
25
+ filters: Object.freeze(metric.filters.map(String)),
26
+ grains: Object.freeze([...metric.grains]),
27
+ });
28
+ const pin = (target) => {
29
+ const contract = () => ({
30
+ ...target.contract(),
31
+ dimensions: [...capabilities.dimensions],
32
+ filters: [...capabilities.filters],
33
+ grains: [...capabilities.grains],
34
+ });
35
+ if (target.__type === 'grained_metric_ref') {
36
+ return Object.freeze({ ...target, [CAPABILITIES]: capabilities, metric: pin(target.metric), contract });
37
+ }
38
+ const pinned = {
39
+ ...target,
40
+ [CAPABILITIES]: capabilities,
41
+ contract,
42
+ by(grain) {
43
+ if (!capabilities.grains.includes(grain))
44
+ throw new Error(`Grain "${grain}" is not published for this metric.`);
45
+ const grained = target.by(grain);
46
+ return Object.freeze({
47
+ ...grained,
48
+ [CAPABILITIES]: capabilities,
49
+ metric: pinned,
50
+ contract: () => ({ ...contract(), kind: 'grained_metric', grain }),
51
+ });
52
+ },
53
+ };
54
+ return Object.freeze(pinned);
55
+ };
56
+ return pin(handle);
57
+ }
@@ -0,0 +1,10 @@
1
+ import type { ProtocolExpression } from '@hypequery/protocol';
2
+ import type { MetricFilter } from '../types.js';
3
+ /**
4
+ * A measure's fixed filters cross the contract boundary as RFC 0003
5
+ * expressions. `protocol-adapter.ts` only ever emits `reference <op> literal`
6
+ * for them, so this inverts exactly that shape and refuses anything else rather
7
+ * than partially interpreting an expression it cannot faithfully rebuild.
8
+ */
9
+ export declare function rehydrateMeasureFilter(expression: ProtocolExpression, unsupported: () => Error): MetricFilter;
10
+ //# sourceMappingURL=protocol-rehydrate-filters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol-rehydrate-filters.d.ts","sourceRoot":"","sources":["../../src/utils/protocol-rehydrate-filters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,kBAAkB,EAC9B,WAAW,EAAE,MAAM,KAAK,GACvB,YAAY,CASd"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * A measure's fixed filters cross the contract boundary as RFC 0003
3
+ * expressions. `protocol-adapter.ts` only ever emits `reference <op> literal`
4
+ * for them, so this inverts exactly that shape and refuses anything else rather
5
+ * than partially interpreting an expression it cannot faithfully rebuild.
6
+ */
7
+ export function rehydrateMeasureFilter(expression, unsupported) {
8
+ if (expression.kind !== 'comparison')
9
+ throw unsupported();
10
+ const { left, right, operator } = expression;
11
+ if (left.kind !== 'reference' || right.kind !== 'literal')
12
+ throw unsupported();
13
+ return {
14
+ field: String(left.name),
15
+ operator,
16
+ value: fromCanonicalValue(right.value, unsupported),
17
+ };
18
+ }
19
+ /**
20
+ * Unwraps the RFC 0001 tagged value model back to the plain JavaScript value a
21
+ * measure filter was authored with.
22
+ */
23
+ function fromCanonicalValue(value, unsupported) {
24
+ if (value === null || typeof value !== 'object')
25
+ return value;
26
+ const tagged = value
27
+ .$hypequery;
28
+ if (tagged === undefined)
29
+ throw unsupported();
30
+ if (tagged.type === 'array') {
31
+ return (tagged.values ?? []).map(item => fromCanonicalValue(item, unsupported));
32
+ }
33
+ if (tagged.value === undefined)
34
+ throw unsupported();
35
+ return tagged.value;
36
+ }
@@ -0,0 +1,5 @@
1
+ import type { ProtocolSemanticMetadata } from '@hypequery/protocol';
2
+ import type { SemanticMetadata } from '../types.js';
3
+ /** Map local semantic metadata onto its portable protocol representation. */
4
+ export declare function toProtocolSemanticMetadata(metadata: SemanticMetadata): ProtocolSemanticMetadata;
5
+ //# sourceMappingURL=protocol-semantic-metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol-semantic-metadata.d.ts","sourceRoot":"","sources":["../../src/utils/protocol-semantic-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,6EAA6E;AAC7E,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,gBAAgB,GACzB,wBAAwB,CAE1B"}
@@ -0,0 +1,5 @@
1
+ import { snapshotSemanticMetadata } from './semantic-metadata.js';
2
+ /** Map local semantic metadata onto its portable protocol representation. */
3
+ export function toProtocolSemanticMetadata(metadata) {
4
+ return snapshotSemanticMetadata(metadata);
5
+ }
@@ -0,0 +1,3 @@
1
+ /** Validate names that become public dataset, metric, tool, or schema identifiers. */
2
+ export declare function assertPublishedName(name: string, kind: 'dataset' | 'metric'): void;
3
+ //# sourceMappingURL=published-name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"published-name.d.ts","sourceRoot":"","sources":["../../src/utils/published-name.ts"],"names":[],"mappings":"AAEA,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,IAAI,CAMlF"}
@@ -0,0 +1,7 @@
1
+ const PUBLISHED_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
2
+ /** Validate names that become public dataset, metric, tool, or schema identifiers. */
3
+ export function assertPublishedName(name, kind) {
4
+ if (!PUBLISHED_NAME.test(name)) {
5
+ throw new Error(`Published ${kind} name "${name}" must start with a letter or underscore and contain only letters, numbers, and underscores.`);
6
+ }
7
+ }
@@ -0,0 +1,34 @@
1
+ import type { AnyDatasetInstance, RelationshipDefinition } from '../types.js';
2
+ /**
3
+ * Resolves a published alias back to the materialized instance registered under
4
+ * it. Deliberately a lookup rather than a value: relationships are rewired
5
+ * while the registry is still being built, and datasets may reference each
6
+ * other, so a target is resolved on call rather than at rewiring time.
7
+ */
8
+ export type PublishedLookup = (alias: string) => AnyDatasetInstance | undefined;
9
+ /**
10
+ * Maps each published dataset's defined name to the alias it was published
11
+ * under. Keyed by name because that is the uniqueness the publisher enforces —
12
+ * it rejects a second entry for the same dataset name — so a copied instance
13
+ * still resolves to the right alias.
14
+ */
15
+ export declare function publishedAliasesByName(entries: readonly {
16
+ readonly alias: string;
17
+ readonly dataset: AnyDatasetInstance;
18
+ }[]): ReadonlyMap<string, string>;
19
+ /**
20
+ * Rewires a dataset's relationships onto the aliases their targets were
21
+ * published under.
22
+ *
23
+ * Publishing only renames the copied dataset, so without this a relationship's
24
+ * `target()` closure still returns the original instance — a dataset published
25
+ * as `accounts` would still be advertised as `customers` by everything that
26
+ * relates to it, giving agents a target absent from the registry and a
27
+ * reference the deployment contract validator rejects.
28
+ *
29
+ * A target that was not published is left untouched: the join still resolves
30
+ * through the original instance, which is how an unpublished target already
31
+ * behaves in a hand-built registry.
32
+ */
33
+ export declare function rewirePublishedRelationships(relationships: Readonly<Record<string, RelationshipDefinition>>, aliasesByName: ReadonlyMap<string, string>, lookup: PublishedLookup): Record<string, RelationshipDefinition>;
34
+ //# sourceMappingURL=published-relationships.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"published-relationships.d.ts","sourceRoot":"","sources":["../../src/utils/published-relationships.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE9E;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAC;AAEhF;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,SAAS;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE,EAAE,GACnF,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAE7B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAC1C,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC,EAC/D,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1C,MAAM,EAAE,eAAe,GACtB,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAUxC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Maps each published dataset's defined name to the alias it was published
3
+ * under. Keyed by name because that is the uniqueness the publisher enforces —
4
+ * it rejects a second entry for the same dataset name — so a copied instance
5
+ * still resolves to the right alias.
6
+ */
7
+ export function publishedAliasesByName(entries) {
8
+ return new Map(entries.map(entry => [entry.dataset.name, entry.alias]));
9
+ }
10
+ /**
11
+ * Rewires a dataset's relationships onto the aliases their targets were
12
+ * published under.
13
+ *
14
+ * Publishing only renames the copied dataset, so without this a relationship's
15
+ * `target()` closure still returns the original instance — a dataset published
16
+ * as `accounts` would still be advertised as `customers` by everything that
17
+ * relates to it, giving agents a target absent from the registry and a
18
+ * reference the deployment contract validator rejects.
19
+ *
20
+ * A target that was not published is left untouched: the join still resolves
21
+ * through the original instance, which is how an unpublished target already
22
+ * behaves in a hand-built registry.
23
+ */
24
+ export function rewirePublishedRelationships(relationships, aliasesByName, lookup) {
25
+ const rewired = Object.entries(relationships).map(([name, relationship]) => {
26
+ const alias = aliasesByName.get(relationship.target().name);
27
+ if (alias === undefined)
28
+ return [name, relationship];
29
+ return [name, Object.freeze({
30
+ ...relationship,
31
+ target: () => lookup(alias) ?? relationship.target(),
32
+ })];
33
+ });
34
+ return Object.freeze(Object.fromEntries(rewired));
35
+ }
@@ -15,6 +15,13 @@ import type { AnyDatasetInstance, DimensionDefinition, RelationshipDefinition }
15
15
  * generated input schemas all advertise exactly this list.
16
16
  */
17
17
  export declare function listQueryableRelationshipFields(name: string, relationship: RelationshipDefinition): string[];
18
+ /**
19
+ * The subset of {@link listQueryableRelationshipFields} that may be used as a
20
+ * grouping key, honouring `groupable: false` on the target dimension. The rest
21
+ * stay queryable as filters, which is why this is a separate list rather than a
22
+ * narrowing of the one above.
23
+ */
24
+ export declare function listGroupableRelationshipFields(name: string, relationship: RelationshipDefinition): string[];
18
25
  export interface ParsedQualifiedField {
19
26
  /** The relationship name (prefix before the first dot). */
20
27
  relationship: string;
@@ -1 +1 @@
1
- {"version":3,"file":"relationship-fields.d.ts","sourceRoot":"","sources":["../../src/utils/relationship-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,sBAAsB,GACnC,MAAM,EAAE,CAQV;AAED,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;CACf;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAM7E;AAED,MAAM,WAAW,sBAAsB;IACrC,8EAA8E;IAC9E,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,sBAAsB,CAAC;IACrC,4CAA4C;IAC5C,MAAM,EAAE,kBAAkB,CAAC;IAC3B,+DAA+D;IAC/D,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,mBAAmB,CAAC;IACrC,oDAAoD;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,wBAAwB,GAChC;IAAE,QAAQ,EAAE,sBAAsB,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5C;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,kBAAkB,EACtB,IAAI,EAAE,MAAM,GACX,wBAAwB,GAAG,IAAI,CAuDjC"}
1
+ {"version":3,"file":"relationship-fields.d.ts","sourceRoot":"","sources":["../../src/utils/relationship-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,sBAAsB,GACnC,MAAM,EAAE,CAEV;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,sBAAsB,GACnC,MAAM,EAAE,CAEV;AAgBD,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;CACf;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAM7E;AAED,MAAM,WAAW,sBAAsB;IACrC,8EAA8E;IAC9E,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,sBAAsB,CAAC;IACrC,4CAA4C;IAC5C,MAAM,EAAE,kBAAkB,CAAC;IAC3B,+DAA+D;IAC/D,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,mBAAmB,CAAC;IACrC,oDAAoD;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,wBAAwB,GAChC;IAAE,QAAQ,EAAE,sBAAsB,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5C;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,kBAAkB,EACtB,IAAI,EAAE,MAAM,GACX,wBAAwB,GAAG,IAAI,CAuDjC"}
@@ -14,12 +14,24 @@
14
14
  * generated input schemas all advertise exactly this list.
15
15
  */
16
16
  export function listQueryableRelationshipFields(name, relationship) {
17
+ return relationshipFields(name, relationship, () => true);
18
+ }
19
+ /**
20
+ * The subset of {@link listQueryableRelationshipFields} that may be used as a
21
+ * grouping key, honouring `groupable: false` on the target dimension. The rest
22
+ * stay queryable as filters, which is why this is a separate list rather than a
23
+ * narrowing of the one above.
24
+ */
25
+ export function listGroupableRelationshipFields(name, relationship) {
26
+ return relationshipFields(name, relationship, dimension => dimension.groupable !== false);
27
+ }
28
+ function relationshipFields(name, relationship, include) {
17
29
  if (relationship.kind === 'hasMany') {
18
30
  return [];
19
31
  }
20
32
  const target = relationship.target();
21
33
  return Object.entries(target?.dimensions ?? {})
22
- .filter(([, dimension]) => !dimension.sql)
34
+ .filter(([, dimension]) => !dimension.sql && include(dimension))
23
35
  .map(([field]) => `${name}.${field}`);
24
36
  }
25
37
  /** True when a field name is relationship-qualified (contains a dot). */
@@ -0,0 +1,34 @@
1
+ import type { DatasetLimits, MetricResultMeta } from '../types.js';
2
+ export type ResultLimitMeta = NonNullable<MetricResultMeta['resultLimit']>;
3
+ export interface ResolvedResultLimit {
4
+ /** The limit to execute with; `undefined` leaves the query unbounded. */
5
+ limit?: number;
6
+ /** Meta to surface, present only when the declared ceiling supplied the limit. */
7
+ meta?: ResultLimitMeta;
8
+ }
9
+ /**
10
+ * Applies a dataset's declared `maxResultSize` to a query that set no limit.
11
+ *
12
+ * Query validation already rejects a limit *above* the ceiling, so the case
13
+ * left open is the one with no limit at all: `query.limit != null` guards both
14
+ * validators, so an unbounded query has always skipped the ceiling entirely and
15
+ * streamed whatever the table held. A ceiling that only binds callers who
16
+ * happened to name a limit is not a ceiling.
17
+ *
18
+ * Bounding is reported, never silent. A caller who asked for everything and
19
+ * received 1,000 rows cannot otherwise distinguish a bounded answer from a
20
+ * complete one, and 1,000 rows is a plausible-looking number.
21
+ */
22
+ export declare function resolveResultLimit(requested: number | undefined, limits: DatasetLimits | undefined): ResolvedResultLimit;
23
+ /**
24
+ * Merges result-limit meta onto a pending result.
25
+ *
26
+ * Takes the promise rather than the resolved value so the caller stays
27
+ * synchronous: query validation throws before execution begins, and callers
28
+ * (and their tests) rely on that being a synchronous throw rather than a
29
+ * rejected promise.
30
+ */
31
+ export declare function withResultLimit<T extends {
32
+ meta?: MetricResultMeta;
33
+ }>(result: Promise<T>, meta: ResultLimitMeta | undefined): Promise<T>;
34
+ //# sourceMappingURL=result-limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result-limits.d.ts","sourceRoot":"","sources":["../../src/utils/result-limits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEnE,MAAM,MAAM,eAAe,GAAG,WAAW,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC,CAAC;AAE3E,MAAM,WAAW,mBAAmB;IAClC,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,IAAI,CAAC,EAAE,eAAe,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,aAAa,GAAG,SAAS,GAChC,mBAAmB,CAWrB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,gBAAgB,CAAA;CAAE,EACnE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAClB,IAAI,EAAE,eAAe,GAAG,SAAS,GAChC,OAAO,CAAC,CAAC,CAAC,CAQZ"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Applies a dataset's declared `maxResultSize` to a query that set no limit.
3
+ *
4
+ * Query validation already rejects a limit *above* the ceiling, so the case
5
+ * left open is the one with no limit at all: `query.limit != null` guards both
6
+ * validators, so an unbounded query has always skipped the ceiling entirely and
7
+ * streamed whatever the table held. A ceiling that only binds callers who
8
+ * happened to name a limit is not a ceiling.
9
+ *
10
+ * Bounding is reported, never silent. A caller who asked for everything and
11
+ * received 1,000 rows cannot otherwise distinguish a bounded answer from a
12
+ * complete one, and 1,000 rows is a plausible-looking number.
13
+ */
14
+ export function resolveResultLimit(requested, limits) {
15
+ const maxResultSize = limits?.maxResultSize;
16
+ if (maxResultSize === undefined || requested !== undefined) {
17
+ return { limit: requested };
18
+ }
19
+ return {
20
+ limit: maxResultSize,
21
+ meta: { maxResultSize, applied: maxResultSize },
22
+ };
23
+ }
24
+ /**
25
+ * Merges result-limit meta onto a pending result.
26
+ *
27
+ * Takes the promise rather than the resolved value so the caller stays
28
+ * synchronous: query validation throws before execution begins, and callers
29
+ * (and their tests) rely on that being a synchronous throw rather than a
30
+ * rejected promise.
31
+ */
32
+ export function withResultLimit(result, meta) {
33
+ if (meta === undefined) {
34
+ return result;
35
+ }
36
+ return result.then((value) => ({
37
+ ...value,
38
+ meta: { ...(value.meta ?? {}), resultLimit: { ...meta } },
39
+ }));
40
+ }
@@ -0,0 +1,4 @@
1
+ import type { DatasetConfig, DimensionDefinition, MeasureDefinition, RelationshipDefinition, SemanticMetadata } from '../types.js';
2
+ export declare function validateSemanticMetadata(datasetName: string, location: string, metadata: SemanticMetadata): void;
3
+ export declare function validateDatasetAgentMetadata<TDimensions extends Record<string, DimensionDefinition>, TMeasures extends Record<string, MeasureDefinition>, TRelationships extends Record<string, RelationshipDefinition>>(datasetName: string, config: DatasetConfig<TDimensions, TMeasures, TRelationships>): void;
4
+ //# sourceMappingURL=semantic-metadata-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic-metadata-validation.d.ts","sourceRoot":"","sources":["../../src/utils/semantic-metadata-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAoBrB,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,gBAAgB,GACzB,IAAI,CA8BN;AAED,wBAAgB,4BAA4B,CAC1C,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,EACvD,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACnD,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,EAE7D,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,aAAa,CAAC,WAAW,EAAE,SAAS,EAAE,cAAc,CAAC,GAC5D,IAAI,CAuDN"}
@@ -0,0 +1,103 @@
1
+ const MAX_METADATA_ITEMS = 100;
2
+ const MAX_METADATA_TEXT_BYTES = 4_096;
3
+ const SENSITIVITIES = new Set(['public', 'internal', 'confidential', 'restricted']);
4
+ const TIME_GRAINS = new Set(['day', 'week', 'month', 'quarter', 'year']);
5
+ const textEncoder = new TextEncoder();
6
+ function fail(datasetName, location, message) {
7
+ throw new Error(`Invalid dataset "${datasetName}": ${location} ${message}`);
8
+ }
9
+ function validateText(datasetName, location, value) {
10
+ if (typeof value !== 'string')
11
+ fail(datasetName, location, 'must be a string.');
12
+ if (value.trim().length === 0)
13
+ fail(datasetName, location, 'must not be empty.');
14
+ if (textEncoder.encode(value).byteLength > MAX_METADATA_TEXT_BYTES) {
15
+ fail(datasetName, location, `must not exceed ${MAX_METADATA_TEXT_BYTES} UTF-8 bytes.`);
16
+ }
17
+ }
18
+ export function validateSemanticMetadata(datasetName, location, metadata) {
19
+ for (const key of ['examples', 'synonyms']) {
20
+ const values = metadata[key];
21
+ if (values === undefined)
22
+ continue;
23
+ if (!Array.isArray(values) || values.length > MAX_METADATA_ITEMS) {
24
+ fail(datasetName, `${location}.${key}`, `must contain at most ${MAX_METADATA_ITEMS} strings.`);
25
+ }
26
+ if (new Set(values).size !== values.length) {
27
+ fail(datasetName, `${location}.${key}`, 'must not contain duplicates.');
28
+ }
29
+ for (const [index, value] of values.entries()) {
30
+ if (typeof value !== 'string') {
31
+ fail(datasetName, `${location}.${key}[${index}]`, 'must be a string.');
32
+ }
33
+ validateText(datasetName, `${location}.${key}[${index}]`, value);
34
+ }
35
+ }
36
+ for (const key of ['format', 'unit', 'timezone']) {
37
+ const value = metadata[key];
38
+ if (value !== undefined)
39
+ validateText(datasetName, `${location}.${key}`, value);
40
+ }
41
+ if (metadata.currency !== undefined) {
42
+ if (typeof metadata.currency !== 'string' || !/^[A-Z]{3}$/.test(metadata.currency)) {
43
+ fail(datasetName, `${location}.currency`, 'must be a three-letter uppercase currency code.');
44
+ }
45
+ }
46
+ if (metadata.sensitivity !== undefined
47
+ && (typeof metadata.sensitivity !== 'string' || !SENSITIVITIES.has(metadata.sensitivity))) {
48
+ fail(datasetName, `${location}.sensitivity`, 'is not a supported sensitivity classification.');
49
+ }
50
+ }
51
+ export function validateDatasetAgentMetadata(datasetName, config) {
52
+ validateSemanticMetadata(datasetName, 'metadata', config);
53
+ if (config.description !== undefined)
54
+ validateText(datasetName, 'description', config.description);
55
+ if (config.owner !== undefined)
56
+ validateText(datasetName, 'owner', config.owner);
57
+ if (config.freshness !== undefined) {
58
+ if (typeof config.freshness !== 'object' || config.freshness === null
59
+ || !Number.isSafeInteger(config.freshness.maxAgeSeconds)
60
+ || config.freshness.maxAgeSeconds < 1) {
61
+ fail(datasetName, 'freshness.maxAgeSeconds', 'must be a positive safe integer.');
62
+ }
63
+ }
64
+ if (config.defaults !== undefined) {
65
+ if (typeof config.defaults !== 'object' || config.defaults === null) {
66
+ fail(datasetName, 'defaults', 'must be an object.');
67
+ }
68
+ if (config.defaults.dimensions !== undefined && !Array.isArray(config.defaults.dimensions)) {
69
+ fail(datasetName, 'defaults.dimensions', 'must be an array.');
70
+ }
71
+ const dimensions = config.defaults.dimensions ?? [];
72
+ if (dimensions.length > MAX_METADATA_ITEMS) {
73
+ fail(datasetName, 'defaults.dimensions', `must contain at most ${MAX_METADATA_ITEMS} dimensions.`);
74
+ }
75
+ if (new Set(dimensions).size !== dimensions.length) {
76
+ fail(datasetName, 'defaults.dimensions', 'must not contain duplicates.');
77
+ }
78
+ for (const dimension of dimensions) {
79
+ const definition = config.dimensions[dimension];
80
+ if (!definition || definition.groupable === false) {
81
+ fail(datasetName, 'defaults.dimensions', `references non-groupable dimension "${dimension}".`);
82
+ }
83
+ }
84
+ if (config.defaults.timeGrain !== undefined && config.timeKey === undefined) {
85
+ fail(datasetName, 'defaults.timeGrain', 'requires the dataset to define timeKey.');
86
+ }
87
+ if (config.defaults.timeGrain !== undefined && !TIME_GRAINS.has(config.defaults.timeGrain)) {
88
+ fail(datasetName, 'defaults.timeGrain', 'is not a supported time grain.');
89
+ }
90
+ if (dimensions.length === 0 && config.defaults.timeGrain === undefined) {
91
+ fail(datasetName, 'defaults', 'must define dimensions or timeGrain.');
92
+ }
93
+ }
94
+ for (const [name, dimension] of Object.entries(config.dimensions)) {
95
+ validateSemanticMetadata(datasetName, `dimensions.${name}`, dimension);
96
+ }
97
+ for (const [name, measure] of Object.entries(config.measures ?? {})) {
98
+ validateSemanticMetadata(datasetName, `measures.${name}`, measure);
99
+ }
100
+ for (const [name, filter] of Object.entries(config.filters ?? {})) {
101
+ validateSemanticMetadata(datasetName, `filters.${name}`, filter);
102
+ }
103
+ }
@@ -0,0 +1,4 @@
1
+ import type { SemanticMetadata } from '../types.js';
2
+ /** Copy agent metadata without carrying unrelated config fields into public contracts. */
3
+ export declare function snapshotSemanticMetadata(metadata: SemanticMetadata): SemanticMetadata;
4
+ //# sourceMappingURL=semantic-metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic-metadata.d.ts","sourceRoot":"","sources":["../../src/utils/semantic-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,0FAA0F;AAC1F,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,CAUrF"}
@@ -0,0 +1,13 @@
1
+ import { uniqueSorted } from './canonical-json.js';
2
+ /** Copy agent metadata without carrying unrelated config fields into public contracts. */
3
+ export function snapshotSemanticMetadata(metadata) {
4
+ return {
5
+ ...(metadata.examples !== undefined ? { examples: uniqueSorted(metadata.examples) } : {}),
6
+ ...(metadata.synonyms !== undefined ? { synonyms: uniqueSorted(metadata.synonyms) } : {}),
7
+ ...(metadata.format !== undefined ? { format: metadata.format } : {}),
8
+ ...(metadata.unit !== undefined ? { unit: metadata.unit } : {}),
9
+ ...(metadata.currency !== undefined ? { currency: metadata.currency } : {}),
10
+ ...(metadata.timezone !== undefined ? { timezone: metadata.timezone } : {}),
11
+ ...(metadata.sensitivity !== undefined ? { sensitivity: metadata.sensitivity } : {}),
12
+ };
13
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Normalize semantic measure columns to the public wire contract.
3
+ *
4
+ * ClickHouse's JSON formats can return different JavaScript primitives for
5
+ * different aggregate result types. Semantic results expose one stable shape:
6
+ * every non-null measure is a string, while SQL NULL remains null.
7
+ */
8
+ export declare function serializeSemanticMeasureValues<T>(rows: readonly T[], measureNames: readonly string[]): T[];
9
+ //# sourceMappingURL=semantic-result-serialization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic-result-serialization.d.ts","sourceRoot":"","sources":["../../src/utils/semantic-result-serialization.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAAC,CAAC,EAC9C,IAAI,EAAE,SAAS,CAAC,EAAE,EAClB,YAAY,EAAE,SAAS,MAAM,EAAE,GAC9B,CAAC,EAAE,CAkBL"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Normalize semantic measure columns to the public wire contract.
3
+ *
4
+ * ClickHouse's JSON formats can return different JavaScript primitives for
5
+ * different aggregate result types. Semantic results expose one stable shape:
6
+ * every non-null measure is a string, while SQL NULL remains null.
7
+ */
8
+ export function serializeSemanticMeasureValues(rows, measureNames) {
9
+ return rows.map((row) => {
10
+ if (row === null || typeof row !== 'object') {
11
+ return row;
12
+ }
13
+ const serialized = { ...row };
14
+ for (const name of measureNames) {
15
+ const value = serialized[name];
16
+ if (typeof value === 'number' && !Number.isFinite(value)) {
17
+ // ClickHouse JSON output emits NaN and infinities as null by default.
18
+ serialized[name] = null;
19
+ }
20
+ else if (value != null) {
21
+ serialized[name] = String(value);
22
+ }
23
+ }
24
+ return serialized;
25
+ });
26
+ }
@@ -0,0 +1,5 @@
1
+ import type { ZodTypeAny } from 'zod';
2
+ export declare function semanticToolNamePart(name: string): string;
3
+ export declare function parseCanonicalToolInput(schema: ZodTypeAny, input: Record<string, unknown>, label: string): Record<string, unknown>;
4
+ export declare function redactSemanticToolSql(result: unknown, includeSql: boolean): unknown;
5
+ //# sourceMappingURL=semantic-tool.d.ts.map