@atomic-ehr/codegen 0.0.4 → 0.0.5-canary.20251229160950.1f6114f

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,802 @@ 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, {
890
+ resolutionTree,
891
+ logger,
892
+ treeShakeReport
893
+ }) => {
894
+ const index = {};
895
+ const append = (schema) => {
896
+ const url = schema.identifier.url;
897
+ const pkg = schema.identifier.package;
898
+ if (!index[url]) index[url] = {};
899
+ if (index[url][schema.identifier.package] && pkg !== "shared") {
900
+ const r1 = JSON.stringify(schema.identifier, void 0, 2);
901
+ const r2 = JSON.stringify(index[url][pkg]?.identifier, void 0, 2);
902
+ if (r1 !== r2) throw new Error(`Duplicate schema: ${r1} and ${r2}`);
903
+ return;
904
+ }
905
+ index[url][pkg] = schema;
906
+ };
907
+ for (const schema of schemas) {
908
+ append(schema);
909
+ }
910
+ const relations = resourceRelatives(schemas);
911
+ const resolve6 = (id) => index[id.url]?.[id.package];
912
+ const resolveByUrl = (pkgName, url) => {
913
+ if (resolutionTree) {
914
+ const resolution = resolutionTree[pkgName]?.[url]?.[0];
915
+ if (resolution) {
916
+ return index[url]?.[resolution.pkg.name];
917
+ }
918
+ }
919
+ return index[url]?.[pkgName];
920
+ };
921
+ const resourceChildren = (id) => {
922
+ return relations.filter((relative) => relative.parent.name === id.name).map((relative) => relative.child);
923
+ };
924
+ const tryHierarchy = (schema) => {
925
+ const res = [];
926
+ let cur = schema;
927
+ while (cur) {
928
+ res.push(cur);
929
+ const base = cur.base;
930
+ if (base === void 0) break;
931
+ const resolved = resolve6(base);
932
+ if (!resolved) {
933
+ logger?.warn(
934
+ `Failed to resolve base type: ${res.map((e) => `${e.identifier.url} (${e.identifier.kind})`).join(", ")}`
703
935
  );
704
- genealogy.push(fs4);
936
+ return void 0;
937
+ }
938
+ cur = resolved;
939
+ }
940
+ return res;
941
+ };
942
+ const hierarchy = (schema) => {
943
+ const genealogy = tryHierarchy(schema);
944
+ if (genealogy === void 0) {
945
+ throw new Error(`Failed to resolve base type: ${schema.identifier.url} (${schema.identifier.kind})`);
705
946
  }
706
947
  return genealogy;
707
948
  };
708
- const resolveFsSpecializations = (pkg, canonicalUrl) => {
709
- return resolveFsGenealogy(pkg, canonicalUrl).filter((fs4) => fs4.derivation === "specialization");
949
+ const findLastSpecialization = (schema) => {
950
+ const nonConstraintSchema = hierarchy(schema).find((s) => s.identifier.kind !== "profile");
951
+ if (!nonConstraintSchema) {
952
+ throw new Error(`No non-constraint schema found in hierarchy for: ${schema.identifier.name}`);
953
+ }
954
+ return nonConstraintSchema;
710
955
  };
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;
956
+ const findLastSpecializationByIdentifier = (id) => {
957
+ const schema = resolve6(id);
958
+ if (!schema) return id;
959
+ return findLastSpecialization(schema).identifier;
716
960
  };
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);
961
+ const flatProfile = (schema) => {
962
+ const hierarchySchemas = hierarchy(schema);
963
+ const constraintSchemas = hierarchySchemas.filter((s) => s.identifier.kind === "profile");
964
+ const nonConstraintSchema = hierarchySchemas.find((s) => s.identifier.kind !== "profile");
965
+ if (!nonConstraintSchema)
966
+ throw new Error(`No non-constraint schema found in hierarchy for ${schema.identifier.name}`);
967
+ const mergedFields = {};
968
+ for (const anySchema of constraintSchemas.slice().reverse()) {
969
+ const schema2 = anySchema;
970
+ if (!schema2.fields) continue;
971
+ for (const [fieldName, fieldConstraints] of Object.entries(schema2.fields)) {
972
+ if (mergedFields[fieldName]) {
973
+ mergedFields[fieldName] = {
974
+ ...mergedFields[fieldName],
975
+ ...fieldConstraints
976
+ };
977
+ } else {
978
+ mergedFields[fieldName] = { ...fieldConstraints };
724
979
  }
725
980
  }
726
981
  }
727
- return Array.from(keys);
982
+ const deps = {};
983
+ for (const e of constraintSchemas.flatMap((e2) => e2.dependencies ?? [])) {
984
+ deps[e.url] = e;
985
+ }
986
+ const dependencies = Object.values(deps);
987
+ return {
988
+ ...schema,
989
+ base: nonConstraintSchema.identifier,
990
+ fields: mergedFields,
991
+ dependencies
992
+ };
728
993
  };
729
- 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
- }
994
+ const isWithMetaField = (profile) => {
995
+ const genealogy = tryHierarchy(profile);
996
+ if (!genealogy) return false;
997
+ return genealogy.filter(isSpecializationTypeSchema).some((schema) => {
998
+ return schema.fields?.meta !== void 0;
999
+ });
1000
+ };
1001
+ const entityTree = () => {
1002
+ const tree = {};
1003
+ for (const [pkgId, shemas] of Object.entries(groupByPackages(schemas))) {
1004
+ tree[pkgId] = {
1005
+ "primitive-type": {},
1006
+ "complex-type": {},
1007
+ resource: {},
1008
+ "value-set": {},
1009
+ nested: {},
1010
+ binding: {},
1011
+ profile: {},
1012
+ logical: {}
1013
+ };
1014
+ for (const schema of shemas) {
1015
+ tree[pkgId][schema.identifier.kind][schema.identifier.url] = {};
765
1016
  }
766
- return res;
767
1017
  }
1018
+ return tree;
1019
+ };
1020
+ const exportTree = async (filename) => {
1021
+ const tree = entityTree();
1022
+ const raw = filename.endsWith(".yaml") ? YAML.stringify(tree) : JSON.stringify(tree, void 0, 2);
1023
+ await afs2.mkdir(Path.dirname(filename), { recursive: true });
1024
+ await afs2.writeFile(filename, raw);
1025
+ };
1026
+ return {
1027
+ _schemaIndex: index,
1028
+ _relations: relations,
1029
+ schemas,
1030
+ schemasByPackage: groupByPackages(schemas),
1031
+ collectComplexTypes: () => schemas.filter(isComplexTypeTypeSchema),
1032
+ collectResources: () => schemas.filter(isResourceTypeSchema),
1033
+ collectLogicalModels: () => schemas.filter(isLogicalTypeSchema),
1034
+ collectProfiles: () => schemas.filter(isProfileTypeSchema),
1035
+ resolve: resolve6,
1036
+ resolveByUrl,
1037
+ resourceChildren,
1038
+ tryHierarchy,
1039
+ hierarchy,
1040
+ findLastSpecialization,
1041
+ findLastSpecializationByIdentifier,
1042
+ flatProfile,
1043
+ isWithMetaField,
1044
+ entityTree,
1045
+ exportTree,
1046
+ treeShakeReport: () => treeShakeReport
768
1047
  };
