@atomic-ehr/codegen 0.0.4-canary.20251217085017.be512ec → 0.0.4-canary.20251217144707.ef99bf1

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
@@ -3,13 +3,14 @@ import * as fs from 'fs';
3
3
  import fs__default, { existsSync } from 'fs';
4
4
  import * as afs2 from 'fs/promises';
5
5
  import { readFile } from 'fs/promises';
6
- import * as Path4 from 'path';
7
- import Path4__default, { resolve } from 'path';
6
+ import * as Path5 from 'path';
7
+ import Path5__default, { resolve } from 'path';
8
8
  import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
9
+ import assert2 from 'assert';
10
+ import { fileURLToPath } from 'url';
11
+ import * as YAML from 'yaml';
9
12
  import * as fhirschema from '@atomic-ehr/fhirschema';
10
13
  import { isStructureDefinition } from '@atomic-ehr/fhirschema';
11
- import * as YAML from 'yaml';
12
- import assert from 'assert';
13
14
 
14
15
  // src/utils/codegen-logger.ts
15
16
  var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
@@ -178,6 +179,9 @@ var camelCase = (s) => {
178
179
  var pascalCase = (s) => {
179
180
  return words(s).map(capitalCase).join("");
180
181
  };
182
+ var snakeCase = (s) => {
183
+ return words(s).map((s2) => s2.toLowerCase()).join("_");
184
+ };
181
185
  var uppercaseFirstLetter = (str) => {
182
186
  if (!str || str.length === 0) return str;
183
187
  return str.charAt(0).toUpperCase() + str.slice(1);
@@ -224,7 +228,7 @@ var FileSystemWriter = class {
224
228
  }
225
229
  cd(path, gen) {
226
230
  const prev = this.currentDir;
227
- this.currentDir = path.startsWith("/") ? Path4.join(this.opts.outputDir, path) : Path4.join(this.currentDir ?? this.opts.outputDir, path);
231
+ this.currentDir = path.startsWith("/") ? Path5.join(this.opts.outputDir, path) : Path5.join(this.currentDir ?? this.opts.outputDir, path);
228
232
  this.onDiskMkDir(this.currentDir);
229
233
  this.logger()?.debug(`cd '${this.currentDir}'`);
230
234
  gen();
@@ -233,12 +237,12 @@ var FileSystemWriter = class {
233
237
  cat(fn, gen) {
234
238
  if (this.currentFile) throw new Error("Can't open file when another file is open");
235
239
  if (fn.includes("/")) throw new Error(`Change file path separatly: ${fn}`);
236
- const relPath = Path4.normalize(`${this.currentDir}/${fn}`);
240
+ const relPath = Path5.normalize(`${this.currentDir}/${fn}`);
237
241
  try {
238
242
  const descriptor = this.onDiskOpenFile(relPath);
239
243
  this.logger()?.debug(`cat > '${relPath}'`);
240
244
  this.currentFile = { descriptor, relPath };
241
- this.writtenFilesBuffer[this.currentFile.relPath] = { relPath, absPath: Path4.resolve(relPath), tokens: [] };
245
+ this.writtenFilesBuffer[this.currentFile.relPath] = { relPath, absPath: Path5.resolve(relPath), tokens: [] };
242
246
  gen();
243
247
  } finally {
244
248
  if (this.currentFile) this.onDiskCloseFile(this.currentFile.descriptor);
@@ -726,212 +730,16 @@ var CSharp = class extends Writer {
726
730
  }
727
731
  copyStaticFiles() {
728
732
  if (!this.opts.staticSourceDir) return;
729
- const sourcePath = Path4__default.resolve(this.opts.staticSourceDir);
733
+ const sourcePath = Path5__default.resolve(this.opts.staticSourceDir);
730
734
  fs__default.cpSync(sourcePath, this.opts.outputDir, { recursive: true });
731
735
  }
732
736
  generateHelperFile() {
733
737
  const sourceFile = "src/api/writer-generator/csharp/Helper.cs";
734
- const destFile = Path4__default.join(this.opts.outputDir, "Helper.cs");
738
+ const destFile = Path5__default.join(this.opts.outputDir, "Helper.cs");
735
739
  fs__default.copyFileSync(sourceFile, destFile);
736
740
  }
737
741
  };
738
742
 
739
- // src/fhir-types/hl7-fhir-r4-core/CodeSystem.ts
740
- var isCodeSystem = (resource) => {
741
- return resource !== null && typeof resource === "object" && resource.resourceType === "CodeSystem";
742
- };
743
-
744
- // src/fhir-types/hl7-fhir-r4-core/ValueSet.ts
745
- var isValueSet = (resource) => {
746
- return resource !== null && typeof resource === "object" && resource.resourceType === "ValueSet";
747
- };
748
-
749
- // src/typeschema/register.ts
750
- var readPackageDependencies = async (manager, packageMeta) => {
751
- const packageJSON = await manager.packageJson(packageMeta.name);
752
- const dependencies = packageJSON.dependencies;
753
- if (dependencies !== void 0) {
754
- return Object.entries(dependencies).map(([name, version]) => {
755
- return { name, version };
756
- });
757
- }
758
- return [];
759
- };
760
- var mkEmptyPkgIndex = (pkg) => {
761
- return {
762
- pkg,
763
- canonicalResolution: {},
764
- fhirSchemas: {},
765
- valueSets: {}
766
- };
767
- };
768
- var mkPackageAwareResolver = async (manager, pkg, deep, acc, logger) => {
769
- const pkgId = packageMetaToFhir(pkg);
770
- logger?.info(`${" ".repeat(deep * 2)}+ ${pkgId}`);
771
- if (acc[pkgId]) return acc[pkgId];
772
- const index = mkEmptyPkgIndex(pkg);
773
- for (const resource of await manager.search({ package: pkg })) {
774
- const rawUrl = resource.url;
775
- if (!rawUrl) continue;
776
- if (!(isStructureDefinition(resource) || isValueSet(resource) || isCodeSystem(resource))) continue;
777
- const url = rawUrl;
778
- if (index.canonicalResolution[url]) logger?.dry_warn(`Duplicate canonical URL: ${url} at ${pkgId}.`);
779
- index.canonicalResolution[url] = [{ deep, pkg, pkgId, resource }];
780
- }
781
- const deps = await readPackageDependencies(manager, pkg);
782
- for (const depPkg of deps) {
783
- const { canonicalResolution } = await mkPackageAwareResolver(manager, depPkg, deep + 1, acc, logger);
784
- for (const [surl, resolutions] of Object.entries(canonicalResolution)) {
785
- const url = surl;
786
- index.canonicalResolution[url] = [...index.canonicalResolution[url] || [], ...resolutions];
787
- }
788
- }
789
- for (const resolutionOptions of Object.values(index.canonicalResolution)) {
790
- resolutionOptions.sort((a, b) => a.deep - b.deep);
791
- }
792
- acc[pkgId] = index;
793
- return index;
794
- };
795
- var packageAgnosticResolveCanonical = (resolver, url, _logger) => {
796
- const options = Object.values(resolver).flatMap((pkg) => pkg.canonicalResolution[url]);
797
- if (!options) throw new Error(`No canonical resolution found for ${url} in any package`);
798
- return options[0]?.resource;
799
- };
800
- var registerFromManager = async (manager, { logger, fallbackPackageForNameResolution, focusedPackages }) => {
801
- const packages = focusedPackages ?? await manager.packages();
802
- const resolver = {};
803
- for (const pkg of packages) {
804
- await mkPackageAwareResolver(manager, pkg, 0, resolver, logger);
805
- }
806
- for (const { pkg, canonicalResolution } of Object.values(resolver)) {
807
- const pkgId = packageMetaToFhir(pkg);
808
- if (!resolver[pkgId]) throw new Error(`Package ${pkgId} not found`);
809
- let counter = 0;
810
- logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' begins...`);
811
- for (const [_url, options] of Object.entries(canonicalResolution)) {
812
- const resolition = options[0];
813
- if (!resolition) throw new Error(`Resource not found`);
814
- const resource = resolition.resource;
815
- const resourcePkg = resolition.pkg;
816
- if (isStructureDefinition(resource)) {
817
- const rfs = enrichFHIRSchema(
818
- fhirschema.translate(resource),
819
- resourcePkg
820
- );
821
- counter++;
822
- resolver[pkgId].fhirSchemas[rfs.url] = rfs;
823
- }
824
- if (isValueSet(resource)) {
825
- const rvs = enrichValueSet(resource, resourcePkg);
826
- resolver[pkgId].valueSets[rvs.url] = rvs;
827
- }
828
- }
829
- logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' completed: ${counter} successful`);
830
- }
831
- const resolveFs = (pkg, canonicalUrl) => {
832
- return resolver[packageMetaToFhir(pkg)]?.fhirSchemas[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.fhirSchemas[canonicalUrl];
833
- };
834
- const resolveVs = (pkg, canonicalUrl) => {
835
- return resolver[packageMetaToFhir(pkg)]?.valueSets[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.valueSets[canonicalUrl];
836
- };
837
- const ensureSpecializationCanonicalUrl = (name) => name.match(/^[a-zA-Z0-9]+$/) && `http://hl7.org/fhir/StructureDefinition/${name}` || name;
838
- const resolveFsGenealogy = (pkg, canonicalUrl) => {
839
- let fs4 = resolveFs(pkg, canonicalUrl);
840
- if (fs4 === void 0) throw new Error(`Failed to resolve FHIR Schema: '${canonicalUrl}'`);
841
- const genealogy = [fs4];
842
- while (fs4?.base) {
843
- const pkg2 = fs4.package_meta;
844
- const baseUrl = ensureSpecializationCanonicalUrl(fs4.base);
845
- fs4 = resolveFs(pkg2, baseUrl);
846
- if (fs4 === void 0)
847
- throw new Error(
848
- `Failed to resolve FHIR Schema base for '${canonicalUrl}'. Problem: '${baseUrl}' from '${packageMetaToFhir(pkg2)}'`
849
- );
850
- genealogy.push(fs4);
851
- }
852
- return genealogy;
853
- };
854
- const resolveFsSpecializations = (pkg, canonicalUrl) => {
855
- return resolveFsGenealogy(pkg, canonicalUrl).filter((fs4) => fs4.derivation === "specialization");
856
- };
857
- const resolveElementSnapshot = (fhirSchema, path) => {
858
- const geneology = resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url);
859
- const elemGeneology = resolveFsElementGenealogy(geneology, path);
860
- const elemSnapshot = fsElementSnapshot(elemGeneology);
861
- return elemSnapshot;
862
- };
863
- const getAllElementKeys = (elems) => {
864
- const keys = /* @__PURE__ */ new Set();
865
- for (const [key, elem] of Object.entries(elems)) {
866
- keys.add(key);
867
- for (const choiceKey of elem?.choices || []) {
868
- if (!elems[choiceKey]) {
869
- keys.add(choiceKey);
870
- }
871
- }
872
- }
873
- return Array.from(keys);
874
- };
875
- return {
876
- ...manager,
877
- testAppendFs(fs4) {
878
- const rfs = enrichFHIRSchema(fs4);
879
- const pkgId = packageMetaToFhir(rfs.package_meta);
880
- if (!resolver[pkgId]) resolver[pkgId] = mkEmptyPkgIndex(rfs.package_meta);
881
- resolver[pkgId].fhirSchemas[rfs.url] = rfs;
882
- },
883
- resolveFs,
884
- resolveFsGenealogy,
885
- resolveFsSpecializations,
886
- ensureSpecializationCanonicalUrl,
887
- resolveSd: (_pkg, canonicalUrl) => {
888
- const res = packageAgnosticResolveCanonical(resolver, canonicalUrl);
889
- if (isStructureDefinition(res)) return res;
890
- return void 0;
891
- },
892
- allFs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.fhirSchemas)),
893
- allVs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.valueSets)),
894
- resolveVs,
895
- resolveAny: (canonicalUrl) => packageAgnosticResolveCanonical(resolver, canonicalUrl),
896
- resolveElementSnapshot,
897
- getAllElementKeys,
898
- resolver,
899
- resolutionTree: () => {
900
- const res = {};
901
- for (const [_pkgId, pkgIndex] of Object.entries(resolver)) {
902
- const pkgName = pkgIndex.pkg.name;
903
- res[pkgName] = {};
904
- for (const [surl, resolutions] of Object.entries(pkgIndex.canonicalResolution)) {
905
- const url = surl;
906
- res[pkgName][url] = [];
907
- for (const resolution of resolutions) {
908
- res[pkgName][url].push({ deep: resolution.deep, pkg: resolution.pkg });
909
- }
910
- }
911
- }
912
- return res;
913
- }
914
- };
915
- };
916
- var resolveFsElementGenealogy = (genealogy, path) => {
917
- const [top, ...rest] = path;
918
- if (top === void 0) return [];
919
- return genealogy.map((fs4) => {
920
- if (!fs4.elements) return void 0;
921
- let elem = fs4.elements?.[top];
922
- for (const k of rest) {
923
- elem = elem?.elements?.[k];
924
- }
925
- return elem;
926
- }).filter((elem) => elem !== void 0);
927
- };
928
- function fsElementSnapshot(genealogy) {
929
- const revGenealogy = genealogy.reverse();
930
- const snapshot = Object.assign({}, ...revGenealogy);
931
- snapshot.elements = void 0;
932
- return snapshot;
933
- }
934
-
935
743
  // src/typeschema/core/identifier.ts
936
744
  function dropVersionFromUrl(url) {
937
745
  const baseUrl = url.split("|")[0];
@@ -1002,7 +810,7 @@ function mkNestedIdentifier(register, fhirSchema, path, logger) {
1002
810
  const nestedTypeOrigins = {};
1003
811
  if (fhirSchema.derivation === "constraint") {
1004
812
  const specializations = register.resolveFsSpecializations(fhirSchema.package_meta, fhirSchema.url);
1005
- const nestedTypeGenealogy = specializations.map((fs4) => mkNestedTypes(register, fs4, logger)).filter((e) => e !== void 0).flat();
813
+ const nestedTypeGenealogy = specializations.map((fs5) => mkNestedTypes(register, fs5, logger)).filter((e) => e !== void 0).flat();
1006
814
  for (const nt of nestedTypeGenealogy.reverse()) {
1007
815
  nestedTypeOrigins[nt.identifier.name] = nt.identifier.url;
1008
816
  }
@@ -1101,10 +909,10 @@ function isRequired(register, fhirSchema, path) {
1101
909
  const fieldName = path[path.length - 1];
1102
910
  if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
1103
911
  const parentPath = path.slice(0, -1);
1104
- const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs4) => {
1105
- if (parentPath.length === 0) return fs4.required || [];
1106
- if (!fs4.elements) return [];
1107
- let elem = fs4;
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;
1108
916
  for (const k of parentPath) {
1109
917
  elem = elem?.elements?.[k];
1110
918
  }
@@ -1116,10 +924,10 @@ function isExcluded(register, fhirSchema, path) {
1116
924
  const fieldName = path[path.length - 1];
1117
925
  if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
1118
926
  const parentPath = path.slice(0, -1);
1119
- const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs4) => {
1120
- if (parentPath.length === 0) return fs4.excluded || [];
1121
- if (!fs4.elements) return [];
1122
- let elem = fs4;
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;
1123
931
  for (const k of parentPath) {
1124
932
  elem = elem?.elements?.[k];
1125
933
  }
@@ -1131,9 +939,9 @@ var buildReferences = (register, fhirSchema, element) => {
1131
939
  if (!element.refers) return void 0;
1132
940
  return element.refers.map((ref) => {
1133
941
  const curl = register.ensureSpecializationCanonicalUrl(ref);
1134
- const fs4 = register.resolveFs(fhirSchema.package_meta, curl);
1135
- if (!fs4) throw new Error(`Failed to resolve fs for ${curl}`);
1136
- return mkIdentifier(fs4);
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);
1137
945
  });
1138
946
  };
1139
947
  function buildFieldType(register, fhirSchema, path, element, logger) {
@@ -1211,7 +1019,7 @@ function extractValueSetConceptsByUrl(register, pkg, valueSetUrl, logger) {
1211
1019
  function extractValueSetConcepts(register, valueSet, _logger) {
1212
1020
  if (valueSet.expansion?.contains) {
1213
1021
  return valueSet.expansion.contains.filter((item) => item.code !== void 0).map((item) => {
1214
- assert(item.code);
1022
+ assert2(item.code);
1215
1023
  return {
1216
1024
  code: item.code,
1217
1025
  display: item.display,
@@ -1515,6 +1323,54 @@ var treeShake = (tsIndex, treeShake2, { resolutionTree, logger }) => {
1515
1323
  const shaked = collectDeps(focusedSchemas, {});
1516
1324
  return mkTypeSchemaIndex(shaked, { resolutionTree, logger });
1517
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
+ };
1518
1374
  var resourceRelatives = (schemas) => {
1519
1375
  const regularSchemas = schemas.filter((e) => isResourceTypeSchema(e) || isLogicalTypeSchema(e));
1520
1376
  const directPairs = [];
@@ -1560,7 +1416,7 @@ var mkTypeSchemaIndex = (schemas, { resolutionTree, logger }) => {
1560
1416
  append(schema);
1561
1417
  }
1562
1418
  const relations = resourceRelatives(schemas);
1563
- const resolve4 = (id) => index[id.url]?.[id.package];
1419
+ const resolve5 = (id) => index[id.url]?.[id.package];
1564
1420
  const resolveByUrl = (pkgName, url) => {
1565
1421
  if (resolutionTree) {
1566
1422
  const resolution = resolutionTree[pkgName]?.[url]?.[0];
@@ -1580,7 +1436,7 @@ var mkTypeSchemaIndex = (schemas, { resolutionTree, logger }) => {
1580
1436
  res.push(cur);
1581
1437
  const base = cur.base;
1582
1438
  if (base === void 0) break;
1583
- const resolved = resolve4(base);
1439
+ const resolved = resolve5(base);
1584
1440
  if (!resolved) {
1585
1441
  logger?.warn(
1586
1442
  `Failed to resolve base type: ${res.map((e) => `${e.identifier.url} (${e.identifier.kind})`).join(", ")}`
@@ -1606,7 +1462,7 @@ var mkTypeSchemaIndex = (schemas, { resolutionTree, logger }) => {
1606
1462
  return nonConstraintSchema;
1607
1463
  };
1608
1464
  const findLastSpecializationByIdentifier = (id) => {
1609
- const schema = resolve4(id);
1465
+ const schema = resolve5(id);
1610
1466
  if (!schema) return id;
1611
1467
  return findLastSpecialization(schema).identifier;
1612
1468
  };
@@ -1668,7 +1524,7 @@ var mkTypeSchemaIndex = (schemas, { resolutionTree, logger }) => {
1668
1524
  }
1669
1525
  }
1670
1526
  const raw = filename.endsWith(".yaml") ? YAML.stringify(tree) : JSON.stringify(tree, void 0, 2);
1671
- await afs2.mkdir(Path4.dirname(filename), { recursive: true });
1527
+ await afs2.mkdir(Path5.dirname(filename), { recursive: true });
1672
1528
  await afs2.writeFile(filename, raw);
1673
1529
  };
1674
1530
  return {
@@ -1678,7 +1534,7 @@ var mkTypeSchemaIndex = (schemas, { resolutionTree, logger }) => {
1678
1534
  collectResources: () => schemas.filter(isResourceTypeSchema),
1679
1535
  collectLogicalModels: () => schemas.filter(isLogicalTypeSchema),
1680
1536
  collectProfiles: () => schemas.filter(isProfileTypeSchema),
1681
- resolve: resolve4,
1537
+ resolve: resolve5,
1682
1538
  resolveByUrl,
1683
1539
  resourceChildren,
1684
1540
  tryHierarchy,
@@ -1691,46 +1547,773 @@ var mkTypeSchemaIndex = (schemas, { resolutionTree, logger }) => {
1691
1547
  };
1692
1548
  };
1693
1549
 
1694
- // src/typeschema/index.ts
1695
- var codeableReferenceInR4 = "Use CodeableReference which is not provided by FHIR R4.";
1696
- var availabilityInR4 = "Use Availability which is not provided by FHIR R4.";
1697
- var skipMe = {
1698
- "hl7.fhir.uv.extensions.r4#1.0.0": {
1699
- "http://hl7.org/fhir/StructureDefinition/extended-contact-availability": availabilityInR4,
1700
- "http://hl7.org/fhir/StructureDefinition/immunization-procedure": codeableReferenceInR4,
1701
- "http://hl7.org/fhir/StructureDefinition/specimen-additive": codeableReferenceInR4,
1702
- "http://hl7.org/fhir/StructureDefinition/workflow-barrier": codeableReferenceInR4,
1703
- "http://hl7.org/fhir/StructureDefinition/workflow-protectiveFactor": codeableReferenceInR4,
1704
- "http://hl7.org/fhir/StructureDefinition/workflow-reason": codeableReferenceInR4
1705
- },
1706
- "hl7.fhir.r5.core#5.0.0": {
1707
- "http://hl7.org/fhir/StructureDefinition/shareablecodesystem": "FIXME: CodeSystem.concept.concept defined by ElementReference. FHIR Schema generator output broken value in it, so we just skip it for now."
1550
+ // src/api/writer-generator/python.ts
1551
+ var PRIMITIVE_TYPE_MAP2 = {
1552
+ boolean: "bool",
1553
+ instant: "str",
1554
+ time: "str",
1555
+ date: "str",
1556
+ dateTime: "str",
1557
+ decimal: "float",
1558
+ integer: "int",
1559
+ unsignedInt: "int",
1560
+ positiveInt: "PositiveInt",
1561
+ integer64: "int",
1562
+ base64Binary: "str",
1563
+ uri: "str",
1564
+ url: "str",
1565
+ canonical: "str",
1566
+ oid: "str",
1567
+ uuid: "str",
1568
+ string: "str",
1569
+ code: "str",
1570
+ markdown: "str",
1571
+ id: "str",
1572
+ xhtml: "str"
1573
+ };
1574
+ var AVAILABLE_STRING_FORMATS = {
1575
+ snake_case: snakeCase,
1576
+ PascalCase: pascalCase,
1577
+ camelCase
1578
+ };
1579
+ var PYTHON_KEYWORDS = /* @__PURE__ */ new Set([
1580
+ "False",
1581
+ "None",
1582
+ "True",
1583
+ "and",
1584
+ "as",
1585
+ "assert",
1586
+ "async",
1587
+ "await",
1588
+ "break",
1589
+ "class",
1590
+ "continue",
1591
+ "def",
1592
+ "del",
1593
+ "elif",
1594
+ "else",
1595
+ "except",
1596
+ "finally",
1597
+ "for",
1598
+ "from",
1599
+ "global",
1600
+ "if",
1601
+ "import",
1602
+ "in",
1603
+ "is",
1604
+ "lambda",
1605
+ "nonlocal",
1606
+ "not",
1607
+ "or",
1608
+ "pass",
1609
+ "raise",
1610
+ "return",
1611
+ "try",
1612
+ "while",
1613
+ "with",
1614
+ "yield",
1615
+ "List"
1616
+ ]);
1617
+ var MAX_IMPORT_LINE_LENGTH = 100;
1618
+ var fixReservedWords = (name) => {
1619
+ return PYTHON_KEYWORDS.has(name) ? `${name}_` : name;
1620
+ };
1621
+ var injectSuperClasses = (name) => {
1622
+ return name === "Resource" || name === "Element" ? ["BaseModel"] : [];
1623
+ };
1624
+ var canonicalToName2 = (canonical, dropFragment = true) => {
1625
+ if (!canonical) return void 0;
1626
+ let localName = canonical.split("/").pop();
1627
+ if (!localName) return void 0;
1628
+ if (dropFragment && localName.includes("#")) {
1629
+ localName = localName.split("#")[0];
1630
+ }
1631
+ if (!localName) return void 0;
1632
+ if (/^\d/.test(localName)) {
1633
+ localName = `number_${localName}`;
1708
1634
  }
1635
+ return snakeCase(localName);
1709
1636
  };
1710
- var generateTypeSchemas = async (register, logger) => {
1711
- const fhirSchemas = [];
1712
- for (const fhirSchema of register.allFs()) {
1713
- const pkgId = packageMetaToFhir(fhirSchema.package_meta);
1714
- if (skipMe[pkgId]?.[fhirSchema.url]) {
1715
- logger?.dry_warn(`Skip ${fhirSchema.url} from ${pkgId}. Reason: ${skipMe[pkgId]?.[fhirSchema.url]}`);
1716
- continue;
1717
- }
1718
- fhirSchemas.push(...await transformFhirSchema(register, fhirSchema, logger));
1637
+ var deriveResourceName = (id) => {
1638
+ if (id.kind === "nested") {
1639
+ const url = id.url;
1640
+ const path = canonicalToName2(url, false);
1641
+ if (!path) return "";
1642
+ const [resourceName, fragment] = path.split("#");
1643
+ const name = uppercaseFirstLetterOfEach((fragment ?? "").split(".")).join("");
1644
+ return pascalCase([resourceName, name].join(""));
1719
1645
  }
1720
- for (const vsSchema of register.allVs()) {
1721
- fhirSchemas.push(await transformValueSet(register, vsSchema));
1646
+ return pascalCase(id.name);
1647
+ };
1648
+ var resolvePyAssets = (fn) => {
1649
+ const __dirname = Path5.dirname(fileURLToPath(import.meta.url));
1650
+ if (__filename.endsWith("dist/index.js")) {
1651
+ return Path5.resolve(__dirname, "..", "assets", "api", "writer-generator", "python", fn);
1652
+ } else {
1653
+ return Path5.resolve(__dirname, "../../..", "assets", "api", "writer-generator", "python", fn);
1722
1654
  }
1723
- return fhirSchemas;
1724
1655
  };
1725
-
1726
- // src/api/writer-generator/typescript.ts
1727
- var primitiveType2tsType = {
1728
- boolean: "boolean",
1729
- instant: "string",
1730
- time: "string",
1731
- date: "string",
1732
- dateTime: "string",
1733
- decimal: "number",
1656
+ var Python = class extends Writer {
1657
+ nameFormatFunction;
1658
+ tsIndex;
1659
+ constructor(options) {
1660
+ super(options);
1661
+ this.nameFormatFunction = this.getFieldFormatFunction(options.fieldFormat);
1662
+ }
1663
+ async generate(tsIndex) {
1664
+ this.tsIndex = tsIndex;
1665
+ const groups = {
1666
+ groupedComplexTypes: groupByPackages(tsIndex.collectComplexTypes()),
1667
+ groupedResources: groupByPackages(tsIndex.collectResources())
1668
+ };
1669
+ this.generateRootPackages(groups);
1670
+ this.generateSDKPackages(groups);
1671
+ }
1672
+ generateRootPackages(groups) {
1673
+ this.generateRootInitFile(groups);
1674
+ fs__default.cpSync(resolvePyAssets("requirements.txt"), Path5.resolve(this.opts.outputDir, "requirements.txt"));
1675
+ }
1676
+ generateSDKPackages(groups) {
1677
+ this.generateComplexTypesPackages(groups.groupedComplexTypes);
1678
+ this.generateResourcePackages(groups);
1679
+ }
1680
+ generateComplexTypesPackages(groupedComplexTypes) {
1681
+ for (const [packageName, packageComplexTypes] of Object.entries(groupedComplexTypes)) {
1682
+ this.cd(`/${snakeCase(packageName)}`, () => {
1683
+ this.generateBasePy(packageComplexTypes);
1684
+ });
1685
+ }
1686
+ }
1687
+ generateResourcePackages(groups) {
1688
+ for (const [packageName, packageResources] of Object.entries(groups.groupedResources)) {
1689
+ this.cd(`/${snakeCase(packageName)}`, () => {
1690
+ this.generateResourcePackageContent(
1691
+ packageName,
1692
+ packageResources,
1693
+ groups.groupedComplexTypes[packageName] || []
1694
+ );
1695
+ });
1696
+ }
1697
+ }
1698
+ generateResourcePackageContent(packageName, packageResources, packageComplexTypes) {
1699
+ const pyPackageName = this.pyFhirPackageByName(packageName);
1700
+ this.generateResourcePackageInit(pyPackageName, packageResources, packageComplexTypes);
1701
+ this.generateResourceFamilies(packageResources);
1702
+ for (const schema of packageResources) {
1703
+ this.generateResourceModule(schema);
1704
+ }
1705
+ }
1706
+ generateRootInitFile(groups) {
1707
+ this.cd("/", () => {
1708
+ this.cat("__init__.py", () => {
1709
+ this.generateDisclaimer();
1710
+ const pydanticModels = this.collectAndImportAllModels(groups);
1711
+ this.generateModelRebuilds(pydanticModels);
1712
+ });
1713
+ });
1714
+ }
1715
+ collectAndImportAllModels(groups) {
1716
+ const models = [];
1717
+ for (const packageName of Object.keys(groups.groupedResources)) {
1718
+ const fullPyPackageName = this.pyFhirPackageByName(packageName);
1719
+ models.push(...this.importComplexTypes(fullPyPackageName, groups.groupedComplexTypes[packageName]));
1720
+ models.push(...this.importResources(fullPyPackageName, false, groups.groupedResources[packageName]));
1721
+ }
1722
+ this.line();
1723
+ return models;
1724
+ }
1725
+ generateModelRebuilds(models) {
1726
+ for (const modelName of models.sort()) {
1727
+ this.line(`${modelName}.model_rebuild()`);
1728
+ }
1729
+ }
1730
+ generateBasePy(packageComplexTypes) {
1731
+ this.cat("base.py", () => {
1732
+ this.generateDisclaimer();
1733
+ this.generateDefaultImports();
1734
+ this.line();
1735
+ this.generateComplexTypes(packageComplexTypes);
1736
+ this.line();
1737
+ });
1738
+ }
1739
+ generateComplexTypes(complexTypes) {
1740
+ for (const schema of sortAsDeclarationSequence(complexTypes)) {
1741
+ this.generateNestedTypes(schema);
1742
+ this.line();
1743
+ this.generateType(schema);
1744
+ }
1745
+ }
1746
+ generateResourcePackageInit(fullPyPackageName, packageResources, packageComplexTypes) {
1747
+ this.cat("__init__.py", () => {
1748
+ this.generateDisclaimer();
1749
+ this.importComplexTypes(fullPyPackageName, packageComplexTypes);
1750
+ const allResourceNames = this.importResources(fullPyPackageName, true, packageResources);
1751
+ this.line();
1752
+ this.generateExportsDeclaration(packageComplexTypes, allResourceNames);
1753
+ });
1754
+ }
1755
+ importComplexTypes(fullPyPackageName, packageComplexTypes) {
1756
+ if (!packageComplexTypes || packageComplexTypes.length === 0) return [];
1757
+ const baseTypes = packageComplexTypes.map((t) => t.identifier.name).sort();
1758
+ this.pyImportFrom(`${fullPyPackageName}.base`, ...baseTypes);
1759
+ this.line();
1760
+ return baseTypes;
1761
+ }
1762
+ buildImportLine(remaining, maxImportLineLength) {
1763
+ let line = "";
1764
+ while (remaining.length > 0 && line.length < maxImportLineLength) {
1765
+ const entity = remaining.shift();
1766
+ if (!entity) throw new Error("Unexpected empty entity");
1767
+ if (line.length > 0) {
1768
+ line += ", ";
1769
+ }
1770
+ line += entity;
1771
+ }
1772
+ if (remaining.length > 0) {
1773
+ line += ", \\";
1774
+ }
1775
+ return line;
1776
+ }
1777
+ importResources(fullPyPackageName, importEmptyResources, packageResources) {
1778
+ if (!packageResources || packageResources.length === 0) return [];
1779
+ const allResourceNames = [];
1780
+ for (const resource of packageResources) {
1781
+ const names = this.importOneResource(resource, fullPyPackageName);
1782
+ if (!importEmptyResources && !resource.fields) continue;
1783
+ allResourceNames.push(...names);
1784
+ }
1785
+ return allResourceNames;
1786
+ }
1787
+ importOneResource(resource, fullPyPackageName) {
1788
+ const moduleName = `${fullPyPackageName}.${snakeCase(resource.identifier.name)}`;
1789
+ const importNames = this.collectResourceImportNames(resource);
1790
+ this.pyImportFrom(moduleName, ...importNames);
1791
+ const names = [...importNames];
1792
+ if (this.shouldImportResourceFamily(resource)) {
1793
+ const familyName = `${resource.identifier.name}Family`;
1794
+ this.pyImportFrom(`${fullPyPackageName}.resource_families`, familyName);
1795
+ }
1796
+ return names;
1797
+ }
1798
+ collectResourceImportNames(resource) {
1799
+ const names = [deriveResourceName(resource.identifier)];
1800
+ for (const nested of resource.nested ?? []) {
1801
+ const nestedName = deriveResourceName(nested.identifier);
1802
+ names.push(nestedName);
1803
+ }
1804
+ return names;
1805
+ }
1806
+ shouldImportResourceFamily(resource) {
1807
+ assert2(this.tsIndex !== void 0);
1808
+ return resource.identifier.kind === "resource" && this.tsIndex.resourceChildren(resource.identifier).length > 0;
1809
+ }
1810
+ generateExportsDeclaration(packageComplexTypes, allResourceNames) {
1811
+ this.squareBlock(["__all__", "="], () => {
1812
+ const allExports = [
1813
+ ...(packageComplexTypes || []).map((t) => t.identifier.name),
1814
+ ...allResourceNames
1815
+ ].sort();
1816
+ for (const schemaName of allExports) {
1817
+ this.line(`'${schemaName}',`);
1818
+ }
1819
+ });
1820
+ }
1821
+ generateResourceModule(schema) {
1822
+ this.cat(`${snakeCase(schema.identifier.name)}.py`, () => {
1823
+ this.generateDisclaimer();
1824
+ this.generateDefaultImports();
1825
+ this.line();
1826
+ this.generateDependenciesImports(schema);
1827
+ this.line();
1828
+ this.generateNestedTypes(schema);
1829
+ this.line();
1830
+ this.generateType(schema);
1831
+ });
1832
+ }
1833
+ generateType(schema) {
1834
+ const className = deriveResourceName(schema.identifier);
1835
+ const superClasses = this.getSuperClasses(schema);
1836
+ this.line(`class ${className}(${superClasses.join(", ")}):`);
1837
+ this.indentBlock(() => {
1838
+ this.generateClassBody(schema);
1839
+ });
1840
+ this.line();
1841
+ }
1842
+ getSuperClasses(schema) {
1843
+ return [...schema.base ? [schema.base.name] : [], ...injectSuperClasses(schema.identifier.name)];
1844
+ }
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
+ }
1858
+ }
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}")`);
1862
+ }
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(")");
1873
+ }
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}`);
1880
+ }
1881
+ }
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
+ };
1891
+ }
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}]`;
1897
+ }
1898
+ if (field.array) {
1899
+ fieldType = `PyList[${fieldType}]`;
1900
+ }
1901
+ if (!field.required) {
1902
+ fieldType = `${fieldType} | None`;
1903
+ }
1904
+ return fieldType;
1905
+ }
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})`;
1917
+ }
1918
+ return ` = Field(${aliasSpec})`;
1919
+ }
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)");
1929
+ }
1930
+ generateNestedTypes(schema) {
1931
+ if (!schema.nested) return;
1932
+ this.line();
1933
+ for (const subtype of schema.nested) {
1934
+ this.generateType(subtype);
1935
+ }
1936
+ }
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());
1952
+ }
1953
+ }
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);
1961
+ }
1962
+ }
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] = [];
1969
+ }
1970
+ grouped[pyPackage].push(dep.name);
1971
+ }
1972
+ return grouped;
1973
+ }
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
+ }
1981
+ }
1982
+ shouldUseSingleLineImport(oneLine, entities) {
1983
+ return oneLine.length <= MAX_IMPORT_LINE_LENGTH || entities.length === 1;
1984
+ }
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);
1992
+ }
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;
2013
+ }
2014
+ }
2015
+ const exportList = Object.keys(families);
2016
+ if (exportList.length === 0) return;
2017
+ this.buildResourceFamiliesFile(packages, families, exportList);
2018
+ }
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
+ });
2027
+ }
2028
+ includeResourceFamilyValidator() {
2029
+ const content = fs__default.readFileSync(resolvePyAssets("resource_family_validator.py"), "utf-8");
2030
+ this.line(content);
2031
+ }
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);
2037
+ }
2038
+ }
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();
2048
+ }
2049
+ generateFamilyExports(exportList) {
2050
+ this.line(`__all__ = [${exportList.map((e) => `'${e}'`).join(", ")}]`);
2051
+ }
2052
+ buildPyPackageName(packageName) {
2053
+ const parts = packageName ? [snakeCase(packageName)] : [""];
2054
+ return parts.join(".");
2055
+ }
2056
+ pyFhirPackage(identifier) {
2057
+ return this.pyFhirPackageByName(identifier.package);
2058
+ }
2059
+ pyFhirPackageByName(name) {
2060
+ return [this.opts.rootPackageName, this.buildPyPackageName(name)].join(".");
2061
+ }
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(".");
2068
+ }
2069
+ return this.pyFhirPackage(identifier);
2070
+ }
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];
2078
+ }
2079
+ };
2080
+
2081
+ // src/fhir-types/hl7-fhir-r4-core/CodeSystem.ts
2082
+ var isCodeSystem = (resource) => {
2083
+ return resource !== null && typeof resource === "object" && resource.resourceType === "CodeSystem";
2084
+ };
2085
+
2086
+ // src/fhir-types/hl7-fhir-r4-core/ValueSet.ts
2087
+ var isValueSet = (resource) => {
2088
+ return resource !== null && typeof resource === "object" && resource.resourceType === "ValueSet";
2089
+ };
2090
+
2091
+ // src/typeschema/register.ts
2092
+ var readPackageDependencies = async (manager, packageMeta) => {
2093
+ const packageJSON = await manager.packageJson(packageMeta.name);
2094
+ const dependencies = packageJSON.dependencies;
2095
+ if (dependencies !== void 0) {
2096
+ return Object.entries(dependencies).map(([name, version]) => {
2097
+ return { name, version };
2098
+ });
2099
+ }
2100
+ return [];
2101
+ };
2102
+ var mkEmptyPkgIndex = (pkg) => {
2103
+ return {
2104
+ pkg,
2105
+ canonicalResolution: {},
2106
+ fhirSchemas: {},
2107
+ valueSets: {}
2108
+ };
2109
+ };
2110
+ var mkPackageAwareResolver = async (manager, pkg, deep, acc, logger) => {
2111
+ const pkgId = packageMetaToFhir(pkg);
2112
+ logger?.info(`${" ".repeat(deep * 2)}+ ${pkgId}`);
2113
+ if (acc[pkgId]) return acc[pkgId];
2114
+ const index = mkEmptyPkgIndex(pkg);
2115
+ for (const resource of await manager.search({ package: pkg })) {
2116
+ const rawUrl = resource.url;
2117
+ if (!rawUrl) continue;
2118
+ if (!(isStructureDefinition(resource) || isValueSet(resource) || isCodeSystem(resource))) continue;
2119
+ const url = rawUrl;
2120
+ if (index.canonicalResolution[url]) logger?.dry_warn(`Duplicate canonical URL: ${url} at ${pkgId}.`);
2121
+ index.canonicalResolution[url] = [{ deep, pkg, pkgId, resource }];
2122
+ }
2123
+ const deps = await readPackageDependencies(manager, pkg);
2124
+ for (const depPkg of deps) {
2125
+ const { canonicalResolution } = await mkPackageAwareResolver(manager, depPkg, deep + 1, acc, logger);
2126
+ for (const [surl, resolutions] of Object.entries(canonicalResolution)) {
2127
+ const url = surl;
2128
+ index.canonicalResolution[url] = [...index.canonicalResolution[url] || [], ...resolutions];
2129
+ }
2130
+ }
2131
+ for (const resolutionOptions of Object.values(index.canonicalResolution)) {
2132
+ resolutionOptions.sort((a, b) => a.deep - b.deep);
2133
+ }
2134
+ acc[pkgId] = index;
2135
+ return index;
2136
+ };
2137
+ var packageAgnosticResolveCanonical = (resolver, url, _logger) => {
2138
+ const options = Object.values(resolver).flatMap((pkg) => pkg.canonicalResolution[url]);
2139
+ if (!options) throw new Error(`No canonical resolution found for ${url} in any package`);
2140
+ return options[0]?.resource;
2141
+ };
2142
+ var registerFromManager = async (manager, { logger, fallbackPackageForNameResolution, focusedPackages }) => {
2143
+ const packages = focusedPackages ?? await manager.packages();
2144
+ const resolver = {};
2145
+ for (const pkg of packages) {
2146
+ await mkPackageAwareResolver(manager, pkg, 0, resolver, logger);
2147
+ }
2148
+ for (const { pkg, canonicalResolution } of Object.values(resolver)) {
2149
+ const pkgId = packageMetaToFhir(pkg);
2150
+ if (!resolver[pkgId]) throw new Error(`Package ${pkgId} not found`);
2151
+ let counter = 0;
2152
+ logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' begins...`);
2153
+ for (const [_url, options] of Object.entries(canonicalResolution)) {
2154
+ const resolition = options[0];
2155
+ if (!resolition) throw new Error(`Resource not found`);
2156
+ const resource = resolition.resource;
2157
+ const resourcePkg = resolition.pkg;
2158
+ if (isStructureDefinition(resource)) {
2159
+ const rfs = enrichFHIRSchema(
2160
+ fhirschema.translate(resource),
2161
+ resourcePkg
2162
+ );
2163
+ counter++;
2164
+ resolver[pkgId].fhirSchemas[rfs.url] = rfs;
2165
+ }
2166
+ if (isValueSet(resource)) {
2167
+ const rvs = enrichValueSet(resource, resourcePkg);
2168
+ resolver[pkgId].valueSets[rvs.url] = rvs;
2169
+ }
2170
+ }
2171
+ logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' completed: ${counter} successful`);
2172
+ }
2173
+ const resolveFs = (pkg, canonicalUrl) => {
2174
+ return resolver[packageMetaToFhir(pkg)]?.fhirSchemas[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.fhirSchemas[canonicalUrl];
2175
+ };
2176
+ const resolveVs = (pkg, canonicalUrl) => {
2177
+ return resolver[packageMetaToFhir(pkg)]?.valueSets[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.valueSets[canonicalUrl];
2178
+ };
2179
+ const ensureSpecializationCanonicalUrl = (name) => name.match(/^[a-zA-Z0-9]+$/) && `http://hl7.org/fhir/StructureDefinition/${name}` || name;
2180
+ const resolveFsGenealogy = (pkg, canonicalUrl) => {
2181
+ let fs5 = resolveFs(pkg, canonicalUrl);
2182
+ if (fs5 === void 0) throw new Error(`Failed to resolve FHIR Schema: '${canonicalUrl}'`);
2183
+ const genealogy = [fs5];
2184
+ while (fs5?.base) {
2185
+ const pkg2 = fs5.package_meta;
2186
+ const baseUrl = ensureSpecializationCanonicalUrl(fs5.base);
2187
+ fs5 = resolveFs(pkg2, baseUrl);
2188
+ if (fs5 === void 0)
2189
+ throw new Error(
2190
+ `Failed to resolve FHIR Schema base for '${canonicalUrl}'. Problem: '${baseUrl}' from '${packageMetaToFhir(pkg2)}'`
2191
+ );
2192
+ genealogy.push(fs5);
2193
+ }
2194
+ return genealogy;
2195
+ };
2196
+ const resolveFsSpecializations = (pkg, canonicalUrl) => {
2197
+ return resolveFsGenealogy(pkg, canonicalUrl).filter((fs5) => fs5.derivation === "specialization");
2198
+ };
2199
+ const resolveElementSnapshot = (fhirSchema, path) => {
2200
+ const geneology = resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url);
2201
+ const elemGeneology = resolveFsElementGenealogy(geneology, path);
2202
+ const elemSnapshot = fsElementSnapshot(elemGeneology);
2203
+ return elemSnapshot;
2204
+ };
2205
+ const getAllElementKeys = (elems) => {
2206
+ const keys = /* @__PURE__ */ new Set();
2207
+ for (const [key, elem] of Object.entries(elems)) {
2208
+ keys.add(key);
2209
+ for (const choiceKey of elem?.choices || []) {
2210
+ if (!elems[choiceKey]) {
2211
+ keys.add(choiceKey);
2212
+ }
2213
+ }
2214
+ }
2215
+ return Array.from(keys);
2216
+ };
2217
+ return {
2218
+ ...manager,
2219
+ testAppendFs(fs5) {
2220
+ const rfs = enrichFHIRSchema(fs5);
2221
+ const pkgId = packageMetaToFhir(rfs.package_meta);
2222
+ if (!resolver[pkgId]) resolver[pkgId] = mkEmptyPkgIndex(rfs.package_meta);
2223
+ resolver[pkgId].fhirSchemas[rfs.url] = rfs;
2224
+ },
2225
+ resolveFs,
2226
+ resolveFsGenealogy,
2227
+ resolveFsSpecializations,
2228
+ ensureSpecializationCanonicalUrl,
2229
+ resolveSd: (_pkg, canonicalUrl) => {
2230
+ const res = packageAgnosticResolveCanonical(resolver, canonicalUrl);
2231
+ if (isStructureDefinition(res)) return res;
2232
+ return void 0;
2233
+ },
2234
+ allFs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.fhirSchemas)),
2235
+ allVs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.valueSets)),
2236
+ resolveVs,
2237
+ resolveAny: (canonicalUrl) => packageAgnosticResolveCanonical(resolver, canonicalUrl),
2238
+ resolveElementSnapshot,
2239
+ getAllElementKeys,
2240
+ resolver,
2241
+ resolutionTree: () => {
2242
+ const res = {};
2243
+ for (const [_pkgId, pkgIndex] of Object.entries(resolver)) {
2244
+ const pkgName = pkgIndex.pkg.name;
2245
+ res[pkgName] = {};
2246
+ for (const [surl, resolutions] of Object.entries(pkgIndex.canonicalResolution)) {
2247
+ const url = surl;
2248
+ res[pkgName][url] = [];
2249
+ for (const resolution of resolutions) {
2250
+ res[pkgName][url].push({ deep: resolution.deep, pkg: resolution.pkg });
2251
+ }
2252
+ }
2253
+ }
2254
+ return res;
2255
+ }
2256
+ };
2257
+ };
2258
+ var resolveFsElementGenealogy = (genealogy, path) => {
2259
+ const [top, ...rest] = path;
2260
+ if (top === void 0) return [];
2261
+ return genealogy.map((fs5) => {
2262
+ if (!fs5.elements) return void 0;
2263
+ let elem = fs5.elements?.[top];
2264
+ for (const k of rest) {
2265
+ elem = elem?.elements?.[k];
2266
+ }
2267
+ return elem;
2268
+ }).filter((elem) => elem !== void 0);
2269
+ };
2270
+ function fsElementSnapshot(genealogy) {
2271
+ const revGenealogy = genealogy.reverse();
2272
+ const snapshot = Object.assign({}, ...revGenealogy);
2273
+ snapshot.elements = void 0;
2274
+ return snapshot;
2275
+ }
2276
+
2277
+ // src/typeschema/index.ts
2278
+ var codeableReferenceInR4 = "Use CodeableReference which is not provided by FHIR R4.";
2279
+ var availabilityInR4 = "Use Availability which is not provided by FHIR R4.";
2280
+ var skipMe = {
2281
+ "hl7.fhir.uv.extensions.r4#1.0.0": {
2282
+ "http://hl7.org/fhir/StructureDefinition/extended-contact-availability": availabilityInR4,
2283
+ "http://hl7.org/fhir/StructureDefinition/immunization-procedure": codeableReferenceInR4,
2284
+ "http://hl7.org/fhir/StructureDefinition/specimen-additive": codeableReferenceInR4,
2285
+ "http://hl7.org/fhir/StructureDefinition/workflow-barrier": codeableReferenceInR4,
2286
+ "http://hl7.org/fhir/StructureDefinition/workflow-protectiveFactor": codeableReferenceInR4,
2287
+ "http://hl7.org/fhir/StructureDefinition/workflow-reason": codeableReferenceInR4
2288
+ },
2289
+ "hl7.fhir.r5.core#5.0.0": {
2290
+ "http://hl7.org/fhir/StructureDefinition/shareablecodesystem": "FIXME: CodeSystem.concept.concept defined by ElementReference. FHIR Schema generator output broken value in it, so we just skip it for now."
2291
+ }
2292
+ };
2293
+ var generateTypeSchemas = async (register, logger) => {
2294
+ const fhirSchemas = [];
2295
+ for (const fhirSchema of register.allFs()) {
2296
+ const pkgId = packageMetaToFhir(fhirSchema.package_meta);
2297
+ if (skipMe[pkgId]?.[fhirSchema.url]) {
2298
+ logger?.dry_warn(`Skip ${fhirSchema.url} from ${pkgId}. Reason: ${skipMe[pkgId]?.[fhirSchema.url]}`);
2299
+ continue;
2300
+ }
2301
+ fhirSchemas.push(...await transformFhirSchema(register, fhirSchema, logger));
2302
+ }
2303
+ for (const vsSchema of register.allVs()) {
2304
+ fhirSchemas.push(await transformValueSet(register, vsSchema));
2305
+ }
2306
+ return fhirSchemas;
2307
+ };
2308
+
2309
+ // src/api/writer-generator/typescript.ts
2310
+ var primitiveType2tsType = {
2311
+ boolean: "boolean",
2312
+ instant: "string",
2313
+ time: "string",
2314
+ date: "string",
2315
+ dateTime: "string",
2316
+ decimal: "number",
1734
2317
  integer: "number",
1735
2318
  unsignedInt: "number",
1736
2319
  positiveInt: "number",
@@ -1765,7 +2348,7 @@ var tsModuleFileName = (id) => {
1765
2348
  var tsModulePath = (id) => {
1766
2349
  return `${tsFhirPackageDir(id.package)}/${tsModuleName(id)}`;
1767
2350
  };
1768
- var canonicalToName2 = (canonical, dropFragment = true) => {
2351
+ var canonicalToName3 = (canonical, dropFragment = true) => {
1769
2352
  if (!canonical) return void 0;
1770
2353
  const localName = extractNameFromCanonical(canonical, dropFragment);
1771
2354
  if (!localName) return void 0;
@@ -1774,7 +2357,7 @@ var canonicalToName2 = (canonical, dropFragment = true) => {
1774
2357
  var tsResourceName = (id) => {
1775
2358
  if (id.kind === "nested") {
1776
2359
  const url = id.url;
1777
- const path = canonicalToName2(url, false);
2360
+ const path = canonicalToName3(url, false);
1778
2361
  if (!path) return "";
1779
2362
  const [resourceName, fragment] = path.split("#");
1780
2363
  const name = uppercaseFirstLetterOfEach((fragment ?? "").split(".")).join("");
@@ -1840,7 +2423,7 @@ var TypeScript = class extends Writer {
1840
2423
  });
1841
2424
  } else if (isNestedIdentifier(dep)) {
1842
2425
  const ndep = { ...dep };
1843
- ndep.name = canonicalToName2(dep.url);
2426
+ ndep.name = canonicalToName3(dep.url);
1844
2427
  imports.push({
1845
2428
  tsPackage: `../${tsModulePath(ndep)}`,
1846
2429
  name: tsResourceName(dep),
@@ -1893,7 +2476,7 @@ var TypeScript = class extends Writer {
1893
2476
  name = tsResourceName(schema.identifier);
1894
2477
  }
1895
2478
  let extendsClause;
1896
- if (schema.base) extendsClause = `extends ${canonicalToName2(schema.base.url)}`;
2479
+ if (schema.base) extendsClause = `extends ${canonicalToName3(schema.base.url)}`;
1897
2480
  this.debugComment(schema.identifier);
1898
2481
  if (!schema.fields && !extendsClause && !isResourceTypeSchema(schema)) {
1899
2482
  this.lineSM(`export type ${name} = object`);
@@ -2200,7 +2783,7 @@ var writeTypeSchemasToSeparateFiles = async (typeSchemas, outputDir, logger) =>
2200
2783
  const pkgPath = normalizeFileName(packageMetaToFhir(pkg));
2201
2784
  const name = normalizeFileName(`${ts.identifier.name}(${extractNameFromCanonical(ts.identifier.url)})`);
2202
2785
  const json = JSON.stringify(ts, null, 2);
2203
- const baseName = Path4.join(outputDir, pkgPath, name);
2786
+ const baseName = Path5.join(outputDir, pkgPath, name);
2204
2787
  if (!files[baseName]) files[baseName] = [];
2205
2788
  if (!files[baseName]?.some((e) => e === json)) {
2206
2789
  files[baseName].push(json);
@@ -2215,7 +2798,7 @@ var writeTypeSchemasToSeparateFiles = async (typeSchemas, outputDir, logger) =>
2215
2798
  } else {
2216
2799
  fullName = `${baseName}-${index}.typeschema.json`;
2217
2800
  }
2218
- await afs2.mkdir(Path4.dirname(fullName), { recursive: true });
2801
+ await afs2.mkdir(Path5.dirname(fullName), { recursive: true });
2219
2802
  await afs2.writeFile(fullName, json);
2220
2803
  })
2221
2804
  );
@@ -2223,7 +2806,7 @@ var writeTypeSchemasToSeparateFiles = async (typeSchemas, outputDir, logger) =>
2223
2806
  };
2224
2807
  var writeTypeSchemasToSingleFile = async (typeSchemas, outputFile, logger) => {
2225
2808
  logger.info(`Writing TypeSchema files to: ${outputFile}`);
2226
- await afs2.mkdir(Path4.dirname(outputFile), { recursive: true });
2809
+ await afs2.mkdir(Path5.dirname(outputFile), { recursive: true });
2227
2810
  logger.info(`Writing TypeSchemas to one file ${outputFile}...`);
2228
2811
  for (const ts of typeSchemas) {
2229
2812
  const json = JSON.stringify(ts, null, 2);
@@ -2234,7 +2817,7 @@ var writeTypeSchemasToSingleFile = async (typeSchemas, outputFile, logger) => {
2234
2817
  var tryWriteTypeSchema = async (typeSchemas, opts, logger) => {
2235
2818
  if (!opts.typeSchemaOutputDir) return;
2236
2819
  try {
2237
- if (Path4.extname(opts.typeSchemaOutputDir) === ".ndjson") {
2820
+ if (Path5.extname(opts.typeSchemaOutputDir) === ".ndjson") {
2238
2821
  await writeTypeSchemasToSingleFile(typeSchemas, opts.typeSchemaOutputDir, logger);
2239
2822
  } else {
2240
2823
  await writeTypeSchemasToSeparateFiles(typeSchemas, opts.typeSchemaOutputDir, logger);
@@ -2297,7 +2880,7 @@ var APIBuilder = class {
2297
2880
  return this;
2298
2881
  }
2299
2882
  localTgzPackage(archivePath) {
2300
- this.localTgzArchives.push(Path4.resolve(archivePath));
2883
+ this.localTgzArchives.push(Path5.resolve(archivePath));
2301
2884
  return this;
2302
2885
  }
2303
2886
  fromSchemas(schemas) {
@@ -2308,7 +2891,7 @@ var APIBuilder = class {
2308
2891
  typescript(userOpts) {
2309
2892
  const defaultWriterOpts = {
2310
2893
  logger: this.logger,
2311
- outputDir: Path4.join(this.options.outputDir, "/types"),
2894
+ outputDir: Path5.join(this.options.outputDir, "/types"),
2312
2895
  tabSize: 4,
2313
2896
  withDebugComment: false,
2314
2897
  commentLinePrefix: "//",
@@ -2328,9 +2911,31 @@ var APIBuilder = class {
2328
2911
  this.logger.debug(`Configured TypeScript generator (${JSON.stringify(opts, void 0, 2)})`);
2329
2912
  return this;
2330
2913
  }
2914
+ python(userOptions) {
2915
+ const defaultWriterOpts = {
2916
+ logger: this.logger,
2917
+ outputDir: this.options.outputDir,
2918
+ tabSize: 4,
2919
+ withDebugComment: false,
2920
+ commentLinePrefix: "#"
2921
+ };
2922
+ const defaultPyOpts = {
2923
+ ...defaultWriterOpts,
2924
+ rootPackageName: "fhir_types",
2925
+ fieldFormat: "snake_case"
2926
+ };
2927
+ const opts = {
2928
+ ...defaultPyOpts,
2929
+ ...Object.fromEntries(Object.entries(userOptions).filter(([_, v]) => v !== void 0))
2930
+ };
2931
+ const generator = new Python(opts);
2932
+ this.generators.set("python", generator);
2933
+ this.logger.debug(`Configured python generator`);
2934
+ return this;
2935
+ }
2331
2936
  csharp(namespace, staticSourceDir) {
2332
2937
  const generator = new CSharp({
2333
- outputDir: Path4.join(this.options.outputDir, "/types"),
2938
+ outputDir: Path5.join(this.options.outputDir, "/types"),
2334
2939
  staticSourceDir: staticSourceDir ?? void 0,
2335
2940
  targetNamespace: namespace,
2336
2941
  logger: new CodegenLogger({