apify-client 3.0.0-beta.50 → 3.0.0-beta.51
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.
- package/dist/bundle.js +3 -3
- package/dist/bundle.js.map +1 -1
- package/dist/resource_clients/log.d.ts +1 -1
- package/dist/resource_clients/log.js +36 -19
- package/package.json +1 -1
|
@@ -5,6 +5,8 @@ import { ResourceClient } from '../base/resource_client.js';
|
|
|
5
5
|
import { timeoutOptionsShape } from '../timeouts.js';
|
|
6
6
|
import { cast, catchNotFoundForResourceOrThrow, concatBytes, parseArgument } from '../utils.js';
|
|
7
7
|
const logOptionsSchema = z.strictObject({ raw: z.boolean().optional(), ...timeoutOptionsShape });
|
|
8
|
+
/** How long `StreamedLog.stop()` waits for lines already in flight before aborting the log stream request. */
|
|
9
|
+
const STOP_GRACE_MILLIS = 1000;
|
|
8
10
|
/**
|
|
9
11
|
* Client for accessing Actor run or build logs.
|
|
10
12
|
*
|
|
@@ -141,6 +143,7 @@ export class StreamedLog {
|
|
|
141
143
|
#signal;
|
|
142
144
|
#streamingTask = null;
|
|
143
145
|
#stopLogging = false;
|
|
146
|
+
#stopController = new AbortController();
|
|
144
147
|
constructor(options) {
|
|
145
148
|
const { toLog, logClient, fromStart = true, signal } = options;
|
|
146
149
|
this.#destinationLog = toLog;
|
|
@@ -156,16 +159,19 @@ export class StreamedLog {
|
|
|
156
159
|
throw new Error('Streaming task already active');
|
|
157
160
|
}
|
|
158
161
|
this.#stopLogging = false;
|
|
162
|
+
this.#stopController = new AbortController();
|
|
159
163
|
this.#streamingTask = this.#streamLog();
|
|
160
164
|
}
|
|
161
165
|
/**
|
|
162
|
-
* Stop log redirection.
|
|
166
|
+
* Stop log redirection. Waits up to one second for lines already in flight, then aborts the log stream request.
|
|
163
167
|
*/
|
|
164
168
|
async stop() {
|
|
165
169
|
if (!this.#streamingTask) {
|
|
166
170
|
throw new Error('Streaming task is not active');
|
|
167
171
|
}
|
|
168
172
|
this.#stopLogging = true;
|
|
173
|
+
const stopController = this.#stopController;
|
|
174
|
+
const abortTimeout = setTimeout(() => stopController.abort(), STOP_GRACE_MILLIS);
|
|
169
175
|
try {
|
|
170
176
|
await this.#streamingTask;
|
|
171
177
|
}
|
|
@@ -175,6 +181,7 @@ export class StreamedLog {
|
|
|
175
181
|
}
|
|
176
182
|
}
|
|
177
183
|
finally {
|
|
184
|
+
clearTimeout(abortTimeout);
|
|
178
185
|
this.#streamingTask = null;
|
|
179
186
|
}
|
|
180
187
|
}
|
|
@@ -182,12 +189,15 @@ export class StreamedLog {
|
|
|
182
189
|
* Get log stream from response and redirect it to another log.
|
|
183
190
|
*/
|
|
184
191
|
async #streamLog() {
|
|
192
|
+
const signal = this.#signal
|
|
193
|
+
? AbortSignal.any([this.#signal, this.#stopController.signal])
|
|
194
|
+
: this.#stopController.signal;
|
|
185
195
|
try {
|
|
186
|
-
const logStream = await this.#logClient.stream({ raw: true, signal
|
|
196
|
+
const logStream = await this.#logClient.stream({ raw: true, signal });
|
|
187
197
|
if (!logStream) {
|
|
188
198
|
return;
|
|
189
199
|
}
|
|
190
|
-
const lastChunkRemainder = await this.#logStreamChunks(logStream);
|
|
200
|
+
const lastChunkRemainder = await this.#logStreamChunks(logStream, signal);
|
|
191
201
|
// Process whatever is left when exiting. Maybe it is incomplete, maybe it is last log without EOL.
|
|
192
202
|
const lastMessage = this.#decoder.decode(lastChunkRemainder).trim();
|
|
193
203
|
if (lastMessage.length) {
|
|
@@ -195,30 +205,37 @@ export class StreamedLog {
|
|
|
195
205
|
}
|
|
196
206
|
}
|
|
197
207
|
catch (err) {
|
|
198
|
-
if (
|
|
208
|
+
if (signal.aborted)
|
|
199
209
|
return;
|
|
200
210
|
log.warning(`Log redirection stopped due to error`, err);
|
|
201
211
|
}
|
|
202
212
|
}
|
|
203
|
-
async #logStreamChunks(logStream) {
|
|
213
|
+
async #logStreamChunks(logStream, signal) {
|
|
204
214
|
// Chunk may be incomplete. Keep remainder for next chunk.
|
|
205
215
|
let previousChunkRemainder = new Uint8Array();
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
216
|
+
try {
|
|
217
|
+
for await (const chunk of logStream) {
|
|
218
|
+
// Handle possible leftover incomplete line from previous chunk.
|
|
219
|
+
// Everything before last end of line is complete.
|
|
220
|
+
const chunkWithPreviousRemainder = new Uint8Array(previousChunkRemainder.length + chunk.length);
|
|
221
|
+
chunkWithPreviousRemainder.set(previousChunkRemainder, 0);
|
|
222
|
+
chunkWithPreviousRemainder.set(chunk, previousChunkRemainder.length);
|
|
223
|
+
const lastCompleteMessageIndex = chunkWithPreviousRemainder.lastIndexOf(0x0a);
|
|
224
|
+
previousChunkRemainder = chunkWithPreviousRemainder.slice(lastCompleteMessageIndex);
|
|
225
|
+
// Push complete part of the chunk to the buffer
|
|
226
|
+
this.#streamBuffer.push(chunkWithPreviousRemainder.slice(0, lastCompleteMessageIndex));
|
|
227
|
+
this.#logBufferContent();
|
|
228
|
+
// Keep processing the new data until stopped
|
|
229
|
+
if (this.#stopLogging) {
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
220
232
|
}
|
|
221
233
|
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
// An aborted stream still hands back its unterminated last line, so the caller can flush it.
|
|
236
|
+
if (!signal.aborted)
|
|
237
|
+
throw err;
|
|
238
|
+
}
|
|
222
239
|
return previousChunkRemainder;
|
|
223
240
|
}
|
|
224
241
|
/**
|