everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
247 lines (245 loc) • 8.78 kB
JavaScript
const require_services_deploy_version_manifest = require('./version-manifest.cjs');
const require_source_archive = require('./source-archive.cjs');
const require_storage = require('./storage.cjs');
const require_diff = require('./diff.cjs');
let node_fs = require("node:fs");
let node_path = require("node:path");
//#region src/services/deploy/upload-leg.ts
const realStorageLegDeps = {
collectDistFiles: require_storage.collectDistFiles,
uploadWorkspaceDist: require_storage.uploadWorkspaceDist,
uploadBundle: require_storage.uploadBundle,
resolveSlotVersion: require_services_deploy_version_manifest.resolveSlotVersion,
readBuildReport: require_services_deploy_version_manifest.readBuildReport,
composeWorkspaceVersionManifest: require_services_deploy_version_manifest.composeWorkspaceVersionManifest,
buildSourceArchive: require_source_archive.buildSourceArchive
};
function formatBundleMb(files) {
return `${(files.reduce((sum, file) => sum + file.bytes.byteLength, 0) / 1024 / 1024).toFixed(1)} MB`;
}
/** One workspace's upload with skip-unchanged diffing; any failure to resolve
* the previous manifest's file map degrades to a full upload. */
async function diffUploadWorkspace(deps, input) {
const localSri = require_diff.localSriMap(input.files);
let uploadFiles = input.files;
let prevFiles;
if (input.previousPin) try {
const resolved = await deps.resolveSlotVersion({
base: input.base,
pin: input.previousPin
});
if (resolved.files) {
const diff = require_diff.diffWorkspaceFiles(input.files, localSri, resolved.files);
prevFiles = resolved.files;
uploadFiles = diff.upload;
if (diff.skipped > 0) input.notes.push(`${diff.skipped} unchanged file(s) — skipping (manifest diff)`);
}
} catch {
input.notes.push("previous manifest unusable — uploading everything");
}
input.notes.push(`uploading ${uploadFiles.length} of ${input.files.length} files (${formatBundleMb(uploadFiles)}) → ${input.base}`);
const result = uploadFiles.length > 0 ? await deps.uploadWorkspaceDist({
origin: input.origin,
apiKey: input.apiKey,
account: input.account,
gateway: input.gateway,
workspace: input.workspace,
files: uploadFiles
}) : {
stored: 0,
totalBytes: 0,
integrity: {}
};
return {
result,
fileIntegrity: require_diff.mergedFileIntegrity(input.files, result.integrity, prevFiles),
uploaded: result.stored,
skipped: input.files.length - uploadFiles.length,
total: input.files.length
};
}
/** Compose + upload the workspace's version manifest — server-computed SRI of
* the just-finished upload, uploaded additively at `versions/<version>.json`.
* Returns undefined when the dist predates hashed entry names. The manifest's
* `shared` record rides back for the publish train's truth gate (#546). */
async function pinWorkspaceVersionManifest(deps, input) {
if (!input.report) return void 0;
const versionManifest = deps.composeWorkspaceVersionManifest({
report: input.report,
ssrReport: input.ssrReport,
integrityMap: input.integrityMap,
...input.files ? { files: input.files } : {},
...input.source ? { source: input.source } : {},
...input.gitSha ? { gitSha: input.gitSha } : {}
});
if (!versionManifest) return void 0;
const manifestFile = `versions/${versionManifest.version}.json`;
const manifestUpload = await deps.uploadBundle({
origin: input.origin,
apiKey: input.apiKey,
account: input.account,
gateway: input.gateway,
workspace: input.workspace,
files: [{
path: manifestFile,
bytes: new TextEncoder().encode(`${JSON.stringify(versionManifest, null, 2)}\n`)
}]
});
const manifestIntegrity = manifestUpload.integrity[manifestFile];
if (!manifestIntegrity) return void 0;
input.notes.push(`version manifest ${manifestFile} (SRI pinned)`);
return {
pin: {
file: manifestFile,
integrity: manifestIntegrity,
storage: manifestUpload.storage
},
...versionManifest.shared ? { shared: versionManifest.shared } : {}
};
}
/** Source-archive leg (publish.sourcePublish, #406): tar the workspace
* subtree, dedupe against the previous pin by archive SRI, otherwise upload
* and take the server-computed SRI. The temp dir is cleaned on every path. */
async function resolveSourceReference(deps, input) {
const notes = input.notes;
const outDir = require_source_archive.makeSourceArchiveTempDir();
try {
const archive = await deps.buildSourceArchive({
repoRoot: input.repoRoot,
workspace: input.workspaceDir,
outDir
});
if (input.previousPin) try {
const resolved = await deps.resolveSlotVersion({
base: input.base,
pin: input.previousPin
});
if (resolved.source?.integrity === archive.integrity) {
notes.push(`source unchanged — reusing ${archive.file}`);
return { source: {
file: resolved.source.file,
integrity: resolved.source.integrity
} };
}
} catch {}
const upload = await deps.uploadBundle({
origin: input.origin,
apiKey: input.apiKey,
account: input.account,
gateway: input.gateway,
workspace: input.workspace,
files: [{
path: archive.file,
bytes: new Uint8Array((0, node_fs.readFileSync)(archive.path))
}]
});
if (upload.storage === "memory") return { memory: true };
const integrity = upload.integrity[archive.file];
if (!integrity) throw new Error(`[publish] source archive upload for ${input.workspace}: missing server SRI for ${archive.file}`);
notes.push(`source archive ${archive.file}`);
return { source: {
file: archive.file,
integrity
} };
} finally {
(0, node_fs.rmSync)(outDir, {
recursive: true,
force: true
});
}
}
async function uploadWorkspaceBundle(input) {
const deps = input.deps ?? realStorageLegDeps;
const base = `${input.urlOrigin}/bundles/${input.account}/${input.gateway}/${input.workspaceKey}/`;
const startedAt = Date.now();
let phase = "upload";
try {
const distFiles = await deps.collectDistFiles(input.distDir);
const notes = [];
const outcome = await diffUploadWorkspace(deps, {
origin: input.storageOrigin,
apiKey: input.storageApiKey,
account: input.account,
gateway: input.gateway,
workspace: input.workspaceKey,
files: distFiles,
base,
previousPin: input.previousPin,
notes
});
const report = deps.readBuildReport(input.distDir);
const ssrReport = deps.readBuildReport((0, node_path.join)(input.distDir, "ssr"));
const entryIntegrity = report ? outcome.fileIntegrity[report.entry] : void 0;
const ssrEntryIntegrity = ssrReport?.entry ? outcome.fileIntegrity[`ssr/${ssrReport.entry}`] : void 0;
let sourceRef;
if (input.source) {
if (outcome.result.storage === "memory") return { kind: "memory" };
phase = "source";
const sourceOutcome = await resolveSourceReference(deps, {
origin: input.storageOrigin,
apiKey: input.storageApiKey,
account: input.account,
gateway: input.gateway,
workspace: input.workspaceKey,
base,
previousPin: input.previousPin,
repoRoot: input.source.repoRoot,
workspaceDir: input.source.workspaceDir,
notes
});
if ("memory" in sourceOutcome) return { kind: "memory" };
sourceRef = sourceOutcome.source;
phase = "upload";
}
const pinned = await pinWorkspaceVersionManifest(deps, {
origin: input.storageOrigin,
apiKey: input.storageApiKey,
account: input.account,
gateway: input.gateway,
workspace: input.workspaceKey,
report,
ssrReport,
integrityMap: outcome.fileIntegrity,
files: outcome.fileIntegrity,
...sourceRef ? { source: sourceRef } : {},
...input.gitSha ? { gitSha: input.gitSha } : {},
notes
});
if (!pinned) return { kind: "missing-manifest" };
const pin = pinned.pin;
if (outcome.result.storage === "memory" || pin.storage === "memory") return { kind: "memory" };
return {
kind: "ok",
pin,
storage: outcome.result.storage,
entryIntegrity,
ssrEntryIntegrity,
...pinned.shared ? { shared: pinned.shared } : {},
uploaded: outcome.uploaded,
skipped: outcome.skipped,
durationMs: Date.now() - startedAt,
detail: {
base,
manifestFile: pin.file,
notes
}
};
} catch (error) {
return {
kind: "error",
message: error instanceof Error ? error.message : phase === "source" ? `[publish] source archive for ${input.workspaceKey} failed` : `[publish] bundle upload for ${input.workspaceKey} failed`
};
}
}
/** Directory names the source leg needs (main workspaces only). */
function sourceLegDirs(workspacePath) {
return {
repoRoot: (0, node_path.dirname)(workspacePath),
workspaceDir: (0, node_path.basename)(workspacePath)
};
}
//#endregion
exports.realStorageLegDeps = realStorageLegDeps;
exports.sourceLegDirs = sourceLegDirs;
exports.uploadWorkspaceBundle = uploadWorkspaceBundle;
//# sourceMappingURL=upload-leg.cjs.map