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.
Files changed (126) hide show
  1. package/README.md +119 -151
  2. package/dist/{condition-resolver.d.ts → cjs/condition-resolver.d.ts} +2 -2
  3. package/dist/cjs/condition-resolver.js +2 -0
  4. package/dist/cjs/condition-resolver.js.map +1 -0
  5. package/dist/cjs/data-narrowing.d.ts +31 -0
  6. package/dist/cjs/data-narrowing.js +2 -0
  7. package/dist/cjs/data-narrowing.js.map +1 -0
  8. package/dist/cjs/format-validator.js +2 -0
  9. package/dist/cjs/format-validator.js.map +1 -0
  10. package/dist/{formatter.d.ts → cjs/formatter.d.ts} +1 -1
  11. package/dist/cjs/formatter.js +2 -0
  12. package/dist/cjs/formatter.js.map +1 -0
  13. package/dist/cjs/index.d.ts +6 -0
  14. package/dist/cjs/index.js +2 -0
  15. package/dist/cjs/index.js.map +1 -0
  16. package/dist/cjs/json-schema-compatibility-checker.d.ts +86 -0
  17. package/dist/cjs/json-schema-compatibility-checker.js +2 -0
  18. package/dist/cjs/json-schema-compatibility-checker.js.map +1 -0
  19. package/dist/cjs/merge-engine.js +2 -0
  20. package/dist/cjs/merge-engine.js.map +1 -0
  21. package/dist/cjs/normalizer.js +2 -0
  22. package/dist/cjs/normalizer.js.map +1 -0
  23. package/dist/cjs/package.json +3 -0
  24. package/dist/cjs/pattern-subset.js +2 -0
  25. package/dist/cjs/pattern-subset.js.map +1 -0
  26. package/dist/{semantic-errors.d.ts → cjs/semantic-errors.d.ts} +1 -1
  27. package/dist/cjs/semantic-errors.js +2 -0
  28. package/dist/cjs/semantic-errors.js.map +1 -0
  29. package/dist/{subset-checker.d.ts → cjs/subset-checker.d.ts} +2 -2
  30. package/dist/cjs/subset-checker.js +2 -0
  31. package/dist/cjs/subset-checker.js.map +1 -0
  32. package/dist/{types.d.ts → cjs/types.d.ts} +19 -3
  33. package/dist/cjs/types.js +2 -0
  34. package/dist/cjs/types.js.map +1 -0
  35. package/dist/cjs/utils.js +2 -0
  36. package/dist/cjs/utils.js.map +1 -0
  37. package/dist/esm/condition-resolver.d.ts +26 -0
  38. package/dist/esm/condition-resolver.js +2 -0
  39. package/dist/esm/condition-resolver.js.map +1 -0
  40. package/dist/esm/data-narrowing.d.ts +31 -0
  41. package/dist/esm/data-narrowing.js +2 -0
  42. package/dist/esm/data-narrowing.js.map +1 -0
  43. package/dist/esm/format-validator.d.ts +78 -0
  44. package/dist/esm/format-validator.js +2 -0
  45. package/dist/esm/format-validator.js.map +1 -0
  46. package/dist/esm/formatter.d.ts +22 -0
  47. package/dist/esm/formatter.js +2 -0
  48. package/dist/{chunk-7mkqk5qv.js.map → esm/formatter.js.map} +1 -10
  49. package/dist/esm/index.d.ts +6 -0
  50. package/dist/esm/index.js +2 -0
  51. package/dist/esm/index.js.map +1 -0
  52. package/dist/esm/json-schema-compatibility-checker.d.ts +86 -0
  53. package/dist/esm/json-schema-compatibility-checker.js +2 -0
  54. package/dist/esm/json-schema-compatibility-checker.js.map +1 -0
  55. package/dist/esm/merge-engine.d.ts +30 -0
  56. package/dist/esm/merge-engine.js +2 -0
  57. package/dist/esm/merge-engine.js.map +1 -0
  58. package/dist/esm/normalizer.d.ts +24 -0
  59. package/dist/esm/normalizer.js +2 -0
  60. package/dist/esm/normalizer.js.map +1 -0
  61. package/dist/esm/pattern-subset.d.ts +59 -0
  62. package/dist/esm/pattern-subset.js +2 -0
  63. package/dist/esm/pattern-subset.js.map +1 -0
  64. package/dist/esm/semantic-errors.d.ts +24 -0
  65. package/dist/esm/semantic-errors.js +2 -0
  66. package/dist/esm/semantic-errors.js.map +1 -0
  67. package/dist/esm/subset-checker.d.ts +81 -0
  68. package/dist/esm/subset-checker.js +2 -0
  69. package/dist/esm/subset-checker.js.map +1 -0
  70. package/dist/esm/types.d.ts +45 -0
  71. package/dist/esm/types.js +2 -0
  72. package/dist/esm/types.js.map +1 -0
  73. package/dist/esm/utils.d.ts +43 -0
  74. package/dist/esm/utils.js +2 -0
  75. package/dist/esm/utils.js.map +1 -0
  76. package/package.json +26 -11
  77. package/dist/chunk-159ezrfm.js +0 -5
  78. package/dist/chunk-159ezrfm.js.map +0 -10
  79. package/dist/chunk-1xda2xvb.js +0 -5
  80. package/dist/chunk-1xda2xvb.js.map +0 -10
  81. package/dist/chunk-3gazezx2.js +0 -5
  82. package/dist/chunk-3gazezx2.js.map +0 -10
  83. package/dist/chunk-7mkqk5qv.js +0 -6
  84. package/dist/chunk-aemw3jv0.js +0 -5
  85. package/dist/chunk-aemw3jv0.js.map +0 -10
  86. package/dist/chunk-hrwygqa2.js +0 -5
  87. package/dist/chunk-hrwygqa2.js.map +0 -10
  88. package/dist/chunk-jg89j4nd.js +0 -5
  89. package/dist/chunk-jg89j4nd.js.map +0 -10
  90. package/dist/chunk-kncywgnx.js +0 -5
  91. package/dist/chunk-kncywgnx.js.map +0 -10
  92. package/dist/chunk-nkpsq34q.js +0 -5
  93. package/dist/chunk-nkpsq34q.js.map +0 -10
  94. package/dist/chunk-nn3cjjtp.js +0 -5
  95. package/dist/chunk-nn3cjjtp.js.map +0 -10
  96. package/dist/condition-resolver.js +0 -4
  97. package/dist/condition-resolver.js.map +0 -9
  98. package/dist/format-validator.js +0 -4
  99. package/dist/format-validator.js.map +0 -9
  100. package/dist/formatter.js +0 -4
  101. package/dist/formatter.js.map +0 -9
  102. package/dist/index.d.ts +0 -5
  103. package/dist/index.js +0 -4
  104. package/dist/index.js.map +0 -9
  105. package/dist/json-schema-compatibility-checker.d.ts +0 -73
  106. package/dist/json-schema-compatibility-checker.js +0 -4
  107. package/dist/json-schema-compatibility-checker.js.map +0 -9
  108. package/dist/merge-engine.js +0 -4
  109. package/dist/merge-engine.js.map +0 -9
  110. package/dist/normalizer.js +0 -4
  111. package/dist/normalizer.js.map +0 -9
  112. package/dist/pattern-subset.js +0 -4
  113. package/dist/pattern-subset.js.map +0 -9
  114. package/dist/semantic-errors.js +0 -4
  115. package/dist/semantic-errors.js.map +0 -9
  116. package/dist/subset-checker.js +0 -4
  117. package/dist/subset-checker.js.map +0 -9
  118. package/dist/types.js +0 -3
  119. package/dist/types.js.map +0 -9
  120. package/dist/utils.js +0 -4
  121. package/dist/utils.js.map +0 -9
  122. /package/dist/{format-validator.d.ts → cjs/format-validator.d.ts} +0 -0
  123. /package/dist/{merge-engine.d.ts → cjs/merge-engine.d.ts} +0 -0
  124. /package/dist/{normalizer.d.ts → cjs/normalizer.d.ts} +0 -0
  125. /package/dist/{pattern-subset.d.ts → cjs/pattern-subset.d.ts} +0 -0
  126. /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
- - [`checkResolved(sub, sup, subData, supData?)`](#checkresolvedsub-sup-subdata-supdata)
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 différences structurelles.
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
- diffs: SchemaDiff[]; // Différences structurelles
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.diffs); // [] (aucune différence)
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.diffs);
262
+ console.log(result.errors);
260
263
  // [
261
- // { path: "required", type: "changed", expected: ["name"], actual: ["name", "age"] },
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.diffs); // [{ path: "$", type: "changed", expected: ..., actual: "Incompatible..." }]
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
- ### `checkResolved(sub, sup, subData, supData?)`
428
+ ### `check(sub, sup, options)`
485
429
 
486
430
  ```ts
487
- checkResolved(
488
- sub: JSONSchema7,
489
- sup: JSONSchema7,
490
- subData: Record<string, unknown>,
491
- supData?: Record<string, unknown>
492
- ): SubsetResult & {
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.checkResolved(sub, conditionalSup, { kind: "text" });
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
- // Diffs:
585
- // ~ minimum: 0 5
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
- Les différentes icônes dans le diff :
600
- - `+`contrainte **ajoutée** par le merge (absente dans sub, présente dans l'intersection)
601
- - `-`contrainte **supprimée** par le merge
602
- - `~` contrainte **modifiée** (valeur différente entre sub et l'intersection)
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.diffs);
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
- const addPropDiff = result.diffs.find(d => d.path === "additionalProperties");
860
- console.log(addPropDiff); // { path: "additionalProperties", type: "added", ... }
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 chemins de diff sont complets
869
+ // Les erreurs montrent les propriétés manquantes avec un chemin complet
914
870
  const result = checker.check(shallow, deep);
915
- const bioDiff = result.diffs.find(
916
- d => d.path === "properties.user.properties.profile.properties.bio"
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` avec distinction dans les chemins de diff.
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). La différence apparaît dans les **chemins de diff**.
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.diffs[0].path; // "oneOf[2]" (et non "anyOf[2]")
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 connection = checker.canConnect(nodeAOutput, nodeBInput);
1640
- console.log(connection.isSubset); // true ✅
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 (!connection.isSubset) {
1645
- console.log(checker.formatResult("NodeA → NodeB", connection));
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.canConnect(apiResponse, consumerExpects);
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.checkResolved(businessOutput, formSchema, {
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.checkResolved(personalOutput, formSchema, {
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
- ConnectionResult,
1785
+ SchemaError,
1833
1786
  ResolvedConditionResult,
1834
- SchemaDiff,
1835
- BranchType,
1836
- BranchResult,
1787
+ ResolvedSubsetResult,
1788
+ CheckConditionsOptions,
1837
1789
  } from "json-schema-compatibility-checker";
1838
1790
  ```
1839
1791
 
1840
- ### `SchemaDiff`
1792
+ ### `SchemaError`
1841
1793
 
1842
1794
  ```ts
1843
- interface SchemaDiff {
1844
- /** Chemin JSON-path-like vers la divergence (ex: "properties.user.required") */
1845
- path: string;
1846
- /** Type de divergence */
1847
- type: "added" | "removed" | "changed";
1848
- /** Valeur dans le schema original (sub) */
1849
- expected: unknown;
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
- /** Différences structurelles détectées entre sub et l'intersection */
1864
- diffs: SchemaDiff[];
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 `checkResolved()` avec les données discriminantes.
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
- │ │ Differ │ │
1936
+ │ │Semantic Errors│ │
1974
1937
  │ │ │ ┌──────────────────────────┐ │
1975
- │ │ - computeDiff│ │ Pattern Subset │ │
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
- │ │ - Diff lines │ │ │ │
1947
+ │ │ - Error lines│ │ │ │
1985
1948
  │ └──────────────┘ │ - validateFormat │ │
1986
1949
  │ │ - isFormatSubset │ │
1987
- │ - Format hierarchy │ │
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
- Projet privé.
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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"}