From 65c4504def272afdf2f3ca95124a0a2b0f87d484 Mon Sep 17 00:00:00 2001 From: Jack Herrington Date: Thu, 3 Sep 2026 08:48:23 -0700 Subject: [PATCH] fix: await function template downloads in functions-create `downloadFromURL` wrote each template file with `res.body?.pipe(dest)`, which was never awaited. `Promise.all` therefore resolved before any write had finished, and the `npm i` that follows ran against a possibly incomplete function directory. `await pipeline(...)` closes the race. A non-OK response was also piped straight to disk, silently writing an error page where the template should be. That is now an explicit throw. Co-Authored-By: Claude Opus 5 (1M context) --- src/commands/functions/functions-create.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/commands/functions/functions-create.ts b/src/commands/functions/functions-create.ts index 093f829c56b..d819bb03664 100644 --- a/src/commands/functions/functions-create.ts +++ b/src/commands/functions/functions-create.ts @@ -4,6 +4,7 @@ import { mkdir, readdir, unlink } from 'fs/promises' import { createRequire } from 'module' import path, { dirname, join, relative } from 'path' import process from 'process' +import { pipeline } from 'stream/promises' import { fileURLToPath, pathToFileURL } from 'url' import { OptionValues } from 'commander' @@ -409,10 +410,13 @@ const downloadFromURL = async function (command, options, argumentName, function folderContents.map(async ({ download_url: downloadUrl, name }) => { try { const res = await fetch(downloadUrl) + if (!res.ok || !res.body) { + throw new Error(`HTTP ${res.status.toString()}: ${res.statusText}`) + } const fileName = path.basename(name) const finalName = path.basename(fileName, '.js') === functionName ? `${nameToUse}.js` : fileName const dest = fs.createWriteStream(path.join(fnFolder, finalName)) - res.body?.pipe(dest) + await pipeline(res.body, dest) } catch (error_) { throw new Error(`Error while retrieving ${downloadUrl} ${error_}`) }