@atomic-ehr/codegen 0.0.1-canary.20251015064414.0fcee22 → 0.0.1

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,11 +1,12 @@
1
1
  import * as fs from 'fs';
2
- import { existsSync, mkdirSync } from 'fs';
3
- import * as afs from 'fs/promises';
2
+ import fs__default, { existsSync, mkdirSync } from 'fs';
3
+ import * as afs2 from 'fs/promises';
4
4
  import { readdir, stat, unlink, readFile, writeFile, access, mkdir, rm } from 'fs/promises';
5
- import * as Path2 from 'path';
6
- import { join, resolve, dirname, relative } from 'path';
5
+ import * as Path4 from 'path';
6
+ import Path4__default, { join, resolve, dirname, relative } from 'path';
7
7
  import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
8
8
  import * as fhirschema from '@atomic-ehr/fhirschema';
9
+ import { isStructureDefinition } from '@atomic-ehr/fhirschema';
9
10
  import pc from 'picocolors';
10
11
 
11
12
  var __defProp = Object.defineProperty;
@@ -1445,6 +1446,22 @@ var isChoiceDeclarationField = (field) => {
1445
1446
  if (!field) return false;
1446
1447
  return field.choices !== void 0;
1447
1448
  };
1449
+ var isValueSet = (res) => {
1450
+ return res.resourceType === "ValueSet";
1451
+ };
1452
+ var isCodeSystem = (res) => {
1453
+ return res.resourceType === "CodeSystem";
1454
+ };
1455
+ var enrichValueSet = (vs, packageMeta) => {
1456
+ if (!vs.url) throw new Error("ValueSet must have a URL");
1457
+ if (!vs.name) throw new Error("ValueSet must have a name");
1458
+ return {
1459
+ ...vs,
1460
+ package_meta: vs.package_meta || packageMeta,
1461
+ name: vs.name,
1462
+ url: vs.url
1463
+ };
1464
+ };
1448
1465
 
1449
1466
  // src/typeschema/core/identifier.ts
1450
1467
  function dropVersionFromUrl(url) {
@@ -1479,7 +1496,7 @@ var getValueSetName = (url) => {
1479
1496
  }
1480
1497
  return url;
1481
1498
  };
