apify-client 3.0.0-beta.6 → 3.0.0-beta.60

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.
Files changed (116) hide show
  1. package/README.md +10 -2
  2. package/dist/apify_api_error.d.ts +11 -26
  3. package/dist/apify_api_error.js +31 -22
  4. package/dist/apify_client.d.ts +125 -10
  5. package/dist/apify_client.js +133 -48
  6. package/dist/base/api_client.d.ts +7 -7
  7. package/dist/base/api_client.js +36 -36
  8. package/dist/base/resource_client.d.ts +21 -8
  9. package/dist/base/resource_client.js +67 -33
  10. package/dist/base/resource_collection_client.d.ts +12 -4
  11. package/dist/base/resource_collection_client.js +34 -11
  12. package/dist/body_parser.d.ts +3 -4
  13. package/dist/body_parser.js +19 -26
  14. package/dist/bundle.js +27 -22
  15. package/dist/bundle.js.map +1 -1
  16. package/dist/generated/api.d.ts +785 -290
  17. package/dist/generated/schemas.d.ts +771 -798
  18. package/dist/generated/schemas.js +183 -179
  19. package/dist/http_clients/axios.d.ts +60 -0
  20. package/dist/http_clients/axios.js +117 -0
  21. package/dist/http_clients/base.d.ts +331 -0
  22. package/dist/http_clients/base.js +563 -0
  23. package/dist/http_clients/index.d.ts +4 -0
  24. package/dist/http_clients/index.js +2 -0
  25. package/dist/http_compressors/base.d.ts +40 -0
  26. package/dist/http_compressors/base.js +9 -0
  27. package/dist/http_compressors/brotli.d.ts +33 -0
  28. package/dist/http_compressors/brotli.js +39 -0
  29. package/dist/http_compressors/gzip.d.ts +33 -0
  30. package/dist/http_compressors/gzip.js +39 -0
  31. package/dist/http_compressors/index.d.ts +6 -0
  32. package/dist/http_compressors/index.js +2 -0
  33. package/dist/http_compressors/resolve.d.ts +23 -0
  34. package/dist/http_compressors/resolve.js +27 -0
  35. package/dist/index.d.ts +4 -1
  36. package/dist/index.js +3 -1
  37. package/dist/invalid_response_body_error.d.ts +13 -0
  38. package/dist/invalid_response_body_error.js +17 -0
  39. package/dist/models.d.ts +162 -194
  40. package/dist/models.js +6 -4
  41. package/dist/resource_clients/actor.d.ts +74 -34
  42. package/dist/resource_clients/actor.js +87 -56
  43. package/dist/resource_clients/actor_collection.d.ts +18 -14
  44. package/dist/resource_clients/actor_collection.js +11 -3
  45. package/dist/resource_clients/actor_env_var.d.ts +11 -4
  46. package/dist/resource_clients/actor_env_var.js +16 -6
  47. package/dist/resource_clients/actor_env_var_collection.d.ts +16 -20
  48. package/dist/resource_clients/actor_env_var_collection.js +17 -11
  49. package/dist/resource_clients/actor_version.d.ts +20 -5
  50. package/dist/resource_clients/actor_version.js +19 -9
  51. package/dist/resource_clients/actor_version_collection.d.ts +18 -19
  52. package/dist/resource_clients/actor_version_collection.js +19 -11
  53. package/dist/resource_clients/build.d.ts +20 -7
  54. package/dist/resource_clients/build.js +35 -15
  55. package/dist/resource_clients/build_collection.d.ts +7 -5
  56. package/dist/resource_clients/build_collection.js +4 -1
  57. package/dist/resource_clients/dataset.d.ts +53 -35
  58. package/dist/resource_clients/dataset.js +105 -80
  59. package/dist/resource_clients/dataset_collection.d.ts +10 -6
  60. package/dist/resource_clients/dataset_collection.js +13 -2
  61. package/dist/resource_clients/key_value_store.d.ts +73 -23
  62. package/dist/resource_clients/key_value_store.js +86 -43
  63. package/dist/resource_clients/key_value_store_collection.d.ts +10 -6
  64. package/dist/resource_clients/key_value_store_collection.js +13 -2
  65. package/dist/resource_clients/log.d.ts +13 -20
  66. package/dist/resource_clients/log.js +141 -59
  67. package/dist/resource_clients/request_queue.d.ts +74 -36
  68. package/dist/resource_clients/request_queue.js +182 -106
  69. package/dist/resource_clients/request_queue_collection.d.ts +9 -5
  70. package/dist/resource_clients/request_queue_collection.js +9 -3
  71. package/dist/resource_clients/run.d.ts +119 -28
  72. package/dist/resource_clients/run.js +190 -49
  73. package/dist/resource_clients/run_collection.d.ts +5 -3
  74. package/dist/resource_clients/run_collection.js +4 -1
  75. package/dist/resource_clients/schedule.d.ts +16 -7
  76. package/dist/resource_clients/schedule.js +30 -22
  77. package/dist/resource_clients/schedule_collection.d.ts +6 -2
  78. package/dist/resource_clients/schedule_collection.js +9 -3
  79. package/dist/resource_clients/store_collection.d.ts +6 -4
  80. package/dist/resource_clients/store_collection.js +4 -1
  81. package/dist/resource_clients/task.d.ts +36 -13
  82. package/dist/resource_clients/task.js +79 -46
  83. package/dist/resource_clients/task_collection.d.ts +9 -5
  84. package/dist/resource_clients/task_collection.js +9 -3
  85. package/dist/resource_clients/user.d.ts +19 -10
  86. package/dist/resource_clients/user.js +42 -38
  87. package/dist/resource_clients/webhook.d.ts +17 -8
  88. package/dist/resource_clients/webhook.js +30 -22
  89. package/dist/resource_clients/webhook_collection.d.ts +8 -4
  90. package/dist/resource_clients/webhook_collection.js +9 -3
  91. package/dist/resource_clients/webhook_dispatch.d.ts +4 -1
  92. package/dist/resource_clients/webhook_dispatch.js +7 -2
  93. package/dist/resource_clients/webhook_dispatch_collection.d.ts +3 -1
  94. package/dist/resource_clients/webhook_dispatch_collection.js +4 -1
  95. package/dist/runtime/node.d.ts +6 -0
  96. package/dist/runtime/node.js +49 -0
  97. package/dist/runtime/types.d.ts +52 -0
  98. package/dist/runtime/types.js +1 -0
  99. package/dist/runtime/web.d.ts +5 -0
  100. package/dist/runtime/web.js +18 -0
  101. package/dist/schemas.d.ts +1 -1
  102. package/dist/schemas.js +1 -1
  103. package/dist/statistics.d.ts +1 -6
  104. package/dist/statistics.js +2 -2
  105. package/dist/timeouts.d.ts +84 -0
  106. package/dist/timeouts.js +37 -0
  107. package/dist/timezones.d.ts +1 -1
  108. package/dist/utils.d.ts +89 -53
  109. package/dist/utils.js +214 -161
  110. package/dist/wait_for_resources.d.ts +8 -0
  111. package/dist/wait_for_resources.js +39 -0
  112. package/package.json +32 -29
  113. package/dist/http_client.d.ts +0 -88
  114. package/dist/http_client.js +0 -255
  115. package/dist/interceptors.d.ts +0 -19
  116. package/dist/interceptors.js +0 -105
