everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
87 lines (85 loc) • 2.99 kB
JavaScript
import { createUploadClient } from "../../apps/upload/src/client.mjs";
import { join, relative, sep } from "node:path";
import { readFile, readdir, stat } from "node:fs/promises";
//#region src/services/deploy/storage.ts
async function walkDist(dir, baseDir) {
const entries = await readdir(dir, { withFileTypes: true });
const files = [];
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
const fullPath = join(dir, entry.name);
if (entry.isDirectory()) {
files.push(...await walkDist(fullPath, baseDir));
continue;
}
if (!(await stat(fullPath)).isFile()) continue;
files.push({
path: relative(baseDir, fullPath).split(sep).join("/"),
bytes: new Uint8Array(await readFile(fullPath))
});
}
return files;
}
async function collectDistFiles(distDir) {
return walkDist(distDir, distDir);
}
async function uploadBundle(input) {
return await createUploadClient({
origin: input.origin,
apiKey: input.apiKey,
fetchImpl: input.fetchImpl
}).uploadBundle({
account: input.account,
gateway: input.gateway,
workspace: input.workspace,
files: input.files.map((file) => ({
path: file.path,
contentBase64: Buffer.from(file.bytes).toString("base64")
}))
});
}
/** Per-request file-count cap for `uploadWorkspaceDist`: each file costs the
* server a stat+put round trip against object storage, so request count — not
* byte size — is what keeps a batch inside the CDN proxy's response timeout. */
const DEFAULT_MAX_FILES_PER_REQUEST = 100;
async function uploadWorkspaceDist(input) {
const maxRequestBytes = input.maxRequestBytes ?? (Number(process.env.BOS_MAX_BUNDLE_UPLOAD_BYTES) || 67108864);
const maxFilesPerRequest = input.maxFilesPerRequest ?? 100;
const merged = {
stored: 0,
totalBytes: 0,
integrity: {}
};
let batch = [];
let batchBytes = 0;
const overheadPerFile = (file) => Math.ceil(file.bytes.byteLength / 3) * 4 + file.path.length + 64;
const flush = async () => {
if (batch.length === 0) return;
const result = await uploadBundle({
origin: input.origin,
apiKey: input.apiKey,
account: input.account,
gateway: input.gateway,
workspace: input.workspace,
files: batch
});
merged.stored += result.stored;
merged.totalBytes += result.totalBytes;
merged.unchanged = (merged.unchanged ?? 0) + (result.unchanged ?? 0);
Object.assign(merged.integrity, result.integrity);
merged.storage = result.storage;
batch = [];
batchBytes = 0;
};
for (const file of input.files) {
const cost = overheadPerFile(file);
if (cost > maxRequestBytes) throw new Error(`[publish] ${input.workspace}/${file.path} (${file.bytes.byteLength} bytes) exceeds the per-request upload ceiling`);
if (batchBytes + cost > maxRequestBytes || batch.length >= maxFilesPerRequest) await flush();
batch.push(file);
batchBytes += cost;
}
await flush();
return merged;
}
//#endregion
export { collectDistFiles, uploadBundle, uploadWorkspaceDist };
//# sourceMappingURL=storage.mjs.map