async-lube 2.0.0 → 2.1.0
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/CHANGELOG.md +34 -0
- package/README.md +1 -1
- package/docs/http.md +1 -1
- package/docs/reference.md +4 -2
- package/docs/tasks.md +19 -2
- package/package.json +4 -5
- package/public.d.ts +112 -0
- package/src/attempt.js +17 -7
- package/src/durable/context.js +107 -64
- package/src/durable/error.js +39 -15
- package/src/durable/index.js +18 -10
- package/src/durable/journal.js +29 -14
- package/src/durable/json.js +26 -9
- package/src/durable/lease.js +15 -5
- package/src/durable/persist.js +60 -22
- package/src/durable/run.js +103 -41
- package/src/durable/send.js +30 -13
- package/src/durable/serve.js +30 -24
- package/src/durable/state.js +28 -13
- package/src/durable/store.js +19 -10
- package/src/durable/watch.js +31 -11
- package/src/errors.js +9 -3
- package/src/flow/attempts.js +49 -29
- package/src/flow/builder.js +260 -161
- package/src/flow/compile.js +86 -66
- package/src/flow/graph.js +108 -42
- package/src/flow/index.js +13 -7
- package/src/flow/items.js +79 -46
- package/src/flow/nodes.js +267 -117
- package/src/flow/refs.js +27 -13
- package/src/flow/resources.js +9 -3
- package/src/flow/run.js +287 -114
- package/src/flow/streams.js +44 -19
- package/src/flow/subflows.js +93 -53
- package/src/http/config.js +11 -5
- package/src/http/index.js +25 -14
- package/src/http/refresh.js +29 -12
- package/src/http/request.js +246 -151
- package/src/http/response.js +105 -50
- package/src/http/retry.js +52 -27
- package/src/http/socket.js +220 -172
- package/src/http/sse.js +113 -74
- package/src/http/throttle.js +9 -4
- package/src/http/url.js +58 -35
- package/src/http/xhr.js +29 -24
- package/src/index.js +1 -0
- package/src/key.js +3 -5
- package/src/limiter.js +146 -65
- package/src/offload.js +116 -85
- package/src/offload_module.js +594 -0
- package/src/options.js +8 -4
- package/src/redial.js +129 -0
- package/src/signal.js +14 -4
- package/src/stream.js +353 -198
- package/types/attempt.d.ts.map +1 -1
- package/types/durable/index.d.ts.map +1 -1
- package/types/durable/state.d.ts.map +1 -1
- package/types/durable/store.d.ts.map +1 -1
- package/types/errors.d.ts.map +1 -1
- package/types/http/index.d.ts.map +1 -1
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
- package/types/limiter.d.ts +5 -1
- package/types/limiter.d.ts.map +1 -1
- package/types/offload.d.ts +2 -16
- package/types/offload.d.ts.map +1 -1
- package/types/redial.d.ts +28 -0
- package/types/redial.d.ts.map +1 -0
- package/types/stream.d.ts +2 -0
- package/types/stream.d.ts.map +1 -1
package/src/http/request.js
CHANGED
|
@@ -78,7 +78,9 @@ const unshared_option_keys = [
|
|
|
78
78
|
* @returns {T}
|
|
79
79
|
*/
|
|
80
80
|
function clone_data(data) {
|
|
81
|
-
if (data == null || typeof data != "object" || data instanceof Blob)
|
|
81
|
+
if (data == null || typeof data != "object" || data instanceof Blob) {
|
|
82
|
+
return data
|
|
83
|
+
}
|
|
82
84
|
try {
|
|
83
85
|
return structuredClone(data)
|
|
84
86
|
} catch {
|
|
@@ -107,11 +109,14 @@ async function execute(c, spec, signal) {
|
|
|
107
109
|
try {
|
|
108
110
|
return await send(c, spec, attempt, signal)
|
|
109
111
|
} catch (error) {
|
|
110
|
-
if (signal.aborted)
|
|
112
|
+
if (signal.aborted) {
|
|
113
|
+
throw to_cancel_error(signal.reason, summary)
|
|
114
|
+
}
|
|
111
115
|
const is_unauthorized = error instanceof HttpError && error.status == 401
|
|
112
116
|
if (is_unauthorized && refresh_state) {
|
|
113
|
-
if (refreshed)
|
|
114
|
-
|
|
117
|
+
if (refreshed) {
|
|
118
|
+
reject_refreshed(c, refresh_state, generation, error)
|
|
119
|
+
} else {
|
|
115
120
|
refreshed = true
|
|
116
121
|
await wait_refresh(
|
|
117
122
|
c,
|
|
@@ -121,13 +126,19 @@ async function execute(c, spec, signal) {
|
|
|
121
126
|
error,
|
|
122
127
|
signal
|
|
123
128
|
)
|
|
124
|
-
if (is_stream_body)
|
|
129
|
+
if (is_stream_body) {
|
|
130
|
+
throw error
|
|
131
|
+
}
|
|
125
132
|
attempt--
|
|
126
133
|
continue
|
|
127
134
|
}
|
|
128
135
|
}
|
|
129
|
-
if (attempt > retry_count)
|
|
130
|
-
|
|
136
|
+
if (attempt > retry_count) {
|
|
137
|
+
throw error
|
|
138
|
+
}
|
|
139
|
+
if (!spec.idempotency_key && options.retry == null && !is_idempotent(method)) {
|
|
140
|
+
throw error
|
|
141
|
+
}
|
|
131
142
|
const retryable = retry.when
|
|
132
143
|
? await race_signal(
|
|
133
144
|
Promise.resolve(retry.when(error, attempt)),
|
|
@@ -139,9 +150,13 @@ async function execute(c, spec, signal) {
|
|
|
139
150
|
}
|
|
140
151
|
)
|
|
141
152
|
: is_retryable(error)
|
|
142
|
-
if (!retryable)
|
|
153
|
+
if (!retryable) {
|
|
154
|
+
throw error
|
|
155
|
+
}
|
|
143
156
|
const delay = get_retry_wait(retry, attempt, error)
|
|
144
|
-
if (delay == null)
|
|
157
|
+
if (delay == null) {
|
|
158
|
+
throw error
|
|
159
|
+
}
|
|
145
160
|
await sleep(delay, signal)
|
|
146
161
|
.catch(
|
|
147
162
|
(/** @type {unknown} */ reason) => {
|
|
@@ -151,7 +166,9 @@ async function execute(c, spec, signal) {
|
|
|
151
166
|
}
|
|
152
167
|
}
|
|
153
168
|
} catch (error) {
|
|
154
|
-
if (!spec.silent)
|
|
169
|
+
if (!spec.silent) {
|
|
170
|
+
report(c, error)
|
|
171
|
+
}
|
|
155
172
|
throw error
|
|
156
173
|
}
|
|
157
174
|
}
|
|
@@ -161,12 +178,12 @@ async function execute(c, spec, signal) {
|
|
|
161
178
|
*/
|
|
162
179
|
function is_raw_body(body) {
|
|
163
180
|
return typeof body == "string"
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
181
|
+
|| body instanceof ArrayBuffer
|
|
182
|
+
|| ArrayBuffer.isView(body)
|
|
183
|
+
|| body instanceof Blob
|
|
184
|
+
|| body instanceof FormData
|
|
185
|
+
|| body instanceof ReadableStream
|
|
186
|
+
|| body instanceof URLSearchParams
|
|
170
187
|
}
|
|
171
188
|
/**
|
|
172
189
|
* @param {AsyncIterable<unknown>} lines
|
|
@@ -174,7 +191,9 @@ function is_raw_body(body) {
|
|
|
174
191
|
* @returns {AsyncGenerator<unknown, void, undefined>}
|
|
175
192
|
*/
|
|
176
193
|
async function* map_lines(lines, parse) {
|
|
177
|
-
for await (const line of lines)
|
|
194
|
+
for await (const line of lines) {
|
|
195
|
+
yield parse(line)
|
|
196
|
+
}
|
|
178
197
|
}
|
|
179
198
|
/**
|
|
180
199
|
* @param {ClientContext} c
|
|
@@ -182,7 +201,9 @@ async function* map_lines(lines, parse) {
|
|
|
182
201
|
* @returns {void}
|
|
183
202
|
*/
|
|
184
203
|
export function report(c, error) {
|
|
185
|
-
if (!(error instanceof CancelError))
|
|
204
|
+
if (!(error instanceof CancelError)) {
|
|
205
|
+
call_hook(c.config.on?.error, error)
|
|
206
|
+
}
|
|
186
207
|
}
|
|
187
208
|
/**
|
|
188
209
|
* @param {ClientContext} c
|
|
@@ -207,17 +228,6 @@ export function request(
|
|
|
207
228
|
/** @type {import("../../public.js").RequestSummary} */
|
|
208
229
|
const summary = { method, url: path }
|
|
209
230
|
const controller = new AbortController()
|
|
210
|
-
/**
|
|
211
|
-
* @param {unknown} reason
|
|
212
|
-
* @returns {void}
|
|
213
|
-
*/
|
|
214
|
-
function cancel(reason) {
|
|
215
|
-
return controller.abort(
|
|
216
|
-
reason instanceof CancelError
|
|
217
|
-
? reason
|
|
218
|
-
: new CancelError(reason ?? "Cancelled", summary)
|
|
219
|
-
)
|
|
220
|
-
}
|
|
221
231
|
const is_stream = options.as != null && stream_types.has(options.as)
|
|
222
232
|
const raw = (async () => {
|
|
223
233
|
const unlink = link_signal(
|
|
@@ -228,18 +238,19 @@ export function request(
|
|
|
228
238
|
let keeps_link = false
|
|
229
239
|
/** @type {string | undefined} */
|
|
230
240
|
let latest_key
|
|
231
|
-
function forget_latest() {
|
|
232
|
-
if (latest_key != null && c.shared.latest.get(latest_key) == cancel) c.shared.latest.delete(latest_key)
|
|
233
|
-
}
|
|
234
241
|
/** @type {Job | undefined} */
|
|
235
242
|
let job
|
|
236
243
|
try {
|
|
237
|
-
if ((method == "GET" || method == "HEAD") && body !== void 0)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
)
|
|
244
|
+
if ((method == "GET" || method == "HEAD") && body !== void 0) {
|
|
245
|
+
throw TypeError(
|
|
246
|
+
`A ${method} request cannot have a body: ${path}`
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
if (is_stream && options.lock != null) {
|
|
250
|
+
throw TypeError(
|
|
251
|
+
`A request read as "${options.as}" cannot be locked, because its body is read once: ${path}`
|
|
252
|
+
)
|
|
253
|
+
}
|
|
243
254
|
check_retry(
|
|
244
255
|
options.retry ?? c.config.retry,
|
|
245
256
|
"request"
|
|
@@ -251,44 +262,57 @@ export function request(
|
|
|
251
262
|
c.config.query
|
|
252
263
|
)
|
|
253
264
|
summary.url = url
|
|
254
|
-
if (!c.config.fetch && typeof location == "undefined" && !scheme_regex.test(url))
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
265
|
+
if (!c.config.fetch && typeof location == "undefined" && !scheme_regex.test(url)) {
|
|
266
|
+
throw TypeError(
|
|
267
|
+
`The URL "${url}" is relative: set base, or use an absolute URL`
|
|
268
|
+
)
|
|
269
|
+
}
|
|
270
|
+
if (controller.signal.aborted) {
|
|
271
|
+
throw controller.signal.reason
|
|
272
|
+
}
|
|
273
|
+
if (options.latest != null) {
|
|
274
|
+
latest_key = [
|
|
275
|
+
c.client_id,
|
|
276
|
+
"latest",
|
|
277
|
+
options.latest
|
|
278
|
+
].join(" ")
|
|
279
|
+
} else if (options.debounce) {
|
|
280
|
+
latest_key = [
|
|
281
|
+
c.client_id,
|
|
282
|
+
"debounce",
|
|
283
|
+
method,
|
|
284
|
+
url.replace(/[?#].*/s, "")
|
|
285
|
+
].join(" ")
|
|
286
|
+
}
|
|
269
287
|
if (latest_key != null) {
|
|
270
288
|
c.shared.latest.get(latest_key)?.(
|
|
271
289
|
"Superseded by a newer request"
|
|
272
290
|
)
|
|
273
291
|
c.shared.latest.set(latest_key, cancel)
|
|
274
292
|
}
|
|
275
|
-
if (options.debounce)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
293
|
+
if (options.debounce) {
|
|
294
|
+
await sleep(
|
|
295
|
+
options.debounce,
|
|
296
|
+
controller.signal
|
|
297
|
+
)
|
|
298
|
+
}
|
|
299
|
+
if (options.throttle) {
|
|
300
|
+
await throttle(
|
|
301
|
+
c,
|
|
302
|
+
[
|
|
303
|
+
c.client_id,
|
|
304
|
+
"throttle",
|
|
305
|
+
method,
|
|
306
|
+
url.replace(/[?#].*/s, "")
|
|
307
|
+
].join(" "),
|
|
308
|
+
options.throttle,
|
|
309
|
+
cancel,
|
|
310
|
+
controller.signal
|
|
311
|
+
)
|
|
312
|
+
}
|
|
313
|
+
if (controller.signal.aborted) {
|
|
314
|
+
throw controller.signal.reason
|
|
315
|
+
}
|
|
292
316
|
const is_shareable = options.dedupe !== false
|
|
293
317
|
&& (method == "GET" || method == "HEAD")
|
|
294
318
|
&& latest_key == null
|
|
@@ -322,7 +346,6 @@ export function request(
|
|
|
322
346
|
].join(" ")
|
|
323
347
|
: void 0
|
|
324
348
|
job = share_key == null ? void 0 : c.shared.shared.get(share_key)
|
|
325
|
-
if (job?.controller.signal.aborted) job = void 0
|
|
326
349
|
const joined = !!job
|
|
327
350
|
if (!job) {
|
|
328
351
|
job = start_job(
|
|
@@ -344,18 +367,25 @@ export function request(
|
|
|
344
367
|
)
|
|
345
368
|
}
|
|
346
369
|
job.refs++
|
|
347
|
-
if (is_stream)
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
370
|
+
if (is_stream) {
|
|
371
|
+
link_signal(
|
|
372
|
+
job.controller,
|
|
373
|
+
controller.signal
|
|
374
|
+
)
|
|
375
|
+
}
|
|
351
376
|
from_job = true
|
|
352
377
|
const result = await race_signal(job.promise, controller.signal)
|
|
353
|
-
if (joined)
|
|
354
|
-
|
|
355
|
-
|
|
378
|
+
if (joined) {
|
|
379
|
+
return {
|
|
380
|
+
...result,
|
|
381
|
+
data: clone_data(result.data)
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (!is_stream) {
|
|
385
|
+
return result
|
|
356
386
|
}
|
|
357
|
-
if (!is_stream) return result
|
|
358
387
|
keeps_link = true
|
|
388
|
+
/** @returns {void} */
|
|
359
389
|
function done() {
|
|
360
390
|
unlink()
|
|
361
391
|
forget_latest()
|
|
@@ -370,18 +400,25 @@ export function request(
|
|
|
370
400
|
: watch_records(
|
|
371
401
|
/** @type {AsyncIterable<unknown>} */(result.data)/**/,
|
|
372
402
|
(left, failure) => {
|
|
373
|
-
if (left)
|
|
374
|
-
|
|
403
|
+
if (left) {
|
|
404
|
+
cancel(void 0)
|
|
405
|
+
} else if (failure && !silent) {
|
|
406
|
+
report(c, failure.value)
|
|
407
|
+
}
|
|
375
408
|
done()
|
|
376
409
|
}
|
|
377
410
|
)
|
|
378
411
|
}
|
|
379
412
|
} catch (error) {
|
|
380
|
-
if (controller.signal.aborted)
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
413
|
+
if (controller.signal.aborted) {
|
|
414
|
+
throw to_cancel_error(
|
|
415
|
+
controller.signal.reason,
|
|
416
|
+
summary
|
|
417
|
+
)
|
|
418
|
+
}
|
|
419
|
+
if (!from_job && !silent) {
|
|
420
|
+
report(c, error)
|
|
421
|
+
}
|
|
385
422
|
throw error
|
|
386
423
|
} finally {
|
|
387
424
|
if (!keeps_link) {
|
|
@@ -389,7 +426,9 @@ export function request(
|
|
|
389
426
|
forget_latest()
|
|
390
427
|
}
|
|
391
428
|
if (job && !--job.refs && !job.settled) {
|
|
392
|
-
if (job.key != null && c.shared.shared.get(job.key) == job)
|
|
429
|
+
if (job.key != null && c.shared.shared.get(job.key) == job) {
|
|
430
|
+
c.shared.shared.delete(job.key)
|
|
431
|
+
}
|
|
393
432
|
job.controller.abort(
|
|
394
433
|
new CancelError(
|
|
395
434
|
"Cancelled by all callers",
|
|
@@ -398,6 +437,12 @@ export function request(
|
|
|
398
437
|
)
|
|
399
438
|
}
|
|
400
439
|
}
|
|
440
|
+
/** @returns {void} */
|
|
441
|
+
function forget_latest() {
|
|
442
|
+
if (latest_key != null && c.shared.latest.get(latest_key) == cancel) {
|
|
443
|
+
c.shared.latest.delete(latest_key)
|
|
444
|
+
}
|
|
445
|
+
}
|
|
401
446
|
})()
|
|
402
447
|
const data = raw.then(result => result.data)
|
|
403
448
|
return /** @type {import("../../public.js").LubeRequest<T>} */(Object.assign(
|
|
@@ -417,6 +462,17 @@ export function request(
|
|
|
417
462
|
}
|
|
418
463
|
}
|
|
419
464
|
))/**/
|
|
465
|
+
/**
|
|
466
|
+
* @param {unknown} reason
|
|
467
|
+
* @returns {void}
|
|
468
|
+
*/
|
|
469
|
+
function cancel(reason) {
|
|
470
|
+
return controller.abort(
|
|
471
|
+
reason instanceof CancelError
|
|
472
|
+
? reason
|
|
473
|
+
: new CancelError(reason ?? "Cancelled", summary)
|
|
474
|
+
)
|
|
475
|
+
}
|
|
420
476
|
}
|
|
421
477
|
/**
|
|
422
478
|
* @param {ClientContext} c
|
|
@@ -444,41 +500,6 @@ async function send(c, spec, attempt, job_signal) {
|
|
|
444
500
|
let keeps_link = false
|
|
445
501
|
/** @type {Response | undefined} */
|
|
446
502
|
let response
|
|
447
|
-
function abort_error() {
|
|
448
|
-
return timed_out
|
|
449
|
-
? new TimeoutError(
|
|
450
|
-
/** @type {number} */(timeout)/**/,
|
|
451
|
-
summary
|
|
452
|
-
)
|
|
453
|
-
: to_cancel_error(job_signal.reason, summary)
|
|
454
|
-
}
|
|
455
|
-
/**
|
|
456
|
-
* @param {AsyncIterable<unknown>} lines
|
|
457
|
-
* @returns {AsyncGenerator<unknown, void, undefined>}
|
|
458
|
-
*/
|
|
459
|
-
async function* guard_lines(lines) {
|
|
460
|
-
try {
|
|
461
|
-
yield* lines
|
|
462
|
-
} catch (error) {
|
|
463
|
-
if (controller.signal.aborted) throw abort_error()
|
|
464
|
-
if (error instanceof SyntaxError) throw invalid_json(error)
|
|
465
|
-
throw new NetworkError(error, summary)
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
/**
|
|
469
|
-
* @param {SyntaxError} error
|
|
470
|
-
* @returns {SyntaxError}
|
|
471
|
-
*/
|
|
472
|
-
function invalid_json(error) {
|
|
473
|
-
/** @type {SyntaxError & { request?: import("../../public.js").RequestSummary, response?: Response | undefined }} */
|
|
474
|
-
const invalid = new SyntaxError(
|
|
475
|
-
`Invalid JSON in the response of ${summary.method} ${summary.url}: ${error.message}`
|
|
476
|
-
)
|
|
477
|
-
invalid.cause = error
|
|
478
|
-
invalid.request = summary
|
|
479
|
-
invalid.response = response
|
|
480
|
-
return invalid
|
|
481
|
-
}
|
|
482
503
|
try {
|
|
483
504
|
const headers = new Headers()
|
|
484
505
|
await race_signal(
|
|
@@ -498,7 +519,9 @@ async function send(c, spec, attempt, job_signal) {
|
|
|
498
519
|
options: /** @type {import("../../public.js").RequestOptions} */(options)/**/,
|
|
499
520
|
url: spec.url
|
|
500
521
|
}
|
|
501
|
-
if (controller.signal.aborted)
|
|
522
|
+
if (controller.signal.aborted) {
|
|
523
|
+
throw abort_error()
|
|
524
|
+
}
|
|
502
525
|
await race_signal(
|
|
503
526
|
Promise.resolve(
|
|
504
527
|
c.config.on?.request?.(context)
|
|
@@ -508,18 +531,22 @@ async function send(c, spec, attempt, job_signal) {
|
|
|
508
531
|
summary.url = context.url
|
|
509
532
|
const raw_body = context.body === void 0 || is_raw_body(context.body)
|
|
510
533
|
const body = raw_body ? context.body : JSON.stringify(context.body)
|
|
511
|
-
if (!raw_body && !headers.has("Content-Type"))
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
534
|
+
if (!raw_body && !headers.has("Content-Type")) {
|
|
535
|
+
headers.set(
|
|
536
|
+
"Content-Type",
|
|
537
|
+
"application/json"
|
|
538
|
+
)
|
|
539
|
+
}
|
|
540
|
+
if (body instanceof FormData) {
|
|
541
|
+
headers.delete("Content-Type")
|
|
542
|
+
}
|
|
516
543
|
if (spec.idempotency_key && !headers.has("Idempotency-Key")) {
|
|
517
544
|
headers.set(
|
|
518
545
|
"Idempotency-Key",
|
|
519
546
|
spec.idempotency_key
|
|
520
547
|
)
|
|
521
548
|
}
|
|
522
|
-
/** @type {RequestInit & { duplex?: string, headers: Headers }} */
|
|
549
|
+
/** @type {RequestInit & { duplex?: string, headers: Headers, method: string }} */
|
|
523
550
|
const init = {
|
|
524
551
|
...c.base_init,
|
|
525
552
|
...omit_keys(options, option_keys),
|
|
@@ -528,11 +555,15 @@ async function send(c, spec, attempt, job_signal) {
|
|
|
528
555
|
method,
|
|
529
556
|
signal: controller.signal
|
|
530
557
|
}
|
|
531
|
-
if (body instanceof ReadableStream)
|
|
558
|
+
if (body instanceof ReadableStream) {
|
|
559
|
+
init.duplex = "half"
|
|
560
|
+
}
|
|
532
561
|
const uses_xhr = !!options.upload && !c.config.fetch && typeof XMLHttpRequest != "undefined"
|
|
533
|
-
if (uses_xhr && body instanceof ReadableStream)
|
|
534
|
-
|
|
535
|
-
|
|
562
|
+
if (uses_xhr && body instanceof ReadableStream) {
|
|
563
|
+
throw TypeError(
|
|
564
|
+
"Upload progress cannot be reported for a ReadableStream body"
|
|
565
|
+
)
|
|
566
|
+
}
|
|
536
567
|
const started = Date.now()
|
|
537
568
|
try {
|
|
538
569
|
response = await race_signal(
|
|
@@ -556,7 +587,9 @@ async function send(c, spec, attempt, job_signal) {
|
|
|
556
587
|
)
|
|
557
588
|
throw new HttpError(received, error_data, summary)
|
|
558
589
|
}
|
|
559
|
-
if (is_stream)
|
|
590
|
+
if (is_stream) {
|
|
591
|
+
clearTimeout(timer)
|
|
592
|
+
}
|
|
560
593
|
/** @type {unknown} */
|
|
561
594
|
let data
|
|
562
595
|
try {
|
|
@@ -570,13 +603,19 @@ async function send(c, spec, attempt, job_signal) {
|
|
|
570
603
|
controller.signal
|
|
571
604
|
)
|
|
572
605
|
} catch (error) {
|
|
573
|
-
if (controller.signal.aborted)
|
|
574
|
-
|
|
606
|
+
if (controller.signal.aborted) {
|
|
607
|
+
throw abort_error()
|
|
608
|
+
}
|
|
609
|
+
if (!(error instanceof SyntaxError)) {
|
|
610
|
+
throw new NetworkError(error, summary)
|
|
611
|
+
}
|
|
575
612
|
throw invalid_json(error)
|
|
576
613
|
}
|
|
577
|
-
if (options.as == "ndjson" || options.as == "events")
|
|
578
|
-
|
|
579
|
-
|
|
614
|
+
if (options.as == "ndjson" || options.as == "events") {
|
|
615
|
+
data = guard_lines(
|
|
616
|
+
/** @type {AsyncIterable<unknown>} */(data)/**/
|
|
617
|
+
)
|
|
618
|
+
}
|
|
580
619
|
if (options.parse) {
|
|
581
620
|
const parse = options.parse
|
|
582
621
|
data = options.as == "ndjson"
|
|
@@ -611,16 +650,62 @@ async function send(c, spec, attempt, job_signal) {
|
|
|
611
650
|
keeps_link = is_stream
|
|
612
651
|
return result
|
|
613
652
|
} catch (error) {
|
|
614
|
-
if (response && !response.bodyUsed && !response.body?.locked)
|
|
653
|
+
if (response && !response.bodyUsed && !response.body?.locked) {
|
|
654
|
+
response.body?.cancel().catch(noop)
|
|
655
|
+
}
|
|
615
656
|
const is_known = error instanceof CancelError
|
|
616
657
|
|| error instanceof HttpError
|
|
617
658
|
|| error instanceof NetworkError
|
|
618
659
|
|| error instanceof TimeoutError
|
|
619
|
-
if (controller.signal.aborted && !is_known)
|
|
660
|
+
if (controller.signal.aborted && !is_known) {
|
|
661
|
+
throw abort_error()
|
|
662
|
+
}
|
|
620
663
|
throw error
|
|
621
664
|
} finally {
|
|
622
665
|
clearTimeout(timer)
|
|
623
|
-
if (!keeps_link)
|
|
666
|
+
if (!keeps_link) {
|
|
667
|
+
unlink()
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
/** @returns {TimeoutError | CancelError} */
|
|
671
|
+
function abort_error() {
|
|
672
|
+
return timed_out
|
|
673
|
+
? new TimeoutError(
|
|
674
|
+
/** @type {number} */(timeout)/**/,
|
|
675
|
+
summary
|
|
676
|
+
)
|
|
677
|
+
: to_cancel_error(job_signal.reason, summary)
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* @param {AsyncIterable<unknown>} lines
|
|
681
|
+
* @returns {AsyncGenerator<unknown, void, undefined>}
|
|
682
|
+
*/
|
|
683
|
+
async function* guard_lines(lines) {
|
|
684
|
+
try {
|
|
685
|
+
yield* lines
|
|
686
|
+
} catch (error) {
|
|
687
|
+
if (controller.signal.aborted) {
|
|
688
|
+
throw abort_error()
|
|
689
|
+
}
|
|
690
|
+
if (error instanceof SyntaxError) {
|
|
691
|
+
throw invalid_json(error)
|
|
692
|
+
}
|
|
693
|
+
throw new NetworkError(error, summary)
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* @param {SyntaxError} error
|
|
698
|
+
* @returns {SyntaxError}
|
|
699
|
+
*/
|
|
700
|
+
function invalid_json(error) {
|
|
701
|
+
/** @type {SyntaxError & { request?: import("../../public.js").RequestSummary, response?: Response | undefined }} */
|
|
702
|
+
const invalid = new SyntaxError(
|
|
703
|
+
`Invalid JSON in the response of ${summary.method} ${summary.url}: ${error.message}`
|
|
704
|
+
)
|
|
705
|
+
invalid.cause = error
|
|
706
|
+
invalid.request = summary
|
|
707
|
+
invalid.response = response
|
|
708
|
+
return invalid
|
|
624
709
|
}
|
|
625
710
|
}
|
|
626
711
|
/**
|
|
@@ -641,12 +726,16 @@ function start_job(c, spec, key) {
|
|
|
641
726
|
settled: false
|
|
642
727
|
}
|
|
643
728
|
job.promise = execute(c, spec, job.controller.signal)
|
|
644
|
-
if (key != null)
|
|
729
|
+
if (key != null) {
|
|
730
|
+
c.shared.shared.set(key, job)
|
|
731
|
+
}
|
|
645
732
|
job.promise.then(noop, noop)
|
|
646
733
|
.finally(
|
|
647
734
|
() => {
|
|
648
735
|
job.settled = true
|
|
649
|
-
if (key != null && c.shared.shared.get(key) == job)
|
|
736
|
+
if (key != null && c.shared.shared.get(key) == job) {
|
|
737
|
+
c.shared.shared.delete(key)
|
|
738
|
+
}
|
|
650
739
|
}
|
|
651
740
|
)
|
|
652
741
|
return job
|
|
@@ -682,17 +771,23 @@ export function watch_records(records, finish) {
|
|
|
682
771
|
return this
|
|
683
772
|
},
|
|
684
773
|
async next() {
|
|
685
|
-
if (finished)
|
|
774
|
+
if (finished) {
|
|
775
|
+
return { done: true, value: void 0 }
|
|
776
|
+
}
|
|
686
777
|
try {
|
|
687
778
|
const step = await reader.next()
|
|
688
|
-
if (finished)
|
|
779
|
+
if (finished) {
|
|
780
|
+
return { done: true, value: void 0 }
|
|
781
|
+
}
|
|
689
782
|
if (step.done) {
|
|
690
783
|
finished = true
|
|
691
784
|
finish(false, void 0)
|
|
692
785
|
}
|
|
693
786
|
return step
|
|
694
787
|
} catch (error) {
|
|
695
|
-
if (finished)
|
|
788
|
+
if (finished) {
|
|
789
|
+
return { done: true, value: void 0 }
|
|
790
|
+
}
|
|
696
791
|
finished = true
|
|
697
792
|
finish(false, { value: error })
|
|
698
793
|
throw error
|
|
@@ -724,9 +819,7 @@ function watch_stream(stream, done) {
|
|
|
724
819
|
const reader = stream.getReader()
|
|
725
820
|
return new ReadableStream(
|
|
726
821
|
{
|
|
727
|
-
/**
|
|
728
|
-
* @param {unknown} reason
|
|
729
|
-
*/
|
|
822
|
+
/** @param {unknown} reason */
|
|
730
823
|
cancel(reason) {
|
|
731
824
|
done()
|
|
732
825
|
return reader.cancel(reason)
|
|
@@ -737,7 +830,9 @@ function watch_stream(stream, done) {
|
|
|
737
830
|
if (finished) {
|
|
738
831
|
done()
|
|
739
832
|
controller.close()
|
|
740
|
-
} else
|
|
833
|
+
} else {
|
|
834
|
+
controller.enqueue(value)
|
|
835
|
+
}
|
|
741
836
|
} catch (error) {
|
|
742
837
|
done()
|
|
743
838
|
controller.error(error)
|