@gefyra/diffyr6-cli 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gefyra/diffyr6-cli",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "FHIR R4 to R6 migration pipeline runner with automated profile comparison and rule-based analysis",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -54,10 +54,13 @@ export async function compareTerminology(resourcesDir, resourcesR6Dir, outputDir
54
54
  console.log(` Found ${findings.length} profile(s) with binding differences`);
55
55
 
56
56
  // Identify common bindings across all profiles
57
- const commonBindings = identifyCommonBindings(findings);
57
+ const commonBindings = sortFindings(identifyCommonBindings(findings));
58
58
 
59
59
  // Remove common bindings from individual profiles
60
- const filteredFindings = removeCommonBindingsFromProfiles(findings, commonBindings);
60
+ const filteredFindings = removeCommonBindingsFromProfiles(findings, commonBindings).map(profile => ({
61
+ ...profile,
62
+ findings: sortFindings(profile.findings),
63
+ }));
61
64
 
62
65
  // Generate reports
63
66
  const timestamp = new Date().toISOString().replace(/[-:]/g, '').replace(/\..+/, '').replace('T', '-');
@@ -280,6 +283,7 @@ async function compareProfileBindings(r4Profile, r6Profile, options = {}) {
280
283
  findings.push({
281
284
  type: 'new-binding',
282
285
  path,
286
+ isMustSupport: isMustSupportElement(r6Elem),
283
287
  r6Binding: r6Elem.binding,
284
288
  });
285
289
  continue;
@@ -297,6 +301,7 @@ async function compareProfileBindings(r4Profile, r6Profile, options = {}) {
297
301
  const finding = {
298
302
  type: 'strength-and-valueset-change',
299
303
  path,
304
+ isMustSupport: isMustSupportElement(r4Elem) || isMustSupportElement(r6Elem),
300
305
  r4Strength: r4Binding.strength,
301
306
  r6Strength: r6Binding.strength,
302
307
  r4ValueSet: r4Binding.valueSet,
@@ -328,6 +333,7 @@ async function compareProfileBindings(r4Profile, r6Profile, options = {}) {
328
333
  findings.push({
329
334
  type: 'strength-change',
330
335
  path,
336
+ isMustSupport: isMustSupportElement(r4Elem) || isMustSupportElement(r6Elem),
331
337
  r4Strength: r4Binding.strength,
332
338
  r6Strength: r6Binding.strength,
333
339
  r4ValueSet: r4Binding.valueSet,
@@ -341,6 +347,7 @@ async function compareProfileBindings(r4Profile, r6Profile, options = {}) {
341
347
  findings.push({
342
348
  type: 'strength-change',
343
349
  path,
350
+ isMustSupport: isMustSupportElement(r4Elem) || isMustSupportElement(r6Elem),
344
351
  r4Strength: r4Binding.strength,
345
352
  r6Strength: r6Binding.strength,
346
353
  r4ValueSet: r4Binding.valueSet,
@@ -351,6 +358,7 @@ async function compareProfileBindings(r4Profile, r6Profile, options = {}) {
351
358
  const finding = {
352
359
  type: 'valueset-change',
353
360
  path,
361
+ isMustSupport: isMustSupportElement(r4Elem) || isMustSupportElement(r6Elem),
354
362
  r4ValueSet: r4Binding.valueSet,
355
363
  r6ValueSet: r6Binding.valueSet,
356
364
  r4Strength: r4Binding.strength,
@@ -392,6 +400,7 @@ async function compareProfileBindings(r4Profile, r6Profile, options = {}) {
392
400
  findings.push({
393
401
  type: 'removed-binding',
394
402
  path,
403
+ isMustSupport: isMustSupportElement(r4Elem),
395
404
  r4Binding: r4Elem.binding,
396
405
  });
397
406
  }
@@ -400,6 +409,10 @@ async function compareProfileBindings(r4Profile, r6Profile, options = {}) {
400
409
  return findings;
401
410
  }
402
411
 
412
+ function isMustSupportElement(element) {
413
+ return element?.mustSupport === true;
414
+ }
415
+
403
416
  /**
404
417
  * Check if valueSet URL has a version (pipe notation)
405
418
  */
@@ -756,6 +769,38 @@ function groupFindingsByType(findings) {
756
769
  return byType;
757
770
  }
758
771
 
772
+ function sortFindings(findings) {
773
+ return [...findings].sort((left, right) => {
774
+ if ((left.isMustSupport === true) !== (right.isMustSupport === true)) {
775
+ return left.isMustSupport === true ? -1 : 1;
776
+ }
777
+ const pathCompare = (left.path || '').localeCompare(right.path || '');
778
+ if (pathCompare !== 0) {
779
+ return pathCompare;
780
+ }
781
+ return (left.type || '').localeCompare(right.type || '');
782
+ });
783
+ }
784
+
785
+ function splitFindingsByMustSupport(findings) {
786
+ const mustSupport = [];
787
+ const others = [];
788
+
789
+ for (const finding of findings) {
790
+ if (finding.isMustSupport === true) {
791
+ mustSupport.push(finding);
792
+ } else {
793
+ others.push(finding);
794
+ }
795
+ }
796
+
797
+ return { mustSupport, others };
798
+ }
799
+
800
+ function appendFindingMetadata(lines, finding) {
801
+ lines.push(`- Must Support: ${finding.isMustSupport === true ? 'yes' : 'no'}`);
802
+ }
803
+
759
804
  /**
760
805
  * Append findings to markdown lines
761
806
  */
@@ -767,6 +812,7 @@ function appendFindingsToMarkdown(lines, byType) {
767
812
 
768
813
  for (const f of byType['strength-and-valueset-change']) {
769
814
  lines.push(`**${f.path}**`);
815
+ appendFindingMetadata(lines, f);
770
816
  lines.push(`- Strength: \`${f.r4Strength}\` → \`${f.r6Strength}\``);
771
817
  lines.push(`- R4 ValueSet: ${f.r4ValueSet || 'none'}`);
772
818
  lines.push(`- R6 ValueSet: ${f.r6ValueSet || 'none'}`);
@@ -818,6 +864,7 @@ function appendFindingsToMarkdown(lines, byType) {
818
864
 
819
865
  for (const f of byType['strength-change']) {
820
866
  lines.push(`**${f.path}**`);
867
+ appendFindingMetadata(lines, f);
821
868
  lines.push(`- Strength: \`${f.r4Strength}\` → \`${f.r6Strength}\``);
822
869
  if (f.r4ValueSet) {
823
870
  lines.push(`- ValueSet (R4): ${f.r4ValueSet}`);
@@ -836,6 +883,7 @@ function appendFindingsToMarkdown(lines, byType) {
836
883
 
837
884
  for (const f of byType['valueset-change']) {
838
885
  lines.push(`**${f.path}**`);
886
+ appendFindingMetadata(lines, f);
839
887
  lines.push(`- R4 ValueSet: ${f.r4ValueSet || 'none'}`);
840
888
  lines.push(`- R6 ValueSet: ${f.r6ValueSet || 'none'}`);
841
889
 
@@ -890,6 +938,7 @@ function appendFindingsToMarkdown(lines, byType) {
890
938
 
891
939
  for (const f of byType['new-binding']) {
892
940
  lines.push(`**${f.path}**`);
941
+ appendFindingMetadata(lines, f);
893
942
  if (f.r6Binding?.valueSet) {
894
943
  lines.push(`- ValueSet: ${f.r6Binding.valueSet}`);
895
944
  }
@@ -907,6 +956,7 @@ function appendFindingsToMarkdown(lines, byType) {
907
956
 
908
957
  for (const f of byType['removed-binding']) {
909
958
  lines.push(`**${f.path}**`);
959
+ appendFindingMetadata(lines, f);
910
960
  if (f.r4Binding?.valueSet) {
911
961
  lines.push(`- ValueSet (R4): ${f.r4Binding.valueSet}`);
912
962
  }
@@ -923,11 +973,24 @@ function appendFindingsToMarkdown(lines, byType) {
923
973
  */
924
974
  function generateTerminologyReport(findings, commonBindings = []) {
925
975
  const lines = [];
976
+ const sortedProfiles = sortProfilesAlphabetically(findings);
977
+ const mustSupportProfiles = sortedProfiles
978
+ .map(profile => ({
979
+ ...profile,
980
+ findings: profile.findings.filter(finding => finding.isMustSupport === true),
981
+ }))
982
+ .filter(profile => profile.findings.length > 0);
983
+ const otherProfiles = sortedProfiles
984
+ .map(profile => ({
985
+ ...profile,
986
+ findings: profile.findings.filter(finding => finding.isMustSupport !== true),
987
+ }))
988
+ .filter(profile => profile.findings.length > 0);
926
989
 
927
990
  lines.push('# Terminology Binding Comparison Report');
928
991
  lines.push('');
929
992
  lines.push(`**Generated:** ${new Date().toISOString()}`);
930
- lines.push(`**Profiles with Differences:** ${findings.length}`);
993
+ lines.push(`**Profiles with Differences:** ${sortedProfiles.length}`);
931
994
  if (commonBindings.length > 0) {
932
995
  lines.push(`**Common Bindings Across All Profiles:** ${commonBindings.length}`);
933
996
  }
@@ -943,34 +1006,75 @@ function generateTerminologyReport(findings, commonBindings = []) {
943
1006
  lines.push('');
944
1007
  lines.push('The following binding changes occur in **all** profiles:');
945
1008
  lines.push('');
946
-
947
- const groupedCommon = groupFindingsByType(commonBindings);
948
- appendFindingsToMarkdown(lines, groupedCommon);
1009
+
1010
+ appendMustSupportSections(lines, commonBindings);
949
1011
 
950
1012
  lines.push('---');
951
1013
  lines.push('');
952
1014
  }
953
1015
 
954
- if (findings.length === 0) {
1016
+ if (sortedProfiles.length === 0) {
955
1017
  lines.push('✅ **No profile-specific binding differences found.**');
956
1018
  lines.push('');
957
1019
  return lines.join('\n');
958
1020
  }
959
-
960
- for (const profile of findings) {
961
- lines.push(`## ${profile.profileName}`);
1021
+
1022
+ if (mustSupportProfiles.length > 0) {
1023
+ lines.push('## Must Support');
962
1024
  lines.push('');
963
- lines.push(`- **R4 URL:** ${profile.r4Url}`);
964
- lines.push(`- **R6 URL:** ${profile.r6Url}`);
965
- lines.push(`- **Differences:** ${profile.findings.length}`);
1025
+ lines.push('The following profiles contain binding differences on Must-Support elements:');
966
1026
  lines.push('');
967
-
968
- const groupedFindings = groupFindingsByType(profile.findings);
969
- appendFindingsToMarkdown(lines, groupedFindings);
970
-
971
- lines.push('---');
1027
+
1028
+ for (const profile of mustSupportProfiles) {
1029
+ appendProfileSection(lines, profile);
1030
+ }
1031
+ }
1032
+
1033
+ if (otherProfiles.length > 0) {
1034
+ lines.push('## Non Must Support');
1035
+ lines.push('');
1036
+ lines.push('The following profiles contain binding differences on elements without Must Support:');
972
1037
  lines.push('');
1038
+
1039
+ for (const profile of otherProfiles) {
1040
+ appendProfileSection(lines, profile);
1041
+ }
973
1042
  }
974
1043
 
975
1044
  return lines.join('\n');
976
1045
  }
1046
+
1047
+ function appendMustSupportSections(lines, findings) {
1048
+ const sortedFindings = sortFindings(findings);
1049
+ const { mustSupport, others } = splitFindingsByMustSupport(sortedFindings);
1050
+
1051
+ if (mustSupport.length > 0) {
1052
+ lines.push('### Must Support Elements');
1053
+ lines.push('');
1054
+ appendFindingsToMarkdown(lines, groupFindingsByType(mustSupport));
1055
+ }
1056
+
1057
+ if (others.length > 0) {
1058
+ lines.push('### Other Elements');
1059
+ lines.push('');
1060
+ appendFindingsToMarkdown(lines, groupFindingsByType(others));
1061
+ }
1062
+ }
1063
+
1064
+ function appendProfileSection(lines, profile) {
1065
+ lines.push(`### ${profile.profileName}`);
1066
+ lines.push('');
1067
+ lines.push(`- **R4 URL:** ${profile.r4Url}`);
1068
+ lines.push(`- **R6 URL:** ${profile.r6Url}`);
1069
+ lines.push(`- **Differences:** ${profile.findings.length}`);
1070
+ lines.push('');
1071
+ appendFindingsToMarkdown(lines, groupFindingsByType(sortFindings(profile.findings)));
1072
+ lines.push('---');
1073
+ lines.push('');
1074
+ }
1075
+
1076
+ function sortProfilesAlphabetically(profiles) {
1077
+ return [...profiles].sort((left, right) =>
1078
+ (left.profileName || '').localeCompare(right.profileName || '')
1079
+ );
1080
+ }