@atomic-ehr/codegen 0.0.1-canary.20251106144955.d906520 → 0.0.1-canary.20251106152808.f4530b9

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,6 +1,6 @@
1
1
  import * as fs from 'fs';
2
2
  import fs__default, { existsSync, mkdirSync } from 'fs';
3
- import * as afs2 from 'fs/promises';
3
+ import * as afs3 from 'fs/promises';
4
4
  import { readdir, stat, unlink, readFile, writeFile, access, mkdir, rm } from 'fs/promises';
5
5
  import * as Path4 from 'path';
6
6
  import Path4__default, { join, resolve, dirname, relative } from 'path';
@@ -8,6 +8,7 @@ import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
8
8
  import * as fhirschema from '@atomic-ehr/fhirschema';
9
9
  import { isStructureDefinition } from '@atomic-ehr/fhirschema';
10
10
  import pc from 'picocolors';
11
+ import * as YAML from 'yaml';
11
12
 
12
13
  var __defProp = Object.defineProperty;
13
14
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -1397,7 +1398,24 @@ var init_IndexBuilder = __esm({
1397
1398
  });
1398
1399
 
1399
1400
  // src/typeschema/types.ts
1401
+ var extractNameFromCanonical = (canonical, dropFragment = true) => {
1402
+ let localName = canonical.split("/").pop();
1403
+ if (!localName) return void 0;
1404
+ if (dropFragment && localName.includes("#")) {
1405
+ localName = localName.split("#")[0];
1406
+ }
1407
+ if (!localName) return void 0;
1408
+ if (/^\d/.test(localName)) {
1409
+ localName = `number_${localName}`;
1410
+ }
1411
+ return localName;
1412
+ };
1400
1413
  var packageMetaToFhir = (packageMeta) => `${packageMeta.name}#${packageMeta.version}`;
1414
+ var npmToPackageMeta = (fhir) => {
1415
+ const [name, version] = fhir.split("@");
1416
+ if (!name) throw new Error(`Invalid FHIR package meta: ${fhir}`);
1417
+ return { name, version: version ?? "latest" };
1418
+ };
1401
1419
  var packageMetaToNpm = (packageMeta) => `${packageMeta.name}@${packageMeta.version}`;
1402
1420
  var enrichFHIRSchema = (schema, packageMeta) => {
1403
1421
  if (!packageMeta) {
@@ -1447,10 +1465,10 @@ var isChoiceDeclarationField = (field) => {
1447
1465
  return field.choices !== void 0;
1448
1466
  };
1449
1467
  var isValueSet = (res) => {
1450
- return res.resourceType === "ValueSet";
1468
+ return res?.resourceType === "ValueSet";
1451
1469
  };
1452
1470
  var isCodeSystem = (res) => {
1453
- return res.resourceType === "CodeSystem";
1471
+ return res?.resourceType === "CodeSystem";
1454
1472
  };
1455
1473
  var enrichValueSet = (vs, packageMeta) => {
1456
1474
  if (!vs.url) throw new Error("ValueSet must have a URL");
@@ -1587,12 +1605,15 @@ function mkNestedTypes(register, fhirSchema, logger) {
1587
1605
  } else {
1588
1606
  baseName = element.type;
1589
1607
  }
1608
+ const baseUrl = register.ensureSpecializationCanonicalUrl(baseName);
1609
+ const baseFs = register.resolveFs(fhirSchema.package_meta, baseUrl);
1610
+ if (!baseFs) throw new Error(`Could not resolve base type ${baseName}`);
1590
1611
  const base = {
1591
1612
  kind: "complex-type",
1592
- package: fhirSchema.package_meta.name,
1593
- version: fhirSchema.package_meta.version,
1613
+ package: baseFs.package_meta.name,
1614
+ version: baseFs.package_meta.version,
1594
1615
  name: baseName,
1595
- url: register.ensureSpecializationCanonicalUrl(fhirSchema.package_meta, baseName)
1616
+ url: baseUrl
1596
1617
  };
1597
1618
  const fields = transformNestedElements(register, fhirSchema, path, element.elements, logger);
1598
1619
  const nestedType = {
@@ -1656,8 +1677,9 @@ function isExcluded(register, fhirSchema, path) {
1656
1677
  var buildReferences = (register, fhirSchema, element) => {
1657
1678
  if (!element.refers) return void 0;
1658
1679
  return element.refers.map((ref) => {
1659
- const curl = register.ensureSpecializationCanonicalUrl(fhirSchema.package_meta, ref);
1680
+ const curl = register.ensureSpecializationCanonicalUrl(ref);
1660
1681
  const fs4 = register.resolveFs(fhirSchema.package_meta, curl);
1682
+ if (!fs4) throw new Error(`Failed to resolve fs for ${curl}`);
1661
1683
  return mkIdentifier(fs4);
1662
1684
  });
1663
1685
  };
@@ -1666,9 +1688,12 @@ function buildFieldType(register, fhirSchema, path, element, logger) {
1666
1688
  const refPath = element.elementReference.slice(1).filter((_, i) => i % 2 === 1);
1667
1689
  return mkNestedIdentifier(register, fhirSchema, refPath, logger);
1668
1690
  } else if (element.type) {
1669
- const url = register.ensureSpecializationCanonicalUrl(fhirSchema.package_meta, element.type);
1691
+ const url = register.ensureSpecializationCanonicalUrl(element.type);
1670
1692
  const fieldFs = register.resolveFs(fhirSchema.package_meta, url);
1671
- if (!fieldFs) throw new Error(`Could not resolve field type: '${element.type}'`);
1693
+ if (!fieldFs)
1694
+ throw new Error(
1695
+ `Could not resolve field type: '${element.type}' (from '${fhirSchema.url}' in '${packageMetaToFhir(fhirSchema.package_meta)}')`
1696
+ );
1672
1697
  return mkIdentifier(fieldFs);
1673
1698
  } else if (element.choices) {
1674
1699
  return void 0;
@@ -1711,8 +1736,9 @@ function isNestedElement(element) {
1711
1736
  return isBackbone || isElement || elementsWithoutType;
1712
1737
  }
1713
1738
  function mkNestedField(register, fhirSchema, path, element, logger) {
1739
+ const nestedIdentifier = mkNestedIdentifier(register, fhirSchema, path, logger);
1714
1740
  return {
1715
- type: mkNestedIdentifier(register, fhirSchema, path, logger),
1741
+ type: nestedIdentifier,
1716
1742
  array: element.array || false,
1717
1743
  required: isRequired(register, fhirSchema, path),
1718
1744
  excluded: isExcluded(register, fhirSchema, path)
@@ -1868,19 +1894,6 @@ function extractFieldDependencies(fields) {
1868
1894
  }
1869
1895
  return deps;
1870
1896
  }
1871
- function deduplicateDependencies(deps) {
1872
- const seen = /* @__PURE__ */ new Set();
1873
- const unique = [];
1874
- for (const dep of deps) {
1875
- const key = dep.url;
1876
- if (!seen.has(key)) {
1877
- seen.add(key);
1878
- unique.push(dep);
1879
- }
1880
- }
1881
- unique.sort((a, b) => a.name.localeCompare(b.name));
1882
- return unique;
1883
- }
1884
1897
  function isExtensionSchema(fhirSchema, _identifier) {
1885
1898
  if (fhirSchema.base === "Extension" || fhirSchema.base === "http://hl7.org/fhir/StructureDefinition/Extension") {
1886
1899
  return true;
@@ -1907,62 +1920,6 @@ async function transformValueSet(register, valueSet, logger) {
1907
1920
  compose: !concept ? valueSet.compose : void 0
1908
1921
  };
1909
1922
  }
1910
- async function transformExtension(fhirSchema, register, logger) {
1911
- try {
1912
- const identifier = mkIdentifier(fhirSchema);
1913
- let base;
1914
- if (fhirSchema.base && fhirSchema.base !== "Extension") {
1915
- const baseUrl = fhirSchema.base.includes("/") ? fhirSchema.base : `http://hl7.org/fhir/StructureDefinition/${fhirSchema.base}`;
1916
- const baseName = fhirSchema.base.split("/").pop() || fhirSchema.base;
1917
- base = {
1918
- kind: "complex-type",
1919
- package: "hl7.fhir.r4.core",
1920
- version: "4.0.1",
1921
- name: baseName,
1922
- url: baseUrl
1923
- };
1924
- } else {
1925
- base = {
1926
- kind: "complex-type",
1927
- package: "hl7.fhir.r4.core",
1928
- version: "4.0.1",
1929
- name: "Extension",
1930
- url: "http://hl7.org/fhir/StructureDefinition/Extension"
1931
- };
1932
- }
1933
- const extensionSchema = {
1934
- identifier,
1935
- base,
1936
- description: fhirSchema.description,
1937
- dependencies: [],
1938
- metadata: {
1939
- isExtension: true
1940
- // Mark as extension for file organization
1941
- }
1942
- };
1943
- if (base) {
1944
- extensionSchema.dependencies.push(base);
1945
- }
1946
- if (fhirSchema.elements) {
1947
- const fields = mkFields(register, fhirSchema, [], fhirSchema.elements, logger);
1948
- if (fields && Object.keys(fields).length > 0) {
1949
- extensionSchema.fields = fields;
1950
- extensionSchema.dependencies.push(...extractFieldDependencies(fields));
1951
- }
1952
- }
1953
- const nestedTypes = mkNestedTypes(register, fhirSchema, logger);
1954
- if (nestedTypes && nestedTypes.length > 0) {
1955
- extensionSchema.nested = nestedTypes;
1956
- extensionSchema.dependencies.push(...extractNestedDependencies(nestedTypes));
1957
- }
1958
- extensionSchema.dependencies = deduplicateDependencies(extensionSchema.dependencies);
1959
- extensionSchema.dependencies = extensionSchema.dependencies.filter((dep) => dep.url !== identifier.url);
1960
- return extensionSchema;
1961
- } catch (error) {
1962
- console.warn(`Failed to transform extension ${fhirSchema.name}: ${error}`);
1963
- return null;
1964
- }
1965
- }
1966
1923
  function extractDependencies(identifier, base, fields, nestedTypes) {
1967
1924
  const deps = [];
1968
1925
  if (base) deps.push(base);
@@ -1987,10 +1944,12 @@ function transformFhirSchemaResource(register, fhirSchema, logger) {
1987
1944
  if (fhirSchema.base && fhirSchema.type !== "Element") {
1988
1945
  const baseFs = register.resolveFs(
1989
1946
  fhirSchema.package_meta,
1990
- register.ensureSpecializationCanonicalUrl(fhirSchema.package_meta, fhirSchema.base)
1947
+ register.ensureSpecializationCanonicalUrl(fhirSchema.base)
1991
1948
  );
1992
1949
  if (!baseFs) {
1993
- throw new Error(`Base resource not found '${fhirSchema.base}' for '${fhirSchema.url}'`);
1950
+ throw new Error(
1951
+ `Base resource not found '${fhirSchema.base}' for <${fhirSchema.url}> from ${packageMetaToFhir(fhirSchema.package_meta)}`
1952
+ );
1994
1953
  }
1995
1954
  base = mkIdentifier(baseFs);
1996
1955
  }
@@ -2009,16 +1968,16 @@ function transformFhirSchemaResource(register, fhirSchema, logger) {
2009
1968
  return [typeSchema, ...bindingSchemas];
2010
1969
  }
2011
1970
  async function transformFhirSchema(register, fhirSchema, logger) {
2012
- const results = [];
2013
- mkIdentifier(fhirSchema);
2014
- if (isExtensionSchema(fhirSchema)) {
2015
- const extensionSchema = await transformExtension(fhirSchema, register, logger);
2016
- if (extensionSchema) {
2017
- results.push(extensionSchema);
2018
- }
2019
- return results;
1971
+ const schemas = transformFhirSchemaResource(register, fhirSchema, logger);
1972
+ if (isExtensionSchema(fhirSchema, mkIdentifier(fhirSchema))) {
1973
+ const schema = schemas[0];
1974
+ if (!schema) throw new Error(`Expected schema to be defined`);
1975
+ schema.metadata = {
1976
+ isExtension: true
1977
+ // Mark as extension for file organization
1978
+ };
2020
1979
  }
2021
- return transformFhirSchemaResource(register, fhirSchema, logger);
1980
+ return schemas;
2022
1981
  }
2023
1982
  var TypeSchemaCache = class {
2024
1983
  cache = /* @__PURE__ */ new Map();
@@ -2362,7 +2321,7 @@ function createLogger(options = {}) {
2362
2321
  }
2363
2322
  var readPackageJSON = async (workDir, packageMeta) => {
2364
2323
  const packageJSONFileName = Path4.join(workDir, "node_modules", packageMeta.name, "package.json");
2365
- const packageJSON = JSON.parse(await afs2.readFile(packageJSONFileName, "utf8"));
2324
+ const packageJSON = JSON.parse(await afs3.readFile(packageJSONFileName, "utf8"));
2366
2325
  return packageJSON;
2367
2326
  };
2368
2327
  var readPackageDependencies = async (workDir, packageMeta) => {
@@ -2376,69 +2335,50 @@ var readPackageDependencies = async (workDir, packageMeta) => {
2376
2335
  return [];
2377
2336
  };
2378
2337
  var mkEmptyPkgIndex = (pkg) => {
2379
- return { pkg, nameResolution: {}, canonicalResolution: {}, fhirSchemas: {}, valueSets: {} };
2338
+ return {
2339
+ pkg,
2340
+ canonicalResolution: {},
2341
+ fhirSchemas: {},
2342
+ valueSets: {}
2343
+ };
2380
2344
  };
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
- }
2345
+ var mkPackageAwareResolver = async (manager, pkg, deep, acc, logger) => {
2388
2346
  const pkgId = packageMetaToFhir(pkg);
2389
- if (!options[pkgId]) options[pkgId] = mkEmptyPkgIndex(pkg);
2347
+ logger?.info(`${" ".repeat(deep * 2)}+ ${pkgId}`);
2348
+ if (acc[pkgId]) return acc[pkgId];
2349
+ const index = mkEmptyPkgIndex(pkg);
2390
2350
  for (const resource of await manager.search({ package: pkg })) {
2391
2351
  const rawUrl = resource.url;
2392
2352
  if (!rawUrl) continue;
2393
2353
  if (!(isStructureDefinition(resource) || isValueSet(resource) || isCodeSystem(resource))) continue;
2394
2354
  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
- });
2406
- }
2407
- }
2408
- }
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}`);
2417
- }
2355
+ if (index.canonicalResolution[url]) logger?.dry_warn(`Duplicate canonical URL: ${url} at ${pkgId}.`);
2356
+ index.canonicalResolution[url] = [{ deep, pkg, pkgId, resource }];
2357
+ }
2358
+ const deps = await readPackageDependencies("tmp/fhir", pkg);
2359
+ for (const depPkg of deps) {
2360
+ const { canonicalResolution } = await mkPackageAwareResolver(manager, depPkg, deep + 1, acc, logger);
2361
+ for (const [surl, resolutions] of Object.entries(canonicalResolution)) {
2362
+ const url = surl;
2363
+ index.canonicalResolution[url] = [...index.canonicalResolution[url] || [], ...resolutions];
2418
2364
  }
2419
2365
  }
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;
2366
+ for (const resolutionOptions of Object.values(index.canonicalResolution)) {
2367
+ resolutionOptions.sort((a, b) => a.deep - b.deep);
2368
+ }
2369
+ acc[pkgId] = index;
2370
+ return index;
2427
2371
  };
2428
- var packageAgnosticResolveCanonical = (resolver, url, logger) => {
2372
+ var packageAgnosticResolveCanonical = (resolver, url, _logger) => {
2429
2373
  const options = Object.values(resolver).flatMap((pkg) => pkg.canonicalResolution[url]);
2430
2374
  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
2375
  return options[0]?.resource;
2436
2376
  };
2437
- var registerFromManager = async (manager, { logger, fallbackPackageForNameResolution }) => {
2438
- const packages = await manager.packages();
2377
+ var registerFromManager = async (manager, { logger, fallbackPackageForNameResolution, focusedPackages }) => {
2378
+ const packages = focusedPackages ?? await manager.packages();
2439
2379
  const resolver = {};
2440
2380
  for (const pkg of packages) {
2441
- Object.assign(resolver, await mkPackageAwareResolver(manager, pkg));
2381
+ await mkPackageAwareResolver(manager, pkg, 0, resolver, logger);
2442
2382
  }
2443
2383
  for (const { pkg, canonicalResolution } of Object.values(resolver)) {
2444
2384
  const pkgId = packageMetaToFhir(pkg);
@@ -2446,19 +2386,21 @@ var registerFromManager = async (manager, { logger, fallbackPackageForNameResolu
2446
2386
  let counter = 0;
2447
2387
  logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' begins...`);
2448
2388
  for (const [_url, options] of Object.entries(canonicalResolution)) {
2449
- const resource = options[0]?.resource;
2450
- if (!resource) throw new Error(`Resource not found`);
2389
+ const resolition = options[0];
2390
+ if (!resolition) throw new Error(`Resource not found`);
2391
+ const resource = resolition.resource;
2392
+ const resourcePkg = resolition.pkg;
2451
2393
  if (isStructureDefinition(resource)) {
2452
- const rfs = enrichFHIRSchema(fhirschema.translate(resource), pkg);
2394
+ const rfs = enrichFHIRSchema(fhirschema.translate(resource), resourcePkg);
2453
2395
  counter++;
2454
2396
  resolver[pkgId].fhirSchemas[rfs.url] = rfs;
2455
2397
  }
2456
2398
  if (isValueSet(resource)) {
2457
- const rvs = enrichValueSet(resource, pkg);
2399
+ const rvs = enrichValueSet(resource, resourcePkg);
2458
2400
  resolver[pkgId].valueSets[rvs.url] = rvs;
2459
2401
  }
2460
2402
  }
2461
- logger?.success(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' completed: ${counter} successful`);
2403
+ logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' completed: ${counter} successful`);
2462
2404
  }
2463
2405
  const resolveFs = (pkg, canonicalUrl) => {
2464
2406
  return resolver[packageMetaToFhir(pkg)]?.fhirSchemas[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.fhirSchemas[canonicalUrl];
@@ -2466,14 +2408,19 @@ var registerFromManager = async (manager, { logger, fallbackPackageForNameResolu
2466
2408
  const resolveVs = (pkg, canonicalUrl) => {
2467
2409
  return resolver[packageMetaToFhir(pkg)]?.valueSets[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.valueSets[canonicalUrl];
2468
2410
  };
2469
- const ensureSpecializationCanonicalUrl = (pkg, name) => packageAwareResolveName(resolver, pkg, name) || fallbackPackageForNameResolution && packageAwareResolveName(resolver, fallbackPackageForNameResolution, name) || name;
2411
+ const ensureSpecializationCanonicalUrl = (name) => name.match(/^[a-zA-Z0-9]+$/) && `http://hl7.org/fhir/StructureDefinition/${name}` || name;
2470
2412
  const resolveFsGenealogy = (pkg, canonicalUrl) => {
2471
2413
  let fs4 = resolveFs(pkg, canonicalUrl);
2472
- if (fs4 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
2414
+ if (fs4 === void 0) throw new Error(`Failed to resolve FHIR Schema: '${canonicalUrl}'`);
2473
2415
  const genealogy = [fs4];
2474
2416
  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}'`);
2417
+ const pkg2 = fs4.package_meta;
2418
+ const baseUrl = ensureSpecializationCanonicalUrl(fs4.base);
2419
+ fs4 = resolveFs(pkg2, baseUrl);
2420
+ if (fs4 === void 0)
2421
+ throw new Error(
2422
+ `Failed to resolve FHIR Schema base for '${canonicalUrl}'. Problem: '${baseUrl}' from '${packageMetaToFhir(pkg2)}'`
2423
+ );
2477
2424
  genealogy.push(fs4);
2478
2425
  }
2479
2426
  return genealogy;
@@ -2501,11 +2448,10 @@ var registerFromManager = async (manager, { logger, fallbackPackageForNameResolu
2501
2448
  };
2502
2449
  return {
2503
2450
  ...manager,
2504
- unsafeAppendFs(fs4) {
2451
+ testAppendFs(fs4) {
2505
2452
  const rfs = enrichFHIRSchema(fs4);
2506
2453
  const pkgId = packageMetaToFhir(rfs.package_meta);
2507
2454
  if (!resolver[pkgId]) resolver[pkgId] = mkEmptyPkgIndex(rfs.package_meta);
2508
- resolver[pkgId].nameResolution[rfs.name] = rfs.url;
2509
2455
  resolver[pkgId].fhirSchemas[rfs.url] = rfs;
2510
2456
  },
2511
2457
  resolveFs,
@@ -2513,16 +2459,17 @@ var registerFromManager = async (manager, { logger, fallbackPackageForNameResolu
2513
2459
  resolveFsSpecializations,
2514
2460
  ensureSpecializationCanonicalUrl,
2515
2461
  resolveSd: (_pkg, canonicalUrl) => {
2516
- const res = packageAgnosticResolveCanonical(resolver, canonicalUrl, logger);
2462
+ const res = packageAgnosticResolveCanonical(resolver, canonicalUrl);
2517
2463
  if (isStructureDefinition(res)) return res;
2518
2464
  return void 0;
2519
2465
  },
2520
2466
  allFs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.fhirSchemas)),
2521
2467
  allVs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.valueSets)),
2522
2468
  resolveVs,
2523
- resolveAny: (canonicalUrl) => packageAgnosticResolveCanonical(resolver, canonicalUrl, logger),
2469
+ resolveAny: (canonicalUrl) => packageAgnosticResolveCanonical(resolver, canonicalUrl),
2524
2470
  resolveElementSnapshot,
2525
- getAllElementKeys
2471
+ getAllElementKeys,
2472
+ resolver
2526
2473
  };
2527
2474
  };
2528
2475
  var resolveFsElementGenealogy = (genealogy, path) => {
@@ -2567,10 +2514,10 @@ var TypeSchemaGenerator = class {
2567
2514
  }
2568
2515
  }
2569
2516
  async registerFromPackageMetas(packageMetas) {
2570
- const packageNames = packageMetas.map((meta) => `${meta.name}${meta.version}`);
2517
+ const packageNames = packageMetas.map(packageMetaToFhir);
2571
2518
  this.logger?.step(`Loading FHIR packages: ${packageNames.join(", ")}`);
2572
2519
  await this.manager.init();
2573
- return registerFromManager(this.manager, {});
2520
+ return registerFromManager(this.manager, { focusedPackages: packageMetas });
2574
2521
  }
2575
2522
  generateFhirSchemas(structureDefinitions) {
2576
2523
  this.logger?.progress(`Converting ${structureDefinitions.length} StructureDefinitions to FHIRSchemas`);
@@ -3013,9 +2960,29 @@ var TypeSchemaParser = class {
3013
2960
  };
3014
2961
 
3015
2962
  // src/typeschema/index.ts
2963
+ var codeableReferenceInR4 = "Use CodeableReference which is not provided by FHIR R4.";
2964
+ var availabilityInR4 = "Use Availability which is not provided by FHIR R4.";
2965
+ var skipMe = {
2966
+ "hl7.fhir.uv.extensions.r4#1.0.0": {
2967
+ "http://hl7.org/fhir/StructureDefinition/extended-contact-availability": availabilityInR4,
2968
+ "http://hl7.org/fhir/StructureDefinition/immunization-procedure": codeableReferenceInR4,
2969
+ "http://hl7.org/fhir/StructureDefinition/specimen-additive": codeableReferenceInR4,
2970
+ "http://hl7.org/fhir/StructureDefinition/workflow-barrier": codeableReferenceInR4,
2971
+ "http://hl7.org/fhir/StructureDefinition/workflow-protectiveFactor": codeableReferenceInR4,
2972
+ "http://hl7.org/fhir/StructureDefinition/workflow-reason": codeableReferenceInR4
2973
+ },
2974
+ "hl7.fhir.r5.core#5.0.0": {
2975
+ "http://hl7.org/fhir/StructureDefinition/shareablecodesystem": "FIXME: CodeSystem.concept.concept defined by ElementReference. FHIR Schema generator output broken value in it, so we just skip it for now."
2976
+ }
2977
+ };
3016
2978
  var generateTypeSchemas = async (register, logger) => {
3017
2979
  const fhirSchemas = [];
3018
2980
  for (const fhirSchema of register.allFs()) {
2981
+ const pkgId = packageMetaToFhir(fhirSchema.package_meta);
2982
+ if (skipMe[pkgId]?.[fhirSchema.url]) {
2983
+ logger?.dry_warn(`Skip ${fhirSchema.url} from ${pkgId}. Reason: ${skipMe[pkgId]?.[fhirSchema.url]}`);
2984
+ continue;
2985
+ }
3019
2986
  fhirSchemas.push(...await transformFhirSchema(register, fhirSchema, logger));
3020
2987
  }
3021
2988
  for (const vsSchema of register.allVs()) {
@@ -3050,23 +3017,9 @@ var uppercaseFirstLetter = (str) => {
3050
3017
  var uppercaseFirstLetterOfEach = (strings) => {
3051
3018
  return strings.map((str) => uppercaseFirstLetter(str));
3052
3019
  };
3053
- function deepEqual(obj1, obj2) {
3054
- if (obj1 === obj2) return true;
3055
- if (obj1 === null || obj2 === null || typeof obj1 !== "object" || typeof obj2 !== "object") {
3056
- return false;
3057
- }
3058
- if (Array.isArray(obj1) && Array.isArray(obj2)) {
3059
- if (obj1.length !== obj2.length) return false;
3060
- return obj1.every((item, index) => deepEqual(item, obj2[index]));
3061
- }
3062
- if (Array.isArray(obj1) || Array.isArray(obj2)) {
3063
- return false;
3064
- }
3065
- const keys1 = Object.keys(obj1);
3066
- const keys2 = Object.keys(obj2);
3067
- if (keys1.length !== keys2.length) return false;
3068
- return keys1.every((key) => keys2.includes(key) && deepEqual(obj1[key], obj2[key]));
3069
- }
3020
+ var typeSchemaInfo = (schema) => {
3021
+ return `<${schema.identifier.url}> from ${schema.identifier.package}#${schema.identifier.version}`;
3022
+ };
3070
3023
  var FileSystemWriter = class {
3071
3024
  opts;
3072
3025
  currentDir;
@@ -3080,12 +3033,14 @@ var FileSystemWriter = class {
3080
3033
  return this.opts.logger;
3081
3034
  }
3082
3035
  cd(path, gen) {
3036
+ const prev = this.currentDir;
3083
3037
  this.currentDir = path.startsWith("/") ? Path4.join(this.opts.outputDir, path) : Path4.join(this.currentDir, path);
3084
3038
  if (!fs.existsSync(this.currentDir)) {
3085
3039
  fs.mkdirSync(this.currentDir, { recursive: true });
3086
3040
  }
3087
3041
  this.logger()?.debug(`cd '${this.currentDir}'`);
3088
3042
  gen();
3043
+ this.currentDir = prev;
3089
3044
  }
3090
3045
  cat(fn, gen) {
3091
3046
  if (this.currentFileDescriptor) throw new Error("Can't open file in file");
@@ -3098,6 +3053,7 @@ var FileSystemWriter = class {
3098
3053
  gen();
3099
3054
  } finally {
3100
3055
  if (this.currentFileDescriptor) {
3056
+ fs.fsyncSync(this.currentFileDescriptor);
3101
3057
  fs.closeSync(this.currentFileDescriptor);
3102
3058
  }
3103
3059
  this.currentFileDescriptor = void 0;
@@ -3512,19 +3468,21 @@ var CSharp = class extends Writer {
3512
3468
  fs__default.copyFileSync(sourceFile, destFile);
3513
3469
  }
3514
3470
  };
3515
-
3516
- // src/typeschema/utils.ts
3517
3471
  var groupByPackages = (typeSchemas) => {
3518
3472
  const grouped = {};
3519
3473
  for (const ts of typeSchemas) {
3520
- const packageName = ts.identifier.package;
3521
- if (!grouped[packageName]) {
3522
- grouped[packageName] = [];
3523
- }
3524
- grouped[packageName].push(ts);
3474
+ const pkgName = ts.identifier.package;
3475
+ if (!grouped[pkgName]) grouped[pkgName] = [];
3476
+ grouped[pkgName].push(ts);
3525
3477
  }
3526
- for (const [_packageName, typeSchemas2] of Object.entries(grouped)) {
3527
- typeSchemas2.sort((a, b) => a.identifier.name.localeCompare(b.identifier.name));
3478
+ for (const [packageName, typeSchemas2] of Object.entries(grouped)) {
3479
+ const dict = {};
3480
+ for (const ts of typeSchemas2) {
3481
+ dict[JSON.stringify(ts.identifier)] = ts;
3482
+ }
3483
+ const tmp = Object.values(dict);
3484
+ tmp.sort((a, b) => a.identifier.name.localeCompare(b.identifier.name));
3485
+ grouped[packageName] = tmp;
3528
3486
  }
3529
3487
  return grouped;
3530
3488
  };
@@ -3564,7 +3522,8 @@ var mkTypeSchemaIndex = (schemas, logger) => {
3564
3522
  if (index[url][schema.identifier.package] && pkg !== "shared") {
3565
3523
  const r1 = JSON.stringify(schema.identifier, void 0, 2);
3566
3524
  const r2 = JSON.stringify(index[url][pkg]?.identifier, void 0, 2);
3567
- throw new Error(`Duplicate schema: ${r1} and ${r2}`);
3525
+ if (r1 !== r2) throw new Error(`Duplicate schema: ${r1} and ${r2}`);
3526
+ return;
3568
3527
  }
3569
3528
  index[url][pkg] = schema;
3570
3529
  };
@@ -3622,7 +3581,10 @@ var mkTypeSchemaIndex = (schemas, logger) => {
3622
3581
  if (!schema2.fields) continue;
3623
3582
  for (const [fieldName, fieldConstraints] of Object.entries(schema2.fields)) {
3624
3583
  if (mergedFields[fieldName]) {
3625
- mergedFields[fieldName] = { ...mergedFields[fieldName], ...fieldConstraints };
3584
+ mergedFields[fieldName] = {
3585
+ ...mergedFields[fieldName],
3586
+ ...fieldConstraints
3587
+ };
3626
3588
  } else {
3627
3589
  mergedFields[fieldName] = { ...fieldConstraints };
3628
3590
  }
@@ -3647,6 +3609,26 @@ var mkTypeSchemaIndex = (schemas, logger) => {
3647
3609
  return schema.fields?.meta !== void 0;
3648
3610
  });
3649
3611
  };
3612
+ const exportTree = async (filename) => {
3613
+ const tree = {};
3614
+ for (const [pkgId, shemas] of Object.entries(groupByPackages(schemas))) {
3615
+ tree[pkgId] = { complexTypes: {}, resources: {}, profiles: {} };
3616
+ for (const schema of shemas) {
3617
+ schema.identifier;
3618
+ if (isResourceTypeSchema(schema)) {
3619
+ tree[pkgId].resources[schema.identifier.url] = {};
3620
+ }
3621
+ if (isProfileTypeSchema(schema)) {
3622
+ tree[pkgId].profiles[schema.identifier.url] = {};
3623
+ }
3624
+ if (isComplexTypeTypeSchema(schema)) {
3625
+ tree[pkgId].complexTypes[schema.identifier.url] = {};
3626
+ }
3627
+ }
3628
+ }
3629
+ const raw = filename.endsWith(".yaml") ? YAML.stringify(tree) : JSON.stringify(tree, void 0, 2);
3630
+ await afs3.writeFile(filename, raw);
3631
+ };
3650
3632
  return {
3651
3633
  _schemaIndex: index,
3652
3634
  _relations: relations,
@@ -3661,7 +3643,8 @@ var mkTypeSchemaIndex = (schemas, logger) => {
3661
3643
  findLastSpecialization,
3662
3644
  findLastSpecializationByIdentifier,
3663
3645
  flatProfile,
3664
- isWithMetaField
3646
+ isWithMetaField,
3647
+ exportTree
3665
3648
  };
3666
3649
  };
3667
3650
 
@@ -5730,15 +5713,8 @@ var tsModuleFileName = (id) => {
5730
5713
  };
5731
5714
  var canonicalToName2 = (canonical, dropFragment = true) => {
5732
5715
  if (!canonical) return void 0;
5733
- let localName = canonical.split("/").pop();
5734
- if (!localName) return void 0;
5735
- if (dropFragment && localName.includes("#")) {
5736
- localName = localName.split("#")[0];
5737
- }
5716
+ const localName = extractNameFromCanonical(canonical, dropFragment);
5738
5717
  if (!localName) return void 0;
5739
- if (/^\d/.test(localName)) {
5740
- localName = `number_${localName}`;
5741
- }
5742
5718
  return normalizeTsName(localName);
5743
5719
  };
5744
5720
  var tsResourceName = (id) => {
@@ -5794,12 +5770,14 @@ var TypeScript = class extends Writer {
5794
5770
  if (["complex-type", "resource", "logical"].includes(dep.kind)) {
5795
5771
  imports.push({
5796
5772
  tsPackage: `../${kebabCase(dep.package)}/${pascalCase(dep.name)}`,
5797
- name: uppercaseFirstLetter(dep.name)
5773
+ name: uppercaseFirstLetter(dep.name),
5774
+ dep
5798
5775
  });
5799
5776
  } else if (isNestedIdentifier(dep)) {
5800
5777
  imports.push({
5801
5778
  tsPackage: `../${kebabCase(dep.package)}/${pascalCase(canonicalToName2(dep.url) ?? "")}`,
5802
- name: tsResourceName(dep)
5779
+ name: tsResourceName(dep),
5780
+ dep
5803
5781
  });
5804
5782
  } else {
5805
5783
  skipped.push(dep);
@@ -5807,6 +5785,7 @@ var TypeScript = class extends Writer {
5807
5785
  }
5808
5786
  imports.sort((a, b) => a.name.localeCompare(b.name));
5809
5787
  for (const dep of imports) {
5788
+ this.debugComment(dep.dep);
5810
5789
  this.tsImportType(dep.tsPackage, dep.name);
5811
5790
  }
5812
5791
  for (const dep of skipped) {
@@ -5849,7 +5828,9 @@ var TypeScript = class extends Writer {
5849
5828
  if (isResourceTypeSchema(schema)) {
5850
5829
  const possibleResourceTypes = [schema.identifier];
5851
5830
  possibleResourceTypes.push(...tsIndex.resourceChildren(schema.identifier));
5852
- this.lineSM(`resourceType: ${possibleResourceTypes.map((e) => `"${e.name}"`).join(" | ")}`);
5831
+ this.lineSM(
5832
+ `resourceType: ${possibleResourceTypes.sort((a, b) => a.name.localeCompare(b.name)).map((e) => `"${e.name}"`).join(" | ")}`
5833
+ );
5853
5834
  this.line();
5854
5835
  }
5855
5836
  if (!schema.fields) return;
@@ -5891,6 +5872,7 @@ var TypeScript = class extends Writer {
5891
5872
  }
5892
5873
  }
5893
5874
  generateProfileType(tsIndex, flatProfile) {
5875
+ this.debugComment("flatProfile", flatProfile);
5894
5876
  const tsName = tsResourceName(flatProfile.identifier);
5895
5877
  this.debugComment("identifier", flatProfile.identifier);
5896
5878
  this.debugComment("base", flatProfile.base);
@@ -5928,6 +5910,8 @@ var TypeScript = class extends Writer {
5928
5910
  tsType = tsResourceName(field.type);
5929
5911
  } else if (isPrimitiveIdentifier(field.type)) {
5930
5912
  tsType = resolvePrimitiveType(field.type.name);
5913
+ } else if (field.type === void 0) {
5914
+ throw new Error(`Undefined type for '${fieldName}' field at ${typeSchemaInfo(flatProfile)}`);
5931
5915
  } else {
5932
5916
  tsType = field.type.name;
5933
5917
  }
@@ -6095,6 +6079,9 @@ var writerToGenerator = (writerGen) => {
6095
6079
  return {
6096
6080
  generate: async (schemas) => {
6097
6081
  const tsIndex = mkTypeSchemaIndex(schemas);
6082
+ if (writerGen.opts.writeTypeTree) {
6083
+ await tsIndex.exportTree(writerGen.opts.writeTypeTree);
6084
+ }
6098
6085
  writerGen.generate(tsIndex);
6099
6086
  return getGeneratedFiles();
6100
6087
  },
@@ -6102,8 +6089,12 @@ var writerToGenerator = (writerGen) => {
6102
6089
  build: async (_schemas) => getGeneratedFiles()
6103
6090
  };
6104
6091
  };
6105
- var normalizeFileName = (str) => str.replace(/[^a-zA-Z0-9]/g, "");
6106
- var APIBuilder = class _APIBuilder {
6092
+ var normalizeFileName = (str) => {
6093
+ const res = str.replace(/[^a-zA-Z0-9\-_.@#()]/g, "");
6094
+ if (res.length === 0) return "unknown";
6095
+ return res;
6096
+ };
6097
+ var APIBuilder = class {
6107
6098
  schemas = [];
6108
6099
  options;
6109
6100
  generators = /* @__PURE__ */ new Map();
@@ -6124,7 +6115,8 @@ var APIBuilder = class _APIBuilder {
6124
6115
  typeSchemaConfig: options.typeSchemaConfig,
6125
6116
  manager: options.manager || null,
6126
6117
  throwException: options.throwException || false,
6127
- typeSchemaOutputDir: options.typeSchemaOutputDir
6118
+ typeSchemaOutputDir: options.typeSchemaOutputDir,
6119
+ exportTypeTree: options.exportTypeTree
6128
6120
  };
6129
6121
  this.typeSchemaConfig = options.typeSchemaConfig;
6130
6122
  this.logger = options.logger || createLogger({
@@ -6186,7 +6178,8 @@ var APIBuilder = class _APIBuilder {
6186
6178
  outputDir: Path4.join(this.options.outputDir, "/types"),
6187
6179
  tabSize: 4,
6188
6180
  withDebugComment: false,
6189
- commentLinePrefix: "//"
6181
+ commentLinePrefix: "//",
6182
+ exportTypeTree: this.options.exportTypeTree
6190
6183
  };
6191
6184
  const effectiveOpts = { logger: this.logger, ...writerOpts, ...opts };
6192
6185
  const generator = writerToGenerator(new TypeScript(effectiveOpts));
@@ -6245,63 +6238,70 @@ var APIBuilder = class _APIBuilder {
6245
6238
  this.options.cleanOutput = enabled;
6246
6239
  return this;
6247
6240
  }
6241
+ writeTypeTree(filename) {
6242
+ this.options.exportTypeTree = filename;
6243
+ return this;
6244
+ }
6248
6245
  writeTypeSchemas(target) {
6249
6246
  this.options.typeSchemaOutputDir = target;
6250
6247
  return this;
6251
6248
  }
6252
- static async isIdenticalTo(path, tsJSON) {
6253
- if (!await afs2.exists(path)) return false;
6254
- const json = await afs2.readFile(path);
6255
- const ts1 = JSON.parse(json.toString());
6256
- const ts2 = JSON.parse(tsJSON);
6257
- return deepEqual(ts1, ts2);
6258
- }
6259
6249
  async writeTypeSchemasToSeparateFiles(typeSchemas, outputDir) {
6260
- if (this.options.cleanOutput) fs.rmSync(outputDir, { recursive: true, force: true });
6261
- await afs2.mkdir(outputDir, { recursive: true });
6262
- const usedNames = {};
6263
- this.logger.info(`Writing TypeSchema files to ${outputDir}...`);
6250
+ await afs3.mkdir(outputDir, { recursive: true });
6251
+ this.logger.info(`Writing TypeSchema files to ${outputDir}/...`);
6252
+ const files = {};
6264
6253
  for (const ts of typeSchemas) {
6265
- const package_name = camelCase(ts.identifier.package.replaceAll("/", "-"));
6266
- const name = normalizeFileName(ts.identifier.name.toString());
6254
+ const pkg = { name: ts.identifier.package, version: ts.identifier.version };
6255
+ const pkgPath = normalizeFileName(packageMetaToFhir(pkg));
6256
+ const name = normalizeFileName(`${ts.identifier.name}(${extractNameFromCanonical(ts.identifier.url)})`);
6267
6257
  const json = JSON.stringify(ts, null, 2);
6268
- const baseName = Path4.join(outputDir, package_name, name);
6269
- let fullName;
6270
- if (usedNames[baseName] !== void 0) {
6271
- usedNames[baseName]++;
6272
- fullName = `${baseName}-${usedNames[baseName]}.typeschema.json`;
6273
- } else {
6274
- usedNames[baseName] = 0;
6275
- fullName = `${baseName}.typeschema.json`;
6276
- }
6277
- if (await _APIBuilder.isIdenticalTo(fullName, json)) continue;
6278
- await afs2.mkdir(Path4.dirname(fullName), { recursive: true });
6279
- await afs2.writeFile(fullName, json);
6258
+ const baseName = Path4.join(outputDir, pkgPath, name);
6259
+ if (!files[baseName]) files[baseName] = [];
6260
+ if (!files[baseName]?.some((e) => e === json)) {
6261
+ files[baseName].push(json);
6262
+ }
6263
+ }
6264
+ for (const [baseName, jsons] of Object.entries(files)) {
6265
+ await Promise.all(
6266
+ jsons.map(async (json, index) => {
6267
+ let fullName;
6268
+ if (index === 0) {
6269
+ fullName = `${baseName}.typeschema.json`;
6270
+ } else {
6271
+ fullName = `${baseName}-${index}.typeschema.json`;
6272
+ }
6273
+ await afs3.mkdir(Path4.dirname(fullName), { recursive: true });
6274
+ await afs3.writeFile(fullName, json);
6275
+ })
6276
+ );
6280
6277
  }
6281
6278
  }
6282
6279
  async writeTypeSchemasToSingleFile(typeSchemas, outputFile) {
6280
+ this.logger.info(`Writing TypeSchema files to: ${outputFile}`);
6283
6281
  if (this.options.cleanOutput && fs.existsSync(outputFile)) fs.rmSync(outputFile);
6284
- await afs2.mkdir(Path4.dirname(outputFile), { recursive: true });
6282
+ await afs3.mkdir(Path4.dirname(outputFile), { recursive: true });
6285
6283
  this.logger.info(`Writing TypeSchemas to one file ${outputFile}...`);
6286
6284
  for (const ts of typeSchemas) {
6287
6285
  const json = JSON.stringify(ts, null, 2);
6288
- await afs2.appendFile(outputFile, json + "\n");
6286
+ await afs3.appendFile(outputFile, `${json}
6287
+ `);
6289
6288
  }
6290
6289
  }
6291
6290
  async tryWriteTypeSchema(typeSchemas) {
6292
6291
  if (!this.options.typeSchemaOutputDir) return;
6293
6292
  try {
6294
- this.logger.info(`Starting writing TypeSchema files.`);
6295
- if (Path4.extname(this.options.typeSchemaOutputDir) === ".ndjson")
6293
+ if (Path4.extname(this.options.typeSchemaOutputDir) === ".ndjson") {
6296
6294
  await this.writeTypeSchemasToSingleFile(typeSchemas, this.options.typeSchemaOutputDir);
6297
- else await this.writeTypeSchemasToSeparateFiles(typeSchemas, this.options.typeSchemaOutputDir);
6298
- this.logger.info(`Finished writing TypeSchema files.`);
6295
+ } else {
6296
+ await this.writeTypeSchemasToSeparateFiles(typeSchemas, this.options.typeSchemaOutputDir);
6297
+ }
6298
+ this.logger.info(`Writing TypeSchema - DONE`);
6299
6299
  } catch (error) {
6300
- if (this.options.throwException) throw error;
6301
6300
  this.logger.error(
6302
6301
  "Failed to write TypeSchema output",
6303
6302
  error instanceof Error ? error : new Error(String(error))
6304
6303
  );
6304
+ if (this.options.throwException) throw error;
6305
6305
  }
6306
6306
  }
6307
6307
  async generate() {
@@ -6316,10 +6316,18 @@ var APIBuilder = class _APIBuilder {
6316
6316
  };
6317
6317
  this.logger.debug(`Starting generation with ${this.generators.size} generators`);
6318
6318
  if (this.options.cleanOutput) {
6319
- this.logger.info(`Cleaning output directory: ${this.options.outputDir}`);
6319
+ this.logger.info(`Cleaning outputs...`);
6320
6320
  try {
6321
+ this.logger.info(`Clean ${this.options.outputDir}`);
6321
6322
  fs.rmSync(this.options.outputDir, { recursive: true, force: true });
6322
- fs.mkdirSync(this.options.outputDir, { recursive: true });
6323
+ if (this.options.typeSchemaOutputDir) {
6324
+ this.logger.info(`Clean ${this.options.typeSchemaOutputDir}`);
6325
+ fs.rmSync(this.options.typeSchemaOutputDir, { recursive: true, force: true });
6326
+ }
6327
+ if (this.options.exportTypeTree) {
6328
+ this.logger.info(`Clean ${this.options.exportTypeTree}`);
6329
+ fs.rmSync(this.options.exportTypeTree, { recursive: true, force: true });
6330
+ }
6323
6331
  } catch (error) {
6324
6332
  this.logger.warn(
6325
6333
  `Error cleaning output directory: ${error instanceof Error ? error.message : String(error)}`
@@ -6333,7 +6341,10 @@ var APIBuilder = class _APIBuilder {
6333
6341
  workingDir: "tmp/fhir"
6334
6342
  });
6335
6343
  await manager.init();
6336
- const register = await registerFromManager(manager, { logger: this.logger });
6344
+ const register = await registerFromManager(manager, {
6345
+ logger: this.logger,
6346
+ focusedPackages: this.packages.map(npmToPackageMeta)
6347
+ });
6337
6348
  const typeSchemas = await generateTypeSchemas(register, this.logger);
6338
6349
  await this.tryWriteTypeSchema(typeSchemas);
6339
6350
  this.logger.debug(`Executing ${this.generators.size} generators`);