@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/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
- var TypeSchemaCache = class {
1395
- cache = /* @__PURE__ */ new Map();
1396
- config;
1397
- cacheDir;
1398
- constructor(config) {
1399
- this.config = {
1400
- enablePersistence: true,
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
- * Initialize cache directory if persistence is enabled
1521
- */
1522
- async initialize() {
1523
- if (this.config.enablePersistence && this.cacheDir) {
1524
- if (!existsSync(this.cacheDir)) {
1525
- mkdirSync(this.cacheDir, { recursive: true });
1526
- }
1527
- await this.loadFromDisk();
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
- * Load all cached schemas from disk
1532
- */
1533
- async loadFromDisk() {
1534
- if (!this.cacheDir || !existsSync(this.cacheDir)) {
1535
- return;
1536
- }
1537
- try {
1538
- const files = await readdir(this.cacheDir);
1539
- const schemaFiles = files.filter((f) => f.endsWith(".typeschema.json"));
1540
- for (const file of schemaFiles) {
1541
- const filePath = join(this.cacheDir, file);
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 content = await readFile(filePath, "utf-8");
1552
- const metadata = JSON.parse(content);
1553
- if (this.config.validateCached) {
1554
- if (!metadata.schema?.identifier) {
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
- console.warn(`Failed to load cached schema from ${file}:`, error);
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
- } catch (error) {
1565
- console.warn("Failed to load cached schemas from disk:", error);
1566
- }
1567
- }
1568
- /**
1569
- * Persist a schema to disk
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
- const metadata = {
1579
- schema,
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
- const filePath = join(this.cacheDir, fileName);
1588
- try {
1589
- await writeFile(filePath, JSON.stringify(metadata, null, 2), "utf-8");
1590
- } catch (error) {
1591
- console.warn(`Failed to persist schema to ${filePath}:`, error);
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
- * Clear cache directory
1596
- */
1597
- async clearDisk() {
1598
- if (!this.cacheDir || !existsSync(this.cacheDir)) {
1599
- return;
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
- try {
1602
- const files = await readdir(this.cacheDir);
1603
- const schemaFiles = files.filter((f) => f.endsWith(".typeschema.json"));
1604
- for (const file of schemaFiles) {
1605
- await unlink(join(this.cacheDir, file));
1606
- }
1607
- } catch (error) {
1608
- console.warn("Failed to clear cache directory:", error);
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?.package_meta.name,
1876
- version: valueSet?.package_meta.version,
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((fs) => {
2066
- if (parentPath.length === 0) return fs.required || [];
2067
- if (!fs.elements) return [];
2068
- let elem = fs;
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((fs) => {
2081
- if (parentPath.length === 0) return fs.excluded || [];
2082
- if (!fs.elements) return [];
2083
- let elem = fs;
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 fs = register.resolveFs(curl);
2096
- return mkIdentifier(fs);
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
- console.warn(
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, _packageInfo) {
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
- const localNestedTypeUrls = new Set(nestedTypes?.map((nt) => nt.identifier.url));
2510
- const result = Object.values(uniqDeps).filter((e) => !(e.kind === "nested" && localNestedTypeUrls.has(e.url))).sort((a, b) => a.url.localeCompare(b.url));
2511
- return result.length > 0 ? result : void 0;
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
- const fields = mkFields(register, fhirSchema, [], fhirSchema.elements);
2524
- const nested = mkNestedTypes(register, fhirSchema);
2525
- const dependencies = extractDependencies(identifier, base, fields, nested);
2526
- const typeSchema = {
2527
- identifier,
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
- if (isExtensionSchema(fhirSchema)) {
2548
- const extensionSchema = await transformExtension(fhirSchema, register, fhirSchema.package_meta);
2549
- if (extensionSchema) {
2550
- results.push(extensionSchema);
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
- return transformFhirSchemaResource(register, fhirSchema);
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.step(`Loading FHIR packages: ${packageNames.join(", ")}`);
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.progress(`Converting ${structureDefinitions.length} StructureDefinitions to FHIRSchemas`);
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.debug(`Converted StructureDefinition: ${sd.name || sd.id} (${sd.resourceType})`);
2624
+ this.logger?.debug(`Converted StructureDefinition: ${sd.name || sd.id} (${sd.resourceType})`);
2597
2625
  } catch (error) {
2598
2626
  failedCount++;
2599
- this.logger.warn(
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.success(
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, _packageInfo) {
2637
+ async generateValueSetSchemas(valueSets, logger) {
2610
2638
  if (valueSets.length > 0) {
2611
- this.logger.debug(`${valueSets.length} ValueSets available for enum extraction`);
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.progress(`Converting ${valueSets.length} ValueSets to TypeSchema`);
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(await registerFromManager(this.manager), vs);
2649
+ const valueSetSchema = await transformValueSet(register, vs, logger);
2621
2650
  if (valueSetSchema) {
2622
2651
  valueSetSchemas.push(valueSetSchema);
2623
2652
  valueSetConvertedCount++;
2624
- this.logger.debug(`Converted ValueSet: ${vs.name || vs.id}`);
2653
+ this.logger?.debug(`Converted ValueSet: ${vs.name || vs.id}`);
2625
2654
  }
2626
2655
  } catch (error) {
2627
2656
  valueSetFailedCount++;
2628
- this.logger.warn(
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.success(
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.info(
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 allSchemas = [
2656
- ...(await Promise.all(register.allFs().map(async (fs) => await transformFhirSchema(register, fs)))).flat(),
2657
- ...await this.generateValueSetSchemas(register.allVs(), packageInfo)
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.info(`Applying treeshaking filter for ResourceTypes: ${treeshakeList.join(", ")}`);
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.warn(`ResourceType '${resourceType}' not found in structure definitions`);
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.info(`Excluded reference-only targets: ${Array.from(excludedReferenceTargets).join(", ")}`);
2763
+ this.logger?.info(`Excluded reference-only targets: ${Array.from(excludedReferenceTargets).join(", ")}`);
2736
2764
  }
2737
- this.logger.success(
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.logger.debug(`Loading from FHIR package: ${packageName}@${version || "latest"}`);
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
- * Enable/disable validation
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.reportProgress("Loading", 0, 4, "Loading TypeSchema data...");
5183
- await this.resolveSchemas();
5184
- this.logger.debug(`Resolved ${this.schemas.length} schemas`);
5185
- this.reportProgress("Validating", 1, 4, "Validating TypeSchema documents...");
5186
- if (this.options.validate) {
5187
- this.logger.debug("Starting schema validation");
5188
- await this.validateSchemas(result);
5189
- this.logger.debug("Schema validation completed");
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.reportProgress("Complete", 4, 4, "Generation completed successfully");
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 result;
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 resolveSchemas() {
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.reportProgress("Generating", 2 + current / generatorCount, 4, `Generating ${type}...`);
5727
+ this.logger.info(`Generating ${type}...`);
5293
5728
  try {
5294
- const files = await generator.generate(this.schemas);
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 = {