@@ -1,7 +1,20 @@
1
1
  import c from 'ansi-colors';
2
+ import { z } from 'zod';
2
3
  import log, { Logger, LogLevel } from '@apify/log';
3
4
  import { ResourceClient } from '../base/resource_client.js';
4
- import { cast, catchNotFoundOrThrow } from '../utils.js';
5
+ import { timeoutOptionsShape } from '../timeouts.js';
6
+ import { cast, catchNotFoundForResourceOrThrow, concatBytes, parseArgument, sleep } from '../utils.js';
7
+ /**
8
+ * Pause before the first reopen of a log stream that ended before the run logged anything. Each further reopen doubles
9
+ * the pause up to `EMPTY_LOG_STREAM_MAX_RETRY_MILLIS`, which bounds the request rate while a run waits long to start,
10
+ * for example for free memory.
11
+ */
12
+ const EMPTY_LOG_STREAM_RETRY_MILLIS = 500;
13
+ /** Upper bound on the pause between reopens of an empty log stream. */
14
+ const EMPTY_LOG_STREAM_MAX_RETRY_MILLIS = 5_000;
15
+ const logOptionsSchema = z.strictObject({ raw: z.boolean().optional(), ...timeoutOptionsShape });
16
+ /** How long `StreamedLog.stop()` waits for lines already in flight before aborting the log stream request. */
17
+ const STOP_GRACE_MILLIS = 1000;
5
18
  /**
6
19
  * Client for accessing Actor run or build logs.
7
20
  *
@@ -39,21 +52,26 @@ export class LogClient extends ResourceClient {
39
52
  *
40
53
  * @param options - Log retrieval options.
41
54
  * @param options.raw - If `true`, returns raw log content without any processing. Default is `false`.
42
- * @returns The log content as a string, or `undefined` if it does not exist.
55
+ * @param options.timeoutSecs - Timeout for the API request. Default is `'long'`.
56
+ * @returns The log content as a string, or `undefined` if it does not exist. A chained client such as
57
+ * `run.log()` throws an `ApifyApiError` on a 404, since the run itself may be what is missing.
43
58
  * @see https://docs.apify.com/api/v2/log-get
44
59
  */
45
60
  async get(options = {}) {
61
+ const { timeoutSecs = 'long', signal, ...params } = parseArgument(options, logOptionsSchema, 'LogOptions');
46
62
  const requestOpts = {
47
- url: this._url(),
63
+ url: this.buildUrl(),
48
64
  method: 'GET',
49
- params: this._params(options),
65
+ params: this.buildParams(params),
66
+ timeoutSecs,
67
+ signal,
50
68
  };
51
69
  try {
52
70
  const response = await this.httpClient.call(requestOpts);
53
71
  return cast(response.data);
54
72
  }
55
73
  catch (err) {
56
- catchNotFoundOrThrow(err);
74
+ catchNotFoundForResourceOrThrow(err, this.id);
57
75
  }
58
76
  return undefined;
59
77
  }
@@ -62,26 +80,31 @@ export class LogClient extends ResourceClient {
62
80
  *
63
81
  * @param options - Log retrieval options.
64
82
  * @param options.raw - If `true`, returns raw log content without any processing. Default is `false`.
65
- * @returns The log content as a Readable stream, or `undefined` if it does not exist.
83
+ * @param options.timeoutSecs - Timeout for the API request. Default is `'long'`.
84
+ * @returns The log content as a Readable stream, or `undefined` if it does not exist. A chained client such as
85
+ * `run.log()` throws an `ApifyApiError` on a 404, since the run itself may be what is missing.
66
86
  * @see https://docs.apify.com/api/v2/log-get
67
87
  */
68
88
  async stream(options = {}) {
89
+ const { timeoutSecs = 'long', signal, raw } = parseArgument(options, logOptionsSchema, 'LogOptions');
69
90
  const params = {
70
91
  stream: true,
71
- raw: options.raw,
92
+ raw,
72
93
  };
73
94
  const requestOpts = {
74
- url: this._url(),
95
+ url: this.buildUrl(),
75
96
  method: 'GET',
76
- params: this._params(params),
97
+ params: this.buildParams(params),
77
98
  responseType: 'stream',
99
+ timeoutSecs,
100
+ signal,
78
101
  };
79
102
  try {
80
103
  const response = await this.httpClient.call(requestOpts);
81
104
  return cast(response.data);
82
105
  }
83
106
  catch (err) {
84
- catchNotFoundOrThrow(err);
107
+ catchNotFoundForResourceOrThrow(err, this.id);
85
108
  }
86
109
  return undefined;
87
110
  }
@@ -119,39 +142,50 @@ export class LoggerActorRedirect extends Logger {
119
142
  * @since Added in 2.20.0
120
143
  */
121
144
  export class StreamedLog {
122
- destinationLog;
123
- streamBuffer = [];
124
- splitMarker = /(?:\n|^)(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)/g;
125
- relevancyTimeLimit;
126
- logClient;
127
- streamingTask = null;
128
- stopLogging = false;
145
+ #destinationLog;
146
+ #streamBuffer = [];
147
+ #decoder = new TextDecoder();
148
+ #splitMarker = /(?:\n|^)(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)/g;
149
+ #relevancyTimeLimit;
150
+ #logClient;
151
+ #signal;
152
+ #streamingTask = null;
153
+ #stopLogging = false;
154
+ #stopController = new AbortController();
155
+ #wakeController = new AbortController();
129
156
  constructor(options) {
130
- const { toLog, logClient, fromStart = true } = options;
131
- this.destinationLog = toLog;
132
- this.logClient = logClient;
133
- this.relevancyTimeLimit = fromStart ? null : new Date();
157
+ const { toLog, logClient, fromStart = true, signal } = options;
158
+ this.#destinationLog = toLog;
159
+ this.#logClient = logClient;
160
+ this.#signal = signal;
161
+ this.#relevancyTimeLimit = fromStart ? null : new Date();
134
162
  }
135
163
  /**
136
164
  * Start log redirection.
137
165
  */
138
166
  start() {
139
- if (this.streamingTask) {
167
+ if (this.#streamingTask) {
140
168
  throw new Error('Streaming task already active');
141
169
  }
142
- this.stopLogging = false;
143
- this.streamingTask = this.streamLog();
170
+ this.#stopLogging = false;
171
+ this.#stopController = new AbortController();
172
+ this.#wakeController = new AbortController();
173
+ this.#streamingTask = this.#streamLog();
144
174
  }
145
175
  /**
146
- * Stop log redirection.
176
+ * Stop log redirection. Waits up to one second for lines already in flight, then aborts the log stream request. If
177
+ * no stream has delivered anything yet, reads the whole log in one request instead.
147
178
  */
148
179
  async stop() {
149
- if (!this.streamingTask) {
180
+ if (!this.#streamingTask) {
150
181
  throw new Error('Streaming task is not active');
151
182
  }
152
- this.stopLogging = true;
183
+ this.#stopLogging = true;
184
+ this.#wakeController.abort();
185
+ const stopController = this.#stopController;
186
+ const abortTimeout = setTimeout(() => stopController.abort(), STOP_GRACE_MILLIS);
153
187
  try {
154
- await this.streamingTask;
188
+ await this.#streamingTask;
155
189
  }
156
190
  catch (err) {
157
191
  if (!(err instanceof Error && err.name === 'AbortError')) {
@@ -159,73 +193,121 @@ export class StreamedLog {
159
193
  }
160
194
  }
161
195
  finally {
162
- this.streamingTask = null;
196
+ clearTimeout(abortTimeout);
197
+ this.#streamingTask = null;
163
198
  }
164
199
  }
165
200
  /**
166
201
  * Get log stream from response and redirect it to another log.
167
202
  */
168
- async streamLog() {
169
- const logStream = await this.logClient.stream({ raw: true });
170
- if (!logStream) {
171
- return;
172
- }
203
+ async #streamLog() {
204
+ const signal = this.#signal
205
+ ? AbortSignal.any([this.#signal, this.#stopController.signal])
206
+ : this.#stopController.signal;
173
207
  try {
174
- const lastChunkRemainder = await this.logStreamChunks(logStream);
208
+ let lastChunkRemainder;
209
+ let retryMillis = EMPTY_LOG_STREAM_RETRY_MILLIS;
210
+ // The API serves the log of a run that has not logged anything yet as an empty stream that ends at once,
211
+ // so reopen it until the first bytes arrive. If stopped before any bytes arrive, fetch it in one request.
212
+ while (!lastChunkRemainder) {
213
+ if (this.#stopLogging) {
214
+ // `signal` is aborted once the stop grace period ends, which would cut this read short.
215
+ const logContent = await this.#logClient.get({ raw: true, signal: this.#signal });
216
+ lastChunkRemainder = await this.#logStreamChunks([new TextEncoder().encode(logContent ?? '')], signal);
217
+ break;
218
+ }
219
+ let logStream;
220
+ try {
221
+ logStream = await this.#logClient.stream({ raw: true, signal });
222
+ }
223
+ catch (err) {
224
+ // The stop grace period ended while the stream was connecting, so read the log in one request.
225
+ if (this.#stopLogging && !this.#signal?.aborted)
226
+ continue;
227
+ throw err;
228
+ }
229
+ if (!logStream) {
230
+ return;
231
+ }
232
+ // A stream opened during stop() would be cut after its first chunk, so read the log in one request.
233
+ if (this.#stopLogging) {
234
+ logStream.destroy();
235
+ continue;
236
+ }
237
+ lastChunkRemainder = await this.#logStreamChunks(logStream, signal);
238
+ if (!lastChunkRemainder) {
239
+ await sleep(retryMillis, AbortSignal.any([signal, this.#wakeController.signal]));
240
+ retryMillis = Math.min(retryMillis * 2, EMPTY_LOG_STREAM_MAX_RETRY_MILLIS);
241
+ }
242
+ }
175
243
  // Process whatever is left when exiting. Maybe it is incomplete, maybe it is last log without EOL.
176
- const lastMessage = Buffer.from(lastChunkRemainder).toString().trim();
244
+ const lastMessage = this.#decoder.decode(lastChunkRemainder).trim();
177
245
  if (lastMessage.length) {
178
- this.destinationLog.info(lastMessage);
246
+ this.#destinationLog.info(lastMessage);
179
247
  }
180
248
  }
181
249
  catch (err) {
250
+ if (signal.aborted)
251
+ return;
182
252
  log.warning(`Log redirection stopped due to error`, err);
183
253
  }
184
254
  }
185
- async logStreamChunks(logStream) {
255
+ /**
256
+ * Redirect every complete message in the chunks and return the incomplete rest, or `undefined` when there were no
257
+ * chunks at all.
258
+ */
259
+ async #logStreamChunks(logStream, signal) {
186
260
  // Chunk may be incomplete. Keep remainder for next chunk.
187
- let previousChunkRemainder = new Uint8Array();
188
- for await (const chunk of logStream) {
189
- // Handle possible leftover incomplete line from previous chunk.
190
- // Everything before last end of line is complete.
191
- const chunkWithPreviousRemainder = new Uint8Array(previousChunkRemainder.length + chunk.length);
192
- chunkWithPreviousRemainder.set(previousChunkRemainder, 0);
193
- chunkWithPreviousRemainder.set(chunk, previousChunkRemainder.length);
194
- const lastCompleteMessageIndex = chunkWithPreviousRemainder.lastIndexOf(0x0a);
195
- previousChunkRemainder = chunkWithPreviousRemainder.slice(lastCompleteMessageIndex);
196
- // Push complete part of the chunk to the buffer
197
- this.streamBuffer.push(Buffer.from(chunkWithPreviousRemainder.slice(0, lastCompleteMessageIndex)));
198
- this.logBufferContent();
199
- // Keep processing the new data until stopped
200
- if (this.stopLogging) {
201
- break;
261
+ let previousChunkRemainder;
262
+ try {
263
+ for await (const chunk of logStream) {
264
+ // Handle possible leftover incomplete line from previous chunk.
265
+ // Everything before last end of line is complete.
266
+ previousChunkRemainder ??= new Uint8Array();
267
+ const chunkWithPreviousRemainder = new Uint8Array(previousChunkRemainder.length + chunk.length);
268
+ chunkWithPreviousRemainder.set(previousChunkRemainder, 0);
269
+ chunkWithPreviousRemainder.set(chunk, previousChunkRemainder.length);
270
+ const lastCompleteMessageIndex = chunkWithPreviousRemainder.lastIndexOf(0x0a);
271
+ previousChunkRemainder = chunkWithPreviousRemainder.slice(lastCompleteMessageIndex);
272
+ // Push complete part of the chunk to the buffer
273
+ this.#streamBuffer.push(chunkWithPreviousRemainder.slice(0, lastCompleteMessageIndex));
274
+ this.#logBufferContent();
275
+ // Keep processing the new data until stopped
276
+ if (this.#stopLogging) {
277
+ break;
278
+ }
202
279
  }
203
280
  }
281
+ catch (err) {
282
+ // An aborted stream still hands back its unterminated last line, so the caller can flush it.
283
+ if (!signal.aborted)
284
+ throw err;
285
+ }
204
286
  return previousChunkRemainder;
205
287
  }
206
288
  /**
207
289
  * Parse the buffer and log complete messages.
208
290
  */
209
- logBufferContent() {
210
- const allParts = Buffer.concat(this.streamBuffer).toString().split(this.splitMarker).slice(1);
291
+ #logBufferContent() {
292
+ const allParts = this.#decoder.decode(concatBytes(this.#streamBuffer)).split(this.#splitMarker).slice(1);
211
293
  // Parse the buffer parts into complete messages
212
294
  const messageMarkers = allParts.filter((_, i) => i % 2 === 0);
213
295
  const messageContents = allParts.filter((_, i) => i % 2 !== 0);
214
- this.streamBuffer = [];
296
+ this.#streamBuffer = [];
215
297
  messageMarkers.forEach((marker, index) => {
216
298
  const decodedMarker = marker;
217
299
  const decodedContent = messageContents[index];
218
- if (this.relevancyTimeLimit) {
300
+ if (this.#relevancyTimeLimit) {
219
301
  // Log only relevant messages. Ignore too old log messages.
220
302
  const logTime = new Date(decodedMarker);
221
- if (logTime < this.relevancyTimeLimit) {
303
+ if (logTime < this.#relevancyTimeLimit) {
222
304
  return;
223
305
  }
224
306
  }
225
307
  const message = decodedMarker + decodedContent;
226
308
  // Original log level information is not available. Log all on info level. Log level could be guessed for
227
309
  // some logs, but for any multiline logs such guess would be probably correct only for the first line.
228
- this.destinationLog.info(message.trim());
310
+ this.#destinationLog.info(message.trim());
229
311
  });
230
312
  }
231
313
  }