@hypequery/datasets 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/aggregations.d.ts +3 -0
- package/dist/aggregations.d.ts.map +1 -1
- package/dist/aggregations.js +6 -0
- package/dist/cache/definition-identity.js +2 -2
- package/dist/cache/query-signature.d.ts.map +1 -1
- package/dist/cache/query-signature.js +3 -0
- package/dist/catalog.d.ts +3 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +3 -2
- package/dist/client-contract/query-schema.d.ts +19 -0
- package/dist/client-contract/query-schema.d.ts.map +1 -0
- package/dist/client-contract/query-schema.js +85 -0
- package/dist/constants.d.ts +5 -4
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +7 -6
- package/dist/contract.d.ts +2 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +4 -3
- package/dist/dataset-sql-dialect.d.ts +117 -0
- package/dist/dataset-sql-dialect.d.ts.map +1 -1
- package/dist/dataset.d.ts +1 -1
- package/dist/dataset.d.ts.map +1 -1
- package/dist/dataset.js +2 -1
- package/dist/in-memory-backend.d.ts.map +1 -1
- package/dist/in-memory-backend.js +2 -0
- package/dist/index.d.ts +48 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -6
- package/dist/internal.d.ts +5 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +5 -1
- package/dist/measure.d.ts +9 -1
- package/dist/measure.d.ts.map +1 -1
- package/dist/measure.js +9 -1
- package/dist/metric-query-engine.d.ts.map +1 -1
- package/dist/metric-query-engine.js +4 -3
- package/dist/protocol-adapter.d.ts.map +1 -1
- package/dist/protocol-adapter.js +5 -3
- package/dist/protocol-deployment-adapter.d.ts.map +1 -1
- package/dist/protocol-deployment-adapter.js +7 -1
- package/dist/protocol-schema-adapter.d.ts +7 -7
- package/dist/protocol-schema-adapter.d.ts.map +1 -1
- package/dist/protocol-schema-adapter.js +12 -245
- package/dist/query-builder-protocol.d.ts +32 -1
- package/dist/query-builder-protocol.d.ts.map +1 -1
- package/dist/query-planner.d.ts +14 -5
- package/dist/query-planner.d.ts.map +1 -1
- package/dist/query-planner.js +53 -34
- package/dist/relationship-check.d.ts.map +1 -1
- package/dist/relationship-check.js +5 -3
- package/dist/schemas.d.ts +17 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +16 -0
- package/dist/semantic-planner.d.ts.map +1 -1
- package/dist/semantic-planner.js +16 -0
- package/dist/semantic-query-schema.d.ts +2 -1
- package/dist/semantic-query-schema.d.ts.map +1 -1
- package/dist/semantic-query-schema.js +12 -12
- package/dist/types.d.ts +22 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/aggregate-state-functions.d.ts +24 -0
- package/dist/utils/aggregate-state-functions.d.ts.map +1 -0
- package/dist/utils/aggregate-state-functions.js +60 -0
- package/dist/utils/approximate-measures.d.ts +3 -1
- package/dist/utils/approximate-measures.d.ts.map +1 -1
- package/dist/utils/approximate-measures.js +8 -2
- package/dist/utils/base-measure-definition.d.ts +16 -0
- package/dist/utils/base-measure-definition.d.ts.map +1 -1
- package/dist/utils/base-measure-definition.js +31 -0
- package/dist/utils/build-dataset-query-builder.d.ts.map +1 -1
- package/dist/utils/build-dataset-query-builder.js +16 -8
- package/dist/utils/by-name.d.ts +8 -0
- package/dist/utils/by-name.d.ts.map +1 -0
- package/dist/utils/by-name.js +44 -0
- package/dist/utils/calendar-shift-sql.d.ts +5 -4
- package/dist/utils/calendar-shift-sql.d.ts.map +1 -1
- package/dist/utils/calendar-shift-sql.js +8 -8
- package/dist/utils/clickhouse-dataset-sql-dialect.d.ts +2 -2
- package/dist/utils/clickhouse-dataset-sql-dialect.d.ts.map +1 -1
- package/dist/utils/clickhouse-dataset-sql-dialect.js +140 -1
- package/dist/utils/compile-dataset-query.d.ts.map +1 -1
- package/dist/utils/compile-dataset-query.js +9 -6
- package/dist/utils/composite-time-measure-sql.d.ts +3 -5
- package/dist/utils/composite-time-measure-sql.d.ts.map +1 -1
- package/dist/utils/composite-time-measure-sql.js +15 -16
- package/dist/utils/count-aggregations.d.ts +4 -0
- package/dist/utils/count-aggregations.d.ts.map +1 -0
- package/dist/utils/count-aggregations.js +6 -0
- package/dist/utils/dataset-definition-validation.d.ts.map +1 -1
- package/dist/utils/dataset-definition-validation.js +36 -26
- package/dist/utils/dataset-derived-query.d.ts +2 -4
- package/dist/utils/dataset-derived-query.d.ts.map +1 -1
- package/dist/utils/dataset-derived-query.js +11 -14
- package/dist/utils/dataset-having.d.ts +5 -7
- package/dist/utils/dataset-having.d.ts.map +1 -1
- package/dist/utils/dataset-having.js +9 -12
- package/dist/utils/dataset-normalization.d.ts.map +1 -1
- package/dist/utils/dataset-normalization.js +1 -0
- package/dist/utils/dataset-query-validation.d.ts.map +1 -1
- package/dist/utils/dataset-query-validation.js +7 -0
- package/dist/utils/dataset-source.d.ts +9 -0
- package/dist/utils/dataset-source.d.ts.map +1 -0
- package/dist/utils/dataset-source.js +16 -0
- package/dist/utils/dataset-sql-dialect.d.ts +13 -2
- package/dist/utils/dataset-sql-dialect.d.ts.map +1 -1
- package/dist/utils/dataset-sql-dialect.js +74 -1
- package/dist/utils/dataset-validation.d.ts.map +1 -1
- package/dist/utils/dataset-validation.js +7 -0
- package/dist/utils/deployable-formulas.d.ts +33 -0
- package/dist/utils/deployable-formulas.d.ts.map +1 -0
- package/dist/utils/deployable-formulas.js +102 -0
- package/dist/utils/derived-cte-validation.d.ts +5 -0
- package/dist/utils/derived-cte-validation.d.ts.map +1 -1
- package/dist/utils/derived-cte-validation.js +77 -8
- package/dist/utils/derived-measure-sql.d.ts +10 -2
- package/dist/utils/derived-measure-sql.d.ts.map +1 -1
- package/dist/utils/derived-measure-sql.js +23 -43
- package/dist/utils/filter-field-type.d.ts +14 -0
- package/dist/utils/filter-field-type.d.ts.map +1 -0
- package/dist/utils/filter-field-type.js +21 -0
- package/dist/utils/filtered-aggregation-sql.d.ts +2 -1
- package/dist/utils/filtered-aggregation-sql.d.ts.map +1 -1
- package/dist/utils/filtered-aggregation-sql.js +18 -35
- package/dist/utils/groupable.d.ts +7 -0
- package/dist/utils/groupable.d.ts.map +1 -0
- package/dist/utils/groupable.js +8 -0
- package/dist/utils/metric-derived-query.d.ts +2 -4
- package/dist/utils/metric-derived-query.d.ts.map +1 -1
- package/dist/utils/metric-derived-query.js +54 -41
- package/dist/utils/metric-query-builder.d.ts.map +1 -1
- package/dist/utils/metric-query-builder.js +17 -9
- package/dist/utils/metric-query-validation.d.ts.map +1 -1
- package/dist/utils/metric-query-validation.js +7 -0
- package/dist/utils/portable-aggregations.d.ts +1 -0
- package/dist/utils/portable-aggregations.d.ts.map +1 -1
- package/dist/utils/portable-aggregations.js +5 -0
- package/dist/utils/protocol-schema-conversion.d.ts +12 -0
- package/dist/utils/protocol-schema-conversion.d.ts.map +1 -0
- package/dist/utils/protocol-schema-conversion.js +61 -0
- package/dist/utils/query-field-references.d.ts +10 -0
- package/dist/utils/query-field-references.d.ts.map +1 -0
- package/dist/utils/query-field-references.js +38 -0
- package/dist/utils/query-timezone.d.ts +41 -3
- package/dist/utils/query-timezone.d.ts.map +1 -1
- package/dist/utils/query-timezone.js +75 -8
- package/dist/utils/raw-sql-dialect-check.d.ts +12 -0
- package/dist/utils/raw-sql-dialect-check.d.ts.map +1 -0
- package/dist/utils/raw-sql-dialect-check.js +37 -0
- package/dist/utils/relationship-builder-plan.d.ts +25 -8
- package/dist/utils/relationship-builder-plan.d.ts.map +1 -1
- package/dist/utils/relationship-builder-plan.js +23 -9
- package/dist/utils/relationship-measure-sql.d.ts +14 -4
- package/dist/utils/relationship-measure-sql.d.ts.map +1 -1
- package/dist/utils/relationship-measure-sql.js +33 -10
- package/dist/utils/relationship-measures.d.ts.map +1 -1
- package/dist/utils/relationship-measures.js +3 -0
- package/dist/utils/semantic-tool.d.ts +2 -2
- package/dist/utils/semantic-tool.d.ts.map +1 -1
- package/dist/utils/semantic-tool.js +1 -1
- package/dist/utils/shift-measure-sql.d.ts +5 -4
- package/dist/utils/shift-measure-sql.d.ts.map +1 -1
- package/dist/utils/shift-measure-sql.js +25 -26
- package/dist/utils/shift-wall-time-sql.d.ts +2 -1
- package/dist/utils/shift-wall-time-sql.d.ts.map +1 -1
- package/dist/utils/shift-wall-time-sql.js +7 -7
- package/dist/utils/statement-parameters.d.ts +58 -0
- package/dist/utils/statement-parameters.d.ts.map +1 -0
- package/dist/utils/statement-parameters.js +101 -0
- package/dist/utils/time-axis-calendar-guard.d.ts +2 -1
- package/dist/utils/time-axis-calendar-guard.d.ts.map +1 -1
- package/dist/utils/time-axis-calendar-guard.js +5 -7
- package/dist/utils/time-measure-axis-sql.d.ts +2 -1
- package/dist/utils/time-measure-axis-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-axis-sql.js +19 -22
- package/dist/utils/time-measure-context-sql.d.ts +3 -2
- package/dist/utils/time-measure-context-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-context-sql.js +11 -11
- package/dist/utils/time-measure-dataset-sql.d.ts +7 -5
- package/dist/utils/time-measure-dataset-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-dataset-sql.js +17 -12
- package/dist/utils/time-measure-leaf-sql.d.ts +2 -1
- package/dist/utils/time-measure-leaf-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-leaf-sql.js +17 -18
- package/dist/utils/time-measure-result-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-result-sql.js +10 -11
- package/dist/utils/time-measure-source-sql.d.ts +2 -2
- package/dist/utils/time-measure-source-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-source-sql.js +25 -16
- package/dist/utils/time-measure-values-sql.d.ts +4 -3
- package/dist/utils/time-measure-values-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-values-sql.js +26 -29
- package/dist/utils/timestamp-instants.d.ts +17 -0
- package/dist/utils/timestamp-instants.d.ts.map +1 -0
- package/dist/utils/timestamp-instants.js +34 -0
- package/dist/utils/timestamp-text.d.ts +12 -0
- package/dist/utils/timestamp-text.d.ts.map +1 -0
- package/dist/utils/timestamp-text.js +18 -0
- package/dist/utils/zod-json-schema.d.ts +8 -0
- package/dist/utils/zod-json-schema.d.ts.map +1 -0
- package/dist/utils/zod-json-schema.js +18 -0
- package/dist/utils/zod-schema-version.d.ts +19 -0
- package/dist/utils/zod-schema-version.d.ts.map +1 -0
- package/dist/utils/zod-schema-version.js +6 -0
- package/dist/utils/zod-v3-protocol-schema.d.ts +10 -0
- package/dist/utils/zod-v3-protocol-schema.d.ts.map +1 -0
- package/dist/utils/zod-v3-protocol-schema.js +164 -0
- package/dist/utils/zod-v4-protocol-schema.d.ts +12 -0
- package/dist/utils/zod-v4-protocol-schema.d.ts.map +1 -0
- package/dist/utils/zod-v4-protocol-schema.js +171 -0
- package/dist/validation.d.ts +7 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +42 -8
- package/package.json +21 -12
- package/dist/api.type-test.d.ts +0 -2
- package/dist/api.type-test.d.ts.map +0 -1
- package/dist/api.type-test.js +0 -251
- package/dist/utils/time-arithmetic-sql.d.ts +0 -4
- package/dist/utils/time-arithmetic-sql.d.ts.map +0 -1
- package/dist/utils/time-arithmetic-sql.js +0 -9
- package/dist/utils/window-aggregation-sql.d.ts +0 -5
- package/dist/utils/window-aggregation-sql.d.ts.map +0 -1
- package/dist/utils/window-aggregation-sql.js +0 -17
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { ProtocolSchemaAdapterError, annotate, canonicalValue, nativeEnumValues, withDefault, } from './protocol-schema-conversion.js';
|
|
2
|
+
function typeName(schema) {
|
|
3
|
+
return String(schema?._def?.typeName ?? 'Unknown');
|
|
4
|
+
}
|
|
5
|
+
function convertString(schema, path) {
|
|
6
|
+
const result = { kind: 'string' };
|
|
7
|
+
for (const check of schema._def.checks) {
|
|
8
|
+
switch (check.kind) {
|
|
9
|
+
case 'min':
|
|
10
|
+
result.minLength = check.value;
|
|
11
|
+
break;
|
|
12
|
+
case 'max':
|
|
13
|
+
result.maxLength = check.value;
|
|
14
|
+
break;
|
|
15
|
+
case 'length':
|
|
16
|
+
result.minLength = check.value;
|
|
17
|
+
result.maxLength = check.value;
|
|
18
|
+
break;
|
|
19
|
+
default:
|
|
20
|
+
throw new ProtocolSchemaAdapterError(`Unsupported Zod string check "${String(check.kind)}"`, path);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return annotate(schema.description, result);
|
|
24
|
+
}
|
|
25
|
+
function convertNumber(schema, path) {
|
|
26
|
+
const result = { kind: 'number' };
|
|
27
|
+
for (const check of schema._def.checks) {
|
|
28
|
+
switch (check.kind) {
|
|
29
|
+
case 'int':
|
|
30
|
+
result.kind = 'integer';
|
|
31
|
+
break;
|
|
32
|
+
case 'min':
|
|
33
|
+
result[check.inclusive === false ? 'exclusiveMinimum' : 'minimum'] = check.value;
|
|
34
|
+
break;
|
|
35
|
+
case 'max':
|
|
36
|
+
result[check.inclusive === false ? 'exclusiveMaximum' : 'maximum'] = check.value;
|
|
37
|
+
break;
|
|
38
|
+
case 'finite':
|
|
39
|
+
break;
|
|
40
|
+
default:
|
|
41
|
+
throw new ProtocolSchemaAdapterError(`Unsupported Zod number check "${String(check.kind)}"`, path);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return annotate(schema.description, result);
|
|
45
|
+
}
|
|
46
|
+
function isOptionalProperty(schema) {
|
|
47
|
+
const name = typeName(schema);
|
|
48
|
+
return name === 'ZodOptional' || name === 'ZodDefault';
|
|
49
|
+
}
|
|
50
|
+
function isUnconstrainedRecordKey(schema) {
|
|
51
|
+
if (schema === undefined || typeName(schema) !== 'ZodString')
|
|
52
|
+
return false;
|
|
53
|
+
const definition = schema._def;
|
|
54
|
+
return Array.isArray(definition.checks)
|
|
55
|
+
&& definition.checks.length === 0
|
|
56
|
+
&& definition.coerce !== true;
|
|
57
|
+
}
|
|
58
|
+
/** Converts the portable subset of a Zod 3 schema into an RFC 0004 draft. */
|
|
59
|
+
export function convertZodV3Schema(schema, path) {
|
|
60
|
+
const definition = schema._def;
|
|
61
|
+
const done = (result) => annotate(schema.description, result);
|
|
62
|
+
switch (typeName(schema)) {
|
|
63
|
+
case 'ZodAny':
|
|
64
|
+
case 'ZodUnknown':
|
|
65
|
+
return done({ kind: 'any' });
|
|
66
|
+
case 'ZodNever':
|
|
67
|
+
case 'ZodVoid':
|
|
68
|
+
case 'ZodUndefined':
|
|
69
|
+
return done({ kind: 'void' });
|
|
70
|
+
case 'ZodNull':
|
|
71
|
+
return done({ kind: 'null' });
|
|
72
|
+
case 'ZodBoolean':
|
|
73
|
+
return done({ kind: 'boolean' });
|
|
74
|
+
case 'ZodString':
|
|
75
|
+
return convertString(schema, path);
|
|
76
|
+
case 'ZodNumber':
|
|
77
|
+
return convertNumber(schema, path);
|
|
78
|
+
case 'ZodLiteral':
|
|
79
|
+
return done({ kind: 'literal', value: canonicalValue(definition.value, `${path}.value`) });
|
|
80
|
+
case 'ZodEnum':
|
|
81
|
+
return done({ kind: 'enum', values: [...definition.values] });
|
|
82
|
+
case 'ZodNativeEnum':
|
|
83
|
+
return done({ kind: 'enum', values: nativeEnumValues(definition.values) });
|
|
84
|
+
case 'ZodArray': {
|
|
85
|
+
const result = {
|
|
86
|
+
kind: 'array',
|
|
87
|
+
items: convertZodV3Schema(definition.type, `${path}.items`),
|
|
88
|
+
};
|
|
89
|
+
if (definition.minLength?.value !== undefined)
|
|
90
|
+
result.minItems = definition.minLength.value;
|
|
91
|
+
if (definition.maxLength?.value !== undefined)
|
|
92
|
+
result.maxItems = definition.maxLength.value;
|
|
93
|
+
if (definition.exactLength?.value !== undefined) {
|
|
94
|
+
result.minItems = definition.exactLength.value;
|
|
95
|
+
result.maxItems = definition.exactLength.value;
|
|
96
|
+
}
|
|
97
|
+
return done(result);
|
|
98
|
+
}
|
|
99
|
+
case 'ZodObject': {
|
|
100
|
+
if (typeName(definition.catchall) !== 'ZodNever') {
|
|
101
|
+
throw new ProtocolSchemaAdapterError('Unsupported Zod object catchall', path);
|
|
102
|
+
}
|
|
103
|
+
const shape = definition.shape();
|
|
104
|
+
return done({
|
|
105
|
+
kind: 'object',
|
|
106
|
+
properties: Object.fromEntries(Object.entries(shape).map(([name, property]) => [
|
|
107
|
+
name,
|
|
108
|
+
convertZodV3Schema(property, `${path}.properties.${name}`),
|
|
109
|
+
])),
|
|
110
|
+
required: Object.entries(shape)
|
|
111
|
+
.filter(([, property]) => !isOptionalProperty(property))
|
|
112
|
+
.map(([name]) => name),
|
|
113
|
+
unknownProperties: definition.unknownKeys === 'passthrough'
|
|
114
|
+
? 'preserve'
|
|
115
|
+
: definition.unknownKeys === 'strict'
|
|
116
|
+
? 'reject'
|
|
117
|
+
: 'strip',
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
case 'ZodRecord':
|
|
121
|
+
if (!isUnconstrainedRecordKey(definition.keyType)) {
|
|
122
|
+
throw new ProtocolSchemaAdapterError('Unsupported constrained Zod record key', path);
|
|
123
|
+
}
|
|
124
|
+
return done({ kind: 'record', values: convertZodV3Schema(definition.valueType, `${path}.values`) });
|
|
125
|
+
case 'ZodUnion':
|
|
126
|
+
return done({
|
|
127
|
+
kind: 'union',
|
|
128
|
+
variants: definition.options.map((option, index) => convertZodV3Schema(option, `${path}.variants[${index}]`)),
|
|
129
|
+
});
|
|
130
|
+
case 'ZodDiscriminatedUnion':
|
|
131
|
+
// RFC 0004 has no separate discriminator index. Converting every option
|
|
132
|
+
// drops only Zod's lookup metadata; each object's literal discriminator
|
|
133
|
+
// property remains part of its protocol variant.
|
|
134
|
+
return done({
|
|
135
|
+
kind: 'union',
|
|
136
|
+
variants: [...definition.options.values()].map((option, index) => convertZodV3Schema(option, `${path}.variants[${index}]`)),
|
|
137
|
+
});
|
|
138
|
+
case 'ZodNullable':
|
|
139
|
+
return done({
|
|
140
|
+
kind: 'union',
|
|
141
|
+
variants: [convertZodV3Schema(definition.innerType, `${path}.variants[0]`), { kind: 'null' }],
|
|
142
|
+
});
|
|
143
|
+
case 'ZodOptional':
|
|
144
|
+
return done(convertZodV3Schema(definition.innerType, path));
|
|
145
|
+
case 'ZodDefault':
|
|
146
|
+
return done(withDefault(convertZodV3Schema(definition.innerType, path), definition.defaultValue(), path));
|
|
147
|
+
case 'ZodBranded':
|
|
148
|
+
return done(convertZodV3Schema(definition.type, path));
|
|
149
|
+
case 'ZodReadonly':
|
|
150
|
+
return done(convertZodV3Schema(definition.innerType, path));
|
|
151
|
+
case 'ZodEffects': {
|
|
152
|
+
const effectType = String(definition.effect?.type ?? 'unknown');
|
|
153
|
+
if (effectType !== 'refinement') {
|
|
154
|
+
throw new ProtocolSchemaAdapterError(`Unsupported Zod effect "${effectType}"`, path);
|
|
155
|
+
}
|
|
156
|
+
// A refinement preserves the wrapped schema's input and output shape.
|
|
157
|
+
// The rule itself remains enforced by Zod, but cannot be advertised by
|
|
158
|
+
// ProtocolSchema (for example, a cross-field selection requirement).
|
|
159
|
+
return done(convertZodV3Schema(definition.schema, path));
|
|
160
|
+
}
|
|
161
|
+
default:
|
|
162
|
+
throw new ProtocolSchemaAdapterError(`Unsupported Zod type "${typeName(schema)}"`, path);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ProtocolSchemaDraft } from './protocol-schema-conversion.js';
|
|
2
|
+
/** A Zod 4 schema, read through `_zod.def`. Classic schemas also expose `description`. */
|
|
3
|
+
type ZodV4Schema = {
|
|
4
|
+
readonly _zod: {
|
|
5
|
+
readonly def: Record<string, any>;
|
|
6
|
+
};
|
|
7
|
+
readonly description?: string;
|
|
8
|
+
};
|
|
9
|
+
/** Converts the portable subset of a Zod 4 schema into an RFC 0004 draft. */
|
|
10
|
+
export declare function convertZodV4Schema(schema: ZodV4Schema, path: string): ProtocolSchemaDraft;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=zod-v4-protocol-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod-v4-protocol-schema.d.ts","sourceRoot":"","sources":["../../src/utils/zod-v4-protocol-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,mBAAmB,EACzB,MAAM,iCAAiC,CAAC;AAEzC,0FAA0F;AAC1F,KAAK,WAAW,GAAG;IACjB,QAAQ,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAAE,CAAC;IACrD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAwHF,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,mBAAmB,CA4EzF"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { ProtocolSchemaAdapterError, annotate, canonicalValue, nativeEnumValues, withDefault, } from './protocol-schema-conversion.js';
|
|
2
|
+
const INTEGER_FORMATS = new Set(['safeint', 'int32', 'uint32']);
|
|
3
|
+
function typeOf(schema) {
|
|
4
|
+
return String(schema?._zod?.def?.type ?? 'unknown');
|
|
5
|
+
}
|
|
6
|
+
function checksOf(schema) {
|
|
7
|
+
return (schema._zod.def.checks ?? []).map(check => check._zod.def);
|
|
8
|
+
}
|
|
9
|
+
/** Zod 4 stores `.refine()` as a `custom` check on the schema it refines. */
|
|
10
|
+
function isRefinement(check) {
|
|
11
|
+
return check.check === 'custom';
|
|
12
|
+
}
|
|
13
|
+
function applyLengthChecks(schema, result, keys, label, path) {
|
|
14
|
+
for (const check of checksOf(schema)) {
|
|
15
|
+
switch (check.check) {
|
|
16
|
+
case 'min_length':
|
|
17
|
+
result[keys.min] = check.minimum;
|
|
18
|
+
break;
|
|
19
|
+
case 'max_length':
|
|
20
|
+
result[keys.max] = check.maximum;
|
|
21
|
+
break;
|
|
22
|
+
case 'length_equals':
|
|
23
|
+
result[keys.min] = check.length;
|
|
24
|
+
result[keys.max] = check.length;
|
|
25
|
+
break;
|
|
26
|
+
default:
|
|
27
|
+
if (isRefinement(check))
|
|
28
|
+
break;
|
|
29
|
+
throw new ProtocolSchemaAdapterError(`Unsupported Zod ${label} check "${String(check.format ?? check.check)}"`, path);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
function convertNumber(schema, path) {
|
|
35
|
+
const result = { kind: 'number' };
|
|
36
|
+
for (const check of checksOf(schema)) {
|
|
37
|
+
switch (check.check) {
|
|
38
|
+
case 'number_format':
|
|
39
|
+
if (!INTEGER_FORMATS.has(check.format)) {
|
|
40
|
+
throw new ProtocolSchemaAdapterError(`Unsupported Zod number check "${String(check.format)}"`, path);
|
|
41
|
+
}
|
|
42
|
+
result.kind = 'integer';
|
|
43
|
+
break;
|
|
44
|
+
case 'greater_than':
|
|
45
|
+
result[check.inclusive ? 'minimum' : 'exclusiveMinimum'] = check.value;
|
|
46
|
+
break;
|
|
47
|
+
case 'less_than':
|
|
48
|
+
result[check.inclusive ? 'maximum' : 'exclusiveMaximum'] = check.value;
|
|
49
|
+
break;
|
|
50
|
+
default:
|
|
51
|
+
if (isRefinement(check))
|
|
52
|
+
break;
|
|
53
|
+
throw new ProtocolSchemaAdapterError(`Unsupported Zod number check "${String(check.check)}"`, path);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
function isOptionalProperty(schema) {
|
|
59
|
+
const type = typeOf(schema);
|
|
60
|
+
return type === 'optional' || type === 'default';
|
|
61
|
+
}
|
|
62
|
+
function isUnconstrainedRecordKey(schema) {
|
|
63
|
+
return schema !== undefined
|
|
64
|
+
&& typeOf(schema) === 'string'
|
|
65
|
+
&& checksOf(schema).length === 0
|
|
66
|
+
&& schema._zod.def.coerce !== true;
|
|
67
|
+
}
|
|
68
|
+
function unknownProperties(catchall, path) {
|
|
69
|
+
if (catchall === undefined)
|
|
70
|
+
return 'strip';
|
|
71
|
+
switch (typeOf(catchall)) {
|
|
72
|
+
case 'never':
|
|
73
|
+
return 'reject';
|
|
74
|
+
case 'unknown':
|
|
75
|
+
return 'preserve';
|
|
76
|
+
default:
|
|
77
|
+
throw new ProtocolSchemaAdapterError('Unsupported Zod object catchall', path);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function literal(values, path) {
|
|
81
|
+
if (values.length === 1) {
|
|
82
|
+
return { kind: 'literal', value: canonicalValue(values[0], `${path}.value`) };
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
kind: 'union',
|
|
86
|
+
variants: values.map((value, index) => ({
|
|
87
|
+
kind: 'literal',
|
|
88
|
+
value: canonicalValue(value, `${path}.variants[${index}].value`),
|
|
89
|
+
})),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Zod 4 has no effects wrapper: a transform is a pipe into a `transform`, and
|
|
94
|
+
* a preprocessor is a pipe out of one. Neither preserves the advertised shape.
|
|
95
|
+
*/
|
|
96
|
+
function pipeEffect(definition) {
|
|
97
|
+
if (typeOf(definition.out) === 'transform')
|
|
98
|
+
return 'transform';
|
|
99
|
+
if (typeOf(definition.in) === 'transform')
|
|
100
|
+
return 'preprocess';
|
|
101
|
+
return 'pipe';
|
|
102
|
+
}
|
|
103
|
+
/** Converts the portable subset of a Zod 4 schema into an RFC 0004 draft. */
|
|
104
|
+
export function convertZodV4Schema(schema, path) {
|
|
105
|
+
const definition = schema._zod.def;
|
|
106
|
+
const done = (result) => annotate(schema.description, result);
|
|
107
|
+
switch (typeOf(schema)) {
|
|
108
|
+
case 'any':
|
|
109
|
+
case 'unknown':
|
|
110
|
+
return done({ kind: 'any' });
|
|
111
|
+
case 'never':
|
|
112
|
+
case 'void':
|
|
113
|
+
case 'undefined':
|
|
114
|
+
return done({ kind: 'void' });
|
|
115
|
+
case 'null':
|
|
116
|
+
return done({ kind: 'null' });
|
|
117
|
+
case 'boolean':
|
|
118
|
+
return done({ kind: 'boolean' });
|
|
119
|
+
case 'string':
|
|
120
|
+
return done(applyLengthChecks(schema, { kind: 'string' }, { min: 'minLength', max: 'maxLength' }, 'string', path));
|
|
121
|
+
case 'number':
|
|
122
|
+
return done(convertNumber(schema, path));
|
|
123
|
+
case 'literal':
|
|
124
|
+
return done(literal(definition.values, path));
|
|
125
|
+
case 'enum':
|
|
126
|
+
return done({ kind: 'enum', values: nativeEnumValues(definition.entries) });
|
|
127
|
+
case 'array':
|
|
128
|
+
return done(applyLengthChecks(schema, { kind: 'array', items: convertZodV4Schema(definition.element, `${path}.items`) }, { min: 'minItems', max: 'maxItems' }, 'array', path));
|
|
129
|
+
case 'object': {
|
|
130
|
+
const policy = unknownProperties(definition.catchall, path);
|
|
131
|
+
const shape = definition.shape;
|
|
132
|
+
return done({
|
|
133
|
+
kind: 'object',
|
|
134
|
+
properties: Object.fromEntries(Object.entries(shape).map(([name, property]) => [
|
|
135
|
+
name,
|
|
136
|
+
convertZodV4Schema(property, `${path}.properties.${name}`),
|
|
137
|
+
])),
|
|
138
|
+
required: Object.entries(shape)
|
|
139
|
+
.filter(([, property]) => !isOptionalProperty(property))
|
|
140
|
+
.map(([name]) => name),
|
|
141
|
+
unknownProperties: policy,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
case 'record':
|
|
145
|
+
if (!isUnconstrainedRecordKey(definition.keyType)) {
|
|
146
|
+
throw new ProtocolSchemaAdapterError('Unsupported constrained Zod record key', path);
|
|
147
|
+
}
|
|
148
|
+
return done({ kind: 'record', values: convertZodV4Schema(definition.valueType, `${path}.values`) });
|
|
149
|
+
case 'union':
|
|
150
|
+
// A discriminated union is a union with a `discriminator`. RFC 0004 has
|
|
151
|
+
// no discriminator index; each variant keeps its literal property.
|
|
152
|
+
return done({
|
|
153
|
+
kind: 'union',
|
|
154
|
+
variants: definition.options.map((option, index) => convertZodV4Schema(option, `${path}.variants[${index}]`)),
|
|
155
|
+
});
|
|
156
|
+
case 'nullable':
|
|
157
|
+
return done({
|
|
158
|
+
kind: 'union',
|
|
159
|
+
variants: [convertZodV4Schema(definition.innerType, `${path}.variants[0]`), { kind: 'null' }],
|
|
160
|
+
});
|
|
161
|
+
case 'optional':
|
|
162
|
+
case 'readonly':
|
|
163
|
+
return done(convertZodV4Schema(definition.innerType, path));
|
|
164
|
+
case 'default':
|
|
165
|
+
return done(withDefault(convertZodV4Schema(definition.innerType, path), definition.defaultValue, path));
|
|
166
|
+
case 'pipe':
|
|
167
|
+
throw new ProtocolSchemaAdapterError(`Unsupported Zod effect "${pipeEffect(definition)}"`, path);
|
|
168
|
+
default:
|
|
169
|
+
throw new ProtocolSchemaAdapterError(`Unsupported Zod type "${typeOf(schema)}"`, path);
|
|
170
|
+
}
|
|
171
|
+
}
|
package/dist/validation.d.ts
CHANGED
|
@@ -17,6 +17,13 @@ export interface ValidationResult {
|
|
|
17
17
|
* @returns true if the value matches the field type
|
|
18
18
|
*/
|
|
19
19
|
export declare function matchesFieldType(fieldType: FieldType, value: unknown): boolean;
|
|
20
|
+
/** True when a list holds both instants (`Z` or an offset) and offset-free timestamps. */
|
|
21
|
+
export declare function mixesInstantsAndLocalTimes(values: readonly unknown[]): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* An `in` list is one predicate, so every member is read in the same zone:
|
|
24
|
+
* an instant and a local time cannot share one without changing a meaning.
|
|
25
|
+
*/
|
|
26
|
+
export declare function mixedInstantListError(field: string): string;
|
|
20
27
|
/**
|
|
21
28
|
* Validates a filter value against the expected field type.
|
|
22
29
|
*
|
package/dist/validation.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAI1D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAgB9E;AAED,0FAA0F;AAC1F,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAG9E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3D;AAgBD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CA6C7F"}
|
package/dist/validation.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared validation utilities for semantic layer queries.
|
|
3
3
|
*/
|
|
4
|
+
import { parseTimestampInstant } from './utils/timestamp-instants.js';
|
|
5
|
+
import { isTimestampText } from './utils/timestamp-text.js';
|
|
4
6
|
/**
|
|
5
7
|
* Checks if a value matches the expected field type.
|
|
6
8
|
*
|
|
@@ -11,16 +13,45 @@
|
|
|
11
13
|
export function matchesFieldType(fieldType, value) {
|
|
12
14
|
switch (fieldType) {
|
|
13
15
|
case 'string':
|
|
14
|
-
case 'timestamp':
|
|
15
16
|
return typeof value === 'string';
|
|
17
|
+
case 'timestamp':
|
|
18
|
+
return isTimestampText(value);
|
|
16
19
|
case 'number':
|
|
17
|
-
|
|
20
|
+
// Number filters bind as Float64; an integer beyond 2^53 is already rounded
|
|
21
|
+
// and would silently match nothing against an exact Int64/UInt64 column.
|
|
22
|
+
return typeof value === 'number' && Number.isFinite(value)
|
|
23
|
+
&& (!Number.isInteger(value) || Number.isSafeInteger(value));
|
|
18
24
|
case 'boolean':
|
|
19
25
|
return typeof value === 'boolean';
|
|
20
26
|
default:
|
|
21
27
|
return false;
|
|
22
28
|
}
|
|
23
29
|
}
|
|
30
|
+
/** True when a list holds both instants (`Z` or an offset) and offset-free timestamps. */
|
|
31
|
+
export function mixesInstantsAndLocalTimes(values) {
|
|
32
|
+
const instants = values.map(value => parseTimestampInstant(value) !== undefined);
|
|
33
|
+
return instants.some(Boolean) && !instants.every(Boolean);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* An `in` list is one predicate, so every member is read in the same zone:
|
|
37
|
+
* an instant and a local time cannot share one without changing a meaning.
|
|
38
|
+
*/
|
|
39
|
+
export function mixedInstantListError(field) {
|
|
40
|
+
return `"in" cannot mix instants (with Z or an offset) and local timestamps for field "${field}"; use one form.`;
|
|
41
|
+
}
|
|
42
|
+
function typeError(filter, fieldType, plural) {
|
|
43
|
+
if (fieldType === 'timestamp') {
|
|
44
|
+
const what = plural ? 'ISO 8601 timestamps' : 'an ISO 8601 timestamp';
|
|
45
|
+
return `"${filter.operator}" expects ${what} for field "${filter.field}", such as 2026-01-31 or 2026-01-31T12:00:00Z, with a real calendar date and time.`;
|
|
46
|
+
}
|
|
47
|
+
if (fieldType === 'number') {
|
|
48
|
+
const what = plural ? 'number values' : 'a number value';
|
|
49
|
+
return `"${filter.operator}" expects ${what} for field "${filter.field}"; integers must be safe integers (within ±2^53) to compare exactly. Declare a large id as dimension.string() to filter it exactly.`;
|
|
50
|
+
}
|
|
51
|
+
return plural
|
|
52
|
+
? `"${filter.operator}" expects ${fieldType} values for field "${filter.field}".`
|
|
53
|
+
: `"${filter.operator}" expects a ${fieldType} value for field "${filter.field}".`;
|
|
54
|
+
}
|
|
24
55
|
/**
|
|
25
56
|
* Validates a filter value against the expected field type.
|
|
26
57
|
*
|
|
@@ -34,7 +65,7 @@ export function validateFilterValue(filter, fieldType) {
|
|
|
34
65
|
case 'neq':
|
|
35
66
|
return matchesFieldType(fieldType, filter.value)
|
|
36
67
|
? null
|
|
37
|
-
:
|
|
68
|
+
: typeError(filter, fieldType, false);
|
|
38
69
|
case 'gt':
|
|
39
70
|
case 'gte':
|
|
40
71
|
case 'lt':
|
|
@@ -44,7 +75,7 @@ export function validateFilterValue(filter, fieldType) {
|
|
|
44
75
|
}
|
|
45
76
|
return matchesFieldType(fieldType, filter.value)
|
|
46
77
|
? null
|
|
47
|
-
:
|
|
78
|
+
: typeError(filter, fieldType, false);
|
|
48
79
|
case 'like':
|
|
49
80
|
if (fieldType !== 'string' && fieldType !== 'timestamp') {
|
|
50
81
|
return `"like" is only supported for string or timestamp field "${filter.field}".`;
|
|
@@ -57,16 +88,19 @@ export function validateFilterValue(filter, fieldType) {
|
|
|
57
88
|
if (!Array.isArray(filter.value) || filter.value.length === 0) {
|
|
58
89
|
return `"${filter.operator}" expects a non-empty array for field "${filter.field}".`;
|
|
59
90
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
91
|
+
if (!filter.value.every(value => matchesFieldType(fieldType, value))) {
|
|
92
|
+
return typeError(filter, fieldType, true);
|
|
93
|
+
}
|
|
94
|
+
return filter.operator === 'in' && fieldType === 'timestamp' && mixesInstantsAndLocalTimes(filter.value)
|
|
95
|
+
? mixedInstantListError(filter.field)
|
|
96
|
+
: null;
|
|
63
97
|
case 'between':
|
|
64
98
|
if (!Array.isArray(filter.value) || filter.value.length !== 2) {
|
|
65
99
|
return `"between" expects a two-item array for field "${filter.field}".`;
|
|
66
100
|
}
|
|
67
101
|
return filter.value.every(value => matchesFieldType(fieldType, value))
|
|
68
102
|
? null
|
|
69
|
-
:
|
|
103
|
+
: typeError(filter, fieldType, true);
|
|
70
104
|
default:
|
|
71
105
|
return `Unsupported semantic filter operator "${String(filter.operator)}".`;
|
|
72
106
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypequery/datasets",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "Code-first TypeScript semantic layer for ClickHouse datasets, metrics, multi-tenancy, and AI agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"semantic-layer",
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
"import": "./dist/index.js",
|
|
25
25
|
"require": "./dist/index.js"
|
|
26
26
|
},
|
|
27
|
+
"./schemas": {
|
|
28
|
+
"types": "./dist/schemas.d.ts",
|
|
29
|
+
"import": "./dist/schemas.js",
|
|
30
|
+
"require": "./dist/schemas.js"
|
|
31
|
+
},
|
|
27
32
|
"./internal": {
|
|
28
33
|
"types": "./dist/internal.d.ts",
|
|
29
34
|
"import": "./dist/internal.js",
|
|
@@ -34,27 +39,31 @@
|
|
|
34
39
|
"*": {
|
|
35
40
|
"internal": [
|
|
36
41
|
"./dist/internal.d.ts"
|
|
42
|
+
],
|
|
43
|
+
"schemas": [
|
|
44
|
+
"./dist/schemas.d.ts"
|
|
37
45
|
]
|
|
38
46
|
}
|
|
39
47
|
},
|
|
40
48
|
"license": "Apache-2.0",
|
|
41
49
|
"files": [
|
|
42
|
-
"dist"
|
|
50
|
+
"dist",
|
|
51
|
+
"!dist/**/*.test.*",
|
|
52
|
+
"!dist/**/*.type-test.*",
|
|
53
|
+
"!dist/**/__tests__/**",
|
|
54
|
+
"!dist/**/tests/**",
|
|
55
|
+
"!dist/**/type-tests/**"
|
|
43
56
|
],
|
|
44
57
|
"dependencies": {
|
|
45
|
-
"@hypequery/protocol": "^0.
|
|
46
|
-
"@noble/hashes": "^
|
|
47
|
-
"zod": "^
|
|
58
|
+
"@hypequery/protocol": "^0.21.0",
|
|
59
|
+
"@noble/hashes": "^2.4.0",
|
|
60
|
+
"zod": "^4.6.6",
|
|
48
61
|
"zod-to-json-schema": "^3.25.2"
|
|
49
62
|
},
|
|
50
63
|
"peerDependencies": {
|
|
51
|
-
"zod": "^3.22.4",
|
|
52
64
|
"@hypequery/clickhouse": ">=2.13.0"
|
|
53
65
|
},
|
|
54
66
|
"peerDependenciesMeta": {
|
|
55
|
-
"zod": {
|
|
56
|
-
"optional": true
|
|
57
|
-
},
|
|
58
67
|
"@hypequery/clickhouse": {
|
|
59
68
|
"optional": true
|
|
60
69
|
}
|
|
@@ -62,9 +71,9 @@
|
|
|
62
71
|
"devDependencies": {
|
|
63
72
|
"@types/node": "^18.19.80",
|
|
64
73
|
"typescript": "^6.0.3",
|
|
65
|
-
"@vitest/coverage-v8": "^5.0.
|
|
66
|
-
"vitest": "^5.0.
|
|
67
|
-
"@hypequery/protocol-conformance": "^0.
|
|
74
|
+
"@vitest/coverage-v8": "^5.0.5",
|
|
75
|
+
"vitest": "^5.0.5",
|
|
76
|
+
"@hypequery/protocol-conformance": "^0.16.0"
|
|
68
77
|
},
|
|
69
78
|
"repository": {
|
|
70
79
|
"type": "git",
|
package/dist/api.type-test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api.type-test.d.ts","sourceRoot":"","sources":["../src/api.type-test.ts"],"names":[],"mappings":""}
|