Bug
packages/core/storage-js/src/packages/StreamDownloadBuilder.ts implements PromiseLike, not Promise. As a result:
.catch() and .finally() are not defined on the builder, so common consumer patterns fail at compile time (in strict mode) or at runtime.
- The builder is missing
[Symbol.toStringTag], so Object.prototype.toString.call(builder) returns [object Object] instead of [object StreamDownloadBuilder].
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()
Bug
packages/core/storage-js/src/packages/StreamDownloadBuilder.tsimplementsPromiseLike, notPromise. As a result:.catch()and.finally()are not defined on the builder, so common consumer patterns fail at compile time (in strict mode) or at runtime.[Symbol.toStringTag], soObject.prototype.toString.call(builder)returns[object Object]instead of[object StreamDownloadBuilder].thencallsthis.execute()directly, so the underlyingdownloadFn(a fetch call) runs once perawait. Awaiting the same builder twice issues two HTTP requests.The sibling
BlobDownloadBuilder.tsalready follows the correct pattern: it implementsPromise, declares[Symbol.toStringTag], definescatchandfinally, and caches the in-flight promise in a privategetPromise()helper.StreamDownloadBuildershould mirror that shape.Reproduction
Expected
StreamDownloadBuildershould match thePromisecontract used byBlobDownloadBuilder, so awaiting twice does not re-fetch,.catch()and.finally()work, andSymbol.toStringTagis set.Environment
@supabase/storage-jscurrent master.download(...).asStream()