UNPKG

fhir-snapshot-generator

Version:
2,260 lines 76.1 kB
'use strict';

var path = require('path');
var fs = require('fs-extra');
var fhirPackageExplorer = require('fhir-package-explorer');

function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }

var path__default = /*#__PURE__*/_interopDefault(path);
var fs__default = /*#__PURE__*/_interopDefault(fs);

// src/utils/element/applyDiffs.ts
var applySingleDiff = (elements, diffElement) => {
  const index = elements.findIndex((el) => el.id === diffElement.id);
  if (index === -1) {
    throw new Error(`Element with id "${diffElement.id}" not found`);
  }
  const baseElement = { ...elements[index] };
  const mergedElement = mergeElement(baseElement, diffElement);
  if (mergedElement.sliceName && !mergedElement.id.endsWith(`:${mergedElement.sliceName}`)) {
    delete mergedElement.sliceName;
  }
  const updatedElements = [...elements.slice(0, index), mergedElement, ...elements.slice(index + 1)];
  return updatedElements;
};
var rewrite = (original, kind, pathRewriteMap) => {
  for (const [illegalPrefix, rewrite2] of pathRewriteMap.entries()) {
    if (original === illegalPrefix || original.startsWith(illegalPrefix + ".")) {
      return original.replace(illegalPrefix, rewrite2[kind]);
    }
  }
  return original;
};
var applyDiffs = async (elements, diffs, fetcher, logger) => {
  let updatedElements = [...elements];
  const pathRewriteMap = /* @__PURE__ */ new Map();
  if (updatedElements[0].extension) {
    delete updatedElements[0].extension;
  }
  if (diffs.length === 0) return updatedElements;
  for (const diff of diffs) {
    if (!elementExists(updatedElements, diff.id)) {
      updatedElements = await ensureBranch(updatedElements, diff.id, fetcher, logger, pathRewriteMap);
    }
    const rewrittenDiff = {
      ...diff,
      id: rewrite(diff.id, "id", pathRewriteMap),
      path: rewrite(diff.path, "path", pathRewriteMap)
    };
    updatedElements = applySingleDiff(updatedElements, rewrittenDiff);
  }
  return updatedElements;
};

// src/utils/element/ensureChild.ts
var ensureChild = async (elements, parentId, childId, fetcher, logger, pathRewriteMap) => {
  const parentElementBlock = elements.filter((element) => element.id === parentId || element.id.startsWith(`${parentId}.`));
  if (parentElementBlock.length === 0) {
    throw new Error(`Parent element '${parentId}' not found in the working snapshot array`);
  }
  let parentNode = toTree(parentElementBlock);
  if (isNodeSliceable(parentNode)) {
    parentNode = parentNode.children[0];
  }
  const isExpanded = parentNode.children.length > 0;
  if (!isExpanded) {
    parentNode = await expandNode(parentNode, fetcher);
    elements = injectElementBlock(elements, parentId, fromTree(parentNode));
  }
  const [elementName, sliceName] = childId.split(":");
  const childElement = parentNode.children.find((element) => element.id.endsWith(`.${elementName}`));
  if (!childElement) {
    const match = findMonopolyShortcutTarget(parentId, elementName, parentNode.children);
    if (match) {
      const canonicalId = `${parentId}.${match.rewrittenSegment}`;
      const elementDefinition = elements.find((e) => e.id === canonicalId);
      const canonicalPath = elementDefinition?.path ?? canonicalId;
      pathRewriteMap.set(`${parentId}.${elementName}`, {
        id: canonicalId,
        path: canonicalPath
      });
      const virtualDiff = {
        id: canonicalId,
        path: canonicalPath,
        type: [{ code: match.type }]
      };
      elements = applySingleDiff(elements, virtualDiff);
      return elements;
    }
    throw new Error(`Element '${childId}' is illegal under '${parentId}'.`);
  }
  if (!sliceName) return elements;
  if (!isNodeSliceable(childElement)) {
    const aliasId = `${childElement.id}:${sliceName}`;
    pathRewriteMap.set(aliasId, {
      id: childElement.id,
      path: childElement.path
    });
    return elements;
  }
  const slice = childElement.children.find((slice2) => slice2.sliceName === sliceName);
  if (slice) {
    return elements;
  }
  if (!slice) {
    if (elementName.endsWith("[x]") && childElement.children[0].definition?.type?.length === 1) {
      const onlyType = childElement.children[0].definition.type[0].code;
      const monopolySliceName = `${elementName.slice(0, -3)}${initCap(onlyType)}`;
      if (sliceName === monopolySliceName) {
        const aliasId = `${childElement.id}:${sliceName}`;
        const aliasPath = childElement.path;
        pathRewriteMap.set(aliasId, {
          id: childElement.id,
          path: aliasPath
        });
        return elements;
      }
    }
    const headSlice = childElement.children[0];
    const newId = `${headSlice.id}:${sliceName}`;
    const newSlice = rewriteNodePaths(headSlice, newId, headSlice.id);
    newSlice.nodeType = "slice";
    delete newSlice.definition?.slicing;
    delete newSlice.definition?.mustSupport;
    newSlice.sliceName = sliceName;
    if (newSlice.definition) {
      newSlice.definition.sliceName = sliceName;
    }
    childElement.children.push(newSlice);
    elements = injectElementBlock(elements, childElement.id, fromTree(childElement));
  }
  return elements;
};

// src/utils/element/ensureBranch.ts
var ensureBranch = async (elements, targetElementId, fetcher, logger, pathRewriteMap) => {
  const idSegments = targetElementId.split(".");
  const rootId = idSegments[0];
  let updatedElements = elements;
  if (elements[0].id !== rootId) {
    throw new Error(`Root element '${rootId}' not found in the working snapshot array`);
  }
  if (rootId === targetElementId) {
    return updatedElements;
  }
  let canonicalParentId = rootId;
  for (let i = 1; i < idSegments.length; i++) {
    const rawChildSegment = idSegments[i];
    const rewrite2 = pathRewriteMap.get(canonicalParentId);
    if (rewrite2) {
      canonicalParentId = rewrite2.id;
    }
    updatedElements = await ensureChild(
      updatedElements,
      canonicalParentId,
      rawChildSegment,
      fetcher,
      logger,
      pathRewriteMap
    );
    canonicalParentId = `${canonicalParentId}.${rawChildSegment}`;
  }
  return updatedElements;
};

// src/utils/element/toNodeType.ts
var toNodeType = (element) => {
  if (element.id.endsWith("[x]")) {
    return "poly";
  }
  if (element.sliceName) {
    if (element.slicing) return "resliced";
    return "slice";
  }
  if (element.base?.max && (element.base.max === "*" || parseInt(element.base.max) > 1)) {
    return "array";
  }
  return "element";
};

// src/utils/element/injectElementBlock.ts
var injectElementBlock = (elements, injectionPoint, elementBlock) => {
  const index = elements.findIndex((el) => el.id === injectionPoint);
  if (index === -1) throw new Error(`Element with id "${injectionPoint}" not found`);
  const before = elements.slice(0, index);
  const after = elements.slice(index + 1).filter((element) => !element.id.startsWith(`${injectionPoint}.`) && !element.id.startsWith(`${injectionPoint}:`));
  const results = [...before, ...elementBlock, ...after];
  return results;
};

// src/utils/element/mergeElement.ts
var mergeElement = (base, diff) => {
  if (diff.id !== base.id) {
    throw new Error(`Element ID mismatch. Tried to apply "${diff.id}" onto "${base.id}".`);
  }
  const mergedElement = { ...base };
  for (const key of Object.keys(diff)) {
    if (["constraint", "condition", "mapping"].includes(key)) {
      const baseArr = base[key] || [], diffArr = diff[key] || [];
      if (key === "constraint") {
        mergedElement.constraint = [...baseArr, ...diffArr];
      } else if (key === "condition") {
        const ids = Array.from(/* @__PURE__ */ new Set([...baseArr, ...diffArr]));
        mergedElement.condition = ids;
      } else {
        const seen = /* @__PURE__ */ new Set();
        const serialize = (obj) => JSON.stringify(Object.entries(obj).sort());
        const combined = [...baseArr, ...diffArr];
        mergedElement.mapping = combined.filter((m) => {
          const s = serialize(m);
          return seen.has(s) ? false : seen.add(s);
        });
      }
    } else if (key !== "id" && key !== "path") {
      if (diff[key] !== void 0) {
        mergedElement[key] = diff[key];
      }
    }
  }
  return mergedElement;
};

// src/utils/element/elementExists.ts
var elementExists = (elements, targetElementId) => {
  return elements.some((element) => element.id === targetElementId);
};

// src/utils/element/rewriteElementPaths.ts
var rewriteElementPaths = (elements, newPrefix, oldPrefix) => {
  const oldPrefixDot = oldPrefix.endsWith(".") ? oldPrefix : oldPrefix + ".";
  const newPrefixDot = newPrefix.endsWith(".") ? newPrefix : newPrefix + ".";
  const removeSlices = (elementIdPart) => {
    const segments = elementIdPart.split(".");
    const cleanedSegments = segments.map((segment) => {
      const sliceIndex = segment.indexOf(":");
      return sliceIndex !== -1 ? segment.slice(0, sliceIndex) : segment;
    });
    return cleanedSegments.join(".");
  };
  const replaceId = (str) => str === oldPrefix ? newPrefix : str.startsWith(oldPrefixDot) ? newPrefixDot + str.slice(oldPrefixDot.length) : str;
  const replacePath = (elementPath) => {
    const newPathPrefix = removeSlices(newPrefixDot);
    const oldPathPrefix = removeSlices(oldPrefixDot);
    return elementPath === oldPathPrefix ? newPathPrefix : elementPath.startsWith(oldPathPrefix) ? newPathPrefix + elementPath.slice(oldPathPrefix.length) : elementPath;
  };
  return elements.map((el) => ({
    ...el,
    id: replaceId(el.id),
    path: replacePath(el.path)
  }));
};

