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
@@ -42,17 +42,15 @@ function decode_bytes(bytes, charset) {
42
42
  * @returns {string | undefined}
43
43
  */
44
44
  function decode_korean(bytes) {
45
- if (!get_cp949_regex().test(bytes)) return
45
+ if (!get_cp949_regex().test(bytes)) {
46
+ return
47
+ }
46
48
  let korean
47
49
  try {
48
50
  korean = decode_bytes(bytes, "euc-kr")
49
51
  } catch {
50
52
  return
51
53
  }
52
- for (let i = 0; i < korean.length; i++) {
53
- const code = korean.charCodeAt(i)
54
- if (code >= 0x80 && code < 0xa0) return
55
- }
56
54
  return reads_as_latin1(bytes) ? void 0 : korean
57
55
  }
58
56
  /**
@@ -75,11 +73,11 @@ function decode_plain_name(name) {
75
73
  }
76
74
  return decoded
77
75
  }
78
- /**
79
- * @returns {RegExp}
80
- */
76
+ /** @returns {RegExp} */
81
77
  function get_cp949_regex() {
82
- if (cp949_regex) return cp949_regex
78
+ if (cp949_regex) {
79
+ return cp949_regex
80
+ }
83
81
  let uhc = false
84
82
  try {
85
83
  uhc = decode_bytes("\x8c\x63", "euc-kr") == "똠"
@@ -105,20 +103,25 @@ export function get_file_name(response, url) {
105
103
  hex => String.fromCharCode(parseInt(hex.slice(1), 16))
106
104
  )
107
105
  try {
108
- const decoded = byte_string_regex.test(bytes)
109
- ? decode_bytes(bytes, extended?.[1] || "utf-8")
110
- : ""
111
- if (decoded) return decoded
106
+ const decoded = decode_bytes(bytes, extended?.[1] || "utf-8")
107
+ if (decoded) {
108
+ return decoded
109
+ }
112
110
  } catch {}
113
111
  }
114
112
  const plain = plain_file_name_regex.exec(disposition)
115
113
  const name = plain?.[1]?.replace(/\\(.)/g, "$1") ?? plain?.[2]?.trim()
116
- if (name) return decode_plain_name(name)
114
+ if (name) {
115
+ return decode_plain_name(name)
116
+ }
117
117
  const segment = new URL(url, "http://localhost").pathname.split("/").pop()
118
+ if (!segment) {
119
+ return "download"
120
+ }
118
121
  try {
119
- return segment ? decodeURIComponent(segment) : "download"
122
+ return decodeURIComponent(segment)
120
123
  } catch {
121
- return segment || "download"
124
+ return segment
122
125
  }
123
126
  }
124
127
  /**
@@ -188,10 +191,16 @@ export async function read_body(response, as, method, url) {
188
191
  case "text":
189
192
  return response.text()
190
193
  }
191
- if (method == "HEAD" || null_body_statuses.has(response.status)) return
194
+ if (method == "HEAD" || null_body_statuses.has(response.status)) {
195
+ return
196
+ }
192
197
  const type = response.headers.get("Content-Type") ?? ""
193
- if (json_type_regex.test(type)) return parse_json(await response.text())
194
- if (type && !text_type_regex.test(type)) return response.blob()
198
+ if (json_type_regex.test(type)) {
199
+ return parse_json(await response.text())
200
+ }
201
+ if (type && !text_type_regex.test(type)) {
202
+ return response.blob()
203
+ }
195
204
  const text = await response.text()
196
205
  return type || text ? text : void 0
197
206
  }
@@ -200,12 +209,18 @@ export async function read_body(response, as, method, url) {
200
209
  * @returns {Promise<unknown>}
201
210
  */
202
211
  export async function read_error_data(response) {
203
- if (null_body_statuses.has(response.status)) return
212
+ if (null_body_statuses.has(response.status)) {
213
+ return
214
+ }
204
215
  const type = response.headers.get("Content-Type") ?? ""
205
216
  try {
206
- if (type && !json_type_regex.test(type) && !text_type_regex.test(type)) return await response.blob()
217
+ if (type && !json_type_regex.test(type) && !text_type_regex.test(type)) {
218
+ return await response.blob()
219
+ }
207
220
  const text = await response.text()
208
- if (!json_type_regex.test(type)) return text || void 0
221
+ if (!json_type_regex.test(type)) {
222
+ return text || void 0
223
+ }
209
224
  try {
210
225
  return parse_json(text)
211
226
  } catch {
@@ -220,7 +235,9 @@ export async function read_error_data(response) {
220
235
  * @returns {AsyncGenerator<StreamRecord, void, undefined>}
221
236
  */
222
237
  async function* read_events(response) {
223
- if (!response.body) return
238
+ if (!response.body) {
239
+ return
240
+ }
224
241
  const reader = response.body.pipeThrough(new TextDecoderStream())
225
242
  .getReader()
226
243
  let buffer = ""
@@ -235,10 +252,14 @@ async function* read_events(response) {
235
252
  try {
236
253
  for (;;) {
237
254
  const { done, value } = await reader.read()
238
- if (done) return
255
+ if (done) {
256
+ return
257
+ }
239
258
  const text = pending_cr && value.startsWith("\n") ? value.slice(1) : value
240
259
  pending_cr = false
241
- if (!text) continue
260
+ if (!text) {
261
+ continue
262
+ }
242
263
  buffer += text
243
264
  pending_cr = buffer.endsWith("\r")
244
265
  const lines = buffer.split(line_break_regex)
@@ -265,7 +286,9 @@ async function* read_events(response) {
265
286
  retry = void 0
266
287
  continue
267
288
  }
268
- if (line.startsWith(":")) continue
289
+ if (line.startsWith(":")) {
290
+ continue
291
+ }
269
292
  const colon = line.indexOf(":")
270
293
  const field = colon < 0 ? line : line.slice(0, colon)
271
294
  const field_value = colon < 0
@@ -273,10 +296,13 @@ async function* read_events(response) {
273
296
  : line.slice(
274
297
  line[colon + 1] == " " ? colon + 2 : colon + 1
275
298
  )
276
- if (field == "data") data.push(field_value)
277
- else if (field == "event") event = field_value
278
- else if (field == "id" && !field_value.includes("\0")) id = field_value
279
- else if (field == "retry" && /^\d+$/.test(field_value)) {
299
+ if (field == "data") {
300
+ data.push(field_value)
301
+ } else if (field == "event") {
302
+ event = field_value
303
+ } else if (field == "id" && !field_value.includes("\0")) {
304
+ id = field_value
305
+ } else if (field == "retry" && /^\d+$/.test(field_value)) {
280
306
  retry = +field_value
281
307
  yield { retry, type: "retry" }
282
308
  }
@@ -292,22 +318,30 @@ async function* read_events(response) {
292
318
  * @returns {AsyncGenerator<unknown, void, undefined>}
293
319
  */
294
320
  async function* read_lines(response) {
295
- if (!response.body) return
321
+ if (!response.body) {
322
+ return
323
+ }
296
324
  const reader = response.body.pipeThrough(new TextDecoderStream())
297
325
  .getReader()
298
326
  let buffer = ""
299
327
  try {
300
328
  for (;;) {
301
329
  const { done, value } = await reader.read()
302
- if (done) break
330
+ if (done) {
331
+ break
332
+ }
303
333
  buffer += value
304
334
  const lines = buffer.split("\n")
305
335
  buffer = /** @type {string} */(lines.pop())/**/
306
336
  for (const line of lines) {
307
- if (line.trim()) yield JSON.parse(line)
337
+ if (line.trim()) {
338
+ yield JSON.parse(line)
339
+ }
308
340
  }
309
341
  }
310
- if (buffer.trim()) yield JSON.parse(buffer)
342
+ if (buffer.trim()) {
343
+ yield JSON.parse(buffer)
344
+ }
311
345
  } finally {
312
346
  reader.cancel()
313
347
  .catch(noop)
@@ -321,20 +355,34 @@ function reads_as_latin1(bytes) {
321
355
  let in_word = false
322
356
  for (let i = 0; i < bytes.length; i++) {
323
357
  const first = bytes.charCodeAt(i)
324
- if (first < 0x80) continue
325
- if (first < 0xa0) return false
358
+ if (first < 0x80) {
359
+ continue
360
+ }
361
+ if (first < 0xa0) {
362
+ return false
363
+ }
326
364
  const second = bytes.charCodeAt(i + 1)
327
365
  if (second < 0x80 || i + 1 == bytes.length) {
328
366
  in_word = true
329
367
  continue
330
368
  }
331
- if (bytes.charCodeAt(i + 2) >= 0x80 || !is_latin1_letter(first) || !is_latin1_letter(second)) return false
369
+ if (bytes.charCodeAt(i + 2) >= 0x80 || !is_latin1_letter(first) || !is_latin1_letter(second)) {
370
+ return false
371
+ }
332
372
  if (is_latin1_lower(first)) {
333
- if (!is_latin1_lower(second)) return false
334
- if (is_ascii_letter(bytes.charCodeAt(i - 1)) || is_ascii_letter(bytes.charCodeAt(i + 2))) in_word = true
373
+ if (!is_latin1_lower(second)) {
374
+ return false
375
+ }
376
+ if (is_ascii_letter(bytes.charCodeAt(i - 1)) || is_ascii_letter(bytes.charCodeAt(i + 2))) {
377
+ in_word = true
378
+ }
335
379
  } else {
336
- if (is_latin1_lower(second) && second != 0xdf) return false
337
- if (is_ascii_upper(bytes.charCodeAt(i - 1)) && is_ascii_upper(bytes.charCodeAt(i + 2))) in_word = true
380
+ if (is_latin1_lower(second) && second != 0xdf) {
381
+ return false
382
+ }
383
+ if (is_ascii_upper(bytes.charCodeAt(i - 1)) && is_ascii_upper(bytes.charCodeAt(i + 2))) {
384
+ in_word = true
385
+ }
338
386
  }
339
387
  i++
340
388
  }
@@ -358,7 +406,9 @@ export function to_progress(loaded, total) {
358
406
  * @returns {Response}
359
407
  */
360
408
  export function track_progress(response, on_progress) {
361
- if (!on_progress || response.status < 200 || response.status > 599) return response
409
+ if (!on_progress || response.status < 200 || response.status > 599) {
410
+ return response
411
+ }
362
412
  const notify = on_progress
363
413
  if (!response.body) {
364
414
  call_progress(
@@ -375,19 +425,16 @@ export function track_progress(response, on_progress) {
375
425
  : void 0
376
426
  let loaded = 0
377
427
  let complete = false
378
- /** @param {import("../../public.js").Progress} progress */
379
- function report(progress) {
380
- complete = progress.ratio == 1
381
- call_progress(notify, progress)
382
- }
383
428
  report(to_progress(0, total))
384
429
  const body = response.body.pipeThrough(
385
430
  new TransformStream(
386
431
  {
387
432
  flush() {
388
- if (!complete) report(
389
- { loaded, ratio: 1, total: loaded }
390
- )
433
+ if (!complete) {
434
+ report(
435
+ { loaded, ratio: 1, total: loaded }
436
+ )
437
+ }
391
438
  },
392
439
  transform(chunk, controller) {
393
440
  loaded += chunk.byteLength
@@ -405,4 +452,12 @@ export function track_progress(response, on_progress) {
405
452
  statusText: response.statusText
406
453
  }
407
454
  )
455
+ /**
456
+ * @param {import("../../public.js").Progress} progress
457
+ * @returns {void}
458
+ */
459
+ function report(progress) {
460
+ complete = progress.ratio == 1
461
+ call_progress(notify, progress)
462
+ }
408
463
  }
package/src/http/retry.js CHANGED
@@ -25,7 +25,9 @@ const retry_statuses = new Set(
25
25
  * @returns {void}
26
26
  */
27
27
  function check_delay(delay, name) {
28
- if (delay == null || typeof delay == "function" || typeof delay == "number" && delay >= 0) return
28
+ if (delay == null || typeof delay == "function" || typeof delay == "number" && delay >= 0) {
29
+ return
30
+ }
29
31
  throw TypeError(
30
32
  `${name} must be non-negative milliseconds or a function`
31
33
  )
@@ -36,20 +38,27 @@ function check_delay(delay, name) {
36
38
  * @returns {void}
37
39
  */
38
40
  export function check_reconnect(reconnect, kind) {
39
- if (reconnect == null || typeof reconnect == "boolean") return
40
- if (typeof reconnect != "object") throw TypeError(
41
- `The reconnect of ${kind}() must be a boolean or { count, delay }`
42
- )
41
+ if (reconnect == null || typeof reconnect == "boolean") {
42
+ return
43
+ }
44
+ if (typeof reconnect != "object") {
45
+ throw TypeError(
46
+ `The reconnect of ${kind}() must be a boolean or { count, delay }`
47
+ )
48
+ }
43
49
  check_options(
44
50
  reconnect,
45
51
  [ "count", "delay" ],
46
52
  kind,
47
53
  "reconnect."
48
54
  )
49
- const { count, delay } = /** @type {import("../../public.js").ReconnectOptions} */(reconnect)/**/
50
- if (count != null && !is_count(count)) throw TypeError(
51
- `The reconnect count of ${kind}() must be a non-negative integer or Infinity`
52
- )
55
+ /** @type {import("../../public.js").ReconnectOptions} */
56
+ const { count, delay } = reconnect
57
+ if (count != null && !is_count(count)) {
58
+ throw TypeError(
59
+ `The reconnect count of ${kind}() must be a non-negative integer or Infinity`
60
+ )
61
+ }
53
62
  check_delay(
54
63
  delay,
55
64
  `The reconnect delay of ${kind}()`
@@ -61,16 +70,22 @@ export function check_reconnect(reconnect, kind) {
61
70
  * @returns {void}
62
71
  */
63
72
  export function check_retry(retry, kind) {
64
- if (retry == null) return
73
+ if (retry == null) {
74
+ return
75
+ }
65
76
  if (typeof retry == "number") {
66
- if (!is_count(retry)) throw TypeError(
67
- `The retry count of ${kind}() must be a non-negative integer or Infinity`
68
- )
77
+ if (!is_count(retry)) {
78
+ throw TypeError(
79
+ `The retry count of ${kind}() must be a non-negative integer or Infinity`
80
+ )
81
+ }
69
82
  return
70
83
  }
71
- if (typeof retry != "object") throw TypeError(
72
- `The retry of ${kind}() must be a non-negative integer or { count, delay, when }`
73
- )
84
+ if (typeof retry != "object") {
85
+ throw TypeError(
86
+ `The retry of ${kind}() must be a non-negative integer or { count, delay, when }`
87
+ )
88
+ }
74
89
  check_options(
75
90
  retry,
76
91
  [ "count", "delay", "when" ],
@@ -78,16 +93,20 @@ export function check_retry(retry, kind) {
78
93
  "retry."
79
94
  )
80
95
  const { count, delay, when } = /** @type {import("../../public.js").RetryOptions} */(retry)/**/
81
- if (!is_count(count)) throw TypeError(
82
- `The retry count of ${kind}() must be a non-negative integer or Infinity`
83
- )
96
+ if (!is_count(count)) {
97
+ throw TypeError(
98
+ `The retry count of ${kind}() must be a non-negative integer or Infinity`
99
+ )
100
+ }
84
101
  check_delay(
85
102
  delay,
86
103
  `The retry delay of ${kind}()`
87
104
  )
88
- if (when != null && typeof when != "function") throw TypeError(
89
- `The retry when of ${kind}() must be a function`
90
- )
105
+ if (when != null && typeof when != "function") {
106
+ throw TypeError(
107
+ `The retry when of ${kind}() must be a function`
108
+ )
109
+ }
91
110
  }
92
111
  /**
93
112
  * @param {number} attempt
@@ -131,7 +150,9 @@ export function get_reconnect_wait(reconnect, failures, error) {
131
150
  */
132
151
  export function get_retry_delay(error, attempt) {
133
152
  const ms = read_retry_after(error)
134
- if (ms == null) return get_backoff(attempt)
153
+ if (ms == null) {
154
+ return get_backoff(attempt)
155
+ }
135
156
  return ms <= max_retry_after ? ms : void 0
136
157
  }
137
158
  /**
@@ -141,7 +162,9 @@ export function get_retry_delay(error, attempt) {
141
162
  * @returns {number | undefined}
142
163
  */
143
164
  export function get_retry_wait(retry, attempt, error) {
144
- if (typeof retry.delay == "number") return retry.delay
165
+ if (typeof retry.delay == "number") {
166
+ return retry.delay
167
+ }
145
168
  const fallback = get_retry_delay(error, attempt)
146
169
  return retry.delay ? retry.delay(attempt, error, fallback) : fallback
147
170
  }
@@ -165,8 +188,8 @@ export function is_idempotent(method) {
165
188
  */
166
189
  export function is_retryable(error) {
167
190
  return error instanceof NetworkError
168
- || error instanceof TimeoutError
169
- || error instanceof HttpError && retry_statuses.has(error.status)
191
+ || error instanceof TimeoutError
192
+ || error instanceof HttpError && retry_statuses.has(error.status)
170
193
  }
171
194
  /**
172
195
  * @param {unknown} error
@@ -174,7 +197,9 @@ export function is_retryable(error) {
174
197
  */
175
198
  export function read_retry_after(error) {
176
199
  const retry_after = error instanceof HttpError ? error.headers.get("Retry-After") : null
177
- if (!retry_after) return
200
+ if (!retry_after) {
201
+ return
202
+ }
178
203
  const ms = /^\s*\d+\s*$/.test(retry_after)
179
204
  ? Number(retry_after) * 1000
180
205
  : Date.parse(retry_after) - Date.now()