Skip to content

Commit c3a498a

Browse files
committed
Stream iterator throws an ApiError if fetch fails
1 parent fc62230 commit c3a498a

File tree

2 files changed

+52
-5
lines changed

2 files changed

+52
-5
lines changed

index.test.ts

+38-2
Original file line numberDiff line numberDiff line change
@@ -1189,12 +1189,12 @@ describe("Replicate client", () => {
11891189
// Continue with tests for other methods
11901190

11911191
describe("Stream", () => {
1192-
function createStream(body: string | NodeJS.ReadableStream) {
1192+
function createStream(body: string | NodeJS.ReadableStream, status = 200) {
11931193
const streamEndpoint = "https://stream.replicate.com";
11941194
nock(streamEndpoint)
11951195
.get("/fake_stream")
11961196
.matchHeader("Accept", "text/event-stream")
1197-
.reply(200, body);
1197+
.reply(status, body);
11981198

11991199
return new Stream({ url: `${streamEndpoint}/fake_stream`, fetch });
12001200
}
@@ -1468,5 +1468,41 @@ describe("Replicate client", () => {
14681468
});
14691469
expect(await iterator.next()).toEqual({ done: true });
14701470
});
1471+
1472+
test("an error event in the stream raises an exception", async () => {
1473+
const stream = createStream(
1474+
`
1475+
event: output
1476+
id: EVENT_1
1477+
data: hello world
1478+
1479+
event: error
1480+
id: EVENT_2
1481+
data: An unexpected error occurred
1482+
1483+
`
1484+
.trim()
1485+
.replace(/^[ ]+/gm, "")
1486+
);
1487+
1488+
const iterator = stream[Symbol.asyncIterator]();
1489+
expect(await iterator.next()).toEqual({
1490+
done: false,
1491+
value: { event: "output", id: "EVENT_1", data: "hello world" },
1492+
});
1493+
await expect(iterator.next()).rejects.toThrowError(
1494+
"An unexpected error occurred"
1495+
);
1496+
expect(await iterator.next()).toEqual({ done: true });
1497+
});
1498+
1499+
test("an error when fetching the stream raises an exception", async () => {
1500+
const stream = createStream("{}", 500);
1501+
const iterator = stream[Symbol.asyncIterator]();
1502+
await expect(iterator.next()).rejects.toThrowError(
1503+
"Request to https://stream.replicate.com/fake_stream failed with status 500 Internal Server Error: {}."
1504+
);
1505+
expect(await iterator.next()).toEqual({ done: true });
1506+
});
14711507
});
14721508
});

lib/stream.js

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
// Attempt to use readable-stream if available, attempt to use the built-in stream module.
2+
3+
const ApiError = require("./error");
4+
25
let Readable;
36
try {
47
Readable = require("readable-stream").Readable;
@@ -107,15 +110,23 @@ class Stream extends Readable {
107110
}
108111

109112
async *[Symbol.asyncIterator]() {
110-
const response = await this.fetch(this.url, {
113+
const init = {
111114
...this.options,
112115
headers: {
113116
Accept: "text/event-stream",
114117
},
115-
});
118+
};
119+
const response = await this.fetch(this.url, init);
116120

117121
if (!response.ok) {
118-
throw new Error();
122+
// The cross-fetch shim doesn't accept Request objects so we create one here.
123+
const request = new Request(this.url, init);
124+
const text = await response.text();
125+
throw new ApiError(
126+
`Request to ${request.url} failed with status ${response.status} ${response.statusText}: ${text}.`,
127+
request,
128+
response
129+
);
119130
}
120131

121132
let partialChunk = "";

0 commit comments

Comments
 (0)