// src/utils/element/migrateElements.ts
var markdownKeys = ["definition", "comment", "requirements", "meaningWhenMissing"];
var ignoredExtensions = [
  "http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm",
  "http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm-no-warnings",
  "http://hl7.org/fhir/StructureDefinition/structuredefinition-hierarchy",
  "http://hl7.org/fhir/StructureDefinition/structuredefinition-interface",
  "http://hl7.org/fhir/StructureDefinition/structuredefinition-normative-version",
  "http://hl7.org/fhir/StructureDefinition/structuredefinition-applicable-version",
  "http://hl7.org/fhir/StructureDefinition/structuredefinition-category",
  "http://hl7.org/fhir/StructureDefinition/structuredefinition-codegen-super",
  "http://hl7.org/fhir/StructureDefinition/structuredefinition-security-category",
  "http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status",
  "http://hl7.org/fhir/StructureDefinition/structuredefinition-summary",
  "http://hl7.org/fhir/StructureDefinition/structuredefinition-wg",
  "http://hl7.org/fhir/StructureDefinition/replaces",
  "http://hl7.org/fhir/StructureDefinition/resource-approvalDate",
  "http://hl7.org/fhir/StructureDefinition/resource-effectivePeriod",
  "http://hl7.org/fhir/StructureDefinition/resource-lastReviewDate"
];
var replaceRelativeLinks = (markdown) => {
  return markdown.replace(
    /\[([^\]]+)\]\((?!https?:\/\/)([^)]+)\)/g,
    (_match, text, url) => `[${text}](http://hl7.org/fhir/${url})`
  );
};
var filterExtensions = (extensions) => {
  const result = extensions.filter(
    (ext) => !ignoredExtensions.includes(ext.url)
  );
  return result.length > 0 ? result : void 0;
};
var addSourceToConstraints = (constraints, sourceUrl) => {
  return constraints.map((constraint) => ({ source: sourceUrl, ...constraint }));
};
var migrateElements = (elements, sourceUrl) => {
  return elements.map((el, index) => {
    const updated = { ...el };
    if (index === 0 && el.extension) {
      const filteredExtensions = filterExtensions(el.extension);
      if (filteredExtensions) {
        updated.extension = filteredExtensions;
      } else {
        delete updated.extension;
      }
    }
    if (sourceUrl.startsWith("http://hl7.org/fhir")) {
      for (const key of markdownKeys) {
        const value = el[key];
        if (typeof value === "string") {
          updated[key] = replaceRelativeLinks(value);
        }
      }
    }
    if (el.constraint) {
      updated.constraint = addSourceToConstraints(el.constraint, sourceUrl);
    }
    return updated;
  });
};

// src/utils/element/findMonopolyShortcutTarget.ts
var findMonopolyShortcutTarget = (parentId, missingSegment, elements) => {
  const prefix = `${parentId}.`;
  const childElements = elements.filter(
    (el) => el.id.startsWith(prefix) && el.id !== parentId && el.id.endsWith("[x]")
  );
  for (const el of childElements) {
    const idSegment = el.id.slice(prefix.length);
    const base = idSegment.slice(0, -3);
    for (const type of el?.children[0]?.definition?.type ?? []) {
      const shortcut = base + initCap(type.code);
      if (shortcut === missingSegment) {
        return {
          rewrittenSegment: `${base}[x]`,
          type: type.code
        };
      }
    }
  }
  return null;
};

// src/utils/tree/expandNode.ts
var expandNode = async (node, fetcher) => {
  if (isNodeSliceable(node)) {
    throw new Error(`Node '${node.id}' is sliceable. Expand node must be called on a specific slice, headslice or a non-sliceable node.`);
  }
  if (node.children.length > 0) return node;
  if (!node.definition) {
    throw new Error(`Node '${node.id}' has no ElementDefinition. Cannot expand.`);
  }
  const definition = node.definition;
  if ((!definition.type || definition.type.length === 0) && !definition.contentReference) {
    throw new Error(`Node '${node.id}' has no type or contentReference defined. Cannot expand.`);
  }
  let snapshotElements = [];
  if (definition.contentReference) {
    snapshotElements = await fetcher.getContentReference(definition.contentReference);
    if (!snapshotElements || snapshotElements.length === 0) {
      throw new Error(`Snapshot for contentReference '${definition.contentReference}' is empty or missing.`);
    }
    delete definition.contentReference;
  } else if (definition.type && definition.type.length > 1) {
    snapshotElements = await fetcher.getBaseType("Element");
    if (!snapshotElements || snapshotElements.length === 0) {
      throw new Error("Snapshot for base type 'Element' is empty or missing.");
    }
  } else if (definition.type && definition.type.length === 1) {
    const elementType = definition.type[0];
    if (elementType.profile && elementType.profile.length === 1) {
      const url = elementType.profile[0];
      snapshotElements = await fetcher.getByUrl(url);
      if (!snapshotElements || snapshotElements.length === 0) {
        throw new Error(`Snapshot for type '${url}' is empty or missing.`);
      }
    } else {
      const id = elementType.code;
      if (id.startsWith("http://hl7.org/fhirpath/System.")) {
        throw new Error(`Cannot expand node '${node.id}', type '${id}' can never have children.`);
      }
      snapshotElements = await fetcher.getBaseType(id);
      if (!snapshotElements || snapshotElements.length === 0) {
        throw new Error(`Snapshot for type '${id}' is empty or missing.`);
      }
    }
  }
  if (!snapshotElements || snapshotElements.length === 0) {
    throw new Error(`Error expanding node '${node.id}' - the snapshot contains no elements.`);
  }
  const oldPrefix = snapshotElements[0].id;
  const newPrefix = node.id;
  const rewrittenElements = rewriteElementPaths(snapshotElements, newPrefix, oldPrefix);
  const expandedSubtree = toTree(rewrittenElements);
  const clonedNode = { ...node, children: expandedSubtree.children };
  return clonedNode;
};

// src/utils/tree/fromTree.ts
var fromTree = (tree) => {
  const result = [];
  function visit(node) {
    if (["element", "slice", "headslice"].includes(node.nodeType)) {
      if (!node.definition) {
        throw new Error(`Node ${node.id} of type ${node.nodeType} is missing its definition`);
      }
      result.push(node.definition);
    }
    for (const child of node.children) {
      visit(child);
    }
  }
  visit(tree);
  return result;
};

// src/utils/tree/toTree.ts
var toTree = (elements) => {
  if (elements.length === 0) {
    throw new Error("Element array is empty");
  }
  const idToNode = /* @__PURE__ */ new Map();
  const makeSegments = (fullId) => {
    const idSegments = fullId.split(".");
    const pathSegments = idSegments.map((segment) => segment.split(":")[0]);
    return { idSegments, pathSegments };
  };
  const createNode = (element, forceNodeType) => {
    const { idSegments, pathSegments } = makeSegments(element.id);
    const nodeType = forceNodeType || toNodeType(element);
    const node = {
      id: element.id,
      path: element.path,
      idSegments,
      pathSegments,
      nodeType,
      children: []
    };
    if (isNodeSliceable(node)) {
      const headSlice = {
        id: element.id,
        path: element.path,
        idSegments,
        pathSegments,
        nodeType: "headslice",
        definition: element,
        children: []
      };
      node.children.push(headSlice);
    } else {
      node.definition = element;
      if (element.sliceName) {
        node.sliceName = element.sliceName;
      }
    }
    return node;
  };
  const rootElement = elements[0];
  const rootNode = createNode(rootElement, "element");
  idToNode.set(rootNode.id, rootNode);
  for (let i = 1; i < elements.length; i++) {
    const element = elements[i];
    const { idSegments } = makeSegments(element.id);
    const lastSegment = idSegments[idSegments.length - 1];
    const isSlice = lastSegment.includes(":");
    const lastSegmentWithoutSlice = isSlice ? lastSegment.split(":")[0] : lastSegment;
    const parentPath = isSlice ? idSegments.slice(0, -1).join(".") + `.${lastSegmentWithoutSlice}` : idSegments.slice(0, -1).join(".");
    const parentNode = idToNode.get(parentPath);
    if (!parentNode) {
      throw new Error(`Parent node not found for element ${element.id}`);
    }
    const newNode = createNode(element);
    if (isNodeSliceable(parentNode)) {
      if (isSlice) {
        parentNode.children.push(newNode);
      } else {
        parentNode.children[0].children.push(newNode);
      }
    } else {
      parentNode.children.push(newNode);
    }
    idToNode.set(newNode.id, newNode);
  }
  return rootNode;
};

// src/utils/tree/rewriteNodePaths.ts
var rewriteNodePaths = (node, newPrefix, oldPrefix) => {
  const flattened = fromTree(node);
  const rewritten = rewriteElementPaths(flattened, newPrefix, oldPrefix);
  const newNode = toTree(rewritten);
  return newNode;
};

// src/utils/tree/isNodeSliceable.ts
var isNodeSliceable = (node) => {
  return ["array", "poly", "resliced"].includes(node.nodeType);
};

// src/utils/misc/logger.ts
var defaultLogger = {
  info: (msg) => console.log(msg),
  warn: (msg) => console.warn(msg),
  error: (msg) => console.error(msg)
};
var defaultPrethrow = (msg) => {
  if (msg instanceof Error) {
    return msg;
  }
  const error = new Error(msg);
  return error;
};
var customPrethrower = (logger) => {
  return (msg) => {
    if (msg instanceof Error) {
      logger.error(msg);
      return msg;
    }
    const error = new Error(msg);
    logger.error(error);
    return error;
  };
};

// src/utils/misc/resolveFhirVersion.ts
var fhirVersionMap = {
  "3.0.2": "STU3",
  "3.0": "STU3",
  "R3": "STU3",
  "4.0.0": "R4",
  "4.0.1": "R4",
  "4.0": "R4",
  "4.3.0": "R4B",
  "4.3": "R4B",
  "5.0.0": "R5",
  "5.0": "R5"
};
var fhirCorePackages = {
  "STU3": "hl7.fhir.r3.core@3.0.2",
  "R4": "hl7.fhir.r4.core@4.0.1",
  "R4B": "hl7.fhir.r4b.core@4.3.0",
  "R5": "hl7.fhir.r5.core@5.0.0"
};
var resolveFhirVersion = (version2, toPackage) => {
  const canonicalVersion = fhirVersionMap[version2] || version2;
  const corePackage = fhirCorePackages[canonicalVersion];
  if (!corePackage) {
    throw new Error(`Unsupported FHIR version: ${version2}. Supported versions are: ${Object.keys(fhirCorePackages).join(", ")}`);
  }
  if (toPackage) {
    const [id, version3] = corePackage.split("@");
    return { id, version: version3 };
  }
  return canonicalVersion;
};

