Skip to content

Bug: StreamDownloadBuilder is missing Promise interface methods and re-runs executor on every await #2366

@oniani1

Description

@oniani1

Bug

packages/core/storage-js/src/packages/StreamDownloadBuilder.ts implements PromiseLike, not Promise. As a result:

  1. .catch() and .finally() are not defined on the builder, so common consumer patterns fail at compile time (in strict mode) or at runtime.
  2. The builder is missing [Symbol.toStringTag], so Object.prototype.toString.call(builder) returns [object Object] instead of [object StreamDownloadBuilder].
  3. then calls this.execute() directly, so the underlying downloadFn (a fetch call) runs once per await. Awaiting the same builder twice issues two HTTP requests.

The sibling BlobDownloadBuilder.ts already follows the correct pattern: it implements Promise, declares [Symbol.toStringTag], defines catch and finally, and caches the in-flight promise in a private getPromise() helper. StreamDownloadBuilder should mirror that shape.

Reproduction

const downloadFn = jest.fn(async () => new Response('hello'))
const builder = new StreamDownloadBuilder(downloadFn, false)

await builder
await builder
await builder

// expected: downloadFn called 1 time (cached)
// actual:   downloadFn called 3 times
const builder = new StreamDownloadBuilder(async () => new Response(''), false)
builder.catch(() => {})    // TS2339: Property catch does not exist
builder.finally(() => {})  // TS2339: Property finally does not exist
builder[Symbol.toStringTag] // undefined

Expected

StreamDownloadBuilder should match the Promise contract used by BlobDownloadBuilder, so awaiting twice does not re-fetch, .catch() and .finally() work, and Symbol.toStringTag is set.

Environment

  • @supabase/storage-js current master
  • Affects every caller of .download(...).asStream()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions