@atomic-ehr/codegen 0.0.1-canary.20251006094042.7f0be72 → 0.0.1-canary.20251007114955.88a8afc
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 +22 -19
- package/dist/index.d.ts +250 -257
- package/dist/index.js +1027 -603
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
|
|
2
|
+
import * as fhirschema from '@atomic-ehr/fhirschema';
|
|
3
|
+
import * as fs from 'fs';
|
|
1
4
|
import { existsSync, mkdirSync } from 'fs';
|
|
2
5
|
import { readdir, stat, unlink, readFile, writeFile, access, mkdir, rm } from 'fs/promises';
|
|
6
|
+
import * as Path2 from 'path';
|
|
3
7
|
import { join, resolve, dirname, relative } from 'path';
|
|
4
|
-
import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
|
|
5
|
-
import * as fhirschema from '@atomic-ehr/fhirschema';
|
|
6
8
|
import pc from 'picocolors';
|
|
7
9
|
|
|
8
10
|
var __defProp = Object.defineProperty;
|
|
@@ -1391,424 +1393,127 @@ var init_IndexBuilder = __esm({
|
|
|
1391
1393
|
};
|
|
1392
1394
|
}
|
|
1393
1395
|
});
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
cacheDir: ".typeschema-cache",
|
|
1402
|
-
maxAge: 24 * 60 * 60 * 1e3,
|
|
1403
|
-
// 24 hours
|
|
1404
|
-
validateCached: true,
|
|
1405
|
-
...config
|
|
1406
|
-
};
|
|
1407
|
-
if (this.config.enablePersistence && this.config.cacheDir) {
|
|
1408
|
-
this.cacheDir = this.config.cacheDir;
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
/**
|
|
1412
|
-
* Store a schema in the cache
|
|
1413
|
-
*/
|
|
1414
|
-
async set(schema) {
|
|
1415
|
-
const key = this.generateKey(schema.identifier);
|
|
1416
|
-
this.cache.set(key, schema);
|
|
1417
|
-
if (this.config.enablePersistence && this.cacheDir) {
|
|
1418
|
-
await this.persistSchema(schema);
|
|
1419
|
-
}
|
|
1420
|
-
}
|
|
1421
|
-
/**
|
|
1422
|
-
* Retrieve a schema by identifier
|
|
1423
|
-
*/
|
|
1424
|
-
get(identifier) {
|
|
1425
|
-
const key = this.generateKey(identifier);
|
|
1426
|
-
return this.cache.get(key) || null;
|
|
1427
|
-
}
|
|
1428
|
-
/**
|
|
1429
|
-
* Retrieve a schema by URL
|
|
1430
|
-
*/
|
|
1431
|
-
getByUrl(url) {
|
|
1432
|
-
for (const schema of this.cache.values()) {
|
|
1433
|
-
if (schema.identifier.url === url) {
|
|
1434
|
-
return schema;
|
|
1435
|
-
}
|
|
1436
|
-
}
|
|
1437
|
-
return null;
|
|
1438
|
-
}
|
|
1439
|
-
/**
|
|
1440
|
-
* Check if a schema exists in cache
|
|
1441
|
-
*/
|
|
1442
|
-
has(identifier) {
|
|
1443
|
-
const key = this.generateKey(identifier);
|
|
1444
|
-
return this.cache.has(key);
|
|
1445
|
-
}
|
|
1446
|
-
/**
|
|
1447
|
-
* Check if a schema exists by URL
|
|
1448
|
-
*/
|
|
1449
|
-
hasByUrl(url) {
|
|
1450
|
-
for (const schema of this.cache.values()) {
|
|
1451
|
-
if (schema.identifier.url === url) {
|
|
1452
|
-
return true;
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1455
|
-
return false;
|
|
1456
|
-
}
|
|
1457
|
-
/**
|
|
1458
|
-
* Delete a schema from cache
|
|
1459
|
-
*/
|
|
1460
|
-
delete(identifier) {
|
|
1461
|
-
const key = this.generateKey(identifier);
|
|
1462
|
-
return this.cache.delete(key);
|
|
1463
|
-
}
|
|
1464
|
-
/**
|
|
1465
|
-
* Delete a schema by URL
|
|
1466
|
-
*/
|
|
1467
|
-
deleteByUrl(url) {
|
|
1468
|
-
for (const [key, schema] of this.cache.entries()) {
|
|
1469
|
-
if (schema.identifier.url === url) {
|
|
1470
|
-
return this.cache.delete(key);
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
return false;
|
|
1474
|
-
}
|
|
1475
|
-
/**
|
|
1476
|
-
* Get schemas by package
|
|
1477
|
-
*/
|
|
1478
|
-
getByPackage(packageName) {
|
|
1479
|
-
const results = [];
|
|
1480
|
-
for (const schema of this.cache.values()) {
|
|
1481
|
-
if (schema.identifier.package === packageName) {
|
|
1482
|
-
results.push(schema);
|
|
1483
|
-
}
|
|
1484
|
-
}
|
|
1485
|
-
return results;
|
|
1486
|
-
}
|
|
1487
|
-
/**
|
|
1488
|
-
* Get schemas by kind
|
|
1489
|
-
*/
|
|
1490
|
-
getByKind(kind) {
|
|
1491
|
-
const results = [];
|
|
1492
|
-
for (const schema of this.cache.values()) {
|
|
1493
|
-
if (schema.identifier.kind === kind) {
|
|
1494
|
-
results.push(schema);
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
return results;
|
|
1498
|
-
}
|
|
1499
|
-
/**
|
|
1500
|
-
* Store multiple schemas
|
|
1501
|
-
*/
|
|
1502
|
-
setMany(schemas) {
|
|
1503
|
-
for (const schema of schemas) {
|
|
1504
|
-
this.set(schema);
|
|
1505
|
-
}
|
|
1506
|
-
}
|
|
1507
|
-
/**
|
|
1508
|
-
* Clear all cached schemas
|
|
1509
|
-
*/
|
|
1510
|
-
clear() {
|
|
1511
|
-
this.cache.clear();
|
|
1512
|
-
}
|
|
1513
|
-
/**
|
|
1514
|
-
* Generate cache key for identifier
|
|
1515
|
-
*/
|
|
1516
|
-
generateKey(identifier) {
|
|
1517
|
-
return `${identifier.package}:${identifier.version}:${identifier.kind}:${identifier.name}`;
|
|
1396
|
+
|
|
1397
|
+
// src/typeschema/types.ts
|
|
1398
|
+
var packageMetaToFhir = (packageMeta) => `${packageMeta.name}#${packageMeta.version}`;
|
|
1399
|
+
var packageMetaToNpm = (packageMeta) => `${packageMeta.name}@${packageMeta.version}`;
|
|
1400
|
+
var enrichFHIRSchema = (schema, packageMeta) => {
|
|
1401
|
+
if (!packageMeta) {
|
|
1402
|
+
packageMeta = { name: "undefined", version: "undefined" };
|
|
1518
1403
|
}
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1404
|
+
return {
|
|
1405
|
+
...schema,
|
|
1406
|
+
package_meta: schema.package_meta || packageMeta,
|
|
1407
|
+
name: schema.name,
|
|
1408
|
+
url: schema.url,
|
|
1409
|
+
base: schema.base
|
|
1410
|
+
};
|
|
1411
|
+
};
|
|
1412
|
+
function isBindingSchema(schema) {
|
|
1413
|
+
return schema.identifier.kind === "binding";
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
// src/typeschema/register.ts
|
|
1417
|
+
var registerFromManager = async (manager, logger) => {
|
|
1418
|
+
const packages = await manager.packages();
|
|
1419
|
+
const flatRawIndex = {};
|
|
1420
|
+
const indexByPackages = [];
|
|
1421
|
+
for (const pkg of packages) {
|
|
1422
|
+
const resources = await manager.search({ package: pkg });
|
|
1423
|
+
const index = {};
|
|
1424
|
+
for (const resource of resources) {
|
|
1425
|
+
const url = resource.url;
|
|
1426
|
+
if (!url) continue;
|
|
1427
|
+
index[url] = resource;
|
|
1428
|
+
flatRawIndex[url] = resource;
|
|
1528
1429
|
}
|
|
1430
|
+
indexByPackages.push({
|
|
1431
|
+
package_meta: pkg,
|
|
1432
|
+
index
|
|
1433
|
+
});
|
|
1529
1434
|
}
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
const
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
const stats = await stat(filePath);
|
|
1543
|
-
if (this.config.maxAge) {
|
|
1544
|
-
const age = Date.now() - stats.mtimeMs;
|
|
1545
|
-
if (age > this.config.maxAge) {
|
|
1546
|
-
await unlink(filePath);
|
|
1547
|
-
continue;
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1435
|
+
const sdIndex = {};
|
|
1436
|
+
const vsIndex = {};
|
|
1437
|
+
const fsIndex = {};
|
|
1438
|
+
const nameToCanonical = {};
|
|
1439
|
+
let [fsSuccess, fsFailed] = [0, 0];
|
|
1440
|
+
for (const resourcesByPackage of indexByPackages) {
|
|
1441
|
+
const packageMeta = resourcesByPackage.package_meta;
|
|
1442
|
+
for (const [surl, resource] of Object.entries(resourcesByPackage.index)) {
|
|
1443
|
+
const url = surl;
|
|
1444
|
+
if (resource.resourceType === "StructureDefinition") {
|
|
1445
|
+
const sd = resource;
|
|
1446
|
+
sdIndex[url] = sd;
|
|
1550
1447
|
try {
|
|
1551
|
-
const
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
continue;
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
const key = this.generateKey(metadata.schema.identifier);
|
|
1559
|
-
this.cache.set(key, metadata.schema);
|
|
1448
|
+
const rfs = enrichFHIRSchema(fhirschema.translate(sd), packageMeta);
|
|
1449
|
+
fsIndex[rfs.url] = rfs;
|
|
1450
|
+
nameToCanonical[rfs.name] = rfs.url;
|
|
1451
|
+
fsSuccess++;
|
|
1560
1452
|
} catch (error) {
|
|
1561
|
-
|
|
1453
|
+
logger?.warn(
|
|
1454
|
+
`Failed to convert StructureDefinition ${sd.name || sd.id}: ${error instanceof Error ? error.message : String(error)}`
|
|
1455
|
+
);
|
|
1456
|
+
fsFailed++;
|
|
1562
1457
|
}
|
|
1563
1458
|
}
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
*/
|
|
1571
|
-
async persistSchema(schema) {
|
|
1572
|
-
if (!this.cacheDir) {
|
|
1573
|
-
return;
|
|
1574
|
-
}
|
|
1575
|
-
if (!existsSync(this.cacheDir)) {
|
|
1576
|
-
mkdirSync(this.cacheDir, { recursive: true });
|
|
1459
|
+
if (resource.resourceType === "ValueSet") {
|
|
1460
|
+
if (!resource.package_meta) {
|
|
1461
|
+
resource.package_meta = packageMeta;
|
|
1462
|
+
}
|
|
1463
|
+
vsIndex[resource.url] = resource;
|
|
1464
|
+
}
|
|
1577
1465
|
}
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
timestamp: Date.now(),
|
|
1581
|
-
version: schema.identifier.version
|
|
1582
|
-
};
|
|
1583
|
-
const fileName = `${schema.identifier.package}-${schema.identifier.version}-${schema.identifier.kind}-${schema.identifier.name}.typeschema.json`.replace(
|
|
1584
|
-
/[^a-zA-Z0-9.-]/g,
|
|
1585
|
-
"_"
|
|
1466
|
+
logger?.success(
|
|
1467
|
+
`FHIR Schema conversion for '${packageMetaToFhir(packageMeta)}' completed: ${fsSuccess} successful, ${fsFailed} failed`
|
|
1586
1468
|
);
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1469
|
+
}
|
|
1470
|
+
const complexTypes = {};
|
|
1471
|
+
for (const fs2 of Object.values(fsIndex)) {
|
|
1472
|
+
if (fs2.kind === "complex-type") {
|
|
1473
|
+
complexTypes[fs2.url] = fs2;
|
|
1592
1474
|
}
|
|
1593
1475
|
}
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1476
|
+
const resolveFsGenealogy = (canonicalUrl) => {
|
|
1477
|
+
let fs2 = fsIndex[canonicalUrl];
|
|
1478
|
+
if (fs2 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
|
|
1479
|
+
const genealogy = [fs2];
|
|
1480
|
+
while (fs2?.base) {
|
|
1481
|
+
fs2 = fsIndex[fs2.base] || fsIndex[nameToCanonical[fs2.base]];
|
|
1482
|
+
genealogy.push(fs2);
|
|
1483
|
+
if (fs2 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
|
|
1600
1484
|
}
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1485
|
+
return genealogy;
|
|
1486
|
+
};
|
|
1487
|
+
return {
|
|
1488
|
+
...manager,
|
|
1489
|
+
appendFs(fs2) {
|
|
1490
|
+
const rfs = enrichFHIRSchema(fs2);
|
|
1491
|
+
fsIndex[rfs.url] = rfs;
|
|
1492
|
+
nameToCanonical[rfs.name] = rfs.url;
|
|
1493
|
+
},
|
|
1494
|
+
resolveFs: (canonicalUrl) => fsIndex[canonicalUrl],
|
|
1495
|
+
resolveFsGenealogy,
|
|
1496
|
+
ensureCanonicalUrl: (name) => nameToCanonical[name] || name,
|
|
1497
|
+
allSd: () => Object.values(sdIndex),
|
|
1498
|
+
resolveSd: (canonicalUrl) => sdIndex[canonicalUrl],
|
|
1499
|
+
allFs: () => Object.values(fsIndex),
|
|
1500
|
+
allVs: () => Object.values(vsIndex),
|
|
1501
|
+
resolveVs: (canonicalUrl) => vsIndex[canonicalUrl],
|
|
1502
|
+
complexTypeDict: () => complexTypes,
|
|
1503
|
+
resolveAny: (canonicalUrl) => flatRawIndex[canonicalUrl]
|
|
1504
|
+
};
|
|
1505
|
+
};
|
|
1506
|
+
var resolveFsElementGenealogy = (genealogy, path) => {
|
|
1507
|
+
const [top, ...rest] = path;
|
|
1508
|
+
if (top === void 0) return [];
|
|
1509
|
+
return genealogy.map((fs2) => {
|
|
1510
|
+
if (!fs2.elements) return void 0;
|
|
1511
|
+
let elem = fs2.elements?.[top];
|
|
1512
|
+
for (const k of rest) {
|
|
1513
|
+
elem = elem?.elements?.[k];
|
|
1609
1514
|
}
|
|
1610
|
-
|
|
1611
|
-
};
|
|
1612
|
-
var CodegenLogger = class _CodegenLogger {
|
|
1613
|
-
options;
|
|
1614
|
-
constructor(options = {}) {
|
|
1615
|
-
this.options = {
|
|
1616
|
-
timestamp: false,
|
|
1617
|
-
verbose: false,
|
|
1618
|
-
...options
|
|
1619
|
-
};
|
|
1620
|
-
}
|
|
1621
|
-
formatMessage(level, message, color) {
|
|
1622
|
-
const timestamp = this.options.timestamp ? `${pc.gray((/* @__PURE__ */ new Date()).toLocaleTimeString())} ` : "";
|
|
1623
|
-
const prefix = this.options.prefix ? `${pc.cyan(`[${this.options.prefix}]`)} ` : "";
|
|
1624
|
-
return `${timestamp}${color(level)} ${prefix}${message}`;
|
|
1625
|
-
}
|
|
1626
|
-
/**
|
|
1627
|
-
* Success message with checkmark
|
|
1628
|
-
*/
|
|
1629
|
-
success(message) {
|
|
1630
|
-
console.log(this.formatMessage("\u2705", message, pc.green));
|
|
1631
|
-
}
|
|
1632
|
-
/**
|
|
1633
|
-
* Error message with X mark
|
|
1634
|
-
*/
|
|
1635
|
-
error(message, error) {
|
|
1636
|
-
console.error(this.formatMessage("\u274C", message, pc.red));
|
|
1637
|
-
if (error && this.options.verbose) {
|
|
1638
|
-
console.error(pc.red(` ${error.message}`));
|
|
1639
|
-
if (error.stack) {
|
|
1640
|
-
console.error(pc.gray(error.stack));
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
/**
|
|
1645
|
-
* Warning message with warning sign
|
|
1646
|
-
*/
|
|
1647
|
-
warn(message) {
|
|
1648
|
-
console.warn(this.formatMessage("\u26A0\uFE0F", message, pc.yellow));
|
|
1649
|
-
}
|
|
1650
|
-
/**
|
|
1651
|
-
* Info message with info icon
|
|
1652
|
-
*/
|
|
1653
|
-
info(message) {
|
|
1654
|
-
console.log(this.formatMessage("\u2139\uFE0F", message, pc.blue));
|
|
1655
|
-
}
|
|
1656
|
-
/**
|
|
1657
|
-
* Debug message (only shows in verbose mode)
|
|
1658
|
-
*/
|
|
1659
|
-
debug(message) {
|
|
1660
|
-
if (this.options.verbose) {
|
|
1661
|
-
console.log(this.formatMessage("\u{1F41B}", message, pc.magenta));
|
|
1662
|
-
}
|
|
1663
|
-
}
|
|
1664
|
-
/**
|
|
1665
|
-
* Step message with rocket
|
|
1666
|
-
*/
|
|
1667
|
-
step(message) {
|
|
1668
|
-
console.log(this.formatMessage("\u{1F680}", message, pc.cyan));
|
|
1669
|
-
}
|
|
1670
|
-
/**
|
|
1671
|
-
* Progress message with clock
|
|
1672
|
-
*/
|
|
1673
|
-
progress(message) {
|
|
1674
|
-
console.log(this.formatMessage("\u23F3", message, pc.blue));
|
|
1675
|
-
}
|
|
1676
|
-
/**
|
|
1677
|
-
* Plain message (no icon, just colored text)
|
|
1678
|
-
*/
|
|
1679
|
-
plain(message, color = (s) => s) {
|
|
1680
|
-
const timestamp = this.options.timestamp ? `${pc.gray((/* @__PURE__ */ new Date()).toLocaleTimeString())} ` : "";
|
|
1681
|
-
const prefix = this.options.prefix ? `${pc.cyan(`[${this.options.prefix}]`)} ` : "";
|
|
1682
|
-
console.log(`${timestamp}${prefix}${color(message)}`);
|
|
1683
|
-
}
|
|
1684
|
-
/**
|
|
1685
|
-
* Dimmed/gray text for less important info
|
|
1686
|
-
*/
|
|
1687
|
-
dim(message) {
|
|
1688
|
-
this.plain(message, pc.gray);
|
|
1689
|
-
}
|
|
1690
|
-
/**
|
|
1691
|
-
* Create a child logger with a prefix
|
|
1692
|
-
*/
|
|
1693
|
-
child(prefix) {
|
|
1694
|
-
return new _CodegenLogger({
|
|
1695
|
-
...this.options,
|
|
1696
|
-
prefix: this.options.prefix ? `${this.options.prefix}:${prefix}` : prefix
|
|
1697
|
-
});
|
|
1698
|
-
}
|
|
1699
|
-
/**
|
|
1700
|
-
* Update options
|
|
1701
|
-
*/
|
|
1702
|
-
configure(options) {
|
|
1703
|
-
this.options = { ...this.options, ...options };
|
|
1704
|
-
}
|
|
1705
|
-
};
|
|
1706
|
-
new CodegenLogger();
|
|
1707
|
-
function createLogger(options = {}) {
|
|
1708
|
-
return new CodegenLogger(options);
|
|
1709
|
-
}
|
|
1710
|
-
|
|
1711
|
-
// src/typeschema/types.ts
|
|
1712
|
-
var enrichFHIRSchema = (schema, packageMeta) => {
|
|
1713
|
-
if (!packageMeta) {
|
|
1714
|
-
packageMeta = { name: "undefined", version: "undefined" };
|
|
1715
|
-
}
|
|
1716
|
-
return {
|
|
1717
|
-
...schema,
|
|
1718
|
-
package_meta: schema.package_meta || packageMeta,
|
|
1719
|
-
name: schema.name,
|
|
1720
|
-
url: schema.url,
|
|
1721
|
-
base: schema.base
|
|
1722
|
-
};
|
|
1723
|
-
};
|
|
1724
|
-
function isBindingSchema(schema) {
|
|
1725
|
-
return schema.identifier.kind === "binding";
|
|
1726
|
-
}
|
|
1727
|
-
|
|
1728
|
-
// src/typeschema/register.ts
|
|
1729
|
-
var registerFromManager = async (manager, logger, packageInfo) => {
|
|
1730
|
-
const resources = await manager.search({});
|
|
1731
|
-
const any = {};
|
|
1732
|
-
for (const resource of resources) {
|
|
1733
|
-
const url = resource.url;
|
|
1734
|
-
if (!url) continue;
|
|
1735
|
-
any[url] = resource;
|
|
1736
|
-
}
|
|
1737
|
-
const structureDefinitions = {};
|
|
1738
|
-
for (const resource of resources) {
|
|
1739
|
-
if (resource.resourceType === "StructureDefinition") {
|
|
1740
|
-
const url = resource.url;
|
|
1741
|
-
structureDefinitions[url] = resource;
|
|
1742
|
-
}
|
|
1743
|
-
}
|
|
1744
|
-
const fhirSchemas = {};
|
|
1745
|
-
const nameDict = {};
|
|
1746
|
-
let [success] = [0, 0];
|
|
1747
|
-
for (const sd of Object.values(structureDefinitions)) {
|
|
1748
|
-
try {
|
|
1749
|
-
const rfs = enrichFHIRSchema(fhirschema.translate(sd), packageInfo);
|
|
1750
|
-
fhirSchemas[rfs.url] = rfs;
|
|
1751
|
-
nameDict[rfs.name] = rfs.url;
|
|
1752
|
-
success++;
|
|
1753
|
-
} catch (error) {
|
|
1754
|
-
}
|
|
1755
|
-
}
|
|
1756
|
-
const valueSets = {};
|
|
1757
|
-
for (const resource of resources) {
|
|
1758
|
-
if (resource.resourceType === "ValueSet") {
|
|
1759
|
-
if (!resource.package_meta) {
|
|
1760
|
-
resource.package_meta = packageInfo;
|
|
1761
|
-
}
|
|
1762
|
-
valueSets[resource.url] = resource;
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
const complexTypes = {};
|
|
1766
|
-
for (const fs of Object.values(fhirSchemas)) {
|
|
1767
|
-
if (fs.kind === "complex-type") {
|
|
1768
|
-
complexTypes[fs.url] = fs;
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
const resolveFsGenealogy = (canonicalUrl) => {
|
|
1772
|
-
let fs = fhirSchemas[canonicalUrl];
|
|
1773
|
-
if (fs === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
|
|
1774
|
-
const genealogy = [fs];
|
|
1775
|
-
while (fs?.base) {
|
|
1776
|
-
fs = fhirSchemas[fs.base] || fhirSchemas[nameDict[fs.base]];
|
|
1777
|
-
genealogy.push(fs);
|
|
1778
|
-
if (fs === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
|
|
1779
|
-
}
|
|
1780
|
-
return genealogy;
|
|
1781
|
-
};
|
|
1782
|
-
return {
|
|
1783
|
-
...manager,
|
|
1784
|
-
appendFs(fs) {
|
|
1785
|
-
const rfs = enrichFHIRSchema(fs);
|
|
1786
|
-
fhirSchemas[rfs.url] = rfs;
|
|
1787
|
-
nameDict[rfs.name] = rfs.url;
|
|
1788
|
-
},
|
|
1789
|
-
resolveFs: (canonicalUrl) => fhirSchemas[canonicalUrl],
|
|
1790
|
-
resolveFsGenealogy,
|
|
1791
|
-
ensureCanonicalUrl: (name) => nameDict[name] || name,
|
|
1792
|
-
allSd: () => Object.values(structureDefinitions),
|
|
1793
|
-
resolveSd: (canonicalUrl) => structureDefinitions[canonicalUrl],
|
|
1794
|
-
allFs: () => Object.values(fhirSchemas),
|
|
1795
|
-
allVs: () => Object.values(valueSets),
|
|
1796
|
-
resolveVs: (canonicalUrl) => valueSets[canonicalUrl],
|
|
1797
|
-
complexTypeDict: () => complexTypes,
|
|
1798
|
-
resolveAny: (canonicalUrl) => any[canonicalUrl]
|
|
1799
|
-
};
|
|
1800
|
-
};
|
|
1801
|
-
var resolveFsElementGenealogy = (genealogy, path) => {
|
|
1802
|
-
const [top, ...rest] = path;
|
|
1803
|
-
if (top === void 0) return [];
|
|
1804
|
-
return genealogy.map((fs) => {
|
|
1805
|
-
if (!fs.elements) return void 0;
|
|
1806
|
-
let elem = fs.elements?.[top];
|
|
1807
|
-
for (const k of rest) {
|
|
1808
|
-
elem = elem?.elements?.[k];
|
|
1809
|
-
}
|
|
1810
|
-
return elem;
|
|
1811
|
-
}).filter((elem) => elem !== void 0);
|
|
1515
|
+
return elem;
|
|
1516
|
+
}).filter((elem) => elem !== void 0);
|
|
1812
1517
|
};
|
|
1813
1518
|
function fsElementSnapshot(genealogy) {
|
|
1814
1519
|
const snapshot = genealogy.reverse().reduce((snapshot2, elem) => ({ ...snapshot2, ...elem }), {});
|
|
@@ -1872,8 +1577,8 @@ function mkValueSetIdentifierByUrl(register, fullValueSetUrl) {
|
|
|
1872
1577
|
const valueSetName = valueSet?.id && !/^[a-zA-Z0-9_-]{20,}$/.test(valueSet.id) ? valueSet.id : valueSetNameFallback;
|
|
1873
1578
|
return {
|
|
1874
1579
|
kind: "value-set",
|
|
1875
|
-
package: valueSet
|
|
1876
|
-
version: valueSet
|
|
1580
|
+
package: valueSet.package_meta.name,
|
|
1581
|
+
version: valueSet.package_meta.version,
|
|
1877
1582
|
name: valueSetName,
|
|
1878
1583
|
url: valueSetUrl
|
|
1879
1584
|
};
|
|
@@ -1891,7 +1596,7 @@ function mkBindingIdentifier(fhirSchema, path, bindingName) {
|
|
|
1891
1596
|
}
|
|
1892
1597
|
|
|
1893
1598
|
// src/typeschema/profile/processor.ts
|
|
1894
|
-
async function transformProfile(register, fhirSchema) {
|
|
1599
|
+
async function transformProfile(register, fhirSchema, logger) {
|
|
1895
1600
|
const identifier = mkIdentifier(fhirSchema);
|
|
1896
1601
|
if (identifier.kind !== "profile") {
|
|
1897
1602
|
throw new Error(`Expected profile, got ${identifier.kind} for ${fhirSchema.name}`);
|
|
@@ -1923,7 +1628,7 @@ async function transformProfile(register, fhirSchema) {
|
|
|
1923
1628
|
profileSchema.metadata = metadata;
|
|
1924
1629
|
}
|
|
1925
1630
|
if (fhirSchema.elements) {
|
|
1926
|
-
const fields = await mkFields(register, fhirSchema, [], fhirSchema.elements);
|
|
1631
|
+
const fields = await mkFields(register, fhirSchema, [], fhirSchema.elements, logger);
|
|
1927
1632
|
if (fields && Object.keys(fields).length > 0) {
|
|
1928
1633
|
profileSchema.fields = fields;
|
|
1929
1634
|
}
|
|
@@ -2062,10 +1767,10 @@ function extractValidationRules(fhirSchema) {
|
|
|
2062
1767
|
function isRequired(register, fhirSchema, path) {
|
|
2063
1768
|
const fieldName = path[path.length - 1];
|
|
2064
1769
|
const parentPath = path.slice(0, -1);
|
|
2065
|
-
const requires = register.resolveFsGenealogy(fhirSchema.url).flatMap((
|
|
2066
|
-
if (parentPath.length === 0) return
|
|
2067
|
-
if (!
|
|
2068
|
-
let elem =
|
|
1770
|
+
const requires = register.resolveFsGenealogy(fhirSchema.url).flatMap((fs2) => {
|
|
1771
|
+
if (parentPath.length === 0) return fs2.required || [];
|
|
1772
|
+
if (!fs2.elements) return [];
|
|
1773
|
+
let elem = fs2;
|
|
2069
1774
|
for (const k of parentPath) {
|
|
2070
1775
|
elem = elem?.elements?.[k];
|
|
2071
1776
|
}
|
|
@@ -2077,10 +1782,10 @@ function isExcluded(register, fhirSchema, path) {
|
|
|
2077
1782
|
const fieldName = path[path.length - 1];
|
|
2078
1783
|
if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
|
|
2079
1784
|
const parentPath = path.slice(0, -1);
|
|
2080
|
-
const requires = register.resolveFsGenealogy(fhirSchema.url).flatMap((
|
|
2081
|
-
if (parentPath.length === 0) return
|
|
2082
|
-
if (!
|
|
2083
|
-
let elem =
|
|
1785
|
+
const requires = register.resolveFsGenealogy(fhirSchema.url).flatMap((fs2) => {
|
|
1786
|
+
if (parentPath.length === 0) return fs2.excluded || [];
|
|
1787
|
+
if (!fs2.elements) return [];
|
|
1788
|
+
let elem = fs2;
|
|
2084
1789
|
for (const k of parentPath) {
|
|
2085
1790
|
elem = elem?.elements?.[k];
|
|
2086
1791
|
}
|
|
@@ -2092,8 +1797,8 @@ var buildReferences = (element, register, _packageInfo) => {
|
|
|
2092
1797
|
if (!element.refers) return void 0;
|
|
2093
1798
|
return element.refers.map((ref) => {
|
|
2094
1799
|
const curl = register.ensureCanonicalUrl(ref);
|
|
2095
|
-
const
|
|
2096
|
-
return mkIdentifier(
|
|
1800
|
+
const fs2 = register.resolveFs(curl);
|
|
1801
|
+
return mkIdentifier(fs2);
|
|
2097
1802
|
});
|
|
2098
1803
|
};
|
|
2099
1804
|
function buildFieldType(register, fhirSchema, element) {
|
|
@@ -2120,13 +1825,13 @@ function buildFieldType(register, fhirSchema, element) {
|
|
|
2120
1825
|
}
|
|
2121
1826
|
return void 0;
|
|
2122
1827
|
}
|
|
2123
|
-
var mkField = (register, fhirSchema, path, element) => {
|
|
1828
|
+
var mkField = (register, fhirSchema, path, element, logger) => {
|
|
2124
1829
|
let binding;
|
|
2125
1830
|
let enumValues;
|
|
2126
1831
|
if (element.binding) {
|
|
2127
1832
|
binding = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
|
|
2128
1833
|
if (element.binding.strength === "required" && element.type === "code") {
|
|
2129
|
-
enumValues = buildEnum(register, element);
|
|
1834
|
+
enumValues = buildEnum(register, element, logger);
|
|
2130
1835
|
}
|
|
2131
1836
|
}
|
|
2132
1837
|
return {
|
|
@@ -2159,13 +1864,13 @@ function mkNestedField(register, fhirSchema, path, element) {
|
|
|
2159
1864
|
}
|
|
2160
1865
|
|
|
2161
1866
|
// src/typeschema/core/binding.ts
|
|
2162
|
-
function extractValueSetConceptsByUrl(register, valueSetUrl) {
|
|
1867
|
+
function extractValueSetConceptsByUrl(register, valueSetUrl, logger) {
|
|
2163
1868
|
const cleanUrl = dropVersionFromUrl(valueSetUrl) || valueSetUrl;
|
|
2164
1869
|
const valueSet = register.resolveVs(cleanUrl);
|
|
2165
1870
|
if (!valueSet) return void 0;
|
|
2166
1871
|
return extractValueSetConcepts(register, valueSet);
|
|
2167
1872
|
}
|
|
2168
|
-
function extractValueSetConcepts(register, valueSet) {
|
|
1873
|
+
function extractValueSetConcepts(register, valueSet, _logger) {
|
|
2169
1874
|
if (valueSet.expansion?.contains) return valueSet.expansion.contains;
|
|
2170
1875
|
const concepts = [];
|
|
2171
1876
|
if (valueSet.compose?.include) {
|
|
@@ -2204,7 +1909,7 @@ function extractValueSetConcepts(register, valueSet) {
|
|
|
2204
1909
|
return concepts.length > 0 ? concepts : void 0;
|
|
2205
1910
|
}
|
|
2206
1911
|
var MAX_ENUM_LENGTH = 100;
|
|
2207
|
-
function buildEnum(register, element) {
|
|
1912
|
+
function buildEnum(register, element, logger) {
|
|
2208
1913
|
if (!element.binding) return void 0;
|
|
2209
1914
|
const strength = element.binding.strength;
|
|
2210
1915
|
const valueSetUrl = element.binding.valueSet;
|
|
@@ -2215,14 +1920,14 @@ function buildEnum(register, element) {
|
|
|
2215
1920
|
if (!concepts || concepts.length === 0) return void 0;
|
|
2216
1921
|
const codes = concepts.map((c) => c.code).filter((code) => code && typeof code === "string" && code.trim().length > 0);
|
|
2217
1922
|
if (codes.length > MAX_ENUM_LENGTH) {
|
|
2218
|
-
|
|
2219
|
-
`Value set ${valueSetUrl} has more than ${MAX_ENUM_LENGTH} codes, which may cause issues with code generation.`
|
|
1923
|
+
logger?.dry_warn(
|
|
1924
|
+
`Value set ${valueSetUrl} has ${codes.length} which is more than ${MAX_ENUM_LENGTH} codes, which may cause issues with code generation.`
|
|
2220
1925
|
);
|
|
2221
1926
|
return void 0;
|
|
2222
1927
|
}
|
|
2223
1928
|
return codes.length > 0 ? codes : void 0;
|
|
2224
1929
|
}
|
|
2225
|
-
function generateBindingSchema(register, fhirSchema, path, element) {
|
|
1930
|
+
function generateBindingSchema(register, fhirSchema, path, element, logger) {
|
|
2226
1931
|
if (!element.binding?.valueSet) return void 0;
|
|
2227
1932
|
const identifier = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
|
|
2228
1933
|
const fieldType = buildFieldType(register, fhirSchema, element);
|
|
@@ -2232,7 +1937,7 @@ function generateBindingSchema(register, fhirSchema, path, element) {
|
|
|
2232
1937
|
dependencies.push(fieldType);
|
|
2233
1938
|
}
|
|
2234
1939
|
dependencies.push(valueSetIdentifier);
|
|
2235
|
-
const enumValues = buildEnum(register, element);
|
|
1940
|
+
const enumValues = buildEnum(register, element, logger);
|
|
2236
1941
|
return {
|
|
2237
1942
|
identifier,
|
|
2238
1943
|
type: fieldType,
|
|
@@ -2242,7 +1947,7 @@ function generateBindingSchema(register, fhirSchema, path, element) {
|
|
|
2242
1947
|
dependencies
|
|
2243
1948
|
};
|
|
2244
1949
|
}
|
|
2245
|
-
function collectBindingSchemas(register, fhirSchema) {
|
|
1950
|
+
function collectBindingSchemas(register, fhirSchema, logger) {
|
|
2246
1951
|
const processedPaths = /* @__PURE__ */ new Set();
|
|
2247
1952
|
if (!fhirSchema.elements) return [];
|
|
2248
1953
|
const bindings = [];
|
|
@@ -2253,7 +1958,7 @@ function collectBindingSchemas(register, fhirSchema) {
|
|
|
2253
1958
|
if (processedPaths.has(pathKey)) continue;
|
|
2254
1959
|
processedPaths.add(pathKey);
|
|
2255
1960
|
if (element.binding) {
|
|
2256
|
-
const binding = generateBindingSchema(register, fhirSchema, path, element);
|
|
1961
|
+
const binding = generateBindingSchema(register, fhirSchema, path, element, logger);
|
|
2257
1962
|
if (binding) {
|
|
2258
1963
|
bindings.push(binding);
|
|
2259
1964
|
}
|
|
@@ -2290,19 +1995,19 @@ function collectNestedElements(fhirSchema, parentPath, elements) {
|
|
|
2290
1995
|
}
|
|
2291
1996
|
return nested;
|
|
2292
1997
|
}
|
|
2293
|
-
function transformNestedElements(fhirSchema, parentPath, elements, register) {
|
|
1998
|
+
function transformNestedElements(fhirSchema, parentPath, elements, register, logger) {
|
|
2294
1999
|
const fields = {};
|
|
2295
2000
|
for (const [key, element] of Object.entries(elements)) {
|
|
2296
2001
|
const path = [...parentPath, key];
|
|
2297
2002
|
if (isNestedElement(element)) {
|
|
2298
2003
|
fields[key] = mkNestedField(register, fhirSchema, path, element);
|
|
2299
2004
|
} else {
|
|
2300
|
-
fields[key] = mkField(register, fhirSchema, path, element);
|
|
2005
|
+
fields[key] = mkField(register, fhirSchema, path, element, logger);
|
|
2301
2006
|
}
|
|
2302
2007
|
}
|
|
2303
2008
|
return fields;
|
|
2304
2009
|
}
|
|
2305
|
-
function mkNestedTypes(register, fhirSchema) {
|
|
2010
|
+
function mkNestedTypes(register, fhirSchema, logger) {
|
|
2306
2011
|
if (!fhirSchema.elements) return void 0;
|
|
2307
2012
|
const nestedElements = collectNestedElements(fhirSchema, [], fhirSchema.elements);
|
|
2308
2013
|
const actualNested = nestedElements.filter(
|
|
@@ -2329,7 +2034,7 @@ function mkNestedTypes(register, fhirSchema) {
|
|
|
2329
2034
|
url: `http://hl7.org/fhir/StructureDefinition/${element.type}`
|
|
2330
2035
|
};
|
|
2331
2036
|
}
|
|
2332
|
-
const fields = transformNestedElements(fhirSchema, path, element.elements, register);
|
|
2037
|
+
const fields = transformNestedElements(fhirSchema, path, element.elements, register, logger);
|
|
2333
2038
|
const nestedType = {
|
|
2334
2039
|
identifier,
|
|
2335
2040
|
base,
|
|
@@ -2359,7 +2064,7 @@ function extractNestedDependencies(nestedTypes) {
|
|
|
2359
2064
|
}
|
|
2360
2065
|
|
|
2361
2066
|
// src/typeschema/core/transformer.ts
|
|
2362
|
-
function mkFields(register, fhirSchema, parentPath, elements) {
|
|
2067
|
+
function mkFields(register, fhirSchema, parentPath, elements, logger) {
|
|
2363
2068
|
if (!elements) return void 0;
|
|
2364
2069
|
const geneology = register.resolveFsGenealogy(fhirSchema.url);
|
|
2365
2070
|
const elems = {};
|
|
@@ -2384,7 +2089,7 @@ function mkFields(register, fhirSchema, parentPath, elements) {
|
|
|
2384
2089
|
if (isNestedElement(elemSnapshot)) {
|
|
2385
2090
|
fields[key] = mkNestedField(register, fhirSchema, path, elemSnapshot);
|
|
2386
2091
|
} else {
|
|
2387
|
-
fields[key] = mkField(register, fhirSchema, path, elemSnapshot);
|
|
2092
|
+
fields[key] = mkField(register, fhirSchema, path, elemSnapshot, logger);
|
|
2388
2093
|
}
|
|
2389
2094
|
}
|
|
2390
2095
|
return fields;
|
|
@@ -2429,7 +2134,7 @@ function isExtensionSchema(fhirSchema, _identifier) {
|
|
|
2429
2134
|
}
|
|
2430
2135
|
return false;
|
|
2431
2136
|
}
|
|
2432
|
-
async function transformValueSet(register, valueSet) {
|
|
2137
|
+
async function transformValueSet(register, valueSet, logger) {
|
|
2433
2138
|
if (!valueSet.url) throw new Error("ValueSet URL is required");
|
|
2434
2139
|
const identifier = mkValueSetIdentifierByUrl(register, valueSet.url);
|
|
2435
2140
|
const concept = extractValueSetConceptsByUrl(register, valueSet.url);
|
|
@@ -2440,7 +2145,7 @@ async function transformValueSet(register, valueSet) {
|
|
|
2440
2145
|
compose: !concept ? valueSet.compose : void 0
|
|
2441
2146
|
};
|
|
2442
2147
|
}
|
|
2443
|
-
async function transformExtension(fhirSchema, register,
|
|
2148
|
+
async function transformExtension(fhirSchema, register, logger) {
|
|
2444
2149
|
try {
|
|
2445
2150
|
const identifier = mkIdentifier(fhirSchema);
|
|
2446
2151
|
let base;
|
|
@@ -2477,13 +2182,13 @@ async function transformExtension(fhirSchema, register, _packageInfo) {
|
|
|
2477
2182
|
extensionSchema.dependencies.push(base);
|
|
2478
2183
|
}
|
|
2479
2184
|
if (fhirSchema.elements) {
|
|
2480
|
-
const fields = mkFields(register, fhirSchema, [], fhirSchema.elements);
|
|
2185
|
+
const fields = mkFields(register, fhirSchema, [], fhirSchema.elements, logger);
|
|
2481
2186
|
if (fields && Object.keys(fields).length > 0) {
|
|
2482
2187
|
extensionSchema.fields = fields;
|
|
2483
2188
|
extensionSchema.dependencies.push(...extractFieldDependencies(fields));
|
|
2484
2189
|
}
|
|
2485
2190
|
}
|
|
2486
|
-
const nestedTypes = mkNestedTypes(register, fhirSchema);
|
|
2191
|
+
const nestedTypes = mkNestedTypes(register, fhirSchema, logger);
|
|
2487
2192
|
if (nestedTypes && nestedTypes.length > 0) {
|
|
2488
2193
|
extensionSchema.nested = nestedTypes;
|
|
2489
2194
|
extensionSchema.dependencies.push(...extractNestedDependencies(nestedTypes));
|
|
@@ -2495,63 +2200,386 @@ async function transformExtension(fhirSchema, register, _packageInfo) {
|
|
|
2495
2200
|
console.warn(`Failed to transform extension ${fhirSchema.name}: ${error}`);
|
|
2496
2201
|
return null;
|
|
2497
2202
|
}
|
|
2498
|
-
}
|
|
2499
|
-
function extractDependencies(identifier, base, fields, nestedTypes) {
|
|
2500
|
-
const deps = [];
|
|
2501
|
-
if (base) deps.push(base);
|
|
2502
|
-
if (fields) deps.push(...extractFieldDependencies(fields));
|
|
2503
|
-
if (nestedTypes) deps.push(...extractNestedDependencies(nestedTypes));
|
|
2504
|
-
const uniqDeps = {};
|
|
2505
|
-
for (const dep of deps) {
|
|
2506
|
-
if (dep.url === identifier.url) continue;
|
|
2507
|
-
uniqDeps[dep.url] = dep;
|
|
2203
|
+
}
|
|
2204
|
+
function extractDependencies(identifier, base, fields, nestedTypes) {
|
|
2205
|
+
const deps = [];
|
|
2206
|
+
if (base) deps.push(base);
|
|
2207
|
+
if (fields) deps.push(...extractFieldDependencies(fields));
|
|
2208
|
+
if (nestedTypes) deps.push(...extractNestedDependencies(nestedTypes));
|
|
2209
|
+
const uniqDeps = {};
|
|
2210
|
+
for (const dep of deps) {
|
|
2211
|
+
if (dep.url === identifier.url) continue;
|
|
2212
|
+
uniqDeps[dep.url] = dep;
|
|
2213
|
+
}
|
|
2214
|
+
const localNestedTypeUrls = new Set(nestedTypes?.map((nt) => nt.identifier.url));
|
|
2215
|
+
const result = Object.values(uniqDeps).filter((e) => !(e.kind === "nested" && localNestedTypeUrls.has(e.url))).sort((a, b) => a.url.localeCompare(b.url));
|
|
2216
|
+
return result.length > 0 ? result : void 0;
|
|
2217
|
+
}
|
|
2218
|
+
function transformFhirSchemaResource(register, fhirSchema, logger) {
|
|
2219
|
+
const identifier = mkIdentifier(fhirSchema);
|
|
2220
|
+
let base;
|
|
2221
|
+
if (fhirSchema.base && fhirSchema.type !== "Element") {
|
|
2222
|
+
const baseFs = register.resolveFs(register.ensureCanonicalUrl(fhirSchema.base));
|
|
2223
|
+
if (!baseFs) {
|
|
2224
|
+
throw new Error(`Base resource not found '${fhirSchema.base}' for '${fhirSchema.url}'`);
|
|
2225
|
+
}
|
|
2226
|
+
base = mkIdentifier(baseFs);
|
|
2227
|
+
}
|
|
2228
|
+
const fields = mkFields(register, fhirSchema, [], fhirSchema.elements, logger);
|
|
2229
|
+
const nested = mkNestedTypes(register, fhirSchema, logger);
|
|
2230
|
+
const dependencies = extractDependencies(identifier, base, fields, nested);
|
|
2231
|
+
const typeSchema = {
|
|
2232
|
+
identifier,
|
|
2233
|
+
base,
|
|
2234
|
+
fields,
|
|
2235
|
+
nested,
|
|
2236
|
+
description: fhirSchema.description,
|
|
2237
|
+
dependencies
|
|
2238
|
+
};
|
|
2239
|
+
const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger);
|
|
2240
|
+
return [typeSchema, ...bindingSchemas];
|
|
2241
|
+
}
|
|
2242
|
+
async function transformFhirSchema(register, fhirSchema, logger) {
|
|
2243
|
+
const results = [];
|
|
2244
|
+
const identifier = mkIdentifier(fhirSchema);
|
|
2245
|
+
if (identifier.kind === "profile") {
|
|
2246
|
+
const profileSchema = await transformProfile(register, fhirSchema, logger);
|
|
2247
|
+
results.push(profileSchema);
|
|
2248
|
+
const bindingSchemas = collectBindingSchemas(register, fhirSchema, logger);
|
|
2249
|
+
results.push(...bindingSchemas);
|
|
2250
|
+
return results;
|
|
2251
|
+
}
|
|
2252
|
+
if (isExtensionSchema(fhirSchema)) {
|
|
2253
|
+
const extensionSchema = await transformExtension(fhirSchema, register, logger);
|
|
2254
|
+
if (extensionSchema) {
|
|
2255
|
+
results.push(extensionSchema);
|
|
2256
|
+
}
|
|
2257
|
+
return results;
|
|
2258
|
+
}
|
|
2259
|
+
return transformFhirSchemaResource(register, fhirSchema, logger);
|
|
2260
|
+
}
|
|
2261
|
+
var TypeSchemaCache = class {
|
|
2262
|
+
cache = /* @__PURE__ */ new Map();
|
|
2263
|
+
config;
|
|
2264
|
+
cacheDir;
|
|
2265
|
+
constructor(config) {
|
|
2266
|
+
this.config = {
|
|
2267
|
+
enablePersistence: true,
|
|
2268
|
+
cacheDir: ".typeschema-cache",
|
|
2269
|
+
maxAge: 24 * 60 * 60 * 1e3,
|
|
2270
|
+
// 24 hours
|
|
2271
|
+
validateCached: true,
|
|
2272
|
+
...config
|
|
2273
|
+
};
|
|
2274
|
+
if (this.config.enablePersistence && this.config.cacheDir) {
|
|
2275
|
+
this.cacheDir = this.config.cacheDir;
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
/**
|
|
2279
|
+
* Store a schema in the cache
|
|
2280
|
+
*/
|
|
2281
|
+
async set(schema) {
|
|
2282
|
+
const key = this.generateKey(schema.identifier);
|
|
2283
|
+
this.cache.set(key, schema);
|
|
2284
|
+
if (this.config.enablePersistence && this.cacheDir) {
|
|
2285
|
+
await this.persistSchema(schema);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
/**
|
|
2289
|
+
* Retrieve a schema by identifier
|
|
2290
|
+
*/
|
|
2291
|
+
get(identifier) {
|
|
2292
|
+
const key = this.generateKey(identifier);
|
|
2293
|
+
return this.cache.get(key) || null;
|
|
2294
|
+
}
|
|
2295
|
+
/**
|
|
2296
|
+
* Retrieve a schema by URL
|
|
2297
|
+
*/
|
|
2298
|
+
getByUrl(url) {
|
|
2299
|
+
for (const schema of this.cache.values()) {
|
|
2300
|
+
if (schema.identifier.url === url) {
|
|
2301
|
+
return schema;
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
return null;
|
|
2305
|
+
}
|
|
2306
|
+
/**
|
|
2307
|
+
* Check if a schema exists in cache
|
|
2308
|
+
*/
|
|
2309
|
+
has(identifier) {
|
|
2310
|
+
const key = this.generateKey(identifier);
|
|
2311
|
+
return this.cache.has(key);
|
|
2312
|
+
}
|
|
2313
|
+
/**
|
|
2314
|
+
* Check if a schema exists by URL
|
|
2315
|
+
*/
|
|
2316
|
+
hasByUrl(url) {
|
|
2317
|
+
for (const schema of this.cache.values()) {
|
|
2318
|
+
if (schema.identifier.url === url) {
|
|
2319
|
+
return true;
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
return false;
|
|
2323
|
+
}
|
|
2324
|
+
/**
|
|
2325
|
+
* Delete a schema from cache
|
|
2326
|
+
*/
|
|
2327
|
+
delete(identifier) {
|
|
2328
|
+
const key = this.generateKey(identifier);
|
|
2329
|
+
return this.cache.delete(key);
|
|
2330
|
+
}
|
|
2331
|
+
/**
|
|
2332
|
+
* Delete a schema by URL
|
|
2333
|
+
*/
|
|
2334
|
+
deleteByUrl(url) {
|
|
2335
|
+
for (const [key, schema] of this.cache.entries()) {
|
|
2336
|
+
if (schema.identifier.url === url) {
|
|
2337
|
+
return this.cache.delete(key);
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
return false;
|
|
2341
|
+
}
|
|
2342
|
+
/**
|
|
2343
|
+
* Get schemas by package
|
|
2344
|
+
*/
|
|
2345
|
+
getByPackage(packageName) {
|
|
2346
|
+
const results = [];
|
|
2347
|
+
for (const schema of this.cache.values()) {
|
|
2348
|
+
if (schema.identifier.package === packageName) {
|
|
2349
|
+
results.push(schema);
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
return results;
|
|
2353
|
+
}
|
|
2354
|
+
/**
|
|
2355
|
+
* Get schemas by kind
|
|
2356
|
+
*/
|
|
2357
|
+
getByKind(kind) {
|
|
2358
|
+
const results = [];
|
|
2359
|
+
for (const schema of this.cache.values()) {
|
|
2360
|
+
if (schema.identifier.kind === kind) {
|
|
2361
|
+
results.push(schema);
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
return results;
|
|
2365
|
+
}
|
|
2366
|
+
/**
|
|
2367
|
+
* Store multiple schemas
|
|
2368
|
+
*/
|
|
2369
|
+
setMany(schemas) {
|
|
2370
|
+
for (const schema of schemas) {
|
|
2371
|
+
this.set(schema);
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
/**
|
|
2375
|
+
* Clear all cached schemas
|
|
2376
|
+
*/
|
|
2377
|
+
clear() {
|
|
2378
|
+
this.cache.clear();
|
|
2379
|
+
}
|
|
2380
|
+
/**
|
|
2381
|
+
* Generate cache key for identifier
|
|
2382
|
+
*/
|
|
2383
|
+
generateKey(identifier) {
|
|
2384
|
+
return `${identifier.package}:${identifier.version}:${identifier.kind}:${identifier.name}`;
|
|
2385
|
+
}
|
|
2386
|
+
/**
|
|
2387
|
+
* Initialize cache directory if persistence is enabled
|
|
2388
|
+
*/
|
|
2389
|
+
async initialize() {
|
|
2390
|
+
if (this.config.enablePersistence && this.cacheDir) {
|
|
2391
|
+
if (!existsSync(this.cacheDir)) {
|
|
2392
|
+
mkdirSync(this.cacheDir, { recursive: true });
|
|
2393
|
+
}
|
|
2394
|
+
await this.loadFromDisk();
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
/**
|
|
2398
|
+
* Load all cached schemas from disk
|
|
2399
|
+
*/
|
|
2400
|
+
async loadFromDisk() {
|
|
2401
|
+
if (!this.cacheDir || !existsSync(this.cacheDir)) {
|
|
2402
|
+
return;
|
|
2403
|
+
}
|
|
2404
|
+
try {
|
|
2405
|
+
const files = await readdir(this.cacheDir);
|
|
2406
|
+
const schemaFiles = files.filter((f) => f.endsWith(".typeschema.json"));
|
|
2407
|
+
for (const file of schemaFiles) {
|
|
2408
|
+
const filePath = join(this.cacheDir, file);
|
|
2409
|
+
const stats = await stat(filePath);
|
|
2410
|
+
if (this.config.maxAge) {
|
|
2411
|
+
const age = Date.now() - stats.mtimeMs;
|
|
2412
|
+
if (age > this.config.maxAge) {
|
|
2413
|
+
await unlink(filePath);
|
|
2414
|
+
continue;
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
try {
|
|
2418
|
+
const content = await readFile(filePath, "utf-8");
|
|
2419
|
+
const metadata = JSON.parse(content);
|
|
2420
|
+
if (this.config.validateCached) {
|
|
2421
|
+
if (!metadata.schema?.identifier) {
|
|
2422
|
+
continue;
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
const key = this.generateKey(metadata.schema.identifier);
|
|
2426
|
+
this.cache.set(key, metadata.schema);
|
|
2427
|
+
} catch (error) {
|
|
2428
|
+
console.warn(`Failed to load cached schema from ${file}:`, error);
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
} catch (error) {
|
|
2432
|
+
console.warn("Failed to load cached schemas from disk:", error);
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
/**
|
|
2436
|
+
* Persist a schema to disk
|
|
2437
|
+
*/
|
|
2438
|
+
async persistSchema(schema) {
|
|
2439
|
+
if (!this.cacheDir) {
|
|
2440
|
+
return;
|
|
2441
|
+
}
|
|
2442
|
+
if (!existsSync(this.cacheDir)) {
|
|
2443
|
+
mkdirSync(this.cacheDir, { recursive: true });
|
|
2444
|
+
}
|
|
2445
|
+
const metadata = {
|
|
2446
|
+
schema,
|
|
2447
|
+
timestamp: Date.now(),
|
|
2448
|
+
version: schema.identifier.version
|
|
2449
|
+
};
|
|
2450
|
+
const fileName = `${schema.identifier.package}-${schema.identifier.version}-${schema.identifier.kind}-${schema.identifier.name}.typeschema.json`.replace(
|
|
2451
|
+
/[^a-zA-Z0-9.-]/g,
|
|
2452
|
+
"_"
|
|
2453
|
+
);
|
|
2454
|
+
const filePath = join(this.cacheDir, fileName);
|
|
2455
|
+
try {
|
|
2456
|
+
await writeFile(filePath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
2457
|
+
} catch (error) {
|
|
2458
|
+
console.warn(`Failed to persist schema to ${filePath}:`, error);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
/**
|
|
2462
|
+
* Clear cache directory
|
|
2463
|
+
*/
|
|
2464
|
+
async clearDisk() {
|
|
2465
|
+
if (!this.cacheDir || !existsSync(this.cacheDir)) {
|
|
2466
|
+
return;
|
|
2467
|
+
}
|
|
2468
|
+
try {
|
|
2469
|
+
const files = await readdir(this.cacheDir);
|
|
2470
|
+
const schemaFiles = files.filter((f) => f.endsWith(".typeschema.json"));
|
|
2471
|
+
for (const file of schemaFiles) {
|
|
2472
|
+
await unlink(join(this.cacheDir, file));
|
|
2473
|
+
}
|
|
2474
|
+
} catch (error) {
|
|
2475
|
+
console.warn("Failed to clear cache directory:", error);
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
};
|
|
2479
|
+
var CodegenLogger = class _CodegenLogger {
|
|
2480
|
+
options;
|
|
2481
|
+
dryWarnSet = /* @__PURE__ */ new Set();
|
|
2482
|
+
constructor(options = {}) {
|
|
2483
|
+
this.options = {
|
|
2484
|
+
timestamp: false,
|
|
2485
|
+
verbose: false,
|
|
2486
|
+
...options
|
|
2487
|
+
};
|
|
2488
|
+
}
|
|
2489
|
+
formatMessage(level, message, color) {
|
|
2490
|
+
const timestamp = this.options.timestamp ? `${pc.gray((/* @__PURE__ */ new Date()).toLocaleTimeString())} ` : "";
|
|
2491
|
+
const prefix = this.options.prefix ? `${pc.cyan(`[${this.options.prefix}]`)} ` : "";
|
|
2492
|
+
return `${timestamp}${color(level)} ${prefix}${message}`;
|
|
2493
|
+
}
|
|
2494
|
+
/**
|
|
2495
|
+
* Success message with checkmark
|
|
2496
|
+
*/
|
|
2497
|
+
success(message) {
|
|
2498
|
+
console.log(this.formatMessage("\u2705", message, pc.green));
|
|
2499
|
+
}
|
|
2500
|
+
/**
|
|
2501
|
+
* Error message with X mark
|
|
2502
|
+
*/
|
|
2503
|
+
error(message, error) {
|
|
2504
|
+
console.error(this.formatMessage("\u274C", message, pc.red));
|
|
2505
|
+
if (error && this.options.verbose) {
|
|
2506
|
+
console.error(pc.red(` ${error.message}`));
|
|
2507
|
+
if (error.stack) {
|
|
2508
|
+
console.error(pc.gray(error.stack));
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
/**
|
|
2513
|
+
* Warning message with warning sign
|
|
2514
|
+
*/
|
|
2515
|
+
warn(message) {
|
|
2516
|
+
console.warn(this.formatMessage("!", message, pc.yellow));
|
|
2508
2517
|
}
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
function transformFhirSchemaResource(register, fhirSchema) {
|
|
2514
|
-
const identifier = mkIdentifier(fhirSchema);
|
|
2515
|
-
let base;
|
|
2516
|
-
if (fhirSchema.base && fhirSchema.type !== "Element") {
|
|
2517
|
-
const baseFs = register.resolveFs(register.ensureCanonicalUrl(fhirSchema.base));
|
|
2518
|
-
if (!baseFs) {
|
|
2519
|
-
throw new Error(`Base resource not found '${fhirSchema.base}' for '${fhirSchema.url}'`);
|
|
2518
|
+
dry_warn(message) {
|
|
2519
|
+
if (!this.dryWarnSet.has(message)) {
|
|
2520
|
+
this.warn(message);
|
|
2521
|
+
this.dryWarnSet.add(message);
|
|
2520
2522
|
}
|
|
2521
|
-
base = mkIdentifier(baseFs);
|
|
2522
2523
|
}
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
base,
|
|
2529
|
-
fields,
|
|
2530
|
-
nested,
|
|
2531
|
-
description: fhirSchema.description,
|
|
2532
|
-
dependencies
|
|
2533
|
-
};
|
|
2534
|
-
const bindingSchemas = collectBindingSchemas(register, fhirSchema);
|
|
2535
|
-
return [typeSchema, ...bindingSchemas];
|
|
2536
|
-
}
|
|
2537
|
-
async function transformFhirSchema(register, fhirSchema) {
|
|
2538
|
-
const results = [];
|
|
2539
|
-
const identifier = mkIdentifier(fhirSchema);
|
|
2540
|
-
if (identifier.kind === "profile") {
|
|
2541
|
-
const profileSchema = await transformProfile(register, fhirSchema);
|
|
2542
|
-
results.push(profileSchema);
|
|
2543
|
-
const bindingSchemas = collectBindingSchemas(register, fhirSchema);
|
|
2544
|
-
results.push(...bindingSchemas);
|
|
2545
|
-
return results;
|
|
2524
|
+
/**
|
|
2525
|
+
* Info message with info icon
|
|
2526
|
+
*/
|
|
2527
|
+
info(message) {
|
|
2528
|
+
console.log(this.formatMessage("i", message, pc.blue));
|
|
2546
2529
|
}
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2530
|
+
/**
|
|
2531
|
+
* Debug message (only shows in verbose mode)
|
|
2532
|
+
*/
|
|
2533
|
+
debug(message) {
|
|
2534
|
+
if (this.options.verbose) {
|
|
2535
|
+
console.log(this.formatMessage("\u{1F41B}", message, pc.magenta));
|
|
2551
2536
|
}
|
|
2552
|
-
return results;
|
|
2553
2537
|
}
|
|
2554
|
-
|
|
2538
|
+
/**
|
|
2539
|
+
* Step message with rocket
|
|
2540
|
+
*/
|
|
2541
|
+
step(message) {
|
|
2542
|
+
console.log(this.formatMessage("\u{1F680}", message, pc.cyan));
|
|
2543
|
+
}
|
|
2544
|
+
/**
|
|
2545
|
+
* Progress message with clock
|
|
2546
|
+
*/
|
|
2547
|
+
progress(message) {
|
|
2548
|
+
console.log(this.formatMessage("\u23F3", message, pc.blue));
|
|
2549
|
+
}
|
|
2550
|
+
/**
|
|
2551
|
+
* Plain message (no icon, just colored text)
|
|
2552
|
+
*/
|
|
2553
|
+
plain(message, color = (s) => s) {
|
|
2554
|
+
const timestamp = this.options.timestamp ? `${pc.gray((/* @__PURE__ */ new Date()).toLocaleTimeString())} ` : "";
|
|
2555
|
+
const prefix = this.options.prefix ? `${pc.cyan(`[${this.options.prefix}]`)} ` : "";
|
|
2556
|
+
console.log(`${timestamp}${prefix}${color(message)}`);
|
|
2557
|
+
}
|
|
2558
|
+
/**
|
|
2559
|
+
* Dimmed/gray text for less important info
|
|
2560
|
+
*/
|
|
2561
|
+
dim(message) {
|
|
2562
|
+
this.plain(message, pc.gray);
|
|
2563
|
+
}
|
|
2564
|
+
/**
|
|
2565
|
+
* Create a child logger with a prefix
|
|
2566
|
+
*/
|
|
2567
|
+
child(prefix) {
|
|
2568
|
+
return new _CodegenLogger({
|
|
2569
|
+
...this.options,
|
|
2570
|
+
prefix: this.options.prefix ? `${this.options.prefix}:${prefix}` : prefix
|
|
2571
|
+
});
|
|
2572
|
+
}
|
|
2573
|
+
/**
|
|
2574
|
+
* Update options
|
|
2575
|
+
*/
|
|
2576
|
+
configure(options) {
|
|
2577
|
+
this.options = { ...this.options, ...options };
|
|
2578
|
+
}
|
|
2579
|
+
};
|
|
2580
|
+
new CodegenLogger();
|
|
2581
|
+
function createLogger(options = {}) {
|
|
2582
|
+
return new CodegenLogger(options);
|
|
2555
2583
|
}
|
|
2556
2584
|
|
|
2557
2585
|
// src/typeschema/generator.ts
|
|
@@ -2578,12 +2606,12 @@ var TypeSchemaGenerator = class {
|
|
|
2578
2606
|
}
|
|
2579
2607
|
async registerFromPackageMetas(packageMetas) {
|
|
2580
2608
|
const packageNames = packageMetas.map((meta) => `${meta.name}${meta.version}`);
|
|
2581
|
-
this.logger
|
|
2609
|
+
this.logger?.step(`Loading FHIR packages: ${packageNames.join(", ")}`);
|
|
2582
2610
|
await this.manager.init();
|
|
2583
2611
|
return registerFromManager(this.manager);
|
|
2584
2612
|
}
|
|
2585
2613
|
generateFhirSchemas(structureDefinitions) {
|
|
2586
|
-
this.logger
|
|
2614
|
+
this.logger?.progress(`Converting ${structureDefinitions.length} StructureDefinitions to FHIRSchemas`);
|
|
2587
2615
|
const filteredStructureDefinitions = this.applyStructureDefinitionTreeshaking(structureDefinitions);
|
|
2588
2616
|
const fhirSchemas = [];
|
|
2589
2617
|
let convertedCount = 0;
|
|
@@ -2593,55 +2621,56 @@ var TypeSchemaGenerator = class {
|
|
|
2593
2621
|
const fhirSchema = fhirschema.translate(sd);
|
|
2594
2622
|
fhirSchemas.push(fhirSchema);
|
|
2595
2623
|
convertedCount++;
|
|
2596
|
-
this.logger
|
|
2624
|
+
this.logger?.debug(`Converted StructureDefinition: ${sd.name || sd.id} (${sd.resourceType})`);
|
|
2597
2625
|
} catch (error) {
|
|
2598
2626
|
failedCount++;
|
|
2599
|
-
this.logger
|
|
2627
|
+
this.logger?.warn(
|
|
2600
2628
|
`Failed to convert StructureDefinition ${sd.name || sd.id}: ${error instanceof Error ? error.message : String(error)}`
|
|
2601
2629
|
);
|
|
2602
2630
|
}
|
|
2603
2631
|
}
|
|
2604
|
-
this.logger
|
|
2632
|
+
this.logger?.success(
|
|
2605
2633
|
`FHIR Schema conversion completed: ${convertedCount}/${filteredStructureDefinitions.length} successful, ${failedCount} failed`
|
|
2606
2634
|
);
|
|
2607
2635
|
return fhirSchemas;
|
|
2608
2636
|
}
|
|
2609
|
-
async generateValueSetSchemas(valueSets,
|
|
2637
|
+
async generateValueSetSchemas(valueSets, logger) {
|
|
2610
2638
|
if (valueSets.length > 0) {
|
|
2611
|
-
this.logger
|
|
2639
|
+
this.logger?.debug(`${valueSets.length} ValueSets available for enum extraction`);
|
|
2612
2640
|
}
|
|
2641
|
+
const register = await registerFromManager(this.manager, logger);
|
|
2613
2642
|
const valueSetSchemas = [];
|
|
2614
2643
|
if (valueSets.length > 0) {
|
|
2615
|
-
this.logger
|
|
2644
|
+
this.logger?.progress(`Converting ${valueSets.length} ValueSets to TypeSchema`);
|
|
2616
2645
|
let valueSetConvertedCount = 0;
|
|
2617
2646
|
let valueSetFailedCount = 0;
|
|
2618
2647
|
for (const vs of valueSets) {
|
|
2619
2648
|
try {
|
|
2620
|
-
const valueSetSchema = await transformValueSet(
|
|
2649
|
+
const valueSetSchema = await transformValueSet(register, vs, logger);
|
|
2621
2650
|
if (valueSetSchema) {
|
|
2622
2651
|
valueSetSchemas.push(valueSetSchema);
|
|
2623
2652
|
valueSetConvertedCount++;
|
|
2624
|
-
this.logger
|
|
2653
|
+
this.logger?.debug(`Converted ValueSet: ${vs.name || vs.id}`);
|
|
2625
2654
|
}
|
|
2626
2655
|
} catch (error) {
|
|
2627
2656
|
valueSetFailedCount++;
|
|
2628
|
-
this.logger
|
|
2657
|
+
this.logger?.warn(
|
|
2629
2658
|
`Failed to convert ValueSet ${vs.name || vs.id}: ${error instanceof Error ? error.message : String(error)}`
|
|
2630
2659
|
);
|
|
2631
2660
|
}
|
|
2632
2661
|
}
|
|
2633
|
-
this.logger
|
|
2662
|
+
this.logger?.success(
|
|
2634
2663
|
`ValueSet conversion completed: ${valueSetConvertedCount}/${valueSets.length} successful, ${valueSetFailedCount} failed`
|
|
2635
2664
|
);
|
|
2636
2665
|
}
|
|
2637
2666
|
return valueSetSchemas;
|
|
2638
2667
|
}
|
|
2639
|
-
async generateFromPackage(packageName, packageVersion) {
|
|
2668
|
+
async generateFromPackage(packageName, packageVersion, logger) {
|
|
2640
2669
|
await this.initializeCache();
|
|
2641
2670
|
if (this.cache && !(this.cacheConfig?.forceRegenerate ?? false)) {
|
|
2642
2671
|
const cachedSchemas = this.cache.getByPackage(packageName);
|
|
2643
2672
|
if (cachedSchemas.length > 0) {
|
|
2644
|
-
this.logger
|
|
2673
|
+
this.logger?.info(
|
|
2645
2674
|
`Using cached TypeSchemas for package: ${packageName} (${cachedSchemas.length} schemas)`
|
|
2646
2675
|
);
|
|
2647
2676
|
return cachedSchemas;
|
|
@@ -2652,10 +2681,9 @@ var TypeSchemaGenerator = class {
|
|
|
2652
2681
|
version: packageVersion || "latest"
|
|
2653
2682
|
};
|
|
2654
2683
|
const register = await this.registerFromPackageMetas([packageInfo]);
|
|
2655
|
-
const
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
];
|
|
2684
|
+
const valueSets = await this.generateValueSetSchemas(register.allVs(), logger);
|
|
2685
|
+
const fhirSchemas = (await Promise.all(register.allFs().map(async (fs2) => await transformFhirSchema(register, fs2, logger)))).flat();
|
|
2686
|
+
const allSchemas = [...fhirSchemas, ...valueSets];
|
|
2659
2687
|
if (this.cache) {
|
|
2660
2688
|
for (const schema of allSchemas) {
|
|
2661
2689
|
await this.cache.set(schema);
|
|
@@ -2672,7 +2700,7 @@ var TypeSchemaGenerator = class {
|
|
|
2672
2700
|
if (!treeshakeList || treeshakeList.length === 0) {
|
|
2673
2701
|
return structureDefinitions;
|
|
2674
2702
|
}
|
|
2675
|
-
this.logger
|
|
2703
|
+
this.logger?.info(`Applying treeshaking filter for ResourceTypes: ${treeshakeList.join(", ")}`);
|
|
2676
2704
|
const allStructureDefinitions = /* @__PURE__ */ new Map();
|
|
2677
2705
|
const realDependencies = /* @__PURE__ */ new Map();
|
|
2678
2706
|
const referenceTargets = /* @__PURE__ */ new Map();
|
|
@@ -2696,7 +2724,7 @@ var TypeSchemaGenerator = class {
|
|
|
2696
2724
|
if (allStructureDefinitions.has(resourceType)) {
|
|
2697
2725
|
structureDefinitionsToKeep.add(resourceType);
|
|
2698
2726
|
} else {
|
|
2699
|
-
this.logger
|
|
2727
|
+
this.logger?.warn(`ResourceType '${resourceType}' not found in structure definitions`);
|
|
2700
2728
|
}
|
|
2701
2729
|
}
|
|
2702
2730
|
const addRealDependenciesRecursively = (name, visited = /* @__PURE__ */ new Set()) => {
|
|
@@ -2732,9 +2760,9 @@ var TypeSchemaGenerator = class {
|
|
|
2732
2760
|
}
|
|
2733
2761
|
}
|
|
2734
2762
|
if (excludedReferenceTargets.size > 0) {
|
|
2735
|
-
this.logger
|
|
2763
|
+
this.logger?.info(`Excluded reference-only targets: ${Array.from(excludedReferenceTargets).join(", ")}`);
|
|
2736
2764
|
}
|
|
2737
|
-
this.logger
|
|
2765
|
+
this.logger?.success(
|
|
2738
2766
|
`Treeshaking completed: kept ${filteredStructureDefinitions.length}/${structureDefinitions.length} structure definitions`
|
|
2739
2767
|
);
|
|
2740
2768
|
return filteredStructureDefinitions;
|
|
@@ -3022,6 +3050,18 @@ var TypeSchemaParser = class {
|
|
|
3022
3050
|
}
|
|
3023
3051
|
};
|
|
3024
3052
|
|
|
3053
|
+
// src/typeschema/index.ts
|
|
3054
|
+
var generateTypeSchemas = async (register, logger) => {
|
|
3055
|
+
const fhirSchemas = [];
|
|
3056
|
+
for (const fhirSchema of register.allFs()) {
|
|
3057
|
+
fhirSchemas.push(...await transformFhirSchema(register, fhirSchema, logger));
|
|
3058
|
+
}
|
|
3059
|
+
for (const vsSchema of register.allVs()) {
|
|
3060
|
+
fhirSchemas.push(await transformValueSet(register, vsSchema));
|
|
3061
|
+
}
|
|
3062
|
+
return fhirSchemas;
|
|
3063
|
+
};
|
|
3064
|
+
|
|
3025
3065
|
// src/api/generators/base/error-handler.ts
|
|
3026
3066
|
init_errors();
|
|
3027
3067
|
var ErrorHandler = class {
|
|
@@ -5046,26 +5086,456 @@ ${nestedInterfaces}`;
|
|
|
5046
5086
|
}
|
|
5047
5087
|
};
|
|
5048
5088
|
|
|
5089
|
+
// src/api/writer-generator/utils.ts
|
|
5090
|
+
var words = (s) => {
|
|
5091
|
+
return s.split(/(?<=[a-z])(?=[A-Z])|[-_.\s]/).filter(Boolean);
|
|
5092
|
+
};
|
|
5093
|
+
var kebabCase = (s) => {
|
|
5094
|
+
return words(s).map((s2) => s2.toLowerCase()).join("-");
|
|
5095
|
+
};
|
|
5096
|
+
var capitalCase = (s) => {
|
|
5097
|
+
if (s.length === 0) throw new Error("Empty string");
|
|
5098
|
+
return s[0]?.toUpperCase() + s.substring(1).toLowerCase();
|
|
5099
|
+
};
|
|
5100
|
+
var pascalCase = (s) => {
|
|
5101
|
+
return words(s).map(capitalCase).join("");
|
|
5102
|
+
};
|
|
5103
|
+
var uppercaseFirstLetter = (str) => {
|
|
5104
|
+
if (!str || str.length === 0) return str;
|
|
5105
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
5106
|
+
};
|
|
5107
|
+
var uppercaseFirstLetterOfEach = (strings) => {
|
|
5108
|
+
return strings.map((str) => uppercaseFirstLetter(str));
|
|
5109
|
+
};
|
|
5110
|
+
var FileSystemWriter = class {
|
|
5111
|
+
opts;
|
|
5112
|
+
currentDir;
|
|
5113
|
+
currentFileDescriptor;
|
|
5114
|
+
writtenFilesSet = /* @__PURE__ */ new Set();
|
|
5115
|
+
constructor(opts) {
|
|
5116
|
+
this.opts = opts;
|
|
5117
|
+
this.currentDir = opts.outputDir;
|
|
5118
|
+
}
|
|
5119
|
+
logger() {
|
|
5120
|
+
return this.opts.logger;
|
|
5121
|
+
}
|
|
5122
|
+
cd(path, gen) {
|
|
5123
|
+
this.currentDir = path.startsWith("/") ? Path2.join(this.opts.outputDir, path) : Path2.join(this.currentDir, path);
|
|
5124
|
+
if (!fs.existsSync(this.currentDir)) {
|
|
5125
|
+
fs.mkdirSync(this.currentDir, { recursive: true });
|
|
5126
|
+
}
|
|
5127
|
+
this.logger()?.debug(`cd '${this.currentDir}'`);
|
|
5128
|
+
gen();
|
|
5129
|
+
}
|
|
5130
|
+
cat(fn, gen) {
|
|
5131
|
+
if (this.currentFileDescriptor) throw new Error("Can't open file in file");
|
|
5132
|
+
if (fn.includes("/")) throw new Error(`Change file path separatly: ${fn}`);
|
|
5133
|
+
const fullFn = `${this.currentDir}/${fn}`;
|
|
5134
|
+
try {
|
|
5135
|
+
this.currentFileDescriptor = fs.openSync(fullFn, "w");
|
|
5136
|
+
this.writtenFilesSet.add(fn);
|
|
5137
|
+
this.logger()?.debug(`cat > '${fullFn}'`);
|
|
5138
|
+
gen();
|
|
5139
|
+
} finally {
|
|
5140
|
+
if (this.currentFileDescriptor) {
|
|
5141
|
+
fs.closeSync(this.currentFileDescriptor);
|
|
5142
|
+
}
|
|
5143
|
+
this.currentFileDescriptor = void 0;
|
|
5144
|
+
}
|
|
5145
|
+
}
|
|
5146
|
+
write(str) {
|
|
5147
|
+
if (!this.currentFileDescriptor) throw new Error("No file opened");
|
|
5148
|
+
fs.writeSync(this.currentFileDescriptor, str);
|
|
5149
|
+
}
|
|
5150
|
+
generate(_schemas) {
|
|
5151
|
+
throw new Error("Not implemented");
|
|
5152
|
+
}
|
|
5153
|
+
writtenFiles() {
|
|
5154
|
+
return Array.from(this.writtenFilesSet);
|
|
5155
|
+
}
|
|
5156
|
+
};
|
|
5157
|
+
var Writer = class extends FileSystemWriter {
|
|
5158
|
+
currentIndent = 0;
|
|
5159
|
+
indent() {
|
|
5160
|
+
this.currentIndent += this.opts.tabSize;
|
|
5161
|
+
}
|
|
5162
|
+
deindent() {
|
|
5163
|
+
this.currentIndent -= this.opts.tabSize;
|
|
5164
|
+
}
|
|
5165
|
+
writeIndent() {
|
|
5166
|
+
this.write(" ".repeat(this.currentIndent));
|
|
5167
|
+
}
|
|
5168
|
+
line(...tokens) {
|
|
5169
|
+
this.writeIndent();
|
|
5170
|
+
this.write(`${tokens.join(" ")}
|
|
5171
|
+
`);
|
|
5172
|
+
}
|
|
5173
|
+
lineSM(...tokens) {
|
|
5174
|
+
this.writeIndent();
|
|
5175
|
+
this.write(`${tokens.join(" ")};
|
|
5176
|
+
`);
|
|
5177
|
+
}
|
|
5178
|
+
comment(...tokens) {
|
|
5179
|
+
const lines = tokens.join(" ").split("\n");
|
|
5180
|
+
for (const line of lines) {
|
|
5181
|
+
this.line(this.opts.commentLinePrefix, line);
|
|
5182
|
+
}
|
|
5183
|
+
}
|
|
5184
|
+
debugComment(...tokens) {
|
|
5185
|
+
if (this.opts.withDebugComment) {
|
|
5186
|
+
tokens = tokens.map((token) => {
|
|
5187
|
+
if (typeof token === "string") {
|
|
5188
|
+
return token;
|
|
5189
|
+
} else {
|
|
5190
|
+
return JSON.stringify(token, null, 2);
|
|
5191
|
+
}
|
|
5192
|
+
});
|
|
5193
|
+
this.comment(...tokens);
|
|
5194
|
+
}
|
|
5195
|
+
}
|
|
5196
|
+
disclaimer() {
|
|
5197
|
+
return [
|
|
5198
|
+
"WARNING: This file is autogenerated by FHIR Schema Codegen.",
|
|
5199
|
+
"https://github.com/fhir-schema/fhir-schema-codegen",
|
|
5200
|
+
"Any manual changes made to this file may be overwritten."
|
|
5201
|
+
];
|
|
5202
|
+
}
|
|
5203
|
+
generateDisclaimer() {
|
|
5204
|
+
this.disclaimer().forEach((e) => this.comment(e));
|
|
5205
|
+
this.line();
|
|
5206
|
+
}
|
|
5207
|
+
curlyBlock(tokens, gencontent, endTokens) {
|
|
5208
|
+
this.line(`${tokens.filter(Boolean).join(" ")} {`);
|
|
5209
|
+
this.indent();
|
|
5210
|
+
gencontent();
|
|
5211
|
+
this.deindent();
|
|
5212
|
+
this.line(`}${endTokens?.filter(Boolean).join(" ") ?? ""}`);
|
|
5213
|
+
}
|
|
5214
|
+
squareBlock(tokens, gencontent, endTokens) {
|
|
5215
|
+
this.line(`${tokens.filter(Boolean).join(" ")} [`);
|
|
5216
|
+
this.indent();
|
|
5217
|
+
gencontent();
|
|
5218
|
+
this.deindent();
|
|
5219
|
+
this.line(`]${endTokens?.filter(Boolean).join(" ") ?? ""}`);
|
|
5220
|
+
}
|
|
5221
|
+
};
|
|
5222
|
+
|
|
5223
|
+
// src/typeschema/utils.ts
|
|
5224
|
+
var groupByPackages = (typeSchemas) => {
|
|
5225
|
+
const grouped = {};
|
|
5226
|
+
for (const ts of typeSchemas) {
|
|
5227
|
+
const packageName = ts.identifier.package;
|
|
5228
|
+
if (!grouped[packageName]) {
|
|
5229
|
+
grouped[packageName] = [];
|
|
5230
|
+
}
|
|
5231
|
+
grouped[packageName].push(ts);
|
|
5232
|
+
}
|
|
5233
|
+
for (const [_packageName, typeSchemas2] of Object.entries(grouped)) {
|
|
5234
|
+
typeSchemas2.sort((a, b) => a.identifier.name.localeCompare(b.identifier.name));
|
|
5235
|
+
}
|
|
5236
|
+
return grouped;
|
|
5237
|
+
};
|
|
5238
|
+
var collectComplexTypes = (tss) => tss.filter((t) => t.identifier.kind === "complex-type");
|
|
5239
|
+
var collectResources = (tss) => tss.filter((t) => t.identifier.kind === "resource");
|
|
5240
|
+
var notChoiceDeclaration = (field) => {
|
|
5241
|
+
if (field.choices) return void 0;
|
|
5242
|
+
if (field.choiceOf) return field;
|
|
5243
|
+
return field;
|
|
5244
|
+
};
|
|
5245
|
+
var resourceRelatives = (schemas) => {
|
|
5246
|
+
const regularSchemas = collectResources(schemas);
|
|
5247
|
+
const directPairs = [];
|
|
5248
|
+
for (const schema of regularSchemas) {
|
|
5249
|
+
if (schema.base) {
|
|
5250
|
+
directPairs.push({ parent: schema.base, child: schema.identifier });
|
|
5251
|
+
}
|
|
5252
|
+
}
|
|
5253
|
+
const allPairs = [...directPairs];
|
|
5254
|
+
const findTransitiveRelatives = (parentRef) => {
|
|
5255
|
+
const directChildren = directPairs.filter((pair) => pair.parent.name === parentRef.name).map((pair) => pair.child);
|
|
5256
|
+
const transitiveChildren = [];
|
|
5257
|
+
for (const child of directChildren) {
|
|
5258
|
+
transitiveChildren.push(...findTransitiveRelatives(child));
|
|
5259
|
+
}
|
|
5260
|
+
return [...directChildren, ...transitiveChildren];
|
|
5261
|
+
};
|
|
5262
|
+
for (const pair of directPairs) {
|
|
5263
|
+
const transitiveChildren = findTransitiveRelatives(pair.child);
|
|
5264
|
+
for (const transitiveChild of transitiveChildren) {
|
|
5265
|
+
if (!directPairs.some((dp) => dp.parent.name === pair.parent.name && dp.child.name === transitiveChild.name)) {
|
|
5266
|
+
allPairs.push({ parent: pair.parent, child: transitiveChild });
|
|
5267
|
+
}
|
|
5268
|
+
}
|
|
5269
|
+
}
|
|
5270
|
+
return allPairs;
|
|
5271
|
+
};
|
|
5272
|
+
var resourceChildren = (relatives, id) => {
|
|
5273
|
+
return relatives.filter((relative2) => relative2.parent.name === id.name).map((relative2) => relative2.child);
|
|
5274
|
+
};
|
|
5275
|
+
|
|
5276
|
+
// src/api/writer-generator/typescript.ts
|
|
5277
|
+
var primitiveType2tsType = {
|
|
5278
|
+
boolean: "boolean",
|
|
5279
|
+
instant: "string",
|
|
5280
|
+
time: "string",
|
|
5281
|
+
date: "string",
|
|
5282
|
+
dateTime: "string",
|
|
5283
|
+
decimal: "number",
|
|
5284
|
+
integer: "number",
|
|
5285
|
+
unsignedInt: "number",
|
|
5286
|
+
positiveInt: "number",
|
|
5287
|
+
integer64: "number",
|
|
5288
|
+
base64Binary: "string",
|
|
5289
|
+
uri: "string",
|
|
5290
|
+
url: "string",
|
|
5291
|
+
canonical: "string",
|
|
5292
|
+
oid: "string",
|
|
5293
|
+
uuid: "string",
|
|
5294
|
+
string: "string",
|
|
5295
|
+
code: "string",
|
|
5296
|
+
markdown: "string",
|
|
5297
|
+
id: "string",
|
|
5298
|
+
xhtml: "string"
|
|
5299
|
+
};
|
|
5300
|
+
var canonicalToName = (canonical, dropFragment = true) => {
|
|
5301
|
+
if (!canonical) return void 0;
|
|
5302
|
+
let localName = canonical.split("/").pop();
|
|
5303
|
+
if (dropFragment && localName?.includes("#")) {
|
|
5304
|
+
localName = localName.split("#")[0];
|
|
5305
|
+
}
|
|
5306
|
+
if (/^\d/.test(localName ?? "")) {
|
|
5307
|
+
localName = `number_${localName}`;
|
|
5308
|
+
}
|
|
5309
|
+
return localName?.replace(/[- ]/g, "_");
|
|
5310
|
+
};
|
|
5311
|
+
var tsBaseFileName = (id) => {
|
|
5312
|
+
return pascalCase(id.name);
|
|
5313
|
+
};
|
|
5314
|
+
var tsFileName = (id) => {
|
|
5315
|
+
return `${tsBaseFileName(id)}.ts`;
|
|
5316
|
+
};
|
|
5317
|
+
var normalizeName = (n) => {
|
|
5318
|
+
if (n === "extends") {
|
|
5319
|
+
return "extends_";
|
|
5320
|
+
}
|
|
5321
|
+
return n.replace(/[- ]/g, "_");
|
|
5322
|
+
};
|
|
5323
|
+
var resourceName = (id) => {
|
|
5324
|
+
return normalizeName(id.name);
|
|
5325
|
+
};
|
|
5326
|
+
var deriveNestedSchemaName = (url) => {
|
|
5327
|
+
const path = canonicalToName(url, false);
|
|
5328
|
+
if (!path) {
|
|
5329
|
+
return "";
|
|
5330
|
+
}
|
|
5331
|
+
const [resourceName2, fragment] = path.split("#");
|
|
5332
|
+
const name = uppercaseFirstLetterOfEach((fragment ?? "").split(".")).join("");
|
|
5333
|
+
return [resourceName2, name].join("");
|
|
5334
|
+
};
|
|
5335
|
+
var TypeScript = class extends Writer {
|
|
5336
|
+
resourceRelatives = [];
|
|
5337
|
+
tsImport(tsPackageName, ...entities) {
|
|
5338
|
+
this.lineSM(`import { ${entities.join(", ")} } from '${tsPackageName}'`);
|
|
5339
|
+
}
|
|
5340
|
+
generateFhirPackageIndexFile(schemas) {
|
|
5341
|
+
this.cat("index.ts", () => {
|
|
5342
|
+
let exports = schemas.map((schema) => ({
|
|
5343
|
+
identifier: schema.identifier,
|
|
5344
|
+
tsPackageName: tsBaseFileName(schema.identifier),
|
|
5345
|
+
resourceName: resourceName(schema.identifier)
|
|
5346
|
+
})).sort((a, b) => a.resourceName.localeCompare(b.resourceName));
|
|
5347
|
+
exports = Array.from(new Map(exports.map((exp) => [exp.resourceName.toLowerCase(), exp])).values()).sort(
|
|
5348
|
+
(a, b) => a.resourceName.localeCompare(b.resourceName)
|
|
5349
|
+
);
|
|
5350
|
+
for (const exp of exports) {
|
|
5351
|
+
this.debugComment(exp.identifier);
|
|
5352
|
+
this.tsImport(`./${exp.tsPackageName}`, exp.resourceName);
|
|
5353
|
+
}
|
|
5354
|
+
this.lineSM(`export { ${exports.map((e) => e.resourceName).join(", ")} }`);
|
|
5355
|
+
this.line("");
|
|
5356
|
+
this.curlyBlock(["export type ResourceTypeMap = "], () => {
|
|
5357
|
+
this.lineSM("User: Record<string, any>");
|
|
5358
|
+
exports.forEach((exp) => {
|
|
5359
|
+
this.debugComment(exp.identifier);
|
|
5360
|
+
this.lineSM(`${exp.resourceName}: ${exp.resourceName}`);
|
|
5361
|
+
});
|
|
5362
|
+
});
|
|
5363
|
+
this.lineSM("export type ResourceType = keyof ResourceTypeMap");
|
|
5364
|
+
this.squareBlock(["export const resourceList: readonly ResourceType[] = "], () => {
|
|
5365
|
+
exports.forEach((exp) => {
|
|
5366
|
+
this.debugComment(exp.identifier);
|
|
5367
|
+
this.line(`'${exp.resourceName}', `);
|
|
5368
|
+
});
|
|
5369
|
+
});
|
|
5370
|
+
});
|
|
5371
|
+
}
|
|
5372
|
+
generateDependenciesImports(schema) {
|
|
5373
|
+
if (schema.dependencies) {
|
|
5374
|
+
const deps = [
|
|
5375
|
+
...schema.dependencies.filter((dep) => ["complex-type", "resource", "logical"].includes(dep.kind)).map((dep) => ({
|
|
5376
|
+
tsPackage: `../${kebabCase(dep.package)}/${pascalCase(dep.name)}`,
|
|
5377
|
+
name: uppercaseFirstLetter(dep.name)
|
|
5378
|
+
})),
|
|
5379
|
+
...schema.dependencies.filter((dep) => ["nested"].includes(dep.kind)).map((dep) => ({
|
|
5380
|
+
tsPackage: `../${kebabCase(dep.package)}/${pascalCase(canonicalToName(dep.url) ?? "")}`,
|
|
5381
|
+
name: deriveNestedSchemaName(dep.url)
|
|
5382
|
+
}))
|
|
5383
|
+
].sort((a, b) => a.name.localeCompare(b.name));
|
|
5384
|
+
for (const dep of deps) {
|
|
5385
|
+
this.tsImport(dep.tsPackage, dep.name);
|
|
5386
|
+
}
|
|
5387
|
+
}
|
|
5388
|
+
}
|
|
5389
|
+
addFieldExtension(fieldName, field) {
|
|
5390
|
+
if (field.type.kind === "primitive-type") {
|
|
5391
|
+
this.lineSM(`_${normalizeName(fieldName)}?: Element`);
|
|
5392
|
+
}
|
|
5393
|
+
}
|
|
5394
|
+
generateType(schema) {
|
|
5395
|
+
var name;
|
|
5396
|
+
if (schema.identifier.name === "Reference") {
|
|
5397
|
+
name = "Reference<T extends string = string>";
|
|
5398
|
+
} else if (schema.identifier.kind === "nested") {
|
|
5399
|
+
name = normalizeName(deriveNestedSchemaName(schema.identifier.url));
|
|
5400
|
+
} else {
|
|
5401
|
+
name = normalizeName(schema.identifier.name);
|
|
5402
|
+
}
|
|
5403
|
+
const parent = canonicalToName(schema.base?.url);
|
|
5404
|
+
const extendsClause = parent && `extends ${parent}`;
|
|
5405
|
+
this.debugComment(schema.identifier);
|
|
5406
|
+
this.curlyBlock(["export", "interface", name, extendsClause], () => {
|
|
5407
|
+
if (!schema.fields) {
|
|
5408
|
+
return;
|
|
5409
|
+
}
|
|
5410
|
+
if (schema.identifier.kind === "resource") {
|
|
5411
|
+
const possibleResourceTypes = [schema.identifier];
|
|
5412
|
+
possibleResourceTypes.push(...resourceChildren(this.resourceRelatives, schema.identifier));
|
|
5413
|
+
this.lineSM(`resourceType: ${possibleResourceTypes.map((e) => `'${e.name}'`).join(" | ")}`);
|
|
5414
|
+
this.line();
|
|
5415
|
+
}
|
|
5416
|
+
const fields = Object.entries(schema.fields).sort((a, b) => a[0].localeCompare(b[0]));
|
|
5417
|
+
for (const [fieldName, anyField] of fields) {
|
|
5418
|
+
const field = notChoiceDeclaration(anyField);
|
|
5419
|
+
if (field === void 0) continue;
|
|
5420
|
+
this.debugComment(fieldName, ":", field);
|
|
5421
|
+
const fieldNameFixed = normalizeName(fieldName);
|
|
5422
|
+
const optionalSymbol = field.required ? "" : "?";
|
|
5423
|
+
const arraySymbol = field.array ? "[]" : "";
|
|
5424
|
+
if (field.type === void 0) {
|
|
5425
|
+
continue;
|
|
5426
|
+
}
|
|
5427
|
+
let type = field.type.name;
|
|
5428
|
+
if (field.type.kind === "nested") {
|
|
5429
|
+
type = deriveNestedSchemaName(field.type.url);
|
|
5430
|
+
}
|
|
5431
|
+
if (field.type.kind === "primitive-type") {
|
|
5432
|
+
type = primitiveType2tsType[field.type.name] ?? "string";
|
|
5433
|
+
}
|
|
5434
|
+
if (schema.identifier.name === "Reference" && fieldNameFixed === "reference") {
|
|
5435
|
+
type = "`${T}/${string}`";
|
|
5436
|
+
}
|
|
5437
|
+
if (field.reference?.length) {
|
|
5438
|
+
const references = field.reference.map((ref) => `'${ref.name}'`).join(" | ");
|
|
5439
|
+
type = `Reference<${references}>`;
|
|
5440
|
+
}
|
|
5441
|
+
if (field.enum) {
|
|
5442
|
+
type = field.enum.map((e) => `'${e}'`).join(" | ");
|
|
5443
|
+
}
|
|
5444
|
+
this.lineSM(`${fieldNameFixed}${optionalSymbol}:`, `${type}${arraySymbol}`);
|
|
5445
|
+
if (["resource", "complex-type"].includes(schema.identifier.kind)) {
|
|
5446
|
+
this.addFieldExtension(fieldName, field);
|
|
5447
|
+
}
|
|
5448
|
+
}
|
|
5449
|
+
});
|
|
5450
|
+
this.line();
|
|
5451
|
+
}
|
|
5452
|
+
generateNestedTypes(schema) {
|
|
5453
|
+
if (schema.nested) {
|
|
5454
|
+
this.line();
|
|
5455
|
+
for (const subtype of schema.nested) {
|
|
5456
|
+
this.generateType(subtype);
|
|
5457
|
+
}
|
|
5458
|
+
}
|
|
5459
|
+
}
|
|
5460
|
+
generateResourceModule(schema) {
|
|
5461
|
+
this.cat(`${tsFileName(schema.identifier)}`, () => {
|
|
5462
|
+
this.generateDisclaimer();
|
|
5463
|
+
if (["complex-type", "resource", "logical", "nested"].includes(schema.identifier.kind)) {
|
|
5464
|
+
this.generateDependenciesImports(schema);
|
|
5465
|
+
this.line();
|
|
5466
|
+
this.generateNestedTypes(schema);
|
|
5467
|
+
this.generateType(schema);
|
|
5468
|
+
} else {
|
|
5469
|
+
throw new Error(`Profile generation not implemented for kind: ${schema.identifier.kind}`);
|
|
5470
|
+
}
|
|
5471
|
+
});
|
|
5472
|
+
}
|
|
5473
|
+
generate(schemas) {
|
|
5474
|
+
this.resourceRelatives = resourceRelatives(schemas);
|
|
5475
|
+
const typesToGenerate = [
|
|
5476
|
+
...collectComplexTypes(schemas),
|
|
5477
|
+
...collectResources(schemas)
|
|
5478
|
+
// ...collectLogicalModels(typeSchemas),
|
|
5479
|
+
// ...collectProfiles(typeSchemas),
|
|
5480
|
+
];
|
|
5481
|
+
const grouped = groupByPackages(typesToGenerate);
|
|
5482
|
+
this.cd("/", () => {
|
|
5483
|
+
for (const [packageName, packageSchemas] of Object.entries(grouped)) {
|
|
5484
|
+
const tsPackageName = kebabCase(packageName);
|
|
5485
|
+
this.cd(tsPackageName, () => {
|
|
5486
|
+
for (const schema of packageSchemas) {
|
|
5487
|
+
this.generateResourceModule(schema);
|
|
5488
|
+
}
|
|
5489
|
+
this.generateFhirPackageIndexFile(packageSchemas);
|
|
5490
|
+
});
|
|
5491
|
+
}
|
|
5492
|
+
});
|
|
5493
|
+
}
|
|
5494
|
+
};
|
|
5495
|
+
|
|
5049
5496
|
// src/api/builder.ts
|
|
5497
|
+
var writerToGenerator = (writerGen) => {
|
|
5498
|
+
const getGeneratedFiles = () => {
|
|
5499
|
+
return writerGen.writtenFiles().map((fn) => {
|
|
5500
|
+
return {
|
|
5501
|
+
path: Path2.normalize(Path2.join(writerGen.opts.outputDir, fn)),
|
|
5502
|
+
filename: fn.replace(/^.*[\\/]/, ""),
|
|
5503
|
+
content: "",
|
|
5504
|
+
exports: [],
|
|
5505
|
+
size: 0,
|
|
5506
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
5507
|
+
};
|
|
5508
|
+
});
|
|
5509
|
+
};
|
|
5510
|
+
return {
|
|
5511
|
+
generate: async (schemas) => {
|
|
5512
|
+
writerGen.generate(schemas);
|
|
5513
|
+
return getGeneratedFiles();
|
|
5514
|
+
},
|
|
5515
|
+
setOutputDir: (outputDir) => writerGen.opts.outputDir = outputDir,
|
|
5516
|
+
build: async (_schemas) => getGeneratedFiles()
|
|
5517
|
+
};
|
|
5518
|
+
};
|
|
5050
5519
|
var APIBuilder = class {
|
|
5051
5520
|
schemas = [];
|
|
5052
5521
|
options;
|
|
5053
5522
|
generators = /* @__PURE__ */ new Map();
|
|
5054
|
-
progressCallback;
|
|
5055
5523
|
cache;
|
|
5056
5524
|
pendingOperations = [];
|
|
5057
5525
|
typeSchemaGenerator;
|
|
5058
5526
|
logger;
|
|
5527
|
+
packages = [];
|
|
5528
|
+
progressCallback;
|
|
5059
5529
|
typeSchemaConfig;
|
|
5060
5530
|
constructor(options = {}) {
|
|
5061
5531
|
this.options = {
|
|
5062
5532
|
outputDir: options.outputDir || "./generated",
|
|
5063
5533
|
verbose: options.verbose ?? false,
|
|
5064
5534
|
overwrite: options.overwrite ?? true,
|
|
5065
|
-
validate: options.validate ?? true,
|
|
5066
5535
|
cache: options.cache ?? true,
|
|
5067
5536
|
typeSchemaConfig: options.typeSchemaConfig,
|
|
5068
|
-
manager: options.manager || null
|
|
5537
|
+
manager: options.manager || null,
|
|
5538
|
+
throwException: options.throwException || false
|
|
5069
5539
|
};
|
|
5070
5540
|
this.typeSchemaConfig = options.typeSchemaConfig;
|
|
5071
5541
|
this.logger = options.logger || createLogger({
|
|
@@ -5076,13 +5546,8 @@ var APIBuilder = class {
|
|
|
5076
5546
|
this.cache = new TypeSchemaCache(this.typeSchemaConfig);
|
|
5077
5547
|
}
|
|
5078
5548
|
}
|
|
5079
|
-
/**
|
|
5080
|
-
* Load TypeSchema from a FHIR package
|
|
5081
|
-
*/
|
|
5082
5549
|
fromPackage(packageName, version) {
|
|
5083
|
-
this.
|
|
5084
|
-
const operation = this.loadFromPackage(packageName, version);
|
|
5085
|
-
this.pendingOperations.push(operation);
|
|
5550
|
+
this.packages.push(packageMetaToNpm({ name: packageName, version: version || "latest" }));
|
|
5086
5551
|
return this;
|
|
5087
5552
|
}
|
|
5088
5553
|
/**
|
|
@@ -5102,9 +5567,6 @@ var APIBuilder = class {
|
|
|
5102
5567
|
this.schemas = [...this.schemas, ...schemas];
|
|
5103
5568
|
return this;
|
|
5104
5569
|
}
|
|
5105
|
-
/**
|
|
5106
|
-
* Configure TypeScript generation
|
|
5107
|
-
*/
|
|
5108
5570
|
typescript(options = {}) {
|
|
5109
5571
|
const typesOutputDir = `${this.options.outputDir}/types`;
|
|
5110
5572
|
const generator = new TypeScriptGenerator({
|
|
@@ -5130,6 +5592,19 @@ var APIBuilder = class {
|
|
|
5130
5592
|
this.logger.debug(`Configured TypeScript generator (${options.moduleFormat || "esm"})`);
|
|
5131
5593
|
return this;
|
|
5132
5594
|
}
|
|
5595
|
+
typescript2(opts) {
|
|
5596
|
+
const writerOpts = {
|
|
5597
|
+
outputDir: Path2.join(this.options.outputDir, "/types"),
|
|
5598
|
+
tabSize: 2,
|
|
5599
|
+
withDebugComment: false,
|
|
5600
|
+
commentLinePrefix: "//"
|
|
5601
|
+
};
|
|
5602
|
+
const effectiveOpts = { logger: this.logger, ...writerOpts, ...opts };
|
|
5603
|
+
const generator = writerToGenerator(new TypeScript(effectiveOpts));
|
|
5604
|
+
this.generators.set("typescript2", generator);
|
|
5605
|
+
this.logger.debug(`Configured TypeScript2 generator (${JSON.stringify(effectiveOpts, void 0, 2)})`);
|
|
5606
|
+
return this;
|
|
5607
|
+
}
|
|
5133
5608
|
/**
|
|
5134
5609
|
* Set a progress callback for monitoring generation
|
|
5135
5610
|
*/
|
|
@@ -5150,23 +5625,15 @@ var APIBuilder = class {
|
|
|
5150
5625
|
}
|
|
5151
5626
|
return this;
|
|
5152
5627
|
}
|
|
5153
|
-
/**
|
|
5154
|
-
* Enable/disable verbose logging
|
|
5155
|
-
*/
|
|
5156
5628
|
verbose(enabled = true) {
|
|
5157
5629
|
this.options.verbose = enabled;
|
|
5630
|
+
this.logger?.configure({ verbose: enabled });
|
|
5158
5631
|
return this;
|
|
5159
5632
|
}
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
*/
|
|
5163
|
-
validate(enabled = true) {
|
|
5164
|
-
this.options.validate = enabled;
|
|
5633
|
+
throwException(enabled = true) {
|
|
5634
|
+
this.options.throwException = enabled;
|
|
5165
5635
|
return this;
|
|
5166
5636
|
}
|
|
5167
|
-
/**
|
|
5168
|
-
* Execute the generation process
|
|
5169
|
-
*/
|
|
5170
5637
|
async generate() {
|
|
5171
5638
|
const startTime = performance.now();
|
|
5172
5639
|
const result = {
|
|
@@ -5179,35 +5646,33 @@ var APIBuilder = class {
|
|
|
5179
5646
|
};
|
|
5180
5647
|
this.logger.debug(`Starting generation with ${this.generators.size} generators`);
|
|
5181
5648
|
try {
|
|
5182
|
-
this.
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
}
|
|
5191
|
-
this.reportProgress("Generating", 2, 4, "Generating code...");
|
|
5649
|
+
this.logger.info("Initialize Canonical Manager");
|
|
5650
|
+
const manager = CanonicalManager({
|
|
5651
|
+
packages: this.packages,
|
|
5652
|
+
workingDir: "tmp/fhir"
|
|
5653
|
+
});
|
|
5654
|
+
await manager.init();
|
|
5655
|
+
const register = await registerFromManager(manager, this.logger);
|
|
5656
|
+
const typeSchemas = await generateTypeSchemas(register, this.logger);
|
|
5192
5657
|
this.logger.debug(`Executing ${this.generators.size} generators`);
|
|
5193
|
-
await this.executeGenerators(result);
|
|
5194
|
-
this.
|
|
5658
|
+
await this.executeGenerators(result, typeSchemas);
|
|
5659
|
+
this.logger.info("Generation completed successfully");
|
|
5195
5660
|
result.success = result.errors.length === 0;
|
|
5196
5661
|
this.logger.debug(`Generation completed: ${result.filesGenerated.length} files`);
|
|
5197
5662
|
} catch (error) {
|
|
5198
5663
|
this.logger.error("Code generation failed", error instanceof Error ? error : new Error(String(error)));
|
|
5199
5664
|
result.errors.push(error instanceof Error ? error.message : String(error));
|
|
5200
|
-
result.success = false;
|
|
5201
|
-
} finally {
|
|
5202
|
-
result.duration = performance.now() - startTime;
|
|
5203
5665
|
}
|
|
5204
|
-
return
|
|
5666
|
+
return {
|
|
5667
|
+
...result,
|
|
5668
|
+
success: result.errors.length === 0,
|
|
5669
|
+
duration: performance.now() - startTime
|
|
5670
|
+
};
|
|
5205
5671
|
}
|
|
5206
5672
|
/**
|
|
5207
5673
|
* Generate and return the results without writing to files
|
|
5208
5674
|
*/
|
|
5209
5675
|
async build() {
|
|
5210
|
-
await this.resolveSchemas();
|
|
5211
5676
|
const results = {};
|
|
5212
5677
|
for (const [type, generator] of this.generators.entries()) {
|
|
5213
5678
|
if (generator.build) {
|
|
@@ -5237,24 +5702,6 @@ var APIBuilder = class {
|
|
|
5237
5702
|
getGenerators() {
|
|
5238
5703
|
return Array.from(this.generators.keys());
|
|
5239
5704
|
}
|
|
5240
|
-
// Private implementation methods
|
|
5241
|
-
async loadFromPackage(packageName, version) {
|
|
5242
|
-
const generator = new TypeSchemaGenerator(
|
|
5243
|
-
{
|
|
5244
|
-
verbose: this.options.verbose,
|
|
5245
|
-
logger: this.logger.child("Schema"),
|
|
5246
|
-
treeshake: this.typeSchemaConfig?.treeshake,
|
|
5247
|
-
manager: this.options.manager
|
|
5248
|
-
},
|
|
5249
|
-
this.typeSchemaConfig
|
|
5250
|
-
);
|
|
5251
|
-
this.typeSchemaGenerator = generator;
|
|
5252
|
-
const schemas = await generator.generateFromPackage(packageName, version);
|
|
5253
|
-
this.schemas = [...this.schemas, ...schemas];
|
|
5254
|
-
if (this.cache) {
|
|
5255
|
-
this.cache.setMany(schemas);
|
|
5256
|
-
}
|
|
5257
|
-
}
|
|
5258
5705
|
async loadFromFiles(filePaths) {
|
|
5259
5706
|
if (!this.typeSchemaGenerator) {
|
|
5260
5707
|
this.typeSchemaGenerator = new TypeSchemaGenerator(
|
|
@@ -5267,8 +5714,7 @@ var APIBuilder = class {
|
|
|
5267
5714
|
);
|
|
5268
5715
|
}
|
|
5269
5716
|
const parser = new TypeSchemaParser({
|
|
5270
|
-
format: "auto"
|
|
5271
|
-
validate: this.options.validate
|
|
5717
|
+
format: "auto"
|
|
5272
5718
|
});
|
|
5273
5719
|
const schemas = await parser.parseFromFiles(filePaths);
|
|
5274
5720
|
this.schemas = [...this.schemas, ...schemas];
|
|
@@ -5276,37 +5722,18 @@ var APIBuilder = class {
|
|
|
5276
5722
|
this.cache.setMany(schemas);
|
|
5277
5723
|
}
|
|
5278
5724
|
}
|
|
5279
|
-
async
|
|
5280
|
-
if (this.pendingOperations.length > 0) {
|
|
5281
|
-
await Promise.all(this.pendingOperations);
|
|
5282
|
-
this.pendingOperations = [];
|
|
5283
|
-
}
|
|
5284
|
-
}
|
|
5285
|
-
async validateSchemas(_result) {
|
|
5286
|
-
return;
|
|
5287
|
-
}
|
|
5288
|
-
async executeGenerators(result) {
|
|
5289
|
-
const generatorCount = this.generators.size;
|
|
5290
|
-
let current = 0;
|
|
5725
|
+
async executeGenerators(result, typeSchemas) {
|
|
5291
5726
|
for (const [type, generator] of this.generators.entries()) {
|
|
5292
|
-
this.
|
|
5727
|
+
this.logger.info(`Generating ${type}...`);
|
|
5293
5728
|
try {
|
|
5294
|
-
const files = await generator.generate(
|
|
5729
|
+
const files = await generator.generate(typeSchemas);
|
|
5295
5730
|
result.filesGenerated.push(...files.map((f) => f.path || f.filename));
|
|
5731
|
+
this.logger.info(`Generating ${type} finished successfully`);
|
|
5296
5732
|
} catch (error) {
|
|
5297
5733
|
result.errors.push(
|
|
5298
5734
|
`${type} generator failed: ${error instanceof Error ? error.message : String(error)}`
|
|
5299
5735
|
);
|
|
5300
5736
|
}
|
|
5301
|
-
current++;
|
|
5302
|
-
}
|
|
5303
|
-
}
|
|
5304
|
-
reportProgress(phase, current, total, message) {
|
|
5305
|
-
if (this.progressCallback) {
|
|
5306
|
-
this.progressCallback(phase, current, total, message);
|
|
5307
|
-
}
|
|
5308
|
-
if (this.options.verbose && message) {
|
|
5309
|
-
this.logger.debug(`[${phase}] ${message}`);
|
|
5310
5737
|
}
|
|
5311
5738
|
}
|
|
5312
5739
|
};
|
|
@@ -5318,7 +5745,6 @@ function createAPIFromConfig(config) {
|
|
|
5318
5745
|
outputDir: config.outputDir,
|
|
5319
5746
|
verbose: config.verbose,
|
|
5320
5747
|
overwrite: config.overwrite,
|
|
5321
|
-
validate: config.validate,
|
|
5322
5748
|
cache: config.cache,
|
|
5323
5749
|
typeSchemaConfig: config.typeSchema
|
|
5324
5750
|
});
|
|
@@ -5338,15 +5764,13 @@ function createAPIFromConfig(config) {
|
|
|
5338
5764
|
async function generateTypesFromPackage(packageName, outputDir, options = {}) {
|
|
5339
5765
|
return createAPI({
|
|
5340
5766
|
outputDir,
|
|
5341
|
-
verbose: options.verbose
|
|
5342
|
-
validate: options.validate
|
|
5767
|
+
verbose: options.verbose
|
|
5343
5768
|
}).fromPackage(packageName, options.version).typescript().generate();
|
|
5344
5769
|
}
|
|
5345
5770
|
async function generateTypesFromFiles(inputFiles, outputDir, options = {}) {
|
|
5346
5771
|
return createAPI({
|
|
5347
5772
|
outputDir,
|
|
5348
|
-
verbose: options.verbose
|
|
5349
|
-
validate: options.validate
|
|
5773
|
+
verbose: options.verbose
|
|
5350
5774
|
}).fromFiles(...inputFiles).typescript().generate();
|
|
5351
5775
|
}
|
|
5352
5776
|
var DEFAULT_CONFIG = {
|