@featurevisor/core 2.14.0 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/coverage/clover.xml +774 -652
  3. package/coverage/coverage-final.json +19 -18
  4. package/coverage/lcov-report/builder/allocator.ts.html +1 -1
  5. package/coverage/lcov-report/builder/buildScopedConditions.ts.html +1 -1
  6. package/coverage/lcov-report/builder/buildScopedDatafile.ts.html +1 -1
  7. package/coverage/lcov-report/builder/buildScopedSegments.ts.html +1 -1
  8. package/coverage/lcov-report/builder/index.html +1 -1
  9. package/coverage/lcov-report/builder/mutateVariables.ts.html +1 -1
  10. package/coverage/lcov-report/builder/mutator.ts.html +1 -1
  11. package/coverage/lcov-report/builder/revision.ts.html +1 -1
  12. package/coverage/lcov-report/builder/traffic.ts.html +1 -1
  13. package/coverage/lcov-report/config/index.html +30 -15
  14. package/coverage/lcov-report/config/index.ts.html +88 -0
  15. package/coverage/lcov-report/config/projectConfig.ts.html +79 -46
  16. package/coverage/lcov-report/datasource/adapter.ts.html +74 -11
  17. package/coverage/lcov-report/datasource/datasource.ts.html +76 -25
  18. package/coverage/lcov-report/datasource/filesystemAdapter.ts.html +690 -66
  19. package/coverage/lcov-report/datasource/index.html +35 -35
  20. package/coverage/lcov-report/datasource/index.ts.html +1 -1
  21. package/coverage/lcov-report/index.html +43 -43
  22. package/coverage/lcov-report/linter/attributeSchema.ts.html +4 -4
  23. package/coverage/lcov-report/linter/checkCircularDependency.ts.html +1 -1
  24. package/coverage/lcov-report/linter/checkPercentageExceedingSlot.ts.html +1 -1
  25. package/coverage/lcov-report/linter/conditionSchema.ts.html +7 -7
  26. package/coverage/lcov-report/linter/featureSchema.ts.html +152 -164
  27. package/coverage/lcov-report/linter/groupSchema.ts.html +3 -3
  28. package/coverage/lcov-report/linter/index.html +34 -34
  29. package/coverage/lcov-report/linter/lintProject.ts.html +428 -83
  30. package/coverage/lcov-report/linter/mutationNotation.ts.html +3 -3
  31. package/coverage/lcov-report/linter/printError.ts.html +9 -9
  32. package/coverage/lcov-report/linter/schema.ts.html +4 -4
  33. package/coverage/lcov-report/linter/segmentSchema.ts.html +3 -3
  34. package/coverage/lcov-report/linter/testSchema.ts.html +6 -6
  35. package/coverage/lcov-report/list/index.html +1 -1
  36. package/coverage/lcov-report/list/matrix.ts.html +1 -1
  37. package/coverage/lcov-report/parsers/index.html +1 -1
  38. package/coverage/lcov-report/parsers/index.ts.html +4 -4
  39. package/coverage/lcov-report/parsers/json.ts.html +2 -2
  40. package/coverage/lcov-report/parsers/yml.ts.html +6 -6
  41. package/coverage/lcov-report/tester/cliFormat.ts.html +1 -1
  42. package/coverage/lcov-report/tester/helpers.ts.html +1 -1
  43. package/coverage/lcov-report/tester/index.html +1 -1
  44. package/coverage/lcov-report/utils/git.ts.html +118 -16
  45. package/coverage/lcov-report/utils/index.html +14 -14
  46. package/coverage/lcov.info +1374 -1107
  47. package/lib/config/projectConfig.d.ts +3 -0
  48. package/lib/config/projectConfig.js +7 -1
  49. package/lib/config/projectConfig.js.map +1 -1
  50. package/lib/config/projectConfig.spec.d.ts +1 -0
  51. package/lib/config/projectConfig.spec.js +24 -0
  52. package/lib/config/projectConfig.spec.js.map +1 -0
  53. package/lib/datasource/adapter.d.ts +7 -0
  54. package/lib/datasource/adapter.js +7 -0
  55. package/lib/datasource/adapter.js.map +1 -1
  56. package/lib/datasource/datasource.d.ts +3 -1
  57. package/lib/datasource/datasource.js +6 -0
  58. package/lib/datasource/datasource.js.map +1 -1
  59. package/lib/datasource/filesystemAdapter.d.ts +8 -0
  60. package/lib/datasource/filesystemAdapter.js +134 -7
  61. package/lib/datasource/filesystemAdapter.js.map +1 -1
  62. package/lib/datasource/filesystemAdapter.spec.d.ts +1 -0
  63. package/lib/datasource/filesystemAdapter.spec.js +88 -0
  64. package/lib/datasource/filesystemAdapter.spec.js.map +1 -0
  65. package/lib/generate-code/index.d.ts +3 -0
  66. package/lib/generate-code/index.js +16 -1
  67. package/lib/generate-code/index.js.map +1 -1
  68. package/lib/generate-code/typescript.d.ts +6 -1
  69. package/lib/generate-code/typescript.js +193 -42
  70. package/lib/generate-code/typescript.js.map +1 -1
  71. package/lib/linter/conditionSchema.spec.js +2 -0
  72. package/lib/linter/conditionSchema.spec.js.map +1 -1
  73. package/lib/linter/featureSchema.js +46 -49
  74. package/lib/linter/featureSchema.js.map +1 -1
  75. package/lib/linter/featureSchema.spec.js +17 -0
  76. package/lib/linter/featureSchema.spec.js.map +1 -1
  77. package/lib/linter/lintProject.d.ts +2 -0
  78. package/lib/linter/lintProject.js +85 -8
  79. package/lib/linter/lintProject.js.map +1 -1
  80. package/lib/linter/lintProject.spec.js +37 -0
  81. package/lib/linter/lintProject.spec.js.map +1 -1
  82. package/lib/linter/segmentSchema.spec.js +2 -0
  83. package/lib/linter/segmentSchema.spec.js.map +1 -1
  84. package/lib/utils/git.js +32 -7
  85. package/lib/utils/git.js.map +1 -1
  86. package/package.json +2 -2
  87. package/src/config/projectConfig.spec.ts +31 -0
  88. package/src/config/projectConfig.ts +11 -0
  89. package/src/datasource/adapter.ts +21 -0
  90. package/src/datasource/datasource.ts +18 -1
  91. package/src/datasource/filesystemAdapter.spec.ts +153 -0
  92. package/src/datasource/filesystemAdapter.ts +216 -8
  93. package/src/generate-code/index.ts +20 -1
  94. package/src/generate-code/typescript.ts +266 -64
  95. package/src/linter/conditionSchema.spec.ts +2 -0
  96. package/src/linter/featureSchema.spec.ts +21 -0
  97. package/src/linter/featureSchema.ts +84 -88
  98. package/src/linter/lintProject.spec.ts +74 -0
  99. package/src/linter/lintProject.ts +123 -8
  100. package/src/linter/segmentSchema.spec.ts +2 -0
  101. package/src/utils/git.ts +41 -7
@@ -23,30 +23,30 @@
23
23
  <div class='clearfix'>
24
24
 
25
25
  <div class='fl pad1y space-right2'>
26
- <span class="strong">27.41% </span>
26
+ <span class="strong">36.25% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>51/186</span>
28
+ <span class='fraction'>91/251</span>
29
29
  </div>
30
30
 
31
31
 
32
32
  <div class='fl pad1y space-right2'>
33
- <span class="strong">17.14% </span>
33
+ <span class="strong">27.67% </span>
34
34
  <span class="quiet">Branches</span>
35
- <span class='fraction'>12/70</span>
35
+ <span class='fraction'>31/112</span>
36
36
  </div>
37
37
 
38
38
 
39
39
  <div class='fl pad1y space-right2'>
40
- <span class="strong">35.48% </span>
40
+ <span class="strong">43.58% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>11/31</span>
42
+ <span class='fraction'>17/39</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
- <span class="strong">27.47% </span>
47
+ <span class="strong">36.17% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>50/182</span>
49
+ <span class='fraction'>89/246</span>
50
50
  </div>
51
51
 
52
52
 
@@ -486,116 +486,397 @@
486
486
  <a name='L421'></a><a href='#L421'>421</a>
487
487
  <a name='L422'></a><a href='#L422'>422</a>
488
488
  <a name='L423'></a><a href='#L423'>423</a>
