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
JavaScript
// 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