@atomic-ehr/codegen 0.0.4 → 0.0.5-canary.20251226085624.be72273

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
@@ -1,17 +1,183 @@
1
+ import pc from 'picocolors';
1
2
  import * as fs from 'fs';
2
3
  import fs__default, { existsSync } from 'fs';
3
4
  import * as afs2 from 'fs/promises';
4
5
  import { readFile } from 'fs/promises';
5
- import * as Path4 from 'path';
6
- import Path4__default, { resolve } from 'path';
6
+ import * as Path from 'path';
7
+ import Path__default, { resolve } from 'path';
7
8
  import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
9
+ import { fileURLToPath } from 'url';
10
+ import assert3 from 'assert';
11
+ import * as YAML from 'yaml';
8
12
  import * as fhirschema from '@atomic-ehr/fhirschema';
9
13
  import { isStructureDefinition } from '@atomic-ehr/fhirschema';
10
- import * as YAML from 'yaml';
11
- import assert from 'assert';
12
- import pc from 'picocolors';
14
+ import { spawn } from 'child_process';
15
+ import * as util from 'util';
16
+ import Mustache from 'mustache';
13
17
 
14
- // src/api/builder.ts
18
+ // src/utils/codegen-logger.ts
19
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
20
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
21
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
22
+ LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
23
+ LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
24
+ LogLevel2[LogLevel2["SILENT"] = 4] = "SILENT";
25
+ return LogLevel2;
26
+ })(LogLevel || {});
27
+ var parseLogLevel = (level) => {
28
+ switch (level.toUpperCase()) {
29
+ case "DEBUG":
30
+ return 0 /* DEBUG */;
31
+ case "INFO":
32
+ return 1 /* INFO */;
33
+ case "WARN":
34
+ return 2 /* WARN */;
35
+ case "ERROR":
36
+ return 3 /* ERROR */;
37
+ case "SILENT":
38
+ return 4 /* SILENT */;
39
+ default:
40
+ throw new Error(`Invalid log level: ${level}`);
41
+ }
42
+ };
43
+ var CodegenLogger = class _CodegenLogger {
44
+ options;
45
+ dryWarnSet = /* @__PURE__ */ new Set();
46
+ constructor(options = {}) {
47
+ this.options = {
48
+ timestamp: false,
49
+ level: 1 /* INFO */,
50
+ ...options
51
+ };
52
+ }
53
+ /**
54
+ * Check if a message at the given level should be logged
55
+ */
56
+ shouldLog(messageLevel) {
57
+ const currentLevel = this.options.level ?? 1 /* INFO */;
58
+ return messageLevel >= currentLevel;
59
+ }
60
+ static consoleLevelsMap = {
61
+ [1 /* INFO */]: console.log,
62
+ [2 /* WARN */]: console.warn,
63
+ [3 /* ERROR */]: console.error,
64
+ [0 /* DEBUG */]: console.log,
65
+ [4 /* SILENT */]: () => {
66
+ }
67
+ };
68
+ formatMessage(level, message, color) {
69
+ const timestamp = this.options.timestamp ? `${pc.gray((/* @__PURE__ */ new Date()).toLocaleTimeString())} ` : "";
70
+ const prefix = this.options.prefix ? `${pc.cyan(`[${this.options.prefix}]`)} ` : "";
71
+ return `${timestamp}${color(level)} ${prefix}${message}`;
72
+ }
73
+ isSuppressed(level) {
74
+ return this.options.suppressLoggingLevel === "all" || this.options.suppressLoggingLevel?.includes(level) || false;
75
+ }
76
+ tryWriteToConsole(level, formattedMessage) {
77
+ if (this.isSuppressed(level)) return;
78
+ if (!this.shouldLog(level)) return;
79
+ const logFn = _CodegenLogger.consoleLevelsMap[level] || console.log;
80
+ logFn(formattedMessage);
81
+ }
82
+ /**
83
+ * Success message with checkmark
84
+ */
85
+ success(message) {
86
+ this.tryWriteToConsole(1 /* INFO */, this.formatMessage("", message, pc.green));
87
+ }
88
+ /**
89
+ * Error message with X mark
90
+ */
91
+ error(message, error) {
92
+ if (this.isSuppressed(3 /* ERROR */)) return;
93
+ if (!this.shouldLog(3 /* ERROR */)) return;
94
+ console.error(this.formatMessage("X", message, pc.red));
95
+ const showDetails = this.options.level === 0 /* DEBUG */;
96
+ if (error && showDetails) {
97
+ console.error(pc.red(` ${error.message}`));
98
+ if (error.stack) {
99
+ console.error(pc.gray(error.stack));
100
+ }
101
+ }
102
+ }
103
+ /**
104
+ * Warning message with warning sign
105
+ */
106
+ warn(message) {
107
+ this.tryWriteToConsole(2 /* WARN */, this.formatMessage("!", message, pc.yellow));
108
+ }
109
+ dry_warn(message) {
110
+ if (!this.dryWarnSet.has(message)) {
111
+ this.warn(message);
112
+ this.dryWarnSet.add(message);
113
+ }
114
+ }
115
+ /**
116
+ * Info message with info icon
117
+ */
118
+ info(message) {
119
+ this.tryWriteToConsole(1 /* INFO */, this.formatMessage("i", message, pc.blue));
120
+ }
121
+ /**
122
+ * Debug message (only shows when log level is DEBUG or verbose is true)
123
+ */
124
+ debug(message) {
125
+ if (this.shouldLog(0 /* DEBUG */)) {
126
+ this.tryWriteToConsole(0 /* DEBUG */, this.formatMessage("\u{1F41B}", message, pc.magenta));
127
+ }
128
+ }
129
+ /**
130
+ * Step message with rocket
131
+ */
132
+ step(message) {
133
+ this.tryWriteToConsole(1 /* INFO */, this.formatMessage("\u{1F680}", message, pc.cyan));
134
+ }
135
+ /**
136
+ * Progress message with clock
137
+ */
138
+ progress(message) {
139
+ this.tryWriteToConsole(1 /* INFO */, this.formatMessage("\u23F3", message, pc.blue));
140
+ }
141
+ /**
142
+ * Plain message (no icon, just colored text)
143
+ */
144
+ plain(message, color = (s) => s) {
145
+ const timestamp = this.options.timestamp ? `${pc.gray((/* @__PURE__ */ new Date()).toLocaleTimeString())} ` : "";
146
+ const prefix = this.options.prefix ? `${pc.cyan(`[${this.options.prefix}]`)} ` : "";
147
+ this.tryWriteToConsole(1 /* INFO */, `${timestamp}${prefix}${color(message)}`);
148
+ }
149
+ /**
150
+ * Dimmed/gray text for less important info
151
+ */
152
+ dim(message) {
153
+ this.plain(message, pc.gray);
154
+ }
155
+ /**
156
+ * Create a child logger with a prefix
157
+ */
158
+ child(prefix) {
159
+ return new _CodegenLogger({
160
+ ...this.options,
161
+ prefix: this.options.prefix ? `${this.options.prefix}:${prefix}` : prefix
162
+ });
163
+ }
164
+ /**
165
+ * Update options
166
+ */
167
+ configure(options) {
168
+ this.options = { ...this.options, ...options };
169
+ }
170
+ getLevel() {
171
+ return this.options.level ?? 1 /* INFO */;
172
+ }
173
+ setLevel(level) {
174
+ this.options.level = level;
175
+ }
176
+ };
177
+ new CodegenLogger();
178
+ function createLogger(options = {}) {
179
+ return new CodegenLogger(options);
180
+ }
15
181
 
16
182
  // src/api/writer-generator/utils.ts
