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
@@ -44,15 +44,21 @@ function check_socket_options(options) {
44
44
  "ws"
45
45
  )
46
46
  const { as, heartbeat, limit, outbox } = options
47
- if (as != null && as != "json" && as != "text") throw TypeError(
48
- "The as of ws() must be \"json\" or \"text\""
49
- )
50
- if (limit != null && !(limit === Infinity || Number.isInteger(limit) && limit >= 1)) throw TypeError(
51
- "The limit of ws() must be a positive integer or Infinity"
52
- )
53
- if (outbox != null && !(outbox === Infinity || Number.isInteger(outbox) && outbox >= 0)) throw TypeError(
54
- "The outbox of ws() must be a non-negative integer or Infinity"
55
- )
47
+ if (as != null && as != "json" && as != "text") {
48
+ throw TypeError(
49
+ "The as of ws() must be \"json\" or \"text\""
50
+ )
51
+ }
52
+ if (limit != null && !(limit === Infinity || Number.isInteger(limit) && limit >= 1)) {
53
+ throw TypeError(
54
+ "The limit of ws() must be a positive integer or Infinity"
55
+ )
56
+ }
57
+ if (outbox != null && !(outbox === Infinity || Number.isInteger(outbox) && outbox >= 0)) {
58
+ throw TypeError(
59
+ "The outbox of ws() must be a non-negative integer or Infinity"
60
+ )
61
+ }
56
62
  if (heartbeat != null) {
57
63
  check_options(
58
64
  heartbeat,
@@ -60,9 +66,11 @@ function check_socket_options(options) {
60
66
  "ws",
61
67
  "heartbeat."
62
68
  )
63
- if (!(typeof heartbeat.interval == "number" && heartbeat.interval > 0)) throw TypeError(
64
- "The heartbeat interval of ws() must be positive milliseconds"
65
- )
69
+ if (!(typeof heartbeat.interval == "number" && heartbeat.interval > 0)) {
70
+ throw TypeError(
71
+ "The heartbeat interval of ws() must be positive milliseconds"
72
+ )
73
+ }
66
74
  }
67
75
  check_reconnect(options.reconnect, "ws")
68
76
  }
