Skip to content

feat: add ZIP file import for collections#7063

Merged
bijin-bruno merged 5 commits intousebruno:mainfrom
naman-bruno:feat/zip-import
Feb 9, 2026
Merged

feat: add ZIP file import for collections#7063
bijin-bruno merged 5 commits intousebruno:mainfrom
naman-bruno:feat/zip-import

Conversation

@naman-bruno
Copy link
Collaborator

@naman-bruno naman-bruno commented Feb 5, 2026

Description

Contribution Checklist:

  • I've used AI significantly to create this pull request
  • The pull request only addresses one issue or adds one feature.
  • The pull request does not introduce any breaking changes
  • I have added screenshots or gifs to help explain the change if applicable.
  • I have read the contribution guidelines.
  • Create an issue and link to the pull request.

Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.

Publishing to New Package Managers

Please see here for more information.

Summary by CodeRabbit

  • New Features

    • Import collections from ZIP (bruno-zip) via the import UI; collection name can be derived from the ZIP.
    • New file-based import tab for choosing or dropping supported files, with format detection and ZIP submission.
  • UX Improvements

    • ZIP imports hide irrelevant format options and enforce single-ZIP and mixing-file validation.
    • Import errors display as an in-modal dismissible error banner; loading shows a fullscreen loader and successful imports show a success notification.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 5, 2026

Walkthrough

Adds ZIP-backed collection import: UI now delegates file input to a new FileTab component which emits bruno-zip submissions; a new Redux thunk importCollectionFromZip invokes an Electron IPC handler that extracts, validates, sanitizes, and moves the archive contents into the collections directory and associates it with the active workspace. (41 words)

Changes

Cohort / File(s) Summary
Import UI & entry
packages/bruno-app/src/components/Sidebar/ImportCollection/index.js, packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/index.js
Replaced inline drag/drop parsing with FileTab; added isZipImport branching that hides format controls for ZIPs and submits raw ZIP metadata as bruno-zip.
File handling component
packages/bruno-app/src/components/Sidebar/ImportCollection/FileTab.js
New component implementing drag/drop + picker, JSON/YAML/WSDL parsing, import-type detection, ZIP handling via ipcRenderer to obtain zipFilePath and emit bruno-zip submissions; surfaces errors via setErrorMessage.
UI → Redux wiring
packages/bruno-app/src/components/Sidebar/Sections/CollectionsSection/index.js, packages/bruno-app/src/components/WorkspaceHome/WorkspaceOverview/index.js
Submit handlers accept extra payload (...rest) and branch dispatch to either importCollection or new importCollectionFromZip when isZipImport is present.
Redux actions
packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
Added exported thunk importCollectionFromZip(zipFilePath, collectionLocation) which invokes IPC and adds the imported collection to the active workspace.
Electron IPC
packages/bruno-electron/src/ipc/collection.js
Added renderer:import-collection-zip handler: extracts ZIP to temp, prevents unsafe symlinks, locates bruno.json/opencollection.yml, derives/sanitizes name, ensures unique destination, moves files, and cleans up on error.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as ImportCollection / FileTab
    participant Sections as CollectionsSection / WorkspaceOverview
    participant Redux as Redux Thunk
    participant IPC as Electron IPC
    participant FS as File System

    User->>UI: drop/select ZIP
    UI->>Sections: submit({ isZipImport: true, zipFilePath, collectionName })
    Sections->>Redux: dispatch importCollectionFromZip(zipFilePath, location, ...)
    Redux->>IPC: ipcRenderer.invoke('renderer:import-collection-zip', zipFilePath, location)
    IPC->>FS: extract ZIP to temp dir
    IPC->>FS: run security checks (no escaping symlinks)
    IPC->>FS: find bruno.json / opencollection.yml, derive name
    IPC->>FS: sanitize & compute unique dest path
    IPC->>FS: move collection to target
    IPC->>FS: cleanup temp files
    IPC-->>Redux: return final collectionPath
    Redux->>Sections: resolve with collectionPath
    Sections->>User: show success or error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • lohit-bruno
  • bijin-bruno
  • helloanoop

