@hypequery/datasets 0.18.0 → 0.20.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/dist/agent-catalog.d.ts +11 -1
- package/dist/agent-catalog.d.ts.map +1 -1
- package/dist/aggregations.d.ts +2 -0
- package/dist/aggregations.d.ts.map +1 -1
- package/dist/aggregations.js +4 -0
- package/dist/api.type-test.js +57 -0
- package/dist/cache/definition-identity.d.ts +26 -0
- package/dist/cache/definition-identity.d.ts.map +1 -0
- package/dist/cache/definition-identity.js +164 -0
- package/dist/cache/protocol-cache-keys.d.ts +38 -0
- package/dist/cache/protocol-cache-keys.d.ts.map +1 -0
- package/dist/cache/protocol-cache-keys.js +191 -0
- package/dist/cache/query-signature.d.ts +6 -0
- package/dist/cache/query-signature.d.ts.map +1 -1
- package/dist/cache/query-signature.js +27 -13
- package/dist/cache/semantic-query-cache.d.ts +23 -0
- package/dist/cache/semantic-query-cache.d.ts.map +1 -1
- package/dist/catalog.d.ts +14 -2
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +37 -10
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -0
- package/dist/contract.d.ts +7 -2
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +9 -0
- package/dist/dataset-query.d.ts.map +1 -1
- package/dist/dataset-query.js +27 -5
- package/dist/dataset.d.ts +2 -2
- package/dist/dataset.d.ts.map +1 -1
- package/dist/dataset.js +12 -4
- package/dist/executor.d.ts +7 -1
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +75 -22
- package/dist/in-memory-backend.d.ts.map +1 -1
- package/dist/in-memory-backend.js +34 -7
- package/dist/index.d.ts +8 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/measure.d.ts +17 -2
- package/dist/measure.d.ts.map +1 -1
- package/dist/measure.js +16 -1
- package/dist/portable-execution-errors.d.ts +4 -0
- package/dist/portable-execution-errors.d.ts.map +1 -1
- package/dist/portable-execution-errors.js +4 -1
- package/dist/portable-executor.d.ts.map +1 -1
- package/dist/portable-executor.js +6 -3
- package/dist/protocol-adapter.d.ts.map +1 -1
- package/dist/protocol-adapter.js +25 -8
- package/dist/protocol-deployment-adapter.d.ts.map +1 -1
- package/dist/protocol-deployment-adapter.js +2 -1
- package/dist/protocol-rehydrate.d.ts +5 -1
- package/dist/protocol-rehydrate.d.ts.map +1 -1
- package/dist/protocol-rehydrate.js +20 -10
- package/dist/query-builder-protocol.d.ts +7 -3
- package/dist/query-builder-protocol.d.ts.map +1 -1
- package/dist/query-planner.d.ts +1 -1
- package/dist/query-planner.d.ts.map +1 -1
- package/dist/query-planner.js +10 -17
- package/dist/relationship-check.d.ts +47 -0
- package/dist/relationship-check.d.ts.map +1 -0
- package/dist/relationship-check.js +59 -0
- package/dist/relationships.d.ts +10 -10
- package/dist/relationships.js +10 -10
- package/dist/semantic-plan.d.ts +0 -1
- package/dist/semantic-plan.d.ts.map +1 -1
- package/dist/semantic-planner.d.ts.map +1 -1
- package/dist/semantic-planner.js +15 -4
- package/dist/semantic-query-schema.d.ts.map +1 -1
- package/dist/semantic-query-schema.js +7 -0
- package/dist/types.d.ts +99 -18
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/agent-catalog-projection.d.ts +9 -2
- package/dist/utils/agent-catalog-projection.d.ts.map +1 -1
- package/dist/utils/agent-catalog-projection.js +6 -10
- package/dist/utils/approximate-measures.d.ts +10 -0
- package/dist/utils/approximate-measures.d.ts.map +1 -0
- package/dist/utils/approximate-measures.js +12 -0
- package/dist/utils/dataset-contract.d.ts.map +1 -1
- package/dist/utils/dataset-contract.js +4 -3
- package/dist/utils/dataset-definition-validation.d.ts.map +1 -1
- package/dist/utils/dataset-definition-validation.js +6 -24
- package/dist/utils/dataset-derived-query.d.ts.map +1 -1
- package/dist/utils/dataset-derived-query.js +7 -50
- package/dist/utils/dataset-measures.d.ts +13 -6
- package/dist/utils/dataset-measures.d.ts.map +1 -1
- package/dist/utils/dataset-measures.js +25 -4
- 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 -0
- package/dist/utils/dataset-query-result.d.ts.map +1 -1
- package/dist/utils/dataset-query-result.js +2 -1
- package/dist/utils/dataset-query-validation.d.ts.map +1 -1
- package/dist/utils/dataset-query-validation.js +13 -6
- package/dist/utils/dataset-time-grains.d.ts +15 -0
- package/dist/utils/dataset-time-grains.d.ts.map +1 -0
- package/dist/utils/dataset-time-grains.js +17 -0
- package/dist/utils/derived-measure-sql.d.ts +3 -0
- package/dist/utils/derived-measure-sql.d.ts.map +1 -0
- package/dist/utils/derived-measure-sql.js +46 -0
- package/dist/utils/derived-measure-validation.d.ts.map +1 -1
- package/dist/utils/derived-measure-validation.js +6 -5
- package/dist/utils/filtered-aggregation-sql.d.ts.map +1 -1
- package/dist/utils/filtered-aggregation-sql.js +1 -0
- package/dist/utils/measure-time-grains.d.ts +4 -0
- package/dist/utils/measure-time-grains.d.ts.map +1 -0
- package/dist/utils/measure-time-grains.js +16 -0
- package/dist/utils/portable-aggregations.d.ts +14 -0
- package/dist/utils/portable-aggregations.d.ts.map +1 -0
- package/dist/utils/portable-aggregations.js +15 -0
- package/dist/utils/portable-grains.d.ts +22 -0
- package/dist/utils/portable-grains.d.ts.map +1 -0
- package/dist/utils/portable-grains.js +35 -0
- package/dist/utils/portable-semantic-query.d.ts.map +1 -1
- package/dist/utils/portable-semantic-query.js +2 -1
- package/dist/utils/protocol-metric-expressions.d.ts.map +1 -1
- package/dist/utils/protocol-metric-expressions.js +2 -1
- package/dist/utils/query-timezone.d.ts +9 -0
- package/dist/utils/query-timezone.d.ts.map +1 -0
- package/dist/utils/query-timezone.js +35 -0
- package/dist/utils/relationship-builder-plan.d.ts +6 -3
- package/dist/utils/relationship-builder-plan.d.ts.map +1 -1
- package/dist/utils/relationship-builder-plan.js +13 -5
- package/dist/utils/relationship-key-check.d.ts +43 -0
- package/dist/utils/relationship-key-check.d.ts.map +1 -0
- package/dist/utils/relationship-key-check.js +72 -0
- package/dist/utils/segments.d.ts +31 -0
- package/dist/utils/segments.d.ts.map +1 -0
- package/dist/utils/segments.js +91 -0
- package/dist/utils/semantic-metadata-validation.d.ts.map +1 -1
- package/dist/utils/semantic-metadata-validation.js +23 -2
- package/dist/utils/semantic-metadata.d.ts.map +1 -1
- package/dist/utils/semantic-metadata.js +3 -1
- package/dist/utils/shift-measure-definition.d.ts +4 -0
- package/dist/utils/shift-measure-definition.d.ts.map +1 -0
- package/dist/utils/shift-measure-definition.js +9 -0
- package/dist/utils/shift-measure-sql.d.ts +18 -0
- package/dist/utils/shift-measure-sql.d.ts.map +1 -0
- package/dist/utils/shift-measure-sql.js +46 -0
- package/dist/utils/shift-measure-validation.d.ts +3 -0
- package/dist/utils/shift-measure-validation.d.ts.map +1 -0
- package/dist/utils/shift-measure-validation.js +20 -0
- package/dist/utils/sql-under-joins.d.ts +10 -0
- package/dist/utils/sql-under-joins.d.ts.map +1 -0
- package/dist/utils/sql-under-joins.js +15 -0
- package/dist/utils/time-arithmetic-sql.d.ts +4 -0
- package/dist/utils/time-arithmetic-sql.d.ts.map +1 -0
- package/dist/utils/time-arithmetic-sql.js +9 -0
- package/dist/utils/time-axis-bounds.d.ts +24 -0
- package/dist/utils/time-axis-bounds.d.ts.map +1 -0
- package/dist/utils/time-axis-bounds.js +71 -0
- package/dist/utils/time-axis-calendar-guard.d.ts +9 -0
- package/dist/utils/time-axis-calendar-guard.d.ts.map +1 -0
- package/dist/utils/time-axis-calendar-guard.js +17 -0
- package/dist/utils/time-axis-intervals.d.ts +13 -0
- package/dist/utils/time-axis-intervals.d.ts.map +1 -0
- package/dist/utils/time-axis-intervals.js +81 -0
- package/dist/utils/time-measure-axis-sql.d.ts +12 -0
- package/dist/utils/time-measure-axis-sql.d.ts.map +1 -0
- package/dist/utils/time-measure-axis-sql.js +47 -0
- package/dist/utils/time-measure-axis.d.ts +19 -0
- package/dist/utils/time-measure-axis.d.ts.map +1 -0
- package/dist/utils/time-measure-axis.js +45 -0
- package/dist/utils/time-measure-dataset-sql.d.ts +9 -0
- package/dist/utils/time-measure-dataset-sql.d.ts.map +1 -0
- package/dist/utils/time-measure-dataset-sql.js +63 -0
- package/dist/utils/time-measure-result-sql.d.ts +6 -0
- package/dist/utils/time-measure-result-sql.d.ts.map +1 -0
- package/dist/utils/time-measure-result-sql.js +30 -0
- package/dist/utils/time-measure-source-sql.d.ts +22 -0
- package/dist/utils/time-measure-source-sql.d.ts.map +1 -0
- package/dist/utils/time-measure-source-sql.js +75 -0
- package/dist/utils/time-measure-values-sql.d.ts +20 -0
- package/dist/utils/time-measure-values-sql.d.ts.map +1 -0
- package/dist/utils/time-measure-values-sql.js +87 -0
- package/dist/utils/time-query-measures.d.ts +6 -0
- package/dist/utils/time-query-measures.d.ts.map +1 -0
- package/dist/utils/time-query-measures.js +28 -0
- package/dist/utils/unsupported-contract-reasons.d.ts +32 -0
- package/dist/utils/unsupported-contract-reasons.d.ts.map +1 -0
- package/dist/utils/unsupported-contract-reasons.js +30 -0
- package/dist/utils/window-aggregation-sql.d.ts +5 -0
- package/dist/utils/window-aggregation-sql.d.ts.map +1 -0
- package/dist/utils/window-aggregation-sql.js +17 -0
- package/dist/utils/window-catalog-metadata.d.ts +14 -0
- package/dist/utils/window-catalog-metadata.d.ts.map +1 -0
- package/dist/utils/window-catalog-metadata.js +17 -0
- package/dist/utils/window-measure-definition.d.ts +4 -0
- package/dist/utils/window-measure-definition.d.ts.map +1 -0
- package/dist/utils/window-measure-definition.js +11 -0
- package/dist/utils/window-measure-dependencies.d.ts +4 -0
- package/dist/utils/window-measure-dependencies.d.ts.map +1 -0
- package/dist/utils/window-measure-dependencies.js +5 -0
- package/dist/utils/window-measure-validation.d.ts +3 -0
- package/dist/utils/window-measure-validation.d.ts.map +1 -0
- package/dist/utils/window-measure-validation.js +49 -0
- package/dist/utils/window-query-measures.d.ts +4 -0
- package/dist/utils/window-query-measures.d.ts.map +1 -0
- package/dist/utils/window-query-measures.js +17 -0
- package/dist/utils/window-query-signature.d.ts +4 -0
- package/dist/utils/window-query-signature.d.ts.map +1 -0
- package/dist/utils/window-query-signature.js +25 -0
- package/package.json +3 -3
package/dist/agent-catalog.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type WindowCatalogMetadata } from './utils/window-catalog-metadata.js';
|
|
1
2
|
import type { ProtocolDeploymentContract } from '@hypequery/protocol';
|
|
2
3
|
import { type DatasetCatalog, type DatasetCatalogMap, type DatasetCatalogSource } from './catalog.js';
|
|
3
4
|
import type { SemanticContract } from './contract.js';
|
|
@@ -10,10 +11,12 @@ export interface AgentCatalogDimension extends SemanticMetadata {
|
|
|
10
11
|
filterable: boolean;
|
|
11
12
|
groupable: boolean;
|
|
12
13
|
}
|
|
13
|
-
export interface AgentCatalogMeasure extends SemanticMetadata {
|
|
14
|
+
export interface AgentCatalogMeasure extends SemanticMetadata, WindowCatalogMetadata {
|
|
14
15
|
name: string;
|
|
15
16
|
label?: string;
|
|
16
17
|
description?: string;
|
|
18
|
+
/** Present, as `true`, when the value is an estimate; say so when reporting it. */
|
|
19
|
+
approximate?: true;
|
|
17
20
|
}
|
|
18
21
|
export interface AgentCatalogMetric extends SemanticMetadata {
|
|
19
22
|
name: string;
|
|
@@ -47,9 +50,16 @@ export interface AgentCatalogDataset extends SemanticMetadata {
|
|
|
47
50
|
measures: AgentCatalogMeasure[];
|
|
48
51
|
metrics: AgentCatalogMetric[];
|
|
49
52
|
filters: AgentCatalogFilter[];
|
|
53
|
+
/** Named segments an agent can select; their conditions are never shown. */
|
|
54
|
+
segments?: AgentCatalogSegment[];
|
|
50
55
|
relationships: AgentCatalogRelationship[];
|
|
51
56
|
limits: DatasetLimits;
|
|
52
57
|
}
|
|
58
|
+
export interface AgentCatalogSegment {
|
|
59
|
+
name: string;
|
|
60
|
+
label?: string;
|
|
61
|
+
description?: string;
|
|
62
|
+
}
|
|
53
63
|
export interface AgentSafeCatalog {
|
|
54
64
|
datasets: AgentCatalogDataset[];
|
|
55
65
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-catalog.d.ts","sourceRoot":"","sources":["../src/agent-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,SAAS,EACT,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAOpB,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,mBAAoB,SAAQ,gBAAgB;
|
|
1
|
+
{"version":3,"file":"agent-catalog.d.ts","sourceRoot":"","sources":["../src/agent-catalog.ts"],"names":[],"mappings":"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"}
|
package/dist/aggregations.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ import type { AggregationSpec } from './types.js';
|
|
|
18
18
|
export declare function sum(field: string): AggregationSpec;
|
|
19
19
|
export declare function count(field: string): AggregationSpec;
|
|
20
20
|
export declare function countDistinct(field: string): AggregationSpec;
|
|
21
|
+
/** Estimated distinct count (ClickHouse `uniq`). */
|
|
22
|
+
export declare function approxCountDistinct(field: string): AggregationSpec;
|
|
21
23
|
export declare function avg(field: string): AggregationSpec;
|
|
22
24
|
export declare function min(field: string): AggregationSpec;
|
|
23
25
|
export declare function max(field: string): AggregationSpec;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aggregations.d.ts","sourceRoot":"","sources":["../src/aggregations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAmB,MAAM,YAAY,CAAC;AAWnE,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAEpD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAE5D;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD;AAED,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,CAGxE;AAED,mDAAmD;AACnD,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAErD;AAED,0DAA0D;AAC1D,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,eAAe,CAKjE;AAED,0DAA0D;AAC1D,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,eAAe,CAKjE;AAED,iCAAiC;AACjC,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAErD;AAED,uBAAuB;AACvB,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAEvD"}
|
|
1
|
+
{"version":3,"file":"aggregations.d.ts","sourceRoot":"","sources":["../src/aggregations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAmB,MAAM,YAAY,CAAC;AAWnE,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAEpD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAE5D;AAED,oDAAoD;AACpD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElE;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD;AAED,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAElD;AAED,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,CAGxE;AAED,mDAAmD;AACnD,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAErD;AAED,0DAA0D;AAC1D,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,eAAe,CAKjE;AAED,0DAA0D;AAC1D,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,eAAe,CAKjE;AAED,iCAAiC;AACjC,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAErD;AAED,uBAAuB;AACvB,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAEvD"}
|
package/dist/aggregations.js
CHANGED
|
@@ -31,6 +31,10 @@ export function count(field) {
|
|
|
31
31
|
export function countDistinct(field) {
|
|
32
32
|
return createAggregation('countDistinct', field);
|
|
33
33
|
}
|
|
34
|
+
/** Estimated distinct count (ClickHouse `uniq`). */
|
|
35
|
+
export function approxCountDistinct(field) {
|
|
36
|
+
return createAggregation('approxCountDistinct', field);
|
|
37
|
+
}
|
|
34
38
|
export function avg(field) {
|
|
35
39
|
return createAggregation('avg', field);
|
|
36
40
|
}
|
package/dist/api.type-test.js
CHANGED
|
@@ -21,6 +21,25 @@ const Orders = dataset('orders', {
|
|
|
21
21
|
}),
|
|
22
22
|
},
|
|
23
23
|
});
|
|
24
|
+
const _SegmentedOrders = dataset('segmentedOrders', {
|
|
25
|
+
source: 'orders',
|
|
26
|
+
dimensions: { status: dimension.string() },
|
|
27
|
+
measures: { revenue: measure.sum('amount') },
|
|
28
|
+
segments: { paid: { filters: [eq('status', 'paid')] } },
|
|
29
|
+
});
|
|
30
|
+
const validDatasetSegments = { segments: ['paid'] };
|
|
31
|
+
const validMetricSegments = { segments: ['paid'] };
|
|
32
|
+
void validDatasetSegments;
|
|
33
|
+
void validMetricSegments;
|
|
34
|
+
// @ts-expect-error only declared segment names are accepted.
|
|
35
|
+
const invalidDatasetSegments = { segments: ['paidd'] };
|
|
36
|
+
// @ts-expect-error only declared segment names are accepted on metric queries.
|
|
37
|
+
const invalidMetricSegments = { segments: ['paidd'] };
|
|
38
|
+
// @ts-expect-error a dataset without segments has no selectable segment names.
|
|
39
|
+
const invalidPlainSegments = { segments: ['paid'] };
|
|
40
|
+
void invalidDatasetSegments;
|
|
41
|
+
void invalidMetricSegments;
|
|
42
|
+
void invalidPlainSegments;
|
|
24
43
|
// @ts-expect-error standalone metrics may only target base measures.
|
|
25
44
|
Orders.metric('invalidDerivedMetric', { measure: 'doubledRevenue' });
|
|
26
45
|
dataset('derivedDependency', {
|
|
@@ -33,6 +52,28 @@ dataset('derivedDependency', {
|
|
|
33
52
|
quadrupled: measure.derived({ uses: { value: 'doubled' }, formula: ({ value }) => add(value, value) }),
|
|
34
53
|
},
|
|
35
54
|
});
|
|
55
|
+
const _WindowOrders = dataset('windowOrders', {
|
|
56
|
+
source: 'orders', timeKey: 'createdAt',
|
|
57
|
+
dimensions: { createdAt: dimension.timestamp(), amount: dimension.number() },
|
|
58
|
+
measures: {
|
|
59
|
+
revenue: measure.sum('amount'),
|
|
60
|
+
trailingRevenue: measure.trailing('revenue', { amount: 7, unit: 'day' }),
|
|
61
|
+
runningRevenue: measure.cumulative('revenue'),
|
|
62
|
+
growth: measure.derived({ uses: { running: 'runningRevenue' }, formula: ({ running }) => add(running, running) }),
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
// @ts-expect-error standalone metrics may only target base measures, even in the unified registry.
|
|
66
|
+
_WindowOrders.metric('invalidWindowMetric', { measure: 'runningRevenue' });
|
|
67
|
+
dataset('invalidWindowInput', {
|
|
68
|
+
source: 'orders', timeKey: 'createdAt',
|
|
69
|
+
dimensions: { createdAt: dimension.timestamp(), amount: dimension.number() },
|
|
70
|
+
measures: {
|
|
71
|
+
revenue: measure.sum('amount'),
|
|
72
|
+
growth: measure.derived({ uses: { value: 'revenue' }, formula: ({ value }) => add(value, value) }),
|
|
73
|
+
// @ts-expect-error a window must wrap a base measure.
|
|
74
|
+
invalid: measure.cumulative('growth'),
|
|
75
|
+
},
|
|
76
|
+
});
|
|
36
77
|
const deploymentContract = buildProtocolDeploymentContract([Orders]);
|
|
37
78
|
rehydrateProtocolDeploymentContract(deploymentContract);
|
|
38
79
|
dataset('invalidDependency', {
|
|
@@ -186,3 +227,19 @@ void typedAccepted;
|
|
|
186
227
|
void typedClient;
|
|
187
228
|
void typedRuntime;
|
|
188
229
|
void rejectedFactory;
|
|
230
|
+
// ---------------------------------------------------------------------------
|
|
231
|
+
// Portable execution exclusions carry a named reason (HQ-45)
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
import { PortableExecutionUnsupportedError as PortableUnsupported, UNSUPPORTED_CONTRACT_REASONS as REASONS, UnsupportedContractFeatureError as ContractFeatureError, } from './index.js';
|
|
234
|
+
const namedReason = REASONS.ambiguousMeasureSql;
|
|
235
|
+
const literalReason = 'HQ_PORTABLE_DATASET_NOT_ACTIVATED';
|
|
236
|
+
// @ts-expect-error an unnamed reason is not an exclusion
|
|
237
|
+
const unnamedReason = 'HQ_PORTABLE_SOMETHING_ELSE';
|
|
238
|
+
const featureReason = new ContractFeatureError('orders', 'metric', 'detail', namedReason).reason;
|
|
239
|
+
// The three-argument constructor stays valid for existing callers.
|
|
240
|
+
const legacyFeatureError = new ContractFeatureError('orders', 'metric', 'detail');
|
|
241
|
+
const executionReason = new PortableUnsupported('message', { reason: literalReason }).reason;
|
|
242
|
+
void unnamedReason;
|
|
243
|
+
void featureReason;
|
|
244
|
+
void legacyFeatureError;
|
|
245
|
+
void executionReason;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AnyDatasetInstance } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The local definition identity RFC 0009 leaves implementation-defined: a
|
|
4
|
+
* digest of every definition that could affect the rows of a query over
|
|
5
|
+
* `root`, plus the metric spec, cache scope, and timezone when present.
|
|
6
|
+
*
|
|
7
|
+
* Throws when a definition holds a function it cannot describe safely; the
|
|
8
|
+
* caller then runs the query uncached.
|
|
9
|
+
*
|
|
10
|
+
* Entries under it are never shared with released deployments or with other
|
|
11
|
+
* implementations. That costs only misses, and any definition change starts
|
|
12
|
+
* fresh.
|
|
13
|
+
*/
|
|
14
|
+
export declare function localDefinitionIdentity(root: AnyDatasetInstance, extra?: {
|
|
15
|
+
metric?: unknown;
|
|
16
|
+
scope?: string;
|
|
17
|
+
timezone?: string;
|
|
18
|
+
}): string;
|
|
19
|
+
/**
|
|
20
|
+
* Folds a cache scope and a non-default timezone into a deployed definition
|
|
21
|
+
* identity. Both change the rows one release returns (`scope` picks the data
|
|
22
|
+
* source, the timezone moves bucket boundaries), so they must separate keys
|
|
23
|
+
* even when the definitions are identical.
|
|
24
|
+
*/
|
|
25
|
+
export declare function scopedDefinitionIdentity(identity: string, scope: string | undefined, timezone?: string): string;
|
|
26
|
+
//# sourceMappingURL=definition-identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definition-identity.d.ts","sourceRoot":"","sources":["../../src/cache/definition-identity.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAe,MAAM,aAAa,CAAC;AA8InE;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,kBAAkB,EACxB,KAAK,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE,MAAM,CAWR;AAMD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,CAKR"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { sha256 } from '@noble/hashes/sha2';
|
|
2
|
+
import { bytesToHex } from '@noble/hashes/utils';
|
|
3
|
+
import { stableStringify } from '../utils/canonical-json.js';
|
|
4
|
+
const LOCAL_DEFINITION_DOMAIN = 'hypequery.ts.local-definitions.v2\0';
|
|
5
|
+
/**
|
|
6
|
+
* Library-created methods, by the `__type` of the object that carries them.
|
|
7
|
+
* They read only state that is described alongside them, so they contribute
|
|
8
|
+
* nothing of their own.
|
|
9
|
+
*/
|
|
10
|
+
const LIBRARY_METHODS = {
|
|
11
|
+
dataset: ['metric'],
|
|
12
|
+
metric_ref: ['by', 'contract'],
|
|
13
|
+
grained_metric_ref: ['contract'],
|
|
14
|
+
};
|
|
15
|
+
function isDataset(value) {
|
|
16
|
+
return (typeof value === 'object'
|
|
17
|
+
&& value !== null
|
|
18
|
+
&& value.__type === 'dataset'
|
|
19
|
+
&& typeof value.name === 'string');
|
|
20
|
+
}
|
|
21
|
+
function isFormulaExpr(value) {
|
|
22
|
+
return (typeof value === 'object'
|
|
23
|
+
&& value !== null
|
|
24
|
+
&& value.__type === 'formula_expr'
|
|
25
|
+
&& typeof value.toSQL === 'function');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The formula's own SQL, which derived metrics execute. `null` when it throws:
|
|
29
|
+
* derived measures compile the expression instead, so it need not render.
|
|
30
|
+
*/
|
|
31
|
+
function formulaSql(formula) {
|
|
32
|
+
try {
|
|
33
|
+
return formula.toSQL();
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function undescribable(key) {
|
|
40
|
+
throw new Error(`Cannot derive a local definition identity: the function at "${key ?? '?'}" `
|
|
41
|
+
+ 'is not a formula, relationship target, or library method.');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Builds a deterministic description of every definition a query over a root
|
|
45
|
+
* dataset could read.
|
|
46
|
+
*
|
|
47
|
+
* A function never contributes its source text: that omits the values it
|
|
48
|
+
* captures, so `make(2)` and `make(3)` over `({ r }) => round(r, digits)`
|
|
49
|
+
* would collide. Instead:
|
|
50
|
+
* - a formula is evaluated, and its resolved expression and SQL are described;
|
|
51
|
+
* - a relationship target contributes its dataset's name, and that dataset is
|
|
52
|
+
* described separately;
|
|
53
|
+
* - a known library method contributes nothing.
|
|
54
|
+
*
|
|
55
|
+
* Any other function throws, so the caller runs the query uncached rather
|
|
56
|
+
* than risk serving another definition's rows.
|
|
57
|
+
*/
|
|
58
|
+
class DefinitionDescriber {
|
|
59
|
+
found = new Map();
|
|
60
|
+
pending = [];
|
|
61
|
+
/** Queues a dataset for description and stands in for it by name. */
|
|
62
|
+
reference(dataset) {
|
|
63
|
+
if (!this.found.has(dataset.name)) {
|
|
64
|
+
this.found.set(dataset.name, dataset);
|
|
65
|
+
this.pending.push(dataset);
|
|
66
|
+
}
|
|
67
|
+
return { dataset: dataset.name };
|
|
68
|
+
}
|
|
69
|
+
/** Describes every queued dataset, including those queued along the way. */
|
|
70
|
+
describeDatasets() {
|
|
71
|
+
const described = {};
|
|
72
|
+
while (this.pending.length > 0) {
|
|
73
|
+
const dataset = this.pending.pop();
|
|
74
|
+
described[dataset.name] = this.describeFields(dataset, new WeakSet());
|
|
75
|
+
}
|
|
76
|
+
return described;
|
|
77
|
+
}
|
|
78
|
+
describe(value, seen, owner, key) {
|
|
79
|
+
if (typeof value === 'function')
|
|
80
|
+
return this.describeFunction(value, seen, owner, key);
|
|
81
|
+
if (value instanceof Date)
|
|
82
|
+
return { date: value.toISOString() };
|
|
83
|
+
if (typeof value === 'bigint')
|
|
84
|
+
return { bigint: value.toString() };
|
|
85
|
+
if (typeof value !== 'object' || value === null)
|
|
86
|
+
return value;
|
|
87
|
+
if (isDataset(value))
|
|
88
|
+
return this.reference(value);
|
|
89
|
+
return this.describeFields(value, seen);
|
|
90
|
+
}
|
|
91
|
+
describeFields(value, seen) {
|
|
92
|
+
if (seen.has(value))
|
|
93
|
+
return '[cycle]';
|
|
94
|
+
seen.add(value);
|
|
95
|
+
const described = Array.isArray(value)
|
|
96
|
+
? value.map((item) => this.describe(item, seen))
|
|
97
|
+
: Object.fromEntries(Object.entries(value).map(([name, item]) => [
|
|
98
|
+
name,
|
|
99
|
+
this.describe(item, seen, value, name),
|
|
100
|
+
]));
|
|
101
|
+
seen.delete(value);
|
|
102
|
+
return described;
|
|
103
|
+
}
|
|
104
|
+
describeFunction(fn, seen, owner, key) {
|
|
105
|
+
if (key === 'target') {
|
|
106
|
+
const target = fn();
|
|
107
|
+
return isDataset(target) ? this.reference(target) : undescribable(key);
|
|
108
|
+
}
|
|
109
|
+
if (key === 'formula' && owner && typeof owner.uses === 'object' && owner.uses !== null) {
|
|
110
|
+
// The same identity aliases the planner and validation evaluate with.
|
|
111
|
+
const aliases = Object.fromEntries(Object.keys(owner.uses).map((alias) => [alias, alias]));
|
|
112
|
+
const result = fn(aliases);
|
|
113
|
+
if (!isFormulaExpr(result))
|
|
114
|
+
return undescribable(key);
|
|
115
|
+
return { expression: this.describe(result.expression, seen), sql: formulaSql(result) };
|
|
116
|
+
}
|
|
117
|
+
const type = typeof owner?.__type === 'string' ? owner.__type : undefined;
|
|
118
|
+
if (type !== undefined && key !== undefined && LIBRARY_METHODS[type]?.includes(key)) {
|
|
119
|
+
return 'method';
|
|
120
|
+
}
|
|
121
|
+
return undescribable(key);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The local definition identity RFC 0009 leaves implementation-defined: a
|
|
126
|
+
* digest of every definition that could affect the rows of a query over
|
|
127
|
+
* `root`, plus the metric spec, cache scope, and timezone when present.
|
|
128
|
+
*
|
|
129
|
+
* Throws when a definition holds a function it cannot describe safely; the
|
|
130
|
+
* caller then runs the query uncached.
|
|
131
|
+
*
|
|
132
|
+
* Entries under it are never shared with released deployments or with other
|
|
133
|
+
* implementations. That costs only misses, and any definition change starts
|
|
134
|
+
* fresh.
|
|
135
|
+
*/
|
|
136
|
+
export function localDefinitionIdentity(root, extra = {}) {
|
|
137
|
+
const describer = new DefinitionDescriber();
|
|
138
|
+
describer.reference(root);
|
|
139
|
+
const metric = extra.metric === undefined ? null : describer.describe(extra.metric, new WeakSet());
|
|
140
|
+
const description = stableStringify({
|
|
141
|
+
datasets: describer.describeDatasets(),
|
|
142
|
+
metric,
|
|
143
|
+
scope: extra.scope ?? null,
|
|
144
|
+
timezone: extra.timezone ?? null,
|
|
145
|
+
});
|
|
146
|
+
return bytesToHex(sha256(new TextEncoder().encode(LOCAL_DEFINITION_DOMAIN + description)));
|
|
147
|
+
}
|
|
148
|
+
function foldPartition(identity, domain, value) {
|
|
149
|
+
return bytesToHex(sha256(new TextEncoder().encode(`${domain}\0${identity}\0${value}`)));
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Folds a cache scope and a non-default timezone into a deployed definition
|
|
153
|
+
* identity. Both change the rows one release returns (`scope` picks the data
|
|
154
|
+
* source, the timezone moves bucket boundaries), so they must separate keys
|
|
155
|
+
* even when the definitions are identical.
|
|
156
|
+
*/
|
|
157
|
+
export function scopedDefinitionIdentity(identity, scope, timezone) {
|
|
158
|
+
let folded = identity;
|
|
159
|
+
if (scope !== undefined)
|
|
160
|
+
folded = foldPartition(folded, 'hypequery.ts.cache-scope.v1', scope);
|
|
161
|
+
if (timezone !== undefined)
|
|
162
|
+
folded = foldPartition(folded, 'hypequery.ts.cache-timezone.v1', timezone);
|
|
163
|
+
return folded;
|
|
164
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AnyDatasetInstance, DatasetQuery, ExecutionContext, GrainedMetricRef, MetricQuery, MetricRef } from '../types.js';
|
|
2
|
+
/** Namespace defaults, so caching works without any configuration. */
|
|
3
|
+
export declare const DEFAULT_CACHE_PROJECT = "hypequery";
|
|
4
|
+
export declare const DEFAULT_CACHE_ENVIRONMENT = "default";
|
|
5
|
+
/** Key settings from `SemanticCacheOptions`, resolved once per client. */
|
|
6
|
+
export interface ProtocolCacheKeySettings {
|
|
7
|
+
readonly secret: Uint8Array;
|
|
8
|
+
readonly project: string;
|
|
9
|
+
readonly environment: string;
|
|
10
|
+
readonly keyVersion: number;
|
|
11
|
+
readonly definitionIdentity?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ProtocolCacheKeyOptions {
|
|
14
|
+
secret?: Uint8Array;
|
|
15
|
+
project?: string;
|
|
16
|
+
environment?: string;
|
|
17
|
+
keyVersion?: number;
|
|
18
|
+
definitionIdentity?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Resolves and validates key settings at client construction, so a
|
|
22
|
+
* misconfiguration fails at startup rather than silently caching nothing.
|
|
23
|
+
*
|
|
24
|
+
* Without a secret the cache generates a random one for its lifetime. Keys
|
|
25
|
+
* stay HMAC-derived and opaque; entries are simply not shared beyond this
|
|
26
|
+
* client. An explicitly empty or short secret is an error, never a reason to
|
|
27
|
+
* generate one.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveProtocolCacheKeySettings(options: ProtocolCacheKeyOptions, sharedStore: boolean): ProtocolCacheKeySettings;
|
|
30
|
+
/**
|
|
31
|
+
* The RFC 0013 store key for a dataset query, or `undefined` when the call
|
|
32
|
+
* cannot be cached (for example, a filter value with no portable form).
|
|
33
|
+
* `query` must already carry its effective limit.
|
|
34
|
+
*/
|
|
35
|
+
export declare function datasetCacheKey(settings: ProtocolCacheKeySettings, ds: AnyDatasetInstance, query: DatasetQuery, context: ExecutionContext | undefined, scope: string | undefined): string | undefined;
|
|
36
|
+
/** The RFC 0013 store key for a metric query; see `datasetCacheKey`. */
|
|
37
|
+
export declare function metricCacheKey(settings: ProtocolCacheKeySettings, metric: MetricRef | GrainedMetricRef, query: MetricQuery, context: ExecutionContext | undefined, scope: string | undefined): string | undefined;
|
|
38
|
+
//# sourceMappingURL=protocol-cache-keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol-cache-keys.d.ts","sourceRoot":"","sources":["../../src/cache/protocol-cache-keys.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAEhB,WAAW,EACX,SAAS,EACV,MAAM,aAAa,CAAC;AAIrB,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,cAAc,CAAC;AACjD,eAAO,MAAM,yBAAyB,YAAY,CAAC;AAInD,0EAA0E;AAC1E,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAID;;;;;;;;GAQG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,uBAAuB,EAChC,WAAW,EAAE,OAAO,GACnB,wBAAwB,CAqC1B;AAkHD;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,wBAAwB,EAClC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,gBAAgB,GAAG,SAAS,EACrC,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,MAAM,GAAG,SAAS,CAcpB;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,wBAAwB,EAClC,MAAM,EAAE,SAAS,GAAG,gBAAgB,EACpC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,gBAAgB,GAAG,SAAS,EACrC,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,MAAM,GAAG,SAAS,CAgBpB"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { buildProtocolCachePreimage, deriveProtocolCacheKey, validateCanonicalValue, } from '@hypequery/protocol';
|
|
2
|
+
import { getMetricGrain, getMetricRef } from '../utils/metric-handle.js';
|
|
3
|
+
import { localDefinitionIdentity, scopedDefinitionIdentity } from './definition-identity.js';
|
|
4
|
+
/** Namespace defaults, so caching works without any configuration. */
|
|
5
|
+
export const DEFAULT_CACHE_PROJECT = 'hypequery';
|
|
6
|
+
export const DEFAULT_CACHE_ENVIRONMENT = 'default';
|
|
7
|
+
const SECRET_BYTES = 32;
|
|
8
|
+
const DEFINITION_IDENTITY = /^[0-9a-f]{64}$/;
|
|
9
|
+
let warnedSharedStoreWithoutSecret = false;
|
|
10
|
+
/**
|
|
11
|
+
* Resolves and validates key settings at client construction, so a
|
|
12
|
+
* misconfiguration fails at startup rather than silently caching nothing.
|
|
13
|
+
*
|
|
14
|
+
* Without a secret the cache generates a random one for its lifetime. Keys
|
|
15
|
+
* stay HMAC-derived and opaque; entries are simply not shared beyond this
|
|
16
|
+
* client. An explicitly empty or short secret is an error, never a reason to
|
|
17
|
+
* generate one.
|
|
18
|
+
*/
|
|
19
|
+
export function resolveProtocolCacheKeySettings(options, sharedStore) {
|
|
20
|
+
let secret = options.secret;
|
|
21
|
+
if (secret === undefined) {
|
|
22
|
+
secret = globalThis.crypto.getRandomValues(new Uint8Array(SECRET_BYTES));
|
|
23
|
+
if (sharedStore && !warnedSharedStoreWithoutSecret) {
|
|
24
|
+
warnedSharedStoreWithoutSecret = true;
|
|
25
|
+
console.warn('[hypequery/cache] No cache secret is configured, so this client generated one. '
|
|
26
|
+
+ 'Other instances sharing this store will not reuse its entries. '
|
|
27
|
+
+ 'Pass the same cache.secret to every instance to share them.');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (options.definitionIdentity !== undefined
|
|
31
|
+
&& !DEFINITION_IDENTITY.test(options.definitionIdentity)) {
|
|
32
|
+
throw new Error('cache.definitionIdentity must be 64 lowercase hexadecimal characters.');
|
|
33
|
+
}
|
|
34
|
+
const settings = {
|
|
35
|
+
secret,
|
|
36
|
+
project: options.project ?? DEFAULT_CACHE_PROJECT,
|
|
37
|
+
environment: options.environment ?? DEFAULT_CACHE_ENVIRONMENT,
|
|
38
|
+
keyVersion: options.keyVersion ?? 1,
|
|
39
|
+
...(options.definitionIdentity !== undefined
|
|
40
|
+
? { definitionIdentity: options.definitionIdentity }
|
|
41
|
+
: {}),
|
|
42
|
+
};
|
|
43
|
+
// Throws ProtocolCacheKeyError for an empty or short secret, an invalid
|
|
44
|
+
// namespace, or an out-of-range key version.
|
|
45
|
+
deriveProtocolCacheKey({
|
|
46
|
+
secret: settings.secret,
|
|
47
|
+
namespace: { project: settings.project, environment: settings.environment },
|
|
48
|
+
keyVersion: settings.keyVersion,
|
|
49
|
+
preimage: '',
|
|
50
|
+
});
|
|
51
|
+
return settings;
|
|
52
|
+
}
|
|
53
|
+
function datetimeValue(value) {
|
|
54
|
+
return validateCanonicalValue({
|
|
55
|
+
$hypequery: {
|
|
56
|
+
type: 'datetime',
|
|
57
|
+
version: 1,
|
|
58
|
+
clickhouseType: 'DateTime64',
|
|
59
|
+
precision: 3,
|
|
60
|
+
timezone: 'UTC',
|
|
61
|
+
value: value.toISOString(),
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/** A filter value as an RFC 0001 canonical value. Throws when it has none. */
|
|
66
|
+
function canonicalLiteral(value) {
|
|
67
|
+
if (value instanceof Date)
|
|
68
|
+
return datetimeValue(value);
|
|
69
|
+
if (Array.isArray(value)) {
|
|
70
|
+
return validateCanonicalValue({
|
|
71
|
+
$hypequery: { type: 'array', version: 1, values: value.map(canonicalLiteral) },
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return validateCanonicalValue(value);
|
|
75
|
+
}
|
|
76
|
+
function filterNode(filter) {
|
|
77
|
+
let literal;
|
|
78
|
+
if (filter.operator === 'between' && Array.isArray(filter.value)) {
|
|
79
|
+
// RFC 0003 bounds are a two-item tuple; in/notIn take an array.
|
|
80
|
+
literal = validateCanonicalValue({
|
|
81
|
+
$hypequery: { type: 'tuple', version: 1, values: filter.value.map(canonicalLiteral) },
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
literal = canonicalLiteral(filter.value);
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
kind: 'comparison',
|
|
89
|
+
operator: filter.operator,
|
|
90
|
+
left: { kind: 'reference', name: filter.field },
|
|
91
|
+
right: { kind: 'literal', value: literal },
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function sharedQueryFields(query) {
|
|
95
|
+
return {
|
|
96
|
+
...(query.dimensions !== undefined ? { dimensions: [...query.dimensions] } : {}),
|
|
97
|
+
...(query.filters !== undefined ? { filters: query.filters.map(filterNode) } : {}),
|
|
98
|
+
...(query.segments !== undefined ? { segments: [...query.segments] } : {}),
|
|
99
|
+
...(query.orderBy !== undefined
|
|
100
|
+
? { orderBy: query.orderBy.map(({ field, direction }) => ({ field, direction })) }
|
|
101
|
+
: {}),
|
|
102
|
+
...(query.limit !== undefined ? { limit: query.limit } : {}),
|
|
103
|
+
...(query.offset !== undefined ? { offset: query.offset } : {}),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/** The tenant capability present on the call, whether or not it is applied. */
|
|
107
|
+
function tenantScope(context) {
|
|
108
|
+
const tenant = context?.runtime?.tenant;
|
|
109
|
+
if (tenant === undefined)
|
|
110
|
+
return { mode: 'none' };
|
|
111
|
+
if (typeof tenant === 'string')
|
|
112
|
+
return { mode: 'scoped', ids: [tenant] };
|
|
113
|
+
if ('scope' in tenant && tenant.scope === 'all')
|
|
114
|
+
return { mode: 'all' };
|
|
115
|
+
if ('id' in tenant)
|
|
116
|
+
return { mode: 'scoped', ids: [tenant.id] };
|
|
117
|
+
if ('in' in tenant)
|
|
118
|
+
return { mode: 'scoped', ids: [...tenant.in] };
|
|
119
|
+
// Unrecognized runtime shapes fail preimage validation and run uncached.
|
|
120
|
+
return tenant;
|
|
121
|
+
}
|
|
122
|
+
function deriveKey(settings, query, definitionIdentity, context, rowLimit) {
|
|
123
|
+
const preimage = buildProtocolCachePreimage({
|
|
124
|
+
secret: settings.secret,
|
|
125
|
+
definitionIdentity,
|
|
126
|
+
query,
|
|
127
|
+
tenant: tenantScope(context),
|
|
128
|
+
rowLimit: rowLimit ?? null,
|
|
129
|
+
});
|
|
130
|
+
return deriveProtocolCacheKey({
|
|
131
|
+
secret: settings.secret,
|
|
132
|
+
namespace: { project: settings.project, environment: settings.environment },
|
|
133
|
+
keyVersion: settings.keyVersion,
|
|
134
|
+
preimage,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* RFC 0009's normalized query has no timezone, yet it moves bucket boundaries
|
|
139
|
+
* and local time-key bounds. It is folded into the definition identity, as
|
|
140
|
+
* `scope` is. UTC, the client default, folds in nothing, so default queries
|
|
141
|
+
* keep the keys other runtimes derive for the same release.
|
|
142
|
+
*/
|
|
143
|
+
function partitionTimezone(query) {
|
|
144
|
+
return query.timezone === 'UTC' ? undefined : query.timezone;
|
|
145
|
+
}
|
|
146
|
+
function definitionFor(settings, root, scope, query, metric) {
|
|
147
|
+
const timezone = partitionTimezone(query);
|
|
148
|
+
return settings.definitionIdentity !== undefined
|
|
149
|
+
? scopedDefinitionIdentity(settings.definitionIdentity, scope, timezone)
|
|
150
|
+
: localDefinitionIdentity(root, { metric, scope, timezone });
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* The RFC 0013 store key for a dataset query, or `undefined` when the call
|
|
154
|
+
* cannot be cached (for example, a filter value with no portable form).
|
|
155
|
+
* `query` must already carry its effective limit.
|
|
156
|
+
*/
|
|
157
|
+
export function datasetCacheKey(settings, ds, query, context, scope) {
|
|
158
|
+
try {
|
|
159
|
+
const wire = {
|
|
160
|
+
kind: 'dataset',
|
|
161
|
+
dataset: ds.name,
|
|
162
|
+
...sharedQueryFields(query),
|
|
163
|
+
...(query.measures !== undefined ? { measures: [...query.measures] } : {}),
|
|
164
|
+
...(query.by !== undefined ? { by: query.by } : {}),
|
|
165
|
+
};
|
|
166
|
+
return deriveKey(settings, wire, definitionFor(settings, ds, scope, query), context, query.limit);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// A cache failure never fails a query: run uncached.
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/** The RFC 0013 store key for a metric query; see `datasetCacheKey`. */
|
|
174
|
+
export function metricCacheKey(settings, metric, query, context, scope) {
|
|
175
|
+
try {
|
|
176
|
+
const ref = getMetricRef(metric);
|
|
177
|
+
const grain = getMetricGrain(metric, query);
|
|
178
|
+
const wire = {
|
|
179
|
+
kind: 'metric',
|
|
180
|
+
dataset: ref.dataset.name,
|
|
181
|
+
metric: ref.name,
|
|
182
|
+
...sharedQueryFields(query),
|
|
183
|
+
...(grain !== undefined ? { by: grain } : {}),
|
|
184
|
+
};
|
|
185
|
+
const definition = definitionFor(settings, ref.dataset, scope, query, ref.spec);
|
|
186
|
+
return deriveKey(settings, wire, definition, context, query.limit);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
import type { AnyDatasetInstance, DatasetQuery, ExecutionContext, GrainedMetricRef, MetricQuery, MetricRef } from '../types.js';
|
|
15
15
|
import { stableStringify } from '../utils/canonical-json.js';
|
|
16
16
|
export { stableStringify };
|
|
17
|
+
/**
|
|
18
|
+
* @deprecated The result cache no longer keys entries by this readable
|
|
19
|
+
* signature. It uses opaque RFC 0009/0013 keys, which never contain tenant ids,
|
|
20
|
+
* filter values, or table names. Kept for callers that used it directly.
|
|
21
|
+
*/
|
|
17
22
|
export declare function buildDatasetQuerySignature(ds: AnyDatasetInstance, query: DatasetQuery, context?: ExecutionContext): string;
|
|
23
|
+
/** @deprecated See `buildDatasetQuerySignature`. */
|
|
18
24
|
export declare function buildMetricQuerySignature(metric: MetricRef | GrainedMetricRef, query: MetricQuery, context?: ExecutionContext): string;
|
|
19
25
|
//# sourceMappingURL=query-signature.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-signature.d.ts","sourceRoot":"","sources":["../../src/cache/query-signature.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"query-signature.d.ts","sourceRoot":"","sources":["../../src/cache/query-signature.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAGhB,WAAW,EACX,SAAS,EACV,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,EAAE,eAAe,EAAE,CAAC;AAsF3B;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAoBR;AAED,oDAAoD;AACpD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,SAAS,GAAG,gBAAgB,EACpC,KAAK,EAAE,WAAW,EAClB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAoBR"}
|