489
- <a name='L424'></a><a href='#L424'>424</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">1x</span>
490
- <span class="cline-any cline-yes">1x</span>
491
- <span class="cline-any cline-yes">1x</span>
489
+ <a name='L424'></a><a href='#L424'>424</a>
490
+ <a name='L425'></a><a href='#L425'>425</a>
491
+ <a name='L426'></a><a href='#L426'>426</a>
492
+ <a name='L427'></a><a href='#L427'>427</a>
493
+ <a name='L428'></a><a href='#L428'>428</a>
494
+ <a name='L429'></a><a href='#L429'>429</a>
495
+ <a name='L430'></a><a href='#L430'>430</a>
496
+ <a name='L431'></a><a href='#L431'>431</a>
497
+ <a name='L432'></a><a href='#L432'>432</a>
498
+ <a name='L433'></a><a href='#L433'>433</a>
499
+ <a name='L434'></a><a href='#L434'>434</a>
500
+ <a name='L435'></a><a href='#L435'>435</a>
501
+ <a name='L436'></a><a href='#L436'>436</a>
502
+ <a name='L437'></a><a href='#L437'>437</a>
503
+ <a name='L438'></a><a href='#L438'>438</a>
504
+ <a name='L439'></a><a href='#L439'>439</a>
505
+ <a name='L440'></a><a href='#L440'>440</a>
506
+ <a name='L441'></a><a href='#L441'>441</a>
507
+ <a name='L442'></a><a href='#L442'>442</a>
508
+ <a name='L443'></a><a href='#L443'>443</a>
509
+ <a name='L444'></a><a href='#L444'>444</a>
510
+ <a name='L445'></a><a href='#L445'>445</a>
511
+ <a name='L446'></a><a href='#L446'>446</a>
512
+ <a name='L447'></a><a href='#L447'>447</a>
513
+ <a name='L448'></a><a href='#L448'>448</a>
514
+ <a name='L449'></a><a href='#L449'>449</a>
515
+ <a name='L450'></a><a href='#L450'>450</a>
516
+ <a name='L451'></a><a href='#L451'>451</a>
517
+ <a name='L452'></a><a href='#L452'>452</a>
518
+ <a name='L453'></a><a href='#L453'>453</a>
519
+ <a name='L454'></a><a href='#L454'>454</a>
520
+ <a name='L455'></a><a href='#L455'>455</a>
521
+ <a name='L456'></a><a href='#L456'>456</a>
522
+ <a name='L457'></a><a href='#L457'>457</a>
523
+ <a name='L458'></a><a href='#L458'>458</a>
524
+ <a name='L459'></a><a href='#L459'>459</a>
525
+ <a name='L460'></a><a href='#L460'>460</a>
526
+ <a name='L461'></a><a href='#L461'>461</a>
527
+ <a name='L462'></a><a href='#L462'>462</a>
528
+ <a name='L463'></a><a href='#L463'>463</a>
529
+ <a name='L464'></a><a href='#L464'>464</a>
530
+ <a name='L465'></a><a href='#L465'>465</a>
531
+ <a name='L466'></a><a href='#L466'>466</a>
532
+ <a name='L467'></a><a href='#L467'>467</a>
533
+ <a name='L468'></a><a href='#L468'>468</a>
534
+ <a name='L469'></a><a href='#L469'>469</a>
535
+ <a name='L470'></a><a href='#L470'>470</a>
536
+ <a name='L471'></a><a href='#L471'>471</a>
537
+ <a name='L472'></a><a href='#L472'>472</a>
538
+ <a name='L473'></a><a href='#L473'>473</a>
539
+ <a name='L474'></a><a href='#L474'>474</a>
540
+ <a name='L475'></a><a href='#L475'>475</a>
541
+ <a name='L476'></a><a href='#L476'>476</a>
542
+ <a name='L477'></a><a href='#L477'>477</a>
543
+ <a name='L478'></a><a href='#L478'>478</a>
544
+ <a name='L479'></a><a href='#L479'>479</a>
545
+ <a name='L480'></a><a href='#L480'>480</a>
546
+ <a name='L481'></a><a href='#L481'>481</a>
547
+ <a name='L482'></a><a href='#L482'>482</a>
548
+ <a name='L483'></a><a href='#L483'>483</a>
549
+ <a name='L484'></a><a href='#L484'>484</a>
550
+ <a name='L485'></a><a href='#L485'>485</a>
551
+ <a name='L486'></a><a href='#L486'>486</a>
552
+ <a name='L487'></a><a href='#L487'>487</a>
553
+ <a name='L488'></a><a href='#L488'>488</a>
554
+ <a name='L489'></a><a href='#L489'>489</a>
555
+ <a name='L490'></a><a href='#L490'>490</a>
556
+ <a name='L491'></a><a href='#L491'>491</a>
557
+ <a name='L492'></a><a href='#L492'>492</a>
558
+ <a name='L493'></a><a href='#L493'>493</a>
559
+ <a name='L494'></a><a href='#L494'>494</a>
560
+ <a name='L495'></a><a href='#L495'>495</a>
561
+ <a name='L496'></a><a href='#L496'>496</a>
562
+ <a name='L497'></a><a href='#L497'>497</a>
563
+ <a name='L498'></a><a href='#L498'>498</a>
564
+ <a name='L499'></a><a href='#L499'>499</a>
565
+ <a name='L500'></a><a href='#L500'>500</a>
566
+ <a name='L501'></a><a href='#L501'>501</a>
567
+ <a name='L502'></a><a href='#L502'>502</a>
568
+ <a name='L503'></a><a href='#L503'>503</a>
569
+ <a name='L504'></a><a href='#L504'>504</a>
570
+ <a name='L505'></a><a href='#L505'>505</a>
571
+ <a name='L506'></a><a href='#L506'>506</a>
572
+ <a name='L507'></a><a href='#L507'>507</a>
573
+ <a name='L508'></a><a href='#L508'>508</a>
574
+ <a name='L509'></a><a href='#L509'>509</a>
575
+ <a name='L510'></a><a href='#L510'>510</a>
576
+ <a name='L511'></a><a href='#L511'>511</a>
577
+ <a name='L512'></a><a href='#L512'>512</a>
578
+ <a name='L513'></a><a href='#L513'>513</a>
579
+ <a name='L514'></a><a href='#L514'>514</a>
580
+ <a name='L515'></a><a href='#L515'>515</a>
581
+ <a name='L516'></a><a href='#L516'>516</a>
582
+ <a name='L517'></a><a href='#L517'>517</a>
583
+ <a name='L518'></a><a href='#L518'>518</a>
584
+ <a name='L519'></a><a href='#L519'>519</a>
585
+ <a name='L520'></a><a href='#L520'>520</a>
586
+ <a name='L521'></a><a href='#L521'>521</a>
587
+ <a name='L522'></a><a href='#L522'>522</a>
588
+ <a name='L523'></a><a href='#L523'>523</a>
589
+ <a name='L524'></a><a href='#L524'>524</a>
590
+ <a name='L525'></a><a href='#L525'>525</a>
591
+ <a name='L526'></a><a href='#L526'>526</a>
592
+ <a name='L527'></a><a href='#L527'>527</a>
593
+ <a name='L528'></a><a href='#L528'>528</a>
594
+ <a name='L529'></a><a href='#L529'>529</a>
595
+ <a name='L530'></a><a href='#L530'>530</a>
596
+ <a name='L531'></a><a href='#L531'>531</a>
597
+ <a name='L532'></a><a href='#L532'>532</a>
598
+ <a name='L533'></a><a href='#L533'>533</a>
599
+ <a name='L534'></a><a href='#L534'>534</a>
600
+ <a name='L535'></a><a href='#L535'>535</a>
601
+ <a name='L536'></a><a href='#L536'>536</a>
602
+ <a name='L537'></a><a href='#L537'>537</a>
603
+ <a name='L538'></a><a href='#L538'>538</a>
604
+ <a name='L539'></a><a href='#L539'>539</a>
605
+ <a name='L540'></a><a href='#L540'>540</a>
606
+ <a name='L541'></a><a href='#L541'>541</a>
607
+ <a name='L542'></a><a href='#L542'>542</a>
608
+ <a name='L543'></a><a href='#L543'>543</a>
609
+ <a name='L544'></a><a href='#L544'>544</a>
610
+ <a name='L545'></a><a href='#L545'>545</a>
611
+ <a name='L546'></a><a href='#L546'>546</a>
612
+ <a name='L547'></a><a href='#L547'>547</a>
613
+ <a name='L548'></a><a href='#L548'>548</a>
614
+ <a name='L549'></a><a href='#L549'>549</a>
615
+ <a name='L550'></a><a href='#L550'>550</a>
616
+ <a name='L551'></a><a href='#L551'>551</a>
617
+ <a name='L552'></a><a href='#L552'>552</a>
618
+ <a name='L553'></a><a href='#L553'>553</a>
619
+ <a name='L554'></a><a href='#L554'>554</a>
620
+ <a name='L555'></a><a href='#L555'>555</a>
621
+ <a name='L556'></a><a href='#L556'>556</a>
622
+ <a name='L557'></a><a href='#L557'>557</a>
623
+ <a name='L558'></a><a href='#L558'>558</a>
624
+ <a name='L559'></a><a href='#L559'>559</a>
625
+ <a name='L560'></a><a href='#L560'>560</a>
626
+ <a name='L561'></a><a href='#L561'>561</a>
627
+ <a name='L562'></a><a href='#L562'>562</a>
628
+ <a name='L563'></a><a href='#L563'>563</a>
629
+ <a name='L564'></a><a href='#L564'>564</a>
630
+ <a name='L565'></a><a href='#L565'>565</a>
631
+ <a name='L566'></a><a href='#L566'>566</a>
632
+ <a name='L567'></a><a href='#L567'>567</a>
633
+ <a name='L568'></a><a href='#L568'>568</a>
634
+ <a name='L569'></a><a href='#L569'>569</a>
635
+ <a name='L570'></a><a href='#L570'>570</a>
636
+ <a name='L571'></a><a href='#L571'>571</a>
637
+ <a name='L572'></a><a href='#L572'>572</a>
638
+ <a name='L573'></a><a href='#L573'>573</a>
639
+ <a name='L574'></a><a href='#L574'>574</a>
640
+ <a name='L575'></a><a href='#L575'>575</a>
641
+ <a name='L576'></a><a href='#L576'>576</a>
642
+ <a name='L577'></a><a href='#L577'>577</a>
643
+ <a name='L578'></a><a href='#L578'>578</a>
644
+ <a name='L579'></a><a href='#L579'>579</a>
645
+ <a name='L580'></a><a href='#L580'>580</a>
646
+ <a name='L581'></a><a href='#L581'>581</a>
647
+ <a name='L582'></a><a href='#L582'>582</a>
648
+ <a name='L583'></a><a href='#L583'>583</a>
649
+ <a name='L584'></a><a href='#L584'>584</a>
650
+ <a name='L585'></a><a href='#L585'>585</a>
651
+ <a name='L586'></a><a href='#L586'>586</a>
652
+ <a name='L587'></a><a href='#L587'>587</a>
653
+ <a name='L588'></a><a href='#L588'>588</a>
654
+ <a name='L589'></a><a href='#L589'>589</a>
655
+ <a name='L590'></a><a href='#L590'>590</a>
656
+ <a name='L591'></a><a href='#L591'>591</a>
657
+ <a name='L592'></a><a href='#L592'>592</a>
658
+ <a name='L593'></a><a href='#L593'>593</a>
659
+ <a name='L594'></a><a href='#L594'>594</a>
660
+ <a name='L595'></a><a href='#L595'>595</a>
661
+ <a name='L596'></a><a href='#L596'>596</a>
662
+ <a name='L597'></a><a href='#L597'>597</a>
663
+ <a name='L598'></a><a href='#L598'>598</a>
664
+ <a name='L599'></a><a href='#L599'>599</a>
665
+ <a name='L600'></a><a href='#L600'>600</a>
666
+ <a name='L601'></a><a href='#L601'>601</a>
667
+ <a name='L602'></a><a href='#L602'>602</a>
668
+ <a name='L603'></a><a href='#L603'>603</a>
669
+ <a name='L604'></a><a href='#L604'>604</a>
670
+ <a name='L605'></a><a href='#L605'>605</a>
671
+ <a name='L606'></a><a href='#L606'>606</a>
672
+ <a name='L607'></a><a href='#L607'>607</a>
673
+ <a name='L608'></a><a href='#L608'>608</a>
674
+ <a name='L609'></a><a href='#L609'>609</a>
675
+ <a name='L610'></a><a href='#L610'>610</a>
676
+ <a name='L611'></a><a href='#L611'>611</a>
677
+ <a name='L612'></a><a href='#L612'>612</a>
678
+ <a name='L613'></a><a href='#L613'>613</a>
679
+ <a name='L614'></a><a href='#L614'>614</a>
680
+ <a name='L615'></a><a href='#L615'>615</a>
681
+ <a name='L616'></a><a href='#L616'>616</a>
682
+ <a name='L617'></a><a href='#L617'>617</a>
683
+ <a name='L618'></a><a href='#L618'>618</a>
684
+ <a name='L619'></a><a href='#L619'>619</a>
685
+ <a name='L620'></a><a href='#L620'>620</a>
686
+ <a name='L621'></a><a href='#L621'>621</a>
687
+ <a name='L622'></a><a href='#L622'>622</a>
688
+ <a name='L623'></a><a href='#L623'>623</a>
689
+ <a name='L624'></a><a href='#L624'>624</a>
690
+ <a name='L625'></a><a href='#L625'>625</a>
691
+ <a name='L626'></a><a href='#L626'>626</a>
692
+ <a name='L627'></a><a href='#L627'>627</a>
693
+ <a name='L628'></a><a href='#L628'>628</a>
694
+ <a name='L629'></a><a href='#L629'>629</a>
695
+ <a name='L630'></a><a href='#L630'>630</a>
696
+ <a name='L631'></a><a href='#L631'>631</a>
697
+ <a name='L632'></a><a href='#L632'>632</a></td><td class="line-coverage quiet"><span class="cline-any cline-yes">3x</span>
698
+ <span class="cline-any cline-yes">3x</span>
699
+ <span class="cline-any cline-yes">3x</span>
700
+ <span class="cline-any cline-neutral">&nbsp;</span>
701
+ <span class="cline-any cline-neutral">&nbsp;</span>
702
+ <span class="cline-any cline-neutral">&nbsp;</span>
703
+ <span class="cline-any cline-neutral">&nbsp;</span>
704
+ <span class="cline-any cline-neutral">&nbsp;</span>
705
+ <span class="cline-any cline-neutral">&nbsp;</span>
706
+ <span class="cline-any cline-neutral">&nbsp;</span>
707
+ <span class="cline-any cline-neutral">&nbsp;</span>
708
+ <span class="cline-any cline-neutral">&nbsp;</span>
709
+ <span class="cline-any cline-neutral">&nbsp;</span>
710
+ <span class="cline-any cline-neutral">&nbsp;</span>
711
+ <span class="cline-any cline-neutral">&nbsp;</span>
712
+ <span class="cline-any cline-neutral">&nbsp;</span>
713
+ <span class="cline-any cline-neutral">&nbsp;</span>
714
+ <span class="cline-any cline-neutral">&nbsp;</span>
715
+ <span class="cline-any cline-neutral">&nbsp;</span>
716
+ <span class="cline-any cline-yes">3x</span>
717
+ <span class="cline-any cline-neutral">&nbsp;</span>
718
+ <span class="cline-any cline-neutral">&nbsp;</span>
719
+ <span class="cline-any cline-yes">3x</span>
720
+ <span class="cline-any cline-neutral">&nbsp;</span>
721
+ <span class="cline-any cline-yes">3x</span>
492
722
  <span class="cline-any cline-neutral">&nbsp;</span>
