@heroku/sdk 0.5.3 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -90,6 +90,21 @@ export async function* streamLogs(ctx, appIdentity, options = {}) {
|
|
|
90
90
|
const reader = response.body.getReader();
|
|
91
91
|
const decoder = new TextDecoder();
|
|
92
92
|
let pending = '';
|
|
93
|
+
// Wire the caller's abort signal into the reader. `await reader.read()`
|
|
94
|
+
// parks until the next chunk arrives; a bare `signal.aborted` check
|
|
95
|
+
// between reads never runs while it's parked, so on a quiet tail the
|
|
96
|
+
// read would block indefinitely past an abort. Cancelling the reader
|
|
97
|
+
// settles the pending `read()` promise, letting the loop below observe
|
|
98
|
+
// the abort and terminate — which also closes the underlying socket.
|
|
99
|
+
const onAbort = () => {
|
|
100
|
+
reader.cancel().catch(() => { });
|
|
101
|
+
};
|
|
102
|
+
if (signal) {
|
|
103
|
+
if (signal.aborted)
|
|
104
|
+
reader.cancel().catch(() => { });
|
|
105
|
+
else
|
|
106
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
107
|
+
}
|
|
93
108
|
// Force a recreate after sessionTimeoutMs of silence; ignored
|
|
94
109
|
// when not tailing.
|
|
95
110
|
const armTimeout = () => {
|
|
@@ -109,6 +124,11 @@ export async function* streamLogs(ctx, appIdentity, options = {}) {
|
|
|
109
124
|
const { done, value } = await reader.read();
|
|
110
125
|
if (done)
|
|
111
126
|
break;
|
|
127
|
+
// A cancel triggered by the caller's signal also resolves the read
|
|
128
|
+
// as `done`; stop rather than treating it as a remote close and
|
|
129
|
+
// recreating the session.
|
|
130
|
+
if (signal?.aborted)
|
|
131
|
+
break;
|
|
112
132
|
armTimeout();
|
|
113
133
|
pending += decoder.decode(value, { stream: true });
|
|
114
134
|
if (isSse) {
|
|
@@ -156,8 +176,26 @@ export async function* streamLogs(ctx, appIdentity, options = {}) {
|
|
|
156
176
|
finally {
|
|
157
177
|
if (timeoutHandle)
|
|
158
178
|
clearTimeout(timeoutHandle);
|
|
179
|
+
signal?.removeEventListener('abort', onAbort);
|
|
180
|
+
// Tear the reader down. If a consumer stops early — `break`/`return()`
|
|
181
|
+
// out of a `for await`, or an explicit `.return()` — this finally runs
|
|
182
|
+
// while the stream is still open, and `releaseLock()` alone would leak
|
|
183
|
+
// the underlying socket (plus, when tailing, the session-recreate
|
|
184
|
+
// loop). `cancel()` closes the body; fire-and-forget because on an
|
|
185
|
+
// errored/already-closed stream it rejects with the stored error,
|
|
186
|
+
// which we don't act on. It does not release the lock, so we always
|
|
187
|
+
// `releaseLock()` afterward — otherwise the next recreate's
|
|
188
|
+
// `getReader()` throws "ReadableStream is locked". No pending read
|
|
189
|
+
// exists once the loop above has exited, so `releaseLock()` is safe.
|
|
190
|
+
reader.cancel().catch(() => { });
|
|
159
191
|
reader.releaseLock();
|
|
160
192
|
}
|
|
193
|
+
// The caller aborted (or their signal fired mid-read): terminate the
|
|
194
|
+
// whole stream, throwing the abort reason so `for await` consumers see
|
|
195
|
+
// an AbortError rather than a silent end.
|
|
196
|
+
if (signal?.aborted) {
|
|
197
|
+
throw signal.reason ?? new DOMException('The operation was aborted.', 'AbortError');
|
|
198
|
+
}
|
|
161
199
|
if (timedOut) {
|
|
162
200
|
debug('streamLogs session=%s recreating after timeout', session.id);
|
|
163
201
|
continue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroku/sdk",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
4
4
|
"description": "Heroku SDK",
|
|
5
5
|
"author": "Heroku",
|
|
6
6
|
"type": "module",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"vitest": "^4.1.4"
|
|
100
100
|
},
|
|
101
101
|
"dependencies": {
|
|
102
|
-
"@heroku/heroku-fetch": "^0.1.
|
|
102
|
+
"@heroku/heroku-fetch": "^0.1.3",
|
|
103
103
|
"@heroku/types": "^4.0.1",
|
|
104
104
|
"debug": "^4.4.0"
|
|
105
105
|
}
|