@atomic-ehr/codegen 0.0.4-canary.20251218085438.283b979 → 0.0.4-canary.20251222144228.ac2a1d7
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 +27 -1
- package/dist/cli/index.js +10 -10
- package/dist/index.d.ts +8 -6
- package/dist/index.js +948 -856
- 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';
|
|
@@ -21,6 +21,22 @@ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
|
21
21
|
LogLevel2[LogLevel2["SILENT"] = 4] = "SILENT";
|
|
22
22
|
return LogLevel2;
|
|
23
23
|
})(LogLevel || {});
|
|
24
|
+
var parseLogLevel = (level) => {
|
|
25
|
+
switch (level.toUpperCase()) {
|
|
26
|
+
case "DEBUG":
|
|
27
|
+
return 0 /* DEBUG */;
|
|
28
|
+
case "INFO":
|
|
29
|
+
return 1 /* INFO */;
|
|
30
|
+
case "WARN":
|
|
31
|
+
return 2 /* WARN */;
|
|
32
|
+
case "ERROR":
|
|
33
|
+
return 3 /* ERROR */;
|
|
34
|
+
case "SILENT":
|
|
35
|
+
return 4 /* SILENT */;
|
|
36
|
+
default:
|
|
37
|
+
throw new Error(`Invalid log level: ${level}`);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
24
40
|
var CodegenLogger = class _CodegenLogger {
|
|
25
41
|
options;
|
|
26
42
|
dryWarnSet = /* @__PURE__ */ new Set();
|
|
@@ -410,6 +426,10 @@ var isChoiceDeclarationField = (field) => {
|
|
|
410
426
|
if (!field) return false;
|
|
411
427
|
return field.choices !== void 0;
|
|
412
428
|
};
|
|
429
|
+
var isChoiceInstanceField = (field) => {
|
|
430
|
+
if (!field) return false;
|
|
431
|
+
return field.choiceOf !== void 0;
|
|
432
|
+
};
|
|
413
433
|
var enrichValueSet = (vs, packageMeta) => {
|
|
414
434
|
if (!vs.url) throw new Error("ValueSet must have a URL");
|
|
415
435
|
if (!vs.name) throw new Error("ValueSet must have a name");
|
|
@@ -739,676 +759,110 @@ var CSharp = class extends Writer {
|
|
|
739
759
|
fs__default.copyFileSync(sourceFile, destFile);
|
|
740
760
|
}
|
|
741
761
|
};
|
|
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("");
|
|
762
|
+
var groupByPackages = (typeSchemas) => {
|
|
763
|
+
const grouped = {};
|
|
764
|
+
for (const ts of typeSchemas) {
|
|
765
|
+
const pkgName = ts.identifier.package;
|
|
766
|
+
if (!grouped[pkgName]) grouped[pkgName] = [];
|
|
767
|
+
grouped[pkgName].push(ts);
|
|
774
768
|
}
|
|
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;
|
|
769
|
+
for (const [packageName, typeSchemas2] of Object.entries(grouped)) {
|
|
770
|
+
const dict = {};
|
|
771
|
+
for (const ts of typeSchemas2) {
|
|
772
|
+
dict[JSON.stringify(ts.identifier)] = ts;
|
|
816
773
|
}
|
|
774
|
+
const tmp = Object.values(dict);
|
|
775
|
+
tmp.sort((a, b) => a.identifier.name.localeCompare(b.identifier.name));
|
|
776
|
+
grouped[packageName] = tmp;
|
|
817
777
|
}
|
|
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]);
|
|
778
|
+
return grouped;
|
|
779
|
+
};
|
|
780
|
+
var buildDependencyGraph = (schemas) => {
|
|
781
|
+
const nameToMap = {};
|
|
782
|
+
for (const schema of schemas) {
|
|
783
|
+
nameToMap[schema.identifier.name] = schema;
|
|
784
|
+
}
|
|
785
|
+
const graph = {};
|
|
786
|
+
for (const schema of schemas) {
|
|
787
|
+
const name = schema.identifier.name;
|
|
788
|
+
const base = schema.base?.name;
|
|
789
|
+
if (!graph[name]) {
|
|
790
|
+
graph[name] = [];
|
|
834
791
|
}
|
|
835
|
-
if (
|
|
836
|
-
|
|
792
|
+
if (base && nameToMap[base]) {
|
|
793
|
+
graph[name].push(base);
|
|
837
794
|
}
|
|
838
795
|
}
|
|
839
|
-
return
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
const
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
if (
|
|
847
|
-
|
|
848
|
-
}
|
|
849
|
-
|
|
796
|
+
return graph;
|
|
797
|
+
};
|
|
798
|
+
var topologicalSort = (graph) => {
|
|
799
|
+
const sorted = [];
|
|
800
|
+
const visited = {};
|
|
801
|
+
const temp = {};
|
|
802
|
+
const visit = (node) => {
|
|
803
|
+
if (temp[node]) {
|
|
804
|
+
throw new Error(`Graph has cycles ${node}`);
|
|
805
|
+
}
|
|
806
|
+
if (!visited[node]) {
|
|
807
|
+
temp[node] = true;
|
|
808
|
+
for (const neighbor of graph[node] ?? []) {
|
|
809
|
+
visit(neighbor);
|
|
810
|
+
}
|
|
811
|
+
temp[node] = false;
|
|
812
|
+
visited[node] = true;
|
|
813
|
+
sorted.push(node);
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
for (const node in graph) {
|
|
817
|
+
if (!visited[node]) {
|
|
818
|
+
visit(node);
|
|
850
819
|
}
|
|
851
820
|
}
|
|
852
|
-
return
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
const
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
if (
|
|
864
|
-
|
|
865
|
-
} else {
|
|
866
|
-
baseName = element.type;
|
|
821
|
+
return sorted;
|
|
822
|
+
};
|
|
823
|
+
var sortAsDeclarationSequence = (schemas) => {
|
|
824
|
+
const graph = buildDependencyGraph(schemas);
|
|
825
|
+
const sorted = topologicalSort(graph);
|
|
826
|
+
return sorted.map((name) => schemas.find((schema) => schema.identifier.name === name)).filter(Boolean);
|
|
827
|
+
};
|
|
828
|
+
var resourceRelatives = (schemas) => {
|
|
829
|
+
const regularSchemas = schemas.filter((e) => isResourceTypeSchema(e) || isLogicalTypeSchema(e));
|
|
830
|
+
const directPairs = [];
|
|
831
|
+
for (const schema of regularSchemas) {
|
|
832
|
+
if (schema.base) {
|
|
833
|
+
directPairs.push({ parent: schema.base, child: schema.identifier });
|
|
867
834
|
}
|
|
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
835
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
if (nested.base) {
|
|
893
|
-
deps.push(nested.base);
|
|
836
|
+
const allPairs = [...directPairs];
|
|
837
|
+
const findTransitiveRelatives = (parentRef) => {
|
|
838
|
+
const directChildren = directPairs.filter((pair) => pair.parent.name === parentRef.name).map((pair) => pair.child);
|
|
839
|
+
const transitiveChildren = [];
|
|
840
|
+
for (const child of directChildren) {
|
|
841
|
+
transitiveChildren.push(...findTransitiveRelatives(child));
|
|
894
842
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
843
|
+
return [...directChildren, ...transitiveChildren];
|
|
844
|
+
};
|
|
845
|
+
for (const pair of directPairs) {
|
|
846
|
+
const transitiveChildren = findTransitiveRelatives(pair.child);
|
|
847
|
+
for (const transitiveChild of transitiveChildren) {
|
|
848
|
+
if (!directPairs.some((dp) => dp.parent.name === pair.parent.name && dp.child.name === transitiveChild.name)) {
|
|
849
|
+
allPairs.push({ parent: pair.parent, child: transitiveChild });
|
|
901
850
|
}
|
|
902
851
|
}
|
|
903
852
|
}
|
|
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;
|
|
853
|
+
return allPairs;
|
|
854
|
+
};
|
|
855
|
+
var mkTypeSchemaIndex = (schemas, { resolutionTree, logger }) => {
|
|
856
|
+
const index = {};
|
|
857
|
+
const append = (schema) => {
|
|
858
|
+
const url = schema.identifier.url;
|
|
859
|
+
const pkg = schema.identifier.package;
|
|
860
|
+
if (!index[url]) index[url] = {};
|
|
861
|
+
if (index[url][schema.identifier.package] && pkg !== "shared") {
|
|
862
|
+
const r1 = JSON.stringify(schema.identifier, void 0, 2);
|
|
863
|
+
const r2 = JSON.stringify(index[url][pkg]?.identifier, void 0, 2);
|
|
864
|
+
if (r1 !== r2) throw new Error(`Duplicate schema: ${r1} and ${r2}`);
|
|
865
|
+
return;
|
|
1412
866
|
}
|
|
1413
867
|
index[url][pkg] = schema;
|
|
1414
868
|
};
|
|
@@ -1804,7 +1258,7 @@ var Python = class extends Writer {
|
|
|
1804
1258
|
return names;
|
|
1805
1259
|
}
|
|
1806
1260
|
shouldImportResourceFamily(resource) {
|
|
1807
|
-
|
|
1261
|
+
assert3(this.tsIndex !== void 0);
|
|
1808
1262
|
return resource.identifier.kind === "resource" && this.tsIndex.resourceChildren(resource.identifier).length > 0;
|
|
1809
1263
|
}
|
|
1810
1264
|
generateExportsDeclaration(packageComplexTypes, allResourceNames) {
|
|
@@ -1839,244 +1293,757 @@ var Python = class extends Writer {
|
|
|
1839
1293
|
});
|
|
1840
1294
|
this.line();
|
|
1841
1295
|
}
|
|
1842
|
-
getSuperClasses(schema) {
|
|
1843
|
-
return [...schema.base ? [schema.base.name] : [], ...injectSuperClasses(schema.identifier.name)];
|
|
1296
|
+
getSuperClasses(schema) {
|
|
1297
|
+
return [...schema.base ? [schema.base.name] : [], ...injectSuperClasses(schema.identifier.name)];
|
|
1298
|
+
}
|
|
1299
|
+
generateClassBody(schema) {
|
|
1300
|
+
this.generateModelConfig();
|
|
1301
|
+
if (!schema.fields) {
|
|
1302
|
+
this.line("pass");
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
if (schema.identifier.kind === "resource") {
|
|
1306
|
+
this.generateResourceTypeField(schema);
|
|
1307
|
+
}
|
|
1308
|
+
this.generateFields(schema);
|
|
1309
|
+
if (schema.identifier.kind === "resource") {
|
|
1310
|
+
this.generateResourceMethods(schema);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
generateModelConfig() {
|
|
1314
|
+
const extraMode = this.opts.allowExtraFields ? "allow" : "forbid";
|
|
1315
|
+
this.line(`model_config = ConfigDict(validate_by_name=True, serialize_by_alias=True, extra="${extraMode}")`);
|
|
1316
|
+
}
|
|
1317
|
+
generateResourceTypeField(schema) {
|
|
1318
|
+
this.line(`${this.nameFormatFunction("resourceType")}: str = Field(`);
|
|
1319
|
+
this.indentBlock(() => {
|
|
1320
|
+
this.line(`default='${schema.identifier.name}',`);
|
|
1321
|
+
this.line(`alias='resourceType',`);
|
|
1322
|
+
this.line(`serialization_alias='resourceType',`);
|
|
1323
|
+
this.line("frozen=True,");
|
|
1324
|
+
this.line(`pattern='${schema.identifier.name}'`);
|
|
1325
|
+
});
|
|
1326
|
+
this.line(")");
|
|
1327
|
+
}
|
|
1328
|
+
generateFields(schema) {
|
|
1329
|
+
const sortedFields = Object.entries(schema.fields ?? []).sort(([a], [b]) => a.localeCompare(b));
|
|
1330
|
+
for (const [fieldName, field] of sortedFields) {
|
|
1331
|
+
if ("choices" in field && field.choices) continue;
|
|
1332
|
+
const fieldInfo = this.buildFieldInfo(fieldName, field);
|
|
1333
|
+
this.line(`${fieldInfo.name}: ${fieldInfo.type}${fieldInfo.defaultValue}`);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
buildFieldInfo(fieldName, field) {
|
|
1337
|
+
const pyFieldName = fixReservedWords(this.nameFormatFunction(fieldName));
|
|
1338
|
+
const fieldType = this.determineFieldType(field);
|
|
1339
|
+
const defaultValue = this.getFieldDefaultValue(field, fieldName);
|
|
1340
|
+
return {
|
|
1341
|
+
name: pyFieldName,
|
|
1342
|
+
type: fieldType,
|
|
1343
|
+
defaultValue
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
determineFieldType(field) {
|
|
1347
|
+
let fieldType = field ? this.getBaseFieldType(field) : "";
|
|
1348
|
+
if ("enum" in field && field.enum) {
|
|
1349
|
+
const s = field.enum.map((e) => `"${e}"`).join(", ");
|
|
1350
|
+
fieldType = `Literal[${s}]`;
|
|
1351
|
+
}
|
|
1352
|
+
if (field.array) {
|
|
1353
|
+
fieldType = `PyList[${fieldType}]`;
|
|
1354
|
+
}
|
|
1355
|
+
if (!field.required) {
|
|
1356
|
+
fieldType = `${fieldType} | None`;
|
|
1357
|
+
}
|
|
1358
|
+
return fieldType;
|
|
1359
|
+
}
|
|
1360
|
+
getBaseFieldType(field) {
|
|
1361
|
+
if ("type" in field && field.type.kind === "resource") return `${field.type.name}Family`;
|
|
1362
|
+
if ("type" in field && field.type.kind === "nested") return deriveResourceName(field.type);
|
|
1363
|
+
if ("type" in field && field.type.kind === "primitive-type")
|
|
1364
|
+
return PRIMITIVE_TYPE_MAP2[field.type.name] ?? "str";
|
|
1365
|
+
return "type" in field ? field.type.name : "";
|
|
1366
|
+
}
|
|
1367
|
+
getFieldDefaultValue(field, fieldName) {
|
|
1368
|
+
const aliasSpec = `alias="${fieldName}", serialization_alias="${fieldName}"`;
|
|
1369
|
+
if (!field.required) {
|
|
1370
|
+
return ` = Field(None, ${aliasSpec})`;
|
|
1371
|
+
}
|
|
1372
|
+
return ` = Field(${aliasSpec})`;
|
|
1373
|
+
}
|
|
1374
|
+
generateResourceMethods(schema) {
|
|
1375
|
+
const className = schema.identifier.name.toString();
|
|
1376
|
+
this.line();
|
|
1377
|
+
this.line("def to_json(self, indent: int | None = None) -> str:");
|
|
1378
|
+
this.line(" return self.model_dump_json(exclude_unset=True, exclude_none=True, indent=indent)");
|
|
1379
|
+
this.line();
|
|
1380
|
+
this.line("@classmethod");
|
|
1381
|
+
this.line(`def from_json(cls, json: str) -> ${className}:`);
|
|
1382
|
+
this.line(" return cls.model_validate_json(json)");
|
|
1383
|
+
}
|
|
1384
|
+
generateNestedTypes(schema) {
|
|
1385
|
+
if (!schema.nested) return;
|
|
1386
|
+
this.line();
|
|
1387
|
+
for (const subtype of schema.nested) {
|
|
1388
|
+
this.generateType(subtype);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
generateDefaultImports() {
|
|
1392
|
+
this.pyImportFrom("__future__", "annotations");
|
|
1393
|
+
this.pyImportFrom("pydantic", "BaseModel", "ConfigDict", "Field", "PositiveInt");
|
|
1394
|
+
this.pyImportFrom("typing", "List as PyList", "Literal");
|
|
1395
|
+
}
|
|
1396
|
+
generateDependenciesImports(schema) {
|
|
1397
|
+
if (!schema.dependencies || schema.dependencies.length === 0) return;
|
|
1398
|
+
this.importComplexTypeDependencies(schema.dependencies);
|
|
1399
|
+
this.importResourceDependencies(schema.dependencies);
|
|
1400
|
+
}
|
|
1401
|
+
importComplexTypeDependencies(dependencies) {
|
|
1402
|
+
const complexTypeDeps = dependencies.filter((dep) => dep.kind === "complex-type");
|
|
1403
|
+
const depsByPackage = this.groupDependenciesByPackage(complexTypeDeps);
|
|
1404
|
+
for (const [pyPackage, names] of Object.entries(depsByPackage)) {
|
|
1405
|
+
this.pyImportFrom(pyPackage, ...names.sort());
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
importResourceDependencies(dependencies) {
|
|
1409
|
+
const resourceDeps = dependencies.filter((dep) => dep.kind === "resource");
|
|
1410
|
+
for (const dep of resourceDeps) {
|
|
1411
|
+
this.pyImportType(dep);
|
|
1412
|
+
const familyName = `${pascalCase(dep.name)}Family`;
|
|
1413
|
+
const familyPackage = `${this.pyFhirPackage(dep)}.resource_families`;
|
|
1414
|
+
this.pyImportFrom(familyPackage, familyName);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
groupDependenciesByPackage(dependencies) {
|
|
1418
|
+
const grouped = {};
|
|
1419
|
+
for (const dep of dependencies) {
|
|
1420
|
+
const pyPackage = this.pyPackage(dep);
|
|
1421
|
+
if (!grouped[pyPackage]) {
|
|
1422
|
+
grouped[pyPackage] = [];
|
|
1423
|
+
}
|
|
1424
|
+
grouped[pyPackage].push(dep.name);
|
|
1425
|
+
}
|
|
1426
|
+
return grouped;
|
|
1427
|
+
}
|
|
1428
|
+
pyImportFrom(pyPackage, ...entities) {
|
|
1429
|
+
const oneLine = `from ${pyPackage} import ${entities.join(", ")}`;
|
|
1430
|
+
if (this.shouldUseSingleLineImport(oneLine, entities)) {
|
|
1431
|
+
this.line(oneLine);
|
|
1432
|
+
} else {
|
|
1433
|
+
this.writeMultiLineImport(pyPackage, entities);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
shouldUseSingleLineImport(oneLine, entities) {
|
|
1437
|
+
return oneLine.length <= MAX_IMPORT_LINE_LENGTH || entities.length === 1;
|
|
1438
|
+
}
|
|
1439
|
+
writeMultiLineImport(pyPackage, entities) {
|
|
1440
|
+
this.line(`from ${pyPackage} import (\\`);
|
|
1441
|
+
this.indentBlock(() => {
|
|
1442
|
+
const remaining = [...entities];
|
|
1443
|
+
while (remaining.length > 0) {
|
|
1444
|
+
const line = this.buildImportLine(remaining, MAX_IMPORT_LINE_LENGTH);
|
|
1445
|
+
this.line(line);
|
|
1446
|
+
}
|
|
1447
|
+
});
|
|
1448
|
+
this.line(")");
|
|
1449
|
+
}
|
|
1450
|
+
pyImportType(identifier) {
|
|
1451
|
+
this.pyImportFrom(this.pyPackage(identifier), pascalCase(identifier.name));
|
|
1452
|
+
}
|
|
1453
|
+
generateResourceFamilies(packageResources) {
|
|
1454
|
+
assert3(this.tsIndex !== void 0);
|
|
1455
|
+
const packages = (
|
|
1456
|
+
//this.helper.getPackages(packageResources, this.opts.rootPackageName);
|
|
1457
|
+
Object.keys(groupByPackages(packageResources)).map(
|
|
1458
|
+
(pkgName) => `${this.opts.rootPackageName}.${pkgName.replaceAll(".", "_")}`
|
|
1459
|
+
)
|
|
1460
|
+
);
|
|
1461
|
+
const families = {};
|
|
1462
|
+
for (const resource of this.tsIndex.collectResources()) {
|
|
1463
|
+
const children = this.tsIndex.resourceChildren(resource.identifier).map((c) => c.name);
|
|
1464
|
+
if (children.length > 0) {
|
|
1465
|
+
const familyName = `${resource.identifier.name}Family`;
|
|
1466
|
+
families[familyName] = children;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
const exportList = Object.keys(families);
|
|
1470
|
+
if (exportList.length === 0) return;
|
|
1471
|
+
this.buildResourceFamiliesFile(packages, families, exportList);
|
|
1472
|
+
}
|
|
1473
|
+
buildResourceFamiliesFile(packages, families, exportList) {
|
|
1474
|
+
this.cat("resource_families.py", () => {
|
|
1475
|
+
this.generateDisclaimer();
|
|
1476
|
+
this.includeResourceFamilyValidator();
|
|
1477
|
+
this.line();
|
|
1478
|
+
this.generateFamilyDefinitions(packages, families);
|
|
1479
|
+
this.generateFamilyExports(exportList);
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
includeResourceFamilyValidator() {
|
|
1483
|
+
const content = fs__default.readFileSync(resolvePyAssets("resource_family_validator.py"), "utf-8");
|
|
1484
|
+
this.line(content);
|
|
1485
|
+
}
|
|
1486
|
+
generateFamilyDefinitions(packages, families) {
|
|
1487
|
+
this.line(`packages = [${packages.map((p) => `'${p}'`).join(", ")}]`);
|
|
1488
|
+
this.line();
|
|
1489
|
+
for (const [familyName, resources] of Object.entries(families)) {
|
|
1490
|
+
this.generateFamilyDefinition(familyName, resources);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
generateFamilyDefinition(familyName, resources) {
|
|
1494
|
+
const listName = `${familyName}_resources`;
|
|
1495
|
+
this.line(`${listName} = [${resources.map((r) => `'${r}'`).join(", ")}]`);
|
|
1496
|
+
this.line();
|
|
1497
|
+
this.line(`def validate_and_downcast_${familyName}(v: Any) -> Any:`);
|
|
1498
|
+
this.line(` return validate_and_downcast(v, packages, ${listName})`);
|
|
1499
|
+
this.line();
|
|
1500
|
+
this.line(`type ${familyName} = Annotated[Any, BeforeValidator(validate_and_downcast_${familyName})]`);
|
|
1501
|
+
this.line();
|
|
1502
|
+
}
|
|
1503
|
+
generateFamilyExports(exportList) {
|
|
1504
|
+
this.line(`__all__ = [${exportList.map((e) => `'${e}'`).join(", ")}]`);
|
|
1844
1505
|
}
|
|
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
|
-
}
|
|
1506
|
+
buildPyPackageName(packageName) {
|
|
1507
|
+
const parts = packageName ? [snakeCase(packageName)] : [""];
|
|
1508
|
+
return parts.join(".");
|
|
1858
1509
|
}
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
this.line(`model_config = ConfigDict(validate_by_name=True, serialize_by_alias=True, extra="${extraMode}")`);
|
|
1510
|
+
pyFhirPackage(identifier) {
|
|
1511
|
+
return this.pyFhirPackageByName(identifier.package);
|
|
1862
1512
|
}
|
|
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(")");
|
|
1513
|
+
pyFhirPackageByName(name) {
|
|
1514
|
+
return [this.opts.rootPackageName, this.buildPyPackageName(name)].join(".");
|
|
1873
1515
|
}
|
|
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}`);
|
|
1516
|
+
pyPackage(identifier) {
|
|
1517
|
+
if (identifier.kind === "complex-type") {
|
|
1518
|
+
return `${this.pyFhirPackage(identifier)}.base`;
|
|
1880
1519
|
}
|
|
1520
|
+
if (identifier.kind === "resource") {
|
|
1521
|
+
return [this.pyFhirPackage(identifier), snakeCase(identifier.name)].join(".");
|
|
1522
|
+
}
|
|
1523
|
+
return this.pyFhirPackage(identifier);
|
|
1881
1524
|
}
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
defaultValue
|
|
1890
|
-
};
|
|
1525
|
+
getFieldFormatFunction(format) {
|
|
1526
|
+
if (!AVAILABLE_STRING_FORMATS[format]) {
|
|
1527
|
+
this.logger()?.warn(`Unknown field format '${format}'. Defaulting to SnakeCase.`);
|
|
1528
|
+
this.logger()?.warn(`Supported formats: ${Object.keys(AVAILABLE_STRING_FORMATS).join(", ")}`);
|
|
1529
|
+
return snakeCase;
|
|
1530
|
+
}
|
|
1531
|
+
return AVAILABLE_STRING_FORMATS[format];
|
|
1891
1532
|
}
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1533
|
+
};
|
|
1534
|
+
|
|
1535
|
+
// src/typeschema/core/identifier.ts
|
|
1536
|
+
function dropVersionFromUrl(url) {
|
|
1537
|
+
const baseUrl = url.split("|")[0];
|
|
1538
|
+
return baseUrl ? baseUrl : url;
|
|
1539
|
+
}
|
|
1540
|
+
function getVersionFromUrl(url) {
|
|
1541
|
+
const version = url.split("|")[1];
|
|
1542
|
+
return version;
|
|
1543
|
+
}
|
|
1544
|
+
function determineKind(fhirSchema) {
|
|
1545
|
+
if (fhirSchema.derivation === "constraint") return "profile";
|
|
1546
|
+
if (fhirSchema.kind === "primitive-type") return "primitive-type";
|
|
1547
|
+
if (fhirSchema.kind === "complex-type") return "complex-type";
|
|
1548
|
+
if (fhirSchema.kind === "resource") return "resource";
|
|
1549
|
+
if (fhirSchema.kind === "logical") return "logical";
|
|
1550
|
+
return "resource";
|
|
1551
|
+
}
|
|
1552
|
+
function mkIdentifier(fhirSchema) {
|
|
1553
|
+
return {
|
|
1554
|
+
kind: determineKind(fhirSchema),
|
|
1555
|
+
package: fhirSchema.package_meta.name,
|
|
1556
|
+
version: fhirSchema.package_meta.version,
|
|
1557
|
+
name: fhirSchema.name,
|
|
1558
|
+
url: fhirSchema.url
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
var getValueSetName = (url) => {
|
|
1562
|
+
const urlParts = url.split("/");
|
|
1563
|
+
const lastSegment = urlParts[urlParts.length - 1];
|
|
1564
|
+
if (lastSegment && lastSegment.length > 0) {
|
|
1565
|
+
return lastSegment.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
1566
|
+
}
|
|
1567
|
+
return url;
|
|
1568
|
+
};
|
|
1569
|
+
function mkValueSetIdentifierByUrl(register, pkg, fullValueSetUrl) {
|
|
1570
|
+
const valueSetUrl = dropVersionFromUrl(fullValueSetUrl);
|
|
1571
|
+
const valueSetNameFallback = getValueSetName(valueSetUrl);
|
|
1572
|
+
const valuesSetFallback = {
|
|
1573
|
+
package_meta: {
|
|
1574
|
+
name: "missing_valuesets",
|
|
1575
|
+
version: getVersionFromUrl(valueSetUrl) || "0.0.0"
|
|
1576
|
+
},
|
|
1577
|
+
id: fullValueSetUrl};
|
|
1578
|
+
const valueSet = register.resolveVs(pkg, valueSetUrl) || valuesSetFallback;
|
|
1579
|
+
const valueSetName = valueSet?.id && !/^[a-zA-Z0-9_-]{20,}$/.test(valueSet.id) ? valueSet.id : valueSetNameFallback;
|
|
1580
|
+
return {
|
|
1581
|
+
kind: "value-set",
|
|
1582
|
+
package: valueSet.package_meta.name,
|
|
1583
|
+
version: valueSet.package_meta.version,
|
|
1584
|
+
name: valueSetName,
|
|
1585
|
+
url: valueSetUrl
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
function mkBindingIdentifier(fhirSchema, path, bindingName) {
|
|
1589
|
+
const pathStr = path.join(".");
|
|
1590
|
+
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`];
|
|
1591
|
+
return {
|
|
1592
|
+
kind: "binding",
|
|
1593
|
+
package: pkg.name,
|
|
1594
|
+
version: pkg.version,
|
|
1595
|
+
name,
|
|
1596
|
+
url
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
// src/typeschema/core/nested-types.ts
|
|
1601
|
+
function mkNestedIdentifier(register, fhirSchema, path, logger) {
|
|
1602
|
+
const nestedTypeOrigins = {};
|
|
1603
|
+
if (fhirSchema.derivation === "constraint") {
|
|
1604
|
+
const specializations = register.resolveFsSpecializations(fhirSchema.package_meta, fhirSchema.url);
|
|
1605
|
+
const nestedTypeGenealogy = specializations.map((fs5) => mkNestedTypes(register, fs5, logger)).filter((e) => e !== void 0).flat();
|
|
1606
|
+
for (const nt of nestedTypeGenealogy.reverse()) {
|
|
1607
|
+
nestedTypeOrigins[nt.identifier.name] = nt.identifier.url;
|
|
1897
1608
|
}
|
|
1898
|
-
|
|
1899
|
-
|
|
1609
|
+
}
|
|
1610
|
+
const nestedName = path.join(".");
|
|
1611
|
+
const url = nestedTypeOrigins[nestedName] ?? `${fhirSchema.url}#${nestedName}`;
|
|
1612
|
+
return {
|
|
1613
|
+
kind: "nested",
|
|
1614
|
+
package: fhirSchema.package_meta.name,
|
|
1615
|
+
version: fhirSchema.package_meta.version,
|
|
1616
|
+
name: nestedName,
|
|
1617
|
+
url
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
function collectNestedElements(fhirSchema, parentPath, elements) {
|
|
1621
|
+
const nested = [];
|
|
1622
|
+
for (const [key, element] of Object.entries(elements)) {
|
|
1623
|
+
const path = [...parentPath, key];
|
|
1624
|
+
if (isNestedElement(element)) {
|
|
1625
|
+
nested.push([path, element]);
|
|
1900
1626
|
}
|
|
1901
|
-
if (
|
|
1902
|
-
|
|
1627
|
+
if (element.elements) {
|
|
1628
|
+
nested.push(...collectNestedElements(fhirSchema, path, element.elements));
|
|
1903
1629
|
}
|
|
1904
|
-
return fieldType;
|
|
1905
1630
|
}
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1631
|
+
return nested;
|
|
1632
|
+
}
|
|
1633
|
+
function transformNestedElements(register, fhirSchema, parentPath, elements, logger) {
|
|
1634
|
+
const fields = {};
|
|
1635
|
+
for (const [key, _element] of Object.entries(elements)) {
|
|
1636
|
+
const path = [...parentPath, key];
|
|
1637
|
+
const elemSnapshot = register.resolveElementSnapshot(fhirSchema, path);
|
|
1638
|
+
if (isNestedElement(elemSnapshot)) {
|
|
1639
|
+
fields[key] = mkNestedField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1640
|
+
} else {
|
|
1641
|
+
fields[key] = mkField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1917
1642
|
}
|
|
1918
|
-
return ` = Field(${aliasSpec})`;
|
|
1919
1643
|
}
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1644
|
+
return fields;
|
|
1645
|
+
}
|
|
1646
|
+
function mkNestedTypes(register, fhirSchema, logger) {
|
|
1647
|
+
if (!fhirSchema.elements) return void 0;
|
|
1648
|
+
const nested = collectNestedElements(fhirSchema, [], fhirSchema.elements).filter(
|
|
1649
|
+
([_, element]) => element.elements && Object.keys(element.elements).length > 0
|
|
1650
|
+
);
|
|
1651
|
+
const nestedTypes = [];
|
|
1652
|
+
for (const [path, element] of nested) {
|
|
1653
|
+
const identifier = mkNestedIdentifier(register, fhirSchema, path, logger);
|
|
1654
|
+
let baseName;
|
|
1655
|
+
if (element.type === "BackboneElement" || !element.type) {
|
|
1656
|
+
baseName = "BackboneElement";
|
|
1657
|
+
} else {
|
|
1658
|
+
baseName = element.type;
|
|
1659
|
+
}
|
|
1660
|
+
const baseUrl = register.ensureSpecializationCanonicalUrl(baseName);
|
|
1661
|
+
const baseFs = register.resolveFs(fhirSchema.package_meta, baseUrl);
|
|
1662
|
+
if (!baseFs) throw new Error(`Could not resolve base type ${baseName}`);
|
|
1663
|
+
const base = {
|
|
1664
|
+
kind: "complex-type",
|
|
1665
|
+
package: baseFs.package_meta.name,
|
|
1666
|
+
version: baseFs.package_meta.version,
|
|
1667
|
+
name: baseName,
|
|
1668
|
+
url: baseUrl
|
|
1669
|
+
};
|
|
1670
|
+
const fields = transformNestedElements(register, fhirSchema, path, element.elements ?? {}, logger);
|
|
1671
|
+
const nestedType = {
|
|
1672
|
+
identifier,
|
|
1673
|
+
base,
|
|
1674
|
+
fields
|
|
1675
|
+
};
|
|
1676
|
+
nestedTypes.push(nestedType);
|
|
1929
1677
|
}
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1678
|
+
nestedTypes.sort((a, b) => a.identifier.url.localeCompare(b.identifier.url));
|
|
1679
|
+
return nestedTypes.length === 0 ? void 0 : nestedTypes;
|
|
1680
|
+
}
|
|
1681
|
+
function extractNestedDependencies(nestedTypes) {
|
|
1682
|
+
const deps = [];
|
|
1683
|
+
for (const nested of nestedTypes) {
|
|
1684
|
+
if (nested.base) {
|
|
1685
|
+
deps.push(nested.base);
|
|
1686
|
+
}
|
|
1687
|
+
for (const field of Object.values(nested.fields || {})) {
|
|
1688
|
+
if ("type" in field && field.type) {
|
|
1689
|
+
deps.push(field.type);
|
|
1690
|
+
}
|
|
1691
|
+
if ("binding" in field && field.binding) {
|
|
1692
|
+
deps.push(field.binding);
|
|
1693
|
+
}
|
|
1935
1694
|
}
|
|
1936
1695
|
}
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
const
|
|
1950
|
-
|
|
1951
|
-
|
|
1696
|
+
return deps;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
// src/typeschema/core/field-builder.ts
|
|
1700
|
+
function isRequired(register, fhirSchema, path) {
|
|
1701
|
+
const fieldName = path[path.length - 1];
|
|
1702
|
+
if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
|
|
1703
|
+
const parentPath = path.slice(0, -1);
|
|
1704
|
+
const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs5) => {
|
|
1705
|
+
if (parentPath.length === 0) return fs5.required || [];
|
|
1706
|
+
if (!fs5.elements) return [];
|
|
1707
|
+
let elem = fs5;
|
|
1708
|
+
for (const k of parentPath) {
|
|
1709
|
+
elem = elem?.elements?.[k];
|
|
1710
|
+
}
|
|
1711
|
+
return elem?.required || [];
|
|
1712
|
+
});
|
|
1713
|
+
return new Set(requires).has(fieldName);
|
|
1714
|
+
}
|
|
1715
|
+
function isExcluded(register, fhirSchema, path) {
|
|
1716
|
+
const fieldName = path[path.length - 1];
|
|
1717
|
+
if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
|
|
1718
|
+
const parentPath = path.slice(0, -1);
|
|
1719
|
+
const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs5) => {
|
|
1720
|
+
if (parentPath.length === 0) return fs5.excluded || [];
|
|
1721
|
+
if (!fs5.elements) return [];
|
|
1722
|
+
let elem = fs5;
|
|
1723
|
+
for (const k of parentPath) {
|
|
1724
|
+
elem = elem?.elements?.[k];
|
|
1952
1725
|
}
|
|
1726
|
+
return elem?.excluded || [];
|
|
1727
|
+
});
|
|
1728
|
+
return new Set(requires).has(fieldName);
|
|
1729
|
+
}
|
|
1730
|
+
var buildReferences = (register, fhirSchema, element) => {
|
|
1731
|
+
if (!element.refers) return void 0;
|
|
1732
|
+
return element.refers.map((ref) => {
|
|
1733
|
+
const curl = register.ensureSpecializationCanonicalUrl(ref);
|
|
1734
|
+
const fs5 = register.resolveFs(fhirSchema.package_meta, curl);
|
|
1735
|
+
if (!fs5) throw new Error(`Failed to resolve fs for ${curl}`);
|
|
1736
|
+
return mkIdentifier(fs5);
|
|
1737
|
+
});
|
|
1738
|
+
};
|
|
1739
|
+
function buildFieldType(register, fhirSchema, path, element, logger) {
|
|
1740
|
+
if (element.elementReference) {
|
|
1741
|
+
const refPath = element.elementReference.slice(1).filter((_, i) => i % 2 === 1);
|
|
1742
|
+
return mkNestedIdentifier(register, fhirSchema, refPath, logger);
|
|
1743
|
+
} else if (element.type) {
|
|
1744
|
+
const url = register.ensureSpecializationCanonicalUrl(element.type);
|
|
1745
|
+
const fieldFs = register.resolveFs(fhirSchema.package_meta, url);
|
|
1746
|
+
if (!fieldFs)
|
|
1747
|
+
throw new Error(
|
|
1748
|
+
`Could not resolve field type: '${element.type}' (from '${fhirSchema.url}' in '${packageMetaToFhir(fhirSchema.package_meta)}')`
|
|
1749
|
+
);
|
|
1750
|
+
return mkIdentifier(fieldFs);
|
|
1751
|
+
} else if (element.choices) {
|
|
1752
|
+
return void 0;
|
|
1753
|
+
} else if (fhirSchema.derivation === "constraint") {
|
|
1754
|
+
return void 0;
|
|
1755
|
+
} else {
|
|
1756
|
+
logger?.error(
|
|
1757
|
+
`Can't recognize element type '${fhirSchema.url}' (${fhirSchema.derivation}) at '${path.join(".")}': ${JSON.stringify(element, void 0, 2)}`
|
|
1758
|
+
);
|
|
1759
|
+
throw new Error(`Unrecognized element type`);
|
|
1953
1760
|
}
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1761
|
+
}
|
|
1762
|
+
var mkField = (register, fhirSchema, path, element, logger) => {
|
|
1763
|
+
let binding;
|
|
1764
|
+
let enumValues;
|
|
1765
|
+
if (element.binding) {
|
|
1766
|
+
binding = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
|
|
1767
|
+
if (element.binding.strength === "required" && element.type === "code") {
|
|
1768
|
+
enumValues = buildEnum(register, fhirSchema, element, logger);
|
|
1961
1769
|
}
|
|
1962
1770
|
}
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1771
|
+
const fieldType = buildFieldType(register, fhirSchema, path, element, logger);
|
|
1772
|
+
if (!fieldType)
|
|
1773
|
+
logger?.warn(`Field type not found for '${fhirSchema.url}#${path.join(".")}' (${fhirSchema.derivation})`);
|
|
1774
|
+
return {
|
|
1775
|
+
type: fieldType,
|
|
1776
|
+
required: isRequired(register, fhirSchema, path),
|
|
1777
|
+
excluded: isExcluded(register, fhirSchema, path),
|
|
1778
|
+
reference: buildReferences(register, fhirSchema, element),
|
|
1779
|
+
array: element.array || false,
|
|
1780
|
+
min: element.min,
|
|
1781
|
+
max: element.max,
|
|
1782
|
+
choices: element.choices,
|
|
1783
|
+
choiceOf: element.choiceOf,
|
|
1784
|
+
binding,
|
|
1785
|
+
enum: enumValues
|
|
1786
|
+
};
|
|
1787
|
+
};
|
|
1788
|
+
function isNestedElement(element) {
|
|
1789
|
+
const isBackbone = element.type === "BackboneElement";
|
|
1790
|
+
const isElement = element.type === "Element" && element.elements !== void 0 && Object.keys(element.elements).length > 0;
|
|
1791
|
+
const elementsWithoutType = element.type === void 0 && element.choiceOf === void 0 && element.elements !== void 0 && Object.keys(element.elements).length > 0;
|
|
1792
|
+
return isBackbone || isElement || elementsWithoutType;
|
|
1793
|
+
}
|
|
1794
|
+
function mkNestedField(register, fhirSchema, path, element, logger) {
|
|
1795
|
+
const nestedIdentifier = mkNestedIdentifier(register, fhirSchema, path, logger);
|
|
1796
|
+
return {
|
|
1797
|
+
type: nestedIdentifier,
|
|
1798
|
+
array: element.array || false,
|
|
1799
|
+
required: isRequired(register, fhirSchema, path),
|
|
1800
|
+
excluded: isExcluded(register, fhirSchema, path)
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
// src/typeschema/core/binding.ts
|
|
1805
|
+
function extractValueSetConceptsByUrl(register, pkg, valueSetUrl, logger) {
|
|
1806
|
+
const cleanUrl = dropVersionFromUrl(valueSetUrl) || valueSetUrl;
|
|
1807
|
+
const valueSet = register.resolveVs(pkg, cleanUrl);
|
|
1808
|
+
if (!valueSet) return void 0;
|
|
1809
|
+
return extractValueSetConcepts(register, valueSet);
|
|
1810
|
+
}
|
|
1811
|
+
function extractValueSetConcepts(register, valueSet, _logger) {
|
|
1812
|
+
if (valueSet.expansion?.contains) {
|
|
1813
|
+
return valueSet.expansion.contains.filter((item) => item.code !== void 0).map((item) => {
|
|
1814
|
+
assert3(item.code);
|
|
1815
|
+
return {
|
|
1816
|
+
code: item.code,
|
|
1817
|
+
display: item.display,
|
|
1818
|
+
system: item.system
|
|
1819
|
+
};
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
const concepts = [];
|
|
1823
|
+
if (valueSet.compose?.include) {
|
|
1824
|
+
for (const include of valueSet.compose.include) {
|
|
1825
|
+
if (include.concept) {
|
|
1826
|
+
for (const concept of include.concept) {
|
|
1827
|
+
concepts.push({
|
|
1828
|
+
system: include.system,
|
|
1829
|
+
code: concept.code,
|
|
1830
|
+
display: concept.display
|
|
1831
|
+
});
|
|
1832
|
+
}
|
|
1833
|
+
} else if (include.system && !include.filter) {
|
|
1834
|
+
try {
|
|
1835
|
+
const codeSystem = register.resolveAny(include.system);
|
|
1836
|
+
if (codeSystem?.concept) {
|
|
1837
|
+
const extractConcepts = (conceptList, system) => {
|
|
1838
|
+
for (const concept of conceptList) {
|
|
1839
|
+
concepts.push({
|
|
1840
|
+
system,
|
|
1841
|
+
code: concept.code,
|
|
1842
|
+
display: concept.display
|
|
1843
|
+
});
|
|
1844
|
+
if (concept.concept) {
|
|
1845
|
+
extractConcepts(concept.concept, system);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
};
|
|
1849
|
+
extractConcepts(codeSystem.concept, include.system);
|
|
1850
|
+
}
|
|
1851
|
+
} catch {
|
|
1852
|
+
}
|
|
1969
1853
|
}
|
|
1970
|
-
grouped[pyPackage].push(dep.name);
|
|
1971
1854
|
}
|
|
1972
|
-
return grouped;
|
|
1973
1855
|
}
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1856
|
+
return concepts.length > 0 ? concepts : void 0;
|
|
1857
|
+
}
|
|
1858
|
+
var MAX_ENUM_LENGTH = 100;
|
|
1859
|
+
function buildEnum(register, fhirSchema, element, logger) {
|
|
1860
|
+
if (!element.binding) return void 0;
|
|
1861
|
+
const strength = element.binding.strength;
|
|
1862
|
+
const valueSetUrl = element.binding.valueSet;
|
|
1863
|
+
if (!valueSetUrl) return void 0;
|
|
1864
|
+
const shouldGenerateEnum = strength === "required" || strength === "extensible" && (element.type === "code" || element.type === "Coding") || strength === "preferred" && (element.type === "code" || element.type === "Coding");
|
|
1865
|
+
if (!shouldGenerateEnum) return void 0;
|
|
1866
|
+
const concepts = extractValueSetConceptsByUrl(register, fhirSchema.package_meta, valueSetUrl);
|
|
1867
|
+
if (!concepts || concepts.length === 0) return void 0;
|
|
1868
|
+
const codes = concepts.map((c) => c.code).filter((code) => code && typeof code === "string" && code.trim().length > 0);
|
|
1869
|
+
if (codes.length > MAX_ENUM_LENGTH) {
|
|
1870
|
+
logger?.dry_warn(
|
|
1871
|
+
`Value set ${valueSetUrl} has ${codes.length} which is more than ${MAX_ENUM_LENGTH} codes, which may cause issues with code generation.`
|
|
1872
|
+
);
|
|
1873
|
+
return void 0;
|
|
1981
1874
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1875
|
+
return codes.length > 0 ? codes : void 0;
|
|
1876
|
+
}
|
|
1877
|
+
function generateBindingSchema(register, fhirSchema, path, element, logger) {
|
|
1878
|
+
if (!element.binding?.valueSet) return void 0;
|
|
1879
|
+
const identifier = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
|
|
1880
|
+
const fieldType = buildFieldType(register, fhirSchema, path, element, logger);
|
|
1881
|
+
const valueSetIdentifier = mkValueSetIdentifierByUrl(
|
|
1882
|
+
register,
|
|
1883
|
+
fhirSchema.package_meta,
|
|
1884
|
+
element.binding.valueSet
|
|
1885
|
+
);
|
|
1886
|
+
const dependencies = [];
|
|
1887
|
+
if (fieldType) {
|
|
1888
|
+
dependencies.push(fieldType);
|
|
1984
1889
|
}
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1890
|
+
dependencies.push(valueSetIdentifier);
|
|
1891
|
+
const enumValues = buildEnum(register, fhirSchema, element, logger);
|
|
1892
|
+
return {
|
|
1893
|
+
identifier,
|
|
1894
|
+
type: fieldType,
|
|
1895
|
+
valueset: valueSetIdentifier,
|
|
1896
|
+
strength: element.binding.strength,
|
|
1897
|
+
enum: enumValues,
|
|
1898
|
+
dependencies
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
function collectBindingSchemas(register, fhirSchema, logger) {
|
|
1902
|
+
const processedPaths = /* @__PURE__ */ new Set();
|
|
1903
|
+
if (!fhirSchema.elements) return [];
|
|
1904
|
+
const bindings = [];
|
|
1905
|
+
function collectBindings(elements, parentPath) {
|
|
1906
|
+
for (const [key, element] of Object.entries(elements)) {
|
|
1907
|
+
const path = [...parentPath, key];
|
|
1908
|
+
const pathKey = path.join(".");
|
|
1909
|
+
if (processedPaths.has(pathKey)) continue;
|
|
1910
|
+
processedPaths.add(pathKey);
|
|
1911
|
+
if (element.binding) {
|
|
1912
|
+
const binding = generateBindingSchema(register, fhirSchema, path, element, logger);
|
|
1913
|
+
if (binding) {
|
|
1914
|
+
bindings.push(binding);
|
|
1915
|
+
}
|
|
1992
1916
|
}
|
|
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;
|
|
1917
|
+
if (element.elements) {
|
|
1918
|
+
collectBindings(element.elements, path);
|
|
2013
1919
|
}
|
|
2014
1920
|
}
|
|
2015
|
-
const exportList = Object.keys(families);
|
|
2016
|
-
if (exportList.length === 0) return;
|
|
2017
|
-
this.buildResourceFamiliesFile(packages, families, exportList);
|
|
2018
1921
|
}
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
1922
|
+
collectBindings(fhirSchema.elements, []);
|
|
1923
|
+
bindings.sort((a, b) => a.identifier.name.localeCompare(b.identifier.name));
|
|
1924
|
+
const uniqueBindings = [];
|
|
1925
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
1926
|
+
for (const binding of bindings) {
|
|
1927
|
+
if (!seenUrls.has(binding.identifier.url)) {
|
|
1928
|
+
seenUrls.add(binding.identifier.url);
|
|
1929
|
+
uniqueBindings.push(binding);
|
|
1930
|
+
}
|
|
2027
1931
|
}
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
1932
|
+
return uniqueBindings;
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
// src/typeschema/core/transformer.ts
|
|
1936
|
+
function mkFields(register, fhirSchema, parentPath, elements, logger) {
|
|
1937
|
+
if (!elements) return void 0;
|
|
1938
|
+
const fields = {};
|
|
1939
|
+
for (const key of register.getAllElementKeys(elements)) {
|
|
1940
|
+
const path = [...parentPath, key];
|
|
1941
|
+
const elemSnapshot = register.resolveElementSnapshot(fhirSchema, path);
|
|
1942
|
+
if (isNestedElement(elemSnapshot)) {
|
|
1943
|
+
fields[key] = mkNestedField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1944
|
+
} else {
|
|
1945
|
+
fields[key] = mkField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1946
|
+
}
|
|
2031
1947
|
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
1948
|
+
return fields;
|
|
1949
|
+
}
|
|
1950
|
+
function extractFieldDependencies(fields) {
|
|
1951
|
+
const deps = [];
|
|
1952
|
+
for (const field of Object.values(fields)) {
|
|
1953
|
+
if ("type" in field && field.type) {
|
|
1954
|
+
deps.push(field.type);
|
|
1955
|
+
}
|
|
1956
|
+
if ("binding" in field && field.binding) {
|
|
1957
|
+
deps.push(field.binding);
|
|
2037
1958
|
}
|
|
2038
1959
|
}
|
|
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();
|
|
1960
|
+
return deps;
|
|
1961
|
+
}
|
|
1962
|
+
function isExtensionSchema(fhirSchema, _identifier) {
|
|
1963
|
+
if (fhirSchema.base === "Extension" || fhirSchema.base === "http://hl7.org/fhir/StructureDefinition/Extension") {
|
|
1964
|
+
return true;
|
|
2048
1965
|
}
|
|
2049
|
-
|
|
2050
|
-
|
|
1966
|
+
if (fhirSchema.url?.includes("/extension/") || fhirSchema.url?.includes("-extension")) {
|
|
1967
|
+
return true;
|
|
2051
1968
|
}
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
return parts.join(".");
|
|
1969
|
+
if (fhirSchema.name?.toLowerCase().includes("extension")) {
|
|
1970
|
+
return true;
|
|
2055
1971
|
}
|
|
2056
|
-
|
|
2057
|
-
return
|
|
1972
|
+
if (fhirSchema.type === "Extension") {
|
|
1973
|
+
return true;
|
|
2058
1974
|
}
|
|
2059
|
-
|
|
2060
|
-
|
|
1975
|
+
return false;
|
|
1976
|
+
}
|
|
1977
|
+
async function transformValueSet(register, valueSet, logger) {
|
|
1978
|
+
if (!valueSet.url) throw new Error("ValueSet URL is required");
|
|
1979
|
+
const identifier = mkValueSetIdentifierByUrl(register, valueSet.package_meta, valueSet.url);
|
|
1980
|
+
const concept = extractValueSetConceptsByUrl(register, valueSet.package_meta, valueSet.url);
|
|
1981
|
+
return {
|
|
1982
|
+
identifier,
|
|
1983
|
+
description: valueSet.description,
|
|
1984
|
+
concept,
|
|
1985
|
+
compose: !concept ? valueSet.compose : void 0
|
|
1986
|
+
};
|
|
1987
|
+
}
|
|
1988
|
+
function extractDependencies(identifier, base, fields, nestedTypes) {
|
|
1989
|
+
const deps = [];
|
|
1990
|
+
if (base) deps.push(base);
|
|
1991
|
+
if (fields) deps.push(...extractFieldDependencies(fields));
|
|
1992
|
+
if (nestedTypes) deps.push(...extractNestedDependencies(nestedTypes));
|
|
1993
|
+
const uniqDeps = {};
|
|
1994
|
+
for (const dep of deps) {
|
|
1995
|
+
if (dep.url === identifier.url) continue;
|
|
1996
|
+
uniqDeps[dep.url] = dep;
|
|
2061
1997
|
}
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
1998
|
+
const localNestedTypeUrls = new Set(nestedTypes?.map((nt) => nt.identifier.url));
|
|
1999
|
+
const result = Object.values(uniqDeps).filter((e) => {
|
|
2000
|
+
if (isProfileIdentifier(identifier)) return true;
|
|
2001
|
+
if (!isNestedIdentifier(e)) return true;
|
|
2002
|
+
return !localNestedTypeUrls.has(e.url);
|
|
2003
|
+
}).sort((a, b) => a.url.localeCompare(b.url));
|
|
2004
|
+
return result.length > 0 ? result : void 0;
|
|
2005
|
+
}
|
|
2006
|
+
function transformFhirSchemaResource(register, fhirSchema, logger) {
|
|
2007
|
+
const identifier = mkIdentifier(fhirSchema);
|
|
2008
|
+
let base;
|
|
2009
|
+
if (fhirSchema.base && fhirSchema.type !== "Element") {
|
|
2010
|
+
const baseFs = register.resolveFs(
|
|
2011
|
+
fhirSchema.package_meta,
|
|
2012
|
+
register.ensureSpecializationCanonicalUrl(fhirSchema.base)
|
|
2013
|
+
);
|
|
2014
|
+
if (!baseFs) {
|
|
2015
|
+
throw new Error(
|
|
2016
|
+
`Base resource not found '${fhirSchema.base}' for <${fhirSchema.url}> from ${packageMetaToFhir(fhirSchema.package_meta)}`
|
|
2017
|
+
);
|
|
2068
2018
|
}
|
|
2069
|
-
|
|
2019
|
+
base = mkIdentifier(baseFs);
|
|
2070
2020
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2021
|
+
const fields = mkFields(register, fhirSchema, [], fhirSchema.elements, logger);
|
|
2022
|
+
const nested = mkNestedTypes(register, fhirSchema, logger);
|
|
2023
|
+
const dependencies = extractDependencies(identifier, base, fields, nested);
|
|
2024
|
+
const typeSchema = {
|
|
2025
|
+
identifier,
|
|
2026
|
+
base,
|
|
2027
|
+
fields,
|
|
2028
|
+
nested,
|
|
2029
|
+
description: fhirSchema.description,
|
|
2030
|
+
dependencies
|
|
2031
|
+
};
|
|
2032
|
+
const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger);
|
|
2033
|
+
return [typeSchema, ...bindingSchemas];
|
|
2034
|
+
}
|
|
2035
|
+
async function transformFhirSchema(register, fhirSchema, logger) {
|
|
2036
|
+
const schemas = transformFhirSchemaResource(register, fhirSchema, logger);
|
|
2037
|
+
if (isExtensionSchema(fhirSchema, mkIdentifier(fhirSchema))) {
|
|
2038
|
+
const schema = schemas[0];
|
|
2039
|
+
if (!schema) throw new Error(`Expected schema to be defined`);
|
|
2040
|
+
schema.metadata = {
|
|
2041
|
+
isExtension: true
|
|
2042
|
+
// Mark as extension for file organization
|
|
2043
|
+
};
|
|
2078
2044
|
}
|
|
2079
|
-
|
|
2045
|
+
return schemas;
|
|
2046
|
+
}
|
|
2080
2047
|
|
|
2081
2048
|
// src/fhir-types/hl7-fhir-r4-core/CodeSystem.ts
|
|
2082
2049
|
var isCodeSystem = (resource) => {
|
|
@@ -2305,6 +2272,131 @@ var generateTypeSchemas = async (register, logger) => {
|
|
|
2305
2272
|
}
|
|
2306
2273
|
return fhirSchemas;
|
|
2307
2274
|
};
|
|
2275
|
+
var mutableSelectFields = (schema, selectFields) => {
|
|
2276
|
+
const selectedFields = {};
|
|
2277
|
+
const selectPolimorphic = {};
|
|
2278
|
+
for (const fieldName of selectFields) {
|
|
2279
|
+
const field = schema.fields?.[fieldName];
|
|
2280
|
+
if (!schema.fields || !field) throw new Error(`Field ${fieldName} not found`);
|
|
2281
|
+
if (isChoiceDeclarationField(field)) {
|
|
2282
|
+
if (!selectPolimorphic[fieldName]) selectPolimorphic[fieldName] = {};
|
|
2283
|
+
selectPolimorphic[fieldName].declaration = field.choices;
|
|
2284
|
+
} else if (isChoiceInstanceField(field)) {
|
|
2285
|
+
const choiceName = field.choiceOf;
|
|
2286
|
+
if (!selectPolimorphic[choiceName]) selectPolimorphic[choiceName] = {};
|
|
2287
|
+
selectPolimorphic[choiceName].instances = [...selectPolimorphic[choiceName].instances ?? [], fieldName];
|
|
2288
|
+
} else {
|
|
2289
|
+
selectedFields[fieldName] = field;
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
for (const [choiceName, { declaration, instances }] of Object.entries(selectPolimorphic)) {
|
|
2293
|
+
const choices = instances ?? declaration;
|
|
2294
|
+
assert3(choices);
|
|
2295
|
+
for (const choiceInstanceName of choices) {
|
|
2296
|
+
const field = schema.fields?.[choiceInstanceName];
|
|
2297
|
+
assert3(field);
|
|
2298
|
+
selectedFields[choiceInstanceName] = field;
|
|
2299
|
+
}
|
|
2300
|
+
const decl = schema.fields?.[choiceName];
|
|
2301
|
+
assert3(decl);
|
|
2302
|
+
selectedFields[choiceName] = { ...decl, choices };
|
|
2303
|
+
}
|
|
2304
|
+
schema.fields = selectedFields;
|
|
2305
|
+
};
|
|
2306
|
+
var mutableIgnoreFields = (schema, ignoreFields) => {
|
|
2307
|
+
for (const fieldName of ignoreFields) {
|
|
2308
|
+
const field = schema.fields?.[fieldName];
|
|
2309
|
+
if (!schema.fields || !field) throw new Error(`Field ${fieldName} not found`);
|
|
2310
|
+
if (schema.fields) {
|
|
2311
|
+
if (isChoiceDeclarationField(field)) {
|
|
2312
|
+
for (const choiceName of field.choices) {
|
|
2313
|
+
delete schema.fields[choiceName];
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
if (isChoiceInstanceField(field)) {
|
|
2317
|
+
const choiceDeclaration = schema.fields[field.choiceOf];
|
|
2318
|
+
assert3(isChoiceDeclarationField(choiceDeclaration));
|
|
2319
|
+
choiceDeclaration.choices = choiceDeclaration.choices.filter((c) => c !== fieldName);
|
|
2320
|
+
if (choiceDeclaration.choices.length === 0) {
|
|
2321
|
+
delete schema.fields[field.choiceOf];
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
delete schema.fields[fieldName];
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
};
|
|
2328
|
+
var treeShakeTypeSchema = (schema, rule, _logger) => {
|
|
2329
|
+
schema = structuredClone(schema);
|
|
2330
|
+
if (isPrimitiveTypeSchema(schema) || isValueSetTypeSchema(schema) || isBindingSchema(schema)) return schema;
|
|
2331
|
+
if (rule.selectFields) {
|
|
2332
|
+
if (rule.ignoreFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule");
|
|
2333
|
+
mutableSelectFields(schema, rule.selectFields);
|
|
2334
|
+
}
|
|
2335
|
+
if (rule.ignoreFields) {
|
|
2336
|
+
if (rule.selectFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule");
|
|
2337
|
+
mutableIgnoreFields(schema, rule.ignoreFields);
|
|
2338
|
+
}
|
|
2339
|
+
if (schema.nested) {
|
|
2340
|
+
const usedTypes = /* @__PURE__ */ new Set();
|
|
2341
|
+
const collectUsedNestedTypes = (s) => {
|
|
2342
|
+
Object.values(s.fields ?? {}).filter(isNotChoiceDeclarationField).filter((f) => isNestedIdentifier(f.type)).forEach((f) => {
|
|
2343
|
+
const url = f.type.url;
|
|
2344
|
+
if (!usedTypes.has(url)) {
|
|
2345
|
+
usedTypes.add(url);
|
|
2346
|
+
const nestedTypeDef = schema.nested?.find((f2) => f2.identifier.url === url);
|
|
2347
|
+
assert3(nestedTypeDef);
|
|
2348
|
+
collectUsedNestedTypes(nestedTypeDef);
|
|
2349
|
+
}
|
|
2350
|
+
});
|
|
2351
|
+
};
|
|
2352
|
+
collectUsedNestedTypes(schema);
|
|
2353
|
+
schema.nested = schema.nested.filter((n) => usedTypes.has(n.identifier.url));
|
|
2354
|
+
}
|
|
2355
|
+
schema.dependencies = extractDependencies(schema.identifier, schema.base, schema.fields, schema.nested);
|
|
2356
|
+
return schema;
|
|
2357
|
+
};
|
|
2358
|
+
var treeShake = (tsIndex, treeShake2, { resolutionTree, logger }) => {
|
|
2359
|
+
const focusedSchemas = [];
|
|
2360
|
+
for (const [pkgId, requires] of Object.entries(treeShake2)) {
|
|
2361
|
+
for (const [url, rule] of Object.entries(requires)) {
|
|
2362
|
+
const schema = tsIndex.resolveByUrl(pkgId, url);
|
|
2363
|
+
if (!schema) throw new Error(`Schema not found for ${pkgId} ${url}`);
|
|
2364
|
+
const shaked2 = treeShakeTypeSchema(schema, rule);
|
|
2365
|
+
focusedSchemas.push(shaked2);
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
const collectDeps = (schemas, acc) => {
|
|
2369
|
+
if (schemas.length === 0) return Object.values(acc);
|
|
2370
|
+
for (const schema of schemas) {
|
|
2371
|
+
acc[JSON.stringify(schema.identifier)] = schema;
|
|
2372
|
+
}
|
|
2373
|
+
const newSchemas = [];
|
|
2374
|
+
for (const schema of schemas) {
|
|
2375
|
+
if (isSpecializationTypeSchema(schema)) {
|
|
2376
|
+
if (!schema.dependencies) continue;
|
|
2377
|
+
schema.dependencies.forEach((dep) => {
|
|
2378
|
+
const depSchema = tsIndex.resolve(dep);
|
|
2379
|
+
if (!depSchema)
|
|
2380
|
+
throw new Error(
|
|
2381
|
+
`Dependent schema ${JSON.stringify(dep)} not found for ${JSON.stringify(schema.identifier)}`
|
|
2382
|
+
);
|
|
2383
|
+
const id = JSON.stringify(depSchema.identifier);
|
|
2384
|
+
if (!acc[id]) newSchemas.push(depSchema);
|
|
2385
|
+
});
|
|
2386
|
+
if (schema.nested) {
|
|
2387
|
+
for (const nest of schema.nested) {
|
|
2388
|
+
if (isNestedIdentifier(nest.identifier)) continue;
|
|
2389
|
+
const id = JSON.stringify(nest.identifier);
|
|
2390
|
+
if (!acc[id]) newSchemas.push(nest);
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
return collectDeps(newSchemas, acc);
|
|
2396
|
+
};
|
|
2397
|
+
const shaked = collectDeps(focusedSchemas, {});
|
|
2398
|
+
return mkTypeSchemaIndex(shaked, { resolutionTree, logger });
|
|
2399
|
+
};
|
|
2308
2400
|
|
|
2309
2401
|
// src/api/writer-generator/typescript.ts
|
|
2310
2402
|
var primitiveType2tsType = {
|
|
@@ -2967,7 +3059,7 @@ var APIBuilder = class {
|
|
|
2967
3059
|
return this;
|
|
2968
3060
|
}
|
|
2969
3061
|
setLogLevel(level) {
|
|
2970
|
-
this.logger?.setLevel(level);
|
|
3062
|
+
this.logger?.setLevel(typeof level === "string" ? parseLogLevel(level) : level);
|
|
2971
3063
|
return this;
|
|
2972
3064
|
}
|
|
2973
3065
|
throwException(enabled = true) {
|