Poem

🧳 A zip arrives, the FileTab greets the day,
IPC peels layers, temp files swept away.
Redux pins the folder, workspace lights aglow,
Names are cleaned, moved in, and finally show.
Cheers — a collection finds a proper home.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main feature added: ZIP file import capability for collections, which aligns with all substantial changes across the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js`:
- Around line 2673-2678: Replace the brittle split('/').pop() extraction with
the platform-aware path.basename to get the collection name; in the block that
checks activeWorkspace (the code that computes collectionName from
collectionPath before calling
ipcRenderer.invoke('renderer:add-collection-to-workspace', ...)), use
path.basename(collectionPath) so Windows backslashes are handled correctly and
pass that name into the payload.
- Around line 2663-2685: The importCollectionFromZip thunk uses an async Promise
executor which is an antipattern; replace the new Promise(async (resolve,
reject) => { ... }) wrapper by making the thunk itself async: export const
importCollectionFromZip = (zipFilePath, collectionLocation) => async (dispatch,
getState) => { ... }; inside, await
ipcRenderer.invoke('renderer:import-collection-zip', ...), perform the workspace
add via ipcRenderer.invoke if needed, then return collectionPath (and let thrown
errors propagate instead of calling reject) so callers receive the resolved
value or exception normally; update references to resolve/reject accordingly.

In `@packages/bruno-electron/src/ipc/collection.js`:
- Around line 2065-2071: If sanitizeName(collectionName) can return an empty
string, ensure you provide a safe fallback before building finalCollectionPath:
after computing sanitizedName in the function handling collection creation
(where sanitizeName, finalCollectionPath, collectionLocation and collectionName
are used) check if sanitizedName is falsy and replace it with a default like
"untitled" (optionally append a timestamp or UUID to avoid collisions), then run
the existing existsSync loop to append " (n)" as needed so finalCollectionPath
never resolves to the parent directory.
- Around line 2018-2086: The import handler (ipcMain.handle
'renderer:import-collection-zip') must reject ZIPs that create symlinks pointing
outside the extraction dir: after extraction to tempDir but before touching
brunoJsonPath/openCollectionYmlPath or calling fs.statSync/fs.readFileSync,
recursively walk collectionDir/extractedItems and for each entry use
fs.lstatSync to detect symbolic links; for any symlink use fs.realpathSync (or
path.resolve) to compute its target and ensure the resolved target is inside
tempDir (startsWith tempDir) — if any symlink points outside, abort the import
(clean up tempDir) and throw; additionally, when reading
bruno.json/opencollection.yml check with lstat that those specific files are not
symlinks and fail if they are, then proceed to move with fsExtra.move only after
validation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/bruno-electron/src/ipc/collection.js`:
- Line 2109: The ZIP import branch returns finalCollectionPath without emitting
the UI event, so add the same emit used by renderer:import-collection: after
computing finalCollectionPath and before returning, send the
main:collection-opened event (with finalCollectionPath) for non-workspace
imports; keep the existing main:workspace-config-updated behavior for workspace
imports and ensure you call the same sending helper (the code path that
currently emits main:collection-opened at renderer:import-collection) so the
sidebar auto-loads the imported collection.
🧹 Nitpick comments (2)
packages/bruno-electron/src/ipc/collection.js (2)

2082-2094: Silent catch blocks hide config-parsing failures.

If bruno.json or opencollection.yml is present but malformed, the import silently falls back to 'Imported Collection' with zero diagnostics. Consider logging at warn level so users (and devs debugging support tickets) know why the name wasn't picked up.

Proposed fix
           collectionName = brunoConfig?.name || collectionName;
