json-schema-compatibility-checker 1.0.5 → 1.0.7
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 +119 -151
- package/dist/{condition-resolver.d.ts → cjs/condition-resolver.d.ts} +2 -2
- package/dist/cjs/condition-resolver.js +2 -0
- package/dist/cjs/condition-resolver.js.map +1 -0
- package/dist/cjs/data-narrowing.d.ts +31 -0
- package/dist/cjs/data-narrowing.js +2 -0
- package/dist/cjs/data-narrowing.js.map +1 -0
- package/dist/cjs/format-validator.js +2 -0
- package/dist/cjs/format-validator.js.map +1 -0
- package/dist/{formatter.d.ts → cjs/formatter.d.ts} +1 -1
- package/dist/cjs/formatter.js +2 -0
- package/dist/cjs/formatter.js.map +1 -0
- package/dist/cjs/index.d.ts +6 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/json-schema-compatibility-checker.d.ts +86 -0
- package/dist/cjs/json-schema-compatibility-checker.js +2 -0
- package/dist/cjs/json-schema-compatibility-checker.js.map +1 -0
- package/dist/cjs/merge-engine.js +2 -0
- package/dist/cjs/merge-engine.js.map +1 -0
- package/dist/cjs/normalizer.js +2 -0
- package/dist/cjs/normalizer.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/pattern-subset.js +2 -0
- package/dist/cjs/pattern-subset.js.map +1 -0
- package/dist/{semantic-errors.d.ts → cjs/semantic-errors.d.ts} +1 -1
- package/dist/cjs/semantic-errors.js +2 -0
- package/dist/cjs/semantic-errors.js.map +1 -0
- package/dist/{subset-checker.d.ts → cjs/subset-checker.d.ts} +2 -2
- package/dist/cjs/subset-checker.js +2 -0
- package/dist/cjs/subset-checker.js.map +1 -0
- package/dist/{types.d.ts → cjs/types.d.ts} +19 -3
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/utils.js +2 -0
- package/dist/cjs/utils.js.map +1 -0
- package/dist/esm/condition-resolver.d.ts +26 -0
- package/dist/esm/condition-resolver.js +2 -0
- package/dist/esm/condition-resolver.js.map +1 -0
- package/dist/esm/data-narrowing.d.ts +31 -0
- package/dist/esm/data-narrowing.js +2 -0
- package/dist/esm/data-narrowing.js.map +1 -0
- package/dist/esm/format-validator.d.ts +78 -0
- package/dist/esm/format-validator.js +2 -0
- package/dist/esm/format-validator.js.map +1 -0
- package/dist/esm/formatter.d.ts +22 -0
- package/dist/esm/formatter.js +2 -0
- package/dist/{chunk-7mkqk5qv.js.map → esm/formatter.js.map} +1 -10
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/json-schema-compatibility-checker.d.ts +86 -0
- package/dist/esm/json-schema-compatibility-checker.js +2 -0
- package/dist/esm/json-schema-compatibility-checker.js.map +1 -0
- package/dist/esm/merge-engine.d.ts +30 -0
- package/dist/esm/merge-engine.js +2 -0
- package/dist/esm/merge-engine.js.map +1 -0
- package/dist/esm/normalizer.d.ts +24 -0
- package/dist/esm/normalizer.js +2 -0
- package/dist/esm/normalizer.js.map +1 -0
- package/dist/esm/pattern-subset.d.ts +59 -0
- package/dist/esm/pattern-subset.js +2 -0
- package/dist/esm/pattern-subset.js.map +1 -0
- package/dist/esm/semantic-errors.d.ts +24 -0
- package/dist/esm/semantic-errors.js +2 -0
- package/dist/esm/semantic-errors.js.map +1 -0
- package/dist/esm/subset-checker.d.ts +81 -0
- package/dist/esm/subset-checker.js +2 -0
- package/dist/esm/subset-checker.js.map +1 -0
- package/dist/esm/types.d.ts +45 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/utils.d.ts +43 -0
- package/dist/esm/utils.js +2 -0
- package/dist/esm/utils.js.map +1 -0
- package/package.json +26 -11
- package/dist/chunk-159ezrfm.js +0 -5
- package/dist/chunk-159ezrfm.js.map +0 -10
- package/dist/chunk-1xda2xvb.js +0 -5
- package/dist/chunk-1xda2xvb.js.map +0 -10
- package/dist/chunk-3gazezx2.js +0 -5
- package/dist/chunk-3gazezx2.js.map +0 -10
- package/dist/chunk-7mkqk5qv.js +0 -6
- package/dist/chunk-aemw3jv0.js +0 -5
- package/dist/chunk-aemw3jv0.js.map +0 -10
- package/dist/chunk-hrwygqa2.js +0 -5
- package/dist/chunk-hrwygqa2.js.map +0 -10
- package/dist/chunk-jg89j4nd.js +0 -5
- package/dist/chunk-jg89j4nd.js.map +0 -10
- package/dist/chunk-kncywgnx.js +0 -5
- package/dist/chunk-kncywgnx.js.map +0 -10
- package/dist/chunk-nkpsq34q.js +0 -5
- package/dist/chunk-nkpsq34q.js.map +0 -10
- package/dist/chunk-nn3cjjtp.js +0 -5
- package/dist/chunk-nn3cjjtp.js.map +0 -10
- package/dist/condition-resolver.js +0 -4
- package/dist/condition-resolver.js.map +0 -9
- package/dist/format-validator.js +0 -4
- package/dist/format-validator.js.map +0 -9
- package/dist/formatter.js +0 -4
- package/dist/formatter.js.map +0 -9
- package/dist/index.d.ts +0 -5
- package/dist/index.js +0 -4
- package/dist/index.js.map +0 -9
- package/dist/json-schema-compatibility-checker.d.ts +0 -73
- package/dist/json-schema-compatibility-checker.js +0 -4
- package/dist/json-schema-compatibility-checker.js.map +0 -9
- package/dist/merge-engine.js +0 -4
- package/dist/merge-engine.js.map +0 -9
- package/dist/normalizer.js +0 -4
- package/dist/normalizer.js.map +0 -9
- package/dist/pattern-subset.js +0 -4
- package/dist/pattern-subset.js.map +0 -9
- package/dist/semantic-errors.js +0 -4
- package/dist/semantic-errors.js.map +0 -9
- package/dist/subset-checker.js +0 -4
- package/dist/subset-checker.js.map +0 -9
- package/dist/types.js +0 -3
- package/dist/types.js.map +0 -9
- package/dist/utils.js +0 -4
- package/dist/utils.js.map +0 -9
- /package/dist/{format-validator.d.ts → cjs/format-validator.d.ts} +0 -0
- /package/dist/{merge-engine.d.ts → cjs/merge-engine.d.ts} +0 -0
- /package/dist/{normalizer.d.ts → cjs/normalizer.d.ts} +0 -0
- /package/dist/{pattern-subset.d.ts → cjs/pattern-subset.d.ts} +0 -0
- /package/dist/{utils.d.ts → cjs/utils.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -15,9 +15,8 @@
|
|
|
15
15
|
- [`check(sub, sup)`](#checksub-sup)
|
|
16
16
|
- [`isEqual(a, b)`](#isequala-b)
|
|
17
17
|
- [`intersect(a, b)`](#intersecta-b)
|
|
18
|
-
- [`canConnect(sourceOutput, targetInput)`](#canconnectsourceoutput-targetinput)
|
|
19
18
|
- [`resolveConditions(schema, data)`](#resolveconditionsschema-data)
|
|
20
|
-
- [`
|
|
19
|
+
- [`check(sub, sup, options)`](#checksub-sup-options)
|
|
21
20
|
- [`normalize(schema)`](#normalizeschema)
|
|
22
21
|
- [`formatResult(label, result)`](#formatresultlabel-result)
|
|
23
22
|
- [Guide des fonctionnalités](#guide-des-fonctionnalités)
|
|
@@ -203,22 +202,24 @@ checker.isSubset({ type: "string" }, true); // → true
|
|
|
203
202
|
|
|
204
203
|
```ts
|
|
205
204
|
check(sub: JSONSchema7Definition, sup: JSONSchema7Definition): SubsetResult
|
|
205
|
+
check(sub: JSONSchema7Definition, sup: JSONSchema7Definition, options: CheckConditionsOptions): ResolvedSubsetResult
|
|
206
206
|
```
|
|
207
207
|
|
|
208
|
-
Comme `isSubset`, mais retourne un **résultat détaillé** avec les
|
|
208
|
+
Comme `isSubset`, mais retourne un **résultat détaillé** avec les erreurs sémantiques.
|
|
209
|
+
|
|
210
|
+
Quand `options` est fourni, les conditions `if/then/else` sont résolues avant le check (voir [`check(sub, sup, options)`](#checksub-sup-options) plus bas).
|
|
209
211
|
|
|
210
212
|
```ts
|
|
213
|
+
interface SchemaError {
|
|
214
|
+
key: string; // Chemin normalisé (ex: "user.name", "users[].email")
|
|
215
|
+
expected: string; // Type/valeur attendu(e) par le schema cible (sup)
|
|
216
|
+
received: string; // Type/valeur reçu(e) depuis le schema source (sub)
|
|
217
|
+
}
|
|
218
|
+
|
|
211
219
|
interface SubsetResult {
|
|
212
220
|
isSubset: boolean;
|
|
213
221
|
merged: JSONSchema7Definition | null; // Résultat de l'intersection
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
interface SchemaDiff {
|
|
218
|
-
path: string; // Chemin JSON-path vers la divergence
|
|
219
|
-
type: "added" | "removed" | "changed";
|
|
220
|
-
expected: unknown; // Valeur dans le schema original (sub)
|
|
221
|
-
actual: unknown; // Valeur dans le schema mergé
|
|
222
|
+
errors: SchemaError[]; // Erreurs sémantiques
|
|
222
223
|
}
|
|
223
224
|
```
|
|
224
225
|
|
|
@@ -231,7 +232,7 @@ const result = checker.check(
|
|
|
231
232
|
);
|
|
232
233
|
|
|
233
234
|
console.log(result.isSubset); // true
|
|
234
|
-
console.log(result.
|
|
235
|
+
console.log(result.errors); // [] (aucune erreur)
|
|
235
236
|
console.log(result.merged); // { type: "string", minLength: 5 }
|
|
236
237
|
```
|
|
237
238
|
|
|
@@ -254,12 +255,13 @@ const sup = {
|
|
|
254
255
|
};
|
|
255
256
|
|
|
256
257
|
const result = checker.check(sub, sup);
|
|
258
|
+
console.log(result.errors);
|
|
259
|
+
// [{ key: "age", expected: "number", received: "undefined" }]
|
|
257
260
|
|
|
258
261
|
console.log(result.isSubset); // false
|
|
259
|
-
console.log(result.
|
|
262
|
+
console.log(result.errors);
|
|
260
263
|
// [
|
|
261
|
-
// {
|
|
262
|
-
// { path: "properties.age", type: "added", expected: undefined, actual: { type: "number" } }
|
|
264
|
+
// { key: "age", expected: "number", received: "undefined" }
|
|
263
265
|
// ]
|
|
264
266
|
```
|
|
265
267
|
|
|
@@ -270,7 +272,7 @@ const result = checker.check({ type: "string" }, { type: "number" });
|
|
|
270
272
|
|
|
271
273
|
console.log(result.isSubset); // false
|
|
272
274
|
console.log(result.merged); // null (intersection impossible)
|
|
273
|
-
console.log(result.
|
|
275
|
+
console.log(result.errors); // [{ key: "$root", expected: "number", received: "string" }]
|
|
274
276
|
```
|
|
275
277
|
|
|
276
278
|
---
|
|
@@ -363,64 +365,6 @@ checker.intersect({ type: "string" }, { type: "number" });
|
|
|
363
365
|
|
|
364
366
|
---
|
|
365
367
|
|
|
366
|
-
### `canConnect(sourceOutput, targetInput)`
|
|
367
|
-
|
|
368
|
-
```ts
|
|
369
|
-
canConnect(
|
|
370
|
-
sourceOutput: JSONSchema7Definition,
|
|
371
|
-
targetInput: JSONSchema7Definition
|
|
372
|
-
): ConnectionResult
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
Vérifie si la **sortie d'un nœud source** peut alimenter l'**entrée d'un nœud cible**. Sémantiquement : `sourceOutput ⊆ targetInput`.
|
|
376
|
-
|
|
377
|
-
```ts
|
|
378
|
-
interface ConnectionResult extends SubsetResult {
|
|
379
|
-
direction: string; // "sourceOutput ⊆ targetInput"
|
|
380
|
-
}
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
```ts
|
|
384
|
-
const nodeAOutput = {
|
|
385
|
-
type: "object",
|
|
386
|
-
properties: {
|
|
387
|
-
id: { type: "string" },
|
|
388
|
-
total: { type: "number", minimum: 0 },
|
|
389
|
-
customer: {
|
|
390
|
-
type: "object",
|
|
391
|
-
properties: {
|
|
392
|
-
email: { type: "string", format: "email" },
|
|
393
|
-
name: { type: "string" },
|
|
394
|
-
},
|
|
395
|
-
required: ["email", "name"],
|
|
396
|
-
},
|
|
397
|
-
},
|
|
398
|
-
required: ["id", "total", "customer"],
|
|
399
|
-
};
|
|
400
|
-
|
|
401
|
-
const nodeBInput = {
|
|
402
|
-
type: "object",
|
|
403
|
-
properties: {
|
|
404
|
-
id: { type: "string" },
|
|
405
|
-
total: { type: "number" },
|
|
406
|
-
customer: {
|
|
407
|
-
type: "object",
|
|
408
|
-
properties: { email: { type: "string" } },
|
|
409
|
-
required: ["email"],
|
|
410
|
-
},
|
|
411
|
-
},
|
|
412
|
-
required: ["id", "total", "customer"],
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
const result = checker.canConnect(nodeAOutput, nodeBInput);
|
|
416
|
-
|
|
417
|
-
console.log(result.isSubset); // true ✅
|
|
418
|
-
console.log(result.direction); // "sourceOutput ⊆ targetInput"
|
|
419
|
-
console.log(result.diffs); // []
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
---
|
|
423
|
-
|
|
424
368
|
### `resolveConditions(schema, data)`
|
|
425
369
|
|
|
426
370
|
```ts
|
|
@@ -481,22 +425,32 @@ console.log(personal.resolved.required);
|
|
|
481
425
|
|
|
482
426
|
---
|
|
483
427
|
|
|
484
|
-
### `
|
|
428
|
+
### `check(sub, sup, options)`
|
|
485
429
|
|
|
486
430
|
```ts
|
|
487
|
-
|
|
488
|
-
sub:
|
|
489
|
-
sup:
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
431
|
+
check(
|
|
432
|
+
sub: JSONSchema7Definition,
|
|
433
|
+
sup: JSONSchema7Definition,
|
|
434
|
+
options: CheckConditionsOptions
|
|
435
|
+
): ResolvedSubsetResult
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
Résout les conditions `if/then/else` des deux schemas **puis** vérifie `sub ⊆ sup`. Utile quand le superset contient des `if/then/else` et que vous connaissez les valeurs discriminantes. Effectue aussi un **narrowing** du sub par rapport aux contraintes `enum`/`const` du sup en utilisant les données runtime.
|
|
439
|
+
|
|
440
|
+
```ts
|
|
441
|
+
interface CheckConditionsOptions {
|
|
442
|
+
/** Runtime data for the sub schema — used for condition resolution and enum narrowing */
|
|
443
|
+
subData: unknown;
|
|
444
|
+
/** Runtime data for the sup schema (defaults to subData) — used for condition resolution and enum narrowing */
|
|
445
|
+
supData?: unknown;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
interface ResolvedSubsetResult extends SubsetResult {
|
|
493
449
|
resolvedSub: ResolvedConditionResult;
|
|
494
450
|
resolvedSup: ResolvedConditionResult;
|
|
495
451
|
}
|
|
496
452
|
```
|
|
497
453
|
|
|
498
|
-
Raccourci : résout les conditions des deux schemas **puis** vérifie `sub ⊆ sup`. Utile quand le superset contient des `if/then/else` et que vous connaissez les valeurs discriminantes.
|
|
499
|
-
|
|
500
454
|
```ts
|
|
501
455
|
const conditionalSup = {
|
|
502
456
|
type: "object",
|
|
@@ -529,10 +483,16 @@ const sub = {
|
|
|
529
483
|
// Sans résolution : false (le if/then/else brut ne matche pas)
|
|
530
484
|
console.log(checker.isSubset(sub, conditionalSup)); // false
|
|
531
485
|
|
|
532
|
-
// Avec résolution : true !
|
|
533
|
-
const result = checker.
|
|
486
|
+
// Avec résolution via options : true !
|
|
487
|
+
const result = checker.check(sub, conditionalSup, { subData: { kind: "text" } });
|
|
534
488
|
console.log(result.isSubset); // true ✅
|
|
535
489
|
console.log(result.resolvedSup.branch); // "then"
|
|
490
|
+
|
|
491
|
+
// Avec des données différentes pour sub et sup
|
|
492
|
+
const result2 = checker.check(sub, conditionalSup, {
|
|
493
|
+
subData: { kind: "text" },
|
|
494
|
+
supData: { kind: "text" },
|
|
495
|
+
});
|
|
536
496
|
```
|
|
537
497
|
|
|
538
498
|
---
|
|
@@ -581,9 +541,8 @@ const result = checker.check(
|
|
|
581
541
|
|
|
582
542
|
console.log(checker.formatResult("range check", result));
|
|
583
543
|
// ❌ range check: false
|
|
584
|
-
//
|
|
585
|
-
//
|
|
586
|
-
// ~ maximum: 100 → 10
|
|
544
|
+
// Errors:
|
|
545
|
+
// ✗ $root: expected minimum 5, received minimum 0
|
|
587
546
|
```
|
|
588
547
|
|
|
589
548
|
```ts
|
|
@@ -596,10 +555,10 @@ console.log(checker.formatResult("strict ⊆ loose", result2));
|
|
|
596
555
|
// ✅ strict ⊆ loose: true
|
|
597
556
|
```
|
|
598
557
|
|
|
599
|
-
|
|
600
|
-
-
|
|
601
|
-
-
|
|
602
|
-
-
|
|
558
|
+
Format de sortie :
|
|
559
|
+
- `✅` — le check a réussi (`isSubset: true`)
|
|
560
|
+
- `❌` — le check a échoué (`isSubset: false`), suivi de la liste des erreurs
|
|
561
|
+
- `✗ key: expected X, received Y` — détail de chaque erreur sémantique
|
|
603
562
|
|
|
604
563
|
---
|
|
605
564
|
|
|
@@ -664,11 +623,8 @@ checker.isSubset(loose, strict); // false
|
|
|
664
623
|
|
|
665
624
|
// Le diagnostic montre exactement ce qui manque
|
|
666
625
|
const result = checker.check(loose, strict);
|
|
667
|
-
console.log(result.
|
|
668
|
-
// [
|
|
669
|
-
// { path: "required", type: "changed", expected: ["name"], actual: ["name", "age"] },
|
|
670
|
-
// { path: "properties.age", type: "added", expected: undefined, actual: { type: "number" } }
|
|
671
|
-
// ]
|
|
626
|
+
console.log(result.errors);
|
|
627
|
+
// [{ key: "age", expected: "number", received: "undefined" }]
|
|
672
628
|
```
|
|
673
629
|
|
|
674
630
|
---
|
|
@@ -856,8 +812,8 @@ checker.isSubset(open, closed); // false
|
|
|
856
812
|
|
|
857
813
|
// Le diagnostic montre la contrainte
|
|
858
814
|
const result = checker.check(open, closed);
|
|
859
|
-
|
|
860
|
-
|
|
815
|
+
console.log(result.errors);
|
|
816
|
+
// [{ key: "age", expected: "not allowed (additionalProperties: false)", received: "number" }]
|
|
861
817
|
```
|
|
862
818
|
|
|
863
819
|
---
|
|
@@ -910,19 +866,17 @@ const shallow = {
|
|
|
910
866
|
checker.isSubset(deep, shallow); // true
|
|
911
867
|
checker.isSubset(shallow, deep); // false
|
|
912
868
|
|
|
913
|
-
// Les
|
|
869
|
+
// Les erreurs montrent les propriétés manquantes avec un chemin complet
|
|
914
870
|
const result = checker.check(shallow, deep);
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
);
|
|
918
|
-
console.log(bioDiff?.type); // "added"
|
|
871
|
+
console.log(result.errors);
|
|
872
|
+
// [{ key: "user.profile.bio", expected: "string", received: "undefined" }]
|
|
919
873
|
```
|
|
920
874
|
|
|
921
875
|
---
|
|
922
876
|
|
|
923
877
|
### 9. `anyOf` / `oneOf`
|
|
924
878
|
|
|
925
|
-
La librairie supporte `anyOf` et `oneOf`
|
|
879
|
+
La librairie supporte `anyOf` et `oneOf` pour la vérification de sous-ensemble.
|
|
926
880
|
|
|
927
881
|
#### anyOf
|
|
928
882
|
|
|
@@ -953,16 +907,16 @@ checker.isSubset(
|
|
|
953
907
|
|
|
954
908
|
#### oneOf
|
|
955
909
|
|
|
956
|
-
Le `oneOf` est traité comme `anyOf` pour la vérification de sous-ensemble (chaque branche doit être acceptée).
|
|
910
|
+
Le `oneOf` est traité comme `anyOf` pour la vérification de sous-ensemble (chaque branche doit être acceptée).
|
|
957
911
|
|
|
958
912
|
```ts
|
|
959
|
-
// Les chemins de diff utilisent le bon label
|
|
960
913
|
const result = checker.check(
|
|
961
914
|
{ oneOf: [{ type: "string" }, { type: "number" }, { type: "boolean" }] },
|
|
962
915
|
{ oneOf: [{ type: "string" }, { type: "number" }] }
|
|
963
916
|
);
|
|
964
917
|
|
|
965
|
-
result.
|
|
918
|
+
console.log(result.isSubset); // false
|
|
919
|
+
console.log(result.errors); // erreurs pour la branche non couverte
|
|
966
920
|
```
|
|
967
921
|
|
|
968
922
|
#### Unions discriminées
|
|
@@ -1636,13 +1590,12 @@ const nodeBInput = {
|
|
|
1636
1590
|
required: ["items"],
|
|
1637
1591
|
};
|
|
1638
1592
|
|
|
1639
|
-
const
|
|
1640
|
-
console.log(
|
|
1641
|
-
console.log(connection.direction); // "sourceOutput ⊆ targetInput"
|
|
1593
|
+
const result = checker.check(nodeAOutput, nodeBInput);
|
|
1594
|
+
console.log(result.isSubset); // true ✅
|
|
1642
1595
|
|
|
1643
1596
|
// Si incompatible, le diagnostic explique pourquoi
|
|
1644
|
-
if (!
|
|
1645
|
-
console.log(checker.formatResult("NodeA → NodeB",
|
|
1597
|
+
if (!result.isSubset) {
|
|
1598
|
+
console.log(checker.formatResult("NodeA → NodeB", result));
|
|
1646
1599
|
}
|
|
1647
1600
|
```
|
|
1648
1601
|
|
|
@@ -1706,7 +1659,7 @@ const consumerExpects = {
|
|
|
1706
1659
|
required: ["data"],
|
|
1707
1660
|
};
|
|
1708
1661
|
|
|
1709
|
-
const result = checker.
|
|
1662
|
+
const result = checker.check(apiResponse, consumerExpects);
|
|
1710
1663
|
console.log(result.isSubset); // true ✅
|
|
1711
1664
|
// L'API retourne plus de données que ce que le consommateur attend,
|
|
1712
1665
|
// mais TOUTES les données requises sont présentes et du bon type.
|
|
@@ -1796,8 +1749,8 @@ const businessOutput = {
|
|
|
1796
1749
|
checker.isSubset(businessOutput, formSchema); // false ❌
|
|
1797
1750
|
|
|
1798
1751
|
// Avec résolution, le schéma conditionnel est aplati
|
|
1799
|
-
const result = checker.
|
|
1800
|
-
accountType: "business",
|
|
1752
|
+
const result = checker.check(businessOutput, formSchema, {
|
|
1753
|
+
subData: { accountType: "business" },
|
|
1801
1754
|
});
|
|
1802
1755
|
console.log(result.isSubset); // true ✅
|
|
1803
1756
|
console.log(result.resolvedSup.branch); // "then"
|
|
@@ -1815,8 +1768,8 @@ const personalOutput = {
|
|
|
1815
1768
|
additionalProperties: false,
|
|
1816
1769
|
};
|
|
1817
1770
|
|
|
1818
|
-
const personalResult = checker.
|
|
1819
|
-
accountType: "personal",
|
|
1771
|
+
const personalResult = checker.check(personalOutput, formSchema, {
|
|
1772
|
+
subData: { accountType: "personal" },
|
|
1820
1773
|
});
|
|
1821
1774
|
console.log(personalResult.isSubset); // true ✅
|
|
1822
1775
|
console.log(personalResult.resolvedSup.branch); // "else"
|
|
@@ -1829,26 +1782,23 @@ console.log(personalResult.resolvedSup.branch); // "else"
|
|
|
1829
1782
|
```ts
|
|
1830
1783
|
import type {
|
|
1831
1784
|
SubsetResult,
|
|
1832
|
-
|
|
1785
|
+
SchemaError,
|
|
1833
1786
|
ResolvedConditionResult,
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
BranchResult,
|
|
1787
|
+
ResolvedSubsetResult,
|
|
1788
|
+
CheckConditionsOptions,
|
|
1837
1789
|
} from "json-schema-compatibility-checker";
|
|
1838
1790
|
```
|
|
1839
1791
|
|
|
1840
|
-
### `
|
|
1792
|
+
### `SchemaError`
|
|
1841
1793
|
|
|
1842
1794
|
```ts
|
|
1843
|
-
interface
|
|
1844
|
-
/** Chemin
|
|
1845
|
-
|
|
1846
|
-
/** Type
|
|
1847
|
-
|
|
1848
|
-
/**
|
|
1849
|
-
|
|
1850
|
-
/** Valeur dans le schema mergé (intersection) */
|
|
1851
|
-
actual: unknown;
|
|
1795
|
+
interface SchemaError {
|
|
1796
|
+
/** Chemin normalisé vers la propriété concernée (ex: "user.name", "users[].name", "accountId") */
|
|
1797
|
+
key: string;
|
|
1798
|
+
/** Type ou valeur attendu(e) par le schema cible (sup) */
|
|
1799
|
+
expected: string;
|
|
1800
|
+
/** Type ou valeur reçu(e) depuis le schema source (sub) */
|
|
1801
|
+
received: string;
|
|
1852
1802
|
}
|
|
1853
1803
|
```
|
|
1854
1804
|
|
|
@@ -1860,17 +1810,8 @@ interface SubsetResult {
|
|
|
1860
1810
|
isSubset: boolean;
|
|
1861
1811
|
/** Le schema résultant de l'intersection allOf(sub, sup), ou null si incompatible */
|
|
1862
1812
|
merged: JSONSchema7Definition | null;
|
|
1863
|
-
/**
|
|
1864
|
-
|
|
1865
|
-
}
|
|
1866
|
-
```
|
|
1867
|
-
|
|
1868
|
-
### `ConnectionResult`
|
|
1869
|
-
|
|
1870
|
-
```ts
|
|
1871
|
-
interface ConnectionResult extends SubsetResult {
|
|
1872
|
-
/** Direction lisible du check */
|
|
1873
|
-
direction: string;
|
|
1813
|
+
/** Erreurs sémantiques décrivant les incompatibilités entre les deux schemas */
|
|
1814
|
+
errors: SchemaError[];
|
|
1874
1815
|
}
|
|
1875
1816
|
```
|
|
1876
1817
|
|
|
@@ -1887,6 +1828,28 @@ interface ResolvedConditionResult {
|
|
|
1887
1828
|
}
|
|
1888
1829
|
```
|
|
1889
1830
|
|
|
1831
|
+
### `ResolvedSubsetResult`
|
|
1832
|
+
|
|
1833
|
+
```ts
|
|
1834
|
+
interface ResolvedSubsetResult extends SubsetResult {
|
|
1835
|
+
/** Résultat de résolution des conditions du sub */
|
|
1836
|
+
resolvedSub: ResolvedConditionResult;
|
|
1837
|
+
/** Résultat de résolution des conditions du sup */
|
|
1838
|
+
resolvedSup: ResolvedConditionResult;
|
|
1839
|
+
}
|
|
1840
|
+
```
|
|
1841
|
+
|
|
1842
|
+
### `CheckConditionsOptions`
|
|
1843
|
+
|
|
1844
|
+
```ts
|
|
1845
|
+
interface CheckConditionsOptions {
|
|
1846
|
+
/** Runtime data for the sub schema — used for condition resolution and enum narrowing */
|
|
1847
|
+
subData: unknown;
|
|
1848
|
+
/** Runtime data for the sup schema (defaults to subData) — used for condition resolution and enum narrowing */
|
|
1849
|
+
supData?: unknown;
|
|
1850
|
+
}
|
|
1851
|
+
```
|
|
1852
|
+
|
|
1890
1853
|
---
|
|
1891
1854
|
|
|
1892
1855
|
## Limitations connues
|
|
@@ -1929,7 +1892,7 @@ La comparaison de patterns regex utilise un **échantillonnage** (200 samples pa
|
|
|
1929
1892
|
|
|
1930
1893
|
### 4. `if/then/else` — nécessite des données discriminantes
|
|
1931
1894
|
|
|
1932
|
-
Les schemas avec `if/then/else` ne peuvent pas être comparés directement via `isSubset` car le merge brut ajoute les mots-clés conditionnels. Il faut utiliser `
|
|
1895
|
+
Les schemas avec `if/then/else` ne peuvent pas être comparés directement via `isSubset` car le merge brut ajoute les mots-clés conditionnels. Il faut utiliser `check(sub, sup, { subData })` avec les données discriminantes.
|
|
1933
1896
|
|
|
1934
1897
|
### 5. `$ref` — non supporté
|
|
1935
1898
|
|
|
@@ -1970,9 +1933,9 @@ La librairie est organisée en modules spécialisés, orchestrés par la façade
|
|
|
1970
1933
|
│ └──────────────┘ │ - stripNotFromSup │ │
|
|
1971
1934
|
│ │ - stripPatternFromSup │ │
|
|
1972
1935
|
│ ┌──────────────┐ └──────────────────────────┘ │
|
|
1973
|
-
│ │
|
|
1936
|
+
│ │Semantic Errors│ │
|
|
1974
1937
|
│ │ │ ┌──────────────────────────┐ │
|
|
1975
|
-
│ │
|
|
1938
|
+
│ │-computeErrors│ │ Pattern Subset │ │
|
|
1976
1939
|
│ │ - Recurse │ │ │ │
|
|
1977
1940
|
│ │ - Properties │ │ - isPatternSubset │ │
|
|
1978
1941
|
│ └──────────────┘ │ - arePatternsEquivalent │ │
|
|
@@ -1981,11 +1944,17 @@ La librairie est organisée en modules spécialisés, orchestrés par la façade
|
|
|
1981
1944
|
│ │ Formatter │ │
|
|
1982
1945
|
│ │ │ ┌──────────────────────────┐ │
|
|
1983
1946
|
│ │ - formatResult│ │ Format Validator │ │
|
|
1984
|
-
│ │ -
|
|
1947
|
+
│ │ - Error lines│ │ │ │
|
|
1985
1948
|
│ └──────────────┘ │ - validateFormat │ │
|
|
1986
1949
|
│ │ - isFormatSubset │ │
|
|
1987
|
-
│
|
|
1988
|
-
│
|
|
1950
|
+
│ ┌──────────────┐ │ - Format hierarchy │ │
|
|
1951
|
+
│ │Data Narrowing │ └──────────────────────────┘ │
|
|
1952
|
+
│ │ │ │
|
|
1953
|
+
│ │-narrowSchema │ │
|
|
1954
|
+
│ │ WithData │ │
|
|
1955
|
+
│ │ - enum match │ │
|
|
1956
|
+
│ │ - Recurse │ │
|
|
1957
|
+
│ └──────────────┘ │
|
|
1989
1958
|
└──────────────────────────────────────────────────┘
|
|
1990
1959
|
```
|
|
1991
1960
|
|
|
@@ -2010,7 +1979,6 @@ La librairie est organisée en modules spécialisés, orchestrés par la façade
|
|
|
2010
1979
|
| Package | Usage |
|
|
2011
1980
|
|---|---|
|
|
2012
1981
|
| `@x0k/json-schema-merge` | Merge engine pour `allOf` resolution |
|
|
2013
|
-
| `lodash` | Utilitaires (isEqual, mapValues, union, etc.) |
|
|
2014
1982
|
| `class-validator` | Validation des formats (email, URL, UUID, etc.) |
|
|
2015
1983
|
| `randexp` | Génération de strings pour le sampling de patterns |
|
|
2016
1984
|
|
|
@@ -2018,4 +1986,4 @@ La librairie est organisée en modules spécialisés, orchestrés par la façade
|
|
|
2018
1986
|
|
|
2019
1987
|
## Licence
|
|
2020
1988
|
|
|
2021
|
-
|
|
1989
|
+
MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { JSONSchema7 } from "json-schema";
|
|
2
|
-
import type { MergeEngine } from "./merge-engine";
|
|
3
|
-
import type { ResolvedConditionResult } from "./types";
|
|
2
|
+
import type { MergeEngine } from "./merge-engine.js";
|
|
3
|
+
import type { ResolvedConditionResult } from "./types.js";
|
|
4
4
|
/**
|
|
5
5
|
* Résout les `if/then/else` d'un schema en évaluant le `if` contre
|
|
6
6
|
* des données partielles (discriminants).
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"resolveConditions",{enumerable:true,get:function(){return resolveConditions}});const _formatvalidator=require("./format-validator.js");const _normalizer=require("./normalizer.js");const _utils=require("./utils.js");const SPECIAL_MERGE_KEYS=new Set(["required","properties","dependencies"]);const SUB_SCHEMA_KEYS=new Set(["additionalProperties","items","contains","propertyNames","not"]);const MIN_KEYS=new Set(["minimum","exclusiveMinimum","minLength","minItems","minProperties"]);const MAX_KEYS=new Set(["maximum","exclusiveMaximum","maxLength","maxItems","maxProperties"]);function matchesType(value,type){if(type===undefined)return true;const types=Array.isArray(type)?type:[type];const actualType=(0,_normalizer.inferType)(value);return types.some(t=>t===actualType||t==="number"&&actualType==="integer")}function evaluateNumericConstraints(value,prop){if(prop.minimum!==undefined&&!(value>=prop.minimum))return false;if(prop.maximum!==undefined&&!(value<=prop.maximum))return false;if(prop.exclusiveMinimum!==undefined&&!(value>prop.exclusiveMinimum))return false;if(prop.exclusiveMaximum!==undefined&&!(value<prop.exclusiveMaximum))return false;if(prop.multipleOf!==undefined&&value%prop.multipleOf!==0)return false;return true}const patternRegexCache=new Map;function getOrCompileRegex(pattern){let regex=patternRegexCache.get(pattern);if(regex===undefined){regex=new RegExp(pattern);patternRegexCache.set(pattern,regex)}return regex}function evaluateStringConstraints(value,prop){if(prop.minLength!==undefined&&!(value.length>=prop.minLength))return false;if(prop.maxLength!==undefined&&!(value.length<=prop.maxLength))return false;if(prop.pattern!==undefined&&!getOrCompileRegex(prop.pattern).test(value))return false;return true}function evaluateArrayConstraints(value,prop){if(prop.minItems!==undefined&&!(value.length>=prop.minItems))return false;if(prop.maxItems!==undefined&&!(value.length<=prop.maxItems))return false;if(prop.uniqueItems===true){const len=value.length;for(let i=0;i<len;i++){for(let j=i+1;j<len;j++){if((0,_utils.deepEqual)(value[i],value[j]))return false}}}return true}function evaluateCondition(ifSchema,data){if((0,_utils.isPlainObj)(ifSchema.properties)){const propsOk=Object.keys(ifSchema.properties).every(key=>{const propDef=ifSchema.properties?.[key];if(typeof propDef==="boolean")return true;const prop=propDef;const value=data[key];if(value===undefined)return true;if((0,_utils.hasOwn)(prop,"const")){if(!(0,_utils.deepEqual)(value,prop.const))return false}if((0,_utils.hasOwn)(prop,"enum")){if(!prop.enum?.some(v=>(0,_utils.deepEqual)(v,value)))return false}if((0,_utils.hasOwn)(prop,"type")&&value!==undefined){if(!matchesType(value,prop.type))return false}if(typeof value==="number"){if(!evaluateNumericConstraints(value,prop))return false}if(typeof value==="string"){if(!evaluateStringConstraints(value,prop))return false}if(Array.isArray(value)){if(!evaluateArrayConstraints(value,prop))return false}if(prop.format!==undefined&&typeof value==="string"){const formatResult=(0,_formatvalidator.validateFormat)(value,prop.format);if(formatResult===false)return false}if((0,_utils.isPlainObj)(prop.properties)||Array.isArray(prop.required)){if((0,_utils.isPlainObj)(value)){if(!evaluateCondition(prop,value)){return false}}}return true});if(!propsOk)return false}if(Array.isArray(ifSchema.required)){const allRequired=ifSchema.required.every(key=>(0,_utils.hasOwn)(data,key));if(!allRequired)return false}if(Array.isArray(ifSchema.allOf)){const allMatch=ifSchema.allOf.every(entry=>{if(typeof entry==="boolean")return entry;return evaluateCondition(entry,data)});if(!allMatch)return false}if(Array.isArray(ifSchema.anyOf)){const anyMatch=ifSchema.anyOf.some(entry=>{if(typeof entry==="boolean")return entry;return evaluateCondition(entry,data)});if(!anyMatch)return false}if(Array.isArray(ifSchema.oneOf)){let matchCount=0;for(const entry of ifSchema.oneOf){const matches=typeof entry==="boolean"?entry:evaluateCondition(entry,data);if(matches)matchCount++;if(matchCount>1)break}if(matchCount!==1)return false}if((0,_utils.hasOwn)(ifSchema,"not")&&(0,_utils.isPlainObj)(ifSchema.not)&&typeof ifSchema.not!=="boolean"){const notResult=evaluateCondition(ifSchema.not,data);if(notResult)return false}return true}const DISCRIMINANT_INDICATORS=["const","enum","minimum","maximum","exclusiveMinimum","exclusiveMaximum","pattern","minLength","maxLength","multipleOf","minItems","maxItems","format"];function extractDiscriminants(ifSchema,data,out){if(!(0,_utils.isPlainObj)(ifSchema.properties))return;const props=ifSchema.properties;for(const key of Object.keys(props)){const propDef=props[key];if(typeof propDef==="boolean")continue;const prop=propDef;const hasIndicator=DISCRIMINANT_INDICATORS.some(indicator=>(0,_utils.hasOwn)(prop,indicator));if(hasIndicator&&(0,_utils.hasOwn)(data,key)){out[key]=data[key]}}}function mergeBranchInto(resolved,branchDef,engine){if(typeof branchDef==="boolean")return;const branchSchema=branchDef;if(Array.isArray(branchSchema.required)){resolved.required=(0,_utils.unionStrings)(resolved.required??[],branchSchema.required)}if((0,_utils.isPlainObj)(branchSchema.properties)){const branchProps=branchSchema.properties;const mergedProps={...resolved.properties??{}};for(const key of Object.keys(branchProps)){const branchProp=branchProps[key];if(branchProp===undefined)continue;const existing=resolved.properties?.[key];if(existing!==undefined&&typeof existing!=="boolean"&&typeof branchProp!=="boolean"){const merged=engine.merge(existing,branchProp);mergedProps[key]=merged??branchProp}else{mergedProps[key]=branchProp}}resolved.properties=mergedProps}if((0,_utils.isPlainObj)(branchSchema.dependencies)){const resolvedDeps=resolved.dependencies??{};const branchDeps=branchSchema.dependencies;const acc={...resolvedDeps};for(const depKey of Object.keys(branchDeps)){const branchVal=branchDeps[depKey];if(branchVal===undefined)continue;const existingVal=acc[depKey];if(existingVal===undefined){acc[depKey]=branchVal}else if(Array.isArray(existingVal)&&Array.isArray(branchVal)){acc[depKey]=(0,_utils.unionStrings)(existingVal,branchVal)}else if((0,_utils.isPlainObj)(existingVal)&&(0,_utils.isPlainObj)(branchVal)){const merged=engine.merge(existingVal,branchVal);acc[depKey]=merged??branchVal}else{acc[depKey]=branchVal}}resolved.dependencies=acc}for(const key of Object.keys(branchSchema)){if(SPECIAL_MERGE_KEYS.has(key))return;const branchVal=branchSchema[key];const resolvedVal=resolved[key];if(resolvedVal===undefined){resolved[key]=branchVal;return}if((0,_utils.deepEqual)(resolvedVal,branchVal))return;if(SUB_SCHEMA_KEYS.has(key)){const merged=engine.merge(resolvedVal,branchVal);if(merged!==null){resolved[key]=merged}else{resolved[key]=branchVal}return}if(MIN_KEYS.has(key)){if(typeof resolvedVal==="number"&&typeof branchVal==="number"){resolved[key]=Math.max(resolvedVal,branchVal)}else{resolved[key]=branchVal}return}if(MAX_KEYS.has(key)){if(typeof resolvedVal==="number"&&typeof branchVal==="number"){resolved[key]=Math.min(resolvedVal,branchVal)}else{resolved[key]=branchVal}return}if(key==="uniqueItems"){resolved[key]=resolvedVal===true||branchVal===true;return}if(key==="pattern"||key==="format"){resolved[key]=branchVal;return}const base={[key]:resolvedVal};const branch={[key]:branchVal};const merged=engine.merge(base,branch);if(merged&&typeof merged!=="boolean"&&(0,_utils.hasOwn)(merged,key)){resolved[key]=merged[key]}else{resolved[key]=branchVal}}}function resolveConditions(schema,data,engine){let branch=null;const discriminant={};const hasTopLevelIf=schema.if!==undefined;const hasAllOfConditions=Array.isArray(schema.allOf)&&schema.allOf.some(e=>typeof e!=="boolean"&&(0,_utils.hasOwn)(e,"if"));if(!hasTopLevelIf&&!hasAllOfConditions){const resolved=resolveNestedProperties(schema,data,engine,discriminant);return{resolved,branch,discriminant}}let resolved={...schema};if(hasAllOfConditions){resolved=resolveAllOfConditions(resolved,data,engine,discriminant)}if(resolved.if!==undefined){const ifSchema=resolved.if;const matches=evaluateCondition(ifSchema,data);extractDiscriminants(ifSchema,data,discriminant);const applicableBranch=matches?resolved.then:resolved.else;branch=matches?"then":"else";if(applicableBranch){mergeBranchInto(resolved,applicableBranch,engine)}delete resolved.if;delete resolved.then;delete resolved.else}resolved=resolveNestedProperties(resolved,data,engine,discriminant);return{resolved,branch,discriminant}}function resolveAllOfConditions(resolved,data,engine,discriminant){if(!Array.isArray(resolved.allOf))return resolved;const remainingAllOf=[];for(const entry of resolved.allOf){if(typeof entry==="boolean"){remainingAllOf.push(entry);continue}const subSchema=entry;if(subSchema.if===undefined){remainingAllOf.push(entry);continue}const ifSchema=subSchema.if;const matches=evaluateCondition(ifSchema,data);extractDiscriminants(ifSchema,data,discriminant);const applicableBranch=matches?subSchema.then:subSchema.else;if(applicableBranch){mergeBranchInto(resolved,applicableBranch,engine)}const remaining=(0,_utils.omitKeys)(subSchema,["if","then","else"]);if(Object.keys(remaining).length>0){remainingAllOf.push(remaining)}}resolved={...resolved};if(remainingAllOf.length===0){delete resolved.allOf}else{resolved.allOf=remainingAllOf}return resolved}function resolveNestedProperties(resolved,data,engine,discriminant){if(!(0,_utils.isPlainObj)(resolved.properties))return resolved;const props=resolved.properties;const propKeys=Object.keys(props);let changed=false;const resolvedProps={};for(const key of propKeys){const propDef=props[key];if(propDef===undefined)continue;if(typeof propDef==="boolean"){resolvedProps[key]=propDef;continue}const propSchema=propDef;const hasConditions=propSchema.if!==undefined||Array.isArray(propSchema.allOf)&&propSchema.allOf.some(e=>typeof e!=="boolean"&&(0,_utils.hasOwn)(e,"if"));if(!hasConditions){resolvedProps[key]=propDef;continue}const nestedData=(0,_utils.isPlainObj)(data[key])?data[key]:{};const nested=resolveConditions(propSchema,nestedData,engine);for(const dk of Object.keys(nested.discriminant)){discriminant[`${key}.${dk}`]=nested.discriminant[dk]}resolvedProps[key]=nested.resolved;changed=true}return changed?{...resolved,properties:resolvedProps}:resolved}
|
|
2
|
+
//# sourceMappingURL=condition-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/condition-resolver.ts"],"sourcesContent":["import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\nimport { validateFormat } from \"./format-validator\";\nimport type { MergeEngine } from \"./merge-engine\";\nimport { inferType } from \"./normalizer\";\nimport type { ResolvedConditionResult } from \"./types\";\nimport { deepEqual, hasOwn, isPlainObj, omitKeys, unionStrings } from \"./utils\";\n\n// ─── Condition Resolver ──────────────────────────────────────────────────────\n//\n// Résout les `if/then/else` d'un schema en évaluant le `if` contre\n// des données partielles (discriminants).\n//\n// Stratégie :\n// 1. Évaluer si les données partielles satisfont le `if`\n// 2. Merger la branche applicable (`then` ou `else`) dans le schema de base\n// 3. Supprimer les mots-clés `if/then/else` du résultat\n// 4. Récurser dans les `properties` pour résoudre les conditions imbriquées\n//\n// L'évaluation du `if` (via `evaluateCondition`) gère :\n// - `properties` avec `const`, `enum`, `type`, contraintes numériques/string/array\n// - `required` (vérification de présence des clés)\n// - `allOf` (toutes les entrées doivent matcher — récursion) [2.1]\n// - `anyOf` (au moins une entrée doit matcher — récursion) [2.2]\n// - `oneOf` (exactement une entrée doit matcher — récursion) [2.3]\n// - `not` (inversion du résultat — récursion) [2.4]\n// - Propriétés imbriquées (nested objects — récursion) [2.5]\n// - `format` via `validateFormat` de `format-validator.ts` [2.6]\n//\n// Uses custom utilities from `utils.ts`:\n// - `hasOwn` / `isPlainObj` for safe property access and type checks\n// - `deepEqual` for deep structural comparison\n// - `unionStrings` for merging string arrays (required, deps)\n// - `omitKeys` for excluding keys from objects\n\n// ─── Keywords classification ─────────────────────────────────────────────────\n\n/** Mots-clés qui ne doivent pas être traités par la boucle générique de mergeBranchInto */\nconst SPECIAL_MERGE_KEYS = new Set([\"required\", \"properties\", \"dependencies\"]);\n\n/** Mots-clés contenant un sous-schema unique (mergeable via engine.merge) */\nconst SUB_SCHEMA_KEYS = new Set([\n\t\"additionalProperties\",\n\t\"items\",\n\t\"contains\",\n\t\"propertyNames\",\n\t\"not\",\n]);\n\n/** Mots-clés numériques de type \"minimum\" (prendre le max pour être plus restrictif) */\nconst MIN_KEYS = new Set([\n\t\"minimum\",\n\t\"exclusiveMinimum\",\n\t\"minLength\",\n\t\"minItems\",\n\t\"minProperties\",\n]);\n\n/** Mots-clés numériques de type \"maximum\" (prendre le min pour être plus restrictif) */\nconst MAX_KEYS = new Set([\n\t\"maximum\",\n\t\"exclusiveMaximum\",\n\t\"maxLength\",\n\t\"maxItems\",\n\t\"maxProperties\",\n]);\n\n// ─── Condition evaluation (internal) ─────────────────────────────────────────\n\n/**\n * Vérifie si `value` correspond à un type JSON Schema.\n */\nfunction matchesType(value: unknown, type: JSONSchema7[\"type\"]): boolean {\n\tif (type === undefined) return true;\n\n\tconst types = Array.isArray(type) ? type : [type];\n\tconst actualType = inferType(value);\n\n\treturn types.some(\n\t\t(t) => t === actualType || (t === \"number\" && actualType === \"integer\"),\n\t);\n}\n\n/**\n * Évalue une contrainte numérique sur une valeur.\n * Point 5 — Enrichissement de evaluateCondition.\n */\nfunction evaluateNumericConstraints(value: number, prop: JSONSchema7): boolean {\n\tif (prop.minimum !== undefined && !(value >= prop.minimum)) return false;\n\tif (prop.maximum !== undefined && !(value <= prop.maximum)) return false;\n\tif (\n\t\tprop.exclusiveMinimum !== undefined &&\n\t\t!(value > (prop.exclusiveMinimum as number))\n\t)\n\t\treturn false;\n\tif (\n\t\tprop.exclusiveMaximum !== undefined &&\n\t\t!(value < (prop.exclusiveMaximum as number))\n\t)\n\t\treturn false;\n\tif (prop.multipleOf !== undefined && value % prop.multipleOf !== 0)\n\t\treturn false;\n\treturn true;\n}\n\n/**\n * Évalue une contrainte string sur une valeur.\n * Point 5 — Enrichissement de evaluateCondition.\n */\n/** Cache for compiled RegExp patterns used in evaluateStringConstraints */\nconst patternRegexCache = new Map<string, RegExp>();\n\nfunction getOrCompileRegex(pattern: string): RegExp {\n\tlet regex = patternRegexCache.get(pattern);\n\tif (regex === undefined) {\n\t\tregex = new RegExp(pattern);\n\t\tpatternRegexCache.set(pattern, regex);\n\t}\n\treturn regex;\n}\n\nfunction evaluateStringConstraints(value: string, prop: JSONSchema7): boolean {\n\tif (prop.minLength !== undefined && !(value.length >= prop.minLength))\n\t\treturn false;\n\tif (prop.maxLength !== undefined && !(value.length <= prop.maxLength))\n\t\treturn false;\n\tif (\n\t\tprop.pattern !== undefined &&\n\t\t!getOrCompileRegex(prop.pattern).test(value)\n\t)\n\t\treturn false;\n\treturn true;\n}\n\n/**\n * Évalue une contrainte array sur une valeur.\n * Point 5 — Enrichissement de evaluateCondition.\n */\nfunction evaluateArrayConstraints(\n\tvalue: unknown[],\n\tprop: JSONSchema7,\n): boolean {\n\tif (prop.minItems !== undefined && !(value.length >= prop.minItems))\n\t\treturn false;\n\tif (prop.maxItems !== undefined && !(value.length <= prop.maxItems))\n\t\treturn false;\n\tif (prop.uniqueItems === true) {\n\t\t// Vérifier l'unicité via deepEqual pour les éléments non-primitifs\n\t\t// Optimisation : double boucle sans slice pour éviter les allocations\n\t\tconst len = value.length;\n\t\tfor (let i = 0; i < len; i++) {\n\t\t\tfor (let j = i + 1; j < len; j++) {\n\t\t\t\tif (deepEqual(value[i], value[j])) return false;\n\t\t\t}\n\t\t}\n\t}\n\treturn true;\n}\n\n/**\n * Évalue si des données partielles satisfont un `if` schema.\n *\n * Stratégie pragmatique (pas un validateur complet) :\n * - Vérifie les `properties` avec `const`, `enum`, `type`\n * - Point 5 : Vérifie aussi minimum/maximum, minLength/maxLength,\n * pattern, multipleOf, minItems/maxItems, uniqueItems\n * - Vérifie les `required`\n * - 2.1 : `allOf` → toutes les entrées doivent matcher (récursion)\n * - 2.2 : `anyOf` → au moins une entrée doit matcher (récursion)\n * - 2.3 : `oneOf` → exactement une entrée doit matcher (récursion)\n * - 2.4 : `not` → inversion du résultat (récursion)\n * - 2.5 : Propriétés imbriquées → récursion sur les sous-objets\n * - 2.6 : `format` → validation via `validateFormat`\n *\n * Utilise `_.forEach` / `_.every` / `_.has` pour une itération idiomatique.\n */\nfunction evaluateCondition(\n\tifSchema: JSONSchema7,\n\tdata: Record<string, unknown>,\n): boolean {\n\tif (isPlainObj(ifSchema.properties)) {\n\t\tconst propsOk = Object.keys(ifSchema.properties).every((key) => {\n\t\t\tconst propDef = ifSchema.properties?.[key];\n\t\t\tif (typeof propDef === \"boolean\") return true;\n\t\t\tconst prop = propDef as JSONSchema7;\n\t\t\tconst value = data[key];\n\n\t\t\t// ── Propriété absente → skip ──\n\t\t\t// Selon la spec JSON Schema Draft-07, le keyword `properties` ne valide\n\t\t\t// une propriété que si elle est **présente** dans l'instance.\n\t\t\t// C'est le keyword `required` qui gère la présence obligatoire.\n\t\t\tif (value === undefined) return true;\n\n\t\t\t// ── const ──\n\t\t\tif (hasOwn(prop, \"const\")) {\n\t\t\t\tif (!deepEqual(value, prop.const)) return false;\n\t\t\t}\n\n\t\t\t// ── enum ──\n\t\t\tif (hasOwn(prop, \"enum\")) {\n\t\t\t\tif (!prop.enum?.some((v) => deepEqual(v, value))) return false;\n\t\t\t}\n\n\t\t\t// ── type ──\n\t\t\tif (hasOwn(prop, \"type\") && value !== undefined) {\n\t\t\t\tif (!matchesType(value, prop.type)) return false;\n\t\t\t}\n\n\t\t\t// ── Point 5 : Contraintes numériques/string/array ──\n\t\t\t// Quand `value` est `undefined`, aucun de ces blocs ne s'exécute\n\t\t\t// (`typeof undefined` vaut `\"undefined\"`, pas `\"number\"` ni `\"string\"`,\n\t\t\t// et `isArray(undefined)` retourne `false`).\n\t\t\t// C'est le comportement voulu : on ne peut pas évaluer une contrainte\n\t\t\t// sur une donnée absente → on skip, cohérent avec la logique pragmatique.\n\t\t\tif (typeof value === \"number\") {\n\t\t\t\tif (!evaluateNumericConstraints(value, prop)) return false;\n\t\t\t}\n\n\t\t\tif (typeof value === \"string\") {\n\t\t\t\tif (!evaluateStringConstraints(value, prop)) return false;\n\t\t\t}\n\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tif (!evaluateArrayConstraints(value as unknown[], prop)) return false;\n\t\t\t}\n\n\t\t\t// ── 2.6 — format ──\n\t\t\t// Valide la valeur contre le format via class-validator.\n\t\t\t// Le format ne s'applique qu'aux strings en Draft-07.\n\t\t\t// Si le format est inconnu → skip (retourne null → on continue).\n\t\t\tif (prop.format !== undefined && typeof value === \"string\") {\n\t\t\t\tconst formatResult = validateFormat(value, prop.format);\n\t\t\t\tif (formatResult === false) return false;\n\t\t\t\t// null (format inconnu) → skip, cohérent avec l'approche pragmatique\n\t\t\t}\n\n\t\t\t// ── 2.5 — Propriétés imbriquées (nested objects) ──\n\t\t\t// Si la propriété elle-même a des `properties` ou un `required`,\n\t\t\t// et que la valeur dans data est un objet, récurser dans evaluateCondition\n\t\t\t// en passant la sous-donnée comme nouveau `data`.\n\t\t\t// Si data[key] n'est pas un objet, on skip (retourne true pour cette prop,\n\t\t\t// cohérent avec \"absence = pas de contrainte\").\n\t\t\tif (isPlainObj(prop.properties) || Array.isArray(prop.required)) {\n\t\t\t\tif (isPlainObj(value)) {\n\t\t\t\t\tif (!evaluateCondition(prop, value as Record<string, unknown>)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// value n'est pas un objet → skip, on ne peut pas évaluer les sous-props\n\t\t\t}\n\n\t\t\treturn true;\n\t\t});\n\t\tif (!propsOk) return false;\n\t}\n\n\t// ── required ──\n\tif (Array.isArray(ifSchema.required)) {\n\t\tconst allRequired = ifSchema.required.every((key) =>\n\t\t\thasOwn(data, key as string),\n\t\t);\n\t\tif (!allRequired) return false;\n\t}\n\n\t// ── 2.1 — allOf ──\n\t// Toutes les entrées du allOf doivent matcher (évaluation récursive).\n\tif (Array.isArray(ifSchema.allOf)) {\n\t\tconst allMatch = ifSchema.allOf.every((entry) => {\n\t\t\tif (typeof entry === \"boolean\") return entry;\n\t\t\treturn evaluateCondition(entry as JSONSchema7, data);\n\t\t});\n\t\tif (!allMatch) return false;\n\t}\n\n\t// ── 2.2 — anyOf ──\n\t// Au moins une entrée du anyOf doit matcher (évaluation récursive).\n\tif (Array.isArray(ifSchema.anyOf)) {\n\t\tconst anyMatch = ifSchema.anyOf.some((entry) => {\n\t\t\tif (typeof entry === \"boolean\") return entry;\n\t\t\treturn evaluateCondition(entry as JSONSchema7, data);\n\t\t});\n\t\tif (!anyMatch) return false;\n\t}\n\n\t// ── 2.3 — oneOf ──\n\t// Exactement une entrée du oneOf doit matcher (évaluation récursive).\n\tif (Array.isArray(ifSchema.oneOf)) {\n\t\tlet matchCount = 0;\n\t\tfor (const entry of ifSchema.oneOf) {\n\t\t\tconst matches =\n\t\t\t\ttypeof entry === \"boolean\"\n\t\t\t\t\t? entry\n\t\t\t\t\t: evaluateCondition(entry as JSONSchema7, data);\n\t\t\tif (matches) matchCount++;\n\t\t\tif (matchCount > 1) break;\n\t\t}\n\t\tif (matchCount !== 1) return false;\n\t}\n\n\t// ── 2.4 — not ──\n\t// Inverser le résultat de l'évaluation du contenu du `not`.\n\tif (\n\t\thasOwn(ifSchema, \"not\") &&\n\t\tisPlainObj(ifSchema.not) &&\n\t\ttypeof ifSchema.not !== \"boolean\"\n\t) {\n\t\tconst notResult = evaluateCondition(ifSchema.not as JSONSchema7, data);\n\t\tif (notResult) return false; // Le not matche → la condition not ne matche pas\n\t}\n\n\treturn true;\n}\n\n// ─── Discriminant extraction ─────────────────────────────────────────────────\n\n/**\n * Mots-clés qui indiquent qu'une propriété est un discriminant\n * (sa valeur dans les données est utilisée pour la résolution).\n *\n * Point 5 — Étendu avec les contraintes numériques/string/pattern.\n */\nconst DISCRIMINANT_INDICATORS = [\n\t\"const\",\n\t\"enum\",\n\t\"minimum\",\n\t\"maximum\",\n\t\"exclusiveMinimum\",\n\t\"exclusiveMaximum\",\n\t\"pattern\",\n\t\"minLength\",\n\t\"maxLength\",\n\t\"multipleOf\",\n\t\"minItems\",\n\t\"maxItems\",\n\t\"format\",\n] as const;\n\n/**\n * Extrait les valeurs discriminantes utilisées dans un `if` schema\n * depuis les données partielles.\n *\n * Point 5 — Collecte aussi les discriminants pour les nouvelles contraintes\n * (minimum, maximum, pattern, etc.).\n *\n * Utilise `_.some` pour vérifier qu'au moins un indicateur est présent,\n * et `_.has` pour un accès sûr.\n */\nfunction extractDiscriminants(\n\tifSchema: JSONSchema7,\n\tdata: Record<string, unknown>,\n\tout: Record<string, unknown>,\n): void {\n\tif (!isPlainObj(ifSchema.properties)) return;\n\n\tconst props = ifSchema.properties as Record<string, JSONSchema7Definition>;\n\tfor (const key of Object.keys(props)) {\n\t\tconst propDef = props[key];\n\t\tif (typeof propDef === \"boolean\") continue;\n\t\tconst prop = propDef as JSONSchema7;\n\n\t\t// Collecter si au moins un indicateur de discriminant est présent\n\t\tconst hasIndicator = DISCRIMINANT_INDICATORS.some((indicator) =>\n\t\t\thasOwn(prop, indicator),\n\t\t);\n\n\t\tif (hasIndicator && hasOwn(data, key)) {\n\t\t\tout[key] = data[key];\n\t\t}\n\t}\n}\n\n// ─── Branch merging (deduplicated) ───────────────────────────────────────────\n\n/**\n * Merge une branche conditionnelle (`then` ou `else`) dans le schema résolu.\n *\n * Point 4 — Fix first-writer-wins :\n * Au lieu d'ignorer les keywords déjà présents dans `resolved`,\n * on tente un merge intelligent selon le type de keyword :\n *\n * - `required` → union dédupliquée via `_.union`\n * - `properties` → merge individuel via engine.merge\n * - `dependencies` → Point 3 : union des tableaux (forme 1),\n * merge des schemas (forme 2) via `_.mapValues`\n * - Sub-schema keys → merge via engine.merge\n * - Min keys → `Math.max` (plus restrictif)\n * - Max keys → `Math.min` (plus restrictif)\n * - `uniqueItems` → `true` gagne sur `false`\n * - `pattern` / `format` → la branche gagne (plus spécifique)\n * - Autres → tentative de merge via engine, sinon la branche gagne\n *\n * Uses custom utilities from `utils.ts` for each merge operation.\n */\nfunction mergeBranchInto(\n\tresolved: JSONSchema7,\n\tbranchDef: JSONSchema7Definition,\n\tengine: MergeEngine,\n): void {\n\tif (typeof branchDef === \"boolean\") return;\n\n\tconst branchSchema = branchDef as JSONSchema7;\n\n\t// ── Merge required via unionStrings (deduplicated automatically) ──\n\tif (Array.isArray(branchSchema.required)) {\n\t\tresolved.required = unionStrings(\n\t\t\tresolved.required ?? [],\n\t\t\tbranchSchema.required,\n\t\t);\n\t}\n\n\t// ── Merger properties ──\n\tif (isPlainObj(branchSchema.properties)) {\n\t\tconst branchProps = branchSchema.properties as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition\n\t\t>;\n\t\tconst mergedProps: Record<string, JSONSchema7Definition> = {\n\t\t\t...(resolved.properties ?? {}),\n\t\t};\n\t\tfor (const key of Object.keys(branchProps)) {\n\t\t\tconst branchProp = branchProps[key];\n\t\t\tif (branchProp === undefined) continue;\n\t\t\tconst existing = resolved.properties?.[key];\n\t\t\tif (\n\t\t\t\texisting !== undefined &&\n\t\t\t\ttypeof existing !== \"boolean\" &&\n\t\t\t\ttypeof branchProp !== \"boolean\"\n\t\t\t) {\n\t\t\t\tconst merged = engine.merge(\n\t\t\t\t\texisting as JSONSchema7Definition,\n\t\t\t\t\tbranchProp as JSONSchema7Definition,\n\t\t\t\t);\n\t\t\t\tmergedProps[key] = (merged ?? branchProp) as JSONSchema7Definition;\n\t\t\t} else {\n\t\t\t\tmergedProps[key] = branchProp;\n\t\t\t}\n\t\t}\n\t\tresolved.properties = mergedProps;\n\t}\n\n\t// ── Merger dependencies (Point 3) ──\n\tif (isPlainObj(branchSchema.dependencies)) {\n\t\tconst resolvedDeps = (resolved.dependencies ?? {}) as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition | string[]\n\t\t>;\n\t\tconst branchDeps = branchSchema.dependencies as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition | string[]\n\t\t>;\n\n\t\tconst acc = { ...resolvedDeps };\n\t\tfor (const depKey of Object.keys(branchDeps)) {\n\t\t\tconst branchVal = branchDeps[depKey] as\n\t\t\t\t| JSONSchema7Definition\n\t\t\t\t| string[]\n\t\t\t\t| undefined;\n\t\t\tif (branchVal === undefined) continue;\n\t\t\tconst existingVal = acc[depKey] as\n\t\t\t\t| JSONSchema7Definition\n\t\t\t\t| string[]\n\t\t\t\t| undefined;\n\n\t\t\tif (existingVal === undefined) {\n\t\t\t\t// Pas de valeur existante → copier directement\n\t\t\t\tacc[depKey] = branchVal;\n\t\t\t} else if (Array.isArray(existingVal) && Array.isArray(branchVal)) {\n\t\t\t\t// Forme 1 : union dédupliquée des tableaux de strings\n\t\t\t\tacc[depKey] = unionStrings(\n\t\t\t\t\texistingVal as string[],\n\t\t\t\t\tbranchVal as string[],\n\t\t\t\t);\n\t\t\t} else if (isPlainObj(existingVal) && isPlainObj(branchVal)) {\n\t\t\t\t// Forme 2 : merge des sous-schemas\n\t\t\t\tconst merged = engine.merge(\n\t\t\t\t\texistingVal as JSONSchema7Definition,\n\t\t\t\t\tbranchVal as JSONSchema7Definition,\n\t\t\t\t);\n\t\t\t\tacc[depKey] = (merged ?? branchVal) as JSONSchema7Definition;\n\t\t\t} else {\n\t\t\t\t// Types incompatibles (tableau vs schema) → la branche gagne\n\t\t\t\tacc[depKey] = branchVal;\n\t\t\t}\n\t\t}\n\t\tresolved.dependencies = acc as Record<\n\t\t\tstring,\n\t\t\tJSONSchema7Definition | string[]\n\t\t>;\n\t}\n\n\t// ── Merger les autres mots-clés (Point 4 — fix first-writer-wins) ──\n\tfor (const key of Object.keys(branchSchema) as (keyof JSONSchema7)[]) {\n\t\t// Skip les clés déjà traitées ci-dessus\n\t\tif (SPECIAL_MERGE_KEYS.has(key)) return;\n\n\t\tconst branchVal = branchSchema[key];\n\t\tconst resolvedVal = resolved[key];\n\n\t\t// Si le resolved n'a pas cette clé → copier directement\n\t\tif (resolvedVal === undefined) {\n\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t\treturn;\n\t\t}\n\n\t\t// Si les deux ont la même valeur → rien à faire\n\t\tif (deepEqual(resolvedVal, branchVal)) return;\n\n\t\t// ── Sub-schema keys → merge via engine ──\n\t\tif (SUB_SCHEMA_KEYS.has(key)) {\n\t\t\tconst merged = engine.merge(\n\t\t\t\tresolvedVal as JSONSchema7Definition,\n\t\t\t\tbranchVal as JSONSchema7Definition,\n\t\t\t);\n\t\t\tif (merged !== null) {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = merged;\n\t\t\t} else {\n\t\t\t\t// Merge impossible → la branche gagne (contexte conditionnel)\n\t\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// ── Min keys → Math.max (plus restrictif) ──\n\t\tif (MIN_KEYS.has(key)) {\n\t\t\tif (typeof resolvedVal === \"number\" && typeof branchVal === \"number\") {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = Math.max(\n\t\t\t\t\tresolvedVal,\n\t\t\t\t\tbranchVal,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// ── Max keys → Math.min (plus restrictif) ──\n\t\tif (MAX_KEYS.has(key)) {\n\t\t\tif (typeof resolvedVal === \"number\" && typeof branchVal === \"number\") {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = Math.min(\n\t\t\t\t\tresolvedVal,\n\t\t\t\t\tbranchVal,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// ── uniqueItems → true gagne sur false ──\n\t\tif (key === \"uniqueItems\") {\n\t\t\t(resolved as Record<string, unknown>)[key] =\n\t\t\t\tresolvedVal === true || branchVal === true;\n\t\t\treturn;\n\t\t}\n\n\t\t// ── pattern / format → la branche gagne (plus spécifique au contexte) ──\n\t\tif (key === \"pattern\" || key === \"format\") {\n\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t\treturn;\n\t\t}\n\n\t\t// ── Fallback : tentative de merge via engine pour les cas restants ──\n\t\tconst base = { [key]: resolvedVal } as JSONSchema7Definition;\n\t\tconst branch = { [key]: branchVal } as JSONSchema7Definition;\n\t\tconst merged = engine.merge(base, branch);\n\t\tif (\n\t\t\tmerged &&\n\t\t\ttypeof merged !== \"boolean\" &&\n\t\t\thasOwn(merged as object, key)\n\t\t) {\n\t\t\t(resolved as Record<string, unknown>)[key] = (\n\t\t\t\tmerged as unknown as Record<string, unknown>\n\t\t\t)[key];\n\t\t} else {\n\t\t\t// Merge échoué → la branche gagne (contexte conditionnel applicable)\n\t\t\t(resolved as Record<string, unknown>)[key] = branchVal;\n\t\t}\n\t}\n}\n\n// ─── Public API ──────────────────────────────────────────────────────────────\n\n/**\n * Résout les `if/then/else` d'un schema en évaluant le `if` contre\n * des données partielles (discriminants).\n *\n * @param schema Le schema contenant potentiellement des if/then/else\n * @param data Données partielles utilisées pour évaluer les conditions\n * @param engine Le MergeEngine pour merger les branches\n *\n * @example\n * ```ts\n * const form = {\n * type: \"object\",\n * properties: { accountType: { type: \"string\" }, ... },\n * if: { properties: { accountType: { const: \"business\" } } },\n * then: { required: [\"companyName\"] },\n * else: { required: [\"firstName\"] },\n * };\n *\n * const { resolved } = resolveConditions(form, { accountType: \"business\" }, engine);\n * // → resolved n'a plus de if/then/else, mais a required: [\"companyName\"]\n * ```\n */\nexport function resolveConditions(\n\tschema: JSONSchema7,\n\tdata: Record<string, unknown>,\n\tengine: MergeEngine,\n): ResolvedConditionResult {\n\tlet branch: \"then\" | \"else\" | null = null;\n\tconst discriminant: Record<string, unknown> = {};\n\n\t// ── Fast path: no conditions at all ──\n\t// If there's no `if` and no `allOf` with conditions, skip the copy entirely.\n\tconst hasTopLevelIf = schema.if !== undefined;\n\tconst hasAllOfConditions =\n\t\tArray.isArray(schema.allOf) &&\n\t\tschema.allOf.some(\n\t\t\t(e) => typeof e !== \"boolean\" && hasOwn(e as object, \"if\"),\n\t\t);\n\n\tif (!hasTopLevelIf && !hasAllOfConditions) {\n\t\t// Phase 3 only: check nested properties (resolveNestedProperties\n\t\t// already returns the original if nothing changes)\n\t\tconst resolved = resolveNestedProperties(\n\t\t\tschema,\n\t\t\tdata,\n\t\t\tengine,\n\t\t\tdiscriminant,\n\t\t);\n\t\treturn { resolved, branch, discriminant };\n\t}\n\n\t// ── Copy-on-write: only copy when mutations are needed ──\n\tlet resolved = { ...schema };\n\n\t// ── Phase 1 : Résoudre les if/then/else dans allOf ──\n\tif (hasAllOfConditions) {\n\t\tresolved = resolveAllOfConditions(resolved, data, engine, discriminant);\n\t}\n\n\t// ── Phase 2 : Résoudre le if/then/else de ce niveau ──\n\tif (resolved.if !== undefined) {\n\t\tconst ifSchema = resolved.if as JSONSchema7;\n\t\tconst matches = evaluateCondition(ifSchema, data);\n\n\t\textractDiscriminants(ifSchema, data, discriminant);\n\n\t\tconst applicableBranch = matches ? resolved.then : resolved.else;\n\t\tbranch = matches ? \"then\" : \"else\";\n\n\t\tif (applicableBranch) {\n\t\t\tmergeBranchInto(\n\t\t\t\tresolved,\n\t\t\t\tapplicableBranch as JSONSchema7Definition,\n\t\t\t\tengine,\n\t\t\t);\n\t\t}\n\n\t\tdelete resolved.if;\n\t\tdelete resolved.then;\n\t\tdelete resolved.else;\n\t}\n\n\t// ── Phase 3 : Récurser dans les properties ──\n\tresolved = resolveNestedProperties(resolved, data, engine, discriminant);\n\n\treturn { resolved, branch, discriminant };\n}\n\n// ─── Internal phases ─────────────────────────────────────────────────────────\n\n/**\n * Phase 1 : Parcourt les entrées `allOf` et résout celles qui contiennent\n * un `if/then/else`. Les entrées non-conditionnelles sont préservées.\n *\n * Utilise `_.reduce` pour accumuler les entrées restantes et `_.filter`\n * pour séparer les clés conditionnelles des non-conditionnelles.\n */\nfunction resolveAllOfConditions(\n\tresolved: JSONSchema7,\n\tdata: Record<string, unknown>,\n\tengine: MergeEngine,\n\tdiscriminant: Record<string, unknown>,\n): JSONSchema7 {\n\tif (!Array.isArray(resolved.allOf)) return resolved;\n\n\tconst remainingAllOf: JSONSchema7Definition[] = [];\n\n\tfor (const entry of resolved.allOf) {\n\t\tif (typeof entry === \"boolean\") {\n\t\t\tremainingAllOf.push(entry);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst subSchema = entry as JSONSchema7;\n\n\t\tif (subSchema.if === undefined) {\n\t\t\tremainingAllOf.push(entry);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Résoudre la condition de cette entrée allOf\n\t\tconst ifSchema = subSchema.if as JSONSchema7;\n\t\tconst matches = evaluateCondition(ifSchema, data);\n\n\t\textractDiscriminants(ifSchema, data, discriminant);\n\n\t\tconst applicableBranch = matches ? subSchema.then : subSchema.else;\n\n\t\tif (applicableBranch) {\n\t\t\tmergeBranchInto(\n\t\t\t\tresolved,\n\t\t\t\tapplicableBranch as JSONSchema7Definition,\n\t\t\t\tengine,\n\t\t\t);\n\t\t}\n\n\t\t// Garder les parties non-conditionnelles de l'entrée allOf\n\t\tconst remaining = omitKeys(\n\t\t\tsubSchema as unknown as Record<string, unknown>,\n\t\t\t[\"if\", \"then\", \"else\"],\n\t\t);\n\t\tif (Object.keys(remaining).length > 0) {\n\t\t\tremainingAllOf.push(remaining as JSONSchema7);\n\t\t}\n\t}\n\n\tresolved = { ...resolved };\n\tif (remainingAllOf.length === 0) {\n\t\tdelete resolved.allOf;\n\t} else {\n\t\tresolved.allOf = remainingAllOf;\n\t}\n\n\treturn resolved;\n}\n\n/**\n * Phase 3 : Récurse dans les `properties` du schema résolu pour résoudre\n * les conditions imbriquées (ex: un objet dont une propriété a un if/then/else).\n *\n * Utilise `_.mapValues` pour transformer chaque propriété en une seule passe,\n * et `_.forEach` pour remonter les discriminants imbriqués.\n */\nfunction resolveNestedProperties(\n\tresolved: JSONSchema7,\n\tdata: Record<string, unknown>,\n\tengine: MergeEngine,\n\tdiscriminant: Record<string, unknown>,\n): JSONSchema7 {\n\tif (!isPlainObj(resolved.properties)) return resolved;\n\n\tconst props = resolved.properties as Record<string, JSONSchema7Definition>;\n\tconst propKeys = Object.keys(props);\n\tlet changed = false;\n\tconst resolvedProps: Record<string, JSONSchema7Definition> = {};\n\n\tfor (const key of propKeys) {\n\t\tconst propDef = props[key];\n\t\tif (propDef === undefined) continue;\n\t\tif (typeof propDef === \"boolean\") {\n\t\t\tresolvedProps[key] = propDef;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst propSchema = propDef as JSONSchema7;\n\t\tconst hasConditions =\n\t\t\tpropSchema.if !== undefined ||\n\t\t\t(Array.isArray(propSchema.allOf) &&\n\t\t\t\tpropSchema.allOf.some(\n\t\t\t\t\t(e) => typeof e !== \"boolean\" && hasOwn(e as object, \"if\"),\n\t\t\t\t));\n\n\t\tif (!hasConditions) {\n\t\t\tresolvedProps[key] = propDef;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Données imbriquées disponibles → résoudre récursivement\n\t\tconst nestedData = isPlainObj(data[key])\n\t\t\t? (data[key] as Record<string, unknown>)\n\t\t\t: {};\n\n\t\tconst nested = resolveConditions(propSchema, nestedData, engine);\n\n\t\t// Remonter les discriminants imbriqués avec prefix\n\t\tfor (const dk of Object.keys(nested.discriminant)) {\n\t\t\tdiscriminant[`${key}.${dk}`] = nested.discriminant[dk];\n\t\t}\n\n\t\tresolvedProps[key] = nested.resolved;\n\t\tchanged = true;\n\t}\n\n\treturn changed ? { ...resolved, properties: resolvedProps } : resolved;\n}\n"],"names":["resolveConditions","SPECIAL_MERGE_KEYS","Set","SUB_SCHEMA_KEYS","MIN_KEYS","MAX_KEYS","matchesType","value","type","undefined","types","Array","isArray","actualType","inferType","some","t","evaluateNumericConstraints","prop","minimum","maximum","exclusiveMinimum","exclusiveMaximum","multipleOf","patternRegexCache","Map","getOrCompileRegex","pattern","regex","get","RegExp","set","evaluateStringConstraints","minLength","length","maxLength","test","evaluateArrayConstraints","minItems","maxItems","uniqueItems","len","i","j","deepEqual","evaluateCondition","ifSchema","data","isPlainObj","properties","propsOk","Object","keys","every","key","propDef","hasOwn","const","enum","v","format","formatResult","validateFormat","required","allRequired","allOf","allMatch","entry","anyOf","anyMatch","oneOf","matchCount","matches","not","notResult","DISCRIMINANT_INDICATORS","extractDiscriminants","out","props","hasIndicator","indicator","mergeBranchInto","resolved","branchDef","engine","branchSchema","unionStrings","branchProps","mergedProps","branchProp","existing","merged","merge","dependencies","resolvedDeps","branchDeps","acc","depKey","branchVal","existingVal","has","resolvedVal","Math","max","min","base","branch","schema","discriminant","hasTopLevelIf","if","hasAllOfConditions","e","resolveNestedProperties","resolveAllOfConditions","applicableBranch","then","else","remainingAllOf","push","subSchema","remaining","omitKeys","propKeys","changed","resolvedProps","propSchema","hasConditions","nestedData","nested","dk"],"mappings":"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"}
|