769
1048
  };
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];
778
- }
779
- return elem;
780
- }).filter((elem) => elem !== void 0);
1049
+
1050
+ // src/api/writer-generator/python.ts
1051
+ var PRIMITIVE_TYPE_MAP2 = {
1052
+ boolean: "bool",
1053
+ instant: "str",
1054
+ time: "str",
1055
+ date: "str",
1056
+ dateTime: "str",
1057
+ decimal: "float",
1058
+ integer: "int",
1059
+ unsignedInt: "int",
1060
+ positiveInt: "PositiveInt",
1061
+ integer64: "int",
1062
+ base64Binary: "str",
1063
+ uri: "str",
1064
+ url: "str",
1065
+ canonical: "str",
1066
+ oid: "str",
1067
+ uuid: "str",
1068
+ string: "str",
1069
+ code: "str",
1070
+ markdown: "str",
1071
+ id: "str",
1072
+ xhtml: "str"
1073
+ };
1074
+ var AVAILABLE_STRING_FORMATS = {
1075
+ snake_case: snakeCase,
1076
+ PascalCase: pascalCase,
1077
+ camelCase
1078
+ };
1079
+ var PYTHON_KEYWORDS = /* @__PURE__ */ new Set([
1080
+ "False",
1081
+ "None",
1082
+ "True",
1083
+ "and",
1084
+ "as",
1085
+ "assert",
1086
+ "async",
1087
+ "await",
1088
+ "break",
1089
+ "class",
1090
+ "continue",
1091
+ "def",
1092
+ "del",
1093
+ "elif",
1094
+ "else",
1095
+ "except",
1096
+ "finally",
1097
+ "for",
1098
+ "from",
1099
+ "global",
1100
+ "if",
1101
+ "import",
1102
+ "in",
1103
+ "is",
1104
+ "lambda",
1105
+ "nonlocal",
1106
+ "not",
1107
+ "or",
1108
+ "pass",
1109
+ "raise",
1110
+ "return",
1111
+ "try",
1112
+ "while",
1113
+ "with",
1114
+ "yield",
1115
+ "List"
1116
+ ]);
1117
+ var MAX_IMPORT_LINE_LENGTH = 100;
1118
+ var fixReservedWords = (name) => {
1119
+ return PYTHON_KEYWORDS.has(name) ? `${name}_` : name;
1120
+ };
1121
+ var injectSuperClasses = (name) => {
1122
+ return name === "Resource" || name === "Element" ? ["BaseModel"] : [];
1123
+ };
1124
+ var canonicalToName2 = (canonical, dropFragment = true) => {
1125
+ if (!canonical) return void 0;
1126
+ let localName = canonical.split("/").pop();
1127
+ if (!localName) return void 0;
1128
+ if (dropFragment && localName.includes("#")) {
1129
+ localName = localName.split("#")[0];
1130
+ }
1131
+ if (!localName) return void 0;
1132
+ if (/^\d/.test(localName)) {
1133
+ localName = `number_${localName}`;
1134
+ }
1135
+ return snakeCase(localName);
1136
+ };
1137
+ var deriveResourceName = (id) => {
1138
+ if (id.kind === "nested") {
1139
+ const url = id.url;
1140
+ const path = canonicalToName2(url, false);
1141
+ if (!path) return "";
1142
+ const [resourceName, fragment] = path.split("#");
1143
+ const name = uppercaseFirstLetterOfEach((fragment ?? "").split(".")).join("");
1144
+ return pascalCase([resourceName, name].join(""));
1145
+ }
1146
+ return pascalCase(id.name);
1147
+ };
1148
+ var resolvePyAssets = (fn) => {
1149
+ const __dirname = Path.dirname(fileURLToPath(import.meta.url));
1150
+ if (__filename.endsWith("dist/index.js")) {
1151
+ return Path.resolve(__dirname, "..", "assets", "api", "writer-generator", "python", fn);
1152
+ } else {
1153
+ return Path.resolve(__dirname, "../../..", "assets", "api", "writer-generator", "python", fn);
1154
+ }
1155
+ };
1156
+ var Python = class extends Writer {
1157
+ nameFormatFunction;
1158
+ tsIndex;
1159
+ constructor(options) {
1160
+ super({ ...options, resolveAssets: options.resolveAssets ?? resolvePyAssets });
1161
+ this.nameFormatFunction = this.getFieldFormatFunction(options.fieldFormat);
1162
+ }
1163
+ async generate(tsIndex) {
1164
+ this.tsIndex = tsIndex;
1165
+ const groups = {
1166
+ groupedComplexTypes: groupByPackages(tsIndex.collectComplexTypes()),
1167
+ groupedResources: groupByPackages(tsIndex.collectResources())
1168
+ };
1169
+ this.generateRootPackages(groups);
1170
+ this.generateSDKPackages(groups);
1171
+ }
1172
+ generateRootPackages(groups) {
1173
+ this.generateRootInitFile(groups);
1174
+ this.cp(resolvePyAssets("requirements.txt"), "requirements.txt");
1175
+ }
1176
+ generateSDKPackages(groups) {
1177
+ this.generateComplexTypesPackages(groups.groupedComplexTypes);
1178
+ this.generateResourcePackages(groups);
1179
+ }
1180
+ generateComplexTypesPackages(groupedComplexTypes) {
1181
+ for (const [packageName, packageComplexTypes] of Object.entries(groupedComplexTypes)) {
1182
+ this.cd(`/${snakeCase(packageName)}`, () => {
1183
+ this.generateBasePy(packageComplexTypes);
1184
+ });
1185
+ }
1186
+ }
1187
+ generateResourcePackages(groups) {
1188
+ for (const [packageName, packageResources] of Object.entries(groups.groupedResources)) {
1189
+ this.cd(`/${snakeCase(packageName)}`, () => {
1190
+ this.generateResourcePackageContent(
1191
+ packageName,
1192
+ packageResources,
1193
+ groups.groupedComplexTypes[packageName] || []
1194
+ );
1195
+ });
1196
+ }
1197
+ }
1198
+ generateResourcePackageContent(packageName, packageResources, packageComplexTypes) {
1199
+ const pyPackageName = this.pyFhirPackageByName(packageName);
1200
+ this.generateResourcePackageInit(pyPackageName, packageResources, packageComplexTypes);
1201
+ this.generateResourceFamilies(packageResources);
1202
+ for (const schema of packageResources) {
1203
+ this.generateResourceModule(schema);
1204
+ }
1205
+ }
1206
+ generateRootInitFile(groups) {
1207
+ this.cd("/", () => {
1208
+ this.cat("__init__.py", () => {
1209
+ this.generateDisclaimer();
1210
+ const pydanticModels = this.collectAndImportAllModels(groups);
1211
+ this.generateModelRebuilds(pydanticModels);
1212
+ });
1213
+ });
1214
+ }
1215
+ collectAndImportAllModels(groups) {
1216
+ const models = [];
1217
+ for (const packageName of Object.keys(groups.groupedResources)) {
1218
+ const fullPyPackageName = this.pyFhirPackageByName(packageName);
1219
+ models.push(...this.importComplexTypes(fullPyPackageName, groups.groupedComplexTypes[packageName]));
1220
+ models.push(...this.importResources(fullPyPackageName, false, groups.groupedResources[packageName]));
1221
+ }
1222
+ this.line();
1223
+ return models;
1224
+ }
1225
+ generateModelRebuilds(models) {
1226
+ for (const modelName of models.sort()) {
1227
+ this.line(`${modelName}.model_rebuild()`);
1228
+ }
1229
+ }
1230
+ generateBasePy(packageComplexTypes) {
1231
+ this.cat("base.py", () => {
1232
+ this.generateDisclaimer();
1233
+ this.generateDefaultImports();
1234
+ this.line();
1235
+ this.generateComplexTypes(packageComplexTypes);
1236
+ this.line();
1237
+ });
1238
+ }
1239
+ generateComplexTypes(complexTypes) {
1240
+ for (const schema of sortAsDeclarationSequence(complexTypes)) {
1241
+ this.generateNestedTypes(schema);
1242
+ this.line();
1243
+ this.generateType(schema);
1244
+ }
1245
+ }
1246
+ generateResourcePackageInit(fullPyPackageName, packageResources, packageComplexTypes) {
1247
+ this.cat("__init__.py", () => {
1248
+ this.generateDisclaimer();
1249
+ this.importComplexTypes(fullPyPackageName, packageComplexTypes);
1250
+ const allResourceNames = this.importResources(fullPyPackageName, true, packageResources);
1251
+ this.line();
1252
+ this.generateExportsDeclaration(packageComplexTypes, allResourceNames);
1253
+ });
1254
+ }
1255
+ importComplexTypes(fullPyPackageName, packageComplexTypes) {
1256
+ if (!packageComplexTypes || packageComplexTypes.length === 0) return [];
1257
+ const baseTypes = packageComplexTypes.map((t) => t.identifier.name).sort();
1258
+ this.pyImportFrom(`${fullPyPackageName}.base`, ...baseTypes);
1259
+ this.line();
1260
+ return baseTypes;
1261
+ }
1262
+ buildImportLine(remaining, maxImportLineLength) {
1263
+ let line = "";
1264
+ while (remaining.length > 0 && line.length < maxImportLineLength) {
1265
+ const entity = remaining.shift();
1266
+ if (!entity) throw new Error("Unexpected empty entity");
1267
+ if (line.length > 0) {
1268
+ line += ", ";
1269
+ }
1270
+ line += entity;
1271
+ }
1272
+ if (remaining.length > 0) {
1273
+ line += ", \\";
1274
+ }
1275
+ return line;
1276
+ }
1277
+ importResources(fullPyPackageName, importEmptyResources, packageResources) {
1278
+ if (!packageResources || packageResources.length === 0) return [];
1279
+ const allResourceNames = [];
1280
+ for (const resource of packageResources) {
1281
+ const names = this.importOneResource(resource, fullPyPackageName);
1282
+ if (!importEmptyResources && !resource.fields) continue;
1283
+ allResourceNames.push(...names);
1284
+ }
1285
+ return allResourceNames;
1286
+ }
1287
+ importOneResource(resource, fullPyPackageName) {
1288
+ const moduleName = `${fullPyPackageName}.${snakeCase(resource.identifier.name)}`;
1289
+ const importNames = this.collectResourceImportNames(resource);
1290
+ this.pyImportFrom(moduleName, ...importNames);
1291
+ const names = [...importNames];
1292
+ if (this.shouldImportResourceFamily(resource)) {
1293
+ const familyName = `${resource.identifier.name}Family`;
1294
+ this.pyImportFrom(`${fullPyPackageName}.resource_families`, familyName);
1295
+ }
1296
+ return names;
1297
+ }
1298
+ collectResourceImportNames(resource) {
1299
+ const names = [deriveResourceName(resource.identifier)];
1300
+ for (const nested of resource.nested ?? []) {
1301
+ const nestedName = deriveResourceName(nested.identifier);
1302
+ names.push(nestedName);
1303
+ }
1304
+ return names;
1305
+ }
1306
+ shouldImportResourceFamily(resource) {
1307
+ assert3(this.tsIndex !== void 0);
1308
+ return resource.identifier.kind === "resource" && this.tsIndex.resourceChildren(resource.identifier).length > 0;
1309
+ }
1310
+ generateExportsDeclaration(packageComplexTypes, allResourceNames) {
1311
+ this.squareBlock(["__all__", "="], () => {
1312
+ const allExports = [
1313
+ ...(packageComplexTypes || []).map((t) => t.identifier.name),
1314
+ ...allResourceNames
1315
+ ].sort();
1316
+ for (const schemaName of allExports) {
1317
+ this.line(`'${schemaName}',`);
1318
+ }
1319
+ });
1320
+ }
1321
+ generateResourceModule(schema) {
1322
+ this.cat(`${snakeCase(schema.identifier.name)}.py`, () => {
1323
+ this.generateDisclaimer();
1324
+ this.generateDefaultImports();
1325
+ this.line();
1326
+ this.generateDependenciesImports(schema);
1327
+ this.line();
1328
+ this.generateNestedTypes(schema);
1329
+ this.line();
1330
+ this.generateType(schema);
1331
+ });
1332
+ }
1333
+ generateType(schema) {
1334
+ const className = deriveResourceName(schema.identifier);
1335
+ const superClasses = this.getSuperClasses(schema);
1336
+ this.line(`class ${className}(${superClasses.join(", ")}):`);
1337
+ this.indentBlock(() => {
1338
+ this.generateClassBody(schema);
1339
+ });
1340
+ this.line();
1341
+ }
1342
+ getSuperClasses(schema) {
1343
+ return [...schema.base ? [schema.base.name] : [], ...injectSuperClasses(schema.identifier.name)];
1344
+ }
1345
+ generateClassBody(schema) {
1346
+ this.generateModelConfig();
1347
+ if (!schema.fields) {
1348
+ this.line("pass");
1349
+ return;
1350
+ }
1351
+ if (schema.identifier.kind === "resource") {
1352
+ this.generateResourceTypeField(schema);
1353
+ }
1354
+ this.generateFields(schema);
1355
+ if (schema.identifier.kind === "resource") {
1356
+ this.generateResourceMethods(schema);
1357
+ }
1358
+ }
1359
+ generateModelConfig() {
1360
+ const extraMode = this.opts.allowExtraFields ? "allow" : "forbid";
1361
+ this.line(`model_config = ConfigDict(validate_by_name=True, serialize_by_alias=True, extra="${extraMode}")`);
1362
+ }
1363
+ generateResourceTypeField(schema) {
1364
+ this.line(`${this.nameFormatFunction("resourceType")}: str = Field(`);
1365
+ this.indentBlock(() => {
1366
+ this.line(`default='${schema.identifier.name}',`);
1367
+ this.line(`alias='resourceType',`);
1368
+ this.line(`serialization_alias='resourceType',`);
1369
+ this.line("frozen=True,");
1370
+ this.line(`pattern='${schema.identifier.name}'`);
1371
+ });
1372
+ this.line(")");
1373
+ }
1374
+ generateFields(schema) {
1375
+ const sortedFields = Object.entries(schema.fields ?? []).sort(([a], [b]) => a.localeCompare(b));
1376
+ for (const [fieldName, field] of sortedFields) {
1377
+ if ("choices" in field && field.choices) continue;
1378
+ const fieldInfo = this.buildFieldInfo(fieldName, field);
1379
+ this.line(`${fieldInfo.name}: ${fieldInfo.type}${fieldInfo.defaultValue}`);
1380
+ }
1381
+ }
1382
+ buildFieldInfo(fieldName, field) {
1383
+ const pyFieldName = fixReservedWords(this.nameFormatFunction(fieldName));
1384
+ const fieldType = this.determineFieldType(field);
1385
+ const defaultValue = this.getFieldDefaultValue(field, fieldName);
1386
+ return {
1387
+ name: pyFieldName,
1388
+ type: fieldType,
1389
+ defaultValue
1390
+ };
1391
+ }
1392
+ determineFieldType(field) {
1393
+ let fieldType = field ? this.getBaseFieldType(field) : "";
1394
+ if ("enum" in field && field.enum) {
1395
+ const s = field.enum.map((e) => `"${e}"`).join(", ");
1396
+ fieldType = `Literal[${s}]`;
1397
+ }
1398
+ if (field.array) {
1399
+ fieldType = `PyList[${fieldType}]`;
1400
+ }
1401
+ if (!field.required) {
1402
+ fieldType = `${fieldType} | None`;
1403
+ }
1404
+ return fieldType;
1405
+ }
1406
+ getBaseFieldType(field) {
1407
+ if ("type" in field && field.type.kind === "resource") return `${field.type.name}Family`;
1408
+ if ("type" in field && field.type.kind === "nested") return deriveResourceName(field.type);
1409
+ if ("type" in field && field.type.kind === "primitive-type")
1410
+ return PRIMITIVE_TYPE_MAP2[field.type.name] ?? "str";
1411
+ return "type" in field ? field.type.name : "";
1412
+ }
1413
+ getFieldDefaultValue(field, fieldName) {
1414
+ const aliasSpec = `alias="${fieldName}", serialization_alias="${fieldName}"`;
1415
+ if (!field.required) {
1416
+ return ` = Field(None, ${aliasSpec})`;
1417
+ }
1418
+ return ` = Field(${aliasSpec})`;
1419
+ }
1420
+ generateResourceMethods(schema) {
1421
+ const className = schema.identifier.name.toString();
1422
+ this.line();
1423
+ this.line("def to_json(self, indent: int | None = None) -> str:");
1424
+ this.line(" return self.model_dump_json(exclude_unset=True, exclude_none=True, indent=indent)");
1425
+ this.line();
1426
+ this.line("@classmethod");
1427
+ this.line(`def from_json(cls, json: str) -> ${className}:`);
1428
+ this.line(" return cls.model_validate_json(json)");
1429
+ }
1430
+ generateNestedTypes(schema) {
1431
+ if (!schema.nested) return;
1432
+ this.line();
1433
+ for (const subtype of schema.nested) {
1434
+ this.generateType(subtype);
1435
+ }
1436
+ }
1437
+ generateDefaultImports() {
1438
+ this.pyImportFrom("__future__", "annotations");
1439
+ this.pyImportFrom("pydantic", "BaseModel", "ConfigDict", "Field", "PositiveInt");
1440
+ this.pyImportFrom("typing", "List as PyList", "Literal");
1441
+ }
1442
+ generateDependenciesImports(schema) {
1443
+ if (!schema.dependencies || schema.dependencies.length === 0) return;
1444
+ this.importComplexTypeDependencies(schema.dependencies);
1445
+ this.importResourceDependencies(schema.dependencies);
1446
+ }
1447
+ importComplexTypeDependencies(dependencies) {
1448
+ const complexTypeDeps = dependencies.filter((dep) => dep.kind === "complex-type");
1449
+ const depsByPackage = this.groupDependenciesByPackage(complexTypeDeps);
1450
+ for (const [pyPackage, names] of Object.entries(depsByPackage)) {
1451
+ this.pyImportFrom(pyPackage, ...names.sort());
1452
+ }
1453
+ }
1454
+ importResourceDependencies(dependencies) {
1455
+ const resourceDeps = dependencies.filter((dep) => dep.kind === "resource");
1456
+ for (const dep of resourceDeps) {
1457
+ this.pyImportType(dep);
1458
+ const familyName = `${pascalCase(dep.name)}Family`;
1459
+ const familyPackage = `${this.pyFhirPackage(dep)}.resource_families`;
1460
+ this.pyImportFrom(familyPackage, familyName);
1461
+ }
1462
+ }
1463
+ groupDependenciesByPackage(dependencies) {
1464
+ const grouped = {};
1465
+ for (const dep of dependencies) {
1466
+ const pyPackage = this.pyPackage(dep);
1467
+ if (!grouped[pyPackage]) {
1468
+ grouped[pyPackage] = [];
1469
+ }
1470
+ grouped[pyPackage].push(dep.name);
1471
+ }
1472
+ return grouped;
1473
+ }
1474
+ pyImportFrom(pyPackage, ...entities) {
1475
+ const oneLine = `from ${pyPackage} import ${entities.join(", ")}`;
1476
+ if (this.shouldUseSingleLineImport(oneLine, entities)) {
1477
+ this.line(oneLine);
1478
+ } else {
1479
+ this.writeMultiLineImport(pyPackage, entities);
1480
+ }
1481
+ }
1482
+ shouldUseSingleLineImport(oneLine, entities) {
1483
+ return oneLine.length <= MAX_IMPORT_LINE_LENGTH || entities.length === 1;
1484
+ }
1485
+ writeMultiLineImport(pyPackage, entities) {
1486
+ this.line(`from ${pyPackage} import (\\`);
1487
+ this.indentBlock(() => {
1488
+ const remaining = [...entities];
1489
+ while (remaining.length > 0) {
1490
+ const line = this.buildImportLine(remaining, MAX_IMPORT_LINE_LENGTH);
1491
+ this.line(line);
1492
+ }
1493
+ });
1494
+ this.line(")");
1495
+ }
1496
+ pyImportType(identifier) {
1497
+ this.pyImportFrom(this.pyPackage(identifier), pascalCase(identifier.name));
1498
+ }
1499
+ generateResourceFamilies(packageResources) {
1500
+ assert3(this.tsIndex !== void 0);
1501
+ const packages = (
1502
+ //this.helper.getPackages(packageResources, this.opts.rootPackageName);
1503
+ Object.keys(groupByPackages(packageResources)).map(
1504
+ (pkgName) => `${this.opts.rootPackageName}.${pkgName.replaceAll(".", "_")}`
1505
+ )
1506
+ );
1507
+ const families = {};
1508
+ for (const resource of this.tsIndex.collectResources()) {
1509
+ const children = this.tsIndex.resourceChildren(resource.identifier).map((c) => c.name);
1510
+ if (children.length > 0) {
1511
+ const familyName = `${resource.identifier.name}Family`;
1512
+ families[familyName] = children;
1513
+ }
1514
+ }
1515
+ const exportList = Object.keys(families);
1516
+ if (exportList.length === 0) return;
1517
+ this.buildResourceFamiliesFile(packages, families, exportList);
1518
+ }
1519
+ buildResourceFamiliesFile(packages, families, exportList) {
1520
+ this.cat("resource_families.py", () => {
1521
+ this.generateDisclaimer();
1522
+ this.includeResourceFamilyValidator();
1523
+ this.line();
1524
+ this.generateFamilyDefinitions(packages, families);
1525
+ this.generateFamilyExports(exportList);
1526
+ });
1527
+ }
1528
+ includeResourceFamilyValidator() {
1529
+ const content = fs__default.readFileSync(resolvePyAssets("resource_family_validator.py"), "utf-8");
1530
+ this.line(content);
1531
+ }
1532
+ generateFamilyDefinitions(packages, families) {
1533
+ this.line(`packages = [${packages.map((p) => `'${p}'`).join(", ")}]`);
1534
+ this.line();
1535
+ for (const [familyName, resources] of Object.entries(families)) {
1536
+ this.generateFamilyDefinition(familyName, resources);
1537
+ }
1538
+ }
1539
+ generateFamilyDefinition(familyName, resources) {
1540
+ const listName = `${familyName}_resources`;
1541
+ this.line(`${listName} = [${resources.map((r) => `'${r}'`).join(", ")}]`);
1542
+ this.line();
1543
+ this.line(`def validate_and_downcast_${familyName}(v: Any) -> Any:`);
1544
+ this.line(` return validate_and_downcast(v, packages, ${listName})`);
1545
+ this.line();
1546
+ this.line(`type ${familyName} = Annotated[Any, BeforeValidator(validate_and_downcast_${familyName})]`);
1547
+ this.line();
1548
+ }
1549
+ generateFamilyExports(exportList) {
1550
+ this.line(`__all__ = [${exportList.map((e) => `'${e}'`).join(", ")}]`);
1551
+ }
1552
+ buildPyPackageName(packageName) {
1553
+ const parts = packageName ? [snakeCase(packageName)] : [""];
1554
+ return parts.join(".");
1555
+ }
1556
+ pyFhirPackage(identifier) {
1557
+ return this.pyFhirPackageByName(identifier.package);
1558
+ }
1559
+ pyFhirPackageByName(name) {
1560
+ return [this.opts.rootPackageName, this.buildPyPackageName(name)].join(".");
1561
+ }
1562
+ pyPackage(identifier) {
1563
+ if (identifier.kind === "complex-type") {
1564
+ return `${this.pyFhirPackage(identifier)}.base`;
1565
+ }
1566
+ if (identifier.kind === "resource") {
1567
+ return [this.pyFhirPackage(identifier), snakeCase(identifier.name)].join(".");
1568
+ }
1569
+ return this.pyFhirPackage(identifier);
1570
+ }
1571
+ getFieldFormatFunction(format2) {
1572
+ if (!AVAILABLE_STRING_FORMATS[format2]) {
1573
+ this.logger()?.warn(`Unknown field format '${format2}'. Defaulting to SnakeCase.`);
1574
+ this.logger()?.warn(`Supported formats: ${Object.keys(AVAILABLE_STRING_FORMATS).join(", ")}`);
1575
+ return snakeCase;
1576
+ }
1577
+ return AVAILABLE_STRING_FORMATS[format2];
1578
+ }
781
1579
  };
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
1580
 