-          } catch (e) {}
+          } catch (e) {
+            console.warn('Failed to parse opencollection.yml for collection name:', e.message);
+          }
         } else if (fs.existsSync(brunoJsonPath)) {
           try {
             const config = JSON.parse(fs.readFileSync(brunoJsonPath, 'utf8'));
             collectionName = config.name || collectionName;
-          } catch (e) {}
+          } catch (e) {
+            console.warn('Failed to parse bruno.json for collection name:', e.message);
+          }

2114-2117: Redundant outer try-catch — just re-throws.

The inner catch (line 2110) already cleans up tempDir and re-throws. The outer catch on line 2114 does nothing but throw error, adding an unnecessary nesting level.

Proposed simplification
   ipcMain.handle('renderer:import-collection-zip', async (event, zipFilePath, collectionLocation) => {
-    try {
       if (!fs.existsSync(zipFilePath)) {
         throw new Error('ZIP file does not exist');
       }
       // ... rest of validation ...
       // ... inner try-catch handles cleanup ...
-    } catch (error) {
-      throw error;
-    }
   });

@pull-request-size pull-request-size bot added size/XL and removed size/L labels Feb 6, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/bruno-app/src/components/Sidebar/ImportCollection/FileTab.js`:
- Around line 123-148: The processFiles function currently processes only the
first non-ZIP file (fileArray[0]) and silently ignores others; update
processFiles to detect multiple non-ZIP files and either (a) setErrorMessage
with a clear warning like "Multiple collection files selected. Please select
only one collection file or a single ZIP." and return, or (b) iterate and call
processFile for each file if batch imports are supported; locate and change
logic inside processFiles (referencing processFiles, processFile,
processZipFile, and setErrorMessage) to enforce one-of-two behaviors (explicit
error on multiple non-ZIP files or loop through fileArray and call processFile
for each) and ensure user feedback is shown when files are rejected.
- Around line 165-169: The file input's onChange can fail to fire when a user
re-selects the same file because the input value isn't cleared; inside
handleFileInputChange (which calls processFiles), after awaiting
processFiles(e.target.files) reset the file input value (e.target.value = '') so
subsequent selections of the same file trigger onChange again. Ensure this reset
happens only after processing completes and keep the existing null/length guard.
🧹 Nitpick comments (1)
packages/bruno-app/src/components/Sidebar/ImportCollection/FileTab.js (1)

13-34: WSDL guard catches all XML MIME types, not just WSDL.

Line 17 checks file.type === 'text/xml' || file.type === 'application/xml', which will short-circuit any XML file (e.g., a non-WSDL XML config) to raw text, skipping JSON/YAML parsing. Downstream detection (isWSDLCollection etc.) will catch it as unsupported, so it's not broken, but the comment says "Handle WSDL files" while the condition is broader than that.

Consider tightening the condition to just the .wsdl extension check, and let mistyped XML files fall through to the normal parse-and-detect path.

Suggested narrowing
-  if (file.name.endsWith('.wsdl') || file.type === 'text/xml' || file.type === 'application/xml') {
+  if (file.name.endsWith('.wsdl')) {

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
packages/bruno-app/src/components/WorkspaceHome/WorkspaceOverview/index.js (1)

60-75: ⚠️ Potential issue | 🟡 Minor

Inconsistent error toast between WorkspaceOverview and CollectionsSection.

Line 73 uses toast.error(err.message) which shows the raw error, while CollectionsSection (line 74) uses toast.error('An error occurred while importing the collection'). Consider aligning them — raw error messages from IPC can be technical and unhelpful to users.

🤖 Fix all issues with AI agents
In `@packages/bruno-electron/src/ipc/collection.js`:
- Around line 2119-2131: The two empty catch blocks around parsing
opencollection.yml and bruno.json should log the parsing errors instead of
swallowing them; update the catch for the parseCollection call (relating to
openCollectionYmlPath and brunoConfig) and the catch for JSON.parse (relating to
brunoJsonPath) to emit a clear error message including the file path and the
exception (e.g., console.error or the project's logger) while preserving the
existing fallback to 'Imported Collection'.
🧹 Nitpick comments (2)
packages/bruno-electron/src/ipc/collection.js (1)

2151-2153: Redundant outer try-catch re-throws without modification.

The outer catch at line 2152 just does throw error, which is a no-op wrapper around the inner try-catch that already handles cleanup and re-throws.

♻️ Simplified structure
   ipcMain.handle('renderer:import-collection-zip', async (event, zipFilePath, collectionLocation) => {
-    try {
       if (!fs.existsSync(zipFilePath)) {
         throw new Error('ZIP file does not exist');
       }
 
       if (!collectionLocation || !fs.existsSync(collectionLocation)) {
         throw new Error('Collection location does not exist');
       }
       // ... rest of handler unchanged ...
-    } catch (error) {
-      throw error;
-    }
   });
packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/index.js (1)

77-80: Dead code path for bruno-zip in convertCollection.

The formik.onSubmit at line 129 is never invoked for ZIP imports (guarded by line 170), so the 'bruno-zip' case here is unreachable. It's harmless as a defensive fallback, but worth a comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@packages/bruno-app/src/components/Sidebar/ImportCollection/FileTab.js`:
- Around line 75-86: The processZipFile function clears loading too early
because handleSubmit is not awaited; change processZipFile to await
handleSubmit({ rawData: { zipFilePath: filePath, collectionName }, type:
'bruno-zip' }) inside the try so setIsLoading(false) in the finally runs only
after the async import completes, and keep the existing catch (toastError) to
surface any errors from handleSubmit; reference the processZipFile function, the
handleSubmit call, setIsLoading and toastError when making this change.
- Around line 88-121: The processFile function calls handleSubmit({ rawData:
data, type }) without awaiting it, which can cause loading state to clear before
submission completes; update processFile to await handleSubmit(...) (i.e., use
await handleSubmit({ rawData: data, type })) so the try/finally lifecycle and
setIsLoading state behave correctly — locate the call inside processFile in
FileTab.js and add the missing await.

