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
package/src/limiter.js CHANGED
@@ -3,48 +3,49 @@ import { CancelError } from "./errors.js"
3
3
  import { check_options } from "./options.js"
4
4
  import { clamp_delay, noop, on_abort } from "./signal.js"
5
5
  /**
6
- * @param {{ concurrency?: number | undefined, rate?: { count: number, per: number } | undefined }} options
6
+ * @param {{ concurrency?: number | undefined, rate?: { count: number, per: number, window?: "fixed" | "sliding" | undefined } | undefined }} options
7
7
  * @param {() => void=} on_idle
8
8
  * @returns {LimiterState}
9
9
  */
10
10
  function create_state(options, on_idle) {
11
11
  const { concurrency = Infinity, rate } = options
12
+ const fixed = rate?.window == "fixed"
12
13
  /** @type {LimiterJob[]} */
13
14
  const queue = []
14
15
  /** @type {number[]} */
15
16
  const starts = []
17
+ let window_start = 0
18
+ let used = 0
16
19
  /** @type {LimiterState} */
17
- const state = { queue, running: 0, schedule: pump }
20
+ const state = {
21
+ queue,
22
+ running: 0,
23
+ schedule: pump,
24
+ take
25
+ }
18
26
  /** @type {ReturnType<typeof setTimeout> | undefined} */
19
27
  let timer
20
- /**
21
- * @returns {void}
22
- */
28
+ return state
29
+ /** @returns {void} */
23
30
  function done() {
24
31
  state.running--
25
32
  pump()
26
33
  }
27
- /**
28
- * @returns {void}
29
- */
34
+ /** @returns {void} */
30
35
  function pump() {
31
36
  while (queue.length && state.running < concurrency) {
32
37
  if (rate) {
33
- const now = Date.now()
34
- while (starts.length && now - /** @type {number} */(starts[0])/**/ >= rate.per) starts.shift()
35
- if (starts.length >= rate.count) {
38
+ const wait = reserve(Date.now())
39
+ if (wait > 0) {
36
40
  timer ??= setTimeout(
37
41
  () => {
38
42
  timer = void 0
39
43
  pump()
40
44
  },
41
- clamp_delay(
42
- /** @type {number} */(starts[0])/**/ + rate.per - now
43
- )
45
+ clamp_delay(wait)
44
46
  )
45
47
  return
46
48
  }
47
- starts.push(now)
48
49
  }
49
50
  const job = /** @type {LimiterJob} */(queue.shift())/**/
50
51
  job.unlink()
@@ -61,17 +62,27 @@ function create_state(options, on_idle) {
61
62
  job.resolve(
62
63
  {
63
64
  release: () => {
64
- if (released) return
65
+ if (released) {
66
+ return
67
+ }
65
68
  released = true
66
69
  done()
67
70
  }
68
71
  }
69
72
  )
70
73
  }
71
- if (queue.length || state.running || timer || !on_idle) return
72
- const left = rate && starts.length
73
- ? /** @type {number} */(starts.at(-1))/**/ + rate.per - Date.now()
74
- : 0
74
+ if (queue.length || state.running || timer || !on_idle) {
75
+ return
76
+ }
77
+ const left = !rate
78
+ ? 0
79
+ : fixed
80
+ ? used
81
+ ? window_start + rate.per - Date.now()
82
+ : 0
83
+ : starts.length
84
+ ? /** @type {number} */(starts.at(-1))/**/ + rate.per - Date.now()
85
+ : 0
75
86
  if (left > 0) {
76
87
  timer = setTimeout(
77
88
  () => {
@@ -84,16 +95,50 @@ function create_state(options, on_idle) {
84
95
  }
85
96
  on_idle()
86
97
  }
87
- return state
98
+ /**
99
+ * @param {number} now
100
+ * @returns {number}
101
+ */
102
+ function reserve(now) {
103
+ const { count, per } = /** @type {{ count: number, per: number }} */(rate)/**/
104
+ if (fixed) {
105
+ if (used && now - window_start >= per) {
106
+ used = 0
107
+ }
108
+ if (used >= count) {
109
+ return window_start + per - now
110
+ }
111
+ if (!used) {
112
+ window_start = now
113
+ }
114
+ used++
115
+ return 0
116
+ }
117
+ while (starts.length && now - /** @type {number} */(starts[0])/**/ >= per) {
118
+ starts.shift()
119
+ }
120
+ if (starts.length >= count) {
121
+ return /** @type {number} */(starts[0])/**/ + per - now
122
+ }
123
+ starts.push(now)
124
+ return 0
125
+ }
126
+ /** @returns {number} */
127
+ function take() {
128
+ return reserve(Date.now())
129
+ }
88
130
  }
89
131
  /**
90
132
  * Limits the calls that run at the same time, and the calls that start per interval, across everything that shares it:
91
133
  * a database pool, a file system, an API quota. Use it inside flow nodes to limit them across runs.
134
+ * With a `rate`, `take()` answers at once whether a start is free, e.g. for a packet limit per socket.
92
135
  * @example
93
136
  * const db = limiter({ concurrency: 10 })
94
137
  * const openai = limiter({ rate: { count: 60, per: 60000 } })
95
138
  * const user = await db(() => pool.query("select ..."))
96
- * @param {{ concurrency?: number | undefined, rate?: { count: number, per: number } | undefined }} options
139
+ * const packets = limiter({ rate: { count: 20, per: 1000, window: "fixed" } })
140
+ * const retry_after = packets.take(socket.id)
141
+ * @param {{ concurrency?: number | undefined, rate?: { count: number, per: number, window?: "fixed" | "sliding" | undefined } | undefined }} options
97
142
  * @returns {import("../public.js").Limiter}
98
143
  */
99
144
  export function limiter(options) {
@@ -104,46 +149,78 @@ export function limiter(options) {
104
149
  )
105
150
  check_options(
106
151
  options.rate,
107
- [ "count", "per" ],
152
+ [ "count", "per", "window" ],
108
153
  "limiter",
109
154
  "rate."
110
155
  )
111
156
  const { concurrency = Infinity, rate } = options
112
- if (!(concurrency === Infinity || Number.isInteger(concurrency) && concurrency >= 1)) throw TypeError(
113
- "The concurrency of limiter() must be a positive integer or Infinity"
114
- )
115
- if (rate && !(Number.isInteger(rate.count) && rate.count >= 1 && typeof rate.per == "number" && rate.per > 0)) throw TypeError(
116
- "The rate of limiter() must be { count, per } with a positive integer count and positive milliseconds"
117
- )
157
+ if (!(concurrency === Infinity || Number.isInteger(concurrency) && concurrency >= 1)) {
158
+ throw TypeError(
159
+ "The concurrency of limiter() must be a positive integer or Infinity"
160
+ )
161
+ }
162
+ if (rate && !(Number.isInteger(rate.count) && rate.count >= 1 && typeof rate.per == "number" && rate.per > 0)) {
163
+ throw TypeError(
164
+ "The rate of limiter() must be { count, per } with a positive integer count and positive milliseconds"
165
+ )
166
+ }
167
+ if (rate?.window !== void 0 && rate.window != "sliding" && rate.window != "fixed") {
168
+ throw TypeError(
169
+ "The rate.window of limiter() must be \"sliding\" or \"fixed\""
170
+ )
171
+ }
118
172
  /** @type {Map<unknown, LimiterState>} */
119
173
  const keyed = new Map()
120
174
  const root = create_state(options)
175
+ return to_limiter(
176
+ () => root,
177
+ () => root,
178
+ key,
179
+ name => take(() => root, name)
180
+ )
121
181
  /**
122
182
  * @param {unknown} name
123
183
  * @returns {import("../public.js").Limiter}
124
184
  */
125
185
  function key(name) {
126
- /**
127
- * @returns {LimiterState}
128
- */
129
- function current() {
130
- let state = keyed.get(name)
131
- if (!state) {
132
- state = create_state(
133
- options,
134
- () => keyed.delete(name)
135
- )
136
- keyed.set(name, state)
137
- }
138
- return state
139
- }
140
186
  return to_limiter(
141
- current,
187
+ () => state_of(name),
142
188
  () => keyed.get(name),
143
- key
189
+ key,
190
+ other => take(() => state_of(name), other)
144
191
  )
145
192
  }
146
- return to_limiter(() => root, () => root, key)
193
+ /**
194
+ * @param {unknown} name
195
+ * @returns {LimiterState}
196
+ */
197
+ function state_of(name) {
198
+ let state = keyed.get(name)
199
+ if (!state) {
200
+ state = create_state(
201
+ options,
202
+ () => keyed.delete(name)
203
+ )
204
+ keyed.set(name, state)
205
+ }
206
+ return state
207
+ }
208
+ /**
209
+ * @param {() => LimiterState} own
210
+ * @param {unknown} name
211
+ * @returns {number}
212
+ */
213
+ function take(own, name) {
214
+ if (!rate) {
215
+ throw TypeError(
216
+ "take() of limiter() needs a rate"
217
+ )
218
+ }
219
+ const state = name === void 0 ? own() : state_of(name)
220
+ const wait = state.take()
221
+ state.schedule()
222
+ return wait
223
+ }
147
224
  }
148
225
  /**
149
226
  * @param {unknown} reason
@@ -156,9 +233,28 @@ function to_cancel_error(reason) {
156
233
  * @param {() => LimiterState} current
157
234
  * @param {() => LimiterState | undefined} peek
158
235
  * @param {(key: unknown) => import("../public.js").Limiter} key
236
+ * @param {(key: unknown) => number} take
159
237
  * @returns {import("../public.js").Limiter}
160
238
  */
161
- function to_limiter(current, peek, key) {
239
+ function to_limiter(current, peek, key, take) {
240
+ return /** @type {import("../public.js").Limiter} */(Object.defineProperties(
241
+ limit,
242
+ {
243
+ acquire: {
244
+ value: (
245
+ /** @type {AbortSignal=} */ signal
246
+ ) => enqueue(void 0, signal)
247
+ },
248
+ key: { value: key },
249
+ pending: {
250
+ get: () => peek()?.queue.length ?? 0
251
+ },
252
+ running: {
253
+ get: () => peek()?.running ?? 0
254
+ },
255
+ take: { value: take }
256
+ }
257
+ ))/**/
162
258
  /**
163
259
  * @param {(() => unknown) | undefined} fn
164
260
  * @param {AbortSignal | undefined} signal
@@ -179,7 +275,9 @@ function to_limiter(current, peek, key) {
179
275
  signal,
180
276
  () => {
181
277
  const index = state.queue.indexOf(job)
182
- if (index < 0) return
278
+ if (index < 0) {
279
+ return
280
+ }
183
281
  state.queue.splice(index, 1)
184
282
  reject(to_cancel_error(signal.reason))
185
283
  state.schedule()
@@ -199,21 +297,4 @@ function to_limiter(current, peek, key) {
199
297
  function limit(fn, signal) {
200
298
  return enqueue(fn, signal)
201
299
  }
202
- return /** @type {import("../public.js").Limiter} */(Object.defineProperties(
203
- limit,
204
- {
205
- acquire: {
206
- value: (
207
- /** @type {AbortSignal=} */ signal
208
- ) => enqueue(void 0, signal)
209
- },
210
- key: { value: key },
211
- pending: {
212
- get: () => peek()?.queue.length ?? 0
213
- },
214
- running: {
215
- get: () => peek()?.running ?? 0
216
- }
217
- }
218
- ))/**/
219
300
  }
package/src/offload.js CHANGED
@@ -1,31 +1,54 @@
1
1
  /** @import { NodeWorker, OffloadJob, OffloadWorker } from "../private.js" */
2
2
  import { CancelError } from "./errors.js"
3
+ import { offload_module, to_error } from "./offload_module.js"
3
4
  import { check_options } from "./options.js"
4
5
  import { noop, on_abort } from "./signal.js"
5
6
  /**
6
- * Runs a function in a Worker, or a worker thread in Node.js 20.16 or later, so that heavy work does not block the page or the server.
7
- * The function travels as its source, so it may use only its arguments and globals: an arrow function or a function expression,
8
- * not a method or a closure. Arguments and results are copied as `postMessage` copies them. `concurrency` workers run at once, 1 by default.
9
- * @example
10
- * const resize = offload(async (bytes: ArrayBuffer, width: number) => shrink(bytes, width))
11
- * const thumbnail = await resize(bytes, 200, signal) // the signal terminates the worker
7
+ * @param {unknown} target
8
+ * @param {unknown=} options
9
+ * @returns {never}
10
+ */
11
+ function create_offload(target, options) {
12
+ if (typeof target == "function") {
13
+ return /** @type {never} */(offload_function(
14
+ /** @type {(...args: never[]) => unknown} */(target)/**/,
15
+ /** @type {{ concurrency?: number | undefined } | undefined} */(options)/**/
16
+ ))/**/
17
+ }
18
+ if (typeof target == "string" || target instanceof URL) {
19
+ return /** @type {never} */(offload_module(
20
+ target,
21
+ /** @type {import("../public.js").OffloadModuleOptions | undefined} */(options)/**/
22
+ ))/**/
23
+ }
24
+ throw TypeError(
25
+ "offload() needs a function, or the URL of a worker module"
26
+ )
27
+ }
28
+ /** @type {import("../public.js").OffloadFunction} */
29
+ export const offload = create_offload
30
+ /**
12
31
  * @template {unknown[]} A
13
32
  * @template R
14
33
  * @param {(...args: A) => R} fn
15
34
  * @param {{ concurrency?: number | undefined }=} options
16
35
  * @returns {import("../public.js").Offloaded<A, R>}
17
36
  */
18
- export function offload(fn, options = {}) {
19
- if (typeof fn != "function") throw TypeError("offload() needs a function")
37
+ function offload_function(fn, options = {}) {
38
+ if (typeof fn != "function") {
39
+ throw TypeError("offload() needs a function")
40
+ }
20
41
  check_options(
21
42
  options,
22
43
  [ "concurrency" ],
23
44
  "offload"
24
45
  )
25
46
  const concurrency = options.concurrency ?? 1
26
- if (!(concurrency === Infinity || Number.isInteger(concurrency) && concurrency >= 1)) throw TypeError(
27
- "The concurrency of offload() must be a positive integer or Infinity"
28
- )
47
+ if (!(concurrency === Infinity || Number.isInteger(concurrency) && concurrency >= 1)) {
48
+ throw TypeError(
49
+ "The concurrency of offload() must be a positive integer or Infinity"
50
+ )
51
+ }
29
52
  const source = String(fn)
30
53
  /** @type {OffloadJob[]} */
31
54
  const queue = []
@@ -34,6 +57,26 @@ export function offload(fn, options = {}) {
34
57
  /** @type {string | undefined} */
35
58
  let url
36
59
  let count = 0
60
+ const run = /** @type {import("../public.js").Offloaded<never[], unknown>} */(/** @type {unknown} */(call))/**/
61
+ run.close = () => {
62
+ for (const job of queue.splice(0)) {
63
+ job.reject(
64
+ new CancelError("The offload was closed")
65
+ )
66
+ }
67
+ for (const worker of workers) {
68
+ worker.job?.reject(
69
+ new CancelError("The offload was closed")
70
+ )
71
+ worker.stop()
72
+ }
73
+ workers.clear()
74
+ if (url) {
75
+ URL.revokeObjectURL(url)
76
+ }
77
+ url = void 0
78
+ }
79
+ return /** @type {import("../public.js").Offloaded<A, R>} */(/** @type {unknown} */(run))/**/
37
80
  /**
38
81
  * @param {...unknown} args
39
82
  * @returns {import("../public.js").OffloadCall<unknown>}
@@ -41,14 +84,16 @@ export function offload(fn, options = {}) {
41
84
  function call(...args) {
42
85
  const last = args.at(-1)
43
86
  const signal = last instanceof AbortSignal ? last : void 0
44
- if (signal) args.pop()
87
+ if (signal) {
88
+ args.pop()
89
+ }
45
90
  let unlink = noop
46
91
  /** @type {OffloadJob} */
47
92
  const job = {
48
93
  args,
49
94
  id: ++count,
50
- reject: () => {},
51
- resolve: () => {}
95
+ reject: noop,
96
+ resolve: noop
52
97
  }
53
98
  const promise = new Promise(
54
99
  (resolve, reject) => {
@@ -62,12 +107,15 @@ export function offload(fn, options = {}) {
62
107
  }
63
108
  }
64
109
  )
65
- if (signal?.aborted) job.reject(new CancelError(signal.reason))
66
- else {
67
- if (signal) unlink = on_abort(
68
- signal,
69
- () => cancel_job(job, signal.reason)
70
- )
110
+ if (signal?.aborted) {
111
+ job.reject(new CancelError(signal.reason))
112
+ } else {
113
+ if (signal) {
114
+ unlink = on_abort(
115
+ signal,
116
+ () => cancel_job(job, signal.reason)
117
+ )
118
+ }
71
119
  queue.push(job)
72
120
  dispatch()
73
121
  }
@@ -86,9 +134,13 @@ export function offload(fn, options = {}) {
86
134
  */
87
135
  function cancel_job(job, reason) {
88
136
  const index = queue.indexOf(job)
89
- if (index >= 0) queue.splice(index, 1)
137
+ if (index >= 0) {
138
+ queue.splice(index, 1)
139
+ }
90
140
  for (const worker of workers) {
91
- if (worker.job != job) continue
141
+ if (worker.job != job) {
142
+ continue
143
+ }
92
144
  worker.job = void 0
93
145
  worker.stop()
94
146
  workers.delete(worker)
@@ -98,18 +150,20 @@ export function offload(fn, options = {}) {
98
150
  )
99
151
  dispatch()
100
152
  }
101
- /**
102
- * @returns {void}
103
- */
153
+ /** @returns {void} */
104
154
  function dispatch() {
105
155
  while (queue.length) {
106
156
  let worker = [ ...workers ].find(item => !item.job)
107
157
  if (!worker) {
108
- if (workers.size >= concurrency) return
158
+ if (workers.size >= concurrency) {
159
+ return
160
+ }
109
161
  try {
110
162
  worker = spawn()
111
163
  } catch (error) {
112
- for (const job of queue.splice(0)) job.reject(error)
164
+ for (const job of queue.splice(0)) {
165
+ job.reject(error)
166
+ }
113
167
  return
114
168
  }
115
169
  }
@@ -132,40 +186,14 @@ export function offload(fn, options = {}) {
132
186
  worker.idle?.()
133
187
  dispatch()
134
188
  }
135
- /**
136
- * @returns {OffloadWorker}
137
- */
189
+ /** @returns {OffloadWorker} */
138
190
  function spawn() {
139
191
  const threads = /** @type {{ process?: { getBuiltinModule?: (name: string) => unknown } }} */(globalThis)/**/.process?.getBuiltinModule?.("node:worker_threads")
140
192
  /** @type {OffloadWorker} */
141
193
  const worker = {
142
194
  job: void 0,
143
- send: () => {},
144
- stop: () => {}
145
- }
146
- /**
147
- * @param {unknown} error
148
- * @returns {void}
149
- */
150
- function crash(error) {
151
- const { job } = worker
152
- worker.job = void 0
153
- worker.stop()
154
- workers.delete(worker)
155
- job?.reject(to_error(error))
156
- dispatch()
157
- }
158
- /**
159
- * @param {[number, boolean, unknown]} message
160
- * @returns {void}
161
- */
162
- function receive([ id, ok, value ]) {
163
- const { job } = worker
164
- if (!job || job.id != id) return
165
- worker.job = void 0
166
- if (ok) job.resolve(value)
167
- else job.reject(to_error(value))
168
- finish(worker)
195
+ send: noop,
196
+ stop: noop
169
197
  }
170
198
  if (threads) {
171
199
  const { Worker } = /** @type {{ Worker: new (code: string, options: { eval: true }) => NodeWorker }} */(threads)/**/
@@ -201,28 +229,43 @@ export function offload(fn, options = {}) {
201
229
  }
202
230
  worker.send = message => thread.postMessage(message)
203
231
  worker.stop = () => thread.terminate()
204
- } else throw TypeError(
205
- "offload() needs Worker, or worker_threads of Node.js 20.16 or later"
206
- )
232
+ } else {
233
+ throw TypeError(
234
+ "offload() needs Worker, or worker_threads of Node.js 20.16 or later"
235
+ )
236
+ }
207
237
  workers.add(worker)
208
238
  return worker
209
- }
210
- const run = /** @type {import("../public.js").Offloaded<never[], unknown>} */(/** @type {unknown} */(call))/**/
211
- run.close = () => {
212
- for (const job of queue.splice(0)) job.reject(
213
- new CancelError("The offload was closed")
214
- )
215
- for (const worker of workers) {
216
- worker.job?.reject(
217
- new CancelError("The offload was closed")
218
- )
239
+ /**
240
+ * @param {unknown} error
241
+ * @returns {void}
242
+ */
243
+ function crash(error) {
244
+ const { job } = worker
245
+ worker.job = void 0
219
246
  worker.stop()
247
+ workers.delete(worker)
248
+ job?.reject(to_error(error))
249
+ dispatch()
250
+ }
251
+ /**
252
+ * @param {[number, boolean, unknown]} message
253
+ * @returns {void}
254
+ */
255
+ function receive([ id, ok, value ]) {
256
+ const { job } = worker
257
+ if (!job || job.id != id) {
258
+ return
259
+ }
260
+ worker.job = void 0
261
+ if (ok) {
262
+ job.resolve(value)
263
+ } else {
264
+ job.reject(to_error(value))
265
+ }
266
+ finish(worker)
220
267
  }
221
- workers.clear()
222
- if (url) URL.revokeObjectURL(url)
223
- url = void 0
224
268
  }
225
- return /** @type {import("../public.js").Offloaded<A, R>} */(/** @type {unknown} */(run))/**/
226
269
  }
227
270
  /**
228
271
  * @param {string} post
@@ -240,16 +283,4 @@ function reply(post) {
240
283
  }
241
284
  }
242
285
  }`
243
- }
244
- /**
245
- * @param {unknown} value
246
- * @returns {unknown}
247
- */
248
- function to_error(value) {
249
- if (value instanceof Error || value == null || typeof value != "object" || !("message" in value)) return value
250
- const error = Error(String(value.message))
251
- error.name = String(
252
- /** @type {{ name?: unknown }} */(value)/**/.name ?? "Error"
253
- )
254
- return error
255
286
  }