fhir-openapi-translator 0.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts","../src/ig/package.ts","../src/ig/minimize.ts","../src/definitions.ts","../src/ir/structureWalker.ts","../src/ig/profile.ts","../src/backends/schemaJson.ts","../src/backends/structureDefinition.ts","../src/emit/schema.ts","../src/ir/registry.ts","../src/operations.ts","../src/paths.ts","../src/generate.ts","../src/capability.ts","../src/merge.ts"],"sourcesContent":["export type FhirVersion = \"r4\" | \"r4b\" | \"r5\";\r\n\r\nexport const FHIR_VERSIONS: readonly FhirVersion[] = [\"r4\", \"r4b\", \"r5\"];\r\n\r\nexport const FHIR_VERSION_NUMBERS: Record<FhirVersion, string> = {\r\n r4: \"4.0.1\",\r\n r4b: \"4.3.0\",\r\n r5: \"5.0.0\",\r\n};\r\n\r\nexport type OpenApiVersion = \"3.0.3\" | \"3.1.0\";\r\n\r\nexport type SourceBackend = \"schema-json\" | \"structure-def\";\r\n\r\nexport interface TrimOptions {\r\n /**\r\n * Replace the Narrative type (human-readable HTML in `Resource.text`) with a\r\n * generic object. Shrinks generated models that never render narratives.\r\n */\r\n excludeNarrative?: boolean;\r\n /**\r\n * Stub out schema definitions first reached deeper than this many hops from\r\n * a requested resource with a generic object. Bounds the size of the\r\n * dependency closure for codegen targets that struggle with large graphs.\r\n */\r\n maxDepth?: number;\r\n /**\r\n * Replace required-binding enums on code fields with plain strings (the\r\n * allowed codes are appended to the description). Codegen'd models then\r\n * tolerate servers that return codes outside the strict ValueSet — a common\r\n * reality with legacy data. `resourceType` discriminators keep their enum.\r\n */\r\n noEnums?: boolean;\r\n}\r\n\r\n/** An IG package resolved to profiles + terminology (see ig/package.ts). */\r\nexport interface IgContextLike {\r\n name: string;\r\n version: string;\r\n fhirVersion: FhirVersion;\r\n profiles: unknown[];\r\n profilesMissingSnapshot: string[];\r\n resolveValueSet: (valueSetUrl: string) => string[] | undefined;\r\n}\r\n\r\n/** A parsed CapabilityStatement (see capability.ts). */\r\nexport interface CapabilityLike {\r\n fhirVersion?: string;\r\n resources: {\r\n type: string;\r\n interactions: Set<string>;\r\n searchParamCodes: Set<string>;\r\n operations: Set<string>;\r\n }[];\r\n}\r\n\r\nexport interface GenerateOptions {\r\n /**\r\n * FHIR resource names, e.g. [\"Patient\", \"Observation\"]. Optional when a\r\n * `capability` statement is given (its declared resources are used); when\r\n * both are set, generation is narrowed to the requested resources.\r\n */\r\n resources?: string[];\r\n fhirVersion: FhirVersion;\r\n /**\r\n * A parsed CapabilityStatement restricting generation to one server's\r\n * declared support (resources, interactions, search params, operations).\r\n * Use `loadCapabilityStatement` / `parseCapabilityStatement` to build it.\r\n */\r\n capability?: CapabilityLike;\r\n /**\r\n * IG package to apply profiles from: a local path (`.tgz` or unpacked\r\n * directory) loaded synchronously, or a pre-resolved IgContext (use the\r\n * async `loadIg` for registry coordinates). Required when `profiles` is set.\r\n */\r\n ig?: string | IgContextLike;\r\n /**\r\n * Profile ids, names, or canonical URLs to apply to their base resources.\r\n * Each profiled resource's schemas/paths reference the named profile schema.\r\n */\r\n profiles?: string[];\r\n /** Target OpenAPI version. Default: \"3.0.3\" (widest codegen support). */\r\n openApiVersion?: OpenApiVersion;\r\n /** Definition source backend. Default: \"schema-json\". */\r\n source?: SourceBackend;\r\n /**\r\n * Also emit the standard FHIR operations applicable to the requested\r\n * resources ($everything, $validate, ...) from the official\r\n * OperationDefinitions. Default: false.\r\n */\r\n operations?: boolean;\r\n /** Server base URL for the `servers` entry. Omitted when not given. */\r\n baseUrl?: string;\r\n /** Override the generated `info.title`. */\r\n title?: string;\r\n trim?: TrimOptions;\r\n}\r\n\r\n/** A JSON-Schema-like node. Kept loose: definitions come from vendored files. */\r\nexport type JsonSchemaNode = { [key: string]: unknown };\r\n\r\n/** A generated OpenAPI document (3.0.3 or 3.1.0) as a plain object. */\r\nexport type OpenApiDocument = { [key: string]: unknown };\r\n","import { execFileSync } from \"node:child_process\";\r\nimport fs from \"node:fs\";\r\nimport os from \"node:os\";\r\nimport path from \"node:path\";\r\nimport {\r\n buildValueSetResolver,\r\n minimizeStructureDefinition,\r\n type RawStructureDefinition,\r\n type ValueSetResolver,\r\n} from \"./minimize.js\";\r\nimport type { MinStructureDefinition } from \"../definitions.js\";\r\nimport { FHIR_VERSIONS, type FhirVersion } from \"../types.js\";\r\n\r\n/** Maps an IG package's FHIR version string to our supported version keys. */\r\nconst FHIR_VERSION_BY_NUMBER: Record<string, FhirVersion> = {\r\n \"4.0.1\": \"r4\",\r\n \"4.0.0\": \"r4\",\r\n \"4.3.0\": \"r4b\",\r\n \"5.0.0\": \"r5\",\r\n};\r\n\r\nexport interface IgProfile {\r\n /** Versionless canonical URL. */\r\n url: string;\r\n id?: string;\r\n name: string;\r\n type: string;\r\n definition: MinStructureDefinition;\r\n}\r\n\r\nexport interface IgContext {\r\n name: string;\r\n version: string;\r\n fhirVersion: FhirVersion;\r\n profiles: IgProfile[];\r\n /** Names/URLs of constraint profiles that lack a snapshot (cannot be applied). */\r\n profilesMissingSnapshot: string[];\r\n resolveValueSet: ValueSetResolver;\r\n}\r\n\r\nexport interface LoadIgOptions {\r\n /** Resolve core bindings (e.g. administrative-gender) not defined in the IG. */\r\n coreValueSetFallback?: ValueSetResolver;\r\n /** Base directory for the registry download cache. Defaults to ~/.fhir-oas. */\r\n cacheDir?: string;\r\n fetchImpl?: typeof fetch;\r\n}\r\n\r\ninterface RawResource {\r\n resourceType?: string;\r\n url?: string;\r\n name?: string;\r\n id?: string;\r\n type?: string;\r\n kind?: string;\r\n derivation?: string;\r\n snapshot?: unknown;\r\n [key: string]: unknown;\r\n}\r\n\r\nconst REGISTRY_BASE = \"https://packages.fhir.org\";\r\n\r\n/**\r\n * Resolves the `--ig` input to an IgContext. Accepts a path to a package\r\n * tarball (`.tgz`), a path to an unpacked package directory, or a registry\r\n * coordinate (`name` or `name@version`) fetched from packages.fhir.org and\r\n * cached locally. Async because registry coordinates hit the network.\r\n */\r\nexport async function loadIg(input: string, options: LoadIgOptions = {}): Promise<IgContext> {\r\n if (isRegistryCoordinate(input)) {\r\n const tarball = await fetchFromRegistry(input, options);\r\n return buildContext(readFromTarball(tarball), options.coreValueSetFallback);\r\n }\r\n return loadIgSync(input, options);\r\n}\r\n\r\n/**\r\n * Synchronous IG loader for local inputs (a `.tgz` or an unpacked directory).\r\n * Registry coordinates require the async {@link loadIg} because they fetch\r\n * over the network.\r\n */\r\nexport function loadIgSync(input: string, options: LoadIgOptions = {}): IgContext {\r\n if (isRegistryCoordinate(input)) {\r\n throw new Error(\r\n `\"${input}\" looks like a registry package coordinate, which requires a network ` +\r\n `fetch. Use loadIg() (async) or the CLI, or download the package and pass a local path.`,\r\n );\r\n }\r\n const stat = fs.existsSync(input) ? fs.statSync(input) : undefined;\r\n if (!stat) {\r\n throw new Error(\r\n `--ig \"${input}\" is not a file or directory. Pass a package .tgz, an unpacked ` +\r\n `package directory, or a registry coordinate like hl7.fhir.us.core@5.0.1.`,\r\n );\r\n }\r\n const files = stat.isDirectory() ? readFromDirectory(input) : readFromTarball(input);\r\n return buildContext(files, options.coreValueSetFallback);\r\n}\r\n\r\n/** package.json plus every FHIR JSON resource, keyed by base filename. */\r\ninterface PackageFiles {\r\n packageJson: { name?: string; version?: string; \"fhir-version-list\"?: string[]; fhirVersions?: string[] };\r\n resources: RawResource[];\r\n}\r\n\r\nfunction isRegistryCoordinate(input: string): boolean {\r\n // A registry coordinate is a package id (optionally @version); it never\r\n // looks like a path and does not exist on disk.\r\n if (fs.existsSync(input)) return false;\r\n if (input.includes(\"/\") || input.includes(\"\\\\\") || input.endsWith(\".tgz\")) return false;\r\n return /^[a-z0-9][a-z0-9.\\-]*(@[\\w.\\-]+)?$/i.test(input);\r\n}\r\n\r\nfunction packageDir(root: string): string {\r\n const nested = path.join(root, \"package\");\r\n if (fs.existsSync(path.join(nested, \"package.json\"))) return nested;\r\n if (fs.existsSync(path.join(root, \"package.json\"))) return root;\r\n throw new Error(`No package.json found under \"${root}\" (looked in ./ and ./package)`);\r\n}\r\n\r\nfunction readFromDirectory(dir: string): PackageFiles {\r\n const base = packageDir(dir);\r\n const packageJson = JSON.parse(fs.readFileSync(path.join(base, \"package.json\"), \"utf8\"));\r\n const resources: RawResource[] = [];\r\n for (const file of fs.readdirSync(base).sort()) {\r\n if (!file.endsWith(\".json\") || file === \"package.json\" || file === \".index.json\") continue;\r\n try {\r\n resources.push(JSON.parse(fs.readFileSync(path.join(base, file), \"utf8\")));\r\n } catch {\r\n // Skip non-resource JSON (e.g. index files that aren't FHIR resources).\r\n }\r\n }\r\n return { packageJson, resources };\r\n}\r\n\r\n/**\r\n * Extracts a package tarball to an isolated temp directory (tar refuses\r\n * absolute/`..` members, and the destination is a private mkdtemp), reads the\r\n * JSON resources, then removes the directory.\r\n */\r\nfunction readFromTarball(tarballPath: string): PackageFiles {\r\n if (!fs.existsSync(tarballPath)) {\r\n throw new Error(`Package tarball not found: ${tarballPath}`);\r\n }\r\n const tmp = fs.mkdtempSync(path.join(os.tmpdir(), \"fhir-oas-ig-\"));\r\n try {\r\n execFileSync(\"tar\", [\"-xzf\", tarballPath, \"-C\", tmp], { stdio: \"pipe\" });\r\n return readFromDirectory(tmp);\r\n } finally {\r\n fs.rmSync(tmp, { recursive: true, force: true });\r\n }\r\n}\r\n\r\nasync function fetchFromRegistry(coordinate: string, options: LoadIgOptions): Promise<string> {\r\n const [name, version] = coordinate.split(\"@\");\r\n const cacheDir = path.join(options.cacheDir ?? path.join(os.homedir(), \".fhir-oas\"), \"packages\");\r\n const resolvedVersion = version ?? \"latest\";\r\n const cachePath = path.join(cacheDir, `${name}#${resolvedVersion}.tgz`);\r\n if (fs.existsSync(cachePath)) return cachePath;\r\n\r\n const url = `${REGISTRY_BASE}/${name}/${resolvedVersion}`;\r\n const doFetch = options.fetchImpl ?? fetch;\r\n let response: Response;\r\n try {\r\n response = await doFetch(url);\r\n } catch (cause) {\r\n throw new Error(\r\n `Failed to reach the FHIR package registry at ${url}: ${(cause as Error).message}. ` +\r\n `If you are offline or behind a restrictive proxy, download the package and pass ` +\r\n `--ig ./${name}.tgz instead.`,\r\n );\r\n }\r\n if (!response.ok) {\r\n throw new Error(\r\n `FHIR package registry returned ${response.status} for ${url}. ` +\r\n `Check the package name and version, or download it and pass --ig ./${name}.tgz.`,\r\n );\r\n }\r\n const buffer = Buffer.from(await response.arrayBuffer());\r\n fs.mkdirSync(cacheDir, { recursive: true });\r\n fs.writeFileSync(cachePath, buffer);\r\n return cachePath;\r\n}\r\n\r\nfunction detectFhirVersion(packageJson: PackageFiles[\"packageJson\"]): FhirVersion {\r\n const versions = packageJson[\"fhir-version-list\"] ?? packageJson.fhirVersions ?? [];\r\n for (const v of versions) {\r\n const mapped = FHIR_VERSION_BY_NUMBER[v];\r\n if (mapped) return mapped;\r\n }\r\n throw new Error(\r\n `Could not determine a supported FHIR version for IG package \"${packageJson.name}\" ` +\r\n `(found: ${versions.join(\", \") || \"none\"}). Supported: ${FHIR_VERSIONS.join(\", \")}.`,\r\n );\r\n}\r\n\r\nfunction buildContext(files: PackageFiles, coreFallback?: ValueSetResolver): IgContext {\r\n const fhirVersion = detectFhirVersion(files.packageJson);\r\n const terminology = files.resources.filter(\r\n (r) => r.resourceType === \"ValueSet\" || r.resourceType === \"CodeSystem\",\r\n );\r\n const resolveValueSet = buildValueSetResolver(terminology as never[], coreFallback);\r\n\r\n const profiles: IgProfile[] = [];\r\n const profilesMissingSnapshot: string[] = [];\r\n for (const r of files.resources) {\r\n if (\r\n r.resourceType === \"StructureDefinition\" &&\r\n r.derivation === \"constraint\" &&\r\n r.kind === \"resource\"\r\n ) {\r\n if (!r.snapshot) {\r\n profilesMissingSnapshot.push(r.name ?? r.url ?? \"unknown\");\r\n continue;\r\n }\r\n const sd = r as unknown as RawStructureDefinition;\r\n profiles.push({\r\n url: sd.url.split(\"|\")[0]!,\r\n id: sd.id,\r\n name: sd.name,\r\n type: sd.type,\r\n definition: minimizeStructureDefinition(sd, resolveValueSet),\r\n });\r\n }\r\n }\r\n profiles.sort((a, b) => a.url.localeCompare(b.url));\r\n\r\n return {\r\n name: files.packageJson.name ?? \"unknown\",\r\n version: files.packageJson.version ?? \"unknown\",\r\n fhirVersion,\r\n profiles,\r\n profilesMissingSnapshot,\r\n resolveValueSet,\r\n };\r\n}\r\n","import type { MinElement, MinElementType, MinStructureDefinition } from \"../definitions.js\";\r\n\r\n/**\r\n * Minimizes raw FHIR StructureDefinition / terminology resources loaded from\r\n * an IG package into the same `MinStructureDefinition` shape the vendored core\r\n * definitions use, so profile snapshots feed the shared structure walker.\r\n *\r\n * This mirrors the element-minimizing shape in scripts/update-definitions.mjs\r\n * (which vendors the core packages at build time). The two are kept separate\r\n * on purpose: this one runs at generation time on arbitrary IG packages and\r\n * additionally captures profile constraints (`fixed[x]`, `mustSupport`,\r\n * unenforced `pattern`/slicing), which the base definitions never need.\r\n */\r\n\r\n/** Above this size an enum stops helping codegen and starts hurting it. */\r\nconst MAX_ENUM_CODES = 150;\r\n\r\ninterface RawElement {\r\n path: string;\r\n min?: number;\r\n max?: string;\r\n short?: string;\r\n definition?: string;\r\n contentReference?: string;\r\n mustSupport?: boolean;\r\n slicing?: unknown;\r\n sliceName?: string;\r\n type?: { code: string; targetProfile?: string[]; extension?: RawExtension[] }[];\r\n binding?: { strength?: string; valueSet?: string };\r\n [key: string]: unknown;\r\n}\r\n\r\ninterface RawExtension {\r\n url: string;\r\n valueUrl?: string;\r\n}\r\n\r\nexport interface RawStructureDefinition {\r\n resourceType: string;\r\n name: string;\r\n id?: string;\r\n url: string;\r\n kind: string;\r\n type: string;\r\n abstract?: boolean;\r\n derivation?: string;\r\n baseDefinition?: string;\r\n fhirVersion?: string;\r\n snapshot?: { element: RawElement[] };\r\n}\r\n\r\nexport type ValueSetResolver = (valueSetUrl: string) => string[] | undefined;\r\n\r\nconst FHIR_TYPE_EXTENSION =\r\n \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\";\r\n\r\n/** Extracts the normalized `fixed[x]` value from a snapshot element, if any. */\r\nfunction fixedValue(el: RawElement): unknown {\r\n for (const key of Object.keys(el)) {\r\n if (key.startsWith(\"fixed\") && key.length > 5) return el[key];\r\n }\r\n return undefined;\r\n}\r\n\r\nfunction omittedConstraints(el: RawElement): string[] | undefined {\r\n const notes: string[] = [];\r\n if (el.slicing || el.sliceName) notes.push(\"slicing\");\r\n if (Object.keys(el).some((k) => k.startsWith(\"pattern\") && k.length > 7)) {\r\n notes.push(\"pattern\");\r\n }\r\n return notes.length > 0 ? notes : undefined;\r\n}\r\n\r\nfunction minimizeElement(el: RawElement, resolveValueSet?: ValueSetResolver): MinElement {\r\n const out: MinElement = { path: el.path, min: el.min ?? 0, max: el.max ?? \"*\" };\r\n if (el.short) out.short = el.short;\r\n if (el.definition) out.definition = el.definition;\r\n if (el.contentReference) out.contentReference = el.contentReference;\r\n if (el.type) {\r\n out.types = el.type.map((t) => {\r\n const type: MinElementType = { code: t.code };\r\n if (t.targetProfile) type.targetProfile = t.targetProfile;\r\n const fhirType = (t.extension ?? []).find((e) => e.url === FHIR_TYPE_EXTENSION);\r\n if (fhirType?.valueUrl) type.fhirType = fhirType.valueUrl;\r\n return type;\r\n });\r\n }\r\n if (el.binding?.strength === \"required\" && el.binding.valueSet) {\r\n out.binding = { strength: el.binding.strength, valueSet: el.binding.valueSet };\r\n const codes = resolveValueSet?.(el.binding.valueSet);\r\n if (codes) out.binding.codes = [...codes].sort();\r\n }\r\n const fixed = fixedValue(el);\r\n if (fixed !== undefined) out.fixed = fixed;\r\n if (el.mustSupport) out.mustSupport = true;\r\n const omitted = omittedConstraints(el);\r\n if (omitted) out.omittedConstraints = omitted;\r\n return out;\r\n}\r\n\r\nexport function minimizeStructureDefinition(\r\n sd: RawStructureDefinition,\r\n resolveValueSet?: ValueSetResolver,\r\n): MinStructureDefinition {\r\n return {\r\n name: sd.name,\r\n url: sd.url,\r\n kind: sd.kind as MinStructureDefinition[\"kind\"],\r\n type: sd.type,\r\n abstract: !!sd.abstract,\r\n baseDefinition: sd.baseDefinition,\r\n elements: (sd.snapshot?.element ?? []).map((el) => minimizeElement(el, resolveValueSet)),\r\n };\r\n}\r\n\r\ninterface RawValueSet {\r\n resourceType: string;\r\n url?: string;\r\n compose?: {\r\n include?: RawValueSetGroup[];\r\n exclude?: RawValueSetGroup[];\r\n };\r\n}\r\n\r\ninterface RawValueSetGroup {\r\n system?: string;\r\n concept?: { code: string }[];\r\n filter?: unknown[];\r\n valueSet?: string[];\r\n}\r\n\r\ninterface RawCodeSystem {\r\n resourceType: string;\r\n url?: string;\r\n content?: string;\r\n concept?: RawConcept[];\r\n}\r\n\r\ninterface RawConcept {\r\n code: string;\r\n concept?: RawConcept[];\r\n}\r\n\r\n/**\r\n * Builds a ValueSet URL -> flat code list resolver from the ValueSet and\r\n * CodeSystem resources available (IG-local plus, optionally, a fallback for\r\n * the vendored core terminology). Only simple composes are resolved; anything\r\n * with filters or valueSet imports is left unresolved, so the binding stays a\r\n * plain code — exactly the core behavior.\r\n */\r\nexport function buildValueSetResolver(\r\n resources: (RawValueSet | RawCodeSystem)[],\r\n fallback?: ValueSetResolver,\r\n): ValueSetResolver {\r\n const codeSystems = new Map<string, RawCodeSystem>();\r\n const valueSets = new Map<string, RawValueSet>();\r\n for (const r of resources) {\r\n if (r.resourceType === \"CodeSystem\" && r.url) codeSystems.set(r.url, r as RawCodeSystem);\r\n if (r.resourceType === \"ValueSet\" && r.url) valueSets.set(r.url, r as RawValueSet);\r\n }\r\n\r\n function conceptCodes(concepts: RawConcept[] | undefined, out: string[]): string[] {\r\n for (const c of concepts ?? []) {\r\n out.push(c.code);\r\n if (c.concept) conceptCodes(c.concept, out);\r\n }\r\n return out;\r\n }\r\n\r\n function resolveGroup(group: RawValueSetGroup): string[] | undefined {\r\n if (group.filter?.length || group.valueSet?.length) return undefined;\r\n if (group.concept?.length) return group.concept.map((c) => c.code);\r\n if (!group.system) return undefined;\r\n const cs = codeSystems.get(group.system);\r\n if (!cs || cs.content === \"not-present\" || !cs.concept) return undefined;\r\n return conceptCodes(cs.concept, []);\r\n }\r\n\r\n return function resolve(valueSetUrl: string): string[] | undefined {\r\n const versionless = valueSetUrl.split(\"|\")[0]!;\r\n const vs = valueSets.get(versionless);\r\n if (!vs?.compose?.include?.length) return fallback?.(valueSetUrl);\r\n const codes = new Set<string>();\r\n for (const group of vs.compose.include) {\r\n const groupCodes = resolveGroup(group);\r\n if (!groupCodes) return fallback?.(valueSetUrl);\r\n for (const code of groupCodes) codes.add(code);\r\n }\r\n for (const group of vs.compose.exclude ?? []) {\r\n const groupCodes = resolveGroup(group);\r\n if (!groupCodes) return fallback?.(valueSetUrl);\r\n for (const code of groupCodes) codes.delete(code);\r\n }\r\n if (codes.size === 0 || codes.size > MAX_ENUM_CODES) return fallback?.(valueSetUrl);\r\n return [...codes];\r\n };\r\n}\r\n","import { gunzipSync } from \"node:zlib\";\r\nimport fs from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { fileURLToPath } from \"node:url\";\r\nimport type { FhirVersion, JsonSchemaNode } from \"./types.js\";\r\n\r\n/**\r\n * The vendored `definitions/` directory sits at the package root, next to\r\n * `src/` (dev) or `dist/` (published build), so walking up from this module\r\n * finds it in both layouts.\r\n */\r\nfunction definitionsRoot(): string {\r\n let dir = path.dirname(fileURLToPath(import.meta.url));\r\n for (let i = 0; i < 5; i++) {\r\n const candidate = path.join(dir, \"definitions\");\r\n if (fs.existsSync(candidate)) return candidate;\r\n dir = path.dirname(dir);\r\n }\r\n throw new Error(\"Could not locate the vendored FHIR definitions directory\");\r\n}\r\n\r\nconst cache = new Map<string, unknown>();\r\n\r\nfunction loadGzJson(fhirVersion: FhirVersion, file: string): unknown {\r\n const key = `${fhirVersion}/${file}`;\r\n let value = cache.get(key);\r\n if (value === undefined) {\r\n const fullPath = path.join(definitionsRoot(), fhirVersion, `${file}.gz`);\r\n value = JSON.parse(gunzipSync(fs.readFileSync(fullPath)).toString(\"utf8\"));\r\n cache.set(key, value);\r\n }\r\n return value;\r\n}\r\n\r\nexport interface FhirJsonSchema {\r\n discriminator?: { propertyName: string; mapping: Record<string, string> };\r\n definitions: Record<string, JsonSchemaNode>;\r\n}\r\n\r\nexport function loadFhirSchema(fhirVersion: FhirVersion): FhirJsonSchema {\r\n return loadGzJson(fhirVersion, \"fhir.schema.json\") as FhirJsonSchema;\r\n}\r\n\r\nexport interface SearchParameter {\r\n name: string;\r\n code: string;\r\n base: string[];\r\n type: string;\r\n description?: string;\r\n}\r\n\r\nexport function loadSearchParameters(fhirVersion: FhirVersion): SearchParameter[] {\r\n const bundle = loadGzJson(fhirVersion, \"search-parameters.json\") as {\r\n entry?: { resource?: SearchParameter & { resourceType: string } }[];\r\n };\r\n return (bundle.entry ?? [])\r\n .map((e) => e.resource)\r\n .filter(\r\n // R4B ships a few draft codesystem-extensions-* SearchParameters with no\r\n // `base`; they can't be attached to any resource, so drop them here.\r\n (r): r is SearchParameter & { resourceType: string } =>\r\n r?.resourceType === \"SearchParameter\" && Array.isArray(r.base),\r\n );\r\n}\r\n\r\nexport interface MinOperationParameter {\r\n name: string;\r\n use: \"in\" | \"out\";\r\n min: number;\r\n max: string;\r\n /** Absent for multi-part parameters (they force POST + Parameters). */\r\n type?: string;\r\n documentation?: string;\r\n}\r\n\r\nexport interface MinOperationDefinition {\r\n name: string;\r\n code: string;\r\n url: string;\r\n description?: string;\r\n /** Applicable resource types; [\"Resource\"] means every resource. */\r\n resource: string[];\r\n system: boolean;\r\n type: boolean;\r\n instance: boolean;\r\n parameters: MinOperationParameter[];\r\n}\r\n\r\nexport function loadOperationDefinitions(fhirVersion: FhirVersion): MinOperationDefinition[] {\r\n return loadGzJson(fhirVersion, \"operation-definitions.json\") as MinOperationDefinition[];\r\n}\r\n\r\nexport interface MinElementType {\r\n code: string;\r\n targetProfile?: string[];\r\n fhirType?: string;\r\n}\r\n\r\nexport interface MinElement {\r\n path: string;\r\n min: number;\r\n max: string;\r\n short?: string;\r\n definition?: string;\r\n contentReference?: string;\r\n types?: MinElementType[];\r\n binding?: { strength: string; valueSet: string; codes?: string[] };\r\n /**\r\n * Value the element is fixed to by a profile (`fixed[x]`), normalized to a\r\n * JSON value. Only populated for profiles loaded from an IG package; the\r\n * vendored base definitions do not carry it.\r\n */\r\n fixed?: unknown;\r\n /** Profile `mustSupport` flag (surfaced in descriptions, not enforced). */\r\n mustSupport?: boolean;\r\n /**\r\n * Human labels for profile constraints this tool does not enforce in\r\n * OpenAPI (e.g. \"pattern\", \"slicing\"), surfaced in the property description\r\n * and `x-fhir-constraints-omitted`. IG profiles only.\r\n */\r\n omittedConstraints?: string[];\r\n}\r\n\r\nexport interface MinStructureDefinition {\r\n name: string;\r\n url: string;\r\n kind: \"resource\" | \"complex-type\" | \"primitive-type\";\r\n type: string;\r\n abstract: boolean;\r\n baseDefinition?: string;\r\n elements: MinElement[];\r\n}\r\n\r\nexport function loadStructureDefinitions(fhirVersion: FhirVersion): MinStructureDefinition[] {\r\n return loadGzJson(fhirVersion, \"structure-definitions.json\") as MinStructureDefinition[];\r\n}\r\n","import type { MinElement, MinStructureDefinition } from \"../definitions.js\";\r\nimport type { JsonSchemaNode } from \"../types.js\";\r\n\r\n/**\r\n * Walks a StructureDefinition snapshot into named IR schema definitions\r\n * (`#/definitions/<Name>` refs). Shared by the core structure-def backend\r\n * (base resources/types) and the profile applier (IG profiles), so the two\r\n * produce identical schema shapes and only differ in the constraints applied.\r\n */\r\nexport interface EmitOptions {\r\n /** Schema name for the root definition (e.g. \"Patient\" or \"USCorePatient\"). */\r\n rootName: string;\r\n /** Emit a `resourceType` discriminator property. */\r\n isResourceRoot: boolean;\r\n /**\r\n * Wire resourceType/description name. Defaults to the SD `type`. For a\r\n * profile this stays the base resource (\"Patient\") even though rootName is\r\n * the profile name, because resourceType on the wire is the base type.\r\n */\r\n resourceDisplayName?: string;\r\n /**\r\n * Apply profile constraints while walking: drop `max: \"0\"` elements and\r\n * turn `fixed[x]` into a `const`. Off for base definitions so their output\r\n * is unchanged.\r\n */\r\n profile?: boolean;\r\n}\r\n\r\ninterface ElementNode {\r\n element: MinElement;\r\n children: Map<string, ElementNode>;\r\n}\r\n\r\nfunction segmentToPascal(segment: string): string {\r\n return segment.charAt(0).toUpperCase() + segment.slice(1);\r\n}\r\n\r\nfunction isBackbone(el: MinElement): boolean {\r\n return (el.types ?? []).some((t) => t.code === \"BackboneElement\" || t.code === \"Element\");\r\n}\r\n\r\n/** Type codes from the FHIRPath system (used on `id`, `Extension.url`, ...). */\r\nfunction systemTypeSchema(code: string): JsonSchemaNode | undefined {\r\n if (!code.startsWith(\"http://hl7.org/fhirpath/System.\")) return undefined;\r\n const kind = code.slice(\"http://hl7.org/fhirpath/System.\".length);\r\n switch (kind) {\r\n case \"Boolean\":\r\n return { type: \"boolean\" };\r\n case \"Integer\":\r\n case \"Decimal\":\r\n return { type: \"number\" };\r\n default:\r\n return { type: \"string\" };\r\n }\r\n}\r\n\r\nfunction buildElementTree(sd: MinStructureDefinition): ElementNode | undefined {\r\n const rootPath = sd.type ?? sd.name;\r\n let root: ElementNode | undefined;\r\n const nodes = new Map<string, ElementNode>();\r\n for (const el of sd.elements) {\r\n const node: ElementNode = { element: el, children: new Map() };\r\n nodes.set(el.path, node);\r\n if (el.path === rootPath) {\r\n root = node;\r\n continue;\r\n }\r\n const parentPath = el.path.slice(0, el.path.lastIndexOf(\".\"));\r\n const segment = el.path.slice(el.path.lastIndexOf(\".\") + 1);\r\n nodes.get(parentPath)?.children.set(segment, node);\r\n }\r\n return root;\r\n}\r\n\r\n/**\r\n * Names a backbone reached by a `contentReference` (e.g. \"#Questionnaire.item\"\r\n * -> \"Questionnaire_Item\"). When walking a profile, a self-reference to the\r\n * base type root is remapped onto the profile's own name so the ref resolves\r\n * within the emitted profile schemas.\r\n */\r\nfunction contentReferenceName(reference: string, sd: MinStructureDefinition, rootName: string): string {\r\n const path = reference.replace(/^#/, \"\");\r\n const [root, ...rest] = path.split(\".\");\r\n if (rest.length === 0) return root ?? path;\r\n const prefix = root === (sd.type ?? sd.name) ? rootName : (root ?? \"\");\r\n return [prefix, ...rest.map(segmentToPascal)].join(\"_\");\r\n}\r\n\r\nexport function emitStructureDefinitionSchemas(\r\n sd: MinStructureDefinition,\r\n definitions: Map<string, JsonSchemaNode>,\r\n opts: EmitOptions,\r\n): void {\r\n const root = buildElementTree(sd);\r\n if (!root) return;\r\n emitObjectDefinition(sd, opts.rootName, root, definitions, opts.isResourceRoot, opts);\r\n}\r\n\r\nfunction emitObjectDefinition(\r\n sd: MinStructureDefinition,\r\n name: string,\r\n node: ElementNode,\r\n definitions: Map<string, JsonSchemaNode>,\r\n isResourceRoot: boolean,\r\n opts: EmitOptions,\r\n): void {\r\n if (definitions.has(name)) return;\r\n const properties: Record<string, JsonSchemaNode> = {};\r\n const required: string[] = [];\r\n const displayName = opts.resourceDisplayName ?? sd.type ?? sd.name;\r\n\r\n if (isResourceRoot) {\r\n properties.resourceType = {\r\n description: `This is a ${displayName} resource`,\r\n const: displayName,\r\n };\r\n required.push(\"resourceType\");\r\n }\r\n\r\n // Reserve the name before recursing so cycles terminate.\r\n const definition: JsonSchemaNode = {\r\n ...(node.element.definition ? { description: node.element.definition } : {}),\r\n properties,\r\n additionalProperties: false,\r\n };\r\n definitions.set(name, definition);\r\n\r\n for (const [segment, child] of node.children) {\r\n emitProperty(sd, name, segment, child, properties, required, definitions, opts);\r\n }\r\n if (required.length > 0) definition.required = required.sort();\r\n}\r\n\r\nfunction emitProperty(\r\n sd: MinStructureDefinition,\r\n parentName: string,\r\n segment: string,\r\n node: ElementNode,\r\n properties: Record<string, JsonSchemaNode>,\r\n required: string[],\r\n definitions: Map<string, JsonSchemaNode>,\r\n opts: EmitOptions,\r\n): void {\r\n const el = node.element;\r\n // Profile constraint: an element constrained out (max 0) is removed.\r\n if (opts.profile && el.max === \"0\") return;\r\n\r\n const isArray = el.max === \"*\" || Number(el.max) > 1;\r\n const isChoice = segment.endsWith(\"[x]\");\r\n\r\n // In profile mode, annotate must-support and constraints not enforced in\r\n // OpenAPI (pattern, slicing) so they survive into generated docs.\r\n let description = el.definition ?? el.short;\r\n const omitted: string[] = [];\r\n if (opts.profile) {\r\n if (el.mustSupport) omitted.push(\"must-support\");\r\n if (el.omittedConstraints) omitted.push(...el.omittedConstraints);\r\n if (omitted.length > 0) {\r\n const note = `Profile constraints not enforced here: ${omitted.join(\", \")}.`;\r\n description = description ? `${description} ${note}` : note;\r\n }\r\n }\r\n\r\n const addProperty = (fieldName: string, schema: JsonSchemaNode, primitive: boolean) => {\r\n const annotated =\r\n omitted.length > 0 && !(\"$ref\" in schema)\r\n ? { ...schema, \"x-fhir-constraints-omitted\": omitted }\r\n : schema;\r\n properties[fieldName] = wrapCardinality(annotated, isArray, description);\r\n if (primitive) {\r\n properties[`_${fieldName}`] = wrapCardinality(\r\n { $ref: \"#/definitions/Element\" },\r\n isArray,\r\n `Extensions for ${fieldName}`,\r\n );\r\n }\r\n };\r\n\r\n if (isChoice) {\r\n const base = segment.slice(0, -3);\r\n for (const type of el.types ?? []) {\r\n const fieldName = base + segmentToPascal(type.code);\r\n const { schema, primitive } = schemaForTypeCode(type.code, el, opts);\r\n addProperty(fieldName, schema, primitive);\r\n }\r\n // Choice fields are never individually required: the constraint \"exactly\r\n // one of the expansions\" is not expressible in required[].\r\n return;\r\n }\r\n\r\n if (el.contentReference) {\r\n const target = contentReferenceName(el.contentReference, sd, opts.rootName);\r\n addProperty(segment, { $ref: `#/definitions/${target}` }, false);\r\n } else if (isBackbone(el) && node.children.size > 0) {\r\n const childName = `${parentName}_${segmentToPascal(segment)}`;\r\n emitObjectDefinition(sd, childName, node, definitions, false, opts);\r\n addProperty(segment, { $ref: `#/definitions/${childName}` }, false);\r\n } else {\r\n const type = (el.types ?? [])[0];\r\n if (!type) return; // extension-only or profiled-out element\r\n const { schema, primitive } = schemaForTypeCode(type.code, el, opts);\r\n addProperty(segment, schema, primitive);\r\n }\r\n\r\n if (el.min >= 1) required.push(segment);\r\n}\r\n\r\nfunction schemaForTypeCode(\r\n code: string,\r\n el: MinElement,\r\n opts: EmitOptions,\r\n): { schema: JsonSchemaNode; primitive: boolean } {\r\n const primitive = code.charAt(0) === code.charAt(0).toLowerCase();\r\n\r\n const system = systemTypeSchema(code);\r\n if (system) return { schema: system, primitive: false };\r\n if (code === \"Resource\" || code === \"DomainResource\") {\r\n return { schema: { $ref: \"#/definitions/ResourceList\" }, primitive: false };\r\n }\r\n // Profile constraint: a fixed value pins the element to a single constant.\r\n if (opts.profile && el.fixed !== undefined) {\r\n return { schema: { const: el.fixed }, primitive };\r\n }\r\n // Required bindings whose ValueSet was resolved become inline enums on\r\n // `code` elements, mirroring the official fhir.schema.json.\r\n if (code === \"code\" && el.binding?.codes?.length) {\r\n return { schema: { enum: el.binding.codes }, primitive: true };\r\n }\r\n return { schema: { $ref: `#/definitions/${code}` }, primitive };\r\n}\r\n\r\nfunction wrapCardinality(\r\n schema: JsonSchemaNode,\r\n isArray: boolean,\r\n description?: string,\r\n): JsonSchemaNode {\r\n const withDescription = (s: JsonSchemaNode) =>\r\n description && !(\"$ref\" in s) ? { description, ...s } : s;\r\n if (isArray) {\r\n return {\r\n ...(description ? { description } : {}),\r\n type: \"array\",\r\n items: schema,\r\n };\r\n }\r\n if (\"$ref\" in schema && description) {\r\n // JSON Schema draft-06/OAS 3.0 ignores siblings of $ref; nest description\r\n // via allOf only in 3.1. Keep plain $ref for compatibility.\r\n return schema;\r\n }\r\n return withDescription(schema);\r\n}\r\n","import { loadStructureDefinitions } from \"../definitions.js\";\r\nimport type { DefinitionRegistry } from \"../ir/registry.js\";\r\nimport { emitStructureDefinitionSchemas } from \"../ir/structureWalker.js\";\r\nimport type { FhirVersion, JsonSchemaNode } from \"../types.js\";\r\nimport type { IgContext, IgProfile } from \"./package.js\";\r\nimport type { ValueSetResolver } from \"./minimize.js\";\r\n\r\nexport interface AppliedProfile {\r\n /** Component schema name emitted for the profile (e.g. \"USCorePatient\"). */\r\n schemaName: string;\r\n /** Base resource type the profile constrains (e.g. \"Patient\"). */\r\n resourceType: string;\r\n /** Versionless canonical URL of the profile. */\r\n url: string;\r\n}\r\n\r\n/**\r\n * A ValueSet resolver backed by the vendored core definitions: base elements\r\n * already carry the resolved code list for their required binding, so core\r\n * ValueSets (e.g. administrative-gender) referenced by profile elements can be\r\n * resolved without shipping the core ValueSets themselves.\r\n */\r\nexport function buildCoreValueSetFallback(fhirVersion: FhirVersion): ValueSetResolver {\r\n const map = new Map<string, string[]>();\r\n for (const sd of loadStructureDefinitions(fhirVersion)) {\r\n for (const el of sd.elements) {\r\n if (el.binding?.codes?.length && el.binding.valueSet) {\r\n const key = el.binding.valueSet.split(\"|\")[0]!;\r\n if (!map.has(key)) map.set(key, el.binding.codes);\r\n }\r\n }\r\n }\r\n return (valueSetUrl) => map.get(valueSetUrl.split(\"|\")[0]!);\r\n}\r\n\r\nfunction sanitizeSchemaName(name: string): string {\r\n const cleaned = name.replace(/[^A-Za-z0-9_]/g, \"\");\r\n return /^[A-Za-z]/.test(cleaned) ? cleaned : `Profile${cleaned}`;\r\n}\r\n\r\n/** Finds a profile in the IG by canonical URL, id, or name (case-insensitive). */\r\nfunction findProfile(context: IgContext, requested: string): IgProfile {\r\n const needle = requested.toLowerCase();\r\n const matches = context.profiles.filter(\r\n (p) =>\r\n p.url.toLowerCase() === needle ||\r\n p.id?.toLowerCase() === needle ||\r\n p.name.toLowerCase() === needle,\r\n );\r\n if (matches.length === 1) return matches[0]!;\r\n if (matches.length > 1) {\r\n throw new Error(\r\n `Profile \"${requested}\" is ambiguous in ${context.name}; match by canonical URL. ` +\r\n `Candidates: ${matches.map((p) => p.url).join(\", \")}`,\r\n );\r\n }\r\n if (context.profilesMissingSnapshot.some((p) => p.toLowerCase() === needle)) {\r\n throw new Error(\r\n `Profile \"${requested}\" in ${context.name} has no snapshot. Snapshot generation is ` +\r\n `out of scope; use a snapshot-bearing release of the IG.`,\r\n );\r\n }\r\n const available = context.profiles.map((p) => p.id ?? p.name).sort();\r\n throw new Error(\r\n `Profile \"${requested}\" not found in ${context.name}@${context.version}. ` +\r\n `Available profiles: ${available.join(\", \") || \"none\"}`,\r\n );\r\n}\r\n\r\n/**\r\n * Emits the profiled schema (and its backbones) into the registry, applying\r\n * the profile's constraints, and returns how it maps onto the base resource.\r\n * Unconstrained elements resolve to base type refs, so the dependency closure\r\n * still draws from the vendored core registry.\r\n */\r\nexport function applyProfile(\r\n context: IgContext,\r\n requested: string,\r\n registry: DefinitionRegistry,\r\n): AppliedProfile {\r\n const profile = findProfile(context, requested);\r\n let schemaName = sanitizeSchemaName(profile.name);\r\n // Guard against collision with a base definition of a different shape.\r\n if (registry.definitions.has(schemaName) && schemaName !== profile.type) {\r\n schemaName = `${schemaName}Profile`;\r\n }\r\n\r\n emitStructureDefinitionSchemas(profile.definition, registry.definitions, {\r\n rootName: schemaName,\r\n isResourceRoot: true,\r\n resourceDisplayName: profile.type,\r\n profile: true,\r\n });\r\n\r\n const schema = registry.definitions.get(schemaName) as JsonSchemaNode | undefined;\r\n if (schema) schema[\"x-fhir-profile\"] = profile.url;\r\n\r\n return { schemaName, resourceType: profile.type, url: profile.url };\r\n}\r\n","import { loadFhirSchema } from \"../definitions.js\";\r\nimport type { DefinitionRegistry } from \"../ir/registry.js\";\r\nimport type { FhirVersion, JsonSchemaNode } from \"../types.js\";\r\n\r\n/**\r\n * Builds the definition registry from the official `fhir.schema.json`\r\n * published with each FHIR release. This schema already has choice types\r\n * (value[x]) expanded and backbone elements flattened into named definitions\r\n * (e.g. `Patient_Contact`), so it maps directly onto the IR.\r\n */\r\nexport function buildRegistryFromSchemaJson(fhirVersion: FhirVersion): DefinitionRegistry {\r\n const schema = loadFhirSchema(fhirVersion);\r\n const definitions = new Map<string, JsonSchemaNode>(Object.entries(schema.definitions));\r\n\r\n // Resource types are exactly the discriminator mapping keys; fall back to\r\n // \"has a resourceType const\" for robustness.\r\n let resourceNames: string[];\r\n if (schema.discriminator?.mapping) {\r\n resourceNames = Object.keys(schema.discriminator.mapping);\r\n } else {\r\n resourceNames = [...definitions.entries()]\r\n .filter(([, def]) => {\r\n const props = def.properties as Record<string, JsonSchemaNode> | undefined;\r\n return props?.resourceType !== undefined && \"const\" in (props.resourceType ?? {});\r\n })\r\n .map(([name]) => name);\r\n }\r\n\r\n return { fhirVersion, definitions, resourceNames };\r\n}\r\n","import {\r\n loadStructureDefinitions,\r\n type MinStructureDefinition,\r\n} from \"../definitions.js\";\r\nimport type { DefinitionRegistry } from \"../ir/registry.js\";\r\nimport { emitStructureDefinitionSchemas } from \"../ir/structureWalker.js\";\r\nimport type { FhirVersion, JsonSchemaNode } from \"../types.js\";\r\n\r\n/**\r\n * Builds the definition registry by walking StructureDefinition snapshots —\r\n * the canonical FHIR metadata, and the basis for profile support. Produces the\r\n * same IR shape as the fhir.schema.json backend: named definitions referencing\r\n * each other via `#/definitions/<Name>`.\r\n *\r\n * The per-snapshot walk lives in ../ir/structureWalker.ts so profiles loaded\r\n * from an IG package emit identically-shaped schemas.\r\n */\r\nexport function buildRegistryFromStructureDefinitions(\r\n fhirVersion: FhirVersion,\r\n): DefinitionRegistry {\r\n const sds = loadStructureDefinitions(fhirVersion);\r\n const definitions = new Map<string, JsonSchemaNode>();\r\n const resourceNames: string[] = [];\r\n\r\n const byName = new Map(sds.map((sd) => [sd.name, sd]));\r\n\r\n for (const sd of sds) {\r\n if (sd.kind === \"primitive-type\") {\r\n definitions.set(sd.name, primitiveSchema(sd));\r\n }\r\n }\r\n // `xhtml` (Narrative.div) has no StructureDefinition in the core packages.\r\n if (!definitions.has(\"xhtml\")) {\r\n definitions.set(\"xhtml\", {\r\n type: \"string\",\r\n description: \"XHTML narrative content\",\r\n });\r\n }\r\n\r\n for (const sd of sds) {\r\n if (sd.kind === \"primitive-type\" || sd.abstract) continue;\r\n emitStructureDefinitionSchemas(sd, definitions, {\r\n rootName: sd.name,\r\n isResourceRoot: sd.kind === \"resource\",\r\n });\r\n if (sd.kind === \"resource\") resourceNames.push(sd.name);\r\n }\r\n\r\n // Element/BackboneElement are abstract but referenced by primitive-extension\r\n // (`_field`) properties, so they need concrete definitions.\r\n for (const abstractName of [\"Element\", \"BackboneElement\"]) {\r\n const sd = byName.get(abstractName);\r\n if (sd && !definitions.has(abstractName)) {\r\n emitStructureDefinitionSchemas(sd, definitions, {\r\n rootName: sd.name,\r\n isResourceRoot: false,\r\n });\r\n }\r\n }\r\n\r\n resourceNames.sort();\r\n definitions.set(\"ResourceList\", {\r\n oneOf: resourceNames.map((name) => ({ $ref: `#/definitions/${name}` })),\r\n });\r\n\r\n return { fhirVersion, definitions, resourceNames };\r\n}\r\n\r\n/** Maps FHIR primitive type names to their JSON representation. */\r\nfunction primitiveJsonType(name: string): JsonSchemaNode {\r\n switch (name) {\r\n case \"boolean\":\r\n return { type: \"boolean\" };\r\n case \"decimal\":\r\n return { type: \"number\" };\r\n case \"integer\":\r\n case \"positiveInt\":\r\n case \"unsignedInt\":\r\n return { type: \"number\" };\r\n // integer64 (R5) is represented as a JSON string per the spec.\r\n default:\r\n return { type: \"string\" };\r\n }\r\n}\r\n\r\nfunction primitiveSchema(sd: MinStructureDefinition): JsonSchemaNode {\r\n const root = sd.elements[0];\r\n return {\r\n ...primitiveJsonType(sd.name),\r\n ...(root?.definition ? { description: root.definition } : {}),\r\n };\r\n}\r\n","import type { JsonSchemaNode, OpenApiVersion } from \"../types.js\";\r\n\r\nconst DEFINITIONS_REF = \"#/definitions/\";\r\nconst COMPONENTS_REF = \"#/components/schemas/\";\r\nconst NO_ENUMS_NOTE_MAX_CODES = 25;\r\n\r\nexport interface ConvertOptions {\r\n /** Strip source enums (required-binding codes), noting them in description. */\r\n noEnums?: boolean;\r\n}\r\n\r\n/**\r\n * Converts one JSON-Schema-shaped definition (draft-06 subset as used by\r\n * fhir.schema.json) into an OpenAPI schema object:\r\n * - rewrites `#/definitions/X` refs to `#/components/schemas/X`\r\n * - strips `pattern` from non-string types (fhir.schema.json puts regex\r\n * patterns on booleans/numbers, where the keyword is meaningless)\r\n * - drops JSON Schema bookkeeping keywords ($schema, id/$id, $comment)\r\n * - for 3.0.x, downconverts `const` to a single-value `enum`\r\n * - with `noEnums`, replaces source enums with plain strings, keeping the\r\n * allowed codes in the description; `const`-derived enums (resourceType\r\n * discriminators) are exempt\r\n */\r\nexport function convertSchema(\r\n node: JsonSchemaNode,\r\n target: OpenApiVersion,\r\n options: ConvertOptions = {},\r\n): JsonSchemaNode {\r\n return convertNode(node, target, options) as JsonSchemaNode;\r\n}\r\n\r\nfunction convertNode(node: unknown, target: OpenApiVersion, options: ConvertOptions): unknown {\r\n if (Array.isArray(node)) {\r\n return node.map((item) => convertNode(item, target, options));\r\n }\r\n if (!node || typeof node !== \"object\") return node;\r\n\r\n const out: JsonSchemaNode = {};\r\n const source = node as JsonSchemaNode;\r\n let strippedEnum: unknown[] | undefined;\r\n for (const [key, value] of Object.entries(source)) {\r\n switch (key) {\r\n case \"$schema\":\r\n case \"$comment\":\r\n case \"id\":\r\n case \"$id\":\r\n break;\r\n case \"$ref\":\r\n out.$ref =\r\n typeof value === \"string\" && value.startsWith(DEFINITIONS_REF)\r\n ? COMPONENTS_REF + value.slice(DEFINITIONS_REF.length)\r\n : value;\r\n break;\r\n case \"const\":\r\n if (target === \"3.1.0\") out.const = value;\r\n else out.enum = [value];\r\n break;\r\n case \"enum\":\r\n if (options.noEnums && Array.isArray(value)) strippedEnum = value;\r\n else out.enum = value;\r\n break;\r\n case \"pattern\":\r\n if (source.type === undefined || source.type === \"string\") out.pattern = value;\r\n break;\r\n default:\r\n out[key] = convertNode(value, target, options);\r\n }\r\n }\r\n\r\n if (strippedEnum) {\r\n if (out.type === undefined && out.$ref === undefined) out.type = \"string\";\r\n const listed = strippedEnum.slice(0, NO_ENUMS_NOTE_MAX_CODES).join(\" | \");\r\n const ellipsis = strippedEnum.length > NO_ENUMS_NOTE_MAX_CODES ? \" | ...\" : \"\";\r\n const note = `Codes (FHIR required binding, not enforced here): ${listed}${ellipsis}`;\r\n out.description = out.description ? `${out.description} ${note}` : note;\r\n }\r\n return out;\r\n}\r\n","import type { FhirVersion, JsonSchemaNode, TrimOptions } from \"../types.js\";\r\n\r\n/**\r\n * Backend-neutral intermediate representation: a set of named,\r\n * JSON-Schema-shaped type definitions whose internal references use the\r\n * `#/definitions/<Name>` form (the convention of the official fhir.schema.json).\r\n * Both backends produce this shape; emitters consume it.\r\n */\r\nexport interface DefinitionRegistry {\r\n fhirVersion: FhirVersion;\r\n definitions: Map<string, JsonSchemaNode>;\r\n /** Concrete (non-abstract) resource type names, e.g. \"Patient\". */\r\n resourceNames: string[];\r\n}\r\n\r\nconst REF_PREFIX = \"#/definitions/\";\r\n\r\nexport function refName(ref: string): string | undefined {\r\n return ref.startsWith(REF_PREFIX) ? ref.slice(REF_PREFIX.length) : undefined;\r\n}\r\n\r\n/** Collects the names of all `#/definitions/...` references inside a schema node. */\r\nexport function collectRefs(node: unknown, out = new Set<string>()): Set<string> {\r\n if (Array.isArray(node)) {\r\n for (const item of node) collectRefs(item, out);\r\n } else if (node && typeof node === \"object\") {\r\n for (const [key, value] of Object.entries(node)) {\r\n if (key === \"$ref\" && typeof value === \"string\") {\r\n const name = refName(value);\r\n if (name) out.add(name);\r\n } else {\r\n collectRefs(value, out);\r\n }\r\n }\r\n }\r\n return out;\r\n}\r\n\r\nexport interface ClosureResult {\r\n /** Definition name -> schema, for the full dependency closure of the roots. */\r\n schemas: Map<string, JsonSchemaNode>;\r\n /** Names whose bodies were replaced by generic-object stubs via trimming. */\r\n stubbed: Set<string>;\r\n}\r\n\r\nfunction stubSchema(reason: string): JsonSchemaNode {\r\n return {\r\n type: \"object\",\r\n additionalProperties: true,\r\n description: reason,\r\n };\r\n}\r\n\r\n/**\r\n * `ResourceList` in fhir.schema.json is a oneOf over every resource type\r\n * (150-200 entries), referenced by e.g. `Bundle.entry.resource` and\r\n * `DomainResource.contained`. Following it verbatim would drag the entire\r\n * specification into every output, so it is narrowed to the resource types\r\n * actually requested (plus Bundle/OperationOutcome, which the generated REST\r\n * paths always use).\r\n */\r\nfunction narrowedResourceList(included: string[]): JsonSchemaNode {\r\n return {\r\n oneOf: included.map((name) => ({ $ref: `${REF_PREFIX}${name}` })),\r\n description:\r\n \"A contained/bundled FHIR resource. Narrowed by fhir-openapi-translator to the \" +\r\n \"resource types requested at generation time; servers may return other types.\",\r\n };\r\n}\r\n\r\n/**\r\n * Extracts the transitive dependency closure of `roots` from the registry via\r\n * breadth-first search. Cycles are fine: every definition is visited once and\r\n * mutual references remain as $refs. Trim options replace selected\r\n * definitions with stubs whose dependencies are not followed.\r\n */\r\nexport function extractClosure(\r\n registry: DefinitionRegistry,\r\n roots: string[],\r\n trim: TrimOptions = {},\r\n resourceListNarrowing: string[] = roots,\r\n): ClosureResult {\r\n const schemas = new Map<string, JsonSchemaNode>();\r\n const stubbed = new Set<string>();\r\n const maxDepth = trim.maxDepth;\r\n\r\n let frontier = [...new Set(roots)];\r\n let depth = 0;\r\n while (frontier.length > 0) {\r\n const next: string[] = [];\r\n for (const name of frontier) {\r\n if (schemas.has(name)) continue;\r\n const original = registry.definitions.get(name);\r\n if (!original) {\r\n throw new Error(`Unknown FHIR definition: ${name}`);\r\n }\r\n\r\n let schema: JsonSchemaNode = original;\r\n let followDeps = true;\r\n if (trim.excludeNarrative && name === \"Narrative\") {\r\n schema = stubSchema(\r\n \"Human-readable narrative. Excluded from this specification (--exclude-narrative).\",\r\n );\r\n followDeps = false;\r\n stubbed.add(name);\r\n } else if (maxDepth !== undefined && depth > maxDepth) {\r\n schema = stubSchema(\r\n `FHIR ${name}. Pruned from this specification by --max-depth ${maxDepth}.`,\r\n );\r\n followDeps = false;\r\n stubbed.add(name);\r\n } else if (name === \"ResourceList\") {\r\n schema = narrowedResourceList(\r\n [...new Set(resourceListNarrowing)].filter((n) => registry.definitions.has(n)),\r\n );\r\n }\r\n\r\n schemas.set(name, schema);\r\n if (followDeps) {\r\n for (const dep of collectRefs(schema)) {\r\n if (!schemas.has(dep)) next.push(dep);\r\n }\r\n }\r\n }\r\n frontier = next;\r\n depth++;\r\n }\r\n\r\n return { schemas, stubbed };\r\n}\r\n","import {\r\n loadOperationDefinitions,\r\n type MinOperationDefinition,\r\n type MinOperationParameter,\r\n} from \"./definitions.js\";\r\nimport type { FhirVersion, JsonSchemaNode } from \"./types.js\";\r\n\r\nconst FHIR_JSON = \"application/fhir+json\";\r\nconst SCHEMAS = \"#/components/schemas/\";\r\n\r\n/** FHIR primitive type names are lowercase-first; complex types are not. */\r\nfunction isPrimitive(type: string | undefined): boolean {\r\n return !!type && type.charAt(0) === type.charAt(0).toLowerCase();\r\n}\r\n\r\nfunction camelCode(code: string): string {\r\n return code.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());\r\n}\r\n\r\nfunction ref(schema: string) {\r\n return { $ref: `${SCHEMAS}${schema}` };\r\n}\r\n\r\nfunction fhirContent(schema: string) {\r\n return { [FHIR_JSON]: { schema: ref(schema) } };\r\n}\r\n\r\nfunction errorResponses() {\r\n return {\r\n default: {\r\n description: \"Error, with an OperationOutcome describing the problem\",\r\n content: fhirContent(\"OperationOutcome\"),\r\n },\r\n };\r\n}\r\n\r\nexport interface OperationPathsResult {\r\n paths: Record<string, JsonSchemaNode>;\r\n /** Schema names the operations reference, to add to the closure roots. */\r\n extraSchemaRoots: string[];\r\n}\r\n\r\n/**\r\n * Emits paths for the standard FHIR operations applicable to one resource\r\n * type, from the vendored OperationDefinitions.\r\n *\r\n * Method selection is deterministic per operation: GET with query parameters\r\n * when every `in` parameter is a primitive type (the spec-legal simple\r\n * invocation, and how e.g. $everything is used in practice); otherwise POST\r\n * with a Parameters request body. System-level-only operations are not\r\n * resource-scoped and are out of scope here.\r\n */\r\nexport interface OperationPathOptions {\r\n /** Maps a resource type to the schema name to reference (profile-aware). */\r\n schemaFor?: (resourceType: string) => string;\r\n /**\r\n * Restrict to operations whose code or canonical URL is in this set (e.g.\r\n * from a CapabilityStatement). Default: all operations applicable to the\r\n * resource.\r\n */\r\n only?: ReadonlySet<string>;\r\n}\r\n\r\nexport function buildOperationPaths(\r\n fhirVersion: FhirVersion,\r\n resource: string,\r\n knownResources: ReadonlySet<string>,\r\n options: OperationPathOptions = {},\r\n): OperationPathsResult {\r\n const schemaFor = options.schemaFor ?? ((r) => r);\r\n const paths: Record<string, JsonSchemaNode> = {};\r\n const extraSchemaRoots = new Set<string>();\r\n\r\n const applicable = loadOperationDefinitions(fhirVersion).filter(\r\n (op) =>\r\n (op.type || op.instance) &&\r\n (op.resource.includes(resource) || op.resource.includes(\"Resource\")) &&\r\n (!options.only || options.only.has(op.code) || options.only.has(op.url)),\r\n );\r\n\r\n for (const op of applicable) {\r\n const inParams = op.parameters.filter((p) => p.use === \"in\");\r\n const useGet = inParams.every((p) => isPrimitive(p.type));\r\n const responseSchema = schemaFor(responseSchemaName(op, knownResources));\r\n extraSchemaRoots.add(responseSchema);\r\n if (!useGet) extraSchemaRoots.add(\"Parameters\");\r\n\r\n const buildOperation = (level: \"Type\" | \"Instance\") => {\r\n const operation: JsonSchemaNode = {\r\n tags: [resource],\r\n summary: `$${op.code} (${level.toLowerCase()} level)`,\r\n ...(op.description ? { description: op.description } : {}),\r\n operationId: `${camelCode(op.code)}${resource}${level}`,\r\n externalDocs: { url: op.url },\r\n responses: {\r\n \"200\": {\r\n description: `Result of the $${op.code} operation`,\r\n content: fhirContent(responseSchema),\r\n },\r\n ...errorResponses(),\r\n },\r\n };\r\n if (useGet) {\r\n if (inParams.length > 0) operation.parameters = inParams.map(queryParameter);\r\n } else {\r\n operation.requestBody = {\r\n required: inParams.some((p) => p.min >= 1),\r\n content: fhirContent(\"Parameters\"),\r\n };\r\n }\r\n return operation;\r\n };\r\n\r\n const method = useGet ? \"get\" : \"post\";\r\n if (op.type) {\r\n paths[`/${resource}/$${op.code}`] = { [method]: buildOperation(\"Type\") };\r\n }\r\n if (op.instance) {\r\n paths[`/${resource}/{id}/$${op.code}`] = {\r\n parameters: [\r\n {\r\n name: \"id\",\r\n in: \"path\",\r\n required: true,\r\n description: `Logical id of the ${resource}`,\r\n schema: { type: \"string\", pattern: \"^[A-Za-z0-9\\\\-\\\\.]{1,64}$\" },\r\n },\r\n ],\r\n [method]: buildOperation(\"Instance\"),\r\n };\r\n }\r\n }\r\n\r\n return { paths, extraSchemaRoots: [...extraSchemaRoots].sort() };\r\n}\r\n\r\n/**\r\n * Exactly one `out` parameter named `return` typed as a resource maps to that\r\n * resource; every other output shape is a Parameters resource.\r\n */\r\nfunction responseSchemaName(\r\n op: MinOperationDefinition,\r\n knownResources: ReadonlySet<string>,\r\n): string {\r\n const outParams = op.parameters.filter((p) => p.use === \"out\");\r\n if (outParams.length === 1) {\r\n const only = outParams[0]!;\r\n if (only.name === \"return\" && only.type && knownResources.has(only.type)) {\r\n return only.type;\r\n }\r\n }\r\n return \"Parameters\";\r\n}\r\n\r\nfunction queryParameter(param: MinOperationParameter): JsonSchemaNode {\r\n const isArray = param.max === \"*\" || Number(param.max) > 1;\r\n const base: JsonSchemaNode = { type: \"string\" };\r\n return {\r\n name: param.name,\r\n in: \"query\",\r\n required: param.min >= 1,\r\n ...(param.documentation ? { description: param.documentation } : {}),\r\n // FHIR primitives serialize as strings in URLs; the FHIR type is kept\r\n // as an extension, consistent with search parameters.\r\n schema: isArray ? { type: \"array\", items: base } : base,\r\n ...(isArray ? { explode: true } : {}),\r\n \"x-fhir-type\": param.type,\r\n };\r\n}\r\n","import { loadSearchParameters } from \"./definitions.js\";\r\nimport type { FhirVersion, JsonSchemaNode } from \"./types.js\";\r\n\r\nconst FHIR_JSON = \"application/fhir+json\";\r\nconst SCHEMAS = \"#/components/schemas/\";\r\nconst PARAMETERS = \"#/components/parameters/\";\r\n\r\nfunction ref(schema: string) {\r\n return { $ref: `${SCHEMAS}${schema}` };\r\n}\r\n\r\nfunction fhirContent(schema: string) {\r\n return { [FHIR_JSON]: { schema: ref(schema) } };\r\n}\r\n\r\nfunction errorResponses() {\r\n return {\r\n default: {\r\n description: \"Error, with an OperationOutcome describing the problem\",\r\n content: fhirContent(\"OperationOutcome\"),\r\n },\r\n };\r\n}\r\n\r\nfunction idParameter(name: string, description: string) {\r\n return {\r\n name,\r\n in: \"path\",\r\n required: true,\r\n description,\r\n schema: { type: \"string\", pattern: \"^[A-Za-z0-9\\\\-\\\\.]{1,64}$\" },\r\n };\r\n}\r\n\r\n/**\r\n * Search parameters common to all resources plus the standard search result\r\n * parameters, emitted once under components/parameters and $ref'd from every\r\n * search operation. All are strings except _count: FHIR search values carry\r\n * prefixes/modifiers (e.g. `ge2020-01-01`, `:exact`), so stricter types would\r\n * reject valid requests.\r\n */\r\nconst COMMON_SEARCH_PARAMETERS: Record<string, { description: string; schema: JsonSchemaNode }> = {\r\n _id: { description: \"Logical id of this artifact\", schema: { type: \"string\" } },\r\n _lastUpdated: {\r\n description: \"When the resource version last changed (supports date prefixes, e.g. ge2021-01-01)\",\r\n schema: { type: \"string\" },\r\n },\r\n _tag: { description: \"Tags applied to this resource\", schema: { type: \"string\" } },\r\n _profile: { description: \"Profiles this resource claims to conform to\", schema: { type: \"string\" } },\r\n _security: { description: \"Security labels applied to this resource\", schema: { type: \"string\" } },\r\n _text: { description: \"Search on the narrative of the resource\", schema: { type: \"string\" } },\r\n _content: { description: \"Search on the entire content of the resource\", schema: { type: \"string\" } },\r\n _sort: { description: \"Sort order of the results (comma-separated parameter names, '-' prefix for descending)\", schema: { type: \"string\" } },\r\n _count: { description: \"Maximum number of results per page\", schema: { type: \"integer\", minimum: 0 } },\r\n _include: { description: \"Include referenced resources in the results\", schema: { type: \"string\" } },\r\n _revinclude: { description: \"Include resources that reference the matches in the results\", schema: { type: \"string\" } },\r\n _summary: {\r\n description: \"Return only a portion of each resource\",\r\n schema: { type: \"string\", enum: [\"true\", \"text\", \"data\", \"count\", \"false\"] },\r\n },\r\n _total: {\r\n description: \"Requested precision of the Bundle.total\",\r\n schema: { type: \"string\", enum: [\"none\", \"estimate\", \"accurate\"] },\r\n },\r\n _elements: { description: \"Restrict returned elements (comma-separated element names)\", schema: { type: \"string\" } },\r\n};\r\n\r\nexport function commonSearchParameterComponents(): Record<string, JsonSchemaNode> {\r\n const out: Record<string, JsonSchemaNode> = {};\r\n for (const [name, { description, schema }] of Object.entries(COMMON_SEARCH_PARAMETERS)) {\r\n out[name] = { name, in: \"query\", required: false, description, schema };\r\n }\r\n return out;\r\n}\r\n\r\n/** FHIR RESTful interaction codes (CapabilityStatement.rest.resource.interaction). */\r\nexport type FhirInteraction =\r\n | \"read\"\r\n | \"vread\"\r\n | \"update\"\r\n | \"patch\"\r\n | \"delete\"\r\n | \"history-instance\"\r\n | \"history-type\"\r\n | \"create\"\r\n | \"search-type\";\r\n\r\nexport const ALL_INTERACTIONS: readonly FhirInteraction[] = [\r\n \"read\",\r\n \"vread\",\r\n \"update\",\r\n \"patch\",\r\n \"delete\",\r\n \"history-instance\",\r\n \"history-type\",\r\n \"create\",\r\n \"search-type\",\r\n];\r\n\r\nexport interface ResourcePathOptions {\r\n /** Emit only these interactions. Default: all of them. */\r\n interactions?: ReadonlySet<FhirInteraction>;\r\n /**\r\n * Restrict resource-specific search parameters to these codes (the common\r\n * result parameters like `_id`/`_count` are always kept). Default: all\r\n * search parameters the FHIR version defines for the resource.\r\n */\r\n searchParamCodes?: ReadonlySet<string>;\r\n}\r\n\r\nfunction resourceSearchParameters(\r\n fhirVersion: FhirVersion,\r\n resource: string,\r\n only?: ReadonlySet<string>,\r\n): JsonSchemaNode[] {\r\n return loadSearchParameters(fhirVersion)\r\n .filter((sp) => sp.base.includes(resource) && (!only || only.has(sp.code)))\r\n .sort((a, b) => a.code.localeCompare(b.code))\r\n .map((sp) => ({\r\n name: sp.code,\r\n in: \"query\",\r\n required: false,\r\n description: sp.description,\r\n // FHIR search values carry prefixes and modifiers, so all are strings.\r\n schema: { type: \"string\" },\r\n \"x-fhir-search-type\": sp.type,\r\n }));\r\n}\r\n\r\nconst historyParameters = () => [\r\n { $ref: `${PARAMETERS}_count` },\r\n {\r\n name: \"_since\",\r\n in: \"query\",\r\n required: false,\r\n description: \"Only include versions created at or after this instant\",\r\n schema: { type: \"string\", format: \"date-time\" },\r\n },\r\n];\r\n\r\n/**\r\n * FHIR RESTful API interactions for one resource type. By default every\r\n * standard interaction (search, create, read, vread, update, patch, delete,\r\n * and instance/type history) is emitted; `options.interactions` restricts the\r\n * set (e.g. from a CapabilityStatement), and empty path items are dropped.\r\n */\r\nexport function buildResourcePaths(\r\n fhirVersion: FhirVersion,\r\n resource: string,\r\n /** Schema referenced by request/response bodies; a profile name or the resource. */\r\n schemaName: string = resource,\r\n options: ResourcePathOptions = {},\r\n): Record<string, JsonSchemaNode> {\r\n const want = (interaction: FhirInteraction) =>\r\n !options.interactions || options.interactions.has(interaction);\r\n const body = () => fhirContent(schemaName);\r\n const idParam = () => idParameter(\"id\", `Logical id of the ${resource}`);\r\n const paths: Record<string, JsonSchemaNode> = {};\r\n\r\n const typeItem: Record<string, JsonSchemaNode> = {};\r\n if (want(\"search-type\")) {\r\n typeItem.get = {\r\n tags: [resource],\r\n summary: `Search for ${resource} resources`,\r\n operationId: `search${resource}`,\r\n parameters: [\r\n ...Object.keys(COMMON_SEARCH_PARAMETERS).map((name) => ({ $ref: `${PARAMETERS}${name}` })),\r\n ...resourceSearchParameters(fhirVersion, resource, options.searchParamCodes),\r\n ],\r\n responses: {\r\n \"200\": {\r\n description: `Bundle of matching ${resource} resources`,\r\n content: fhirContent(\"Bundle\"),\r\n },\r\n ...errorResponses(),\r\n },\r\n };\r\n }\r\n if (want(\"create\")) {\r\n typeItem.post = {\r\n tags: [resource],\r\n summary: `Create a ${resource} resource`,\r\n operationId: `create${resource}`,\r\n requestBody: { required: true, content: body() },\r\n responses: {\r\n \"201\": { description: `${resource} created`, content: body() },\r\n ...errorResponses(),\r\n },\r\n };\r\n }\r\n if (Object.keys(typeItem).length > 0) paths[`/${resource}`] = typeItem;\r\n\r\n const instanceItem: Record<string, JsonSchemaNode> = {};\r\n if (want(\"read\")) {\r\n instanceItem.get = {\r\n tags: [resource],\r\n summary: `Read a ${resource} resource by id`,\r\n operationId: `read${resource}`,\r\n responses: {\r\n \"200\": { description: `The ${resource} resource`, content: body() },\r\n ...errorResponses(),\r\n },\r\n };\r\n }\r\n if (want(\"update\")) {\r\n instanceItem.put = {\r\n tags: [resource],\r\n summary: `Update (or create) a ${resource} resource by id`,\r\n operationId: `update${resource}`,\r\n parameters: [\r\n {\r\n name: \"If-Match\",\r\n in: \"header\",\r\n required: false,\r\n description: \"Version-aware update: weak ETag of the version being updated\",\r\n schema: { type: \"string\" },\r\n },\r\n ],\r\n requestBody: { required: true, content: body() },\r\n responses: {\r\n \"200\": { description: `${resource} updated`, content: body() },\r\n \"201\": { description: `${resource} created`, content: body() },\r\n ...errorResponses(),\r\n },\r\n };\r\n }\r\n if (want(\"patch\")) {\r\n instanceItem.patch = {\r\n tags: [resource],\r\n summary: `Patch a ${resource} resource by id`,\r\n operationId: `patch${resource}`,\r\n requestBody: {\r\n required: true,\r\n content: {\r\n \"application/json-patch+json\": {\r\n schema: {\r\n type: \"array\",\r\n items: { type: \"object\", additionalProperties: true },\r\n description: \"JSON Patch operations (RFC 6902)\",\r\n },\r\n },\r\n },\r\n },\r\n responses: {\r\n \"200\": { description: `${resource} patched`, content: body() },\r\n ...errorResponses(),\r\n },\r\n };\r\n }\r\n if (want(\"delete\")) {\r\n instanceItem.delete = {\r\n tags: [resource],\r\n summary: `Delete a ${resource} resource by id`,\r\n operationId: `delete${resource}`,\r\n responses: { \"204\": { description: `${resource} deleted` }, ...errorResponses() },\r\n };\r\n }\r\n if (Object.keys(instanceItem).length > 0) {\r\n paths[`/${resource}/{id}`] = { parameters: [idParam()], ...instanceItem };\r\n }\r\n\r\n if (want(\"history-instance\")) {\r\n paths[`/${resource}/{id}/_history`] = {\r\n parameters: [idParam()],\r\n get: {\r\n tags: [resource],\r\n summary: `History of a ${resource} instance`,\r\n operationId: `history${resource}Instance`,\r\n parameters: historyParameters(),\r\n responses: {\r\n \"200\": { description: \"History bundle\", content: fhirContent(\"Bundle\") },\r\n ...errorResponses(),\r\n },\r\n },\r\n };\r\n }\r\n if (want(\"vread\")) {\r\n paths[`/${resource}/{id}/_history/{vid}`] = {\r\n parameters: [idParam(), idParameter(\"vid\", \"Version id of the resource\")],\r\n get: {\r\n tags: [resource],\r\n summary: `Read a specific version of a ${resource} resource`,\r\n operationId: `vread${resource}`,\r\n responses: {\r\n \"200\": { description: `The ${resource} resource version`, content: body() },\r\n ...errorResponses(),\r\n },\r\n },\r\n };\r\n }\r\n if (want(\"history-type\")) {\r\n paths[`/${resource}/_history`] = {\r\n get: {\r\n tags: [resource],\r\n summary: `History across all ${resource} resources`,\r\n operationId: `history${resource}Type`,\r\n parameters: historyParameters(),\r\n responses: {\r\n \"200\": { description: \"History bundle\", content: fhirContent(\"Bundle\") },\r\n ...errorResponses(),\r\n },\r\n },\r\n };\r\n }\r\n\r\n return paths;\r\n}\r\n","import { buildRegistryFromSchemaJson } from \"./backends/schemaJson.js\";\r\nimport { buildRegistryFromStructureDefinitions } from \"./backends/structureDefinition.js\";\r\nimport type { Capability, ResourceCapability } from \"./capability.js\";\r\nimport { convertSchema } from \"./emit/schema.js\";\r\nimport { loadIgSync, type IgContext } from \"./ig/package.js\";\r\nimport { applyProfile, buildCoreValueSetFallback } from \"./ig/profile.js\";\r\nimport { extractClosure, type DefinitionRegistry } from \"./ir/registry.js\";\r\nimport { buildOperationPaths } from \"./operations.js\";\r\nimport { buildResourcePaths, commonSearchParameterComponents } from \"./paths.js\";\r\nimport {\r\n FHIR_VERSION_NUMBERS,\r\n FHIR_VERSIONS,\r\n type FhirVersion,\r\n type GenerateOptions,\r\n type JsonSchemaNode,\r\n type OpenApiDocument,\r\n type SourceBackend,\r\n} from \"./types.js\";\r\n\r\nfunction buildRegistry(fhirVersion: FhirVersion, source: SourceBackend): DefinitionRegistry {\r\n return source === \"structure-def\"\r\n ? buildRegistryFromStructureDefinitions(fhirVersion)\r\n : buildRegistryFromSchemaJson(fhirVersion);\r\n}\r\n\r\nfunction resolveResourceName(registry: DefinitionRegistry, requested: string): string {\r\n const match = registry.resourceNames.find(\r\n (name) => name.toLowerCase() === requested.toLowerCase(),\r\n );\r\n if (!match) {\r\n throw new Error(\r\n `Unknown FHIR ${registry.fhirVersion.toUpperCase()} resource: \"${requested}\". ` +\r\n `Run \"fhir-oas list --fhir-version ${registry.fhirVersion}\" to see available resources.`,\r\n );\r\n }\r\n return match;\r\n}\r\n\r\n/** Lists the resource type names available for a FHIR version. */\r\nexport function listResources(\r\n fhirVersion: FhirVersion,\r\n source: SourceBackend = \"schema-json\",\r\n): string[] {\r\n assertFhirVersion(fhirVersion);\r\n return [...buildRegistry(fhirVersion, source).resourceNames].sort();\r\n}\r\n\r\nfunction assertFhirVersion(fhirVersion: string): asserts fhirVersion is FhirVersion {\r\n if (!FHIR_VERSIONS.includes(fhirVersion as FhirVersion)) {\r\n throw new Error(\r\n `Unsupported FHIR version \"${fhirVersion}\". Supported: ${FHIR_VERSIONS.join(\", \")}`,\r\n );\r\n }\r\n}\r\n\r\n/** The FHIR version a CapabilityStatement fhirVersion string belongs to. */\r\nfunction assertCapabilityFhirVersion(capability: Capability, fhirVersion: FhirVersion): void {\r\n const declared = capability.fhirVersion;\r\n if (!declared) return; // statement omitted it; trust the caller's --fhir-version\r\n const expectedMajor = FHIR_VERSION_NUMBERS[fhirVersion].split(\".\").slice(0, 2).join(\".\");\r\n // R4 (4.0.x) and R4B (4.3.x) share major 4 but differ in minor; compare the\r\n // major.minor prefix, tolerating the statement carrying only \"4.0\" etc.\r\n const declaredPrefix = declared.split(\".\").slice(0, 2).join(\".\");\r\n if (declaredPrefix && expectedMajor && declaredPrefix !== expectedMajor) {\r\n throw new Error(\r\n `CapabilityStatement targets FHIR ${declared}, but generation is for ` +\r\n `${fhirVersion.toUpperCase()} (${FHIR_VERSION_NUMBERS[fhirVersion]}). ` +\r\n `Set --fhir-version to match the server.`,\r\n );\r\n }\r\n}\r\n\r\n/** Resolves options.ig to an IgContext: a local path is loaded synchronously. */\r\nfunction resolveIg(options: GenerateOptions): IgContext {\r\n const ig = options.ig;\r\n if (!ig) throw new Error(\"--profile requires --ig: point at the IG package.\");\r\n if (typeof ig === \"string\") {\r\n return loadIgSync(ig, { coreValueSetFallback: buildCoreValueSetFallback(options.fhirVersion) });\r\n }\r\n return ig as IgContext;\r\n}\r\n\r\n/**\r\n * Generates an OpenAPI document covering the requested FHIR resources: their\r\n * full schema dependency closure plus the standard FHIR RESTful interactions.\r\n */\r\nexport function generateOpenApi(options: GenerateOptions): OpenApiDocument {\r\n assertFhirVersion(options.fhirVersion);\r\n const openApiVersion = options.openApiVersion ?? \"3.0.3\";\r\n if (openApiVersion !== \"3.0.3\" && openApiVersion !== \"3.1.0\") {\r\n throw new Error(`Unsupported OpenAPI version \"${openApiVersion}\". Supported: 3.0.3, 3.1.0`);\r\n }\r\n\r\n const registry = buildRegistry(options.fhirVersion, options.source ?? \"schema-json\");\r\n\r\n // A CapabilityStatement restricts generation to one server's declared\r\n // surface: which resources, interactions, search params, and operations.\r\n const capability = options.capability as Capability | undefined;\r\n const capabilityByResource = new Map<string, ResourceCapability>();\r\n if (capability) {\r\n assertCapabilityFhirVersion(capability, options.fhirVersion);\r\n for (const cap of capability.resources) {\r\n const match = registry.resourceNames.find(\r\n (name) => name.toLowerCase() === cap.type.toLowerCase(),\r\n );\r\n // Skip resource types the FHIR version doesn't define (custom/unknown).\r\n if (match) capabilityByResource.set(match, cap);\r\n }\r\n }\r\n\r\n const requested = (options.resources ?? []).map((r) => resolveResourceName(registry, r));\r\n if (requested.length === 0 && capabilityByResource.size === 0) {\r\n throw new Error(\r\n capability\r\n ? \"The CapabilityStatement declares no resources this FHIR version supports.\"\r\n : \"At least one FHIR resource name (or a --capability statement) is required\",\r\n );\r\n }\r\n\r\n // Apply IG profiles (if any) into the registry, mapping each profiled base\r\n // resource to its emitted schema name. The base resource is auto-included.\r\n const schemaByResource = new Map<string, string>();\r\n // In capability mode, the resource set is the statement's (optionally\r\n // narrowed to the explicitly requested ones); otherwise it's the requested.\r\n const resourceSet = new Set<string>(\r\n capability\r\n ? requested.length > 0\r\n ? requested.filter((r) => capabilityByResource.has(r))\r\n : [...capabilityByResource.keys()]\r\n : requested,\r\n );\r\n if (options.profiles?.length) {\r\n const ig = resolveIg(options);\r\n if (ig.fhirVersion !== options.fhirVersion) {\r\n throw new Error(\r\n `IG package \"${ig.name}\" targets FHIR ${ig.fhirVersion.toUpperCase()}, but generation ` +\r\n `is for ${options.fhirVersion.toUpperCase()}. Use --fhir-version ${ig.fhirVersion}.`,\r\n );\r\n }\r\n for (const profileId of options.profiles) {\r\n const applied = applyProfile(ig, profileId, registry);\r\n const base = resolveResourceName(registry, applied.resourceType);\r\n if (schemaByResource.has(base)) {\r\n throw new Error(\r\n `Multiple profiles target ${base}; generate one profiled resource per run.`,\r\n );\r\n }\r\n schemaByResource.set(base, applied.schemaName);\r\n resourceSet.add(base);\r\n }\r\n } else if (options.ig) {\r\n throw new Error(\"--ig requires --profile: name the profile(s) to apply.\");\r\n }\r\n\r\n const resources = [...resourceSet];\r\n const schemaFor = (resource: string) => schemaByResource.get(resource) ?? resource;\r\n\r\n // Operations: in capability mode, emit exactly the operations each resource\r\n // declares (mapped to known OperationDefinitions); otherwise --operations\r\n // emits every applicable operation.\r\n const operationPaths: Record<string, JsonSchemaNode> = {};\r\n const operationRoots: string[] = [];\r\n if (capability || options.operations) {\r\n const knownResources = new Set(registry.resourceNames);\r\n for (const resource of resources) {\r\n const cap = capabilityByResource.get(resource);\r\n if (capability && (!cap || cap.operations.size === 0)) continue;\r\n const result = buildOperationPaths(options.fhirVersion, resource, knownResources, {\r\n schemaFor,\r\n only: cap?.operations,\r\n });\r\n Object.assign(operationPaths, result.paths);\r\n operationRoots.push(...result.extraSchemaRoots);\r\n }\r\n }\r\n\r\n // Closure roots use the profiled schema name where a resource is profiled,\r\n // so the base schema is only emitted if something else references it.\r\n // Bundle and OperationOutcome are always present: search/history responses\r\n // are Bundles and every error response is an OperationOutcome.\r\n const roots = [\r\n ...new Set([...resources.map(schemaFor), \"Bundle\", \"OperationOutcome\", ...operationRoots]),\r\n ];\r\n const { schemas } = extractClosure(registry, roots, options.trim ?? {}, roots);\r\n\r\n const componentSchemas: Record<string, JsonSchemaNode> = {};\r\n const convertOptions = { noEnums: options.trim?.noEnums };\r\n for (const name of [...schemas.keys()].sort()) {\r\n componentSchemas[name] = convertSchema(schemas.get(name)!, openApiVersion, convertOptions);\r\n }\r\n\r\n const paths: Record<string, JsonSchemaNode> = {};\r\n for (const resource of resources) {\r\n const cap = capabilityByResource.get(resource);\r\n Object.assign(\r\n paths,\r\n buildResourcePaths(options.fhirVersion, resource, schemaFor(resource), {\r\n interactions: cap?.interactions,\r\n searchParamCodes: cap?.searchParamCodes,\r\n }),\r\n );\r\n }\r\n Object.assign(paths, operationPaths);\r\n\r\n const fhirNumber = FHIR_VERSION_NUMBERS[options.fhirVersion];\r\n const document: OpenApiDocument = {\r\n openapi: openApiVersion,\r\n info: {\r\n title:\r\n options.title ??\r\n `FHIR ${options.fhirVersion.toUpperCase()} REST API: ${resources.join(\", \")}`,\r\n description:\r\n `OpenAPI definition of the FHIR ${options.fhirVersion.toUpperCase()} (${fhirNumber}) ` +\r\n `RESTful interactions and JSON schemas for: ${resources.join(\", \")}. ` +\r\n \"Generated by fhir-openapi-translator from the official HL7 FHIR definitions. \" +\r\n \"Schema validity does not imply full FHIR conformance: profiles, terminology \" +\r\n \"bindings, and FHIRPath invariants are not represented.\",\r\n version: fhirNumber,\r\n },\r\n ...(options.baseUrl ? { servers: [{ url: options.baseUrl }] } : {}),\r\n tags: resources.map((resource) => ({\r\n name: resource,\r\n description: `Operations on the ${resource} resource`,\r\n })),\r\n paths,\r\n components: {\r\n parameters: commonSearchParameterComponents(),\r\n schemas: componentSchemas,\r\n },\r\n };\r\n return document;\r\n}\r\n","import fs from \"node:fs\";\r\nimport type { FhirInteraction } from \"./paths.js\";\r\n\r\n/**\r\n * Loads and parses a FHIR CapabilityStatement (a server's `/metadata`) into a\r\n * per-resource description of what the server actually supports, so a spec can\r\n * be generated to match exactly one server's surface rather than the full\r\n * standard interaction set.\r\n */\r\n\r\nexport interface ResourceCapability {\r\n type: string;\r\n interactions: Set<FhirInteraction>;\r\n /** Search parameter codes the server declares for this resource. */\r\n searchParamCodes: Set<string>;\r\n /** Operation codes and canonical URLs the server declares for this resource. */\r\n operations: Set<string>;\r\n}\r\n\r\nexport interface Capability {\r\n /** FHIR version string from the statement, e.g. \"4.0.1\" (may be undefined). */\r\n fhirVersion?: string;\r\n resources: ResourceCapability[];\r\n}\r\n\r\ninterface RawInteraction {\r\n code?: string;\r\n}\r\ninterface RawSearchParam {\r\n name?: string;\r\n}\r\ninterface RawOperation {\r\n name?: string;\r\n definition?: string;\r\n}\r\ninterface RawResource {\r\n type?: string;\r\n interaction?: RawInteraction[];\r\n searchParam?: RawSearchParam[];\r\n operation?: RawOperation[];\r\n}\r\ninterface RawCapabilityStatement {\r\n resourceType?: string;\r\n fhirVersion?: string;\r\n rest?: { mode?: string; resource?: RawResource[] }[];\r\n}\r\n\r\nconst KNOWN_INTERACTIONS = new Set<string>([\r\n \"read\",\r\n \"vread\",\r\n \"update\",\r\n \"patch\",\r\n \"delete\",\r\n \"history-instance\",\r\n \"history-type\",\r\n \"create\",\r\n \"search-type\",\r\n]);\r\n\r\nexport interface LoadCapabilityOptions {\r\n fetchImpl?: typeof fetch;\r\n}\r\n\r\n/**\r\n * Loads a CapabilityStatement from a local JSON file or an http(s) URL. A URL\r\n * that does not already end in `/metadata` has it appended, so a base server\r\n * URL works directly.\r\n */\r\nexport async function loadCapabilityStatement(\r\n input: string,\r\n options: LoadCapabilityOptions = {},\r\n): Promise<Capability> {\r\n let json: unknown;\r\n if (/^https?:\\/\\//i.test(input)) {\r\n const url = /\\/metadata\\/?$/.test(input) ? input : `${input.replace(/\\/$/, \"\")}/metadata`;\r\n const doFetch = options.fetchImpl ?? fetch;\r\n let response: Response;\r\n try {\r\n response = await doFetch(url, { headers: { Accept: \"application/fhir+json\" } });\r\n } catch (cause) {\r\n throw new Error(\r\n `Failed to fetch CapabilityStatement from ${url}: ${(cause as Error).message}. ` +\r\n `Save it locally (curl -H 'Accept: application/fhir+json' ${url} > metadata.json) ` +\r\n `and pass the file instead.`,\r\n );\r\n }\r\n if (!response.ok) {\r\n throw new Error(`CapabilityStatement request to ${url} returned ${response.status}.`);\r\n }\r\n json = await response.json();\r\n } else {\r\n if (!fs.existsSync(input)) throw new Error(`No such CapabilityStatement file: ${input}`);\r\n json = JSON.parse(fs.readFileSync(input, \"utf8\"));\r\n }\r\n return parseCapabilityStatement(json);\r\n}\r\n\r\nexport function parseCapabilityStatement(json: unknown): Capability {\r\n const statement = json as RawCapabilityStatement;\r\n if (statement?.resourceType !== \"CapabilityStatement\") {\r\n throw new Error(\r\n `Expected a CapabilityStatement resource, got \"${statement?.resourceType ?? \"unknown\"}\".`,\r\n );\r\n }\r\n\r\n // Merge resource entries across all `rest` blocks with mode \"server\".\r\n const byType = new Map<string, ResourceCapability>();\r\n for (const rest of statement.rest ?? []) {\r\n if (rest.mode && rest.mode !== \"server\") continue;\r\n for (const raw of rest.resource ?? []) {\r\n if (!raw.type) continue;\r\n let cap = byType.get(raw.type);\r\n if (!cap) {\r\n cap = {\r\n type: raw.type,\r\n interactions: new Set(),\r\n searchParamCodes: new Set(),\r\n operations: new Set(),\r\n };\r\n byType.set(raw.type, cap);\r\n }\r\n for (const i of raw.interaction ?? []) {\r\n if (i.code && KNOWN_INTERACTIONS.has(i.code)) cap.interactions.add(i.code as FhirInteraction);\r\n }\r\n for (const sp of raw.searchParam ?? []) {\r\n if (sp.name) cap.searchParamCodes.add(sp.name);\r\n }\r\n for (const op of raw.operation ?? []) {\r\n if (op.name) cap.operations.add(op.name);\r\n if (op.definition) cap.operations.add(op.definition);\r\n }\r\n }\r\n }\r\n\r\n if (byType.size === 0) {\r\n throw new Error(\r\n \"CapabilityStatement declares no server resources (rest[].resource). Nothing to generate.\",\r\n );\r\n }\r\n\r\n return {\r\n fhirVersion: statement.fhirVersion,\r\n resources: [...byType.values()].sort((a, b) => a.type.localeCompare(b.type)),\r\n };\r\n}\r\n","import { Document, parseDocument, isMap, type YAMLMap } from \"yaml\";\r\nimport type { OpenApiDocument } from \"./types.js\";\r\n\r\nexport interface MergeOptions {\r\n /** Overwrite conflicting entries instead of failing. */\r\n force?: boolean;\r\n}\r\n\r\nexport interface MergeConflict {\r\n /** e.g. \"components.schemas.Patient\" or \"paths./Patient/{id}\" */\r\n location: string;\r\n}\r\n\r\nexport class MergeConflictError extends Error {\r\n constructor(public readonly conflicts: MergeConflict[]) {\r\n super(\r\n \"Refusing to overwrite existing, differing entries (use --force to overwrite):\\n\" +\r\n conflicts.map((c) => ` - ${c.location}`).join(\"\\n\"),\r\n );\r\n this.name = \"MergeConflictError\";\r\n }\r\n}\r\n\r\n/** Top-level maps whose entries are merged key-by-key. */\r\nconst MERGED_SECTIONS: [string, string][] = [\r\n [\"paths\", \"\"],\r\n [\"components\", \"schemas\"],\r\n [\"components\", \"parameters\"],\r\n];\r\n\r\nfunction deepEqual(a: unknown, b: unknown): boolean {\r\n return JSON.stringify(a) === JSON.stringify(b);\r\n}\r\n\r\n/**\r\n * `ResourceList` is narrowed to the resource types requested at generation\r\n * time (see ir/registry.ts), so two generated specs legitimately differ in\r\n * it. Merging unions the two narrowings instead of conflicting. Returns\r\n * undefined when either side is not the expected oneOf-of-$refs shape.\r\n */\r\nfunction unionResourceList(existing: unknown, generated: unknown): unknown | undefined {\r\n const refsOf = (node: unknown): string[] | undefined => {\r\n if (!node || typeof node !== \"object\" || Array.isArray(node)) return undefined;\r\n const oneOf = (node as { oneOf?: unknown }).oneOf;\r\n if (!Array.isArray(oneOf)) return undefined;\r\n const refs: string[] = [];\r\n for (const item of oneOf) {\r\n if (!item || typeof item !== \"object\" || Object.keys(item).length !== 1) return undefined;\r\n const ref = (item as { $ref?: unknown }).$ref;\r\n if (typeof ref !== \"string\") return undefined;\r\n refs.push(ref);\r\n }\r\n return refs;\r\n };\r\n const existingRefs = refsOf(existing);\r\n const generatedRefs = refsOf(generated);\r\n if (!existingRefs || !generatedRefs) return undefined;\r\n return {\r\n ...(generated as Record<string, unknown>),\r\n oneOf: [...new Set([...existingRefs, ...generatedRefs])].map(($ref) => ({ $ref })),\r\n };\r\n}\r\n\r\ninterface MergePlan {\r\n doc: ReturnType<typeof parseDocument>;\r\n conflicts: MergeConflict[];\r\n additions: { path: (string | number)[]; value: unknown }[];\r\n}\r\n\r\n/**\r\n * Parses the existing YAML and computes what a merge would do: entries to add\r\n * (absent from the file, or ResourceList unions) and conflicting entries\r\n * (present with different content). Shared by merge and check.\r\n */\r\nfunction computeMergePlan(\r\n generated: OpenApiDocument,\r\n existingText: string,\r\n options: MergeOptions,\r\n): MergePlan {\r\n const doc = parseDocument(existingText);\r\n if (doc.errors.length > 0) {\r\n throw new Error(`Cannot parse existing YAML: ${doc.errors[0]?.message}`);\r\n }\r\n if (doc.contents !== null && !isMap(doc.contents)) {\r\n throw new Error(\"Existing file is not a YAML mapping; refusing to merge\");\r\n }\r\n\r\n const existingVersion = doc.getIn([\"openapi\"]);\r\n const generatedVersion = generated.openapi;\r\n if (existingVersion !== undefined && String(existingVersion) !== String(generatedVersion)) {\r\n throw new Error(\r\n `OpenAPI version mismatch: existing file declares ${String(existingVersion)}, ` +\r\n `generating ${String(generatedVersion)}. Regenerate with a matching --openapi-version.`,\r\n );\r\n }\r\n\r\n const conflicts: MergeConflict[] = [];\r\n const additions: { path: (string | number)[]; value: unknown }[] = [];\r\n\r\n // Top-level scalars/objects that only apply when absent.\r\n for (const key of [\"openapi\", \"info\", \"servers\"]) {\r\n if (generated[key] !== undefined && doc.getIn([key]) === undefined) {\r\n additions.push({ path: [key], value: generated[key] });\r\n }\r\n }\r\n\r\n // Tags: append missing tag names.\r\n const generatedTags = (generated.tags ?? []) as { name: string }[];\r\n if (generatedTags.length > 0) {\r\n const existingTags = doc.toJS()?.tags as { name: string }[] | undefined;\r\n if (existingTags === undefined) {\r\n additions.push({ path: [\"tags\"], value: generatedTags });\r\n } else {\r\n const existingNames = new Set(existingTags.map((t) => t?.name));\r\n let index = existingTags.length;\r\n for (const tag of generatedTags) {\r\n if (!existingNames.has(tag.name)) {\r\n additions.push({ path: [\"tags\", index++], value: tag });\r\n }\r\n }\r\n }\r\n }\r\n\r\n for (const [section, subsection] of MERGED_SECTIONS) {\r\n const generatedSection = subsection\r\n ? ((generated[section] as Record<string, unknown> | undefined)?.[subsection] as\r\n | Record<string, unknown>\r\n | undefined)\r\n : (generated[section] as Record<string, unknown> | undefined);\r\n if (!generatedSection) continue;\r\n const basePath = subsection ? [section, subsection] : [section];\r\n\r\n for (const [key, value] of Object.entries(generatedSection)) {\r\n const existing = doc.getIn([...basePath, key], true);\r\n if (existing === undefined) {\r\n additions.push({ path: [...basePath, key], value });\r\n } else {\r\n const existingJs =\r\n typeof (existing as YAMLMap)?.toJS === \"function\"\r\n ? (existing as YAMLMap).toJS(doc)\r\n : existing;\r\n if (!deepEqual(existingJs, value)) {\r\n if (section === \"components\" && subsection === \"schemas\" && key === \"ResourceList\") {\r\n const union = unionResourceList(existingJs, value);\r\n if (union !== undefined) {\r\n if (!deepEqual(existingJs, union)) {\r\n additions.push({ path: [...basePath, key], value: union });\r\n }\r\n continue;\r\n }\r\n }\r\n conflicts.push({ location: [...basePath, key].join(\".\") });\r\n if (options.force) additions.push({ path: [...basePath, key], value });\r\n }\r\n }\r\n }\r\n }\r\n\r\n return { doc, conflicts, additions };\r\n}\r\n\r\n/**\r\n * Merges a generated OpenAPI document into existing YAML text, preserving the\r\n * existing file's comments, anchors, and key order. Generated entries are\r\n * added alongside existing content; an existing entry with different content\r\n * is a conflict (all conflicts are reported; nothing is written unless every\r\n * conflict is resolved by `force`). Identical entries are left untouched, so\r\n * re-running the generator is idempotent.\r\n */\r\nexport function mergeIntoYaml(\r\n generated: OpenApiDocument,\r\n existingText: string,\r\n options: MergeOptions = {},\r\n): string {\r\n const { doc, conflicts, additions } = computeMergePlan(generated, existingText, options);\r\n if (doc.contents === null) {\r\n // Empty file: treat as a fresh write.\r\n return stringifyDocument(generated);\r\n }\r\n if (conflicts.length > 0 && !options.force) {\r\n throw new MergeConflictError(conflicts);\r\n }\r\n for (const { path, value } of additions) {\r\n doc.setIn(path, doc.createNode(value));\r\n }\r\n return doc.toString({ lineWidth: 0 });\r\n}\r\n\r\nexport interface SpecDiff {\r\n /** Entries the file lacks (dot paths), e.g. \"components.schemas.Patient\". */\r\n missing: string[];\r\n /** Entries present in the file but differing from generation. */\r\n changed: string[];\r\n /** True when the file already contains exactly what generation produces. */\r\n inSync: boolean;\r\n}\r\n\r\n/**\r\n * Compares existing spec text against a generated document without writing\r\n * anything — the CI drift guard behind `fhir-oas check`. The file is in sync\r\n * when a merge would be a no-op: nothing to add, nothing conflicting.\r\n */\r\nexport function diffAgainstYaml(generated: OpenApiDocument, existingText: string): SpecDiff {\r\n const { doc, conflicts, additions } = computeMergePlan(generated, existingText, {});\r\n if (doc.contents === null) {\r\n return { missing: [\"(entire document: file is empty)\"], changed: [], inSync: false };\r\n }\r\n const missing = additions.map((a) => a.path.join(\".\"));\r\n const changed = conflicts.map((c) => c.location);\r\n return { missing, changed, inSync: missing.length === 0 && changed.length === 0 };\r\n}\r\n\r\n/** Serializes a generated document to YAML text. */\r\nexport function stringifyDocument(generated: OpenApiDocument): string {\r\n const doc = new Document(generated);\r\n return doc.toString({ lineWidth: 0 });\r\n}\r\n"],"mappings":"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","names":["fs","path","path","ref","FHIR_JSON","SCHEMAS","ref","fhirContent","errorResponses","fhirContent","errorResponses","fs","ref","path"]}
package/dist/cli.js ADDED
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ MergeConflictError,
4
+ buildCoreValueSetFallback,
5
+ diffAgainstYaml,
6
+ generateOpenApi,
7
+ listResources,
8
+ loadCapabilityStatement,
9
+ loadIg,
10
+ mergeIntoYaml,
11
+ stringifyDocument
12
+ } from "./chunk-G3DCRADV.js";
13
+
14
+ // src/cli.ts
15
+ import { Command, Option } from "commander";
16
+ import fs from "fs";
17
+ var program = new Command();
18
+ program.name("fhir-oas").description(
19
+ "Generate OpenAPI specifications for HL7 FHIR resources, for model codegen in any language."
20
+ );
21
+ var fhirVersionOption = () => new Option("-f, --fhir-version <version>", "FHIR version").choices(["r4", "r4b", "r5"]).makeOptionMandatory();
22
+ var sourceOption = () => new Option("-s, --source <backend>", "definition source backend").choices(["schema-json", "structure-def"]).default("schema-json");
23
+ function addGenerationOptions(command) {
24
+ return command.addOption(fhirVersionOption()).addOption(
25
+ new Option("--openapi-version <version>", "target OpenAPI version").choices(["3.0", "3.0.3", "3.1", "3.1.0"]).default("3.0.3")
26
+ ).addOption(sourceOption()).option("--base-url <url>", "server base URL to embed in the spec").option("--title <title>", "override the generated info.title").option(
27
+ "--operations",
28
+ "include the standard FHIR operations ($everything, $validate, ...) for the requested resources",
29
+ false
30
+ ).option("--exclude-narrative", "replace the Narrative type with a generic object", false).option(
31
+ "--no-enums",
32
+ "replace required-binding enums with plain strings (codes noted in descriptions)"
33
+ ).option(
34
+ "--max-depth <n>",
35
+ "stub schema definitions deeper than n hops from the requested resources",
36
+ (value) => Number.parseInt(value, 10)
37
+ ).option(
38
+ "--ig <package>",
39
+ "IG package to apply profiles from: a local .tgz/directory or a registry coordinate (name@version)"
40
+ ).option(
41
+ "--profile <id...>",
42
+ "profile id, name, or canonical URL to apply to its base resource (requires --ig)"
43
+ ).option(
44
+ "--capability <source>",
45
+ "CapabilityStatement file or server base/metadata URL: emit only its declared surface"
46
+ );
47
+ }
48
+ async function toGenerateOptions(resources, opts) {
49
+ if (opts.maxDepth !== void 0 && (!Number.isInteger(opts.maxDepth) || opts.maxDepth < 0)) {
50
+ throw new Error("--max-depth must be a non-negative integer");
51
+ }
52
+ const openApiVersion = opts.openapiVersion.startsWith("3.1") ? "3.1.0" : "3.0.3";
53
+ const fhirVersion = opts.fhirVersion;
54
+ let ig;
55
+ if (opts.ig) {
56
+ ig = await loadIg(opts.ig, { coreValueSetFallback: buildCoreValueSetFallback(fhirVersion) });
57
+ } else if (opts.profile?.length) {
58
+ throw new Error("--profile requires --ig: point at the IG package.");
59
+ }
60
+ let capability;
61
+ if (opts.capability) {
62
+ capability = await loadCapabilityStatement(opts.capability);
63
+ }
64
+ return {
65
+ resources,
66
+ fhirVersion,
67
+ openApiVersion,
68
+ source: opts.source,
69
+ operations: opts.operations,
70
+ baseUrl: opts.baseUrl,
71
+ title: opts.title,
72
+ ig,
73
+ profiles: opts.profile,
74
+ capability,
75
+ trim: {
76
+ excludeNarrative: opts.excludeNarrative,
77
+ maxDepth: opts.maxDepth,
78
+ noEnums: opts.enums === false
79
+ }
80
+ };
81
+ }
82
+ addGenerationOptions(
83
+ program.command("generate").description("Generate an OpenAPI spec for one or more FHIR resources").argument(
84
+ "[resources...]",
85
+ 'FHIR resource names, e.g. "Patient Observation" (optional with --capability)'
86
+ )
87
+ ).option("-o, --output <file>", "write a new spec file (YAML unless --format json)").option("--merge-into <file>", "merge into an existing YAML spec file").option("--force", "overwrite conflicting entries when merging", false).addOption(
88
+ new Option("--format <format>", "output format for --output/stdout").choices(["yaml", "json"]).default("yaml")
89
+ ).action(async (resources, opts) => {
90
+ try {
91
+ if (opts.output && opts.mergeInto) {
92
+ throw new Error("Use either --output or --merge-into, not both");
93
+ }
94
+ const document = generateOpenApi(await toGenerateOptions(resources, opts));
95
+ if (opts.mergeInto) {
96
+ const existing = fs.existsSync(opts.mergeInto) ? fs.readFileSync(opts.mergeInto, "utf8") : "";
97
+ const merged = mergeIntoYaml(document, existing, { force: opts.force });
98
+ fs.writeFileSync(opts.mergeInto, merged);
99
+ console.error(`Merged into ${opts.mergeInto}`);
100
+ return;
101
+ }
102
+ const text = opts.format === "json" ? JSON.stringify(document, null, 2) + "\n" : stringifyDocument(document);
103
+ if (opts.output) {
104
+ fs.writeFileSync(opts.output, text);
105
+ console.error(`Wrote ${opts.output}`);
106
+ } else {
107
+ process.stdout.write(text);
108
+ }
109
+ } catch (error) {
110
+ fail(error);
111
+ }
112
+ });
113
+ addGenerationOptions(
114
+ program.command("check").description(
115
+ "Verify that a committed spec file is in sync with what generation would produce (CI drift guard)"
116
+ ).argument("[resources...]", "FHIR resource names the spec should cover (optional with --capability)")
117
+ ).requiredOption("--file <file>", "spec file (YAML or JSON) to check").action(async (resources, opts) => {
118
+ try {
119
+ if (!fs.existsSync(opts.file)) {
120
+ throw new Error(`No such file: ${opts.file}`);
121
+ }
122
+ const document = generateOpenApi(await toGenerateOptions(resources, opts));
123
+ const diff = diffAgainstYaml(document, fs.readFileSync(opts.file, "utf8"));
124
+ if (diff.inSync) {
125
+ console.log(`${opts.file} is in sync`);
126
+ return;
127
+ }
128
+ console.error(`${opts.file} is out of sync with generation:`);
129
+ for (const location of diff.missing) console.error(` missing: ${location}`);
130
+ for (const location of diff.changed) console.error(` changed: ${location}`);
131
+ const modeFlags = (opts.capability ? ` --capability ${opts.capability}` : "") + (opts.ig ? ` --ig ${opts.ig}` : "") + (opts.profile?.length ? ` --profile ${opts.profile.join(" ")}` : "") + (opts.operations ? " --operations" : "");
132
+ console.error(
133
+ `Regenerate with: fhir-oas generate ${resources.join(" ")} --fhir-version ${opts.fhirVersion}${modeFlags} --merge-into ${opts.file}` + (diff.changed.length > 0 ? " --force" : "")
134
+ );
135
+ process.exit(1);
136
+ } catch (error) {
137
+ fail(error);
138
+ }
139
+ });
140
+ program.command("list").description("List the FHIR resource types for a version, or the profiles in an IG package").addOption(
141
+ new Option("-f, --fhir-version <version>", "FHIR version (required unless --ig is given)").choices(
142
+ ["r4", "r4b", "r5"]
143
+ )
144
+ ).addOption(sourceOption()).option("--ig <package>", "list the profiles in an IG package instead of core resources").action(async (opts) => {
145
+ try {
146
+ if (opts.ig) {
147
+ const ig = await loadIg(opts.ig);
148
+ for (const profile of ig.profiles) {
149
+ console.log(`${profile.id ?? profile.name} ${profile.type} ${profile.url}`);
150
+ }
151
+ return;
152
+ }
153
+ if (!opts.fhirVersion) throw new Error("--fhir-version is required (or pass --ig to list profiles)");
154
+ for (const name of listResources(opts.fhirVersion, opts.source)) {
155
+ console.log(name);
156
+ }
157
+ } catch (error) {
158
+ fail(error);
159
+ }
160
+ });
161
+ function fail(error) {
162
+ if (error instanceof MergeConflictError) {
163
+ console.error(error.message);
164
+ } else {
165
+ console.error(`error: ${error instanceof Error ? error.message : String(error)}`);
166
+ }
167
+ process.exit(1);
168
+ }
169
+ program.parseAsync();
170
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport { Command, Option } from \"commander\";\r\nimport fs from \"node:fs\";\r\nimport { generateOpenApi, listResources } from \"./generate.js\";\r\nimport { loadCapabilityStatement, type Capability } from \"./capability.js\";\r\nimport { loadIg, type IgContext } from \"./ig/package.js\";\r\nimport { buildCoreValueSetFallback } from \"./ig/profile.js\";\r\nimport { diffAgainstYaml, MergeConflictError, mergeIntoYaml, stringifyDocument } from \"./merge.js\";\r\nimport type { FhirVersion, GenerateOptions, OpenApiVersion, SourceBackend } from \"./types.js\";\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name(\"fhir-oas\")\r\n .description(\r\n \"Generate OpenAPI specifications for HL7 FHIR resources, for model codegen in any language.\",\r\n );\r\n\r\nconst fhirVersionOption = () =>\r\n new Option(\"-f, --fhir-version <version>\", \"FHIR version\")\r\n .choices([\"r4\", \"r4b\", \"r5\"])\r\n .makeOptionMandatory();\r\n\r\nconst sourceOption = () =>\r\n new Option(\"-s, --source <backend>\", \"definition source backend\")\r\n .choices([\"schema-json\", \"structure-def\"])\r\n .default(\"schema-json\");\r\n\r\n/** Options that shape the generated document, shared by generate and check. */\r\nfunction addGenerationOptions(command: Command): Command {\r\n return command\r\n .addOption(fhirVersionOption())\r\n .addOption(\r\n new Option(\"--openapi-version <version>\", \"target OpenAPI version\")\r\n .choices([\"3.0\", \"3.0.3\", \"3.1\", \"3.1.0\"])\r\n .default(\"3.0.3\"),\r\n )\r\n .addOption(sourceOption())\r\n .option(\"--base-url <url>\", \"server base URL to embed in the spec\")\r\n .option(\"--title <title>\", \"override the generated info.title\")\r\n .option(\r\n \"--operations\",\r\n \"include the standard FHIR operations ($everything, $validate, ...) for the requested resources\",\r\n false,\r\n )\r\n .option(\"--exclude-narrative\", \"replace the Narrative type with a generic object\", false)\r\n .option(\r\n \"--no-enums\",\r\n \"replace required-binding enums with plain strings (codes noted in descriptions)\",\r\n )\r\n .option(\r\n \"--max-depth <n>\",\r\n \"stub schema definitions deeper than n hops from the requested resources\",\r\n (value) => Number.parseInt(value, 10),\r\n )\r\n .option(\r\n \"--ig <package>\",\r\n \"IG package to apply profiles from: a local .tgz/directory or a registry coordinate (name@version)\",\r\n )\r\n .option(\r\n \"--profile <id...>\",\r\n \"profile id, name, or canonical URL to apply to its base resource (requires --ig)\",\r\n )\r\n .option(\r\n \"--capability <source>\",\r\n \"CapabilityStatement file or server base/metadata URL: emit only its declared surface\",\r\n );\r\n}\r\n\r\ninterface SharedCliOptions {\r\n fhirVersion: string;\r\n openapiVersion: string;\r\n source: string;\r\n baseUrl?: string;\r\n title?: string;\r\n operations: boolean;\r\n excludeNarrative: boolean;\r\n /** commander maps --no-enums here: undefined/true = keep, false = strip. */\r\n enums?: boolean;\r\n maxDepth?: number;\r\n ig?: string;\r\n profile?: string[];\r\n capability?: string;\r\n}\r\n\r\n/**\r\n * Resolves the shared generation options, loading the IG package (async, so\r\n * registry coordinates work) when profiles are requested.\r\n */\r\nasync function toGenerateOptions(\r\n resources: string[],\r\n opts: SharedCliOptions,\r\n): Promise<GenerateOptions> {\r\n if (opts.maxDepth !== undefined && (!Number.isInteger(opts.maxDepth) || opts.maxDepth < 0)) {\r\n throw new Error(\"--max-depth must be a non-negative integer\");\r\n }\r\n const openApiVersion: OpenApiVersion = opts.openapiVersion.startsWith(\"3.1\")\r\n ? \"3.1.0\"\r\n : \"3.0.3\";\r\n const fhirVersion = opts.fhirVersion as FhirVersion;\r\n\r\n let ig: IgContext | undefined;\r\n if (opts.ig) {\r\n ig = await loadIg(opts.ig, { coreValueSetFallback: buildCoreValueSetFallback(fhirVersion) });\r\n } else if (opts.profile?.length) {\r\n throw new Error(\"--profile requires --ig: point at the IG package.\");\r\n }\r\n\r\n let capability: Capability | undefined;\r\n if (opts.capability) {\r\n capability = await loadCapabilityStatement(opts.capability);\r\n }\r\n\r\n return {\r\n resources,\r\n fhirVersion,\r\n openApiVersion,\r\n source: opts.source as SourceBackend,\r\n operations: opts.operations,\r\n baseUrl: opts.baseUrl,\r\n title: opts.title,\r\n ig,\r\n profiles: opts.profile,\r\n capability,\r\n trim: {\r\n excludeNarrative: opts.excludeNarrative,\r\n maxDepth: opts.maxDepth,\r\n noEnums: opts.enums === false,\r\n },\r\n };\r\n}\r\n\r\naddGenerationOptions(\r\n program\r\n .command(\"generate\")\r\n .description(\"Generate an OpenAPI spec for one or more FHIR resources\")\r\n .argument(\r\n \"[resources...]\",\r\n 'FHIR resource names, e.g. \"Patient Observation\" (optional with --capability)',\r\n ),\r\n)\r\n .option(\"-o, --output <file>\", \"write a new spec file (YAML unless --format json)\")\r\n .option(\"--merge-into <file>\", \"merge into an existing YAML spec file\")\r\n .option(\"--force\", \"overwrite conflicting entries when merging\", false)\r\n .addOption(\r\n new Option(\"--format <format>\", \"output format for --output/stdout\")\r\n .choices([\"yaml\", \"json\"])\r\n .default(\"yaml\"),\r\n )\r\n .action(async (resources: string[], opts) => {\r\n try {\r\n if (opts.output && opts.mergeInto) {\r\n throw new Error(\"Use either --output or --merge-into, not both\");\r\n }\r\n const document = generateOpenApi(await toGenerateOptions(resources, opts));\r\n\r\n if (opts.mergeInto) {\r\n const existing = fs.existsSync(opts.mergeInto)\r\n ? fs.readFileSync(opts.mergeInto, \"utf8\")\r\n : \"\";\r\n const merged = mergeIntoYaml(document, existing, { force: opts.force });\r\n fs.writeFileSync(opts.mergeInto, merged);\r\n console.error(`Merged into ${opts.mergeInto}`);\r\n return;\r\n }\r\n\r\n const text =\r\n opts.format === \"json\"\r\n ? JSON.stringify(document, null, 2) + \"\\n\"\r\n : stringifyDocument(document);\r\n if (opts.output) {\r\n fs.writeFileSync(opts.output, text);\r\n console.error(`Wrote ${opts.output}`);\r\n } else {\r\n process.stdout.write(text);\r\n }\r\n } catch (error) {\r\n fail(error);\r\n }\r\n });\r\n\r\naddGenerationOptions(\r\n program\r\n .command(\"check\")\r\n .description(\r\n \"Verify that a committed spec file is in sync with what generation would produce (CI drift guard)\",\r\n )\r\n .argument(\"[resources...]\", \"FHIR resource names the spec should cover (optional with --capability)\"),\r\n)\r\n .requiredOption(\"--file <file>\", \"spec file (YAML or JSON) to check\")\r\n .action(async (resources: string[], opts) => {\r\n try {\r\n if (!fs.existsSync(opts.file)) {\r\n throw new Error(`No such file: ${opts.file}`);\r\n }\r\n const document = generateOpenApi(await toGenerateOptions(resources, opts));\r\n const diff = diffAgainstYaml(document, fs.readFileSync(opts.file, \"utf8\"));\r\n if (diff.inSync) {\r\n console.log(`${opts.file} is in sync`);\r\n return;\r\n }\r\n console.error(`${opts.file} is out of sync with generation:`);\r\n for (const location of diff.missing) console.error(` missing: ${location}`);\r\n for (const location of diff.changed) console.error(` changed: ${location}`);\r\n const modeFlags =\r\n (opts.capability ? ` --capability ${opts.capability}` : \"\") +\r\n (opts.ig ? ` --ig ${opts.ig}` : \"\") +\r\n (opts.profile?.length ? ` --profile ${opts.profile.join(\" \")}` : \"\") +\r\n (opts.operations ? \" --operations\" : \"\");\r\n console.error(\r\n `Regenerate with: fhir-oas generate ${resources.join(\" \")} --fhir-version ${opts.fhirVersion}${modeFlags} --merge-into ${opts.file}` +\r\n (diff.changed.length > 0 ? \" --force\" : \"\"),\r\n );\r\n process.exit(1);\r\n } catch (error) {\r\n fail(error);\r\n }\r\n });\r\n\r\nprogram\r\n .command(\"list\")\r\n .description(\"List the FHIR resource types for a version, or the profiles in an IG package\")\r\n .addOption(\r\n new Option(\"-f, --fhir-version <version>\", \"FHIR version (required unless --ig is given)\").choices(\r\n [\"r4\", \"r4b\", \"r5\"],\r\n ),\r\n )\r\n .addOption(sourceOption())\r\n .option(\"--ig <package>\", \"list the profiles in an IG package instead of core resources\")\r\n .action(async (opts) => {\r\n try {\r\n if (opts.ig) {\r\n const ig = await loadIg(opts.ig);\r\n for (const profile of ig.profiles) {\r\n console.log(`${profile.id ?? profile.name}\\t${profile.type}\\t${profile.url}`);\r\n }\r\n return;\r\n }\r\n if (!opts.fhirVersion) throw new Error(\"--fhir-version is required (or pass --ig to list profiles)\");\r\n for (const name of listResources(opts.fhirVersion as FhirVersion, opts.source)) {\r\n console.log(name);\r\n }\r\n } catch (error) {\r\n fail(error);\r\n }\r\n });\r\n\r\nfunction fail(error: unknown): never {\r\n if (error instanceof MergeConflictError) {\r\n console.error(error.message);\r\n } else {\r\n console.error(`error: ${error instanceof Error ? error.message : String(error)}`);\r\n }\r\n process.exit(1);\r\n}\r\n\r\nprogram.parseAsync();\r\n"],"mappings":"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","names":[]}