Skip to content

fix(storage): bundle input and response#78

Merged
designcode merged 3 commits intomainfrom
fix/bundle-response
Apr 8, 2026
Merged

fix(storage): bundle input and response#78
designcode merged 3 commits intomainfrom
fix/bundle-response

Conversation

@designcode
Copy link
Copy Markdown
Collaborator

@designcode designcode commented Apr 7, 2026

Note

Medium Risk
Medium risk due to a breaking bundle() signature change and new streaming-path handling in the shared HTTP client, which could affect existing callers and error handling behavior.

Overview
bundle() now takes only keys plus an options object (bucket comes from options.config.bucket or global config) and routes requests through createStorageClient() instead of manually building/singing fetch requests.

The shared HTTP client adds a stream mode to return the raw ReadableStream body (and error if missing), and bundle() surfaces client errors directly while returning body as a stream.

Tests are updated to match the new API and to cover stream body success, missing-body errors, and updated error response parsing.

Reviewed by Cursor Bugbot for commit 8e6dc0d. Bugbot is set up for automated code reviews on this repo. Configure here.

@designcode designcode requested a review from efirs April 7, 2026 18:27
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 7, 2026

Greptile Summary

This PR refactors bundle() to accept the bucket name inside options.config.bucket rather than as a positional argument, and delegates auth/HTTP to the shared createStorageClient. It also adds a stream flag to createTigrisHttpClient so callers can receive the raw ReadableStream without body parsing.

  • P1 — null body not guarded in streaming path (shared/http-client.ts line 206): When stream: true, response.body is assigned directly without a null check. Response.body is ReadableStream | null; a 200 response with no body silently returns null as data instead of an error. The old bundle.ts had an explicit guard for this case that was removed without a replacement.

Confidence Score: 4/5

Safe to merge once the null-body guard is restored in the streaming path of http-client.ts.

One P1 finding: the removal of the response.body null check in the shared HTTP client's stream branch means a 200 response with no body propagates null to callers instead of an error, causing a runtime crash when .getReader() is called. The rest of the refactor (API shape change, auth delegation, new tests) looks correct.

shared/http-client.ts — the stream branch at line 205–206 needs a null guard for response.body.

Important Files Changed

Filename Overview
shared/http-client.ts Adds stream flag to bypass body parsing and return the raw ReadableStream; response.body can be null (no null guard) leading to a silent null being returned as TResponse.
packages/storage/src/lib/object/bundle.ts Refactors bundle() to move bucket into options, delegates HTTP + auth to createStorageClient; JSDoc example and @param are stale (still reference old 3-param signature).
packages/storage/src/lib/object/bundle.test.ts Updates tests for new API shape; removes the "no body" guard test without adding a replacement, leaving the null-body regression uncovered.

Comments Outside Diff (1)

  1. packages/storage/src/lib/object/bundle.ts, line 37-47 (link)

    P2 Stale JSDoc — still references old 3-parameter signature

    The @example block shows bundle("my-bucket", ["img_001.jpg", "img_002.jpg"]) and the @param bucketName tag documents a parameter that no longer exists. Both should be updated to reflect the new bundle(keys, options) signature where the bucket is passed inside options.config.bucket.

    ts

    • const result = await bundle(["img_001.jpg", "img_002.jpg"], {
    • config: { bucket: "my-bucket" },
    • });
    • if (result.error) throw result.error;
    • // Pipe to file, or process with a tar library
    • const reader = result.data.body.getReader();
    • @param keys - Array of object keys to include in the bundle (max 5,000).
    • @param options - Optional configuration including bucket, compression, and error handling.
    
    

Reviews (1): Last reviewed commit: "fix(storage): bundle input and response" | Re-trigger Greptile

Comment thread shared/http-client.ts
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Stream mode doesn't guard against null response.body
    • Added a null-body guard in stream mode that returns an explicit error and covered it with a bundle test for a successful response with no body.

Create PR

Or push these changes by commenting:

@cursor push d78b2062c4
Preview (d78b2062c4)
diff --git a/packages/storage/src/lib/object/bundle.test.ts b/packages/storage/src/lib/object/bundle.test.ts
--- a/packages/storage/src/lib/object/bundle.test.ts
+++ b/packages/storage/src/lib/object/bundle.test.ts
@@ -117,6 +117,30 @@
     expect(result.data?.body.getReader).toBeDefined();
   });
 
+  it('returns error when stream response has no body', async () => {
+    globalThis.fetch = vi.fn(async () => {
+      return new Response(null, {
+        status: 200,
+        headers: { 'content-type': 'application/x-tar' },
+      });
+    }) as typeof fetch;
+
+    const result = await bundle(['a.jpg'], {
+      config: {
+        bucket: 'my-bucket',
+        endpoint: 'https://test.endpoint.dev',
+        accessKeyId: 'test-key',
+        secretAccessKey: 'test-secret',
+      },
+    });
+
+    expect(result.error).toBeDefined();
+    expect(result.error?.message).toContain(
+      'No body returned from stream request'
+    );
+    expect(result.data).toBeUndefined();
+  });
+
   it('sends custom compression and error mode', async () => {
     let capturedInit: RequestInit | undefined;
 

diff --git a/shared/http-client.ts b/shared/http-client.ts
--- a/shared/http-client.ts
+++ b/shared/http-client.ts
@@ -203,6 +203,14 @@
       let data: TResponse;
 
       if (req.stream) {
+        if (!response.body) {
+          return {
+            status: response.status,
+            statusText: response.statusText,
+            headers: response.headers,
+            error: new Error('No body returned from stream request'),
+          };
+        }
         data = response.body as TResponse;
       } else {
         const contentType = response.headers.get('content-type');

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Comment thread shared/http-client.ts
Add null check for response.body in HTTP client stream mode to prevent
silent null propagation. Update bundle JSDoc to reflect current API
signature.

Assisted-by: Claude Opus 4.6 via Claude Code
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit af65a3b. Configure here.

Comment thread packages/storage/src/lib/object/bundle.ts Outdated
Prevents unhandled exceptions from network failures (DNS, timeout, etc.)
by catching and returning errors using toError, matching the pattern
used across the rest of the SDK.

Assisted-by: Claude Opus 4.6 via Claude Code
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@designcode designcode merged commit 836f112 into main Apr 8, 2026
2 checks passed
@designcode designcode deleted the fix/bundle-response branch April 8, 2026 07:31
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 8, 2026

🎉 This PR is included in version 2.16.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 8, 2026

🎉 This PR is included in version 2.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 8, 2026

🎉 This PR is included in version 2.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 8, 2026

🎉 This PR is included in version 2.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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