@@ -72,9 +80,9 @@ function check_socket_options(options) {
72
80
  */
73
81
  function is_raw_message(data) {
74
82
  return typeof data == "string"
75
- || data instanceof ArrayBuffer
76
- || ArrayBuffer.isView(data)
77
- || typeof Blob != "undefined" && data instanceof Blob
83
+ || data instanceof ArrayBuffer
84
+ || ArrayBuffer.isView(data)
85
+ || typeof Blob != "undefined" && data instanceof Blob
78
86
  }
79
87
  /**
80
88
  * @param {unknown} error
@@ -82,8 +90,8 @@ function is_raw_message(data) {
82
90
  */
83
91
  function is_reconnectable(error) {
84
92
  return error instanceof NetworkError
85
- || error instanceof TimeoutError
86
- || error instanceof SocketError && reconnect_codes.has(error.code)
93
+ || error instanceof TimeoutError
94
+ || error instanceof SocketError && reconnect_codes.has(error.code)
87
95
  }
88
96
  /**
89
97
  * @param {SocketSetup} setup
@@ -119,6 +127,140 @@ export function open_socket(setup) {
119
127
  /** @type {ReturnType<typeof setTimeout> | undefined} */
120
128
  let timer
121
129
  const unlink = link_signal(closed, options.signal ?? void 0)
130
+ if (closed.signal.aborted) {
131
+ status = "closed"
132
+ } else {
133
+ on_abort(
134
+ closed.signal,
135
+ () => {
136
+ unlink()
137
+ finish(void 0)
138
+ }
139
+ )
140
+ }
141
+ return {
142
+ /** @returns {AsyncIterableIterator<unknown>} */
143
+ [Symbol.asyncIterator]() {
144
+ /** @type {SocketLoop} */
145
+ const loop = {
146
+ buffer: [],
147
+ ended: closed.signal.aborted,
148
+ error: void 0,
149
+ head: 0,
150
+ wake: void 0
151
+ }
152
+ let finished = false
153
+ let started = false
154
+ return {
155
+ [Symbol.asyncIterator]() {
156
+ return this
157
+ },
158
+ async next() {
159
+ if (!started) {
160
+ started = true
161
+ if (!loop.ended) {
162
+ loops.add(loop)
163
+ start()
164
+ }
165
+ }
166
+ for (;;) {
167
+ if (finished) {
168
+ return { done: true, value: void 0 }
169
+ }
170
+ if (loop.head < loop.buffer.length) {
171
+ const message = loop.buffer[loop.head]
172
+ loop.buffer[loop.head++] = void 0
173
+ if (loop.head == loop.buffer.length) {
174
+ loop.buffer = []
175
+ loop.head = 0
176
+ } else if (loop.head > 1024 && loop.head * 2 > loop.buffer.length) {
177
+ loop.buffer = loop.buffer.slice(loop.head)
178
+ loop.head = 0
179
+ }
180
+ if (!(message instanceof Promise)) {
181
+ return { done: false, value: message }
182
+ }
183
+ /** @type {unknown} */
184
+ let value
185
+ try {
186
+ value = await message
187
+ } catch (error) {
188
+ if (finished) {
189
+ return { done: true, value: void 0 }
190
+ }
191
+ stop()
192
+ throw error
193
+ }
194
+ if (finished) {
195
+ return { done: true, value: void 0 }
196
+ }
197
+ return { done: false, value }
198
+ }
199
+ if (loop.error) {
200
+ const { value } = loop.error
201
+ stop()
202
+ throw value
203
+ }
204
+ if (loop.ended) {
205
+ return stop()
206
+ }
207
+ await new Promise(
208
+ resolve => {
209
+ loop.wake = () => resolve(void 0)
210
+ }
211
+ )
212
+ loop.wake = void 0
213
+ }
214
+ },
215
+ async return() {
216
+ return stop()
217
+ }
218
+ }
219
+ /** @returns {IteratorResult<unknown>} */
220
+ function stop() {
221
+ if (!finished) {
222
+ finished = true
223
+ loops.delete(loop)
224
+ loop.wake?.()
225
+ }
226
+ return { done: true, value: void 0 }
227
+ }
228
+ },
229
+ /**
230
+ * @param {unknown} reason
231
+ * @returns {void}
232
+ */
233
+ cancel: reason => closed.abort(
234
+ new CancelError(reason ?? "Cancelled", summary)
235
+ ),
236
+ /**
237
+ * @param {unknown} data
238
+ * @returns {boolean}
239
+ */
240
+ send: data => {
241
+ if (closed.signal.aborted) {
242
+ throw closed.signal.reason instanceof CancelError
243
+ ? closed.signal.reason
244
+ : new CancelError(closed.signal.reason, summary)
245
+ }
246
+ const payload = serialize(data)
247
+ if (status == "open" && current?.readyState == 1) {
248
+ write(payload)
249
+ return true
250
+ }
251
+ if (outbox_limit > 0) {
252
+ if (outbox.length >= outbox_limit) {
253
+ outbox.shift()
254
+ }
255
+ outbox.push(payload)
256
+ }
257
+ start()
258
+ return false
259
+ },
260
+ get status() {
261
+ return status
262
+ }
263
+ }
122
264
  /**
123
265
  * @param {number} id
124
266
  * @returns {Promise<void>}
@@ -136,43 +278,52 @@ export function open_socket(setup) {
136
278
  }
137
279
  try {
138
280
  const { redacted, url } = await setup.url()
139
- if (id != generation) return
281
+ if (id != generation) {
282
+ return
283
+ }
140
284
  summary.url = redacted
141
285
  const socket = setup.create(url, options.protocols)
142
286
  current = socket
143
287
  socket.binaryType = "arraybuffer"
144
288
  socket.onerror = noop
145
289
  socket.onopen = () => {
146
- if (id == generation) open(id)
290
+ if (id == generation) {
291
+ open(id)
292
+ }
147
293
  }
148
294
  socket.onmessage = (
149
295
  /** @type {MessageEvent} */ event
150
296
  ) => {
151
- if (id == generation) receive(event.data)
297
+ if (id == generation) {
298
+ receive(event.data)
299
+ }
152
300
  }
