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

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
  /**
@@ -828,6 +830,9 @@ declare class APIBuilder {
828
830
  throwException(enabled?: boolean): APIBuilder;
829
831
  cleanOutput(enabled?: boolean): APIBuilder;
830
832
  writeTypeSchemas(target: string): this;
833
+ private static isIdenticalTo;
834
+ private writeTypeSchemasToSeparateFiles;
835
+ private writeTypeSchemasToSingleFile;
831
836
  private tryWriteTypeSchema;
832
837
  generate(): Promise<GenerationResult>;
833
838
  /**
package/dist/index.js CHANGED
@@ -1,11 +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';
5
3
  import * as afs from 'fs/promises';
6
4
  import { readdir, stat, unlink, readFile, writeFile, access, mkdir, rm } from 'fs/promises';
7
5
  import * as Path2 from 'path';
8
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';
9
9
  import pc from 'picocolors';
10
10
 
11
11
  var __defProp = Object.defineProperty;
@@ -1446,122 +1446,6 @@ var isChoiceDeclarationField = (field) => {
1446
1446
  return field.choices !== void 0;
1447
1447
  };
1448
1448
 
1449
- // src/typeschema/register.ts
1450
- var registerFromManager = async (manager, logger) => {
1451
- const packages = await manager.packages();
1452
- const flatRawIndex = {};
1453
- const indexByPackages = [];
1454
- for (const pkg of packages) {
1455
- const resources = await manager.search({ package: pkg });
1456
- const perPackageIndex = {};
1457
- for (const resource of resources) {
1458
- const url = resource.url;
1459
- if (!url) continue;
1460
- if (perPackageIndex[url]) throw new Error(`Duplicate resource URL: ${url}`);
1461
- perPackageIndex[url] = resource;
1462
- if (flatRawIndex[url]) throw new Error(`Duplicate resource URL: ${url}`);
1463
- flatRawIndex[url] = resource;
1464
- }
1465
- indexByPackages.push({
1466
- package_meta: pkg,
1467
- index: perPackageIndex
1468
- });
1469
- }
1470
- const sdIndex = {};
1471
- const vsIndex = {};
1472
- const fsIndex = {};
1473
- const specNameToCanonical = {};
1474
- for (const resourcesByPackage of indexByPackages) {
1475
- const packageMeta = resourcesByPackage.package_meta;
1476
- for (const [surl, resource] of Object.entries(resourcesByPackage.index)) {
1477
- const url = surl;
1478
- if (resource.resourceType === "StructureDefinition") {
1479
- const sd = resource;
1480
- sdIndex[url] = sd;
1481
- const rfs = enrichFHIRSchema(fhirschema.translate(sd), packageMeta);
1482
- fsIndex[rfs.url] = rfs;
1483
- if (rfs.derivation === void 0 || rfs.derivation === "specialization") {
1484
- if (specNameToCanonical[rfs.name]) {
1485
- const info = {
1486
- old: specNameToCanonical[rfs.name],
1487
- oldDerivation: flatRawIndex[specNameToCanonical[rfs.name]].derivation,
1488
- new: rfs.url,
1489
- newDerivation: rfs.derivation
1490
- };
1491
- throw new Error(`Duplicate name ${rfs.name} ${JSON.stringify(info, void 0, 2)}`);
1492
- }
1493
- specNameToCanonical[rfs.name] = rfs.url;
1494
- }
1495
- }
1496
- if (resource.resourceType === "ValueSet") {
1497
- if (!resource.package_meta) {
1498
- resource.package_meta = packageMeta;
1499
- }
1500
- vsIndex[resource.url] = resource;
1501
- }
1502
- }
1503
- logger?.success(
1504
- `FHIR Schema conversion for '${packageMetaToFhir(packageMeta)}' completed: ${Object.keys(fsIndex).length} successful`
1505
- );
1506
- }
1507
- const complexTypes = {};
1508
- for (const fs3 of Object.values(fsIndex)) {
1509
- if (fs3.kind === "complex-type") {
1510
- complexTypes[fs3.url] = fs3;
1511
- }
1512
- }
1513
- const resolveFsGenealogy = (canonicalUrl) => {
1514
- let fs3 = fsIndex[canonicalUrl];
1515
- if (fs3 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
1516
- const genealogy = [fs3];
1517
- while (fs3?.base) {
1518
- fs3 = fsIndex[fs3.base] || fsIndex[specNameToCanonical[fs3.base]];
1519
- genealogy.push(fs3);
1520
- if (fs3 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
1521
- }
1522
- return genealogy;
1523
- };
1524
- const resolveFsSpecializations = (canonicalUrl) => {
1525
- return resolveFsGenealogy(canonicalUrl).filter((fs3) => fs3.derivation === "specialization");
1526
- };
1527
- return {
1528
- ...manager,
1529
- appendFs(fs3) {
1530
- const rfs = enrichFHIRSchema(fs3);
1531
- fsIndex[rfs.url] = rfs;
1532
- specNameToCanonical[rfs.name] = rfs.url;
1533
- },
1534
- resolveFs: (canonicalUrl) => fsIndex[canonicalUrl],
1535
- resolveFsGenealogy,
1536
- resolveFsSpecializations,
1537
- ensureSpecializationCanonicalUrl: (name) => specNameToCanonical[name] || name,
1538
- allSd: () => Object.values(sdIndex),
1539
- resolveSd: (canonicalUrl) => sdIndex[canonicalUrl],
1540
- allFs: () => Object.values(fsIndex),
1541
- allVs: () => Object.values(vsIndex),
1542
- resolveVs: (canonicalUrl) => vsIndex[canonicalUrl],
1543
- complexTypeDict: () => complexTypes,
1544
- resolveAny: (canonicalUrl) => flatRawIndex[canonicalUrl]
1545
- };
1546
- };
1547
- var resolveFsElementGenealogy = (genealogy, path) => {
1548
- const [top, ...rest] = path;
1549
- if (top === void 0) return [];
1550
- return genealogy.map((fs3) => {
1551
- if (!fs3.elements) return void 0;
1552
- let elem = fs3.elements?.[top];
1553
- for (const k of rest) {
1554
- elem = elem?.elements?.[k];
1555
- }
1556
- return elem;
1557
- }).filter((elem) => elem !== void 0);
1558
- };
1559
- function fsElementSnapshot(genealogy) {
1560
- const snapshot = genealogy.reverse().reduce((snapshot2, elem) => ({ ...snapshot2, ...elem }), {});
1561
- snapshot.elements = void 0;
1562
- return snapshot;
1563
- }
1564
-
1565
1449
  // src/typeschema/core/identifier.ts
1566
1450
  function dropVersionFromUrl(url) {
1567
1451
  const baseUrl = url.split("|")[0];
@@ -1661,12 +1545,13 @@ function collectNestedElements(fhirSchema, parentPath, elements) {
1661
1545
  }
1662
1546
  function transformNestedElements(register, fhirSchema, parentPath, elements, logger) {
1663
1547
  const fields = {};
1664
- for (const [key, element] of Object.entries(elements)) {
1548
+ for (const [key, _element] of Object.entries(elements)) {
1665
1549
  const path = [...parentPath, key];
1666
- if (isNestedElement(element)) {
1667
- 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);
1668
1553
  } else {
1669
- fields[key] = mkField(register, fhirSchema, path, element, logger);
1554
+ fields[key] = mkField(register, fhirSchema, path, elemSnapshot, logger);
1670
1555
  }
1671
1556
  }
1672
1557
  return fields;
@@ -1759,7 +1644,7 @@ var buildReferences = (element, register, _packageInfo) => {
1759
1644
  return mkIdentifier(fs3);
1760
1645
  });
1761
1646
  };
1762
- function buildFieldType(register, fhirSchema, element, logger) {
1647
+ function buildFieldType(register, fhirSchema, path, element, logger) {
1763
1648
  if (element.elementReference) {
1764
1649
  const refPath = element.elementReference.slice(1).filter((_, i) => i % 2 === 1);
1765
1650
  return mkNestedIdentifier(register, fhirSchema, refPath, logger);
@@ -1772,10 +1657,12 @@ function buildFieldType(register, fhirSchema, element, logger) {
1772
1657
  return void 0;
1773
1658
  } else if (fhirSchema.derivation === "constraint") {
1774
1659
  return void 0;
1775
- } else
1776
- throw new Error(
1777
- `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)}`
1778
1663
  );
1664
+ throw new Error(`Unrecognized element type`);
1665
+ }
1779
1666
  }
1780
1667
  var mkField = (register, fhirSchema, path, element, logger) => {
1781
1668
  let binding;
@@ -1787,7 +1674,7 @@ var mkField = (register, fhirSchema, path, element, logger) => {
1787
1674
  }
1788
1675
  }
1789
1676
  return {
1790
- type: buildFieldType(register, fhirSchema, element, logger),
1677
+ type: buildFieldType(register, fhirSchema, path, element, logger),
1791
1678
  required: isRequired(register, fhirSchema, path),
1792
1679
  excluded: isExcluded(register, fhirSchema, path),
1793
1680
  reference: buildReferences(element, register, fhirSchema.package_meta),
@@ -1882,7 +1769,7 @@ function buildEnum(register, element, logger) {
1882
1769
  function generateBindingSchema(register, fhirSchema, path, element, logger) {
1883
1770
  if (!element.binding?.valueSet) return void 0;
1884
1771
  const identifier = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
1885
- const fieldType = buildFieldType(register, fhirSchema, element, logger);
1772
+ const fieldType = buildFieldType(register, fhirSchema, path, element, logger);
1886
1773
  const valueSetIdentifier = mkValueSetIdentifierByUrl(register, element.binding.valueSet);
1887
1774
  const dependencies = [];
1888
1775
  if (fieldType) {
@@ -1936,26 +1823,10 @@ function collectBindingSchemas(register, fhirSchema, logger) {
1936
1823
  // src/typeschema/core/transformer.ts
1937
1824
  function mkFields(register, fhirSchema, parentPath, elements, logger) {
1938
1825
  if (!elements) return void 0;
1939
- const geneology = register.resolveFsGenealogy(fhirSchema.url);
1940
- const elems = {};
1941
- for (const [key, elem] of Object.entries(elements)) {
1942
- const path = [...parentPath, key];
1943
- const elemGeneology = resolveFsElementGenealogy(geneology, path);
1944
- const elemSnapshot = fsElementSnapshot(elemGeneology);
1945
- elems[key] = { elem, elemSnapshot, path };
1946
- }
1947
- for (const [_key, { elem, elemSnapshot, path }] of Object.entries(elems)) {
1948
- for (const choiceKey of elem?.choices || []) {
1949
- if (!elems[choiceKey]) {
1950
- const path2 = [...parentPath, choiceKey];
1951
- const elemGeneology = resolveFsElementGenealogy(geneology, path2);
1952
- const elemSnapshot2 = fsElementSnapshot(elemGeneology);
1953
- elems[choiceKey] = { elem: void 0, elemSnapshot: elemSnapshot2, path: path2 };
1954
- }
1955
- }
1956
- }
1957
1826
  const fields = {};
1958
- 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);
1959
1830
  if (isNestedElement(elemSnapshot)) {
1960
1831
  fields[key] = mkNestedField(register, fhirSchema, path, elemSnapshot, logger);
1961
1832
  } else {
@@ -2385,7 +2256,7 @@ var CodegenLogger = class _CodegenLogger {
2385
2256
  */
2386
2257
  error(message, error) {
2387
2258
  if (this.isSuppressed(3 /* ERROR */)) return;
2388
- console.error(this.formatMessage("", message, pc.red));
2259
+ console.error(this.formatMessage("X", message, pc.red));
2389
2260
  if (error && this.options.verbose) {
2390
2261
  console.error(pc.red(` ${error.message}`));
2391
2262
  if (error.stack) {
@@ -2465,6 +2336,175 @@ new CodegenLogger();
2465
2336
  function createLogger(options = {}) {
2466
2337
  return new CodegenLogger(options);
2467
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
+ }
2468
2508
 
2469
2509
  // src/typeschema/generator.ts
2470
2510
  var TypeSchemaGenerator = class {
@@ -2522,7 +2562,7 @@ var TypeSchemaGenerator = class {
2522
2562
  if (valueSets.length > 0) {
2523
2563
  this.logger?.debug(`${valueSets.length} ValueSets available for enum extraction`);
2524
2564
  }
2525
- const register = await registerFromManager(this.manager, logger);
2565
+ const register = await registerFromManager(this.manager, { logger: this.logger });
2526
2566
  const valueSetSchemas = [];
2527
2567
  if (valueSets.length > 0) {
2528
2568
  this.logger?.progress(`Converting ${valueSets.length} ValueSets to TypeSchema`);
@@ -2972,6 +3012,23 @@ var uppercaseFirstLetter = (str) => {
2972
3012
  var uppercaseFirstLetterOfEach = (strings) => {
2973
3013
  return strings.map((str) => uppercaseFirstLetter(str));
2974
3014
  };
3015
+ function deepEqual(obj1, obj2) {
3016
+ if (obj1 === obj2) return true;
3017
+ if (obj1 === null || obj2 === null || typeof obj1 !== "object" || typeof obj2 !== "object") {
3018
+ return false;
3019
+ }
3020
+ if (Array.isArray(obj1) && Array.isArray(obj2)) {
3021
+ if (obj1.length !== obj2.length) return false;
3022
+ return obj1.every((item, index) => deepEqual(item, obj2[index]));
3023
+ }
3024
+ if (Array.isArray(obj1) || Array.isArray(obj2)) {
3025
+ return false;
3026
+ }
3027
+ const keys1 = Object.keys(obj1);
3028
+ const keys2 = Object.keys(obj2);
3029
+ if (keys1.length !== keys2.length) return false;
3030
+ return keys1.every((key) => keys2.includes(key) && deepEqual(obj1[key], obj2[key]));
3031
+ }
2975
3032
 
2976
3033
  // src/typeschema/utils.ts
2977
3034
  var groupByPackages = (typeSchemas) => {
@@ -3015,7 +3072,7 @@ var resourceRelatives = (schemas) => {
3015
3072
  }
3016
3073
  return allPairs;
3017
3074
  };
3018
- var mkTypeSchemaIndex = (schemas) => {
3075
+ var mkTypeSchemaIndex = (schemas, logger) => {
3019
3076
  const index = {};
3020
3077
  const append = (schema) => {
3021
3078
  const url = schema.identifier.url;
@@ -3036,7 +3093,7 @@ var mkTypeSchemaIndex = (schemas) => {
3036
3093
  const resourceChildren = (id) => {
3037
3094
  return relations.filter((relative2) => relative2.parent.name === id.name).map((relative2) => relative2.child);
3038
3095
  };
3039
- const hierarchy = (schema) => {
3096
+ const tryHierarchy = (schema) => {
3040
3097
  const res = [];
3041
3098
  let cur = schema;
3042
3099
  while (cur) {
@@ -3045,18 +3102,23 @@ var mkTypeSchemaIndex = (schemas) => {
3045
3102
  if (base === void 0) break;
3046
3103
  const resolved = resolve2(base);
3047
3104
  if (!resolved) {
3048
- throw new Error(
3049
- `Failed to resolve base type: ${res.map((e) => `${e.identifier.url} (${e.identifier.kind})`).join(", ")}`
3050
- );
3105
+ return void 0;
3051
3106
  }
3052
3107
  cur = resolved;
3053
3108
  }
3054
3109
  return res;
3055
3110
  };
3111
+ const hierarchy = (schema) => {
3112
+ const genealogy = tryHierarchy(schema);
3113
+ if (genealogy === void 0) {
3114
+ throw new Error(`Failed to resolve base type: ${schema.identifier.url} (${schema.identifier.kind})`);
3115
+ }
3116
+ return genealogy;
3117
+ };
3056
3118
  const findLastSpecialization = (schema) => {
3057
3119
  const nonConstraintSchema = hierarchy(schema).find((s) => s.identifier.kind !== "profile");
3058
3120
  if (!nonConstraintSchema) {
3059
- throw new Error(`No non-constraint schema found in hierarchy for ${schema.identifier.name}`);
3121
+ throw new Error(`No non-constraint schema found in hierarchy for: ${schema.identifier.name}`);
3060
3122
  }
3061
3123
  return nonConstraintSchema;
3062
3124
  };
@@ -3096,7 +3158,9 @@ var mkTypeSchemaIndex = (schemas) => {
3096
3158
  };
3097
3159
  };
3098
3160
  const isWithMetaField = (profile) => {
3099
- return hierarchy(profile).filter(isSpecializationTypeSchema).some((schema) => {
3161
+ const genealogy = tryHierarchy(profile);
3162
+ if (!genealogy) return false;
3163
+ return genealogy.filter(isSpecializationTypeSchema).some((schema) => {
3100
3164
  return schema.fields?.meta !== void 0;
3101
3165
  });
3102
3166
  };
@@ -3109,6 +3173,7 @@ var mkTypeSchemaIndex = (schemas) => {
3109
3173
  collectProfiles: () => schemas.filter(isProfileTypeSchema),
3110
3174
  resolve: resolve2,
3111
3175
  resourceChildren,
3176
+ tryHierarchy,
3112
3177
  hierarchy,
3113
3178
  findLastSpecialization,
3114
3179
  findLastSpecializationByIdentifier,
@@ -5676,7 +5741,7 @@ var writerToGenerator = (writerGen) => {
5676
5741
  };
5677
5742
  };
5678
5743
  var normalizeFileName = (str) => str.replace(/[^a-zA-Z0-9]/g, "");
5679
- var APIBuilder = class {
5744
+ var APIBuilder = class _APIBuilder {
5680
5745
  schemas = [];
5681
5746
  options;
5682
5747
  generators = /* @__PURE__ */ new Map();
@@ -5804,33 +5869,53 @@ var APIBuilder = class {
5804
5869
  this.options.typeSchemaOutputDir = target;
5805
5870
  return this;
5806
5871
  }
5872
+ static async isIdenticalTo(path, tsJSON) {
5873
+ if (!await afs.exists(path)) return false;
5874
+ const json = await afs.readFile(path);
5875
+ const ts1 = JSON.parse(json.toString());
5876
+ const ts2 = JSON.parse(tsJSON);
5877
+ return deepEqual(ts1, ts2);
5878
+ }
5879
+ async writeTypeSchemasToSeparateFiles(typeSchemas, outputDir) {
5880
+ if (this.options.cleanOutput) fs.rmSync(outputDir, { recursive: true, force: true });
5881
+ await afs.mkdir(outputDir, { recursive: true });
5882
+ const usedNames = {};
5883
+ this.logger.info(`Writing TypeSchema files to ${outputDir}...`);
5884
+ for (const ts of typeSchemas) {
5885
+ const package_name = camelCase(ts.identifier.package.replaceAll("/", "-"));
5886
+ const name = normalizeFileName(ts.identifier.name.toString());
5887
+ const json = JSON.stringify(ts, null, 2);
5888
+ const baseName = Path2.join(outputDir, package_name, name);
5889
+ let fullName;
5890
+ if (usedNames[baseName] !== void 0) {
5891
+ usedNames[baseName]++;
5892
+ fullName = `${baseName}-${usedNames[baseName]}.typeschema.json`;
5893
+ } else {
5894
+ usedNames[baseName] = 0;
5895
+ fullName = `${baseName}.typeschema.json`;
5896
+ }
5897
+ if (await _APIBuilder.isIdenticalTo(fullName, json)) continue;
5898
+ await afs.mkdir(Path2.dirname(fullName), { recursive: true });
5899
+ await afs.writeFile(fullName, json);
5900
+ }
5901
+ }
5902
+ async writeTypeSchemasToSingleFile(typeSchemas, outputFile) {
5903
+ if (this.options.cleanOutput && fs.existsSync(outputFile)) fs.rmSync(outputFile);
5904
+ await afs.mkdir(Path2.dirname(outputFile), { recursive: true });
5905
+ this.logger.info(`Writing TypeSchemas to one file ${outputFile}...`);
5906
+ for (const ts of typeSchemas) {
5907
+ const json = JSON.stringify(ts, null, 2);
5908
+ await afs.appendFile(outputFile, json + "\n");
5909
+ }
5910
+ }
5807
5911
  async tryWriteTypeSchema(typeSchemas) {
5808
5912
  if (!this.options.typeSchemaOutputDir) return;
5809
5913
  try {
5810
- if (this.options.cleanOutput) fs.rmSync(this.options.typeSchemaOutputDir, { recursive: true, force: true });
5811
- await afs.mkdir(this.options.typeSchemaOutputDir, { recursive: true });
5812
- let writtenCount = 0;
5813
- let overrideCount = 0;
5814
- const usedNames = {};
5815
- this.logger.info(`Writing TypeSchema files to ${this.options.typeSchemaOutputDir}...`);
5816
- for (const ts of typeSchemas) {
5817
- const package_name = camelCase(ts.identifier.package.replaceAll("/", "-"));
5818
- const name = normalizeFileName(ts.identifier.name.toString());
5819
- const baseName = Path2.join(this.options.typeSchemaOutputDir, package_name, name);
5820
- let fullName;
5821
- if (usedNames[baseName] !== void 0) {
5822
- usedNames[baseName]++;
5823
- fullName = `${baseName}-${usedNames[baseName]}.typeschema.json`;
5824
- } else {
5825
- usedNames[baseName] = 0;
5826
- fullName = `${baseName}.typeschema.json`;
5827
- }
5828
- await afs.mkdir(Path2.dirname(fullName), { recursive: true });
5829
- afs.writeFile(fullName, JSON.stringify(ts, null, 2));
5830
- if (await afs.exists(fullName)) overrideCount++;
5831
- else writtenCount++;
5832
- }
5833
- this.logger.info(`Created ${writtenCount} new TypeSchema files, overrode ${overrideCount} files`);
5914
+ this.logger.info(`Starting writing TypeSchema files.`);
5915
+ if (Path2.extname(this.options.typeSchemaOutputDir) === ".ndjson")
5916
+ await this.writeTypeSchemasToSingleFile(typeSchemas, this.options.typeSchemaOutputDir);
5917
+ else await this.writeTypeSchemasToSeparateFiles(typeSchemas, this.options.typeSchemaOutputDir);
5918
+ this.logger.info(`Finished writing TypeSchema files.`);
5834
5919
  } catch (error) {
5835
5920
  if (this.options.throwException) throw error;
5836
5921
  this.logger.error(
@@ -5868,7 +5953,7 @@ var APIBuilder = class {
5868
5953
  workingDir: "tmp/fhir"
5869
5954
  });
5870
5955
  await manager.init();
5871
- const register = await registerFromManager(manager, this.logger);
5956
+ const register = await registerFromManager(manager, { logger: this.logger });
5872
5957
  const typeSchemas = await generateTypeSchemas(register, this.logger);
5873
5958
  await this.tryWriteTypeSchema(typeSchemas);
5874
5959
  this.logger.debug(`Executing ${this.generators.size} generators`);