@atomic-ehr/codegen 0.0.6 → 0.0.7-canary.20260213165156.a064b6a
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/dist/cli/index.js +7 -7
- package/dist/index.d.ts +13 -7
- package/dist/index.js +905 -661
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import Path5__default from 'path';
|
|
|
7
7
|
import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
9
|
import * as fsPromises from 'fs/promises';
|
|
10
|
+
import { createHash } from 'crypto';
|
|
10
11
|
import * as YAML from 'yaml';
|
|
11
12
|
import YAML__default from 'yaml';
|
|
12
13
|
import * as fhirschema from '@atomic-ehr/fhirschema';
|
|
@@ -106,7 +107,7 @@ var CodegenLogger = class _CodegenLogger {
|
|
|
106
107
|
warn(message) {
|
|
107
108
|
this.tryWriteToConsole(2 /* WARN */, this.formatMessage("!", message, pc.yellow));
|
|
108
109
|
}
|
|
109
|
-
|
|
110
|
+
dryWarn(message) {
|
|
110
111
|
if (!this.dryWarnSet.has(message)) {
|
|
111
112
|
this.warn(message);
|
|
112
113
|
this.dryWarnSet.add(message);
|
|
@@ -258,6 +259,10 @@ var FileSystemWriter = class {
|
|
|
258
259
|
if (this.currentFile) throw new Error("Can't open file when another file is open");
|
|
259
260
|
if (fn.includes("/")) throw new Error(`Change file path separatly: ${fn}`);
|
|
260
261
|
const relPath = Path5.normalize(`${this.currentDir}/${fn}`);
|
|
262
|
+
if (this.writtenFilesBuffer[relPath]) {
|
|
263
|
+
this.logger()?.warn(`File will be rewritten '${relPath}'`);
|
|
264
|
+
this.logger()?.debug(`File content: ${this.writtenFilesBuffer[relPath].tokens.join("")}`);
|
|
265
|
+
}
|
|
261
266
|
try {
|
|
262
267
|
const descriptor = this.onDiskOpenFile(relPath);
|
|
263
268
|
this.logger()?.debug(`cat > '${relPath}'`);
|
|
@@ -402,8 +407,6 @@ var Writer = class extends FileSystemWriter {
|
|
|
402
407
|
this.line(`]${endTokens?.filter(Boolean).join(" ") ?? ""}`);
|
|
403
408
|
}
|
|
404
409
|
};
|
|
405
|
-
|
|
406
|
-
// src/typeschema/types.ts
|
|
407
410
|
var extractNameFromCanonical = (canonical, dropFragment = true) => {
|
|
408
411
|
let localName = canonical.split("/").pop();
|
|
409
412
|
if (!localName) return void 0;
|
|
@@ -424,10 +427,11 @@ var packageMeta = (schema) => {
|
|
|
424
427
|
};
|
|
425
428
|
var packageMetaToFhir = (packageMeta2) => `${packageMeta2.name}#${packageMeta2.version}`;
|
|
426
429
|
var packageMetaToNpm = (packageMeta2) => `${packageMeta2.name}@${packageMeta2.version}`;
|
|
430
|
+
var hashSchema = (schema) => {
|
|
431
|
+
const json = JSON.stringify(schema);
|
|
432
|
+
return createHash("sha256").update(json).digest("hex").slice(0, 16);
|
|
433
|
+
};
|
|
427
434
|
var enrichFHIRSchema = (schema, packageMeta2) => {
|
|
428
|
-
if (!packageMeta2) {
|
|
429
|
-
packageMeta2 = { name: "undefined", version: "undefined" };
|
|
430
|
-
}
|
|
431
435
|
return {
|
|
432
436
|
...schema,
|
|
433
437
|
package_meta: schema.package_meta || packageMeta2,
|
|
@@ -681,7 +685,7 @@ var CSharp = class extends Writer {
|
|
|
681
685
|
}
|
|
682
686
|
determineFieldType(fieldName, field, packageName) {
|
|
683
687
|
let typeName = this.getBaseTypeName(field);
|
|
684
|
-
if ("enum" in field && field.enum) {
|
|
688
|
+
if ("enum" in field && field.enum && !field.enum.isOpen) {
|
|
685
689
|
typeName = this.registerAndGetEnumType(fieldName, field, packageName);
|
|
686
690
|
}
|
|
687
691
|
typeName = prefixReservedTypeName(typeName);
|
|
@@ -704,7 +708,7 @@ var CSharp = class extends Writer {
|
|
|
704
708
|
const enumName = formatName(field.binding?.name ?? fieldName);
|
|
705
709
|
const enumTypeName = `${enumName}Enum`;
|
|
706
710
|
if (!this.enums[packageName]) this.enums[packageName] = {};
|
|
707
|
-
if (field.enum) this.enums[packageName][enumTypeName] = field.enum;
|
|
711
|
+
if (field.enum) this.enums[packageName][enumTypeName] = field.enum.values;
|
|
708
712
|
return enumTypeName;
|
|
709
713
|
}
|
|
710
714
|
includeHelperMethods() {
|
|
@@ -1480,8 +1484,8 @@ var Python = class extends Writer {
|
|
|
1480
1484
|
}
|
|
1481
1485
|
determineFieldType(field) {
|
|
1482
1486
|
let fieldType = field ? this.getBaseFieldType(field) : "";
|
|
1483
|
-
if ("enum" in field && field.enum) {
|
|
1484
|
-
const s = field.enum.map((e) => `"${e}"`).join(", ");
|
|
1487
|
+
if ("enum" in field && field.enum && !field.enum.isOpen) {
|
|
1488
|
+
const s = field.enum.values.map((e) => `"${e}"`).join(", ");
|
|
1485
1489
|
fieldType = `Literal[${s}]`;
|
|
1486
1490
|
}
|
|
1487
1491
|
if (field.array) {
|
|
@@ -1758,7 +1762,8 @@ function mkValueSetIdentifierByUrl(register, pkg, fullValueSetUrl) {
|
|
|
1758
1762
|
url: valueSetUrl
|
|
1759
1763
|
};
|
|
1760
1764
|
}
|
|
1761
|
-
function mkBindingIdentifier(fhirSchema, path,
|
|
1765
|
+
function mkBindingIdentifier(fhirSchema, path, element) {
|
|
1766
|
+
const bindingName = element.binding?.bindingName;
|
|
1762
1767
|
const pathStr = path.join(".");
|
|
1763
1768
|
const [pkg, name, url] = bindingName ? [{ name: "shared", version: "1.0.0" }, bindingName, `urn:fhir:binding:${bindingName}`] : [fhirSchema.package_meta, `${fhirSchema.name}.${pathStr}_binding`, `${fhirSchema.url}#${pathStr}_binding`];
|
|
1764
1769
|
return {
|
|
@@ -1770,6 +1775,146 @@ function mkBindingIdentifier(fhirSchema, path, bindingName) {
|
|
|
1770
1775
|
};
|
|
1771
1776
|
}
|
|
1772
1777
|
|
|
1778
|
+
// src/typeschema/core/binding.ts
|
|
1779
|
+
function extractValueSetConceptsByUrl(register, pkg, valueSetUrl, logger) {
|
|
1780
|
+
const cleanUrl = dropVersionFromUrl(valueSetUrl) || valueSetUrl;
|
|
1781
|
+
const valueSet = register.resolveVs(pkg, cleanUrl);
|
|
1782
|
+
if (!valueSet) return void 0;
|
|
1783
|
+
return extractValueSetConcepts(register, valueSet);
|
|
1784
|
+
}
|
|
1785
|
+
function extractValueSetConcepts(register, valueSet, _logger) {
|
|
1786
|
+
if (valueSet.expansion?.contains) {
|
|
1787
|
+
return valueSet.expansion.contains.filter((item) => item.code !== void 0).map((item) => {
|
|
1788
|
+
assert3(item.code);
|
|
1789
|
+
return {
|
|
1790
|
+
code: item.code,
|
|
1791
|
+
display: item.display,
|
|
1792
|
+
system: item.system
|
|
1793
|
+
};
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
const concepts = [];
|
|
1797
|
+
if (valueSet.compose?.include) {
|
|
1798
|
+
for (const include of valueSet.compose.include) {
|
|
1799
|
+
if (include.concept) {
|
|
1800
|
+
for (const concept of include.concept) {
|
|
1801
|
+
concepts.push({
|
|
1802
|
+
system: include.system,
|
|
1803
|
+
code: concept.code,
|
|
1804
|
+
display: concept.display
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
} else if (include.system && !include.filter) {
|
|
1808
|
+
try {
|
|
1809
|
+
const codeSystem = register.resolveAny(include.system);
|
|
1810
|
+
if (codeSystem?.concept) {
|
|
1811
|
+
const extractConcepts = (conceptList, system) => {
|
|
1812
|
+
for (const concept of conceptList) {
|
|
1813
|
+
concepts.push({
|
|
1814
|
+
system,
|
|
1815
|
+
code: concept.code,
|
|
1816
|
+
display: concept.display
|
|
1817
|
+
});
|
|
1818
|
+
if (concept.concept) {
|
|
1819
|
+
extractConcepts(concept.concept, system);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
};
|
|
1823
|
+
extractConcepts(codeSystem.concept, include.system);
|
|
1824
|
+
}
|
|
1825
|
+
} catch {
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
return concepts.length > 0 ? concepts : void 0;
|
|
1831
|
+
}
|
|
1832
|
+
var MAX_ENUM_LENGTH = 100;
|
|
1833
|
+
var BINDABLE_TYPES = /* @__PURE__ */ new Set([
|
|
1834
|
+
"code",
|
|
1835
|
+
"Coding",
|
|
1836
|
+
"CodeableConcept",
|
|
1837
|
+
"CodeableReference",
|
|
1838
|
+
"Quantity",
|
|
1839
|
+
"string",
|
|
1840
|
+
"uri",
|
|
1841
|
+
"Duration"
|
|
1842
|
+
]);
|
|
1843
|
+
function buildEnum(register, fhirSchema, element, logger) {
|
|
1844
|
+
if (!element.binding) return void 0;
|
|
1845
|
+
const strength = element.binding.strength;
|
|
1846
|
+
const valueSetUrl = element.binding.valueSet;
|
|
1847
|
+
if (!valueSetUrl) return void 0;
|
|
1848
|
+
if (!BINDABLE_TYPES.has(element.type ?? "")) {
|
|
1849
|
+
logger?.dryWarn(`eld-11: Binding on non-bindable type '${element.type}' (valueSet: ${valueSetUrl})`);
|
|
1850
|
+
return void 0;
|
|
1851
|
+
}
|
|
1852
|
+
const shouldGenerateEnum = strength === "required" || strength === "extensible" || strength === "preferred";
|
|
1853
|
+
if (!shouldGenerateEnum) return void 0;
|
|
1854
|
+
const concepts = extractValueSetConceptsByUrl(register, fhirSchema.package_meta, valueSetUrl);
|
|
1855
|
+
if (!concepts || concepts.length === 0) return void 0;
|
|
1856
|
+
const codes = concepts.map((c) => c.code).filter((code) => code && typeof code === "string" && code.trim().length > 0);
|
|
1857
|
+
if (codes.length > MAX_ENUM_LENGTH) {
|
|
1858
|
+
logger?.dryWarn(
|
|
1859
|
+
`Value set ${valueSetUrl} has ${codes.length} which is more than ${MAX_ENUM_LENGTH} codes, which may cause issues with code generation.`
|
|
1860
|
+
);
|
|
1861
|
+
return void 0;
|
|
1862
|
+
}
|
|
1863
|
+
if (codes.length === 0) return void 0;
|
|
1864
|
+
return { isOpen: strength !== "required", values: codes };
|
|
1865
|
+
}
|
|
1866
|
+
function generateBindingSchema(register, fhirSchema, path, element, logger) {
|
|
1867
|
+
if (!element.binding?.valueSet) return void 0;
|
|
1868
|
+
const identifier = mkBindingIdentifier(fhirSchema, path, element);
|
|
1869
|
+
const valueSetIdentifier = mkValueSetIdentifierByUrl(
|
|
1870
|
+
register,
|
|
1871
|
+
fhirSchema.package_meta,
|
|
1872
|
+
element.binding.valueSet
|
|
1873
|
+
);
|
|
1874
|
+
const enumResult = buildEnum(register, fhirSchema, element, logger);
|
|
1875
|
+
return {
|
|
1876
|
+
identifier,
|
|
1877
|
+
valueset: valueSetIdentifier,
|
|
1878
|
+
strength: element.binding.strength,
|
|
1879
|
+
enum: enumResult,
|
|
1880
|
+
dependencies: [valueSetIdentifier]
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
function collectBindingSchemas(register, fhirSchema, logger) {
|
|
1884
|
+
const processedPaths = /* @__PURE__ */ new Set();
|
|
1885
|
+
if (!fhirSchema.elements) return [];
|
|
1886
|
+
const bindings = [];
|
|
1887
|
+
function collectBindings(elements, parentPath) {
|
|
1888
|
+
for (const [key, element] of Object.entries(elements)) {
|
|
1889
|
+
const path = [...parentPath, key];
|
|
1890
|
+
const pathKey = path.join(".");
|
|
1891
|
+
const elemSnapshot = register.resolveElementSnapshot(fhirSchema, path);
|
|
1892
|
+
if (processedPaths.has(pathKey)) continue;
|
|
1893
|
+
processedPaths.add(pathKey);
|
|
1894
|
+
if (elemSnapshot.binding) {
|
|
1895
|
+
const binding = generateBindingSchema(register, fhirSchema, path, elemSnapshot, logger);
|
|
1896
|
+
if (binding) {
|
|
1897
|
+
bindings.push(binding);
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
if (element.elements) {
|
|
1901
|
+
collectBindings(element.elements, path);
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
collectBindings(fhirSchema.elements, []);
|
|
1906
|
+
bindings.sort((a, b) => a.identifier.name.localeCompare(b.identifier.name));
|
|
1907
|
+
const uniqueBindings = [];
|
|
1908
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
1909
|
+
for (const binding of bindings) {
|
|
1910
|
+
if (!seenUrls.has(binding.identifier.url)) {
|
|
1911
|
+
seenUrls.add(binding.identifier.url);
|
|
1912
|
+
uniqueBindings.push(binding);
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
return uniqueBindings;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1773
1918
|
// src/typeschema/core/nested-types.ts
|
|
1774
1919
|
function mkNestedIdentifier(register, fhirSchema, path, logger) {
|
|
1775
1920
|
const nestedTypeOrigins = {};
|
|
@@ -1972,24 +2117,24 @@ function buildFieldType(register, fhirSchema, path, element, logger) {
|
|
|
1972
2117
|
} else if (fhirSchema.derivation === "constraint") {
|
|
1973
2118
|
return void 0;
|
|
1974
2119
|
} else {
|
|
1975
|
-
logger?.
|
|
1976
|
-
`Can't recognize element type: <${fhirSchema.url}>.${path.join(".")} (pkg: '${packageMetaToFhir(fhirSchema.package_meta)}'):
|
|
2120
|
+
logger?.dryWarn(
|
|
2121
|
+
`Can't recognize element type: <${fhirSchema.url}>.${path.join(".")} (pkg: '${packageMetaToFhir(fhirSchema.package_meta)}'): missing type info`
|
|
1977
2122
|
);
|
|
1978
|
-
|
|
2123
|
+
return void 0;
|
|
1979
2124
|
}
|
|
1980
2125
|
}
|
|
1981
2126
|
var mkField = (register, fhirSchema, path, element, logger) => {
|
|
1982
2127
|
let binding;
|
|
1983
|
-
let
|
|
2128
|
+
let enumResult;
|
|
1984
2129
|
if (element.binding) {
|
|
1985
|
-
binding = mkBindingIdentifier(fhirSchema, path, element
|
|
1986
|
-
if (
|
|
1987
|
-
|
|
2130
|
+
binding = mkBindingIdentifier(fhirSchema, path, element);
|
|
2131
|
+
if (BINDABLE_TYPES.has(element.type ?? "")) {
|
|
2132
|
+
enumResult = buildEnum(register, fhirSchema, element, logger);
|
|
1988
2133
|
}
|
|
1989
2134
|
}
|
|
1990
2135
|
const fieldType = buildFieldType(register, fhirSchema, path, element, logger);
|
|
1991
2136
|
if (!fieldType)
|
|
1992
|
-
logger?.
|
|
2137
|
+
logger?.dryWarn(`Field type not found for '${fhirSchema.url}#${path.join(".")}' (${fhirSchema.derivation})`);
|
|
1993
2138
|
return {
|
|
1994
2139
|
type: fieldType,
|
|
1995
2140
|
required: isRequired(register, fhirSchema, path),
|
|
@@ -2002,13 +2147,18 @@ var mkField = (register, fhirSchema, path, element, logger) => {
|
|
|
2002
2147
|
choices: element.choices,
|
|
2003
2148
|
choiceOf: element.choiceOf,
|
|
2004
2149
|
binding,
|
|
2005
|
-
enum:
|
|
2150
|
+
enum: enumResult
|
|
2006
2151
|
};
|
|
2007
2152
|
};
|
|
2008
2153
|
function isNestedElement(element) {
|
|
2009
2154
|
const isBackbone = element.type === "BackboneElement";
|
|
2010
2155
|
const isElement = element.type === "Element" && element.elements !== void 0 && Object.keys(element.elements).length > 0;
|
|
2011
|
-
const elementsWithoutType =
|
|
2156
|
+
const elementsWithoutType = (
|
|
2157
|
+
// FIXME: understand and make a decision.
|
|
2158
|
+
// Problem example: http://hl7.org/cda/stds/core/StructureDefinition/SubstanceAdministration -> consumable
|
|
2159
|
+
// Don't generate nested type for that field, but defetly expect it.
|
|
2160
|
+
element.type === void 0 && element.choiceOf === void 0 && element.elements !== void 0 && Object.keys(element.elements).length > 0
|
|
2161
|
+
);
|
|
2012
2162
|
return isBackbone || isElement || elementsWithoutType;
|
|
2013
2163
|
}
|
|
2014
2164
|
function mkNestedField(register, fhirSchema, path, element, logger) {
|
|
@@ -2022,138 +2172,6 @@ function mkNestedField(register, fhirSchema, path, element, logger) {
|
|
|
2022
2172
|
};
|
|
2023
2173
|
}
|
|
2024
2174
|
|
|
2025
|
-
// src/typeschema/core/binding.ts
|
|
2026
|
-
function extractValueSetConceptsByUrl(register, pkg, valueSetUrl, logger) {
|
|
2027
|
-
const cleanUrl = dropVersionFromUrl(valueSetUrl) || valueSetUrl;
|
|
2028
|
-
const valueSet = register.resolveVs(pkg, cleanUrl);
|
|
2029
|
-
if (!valueSet) return void 0;
|
|
2030
|
-
return extractValueSetConcepts(register, valueSet);
|
|
2031
|
-
}
|
|
2032
|
-
function extractValueSetConcepts(register, valueSet, _logger) {
|
|
2033
|
-
if (valueSet.expansion?.contains) {
|
|
2034
|
-
return valueSet.expansion.contains.filter((item) => item.code !== void 0).map((item) => {
|
|
2035
|
-
assert3(item.code);
|
|
2036
|
-
return {
|
|
2037
|
-
code: item.code,
|
|
2038
|
-
display: item.display,
|
|
2039
|
-
system: item.system
|
|
2040
|
-
};
|
|
2041
|
-
});
|
|
2042
|
-
}
|
|
2043
|
-
const concepts = [];
|
|
2044
|
-
if (valueSet.compose?.include) {
|
|
2045
|
-
for (const include of valueSet.compose.include) {
|
|
2046
|
-
if (include.concept) {
|
|
2047
|
-
for (const concept of include.concept) {
|
|
2048
|
-
concepts.push({
|
|
2049
|
-
system: include.system,
|
|
2050
|
-
code: concept.code,
|
|
2051
|
-
display: concept.display
|
|
2052
|
-
});
|
|
2053
|
-
}
|
|
2054
|
-
} else if (include.system && !include.filter) {
|
|
2055
|
-
try {
|
|
2056
|
-
const codeSystem = register.resolveAny(include.system);
|
|
2057
|
-
if (codeSystem?.concept) {
|
|
2058
|
-
const extractConcepts = (conceptList, system) => {
|
|
2059
|
-
for (const concept of conceptList) {
|
|
2060
|
-
concepts.push({
|
|
2061
|
-
system,
|
|
2062
|
-
code: concept.code,
|
|
2063
|
-
display: concept.display
|
|
2064
|
-
});
|
|
2065
|
-
if (concept.concept) {
|
|
2066
|
-
extractConcepts(concept.concept, system);
|
|
2067
|
-
}
|
|
2068
|
-
}
|
|
2069
|
-
};
|
|
2070
|
-
extractConcepts(codeSystem.concept, include.system);
|
|
2071
|
-
}
|
|
2072
|
-
} catch {
|
|
2073
|
-
}
|
|
2074
|
-
}
|
|
2075
|
-
}
|
|
2076
|
-
}
|
|
2077
|
-
return concepts.length > 0 ? concepts : void 0;
|
|
2078
|
-
}
|
|
2079
|
-
var MAX_ENUM_LENGTH = 100;
|
|
2080
|
-
function buildEnum(register, fhirSchema, element, logger) {
|
|
2081
|
-
if (!element.binding) return void 0;
|
|
2082
|
-
const strength = element.binding.strength;
|
|
2083
|
-
const valueSetUrl = element.binding.valueSet;
|
|
2084
|
-
if (!valueSetUrl) return void 0;
|
|
2085
|
-
const shouldGenerateEnum = strength === "required" || strength === "extensible" && (element.type === "code" || element.type === "Coding") || strength === "preferred" && (element.type === "code" || element.type === "Coding");
|
|
2086
|
-
if (!shouldGenerateEnum) return void 0;
|
|
2087
|
-
const concepts = extractValueSetConceptsByUrl(register, fhirSchema.package_meta, valueSetUrl);
|
|
2088
|
-
if (!concepts || concepts.length === 0) return void 0;
|
|
2089
|
-
const codes = concepts.map((c) => c.code).filter((code) => code && typeof code === "string" && code.trim().length > 0);
|
|
2090
|
-
if (codes.length > MAX_ENUM_LENGTH) {
|
|
2091
|
-
logger?.dry_warn(
|
|
2092
|
-
`Value set ${valueSetUrl} has ${codes.length} which is more than ${MAX_ENUM_LENGTH} codes, which may cause issues with code generation.`
|
|
2093
|
-
);
|
|
2094
|
-
return void 0;
|
|
2095
|
-
}
|
|
2096
|
-
return codes.length > 0 ? codes : void 0;
|
|
2097
|
-
}
|
|
2098
|
-
function generateBindingSchema(register, fhirSchema, path, element, logger) {
|
|
2099
|
-
if (!element.binding?.valueSet) return void 0;
|
|
2100
|
-
const identifier = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
|
|
2101
|
-
const fieldType = buildFieldType(register, fhirSchema, path, element, logger);
|
|
2102
|
-
const valueSetIdentifier = mkValueSetIdentifierByUrl(
|
|
2103
|
-
register,
|
|
2104
|
-
fhirSchema.package_meta,
|
|
2105
|
-
element.binding.valueSet
|
|
2106
|
-
);
|
|
2107
|
-
const dependencies = [];
|
|
2108
|
-
if (fieldType) {
|
|
2109
|
-
dependencies.push(fieldType);
|
|
2110
|
-
}
|
|
2111
|
-
dependencies.push(valueSetIdentifier);
|
|
2112
|
-
const enumValues = buildEnum(register, fhirSchema, element, logger);
|
|
2113
|
-
return {
|
|
2114
|
-
identifier,
|
|
2115
|
-
type: fieldType,
|
|
2116
|
-
valueset: valueSetIdentifier,
|
|
2117
|
-
strength: element.binding.strength,
|
|
2118
|
-
enum: enumValues,
|
|
2119
|
-
dependencies
|
|
2120
|
-
};
|
|
2121
|
-
}
|
|
2122
|
-
function collectBindingSchemas(register, fhirSchema, logger) {
|
|
2123
|
-
const processedPaths = /* @__PURE__ */ new Set();
|
|
2124
|
-
if (!fhirSchema.elements) return [];
|
|
2125
|
-
const bindings = [];
|
|
2126
|
-
function collectBindings(elements, parentPath) {
|
|
2127
|
-
for (const [key, element] of Object.entries(elements)) {
|
|
2128
|
-
const path = [...parentPath, key];
|
|
2129
|
-
const pathKey = path.join(".");
|
|
2130
|
-
const elemSnapshot = register.resolveElementSnapshot(fhirSchema, path);
|
|
2131
|
-
if (processedPaths.has(pathKey)) continue;
|
|
2132
|
-
processedPaths.add(pathKey);
|
|
2133
|
-
if (elemSnapshot.binding) {
|
|
2134
|
-
const binding = generateBindingSchema(register, fhirSchema, path, elemSnapshot, logger);
|
|
2135
|
-
if (binding) {
|
|
2136
|
-
bindings.push(binding);
|
|
2137
|
-
}
|
|
2138
|
-
}
|
|
2139
|
-
if (element.elements) {
|
|
2140
|
-
collectBindings(element.elements, path);
|
|
2141
|
-
}
|
|
2142
|
-
}
|
|
2143
|
-
}
|
|
2144
|
-
collectBindings(fhirSchema.elements, []);
|
|
2145
|
-
bindings.sort((a, b) => a.identifier.name.localeCompare(b.identifier.name));
|
|
2146
|
-
const uniqueBindings = [];
|
|
2147
|
-
const seenUrls = /* @__PURE__ */ new Set();
|
|
2148
|
-
for (const binding of bindings) {
|
|
2149
|
-
if (!seenUrls.has(binding.identifier.url)) {
|
|
2150
|
-
seenUrls.add(binding.identifier.url);
|
|
2151
|
-
uniqueBindings.push(binding);
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
return uniqueBindings;
|
|
2155
|
-
}
|
|
2156
|
-
|
|
2157
2175
|
// src/typeschema/core/transformer.ts
|
|
2158
2176
|
function mkFields(register, fhirSchema, parentPath, elements, logger) {
|
|
2159
2177
|
if (!elements) return void 0;
|
|
@@ -2284,15 +2302,13 @@ function extractExtensionValueTypes(register, fhirSchema, extensionUrl, logger)
|
|
|
2284
2302
|
const uniq = new Map(valueTypes.map((type) => [type.url, type]));
|
|
2285
2303
|
return Array.from(uniq.values());
|
|
2286
2304
|
}
|
|
2287
|
-
|
|
2288
|
-
const extensionSchema = register.resolveFs(fhirSchema.package_meta, extensionUrl);
|
|
2289
|
-
if (!extensionSchema?.elements) return void 0;
|
|
2305
|
+
var extractLegacySubExtensions = (register, extensionSchema, logger) => {
|
|
2290
2306
|
const subExtensions = [];
|
|
2307
|
+
if (!extensionSchema.elements) return subExtensions;
|
|
2291
2308
|
for (const [key, element] of Object.entries(extensionSchema.elements)) {
|
|
2292
2309
|
if (!key.startsWith("extension:")) continue;
|
|
2293
2310
|
const sliceName = key.split(":")[1];
|
|
2294
2311
|
if (!sliceName) continue;
|
|
2295
|
-
const sliceUrl = element.url ?? sliceName;
|
|
2296
2312
|
let valueType;
|
|
2297
2313
|
for (const [elemKey, elemValue] of Object.entries(element.elements ?? {})) {
|
|
2298
2314
|
if (elemValue.choiceOf !== "value" && !elemKey.startsWith("value")) continue;
|
|
@@ -2301,48 +2317,57 @@ function extractSubExtensions(register, fhirSchema, extensionUrl, logger) {
|
|
|
2301
2317
|
}
|
|
2302
2318
|
subExtensions.push({
|
|
2303
2319
|
name: sliceName,
|
|
2304
|
-
url:
|
|
2320
|
+
url: element.url ?? sliceName,
|
|
2305
2321
|
valueType,
|
|
2306
2322
|
min: element.min,
|
|
2307
2323
|
max: element.max !== void 0 ? String(element.max) : void 0
|
|
2308
2324
|
});
|
|
2309
2325
|
}
|
|
2310
|
-
|
|
2326
|
+
return subExtensions;
|
|
2327
|
+
};
|
|
2328
|
+
var extractSlicingSubExtensions = (extensionSchema) => {
|
|
2329
|
+
const subExtensions = [];
|
|
2330
|
+
const extensionElement = extensionSchema.elements?.extension;
|
|
2311
2331
|
const slices = extensionElement?.slicing?.slices;
|
|
2312
|
-
if (slices
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
const
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
};
|
|
2331
|
-
break;
|
|
2332
|
-
}
|
|
2332
|
+
if (!slices || typeof slices !== "object") return subExtensions;
|
|
2333
|
+
for (const [sliceName, sliceData] of Object.entries(slices)) {
|
|
2334
|
+
const slice = sliceData;
|
|
2335
|
+
const schema = slice.schema;
|
|
2336
|
+
if (!schema) continue;
|
|
2337
|
+
let valueType;
|
|
2338
|
+
for (const [elemKey, elemValue] of Object.entries(schema.elements ?? {})) {
|
|
2339
|
+
const elem = elemValue;
|
|
2340
|
+
if (elem.choiceOf !== "value" && !elemKey.startsWith("value")) continue;
|
|
2341
|
+
if (elem.type) {
|
|
2342
|
+
valueType = {
|
|
2343
|
+
kind: "complex-type",
|
|
2344
|
+
package: extensionSchema.package_meta.name,
|
|
2345
|
+
version: extensionSchema.package_meta.version,
|
|
2346
|
+
name: elem.type,
|
|
2347
|
+
url: `http://hl7.org/fhir/StructureDefinition/${elem.type}`
|
|
2348
|
+
};
|
|
2349
|
+
break;
|
|
2333
2350
|
}
|
|
2334
|
-
subExtensions.push({
|
|
2335
|
-
name: sliceName,
|
|
2336
|
-
url: sliceUrl,
|
|
2337
|
-
valueType,
|
|
2338
|
-
min: schema._required ? 1 : schema.min ?? 0,
|
|
2339
|
-
// biome-ignore lint/style/noNestedTernary : okay here
|
|
2340
|
-
max: schema.max !== void 0 ? String(schema.max) : schema.array ? "*" : "1"
|
|
2341
|
-
});
|
|
2342
2351
|
}
|
|
2352
|
+
subExtensions.push({
|
|
2353
|
+
name: sliceName,
|
|
2354
|
+
url: slice.match?.url ?? sliceName,
|
|
2355
|
+
valueType,
|
|
2356
|
+
min: schema._required ? 1 : schema.min ?? 0,
|
|
2357
|
+
// biome-ignore lint/style/noNestedTernary : okay here
|
|
2358
|
+
max: schema.max !== void 0 ? String(schema.max) : schema.array ? "*" : "1"
|
|
2359
|
+
});
|
|
2343
2360
|
}
|
|
2361
|
+
return subExtensions;
|
|
2362
|
+
};
|
|
2363
|
+
var extractSubExtensions = (register, fhirSchema, extensionUrl, logger) => {
|
|
2364
|
+
const extensionSchema = register.resolveFs(fhirSchema.package_meta, extensionUrl);
|
|
2365
|
+
if (!extensionSchema?.elements) return void 0;
|
|
2366
|
+
const legacySubs = extractLegacySubExtensions(register, extensionSchema, logger);
|
|
2367
|
+
const slicingSubs = extractSlicingSubExtensions(extensionSchema);
|
|
2368
|
+
const subExtensions = [...legacySubs, ...slicingSubs];
|
|
2344
2369
|
return subExtensions.length > 0 ? subExtensions : void 0;
|
|
2345
|
-
}
|
|
2370
|
+
};
|
|
2346
2371
|
function extractProfileExtensions(register, fhirSchema, logger) {
|
|
2347
2372
|
const extensions = [];
|
|
2348
2373
|
const addExtensionEntry = (path, name, schema) => {
|
|
@@ -2399,256 +2424,64 @@ async function transformFhirSchema(register, fhirSchema, logger) {
|
|
|
2399
2424
|
return schemas;
|
|
2400
2425
|
}
|
|
2401
2426
|
|
|
2402
|
-
// src/
|
|
2403
|
-
var
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
const
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
};
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
};
|
|
2431
|
-
var mkPackageAwareResolver = async (manager, pkg, deep, acc, logger) => {
|
|
2432
|
-
const pkgId = packageMetaToFhir(pkg);
|
|
2433
|
-
logger?.info(`${" ".repeat(deep * 2)}+ ${pkgId}`);
|
|
2434
|
-
if (acc[pkgId]) return acc[pkgId];
|
|
2435
|
-
const index = mkEmptyPkgIndex(pkg);
|
|
2436
|
-
for (const resource of await manager.search({ package: pkg })) {
|
|
2437
|
-
const rawUrl = resource.url;
|
|
2438
|
-
if (!rawUrl) continue;
|
|
2439
|
-
if (!(isStructureDefinition(resource) || isValueSet(resource) || isCodeSystem(resource))) continue;
|
|
2440
|
-
const url = rawUrl;
|
|
2441
|
-
if (index.canonicalResolution[url]) logger?.dry_warn(`Duplicate canonical URL: ${url} at ${pkgId}.`);
|
|
2442
|
-
index.canonicalResolution[url] = [{ deep, pkg, pkgId, resource }];
|
|
2443
|
-
}
|
|
2444
|
-
const deps = await readPackageDependencies(manager, pkg);
|
|
2445
|
-
for (const depPkg of deps) {
|
|
2446
|
-
const { canonicalResolution } = await mkPackageAwareResolver(manager, depPkg, deep + 1, acc, logger);
|
|
2447
|
-
for (const [surl, resolutions] of Object.entries(canonicalResolution)) {
|
|
2448
|
-
const url = surl;
|
|
2449
|
-
index.canonicalResolution[url] = [...index.canonicalResolution[url] || [], ...resolutions];
|
|
2450
|
-
}
|
|
2451
|
-
}
|
|
2452
|
-
for (const resolutionOptions of Object.values(index.canonicalResolution)) {
|
|
2453
|
-
resolutionOptions.sort((a, b) => a.deep - b.deep);
|
|
2454
|
-
}
|
|
2455
|
-
acc[pkgId] = index;
|
|
2456
|
-
return index;
|
|
2457
|
-
};
|
|
2458
|
-
var enrichResolver = (resolver, logger) => {
|
|
2459
|
-
for (const { pkg, canonicalResolution } of Object.values(resolver)) {
|
|
2460
|
-
const pkgId = packageMetaToFhir(pkg);
|
|
2461
|
-
if (!resolver[pkgId]) throw new Error(`Package ${pkgId} not found`);
|
|
2462
|
-
for (const [_url, options] of Object.entries(canonicalResolution)) {
|
|
2463
|
-
const resolition = options[0];
|
|
2464
|
-
if (!resolition) throw new Error(`Resource not found`);
|
|
2465
|
-
const resource = resolition.resource;
|
|
2466
|
-
const resourcePkg = resolition.pkg;
|
|
2467
|
-
if (isStructureDefinition(resource)) {
|
|
2468
|
-
const fs7 = fhirschema.translate(resource);
|
|
2469
|
-
const rfs = enrichFHIRSchema(fs7, resourcePkg);
|
|
2470
|
-
resolver[pkgId].fhirSchemas[rfs.url] = rfs;
|
|
2471
|
-
}
|
|
2472
|
-
if (isValueSet(resource)) {
|
|
2473
|
-
const rvs = enrichValueSet(resource, resourcePkg);
|
|
2474
|
-
resolver[pkgId].valueSets[rvs.url] = rvs;
|
|
2475
|
-
}
|
|
2476
|
-
}
|
|
2477
|
-
}
|
|
2478
|
-
};
|
|
2479
|
-
var packageAgnosticResolveCanonical = (resolver, url, _logger) => {
|
|
2480
|
-
const options = Object.values(resolver).flatMap((pkg) => pkg.canonicalResolution[url]);
|
|
2481
|
-
if (!options) throw new Error(`No canonical resolution found for ${url} in any package`);
|
|
2482
|
-
return options[0]?.resource;
|
|
2483
|
-
};
|
|
2484
|
-
var registerFromManager = async (manager, { logger, fallbackPackageForNameResolution, focusedPackages }) => {
|
|
2485
|
-
const packages = focusedPackages ?? await manager.packages();
|
|
2486
|
-
const resolver = {};
|
|
2487
|
-
for (const pkg of packages) {
|
|
2488
|
-
await mkPackageAwareResolver(manager, pkg, 0, resolver, logger);
|
|
2489
|
-
}
|
|
2490
|
-
enrichResolver(resolver);
|
|
2491
|
-
const resolveFs = (pkg, canonicalUrl) => {
|
|
2492
|
-
return resolver[packageMetaToFhir(pkg)]?.fhirSchemas[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.fhirSchemas[canonicalUrl];
|
|
2493
|
-
};
|
|
2494
|
-
const resolveVs = (pkg, canonicalUrl) => {
|
|
2495
|
-
return resolver[packageMetaToFhir(pkg)]?.valueSets[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.valueSets[canonicalUrl];
|
|
2496
|
-
};
|
|
2497
|
-
const ensureSpecializationCanonicalUrl = (name) => {
|
|
2498
|
-
if (name.includes("|")) name = name.split("|")[0];
|
|
2499
|
-
if (name.match(/^[a-zA-Z0-9]+$/)) {
|
|
2500
|
-
return `http://hl7.org/fhir/StructureDefinition/${name}`;
|
|
2501
|
-
}
|
|
2502
|
-
return name;
|
|
2503
|
-
};
|
|
2504
|
-
const resolveFsGenealogy = (pkg, canonicalUrl) => {
|
|
2505
|
-
let fs7 = resolveFs(pkg, canonicalUrl);
|
|
2506
|
-
if (fs7 === void 0) throw new Error(`Failed to resolve FHIR Schema: '${canonicalUrl}'`);
|
|
2507
|
-
const genealogy = [fs7];
|
|
2508
|
-
while (fs7?.base) {
|
|
2509
|
-
const pkg2 = fs7.package_meta;
|
|
2510
|
-
const baseUrl = ensureSpecializationCanonicalUrl(fs7.base);
|
|
2511
|
-
fs7 = resolveFs(pkg2, baseUrl);
|
|
2512
|
-
if (fs7 === void 0)
|
|
2513
|
-
throw new Error(
|
|
2514
|
-
`Failed to resolve FHIR Schema base for '${canonicalUrl}'. Problem: '${baseUrl}' from '${packageMetaToFhir(pkg2)}'`
|
|
2515
|
-
);
|
|
2516
|
-
genealogy.push(fs7);
|
|
2517
|
-
}
|
|
2518
|
-
return genealogy;
|
|
2519
|
-
};
|
|
2520
|
-
const resolveFsSpecializations = (pkg, canonicalUrl) => {
|
|
2521
|
-
return resolveFsGenealogy(pkg, canonicalUrl).filter((fs7) => fs7.derivation === "specialization");
|
|
2522
|
-
};
|
|
2523
|
-
const resolveElementSnapshot = (fhirSchema, path) => {
|
|
2524
|
-
const geneology = resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url);
|
|
2525
|
-
const elemGeneology = resolveFsElementGenealogy(geneology, path);
|
|
2526
|
-
const elemSnapshot = fsElementSnapshot(elemGeneology);
|
|
2527
|
-
return elemSnapshot;
|
|
2528
|
-
};
|
|
2529
|
-
const getAllElementKeys = (elems) => {
|
|
2530
|
-
const keys = /* @__PURE__ */ new Set();
|
|
2531
|
-
for (const [key, elem] of Object.entries(elems)) {
|
|
2532
|
-
keys.add(key);
|
|
2533
|
-
for (const choiceKey of elem?.choices || []) {
|
|
2534
|
-
if (!elems[choiceKey]) {
|
|
2535
|
-
keys.add(choiceKey);
|
|
2536
|
-
}
|
|
2537
|
-
}
|
|
2538
|
-
}
|
|
2539
|
-
return Array.from(keys);
|
|
2540
|
-
};
|
|
2541
|
-
let cachedResolutionTree;
|
|
2542
|
-
return {
|
|
2543
|
-
testAppendFs(fs7) {
|
|
2544
|
-
const rfs = enrichFHIRSchema(fs7);
|
|
2545
|
-
const pkgId = packageMetaToFhir(rfs.package_meta);
|
|
2546
|
-
if (!resolver[pkgId]) resolver[pkgId] = mkEmptyPkgIndex(rfs.package_meta);
|
|
2547
|
-
resolver[pkgId].fhirSchemas[rfs.url] = rfs;
|
|
2548
|
-
cachedResolutionTree = void 0;
|
|
2549
|
-
},
|
|
2550
|
-
resolveFs,
|
|
2551
|
-
resolveFsGenealogy,
|
|
2552
|
-
resolveFsSpecializations,
|
|
2553
|
-
ensureSpecializationCanonicalUrl,
|
|
2554
|
-
resolveSd: (pkg, canonicalUrl) => {
|
|
2555
|
-
const res = resolver[packageMetaToFhir(pkg)]?.canonicalResolution[canonicalUrl]?.[0]?.resource;
|
|
2556
|
-
if (isStructureDefinition(res)) return res;
|
|
2557
|
-
return void 0;
|
|
2558
|
-
},
|
|
2559
|
-
allSd: () => Object.values(resolver).flatMap(
|
|
2560
|
-
(pkgIndex) => Object.values(pkgIndex.canonicalResolution).flatMap(
|
|
2561
|
-
(resolutions) => resolutions.map((r) => {
|
|
2562
|
-
const sd = r.resource;
|
|
2563
|
-
if (!sd.package_name) {
|
|
2564
|
-
return {
|
|
2565
|
-
...sd,
|
|
2566
|
-
package_name: pkgIndex.pkg.name,
|
|
2567
|
-
package_version: pkgIndex.pkg.version
|
|
2568
|
-
};
|
|
2569
|
-
}
|
|
2570
|
-
return sd;
|
|
2571
|
-
})
|
|
2572
|
-
)
|
|
2573
|
-
).filter((r) => isStructureDefinition(r)).sort((sd1, sd2) => sd1.url.localeCompare(sd2.url)),
|
|
2574
|
-
patchSd: (fn) => {
|
|
2575
|
-
Object.values(resolver).flatMap(
|
|
2576
|
-
(pkgIndex) => Object.values(pkgIndex.canonicalResolution).forEach((resolutions) => {
|
|
2577
|
-
resolutions.forEach((e) => {
|
|
2578
|
-
if (isStructureDefinition(e.resource)) {
|
|
2579
|
-
const sd = e.resource;
|
|
2580
|
-
const newSd = fn(pkgIndex.pkg, sd);
|
|
2581
|
-
if (sd.url !== newSd.url)
|
|
2582
|
-
throw new Error(`Patch update StructureDefinition URL: ${sd.url} !== ${newSd.url}`);
|
|
2583
|
-
e.resource = newSd;
|
|
2584
|
-
}
|
|
2585
|
-
});
|
|
2586
|
-
})
|
|
2427
|
+
// src/typeschema/index.ts
|
|
2428
|
+
var deduplicateSchemas = (schemasWithSources, logger) => {
|
|
2429
|
+
const groups = {};
|
|
2430
|
+
for (const item of schemasWithSources) {
|
|
2431
|
+
const key = `${item.schema.identifier.url}|${item.schema.identifier.package}`;
|
|
2432
|
+
const hash = hashSchema(item.schema);
|
|
2433
|
+
groups[key] ??= {};
|
|
2434
|
+
groups[key][hash] ??= { typeSchema: item.schema, sources: [] };
|
|
2435
|
+
groups[key][hash].sources.push(item);
|
|
2436
|
+
}
|
|
2437
|
+
const schemas = [];
|
|
2438
|
+
const collisions = {};
|
|
2439
|
+
for (const versions of Object.values(groups)) {
|
|
2440
|
+
const sorted = Object.values(versions).sort((a, b) => b.sources.length - a.sources.length);
|
|
2441
|
+
const best = sorted[0];
|
|
2442
|
+
if (!best) continue;
|
|
2443
|
+
schemas.push(best.typeSchema);
|
|
2444
|
+
if (sorted.length > 1) {
|
|
2445
|
+
const pkg = best.typeSchema.identifier.package;
|
|
2446
|
+
const url = best.typeSchema.identifier.url;
|
|
2447
|
+
logger?.dryWarn(`'${url}' from '${pkg}'' has ${sorted.length} versions`);
|
|
2448
|
+
collisions[pkg] ??= {};
|
|
2449
|
+
collisions[pkg][url] = sorted.flatMap(
|
|
2450
|
+
(v) => v.sources.map((s) => ({
|
|
2451
|
+
typeSchema: v.typeSchema,
|
|
2452
|
+
sourcePackage: s.sourcePackage,
|
|
2453
|
+
sourceCanonical: s.sourceCanonical
|
|
2454
|
+
}))
|
|
2587
2455
|
);
|
|
2588
|
-
enrichResolver(resolver);
|
|
2589
|
-
cachedResolutionTree = void 0;
|
|
2590
|
-
},
|
|
2591
|
-
allFs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.fhirSchemas)),
|
|
2592
|
-
allVs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.valueSets)),
|
|
2593
|
-
resolveVs,
|
|
2594
|
-
resolveAny: (canonicalUrl) => packageAgnosticResolveCanonical(resolver, canonicalUrl),
|
|
2595
|
-
resolveElementSnapshot,
|
|
2596
|
-
getAllElementKeys,
|
|
2597
|
-
resolver,
|
|
2598
|
-
resolutionTree: () => {
|
|
2599
|
-
if (cachedResolutionTree) return cachedResolutionTree;
|
|
2600
|
-
const res = {};
|
|
2601
|
-
for (const [_pkgId, pkgIndex] of Object.entries(resolver)) {
|
|
2602
|
-
const pkgName = pkgIndex.pkg.name;
|
|
2603
|
-
res[pkgName] = {};
|
|
2604
|
-
for (const [surl, resolutions] of Object.entries(pkgIndex.canonicalResolution)) {
|
|
2605
|
-
const url = surl;
|
|
2606
|
-
res[pkgName][url] = [];
|
|
2607
|
-
for (const resolution of resolutions) {
|
|
2608
|
-
res[pkgName][url].push({ deep: resolution.deep, pkg: resolution.pkg });
|
|
2609
|
-
}
|
|
2610
|
-
}
|
|
2611
|
-
}
|
|
2612
|
-
cachedResolutionTree = res;
|
|
2613
|
-
return res;
|
|
2614
|
-
}
|
|
2615
|
-
};
|
|
2616
|
-
};
|
|
2617
|
-
var resolveFsElementGenealogy = (genealogy, path) => {
|
|
2618
|
-
const [top, ...rest] = path;
|
|
2619
|
-
if (top === void 0) return [];
|
|
2620
|
-
return genealogy.map((fs7) => {
|
|
2621
|
-
if (!fs7.elements) return void 0;
|
|
2622
|
-
let elem = fs7.elements?.[top];
|
|
2623
|
-
for (const k of rest) {
|
|
2624
|
-
elem = elem?.elements?.[k];
|
|
2625
2456
|
}
|
|
2626
|
-
|
|
2627
|
-
|
|
2457
|
+
}
|
|
2458
|
+
return { schemas, collisions };
|
|
2628
2459
|
};
|
|
2629
|
-
function fsElementSnapshot(genealogy) {
|
|
2630
|
-
const revGenealogy = genealogy.reverse();
|
|
2631
|
-
const snapshot = Object.assign({}, ...revGenealogy);
|
|
2632
|
-
snapshot.elements = void 0;
|
|
2633
|
-
return snapshot;
|
|
2634
|
-
}
|
|
2635
|
-
|
|
2636
|
-
// src/typeschema/index.ts
|
|
2637
2460
|
var generateTypeSchemas = async (register, logger) => {
|
|
2638
|
-
const
|
|
2461
|
+
const schemasWithSources = [];
|
|
2639
2462
|
for (const fhirSchema of register.allFs()) {
|
|
2640
2463
|
const pkgId = packageMetaToFhir(fhirSchema.package_meta);
|
|
2641
2464
|
const skipCheck = shouldSkipCanonical(fhirSchema.package_meta, fhirSchema.url);
|
|
2642
2465
|
if (skipCheck.shouldSkip) {
|
|
2643
|
-
logger?.
|
|
2466
|
+
logger?.dryWarn(`Skip ${fhirSchema.url} from ${pkgId}. Reason: ${skipCheck.reason}`);
|
|
2644
2467
|
continue;
|
|
2645
2468
|
}
|
|
2646
|
-
|
|
2469
|
+
for (const schema of await transformFhirSchema(register, fhirSchema, logger)) {
|
|
2470
|
+
schemasWithSources.push({
|
|
2471
|
+
schema,
|
|
2472
|
+
sourcePackage: pkgId,
|
|
2473
|
+
sourceCanonical: fhirSchema.url
|
|
2474
|
+
});
|
|
2475
|
+
}
|
|
2647
2476
|
}
|
|
2648
2477
|
for (const vsSchema of register.allVs()) {
|
|
2649
|
-
|
|
2478
|
+
schemasWithSources.push({
|
|
2479
|
+
schema: await transformValueSet(register, vsSchema),
|
|
2480
|
+
sourcePackage: packageMetaToFhir(vsSchema.package_meta),
|
|
2481
|
+
sourceCanonical: vsSchema.url
|
|
2482
|
+
});
|
|
2650
2483
|
}
|
|
2651
|
-
return
|
|
2484
|
+
return deduplicateSchemas(schemasWithSources, logger);
|
|
2652
2485
|
};
|
|
2653
2486
|
|
|
2654
2487
|
// src/typeschema/ir/logic-promotion.ts
|
|
@@ -2829,38 +2662,316 @@ var treeShake = (tsIndex, treeShake2) => {
|
|
|
2829
2662
|
for (const schema of schemas) {
|
|
2830
2663
|
acc[JSON.stringify(schema.identifier)] = schema;
|
|
2831
2664
|
}
|
|
2832
|
-
const newSchemas = [];
|
|
2833
|
-
for (const schema of schemas) {
|
|
2834
|
-
if (isSpecializationTypeSchema(schema) || isProfileTypeSchema(schema)) {
|
|
2835
|
-
if (!schema.dependencies) continue;
|
|
2836
|
-
schema.dependencies.forEach((dep) => {
|
|
2837
|
-
const depSchema = tsIndex.resolve(dep);
|
|
2838
|
-
if (!depSchema)
|
|
2839
|
-
throw new Error(
|
|
2840
|
-
`Dependent schema ${JSON.stringify(dep)} not found for ${JSON.stringify(schema.identifier)}`
|
|
2841
|
-
);
|
|
2842
|
-
const id = JSON.stringify(depSchema.identifier);
|
|
2843
|
-
if (!acc[id]) newSchemas.push(depSchema);
|
|
2844
|
-
});
|
|
2845
|
-
if (schema.nested) {
|
|
2846
|
-
for (const nest of schema.nested) {
|
|
2847
|
-
if (isNestedIdentifier(nest.identifier)) continue;
|
|
2848
|
-
const id = JSON.stringify(nest.identifier);
|
|
2849
|
-
if (!acc[id]) newSchemas.push(nest);
|
|
2665
|
+
const newSchemas = [];
|
|
2666
|
+
for (const schema of schemas) {
|
|
2667
|
+
if (isSpecializationTypeSchema(schema) || isProfileTypeSchema(schema)) {
|
|
2668
|
+
if (!schema.dependencies) continue;
|
|
2669
|
+
schema.dependencies.forEach((dep) => {
|
|
2670
|
+
const depSchema = tsIndex.resolve(dep);
|
|
2671
|
+
if (!depSchema)
|
|
2672
|
+
throw new Error(
|
|
2673
|
+
`Dependent schema ${JSON.stringify(dep)} not found for ${JSON.stringify(schema.identifier)}`
|
|
2674
|
+
);
|
|
2675
|
+
const id = JSON.stringify(depSchema.identifier);
|
|
2676
|
+
if (!acc[id]) newSchemas.push(depSchema);
|
|
2677
|
+
});
|
|
2678
|
+
if (schema.nested) {
|
|
2679
|
+
for (const nest of schema.nested) {
|
|
2680
|
+
if (isNestedIdentifier(nest.identifier)) continue;
|
|
2681
|
+
const id = JSON.stringify(nest.identifier);
|
|
2682
|
+
if (!acc[id]) newSchemas.push(nest);
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2687
|
+
return collectDeps(newSchemas, acc);
|
|
2688
|
+
};
|
|
2689
|
+
const shaked = collectDeps(focusedSchemas, {});
|
|
2690
|
+
const shakedIndex = tsIndex.replaceSchemas(shaked);
|
|
2691
|
+
const treeShakeReport = { skippedPackages: [], packages: {} };
|
|
2692
|
+
const irReport = shakedIndex.irReport();
|
|
2693
|
+
irReport.treeShake = treeShakeReport;
|
|
2694
|
+
mutableFillReport(treeShakeReport, tsIndex, shakedIndex);
|
|
2695
|
+
return shakedIndex;
|
|
2696
|
+
};
|
|
2697
|
+
|
|
2698
|
+
// src/fhir-types/hl7-fhir-r4-core/CodeSystem.ts
|
|
2699
|
+
var isCodeSystem = (resource) => {
|
|
2700
|
+
return resource !== null && typeof resource === "object" && resource.resourceType === "CodeSystem";
|
|
2701
|
+
};
|
|
2702
|
+
|
|
2703
|
+
// src/fhir-types/hl7-fhir-r4-core/ValueSet.ts
|
|
2704
|
+
var isValueSet = (resource) => {
|
|
2705
|
+
return resource !== null && typeof resource === "object" && resource.resourceType === "ValueSet";
|
|
2706
|
+
};
|
|
2707
|
+
|
|
2708
|
+
// src/typeschema/register.ts
|
|
2709
|
+
var readPackageDependencies = async (manager, packageMeta2) => {
|
|
2710
|
+
const packageJSON = await manager.packageJson(packageMeta2.name);
|
|
2711
|
+
if (!packageJSON) return [];
|
|
2712
|
+
const dependencies = packageJSON.dependencies;
|
|
2713
|
+
if (dependencies !== void 0) {
|
|
2714
|
+
return Object.entries(dependencies).map(([name, version]) => {
|
|
2715
|
+
return { name, version };
|
|
2716
|
+
});
|
|
2717
|
+
}
|
|
2718
|
+
return [];
|
|
2719
|
+
};
|
|
2720
|
+
var mkEmptyPkgIndex = (pkg) => {
|
|
2721
|
+
return {
|
|
2722
|
+
pkg,
|
|
2723
|
+
canonicalResolution: {},
|
|
2724
|
+
fhirSchemas: {},
|
|
2725
|
+
valueSets: {}
|
|
2726
|
+
};
|
|
2727
|
+
};
|
|
2728
|
+
var mkPackageAwareResolver = async (manager, pkg, deep, acc, logger) => {
|
|
2729
|
+
const pkgId = packageMetaToFhir(pkg);
|
|
2730
|
+
logger?.info(`${" ".repeat(deep * 2)}+ ${pkgId}`);
|
|
2731
|
+
if (acc[pkgId]) return acc[pkgId];
|
|
2732
|
+
const index = mkEmptyPkgIndex(pkg);
|
|
2733
|
+
for (const resource of await manager.search({ package: pkg })) {
|
|
2734
|
+
const rawUrl = resource.url;
|
|
2735
|
+
if (!rawUrl) continue;
|
|
2736
|
+
if (!(isStructureDefinition(resource) || isValueSet(resource) || isCodeSystem(resource))) continue;
|
|
2737
|
+
const url = rawUrl;
|
|
2738
|
+
if (index.canonicalResolution[url]) logger?.dryWarn(`Duplicate canonical URL: ${url} at ${pkgId}.`);
|
|
2739
|
+
index.canonicalResolution[url] = [{ deep, pkg, pkgId, resource }];
|
|
2740
|
+
}
|
|
2741
|
+
const deps = await readPackageDependencies(manager, pkg);
|
|
2742
|
+
for (const depPkg of deps) {
|
|
2743
|
+
const { canonicalResolution } = await mkPackageAwareResolver(manager, depPkg, deep + 1, acc, logger);
|
|
2744
|
+
for (const [surl, resolutions] of Object.entries(canonicalResolution)) {
|
|
2745
|
+
const url = surl;
|
|
2746
|
+
index.canonicalResolution[url] = [...index.canonicalResolution[url] || [], ...resolutions];
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
for (const resolutionOptions of Object.values(index.canonicalResolution)) {
|
|
2750
|
+
resolutionOptions.sort((a, b) => a.deep - b.deep);
|
|
2751
|
+
}
|
|
2752
|
+
acc[pkgId] = index;
|
|
2753
|
+
return index;
|
|
2754
|
+
};
|
|
2755
|
+
var enrichResolver = (resolver, logger) => {
|
|
2756
|
+
for (const { pkg, canonicalResolution } of Object.values(resolver)) {
|
|
2757
|
+
const pkgId = packageMetaToFhir(pkg);
|
|
2758
|
+
if (!resolver[pkgId]) throw new Error(`Package ${pkgId} not found`);
|
|
2759
|
+
for (const [_url, options] of Object.entries(canonicalResolution)) {
|
|
2760
|
+
const resolition = options[0];
|
|
2761
|
+
if (!resolition) throw new Error(`Resource not found`);
|
|
2762
|
+
const resource = resolition.resource;
|
|
2763
|
+
const resourcePkg = resolition.pkg;
|
|
2764
|
+
if (isStructureDefinition(resource)) {
|
|
2765
|
+
const fs7 = fhirschema.translate(resource);
|
|
2766
|
+
const rfs = enrichFHIRSchema(fs7, resourcePkg);
|
|
2767
|
+
resolver[pkgId].fhirSchemas[rfs.url] = rfs;
|
|
2768
|
+
}
|
|
2769
|
+
if (isValueSet(resource)) {
|
|
2770
|
+
const rvs = enrichValueSet(resource, resourcePkg);
|
|
2771
|
+
resolver[pkgId].valueSets[rvs.url] = rvs;
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
};
|
|
2776
|
+
var packageAgnosticResolveCanonical = (resolver, url, _logger) => {
|
|
2777
|
+
const options = Object.values(resolver).flatMap((pkg) => pkg.canonicalResolution[url]);
|
|
2778
|
+
if (!options) throw new Error(`No canonical resolution found for ${url} in any package`);
|
|
2779
|
+
return options[0]?.resource;
|
|
2780
|
+
};
|
|
2781
|
+
var registerFromManager = async (manager, { logger, focusedPackages }) => {
|
|
2782
|
+
const packages = focusedPackages ?? await manager.packages();
|
|
2783
|
+
const resolver = {};
|
|
2784
|
+
for (const pkg of packages) {
|
|
2785
|
+
await mkPackageAwareResolver(manager, pkg, 0, resolver, logger);
|
|
2786
|
+
}
|
|
2787
|
+
enrichResolver(resolver);
|
|
2788
|
+
const resolveFs = (pkg, canonicalUrl) => {
|
|
2789
|
+
const pkgIndex = resolver[packageMetaToFhir(pkg)];
|
|
2790
|
+
if (pkgIndex) {
|
|
2791
|
+
const resolution = pkgIndex.canonicalResolution[canonicalUrl]?.[0];
|
|
2792
|
+
if (resolution) {
|
|
2793
|
+
return resolver[resolution.pkgId]?.fhirSchemas[canonicalUrl];
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
for (const idx of Object.values(resolver)) {
|
|
2797
|
+
const fs7 = idx.fhirSchemas[canonicalUrl];
|
|
2798
|
+
if (fs7 && fs7.package_meta.name === pkg.name) return fs7;
|
|
2799
|
+
}
|
|
2800
|
+
for (const idx of Object.values(resolver)) {
|
|
2801
|
+
const fs7 = idx.fhirSchemas[canonicalUrl];
|
|
2802
|
+
if (fs7) return fs7;
|
|
2803
|
+
}
|
|
2804
|
+
return void 0;
|
|
2805
|
+
};
|
|
2806
|
+
const resolveVs = (pkg, canonicalUrl) => {
|
|
2807
|
+
const pkgIndex = resolver[packageMetaToFhir(pkg)];
|
|
2808
|
+
if (pkgIndex) {
|
|
2809
|
+
const resolution = pkgIndex.canonicalResolution[canonicalUrl]?.[0];
|
|
2810
|
+
if (resolution) {
|
|
2811
|
+
return resolver[resolution.pkgId]?.valueSets[canonicalUrl];
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
for (const idx of Object.values(resolver)) {
|
|
2815
|
+
const vs = idx.valueSets[canonicalUrl];
|
|
2816
|
+
if (vs && vs.package_meta.name === pkg.name) return vs;
|
|
2817
|
+
}
|
|
2818
|
+
for (const idx of Object.values(resolver)) {
|
|
2819
|
+
const vs = idx.valueSets[canonicalUrl];
|
|
2820
|
+
if (vs) return vs;
|
|
2821
|
+
}
|
|
2822
|
+
return void 0;
|
|
2823
|
+
};
|
|
2824
|
+
const ensureSpecializationCanonicalUrl = (name) => {
|
|
2825
|
+
if (name.includes("|")) name = name.split("|")[0];
|
|
2826
|
+
if (name.match(/^[a-zA-Z0-9]+$/)) {
|
|
2827
|
+
return `http://hl7.org/fhir/StructureDefinition/${name}`;
|
|
2828
|
+
}
|
|
2829
|
+
return name;
|
|
2830
|
+
};
|
|
2831
|
+
const resolveFsGenealogy = (pkg, canonicalUrl) => {
|
|
2832
|
+
let fs7 = resolveFs(pkg, canonicalUrl);
|
|
2833
|
+
if (fs7 === void 0) throw new Error(`Failed to resolve FHIR Schema: '${canonicalUrl}'`);
|
|
2834
|
+
const genealogy = [fs7];
|
|
2835
|
+
while (fs7?.base) {
|
|
2836
|
+
const pkg2 = fs7.package_meta;
|
|
2837
|
+
const baseUrl = ensureSpecializationCanonicalUrl(fs7.base);
|
|
2838
|
+
fs7 = resolveFs(pkg2, baseUrl);
|
|
2839
|
+
if (fs7 === void 0)
|
|
2840
|
+
throw new Error(
|
|
2841
|
+
`Failed to resolve FHIR Schema base for '${canonicalUrl}'. Problem: '${baseUrl}' from '${packageMetaToFhir(pkg2)}'`
|
|
2842
|
+
);
|
|
2843
|
+
genealogy.push(fs7);
|
|
2844
|
+
}
|
|
2845
|
+
return genealogy;
|
|
2846
|
+
};
|
|
2847
|
+
const resolveFsSpecializations = (pkg, canonicalUrl) => {
|
|
2848
|
+
return resolveFsGenealogy(pkg, canonicalUrl).filter((fs7) => fs7.derivation === "specialization");
|
|
2849
|
+
};
|
|
2850
|
+
const resolveElementSnapshot = (fhirSchema, path) => {
|
|
2851
|
+
const geneology = resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url);
|
|
2852
|
+
const elemGeneology = resolveFsElementGenealogy(geneology, path);
|
|
2853
|
+
const elemSnapshot = fsElementSnapshot(elemGeneology);
|
|
2854
|
+
return elemSnapshot;
|
|
2855
|
+
};
|
|
2856
|
+
const getAllElementKeys = (elems) => {
|
|
2857
|
+
const keys = /* @__PURE__ */ new Set();
|
|
2858
|
+
for (const [key, elem] of Object.entries(elems)) {
|
|
2859
|
+
keys.add(key);
|
|
2860
|
+
for (const choiceKey of elem?.choices || []) {
|
|
2861
|
+
if (!elems[choiceKey]) {
|
|
2862
|
+
keys.add(choiceKey);
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
return Array.from(keys);
|
|
2867
|
+
};
|
|
2868
|
+
let cachedResolutionTree;
|
|
2869
|
+
return {
|
|
2870
|
+
testAppendFs(rfs) {
|
|
2871
|
+
const pkgId = packageMetaToFhir(rfs.package_meta);
|
|
2872
|
+
if (!resolver[pkgId]) resolver[pkgId] = mkEmptyPkgIndex(rfs.package_meta);
|
|
2873
|
+
resolver[pkgId].fhirSchemas[rfs.url] = rfs;
|
|
2874
|
+
cachedResolutionTree = void 0;
|
|
2875
|
+
},
|
|
2876
|
+
resolveFs,
|
|
2877
|
+
resolveFsGenealogy,
|
|
2878
|
+
resolveFsSpecializations,
|
|
2879
|
+
ensureSpecializationCanonicalUrl,
|
|
2880
|
+
resolveSd: (pkg, canonicalUrl) => {
|
|
2881
|
+
const res = resolver[packageMetaToFhir(pkg)]?.canonicalResolution[canonicalUrl]?.[0]?.resource;
|
|
2882
|
+
if (isStructureDefinition(res)) return res;
|
|
2883
|
+
return void 0;
|
|
2884
|
+
},
|
|
2885
|
+
allSd: () => Object.values(resolver).flatMap(
|
|
2886
|
+
(pkgIndex) => Object.values(pkgIndex.canonicalResolution).flatMap(
|
|
2887
|
+
(resolutions) => resolutions.map((r) => {
|
|
2888
|
+
const sd = r.resource;
|
|
2889
|
+
if (!sd.package_name) {
|
|
2890
|
+
return {
|
|
2891
|
+
...sd,
|
|
2892
|
+
package_name: pkgIndex.pkg.name,
|
|
2893
|
+
package_version: pkgIndex.pkg.version
|
|
2894
|
+
};
|
|
2895
|
+
}
|
|
2896
|
+
return sd;
|
|
2897
|
+
})
|
|
2898
|
+
)
|
|
2899
|
+
).filter((r) => isStructureDefinition(r)).sort((sd1, sd2) => sd1.url.localeCompare(sd2.url)),
|
|
2900
|
+
patchSd: (fn) => {
|
|
2901
|
+
Object.values(resolver).flatMap(
|
|
2902
|
+
(pkgIndex) => Object.values(pkgIndex.canonicalResolution).forEach((resolutions) => {
|
|
2903
|
+
resolutions.forEach((e) => {
|
|
2904
|
+
if (isStructureDefinition(e.resource)) {
|
|
2905
|
+
const sd = e.resource;
|
|
2906
|
+
const newSd = fn(pkgIndex.pkg, sd);
|
|
2907
|
+
if (sd.url !== newSd.url)
|
|
2908
|
+
throw new Error(`Patch update StructureDefinition URL: ${sd.url} !== ${newSd.url}`);
|
|
2909
|
+
e.resource = newSd;
|
|
2910
|
+
}
|
|
2911
|
+
});
|
|
2912
|
+
})
|
|
2913
|
+
);
|
|
2914
|
+
enrichResolver(resolver);
|
|
2915
|
+
cachedResolutionTree = void 0;
|
|
2916
|
+
},
|
|
2917
|
+
allFs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.fhirSchemas)),
|
|
2918
|
+
allVs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.valueSets)),
|
|
2919
|
+
resolveVs,
|
|
2920
|
+
resolveAny: (canonicalUrl) => packageAgnosticResolveCanonical(resolver, canonicalUrl),
|
|
2921
|
+
resolveElementSnapshot,
|
|
2922
|
+
getAllElementKeys,
|
|
2923
|
+
resolver,
|
|
2924
|
+
resolutionTree: () => {
|
|
2925
|
+
if (cachedResolutionTree) return cachedResolutionTree;
|
|
2926
|
+
const res = {};
|
|
2927
|
+
for (const [_pkgId, pkgIndex] of Object.entries(resolver)) {
|
|
2928
|
+
const pkgName = pkgIndex.pkg.name;
|
|
2929
|
+
res[pkgName] = {};
|
|
2930
|
+
for (const [surl, resolutions] of Object.entries(pkgIndex.canonicalResolution)) {
|
|
2931
|
+
const url = surl;
|
|
2932
|
+
res[pkgName][url] = [];
|
|
2933
|
+
for (const resolution of resolutions) {
|
|
2934
|
+
res[pkgName][url].push({ deep: resolution.deep, pkg: resolution.pkg });
|
|
2850
2935
|
}
|
|
2851
2936
|
}
|
|
2852
2937
|
}
|
|
2938
|
+
cachedResolutionTree = res;
|
|
2939
|
+
return res;
|
|
2853
2940
|
}
|
|
2854
|
-
return collectDeps(newSchemas, acc);
|
|
2855
2941
|
};
|
|
2856
|
-
const shaked = collectDeps(focusedSchemas, {});
|
|
2857
|
-
const shakedIndex = tsIndex.replaceSchemas(shaked);
|
|
2858
|
-
const treeShakeReport = { skippedPackages: [], packages: {} };
|
|
2859
|
-
const irReport = shakedIndex.irReport();
|
|
2860
|
-
irReport.treeShake = treeShakeReport;
|
|
2861
|
-
mutableFillReport(treeShakeReport, tsIndex, shakedIndex);
|
|
2862
|
-
return shakedIndex;
|
|
2863
2942
|
};
|
|
2943
|
+
var registerFromPackageMetas = async (packageMetas, conf) => {
|
|
2944
|
+
const packageNames = packageMetas.map(packageMetaToNpm);
|
|
2945
|
+
conf?.logger?.step(`Loading FHIR packages: ${packageNames.join(", ")}`);
|
|
2946
|
+
const manager = CanonicalManager({
|
|
2947
|
+
packages: packageNames,
|
|
2948
|
+
workingDir: "tmp/fhir",
|
|
2949
|
+
registry: conf.registry || void 0
|
|
2950
|
+
});
|
|
2951
|
+
await manager.init();
|
|
2952
|
+
return await registerFromManager(manager, {
|
|
2953
|
+
...conf,
|
|
2954
|
+
focusedPackages: packageMetas
|
|
2955
|
+
});
|
|
2956
|
+
};
|
|
2957
|
+
var resolveFsElementGenealogy = (genealogy, path) => {
|
|
2958
|
+
const [top, ...rest] = path;
|
|
2959
|
+
if (top === void 0) return [];
|
|
2960
|
+
return genealogy.map((fs7) => {
|
|
2961
|
+
if (!fs7.elements) return void 0;
|
|
2962
|
+
let elem = fs7.elements?.[top];
|
|
2963
|
+
for (const k of rest) {
|
|
2964
|
+
elem = elem?.elements?.[k];
|
|
2965
|
+
}
|
|
2966
|
+
return elem;
|
|
2967
|
+
}).filter((elem) => elem !== void 0);
|
|
2968
|
+
};
|
|
2969
|
+
function fsElementSnapshot(genealogy) {
|
|
2970
|
+
const revGenealogy = genealogy.reverse();
|
|
2971
|
+
const snapshot = Object.assign({}, ...revGenealogy);
|
|
2972
|
+
snapshot.elements = void 0;
|
|
2973
|
+
return snapshot;
|
|
2974
|
+
}
|
|
2864
2975
|
var normalizeFileName = (str) => {
|
|
2865
2976
|
const res = str.replace(/[^a-zA-Z0-9\-_.@#()]/g, "");
|
|
2866
2977
|
if (res.length === 0) return "unknown";
|
|
@@ -2902,23 +3013,60 @@ var IntrospectionWriter = class extends FileSystemWriter {
|
|
|
2902
3013
|
this.logger()?.info(`IntrospectionWriter: Type tree written to ${this.opts.typeTree}`);
|
|
2903
3014
|
}
|
|
2904
3015
|
if (this.opts.typeSchemas) {
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
if (Path5.extname(outputPath) === ".ndjson") {
|
|
2908
|
-
this.writeNdjson(typeSchemas, outputPath, typeSchemaToJson);
|
|
3016
|
+
if (Path5.extname(this.opts.typeSchemas) === ".ndjson") {
|
|
3017
|
+
this.writeNdjson(tsIndex.schemas, this.opts.typeSchemas, typeSchemaToJson);
|
|
2909
3018
|
} else {
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
3019
|
+
const items = tsIndex.schemas.map((ts) => typeSchemaToJson(ts, true));
|
|
3020
|
+
const seenFilenames = /* @__PURE__ */ new Set();
|
|
3021
|
+
const dedupedItems = items.filter((item) => {
|
|
3022
|
+
if (seenFilenames.has(item.filename)) return false;
|
|
3023
|
+
seenFilenames.add(item.filename);
|
|
3024
|
+
return true;
|
|
3025
|
+
});
|
|
3026
|
+
this.cd(this.opts.typeSchemas, () => {
|
|
3027
|
+
for (const { filename, genContent } of dedupedItems) {
|
|
3028
|
+
const fileName = `${filename}.json`;
|
|
3029
|
+
this.cd(Path5.dirname(fileName), () => {
|
|
3030
|
+
this.cat(Path5.basename(fileName), () => {
|
|
3031
|
+
this.write(genContent());
|
|
3032
|
+
});
|
|
3033
|
+
});
|
|
3034
|
+
}
|
|
3035
|
+
for (const [pkg, canonicals] of Object.entries(tsIndex.irReport().collisions ?? {})) {
|
|
3036
|
+
this.cd(`${normalizeFileName(pkg)}`, () => {
|
|
3037
|
+
for (const [canonical, entries] of Object.entries(canonicals)) {
|
|
3038
|
+
if (entries.length <= 1) continue;
|
|
3039
|
+
const firstEntry = entries[0];
|
|
3040
|
+
assert3(firstEntry);
|
|
3041
|
+
const name = normalizeFileName(
|
|
3042
|
+
`${firstEntry.typeSchema.identifier.name}(${extractNameFromCanonical(canonical)})`
|
|
3043
|
+
);
|
|
3044
|
+
this.cd(Path5.join("collisions", name), () => {
|
|
3045
|
+
for (let i = 0; i < entries.length; i++) {
|
|
3046
|
+
const entry = entries[i];
|
|
3047
|
+
this.cat(`${i + 1}.json`, () => {
|
|
3048
|
+
this.write(JSON.stringify(entry, null, 2));
|
|
3049
|
+
});
|
|
3050
|
+
}
|
|
3051
|
+
});
|
|
3052
|
+
}
|
|
3053
|
+
});
|
|
3054
|
+
}
|
|
3055
|
+
});
|
|
2914
3056
|
}
|
|
2915
3057
|
this.logger()?.info(
|
|
2916
|
-
`IntrospectionWriter: ${
|
|
3058
|
+
`IntrospectionWriter: ${tsIndex.schemas.length} TypeSchema written to ${this.opts.typeSchemas}`
|
|
2917
3059
|
);
|
|
2918
3060
|
}
|
|
2919
3061
|
if (this.opts.fhirSchemas && tsIndex.register) {
|
|
2920
3062
|
const outputPath = this.opts.fhirSchemas;
|
|
2921
|
-
const
|
|
3063
|
+
const allFs = tsIndex.register.allFs();
|
|
3064
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
3065
|
+
const fhirSchemas = allFs.filter((fs7) => {
|
|
3066
|
+
if (seenUrls.has(fs7.url)) return false;
|
|
3067
|
+
seenUrls.add(fs7.url);
|
|
3068
|
+
return true;
|
|
3069
|
+
});
|
|
2922
3070
|
if (Path5.extname(outputPath) === ".ndjson") {
|
|
2923
3071
|
this.writeNdjson(fhirSchemas, outputPath, fhirSchemaToJson);
|
|
2924
3072
|
} else {
|
|
@@ -2931,7 +3079,13 @@ var IntrospectionWriter = class extends FileSystemWriter {
|
|
|
2931
3079
|
}
|
|
2932
3080
|
if (this.opts.structureDefinitions && tsIndex.register) {
|
|
2933
3081
|
const outputPath = this.opts.structureDefinitions;
|
|
2934
|
-
const
|
|
3082
|
+
const allSd = tsIndex.register.allSd();
|
|
3083
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
3084
|
+
const structureDefinitions = allSd.filter((sd) => {
|
|
3085
|
+
if (seenUrls.has(sd.url)) return false;
|
|
3086
|
+
seenUrls.add(sd.url);
|
|
3087
|
+
return true;
|
|
3088
|
+
});
|
|
2935
3089
|
if (Path5.extname(outputPath) === ".ndjson") {
|
|
2936
3090
|
this.writeNdjson(structureDefinitions, outputPath, structureDefinitionToJson);
|
|
2937
3091
|
} else {
|
|
@@ -2984,58 +3138,118 @@ var IntrospectionWriter = class extends FileSystemWriter {
|
|
|
2984
3138
|
};
|
|
2985
3139
|
|
|
2986
3140
|
// src/typeschema/ir/report.ts
|
|
2987
|
-
var
|
|
2988
|
-
|
|
2989
|
-
const
|
|
2990
|
-
|
|
2991
|
-
...Object.keys(report.logicalPromotion?.packages ?? {})
|
|
2992
|
-
]);
|
|
2993
|
-
if (allPackages.size === 0) {
|
|
2994
|
-
lines.push("No IR modifications applied.");
|
|
2995
|
-
return lines.join("\n");
|
|
3141
|
+
var generateSkippedPackagesSection = (lines, skippedPackages) => {
|
|
3142
|
+
lines.push("## Skipped Packages", "");
|
|
3143
|
+
for (const pkg of skippedPackages) {
|
|
3144
|
+
lines.push(`- ${pkg}`);
|
|
2996
3145
|
}
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3146
|
+
lines.push("");
|
|
3147
|
+
};
|
|
3148
|
+
var generatePackageSection = (lines, pkgName, treeShakePkg, promotedCanonicals) => {
|
|
3149
|
+
lines.push(`## Package: \`${pkgName}\``, "");
|
|
3150
|
+
if (promotedCanonicals?.length) {
|
|
3151
|
+
lines.push("### Promoted Logical Models", "");
|
|
3152
|
+
for (const canonical of promotedCanonicals) {
|
|
3153
|
+
lines.push(`- \`${canonical}\``);
|
|
3001
3154
|
}
|
|
3002
3155
|
lines.push("");
|
|
3003
3156
|
}
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3157
|
+
if (!treeShakePkg) return;
|
|
3158
|
+
const canonicalsWithChanges = Object.entries(treeShakePkg.canonicals).filter(
|
|
3159
|
+
([_, data]) => data.skippedFields.length > 0
|
|
3160
|
+
);
|
|
3161
|
+
if (canonicalsWithChanges.length > 0) {
|
|
3162
|
+
lines.push("### Modified Canonicals", "");
|
|
3163
|
+
for (const [canonical, data] of canonicalsWithChanges) {
|
|
3164
|
+
lines.push(`#### \`${canonical}\``, "");
|
|
3165
|
+
lines.push("Skipped fields:", "");
|
|
3166
|
+
for (const field of data.skippedFields) {
|
|
3167
|
+
lines.push(`- \`${field}\``);
|
|
3012
3168
|
}
|
|
3013
3169
|
lines.push("");
|
|
3014
3170
|
}
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3171
|
+
}
|
|
3172
|
+
if (treeShakePkg.skippedCanonicals.length > 0) {
|
|
3173
|
+
lines.push("### Skipped Canonicals", "");
|
|
3174
|
+
for (const canonical of treeShakePkg.skippedCanonicals) {
|
|
3175
|
+
lines.push(`- \`${canonical}\``);
|
|
3176
|
+
}
|
|
3177
|
+
lines.push("");
|
|
3178
|
+
}
|
|
3179
|
+
};
|
|
3180
|
+
var generateCollisionVersionLines = (entries) => {
|
|
3181
|
+
const uniqueSchemas = /* @__PURE__ */ new Map();
|
|
3182
|
+
for (const entry of entries) {
|
|
3183
|
+
const key = JSON.stringify(entry.typeSchema);
|
|
3184
|
+
if (!uniqueSchemas.has(key)) uniqueSchemas.set(key, []);
|
|
3185
|
+
uniqueSchemas.get(key)?.push(entry);
|
|
3186
|
+
}
|
|
3187
|
+
const versionLines = [];
|
|
3188
|
+
const sortedVersions = [...uniqueSchemas.values()].sort((a, b) => b.length - a.length);
|
|
3189
|
+
let version = 1;
|
|
3190
|
+
for (const schemaEntries of sortedVersions) {
|
|
3191
|
+
const sourceList = schemaEntries.map((e) => {
|
|
3192
|
+
const name = extractNameFromCanonical(e.sourceCanonical) ?? e.sourceCanonical;
|
|
3193
|
+
return `${name} (${e.sourcePackage})`;
|
|
3194
|
+
}).join(", ");
|
|
3195
|
+
versionLines.push(` - Version ${version++}: ${sourceList}`);
|
|
3196
|
+
}
|
|
3197
|
+
return versionLines;
|
|
3198
|
+
};
|
|
3199
|
+
var generateCollisionsSection = (lines, collisions) => {
|
|
3200
|
+
if (!collisions) return;
|
|
3201
|
+
lines.push("## Schema Collisions", "");
|
|
3202
|
+
lines.push("The following canonicals have multiple schema versions with different content.");
|
|
3203
|
+
lines.push("To inspect collision versions, export TypeSchemas using `.introspection({ typeSchemas: 'path' })`");
|
|
3204
|
+
lines.push("and check `<pkg>/collisions/<name>/1.json, 2.json, ...` files.", "");
|
|
3205
|
+
const collisionPackages = Object.keys(collisions).sort();
|
|
3206
|
+
for (const pkgName of collisionPackages) {
|
|
3207
|
+
const collisionsPkg = collisions[pkgName];
|
|
3208
|
+
if (!collisionsPkg) throw new Error(`Missing collisions for package ${pkgName}`);
|
|
3209
|
+
const sortedEntries = Object.entries(collisionsPkg).sort(([a], [b]) => {
|
|
3210
|
+
const nameA = a.split("/").pop() ?? a;
|
|
3211
|
+
const nameB = b.split("/").pop() ?? b;
|
|
3212
|
+
return nameA.localeCompare(nameB);
|
|
3213
|
+
});
|
|
3214
|
+
if (sortedEntries.length > 0) {
|
|
3215
|
+
lines.push(`### \`${pkgName}\``, "");
|
|
3216
|
+
for (const [canonical, entries] of sortedEntries) {
|
|
3217
|
+
const versionLines = generateCollisionVersionLines(entries);
|
|
3218
|
+
lines.push(`- \`${canonical}\` (${versionLines.length} versions)`);
|
|
3219
|
+
lines.push(...versionLines);
|
|
3036
3220
|
}
|
|
3221
|
+
lines.push("");
|
|
3037
3222
|
}
|
|
3038
3223
|
}
|
|
3224
|
+
};
|
|
3225
|
+
var generateIrReportReadme = (report) => {
|
|
3226
|
+
const lines = ["# IR Report", ""];
|
|
3227
|
+
const irPackages = [
|
|
3228
|
+
.../* @__PURE__ */ new Set([
|
|
3229
|
+
...Object.keys(report.treeShake?.packages ?? {}),
|
|
3230
|
+
...Object.keys(report.logicalPromotion?.packages ?? {})
|
|
3231
|
+
])
|
|
3232
|
+
].sort();
|
|
3233
|
+
const hasIrChanges = irPackages.length > 0 || (report.treeShake?.skippedPackages.length ?? 0) > 0;
|
|
3234
|
+
const hasCollisions = Object.keys(report.collisions ?? {}).length > 0;
|
|
3235
|
+
if (!hasIrChanges && !hasCollisions) {
|
|
3236
|
+
lines.push("No IR modifications applied.");
|
|
3237
|
+
return lines.join("\n");
|
|
3238
|
+
}
|
|
3239
|
+
if (report.treeShake?.skippedPackages.length) {
|
|
3240
|
+
generateSkippedPackagesSection(lines, report.treeShake.skippedPackages);
|
|
3241
|
+
}
|
|
3242
|
+
for (const pkgName of irPackages) {
|
|
3243
|
+
generatePackageSection(
|
|
3244
|
+
lines,
|
|
3245
|
+
pkgName,
|
|
3246
|
+
report.treeShake?.packages[pkgName],
|
|
3247
|
+
report.logicalPromotion?.packages[pkgName]?.promotedCanonicals
|
|
3248
|
+
);
|
|
3249
|
+
}
|
|
3250
|
+
if (hasCollisions) {
|
|
3251
|
+
generateCollisionsSection(lines, report.collisions);
|
|
3252
|
+
}
|
|
3039
3253
|
return lines.join("\n");
|
|
3040
3254
|
};
|
|
3041
3255
|
|
|
@@ -3407,25 +3621,27 @@ var ViewModelFactory = class {
|
|
|
3407
3621
|
hasFields: fields.length > 0,
|
|
3408
3622
|
hasNestedComplexTypes: nestedComplexTypes.length > 0,
|
|
3409
3623
|
hasNestedEnums: nestedEnums.length > 0,
|
|
3410
|
-
fields: fields.filter(
|
|
3624
|
+
fields: fields.filter(
|
|
3625
|
+
(entry) => isNotChoiceDeclarationField(entry[1])
|
|
3626
|
+
).sort((a, b) => a[0].localeCompare(b[0])).map(([fieldName, field]) => {
|
|
3411
3627
|
return {
|
|
3412
3628
|
owner: name,
|
|
3413
|
-
schema:
|
|
3629
|
+
schema: field,
|
|
3414
3630
|
name: fieldName,
|
|
3415
3631
|
saveName: this.nameGenerator.generateField(fieldName),
|
|
3416
|
-
typeName: this.nameGenerator.generateFieldType(
|
|
3417
|
-
isArray:
|
|
3418
|
-
isRequired:
|
|
3419
|
-
isEnum: !!
|
|
3420
|
-
isSizeConstrained:
|
|
3421
|
-
min:
|
|
3422
|
-
max:
|
|
3423
|
-
isResource: this._createIsResource(
|
|
3424
|
-
isComplexType: this._createIsComplexType(
|
|
3425
|
-
isPrimitive: this._createIsPrimitiveType(
|
|
3426
|
-
isCode:
|
|
3427
|
-
isIdentifier:
|
|
3428
|
-
isReference:
|
|
3632
|
+
typeName: this.nameGenerator.generateFieldType(field),
|
|
3633
|
+
isArray: field.array ?? false,
|
|
3634
|
+
isRequired: field.required ?? false,
|
|
3635
|
+
isEnum: !!field.enum && !field.enum.isOpen,
|
|
3636
|
+
isSizeConstrained: field.min !== void 0 || field.max !== void 0,
|
|
3637
|
+
min: field.min,
|
|
3638
|
+
max: field.max,
|
|
3639
|
+
isResource: this._createIsResource(field.type),
|
|
3640
|
+
isComplexType: this._createIsComplexType(field.type),
|
|
3641
|
+
isPrimitive: this._createIsPrimitiveType(field.type),
|
|
3642
|
+
isCode: field.type?.name === "code",
|
|
3643
|
+
isIdentifier: field.type?.name === "Identifier",
|
|
3644
|
+
isReference: field.type?.name === "Reference"
|
|
3429
3645
|
};
|
|
3430
3646
|
})
|
|
3431
3647
|
};
|
|
@@ -3501,11 +3717,11 @@ var ViewModelFactory = class {
|
|
|
3501
3717
|
_collectNestedEnums(fields) {
|
|
3502
3718
|
const nestedEnumValues = {};
|
|
3503
3719
|
fields.forEach(([fieldName, fieldSchema]) => {
|
|
3504
|
-
if ("enum" in fieldSchema && fieldSchema.enum) {
|
|
3720
|
+
if ("enum" in fieldSchema && fieldSchema.enum && !fieldSchema.enum.isOpen) {
|
|
3505
3721
|
const name = ("binding" in fieldSchema && fieldSchema.binding?.name) ?? fieldName;
|
|
3506
3722
|
if (typeof name === "string") {
|
|
3507
3723
|
nestedEnumValues[name] = nestedEnumValues[name] ?? /* @__PURE__ */ new Set();
|
|
3508
|
-
fieldSchema.enum
|
|
3724
|
+
fieldSchema.enum.values.forEach(nestedEnumValues[name].add.bind(nestedEnumValues[name]));
|
|
3509
3725
|
}
|
|
3510
3726
|
}
|
|
3511
3727
|
});
|
|
@@ -3734,13 +3950,22 @@ var tsFhirPackageDir = (name) => {
|
|
|
3734
3950
|
var tsModuleName = (id) => {
|
|
3735
3951
|
return uppercaseFirstLetter(normalizeTsName(id.name));
|
|
3736
3952
|
};
|
|
3953
|
+
var tsProfileModuleName = (tsIndex, schema) => {
|
|
3954
|
+
const resourceSchema = tsIndex.findLastSpecialization(schema);
|
|
3955
|
+
const resourceName = uppercaseFirstLetter(normalizeTsName(resourceSchema.identifier.name));
|
|
3956
|
+
const profileName = extractNameFromCanonical(schema.identifier.url);
|
|
3957
|
+
if (profileName) {
|
|
3958
|
+
return `${resourceName}_${normalizeTsName(profileName)}`;
|
|
3959
|
+
}
|
|
3960
|
+
return `${resourceName}_${normalizeTsName(schema.identifier.name)}`;
|
|
3961
|
+
};
|
|
3737
3962
|
var tsModuleFileName = (id) => {
|
|
3738
3963
|
return `${tsModuleName(id)}.ts`;
|
|
3739
3964
|
};
|
|
3965
|
+
var tsProfileModuleFileName = (tsIndex, schema) => {
|
|
3966
|
+
return `${tsProfileModuleName(tsIndex, schema)}.ts`;
|
|
3967
|
+
};
|
|
3740
3968
|
var tsModulePath = (id) => {
|
|
3741
|
-
if (isProfileIdentifier(id)) {
|
|
3742
|
-
return `${tsFhirPackageDir(id.package)}/profiles/${tsModuleName(id)}`;
|
|
3743
|
-
}
|
|
3744
3969
|
return `${tsFhirPackageDir(id.package)}/${tsModuleName(id)}`;
|
|
3745
3970
|
};
|
|
3746
3971
|
var canonicalToName3 = (canonical, dropFragment = true) => {
|
|
@@ -3752,9 +3977,9 @@ var canonicalToName3 = (canonical, dropFragment = true) => {
|
|
|
3752
3977
|
var tsResourceName = (id) => {
|
|
3753
3978
|
if (id.kind === "nested") {
|
|
3754
3979
|
const url = id.url;
|
|
3755
|
-
const
|
|
3756
|
-
if (!
|
|
3757
|
-
const [resourceName, fragment] =
|
|
3980
|
+
const localName = extractNameFromCanonical(url, false);
|
|
3981
|
+
if (!localName) return "";
|
|
3982
|
+
const [resourceName, fragment] = localName.split("#");
|
|
3758
3983
|
const name = uppercaseFirstLetterOfEach((fragment ?? "").split(".")).join("");
|
|
3759
3984
|
return normalizeTsName([resourceName, name].join(""));
|
|
3760
3985
|
}
|
|
@@ -3768,14 +3993,37 @@ var tsFieldName = (n) => {
|
|
|
3768
3993
|
};
|
|
3769
3994
|
var normalizeTsName = (n) => {
|
|
3770
3995
|
if (tsKeywords.has(n)) n = `${n}_`;
|
|
3771
|
-
return n.replace(/\[x\]/g, "_x_").replace(/[-
|
|
3996
|
+
return n.replace(/\[x\]/g, "_x_").replace(/[- :.]/g, "_");
|
|
3772
3997
|
};
|
|
3773
3998
|
var tsGet = (object, tsFieldName2) => {
|
|
3774
3999
|
if (tsFieldName2.startsWith('"')) return `${object}[${tsFieldName2}]`;
|
|
3775
4000
|
return `${object}.${tsFieldName2}`;
|
|
3776
4001
|
};
|
|
3777
|
-
var tsEnumType = (
|
|
3778
|
-
|
|
4002
|
+
var tsEnumType = (enumDef) => {
|
|
4003
|
+
const values = enumDef.values.map((e) => `"${e}"`).join(" | ");
|
|
4004
|
+
return enumDef.isOpen ? `(${values} | string)` : `(${values})`;
|
|
4005
|
+
};
|
|
4006
|
+
var rewriteFieldTypeDefs = {
|
|
4007
|
+
Coding: { code: () => "T" },
|
|
4008
|
+
// biome-ignore lint: that is exactly string what we want
|
|
4009
|
+
Reference: { reference: () => "`${T}/${string}`" },
|
|
4010
|
+
CodeableConcept: { coding: () => "Coding<T>" }
|
|
4011
|
+
};
|
|
4012
|
+
var resolveFieldTsType = (schemaName, tsName, field) => {
|
|
4013
|
+
const rewriteFieldType = rewriteFieldTypeDefs[schemaName]?.[tsName];
|
|
4014
|
+
if (rewriteFieldType) return rewriteFieldType();
|
|
4015
|
+
if (field.enum) {
|
|
4016
|
+
if (field.type.name === "Coding") return `Coding<${tsEnumType(field.enum)}>`;
|
|
4017
|
+
if (field.type.name === "CodeableConcept") return `CodeableConcept<${tsEnumType(field.enum)}>`;
|
|
4018
|
+
return tsEnumType(field.enum);
|
|
4019
|
+
}
|
|
4020
|
+
if (field.reference && field.reference.length > 0) {
|
|
4021
|
+
const references = field.reference.map((ref) => `"${ref.name}"`).join(" | ");
|
|
4022
|
+
return `Reference<${references}>`;
|
|
4023
|
+
}
|
|
4024
|
+
if (isPrimitiveIdentifier(field.type)) return resolvePrimitiveType(field.type.name);
|
|
4025
|
+
if (isNestedIdentifier(field.type)) return tsResourceName(field.type);
|
|
4026
|
+
return field.type.name;
|
|
3779
4027
|
};
|
|
3780
4028
|
var tsTypeFromIdentifier = (id) => {
|
|
3781
4029
|
if (isNestedIdentifier(id)) return tsResourceName(id);
|
|
@@ -3784,8 +4032,12 @@ var tsTypeFromIdentifier = (id) => {
|
|
|
3784
4032
|
if (primitiveType !== void 0) return primitiveType;
|
|
3785
4033
|
return id.name;
|
|
3786
4034
|
};
|
|
3787
|
-
var tsProfileClassName = (
|
|
3788
|
-
|
|
4035
|
+
var tsProfileClassName = (schema) => {
|
|
4036
|
+
const profileName = extractNameFromCanonical(schema.identifier.url);
|
|
4037
|
+
if (profileName) {
|
|
4038
|
+
return `${normalizeTsName(profileName)}Profile`;
|
|
4039
|
+
}
|
|
4040
|
+
return `${normalizeTsName(schema.identifier.name)}Profile`;
|
|
3789
4041
|
};
|
|
3790
4042
|
var tsSliceInputTypeName = (profileName, fieldName, sliceName) => {
|
|
3791
4043
|
return `${uppercaseFirstLetter(profileName)}_${uppercaseFirstLetter(normalizeTsName(fieldName))}_${uppercaseFirstLetter(normalizeTsName(sliceName))}SliceInput`;
|
|
@@ -3821,21 +4073,33 @@ var TypeScript = class extends Writer {
|
|
|
3821
4073
|
tsImportType(tsPackageName, ...entities) {
|
|
3822
4074
|
this.lineSM(`import type { ${entities.join(", ")} } from "${tsPackageName}"`);
|
|
3823
4075
|
}
|
|
3824
|
-
generateProfileIndexFile(
|
|
3825
|
-
if (
|
|
4076
|
+
generateProfileIndexFile(tsIndex, initialProfiles) {
|
|
4077
|
+
if (initialProfiles.length === 0) return;
|
|
3826
4078
|
this.cd("profiles", () => {
|
|
3827
4079
|
this.cat("index.ts", () => {
|
|
3828
|
-
const
|
|
3829
|
-
|
|
3830
|
-
const
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
4080
|
+
const profiles = initialProfiles.map((profile) => {
|
|
4081
|
+
const className = tsProfileClassName(profile);
|
|
4082
|
+
const resourceName = tsResourceName(profile.identifier);
|
|
4083
|
+
const overrides = this.detectFieldOverrides(tsIndex, profile);
|
|
4084
|
+
let typeExport;
|
|
4085
|
+
if (overrides.size > 0) typeExport = resourceName;
|
|
4086
|
+
return [profile, className, typeExport];
|
|
3834
4087
|
});
|
|
3835
|
-
if (
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
4088
|
+
if (profiles.length === 0) return;
|
|
4089
|
+
const classExports = /* @__PURE__ */ new Map();
|
|
4090
|
+
const typeExports = /* @__PURE__ */ new Map();
|
|
4091
|
+
for (const [profile, className, typeName] of profiles) {
|
|
4092
|
+
const moduleName = tsProfileModuleName(tsIndex, profile);
|
|
4093
|
+
if (!classExports.has(className)) {
|
|
4094
|
+
classExports.set(className, `export { ${className} } from "./${moduleName}"`);
|
|
4095
|
+
}
|
|
4096
|
+
if (typeName && !typeExports.has(typeName)) {
|
|
4097
|
+
typeExports.set(typeName, `export type { ${typeName} } from "./${moduleName}"`);
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
const allExports = [...classExports.values(), ...typeExports.values()].sort();
|
|
4101
|
+
for (const exp of allExports) {
|
|
4102
|
+
this.lineSM(exp);
|
|
3839
4103
|
}
|
|
3840
4104
|
});
|
|
3841
4105
|
});
|
|
@@ -3935,8 +4199,9 @@ var TypeScript = class extends Writer {
|
|
|
3935
4199
|
}
|
|
3936
4200
|
generateType(tsIndex, schema) {
|
|
3937
4201
|
let name;
|
|
3938
|
-
|
|
3939
|
-
|
|
4202
|
+
const genericTypes = ["Reference", "Coding", "CodeableConcept"];
|
|
4203
|
+
if (genericTypes.includes(schema.identifier.name)) {
|
|
4204
|
+
name = `${schema.identifier.name}<T extends string = string>`;
|
|
3940
4205
|
} else if (schema.identifier.kind === "nested") {
|
|
3941
4206
|
name = tsResourceName(schema.identifier);
|
|
3942
4207
|
} else {
|
|
@@ -3963,23 +4228,10 @@ var TypeScript = class extends Writer {
|
|
|
3963
4228
|
const fields = Object.entries(schema.fields).sort((a, b) => a[0].localeCompare(b[0]));
|
|
3964
4229
|
for (const [fieldName, field] of fields) {
|
|
3965
4230
|
if (isChoiceDeclarationField(field)) continue;
|
|
4231
|
+
if (!field.type) continue;
|
|
3966
4232
|
this.debugComment(fieldName, ":", field);
|
|
3967
4233
|
const tsName = tsFieldName(fieldName);
|
|
3968
|
-
|
|
3969
|
-
if (field.enum) {
|
|
3970
|
-
tsType = tsEnumType(field.enum);
|
|
3971
|
-
} else if (schema.identifier.name === "Reference" && tsName === "reference") {
|
|
3972
|
-
tsType = "`${T}/${string}`";
|
|
3973
|
-
} else if (field.reference && field.reference.length > 0) {
|
|
3974
|
-
const references = field.reference.map((ref) => `"${ref.name}"`).join(" | ");
|
|
3975
|
-
tsType = `Reference<${references}>`;
|
|
3976
|
-
} else if (isPrimitiveIdentifier(field.type)) {
|
|
3977
|
-
tsType = resolvePrimitiveType(field.type.name);
|
|
3978
|
-
} else if (isNestedIdentifier(field.type)) {
|
|
3979
|
-
tsType = tsResourceName(field.type);
|
|
3980
|
-
} else {
|
|
3981
|
-
tsType = field.type.name;
|
|
3982
|
-
}
|
|
4234
|
+
const tsType = resolveFieldTsType(schema.identifier.name, tsName, field);
|
|
3983
4235
|
const optionalSymbol = field.required ? "" : "?";
|
|
3984
4236
|
const arraySymbol = field.array ? "[]" : "";
|
|
3985
4237
|
this.lineSM(`${tsName}${optionalSymbol}: ${tsType}${arraySymbol}`);
|
|
@@ -4021,10 +4273,16 @@ var TypeScript = class extends Writer {
|
|
|
4021
4273
|
if (!isNotChoiceDeclarationField(field)) {
|
|
4022
4274
|
throw new Error(`Choice declaration fields not supported for '${fieldName}'`);
|
|
4023
4275
|
}
|
|
4276
|
+
let tsType;
|
|
4024
4277
|
if (field.enum) {
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4278
|
+
if (field.type?.name === "Coding") {
|
|
4279
|
+
tsType = `Coding<${tsEnumType(field.enum)}>`;
|
|
4280
|
+
} else if (field.type?.name === "CodeableConcept") {
|
|
4281
|
+
tsType = `CodeableConcept<${tsEnumType(field.enum)}>`;
|
|
4282
|
+
} else {
|
|
4283
|
+
tsType = tsEnumType(field.enum);
|
|
4284
|
+
}
|
|
4285
|
+
} else if (field.reference && field.reference.length > 0) {
|
|
4028
4286
|
const specialization = tsIndex.findLastSpecialization(flatProfile);
|
|
4029
4287
|
if (!isSpecializationTypeSchema(specialization))
|
|
4030
4288
|
throw new Error(`Invalid specialization for ${flatProfile.identifier}`);
|
|
@@ -4041,20 +4299,20 @@ var TypeScript = class extends Writer {
|
|
|
4041
4299
|
}).join(" | ");
|
|
4042
4300
|
if (sRefs.length === 1 && sRefs[0] === "Resource" && references !== '"Resource"') {
|
|
4043
4301
|
const cleanRefs = references.replace(/\/\*[^*]*\*\//g, "").trim();
|
|
4044
|
-
|
|
4302
|
+
tsType = `Reference<"Resource" /* ${cleanRefs} */ >`;
|
|
4303
|
+
} else {
|
|
4304
|
+
tsType = `Reference<${references}>`;
|
|
4045
4305
|
}
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
if (isNestedIdentifier(field.type)) {
|
|
4049
|
-
|
|
4050
|
-
}
|
|
4051
|
-
if (isPrimitiveIdentifier(field.type)) {
|
|
4052
|
-
return resolvePrimitiveType(field.type.name);
|
|
4053
|
-
}
|
|
4054
|
-
if (field.type === void 0) {
|
|
4306
|
+
} else if (isPrimitiveIdentifier(field.type)) {
|
|
4307
|
+
tsType = resolvePrimitiveType(field.type.name);
|
|
4308
|
+
} else if (isNestedIdentifier(field.type)) {
|
|
4309
|
+
tsType = tsResourceName(field.type);
|
|
4310
|
+
} else if (field.type === void 0) {
|
|
4055
4311
|
throw new Error(`Undefined type for '${fieldName}' field at ${typeSchemaInfo(flatProfile)}`);
|
|
4312
|
+
} else {
|
|
4313
|
+
tsType = field.type.name;
|
|
4056
4314
|
}
|
|
4057
|
-
return
|
|
4315
|
+
return tsType;
|
|
4058
4316
|
}
|
|
4059
4317
|
generateProfileType(tsIndex, flatProfile) {
|
|
4060
4318
|
this.debugComment("flatProfile", flatProfile);
|
|
@@ -4358,104 +4616,95 @@ var TypeScript = class extends Writer {
|
|
|
4358
4616
|
this.lineSM(`import { ${imports.join(", ")} } from "../../profile-helpers"`);
|
|
4359
4617
|
}
|
|
4360
4618
|
}
|
|
4361
|
-
|
|
4362
|
-
const
|
|
4363
|
-
const getModulePath = (typeId) => {
|
|
4364
|
-
if (isNestedIdentifier(typeId)) {
|
|
4365
|
-
const url = typeId.url;
|
|
4366
|
-
const path = canonicalToName3(url, true);
|
|
4367
|
-
if (path) {
|
|
4368
|
-
return `../../${tsFhirPackageDir(typeId.package)}/${pascalCase(path)}`;
|
|
4369
|
-
}
|
|
4370
|
-
}
|
|
4371
|
-
return `../../${tsModulePath(typeId)}`;
|
|
4372
|
-
};
|
|
4373
|
-
const addType = (typeId) => {
|
|
4374
|
-
if (typeId.kind === "primitive-type") return;
|
|
4375
|
-
const tsName = tsResourceName(typeId);
|
|
4376
|
-
if (!usedTypes.has(tsName)) {
|
|
4377
|
-
usedTypes.set(tsName, {
|
|
4378
|
-
importPath: getModulePath(typeId),
|
|
4379
|
-
tsName
|
|
4380
|
-
});
|
|
4381
|
-
}
|
|
4382
|
-
};
|
|
4383
|
-
addType(flatProfile.base);
|
|
4384
|
-
const fields = flatProfile.fields ?? {};
|
|
4385
|
-
for (const [_fieldName, field] of Object.entries(fields)) {
|
|
4619
|
+
collectTypesFromSlices(flatProfile, addType) {
|
|
4620
|
+
for (const field of Object.values(flatProfile.fields ?? {})) {
|
|
4386
4621
|
if (!isNotChoiceDeclarationField(field) || !field.slicing?.slices || !field.type) continue;
|
|
4387
|
-
for (const
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4622
|
+
for (const slice of Object.values(field.slicing.slices)) {
|
|
4623
|
+
if (Object.keys(slice.match ?? {}).length > 0) {
|
|
4624
|
+
addType(field.type);
|
|
4625
|
+
}
|
|
4391
4626
|
}
|
|
4392
4627
|
}
|
|
4393
|
-
|
|
4628
|
+
}
|
|
4629
|
+
collectTypesFromExtensions(tsIndex, flatProfile, addType) {
|
|
4394
4630
|
let needsExtensionType = false;
|
|
4395
|
-
for (const ext of extensions) {
|
|
4631
|
+
for (const ext of flatProfile.extensions ?? []) {
|
|
4396
4632
|
if (ext.isComplex && ext.subExtensions) {
|
|
4397
4633
|
needsExtensionType = true;
|
|
4398
4634
|
for (const sub of ext.subExtensions) {
|
|
4399
|
-
if (sub.valueType)
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
addType(resolvedType.identifier);
|
|
4406
|
-
} else {
|
|
4407
|
-
addType(sub.valueType);
|
|
4408
|
-
}
|
|
4409
|
-
}
|
|
4635
|
+
if (!sub.valueType) continue;
|
|
4636
|
+
const resolvedType = tsIndex.resolveByUrl(
|
|
4637
|
+
flatProfile.identifier.package,
|
|
4638
|
+
sub.valueType.url
|
|
4639
|
+
);
|
|
4640
|
+
addType(resolvedType?.identifier ?? sub.valueType);
|
|
4410
4641
|
}
|
|
4411
4642
|
} else if (ext.valueTypes && ext.valueTypes.length === 1) {
|
|
4412
4643
|
needsExtensionType = true;
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4644
|
+
if (ext.valueTypes[0]) {
|
|
4645
|
+
const resolvedType = tsIndex.resolveByUrl(
|
|
4646
|
+
flatProfile.identifier.package,
|
|
4647
|
+
ext.valueTypes[0].url
|
|
4648
|
+
);
|
|
4649
|
+
addType(resolvedType?.identifier ?? ext.valueTypes[0]);
|
|
4416
4650
|
}
|
|
4417
4651
|
} else {
|
|
4418
4652
|
needsExtensionType = true;
|
|
4419
4653
|
}
|
|
4420
4654
|
}
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
if (extensionSchema) {
|
|
4425
|
-
addType(extensionSchema.identifier);
|
|
4426
|
-
}
|
|
4427
|
-
}
|
|
4655
|
+
return needsExtensionType;
|
|
4656
|
+
}
|
|
4657
|
+
collectTypesFromFieldOverrides(tsIndex, flatProfile, addType) {
|
|
4428
4658
|
const referenceUrl = "http://hl7.org/fhir/StructureDefinition/Reference";
|
|
4429
4659
|
const referenceSchema = tsIndex.resolveByUrl(flatProfile.identifier.package, referenceUrl);
|
|
4430
4660
|
const specialization = tsIndex.findLastSpecialization(flatProfile);
|
|
4431
|
-
if (isSpecializationTypeSchema(specialization))
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4661
|
+
if (!isSpecializationTypeSchema(specialization)) return;
|
|
4662
|
+
for (const [fieldName, pField] of Object.entries(flatProfile.fields ?? {})) {
|
|
4663
|
+
if (!isNotChoiceDeclarationField(pField)) continue;
|
|
4664
|
+
const sField = specialization.fields?.[fieldName];
|
|
4665
|
+
if (!sField || isChoiceDeclarationField(sField)) continue;
|
|
4666
|
+
if (pField.reference && sField.reference && pField.reference.length < sField.reference.length) {
|
|
4667
|
+
if (referenceSchema) addType(referenceSchema.identifier);
|
|
4668
|
+
} else if (pField.required && !sField.required && pField.type) {
|
|
4669
|
+
addType(pField.type);
|
|
4670
|
+
}
|
|
4671
|
+
}
|
|
4672
|
+
}
|
|
4673
|
+
generateProfileImports(tsIndex, flatProfile) {
|
|
4674
|
+
const usedTypes = /* @__PURE__ */ new Map();
|
|
4675
|
+
const getModulePath = (typeId) => {
|
|
4676
|
+
if (isNestedIdentifier(typeId)) {
|
|
4677
|
+
const path = canonicalToName3(typeId.url, true);
|
|
4678
|
+
if (path) return `../../${tsFhirPackageDir(typeId.package)}/${pascalCase(path)}`;
|
|
4679
|
+
}
|
|
4680
|
+
return `../../${tsModulePath(typeId)}`;
|
|
4681
|
+
};
|
|
4682
|
+
const addType = (typeId) => {
|
|
4683
|
+
if (typeId.kind === "primitive-type") return;
|
|
4684
|
+
const tsName = tsResourceName(typeId);
|
|
4685
|
+
if (!usedTypes.has(tsName)) {
|
|
4686
|
+
usedTypes.set(tsName, { importPath: getModulePath(typeId), tsName });
|
|
4445
4687
|
}
|
|
4688
|
+
};
|
|
4689
|
+
addType(flatProfile.base);
|
|
4690
|
+
this.collectTypesFromSlices(flatProfile, addType);
|
|
4691
|
+
const needsExtensionType = this.collectTypesFromExtensions(tsIndex, flatProfile, addType);
|
|
4692
|
+
this.collectTypesFromFieldOverrides(tsIndex, flatProfile, addType);
|
|
4693
|
+
if (needsExtensionType) {
|
|
4694
|
+
const extensionUrl = "http://hl7.org/fhir/StructureDefinition/Extension";
|
|
4695
|
+
const extensionSchema = tsIndex.resolveByUrl(flatProfile.identifier.package, extensionUrl);
|
|
4696
|
+
if (extensionSchema) addType(extensionSchema.identifier);
|
|
4446
4697
|
}
|
|
4447
4698
|
const sortedImports = Array.from(usedTypes.values()).sort((a, b) => a.tsName.localeCompare(b.tsName));
|
|
4448
4699
|
for (const { importPath, tsName } of sortedImports) {
|
|
4449
4700
|
this.tsImportType(importPath, tsName);
|
|
4450
4701
|
}
|
|
4451
|
-
if (sortedImports.length > 0)
|
|
4452
|
-
this.line();
|
|
4453
|
-
}
|
|
4702
|
+
if (sortedImports.length > 0) this.line();
|
|
4454
4703
|
}
|
|
4455
4704
|
generateProfileClass(tsIndex, flatProfile) {
|
|
4456
4705
|
const tsBaseResourceName = tsTypeFromIdentifier(flatProfile.base);
|
|
4457
4706
|
const tsProfileName = tsResourceName(flatProfile.identifier);
|
|
4458
|
-
const profileClassName = tsProfileClassName(flatProfile
|
|
4707
|
+
const profileClassName = tsProfileClassName(flatProfile);
|
|
4459
4708
|
const polymorphicBaseNames = /* @__PURE__ */ new Set([
|
|
4460
4709
|
"value",
|
|
4461
4710
|
"effective",
|
|
@@ -4887,7 +5136,7 @@ var TypeScript = class extends Writer {
|
|
|
4887
5136
|
generateResourceModule(tsIndex, schema) {
|
|
4888
5137
|
if (isProfileTypeSchema(schema)) {
|
|
4889
5138
|
this.cd("profiles", () => {
|
|
4890
|
-
this.cat(`${
|
|
5139
|
+
this.cat(`${tsProfileModuleFileName(tsIndex, schema)}`, () => {
|
|
4891
5140
|
this.generateDisclaimer();
|
|
4892
5141
|
const flatProfile = tsIndex.flatProfile(schema);
|
|
4893
5142
|
this.generateProfileImports(tsIndex, flatProfile);
|
|
@@ -4937,7 +5186,7 @@ var TypeScript = class extends Writer {
|
|
|
4937
5186
|
for (const schema of packageSchemas) {
|
|
4938
5187
|
this.generateResourceModule(tsIndex, schema);
|
|
4939
5188
|
}
|
|
4940
|
-
this.generateProfileIndexFile(packageSchemas.filter(isProfileTypeSchema));
|
|
5189
|
+
this.generateProfileIndexFile(tsIndex, packageSchemas.filter(isProfileTypeSchema));
|
|
4941
5190
|
this.generateFhirPackageIndexFile(packageSchemas);
|
|
4942
5191
|
});
|
|
4943
5192
|
}
|
|
@@ -4994,7 +5243,8 @@ var APIBuilder = class {
|
|
|
4994
5243
|
treeShake: void 0,
|
|
4995
5244
|
promoteLogical: void 0,
|
|
4996
5245
|
registry: void 0,
|
|
4997
|
-
logLevel: parseLogLevel("INFO")
|
|
5246
|
+
logLevel: parseLogLevel("INFO"),
|
|
5247
|
+
dropCanonicalManagerCache: false
|
|
4998
5248
|
};
|
|
4999
5249
|
const opts = {
|
|
5000
5250
|
...defaultOpts,
|
|
@@ -5016,7 +5266,8 @@ var APIBuilder = class {
|
|
|
5016
5266
|
this.manager = userOpts.manager ?? CanonicalManager({
|
|
5017
5267
|
packages: [],
|
|
5018
5268
|
workingDir: ".codegen-cache/canonical-manager-cache",
|
|
5019
|
-
registry: userOpts.registry
|
|
5269
|
+
registry: userOpts.registry,
|
|
5270
|
+
dropCache: userOpts.dropCanonicalManagerCache
|
|
5020
5271
|
});
|
|
5021
5272
|
this.logger = userOpts.logger ?? createLogger({ prefix: "API", level: opts.logLevel });
|
|
5022
5273
|
this.options = opts;
|
|
@@ -5030,14 +5281,6 @@ var APIBuilder = class {
|
|
|
5030
5281
|
this.managerInput.npmPackages.push(packageRef);
|
|
5031
5282
|
return this;
|
|
5032
5283
|
}
|
|
5033
|
-
/**
|
|
5034
|
-
* Set a custom FHIR package registry URL
|
|
5035
|
-
* @param url The registry URL (default: https://fs.get-ig.org/pkgs/)
|
|
5036
|
-
*/
|
|
5037
|
-
registry(url) {
|
|
5038
|
-
this.options.registry = url;
|
|
5039
|
-
return this;
|
|
5040
|
-
}
|
|
5041
5284
|
localStructureDefinitions(config) {
|
|
5042
5285
|
this.logger.info(`Registering local StructureDefinitions for ${config.package.name}@${config.package.version}`);
|
|
5043
5286
|
this.managerInput.localSDs.push({
|
|
@@ -5241,10 +5484,11 @@ var APIBuilder = class {
|
|
|
5241
5484
|
focusedPackages: packageMetas
|
|
5242
5485
|
});
|
|
5243
5486
|
}
|
|
5244
|
-
const typeSchemas = await generateTypeSchemas(register, this.logger);
|
|
5487
|
+
const { schemas: typeSchemas, collisions } = await generateTypeSchemas(register, this.logger);
|
|
5245
5488
|
const tsIndexOpts = {
|
|
5246
5489
|
register,
|
|
5247
|
-
logger: this.logger
|
|
5490
|
+
logger: this.logger,
|
|
5491
|
+
irReport: Object.keys(collisions).length > 0 ? { collisions } : {}
|
|
5248
5492
|
};
|
|
5249
5493
|
let tsIndex = mkTypeSchemaIndex(typeSchemas, tsIndexOpts);
|
|
5250
5494
|
if (this.options.treeShake) tsIndex = treeShake(tsIndex, this.options.treeShake);
|
|
@@ -5298,6 +5542,6 @@ var APIBuilder = class {
|
|
|
5298
5542
|
}
|
|
5299
5543
|
};
|
|
5300
5544
|
|
|
5301
|
-
export { APIBuilder, LogLevel, prettyReport };
|
|
5545
|
+
export { APIBuilder, LogLevel, prettyReport, registerFromPackageMetas };
|
|
5302
5546
|
//# sourceMappingURL=index.js.map
|
|
5303
5547
|
//# sourceMappingURL=index.js.map
|