In `@packages/bruno-electron/src/ipc/collection.js`:
- Around line 2069-2086: The code uses fs.realpathSync in
validateNoExternalSymlinks which throws on dangling symlinks; replace that with
resolving the symlink target via fs.readlinkSync combined with path.resolve so
broken links don't cause ENOENT. Specifically, inside validateNoExternalSymlinks
where stat.isSymbolicLink() is checked, call const linkTarget =
fs.readlinkSync(fullPath) and then const resolvedTarget =
path.resolve(path.dirname(fullPath), linkTarget) (this handles both relative and
absolute link contents), and keep the existing check that resolvedTarget
startsWith(baseDir + path.sep) || resolvedTarget === baseDir, throwing the same
security error if it points outside; leave the surrounding recursion and
directory checks unchanged.
- Around line 2148-2153: The ZIP-import flow currently returns
finalCollectionPath after performing fsExtra.move(...) (and cleaning tempDir)
but never notifies the app; before returning finalCollectionPath add the same
notifications used by the renderer:import-collection handler: call
mainWindow.webContents.send('main:collection-opened', finalCollectionPath) and
ipcMain.emit('main:collection-opened', finalCollectionPath) (or the same payload
object used elsewhere) so the renderer sidebar picks up the new collection;
locate the block with fsExtra.move(collectionDir, finalCollectionPath), tempDir
checks, and the return finalCollectionPath and insert the two emits just prior
to the return.
🧹 Nitpick comments (1)
packages/bruno-electron/src/ipc/collection.js (1)

2158-2161: Redundant outer try-catch just re-throws.