789
1581
  // src/typeschema/core/identifier.ts
790
1582
  function dropVersionFromUrl(url) {
@@ -856,7 +1648,7 @@ function mkNestedIdentifier(register, fhirSchema, path, logger) {
856
1648
  const nestedTypeOrigins = {};
857
1649
  if (fhirSchema.derivation === "constraint") {
858
1650
  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();
1651
+ const nestedTypeGenealogy = specializations.map((fs7) => mkNestedTypes(register, fs7, logger)).filter((e) => e !== void 0).flat();
860
1652
  for (const nt of nestedTypeGenealogy.reverse()) {
861
1653
  nestedTypeOrigins[nt.identifier.name] = nt.identifier.url;
862
1654
  }
@@ -955,10 +1747,10 @@ function isRequired(register, fhirSchema, path) {
955
1747
  const fieldName = path[path.length - 1];
956
1748
  if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
957
1749
  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;
1750
+ const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs7) => {
1751
+ if (parentPath.length === 0) return fs7.required || [];
1752
+ if (!fs7.elements) return [];
1753
+ let elem = fs7;
962
1754
  for (const k of parentPath) {
963
1755
  elem = elem?.elements?.[k];
964
1756
  }
@@ -970,10 +1762,10 @@ function isExcluded(register, fhirSchema, path) {
970
1762
  const fieldName = path[path.length - 1];
971
1763
  if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
972
1764
  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;
1765
+ const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs7) => {
1766
+ if (parentPath.length === 0) return fs7.excluded || [];
1767
+ if (!fs7.elements) return [];
1768
+ let elem = fs7;
977
1769
  for (const k of parentPath) {
978
1770
  elem = elem?.elements?.[k];
979
1771
  }
@@ -985,9 +1777,9 @@ var buildReferences = (register, fhirSchema, element) => {
985
1777
  if (!element.refers) return void 0;
986
1778
  return element.refers.map((ref) => {
987
1779
  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);
1780
+ const fs7 = register.resolveFs(fhirSchema.package_meta, curl);
1781
+ if (!fs7) throw new Error(`Failed to resolve fs for ${curl}`);
1782
+ return mkIdentifier(fs7);
991
1783
  });
992
1784
  };
993
1785
  function buildFieldType(register, fhirSchema, path, element, logger) {
@@ -1065,7 +1857,7 @@ function extractValueSetConceptsByUrl(register, pkg, valueSetUrl, logger) {
1065
1857
  function extractValueSetConcepts(register, valueSet, _logger) {
1066
1858
  if (valueSet.expansion?.contains) {
1067
1859
  return valueSet.expansion.contains.filter((item) => item.code !== void 0).map((item) => {
1068
- assert(item.code);
1860
+ assert3(item.code);
1069
1861
  return {
1070
1862
  code: item.code,
1071
1863
  display: item.display,
@@ -1270,62 +2062,422 @@ function transformFhirSchemaResource(register, fhirSchema, logger) {
1270
2062
  `Base resource not found '${fhirSchema.base}' for <${fhirSchema.url}> from ${packageMetaToFhir(fhirSchema.package_meta)}`
1271
2063
  );
1272
2064
  }
1273
- base = mkIdentifier(baseFs);
2065
+ base = mkIdentifier(baseFs);
2066
+ }
2067
+ const fields = mkFields(register, fhirSchema, [], fhirSchema.elements, logger);
2068
+ const nested = mkNestedTypes(register, fhirSchema, logger);
2069
+ const dependencies = extractDependencies(identifier, base, fields, nested);
2070
+ const typeSchema = {
2071
+ identifier,
2072
+ base,
2073
+ fields,
2074
+ nested,
2075
+ description: fhirSchema.description,
2076
+ dependencies
2077
+ };
2078
+ const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger);
2079
+ return [typeSchema, ...bindingSchemas];
2080
+ }
2081
+ async function transformFhirSchema(register, fhirSchema, logger) {
2082
+ const schemas = transformFhirSchemaResource(register, fhirSchema, logger);
2083
+ if (isExtensionSchema(fhirSchema, mkIdentifier(fhirSchema))) {
2084
+ const schema = schemas[0];
2085
+ if (!schema) throw new Error(`Expected schema to be defined`);
2086
+ schema.metadata = {
2087
+ isExtension: true
2088
+ // Mark as extension for file organization
2089
+ };
2090
+ }
2091
+ return schemas;
2092
+ }
2093
+
2094
+ // src/fhir-types/hl7-fhir-r4-core/CodeSystem.ts
2095
+ var isCodeSystem = (resource) => {
2096
+ return resource !== null && typeof resource === "object" && resource.resourceType === "CodeSystem";
2097
+ };
2098
+
2099
+ // src/fhir-types/hl7-fhir-r4-core/ValueSet.ts
2100
+ var isValueSet = (resource) => {
2101
+ return resource !== null && typeof resource === "object" && resource.resourceType === "ValueSet";
2102
+ };
2103
+
2104
+ // src/typeschema/register.ts
2105
+ var readPackageDependencies = async (manager, packageMeta) => {
2106
+ const packageJSON = await manager.packageJson(packageMeta.name);
2107
+ const dependencies = packageJSON.dependencies;
2108
+ if (dependencies !== void 0) {
2109
+ return Object.entries(dependencies).map(([name, version]) => {
2110
+ return { name, version };
2111
+ });
2112
+ }
2113
+ return [];
2114
+ };
2115
+ var mkEmptyPkgIndex = (pkg) => {
2116
+ return {
2117
+ pkg,
2118
+ canonicalResolution: {},
2119
+ fhirSchemas: {},
2120
+ valueSets: {}
2121
+ };
2122
+ };
2123
+ var mkPackageAwareResolver = async (manager, pkg, deep, acc, logger) => {
2124
+ const pkgId = packageMetaToFhir(pkg);
2125
+ logger?.info(`${" ".repeat(deep * 2)}+ ${pkgId}`);
2126
+ if (acc[pkgId]) return acc[pkgId];
2127
+ const index = mkEmptyPkgIndex(pkg);
2128
+ for (const resource of await manager.search({ package: pkg })) {
2129
+ const rawUrl = resource.url;
2130
+ if (!rawUrl) continue;
2131
+ if (!(isStructureDefinition(resource) || isValueSet(resource) || isCodeSystem(resource))) continue;
2132
+ const url = rawUrl;
2133
+ if (index.canonicalResolution[url]) logger?.dry_warn(`Duplicate canonical URL: ${url} at ${pkgId}.`);
2134
+ index.canonicalResolution[url] = [{ deep, pkg, pkgId, resource }];
2135
+ }
2136
+ const deps = await readPackageDependencies(manager, pkg);
2137
+ for (const depPkg of deps) {
2138
+ const { canonicalResolution } = await mkPackageAwareResolver(manager, depPkg, deep + 1, acc, logger);
2139
+ for (const [surl, resolutions] of Object.entries(canonicalResolution)) {
2140
+ const url = surl;
2141
+ index.canonicalResolution[url] = [...index.canonicalResolution[url] || [], ...resolutions];
2142
+ }
2143
+ }
2144
+ for (const resolutionOptions of Object.values(index.canonicalResolution)) {
2145
+ resolutionOptions.sort((a, b) => a.deep - b.deep);
2146
+ }
2147
+ acc[pkgId] = index;
2148
+ return index;
2149
+ };
2150
+ var packageAgnosticResolveCanonical = (resolver, url, _logger) => {
2151
+ const options = Object.values(resolver).flatMap((pkg) => pkg.canonicalResolution[url]);
2152
+ if (!options) throw new Error(`No canonical resolution found for ${url} in any package`);
2153
+ return options[0]?.resource;
2154
+ };
2155
+ var registerFromManager = async (manager, { logger, fallbackPackageForNameResolution, focusedPackages }) => {
2156
+ const packages = focusedPackages ?? await manager.packages();
2157
+ const resolver = {};
2158
+ for (const pkg of packages) {
2159
+ await mkPackageAwareResolver(manager, pkg, 0, resolver, logger);
2160
+ }
2161
+ for (const { pkg, canonicalResolution } of Object.values(resolver)) {
2162
+ const pkgId = packageMetaToFhir(pkg);
2163
+ if (!resolver[pkgId]) throw new Error(`Package ${pkgId} not found`);
2164
+ let counter = 0;
2165
+ logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' begins...`);
2166
+ for (const [_url, options] of Object.entries(canonicalResolution)) {
2167
+ const resolition = options[0];
2168
+ if (!resolition) throw new Error(`Resource not found`);
2169
+ const resource = resolition.resource;
2170
+ const resourcePkg = resolition.pkg;
2171
+ if (isStructureDefinition(resource)) {
2172
+ const rfs = enrichFHIRSchema(
2173
+ fhirschema.translate(resource),
2174
+ resourcePkg
2175
+ );
2176
+ counter++;
2177
+ resolver[pkgId].fhirSchemas[rfs.url] = rfs;
2178
+ }
2179
+ if (isValueSet(resource)) {
2180
+ const rvs = enrichValueSet(resource, resourcePkg);
2181
+ resolver[pkgId].valueSets[rvs.url] = rvs;
2182
+ }
2183
+ }
2184
+ logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' completed: ${counter} successful`);
2185
+ }
2186
+ const resolveFs = (pkg, canonicalUrl) => {
2187
+ return resolver[packageMetaToFhir(pkg)]?.fhirSchemas[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.fhirSchemas[canonicalUrl];
2188
+ };
2189
+ const resolveVs = (pkg, canonicalUrl) => {
2190
+ return resolver[packageMetaToFhir(pkg)]?.valueSets[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.valueSets[canonicalUrl];
2191
+ };
2192
+ const ensureSpecializationCanonicalUrl = (name) => name.match(/^[a-zA-Z0-9]+$/) && `http://hl7.org/fhir/StructureDefinition/${name}` || name;
2193
+ const resolveFsGenealogy = (pkg, canonicalUrl) => {
2194
+ let fs7 = resolveFs(pkg, canonicalUrl);
2195
+ if (fs7 === void 0) throw new Error(`Failed to resolve FHIR Schema: '${canonicalUrl}'`);
2196
+ const genealogy = [fs7];
2197
+ while (fs7?.base) {
2198
+ const pkg2 = fs7.package_meta;
2199
+ const baseUrl = ensureSpecializationCanonicalUrl(fs7.base);
2200
+ fs7 = resolveFs(pkg2, baseUrl);
2201
+ if (fs7 === void 0)
2202
+ throw new Error(
2203
+ `Failed to resolve FHIR Schema base for '${canonicalUrl}'. Problem: '${baseUrl}' from '${packageMetaToFhir(pkg2)}'`
2204
+ );
2205
+ genealogy.push(fs7);
2206
+ }
2207
+ return genealogy;
2208
+ };
2209
+ const resolveFsSpecializations = (pkg, canonicalUrl) => {
2210
+ return resolveFsGenealogy(pkg, canonicalUrl).filter((fs7) => fs7.derivation === "specialization");
2211
+ };
2212
+ const resolveElementSnapshot = (fhirSchema, path) => {
2213
+ const geneology = resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url);
2214
+ const elemGeneology = resolveFsElementGenealogy(geneology, path);
2215
+ const elemSnapshot = fsElementSnapshot(elemGeneology);
2216
+ return elemSnapshot;
2217
+ };
2218
+ const getAllElementKeys = (elems) => {
2219
+ const keys = /* @__PURE__ */ new Set();
2220
+ for (const [key, elem] of Object.entries(elems)) {
2221
+ keys.add(key);
2222
+ for (const choiceKey of elem?.choices || []) {
2223
+ if (!elems[choiceKey]) {
2224
+ keys.add(choiceKey);
2225
+ }
2226
+ }
2227
+ }
2228
+ return Array.from(keys);
2229
+ };
2230
+ return {
2231
+ ...manager,
2232
+ testAppendFs(fs7) {
2233
+ const rfs = enrichFHIRSchema(fs7);
2234
+ const pkgId = packageMetaToFhir(rfs.package_meta);
2235
+ if (!resolver[pkgId]) resolver[pkgId] = mkEmptyPkgIndex(rfs.package_meta);
2236
+ resolver[pkgId].fhirSchemas[rfs.url] = rfs;
2237
+ },
2238
+ resolveFs,
2239
+ resolveFsGenealogy,
2240
+ resolveFsSpecializations,
2241
+ ensureSpecializationCanonicalUrl,
2242
+ resolveSd: (_pkg, canonicalUrl) => {
2243
+ const res = packageAgnosticResolveCanonical(resolver, canonicalUrl);
2244
+ if (isStructureDefinition(res)) return res;
2245
+ return void 0;
2246
+ },
2247
+ allFs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.fhirSchemas)),
2248
+ allVs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.valueSets)),
2249
+ resolveVs,
2250
+ resolveAny: (canonicalUrl) => packageAgnosticResolveCanonical(resolver, canonicalUrl),
2251
+ resolveElementSnapshot,
2252
+ getAllElementKeys,
2253
+ resolver,
2254
+ resolutionTree: () => {
2255
+ const res = {};
2256
+ for (const [_pkgId, pkgIndex] of Object.entries(resolver)) {
2257
+ const pkgName = pkgIndex.pkg.name;
2258
+ res[pkgName] = {};
2259
+ for (const [surl, resolutions] of Object.entries(pkgIndex.canonicalResolution)) {
2260
+ const url = surl;
2261
+ res[pkgName][url] = [];
2262
+ for (const resolution of resolutions) {
2263
+ res[pkgName][url].push({ deep: resolution.deep, pkg: resolution.pkg });
2264
+ }
2265
+ }
2266
+ }
2267
+ return res;
2268
+ }
2269
+ };
2270
+ };
2271
+ var resolveFsElementGenealogy = (genealogy, path) => {
2272
+ const [top, ...rest] = path;
2273
+ if (top === void 0) return [];
2274
+ return genealogy.map((fs7) => {
2275
+ if (!fs7.elements) return void 0;
2276
+ let elem = fs7.elements?.[top];
2277
+ for (const k of rest) {
2278
+ elem = elem?.elements?.[k];
2279
+ }
2280
+ return elem;
2281
+ }).filter((elem) => elem !== void 0);
2282
+ };
2283
+ function fsElementSnapshot(genealogy) {
2284
+ const revGenealogy = genealogy.reverse();
2285
+ const snapshot = Object.assign({}, ...revGenealogy);
2286
+ snapshot.elements = void 0;
2287
+ return snapshot;
2288
+ }
2289
+
2290
+ // src/typeschema/index.ts
2291
+ var codeableReferenceInR4 = "Use CodeableReference which is not provided by FHIR R4.";
2292
+ var availabilityInR4 = "Use Availability which is not provided by FHIR R4.";
2293
+ var skipMe = {
2294
+ "hl7.fhir.uv.extensions.r4#1.0.0": {
2295
+ "http://hl7.org/fhir/StructureDefinition/extended-contact-availability": availabilityInR4,
2296
+ "http://hl7.org/fhir/StructureDefinition/immunization-procedure": codeableReferenceInR4,
2297
+ "http://hl7.org/fhir/StructureDefinition/specimen-additive": codeableReferenceInR4,
2298
+ "http://hl7.org/fhir/StructureDefinition/workflow-barrier": codeableReferenceInR4,
2299
+ "http://hl7.org/fhir/StructureDefinition/workflow-protectiveFactor": codeableReferenceInR4,
2300
+ "http://hl7.org/fhir/StructureDefinition/workflow-reason": codeableReferenceInR4
2301
+ },
2302
+ "hl7.fhir.r5.core#5.0.0": {
2303
+ "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."
2304
+ }
2305
+ };
2306
+ var generateTypeSchemas = async (register, logger) => {
2307
+ const fhirSchemas = [];
2308
+ for (const fhirSchema of register.allFs()) {
2309
+ const pkgId = packageMetaToFhir(fhirSchema.package_meta);
2310
+ if (skipMe[pkgId]?.[fhirSchema.url]) {
2311
+ logger?.dry_warn(`Skip ${fhirSchema.url} from ${pkgId}. Reason: ${skipMe[pkgId]?.[fhirSchema.url]}`);
2312
+ continue;
2313
+ }
2314
+ fhirSchemas.push(...await transformFhirSchema(register, fhirSchema, logger));
2315
+ }
2316
+ for (const vsSchema of register.allVs()) {
2317
+ fhirSchemas.push(await transformValueSet(register, vsSchema));
2318
+ }
2319
+ return fhirSchemas;
2320
+ };
2321
+ var ensureTreeShakeReport = (indexOrReport) => {
2322
+ if ("treeShakeReport" in indexOrReport && typeof indexOrReport.treeShakeReport === "function") {
2323
+ const report = indexOrReport.treeShakeReport();
2324
+ assert3(report);
2325
+ return report;
2326
+ } else {
2327
+ return indexOrReport;
2328
+ }
2329
+ };
2330
+ var rootTreeShakeReadme = (report) => {
2331
+ report = ensureTreeShakeReport(report);
2332
+ const lines = ["# Tree Shake Report", ""];
2333
+ if (report.skippedPackages.length === 0) lines.push("All packages are included.");
2334
+ else lines.push("Skipped packages:", "");
2335
+ for (const pkgName of report.skippedPackages) {
2336
+ lines.push(`- ${pkgName}`);
2337
+ }
2338
+ lines.push("");
2339
+ return lines.join("\n");
2340
+ };
2341
+ var packageTreeShakeReadme = (report, pkgName) => {
2342
+ report = ensureTreeShakeReport(report);
2343
+ const lines = [`# Package: ${pkgName}`, ""];
2344
+ assert3(report.packages[pkgName]);
2345
+ lines.push("## Canonical Fields Changes", "");
2346
+ if (Object.keys(report.packages[pkgName].canonicals).length === 0) {
2347
+ lines.push("All canonicals translated as is.", "");
2348
+ } else {
2349
+ for (const [canonicalUrl, { skippedFields }] of Object.entries(report.packages[pkgName].canonicals)) {
2350
+ lines.push(`- <${canonicalUrl}>`);
2351
+ if (skippedFields.length === 0) {
2352
+ lines.push(" - All fields translated as is.", "");
2353
+ } else {
2354
+ lines.push(` - Skipped fields: ${skippedFields.map((f) => `\`${f}\``).join(", ")}`);
2355
+ lines.push("");
2356
+ }
2357
+ }
2358
+ lines.push("");
2359
+ }
2360
+ lines.push("## Skipped Canonicals", "");
2361
+ if (report.packages[pkgName].skippedCanonicals.length === 0) {
2362
+ lines.push("No skipped canonicals");
2363
+ } else {
2364
+ lines.push("Skipped canonicals:", "");
2365
+ for (const canonicalUrl of report.packages[pkgName].skippedCanonicals) {
2366
+ lines.push(`- <${canonicalUrl}>`);
2367
+ }
2368
+ lines.push("");
1274
2369
  }
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
- };
2370
+ return lines.join("\n");
2371
+ };
2372
+ var mutableSelectFields = (schema, selectFields) => {
2373
+ const selectedFields = {};
2374
+ const selectPolimorphic = {};
2375
+ for (const fieldName of selectFields) {
2376
+ const field = schema.fields?.[fieldName];
2377
+ if (!schema.fields || !field) throw new Error(`Field ${fieldName} not found`);
2378
+ if (isChoiceDeclarationField(field)) {
2379
+ if (!selectPolimorphic[fieldName]) selectPolimorphic[fieldName] = {};
2380
+ selectPolimorphic[fieldName].declaration = field.choices;
2381
+ } else if (isChoiceInstanceField(field)) {
2382
+ const choiceName = field.choiceOf;
2383
+ if (!selectPolimorphic[choiceName]) selectPolimorphic[choiceName] = {};
2384
+ selectPolimorphic[choiceName].instances = [...selectPolimorphic[choiceName].instances ?? [], fieldName];
2385
+ } else {
2386
+ selectedFields[fieldName] = field;
2387
+ }
1298
2388
  }
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);
2389
+ for (const [choiceName, { declaration, instances }] of Object.entries(selectPolimorphic)) {
2390
+ const choices = instances ?? declaration;
2391
+ assert3(choices);
2392
+ for (const choiceInstanceName of choices) {
2393
+ const field = schema.fields?.[choiceInstanceName];
2394
+ assert3(field);
2395
+ selectedFields[choiceInstanceName] = field;
2396
+ }
2397
+ const decl = schema.fields?.[choiceName];
2398
+ assert3(decl);
2399
+ selectedFields[choiceName] = { ...decl, choices };
1309
2400
  }
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;
2401
+ schema.fields = selectedFields;
2402
+ };
2403
+ var mutableIgnoreFields = (schema, ignoreFields) => {
2404
+ for (const fieldName of ignoreFields) {
2405
+ const field = schema.fields?.[fieldName];
2406
+ if (!schema.fields || !field) throw new Error(`Field ${fieldName} not found`);
2407
+ if (schema.fields) {
2408
+ if (isChoiceDeclarationField(field)) {
2409
+ for (const choiceName of field.choices) {
2410
+ delete schema.fields[choiceName];
2411
+ }
2412
+ }
2413
+ if (isChoiceInstanceField(field)) {
2414
+ const choiceDeclaration = schema.fields[field.choiceOf];
2415
+ assert3(isChoiceDeclarationField(choiceDeclaration));
2416
+ choiceDeclaration.choices = choiceDeclaration.choices.filter((c) => c !== fieldName);
2417
+ if (choiceDeclaration.choices.length === 0) {
2418
+ delete schema.fields[field.choiceOf];
2419
+ }
2420
+ }
2421
+ delete schema.fields[fieldName];
1314
2422
  }
1315
- const tmp = Object.values(dict);
1316
- tmp.sort((a, b) => a.identifier.name.localeCompare(b.identifier.name));
1317
- grouped[packageName] = tmp;
1318
2423
  }
1319
- return grouped;
2424
+ };
2425
+ var mutableFillReport = (report, tsIndex, shakedIndex) => {
2426
+ const packages = Object.keys(tsIndex.schemasByPackage);
2427
+ const shakedPackages = Object.keys(shakedIndex.schemasByPackage);
2428
+ const skippedPackages = packages.filter((pkg) => !shakedPackages.includes(pkg));
2429
+ report.skippedPackages = skippedPackages;
2430
+ for (const [pkgName, shakedSchemas] of Object.entries(shakedIndex.schemasByPackage)) {
2431
+ if (skippedPackages.includes(pkgName)) continue;
2432
+ const tsSchemas = tsIndex.schemasByPackage[pkgName];
2433
+ assert3(tsSchemas);
2434
+ report.packages[pkgName] = {
2435
+ skippedCanonicals: tsSchemas.filter((schema) => !shakedSchemas.includes(schema)).map((schema) => schema.identifier.url).sort(),
2436
+ canonicals: Object.fromEntries(
2437
+ shakedSchemas.map((shakedSchema) => {
2438
+ const schema = tsIndex.resolve(shakedSchema.identifier);
2439
+ assert3(schema);
2440
+ if (!isSpecializationTypeSchema(schema)) return void 0;
2441
+ assert3(isSpecializationTypeSchema(shakedSchema));
2442
+ if (!schema.fields) return void 0;
2443
+ if (!shakedSchema.fields) {
2444
+ return [shakedSchema.identifier.url, Object.keys(schema.fields)];
2445
+ }
2446
+ const shakedFieldNames = Object.keys(shakedSchema.fields);
2447
+ const skippedFields = Object.keys(schema.fields).filter((field) => !shakedFieldNames.includes(field)).sort();
2448
+ if (skippedFields.length === 0) return void 0;
2449
+ return [shakedSchema.identifier.url, { skippedFields }];
2450
+ }).filter((e) => e !== void 0)
2451
+ )
2452
+ };
2453
+ }
1320
2454
  };
1321
2455
  var treeShakeTypeSchema = (schema, rule, _logger) => {
1322
2456
  schema = structuredClone(schema);
1323
2457
  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`);
1326
- if (schema.fields) {
1327
- delete schema.fields[fieldName];
1328
- }
2458
+ if (rule.selectFields) {
2459
+ if (rule.ignoreFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule");
2460
+ mutableSelectFields(schema, rule.selectFields);
2461
+ }
2462
+ if (rule.ignoreFields) {
2463
+ if (rule.selectFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule");
2464
+ mutableIgnoreFields(schema, rule.ignoreFields);
2465
+ }
2466
+ if (schema.nested) {
2467
+ const usedTypes = /* @__PURE__ */ new Set();
2468
+ const collectUsedNestedTypes = (s) => {
2469
+ Object.values(s.fields ?? {}).filter(isNotChoiceDeclarationField).filter((f) => isNestedIdentifier(f.type)).forEach((f) => {
2470
+ const url = f.type.url;
2471
+ if (!usedTypes.has(url)) {
2472
+ usedTypes.add(url);
2473
+ const nestedTypeDef = schema.nested?.find((f2) => f2.identifier.url === url);
2474
+ assert3(nestedTypeDef);
2475
+ collectUsedNestedTypes(nestedTypeDef);
2476
+ }
2477
+ });
2478
+ };
2479
+ collectUsedNestedTypes(schema);
2480
+ schema.nested = schema.nested.filter((n) => usedTypes.has(n.identifier.url));
1329
2481
  }
1330
2482
  schema.dependencies = extractDependencies(schema.identifier, schema.base, schema.fields, schema.nested);
1331
2483
  return schema;
@@ -1351,7 +2503,10 @@ var treeShake = (tsIndex, treeShake2, { resolutionTree, logger }) => {
1351
2503
  if (!schema.dependencies) continue;
1352
2504
  schema.dependencies.forEach((dep) => {
1353
2505
  const depSchema = tsIndex.resolve(dep);
1354
- if (!depSchema) throw new Error(`Schema not found for ${dep}`);
2506
+ if (!depSchema)
2507
+ throw new Error(
2508
+ `Dependent schema ${JSON.stringify(dep)} not found for ${JSON.stringify(schema.identifier)}`
2509
+ );
1355
2510
  const id = JSON.stringify(depSchema.identifier);
1356
2511
  if (!acc[id]) newSchemas.push(depSchema);
1357
2512
  });
@@ -1367,336 +2522,657 @@ var treeShake = (tsIndex, treeShake2, { resolutionTree, logger }) => {
1367
2522
  return collectDeps(newSchemas, acc);
1368
2523
  };
1369
2524
  const shaked = collectDeps(focusedSchemas, {});
1370
- return mkTypeSchemaIndex(shaked, { resolutionTree, logger });
2525
+ const report = { skippedPackages: [], packages: {} };
2526
+ const shakedIndex = mkTypeSchemaIndex(shaked, { resolutionTree, logger, treeShakeReport: report });
2527
+ mutableFillReport(report, tsIndex, shakedIndex);
2528
+ return shakedIndex;
1371
2529
  };
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
- }
2530
+
2531
+ // src/api/mustache/generator/DebugMixinProvider.ts
2532
+ var DebugMixinProvider = class {
2533
+ constructor(mode) {
2534
+ this.mode = mode;
1379
2535
  }
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));
2536
+ apply(target) {
2537
+ return this._addDebug(target);
2538
+ }
2539
+ _addDebug(value) {
2540
+ if (Array.isArray(value)) {
2541
+ return value.map((v) => this._addDebug(v));
1386
2542
  }
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 });
2543
+ if (value !== null && typeof value === "object") {
2544
+ const obj = value;
2545
+ const result = {};
2546
+ const debugString = JSON.stringify(obj, null, this.mode === "FORMATTED" ? 2 : void 0);
2547
+ for (const [key, val] of Object.entries(obj)) {
2548
+ result[key] = this._addDebug(val);
1394
2549
  }
2550
+ result.debug = debugString;
2551
+ return result;
1395
2552
  }
2553
+ return value;
1396
2554
  }
1397
- return allPairs;
1398
2555
  };
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;
1410
- }
1411
- index[url][pkg] = schema;
1412
- };
1413
- for (const schema of schemas) {
1414
- append(schema);
2556
+
2557
+ // src/api/mustache/generator/LambdaMixinProvider.ts
2558
+ var LambdaMixinProvider = class {
2559
+ constructor(nameGenerator) {
2560
+ this.nameGenerator = nameGenerator;
2561
+ this.lambda = {
2562
+ saveTypeName: () => (text, render) => this.nameGenerator.generateType(render(text)),
2563
+ saveEnumValueName: () => (text, render) => this.nameGenerator.generateEnumValue(render(text)),
2564
+ saveFieldName: () => (text, render) => this.nameGenerator.generateField(render(text)),
2565
+ camelCase: () => (text, render) => camelCase(render(text)),
2566
+ snakeCase: () => (text, render) => snakeCase(render(text)),
2567
+ pascalCase: () => (text, render) => pascalCase(render(text)),
2568
+ kebabCase: () => (text, render) => kebabCase(render(text)),
2569
+ lowerCase: () => (text, render) => render(text).toLowerCase(),
2570
+ upperCase: () => (text, render) => render(text).toUpperCase()
2571
+ };
1415
2572
  }
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];
1423
- }
2573
+ lambda;
2574
+ apply(target) {
2575
+ return {
2576
+ ...target,
2577
+ lambda: this.lambda
2578
+ };
2579
+ }
2580
+ };
2581
+
2582
+ // src/api/mustache/generator/NameGenerator.ts
2583
+ var NameGenerator = class {
2584
+ constructor(keywords, typeMap, nameTransformations, unsaveCharacterPattern) {
2585
+ this.keywords = keywords;
2586
+ this.typeMap = typeMap;
2587
+ this.nameTransformations = nameTransformations;
2588
+ this.unsaveCharacterPattern = unsaveCharacterPattern;
2589
+ }
2590
+ _replaceUnsaveChars(name) {
2591
+ const pattern = this.unsaveCharacterPattern instanceof RegExp ? this.unsaveCharacterPattern : new RegExp(this.unsaveCharacterPattern, "g");
2592
+ return name.replace(pattern, "_");
2593
+ }
2594
+ _applyNameTransformations(name, transformations) {
2595
+ for (const transformation of this.nameTransformations.common) {
2596
+ name = name.replace(
2597
+ transformation.pattern instanceof RegExp ? transformation.pattern : new RegExp(transformation.pattern, "g"),
2598
+ transformation.format
2599
+ );
1424
2600
  }
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;
2601
+ for (const transformation of transformations) {
2602
+ name = name.replace(
2603
+ transformation.pattern instanceof RegExp ? transformation.pattern : new RegExp(transformation.pattern, "g"),
2604
+ transformation.format
2605
+ );
2606
+ }
2607
+ return name;
2608
+ }
2609
+ _generateTypeName(name) {
2610
+ if (this.typeMap[name]) {
2611
+ name = this.typeMap[name];
2612
+ } else {
2613
+ name = this._applyNameTransformations(name, this.nameTransformations.type);
2614
+ name = name.charAt(0).toUpperCase() + name.slice(1);
2615
+ if (this.keywords.has(name)) {
2616
+ return `_${name}`;
1443
2617
  }
1444
- cur = resolved;
2618
+ name = this._replaceUnsaveChars(name);
1445
2619
  }
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})`);
2620
+ return name;
2621
+ }
2622
+ generateEnumType(name) {
2623
+ return this._generateTypeName(name);
2624
+ }
2625
+ _generateTypeFromTypeRef(typeRef) {
2626
+ if (typeRef.kind === "primitive-type") {
2627
+ return this._generateTypeName(typeRef.name);
1452
2628
  }
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}`);
2629
+ return this._generateTypeName(
2630
+ typeRef.url.split("/").pop()?.split("#").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("") ?? "<UNKNOWN>"
2631
+ );
2632
+ }
2633
+ generateFieldType(schema) {
2634
+ if (schema.enum) {
2635
+ return this.generateEnumType(schema.binding?.name ?? schema.type.name);
1459
2636
  }
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
- }
2637
+ return this._generateTypeFromTypeRef(schema.type);
2638
+ }
2639
+ generateType(schemaOrRefOrString) {
2640
+ if (typeof schemaOrRefOrString === "string") {
2641
+ return this._generateTypeName(schemaOrRefOrString);
1487
2642
  }
1488
- const deps = {};
1489
- for (const e of constraintSchemas.flatMap((e2) => e2.dependencies ?? [])) {
1490
- deps[e.url] = e;
2643
+ if ("url" in schemaOrRefOrString) {
2644
+ return this._generateTypeFromTypeRef(schemaOrRefOrString);
1491
2645
  }
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
- }
2646
+ return this._generateTypeFromTypeRef(schemaOrRefOrString.identifier);
2647
+ }
2648
+ generateField(name) {
2649
+ name = this._applyNameTransformations(name, this.nameTransformations.field);
2650
+ if (this.keywords.has(name)) {
2651
+ return `_${name}`;
1523
2652
  }
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
- };
1546
- };
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
- };
2653
+ name = this._replaceUnsaveChars(name);
2654
+ return name;
1556
2655
  }
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 */]: () => {
2656
+ generateEnumValue(name) {
2657
+ name = this._applyNameTransformations(name, this.nameTransformations.enumValue);
2658
+ if (this.keywords.has(name)) {
2659
+ return `_${name}`;
1563
2660
  }
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}`;
2661
+ name = this._replaceUnsaveChars(name).toUpperCase();
2662
+ return name;
1569
2663
  }
1570
- isSuppressed(level) {
1571
- return this.options.suppressLoggingLevel === "all" || this.options.suppressLoggingLevel?.includes(level) || false;
2664
+ };
2665
+ var TemplateFileCache = class {
2666
+ templateBaseDir;
2667
+ templateCache = {};
2668
+ constructor(templateBaseDir) {
2669
+ this.templateBaseDir = Path__default.resolve(templateBaseDir);
2670
+ }
2671
+ _normalizeName(name) {
2672
+ if (name.endsWith(".mustache")) {
2673
+ return name;
2674
+ }
2675
+ return `${name}.mustache`;
1572
2676
  }
1573
- tryWriteToConsole(level, formattedMessage) {
1574
- if (this.isSuppressed(level)) return;
1575
- const logFn = _CodegenLogger.consoleLevelsMap[level] || console.log;
1576
- logFn(formattedMessage);
2677
+ read(template) {
2678
+ return this.readTemplate(template.source);
1577
2679
  }
1578
- /**
1579
- * Success message with checkmark
1580
- */
1581
- success(message) {
1582
- this.tryWriteToConsole(1 /* INFO */, this.formatMessage("", message, pc.green));
2680
+ readTemplate(name) {
2681
+ const normalizedName = this._normalizeName(name);
2682
+ if (!this.templateCache[normalizedName]) {
2683
+ this.templateCache[normalizedName] = fs__default.readFileSync(
2684
+ Path__default.join(this.templateBaseDir, normalizedName),
2685
+ "utf-8"
2686
+ );
2687
+ }
2688
+ return this.templateCache[normalizedName];
1583
2689
  }
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));
2690
+ };
2691
+
2692
+ // src/api/mustache/generator/ListElementInformationMixinProvider.ts
2693
+ var ListElementInformationMixinProvider = class _ListElementInformationMixinProvider {
2694
+ static _array(value) {
2695
+ return Array.isArray(value) ? value : Array.from(value);
2696
+ }
2697
+ apply(source) {
2698
+ return this._addListElementInformation(source);
2699
+ }
2700
+ _addListElementInformation(value) {
2701
+ if (Array.isArray(value) || value instanceof Set) {
2702
+ return _ListElementInformationMixinProvider._array(value).map((v, index, array) => {
2703
+ if (typeof v === "object" && v !== null) {
2704
+ return {
2705
+ ...this._addListElementInformation(v),
2706
+ "-index": index,
2707
+ "-length": array.length,
2708
+ "-first": index === 0,
2709
+ "-last": index === array.length - 1
2710
+ };
2711
+ }
2712
+ return v;
2713
+ });
2714
+ }
2715
+ if (value !== null && typeof value === "object") {
2716
+ const obj = value;
2717
+ const result = {};
2718
+ for (const [key, val] of Object.entries(obj)) {
2719
+ result[key] = this._addListElementInformation(val);
1594
2720
  }
2721
+ return result;
1595
2722
  }
2723
+ return value;
1596
2724
  }
1597
- /**
1598
- * Warning message with warning sign
1599
- */
1600
- warn(message) {
1601
- this.tryWriteToConsole(2 /* WARN */, this.formatMessage("!", message, pc.yellow));
2725
+ };
2726
+
2727
+ // src/api/mustache/types.ts
2728
+ var PRIMITIVE_TYPES = [
2729
+ "boolean",
2730
+ "instant",
2731
+ "time",
2732
+ "date",
2733
+ "dateTime",
2734
+ "decimal",
2735
+ "integer",
2736
+ "unsignedInt",
2737
+ "positiveInt",
2738
+ "integer64",
2739
+ "base64Binary",
2740
+ "uri",
2741
+ "url",
2742
+ "canonical",
2743
+ "oid",
2744
+ "uuid",
2745
+ "string",
2746
+ "code",
2747
+ "markdown",
2748
+ "id",
2749
+ "xhtml"
2750
+ ];
2751
+
2752
+ // src/api/mustache/generator/ViewModelFactory.ts
2753
+ var ViewModelFactory = class {
2754
+ constructor(tsIndex, nameGenerator, filterPred) {
2755
+ this.tsIndex = tsIndex;
2756
+ this.nameGenerator = nameGenerator;
2757
+ this.filterPred = filterPred;
2758
+ }
2759
+ arrayMixinProvider = new ListElementInformationMixinProvider();
2760
+ createUtility() {
2761
+ return this._createForRoot();
2762
+ }
2763
+ createComplexType(typeRef, cache = { resourcesByUri: {}, complexTypesByUri: {} }) {
2764
+ const base = this._createForComplexType(typeRef, cache);
2765
+ const parents = this._createParentsFor(base.schema, cache);
2766
+ const children = this._createChildrenFor(typeRef, cache);
2767
+ const inheritedFields = parents.flatMap((p) => p.fields);
2768
+ return this.arrayMixinProvider.apply({
2769
+ ...this._createForRoot(),
2770
+ ...base,
2771
+ parents,
2772
+ children,
2773
+ inheritedFields,
2774
+ allFields: [...base.fields, ...parents.flatMap((p) => p.fields)],
2775
+ hasChildren: children.length > 0,
2776
+ hasParents: parents.length > 0,
2777
+ hasInheritedFields: inheritedFields.length > 0
2778
+ });
1602
2779
  }
1603
- dry_warn(message) {
1604
- if (!this.dryWarnSet.has(message)) {
1605
- this.warn(message);
1606
- this.dryWarnSet.add(message);
2780
+ createResource(typeRef, cache = { resourcesByUri: {}, complexTypesByUri: {} }) {
2781
+ const base = this._createForResource(typeRef, cache);
2782
+ const parents = this._createParentsFor(base.schema, cache);
2783
+ const children = this._createChildrenFor(typeRef, cache);
2784
+ const inheritedFields = parents.flatMap((p) => p.fields);
2785
+ return this.arrayMixinProvider.apply({
2786
+ ...this._createForRoot(),
2787
+ ...base,
2788
+ parents,
2789
+ children,
2790
+ inheritedFields,
2791
+ allFields: [...base.fields, ...inheritedFields],
2792
+ hasChildren: children.length > 0,
2793
+ hasParents: parents.length > 0,
2794
+ hasInheritedFields: inheritedFields.length > 0
2795
+ });
2796
+ }
2797
+ _createFor(typeRef, cache, nestedIn) {
2798
+ if (typeRef.kind === "complex-type") {
2799
+ return this._createForComplexType(typeRef, cache, nestedIn);
2800
+ }
2801
+ if (typeRef.kind === "resource") {
2802
+ return this._createForResource(typeRef, cache, nestedIn);
1607
2803
  }
2804
+ throw new Error(`Unknown type ${typeRef.kind}`);
1608
2805
  }
1609
- /**
1610
- * Info message with info icon
1611
- */
1612
- info(message) {
1613
- this.tryWriteToConsole(1 /* INFO */, this.formatMessage("i", message, pc.blue));
2806
+ _createForComplexType(typeRef, cache, nestedIn) {
2807
+ const type = this.tsIndex.resolve(typeRef);
2808
+ if (!type) {
2809
+ throw new Error(`ComplexType ${typeRef.name} not found`);
2810
+ }
2811
+ if (!Object.hasOwn(cache.complexTypesByUri, type.identifier.url)) {
2812
+ cache.complexTypesByUri[type.identifier.url] = this._createTypeViewModel(type, cache, nestedIn);
2813
+ }
2814
+ const res = cache.complexTypesByUri[type.identifier.url];
2815
+ if (!res) throw new Error(`ComplexType ${typeRef.name} not found`);
2816
+ return res;
1614
2817
  }
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));
2818
+ _createForResource(typeRef, cache, nestedIn) {
2819
+ const type = this.tsIndex.resolve(typeRef);
2820
+ if (!type) {
2821
+ throw new Error(`Resource ${typeRef.name} not found`);
1621
2822
  }
2823
+ if (!Object.hasOwn(cache.resourcesByUri, type.identifier.url)) {
2824
+ cache.resourcesByUri[type.identifier.url] = this._createTypeViewModel(type, cache, nestedIn);
2825
+ }
2826
+ const res = cache.resourcesByUri[type.identifier.url];
2827
+ if (!res) throw new Error(`Resource ${typeRef.name} not found`);
2828
+ return res;
1622
2829
  }
1623
- /**
1624
- * Step message with rocket
1625
- */
1626
- step(message) {
1627
- this.tryWriteToConsole(1 /* INFO */, this.formatMessage("\u{1F680}", message, pc.cyan));
2830
+ _createChildrenFor(typeRef, cache, nestedIn) {
2831
+ if (isComplexTypeIdentifier(typeRef)) {
2832
+ return this.tsIndex.resourceChildren(typeRef).filter(isComplexTypeIdentifier).filter(this.filterPred).map((childRef) => this._createFor(childRef, cache, nestedIn));
2833
+ }
2834
+ if (isResourceIdentifier(typeRef)) {
2835
+ return this.tsIndex.resourceChildren(typeRef).filter(isResourceIdentifier).filter(this.filterPred).map((childRef) => this._createFor(childRef, cache, nestedIn));
2836
+ }
2837
+ return [];
2838
+ }
2839
+ _createParentsFor(base, cache) {
2840
+ const parents = [];
2841
+ let parentRef = "base" in base ? base.base : void 0;
2842
+ while (parentRef) {
2843
+ parents.push(this._createFor(parentRef, cache, void 0));
2844
+ const parent = this.tsIndex.resolve(parentRef);
2845
+ parentRef = parent && "base" in parent ? parent.base : void 0;
2846
+ }
2847
+ return parents;
1628
2848
  }
1629
- /**
1630
- * Progress message with clock
1631
- */
1632
- progress(message) {
1633
- this.tryWriteToConsole(1 /* INFO */, this.formatMessage("\u23F3", message, pc.blue));
2849
+ _createForNestedType(nested, cache, nestedIn) {
2850
+ const base = this._createTypeViewModel(nested, cache, nestedIn);
2851
+ const parents = this._createParentsFor(nested, cache);
2852
+ const children = this._createChildrenFor(nested.identifier, cache, nestedIn);
2853
+ const inheritedFields = parents.flatMap((p) => p.fields);
2854
+ return {
2855
+ ...base,
2856
+ parents,
2857
+ children,
2858
+ inheritedFields,
2859
+ allFields: [...base.fields, ...inheritedFields],
2860
+ hasChildren: children.length > 0,
2861
+ hasParents: parents.length > 0,
2862
+ hasInheritedFields: inheritedFields.length > 0
2863
+ };
1634
2864
  }
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)}`);
2865
+ _createTypeViewModel(schema, cache, nestedIn) {
2866
+ const fields = Object.entries(("fields" in schema ? schema.fields : {}) ?? {});
2867
+ const nestedComplexTypes = this._collectNestedComplex(schema, cache);
2868
+ const nestedEnums = this._collectNestedEnums(fields);
2869
+ const dependencies = this._collectDependencies(schema);
2870
+ const name = {
2871
+ name: schema.identifier.name,
2872
+ saveName: this.nameGenerator.generateType(schema)
2873
+ };
2874
+ return {
2875
+ nestedComplexTypes,
2876
+ nestedEnums,
2877
+ dependencies,
2878
+ isNested: !!nestedIn,
2879
+ schema,
2880
+ ...name,
2881
+ isResource: this._createIsResource(schema.identifier),
2882
+ isComplexType: this._createIsComplexType(schema.identifier),
2883
+ hasFields: fields.length > 0,
2884
+ hasNestedComplexTypes: nestedComplexTypes.length > 0,
2885
+ hasNestedEnums: nestedEnums.length > 0,
2886
+ fields: fields.filter(([_fieldName, fieldSchema]) => !!fieldSchema.type).sort((a, b) => a[0].localeCompare(b[0])).map(([fieldName, fieldSchema]) => {
2887
+ return {
2888
+ owner: name,
2889
+ schema: fieldSchema,
2890
+ name: fieldName,
2891
+ saveName: this.nameGenerator.generateField(fieldName),
2892
+ typeName: this.nameGenerator.generateFieldType(fieldSchema),
2893
+ isArray: fieldSchema.array,
2894
+ isRequired: fieldSchema.required,
2895
+ isEnum: !!fieldSchema.enum,
2896
+ isSizeConstrained: fieldSchema.min !== void 0 || fieldSchema.max !== void 0,
2897
+ min: fieldSchema.min,
2898
+ max: fieldSchema.max,
2899
+ isResource: this._createIsResource(fieldSchema.type),
2900
+ isComplexType: this._createIsComplexType(fieldSchema.type),
2901
+ isPrimitive: this._createIsPrimitiveType(fieldSchema.type),
2902
+ isCode: fieldSchema.type?.name === "code",
2903
+ isIdentifier: fieldSchema.type?.name === "Identifier",
2904
+ isReference: fieldSchema.type?.name === "Reference"
2905
+ };
2906
+ })
2907
+ };
1642
2908
  }