493
723
  <span class="cline-any cline-neutral">&nbsp;</span>
494
724
  <span class="cline-any cline-neutral">&nbsp;</span>
495
725
  <span class="cline-any cline-neutral">&nbsp;</span>
496
726
  <span class="cline-any cline-neutral">&nbsp;</span>
727
+ <span class="cline-any cline-yes">4x</span>
728
+ <span class="cline-any cline-yes">4x</span>
729
+ <span class="cline-any cline-yes">4x</span>
497
730
  <span class="cline-any cline-neutral">&nbsp;</span>
498
731
  <span class="cline-any cline-neutral">&nbsp;</span>
499
732
  <span class="cline-any cline-neutral">&nbsp;</span>
733
+ <span class="cline-any cline-yes">3x</span>
500
734
  <span class="cline-any cline-neutral">&nbsp;</span>
501
735
  <span class="cline-any cline-neutral">&nbsp;</span>
502
736
  <span class="cline-any cline-neutral">&nbsp;</span>
737
+ <span class="cline-any cline-no">&nbsp;</span>
503
738
  <span class="cline-any cline-neutral">&nbsp;</span>
504
- <span class="cline-any cline-yes">1x</span>
739
+ <span class="cline-any cline-no">&nbsp;</span>
505
740
  <span class="cline-any cline-neutral">&nbsp;</span>
506
741
  <span class="cline-any cline-neutral">&nbsp;</span>
507
- <span class="cline-any cline-yes">1x</span>
742
+ <span class="cline-any cline-yes">3x</span>
743
+ <span class="cline-any cline-no">&nbsp;</span>
508
744
  <span class="cline-any cline-neutral">&nbsp;</span>
509
- <span class="cline-any cline-yes">1x</span>
510
745
  <span class="cline-any cline-neutral">&nbsp;</span>
746
+ <span class="cline-any cline-yes">3x</span>
747
+ <span class="cline-any cline-yes">66x</span>
511
748
  <span class="cline-any cline-neutral">&nbsp;</span>
749
+ <span class="cline-any cline-yes">66x</span>
750
+ <span class="cline-any cline-yes">12x</span>
512
751
  <span class="cline-any cline-neutral">&nbsp;</span>
513
- <span class="cline-any cline-no">&nbsp;</span>
514
752
  <span class="cline-any cline-neutral">&nbsp;</span>
