@hypequery/protocol 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/README.md +50 -9
  2. package/dist/deployments/errors.d.ts +8 -0
  3. package/dist/deployments/errors.d.ts.map +1 -0
  4. package/dist/deployments/errors.js +13 -0
  5. package/dist/deployments/index.d.ts +5 -0
  6. package/dist/deployments/index.d.ts.map +1 -0
  7. package/dist/deployments/index.js +3 -0
  8. package/dist/deployments/limits.d.ts +4 -0
  9. package/dist/deployments/limits.d.ts.map +1 -0
  10. package/dist/deployments/limits.js +23 -0
  11. package/dist/deployments/types.d.ts +148 -0
  12. package/dist/deployments/types.d.ts.map +1 -0
  13. package/dist/deployments/types.js +1 -0
  14. package/dist/deployments/validate.d.ts +4 -0
  15. package/dist/deployments/validate.d.ts.map +1 -0
  16. package/dist/deployments/validate.js +537 -0
  17. package/dist/expressions/errors.d.ts +8 -0
  18. package/dist/expressions/errors.d.ts.map +1 -0
  19. package/dist/expressions/errors.js +13 -0
  20. package/dist/expressions/index.d.ts +5 -0
  21. package/dist/expressions/index.d.ts.map +1 -0
  22. package/dist/expressions/index.js +3 -0
  23. package/dist/expressions/limits.d.ts +4 -0
  24. package/dist/expressions/limits.d.ts.map +1 -0
  25. package/dist/expressions/limits.js +17 -0
  26. package/dist/expressions/types.d.ts +77 -0
  27. package/dist/expressions/types.d.ts.map +1 -0
  28. package/dist/expressions/types.js +1 -0
  29. package/dist/expressions/validate.d.ts +4 -0
  30. package/dist/expressions/validate.d.ts.map +1 -0
  31. package/dist/expressions/validate.js +338 -0
  32. package/dist/identifiers/identifiers.d.ts +14 -0
  33. package/dist/identifiers/identifiers.d.ts.map +1 -0
  34. package/dist/identifiers/identifiers.js +87 -0
  35. package/dist/identifiers/index.d.ts +4 -0
  36. package/dist/identifiers/index.d.ts.map +1 -0
  37. package/dist/identifiers/index.js +2 -0
  38. package/dist/identifiers/types.d.ts +16 -0
  39. package/dist/identifiers/types.d.ts.map +1 -0
  40. package/dist/identifiers/types.js +5 -0
  41. package/dist/index.d.ts +12 -8
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +6 -1
  44. package/dist/query-implementations/errors.d.ts +8 -0
  45. package/dist/query-implementations/errors.d.ts.map +1 -0
  46. package/dist/query-implementations/errors.js +13 -0
  47. package/dist/query-implementations/index.d.ts +5 -0
  48. package/dist/query-implementations/index.d.ts.map +1 -0
  49. package/dist/query-implementations/index.js +3 -0
  50. package/dist/query-implementations/limits.d.ts +4 -0
  51. package/dist/query-implementations/limits.d.ts.map +1 -0
  52. package/dist/query-implementations/limits.js +22 -0
  53. package/dist/query-implementations/types.d.ts +57 -0
  54. package/dist/query-implementations/types.d.ts.map +1 -0
  55. package/dist/query-implementations/types.js +1 -0
  56. package/dist/query-implementations/validate.d.ts +4 -0
  57. package/dist/query-implementations/validate.d.ts.map +1 -0
  58. package/dist/query-implementations/validate.js +248 -0
  59. package/dist/schemas/errors.d.ts +8 -0
  60. package/dist/schemas/errors.d.ts.map +1 -0
  61. package/dist/schemas/errors.js +13 -0
  62. package/dist/schemas/index.d.ts +5 -0
  63. package/dist/schemas/index.d.ts.map +1 -0
  64. package/dist/schemas/index.js +3 -0
  65. package/dist/schemas/limits.d.ts +4 -0
  66. package/dist/schemas/limits.d.ts.map +1 -0
  67. package/dist/schemas/limits.js +20 -0
  68. package/dist/schemas/types.d.ts +60 -0
  69. package/dist/schemas/types.d.ts.map +1 -0
  70. package/dist/schemas/types.js +1 -0
  71. package/dist/schemas/validate.d.ts +3 -0
  72. package/dist/schemas/validate.d.ts.map +1 -0
  73. package/dist/schemas/validate.js +391 -0
  74. package/dist/values/codec.d.ts +13 -0
  75. package/dist/values/codec.d.ts.map +1 -0
  76. package/dist/values/codec.js +39 -0
  77. package/dist/values/errors.d.ts +8 -0
  78. package/dist/values/errors.d.ts.map +1 -0
  79. package/dist/values/errors.js +13 -0
  80. package/dist/values/index.d.ts +7 -0
  81. package/dist/values/index.d.ts.map +1 -0
  82. package/dist/values/index.js +4 -0
  83. package/dist/values/jcs.d.ts +2 -0
  84. package/dist/values/jcs.d.ts.map +1 -0
  85. package/dist/values/jcs.js +23 -0
  86. package/dist/values/limits.d.ts +4 -0
  87. package/dist/values/limits.d.ts.map +1 -0
  88. package/dist/values/limits.js +26 -0
  89. package/dist/values/parser.d.ts +3 -0
  90. package/dist/values/parser.d.ts.map +1 -0
  91. package/dist/values/parser.js +254 -0
  92. package/dist/values/snapshot.d.ts +3 -0
  93. package/dist/values/snapshot.d.ts.map +1 -0
  94. package/dist/values/snapshot.js +96 -0
  95. package/dist/values/types.d.ts +78 -0
  96. package/dist/values/types.d.ts.map +1 -0
  97. package/dist/values/types.js +1 -0
  98. package/dist/values/validate.d.ts +7 -0
  99. package/dist/values/validate.d.ts.map +1 -0
  100. package/dist/values/validate.js +332 -0
  101. package/package.json +4 -1