1643
- /**
1644
- * Dimmed/gray text for less important info
1645
- */
1646
- dim(message) {
1647
- this.plain(message, pc.gray);
2909
+ _collectDependencies(schema) {
2910
+ const dependencies = {
2911
+ resources: [],
2912
+ complexTypes: []
2913
+ };
2914
+ if ("dependencies" in schema && schema.dependencies) {
2915
+ schema.dependencies.filter((dependency) => dependency.kind === "complex-type").map((dependency) => ({ name: dependency.name, saveName: this.nameGenerator.generateType(dependency) })).forEach((dependency) => {
2916
+ dependencies.complexTypes.push(dependency);
2917
+ });
2918
+ schema.dependencies.filter((dependency) => dependency.kind === "resource").map((dependency) => ({ name: dependency.name, saveName: this.nameGenerator.generateType(dependency) })).forEach((dependency) => {
2919
+ dependencies.resources.push(dependency);
2920
+ });
2921
+ }
2922
+ if ("nested" in schema && schema.nested) {
2923
+ schema.nested.map((nested) => this._collectDependencies(nested)).forEach((d) => {
2924
+ d.complexTypes.filter(
2925
+ (complexType) => !dependencies.complexTypes.some((dependency) => dependency.name === complexType.name)
2926
+ ).forEach((complexType) => {
2927
+ dependencies.complexTypes.push(complexType);
2928
+ });
2929
+ d.resources.filter(
2930
+ (resource) => !dependencies.resources.some((dependency) => dependency.name === resource.name)
2931
+ ).forEach((resource) => {
2932
+ dependencies.resources.push(resource);
2933
+ });
2934
+ });
2935
+ }
2936
+ return dependencies;
1648
2937
  }
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
2938
+ _createIsResource(typeRef) {
2939
+ if (typeRef.kind !== "resource") {
2940
+ return false;
2941
+ }
2942
+ return Object.fromEntries(
2943
+ this.tsIndex.collectResources().map((e) => e.identifier).map((resourceRef) => [
2944
+ `is${resourceRef.name.charAt(0).toUpperCase() + resourceRef.name.slice(1)}`,
2945
+ resourceRef.url === typeRef.url
2946
+ ])
2947
+ );
2948
+ }
2949
+ _createIsComplexType(typeRef) {
2950
+ if (typeRef.kind !== "complex-type" && typeRef.kind !== "nested") {
2951
+ return false;
2952
+ }
2953
+ return Object.fromEntries(
2954
+ this.tsIndex.collectComplexTypes().map((e) => e.identifier).map((complexTypeRef) => [
2955
+ `is${complexTypeRef.name.charAt(0).toUpperCase() + complexTypeRef.name.slice(1)}`,
2956
+ complexTypeRef.url === typeRef.url
2957
+ ])
2958
+ );
2959
+ }
2960
+ _createIsPrimitiveType(typeRef) {
2961
+ if (typeRef.kind !== "primitive-type") {
2962
+ return false;
2963
+ }
2964
+ return Object.fromEntries(
2965
+ PRIMITIVE_TYPES.map((type) => [`is${type.charAt(0).toUpperCase()}${type.slice(1)}`, typeRef.name === type])
2966
+ );
2967
+ }
2968
+ _collectNestedComplex(schema, cache) {
2969
+ const nested = [];
2970
+ if ("nested" in schema && schema.nested) {
2971
+ schema.nested.map((nested2) => this._createForNestedType(nested2, cache, schema)).forEach((n) => {
2972
+ nested.push(n);
2973
+ });
2974
+ }
2975
+ return nested;
2976
+ }
2977
+ _collectNestedEnums(fields) {
2978
+ const nestedEnumValues = {};
2979
+ fields.forEach(([fieldName, fieldSchema]) => {
2980
+ if ("enum" in fieldSchema && fieldSchema.enum) {
2981
+ const name = ("binding" in fieldSchema && fieldSchema.binding?.name) ?? fieldName;
2982
+ if (typeof name === "string") {
2983
+ nestedEnumValues[name] = nestedEnumValues[name] ?? /* @__PURE__ */ new Set();
2984
+ fieldSchema.enum?.forEach(nestedEnumValues[name].add.bind(nestedEnumValues[name]));
2985
+ }
2986
+ }
1656
2987
  });
2988
+ return Object.entries(nestedEnumValues).map(([name, values]) => ({
2989
+ name,
2990
+ saveName: this.nameGenerator.generateEnumType(name),
2991
+ values: Array.from(values).map((value) => ({
2992
+ name: value,
2993
+ saveName: this.nameGenerator.generateEnumValue(value)
2994
+ }))
2995
+ }));
1657
2996
  }
1658
- /**
1659
- * Update options
1660
- */
1661
- configure(options) {
1662
- this.options = { ...this.options, ...options };
2997
+ _createForRoot() {
2998
+ return this.arrayMixinProvider.apply({
2999
+ complexTypes: this.tsIndex.collectComplexTypes().map((e) => e.identifier).map((typeRef) => ({
3000
+ name: typeRef.name,
3001
+ saveName: this.nameGenerator.generateType(typeRef)
3002
+ })),
3003
+ resources: this.tsIndex.collectResources().map((e) => e.identifier).map((typeRef) => ({
3004
+ name: typeRef.name,
3005
+ saveName: this.nameGenerator.generateType(typeRef)
3006
+ }))
3007
+ });
1663
3008
  }
1664
3009
  };
1665
- new CodegenLogger();
1666
- function createLogger(options = {}) {
1667
- return new CodegenLogger(options);
1668
- }
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."
3010
+ function loadMustacheGeneratorConfig(templatePath, logger) {
3011
+ const filePath = Path.resolve(templatePath, "config.json");
3012
+ try {
3013
+ const raw = fs.readFileSync(filePath, "utf-8");
3014
+ const parsed = JSON.parse(raw);
3015
+ if (parsed && typeof parsed === "object") {
3016
+ return parsed;
3017
+ }
3018
+ } catch (_e) {
1684
3019
  }
3020
+ return {};
3021
+ }
3022
+ var createGenerator = (templatePath, apiOpts) => {
3023
+ const defaultFileOpts = {
3024
+ debug: "OFF",
3025
+ hooks: {},
3026
+ meta: {},
3027
+ keywords: [],
3028
+ unsaveCharacterPattern: /[^a-zA-Z0-9]/g,
3029
+ nameTransformations: {
3030
+ common: [],
3031
+ enumValue: [],
3032
+ type: [],
3033
+ field: []
3034
+ },
3035
+ renderings: {
3036
+ utility: [],
3037
+ resource: [],
3038
+ complexType: []
3039
+ },
3040
+ shouldRunHooks: true,
3041
+ primitiveTypeMap: {}
3042
+ };
3043
+ const actualFileOpts = loadMustacheGeneratorConfig(templatePath);
3044
+ const mustacheOptions = {
3045
+ ...defaultFileOpts,
3046
+ ...apiOpts,
3047
+ ...actualFileOpts,
3048
+ sources: {
3049
+ staticSource: Path.resolve(templatePath, "static"),
3050
+ templateSource: Path.resolve(templatePath, "templates")
3051
+ }
3052
+ };
3053
+ return new MustacheGenerator(mustacheOptions);
1685
3054
  };
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;
3055
+ function runCommand(cmd, args = [], options = {}) {
3056
+ return new Promise((resolve6, reject) => {
3057
+ const child = spawn(cmd, args, {
3058
+ stdio: "inherit",
3059
+ ...options
3060
+ });
3061
+ child.on("error", reject);
3062
+ child.on("close", (code) => {
3063
+ if (code === 0) resolve6(code);
3064
+ else reject(new Error(`Prozess beendet mit Fehlercode ${code}`));
3065
+ });
3066
+ });
3067
+ }
3068
+ var MustacheGenerator = class extends FileSystemWriter {
3069
+ templateFileCache;
3070
+ nameGenerator;
3071
+ lambdaMixinProvider;
3072
+ debugMixinProvider;
3073
+ constructor(opts) {
3074
+ super(opts);
3075
+ this.nameGenerator = new NameGenerator(
3076
+ new Set(opts.keywords),
3077
+ opts.primitiveTypeMap,
3078
+ opts.nameTransformations,
3079
+ opts.unsaveCharacterPattern
3080
+ );
3081
+ this.templateFileCache = new TemplateFileCache(opts.sources.templateSource);
3082
+ this.lambdaMixinProvider = new LambdaMixinProvider(this.nameGenerator);
3083
+ this.debugMixinProvider = opts.debug !== "OFF" ? new DebugMixinProvider(opts.debug) : void 0;
3084
+ }
3085
+ async generate(tsIndex) {
3086
+ const modelFactory = new ViewModelFactory(tsIndex, this.nameGenerator, () => true);
3087
+ const cache = {
3088
+ resourcesByUri: {},
3089
+ complexTypesByUri: {}
3090
+ };
3091
+ 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));
3092
+ 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));
3093
+ this._renderUtility(modelFactory.createUtility());
3094
+ this.copyStaticFiles();
3095
+ if (this.opts.shouldRunHooks) {
3096
+ await this._runHooks(this.opts.hooks.afterGenerate);
1693
3097
  }