515
- <span class="cline-any cline-no">&nbsp;</span>
753
+ <span class="cline-any cline-yes">54x</span>
516
754
  <span class="cline-any cline-neutral">&nbsp;</span>
755
+ <span class="cline-any cline-yes">54x</span>
756
+ <span class="cline-any cline-yes">473x</span>
757
+ <span class="cline-any cline-yes">473x</span>
517
758
  <span class="cline-any cline-neutral">&nbsp;</span>
518
- <span class="cline-any cline-yes">1x</span>
519
- <span class="cline-any cline-no">&nbsp;</span>
759
+ <span class="cline-any cline-yes">473x</span>
760
+ <span class="cline-any cline-yes">24x</span>
761
+ <span class="cline-any cline-yes">449x</span>
762
+ <span class="cline-any cline-yes">449x</span>
763
+ <span class="cline-any cline-neutral">&nbsp;</span>
764
+ <span class="cline-any cline-neutral">&nbsp;</span>
765
+ <span class="cline-any cline-neutral">&nbsp;</span>
766
+ <span class="cline-any cline-yes">54x</span>
520
767
  <span class="cline-any cline-neutral">&nbsp;</span>
521
768
  <span class="cline-any cline-neutral">&nbsp;</span>
522
- <span class="cline-any cline-yes">1x</span>
523
- <span class="cline-any cline-yes">52x</span>
524
769
  <span class="cline-any cline-neutral">&nbsp;</span>
525
- <span class="cline-any cline-yes">52x</span>
526
770
  <span class="cline-any cline-no">&nbsp;</span>
527
771
  <span class="cline-any cline-neutral">&nbsp;</span>
528
772
  <span class="cline-any cline-neutral">&nbsp;</span>
529
- <span class="cline-any cline-yes">52x</span>
530
773
  <span class="cline-any cline-neutral">&nbsp;</span>
531
- <span class="cline-any cline-yes">52x</span>
532
- <span class="cline-any cline-yes">471x</span>
533
- <span class="cline-any cline-yes">471x</span>
534
774
  <span class="cline-any cline-neutral">&nbsp;</span>
535
- <span class="cline-any cline-yes">471x</span>
536
- <span class="cline-any cline-yes">24x</span>
537
- <span class="cline-any cline-yes">447x</span>
538
- <span class="cline-any cline-yes">447x</span>
775
+ <span class="cline-any cline-yes">3x</span>
539
776
  <span class="cline-any cline-neutral">&nbsp;</span>
540
777
  <span class="cline-any cline-neutral">&nbsp;</span>
541
778
  <span class="cline-any cline-neutral">&nbsp;</span>
542
- <span class="cline-any cline-yes">52x</span>
779
+ <span class="cline-any cline-yes">10x</span>
780
+ <span class="cline-any cline-yes">10x</span>
781
+ <span class="cline-any cline-neutral">&nbsp;</span>
782
+ <span class="cline-any cline-yes">10x</span>
783
+ <span class="cline-any cline-neutral">&nbsp;</span>
784
+ <span class="cline-any cline-yes">10x</span>
785
+ <span class="cline-any cline-neutral">&nbsp;</span>
786
+ <span class="cline-any cline-neutral">&nbsp;</span>
787
+ <span class="cline-any cline-neutral">&nbsp;</span>
788
+ <span class="cline-any cline-yes">422x</span>
789
+ <span class="cline-any cline-yes">68x</span>
790
+ <span class="cline-any cline-yes">354x</span>
791
+ <span class="cline-any cline-yes">8x</span>
792
+ <span class="cline-any cline-yes">346x</span>
793
+ <span class="cline-any cline-yes">38x</span>
794
+ <span class="cline-any cline-yes">308x</span>
795
+ <span class="cline-any cline-yes">126x</span>
796
+ <span class="cline-any cline-yes">182x</span>
797
+ <span class="cline-any cline-yes">91x</span>
798
+ <span class="cline-any cline-neutral">&nbsp;</span>
799
+ <span class="cline-any cline-neutral">&nbsp;</span>
800
+ <span class="cline-any cline-yes">91x</span>
801
+ <span class="cline-any cline-neutral">&nbsp;</span>
802
+ <span class="cline-any cline-neutral">&nbsp;</span>
803
+ <span class="cline-any cline-neutral">&nbsp;</span>
804
+ <span class="cline-any cline-yes">380x</span>
805
+ <span class="cline-any cline-neutral">&nbsp;</span>
806
+ <span class="cline-any cline-neutral">&nbsp;</span>
807
+ <span class="cline-any cline-yes">380x</span>
808
+ <span class="cline-any cline-neutral">&nbsp;</span>
809
+ <span class="cline-any cline-yes">380x</span>
810
+ <span class="cline-any cline-neutral">&nbsp;</span>
811
+ <span class="cline-any cline-neutral">&nbsp;</span>
812
+ <span class="cline-any cline-neutral">&nbsp;</span>
813
+ <span class="cline-any cline-yes">10x</span>
814
+ <span class="cline-any cline-neutral">&nbsp;</span>
815
+ <span class="cline-any cline-yes">10x</span>
543
816
  <span class="cline-any cline-neutral">&nbsp;</span>
544
817
  <span class="cline-any cline-neutral">&nbsp;</span>
545
- <span class="cline-any cline-yes">1x</span>
546
818
  <span class="cline-any cline-neutral">&nbsp;</span>
547
819
  <span class="cline-any cline-neutral">&nbsp;</span>
548
820
  <span class="cline-any cline-neutral">&nbsp;</span>
549
- <span class="cline-any cline-yes">4x</span>
550
- <span class="cline-any cline-yes">4x</span>
551
821
  <span class="cline-any cline-neutral">&nbsp;</span>
552
- <span class="cline-any cline-yes">4x</span>
553
822
  <span class="cline-any cline-neutral">&nbsp;</span>
554
- <span class="cline-any cline-yes">4x</span>
555
823
  <span class="cline-any cline-neutral">&nbsp;</span>
556
824
  <span class="cline-any cline-neutral">&nbsp;</span>
557
825
  <span class="cline-any cline-neutral">&nbsp;</span>
558
- <span class="cline-any cline-yes">402x</span>
559
- <span class="cline-any cline-yes">60x</span>
560
- <span class="cline-any cline-yes">342x</span>
561
826
  <span class="cline-any cline-yes">6x</span>
562
- <span class="cline-any cline-yes">336x</span>
563
- <span class="cline-any cline-yes">36x</span>
564
- <span class="cline-any cline-yes">300x</span>
565
- <span class="cline-any cline-yes">124x</span>
566
- <span class="cline-any cline-yes">176x</span>
567
- <span class="cline-any cline-yes">89x</span>
568
827
  <span class="cline-any cline-neutral">&nbsp;</span>
569
828
  <span class="cline-any cline-neutral">&nbsp;</span>
570
- <span class="cline-any cline-yes">87x</span>
571
829
  <span class="cline-any cline-neutral">&nbsp;</span>
572
830
  <span class="cline-any cline-neutral">&nbsp;</span>
831
+ <span class="cline-any cline-yes">1x</span>
832
+ <span class="cline-any cline-neutral">&nbsp;</span>
573
833
  <span class="cline-any cline-neutral">&nbsp;</span>
574
- <span class="cline-any cline-yes">374x</span>
575
834
  <span class="cline-any cline-neutral">&nbsp;</span>
576
835
  <span class="cline-any cline-neutral">&nbsp;</span>
577
- <span class="cline-any cline-yes">374x</span>
578
836
  <span class="cline-any cline-neutral">&nbsp;</span>
579
- <span class="cline-any cline-yes">374x</span>
580
837
  <span class="cline-any cline-neutral">&nbsp;</span>
581
838
  <span class="cline-any cline-neutral">&nbsp;</span>
839
+ <span class="cline-any cline-yes">9x</span>
582
840
  <span class="cline-any cline-neutral">&nbsp;</span>
583
- <span class="cline-any cline-yes">28x</span>
584
- <span class="cline-any cline-yes">28x</span>
841
+ <span class="cline-any cline-yes">9x</span>
842
+ <span class="cline-any cline-yes">2x</span>
585
843
  <span class="cline-any cline-neutral">&nbsp;</span>
586
- <span class="cline-any cline-yes">28x</span>
587
844
  <span class="cline-any cline-neutral">&nbsp;</span>
588
845
  <span class="cline-any cline-neutral">&nbsp;</span>
589
- <span class="cline-any cline-yes">447x</span>
590
846
  <span class="cline-any cline-neutral">&nbsp;</span>
591
847
  <span class="cline-any cline-neutral">&nbsp;</span>
592
- <span class="cline-any cline-yes">447x</span>
848
+ <span class="cline-any cline-yes">7x</span>
849
+ <span class="cline-any cline-yes">7x</span>
850
+ <span class="cline-any cline-yes">7x</span>
851
+ <span class="cline-any cline-yes">9x</span>
593
852
  <span class="cline-any cline-neutral">&nbsp;</span>
