@atomic-ehr/codegen 0.0.1-canary.20251006094042.7f0be72 → 0.0.1-canary.20251007094146.5297616

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
  };
@@ -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) {
@@ -2216,7 +1921,7 @@ function buildEnum(register, element) {
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
1923
  console.warn(
2219
- `Value set ${valueSetUrl} has more than ${MAX_ENUM_LENGTH} codes, which may cause issues with code generation.`
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
  }
@@ -2483,75 +2188,391 @@ async function transformExtension(fhirSchema, register, _packageInfo) {
2483
2188
  extensionSchema.dependencies.push(...extractFieldDependencies(fields));
2484
2189
  }
2485
2190
  }
2486
- const nestedTypes = mkNestedTypes(register, fhirSchema);
2487
- if (nestedTypes && nestedTypes.length > 0) {
2488
- extensionSchema.nested = nestedTypes;
2489
- extensionSchema.dependencies.push(...extractNestedDependencies(nestedTypes));
2490
- }
2491
- extensionSchema.dependencies = deduplicateDependencies(extensionSchema.dependencies);
2492
- extensionSchema.dependencies = extensionSchema.dependencies.filter((dep) => dep.url !== identifier.url);
2493
- return extensionSchema;
2494
- } catch (error) {
2495
- console.warn(`Failed to transform extension ${fhirSchema.name}: ${error}`);
2496
- return null;
2191
+ const nestedTypes = mkNestedTypes(register, fhirSchema);
2192
+ if (nestedTypes && nestedTypes.length > 0) {
2193
+ extensionSchema.nested = nestedTypes;
2194
+ extensionSchema.dependencies.push(...extractNestedDependencies(nestedTypes));
2195
+ }
2196
+ extensionSchema.dependencies = deduplicateDependencies(extensionSchema.dependencies);
2197
+ extensionSchema.dependencies = extensionSchema.dependencies.filter((dep) => dep.url !== identifier.url);
2198
+ return extensionSchema;
2199
+ } catch (error) {
2200
+ console.warn(`Failed to transform extension ${fhirSchema.name}: ${error}`);
2201
+ return null;
2202
+ }
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) {
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);
2229
+ const nested = mkNestedTypes(register, fhirSchema);
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);
2240
+ return [typeSchema, ...bindingSchemas];
2241
+ }
2242
+ async function transformFhirSchema(register, fhirSchema) {
2243
+ const results = [];
2244
+ const identifier = mkIdentifier(fhirSchema);
2245
+ if (identifier.kind === "profile") {
2246
+ const profileSchema = await transformProfile(register, fhirSchema);
2247
+ results.push(profileSchema);
2248
+ const bindingSchemas = collectBindingSchemas(register, fhirSchema);
2249
+ results.push(...bindingSchemas);
2250
+ return results;
2251
+ }
2252
+ if (isExtensionSchema(fhirSchema)) {
2253
+ const extensionSchema = await transformExtension(fhirSchema, register, fhirSchema.package_meta);
2254
+ if (extensionSchema) {
2255
+ results.push(extensionSchema);
2256
+ }
2257
+ return results;
2258
+ }
2259
+ return transformFhirSchemaResource(register, fhirSchema);
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
+ }
2497
2477
  }
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;
2478
+ };
2479
+ var CodegenLogger = class _CodegenLogger {
2480
+ options;
2481
+ constructor(options = {}) {
2482
+ this.options = {
2483
+ timestamp: false,
2484
+ verbose: false,
2485
+ ...options
2486
+ };
2508
2487
  }
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}'`);
2488
+ formatMessage(level, message, color) {
2489
+ const timestamp = this.options.timestamp ? `${pc.gray((/* @__PURE__ */ new Date()).toLocaleTimeString())} ` : "";
2490
+ const prefix = this.options.prefix ? `${pc.cyan(`[${this.options.prefix}]`)} ` : "";
2491
+ return `${timestamp}${color(level)} ${prefix}${message}`;
2492
+ }
2493
+ /**
2494
+ * Success message with checkmark
2495
+ */
2496
+ success(message) {
2497
+ console.log(this.formatMessage("\u2705", message, pc.green));
2498
+ }
2499
+ /**
2500
+ * Error message with X mark
2501
+ */
2502
+ error(message, error) {
2503
+ console.error(this.formatMessage("\u274C", message, pc.red));
2504
+ if (error && this.options.verbose) {
2505
+ console.error(pc.red(` ${error.message}`));
2506
+ if (error.stack) {
2507
+ console.error(pc.gray(error.stack));
2508
+ }
2520
2509
  }
2521
- base = mkIdentifier(baseFs);
2522
2510
  }
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;
2511
+ /**
2512
+ * Warning message with warning sign
2513
+ */
2514
+ warn(message) {
2515
+ console.warn(this.formatMessage("\u26A0\uFE0F", message, pc.yellow));
2546
2516
  }