@@ -0,0 +1,391 @@
1
+ import { ProtocolIdentifierError, parseProtocolIdentifier } from '../identifiers/index.js';
2
+ import { ProtocolValueError, encodeCanonicalValueToString, validateCanonicalValue, } from '../values/index.js';
3
+ import { schemaError } from './errors.js';
4
+ import { resolveSchemaLimits } from './limits.js';
5
+ const textEncoder = new TextEncoder();
6
+ const ANNOTATIONS = ['description', 'default'];
7
+ function requireRecord(input, path) {
8
+ if (typeof input !== 'object' || input === null || Array.isArray(input)) {
9
+ schemaError('HQ_SCHEMA_TYPE', path);
10
+ }
11
+ const prototype = Object.getPrototypeOf(input);
12
+ if (prototype !== Object.prototype && prototype !== null)
13
+ schemaError('HQ_SCHEMA_UNSAFE_OBJECT', path);
14
+ if (Object.getOwnPropertySymbols(input).length > 0)
15
+ schemaError('HQ_SCHEMA_UNSAFE_OBJECT', path);
16
+ for (const descriptor of Object.values(Object.getOwnPropertyDescriptors(input))) {
17
+ if (!descriptor.enumerable || !('value' in descriptor))
18
+ schemaError('HQ_SCHEMA_UNSAFE_OBJECT', path);
19
+ }
20
+ return input;
21
+ }
22
+ function requireArray(input, path, state) {
23
+ if (!Array.isArray(input))
24
+ schemaError('HQ_SCHEMA_TYPE', path);
25
+ if (Object.getPrototypeOf(input) !== Array.prototype || Object.getOwnPropertySymbols(input).length > 0) {
26
+ schemaError('HQ_SCHEMA_UNSAFE_OBJECT', path);
27
+ }
28
+ if (input.length > state.limits.maxCollectionItems)
29
+ schemaError('HQ_SCHEMA_TOO_MANY_ITEMS', path);
30
+ const descriptors = Object.getOwnPropertyDescriptors(input);
31
+ for (let index = 0; index < input.length; index += 1) {
32
+ const descriptor = descriptors[String(index)];
33
+ if (!descriptor || !descriptor.enumerable || !('value' in descriptor)) {
34
+ schemaError('HQ_SCHEMA_UNSAFE_OBJECT', `${path}[${index}]`);
35
+ }
36
+ }
37
+ if (Object.keys(input).length !== input.length)
38
+ schemaError('HQ_SCHEMA_UNSAFE_OBJECT', path);
39
+ return input;
40
+ }
41
+ function exactFields(value, required, optional, path) {
42
+ const allowed = new Set([...required, ...optional]);
43
+ for (const key of Object.keys(value)) {
44
+ if (!allowed.has(key))
45
+ schemaError('HQ_SCHEMA_UNKNOWN_FIELD', `${path}.${key}`);
46
+ }
47
+ for (const key of required) {
48
+ if (!Object.hasOwn(value, key))
49
+ schemaError('HQ_SCHEMA_TYPE', `${path}.${key}`);
50
+ }
51
+ }
52
+ function enter(value, depth, state, path) {
53
+ if (depth > state.limits.maxDepth)
54
+ schemaError('HQ_SCHEMA_TOO_DEEP', path);
55
+ state.nodes += 1;
56
+ if (state.nodes > state.limits.maxNodes)
57
+ schemaError('HQ_SCHEMA_TOO_MANY_NODES', path);
58
+ if (state.active.has(value))
59
+ schemaError('HQ_SCHEMA_UNSAFE_OBJECT', path);
60
+ state.active.add(value);
61
+ }
62
+ function freezeRecord(entries) {
63
+ return Object.freeze(Object.assign(Object.create(null), entries));
64
+ }
65
+ function validateAnnotations(value, result, state, path, allowDefault = true) {
66
+ if (value.description !== undefined) {
67
+ if (typeof value.description !== 'string')
68
+ schemaError('HQ_SCHEMA_TYPE', `${path}.description`);
69
+ // UTF-16 code units are a conservative allocation-free lower bound on UTF-8 bytes.
70
+ if (value.description.length > state.limits.maxDescriptionBytes) {
71
+ schemaError('HQ_SCHEMA_TOO_LARGE', `${path}.description`);
72
+ }
73
+ if (textEncoder.encode(value.description).byteLength > state.limits.maxDescriptionBytes) {
74
+ schemaError('HQ_SCHEMA_TOO_LARGE', `${path}.description`);
75
+ }
76
+ const description = canonicalValue(value.description, `${path}.description`);
77
+ result.description = description;
78
+ }
79
+ if (value.default !== undefined) {
80
+ if (!allowDefault)
81
+ schemaError('HQ_SCHEMA_INVALID_VALUE', `${path}.default`);
82
+ try {
83
+ result.default = validateCanonicalValue(value.default);
84
+ }
85
+ catch (error) {
86
+ if (error instanceof ProtocolValueError)
87
+ schemaError('HQ_SCHEMA_INVALID_VALUE', `${path}.default`);
88
+ throw error;
89
+ }
90
+ }
91
+ }
92
+ function nonNegativeInteger(value, path) {
93
+ if (!Number.isSafeInteger(value) || value < 0) {
94
+ schemaError('HQ_SCHEMA_INVALID_CONSTRAINT', path);
95
+ }
96
+ return value;
97
+ }
98
+ function finiteNumber(value, path) {
99
+ if (typeof value !== 'number' || !Number.isFinite(value) || Object.is(value, -0)) {
100
+ schemaError('HQ_SCHEMA_INVALID_CONSTRAINT', path);
101
+ }
102
+ return value;
103
+ }
104
+ function copyRange(value, result, minimumKey, maximumKey, path, integer) {
105
+ if (value[minimumKey] !== undefined) {
106
+ const number = finiteNumber(value[minimumKey], `${path}.${minimumKey}`);
107
+ if (integer && !Number.isSafeInteger(number))
108
+ schemaError('HQ_SCHEMA_INVALID_CONSTRAINT', `${path}.${minimumKey}`);
109
+ result[minimumKey] = number;
110
+ }
111
+ if (value[maximumKey] !== undefined) {
112
+ const number = finiteNumber(value[maximumKey], `${path}.${maximumKey}`);
113
+ if (integer && !Number.isSafeInteger(number))
114
+ schemaError('HQ_SCHEMA_INVALID_CONSTRAINT', `${path}.${maximumKey}`);
115
+ result[maximumKey] = number;
116
+ }
117
+ }
118
+ function copyNonNegativeRange(value, result, minimumKey, maximumKey, path) {
119
+ if (value[minimumKey] !== undefined) {
120
+ result[minimumKey] = nonNegativeInteger(value[minimumKey], `${path}.${minimumKey}`);
121
+ }
122
+ if (value[maximumKey] !== undefined) {
123
+ result[maximumKey] = nonNegativeInteger(value[maximumKey], `${path}.${maximumKey}`);
124
+ }
125
+ }
126
+ function assertOrderedBounds(result, path) {
127
+ if (result.minimum !== undefined && result.exclusiveMinimum !== undefined) {
128
+ schemaError('HQ_SCHEMA_INVALID_CONSTRAINT', path);
129
+ }
130
+ if (result.maximum !== undefined && result.exclusiveMaximum !== undefined) {
131
+ schemaError('HQ_SCHEMA_INVALID_CONSTRAINT', path);
132
+ }
133
+ const lower = result.exclusiveMinimum ?? result.minimum;
134
+ const upper = result.exclusiveMaximum ?? result.maximum;
135
+ const emptyAtEqual = lower === upper
136
+ && (result.exclusiveMinimum !== undefined || result.exclusiveMaximum !== undefined);
137
+ if (lower !== undefined && upper !== undefined
138
+ && (lower > upper || emptyAtEqual)) {
139
+ schemaError('HQ_SCHEMA_INVALID_CONSTRAINT', path);
140
+ }
141
+ }
142
+ function validateSchema(input, path, depth, state) {
143
+ const value = requireRecord(input, path);
144
+ enter(value, depth, state, path);
145
+ try {
146
+ if (typeof value.kind !== 'string')
147
+ schemaError('HQ_SCHEMA_TYPE', `${path}.kind`);
148
+ const kind = value.kind;
149
+ const result = { kind };
150
+ switch (kind) {
151
+ case 'any':
152
+ case 'null':
153
+ case 'boolean':
154
+ exactFields(value, ['kind'], ANNOTATIONS, path);
155
+ validateAnnotations(value, result, state, path);
156
+ break;
157
+ case 'void':
158
+ exactFields(value, ['kind'], ['description'], path);
159
+ validateAnnotations(value, result, state, path, false);
160
+ break;
161
+ case 'string': {
162
+ exactFields(value, ['kind'], [...ANNOTATIONS, 'minLength', 'maxLength'], path);
163
+ validateAnnotations(value, result, state, path);
164
+ copyNonNegativeRange(value, result, 'minLength', 'maxLength', path);
165
+ if (result.minLength !== undefined && result.maxLength !== undefined
166
+ && result.minLength > result.maxLength) {
167
+ schemaError('HQ_SCHEMA_INVALID_CONSTRAINT', path);
168
+ }
169
+ break;
170
+ }
171
+ case 'number':
172
+ case 'integer':
173
+ exactFields(value, ['kind'], [
174
+ ...ANNOTATIONS, 'minimum', 'exclusiveMinimum', 'maximum', 'exclusiveMaximum',
175
+ ], path);
176
+ validateAnnotations(value, result, state, path);
177
+ copyRange(value, result, 'minimum', 'maximum', path, kind === 'integer');
178
+ copyRange(value, result, 'exclusiveMinimum', 'exclusiveMaximum', path, kind === 'integer');
179
+ assertOrderedBounds(result, path);
180
+ break;
181
+ case 'literal':
182
+ exactFields(value, ['kind', 'value'], ANNOTATIONS, path);
183
+ validateAnnotations(value, result, state, path);
184
+ result.value = canonicalValue(value.value, `${path}.value`);
185
+ break;
186
+ case 'enum':
187
+ exactFields(value, ['kind', 'values'], ANNOTATIONS, path);
188
+ validateAnnotations(value, result, state, path);
189
+ result.values = validateEnum(value.values, `${path}.values`, state);
190
+ break;
191
+ case 'array': {
192
+ exactFields(value, ['kind', 'items'], [...ANNOTATIONS, 'minItems', 'maxItems'], path);
193
+ validateAnnotations(value, result, state, path);
194
+ result.items = validateSchema(value.items, `${path}.items`, depth + 1, state);
195
+ copyNonNegativeRange(value, result, 'minItems', 'maxItems', path);
196
+ if (result.minItems !== undefined && result.maxItems !== undefined
197
+ && result.minItems > result.maxItems) {
198
+ schemaError('HQ_SCHEMA_INVALID_CONSTRAINT', path);
199
+ }
200
+ break;
201
+ }
202
+ case 'object':
203
+ validateObject(value, result, path, depth, state);
204
+ break;
205
+ case 'record':
206
+ exactFields(value, ['kind', 'values'], ANNOTATIONS, path);
207
+ validateAnnotations(value, result, state, path);
208
+ result.values = validateSchema(value.values, `${path}.values`, depth + 1, state);
209
+ break;
210
+ case 'union': {
211
+ exactFields(value, ['kind', 'variants'], ANNOTATIONS, path);
212
+ validateAnnotations(value, result, state, path);
213
+ const variants = requireArray(value.variants, `${path}.variants`, state);
214
+ if (variants.length < 2)
215
+ schemaError('HQ_SCHEMA_INVALID_CONSTRAINT', `${path}.variants`);
216
+ result.variants = Object.freeze(variants.map((variant, index) => validateSchema(variant, `${path}.variants[${index}]`, depth + 1, state)));
217
+ break;
218
+ }
219
+ default:
220
+ schemaError('HQ_SCHEMA_UNKNOWN_KIND', `${path}.kind`);
221
+ }
222
+ validateDefault(result, path);
223
+ return freezeRecord(result);
224
+ }
225
+ finally {
226
+ state.active.delete(value);
227
+ }
228
+ }
229
+ function canonicalValue(input, path) {
230
+ try {
231
+ return validateCanonicalValue(input);
232
+ }
233
+ catch (error) {
234
+ if (error instanceof ProtocolValueError)
235
+ schemaError('HQ_SCHEMA_INVALID_VALUE', path);
236
+ throw error;
237
+ }
238
+ }
239
+ function validateEnum(input, path, state) {
240
+ const values = requireArray(input, path, state);
241
+ if (values.length === 0)
242
+ schemaError('HQ_SCHEMA_INVALID_CONSTRAINT', path);
243
+ const validated = values.map((value, index) => canonicalValue(value, `${path}[${index}]`));
244
+ const encoded = validated.map(value => encodeCanonicalValueToString(value));
245
+ if (new Set(encoded).size !== encoded.length)
246
+ schemaError('HQ_SCHEMA_DUPLICATE_VALUE', path);
247
+ return Object.freeze(validated);
248
+ }
249
+ function validateObject(value, result, path, depth, state) {
250
+ exactFields(value, ['kind', 'properties', 'required', 'unknownProperties'], ANNOTATIONS, path);
251
+ validateAnnotations(value, result, state, path);
252
+ const properties = requireRecord(value.properties, `${path}.properties`);
253
+ if (state.active.has(properties))
254
+ schemaError('HQ_SCHEMA_UNSAFE_OBJECT', `${path}.properties`);
255
+ state.active.add(properties);
256
+ const propertyEntries = Object.entries(properties);
257
+ if (propertyEntries.length > state.limits.maxCollectionItems) {
258
+ schemaError('HQ_SCHEMA_TOO_MANY_ITEMS', `${path}.properties`);
259
+ }
260
+ const snapshot = Object.create(null);
261
+ try {
262
+ for (const [name, schema] of propertyEntries) {
263
+ try {
264
+ parseProtocolIdentifier(name);
265
+ }
266
+ catch (error) {
267
+ if (error instanceof ProtocolIdentifierError)
268
+ schemaError('HQ_SCHEMA_INVALID_IDENTIFIER', `${path}.properties`);
269
+ throw error;
270
+ }
271
+ snapshot[name] = validateSchema(schema, `${path}.properties.${name}`, depth + 1, state);
272
+ }
273
+ }
274
+ finally {
275
+ state.active.delete(properties);
276
+ }
277
+ result.properties = Object.freeze(snapshot);
278
+ const required = requireArray(value.required, `${path}.required`, state);
279
+ const requiredNames = required.map((name, index) => {
280
+ if (typeof name !== 'string' || !Object.hasOwn(snapshot, name)) {
281
+ schemaError('HQ_SCHEMA_INVALID_REQUIRED', `${path}.required[${index}]`);
282
+ }
283
+ return name;
284
+ });
285
+ if (new Set(requiredNames).size !== requiredNames.length) {
286
+ schemaError('HQ_SCHEMA_INVALID_REQUIRED', `${path}.required`);
287
+ }
288
+ result.required = Object.freeze(requiredNames);
289
+ if (value.unknownProperties !== 'reject' && value.unknownProperties !== 'strip'
290
+ && value.unknownProperties !== 'preserve') {
291
+ schemaError('HQ_SCHEMA_INVALID_CONSTRAINT', `${path}.unknownProperties`);
292
+ }
293
+ result.unknownProperties = value.unknownProperties;
294
+ }
295
+ function validateDefault(schema, path) {
296
+ if (schema.default === undefined)
297
+ return;
298
+ if (!matchesSchema(schema, schema.default)) {
299
+ schemaError('HQ_SCHEMA_INVALID_VALUE', `${path}.default`);
300
+ }
301
+ }
302
+ function matchesSchema(schema, value) {
303
+ switch (schema.kind) {
304
+ case 'any':
305
+ return true;
306
+ case 'void':
307
+ return false;
308
+ case 'null':
309
+ return value === null;
310
+ case 'boolean':
311
+ return typeof value === 'boolean';
312
+ case 'string': {
313
+ if (typeof value !== 'string')
314
+ return false;
315
+ const length = [...value].length;
316
+ return (schema.minLength === undefined || length >= schema.minLength)
317
+ && (schema.maxLength === undefined || length <= schema.maxLength);
318
+ }
319
+ case 'number':
320
+ case 'integer':
321
+ return matchesNumber(schema, value);
322
+ case 'literal':
323
+ return encodeCanonicalValueToString(value) === encodeCanonicalValueToString(schema.value);
324
+ case 'enum': {
325
+ const encoded = encodeCanonicalValueToString(value);
326
+ return schema.values.some(item => encodeCanonicalValueToString(item) === encoded);
327
+ }
328
+ case 'array': {
329
+ const values = taggedValues(value, 'array');
330
+ return values !== undefined
331
+ && (schema.minItems === undefined || values.length >= schema.minItems)
332
+ && (schema.maxItems === undefined || values.length <= schema.maxItems)
333
+ && values.every(item => matchesSchema(schema.items, item));
334
+ }
335
+ case 'object': {
336
+ const entries = taggedEntries(value);
337
+ if (!entries)
338
+ return false;
339
+ const found = new Set();
340
+ for (const [key, item] of entries) {
341
+ if (typeof key !== 'string')
342
+ return false;
343
+ const property = schema.properties[key];
344
+ if (property) {
345
+ if (!matchesSchema(property, item))
346
+ return false;
347
+ found.add(key);
348
+ }
349
+ else if (schema.unknownProperties === 'reject' || schema.unknownProperties === 'strip') {
350
+ return false;
351
+ }
352
+ }
353
+ return schema.required.every(name => found.has(name));
354
+ }
355
+ case 'record': {
356
+ const entries = taggedEntries(value);
357
+ return entries !== undefined
358
+ && entries.every(([key, item]) => typeof key === 'string' && matchesSchema(schema.values, item));
359
+ }
360
+ case 'union':
361
+ return schema.variants.some(variant => matchesSchema(variant, value));
362
+ }
363
+ }
364
+ function matchesNumber(schema, value) {
365
+ if (typeof value !== 'number' || (schema.kind === 'integer' && !Number.isSafeInteger(value))) {
366
+ return false;
367
+ }
368
+ return (schema.minimum === undefined || value >= schema.minimum)
369
+ && (schema.exclusiveMinimum === undefined || value > schema.exclusiveMinimum)
370
+ && (schema.maximum === undefined || value <= schema.maximum)
371
+ && (schema.exclusiveMaximum === undefined || value < schema.exclusiveMaximum);
372
+ }
373
+ function taggedValues(value, type) {
374
+ if (typeof value !== 'object' || value === null || !('$hypequery' in value))
375
+ return undefined;
376
+ const tag = value.$hypequery;
377
+ return tag.type === type ? tag.values : undefined;
378
+ }
379
+ function taggedEntries(value) {
380
+ if (typeof value !== 'object' || value === null || !('$hypequery' in value))
381
+ return undefined;
382
+ const tag = value.$hypequery;
383
+ return tag.type === 'map' ? tag.entries : undefined;
384
+ }
385
+ export function validateProtocolSchema(input, options = {}) {
386
+ return validateSchema(input, '$', 1, {
387
+ limits: resolveSchemaLimits(options),
388
+ active: new WeakSet(),
389
+ nodes: 0,
390
+ });
391
+ }
@@ -0,0 +1,13 @@
1
+ import type { CanonicalValue, CanonicalValueOptions } from './types.js';
2
+ /** Validates plain data and returns exact RFC 8785 canonical UTF-8 bytes. */
3
+ export declare function encodeCanonicalValue(input: unknown, options?: CanonicalValueOptions): Uint8Array;
4
+ /** String form of the exact bytes returned by `encodeCanonicalValue`. */
5
+ export declare function encodeCanonicalValueToString(input: unknown, options?: CanonicalValueOptions): string;
6
+ /**
7
+ * Parses duplicate-aware UTF-8/JSON and returns a validated, deeply frozen
8
+ * canonical value. Input need not already use canonical object-key ordering.
9
+ */
10
+ export declare function decodeCanonicalValue(input: string | Uint8Array, options?: CanonicalValueOptions): CanonicalValue;
11
+ /** Raw conformance hash only; deployment and cache domains are specified later. */
12
+ export declare function hashCanonicalValue(input: unknown, options?: CanonicalValueOptions): string;
13
+ //# sourceMappingURL=codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../src/values/codec.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AA0BxE,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,qBAA0B,GAClC,UAAU,CAEZ;AAED,yEAAyE;AACzE,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,qBAA0B,GAClC,MAAM,CAER;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,GAAG,UAAU,EAC1B,OAAO,GAAE,qBAA0B,GAClC,cAAc,CAQhB;AAED,mFAAmF;AACnF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,qBAA0B,GAClC,MAAM,CAER"}
@@ -0,0 +1,39 @@
1
+ import { sha256 } from '@noble/hashes/sha2';
2
+ import { bytesToHex } from '@noble/hashes/utils';
3
+ import { valueError } from './errors.js';
4
+ import { serializeJcs } from './jcs.js';
5
+ import { resolveLimits } from './limits.js';
6
+ import { parseDuplicateAwareJson } from './parser.js';
7
+ import { validateCanonicalValueWithLimits, validateParsedCanonicalValue, } from './validate.js';
8
+ const textEncoder = new TextEncoder();
9
+ function prepare(input, options) {
10
+ const limits = resolveLimits(options);
11
+ const value = validateCanonicalValueWithLimits(input, limits, options.declaredClickHouseType);
12
+ const canonical = serializeJcs(value);
13
+ const bytes = textEncoder.encode(canonical);
14
+ if (bytes.byteLength > limits.maxCanonicalBytes) {
15
+ valueError('HQ_VALUE_TOO_LARGE');
16
+ }
17
+ return { value, canonical, bytes };
18
+ }
19
+ /** Validates plain data and returns exact RFC 8785 canonical UTF-8 bytes. */
20
+ export function encodeCanonicalValue(input, options = {}) {
21
+ return prepare(input, options).bytes;
22
+ }
23
+ /** String form of the exact bytes returned by `encodeCanonicalValue`. */
24
+ export function encodeCanonicalValueToString(input, options = {}) {
25
+ return prepare(input, options).canonical;
26
+ }
27
+ /**
28
+ * Parses duplicate-aware UTF-8/JSON and returns a validated, deeply frozen
29
+ * canonical value. Input need not already use canonical object-key ordering.
30
+ */
31
+ export function decodeCanonicalValue(input, options = {}) {
32
+ const limits = resolveLimits(options);
33
+ const parsed = parseDuplicateAwareJson(input, limits);
34
+ return validateParsedCanonicalValue(parsed, limits, options.declaredClickHouseType);
35
+ }
36
+ /** Raw conformance hash only; deployment and cache domains are specified later. */
37
+ export function hashCanonicalValue(input, options = {}) {
38
+ return bytesToHex(sha256(prepare(input, options).bytes));
39
+ }
@@ -0,0 +1,8 @@
1
+ export type ProtocolValueErrorCode = 'HQ_VALUE_INVALID_JSON' | 'HQ_VALUE_DUPLICATE_KEY' | 'HQ_VALUE_INVALID_UNICODE' | 'HQ_VALUE_CONTROL_CHARACTER' | 'HQ_VALUE_NON_FINITE_FLOAT' | 'HQ_VALUE_NEGATIVE_ZERO' | 'HQ_VALUE_INTEGER_TAG_REQUIRED' | 'HQ_VALUE_RAW_COMPOSITE' | 'HQ_VALUE_UNKNOWN_TAG' | 'HQ_VALUE_UNKNOWN_TAG_VERSION' | 'HQ_VALUE_UNKNOWN_FIELD' | 'HQ_VALUE_INVALID_FORMAT' | 'HQ_VALUE_OUT_OF_RANGE' | 'HQ_VALUE_TYPE_MISMATCH' | 'HQ_VALUE_TOO_DEEP' | 'HQ_VALUE_TOO_MANY_NODES' | 'HQ_VALUE_TOO_MANY_ITEMS' | 'HQ_VALUE_TOO_LARGE' | 'HQ_VALUE_UNSAFE_OBJECT';
2
+ export declare class ProtocolValueError extends TypeError {
3
+ readonly code: ProtocolValueErrorCode;
4
+ readonly path: string;
5
+ constructor(code: ProtocolValueErrorCode, path?: string);
6
+ }
7
+ export declare function valueError(code: ProtocolValueErrorCode, path?: string): never;
8
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/values/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,sBAAsB,GAC9B,uBAAuB,GACvB,wBAAwB,GACxB,0BAA0B,GAC1B,4BAA4B,GAC5B,2BAA2B,GAC3B,wBAAwB,GACxB,+BAA+B,GAC/B,wBAAwB,GACxB,sBAAsB,GACtB,8BAA8B,GAC9B,wBAAwB,GACxB,yBAAyB,GACzB,uBAAuB,GACvB,wBAAwB,GACxB,mBAAmB,GACnB,yBAAyB,GACzB,yBAAyB,GACzB,oBAAoB,GACpB,wBAAwB,CAAC;AAE7B,qBAAa,kBAAmB,SAAQ,SAAS;IAC/C,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,sBAAsB,EAAE,IAAI,SAAM;CAMrD;AAED,wBAAgB,UAAU,CACxB,IAAI,EAAE,sBAAsB,EAC5B,IAAI,SAAM,GACT,KAAK,CAEP"}
@@ -0,0 +1,13 @@
1
+ export class ProtocolValueError extends TypeError {
2
+ code;
3
+ path;
4
+ constructor(code, path = '$') {
5
+ super(`${code} at ${path}`);
6
+ this.name = 'ProtocolValueError';
7
+ this.code = code;
8
+ this.path = path;
9
+ }
10
+ }
11
+ export function valueError(code, path = '$') {
12
+ throw new ProtocolValueError(code, path);
13
+ }
@@ -0,0 +1,7 @@
1
+ export { decodeCanonicalValue, encodeCanonicalValue, encodeCanonicalValueToString, hashCanonicalValue, } from './codec.js';
2
+ export { ProtocolValueError } from './errors.js';
3
+ export type { ProtocolValueErrorCode } from './errors.js';
4
+ export { DEFAULT_CANONICAL_VALUE_LIMITS } from './limits.js';
5
+ export { validateCanonicalValue } from './validate.js';
6
+ export type { ArrayTaggedValue, BytesTaggedValue, CanonicalValue, CanonicalValueLimits, CanonicalValueOptions, DateTaggedValue, DatetimeTaggedValue, DecimalTaggedValue, EnumTaggedValue, IntegerTaggedValue, MapTaggedValue, TaggedValue, TupleTaggedValue, UuidTaggedValue, } from './types.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/values/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,4BAA4B,EAC5B,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,eAAe,GAChB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { decodeCanonicalValue, encodeCanonicalValue, encodeCanonicalValueToString, hashCanonicalValue, } from './codec.js';
2
+ export { ProtocolValueError } from './errors.js';
3
+ export { DEFAULT_CANONICAL_VALUE_LIMITS } from './limits.js';
4
+ export { validateCanonicalValue } from './validate.js';
@@ -0,0 +1,2 @@
1
+ export declare function serializeJcs(value: unknown): string;
2
+ //# sourceMappingURL=jcs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jcs.d.ts","sourceRoot":"","sources":["../../src/values/jcs.ts"],"names":[],"mappings":"AAEA,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAsBnD"}
@@ -0,0 +1,23 @@
1
+ import { valueError } from './errors.js';
2
+ export function serializeJcs(value) {
3
+ if (value === null
4
+ || typeof value === 'boolean'
5
+ || typeof value === 'string'
6
+ || typeof value === 'number') {
7
+ const encoded = JSON.stringify(value);
8
+ if (encoded === undefined)
9
+ valueError('HQ_VALUE_INVALID_FORMAT');
10
+ return encoded;
11
+ }
12
+ if (Array.isArray(value)) {
13
+ return `[${value.map(serializeJcs).join(',')}]`;
14
+ }
15
+ if (typeof value === 'object') {
16
+ const object = value;
17
+ return `{${Object.keys(object)
18
+ .sort()
19
+ .map((key) => `${JSON.stringify(key)}:${serializeJcs(object[key])}`)
20
+ .join(',')}}`;
21
+ }
22
+ valueError('HQ_VALUE_INVALID_FORMAT');
23
+ }
@@ -0,0 +1,4 @@
1
+ import type { CanonicalValueLimits, CanonicalValueOptions } from './types.js';
2
+ export declare const DEFAULT_CANONICAL_VALUE_LIMITS: Readonly<CanonicalValueLimits>;
3
+ export declare function resolveLimits(options?: CanonicalValueOptions): Readonly<CanonicalValueLimits>;
4
+ //# sourceMappingURL=limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/values/limits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAM9E,eAAO,MAAM,8BAA8B,EAAE,QAAQ,CAAC,oBAAoB,CAQxE,CAAC;AAEH,wBAAgB,aAAa,CAC3B,OAAO,GAAE,qBAA0B,GAClC,QAAQ,CAAC,oBAAoB,CAAC,CAuBhC"}
@@ -0,0 +1,26 @@
1
+ export const DEFAULT_CANONICAL_VALUE_LIMITS = Object.freeze({
2
+ maxInputBytes: 1_048_576,
3
+ maxCanonicalBytes: 1_048_576,
4
+ maxDepth: 16,
5
+ maxNodes: 10_000,
6
+ maxCollectionItems: 1_000,
7
+ maxStringBytes: 65_536,
8
+ maxDecodedBytes: 65_536,
9
+ });
10
+ export function resolveLimits(options = {}) {
11
+ const resolved = { ...DEFAULT_CANONICAL_VALUE_LIMITS };
12
+ if (!options.limits) {
13
+ return resolved;
14
+ }
15
+ for (const key of Object.keys(options.limits)) {
16
+ const value = options.limits[key];
17
+ if (value === undefined
18
+ || !Number.isSafeInteger(value)
19
+ || value < 1
20
+ || value > DEFAULT_CANONICAL_VALUE_LIMITS[key]) {
21
+ throw new RangeError(`${key} must be a positive integer no greater than the protocol v1 maximum`);
22
+ }
23
+ resolved[key] = value;
24
+ }
25
+ return Object.freeze(resolved);
26
+ }
@@ -0,0 +1,3 @@
1
+ import type { CanonicalValueLimits } from './types.js';
2
+ export declare function parseDuplicateAwareJson(input: string | Uint8Array, limits: Readonly<CanonicalValueLimits>): unknown;
3
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/values/parser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAoPvD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,GAAG,UAAU,EAC1B,MAAM,EAAE,QAAQ,CAAC,oBAAoB,CAAC,GACrC,OAAO,CA6BT"}