@hypequery/datasets 0.13.6 → 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.
- package/README.md +10 -0
- package/dist/agent-catalog.d.ts +97 -0
- package/dist/agent-catalog.d.ts.map +1 -0
- package/dist/agent-catalog.js +79 -0
- package/dist/api.type-test.js +19 -1
- package/dist/catalog.d.ts +17 -6
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +18 -1
- package/dist/contract.d.ts +11 -7
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +22 -1
- package/dist/dataset-query.js +1 -1
- package/dist/dataset.d.ts.map +1 -1
- package/dist/dataset.js +19 -2
- package/dist/executor.d.ts +6 -0
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +55 -23
- package/dist/field.d.ts.map +1 -1
- package/dist/field.js +2 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/measure.d.ts.map +1 -1
- package/dist/measure.js +4 -0
- package/dist/protocol-adapter.d.ts.map +1 -1
- package/dist/protocol-adapter.js +21 -0
- package/dist/protocol-rehydrate.d.ts +46 -0
- package/dist/protocol-rehydrate.d.ts.map +1 -0
- package/dist/protocol-rehydrate.js +218 -0
- package/dist/publishing/features/dataset-publication.d.ts +10 -0
- package/dist/publishing/features/dataset-publication.d.ts.map +1 -0
- package/dist/publishing/features/dataset-publication.js +34 -0
- package/dist/publishing/node.d.ts +16 -0
- package/dist/publishing/node.d.ts.map +1 -0
- package/dist/publishing/node.js +35 -0
- package/dist/publishing/publisher.d.ts +17 -0
- package/dist/publishing/publisher.d.ts.map +1 -0
- package/dist/publishing/publisher.js +32 -0
- package/dist/publishing/types.d.ts +31 -0
- package/dist/publishing/types.d.ts.map +1 -0
- package/dist/publishing/types.js +1 -0
- package/dist/query-builder-protocol.d.ts +6 -2
- package/dist/query-builder-protocol.d.ts.map +1 -1
- package/dist/semantic-plan.d.ts +3 -1
- package/dist/semantic-plan.d.ts.map +1 -1
- package/dist/semantic-planner.d.ts.map +1 -1
- package/dist/semantic-planner.js +4 -0
- package/dist/semantic-query-schema.d.ts +48 -0
- package/dist/semantic-query-schema.d.ts.map +1 -0
- package/dist/semantic-query-schema.js +218 -0
- package/dist/sql-utils.d.ts +36 -0
- package/dist/sql-utils.d.ts.map +1 -1
- package/dist/sql-utils.js +76 -0
- package/dist/tools.d.ts +5 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +25 -267
- package/dist/types.d.ts +98 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/agent-catalog-projection.d.ts +78 -0
- package/dist/utils/agent-catalog-projection.d.ts.map +1 -0
- package/dist/utils/agent-catalog-projection.js +179 -0
- package/dist/utils/dataset-cache-policy.d.ts +34 -0
- package/dist/utils/dataset-cache-policy.d.ts.map +1 -0
- package/dist/utils/dataset-cache-policy.js +50 -0
- package/dist/utils/dataset-contract.d.ts +2 -2
- package/dist/utils/dataset-contract.d.ts.map +1 -1
- package/dist/utils/dataset-contract.js +3 -1
- package/dist/utils/dataset-definition-validation.d.ts +35 -0
- package/dist/utils/dataset-definition-validation.d.ts.map +1 -0
- package/dist/utils/dataset-definition-validation.js +215 -0
- package/dist/utils/dataset-metric-ref.d.ts +2 -2
- package/dist/utils/dataset-metric-ref.d.ts.map +1 -1
- package/dist/utils/dataset-metric-ref.js +5 -3
- package/dist/utils/metric-alias.d.ts +4 -0
- package/dist/utils/metric-alias.d.ts.map +1 -0
- package/dist/utils/metric-alias.js +28 -0
- package/dist/utils/protocol-metric-capabilities.d.ts +7 -0
- package/dist/utils/protocol-metric-capabilities.d.ts.map +1 -0
- package/dist/utils/protocol-metric-capabilities.js +57 -0
- package/dist/utils/protocol-rehydrate-filters.d.ts +10 -0
- package/dist/utils/protocol-rehydrate-filters.d.ts.map +1 -0
- package/dist/utils/protocol-rehydrate-filters.js +36 -0
- package/dist/utils/protocol-semantic-metadata.d.ts +5 -0
- package/dist/utils/protocol-semantic-metadata.d.ts.map +1 -0
- package/dist/utils/protocol-semantic-metadata.js +5 -0
- package/dist/utils/published-name.d.ts +3 -0
- package/dist/utils/published-name.d.ts.map +1 -0
- package/dist/utils/published-name.js +7 -0
- package/dist/utils/published-relationships.d.ts +34 -0
- package/dist/utils/published-relationships.d.ts.map +1 -0
- package/dist/utils/published-relationships.js +35 -0
- package/dist/utils/relationship-fields.d.ts +7 -0
- package/dist/utils/relationship-fields.d.ts.map +1 -1
- package/dist/utils/relationship-fields.js +13 -1
- package/dist/utils/result-limits.d.ts +34 -0
- package/dist/utils/result-limits.d.ts.map +1 -0
- package/dist/utils/result-limits.js +40 -0
- package/dist/utils/semantic-metadata-validation.d.ts +4 -0
- package/dist/utils/semantic-metadata-validation.d.ts.map +1 -0
- package/dist/utils/semantic-metadata-validation.js +103 -0
- package/dist/utils/semantic-metadata.d.ts +4 -0
- package/dist/utils/semantic-metadata.d.ts.map +1 -0
- package/dist/utils/semantic-metadata.js +13 -0
- package/dist/utils/semantic-tool.d.ts +5 -0
- package/dist/utils/semantic-tool.d.ts.map +1 -0
- package/dist/utils/semantic-tool.js +29 -0
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -56,10 +56,14 @@ const result = await analytics.execute(
|
|
|
56
56
|
},
|
|
57
57
|
{
|
|
58
58
|
runtime: { tenant: session.accountId },
|
|
59
|
+
abortSignal: request.signal,
|
|
59
60
|
},
|
|
60
61
|
);
|
|
61
62
|
```
|
|
62
63
|
|
|
64
|
+
Passing an `AbortSignal` cancels the semantic query and forwards cancellation to
|
|
65
|
+
the backing query builder/database request.
|
|
66
|
+
|
|
63
67
|
The same definition can become:
|
|
64
68
|
|
|
65
69
|
- a named KPI or flexible dataset query;
|
|
@@ -68,6 +72,12 @@ The same definition can become:
|
|
|
68
72
|
- an OpenAI, AI SDK, or MCP tool schema;
|
|
69
73
|
- a stable semantic contract for CI and deployment.
|
|
70
74
|
|
|
75
|
+
`buildCanonicalSemanticQuerySchemas()` compiles the same catalog into strict
|
|
76
|
+
dataset and metric Zod validators, JSON Schemas, and a deterministic manifest
|
|
77
|
+
hash. It accepts either live local Dataset definitions or an already-normalized
|
|
78
|
+
catalog from a hosted activation, so Serve and MCP do not maintain separate
|
|
79
|
+
field or operator lists.
|
|
80
|
+
|
|
71
81
|
## Analytics teams actually need
|
|
72
82
|
|
|
73
83
|
- sums, counts, distinct counts, averages, min, and max;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { ProtocolDeploymentContract } from '@hypequery/protocol';
|
|
2
|
+
import { type DatasetCatalog, type DatasetCatalogMap, type DatasetCatalogSource } from './catalog.js';
|
|
3
|
+
import type { SemanticContract } from './contract.js';
|
|
4
|
+
import type { DatasetDefaults, DatasetFreshness, DatasetLimits, FieldType, SemanticMetadata } from './types.js';
|
|
5
|
+
export interface AgentCatalogDimension extends SemanticMetadata {
|
|
6
|
+
name: string;
|
|
7
|
+
type: FieldType;
|
|
8
|
+
label?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
filterable: boolean;
|
|
11
|
+
groupable: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface AgentCatalogMeasure extends SemanticMetadata {
|
|
14
|
+
name: string;
|
|
15
|
+
label?: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface AgentCatalogMetric extends SemanticMetadata {
|
|
19
|
+
name: string;
|
|
20
|
+
label?: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
dimensions: string[];
|
|
23
|
+
filters: string[];
|
|
24
|
+
grains: string[];
|
|
25
|
+
grain?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface AgentCatalogFilter extends SemanticMetadata {
|
|
28
|
+
name: string;
|
|
29
|
+
type: FieldType;
|
|
30
|
+
label?: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
operators: string[];
|
|
33
|
+
}
|
|
34
|
+
export interface AgentCatalogRelationship {
|
|
35
|
+
name: string;
|
|
36
|
+
target: string;
|
|
37
|
+
fields: string[];
|
|
38
|
+
}
|
|
39
|
+
export interface AgentCatalogDataset extends SemanticMetadata {
|
|
40
|
+
name: string;
|
|
41
|
+
description: string;
|
|
42
|
+
freshness?: DatasetFreshness;
|
|
43
|
+
owner?: string;
|
|
44
|
+
defaults?: DatasetDefaults;
|
|
45
|
+
timeDimension: string | null;
|
|
46
|
+
dimensions: AgentCatalogDimension[];
|
|
47
|
+
measures: AgentCatalogMeasure[];
|
|
48
|
+
metrics: AgentCatalogMetric[];
|
|
49
|
+
filters: AgentCatalogFilter[];
|
|
50
|
+
relationships: AgentCatalogRelationship[];
|
|
51
|
+
limits: DatasetLimits;
|
|
52
|
+
}
|
|
53
|
+
export interface AgentSafeCatalog {
|
|
54
|
+
datasets: AgentCatalogDataset[];
|
|
55
|
+
}
|
|
56
|
+
export interface AgentCatalogProjectionOptions {
|
|
57
|
+
/** Maximum UTF-8 bytes in the complete safe catalog. Defaults to 256 KiB. */
|
|
58
|
+
maxCatalogBytes?: number;
|
|
59
|
+
}
|
|
60
|
+
export declare const DEFAULT_AGENT_CATALOG_MAX_BYTES: number;
|
|
61
|
+
export type AgentCatalogDatasetRegistry = Readonly<Record<string, DatasetCatalogSource | DatasetCatalog>>;
|
|
62
|
+
export type AgentCatalogSource = AgentCatalogDatasetRegistry | SemanticContract | ProtocolDeploymentContract;
|
|
63
|
+
/**
|
|
64
|
+
* Proof supplied by an owning application after it authorizes a trusted-debug
|
|
65
|
+
* request. The datasets package deliberately does not manufacture this value:
|
|
66
|
+
* authentication and authorization belong to the calling transport/runtime.
|
|
67
|
+
*/
|
|
68
|
+
export interface TrustedDebugCatalogAuthorization {
|
|
69
|
+
readonly authorized: true;
|
|
70
|
+
}
|
|
71
|
+
export type TrustedDebugCatalog = {
|
|
72
|
+
readonly kind: 'dataset-catalog';
|
|
73
|
+
readonly datasets: DatasetCatalogMap;
|
|
74
|
+
} | {
|
|
75
|
+
readonly kind: 'semantic-contract';
|
|
76
|
+
readonly contract: SemanticContract;
|
|
77
|
+
} | {
|
|
78
|
+
readonly kind: 'deployment-contract';
|
|
79
|
+
readonly contract: ProtocolDeploymentContract;
|
|
80
|
+
};
|
|
81
|
+
/** Build the deterministic logical catalog that may safely be shown to an agent. */
|
|
82
|
+
export declare function projectAgentSafeCatalog(source: AgentCatalogSource, options?: AgentCatalogProjectionOptions): AgentSafeCatalog;
|
|
83
|
+
/**
|
|
84
|
+
* Enforce the agent-safe catalog byte budget.
|
|
85
|
+
*
|
|
86
|
+
* Exported so an adapter that builds a projection by another route — the MCP
|
|
87
|
+
* legacy registry fallback is the only one today — gets the same guarantee as
|
|
88
|
+
* `projectAgentSafeCatalog` rather than serializing an unbounded catalog and
|
|
89
|
+
* failing later against an unrelated response limit.
|
|
90
|
+
*/
|
|
91
|
+
export declare function assertAgentSafeCatalogBudget(catalog: AgentSafeCatalog, options?: AgentCatalogProjectionOptions): void;
|
|
92
|
+
/**
|
|
93
|
+
* Return physical diagnostics only after the owning application has performed
|
|
94
|
+
* a separate authorization check. Never place this projection in model context.
|
|
95
|
+
*/
|
|
96
|
+
export declare function projectTrustedDebugCatalog(source: AgentCatalogSource, authorization: TrustedDebugCatalogAuthorization): TrustedDebugCatalog;
|
|
97
|
+
//# sourceMappingURL=agent-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-catalog.d.ts","sourceRoot":"","sources":["../src/agent-catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { getDatasetCatalog, } from './catalog.js';
|
|
2
|
+
import { compareStrings } from './utils/canonical-json.js';
|
|
3
|
+
import { protocolDatasetToAgentDataset, recordDatasetToAgentDataset, } from './utils/agent-catalog-projection.js';
|
|
4
|
+
export const DEFAULT_AGENT_CATALOG_MAX_BYTES = 256 * 1024;
|
|
5
|
+
function isProtocolDeploymentContract(source) {
|
|
6
|
+
return 'kind' in source && source.kind === 'hypequery-deployment';
|
|
7
|
+
}
|
|
8
|
+
function isSemanticContract(source) {
|
|
9
|
+
return 'contentHash' in source && typeof source.contentHash === 'string';
|
|
10
|
+
}
|
|
11
|
+
/** Build the deterministic logical catalog that may safely be shown to an agent. */
|
|
12
|
+
export function projectAgentSafeCatalog(source, options = {}) {
|
|
13
|
+
let catalog;
|
|
14
|
+
if (isProtocolDeploymentContract(source)) {
|
|
15
|
+
const datasets = new Map(source.datasets.map(dataset => [dataset.name, dataset]));
|
|
16
|
+
catalog = {
|
|
17
|
+
datasets: source.datasets
|
|
18
|
+
.map(dataset => protocolDatasetToAgentDataset(dataset, datasets))
|
|
19
|
+
.sort((left, right) => compareStrings(left.name, right.name)),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
else if (isSemanticContract(source)) {
|
|
23
|
+
catalog = {
|
|
24
|
+
datasets: Object.values(source.datasets)
|
|
25
|
+
.map(recordDatasetToAgentDataset)
|
|
26
|
+
.sort((left, right) => compareStrings(left.name, right.name)),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
catalog = {
|
|
31
|
+
datasets: Object.entries(source)
|
|
32
|
+
.map(([, dataset]) => ('requiresTenant' in dataset ? dataset : getDatasetCatalog(dataset)))
|
|
33
|
+
.map(recordDatasetToAgentDataset)
|
|
34
|
+
.sort((left, right) => compareStrings(left.name, right.name)),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
assertAgentSafeCatalogBudget(catalog, options);
|
|
38
|
+
return catalog;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Enforce the agent-safe catalog byte budget.
|
|
42
|
+
*
|
|
43
|
+
* Exported so an adapter that builds a projection by another route — the MCP
|
|
44
|
+
* legacy registry fallback is the only one today — gets the same guarantee as
|
|
45
|
+
* `projectAgentSafeCatalog` rather than serializing an unbounded catalog and
|
|
46
|
+
* failing later against an unrelated response limit.
|
|
47
|
+
*/
|
|
48
|
+
export function assertAgentSafeCatalogBudget(catalog, options = {}) {
|
|
49
|
+
const maxBytes = options.maxCatalogBytes ?? DEFAULT_AGENT_CATALOG_MAX_BYTES;
|
|
50
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) {
|
|
51
|
+
throw new RangeError('maxCatalogBytes must be a positive safe integer.');
|
|
52
|
+
}
|
|
53
|
+
const byteLength = new TextEncoder().encode(JSON.stringify(catalog)).byteLength;
|
|
54
|
+
if (byteLength > maxBytes) {
|
|
55
|
+
throw new RangeError(`Agent-safe catalog exceeds the ${maxBytes}-byte limit.`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Return physical diagnostics only after the owning application has performed
|
|
60
|
+
* a separate authorization check. Never place this projection in model context.
|
|
61
|
+
*/
|
|
62
|
+
export function projectTrustedDebugCatalog(source, authorization) {
|
|
63
|
+
if (authorization?.authorized !== true) {
|
|
64
|
+
throw new Error('Trusted debug catalog access requires explicit authorization.');
|
|
65
|
+
}
|
|
66
|
+
if (isProtocolDeploymentContract(source)) {
|
|
67
|
+
return { kind: 'deployment-contract', contract: source };
|
|
68
|
+
}
|
|
69
|
+
if (isSemanticContract(source)) {
|
|
70
|
+
return { kind: 'semantic-contract', contract: source };
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
kind: 'dataset-catalog',
|
|
74
|
+
datasets: Object.fromEntries(Object.entries(source).map(([name, dataset]) => [
|
|
75
|
+
name,
|
|
76
|
+
'requiresTenant' in dataset ? dataset : getDatasetCatalog(dataset),
|
|
77
|
+
])),
|
|
78
|
+
};
|
|
79
|
+
}
|
package/dist/api.type-test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { add, dataset,
|
|
1
|
+
import { add, between, buildCanonicalSemanticQuerySchemas, createDatasetClient, dataset, desc, dimension, eq, measure, publishDatasets, } from './index.js';
|
|
2
2
|
const Orders = dataset('orders', {
|
|
3
3
|
source: 'orders',
|
|
4
4
|
tenantKey: 'tenant_id',
|
|
@@ -34,6 +34,20 @@ const averageRevenueMetric = Orders.metric('averageRevenueMetric', {
|
|
|
34
34
|
formula: ({ revenue, completedRevenue }) => add(revenue, completedRevenue),
|
|
35
35
|
});
|
|
36
36
|
const customerCountMetric = Customers.metric('customerCountMetric', { measure: 'customerCount' });
|
|
37
|
+
const _publishedDatasets = publishDatasets()
|
|
38
|
+
.publish(Orders, { metrics: { revenue: revenueMetric } })
|
|
39
|
+
.publish(Customers, {
|
|
40
|
+
alias: 'accounts',
|
|
41
|
+
metrics: { customers: customerCountMetric },
|
|
42
|
+
})
|
|
43
|
+
.build();
|
|
44
|
+
// @ts-expect-error published metrics must belong to the dataset being published.
|
|
45
|
+
publishDatasets().publish(Orders, { metrics: { customerCountMetric } });
|
|
46
|
+
const canonicalSchemas = buildCanonicalSemanticQuerySchemas({
|
|
47
|
+
orders: { ...Orders, metrics: { revenueMetric } },
|
|
48
|
+
});
|
|
49
|
+
canonicalSchemas.queryDataset.parse({ dataset: 'orders', measures: ['revenue'] });
|
|
50
|
+
const _manifestHash = canonicalSchemas.manifestHash;
|
|
37
51
|
const _statusFilter = eq('status', 'completed');
|
|
38
52
|
const _createdAtRange = between('createdAt', '2025-01-01', '2025-01-31');
|
|
39
53
|
const _revenueSort = desc('revenueMetric');
|
|
@@ -56,6 +70,10 @@ const runtimeContext = {
|
|
|
56
70
|
tenant: 'tenant-1',
|
|
57
71
|
},
|
|
58
72
|
};
|
|
73
|
+
const abortableContext = {
|
|
74
|
+
abortSignal: new AbortController().signal,
|
|
75
|
+
};
|
|
76
|
+
void abortableContext;
|
|
59
77
|
const _legacyTenantRuntimeContext = {
|
|
60
78
|
runtime: {
|
|
61
79
|
tenant: { id: 'tenant-1' },
|
package/dist/catalog.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AnyDatasetInstance, DimensionDefinition, MeasureDefinition, MetricHandle, RelationshipDefinition, SemanticFilterDefinition, DatasetLimits, TimeGrain } from './types.js';
|
|
2
|
-
export interface DimensionCatalogEntry {
|
|
1
|
+
import type { AnyDatasetInstance, DimensionDefinition, MeasureDefinition, MetricHandle, RelationshipDefinition, SemanticFilterDefinition, DatasetLimits, DatasetDefaults, DatasetFreshness, SemanticMetadata, TimeGrain } from './types.js';
|
|
2
|
+
export interface DimensionCatalogEntry extends SemanticMetadata {
|
|
3
3
|
type: DimensionDefinition['fieldType'];
|
|
4
4
|
column?: string;
|
|
5
5
|
sql?: string;
|
|
@@ -8,7 +8,7 @@ export interface DimensionCatalogEntry {
|
|
|
8
8
|
filterable: boolean;
|
|
9
9
|
groupable: boolean;
|
|
10
10
|
}
|
|
11
|
-
export interface MeasureCatalogEntry {
|
|
11
|
+
export interface MeasureCatalogEntry extends SemanticMetadata {
|
|
12
12
|
aggregation: MeasureDefinition['aggregation'];
|
|
13
13
|
field: string;
|
|
14
14
|
/** Second column for argMax/argMin. */
|
|
@@ -20,14 +20,14 @@ export interface MeasureCatalogEntry {
|
|
|
20
20
|
description?: string;
|
|
21
21
|
filterCount: number;
|
|
22
22
|
}
|
|
23
|
-
export interface FilterCatalogEntry {
|
|
23
|
+
export interface FilterCatalogEntry extends SemanticMetadata {
|
|
24
24
|
field: string;
|
|
25
25
|
label?: string;
|
|
26
26
|
description?: string;
|
|
27
27
|
operators: SemanticFilterDefinition['operators'];
|
|
28
28
|
valueType?: DimensionDefinition['fieldType'];
|
|
29
29
|
}
|
|
30
|
-
export interface MetricCatalogEntry {
|
|
30
|
+
export interface MetricCatalogEntry extends SemanticMetadata {
|
|
31
31
|
kind: ReturnType<MetricHandle['contract']>['kind'];
|
|
32
32
|
dataset: string;
|
|
33
33
|
valueType: 'number';
|
|
@@ -47,10 +47,19 @@ export interface RelationshipCatalogEntry {
|
|
|
47
47
|
to: string;
|
|
48
48
|
queryable: boolean;
|
|
49
49
|
fields: string[];
|
|
50
|
+
/**
|
|
51
|
+
* Subset of `fields` usable as a grouping key. Optional so an externally
|
|
52
|
+
* supplied catalog that predates it still resolves, falling back to `fields`.
|
|
53
|
+
*/
|
|
54
|
+
groupableFields?: string[];
|
|
50
55
|
}
|
|
51
|
-
export interface DatasetCatalog {
|
|
56
|
+
export interface DatasetCatalog extends SemanticMetadata {
|
|
52
57
|
name: string;
|
|
58
|
+
description?: string;
|
|
53
59
|
source: string;
|
|
60
|
+
freshness?: DatasetFreshness;
|
|
61
|
+
owner?: string;
|
|
62
|
+
defaults?: DatasetDefaults;
|
|
54
63
|
tenantKey?: string;
|
|
55
64
|
timeKey?: string;
|
|
56
65
|
dimensions: Record<string, DimensionCatalogEntry>;
|
|
@@ -70,6 +79,8 @@ export type DatasetCatalogSource = AnyDatasetInstance & {
|
|
|
70
79
|
};
|
|
71
80
|
/** All queryable relationship field names (`<relationship>.<dimension>`) a catalog advertises. */
|
|
72
81
|
export declare function getQueryableRelationshipFields(catalog: DatasetCatalog): string[];
|
|
82
|
+
/** The queryable relationship fields a catalog also allows as grouping keys. */
|
|
83
|
+
export declare function getGroupableRelationshipFields(catalog: DatasetCatalog): string[];
|
|
73
84
|
export declare function getDatasetCatalog(dataset: DatasetCatalogSource): DatasetCatalog;
|
|
74
85
|
export declare function getDatasetCatalogs(datasets: Record<string, DatasetCatalogSource>): DatasetCatalogMap;
|
|
75
86
|
//# sourceMappingURL=catalog.d.ts.map
|
package/dist/catalog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,sBAAsB,EACtB,wBAAwB,EACxB,aAAa,EACb,SAAS,EACV,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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"}
|
package/dist/catalog.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SEMANTIC_FILTER_OPERATORS, SUPPORTED_TIME_GRAINS } from './constants.js';
|
|
2
|
-
import { listQueryableRelationshipFields } from './utils/relationship-fields.js';
|
|
2
|
+
import { listGroupableRelationshipFields, listQueryableRelationshipFields, } from './utils/relationship-fields.js';
|
|
3
|
+
import { snapshotSemanticMetadata } from './utils/semantic-metadata.js';
|
|
3
4
|
function dimensionToCatalog(dimension) {
|
|
4
5
|
return {
|
|
5
6
|
type: dimension.fieldType,
|
|
@@ -7,6 +8,7 @@ function dimensionToCatalog(dimension) {
|
|
|
7
8
|
sql: dimension.sql,
|
|
8
9
|
label: dimension.label,
|
|
9
10
|
description: dimension.description,
|
|
11
|
+
...snapshotSemanticMetadata(dimension),
|
|
10
12
|
filterable: dimension.filterable !== false,
|
|
11
13
|
groupable: dimension.groupable !== false,
|
|
12
14
|
};
|
|
@@ -20,6 +22,7 @@ function measureToCatalog(measure) {
|
|
|
20
22
|
sql: measure.sql,
|
|
21
23
|
label: measure.label,
|
|
22
24
|
description: measure.description,
|
|
25
|
+
...snapshotSemanticMetadata(measure),
|
|
23
26
|
filterCount: measure.filters?.length ?? 0,
|
|
24
27
|
};
|
|
25
28
|
}
|
|
@@ -28,6 +31,7 @@ function filterToCatalog(filter, dimensions) {
|
|
|
28
31
|
field: filter.field,
|
|
29
32
|
label: filter.label,
|
|
30
33
|
description: filter.description,
|
|
34
|
+
...snapshotSemanticMetadata(filter),
|
|
31
35
|
operators: filter.operators ? [...filter.operators] : [...SEMANTIC_FILTER_OPERATORS],
|
|
32
36
|
valueType: dimensions[filter.field]?.fieldType,
|
|
33
37
|
};
|
|
@@ -40,6 +44,7 @@ function metricToCatalog(metric) {
|
|
|
40
44
|
valueType: contract.valueType,
|
|
41
45
|
label: contract.label,
|
|
42
46
|
description: contract.description,
|
|
47
|
+
...snapshotSemanticMetadata(contract),
|
|
43
48
|
dimensions: contract.dimensions,
|
|
44
49
|
measures: contract.measures,
|
|
45
50
|
filters: contract.filters,
|
|
@@ -56,6 +61,7 @@ function relationshipToCatalog(name, relationship) {
|
|
|
56
61
|
to: relationship.to,
|
|
57
62
|
queryable: relationship.kind !== 'hasMany',
|
|
58
63
|
fields: listQueryableRelationshipFields(name, relationship),
|
|
64
|
+
groupableFields: listGroupableRelationshipFields(name, relationship),
|
|
59
65
|
};
|
|
60
66
|
}
|
|
61
67
|
/** All queryable relationship field names (`<relationship>.<dimension>`) a catalog advertises. */
|
|
@@ -64,6 +70,12 @@ export function getQueryableRelationshipFields(catalog) {
|
|
|
64
70
|
.filter(relationship => relationship.queryable)
|
|
65
71
|
.flatMap(relationship => relationship.fields);
|
|
66
72
|
}
|
|
73
|
+
/** The queryable relationship fields a catalog also allows as grouping keys. */
|
|
74
|
+
export function getGroupableRelationshipFields(catalog) {
|
|
75
|
+
return Object.values(catalog.relationships)
|
|
76
|
+
.filter(relationship => relationship.queryable)
|
|
77
|
+
.flatMap(relationship => relationship.groupableFields ?? relationship.fields);
|
|
78
|
+
}
|
|
67
79
|
export function getDatasetCatalog(dataset) {
|
|
68
80
|
const dimensionNames = Object.keys(dataset.dimensions);
|
|
69
81
|
const measureNames = Object.keys(dataset.measures);
|
|
@@ -76,7 +88,12 @@ export function getDatasetCatalog(dataset) {
|
|
|
76
88
|
]));
|
|
77
89
|
return {
|
|
78
90
|
name: dataset.name,
|
|
91
|
+
description: dataset.description,
|
|
92
|
+
...snapshotSemanticMetadata(dataset),
|
|
79
93
|
source: dataset.source,
|
|
94
|
+
freshness: dataset.freshness,
|
|
95
|
+
owner: dataset.owner,
|
|
96
|
+
defaults: dataset.defaults,
|
|
80
97
|
tenantKey: dataset.tenantKey,
|
|
81
98
|
timeKey: dataset.timeKey,
|
|
82
99
|
dimensions: Object.fromEntries(Object.entries(dataset.dimensions).map(([name, dimension]) => [
|
package/dist/contract.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { type DatasetCatalogSource, type DimensionCatalogEntry, type MeasureCatalogEntry, type MetricCatalogEntry, type FilterCatalogEntry, type RelationshipCatalogEntry } from './catalog.js';
|
|
2
|
-
import type { DatasetLimits } from './types.js';
|
|
2
|
+
import type { DatasetDefaults, DatasetFreshness, DatasetLimits, SemanticMetadata } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Version of the semantic contract format. Bump when the serialized shape
|
|
5
5
|
* changes in a way that snapshot consumers must account for.
|
|
6
6
|
*/
|
|
7
|
-
export declare const SEMANTIC_CONTRACT_VERSION =
|
|
8
|
-
export interface ContractDimension {
|
|
7
|
+
export declare const SEMANTIC_CONTRACT_VERSION = 3;
|
|
8
|
+
export interface ContractDimension extends SemanticMetadata {
|
|
9
9
|
type: DimensionCatalogEntry['type'];
|
|
10
10
|
column?: string;
|
|
11
11
|
sql?: string;
|
|
@@ -14,7 +14,7 @@ export interface ContractDimension {
|
|
|
14
14
|
filterable: boolean;
|
|
15
15
|
groupable: boolean;
|
|
16
16
|
}
|
|
17
|
-
export interface ContractMeasure {
|
|
17
|
+
export interface ContractMeasure extends SemanticMetadata {
|
|
18
18
|
aggregation: MeasureCatalogEntry['aggregation'];
|
|
19
19
|
field: string;
|
|
20
20
|
/** Second column for argMax/argMin. */
|
|
@@ -25,7 +25,7 @@ export interface ContractMeasure {
|
|
|
25
25
|
label?: string;
|
|
26
26
|
description?: string;
|
|
27
27
|
}
|
|
28
|
-
export interface ContractMetric {
|
|
28
|
+
export interface ContractMetric extends SemanticMetadata {
|
|
29
29
|
kind: MetricCatalogEntry['kind'];
|
|
30
30
|
valueType: MetricCatalogEntry['valueType'];
|
|
31
31
|
label?: string;
|
|
@@ -37,7 +37,7 @@ export interface ContractMetric {
|
|
|
37
37
|
grain?: string;
|
|
38
38
|
requires?: string[];
|
|
39
39
|
}
|
|
40
|
-
export interface ContractFilter {
|
|
40
|
+
export interface ContractFilter extends SemanticMetadata {
|
|
41
41
|
field: string;
|
|
42
42
|
label?: string;
|
|
43
43
|
description?: string;
|
|
@@ -52,9 +52,13 @@ export interface ContractRelationship {
|
|
|
52
52
|
queryable: boolean;
|
|
53
53
|
fields: string[];
|
|
54
54
|
}
|
|
55
|
-
export interface ContractDataset {
|
|
55
|
+
export interface ContractDataset extends SemanticMetadata {
|
|
56
56
|
name: string;
|
|
57
|
+
description?: string;
|
|
57
58
|
source: string;
|
|
59
|
+
freshness?: DatasetFreshness;
|
|
60
|
+
owner?: string;
|
|
61
|
+
defaults?: DatasetDefaults;
|
|
58
62
|
tenantKey?: string;
|
|
59
63
|
timeKey?: string;
|
|
60
64
|
requiresTenant: boolean;
|
package/dist/contract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC9B,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
|
package/dist/contract.js
CHANGED
|
@@ -2,11 +2,12 @@ import { sha256 } from '@noble/hashes/sha2';
|
|
|
2
2
|
import { bytesToHex } from '@noble/hashes/utils';
|
|
3
3
|
import { getDatasetCatalogs, } from './catalog.js';
|
|
4
4
|
import { sortedRecord, uniqueSorted } from './utils/canonical-json.js';
|
|
5
|
+
import { snapshotSemanticMetadata } from './utils/semantic-metadata.js';
|
|
5
6
|
/**
|
|
6
7
|
* Version of the semantic contract format. Bump when the serialized shape
|
|
7
8
|
* changes in a way that snapshot consumers must account for.
|
|
8
9
|
*/
|
|
9
|
-
export const SEMANTIC_CONTRACT_VERSION =
|
|
10
|
+
export const SEMANTIC_CONTRACT_VERSION = 3;
|
|
10
11
|
/**
|
|
11
12
|
* Builds a deterministic semantic contract from dataset instances.
|
|
12
13
|
*
|
|
@@ -31,7 +32,23 @@ export function serializeSemanticContract(datasets, options = {}) {
|
|
|
31
32
|
function datasetToContract(catalog, includeSql) {
|
|
32
33
|
return {
|
|
33
34
|
name: catalog.name,
|
|
35
|
+
...(catalog.description !== undefined ? { description: catalog.description } : {}),
|
|
36
|
+
...snapshotSemanticMetadata(catalog),
|
|
34
37
|
source: catalog.source,
|
|
38
|
+
...(catalog.freshness !== undefined ? { freshness: { ...catalog.freshness } } : {}),
|
|
39
|
+
...(catalog.owner !== undefined ? { owner: catalog.owner } : {}),
|
|
40
|
+
...(catalog.defaults !== undefined
|
|
41
|
+
? {
|
|
42
|
+
defaults: {
|
|
43
|
+
...(catalog.defaults.dimensions !== undefined
|
|
44
|
+
? { dimensions: uniqueSorted(catalog.defaults.dimensions) }
|
|
45
|
+
: {}),
|
|
46
|
+
...(catalog.defaults.timeGrain !== undefined
|
|
47
|
+
? { timeGrain: catalog.defaults.timeGrain }
|
|
48
|
+
: {}),
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
: {}),
|
|
35
52
|
...(catalog.tenantKey !== undefined ? { tenantKey: catalog.tenantKey } : {}),
|
|
36
53
|
...(catalog.timeKey !== undefined ? { timeKey: catalog.timeKey } : {}),
|
|
37
54
|
requiresTenant: catalog.requiresTenant,
|
|
@@ -64,6 +81,7 @@ function dimensionToContract(entry, includeSql) {
|
|
|
64
81
|
...(includeSql && entry.sql !== undefined ? { sql: normalizeSql(entry.sql) } : {}),
|
|
65
82
|
...(entry.label !== undefined ? { label: entry.label } : {}),
|
|
66
83
|
...(entry.description !== undefined ? { description: entry.description } : {}),
|
|
84
|
+
...snapshotSemanticMetadata(entry),
|
|
67
85
|
filterable: entry.filterable,
|
|
68
86
|
groupable: entry.groupable,
|
|
69
87
|
};
|
|
@@ -77,6 +95,7 @@ function measureToContract(entry, includeSql) {
|
|
|
77
95
|
...(includeSql && entry.sql !== undefined ? { sql: normalizeSql(entry.sql) } : {}),
|
|
78
96
|
...(entry.label !== undefined ? { label: entry.label } : {}),
|
|
79
97
|
...(entry.description !== undefined ? { description: entry.description } : {}),
|
|
98
|
+
...snapshotSemanticMetadata(entry),
|
|
80
99
|
};
|
|
81
100
|
}
|
|
82
101
|
function metricToContract(entry) {
|
|
@@ -85,6 +104,7 @@ function metricToContract(entry) {
|
|
|
85
104
|
valueType: entry.valueType,
|
|
86
105
|
...(entry.label !== undefined ? { label: entry.label } : {}),
|
|
87
106
|
...(entry.description !== undefined ? { description: entry.description } : {}),
|
|
107
|
+
...snapshotSemanticMetadata(entry),
|
|
88
108
|
dimensions: uniqueSorted(entry.dimensions),
|
|
89
109
|
...(entry.measures !== undefined ? { measures: uniqueSorted(entry.measures) } : {}),
|
|
90
110
|
filters: uniqueSorted(entry.filters),
|
|
@@ -98,6 +118,7 @@ function filterToContract(entry) {
|
|
|
98
118
|
field: entry.field,
|
|
99
119
|
...(entry.label !== undefined ? { label: entry.label } : {}),
|
|
100
120
|
...(entry.description !== undefined ? { description: entry.description } : {}),
|
|
121
|
+
...snapshotSemanticMetadata(entry),
|
|
101
122
|
operators: uniqueSorted(entry.operators ?? []),
|
|
102
123
|
...(entry.valueType !== undefined ? { valueType: entry.valueType } : {}),
|
|
103
124
|
};
|
package/dist/dataset-query.js
CHANGED
|
@@ -51,7 +51,7 @@ export async function runDatasetQuery(ds, query, options) {
|
|
|
51
51
|
...options,
|
|
52
52
|
executionLimit: overfetchLimit(query.limit),
|
|
53
53
|
});
|
|
54
|
-
const rows = await qb.execute();
|
|
54
|
+
const rows = await qb.execute({ abortSignal: options.context?.abortSignal });
|
|
55
55
|
const { data, pagination } = applyPagination(rows, query.limit, query.offset);
|
|
56
56
|
const serializedData = serializeSemanticMeasureValues(data, query.measures ?? Object.keys(ds.measures));
|
|
57
57
|
return {
|
package/dist/dataset.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataset.d.ts","sourceRoot":"","sources":["../src/dataset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EAKvB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"dataset.d.ts","sourceRoot":"","sources":["../src/dataset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EAKvB,MAAM,YAAY,CAAC;AAqBpB,wBAAgB,OAAO,CACrB,YAAY,SAAS,MAAM,EAC3B,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,EACvD,SAAS,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAC3E,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAErF,IAAI,EAAE,YAAY,EAClB,MAAM,EAAE,aAAa,CAAC,WAAW,EAAE,SAAS,EAAE,cAAc,CAAC,GAC5D,eAAe,CAAC,WAAW,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,CAAC,CA2EvE"}
|
package/dist/dataset.js
CHANGED
|
@@ -32,16 +32,24 @@
|
|
|
32
32
|
import { createDerivedMetricSpec, createMetricRef, isDerivedMetricConfig, } from './utils/dataset-metric-ref.js';
|
|
33
33
|
import { measureToAggregationSpec, normalizeDimensions, normalizeFilters, normalizeMeasures, normalizeRelationships, } from './utils/dataset-normalization.js';
|
|
34
34
|
import { validateBaseMetric, validateDerivedMetric, } from './utils/dataset-validation.js';
|
|
35
|
+
import { validateDatasetDefinition } from './utils/dataset-definition-validation.js';
|
|
36
|
+
import { snapshotSemanticMetadata } from './utils/semantic-metadata.js';
|
|
37
|
+
import { validateSemanticMetadata } from './utils/semantic-metadata-validation.js';
|
|
35
38
|
export function dataset(name, config) {
|
|
39
|
+
// Structural validation runs before anything is normalized, so an invalid
|
|
40
|
+
// model fails at definition time rather than on the first query that reaches
|
|
41
|
+
// the broken part of it.
|
|
42
|
+
validateDatasetDefinition(name, config);
|
|
36
43
|
const dimensions = normalizeDimensions(config);
|
|
37
44
|
const measures = normalizeMeasures(config.measures);
|
|
38
45
|
const filters = normalizeFilters(dimensions, config.filters);
|
|
39
46
|
const relationships = normalizeRelationships(config.relationships, config.source);
|
|
40
47
|
function metric(metricName, metricConfig) {
|
|
48
|
+
validateSemanticMetadata(ds.name, `metrics.${metricName}`, metricConfig);
|
|
41
49
|
if (isDerivedMetricConfig(metricConfig)) {
|
|
42
50
|
validateDerivedMetric(ds, metricName, metricConfig);
|
|
43
51
|
const derivedSpec = createDerivedMetricSpec(metricConfig);
|
|
44
|
-
return createMetricRef(ds, metricName, derivedSpec, metricConfig.label, metricConfig.description);
|
|
52
|
+
return createMetricRef(ds, metricName, derivedSpec, metricConfig.label, metricConfig.description, metricConfig);
|
|
45
53
|
}
|
|
46
54
|
const measureName = metricConfig.measure;
|
|
47
55
|
const measure = ds.measures[measureName];
|
|
@@ -50,12 +58,20 @@ export function dataset(name, config) {
|
|
|
50
58
|
}
|
|
51
59
|
const spec = measureToAggregationSpec(measureName, measure);
|
|
52
60
|
validateBaseMetric(ds, metricName, spec, { allowHiddenField: true });
|
|
53
|
-
return createMetricRef(ds, metricName, spec, metricConfig.label ?? measure.label, metricConfig.description ?? measure.description);
|
|
61
|
+
return createMetricRef(ds, metricName, spec, metricConfig.label ?? measure.label, metricConfig.description ?? measure.description, { ...measure, ...metricConfig });
|
|
54
62
|
}
|
|
55
63
|
const ds = {
|
|
56
64
|
__type: 'dataset',
|
|
57
65
|
name,
|
|
58
66
|
source: config.source,
|
|
67
|
+
description: config.description,
|
|
68
|
+
...snapshotSemanticMetadata(config),
|
|
69
|
+
freshness: config.freshness === undefined ? undefined : { ...config.freshness },
|
|
70
|
+
owner: config.owner,
|
|
71
|
+
defaults: config.defaults === undefined ? undefined : {
|
|
72
|
+
...config.defaults,
|
|
73
|
+
...(config.defaults.dimensions === undefined ? {} : { dimensions: [...config.defaults.dimensions] }),
|
|
74
|
+
},
|
|
59
75
|
tenantKey: config.tenantKey,
|
|
60
76
|
timeKey: config.timeKey,
|
|
61
77
|
dimensions,
|
|
@@ -63,6 +79,7 @@ export function dataset(name, config) {
|
|
|
63
79
|
filters,
|
|
64
80
|
relationships,
|
|
65
81
|
limits: config.limits,
|
|
82
|
+
cache: config.cache,
|
|
66
83
|
metric,
|
|
67
84
|
};
|
|
68
85
|
return ds;
|
package/dist/executor.d.ts
CHANGED
|
@@ -79,6 +79,7 @@ export declare class DatasetClientImpl extends MetricQueryEngine implements Data
|
|
|
79
79
|
private backend?;
|
|
80
80
|
private readonly queryCache;
|
|
81
81
|
private readonly cacheEnabledByDefault;
|
|
82
|
+
private readonly cacheDefaults;
|
|
82
83
|
private readonly defaultCacheScope?;
|
|
83
84
|
constructor(options: CreateDatasetClientOptions);
|
|
84
85
|
getCacheStats(): SemanticCacheStats;
|
|
@@ -107,6 +108,11 @@ export declare class DatasetClientImpl extends MetricQueryEngine implements Data
|
|
|
107
108
|
validate<TTarget extends SemanticTarget>(target: TTarget, query?: SemanticQuery<TTarget>, context?: ExecutionContext): ValidationResult;
|
|
108
109
|
private executeMetric;
|
|
109
110
|
private executeDataset;
|
|
111
|
+
/**
|
|
112
|
+
* Folds the dataset's declared cache policy into the call's context, so both
|
|
113
|
+
* the cacheability decision and the lookup see the same resolved TTL.
|
|
114
|
+
*/
|
|
115
|
+
private datasetCacheContext;
|
|
110
116
|
private toDatasetSQL;
|
|
111
117
|
}
|
|
112
118
|
export declare function createDatasetClient(options: CreateDatasetClientOptions): DatasetClient;
|