//#region src/openapi/bundle.d.ts /** * OpenAPI document bundler — inlines external $ref files. * * Walks all `$ref` strings in an OpenAPI document, fetches external * documents via a user-provided resolver, inlines their schemas into * `components.schemas` with synthesised names, and rewrites the refs * to point to the inlined copies. * * This is an opt-in async pre-step. The synchronous core API is unchanged; * consumers call `bundleOpenApiDoc` once before rendering. * * Usage: * ```ts * import { bundleOpenApiDoc } from "schema-components/openapi/bundle"; * * const resolver = async (uri: string) => { * const response = await fetch(uri); * return response.json(); * }; * * const bundled = await bundleOpenApiDoc(doc, resolver); * // Now pass bundled to SchemaComponent / ApiOperation * ``` */ /** * Resolver function for external documents. * Called with the URI portion of an external $ref (everything before `#`). * Returns the parsed JSON document. * * ### Security warning — SSRF and local-file disclosure * * Consumers MUST validate the URI before fetching the target document. * The bundler hands the resolver the raw `$ref` URI from the OpenAPI * document — which is typically user-controlled — and any network or * filesystem access the resolver performs runs with the host * application's full privileges. An attacker-crafted document that * references an internal endpoint or a local filesystem path will * happily exfiltrate or expose data the application never intended to * surface. * * At a minimum the resolver should: * * - Refuse non-`https:` schemes by default. Permit `http:` only on an * explicit allow-list. Refuse `file:`, `data:`, `javascript:`, * `ftp:`, `gopher:`, and every other scheme outright. * - Resolve the URI's hostname and refuse loopback addresses * (`127.0.0.0/8`, `::1`), link-local addresses (`169.254.0.0/16`, * `fe80::/10`), private ranges (`10.0.0.0/8`, `172.16.0.0/12`, * `192.168.0.0/16`, `fc00::/7`), and cloud-metadata IPs * (`169.254.169.254`, `fd00:ec2::254`). * - Apply a strict allow-list of permitted hosts where possible. * - Set request timeouts and a maximum response size. * - Disable HTTP redirects, or re-validate the redirected URL against * the same denylist before following. * - Reject responses that are not `application/json` or * `application/yaml`. * * The bundler itself performs no validation — that responsibility sits * exclusively with the resolver implementation supplied by the caller. */ type BundleResolver = (uri: string) => unknown; /** * Bundle an OpenAPI document by inlining all external $ref targets. * * Walks every $ref in the document. For external refs (not starting with `#`), * calls the resolver to fetch the external document, extracts the referenced * schema, inlines it into `components.schemas` under a synthesised name, and * rewrites the original $ref to point at the new internal location * (`#/components/schemas/`). * * Identical external refs share a single entry — the second occurrence of * the same `(uri, fragment)` pair reuses the name produced for the first. * Name collisions between different refs are resolved by suffixing a counter. * * The resolver is called once per unique URI and the result is cached. * * Returns a deep-cloned document with all external refs replaced by internal * refs. The original document is never mutated. */ declare function bundleOpenApiDoc(doc: Record, resolver: BundleResolver): Promise>; //#endregion export { BundleResolver, bundleOpenApiDoc };