UNPKG

fhir-package-installer

Version:

A utility module for downloading, indexing, caching, and managing FHIR packages from the FHIR Package Registry and Simplifier

163 lines 5.62 kB
// src/mock-artifactory-server.ts import http from "http"; import https from "https"; import { URL } from "url"; var InternalMockArtifactoryServer = class { server; port; validToken = "test-token"; upstreamRegistryUrl; upstreamTarballBaseUrl; constructor(port, options = {}) { this.port = port; this.upstreamRegistryUrl = options.upstreamRegistryUrl ?? "https://packages.fhir.org"; this.upstreamTarballBaseUrl = options.upstreamTarballBaseUrl ?? "https://packages.simplifier.net"; this.server = this.createServer(); } async start() { if (this.server.listening) return; await new Promise((resolve, reject) => { this.server.once("error", reject); this.server.listen(this.port, () => resolve()); }); const addr = this.server.address(); if (typeof addr === "object" && addr && "port" in addr) this.port = addr.port; } async stop() { if (!this.server.listening) return; await new Promise((resolve) => this.server.close(() => resolve())); } getBaseUrl() { return `http://localhost:${this.port}`; } getValidToken() { return this.validToken; } // ---- Internal implementation ---- createServer() { return http.createServer(async (req, res) => { res.setHeader("Access-Control-Allow-Origin", "*"); res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS"); res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization"); if (req.method === "OPTIONS") { res.writeHead(200); res.end(); return; } const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith("Bearer ")) { this.json(res, 401, { error: "Unauthorized" }); return; } const token = authHeader.substring("Bearer ".length); if (token !== this.validToken) { this.json(res, 403, { error: "Forbidden" }); return; } const url = new URL(req.url || "/", this.getBaseUrl()); try { if (url.pathname.includes("/-/")) { this.handleTarballRedirect(res, url); return; } if (url.pathname.includes("/artifactory/api/npm/")) { await this.handleArtifactoryMetadata(res, url); return; } if (url.pathname.endsWith("/")) { await this.handleSimplifierMetadata(res, url); return; } this.json(res, 404, { error: "Not found" }); } catch { this.json(res, 500, { error: "Internal server error" }); } }); } async handleArtifactoryMetadata(res, url) { const segments = url.pathname.split("/").filter(Boolean); const packageName = segments.at(-1) || segments.at(-2); if (!packageName) { this.json(res, 404, { error: "Package not found" }); return; } await this.fetchAndRewrite(packageName, res); } async handleSimplifierMetadata(res, url) { const packageName = url.pathname.split("/").filter(Boolean)[0]; if (!packageName) { this.json(res, 404, { error: "Package not found" }); return; } await this.fetchAndRewrite(packageName, res); } handleTarballRedirect(res, url) { const parts = url.pathname.split("/").filter(Boolean); const tgzFile = parts.at(-1); const packageName = parts.at(-3); if (!tgzFile || !packageName) { this.json(res, 404, { error: "Tarball not found" }); return; } const base = this.upstreamTarballBaseUrl.replace(/\/+$/, ""); const redirectUrl = `${base}/${packageName}/-/${tgzFile}`; res.writeHead(302, { Location: redirectUrl, "Content-Type": "application/json" }); res.end(JSON.stringify({ message: "Redirecting to Simplifier" })); } async fetchAndRewrite(packageName, res) { try { const base = this.upstreamRegistryUrl.replace(/\/+$/, ""); const sourceUrl = `${base}/${packageName}/`; const { data } = await this.fetchRaw(sourceUrl); const modified = this.rewriteTarballs(data); this.json(res, 200, modified); } catch { this.json(res, 404, { error: "Package not found" }); } } fetchRaw(url) { return new Promise((resolve, reject) => { const parsed = new URL(url); const client = parsed.protocol === "http:" ? http : https; client.get(url, (resp) => { let acc = ""; resp.on("data", (chunk) => acc += chunk); resp.on("end", () => resolve({ statusCode: resp.statusCode || 0, data: acc })); }).on("error", reject); }); } rewriteTarballs(raw) { try { const json = JSON.parse(raw); if (json.versions) { for (const v of Object.values(json.versions)) { if (v.dist?.tarball) { const original = new URL(v.dist.tarball); const parts = original.pathname.split("/").filter(Boolean); const packageName = parts[0]; const tgzFile = parts.at(-1); if (packageName && tgzFile) { v.dist.tarball = `${this.getBaseUrl()}/${packageName}/-/${tgzFile}`; } } } } return json; } catch { return raw; } } json(res, status, body) { res.writeHead(status, { "Content-Type": "application/json" }); res.end(typeof body === "string" ? body : JSON.stringify(body)); } }; function createMockArtifactoryServer(port = 3333, options = {}) { return new InternalMockArtifactoryServer(port, options); } var MOCK_ARTIFACTORY_VALID_TOKEN = "test-token"; export { MOCK_ARTIFACTORY_VALID_TOKEN, createMockArtifactoryServer }; //# sourceMappingURL=mock-artifactory-server.mjs.map