apify-client 3.0.0-beta.7 → 3.0.0-beta.71

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