The outer try { ... } catch (error) { throw error; } (lines 2056/2158–2160) adds no value — the inner block already throws, and the ipcMain.handle rejection path works the same either way. Removing it flattens the nesting.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/bruno-app/src/components/Sidebar/ImportCollection/FileTab.js`:
- Around line 75-86: processZipFile can produce an empty collectionName for a
file literally named ".zip"; after computing collectionName from zipFile.name in
processZipFile, add a guard: if collectionName is empty, derive a safe fallback
(for example use the file basename from filePath without extension, or
zipFile.name if non-empty, or a deterministic default like
"imported-zip-<timestamp>"). Update the logic inside processZipFile (the
collectionName assignment and immediately before calling handleSubmit) to ensure
collectionName is never an empty string.
- Around line 13-34: The current convertFileToObject function treats any XML
MIME as WSDL which short-circuits JSON/YAML parsing; change the WSDL detection
to be specific: keep filename check (file.name.endsWith('.wsdl')) and accept the
official MIME 'application/wsdl+xml', and otherwise detect WSDL by inspecting
the file text for WSDL-specific root elements (e.g. '<definitions' or
'wsdl:definitions') before returning raw text; if none of those are present,
allow the JSON/YAML parsing branches (JSON.parse and jsyaml.load) to run so
valid JSON served with an XML MIME still gets parsed; update convertFileToObject
accordingly and leave processFile behavior unchanged.
🧹 Nitpick comments (4)
packages/bruno-electron/src/ipc/collection.js (2)

2138-2147: Consider reusing generateUniqueName utility.

The counter-based dedup loop works, but the codebase already has generateUniqueName (imported at line 50, used elsewhere e.g. line 534). Using it here would reduce duplication.

♻️ Suggested refactor
         let sanitizedName = sanitizeName(collectionName);
         if (!sanitizedName) {
           sanitizedName = `untitled-${Date.now()}`;
         }
-        let finalCollectionPath = path.join(collectionLocation, sanitizedName);
-        let counter = 1;
-        while (fs.existsSync(finalCollectionPath)) {
-          finalCollectionPath = path.join(collectionLocation, `${sanitizedName} (${counter})`);
-          counter++;
-        }
+        const uniqueName = generateUniqueName(sanitizedName, (name) => fs.existsSync(path.join(collectionLocation, name)));
+        let finalCollectionPath = path.join(collectionLocation, uniqueName);

2159-2162: Redundant outer try-catch.

The outer try/catch (lines 2056, 2159-2161) only re-throws. All meaningful error handling (temp dir cleanup) is in the inner block. The outer wrapper can be removed to reduce nesting.

♻️ Suggested simplification
   ipcMain.handle('renderer:import-collection-zip', async (event, zipFilePath, collectionLocation) => {
-    try {
       if (!fs.existsSync(zipFilePath)) {
         throw new Error('ZIP file does not exist');
       }

       if (!collectionLocation || !fs.existsSync(collectionLocation)) {
         throw new Error('Collection location does not exist');
       }

       const tempDir = path.join(os.tmpdir(), `bruno_zip_import_${Date.now()}`);
       await fsExtra.ensureDir(tempDir);
       // ... rest of inner try/catch stays as-is ...
-    } catch (error) {
-      throw error;
-    }
   });
packages/bruno-app/src/components/Sidebar/ImportCollection/FileTab.js (2)

172-215: JSX rendering is clean.

One small observation: the drag-active styling uses hardcoded Tailwind dark: classes (line 182) while also pulling useTheme() for the link color (line 204). This is a minor inconsistency in theming approach, but not blocking.


36-40: Consider adding brief JSDoc to the exported component and key helper functions.

FileTab, processFile, processFiles, and convertFileToObject are non-trivial abstractions. A one-liner JSDoc describing purpose and parameters would help future maintainers. As per coding guidelines: "Add JSDoc comments to abstractions for additional details."

Also applies to: 88-88, 123-123

Comment on lines +13 to +34
const convertFileToObject = async (file) => {
const text = await file.text();

// Handle WSDL files - return as plain text
if (file.name.endsWith('.wsdl') || file.type === 'text/xml' || file.type === 'application/xml') {
return text;
}

try {
if (file.type === 'application/json' || file.name.endsWith('.json')) {
return JSON.parse(text);
}

const parsed = jsyaml.load(text);
if (typeof parsed !== 'object' || parsed === null) {
throw new Error();
}
return parsed;
} catch {
throw new Error('Failed to parse the file – ensure it is valid JSON or YAML');
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

MIME-type matching for WSDL is overly broad.

Line 17 catches text/xml and application/xml, meaning any XML file (not just WSDL) gets returned as raw text. This won't cause a crash—processFile will correctly reject it as unsupported at line 112—but the error message the user sees will be "Unsupported collection format" instead of a more helpful "Failed to parse the file". It's a minor UX gap.

Also, consider what happens when a valid JSON file is served with an XML MIME type (rare, but possible in browser drag-and-drop): the XML branch short-circuits and the JSON is never parsed.

🤖 Prompt for AI Agents
In `@packages/bruno-app/src/components/Sidebar/ImportCollection/FileTab.js` around
lines 13 - 34, The current convertFileToObject function treats any XML MIME as
WSDL which short-circuits JSON/YAML parsing; change the WSDL detection to be
specific: keep filename check (file.name.endsWith('.wsdl')) and accept the
official MIME 'application/wsdl+xml', and otherwise detect WSDL by inspecting
the file text for WSDL-specific root elements (e.g. '<definitions' or
'wsdl:definitions') before returning raw text; if none of those are present,
allow the JSON/YAML parsing branches (JSON.parse and jsyaml.load) to run so
valid JSON served with an XML MIME still gets parsed; update convertFileToObject
accordingly and leave processFile behavior unchanged.

Comment on lines +75 to +86
const processZipFile = async (zipFile) => {
setIsLoading(true);
try {
const filePath = window.ipcRenderer.getFilePath(zipFile);
const collectionName = zipFile.name.replace(/\.zip$/i, '');
await handleSubmit({ rawData: { zipFilePath: filePath, collectionName }, type: 'bruno-zip' });
} catch (err) {
toastError(err, 'Import ZIP file failed');
} finally {
setIsLoading(false);
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Edge case: a file literally named .zip produces an empty collectionName.

Line 79's regex replacement yields '' for a file named .zip. Downstream code likely uses this as a directory name, which could cause unexpected behavior or failures.

Proposed guard
   const processZipFile = async (zipFile) => {
     setIsLoading(true);
     try {
       const filePath = window.ipcRenderer.getFilePath(zipFile);
       const collectionName = zipFile.name.replace(/\.zip$/i, '');
+      if (!collectionName) {
+        throw new Error('ZIP file name is invalid');
+      }
       await handleSubmit({ rawData: { zipFilePath: filePath, collectionName }, type: 'bruno-zip' });
🤖 Prompt for AI Agents
In `@packages/bruno-app/src/components/Sidebar/ImportCollection/FileTab.js` around
lines 75 - 86, processZipFile can produce an empty collectionName for a file
literally named ".zip"; after computing collectionName from zipFile.name in
processZipFile, add a guard: if collectionName is empty, derive a safe fallback
(for example use the file basename from filePath without extension, or
zipFile.name if non-empty, or a deterministic default like
"imported-zip-<timestamp>"). Update the logic inside processZipFile (the
collectionName assignment and immediately before calling handleSubmit) to ensure
collectionName is never an empty string.

@bijin-bruno bijin-bruno merged commit 37fbdec into usebruno:main Feb 9, 2026
7 of 11 checks passed
bijin-bruno pushed a commit that referenced this pull request Feb 11, 2026
* feat: add ZIP file import for collections
@coderabbitai coderabbitai bot mentioned this pull request Mar 2, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants