Conversation
📝 WalkthroughWalkthroughExtracted URL-download logic into a new exported utility Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as "CLI/Command"
participant Layer as "Layer.publish()"
participant Deploy as "Deploy/function flow"
participant Utils as "utils._downloadFromUrl"
participant FS as "Filesystem (temp dirs)"
CLI->>Layer: publish(codeUri)
alt codeUri is http(s)
Layer->>Utils: _downloadFromUrl(codeUri)
Utils->>FS: create temp dir & download file
Utils-->>Layer: local downloadedTempFile
Layer->>Layer: safe_publish_layer(..., downloadedTempFile)
Layer->>FS: remove temp zip & downloadedTempFile
else non-URL
Layer->>Layer: proceed with local path
end
CLI->>Deploy: deploy/function(codeUri)
alt codeUri is http(s)
Deploy->>Utils: _downloadFromUrl(codeUri)
Utils->>FS: create temp dir & download file
Utils-->>Deploy: local downloadedTempFile
Deploy->>FS: remove downloadedTempFile on skip/after upload
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/subCommands/layer/index.ts (1)
34-100:⚠️ Potential issue | 🟠 MajorUse
try/finallyinsafe_publish_layerfor guaranteed temp cleanup.
downloadedTempDirand generated zip cleanup run only on success/skip branches. If an exception is thrown during checksum, OSS upload, or create-layer call, temp artifacts are left behind.Suggested fix
static async safe_publish_layer( @@ - let zipPath = toZipDir; - let generateZipFilePath = ''; - if (!toZipDir.endsWith('.zip')) { + let zipPath = toZipDir; + let generateZipFilePath = ''; + try { + if (!toZipDir.endsWith('.zip')) { const zipConfig = { @@ - generateZipFilePath = (await zip(zipConfig)).outputFile; - zipPath = generateZipFilePath; - } - const latestLayer = await fcSdk.getLayerLatestVersion(layerName); - if (latestLayer) { + generateZipFilePath = (await zip(zipConfig)).outputFile; + zipPath = generateZipFilePath; + } + const latestLayer = await fcSdk.getLayerLatestVersion(layerName); + if (latestLayer) { @@ - return latestLayer; + return latestLayer; + } } - } - logger.debug(`Zip file: ${zipPath}`); - getFileSize(zipPath); - const ossConfig = await fcSdk.uploadCodeToTmpOss(zipPath); - logger.debug('ossConfig: ', ossConfig); + logger.debug(`Zip file: ${zipPath}`); + getFileSize(zipPath); + const ossConfig = await fcSdk.uploadCodeToTmpOss(zipPath); + logger.debug('ossConfig: ', ossConfig); @@ - console.log(JSON.stringify(result)); - return result; + console.log(JSON.stringify(result)); + return result; + } finally { + if (generateZipFilePath) { + try { + fs.rmSync(generateZipFilePath); + } catch (ex) { + logger.debug(`Unable to remove zip file: ${zipPath}`); + } + } + if (downloadedTempDir) { + try { + logger.debug(`Removing temp download dir: ${downloadedTempDir}`); + fs.rmSync(downloadedTempDir, { recursive: true, force: true }); + } catch (ex) { + logger.debug(`Unable to remove temp download dir: ${downloadedTempDir}`); + } + } + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/subCommands/layer/index.ts` around lines 34 - 100, The temp artifacts (downloadedTempDir and generateZipFilePath) are only deleted on the normal/skip paths, leaving leftovers on exceptions; update the function (safe_publish_layer / the block using zipPath, generateZipFilePath, downloadedTempDir, calculateCRC64, uploadCodeToTmpOss, createLayerVersion) to wrap the main work in a try block and move all cleanup logic into a finally block that always attempts to fs.rmSync(generateZipFilePath) and fs.rmSync(downloadedTempDir, { recursive: true, force: true }), logging any removal errors with logger.debug; keep existing conditional checks so you only remove files that were actually created and preserve existing debug messages in the finally.src/utils/index.ts (1)
301-304:⚠️ Potential issue | 🟠 MajorPrefix validation in
checkFcDiris over-broad and rejects valid paths.Using
startsWith(prefix)blocks unrelated paths like/various/...when checking/var. Match exact prefix directory boundaries instead.Suggested fix
for (const prefix of forbiddenPrefixes) { - if (normalizedPath.startsWith(prefix)) { + if (normalizedPath === prefix || normalizedPath.startsWith(`${prefix}/`)) { throw new Error(`Invalid ${paramName}, ${prefix} and its subdirectories are not allowed`); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/utils/index.ts` around lines 301 - 304, The check in checkFcDir currently uses normalizedPath.startsWith(prefix) which falsely rejects paths that only share a leading segment (e.g., "/various" vs "/var"); update the logic to only reject when the normalizedPath equals the forbidden prefix or lies inside it by checking boundary-aware matches: i.e., treat a forbidden prefix as matched only if normalizedPath === prefix or normalizedPath.startsWith(prefix + path.sep) (use the appropriate path.sep/posix separator for your environment) for each entry in forbiddenPrefixes; update any references to normalizedPath and forbiddenPrefixes in checkFcDir accordingly.src/subCommands/deploy/impl/function.ts (1)
286-360:⚠️ Potential issue | 🟠 MajorMake temp file cleanup exception-safe with
finally.
downloadedTempDircleanup currently runs only on normal branches. If an exception occurs after download (for example at Line 341), the temp dir can leak.Suggested fix
private async _uploadCode(): Promise<boolean> { @@ - let generateZipFilePath = ''; - if (needZip) { + let generateZipFilePath = ''; + try { + if (needZip) { setNodeModulesBinPermissions(zipPath); @@ - zipPath = generateZipFilePath; - } + zipPath = generateZipFilePath; + } @@ - const ossConfig = await this.fcSdk.uploadCodeToTmpOss(zipPath); - logger.debug('ossConfig: ', ossConfig); - _.set(this.local, 'code', ossConfig); - - if (generateZipFilePath) { - try { - fs.rmSync(generateZipFilePath); - } catch (ex) { - logger.debug(`Unable to remove zip file: ${zipPath}`); - } - } - - if (downloadedTempDir) { - try { - logger.debug(`Removing temp download dir: ${downloadedTempDir}`); - fs.rmSync(downloadedTempDir, { recursive: true, force: true }); - } catch (ex) { - logger.debug(`Unable to remove temp download dir: ${downloadedTempDir}`); - } - } - - return true; + const ossConfig = await this.fcSdk.uploadCodeToTmpOss(zipPath); + logger.debug('ossConfig: ', ossConfig); + _.set(this.local, 'code', ossConfig); + return true; + } finally { + if (generateZipFilePath) { + try { + fs.rmSync(generateZipFilePath); + } catch (ex) { + logger.debug(`Unable to remove zip file: ${zipPath}`); + } + } + if (downloadedTempDir) { + try { + logger.debug(`Removing temp download dir: ${downloadedTempDir}`); + fs.rmSync(downloadedTempDir, { recursive: true, force: true }); + } catch (ex) { + logger.debug(`Unable to remove temp download dir: ${downloadedTempDir}`); + } + } + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/subCommands/deploy/impl/function.ts` around lines 286 - 360, The temp download and generated zip cleanup can leak if an exception occurs; move the deletion of downloadedTempDir and generateZipFilePath into a finally block so they always run after processing (wrap the code between obtaining zipPath and the return/oss upload in try { ... } finally { try { fs.rmSync(generateZipFilePath) } catch{}; try { fs.rmSync(downloadedTempDir, { recursive: true, force: true }) } catch{} }). Update the block that calls calculateCRC64, this.fcSdk.uploadCodeToTmpOss, and _.set(this.local, 'code', ossConfig) to be inside the try, and ensure you only attempt to remove generateZipFilePath when it was created and not remove zipPath if it pointed to a local codeUri; reference variables downloadedTempDir, generateZipFilePath, zipPath, calculateCRC64, and uploadCodeToTmpOss when making the changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/utils/index.ts`:
- Around line 322-323: The current fixed temp directory (const tempDir =
path.join(os.tmpdir(), 'fc_code_download')) in _downloadFromUrl risks
collisions; replace it with a unique per-invocation temp directory (e.g., create
via fs.mkdtemp or fs.promises.mkdtemp using os.tmpdir() + 'fc_code_download-')
and use that new directory as the working downloadPath so each call gets its own
folder; ensure cleanup only removes the specific per-call directory (not a
shared parent) and update any logic that assumes a single shared tempDir.
---
Outside diff comments:
In `@src/subCommands/deploy/impl/function.ts`:
- Around line 286-360: The temp download and generated zip cleanup can leak if
an exception occurs; move the deletion of downloadedTempDir and
generateZipFilePath into a finally block so they always run after processing
(wrap the code between obtaining zipPath and the return/oss upload in try { ...
} finally { try { fs.rmSync(generateZipFilePath) } catch{}; try {
fs.rmSync(downloadedTempDir, { recursive: true, force: true }) } catch{} }).
Update the block that calls calculateCRC64, this.fcSdk.uploadCodeToTmpOss, and
_.set(this.local, 'code', ossConfig) to be inside the try, and ensure you only
attempt to remove generateZipFilePath when it was created and not remove zipPath
if it pointed to a local codeUri; reference variables downloadedTempDir,
generateZipFilePath, zipPath, calculateCRC64, and uploadCodeToTmpOss when making
the changes.
In `@src/subCommands/layer/index.ts`:
- Around line 34-100: The temp artifacts (downloadedTempDir and
generateZipFilePath) are only deleted on the normal/skip paths, leaving
leftovers on exceptions; update the function (safe_publish_layer / the block
using zipPath, generateZipFilePath, downloadedTempDir, calculateCRC64,
uploadCodeToTmpOss, createLayerVersion) to wrap the main work in a try block and
move all cleanup logic into a finally block that always attempts to
fs.rmSync(generateZipFilePath) and fs.rmSync(downloadedTempDir, { recursive:
true, force: true }), logging any removal errors with logger.debug; keep
existing conditional checks so you only remove files that were actually created
and preserve existing debug messages in the finally.
In `@src/utils/index.ts`:
- Around line 301-304: The check in checkFcDir currently uses
normalizedPath.startsWith(prefix) which falsely rejects paths that only share a
leading segment (e.g., "/various" vs "/var"); update the logic to only reject
when the normalizedPath equals the forbidden prefix or lies inside it by
checking boundary-aware matches: i.e., treat a forbidden prefix as matched only
if normalizedPath === prefix or normalizedPath.startsWith(prefix + path.sep)
(use the appropriate path.sep/posix separator for your environment) for each
entry in forbiddenPrefixes; update any references to normalizedPath and
forbiddenPrefixes in checkFcDir accordingly.
ℹ️ Review info
Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 72143392-0abe-4488-9c7f-04c710e13482
📒 Files selected for processing (5)
__tests__/ut/commands/invoke_test.ts__tests__/ut/utils/utils_test.tssrc/subCommands/deploy/impl/function.tssrc/subCommands/layer/index.tssrc/utils/index.ts
| logger.debug( | ||
| yellow(`skip uploadCode because code is no changed, codeChecksum=${crc64Value}`), | ||
| ); | ||
| if (downloadedTempDir) { |
There was a problem hiding this comment.
为什么是删除那个文件夹, 不是那个 zip 文件
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/subCommands/layer/index.ts (1)
52-97:⚠️ Potential issue | 🟠 MajorCleanup is not guaranteed on failures and some early returns.
downloadedTempDir/generated zip cleanup only runs in selected branches. If upload/create throws, temp artifacts are left behind. Also, checksum-skip return does not cleangenerateZipFilePath.Suggested refactor (single cleanup point)
static async safe_publish_layer(...) { let zipPath = toZipDir; let generateZipFilePath = ''; - if (!toZipDir.endsWith('.zip')) { + try { + if (!toZipDir.endsWith('.zip')) { ... - } - const latestLayer = await fcSdk.getLayerLatestVersion(layerName); - if (latestLayer) { + } + const latestLayer = await fcSdk.getLayerLatestVersion(layerName); + if (latestLayer) { ... - if (latestLayer.codeChecksum === crc64Value) { + if (latestLayer.codeChecksum === crc64Value) { ... - if (downloadedTempDir) { - ... - } - return latestLayer; + return latestLayer; + } } - } - ... - const result = await fcSdk.createLayerVersion(...); - ... - if (generateZipFilePath) { ... } - if (downloadedTempDir) { ... } - console.log(JSON.stringify(result)); - return result; + ... + const result = await fcSdk.createLayerVersion(...); + console.log(JSON.stringify(result)); + return result; + } finally { + if (generateZipFilePath) { + try { fs.rmSync(generateZipFilePath); } catch (ex) { logger.debug(`Unable to remove zip file: ${zipPath}`); } + } + if (downloadedTempDir) { + try { fs.rmSync(downloadedTempDir, { recursive: true, force: true }); } catch (ex) { logger.debug(`Unable to remove temp download dir: ${downloadedTempDir}`); } + } + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/subCommands/layer/index.ts` around lines 52 - 97, The code leaves temp artifacts when errors or early-returns occur (e.g., the checksum-skip branch and failures in fcSdk.uploadCodeToTmpOss / fcSdk.createLayerVersion); refactor the flow in src/subCommands/layer/index.ts so all temporary cleanup is done in one place: wrap the main work (checksum check, getFileSize, fcSdk.uploadCodeToTmpOss, fcSdk.createLayerVersion) in a try block and perform removal of generateZipFilePath and downloadedTempDir in a single finally block (or extract a cleanup function and call it from finally), ensuring the checksum early-return does not bypass cleanup by returning after the finally (e.g., set latestLayer/skip flag and return after finally), and keep the existing guarded fs.rmSync(... { recursive:true, force:true }) calls with try/catch and logger.debug for any removal errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/utils/index.ts`:
- Line 319: The current logger.info call logs the full download URL
(logger.info(`Downloading code from URL: ${url}`)), which can leak sensitive
query parameters; update the logging to redact query strings by parsing the url
variable with the URL class and logging only the origin + pathname (e.g., new
URL(url).origin + new URL(url).pathname) or otherwise strip/redact url.search
before passing to logger.info so only non-sensitive parts are emitted.
---
Outside diff comments:
In `@src/subCommands/layer/index.ts`:
- Around line 52-97: The code leaves temp artifacts when errors or early-returns
occur (e.g., the checksum-skip branch and failures in fcSdk.uploadCodeToTmpOss /
fcSdk.createLayerVersion); refactor the flow in src/subCommands/layer/index.ts
so all temporary cleanup is done in one place: wrap the main work (checksum
check, getFileSize, fcSdk.uploadCodeToTmpOss, fcSdk.createLayerVersion) in a try
block and perform removal of generateZipFilePath and downloadedTempDir in a
single finally block (or extract a cleanup function and call it from finally),
ensuring the checksum early-return does not bypass cleanup by returning after
the finally (e.g., set latestLayer/skip flag and return after finally), and keep
the existing guarded fs.rmSync(... { recursive:true, force:true }) calls with
try/catch and logger.debug for any removal errors.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f23cd7fb-32fc-47ab-bdae-69d0d1d0d303
📒 Files selected for processing (5)
__tests__/ut/commands/invoke_test.ts__tests__/ut/utils/utils_test.tssrc/subCommands/deploy/impl/function.tssrc/subCommands/layer/index.tssrc/utils/index.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- tests/ut/utils/utils_test.ts
- tests/ut/commands/invoke_test.ts
- src/subCommands/deploy/impl/function.ts
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/utils/index.ts (1)
320-321:⚠️ Potential issue | 🟠 MajorUse a per-invocation temp directory to avoid collisions.
A fixed
fc_code_downloadfolder can race across parallel runs and cross-cleanup each other.Suggested fix
- const tempDir = path.join(os.tmpdir(), 'fc_code_download'); + const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'fc_code_download-'));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/utils/index.ts` around lines 320 - 321, The temp directory is currently a fixed path (tempDir = path.join(os.tmpdir(), 'fc_code_download')) which can collide across concurrent runs; change it to create a per-invocation temp dir (e.g. use fs.mkdtemp or fs.mkdtempSync with a prefix like path.join(os.tmpdir(), 'fc_code_download-')) and assign that result to tempDir/downloadPath so each invocation gets a unique folder; update any code that references tempDir or downloadPath accordingly and keep existing cleanup logic to remove the created temp dir when finished.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/utils/index.ts`:
- Around line 326-333: The download filename sent to downloads() is missing the
extension (parsedPathName uses path.parse(...).name) causing downloadPath to
mismatch the actual file; fix by using the full basename (use
path.parse(...).base or path.basename(urlPath)) when constructing the name
passed to downloads() and ensure downloadPath is built using that same full
filename variable (update parsedPathName/filename usage so downloads(url, {
filename: fullFilename }) and downloadPath = path.join(tempDir, fullFilename)
are consistent).
---
Duplicate comments:
In `@src/utils/index.ts`:
- Around line 320-321: The temp directory is currently a fixed path (tempDir =
path.join(os.tmpdir(), 'fc_code_download')) which can collide across concurrent
runs; change it to create a per-invocation temp dir (e.g. use fs.mkdtemp or
fs.mkdtempSync with a prefix like path.join(os.tmpdir(), 'fc_code_download-'))
and assign that result to tempDir/downloadPath so each invocation gets a unique
folder; update any code that references tempDir or downloadPath accordingly and
keep existing cleanup logic to remove the created temp dir when finished.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0a7d0287-b6d2-4a4f-a504-86adb154e7a8
📒 Files selected for processing (5)
__tests__/ut/commands/invoke_test.ts__tests__/ut/utils/utils_test.tssrc/subCommands/deploy/impl/function.tssrc/subCommands/layer/index.tssrc/utils/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/ut/utils/utils_test.ts
| const parsedPathName = path.parse(urlPath).name; | ||
| const filename = path.basename(urlPath) || `downloaded_code_${Date.now()}`; | ||
| downloadPath = path.join(tempDir, filename); | ||
|
|
||
| await downloads(url, { | ||
| dest: tempDir, | ||
| filename: parsedPathName, | ||
| extract: false, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
git ls-files | head -20Repository: devsapp/fc3
Length of output: 468
🏁 Script executed:
fd -t f "index.ts" | grep -E "(utils|layer|deploy)" | head -20Repository: devsapp/fc3
Length of output: 211
🏁 Script executed:
cat -n src/utils/index.ts | sed -n '320,340p'Repository: devsapp/fc3
Length of output: 786
🏁 Script executed:
cat -n src/utils/index.ts | sed -n '300,350p'Repository: devsapp/fc3
Length of output: 1906
🏁 Script executed:
cat -n src/subCommands/layer/index.ts | sed -n '240,255p'Repository: devsapp/fc3
Length of output: 653
🏁 Script executed:
cat -n src/subCommands/deploy/impl/function.ts | sed -n '283,297p'Repository: devsapp/fc3
Length of output: 647
🏁 Script executed:
cat package.json | grep -A 5 -B 5 "downloads"Repository: devsapp/fc3
Length of output: 562
🏁 Script executed:
rg "from.*downloads|require.*downloads" -A 2 -B 2Repository: devsapp/fc3
Length of output: 2179
🏁 Script executed:
rg "downloadPath" src/utils/index.ts -B 3 -A 3Repository: devsapp/fc3
Length of output: 724
🏁 Script executed:
#!/bin/bash
# Check the main file structure around the lines mentioned
echo "=== src/utils/index.ts lines 320-350 ==="
head -350 src/utils/index.ts | tail -35 | cat -n
echo ""
echo "=== Package.json to find downloads version ==="
grep -A 10 '"dependencies"' package.json | grep downloads
echo ""
echo "=== Check layer/index.ts around line 247 ==="
sed -n '240,255p' src/subCommands/layer/index.ts | cat -n
echo ""
echo "=== Check function.ts around line 290 ==="
sed -n '283,300p' src/subCommands/deploy/impl/function.ts | cat -nRepository: devsapp/fc3
Length of output: 2878
🌐 Web query:
@serverless-devs/downloads v0.0.7 filename option behavior documentation
💡 Result:
In @serverless-devs/downloads v0.0.7, the options.filename parameter controls the name of the file written to disk (“文件保存的名称” / file save name). It’s optional, and defaults to demo.zip. If you don’t set it, the downloaded content will be saved using that default name (in the directory indicated by options.dest, if provided). [1]
Source(s):
- [1] Socket.dev package README for
@serverless-devs/downloads(v0.0.7): options table (filename, defaultdemo.zip).
Use the filename option with the full filename including extension.
Line 326 strips the extension from the filename (path.parse(...).name), but Line 332 passes this stripped name to the downloads() function. This creates a mismatch: downloadPath (Line 328) is calculated with the full filename including extension, but the actual downloaded file is saved without the extension. The function then returns a path that doesn't match the actual file location. This causes downstream issues in src/subCommands/layer/index.ts (line 247) and src/subCommands/deploy/impl/function.ts (line 290) when processing the returned path.
Suggested fix
- const parsedPathName = path.parse(urlPath).name;
- const filename = path.basename(urlPath) || `downloaded_code_${Date.now()}`;
+ const filename = path.basename(urlPath) || `downloaded_code_${Date.now()}.zip`;
downloadPath = path.join(tempDir, filename);
await downloads(url, {
dest: tempDir,
- filename: parsedPathName,
+ filename,
extract: false,
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const parsedPathName = path.parse(urlPath).name; | |
| const filename = path.basename(urlPath) || `downloaded_code_${Date.now()}`; | |
| downloadPath = path.join(tempDir, filename); | |
| await downloads(url, { | |
| dest: tempDir, | |
| filename: parsedPathName, | |
| extract: false, | |
| const filename = path.basename(urlPath) || `downloaded_code_${Date.now()}.zip`; | |
| downloadPath = path.join(tempDir, filename); | |
| await downloads(url, { | |
| dest: tempDir, | |
| filename, | |
| extract: false, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/utils/index.ts` around lines 326 - 333, The download filename sent to
downloads() is missing the extension (parsedPathName uses path.parse(...).name)
causing downloadPath to mismatch the actual file; fix by using the full basename
(use path.parse(...).base or path.basename(urlPath)) when constructing the name
passed to downloads() and ensure downloadPath is built using that same full
filename variable (update parsedPathName/filename usage so downloads(url, {
filename: fullFilename }) and downloadPath = path.join(tempDir, fullFilename)
are consistent).
Summary by CodeRabbit
Tests
Bug Fixes
Refactor