1482
- function mkValueSetIdentifierByUrl(register, fullValueSetUrl) {
1499
+ function mkValueSetIdentifierByUrl(register, pkg, fullValueSetUrl) {
1483
1500
  const valueSetUrl = dropVersionFromUrl(fullValueSetUrl);
1484
1501
  const valueSetNameFallback = getValueSetName(valueSetUrl);
1485
1502
  const valuesSetFallback = {
@@ -1488,7 +1505,7 @@ function mkValueSetIdentifierByUrl(register, fullValueSetUrl) {
1488
1505
  version: getVersionFromUrl(valueSetUrl) || "0.0.0"
1489
1506
  },
1490
1507
  id: fullValueSetUrl};
1491
- const valueSet = register.resolveVs(valueSetUrl) || valuesSetFallback;
1508
+ const valueSet = register.resolveVs(pkg, valueSetUrl) || valuesSetFallback;
1492
1509
  const valueSetName = valueSet?.id && !/^[a-zA-Z0-9_-]{20,}$/.test(valueSet.id) ? valueSet.id : valueSetNameFallback;
1493
1510
  return {
1494
1511
  kind: "value-set",
@@ -1514,8 +1531,8 @@ function mkBindingIdentifier(fhirSchema, path, bindingName) {
1514
1531
  function mkNestedIdentifier(register, fhirSchema, path, logger) {
1515
1532
  const nestedTypeOrigins = {};
1516
1533
  if (fhirSchema.derivation === "constraint") {
1517
- const specializations = register.resolveFsSpecializations(fhirSchema.url);
1518
- const nestedTypeGenealogy = specializations.map((fs3) => mkNestedTypes(register, fs3, logger)).filter((e) => e !== void 0).flat();
1534
+ const specializations = register.resolveFsSpecializations(fhirSchema.package_meta, fhirSchema.url);
1535
+ const nestedTypeGenealogy = specializations.map((fs4) => mkNestedTypes(register, fs4, logger)).filter((e) => e !== void 0).flat();
1519
1536
  for (const nt of nestedTypeGenealogy.reverse()) {
1520
1537
  nestedTypeOrigins[nt.identifier.name] = nt.identifier.url;
1521
1538
  }
@@ -1575,7 +1592,7 @@ function mkNestedTypes(register, fhirSchema, logger) {
1575
1592
  package: fhirSchema.package_meta.name,
1576
1593
  version: fhirSchema.package_meta.version,
1577
1594
  name: baseName,
1578
- url: register.ensureSpecializationCanonicalUrl(baseName)
1595
+ url: register.ensureSpecializationCanonicalUrl(fhirSchema.package_meta, baseName)
1579
1596
  };
1580
1597
  const fields = transformNestedElements(register, fhirSchema, path, element.elements, logger);
1581
1598
  const nestedType = {
@@ -1610,10 +1627,10 @@ function extractNestedDependencies(nestedTypes) {
1610
1627
  function isRequired(register, fhirSchema, path) {
1611
1628
  const fieldName = path[path.length - 1];
1612
1629
  const parentPath = path.slice(0, -1);
1613
- const requires = register.resolveFsGenealogy(fhirSchema.url).flatMap((fs3) => {
1614
- if (parentPath.length === 0) return fs3.required || [];
1615
- if (!fs3.elements) return [];
1616
- let elem = fs3;
1630
+ const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs4) => {
1631
+ if (parentPath.length === 0) return fs4.required || [];
1632
+ if (!fs4.elements) return [];
1633
+ let elem = fs4;
1617
1634
  for (const k of parentPath) {
1618
1635
  elem = elem?.elements?.[k];
1619
1636
  }
@@ -1625,10 +1642,10 @@ function isExcluded(register, fhirSchema, path) {
1625
1642
  const fieldName = path[path.length - 1];
1626
1643
  if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
1627
1644
  const parentPath = path.slice(0, -1);
1628
- const requires = register.resolveFsGenealogy(fhirSchema.url).flatMap((fs3) => {
1629
- if (parentPath.length === 0) return fs3.excluded || [];
1630
- if (!fs3.elements) return [];
1631
- let elem = fs3;
1645
+ const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs4) => {
1646
+ if (parentPath.length === 0) return fs4.excluded || [];
1647
+ if (!fs4.elements) return [];
1648
+ let elem = fs4;
1632
1649
  for (const k of parentPath) {
1633
1650
  elem = elem?.elements?.[k];
1634
1651
  }
@@ -1636,12 +1653,12 @@ function isExcluded(register, fhirSchema, path) {
1636
1653
  });
1637
1654
  return new Set(requires).has(fieldName);
1638
1655
  }
1639
- var buildReferences = (element, register, _packageInfo) => {
1656
+ var buildReferences = (register, fhirSchema, element) => {
1640
1657
  if (!element.refers) return void 0;
1641
1658
  return element.refers.map((ref) => {
1642
- const curl = register.ensureSpecializationCanonicalUrl(ref);
1643
- const fs3 = register.resolveFs(curl);
1644
- return mkIdentifier(fs3);
1659
+ const curl = register.ensureSpecializationCanonicalUrl(fhirSchema.package_meta, ref);
1660
+ const fs4 = register.resolveFs(fhirSchema.package_meta, curl);
1661
+ return mkIdentifier(fs4);
1645
1662
  });
1646
1663
  };
1647
1664
  function buildFieldType(register, fhirSchema, path, element, logger) {
@@ -1649,9 +1666,9 @@ function buildFieldType(register, fhirSchema, path, element, logger) {
1649
1666
  const refPath = element.elementReference.slice(1).filter((_, i) => i % 2 === 1);
1650
1667
  return mkNestedIdentifier(register, fhirSchema, refPath, logger);
1651
1668
  } else if (element.type) {
1652
- const url = register.ensureSpecializationCanonicalUrl(element.type);
1653
- const fieldFs = register.resolveFs(url);
1654
- if (!fieldFs) throw new Error(`Could not resolve field '${element.type}'`);
1669
+ const url = register.ensureSpecializationCanonicalUrl(fhirSchema.package_meta, element.type);
1670
+ const fieldFs = register.resolveFs(fhirSchema.package_meta, url);
1671
+ if (!fieldFs) throw new Error(`Could not resolve field type: '${element.type}'`);
1655
1672
  return mkIdentifier(fieldFs);
1656
1673
  } else if (element.choices) {
1657
1674
  return void 0;
@@ -1670,14 +1687,14 @@ var mkField = (register, fhirSchema, path, element, logger) => {
1670
1687
  if (element.binding) {
1671
1688
  binding = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
1672
1689
  if (element.binding.strength === "required" && element.type === "code") {
1673
- enumValues = buildEnum(register, element, logger);
1690
+ enumValues = buildEnum(register, fhirSchema, element, logger);
1674
1691
  }
1675
1692
  }
1676
1693
  return {
1677
1694
  type: buildFieldType(register, fhirSchema, path, element, logger),
1678
1695
  required: isRequired(register, fhirSchema, path),
1679
1696
  excluded: isExcluded(register, fhirSchema, path),
1680
- reference: buildReferences(element, register, fhirSchema.package_meta),
1697
+ reference: buildReferences(register, fhirSchema, element),
1681
1698
  array: element.array || false,
1682
1699
  min: element.min,
1683
1700
  max: element.max,
@@ -1703,9 +1720,9 @@ function mkNestedField(register, fhirSchema, path, element, logger) {
1703
1720
  }
1704
1721
 
1705
1722
  // src/typeschema/core/binding.ts
1706
- function extractValueSetConceptsByUrl(register, valueSetUrl, logger) {
1723
+ function extractValueSetConceptsByUrl(register, pkg, valueSetUrl, logger) {
1707
1724
  const cleanUrl = dropVersionFromUrl(valueSetUrl) || valueSetUrl;
1708
- const valueSet = register.resolveVs(cleanUrl);
1725
+ const valueSet = register.resolveVs(pkg, cleanUrl);
1709
1726
  if (!valueSet) return void 0;
1710
1727
  return extractValueSetConcepts(register, valueSet);
1711
1728
  }
@@ -1748,14 +1765,14 @@ function extractValueSetConcepts(register, valueSet, _logger) {
1748
1765
  return concepts.length > 0 ? concepts : void 0;
1749
1766
  }
1750
1767
  var MAX_ENUM_LENGTH = 100;
1751
- function buildEnum(register, element, logger) {
1768
+ function buildEnum(register, fhirSchema, element, logger) {
1752
1769
  if (!element.binding) return void 0;
1753
1770
  const strength = element.binding.strength;
1754
1771
  const valueSetUrl = element.binding.valueSet;
1755
1772
  if (!valueSetUrl) return void 0;
1756
1773
  const shouldGenerateEnum = strength === "required" || strength === "extensible" && (element.type === "code" || element.type === "Coding") || strength === "preferred" && (element.type === "code" || element.type === "Coding");
1757
1774
  if (!shouldGenerateEnum) return void 0;
1758
- const concepts = extractValueSetConceptsByUrl(register, valueSetUrl);
1775
+ const concepts = extractValueSetConceptsByUrl(register, fhirSchema.package_meta, valueSetUrl);
1759
1776
  if (!concepts || concepts.length === 0) return void 0;
1760
1777
  const codes = concepts.map((c) => c.code).filter((code) => code && typeof code === "string" && code.trim().length > 0);
1761
1778
  if (codes.length > MAX_ENUM_LENGTH) {
@@ -1770,13 +1787,17 @@ function generateBindingSchema(register, fhirSchema, path, element, logger) {
1770
1787
  if (!element.binding?.valueSet) return void 0;
1771
1788
  const identifier = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
1772
1789
  const fieldType = buildFieldType(register, fhirSchema, path, element, logger);
1773
- const valueSetIdentifier = mkValueSetIdentifierByUrl(register, element.binding.valueSet);
1790
+ const valueSetIdentifier = mkValueSetIdentifierByUrl(
1791
+ register,
1792
+ fhirSchema.package_meta,
1793
+ element.binding.valueSet
1794
+ );
1774
1795
  const dependencies = [];
1775
1796
  if (fieldType) {
1776
1797
  dependencies.push(fieldType);
1777
1798
  }
1778
1799
  dependencies.push(valueSetIdentifier);
1779
- const enumValues = buildEnum(register, element, logger);
1800
+ const enumValues = buildEnum(register, fhirSchema, element, logger);
1780
1801
  return {
1781
1802
  identifier,
1782
1803
  type: fieldType,
@@ -1877,8 +1898,8 @@ function isExtensionSchema(fhirSchema, _identifier) {
1877
1898
  }
1878
1899
  async function transformValueSet(register, valueSet, logger) {
1879
1900
  if (!valueSet.url) throw new Error("ValueSet URL is required");
1880
- const identifier = mkValueSetIdentifierByUrl(register, valueSet.url);
1881
- const concept = extractValueSetConceptsByUrl(register, valueSet.url);
1901
+ const identifier = mkValueSetIdentifierByUrl(register, valueSet.package_meta, valueSet.url);
1902
+ const concept = extractValueSetConceptsByUrl(register, valueSet.package_meta, valueSet.url);
1882
1903
  return {
1883
1904
  identifier,
1884
1905
  description: valueSet.description,
@@ -1964,7 +1985,10 @@ function transformFhirSchemaResource(register, fhirSchema, logger) {
1964
1985
  const identifier = mkIdentifier(fhirSchema);
1965
1986
  let base;
1966
1987
  if (fhirSchema.base && fhirSchema.type !== "Element") {
1967
- const baseFs = register.resolveFs(register.ensureSpecializationCanonicalUrl(fhirSchema.base));
1988
+ const baseFs = register.resolveFs(
1989
+ fhirSchema.package_meta,
1990
+ register.ensureSpecializationCanonicalUrl(fhirSchema.package_meta, fhirSchema.base)
1991
+ );
1968
1992
  if (!baseFs) {
1969
1993
  throw new Error(`Base resource not found '${fhirSchema.base}' for '${fhirSchema.url}'`);
1970
1994
  }
@@ -2336,120 +2360,129 @@ new CodegenLogger();
2336
2360
  function createLogger(options = {}) {
2337
2361
  return new CodegenLogger(options);
2338
2362
  }
2339
- var registerFromManager = async (manager, conf) => {
2340
- const packageNameResolver = conf?.packageNameResolver ?? ["hl7.fhir.r5.core", "hl7.cda.uv.core"];
2341
- const logger = conf?.logger;
2342
- const flatRawIndex = {};
2343
- const canonicalToPackages = {};
2344
- const resourceTypes = /* @__PURE__ */ new Set(["StructureDefinition", "ValueSet", "CodeSystem"]);
2345
- for (const res of await manager.search({})) {
2346
- const rawUrl = res.url;
2347
- if (!rawUrl) continue;
2348
- if (!resourceTypes.has(res.resourceType)) continue;
2349
- const url = rawUrl;
2350
- const pkg = (await manager.resolveEntry(url)).package;
2351
- if (!pkg) throw new Error(`Can't resolve package for ${url}`);
2352
- if (!canonicalToPackages[url]) canonicalToPackages[url] = [];
2353
- canonicalToPackages[url].push(pkg);
2354
- flatRawIndex[url] = res;
2355
- }
2356
- const collisions = Object.entries(canonicalToPackages).filter(([_, e]) => e.length > 1).map(([url, pkgs]) => `${url}: ${pkgs.map((p) => `${p.name}@${p.version}`).join(", ")}`).join("\n");
2357
- logger?.warn(`Duplicated canonicals: ${collisions}`);
2358
- const packageToResources = {};
2359
- const packageToPackageMeta = {};
2360
- for (const [url, _pkgs] of Object.entries(canonicalToPackages)) {
2361
- const pkg = (await manager.resolveEntry(url)).package;
2362
- if (!pkg) throw new Error(`Can't find package for ${url}`);
2363
- const pkgId = packageMetaToFhir(pkg);
2364
- packageToPackageMeta[pkgId] = pkg;
2365
- const res = await manager.resolve(url);
2366
- if (!packageToResources[pkgId]) {
2367
- packageToResources[pkgId] = {};
2368
- }
2369
- const index = packageToResources[pkgId];
2370
- if (!index) throw new Error(`Can't find index for ${pkg.name}@${pkg.version}`);
2371
- index[url] = res;
2372
- }
2373
- const indexByPackages = [];
2374
- for (const [pkgId, index] of Object.entries(packageToResources)) {
2375
- indexByPackages.push({
2376
- package_meta: packageToPackageMeta[pkgId],
2377
- index
2363
+ var readPackageJSON = async (workDir, packageMeta) => {
2364
+ const packageJSONFileName = Path4.join(workDir, "node_modules", packageMeta.name, "package.json");
2365
+ const packageJSON = JSON.parse(await afs2.readFile(packageJSONFileName, "utf8"));
2366
+ return packageJSON;
2367
+ };
2368
+ var readPackageDependencies = async (workDir, packageMeta) => {
2369
+ const packageJSON = await readPackageJSON(workDir, packageMeta);
2370
+ const dependencies = packageJSON.dependencies;
2371
+ if (dependencies !== void 0) {
2372
+ return Object.entries(dependencies).map(([name, version]) => {
2373
+ return { name, version };
2378
2374
  });
2379
2375
  }
2380
- const sdIndex = {};
2381
- const vsIndex = {};
2382
- const fsIndex = {};
2383
- for (const resourcesByPackage of indexByPackages) {
2384
- const packageMeta = resourcesByPackage.package_meta;
2385
- let counter = 0;
2386
- logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(packageMeta)}' begins...`);
2387
- for (const [surl, resource] of Object.entries(resourcesByPackage.index)) {
2388
- const url = surl;
2389
- if (resource.resourceType === "StructureDefinition") {
2390
- const sd = resource;
2391
- sdIndex[url] = sd;
2392
- const rfs = enrichFHIRSchema(fhirschema.translate(sd), packageMeta);
2393
- counter++;
2394
- fsIndex[rfs.url] = rfs;
2395
- }
2396
- if (resource.resourceType === "ValueSet") {
2397
- if (!resource.package_meta) {
2398
- resource.package_meta = packageMeta;
2376
+ return [];
2377
+ };
2378
+ var mkEmptyPkgIndex = (pkg) => {
2379
+ return { pkg, nameResolution: {}, canonicalResolution: {}, fhirSchemas: {}, valueSets: {} };
2380
+ };
2381
+ var mkPackageAwareResolver = async (manager, pkg, deep = 0, _logger) => {
2382
+ const options = {};
2383
+ const deps = await readPackageDependencies("tmp/fhir", pkg);
2384
+ for (const dep of deps) {
2385
+ const depOptions = mkPackageAwareResolver(manager, dep, deep);
2386
+ Object.assign(options, depOptions);
2387
+ }
2388
+ const pkgId = packageMetaToFhir(pkg);
2389
+ if (!options[pkgId]) options[pkgId] = mkEmptyPkgIndex(pkg);
2390
+ for (const resource of await manager.search({ package: pkg })) {
2391
+ const rawUrl = resource.url;
2392
+ if (!rawUrl) continue;
2393
+ if (!(isStructureDefinition(resource) || isValueSet(resource) || isCodeSystem(resource))) continue;
2394
+ const url = rawUrl;
2395
+ if (!options[pkgId].canonicalResolution[url]) {
2396
+ options[pkgId].canonicalResolution[url] = [];
2397
+ for (const [depPkgId, { canonicalResolution }] of Object.entries(options)) {
2398
+ if (pkgId === depPkgId) continue;
2399
+ if (!canonicalResolution[url]) continue;
2400
+ for (const deepRes of canonicalResolution[url]) {
2401
+ options[pkgId].canonicalResolution[url].push({
2402
+ deep: deepRes.deep + 1,
2403
+ resource: deepRes.resource,
2404
+ packageMeta: deepRes.packageMeta
2405
+ });
2399
2406
  }
2400
- vsIndex[resource.url] = resource;
2401
2407
  }
2402
2408
  }
2403
- logger?.success(
2404
- `FHIR Schema conversion for '${packageMetaToFhir(packageMeta)}' completed: ${counter} successful`
2405
- );
2406
- }
2407
- const specNameToCanonicals = {};
2408
- for (const rfs of Object.values(fsIndex)) {
2409
- if (rfs.derivation === "constraint") continue;
2410
- const name = rfs.name;
2411
- if (!specNameToCanonicals[name]) specNameToCanonicals[name] = {};
2412
- specNameToCanonicals[name][rfs.package_meta.name] = rfs.url;
2413
- }
2414
- const specNameToCanonical = {};
2415
- for (const [sname, canonicals] of Object.entries(specNameToCanonicals)) {
2416
- const name = sname;
2417
- const canonicalValues = Object.values(canonicals);
2418
- if (canonicalValues.length === 1) {
2419
- const url = canonicalValues[0];
2420
- specNameToCanonical[name] = url;
2421
- } else {
2422
- for (const pname of packageNameResolver) {
2423
- if (canonicals[pname]) {
2424
- specNameToCanonical[name] = canonicals[pname];
2425
- break;
2426
- }
2409
+ options[pkgId].canonicalResolution[url].push({ deep, packageMeta: pkg, resource });
2410
+ options[pkgId].canonicalResolution[url].sort((a, b) => a.deep - b.deep);
2411
+ const name = resource.name;
2412
+ if (name && isStructureDefinition(resource) && (resource.derivation === "specialization" || resource.derivation === void 0)) {
2413
+ if (!options[pkgId].nameResolution[name]) {
2414
+ options[pkgId].nameResolution[name] = resource.url;
2415
+ } else {
2416
+ throw new Error(`Duplicate name ${name} in package ${pkgId}`);
2427
2417
  }
2428
- if (specNameToCanonical[name] === void 0) throw new Error(`No canonical URL found for ${name}`);
2429
2418
  }
2430
2419
  }
2431
- const complexTypes = {};
2432
- for (const fs3 of Object.values(fsIndex)) {
2433
- if (fs3.kind === "complex-type") {
2434
- complexTypes[fs3.url] = fs3;
2420
+ return options;
2421
+ };
2422
+ var packageAwareResolveName = (resolver, pkg, name) => {
2423
+ const pkgId = packageMetaToFhir(pkg);
2424
+ if (!resolver[pkgId]) throw new Error(`Package ${pkgId} not found`);
2425
+ const resource = resolver[pkgId].nameResolution[name];
2426
+ return resource;
2427
+ };
2428
+ var packageAgnosticResolveCanonical = (resolver, url, logger) => {
2429
+ const options = Object.values(resolver).flatMap((pkg) => pkg.canonicalResolution[url]);
2430
+ if (!options) throw new Error(`No canonical resolution found for ${url} in any package`);
2431
+ if (options.length > 1)
2432
+ logger?.dry_warn(
2433
+ `Multiple canonical resolutions found for ${url} in: ${JSON.stringify(options, void 0, 2)}`
2434
+ );
2435
+ return options[0]?.resource;
2436
+ };
2437
+ var registerFromManager = async (manager, { logger, fallbackPackageForNameResolution }) => {
2438
+ const packages = await manager.packages();
2439
+ const resolver = {};
2440
+ for (const pkg of packages) {
2441
+ Object.assign(resolver, await mkPackageAwareResolver(manager, pkg));
2442
+ }
2443
+ for (const { pkg, canonicalResolution } of Object.values(resolver)) {
2444
+ const pkgId = packageMetaToFhir(pkg);
2445
+ if (!resolver[pkgId]) throw new Error(`Package ${pkgId} not found`);
2446
+ let counter = 0;
2447
+ logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' begins...`);
2448
+ for (const [_url, options] of Object.entries(canonicalResolution)) {
2449
+ const resource = options[0]?.resource;
2450
+ if (!resource) throw new Error(`Resource not found`);
2451
+ if (isStructureDefinition(resource)) {
2452
+ const rfs = enrichFHIRSchema(fhirschema.translate(resource), pkg);
2453
+ counter++;
2454
+ resolver[pkgId].fhirSchemas[rfs.url] = rfs;
2455
+ }
2456
+ if (isValueSet(resource)) {
2457
+ const rvs = enrichValueSet(resource, pkg);
2458
+ resolver[pkgId].valueSets[rvs.url] = rvs;
2459
+ }
2435
2460
  }
2461
+ logger?.success(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' completed: ${counter} successful`);
2436
2462
  }
2437
- const resolveFsGenealogy = (canonicalUrl) => {
2438
- let fs3 = fsIndex[canonicalUrl];
2439
- if (fs3 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
2440
- const genealogy = [fs3];
2441
- while (fs3?.base) {
2442
- fs3 = fsIndex[fs3.base] || fsIndex[specNameToCanonical[fs3.base]];
2443
- genealogy.push(fs3);
2444
- if (fs3 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
2463
+ const resolveFs = (pkg, canonicalUrl) => {
2464
+ return resolver[packageMetaToFhir(pkg)]?.fhirSchemas[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.fhirSchemas[canonicalUrl];
2465
+ };
2466
+ const resolveVs = (pkg, canonicalUrl) => {
2467
+ return resolver[packageMetaToFhir(pkg)]?.valueSets[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.valueSets[canonicalUrl];
2468
+ };
2469
+ const ensureSpecializationCanonicalUrl = (pkg, name) => packageAwareResolveName(resolver, pkg, name) || fallbackPackageForNameResolution && packageAwareResolveName(resolver, fallbackPackageForNameResolution, name) || name;
2470
+ const resolveFsGenealogy = (pkg, canonicalUrl) => {
2471
+ let fs4 = resolveFs(pkg, canonicalUrl);
2472
+ if (fs4 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
2473
+ const genealogy = [fs4];
2474
+ while (fs4?.base) {
2475
+ fs4 = resolveFs(fs4.package_meta, fs4.base) || resolveFs(fs4.package_meta, ensureSpecializationCanonicalUrl(fs4.package_meta, fs4.base));
2476
+ if (fs4 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
2477
+ genealogy.push(fs4);
2445
2478
  }
2446
2479
  return genealogy;
2447
2480
  };
2448
- const resolveFsSpecializations = (canonicalUrl) => {
2449
- return resolveFsGenealogy(canonicalUrl).filter((fs3) => fs3.derivation === "specialization");
2481
+ const resolveFsSpecializations = (pkg, canonicalUrl) => {
2482
+ return resolveFsGenealogy(pkg, canonicalUrl).filter((fs4) => fs4.derivation === "specialization");
2450
2483
  };
2451
2484
  const resolveElementSnapshot = (fhirSchema, path) => {
2452
- const geneology = resolveFsGenealogy(fhirSchema.url);
2485
+ const geneology = resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url);
2453
2486
  const elemGeneology = resolveFsElementGenealogy(geneology, path);
2454
2487
  const elemSnapshot = fsElementSnapshot(elemGeneology);
2455
2488
  return elemSnapshot;
@@ -2468,22 +2501,26 @@ var registerFromManager = async (manager, conf) => {
2468
2501
  };
2469
2502
  return {
2470
2503
  ...manager,
2471
- appendFs(fs3) {
2472
- const rfs = enrichFHIRSchema(fs3);
2473
- fsIndex[rfs.url] = rfs;
2474
- specNameToCanonical[rfs.name] = rfs.url;
2504
+ unsafeAppendFs(fs4) {
2505
+ const rfs = enrichFHIRSchema(fs4);
2506
+ const pkgId = packageMetaToFhir(rfs.package_meta);
2507
+ if (!resolver[pkgId]) resolver[pkgId] = mkEmptyPkgIndex(rfs.package_meta);
2508
+ resolver[pkgId].nameResolution[rfs.name] = rfs.url;
2509
+ resolver[pkgId].fhirSchemas[rfs.url] = rfs;
2475
2510
  },
2476
- resolveFs: (canonicalUrl) => fsIndex[canonicalUrl],
2511
+ resolveFs,
2477
2512
  resolveFsGenealogy,
2478
2513
  resolveFsSpecializations,
2479
- ensureSpecializationCanonicalUrl: (name) => specNameToCanonical[name] || name,
2480
- allSd: () => Object.values(sdIndex),
2481
- resolveSd: (canonicalUrl) => sdIndex[canonicalUrl],
2482
- allFs: () => Object.values(fsIndex),
2483
- allVs: () => Object.values(vsIndex),
2484
- resolveVs: (canonicalUrl) => vsIndex[canonicalUrl],
2485
- complexTypeDict: () => complexTypes,
2486
- resolveAny: (canonicalUrl) => flatRawIndex[canonicalUrl],
2514
+ ensureSpecializationCanonicalUrl,
2515
+ resolveSd: (_pkg, canonicalUrl) => {
2516
+ const res = packageAgnosticResolveCanonical(resolver, canonicalUrl, logger);
2517
+ if (isStructureDefinition(res)) return res;
2518
+ return void 0;
2519
+ },
2520
+ allFs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.fhirSchemas)),
2521
+ allVs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.valueSets)),
2522
+ resolveVs,
2523
+ resolveAny: (canonicalUrl) => packageAgnosticResolveCanonical(resolver, canonicalUrl, logger),
2487
2524
  resolveElementSnapshot,
2488
2525
  getAllElementKeys
2489
2526
  };
@@ -2491,9 +2528,9 @@ var registerFromManager = async (manager, conf) => {
2491
2528
  var resolveFsElementGenealogy = (genealogy, path) => {
2492
2529
  const [top, ...rest] = path;
2493
2530
  if (top === void 0) return [];
2494
- return genealogy.map((fs3) => {
2495
- if (!fs3.elements) return void 0;
2496
- let elem = fs3.elements?.[top];
2531
+ return genealogy.map((fs4) => {
2532
+ if (!fs4.elements) return void 0;
2533
+ let elem = fs4.elements?.[top];
2497
2534
  for (const k of rest) {
2498
2535
  elem = elem?.elements?.[k];
2499
2536
  }
@@ -2501,7 +2538,8 @@ var resolveFsElementGenealogy = (genealogy, path) => {
2501
2538
  }).filter((elem) => elem !== void 0);
2502
2539
  };
2503
2540
  function fsElementSnapshot(genealogy) {
2504
- const snapshot = genealogy.reverse().reduce((snapshot2, elem) => ({ ...snapshot2, ...elem }), {});
2541
+ const revGenealogy = genealogy.reverse();
2542
+ const snapshot = Object.assign({}, ...revGenealogy);
2505
2543
  snapshot.elements = void 0;
2506
2544
  return snapshot;
2507
2545
  }
@@ -2532,7 +2570,7 @@ var TypeSchemaGenerator = class {
2532
2570
  const packageNames = packageMetas.map((meta) => `${meta.name}${meta.version}`);
2533
2571
  this.logger?.step(`Loading FHIR packages: ${packageNames.join(", ")}`);
2534
2572
  await this.manager.init();
2535
- return registerFromManager(this.manager);
2573
+ return registerFromManager(this.manager, {});
2536
2574
  }
2537
2575
  generateFhirSchemas(structureDefinitions) {
2538
2576
  this.logger?.progress(`Converting ${structureDefinitions.length} StructureDefinitions to FHIRSchemas`);
@@ -2606,7 +2644,7 @@ var TypeSchemaGenerator = class {
2606
2644
  };
2607
2645
  const register = await this.registerFromPackageMetas([packageInfo]);
2608
2646
  const valueSets = await this.generateValueSetSchemas(register.allVs(), logger);
2609
- const fhirSchemas = (await Promise.all(register.allFs().map(async (fs3) => await transformFhirSchema(register, fs3, logger)))).flat();
2647
+ const fhirSchemas = (await Promise.all(register.allFs().map(async (fs4) => await transformFhirSchema(register, fs4, logger)))).flat();
2610
2648
  const allSchemas = [...fhirSchemas, ...valueSets];
2611
2649
  if (this.cache) {
2612
2650
  for (const schema of allSchemas) {
@@ -3029,6 +3067,451 @@ function deepEqual(obj1, obj2) {
3029
3067
  if (keys1.length !== keys2.length) return false;
3030
3068
  return keys1.every((key) => keys2.includes(key) && deepEqual(obj1[key], obj2[key]));
3031
3069
  }
3070
+ var FileSystemWriter = class {
3071
+ opts;
3072
+ currentDir;
3073
+ currentFileDescriptor;
3074
+ writtenFilesSet = /* @__PURE__ */ new Set();
3075
+ constructor(opts) {
3076
+ this.opts = opts;
3077
+ this.currentDir = opts.outputDir;
3078
+ }
3079
+ logger() {
3080
+ return this.opts.logger;
3081
+ }
3082
+ cd(path, gen) {
3083
+ this.currentDir = path.startsWith("/") ? Path4.join(this.opts.outputDir, path) : Path4.join(this.currentDir, path);
3084
+ if (!fs.existsSync(this.currentDir)) {
3085
+ fs.mkdirSync(this.currentDir, { recursive: true });
3086
+ }
3087
+ this.logger()?.debug(`cd '${this.currentDir}'`);
3088
+ gen();
3089
+ }
3090
+ cat(fn, gen) {
3091
+ if (this.currentFileDescriptor) throw new Error("Can't open file in file");
3092
+ if (fn.includes("/")) throw new Error(`Change file path separatly: ${fn}`);
3093
+ const fullFn = `${this.currentDir}/${fn}`;
3094
+ try {
3095
+ this.currentFileDescriptor = fs.openSync(fullFn, "w");
3096
+ this.writtenFilesSet.add(fn);
3097
+ this.logger()?.debug(`cat > '${fullFn}'`);
3098
+ gen();
3099
+ } finally {
3100
+ if (this.currentFileDescriptor) {
3101
+ fs.closeSync(this.currentFileDescriptor);
3102
+ }
3103
+ this.currentFileDescriptor = void 0;
3104
+ }
3105
+ }
3106
+ write(str) {
3107
+ if (!this.currentFileDescriptor) throw new Error("No file opened");
3108
+ fs.writeSync(this.currentFileDescriptor, str);
3109
+ }
3110
+ generate(_tsIndex) {
3111
+ throw new Error("Not implemented");
3112
+ }
3113
+ writtenFiles() {
3114
+ return Array.from(this.writtenFilesSet);
3115
+ }
3116
+ };
3117
+ var Writer = class extends FileSystemWriter {
3118
+ currentIndent = 0;
3119
+ indent() {
3120
+ this.currentIndent += this.opts.tabSize;
3121
+ }
3122
+ deindent() {
3123
+ this.currentIndent -= this.opts.tabSize;
3124
+ }
3125
+ writeIndent() {
3126
+ this.write(" ".repeat(this.currentIndent));
3127
+ }
3128
+ line(...tokens) {
3129
+ if (tokens.length === 0) {
3130
+ this.write("\n");
3131
+ } else {
3132
+ this.writeIndent();
3133
+ this.write(`${tokens.join(" ")}
3134
+ `);
3135
+ }
3136
+ }
3137
+ lineSM(...tokens) {
3138
+ this.writeIndent();
3139
+ this.write(`${tokens.join(" ")};
3140
+ `);
3141
+ }
3142
+ comment(...tokens) {
3143
+ const lines = tokens.join(" ").split("\n");
3144
+ for (const line of lines) {
3145
+ this.line(this.opts.commentLinePrefix, line);
3146
+ }
3147
+ }
3148
+ debugComment(...tokens) {
3149
+ if (this.opts.withDebugComment) {
3150
+ tokens = tokens.map((token) => {
3151
+ if (typeof token === "string") {
3152
+ return token;
3153
+ } else {
3154
+ return JSON.stringify(token, null, 2);
3155
+ }
3156
+ });
3157
+ this.comment(...tokens);
3158
+ }
3159
+ }
3160
+ disclaimer() {
3161
+ return [
3162
+ "WARNING: This file is autogenerated by @atomic-ehr/codegen.",
3163
+ "GitHub: https://github.com/orgs/atomic-ehr/repositories",
3164
+ "Any manual changes made to this file may be overwritten."
3165
+ ];
3166
+ }
3167
+ generateDisclaimer() {
3168
+ this.disclaimer().forEach((e) => this.comment(e));
3169
+ this.line();
3170
+ }
3171
+ indentBlock(gencontent) {
3172
+ this.indent();
3173
+ gencontent();
3174
+ this.deindent();
3175
+ }
3176
+ curlyBlock(tokens, gencontent, endTokens) {
3177
+ this.line(`${tokens.filter(Boolean).join(" ")} {`);
3178
+ this.indent();
3179
+ gencontent();
3180
+ this.deindent();
3181
+ this.line(`}${endTokens?.filter(Boolean).join(" ") ?? ""}`);
3182
+ }
3183
+ squareBlock(tokens, gencontent, endTokens) {
3184
+ this.line(`${tokens.filter(Boolean).join(" ")} [`);
3185
+ this.indent();
3186
+ gencontent();
3187
+ this.deindent();
3188
+ this.line(`]${endTokens?.filter(Boolean).join(" ") ?? ""}`);
3189
+ }
3190
+ };
3191
+
3192
+ // src/api/writer-generator/csharp/formatHelper.ts
3193
+ var ops = {
3194
+ "!": "Not",
3195
+ "<=": "LessOrEqual",
3196
+ ">=": "GreaterOrEqual",
3197
+ "<": "Less",
3198
+ ">": "Greater",
3199
+ "=": "Equal",
3200
+ "-": "Dash",
3201
+ "+": "Plus",
3202
+ "*": "Asterisk",
3203
+ "/": "Slash",
3204
+ "%": "Percent",
3205
+ "&": "And",
3206
+ "|": "Or",
3207
+ "^": "Xor",
3208
+ "~": "Tilde",
3209
+ "?": "Question",
3210
+ ".": "Dot"
3211
+ };
3212
+ function formatEnumDashHandle(entry) {
3213
+ return entry.split("-").map((part) => uppercaseFirstLetter(part)).join("-");
3214
+ }
3215
+ function formatEnumEntryOperation(entry) {
3216
+ let res = entry;
3217
+ for (const op in ops) res = res.replaceAll(op, ops[op] ?? "");
3218
+ return res;
3219
+ }
3220
+ function formatEnumNumber(entry) {
3221
+ const num = Number(entry[0]);
3222
+ if (Number.isInteger(num) && !Number.isNaN(num)) {
3223
+ return `_${entry}`;
3224
+ }
3225
+ return entry;
3226
+ }
3227
+ function formatEnumEntry(entry) {
3228
+ let res = formatEnumDashHandle(entry);
3229
+ res = formatEnumNumber(res);
3230
+ res = formatEnumEntryOperation(res);
3231
+ res = uppercaseFirstLetter(res);
3232
+ return res;
3233
+ }
3234
+ function formatName(input) {
3235
+ return uppercaseFirstLetter(camelCase(input.replaceAll(".", "-")));
3236
+ }
3237
+
3238
+ // src/api/writer-generator/csharp/csharp.ts
3239
+ var PRIMITIVE_TYPE_MAP = {
3240
+ boolean: "bool",
3241
+ instant: "string",
3242
+ time: "string",
3243
+ date: "string",
3244
+ dateTime: "string",
3245
+ decimal: "decimal",
3246
+ integer: "int",
3247
+ unsignedInt: "long",
3248
+ positiveInt: "long",
3249
+ integer64: "long",
3250
+ base64Binary: "string",
3251
+ uri: "string",
3252
+ url: "string",
3253
+ canonical: "string",
3254
+ oid: "string",
3255
+ uuid: "string",
3256
+ string: "string",
3257
+ code: "string",
3258
+ markdown: "string",
3259
+ id: "string",
3260
+ xhtml: "string"
3261
+ };
3262
+ var RESERVED_TYPE_NAMES = ["Reference", "Expression"];
3263
+ var getFieldModifiers = (field) => {
3264
+ return field.required ? ["required"] : [];
3265
+ };
3266
+ var formatClassName = (schema) => {
3267
+ const name = prefixReservedTypeName(getResourceName(schema.identifier));
3268
+ return uppercaseFirstLetter(name);
3269
+ };
3270
+ var formatBaseClass = (schema) => {
3271
+ return schema.base ? `: ${schema.base.name}` : "";
3272
+ };
3273
+ var canonicalToName = (canonical, dropFragment = true) => {
3274
+ if (!canonical) return void 0;
3275
+ let localName = canonical.split("/").pop();
3276
+ if (!localName) return void 0;
3277
+ if (dropFragment && localName.includes("#")) localName = localName.split("#")[0];
3278
+ if (!localName) return void 0;
3279
+ if (/^\d/.test(localName)) {
3280
+ localName = `number_${localName}`;
3281
+ }
3282
+ return formatName(localName);
3283
+ };
3284
+ var getResourceName = (id) => {
3285
+ if (id.kind === "nested") {
3286
+ const url = id.url;
3287
+ const path = canonicalToName(url, false);
3288
+ if (!path) return "";
3289
+ const [resourceName, fragment] = path.split("#");
3290
+ const name = uppercaseFirstLetterOfEach((fragment ?? "").split(".")).join("");
3291
+ return formatName([resourceName, name].join(""));
3292
+ }
3293
+ return formatName(id.name);
3294
+ };
3295
+ var isReservedTypeName = (name) => RESERVED_TYPE_NAMES.includes(name);
3296
+ var prefixReservedTypeName = (name) => isReservedTypeName(name) ? `Resource${name}` : name;
3297
+ var CSharp = class extends Writer {
3298
+ enums = {};
3299
+ staticSourceDir;
3300
+ targetNamespace;
3301
+ constructor(options) {
3302
+ super({
3303
+ outputDir: options.outputDir,
3304
+ tabSize: 4,
3305
+ withDebugComment: false,
3306
+ commentLinePrefix: "//",
3307
+ logger: options.logger
3308
+ });
3309
+ this.staticSourceDir = options.staticSourceDir;
3310
+ this.targetNamespace = options.targetNamespace;
3311
+ }
3312
+ generate(typeSchemaIndex) {
3313
+ const complexTypes = typeSchemaIndex.collectComplexTypes();
3314
+ const resources = typeSchemaIndex.collectResources();
3315
+ const packages = Array.from(new Set(resources.map((r) => formatName(r.identifier.package))));
3316
+ this.generateAllFiles(complexTypes, resources, packages);
3317
+ this.copyStaticFiles();
3318
+ }
3319
+ generateAllFiles(complexTypes, resources, packages) {
3320
+ this.generateUsingFile(packages);
3321
+ this.generateBaseTypes(complexTypes);
3322
+ this.generateResources(resources);
3323
+ this.generateEnumFiles(packages);
3324
+ this.generateResourceDictionaries(resources, packages);
3325
+ this.generateHelperFile();
3326
+ }
3327
+ generateType(schema, packageName) {
3328
+ const className = formatClassName(schema);
3329
+ const baseClass = formatBaseClass(schema);
3330
+ this.curlyBlock(["public", "class", className, baseClass], () => {
3331
+ this.generateFields(schema, packageName);
3332
+ this.generateNestedTypes(schema, packageName);
3333
+ this.line();
3334
+ this.includeHelperMethods();
3335
+ });
3336
+ this.line();
3337
+ }
3338
+ generateFields(schema, packageName) {
3339
+ if (!schema.fields) return;
3340
+ const sortedFields = Object.entries(schema.fields).sort(([a], [b]) => a.localeCompare(b));
3341
+ for (const [fieldName, field] of sortedFields) {
3342
+ this.generateField(fieldName, field, packageName);
3343
+ }
3344
+ }
3345
+ generateNestedTypes(schema, packageName) {
3346
+ if (!("nested" in schema) || !schema.nested) return;
3347
+ this.line();
3348
+ for (const subtype of schema.nested) {
3349
+ this.generateType(subtype, packageName);
3350
+ }
3351
+ }
3352
+ generateField(fieldName, field, packageName) {
3353
+ try {
3354
+ if (isChoiceDeclarationField(field)) return;
3355
+ const fieldDeclaration = this.buildFieldDeclaration(fieldName, field, packageName);
3356
+ this.line(...fieldDeclaration);
3357
+ } catch (error) {
3358
+ this.logger()?.error(`Error processing field ${fieldName}: ${error.message}`);
3359
+ }
3360
+ }
3361
+ buildFieldDeclaration(fieldName, field, packageName) {
3362
+ const fieldType = this.determineFieldType(fieldName, field, packageName);
3363
+ const modifiers = getFieldModifiers(field);
3364
+ const propertyName = pascalCase(fieldName);
3365
+ const accessors = "{ get; set; }";
3366
+ return ["public", ...modifiers, fieldType, propertyName, accessors].filter(Boolean);
3367
+ }
3368
+ determineFieldType(fieldName, field, packageName) {
3369
+ let typeName = this.getBaseTypeName(field);
3370
+ if ("enum" in field && field.enum) {
3371
+ typeName = this.registerAndGetEnumType(fieldName, field, packageName);
3372
+ }
3373
+ typeName = prefixReservedTypeName(typeName);
3374
+ const baseNamespacePrefix = "";
3375
+ const nullable = field.required ? "" : "?";
3376
+ const arraySpecifier = field.array ? "[]" : "";
3377
+ return `${baseNamespacePrefix}${typeName}${arraySpecifier}${nullable}`;
3378
+ }
3379
+ getBaseTypeName(field) {
3380
+ if ("type" in field) {
3381
+ let typeName = field.type.name.toString();
3382
+ if (field.type.kind === "nested") {
3383
+ typeName = getResourceName(field.type);
3384
+ } else if (field.type.kind === "primitive-type") typeName = PRIMITIVE_TYPE_MAP[field.type.name] ?? "string";
3385
+ return typeName;
3386
+ }
3387
+ return "";
3388
+ }
3389
+ registerAndGetEnumType(fieldName, field, packageName) {
3390
+ const enumName = formatName(field.binding?.name ?? fieldName);
3391
+ const enumTypeName = `${enumName}Enum`;
3392
+ if (!this.enums[packageName]) this.enums[packageName] = {};
3393
+ if (field.enum) this.enums[packageName][enumTypeName] = field.enum;
3394
+ return enumTypeName;
3395
+ }
3396
+ includeHelperMethods() {
3397
+ this.line("public override string ToString() => ");
3398
+ this.line(" JsonSerializer.Serialize(this, Helper.JsonSerializerOptions);");
3399
+ this.line();
3400
+ }
3401
+ generateUsingFile(packages) {
3402
+ this.cd("/", async () => {
3403
+ this.cat("Usings.cs", () => {
3404
+ this.generateDisclaimer();
3405
+ this.generateGlobalUsings(packages);
3406
+ });
3407
+ });
3408
+ }
3409
+ generateGlobalUsings(packages) {
3410
+ const globalUsings = [
3411
+ "CSharpSDK",
3412
+ "System.Text.Json",
3413
+ "System.Text.Json.Serialization",
3414
+ this.targetNamespace,
3415
+ ...packages.map((pkg) => `${this.targetNamespace}.${pkg}`)
3416
+ ];
3417
+ for (const using of globalUsings) this.lineSM("global", "using", using);
3418
+ }
3419
+ generateBaseTypes(complexTypes) {
3420
+ this.cd("/", async () => {
3421
+ this.cat("base.cs", () => {
3422
+ this.generateDisclaimer();
3423
+ this.line();
3424
+ this.lineSM("namespace", this.targetNamespace);
3425
+ for (const schema of complexTypes) {
3426
+ const packageName = formatName(schema.identifier.package);
3427
+ this.generateType(schema, packageName);
3428
+ }
3429
+ });
3430
+ });
3431
+ }
3432
+ generateResources(resources) {
3433
+ for (const schema of resources) this.generateResourceFile(schema);
3434
+ }
3435
+ generateResourceFile(schema) {
3436
+ const packageName = formatName(schema.identifier.package);
3437
+ this.cd(`/${packageName}`, async () => {
3438
+ this.cat(`${schema.identifier.name}.cs`, () => {
3439
+ this.generateDisclaimer();
3440
+ this.line();
3441
+ this.lineSM("namespace", `${this.targetNamespace}.${packageName}`);
3442
+ this.line();
3443
+ this.generateType(schema, packageName);
3444
+ });
3445
+ });
3446
+ }
3447
+ generateEnumFiles(packages) {
3448
+ for (const packageName of packages) {
3449
+ this.generatePackageEnums(packageName);
3450
+ }
3451
+ }
3452
+ generatePackageEnums(packageName) {
3453
+ const packageEnums = this.enums[packageName];
3454
+ if (!packageEnums || Object.keys(packageEnums).length === 0) return;
3455
+ this.cd(`/${packageName}`, async () => {
3456
+ this.cat(`${packageName}Enums.cs`, () => {
3457
+ this.generateDisclaimer();
3458
+ this.generateEnumFileContent(packageName, packageEnums);
3459
+ });
3460
+ });
3461
+ }
3462
+ generateEnumFileContent(packageName, enums) {
3463
+ this.lineSM("using", "System.ComponentModel");
3464
+ this.line();
3465
+ this.lineSM(`namespace ${this.targetNamespace}.${packageName}`);
3466
+ for (const [enumName, values] of Object.entries(enums)) {
3467
+ this.generateEnum(enumName, values);
3468
+ }
3469
+ }
3470
+ generateEnum(enumName, values) {
3471
+ this.curlyBlock(["public", "enum", enumName], () => {
3472
+ for (const value of values) {
3473
+ this.line(`[Description("${value}")]`);
3474
+ this.line(`${formatEnumEntry(value)},`);
3475
+ }
3476
+ });
3477
+ this.line();
3478
+ }
3479
+ generateResourceDictionaries(resources, packages) {
3480
+ this.cd("/", async () => {
3481
+ for (const packageName of packages) {
3482
+ const packageResources = resources.filter((r) => formatName(r.identifier.package) === packageName);
3483
+ if (packageResources.length === 0) return;
3484
+ this.cat(`${packageName}ResourceDictionary.cs`, () => {
3485
+ this.generateDisclaimer();
3486
+ this.line();
3487
+ this.lineSM(`namespace ${this.targetNamespace}`);
3488
+ this.generateResourceDictionaryClass(packageName, packageResources);
3489
+ });
3490
+ }
3491
+ });
3492
+ }
3493
+ generateResourceDictionaryClass(packageName, resources) {
3494
+ this.curlyBlock(["public", "static", "class", "ResourceDictionary"], () => {
3495
+ this.curlyBlock(["public static readonly Dictionary<Type, string> Map = new()"], () => {
3496
+ for (const schema of resources) {
3497
+ const typeName = schema.identifier.name;
3498
+ this.line(`{ typeof(${packageName}.${typeName}), "${typeName}" },`);
3499
+ }
3500
+ });
3501
+ this.lineSM();
3502
+ });
3503
+ }
3504
+ copyStaticFiles() {
3505
+ if (!this.staticSourceDir) return;
3506
+ const sourcePath = Path4__default.resolve(this.staticSourceDir);
3507
+ fs__default.cpSync(sourcePath, this.opts.outputDir, { recursive: true });
3508
+ }
3509
+ generateHelperFile() {
3510
+ const sourceFile = "src/api/writer-generator/csharp/Helper.cs";
3511
+ const destFile = Path4__default.join(this.opts.outputDir, "Helper.cs");
3512
+ fs__default.copyFileSync(sourceFile, destFile);
3513
+ }
3514
+ };
3032
3515
 
3033
3516
  // src/typeschema/utils.ts
3034
3517
  var groupByPackages = (typeSchemas) => {
@@ -5205,127 +5688,6 @@ ${nestedInterfaces}`;
5205
5688
  );
5206
5689
  }
5207
5690
  };
5208
- var FileSystemWriter = class {
5209
- opts;
5210
- currentDir;
5211
- currentFileDescriptor;
5212
- writtenFilesSet = /* @__PURE__ */ new Set();
5213
- constructor(opts) {
5214
- this.opts = opts;
5215
- this.currentDir = opts.outputDir;
5216
- }
5217
- logger() {
5218
- return this.opts.logger;
5219
- }
5220
- cd(path, gen) {
5221
- this.currentDir = path.startsWith("/") ? Path2.join(this.opts.outputDir, path) : Path2.join(this.currentDir, path);
5222
- if (!fs.existsSync(this.currentDir)) {
5223
- fs.mkdirSync(this.currentDir, { recursive: true });
5224
- }
5225
- this.logger()?.debug(`cd '${this.currentDir}'`);
5226
- gen();
5227
- }
5228
- cat(fn, gen) {
5229
- if (this.currentFileDescriptor) throw new Error("Can't open file in file");
5230
- if (fn.includes("/")) throw new Error(`Change file path separatly: ${fn}`);
5231
- const fullFn = `${this.currentDir}/${fn}`;
5232
- try {
5233
- this.currentFileDescriptor = fs.openSync(fullFn, "w");
5234
- this.writtenFilesSet.add(fn);
5235
- this.logger()?.debug(`cat > '${fullFn}'`);
5236
- gen();
5237
- } finally {
5238
- if (this.currentFileDescriptor) {
5239
- fs.closeSync(this.currentFileDescriptor);
5240
- }
5241
- this.currentFileDescriptor = void 0;
5242
- }
5243
- }
5244
- write(str) {
5245
- if (!this.currentFileDescriptor) throw new Error("No file opened");
5246
- fs.writeSync(this.currentFileDescriptor, str);
5247
- }
5248
- generate(_tsIndex) {
5249
- throw new Error("Not implemented");
5250
- }
5251
- writtenFiles() {
5252
- return Array.from(this.writtenFilesSet);
5253
- }
5254
- };
5255
- var Writer = class extends FileSystemWriter {
5256
- currentIndent = 0;
5257
- indent() {
5258
- this.currentIndent += this.opts.tabSize;
5259
- }
5260
- deindent() {
5261
- this.currentIndent -= this.opts.tabSize;
5262
- }
5263
- writeIndent() {
5264
- this.write(" ".repeat(this.currentIndent));
5265
- }
5266
- line(...tokens) {
5267
- if (tokens.length === 0) {
5268
- this.write("\n");
5269
- } else {
5270
- this.writeIndent();
5271
- this.write(`${tokens.join(" ")}
5272
- `);
5273
- }
5274
- }
5275
- lineSM(...tokens) {
5276
- this.writeIndent();
5277
- this.write(`${tokens.join(" ")};
5278
- `);
5279
- }
5280
- comment(...tokens) {
5281
- const lines = tokens.join(" ").split("\n");
5282
- for (const line of lines) {
5283
- this.line(this.opts.commentLinePrefix, line);
5284
- }
5285
- }
5286
- debugComment(...tokens) {
5287
- if (this.opts.withDebugComment) {
5288
- tokens = tokens.map((token) => {
5289
- if (typeof token === "string") {
5290
- return token;
5291
- } else {
5292
- return JSON.stringify(token, null, 2);
5293
- }
5294
- });
5295
- this.comment(...tokens);
5296
- }
5297
- }
5298
- disclaimer() {
5299
- return [
5300
- "WARNING: This file is autogenerated by @atomic-ehr/codegen.",
5301
- "GitHub: https://github.com/orgs/atomic-ehr/repositories",
5302
- "Any manual changes made to this file may be overwritten."
5303
- ];
5304
- }
5305
- generateDisclaimer() {
5306
- this.disclaimer().forEach((e) => this.comment(e));
5307
- this.line();
5308
- }
5309
- indentBlock(gencontent) {
5310
- this.indent();
5311
- gencontent();
5312
- this.deindent();
5313
- }
5314
- curlyBlock(tokens, gencontent, endTokens) {
5315
- this.line(`${tokens.filter(Boolean).join(" ")} {`);
5316
- this.indent();
5317
- gencontent();
5318
- this.deindent();
5319
- this.line(`}${endTokens?.filter(Boolean).join(" ") ?? ""}`);
5320
- }
5321
- squareBlock(tokens, gencontent, endTokens) {
5322
- this.line(`${tokens.filter(Boolean).join(" ")} [`);
5323
- this.indent();
5324
- gencontent();
5325
- this.deindent();
5326
- this.line(`]${endTokens?.filter(Boolean).join(" ") ?? ""}`);
5327
- }
5328
- };
5329
5691
 
5330
5692
  // src/api/writer-generator/typescript.ts
5331
5693
  var primitiveType2tsType = {
@@ -5366,7 +5728,7 @@ var tsModuleName = (id) => {
5366
5728
  var tsModuleFileName = (id) => {
5367
5729
  return `${tsModuleName(id)}.ts`;
5368
5730
  };
5369
- var canonicalToName = (canonical, dropFragment = true) => {
5731
+ var canonicalToName2 = (canonical, dropFragment = true) => {
5370
5732
  if (!canonical) return void 0;
5371
5733
  let localName = canonical.split("/").pop();
5372
5734
  if (!localName) return void 0;
@@ -5382,7 +5744,7 @@ var canonicalToName = (canonical, dropFragment = true) => {
5382
5744
  var tsResourceName = (id) => {
5383
5745
  if (id.kind === "nested") {
5384
5746
  const url = id.url;
5385
- const path = canonicalToName(url, false);
5747
+ const path = canonicalToName2(url, false);
5386
5748
  if (!path) return "";
5387
5749
  const [resourceName, fragment] = path.split("#");
5388
5750
  const name = uppercaseFirstLetterOfEach((fragment ?? "").split(".")).join("");
@@ -5436,7 +5798,7 @@ var TypeScript = class extends Writer {
5436
5798
  });
5437
5799
  } else if (isNestedIdentifier(dep)) {
5438
5800
  imports.push({
5439
- tsPackage: `../${kebabCase(dep.package)}/${pascalCase(canonicalToName(dep.url) ?? "")}`,
5801
+ tsPackage: `../${kebabCase(dep.package)}/${pascalCase(canonicalToName2(dep.url) ?? "")}`,
5440
5802
  name: tsResourceName(dep)
5441
5803
  });
5442
5804
  } else {
@@ -5481,7 +5843,7 @@ var TypeScript = class extends Writer {
5481
5843
  name = tsResourceName(schema.identifier);
5482
5844
  }
5483
5845
  let extendsClause;
5484
- if (schema.base) extendsClause = `extends ${canonicalToName(schema.base.url)}`;
5846
+ if (schema.base) extendsClause = `extends ${canonicalToName2(schema.base.url)}`;
5485
5847
  this.debugComment(schema.identifier);
5486
5848
  this.curlyBlock(["export", "interface", name, extendsClause], () => {
5487
5849
  if (isResourceTypeSchema(schema)) {
@@ -5721,7 +6083,7 @@ var writerToGenerator = (writerGen) => {
5721
6083
  const getGeneratedFiles = () => {
5722
6084
  return writerGen.writtenFiles().map((fn) => {
5723
6085
  return {
5724
- path: Path2.normalize(Path2.join(writerGen.opts.outputDir, fn)),
6086
+ path: Path4.normalize(Path4.join(writerGen.opts.outputDir, fn)),
5725
6087
  filename: fn.replace(/^.*[\\/]/, ""),
5726
6088
  content: "",
5727
6089
  exports: [],
@@ -5821,7 +6183,7 @@ var APIBuilder = class _APIBuilder {
5821
6183
  }
5822
6184
  typescript2(opts) {
5823
6185
  const writerOpts = {
5824
- outputDir: Path2.join(this.options.outputDir, "/types"),
6186
+ outputDir: Path4.join(this.options.outputDir, "/types"),
5825
6187
  tabSize: 4,
5826
6188
  withDebugComment: false,
5827
6189
  commentLinePrefix: "//"
@@ -5832,6 +6194,24 @@ var APIBuilder = class _APIBuilder {
5832
6194
  this.logger.debug(`Configured TypeScript2 generator (${JSON.stringify(effectiveOpts, void 0, 2)})`);
5833
6195
  return this;
5834
6196
  }
6197
+ csharp(namespace, staticSourceDir) {
6198
+ const generator = writerToGenerator(
6199
+ new CSharp({
6200
+ outputDir: Path4.join(this.options.outputDir, "/types"),
6201
+ staticSourceDir: staticSourceDir ?? void 0,
6202
+ targetNamespace: namespace,
6203
+ logger: new CodegenLogger({
6204
+ prefix: "C#",
6205
+ timestamp: true,
6206
+ verbose: true,
6207
+ suppressLoggingLevel: []
6208
+ })
6209
+ })
6210
+ );
6211
+ this.generators.set("C#", generator);
6212
+ this.logger.debug(`Configured C# generator`);
6213
+ return this;
6214
+ }
5835
6215
  /**
5836
6216
  * Set a progress callback for monitoring generation
5837
6217
  */
@@ -5870,22 +6250,22 @@ var APIBuilder = class _APIBuilder {
5870
6250
  return this;
5871
6251
  }
5872
6252
  static async isIdenticalTo(path, tsJSON) {
5873
- if (!await afs.exists(path)) return false;
5874
- const json = await afs.readFile(path);
6253
+ if (!await afs2.exists(path)) return false;
6254
+ const json = await afs2.readFile(path);
5875
6255
  const ts1 = JSON.parse(json.toString());
5876
6256
  const ts2 = JSON.parse(tsJSON);
5877
6257
  return deepEqual(ts1, ts2);
5878
6258
  }
5879
6259
  async writeTypeSchemasToSeparateFiles(typeSchemas, outputDir) {
5880
6260
  if (this.options.cleanOutput) fs.rmSync(outputDir, { recursive: true, force: true });
5881
- await afs.mkdir(outputDir, { recursive: true });
6261
+ await afs2.mkdir(outputDir, { recursive: true });
5882
6262
  const usedNames = {};
5883
6263
  this.logger.info(`Writing TypeSchema files to ${outputDir}...`);
5884
6264
  for (const ts of typeSchemas) {
5885
6265
  const package_name = camelCase(ts.identifier.package.replaceAll("/", "-"));
5886
6266
  const name = normalizeFileName(ts.identifier.name.toString());
5887
6267
  const json = JSON.stringify(ts, null, 2);
5888
- const baseName = Path2.join(outputDir, package_name, name);
6268
+ const baseName = Path4.join(outputDir, package_name, name);
5889
6269
  let fullName;
5890
6270
  if (usedNames[baseName] !== void 0) {
5891
6271
  usedNames[baseName]++;
@@ -5895,24 +6275,24 @@ var APIBuilder = class _APIBuilder {
5895
6275
  fullName = `${baseName}.typeschema.json`;
5896
6276
  }
5897
6277
  if (await _APIBuilder.isIdenticalTo(fullName, json)) continue;
5898
- await afs.mkdir(Path2.dirname(fullName), { recursive: true });
5899
- await afs.writeFile(fullName, json);
6278
+ await afs2.mkdir(Path4.dirname(fullName), { recursive: true });
6279
+ await afs2.writeFile(fullName, json);
5900
6280
  }
5901
6281
  }
5902
6282
  async writeTypeSchemasToSingleFile(typeSchemas, outputFile) {
5903
6283
  if (this.options.cleanOutput && fs.existsSync(outputFile)) fs.rmSync(outputFile);
5904
- await afs.mkdir(Path2.dirname(outputFile), { recursive: true });
6284
+ await afs2.mkdir(Path4.dirname(outputFile), { recursive: true });
5905
6285
  this.logger.info(`Writing TypeSchemas to one file ${outputFile}...`);
5906
6286
  for (const ts of typeSchemas) {
5907
6287
  const json = JSON.stringify(ts, null, 2);
5908
- await afs.appendFile(outputFile, json + "\n");
6288
+ await afs2.appendFile(outputFile, json + "\n");
5909
6289
  }
5910
6290
  }
5911
6291
  async tryWriteTypeSchema(typeSchemas) {
5912
6292
  if (!this.options.typeSchemaOutputDir) return;
5913
6293
  try {
5914
6294
  this.logger.info(`Starting writing TypeSchema files.`);
5915
- if (Path2.extname(this.options.typeSchemaOutputDir) === ".ndjson")
6295
+ if (Path4.extname(this.options.typeSchemaOutputDir) === ".ndjson")
5916
6296
  await this.writeTypeSchemasToSingleFile(typeSchemas, this.options.typeSchemaOutputDir);
5917
6297
  else await this.writeTypeSchemasToSeparateFiles(typeSchemas, this.options.typeSchemaOutputDir);
5918
6298
  this.logger.info(`Finished writing TypeSchema files.`);