853
+ <span class="cline-any cline-yes">7x</span>
854
+ <span class="cline-any cline-yes">1x</span>
594
855
  <span class="cline-any cline-neutral">&nbsp;</span>
595
- <span class="cline-any cline-yes">447x</span>
596
856
  <span class="cline-any cline-neutral">&nbsp;</span>
597
857
  <span class="cline-any cline-neutral">&nbsp;</span>
598
- <span class="cline-any cline-yes">447x</span>
858
+ <span class="cline-any cline-neutral">&nbsp;</span>
859
+ <span class="cline-any cline-neutral">&nbsp;</span>
860
+ <span class="cline-any cline-yes">6x</span>
861
+ <span class="cline-any cline-neutral">&nbsp;</span>
862
+ <span class="cline-any cline-neutral">&nbsp;</span>
863
+ <span class="cline-any cline-neutral">&nbsp;</span>
864
+ <span class="cline-any cline-yes">42x</span>
865
+ <span class="cline-any cline-yes">42x</span>
866
+ <span class="cline-any cline-neutral">&nbsp;</span>
867
+ <span class="cline-any cline-yes">42x</span>
868
+ <span class="cline-any cline-neutral">&nbsp;</span>
869
+ <span class="cline-any cline-neutral">&nbsp;</span>
870
+ <span class="cline-any cline-yes">449x</span>
871
+ <span class="cline-any cline-neutral">&nbsp;</span>
872
+ <span class="cline-any cline-neutral">&nbsp;</span>
873
+ <span class="cline-any cline-yes">449x</span>
874
+ <span class="cline-any cline-neutral">&nbsp;</span>
875
+ <span class="cline-any cline-neutral">&nbsp;</span>
876
+ <span class="cline-any cline-yes">449x</span>
877
+ <span class="cline-any cline-neutral">&nbsp;</span>
878
+ <span class="cline-any cline-neutral">&nbsp;</span>
879
+ <span class="cline-any cline-yes">449x</span>
599
880
  <span class="cline-any cline-neutral">&nbsp;</span>
600
881
  <span class="cline-any cline-neutral">&nbsp;</span>
601
882
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -606,6 +887,50 @@
606
887
  <span class="cline-any cline-neutral">&nbsp;</span>
607
888
  <span class="cline-any cline-neutral">&nbsp;</span>
608
889
  <span class="cline-any cline-neutral">&nbsp;</span>
890
+ <span class="cline-any cline-yes">378x</span>
891
+ <span class="cline-any cline-yes">6x</span>
892
+ <span class="cline-any cline-yes">6x</span>
893
+ <span class="cline-any cline-yes">6x</span>
894
+ <span class="cline-any cline-neutral">&nbsp;</span>
895
+ <span class="cline-any cline-yes">6x</span>
896
+ <span class="cline-any cline-neutral">&nbsp;</span>
897
+ <span class="cline-any cline-yes">5x</span>
898
+ <span class="cline-any cline-no">&nbsp;</span>
899
+ <span class="cline-any cline-neutral">&nbsp;</span>
900
+ <span class="cline-any cline-neutral">&nbsp;</span>
901
+ <span class="cline-any cline-neutral">&nbsp;</span>
902
+ <span class="cline-any cline-neutral">&nbsp;</span>
903
+ <span class="cline-any cline-neutral">&nbsp;</span>
904
+ <span class="cline-any cline-yes">5x</span>
905
+ <span class="cline-any cline-yes">5x</span>
906
+ <span class="cline-any cline-yes">5x</span>
907
+ <span class="cline-any cline-neutral">&nbsp;</span>
908
+ <span class="cline-any cline-yes">5x</span>
909
+ <span class="cline-any cline-yes">9x</span>
910
+ <span class="cline-any cline-neutral">&nbsp;</span>
911
+ <span class="cline-any cline-yes">6x</span>
912
+ <span class="cline-any cline-yes">6x</span>
913
+ <span class="cline-any cline-neutral">&nbsp;</span>
914
+ <span class="cline-any cline-neutral">&nbsp;</span>
915
+ <span class="cline-any cline-yes">6x</span>
916
+ <span class="cline-any cline-no">&nbsp;</span>
917
+ <span class="cline-any cline-neutral">&nbsp;</span>
918
+ <span class="cline-any cline-neutral">&nbsp;</span>
919
+ <span class="cline-any cline-yes">6x</span>
920
+ <span class="cline-any cline-yes">1x</span>
921
+ <span class="cline-any cline-neutral">&nbsp;</span>
922
+ <span class="cline-any cline-neutral">&nbsp;</span>
923
+ <span class="cline-any cline-neutral">&nbsp;</span>
924
+ <span class="cline-any cline-yes">2x</span>
925
+ <span class="cline-any cline-neutral">&nbsp;</span>
926
+ <span class="cline-any cline-neutral">&nbsp;</span>
927
+ <span class="cline-any cline-neutral">&nbsp;</span>
928
+ <span class="cline-any cline-neutral">&nbsp;</span>
929
+ <span class="cline-any cline-neutral">&nbsp;</span>
930
+ <span class="cline-any cline-neutral">&nbsp;</span>
931
+ <span class="cline-any cline-yes">2x</span>
932
+ <span class="cline-any cline-neutral">&nbsp;</span>
933
+ <span class="cline-any cline-neutral">&nbsp;</span>
609
934
  <span class="cline-any cline-yes">372x</span>
610
935
  <span class="cline-any cline-yes">372x</span>
611
936
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -794,6 +1119,16 @@
794
1119
  <span class="cline-any cline-neutral">&nbsp;</span>
795
1120
  <span class="cline-any cline-no">&nbsp;</span>
796
1121
  <span class="cline-any cline-no">&nbsp;</span>
1122
+ <span class="cline-any cline-neutral">&nbsp;</span>
1123
+ <span class="cline-any cline-no">&nbsp;</span>
1124
+ <span class="cline-any cline-neutral">&nbsp;</span>
1125
+ <span class="cline-any cline-neutral">&nbsp;</span>
1126
+ <span class="cline-any cline-neutral">&nbsp;</span>
1127
+ <span class="cline-any cline-neutral">&nbsp;</span>
1128
+ <span class="cline-any cline-no">&nbsp;</span>
1129
+ <span class="cline-any cline-no">&nbsp;</span>
1130
+ <span class="cline-any cline-neutral">&nbsp;</span>
1131
+ <span class="cline-any cline-neutral">&nbsp;</span>
797
1132
  <span class="cline-any cline-no">&nbsp;</span>
798
1133
  <span class="cline-any cline-no">&nbsp;</span>
799
1134
  <span class="cline-any cline-no">&nbsp;</span>
@@ -803,6 +1138,9 @@
803
1138
  <span class="cline-any cline-no">&nbsp;</span>
804
1139
  <span class="cline-any cline-no">&nbsp;</span>
805
1140
  <span class="cline-any cline-no">&nbsp;</span>
1141
+ <span class="cline-any cline-neutral">&nbsp;</span>
1142
+ <span class="cline-any cline-no">&nbsp;</span>
1143
+ <span class="cline-any cline-no">&nbsp;</span>
806
1144
  <span class="cline-any cline-no">&nbsp;</span>
807
1145
  <span class="cline-any cline-no">&nbsp;</span>
808
1146
  <span class="cline-any cline-no">&nbsp;</span>
@@ -819,6 +1157,35 @@
819
1157
  <span class="cline-any cline-neutral">&nbsp;</span>
820
1158
  <span class="cline-any cline-neutral">&nbsp;</span>
821
1159
  <span class="cline-any cline-neutral">&nbsp;</span>
1160
+ <span class="cline-any cline-neutral">&nbsp;</span>
1161
+ <span class="cline-any cline-no">&nbsp;</span>
1162
+ <span class="cline-any cline-neutral">&nbsp;</span>
1163
+ <span class="cline-any cline-neutral">&nbsp;</span>
1164
+ <span class="cline-any cline-neutral">&nbsp;</span>
1165
+ <span class="cline-any cline-no">&nbsp;</span>
1166
+ <span class="cline-any cline-no">&nbsp;</span>
1167
+ <span class="cline-any cline-neutral">&nbsp;</span>
1168
+ <span class="cline-any cline-no">&nbsp;</span>
1169
+ <span class="cline-any cline-no">&nbsp;</span>
1170
+ <span class="cline-any cline-no">&nbsp;</span>
1171
+ <span class="cline-any cline-neutral">&nbsp;</span>
1172
+ <span class="cline-any cline-neutral">&nbsp;</span>
1173
+ <span class="cline-any cline-neutral">&nbsp;</span>
1174
+ <span class="cline-any cline-no">&nbsp;</span>
1175
+ <span class="cline-any cline-neutral">&nbsp;</span>
1176
+ <span class="cline-any cline-neutral">&nbsp;</span>
1177
+ <span class="cline-any cline-neutral">&nbsp;</span>
1178
+ <span class="cline-any cline-neutral">&nbsp;</span>
1179
+ <span class="cline-any cline-neutral">&nbsp;</span>
1180
+ <span class="cline-any cline-neutral">&nbsp;</span>
1181
+ <span class="cline-any cline-neutral">&nbsp;</span>
1182
+ <span class="cline-any cline-neutral">&nbsp;</span>
1183
+ <span class="cline-any cline-neutral">&nbsp;</span>
1184
+ <span class="cline-any cline-neutral">&nbsp;</span>
1185
+ <span class="cline-any cline-yes">1x</span>
1186
+ <span class="cline-any cline-yes">1x</span>
1187
+ <span class="cline-any cline-neutral">&nbsp;</span>
1188
+ <span class="cline-any cline-neutral">&nbsp;</span>
822
1189
  <span class="cline-any cline-no">&nbsp;</span>
