@atomic-ehr/codegen 0.0.4-canary.20251217144707.ef99bf1 → 0.0.4-canary.20251218111944.d18e50d
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/README.md +177 -284
- package/dist/cli/index.js +10 -10
- package/dist/index.d.ts +6 -5
- package/dist/index.js +931 -855
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { readFile } from 'fs/promises';
|
|
|
6
6
|
import * as Path5 from 'path';
|
|
7
7
|
import Path5__default, { resolve } from 'path';
|
|
8
8
|
import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
|
|
9
|
-
import
|
|
9
|
+
import assert3 from 'assert';
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
11
11
|
import * as YAML from 'yaml';
|
|
12
12
|
import * as fhirschema from '@atomic-ehr/fhirschema';
|
|
@@ -410,6 +410,10 @@ var isChoiceDeclarationField = (field) => {
|
|
|
410
410
|
if (!field) return false;
|
|
411
411
|
return field.choices !== void 0;
|
|
412
412
|
};
|
|
413
|
+
var isChoiceInstanceField = (field) => {
|
|
414
|
+
if (!field) return false;
|
|
415
|
+
return field.choiceOf !== void 0;
|
|
416
|
+
};
|
|
413
417
|
var enrichValueSet = (vs, packageMeta) => {
|
|
414
418
|
if (!vs.url) throw new Error("ValueSet must have a URL");
|
|
415
419
|
if (!vs.name) throw new Error("ValueSet must have a name");
|
|
@@ -739,676 +743,110 @@ var CSharp = class extends Writer {
|
|
|
739
743
|
fs__default.copyFileSync(sourceFile, destFile);
|
|
740
744
|
}
|
|
741
745
|
};
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
function getVersionFromUrl(url) {
|
|
749
|
-
const version = url.split("|")[1];
|
|
750
|
-
return version;
|
|
751
|
-
}
|
|
752
|
-
function determineKind(fhirSchema) {
|
|
753
|
-
if (fhirSchema.derivation === "constraint") return "profile";
|
|
754
|
-
if (fhirSchema.kind === "primitive-type") return "primitive-type";
|
|
755
|
-
if (fhirSchema.kind === "complex-type") return "complex-type";
|
|
756
|
-
if (fhirSchema.kind === "resource") return "resource";
|
|
757
|
-
if (fhirSchema.kind === "logical") return "logical";
|
|
758
|
-
return "resource";
|
|
759
|
-
}
|
|
760
|
-
function mkIdentifier(fhirSchema) {
|
|
761
|
-
return {
|
|
762
|
-
kind: determineKind(fhirSchema),
|
|
763
|
-
package: fhirSchema.package_meta.name,
|
|
764
|
-
version: fhirSchema.package_meta.version,
|
|
765
|
-
name: fhirSchema.name,
|
|
766
|
-
url: fhirSchema.url
|
|
767
|
-
};
|
|
768
|
-
}
|
|
769
|
-
var getValueSetName = (url) => {
|
|
770
|
-
const urlParts = url.split("/");
|
|
771
|
-
const lastSegment = urlParts[urlParts.length - 1];
|
|
772
|
-
if (lastSegment && lastSegment.length > 0) {
|
|
773
|
-
return lastSegment.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
746
|
+
var groupByPackages = (typeSchemas) => {
|
|
747
|
+
const grouped = {};
|
|
748
|
+
for (const ts of typeSchemas) {
|
|
749
|
+
const pkgName = ts.identifier.package;
|
|
750
|
+
if (!grouped[pkgName]) grouped[pkgName] = [];
|
|
751
|
+
grouped[pkgName].push(ts);
|
|
774
752
|
}
|
|
775
|
-
|
|
776
|
-
};
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
const valueSetNameFallback = getValueSetName(valueSetUrl);
|
|
780
|
-
const valuesSetFallback = {
|
|
781
|
-
package_meta: {
|
|
782
|
-
name: "missing_valuesets",
|
|
783
|
-
version: getVersionFromUrl(valueSetUrl) || "0.0.0"
|
|
784
|
-
},
|
|
785
|
-
id: fullValueSetUrl};
|
|
786
|
-
const valueSet = register.resolveVs(pkg, valueSetUrl) || valuesSetFallback;
|
|
787
|
-
const valueSetName = valueSet?.id && !/^[a-zA-Z0-9_-]{20,}$/.test(valueSet.id) ? valueSet.id : valueSetNameFallback;
|
|
788
|
-
return {
|
|
789
|
-
kind: "value-set",
|
|
790
|
-
package: valueSet.package_meta.name,
|
|
791
|
-
version: valueSet.package_meta.version,
|
|
792
|
-
name: valueSetName,
|
|
793
|
-
url: valueSetUrl
|
|
794
|
-
};
|
|
795
|
-
}
|
|
796
|
-
function mkBindingIdentifier(fhirSchema, path, bindingName) {
|
|
797
|
-
const pathStr = path.join(".");
|
|
798
|
-
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`];
|
|
799
|
-
return {
|
|
800
|
-
kind: "binding",
|
|
801
|
-
package: pkg.name,
|
|
802
|
-
version: pkg.version,
|
|
803
|
-
name,
|
|
804
|
-
url
|
|
805
|
-
};
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
// src/typeschema/core/nested-types.ts
|
|
809
|
-
function mkNestedIdentifier(register, fhirSchema, path, logger) {
|
|
810
|
-
const nestedTypeOrigins = {};
|
|
811
|
-
if (fhirSchema.derivation === "constraint") {
|
|
812
|
-
const specializations = register.resolveFsSpecializations(fhirSchema.package_meta, fhirSchema.url);
|
|
813
|
-
const nestedTypeGenealogy = specializations.map((fs5) => mkNestedTypes(register, fs5, logger)).filter((e) => e !== void 0).flat();
|
|
814
|
-
for (const nt of nestedTypeGenealogy.reverse()) {
|
|
815
|
-
nestedTypeOrigins[nt.identifier.name] = nt.identifier.url;
|
|
753
|
+
for (const [packageName, typeSchemas2] of Object.entries(grouped)) {
|
|
754
|
+
const dict = {};
|
|
755
|
+
for (const ts of typeSchemas2) {
|
|
756
|
+
dict[JSON.stringify(ts.identifier)] = ts;
|
|
816
757
|
}
|
|
758
|
+
const tmp = Object.values(dict);
|
|
759
|
+
tmp.sort((a, b) => a.identifier.name.localeCompare(b.identifier.name));
|
|
760
|
+
grouped[packageName] = tmp;
|
|
817
761
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
const path = [...parentPath, key];
|
|
832
|
-
if (isNestedElement(element)) {
|
|
833
|
-
nested.push([path, element]);
|
|
762
|
+
return grouped;
|
|
763
|
+
};
|
|
764
|
+
var buildDependencyGraph = (schemas) => {
|
|
765
|
+
const nameToMap = {};
|
|
766
|
+
for (const schema of schemas) {
|
|
767
|
+
nameToMap[schema.identifier.name] = schema;
|
|
768
|
+
}
|
|
769
|
+
const graph = {};
|
|
770
|
+
for (const schema of schemas) {
|
|
771
|
+
const name = schema.identifier.name;
|
|
772
|
+
const base = schema.base?.name;
|
|
773
|
+
if (!graph[name]) {
|
|
774
|
+
graph[name] = [];
|
|
834
775
|
}
|
|
835
|
-
if (
|
|
836
|
-
|
|
776
|
+
if (base && nameToMap[base]) {
|
|
777
|
+
graph[name].push(base);
|
|
837
778
|
}
|
|
838
779
|
}
|
|
839
|
-
return
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
const
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
if (
|
|
847
|
-
|
|
848
|
-
}
|
|
849
|
-
|
|
780
|
+
return graph;
|
|
781
|
+
};
|
|
782
|
+
var topologicalSort = (graph) => {
|
|
783
|
+
const sorted = [];
|
|
784
|
+
const visited = {};
|
|
785
|
+
const temp = {};
|
|
786
|
+
const visit = (node) => {
|
|
787
|
+
if (temp[node]) {
|
|
788
|
+
throw new Error(`Graph has cycles ${node}`);
|
|
789
|
+
}
|
|
790
|
+
if (!visited[node]) {
|
|
791
|
+
temp[node] = true;
|
|
792
|
+
for (const neighbor of graph[node] ?? []) {
|
|
793
|
+
visit(neighbor);
|
|
794
|
+
}
|
|
795
|
+
temp[node] = false;
|
|
796
|
+
visited[node] = true;
|
|
797
|
+
sorted.push(node);
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
for (const node in graph) {
|
|
801
|
+
if (!visited[node]) {
|
|
802
|
+
visit(node);
|
|
850
803
|
}
|
|
851
804
|
}
|
|
852
|
-
return
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
const
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
if (
|
|
864
|
-
|
|
865
|
-
} else {
|
|
866
|
-
baseName = element.type;
|
|
805
|
+
return sorted;
|
|
806
|
+
};
|
|
807
|
+
var sortAsDeclarationSequence = (schemas) => {
|
|
808
|
+
const graph = buildDependencyGraph(schemas);
|
|
809
|
+
const sorted = topologicalSort(graph);
|
|
810
|
+
return sorted.map((name) => schemas.find((schema) => schema.identifier.name === name)).filter(Boolean);
|
|
811
|
+
};
|
|
812
|
+
var resourceRelatives = (schemas) => {
|
|
813
|
+
const regularSchemas = schemas.filter((e) => isResourceTypeSchema(e) || isLogicalTypeSchema(e));
|
|
814
|
+
const directPairs = [];
|
|
815
|
+
for (const schema of regularSchemas) {
|
|
816
|
+
if (schema.base) {
|
|
817
|
+
directPairs.push({ parent: schema.base, child: schema.identifier });
|
|
867
818
|
}
|
|
868
|
-
const baseUrl = register.ensureSpecializationCanonicalUrl(baseName);
|
|
869
|
-
const baseFs = register.resolveFs(fhirSchema.package_meta, baseUrl);
|
|
870
|
-
if (!baseFs) throw new Error(`Could not resolve base type ${baseName}`);
|
|
871
|
-
const base = {
|
|
872
|
-
kind: "complex-type",
|
|
873
|
-
package: baseFs.package_meta.name,
|
|
874
|
-
version: baseFs.package_meta.version,
|
|
875
|
-
name: baseName,
|
|
876
|
-
url: baseUrl
|
|
877
|
-
};
|
|
878
|
-
const fields = transformNestedElements(register, fhirSchema, path, element.elements ?? {}, logger);
|
|
879
|
-
const nestedType = {
|
|
880
|
-
identifier,
|
|
881
|
-
base,
|
|
882
|
-
fields
|
|
883
|
-
};
|
|
884
|
-
nestedTypes.push(nestedType);
|
|
885
819
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
if (nested.base) {
|
|
893
|
-
deps.push(nested.base);
|
|
820
|
+
const allPairs = [...directPairs];
|
|
821
|
+
const findTransitiveRelatives = (parentRef) => {
|
|
822
|
+
const directChildren = directPairs.filter((pair) => pair.parent.name === parentRef.name).map((pair) => pair.child);
|
|
823
|
+
const transitiveChildren = [];
|
|
824
|
+
for (const child of directChildren) {
|
|
825
|
+
transitiveChildren.push(...findTransitiveRelatives(child));
|
|
894
826
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
827
|
+
return [...directChildren, ...transitiveChildren];
|
|
828
|
+
};
|
|
829
|
+
for (const pair of directPairs) {
|
|
830
|
+
const transitiveChildren = findTransitiveRelatives(pair.child);
|
|
831
|
+
for (const transitiveChild of transitiveChildren) {
|
|
832
|
+
if (!directPairs.some((dp) => dp.parent.name === pair.parent.name && dp.child.name === transitiveChild.name)) {
|
|
833
|
+
allPairs.push({ parent: pair.parent, child: transitiveChild });
|
|
901
834
|
}
|
|
902
835
|
}
|
|
903
836
|
}
|
|
904
|
-
return
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
elem = elem?.elements?.[k];
|
|
918
|
-
}
|
|
919
|
-
return elem?.required || [];
|
|
920
|
-
});
|
|
921
|
-
return new Set(requires).has(fieldName);
|
|
922
|
-
}
|
|
923
|
-
function isExcluded(register, fhirSchema, path) {
|
|
924
|
-
const fieldName = path[path.length - 1];
|
|
925
|
-
if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
|
|
926
|
-
const parentPath = path.slice(0, -1);
|
|
927
|
-
const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs5) => {
|
|
928
|
-
if (parentPath.length === 0) return fs5.excluded || [];
|
|
929
|
-
if (!fs5.elements) return [];
|
|
930
|
-
let elem = fs5;
|
|
931
|
-
for (const k of parentPath) {
|
|
932
|
-
elem = elem?.elements?.[k];
|
|
933
|
-
}
|
|
934
|
-
return elem?.excluded || [];
|
|
935
|
-
});
|
|
936
|
-
return new Set(requires).has(fieldName);
|
|
937
|
-
}
|
|
938
|
-
var buildReferences = (register, fhirSchema, element) => {
|
|
939
|
-
if (!element.refers) return void 0;
|
|
940
|
-
return element.refers.map((ref) => {
|
|
941
|
-
const curl = register.ensureSpecializationCanonicalUrl(ref);
|
|
942
|
-
const fs5 = register.resolveFs(fhirSchema.package_meta, curl);
|
|
943
|
-
if (!fs5) throw new Error(`Failed to resolve fs for ${curl}`);
|
|
944
|
-
return mkIdentifier(fs5);
|
|
945
|
-
});
|
|
946
|
-
};
|
|
947
|
-
function buildFieldType(register, fhirSchema, path, element, logger) {
|
|
948
|
-
if (element.elementReference) {
|
|
949
|
-
const refPath = element.elementReference.slice(1).filter((_, i) => i % 2 === 1);
|
|
950
|
-
return mkNestedIdentifier(register, fhirSchema, refPath, logger);
|
|
951
|
-
} else if (element.type) {
|
|
952
|
-
const url = register.ensureSpecializationCanonicalUrl(element.type);
|
|
953
|
-
const fieldFs = register.resolveFs(fhirSchema.package_meta, url);
|
|
954
|
-
if (!fieldFs)
|
|
955
|
-
throw new Error(
|
|
956
|
-
`Could not resolve field type: '${element.type}' (from '${fhirSchema.url}' in '${packageMetaToFhir(fhirSchema.package_meta)}')`
|
|
957
|
-
);
|
|
958
|
-
return mkIdentifier(fieldFs);
|
|
959
|
-
} else if (element.choices) {
|
|
960
|
-
return void 0;
|
|
961
|
-
} else if (fhirSchema.derivation === "constraint") {
|
|
962
|
-
return void 0;
|
|
963
|
-
} else {
|
|
964
|
-
logger?.error(
|
|
965
|
-
`Can't recognize element type '${fhirSchema.url}' (${fhirSchema.derivation}) at '${path.join(".")}': ${JSON.stringify(element, void 0, 2)}`
|
|
966
|
-
);
|
|
967
|
-
throw new Error(`Unrecognized element type`);
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
var mkField = (register, fhirSchema, path, element, logger) => {
|
|
971
|
-
let binding;
|
|
972
|
-
let enumValues;
|
|
973
|
-
if (element.binding) {
|
|
974
|
-
binding = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
|
|
975
|
-
if (element.binding.strength === "required" && element.type === "code") {
|
|
976
|
-
enumValues = buildEnum(register, fhirSchema, element, logger);
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
const fieldType = buildFieldType(register, fhirSchema, path, element, logger);
|
|
980
|
-
if (!fieldType)
|
|
981
|
-
logger?.warn(`Field type not found for '${fhirSchema.url}#${path.join(".")}' (${fhirSchema.derivation})`);
|
|
982
|
-
return {
|
|
983
|
-
type: fieldType,
|
|
984
|
-
required: isRequired(register, fhirSchema, path),
|
|
985
|
-
excluded: isExcluded(register, fhirSchema, path),
|
|
986
|
-
reference: buildReferences(register, fhirSchema, element),
|
|
987
|
-
array: element.array || false,
|
|
988
|
-
min: element.min,
|
|
989
|
-
max: element.max,
|
|
990
|
-
choices: element.choices,
|
|
991
|
-
choiceOf: element.choiceOf,
|
|
992
|
-
binding,
|
|
993
|
-
enum: enumValues
|
|
994
|
-
};
|
|
995
|
-
};
|
|
996
|
-
function isNestedElement(element) {
|
|
997
|
-
const isBackbone = element.type === "BackboneElement";
|
|
998
|
-
const isElement = element.type === "Element" && element.elements !== void 0 && Object.keys(element.elements).length > 0;
|
|
999
|
-
const elementsWithoutType = element.type === void 0 && element.choiceOf === void 0 && element.elements !== void 0 && Object.keys(element.elements).length > 0;
|
|
1000
|
-
return isBackbone || isElement || elementsWithoutType;
|
|
1001
|
-
}
|
|
1002
|
-
function mkNestedField(register, fhirSchema, path, element, logger) {
|
|
1003
|
-
const nestedIdentifier = mkNestedIdentifier(register, fhirSchema, path, logger);
|
|
1004
|
-
return {
|
|
1005
|
-
type: nestedIdentifier,
|
|
1006
|
-
array: element.array || false,
|
|
1007
|
-
required: isRequired(register, fhirSchema, path),
|
|
1008
|
-
excluded: isExcluded(register, fhirSchema, path)
|
|
1009
|
-
};
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
// src/typeschema/core/binding.ts
|
|
1013
|
-
function extractValueSetConceptsByUrl(register, pkg, valueSetUrl, logger) {
|
|
1014
|
-
const cleanUrl = dropVersionFromUrl(valueSetUrl) || valueSetUrl;
|
|
1015
|
-
const valueSet = register.resolveVs(pkg, cleanUrl);
|
|
1016
|
-
if (!valueSet) return void 0;
|
|
1017
|
-
return extractValueSetConcepts(register, valueSet);
|
|
1018
|
-
}
|
|
1019
|
-
function extractValueSetConcepts(register, valueSet, _logger) {
|
|
1020
|
-
if (valueSet.expansion?.contains) {
|
|
1021
|
-
return valueSet.expansion.contains.filter((item) => item.code !== void 0).map((item) => {
|
|
1022
|
-
assert2(item.code);
|
|
1023
|
-
return {
|
|
1024
|
-
code: item.code,
|
|
1025
|
-
display: item.display,
|
|
1026
|
-
system: item.system
|
|
1027
|
-
};
|
|
1028
|
-
});
|
|
1029
|
-
}
|
|
1030
|
-
const concepts = [];
|
|
1031
|
-
if (valueSet.compose?.include) {
|
|
1032
|
-
for (const include of valueSet.compose.include) {
|
|
1033
|
-
if (include.concept) {
|
|
1034
|
-
for (const concept of include.concept) {
|
|
1035
|
-
concepts.push({
|
|
1036
|
-
system: include.system,
|
|
1037
|
-
code: concept.code,
|
|
1038
|
-
display: concept.display
|
|
1039
|
-
});
|
|
1040
|
-
}
|
|
1041
|
-
} else if (include.system && !include.filter) {
|
|
1042
|
-
try {
|
|
1043
|
-
const codeSystem = register.resolveAny(include.system);
|
|
1044
|
-
if (codeSystem?.concept) {
|
|
1045
|
-
const extractConcepts = (conceptList, system) => {
|
|
1046
|
-
for (const concept of conceptList) {
|
|
1047
|
-
concepts.push({
|
|
1048
|
-
system,
|
|
1049
|
-
code: concept.code,
|
|
1050
|
-
display: concept.display
|
|
1051
|
-
});
|
|
1052
|
-
if (concept.concept) {
|
|
1053
|
-
extractConcepts(concept.concept, system);
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
};
|
|
1057
|
-
extractConcepts(codeSystem.concept, include.system);
|
|
1058
|
-
}
|
|
1059
|
-
} catch {
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
return concepts.length > 0 ? concepts : void 0;
|
|
1065
|
-
}
|
|
1066
|
-
var MAX_ENUM_LENGTH = 100;
|
|
1067
|
-
function buildEnum(register, fhirSchema, element, logger) {
|
|
1068
|
-
if (!element.binding) return void 0;
|
|
1069
|
-
const strength = element.binding.strength;
|
|
1070
|
-
const valueSetUrl = element.binding.valueSet;
|
|
1071
|
-
if (!valueSetUrl) return void 0;
|
|
1072
|
-
const shouldGenerateEnum = strength === "required" || strength === "extensible" && (element.type === "code" || element.type === "Coding") || strength === "preferred" && (element.type === "code" || element.type === "Coding");
|
|
1073
|
-
if (!shouldGenerateEnum) return void 0;
|
|
1074
|
-
const concepts = extractValueSetConceptsByUrl(register, fhirSchema.package_meta, valueSetUrl);
|
|
1075
|
-
if (!concepts || concepts.length === 0) return void 0;
|
|
1076
|
-
const codes = concepts.map((c) => c.code).filter((code) => code && typeof code === "string" && code.trim().length > 0);
|
|
1077
|
-
if (codes.length > MAX_ENUM_LENGTH) {
|
|
1078
|
-
logger?.dry_warn(
|
|
1079
|
-
`Value set ${valueSetUrl} has ${codes.length} which is more than ${MAX_ENUM_LENGTH} codes, which may cause issues with code generation.`
|
|
1080
|
-
);
|
|
1081
|
-
return void 0;
|
|
1082
|
-
}
|
|
1083
|
-
return codes.length > 0 ? codes : void 0;
|
|
1084
|
-
}
|
|
1085
|
-
function generateBindingSchema(register, fhirSchema, path, element, logger) {
|
|
1086
|
-
if (!element.binding?.valueSet) return void 0;
|
|
1087
|
-
const identifier = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
|
|
1088
|
-
const fieldType = buildFieldType(register, fhirSchema, path, element, logger);
|
|
1089
|
-
const valueSetIdentifier = mkValueSetIdentifierByUrl(
|
|
1090
|
-
register,
|
|
1091
|
-
fhirSchema.package_meta,
|
|
1092
|
-
element.binding.valueSet
|
|
1093
|
-
);
|
|
1094
|
-
const dependencies = [];
|
|
1095
|
-
if (fieldType) {
|
|
1096
|
-
dependencies.push(fieldType);
|
|
1097
|
-
}
|
|
1098
|
-
dependencies.push(valueSetIdentifier);
|
|
1099
|
-
const enumValues = buildEnum(register, fhirSchema, element, logger);
|
|
1100
|
-
return {
|
|
1101
|
-
identifier,
|
|
1102
|
-
type: fieldType,
|
|
1103
|
-
valueset: valueSetIdentifier,
|
|
1104
|
-
strength: element.binding.strength,
|
|
1105
|
-
enum: enumValues,
|
|
1106
|
-
dependencies
|
|
1107
|
-
};
|
|
1108
|
-
}
|
|
1109
|
-
function collectBindingSchemas(register, fhirSchema, logger) {
|
|
1110
|
-
const processedPaths = /* @__PURE__ */ new Set();
|
|
1111
|
-
if (!fhirSchema.elements) return [];
|
|
1112
|
-
const bindings = [];
|
|
1113
|
-
function collectBindings(elements, parentPath) {
|
|
1114
|
-
for (const [key, element] of Object.entries(elements)) {
|
|
1115
|
-
const path = [...parentPath, key];
|
|
1116
|
-
const pathKey = path.join(".");
|
|
1117
|
-
if (processedPaths.has(pathKey)) continue;
|
|
1118
|
-
processedPaths.add(pathKey);
|
|
1119
|
-
if (element.binding) {
|
|
1120
|
-
const binding = generateBindingSchema(register, fhirSchema, path, element, logger);
|
|
1121
|
-
if (binding) {
|
|
1122
|
-
bindings.push(binding);
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
if (element.elements) {
|
|
1126
|
-
collectBindings(element.elements, path);
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
collectBindings(fhirSchema.elements, []);
|
|
1131
|
-
bindings.sort((a, b) => a.identifier.name.localeCompare(b.identifier.name));
|
|
1132
|
-
const uniqueBindings = [];
|
|
1133
|
-
const seenUrls = /* @__PURE__ */ new Set();
|
|
1134
|
-
for (const binding of bindings) {
|
|
1135
|
-
if (!seenUrls.has(binding.identifier.url)) {
|
|
1136
|
-
seenUrls.add(binding.identifier.url);
|
|
1137
|
-
uniqueBindings.push(binding);
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
return uniqueBindings;
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
// src/typeschema/core/transformer.ts
|
|
1144
|
-
function mkFields(register, fhirSchema, parentPath, elements, logger) {
|
|
1145
|
-
if (!elements) return void 0;
|
|
1146
|
-
const fields = {};
|
|
1147
|
-
for (const key of register.getAllElementKeys(elements)) {
|
|
1148
|
-
const path = [...parentPath, key];
|
|
1149
|
-
const elemSnapshot = register.resolveElementSnapshot(fhirSchema, path);
|
|
1150
|
-
if (isNestedElement(elemSnapshot)) {
|
|
1151
|
-
fields[key] = mkNestedField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1152
|
-
} else {
|
|
1153
|
-
fields[key] = mkField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
return fields;
|
|
1157
|
-
}
|
|
1158
|
-
function extractFieldDependencies(fields) {
|
|
1159
|
-
const deps = [];
|
|
1160
|
-
for (const field of Object.values(fields)) {
|
|
1161
|
-
if ("type" in field && field.type) {
|
|
1162
|
-
deps.push(field.type);
|
|
1163
|
-
}
|
|
1164
|
-
if ("binding" in field && field.binding) {
|
|
1165
|
-
deps.push(field.binding);
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
return deps;
|
|
1169
|
-
}
|
|
1170
|
-
function isExtensionSchema(fhirSchema, _identifier) {
|
|
1171
|
-
if (fhirSchema.base === "Extension" || fhirSchema.base === "http://hl7.org/fhir/StructureDefinition/Extension") {
|
|
1172
|
-
return true;
|
|
1173
|
-
}
|
|
1174
|
-
if (fhirSchema.url?.includes("/extension/") || fhirSchema.url?.includes("-extension")) {
|
|
1175
|
-
return true;
|
|
1176
|
-
}
|
|
1177
|
-
if (fhirSchema.name?.toLowerCase().includes("extension")) {
|
|
1178
|
-
return true;
|
|
1179
|
-
}
|
|
1180
|
-
if (fhirSchema.type === "Extension") {
|
|
1181
|
-
return true;
|
|
1182
|
-
}
|
|
1183
|
-
return false;
|
|
1184
|
-
}
|
|
1185
|
-
async function transformValueSet(register, valueSet, logger) {
|
|
1186
|
-
if (!valueSet.url) throw new Error("ValueSet URL is required");
|
|
1187
|
-
const identifier = mkValueSetIdentifierByUrl(register, valueSet.package_meta, valueSet.url);
|
|
1188
|
-
const concept = extractValueSetConceptsByUrl(register, valueSet.package_meta, valueSet.url);
|
|
1189
|
-
return {
|
|
1190
|
-
identifier,
|
|
1191
|
-
description: valueSet.description,
|
|
1192
|
-
concept,
|
|
1193
|
-
compose: !concept ? valueSet.compose : void 0
|
|
1194
|
-
};
|
|
1195
|
-
}
|
|
1196
|
-
function extractDependencies(identifier, base, fields, nestedTypes) {
|
|
1197
|
-
const deps = [];
|
|
1198
|
-
if (base) deps.push(base);
|
|
1199
|
-
if (fields) deps.push(...extractFieldDependencies(fields));
|
|
1200
|
-
if (nestedTypes) deps.push(...extractNestedDependencies(nestedTypes));
|
|
1201
|
-
const uniqDeps = {};
|
|
1202
|
-
for (const dep of deps) {
|
|
1203
|
-
if (dep.url === identifier.url) continue;
|
|
1204
|
-
uniqDeps[dep.url] = dep;
|
|
1205
|
-
}
|
|
1206
|
-
const localNestedTypeUrls = new Set(nestedTypes?.map((nt) => nt.identifier.url));
|
|
1207
|
-
const result = Object.values(uniqDeps).filter((e) => {
|
|
1208
|
-
if (isProfileIdentifier(identifier)) return true;
|
|
1209
|
-
if (!isNestedIdentifier(e)) return true;
|
|
1210
|
-
return !localNestedTypeUrls.has(e.url);
|
|
1211
|
-
}).sort((a, b) => a.url.localeCompare(b.url));
|
|
1212
|
-
return result.length > 0 ? result : void 0;
|
|
1213
|
-
}
|
|
1214
|
-
function transformFhirSchemaResource(register, fhirSchema, logger) {
|
|
1215
|
-
const identifier = mkIdentifier(fhirSchema);
|
|
1216
|
-
let base;
|
|
1217
|
-
if (fhirSchema.base && fhirSchema.type !== "Element") {
|
|
1218
|
-
const baseFs = register.resolveFs(
|
|
1219
|
-
fhirSchema.package_meta,
|
|
1220
|
-
register.ensureSpecializationCanonicalUrl(fhirSchema.base)
|
|
1221
|
-
);
|
|
1222
|
-
if (!baseFs) {
|
|
1223
|
-
throw new Error(
|
|
1224
|
-
`Base resource not found '${fhirSchema.base}' for <${fhirSchema.url}> from ${packageMetaToFhir(fhirSchema.package_meta)}`
|
|
1225
|
-
);
|
|
1226
|
-
}
|
|
1227
|
-
base = mkIdentifier(baseFs);
|
|
1228
|
-
}
|
|
1229
|
-
const fields = mkFields(register, fhirSchema, [], fhirSchema.elements, logger);
|
|
1230
|
-
const nested = mkNestedTypes(register, fhirSchema, logger);
|
|
1231
|
-
const dependencies = extractDependencies(identifier, base, fields, nested);
|
|
1232
|
-
const typeSchema = {
|
|
1233
|
-
identifier,
|
|
1234
|
-
base,
|
|
1235
|
-
fields,
|
|
1236
|
-
nested,
|
|
1237
|
-
description: fhirSchema.description,
|
|
1238
|
-
dependencies
|
|
1239
|
-
};
|
|
1240
|
-
const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger);
|
|
1241
|
-
return [typeSchema, ...bindingSchemas];
|
|
1242
|
-
}
|
|
1243
|
-
async function transformFhirSchema(register, fhirSchema, logger) {
|
|
1244
|
-
const schemas = transformFhirSchemaResource(register, fhirSchema, logger);
|
|
1245
|
-
if (isExtensionSchema(fhirSchema, mkIdentifier(fhirSchema))) {
|
|
1246
|
-
const schema = schemas[0];
|
|
1247
|
-
if (!schema) throw new Error(`Expected schema to be defined`);
|
|
1248
|
-
schema.metadata = {
|
|
1249
|
-
isExtension: true
|
|
1250
|
-
// Mark as extension for file organization
|
|
1251
|
-
};
|
|
1252
|
-
}
|
|
1253
|
-
return schemas;
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
// src/typeschema/utils.ts
|
|
1257
|
-
var groupByPackages = (typeSchemas) => {
|
|
1258
|
-
const grouped = {};
|
|
1259
|
-
for (const ts of typeSchemas) {
|
|
1260
|
-
const pkgName = ts.identifier.package;
|
|
1261
|
-
if (!grouped[pkgName]) grouped[pkgName] = [];
|
|
1262
|
-
grouped[pkgName].push(ts);
|
|
1263
|
-
}
|
|
1264
|
-
for (const [packageName, typeSchemas2] of Object.entries(grouped)) {
|
|
1265
|
-
const dict = {};
|
|
1266
|
-
for (const ts of typeSchemas2) {
|
|
1267
|
-
dict[JSON.stringify(ts.identifier)] = ts;
|
|
1268
|
-
}
|
|
1269
|
-
const tmp = Object.values(dict);
|
|
1270
|
-
tmp.sort((a, b) => a.identifier.name.localeCompare(b.identifier.name));
|
|
1271
|
-
grouped[packageName] = tmp;
|
|
1272
|
-
}
|
|
1273
|
-
return grouped;
|
|
1274
|
-
};
|
|
1275
|
-
var treeShakeTypeSchema = (schema, rule, _logger) => {
|
|
1276
|
-
schema = structuredClone(schema);
|
|
1277
|
-
if (isPrimitiveTypeSchema(schema) || isValueSetTypeSchema(schema) || isBindingSchema(schema)) return schema;
|
|
1278
|
-
for (const fieldName of rule.ignoreFields ?? []) {
|
|
1279
|
-
if (schema.fields && !schema.fields[fieldName]) throw new Error(`Field ${fieldName} not found`);
|
|
1280
|
-
if (schema.fields) {
|
|
1281
|
-
delete schema.fields[fieldName];
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
schema.dependencies = extractDependencies(schema.identifier, schema.base, schema.fields, schema.nested);
|
|
1285
|
-
return schema;
|
|
1286
|
-
};
|
|
1287
|
-
var treeShake = (tsIndex, treeShake2, { resolutionTree, logger }) => {
|
|
1288
|
-
const focusedSchemas = [];
|
|
1289
|
-
for (const [pkgId, requires] of Object.entries(treeShake2)) {
|
|
1290
|
-
for (const [url, rule] of Object.entries(requires)) {
|
|
1291
|
-
const schema = tsIndex.resolveByUrl(pkgId, url);
|
|
1292
|
-
if (!schema) throw new Error(`Schema not found for ${pkgId} ${url}`);
|
|
1293
|
-
const shaked2 = treeShakeTypeSchema(schema, rule);
|
|
1294
|
-
focusedSchemas.push(shaked2);
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
const collectDeps = (schemas, acc) => {
|
|
1298
|
-
if (schemas.length === 0) return Object.values(acc);
|
|
1299
|
-
for (const schema of schemas) {
|
|
1300
|
-
acc[JSON.stringify(schema.identifier)] = schema;
|
|
1301
|
-
}
|
|
1302
|
-
const newSchemas = [];
|
|
1303
|
-
for (const schema of schemas) {
|
|
1304
|
-
if (isSpecializationTypeSchema(schema)) {
|
|
1305
|
-
if (!schema.dependencies) continue;
|
|
1306
|
-
schema.dependencies.forEach((dep) => {
|
|
1307
|
-
const depSchema = tsIndex.resolve(dep);
|
|
1308
|
-
if (!depSchema) throw new Error(`Schema not found for ${dep}`);
|
|
1309
|
-
const id = JSON.stringify(depSchema.identifier);
|
|
1310
|
-
if (!acc[id]) newSchemas.push(depSchema);
|
|
1311
|
-
});
|
|
1312
|
-
if (schema.nested) {
|
|
1313
|
-
for (const nest of schema.nested) {
|
|
1314
|
-
if (isNestedIdentifier(nest.identifier)) continue;
|
|
1315
|
-
const id = JSON.stringify(nest.identifier);
|
|
1316
|
-
if (!acc[id]) newSchemas.push(nest);
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
return collectDeps(newSchemas, acc);
|
|
1322
|
-
};
|
|
1323
|
-
const shaked = collectDeps(focusedSchemas, {});
|
|
1324
|
-
return mkTypeSchemaIndex(shaked, { resolutionTree, logger });
|
|
1325
|
-
};
|
|
1326
|
-
var buildDependencyGraph = (schemas) => {
|
|
1327
|
-
const nameToMap = {};
|
|
1328
|
-
for (const schema of schemas) {
|
|
1329
|
-
nameToMap[schema.identifier.name] = schema;
|
|
1330
|
-
}
|
|
1331
|
-
const graph = {};
|
|
1332
|
-
for (const schema of schemas) {
|
|
1333
|
-
const name = schema.identifier.name;
|
|
1334
|
-
const base = schema.base?.name;
|
|
1335
|
-
if (!graph[name]) {
|
|
1336
|
-
graph[name] = [];
|
|
1337
|
-
}
|
|
1338
|
-
if (base && nameToMap[base]) {
|
|
1339
|
-
graph[name].push(base);
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
return graph;
|
|
1343
|
-
};
|
|
1344
|
-
var topologicalSort = (graph) => {
|
|
1345
|
-
const sorted = [];
|
|
1346
|
-
const visited = {};
|
|
1347
|
-
const temp = {};
|
|
1348
|
-
const visit = (node) => {
|
|
1349
|
-
if (temp[node]) {
|
|
1350
|
-
throw new Error(`Graph has cycles ${node}`);
|
|
1351
|
-
}
|
|
1352
|
-
if (!visited[node]) {
|
|
1353
|
-
temp[node] = true;
|
|
1354
|
-
for (const neighbor of graph[node] ?? []) {
|
|
1355
|
-
visit(neighbor);
|
|
1356
|
-
}
|
|
1357
|
-
temp[node] = false;
|
|
1358
|
-
visited[node] = true;
|
|
1359
|
-
sorted.push(node);
|
|
1360
|
-
}
|
|
1361
|
-
};
|
|
1362
|
-
for (const node in graph) {
|
|
1363
|
-
if (!visited[node]) {
|
|
1364
|
-
visit(node);
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
return sorted;
|
|
1368
|
-
};
|
|
1369
|
-
var sortAsDeclarationSequence = (schemas) => {
|
|
1370
|
-
const graph = buildDependencyGraph(schemas);
|
|
1371
|
-
const sorted = topologicalSort(graph);
|
|
1372
|
-
return sorted.map((name) => schemas.find((schema) => schema.identifier.name === name)).filter(Boolean);
|
|
1373
|
-
};
|
|
1374
|
-
var resourceRelatives = (schemas) => {
|
|
1375
|
-
const regularSchemas = schemas.filter((e) => isResourceTypeSchema(e) || isLogicalTypeSchema(e));
|
|
1376
|
-
const directPairs = [];
|
|
1377
|
-
for (const schema of regularSchemas) {
|
|
1378
|
-
if (schema.base) {
|
|
1379
|
-
directPairs.push({ parent: schema.base, child: schema.identifier });
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
1382
|
-
const allPairs = [...directPairs];
|
|
1383
|
-
const findTransitiveRelatives = (parentRef) => {
|
|
1384
|
-
const directChildren = directPairs.filter((pair) => pair.parent.name === parentRef.name).map((pair) => pair.child);
|
|
1385
|
-
const transitiveChildren = [];
|
|
1386
|
-
for (const child of directChildren) {
|
|
1387
|
-
transitiveChildren.push(...findTransitiveRelatives(child));
|
|
1388
|
-
}
|
|
1389
|
-
return [...directChildren, ...transitiveChildren];
|
|
1390
|
-
};
|
|
1391
|
-
for (const pair of directPairs) {
|
|
1392
|
-
const transitiveChildren = findTransitiveRelatives(pair.child);
|
|
1393
|
-
for (const transitiveChild of transitiveChildren) {
|
|
1394
|
-
if (!directPairs.some((dp) => dp.parent.name === pair.parent.name && dp.child.name === transitiveChild.name)) {
|
|
1395
|
-
allPairs.push({ parent: pair.parent, child: transitiveChild });
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
return allPairs;
|
|
1400
|
-
};
|
|
1401
|
-
var mkTypeSchemaIndex = (schemas, { resolutionTree, logger }) => {
|
|
1402
|
-
const index = {};
|
|
1403
|
-
const append = (schema) => {
|
|
1404
|
-
const url = schema.identifier.url;
|
|
1405
|
-
const pkg = schema.identifier.package;
|
|
1406
|
-
if (!index[url]) index[url] = {};
|
|
1407
|
-
if (index[url][schema.identifier.package] && pkg !== "shared") {
|
|
1408
|
-
const r1 = JSON.stringify(schema.identifier, void 0, 2);
|
|
1409
|
-
const r2 = JSON.stringify(index[url][pkg]?.identifier, void 0, 2);
|
|
1410
|
-
if (r1 !== r2) throw new Error(`Duplicate schema: ${r1} and ${r2}`);
|
|
1411
|
-
return;
|
|
837
|
+
return allPairs;
|
|
838
|
+
};
|
|
839
|
+
var mkTypeSchemaIndex = (schemas, { resolutionTree, logger }) => {
|
|
840
|
+
const index = {};
|
|
841
|
+
const append = (schema) => {
|
|
842
|
+
const url = schema.identifier.url;
|
|
843
|
+
const pkg = schema.identifier.package;
|
|
844
|
+
if (!index[url]) index[url] = {};
|
|
845
|
+
if (index[url][schema.identifier.package] && pkg !== "shared") {
|
|
846
|
+
const r1 = JSON.stringify(schema.identifier, void 0, 2);
|
|
847
|
+
const r2 = JSON.stringify(index[url][pkg]?.identifier, void 0, 2);
|
|
848
|
+
if (r1 !== r2) throw new Error(`Duplicate schema: ${r1} and ${r2}`);
|
|
849
|
+
return;
|
|
1412
850
|
}
|
|
1413
851
|
index[url][pkg] = schema;
|
|
1414
852
|
};
|
|
@@ -1804,7 +1242,7 @@ var Python = class extends Writer {
|
|
|
1804
1242
|
return names;
|
|
1805
1243
|
}
|
|
1806
1244
|
shouldImportResourceFamily(resource) {
|
|
1807
|
-
|
|
1245
|
+
assert3(this.tsIndex !== void 0);
|
|
1808
1246
|
return resource.identifier.kind === "resource" && this.tsIndex.resourceChildren(resource.identifier).length > 0;
|
|
1809
1247
|
}
|
|
1810
1248
|
generateExportsDeclaration(packageComplexTypes, allResourceNames) {
|
|
@@ -1839,244 +1277,757 @@ var Python = class extends Writer {
|
|
|
1839
1277
|
});
|
|
1840
1278
|
this.line();
|
|
1841
1279
|
}
|
|
1842
|
-
getSuperClasses(schema) {
|
|
1843
|
-
return [...schema.base ? [schema.base.name] : [], ...injectSuperClasses(schema.identifier.name)];
|
|
1280
|
+
getSuperClasses(schema) {
|
|
1281
|
+
return [...schema.base ? [schema.base.name] : [], ...injectSuperClasses(schema.identifier.name)];
|
|
1282
|
+
}
|
|
1283
|
+
generateClassBody(schema) {
|
|
1284
|
+
this.generateModelConfig();
|
|
1285
|
+
if (!schema.fields) {
|
|
1286
|
+
this.line("pass");
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
if (schema.identifier.kind === "resource") {
|
|
1290
|
+
this.generateResourceTypeField(schema);
|
|
1291
|
+
}
|
|
1292
|
+
this.generateFields(schema);
|
|
1293
|
+
if (schema.identifier.kind === "resource") {
|
|
1294
|
+
this.generateResourceMethods(schema);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
generateModelConfig() {
|
|
1298
|
+
const extraMode = this.opts.allowExtraFields ? "allow" : "forbid";
|
|
1299
|
+
this.line(`model_config = ConfigDict(validate_by_name=True, serialize_by_alias=True, extra="${extraMode}")`);
|
|
1300
|
+
}
|
|
1301
|
+
generateResourceTypeField(schema) {
|
|
1302
|
+
this.line(`${this.nameFormatFunction("resourceType")}: str = Field(`);
|
|
1303
|
+
this.indentBlock(() => {
|
|
1304
|
+
this.line(`default='${schema.identifier.name}',`);
|
|
1305
|
+
this.line(`alias='resourceType',`);
|
|
1306
|
+
this.line(`serialization_alias='resourceType',`);
|
|
1307
|
+
this.line("frozen=True,");
|
|
1308
|
+
this.line(`pattern='${schema.identifier.name}'`);
|
|
1309
|
+
});
|
|
1310
|
+
this.line(")");
|
|
1311
|
+
}
|
|
1312
|
+
generateFields(schema) {
|
|
1313
|
+
const sortedFields = Object.entries(schema.fields ?? []).sort(([a], [b]) => a.localeCompare(b));
|
|
1314
|
+
for (const [fieldName, field] of sortedFields) {
|
|
1315
|
+
if ("choices" in field && field.choices) continue;
|
|
1316
|
+
const fieldInfo = this.buildFieldInfo(fieldName, field);
|
|
1317
|
+
this.line(`${fieldInfo.name}: ${fieldInfo.type}${fieldInfo.defaultValue}`);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
buildFieldInfo(fieldName, field) {
|
|
1321
|
+
const pyFieldName = fixReservedWords(this.nameFormatFunction(fieldName));
|
|
1322
|
+
const fieldType = this.determineFieldType(field);
|
|
1323
|
+
const defaultValue = this.getFieldDefaultValue(field, fieldName);
|
|
1324
|
+
return {
|
|
1325
|
+
name: pyFieldName,
|
|
1326
|
+
type: fieldType,
|
|
1327
|
+
defaultValue
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
determineFieldType(field) {
|
|
1331
|
+
let fieldType = field ? this.getBaseFieldType(field) : "";
|
|
1332
|
+
if ("enum" in field && field.enum) {
|
|
1333
|
+
const s = field.enum.map((e) => `"${e}"`).join(", ");
|
|
1334
|
+
fieldType = `Literal[${s}]`;
|
|
1335
|
+
}
|
|
1336
|
+
if (field.array) {
|
|
1337
|
+
fieldType = `PyList[${fieldType}]`;
|
|
1338
|
+
}
|
|
1339
|
+
if (!field.required) {
|
|
1340
|
+
fieldType = `${fieldType} | None`;
|
|
1341
|
+
}
|
|
1342
|
+
return fieldType;
|
|
1343
|
+
}
|
|
1344
|
+
getBaseFieldType(field) {
|
|
1345
|
+
if ("type" in field && field.type.kind === "resource") return `${field.type.name}Family`;
|
|
1346
|
+
if ("type" in field && field.type.kind === "nested") return deriveResourceName(field.type);
|
|
1347
|
+
if ("type" in field && field.type.kind === "primitive-type")
|
|
1348
|
+
return PRIMITIVE_TYPE_MAP2[field.type.name] ?? "str";
|
|
1349
|
+
return "type" in field ? field.type.name : "";
|
|
1350
|
+
}
|
|
1351
|
+
getFieldDefaultValue(field, fieldName) {
|
|
1352
|
+
const aliasSpec = `alias="${fieldName}", serialization_alias="${fieldName}"`;
|
|
1353
|
+
if (!field.required) {
|
|
1354
|
+
return ` = Field(None, ${aliasSpec})`;
|
|
1355
|
+
}
|
|
1356
|
+
return ` = Field(${aliasSpec})`;
|
|
1357
|
+
}
|
|
1358
|
+
generateResourceMethods(schema) {
|
|
1359
|
+
const className = schema.identifier.name.toString();
|
|
1360
|
+
this.line();
|
|
1361
|
+
this.line("def to_json(self, indent: int | None = None) -> str:");
|
|
1362
|
+
this.line(" return self.model_dump_json(exclude_unset=True, exclude_none=True, indent=indent)");
|
|
1363
|
+
this.line();
|
|
1364
|
+
this.line("@classmethod");
|
|
1365
|
+
this.line(`def from_json(cls, json: str) -> ${className}:`);
|
|
1366
|
+
this.line(" return cls.model_validate_json(json)");
|
|
1367
|
+
}
|
|
1368
|
+
generateNestedTypes(schema) {
|
|
1369
|
+
if (!schema.nested) return;
|
|
1370
|
+
this.line();
|
|
1371
|
+
for (const subtype of schema.nested) {
|
|
1372
|
+
this.generateType(subtype);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
generateDefaultImports() {
|
|
1376
|
+
this.pyImportFrom("__future__", "annotations");
|
|
1377
|
+
this.pyImportFrom("pydantic", "BaseModel", "ConfigDict", "Field", "PositiveInt");
|
|
1378
|
+
this.pyImportFrom("typing", "List as PyList", "Literal");
|
|
1379
|
+
}
|
|
1380
|
+
generateDependenciesImports(schema) {
|
|
1381
|
+
if (!schema.dependencies || schema.dependencies.length === 0) return;
|
|
1382
|
+
this.importComplexTypeDependencies(schema.dependencies);
|
|
1383
|
+
this.importResourceDependencies(schema.dependencies);
|
|
1384
|
+
}
|
|
1385
|
+
importComplexTypeDependencies(dependencies) {
|
|
1386
|
+
const complexTypeDeps = dependencies.filter((dep) => dep.kind === "complex-type");
|
|
1387
|
+
const depsByPackage = this.groupDependenciesByPackage(complexTypeDeps);
|
|
1388
|
+
for (const [pyPackage, names] of Object.entries(depsByPackage)) {
|
|
1389
|
+
this.pyImportFrom(pyPackage, ...names.sort());
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
importResourceDependencies(dependencies) {
|
|
1393
|
+
const resourceDeps = dependencies.filter((dep) => dep.kind === "resource");
|
|
1394
|
+
for (const dep of resourceDeps) {
|
|
1395
|
+
this.pyImportType(dep);
|
|
1396
|
+
const familyName = `${pascalCase(dep.name)}Family`;
|
|
1397
|
+
const familyPackage = `${this.pyFhirPackage(dep)}.resource_families`;
|
|
1398
|
+
this.pyImportFrom(familyPackage, familyName);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
groupDependenciesByPackage(dependencies) {
|
|
1402
|
+
const grouped = {};
|
|
1403
|
+
for (const dep of dependencies) {
|
|
1404
|
+
const pyPackage = this.pyPackage(dep);
|
|
1405
|
+
if (!grouped[pyPackage]) {
|
|
1406
|
+
grouped[pyPackage] = [];
|
|
1407
|
+
}
|
|
1408
|
+
grouped[pyPackage].push(dep.name);
|
|
1409
|
+
}
|
|
1410
|
+
return grouped;
|
|
1411
|
+
}
|
|
1412
|
+
pyImportFrom(pyPackage, ...entities) {
|
|
1413
|
+
const oneLine = `from ${pyPackage} import ${entities.join(", ")}`;
|
|
1414
|
+
if (this.shouldUseSingleLineImport(oneLine, entities)) {
|
|
1415
|
+
this.line(oneLine);
|
|
1416
|
+
} else {
|
|
1417
|
+
this.writeMultiLineImport(pyPackage, entities);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
shouldUseSingleLineImport(oneLine, entities) {
|
|
1421
|
+
return oneLine.length <= MAX_IMPORT_LINE_LENGTH || entities.length === 1;
|
|
1422
|
+
}
|
|
1423
|
+
writeMultiLineImport(pyPackage, entities) {
|
|
1424
|
+
this.line(`from ${pyPackage} import (\\`);
|
|
1425
|
+
this.indentBlock(() => {
|
|
1426
|
+
const remaining = [...entities];
|
|
1427
|
+
while (remaining.length > 0) {
|
|
1428
|
+
const line = this.buildImportLine(remaining, MAX_IMPORT_LINE_LENGTH);
|
|
1429
|
+
this.line(line);
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
this.line(")");
|
|
1433
|
+
}
|
|
1434
|
+
pyImportType(identifier) {
|
|
1435
|
+
this.pyImportFrom(this.pyPackage(identifier), pascalCase(identifier.name));
|
|
1436
|
+
}
|
|
1437
|
+
generateResourceFamilies(packageResources) {
|
|
1438
|
+
assert3(this.tsIndex !== void 0);
|
|
1439
|
+
const packages = (
|
|
1440
|
+
//this.helper.getPackages(packageResources, this.opts.rootPackageName);
|
|
1441
|
+
Object.keys(groupByPackages(packageResources)).map(
|
|
1442
|
+
(pkgName) => `${this.opts.rootPackageName}.${pkgName.replaceAll(".", "_")}`
|
|
1443
|
+
)
|
|
1444
|
+
);
|
|
1445
|
+
const families = {};
|
|
1446
|
+
for (const resource of this.tsIndex.collectResources()) {
|
|
1447
|
+
const children = this.tsIndex.resourceChildren(resource.identifier).map((c) => c.name);
|
|
1448
|
+
if (children.length > 0) {
|
|
1449
|
+
const familyName = `${resource.identifier.name}Family`;
|
|
1450
|
+
families[familyName] = children;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
const exportList = Object.keys(families);
|
|
1454
|
+
if (exportList.length === 0) return;
|
|
1455
|
+
this.buildResourceFamiliesFile(packages, families, exportList);
|
|
1456
|
+
}
|
|
1457
|
+
buildResourceFamiliesFile(packages, families, exportList) {
|
|
1458
|
+
this.cat("resource_families.py", () => {
|
|
1459
|
+
this.generateDisclaimer();
|
|
1460
|
+
this.includeResourceFamilyValidator();
|
|
1461
|
+
this.line();
|
|
1462
|
+
this.generateFamilyDefinitions(packages, families);
|
|
1463
|
+
this.generateFamilyExports(exportList);
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
includeResourceFamilyValidator() {
|
|
1467
|
+
const content = fs__default.readFileSync(resolvePyAssets("resource_family_validator.py"), "utf-8");
|
|
1468
|
+
this.line(content);
|
|
1469
|
+
}
|
|
1470
|
+
generateFamilyDefinitions(packages, families) {
|
|
1471
|
+
this.line(`packages = [${packages.map((p) => `'${p}'`).join(", ")}]`);
|
|
1472
|
+
this.line();
|
|
1473
|
+
for (const [familyName, resources] of Object.entries(families)) {
|
|
1474
|
+
this.generateFamilyDefinition(familyName, resources);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
generateFamilyDefinition(familyName, resources) {
|
|
1478
|
+
const listName = `${familyName}_resources`;
|
|
1479
|
+
this.line(`${listName} = [${resources.map((r) => `'${r}'`).join(", ")}]`);
|
|
1480
|
+
this.line();
|
|
1481
|
+
this.line(`def validate_and_downcast_${familyName}(v: Any) -> Any:`);
|
|
1482
|
+
this.line(` return validate_and_downcast(v, packages, ${listName})`);
|
|
1483
|
+
this.line();
|
|
1484
|
+
this.line(`type ${familyName} = Annotated[Any, BeforeValidator(validate_and_downcast_${familyName})]`);
|
|
1485
|
+
this.line();
|
|
1486
|
+
}
|
|
1487
|
+
generateFamilyExports(exportList) {
|
|
1488
|
+
this.line(`__all__ = [${exportList.map((e) => `'${e}'`).join(", ")}]`);
|
|
1844
1489
|
}
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
this.line("pass");
|
|
1849
|
-
return;
|
|
1850
|
-
}
|
|
1851
|
-
if (schema.identifier.kind === "resource") {
|
|
1852
|
-
this.generateResourceTypeField(schema);
|
|
1853
|
-
}
|
|
1854
|
-
this.generateFields(schema);
|
|
1855
|
-
if (schema.identifier.kind === "resource") {
|
|
1856
|
-
this.generateResourceMethods(schema);
|
|
1857
|
-
}
|
|
1490
|
+
buildPyPackageName(packageName) {
|
|
1491
|
+
const parts = packageName ? [snakeCase(packageName)] : [""];
|
|
1492
|
+
return parts.join(".");
|
|
1858
1493
|
}
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
this.line(`model_config = ConfigDict(validate_by_name=True, serialize_by_alias=True, extra="${extraMode}")`);
|
|
1494
|
+
pyFhirPackage(identifier) {
|
|
1495
|
+
return this.pyFhirPackageByName(identifier.package);
|
|
1862
1496
|
}
|
|
1863
|
-
|
|
1864
|
-
this.
|
|
1865
|
-
this.indentBlock(() => {
|
|
1866
|
-
this.line(`default='${schema.identifier.name}',`);
|
|
1867
|
-
this.line(`alias='resourceType',`);
|
|
1868
|
-
this.line(`serialization_alias='resourceType',`);
|
|
1869
|
-
this.line("frozen=True,");
|
|
1870
|
-
this.line(`pattern='${schema.identifier.name}'`);
|
|
1871
|
-
});
|
|
1872
|
-
this.line(")");
|
|
1497
|
+
pyFhirPackageByName(name) {
|
|
1498
|
+
return [this.opts.rootPackageName, this.buildPyPackageName(name)].join(".");
|
|
1873
1499
|
}
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
if ("choices" in field && field.choices) continue;
|
|
1878
|
-
const fieldInfo = this.buildFieldInfo(fieldName, field);
|
|
1879
|
-
this.line(`${fieldInfo.name}: ${fieldInfo.type}${fieldInfo.defaultValue}`);
|
|
1500
|
+
pyPackage(identifier) {
|
|
1501
|
+
if (identifier.kind === "complex-type") {
|
|
1502
|
+
return `${this.pyFhirPackage(identifier)}.base`;
|
|
1880
1503
|
}
|
|
1504
|
+
if (identifier.kind === "resource") {
|
|
1505
|
+
return [this.pyFhirPackage(identifier), snakeCase(identifier.name)].join(".");
|
|
1506
|
+
}
|
|
1507
|
+
return this.pyFhirPackage(identifier);
|
|
1881
1508
|
}
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
defaultValue
|
|
1890
|
-
};
|
|
1509
|
+
getFieldFormatFunction(format) {
|
|
1510
|
+
if (!AVAILABLE_STRING_FORMATS[format]) {
|
|
1511
|
+
this.logger()?.warn(`Unknown field format '${format}'. Defaulting to SnakeCase.`);
|
|
1512
|
+
this.logger()?.warn(`Supported formats: ${Object.keys(AVAILABLE_STRING_FORMATS).join(", ")}`);
|
|
1513
|
+
return snakeCase;
|
|
1514
|
+
}
|
|
1515
|
+
return AVAILABLE_STRING_FORMATS[format];
|
|
1891
1516
|
}
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1517
|
+
};
|
|
1518
|
+
|
|
1519
|
+
// src/typeschema/core/identifier.ts
|
|
1520
|
+
function dropVersionFromUrl(url) {
|
|
1521
|
+
const baseUrl = url.split("|")[0];
|
|
1522
|
+
return baseUrl ? baseUrl : url;
|
|
1523
|
+
}
|
|
1524
|
+
function getVersionFromUrl(url) {
|
|
1525
|
+
const version = url.split("|")[1];
|
|
1526
|
+
return version;
|
|
1527
|
+
}
|
|
1528
|
+
function determineKind(fhirSchema) {
|
|
1529
|
+
if (fhirSchema.derivation === "constraint") return "profile";
|
|
1530
|
+
if (fhirSchema.kind === "primitive-type") return "primitive-type";
|
|
1531
|
+
if (fhirSchema.kind === "complex-type") return "complex-type";
|
|
1532
|
+
if (fhirSchema.kind === "resource") return "resource";
|
|
1533
|
+
if (fhirSchema.kind === "logical") return "logical";
|
|
1534
|
+
return "resource";
|
|
1535
|
+
}
|
|
1536
|
+
function mkIdentifier(fhirSchema) {
|
|
1537
|
+
return {
|
|
1538
|
+
kind: determineKind(fhirSchema),
|
|
1539
|
+
package: fhirSchema.package_meta.name,
|
|
1540
|
+
version: fhirSchema.package_meta.version,
|
|
1541
|
+
name: fhirSchema.name,
|
|
1542
|
+
url: fhirSchema.url
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
var getValueSetName = (url) => {
|
|
1546
|
+
const urlParts = url.split("/");
|
|
1547
|
+
const lastSegment = urlParts[urlParts.length - 1];
|
|
1548
|
+
if (lastSegment && lastSegment.length > 0) {
|
|
1549
|
+
return lastSegment.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
1550
|
+
}
|
|
1551
|
+
return url;
|
|
1552
|
+
};
|
|
1553
|
+
function mkValueSetIdentifierByUrl(register, pkg, fullValueSetUrl) {
|
|
1554
|
+
const valueSetUrl = dropVersionFromUrl(fullValueSetUrl);
|
|
1555
|
+
const valueSetNameFallback = getValueSetName(valueSetUrl);
|
|
1556
|
+
const valuesSetFallback = {
|
|
1557
|
+
package_meta: {
|
|
1558
|
+
name: "missing_valuesets",
|
|
1559
|
+
version: getVersionFromUrl(valueSetUrl) || "0.0.0"
|
|
1560
|
+
},
|
|
1561
|
+
id: fullValueSetUrl};
|
|
1562
|
+
const valueSet = register.resolveVs(pkg, valueSetUrl) || valuesSetFallback;
|
|
1563
|
+
const valueSetName = valueSet?.id && !/^[a-zA-Z0-9_-]{20,}$/.test(valueSet.id) ? valueSet.id : valueSetNameFallback;
|
|
1564
|
+
return {
|
|
1565
|
+
kind: "value-set",
|
|
1566
|
+
package: valueSet.package_meta.name,
|
|
1567
|
+
version: valueSet.package_meta.version,
|
|
1568
|
+
name: valueSetName,
|
|
1569
|
+
url: valueSetUrl
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1572
|
+
function mkBindingIdentifier(fhirSchema, path, bindingName) {
|
|
1573
|
+
const pathStr = path.join(".");
|
|
1574
|
+
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`];
|
|
1575
|
+
return {
|
|
1576
|
+
kind: "binding",
|
|
1577
|
+
package: pkg.name,
|
|
1578
|
+
version: pkg.version,
|
|
1579
|
+
name,
|
|
1580
|
+
url
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
// src/typeschema/core/nested-types.ts
|
|
1585
|
+
function mkNestedIdentifier(register, fhirSchema, path, logger) {
|
|
1586
|
+
const nestedTypeOrigins = {};
|
|
1587
|
+
if (fhirSchema.derivation === "constraint") {
|
|
1588
|
+
const specializations = register.resolveFsSpecializations(fhirSchema.package_meta, fhirSchema.url);
|
|
1589
|
+
const nestedTypeGenealogy = specializations.map((fs5) => mkNestedTypes(register, fs5, logger)).filter((e) => e !== void 0).flat();
|
|
1590
|
+
for (const nt of nestedTypeGenealogy.reverse()) {
|
|
1591
|
+
nestedTypeOrigins[nt.identifier.name] = nt.identifier.url;
|
|
1897
1592
|
}
|
|
1898
|
-
|
|
1899
|
-
|
|
1593
|
+
}
|
|
1594
|
+
const nestedName = path.join(".");
|
|
1595
|
+
const url = nestedTypeOrigins[nestedName] ?? `${fhirSchema.url}#${nestedName}`;
|
|
1596
|
+
return {
|
|
1597
|
+
kind: "nested",
|
|
1598
|
+
package: fhirSchema.package_meta.name,
|
|
1599
|
+
version: fhirSchema.package_meta.version,
|
|
1600
|
+
name: nestedName,
|
|
1601
|
+
url
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
1604
|
+
function collectNestedElements(fhirSchema, parentPath, elements) {
|
|
1605
|
+
const nested = [];
|
|
1606
|
+
for (const [key, element] of Object.entries(elements)) {
|
|
1607
|
+
const path = [...parentPath, key];
|
|
1608
|
+
if (isNestedElement(element)) {
|
|
1609
|
+
nested.push([path, element]);
|
|
1900
1610
|
}
|
|
1901
|
-
if (
|
|
1902
|
-
|
|
1611
|
+
if (element.elements) {
|
|
1612
|
+
nested.push(...collectNestedElements(fhirSchema, path, element.elements));
|
|
1903
1613
|
}
|
|
1904
|
-
return fieldType;
|
|
1905
1614
|
}
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1615
|
+
return nested;
|
|
1616
|
+
}
|
|
1617
|
+
function transformNestedElements(register, fhirSchema, parentPath, elements, logger) {
|
|
1618
|
+
const fields = {};
|
|
1619
|
+
for (const [key, _element] of Object.entries(elements)) {
|
|
1620
|
+
const path = [...parentPath, key];
|
|
1621
|
+
const elemSnapshot = register.resolveElementSnapshot(fhirSchema, path);
|
|
1622
|
+
if (isNestedElement(elemSnapshot)) {
|
|
1623
|
+
fields[key] = mkNestedField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1624
|
+
} else {
|
|
1625
|
+
fields[key] = mkField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1917
1626
|
}
|
|
1918
|
-
return ` = Field(${aliasSpec})`;
|
|
1919
1627
|
}
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1628
|
+
return fields;
|
|
1629
|
+
}
|
|
1630
|
+
function mkNestedTypes(register, fhirSchema, logger) {
|
|
1631
|
+
if (!fhirSchema.elements) return void 0;
|
|
1632
|
+
const nested = collectNestedElements(fhirSchema, [], fhirSchema.elements).filter(
|
|
1633
|
+
([_, element]) => element.elements && Object.keys(element.elements).length > 0
|
|
1634
|
+
);
|
|
1635
|
+
const nestedTypes = [];
|
|
1636
|
+
for (const [path, element] of nested) {
|
|
1637
|
+
const identifier = mkNestedIdentifier(register, fhirSchema, path, logger);
|
|
1638
|
+
let baseName;
|
|
1639
|
+
if (element.type === "BackboneElement" || !element.type) {
|
|
1640
|
+
baseName = "BackboneElement";
|
|
1641
|
+
} else {
|
|
1642
|
+
baseName = element.type;
|
|
1643
|
+
}
|
|
1644
|
+
const baseUrl = register.ensureSpecializationCanonicalUrl(baseName);
|
|
1645
|
+
const baseFs = register.resolveFs(fhirSchema.package_meta, baseUrl);
|
|
1646
|
+
if (!baseFs) throw new Error(`Could not resolve base type ${baseName}`);
|
|
1647
|
+
const base = {
|
|
1648
|
+
kind: "complex-type",
|
|
1649
|
+
package: baseFs.package_meta.name,
|
|
1650
|
+
version: baseFs.package_meta.version,
|
|
1651
|
+
name: baseName,
|
|
1652
|
+
url: baseUrl
|
|
1653
|
+
};
|
|
1654
|
+
const fields = transformNestedElements(register, fhirSchema, path, element.elements ?? {}, logger);
|
|
1655
|
+
const nestedType = {
|
|
1656
|
+
identifier,
|
|
1657
|
+
base,
|
|
1658
|
+
fields
|
|
1659
|
+
};
|
|
1660
|
+
nestedTypes.push(nestedType);
|
|
1929
1661
|
}
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1662
|
+
nestedTypes.sort((a, b) => a.identifier.url.localeCompare(b.identifier.url));
|
|
1663
|
+
return nestedTypes.length === 0 ? void 0 : nestedTypes;
|
|
1664
|
+
}
|
|
1665
|
+
function extractNestedDependencies(nestedTypes) {
|
|
1666
|
+
const deps = [];
|
|
1667
|
+
for (const nested of nestedTypes) {
|
|
1668
|
+
if (nested.base) {
|
|
1669
|
+
deps.push(nested.base);
|
|
1670
|
+
}
|
|
1671
|
+
for (const field of Object.values(nested.fields || {})) {
|
|
1672
|
+
if ("type" in field && field.type) {
|
|
1673
|
+
deps.push(field.type);
|
|
1674
|
+
}
|
|
1675
|
+
if ("binding" in field && field.binding) {
|
|
1676
|
+
deps.push(field.binding);
|
|
1677
|
+
}
|
|
1935
1678
|
}
|
|
1936
1679
|
}
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
const
|
|
1950
|
-
|
|
1951
|
-
|
|
1680
|
+
return deps;
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
// src/typeschema/core/field-builder.ts
|
|
1684
|
+
function isRequired(register, fhirSchema, path) {
|
|
1685
|
+
const fieldName = path[path.length - 1];
|
|
1686
|
+
if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
|
|
1687
|
+
const parentPath = path.slice(0, -1);
|
|
1688
|
+
const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs5) => {
|
|
1689
|
+
if (parentPath.length === 0) return fs5.required || [];
|
|
1690
|
+
if (!fs5.elements) return [];
|
|
1691
|
+
let elem = fs5;
|
|
1692
|
+
for (const k of parentPath) {
|
|
1693
|
+
elem = elem?.elements?.[k];
|
|
1694
|
+
}
|
|
1695
|
+
return elem?.required || [];
|
|
1696
|
+
});
|
|
1697
|
+
return new Set(requires).has(fieldName);
|
|
1698
|
+
}
|
|
1699
|
+
function isExcluded(register, fhirSchema, path) {
|
|
1700
|
+
const fieldName = path[path.length - 1];
|
|
1701
|
+
if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
|
|
1702
|
+
const parentPath = path.slice(0, -1);
|
|
1703
|
+
const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs5) => {
|
|
1704
|
+
if (parentPath.length === 0) return fs5.excluded || [];
|
|
1705
|
+
if (!fs5.elements) return [];
|
|
1706
|
+
let elem = fs5;
|
|
1707
|
+
for (const k of parentPath) {
|
|
1708
|
+
elem = elem?.elements?.[k];
|
|
1952
1709
|
}
|
|
1710
|
+
return elem?.excluded || [];
|
|
1711
|
+
});
|
|
1712
|
+
return new Set(requires).has(fieldName);
|
|
1713
|
+
}
|
|
1714
|
+
var buildReferences = (register, fhirSchema, element) => {
|
|
1715
|
+
if (!element.refers) return void 0;
|
|
1716
|
+
return element.refers.map((ref) => {
|
|
1717
|
+
const curl = register.ensureSpecializationCanonicalUrl(ref);
|
|
1718
|
+
const fs5 = register.resolveFs(fhirSchema.package_meta, curl);
|
|
1719
|
+
if (!fs5) throw new Error(`Failed to resolve fs for ${curl}`);
|
|
1720
|
+
return mkIdentifier(fs5);
|
|
1721
|
+
});
|
|
1722
|
+
};
|
|
1723
|
+
function buildFieldType(register, fhirSchema, path, element, logger) {
|
|
1724
|
+
if (element.elementReference) {
|
|
1725
|
+
const refPath = element.elementReference.slice(1).filter((_, i) => i % 2 === 1);
|
|
1726
|
+
return mkNestedIdentifier(register, fhirSchema, refPath, logger);
|
|
1727
|
+
} else if (element.type) {
|
|
1728
|
+
const url = register.ensureSpecializationCanonicalUrl(element.type);
|
|
1729
|
+
const fieldFs = register.resolveFs(fhirSchema.package_meta, url);
|
|
1730
|
+
if (!fieldFs)
|
|
1731
|
+
throw new Error(
|
|
1732
|
+
`Could not resolve field type: '${element.type}' (from '${fhirSchema.url}' in '${packageMetaToFhir(fhirSchema.package_meta)}')`
|
|
1733
|
+
);
|
|
1734
|
+
return mkIdentifier(fieldFs);
|
|
1735
|
+
} else if (element.choices) {
|
|
1736
|
+
return void 0;
|
|
1737
|
+
} else if (fhirSchema.derivation === "constraint") {
|
|
1738
|
+
return void 0;
|
|
1739
|
+
} else {
|
|
1740
|
+
logger?.error(
|
|
1741
|
+
`Can't recognize element type '${fhirSchema.url}' (${fhirSchema.derivation}) at '${path.join(".")}': ${JSON.stringify(element, void 0, 2)}`
|
|
1742
|
+
);
|
|
1743
|
+
throw new Error(`Unrecognized element type`);
|
|
1953
1744
|
}
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1745
|
+
}
|
|
1746
|
+
var mkField = (register, fhirSchema, path, element, logger) => {
|
|
1747
|
+
let binding;
|
|
1748
|
+
let enumValues;
|
|
1749
|
+
if (element.binding) {
|
|
1750
|
+
binding = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
|
|
1751
|
+
if (element.binding.strength === "required" && element.type === "code") {
|
|
1752
|
+
enumValues = buildEnum(register, fhirSchema, element, logger);
|
|
1961
1753
|
}
|
|
1962
1754
|
}
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1755
|
+
const fieldType = buildFieldType(register, fhirSchema, path, element, logger);
|
|
1756
|
+
if (!fieldType)
|
|
1757
|
+
logger?.warn(`Field type not found for '${fhirSchema.url}#${path.join(".")}' (${fhirSchema.derivation})`);
|
|
1758
|
+
return {
|
|
1759
|
+
type: fieldType,
|
|
1760
|
+
required: isRequired(register, fhirSchema, path),
|
|
1761
|
+
excluded: isExcluded(register, fhirSchema, path),
|
|
1762
|
+
reference: buildReferences(register, fhirSchema, element),
|
|
1763
|
+
array: element.array || false,
|
|
1764
|
+
min: element.min,
|
|
1765
|
+
max: element.max,
|
|
1766
|
+
choices: element.choices,
|
|
1767
|
+
choiceOf: element.choiceOf,
|
|
1768
|
+
binding,
|
|
1769
|
+
enum: enumValues
|
|
1770
|
+
};
|
|
1771
|
+
};
|
|
1772
|
+
function isNestedElement(element) {
|
|
1773
|
+
const isBackbone = element.type === "BackboneElement";
|
|
1774
|
+
const isElement = element.type === "Element" && element.elements !== void 0 && Object.keys(element.elements).length > 0;
|
|
1775
|
+
const elementsWithoutType = element.type === void 0 && element.choiceOf === void 0 && element.elements !== void 0 && Object.keys(element.elements).length > 0;
|
|
1776
|
+
return isBackbone || isElement || elementsWithoutType;
|
|
1777
|
+
}
|
|
1778
|
+
function mkNestedField(register, fhirSchema, path, element, logger) {
|
|
1779
|
+
const nestedIdentifier = mkNestedIdentifier(register, fhirSchema, path, logger);
|
|
1780
|
+
return {
|
|
1781
|
+
type: nestedIdentifier,
|
|
1782
|
+
array: element.array || false,
|
|
1783
|
+
required: isRequired(register, fhirSchema, path),
|
|
1784
|
+
excluded: isExcluded(register, fhirSchema, path)
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
// src/typeschema/core/binding.ts
|
|
1789
|
+
function extractValueSetConceptsByUrl(register, pkg, valueSetUrl, logger) {
|
|
1790
|
+
const cleanUrl = dropVersionFromUrl(valueSetUrl) || valueSetUrl;
|
|
1791
|
+
const valueSet = register.resolveVs(pkg, cleanUrl);
|
|
1792
|
+
if (!valueSet) return void 0;
|
|
1793
|
+
return extractValueSetConcepts(register, valueSet);
|
|
1794
|
+
}
|
|
1795
|
+
function extractValueSetConcepts(register, valueSet, _logger) {
|
|
1796
|
+
if (valueSet.expansion?.contains) {
|
|
1797
|
+
return valueSet.expansion.contains.filter((item) => item.code !== void 0).map((item) => {
|
|
1798
|
+
assert3(item.code);
|
|
1799
|
+
return {
|
|
1800
|
+
code: item.code,
|
|
1801
|
+
display: item.display,
|
|
1802
|
+
system: item.system
|
|
1803
|
+
};
|
|
1804
|
+
});
|
|
1805
|
+
}
|
|
1806
|
+
const concepts = [];
|
|
1807
|
+
if (valueSet.compose?.include) {
|
|
1808
|
+
for (const include of valueSet.compose.include) {
|
|
1809
|
+
if (include.concept) {
|
|
1810
|
+
for (const concept of include.concept) {
|
|
1811
|
+
concepts.push({
|
|
1812
|
+
system: include.system,
|
|
1813
|
+
code: concept.code,
|
|
1814
|
+
display: concept.display
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
} else if (include.system && !include.filter) {
|
|
1818
|
+
try {
|
|
1819
|
+
const codeSystem = register.resolveAny(include.system);
|
|
1820
|
+
if (codeSystem?.concept) {
|
|
1821
|
+
const extractConcepts = (conceptList, system) => {
|
|
1822
|
+
for (const concept of conceptList) {
|
|
1823
|
+
concepts.push({
|
|
1824
|
+
system,
|
|
1825
|
+
code: concept.code,
|
|
1826
|
+
display: concept.display
|
|
1827
|
+
});
|
|
1828
|
+
if (concept.concept) {
|
|
1829
|
+
extractConcepts(concept.concept, system);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
};
|
|
1833
|
+
extractConcepts(codeSystem.concept, include.system);
|
|
1834
|
+
}
|
|
1835
|
+
} catch {
|
|
1836
|
+
}
|
|
1969
1837
|
}
|
|
1970
|
-
grouped[pyPackage].push(dep.name);
|
|
1971
1838
|
}
|
|
1972
|
-
return grouped;
|
|
1973
1839
|
}
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1840
|
+
return concepts.length > 0 ? concepts : void 0;
|
|
1841
|
+
}
|
|
1842
|
+
var MAX_ENUM_LENGTH = 100;
|
|
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
|
+
const shouldGenerateEnum = strength === "required" || strength === "extensible" && (element.type === "code" || element.type === "Coding") || strength === "preferred" && (element.type === "code" || element.type === "Coding");
|
|
1849
|
+
if (!shouldGenerateEnum) return void 0;
|
|
1850
|
+
const concepts = extractValueSetConceptsByUrl(register, fhirSchema.package_meta, valueSetUrl);
|
|
1851
|
+
if (!concepts || concepts.length === 0) return void 0;
|
|
1852
|
+
const codes = concepts.map((c) => c.code).filter((code) => code && typeof code === "string" && code.trim().length > 0);
|
|
1853
|
+
if (codes.length > MAX_ENUM_LENGTH) {
|
|
1854
|
+
logger?.dry_warn(
|
|
1855
|
+
`Value set ${valueSetUrl} has ${codes.length} which is more than ${MAX_ENUM_LENGTH} codes, which may cause issues with code generation.`
|
|
1856
|
+
);
|
|
1857
|
+
return void 0;
|
|
1981
1858
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1859
|
+
return codes.length > 0 ? codes : void 0;
|
|
1860
|
+
}
|
|
1861
|
+
function generateBindingSchema(register, fhirSchema, path, element, logger) {
|
|
1862
|
+
if (!element.binding?.valueSet) return void 0;
|
|
1863
|
+
const identifier = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
|
|
1864
|
+
const fieldType = buildFieldType(register, fhirSchema, path, element, logger);
|
|
1865
|
+
const valueSetIdentifier = mkValueSetIdentifierByUrl(
|
|
1866
|
+
register,
|
|
1867
|
+
fhirSchema.package_meta,
|
|
1868
|
+
element.binding.valueSet
|
|
1869
|
+
);
|
|
1870
|
+
const dependencies = [];
|
|
1871
|
+
if (fieldType) {
|
|
1872
|
+
dependencies.push(fieldType);
|
|
1984
1873
|
}
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1874
|
+
dependencies.push(valueSetIdentifier);
|
|
1875
|
+
const enumValues = buildEnum(register, fhirSchema, element, logger);
|
|
1876
|
+
return {
|
|
1877
|
+
identifier,
|
|
1878
|
+
type: fieldType,
|
|
1879
|
+
valueset: valueSetIdentifier,
|
|
1880
|
+
strength: element.binding.strength,
|
|
1881
|
+
enum: enumValues,
|
|
1882
|
+
dependencies
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
function collectBindingSchemas(register, fhirSchema, logger) {
|
|
1886
|
+
const processedPaths = /* @__PURE__ */ new Set();
|
|
1887
|
+
if (!fhirSchema.elements) return [];
|
|
1888
|
+
const bindings = [];
|
|
1889
|
+
function collectBindings(elements, parentPath) {
|
|
1890
|
+
for (const [key, element] of Object.entries(elements)) {
|
|
1891
|
+
const path = [...parentPath, key];
|
|
1892
|
+
const pathKey = path.join(".");
|
|
1893
|
+
if (processedPaths.has(pathKey)) continue;
|
|
1894
|
+
processedPaths.add(pathKey);
|
|
1895
|
+
if (element.binding) {
|
|
1896
|
+
const binding = generateBindingSchema(register, fhirSchema, path, element, logger);
|
|
1897
|
+
if (binding) {
|
|
1898
|
+
bindings.push(binding);
|
|
1899
|
+
}
|
|
1992
1900
|
}
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
}
|
|
1996
|
-
pyImportType(identifier) {
|
|
1997
|
-
this.pyImportFrom(this.pyPackage(identifier), pascalCase(identifier.name));
|
|
1998
|
-
}
|
|
1999
|
-
generateResourceFamilies(packageResources) {
|
|
2000
|
-
assert2(this.tsIndex !== void 0);
|
|
2001
|
-
const packages = (
|
|
2002
|
-
//this.helper.getPackages(packageResources, this.opts.rootPackageName);
|
|
2003
|
-
Object.keys(groupByPackages(packageResources)).map(
|
|
2004
|
-
(pkgName) => `${this.opts.rootPackageName}.${pkgName.replaceAll(".", "_")}`
|
|
2005
|
-
)
|
|
2006
|
-
);
|
|
2007
|
-
const families = {};
|
|
2008
|
-
for (const resource of this.tsIndex.collectResources()) {
|
|
2009
|
-
const children = this.tsIndex.resourceChildren(resource.identifier).map((c) => c.name);
|
|
2010
|
-
if (children.length > 0) {
|
|
2011
|
-
const familyName = `${resource.identifier.name}Family`;
|
|
2012
|
-
families[familyName] = children;
|
|
1901
|
+
if (element.elements) {
|
|
1902
|
+
collectBindings(element.elements, path);
|
|
2013
1903
|
}
|
|
2014
1904
|
}
|
|
2015
|
-
const exportList = Object.keys(families);
|
|
2016
|
-
if (exportList.length === 0) return;
|
|
2017
|
-
this.buildResourceFamiliesFile(packages, families, exportList);
|
|
2018
1905
|
}
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
1906
|
+
collectBindings(fhirSchema.elements, []);
|
|
1907
|
+
bindings.sort((a, b) => a.identifier.name.localeCompare(b.identifier.name));
|
|
1908
|
+
const uniqueBindings = [];
|
|
1909
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
1910
|
+
for (const binding of bindings) {
|
|
1911
|
+
if (!seenUrls.has(binding.identifier.url)) {
|
|
1912
|
+
seenUrls.add(binding.identifier.url);
|
|
1913
|
+
uniqueBindings.push(binding);
|
|
1914
|
+
}
|
|
2027
1915
|
}
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
1916
|
+
return uniqueBindings;
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
// src/typeschema/core/transformer.ts
|
|
1920
|
+
function mkFields(register, fhirSchema, parentPath, elements, logger) {
|
|
1921
|
+
if (!elements) return void 0;
|
|
1922
|
+
const fields = {};
|
|
1923
|
+
for (const key of register.getAllElementKeys(elements)) {
|
|
1924
|
+
const path = [...parentPath, key];
|
|
1925
|
+
const elemSnapshot = register.resolveElementSnapshot(fhirSchema, path);
|
|
1926
|
+
if (isNestedElement(elemSnapshot)) {
|
|
1927
|
+
fields[key] = mkNestedField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1928
|
+
} else {
|
|
1929
|
+
fields[key] = mkField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1930
|
+
}
|
|
2031
1931
|
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
1932
|
+
return fields;
|
|
1933
|
+
}
|
|
1934
|
+
function extractFieldDependencies(fields) {
|
|
1935
|
+
const deps = [];
|
|
1936
|
+
for (const field of Object.values(fields)) {
|
|
1937
|
+
if ("type" in field && field.type) {
|
|
1938
|
+
deps.push(field.type);
|
|
1939
|
+
}
|
|
1940
|
+
if ("binding" in field && field.binding) {
|
|
1941
|
+
deps.push(field.binding);
|
|
2037
1942
|
}
|
|
2038
1943
|
}
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
this.line(` return validate_and_downcast(v, packages, ${listName})`);
|
|
2045
|
-
this.line();
|
|
2046
|
-
this.line(`type ${familyName} = Annotated[Any, BeforeValidator(validate_and_downcast_${familyName})]`);
|
|
2047
|
-
this.line();
|
|
1944
|
+
return deps;
|
|
1945
|
+
}
|
|
1946
|
+
function isExtensionSchema(fhirSchema, _identifier) {
|
|
1947
|
+
if (fhirSchema.base === "Extension" || fhirSchema.base === "http://hl7.org/fhir/StructureDefinition/Extension") {
|
|
1948
|
+
return true;
|
|
2048
1949
|
}
|
|
2049
|
-
|
|
2050
|
-
|
|
1950
|
+
if (fhirSchema.url?.includes("/extension/") || fhirSchema.url?.includes("-extension")) {
|
|
1951
|
+
return true;
|
|
2051
1952
|
}
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
return parts.join(".");
|
|
1953
|
+
if (fhirSchema.name?.toLowerCase().includes("extension")) {
|
|
1954
|
+
return true;
|
|
2055
1955
|
}
|
|
2056
|
-
|
|
2057
|
-
return
|
|
1956
|
+
if (fhirSchema.type === "Extension") {
|
|
1957
|
+
return true;
|
|
2058
1958
|
}
|
|
2059
|
-
|
|
2060
|
-
|
|
1959
|
+
return false;
|
|
1960
|
+
}
|
|
1961
|
+
async function transformValueSet(register, valueSet, logger) {
|
|
1962
|
+
if (!valueSet.url) throw new Error("ValueSet URL is required");
|
|
1963
|
+
const identifier = mkValueSetIdentifierByUrl(register, valueSet.package_meta, valueSet.url);
|
|
1964
|
+
const concept = extractValueSetConceptsByUrl(register, valueSet.package_meta, valueSet.url);
|
|
1965
|
+
return {
|
|
1966
|
+
identifier,
|
|
1967
|
+
description: valueSet.description,
|
|
1968
|
+
concept,
|
|
1969
|
+
compose: !concept ? valueSet.compose : void 0
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1972
|
+
function extractDependencies(identifier, base, fields, nestedTypes) {
|
|
1973
|
+
const deps = [];
|
|
1974
|
+
if (base) deps.push(base);
|
|
1975
|
+
if (fields) deps.push(...extractFieldDependencies(fields));
|
|
1976
|
+
if (nestedTypes) deps.push(...extractNestedDependencies(nestedTypes));
|
|
1977
|
+
const uniqDeps = {};
|
|
1978
|
+
for (const dep of deps) {
|
|
1979
|
+
if (dep.url === identifier.url) continue;
|
|
1980
|
+
uniqDeps[dep.url] = dep;
|
|
2061
1981
|
}
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
1982
|
+
const localNestedTypeUrls = new Set(nestedTypes?.map((nt) => nt.identifier.url));
|
|
1983
|
+
const result = Object.values(uniqDeps).filter((e) => {
|
|
1984
|
+
if (isProfileIdentifier(identifier)) return true;
|
|
1985
|
+
if (!isNestedIdentifier(e)) return true;
|
|
1986
|
+
return !localNestedTypeUrls.has(e.url);
|
|
1987
|
+
}).sort((a, b) => a.url.localeCompare(b.url));
|
|
1988
|
+
return result.length > 0 ? result : void 0;
|
|
1989
|
+
}
|
|
1990
|
+
function transformFhirSchemaResource(register, fhirSchema, logger) {
|
|
1991
|
+
const identifier = mkIdentifier(fhirSchema);
|
|
1992
|
+
let base;
|
|
1993
|
+
if (fhirSchema.base && fhirSchema.type !== "Element") {
|
|
1994
|
+
const baseFs = register.resolveFs(
|
|
1995
|
+
fhirSchema.package_meta,
|
|
1996
|
+
register.ensureSpecializationCanonicalUrl(fhirSchema.base)
|
|
1997
|
+
);
|
|
1998
|
+
if (!baseFs) {
|
|
1999
|
+
throw new Error(
|
|
2000
|
+
`Base resource not found '${fhirSchema.base}' for <${fhirSchema.url}> from ${packageMetaToFhir(fhirSchema.package_meta)}`
|
|
2001
|
+
);
|
|
2068
2002
|
}
|
|
2069
|
-
|
|
2003
|
+
base = mkIdentifier(baseFs);
|
|
2070
2004
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2005
|
+
const fields = mkFields(register, fhirSchema, [], fhirSchema.elements, logger);
|
|
2006
|
+
const nested = mkNestedTypes(register, fhirSchema, logger);
|
|
2007
|
+
const dependencies = extractDependencies(identifier, base, fields, nested);
|
|
2008
|
+
const typeSchema = {
|
|
2009
|
+
identifier,
|
|
2010
|
+
base,
|
|
2011
|
+
fields,
|
|
2012
|
+
nested,
|
|
2013
|
+
description: fhirSchema.description,
|
|
2014
|
+
dependencies
|
|
2015
|
+
};
|
|
2016
|
+
const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger);
|
|
2017
|
+
return [typeSchema, ...bindingSchemas];
|
|
2018
|
+
}
|
|
2019
|
+
async function transformFhirSchema(register, fhirSchema, logger) {
|
|
2020
|
+
const schemas = transformFhirSchemaResource(register, fhirSchema, logger);
|
|
2021
|
+
if (isExtensionSchema(fhirSchema, mkIdentifier(fhirSchema))) {
|
|
2022
|
+
const schema = schemas[0];
|
|
2023
|
+
if (!schema) throw new Error(`Expected schema to be defined`);
|
|
2024
|
+
schema.metadata = {
|
|
2025
|
+
isExtension: true
|
|
2026
|
+
// Mark as extension for file organization
|
|
2027
|
+
};
|
|
2078
2028
|
}
|
|
2079
|
-
|
|
2029
|
+
return schemas;
|
|
2030
|
+
}
|
|
2080
2031
|
|
|
2081
2032
|
// src/fhir-types/hl7-fhir-r4-core/CodeSystem.ts
|
|
2082
2033
|
var isCodeSystem = (resource) => {
|
|
@@ -2305,6 +2256,131 @@ var generateTypeSchemas = async (register, logger) => {
|
|
|
2305
2256
|
}
|
|
2306
2257
|
return fhirSchemas;
|
|
2307
2258
|
};
|
|
2259
|
+
var mutableSelectFields = (schema, selectFields) => {
|
|
2260
|
+
const selectedFields = {};
|
|
2261
|
+
const selectPolimorphic = {};
|
|
2262
|
+
for (const fieldName of selectFields) {
|
|
2263
|
+
const field = schema.fields?.[fieldName];
|
|
2264
|
+
if (!schema.fields || !field) throw new Error(`Field ${fieldName} not found`);
|
|
2265
|
+
if (isChoiceDeclarationField(field)) {
|
|
2266
|
+
if (!selectPolimorphic[fieldName]) selectPolimorphic[fieldName] = {};
|
|
2267
|
+
selectPolimorphic[fieldName].declaration = field.choices;
|
|
2268
|
+
} else if (isChoiceInstanceField(field)) {
|
|
2269
|
+
const choiceName = field.choiceOf;
|
|
2270
|
+
if (!selectPolimorphic[choiceName]) selectPolimorphic[choiceName] = {};
|
|
2271
|
+
selectPolimorphic[choiceName].instances = [...selectPolimorphic[choiceName].instances ?? [], fieldName];
|
|
2272
|
+
} else {
|
|
2273
|
+
selectedFields[fieldName] = field;
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
for (const [choiceName, { declaration, instances }] of Object.entries(selectPolimorphic)) {
|
|
2277
|
+
const choices = instances ?? declaration;
|
|
2278
|
+
assert3(choices);
|
|
2279
|
+
for (const choiceInstanceName of choices) {
|
|
2280
|
+
const field = schema.fields?.[choiceInstanceName];
|
|
2281
|
+
assert3(field);
|
|
2282
|
+
selectedFields[choiceInstanceName] = field;
|
|
2283
|
+
}
|
|
2284
|
+
const decl = schema.fields?.[choiceName];
|
|
2285
|
+
assert3(decl);
|
|
2286
|
+
selectedFields[choiceName] = { ...decl, choices };
|
|
2287
|
+
}
|
|
2288
|
+
schema.fields = selectedFields;
|
|
2289
|
+
};
|
|
2290
|
+
var mutableIgnoreFields = (schema, ignoreFields) => {
|
|
2291
|
+
for (const fieldName of ignoreFields) {
|
|
2292
|
+
const field = schema.fields?.[fieldName];
|
|
2293
|
+
if (!schema.fields || !field) throw new Error(`Field ${fieldName} not found`);
|
|
2294
|
+
if (schema.fields) {
|
|
2295
|
+
if (isChoiceDeclarationField(field)) {
|
|
2296
|
+
for (const choiceName of field.choices) {
|
|
2297
|
+
delete schema.fields[choiceName];
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
if (isChoiceInstanceField(field)) {
|
|
2301
|
+
const choiceDeclaration = schema.fields[field.choiceOf];
|
|
2302
|
+
assert3(isChoiceDeclarationField(choiceDeclaration));
|
|
2303
|
+
choiceDeclaration.choices = choiceDeclaration.choices.filter((c) => c !== fieldName);
|
|
2304
|
+
if (choiceDeclaration.choices.length === 0) {
|
|
2305
|
+
delete schema.fields[field.choiceOf];
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
delete schema.fields[fieldName];
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
};
|
|
2312
|
+
var treeShakeTypeSchema = (schema, rule, _logger) => {
|
|
2313
|
+
schema = structuredClone(schema);
|
|
2314
|
+
if (isPrimitiveTypeSchema(schema) || isValueSetTypeSchema(schema) || isBindingSchema(schema)) return schema;
|
|
2315
|
+
if (rule.selectFields) {
|
|
2316
|
+
if (rule.ignoreFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule");
|
|
2317
|
+
mutableSelectFields(schema, rule.selectFields);
|
|
2318
|
+
}
|
|
2319
|
+
if (rule.ignoreFields) {
|
|
2320
|
+
if (rule.selectFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule");
|
|
2321
|
+
mutableIgnoreFields(schema, rule.ignoreFields);
|
|
2322
|
+
}
|
|
2323
|
+
if (schema.nested) {
|
|
2324
|
+
const usedTypes = /* @__PURE__ */ new Set();
|
|
2325
|
+
const collectUsedNestedTypes = (s) => {
|
|
2326
|
+
Object.values(s.fields ?? {}).filter(isNotChoiceDeclarationField).filter((f) => isNestedIdentifier(f.type)).forEach((f) => {
|
|
2327
|
+
const url = f.type.url;
|
|
2328
|
+
if (!usedTypes.has(url)) {
|
|
2329
|
+
usedTypes.add(url);
|
|
2330
|
+
const nestedTypeDef = schema.nested?.find((f2) => f2.identifier.url === url);
|
|
2331
|
+
assert3(nestedTypeDef);
|
|
2332
|
+
collectUsedNestedTypes(nestedTypeDef);
|
|
2333
|
+
}
|
|
2334
|
+
});
|
|
2335
|
+
};
|
|
2336
|
+
collectUsedNestedTypes(schema);
|
|
2337
|
+
schema.nested = schema.nested.filter((n) => usedTypes.has(n.identifier.url));
|
|
2338
|
+
}
|
|
2339
|
+
schema.dependencies = extractDependencies(schema.identifier, schema.base, schema.fields, schema.nested);
|
|
2340
|
+
return schema;
|
|
2341
|
+
};
|
|
2342
|
+
var treeShake = (tsIndex, treeShake2, { resolutionTree, logger }) => {
|
|
2343
|
+
const focusedSchemas = [];
|
|
2344
|
+
for (const [pkgId, requires] of Object.entries(treeShake2)) {
|
|
2345
|
+
for (const [url, rule] of Object.entries(requires)) {
|
|
2346
|
+
const schema = tsIndex.resolveByUrl(pkgId, url);
|
|
2347
|
+
if (!schema) throw new Error(`Schema not found for ${pkgId} ${url}`);
|
|
2348
|
+
const shaked2 = treeShakeTypeSchema(schema, rule);
|
|
2349
|
+
focusedSchemas.push(shaked2);
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
const collectDeps = (schemas, acc) => {
|
|
2353
|
+
if (schemas.length === 0) return Object.values(acc);
|
|
2354
|
+
for (const schema of schemas) {
|
|
2355
|
+
acc[JSON.stringify(schema.identifier)] = schema;
|
|
2356
|
+
}
|
|
2357
|
+
const newSchemas = [];
|
|
2358
|
+
for (const schema of schemas) {
|
|
2359
|
+
if (isSpecializationTypeSchema(schema)) {
|
|
2360
|
+
if (!schema.dependencies) continue;
|
|
2361
|
+
schema.dependencies.forEach((dep) => {
|
|
2362
|
+
const depSchema = tsIndex.resolve(dep);
|
|
2363
|
+
if (!depSchema)
|
|
2364
|
+
throw new Error(
|
|
2365
|
+
`Dependent schema ${JSON.stringify(dep)} not found for ${JSON.stringify(schema.identifier)}`
|
|
2366
|
+
);
|
|
2367
|
+
const id = JSON.stringify(depSchema.identifier);
|
|
2368
|
+
if (!acc[id]) newSchemas.push(depSchema);
|
|
2369
|
+
});
|
|
2370
|
+
if (schema.nested) {
|
|
2371
|
+
for (const nest of schema.nested) {
|
|
2372
|
+
if (isNestedIdentifier(nest.identifier)) continue;
|
|
2373
|
+
const id = JSON.stringify(nest.identifier);
|
|
2374
|
+
if (!acc[id]) newSchemas.push(nest);
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
return collectDeps(newSchemas, acc);
|
|
2380
|
+
};
|
|
2381
|
+
const shaked = collectDeps(focusedSchemas, {});
|
|
2382
|
+
return mkTypeSchemaIndex(shaked, { resolutionTree, logger });
|
|
2383
|
+
};
|
|
2308
2384
|
|
|
2309
2385
|
// src/api/writer-generator/typescript.ts
|
|
2310
2386
|
var primitiveType2tsType = {
|