@atomic-ehr/codegen 0.0.1-canary.20251010140912.b42d372 → 0.0.1-canary.20251014085150.f19c2e1
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 +23 -22
- package/dist/index.d.ts +6 -1
- package/dist/index.js +277 -181
- 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
|
/**
|
|
@@ -757,6 +759,7 @@ interface APIBuilderOptions {
|
|
|
757
759
|
typeSchemaConfig?: TypeSchemaConfig;
|
|
758
760
|
logger?: CodegenLogger;
|
|
759
761
|
manager?: ReturnType<typeof CanonicalManager> | null;
|
|
762
|
+
typeSchemaOutputDir?: string /** if .ndjson -- put in one file, else -- split into separated files*/;
|
|
760
763
|
throwException?: boolean;
|
|
761
764
|
}
|
|
762
765
|
/**
|
|
@@ -826,6 +829,8 @@ declare class APIBuilder {
|
|
|
826
829
|
verbose(enabled?: boolean): APIBuilder;
|
|
827
830
|
throwException(enabled?: boolean): APIBuilder;
|
|
828
831
|
cleanOutput(enabled?: boolean): APIBuilder;
|
|
832
|
+
writeTypeSchemas(target: string): this;
|
|
833
|
+
private tryWriteTypeSchema;
|
|
829
834
|
generate(): Promise<GenerationResult>;
|
|
830
835
|
/**
|
|
831
836
|
* Generate and return the results without writing to files
|
package/dist/index.js
CHANGED
|
@@ -1,10 +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';
|
|
3
|
+
import * as afs from 'fs/promises';
|
|
5
4
|
import { readdir, stat, unlink, readFile, writeFile, access, mkdir, rm } from 'fs/promises';
|
|
6
5
|
import * as Path2 from 'path';
|
|
7
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';
|
|
8
9
|
import pc from 'picocolors';
|
|
9
10
|
|
|
10
11
|
var __defProp = Object.defineProperty;
|
|
@@ -1445,122 +1446,6 @@ var isChoiceDeclarationField = (field) => {
|
|
|
1445
1446
|
return field.choices !== void 0;
|
|
1446
1447
|
};
|
|
1447
1448
|
|
|
1448
|
-
// src/typeschema/register.ts
|
|
1449
|
-
var registerFromManager = async (manager, logger) => {
|
|
1450
|
-
const packages = await manager.packages();
|
|
1451
|
-
const flatRawIndex = {};
|
|
1452
|
-
const indexByPackages = [];
|
|
1453
|
-
for (const pkg of packages) {
|
|
1454
|
-
const resources = await manager.search({ package: pkg });
|
|
1455
|
-
const perPackageIndex = {};
|
|
1456
|
-
for (const resource of resources) {
|
|
1457
|
-
const url = resource.url;
|
|
1458
|
-
if (!url) continue;
|
|
1459
|
-
if (perPackageIndex[url]) throw new Error(`Duplicate resource URL: ${url}`);
|
|
1460
|
-
perPackageIndex[url] = resource;
|
|
1461
|
-
if (flatRawIndex[url]) throw new Error(`Duplicate resource URL: ${url}`);
|
|
1462
|
-
flatRawIndex[url] = resource;
|
|
1463
|
-
}
|
|
1464
|
-
indexByPackages.push({
|
|
1465
|
-
package_meta: pkg,
|
|
1466
|
-
index: perPackageIndex
|
|
1467
|
-
});
|
|
1468
|
-
}
|
|
1469
|
-
const sdIndex = {};
|
|
1470
|
-
const vsIndex = {};
|
|
1471
|
-
const fsIndex = {};
|
|
1472
|
-
const specNameToCanonical = {};
|
|
1473
|
-
for (const resourcesByPackage of indexByPackages) {
|
|
1474
|
-
const packageMeta = resourcesByPackage.package_meta;
|
|
1475
|
-
for (const [surl, resource] of Object.entries(resourcesByPackage.index)) {
|
|
1476
|
-
const url = surl;
|
|
1477
|
-
if (resource.resourceType === "StructureDefinition") {
|
|
1478
|
-
const sd = resource;
|
|
1479
|
-
sdIndex[url] = sd;
|
|
1480
|
-
const rfs = enrichFHIRSchema(fhirschema.translate(sd), packageMeta);
|
|
1481
|
-
fsIndex[rfs.url] = rfs;
|
|
1482
|
-
if (rfs.derivation === void 0 || rfs.derivation === "specialization") {
|
|
1483
|
-
if (specNameToCanonical[rfs.name]) {
|
|
1484
|
-
const info = {
|
|
1485
|
-
old: specNameToCanonical[rfs.name],
|
|
1486
|
-
oldDerivation: flatRawIndex[specNameToCanonical[rfs.name]].derivation,
|
|
1487
|
-
new: rfs.url,
|
|
1488
|
-
newDerivation: rfs.derivation
|
|
1489
|
-
};
|
|
1490
|
-
throw new Error(`Duplicate name ${rfs.name} ${JSON.stringify(info, void 0, 2)}`);
|
|
1491
|
-
}
|
|
1492
|
-
specNameToCanonical[rfs.name] = rfs.url;
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
if (resource.resourceType === "ValueSet") {
|
|
1496
|
-
if (!resource.package_meta) {
|
|
1497
|
-
resource.package_meta = packageMeta;
|
|
1498
|
-
}
|
|
1499
|
-
vsIndex[resource.url] = resource;
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
logger?.success(
|
|
1503
|
-
`FHIR Schema conversion for '${packageMetaToFhir(packageMeta)}' completed: ${Object.keys(fsIndex).length} successful`
|
|
1504
|
-
);
|
|
1505
|
-
}
|
|
1506
|
-
const complexTypes = {};
|
|
1507
|
-
for (const fs3 of Object.values(fsIndex)) {
|
|
1508
|
-
if (fs3.kind === "complex-type") {
|
|
1509
|
-
complexTypes[fs3.url] = fs3;
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
const resolveFsGenealogy = (canonicalUrl) => {
|
|
1513
|
-
let fs3 = fsIndex[canonicalUrl];
|
|
1514
|
-
if (fs3 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
|
|
1515
|
-
const genealogy = [fs3];
|
|
1516
|
-
while (fs3?.base) {
|
|
1517
|
-
fs3 = fsIndex[fs3.base] || fsIndex[specNameToCanonical[fs3.base]];
|
|
1518
|
-
genealogy.push(fs3);
|
|
1519
|
-
if (fs3 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
|
|
1520
|
-
}
|
|
1521
|
-
return genealogy;
|
|
1522
|
-
};
|
|
1523
|
-
const resolveFsSpecializations = (canonicalUrl) => {
|
|
1524
|
-
return resolveFsGenealogy(canonicalUrl).filter((fs3) => fs3.derivation === "specialization");
|
|
1525
|
-
};
|
|
1526
|
-
return {
|
|
1527
|
-
...manager,
|
|
1528
|
-
appendFs(fs3) {
|
|
1529
|
-
const rfs = enrichFHIRSchema(fs3);
|
|
1530
|
-
fsIndex[rfs.url] = rfs;
|
|
1531
|
-
specNameToCanonical[rfs.name] = rfs.url;
|
|
1532
|
-
},
|
|
1533
|
-
resolveFs: (canonicalUrl) => fsIndex[canonicalUrl],
|
|
1534
|
-
resolveFsGenealogy,
|
|
1535
|
-
resolveFsSpecializations,
|
|
1536
|
-
ensureSpecializationCanonicalUrl: (name) => specNameToCanonical[name] || name,
|
|
1537
|
-
allSd: () => Object.values(sdIndex),
|
|
1538
|
-
resolveSd: (canonicalUrl) => sdIndex[canonicalUrl],
|
|
1539
|
-
allFs: () => Object.values(fsIndex),
|
|
1540
|
-
allVs: () => Object.values(vsIndex),
|
|
1541
|
-
resolveVs: (canonicalUrl) => vsIndex[canonicalUrl],
|
|
1542
|
-
complexTypeDict: () => complexTypes,
|
|
1543
|
-
resolveAny: (canonicalUrl) => flatRawIndex[canonicalUrl]
|
|
1544
|
-
};
|
|
1545
|
-
};
|
|
1546
|
-
var resolveFsElementGenealogy = (genealogy, path) => {
|
|
1547
|
-
const [top, ...rest] = path;
|
|
1548
|
-
if (top === void 0) return [];
|
|
1549
|
-
return genealogy.map((fs3) => {
|
|
1550
|
-
if (!fs3.elements) return void 0;
|
|
1551
|
-
let elem = fs3.elements?.[top];
|
|
1552
|
-
for (const k of rest) {
|
|
1553
|
-
elem = elem?.elements?.[k];
|
|
1554
|
-
}
|
|
1555
|
-
return elem;
|
|
1556
|
-
}).filter((elem) => elem !== void 0);
|
|
1557
|
-
};
|
|
1558
|
-
function fsElementSnapshot(genealogy) {
|
|
1559
|
-
const snapshot = genealogy.reverse().reduce((snapshot2, elem) => ({ ...snapshot2, ...elem }), {});
|
|
1560
|
-
snapshot.elements = void 0;
|
|
1561
|
-
return snapshot;
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
1449
|
// src/typeschema/core/identifier.ts
|
|
1565
1450
|
function dropVersionFromUrl(url) {
|
|
1566
1451
|
const baseUrl = url.split("|")[0];
|
|
@@ -1660,12 +1545,13 @@ function collectNestedElements(fhirSchema, parentPath, elements) {
|
|
|
1660
1545
|
}
|
|
1661
1546
|
function transformNestedElements(register, fhirSchema, parentPath, elements, logger) {
|
|
1662
1547
|
const fields = {};
|
|
1663
|
-
for (const [key,
|
|
1548
|
+
for (const [key, _element] of Object.entries(elements)) {
|
|
1664
1549
|
const path = [...parentPath, key];
|
|
1665
|
-
|
|
1666
|
-
|
|
1550
|
+
const elemSnapshot = register.resolveElementSnapshot(fhirSchema, path);
|
|
1551
|
+
if (isNestedElement(elemSnapshot)) {
|
|
1552
|
+
fields[key] = mkNestedField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1667
1553
|
} else {
|
|
1668
|
-
fields[key] = mkField(register, fhirSchema, path,
|
|
1554
|
+
fields[key] = mkField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1669
1555
|
}
|
|
1670
1556
|
}
|
|
1671
1557
|
return fields;
|
|
@@ -1758,7 +1644,7 @@ var buildReferences = (element, register, _packageInfo) => {
|
|
|
1758
1644
|
return mkIdentifier(fs3);
|
|
1759
1645
|
});
|
|
1760
1646
|
};
|
|
1761
|
-
function buildFieldType(register, fhirSchema, element, logger) {
|
|
1647
|
+
function buildFieldType(register, fhirSchema, path, element, logger) {
|
|
1762
1648
|
if (element.elementReference) {
|
|
1763
1649
|
const refPath = element.elementReference.slice(1).filter((_, i) => i % 2 === 1);
|
|
1764
1650
|
return mkNestedIdentifier(register, fhirSchema, refPath, logger);
|
|
@@ -1771,10 +1657,12 @@ function buildFieldType(register, fhirSchema, element, logger) {
|
|
|
1771
1657
|
return void 0;
|
|
1772
1658
|
} else if (fhirSchema.derivation === "constraint") {
|
|
1773
1659
|
return void 0;
|
|
1774
|
-
} else
|
|
1775
|
-
|
|
1776
|
-
`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)}`
|
|
1777
1663
|
);
|
|
1664
|
+
throw new Error(`Unrecognized element type`);
|
|
1665
|
+
}
|
|
1778
1666
|
}
|
|
1779
1667
|
var mkField = (register, fhirSchema, path, element, logger) => {
|
|
1780
1668
|
let binding;
|
|
@@ -1786,7 +1674,7 @@ var mkField = (register, fhirSchema, path, element, logger) => {
|
|
|
1786
1674
|
}
|
|
1787
1675
|
}
|
|
1788
1676
|
return {
|
|
1789
|
-
type: buildFieldType(register, fhirSchema, element, logger),
|
|
1677
|
+
type: buildFieldType(register, fhirSchema, path, element, logger),
|
|
1790
1678
|
required: isRequired(register, fhirSchema, path),
|
|
1791
1679
|
excluded: isExcluded(register, fhirSchema, path),
|
|
1792
1680
|
reference: buildReferences(element, register, fhirSchema.package_meta),
|
|
@@ -1881,7 +1769,7 @@ function buildEnum(register, element, logger) {
|
|
|
1881
1769
|
function generateBindingSchema(register, fhirSchema, path, element, logger) {
|
|
1882
1770
|
if (!element.binding?.valueSet) return void 0;
|
|
1883
1771
|
const identifier = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
|
|
1884
|
-
const fieldType = buildFieldType(register, fhirSchema, element, logger);
|
|
1772
|
+
const fieldType = buildFieldType(register, fhirSchema, path, element, logger);
|
|
1885
1773
|
const valueSetIdentifier = mkValueSetIdentifierByUrl(register, element.binding.valueSet);
|
|
1886
1774
|
const dependencies = [];
|
|
1887
1775
|
if (fieldType) {
|
|
@@ -1935,26 +1823,10 @@ function collectBindingSchemas(register, fhirSchema, logger) {
|
|
|
1935
1823
|
// src/typeschema/core/transformer.ts
|
|
1936
1824
|
function mkFields(register, fhirSchema, parentPath, elements, logger) {
|
|
1937
1825
|
if (!elements) return void 0;
|
|
1938
|
-
const geneology = register.resolveFsGenealogy(fhirSchema.url);
|
|
1939
|
-
const elems = {};
|
|
1940
|
-
for (const [key, elem] of Object.entries(elements)) {
|
|
1941
|
-
const path = [...parentPath, key];
|
|
1942
|
-
const elemGeneology = resolveFsElementGenealogy(geneology, path);
|
|
1943
|
-
const elemSnapshot = fsElementSnapshot(elemGeneology);
|
|
1944
|
-
elems[key] = { elem, elemSnapshot, path };
|
|
1945
|
-
}
|
|
1946
|
-
for (const [_key, { elem, elemSnapshot, path }] of Object.entries(elems)) {
|
|
1947
|
-
for (const choiceKey of elem?.choices || []) {
|
|
1948
|
-
if (!elems[choiceKey]) {
|
|
1949
|
-
const path2 = [...parentPath, choiceKey];
|
|
1950
|
-
const elemGeneology = resolveFsElementGenealogy(geneology, path2);
|
|
1951
|
-
const elemSnapshot2 = fsElementSnapshot(elemGeneology);
|
|
1952
|
-
elems[choiceKey] = { elem: void 0, elemSnapshot: elemSnapshot2, path: path2 };
|
|
1953
|
-
}
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
1956
1826
|
const fields = {};
|
|
1957
|
-
for (const
|
|
1827
|
+
for (const key of register.getAllElementKeys(elements)) {
|
|
1828
|
+
const path = [...parentPath, key];
|
|
1829
|
+
const elemSnapshot = register.resolveElementSnapshot(fhirSchema, path);
|
|
1958
1830
|
if (isNestedElement(elemSnapshot)) {
|
|
1959
1831
|
fields[key] = mkNestedField(register, fhirSchema, path, elemSnapshot, logger);
|
|
1960
1832
|
} else {
|
|
@@ -2464,6 +2336,175 @@ new CodegenLogger();
|
|
|
2464
2336
|
function createLogger(options = {}) {
|
|
2465
2337
|
return new CodegenLogger(options);
|
|
2466
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
|
+
}
|
|
2467
2508
|
|
|
2468
2509
|
// src/typeschema/generator.ts
|
|
2469
2510
|
var TypeSchemaGenerator = class {
|
|
@@ -2521,7 +2562,7 @@ var TypeSchemaGenerator = class {
|
|
|
2521
2562
|
if (valueSets.length > 0) {
|
|
2522
2563
|
this.logger?.debug(`${valueSets.length} ValueSets available for enum extraction`);
|
|
2523
2564
|
}
|
|
2524
|
-
const register = await registerFromManager(this.manager, logger);
|
|
2565
|
+
const register = await registerFromManager(this.manager, { logger: this.logger });
|
|
2525
2566
|
const valueSetSchemas = [];
|
|
2526
2567
|
if (valueSets.length > 0) {
|
|
2527
2568
|
this.logger?.progress(`Converting ${valueSets.length} ValueSets to TypeSchema`);
|
|
@@ -2945,6 +2986,33 @@ var generateTypeSchemas = async (register, logger) => {
|
|
|
2945
2986
|
return fhirSchemas;
|
|
2946
2987
|
};
|
|
2947
2988
|
|
|
2989
|
+
// src/api/writer-generator/utils.ts
|
|
2990
|
+
var words = (s) => {
|
|
2991
|
+
return s.split(/(?<=[a-z])(?=[A-Z])|[-_.\s]/).filter(Boolean);
|
|
2992
|
+
};
|
|
2993
|
+
var kebabCase = (s) => {
|
|
2994
|
+
return words(s).map((s2) => s2.toLowerCase()).join("-");
|
|
2995
|
+
};
|
|
2996
|
+
var capitalCase = (s) => {
|
|
2997
|
+
if (s.length === 0) throw new Error("Empty string");
|
|
2998
|
+
return s[0]?.toUpperCase() + s.substring(1).toLowerCase();
|
|
2999
|
+
};
|
|
3000
|
+
var camelCase = (s) => {
|
|
3001
|
+
if (s.length === 0) throw new Error("Empty string");
|
|
3002
|
+
const [first, ...rest] = words(s);
|
|
3003
|
+
return [first?.toLowerCase(), ...rest.map(capitalCase)].join("");
|
|
3004
|
+
};
|
|
3005
|
+
var pascalCase = (s) => {
|
|
3006
|
+
return words(s).map(capitalCase).join("");
|
|
3007
|
+
};
|
|
3008
|
+
var uppercaseFirstLetter = (str) => {
|
|
3009
|
+
if (!str || str.length === 0) return str;
|
|
3010
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
3011
|
+
};
|
|
3012
|
+
var uppercaseFirstLetterOfEach = (strings) => {
|
|
3013
|
+
return strings.map((str) => uppercaseFirstLetter(str));
|
|
3014
|
+
};
|
|
3015
|
+
|
|
2948
3016
|
// src/typeschema/utils.ts
|
|
2949
3017
|
var groupByPackages = (typeSchemas) => {
|
|
2950
3018
|
const grouped = {};
|
|
@@ -2987,7 +3055,7 @@ var resourceRelatives = (schemas) => {
|
|
|
2987
3055
|
}
|
|
2988
3056
|
return allPairs;
|
|
2989
3057
|
};
|
|
2990
|
-
var mkTypeSchemaIndex = (schemas) => {
|
|
3058
|
+
var mkTypeSchemaIndex = (schemas, logger) => {
|
|
2991
3059
|
const index = {};
|
|
2992
3060
|
const append = (schema) => {
|
|
2993
3061
|
const url = schema.identifier.url;
|
|
@@ -3008,7 +3076,7 @@ var mkTypeSchemaIndex = (schemas) => {
|
|
|
3008
3076
|
const resourceChildren = (id) => {
|
|
3009
3077
|
return relations.filter((relative2) => relative2.parent.name === id.name).map((relative2) => relative2.child);
|
|
3010
3078
|
};
|
|
3011
|
-
const
|
|
3079
|
+
const tryHierarchy = (schema) => {
|
|
3012
3080
|
const res = [];
|
|
3013
3081
|
let cur = schema;
|
|
3014
3082
|
while (cur) {
|
|
@@ -3017,18 +3085,23 @@ var mkTypeSchemaIndex = (schemas) => {
|
|
|
3017
3085
|
if (base === void 0) break;
|
|
3018
3086
|
const resolved = resolve2(base);
|
|
3019
3087
|
if (!resolved) {
|
|
3020
|
-
|
|
3021
|
-
`Failed to resolve base type: ${res.map((e) => `${e.identifier.url} (${e.identifier.kind})`).join(", ")}`
|
|
3022
|
-
);
|
|
3088
|
+
return void 0;
|
|
3023
3089
|
}
|
|
3024
3090
|
cur = resolved;
|
|
3025
3091
|
}
|
|
3026
3092
|
return res;
|
|
3027
3093
|
};
|
|
3094
|
+
const hierarchy = (schema) => {
|
|
3095
|
+
const genealogy = tryHierarchy(schema);
|
|
3096
|
+
if (genealogy === void 0) {
|
|
3097
|
+
throw new Error(`Failed to resolve base type: ${schema.identifier.url} (${schema.identifier.kind})`);
|
|
3098
|
+
}
|
|
3099
|
+
return genealogy;
|
|
3100
|
+
};
|
|
3028
3101
|
const findLastSpecialization = (schema) => {
|
|
3029
3102
|
const nonConstraintSchema = hierarchy(schema).find((s) => s.identifier.kind !== "profile");
|
|
3030
3103
|
if (!nonConstraintSchema) {
|
|
3031
|
-
throw new Error(`No non-constraint schema found in hierarchy for ${schema.identifier.name}`);
|
|
3104
|
+
throw new Error(`No non-constraint schema found in hierarchy for: ${schema.identifier.name}`);
|
|
3032
3105
|
}
|
|
3033
3106
|
return nonConstraintSchema;
|
|
3034
3107
|
};
|
|
@@ -3068,7 +3141,9 @@ var mkTypeSchemaIndex = (schemas) => {
|
|
|
3068
3141
|
};
|
|
3069
3142
|
};
|
|
3070
3143
|
const isWithMetaField = (profile) => {
|
|
3071
|
-
|
|
3144
|
+
const genealogy = tryHierarchy(profile);
|
|
3145
|
+
if (!genealogy) return false;
|
|
3146
|
+
return genealogy.filter(isSpecializationTypeSchema).some((schema) => {
|
|
3072
3147
|
return schema.fields?.meta !== void 0;
|
|
3073
3148
|
});
|
|
3074
3149
|
};
|
|
@@ -3081,6 +3156,7 @@ var mkTypeSchemaIndex = (schemas) => {
|
|
|
3081
3156
|
collectProfiles: () => schemas.filter(isProfileTypeSchema),
|
|
3082
3157
|
resolve: resolve2,
|
|
3083
3158
|
resourceChildren,
|
|
3159
|
+
tryHierarchy,
|
|
3084
3160
|
hierarchy,
|
|
3085
3161
|
findLastSpecialization,
|
|
3086
3162
|
findLastSpecializationByIdentifier,
|
|
@@ -4162,8 +4238,8 @@ function toCamelCase(str) {
|
|
|
4162
4238
|
return str.replace(/[-_\s]+(.)?/g, (_, char) => char?.toUpperCase() || "");
|
|
4163
4239
|
}
|
|
4164
4240
|
function toPascalCase(str) {
|
|
4165
|
-
const
|
|
4166
|
-
return
|
|
4241
|
+
const camelCase2 = toCamelCase(str);
|
|
4242
|
+
return camelCase2.charAt(0).toUpperCase() + camelCase2.slice(1);
|
|
4167
4243
|
}
|
|
4168
4244
|
function toSnakeCase(str) {
|
|
4169
4245
|
return str.replace(/([A-Z])/g, "_$1").replace(/[-\s]+/g, "_").toLowerCase().replace(/^_/, "");
|
|
@@ -5112,28 +5188,6 @@ ${nestedInterfaces}`;
|
|
|
5112
5188
|
);
|
|
5113
5189
|
}
|
|
5114
5190
|
};
|
|
5115
|
-
|
|
5116
|
-
// src/api/writer-generator/utils.ts
|
|
5117
|
-
var words = (s) => {
|
|
5118
|
-
return s.split(/(?<=[a-z])(?=[A-Z])|[-_.\s]/).filter(Boolean);
|
|
5119
|
-
};
|
|
5120
|
-
var kebabCase = (s) => {
|
|
5121
|
-
return words(s).map((s2) => s2.toLowerCase()).join("-");
|
|
5122
|
-
};
|
|
5123
|
-
var capitalCase = (s) => {
|
|
5124
|
-
if (s.length === 0) throw new Error("Empty string");
|
|
5125
|
-
return s[0]?.toUpperCase() + s.substring(1).toLowerCase();
|
|
5126
|
-
};
|
|
5127
|
-
var pascalCase = (s) => {
|
|
5128
|
-
return words(s).map(capitalCase).join("");
|
|
5129
|
-
};
|
|
5130
|
-
var uppercaseFirstLetter = (str) => {
|
|
5131
|
-
if (!str || str.length === 0) return str;
|
|
5132
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
5133
|
-
};
|
|
5134
|
-
var uppercaseFirstLetterOfEach = (strings) => {
|
|
5135
|
-
return strings.map((str) => uppercaseFirstLetter(str));
|
|
5136
|
-
};
|
|
5137
5191
|
var FileSystemWriter = class {
|
|
5138
5192
|
opts;
|
|
5139
5193
|
currentDir;
|
|
@@ -5669,6 +5723,7 @@ var writerToGenerator = (writerGen) => {
|
|
|
5669
5723
|
build: async (_schemas) => getGeneratedFiles()
|
|
5670
5724
|
};
|
|
5671
5725
|
};
|
|
5726
|
+
var normalizeFileName = (str) => str.replace(/[^a-zA-Z0-9]/g, "");
|
|
5672
5727
|
var APIBuilder = class {
|
|
5673
5728
|
schemas = [];
|
|
5674
5729
|
options;
|
|
@@ -5689,7 +5744,8 @@ var APIBuilder = class {
|
|
|
5689
5744
|
cleanOutput: options.cleanOutput ?? true,
|
|
5690
5745
|
typeSchemaConfig: options.typeSchemaConfig,
|
|
5691
5746
|
manager: options.manager || null,
|
|
5692
|
-
throwException: options.throwException || false
|
|
5747
|
+
throwException: options.throwException || false,
|
|
5748
|
+
typeSchemaOutputDir: options.typeSchemaOutputDir
|
|
5693
5749
|
};
|
|
5694
5750
|
this.typeSchemaConfig = options.typeSchemaConfig;
|
|
5695
5751
|
this.logger = options.logger || createLogger({
|
|
@@ -5792,6 +5848,45 @@ var APIBuilder = class {
|
|
|
5792
5848
|
this.options.cleanOutput = enabled;
|
|
5793
5849
|
return this;
|
|
5794
5850
|
}
|
|
5851
|
+
writeTypeSchemas(target) {
|
|
5852
|
+
this.options.typeSchemaOutputDir = target;
|
|
5853
|
+
return this;
|
|
5854
|
+
}
|
|
5855
|
+
async tryWriteTypeSchema(typeSchemas) {
|
|
5856
|
+
if (!this.options.typeSchemaOutputDir) return;
|
|
5857
|
+
try {
|
|
5858
|
+
if (this.options.cleanOutput) fs.rmSync(this.options.typeSchemaOutputDir, { recursive: true, force: true });
|
|
5859
|
+
await afs.mkdir(this.options.typeSchemaOutputDir, { recursive: true });
|
|
5860
|
+
let writtenCount = 0;
|
|
5861
|
+
let overrideCount = 0;
|
|
5862
|
+
const usedNames = {};
|
|
5863
|
+
this.logger.info(`Writing TypeSchema files to ${this.options.typeSchemaOutputDir}...`);
|
|
5864
|
+
for (const ts of typeSchemas) {
|
|
5865
|
+
const package_name = camelCase(ts.identifier.package.replaceAll("/", "-"));
|
|
5866
|
+
const name = normalizeFileName(ts.identifier.name.toString());
|
|
5867
|
+
const baseName = Path2.join(this.options.typeSchemaOutputDir, package_name, name);
|
|
5868
|
+
let fullName;
|
|
5869
|
+
if (usedNames[baseName] !== void 0) {
|
|
5870
|
+
usedNames[baseName]++;
|
|
5871
|
+
fullName = `${baseName}-${usedNames[baseName]}.typeschema.json`;
|
|
5872
|
+
} else {
|
|
5873
|
+
usedNames[baseName] = 0;
|
|
5874
|
+
fullName = `${baseName}.typeschema.json`;
|
|
5875
|
+
}
|
|
5876
|
+
await afs.mkdir(Path2.dirname(fullName), { recursive: true });
|
|
5877
|
+
afs.writeFile(fullName, JSON.stringify(ts, null, 2));
|
|
5878
|
+
if (await afs.exists(fullName)) overrideCount++;
|
|
5879
|
+
else writtenCount++;
|
|
5880
|
+
}
|
|
5881
|
+
this.logger.info(`Created ${writtenCount} new TypeSchema files, overrode ${overrideCount} files`);
|
|
5882
|
+
} catch (error) {
|
|
5883
|
+
if (this.options.throwException) throw error;
|
|
5884
|
+
this.logger.error(
|
|
5885
|
+
"Failed to write TypeSchema output",
|
|
5886
|
+
error instanceof Error ? error : new Error(String(error))
|
|
5887
|
+
);
|
|
5888
|
+
}
|
|
5889
|
+
}
|
|
5795
5890
|
async generate() {
|
|
5796
5891
|
const startTime = performance.now();
|
|
5797
5892
|
const result = {
|
|
@@ -5821,8 +5916,9 @@ var APIBuilder = class {
|
|
|
5821
5916
|
workingDir: "tmp/fhir"
|
|
5822
5917
|
});
|
|
5823
5918
|
await manager.init();
|
|
5824
|
-
const register = await registerFromManager(manager, this.logger);
|
|
5919
|
+
const register = await registerFromManager(manager, { logger: this.logger });
|
|
5825
5920
|
const typeSchemas = await generateTypeSchemas(register, this.logger);
|
|
5921
|
+
await this.tryWriteTypeSchema(typeSchemas);
|
|
5826
5922
|
this.logger.debug(`Executing ${this.generators.size} generators`);
|
|
5827
5923
|
await this.executeGenerators(result, typeSchemas);
|
|
5828
5924
|
this.logger.info("Generation completed successfully");
|