2547
- if (isExtensionSchema(fhirSchema)) {
2548
- const extensionSchema = await transformExtension(fhirSchema, register, fhirSchema.package_meta);
2549
- if (extensionSchema) {
2550
- results.push(extensionSchema);
2517
+ /**
2518
+ * Info message with info icon
2519
+ */
2520
+ info(message) {
2521
+ console.log(this.formatMessage("\u2139\uFE0F", message, pc.blue));
2522
+ }
2523
+ /**
2524
+ * Debug message (only shows in verbose mode)
2525
+ */
2526
+ debug(message) {
2527
+ if (this.options.verbose) {
2528
+ console.log(this.formatMessage("\u{1F41B}", message, pc.magenta));
2551
2529
  }
2552
- return results;
2553
2530
  }
2554
- return transformFhirSchemaResource(register, fhirSchema);
2531
+ /**
2532
+ * Step message with rocket
2533
+ */
2534
+ step(message) {
2535
+ console.log(this.formatMessage("\u{1F680}", message, pc.cyan));
2536
+ }
2537
+ /**
2538
+ * Progress message with clock
2539
+ */
2540
+ progress(message) {
2541
+ console.log(this.formatMessage("\u23F3", message, pc.blue));
2542
+ }
2543
+ /**
2544
+ * Plain message (no icon, just colored text)
2545
+ */
2546
+ plain(message, color = (s) => s) {
2547
+ const timestamp = this.options.timestamp ? `${pc.gray((/* @__PURE__ */ new Date()).toLocaleTimeString())} ` : "";
2548
+ const prefix = this.options.prefix ? `${pc.cyan(`[${this.options.prefix}]`)} ` : "";
2549
+ console.log(`${timestamp}${prefix}${color(message)}`);
2550
+ }
2551
+ /**
2552
+ * Dimmed/gray text for less important info
2553
+ */
2554
+ dim(message) {
2555
+ this.plain(message, pc.gray);
2556
+ }
2557
+ /**
2558
+ * Create a child logger with a prefix
2559
+ */
2560
+ child(prefix) {
2561
+ return new _CodegenLogger({
2562
+ ...this.options,
2563
+ prefix: this.options.prefix ? `${this.options.prefix}:${prefix}` : prefix
2564
+ });
2565
+ }
2566
+ /**
2567
+ * Update options
2568
+ */
2569
+ configure(options) {
2570
+ this.options = { ...this.options, ...options };
2571
+ }
2572
+ };
2573
+ new CodegenLogger();
2574
+ function createLogger(options = {}) {
2575
+ return new CodegenLogger(options);
2555
2576
  }
2556
2577
 
2557
2578
  // src/typeschema/generator.ts
@@ -2606,10 +2627,11 @@ var TypeSchemaGenerator = class {
2606
2627
  );
2607
2628
  return fhirSchemas;
2608
2629
  }
