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.
Files changed (70) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +1 -1
  3. package/docs/http.md +1 -1
  4. package/docs/reference.md +4 -2
  5. package/docs/tasks.md +19 -2
  6. package/package.json +4 -5
  7. package/public.d.ts +112 -0
  8. package/src/attempt.js +17 -7
  9. package/src/durable/context.js +107 -64
  10. package/src/durable/error.js +39 -15
  11. package/src/durable/index.js +18 -10
  12. package/src/durable/journal.js +29 -14
  13. package/src/durable/json.js +26 -9
  14. package/src/durable/lease.js +15 -5
  15. package/src/durable/persist.js +60 -22
  16. package/src/durable/run.js +103 -41
  17. package/src/durable/send.js +30 -13
  18. package/src/durable/serve.js +30 -24
  19. package/src/durable/state.js +28 -13
  20. package/src/durable/store.js +19 -10
  21. package/src/durable/watch.js +31 -11
  22. package/src/errors.js +9 -3
  23. package/src/flow/attempts.js +49 -29
  24. package/src/flow/builder.js +260 -161
  25. package/src/flow/compile.js +86 -66
  26. package/src/flow/graph.js +108 -42
  27. package/src/flow/index.js +13 -7
  28. package/src/flow/items.js +79 -46
  29. package/src/flow/nodes.js +267 -117
  30. package/src/flow/refs.js +27 -13
  31. package/src/flow/resources.js +9 -3
  32. package/src/flow/run.js +287 -114
  33. package/src/flow/streams.js +44 -19
  34. package/src/flow/subflows.js +93 -53
  35. package/src/http/config.js +11 -5
  36. package/src/http/index.js +25 -14
  37. package/src/http/refresh.js +29 -12
  38. package/src/http/request.js +246 -151
  39. package/src/http/response.js +105 -50
  40. package/src/http/retry.js +52 -27
  41. package/src/http/socket.js +220 -172
  42. package/src/http/sse.js +113 -74
  43. package/src/http/throttle.js +9 -4
  44. package/src/http/url.js +58 -35
  45. package/src/http/xhr.js +29 -24
  46. package/src/index.js +1 -0
  47. package/src/key.js +3 -5
  48. package/src/limiter.js +146 -65
  49. package/src/offload.js +116 -85
  50. package/src/offload_module.js +594 -0
  51. package/src/options.js +8 -4
  52. package/src/redial.js +129 -0
  53. package/src/signal.js +14 -4
  54. package/src/stream.js +353 -198
  55. package/types/attempt.d.ts.map +1 -1
  56. package/types/durable/index.d.ts.map +1 -1
  57. package/types/durable/state.d.ts.map +1 -1
  58. package/types/durable/store.d.ts.map +1 -1
  59. package/types/errors.d.ts.map +1 -1
  60. package/types/http/index.d.ts.map +1 -1
  61. package/types/index.d.ts +1 -0
  62. package/types/index.d.ts.map +1 -1
  63. package/types/limiter.d.ts +5 -1
  64. package/types/limiter.d.ts.map +1 -1
  65. package/types/offload.d.ts +2 -16
  66. package/types/offload.d.ts.map +1 -1
  67. package/types/redial.d.ts +28 -0
  68. package/types/redial.d.ts.map +1 -0
  69. package/types/stream.d.ts +2 -0
  70. package/types/stream.d.ts.map +1 -1
@@ -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) return data
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) throw to_cancel_error(signal.reason, summary)
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) reject_refreshed(c, refresh_state, generation, error)
114
- else {
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) throw error
129
+ if (is_stream_body) {
130
+ throw error
131
+ }
125
132
  attempt--
126
133
  continue
127
134
  }
128
135
  }
129
- if (attempt > retry_count) throw error
130
- if (!spec.idempotency_key && options.retry == null && !is_idempotent(method)) throw error
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) throw error
153
+ if (!retryable) {
154
+ throw error
155
+ }
143
156
  const delay = get_retry_wait(retry, attempt, error)
144
- if (delay == null) throw error
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) report(c, error)
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
- || body instanceof ArrayBuffer
165
- || ArrayBuffer.isView(body)
166
- || body instanceof Blob
167
- || body instanceof FormData
168
- || body instanceof ReadableStream
169
- || body instanceof URLSearchParams
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) yield parse(line)
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)) call_hook(c.config.on?.error, error)
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) throw TypeError(
238
- `A ${method} request cannot have a body: ${path}`
239
- )
240
- if (is_stream && options.lock != null) throw TypeError(
241
- `A request read as "${options.as}" cannot be locked, because its body is read once: ${path}`
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)) throw TypeError(
255
- `The URL "${url}" is relative: set base, or use an absolute URL`
256
- )
257
- if (controller.signal.aborted) throw controller.signal.reason
258
- if (options.latest != null) latest_key = [
259
- c.client_id,
260
- "latest",
261
- options.latest
262
- ].join(" ")
263
- else if (options.debounce) latest_key = [
264
- c.client_id,
265
- "debounce",
266
- method,
267
- url.replace(/[?#].*/s, "")
268
- ].join(" ")
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) await sleep(
276
- options.debounce,
277
- controller.signal
278
- )
279
- if (options.throttle) await throttle(
280
- c,
281
- [
282
- c.client_id,
283
- "throttle",
284
- method,
285
- url.replace(/[?#].*/s, "")
286
- ].join(" "),
287
- options.throttle,
288
- cancel,
289
- controller.signal
290
- )
291
- if (controller.signal.aborted) throw controller.signal.reason
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) link_signal(
348
- job.controller,
349
- controller.signal
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) return {
354
- ...result,
355
- data: clone_data(result.data)
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) cancel(void 0)
374
- else if (failure && !silent) report(c, failure.value)
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) throw to_cancel_error(
381
- controller.signal.reason,
382
- summary
383
- )
384
- if (!from_job && !silent) report(c, error)
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) c.shared.shared.delete(job.key)
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) throw abort_error()
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")) headers.set(
512
- "Content-Type",
513
- "application/json"
514
- )
515
- if (body instanceof FormData) headers.delete("Content-Type")
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) init.duplex = "half"
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) throw TypeError(
534
- "Upload progress cannot be reported for a ReadableStream body"
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) clearTimeout(timer)
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) throw abort_error()
574
- if (!(error instanceof SyntaxError)) throw new NetworkError(error, summary)
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") data = guard_lines(
578
- /** @type {AsyncIterable<unknown>} */(data)/**/
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) response.body?.cancel().catch(noop)
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) throw abort_error()
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) unlink()
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) c.shared.shared.set(key, job)
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) c.shared.shared.delete(key)
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) return { done: true, value: void 0 }
774
+ if (finished) {
775
+ return { done: true, value: void 0 }
776
+ }
686
777
  try {
687
778
  const step = await reader.next()
688
- if (finished) return { done: true, value: void 0 }
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) return { done: true, value: void 0 }
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 controller.enqueue(value)
833
+ } else {
834
+ controller.enqueue(value)
835
+ }
741
836
  } catch (error) {
742
837
  done()
743
838
  controller.error(error)