MCPcopy Create free account
hub / github.com/observablehq/framework / uploadFiles

Method uploadFiles

src/deploy.ts:583–639  ·  view source on GitHub ↗
(deployId: string, buildFilePaths: string[])

Source from the content-addressed store, hash-verified

581 }
582
583 private async uploadFiles(deployId: string, buildFilePaths: string[]) {
584 const progressSpinner = this.effects.clack.spinner();
585 progressSpinner.start("");
586
587 // upload a manifest before uploading the files
588 progressSpinner.message("Hashing local files");
589 const manifestFileInfo: DeployManifestFile[] = [];
590 await runAllWithConcurrencyLimit(buildFilePaths, async (path) => {
591 const fullPath = join(this.deployOptions.config.output, path);
592 const statInfo = await stat(fullPath);
593 const hash = createHash("sha512")
594 .update(await readFile(fullPath))
595 .digest("base64");
596 manifestFileInfo.push({path, size: statInfo.size, hash});
597 });
598 progressSpinner.message("Sending file manifest to server");
599 const instructions = await this.apiClient.postDeployManifest(deployId, manifestFileInfo);
600 const fileErrors: {path: string; detail: string | null}[] = [];
601 for (const fileInstruction of instructions.files) {
602 if (fileInstruction.status === "error") {
603 fileErrors.push({path: fileInstruction.path, detail: fileInstruction.detail});
604 }
605 }
606 if (fileErrors.length) {
607 this.effects.clack.log.error(
608 "The server rejected some files from the upload:\n\n" +
609 fileErrors.map(({path, detail}) => ` - ${path} - ${detail ? `(${detail})` : "no details"}`).join("\n")
610 );
611 }
612 if (instructions.status === "error" || fileErrors.length) {
613 throw new CliError(`Server rejected deploy manifest${instructions.detail ? `: ${instructions.detail}` : ""}`);
614 }
615 const filesToUpload: string[] = instructions.files
616 .filter((instruction) => instruction.status === "upload")
617 .map((instruction) => instruction.path);
618
619 // Upload the files
620 const rateLimiter = new RateLimiter(5);
621 const waitForRateLimit = filesToUpload.length <= 300 ? async () => {} : () => rateLimiter.wait();
622
623 await runAllWithConcurrencyLimit(
624 filesToUpload,
625 async (path, i) => {
626 await waitForRateLimit();
627 progressSpinner.message(
628 `${i + 1} / ${filesToUpload.length} ${faint("uploading")} ${path.slice(0, this.effects.outputColumns - 17)}`
629 );
630 await this.apiClient.postDeployFile(deployId, join(this.deployOptions.config.output, path), path);
631 },
632 {maxConcurrency: this.deployOptions.maxConcurrency}
633 );
634 progressSpinner.stop(
635 `${filesToUpload.length} uploaded, ${buildFilePaths.length - filesToUpload.length} unchanged, ${
636 buildFilePaths.length
637 } total.`
638 );
639 }
640

Callers 2

startNewDeployMethod · 0.95

Calls 9

waitMethod · 0.95
spinnerMethod · 0.80
postDeployManifestMethod · 0.80
postDeployFileMethod · 0.80
stopMethod · 0.80
startMethod · 0.45
messageMethod · 0.45
errorMethod · 0.45

Tested by

no test coverage detected