@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/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 assert2 from 'assert';
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
- // src/typeschema/core/identifier.ts
744
- function dropVersionFromUrl(url) {
745
- const baseUrl = url.split("|")[0];
746
- return baseUrl ? baseUrl : url;
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
- return url;
776
- };
777
- function mkValueSetIdentifierByUrl(register, pkg, fullValueSetUrl) {
778
- const valueSetUrl = dropVersionFromUrl(fullValueSetUrl);
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
- const nestedName = path.join(".");
819
- const url = nestedTypeOrigins[nestedName] ?? `${fhirSchema.url}#${nestedName}`;
820
- return {
821
- kind: "nested",
822
- package: fhirSchema.package_meta.name,
823
- version: fhirSchema.package_meta.version,
824
- name: nestedName,
825
- url
826
- };
827
- }
828
- function collectNestedElements(fhirSchema, parentPath, elements) {
829
- const nested = [];
830
- for (const [key, element] of Object.entries(elements)) {
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 (element.elements) {
836
- nested.push(...collectNestedElements(fhirSchema, path, element.elements));
792
+ if (base && nameToMap[base]) {
793
+ graph[name].push(base);
837
794
  }
838
795
  }
839
- return nested;
840
- }
841
- function transformNestedElements(register, fhirSchema, parentPath, elements, logger) {
842
- const fields = {};
843
- for (const [key, _element] of Object.entries(elements)) {
844
- const path = [...parentPath, key];
845
- const elemSnapshot = register.resolveElementSnapshot(fhirSchema, path);
846
- if (isNestedElement(elemSnapshot)) {
847
- fields[key] = mkNestedField(register, fhirSchema, path, elemSnapshot, logger);
848
- } else {
849
- fields[key] = mkField(register, fhirSchema, path, elemSnapshot, logger);
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 fields;
853
- }
854
- function mkNestedTypes(register, fhirSchema, logger) {
855
- if (!fhirSchema.elements) return void 0;
856
- const nested = collectNestedElements(fhirSchema, [], fhirSchema.elements).filter(
857
- ([_, element]) => element.elements && Object.keys(element.elements).length > 0
858
- );
859
- const nestedTypes = [];
860
- for (const [path, element] of nested) {
861
- const identifier = mkNestedIdentifier(register, fhirSchema, path, logger);
862
- let baseName;
863
- if (element.type === "BackboneElement" || !element.type) {
864
- baseName = "BackboneElement";
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
- nestedTypes.sort((a, b) => a.identifier.url.localeCompare(b.identifier.url));
887
- return nestedTypes.length === 0 ? void 0 : nestedTypes;
888
- }
889
- function extractNestedDependencies(nestedTypes) {
890
- const deps = [];
891
- for (const nested of nestedTypes) {
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
- for (const field of Object.values(nested.fields || {})) {
896
- if ("type" in field && field.type) {
897
- deps.push(field.type);
898
- }
899
- if ("binding" in field && field.binding) {
900
- deps.push(field.binding);
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 deps;
905
- }
906
-
907
- // src/typeschema/core/field-builder.ts
908
- function isRequired(register, fhirSchema, path) {
909
- const fieldName = path[path.length - 1];
910
- if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
911
- const parentPath = path.slice(0, -1);
912
- const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs5) => {
913
- if (parentPath.length === 0) return fs5.required || [];
914
- if (!fs5.elements) return [];
915
- let elem = fs5;
916
- for (const k of parentPath) {
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
- assert2(this.tsIndex !== void 0);
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
- generateClassBody(schema) {
1846
- this.generateModelConfig();
1847
- if (!schema.fields) {
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
- generateModelConfig() {
1860
- const extraMode = this.opts.allowExtraFields ? "allow" : "forbid";
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
- generateResourceTypeField(schema) {
1864
- this.line(`${this.nameFormatFunction("resourceType")}: str = Field(`);
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
- generateFields(schema) {
1875
- const sortedFields = Object.entries(schema.fields ?? []).sort(([a], [b]) => a.localeCompare(b));
1876
- for (const [fieldName, field] of sortedFields) {
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
- buildFieldInfo(fieldName, field) {
1883
- const pyFieldName = fixReservedWords(this.nameFormatFunction(fieldName));
1884
- const fieldType = this.determineFieldType(field);
1885
- const defaultValue = this.getFieldDefaultValue(field, fieldName);
1886
- return {
1887
- name: pyFieldName,
1888
- type: fieldType,
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
- determineFieldType(field) {
1893
- let fieldType = field ? this.getBaseFieldType(field) : "";
1894
- if ("enum" in field && field.enum) {
1895
- const s = field.enum.map((e) => `"${e}"`).join(", ");
1896
- fieldType = `Literal[${s}]`;
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
- if (field.array) {
1899
- fieldType = `PyList[${fieldType}]`;
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 (!field.required) {
1902
- fieldType = `${fieldType} | None`;
1627
+ if (element.elements) {
1628
+ nested.push(...collectNestedElements(fhirSchema, path, element.elements));
1903
1629
  }
1904
- return fieldType;
1905
1630
  }
1906
- getBaseFieldType(field) {
1907
- if ("type" in field && field.type.kind === "resource") return `${field.type.name}Family`;
1908
- if ("type" in field && field.type.kind === "nested") return deriveResourceName(field.type);
1909
- if ("type" in field && field.type.kind === "primitive-type")
1910
- return PRIMITIVE_TYPE_MAP2[field.type.name] ?? "str";
1911
- return "type" in field ? field.type.name : "";
1912
- }
1913
- getFieldDefaultValue(field, fieldName) {
1914
- const aliasSpec = `alias="${fieldName}", serialization_alias="${fieldName}"`;
1915
- if (!field.required) {
1916
- return ` = Field(None, ${aliasSpec})`;
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
- generateResourceMethods(schema) {
1921
- const className = schema.identifier.name.toString();
1922
- this.line();
1923
- this.line("def to_json(self, indent: int | None = None) -> str:");
1924
- this.line(" return self.model_dump_json(exclude_unset=True, exclude_none=True, indent=indent)");
1925
- this.line();
1926
- this.line("@classmethod");
1927
- this.line(`def from_json(cls, json: str) -> ${className}:`);
1928
- this.line(" return cls.model_validate_json(json)");
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
- generateNestedTypes(schema) {
1931
- if (!schema.nested) return;
1932
- this.line();
1933
- for (const subtype of schema.nested) {
1934
- this.generateType(subtype);
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
- generateDefaultImports() {
1938
- this.pyImportFrom("__future__", "annotations");
1939
- this.pyImportFrom("pydantic", "BaseModel", "ConfigDict", "Field", "PositiveInt");
1940
- this.pyImportFrom("typing", "List as PyList", "Literal");
1941
- }
1942
- generateDependenciesImports(schema) {
1943
- if (!schema.dependencies || schema.dependencies.length === 0) return;
1944
- this.importComplexTypeDependencies(schema.dependencies);
1945
- this.importResourceDependencies(schema.dependencies);
1946
- }
1947
- importComplexTypeDependencies(dependencies) {
1948
- const complexTypeDeps = dependencies.filter((dep) => dep.kind === "complex-type");
1949
- const depsByPackage = this.groupDependenciesByPackage(complexTypeDeps);
1950
- for (const [pyPackage, names] of Object.entries(depsByPackage)) {
1951
- this.pyImportFrom(pyPackage, ...names.sort());
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
- importResourceDependencies(dependencies) {
1955
- const resourceDeps = dependencies.filter((dep) => dep.kind === "resource");
1956
- for (const dep of resourceDeps) {
1957
- this.pyImportType(dep);
1958
- const familyName = `${pascalCase(dep.name)}Family`;
1959
- const familyPackage = `${this.pyFhirPackage(dep)}.resource_families`;
1960
- this.pyImportFrom(familyPackage, familyName);
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
- groupDependenciesByPackage(dependencies) {
1964
- const grouped = {};
1965
- for (const dep of dependencies) {
1966
- const pyPackage = this.pyPackage(dep);
1967
- if (!grouped[pyPackage]) {
1968
- grouped[pyPackage] = [];
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
- pyImportFrom(pyPackage, ...entities) {
1975
- const oneLine = `from ${pyPackage} import ${entities.join(", ")}`;
1976
- if (this.shouldUseSingleLineImport(oneLine, entities)) {
1977
- this.line(oneLine);
1978
- } else {
1979
- this.writeMultiLineImport(pyPackage, entities);
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
- shouldUseSingleLineImport(oneLine, entities) {
1983
- return oneLine.length <= MAX_IMPORT_LINE_LENGTH || entities.length === 1;
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
- writeMultiLineImport(pyPackage, entities) {
1986
- this.line(`from ${pyPackage} import (\\`);
1987
- this.indentBlock(() => {
1988
- const remaining = [...entities];
1989
- while (remaining.length > 0) {
1990
- const line = this.buildImportLine(remaining, MAX_IMPORT_LINE_LENGTH);
1991
- this.line(line);
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
- this.line(")");
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
- buildResourceFamiliesFile(packages, families, exportList) {
2020
- this.cat("resource_families.py", () => {
2021
- this.generateDisclaimer();
2022
- this.includeResourceFamilyValidator();
2023
- this.line();
2024
- this.generateFamilyDefinitions(packages, families);
2025
- this.generateFamilyExports(exportList);
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
- includeResourceFamilyValidator() {
2029
- const content = fs__default.readFileSync(resolvePyAssets("resource_family_validator.py"), "utf-8");
2030
- this.line(content);
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
- generateFamilyDefinitions(packages, families) {
2033
- this.line(`packages = [${packages.map((p) => `'${p}'`).join(", ")}]`);
2034
- this.line();
2035
- for (const [familyName, resources] of Object.entries(families)) {
2036
- this.generateFamilyDefinition(familyName, resources);
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
- generateFamilyDefinition(familyName, resources) {
2040
- const listName = `${familyName}_resources`;
2041
- this.line(`${listName} = [${resources.map((r) => `'${r}'`).join(", ")}]`);
2042
- this.line();
2043
- this.line(`def validate_and_downcast_${familyName}(v: Any) -> Any:`);
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
- generateFamilyExports(exportList) {
2050
- this.line(`__all__ = [${exportList.map((e) => `'${e}'`).join(", ")}]`);
1966
+ if (fhirSchema.url?.includes("/extension/") || fhirSchema.url?.includes("-extension")) {
1967
+ return true;
2051
1968
  }
2052
- buildPyPackageName(packageName) {
2053
- const parts = packageName ? [snakeCase(packageName)] : [""];
2054
- return parts.join(".");
1969
+ if (fhirSchema.name?.toLowerCase().includes("extension")) {
1970
+ return true;
2055
1971
  }
2056
- pyFhirPackage(identifier) {
2057
- return this.pyFhirPackageByName(identifier.package);
1972
+ if (fhirSchema.type === "Extension") {
1973
+ return true;
2058
1974
  }
2059
- pyFhirPackageByName(name) {
2060
- return [this.opts.rootPackageName, this.buildPyPackageName(name)].join(".");
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
- pyPackage(identifier) {
2063
- if (identifier.kind === "complex-type") {
2064
- return `${this.pyFhirPackage(identifier)}.base`;
2065
- }
2066
- if (identifier.kind === "resource") {
2067
- return [this.pyFhirPackage(identifier), snakeCase(identifier.name)].join(".");
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
- return this.pyFhirPackage(identifier);
2019
+ base = mkIdentifier(baseFs);
2070
2020
  }
2071
- getFieldFormatFunction(format) {
2072
- if (!AVAILABLE_STRING_FORMATS[format]) {
2073
- this.logger()?.warn(`Unknown field format '${format}'. Defaulting to SnakeCase.`);
2074
- this.logger()?.warn(`Supported formats: ${Object.keys(AVAILABLE_STRING_FORMATS).join(", ")}`);
2075
- return snakeCase;
2076
- }
2077
- return AVAILABLE_STRING_FORMATS[format];
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) {