2609
- async generateValueSetSchemas(valueSets, _packageInfo) {
2630
+ async generateValueSetSchemas(valueSets) {
2610
2631
  if (valueSets.length > 0) {
2611
2632
  this.logger.debug(`${valueSets.length} ValueSets available for enum extraction`);
2612
2633
  }
2634
+ const register = await registerFromManager(this.manager);
2613
2635
  const valueSetSchemas = [];
2614
2636
  if (valueSets.length > 0) {
2615
2637
  this.logger.progress(`Converting ${valueSets.length} ValueSets to TypeSchema`);
@@ -2617,7 +2639,7 @@ var TypeSchemaGenerator = class {
2617
2639
  let valueSetFailedCount = 0;
2618
2640
  for (const vs of valueSets) {
2619
2641
  try {
2620
- const valueSetSchema = await transformValueSet(await registerFromManager(this.manager), vs);
2642
+ const valueSetSchema = await transformValueSet(register, vs);
2621
2643
  if (valueSetSchema) {
2622
2644
  valueSetSchemas.push(valueSetSchema);
2623
2645
  valueSetConvertedCount++;
@@ -2652,10 +2674,10 @@ var TypeSchemaGenerator = class {
2652
2674
  version: packageVersion || "latest"
2653
2675
  };
2654
2676
  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
- ];
2677
+ const valueSets = await this.generateValueSetSchemas(register.allVs());
2678
+ const fhirSchemas = (await Promise.all(register.allFs().map(async (fs2) => await transformFhirSchema(register, fs2)))).flat();
2679
+ const allSchemas = [...fhirSchemas, ...valueSets];
2680
+ console.debug(111);
2659
2681
  if (this.cache) {
2660
2682
  for (const schema of allSchemas) {
2661
2683
  await this.cache.set(schema);
@@ -3022,6 +3044,18 @@ var TypeSchemaParser = class {
3022
3044
  }
3023
3045
  };
3024
3046
 
3047
+ // src/typeschema/index.ts
3048
+ var generateTypeSchemas = async (register) => {
3049
+ const fhirSchemas = [];
3050
+ for (const fhirSchema of register.allFs()) {
3051
+ fhirSchemas.push(...await transformFhirSchema(register, fhirSchema));
3052
+ }
3053
+ for (const vsSchema of register.allVs()) {
3054
+ fhirSchemas.push(await transformValueSet(register, vsSchema));
3055
+ }
3056
+ return fhirSchemas;
3057
+ };
3058
+
3025
3059
  // src/api/generators/base/error-handler.ts
3026
3060
  init_errors();
3027
3061
  var ErrorHandler = class {
@@ -5046,26 +5080,249 @@ ${nestedInterfaces}`;
5046
5080
  }
5047
5081
  };
5048
5082
 
5083
+ // src/api/writer-generator/utils.ts
5084
+ var words = (s) => {
5085
+ return s.split(/(?<=[a-z])(?=[A-Z])|[-_.\s]/).filter(Boolean);
5086
+ };
5087
+ var kebabCase = (s) => {
5088
+ return words(s).map((s2) => s2.toLowerCase()).join("-");
5089
+ };
5090
+ var capitalCase = (s) => {
5091
+ if (s.length === 0) throw new Error("Empty string");
5092
+ return s[0]?.toUpperCase() + s.substring(1).toLowerCase();
5093
+ };
5094
+ var pascalCase = (s) => {
5095
+ return words(s).map(capitalCase).join("");
5096
+ };
5097
+ var FileSystemWriter = class {
5098
+ opts;
5099
+ currentDir;
5100
+ currentFileDescriptor;
5101
+ writtenFilesSet = /* @__PURE__ */ new Set();
5102
+ constructor(opts) {
5103
+ this.opts = opts;
5104
+ this.currentDir = opts.outputDir;
5105
+ }
5106
+ logger() {
5107
+ return this.opts.logger;
5108
+ }
5109
+ cd(path, gen) {
5110
+ this.currentDir = path.startsWith("/") ? Path2.join(this.opts.outputDir, path) : Path2.join(this.currentDir, path);
5111
+ if (!fs.existsSync(this.currentDir)) {
5112
+ fs.mkdirSync(this.currentDir, { recursive: true });
5113
+ }
5114
+ this.logger()?.debug(`cd '${this.currentDir}'`);
5115
+ gen();
5116
+ }
5117
+ cat(fn, gen) {
5118
+ if (this.currentFileDescriptor) throw new Error("Can't open file in file");
5119
+ if (fn.includes("/")) throw new Error(`Change file path separatly: ${fn}`);
5120
+ const fullFn = `${this.currentDir}/${fn}`;
5121
+ try {
5122
+ this.currentFileDescriptor = fs.openSync(fn, "w");
5123
+ this.writtenFilesSet.add(fn);
5124
+ this.logger()?.debug(`cat > '${fullFn}'`);
5125
+ gen();
5126
+ } finally {
5127
+ if (this.currentFileDescriptor) {
5128
+ fs.closeSync(this.currentFileDescriptor);
5129
+ }
5130
+ this.currentFileDescriptor = void 0;
5131
+ }
5132
+ }
5133
+ write(str) {
5134
+ if (!this.currentFileDescriptor) throw new Error("No file opened");
5135
+ fs.writeSync(this.currentFileDescriptor, str);
5136
+ }
5137
+ generate(_schemas) {
5138
+ throw new Error("Not implemented");
5139
+ }
5140
+ writtenFiles() {
5141
+ return Array.from(this.writtenFilesSet);
5142
+ }
5143
+ };
5144
+ var Writer = class extends FileSystemWriter {
5145
+ currentIndent = 0;
5146
+ indent() {
5147
+ this.currentIndent += this.opts.tabSize;
5148
+ }
5149
+ deindent() {
5150
+ this.currentIndent -= this.opts.tabSize;
5151
+ }
5152
+ writeIndent() {
5153
+ this.write(" ".repeat(this.currentIndent));
5154
+ }
5155
+ line(...tokens) {
5156
+ this.writeIndent();
5157
+ this.write(`${tokens.join(" ")}
5158
+ `);
5159
+ }
5160
+ lineSM(...tokens) {
5161
+ this.writeIndent();
5162
+ this.write(`${tokens.join(" ")};
5163
+ `);
5164
+ }
5165
+ comment(...tokens) {
5166
+ const lines = tokens.join(" ").split("\n");
5167
+ for (const line of lines) {
5168
+ this.line(this.opts.commentLinePrefix, line);
5169
+ }
5170
+ }
5171
+ debugComment(...tokens) {
5172
+ if (this.opts.withDebugComment) {
5173
+ tokens = tokens.map((token) => {
5174
+ if (typeof token === "string") {
5175
+ return token;
5176
+ } else {
5177
+ return JSON.stringify(token, null, 2);
5178
+ }
5179
+ });
5180
+ this.comment(...tokens);
5181
+ }
5182
+ }
5183
+ curlyBlock(tokens, gencontent, endTokens) {
5184
+ this.line(`${tokens.filter(Boolean).join(" ")} {`);
5185
+ this.indent();
5186
+ gencontent();
5187
+ this.deindent();
5188
+ this.line(`}${endTokens?.filter(Boolean).join(" ") ?? ""}`);
5189
+ }
5190
+ squareBlock(tokens, gencontent, endTokens) {
5191
+ this.line(`${tokens.filter(Boolean).join(" ")} [`);
5192
+ this.indent();
5193
+ gencontent();
5194
+ this.deindent();
5195
+ this.line(`]${endTokens?.filter(Boolean).join(" ") ?? ""}`);
5196
+ }
5197
+ };
5198
+
5199
+ // src/api/writer-generator/typescript.ts
5200
+ var collectComplexTypes = (tss) => tss.filter((t) => t.identifier.kind === "complex-type");
5201
+ var collectResources = (tss) => tss.filter((t) => t.identifier.kind === "resource");
5202
+ var groupByPackages = (typeSchemas) => {
5203
+ const grouped = {};
5204
+ for (const ts of typeSchemas) {
5205
+ const packageName = ts.identifier.package;
5206
+ if (!grouped[packageName]) {
5207
+ grouped[packageName] = [];
5208
+ }
5209
+ grouped[packageName].push(ts);
5210
+ }
5211
+ for (const [_packageName, typeSchemas2] of Object.entries(grouped)) {
5212
+ typeSchemas2.sort((a, b) => a.identifier.name.localeCompare(b.identifier.name));
5213
+ }
5214
+ return grouped;
5215
+ };
5216
+ var fileNameStem = (id) => {
5217
+ return pascalCase(id.name);
5218
+ };
5219
+ var normalizeName = (n) => {
5220
+ if (n === "extends") {
5221
+ return "extends_";
5222
+ }
5223
+ return n.replace(/[- ]/g, "_");
5224
+ };
5225
+ var resourceName = (id) => {
5226
+ return normalizeName(id.name);
5227
+ };
5228
+ var TypeScript = class extends Writer {
5229
+ tsImportFrom(tsPackage, ...entities) {
5230
+ this.lineSM(`import { ${entities.join(", ")} } from '${tsPackage}'`);
5231
+ }
5232
+ generatePackageIndexFile(schemas) {
5233
+ this.cat("index.ts", () => {
5234
+ let exports = schemas.map((schema) => ({
5235
+ identifier: schema.identifier,
5236
+ fileName: fileNameStem(schema.identifier),
5237
+ name: resourceName(schema.identifier)
5238
+ })).sort((a, b) => a.name.localeCompare(b.name));
5239
+ exports = Array.from(new Map(exports.map((exp) => [exp.name.toLowerCase(), exp])).values()).sort(
5240
+ (a, b) => a.name.localeCompare(b.name)
5241
+ );
5242
+ for (const exp of exports) {
5243
+ this.debugComment(exp.identifier);
5244
+ this.tsImportFrom(`./${exp.fileName}`, exp.name);
5245
+ }
5246
+ this.lineSM(`export { ${exports.map((e) => e.name).join(", ")} }`);
5247
+ this.line("");
5248
+ this.curlyBlock(["export type ResourceTypeMap = "], () => {
5249
+ this.lineSM("User: Record<string, any>");
5250
+ exports.forEach((exp) => {
5251
+ this.debugComment(exp.identifier);
5252
+ this.lineSM(`${exp.name}: ${exp.name}`);
5253
+ });
5254
+ });
5255
+ this.lineSM("export type ResourceType = keyof ResourceTypeMap");
5256
+ this.squareBlock(["export const resourceList: readonly ResourceType[] = "], () => {
5257
+ exports.forEach((exp) => {
5258
+ this.debugComment(exp.identifier);
5259
+ this.line(`'${exp.name}', `);
5260
+ });
5261
+ });
5262
+ });
5263
+ }
5264
+ generate(schemas) {
5265
+ const typesToGenerate = [
5266
+ ...collectComplexTypes(schemas),
5267
+ ...collectResources(schemas)
5268
+ // ...collectLogicalModels(typeSchemas),
5269
+ // ...collectProfiles(typeSchemas),
5270
+ ];
5271
+ const grouped = groupByPackages(typesToGenerate);
5272
+ this.cd("/", () => {
5273
+ for (const [packageName, packageSchemas] of Object.entries(grouped)) {
5274
+ const tsPackageName = kebabCase(packageName);
5275
+ this.cd(tsPackageName, () => {
5276
+ this.generatePackageIndexFile(packageSchemas);
5277
+ });
5278
+ }
5279
+ });
5280
+ }
5281
+ };
5282
+
5049
5283
  // src/api/builder.ts
5284
+ var writerToGenerator = (writerGen) => {
5285
+ const getGeneratedFiles = () => {
5286
+ return writerGen.writtenFiles().map((fn) => {
5287
+ return {
5288
+ path: Path2.normalize(Path2.join(writerGen.opts.outputDir, fn)),
5289
+ filename: fn.replace(/^.*[\\/]/, ""),
5290
+ content: "",
5291
+ exports: [],
5292
+ size: 0,
5293
+ timestamp: /* @__PURE__ */ new Date()
5294
+ };
5295
+ });
5296
+ };
5297
+ return {
5298
+ generate: async (schemas) => {
5299
+ writerGen.generate(schemas);
5300
+ return getGeneratedFiles();
5301
+ },
5302
+ setOutputDir: (outputDir) => writerGen.opts.outputDir = outputDir,
5303
+ build: async (_schemas) => getGeneratedFiles()
5304
+ };
5305
+ };
5050
5306
  var APIBuilder = class {
5051
5307
  schemas = [];
5052
5308
  options;
5053
5309
  generators = /* @__PURE__ */ new Map();
5054
- progressCallback;
5055
5310
  cache;
5056
5311
  pendingOperations = [];
5057
5312
  typeSchemaGenerator;
5058
5313
  logger;
5314
+ packages = [];
5315
+ progressCallback;
5059
5316
  typeSchemaConfig;
5060
5317
  constructor(options = {}) {
5061
5318
  this.options = {
5062
5319
  outputDir: options.outputDir || "./generated",
5063
5320
  verbose: options.verbose ?? false,
5064
5321
  overwrite: options.overwrite ?? true,
5065
- validate: options.validate ?? true,
5066
5322
  cache: options.cache ?? true,
5067
5323
  typeSchemaConfig: options.typeSchemaConfig,
5068
- manager: options.manager || null
5324
+ manager: options.manager || null,
5325
+ throwException: options.throwException || false
5069
5326
  };
5070
5327
  this.typeSchemaConfig = options.typeSchemaConfig;
5071
5328
  this.logger = options.logger || createLogger({
@@ -5076,13 +5333,8 @@ var APIBuilder = class {
5076
5333
  this.cache = new TypeSchemaCache(this.typeSchemaConfig);
5077
5334
  }
5078
5335
  }
5079
- /**
5080
- * Load TypeSchema from a FHIR package
5081
- */
5082
5336
  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);
5337
+ this.packages.push(packageMetaToNpm({ name: packageName, version: version || "latest" }));
5086
5338
  return this;
5087
5339
  }
5088
5340
  /**
@@ -5102,9 +5354,6 @@ var APIBuilder = class {
5102
5354
  this.schemas = [...this.schemas, ...schemas];
5103
5355
  return this;
5104
5356
  }
5105
- /**
5106
- * Configure TypeScript generation
5107
- */
5108
5357
  typescript(options = {}) {
5109
5358
  const typesOutputDir = `${this.options.outputDir}/types`;
5110
5359
  const generator = new TypeScriptGenerator({
@@ -5130,6 +5379,19 @@ var APIBuilder = class {
5130
5379
  this.logger.debug(`Configured TypeScript generator (${options.moduleFormat || "esm"})`);
5131
5380
  return this;
5132
5381
  }
5382
+ typescript2(opts) {
5383
+ const writerOpts = {
5384
+ outputDir: Path2.join(this.options.outputDir, "/types"),
5385
+ tabSize: 2,
5386
+ withDebugComment: true,
5387
+ commentLinePrefix: "//"
5388
+ };
5389
+ const effectiveOpts = { logger: this.logger, ...writerOpts, ...opts };
5390
+ const generator = writerToGenerator(new TypeScript(effectiveOpts));
5391
+ this.generators.set("typescript2", generator);
5392
+ this.logger.debug(`Configured TypeScript2 generator (${JSON.stringify(effectiveOpts, void 0, 2)})`);
5393
+ return this;
5394
+ }
5133
5395
  /**
5134
5396
  * Set a progress callback for monitoring generation
5135
5397
  */
@@ -5150,23 +5412,15 @@ var APIBuilder = class {
5150
5412
  }
5151
5413
  return this;
5152
5414
  }
5153
- /**
5154
- * Enable/disable verbose logging
5155
- */
5156
5415
  verbose(enabled = true) {
5157
5416
  this.options.verbose = enabled;
5417
+ this.logger?.configure({ verbose: enabled });
5158
5418
  return this;
5159
5419
  }
5160
- /**
5161
- * Enable/disable validation
5162
- */
5163
- validate(enabled = true) {
5164
- this.options.validate = enabled;
5420
+ throwException(enabled = true) {
5421
+ this.options.throwException = enabled;
5165
5422
  return this;
5166
5423
  }
5167
- /**
5168
- * Execute the generation process
5169
- */
5170
5424
  async generate() {
5171
5425
  const startTime = performance.now();
5172
5426
  const result = {
@@ -5179,35 +5433,33 @@ var APIBuilder = class {
5179
5433
  };
5180
5434
  this.logger.debug(`Starting generation with ${this.generators.size} generators`);
5181
5435
  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...");
5436
+ this.logger.info("Initialize Canonical Manager");
5437
+ const manager = CanonicalManager({
5438
+ packages: this.packages,
5439
+ workingDir: "tmp/fhir"
5440
+ });
5441
+ await manager.init();
5442
+ const register = await registerFromManager(manager, this.logger);
5443
+ const typeSchemas = await generateTypeSchemas(register);
5192
5444
  this.logger.debug(`Executing ${this.generators.size} generators`);
5193
- await this.executeGenerators(result);
5194
- this.reportProgress("Complete", 4, 4, "Generation completed successfully");
5445
+ await this.executeGenerators(result, typeSchemas);
5446
+ this.logger.info("Generation completed successfully");
5195
5447
  result.success = result.errors.length === 0;
5196
5448
  this.logger.debug(`Generation completed: ${result.filesGenerated.length} files`);
5197
5449
  } catch (error) {
5198
5450
  this.logger.error("Code generation failed", error instanceof Error ? error : new Error(String(error)));
5199
5451
  result.errors.push(error instanceof Error ? error.message : String(error));
5200
- result.success = false;
5201
- } finally {
5202
- result.duration = performance.now() - startTime;
5203
5452
  }
5204
- return result;
5453
+ return {
5454
+ ...result,
5455
+ success: result.errors.length === 0,
5456
+ duration: performance.now() - startTime
5457
+ };
5205
5458
  }
5206
5459
  /**
5207
5460
  * Generate and return the results without writing to files
5208
5461
  */
5209
5462
  async build() {
5210
- await this.resolveSchemas();
5211
5463
  const results = {};
5212
5464
  for (const [type, generator] of this.generators.entries()) {
5213
5465
  if (generator.build) {
@@ -5237,24 +5489,6 @@ var APIBuilder = class {
5237
5489
  getGenerators() {
5238
5490
  return Array.from(this.generators.keys());
5239
5491
  }
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
5492
  async loadFromFiles(filePaths) {
5259
5493
  if (!this.typeSchemaGenerator) {
5260
5494
  this.typeSchemaGenerator = new TypeSchemaGenerator(
@@ -5267,8 +5501,7 @@ var APIBuilder = class {
5267
5501
  );
5268
5502
  }
5269
5503
  const parser = new TypeSchemaParser({
5270
- format: "auto",
5271
- validate: this.options.validate
5504
+ format: "auto"
5272
5505
  });
5273
5506
  const schemas = await parser.parseFromFiles(filePaths);
5274
5507
  this.schemas = [...this.schemas, ...schemas];
@@ -5276,37 +5509,18 @@ var APIBuilder = class {
5276
5509
  this.cache.setMany(schemas);
5277
5510
  }
5278
5511
  }
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;
5512
+ async executeGenerators(result, typeSchemas) {
5291
5513
  for (const [type, generator] of this.generators.entries()) {
5292
- this.reportProgress("Generating", 2 + current / generatorCount, 4, `Generating ${type}...`);
5514
+ this.logger.info(`Generating ${type}...`);
5293
5515
  try {
5294
- const files = await generator.generate(this.schemas);
5516
+ const files = await generator.generate(typeSchemas);
5295
5517
  result.filesGenerated.push(...files.map((f) => f.path || f.filename));
5518
+ this.logger.info(`Generating ${type} finished successfully`);
5296
5519
  } catch (error) {
5297
5520
  result.errors.push(
5298
5521
  `${type} generator failed: ${error instanceof Error ? error.message : String(error)}`
5299
5522
  );
5300
5523
  }
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
5524
  }
5311
5525
  }
5312
5526
  };
@@ -5318,7 +5532,6 @@ function createAPIFromConfig(config) {
5318
5532
  outputDir: config.outputDir,
5319
5533
  verbose: config.verbose,
5320
5534
  overwrite: config.overwrite,
5321
- validate: config.validate,
5322
5535
  cache: config.cache,
5323
5536
  typeSchemaConfig: config.typeSchema
5324
5537
  });
@@ -5338,15 +5551,13 @@ function createAPIFromConfig(config) {
5338
5551
  async function generateTypesFromPackage(packageName, outputDir, options = {}) {
5339
5552
  return createAPI({
5340
5553
  outputDir,
5341
- verbose: options.verbose,
5342
- validate: options.validate
5554
+ verbose: options.verbose
5343
5555
  }).fromPackage(packageName, options.version).typescript().generate();
5344
5556
  }
5345
5557
  async function generateTypesFromFiles(inputFiles, outputDir, options = {}) {
5346
5558
  return createAPI({
5347
5559
  outputDir,
5348
- verbose: options.verbose,
5349
- validate: options.validate
5560
+ verbose: options.verbose
5350
5561
  }).fromFiles(...inputFiles).typescript().generate();
5351
5562
  }
5352
5563
  var DEFAULT_CONFIG = {