@hypequery/protocol 0.11.1 → 0.13.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/deployments/index.d.ts +1 -1
- package/dist/deployments/index.d.ts.map +1 -1
- package/dist/deployments/limits.d.ts.map +1 -1
- package/dist/deployments/limits.js +1 -0
- package/dist/deployments/types.d.ts +60 -5
- package/dist/deployments/types.d.ts.map +1 -1
- package/dist/deployments/validate.d.ts.map +1 -1
- package/dist/deployments/validate.js +239 -9
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/invocations/errors.d.ts +8 -0
- package/dist/invocations/errors.d.ts.map +1 -0
- package/dist/invocations/errors.js +13 -0
- package/dist/invocations/failure-messages.d.ts +4 -0
- package/dist/invocations/failure-messages.d.ts.map +1 -0
- package/dist/invocations/failure-messages.js +16 -0
- package/dist/invocations/index.d.ts +6 -0
- package/dist/invocations/index.d.ts.map +1 -0
- package/dist/invocations/index.js +4 -0
- package/dist/invocations/limits.d.ts +4 -0
- package/dist/invocations/limits.d.ts.map +1 -0
- package/dist/invocations/limits.js +24 -0
- package/dist/invocations/types.d.ts +111 -0
- package/dist/invocations/types.d.ts.map +1 -0
- package/dist/invocations/types.js +1 -0
- package/dist/invocations/validate.d.ts +8 -0
- package/dist/invocations/validate.d.ts.map +1 -0
- package/dist/invocations/validate.js +306 -0
- package/package.json +1 -1
|
@@ -3,5 +3,5 @@ export { PROTOCOL_DEPLOYMENT_IDENTITY_DOMAIN, encodeProtocolDeploymentContract,
|
|
|
3
3
|
export type { PreparedProtocolDeploymentContract } from './codec.js';
|
|
4
4
|
export { DEFAULT_PROTOCOL_DEPLOYMENT_LIMITS } from './limits.js';
|
|
5
5
|
export { validateProtocolDatasetContract, validateProtocolDeploymentContract, } from './validate.js';
|
|
6
|
-
export type { ProtocolAccessPolicy, ProtocolDatasetContract, ProtocolDatasetDimension, ProtocolDatasetFieldSource, ProtocolDatasetFieldType, ProtocolDatasetFilter, ProtocolDatasetLimits, ProtocolDatasetMeasure, ProtocolDatasetMetric, ProtocolDatasetRelationship, ProtocolDatasetTenantPolicy, ProtocolDeploymentContract, ProtocolDeploymentErrorCode, ProtocolDeploymentLimits, ProtocolDeploymentOptions, ProtocolEndpointPolicy, ProtocolEndpointTenantPolicy, ProtocolNamedQueryContract, ProtocolRuntimeArtifact, } from './types.js';
|
|
6
|
+
export type { ProtocolAccessPolicy, ProtocolDatasetContract, ProtocolDatasetDefaults, ProtocolDatasetDimension, ProtocolDatasetFieldSource, ProtocolDatasetFieldType, ProtocolDatasetFilter, ProtocolDatasetFreshness, ProtocolDatasetLimits, ProtocolDatasetMeasure, ProtocolDatasetMetric, ProtocolDatasetRelationship, ProtocolDatasetTenantPolicy, ProtocolSemanticMetadata, ProtocolSemanticSensitivity, ProtocolDeploymentContract, ProtocolDeploymentErrorCode, ProtocolDeploymentLimits, ProtocolDeploymentOptions, ProtocolEndpointPolicy, ProtocolEndpointTenantPolicy, ProtocolMetricDerivation, ProtocolMetricInput, ProtocolNamedQueryContract, ProtocolRuntimeArtifact, } from './types.js';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/deployments/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EACL,mCAAmC,EACnC,gCAAgC,EAChC,wCAAwC,EACxC,8BAA8B,EAC9B,iCAAiC,GAClC,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,kCAAkC,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EAAE,kCAAkC,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EACL,+BAA+B,EAC/B,kCAAkC,GACnC,MAAM,eAAe,CAAC;AAEvB,YAAY,EACV,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,0BAA0B,EAC1B,wBAAwB,EACxB,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,2BAA2B,EAC3B,2BAA2B,EAC3B,0BAA0B,EAC1B,2BAA2B,EAC3B,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,4BAA4B,EAC5B,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/deployments/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EACL,mCAAmC,EACnC,gCAAgC,EAChC,wCAAwC,EACxC,8BAA8B,EAC9B,iCAAiC,GAClC,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,kCAAkC,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EAAE,kCAAkC,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EACL,+BAA+B,EAC/B,kCAAkC,GACnC,MAAM,eAAe,CAAC;AAEvB,YAAY,EACV,oBAAoB,EACpB,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,EACxB,0BAA0B,EAC1B,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,2BAA2B,EAC3B,2BAA2B,EAC3B,wBAAwB,EACxB,2BAA2B,EAC3B,0BAA0B,EAC1B,2BAA2B,EAC3B,wBAAwB,EACxB,yBAAyB,EACzB,sBAAsB,EACtB,4BAA4B,EAC5B,wBAAwB,EACxB,mBAAmB,EACnB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/deployments/limits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAEtF,eAAO,MAAM,kCAAkC,EAAE,QAAQ,CAAC,wBAAwB,
|
|
1
|
+
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/deployments/limits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAEtF,eAAO,MAAM,kCAAkC,EAAE,QAAQ,CAAC,wBAAwB,CAShF,CAAC;AAEH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,yBAA8B,GACtC,QAAQ,CAAC,wBAAwB,CAAC,CAcpC"}
|
|
@@ -3,6 +3,23 @@ import type { ProtocolIdentifier, ProtocolQualifiedIdentifier } from '../identif
|
|
|
3
3
|
import type { ProtocolQueryImplementation, ProtocolSqlExpression } from '../query-implementations/index.js';
|
|
4
4
|
import type { ProtocolSchema } from '../schemas/index.js';
|
|
5
5
|
export type ProtocolDatasetFieldType = 'string' | 'number' | 'boolean' | 'timestamp';
|
|
6
|
+
export type ProtocolSemanticSensitivity = 'public' | 'internal' | 'confidential' | 'restricted';
|
|
7
|
+
export interface ProtocolSemanticMetadata {
|
|
8
|
+
readonly examples?: readonly string[];
|
|
9
|
+
readonly synonyms?: readonly string[];
|
|
10
|
+
readonly format?: string;
|
|
11
|
+
readonly unit?: string;
|
|
12
|
+
readonly currency?: string;
|
|
13
|
+
readonly timezone?: string;
|
|
14
|
+
readonly sensitivity?: ProtocolSemanticSensitivity;
|
|
15
|
+
}
|
|
16
|
+
export interface ProtocolDatasetFreshness {
|
|
17
|
+
readonly maxAgeSeconds: number;
|
|
18
|
+
}
|
|
19
|
+
export interface ProtocolDatasetDefaults {
|
|
20
|
+
readonly dimensions?: readonly ProtocolIdentifier[];
|
|
21
|
+
readonly timeGrain?: ProtocolTimeGrain;
|
|
22
|
+
}
|
|
6
23
|
export type ProtocolAccessPolicy = {
|
|
7
24
|
readonly kind: 'public';
|
|
8
25
|
} | {
|
|
@@ -34,7 +51,7 @@ export type ProtocolDatasetFieldSource = {
|
|
|
34
51
|
readonly kind: 'column';
|
|
35
52
|
readonly column: string;
|
|
36
53
|
} | ProtocolSqlExpression;
|
|
37
|
-
export interface ProtocolDatasetDimension {
|
|
54
|
+
export interface ProtocolDatasetDimension extends ProtocolSemanticMetadata {
|
|
38
55
|
readonly name: ProtocolIdentifier;
|
|
39
56
|
readonly type: ProtocolDatasetFieldType;
|
|
40
57
|
readonly source: ProtocolDatasetFieldSource;
|
|
@@ -43,7 +60,7 @@ export interface ProtocolDatasetDimension {
|
|
|
43
60
|
readonly label?: string;
|
|
44
61
|
readonly description?: string;
|
|
45
62
|
}
|
|
46
|
-
export interface ProtocolDatasetMeasure {
|
|
63
|
+
export interface ProtocolDatasetMeasure extends ProtocolSemanticMetadata {
|
|
47
64
|
readonly name: ProtocolIdentifier;
|
|
48
65
|
readonly aggregation: 'sum' | 'count' | 'countDistinct' | 'avg' | 'min' | 'max' | 'argMax' | 'argMin' | 'percentile' | 'stddev' | 'variance';
|
|
49
66
|
readonly field: ProtocolQualifiedIdentifier;
|
|
@@ -54,7 +71,7 @@ export interface ProtocolDatasetMeasure {
|
|
|
54
71
|
readonly label?: string;
|
|
55
72
|
readonly description?: string;
|
|
56
73
|
}
|
|
57
|
-
export interface ProtocolDatasetFilter {
|
|
74
|
+
export interface ProtocolDatasetFilter extends ProtocolSemanticMetadata {
|
|
58
75
|
readonly name: ProtocolIdentifier;
|
|
59
76
|
readonly field: ProtocolQualifiedIdentifier;
|
|
60
77
|
readonly operators: readonly ('eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'notIn' | 'between' | 'like')[];
|
|
@@ -69,10 +86,37 @@ export interface ProtocolDatasetRelationship {
|
|
|
69
86
|
readonly to: ProtocolQualifiedIdentifier;
|
|
70
87
|
readonly queryable: boolean;
|
|
71
88
|
}
|
|
72
|
-
|
|
89
|
+
/** One aggregate a derived metric's formula names, under the alias it named it by. */
|
|
90
|
+
export interface ProtocolMetricInput {
|
|
91
|
+
readonly alias: ProtocolIdentifier;
|
|
92
|
+
readonly expression: ProtocolExpression;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* A derived metric's formula in the shape it was authored in.
|
|
96
|
+
*
|
|
97
|
+
* `ProtocolDatasetMetric.expression` inlines each input aggregate where the
|
|
98
|
+
* formula referenced it, which says what the metric means but loses the aliases
|
|
99
|
+
* the author chose. Those aliases are not cosmetic: they are emitted as the
|
|
100
|
+
* column names of the intermediate aggregate and referenced by the outer
|
|
101
|
+
* select, so a catalog rebuilt without them computes the same number through
|
|
102
|
+
* different SQL. `expression` here references the aliases instead, and
|
|
103
|
+
* validation proves that substituting the inputs back into it reproduces the
|
|
104
|
+
* inlined form exactly — the two can describe the same formula or the contract
|
|
105
|
+
* is invalid, never disagree silently.
|
|
106
|
+
*/
|
|
107
|
+
export interface ProtocolMetricDerivation {
|
|
108
|
+
readonly inputs: readonly ProtocolMetricInput[];
|
|
109
|
+
readonly expression: ProtocolExpression;
|
|
110
|
+
}
|
|
111
|
+
export interface ProtocolDatasetMetric extends ProtocolSemanticMetadata {
|
|
73
112
|
readonly name: ProtocolIdentifier;
|
|
74
113
|
readonly kind: 'metric' | 'derived-metric' | 'grained-metric';
|
|
75
114
|
readonly expression: ProtocolExpression;
|
|
115
|
+
/**
|
|
116
|
+
* Present only on a derived metric, and optional: a contract written before
|
|
117
|
+
* this field existed stays valid and simply is not portably executable.
|
|
118
|
+
*/
|
|
119
|
+
readonly derivation?: ProtocolMetricDerivation;
|
|
76
120
|
readonly dimensions: readonly ProtocolQualifiedIdentifier[];
|
|
77
121
|
readonly filters: readonly ProtocolIdentifier[];
|
|
78
122
|
readonly grains: readonly ProtocolTimeGrain[];
|
|
@@ -87,9 +131,13 @@ export interface ProtocolDatasetLimits {
|
|
|
87
131
|
readonly maxFilters?: number;
|
|
88
132
|
readonly maxResultSize?: number;
|
|
89
133
|
}
|
|
90
|
-
export interface ProtocolDatasetContract {
|
|
134
|
+
export interface ProtocolDatasetContract extends ProtocolSemanticMetadata {
|
|
91
135
|
readonly name: ProtocolIdentifier;
|
|
136
|
+
readonly description?: string;
|
|
92
137
|
readonly source: string;
|
|
138
|
+
readonly freshness?: ProtocolDatasetFreshness;
|
|
139
|
+
readonly owner?: string;
|
|
140
|
+
readonly defaults?: ProtocolDatasetDefaults;
|
|
93
141
|
readonly tenant: ProtocolDatasetTenantPolicy;
|
|
94
142
|
readonly timeField?: ProtocolQualifiedIdentifier;
|
|
95
143
|
readonly dimensions: readonly ProtocolDatasetDimension[];
|
|
@@ -129,6 +177,13 @@ export interface ProtocolDeploymentLimits {
|
|
|
129
177
|
readonly maxQueries: number;
|
|
130
178
|
readonly maxArtifacts: number;
|
|
131
179
|
readonly maxDatasetItems: number;
|
|
180
|
+
/**
|
|
181
|
+
* Ceiling on each semantic-metadata collection (`examples`, `synonyms`, and
|
|
182
|
+
* `defaults.dimensions`). Deliberately tighter than `maxDatasetItems`: these
|
|
183
|
+
* are authoring aids, and the definition-time validator in
|
|
184
|
+
* `@hypequery/datasets` applies the same ceiling.
|
|
185
|
+
*/
|
|
186
|
+
readonly maxSemanticMetadataItems: number;
|
|
132
187
|
readonly maxTextBytes: number;
|
|
133
188
|
readonly maxSourceBytes: number;
|
|
134
189
|
readonly maxPathBytes: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/deployments/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,KAAK,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAC/F,OAAO,KAAK,EACV,2BAA2B,EAC3B,qBAAqB,EACtB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,MAAM,MAAM,wBAAwB,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/deployments/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/deployments/validate.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAEV,uBAAuB,
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/deployments/validate.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAEV,uBAAuB,EAUvB,0BAA0B,EAE1B,yBAAyB,EAK1B,MAAM,YAAY,CAAC;AA0wBpB,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,yBAA8B,GACtC,uBAAuB,CAEzB;AAqLD,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,yBAA8B,GACtC,0BAA0B,CA2C5B"}
|
|
@@ -14,6 +14,10 @@ const OPERATORS = new Set([
|
|
|
14
14
|
'eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'in', 'notIn', 'between', 'like',
|
|
15
15
|
]);
|
|
16
16
|
const GRAINS = new Set(['day', 'week', 'month', 'quarter', 'year']);
|
|
17
|
+
const SENSITIVITIES = new Set(['public', 'internal', 'confidential', 'restricted']);
|
|
18
|
+
const SEMANTIC_METADATA_FIELDS = [
|
|
19
|
+
'examples', 'synonyms', 'format', 'unit', 'currency', 'timezone', 'sensitivity',
|
|
20
|
+
];
|
|
17
21
|
function requireRecord(input, path) {
|
|
18
22
|
if (typeof input !== 'object' || input === null || Array.isArray(input)) {
|
|
19
23
|
deploymentError('HQ_DEPLOYMENT_TYPE', path);
|
|
@@ -108,6 +112,30 @@ function uniqueStrings(input, path, maxItems, parse) {
|
|
|
108
112
|
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', path);
|
|
109
113
|
return Object.freeze(result);
|
|
110
114
|
}
|
|
115
|
+
function validateSemanticMetadata(value, result, path, limits) {
|
|
116
|
+
const parseText = (item, itemPath) => (boundedText(item, itemPath, limits.maxTextBytes));
|
|
117
|
+
for (const key of ['examples', 'synonyms']) {
|
|
118
|
+
if (value[key] !== undefined) {
|
|
119
|
+
result[key] = uniqueStrings(value[key], `${path}.${key}`, limits.maxSemanticMetadataItems, parseText);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
for (const key of ['format', 'unit', 'timezone']) {
|
|
123
|
+
optionalText(value[key], key, result, path, limits);
|
|
124
|
+
}
|
|
125
|
+
if (value.currency !== undefined) {
|
|
126
|
+
const currency = boundedText(value.currency, `${path}.currency`, limits.maxTextBytes);
|
|
127
|
+
if (!/^[A-Z]{3}$/.test(currency)) {
|
|
128
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.currency`);
|
|
129
|
+
}
|
|
130
|
+
result.currency = currency;
|
|
131
|
+
}
|
|
132
|
+
if (value.sensitivity !== undefined) {
|
|
133
|
+
if (typeof value.sensitivity !== 'string' || !SENSITIVITIES.has(value.sensitivity)) {
|
|
134
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.sensitivity`);
|
|
135
|
+
}
|
|
136
|
+
result.sensitivity = value.sensitivity;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
111
139
|
function validateAccess(input, path, limits) {
|
|
112
140
|
const value = requireRecord(input, path);
|
|
113
141
|
if (value.kind === 'public') {
|
|
@@ -223,7 +251,7 @@ function validateFieldSource(input, path, limits) {
|
|
|
223
251
|
}
|
|
224
252
|
function validateDimension(input, path, limits) {
|
|
225
253
|
const value = requireRecord(input, path);
|
|
226
|
-
exactFields(value, ['name', 'type', 'source', 'filterable', 'groupable'], ['label', 'description'], path);
|
|
254
|
+
exactFields(value, ['name', 'type', 'source', 'filterable', 'groupable'], ['label', 'description', ...SEMANTIC_METADATA_FIELDS], path);
|
|
227
255
|
if (!['string', 'number', 'boolean', 'timestamp'].includes(value.type)) {
|
|
228
256
|
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.type`);
|
|
229
257
|
}
|
|
@@ -240,11 +268,12 @@ function validateDimension(input, path, limits) {
|
|
|
240
268
|
};
|
|
241
269
|
optionalText(value.label, 'label', result, path, limits);
|
|
242
270
|
optionalText(value.description, 'description', result, path, limits);
|
|
271
|
+
validateSemanticMetadata(value, result, path, limits);
|
|
243
272
|
return freezeRecord(result);
|
|
244
273
|
}
|
|
245
274
|
function validateMeasure(input, path, limits) {
|
|
246
275
|
const value = requireRecord(input, path);
|
|
247
|
-
exactFields(value, ['name', 'aggregation', 'field', 'filters'], ['argField', 'level', 'sql', 'label', 'description'], path);
|
|
276
|
+
exactFields(value, ['name', 'aggregation', 'field', 'filters'], ['argField', 'level', 'sql', 'label', 'description', ...SEMANTIC_METADATA_FIELDS], path);
|
|
248
277
|
if (typeof value.aggregation !== 'string' || !AGGREGATIONS.has(value.aggregation)) {
|
|
249
278
|
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.aggregation`);
|
|
250
279
|
}
|
|
@@ -275,11 +304,12 @@ function validateMeasure(input, path, limits) {
|
|
|
275
304
|
result.sql = nested(() => validateProtocolSqlExpression(value.sql), `${path}.sql`);
|
|
276
305
|
optionalText(value.label, 'label', result, path, limits);
|
|
277
306
|
optionalText(value.description, 'description', result, path, limits);
|
|
307
|
+
validateSemanticMetadata(value, result, path, limits);
|
|
278
308
|
return freezeRecord(result);
|
|
279
309
|
}
|
|
280
310
|
function validateFilter(input, path, limits) {
|
|
281
311
|
const value = requireRecord(input, path);
|
|
282
|
-
exactFields(value, ['name', 'field', 'operators'], ['label', 'description'], path);
|
|
312
|
+
exactFields(value, ['name', 'field', 'operators'], ['label', 'description', ...SEMANTIC_METADATA_FIELDS], path);
|
|
283
313
|
const operators = uniqueStrings(value.operators, `${path}.operators`, limits.maxDatasetItems, (operator, operatorPath) => {
|
|
284
314
|
if (typeof operator !== 'string' || !OPERATORS.has(operator)) {
|
|
285
315
|
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', operatorPath);
|
|
@@ -295,6 +325,7 @@ function validateFilter(input, path, limits) {
|
|
|
295
325
|
};
|
|
296
326
|
optionalText(value.label, 'label', result, path, limits);
|
|
297
327
|
optionalText(value.description, 'description', result, path, limits);
|
|
328
|
+
validateSemanticMetadata(value, result, path, limits);
|
|
298
329
|
return freezeRecord(result);
|
|
299
330
|
}
|
|
300
331
|
function validateRelationship(input, path) {
|
|
@@ -317,9 +348,148 @@ function validateRelationship(input, path) {
|
|
|
317
348
|
queryable: value.queryable,
|
|
318
349
|
});
|
|
319
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* The subset of the expression grammar a derived metric's formula may use.
|
|
353
|
+
*
|
|
354
|
+
* `validateProtocolExpression` accepts the whole of RFC 0003, which is wider
|
|
355
|
+
* than anything a formula can be written in: it permits comparisons, logical
|
|
356
|
+
* operators, a bare aggregate, and a one-argument `round`. A `derivation` exists
|
|
357
|
+
* to be rebuilt and executed, so accepting a form nothing can rebuild would
|
|
358
|
+
* publish a contract that validates and then fails at the point of use. The
|
|
359
|
+
* grammar is pinned here, in the artifact that defines the field, rather than
|
|
360
|
+
* left to whichever runtime happens to read it.
|
|
361
|
+
*/
|
|
362
|
+
function validateFormulaGrammar(expression, path) {
|
|
363
|
+
const node = expression;
|
|
364
|
+
const operand = (child, childPath) => validateFormulaGrammar(child, childPath);
|
|
365
|
+
const numericLiteral = (child, childPath) => {
|
|
366
|
+
const candidate = child;
|
|
367
|
+
if (candidate?.kind !== 'literal' || typeof candidate.value !== 'number') {
|
|
368
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', childPath);
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
switch (node.kind) {
|
|
372
|
+
case 'reference':
|
|
373
|
+
return;
|
|
374
|
+
case 'binary':
|
|
375
|
+
// Arithmetic only, and never over a bare value: the authoring helpers
|
|
376
|
+
// take a name or another expression, never a literal.
|
|
377
|
+
if (!['add', 'subtract', 'multiply', 'divide'].includes(node.operator)) {
|
|
378
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.operator`);
|
|
379
|
+
}
|
|
380
|
+
operand(node.left, `${path}.left`);
|
|
381
|
+
operand(node.right, `${path}.right`);
|
|
382
|
+
return;
|
|
383
|
+
case 'call': {
|
|
384
|
+
const args = node.args;
|
|
385
|
+
const expected = node.function === 'round' || node.function === 'coalesce' ? 2 : 1;
|
|
386
|
+
if (!['nullIfZero', 'coalesce', 'round', 'floor', 'ceil'].includes(node.function)) {
|
|
387
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.function`);
|
|
388
|
+
}
|
|
389
|
+
if (args.length !== expected) {
|
|
390
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.args`);
|
|
391
|
+
}
|
|
392
|
+
operand(args[0], `${path}.args[0]`);
|
|
393
|
+
if (node.function === 'round')
|
|
394
|
+
numericLiteral(args[1], `${path}.args[1]`);
|
|
395
|
+
// A `coalesce` fallback is the one position accepting a bare value.
|
|
396
|
+
if (node.function === 'coalesce' && args[1]?.kind === 'literal') {
|
|
397
|
+
numericLiteral(args[1], `${path}.args[1]`);
|
|
398
|
+
}
|
|
399
|
+
else if (node.function === 'coalesce') {
|
|
400
|
+
operand(args[1], `${path}.args[1]`);
|
|
401
|
+
}
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
default:
|
|
405
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', path);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
/** Structural equality over two already-validated expressions. */
|
|
409
|
+
function sameExpression(left, right) {
|
|
410
|
+
if (left === right)
|
|
411
|
+
return true;
|
|
412
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
413
|
+
return Array.isArray(left) && Array.isArray(right)
|
|
414
|
+
&& left.length === right.length
|
|
415
|
+
&& left.every((item, index) => sameExpression(item, right[index]));
|
|
416
|
+
}
|
|
417
|
+
if (typeof left !== 'object' || typeof right !== 'object' || left === null || right === null) {
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
const leftKeys = Object.keys(left);
|
|
421
|
+
const rightKeys = Object.keys(right);
|
|
422
|
+
return leftKeys.length === rightKeys.length
|
|
423
|
+
&& leftKeys.every(key => Object.hasOwn(right, key)
|
|
424
|
+
&& sameExpression(left[key], right[key]));
|
|
425
|
+
}
|
|
426
|
+
/** Replaces each aliased reference with the aggregate the alias stands for. */
|
|
427
|
+
function substituteInputs(expression, inputs) {
|
|
428
|
+
if (Array.isArray(expression)) {
|
|
429
|
+
return expression.map(item => substituteInputs(item, inputs));
|
|
430
|
+
}
|
|
431
|
+
if (typeof expression !== 'object' || expression === null)
|
|
432
|
+
return expression;
|
|
433
|
+
const record = expression;
|
|
434
|
+
if (record.kind === 'reference' && typeof record.name === 'string' && inputs.has(record.name)) {
|
|
435
|
+
return inputs.get(record.name);
|
|
436
|
+
}
|
|
437
|
+
return Object.fromEntries(Object.entries(record).map(([key, value]) => [key, substituteInputs(value, inputs)]));
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Validates the authored form of a derived metric's formula.
|
|
441
|
+
*
|
|
442
|
+
* Input order is significant and deliberately not sorted: each input becomes a
|
|
443
|
+
* column of the intermediate aggregate in this order, so reordering them
|
|
444
|
+
* reorders the emitted SQL. Two datasets that differ only in input order are
|
|
445
|
+
* genuinely different contracts.
|
|
446
|
+
*/
|
|
447
|
+
function validateDerivation(input, path, limits, inlined) {
|
|
448
|
+
const value = requireRecord(input, path);
|
|
449
|
+
exactFields(value, ['inputs', 'expression'], [], path);
|
|
450
|
+
const items = requireArray(value.inputs, `${path}.inputs`, limits.maxDatasetItems);
|
|
451
|
+
if (items.length === 0)
|
|
452
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.inputs`);
|
|
453
|
+
const seen = new Set();
|
|
454
|
+
const inputs = items.map((item, index) => {
|
|
455
|
+
const itemPath = `${path}.inputs[${index}]`;
|
|
456
|
+
const record = requireRecord(item, itemPath);
|
|
457
|
+
exactFields(record, ['alias', 'expression'], [], itemPath);
|
|
458
|
+
const alias = identifier(record.alias, `${itemPath}.alias`);
|
|
459
|
+
if (seen.has(alias))
|
|
460
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${itemPath}.alias`);
|
|
461
|
+
seen.add(alias);
|
|
462
|
+
const expression = nested(() => validateProtocolExpression(record.expression), `${itemPath}.expression`);
|
|
463
|
+
// Only an aggregate can become a column of the intermediate result. A
|
|
464
|
+
// formula that named something else has no faithful rebuild.
|
|
465
|
+
if (expression.kind !== 'aggregate') {
|
|
466
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${itemPath}.expression`);
|
|
467
|
+
}
|
|
468
|
+
return freezeRecord({ alias, expression });
|
|
469
|
+
});
|
|
470
|
+
const expression = nested(() => validateProtocolExpression(value.expression), `${path}.expression`);
|
|
471
|
+
validateFormulaGrammar(expression, `${path}.expression`);
|
|
472
|
+
// A formula that is a bare reference names one of its inputs instead of
|
|
473
|
+
// combining them. The authoring API cannot produce it — a formula must return
|
|
474
|
+
// a composed expression — so nothing could rebuild it.
|
|
475
|
+
if (expression.kind === 'reference') {
|
|
476
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.expression`);
|
|
477
|
+
}
|
|
478
|
+
// The two forms must describe one formula. Substituting the inputs back into
|
|
479
|
+
// the authored form has to reproduce the inlined one exactly, or the contract
|
|
480
|
+
// advertises a meaning its aliases do not compute.
|
|
481
|
+
const substituted = substituteInputs(expression, new Map(inputs.map(entry => [entry.alias, entry.expression])));
|
|
482
|
+
if (!sameExpression(substituted, inlined)) {
|
|
483
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', `${path}.expression`);
|
|
484
|
+
}
|
|
485
|
+
return freezeRecord({
|
|
486
|
+
inputs: Object.freeze(inputs),
|
|
487
|
+
expression,
|
|
488
|
+
});
|
|
489
|
+
}
|
|
320
490
|
function validateMetric(input, path, limits) {
|
|
321
491
|
const value = requireRecord(input, path);
|
|
322
|
-
exactFields(value, ['name', 'kind', 'expression', 'dimensions', 'filters', 'grains', 'endpoint'], ['grain', 'label', 'description'], path);
|
|
492
|
+
exactFields(value, ['name', 'kind', 'expression', 'dimensions', 'filters', 'grains', 'endpoint'], ['grain', 'derivation', 'label', 'description', ...SEMANTIC_METADATA_FIELDS], path);
|
|
323
493
|
if (!['metric', 'derived-metric', 'grained-metric'].includes(value.kind)) {
|
|
324
494
|
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.kind`);
|
|
325
495
|
}
|
|
@@ -353,8 +523,21 @@ function validateMetric(input, path, limits) {
|
|
|
353
523
|
};
|
|
354
524
|
if (grain !== undefined)
|
|
355
525
|
result.grain = grain;
|
|
526
|
+
if (value.derivation !== undefined) {
|
|
527
|
+
// Eligibility follows the expression, not `kind`. `kind` conflates grain
|
|
528
|
+
// with derivation — a derived metric pinned to a grain reports
|
|
529
|
+
// `grained-metric` — so it can neither confirm nor deny that a formula
|
|
530
|
+
// exists. A metric whose expression is a bare aggregate has none, whatever
|
|
531
|
+
// its kind says, and a derivation attached to one describes something the
|
|
532
|
+
// metric does not do.
|
|
533
|
+
if (result.expression.kind === 'aggregate') {
|
|
534
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.derivation`);
|
|
535
|
+
}
|
|
536
|
+
result.derivation = validateDerivation(value.derivation, `${path}.derivation`, limits, result.expression);
|
|
537
|
+
}
|
|
356
538
|
optionalText(value.label, 'label', result, path, limits);
|
|
357
539
|
optionalText(value.description, 'description', result, path, limits);
|
|
540
|
+
validateSemanticMetadata(value, result, path, limits);
|
|
358
541
|
return freezeRecord(result);
|
|
359
542
|
}
|
|
360
543
|
function validateLimits(input, path) {
|
|
@@ -367,6 +550,31 @@ function validateLimits(input, path) {
|
|
|
367
550
|
}
|
|
368
551
|
return freezeRecord(result);
|
|
369
552
|
}
|
|
553
|
+
function validateFreshness(input, path) {
|
|
554
|
+
const value = requireRecord(input, path);
|
|
555
|
+
exactFields(value, ['maxAgeSeconds'], [], path);
|
|
556
|
+
return freezeRecord({
|
|
557
|
+
maxAgeSeconds: positiveInteger(value.maxAgeSeconds, `${path}.maxAgeSeconds`),
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
function validateDefaults(input, path, limits) {
|
|
561
|
+
const value = requireRecord(input, path);
|
|
562
|
+
exactFields(value, [], ['dimensions', 'timeGrain'], path);
|
|
563
|
+
const result = {};
|
|
564
|
+
if (value.dimensions !== undefined) {
|
|
565
|
+
result.dimensions = uniqueStrings(value.dimensions, `${path}.dimensions`, limits.maxSemanticMetadataItems, (dimension, dimensionPath) => identifier(dimension, dimensionPath));
|
|
566
|
+
}
|
|
567
|
+
if (value.timeGrain !== undefined) {
|
|
568
|
+
if (typeof value.timeGrain !== 'string' || !GRAINS.has(value.timeGrain)) {
|
|
569
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', `${path}.timeGrain`);
|
|
570
|
+
}
|
|
571
|
+
result.timeGrain = value.timeGrain;
|
|
572
|
+
}
|
|
573
|
+
if (Object.keys(result).length === 0) {
|
|
574
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_VALUE', path);
|
|
575
|
+
}
|
|
576
|
+
return freezeRecord(result);
|
|
577
|
+
}
|
|
370
578
|
function namedItems(input, path, maxItems, validate) {
|
|
371
579
|
const items = requireArray(input, path, maxItems).map((value, index) => validate(value, `${path}[${index}]`));
|
|
372
580
|
if (new Set(items.map(item => item.name)).size !== items.length) {
|
|
@@ -376,7 +584,11 @@ function namedItems(input, path, maxItems, validate) {
|
|
|
376
584
|
}
|
|
377
585
|
function validateDataset(input, path, limits) {
|
|
378
586
|
const value = requireRecord(input, path);
|
|
379
|
-
exactFields(value, ['name', 'source', 'tenant', 'dimensions', 'measures', 'filters', 'metrics', 'relationships'], [
|
|
587
|
+
exactFields(value, ['name', 'source', 'tenant', 'dimensions', 'measures', 'filters', 'metrics', 'relationships'], [
|
|
588
|
+
'description', 'freshness', 'owner', 'defaults',
|
|
589
|
+
...SEMANTIC_METADATA_FIELDS,
|
|
590
|
+
'timeField', 'limits', 'endpoint',
|
|
591
|
+
], path);
|
|
380
592
|
const result = {
|
|
381
593
|
name: identifier(value.name, `${path}.name`),
|
|
382
594
|
source: boundedText(value.source, `${path}.source`, limits.maxSourceBytes),
|
|
@@ -389,6 +601,15 @@ function validateDataset(input, path, limits) {
|
|
|
389
601
|
};
|
|
390
602
|
if (value.timeField !== undefined)
|
|
391
603
|
result.timeField = identifier(value.timeField, `${path}.timeField`, true);
|
|
604
|
+
optionalText(value.description, 'description', result, path, limits);
|
|
605
|
+
optionalText(value.owner, 'owner', result, path, limits);
|
|
606
|
+
validateSemanticMetadata(value, result, path, limits);
|
|
607
|
+
if (value.freshness !== undefined) {
|
|
608
|
+
result.freshness = validateFreshness(value.freshness, `${path}.freshness`);
|
|
609
|
+
}
|
|
610
|
+
if (value.defaults !== undefined) {
|
|
611
|
+
result.defaults = validateDefaults(value.defaults, `${path}.defaults`, limits);
|
|
612
|
+
}
|
|
392
613
|
if (value.limits !== undefined)
|
|
393
614
|
result.limits = validateLimits(value.limits, `${path}.limits`);
|
|
394
615
|
if (value.endpoint !== undefined)
|
|
@@ -430,6 +651,12 @@ function validateArtifact(input, path) {
|
|
|
430
651
|
function validateReferences(contract) {
|
|
431
652
|
const datasets = new Map(contract.datasets.map(dataset => [dataset.name, dataset]));
|
|
432
653
|
for (const [datasetIndex, dataset] of contract.datasets.entries()) {
|
|
654
|
+
if (dataset.defaults?.dimensions?.some(name => (!dataset.dimensions.some(dimension => dimension.name === name && dimension.groupable)))) {
|
|
655
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', `$.datasets[${datasetIndex}].defaults.dimensions`);
|
|
656
|
+
}
|
|
657
|
+
if (dataset.defaults?.timeGrain !== undefined && dataset.timeField === undefined) {
|
|
658
|
+
deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', `$.datasets[${datasetIndex}].defaults.timeGrain`);
|
|
659
|
+
}
|
|
433
660
|
for (const [relationshipIndex, relationship] of dataset.relationships.entries()) {
|
|
434
661
|
if (!datasets.has(relationship.target)) {
|
|
435
662
|
deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', `$.datasets[${datasetIndex}].relationships[${relationshipIndex}].target`);
|
|
@@ -453,13 +680,16 @@ function validateReferences(contract) {
|
|
|
453
680
|
&& dataset.timeField === undefined) {
|
|
454
681
|
deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', `$.datasets[${datasetIndex}].metrics[${metricIndex}].grains`);
|
|
455
682
|
}
|
|
456
|
-
|
|
683
|
+
// Biconditional, as for a compiled-SQL query below. An endpoint that
|
|
684
|
+
// requires a tenant over a dataset carrying no tenant field resolves one
|
|
685
|
+
// and then has no column to scope by, so the query reads every tenant
|
|
686
|
+
// while both layers believe tenancy was enforced.
|
|
687
|
+
if ((dataset.tenant.kind === 'required') !== (metric.endpoint.tenant.kind === 'required')) {
|
|
457
688
|
deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', `$.datasets[${datasetIndex}].metrics[${metricIndex}].endpoint.tenant`);
|
|
458
689
|
}
|
|
459
690
|
}
|
|
460
|
-
if (dataset.
|
|
461
|
-
&& dataset.
|
|
462
|
-
&& dataset.endpoint.tenant.kind !== 'required') {
|
|
691
|
+
if (dataset.endpoint !== undefined
|
|
692
|
+
&& (dataset.tenant.kind === 'required') !== (dataset.endpoint.tenant.kind === 'required')) {
|
|
463
693
|
deploymentError('HQ_DEPLOYMENT_INVALID_REFERENCE', `$.datasets[${datasetIndex}].endpoint.tenant`);
|
|
464
694
|
}
|
|
465
695
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,11 +11,14 @@ export type { ProtocolIdentifier, ProtocolIdentifierErrorCode, ProtocolQualified
|
|
|
11
11
|
export { DEFAULT_PROTOCOL_QUERY_IMPLEMENTATION_LIMITS, ProtocolQueryImplementationError, validateProtocolQueryImplementation, validateProtocolSqlExpression, } from './query-implementations/index.js';
|
|
12
12
|
export { DEFAULT_PROTOCOL_QUERY_EVENT_LIMITS, ProtocolQueryDiagnosticsError, ProtocolQueryEventError, validateProtocolQueryDiagnostics, validateProtocolQueryEvent, } from './events/index.js';
|
|
13
13
|
export type { ProtocolQueryDiagnostics, ProtocolQueryDiagnosticsErrorCode, ProtocolQueryErrorCategory, ProtocolQueryEvent, ProtocolQueryEventErrorCode, ProtocolQueryEventLimits, ProtocolQueryEventOptions, ProtocolQueryEventOutcome, ProtocolQueryEventTarget, ProtocolQueryOperation, ProtocolQueryTerminalReason, } from './events/index.js';
|
|
14
|
+
export { DEFAULT_PROTOCOL_SEMANTIC_INVOCATION_LIMITS, ProtocolSemanticInvocationError, validateProtocolSemanticInvocation, validateProtocolSemanticInvocationFailure, validateProtocolSemanticInvocationResult, } from './invocations/index.js';
|
|
15
|
+
export type { ProtocolSemanticInvocation, ProtocolSemanticInvocationBudget, ProtocolSemanticInvocationErrorCode, ProtocolSemanticInvocationFailure, ProtocolSemanticInvocationFailureCategory, ProtocolSemanticInvocationLimits, ProtocolSemanticInvocationMeta, ProtocolSemanticInvocationOptions, ProtocolSemanticInvocationPagination, ProtocolSemanticInvocationResult, ProtocolSemanticInvocationRow, ProtocolSemanticInvocationTarget, ProtocolSemanticInvocationValue, } from './invocations/index.js';
|
|
14
16
|
export { DEFAULT_PROTOCOL_DEPLOYMENT_BUNDLE_LIMITS, PROTOCOL_DEPLOYMENT_BUNDLE_IDENTITY_DOMAIN, ProtocolDeploymentBundleError, encodeProtocolDeploymentBundleManifest, encodeProtocolDeploymentBundleManifestToString, hashProtocolDeploymentBundleManifest, prepareProtocolDeploymentBundleManifest, validateProtocolDeploymentBundleManifest, } from './bundles/index.js';
|
|
15
17
|
export { DEFAULT_PROTOCOL_DEPLOYMENT_RELEASE_LIMITS, PROTOCOL_DEPLOYMENT_RELEASE_IDENTITY_DOMAIN, ProtocolDeploymentReleaseError, encodeProtocolDeploymentReleaseEnvelope, encodeProtocolDeploymentReleaseEnvelopeToString, hashProtocolDeploymentReleaseEnvelope, prepareProtocolDeploymentReleaseEnvelope, validateProtocolDeploymentReleaseEnvelope, validateProtocolDeploymentReleaseTarget, } from './releases/index.js';
|
|
16
18
|
export type { PreparedProtocolDeploymentReleaseEnvelope, ProtocolDeploymentReleaseEnvelope, ProtocolDeploymentReleaseErrorCode, ProtocolDeploymentReleaseLimits, ProtocolDeploymentReleaseOptions, ProtocolDeploymentReleaseTarget, } from './releases/index.js';
|
|
17
19
|
export type { PreparedProtocolDeploymentBundleManifest, ProtocolDeploymentBundleArtifact, ProtocolDeploymentBundleDeployment, ProtocolDeploymentBundleErrorCode, ProtocolDeploymentBundleFile, ProtocolDeploymentBundleLimits, ProtocolDeploymentBundleManifest, ProtocolDeploymentBundleOptions, } from './bundles/index.js';
|
|
18
20
|
export { DEFAULT_PROTOCOL_DEPLOYMENT_LIMITS, PROTOCOL_DEPLOYMENT_IDENTITY_DOMAIN, ProtocolDeploymentError, encodeProtocolDeploymentContract, encodeProtocolDeploymentContractToString, hashProtocolDeploymentContract, prepareProtocolDeploymentContract, validateProtocolDatasetContract, validateProtocolDeploymentContract, } from './deployments/index.js';
|
|
19
|
-
export type { PreparedProtocolDeploymentContract, ProtocolAccessPolicy, ProtocolDatasetContract, ProtocolDatasetDimension, ProtocolDatasetFieldSource, ProtocolDatasetFieldType, ProtocolDatasetFilter, ProtocolDatasetLimits, ProtocolDatasetMeasure, ProtocolDatasetMetric, ProtocolDatasetRelationship, ProtocolDatasetTenantPolicy, ProtocolDeploymentContract, ProtocolDeploymentErrorCode, ProtocolDeploymentLimits, ProtocolDeploymentOptions, ProtocolEndpointPolicy, ProtocolEndpointTenantPolicy, ProtocolNamedQueryContract, ProtocolRuntimeArtifact, } from './deployments/index.js';
|
|
21
|
+
export type { PreparedProtocolDeploymentContract, ProtocolAccessPolicy, ProtocolDatasetContract, ProtocolDatasetDefaults, ProtocolDatasetDimension, ProtocolDatasetFieldSource, ProtocolDatasetFieldType, ProtocolDatasetFilter, ProtocolDatasetFreshness, ProtocolDatasetLimits, ProtocolDatasetMeasure, ProtocolDatasetMetric, ProtocolDatasetRelationship, ProtocolDatasetTenantPolicy, ProtocolSemanticMetadata, ProtocolSemanticSensitivity, ProtocolDeploymentContract, ProtocolDeploymentErrorCode, ProtocolDeploymentLimits, ProtocolDeploymentOptions, ProtocolEndpointPolicy, ProtocolEndpointTenantPolicy, ProtocolMetricDerivation, ProtocolMetricInput, ProtocolNamedQueryContract, ProtocolRuntimeArtifact, } from './deployments/index.js';
|
|
20
22
|
export type { ProtocolQueryImplementation, ProtocolQueryImplementationErrorCode, ProtocolQueryImplementationLimits, ProtocolQueryImplementationOptions, ProtocolSqlDialect, ProtocolSqlExpression, ProtocolSqlParameter, ProtocolSqlParameterSource, ProtocolSqlTenantPolicy, } from './query-implementations/index.js';
|
|
23
|
+
export { PROTOCOL_SEMANTIC_FAILURE_MESSAGES } from './invocations/failure-messages.js';
|
|
21
24
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,8 @@ export { DEFAULT_PROTOCOL_EXPRESSION_LIMITS, ProtocolExpressionError, validatePr
|
|
|
5
5
|
export { DEFAULT_PROTOCOL_SCHEMA_LIMITS, DEFAULT_PROTOCOL_SCHEMA_VALUE_LIMITS, ProtocolSchemaError, ProtocolSchemaValueError, applyProtocolSchemaValue, createProtocolSchemaValueParser, resolveProtocolSchemaValueLimits, validateProtocolSchema, } from './schemas/index.js';
|
|
6
6
|
export { DEFAULT_PROTOCOL_QUERY_IMPLEMENTATION_LIMITS, ProtocolQueryImplementationError, validateProtocolQueryImplementation, validateProtocolSqlExpression, } from './query-implementations/index.js';
|
|
7
7
|
export { DEFAULT_PROTOCOL_QUERY_EVENT_LIMITS, ProtocolQueryDiagnosticsError, ProtocolQueryEventError, validateProtocolQueryDiagnostics, validateProtocolQueryEvent, } from './events/index.js';
|
|
8
|
+
export { DEFAULT_PROTOCOL_SEMANTIC_INVOCATION_LIMITS, ProtocolSemanticInvocationError, validateProtocolSemanticInvocation, validateProtocolSemanticInvocationFailure, validateProtocolSemanticInvocationResult, } from './invocations/index.js';
|
|
8
9
|
export { DEFAULT_PROTOCOL_DEPLOYMENT_BUNDLE_LIMITS, PROTOCOL_DEPLOYMENT_BUNDLE_IDENTITY_DOMAIN, ProtocolDeploymentBundleError, encodeProtocolDeploymentBundleManifest, encodeProtocolDeploymentBundleManifestToString, hashProtocolDeploymentBundleManifest, prepareProtocolDeploymentBundleManifest, validateProtocolDeploymentBundleManifest, } from './bundles/index.js';
|
|
9
10
|
export { DEFAULT_PROTOCOL_DEPLOYMENT_RELEASE_LIMITS, PROTOCOL_DEPLOYMENT_RELEASE_IDENTITY_DOMAIN, ProtocolDeploymentReleaseError, encodeProtocolDeploymentReleaseEnvelope, encodeProtocolDeploymentReleaseEnvelopeToString, hashProtocolDeploymentReleaseEnvelope, prepareProtocolDeploymentReleaseEnvelope, validateProtocolDeploymentReleaseEnvelope, validateProtocolDeploymentReleaseTarget, } from './releases/index.js';
|
|
10
11
|
export { DEFAULT_PROTOCOL_DEPLOYMENT_LIMITS, PROTOCOL_DEPLOYMENT_IDENTITY_DOMAIN, ProtocolDeploymentError, encodeProtocolDeploymentContract, encodeProtocolDeploymentContractToString, hashProtocolDeploymentContract, prepareProtocolDeploymentContract, validateProtocolDatasetContract, validateProtocolDeploymentContract, } from './deployments/index.js';
|
|
12
|
+
export { PROTOCOL_SEMANTIC_FAILURE_MESSAGES } from './invocations/failure-messages.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ProtocolSemanticInvocationErrorCode } from './types.js';
|
|
2
|
+
export declare class ProtocolSemanticInvocationError extends TypeError {
|
|
3
|
+
readonly code: ProtocolSemanticInvocationErrorCode;
|
|
4
|
+
readonly path: string;
|
|
5
|
+
constructor(code: ProtocolSemanticInvocationErrorCode, path?: string);
|
|
6
|
+
}
|
|
7
|
+
export declare function invocationError(code: ProtocolSemanticInvocationErrorCode, path?: string): never;
|
|
8
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/invocations/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,YAAY,CAAC;AAEtE,qBAAa,+BAAgC,SAAQ,SAAS;IAC5D,QAAQ,CAAC,IAAI,EAAE,mCAAmC,CAAC;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,mCAAmC,EAAE,IAAI,SAAM;CAMlE;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,mCAAmC,EACzC,IAAI,SAAM,GACT,KAAK,CAEP"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export class ProtocolSemanticInvocationError extends TypeError {
|
|
2
|
+
code;
|
|
3
|
+
path;
|
|
4
|
+
constructor(code, path = '$') {
|
|
5
|
+
super(`${code} at ${path}`);
|
|
6
|
+
this.name = 'ProtocolSemanticInvocationError';
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.path = path;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function invocationError(code, path = '$') {
|
|
12
|
+
throw new ProtocolSemanticInvocationError(code, path);
|
|
13
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ProtocolSemanticInvocationFailureCategory } from './types.js';
|
|
2
|
+
/** Fixed public messages; provider exceptions belong only in private logs. */
|
|
3
|
+
export declare const PROTOCOL_SEMANTIC_FAILURE_MESSAGES: Readonly<Record<ProtocolSemanticInvocationFailureCategory, string>>;
|
|
4
|
+
//# sourceMappingURL=failure-messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failure-messages.d.ts","sourceRoot":"","sources":["../../src/invocations/failure-messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yCAAyC,EAAE,MAAM,YAAY,CAAC;AAE5E,8EAA8E;AAC9E,eAAO,MAAM,kCAAkC,EAAE,QAAQ,CAAC,MAAM,CAAC,yCAAyC,EAAE,MAAM,CAAC,CAcjH,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Fixed public messages; provider exceptions belong only in private logs. */
|
|
2
|
+
export const PROTOCOL_SEMANTIC_FAILURE_MESSAGES = Object.freeze({
|
|
3
|
+
"configuration-invalid": "The execution configuration is invalid.",
|
|
4
|
+
"not-found": "The requested target was not found.",
|
|
5
|
+
"unauthenticated": "Authentication is required.",
|
|
6
|
+
"forbidden": "Access is forbidden.",
|
|
7
|
+
"tenant-required": "A trusted tenant is required.",
|
|
8
|
+
"input-invalid": "The semantic query is invalid.",
|
|
9
|
+
"budget-exceeded": "The invocation budget was exceeded.",
|
|
10
|
+
"cancelled": "The invocation was cancelled.",
|
|
11
|
+
"stale-activation": "The pinned activation is no longer active.",
|
|
12
|
+
"unsupported-capability": "Portable execution cannot reproduce this target.",
|
|
13
|
+
"executor-unavailable": "The executor is unavailable.",
|
|
14
|
+
"executor-failed": "The executor failed.",
|
|
15
|
+
"output-invalid": "The executor returned an invalid result."
|
|
16
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { PROTOCOL_SEMANTIC_FAILURE_MESSAGES } from './failure-messages.js';
|
|
2
|
+
export { ProtocolSemanticInvocationError } from './errors.js';
|
|
3
|
+
export { DEFAULT_PROTOCOL_SEMANTIC_INVOCATION_LIMITS } from './limits.js';
|
|
4
|
+
export { validateProtocolSemanticInvocation, validateProtocolSemanticInvocationFailure, validateProtocolSemanticInvocationResult, } from './validate.js';
|
|
5
|
+
export type { ProtocolSemanticInvocation, ProtocolSemanticInvocationBudget, ProtocolSemanticInvocationErrorCode, ProtocolSemanticInvocationFailure, ProtocolSemanticInvocationFailureCategory, ProtocolSemanticInvocationLimits, ProtocolSemanticInvocationMeta, ProtocolSemanticInvocationOptions, ProtocolSemanticInvocationPagination, ProtocolSemanticInvocationResult, ProtocolSemanticInvocationRow, ProtocolSemanticInvocationTarget, ProtocolSemanticInvocationValue, } from './types.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/invocations/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kCAAkC,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,+BAA+B,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,2CAA2C,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,EACL,kCAAkC,EAClC,yCAAyC,EACzC,wCAAwC,GACzC,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,0BAA0B,EAC1B,gCAAgC,EAChC,mCAAmC,EACnC,iCAAiC,EACjC,yCAAyC,EACzC,gCAAgC,EAChC,8BAA8B,EAC9B,iCAAiC,EACjC,oCAAoC,EACpC,gCAAgC,EAChC,6BAA6B,EAC7B,gCAAgC,EAChC,+BAA+B,GAChC,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { PROTOCOL_SEMANTIC_FAILURE_MESSAGES } from './failure-messages.js';
|
|
2
|
+
export { ProtocolSemanticInvocationError } from './errors.js';
|
|
3
|
+
export { DEFAULT_PROTOCOL_SEMANTIC_INVOCATION_LIMITS } from './limits.js';
|
|
4
|
+
export { validateProtocolSemanticInvocation, validateProtocolSemanticInvocationFailure, validateProtocolSemanticInvocationResult, } from './validate.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ProtocolSemanticInvocationLimits, ProtocolSemanticInvocationOptions } from './types.js';
|
|
2
|
+
export declare const DEFAULT_PROTOCOL_SEMANTIC_INVOCATION_LIMITS: Readonly<ProtocolSemanticInvocationLimits>;
|
|
3
|
+
export declare function resolveSemanticInvocationLimits(options?: ProtocolSemanticInvocationOptions): Readonly<ProtocolSemanticInvocationLimits>;
|
|
4
|
+
//# sourceMappingURL=limits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/invocations/limits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gCAAgC,EAChC,iCAAiC,EAClC,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,2CAA2C,EACxD,QAAQ,CAAC,gCAAgC,CAQvC,CAAC;AAEH,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,iCAAsC,GAC9C,QAAQ,CAAC,gCAAgC,CAAC,CAe5C"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const DEFAULT_PROTOCOL_SEMANTIC_INVOCATION_LIMITS = Object.freeze({
|
|
2
|
+
maxTextBytes: 1_024,
|
|
3
|
+
maxMessageBytes: 1_024,
|
|
4
|
+
maxRows: 10_000,
|
|
5
|
+
maxColumnsPerRow: 256,
|
|
6
|
+
maxValueBytes: 65_536,
|
|
7
|
+
maxDeadlineMs: 3_600_000,
|
|
8
|
+
maxResponseBytes: 33_554_432,
|
|
9
|
+
});
|
|
10
|
+
export function resolveSemanticInvocationLimits(options = {}) {
|
|
11
|
+
const result = { ...DEFAULT_PROTOCOL_SEMANTIC_INVOCATION_LIMITS };
|
|
12
|
+
for (const key of Object.keys(result)) {
|
|
13
|
+
const value = options.limits?.[key];
|
|
14
|
+
if (value === undefined)
|
|
15
|
+
continue;
|
|
16
|
+
const maximum = DEFAULT_PROTOCOL_SEMANTIC_INVOCATION_LIMITS[key];
|
|
17
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > maximum) {
|
|
18
|
+
throw new RangeError(`${key} must be a positive safe integer no greater than ${maximum} `
|
|
19
|
+
+ '(the semantic invocation v1 maximum)');
|
|
20
|
+
}
|
|
21
|
+
result[key] = value;
|
|
22
|
+
}
|
|
23
|
+
return Object.freeze(result);
|
|
24
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { ProtocolSemanticQuery } from '../expressions/index.js';
|
|
2
|
+
export interface ProtocolSemanticInvocationTarget {
|
|
3
|
+
readonly project: string;
|
|
4
|
+
readonly environment: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Caller-requested ceilings. A runtime applies the most restrictive of these,
|
|
8
|
+
* the endpoint policy, and its own defaults — a caller can tighten a budget but
|
|
9
|
+
* never widen one.
|
|
10
|
+
*/
|
|
11
|
+
export interface ProtocolSemanticInvocationBudget {
|
|
12
|
+
readonly deadlineMs?: number;
|
|
13
|
+
readonly maxRows?: number;
|
|
14
|
+
readonly maxResponseBytes?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* One dataset or metric invocation against an activated deployment.
|
|
18
|
+
*
|
|
19
|
+
* Decision 0002 sketched the dataset/metric name beside the nested query, which
|
|
20
|
+
* would let the two disagree. It also permits normalizing to a single
|
|
21
|
+
* identifier location, so the names live only inside `operation`, whose own
|
|
22
|
+
* `kind` discriminates dataset from metric.
|
|
23
|
+
*
|
|
24
|
+
* There is deliberately no tenant field: a caller cannot supply or change a
|
|
25
|
+
* tenant, which is resolved by the provider callback inside trusted execution
|
|
26
|
+
* context.
|
|
27
|
+
*/
|
|
28
|
+
export interface ProtocolSemanticInvocation {
|
|
29
|
+
readonly kind: 'hypequery-semantic-invocation';
|
|
30
|
+
readonly version: 1;
|
|
31
|
+
readonly target: ProtocolSemanticInvocationTarget;
|
|
32
|
+
/** Pins execution to one activation. Omit to accept whichever is active. */
|
|
33
|
+
readonly activationRevision?: string;
|
|
34
|
+
readonly operation: ProtocolSemanticQuery;
|
|
35
|
+
readonly budget?: ProtocolSemanticInvocationBudget;
|
|
36
|
+
/** External correlation only; never authoritative. */
|
|
37
|
+
readonly correlationId?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A result cell. Portable execution returns scalars; anything richer would need
|
|
41
|
+
* the RFC 0001 tagged value model, which this envelope deliberately does not
|
|
42
|
+
* pull into result rows.
|
|
43
|
+
*/
|
|
44
|
+
export type ProtocolSemanticInvocationValue = string | number | boolean | null;
|
|
45
|
+
export type ProtocolSemanticInvocationRow = Readonly<Record<string, ProtocolSemanticInvocationValue>>;
|
|
46
|
+
export interface ProtocolSemanticInvocationPagination {
|
|
47
|
+
readonly limit: number;
|
|
48
|
+
readonly offset: number;
|
|
49
|
+
readonly hasMore: boolean;
|
|
50
|
+
}
|
|
51
|
+
export interface ProtocolSemanticInvocationMeta {
|
|
52
|
+
readonly rowCount: number;
|
|
53
|
+
readonly pagination?: ProtocolSemanticInvocationPagination;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The successful result. Operational metadata (trace, timing, cache outcome)
|
|
57
|
+
* belongs in the transport's own metadata rather than beside the rows shown to
|
|
58
|
+
* a model, so it is not carried here.
|
|
59
|
+
*/
|
|
60
|
+
export interface ProtocolSemanticInvocationResult {
|
|
61
|
+
readonly kind: 'hypequery-semantic-invocation-result';
|
|
62
|
+
readonly version: 1;
|
|
63
|
+
/** The activation that actually served the call, never the one requested. */
|
|
64
|
+
readonly activationRevision: string;
|
|
65
|
+
readonly data: readonly ProtocolSemanticInvocationRow[];
|
|
66
|
+
readonly meta: ProtocolSemanticInvocationMeta;
|
|
67
|
+
}
|
|
68
|
+
/** Provider-neutral failure categories from decision 0002, plus decision 0005. */
|
|
69
|
+
export type ProtocolSemanticInvocationFailureCategory = 'configuration-invalid' | 'not-found' | 'unauthenticated' | 'forbidden' | 'tenant-required' | 'input-invalid' | 'budget-exceeded' | 'cancelled' | 'stale-activation'
|
|
70
|
+
/** Portable execution cannot reproduce this target exactly. Decision 0005. */
|
|
71
|
+
| 'unsupported-capability' | 'executor-unavailable' | 'executor-failed' | 'output-invalid';
|
|
72
|
+
/**
|
|
73
|
+
* A failed invocation. Closed by construction: there is no field that accepts
|
|
74
|
+
* SQL, parameter values, tenant identifiers, physical source details, stack
|
|
75
|
+
* traces, or a provider exception.
|
|
76
|
+
*/
|
|
77
|
+
export interface ProtocolSemanticInvocationFailure {
|
|
78
|
+
readonly kind: 'hypequery-semantic-invocation-failure';
|
|
79
|
+
readonly version: 1;
|
|
80
|
+
readonly category: ProtocolSemanticInvocationFailureCategory;
|
|
81
|
+
/** Stable machine-readable code, e.g. `HQ_SEMANTIC_TENANT_REQUIRED`. */
|
|
82
|
+
readonly code: string;
|
|
83
|
+
readonly message: string;
|
|
84
|
+
/** Bounded input path for a correctable failure, e.g. `$.operation.limit`. */
|
|
85
|
+
readonly path?: string;
|
|
86
|
+
readonly retryable: boolean;
|
|
87
|
+
/** The caller must re-read discovery before retrying. */
|
|
88
|
+
readonly relist: boolean;
|
|
89
|
+
readonly activationRevision?: string;
|
|
90
|
+
}
|
|
91
|
+
export interface ProtocolSemanticInvocationLimits {
|
|
92
|
+
readonly maxTextBytes: number;
|
|
93
|
+
readonly maxMessageBytes: number;
|
|
94
|
+
readonly maxRows: number;
|
|
95
|
+
readonly maxColumnsPerRow: number;
|
|
96
|
+
readonly maxValueBytes: number;
|
|
97
|
+
readonly maxDeadlineMs: number;
|
|
98
|
+
readonly maxResponseBytes: number;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Validation budgets for a semantic invocation.
|
|
102
|
+
*
|
|
103
|
+
* Each configured value must be a positive safe integer no greater than the
|
|
104
|
+
* corresponding value in `DEFAULT_PROTOCOL_SEMANTIC_INVOCATION_LIMITS`. These
|
|
105
|
+
* options may tighten the v1 conformance limits but cannot raise them.
|
|
106
|
+
*/
|
|
107
|
+
export interface ProtocolSemanticInvocationOptions {
|
|
108
|
+
readonly limits?: Partial<ProtocolSemanticInvocationLimits>;
|
|
109
|
+
}
|
|
110
|
+
export type ProtocolSemanticInvocationErrorCode = 'HQ_INVOCATION_TYPE' | 'HQ_INVOCATION_UNKNOWN_FIELD' | 'HQ_INVOCATION_INVALID_VERSION' | 'HQ_INVOCATION_INVALID_VALUE' | 'HQ_INVOCATION_TOO_MANY_ITEMS' | 'HQ_INVOCATION_TOO_LARGE' | 'HQ_INVOCATION_UNSAFE_OBJECT';
|
|
111
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/invocations/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ProtocolSemanticInvocation, ProtocolSemanticInvocationFailure, ProtocolSemanticInvocationOptions, ProtocolSemanticInvocationResult } from './types.js';
|
|
2
|
+
/** Validate one dataset or metric invocation request. */
|
|
3
|
+
export declare function validateProtocolSemanticInvocation(input: unknown, options?: ProtocolSemanticInvocationOptions): ProtocolSemanticInvocation;
|
|
4
|
+
/** Validate a successful invocation result before it is returned or cached. */
|
|
5
|
+
export declare function validateProtocolSemanticInvocationResult(input: unknown, options?: ProtocolSemanticInvocationOptions): ProtocolSemanticInvocationResult;
|
|
6
|
+
/** Validate a failed invocation before it crosses a trust boundary. */
|
|
7
|
+
export declare function validateProtocolSemanticInvocationFailure(input: unknown, options?: ProtocolSemanticInvocationOptions): ProtocolSemanticInvocationFailure;
|
|
8
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/invocations/validate.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,0BAA0B,EAE1B,iCAAiC,EAGjC,iCAAiC,EACjC,gCAAgC,EAGjC,MAAM,YAAY,CAAC;AA+KpB,yDAAyD;AACzD,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,iCAAsC,GAC9C,0BAA0B,CAgC5B;AAyED,+EAA+E;AAC/E,wBAAgB,wCAAwC,CACtD,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,iCAAsC,GAC9C,gCAAgC,CAiBlC;AAED,uEAAuE;AACvE,wBAAgB,yCAAyC,CACvD,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,iCAAsC,GAC9C,iCAAiC,CAyCnC"}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { ProtocolExpressionError, validateProtocolSemanticQuery } from '../expressions/index.js';
|
|
2
|
+
import { PROTOCOL_SEMANTIC_FAILURE_MESSAGES } from './failure-messages.js';
|
|
3
|
+
import { invocationError } from './errors.js';
|
|
4
|
+
import { resolveSemanticInvocationLimits } from './limits.js';
|
|
5
|
+
const HEX64_PATTERN = /^[0-9a-f]{64}$/;
|
|
6
|
+
const FAILURE_CATEGORIES = [
|
|
7
|
+
'configuration-invalid',
|
|
8
|
+
'not-found',
|
|
9
|
+
'unauthenticated',
|
|
10
|
+
'forbidden',
|
|
11
|
+
'tenant-required',
|
|
12
|
+
'input-invalid',
|
|
13
|
+
'budget-exceeded',
|
|
14
|
+
'cancelled',
|
|
15
|
+
'stale-activation',
|
|
16
|
+
'unsupported-capability',
|
|
17
|
+
'executor-unavailable',
|
|
18
|
+
'executor-failed',
|
|
19
|
+
'output-invalid',
|
|
20
|
+
];
|
|
21
|
+
const FAILURE_CODE_PATTERN = /^[A-Z][A-Z0-9_]*$/;
|
|
22
|
+
const textEncoder = new TextEncoder();
|
|
23
|
+
function requireRecord(input, path) {
|
|
24
|
+
if (typeof input !== 'object' || input === null || Array.isArray(input)) {
|
|
25
|
+
invocationError('HQ_INVOCATION_TYPE', path);
|
|
26
|
+
}
|
|
27
|
+
const prototype = Object.getPrototypeOf(input);
|
|
28
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
29
|
+
invocationError('HQ_INVOCATION_UNSAFE_OBJECT', path);
|
|
30
|
+
}
|
|
31
|
+
if (Object.getOwnPropertySymbols(input).length > 0) {
|
|
32
|
+
invocationError('HQ_INVOCATION_UNSAFE_OBJECT', path);
|
|
33
|
+
}
|
|
34
|
+
for (const descriptor of Object.values(Object.getOwnPropertyDescriptors(input))) {
|
|
35
|
+
if (!descriptor.enumerable || !('value' in descriptor)) {
|
|
36
|
+
invocationError('HQ_INVOCATION_UNSAFE_OBJECT', path);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return input;
|
|
40
|
+
}
|
|
41
|
+
function requireArray(input, path, maximum) {
|
|
42
|
+
if (!Array.isArray(input))
|
|
43
|
+
invocationError('HQ_INVOCATION_TYPE', path);
|
|
44
|
+
if (input.length > maximum)
|
|
45
|
+
invocationError('HQ_INVOCATION_TOO_MANY_ITEMS', path);
|
|
46
|
+
if (Object.getPrototypeOf(input) !== Array.prototype || Object.getOwnPropertySymbols(input).length > 0) {
|
|
47
|
+
invocationError('HQ_INVOCATION_UNSAFE_OBJECT', path);
|
|
48
|
+
}
|
|
49
|
+
const descriptors = Object.getOwnPropertyDescriptors(input);
|
|
50
|
+
if (Object.keys(descriptors).length !== input.length + 1) {
|
|
51
|
+
invocationError('HQ_INVOCATION_UNSAFE_OBJECT', path);
|
|
52
|
+
}
|
|
53
|
+
for (let index = 0; index < input.length; index++) {
|
|
54
|
+
const descriptor = descriptors[String(index)];
|
|
55
|
+
if (!descriptor || !descriptor.enumerable || !('value' in descriptor)) {
|
|
56
|
+
invocationError('HQ_INVOCATION_UNSAFE_OBJECT', path);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return input;
|
|
60
|
+
}
|
|
61
|
+
function exactFields(value, required, optional, path) {
|
|
62
|
+
const allowed = new Set([...required, ...optional]);
|
|
63
|
+
for (const key of Object.keys(value)) {
|
|
64
|
+
if (!allowed.has(key))
|
|
65
|
+
invocationError('HQ_INVOCATION_UNKNOWN_FIELD', `${path}.${key}`);
|
|
66
|
+
}
|
|
67
|
+
for (const key of required) {
|
|
68
|
+
if (!Object.hasOwn(value, key))
|
|
69
|
+
invocationError('HQ_INVOCATION_TYPE', `${path}.${key}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function freezeRecord(entries) {
|
|
73
|
+
return Object.freeze(Object.assign(Object.create(null), entries));
|
|
74
|
+
}
|
|
75
|
+
function hasControlCharacter(text) {
|
|
76
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
77
|
+
const code = text.charCodeAt(index);
|
|
78
|
+
if (code <= 0x1f || (code >= 0x7f && code <= 0x9f))
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
function boundedText(value, path, maximum) {
|
|
84
|
+
if (typeof value !== 'string')
|
|
85
|
+
invocationError('HQ_INVOCATION_TYPE', path);
|
|
86
|
+
if (value.length > maximum || textEncoder.encode(value).byteLength > maximum) {
|
|
87
|
+
invocationError('HQ_INVOCATION_TOO_LARGE', path);
|
|
88
|
+
}
|
|
89
|
+
if (hasControlCharacter(value))
|
|
90
|
+
invocationError('HQ_INVOCATION_INVALID_VALUE', path);
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
function boundedInteger(value, path, maximum) {
|
|
94
|
+
if (typeof value !== 'number')
|
|
95
|
+
invocationError('HQ_INVOCATION_TYPE', path);
|
|
96
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
97
|
+
invocationError('HQ_INVOCATION_INVALID_VALUE', path);
|
|
98
|
+
}
|
|
99
|
+
if (value > maximum)
|
|
100
|
+
invocationError('HQ_INVOCATION_TOO_LARGE', path);
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
function nonNegativeInteger(value, path, maximum) {
|
|
104
|
+
if (typeof value !== 'number')
|
|
105
|
+
invocationError('HQ_INVOCATION_TYPE', path);
|
|
106
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
107
|
+
invocationError('HQ_INVOCATION_INVALID_VALUE', path);
|
|
108
|
+
}
|
|
109
|
+
if (value > maximum)
|
|
110
|
+
invocationError('HQ_INVOCATION_TOO_LARGE', path);
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
function requireBoolean(value, path) {
|
|
114
|
+
if (typeof value !== 'boolean')
|
|
115
|
+
invocationError('HQ_INVOCATION_TYPE', path);
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
function activationRevision(value, path) {
|
|
119
|
+
if (typeof value !== 'string')
|
|
120
|
+
invocationError('HQ_INVOCATION_TYPE', path);
|
|
121
|
+
if (!HEX64_PATTERN.test(value))
|
|
122
|
+
invocationError('HQ_INVOCATION_INVALID_VALUE', path);
|
|
123
|
+
return value;
|
|
124
|
+
}
|
|
125
|
+
function requireVersion(value, kind, path) {
|
|
126
|
+
if (value.kind !== kind) {
|
|
127
|
+
if (typeof value.kind !== 'string')
|
|
128
|
+
invocationError('HQ_INVOCATION_TYPE', `${path}.kind`);
|
|
129
|
+
invocationError('HQ_INVOCATION_INVALID_VALUE', `${path}.kind`);
|
|
130
|
+
}
|
|
131
|
+
if (value.version !== 1) {
|
|
132
|
+
if (typeof value.version !== 'number')
|
|
133
|
+
invocationError('HQ_INVOCATION_TYPE', `${path}.version`);
|
|
134
|
+
invocationError('HQ_INVOCATION_INVALID_VERSION', `${path}.version`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function target(input, path, limits) {
|
|
138
|
+
const value = requireRecord(input, path);
|
|
139
|
+
exactFields(value, ['project', 'environment'], [], path);
|
|
140
|
+
const result = {};
|
|
141
|
+
for (const key of ['project', 'environment']) {
|
|
142
|
+
const text = boundedText(value[key], `${path}.${key}`, limits.maxTextBytes);
|
|
143
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(text)) {
|
|
144
|
+
invocationError('HQ_INVOCATION_INVALID_VALUE', `${path}.${key}`);
|
|
145
|
+
}
|
|
146
|
+
result[key] = text;
|
|
147
|
+
}
|
|
148
|
+
return freezeRecord(result);
|
|
149
|
+
}
|
|
150
|
+
function budget(input, path, limits) {
|
|
151
|
+
const value = requireRecord(input, path);
|
|
152
|
+
exactFields(value, [], ['deadlineMs', 'maxRows', 'maxResponseBytes'], path);
|
|
153
|
+
const result = {};
|
|
154
|
+
if (value.deadlineMs !== undefined) {
|
|
155
|
+
result.deadlineMs = boundedInteger(value.deadlineMs, `${path}.deadlineMs`, limits.maxDeadlineMs);
|
|
156
|
+
}
|
|
157
|
+
if (value.maxRows !== undefined) {
|
|
158
|
+
result.maxRows = boundedInteger(value.maxRows, `${path}.maxRows`, limits.maxRows);
|
|
159
|
+
}
|
|
160
|
+
if (value.maxResponseBytes !== undefined) {
|
|
161
|
+
result.maxResponseBytes = boundedInteger(value.maxResponseBytes, `${path}.maxResponseBytes`, limits.maxResponseBytes);
|
|
162
|
+
}
|
|
163
|
+
// An empty budget is indistinguishable from omitting the field, and two
|
|
164
|
+
// encodings of the same request are not portable.
|
|
165
|
+
if (Object.keys(result).length === 0)
|
|
166
|
+
invocationError('HQ_INVOCATION_INVALID_VALUE', path);
|
|
167
|
+
return freezeRecord(result);
|
|
168
|
+
}
|
|
169
|
+
/** Validate one dataset or metric invocation request. */
|
|
170
|
+
export function validateProtocolSemanticInvocation(input, options = {}) {
|
|
171
|
+
const limits = resolveSemanticInvocationLimits(options);
|
|
172
|
+
const value = requireRecord(input, '$');
|
|
173
|
+
exactFields(value, ['kind', 'version', 'target', 'operation'], ['activationRevision', 'budget', 'correlationId'], '$');
|
|
174
|
+
requireVersion(value, 'hypequery-semantic-invocation', '$');
|
|
175
|
+
const result = {
|
|
176
|
+
kind: 'hypequery-semantic-invocation',
|
|
177
|
+
version: 1,
|
|
178
|
+
target: target(value.target, '$.target', limits),
|
|
179
|
+
operation: semanticQuery(value.operation, '$.operation'),
|
|
180
|
+
};
|
|
181
|
+
if (value.activationRevision !== undefined) {
|
|
182
|
+
result.activationRevision = activationRevision(value.activationRevision, '$.activationRevision');
|
|
183
|
+
}
|
|
184
|
+
if (value.budget !== undefined)
|
|
185
|
+
result.budget = budget(value.budget, '$.budget', limits);
|
|
186
|
+
if (value.correlationId !== undefined) {
|
|
187
|
+
result.correlationId = boundedText(value.correlationId, '$.correlationId', limits.maxTextBytes);
|
|
188
|
+
}
|
|
189
|
+
return freezeRecord(result);
|
|
190
|
+
}
|
|
191
|
+
function semanticQuery(input, path) {
|
|
192
|
+
try {
|
|
193
|
+
return validateProtocolSemanticQuery(input);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
if (error instanceof ProtocolExpressionError) {
|
|
197
|
+
invocationError('HQ_INVOCATION_INVALID_VALUE', path);
|
|
198
|
+
}
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function row(input, path, limits) {
|
|
203
|
+
const value = requireRecord(input, path);
|
|
204
|
+
const keys = Object.keys(value);
|
|
205
|
+
if (keys.length > limits.maxColumnsPerRow) {
|
|
206
|
+
invocationError('HQ_INVOCATION_TOO_MANY_ITEMS', path);
|
|
207
|
+
}
|
|
208
|
+
const result = {};
|
|
209
|
+
for (const key of keys) {
|
|
210
|
+
const cell = value[key];
|
|
211
|
+
const cellPath = `${path}.${key}`;
|
|
212
|
+
if (cell === null || typeof cell === 'boolean') {
|
|
213
|
+
result[key] = cell;
|
|
214
|
+
}
|
|
215
|
+
else if (typeof cell === 'number') {
|
|
216
|
+
// A non-finite number has no portable JSON encoding.
|
|
217
|
+
if (!Number.isFinite(cell))
|
|
218
|
+
invocationError('HQ_INVOCATION_INVALID_VALUE', cellPath);
|
|
219
|
+
result[key] = cell;
|
|
220
|
+
}
|
|
221
|
+
else if (typeof cell === 'string') {
|
|
222
|
+
if (textEncoder.encode(cell).byteLength > limits.maxValueBytes) {
|
|
223
|
+
invocationError('HQ_INVOCATION_TOO_LARGE', cellPath);
|
|
224
|
+
}
|
|
225
|
+
if (hasControlCharacter(cell))
|
|
226
|
+
invocationError('HQ_INVOCATION_INVALID_VALUE', cellPath);
|
|
227
|
+
result[key] = cell;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
invocationError('HQ_INVOCATION_TYPE', cellPath);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return freezeRecord(result);
|
|
234
|
+
}
|
|
235
|
+
function meta(input, path, limits, rowCount) {
|
|
236
|
+
const value = requireRecord(input, path);
|
|
237
|
+
exactFields(value, ['rowCount'], ['pagination'], path);
|
|
238
|
+
const declared = nonNegativeInteger(value.rowCount, `${path}.rowCount`, limits.maxRows);
|
|
239
|
+
// A rowCount that disagrees with the rows it describes is a broken result,
|
|
240
|
+
// not a hint — the caller would page or aggregate on a false total.
|
|
241
|
+
if (declared !== rowCount)
|
|
242
|
+
invocationError('HQ_INVOCATION_INVALID_VALUE', `${path}.rowCount`);
|
|
243
|
+
const result = { rowCount: declared };
|
|
244
|
+
if (value.pagination !== undefined) {
|
|
245
|
+
const paginationPath = `${path}.pagination`;
|
|
246
|
+
const pagination = requireRecord(value.pagination, paginationPath);
|
|
247
|
+
exactFields(pagination, ['limit', 'offset', 'hasMore'], [], paginationPath);
|
|
248
|
+
result.pagination = freezeRecord({
|
|
249
|
+
limit: boundedInteger(pagination.limit, `${paginationPath}.limit`, limits.maxRows),
|
|
250
|
+
offset: nonNegativeInteger(pagination.offset, `${paginationPath}.offset`, Number.MAX_SAFE_INTEGER),
|
|
251
|
+
hasMore: requireBoolean(pagination.hasMore, `${paginationPath}.hasMore`),
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
return freezeRecord(result);
|
|
255
|
+
}
|
|
256
|
+
/** Validate a successful invocation result before it is returned or cached. */
|
|
257
|
+
export function validateProtocolSemanticInvocationResult(input, options = {}) {
|
|
258
|
+
const limits = resolveSemanticInvocationLimits(options);
|
|
259
|
+
const value = requireRecord(input, '$');
|
|
260
|
+
exactFields(value, ['kind', 'version', 'activationRevision', 'data', 'meta'], [], '$');
|
|
261
|
+
requireVersion(value, 'hypequery-semantic-invocation-result', '$');
|
|
262
|
+
const rows = requireArray(value.data, '$.data', limits.maxRows);
|
|
263
|
+
const data = Object.freeze(rows.map((entry, index) => row(entry, `$.data[${index}]`, limits)));
|
|
264
|
+
return freezeRecord({
|
|
265
|
+
kind: 'hypequery-semantic-invocation-result',
|
|
266
|
+
version: 1,
|
|
267
|
+
activationRevision: activationRevision(value.activationRevision, '$.activationRevision'),
|
|
268
|
+
data,
|
|
269
|
+
meta: meta(value.meta, '$.meta', limits, data.length),
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/** Validate a failed invocation before it crosses a trust boundary. */
|
|
273
|
+
export function validateProtocolSemanticInvocationFailure(input, options = {}) {
|
|
274
|
+
const limits = resolveSemanticInvocationLimits(options);
|
|
275
|
+
const value = requireRecord(input, '$');
|
|
276
|
+
exactFields(value, ['kind', 'version', 'category', 'code', 'message', 'retryable', 'relist'], ['path', 'activationRevision'], '$');
|
|
277
|
+
requireVersion(value, 'hypequery-semantic-invocation-failure', '$');
|
|
278
|
+
if (typeof value.category !== 'string')
|
|
279
|
+
invocationError('HQ_INVOCATION_TYPE', '$.category');
|
|
280
|
+
if (!FAILURE_CATEGORIES.includes(value.category)) {
|
|
281
|
+
invocationError('HQ_INVOCATION_INVALID_VALUE', '$.category');
|
|
282
|
+
}
|
|
283
|
+
const message = boundedText(value.message, '$.message', limits.maxMessageBytes);
|
|
284
|
+
if (message !== PROTOCOL_SEMANTIC_FAILURE_MESSAGES[value.category]) {
|
|
285
|
+
invocationError('HQ_INVOCATION_INVALID_VALUE', '$.message');
|
|
286
|
+
}
|
|
287
|
+
const code = boundedText(value.code, '$.code', limits.maxTextBytes);
|
|
288
|
+
if (!FAILURE_CODE_PATTERN.test(code))
|
|
289
|
+
invocationError('HQ_INVOCATION_INVALID_VALUE', '$.code');
|
|
290
|
+
const result = {
|
|
291
|
+
kind: 'hypequery-semantic-invocation-failure',
|
|
292
|
+
version: 1,
|
|
293
|
+
category: value.category,
|
|
294
|
+
code,
|
|
295
|
+
message,
|
|
296
|
+
retryable: requireBoolean(value.retryable, '$.retryable'),
|
|
297
|
+
relist: requireBoolean(value.relist, '$.relist'),
|
|
298
|
+
};
|
|
299
|
+
if (value.path !== undefined) {
|
|
300
|
+
result.path = boundedText(value.path, '$.path', limits.maxTextBytes);
|
|
301
|
+
}
|
|
302
|
+
if (value.activationRevision !== undefined) {
|
|
303
|
+
result.activationRevision = activationRevision(value.activationRevision, '$.activationRevision');
|
|
304
|
+
}
|
|
305
|
+
return freezeRecord(result);
|
|
306
|
+
}
|