UNPKG

everything-dev

Version:

A consolidated product package for building Module Federation apps with oRPC APIs.

128 lines (126 loc) • 3.49 kB
import { createHash } from "node:crypto"; //#region src/rollback.ts function collectPinnedSlots(config) { const slots = []; const app = config.app; const plugins = config.plugins; const push = (slot, ref) => { if (ref && typeof ref === "object") slots.push({ slot, ref }); }; if (app) { for (const key of [ "host", "ui", "api", "auth" ]) push(`app.${key}`, app[key]); const auth = app.auth; if (auth?.ui) push("app.auth.ui", auth.ui); } if (plugins) for (const [key, plugin] of Object.entries(plugins)) { if (typeof plugin !== "object" || plugin === null) continue; push(key, plugin); const ui = plugin.ui; if (ui) push(`${key}.ui`, ui); } return slots; } function pinOf(ref) { const manifest = typeof ref.pin?.manifest === "string" ? ref.pin.manifest : void 0; const integrity = typeof ref.pin?.integrity === "string" ? ref.pin.integrity : void 0; return manifest && integrity ? { manifest, integrity } : null; } /** Every pinned slot with both a production base and a verifiable pin. */ function collectSlotPinRefs(config) { return collectPinnedSlots(config).map(({ slot, ref }) => { const pin = pinOf(ref); const base = typeof ref.production === "string" ? ref.production : void 0; return pin && base ? { slot, base, pin } : null; }).filter((entry) => entry !== null); } async function verifySlot(slot, ref, fetchImpl) { const base = typeof ref.production === "string" ? ref.production : void 0; const pin = pinOf(ref); if (!pin) return { slot, ok: true }; const { manifest, integrity } = pin; if (!base) return { slot, manifest, ok: false, reason: "no production URL to fetch the manifest from" }; const manifestUrl = `${base.replace(/\/$/, "")}/${manifest.replace(/^\//, "")}`; try { const response = await fetchImpl(manifestUrl); if (!response.ok) return { slot, manifest, ok: false, reason: `manifest fetch failed: ${response.status}` }; const body = await response.text(); const computed = `sha384-${createHash("sha384").update(body).digest("base64")}`; if (computed !== integrity) return { slot, manifest, ok: false, reason: `integrity mismatch for ${manifestUrl} (expected ${integrity}, computed ${computed})` }; return { slot, manifest, ok: true }; } catch (error) { return { slot, manifest, ok: false, reason: `manifest fetch failed: ${error instanceof Error ? error.message : String(error)}` }; } } async function verifyRollbackSnapshot(config, opts) { const fetchImpl = opts?.fetchImpl ?? fetch; const pinned = collectPinnedSlots(config).filter((entry) => pinOf(entry.ref) !== null); if (pinned.length === 0) return { verifiable: false, ok: false, slots: [] }; const slots = await Promise.all(pinned.map((entry) => verifySlot(entry.slot, entry.ref, fetchImpl))); return { verifiable: true, ok: slots.every((check) => check.ok), slots }; } function summarizeSlotPins(config) { const pinned = collectPinnedSlots(config).map((entry) => { const pin = pinOf(entry.ref); return pin ? `${entry.slot}=${pin.manifest}` : null; }).filter(Boolean); return pinned.length > 0 ? pinned.join(" ") : "no version pins (pre-pin snapshot)"; } function buildRollbackPayload(targetConfig, rolledBackFrom) { return { ...targetConfig, rolledBackFrom }; } //#endregion export { buildRollbackPayload, collectSlotPinRefs, summarizeSlotPins, verifyRollbackSnapshot }; //# sourceMappingURL=rollback.mjs.map