823
1190
  <span class="cline-any cline-neutral">&nbsp;</span>
824
1191
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -849,23 +1216,72 @@
849
1216
  <span class="cline-any cline-no">&nbsp;</span>
850
1217
  <span class="cline-any cline-no">&nbsp;</span>
851
1218
  <span class="cline-any cline-no">&nbsp;</span>
1219
+ <span class="cline-any cline-no">&nbsp;</span>
1220
+ <span class="cline-any cline-no">&nbsp;</span>
852
1221
  <span class="cline-any cline-neutral">&nbsp;</span>
853
1222
  <span class="cline-any cline-no">&nbsp;</span>
1223
+ <span class="cline-any cline-no">&nbsp;</span>
1224
+ <span class="cline-any cline-no">&nbsp;</span>
1225
+ <span class="cline-any cline-no">&nbsp;</span>
1226
+ <span class="cline-any cline-no">&nbsp;</span>
1227
+ <span class="cline-any cline-no">&nbsp;</span>
1228
+ <span class="cline-any cline-no">&nbsp;</span>
1229
+ <span class="cline-any cline-no">&nbsp;</span>
1230
+ <span class="cline-any cline-neutral">&nbsp;</span>
854
1231
  <span class="cline-any cline-neutral">&nbsp;</span>
1232
+ <span class="cline-any cline-neutral">&nbsp;</span>
1233
+ <span class="cline-any cline-no">&nbsp;</span>
855
1234
  <span class="cline-any cline-no">&nbsp;</span>
856
1235
  <span class="cline-any cline-no">&nbsp;</span>
857
1236
  <span class="cline-any cline-no">&nbsp;</span>
858
1237
  <span class="cline-any cline-no">&nbsp;</span>
859
1238
  <span class="cline-any cline-no">&nbsp;</span>
860
1239
  <span class="cline-any cline-no">&nbsp;</span>
1240
+ <span class="cline-any cline-neutral">&nbsp;</span>
1241
+ <span class="cline-any cline-no">&nbsp;</span>
1242
+ <span class="cline-any cline-neutral">&nbsp;</span>
1243
+ <span class="cline-any cline-neutral">&nbsp;</span>
861
1244
  <span class="cline-any cline-no">&nbsp;</span>
862
1245
  <span class="cline-any cline-no">&nbsp;</span>
1246
+ <span class="cline-any cline-neutral">&nbsp;</span>
1247
+ <span class="cline-any cline-neutral">&nbsp;</span>
1248
+ <span class="cline-any cline-neutral">&nbsp;</span>
1249
+ <span class="cline-any cline-neutral">&nbsp;</span>
1250
+ <span class="cline-any cline-neutral">&nbsp;</span>
1251
+ <span class="cline-any cline-neutral">&nbsp;</span>
1252
+ <span class="cline-any cline-neutral">&nbsp;</span>
1253
+ <span class="cline-any cline-neutral">&nbsp;</span>
863
1254
  <span class="cline-any cline-no">&nbsp;</span>
1255
+ <span class="cline-any cline-neutral">&nbsp;</span>
1256
+ <span class="cline-any cline-neutral">&nbsp;</span>
1257
+ <span class="cline-any cline-neutral">&nbsp;</span>
1258
+ <span class="cline-any cline-neutral">&nbsp;</span>
1259
+ <span class="cline-any cline-neutral">&nbsp;</span>
1260
+ <span class="cline-any cline-neutral">&nbsp;</span>
1261
+ <span class="cline-any cline-neutral">&nbsp;</span>
1262
+ <span class="cline-any cline-neutral">&nbsp;</span>
1263
+ <span class="cline-any cline-neutral">&nbsp;</span>
1264
+ <span class="cline-any cline-neutral">&nbsp;</span>
1265
+ <span class="cline-any cline-neutral">&nbsp;</span>
1266
+ <span class="cline-any cline-neutral">&nbsp;</span>
864
1267
  <span class="cline-any cline-no">&nbsp;</span>
1268
+ <span class="cline-any cline-neutral">&nbsp;</span>
1269
+ <span class="cline-any cline-neutral">&nbsp;</span>
1270
+ <span class="cline-any cline-neutral">&nbsp;</span>
1271
+ <span class="cline-any cline-neutral">&nbsp;</span>
865
1272
  <span class="cline-any cline-no">&nbsp;</span>
1273
+ <span class="cline-any cline-neutral">&nbsp;</span>
1274
+ <span class="cline-any cline-neutral">&nbsp;</span>
866
1275
  <span class="cline-any cline-no">&nbsp;</span>
1276
+ <span class="cline-any cline-neutral">&nbsp;</span>
1277
+ <span class="cline-any cline-neutral">&nbsp;</span>
1278
+ <span class="cline-any cline-neutral">&nbsp;</span>
1279
+ <span class="cline-any cline-neutral">&nbsp;</span>
867
1280
  <span class="cline-any cline-no">&nbsp;</span>
868
1281
  <span class="cline-any cline-neutral">&nbsp;</span>
1282
+ <span class="cline-any cline-neutral">&nbsp;</span>
1283
+ <span class="cline-any cline-neutral">&nbsp;</span>
1284
+ <span class="cline-any cline-neutral">&nbsp;</span>
869
1285
  <span class="cline-any cline-no">&nbsp;</span>
870
1286
  <span class="cline-any cline-neutral">&nbsp;</span>
871
1287
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -922,6 +1338,10 @@ import type {
922
1338
  Commit,
923
1339
  CommitHash,
924
1340
  HistoryEntity,
1341
+ ParsedFeature,
1342
+ Expose,
1343
+ Force,
1344
+ Rule,
925
1345
  } from "@featurevisor/types";
926
1346
  &nbsp;
927
1347
  import { Adapter, DatafileOptions } from "./adapter";
@@ -929,6 +1349,18 @@ import { ProjectConfig } from "../config";
929
1349
  import { CustomParser } from "../parsers";
930
1350
  import { getCommit } from "../utils/git";
931
1351
  &nbsp;
