@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/LICENSE +21 -0
- package/README.md +210 -260
- package/assets/api/writer-generator/csharp/Client.cs +333 -0
- package/assets/api/writer-generator/csharp/Helper.cs +19 -0
- package/assets/api/writer-generator/python/requirements.txt +5 -0
- package/assets/api/writer-generator/python/resource_family_validator.py +92 -0
- package/dist/cli/index.js +15 -13
- package/dist/index.d.ts +120 -29
- package/dist/index.js +2172 -577
- package/dist/index.js.map +1 -1
- package/package.json +18 -12
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
|
|
6
|
-
import
|
|
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
|
|
11
|
-
import
|
|
12
|
-
import
|
|
14
|
+
import { spawn } from 'child_process';
|
|
15
|
+
import * as util from 'util';
|
|
16
|
+
import Mustache from 'mustache';
|
|
13
17
|
|
|
14
|
-
// src/
|
|
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("/") ?
|
|
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 =
|
|
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] = {
|
|
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.
|
|
492
|
-
...packages.map((pkg) => `${this.opts.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
583
|
-
|
|
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
|
-
|
|
588
|
-
const
|
|
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
|
-
|
|
594
|
-
|
|
595
|
-
|
|
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
|
-
|
|
613
|
-
};
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
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
|
|
636
|
-
for (const
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
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
|
-
|
|
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
|
|
655
|
-
const
|
|
656
|
-
const
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
if (!
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
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
|
|
689
|
-
|
|
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
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
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
|
-
|
|
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
|
|
709
|
-
|
|
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
|
|
712
|
-
const
|
|
713
|
-
|
|
714
|
-
|
|
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
|
|
718
|
-
const
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
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
|
-
|
|
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
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
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
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
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((
|
|
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((
|
|
959
|
-
if (parentPath.length === 0) return
|
|
960
|
-
if (!
|
|
961
|
-
let elem =
|
|
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((
|
|
974
|
-
if (parentPath.length === 0) return
|
|
975
|
-
if (!
|
|
976
|
-
let elem =
|
|
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
|
|
989
|
-
if (!
|
|
990
|
-
return mkIdentifier(
|
|
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
|
-
|
|
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
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
const
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
fields
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
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
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
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
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1325
|
-
if (
|
|
1326
|
-
|
|
1327
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
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
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
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
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
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
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
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
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
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
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
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
|
-
|
|
2618
|
+
name = this._replaceUnsaveChars(name);
|
|
1445
2619
|
}
|
|
1446
|
-
return
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
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
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
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
|
|
1461
|
-
}
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
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
|
-
|
|
1489
|
-
|
|
1490
|
-
deps[e.url] = e;
|
|
2643
|
+
if ("url" in schemaOrRefOrString) {
|
|
2644
|
+
return this._generateTypeFromTypeRef(schemaOrRefOrString);
|
|
1491
2645
|
}
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
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
|
-
|
|
1525
|
-
|
|
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
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1571
|
-
|
|
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
|
-
|
|
1574
|
-
|
|
1575
|
-
const logFn = _CodegenLogger.consoleLevelsMap[level] || console.log;
|
|
1576
|
-
logFn(formattedMessage);
|
|
2677
|
+
read(template) {
|
|
2678
|
+
return this.readTemplate(template.source);
|
|
1577
2679
|
}
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
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
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
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
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
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
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
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
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
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
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
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
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
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
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
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
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
const
|
|
1640
|
-
const
|
|
1641
|
-
|
|
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
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
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
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
return
|
|
1654
|
-
|
|
1655
|
-
|
|
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
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
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
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
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
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
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
|
-
|
|
3098
|
+
return;
|
|
1695
3099
|
}
|
|
1696
|
-
|
|
1697
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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 ${
|
|
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 =
|
|
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(
|
|
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(
|
|
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 (
|
|
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
|
-
|
|
2249
|
-
|
|
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:
|
|
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
|
-
|
|
2290
|
-
const
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
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
|
-
|
|
2324
|
-
this.
|
|
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
|
-
|
|
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
|