Repository: FHIR/sushi (ts)

Issue:
SUSHI not recognizing CodeSystem codes defined using ^concept
If an author defines codes in a CodeSystem using `^concept` like the following:
```
* ^concept[2].code = #101
* ^concept[2].display = "display 2"
* ^concept[2].definition = "definition 2"
```
then SUSHI does not recognize those codes when used in a ValueSet.  This is reproduced in FSH Online here: https://fshschool.org/FSHOnline/#/share/3vLYBUq

Although this is not the recommended way to create CodeSystems, it still should be supported.

See: https://chat.fhir.org/#narrow/stream/215610-shorthand/topic/sushi.20suddenly.20failing.20on.20cs.2Fvs.20codes.20post.20upgrade/near/274386821

Pull request description:
Find concepts added by CaretValueRules in CodeSystems
Fixes #1039 and completes task [CIMPL-912](https://standardhealthrecord.atlassian.net/browse/CIMPL-912).

When defining a CodeSystem, concepts may be added by ConceptRules or CaretValueRules. ValueSets that refer to the CodeSystem can add those concepts, regardless of what rules were used to add them.

Patch:
diff --git a/src/fhirtypes/common.ts b/src/fhirtypes/common.ts
index f5c658a..b68e99b 100644
--- a/src/fhirtypes/common.ts
+++ b/src/fhirtypes/common.ts
@@ -340,6 +340,7 @@ export function listUndefinedLocalCodes(
 ): void {
   let undefinedCodes: string[] = [];
   applyInsertRules(codeSystem, tank);
+  const conceptRulePath = /^(concept(\[\s*(\d+|\+|=)\s*\])?\.)+code$/;
   // if the CodeSystem content is complete, a code not present in this system should be listed as undefined.
   // if the CodeSystem content is not complete, then do not list any code as undefined.
   // in a FshCodeSystem, content is complete by default, so make sure it isn't set to something else.
@@ -355,8 +356,18 @@ export function listUndefinedLocalCodes(
         rule.value.code !== 'complete'
     )
   ) {
+    // a concept may have been added by a ConceptRule or by a CaretValueRule.
+    // while ConceptRule is strongly preferred, CaretValueRule is still allowed.
     undefinedCodes = codes.filter(code => {
-      return !codeSystem.rules.some(rule => rule instanceof ConceptRule && rule.code === code);
+      return !codeSystem.rules.some(
+        rule =>
+          (rule instanceof ConceptRule && rule.code === code) ||
+          (rule instanceof CaretValueRule &&
+            rule.path === '' &&
+            conceptRulePath.test(rule.caretPath) &&
+            rule.value instanceof FshCode &&
+            rule.value.code === code)
+      );
     });
   } else if (
     codeSystem instanceof Instance &&
@@ -369,7 +380,6 @@ export function listUndefinedLocalCodes(
         rule.value.code === 'complete'
     )
   ) {
-    const conceptRulePath = /^(concept(\[(\d+|\+|=)\])?\.)+code$/;
     undefinedCodes = codes.filter(code => {
       return !codeSystem.rules.some(
         rule =>


Test patch:
diff --git a/test/export/ValueSetExporter.test.ts b/test/export/ValueSetExporter.test.ts
index b51782e..c757348 100644
--- a/test/export/ValueSetExporter.test.ts
+++ b/test/export/ValueSetExporter.test.ts
@@ -491,6 +491,54 @@ describe('ValueSetExporter', () => {
     expect(loggerSpy.getAllMessages('error')).toHaveLength(0);
   });
 
+  it('should export a value set that includes a concept component from a local complete code system name with concepts added by CaretValueRules', () => {
+    const valueSet = new FshValueSet('DinnerVS');
+    const component = new ValueSetConceptComponentRule(true);
+    component.from = { system: 'FoodCS' };
+    component.concepts.push(new FshCode('Pizza', 'FoodCS', 'Delicious pizza to share.'));
+    component.concepts.push(new FshCode('Salad', 'FoodCS', 'Plenty of fresh vegetables.'));
+    valueSet.rules.push(component);
+    doc.valueSets.set(valueSet.name, valueSet);
+    const foodCS = new FshCodeSystem('FoodCS');
+    foodCS.id = 'food';
+    const pizzaCode = new CaretValueRule('');
+    pizzaCode.caretPath = 'concept[0].code';
+    pizzaCode.value = new FshCode('Pizza');
+    const pizzaDisplay = new CaretValueRule('');
+    pizzaDisplay.caretPath = 'concept[0].display';
+    pizzaDisplay.value = 'Delicious pizza to share.';
+    const saladCode = new CaretValueRule('');
+    saladCode.caretPath = 'concept[ 1 ].code';
+    saladCode.value = new FshCode('Salad');
+    const saladDisplay = new CaretValueRule('');
+    saladDisplay.caretPath = 'concept[1].display';
+    saladDisplay.value = 'Plenty of fresh vegetables.';
+    foodCS.rules.push(pizzaCode, pizzaDisplay, saladCode, saladDisplay);
+    doc.codeSystems.set(foodCS.name, foodCS);
+    const exported = exporter.export().valueSets;
+    expect(exported.length).toBe(1);
+    expect(exported[0]).toEqual({
+      resourceType: 'ValueSet',
+      name: 'DinnerVS',
+      id: 'DinnerVS',
+      status: 'active',
+      url: 'http://hl7.org/fhir/us/minimal/ValueSet/DinnerVS',
+      version: '1.0.0',
+      compose: {
+        include: [
+          {
+            system: 'http://hl7.org/fhir/us/minimal/CodeSystem/food',
+            concept: [
+              { code: 'Pizza', display: 'Delicious pizza to share.' },
+              { code: 'Salad', display: 'Plenty of fresh vegetables.' }
+            ]
+          }
+        ]
+      }
+    });
+    expect(loggerSpy.getAllMessages('error')).toHaveLength(0);
+  });
+
   it('should export a value set that includes a concept component from a local complete code system name with at least one concept added by a RuleSet', () => {
     const valueSet = new FshValueSet('DinnerVS');
     const component = new ValueSetConceptComponentRule(true);