1352
+ const FEATURE_ENVIRONMENT_ALLOWED_KEYS = ["rules", "force", "expose"];
1353
+ &nbsp;
1354
+ class FeatureSplitConfigError extends Error {
1355
+ featurevisorFilePath: string;
1356
+ &nbsp;
1357
+ constructor(filePath: string, message: string) {
1358
+ super(message);
1359
+ this.featurevisorFilePath = filePath;
1360
+ this.name = "FeatureSplitConfigError";
1361
+ }
1362
+ }
1363
+ &nbsp;
932
1364
  export function <span class="fstat-no" title="function not covered" >getExistingStateFilePath(</span>
933
1365
  projectConfig: ProjectConfig,
934
1366
  environment: EnvironmentKey | false,
@@ -945,8 +1377,8 @@ export function <span class="fstat-no" title="function not covered" >getRevision
945
1377
  export function getAllEntityFilePathsRecursively(directoryPath, extension) {
946
1378
  let entities: string[] = [];
947
1379
  &nbsp;
948
- <span class="missing-if-branch" title="if path not taken" >I</span>if (!fs.existsSync(directoryPath)) {
949
- <span class="cstat-no" title="statement not covered" > return entities;</span>
1380
+ if (!fs.existsSync(directoryPath)) {
1381
+ return entities;
950
1382
  }
951
1383
  &nbsp;
952
1384
  const files = fs.readdirSync(directoryPath);
@@ -965,6 +1397,12 @@ export function getAllEntityFilePathsRecursively(directoryPath, extension) {
965
1397
  return entities;
966
1398
  }
967
1399
  &nbsp;
1400
+ function <span class="fstat-no" title="function not covered" >isWithinDirectory(</span>directoryPath: string, fileDirectoryPath: string): boolean {
1401
+ <span class="cstat-no" title="statement not covered" > return (</span>
1402
+ fileDirectoryPath === directoryPath || fileDirectoryPath.startsWith(directoryPath + path.sep)
1403
+ );
1404
+ }
1405
+ &nbsp;
968
1406
  export class FilesystemAdapter extends Adapter {
969
1407
  private parser: CustomParser;
970
1408
  &nbsp;
@@ -1001,6 +1439,57 @@ export class FilesystemAdapter extends Adapter {
1001
1439
  &nbsp;
1002
1440
  return path.join(basePath, `${relativeEntityPath}.${this.parser.extension}`);
1003
1441
  }
1442
+ &nbsp;
1443
+ getFeatureEnvironmentPath(featureKey: string, environment: string): string {
1444
+ const relativeEntityPath = featureKey.replace(/\//g, path.sep);
1445
+ &nbsp;
1446
+ return path.join(
1447
+ this.config.environmentsDirectoryPath,
1448
+ environment,
1449
+ `${relativeEntityPath}.${this.parser.extension}`,
1450
+ );
1451
+ }
1452
+ &nbsp;
1453
+ private assertSplitByEnvironmentIsValidFeatureBase(
1454
+ baseFeature: ParsedFeature,
1455
+ featurePath: string,
1456
+ ) {
1457
+ if (
1458
+ typeof baseFeature.rules !== "undefined" ||
1459
+ typeof baseFeature.force !== "undefined" ||
1460
+ typeof baseFeature.expose !== "undefined"
1461
+ ) {
1462
+ throw new FeatureSplitConfigError(
1463
+ featurePath,
1464
+ `Feature "${baseFeature.key}" base file must not define rules, force, or expose when splitByEnvironment=true`,
1465
+ );
1466
+ }
1467
+ }
1468
+ &nbsp;
1469
+ private readFeatureEnvironmentEntity(featureKey: string, environment: string) {
1470
+ const featureEnvironmentPath = this.getFeatureEnvironmentPath(featureKey, environment);
1471
+ &nbsp;
1472
+ if (!fs.existsSync(featureEnvironmentPath)) {
1473
+ throw new FeatureSplitConfigError(
1474
+ featureEnvironmentPath,
1475
+ `Missing environment feature file: environments/${environment}/${featureKey}.${this.parser.extension}`,
1476
+ );
1477
+ }
1478
+ &nbsp;
1479
+ const content = fs.readFileSync(featureEnvironmentPath, "utf8");
1480
+ const parsed = this.parser.parse&lt;Record&lt;string, unknown&gt;&gt;(content, featureEnvironmentPath);
1481
+ const keys = Object.keys(parsed);
1482
+ const unknownKeys = keys.filter((key) =&gt; FEATURE_ENVIRONMENT_ALLOWED_KEYS.indexOf(key) === -1);
1483
+ &nbsp;
1484
+ if (unknownKeys.length &gt; 0) {
1485
+ throw new FeatureSplitConfigError(
1486
+ featureEnvironmentPath,
1487
+ `Unknown key(s) in environment feature file: ${unknownKeys.join(", ")}`,
1488
+ );
1489
+ }
1490
+ &nbsp;
1491
+ return parsed;
1492
+ }
1004
1493
  &nbsp;
1005
1494
  async listEntities(entityType: EntityType): Promise&lt;string[]&gt; {
1006
1495
  const directoryPath = this.getEntityDirectoryPath(entityType);
@@ -1029,6 +1518,50 @@ export class FilesystemAdapter extends Adapter {
1029
1518
  }
1030
1519
  &nbsp;
1031
1520
  async readEntity&lt;T&gt;(entityType: EntityType, entityKey: string): Promise&lt;T&gt; {
1521
+ if (entityType === "feature" &amp;&amp; this.config.splitByEnvironment) {
1522
+ const featurePath = this.getEntityPath(entityType, entityKey);
1523
+ const featureContent = fs.readFileSync(featurePath, "utf8");
1524
+ const baseFeature = this.parser.parse&lt;ParsedFeature&gt;(featureContent, featurePath);
1525
+ &nbsp;
1526
+ this.assertSplitByEnvironmentIsValidFeatureBase(baseFeature, featurePath);
1527
+ &nbsp;
1528
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (!Array.isArray(this.config.environments)) {
1529
+ <span class="cstat-no" title="statement not covered" > throw new FeatureSplitConfigError(</span>
1530
+ featurePath,
1531
+ "splitByEnvironment=true requires environments to be configured as an array",
1532
+ );
1533
+ }
1534
+ &nbsp;
1535
+ const rulesByEnvironment: Record&lt;string, Rule[]&gt; = {};
1536
+ const forceByEnvironment: Record&lt;string, Force[]&gt; = {};
1537
+ const exposeByEnvironment: Record&lt;string, Expose&gt; = {};
1538
+ &nbsp;
1539
+ for (const environment of this.config.environments) {
1540
+ const envFeature = this.readFeatureEnvironmentEntity(entityKey, environment);
1541
+ &nbsp;
1542
+ if (typeof envFeature.rules !== "undefined") {
1543
+ rulesByEnvironment[environment] = envFeature.rules as Rule[];
1544
+ }
1545
+ &nbsp;
1546
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (typeof envFeature.force !== "undefined") {
1547
+ <span class="cstat-no" title="statement not covered" > forceByEnvironment[environment] = envFeature.force as Force[];</span>
1548
+ }
1549
+ &nbsp;
1550
+ if (typeof envFeature.expose !== "undefined") {
1551
+ exposeByEnvironment[environment] = envFeature.expose as Expose;
1552
+ }
1553
+ }
1554
+ &nbsp;
1555
+ const mergedFeature: ParsedFeature = {
1556
+ ...baseFeature,
1557
+ rules: Object.keys(rulesByEnvironment).length &gt; 0 ? rulesByEnvironment : <span class="branch-1 cbranch-no" title="branch not covered" >undefined,</span>
1558
+ force: Object.keys(forceByEnvironment).length &gt; 0 ? <span class="branch-0 cbranch-no" title="branch not covered" >forceByEnvironment </span>: undefined,
1559
+ expose: Object.keys(exposeByEnvironment).length &gt; 0 ? exposeByEnvironment : undefined,
1560
+ };
1561
+ &nbsp;
1562
+ return mergedFeature as T;
1563
+ }
1564
+ &nbsp;
1032
1565
  const filePath = this.getEntityPath(entityType, entityKey);
1033
1566
  const entityContent = fs.readFileSync(filePath, "utf8");
1034
1567
  &nbsp;
@@ -1217,6 +1750,16 @@ export class FilesystemAdapter extends Adapter {
1217
1750
  &nbsp;
1218
1751
  <span class="cstat-no" title="statement not covered" > if (entityType &amp;&amp; entityKey) {</span>
1219
1752
  <span class="cstat-no" title="statement not covered" > pathPatterns = [this.getEntityPath(entityType, entityKey)];</span>
1753
+ &nbsp;
1754
+ <span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (</span>
1755
+ entityType === "feature" &amp;&amp;
1756
+ this.config.splitByEnvironment &amp;&amp;
1757
+ Array.isArray(this.config.environments)
1758
+ ) {
1759
+ <span class="cstat-no" title="statement not covered" > for (const environment of this.config.environments) {</span>
1760
+ <span class="cstat-no" title="statement not covered" > pathPatterns.push(this.getFeatureEnvironmentPath(entityKey, environment));</span>
1761
+ }
1762
+ }
1220
1763
  } else <span class="cstat-no" title="statement not covered" >if (entityType) {</span>
1221
1764
  <span class="cstat-no" title="statement not covered" > if (entityType === "attribute") {</span>
1222
1765
  <span class="cstat-no" title="statement not covered" > pathPatterns = [this.config.attributesDirectoryPath];</span>
@@ -1224,6 +1767,9 @@ export class FilesystemAdapter extends Adapter {
1224
1767
  <span class="cstat-no" title="statement not covered" > pathPatterns = [this.config.segmentsDirectoryPath];</span>
1225
1768
  } else <span class="cstat-no" title="statement not covered" >if (entityType === "feature") {</span>
1226
1769
  <span class="cstat-no" title="statement not covered" > pathPatterns = [this.config.featuresDirectoryPath];</span>
1770
+ <span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (this.config.splitByEnvironment) {</span>
1771
+ <span class="cstat-no" title="statement not covered" > pathPatterns.push(this.config.environmentsDirectoryPath);</span>
1772
+ }
1227
1773
  } else <span class="cstat-no" title="statement not covered" >if (entityType === "group") {</span>
1228
1774
  <span class="cstat-no" title="statement not covered" > pathPatterns = [this.config.groupsDirectoryPath];</span>
1229
1775
  } else <span class="cstat-no" title="statement not covered" >if (entityType === "schema") {</span>
@@ -1234,6 +1780,7 @@ export class FilesystemAdapter extends Adapter {
1234
1780
  } else {
1235
1781
  <span class="cstat-no" title="statement not covered" > pathPatterns = [</span>
1236
1782
  this.config.featuresDirectoryPath,
1783
+ ...(this.config.splitByEnvironment ? [this.config.environmentsDirectoryPath] : []),
1237
1784
  this.config.attributesDirectoryPath,
1238
1785
  this.config.segmentsDirectoryPath,
1239
1786
  this.config.groupsDirectoryPath,
@@ -1245,6 +1792,34 @@ export class FilesystemAdapter extends Adapter {
1245
1792
  <span class="cstat-no" title="statement not covered" > return pathPatterns.map(<span class="fstat-no" title="function not covered" >(p</span>) =&gt; <span class="cstat-no" title="statement not covered" >p.replace((this.rootDirectoryPath as string) + path.sep, ""))</span>;</span>
1246
1793
  }
1247
1794
  &nbsp;
1795
+ <span class="fstat-no" title="function not covered" > async </span>getFeatureSourcePaths(featureKey: string) {
1796
+ const baseFilePath = <span class="cstat-no" title="statement not covered" >this.getEntityPath("feature", featureKey);</span>
1797
+ const environmentFilePaths: Record&lt;string, string&gt; = <span class="cstat-no" title="statement not covered" >{};</span>
1798
+ &nbsp;
1799
+ <span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (this.config.splitByEnvironment &amp;&amp; Array.isArray(this.config.environments)) {</span>
1800
+ <span class="cstat-no" title="statement not covered" > for (const environment of this.config.environments) {</span>
1801
+ <span class="cstat-no" title="statement not covered" > environmentFilePaths[environment] = this.getFeatureEnvironmentPath(featureKey, environment);</span>
1802
+ }
1803
+ }
1804
+ &nbsp;
1805
+ <span class="cstat-no" title="statement not covered" > return {</span>
1806
+ baseFilePath,
1807
+ environmentFilePaths,
1808
+ };
1809
+ }
1810
+ &nbsp;
1811
+ async getFeaturePropertySourcePath(
1812
+ featureKey: string,
1813
+ _property: "rules" | "force" | "expose",
1814
+ environment?: string,
1815
+ ) {
1816
+ if (this.config.splitByEnvironment &amp;&amp; environment) {
1817
+ return this.getFeatureEnvironmentPath(featureKey, environment);
1818
+ }
1819
+ &nbsp;
1820
+ <span class="cstat-no" title="statement not covered" > return this.getEntityPath("feature", featureKey);</span>
1821
+ }
1822
+ &nbsp;
1248
1823
  <span class="fstat-no" title="function not covered" > async </span>listHistoryEntries(entityType?: EntityType, entityKey?: string): Promise&lt;HistoryEntry[]&gt; {
1249
1824
  const pathPatterns = <span class="cstat-no" title="statement not covered" >this.getPathPatterns(entityType, entityKey);</span>
1250
1825
  const rawHistory = <span class="cstat-no" title="statement not covered" >await this.getRawHistory(pathPatterns);</span>
@@ -1272,26 +1847,75 @@ export class FilesystemAdapter extends Adapter {
1272
1847
  const absolutePath = <span class="cstat-no" title="statement not covered" >path.join(this.rootDirectoryPath as string, relativePath);</span>
1273
1848
  const fileName = <span class="cstat-no" title="statement not covered" >absolutePath.split(path.sep).pop() as string;</span>
1274
1849
  const relativeDir = <span class="cstat-no" title="statement not covered" >path.dirname(absolutePath);</span>
1275
- &nbsp;
1276
- const key = <span class="cstat-no" title="statement not covered" >fileName.replace("." + this.parser.extension, "");</span>
1850
+ const extensionWithDot = <span class="cstat-no" title="statement not covered" >"." + this.parser.extension;</span>
1851
+ const key = <span class="cstat-no" title="statement not covered" >fileName.replace(extensionWithDot, "");</span>
1277
1852
  &nbsp;
1278
1853
  let type: EntityType = <span class="cstat-no" title="statement not covered" >"attribute";</span>
1279
- <span class="cstat-no" title="statement not covered" > if (relativeDir === this.config.attributesDirectoryPath) {</span>
1854
+ <span class="cstat-no" title="statement not covered" > if (isWithinDirectory(this.config.attributesDirectoryPath, relativeDir)) {</span>
1280
1855
  <span class="cstat-no" title="statement not covered" > type = "attribute";</span>
1281
- } else <span class="cstat-no" title="statement not covered" >if (relativeDir === this.config.segmentsDirectoryPath) {</span>
1856
+ } else <span class="cstat-no" title="statement not covered" >if (isWithinDirectory(this.config.segmentsDirectoryPath, relativeDir)) {</span>
1282
1857
  <span class="cstat-no" title="statement not covered" > type = "segment";</span>
1283
- } else <span class="cstat-no" title="statement not covered" >if (relativeDir === this.config.featuresDirectoryPath) {</span>
1858
+ } else <span class="cstat-no" title="statement not covered" >if (isWithinDirectory(this.config.featuresDirectoryPath, relativeDir)) {</span>
1284
1859
  <span class="cstat-no" title="statement not covered" > type = "feature";</span>
1285
- } else <span class="cstat-no" title="statement not covered" >if (relativeDir === this.config.groupsDirectoryPath) {</span>
1860
+ } else <span class="cstat-no" title="statement not covered" >if (</span>
1861
+ this.config.splitByEnvironment &amp;&amp;
1862
+ isWithinDirectory(this.config.environmentsDirectoryPath, relativeDir)
1863
+ ) {
1864
+ <span class="cstat-no" title="statement not covered" > type = "feature";</span>
1865
+ } else <span class="cstat-no" title="statement not covered" >if (isWithinDirectory(this.config.groupsDirectoryPath, relativeDir)) {</span>
1286
1866
  <span class="cstat-no" title="statement not covered" > type = "group";</span>
1287
- } else <span class="cstat-no" title="statement not covered" >if (relativeDir === this.config.schemasDirectoryPath) {</span>
1867
+ } else <span class="cstat-no" title="statement not covered" >if (isWithinDirectory(this.config.schemasDirectoryPath, relativeDir)) {</span>
1288
1868
  <span class="cstat-no" title="statement not covered" > type = "schema";</span>
1289
- } else <span class="cstat-no" title="statement not covered" >if (relativeDir === this.config.testsDirectoryPath) {</span>
1869
+ } else <span class="cstat-no" title="statement not covered" >if (isWithinDirectory(this.config.testsDirectoryPath, relativeDir)) {</span>
1290
1870
  <span class="cstat-no" title="statement not covered" > type = "test";</span>
1291
1871
  } else {
1292
1872
  <span class="cstat-no" title="statement not covered" > continue;</span>
1293
1873
  }
1294
1874
  &nbsp;
1875
+ <span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (type === "feature") {</span>
1876
+ <span class="cstat-no" title="statement not covered" > <span class="missing-if-branch" title="if path not taken" >I</span>if (</span>
1877
+ this.config.splitByEnvironment &amp;&amp;
1878
+ relativePath.startsWith(
1879
+ this.config.environmentsDirectoryPath.replace(
1880
+ (this.rootDirectoryPath as string) + path.sep,
1881
+ "",
1882
+ ) + path.sep,
1883
+ )
1884
+ ) {
1885
+ const featureRelativePath = <span class="cstat-no" title="statement not covered" >relativePath</span>
1886
+ .replace(
1887
+ this.config.environmentsDirectoryPath.replace(
1888
+ (this.rootDirectoryPath as string) + path.sep,
1889
+ "",
1890
+ ) + path.sep,
1891
+ "",
1892
+ )
1893
+ .split(path.sep)
1894
+ .slice(1)
1895
+ .join(path.sep)
1896
+ .replace(extensionWithDot, "");
1897
+ &nbsp;
1898
+ <span class="cstat-no" title="statement not covered" > entities.push({</span>
1899
+ type,
1900
+ key: featureRelativePath.replace(/\\/g, "/"),
1901
+ });
1902
+ &nbsp;
1903
+ <span class="cstat-no" title="statement not covered" > continue;</span>
1904
+ }
1905
+ &nbsp;
1906
+ const baseRelativePath = <span class="cstat-no" title="statement not covered" >absolutePath</span>
1907
+ .replace(this.config.featuresDirectoryPath + path.sep, "")
1908
+ .replace(extensionWithDot, "")
1909
+ .replace(/\\/g, "/");
1910
+ &nbsp;
1911
+ <span class="cstat-no" title="statement not covered" > entities.push({</span>
1912
+ type,
1913
+ key: baseRelativePath,
1914
+ });
1915
+ &nbsp;
1916
+ <span class="cstat-no" title="statement not covered" > continue;</span>
1917
+ }
1918
+ &nbsp;
1295
1919
  <span class="cstat-no" title="statement not covered" > entities.push({</span>
1296
1920
  type,
1297
1921
  key,
@@ -1339,7 +1963,7 @@ export class FilesystemAdapter extends Adapter {
1339
1963
  <div class='footer quiet pad2 space-top1 center small'>
1340
1964
  Code coverage generated by
1341
1965
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
1342
- at 2026-02-24T19:50:30.107Z
1966
+ at 2026-02-26T22:05:09.172Z
1343
1967
  </div>
1344
1968
  <script src="../prettify.js"></script>
1345
1969
  <script>