From 1cf4485ad333f9840ec14c21eaa47e03b679077a Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 26 Feb 2026 17:50:41 +1100 Subject: [PATCH 1/6] Added content to TerminologyCapabilities.codeSystem --- tx/cs/cs-api.js | 4 ++++ tx/workers/metadata.js | 13 ++++++++++--- tx/xversion/xv-terminologyCapabilities.js | 22 +++++++++++++++++++++- utilities/icd9cm-parser.js | 0 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 utilities/icd9cm-parser.js diff --git a/tx/cs/cs-api.js b/tx/cs/cs-api.js index 961f6f0..606c8c5 100644 --- a/tx/cs/cs-api.js +++ b/tx/cs/cs-api.js @@ -785,6 +785,10 @@ class CodeSystemFactoryProvider { */ version() { throw new Error("Must override"); } + content() { + return "complete"; + } + getPartialVersion() { let ver = this.version(); if (ver && VersionUtilities.isSemVer(ver)) { diff --git a/tx/workers/metadata.js b/tx/workers/metadata.js index 6c4421c..2eebf4d 100644 --- a/tx/workers/metadata.js +++ b/tx/workers/metadata.js @@ -325,9 +325,10 @@ class MetadataHandler { for (const cs of provider.codeSystems.values()) { const url = cs.url || (cs.jsonObj && cs.jsonObj.url); const version = cs.version || (cs.jsonObj && cs.jsonObj.version); + const content = cs.content || cs.jsonObj.content; if (url) { - this.addCodeSystemEntry(seenSystems, url, version); + this.addCodeSystemEntry(seenSystems, url, version, content); } } } @@ -339,7 +340,7 @@ class MetadataHandler { const version = factory.version(); if (url) { - this.addCodeSystemEntry(seenSystems, url, version); + this.addCodeSystemEntry(seenSystems, url, version, factory.content()); } } } @@ -357,13 +358,16 @@ class MetadataHandler { * @param {string} url - Code system URL * @param {string} version - Code system version (may be null) */ - addCodeSystemEntry(seenSystems, url, version) { + addCodeSystemEntry(seenSystems, url, version, content) { if (!seenSystems.has(url)) { // Create new entry const entry = { uri: url }; if (version) { entry.version = [{ code: version }]; } + if (content) { + entry.content = content; + } seenSystems.set(url, entry); } else if (version) { // Add version to existing entry @@ -371,6 +375,9 @@ class MetadataHandler { if (!entry.version) { entry.version = []; } + if (content) { + entry.content = content; + } // Check if version already exists if (!entry.version.some(v => v.code === version)) { entry.version.push({ code: version }); diff --git a/tx/xversion/xv-terminologyCapabilities.js b/tx/xversion/xv-terminologyCapabilities.js index c6da2c9..edad4bb 100644 --- a/tx/xversion/xv-terminologyCapabilities.js +++ b/tx/xversion/xv-terminologyCapabilities.js @@ -1,4 +1,5 @@ const {VersionUtilities} = require("../../library/version-utilities"); +const {Extensions} = require("../library/extensions"); /** * Converts input TerminologyCapabilities to R5 format (modifies input object for performance) @@ -14,7 +15,17 @@ function terminologyCapabilitiesToR5(jsonObj, sourceVersion) { } if (VersionUtilities.isR4Ver(sourceVersion)) { - // R4 to R5: No major structural changes needed for TerminologyCapabilities + for (const cs of jsonObj.codeSystem || []) { + if (cs.content) { + let cnt = Extensions.readString("http://hl7.org/fhir/5.0/StructureDefinition/extension-TerminologyCapabilities.codeSystem.content"); + if (cnt) { + delete cs.extensions; + cs.content = cnt; + } + } + } + + return jsonObj; } @@ -125,6 +136,15 @@ function terminologyCapabilitiesR5ToR4(r5Obj) { if (r5Obj.versionAlgorithmCoding) { delete r5Obj.versionAlgorithmCoding; } + for (const cs of r5Obj.codeSystem || []) { + if (cs.content) { + if (!cs.extension) { + cs.extension = []; + } + cs.extension.push({"url" : "http://hl7.org/fhir/5.0/StructureDefinition/extension-TerminologyCapabilities.codeSystem.content", valueCode : cs.content}); + delete cs.content; + } + } return r5Obj; } diff --git a/utilities/icd9cm-parser.js b/utilities/icd9cm-parser.js new file mode 100644 index 0000000..e69de29 From 7ef0df40fdeaf14972cc64def92d3e04e4ce050a Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 26 Feb 2026 17:50:55 +1100 Subject: [PATCH 2/6] fix LOINC list filter handling --- tx/cs/cs-loinc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tx/cs/cs-loinc.js b/tx/cs/cs-loinc.js index fc718c3..d1785bb 100644 --- a/tx/cs/cs-loinc.js +++ b/tx/cs/cs-loinc.js @@ -670,7 +670,7 @@ class LoincServices extends BaseCSServices { sql = `SELECT DISTINCT TargetKey as Key FROM Relationships WHERE RelationshipTypeKey = ${this.relationships.get('Answer')} AND SourceKey IN (SELECT CodeKey FROM Codes WHERE Code = '${this.#sqlWrapString(value)}') - ORDER BY SourceKey ASC`; + ORDER BY TargetKey ASC`; lsql = `SELECT COUNT(DISTINCT TargetKey) FROM Relationships WHERE RelationshipTypeKey = ${this.relationships.get('Answer')} AND SourceKey IN (SELECT CodeKey FROM Codes WHERE Code = '${this.#sqlWrapString(value)}') From 76137027221e4073ad4a5a7d7e0228cdd8abac64 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 26 Feb 2026 17:52:07 +1100 Subject: [PATCH 3/6] Improve Diagnostic Logging --- tx/library/renderer.js | 30 ++++++++++++++++++------------ tx/workers/expand.js | 2 +- tx/workers/validate.js | 7 ++++--- tx/workers/worker.js | 2 +- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/tx/library/renderer.js b/tx/library/renderer.js index 86ca7b4..6719da4 100644 --- a/tx/library/renderer.js +++ b/tx/library/renderer.js @@ -22,13 +22,15 @@ class Renderer { displayCoded(...args) { if (args.length === 1) { const arg = args[0]; - if (arg.systemUri !== undefined && arg.version !== undefined && arg.code !== undefined && arg.display !== undefined) { + if (arg instanceof CodeSystemProvider) { + return arg.system() + "|" + arg.version(); + } else if (arg.system !== undefined && arg.version !== undefined && arg.code !== undefined && arg.display !== undefined) { // It's a Coding return this.displayCodedCoding(arg); } else if (arg.coding !== undefined || arg.text) { // It's a CodeableConcept return this.displayCodedCodeableConcept(arg); - } else if (arg.systemUri !== undefined && arg.version !== undefined) { + } else if (arg.system !== undefined && arg.version !== undefined) { // It's a CodeSystemProvider return this.displayCodedProvider(arg); } else if (arg instanceof CodeSystemProvider) { @@ -46,7 +48,7 @@ class Renderer { } displayCodedProvider(system) { - let result = system.systemUri + '|' + system.version; + let result = system.system + '|' + system.version; if (system.sourcePackage) { result = result + ' (from ' + system.sourcePackage + ')'; } @@ -70,7 +72,7 @@ class Renderer { } displayCodedCoding(code) { - return this.displayCodedSystemVersionCodeDisplay(code.systemUri, code.version, code.code, code.display); + return this.displayCodedSystemVersionCodeDisplay(code.system, code.version, code.code, code.display); } displayCodedCodeableConcept(code) { @@ -90,12 +92,12 @@ class Renderer { displayValueSetInclude(inc) { let result; - if (inc.systemUri) { - result = '(' + inc.systemUri + ')'; - if (inc.hasConcepts) { + if (inc.system) { + result = '(' + inc.system + ')'; + if (inc.concept) { result = result + '('; let first = true; - for (const cc of inc.concepts) { + for (const cc of inc.concept) { if (first) { first = false; } else { @@ -105,23 +107,23 @@ class Renderer { } result = result + ')'; } - if (inc.hasFilters) { + if (inc.filter) { result = result + '('; let first = true; - for (const ci of inc.filters) { + for (const ci of inc.filter) { if (first) { first = false; } else { result = result + ','; } - result = result + ci.prop + ci.op + ci.value; + result = result + ci.property + ci.op + ci.value; } result = result + ')'; } } else { result = '('; let first = true; - for (const s of inc.valueSets || []) { + for (const s of inc.valueSet || []) { if (first) { first = false; } else { @@ -1557,6 +1559,10 @@ class Renderer { // No versions specified await this.renderLink(li, cs.uri); } + let content = cs.content || Extensions.readString(cs, "http://hl7.org/fhir/4.0/StructureDefinition/extension-TerminologyCapabilities.codeSystem.content"); + if (content && content != "complete") { + li.tx(" (" + content + ")"); + } } } } diff --git a/tx/workers/expand.js b/tx/workers/expand.js index 7e8b433..a7d2d3e 100644 --- a/tx/workers/expand.js +++ b/tx/workers/expand.js @@ -632,7 +632,7 @@ class ValueSetExpander { } else if (cs.contentMode() === 'supplement') { throw new Issue('error', 'business-rule', null, null, 'The code system definition for ' + cset.system + ' defines a supplement, so this expansion cannot be performed', 'invalid'); } else { - this.addParamUri(exp, cs.contentMode(), cs.system + '|' + cs.version); + this.addParamUri(exp, cs.contentMode(), cs.system() + '|' + cs.version()); Extensions.addString(exp, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed", "This extension is based on a fragment of the code system " + cset.system); } diff --git a/tx/workers/validate.js b/tx/workers/validate.js index b697627..8300856 100644 --- a/tx/workers/validate.js +++ b/tx/workers/validate.js @@ -44,6 +44,7 @@ class ValueSetChecker { valueSet; params; others = new Map(); + indentCount = 0; constructor(worker, valueSet, params) { validateParameter(worker, "worker", TerminologyWorker); @@ -302,8 +303,8 @@ class ValueSetChecker { this.seeValueSet(); this.worker.opContext.addNote(this.valueSet, 'Analysing ' + this.valueSet.vurl + ' for validation purposes', this.indentCount); if (this.indentCount === 0) { - this.worker.opContext.addNote(this.valueSet, 'Parameters: ' + this.params.summary, this.indentCount); - let vrs = this.params.verSummary; + this.worker.opContext.addNote(this.valueSet, 'Parameters: ' + this.params.summary(), this.indentCount); + let vrs = this.params.verSummary(); if (vrs) { this.worker.opContext.addNote(this.valueSet, 'Version Rules: ' + vrs, this.indentCount); } @@ -1731,7 +1732,7 @@ class ValueSetChecker { let list = []; for (let filter of cset.filter) { let s = cset.filter.length > 1 ? "(" : ""; - s = filter.prop+" "+filter.op+" "+filter.value; + s = filter.property+" "+filter.op+" "+filter.value; s = s + (cset.filter.length > 1 ? ")" : ""); list.push(s) } diff --git a/tx/workers/worker.js b/tx/workers/worker.js index 028072e..0a59f7d 100644 --- a/tx/workers/worker.js +++ b/tx/workers/worker.js @@ -316,7 +316,7 @@ class TerminologyWorker { for (const ext of supplementExtensions) { const supplementUrl = ext.valueString || ext.valueUri; if (supplementUrl && !cs.hasSupplement(this.opContext, supplementUrl)) { - throw new TerminologyError(`ValueSet depends on supplement '${supplementUrl}' on ${cs.systemUri} that is not known`); + throw new TerminologyError(`ValueSet depends on supplement '${supplementUrl}' on ${cs.system} that is not known`); } } } From 8c676e148662fc59391badac2e774a1fe6bdd3ab Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 26 Feb 2026 17:52:26 +1100 Subject: [PATCH 4/6] Add icd-9-cm parser --- utilities/icd9cm-parser.js | 313 +++++++++++++++++++++++++++++++++++++ 1 file changed, 313 insertions(+) diff --git a/utilities/icd9cm-parser.js b/utilities/icd9cm-parser.js index e69de29..49aaade 100644 --- a/utilities/icd9cm-parser.js +++ b/utilities/icd9cm-parser.js @@ -0,0 +1,313 @@ +#!/usr/bin/env node +/** + * parse-icd9.js + * Parses an ICD-9-CM text file into a FHIR R4 CodeSystem resource. + * + * Usage: + * node parse-icd9.js [output-file] + * + * File format observed: + * + * GROUP HEADERS (two forms): + * [Roman numeral. ]DISPLAY (NNN-NNN) <- top-level chapter + * DISPLAY (NNN-NNN) <- sub-chapter / block + * + * ICD-9 CONCEPT HEADERS: + * NNN[.D...]DISPLAY + * (code is left-aligned; display follows after one or more spaces/tabs) + * + * CONTINUATION / ANNOTATION LINES: + * Lines that are neither a group header nor a code header and are not + * blank. They belong to the most recently opened concept. + * Keyword prefixes Includes: Excludes: Note: start a named section. + * Subsequent indented lines continue that section. + */ + +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +// ── helpers ────────────────────────────────────────────────────────────────── + +function normalise(str) { + return str.replace(/[ \t]+/g, ' ').trim(); +} + +// Group header: optional roman-numeral prefix + display text + (NNN-NNN) +// Also handles V-code ranges like (V01-V91) and E-code ranges (E800-E999) +const GROUP_HDR = /^(?:[IVXLCDM]+\.\s+)?(.*?)\s+\(([A-Z]?\d{1,3}-[A-Z]?\d{1,3})\)\s*$/; + +// ICD-9 / V-code / E-code concept header: code at column 0, then 1+ spaces, then display +const CODE_HDR = /^([A-Z]?\d{2,3}(?:\.\d+)?)\s+(\S.*)/; + +function isGroupHeader(line) { + return GROUP_HDR.test(line.trim()); +} + +function isCodeHeader(line) { + // Must start at column 0 (no leading whitespace) + return /^[A-Z]?\d/.test(line) && CODE_HDR.test(line); +} + +function parseGroupHeader(line) { + const m = line.trim().match(GROUP_HDR); + if (!m) return null; + return { code: m[2], display: normalise(m[1]) }; +} + +function parseCodeHeader(line) { + const m = line.match(CODE_HDR); + if (!m) return null; + return { code: m[1].trim(), display: normalise(m[2]) }; +} + +// ── block collection ────────────────────────────────────────────────────────── + +/** + * Walk lines and emit raw blocks. + * Each block = { type: 'group'|'icd9', lines: string[] } + * The first line is the header; subsequent lines are body lines (still raw). + */ +function collectBlocks(lines) { + const blocks = []; + let current = null; + + const flush = () => { if (current) { blocks.push(current); current = null; } }; + + for (const raw of lines) { + const trimmed = raw.trim(); + + if (!trimmed) { + // blank line - keep current open; annotations can span blank lines + continue; + } + + if (isGroupHeader(trimmed)) { + flush(); + current = { type: 'group', lines: [trimmed] }; + } else if (isCodeHeader(raw)) { + flush(); + current = { type: 'icd9', lines: [raw] }; + } else { + // continuation / annotation + if (current) current.lines.push(raw); + // lines before the first recognised header are silently dropped + } + } + flush(); + return blocks; +} + +// ── block -> record ──────────────────────────────────────────────────────────── + +/** + * Given a list of body lines (after the header), split into named sections. + * Returns { description, includes, excludes, note } - each a plain string or undefined. + */ +function parseSections(bodyLines) { + const lines = bodyLines.map(normalise).filter(Boolean); + + const sections = { description: [], includes: [], excludes: [], note: [] }; + let cur = 'description'; + + for (const line of lines) { + if (/^includes?:/i.test(line)) { + cur = 'includes'; + const rest = line.replace(/^includes?:\s*/i, '').trim(); + if (rest) sections.includes.push(rest); + } else if (/^excludes?:/i.test(line)) { + cur = 'excludes'; + const rest = line.replace(/^excludes?:\s*/i, '').trim(); + if (rest) sections.excludes.push(rest); + } else if (/^note:/i.test(line)) { + cur = 'note'; + const rest = line.replace(/^note:\s*/i, '').trim(); + if (rest) sections.note.push(rest); + } else { + sections[cur].push(line); + } + } + + const join = arr => arr.join(' ').replace(/\s+/g, ' ').trim() || undefined; + return { + description : join(sections.description), + includes : join(sections.includes), + excludes : join(sections.excludes), + note : join(sections.note), + }; +} + +function parseBlock(block) { + let header; + if (block.type === 'group') { + header = parseGroupHeader(block.lines[0]); + } else { + header = parseCodeHeader(block.lines[0]); + } + if (!header) return null; + + const sections = parseSections(block.lines.slice(1)); + + return { + code : header.code, + display : header.display, + isGroup : block.type === 'group', + ...sections, + }; +} + +// ── hierarchy ───────────────────────────────────────────────────────────────── + +/** + * Find the best (narrowest) parent for a given code from codes already seen. + * Parents always precede children in the source file. + */ +function findParent(code, seenCodes) { + const isRange = /^[A-Z]?\d{1,3}-[A-Z]?\d{1,3}$/.test(code); + + // Helper: strip leading letter and parse int + const numOf = s => parseInt(s.replace(/^[A-Z]/, ''), 10); + + if (isRange) { + const [loStr, hiStr] = code.split('-'); + const lo = numOf(loStr), hi = numOf(hiStr); + let best = null, bestSpan = Infinity; + for (const c of seenCodes) { + if (!/^[A-Z]?\d{1,3}-[A-Z]?\d{1,3}$/.test(c) || c === code) continue; + const [pLo, pHi] = c.split('-').map(numOf); + if (pLo <= lo && pHi >= hi) { + const span = pHi - pLo; + if (span < bestSpan) { best = c; bestSpan = span; } + } + } + return best; + } + + // Decimal code e.g. "002.1" -> try "002" + const dotIdx = code.indexOf('.'); + if (dotIdx !== -1) { + for (let len = code.length - 1; len >= dotIdx; len--) { + const candidate = code.substring(0, len); + if (seenCodes.includes(candidate)) return candidate; + } + const base = code.substring(0, dotIdx); + if (seenCodes.includes(base)) return base; + } + + // Integer code e.g. "002" -> find narrowest containing range + const num = numOf(code); + let best = null, bestSpan = Infinity; + for (const c of seenCodes) { + if (!/^[A-Z]?\d{1,3}-[A-Z]?\d{1,3}$/.test(c)) continue; + const [pLo, pHi] = c.split('-').map(numOf); + if (pLo <= num && pHi >= num) { + const span = pHi - pLo; + if (span < bestSpan) { best = c; bestSpan = span; } + } + } + return best; +} + +// ── FHIR CodeSystem builder ─────────────────────────────────────────────────── + +function buildFhirCodeSystem(records) { + const byCode = new Map(records.map(r => [r.code, r])); + const parentOf = new Map(); + const seenCodes = []; + + for (const r of records) { + parentOf.set(r.code, findParent(r.code, seenCodes)); + seenCodes.push(r.code); + } + + const childrenOf = new Map(); + for (const r of records) { + const p = parentOf.get(r.code); + if (p) { + if (!childrenOf.has(p)) childrenOf.set(p, []); + childrenOf.get(p).push(r.code); + } + } + + function buildConcept(code) { + const r = byCode.get(code); + const concept = { code: r.code, display: r.display }; + + const props = []; + if (r.isGroup) props.push({ code: 'notSelectable', valueBoolean: true }); + if (r.description) props.push({ code: 'description', valueString: r.description }); + if (r.includes) props.push({ code: 'includes', valueString: r.includes }); + if (r.excludes) props.push({ code: 'excludes', valueString: r.excludes }); + if (r.note) props.push({ code: 'note', valueString: r.note }); + if (props.length) concept.property = props; + + const kids = childrenOf.get(code); + if (kids?.length) concept.concept = kids.map(buildConcept); + + return concept; + } + + const roots = records.filter(r => !parentOf.get(r.code)); + + return { + resourceType : 'CodeSystem', + id : 'icd-9-cm', + url : 'http://hl7.org/fhir/sid/icd-9-cm', + version : '2015', + name : 'ICD9CM', + title : 'International Classification of Diseases, 9th Revision, Clinical Modification', + status : 'active', + content : 'complete', + hierarchyMeaning : 'is-a', + property : [ + { + code : 'notSelectable', + uri : 'http://hl7.org/fhir/concept-properties#notSelectable', + description : 'Grouping code not intended for direct coding', + type : 'boolean', + }, + { code: 'description', description: 'Additional descriptive text', type: 'string' }, + { code: 'includes', description: 'Inclusion notes', type: 'string' }, + { code: 'excludes', description: 'Exclusion notes', type: 'string' }, + { code: 'note', description: 'Additional notes', type: 'string' }, + ], + concept: roots.map(r => buildConcept(r.code)), + }; +} + +// ── main ────────────────────────────────────────────────────────────────────── + +function countAll(concepts) { + if (!concepts) return 0; + return concepts.reduce((n, c) => n + 1 + countAll(c.concept), 0); +} + +function main() { + const [,, inputFile, outputFile] = process.argv; + if (!inputFile) { + console.error('Usage: node parse-icd9.js [output-file]'); + process.exit(1); + } + + const outFile = outputFile || path.join(path.dirname(inputFile), 'icd9-cm.json'); + + const text = fs.readFileSync(inputFile, 'utf8'); + const lines = text.split(/\r?\n/); + console.log(`Read ${lines.length} lines`); + + const blocks = collectBlocks(lines); + console.log(`Collected ${blocks.length} blocks`); + + const records = blocks.map(parseBlock).filter(Boolean); + const groups = records.filter(r => r.isGroup).length; + console.log(`Parsed ${records.length} records (${groups} groups, ${records.length - groups} codes)`); + + const cs = buildFhirCodeSystem(records); + console.log(`CodeSystem has ${countAll(cs.concept)} concepts`); + + fs.writeFileSync(outFile, JSON.stringify(cs, null, 2), 'utf8'); + console.log(`Written -> ${outFile}`); +} + +main(); \ No newline at end of file From b7d87c90606ad9a158320cb2a73197341f619d4f Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 26 Feb 2026 17:52:47 +1100 Subject: [PATCH 5/6] upgrade to 0.5.6 --- CHANGELOG.md | 12 ++++++++++++ package.json | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bd50f1..dd24686 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to the Health Intersections Node Server will be documented i The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v0.5.6] - 2026-02-26 + +### Changed +- Added content to TerminologyCapabilities.codeSystem +- fix LOINC list filter handling +- Improve Diagnostic Logging +- Add icd-9-cm parser + +### Tx Conformance Statement + +FHIRsmith 0.5.5 passed all 1382 HL7 terminology service tests (modes tx.fhir.org,omop,general,snomed, tests v1.9.0, runner v6.8.1) + ## [v0.5.5] - 2026-02-26 ### Changed diff --git a/package.json b/package.json index 86a29bc..a90bc8b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fhirsmith", - "version": "0.5.5", + "version": "0.5.6", "description": "A Node.js server that provides a collection of tools to serve the FHIR ecosystem", "main": "server.js", "engines": { From 2daba5966cf181803792f2d04013346abc3d7595 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 26 Feb 2026 17:54:55 +1100 Subject: [PATCH 6/6] lint fix --- tx/library/renderer.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/tx/library/renderer.js b/tx/library/renderer.js index 6719da4..4a6e826 100644 --- a/tx/library/renderer.js +++ b/tx/library/renderer.js @@ -33,9 +33,6 @@ class Renderer { } else if (arg.system !== undefined && arg.version !== undefined) { // It's a CodeSystemProvider return this.displayCodedProvider(arg); - } else if (arg instanceof CodeSystemProvider) { - let cs = arg; - return cs.system() + "|" + cs.version(); } } else if (args.length === 2) { return this.displayCodedSystemVersion(args[0], args[1]);