1694
- fhirSchemas.push(...await transformFhirSchema(register, fhirSchema, logger));
3098
+ return;
1695
3099
  }
1696
- for (const vsSchema of register.allVs()) {
1697
- fhirSchemas.push(await transformValueSet(register, vsSchema));
3100
+ copyStaticFiles() {
3101
+ const staticDir = Path.resolve(this.opts.sources.staticSource);
3102
+ if (!staticDir) {
3103
+ throw new Error("staticDir must be set in subclass.");
3104
+ }
3105
+ fs.cpSync(staticDir, this.opts.outputDir, { recursive: true });
3106
+ }
3107
+ async _runHooks(hooks) {
3108
+ for (const hook of hooks ?? []) {
3109
+ console.info(`Running hook (${this.opts.outputDir}): ${hook.cmd} ${hook.args?.join(" ")}`);
3110
+ await runCommand(hook.cmd, hook.args ?? [], {
3111
+ cwd: this.opts.outputDir
3112
+ });
3113
+ console.info(`Completed hook: ${hook.cmd} ${hook.args?.join(" ")}`);
3114
+ }
3115
+ }
3116
+ _checkRenderingFilter(model, rendering) {
3117
+ if (!rendering.filter?.whitelist?.length && !rendering.filter?.blacklist?.length) {
3118
+ return true;
3119
+ }
3120
+ if ((rendering.filter?.blacklist ?? []).find((v) => model.name.match(v))) {
3121
+ return false;
3122
+ }
3123
+ if ((rendering.filter?.whitelist ?? []).find((v) => model.name.match(v))) {
3124
+ return true;
3125
+ }
3126
+ return !rendering.filter.whitelist?.length;
3127
+ }
3128
+ _renderUtility(model) {
3129
+ this.opts.renderings.utility.forEach((rendering) => {
3130
+ this.cd(rendering.path, () => {
3131
+ this.cat(rendering.fileNameFormat, () => {
3132
+ this.write(this._render(model, rendering));
3133
+ });
3134
+ });
3135
+ });
3136
+ }
3137
+ _renderResource(model) {
3138
+ this.opts.renderings.resource.filter((rendering) => this._checkRenderingFilter(model, rendering)).forEach((rendering) => {
3139
+ this.cd(rendering.path, () => {
3140
+ this.cat(this._calculateFilename(model, rendering), () => {
3141
+ this.write(this._render(model, rendering));
3142
+ });
3143
+ });
3144
+ });
3145
+ }
3146
+ _renderComplexType(model) {
3147
+ this.opts.renderings.complexType.filter((rendering) => this._checkRenderingFilter(model, rendering)).forEach((rendering) => {
3148
+ this.cd(rendering.path, () => {
3149
+ this.cat(this._calculateFilename(model, rendering), () => {
3150
+ this.write(this._render(model, rendering));
3151
+ });
3152
+ });
3153
+ });
3154
+ }
3155
+ _calculateFilename(model, rendering) {
3156
+ return util.format(rendering.fileNameFormat, model.saveName);
3157
+ }
3158
+ _render(model, rendering) {
3159
+ let view = this.lambdaMixinProvider.apply({
3160
+ meta: {
3161
+ timestamp: this.opts.meta.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
3162
+ generator: this.opts.meta.generator ?? "@atomic-ehr/codegen mustache generator"
3163
+ },
3164
+ model,
3165
+ properties: rendering.properties ?? {}
3166
+ });
3167
+ if (this.debugMixinProvider) {
3168
+ view = this.debugMixinProvider.apply(view);
3169
+ }
3170
+ return Mustache.render(
3171
+ this.templateFileCache.read(rendering),
3172
+ view,
3173
+ (partialName) => this.templateFileCache.readTemplate(partialName)
3174
+ );
1698
3175
  }
1699
- return fhirSchemas;
1700
3176
  };
