@json-schema-engine/core 0.0.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 (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/dist/coverage.d.ts +30 -0
  4. package/dist/coverage.d.ts.map +1 -0
  5. package/dist/coverage.js +81 -0
  6. package/dist/coverage.js.map +1 -0
  7. package/dist/cursor.d.ts +16 -0
  8. package/dist/cursor.d.ts.map +1 -0
  9. package/dist/cursor.js +28 -0
  10. package/dist/cursor.js.map +1 -0
  11. package/dist/dialect.d.ts +316 -0
  12. package/dist/dialect.d.ts.map +1 -0
  13. package/dist/dialect.js +135 -0
  14. package/dist/dialect.js.map +1 -0
  15. package/dist/engine.d.ts +203 -0
  16. package/dist/engine.d.ts.map +1 -0
  17. package/dist/engine.js +493 -0
  18. package/dist/engine.js.map +1 -0
  19. package/dist/index.d.ts +182 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +370 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/json.d.ts +49 -0
  24. package/dist/json.d.ts.map +1 -0
  25. package/dist/json.js +128 -0
  26. package/dist/json.js.map +1 -0
  27. package/dist/keywords/applicator.d.ts +57 -0
  28. package/dist/keywords/applicator.d.ts.map +1 -0
  29. package/dist/keywords/applicator.js +789 -0
  30. package/dist/keywords/applicator.js.map +1 -0
  31. package/dist/keywords/core.d.ts +42 -0
  32. package/dist/keywords/core.d.ts.map +1 -0
  33. package/dist/keywords/core.js +139 -0
  34. package/dist/keywords/core.js.map +1 -0
  35. package/dist/keywords/format.d.ts +43 -0
  36. package/dist/keywords/format.d.ts.map +1 -0
  37. package/dist/keywords/format.js +101 -0
  38. package/dist/keywords/format.js.map +1 -0
  39. package/dist/keywords/metaschemas2019.d.ts +3 -0
  40. package/dist/keywords/metaschemas2019.d.ts.map +1 -0
  41. package/dist/keywords/metaschemas2019.js +406 -0
  42. package/dist/keywords/metaschemas2019.js.map +1 -0
  43. package/dist/keywords/metaschemas2020.d.ts +3 -0
  44. package/dist/keywords/metaschemas2020.d.ts.map +1 -0
  45. package/dist/keywords/metaschemas2020.js +441 -0
  46. package/dist/keywords/metaschemas2020.js.map +1 -0
  47. package/dist/keywords/metaschemas6.d.ts +3 -0
  48. package/dist/keywords/metaschemas6.d.ts.map +1 -0
  49. package/dist/keywords/metaschemas6.js +221 -0
  50. package/dist/keywords/metaschemas6.js.map +1 -0
  51. package/dist/keywords/metaschemas7.d.ts +3 -0
  52. package/dist/keywords/metaschemas7.d.ts.map +1 -0
  53. package/dist/keywords/metaschemas7.js +248 -0
  54. package/dist/keywords/metaschemas7.js.map +1 -0
  55. package/dist/keywords/unevaluated.d.ts +14 -0
  56. package/dist/keywords/unevaluated.d.ts.map +1 -0
  57. package/dist/keywords/unevaluated.js +296 -0
  58. package/dist/keywords/unevaluated.js.map +1 -0
  59. package/dist/keywords/validation.d.ts +21 -0
  60. package/dist/keywords/validation.d.ts.map +1 -0
  61. package/dist/keywords/validation.js +332 -0
  62. package/dist/keywords/validation.js.map +1 -0
  63. package/dist/keywords/vocab2019.d.ts +36 -0
  64. package/dist/keywords/vocab2019.d.ts.map +1 -0
  65. package/dist/keywords/vocab2019.js +585 -0
  66. package/dist/keywords/vocab2019.js.map +1 -0
  67. package/dist/keywords/vocab2020.d.ts +20 -0
  68. package/dist/keywords/vocab2020.d.ts.map +1 -0
  69. package/dist/keywords/vocab2020.js +55 -0
  70. package/dist/keywords/vocab2020.js.map +1 -0
  71. package/dist/keywords/vocab7.d.ts +46 -0
  72. package/dist/keywords/vocab7.d.ts.map +1 -0
  73. package/dist/keywords/vocab7.js +305 -0
  74. package/dist/keywords/vocab7.js.map +1 -0
  75. package/dist/loader.d.ts +41 -0
  76. package/dist/loader.d.ts.map +1 -0
  77. package/dist/loader.js +13 -0
  78. package/dist/loader.js.map +1 -0
  79. package/dist/lowering.d.ts +371 -0
  80. package/dist/lowering.d.ts.map +1 -0
  81. package/dist/lowering.js +59 -0
  82. package/dist/lowering.js.map +1 -0
  83. package/dist/output.d.ts +251 -0
  84. package/dist/output.d.ts.map +1 -0
  85. package/dist/output.js +357 -0
  86. package/dist/output.js.map +1 -0
  87. package/dist/records.d.ts +71 -0
  88. package/dist/records.d.ts.map +1 -0
  89. package/dist/records.js +150 -0
  90. package/dist/records.js.map +1 -0
  91. package/dist/ref.d.ts +10 -0
  92. package/dist/ref.d.ts.map +1 -0
  93. package/dist/ref.js +5 -0
  94. package/dist/ref.js.map +1 -0
  95. package/dist/regex.d.ts +45 -0
  96. package/dist/regex.d.ts.map +1 -0
  97. package/dist/regex.js +163 -0
  98. package/dist/regex.js.map +1 -0
  99. package/dist/registry.d.ts +141 -0
  100. package/dist/registry.d.ts.map +1 -0
  101. package/dist/registry.js +407 -0
  102. package/dist/registry.js.map +1 -0
  103. package/dist/result.d.ts +109 -0
  104. package/dist/result.d.ts.map +1 -0
  105. package/dist/result.js +112 -0
  106. package/dist/result.js.map +1 -0
  107. package/dist/uri.d.ts +18 -0
  108. package/dist/uri.d.ts.map +1 -0
  109. package/dist/uri.js +27 -0
  110. package/dist/uri.js.map +1 -0
  111. package/dist/walk.d.ts +25 -0
  112. package/dist/walk.d.ts.map +1 -0
  113. package/dist/walk.js +65 -0
  114. package/dist/walk.js.map +1 -0
  115. package/package.json +32 -0
  116. package/src/coverage.ts +88 -0
  117. package/src/cursor.ts +42 -0
  118. package/src/dialect.ts +428 -0
  119. package/src/engine.ts +741 -0
  120. package/src/index.ts +699 -0
  121. package/src/json.ts +137 -0
  122. package/src/keywords/applicator.ts +895 -0
  123. package/src/keywords/core.ts +168 -0
  124. package/src/keywords/format.ts +143 -0
  125. package/src/keywords/metaschemas2019.ts +410 -0
  126. package/src/keywords/metaschemas2020.ts +443 -0
  127. package/src/keywords/metaschemas6.ts +226 -0
  128. package/src/keywords/metaschemas7.ts +253 -0
  129. package/src/keywords/unevaluated.ts +329 -0
  130. package/src/keywords/validation.ts +627 -0
  131. package/src/keywords/vocab2019.ts +676 -0
  132. package/src/keywords/vocab2020.ts +82 -0
  133. package/src/keywords/vocab7.ts +393 -0
  134. package/src/loader.ts +60 -0
  135. package/src/lowering.ts +414 -0
  136. package/src/output.ts +651 -0
  137. package/src/records.ts +226 -0
  138. package/src/ref.ts +14 -0
  139. package/src/regex.ts +171 -0
  140. package/src/registry.ts +481 -0
  141. package/src/result.ts +252 -0
  142. package/src/uri.ts +35 -0
  143. package/src/walk.ts +88 -0
@@ -0,0 +1,296 @@
1
+ // Unevaluated vocabulary: the channel-consumer keyword class. Phase 1: runs
2
+ // after every other keyword in the same schema object has merged.
3
+ import { isObject } from "../json.js";
4
+ import { lowerIR } from "../lowering.js";
5
+ import { childCursor } from "../cursor.js";
6
+ import { SELF } from "./core.js";
7
+ import { properties, patternProperties, additionalProperties, prefixItems, items, contains, } from "./applicator.js";
8
+ /** 2020-12 unevaluated vocabulary URI. */
9
+ export const VOCAB_UNEVALUATED = "https://json-schema.org/draft/2020-12/vocab/unevaluated";
10
+ /**
11
+ * EXEMPLAR (consumer class): reads visible dependency records (engine.ts
12
+ * visibility rule), applies the subschema to properties nobody evaluated,
13
+ * and produces like any other applicator.
14
+ */
15
+ export const unevaluatedProperties = {
16
+ id: `${VOCAB_UNEVALUATED}#unevaluatedProperties`,
17
+ phase: 1,
18
+ analyze: () => ({
19
+ subschemas: SELF.subschemas,
20
+ consumes: [
21
+ properties.id,
22
+ patternProperties.id,
23
+ additionalProperties.id,
24
+ `${VOCAB_UNEVALUATED}#unevaluatedProperties`,
25
+ ],
26
+ produces: [`${VOCAB_UNEVALUATED}#unevaluatedProperties`],
27
+ evaluatesNames: { kind: "all" },
28
+ applications: [
29
+ { path: [], mode: "childSweep", conditional: false, asserts: true },
30
+ ],
31
+ }),
32
+ // Static-coverage path only (D9a): the planner classifies this schema
33
+ // object as interpreted when any coverage contributor is dynamic, so
34
+ // lower() is never called with a null coverage.
35
+ lower: (_value, lctx) => {
36
+ // Runtime-coverage path (phase B activates it): fold the unit's runtime
37
+ // channel into an evaluated-name set and sweep the names it does not
38
+ // cover, mirroring evaluate()'s seen-set skip. The object-gated
39
+ // collectedNames produce is the same one the static path emits — its
40
+ // attempted-apply accumulator is exactly the interpreter's `matched`.
41
+ if (lctx.runtimeCoverage()) {
42
+ const f = lctx.binding();
43
+ const b = lctx.binding();
44
+ lctx.emit(lowerIR.when(lowerIR.typeIs(lctx.instance, "object"), [
45
+ { kind: "coverageFold", half: "names", binding: f },
46
+ {
47
+ kind: "forEachKey",
48
+ target: lctx.instance,
49
+ binding: b,
50
+ body: [
51
+ lowerIR.when(lowerIR.not({
52
+ kind: "coverageCovers",
53
+ fold: f,
54
+ target: { kind: "binding", id: b },
55
+ }), [
56
+ {
57
+ kind: "apply",
58
+ apply: {
59
+ path: [],
60
+ cursor: {
61
+ kind: "child",
62
+ of: { kind: "here" },
63
+ segment: { kind: "binding", id: b },
64
+ },
65
+ fold: "allMustPass",
66
+ },
67
+ },
68
+ ]),
69
+ ],
70
+ },
71
+ { kind: "produce", value: { kind: "collectedNames" } },
72
+ ]));
73
+ return;
74
+ }
75
+ const coverage = lctx.staticCoverage();
76
+ if (coverage === null) {
77
+ throw new Error("unevaluatedProperties lowering requires static coverage (planner bug)");
78
+ }
79
+ // The sweep is statically vacuous when sibling coverage is total, but the
80
+ // produce is not: evaluate() emits an (empty) names annotation for every
81
+ // object regardless. So the object-type guard always wraps a produce; the
82
+ // sweep is added only when some name can still be unevaluated.
83
+ const body = [];
84
+ if (!coverage.coversAllNames) {
85
+ const b = lctx.binding();
86
+ const covered = [
87
+ ...coverage.names.map((n) => lowerIR.cmp("===", { kind: "binding", id: b }, lowerIR.constant(n))),
88
+ ...coverage.patterns.map((p) => lowerIR.regexTest(p, { kind: "binding", id: b })),
89
+ ];
90
+ body.push({
91
+ kind: "forEachKey",
92
+ target: lctx.instance,
93
+ binding: b,
94
+ body: [
95
+ lowerIR.when(covered.length === 0
96
+ ? lowerIR.constant(true)
97
+ : lowerIR.not(lowerIR.or(...covered)), [
98
+ {
99
+ kind: "apply",
100
+ apply: {
101
+ path: [],
102
+ cursor: {
103
+ kind: "child",
104
+ of: { kind: "here" },
105
+ segment: { kind: "binding", id: b },
106
+ },
107
+ fold: "allMustPass",
108
+ },
109
+ },
110
+ ]),
111
+ ],
112
+ });
113
+ }
114
+ body.push({ kind: "produce", value: { kind: "collectedNames" } });
115
+ // Produce iff the instance is an object (nothing otherwise), matching
116
+ // evaluate()'s object-type guard before ctx.produce.
117
+ lctx.emit(lowerIR.when(lowerIR.typeIs(lctx.instance, "object"), body));
118
+ },
119
+ evaluate: (_value, cursor, ctx) => {
120
+ if (!isObject(cursor.value))
121
+ return true;
122
+ const seen = new Set();
123
+ for (const p of ctx.visible([
124
+ properties.id,
125
+ patternProperties.id,
126
+ additionalProperties.id,
127
+ unevaluatedProperties.id,
128
+ ])) {
129
+ for (const name of p.data)
130
+ seen.add(name);
131
+ }
132
+ let ok = true;
133
+ const matched = [];
134
+ for (const name of Object.keys(cursor.value)) {
135
+ if (seen.has(name))
136
+ continue;
137
+ matched.push(name);
138
+ if (!ctx.apply(["unevaluatedProperties"], childCursor(cursor, name, cursor.value[name])))
139
+ ok = false;
140
+ }
141
+ // Dependency data only from an accepting keyword (Appendix D; see
142
+ // applicator.ts properties).
143
+ if (ok)
144
+ ctx.produce(matched);
145
+ return ok;
146
+ },
147
+ };
148
+ /** Same consumer shape as {@link unevaluatedProperties}, over array indexes not covered by `prefixItems`/`items`/`contains`. */
149
+ export const unevaluatedItems = {
150
+ id: `${VOCAB_UNEVALUATED}#unevaluatedItems`,
151
+ phase: 1,
152
+ analyze: () => ({
153
+ subschemas: SELF.subschemas,
154
+ consumes: [
155
+ prefixItems.id,
156
+ items.id,
157
+ contains.id,
158
+ `${VOCAB_UNEVALUATED}#unevaluatedItems`,
159
+ ],
160
+ produces: [`${VOCAB_UNEVALUATED}#unevaluatedItems`],
161
+ evaluatesIndexes: { kind: "all" },
162
+ applications: [
163
+ { path: [], mode: "childSweep", conditional: false, asserts: true },
164
+ ],
165
+ }),
166
+ // Static-coverage path only (D9a), same discipline as
167
+ // unevaluatedProperties: the planner classifies this schema object as
168
+ // interpreted whenever any index-coverage contributor is dynamic (e.g. a
169
+ // sibling `contains`, whose coverage is instance-dependent), so lower() is
170
+ // never called with a null/incomplete coverage.
171
+ lower: (_value, lctx) => {
172
+ // Runtime-coverage path (phase B activates it): fold the unit's runtime
173
+ // channel into a coveredPrefix/coveredIdx summary over this array's
174
+ // length and sweep the indexes it does not cover, mirroring evaluate()'s
175
+ // skip loop. The appliedTrue produce is the same one the static path
176
+ // emits (any attempted apply -> true, the interpreter's `applied` flag).
177
+ if (lctx.runtimeCoverage()) {
178
+ const f = lctx.binding();
179
+ const b = lctx.binding();
180
+ lctx.emit(lowerIR.when(lowerIR.typeIs(lctx.instance, "array"), [
181
+ { kind: "coverageFold", half: "indexes", binding: f },
182
+ {
183
+ kind: "forEachIndex",
184
+ target: lctx.instance,
185
+ binding: b,
186
+ start: 0,
187
+ body: [
188
+ lowerIR.when(lowerIR.not({
189
+ kind: "coverageCovers",
190
+ fold: f,
191
+ target: { kind: "binding", id: b },
192
+ }), [
193
+ {
194
+ kind: "apply",
195
+ apply: {
196
+ path: [],
197
+ cursor: {
198
+ kind: "child",
199
+ of: { kind: "here" },
200
+ segment: { kind: "binding", id: b },
201
+ },
202
+ fold: "allMustPass",
203
+ },
204
+ },
205
+ ]),
206
+ ],
207
+ },
208
+ {
209
+ kind: "produce",
210
+ value: { kind: "collectedIndexes", render: "appliedTrue" },
211
+ },
212
+ ]));
213
+ return;
214
+ }
215
+ const coverage = lctx.staticCoverage();
216
+ if (coverage === null) {
217
+ throw new Error("unevaluatedItems lowering requires static coverage (planner bug)");
218
+ }
219
+ if (coverage.coversAllIndexes)
220
+ return; // statically vacuous
221
+ const b = lctx.binding();
222
+ lctx.emit(lowerIR.when(lowerIR.typeIs(lctx.instance, "array"), [
223
+ {
224
+ kind: "forEachIndex",
225
+ target: lctx.instance,
226
+ binding: b,
227
+ start: coverage.prefixCount,
228
+ body: [
229
+ {
230
+ kind: "apply",
231
+ apply: {
232
+ path: [],
233
+ cursor: {
234
+ kind: "child",
235
+ of: { kind: "here" },
236
+ segment: { kind: "binding", id: b },
237
+ },
238
+ fold: "allMustPass",
239
+ },
240
+ },
241
+ ],
242
+ },
243
+ ]));
244
+ // Annotation: true iff it applied to any unevaluated index.
245
+ lctx.emit({
246
+ kind: "produce",
247
+ value: { kind: "collectedIndexes", render: "appliedTrue" },
248
+ });
249
+ },
250
+ evaluate: (_value, cursor, ctx) => {
251
+ if (!Array.isArray(cursor.value))
252
+ return true;
253
+ const length = cursor.value.length;
254
+ let coveredPrefix = 0;
255
+ const coveredIdx = new Set();
256
+ for (const p of ctx.visible([
257
+ prefixItems.id,
258
+ items.id,
259
+ contains.id,
260
+ unevaluatedItems.id,
261
+ ])) {
262
+ if (p.behaviorId === contains.id) {
263
+ if (p.data === true)
264
+ coveredPrefix = length;
265
+ else
266
+ for (const i of p.data)
267
+ coveredIdx.add(i);
268
+ }
269
+ else if (p.data === true) {
270
+ coveredPrefix = length;
271
+ }
272
+ else if (p.behaviorId === prefixItems.id) {
273
+ coveredPrefix = Math.max(coveredPrefix, p.data + 1);
274
+ }
275
+ }
276
+ let ok = true;
277
+ let applied = false;
278
+ for (let i = coveredPrefix; i < length; i++) {
279
+ if (coveredIdx.has(i))
280
+ continue;
281
+ applied = true;
282
+ if (!ctx.apply(["unevaluatedItems"], childCursor(cursor, i, cursor.value[i]))) {
283
+ ok = false;
284
+ }
285
+ }
286
+ if (applied && ok)
287
+ ctx.produce(true);
288
+ return ok;
289
+ },
290
+ };
291
+ /** The 2020-12 unevaluated vocabulary's keyword behaviors, by name. */
292
+ export const unevaluatedVocabulary = {
293
+ unevaluatedProperties,
294
+ unevaluatedItems,
295
+ };
296
+ //# sourceMappingURL=unevaluated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unevaluated.js","sourceRoot":"","sources":["../../src/keywords/unevaluated.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { KeywordBehavior } from "../dialect.js";
2
+ /** 2020-12 validation vocabulary URI. */
3
+ export declare const VOCAB_VALIDATION = "https://json-schema.org/draft/2020-12/vocab/validation";
4
+ /**
5
+ * EXEMPLAR (assertion class): inspect the instance, report one error on
6
+ * failure, return the verdict. `pattern` compiles its regex through the
7
+ * context so a caller-supplied engine and the per-engine cache apply
8
+ * (see regex.ts), and declares the pattern via analyze() so
9
+ * `rejectUnsafeRegex` can screen it at registration.
10
+ */
11
+ export declare const pattern: KeywordBehavior;
12
+ /**
13
+ * `instance` is an exact multiple of `divisor` (`multipleOf`'s predicate,
14
+ * shared by both tiers). Scales both operands to integers by the same power
15
+ * of ten before the modulus so binary-float rounding doesn't misfire (e.g.
16
+ * 0.0075 % 0.0001 in raw floats).
17
+ */
18
+ export declare function isMultipleOf(instance: number, divisor: number): boolean;
19
+ /** The 2020-12 validation vocabulary's keyword behaviors, by name. */
20
+ export declare const validationVocabulary: Record<string, KeywordBehavior>;
21
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/keywords/validation.ts"],"names":[],"mappings":"AAYA,OAAO,EAAe,eAAe,EAAkB,MAAM,eAAe,CAAC;AAS7E,yCAAyC;AACzC,eAAO,MAAM,gBAAgB,2DAC6B,CAAC;AA8D3D;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,EAAE,eA4BrB,CAAC;AAsBF;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAYvE;AAED,sEAAsE;AACtE,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CA+chE,CAAC"}
@@ -0,0 +1,332 @@
1
+ // Validation vocabulary: pure assertions. Assertions never produce and
2
+ // never descend.
3
+ import { isObject, jsonTypeOf, jsonEqual, codePointLength, firstDuplicatePair, } from "../json.js";
4
+ import { lowerIR, } from "../lowering.js";
5
+ /** 2020-12 validation vocabulary URI. */
6
+ export const VOCAB_VALIDATION = "https://json-schema.org/draft/2020-12/vocab/validation";
7
+ const id = (name) => `${VOCAB_VALIDATION}#${name}`;
8
+ const assertion = (name, test, message, params) => ({
9
+ id: id(name),
10
+ evaluate: (value, cursor, ctx) => {
11
+ if (test(value, cursor.value))
12
+ return true;
13
+ ctx.error(message(value), params?.(value));
14
+ return false;
15
+ },
16
+ });
17
+ /**
18
+ * Shared shape for the numeric/string/array/object "guard, then compare"
19
+ * assertions (minLength/maxLength/minimum/maximum/exclusiveMinimum/
20
+ * exclusiveMaximum/minItems/maxItems/minProperties/maxProperties): the
21
+ * keyword is vacuously true unless the instance has the guarded type, in
22
+ * which case a comparison against the (hoisted) keyword value must hold.
23
+ * Mirrors each assertion's `test` above exactly — same guard, same
24
+ * direction of comparison — via `when(and(guard, not(cmp)), [fail])`.
25
+ */
26
+ const guardedCompare = (guardType, measure, op, message) => (value, lctx) => {
27
+ lctx.emit(lowerIR.when(lowerIR.and(lowerIR.typeIs(lctx.instance, guardType), lowerIR.not(lowerIR.cmp(op, measure(lctx), lowerIR.constant(value)))), [lowerIR.failWith({ limit: lowerIR.constant(value) }, message(value))]));
28
+ };
29
+ /**
30
+ * The `limit` params shape shared by every guard-then-compare bounds
31
+ * keyword: minLength/maxLength, minItems/maxItems, minProperties/
32
+ * maxProperties, and minimum/maximum/exclusiveMinimum/exclusiveMaximum.
33
+ */
34
+ const limitParams = (value) => ({ limit: value });
35
+ const arrayLengthMeasure = (lctx) => lowerIR.helper("lengthOf", lctx.instance);
36
+ const propertyCountMeasure = (lctx) => lowerIR.helper("lengthOf", lowerIR.helper("keysOf", lctx.instance));
37
+ const numberMeasure = (lctx) => lctx.instance;
38
+ const typeMatches = (t, v) => t === "integer"
39
+ ? typeof v === "number" && Number.isInteger(v)
40
+ : jsonTypeOf(v) === t;
41
+ /**
42
+ * EXEMPLAR (assertion class): inspect the instance, report one error on
43
+ * failure, return the verdict. `pattern` compiles its regex through the
44
+ * context so a caller-supplied engine and the per-engine cache apply
45
+ * (see regex.ts), and declares the pattern via analyze() so
46
+ * `rejectUnsafeRegex` can screen it at registration.
47
+ */
48
+ export const pattern = {
49
+ id: id("pattern"),
50
+ analyze: (value) => ({
51
+ regexes: typeof value === "string" ? [value] : [],
52
+ }),
53
+ evaluate: (value, cursor, ctx) => {
54
+ const instance = cursor.value;
55
+ if (typeof instance !== "string")
56
+ return true;
57
+ if (ctx.compileRegex(value).test(instance))
58
+ return true;
59
+ ctx.error("does not match required pattern", { pattern: value });
60
+ return false;
61
+ },
62
+ lower: (value, lctx) => {
63
+ lctx.emit(lowerIR.when(lowerIR.and(lowerIR.typeIs(lctx.instance, "string"), lowerIR.not(lowerIR.regexTest(value, lctx.instance))), [
64
+ lowerIR.failWith({ pattern: lowerIR.constant(value) }, "does not match required pattern"),
65
+ ]));
66
+ },
67
+ };
68
+ // Number of digits after the decimal point in `n`'s shortest representation,
69
+ // including exponential notation (1e-8 has 8). `%` on the raw floats fails
70
+ // suite cases like 0.0075 % 0.0001 (binary rounding); scaling both operands
71
+ // to integers by the same power of ten sidesteps that at the cost of this
72
+ // string inspection.
73
+ function decimalDigits(n) {
74
+ if (!Number.isFinite(n))
75
+ return 0;
76
+ const s = Math.abs(n).toString();
77
+ const eIndex = s.indexOf("e");
78
+ if (eIndex !== -1) {
79
+ const mantissa = s.slice(0, eIndex);
80
+ const exponent = Number(s.slice(eIndex + 1));
81
+ const dot = mantissa.indexOf(".");
82
+ const mantissaDigits = dot === -1 ? 0 : mantissa.length - dot - 1;
83
+ return Math.max(0, mantissaDigits - exponent);
84
+ }
85
+ const dot = s.indexOf(".");
86
+ return dot === -1 ? 0 : s.length - dot - 1;
87
+ }
88
+ /**
89
+ * `instance` is an exact multiple of `divisor` (`multipleOf`'s predicate,
90
+ * shared by both tiers). Scales both operands to integers by the same power
91
+ * of ten before the modulus so binary-float rounding doesn't misfire (e.g.
92
+ * 0.0075 % 0.0001 in raw floats).
93
+ */
94
+ export function isMultipleOf(instance, divisor) {
95
+ const scale = 10 ** Math.max(decimalDigits(instance), decimalDigits(divisor));
96
+ const scaledInstance = instance * scale;
97
+ const scaledDivisor = divisor * scale;
98
+ // Suite case: the scaling itself can overflow to Infinity for huge
99
+ // instances against a small divisor; that must read as non-multiple, not
100
+ // throw or silently misvalidate.
101
+ if (Number.isFinite(scaledInstance) && Number.isFinite(scaledDivisor)) {
102
+ return Math.round(scaledInstance) % Math.round(scaledDivisor) === 0;
103
+ }
104
+ const quotient = instance / divisor;
105
+ return Number.isFinite(quotient) && Number.isInteger(quotient);
106
+ }
107
+ /** The 2020-12 validation vocabulary's keyword behaviors, by name. */
108
+ export const validationVocabulary = {
109
+ type: {
110
+ id: id("type"),
111
+ evaluate: (value, cursor, ctx) => {
112
+ const ok = Array.isArray(value)
113
+ ? value.some((t) => typeMatches(t, cursor.value))
114
+ : typeMatches(value, cursor.value);
115
+ if (!ok)
116
+ ctx.error(`expected type ${JSON.stringify(value)}`, {
117
+ expected: value,
118
+ });
119
+ return ok;
120
+ },
121
+ lower: (value, lctx) => {
122
+ const types = (Array.isArray(value) ? value : [value]);
123
+ lctx.emit(lowerIR.when(lowerIR.not(lowerIR.typeIs(lctx.instance, ...types)), [
124
+ lowerIR.failWith({ expected: lowerIR.constant(value) }, `expected type ${JSON.stringify(value)}`),
125
+ ]));
126
+ },
127
+ },
128
+ enum: {
129
+ ...assertion("enum", (value, instance) => value.some((x) => jsonEqual(x, instance)), () => "not one of the allowed values", (value) => ({ allowedValues: value })),
130
+ lower: (value, lctx) => {
131
+ const alternatives = value;
132
+ const params = { allowedValues: lowerIR.constant(value) };
133
+ // An empty enum can never match (some() over zero alternatives is
134
+ // false); guard explicitly since lowerIR.or() with zero parts has no
135
+ // meaningful "no alternatives matched" expression to negate.
136
+ lctx.emit(alternatives.length === 0
137
+ ? lowerIR.failWith(params, "not one of the allowed values")
138
+ : lowerIR.when(lowerIR.not(lowerIR.or(...alternatives.map((x) => lowerIR.helper("jsonEqual", lowerIR.constant(x), lctx.instance)))), [lowerIR.failWith(params, "not one of the allowed values")]));
139
+ },
140
+ },
141
+ const: {
142
+ ...assertion("const", (value, instance) => jsonEqual(value, instance), () => "does not equal the required constant", (value) => ({ allowedValue: value })),
143
+ lower: (value, lctx) => {
144
+ lctx.emit(lowerIR.when(lowerIR.not(lowerIR.helper("jsonEqual", lowerIR.constant(value), lctx.instance)), [
145
+ lowerIR.failWith({ allowedValue: lowerIR.constant(value) }, "does not equal the required constant"),
146
+ ]));
147
+ },
148
+ },
149
+ pattern,
150
+ minLength: {
151
+ ...assertion("minLength", (value, instance) => typeof instance !== "string" ||
152
+ codePointLength(instance) >= value, (value) => `must be at least ${value} characters`, limitParams),
153
+ // Violation = code points < n. UTF-16 units bound points from above
154
+ // (points <= units) and below (points >= units/2), so units alone decide
155
+ // outside [n, 2n) — codePointLength runs only in that window (D9).
156
+ lower: (value, lctx) => {
157
+ const n = value;
158
+ const len = lowerIR.helper("lengthOf", lctx.instance);
159
+ const cpl = lowerIR.helper("codePointLength", lctx.instance);
160
+ lctx.emit(lowerIR.when(lowerIR.and(lowerIR.typeIs(lctx.instance, "string"), lowerIR.or(lowerIR.cmp("<", len, lowerIR.constant(n)), lowerIR.and(lowerIR.cmp("<", len, lowerIR.constant(2 * n)), lowerIR.cmp("<", cpl, lowerIR.constant(n))))), [
161
+ lowerIR.failWith({ limit: lowerIR.constant(n) }, `must be at least ${n} characters`),
162
+ ]));
163
+ },
164
+ },
165
+ maxLength: {
166
+ ...assertion("maxLength", (value, instance) => typeof instance !== "string" ||
167
+ codePointLength(instance) <= value, (value) => `must be at most ${value} characters`, limitParams),
168
+ // Violation = code points > n; units <= n implies points <= n, so the
169
+ // expensive count runs only when units exceed the bound (D9).
170
+ lower: (value, lctx) => {
171
+ const n = value;
172
+ lctx.emit(lowerIR.when(lowerIR.and(lowerIR.typeIs(lctx.instance, "string"), lowerIR.cmp(">", lowerIR.helper("lengthOf", lctx.instance), lowerIR.constant(n)), lowerIR.cmp(">", lowerIR.helper("codePointLength", lctx.instance), lowerIR.constant(n))), [
173
+ lowerIR.failWith({ limit: lowerIR.constant(n) }, `must be at most ${n} characters`),
174
+ ]));
175
+ },
176
+ },
177
+ minimum: {
178
+ ...assertion("minimum", (value, instance) => typeof instance !== "number" || instance >= value, (value) => `must be >= ${value}`, limitParams),
179
+ lower: guardedCompare("number", numberMeasure, ">=", (value) => `must be >= ${value}`),
180
+ },
181
+ maximum: {
182
+ ...assertion("maximum", (value, instance) => typeof instance !== "number" || instance <= value, (value) => `must be <= ${value}`, limitParams),
183
+ lower: guardedCompare("number", numberMeasure, "<=", (value) => `must be <= ${value}`),
184
+ },
185
+ exclusiveMinimum: {
186
+ ...assertion("exclusiveMinimum", (value, instance) => typeof instance !== "number" || instance > value, (value) => `must be > ${value}`, limitParams),
187
+ lower: guardedCompare("number", numberMeasure, ">", (value) => `must be > ${value}`),
188
+ },
189
+ exclusiveMaximum: {
190
+ ...assertion("exclusiveMaximum", (value, instance) => typeof instance !== "number" || instance < value, (value) => `must be < ${value}`, limitParams),
191
+ lower: guardedCompare("number", numberMeasure, "<", (value) => `must be < ${value}`),
192
+ },
193
+ minItems: {
194
+ ...assertion("minItems", (value, instance) => !Array.isArray(instance) || instance.length >= value, (value) => `must have at least ${value} items`, limitParams),
195
+ lower: guardedCompare("array", arrayLengthMeasure, ">=", (value) => `must have at least ${value} items`),
196
+ },
197
+ maxItems: {
198
+ ...assertion("maxItems", (value, instance) => !Array.isArray(instance) || instance.length <= value, (value) => `must have at most ${value} items`, limitParams),
199
+ lower: guardedCompare("array", arrayLengthMeasure, "<=", (value) => `must have at most ${value} items`),
200
+ },
201
+ minProperties: {
202
+ ...assertion("minProperties", (value, instance) => !isObject(instance) ||
203
+ Object.keys(instance).length >= value, (value) => `must have at least ${value} properties`, limitParams),
204
+ lower: guardedCompare("object", propertyCountMeasure, ">=", (value) => `must have at least ${value} properties`),
205
+ },
206
+ maxProperties: {
207
+ ...assertion("maxProperties", (value, instance) => !isObject(instance) ||
208
+ Object.keys(instance).length <= value, (value) => `must have at most ${value} properties`, limitParams),
209
+ lower: guardedCompare("object", propertyCountMeasure, "<=", (value) => `must have at most ${value} properties`),
210
+ },
211
+ required: {
212
+ id: id("required"),
213
+ evaluate: (value, cursor, ctx) => {
214
+ if (!isObject(cursor.value))
215
+ return true;
216
+ let ok = true;
217
+ for (const name of value) {
218
+ if (!Object.hasOwn(cursor.value, name)) {
219
+ ctx.error(`missing required property '${name}'`, {
220
+ missingProperty: name,
221
+ });
222
+ ok = false;
223
+ }
224
+ }
225
+ return ok;
226
+ },
227
+ lower: (value, lctx) => {
228
+ lctx.emit(lowerIR.when(lowerIR.typeIs(lctx.instance, "object"), [
229
+ ...value.map((name) => lowerIR.when(lowerIR.not({
230
+ kind: "hasOwn",
231
+ target: lctx.instance,
232
+ key: name,
233
+ }), [
234
+ lowerIR.failWith({ missingProperty: lowerIR.constant(name) }, `missing required property '${name}'`),
235
+ ])),
236
+ ]));
237
+ },
238
+ },
239
+ multipleOf: {
240
+ ...assertion("multipleOf", (value, instance) => typeof instance !== "number" || isMultipleOf(instance, value), (value) => `must be a multiple of ${value}`, (value) => ({ multipleOf: value })),
241
+ lower: (value, lctx) => {
242
+ lctx.emit(lowerIR.when(lowerIR.and(lowerIR.typeIs(lctx.instance, "number"), lowerIR.not(lowerIR.helper("isMultipleOf", lctx.instance, lowerIR.constant(value)))), [
243
+ lowerIR.failWith({ multipleOf: lowerIR.constant(value) }, `must be a multiple of ${value}`),
244
+ ]));
245
+ },
246
+ },
247
+ uniqueItems: {
248
+ id: id("uniqueItems"),
249
+ evaluate: (value, cursor, ctx) => {
250
+ if (value !== true || !Array.isArray(cursor.value))
251
+ return true;
252
+ const pair = firstDuplicatePair(cursor.value);
253
+ if (pair === null)
254
+ return true;
255
+ const [j, i] = pair;
256
+ ctx.error(`items at ${j} and ${i} are not unique`, {
257
+ duplicates: [j, i],
258
+ });
259
+ return false;
260
+ },
261
+ // evaluate() returns true (vacuously) whenever `value !== true` — mirror
262
+ // that by emitting nothing at all when the keyword value isn't literally
263
+ // `true` (the message text can't cite specific indexes at compile time,
264
+ // but the assertion itself does not depend on them: any duplicate fails).
265
+ lower: (value, lctx) => {
266
+ if (value !== true)
267
+ return;
268
+ // The pair lookup in the message runs only on the failure path
269
+ // (D9e), so the duplicate scan's cost is not doubled for valid data.
270
+ const pair = lowerIR.helper("firstDuplicatePair", lctx.instance);
271
+ lctx.emit(lowerIR.when(lowerIR.and(lowerIR.typeIs(lctx.instance, "array"), lowerIR.helper("hasDuplicateItems", lctx.instance)), [
272
+ lowerIR.failWith({ duplicates: pair }, "items at ", { kind: "item", target: pair, index: lowerIR.constant(0) }, " and ", { kind: "item", target: pair, index: lowerIR.constant(1) }, " are not unique"),
273
+ ]));
274
+ },
275
+ },
276
+ dependentRequired: {
277
+ id: id("dependentRequired"),
278
+ evaluate: (value, cursor, ctx) => {
279
+ if (!isObject(cursor.value))
280
+ return true;
281
+ const instance = cursor.value;
282
+ let ok = true;
283
+ for (const [name, deps] of Object.entries(value)) {
284
+ if (!Object.hasOwn(instance, name))
285
+ continue;
286
+ for (const dep of deps) {
287
+ if (!Object.hasOwn(instance, dep)) {
288
+ ctx.error(`'${name}' requires '${dep}' to be present`, {
289
+ property: name,
290
+ missingProperty: dep,
291
+ });
292
+ ok = false;
293
+ }
294
+ }
295
+ }
296
+ return ok;
297
+ },
298
+ lower: (value, lctx) => {
299
+ lctx.emit(lowerIR.when(lowerIR.typeIs(lctx.instance, "object"), [
300
+ ...Object.entries(value).map(([name, deps]) => lowerIR.when({ kind: "hasOwn", target: lctx.instance, key: name }, deps.map((dep) => lowerIR.when(lowerIR.not({
301
+ kind: "hasOwn",
302
+ target: lctx.instance,
303
+ key: dep,
304
+ }), [
305
+ lowerIR.failWith({
306
+ property: lowerIR.constant(name),
307
+ missingProperty: lowerIR.constant(dep),
308
+ }, `'${name}' requires '${dep}' to be present`),
309
+ ])))),
310
+ ]));
311
+ },
312
+ },
313
+ // minContains/maxContains have no assertion of their own; `contains` reads
314
+ // them as inert siblings (applicator.ts), the same way `if` drives `then`/
315
+ // `else`. They must still be registered so unknown-keyword handling and the
316
+ // registration walk don't treat them as annotation-only or absent.
317
+ minContains: {
318
+ id: id("minContains"),
319
+ evaluate: () => true,
320
+ lower: () => {
321
+ /* contains reads this sibling directly (lctx.schema.minContains) */
322
+ },
323
+ },
324
+ maxContains: {
325
+ id: id("maxContains"),
326
+ evaluate: () => true,
327
+ lower: () => {
328
+ /* contains reads this sibling directly (lctx.schema.maxContains) */
329
+ },
330
+ },
331
+ };
332
+ //# sourceMappingURL=validation.js.map