153
301
  socket.onclose = (
154
302
  /** @type {CloseEvent} */ event
155
303
  ) => {
156
- if (id != generation) return
304
+ if (id != generation) {
305
+ return
306
+ }
157
307
  detach()
158
- if (event.code == 1000) finish(void 0)
159
- else lose(
160
- generation,
161
- event.code == 1005 || event.code == 1006
162
- ? new NetworkError(
163
- new SocketError(event.code, event.reason, summary),
164
- summary
165
- )
166
- : new SocketError(event.code, event.reason, summary)
167
- )
308
+ if (event.code == 1000) {
309
+ finish(void 0)
310
+ } else {
311
+ lose(
312
+ generation,
313
+ event.code == 1005 || event.code == 1006
314
+ ? new NetworkError(
315
+ new SocketError(event.code, event.reason, summary),
316
+ summary
317
+ )
318
+ : new SocketError(event.code, event.reason, summary)
319
+ )
320
+ }
168
321
  }
169
322
  } catch (error) {
170
323
  lose(id, error)
171
324
  }
172
325
  }
173
- /**
174
- * @returns {void}
175
- */
326
+ /** @returns {void} */
176
327
  function detach() {
177
328
  generation++
178
329
  stop_waiting?.()
@@ -183,7 +334,9 @@ export function open_socket(setup) {
183
334
  silence_timer = void 0
184
335
  const socket = current
185
336
  current = void 0
186
- if (!socket) return
337
+ if (!socket) {
338
+ return
339
+ }
187
340
  try {
188
341
  socket.close(1000)
189
342
  } catch {}
@@ -206,7 +359,9 @@ export function open_socket(setup) {
206
359
  loop.wake?.()
207
360
  }
208
361
  loops.clear()
209
- if (failed) setup.report(error)
362
+ if (failed) {
363
+ setup.report(error)
364
+ }
210
365
  }
211
366
  /**
212
367
  * @param {number} id
@@ -214,7 +369,9 @@ export function open_socket(setup) {
214
369
  * @returns {void}
215
370
  */
216
371
  function lose(id, error) {
217
- if (id != generation) return
372
+ if (id != generation) {
373
+ return
374
+ }
218
375
  detach()
219
376
  if (!reconnect || !is_reconnectable(error) || ++failures > (reconnect_count ?? (ever_opened ? Infinity : first_failures))) {
220
377
  finish(error, true)
@@ -228,10 +385,6 @@ export function open_socket(setup) {
228
385
  setup.report(error)
229
386
  set_status("connecting")
230
387
  const next = generation
231
- function retry() {
232
- stop_waiting?.()
233
- if (next == generation) connect(next)
234
- }
235
388
  timer = setTimeout(retry, clamp_delay(delay))
236
389
  if (typeof globalThis.addEventListener == "function") {
237
390
  globalThis.addEventListener("online", retry)
@@ -241,6 +394,11 @@ export function open_socket(setup) {
241
394
  globalThis.removeEventListener("online", retry)
242
395
  }
243
396
  }
397
+ /** @returns {void} */
398
+ function retry() {
399
+ stop_waiting?.()
400
+ connect(next)
401
+ }
244
402
  }
245
403
  /**
246
404
  * @param {number} id
@@ -258,9 +416,13 @@ export function open_socket(setup) {
258
416
  set_status("open")
259
417
  const pending = outbox
260
418
  outbox = []
261
- for (const data of pending) write(data)
419
+ for (const data of pending) {
420
+ write(data)
421
+ }
262
422
  const heartbeat = options.heartbeat
263
- if (!heartbeat) return
423
+ if (!heartbeat) {
424
+ return
425
+ }
264
426
  const ping = serialize(heartbeat.message)
265
427
  heartbeat_timer = setInterval(
266
428
  () => {
@@ -294,7 +456,9 @@ export function open_socket(setup) {
294
456
  message = JSON.parse(data)
295
457
  } catch {}
296
458
  }
297
- if (options.parse) message = options.parse(message)
459
+ if (options.parse) {
460
+ message = options.parse(message)
461
+ }
298
462
  } catch (error) {
299
463
  finish(error, true)
300
464
  return
@@ -304,8 +468,11 @@ export function open_socket(setup) {
304
468
  message.then(
305
469
  noop,
306
470
  (/** @type {unknown} */ error) => {
307
- if (id == generation) finish(error, true)
308
- else setup.report(error)
471
+ if (id == generation) {
472
+ finish(error, true)
473
+ } else {
474
+ setup.report(error)
475
+ }
309
476
  }
310
477
  )
311
478
  }