1701
3177
 
1702
3178
  // src/api/writer-generator/typescript.ts
@@ -1741,7 +3217,7 @@ var tsModuleFileName = (id) => {
1741
3217
  var tsModulePath = (id) => {
1742
3218
  return `${tsFhirPackageDir(id.package)}/${tsModuleName(id)}`;
1743
3219
  };
1744
- var canonicalToName2 = (canonical, dropFragment = true) => {
3220
+ var canonicalToName3 = (canonical, dropFragment = true) => {
1745
3221
  if (!canonical) return void 0;
1746
3222
  const localName = extractNameFromCanonical(canonical, dropFragment);
1747
3223
  if (!localName) return void 0;
@@ -1750,7 +3226,7 @@ var canonicalToName2 = (canonical, dropFragment = true) => {
1750
3226
  var tsResourceName = (id) => {
1751
3227
  if (id.kind === "nested") {
1752
3228
  const url = id.url;
1753
- const path = canonicalToName2(url, false);
3229
+ const path = canonicalToName3(url, false);
1754
3230
  if (!path) return "";
1755
3231
  const [resourceName, fragment] = path.split("#");
1756
3232
  const name = uppercaseFirstLetterOfEach((fragment ?? "").split(".")).join("");
@@ -1816,7 +3292,7 @@ var TypeScript = class extends Writer {
1816
3292
  });
1817
3293
  } else if (isNestedIdentifier(dep)) {
1818
3294
  const ndep = { ...dep };
1819
- ndep.name = canonicalToName2(dep.url);
3295
+ ndep.name = canonicalToName3(dep.url);
1820
3296
  imports.push({
1821
3297
  tsPackage: `../${tsModulePath(ndep)}`,
1822
3298
  name: tsResourceName(dep),
@@ -1869,7 +3345,7 @@ var TypeScript = class extends Writer {
1869
3345
  name = tsResourceName(schema.identifier);
1870
3346
  }
1871
3347
  let extendsClause;
1872
- if (schema.base) extendsClause = `extends ${canonicalToName2(schema.base.url)}`;
3348
+ if (schema.base) extendsClause = `extends ${canonicalToName3(schema.base.url)}`;
1873
3349
  this.debugComment(schema.identifier);
1874
3350
  if (!schema.fields && !extendsClause && !isResourceTypeSchema(schema)) {
1875
3351
  this.lineSM(`export type ${name} = object`);
@@ -2121,7 +3597,13 @@ var TypeScript = class extends Writer {
2121
3597
  ...this.opts.generateProfile ? tsIndex.collectProfiles().filter((p) => tsIndex.isWithMetaField(p)) : []
2122
3598
  ];
2123
3599
  const grouped = groupByPackages(typesToGenerate);
3600
+ const treeShakeReport = tsIndex.treeShakeReport();
2124
3601
  this.cd("/", () => {
3602
+ if (treeShakeReport) {
3603
+ this.cat("README.md", () => {
3604
+ this.write(rootTreeShakeReadme(treeShakeReport));
3605
+ });
3606
+ }
2125
3607
  for (const [packageName, packageSchemas] of Object.entries(grouped)) {
2126
3608
  const tsPackageDir = tsFhirPackageDir(packageName);
2127
3609
  this.cd(tsPackageDir, () => {
@@ -2129,6 +3611,11 @@ var TypeScript = class extends Writer {
2129
3611
  this.generateResourceModule(tsIndex, schema);
2130
3612
  }
2131
3613
  this.generateFhirPackageIndexFile(packageSchemas);
3614
+ if (treeShakeReport) {
3615
+ this.cat("README.md", () => {
3616
+ this.write(packageTreeShakeReadme(treeShakeReport, packageName));
3617
+ });
3618
+ }
2132
3619
  });
2133
3620
  }
2134
3621
  });
@@ -2136,6 +3623,25 @@ var TypeScript = class extends Writer {
2136
3623
  };
2137
3624
 
2138
3625
  // src/api/builder.ts
3626
+ var prettyReport = (report) => {
3627
+ const { success, filesGenerated, errors, warnings, duration } = report;
3628
+ const errorsStr = errors.length > 0 ? `Errors: ${errors.join(", ")}` : void 0;
3629
+ const warningsStr = warnings.length > 0 ? `Warnings: ${warnings.join(", ")}` : void 0;
3630
+ let allLoc = 0;
3631
+ const files = Object.entries(filesGenerated).map(([path, content]) => {
3632
+ const loc = content.split("\n").length;
3633
+ allLoc += loc;
3634
+ return ` - ${path} (${loc} loc)`;
3635
+ }).join("\n");
3636
+ return [
3637
+ `Generated files (${Math.round(allLoc / 1e3)} kloc):`,
3638
+ files,
3639
+ errorsStr,
3640
+ warningsStr,
3641
+ `Duration: ${Math.round(duration)}ms`,
3642
+ `Status: ${success ? "\u{1F7E9} Success" : "\u{1F7E5} Failure"}`
3643
+ ].filter((e) => e).join("\n");
3644
+ };
2139
3645
  var normalizeFileName = (str) => {
2140
3646
  const res = str.replace(/[^a-zA-Z0-9\-_.@#()]/g, "");
2141
3647
  if (res.length === 0) return "unknown";
@@ -2176,7 +3682,7 @@ var writeTypeSchemasToSeparateFiles = async (typeSchemas, outputDir, logger) =>
2176
3682
  const pkgPath = normalizeFileName(packageMetaToFhir(pkg));
2177
3683
  const name = normalizeFileName(`${ts.identifier.name}(${extractNameFromCanonical(ts.identifier.url)})`);
2178
3684
  const json = JSON.stringify(ts, null, 2);
2179
- const baseName = Path4.join(outputDir, pkgPath, name);
3685
+ const baseName = Path.join(outputDir, pkgPath, name);
2180
3686
  if (!files[baseName]) files[baseName] = [];
2181
3687
  if (!files[baseName]?.some((e) => e === json)) {
2182
3688
  files[baseName].push(json);
@@ -2191,7 +3697,7 @@ var writeTypeSchemasToSeparateFiles = async (typeSchemas, outputDir, logger) =>
2191
3697
  } else {
2192
3698
  fullName = `${baseName}-${index}.typeschema.json`;
2193
3699
  }
2194
- await afs2.mkdir(Path4.dirname(fullName), { recursive: true });
3700
+ await afs2.mkdir(Path.dirname(fullName), { recursive: true });
2195
3701
  await afs2.writeFile(fullName, json);
2196
3702
  })
2197
3703
  );
@@ -2199,7 +3705,7 @@ var writeTypeSchemasToSeparateFiles = async (typeSchemas, outputDir, logger) =>
2199
3705
  };
2200
3706
  var writeTypeSchemasToSingleFile = async (typeSchemas, outputFile, logger) => {
2201
3707
  logger.info(`Writing TypeSchema files to: ${outputFile}`);
2202
- await afs2.mkdir(Path4.dirname(outputFile), { recursive: true });
3708
+ await afs2.mkdir(Path.dirname(outputFile), { recursive: true });
2203
3709
  logger.info(`Writing TypeSchemas to one file ${outputFile}...`);
2204
3710
  for (const ts of typeSchemas) {
2205
3711
  const json = JSON.stringify(ts, null, 2);
@@ -2210,7 +3716,7 @@ var writeTypeSchemasToSingleFile = async (typeSchemas, outputFile, logger) => {
2210
3716
  var tryWriteTypeSchema = async (typeSchemas, opts, logger) => {
2211
3717
  if (!opts.typeSchemaOutputDir) return;
2212
3718
  try {
2213
- if (Path4.extname(opts.typeSchemaOutputDir) === ".ndjson") {
3719
+ if (Path.extname(opts.typeSchemaOutputDir) === ".ndjson") {
2214
3720
  await writeTypeSchemasToSingleFile(typeSchemas, opts.typeSchemaOutputDir, logger);
2215
3721
  } else {
2216
3722
  await writeTypeSchemasToSeparateFiles(typeSchemas, opts.typeSchemaOutputDir, logger);
@@ -2227,12 +3733,13 @@ var APIBuilder = class {
2227
3733
  generators = /* @__PURE__ */ new Map();
2228
3734
  logger;
2229
3735
  packages = [];
3736
+ localStructurePackages = [];
3737
+ localTgzArchives = [];
2230
3738
  progressCallback;
2231
3739
  typeSchemaConfig;
2232
3740
  constructor(options = {}) {
2233
3741
  this.options = {
2234
3742
  outputDir: options.outputDir || "./generated",
2235
- verbose: options.verbose ?? false,
2236
3743
  overwrite: options.overwrite ?? true,
2237
3744
  cache: options.cache ?? true,
2238
3745
  cleanOutput: options.cleanOutput ?? true,
@@ -2241,12 +3748,13 @@ var APIBuilder = class {
2241
3748
  throwException: options.throwException || false,
2242
3749
  typeSchemaOutputDir: options.typeSchemaOutputDir,
2243
3750
  exportTypeTree: options.exportTypeTree,
2244
- treeShake: options.treeShake
3751
+ treeShake: options.treeShake,
3752
+ registry: options.registry
2245
3753
  };
2246
3754
  this.typeSchemaConfig = options.typeSchemaConfig;
2247
3755
  this.logger = options.logger || createLogger({
2248
- verbose: this.options.verbose,
2249
- prefix: "API"
3756
+ prefix: "API",
3757
+ level: options.logLevel
2250
3758
  });
2251
3759
  }
2252
3760
  fromPackage(packageName, version) {
@@ -2258,6 +3766,22 @@ var APIBuilder = class {
2258
3766
  this.packages.push(packageRef);
2259
3767
  return this;
2260
3768
  }
3769
+ /**
3770
+ * Set a custom FHIR package registry URL
3771
+ * @param url The registry URL (default: https://fs.get-ig.org/pkgs/)
3772
+ */
3773
+ registry(url) {
3774
+ this.options.registry = url;
3775
+ return this;
3776
+ }
3777
+ localStructureDefinitions(config) {
3778
+ this.localStructurePackages.push(config);
3779
+ return this;
3780
+ }
3781
+ localTgzPackage(archivePath) {
3782
+ this.localTgzArchives.push(Path.resolve(archivePath));
3783
+ return this;
3784
+ }
2261
3785
  fromSchemas(schemas) {
2262
3786
  this.logger.debug(`Adding ${schemas.length} TypeSchemas to generation`);
2263
3787
  this.schemas = [...this.schemas, ...schemas];
@@ -2266,7 +3790,7 @@ var APIBuilder = class {
2266
3790
  typescript(userOpts) {
2267
3791
  const defaultWriterOpts = {
2268
3792
  logger: this.logger,
2269
- outputDir: Path4.join(this.options.outputDir, "/types"),
3793
+ outputDir: Path.join(this.options.outputDir, "/types"),
2270
3794
  tabSize: 4,
2271
3795
  withDebugComment: false,
2272
3796
  commentLinePrefix: "//",
@@ -2286,19 +3810,67 @@ var APIBuilder = class {
2286
3810
  this.logger.debug(`Configured TypeScript generator (${JSON.stringify(opts, void 0, 2)})`);
2287
3811
  return this;
2288
3812
  }
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);
3813
+ python(userOptions) {
3814
+ const defaultWriterOpts = {
3815
+ logger: this.logger,
3816
+ outputDir: this.options.outputDir,
3817
+ tabSize: 4,
3818
+ withDebugComment: false,
3819
+ commentLinePrefix: "#"
3820
+ };
3821
+ const defaultPyOpts = {
3822
+ ...defaultWriterOpts,
3823
+ rootPackageName: "fhir_types",
3824
+ fieldFormat: "snake_case"
3825
+ };
3826
+ const opts = {
3827
+ ...defaultPyOpts,
3828
+ ...Object.fromEntries(Object.entries(userOptions).filter(([_, v]) => v !== void 0))
3829
+ };
3830
+ const generator = new Python(opts);
3831
+ this.generators.set("python", generator);
3832
+ this.logger.debug(`Configured python generator`);
3833
+ return this;
3834
+ }
3835
+ mustache(templatePath, userOpts) {
3836
+ const defaultWriterOpts = {
3837
+ logger: this.logger,
3838
+ outputDir: this.options.outputDir
3839
+ };
3840
+ const defaultMustacheOpts = {
3841
+ meta: {
3842
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3843
+ generator: "atomic-codegen"
3844
+ }
3845
+ };
3846
+ const opts = {
3847
+ ...defaultWriterOpts,
3848
+ ...defaultMustacheOpts,
3849
+ ...userOpts
3850
+ };
3851
+ const generator = createGenerator(templatePath, opts);
3852
+ this.generators.set(`mustache[${templatePath}]`, generator);
3853
+ this.logger.debug(`Configured TypeScript generator (${JSON.stringify(opts, void 0, 2)})`);
3854
+ return this;
3855
+ }
3856
+ csharp(userOptions) {
3857
+ const defaultWriterOpts = {
3858
+ logger: this.logger,
3859
+ outputDir: Path.join(this.options.outputDir, "/types"),
3860
+ tabSize: 4,
3861
+ withDebugComment: false,
3862
+ commentLinePrefix: "//"
3863
+ };
3864
+ const defaultCSharpOpts = {
3865
+ ...defaultWriterOpts,
3866
+ rootNamespace: "Fhir.Types"
3867
+ };
3868
+ const opts = {
3869
+ ...defaultCSharpOpts,
3870
+ ...Object.fromEntries(Object.entries(userOptions).filter(([_, v]) => v !== void 0))
3871
+ };
3872
+ const generator = new CSharp(opts);
3873
+ this.generators.set("csharp", generator);
2302
3874
  this.logger.debug(`Configured C# generator`);
2303
3875
  return this;
2304
3876
  }
@@ -2320,9 +3892,8 @@ var APIBuilder = class {
2320
3892
  }
2321
3893
  return this;
2322
3894
  }
2323
- verbose(enabled = true) {
2324
- this.options.verbose = enabled;
2325
- this.logger?.configure({ verbose: enabled });
3895
+ setLogLevel(level) {
3896
+ this.logger?.setLevel(typeof level === "string" ? parseLogLevel(level) : level);
2326
3897
  return this;
2327
3898
  }
2328
3899
  throwException(enabled = true) {
@@ -2350,7 +3921,7 @@ var APIBuilder = class {
2350
3921
  const result = {
2351
3922
  success: false,
2352
3923
  outputDir: this.options.outputDir,
2353
- filesGenerated: [],
3924
+ filesGenerated: {},
2354
3925
  errors: [],
2355
3926
  warnings: [],
2356
3927
  duration: 0
@@ -2359,10 +3930,28 @@ var APIBuilder = class {
2359
3930
  try {
2360
3931
  if (this.options.cleanOutput) cleanup(this.options, this.logger);
2361
3932
  this.logger.info("Initialize Canonical Manager");
2362
- const manager = CanonicalManager({
3933
+ const manager = this.options.manager || CanonicalManager({
2363
3934
  packages: this.packages,
2364
- workingDir: ".codegen-cache/canonical-manager-cache"
3935
+ workingDir: ".codegen-cache/canonical-manager-cache",
3936
+ registry: this.options.registry || void 0
2365
3937
  });
3938
+ if (this.localStructurePackages.length > 0) {
3939
+ for (const config of this.localStructurePackages) {
3940
+ this.logger.info(
3941
+ `Registering local StructureDefinitions for ${config.package.name}@${config.package.version}`
3942
+ );
3943
+ await manager.addLocalPackage({
3944
+ name: config.package.name,
3945
+ version: config.package.version,
3946
+ path: config.path,
3947
+ dependencies: config.dependencies?.map((dep) => packageMetaToNpm(dep))
3948
+ });
3949
+ }
3950
+ }
3951
+ for (const archivePath of this.localTgzArchives) {
3952
+ this.logger.info(`Registering local tgz package: ${archivePath}`);
3953
+ await manager.addTgzPackage({ archivePath });
3954
+ }
2366
3955
  const ref2meta = await manager.init();
2367
3956
  const packageMetas = Object.values(ref2meta);
2368
3957
  const register = await registerFromManager(manager, {
@@ -2370,13 +3959,13 @@ var APIBuilder = class {
2370
3959
  focusedPackages: packageMetas
2371
3960
  });
2372
3961
  const typeSchemas = await generateTypeSchemas(register, this.logger);
2373
- await tryWriteTypeSchema(typeSchemas, this.options, this.logger);
2374
3962
  const tsIndexOpts = {
2375
3963
  resolutionTree: register.resolutionTree(),
2376
3964
  logger: this.logger
2377
3965
  };
2378
3966
  let tsIndex = mkTypeSchemaIndex(typeSchemas, tsIndexOpts);
2379
3967
  if (this.options.treeShake) tsIndex = treeShake(tsIndex, this.options.treeShake, tsIndexOpts);
3968
+ await tryWriteTypeSchema(tsIndex.schemas, this.options, this.logger);
2380
3969
  if (this.options.exportTypeTree) await tsIndex.exportTree(this.options.exportTypeTree);
2381
3970
  this.logger.debug(`Executing ${this.generators.size} generators`);
2382
3971
  await this.executeGenerators(result, tsIndex);
@@ -2401,6 +3990,9 @@ var APIBuilder = class {
2401
3990
  this.schemas = [];
2402
3991
  this.generators.clear();
2403
3992
  this.progressCallback = void 0;
3993
+ this.packages = [];
3994
+ this.localStructurePackages = [];
3995
+ this.localTgzArchives = [];
2404
3996
  return this;
2405
3997
  }
2406
3998
  /**
@@ -2421,7 +4013,9 @@ var APIBuilder = class {
2421
4013
  try {
2422
4014
  await generator.generate(tsIndex);
2423
4015
  const fileBuffer = generator.writtenFiles();
2424
- result.filesGenerated.push(...fileBuffer.map((e) => e.absPath));
4016
+ fileBuffer.forEach((buf) => {
4017
+ result.filesGenerated[buf.relPath] = buf.content;
4018
+ });
2425
4019
  this.logger.info(`Generating ${type} finished successfully`);
2426
4020
  } catch (error) {
2427
4021
  result.errors.push(
@@ -2439,6 +4033,7 @@ var DEFAULT_CONFIG = {
2439
4033
  validate: true,
2440
4034
  cache: true,
2441
4035
  cleanOutput: true,
4036
+ registry: "",
2442
4037
  typescript: {
2443
4038
  moduleFormat: "esm",
2444
4039
  generateIndex: true,
@@ -2945,6 +4540,6 @@ function defineConfig(config) {
2945
4540
  return config;
2946
4541
  }
2947
4542
 
2948
- export { APIBuilder, CONFIG_FILE_NAMES, ConfigLoader, ConfigValidator, DEFAULT_CONFIG, configLoader, defineConfig, isConfig, loadConfig };
4543
+ export { APIBuilder, CONFIG_FILE_NAMES, ConfigLoader, ConfigValidator, DEFAULT_CONFIG, LogLevel, configLoader, defineConfig, isConfig, loadConfig, prettyReport };
2949
4544
  //# sourceMappingURL=index.js.map
2950
4545
  //# sourceMappingURL=index.js.map