@atomic-ehr/codegen 0.0.1-canary.20251013100511.ff2ef29 → 0.0.1-canary.20251015064414.0fcee22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +24 -22
- package/dist/index.d.ts +6 -1
- package/dist/index.js +267 -182
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
|
|
2
2
|
import * as FS from '@atomic-ehr/fhirschema';
|
|
3
|
-
import { FHIRSchema, StructureDefinition } from '@atomic-ehr/fhirschema';
|
|
3
|
+
import { FHIRSchema, StructureDefinition, FHIRSchemaElement } from '@atomic-ehr/fhirschema';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* CodeGen Logger
|
|
@@ -315,6 +315,8 @@ type Register = {
|
|
|
315
315
|
resolveVs(canonicalUrl: CanonicalUrl): RichValueSet | undefined;
|
|
316
316
|
complexTypeDict(): Record<string, RichFHIRSchema>;
|
|
317
317
|
resolveAny(canonicalUrl: CanonicalUrl): any | undefined;
|
|
318
|
+
resolveElementSnapshot(fhirSchema: RichFHIRSchema, path: string[]): FHIRSchemaElement;
|
|
319
|
+
getAllElementKeys(elems: Record<string, FHIRSchemaElement>): string[];
|
|
318
320
|
} & ReturnType<typeof CanonicalManager>;
|
|
319
321
|
|
|
320
322
|
/**
|
|
@@ -828,6 +830,9 @@ declare class APIBuilder {
|
|
|
828
830
|
throwException(enabled?: boolean): APIBuilder;
|
|
829
831
|
cleanOutput(enabled?: boolean): APIBuilder;
|
|
830
832
|
writeTypeSchemas(target: string): this;
|
|
833
|
+
private static isIdenticalTo;
|
|
834
|
+
private writeTypeSchemasToSeparateFiles;
|
|
835
|
+
private writeTypeSchemasToSingleFile;
|
|
831
836
|
private tryWriteTypeSchema;
|
|
832
837
|
generate(): Promise<GenerationResult>;
|
|
833
838
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
|
|
2
|
-
import * as fhirschema from '@atomic-ehr/fhirschema';
|
|
3
1
|
import * as fs from 'fs';
|
|
4
2
|
import { existsSync, mkdirSync } from 'fs';
|
|
5
3
|
import * as afs from 'fs/promises';
|
|
6
4
|
import { readdir, stat, unlink, readFile, writeFile, access, mkdir, rm } from 'fs/promises';
|
|
7
5
|
import * as Path2 from 'path';
|
|
8
6
|
import { join, resolve, dirname, relative } from 'path';
|
|
7
|
+
import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
|
|
8
|
+
import * as fhirschema from '@atomic-ehr/fhirschema';
|
|
9
9
|
import pc from 'picocolors';
|
|
10
10
|
|
|
11
11
|
var __defProp = Object.defineProperty;
|
|
@@ -1446,122 +1446,6 @@ var isChoiceDeclarationField = (field) => {
|
|
|
1446
1446
|
return field.choices !== void 0;
|
|
1447
1447
|
};
|
|
1448
1448
|
|
|
1449
|
-
// src/typeschema/register.ts
|
|
1450
|
-
var registerFromManager = async (manager, logger) => {
|
|
1451
|
-
const packages = await manager.packages();
|
|
1452
|
-
const flatRawIndex = {};
|
|
1453
|
-
const indexByPackages = [];
|
|
1454
|
-
for (const pkg of packages) {
|
|
1455
|
-
const resources = await manager.search({ package: pkg });
|
|
1456
|
-
const perPackageIndex = {};
|
|
1457
|
-
for (const resource of resources) {
|
|
1458
|
-
const url = resource.url;
|
|
1459
|
-
if (!url) continue;
|
|
1460
|
-
if (perPackageIndex[url]) throw new Error(`Duplicate resource URL: ${url}`);
|
|
1461
|
-
perPackageIndex[url] = resource;
|
|
1462
|
-
if (flatRawIndex[url]) throw new Error(`Duplicate resource URL: ${url}`);
|
|
1463
|
-
flatRawIndex[url] = resource;
|
|
1464
|
-
}
|
|
1465
|
-
indexByPackages.push({
|
|
1466
|
-
package_meta: pkg,
|
|
1467
|
-
index: perPackageIndex
|
|
1468
|
-
});
|
|
1469
|
-
}
|
|
1470
|
-
const sdIndex = {};
|
|
1471
|
-
const vsIndex = {};
|
|
1472
|
-
const fsIndex = {};
|
|
1473
|
-
const specNameToCanonical = {};
|
|
1474
|
-
for (const resourcesByPackage of indexByPackages) {
|
|
1475
|
-
const packageMeta = resourcesByPackage.package_meta;
|
|
1476
|
-
for (const [surl, resource] of Object.entries(resourcesByPackage.index)) {
|
|
1477
|
-
const url = surl;
|
|
1478
|
-
if (resource.resourceType === "StructureDefinition") {
|
|
1479
|
-
const sd = resource;
|
|
1480
|
-
sdIndex[url] = sd;
|
|
1481
|
-
const rfs = enrichFHIRSchema(fhirschema.translate(sd), packageMeta);
|
|
1482
|
-
fsIndex[rfs.url] = rfs;
|
|
1483
|
-
if (rfs.derivation === void 0 || rfs.derivation === "specialization") {
|
|
1484
|
-
if (specNameToCanonical[rfs.name]) {
|
|
1485
|
-
const info = {
|
|
1486
|
-
old: specNameToCanonical[rfs.name],
|
|
1487
|
-
oldDerivation: flatRawIndex[specNameToCanonical[rfs.name]].derivation,
|
|
1488
|
-
new: rfs.url,
|
|
1489
|
-
newDerivation: rfs.derivation
|
|
1490
|
-
};
|
|
1491
|
-
throw new Error(`Duplicate name ${rfs.name} ${JSON.stringify(info, void 0, 2)}`);
|
|
1492
|
-
}
|
|
1493
|
-
specNameToCanonical[rfs.name] = rfs.url;
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
if (resource.resourceType === "ValueSet") {
|
|
1497
|
-
if (!resource.package_meta) {
|
|
1498
|
-
resource.package_meta = packageMeta;
|
|
1499
|
-
}
|
|
1500
|
-
vsIndex[resource.url] = resource;
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1503
|
-
logger?.success(
|
|
1504
|
-
`FHIR Schema conversion for '${packageMetaToFhir(packageMeta)}' completed: ${Object.keys(fsIndex).length} successful`
|
|
1505
|
-
);
|
|
1506
|
-
}
|
|
1507
|
-
const complexTypes = {};
|
|
1508
|
-
for (const fs3 of Object.values(fsIndex)) {
|
|
1509
|
-
if (fs3.kind === "complex-type") {
|
|
1510
|
-
complexTypes[fs3.url] = fs3;
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
const resolveFsGenealogy = (canonicalUrl) => {
|
|
1514
|
-
let fs3 = fsIndex[canonicalUrl];
|
|
1515
|
-
if (fs3 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
|
|
1516
|
-
const genealogy = [fs3];
|
|
1517
|
-
while (fs3?.base) {
|
|
1518
|
-
fs3 = fsIndex[fs3.base] || fsIndex[specNameToCanonical[fs3.base]];
|
|
1519
|
-
genealogy.push(fs3);
|
|
1520
|
-
if (fs3 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
|
|
1521
|
-
}
|
|
1522
|
-
return genealogy;
|
|
1523
|
-
};
|
|
1524
|
-
const resolveFsSpecializations = (canonicalUrl) => {
|
|
1525
|
-
return resolveFsGenealogy(canonicalUrl).filter((fs3) => fs3.derivation === "specialization");
|
|
1526
|
-
};
|
|
1527
|
-
return {
|
|
1528
|
-
...manager,
|
|
1529
|
-
appendFs(fs3) {
|
|
1530
|
-
const rfs = enrichFHIRSchema(fs3);
|
|
1531
|
-
fsIndex[rfs.url] = rfs;
|
|
1532
|
-
specNameToCanonical[rfs.name] = rfs.url;
|
|
1533
|
-
},
|
|
1534
|
-
resolveFs: (canonicalUrl) => fsIndex[canonicalUrl],
|
|
1535
|
-
resolveFsGenealogy,
|
|
1536
|
-
resolveFsSpecializations,
|
|
1537
|
-
ensureSpecializationCanonicalUrl: (name) => specNameToCanonical[name] || name,
|
|
1538
|
-
allSd: () => Object.values(sdIndex),
|
|
1539
|
-
resolveSd: (canonicalUrl) => sdIndex[canonicalUrl],
|
|
1540
|
-
allFs: () => Object.values(fsIndex),
|
|
1541
|
-
allVs: () => Object.values(vsIndex),
|
|
1542
|
-
resolveVs: (canonicalUrl) => vsIndex[canonicalUrl],
|
|
1543
|
-
complexTypeDict: () => complexTypes,
|
|
1544
|
-
resolveAny: (canonicalUrl) => flatRawIndex[canonicalUrl]
|
|
1545
|
-
};
|
|
1546
|
-
};
|
|
1547
|
-
var resolveFsElementGenealogy = (genealogy, path) => {
|
|
1548
|
-
const [top, ...rest] = path;
|
|
1549
|
-
if (top === void 0) return [];
|
|
1550
|
-
return genealogy.map((fs3) => {
|
|
1551
|
-
if (!fs3.elements) return void 0;
|
|
1552
|
-
let elem = fs3.elements?.[top];
|
|
1553
|
-
for (const k of rest) {
|
|
1554
|
-
elem = elem?.elements?.[k];
|
|
1555
|
-
}
|
|
1556
|
-
return elem;
|
|
1557
|
-
}).filter((elem) => elem !== void 0);
|
|
1558
|
-
};
|
|
1559
|
-
function fsElementSnapshot(genealogy) {
|
|
1560
|
-
const snapshot = genealogy.reverse().reduce((snapshot2, elem) => ({ ...snapshot2, ...elem }), {});
|
|
1561
|
-
snapshot.elements = void 0;
|
|
1562
|
-
return snapshot;
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
1449
|
// src/typeschema/core/identifier.ts
|
|
1566
1450
|
function dropVersionFromUrl(url) {
|
|
1567
1451
|
const baseUrl = url.split("|")[0];
|
|
@@ -1661,12 +1545,13 @@ function collectNestedElements(fhirSchema, parentPath, elements) {
|
|
|
1661
1545
|
}
|
|
1662
1546
|
function transformNestedElements(register, fhirSchema, parentPath, elements, logger) {
|
|
1663
1547
|
const fields = {};
|
|
1664
|
-
for (const [key,
|
|
1548
|
+
for (const [key, _element] of Object.entries(elements)) {
|
|
1665
1549
|
const path = [...parentPath, key];
|
|
1666
|
-
|
|
1667
|
-
|
|
1550
|
+
const elemSnapshot = register.resolveElementSnapshot(fhirSchema, path);
|
|
1551
|
+
if (isNestedElement(elemSnapshot)) {
|
|
1552
|
+
fields[key] = mkNestedField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1668
1553
|
} else {
|
|
1669
|
-
fields[key] = mkField(register, fhirSchema, path,
|
|
1554
|
+
fields[key] = mkField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1670
1555
|
}
|
|
1671
1556
|
}
|
|
1672
1557
|
return fields;
|
|
@@ -1759,7 +1644,7 @@ var buildReferences = (element, register, _packageInfo) => {
|
|
|
1759
1644
|
return mkIdentifier(fs3);
|
|
1760
1645
|
});
|
|
1761
1646
|
};
|
|
1762
|
-
function buildFieldType(register, fhirSchema, element, logger) {
|
|
1647
|
+
function buildFieldType(register, fhirSchema, path, element, logger) {
|
|
1763
1648
|
if (element.elementReference) {
|
|
1764
1649
|
const refPath = element.elementReference.slice(1).filter((_, i) => i % 2 === 1);
|
|
1765
1650
|
return mkNestedIdentifier(register, fhirSchema, refPath, logger);
|
|
@@ -1772,10 +1657,12 @@ function buildFieldType(register, fhirSchema, element, logger) {
|
|
|
1772
1657
|
return void 0;
|
|
1773
1658
|
} else if (fhirSchema.derivation === "constraint") {
|
|
1774
1659
|
return void 0;
|
|
1775
|
-
} else
|
|
1776
|
-
|
|
1777
|
-
`Can't recognize element type '${fhirSchema.url}' (${fhirSchema.derivation}): ${JSON.stringify(element, void 0, 2)}`
|
|
1660
|
+
} else {
|
|
1661
|
+
logger?.error(
|
|
1662
|
+
`Can't recognize element type '${fhirSchema.url}' (${fhirSchema.derivation}) at '${path.join(".")}': ${JSON.stringify(element, void 0, 2)}`
|
|
1778
1663
|
);
|
|
1664
|
+
throw new Error(`Unrecognized element type`);
|
|
1665
|
+
}
|
|
1779
1666
|
}
|
|
1780
1667
|
var mkField = (register, fhirSchema, path, element, logger) => {
|
|
1781
1668
|
let binding;
|
|
@@ -1787,7 +1674,7 @@ var mkField = (register, fhirSchema, path, element, logger) => {
|
|
|
1787
1674
|
}
|
|
1788
1675
|
}
|
|
1789
1676
|
return {
|
|
1790
|
-
type: buildFieldType(register, fhirSchema, element, logger),
|
|
1677
|
+
type: buildFieldType(register, fhirSchema, path, element, logger),
|
|
1791
1678
|
required: isRequired(register, fhirSchema, path),
|
|
1792
1679
|
excluded: isExcluded(register, fhirSchema, path),
|
|
1793
1680
|
reference: buildReferences(element, register, fhirSchema.package_meta),
|
|
@@ -1882,7 +1769,7 @@ function buildEnum(register, element, logger) {
|
|
|
1882
1769
|
function generateBindingSchema(register, fhirSchema, path, element, logger) {
|
|
1883
1770
|
if (!element.binding?.valueSet) return void 0;
|
|
1884
1771
|
const identifier = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
|
|
1885
|
-
const fieldType = buildFieldType(register, fhirSchema, element, logger);
|
|
1772
|
+
const fieldType = buildFieldType(register, fhirSchema, path, element, logger);
|
|
1886
1773
|
const valueSetIdentifier = mkValueSetIdentifierByUrl(register, element.binding.valueSet);
|
|
1887
1774
|
const dependencies = [];
|
|
1888
1775
|
if (fieldType) {
|
|
@@ -1936,26 +1823,10 @@ function collectBindingSchemas(register, fhirSchema, logger) {
|
|
|
1936
1823
|
// src/typeschema/core/transformer.ts
|
|
1937
1824
|
function mkFields(register, fhirSchema, parentPath, elements, logger) {
|
|
1938
1825
|
if (!elements) return void 0;
|
|
1939
|
-
const geneology = register.resolveFsGenealogy(fhirSchema.url);
|
|
1940
|
-
const elems = {};
|
|
1941
|
-
for (const [key, elem] of Object.entries(elements)) {
|
|
1942
|
-
const path = [...parentPath, key];
|
|
1943
|
-
const elemGeneology = resolveFsElementGenealogy(geneology, path);
|
|
1944
|
-
const elemSnapshot = fsElementSnapshot(elemGeneology);
|
|
1945
|
-
elems[key] = { elem, elemSnapshot, path };
|
|
1946
|
-
}
|
|
1947
|
-
for (const [_key, { elem, elemSnapshot, path }] of Object.entries(elems)) {
|
|
1948
|
-
for (const choiceKey of elem?.choices || []) {
|
|
1949
|
-
if (!elems[choiceKey]) {
|
|
1950
|
-
const path2 = [...parentPath, choiceKey];
|
|
1951
|
-
const elemGeneology = resolveFsElementGenealogy(geneology, path2);
|
|
1952
|
-
const elemSnapshot2 = fsElementSnapshot(elemGeneology);
|
|
1953
|
-
elems[choiceKey] = { elem: void 0, elemSnapshot: elemSnapshot2, path: path2 };
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
1956
|
-
}
|
|
1957
1826
|
const fields = {};
|
|
1958
|
-
for (const
|
|
1827
|
+
for (const key of register.getAllElementKeys(elements)) {
|
|
1828
|
+
const path = [...parentPath, key];
|
|
1829
|
+
const elemSnapshot = register.resolveElementSnapshot(fhirSchema, path);
|
|
1959
1830
|
if (isNestedElement(elemSnapshot)) {
|
|
1960
1831
|
fields[key] = mkNestedField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1961
1832
|
} else {
|
|
@@ -2385,7 +2256,7 @@ var CodegenLogger = class _CodegenLogger {
|
|
|
2385
2256
|
*/
|
|
2386
2257
|
error(message, error) {
|
|
2387
2258
|
if (this.isSuppressed(3 /* ERROR */)) return;
|
|
2388
|
-
console.error(this.formatMessage("", message, pc.red));
|
|
2259
|
+
console.error(this.formatMessage("X", message, pc.red));
|
|
2389
2260
|
if (error && this.options.verbose) {
|
|
2390
2261
|
console.error(pc.red(` ${error.message}`));
|
|
2391
2262
|
if (error.stack) {
|
|
@@ -2465,6 +2336,175 @@ new CodegenLogger();
|
|
|
2465
2336
|
function createLogger(options = {}) {
|
|
2466
2337
|
return new CodegenLogger(options);
|
|
2467
2338
|
}
|
|
2339
|
+
var registerFromManager = async (manager, conf) => {
|
|
2340
|
+
const packageNameResolver = conf?.packageNameResolver ?? ["hl7.fhir.r5.core", "hl7.cda.uv.core"];
|
|
2341
|
+
const logger = conf?.logger;
|
|
2342
|
+
const flatRawIndex = {};
|
|
2343
|
+
const canonicalToPackages = {};
|
|
2344
|
+
const resourceTypes = /* @__PURE__ */ new Set(["StructureDefinition", "ValueSet", "CodeSystem"]);
|
|
2345
|
+
for (const res of await manager.search({})) {
|
|
2346
|
+
const rawUrl = res.url;
|
|
2347
|
+
if (!rawUrl) continue;
|
|
2348
|
+
if (!resourceTypes.has(res.resourceType)) continue;
|
|
2349
|
+
const url = rawUrl;
|
|
2350
|
+
const pkg = (await manager.resolveEntry(url)).package;
|
|
2351
|
+
if (!pkg) throw new Error(`Can't resolve package for ${url}`);
|
|
2352
|
+
if (!canonicalToPackages[url]) canonicalToPackages[url] = [];
|
|
2353
|
+
canonicalToPackages[url].push(pkg);
|
|
2354
|
+
flatRawIndex[url] = res;
|
|
2355
|
+
}
|
|
2356
|
+
const collisions = Object.entries(canonicalToPackages).filter(([_, e]) => e.length > 1).map(([url, pkgs]) => `${url}: ${pkgs.map((p) => `${p.name}@${p.version}`).join(", ")}`).join("\n");
|
|
2357
|
+
logger?.warn(`Duplicated canonicals: ${collisions}`);
|
|
2358
|
+
const packageToResources = {};
|
|
2359
|
+
const packageToPackageMeta = {};
|
|
2360
|
+
for (const [url, _pkgs] of Object.entries(canonicalToPackages)) {
|
|
2361
|
+
const pkg = (await manager.resolveEntry(url)).package;
|
|
2362
|
+
if (!pkg) throw new Error(`Can't find package for ${url}`);
|
|
2363
|
+
const pkgId = packageMetaToFhir(pkg);
|
|
2364
|
+
packageToPackageMeta[pkgId] = pkg;
|
|
2365
|
+
const res = await manager.resolve(url);
|
|
2366
|
+
if (!packageToResources[pkgId]) {
|
|
2367
|
+
packageToResources[pkgId] = {};
|
|
2368
|
+
}
|
|
2369
|
+
const index = packageToResources[pkgId];
|
|
2370
|
+
if (!index) throw new Error(`Can't find index for ${pkg.name}@${pkg.version}`);
|
|
2371
|
+
index[url] = res;
|
|
2372
|
+
}
|
|
2373
|
+
const indexByPackages = [];
|
|
2374
|
+
for (const [pkgId, index] of Object.entries(packageToResources)) {
|
|
2375
|
+
indexByPackages.push({
|
|
2376
|
+
package_meta: packageToPackageMeta[pkgId],
|
|
2377
|
+
index
|
|
2378
|
+
});
|
|
2379
|
+
}
|
|
2380
|
+
const sdIndex = {};
|
|
2381
|
+
const vsIndex = {};
|
|
2382
|
+
const fsIndex = {};
|
|
2383
|
+
for (const resourcesByPackage of indexByPackages) {
|
|
2384
|
+
const packageMeta = resourcesByPackage.package_meta;
|
|
2385
|
+
let counter = 0;
|
|
2386
|
+
logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(packageMeta)}' begins...`);
|
|
2387
|
+
for (const [surl, resource] of Object.entries(resourcesByPackage.index)) {
|
|
2388
|
+
const url = surl;
|
|
2389
|
+
if (resource.resourceType === "StructureDefinition") {
|
|
2390
|
+
const sd = resource;
|
|
2391
|
+
sdIndex[url] = sd;
|
|
2392
|
+
const rfs = enrichFHIRSchema(fhirschema.translate(sd), packageMeta);
|
|
2393
|
+
counter++;
|
|
2394
|
+
fsIndex[rfs.url] = rfs;
|
|
2395
|
+
}
|
|
2396
|
+
if (resource.resourceType === "ValueSet") {
|
|
2397
|
+
if (!resource.package_meta) {
|
|
2398
|
+
resource.package_meta = packageMeta;
|
|
2399
|
+
}
|
|
2400
|
+
vsIndex[resource.url] = resource;
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
logger?.success(
|
|
2404
|
+
`FHIR Schema conversion for '${packageMetaToFhir(packageMeta)}' completed: ${counter} successful`
|
|
2405
|
+
);
|
|
2406
|
+
}
|
|
2407
|
+
const specNameToCanonicals = {};
|
|
2408
|
+
for (const rfs of Object.values(fsIndex)) {
|
|
2409
|
+
if (rfs.derivation === "constraint") continue;
|
|
2410
|
+
const name = rfs.name;
|
|
2411
|
+
if (!specNameToCanonicals[name]) specNameToCanonicals[name] = {};
|
|
2412
|
+
specNameToCanonicals[name][rfs.package_meta.name] = rfs.url;
|
|
2413
|
+
}
|
|
2414
|
+
const specNameToCanonical = {};
|
|
2415
|
+
for (const [sname, canonicals] of Object.entries(specNameToCanonicals)) {
|
|
2416
|
+
const name = sname;
|
|
2417
|
+
const canonicalValues = Object.values(canonicals);
|
|
2418
|
+
if (canonicalValues.length === 1) {
|
|
2419
|
+
const url = canonicalValues[0];
|
|
2420
|
+
specNameToCanonical[name] = url;
|
|
2421
|
+
} else {
|
|
2422
|
+
for (const pname of packageNameResolver) {
|
|
2423
|
+
if (canonicals[pname]) {
|
|
2424
|
+
specNameToCanonical[name] = canonicals[pname];
|
|
2425
|
+
break;
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
if (specNameToCanonical[name] === void 0) throw new Error(`No canonical URL found for ${name}`);
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
const complexTypes = {};
|
|
2432
|
+
for (const fs3 of Object.values(fsIndex)) {
|
|
2433
|
+
if (fs3.kind === "complex-type") {
|
|
2434
|
+
complexTypes[fs3.url] = fs3;
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
const resolveFsGenealogy = (canonicalUrl) => {
|
|
2438
|
+
let fs3 = fsIndex[canonicalUrl];
|
|
2439
|
+
if (fs3 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
|
|
2440
|
+
const genealogy = [fs3];
|
|
2441
|
+
while (fs3?.base) {
|
|
2442
|
+
fs3 = fsIndex[fs3.base] || fsIndex[specNameToCanonical[fs3.base]];
|
|
2443
|
+
genealogy.push(fs3);
|
|
2444
|
+
if (fs3 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
|
|
2445
|
+
}
|
|
2446
|
+
return genealogy;
|
|
2447
|
+
};
|
|
2448
|
+
const resolveFsSpecializations = (canonicalUrl) => {
|
|
2449
|
+
return resolveFsGenealogy(canonicalUrl).filter((fs3) => fs3.derivation === "specialization");
|
|
2450
|
+
};
|
|
2451
|
+
const resolveElementSnapshot = (fhirSchema, path) => {
|
|
2452
|
+
const geneology = resolveFsGenealogy(fhirSchema.url);
|
|
2453
|
+
const elemGeneology = resolveFsElementGenealogy(geneology, path);
|
|
2454
|
+
const elemSnapshot = fsElementSnapshot(elemGeneology);
|
|
2455
|
+
return elemSnapshot;
|
|
2456
|
+
};
|
|
2457
|
+
const getAllElementKeys = (elems) => {
|
|
2458
|
+
const keys = /* @__PURE__ */ new Set();
|
|
2459
|
+
for (const [key, elem] of Object.entries(elems)) {
|
|
2460
|
+
keys.add(key);
|
|
2461
|
+
for (const choiceKey of elem?.choices || []) {
|
|
2462
|
+
if (!elems[choiceKey]) {
|
|
2463
|
+
keys.add(choiceKey);
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
return Array.from(keys);
|
|
2468
|
+
};
|
|
2469
|
+
return {
|
|
2470
|
+
...manager,
|
|
2471
|
+
appendFs(fs3) {
|
|
2472
|
+
const rfs = enrichFHIRSchema(fs3);
|
|
2473
|
+
fsIndex[rfs.url] = rfs;
|
|
2474
|
+
specNameToCanonical[rfs.name] = rfs.url;
|
|
2475
|
+
},
|
|
2476
|
+
resolveFs: (canonicalUrl) => fsIndex[canonicalUrl],
|
|
2477
|
+
resolveFsGenealogy,
|
|
2478
|
+
resolveFsSpecializations,
|
|
2479
|
+
ensureSpecializationCanonicalUrl: (name) => specNameToCanonical[name] || name,
|
|
2480
|
+
allSd: () => Object.values(sdIndex),
|
|
2481
|
+
resolveSd: (canonicalUrl) => sdIndex[canonicalUrl],
|
|
2482
|
+
allFs: () => Object.values(fsIndex),
|
|
2483
|
+
allVs: () => Object.values(vsIndex),
|
|
2484
|
+
resolveVs: (canonicalUrl) => vsIndex[canonicalUrl],
|
|
2485
|
+
complexTypeDict: () => complexTypes,
|
|
2486
|
+
resolveAny: (canonicalUrl) => flatRawIndex[canonicalUrl],
|
|
2487
|
+
resolveElementSnapshot,
|
|
2488
|
+
getAllElementKeys
|
|
2489
|
+
};
|
|
2490
|
+
};
|
|
2491
|
+
var resolveFsElementGenealogy = (genealogy, path) => {
|
|
2492
|
+
const [top, ...rest] = path;
|
|
2493
|
+
if (top === void 0) return [];
|
|
2494
|
+
return genealogy.map((fs3) => {
|
|
2495
|
+
if (!fs3.elements) return void 0;
|
|
2496
|
+
let elem = fs3.elements?.[top];
|
|
2497
|
+
for (const k of rest) {
|
|
2498
|
+
elem = elem?.elements?.[k];
|
|
2499
|
+
}
|
|
2500
|
+
return elem;
|
|
2501
|
+
}).filter((elem) => elem !== void 0);
|
|
2502
|
+
};
|
|
2503
|
+
function fsElementSnapshot(genealogy) {
|
|
2504
|
+
const snapshot = genealogy.reverse().reduce((snapshot2, elem) => ({ ...snapshot2, ...elem }), {});
|
|
2505
|
+
snapshot.elements = void 0;
|
|
2506
|
+
return snapshot;
|
|
2507
|
+
}
|
|
2468
2508
|
|
|
2469
2509
|
// src/typeschema/generator.ts
|
|
2470
2510
|
var TypeSchemaGenerator = class {
|
|
@@ -2522,7 +2562,7 @@ var TypeSchemaGenerator = class {
|
|
|
2522
2562
|
if (valueSets.length > 0) {
|
|
2523
2563
|
this.logger?.debug(`${valueSets.length} ValueSets available for enum extraction`);
|
|
2524
2564
|
}
|
|
2525
|
-
const register = await registerFromManager(this.manager, logger);
|
|
2565
|
+
const register = await registerFromManager(this.manager, { logger: this.logger });
|
|
2526
2566
|
const valueSetSchemas = [];
|
|
2527
2567
|
if (valueSets.length > 0) {
|
|
2528
2568
|
this.logger?.progress(`Converting ${valueSets.length} ValueSets to TypeSchema`);
|
|
@@ -2972,6 +3012,23 @@ var uppercaseFirstLetter = (str) => {
|
|
|
2972
3012
|
var uppercaseFirstLetterOfEach = (strings) => {
|
|
2973
3013
|
return strings.map((str) => uppercaseFirstLetter(str));
|
|
2974
3014
|
};
|
|
3015
|
+
function deepEqual(obj1, obj2) {
|
|
3016
|
+
if (obj1 === obj2) return true;
|
|
3017
|
+
if (obj1 === null || obj2 === null || typeof obj1 !== "object" || typeof obj2 !== "object") {
|
|
3018
|
+
return false;
|
|
3019
|
+
}
|
|
3020
|
+
if (Array.isArray(obj1) && Array.isArray(obj2)) {
|
|
3021
|
+
if (obj1.length !== obj2.length) return false;
|
|
3022
|
+
return obj1.every((item, index) => deepEqual(item, obj2[index]));
|
|
3023
|
+
}
|
|
3024
|
+
if (Array.isArray(obj1) || Array.isArray(obj2)) {
|
|
3025
|
+
return false;
|
|
3026
|
+
}
|
|
3027
|
+
const keys1 = Object.keys(obj1);
|
|
3028
|
+
const keys2 = Object.keys(obj2);
|
|
3029
|
+
if (keys1.length !== keys2.length) return false;
|
|
3030
|
+
return keys1.every((key) => keys2.includes(key) && deepEqual(obj1[key], obj2[key]));
|
|
3031
|
+
}
|
|
2975
3032
|
|
|
2976
3033
|
// src/typeschema/utils.ts
|
|
2977
3034
|
var groupByPackages = (typeSchemas) => {
|
|
@@ -3015,7 +3072,7 @@ var resourceRelatives = (schemas) => {
|
|
|
3015
3072
|
}
|
|
3016
3073
|
return allPairs;
|
|
3017
3074
|
};
|
|
3018
|
-
var mkTypeSchemaIndex = (schemas) => {
|
|
3075
|
+
var mkTypeSchemaIndex = (schemas, logger) => {
|
|
3019
3076
|
const index = {};
|
|
3020
3077
|
const append = (schema) => {
|
|
3021
3078
|
const url = schema.identifier.url;
|
|
@@ -3036,7 +3093,7 @@ var mkTypeSchemaIndex = (schemas) => {
|
|
|
3036
3093
|
const resourceChildren = (id) => {
|
|
3037
3094
|
return relations.filter((relative2) => relative2.parent.name === id.name).map((relative2) => relative2.child);
|
|
3038
3095
|
};
|
|
3039
|
-
const
|
|
3096
|
+
const tryHierarchy = (schema) => {
|
|
3040
3097
|
const res = [];
|
|
3041
3098
|
let cur = schema;
|
|
3042
3099
|
while (cur) {
|
|
@@ -3045,18 +3102,23 @@ var mkTypeSchemaIndex = (schemas) => {
|
|
|
3045
3102
|
if (base === void 0) break;
|
|
3046
3103
|
const resolved = resolve2(base);
|
|
3047
3104
|
if (!resolved) {
|
|
3048
|
-
|
|
3049
|
-
`Failed to resolve base type: ${res.map((e) => `${e.identifier.url} (${e.identifier.kind})`).join(", ")}`
|
|
3050
|
-
);
|
|
3105
|
+
return void 0;
|
|
3051
3106
|
}
|
|
3052
3107
|
cur = resolved;
|
|
3053
3108
|
}
|
|
3054
3109
|
return res;
|
|
3055
3110
|
};
|
|
3111
|
+
const hierarchy = (schema) => {
|
|
3112
|
+
const genealogy = tryHierarchy(schema);
|
|
3113
|
+
if (genealogy === void 0) {
|
|
3114
|
+
throw new Error(`Failed to resolve base type: ${schema.identifier.url} (${schema.identifier.kind})`);
|
|
3115
|
+
}
|
|
3116
|
+
return genealogy;
|
|
3117
|
+
};
|
|
3056
3118
|
const findLastSpecialization = (schema) => {
|
|
3057
3119
|
const nonConstraintSchema = hierarchy(schema).find((s) => s.identifier.kind !== "profile");
|
|
3058
3120
|
if (!nonConstraintSchema) {
|
|
3059
|
-
throw new Error(`No non-constraint schema found in hierarchy for ${schema.identifier.name}`);
|
|
3121
|
+
throw new Error(`No non-constraint schema found in hierarchy for: ${schema.identifier.name}`);
|
|
3060
3122
|
}
|
|
3061
3123
|
return nonConstraintSchema;
|
|
3062
3124
|
};
|
|
@@ -3096,7 +3158,9 @@ var mkTypeSchemaIndex = (schemas) => {
|
|
|
3096
3158
|
};
|
|
3097
3159
|
};
|
|
3098
3160
|
const isWithMetaField = (profile) => {
|
|
3099
|
-
|
|
3161
|
+
const genealogy = tryHierarchy(profile);
|
|
3162
|
+
if (!genealogy) return false;
|
|
3163
|
+
return genealogy.filter(isSpecializationTypeSchema).some((schema) => {
|
|
3100
3164
|
return schema.fields?.meta !== void 0;
|
|
3101
3165
|
});
|
|
3102
3166
|
};
|
|
@@ -3109,6 +3173,7 @@ var mkTypeSchemaIndex = (schemas) => {
|
|
|
3109
3173
|
collectProfiles: () => schemas.filter(isProfileTypeSchema),
|
|
3110
3174
|
resolve: resolve2,
|
|
3111
3175
|
resourceChildren,
|
|
3176
|
+
tryHierarchy,
|
|
3112
3177
|
hierarchy,
|
|
3113
3178
|
findLastSpecialization,
|
|
3114
3179
|
findLastSpecializationByIdentifier,
|
|
@@ -5676,7 +5741,7 @@ var writerToGenerator = (writerGen) => {
|
|
|
5676
5741
|
};
|
|
5677
5742
|
};
|
|
5678
5743
|
var normalizeFileName = (str) => str.replace(/[^a-zA-Z0-9]/g, "");
|
|
5679
|
-
var APIBuilder = class {
|
|
5744
|
+
var APIBuilder = class _APIBuilder {
|
|
5680
5745
|
schemas = [];
|
|
5681
5746
|
options;
|
|
5682
5747
|
generators = /* @__PURE__ */ new Map();
|
|
@@ -5804,33 +5869,53 @@ var APIBuilder = class {
|
|
|
5804
5869
|
this.options.typeSchemaOutputDir = target;
|
|
5805
5870
|
return this;
|
|
5806
5871
|
}
|
|
5872
|
+
static async isIdenticalTo(path, tsJSON) {
|
|
5873
|
+
if (!await afs.exists(path)) return false;
|
|
5874
|
+
const json = await afs.readFile(path);
|
|
5875
|
+
const ts1 = JSON.parse(json.toString());
|
|
5876
|
+
const ts2 = JSON.parse(tsJSON);
|
|
5877
|
+
return deepEqual(ts1, ts2);
|
|
5878
|
+
}
|
|
5879
|
+
async writeTypeSchemasToSeparateFiles(typeSchemas, outputDir) {
|
|
5880
|
+
if (this.options.cleanOutput) fs.rmSync(outputDir, { recursive: true, force: true });
|
|
5881
|
+
await afs.mkdir(outputDir, { recursive: true });
|
|
5882
|
+
const usedNames = {};
|
|
5883
|
+
this.logger.info(`Writing TypeSchema files to ${outputDir}...`);
|
|
5884
|
+
for (const ts of typeSchemas) {
|
|
5885
|
+
const package_name = camelCase(ts.identifier.package.replaceAll("/", "-"));
|
|
5886
|
+
const name = normalizeFileName(ts.identifier.name.toString());
|
|
5887
|
+
const json = JSON.stringify(ts, null, 2);
|
|
5888
|
+
const baseName = Path2.join(outputDir, package_name, name);
|
|
5889
|
+
let fullName;
|
|
5890
|
+
if (usedNames[baseName] !== void 0) {
|
|
5891
|
+
usedNames[baseName]++;
|
|
5892
|
+
fullName = `${baseName}-${usedNames[baseName]}.typeschema.json`;
|
|
5893
|
+
} else {
|
|
5894
|
+
usedNames[baseName] = 0;
|
|
5895
|
+
fullName = `${baseName}.typeschema.json`;
|
|
5896
|
+
}
|
|
5897
|
+
if (await _APIBuilder.isIdenticalTo(fullName, json)) continue;
|
|
5898
|
+
await afs.mkdir(Path2.dirname(fullName), { recursive: true });
|
|
5899
|
+
await afs.writeFile(fullName, json);
|
|
5900
|
+
}
|
|
5901
|
+
}
|
|
5902
|
+
async writeTypeSchemasToSingleFile(typeSchemas, outputFile) {
|
|
5903
|
+
if (this.options.cleanOutput && fs.existsSync(outputFile)) fs.rmSync(outputFile);
|
|
5904
|
+
await afs.mkdir(Path2.dirname(outputFile), { recursive: true });
|
|
5905
|
+
this.logger.info(`Writing TypeSchemas to one file ${outputFile}...`);
|
|
5906
|
+
for (const ts of typeSchemas) {
|
|
5907
|
+
const json = JSON.stringify(ts, null, 2);
|
|
5908
|
+
await afs.appendFile(outputFile, json + "\n");
|
|
5909
|
+
}
|
|
5910
|
+
}
|
|
5807
5911
|
async tryWriteTypeSchema(typeSchemas) {
|
|
5808
5912
|
if (!this.options.typeSchemaOutputDir) return;
|
|
5809
5913
|
try {
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
this.logger.info(`Writing TypeSchema files to ${this.options.typeSchemaOutputDir}...`);
|
|
5816
|
-
for (const ts of typeSchemas) {
|
|
5817
|
-
const package_name = camelCase(ts.identifier.package.replaceAll("/", "-"));
|
|
5818
|
-
const name = normalizeFileName(ts.identifier.name.toString());
|
|
5819
|
-
const baseName = Path2.join(this.options.typeSchemaOutputDir, package_name, name);
|
|
5820
|
-
let fullName;
|
|
5821
|
-
if (usedNames[baseName] !== void 0) {
|
|
5822
|
-
usedNames[baseName]++;
|
|
5823
|
-
fullName = `${baseName}-${usedNames[baseName]}.typeschema.json`;
|
|
5824
|
-
} else {
|
|
5825
|
-
usedNames[baseName] = 0;
|
|
5826
|
-
fullName = `${baseName}.typeschema.json`;
|
|
5827
|
-
}
|
|
5828
|
-
await afs.mkdir(Path2.dirname(fullName), { recursive: true });
|
|
5829
|
-
afs.writeFile(fullName, JSON.stringify(ts, null, 2));
|
|
5830
|
-
if (await afs.exists(fullName)) overrideCount++;
|
|
5831
|
-
else writtenCount++;
|
|
5832
|
-
}
|
|
5833
|
-
this.logger.info(`Created ${writtenCount} new TypeSchema files, overrode ${overrideCount} files`);
|
|
5914
|
+
this.logger.info(`Starting writing TypeSchema files.`);
|
|
5915
|
+
if (Path2.extname(this.options.typeSchemaOutputDir) === ".ndjson")
|
|
5916
|
+
await this.writeTypeSchemasToSingleFile(typeSchemas, this.options.typeSchemaOutputDir);
|
|
5917
|
+
else await this.writeTypeSchemasToSeparateFiles(typeSchemas, this.options.typeSchemaOutputDir);
|
|
5918
|
+
this.logger.info(`Finished writing TypeSchema files.`);
|
|
5834
5919
|
} catch (error) {
|
|
5835
5920
|
if (this.options.throwException) throw error;
|
|
5836
5921
|
this.logger.error(
|
|
@@ -5868,7 +5953,7 @@ var APIBuilder = class {
|
|
|
5868
5953
|
workingDir: "tmp/fhir"
|
|
5869
5954
|
});
|
|
5870
5955
|
await manager.init();
|
|
5871
|
-
const register = await registerFromManager(manager, this.logger);
|
|
5956
|
+
const register = await registerFromManager(manager, { logger: this.logger });
|
|
5872
5957
|
const typeSchemas = await generateTypeSchemas(register, this.logger);
|
|
5873
5958
|
await this.tryWriteTypeSchema(typeSchemas);
|
|
5874
5959
|
this.logger.debug(`Executing ${this.generators.size} generators`);
|