// src/utils/misc/resolveBasePackage.ts
var findCorePackage = async (pkg, fpe) => {
  if (Object.values(fhirCorePackages).includes(`${pkg.id}@${pkg.version}`)) {
    return [{ id: pkg.id, version: pkg.version }];
  }
  const deps = await fpe.getDirectDependencies(pkg);
  return deps.filter((dep) => Object.values(fhirCorePackages).includes(`${dep.id}@${dep.version}`));
};
var resolveBasePackage = async (packageId, packageVersion, fpe, logger) => {
  const corePackages = await findCorePackage({ id: packageId, version: packageVersion }, fpe);
  if (corePackages.length === 0) {
    logger.warn(`No base FHIR package dependency found for ${packageId}@${packageVersion}.`);
    const pkgManifest = await fpe.getPackageManifest({ id: packageId, version: packageVersion });
    if (pkgManifest["fhir-version-list"] && !pkgManifest.fhirVersions) {
      pkgManifest.fhirVersions = pkgManifest["fhir-version-list"];
    }
    if (pkgManifest.fhirVersions && pkgManifest.fhirVersions.length > 0) {
      pkgManifest.fhirVersions.map((fhirVersion) => {
        logger.info(`Resolving core package for FHIR version ${fhirVersion}`);
        const corePackageId = resolveFhirVersion(fhirVersion, true);
        if (corePackageId) {
          const { id, version: version3 } = corePackageId;
          corePackages.push(
            {
              id,
              version: version3
            }
          );
        } else {
          logger.warn(`Unknown FHIR version ${version2} in package ${packageId}@${packageVersion}.`);
        }
      });
      if (corePackages.length === 0) return void 0;
    }
  }
  if (corePackages.length > 1) {
    logger.warn(`Multiple base FHIR packages found for ${packageId}@${packageVersion}: ${corePackages.map((pkg) => `${pkg.id}@${pkg.version}`).join(", ")}.`);
    return void 0;
  }
  const version2 = corePackages[0].id === "hl7.fhir.r4.core" && corePackages[0].version === "4.0.0" ? "4.0.1" : corePackages[0].version;
  return `${corePackages[0].id}@${version2}`;
};

// src/utils/misc/definitionFetcher.ts
var DefinitionFetcher5 = class {
  constructor(sourcePackage, corePackage, fpe, snapshotFetcher) {
    // A map to cache previously fetched element arrays for base types, profiles and contentReference.
    this.elementCache = /* @__PURE__ */ new Map();
    this.sourcePackage = sourcePackage;
    this.corePackage = corePackage;
    this.snapshotFetcher = snapshotFetcher;
    this.fpe = fpe;
  }
  /**
     * Get the definition for one of the base FHIR types.
     * @param type The type ID (e.g., "CodeableConcept", "Quantity", etc.).
     */
  async getBaseType(type) {
    let elements = this.elementCache.get(type);
    if (elements) {
      return elements;
    }
    const definition = await this.fpe.resolve({ resourceType: "StructureDefinition", id: type, package: this.corePackage, derivation: ["Element", "Resource"].includes(type) ? void 0 : "specialization" });
    if (!definition) {
      throw new Error(`FHIR type '${type}' not found in base package '${this.corePackage}'`);
    }
    if (!definition.snapshot || !definition.snapshot.element || definition.snapshot.element.length === 0) {
      throw new Error(`FHIR type '${type}' in base package '${this.corePackage.id}@${this.corePackage.version}' does not have a snapshot`);
    }
    elements = migrateElements(definition.snapshot.element, definition.url);
    this.elementCache.set(type, elements);
    return elements;
  }
  /**
   * Get the structure of a contentReference element.
   * @param identifier The identifier of the contentReference (e.g., "#Observation.referenceRange").
   */
  async getContentReference(identifier) {
    if (!identifier.startsWith("#")) {
      throw new Error(`Invalid contentReference identifier '${identifier}'. Must start with '#'`);
    }
    const elements = this.elementCache.get(identifier);
    if (elements) {
      return elements;
    }
    const elementId = identifier.substring(1);
    const baseType = elementId.split(".")[0];
    const allElements = await this.getBaseType(baseType);
    const matchingElements = allElements.filter((e) => e?.id === elementId || String(e?.id).startsWith(elementId + "."));
    if (matchingElements.length === 0) {
      throw new Error(`No matching elements found for contentReference '${identifier}'`);
    }
    this.elementCache.set(identifier, matchingElements);
    return matchingElements;
  }
  /**
   * When a profile references a type using a URL, the target may be either a profile or a base type.
   * This method resolves the URL, and if it is a base type (derivation=specialization), returns its snapshot.
   * If it is a profile, it returns the snapshot of the profile using the injected snapshotFetcher().
   * The snapshotFetcher is expected to return a pre-generated snapshot from the cache or generate a new one.
   * @param url Canonical URL of the type or profile.
   * @returns The snapshot elements of the resolved type or profile.
   */
  async getByUrl(url) {
    const elements = this.elementCache.get(url);
    if (elements) {
      return elements;
    }
    const metadata = await this.fpe.resolve({ resourceType: "StructureDefinition", url, package: this.sourcePackage });
    if (!metadata) {
      throw new Error(`StructureDefinition '${url}' not found in package '${this.sourcePackage.id}@${this.sourcePackage.version}'`);
    }
    if (metadata.derivation === "specialization") {
      const sd = await this.fpe.resolve({ filename: metadata.filename, package: { id: metadata.__packageId, version: metadata.__packageVersion } });
      const elements2 = migrateElements(sd.snapshot?.element, url);
      if (!elements2 || elements2.length === 0) {
        throw new Error(`StructureDefinition '${url}' does not have a snapshot`);
      }
      this.elementCache.set(url, elements2);
      return elements2;
    }
    if (metadata?.derivation === "constraint") {
      const elements2 = migrateElements(await this.snapshotFetcher(url), url);
      if (!elements2 || elements2.length === 0) {
        throw new Error(`Profile '${url}' does not have a snapshot`);
      }
      this.elementCache.set(url, elements2);
      return elements2;
    }
    throw new Error(`StructureDefinition '${url}' is neither a base type nor a profile`);
  }
};

// src/utils/misc/initCap.ts
var initCap = (s) => s.charAt(0).toUpperCase() + s.slice(1);

// package.json
var version = "1.7.0";

// src/utils/misc/getVersionedCacheDir.ts
var versionedCacheDir = `v${version.split(".").slice(0, 2).join(".")}.x`;

// src/utils/terminology/flattenCodeSystemConcepts.ts
function flattenCodeSystemConcepts(cs) {
  const map = /* @__PURE__ */ new Map();
  const walk = (concepts) => {
    if (!Array.isArray(concepts)) return;
    for (const c of concepts) {
      if (c && typeof c === "object" && typeof c.code === "string") {
        if (!map.has(c.code)) map.set(c.code, c.display);
      }
      if (Array.isArray(c?.concept)) walk(c.concept);
    }
  };
  if (Array.isArray(cs?.concept)) walk(cs.concept);
  return map;
}

// src/utils/terminology/systemMapHelpers.ts
function toSystemCodeMapFromContains(contains) {
  const out = /* @__PURE__ */ new Map();
  if (!Array.isArray(contains)) return out;
  for (const item of contains) {
    if (!item || typeof item !== "object") continue;
    const system = item.system;
    const code = item.code;
    if (!system || !code) continue;
    if (!out.has(system)) out.set(system, /* @__PURE__ */ new Map());
    const m = out.get(system);
    if (!m.has(code)) m.set(code, item.display);
  }
  return out;
}
function mergeSystemMaps(target, source) {
  for (const [system, codes] of source.entries()) {
    if (!target.has(system)) target.set(system, /* @__PURE__ */ new Map());
    const t = target.get(system);
    for (const [code, display] of codes.entries()) {
      if (!t.has(code)) t.set(code, display);
    }
  }
}
function subtractSystemMaps(target, exclude) {
  for (const [system, codes] of exclude.entries()) {
    const t = target.get(system);
    if (!t) continue;
    for (const code of codes.keys()) {
      t.delete(code);
    }
  }
}
function buildExpansionFromSystemMap(map) {
  const contains = [];
  for (const [system, codes] of map.entries()) {
    for (const [code, display] of codes.entries()) {
      const concept = { system, code };
      if (display !== void 0) {
        concept.display = display;
      }
      contains.push(concept);
    }
  }
  return { contains, total: contains.length };
}

// src/utils/terminology/data/iso-3166-1-codes.json
var iso_3166_1_codes_default = {
  AF: "Afghanistan",
  AX: "\xC5land Islands",
  AL: "Albania",
  DZ: "Algeria",
  AS: "American Samoa",
  AD: "Andorra",
  AO: "Angola",
  AI: "Anguilla",
  AQ: "Antarctica",
  AG: "Antigua and Barbuda",
  AR: "Argentina",
  AM: "Armenia",
  AW: "Aruba",
  AU: "Australia",
  AT: "Austria",
  AZ: "Azerbaijan",
  BS: "Bahamas",
  BH: "Bahrain",
  BD: "Bangladesh",
  BB: "Barbados",
  BY: "Belarus",
  BE: "Belgium",
  BZ: "Belize",
  BJ: "Benin",
  BM: "Bermuda",
  BT: "Bhutan",
  BO: "Bolivia (Plurinational State of)",
  BQ: "Bonaire, Sint Eustatius and Saba",
  BA: "Bosnia and Herzegovina",
  BW: "Botswana",
  BV: "Bouvet Island",
  BR: "Brazil",
  IO: "British Indian Ocean Territory",
  BN: "Brunei Darussalam",
  BG: "Bulgaria",
  BF: "Burkina Faso",
  BI: "Burundi",
  CV: "Cabo Verde",
  KH: "Cambodia",
  CM: "Cameroon",
  CA: "Canada",
  KY: "Cayman Islands",
  CF: "Central African Republic",
  TD: "Chad",
  CL: "Chile",
  CN: "China",
  CX: "Christmas Island",
  CC: "Cocos (Keeling) Islands",
  CO: "Colombia",
  KM: "Comoros",
  CG: "Congo",
  CD: "Congo, Democratic Republic of the",
  CK: "Cook Islands",
  CR: "Costa Rica",
  CI: "C\xF4te d'Ivoire",
  HR: "Croatia",
  CU: "Cuba",
  CW: "Cura\xE7ao",
  CY: "Cyprus",
  CZ: "Czechia",
  DK: "Denmark",
  DJ: "Djibouti",
  DM: "Dominica",
  DO: "Dominican Republic",
  EC: "Ecuador",
  EG: "Egypt",
  SV: "El Salvador",
  GQ: "Equatorial Guinea",
  ER: "Eritrea",
  EE: "Estonia",
  SZ: "Eswatini",
  ET: "Ethiopia",
  FK: "Falkland Islands (Malvinas)",
  FO: "Faroe Islands",
  FJ: "Fiji",
  FI: "Finland",
  FR: "France",
  GF: "French Guiana",
  PF: "French Polynesia",
  TF: "French Southern Territories",
  GA: "Gabon",
  GM: "Gambia",
  GE: "Georgia",
  DE: "Germany",
  GH: "Ghana",
  GI: "Gibraltar",
  GR: "Greece",
  GL: "Greenland",
  GD: "Grenada",
  GP: "Guadeloupe",
  GU: "Guam",
  GT: "Guatemala",
  GG: "Guernsey",
  GN: "Guinea",
  GW: "Guinea-Bissau",
  GY: "Guyana",
  HT: "Haiti",
  HM: "Heard Island and McDonald Islands",
  VA: "Holy See",
  HN: "Honduras",
  HK: "Hong Kong",
  HU: "Hungary",
  IS: "Iceland",
  IN: "India",
  ID: "Indonesia",
  IR: "Iran (Islamic Republic of)",
  IQ: "Iraq",
  IE: "Ireland",
  IM: "Isle of Man",
  IL: "Israel",
  IT: "Italy",
  JM: "Jamaica",
  JP: "Japan",
  JE: "Jersey",
  JO: "Jordan",
  KZ: "Kazakhstan",
  KE: "Kenya",
  KI: "Kiribati",
  KP: "Korea (Democratic People's Republic of)",
  KR: "Korea, Republic of",
  KW: "Kuwait",
  KG: "Kyrgyzstan",
  LA: "Lao People's Democratic Republic",
  LV: "Latvia",
  LB: "Lebanon",
  LS: "Lesotho",
  LR: "Liberia",
  LY: "Libya",
  LI: "Liechtenstein",
  LT: "Lithuania",
  LU: "Luxembourg",
  MO: "Macao",
  MG: "Madagascar",
  MW: "Malawi",
  MY: "Malaysia",
  MV: "Maldives",
  ML: "Mali",
  MT: "Malta",
  MH: "Marshall Islands",
  MQ: "Martinique",
  MR: "Mauritania",
  MU: "Mauritius",
  YT: "Mayotte",
  MX: "Mexico",
  FM: "Micronesia (Federated States of)",
  MD: "Moldova, Republic of",
  MC: "Monaco",
  MN: "Mongolia",
  ME: "Montenegro",
  MS: "Montserrat",
  MA: "Morocco",
  MZ: "Mozambique",
  MM: "Myanmar",
  NA: "Namibia",
  NR: "Nauru",
  NP: "Nepal",
  NL: "Netherlands",
  NC: "New Caledonia",
  NZ: "New Zealand",
  NI: "Nicaragua",
  NE: "Niger",
  NG: "Nigeria",
  NU: "Niue",
  NF: "Norfolk Island",
  MK: "North Macedonia",
  MP: "Northern Mariana Islands",
  NO: "Norway",
  OM: "Oman",
  PK: "Pakistan",
  PW: "Palau",
  PS: "Palestine, State of",
  PA: "Panama",
  PG: "Papua New Guinea",
  PY: "Paraguay",
  PE: "Peru",
  PH: "Philippines",
  PN: "Pitcairn",
  PL: "Poland",
  PT: "Portugal",
  PR: "Puerto Rico",
  QA: "Qatar",
  RE: "R\xE9union",
  RO: "Romania",
  RU: "Russian Federation",
  RW: "Rwanda",
  BL: "Saint Barth\xE9lemy",
  SH: "Saint Helena, Ascension and Tristan da Cunha",
  KN: "Saint Kitts and Nevis",
  LC: "Saint Lucia",
  MF: "Saint Martin (French part)",
  PM: "Saint Pierre and Miquelon",
  VC: "Saint Vincent and the Grenadines",
  WS: "Samoa",
  SM: "San Marino",
  ST: "Sao Tome and Principe",
  SA: "Saudi Arabia",
  SN: "Senegal",
  RS: "Serbia",
  SC: "Seychelles",
  SL: "Sierra Leone",
  SG: "Singapore",
  SX: "Sint Maarten (Dutch part)",
  SK: "Slovakia",
  SI: "Slovenia",
  SB: "Solomon Islands",
  SO: "Somalia",
  ZA: "South Africa",
  GS: "South Georgia and the South Sandwich Islands",
  SS: "South Sudan",
  ES: "Spain",
  LK: "Sri Lanka",
  SD: "Sudan",
  SR: "Suriname",
  SJ: "Svalbard and Jan Mayen",
  SE: "Sweden",
  CH: "Switzerland",
  SY: "Syrian Arab Republic",
  TW: "Taiwan, Province of China",
  TJ: "Tajikistan",
  TZ: "Tanzania, United Republic of",
  TH: "Thailand",
  TL: "Timor-Leste",
  TG: "Togo",
  TK: "Tokelau",
  TO: "Tonga",
  TT: "Trinidad and Tobago",
  TN: "Tunisia",
  TR: "Turkey",
  TM: "Turkmenistan",
  TC: "Turks and Caicos Islands",
  TV: "Tuvalu",
  UG: "Uganda",
  UA: "Ukraine",
  AE: "United Arab Emirates",
  GB: "United Kingdom of Great Britain and Northern Ireland",
  US: "United States of America",
  UM: "United States Minor Outlying Islands",
  UY: "Uruguay",
  UZ: "Uzbekistan",
  VU: "Vanuatu",
  VE: "Venezuela (Bolivarian Republic of)",
  VN: "Viet Nam",
  VG: "Virgin Islands (British)",
  VI: "Virgin Islands (U.S.)",
  WF: "Wallis and Futuna",
  EH: "Western Sahara",
  YE: "Yemen",
  ZM: "Zambia",
  ZW: "Zimbabwe",
  AFG: "Afghanistan",
  ALA: "\xC5land Islands",
  ALB: "Albania",
  DZA: "Algeria",
  ASM: "American Samoa",
  AND: "Andorra",
  AGO: "Angola",
  AIA: "Anguilla",
  ATA: "Antarctica",
  ATG: "Antigua and Barbuda",
  ARG: "Argentina",
  ARM: "Armenia",
  ABW: "Aruba",
  AUS: "Australia",
  AUT: "Austria",
  AZE: "Azerbaijan",
  BHS: "Bahamas",
  BHR: "Bahrain",
  BGD: "Bangladesh",
  BRB: "Barbados",
  BLR: "Belarus",
  BEL: "Belgium",
  BLZ: "Belize",
  BEN: "Benin",
  BMU: "Bermuda",
  BTN: "Bhutan",
  BOL: "Bolivia (Plurinational State of)",
  BES: "Bonaire, Sint Eustatius and Saba",
  BIH: "Bosnia and Herzegovina",
  BWA: "Botswana",
  BVT: "Bouvet Island",
  BRA: "Brazil",
  IOT: "British Indian Ocean Territory",
  BRN: "Brunei Darussalam",
  BGR: "Bulgaria",
  BFA: "Burkina Faso",
  BDI: "Burundi",
  CPV: "Cabo Verde",
  KHM: "Cambodia",
  CMR: "Cameroon",
  CAN: "Canada",
  CYM: "Cayman Islands",
  CAF: "Central African Republic",
  TCD: "Chad",
  CHL: "Chile",
  CHN: "China",
  CXR: "Christmas Island",
  CCK: "Cocos (Keeling) Islands",
  COL: "Colombia",
  COM: "Comoros",
  COG: "Congo",
  COD: "Congo, Democratic Republic of the",
  COK: "Cook Islands",
  CRI: "Costa Rica",
  CIV: "C\xF4te d'Ivoire",
  HRV: "Croatia",
  CUB: "Cuba",
  CUW: "Cura\xE7ao",
  CYP: "Cyprus",
  CZE: "Czechia",
  DNK: "Denmark",
  DJI: "Djibouti",
  DMA: "Dominica",
  DOM: "Dominican Republic",
  ECU: "Ecuador",
  EGY: "Egypt",
  SLV: "El Salvador",
  GNQ: "Equatorial Guinea",
  ERI: "Eritrea",
  EST: "Estonia",
  SWZ: "Eswatini",
  ETH: "Ethiopia",
  FLK: "Falkland Islands (Malvinas)",
  FRO: "Faroe Islands",
  FJI: "Fiji",
  FIN: "Finland",
  FRA: "France",
  GUF: "French Guiana",
  PYF: "French Polynesia",
  ATF: "French Southern Territories",
  GAB: "Gabon",
  GMB: "Gambia",
  GEO: "Georgia",
  DEU: "Germany",
  GHA: "Ghana",
  GIB: "Gibraltar",
  GRC: "Greece",
  GRL: "Greenland",
  GRD: "Grenada",
  GLP: "Guadeloupe",
  GUM: "Guam",
  GTM: "Guatemala",
  GGY: "Guernsey",
  GIN: "Guinea",
  GNB: "Guinea-Bissau",
  GUY: "Guyana",
  HTI: "Haiti",
  HMD: "Heard Island and McDonald Islands",
  VAT: "Holy See",
  HND: "Honduras",
  HKG: "Hong Kong",
  HUN: "Hungary",
  ISL: "Iceland",
  IND: "India",
  IDN: "Indonesia",
  IRN: "Iran (Islamic Republic of)",
  IRQ: "Iraq",
  IRL: "Ireland",
  IMN: "Isle of Man",
  ISR: "Israel",
  ITA: "Italy",
  JAM: "Jamaica",
  JPN: "Japan",
  JEY: "Jersey",
  JOR: "Jordan",
  KAZ: "Kazakhstan",
  KEN: "Kenya",
  KIR: "Kiribati",
  PRK: "Korea (Democratic People's Republic of)",
  KOR: "Korea, Republic of",
  KWT: "Kuwait",
  KGZ: "Kyrgyzstan",
  LAO: "Lao People's Democratic Republic",
  LVA: "Latvia",
  LBN: "Lebanon",
  LSO: "Lesotho",
  LBR: "Liberia",
  LBY: "Libya",
  LIE: "Liechtenstein",
  LTU: "Lithuania",
  LUX: "Luxembourg",
  MAC: "Macao",
  MDG: "Madagascar",
  MWI: "Malawi",
  MYS: "Malaysia",
  MDV: "Maldives",
  MLI: "Mali",
  MLT: "Malta",
  MHL: "Marshall Islands",
  MTQ: "Martinique",
  MRT: "Mauritania",
  MUS: "Mauritius",
  MYT: "Mayotte",
  MEX: "Mexico",
  FSM: "Micronesia (Federated States of)",
  MDA: "Moldova, Republic of",
  MCO: "Monaco",
  MNG: "Mongolia",
  MNE: "Montenegro",
  MSR: "Montserrat",
  MAR: "Morocco",
  MOZ: "Mozambique",
  MMR: "Myanmar",
  NAM: "Namibia",
  NRU: "Nauru",
  NPL: "Nepal",
  NLD: "Netherlands",
  NCL: "New Caledonia",
  NZL: "New Zealand",
  NIC: "Nicaragua",
  NER: "Niger",
  NGA: "Nigeria",
  NIU: "Niue",
  NFK: "Norfolk Island",
  MKD: "North Macedonia",
  MNP: "Northern Mariana Islands",
  NOR: "Norway",
  OMN: "Oman",
  PAK: "Pakistan",
  PLW: "Palau",
  PSE: "Palestine, State of",
  PAN: "Panama",
  PNG: "Papua New Guinea",
  PRY: "Paraguay",
  PER: "Peru",
  PHL: "Philippines",
  PCN: "Pitcairn",
  POL: "Poland",
  PRT: "Portugal",
  PRI: "Puerto Rico",
  QAT: "Qatar",
  REU: "R\xE9union",
  ROU: "Romania",
  RUS: "Russian Federation",
  RWA: "Rwanda",
  BLM: "Saint Barth\xE9lemy",
  SHN: "Saint Helena, Ascension and Tristan da Cunha",
  KNA: "Saint Kitts and Nevis",
  LCA: "Saint Lucia",
  MAF: "Saint Martin (French part)",
  SPM: "Saint Pierre and Miquelon",
  VCT: "Saint Vincent and the Grenadines",
  WSM: "Samoa",
  SMR: "San Marino",
  STP: "Sao Tome and Principe",
  SAU: "Saudi Arabia",
  SEN: "Senegal",
  SRB: "Serbia",
  SYC: "Seychelles",
  SLE: "Sierra Leone",
  SGP: "Singapore",
  SXM: "Sint Maarten (Dutch part)",
  SVK: "Slovakia",
  SVN: "Slovenia",
  SLB: "Solomon Islands",
  SOM: "Somalia",
  ZAF: "South Africa",
  SGS: "South Georgia and the South Sandwich Islands",
  SSD: "South Sudan",
  ESP: "Spain",
  LKA: "Sri Lanka",
  SDN: "Sudan",
  SUR: "Suriname",
  SJM: "Svalbard and Jan Mayen",
  SWE: "Sweden",
  CHE: "Switzerland",
  SYR: "Syrian Arab Republic",
  TWN: "Taiwan, Province of China",
  TJK: "Tajikistan",
  TZA: "Tanzania, United Republic of",
  THA: "Thailand",
  TLS: "Timor-Leste",
  TGO: "Togo",
  TKL: "Tokelau",
  TON: "Tonga",
  TTO: "Trinidad and Tobago",
  TUN: "Tunisia",
  TUR: "Turkey",
  TKM: "Turkmenistan",
  TCA: "Turks and Caicos Islands",
  TUV: "Tuvalu",
  UGA: "Uganda",
  UKR: "Ukraine",
  ARE: "United Arab Emirates",
  GBR: "United Kingdom of Great Britain and Northern Ireland",
  USA: "United States of America",
  UMI: "United States Minor Outlying Islands",
  URY: "Uruguay",
  UZB: "Uzbekistan",
  VUT: "Vanuatu",
  VEN: "Venezuela (Bolivarian Republic of)",
  VNM: "Viet Nam",
  VGB: "Virgin Islands (British)",
  VIR: "Virgin Islands (U.S.)",
  WLF: "Wallis and Futuna",
  ESH: "Western Sahara",
  YEM: "Yemen",
  ZMB: "Zambia",
  ZWE: "Zimbabwe"
};

// src/utils/terminology/data/bcp-47-language-codes.json
var bcp_47_language_codes_default = {
  aa: "Afar",
  ab: "Abkhazian",
  ae: "Avestan",
  af: "Afrikaans",
  ak: "Akan",
  am: "Amharic",
  an: "Aragonese",
  ar: "Arabic",
  as: "Assamese",
  av: "Avaric",
  ay: "Aymara",
  az: "Azerbaijani",
  ba: "Bashkir",
  be: "Belarusian",
  bg: "Bulgarian",
  bh: "Bihari languages",
  bi: "Bislama",
  bm: "Bambara",
  bn: "Bengali",
  bo: "Tibetan",
  br: "Breton",
  bs: "Bosnian",
  ca: "Catalan",
  ce: "Chechen",
  ch: "Chamorro",
  co: "Corsican",
  cr: "Cree",
  cs: "Czech",
  cu: "Church Slavic",
  cv: "Chuvash",
  cy: "Welsh",
  da: "Danish",
  de: "German",
  dv: "Divehi",
  dz: "Dzongkha",
  ee: "Ewe",
  el: "Greek",
  en: "English",
  eo: "Esperanto",
  es: "Spanish",
  et: "Estonian",
  eu: "Basque",
  fa: "Persian",
  ff: "Fulah",
  fi: "Finnish",
  fj: "Fijian",
  fo: "Faroese",
  fr: "French",
  fy: "Western Frisian",
  ga: "Irish",
  gd: "Gaelic",
  gl: "Galician",
  gn: "Guarani",
  gu: "Gujarati",
  gv: "Manx",
  ha: "Hausa",
  he: "Hebrew",
  hi: "Hindi",
  ho: "Hiri Motu",
  hr: "Croatian",
  ht: "Haitian",
  hu: "Hungarian",
  hy: "Armenian",
  hz: "Herero",
  ia: "Interlingua",
  id: "Indonesian",
  ie: "Interlingue",
  ig: "Igbo",
  ii: "Sichuan Yi",
  ik: "Inupiaq",
  io: "Ido",
  is: "Icelandic",
  it: "Italian",
  iu: "Inuktitut",
  ja: "Japanese",
  jv: "Javanese",
  ka: "Georgian",
  kg: "Kongo",
  ki: "Kikuyu",
  kj: "Kuanyama",
  kk: "Kazakh",
  kl: "Kalaallisut",
  km: "Central Khmer",
  kn: "Kannada",
  ko: "Korean",
  kr: "Kanuri",
  ks: "Kashmiri",
  ku: "Kurdish",
  kv: "Komi",
  kw: "Cornish",
  ky: "Kirghiz",
  la: "Latin",
  lb: "Luxembourgish",
  lg: "Ganda",
  li: "Limburgan",
  ln: "Lingala",
  lo: "Lao",
  lt: "Lithuanian",
  lu: "Luba-Katanga",
  lv: "Latvian",
  mg: "Malagasy",
  mh: "Marshallese",
  mi: "Maori",
  mk: "Macedonian",
  ml: "Malayalam",
  mn: "Mongolian",
  mr: "Marathi",
  ms: "Malay",
  mt: "Maltese",
  my: "Burmese",
  na: "Nauru",
  nb: "Norwegian Bokm\xE5l",
  nd: "North Ndebele",
  ne: "Nepali",
  ng: "Ndonga",
  nl: "Dutch",
  nn: "Norwegian Nynorsk",
  no: "Norwegian",
  nr: "South Ndebele",
  nv: "Navajo",
  ny: "Chichewa",
  oc: "Occitan",
  oj: "Ojibwa",
  om: "Oromo",
  or: "Oriya",
  os: "Ossetian",
  pa: "Panjabi",
  pi: "Pali",
  pl: "Polish",
  ps: "Pushto",
  pt: "Portuguese",
  qu: "Quechua",
  rm: "Romansh",
  rn: "Rundi",
  ro: "Romanian",
  ru: "Russian",
  rw: "Kinyarwanda",
  sa: "Sanskrit",
  sc: "Sardinian",
  sd: "Sindhi",
  se: "Northern Sami",
  sg: "Sango",
  si: "Sinhala",
  sk: "Slovak",
  sl: "Slovenian",
  sm: "Samoan",
  sn: "Shona",
  so: "Somali",
  sq: "Albanian",
  sr: "Serbian",
  ss: "Swati",
  st: "Southern Sotho",
  su: "Sundanese",
  sv: "Swedish",
  sw: "Swahili",
  ta: "Tamil",
  te: "Telugu",
  tg: "Tajik",
  th: "Thai",
  ti: "Tigrinya",
  tk: "Turkmen",
  tl: "Tagalog",
  tn: "Tswana",
  to: "Tonga",
  tr: "Turkish",
  ts: "Tsonga",
  tt: "Tatar",
  tw: "Twi",
  ty: "Tahitian",
  ug: "Uighur",
  uk: "Ukrainian",
  ur: "Urdu",
  uz: "Uzbek",
  ve: "Venda",
  vi: "Vietnamese",
  vo: "Volap\xFCk",
  wa: "Walloon",
  wo: "Wolof",
  xh: "Xhosa",
  yi: "Yiddish",
  yo: "Yoruba",
  za: "Zhuang",
  zh: "Chinese",
  zu: "Zulu",
  "en-AU": "English (Australia)",
  "en-CA": "English (Canada)",
  "en-GB": "English (United Kingdom)",
  "en-IE": "English (Ireland)",
  "en-IN": "English (India)",
  "en-NZ": "English (New Zealand)",
  "en-SG": "English (Singapore)",
  "en-US": "English (United States)",
  "en-ZA": "English (South Africa)",
  "es-AR": "Spanish (Argentina)",
  "es-BO": "Spanish (Bolivia)",
  "es-CL": "Spanish (Chile)",
  "es-CO": "Spanish (Colombia)",
  "es-CR": "Spanish (Costa Rica)",
  "es-CU": "Spanish (Cuba)",
  "es-DO": "Spanish (Dominican Republic)",
  "es-EC": "Spanish (Ecuador)",
  "es-ES": "Spanish (Spain)",
  "es-GT": "Spanish (Guatemala)",
  "es-HN": "Spanish (Honduras)",
  "es-MX": "Spanish (Mexico)",
  "es-NI": "Spanish (Nicaragua)",
  "es-PA": "Spanish (Panama)",
  "es-PE": "Spanish (Peru)",
  "es-PR": "Spanish (Puerto Rico)",
  "es-PY": "Spanish (Paraguay)",
  "es-SV": "Spanish (El Salvador)",
  "es-UY": "Spanish (Uruguay)",
  "es-VE": "Spanish (Venezuela)",
  "fr-BE": "French (Belgium)",
  "fr-CA": "French (Canada)",
  "fr-CH": "French (Switzerland)",
  "fr-FR": "French (France)",
  "fr-LU": "French (Luxembourg)",
  "fr-MC": "French (Monaco)",
  "de-AT": "German (Austria)",
  "de-CH": "German (Switzerland)",
  "de-DE": "German (Germany)",
  "de-LI": "German (Liechtenstein)",
  "de-LU": "German (Luxembourg)",
  "it-CH": "Italian (Switzerland)",
  "it-IT": "Italian (Italy)",
  "pt-BR": "Portuguese (Brazil)",
  "pt-PT": "Portuguese (Portugal)",
  "zh-CN": "Chinese (China)",
  "zh-HK": "Chinese (Hong Kong SAR China)",
  "zh-MO": "Chinese (Macao SAR China)",
  "zh-SG": "Chinese (Singapore)",
  "zh-TW": "Chinese (Taiwan)",
  "ar-AE": "Arabic (United Arab Emirates)",
  "ar-BH": "Arabic (Bahrain)",
  "ar-DZ": "Arabic (Algeria)",
  "ar-EG": "Arabic (Egypt)",
  "ar-IQ": "Arabic (Iraq)",
  "ar-JO": "Arabic (Jordan)",
  "ar-KW": "Arabic (Kuwait)",
  "ar-LB": "Arabic (Lebanon)",
  "ar-LY": "Arabic (Libya)",
  "ar-MA": "Arabic (Morocco)",
  "ar-OM": "Arabic (Oman)",
  "ar-QA": "Arabic (Qatar)",
  "ar-SA": "Arabic (Saudi Arabia)",
  "ar-SY": "Arabic (Syria)",
  "ar-TN": "Arabic (Tunisia)",
  "ar-YE": "Arabic (Yemen)",
  "ru-BY": "Russian (Belarus)",
  "ru-KG": "Russian (Kyrgyzstan)",
  "ru-KZ": "Russian (Kazakhstan)",
  "ru-MD": "Russian (Moldova)",
  "ru-RU": "Russian (Russia)",
  "ru-UA": "Russian (Ukraine)",
  "hi-IN": "Hindi (India)",
  "ja-JP": "Japanese (Japan)",
  "ko-KR": "Korean (South Korea)",
  "ko-KP": "Korean (North Korea)",
  "sv-FI": "Swedish (Finland)",
  "sv-SE": "Swedish (Sweden)",
  "da-DK": "Danish (Denmark)",
  "da-GL": "Danish (Greenland)",
  "no-NO": "Norwegian (Norway)",
  "nb-NO": "Norwegian Bokm\xE5l (Norway)",
  "nn-NO": "Norwegian Nynorsk (Norway)",
  "fi-FI": "Finnish (Finland)",
  "is-IS": "Icelandic (Iceland)",
  "et-EE": "Estonian (Estonia)",
  "lv-LV": "Latvian (Latvia)",
  "lt-LT": "Lithuanian (Lithuania)",
  "pl-PL": "Polish (Poland)",
  "cs-CZ": "Czech (Czech Republic)",
  "sk-SK": "Slovak (Slovakia)",
  "hu-HU": "Hungarian (Hungary)",
  "ro-RO": "Romanian (Romania)",
  "bg-BG": "Bulgarian (Bulgaria)",
  "hr-HR": "Croatian (Croatia)",
  "sr-RS": "Serbian (Serbia)",
  "bs-BA": "Bosnian (Bosnia and Herzegovina)",
  "mk-MK": "Macedonian (Macedonia)",
  "sl-SI": "Slovenian (Slovenia)",
  "sq-AL": "Albanian (Albania)",
  "el-GR": "Greek (Greece)",
  "tr-TR": "Turkish (Turkey)",
  "he-IL": "Hebrew (Israel)",
  "fa-IR": "Persian (Iran)",
  "ur-PK": "Urdu (Pakistan)",
  "bn-BD": "Bengali (Bangladesh)",
  "bn-IN": "Bengali (India)",
  "th-TH": "Thai (Thailand)",
  "vi-VN": "Vietnamese (Vietnam)",
  "ms-MY": "Malay (Malaysia)",
  "id-ID": "Indonesian (Indonesia)",
  "tl-PH": "Tagalog (Philippines)",
  "sw-KE": "Swahili (Kenya)",
  "sw-TZ": "Swahili (Tanzania)",
  "am-ET": "Amharic (Ethiopia)",
  "yo-NG": "Yoruba (Nigeria)",
  "ig-NG": "Igbo (Nigeria)",
  "ha-NG": "Hausa (Nigeria)",
  "af-ZA": "Afrikaans (South Africa)",
  "zu-ZA": "Zulu (South Africa)",
  "xh-ZA": "Xhosa (South Africa)"
};