17
183
  var words = (s) => {
@@ -32,6 +198,9 @@ var camelCase = (s) => {
32
198
  var pascalCase = (s) => {
33
199
  return words(s).map(capitalCase).join("");
34
200
  };
201
+ var snakeCase = (s) => {
202
+ return words(s).map((s2) => s2.toLowerCase()).join("_");
203
+ };
35
204
  var uppercaseFirstLetter = (str) => {
36
205
  if (!str || str.length === 0) return str;
37
206
  return str.charAt(0).toUpperCase() + str.slice(1);
@@ -78,7 +247,7 @@ var FileSystemWriter = class {
78
247
  }
79
248
  cd(path, gen) {
80
249
  const prev = this.currentDir;
81
- this.currentDir = path.startsWith("/") ? Path4.join(this.opts.outputDir, path) : Path4.join(this.currentDir ?? this.opts.outputDir, path);
250
+ this.currentDir = path.startsWith("/") ? Path.join(this.opts.outputDir, path) : Path.join(this.currentDir ?? this.opts.outputDir, path);
82
251
  this.onDiskMkDir(this.currentDir);
83
252
  this.logger()?.debug(`cd '${this.currentDir}'`);
84
253
  gen();
@@ -87,12 +256,16 @@ var FileSystemWriter = class {
87
256
  cat(fn, gen) {
88
257
  if (this.currentFile) throw new Error("Can't open file when another file is open");
89
258
  if (fn.includes("/")) throw new Error(`Change file path separatly: ${fn}`);
90
- const relPath = Path4.normalize(`${this.currentDir}/${fn}`);
259
+ const relPath = Path.normalize(`${this.currentDir}/${fn}`);
91
260
  try {
92
261
  const descriptor = this.onDiskOpenFile(relPath);
93
262
  this.logger()?.debug(`cat > '${relPath}'`);
94
263
  this.currentFile = { descriptor, relPath };
95
- this.writtenFilesBuffer[this.currentFile.relPath] = { relPath, absPath: Path4.resolve(relPath), tokens: [] };
264
+ this.writtenFilesBuffer[this.currentFile.relPath] = {
265
+ relPath,
266
+ absPath: Path.resolve(relPath),
267
+ tokens: []
268
+ };
96
269
  gen();
97
270
  } finally {
98
271
  if (this.currentFile) this.onDiskCloseFile(this.currentFile.descriptor);
@@ -106,9 +279,21 @@ var FileSystemWriter = class {
106
279
  if (!buf) throw new Error("No buffer found");
107
280
  buf.tokens.push(str);
108
281
  }
282
+ cp(source, destination) {
283
+ if (!this.opts.resolveAssets) throw new Error("resolveAssets is not defined");
284
+ source = Path.resolve(this.opts.resolveAssets(source));
285
+ destination = Path.normalize(`${this.currentDir ?? this.opts.outputDir}/${destination}`);
286
+ const content = fs.readFileSync(source, "utf8");
287
+ this.writtenFilesBuffer[destination] = {
288
+ relPath: destination,
289
+ absPath: Path.resolve(destination),
290
+ tokens: [content]
291
+ };
292
+ fs.cpSync(source, destination);
293
+ }
109
294
  writtenFiles() {
110
295
  return Object.values(this.writtenFilesBuffer).map(({ relPath, absPath, tokens }) => {
111
- return { relPath, absPath, content: tokens.join() };
296
+ return { relPath, absPath, content: tokens.join("") };
112
297
  }).sort((a, b) => a.relPath.localeCompare(b.relPath));
113
298
  }
114
299
  };
@@ -216,15 +401,18 @@ var enrichFHIRSchema = (schema, packageMeta) => {
216
401
  base: schema.base
217
402
  };
218
403
  };
404
+ var isResourceIdentifier = (id) => {
405
+ return id?.kind === "resource";
406
+ };
407
+ var isComplexTypeIdentifier = (id) => {
408
+ return id?.kind === "complex-type";
409
+ };
219
410
  var isPrimitiveIdentifier = (id) => {
220
411
  return id?.kind === "primitive-type";
221
412
  };
222
413
  var isNestedIdentifier = (id) => {
223
414
  return id?.kind === "nested";
224
415
  };
225
- var isComplexTypeIdentifier = (id) => {
226
- return id?.kind === "complex-type";
227
- };
228
416
  var isProfileIdentifier = (id) => {
229
417
  return id?.kind === "profile";
230
418
  };
@@ -260,6 +448,10 @@ var isChoiceDeclarationField = (field) => {
260
448
  if (!field) return false;
261
449
  return field.choices !== void 0;
262
450
  };
451
+ var isChoiceInstanceField = (field) => {
452
+ if (!field) return false;
453
+ return field.choiceOf !== void 0;
454
+ };
263
455
  var enrichValueSet = (vs, packageMeta) => {
264
456
  if (!vs.url) throw new Error("ValueSet must have a URL");
265
457
  if (!vs.name) throw new Error("ValueSet must have a name");
@@ -318,6 +510,15 @@ function formatName(input) {
318
510
  }
319
511
 
320
512
  // src/api/writer-generator/csharp/csharp.ts
513
+ var resolveCSharpAssets = (fn) => {
514
+ const __filename2 = fileURLToPath(import.meta.url);
515
+ const __dirname = Path__default.dirname(__filename2);
516
+ if (__filename2.endsWith("dist/index.js")) {
517
+ return Path__default.resolve(__dirname, "..", "assets", "api", "writer-generator", "csharp", fn);
518
+ } else {
519
+ return Path__default.resolve(__dirname, "../../../..", "assets", "api", "writer-generator", "csharp", fn);
520
+ }
521
+ };
321
522
  var PRIMITIVE_TYPE_MAP = {
322
523
  boolean: "bool",
323
524
  instant: "string",
@@ -383,6 +584,7 @@ var CSharp = class extends Writer {
383
584
  tabSize: 4,
384
585
  withDebugComment: false,
385
586
  commentLinePrefix: "//",
587
+ resolveAssets: options.resolveAssets ?? resolveCSharpAssets,
386
588
  ...options
387
589
  });
388
590
  }
@@ -488,8 +690,8 @@ var CSharp = class extends Writer {
488
690
  "CSharpSDK",
489
691
  "System.Text.Json",
490
692
  "System.Text.Json.Serialization",
491
- this.opts.targetNamespace,
492
- ...packages.map((pkg) => `${this.opts.targetNamespace}.${pkg}`)
693
+ this.opts.rootNamespace,
694
+ ...packages.map((pkg) => `${this.opts.rootNamespace}.${pkg}`)
493
695
  ];
494
696
  for (const using of globalUsings) this.lineSM("global", "using", using);
495
697
  }
@@ -498,7 +700,7 @@ var CSharp = class extends Writer {
498
700
  this.cat("base.cs", () => {
499
701
  this.generateDisclaimer();
500
702
  this.line();
501
- this.lineSM("namespace", this.opts.targetNamespace);
703
+ this.lineSM("namespace", this.opts.rootNamespace);
502
704
  for (const schema of complexTypes) {
503
705
  const packageName = formatName(schema.identifier.package);
504
706
  this.generateType(schema, packageName);
@@ -515,7 +717,7 @@ var CSharp = class extends Writer {
515
717
  this.cat(`${schema.identifier.name}.cs`, () => {
516
718
  this.generateDisclaimer();
517
719
  this.line();
518
- this.lineSM("namespace", `${this.opts.targetNamespace}.${packageName}`);
720
+ this.lineSM("namespace", `${this.opts.rootNamespace}.${packageName}`);
519
721
  this.line();
520
722
  this.generateType(schema, packageName);
521
723
  });
@@ -539,7 +741,7 @@ var CSharp = class extends Writer {
539
741
  generateEnumFileContent(packageName, enums) {
540
742
  this.lineSM("using", "System.ComponentModel");
541
743
  this.line();
542
- this.lineSM(`namespace ${this.opts.targetNamespace}.${packageName}`);
744
+ this.lineSM(`namespace ${this.opts.rootNamespace}.${packageName}`);
543
745
  for (const [enumName, values] of Object.entries(enums)) {
544
746
  this.generateEnum(enumName, values);
545
747
  }
@@ -561,7 +763,7 @@ var CSharp = class extends Writer {
561
763
  this.cat(`${packageName}ResourceDictionary.cs`, () => {
562
764
  this.generateDisclaimer();
563
765
  this.line();
564
- this.lineSM(`namespace ${this.opts.targetNamespace}`);
766
+ this.lineSM(`namespace ${this.opts.rootNamespace}`);
565
767
  this.generateResourceDictionaryClass(packageName, packageResources);
566
768
  });
567
769
  }
@@ -579,212 +781,790 @@ var CSharp = class extends Writer {
579
781
  });
580
782
  }
581
783
  copyStaticFiles() {
582
- if (!this.opts.staticSourceDir) return;
583
- const sourcePath = Path4__default.resolve(this.opts.staticSourceDir);
584
- fs__default.cpSync(sourcePath, this.opts.outputDir, { recursive: true });
784
+ this.cp("Client.cs", "Client.cs");
785
+ this.cp("Helper.cs", "Helper.cs");
585
786
  }
586
787
  generateHelperFile() {
587
- const sourceFile = "src/api/writer-generator/csharp/Helper.cs";
588
- const destFile = Path4__default.join(this.opts.outputDir, "Helper.cs");
788
+ if (this.opts.inMemoryOnly) return;
789
+ const sourceFile = resolveCSharpAssets("Helper.cs");
790
+ const destFile = Path__default.join(this.opts.outputDir, "Helper.cs");
589
791
  fs__default.copyFileSync(sourceFile, destFile);
590
792
  }
591
793
  };
592
-
593
- // src/fhir-types/hl7-fhir-r4-core/CodeSystem.ts
594
- var isCodeSystem = (resource) => {
595
- return resource !== null && typeof resource === "object" && resource.resourceType === "CodeSystem";
596
- };
597
-
598
- // src/fhir-types/hl7-fhir-r4-core/ValueSet.ts
599
- var isValueSet = (resource) => {
600
- return resource !== null && typeof resource === "object" && resource.resourceType === "ValueSet";
601
- };
602
-
603
- // src/typeschema/register.ts
604
- var readPackageDependencies = async (manager, packageMeta) => {
605
- const packageJSON = await manager.packageJson(packageMeta.name);
606
- const dependencies = packageJSON.dependencies;
607
- if (dependencies !== void 0) {
608
- return Object.entries(dependencies).map(([name, version]) => {
609
- return { name, version };
610
- });
794
+ var groupByPackages = (typeSchemas) => {
795
+ const grouped = {};
796
+ for (const ts of typeSchemas) {
797
+ const pkgName = ts.identifier.package;
798
+ if (!grouped[pkgName]) grouped[pkgName] = [];
799
+ grouped[pkgName].push(ts);
611
800
  }
612
- return [];
613
- };
614
- var mkEmptyPkgIndex = (pkg) => {
615
- return {
616
- pkg,
617
- canonicalResolution: {},
618
- fhirSchemas: {},
619
- valueSets: {}
620
- };
801
+ for (const [packageName, typeSchemas2] of Object.entries(grouped)) {
802
+ const dict = {};
803
+ for (const ts of typeSchemas2) {
804
+ dict[JSON.stringify(ts.identifier)] = ts;
805
+ }
806
+ const tmp = Object.values(dict);
807
+ tmp.sort((a, b) => a.identifier.name.localeCompare(b.identifier.name));
808
+ grouped[packageName] = tmp;
809
+ }
810
+ return grouped;
621
811
  };
622
- var mkPackageAwareResolver = async (manager, pkg, deep, acc, logger) => {
623
- const pkgId = packageMetaToFhir(pkg);
624
- logger?.info(`${" ".repeat(deep * 2)}+ ${pkgId}`);
625
- if (acc[pkgId]) return acc[pkgId];
626
- const index = mkEmptyPkgIndex(pkg);
627
- for (const resource of await manager.search({ package: pkg })) {
628
- const rawUrl = resource.url;
629
- if (!rawUrl) continue;
630
- if (!(isStructureDefinition(resource) || isValueSet(resource) || isCodeSystem(resource))) continue;
631
- const url = rawUrl;
632
- if (index.canonicalResolution[url]) logger?.dry_warn(`Duplicate canonical URL: ${url} at ${pkgId}.`);
633
- index.canonicalResolution[url] = [{ deep, pkg, pkgId, resource }];
812
+ var buildDependencyGraph = (schemas) => {
813
+ const nameToMap = {};
814
+ for (const schema of schemas) {
815
+ nameToMap[schema.identifier.name] = schema;
634
816
  }
635
- const deps = await readPackageDependencies(manager, pkg);
636
- for (const depPkg of deps) {
637
- const { canonicalResolution } = await mkPackageAwareResolver(manager, depPkg, deep + 1, acc, logger);
638
- for (const [surl, resolutions] of Object.entries(canonicalResolution)) {
639
- const url = surl;
640
- index.canonicalResolution[url] = [...index.canonicalResolution[url] || [], ...resolutions];
817
+ const graph = {};
818
+ for (const schema of schemas) {
819
+ const name = schema.identifier.name;
820
+ const base = schema.base?.name;
821
+ if (!graph[name]) {
822
+ graph[name] = [];
823
+ }
824
+ if (base && nameToMap[base]) {
825
+ graph[name].push(base);
641
826
  }
642
827
  }
643
- for (const resolutionOptions of Object.values(index.canonicalResolution)) {
644
- resolutionOptions.sort((a, b) => a.deep - b.deep);
645
- }
646
- acc[pkgId] = index;
647
- return index;
648
- };
649
- var packageAgnosticResolveCanonical = (resolver, url, _logger) => {
650
- const options = Object.values(resolver).flatMap((pkg) => pkg.canonicalResolution[url]);
651
- if (!options) throw new Error(`No canonical resolution found for ${url} in any package`);
652
- return options[0]?.resource;
828
+ return graph;
653
829
  };
654
- var registerFromManager = async (manager, { logger, fallbackPackageForNameResolution, focusedPackages }) => {
655
- const packages = focusedPackages ?? await manager.packages();
656
- const resolver = {};
657
- for (const pkg of packages) {
658
- await mkPackageAwareResolver(manager, pkg, 0, resolver, logger);
659
- }
660
- for (const { pkg, canonicalResolution } of Object.values(resolver)) {
661
- const pkgId = packageMetaToFhir(pkg);
662
- if (!resolver[pkgId]) throw new Error(`Package ${pkgId} not found`);
663
- let counter = 0;
664
- logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' begins...`);
665
- for (const [_url, options] of Object.entries(canonicalResolution)) {
666
- const resolition = options[0];
667
- if (!resolition) throw new Error(`Resource not found`);
668
- const resource = resolition.resource;
669
- const resourcePkg = resolition.pkg;
670
- if (isStructureDefinition(resource)) {
671
- const rfs = enrichFHIRSchema(
672
- fhirschema.translate(resource),
673
- resourcePkg
674
- );
675
- counter++;
676
- resolver[pkgId].fhirSchemas[rfs.url] = rfs;
677
- }
678
- if (isValueSet(resource)) {
679
- const rvs = enrichValueSet(resource, resourcePkg);
680
- resolver[pkgId].valueSets[rvs.url] = rvs;
830
+ var topologicalSort = (graph) => {
831
+ const sorted = [];
832
+ const visited = {};
833
+ const temp = {};
834
+ const visit = (node) => {
835
+ if (temp[node]) {
836
+ throw new Error(`Graph has cycles ${node}`);
837
+ }
838
+ if (!visited[node]) {
839
+ temp[node] = true;
840
+ for (const neighbor of graph[node] ?? []) {
841
+ visit(neighbor);
681
842
  }
843
+ temp[node] = false;
844
+ visited[node] = true;
845
+ sorted.push(node);
682
846
  }
683
- logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' completed: ${counter} successful`);
684
- }
685
- const resolveFs = (pkg, canonicalUrl) => {
686
- return resolver[packageMetaToFhir(pkg)]?.fhirSchemas[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.fhirSchemas[canonicalUrl];
687
847
  };
688
- const resolveVs = (pkg, canonicalUrl) => {
689
- return resolver[packageMetaToFhir(pkg)]?.valueSets[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.valueSets[canonicalUrl];
848
+ for (const node in graph) {
849
+ if (!visited[node]) {
850
+ visit(node);
851
+ }
852
+ }
853
+ return sorted;
854
+ };
855
+ var sortAsDeclarationSequence = (schemas) => {
856
+ const graph = buildDependencyGraph(schemas);
857
+ const sorted = topologicalSort(graph);
858
+ return sorted.map((name) => schemas.find((schema) => schema.identifier.name === name)).filter(Boolean);
859
+ };
860
+ var resourceRelatives = (schemas) => {
861
+ const regularSchemas = schemas.filter(
862
+ (e) => isResourceTypeSchema(e) || isLogicalTypeSchema(e) || isComplexTypeTypeSchema(e)
863
+ );
864
+ const directPairs = [];
865
+ for (const schema of regularSchemas) {
866
+ if (schema.base) {
867
+ directPairs.push({ parent: schema.base, child: schema.identifier });
868
+ }
869
+ }
870
+ const allPairs = [...directPairs];
871
+ const findTransitiveRelatives = (parentRef) => {
872
+ const directChildren = directPairs.filter((pair) => pair.parent.name === parentRef.name).map((pair) => pair.child);
873
+ const transitiveChildren = [];
874
+ for (const child of directChildren) {
875
+ transitiveChildren.push(...findTransitiveRelatives(child));
876
+ }
877
+ return [...directChildren, ...transitiveChildren];
690
878
  };
691
- const ensureSpecializationCanonicalUrl = (name) => name.match(/^[a-zA-Z0-9]+$/) && `http://hl7.org/fhir/StructureDefinition/${name}` || name;
692
- const resolveFsGenealogy = (pkg, canonicalUrl) => {
693
- let fs4 = resolveFs(pkg, canonicalUrl);
694
- if (fs4 === void 0) throw new Error(`Failed to resolve FHIR Schema: '${canonicalUrl}'`);
695
- const genealogy = [fs4];
696
- while (fs4?.base) {
697
- const pkg2 = fs4.package_meta;
698
- const baseUrl = ensureSpecializationCanonicalUrl(fs4.base);
699
- fs4 = resolveFs(pkg2, baseUrl);
700
- if (fs4 === void 0)
701
- throw new Error(
702
- `Failed to resolve FHIR Schema base for '${canonicalUrl}'. Problem: '${baseUrl}' from '${packageMetaToFhir(pkg2)}'`
879
+ for (const pair of directPairs) {
880
+ const transitiveChildren = findTransitiveRelatives(pair.child);
881
+ for (const transitiveChild of transitiveChildren) {
882
+ if (!directPairs.some((dp) => dp.parent.name === pair.parent.name && dp.child.name === transitiveChild.name)) {
883
+ allPairs.push({ parent: pair.parent, child: transitiveChild });
884
+ }
885
+ }
886
+ }
887
+ return allPairs;
888
+ };
889
+ var mkTypeSchemaIndex = (schemas, { resolutionTree, logger }) => {
890
+ const index = {};
891
+ const append = (schema) => {
892
+ const url = schema.identifier.url;
893
+ const pkg = schema.identifier.package;
894
+ if (!index[url]) index[url] = {};
895
+ if (index[url][schema.identifier.package] && pkg !== "shared") {
896
+ const r1 = JSON.stringify(schema.identifier, void 0, 2);
897
+ const r2 = JSON.stringify(index[url][pkg]?.identifier, void 0, 2);
898
+ if (r1 !== r2) throw new Error(`Duplicate schema: ${r1} and ${r2}`);
899
+ return;
900
+ }
901
+ index[url][pkg] = schema;
902
+ };
903
+ for (const schema of schemas) {
904
+ append(schema);
905
+ }
906
+ const relations = resourceRelatives(schemas);
907
+ const resolve6 = (id) => index[id.url]?.[id.package];
908
+ const resolveByUrl = (pkgName, url) => {
909
+ if (resolutionTree) {
910
+ const resolution = resolutionTree[pkgName]?.[url]?.[0];
911
+ if (resolution) {
912
+ return index[url]?.[resolution.pkg.name];
913
+ }
914
+ }
915
+ return index[url]?.[pkgName];
916
+ };
917
+ const resourceChildren = (id) => {
918
+ return relations.filter((relative) => relative.parent.name === id.name).map((relative) => relative.child);
919
+ };
920
+ const tryHierarchy = (schema) => {
921
+ const res = [];
922
+ let cur = schema;
923
+ while (cur) {
924
+ res.push(cur);
925
+ const base = cur.base;
926
+ if (base === void 0) break;
927
+ const resolved = resolve6(base);
928
+ if (!resolved) {
929
+ logger?.warn(
930
+ `Failed to resolve base type: ${res.map((e) => `${e.identifier.url} (${e.identifier.kind})`).join(", ")}`
703
931
  );
704
- genealogy.push(fs4);
932
+ return void 0;
933
+ }
934
+ cur = resolved;
935
+ }
936
+ return res;
937
+ };
938
+ const hierarchy = (schema) => {
939
+ const genealogy = tryHierarchy(schema);
940
+ if (genealogy === void 0) {
941
+ throw new Error(`Failed to resolve base type: ${schema.identifier.url} (${schema.identifier.kind})`);
705
942
  }
706
943
  return genealogy;
707
944
  };
708
- const resolveFsSpecializations = (pkg, canonicalUrl) => {
709
- return resolveFsGenealogy(pkg, canonicalUrl).filter((fs4) => fs4.derivation === "specialization");
945
+ const findLastSpecialization = (schema) => {
946
+ const nonConstraintSchema = hierarchy(schema).find((s) => s.identifier.kind !== "profile");
947
+ if (!nonConstraintSchema) {
948
+ throw new Error(`No non-constraint schema found in hierarchy for: ${schema.identifier.name}`);
949
+ }
950
+ return nonConstraintSchema;
710
951
  };
711
- const resolveElementSnapshot = (fhirSchema, path) => {
712
- const geneology = resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url);
713
- const elemGeneology = resolveFsElementGenealogy(geneology, path);
714
- const elemSnapshot = fsElementSnapshot(elemGeneology);
715
- return elemSnapshot;
952
+ const findLastSpecializationByIdentifier = (id) => {
953
+ const schema = resolve6(id);
954
+ if (!schema) return id;
955
+ return findLastSpecialization(schema).identifier;
716
956
  };
717
- const getAllElementKeys = (elems) => {
718
- const keys = /* @__PURE__ */ new Set();
719
- for (const [key, elem] of Object.entries(elems)) {
720
- keys.add(key);
721
- for (const choiceKey of elem?.choices || []) {
722
- if (!elems[choiceKey]) {
723
- keys.add(choiceKey);
957
+ const flatProfile = (schema) => {
958
+ const hierarchySchemas = hierarchy(schema);
959
+ const constraintSchemas = hierarchySchemas.filter((s) => s.identifier.kind === "profile");
960
+ const nonConstraintSchema = hierarchySchemas.find((s) => s.identifier.kind !== "profile");
961
+ if (!nonConstraintSchema)
962
+ throw new Error(`No non-constraint schema found in hierarchy for ${schema.identifier.name}`);
963
+ const mergedFields = {};
964
+ for (const anySchema of constraintSchemas.slice().reverse()) {
965
+ const schema2 = anySchema;
966
+ if (!schema2.fields) continue;
967
+ for (const [fieldName, fieldConstraints] of Object.entries(schema2.fields)) {
968
+ if (mergedFields[fieldName]) {
969
+ mergedFields[fieldName] = {
970
+ ...mergedFields[fieldName],
971
+ ...fieldConstraints
972
+ };
973
+ } else {
974
+ mergedFields[fieldName] = { ...fieldConstraints };
724
975
  }
725
976
  }
726
977
  }
727
- return Array.from(keys);
978
+ const deps = {};
979
+ for (const e of constraintSchemas.flatMap((e2) => e2.dependencies ?? [])) {
980
+ deps[e.url] = e;
981
+ }
982
+ const dependencies = Object.values(deps);
983
+ return {
984
+ ...schema,
985
+ base: nonConstraintSchema.identifier,
986
+ fields: mergedFields,
987
+ dependencies
988
+ };
989
+ };
990
+ const isWithMetaField = (profile) => {
991
+ const genealogy = tryHierarchy(profile);
992
+ if (!genealogy) return false;
993
+ return genealogy.filter(isSpecializationTypeSchema).some((schema) => {
994
+ return schema.fields?.meta !== void 0;
995
+ });
996
+ };
997
+ const exportTree = async (filename) => {
998
+ const tree = {};
999
+ for (const [pkgId, shemas] of Object.entries(groupByPackages(schemas))) {
1000
+ tree[pkgId] = {
1001
+ "primitive-type": {},
1002
+ "complex-type": {},
1003
+ resource: {},
1004
+ "value-set": {},
1005
+ nested: {},
1006
+ binding: {},
1007
+ profile: {},
1008
+ logical: {}
1009
+ };
1010
+ for (const schema of shemas) {
1011
+ tree[pkgId][schema.identifier.kind][schema.identifier.url] = {};
1012
+ }
1013
+ }
1014
+ const raw = filename.endsWith(".yaml") ? YAML.stringify(tree) : JSON.stringify(tree, void 0, 2);
1015
+ await afs2.mkdir(Path.dirname(filename), { recursive: true });
1016
+ await afs2.writeFile(filename, raw);
728
1017
  };
729
1018
  return {
730
- ...manager,
731
- testAppendFs(fs4) {
732
- const rfs = enrichFHIRSchema(fs4);
733
- const pkgId = packageMetaToFhir(rfs.package_meta);
734
- if (!resolver[pkgId]) resolver[pkgId] = mkEmptyPkgIndex(rfs.package_meta);
735
- resolver[pkgId].fhirSchemas[rfs.url] = rfs;
736
- },
737
- resolveFs,
738
- resolveFsGenealogy,
739
- resolveFsSpecializations,
740
- ensureSpecializationCanonicalUrl,
741
- resolveSd: (_pkg, canonicalUrl) => {
742
- const res = packageAgnosticResolveCanonical(resolver, canonicalUrl);
743
- if (isStructureDefinition(res)) return res;
744
- return void 0;
745
- },
746
- allFs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.fhirSchemas)),
747
- allVs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.valueSets)),
748
- resolveVs,
749
- resolveAny: (canonicalUrl) => packageAgnosticResolveCanonical(resolver, canonicalUrl),
750
- resolveElementSnapshot,
751
- getAllElementKeys,
752
- resolver,
753
- resolutionTree: () => {
754
- const res = {};
755
- for (const [_pkgId, pkgIndex] of Object.entries(resolver)) {
756
- const pkgName = pkgIndex.pkg.name;
757
- res[pkgName] = {};
758
- for (const [surl, resolutions] of Object.entries(pkgIndex.canonicalResolution)) {
759
- const url = surl;
760
- res[pkgName][url] = [];
761
- for (const resolution of resolutions) {
762
- res[pkgName][url].push({ deep: resolution.deep, pkg: resolution.pkg });
763
- }
764
- }
1019
+ _schemaIndex: index,
1020
+ _relations: relations,
1021
+ collectComplexTypes: () => schemas.filter(isComplexTypeTypeSchema),
1022
+ collectResources: () => schemas.filter(isResourceTypeSchema),
1023
+ collectLogicalModels: () => schemas.filter(isLogicalTypeSchema),
1024
+ collectProfiles: () => schemas.filter(isProfileTypeSchema),
1025
+ resolve: resolve6,
1026
+ resolveByUrl,
1027
+ resourceChildren,
1028
+ tryHierarchy,
1029
+ hierarchy,
1030
+ findLastSpecialization,
1031
+ findLastSpecializationByIdentifier,
1032
+ flatProfile,
1033
+ isWithMetaField,
1034
+ exportTree
1035
+ };
1036
+ };
1037
+
1038
+ // src/api/writer-generator/python.ts
1039
+ var PRIMITIVE_TYPE_MAP2 = {
1040
+ boolean: "bool",
1041
+ instant: "str",
1042
+ time: "str",
1043
+ date: "str",
1044
+ dateTime: "str",
1045
+ decimal: "float",
1046
+ integer: "int",
1047
+ unsignedInt: "int",
1048
+ positiveInt: "PositiveInt",
1049
+ integer64: "int",
1050
+ base64Binary: "str",
1051
+ uri: "str",
1052
+ url: "str",
1053
+ canonical: "str",
1054
+ oid: "str",
1055
+ uuid: "str",
1056
+ string: "str",
1057
+ code: "str",
1058
+ markdown: "str",
1059
+ id: "str",
1060
+ xhtml: "str"
1061
+ };
1062
+ var AVAILABLE_STRING_FORMATS = {
1063
+ snake_case: snakeCase,
1064
+ PascalCase: pascalCase,
1065
+ camelCase
1066
+ };
1067
+ var PYTHON_KEYWORDS = /* @__PURE__ */ new Set([
1068
+ "False",
1069
+ "None",
1070
+ "True",
1071
+ "and",
1072
+ "as",
1073
+ "assert",
1074
+ "async",
1075
+ "await",
1076
+ "break",
1077
+ "class",
1078
+ "continue",
1079
+ "def",
1080
+ "del",
1081
+ "elif",
1082
+ "else",
1083
+ "except",
1084
+ "finally",
1085
+ "for",
1086
+ "from",
1087
+ "global",
1088
+ "if",
1089
+ "import",
1090
+ "in",
1091
+ "is",
1092
+ "lambda",
1093
+ "nonlocal",
1094
+ "not",
1095
+ "or",
1096
+ "pass",
1097
+ "raise",
1098
+ "return",
1099
+ "try",
1100
+ "while",
1101
+ "with",
1102
+ "yield",
1103
+ "List"
1104
+ ]);
1105
+ var MAX_IMPORT_LINE_LENGTH = 100;
1106
+ var fixReservedWords = (name) => {
1107
+ return PYTHON_KEYWORDS.has(name) ? `${name}_` : name;
1108
+ };
1109
+ var injectSuperClasses = (name) => {
1110
+ return name === "Resource" || name === "Element" ? ["BaseModel"] : [];
1111
+ };
1112
+ var canonicalToName2 = (canonical, dropFragment = true) => {
1113
+ if (!canonical) return void 0;
1114
+ let localName = canonical.split("/").pop();
1115
+ if (!localName) return void 0;
1116
+ if (dropFragment && localName.includes("#")) {
1117
+ localName = localName.split("#")[0];
1118
+ }
1119
+ if (!localName) return void 0;
1120
+ if (/^\d/.test(localName)) {
1121
+ localName = `number_${localName}`;
1122
+ }
1123
+ return snakeCase(localName);
1124
+ };
1125
+ var deriveResourceName = (id) => {
1126
+ if (id.kind === "nested") {
1127
+ const url = id.url;
1128
+ const path = canonicalToName2(url, false);
1129
+ if (!path) return "";
1130
+ const [resourceName, fragment] = path.split("#");
1131
+ const name = uppercaseFirstLetterOfEach((fragment ?? "").split(".")).join("");
1132
+ return pascalCase([resourceName, name].join(""));
1133
+ }
1134
+ return pascalCase(id.name);
1135
+ };
1136
+ var resolvePyAssets = (fn) => {
1137
+ const __dirname = Path.dirname(fileURLToPath(import.meta.url));
1138
+ if (__filename.endsWith("dist/index.js")) {
1139
+ return Path.resolve(__dirname, "..", "assets", "api", "writer-generator", "python", fn);
1140
+ } else {
1141
+ return Path.resolve(__dirname, "../../..", "assets", "api", "writer-generator", "python", fn);
1142
+ }
1143
+ };
1144
+ var Python = class extends Writer {
1145
+ nameFormatFunction;
1146
+ tsIndex;
1147
+ constructor(options) {
1148
+ super({ ...options, resolveAssets: options.resolveAssets ?? resolvePyAssets });
1149
+ this.nameFormatFunction = this.getFieldFormatFunction(options.fieldFormat);
1150
+ }
1151
+ async generate(tsIndex) {
1152
+ this.tsIndex = tsIndex;
1153
+ const groups = {
1154
+ groupedComplexTypes: groupByPackages(tsIndex.collectComplexTypes()),
1155
+ groupedResources: groupByPackages(tsIndex.collectResources())
1156
+ };
1157
+ this.generateRootPackages(groups);
1158
+ this.generateSDKPackages(groups);
1159
+ }
1160
+ generateRootPackages(groups) {
1161
+ this.generateRootInitFile(groups);
1162
+ this.cp(resolvePyAssets("requirements.txt"), "requirements.txt");
1163
+ }
1164
+ generateSDKPackages(groups) {
1165
+ this.generateComplexTypesPackages(groups.groupedComplexTypes);
1166
+ this.generateResourcePackages(groups);
1167
+ }
1168
+ generateComplexTypesPackages(groupedComplexTypes) {
1169
+ for (const [packageName, packageComplexTypes] of Object.entries(groupedComplexTypes)) {
1170
+ this.cd(`/${snakeCase(packageName)}`, () => {
1171
+ this.generateBasePy(packageComplexTypes);
1172
+ });
1173
+ }
1174
+ }
1175
+ generateResourcePackages(groups) {
1176
+ for (const [packageName, packageResources] of Object.entries(groups.groupedResources)) {
1177
+ this.cd(`/${snakeCase(packageName)}`, () => {
1178
+ this.generateResourcePackageContent(
1179
+ packageName,
1180
+ packageResources,
1181
+ groups.groupedComplexTypes[packageName] || []
1182
+ );
1183
+ });
1184
+ }
1185
+ }
1186
+ generateResourcePackageContent(packageName, packageResources, packageComplexTypes) {
1187
+ const pyPackageName = this.pyFhirPackageByName(packageName);
1188
+ this.generateResourcePackageInit(pyPackageName, packageResources, packageComplexTypes);
1189
+ this.generateResourceFamilies(packageResources);
1190
+ for (const schema of packageResources) {
1191
+ this.generateResourceModule(schema);
1192
+ }
1193
+ }
1194
+ generateRootInitFile(groups) {
1195
+ this.cd("/", () => {
1196
+ this.cat("__init__.py", () => {
1197
+ this.generateDisclaimer();
1198
+ const pydanticModels = this.collectAndImportAllModels(groups);
1199
+ this.generateModelRebuilds(pydanticModels);
1200
+ });
1201
+ });
1202
+ }
1203
+ collectAndImportAllModels(groups) {
1204
+ const models = [];
1205
+ for (const packageName of Object.keys(groups.groupedResources)) {
1206
+ const fullPyPackageName = this.pyFhirPackageByName(packageName);
1207
+ models.push(...this.importComplexTypes(fullPyPackageName, groups.groupedComplexTypes[packageName]));
1208
+ models.push(...this.importResources(fullPyPackageName, false, groups.groupedResources[packageName]));
1209
+ }
1210
+ this.line();
1211
+ return models;
1212
+ }
1213
+ generateModelRebuilds(models) {
1214
+ for (const modelName of models.sort()) {
1215
+ this.line(`${modelName}.model_rebuild()`);
1216
+ }
1217
+ }
1218
+ generateBasePy(packageComplexTypes) {
1219
+ this.cat("base.py", () => {
1220
+ this.generateDisclaimer();
1221
+ this.generateDefaultImports();
1222
+ this.line();
1223
+ this.generateComplexTypes(packageComplexTypes);
1224
+ this.line();
1225
+ });
1226
+ }
1227
+ generateComplexTypes(complexTypes) {
1228
+ for (const schema of sortAsDeclarationSequence(complexTypes)) {
1229
+ this.generateNestedTypes(schema);
1230
+ this.line();
1231
+ this.generateType(schema);
1232
+ }
1233
+ }
1234
+ generateResourcePackageInit(fullPyPackageName, packageResources, packageComplexTypes) {
1235
+ this.cat("__init__.py", () => {
1236
+ this.generateDisclaimer();
1237
+ this.importComplexTypes(fullPyPackageName, packageComplexTypes);
1238
+ const allResourceNames = this.importResources(fullPyPackageName, true, packageResources);
1239
+ this.line();
1240
+ this.generateExportsDeclaration(packageComplexTypes, allResourceNames);
1241
+ });
1242
+ }
1243
+ importComplexTypes(fullPyPackageName, packageComplexTypes) {
1244
+ if (!packageComplexTypes || packageComplexTypes.length === 0) return [];
1245
+ const baseTypes = packageComplexTypes.map((t) => t.identifier.name).sort();
1246
+ this.pyImportFrom(`${fullPyPackageName}.base`, ...baseTypes);
1247
+ this.line();
1248
+ return baseTypes;
1249
+ }
1250
+ buildImportLine(remaining, maxImportLineLength) {
1251
+ let line = "";
1252
+ while (remaining.length > 0 && line.length < maxImportLineLength) {
1253
+ const entity = remaining.shift();
1254
+ if (!entity) throw new Error("Unexpected empty entity");
1255
+ if (line.length > 0) {
1256
+ line += ", ";
765
1257
  }
766
- return res;
1258
+ line += entity;
1259
+ }
1260
+ if (remaining.length > 0) {
1261
+ line += ", \\";
1262
+ }
1263
+ return line;
1264
+ }
1265
+ importResources(fullPyPackageName, importEmptyResources, packageResources) {
1266
+ if (!packageResources || packageResources.length === 0) return [];
1267
+ const allResourceNames = [];
1268
+ for (const resource of packageResources) {
1269
+ const names = this.importOneResource(resource, fullPyPackageName);
1270
+ if (!importEmptyResources && !resource.fields) continue;
1271
+ allResourceNames.push(...names);
1272
+ }
1273
+ return allResourceNames;
1274
+ }
1275
+ importOneResource(resource, fullPyPackageName) {
1276
+ const moduleName = `${fullPyPackageName}.${snakeCase(resource.identifier.name)}`;
1277
+ const importNames = this.collectResourceImportNames(resource);
1278
+ this.pyImportFrom(moduleName, ...importNames);
1279
+ const names = [...importNames];
1280
+ if (this.shouldImportResourceFamily(resource)) {
1281
+ const familyName = `${resource.identifier.name}Family`;
1282
+ this.pyImportFrom(`${fullPyPackageName}.resource_families`, familyName);
1283
+ }
1284
+ return names;
1285
+ }
1286
+ collectResourceImportNames(resource) {
1287
+ const names = [deriveResourceName(resource.identifier)];
1288
+ for (const nested of resource.nested ?? []) {
1289
+ const nestedName = deriveResourceName(nested.identifier);
1290
+ names.push(nestedName);
1291
+ }
1292
+ return names;
1293
+ }
1294
+ shouldImportResourceFamily(resource) {
1295
+ assert3(this.tsIndex !== void 0);
1296
+ return resource.identifier.kind === "resource" && this.tsIndex.resourceChildren(resource.identifier).length > 0;
1297
+ }
1298
+ generateExportsDeclaration(packageComplexTypes, allResourceNames) {
1299
+ this.squareBlock(["__all__", "="], () => {
1300
+ const allExports = [
1301
+ ...(packageComplexTypes || []).map((t) => t.identifier.name),
1302
+ ...allResourceNames
1303
+ ].sort();
1304
+ for (const schemaName of allExports) {
1305
+ this.line(`'${schemaName}',`);
1306
+ }
1307
+ });
1308
+ }
1309
+ generateResourceModule(schema) {
1310
+ this.cat(`${snakeCase(schema.identifier.name)}.py`, () => {
1311
+ this.generateDisclaimer();
1312
+ this.generateDefaultImports();
1313
+ this.line();
1314
+ this.generateDependenciesImports(schema);
1315
+ this.line();
1316
+ this.generateNestedTypes(schema);
1317
+ this.line();
1318
+ this.generateType(schema);
1319
+ });
1320
+ }
1321
+ generateType(schema) {
1322
+ const className = deriveResourceName(schema.identifier);
1323
+ const superClasses = this.getSuperClasses(schema);
1324
+ this.line(`class ${className}(${superClasses.join(", ")}):`);
1325
+ this.indentBlock(() => {
1326
+ this.generateClassBody(schema);
1327
+ });
1328
+ this.line();
1329
+ }
1330
+ getSuperClasses(schema) {
1331
+ return [...schema.base ? [schema.base.name] : [], ...injectSuperClasses(schema.identifier.name)];
1332
+ }
1333
+ generateClassBody(schema) {
1334
+ this.generateModelConfig();
1335
+ if (!schema.fields) {
1336
+ this.line("pass");
1337
+ return;
1338
+ }
1339
+ if (schema.identifier.kind === "resource") {
1340
+ this.generateResourceTypeField(schema);
1341
+ }
1342
+ this.generateFields(schema);
1343
+ if (schema.identifier.kind === "resource") {
1344
+ this.generateResourceMethods(schema);
1345
+ }
1346
+ }
1347
+ generateModelConfig() {
1348
+ const extraMode = this.opts.allowExtraFields ? "allow" : "forbid";
1349
+ this.line(`model_config = ConfigDict(validate_by_name=True, serialize_by_alias=True, extra="${extraMode}")`);
1350
+ }
1351
+ generateResourceTypeField(schema) {
1352
+ this.line(`${this.nameFormatFunction("resourceType")}: str = Field(`);
1353
+ this.indentBlock(() => {
1354
+ this.line(`default='${schema.identifier.name}',`);
1355
+ this.line(`alias='resourceType',`);
1356
+ this.line(`serialization_alias='resourceType',`);
1357
+ this.line("frozen=True,");
1358
+ this.line(`pattern='${schema.identifier.name}'`);
1359
+ });
1360
+ this.line(")");
1361
+ }
1362
+ generateFields(schema) {
1363
+ const sortedFields = Object.entries(schema.fields ?? []).sort(([a], [b]) => a.localeCompare(b));
1364
+ for (const [fieldName, field] of sortedFields) {
1365
+ if ("choices" in field && field.choices) continue;
1366
+ const fieldInfo = this.buildFieldInfo(fieldName, field);
1367
+ this.line(`${fieldInfo.name}: ${fieldInfo.type}${fieldInfo.defaultValue}`);
1368
+ }
1369
+ }
1370
+ buildFieldInfo(fieldName, field) {
1371
+ const pyFieldName = fixReservedWords(this.nameFormatFunction(fieldName));
1372
+ const fieldType = this.determineFieldType(field);
1373
+ const defaultValue = this.getFieldDefaultValue(field, fieldName);
1374
+ return {
1375
+ name: pyFieldName,
1376
+ type: fieldType,
1377
+ defaultValue
1378
+ };
1379
+ }
1380
+ determineFieldType(field) {
1381
+ let fieldType = field ? this.getBaseFieldType(field) : "";
1382
+ if ("enum" in field && field.enum) {
1383
+ const s = field.enum.map((e) => `"${e}"`).join(", ");
1384
+ fieldType = `Literal[${s}]`;
1385
+ }
1386
+ if (field.array) {
1387
+ fieldType = `PyList[${fieldType}]`;
1388
+ }
1389
+ if (!field.required) {
1390
+ fieldType = `${fieldType} | None`;
1391
+ }
1392
+ return fieldType;
1393
+ }
1394
+ getBaseFieldType(field) {
1395
+ if ("type" in field && field.type.kind === "resource") return `${field.type.name}Family`;
1396
+ if ("type" in field && field.type.kind === "nested") return deriveResourceName(field.type);
1397
+ if ("type" in field && field.type.kind === "primitive-type")
1398
+ return PRIMITIVE_TYPE_MAP2[field.type.name] ?? "str";
1399
+ return "type" in field ? field.type.name : "";
1400
+ }
1401
+ getFieldDefaultValue(field, fieldName) {
1402
+ const aliasSpec = `alias="${fieldName}", serialization_alias="${fieldName}"`;
1403
+ if (!field.required) {
1404
+ return ` = Field(None, ${aliasSpec})`;
1405
+ }
1406
+ return ` = Field(${aliasSpec})`;
1407
+ }
1408
+ generateResourceMethods(schema) {
1409
+ const className = schema.identifier.name.toString();
1410
+ this.line();
1411
+ this.line("def to_json(self, indent: int | None = None) -> str:");
1412
+ this.line(" return self.model_dump_json(exclude_unset=True, exclude_none=True, indent=indent)");
1413
+ this.line();
1414
+ this.line("@classmethod");
1415
+ this.line(`def from_json(cls, json: str) -> ${className}:`);
1416
+ this.line(" return cls.model_validate_json(json)");
1417
+ }
1418
+ generateNestedTypes(schema) {
1419
+ if (!schema.nested) return;
1420
+ this.line();
1421
+ for (const subtype of schema.nested) {
1422
+ this.generateType(subtype);
1423
+ }
1424
+ }
1425
+ generateDefaultImports() {
1426
+ this.pyImportFrom("__future__", "annotations");
1427
+ this.pyImportFrom("pydantic", "BaseModel", "ConfigDict", "Field", "PositiveInt");
1428
+ this.pyImportFrom("typing", "List as PyList", "Literal");
1429
+ }
1430
+ generateDependenciesImports(schema) {
1431
+ if (!schema.dependencies || schema.dependencies.length === 0) return;
1432
+ this.importComplexTypeDependencies(schema.dependencies);
1433
+ this.importResourceDependencies(schema.dependencies);
1434
+ }
1435
+ importComplexTypeDependencies(dependencies) {
1436
+ const complexTypeDeps = dependencies.filter((dep) => dep.kind === "complex-type");
1437
+ const depsByPackage = this.groupDependenciesByPackage(complexTypeDeps);
1438
+ for (const [pyPackage, names] of Object.entries(depsByPackage)) {
1439
+ this.pyImportFrom(pyPackage, ...names.sort());
1440
+ }
1441
+ }
1442
+ importResourceDependencies(dependencies) {
1443
+ const resourceDeps = dependencies.filter((dep) => dep.kind === "resource");
1444
+ for (const dep of resourceDeps) {
1445
+ this.pyImportType(dep);
1446
+ const familyName = `${pascalCase(dep.name)}Family`;
1447
+ const familyPackage = `${this.pyFhirPackage(dep)}.resource_families`;
1448
+ this.pyImportFrom(familyPackage, familyName);
1449
+ }
1450
+ }
1451
+ groupDependenciesByPackage(dependencies) {
1452
+ const grouped = {};
1453
+ for (const dep of dependencies) {
1454
+ const pyPackage = this.pyPackage(dep);
1455
+ if (!grouped[pyPackage]) {
1456
+ grouped[pyPackage] = [];
1457
+ }
1458
+ grouped[pyPackage].push(dep.name);
1459
+ }
1460
+ return grouped;
1461
+ }
1462
+ pyImportFrom(pyPackage, ...entities) {
1463
+ const oneLine = `from ${pyPackage} import ${entities.join(", ")}`;
1464
+ if (this.shouldUseSingleLineImport(oneLine, entities)) {
1465
+ this.line(oneLine);
1466
+ } else {
1467
+ this.writeMultiLineImport(pyPackage, entities);
1468
+ }
1469
+ }
1470
+ shouldUseSingleLineImport(oneLine, entities) {
1471
+ return oneLine.length <= MAX_IMPORT_LINE_LENGTH || entities.length === 1;
1472
+ }
1473
+ writeMultiLineImport(pyPackage, entities) {
1474
+ this.line(`from ${pyPackage} import (\\`);
1475
+ this.indentBlock(() => {
1476
+ const remaining = [...entities];
1477
+ while (remaining.length > 0) {
1478
+ const line = this.buildImportLine(remaining, MAX_IMPORT_LINE_LENGTH);
1479
+ this.line(line);
1480
+ }
1481
+ });
1482
+ this.line(")");
1483
+ }
1484
+ pyImportType(identifier) {
1485
+ this.pyImportFrom(this.pyPackage(identifier), pascalCase(identifier.name));
1486
+ }
1487
+ generateResourceFamilies(packageResources) {
1488
+ assert3(this.tsIndex !== void 0);
1489
+ const packages = (
1490
+ //this.helper.getPackages(packageResources, this.opts.rootPackageName);
1491
+ Object.keys(groupByPackages(packageResources)).map(
1492
+ (pkgName) => `${this.opts.rootPackageName}.${pkgName.replaceAll(".", "_")}`
1493
+ )
1494
+ );
1495
+ const families = {};
1496
+ for (const resource of this.tsIndex.collectResources()) {
1497
+ const children = this.tsIndex.resourceChildren(resource.identifier).map((c) => c.name);
1498
+ if (children.length > 0) {
1499
+ const familyName = `${resource.identifier.name}Family`;
1500
+ families[familyName] = children;
1501
+ }
1502
+ }
1503
+ const exportList = Object.keys(families);
1504
+ if (exportList.length === 0) return;
1505
+ this.buildResourceFamiliesFile(packages, families, exportList);
1506
+ }
1507
+ buildResourceFamiliesFile(packages, families, exportList) {
1508
+ this.cat("resource_families.py", () => {
1509
+ this.generateDisclaimer();
1510
+ this.includeResourceFamilyValidator();
1511
+ this.line();
1512
+ this.generateFamilyDefinitions(packages, families);
1513
+ this.generateFamilyExports(exportList);
1514
+ });
1515
+ }
1516
+ includeResourceFamilyValidator() {
1517
+ const content = fs__default.readFileSync(resolvePyAssets("resource_family_validator.py"), "utf-8");
1518
+ this.line(content);
1519
+ }
1520
+ generateFamilyDefinitions(packages, families) {
1521
+ this.line(`packages = [${packages.map((p) => `'${p}'`).join(", ")}]`);
1522
+ this.line();
1523
+ for (const [familyName, resources] of Object.entries(families)) {
1524
+ this.generateFamilyDefinition(familyName, resources);
1525
+ }
1526
+ }
1527
+ generateFamilyDefinition(familyName, resources) {
1528
+ const listName = `${familyName}_resources`;
1529
+ this.line(`${listName} = [${resources.map((r) => `'${r}'`).join(", ")}]`);
1530
+ this.line();
1531
+ this.line(`def validate_and_downcast_${familyName}(v: Any) -> Any:`);
1532
+ this.line(` return validate_and_downcast(v, packages, ${listName})`);
1533
+ this.line();
1534
+ this.line(`type ${familyName} = Annotated[Any, BeforeValidator(validate_and_downcast_${familyName})]`);
1535
+ this.line();
1536
+ }
1537
+ generateFamilyExports(exportList) {
1538
+ this.line(`__all__ = [${exportList.map((e) => `'${e}'`).join(", ")}]`);
1539
+ }
1540
+ buildPyPackageName(packageName) {
1541
+ const parts = packageName ? [snakeCase(packageName)] : [""];
1542
+ return parts.join(".");
1543
+ }
1544
+ pyFhirPackage(identifier) {
1545
+ return this.pyFhirPackageByName(identifier.package);
1546
+ }
1547
+ pyFhirPackageByName(name) {
1548
+ return [this.opts.rootPackageName, this.buildPyPackageName(name)].join(".");
1549
+ }
1550
+ pyPackage(identifier) {
1551
+ if (identifier.kind === "complex-type") {
1552
+ return `${this.pyFhirPackage(identifier)}.base`;
767
1553
  }
768
- };
769
- };
770
- var resolveFsElementGenealogy = (genealogy, path) => {
771
- const [top, ...rest] = path;
772
- if (top === void 0) return [];
773
- return genealogy.map((fs4) => {
774
- if (!fs4.elements) return void 0;
775
- let elem = fs4.elements?.[top];
776
- for (const k of rest) {
777
- elem = elem?.elements?.[k];
1554
+ if (identifier.kind === "resource") {
1555
+ return [this.pyFhirPackage(identifier), snakeCase(identifier.name)].join(".");
778
1556
  }
779
- return elem;
780
- }).filter((elem) => elem !== void 0);
1557
+ return this.pyFhirPackage(identifier);
1558
+ }
1559
+ getFieldFormatFunction(format2) {
1560
+ if (!AVAILABLE_STRING_FORMATS[format2]) {
1561
+ this.logger()?.warn(`Unknown field format '${format2}'. Defaulting to SnakeCase.`);
1562
+ this.logger()?.warn(`Supported formats: ${Object.keys(AVAILABLE_STRING_FORMATS).join(", ")}`);
1563
+ return snakeCase;
1564
+ }
1565
+ return AVAILABLE_STRING_FORMATS[format2];
1566
+ }
781
1567
  };
782
- function fsElementSnapshot(genealogy) {
783
- const revGenealogy = genealogy.reverse();
784
- const snapshot = Object.assign({}, ...revGenealogy);
785
- snapshot.elements = void 0;
786
- return snapshot;
787
- }
788
1568
 
789
1569
  // src/typeschema/core/identifier.ts
790
1570
  function dropVersionFromUrl(url) {
@@ -856,7 +1636,7 @@ function mkNestedIdentifier(register, fhirSchema, path, logger) {
856
1636
  const nestedTypeOrigins = {};
857
1637
  if (fhirSchema.derivation === "constraint") {
858
1638
  const specializations = register.resolveFsSpecializations(fhirSchema.package_meta, fhirSchema.url);
859
- const nestedTypeGenealogy = specializations.map((fs4) => mkNestedTypes(register, fs4, logger)).filter((e) => e !== void 0).flat();
1639
+ const nestedTypeGenealogy = specializations.map((fs7) => mkNestedTypes(register, fs7, logger)).filter((e) => e !== void 0).flat();
860
1640
  for (const nt of nestedTypeGenealogy.reverse()) {
861
1641
  nestedTypeOrigins[nt.identifier.name] = nt.identifier.url;
862
1642
  }
@@ -955,10 +1735,10 @@ function isRequired(register, fhirSchema, path) {
955
1735
  const fieldName = path[path.length - 1];
956
1736
  if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
957
1737
  const parentPath = path.slice(0, -1);
958
- const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs4) => {
959
- if (parentPath.length === 0) return fs4.required || [];
960
- if (!fs4.elements) return [];
961
- let elem = fs4;
1738
+ const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs7) => {
1739
+ if (parentPath.length === 0) return fs7.required || [];
1740
+ if (!fs7.elements) return [];
1741
+ let elem = fs7;
962
1742
  for (const k of parentPath) {
963
1743
  elem = elem?.elements?.[k];
964
1744
  }
@@ -970,10 +1750,10 @@ function isExcluded(register, fhirSchema, path) {
970
1750
  const fieldName = path[path.length - 1];
971
1751
  if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
972
1752
  const parentPath = path.slice(0, -1);
973
- const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs4) => {
974
- if (parentPath.length === 0) return fs4.excluded || [];
975
- if (!fs4.elements) return [];
976
- let elem = fs4;
1753
+ const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs7) => {
1754
+ if (parentPath.length === 0) return fs7.excluded || [];
1755
+ if (!fs7.elements) return [];
1756
+ let elem = fs7;
977
1757
  for (const k of parentPath) {
978
1758
  elem = elem?.elements?.[k];
979
1759
  }
@@ -985,9 +1765,9 @@ var buildReferences = (register, fhirSchema, element) => {
985
1765
  if (!element.refers) return void 0;
986
1766
  return element.refers.map((ref) => {
987
1767
  const curl = register.ensureSpecializationCanonicalUrl(ref);
988
- const fs4 = register.resolveFs(fhirSchema.package_meta, curl);
989
- if (!fs4) throw new Error(`Failed to resolve fs for ${curl}`);
990
- return mkIdentifier(fs4);
1768
+ const fs7 = register.resolveFs(fhirSchema.package_meta, curl);
1769
+ if (!fs7) throw new Error(`Failed to resolve fs for ${curl}`);
1770
+ return mkIdentifier(fs7);
991
1771
  });
992
1772
  };
993
1773
  function buildFieldType(register, fhirSchema, path, element, logger) {
@@ -1065,7 +1845,7 @@ function extractValueSetConceptsByUrl(register, pkg, valueSetUrl, logger) {
1065
1845
  function extractValueSetConcepts(register, valueSet, _logger) {
1066
1846
  if (valueSet.expansion?.contains) {
1067
1847
  return valueSet.expansion.contains.filter((item) => item.code !== void 0).map((item) => {
1068
- assert(item.code);
1848
+ assert3(item.code);
1069
1849
  return {
1070
1850
  code: item.code,
1071
1851
  display: item.display,
@@ -1270,63 +2050,342 @@ function transformFhirSchemaResource(register, fhirSchema, logger) {
1270
2050
  `Base resource not found '${fhirSchema.base}' for <${fhirSchema.url}> from ${packageMetaToFhir(fhirSchema.package_meta)}`
1271
2051
  );
1272
2052
  }
1273
- base = mkIdentifier(baseFs);
2053
+ base = mkIdentifier(baseFs);
2054
+ }
2055
+ const fields = mkFields(register, fhirSchema, [], fhirSchema.elements, logger);
2056
+ const nested = mkNestedTypes(register, fhirSchema, logger);
2057
+ const dependencies = extractDependencies(identifier, base, fields, nested);
2058
+ const typeSchema = {
2059
+ identifier,
2060
+ base,
2061
+ fields,
2062
+ nested,
2063
+ description: fhirSchema.description,
2064
+ dependencies
2065
+ };
2066
+ const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger);
2067
+ return [typeSchema, ...bindingSchemas];
2068
+ }
2069
+ async function transformFhirSchema(register, fhirSchema, logger) {
2070
+ const schemas = transformFhirSchemaResource(register, fhirSchema, logger);
2071
+ if (isExtensionSchema(fhirSchema, mkIdentifier(fhirSchema))) {
2072
+ const schema = schemas[0];
2073
+ if (!schema) throw new Error(`Expected schema to be defined`);
2074
+ schema.metadata = {
2075
+ isExtension: true
2076
+ // Mark as extension for file organization
2077
+ };
2078
+ }
2079
+ return schemas;
2080
+ }
2081
+
2082
+ // src/fhir-types/hl7-fhir-r4-core/CodeSystem.ts
2083
+ var isCodeSystem = (resource) => {
2084
+ return resource !== null && typeof resource === "object" && resource.resourceType === "CodeSystem";
2085
+ };
2086
+
2087
+ // src/fhir-types/hl7-fhir-r4-core/ValueSet.ts
2088
+ var isValueSet = (resource) => {
2089
+ return resource !== null && typeof resource === "object" && resource.resourceType === "ValueSet";
2090
+ };
2091
+
2092
+ // src/typeschema/register.ts
2093
+ var readPackageDependencies = async (manager, packageMeta) => {
2094
+ const packageJSON = await manager.packageJson(packageMeta.name);
2095
+ const dependencies = packageJSON.dependencies;
2096
+ if (dependencies !== void 0) {
2097
+ return Object.entries(dependencies).map(([name, version]) => {
2098
+ return { name, version };
2099
+ });
2100
+ }
2101
+ return [];
2102
+ };
2103
+ var mkEmptyPkgIndex = (pkg) => {
2104
+ return {
2105
+ pkg,
2106
+ canonicalResolution: {},
2107
+ fhirSchemas: {},
2108
+ valueSets: {}
2109
+ };
2110
+ };
2111
+ var mkPackageAwareResolver = async (manager, pkg, deep, acc, logger) => {
2112
+ const pkgId = packageMetaToFhir(pkg);
2113
+ logger?.info(`${" ".repeat(deep * 2)}+ ${pkgId}`);
2114
+ if (acc[pkgId]) return acc[pkgId];
2115
+ const index = mkEmptyPkgIndex(pkg);
2116
+ for (const resource of await manager.search({ package: pkg })) {
2117
+ const rawUrl = resource.url;
2118
+ if (!rawUrl) continue;
2119
+ if (!(isStructureDefinition(resource) || isValueSet(resource) || isCodeSystem(resource))) continue;
2120
+ const url = rawUrl;
2121
+ if (index.canonicalResolution[url]) logger?.dry_warn(`Duplicate canonical URL: ${url} at ${pkgId}.`);
2122
+ index.canonicalResolution[url] = [{ deep, pkg, pkgId, resource }];
2123
+ }
2124
+ const deps = await readPackageDependencies(manager, pkg);
2125
+ for (const depPkg of deps) {
2126
+ const { canonicalResolution } = await mkPackageAwareResolver(manager, depPkg, deep + 1, acc, logger);
2127
+ for (const [surl, resolutions] of Object.entries(canonicalResolution)) {
2128
+ const url = surl;
2129
+ index.canonicalResolution[url] = [...index.canonicalResolution[url] || [], ...resolutions];
2130
+ }
2131
+ }
2132
+ for (const resolutionOptions of Object.values(index.canonicalResolution)) {
2133
+ resolutionOptions.sort((a, b) => a.deep - b.deep);
2134
+ }
2135
+ acc[pkgId] = index;
2136
+ return index;
2137
+ };
2138
+ var packageAgnosticResolveCanonical = (resolver, url, _logger) => {
2139
+ const options = Object.values(resolver).flatMap((pkg) => pkg.canonicalResolution[url]);
2140
+ if (!options) throw new Error(`No canonical resolution found for ${url} in any package`);
2141
+ return options[0]?.resource;
2142
+ };
2143
+ var registerFromManager = async (manager, { logger, fallbackPackageForNameResolution, focusedPackages }) => {
2144
+ const packages = focusedPackages ?? await manager.packages();
2145
+ const resolver = {};
2146
+ for (const pkg of packages) {
2147
+ await mkPackageAwareResolver(manager, pkg, 0, resolver, logger);
2148
+ }
2149
+ for (const { pkg, canonicalResolution } of Object.values(resolver)) {
2150
+ const pkgId = packageMetaToFhir(pkg);
2151
+ if (!resolver[pkgId]) throw new Error(`Package ${pkgId} not found`);
2152
+ let counter = 0;
2153
+ logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' begins...`);
2154
+ for (const [_url, options] of Object.entries(canonicalResolution)) {
2155
+ const resolition = options[0];
2156
+ if (!resolition) throw new Error(`Resource not found`);
2157
+ const resource = resolition.resource;
2158
+ const resourcePkg = resolition.pkg;
2159
+ if (isStructureDefinition(resource)) {
2160
+ const rfs = enrichFHIRSchema(
2161
+ fhirschema.translate(resource),
2162
+ resourcePkg
2163
+ );
2164
+ counter++;
2165
+ resolver[pkgId].fhirSchemas[rfs.url] = rfs;
2166
+ }
2167
+ if (isValueSet(resource)) {
2168
+ const rvs = enrichValueSet(resource, resourcePkg);
2169
+ resolver[pkgId].valueSets[rvs.url] = rvs;
2170
+ }
2171
+ }
2172
+ logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' completed: ${counter} successful`);
2173
+ }
2174
+ const resolveFs = (pkg, canonicalUrl) => {
2175
+ return resolver[packageMetaToFhir(pkg)]?.fhirSchemas[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.fhirSchemas[canonicalUrl];
2176
+ };
2177
+ const resolveVs = (pkg, canonicalUrl) => {
2178
+ return resolver[packageMetaToFhir(pkg)]?.valueSets[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.valueSets[canonicalUrl];
2179
+ };
2180
+ const ensureSpecializationCanonicalUrl = (name) => name.match(/^[a-zA-Z0-9]+$/) && `http://hl7.org/fhir/StructureDefinition/${name}` || name;
2181
+ const resolveFsGenealogy = (pkg, canonicalUrl) => {
2182
+ let fs7 = resolveFs(pkg, canonicalUrl);
2183
+ if (fs7 === void 0) throw new Error(`Failed to resolve FHIR Schema: '${canonicalUrl}'`);
2184
+ const genealogy = [fs7];
2185
+ while (fs7?.base) {
2186
+ const pkg2 = fs7.package_meta;
2187
+ const baseUrl = ensureSpecializationCanonicalUrl(fs7.base);
2188
+ fs7 = resolveFs(pkg2, baseUrl);
2189
+ if (fs7 === void 0)
2190
+ throw new Error(
2191
+ `Failed to resolve FHIR Schema base for '${canonicalUrl}'. Problem: '${baseUrl}' from '${packageMetaToFhir(pkg2)}'`
2192
+ );
2193
+ genealogy.push(fs7);
2194
+ }
2195
+ return genealogy;
2196
+ };
2197
+ const resolveFsSpecializations = (pkg, canonicalUrl) => {
2198
+ return resolveFsGenealogy(pkg, canonicalUrl).filter((fs7) => fs7.derivation === "specialization");
2199
+ };
2200
+ const resolveElementSnapshot = (fhirSchema, path) => {
2201
+ const geneology = resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url);
2202
+ const elemGeneology = resolveFsElementGenealogy(geneology, path);
2203
+ const elemSnapshot = fsElementSnapshot(elemGeneology);
2204
+ return elemSnapshot;
2205
+ };
2206
+ const getAllElementKeys = (elems) => {
2207
+ const keys = /* @__PURE__ */ new Set();
2208
+ for (const [key, elem] of Object.entries(elems)) {
2209
+ keys.add(key);
2210
+ for (const choiceKey of elem?.choices || []) {
2211
+ if (!elems[choiceKey]) {
2212
+ keys.add(choiceKey);
2213
+ }
2214
+ }
2215
+ }
2216
+ return Array.from(keys);
2217
+ };
2218
+ return {
2219
+ ...manager,
2220
+ testAppendFs(fs7) {
2221
+ const rfs = enrichFHIRSchema(fs7);
2222
+ const pkgId = packageMetaToFhir(rfs.package_meta);
2223
+ if (!resolver[pkgId]) resolver[pkgId] = mkEmptyPkgIndex(rfs.package_meta);
2224
+ resolver[pkgId].fhirSchemas[rfs.url] = rfs;
2225
+ },
2226
+ resolveFs,
2227
+ resolveFsGenealogy,
2228
+ resolveFsSpecializations,
2229
+ ensureSpecializationCanonicalUrl,
2230
+ resolveSd: (_pkg, canonicalUrl) => {
2231
+ const res = packageAgnosticResolveCanonical(resolver, canonicalUrl);
2232
+ if (isStructureDefinition(res)) return res;
2233
+ return void 0;
2234
+ },
2235
+ allFs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.fhirSchemas)),
2236
+ allVs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.valueSets)),
2237
+ resolveVs,
2238
+ resolveAny: (canonicalUrl) => packageAgnosticResolveCanonical(resolver, canonicalUrl),
2239
+ resolveElementSnapshot,
2240
+ getAllElementKeys,
2241
+ resolver,
2242
+ resolutionTree: () => {
2243
+ const res = {};
2244
+ for (const [_pkgId, pkgIndex] of Object.entries(resolver)) {
2245
+ const pkgName = pkgIndex.pkg.name;
2246
+ res[pkgName] = {};
2247
+ for (const [surl, resolutions] of Object.entries(pkgIndex.canonicalResolution)) {
2248
+ const url = surl;
2249
+ res[pkgName][url] = [];
2250
+ for (const resolution of resolutions) {
2251
+ res[pkgName][url].push({ deep: resolution.deep, pkg: resolution.pkg });
2252
+ }
2253
+ }
2254
+ }
2255
+ return res;
2256
+ }
2257
+ };
2258
+ };
2259
+ var resolveFsElementGenealogy = (genealogy, path) => {
2260
+ const [top, ...rest] = path;
2261
+ if (top === void 0) return [];
2262
+ return genealogy.map((fs7) => {
2263
+ if (!fs7.elements) return void 0;
2264
+ let elem = fs7.elements?.[top];
2265
+ for (const k of rest) {
2266
+ elem = elem?.elements?.[k];
2267
+ }
2268
+ return elem;
2269
+ }).filter((elem) => elem !== void 0);
2270
+ };
2271
+ function fsElementSnapshot(genealogy) {
2272
+ const revGenealogy = genealogy.reverse();
2273
+ const snapshot = Object.assign({}, ...revGenealogy);
2274
+ snapshot.elements = void 0;
2275
+ return snapshot;
2276
+ }
2277
+
2278
+ // src/typeschema/index.ts
2279
+ var codeableReferenceInR4 = "Use CodeableReference which is not provided by FHIR R4.";
2280
+ var availabilityInR4 = "Use Availability which is not provided by FHIR R4.";
2281
+ var skipMe = {
2282
+ "hl7.fhir.uv.extensions.r4#1.0.0": {
2283
+ "http://hl7.org/fhir/StructureDefinition/extended-contact-availability": availabilityInR4,
2284
+ "http://hl7.org/fhir/StructureDefinition/immunization-procedure": codeableReferenceInR4,
2285
+ "http://hl7.org/fhir/StructureDefinition/specimen-additive": codeableReferenceInR4,
2286
+ "http://hl7.org/fhir/StructureDefinition/workflow-barrier": codeableReferenceInR4,
2287
+ "http://hl7.org/fhir/StructureDefinition/workflow-protectiveFactor": codeableReferenceInR4,
2288
+ "http://hl7.org/fhir/StructureDefinition/workflow-reason": codeableReferenceInR4
2289
+ },
2290
+ "hl7.fhir.r5.core#5.0.0": {
2291
+ "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."
2292
+ }
2293
+ };
2294
+ var generateTypeSchemas = async (register, logger) => {
2295
+ const fhirSchemas = [];
2296
+ for (const fhirSchema of register.allFs()) {
2297
+ const pkgId = packageMetaToFhir(fhirSchema.package_meta);
2298
+ if (skipMe[pkgId]?.[fhirSchema.url]) {
2299
+ logger?.dry_warn(`Skip ${fhirSchema.url} from ${pkgId}. Reason: ${skipMe[pkgId]?.[fhirSchema.url]}`);
2300
+ continue;
2301
+ }
2302
+ fhirSchemas.push(...await transformFhirSchema(register, fhirSchema, logger));
1274
2303
  }
1275
- const fields = mkFields(register, fhirSchema, [], fhirSchema.elements, logger);
1276
- const nested = mkNestedTypes(register, fhirSchema, logger);
1277
- const dependencies = extractDependencies(identifier, base, fields, nested);
1278
- const typeSchema = {
1279
- identifier,
1280
- base,
1281
- fields,
1282
- nested,
1283
- description: fhirSchema.description,
1284
- dependencies
1285
- };
1286
- const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger);
1287
- return [typeSchema, ...bindingSchemas];
1288
- }
1289
- async function transformFhirSchema(register, fhirSchema, logger) {
1290
- const schemas = transformFhirSchemaResource(register, fhirSchema, logger);
1291
- if (isExtensionSchema(fhirSchema, mkIdentifier(fhirSchema))) {
1292
- const schema = schemas[0];
1293
- if (!schema) throw new Error(`Expected schema to be defined`);
1294
- schema.metadata = {
1295
- isExtension: true
1296
- // Mark as extension for file organization
1297
- };
2304
+ for (const vsSchema of register.allVs()) {
2305
+ fhirSchemas.push(await transformValueSet(register, vsSchema));
1298
2306
  }
1299
- return schemas;
1300
- }
1301
-
1302
- // src/typeschema/utils.ts
1303
- var groupByPackages = (typeSchemas) => {
1304
- const grouped = {};
1305
- for (const ts of typeSchemas) {
1306
- const pkgName = ts.identifier.package;
1307
- if (!grouped[pkgName]) grouped[pkgName] = [];
1308
- grouped[pkgName].push(ts);
2307
+ return fhirSchemas;
2308
+ };
2309
+ var mutableSelectFields = (schema, selectFields) => {
2310
+ const selectedFields = {};
2311
+ const selectPolimorphic = {};
2312
+ for (const fieldName of selectFields) {
2313
+ const field = schema.fields?.[fieldName];
2314
+ if (!schema.fields || !field) throw new Error(`Field ${fieldName} not found`);
2315
+ if (isChoiceDeclarationField(field)) {
2316
+ if (!selectPolimorphic[fieldName]) selectPolimorphic[fieldName] = {};
2317
+ selectPolimorphic[fieldName].declaration = field.choices;
2318
+ } else if (isChoiceInstanceField(field)) {
2319
+ const choiceName = field.choiceOf;
2320
+ if (!selectPolimorphic[choiceName]) selectPolimorphic[choiceName] = {};
2321
+ selectPolimorphic[choiceName].instances = [...selectPolimorphic[choiceName].instances ?? [], fieldName];
2322
+ } else {
2323
+ selectedFields[fieldName] = field;
2324
+ }
1309
2325
  }
1310
- for (const [packageName, typeSchemas2] of Object.entries(grouped)) {
1311
- const dict = {};
1312
- for (const ts of typeSchemas2) {
1313
- dict[JSON.stringify(ts.identifier)] = ts;
2326
+ for (const [choiceName, { declaration, instances }] of Object.entries(selectPolimorphic)) {
2327
+ const choices = instances ?? declaration;
2328
+ assert3(choices);
2329
+ for (const choiceInstanceName of choices) {
2330
+ const field = schema.fields?.[choiceInstanceName];
2331
+ assert3(field);
2332
+ selectedFields[choiceInstanceName] = field;
1314
2333
  }
1315
- const tmp = Object.values(dict);
1316
- tmp.sort((a, b) => a.identifier.name.localeCompare(b.identifier.name));
1317
- grouped[packageName] = tmp;
2334
+ const decl = schema.fields?.[choiceName];
2335
+ assert3(decl);
2336
+ selectedFields[choiceName] = { ...decl, choices };
1318
2337
  }
1319
- return grouped;
2338
+ schema.fields = selectedFields;
1320
2339
  };
1321
- var treeShakeTypeSchema = (schema, rule, _logger) => {
1322
- schema = structuredClone(schema);
1323
- if (isPrimitiveTypeSchema(schema) || isValueSetTypeSchema(schema) || isBindingSchema(schema)) return schema;
1324
- for (const fieldName of rule.ignoreFields ?? []) {
1325
- if (schema.fields && !schema.fields[fieldName]) throw new Error(`Field ${fieldName} not found`);
2340
+ var mutableIgnoreFields = (schema, ignoreFields) => {
2341
+ for (const fieldName of ignoreFields) {
2342
+ const field = schema.fields?.[fieldName];
2343
+ if (!schema.fields || !field) throw new Error(`Field ${fieldName} not found`);
1326
2344
  if (schema.fields) {
2345
+ if (isChoiceDeclarationField(field)) {
2346
+ for (const choiceName of field.choices) {
2347
+ delete schema.fields[choiceName];
2348
+ }
2349
+ }
2350
+ if (isChoiceInstanceField(field)) {
2351
+ const choiceDeclaration = schema.fields[field.choiceOf];
2352
+ assert3(isChoiceDeclarationField(choiceDeclaration));
2353
+ choiceDeclaration.choices = choiceDeclaration.choices.filter((c) => c !== fieldName);
2354
+ if (choiceDeclaration.choices.length === 0) {
2355
+ delete schema.fields[field.choiceOf];
2356
+ }
2357
+ }
1327
2358
  delete schema.fields[fieldName];
1328
2359
  }
1329
2360
  }
2361
+ };
2362
+ var treeShakeTypeSchema = (schema, rule, _logger) => {
2363
+ schema = structuredClone(schema);
2364
+ if (isPrimitiveTypeSchema(schema) || isValueSetTypeSchema(schema) || isBindingSchema(schema)) return schema;
2365
+ if (rule.selectFields) {
2366
+ if (rule.ignoreFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule");
2367
+ mutableSelectFields(schema, rule.selectFields);
2368
+ }
2369
+ if (rule.ignoreFields) {
2370
+ if (rule.selectFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule");
2371
+ mutableIgnoreFields(schema, rule.ignoreFields);
2372
+ }
2373
+ if (schema.nested) {
2374
+ const usedTypes = /* @__PURE__ */ new Set();
2375
+ const collectUsedNestedTypes = (s) => {
2376
+ Object.values(s.fields ?? {}).filter(isNotChoiceDeclarationField).filter((f) => isNestedIdentifier(f.type)).forEach((f) => {
2377
+ const url = f.type.url;
2378
+ if (!usedTypes.has(url)) {
2379
+ usedTypes.add(url);
2380
+ const nestedTypeDef = schema.nested?.find((f2) => f2.identifier.url === url);
2381
+ assert3(nestedTypeDef);
2382
+ collectUsedNestedTypes(nestedTypeDef);
2383
+ }
2384
+ });
2385
+ };
2386
+ collectUsedNestedTypes(schema);
2387
+ schema.nested = schema.nested.filter((n) => usedTypes.has(n.identifier.url));
2388
+ }
1330
2389
  schema.dependencies = extractDependencies(schema.identifier, schema.base, schema.fields, schema.nested);
1331
2390
  return schema;
1332
2391
  };
@@ -1351,7 +2410,10 @@ var treeShake = (tsIndex, treeShake2, { resolutionTree, logger }) => {
1351
2410
  if (!schema.dependencies) continue;
1352
2411
  schema.dependencies.forEach((dep) => {
1353
2412
  const depSchema = tsIndex.resolve(dep);
1354
- if (!depSchema) throw new Error(`Schema not found for ${dep}`);
2413
+ if (!depSchema)
2414
+ throw new Error(
2415
+ `Dependent schema ${JSON.stringify(dep)} not found for ${JSON.stringify(schema.identifier)}`
2416
+ );
1355
2417
  const id = JSON.stringify(depSchema.identifier);
1356
2418
  if (!acc[id]) newSchemas.push(depSchema);
1357
2419
  });
@@ -1369,334 +2431,652 @@ var treeShake = (tsIndex, treeShake2, { resolutionTree, logger }) => {
1369
2431
  const shaked = collectDeps(focusedSchemas, {});
1370
2432
  return mkTypeSchemaIndex(shaked, { resolutionTree, logger });
1371
2433
  };
1372
- var resourceRelatives = (schemas) => {
1373
- const regularSchemas = schemas.filter((e) => isResourceTypeSchema(e) || isLogicalTypeSchema(e));
1374
- const directPairs = [];
1375
- for (const schema of regularSchemas) {
1376
- if (schema.base) {
1377
- directPairs.push({ parent: schema.base, child: schema.identifier });
1378
- }
2434
+
2435
+ // src/api/mustache/generator/DebugMixinProvider.ts
2436
+ var DebugMixinProvider = class {
2437
+ constructor(mode) {
2438
+ this.mode = mode;
1379
2439
  }
1380
- const allPairs = [...directPairs];
1381
- const findTransitiveRelatives = (parentRef) => {
1382
- const directChildren = directPairs.filter((pair) => pair.parent.name === parentRef.name).map((pair) => pair.child);
1383
- const transitiveChildren = [];
1384
- for (const child of directChildren) {
1385
- transitiveChildren.push(...findTransitiveRelatives(child));
2440
+ apply(target) {
2441
+ return this._addDebug(target);
2442
+ }
2443
+ _addDebug(value) {
2444
+ if (Array.isArray(value)) {
2445
+ return value.map((v) => this._addDebug(v));
1386
2446
  }
1387
- return [...directChildren, ...transitiveChildren];
1388
- };
1389
- for (const pair of directPairs) {
1390
- const transitiveChildren = findTransitiveRelatives(pair.child);
1391
- for (const transitiveChild of transitiveChildren) {
1392
- if (!directPairs.some((dp) => dp.parent.name === pair.parent.name && dp.child.name === transitiveChild.name)) {
1393
- allPairs.push({ parent: pair.parent, child: transitiveChild });
2447
+ if (value !== null && typeof value === "object") {
2448
+ const obj = value;
2449
+ const result = {};
2450
+ const debugString = JSON.stringify(obj, null, this.mode === "FORMATTED" ? 2 : void 0);
2451
+ for (const [key, val] of Object.entries(obj)) {
2452
+ result[key] = this._addDebug(val);
1394
2453
  }
2454
+ result.debug = debugString;
2455
+ return result;
1395
2456
  }
2457
+ return value;
1396
2458
  }
1397
- return allPairs;
1398
2459
  };
1399
- var mkTypeSchemaIndex = (schemas, { resolutionTree, logger }) => {
1400
- const index = {};
1401
- const append = (schema) => {
1402
- const url = schema.identifier.url;
1403
- const pkg = schema.identifier.package;
1404
- if (!index[url]) index[url] = {};
1405
- if (index[url][schema.identifier.package] && pkg !== "shared") {
1406
- const r1 = JSON.stringify(schema.identifier, void 0, 2);
1407
- const r2 = JSON.stringify(index[url][pkg]?.identifier, void 0, 2);
1408
- if (r1 !== r2) throw new Error(`Duplicate schema: ${r1} and ${r2}`);
1409
- return;
2460
+
2461
+ // src/api/mustache/generator/LambdaMixinProvider.ts
2462
+ var LambdaMixinProvider = class {
2463
+ constructor(nameGenerator) {
2464
+ this.nameGenerator = nameGenerator;
2465
+ this.lambda = {
2466
+ saveTypeName: () => (text, render) => this.nameGenerator.generateType(render(text)),
2467
+ saveEnumValueName: () => (text, render) => this.nameGenerator.generateEnumValue(render(text)),
2468
+ saveFieldName: () => (text, render) => this.nameGenerator.generateField(render(text)),
2469
+ camelCase: () => (text, render) => camelCase(render(text)),
2470
+ snakeCase: () => (text, render) => snakeCase(render(text)),
2471
+ pascalCase: () => (text, render) => pascalCase(render(text)),
2472
+ kebabCase: () => (text, render) => kebabCase(render(text)),
2473
+ lowerCase: () => (text, render) => render(text).toLowerCase(),
2474
+ upperCase: () => (text, render) => render(text).toUpperCase()
2475
+ };
2476
+ }
2477
+ lambda;
2478
+ apply(target) {
2479
+ return {
2480
+ ...target,
2481
+ lambda: this.lambda
2482
+ };
2483
+ }
2484
+ };
2485
+
2486
+ // src/api/mustache/generator/NameGenerator.ts
2487
+ var NameGenerator = class {
2488
+ constructor(keywords, typeMap, nameTransformations, unsaveCharacterPattern) {
2489
+ this.keywords = keywords;
2490
+ this.typeMap = typeMap;
2491
+ this.nameTransformations = nameTransformations;
2492
+ this.unsaveCharacterPattern = unsaveCharacterPattern;
2493
+ }
2494
+ _replaceUnsaveChars(name) {
2495
+ const pattern = this.unsaveCharacterPattern instanceof RegExp ? this.unsaveCharacterPattern : new RegExp(this.unsaveCharacterPattern, "g");
2496
+ return name.replace(pattern, "_");
2497
+ }
2498
+ _applyNameTransformations(name, transformations) {
2499
+ for (const transformation of this.nameTransformations.common) {
2500
+ name = name.replace(
2501
+ transformation.pattern instanceof RegExp ? transformation.pattern : new RegExp(transformation.pattern, "g"),
2502
+ transformation.format
2503
+ );
1410
2504
  }
1411
- index[url][pkg] = schema;
1412
- };
1413
- for (const schema of schemas) {
1414
- append(schema);
2505
+ for (const transformation of transformations) {
2506
+ name = name.replace(
2507
+ transformation.pattern instanceof RegExp ? transformation.pattern : new RegExp(transformation.pattern, "g"),
2508
+ transformation.format
2509
+ );
2510
+ }
2511
+ return name;
1415
2512
  }
1416
- const relations = resourceRelatives(schemas);
1417
- const resolve3 = (id) => index[id.url]?.[id.package];
1418
- const resolveByUrl = (pkgName, url) => {
1419
- if (resolutionTree) {
1420
- const resolution = resolutionTree[pkgName]?.[url]?.[0];
1421
- if (resolution) {
1422
- return index[url]?.[resolution.pkg.name];
2513
+ _generateTypeName(name) {
2514
+ if (this.typeMap[name]) {
2515
+ name = this.typeMap[name];
2516
+ } else {
2517
+ name = this._applyNameTransformations(name, this.nameTransformations.type);
2518
+ name = name.charAt(0).toUpperCase() + name.slice(1);
2519
+ if (this.keywords.has(name)) {
2520
+ return `_${name}`;
1423
2521
  }
2522
+ name = this._replaceUnsaveChars(name);
1424
2523
  }
1425
- return index[url]?.[pkgName];
1426
- };
1427
- const resourceChildren = (id) => {
1428
- return relations.filter((relative) => relative.parent.name === id.name).map((relative) => relative.child);
1429
- };
1430
- const tryHierarchy = (schema) => {
1431
- const res = [];
1432
- let cur = schema;
1433
- while (cur) {
1434
- res.push(cur);
1435
- const base = cur.base;
1436
- if (base === void 0) break;
1437
- const resolved = resolve3(base);
1438
- if (!resolved) {
1439
- logger?.warn(
1440
- `Failed to resolve base type: ${res.map((e) => `${e.identifier.url} (${e.identifier.kind})`).join(", ")}`
1441
- );
1442
- return void 0;
1443
- }
1444
- cur = resolved;
2524
+ return name;
2525
+ }
2526
+ generateEnumType(name) {
2527
+ return this._generateTypeName(name);
2528
+ }
2529
+ _generateTypeFromTypeRef(typeRef) {
2530
+ if (typeRef.kind === "primitive-type") {
2531
+ return this._generateTypeName(typeRef.name);
1445
2532
  }
1446
- return res;
1447
- };
1448
- const hierarchy = (schema) => {
1449
- const genealogy = tryHierarchy(schema);
1450
- if (genealogy === void 0) {
1451
- throw new Error(`Failed to resolve base type: ${schema.identifier.url} (${schema.identifier.kind})`);
2533
+ return this._generateTypeName(
2534
+ typeRef.url.split("/").pop()?.split("#").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("") ?? "<UNKNOWN>"
2535
+ );
2536
+ }
2537
+ generateFieldType(schema) {
2538
+ if (schema.enum) {
2539
+ return this.generateEnumType(schema.binding?.name ?? schema.type.name);
1452
2540
  }
1453
- return genealogy;
1454
- };
1455
- const findLastSpecialization = (schema) => {
1456
- const nonConstraintSchema = hierarchy(schema).find((s) => s.identifier.kind !== "profile");
1457
- if (!nonConstraintSchema) {
1458
- throw new Error(`No non-constraint schema found in hierarchy for: ${schema.identifier.name}`);
2541
+ return this._generateTypeFromTypeRef(schema.type);
2542
+ }
2543
+ generateType(schemaOrRefOrString) {
2544
+ if (typeof schemaOrRefOrString === "string") {
2545
+ return this._generateTypeName(schemaOrRefOrString);
1459
2546
  }
1460
- return nonConstraintSchema;
1461
- };
1462
- const findLastSpecializationByIdentifier = (id) => {
1463
- const schema = resolve3(id);
1464
- if (!schema) return id;
1465
- return findLastSpecialization(schema).identifier;
1466
- };
1467
- const flatProfile = (schema) => {
1468
- const hierarchySchemas = hierarchy(schema);
1469
- const constraintSchemas = hierarchySchemas.filter((s) => s.identifier.kind === "profile");
1470
- const nonConstraintSchema = hierarchySchemas.find((s) => s.identifier.kind !== "profile");
1471
- if (!nonConstraintSchema)
1472
- throw new Error(`No non-constraint schema found in hierarchy for ${schema.identifier.name}`);
1473
- const mergedFields = {};
1474
- for (const anySchema of constraintSchemas.slice().reverse()) {
1475
- const schema2 = anySchema;
1476
- if (!schema2.fields) continue;
1477
- for (const [fieldName, fieldConstraints] of Object.entries(schema2.fields)) {
1478
- if (mergedFields[fieldName]) {
1479
- mergedFields[fieldName] = {
1480
- ...mergedFields[fieldName],
1481
- ...fieldConstraints
1482
- };
1483
- } else {
1484
- mergedFields[fieldName] = { ...fieldConstraints };
1485
- }
1486
- }
2547
+ if ("url" in schemaOrRefOrString) {
2548
+ return this._generateTypeFromTypeRef(schemaOrRefOrString);
1487
2549
  }
1488
- const deps = {};
1489
- for (const e of constraintSchemas.flatMap((e2) => e2.dependencies ?? [])) {
1490
- deps[e.url] = e;
2550
+ return this._generateTypeFromTypeRef(schemaOrRefOrString.identifier);
2551
+ }
2552
+ generateField(name) {
2553
+ name = this._applyNameTransformations(name, this.nameTransformations.field);
2554
+ if (this.keywords.has(name)) {
2555
+ return `_${name}`;
1491
2556
  }
1492
- const dependencies = Object.values(deps);
1493
- return {
1494
- ...schema,
1495
- base: nonConstraintSchema.identifier,
1496
- fields: mergedFields,
1497
- dependencies
1498
- };
1499
- };
1500
- const isWithMetaField = (profile) => {
1501
- const genealogy = tryHierarchy(profile);
1502
- if (!genealogy) return false;
1503
- return genealogy.filter(isSpecializationTypeSchema).some((schema) => {
1504
- return schema.fields?.meta !== void 0;
1505
- });
1506
- };
1507
- const exportTree = async (filename) => {
1508
- const tree = {};
1509
- for (const [pkgId, shemas] of Object.entries(groupByPackages(schemas))) {
1510
- tree[pkgId] = {
1511
- "primitive-type": {},
1512
- "complex-type": {},
1513
- resource: {},
1514
- "value-set": {},
1515
- nested: {},
1516
- binding: {},
1517
- profile: {},
1518
- logical: {}
1519
- };
1520
- for (const schema of shemas) {
1521
- tree[pkgId][schema.identifier.kind][schema.identifier.url] = {};
1522
- }
2557
+ name = this._replaceUnsaveChars(name);
2558
+ return name;
2559
+ }
2560
+ generateEnumValue(name) {
2561
+ name = this._applyNameTransformations(name, this.nameTransformations.enumValue);
2562
+ if (this.keywords.has(name)) {
2563
+ return `_${name}`;
1523
2564
  }
1524
- const raw = filename.endsWith(".yaml") ? YAML.stringify(tree) : JSON.stringify(tree, void 0, 2);
1525
- await afs2.mkdir(Path4.dirname(filename), { recursive: true });
1526
- await afs2.writeFile(filename, raw);
1527
- };
1528
- return {
1529
- _schemaIndex: index,
1530
- _relations: relations,
1531
- collectComplexTypes: () => schemas.filter(isComplexTypeTypeSchema),
1532
- collectResources: () => schemas.filter(isResourceTypeSchema),
1533
- collectLogicalModels: () => schemas.filter(isLogicalTypeSchema),
1534
- collectProfiles: () => schemas.filter(isProfileTypeSchema),
1535
- resolve: resolve3,
1536
- resolveByUrl,
1537
- resourceChildren,
1538
- tryHierarchy,
1539
- hierarchy,
1540
- findLastSpecialization,
1541
- findLastSpecializationByIdentifier,
1542
- flatProfile,
1543
- isWithMetaField,
1544
- exportTree
1545
- };
2565
+ name = this._replaceUnsaveChars(name).toUpperCase();
2566
+ return name;
2567
+ }
1546
2568
  };
1547
- var CodegenLogger = class _CodegenLogger {
1548
- options;
1549
- dryWarnSet = /* @__PURE__ */ new Set();
1550
- constructor(options = {}) {
1551
- this.options = {
1552
- timestamp: false,
1553
- verbose: false,
1554
- ...options
1555
- };
2569
+ var TemplateFileCache = class {
2570
+ templateBaseDir;
2571
+ templateCache = {};
2572
+ constructor(templateBaseDir) {
2573
+ this.templateBaseDir = Path__default.resolve(templateBaseDir);
2574
+ }
2575
+ _normalizeName(name) {
2576
+ if (name.endsWith(".mustache")) {
2577
+ return name;
2578
+ }
2579
+ return `${name}.mustache`;
2580
+ }
2581
+ read(template) {
2582
+ return this.readTemplate(template.source);
2583
+ }
2584
+ readTemplate(name) {
2585
+ const normalizedName = this._normalizeName(name);
2586
+ if (!this.templateCache[normalizedName]) {
2587
+ this.templateCache[normalizedName] = fs__default.readFileSync(
2588
+ Path__default.join(this.templateBaseDir, normalizedName),
2589
+ "utf-8"
2590
+ );
2591
+ }
2592
+ return this.templateCache[normalizedName];
1556
2593
  }
1557
- static consoleLevelsMap = {
1558
- [1 /* INFO */]: console.log,
1559
- [2 /* WARN */]: console.warn,
1560
- [3 /* ERROR */]: console.error,
1561
- [0 /* DEBUG */]: console.log,
1562
- [4 /* SILENT */]: () => {
2594
+ };
2595
+
2596
+ // src/api/mustache/generator/ListElementInformationMixinProvider.ts
2597
+ var ListElementInformationMixinProvider = class _ListElementInformationMixinProvider {
2598
+ static _array(value) {
2599
+ return Array.isArray(value) ? value : Array.from(value);
2600
+ }
2601
+ apply(source) {
2602
+ return this._addListElementInformation(source);
2603
+ }
2604
+ _addListElementInformation(value) {
2605
+ if (Array.isArray(value) || value instanceof Set) {
2606
+ return _ListElementInformationMixinProvider._array(value).map((v, index, array) => {
2607
+ if (typeof v === "object" && v !== null) {
2608
+ return {
2609
+ ...this._addListElementInformation(v),
2610
+ "-index": index,
2611
+ "-length": array.length,
2612
+ "-first": index === 0,
2613
+ "-last": index === array.length - 1
2614
+ };
2615
+ }
2616
+ return v;
2617
+ });
1563
2618
  }
1564
- };
1565
- formatMessage(level, message, color) {
1566
- const timestamp = this.options.timestamp ? `${pc.gray((/* @__PURE__ */ new Date()).toLocaleTimeString())} ` : "";
1567
- const prefix = this.options.prefix ? `${pc.cyan(`[${this.options.prefix}]`)} ` : "";
1568
- return `${timestamp}${color(level)} ${prefix}${message}`;
2619
+ if (value !== null && typeof value === "object") {
2620
+ const obj = value;
2621
+ const result = {};
2622
+ for (const [key, val] of Object.entries(obj)) {
2623
+ result[key] = this._addListElementInformation(val);
2624
+ }
2625
+ return result;
2626
+ }
2627
+ return value;
1569
2628
  }
1570
- isSuppressed(level) {
1571
- return this.options.suppressLoggingLevel === "all" || this.options.suppressLoggingLevel?.includes(level) || false;
2629
+ };
2630
+
2631
+ // src/api/mustache/types.ts
2632
+ var PRIMITIVE_TYPES = [
2633
+ "boolean",
2634
+ "instant",
2635
+ "time",
2636
+ "date",
2637
+ "dateTime",
2638
+ "decimal",
2639
+ "integer",
2640
+ "unsignedInt",
2641
+ "positiveInt",
2642
+ "integer64",
2643
+ "base64Binary",
2644
+ "uri",
2645
+ "url",
2646
+ "canonical",
2647
+ "oid",
2648
+ "uuid",
2649
+ "string",
2650
+ "code",
2651
+ "markdown",
2652
+ "id",
2653
+ "xhtml"
2654
+ ];
2655
+
2656
+ // src/api/mustache/generator/ViewModelFactory.ts
2657
+ var ViewModelFactory = class {
2658
+ constructor(tsIndex, nameGenerator, filterPred) {
2659
+ this.tsIndex = tsIndex;
2660
+ this.nameGenerator = nameGenerator;
2661
+ this.filterPred = filterPred;
2662
+ }
2663
+ arrayMixinProvider = new ListElementInformationMixinProvider();
2664
+ createUtility() {
2665
+ return this._createForRoot();
2666
+ }
2667
+ createComplexType(typeRef, cache = { resourcesByUri: {}, complexTypesByUri: {} }) {
2668
+ const base = this._createForComplexType(typeRef, cache);
2669
+ const parents = this._createParentsFor(base.schema, cache);
2670
+ const children = this._createChildrenFor(typeRef, cache);
2671
+ const inheritedFields = parents.flatMap((p) => p.fields);
2672
+ return this.arrayMixinProvider.apply({
2673
+ ...this._createForRoot(),
2674
+ ...base,
2675
+ parents,
2676
+ children,
2677
+ inheritedFields,
2678
+ allFields: [...base.fields, ...parents.flatMap((p) => p.fields)],
2679
+ hasChildren: children.length > 0,
2680
+ hasParents: parents.length > 0,
2681
+ hasInheritedFields: inheritedFields.length > 0
2682
+ });
1572
2683
  }
1573
- tryWriteToConsole(level, formattedMessage) {
1574
- if (this.isSuppressed(level)) return;
1575
- const logFn = _CodegenLogger.consoleLevelsMap[level] || console.log;
1576
- logFn(formattedMessage);
2684
+ createResource(typeRef, cache = { resourcesByUri: {}, complexTypesByUri: {} }) {
2685
+ const base = this._createForResource(typeRef, cache);
2686
+ const parents = this._createParentsFor(base.schema, cache);
2687
+ const children = this._createChildrenFor(typeRef, cache);
2688
+ const inheritedFields = parents.flatMap((p) => p.fields);
2689
+ return this.arrayMixinProvider.apply({
2690
+ ...this._createForRoot(),
2691
+ ...base,
2692
+ parents,
2693
+ children,
2694
+ inheritedFields,
2695
+ allFields: [...base.fields, ...inheritedFields],
2696
+ hasChildren: children.length > 0,
2697
+ hasParents: parents.length > 0,
2698
+ hasInheritedFields: inheritedFields.length > 0
2699
+ });
1577
2700
  }
1578
- /**
1579
- * Success message with checkmark
1580
- */
1581
- success(message) {
1582
- this.tryWriteToConsole(1 /* INFO */, this.formatMessage("", message, pc.green));
2701
+ _createFor(typeRef, cache, nestedIn) {
2702
+ if (typeRef.kind === "complex-type") {
2703
+ return this._createForComplexType(typeRef, cache, nestedIn);
2704
+ }
2705
+ if (typeRef.kind === "resource") {
2706
+ return this._createForResource(typeRef, cache, nestedIn);
2707
+ }
2708
+ throw new Error(`Unknown type ${typeRef.kind}`);
1583
2709
  }
1584
- /**
1585
- * Error message with X mark
1586
- */
1587
- error(message, error) {
1588
- if (this.isSuppressed(3 /* ERROR */)) return;
1589
- console.error(this.formatMessage("X", message, pc.red));
1590
- if (error && this.options.verbose) {
1591
- console.error(pc.red(` ${error.message}`));
1592
- if (error.stack) {
1593
- console.error(pc.gray(error.stack));
1594
- }
2710
+ _createForComplexType(typeRef, cache, nestedIn) {
2711
+ const type = this.tsIndex.resolve(typeRef);
2712
+ if (!type) {
2713
+ throw new Error(`ComplexType ${typeRef.name} not found`);
1595
2714
  }
2715
+ if (!Object.hasOwn(cache.complexTypesByUri, type.identifier.url)) {
2716
+ cache.complexTypesByUri[type.identifier.url] = this._createTypeViewModel(type, cache, nestedIn);
2717
+ }
2718
+ const res = cache.complexTypesByUri[type.identifier.url];
2719
+ if (!res) throw new Error(`ComplexType ${typeRef.name} not found`);
2720
+ return res;
1596
2721
  }
1597
- /**
1598
- * Warning message with warning sign
1599
- */
1600
- warn(message) {
1601
- this.tryWriteToConsole(2 /* WARN */, this.formatMessage("!", message, pc.yellow));
2722
+ _createForResource(typeRef, cache, nestedIn) {
2723
+ const type = this.tsIndex.resolve(typeRef);
2724
+ if (!type) {
2725
+ throw new Error(`Resource ${typeRef.name} not found`);
2726
+ }
2727
+ if (!Object.hasOwn(cache.resourcesByUri, type.identifier.url)) {
2728
+ cache.resourcesByUri[type.identifier.url] = this._createTypeViewModel(type, cache, nestedIn);
2729
+ }
2730
+ const res = cache.resourcesByUri[type.identifier.url];
2731
+ if (!res) throw new Error(`Resource ${typeRef.name} not found`);
2732
+ return res;
1602
2733
  }
1603
- dry_warn(message) {
1604
- if (!this.dryWarnSet.has(message)) {
1605
- this.warn(message);
1606
- this.dryWarnSet.add(message);
2734
+ _createChildrenFor(typeRef, cache, nestedIn) {
2735
+ if (isComplexTypeIdentifier(typeRef)) {
2736
+ return this.tsIndex.resourceChildren(typeRef).filter(isComplexTypeIdentifier).filter(this.filterPred).map((childRef) => this._createFor(childRef, cache, nestedIn));
2737
+ }
2738
+ if (isResourceIdentifier(typeRef)) {
2739
+ return this.tsIndex.resourceChildren(typeRef).filter(isResourceIdentifier).filter(this.filterPred).map((childRef) => this._createFor(childRef, cache, nestedIn));
2740
+ }
2741
+ return [];
2742
+ }
2743
+ _createParentsFor(base, cache) {
2744
+ const parents = [];
2745
+ let parentRef = "base" in base ? base.base : void 0;
2746
+ while (parentRef) {
2747
+ parents.push(this._createFor(parentRef, cache, void 0));
2748
+ const parent = this.tsIndex.resolve(parentRef);
2749
+ parentRef = parent && "base" in parent ? parent.base : void 0;
1607
2750
  }
2751
+ return parents;
1608
2752
  }
1609
- /**
1610
- * Info message with info icon
1611
- */
1612
- info(message) {
1613
- this.tryWriteToConsole(1 /* INFO */, this.formatMessage("i", message, pc.blue));
2753
+ _createForNestedType(nested, cache, nestedIn) {
2754
+ const base = this._createTypeViewModel(nested, cache, nestedIn);
2755
+ const parents = this._createParentsFor(nested, cache);
2756
+ const children = this._createChildrenFor(nested.identifier, cache, nestedIn);
2757
+ const inheritedFields = parents.flatMap((p) => p.fields);
2758
+ return {
2759
+ ...base,
2760
+ parents,
2761
+ children,
2762
+ inheritedFields,
2763
+ allFields: [...base.fields, ...inheritedFields],
2764
+ hasChildren: children.length > 0,
2765
+ hasParents: parents.length > 0,
2766
+ hasInheritedFields: inheritedFields.length > 0
2767
+ };
1614
2768
  }
1615
- /**
1616
- * Debug message (only shows in verbose mode)
1617
- */
1618
- debug(message) {
1619
- if (this.options.verbose) {
1620
- this.tryWriteToConsole(0 /* DEBUG */, this.formatMessage("\u{1F41B}", message, pc.magenta));
2769
+ _createTypeViewModel(schema, cache, nestedIn) {
2770
+ const fields = Object.entries(("fields" in schema ? schema.fields : {}) ?? {});
2771
+ const nestedComplexTypes = this._collectNestedComplex(schema, cache);
2772
+ const nestedEnums = this._collectNestedEnums(fields);
2773
+ const dependencies = this._collectDependencies(schema);
2774
+ const name = {
2775
+ name: schema.identifier.name,
2776
+ saveName: this.nameGenerator.generateType(schema)
2777
+ };
2778
+ return {
2779
+ nestedComplexTypes,
2780
+ nestedEnums,
2781
+ dependencies,
2782
+ isNested: !!nestedIn,
2783
+ schema,
2784
+ ...name,
2785
+ isResource: this._createIsResource(schema.identifier),
2786
+ isComplexType: this._createIsComplexType(schema.identifier),
2787
+ hasFields: fields.length > 0,
2788
+ hasNestedComplexTypes: nestedComplexTypes.length > 0,
2789
+ hasNestedEnums: nestedEnums.length > 0,
2790
+ fields: fields.filter(([_fieldName, fieldSchema]) => !!fieldSchema.type).sort((a, b) => a[0].localeCompare(b[0])).map(([fieldName, fieldSchema]) => {
2791
+ return {
2792
+ owner: name,
2793
+ schema: fieldSchema,
2794
+ name: fieldName,
2795
+ saveName: this.nameGenerator.generateField(fieldName),
2796
+ typeName: this.nameGenerator.generateFieldType(fieldSchema),
2797
+ isArray: fieldSchema.array,
2798
+ isRequired: fieldSchema.required,
2799
+ isEnum: !!fieldSchema.enum,
2800
+ isSizeConstrained: fieldSchema.min !== void 0 || fieldSchema.max !== void 0,
2801
+ min: fieldSchema.min,
2802
+ max: fieldSchema.max,
2803
+ isResource: this._createIsResource(fieldSchema.type),
2804
+ isComplexType: this._createIsComplexType(fieldSchema.type),
2805
+ isPrimitive: this._createIsPrimitiveType(fieldSchema.type),
2806
+ isCode: fieldSchema.type?.name === "code",
2807
+ isIdentifier: fieldSchema.type?.name === "Identifier",
2808
+ isReference: fieldSchema.type?.name === "Reference"
2809
+ };
2810
+ })
2811
+ };
2812
+ }
2813
+ _collectDependencies(schema) {
2814
+ const dependencies = {
2815
+ resources: [],
2816
+ complexTypes: []
2817
+ };
2818
+ if ("dependencies" in schema && schema.dependencies) {
2819
+ schema.dependencies.filter((dependency) => dependency.kind === "complex-type").map((dependency) => ({ name: dependency.name, saveName: this.nameGenerator.generateType(dependency) })).forEach((dependency) => {
2820
+ dependencies.complexTypes.push(dependency);
2821
+ });
2822
+ schema.dependencies.filter((dependency) => dependency.kind === "resource").map((dependency) => ({ name: dependency.name, saveName: this.nameGenerator.generateType(dependency) })).forEach((dependency) => {
2823
+ dependencies.resources.push(dependency);
2824
+ });
2825
+ }
2826
+ if ("nested" in schema && schema.nested) {
2827
+ schema.nested.map((nested) => this._collectDependencies(nested)).forEach((d) => {
2828
+ d.complexTypes.filter(
2829
+ (complexType) => !dependencies.complexTypes.some((dependency) => dependency.name === complexType.name)
2830
+ ).forEach((complexType) => {
2831
+ dependencies.complexTypes.push(complexType);
2832
+ });
2833
+ d.resources.filter(
2834
+ (resource) => !dependencies.resources.some((dependency) => dependency.name === resource.name)
2835
+ ).forEach((resource) => {
2836
+ dependencies.resources.push(resource);
2837
+ });
2838
+ });
1621
2839
  }
2840
+ return dependencies;
1622
2841
  }
1623
- /**
1624
- * Step message with rocket
1625
- */
1626
- step(message) {
1627
- this.tryWriteToConsole(1 /* INFO */, this.formatMessage("\u{1F680}", message, pc.cyan));
2842
+ _createIsResource(typeRef) {
2843
+ if (typeRef.kind !== "resource") {
2844
+ return false;
2845
+ }
2846
+ return Object.fromEntries(
2847
+ this.tsIndex.collectResources().map((e) => e.identifier).map((resourceRef) => [
2848
+ `is${resourceRef.name.charAt(0).toUpperCase() + resourceRef.name.slice(1)}`,
2849
+ resourceRef.url === typeRef.url
2850
+ ])
2851
+ );
1628
2852
  }
1629
- /**
1630
- * Progress message with clock
1631
- */
1632
- progress(message) {
1633
- this.tryWriteToConsole(1 /* INFO */, this.formatMessage("\u23F3", message, pc.blue));
2853
+ _createIsComplexType(typeRef) {
2854
+ if (typeRef.kind !== "complex-type" && typeRef.kind !== "nested") {
2855
+ return false;
2856
+ }
2857
+ return Object.fromEntries(
2858
+ this.tsIndex.collectComplexTypes().map((e) => e.identifier).map((complexTypeRef) => [
2859
+ `is${complexTypeRef.name.charAt(0).toUpperCase() + complexTypeRef.name.slice(1)}`,
2860
+ complexTypeRef.url === typeRef.url
2861
+ ])
2862
+ );
1634
2863
  }
1635
- /**
1636
- * Plain message (no icon, just colored text)
1637
- */
1638
- plain(message, color = (s) => s) {
1639
- const timestamp = this.options.timestamp ? `${pc.gray((/* @__PURE__ */ new Date()).toLocaleTimeString())} ` : "";
1640
- const prefix = this.options.prefix ? `${pc.cyan(`[${this.options.prefix}]`)} ` : "";
1641
- this.tryWriteToConsole(1 /* INFO */, `${timestamp}${prefix}${color(message)}`);
2864
+ _createIsPrimitiveType(typeRef) {
2865
+ if (typeRef.kind !== "primitive-type") {
2866
+ return false;
2867
+ }
2868
+ return Object.fromEntries(
2869
+ PRIMITIVE_TYPES.map((type) => [`is${type.charAt(0).toUpperCase()}${type.slice(1)}`, typeRef.name === type])
2870
+ );
1642
2871
  }
1643
- /**
1644
- * Dimmed/gray text for less important info
1645
- */
1646
- dim(message) {
1647
- this.plain(message, pc.gray);
2872
+ _collectNestedComplex(schema, cache) {
2873
+ const nested = [];
2874
+ if ("nested" in schema && schema.nested) {
2875
+ schema.nested.map((nested2) => this._createForNestedType(nested2, cache, schema)).forEach((n) => {
2876
+ nested.push(n);
2877
+ });
2878
+ }
2879
+ return nested;
2880
+ }
2881
+ _collectNestedEnums(fields) {
2882
+ const nestedEnumValues = {};
2883
+ fields.forEach(([fieldName, fieldSchema]) => {
2884
+ if ("enum" in fieldSchema && fieldSchema.enum) {
2885
+ const name = ("binding" in fieldSchema && fieldSchema.binding?.name) ?? fieldName;
2886
+ if (typeof name === "string") {
2887
+ nestedEnumValues[name] = nestedEnumValues[name] ?? /* @__PURE__ */ new Set();
2888
+ fieldSchema.enum?.forEach(nestedEnumValues[name].add.bind(nestedEnumValues[name]));
2889
+ }
2890
+ }
2891
+ });
2892
+ return Object.entries(nestedEnumValues).map(([name, values]) => ({
2893
+ name,
2894
+ saveName: this.nameGenerator.generateEnumType(name),
2895
+ values: Array.from(values).map((value) => ({
2896
+ name: value,
2897
+ saveName: this.nameGenerator.generateEnumValue(value)
2898
+ }))
2899
+ }));
1648
2900
  }
1649
- /**
1650
- * Create a child logger with a prefix
1651
- */
1652
- child(prefix) {
1653
- return new _CodegenLogger({
1654
- ...this.options,
1655
- prefix: this.options.prefix ? `${this.options.prefix}:${prefix}` : prefix
2901
+ _createForRoot() {
2902
+ return this.arrayMixinProvider.apply({
2903
+ complexTypes: this.tsIndex.collectComplexTypes().map((e) => e.identifier).map((typeRef) => ({
2904
+ name: typeRef.name,
2905
+ saveName: this.nameGenerator.generateType(typeRef)
2906
+ })),
2907
+ resources: this.tsIndex.collectResources().map((e) => e.identifier).map((typeRef) => ({
2908
+ name: typeRef.name,
2909
+ saveName: this.nameGenerator.generateType(typeRef)
2910
+ }))
1656
2911
  });
1657
2912
  }
1658
- /**
1659
- * Update options
1660
- */
1661
- configure(options) {
1662
- this.options = { ...this.options, ...options };
2913
+ };
2914
+ function loadMustacheGeneratorConfig(templatePath, logger) {
2915
+ const filePath = Path.resolve(templatePath, "config.json");
2916
+ try {
2917
+ const raw = fs.readFileSync(filePath, "utf-8");
2918
+ const parsed = JSON.parse(raw);
2919
+ if (parsed && typeof parsed === "object") {
2920
+ return parsed;
2921
+ }
2922
+ } catch (_e) {
1663
2923
  }
2924
+ return {};
2925
+ }
2926
+ var createGenerator = (templatePath, apiOpts) => {
2927
+ const defaultFileOpts = {
2928
+ debug: "OFF",
2929
+ hooks: {},
2930
+ meta: {},
2931
+ keywords: [],
2932
+ unsaveCharacterPattern: /[^a-zA-Z0-9]/g,
2933
+ nameTransformations: {
2934
+ common: [],
2935
+ enumValue: [],
2936
+ type: [],
2937
+ field: []
2938
+ },
2939
+ renderings: {
2940
+ utility: [],
2941
+ resource: [],
2942
+ complexType: []
2943
+ },
2944
+ shouldRunHooks: true,
2945
+ primitiveTypeMap: {}
2946
+ };
2947
+ const actualFileOpts = loadMustacheGeneratorConfig(templatePath);
2948
+ const mustacheOptions = {
2949
+ ...defaultFileOpts,
2950
+ ...apiOpts,
2951
+ ...actualFileOpts,
2952
+ sources: {
2953
+ staticSource: Path.resolve(templatePath, "static"),
2954
+ templateSource: Path.resolve(templatePath, "templates")
2955
+ }
2956
+ };
2957
+ return new MustacheGenerator(mustacheOptions);
1664
2958
  };
1665
- new CodegenLogger();
1666
- function createLogger(options = {}) {
1667
- return new CodegenLogger(options);
2959
+ function runCommand(cmd, args = [], options = {}) {
2960
+ return new Promise((resolve6, reject) => {
2961
+ const child = spawn(cmd, args, {
2962
+ stdio: "inherit",
2963
+ ...options
2964
+ });
2965
+ child.on("error", reject);
2966
+ child.on("close", (code) => {
2967
+ if (code === 0) resolve6(code);
2968
+ else reject(new Error(`Prozess beendet mit Fehlercode ${code}`));
2969
+ });
2970
+ });
1668
2971
  }
1669
-
1670
- // src/typeschema/index.ts
1671
- var codeableReferenceInR4 = "Use CodeableReference which is not provided by FHIR R4.";
1672
- var availabilityInR4 = "Use Availability which is not provided by FHIR R4.";
1673
- var skipMe = {
1674
- "hl7.fhir.uv.extensions.r4#1.0.0": {
1675
- "http://hl7.org/fhir/StructureDefinition/extended-contact-availability": availabilityInR4,
1676
- "http://hl7.org/fhir/StructureDefinition/immunization-procedure": codeableReferenceInR4,
1677
- "http://hl7.org/fhir/StructureDefinition/specimen-additive": codeableReferenceInR4,
1678
- "http://hl7.org/fhir/StructureDefinition/workflow-barrier": codeableReferenceInR4,
1679
- "http://hl7.org/fhir/StructureDefinition/workflow-protectiveFactor": codeableReferenceInR4,
1680
- "http://hl7.org/fhir/StructureDefinition/workflow-reason": codeableReferenceInR4
1681
- },
1682
- "hl7.fhir.r5.core#5.0.0": {
1683
- "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."
2972
+ var MustacheGenerator = class extends FileSystemWriter {
2973
+ templateFileCache;
2974
+ nameGenerator;
2975
+ lambdaMixinProvider;
2976
+ debugMixinProvider;
2977
+ constructor(opts) {
2978
+ super(opts);
2979
+ this.nameGenerator = new NameGenerator(
2980
+ new Set(opts.keywords),
2981
+ opts.primitiveTypeMap,
2982
+ opts.nameTransformations,
2983
+ opts.unsaveCharacterPattern
2984
+ );
2985
+ this.templateFileCache = new TemplateFileCache(opts.sources.templateSource);
2986
+ this.lambdaMixinProvider = new LambdaMixinProvider(this.nameGenerator);
2987
+ this.debugMixinProvider = opts.debug !== "OFF" ? new DebugMixinProvider(opts.debug) : void 0;
1684
2988
  }
1685
- };
1686
- var generateTypeSchemas = async (register, logger) => {
1687
- const fhirSchemas = [];
1688
- for (const fhirSchema of register.allFs()) {
1689
- const pkgId = packageMetaToFhir(fhirSchema.package_meta);
1690
- if (skipMe[pkgId]?.[fhirSchema.url]) {
1691
- logger?.dry_warn(`Skip ${fhirSchema.url} from ${pkgId}. Reason: ${skipMe[pkgId]?.[fhirSchema.url]}`);
1692
- continue;
2989
+ async generate(tsIndex) {
2990
+ const modelFactory = new ViewModelFactory(tsIndex, this.nameGenerator, () => true);
2991
+ const cache = {
2992
+ resourcesByUri: {},
2993
+ complexTypesByUri: {}
2994
+ };
2995
+ tsIndex.collectComplexTypes().map((i) => i.identifier).sort((a, b) => a.url.localeCompare(b.url)).map((typeRef) => modelFactory.createComplexType(typeRef, cache)).forEach(this._renderComplexType.bind(this));
2996
+ tsIndex.collectResources().map((i) => i.identifier).sort((a, b) => a.url.localeCompare(b.url)).map((typeRef) => modelFactory.createResource(typeRef, cache)).forEach(this._renderResource.bind(this));
2997
+ this._renderUtility(modelFactory.createUtility());
2998
+ this.copyStaticFiles();
2999
+ if (this.opts.shouldRunHooks) {
3000
+ await this._runHooks(this.opts.hooks.afterGenerate);
1693
3001
  }
1694
- fhirSchemas.push(...await transformFhirSchema(register, fhirSchema, logger));
3002
+ return;
1695
3003
  }
1696
- for (const vsSchema of register.allVs()) {
1697
- fhirSchemas.push(await transformValueSet(register, vsSchema));
3004
+ copyStaticFiles() {
3005
+ const staticDir = Path.resolve(this.opts.sources.staticSource);
3006
+ if (!staticDir) {
3007
+ throw new Error("staticDir must be set in subclass.");
3008
+ }
3009
+ fs.cpSync(staticDir, this.opts.outputDir, { recursive: true });
3010
+ }
3011
+ async _runHooks(hooks) {
3012
+ for (const hook of hooks ?? []) {
3013
+ console.info(`Running hook (${this.opts.outputDir}): ${hook.cmd} ${hook.args?.join(" ")}`);
3014
+ await runCommand(hook.cmd, hook.args ?? [], {
3015
+ cwd: this.opts.outputDir
3016
+ });
3017
+ console.info(`Completed hook: ${hook.cmd} ${hook.args?.join(" ")}`);
3018
+ }
3019
+ }
3020
+ _checkRenderingFilter(model, rendering) {
3021
+ if (!rendering.filter?.whitelist?.length && !rendering.filter?.blacklist?.length) {
3022
+ return true;
3023
+ }
3024
+ if ((rendering.filter?.blacklist ?? []).find((v) => model.name.match(v))) {
3025
+ return false;
3026
+ }
3027
+ if ((rendering.filter?.whitelist ?? []).find((v) => model.name.match(v))) {
3028
+ return true;
3029
+ }
3030
+ return !rendering.filter.whitelist?.length;
3031
+ }
3032
+ _renderUtility(model) {
3033
+ this.opts.renderings.utility.forEach((rendering) => {
3034
+ this.cd(rendering.path, () => {
3035
+ this.cat(rendering.fileNameFormat, () => {
3036
+ this.write(this._render(model, rendering));
3037
+ });
3038
+ });
3039
+ });
3040
+ }
3041
+ _renderResource(model) {
3042
+ this.opts.renderings.resource.filter((rendering) => this._checkRenderingFilter(model, rendering)).forEach((rendering) => {
3043
+ this.cd(rendering.path, () => {
3044
+ this.cat(this._calculateFilename(model, rendering), () => {
3045
+ this.write(this._render(model, rendering));
3046
+ });
3047
+ });
3048
+ });
3049
+ }
3050
+ _renderComplexType(model) {
3051
+ this.opts.renderings.complexType.filter((rendering) => this._checkRenderingFilter(model, rendering)).forEach((rendering) => {
3052
+ this.cd(rendering.path, () => {
3053
+ this.cat(this._calculateFilename(model, rendering), () => {
3054
+ this.write(this._render(model, rendering));
3055
+ });
3056
+ });
3057
+ });
3058
+ }
3059
+ _calculateFilename(model, rendering) {
3060
+ return util.format(rendering.fileNameFormat, model.saveName);
3061
+ }
3062
+ _render(model, rendering) {
3063
+ let view = this.lambdaMixinProvider.apply({
3064
+ meta: {
3065
+ timestamp: this.opts.meta.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
3066
+ generator: this.opts.meta.generator ?? "@atomic-ehr/codegen mustache generator"
3067
+ },
3068
+ model,
3069
+ properties: rendering.properties ?? {}
3070
+ });
3071
+ if (this.debugMixinProvider) {
3072
+ view = this.debugMixinProvider.apply(view);
3073
+ }
3074
+ return Mustache.render(
3075
+ this.templateFileCache.read(rendering),
3076
+ view,
3077
+ (partialName) => this.templateFileCache.readTemplate(partialName)
3078
+ );
1698
3079
  }
1699
- return fhirSchemas;
1700
3080
  };
1701
3081
 
1702
3082
  // src/api/writer-generator/typescript.ts
@@ -1741,7 +3121,7 @@ var tsModuleFileName = (id) => {
1741
3121
  var tsModulePath = (id) => {
1742
3122
  return `${tsFhirPackageDir(id.package)}/${tsModuleName(id)}`;
1743
3123
  };
1744
- var canonicalToName2 = (canonical, dropFragment = true) => {
3124
+ var canonicalToName3 = (canonical, dropFragment = true) => {
1745
3125
  if (!canonical) return void 0;
1746
3126
  const localName = extractNameFromCanonical(canonical, dropFragment);
1747
3127
  if (!localName) return void 0;
@@ -1750,7 +3130,7 @@ var canonicalToName2 = (canonical, dropFragment = true) => {
1750
3130
  var tsResourceName = (id) => {
1751
3131
  if (id.kind === "nested") {
1752
3132
  const url = id.url;
1753
- const path = canonicalToName2(url, false);
3133
+ const path = canonicalToName3(url, false);
1754
3134
  if (!path) return "";
1755
3135
  const [resourceName, fragment] = path.split("#");
1756
3136
  const name = uppercaseFirstLetterOfEach((fragment ?? "").split(".")).join("");
@@ -1816,7 +3196,7 @@ var TypeScript = class extends Writer {
1816
3196
  });
1817
3197
  } else if (isNestedIdentifier(dep)) {
1818
3198
  const ndep = { ...dep };
1819
- ndep.name = canonicalToName2(dep.url);
3199
+ ndep.name = canonicalToName3(dep.url);
1820
3200
  imports.push({
1821
3201
  tsPackage: `../${tsModulePath(ndep)}`,
1822
3202
  name: tsResourceName(dep),
@@ -1869,7 +3249,7 @@ var TypeScript = class extends Writer {
1869
3249
  name = tsResourceName(schema.identifier);
1870
3250
  }
1871
3251
  let extendsClause;
1872
- if (schema.base) extendsClause = `extends ${canonicalToName2(schema.base.url)}`;
3252
+ if (schema.base) extendsClause = `extends ${canonicalToName3(schema.base.url)}`;
1873
3253
  this.debugComment(schema.identifier);
1874
3254
  if (!schema.fields && !extendsClause && !isResourceTypeSchema(schema)) {
1875
3255
  this.lineSM(`export type ${name} = object`);
@@ -2136,6 +3516,25 @@ var TypeScript = class extends Writer {
2136
3516
  };
2137
3517
 
2138
3518
  // src/api/builder.ts
3519
+ var prettyReport = (report) => {
3520
+ const { success, filesGenerated, errors, warnings, duration } = report;
3521
+ const errorsStr = errors.length > 0 ? `Errors: ${errors.join(", ")}` : void 0;
3522
+ const warningsStr = warnings.length > 0 ? `Warnings: ${warnings.join(", ")}` : void 0;
3523
+ let allLoc = 0;
3524
+ const files = Object.entries(filesGenerated).map(([path, content]) => {
3525
+ const loc = content.split("\n").length;
3526
+ allLoc += loc;
3527
+ return ` - ${path} (${loc} loc)`;
3528
+ }).join("\n");
3529
+ return [
3530
+ `Generated files (${Math.round(allLoc / 1e3)} kloc):`,
3531
+ files,
3532
+ errorsStr,
3533
+ warningsStr,
3534
+ `Duration: ${Math.round(duration)}ms`,
3535
+ `Status: ${success ? "\u{1F7E9} Success" : "\u{1F7E5} Failure"}`
3536
+ ].filter((e) => e).join("\n");
3537
+ };
2139
3538
  var normalizeFileName = (str) => {
2140
3539
  const res = str.replace(/[^a-zA-Z0-9\-_.@#()]/g, "");
2141
3540
  if (res.length === 0) return "unknown";
@@ -2176,7 +3575,7 @@ var writeTypeSchemasToSeparateFiles = async (typeSchemas, outputDir, logger) =>
2176
3575
  const pkgPath = normalizeFileName(packageMetaToFhir(pkg));
2177
3576
  const name = normalizeFileName(`${ts.identifier.name}(${extractNameFromCanonical(ts.identifier.url)})`);
2178
3577
  const json = JSON.stringify(ts, null, 2);
2179
- const baseName = Path4.join(outputDir, pkgPath, name);
3578
+ const baseName = Path.join(outputDir, pkgPath, name);
2180
3579
  if (!files[baseName]) files[baseName] = [];
2181
3580
  if (!files[baseName]?.some((e) => e === json)) {
2182
3581
  files[baseName].push(json);
@@ -2191,7 +3590,7 @@ var writeTypeSchemasToSeparateFiles = async (typeSchemas, outputDir, logger) =>
2191
3590
  } else {
2192
3591
  fullName = `${baseName}-${index}.typeschema.json`;
2193
3592
  }
2194
- await afs2.mkdir(Path4.dirname(fullName), { recursive: true });
3593
+ await afs2.mkdir(Path.dirname(fullName), { recursive: true });
2195
3594
  await afs2.writeFile(fullName, json);
2196
3595
  })
2197
3596
  );
@@ -2199,7 +3598,7 @@ var writeTypeSchemasToSeparateFiles = async (typeSchemas, outputDir, logger) =>
2199
3598
  };
2200
3599
  var writeTypeSchemasToSingleFile = async (typeSchemas, outputFile, logger) => {
2201
3600
  logger.info(`Writing TypeSchema files to: ${outputFile}`);
2202
- await afs2.mkdir(Path4.dirname(outputFile), { recursive: true });
3601
+ await afs2.mkdir(Path.dirname(outputFile), { recursive: true });
2203
3602
  logger.info(`Writing TypeSchemas to one file ${outputFile}...`);
2204
3603
  for (const ts of typeSchemas) {
2205
3604
  const json = JSON.stringify(ts, null, 2);
@@ -2210,7 +3609,7 @@ var writeTypeSchemasToSingleFile = async (typeSchemas, outputFile, logger) => {
2210
3609
  var tryWriteTypeSchema = async (typeSchemas, opts, logger) => {
2211
3610
  if (!opts.typeSchemaOutputDir) return;
2212
3611
  try {
2213
- if (Path4.extname(opts.typeSchemaOutputDir) === ".ndjson") {
3612
+ if (Path.extname(opts.typeSchemaOutputDir) === ".ndjson") {
2214
3613
  await writeTypeSchemasToSingleFile(typeSchemas, opts.typeSchemaOutputDir, logger);
2215
3614
  } else {
2216
3615
  await writeTypeSchemasToSeparateFiles(typeSchemas, opts.typeSchemaOutputDir, logger);
@@ -2227,12 +3626,13 @@ var APIBuilder = class {
2227
3626
  generators = /* @__PURE__ */ new Map();
2228
3627
  logger;
2229
3628
  packages = [];
3629
+ localStructurePackages = [];
3630
+ localTgzArchives = [];
2230
3631
  progressCallback;
2231
3632
  typeSchemaConfig;
2232
3633
  constructor(options = {}) {
2233
3634
  this.options = {
2234
3635
  outputDir: options.outputDir || "./generated",
2235
- verbose: options.verbose ?? false,
2236
3636
  overwrite: options.overwrite ?? true,
2237
3637
  cache: options.cache ?? true,
2238
3638
  cleanOutput: options.cleanOutput ?? true,
@@ -2241,12 +3641,13 @@ var APIBuilder = class {
2241
3641
  throwException: options.throwException || false,
2242
3642
  typeSchemaOutputDir: options.typeSchemaOutputDir,
2243
3643
  exportTypeTree: options.exportTypeTree,
2244
- treeShake: options.treeShake
3644
+ treeShake: options.treeShake,
3645
+ registry: options.registry
2245
3646
  };
2246
3647
  this.typeSchemaConfig = options.typeSchemaConfig;
2247
3648
  this.logger = options.logger || createLogger({
2248
- verbose: this.options.verbose,
2249
- prefix: "API"
3649
+ prefix: "API",
3650
+ level: options.logLevel
2250
3651
  });
2251
3652
  }
2252
3653
  fromPackage(packageName, version) {
@@ -2258,6 +3659,22 @@ var APIBuilder = class {
2258
3659
  this.packages.push(packageRef);
2259
3660
  return this;
2260
3661
  }
3662
+ /**
3663
+ * Set a custom FHIR package registry URL
3664
+ * @param url The registry URL (default: https://fs.get-ig.org/pkgs/)
3665
+ */
3666
+ registry(url) {
3667
+ this.options.registry = url;
3668
+ return this;
3669
+ }
3670
+ localStructureDefinitions(config) {
3671
+ this.localStructurePackages.push(config);
3672
+ return this;
3673
+ }
3674
+ localTgzPackage(archivePath) {
3675
+ this.localTgzArchives.push(Path.resolve(archivePath));
3676
+ return this;
3677
+ }
2261
3678
  fromSchemas(schemas) {
2262
3679
  this.logger.debug(`Adding ${schemas.length} TypeSchemas to generation`);
2263
3680
  this.schemas = [...this.schemas, ...schemas];
@@ -2266,7 +3683,7 @@ var APIBuilder = class {
2266
3683
  typescript(userOpts) {
2267
3684
  const defaultWriterOpts = {
2268
3685
  logger: this.logger,
2269
- outputDir: Path4.join(this.options.outputDir, "/types"),
3686
+ outputDir: Path.join(this.options.outputDir, "/types"),
2270
3687
  tabSize: 4,
2271
3688
  withDebugComment: false,
2272
3689
  commentLinePrefix: "//",
@@ -2286,19 +3703,67 @@ var APIBuilder = class {
2286
3703
  this.logger.debug(`Configured TypeScript generator (${JSON.stringify(opts, void 0, 2)})`);
2287
3704
  return this;
2288
3705
  }
2289
- csharp(namespace, staticSourceDir) {
2290
- const generator = new CSharp({
2291
- outputDir: Path4.join(this.options.outputDir, "/types"),
2292
- staticSourceDir: staticSourceDir ?? void 0,
2293
- targetNamespace: namespace,
2294
- logger: new CodegenLogger({
2295
- prefix: "C#",
2296
- timestamp: true,
2297
- verbose: true,
2298
- suppressLoggingLevel: []
2299
- })
2300
- });
2301
- this.generators.set("C#", generator);
3706
+ python(userOptions) {
3707
+ const defaultWriterOpts = {
3708
+ logger: this.logger,
3709
+ outputDir: this.options.outputDir,
3710
+ tabSize: 4,
3711
+ withDebugComment: false,
3712
+ commentLinePrefix: "#"
3713
+ };
3714
+ const defaultPyOpts = {
3715
+ ...defaultWriterOpts,
3716
+ rootPackageName: "fhir_types",
3717
+ fieldFormat: "snake_case"
3718
+ };
3719
+ const opts = {
3720
+ ...defaultPyOpts,
3721
+ ...Object.fromEntries(Object.entries(userOptions).filter(([_, v]) => v !== void 0))
3722
+ };
3723
+ const generator = new Python(opts);
3724
+ this.generators.set("python", generator);
3725
+ this.logger.debug(`Configured python generator`);
3726
+ return this;
3727
+ }
3728
+ mustache(templatePath, userOpts) {
3729
+ const defaultWriterOpts = {
3730
+ logger: this.logger,
3731
+ outputDir: this.options.outputDir
3732
+ };
3733
+ const defaultMustacheOpts = {
3734
+ meta: {
3735
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3736
+ generator: "atomic-codegen"
3737
+ }
3738
+ };
3739
+ const opts = {
3740
+ ...defaultWriterOpts,
3741
+ ...defaultMustacheOpts,
3742
+ ...userOpts
3743
+ };
3744
+ const generator = createGenerator(templatePath, opts);
3745
+ this.generators.set(`mustache[${templatePath}]`, generator);
3746
+ this.logger.debug(`Configured TypeScript generator (${JSON.stringify(opts, void 0, 2)})`);
3747
+ return this;
3748
+ }
3749
+ csharp(userOptions) {
3750
+ const defaultWriterOpts = {
3751
+ logger: this.logger,
3752
+ outputDir: Path.join(this.options.outputDir, "/types"),
3753
+ tabSize: 4,
3754
+ withDebugComment: false,
3755
+ commentLinePrefix: "//"
3756
+ };
3757
+ const defaultCSharpOpts = {
3758
+ ...defaultWriterOpts,
3759
+ rootNamespace: "Fhir.Types"
3760
+ };
3761
+ const opts = {
3762
+ ...defaultCSharpOpts,
3763
+ ...Object.fromEntries(Object.entries(userOptions).filter(([_, v]) => v !== void 0))
3764
+ };
3765
+ const generator = new CSharp(opts);
3766
+ this.generators.set("csharp", generator);
2302
3767
  this.logger.debug(`Configured C# generator`);
2303
3768
  return this;
2304
3769
  }
@@ -2320,9 +3785,8 @@ var APIBuilder = class {
2320
3785
  }
2321
3786
  return this;
2322
3787
  }
2323
- verbose(enabled = true) {
2324
- this.options.verbose = enabled;
2325
- this.logger?.configure({ verbose: enabled });
3788
+ setLogLevel(level) {
3789
+ this.logger?.setLevel(typeof level === "string" ? parseLogLevel(level) : level);
2326
3790
  return this;
2327
3791
  }
2328
3792
  throwException(enabled = true) {
@@ -2350,7 +3814,7 @@ var APIBuilder = class {
2350
3814
  const result = {
2351
3815
  success: false,
2352
3816
  outputDir: this.options.outputDir,
2353
- filesGenerated: [],
3817
+ filesGenerated: {},
2354
3818
  errors: [],
2355
3819
  warnings: [],
2356
3820
  duration: 0
@@ -2359,10 +3823,28 @@ var APIBuilder = class {
2359
3823
  try {
2360
3824
  if (this.options.cleanOutput) cleanup(this.options, this.logger);
2361
3825
  this.logger.info("Initialize Canonical Manager");
2362
- const manager = CanonicalManager({
3826
+ const manager = this.options.manager || CanonicalManager({
2363
3827
  packages: this.packages,
2364
- workingDir: ".codegen-cache/canonical-manager-cache"
3828
+ workingDir: ".codegen-cache/canonical-manager-cache",
3829
+ registry: this.options.registry || void 0
2365
3830
  });
3831
+ if (this.localStructurePackages.length > 0) {
3832
+ for (const config of this.localStructurePackages) {
3833
+ this.logger.info(
3834
+ `Registering local StructureDefinitions for ${config.package.name}@${config.package.version}`
3835
+ );
3836
+ await manager.addLocalPackage({
3837
+ name: config.package.name,
3838
+ version: config.package.version,
3839
+ path: config.path,
3840
+ dependencies: config.dependencies?.map((dep) => packageMetaToNpm(dep))
3841
+ });
3842
+ }
3843
+ }
3844
+ for (const archivePath of this.localTgzArchives) {
3845
+ this.logger.info(`Registering local tgz package: ${archivePath}`);
3846
+ await manager.addTgzPackage({ archivePath });
3847
+ }
2366
3848
  const ref2meta = await manager.init();
2367
3849
  const packageMetas = Object.values(ref2meta);
2368
3850
  const register = await registerFromManager(manager, {
@@ -2401,6 +3883,9 @@ var APIBuilder = class {
2401
3883
  this.schemas = [];
2402
3884
  this.generators.clear();
2403
3885
  this.progressCallback = void 0;
3886
+ this.packages = [];
3887
+ this.localStructurePackages = [];
3888
+ this.localTgzArchives = [];
2404
3889
  return this;
2405
3890
  }
2406
3891
  /**
@@ -2421,7 +3906,9 @@ var APIBuilder = class {
2421
3906
  try {
2422
3907
  await generator.generate(tsIndex);
2423
3908
  const fileBuffer = generator.writtenFiles();
2424
- result.filesGenerated.push(...fileBuffer.map((e) => e.absPath));
3909
+ fileBuffer.forEach((buf) => {
3910
+ result.filesGenerated[buf.relPath] = buf.content;
3911
+ });
2425
3912
  this.logger.info(`Generating ${type} finished successfully`);
2426
3913
  } catch (error) {
2427
3914
  result.errors.push(
@@ -2439,6 +3926,7 @@ var DEFAULT_CONFIG = {
2439
3926
  validate: true,
2440
3927
  cache: true,
2441
3928
  cleanOutput: true,
3929
+ registry: "",
2442
3930
  typescript: {
2443
3931
  moduleFormat: "esm",
2444
3932
  generateIndex: true,
@@ -2945,6 +4433,6 @@ function defineConfig(config) {
2945
4433
  return config;
2946
4434
  }
2947
4435
 
2948
- export { APIBuilder, CONFIG_FILE_NAMES, ConfigLoader, ConfigValidator, DEFAULT_CONFIG, configLoader, defineConfig, isConfig, loadConfig };
4436
+ export { APIBuilder, CONFIG_FILE_NAMES, ConfigLoader, ConfigValidator, DEFAULT_CONFIG, LogLevel, configLoader, defineConfig, isConfig, loadConfig, prettyReport };
2949
4437
  //# sourceMappingURL=index.js.map
2950
4438
  //# sourceMappingURL=index.js.map