everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
109 lines (107 loc) • 3.67 kB
JavaScript
import { collectSlotPinRefs } from "./rollback.mjs";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { createHash } from "node:crypto";
//#region src/deploy-diff.ts
const BUNDLE_BASE_PATTERN = /\/bundles\/([^/]+)\/([^/]+)\/([^/]+)\/$/;
function workspaceKeyOfBase(base) {
const match = BUNDLE_BASE_PATTERN.exec(`${base.replace(/\/$/, "")}/`);
return match ? match[3] : null;
}
/** The sha384 SRI the storage route would compute for these bytes. */
function localObjectIntegrity(bytes) {
return `sha384-${createHash("sha384").update(bytes).digest("base64")}`;
}
function localSriMap(files) {
return Object.fromEntries(files.map((file) => [file.path, localObjectIntegrity(file.bytes)]));
}
const deployStatePath = (configDir) => join(configDir, ".bos", "deploy-state.json");
function readDeployState(configDir) {
const path = deployStatePath(configDir);
if (!existsSync(path)) return {
version: 1,
apps: {}
};
try {
const parsed = JSON.parse(readFileSync(path, "utf8"));
if (parsed.version !== 1 || typeof parsed.apps !== "object" || parsed.apps === null) return {
version: 1,
apps: {}
};
return {
version: 1,
apps: parsed.apps
};
} catch {
return {
version: 1,
apps: {}
};
}
}
function writeDeployState(configDir, state) {
const path = deployStatePath(configDir);
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, `${JSON.stringify(state, null, 2)}\n`);
}
function upsertDeployStatePins(configDir, account, gateway, pins) {
const state = readDeployState(configDir);
const appKey = `${account}/${gateway}`;
state.apps[appKey] = {
...state.apps[appKey],
...pins
};
writeDeployState(configDir, state);
}
/** Previous pins by workspace key, from the local deploy-state pointer. */
function pinsFromDeployState(configDir, account, gateway) {
return readDeployState(configDir).apps[`${account}/${gateway}`] ?? {};
}
/** Previous pins by workspace key, from the published config's slot pins. */
function pinsFromPublishedConfig(config, account, gateway) {
const pins = {};
for (const ref of collectSlotPinRefs(config)) {
const workspace = workspaceKeyOfBase(ref.base);
const nsMatch = BUNDLE_BASE_PATTERN.exec(`${ref.base.replace(/\/$/, "")}/`);
if (!workspace || !nsMatch || nsMatch[1] !== account || nsMatch[2] !== gateway) continue;
pins[workspace] = ref.pin;
}
return pins;
}
function diffWorkspaceFiles(files, localSri, prevFiles) {
const upload = [];
let skipped = 0;
for (const file of files) {
if (prevFiles[file.path] !== void 0 && prevFiles[file.path] === localSri[file.path]) {
skipped += 1;
continue;
}
upload.push(file);
}
return {
upload,
skipped
};
}
/**
* The manifest's merged per-file map for this deploy: uploaded files carry the
* server-computed SRI from the upload response; skipped files carry the
* previous manifest's server-computed SRI (byte-identical to the local hash by
* the skip decision). Files that left the dist carry nothing.
*/
function mergedFileIntegrity(files, uploadedIntegrity, prevFiles) {
const merged = {};
for (const file of files) {
const uploaded = uploadedIntegrity[file.path];
if (uploaded) {
merged[file.path] = uploaded;
continue;
}
const carried = prevFiles?.[file.path];
if (carried) merged[file.path] = carried;
}
return merged;
}
//#endregion
export { BUNDLE_BASE_PATTERN, diffWorkspaceFiles, localObjectIntegrity, localSriMap, mergedFileIntegrity, pinsFromDeployState, pinsFromPublishedConfig, readDeployState, upsertDeployStatePins, workspaceKeyOfBase, writeDeployState };
//# sourceMappingURL=deploy-diff.mjs.map