// src/utils/terminology/implicitCodeSystems.ts
var Iso3166CountryCodesProvider = class {
  constructor() {
    this.canonicalUrl = "urn:iso:std:iso:3166";
  }
  getConcepts() {
    if (!this.conceptsCache) {
      this.conceptsCache = /* @__PURE__ */ new Map();
      for (const [code, name] of Object.entries(iso_3166_1_codes_default)) {
        this.conceptsCache.set(code, name);
      }
    }
    return this.conceptsCache;
  }
};
var Bcp47LanguageCodesProvider = class {
  constructor() {
    this.canonicalUrl = "urn:ietf:bcp:47";
  }
  getConcepts() {
    if (!this.conceptsCache) {
      this.conceptsCache = /* @__PURE__ */ new Map();
      for (const [code, display] of Object.entries(bcp_47_language_codes_default)) {
        const normalizedCode = this.normalizeBcp47Code(code);
        this.conceptsCache.set(normalizedCode, display);
        if (normalizedCode !== code) {
          this.conceptsCache.set(code, display);
        }
        if (code.includes("-")) {
          const underscoreVariant = code.replace(/-/g, "_");
          this.conceptsCache.set(underscoreVariant, display);
        }
      }
    }
    return this.conceptsCache;
  }
  normalizeBcp47Code(code) {
    if (!code.includes("-")) {
      return code.toLowerCase();
    }
    const parts = code.split("-");
    const language = parts[0].toLowerCase();
    const region = parts[1].toUpperCase();
    return `${language}-${region}`;
  }
};
var ImplicitCodeSystemRegistry = class {
  static {
    this.providers = /* @__PURE__ */ new Map([
      ["urn:iso:std:iso:3166", new Iso3166CountryCodesProvider()],
      ["urn:ietf:bcp:47", new Bcp47LanguageCodesProvider()]
    ]);
  }
  /**
   * Check if a canonical URL corresponds to an implicit code system
   */
  static isImplicitCodeSystem(canonicalUrl) {
    return this.providers.has(canonicalUrl);
  }
  /**
   * Get the provider for an implicit code system
   */
  static getProvider(canonicalUrl) {
    return this.providers.get(canonicalUrl);
  }
  /**
   * Get all concepts for an implicit code system
   */
  static getConcepts(canonicalUrl) {
    const provider = this.getProvider(canonicalUrl);
    return provider?.getConcepts();
  }
  /**
   * Get all supported implicit code system URLs
   */
  static getSupportedSystems() {
    return Array.from(this.providers.keys());
  }
};
var FhirSnapshotGenerator = class _FhirSnapshotGenerator {
  // cache for resolved base packages
  constructor(fpe, cacheMode, fhirVersion, logger) {
    this.resolvedBasePackages = /* @__PURE__ */ new Map();
    if (logger) {
      this.logger = logger;
      this.prethrow = customPrethrower(this.logger);
    } else {
      this.logger = defaultLogger;
      this.prethrow = defaultPrethrow;
    }
    this.cacheMode = cacheMode;
    this.fhirVersion = fhirVersion;
    this.fhirCorePackage = resolveFhirVersion(fhirVersion, true);
    this.fpe = fpe;
    this.cachePath = fpe.getCachePath();
  }
  /**
   * Creates a new instance of the FhirSnapshotGenerator class.
   * @param config - the configuration object for the FhirPackageExplorer
   * @returns - a promise that resolves to a new instance of the FhirSnapshotGenerator class
   */
  static async create(config) {
    const logger = config.logger || defaultLogger;
    const prethrow = config.logger ? customPrethrower(logger) : defaultPrethrow;
    try {
      const cacheMode = config.cacheMode || "lazy";
      const fhirVersion = resolveFhirVersion(config.fhirVersion || "4.0.1");
      const fpeConfig = { ...config, skipExamples: true };
      delete fpeConfig.cacheMode;
      delete fpeConfig.fhirVersion;
      let fpe = await fhirPackageExplorer.FhirPackageExplorer.create(fpeConfig);
      const packagesInContext = fpe.getContextPackages();
      const fhirCorePackage = resolveFhirVersion(fhirVersion, true);
      const hasCorePackage = packagesInContext.some((pkg) => pkg.id.match(/^hl7\.fhir\.r[0-9]+\.core$/));
      if (!hasCorePackage) {
        logger.warn(`No FHIR core package found in the context. Adding: ${fhirCorePackage.id}@${fhirCorePackage.version}.`);
        fpeConfig.context = [...fpeConfig.context, fhirCorePackage];
        fpe = await fhirPackageExplorer.FhirPackageExplorer.create(fpeConfig);
      }
      ;
      const fsg = new _FhirSnapshotGenerator(fpe, cacheMode, fhirVersion, config.logger);
      let precache = false;
      if (cacheMode === "rebuild") {
        precache = true;
        const packageList = fpe.getContextPackages().map((pkg) => path__default.default.join(fpe.getCachePath(), `${pkg.id}#${pkg.version}`, ".fsg.snapshots", versionedCacheDir));
        for (const snapshotCacheDir of packageList) {
          if (await fs__default.default.exists(snapshotCacheDir)) {
            fs__default.default.removeSync(snapshotCacheDir);
          }
        }
      }
      if (cacheMode === "ensure") precache = true;
      if (precache) {
        logger.info(`Pre-caching snapshots in '${cacheMode}' mode...`);
        const allSds = await fpe.lookupMeta({ resourceType: "StructureDefinition", derivation: "constraint" });
        const errors = [];
        for (const sd of allSds) {
          const { filename, __packageId: packageId, __packageVersion: packageVersion, url } = sd;
          try {
            await fsg.ensureSnapshotCached(filename, packageId, packageVersion);
          } catch (e) {
            errors.push(
              `Failed to ${cacheMode} snapshot for '${url}' in package '${packageId}@${packageVersion}': ${e instanceof Error ? e.message : String(e)}`
            );
          }
        }
        if (errors.length > 0) {
          logger.error(`Errors during pre-caching snapshots (${errors.length} total):
${errors.join("\n")}`);
        } else {
          logger.info(`Pre-caching snapshots in '${cacheMode}' mode completed successfully.`);
        }
        logger.info(`Pre-caching ValueSet expansions in '${cacheMode}' mode...`);
        const vsErrors = [];
        const allVs = await fpe.lookupMeta({ resourceType: "ValueSet" });
        for (const vs of allVs) {
          const { filename, __packageId: packageId, __packageVersion: packageVersion, url } = vs;
          try {
            await fsg.ensureExpansionCached(filename, packageId, packageVersion);
          } catch (e) {
            vsErrors.push(`Failed to ${cacheMode} expansion for '${url || filename}' in package '${packageId}@${packageVersion}': ${e instanceof Error ? e.message : String(e)}`);
          }
        }
        if (vsErrors.length > 0) {
          logger.warn(`Errors during pre-caching ValueSet expansions (${vsErrors.length} total):
${vsErrors.join("\n")}`);
        } else {
          logger.info(`Pre-caching ValueSet expansions in '${cacheMode}' mode completed successfully.`);
        }
      }
      return fsg;
    } catch (e) {
      throw prethrow(e);
    }
  }
  getLogger() {
    return this.logger;
  }
  getCachePath() {
    return this.cachePath;
  }
  getCacheMode() {
    return this.cacheMode;
  }
  getFhirVersion() {
    return this.fhirVersion;
  }
  getFpe() {
    return this.fpe;
  }
  /**
   * Get the core FHIR package for a specific FHIR package.
   * Will try to resolve the core package based on the direct dependencies of the source package or its fhirVersions array.
   * Defaults to the FHIR package of this instance's fhirVersion if no base package can be determined.
   * @param sourcePackage The source package identifier (e.g., { id: 'hl7.fhir.us.core', version: '6.1.0' }).
   * @returns The core FHIR package identifier (e.g., { id: 'hl7.fhir.r4.core', version: '4.0.1' }).
   */
  async getCorePackage(sourcePackage) {
    let baseFhirPackage = this.resolvedBasePackages.get(`${sourcePackage.id}@${sourcePackage.version}`);
    if (!baseFhirPackage) {
      try {
        baseFhirPackage = await resolveBasePackage(sourcePackage.id, sourcePackage.version, this.fpe, this.logger);
      } catch (e) {
        this.logger.warn(`Failed to resolve base FHIR package for '${sourcePackage.id}@${sourcePackage.version}': ${e instanceof Error ? e.message : String(e)}`);
      }
    }
    if (!baseFhirPackage) {
      this.logger.warn(`Defaulting to core package ${this.fhirCorePackage.id}@${this.fhirCorePackage.version} for resolving FHIR types within '${sourcePackage.id}@${sourcePackage.version}'.`);
      baseFhirPackage = `${this.fhirCorePackage.id}@${this.fhirCorePackage.version}`;
    }
    if (!baseFhirPackage) {
      throw new Error(`No base FHIR package found for '${sourcePackage.id}@${sourcePackage.version}'.`);
    }
    this.resolvedBasePackages.set(`${sourcePackage.id}@${sourcePackage.version}`, baseFhirPackage);
    const [id, version2] = baseFhirPackage.split("@");
    return { id, version: version2 };
  }
  /**
   * Get an original StructureDefinition from a specific package by filename.
   * After resolving the metadata using any other identifier (id, url, name), filename (combind with the package id and version)
   * is the most reliable way to get to a specific StructureDefinition, and is also used as the basis for the cache.
   */
  async getStructureDefinitionByFileName(filename, packageId, packageVersion) {
    return await this.fpe.resolve({
      filename,
      package: {
        id: packageId,
        version: packageVersion
      }
    });
  }
  getCacheFilePath(filename, packageId, packageVersion) {
    return path__default.default.join(this.cachePath, `${packageId}#${packageVersion}`, ".fsg.snapshots", versionedCacheDir, filename);
  }
  /**
   * Try to get an existing cached StructureDefinition snapshot. If not found, return undefined.
   */
  async getSnapshotFromCache(filename, packageId, packageVersion) {
    const cacheFilePath = this.getCacheFilePath(filename, packageId, packageVersion);
    if (await fs__default.default.exists(cacheFilePath)) {
      return await fs__default.default.readJSON(cacheFilePath);
    } else {
      return void 0;
    }
  }
  async saveSnapshotToCache(filename, packageId, packageVersion, snapshot) {
    const cacheFilePath = this.getCacheFilePath(filename, packageId, packageVersion);
    await fs__default.default.ensureDir(path__default.default.dirname(cacheFilePath));
    await fs__default.default.writeJSON(cacheFilePath, snapshot);
  }
  /**
   * Fetch StructureDefinition metadata by any identifier (id, url, name) - FSH style.
   */
  async getMetadata(identifier, packageFilter) {
    const errors = [];
    if (identifier.startsWith("http:") || identifier.startsWith("https:") || identifier.includes(":")) {
      try {
        const match = await this.fpe.resolveMeta({ resourceType: "StructureDefinition", url: identifier, package: packageFilter });
        return match;
      } catch (e) {
        errors.push(e);
      }
    }
    try {
      const match = await this.fpe.resolveMeta({ resourceType: "StructureDefinition", id: identifier, package: packageFilter });
      return match;
    } catch (e) {
      errors.push(e);
    }
    try {
      const match = await this.fpe.resolveMeta({ resourceType: "StructureDefinition", name: identifier, package: packageFilter });
      return match;
    } catch (e) {
      errors.push(e);
    }
    errors.map((e) => this.logger.error(e));
    throw new Error(`Failed to resolve StructureDefinition '${identifier}'`);
  }
  /**
   * Generate a snapshot for a StructureDefinition.
   */
  async generate(filename, packageId, packageVersion) {
    this.logger.info(`Generating snapshot for '${filename}' in package '${packageId}@${packageVersion}'...`);
    const sd = await this.getStructureDefinitionByFileName(filename, packageId, packageVersion);
    if (!sd) {
      throw new Error(`File '${filename}' not found in package '${packageId}@${packageVersion}'`);
    }
    if (!sd.baseDefinition) {
      throw new Error(`StructureDefinition '${sd?.url}' does not have a baseDefinition`);
    }
    const baseFhirPackage = await this.getCorePackage({ id: packageId, version: packageVersion });
    const snapshotFetcher = (async (url) => {
      let metadata;
      try {
        metadata = await this.fpe.resolveMeta({ resourceType: "StructureDefinition", url, package: {
          id: packageId,
          version: packageVersion
        } });
      } catch (e) {
        this.logger.warn(`Failed to resolve metadata for '${url}' in package '${packageId}@${packageVersion}': ${e instanceof Error ? e.message : String(e)}`);
        metadata = await this.fpe.resolveMeta({ resourceType: "StructureDefinition", url });
      }
      return (await this.getSnapshotByMeta(metadata)).snapshot?.element;
    }).bind(this);
    const fetcher = new DefinitionFetcher5(
      {
        id: packageId,
        version: packageVersion
      },
      baseFhirPackage,
      this.fpe,
      snapshotFetcher
    );
    const diffs = sd.differential?.element;
    if (!diffs || diffs.length === 0) {
      throw new Error(`StructureDefinition '${filename}' does not have a differential`);
    }
    let baseSnapshot;
    try {
      baseSnapshot = await snapshotFetcher(sd.baseDefinition);
      if (!baseSnapshot || baseSnapshot.length === 0) {
        throw new Error(`Base definition '${sd.baseDefinition}' does not have a snapshot`);
      }
      const migratedBaseSnapshot = migrateElements(baseSnapshot, sd.baseDefinition);
      const generated = await applyDiffs(migratedBaseSnapshot, diffs, fetcher, this.logger);
      return { __corePackage: baseFhirPackage, ...sd, snapshot: { element: generated } };
    } catch (e) {
      this.logger.warn(`Failed to generate snapshot for '${sd.url}': ${e instanceof Error ? e.message : String(e)}
Using the original StructureDefinition from source package.`);
      if (!sd.snapshot || !sd.snapshot.element || sd.snapshot.element.length === 0) {
        throw new Error(`The original StructureDefinition '${sd.url}' does not have a snapshot`);
      }
      return { __corePackage: baseFhirPackage, ...sd };
    }
  }
  /**
   * Get snapshot by metadata.
   */
  async getSnapshotByMeta(metadata) {
    const { derivation, filename, __packageId: packageId, __packageVersion: packageVersion } = metadata;
    if (!derivation || derivation === "specialization") {
      const sd = await this.getStructureDefinitionByFileName(filename, packageId, packageVersion);
      const elements = sd?.snapshot?.element;
      if (!elements || elements.length === 0) {
        throw new Error(`StructureDefinition '${metadata.url}' does not have a snapshot`);
      }
      return { __corePackage: { id: packageId, version: packageVersion }, ...sd };
    }
    const cached = this.cacheMode !== "none" ? await this.getSnapshotFromCache(
      filename,
      packageId,
      packageVersion
    ) : void 0;
    if (cached) return cached;
    const generated = await this.generate(filename, packageId, packageVersion);
    if (this.cacheMode !== "none") {
      await this.saveSnapshotToCache(filename, packageId, packageVersion, generated);
    }
    return generated;
  }
  // ValueSet helpers
  async getValueSetByFileName(filename, packageId, packageVersion) {
    return await this.fpe.resolve({ filename, package: { id: packageId, version: packageVersion } });
  }
  async getValueSetMetadata(identifier, packageFilter) {
    const errors = [];
    if (identifier.startsWith("http:") || identifier.startsWith("https:") || identifier.includes(":")) {
      try {
        const match = await this.fpe.resolveMeta({ resourceType: "ValueSet", url: identifier, package: packageFilter });
        return match;
      } catch (e) {
        errors.push(e);
      }
    }
    try {
      const match = await this.fpe.resolveMeta({ resourceType: "ValueSet", id: identifier, package: packageFilter });
      return match;
    } catch (e) {
      errors.push(e);
    }
    try {
      const match = await this.fpe.resolveMeta({ resourceType: "ValueSet", name: identifier, package: packageFilter });
      return match;
    } catch (e) {
      errors.push(e);
    }
    errors.map((e) => this.logger.error(e));
    throw new Error(`Failed to resolve ValueSet '${identifier}'`);
  }
  async getExpansionFromCache(filename, packageId, packageVersion) {
    const cacheFilePath = this.getCacheFilePath(filename, packageId, packageVersion);
    if (await fs__default.default.exists(cacheFilePath)) {
      return await fs__default.default.readJSON(cacheFilePath);
    }
    return void 0;
  }
  async saveExpansionToCache(filename, packageId, packageVersion, vs) {
    const cacheFilePath = this.getCacheFilePath(filename, packageId, packageVersion);
    await fs__default.default.ensureDir(path__default.default.dirname(cacheFilePath));
    await fs__default.default.writeJSON(cacheFilePath, vs);
  }
  // Flatten CodeSystem concepts (collect all nested {code, display})
  flattenCodeSystemConcepts(cs) {
    return flattenCodeSystemConcepts(cs);
  }
  toSystemCodeMapFromContains(contains) {
    return toSystemCodeMapFromContains(contains);
  }
  mergeSystemMaps(target, source) {
    mergeSystemMaps(target, source);
  }
  subtractSystemMaps(target, exclude) {
    subtractSystemMaps(target, exclude);
  }
  buildExpansionFromSystemMap(map) {
    return buildExpansionFromSystemMap(map);
  }
  async expandInclude(include, sourcePackage, visited) {
    if (include.filter && include.filter.length) {
      throw new Error("Unsupported ValueSet.include.filter encountered. Filtering is not implemented yet.");
    }
    const result = /* @__PURE__ */ new Map();
    let vsUnion = /* @__PURE__ */ new Map();
    const referenced = Array.isArray(include.valueSet) ? include.valueSet : include.valueSet ? [include.valueSet] : [];
    for (const vsUrl of referenced) {
      if (typeof vsUrl !== "string") continue;
      if (visited.has(vsUrl)) {
        throw new Error(`Cyclic ValueSet reference detected: '${vsUrl}'.`);
      }
      visited.add(vsUrl);
      let vsMeta;
      try {
        vsMeta = await this.fpe.resolveMeta({ resourceType: "ValueSet", url: vsUrl, package: sourcePackage });
      } catch {
        try {
          vsMeta = await this.fpe.resolveMeta({ resourceType: "ValueSet", url: vsUrl });
        } catch {
          throw new Error(`Referenced ValueSet '${vsUrl}' not found (searched locally, then globally).`);
        }
      }
      const vsExpanded = await this.expandValueSetByMeta(vsMeta, visited);
      const map = this.toSystemCodeMapFromContains(vsExpanded?.expansion?.contains);
      this.mergeSystemMaps(vsUnion, map);
    }
    const hasSystem = typeof include.system === "string" && include.system.length > 0;
    const hasConcepts = Array.isArray(include.concept) && include.concept.length > 0;
    const conceptMap = /* @__PURE__ */ new Map();
    if (hasSystem) {
      const systemUrl = include.system;
      let codesForSystem = /* @__PURE__ */ new Map();
      if (hasConcepts) {
        let csDict;
        const needsCsLookup = include.concept.some((c) => !c?.display);
        if (needsCsLookup) {
          try {
            const cs = await this.resolveCompleteCodeSystem(systemUrl, sourcePackage);
            csDict = this.flattenCodeSystemConcepts(cs);
          } catch (e) {
            if (ImplicitCodeSystemRegistry.isImplicitCodeSystem(systemUrl)) {
              csDict = ImplicitCodeSystemRegistry.getConcepts(systemUrl);
              this.logger.info(`Using implicit code system for '${systemUrl}'`);
            } else {
              this.logger.warn(`CodeSystem lookup failed for '${systemUrl}', display values will be omitted: ${e instanceof Error ? e.message : String(e)}`);
              csDict = void 0;
            }
          }
        }
        for (const c of include.concept) {
          if (!c?.code) continue;
          const display = typeof c.display === "string" ? c.display : csDict?.get(c.code);
          if (!codesForSystem.has(c.code)) codesForSystem.set(c.code, display);
        }
      } else {
        try {
          const cs = await this.resolveCompleteCodeSystem(systemUrl, sourcePackage);
          codesForSystem = this.flattenCodeSystemConcepts(cs);
        } catch (e) {
          if (ImplicitCodeSystemRegistry.isImplicitCodeSystem(systemUrl)) {
            const implicitConcepts = ImplicitCodeSystemRegistry.getConcepts(systemUrl);
            if (implicitConcepts) {
              codesForSystem = implicitConcepts;
              this.logger.info(`Using implicit code system for '${systemUrl}'`);
            } else {
              throw e;
            }
          } else {
            throw e;
          }
        }
      }
      conceptMap.set(systemUrl, codesForSystem);
    }
    if (vsUnion.size > 0) {
      if (hasSystem) {
        const systemUrl = include.system;
        const concepts = conceptMap.get(systemUrl) || /* @__PURE__ */ new Map();
        const refMap = vsUnion.get(systemUrl) || /* @__PURE__ */ new Map();
        const intersection = /* @__PURE__ */ new Map();
        for (const [code, disp] of concepts.entries()) {
          if (refMap.has(code)) intersection.set(code, disp);
        }
        if (intersection.size > 0) result.set(systemUrl, intersection);
      } else {
        this.mergeSystemMaps(result, vsUnion);
      }
    } else {
      this.mergeSystemMaps(result, conceptMap);
    }
    return result;
  }
  async expandValueSetByMeta(metadata, visited = /* @__PURE__ */ new Set()) {
    const { filename, __packageId: packageId, __packageVersion: packageVersion } = metadata;
    const cached = this.cacheMode !== "none" ? await this.getExpansionFromCache(filename, packageId, packageVersion) : void 0;
    if (cached) {
      if (cached?.expansion?.__failure === true) {
        throw new Error(`Previous expansion attempt failed for ValueSet '${cached.url || cached.id || filename}' (cached).`);
      }
      return cached;
    }
    const vs = await this.getValueSetByFileName(filename, packageId, packageVersion);
    if (!vs || vs.resourceType !== "ValueSet") throw new Error(`File '${filename}' not found as a ValueSet in package '${packageId}@${packageVersion}'.`);
    try {
      const compose = vs.compose || {};
      const includes = Array.isArray(compose.include) ? compose.include : [];
      const excludes = Array.isArray(compose.exclude) ? compose.exclude : [];
      let includeMap = /* @__PURE__ */ new Map();
      for (const inc of includes) {
        const incMap = await this.expandInclude(inc, { id: packageId, version: packageVersion }, visited);
        this.mergeSystemMaps(includeMap, incMap);
      }
      let excludeMap = /* @__PURE__ */ new Map();
      for (const exc of excludes) {
        const excMap = await this.expandInclude(exc, { id: packageId, version: packageVersion }, visited);
        this.mergeSystemMaps(excludeMap, excMap);
      }
      this.subtractSystemMaps(includeMap, excludeMap);
      const { contains, total } = this.buildExpansionFromSystemMap(includeMap);
      const expanded = { ...vs, expansion: { timestamp: (/* @__PURE__ */ new Date()).toISOString(), total, contains } };
      if (this.cacheMode !== "none") {
        await this.saveExpansionToCache(filename, packageId, packageVersion, expanded);
      }
      return expanded;
    } catch (e) {
      this.logger.warn(`Failed to expand ValueSet '${vs?.url || vs?.id || filename}': ${e instanceof Error ? e.message : String(e)}. Falling back to original expansion if present.`);
      if (vs?.expansion?.contains && Array.isArray(vs.expansion.contains)) {
        if (this.cacheMode !== "none") {
          await this.saveExpansionToCache(filename, packageId, packageVersion, vs);
        }
        return vs;
      }
      if (this.cacheMode !== "none") {
        const failureStub = { ...vs, expansion: { timestamp: (/* @__PURE__ */ new Date()).toISOString(), __failure: true } };
        try {
          await this.saveExpansionToCache(filename, packageId, packageVersion, failureStub);
        } catch {
        }
      }
      throw e;
    }
  }
  async ensureExpansionCached(filename, packageId, packageVersion) {
    const cacheFilePath = this.getCacheFilePath(filename, packageId, packageVersion);
    try {
      await fs__default.default.access(cacheFilePath);
      return;
    } catch {
      const meta = { resourceType: "ValueSet", filename, __packageId: packageId, __packageVersion: packageVersion };
      try {
        await this.expandValueSetByMeta(meta);
      } catch (e) {
        this.logger.warn(`Failed to pre-cache ValueSet expansion for '${filename}' in '${packageId}@${packageVersion}': ${e instanceof Error ? e.message : String(e)}`);
      }
    }
  }
  async ensureSnapshotCached(filename, packageId, packageVersion) {
    const cacheFilePath = this.getCacheFilePath(filename, packageId, packageVersion);
    try {
      await fs__default.default.access(cacheFilePath);
      return;
    } catch {
      const generated = await this.generate(filename, packageId, packageVersion);
      await this.saveSnapshotToCache(filename, packageId, packageVersion, generated);
    }
  }
  /**
   * Get snapshot by any FSH style identifier (id, url or name), or by a metadata object.
   */
  async getSnapshot(identifier, packageFilter) {
    try {
      let metadata;
      if (typeof identifier === "string") {
        metadata = await this.getMetadata(identifier, packageFilter);
        if (!metadata) {
          throw new Error(`StructureDefinition '${identifier}' not found in context. Could not get or generate a snapshot.`);
        }
      } else {
        metadata = identifier;
        if (!metadata) {
          throw new Error(`StructureDefinition with metadata: 
${JSON.stringify(identifier, null, 2)}
not found in context. Could not get or generate a snapshot.`);
        }
      }
      return await this.getSnapshotByMeta(metadata);
    } catch (e) {
      throw this.prethrow(e);
    }
  }
  /**
   * Get ValueSet expansion by any FSH style identifier (id, url or name), or by a metadata object.
   */
  async expandValueSet(identifier, packageFilter) {
    try {
      let metadata;
      if (typeof identifier === "string") {
        metadata = await this.getValueSetMetadata(identifier, packageFilter);
        if (!metadata) {
          throw new Error(`ValueSet '${identifier}' not found in context. Could not get or generate an expansion.`);
        }
      } else {
        metadata = identifier;
        if (!metadata) {
          throw new Error(`ValueSet with metadata: 
${JSON.stringify(identifier, null, 2)}
not found in context. Could not get or generate an expansion.`);
        }
      }
      return await this.expandValueSetByMeta(metadata);
    } catch (e) {
      throw this.prethrow(e);
    }
  }
  /**
   * Resolve a CodeSystem by canonical URL inside the provided source package context.
   * Will NOT attempt a global resolution fallback (that is the responsibility of the external caller / entrypoint).
   * Only CodeSystems with content === 'complete' are eligible for expansion; if not complete an error is thrown.
   * @param url Canonical URL of the CodeSystem.
   * @param sourcePackage The package (id + version) of the ValueSet that is triggering this resolution.
   * @returns The full CodeSystem resource (content=complete).
   */
  async resolveCompleteCodeSystem(url, sourcePackage) {
    try {
      if (!url) {
        throw new Error("CodeSystem canonical URL missing.");
      }
      if (ImplicitCodeSystemRegistry.isImplicitCodeSystem(url)) {
        const concepts = ImplicitCodeSystemRegistry.getConcepts(url);
        if (!concepts) {
          throw new Error(`Implicit CodeSystem '${url}' provider returned no concepts.`);
        }
        return {
          resourceType: "CodeSystem",
          url,
          status: "active",
          content: "complete",
          concept: Array.from(concepts.entries()).map(([code, display]) => ({
            code,
            display
          }))
        };
      }
      let meta;
      try {
        meta = await this.fpe.resolveMeta({ resourceType: "CodeSystem", url, package: sourcePackage });
      } catch {
      }
      if (!meta) {
        try {
          meta = await this.fpe.resolveMeta({ resourceType: "CodeSystem", url });
        } catch {
          throw new Error(`CodeSystem '${url}' not found (searched in package '${sourcePackage.id}@${sourcePackage.version}' then globally).`);
        }
      }
      if (!meta?.content || typeof meta?.content === "string" && meta.content !== "complete") {
        throw new Error(`CodeSystem '${url}' has content='${meta.content}' and cannot be expanded (only 'complete' supported).`);
      }
      const cs = await this.fpe.resolve({ filename: meta.filename, package: { id: meta.__packageId, version: meta.__packageVersion } });
      if (!cs) {
        throw new Error(`Failed to load CodeSystem '${url}' from package '${meta.__packageId}@${meta.__packageVersion}'.`);
      }
      if (cs.resourceType !== "CodeSystem") {
        throw new Error(`Resolved resource for '${url}' is not a CodeSystem (got '${cs.resourceType || "unknown"}').`);
      }
      if (cs.content !== "complete") {
        throw new Error(`CodeSystem '${url}' has content='${cs.content || "undefined"}' and cannot be expanded (only 'complete' supported).`);
      }
      return cs;
    } catch (e) {
      throw this.prethrow(e);
    }
  }
};

exports.FhirSnapshotGenerator = FhirSnapshotGenerator;
exports.ImplicitCodeSystemRegistry = ImplicitCodeSystemRegistry;
//# sourceMappingURL=index.cjs.map
//# sourceMappingURL=index.cjs.map