@atomic-ehr/codegen 0.0.1-canary.20251010140912.b42d372 → 0.0.1-canary.20251014085150.f19c2e1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
2
2
  import * as FS from '@atomic-ehr/fhirschema';
3
- import { FHIRSchema, StructureDefinition } from '@atomic-ehr/fhirschema';
3
+ import { FHIRSchema, StructureDefinition, FHIRSchemaElement } from '@atomic-ehr/fhirschema';
4
4
 
5
5
  /**
6
6
  * CodeGen Logger
@@ -315,6 +315,8 @@ type Register = {
315
315
  resolveVs(canonicalUrl: CanonicalUrl): RichValueSet | undefined;
316
316
  complexTypeDict(): Record<string, RichFHIRSchema>;
317
317
  resolveAny(canonicalUrl: CanonicalUrl): any | undefined;
318
+ resolveElementSnapshot(fhirSchema: RichFHIRSchema, path: string[]): FHIRSchemaElement;
319
+ getAllElementKeys(elems: Record<string, FHIRSchemaElement>): string[];
318
320
  } & ReturnType<typeof CanonicalManager>;
319
321
 
320
322
  /**
@@ -757,6 +759,7 @@ interface APIBuilderOptions {
757
759
  typeSchemaConfig?: TypeSchemaConfig;
758
760
  logger?: CodegenLogger;
759
761
  manager?: ReturnType<typeof CanonicalManager> | null;
762
+ typeSchemaOutputDir?: string /** if .ndjson -- put in one file, else -- split into separated files*/;
760
763
  throwException?: boolean;
761
764
  }
762
765
  /**
@@ -826,6 +829,8 @@ declare class APIBuilder {
826
829
  verbose(enabled?: boolean): APIBuilder;
827
830
  throwException(enabled?: boolean): APIBuilder;
828
831
  cleanOutput(enabled?: boolean): APIBuilder;
832
+ writeTypeSchemas(target: string): this;
833
+ private tryWriteTypeSchema;
829
834
  generate(): Promise<GenerationResult>;
830
835
  /**
831
836
  * Generate and return the results without writing to files
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
- import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
2
- import * as fhirschema from '@atomic-ehr/fhirschema';
3
1
  import * as fs from 'fs';
4
2
  import { existsSync, mkdirSync } from 'fs';
3
+ import * as afs from 'fs/promises';
5
4
  import { readdir, stat, unlink, readFile, writeFile, access, mkdir, rm } from 'fs/promises';
6
5
  import * as Path2 from 'path';
7
6
  import { join, resolve, dirname, relative } from 'path';
7
+ import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
8
+ import * as fhirschema from '@atomic-ehr/fhirschema';
8
9
  import pc from 'picocolors';
9
10
 
10
11
  var __defProp = Object.defineProperty;
@@ -1445,122 +1446,6 @@ var isChoiceDeclarationField = (field) => {
1445
1446
  return field.choices !== void 0;
1446
1447
  };
1447
1448
 
1448
- // src/typeschema/register.ts
1449
- var registerFromManager = async (manager, logger) => {
1450
- const packages = await manager.packages();
1451
- const flatRawIndex = {};
1452
- const indexByPackages = [];
1453
- for (const pkg of packages) {
1454
- const resources = await manager.search({ package: pkg });
1455
- const perPackageIndex = {};
1456
- for (const resource of resources) {
1457
- const url = resource.url;
1458
- if (!url) continue;
1459
- if (perPackageIndex[url]) throw new Error(`Duplicate resource URL: ${url}`);
1460
- perPackageIndex[url] = resource;
1461
- if (flatRawIndex[url]) throw new Error(`Duplicate resource URL: ${url}`);
1462
- flatRawIndex[url] = resource;
1463
- }
1464
- indexByPackages.push({
1465
- package_meta: pkg,
1466
- index: perPackageIndex
1467
- });
1468
- }
1469
- const sdIndex = {};
1470
- const vsIndex = {};
1471
- const fsIndex = {};
1472
- const specNameToCanonical = {};
1473
- for (const resourcesByPackage of indexByPackages) {
1474
- const packageMeta = resourcesByPackage.package_meta;
1475
- for (const [surl, resource] of Object.entries(resourcesByPackage.index)) {
1476
- const url = surl;
1477
- if (resource.resourceType === "StructureDefinition") {
1478
- const sd = resource;
1479
- sdIndex[url] = sd;
1480
- const rfs = enrichFHIRSchema(fhirschema.translate(sd), packageMeta);
1481
- fsIndex[rfs.url] = rfs;
1482
- if (rfs.derivation === void 0 || rfs.derivation === "specialization") {
1483
- if (specNameToCanonical[rfs.name]) {
1484
- const info = {
1485
- old: specNameToCanonical[rfs.name],
1486
- oldDerivation: flatRawIndex[specNameToCanonical[rfs.name]].derivation,
1487
- new: rfs.url,
1488
- newDerivation: rfs.derivation
1489
- };
1490
- throw new Error(`Duplicate name ${rfs.name} ${JSON.stringify(info, void 0, 2)}`);
1491
- }
1492
- specNameToCanonical[rfs.name] = rfs.url;
1493
- }
1494
- }
1495
- if (resource.resourceType === "ValueSet") {
1496
- if (!resource.package_meta) {
1497
- resource.package_meta = packageMeta;
1498
- }
1499
- vsIndex[resource.url] = resource;
1500
- }
1501
- }
1502
- logger?.success(
1503
- `FHIR Schema conversion for '${packageMetaToFhir(packageMeta)}' completed: ${Object.keys(fsIndex).length} successful`
1504
- );
1505
- }
1506
- const complexTypes = {};
1507
- for (const fs3 of Object.values(fsIndex)) {
1508
- if (fs3.kind === "complex-type") {
1509
- complexTypes[fs3.url] = fs3;
1510
- }
1511
- }
1512
- const resolveFsGenealogy = (canonicalUrl) => {
1513
- let fs3 = fsIndex[canonicalUrl];
1514
- if (fs3 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
1515
- const genealogy = [fs3];
1516
- while (fs3?.base) {
1517
- fs3 = fsIndex[fs3.base] || fsIndex[specNameToCanonical[fs3.base]];
1518
- genealogy.push(fs3);
1519
- if (fs3 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
1520
- }
1521
- return genealogy;
1522
- };
1523
- const resolveFsSpecializations = (canonicalUrl) => {
1524
- return resolveFsGenealogy(canonicalUrl).filter((fs3) => fs3.derivation === "specialization");
1525
- };
1526
- return {
1527
- ...manager,
1528
- appendFs(fs3) {
1529
- const rfs = enrichFHIRSchema(fs3);
1530
- fsIndex[rfs.url] = rfs;
1531
- specNameToCanonical[rfs.name] = rfs.url;
1532
- },
1533
- resolveFs: (canonicalUrl) => fsIndex[canonicalUrl],
1534
- resolveFsGenealogy,
1535
- resolveFsSpecializations,
1536
- ensureSpecializationCanonicalUrl: (name) => specNameToCanonical[name] || name,
1537
- allSd: () => Object.values(sdIndex),
1538
- resolveSd: (canonicalUrl) => sdIndex[canonicalUrl],
1539
- allFs: () => Object.values(fsIndex),
1540
- allVs: () => Object.values(vsIndex),
1541
- resolveVs: (canonicalUrl) => vsIndex[canonicalUrl],
1542
- complexTypeDict: () => complexTypes,
1543
- resolveAny: (canonicalUrl) => flatRawIndex[canonicalUrl]
1544
- };
1545
- };
1546
- var resolveFsElementGenealogy = (genealogy, path) => {
1547
- const [top, ...rest] = path;
1548
- if (top === void 0) return [];
1549
- return genealogy.map((fs3) => {
1550
- if (!fs3.elements) return void 0;
1551
- let elem = fs3.elements?.[top];
1552
- for (const k of rest) {
1553
- elem = elem?.elements?.[k];
1554
- }
1555
- return elem;
1556
- }).filter((elem) => elem !== void 0);
1557
- };
1558
- function fsElementSnapshot(genealogy) {
1559
- const snapshot = genealogy.reverse().reduce((snapshot2, elem) => ({ ...snapshot2, ...elem }), {});
1560
- snapshot.elements = void 0;
1561
- return snapshot;
1562
- }
1563
-
1564
1449
  // src/typeschema/core/identifier.ts
1565
1450
  function dropVersionFromUrl(url) {
1566
1451
  const baseUrl = url.split("|")[0];
@@ -1660,12 +1545,13 @@ function collectNestedElements(fhirSchema, parentPath, elements) {
1660
1545
  }
1661
1546
  function transformNestedElements(register, fhirSchema, parentPath, elements, logger) {
1662
1547
  const fields = {};
1663
- for (const [key, element] of Object.entries(elements)) {
1548
+ for (const [key, _element] of Object.entries(elements)) {
1664
1549
  const path = [...parentPath, key];
1665
- if (isNestedElement(element)) {
1666
- fields[key] = mkNestedField(register, fhirSchema, path, element, logger);
1550
+ const elemSnapshot = register.resolveElementSnapshot(fhirSchema, path);
1551
+ if (isNestedElement(elemSnapshot)) {
1552
+ fields[key] = mkNestedField(register, fhirSchema, path, elemSnapshot, logger);
1667
1553
  } else {
1668
- fields[key] = mkField(register, fhirSchema, path, element, logger);
1554
+ fields[key] = mkField(register, fhirSchema, path, elemSnapshot, logger);
1669
1555
  }
1670
1556
  }
1671
1557
  return fields;
@@ -1758,7 +1644,7 @@ var buildReferences = (element, register, _packageInfo) => {
1758
1644
  return mkIdentifier(fs3);
1759
1645
  });
1760
1646
  };
1761
- function buildFieldType(register, fhirSchema, element, logger) {
1647
+ function buildFieldType(register, fhirSchema, path, element, logger) {
1762
1648
  if (element.elementReference) {
1763
1649
  const refPath = element.elementReference.slice(1).filter((_, i) => i % 2 === 1);
1764
1650
  return mkNestedIdentifier(register, fhirSchema, refPath, logger);
@@ -1771,10 +1657,12 @@ function buildFieldType(register, fhirSchema, element, logger) {
1771
1657
  return void 0;
1772
1658
  } else if (fhirSchema.derivation === "constraint") {
1773
1659
  return void 0;
1774
- } else
1775
- throw new Error(
1776
- `Can't recognize element type '${fhirSchema.url}' (${fhirSchema.derivation}): ${JSON.stringify(element, void 0, 2)}`
1660
+ } else {
1661
+ logger?.error(
1662
+ `Can't recognize element type '${fhirSchema.url}' (${fhirSchema.derivation}) at '${path.join(".")}': ${JSON.stringify(element, void 0, 2)}`
1777
1663
  );
1664
+ throw new Error(`Unrecognized element type`);
1665
+ }
1778
1666
  }
1779
1667
  var mkField = (register, fhirSchema, path, element, logger) => {
1780
1668
  let binding;
@@ -1786,7 +1674,7 @@ var mkField = (register, fhirSchema, path, element, logger) => {
1786
1674
  }
1787
1675
  }
1788
1676
  return {
1789
- type: buildFieldType(register, fhirSchema, element, logger),
1677
+ type: buildFieldType(register, fhirSchema, path, element, logger),
1790
1678
  required: isRequired(register, fhirSchema, path),
1791
1679
  excluded: isExcluded(register, fhirSchema, path),
1792
1680
  reference: buildReferences(element, register, fhirSchema.package_meta),
@@ -1881,7 +1769,7 @@ function buildEnum(register, element, logger) {
1881
1769
  function generateBindingSchema(register, fhirSchema, path, element, logger) {
1882
1770
  if (!element.binding?.valueSet) return void 0;
1883
1771
  const identifier = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
1884
- const fieldType = buildFieldType(register, fhirSchema, element, logger);
1772
+ const fieldType = buildFieldType(register, fhirSchema, path, element, logger);
1885
1773
  const valueSetIdentifier = mkValueSetIdentifierByUrl(register, element.binding.valueSet);
1886
1774
  const dependencies = [];
1887
1775
  if (fieldType) {
@@ -1935,26 +1823,10 @@ function collectBindingSchemas(register, fhirSchema, logger) {
1935
1823
  // src/typeschema/core/transformer.ts
1936
1824
  function mkFields(register, fhirSchema, parentPath, elements, logger) {
1937
1825
  if (!elements) return void 0;
1938
- const geneology = register.resolveFsGenealogy(fhirSchema.url);
1939
- const elems = {};
1940
- for (const [key, elem] of Object.entries(elements)) {
1941
- const path = [...parentPath, key];
1942
- const elemGeneology = resolveFsElementGenealogy(geneology, path);
1943
- const elemSnapshot = fsElementSnapshot(elemGeneology);
1944
- elems[key] = { elem, elemSnapshot, path };
1945
- }
1946
- for (const [_key, { elem, elemSnapshot, path }] of Object.entries(elems)) {
1947
- for (const choiceKey of elem?.choices || []) {
1948
- if (!elems[choiceKey]) {
1949
- const path2 = [...parentPath, choiceKey];
1950
- const elemGeneology = resolveFsElementGenealogy(geneology, path2);
1951
- const elemSnapshot2 = fsElementSnapshot(elemGeneology);
1952
- elems[choiceKey] = { elem: void 0, elemSnapshot: elemSnapshot2, path: path2 };
1953
- }
1954
- }
1955
- }
1956
1826
  const fields = {};
1957
- for (const [key, { elem, elemSnapshot, path }] of Object.entries(elems)) {
1827
+ for (const key of register.getAllElementKeys(elements)) {
1828
+ const path = [...parentPath, key];
1829
+ const elemSnapshot = register.resolveElementSnapshot(fhirSchema, path);
1958
1830
  if (isNestedElement(elemSnapshot)) {
1959
1831
  fields[key] = mkNestedField(register, fhirSchema, path, elemSnapshot, logger);
1960
1832
  } else {
@@ -2464,6 +2336,175 @@ new CodegenLogger();
2464
2336
  function createLogger(options = {}) {
2465
2337
  return new CodegenLogger(options);
2466
2338
  }
2339
+ var registerFromManager = async (manager, conf) => {
2340
+ const packageNameResolver = conf?.packageNameResolver ?? ["hl7.fhir.r5.core", "hl7.cda.uv.core"];
2341
+ const logger = conf?.logger;
2342
+ const flatRawIndex = {};
2343
+ const canonicalToPackages = {};
2344
+ const resourceTypes = /* @__PURE__ */ new Set(["StructureDefinition", "ValueSet", "CodeSystem"]);
2345
+ for (const res of await manager.search({})) {
2346
+ const rawUrl = res.url;
2347
+ if (!rawUrl) continue;
2348
+ if (!resourceTypes.has(res.resourceType)) continue;
2349
+ const url = rawUrl;
2350
+ const pkg = (await manager.resolveEntry(url)).package;
2351
+ if (!pkg) throw new Error(`Can't resolve package for ${url}`);
2352
+ if (!canonicalToPackages[url]) canonicalToPackages[url] = [];
2353
+ canonicalToPackages[url].push(pkg);
2354
+ flatRawIndex[url] = res;
2355
+ }
2356
+ const collisions = Object.entries(canonicalToPackages).filter(([_, e]) => e.length > 1).map(([url, pkgs]) => `${url}: ${pkgs.map((p) => `${p.name}@${p.version}`).join(", ")}`).join("\n");
2357
+ logger?.warn(`Duplicated canonicals: ${collisions}`);
2358
+ const packageToResources = {};
2359
+ const packageToPackageMeta = {};
2360
+ for (const [url, _pkgs] of Object.entries(canonicalToPackages)) {
2361
+ const pkg = (await manager.resolveEntry(url)).package;
2362
+ if (!pkg) throw new Error(`Can't find package for ${url}`);
2363
+ const pkgId = packageMetaToFhir(pkg);
2364
+ packageToPackageMeta[pkgId] = pkg;
2365
+ const res = await manager.resolve(url);
2366
+ if (!packageToResources[pkgId]) {
2367
+ packageToResources[pkgId] = {};
2368
+ }
2369
+ const index = packageToResources[pkgId];
2370
+ if (!index) throw new Error(`Can't find index for ${pkg.name}@${pkg.version}`);
2371
+ index[url] = res;
2372
+ }
2373
+ const indexByPackages = [];
2374
+ for (const [pkgId, index] of Object.entries(packageToResources)) {
2375
+ indexByPackages.push({
2376
+ package_meta: packageToPackageMeta[pkgId],
2377
+ index
2378
+ });
2379
+ }
2380
+ const sdIndex = {};
2381
+ const vsIndex = {};
2382
+ const fsIndex = {};
2383
+ for (const resourcesByPackage of indexByPackages) {
2384
+ const packageMeta = resourcesByPackage.package_meta;
2385
+ let counter = 0;
2386
+ logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(packageMeta)}' begins...`);
2387
+ for (const [surl, resource] of Object.entries(resourcesByPackage.index)) {
2388
+ const url = surl;
2389
+ if (resource.resourceType === "StructureDefinition") {
2390
+ const sd = resource;
2391
+ sdIndex[url] = sd;
2392
+ const rfs = enrichFHIRSchema(fhirschema.translate(sd), packageMeta);
2393
+ counter++;
2394
+ fsIndex[rfs.url] = rfs;
2395
+ }
2396
+ if (resource.resourceType === "ValueSet") {
2397
+ if (!resource.package_meta) {
2398
+ resource.package_meta = packageMeta;
2399
+ }
2400
+ vsIndex[resource.url] = resource;
2401
+ }
2402
+ }
2403
+ logger?.success(
2404
+ `FHIR Schema conversion for '${packageMetaToFhir(packageMeta)}' completed: ${counter} successful`
2405
+ );
2406
+ }
2407
+ const specNameToCanonicals = {};
2408
+ for (const rfs of Object.values(fsIndex)) {
2409
+ if (rfs.derivation === "constraint") continue;
2410
+ const name = rfs.name;
2411
+ if (!specNameToCanonicals[name]) specNameToCanonicals[name] = {};
2412
+ specNameToCanonicals[name][rfs.package_meta.name] = rfs.url;
2413
+ }
2414
+ const specNameToCanonical = {};
2415
+ for (const [sname, canonicals] of Object.entries(specNameToCanonicals)) {
2416
+ const name = sname;
2417
+ const canonicalValues = Object.values(canonicals);
2418
+ if (canonicalValues.length === 1) {
2419
+ const url = canonicalValues[0];
2420
+ specNameToCanonical[name] = url;
2421
+ } else {
2422
+ for (const pname of packageNameResolver) {
2423
+ if (canonicals[pname]) {
2424
+ specNameToCanonical[name] = canonicals[pname];
2425
+ break;
2426
+ }
2427
+ }
2428
+ if (specNameToCanonical[name] === void 0) throw new Error(`No canonical URL found for ${name}`);
2429
+ }
2430
+ }
2431
+ const complexTypes = {};
2432
+ for (const fs3 of Object.values(fsIndex)) {
2433
+ if (fs3.kind === "complex-type") {
2434
+ complexTypes[fs3.url] = fs3;
2435
+ }
2436
+ }
2437
+ const resolveFsGenealogy = (canonicalUrl) => {
2438
+ let fs3 = fsIndex[canonicalUrl];
2439
+ if (fs3 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
2440
+ const genealogy = [fs3];
2441
+ while (fs3?.base) {
2442
+ fs3 = fsIndex[fs3.base] || fsIndex[specNameToCanonical[fs3.base]];
2443
+ genealogy.push(fs3);
2444
+ if (fs3 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
2445
+ }
2446
+ return genealogy;
2447
+ };
2448
+ const resolveFsSpecializations = (canonicalUrl) => {
2449
+ return resolveFsGenealogy(canonicalUrl).filter((fs3) => fs3.derivation === "specialization");
2450
+ };
2451
+ const resolveElementSnapshot = (fhirSchema, path) => {
2452
+ const geneology = resolveFsGenealogy(fhirSchema.url);
2453
+ const elemGeneology = resolveFsElementGenealogy(geneology, path);
2454
+ const elemSnapshot = fsElementSnapshot(elemGeneology);
2455
+ return elemSnapshot;
2456
+ };
2457
+ const getAllElementKeys = (elems) => {
2458
+ const keys = /* @__PURE__ */ new Set();
2459
+ for (const [key, elem] of Object.entries(elems)) {
2460
+ keys.add(key);
2461
+ for (const choiceKey of elem?.choices || []) {
2462
+ if (!elems[choiceKey]) {
2463
+ keys.add(choiceKey);
2464
+ }
2465
+ }
2466
+ }
2467
+ return Array.from(keys);
2468
+ };
2469
+ return {
2470
+ ...manager,
2471
+ appendFs(fs3) {
2472
+ const rfs = enrichFHIRSchema(fs3);
2473
+ fsIndex[rfs.url] = rfs;
2474
+ specNameToCanonical[rfs.name] = rfs.url;
2475
+ },
2476
+ resolveFs: (canonicalUrl) => fsIndex[canonicalUrl],
2477
+ resolveFsGenealogy,
2478
+ resolveFsSpecializations,
2479
+ ensureSpecializationCanonicalUrl: (name) => specNameToCanonical[name] || name,
2480
+ allSd: () => Object.values(sdIndex),
2481
+ resolveSd: (canonicalUrl) => sdIndex[canonicalUrl],
2482
+ allFs: () => Object.values(fsIndex),
2483
+ allVs: () => Object.values(vsIndex),
2484
+ resolveVs: (canonicalUrl) => vsIndex[canonicalUrl],
2485
+ complexTypeDict: () => complexTypes,
2486
+ resolveAny: (canonicalUrl) => flatRawIndex[canonicalUrl],
2487
+ resolveElementSnapshot,
2488
+ getAllElementKeys
2489
+ };
2490
+ };
2491
+ var resolveFsElementGenealogy = (genealogy, path) => {
2492
+ const [top, ...rest] = path;
2493
+ if (top === void 0) return [];
2494
+ return genealogy.map((fs3) => {
2495
+ if (!fs3.elements) return void 0;
2496
+ let elem = fs3.elements?.[top];
2497
+ for (const k of rest) {
2498
+ elem = elem?.elements?.[k];
2499
+ }
2500
+ return elem;
2501
+ }).filter((elem) => elem !== void 0);
2502
+ };
2503
+ function fsElementSnapshot(genealogy) {
2504
+ const snapshot = genealogy.reverse().reduce((snapshot2, elem) => ({ ...snapshot2, ...elem }), {});
2505
+ snapshot.elements = void 0;
2506
+ return snapshot;
2507
+ }
2467
2508
 
2468
2509
  // src/typeschema/generator.ts
2469
2510
  var TypeSchemaGenerator = class {
@@ -2521,7 +2562,7 @@ var TypeSchemaGenerator = class {
2521
2562
  if (valueSets.length > 0) {
2522
2563
  this.logger?.debug(`${valueSets.length} ValueSets available for enum extraction`);
2523
2564
  }
2524
- const register = await registerFromManager(this.manager, logger);
2565
+ const register = await registerFromManager(this.manager, { logger: this.logger });
2525
2566
  const valueSetSchemas = [];
2526
2567
  if (valueSets.length > 0) {
2527
2568
  this.logger?.progress(`Converting ${valueSets.length} ValueSets to TypeSchema`);
@@ -2945,6 +2986,33 @@ var generateTypeSchemas = async (register, logger) => {
2945
2986
  return fhirSchemas;
2946
2987
  };
2947
2988
 
2989
+ // src/api/writer-generator/utils.ts
2990
+ var words = (s) => {
2991
+ return s.split(/(?<=[a-z])(?=[A-Z])|[-_.\s]/).filter(Boolean);
2992
+ };
2993
+ var kebabCase = (s) => {
2994
+ return words(s).map((s2) => s2.toLowerCase()).join("-");
2995
+ };
2996
+ var capitalCase = (s) => {
2997
+ if (s.length === 0) throw new Error("Empty string");
2998
+ return s[0]?.toUpperCase() + s.substring(1).toLowerCase();
2999
+ };
3000
+ var camelCase = (s) => {
3001
+ if (s.length === 0) throw new Error("Empty string");
3002
+ const [first, ...rest] = words(s);
3003
+ return [first?.toLowerCase(), ...rest.map(capitalCase)].join("");
3004
+ };
3005
+ var pascalCase = (s) => {
3006
+ return words(s).map(capitalCase).join("");
3007
+ };
3008
+ var uppercaseFirstLetter = (str) => {
3009
+ if (!str || str.length === 0) return str;
3010
+ return str.charAt(0).toUpperCase() + str.slice(1);
3011
+ };
3012
+ var uppercaseFirstLetterOfEach = (strings) => {
3013
+ return strings.map((str) => uppercaseFirstLetter(str));
3014
+ };
3015
+
2948
3016
  // src/typeschema/utils.ts
2949
3017
  var groupByPackages = (typeSchemas) => {
2950
3018
  const grouped = {};
@@ -2987,7 +3055,7 @@ var resourceRelatives = (schemas) => {
2987
3055
  }
2988
3056
  return allPairs;
2989
3057
  };
2990
- var mkTypeSchemaIndex = (schemas) => {
3058
+ var mkTypeSchemaIndex = (schemas, logger) => {
2991
3059
  const index = {};
2992
3060
  const append = (schema) => {
2993
3061
  const url = schema.identifier.url;
@@ -3008,7 +3076,7 @@ var mkTypeSchemaIndex = (schemas) => {
3008
3076
  const resourceChildren = (id) => {
3009
3077
  return relations.filter((relative2) => relative2.parent.name === id.name).map((relative2) => relative2.child);
3010
3078
  };
3011
- const hierarchy = (schema) => {
3079
+ const tryHierarchy = (schema) => {
3012
3080
  const res = [];
3013
3081
  let cur = schema;
3014
3082
  while (cur) {
@@ -3017,18 +3085,23 @@ var mkTypeSchemaIndex = (schemas) => {
3017
3085
  if (base === void 0) break;
3018
3086
  const resolved = resolve2(base);
3019
3087
  if (!resolved) {
3020
- throw new Error(
3021
- `Failed to resolve base type: ${res.map((e) => `${e.identifier.url} (${e.identifier.kind})`).join(", ")}`
3022
- );
3088
+ return void 0;
3023
3089
  }
3024
3090
  cur = resolved;
3025
3091
  }
3026
3092
  return res;
3027
3093
  };
3094
+ const hierarchy = (schema) => {
3095
+ const genealogy = tryHierarchy(schema);
3096
+ if (genealogy === void 0) {
3097
+ throw new Error(`Failed to resolve base type: ${schema.identifier.url} (${schema.identifier.kind})`);
3098
+ }
3099
+ return genealogy;
3100
+ };
3028
3101
  const findLastSpecialization = (schema) => {
3029
3102
  const nonConstraintSchema = hierarchy(schema).find((s) => s.identifier.kind !== "profile");
3030
3103
  if (!nonConstraintSchema) {
3031
- throw new Error(`No non-constraint schema found in hierarchy for ${schema.identifier.name}`);
3104
+ throw new Error(`No non-constraint schema found in hierarchy for: ${schema.identifier.name}`);
3032
3105
  }
3033
3106
  return nonConstraintSchema;
3034
3107
  };
@@ -3068,7 +3141,9 @@ var mkTypeSchemaIndex = (schemas) => {
3068
3141
  };
3069
3142
  };
3070
3143
  const isWithMetaField = (profile) => {
3071
- return hierarchy(profile).filter(isSpecializationTypeSchema).some((schema) => {
3144
+ const genealogy = tryHierarchy(profile);
3145
+ if (!genealogy) return false;
3146
+ return genealogy.filter(isSpecializationTypeSchema).some((schema) => {
3072
3147
  return schema.fields?.meta !== void 0;
3073
3148
  });
3074
3149
  };
@@ -3081,6 +3156,7 @@ var mkTypeSchemaIndex = (schemas) => {
3081
3156
  collectProfiles: () => schemas.filter(isProfileTypeSchema),
3082
3157
  resolve: resolve2,
3083
3158
  resourceChildren,
3159
+ tryHierarchy,
3084
3160
  hierarchy,
3085
3161
  findLastSpecialization,
3086
3162
  findLastSpecializationByIdentifier,
@@ -4162,8 +4238,8 @@ function toCamelCase(str) {
4162
4238
  return str.replace(/[-_\s]+(.)?/g, (_, char) => char?.toUpperCase() || "");
4163
4239
  }
4164
4240
  function toPascalCase(str) {
4165
- const camelCase = toCamelCase(str);
4166
- return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
4241
+ const camelCase2 = toCamelCase(str);
4242
+ return camelCase2.charAt(0).toUpperCase() + camelCase2.slice(1);
4167
4243
  }
4168
4244
  function toSnakeCase(str) {
4169
4245
  return str.replace(/([A-Z])/g, "_$1").replace(/[-\s]+/g, "_").toLowerCase().replace(/^_/, "");
@@ -5112,28 +5188,6 @@ ${nestedInterfaces}`;
5112
5188
  );
5113
5189
  }
5114
5190
  };
5115
-
5116
- // src/api/writer-generator/utils.ts
5117
- var words = (s) => {
5118
- return s.split(/(?<=[a-z])(?=[A-Z])|[-_.\s]/).filter(Boolean);
5119
- };
5120
- var kebabCase = (s) => {
5121
- return words(s).map((s2) => s2.toLowerCase()).join("-");
5122
- };
5123
- var capitalCase = (s) => {
5124
- if (s.length === 0) throw new Error("Empty string");
5125
- return s[0]?.toUpperCase() + s.substring(1).toLowerCase();
5126
- };
5127
- var pascalCase = (s) => {
5128
- return words(s).map(capitalCase).join("");
5129
- };
5130
- var uppercaseFirstLetter = (str) => {
5131
- if (!str || str.length === 0) return str;
5132
- return str.charAt(0).toUpperCase() + str.slice(1);
5133
- };
5134
- var uppercaseFirstLetterOfEach = (strings) => {
5135
- return strings.map((str) => uppercaseFirstLetter(str));
5136
- };
5137
5191
  var FileSystemWriter = class {
5138
5192
  opts;
5139
5193
  currentDir;
@@ -5669,6 +5723,7 @@ var writerToGenerator = (writerGen) => {
5669
5723
  build: async (_schemas) => getGeneratedFiles()
5670
5724
  };
5671
5725
  };
5726
+ var normalizeFileName = (str) => str.replace(/[^a-zA-Z0-9]/g, "");
5672
5727
  var APIBuilder = class {
5673
5728
  schemas = [];
5674
5729
  options;
@@ -5689,7 +5744,8 @@ var APIBuilder = class {
5689
5744
  cleanOutput: options.cleanOutput ?? true,
5690
5745
  typeSchemaConfig: options.typeSchemaConfig,
5691
5746
  manager: options.manager || null,
5692
- throwException: options.throwException || false
5747
+ throwException: options.throwException || false,
5748
+ typeSchemaOutputDir: options.typeSchemaOutputDir
5693
5749
  };
5694
5750
  this.typeSchemaConfig = options.typeSchemaConfig;
5695
5751
  this.logger = options.logger || createLogger({
@@ -5792,6 +5848,45 @@ var APIBuilder = class {
5792
5848
  this.options.cleanOutput = enabled;
5793
5849
  return this;
5794
5850
  }
5851
+ writeTypeSchemas(target) {
5852
+ this.options.typeSchemaOutputDir = target;
5853
+ return this;
5854
+ }
5855
+ async tryWriteTypeSchema(typeSchemas) {
5856
+ if (!this.options.typeSchemaOutputDir) return;
5857
+ try {
5858
+ if (this.options.cleanOutput) fs.rmSync(this.options.typeSchemaOutputDir, { recursive: true, force: true });
5859
+ await afs.mkdir(this.options.typeSchemaOutputDir, { recursive: true });
5860
+ let writtenCount = 0;
5861
+ let overrideCount = 0;
5862
+ const usedNames = {};
5863
+ this.logger.info(`Writing TypeSchema files to ${this.options.typeSchemaOutputDir}...`);
5864
+ for (const ts of typeSchemas) {
5865
+ const package_name = camelCase(ts.identifier.package.replaceAll("/", "-"));
5866
+ const name = normalizeFileName(ts.identifier.name.toString());
5867
+ const baseName = Path2.join(this.options.typeSchemaOutputDir, package_name, name);
5868
+ let fullName;
5869
+ if (usedNames[baseName] !== void 0) {
5870
+ usedNames[baseName]++;
5871
+ fullName = `${baseName}-${usedNames[baseName]}.typeschema.json`;
5872
+ } else {
5873
+ usedNames[baseName] = 0;
5874
+ fullName = `${baseName}.typeschema.json`;
5875
+ }
5876
+ await afs.mkdir(Path2.dirname(fullName), { recursive: true });
5877
+ afs.writeFile(fullName, JSON.stringify(ts, null, 2));
5878
+ if (await afs.exists(fullName)) overrideCount++;
5879
+ else writtenCount++;
5880
+ }
5881
+ this.logger.info(`Created ${writtenCount} new TypeSchema files, overrode ${overrideCount} files`);
5882
+ } catch (error) {
5883
+ if (this.options.throwException) throw error;
5884
+ this.logger.error(
5885
+ "Failed to write TypeSchema output",
5886
+ error instanceof Error ? error : new Error(String(error))
5887
+ );
5888
+ }
5889
+ }
5795
5890
  async generate() {
5796
5891
  const startTime = performance.now();
5797
5892
  const result = {
@@ -5821,8 +5916,9 @@ var APIBuilder = class {
5821
5916
  workingDir: "tmp/fhir"
5822
5917
  });
5823
5918
  await manager.init();
5824
- const register = await registerFromManager(manager, this.logger);
5919
+ const register = await registerFromManager(manager, { logger: this.logger });
5825
5920
  const typeSchemas = await generateTypeSchemas(register, this.logger);
5921
+ await this.tryWriteTypeSchema(typeSchemas);
5826
5922
  this.logger.debug(`Executing ${this.generators.size} generators`);
5827
5923
  await this.executeGenerators(result, typeSchemas);
5828
5924
  this.logger.info("Generation completed successfully");