@@ -326,17 +493,19 @@ export function open_socket(setup) {
326
493
  * @returns {void}
327
494
  */
328
495
  function set_status(next) {
329
- if (status == next) return
496
+ if (status == next) {
497
+ return
498
+ }
330
499
  status = next
331
500
  try {
332
501
  options.onStatus?.(next)
333
502
  } catch {}
334
503
  }
335
- /**
336
- * @returns {void}
337
- */
504
+ /** @returns {void} */
338
505
  function start() {
339
- if (status != "idle") return
506
+ if (status != "idle") {
507
+ return
508
+ }
340
509
  set_status("connecting")
341
510
  connect(++generation)
342
511
  }
@@ -349,127 +518,6 @@ export function open_socket(setup) {
349
518
  /** @type {never} */(payload)/**/
350
519
  )
351
520
  }
352
- if (closed.signal.aborted) status = "closed"
353
- else {
354
- on_abort(
355
- closed.signal,
356
- () => {
357
- unlink()
358
- finish(void 0)
359
- }
360
- )
361
- }
362
- return {
363
- /**
364
- * @returns {AsyncIterableIterator<unknown>}
365
- */
366
- [Symbol.asyncIterator]() {
367
- /** @type {SocketLoop} */
368
- const loop = {
369
- buffer: [],
370
- ended: closed.signal.aborted,
371
- error: void 0,
372
- head: 0,
373
- wake: void 0
374
- }
375
- let finished = false
376
- let started = false
377
- /** @returns {IteratorResult<unknown>} */
378
- function stop() {
379
- if (!finished) {
380
- finished = true
381
- loops.delete(loop)
382
- loop.wake?.()
383
- }
384
- return { done: true, value: void 0 }
385
- }
386
- return {
387
- [Symbol.asyncIterator]() {
388
- return this
389
- },
390
- async next() {
391
- if (!started) {
392
- started = true
393
- if (!loop.ended) {
394
- loops.add(loop)
395
- start()
396
- }
397
- }
398
- for (;;) {
399
- if (finished) return { done: true, value: void 0 }
400
- if (loop.head < loop.buffer.length) {
401
- const message = loop.buffer[loop.head]
402
- loop.buffer[loop.head++] = void 0
403
- if (loop.head == loop.buffer.length) {
404
- loop.buffer = []
405
- loop.head = 0
406
- } else if (loop.head > 1024 && loop.head * 2 > loop.buffer.length) {
407
- loop.buffer = loop.buffer.slice(loop.head)
408
- loop.head = 0
409
- }
410
- if (!(message instanceof Promise)) return { done: false, value: message }
411
- /** @type {unknown} */
412
- let value
413
- try {
414
- value = await message
415
- } catch (error) {
416
- if (finished) return { done: true, value: void 0 }
417
- stop()
418
- throw error
419
- }
420
- if (finished) return { done: true, value: void 0 }
421
- return { done: false, value }
422
- }
423
- if (loop.error) {
424
- const { value } = loop.error
425
- stop()
426
- throw value
427
- }
428
- if (loop.ended) return stop()
429
- await new Promise(
430
- resolve => {
431
- loop.wake = () => resolve(void 0)
432
- }
433
- )
434
- loop.wake = void 0
435
- }
436
- },
437
- async return() {
438
- return stop()
439
- }
440
- }
441
- },
442
- /**
443
- * @param {unknown} reason
444
- * @returns {void}
445
- */
446
- cancel: reason => closed.abort(
447
- new CancelError(reason ?? "Cancelled", summary)
448
- ),
449
- /**
450
- * @param {unknown} data
451
- * @returns {boolean}
452
- */
453
- send: data => {
454
- if (closed.signal.aborted) throw closed.signal.reason instanceof CancelError
455
- ? closed.signal.reason
456
- : new CancelError(closed.signal.reason, summary)
457
- const payload = serialize(data)
458
- if (status == "open" && current?.readyState == 1) {
459
- write(payload)
460
- return true
461
- }
462
- if (outbox_limit > 0) {
463
- if (outbox.length >= outbox_limit) outbox.shift()
464
- outbox.push(payload)
465
- }
466
- start()
467
- return false
468
- },
469
- get status() {
470
- return status
471
- }
472
- }
473
521
  }
474
522
  /**
475
523
  * @param {unknown} data