@orkestrel/worker 0.0.4 → 0.0.5

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.
@@ -1,4 +1,5 @@
1
1
  import { ContractShape } from '@orkestrel/contract';
2
+ import { Guard } from '@orkestrel/contract';
2
3
  import { Infer } from '@orkestrel/contract';
3
4
  import { QueueExecution } from '@orkestrel/queue';
4
5
  import { QueueStoreInterface } from '@orkestrel/queue';
@@ -25,8 +26,8 @@ import { WorkerInterface } from '../core/index.ts';
25
26
  *
26
27
  * @example
27
28
  * ```ts
28
- * import { stringShape } from '@src/core'
29
- * import { createJSONQueueStore } from '@src/server'
29
+ * import { stringShape } from '@orkestrel/contract'
30
+ * import { createJSONQueueStore } from '@orkestrel/worker/server'
30
31
  *
31
32
  * const store = createJSONQueueStore('data/queue.json', stringShape())
32
33
  * await store.save({ id: 'job-1', input: 'https://example.com', attempts: 0 })
@@ -65,7 +66,7 @@ export declare function createJSONQueueStore<TInput extends ContractShape>(path:
65
66
  *
66
67
  * @example
67
68
  * ```ts
68
- * import { createNodeWorker } from '@src/server'
69
+ * import { createNodeWorker } from '@orkestrel/worker/server'
69
70
  *
70
71
  * const worker = createNodeWorker({
71
72
  * script: new URL('./double.js', import.meta.url),
@@ -75,7 +76,7 @@ export declare function createJSONQueueStore<TInput extends ContractShape>(path:
75
76
  * })
76
77
  *
77
78
  * const doubled = await worker.enqueue(21) // 42, computed on a worker thread
78
- * worker.destroy() // terminates every thread
79
+ * await worker.destroy() // terminates every thread
79
80
  * ```
80
81
  */
81
82
  export declare function createNodeWorker<TInput, TResult>(options: NodeWorkerOptions<TInput, TResult>): WorkerInterface<TInput, TResult>;
@@ -88,15 +89,15 @@ export declare function createNodeWorker<TInput, TResult>(options: NodeWorkerOpt
88
89
  * that id: a success `value` is narrowed through `result` (a value that fails the guard
89
90
  * rejects — the zero-`as` type bridge), a failure rejects with the thread's error string.
90
91
  * A thread that ALREADY died rejects synchronously at entry from the latched
91
- * {@link NodeThread.death} — its death events fired before this dispatch existed (under
92
- * load they arrive in one batched exit drain) and will never fire again, so waiting on
93
- * the listeners below would dangle forever; the latch makes the death total across every
94
- * event ordering. If the thread `error`s / `exit`s mid-flight it is marked dead and the
95
- * job rejects. On `execution.signal` abort it posts an `abort` envelope (cooperative) AND
96
- * evicts the thread `alive = false` + `terminate()` — because CPU-bound work cannot
97
- * honour the signal; the freed pool slot then gets a fresh thread. Every listener (the
98
- * thread's `message` / `error` / `exit` and the signal's `abort`) is removed on settle,
99
- * and a `settled` guard prevents a double-settle.
92
+ * {@link NodeThread.death} — its death events fired before this dispatch existed and will
93
+ * never fire again, so waiting on the listeners below would dangle forever; the latch makes
94
+ * death total across every event ordering. If the thread `error`s / `exit`s mid-flight it is
95
+ * marked dead and the
96
+ * job rejects. An inbound `messageerror` also evicts and terminates the thread before
97
+ * rejection. On `execution.signal` abort it contains the cooperative `abort` post,
98
+ * evicts the thread, and observes `terminate()` settlement because CPU-bound work cannot
99
+ * honour the signal; the freed pool slot then gets a fresh thread. Every per-job listener
100
+ * (`message` / `messageerror` / `error` / `exit` / `abort`) is removed on settle.
100
101
  *
101
102
  * @typeParam TResult - The reply type the `result` guard narrows to
102
103
  * @param thread - The leased thread to run the job on
@@ -107,23 +108,6 @@ export declare function createNodeWorker<TInput, TResult>(options: NodeWorkerOpt
107
108
  */
108
109
  export declare function dispatch<TResult>(thread: NodeThread, input: unknown, execution: QueueExecution, result: Guard<TResult>): Promise<TResult>;
109
110
 
110
- /**
111
- * A runtime type predicate used to narrow a wire payload with no assertion.
112
- *
113
- * @remarks
114
- * Mirrors the core `Guard<T>` (a total `(value: unknown) => value is T` predicate,
115
- * AGENTS §14) but is re-declared here so the server workers surface is self-describing
116
- * and the worker-side `serve.ts` — which may not import from `@src/core` (it loads as
117
- * raw `.ts` inside a spawned thread) — shares the same vocabulary. A `Guard` NEVER
118
- * throws; adversarial input returns `false`. It is the zero-`as` bridge across the
119
- * structured-clone boundary: the main side narrows each reply value through the
120
- * `result` guard and the input through the `input` guard, so a generic `TInput` /
121
- * `TResult` is reconstructed by validation rather than asserted.
122
- *
123
- * @typeParam T - The type a value is narrowed to when the predicate holds
124
- */
125
- export declare type Guard<T> = (value: unknown) => value is T;
126
-
127
111
  /**
128
112
  * Narrow an inbound `message` to a {@link Reply} for a given job `id` — no assertion.
129
113
  *
@@ -142,16 +126,16 @@ export declare function isReply(value: unknown, id: string): value is Reply;
142
126
  * {@link createNodeWorker} leases per job.
143
127
  *
144
128
  * @remarks
145
- * `alive` starts `true` and flips to `false` the moment the thread `error`s, `exit`s, or
146
- * is evicted on abort; the pool's `validate` reads `alive && worker.threadId > 0`, so a
129
+ * `alive` starts `true` and flips to `false` when the thread `error`s, reports a
130
+ * `messageerror`, exits, or is evicted on abort; the pool's `validate` reads
131
+ * `alive && worker.threadId > 0`, so a
147
132
  * dead thread is destroyed and replaced rather than reused. `death` LATCHES the first
148
- * terminal event (`error`'s `Error`, or a synthesized one on `exit`) — the death-signal
133
+ * terminal event (`error` / `messageerror`, or a synthesized error on `exit`) — the death-signal
149
134
  * record a `dispatch` checks at entry, so a job dispatched AFTER the thread died (its
150
135
  * death events already fired and will never fire again) rejects immediately instead of
151
- * awaiting events that already happened. Under event-loop pressure Node delivers a dead
152
- * thread's `online` + `error` + `exit` in ONE synchronous exit-drain batch, starving the
153
- * microtask chain that attaches the dispatch listeners until after every death event —
154
- * the latch is what makes that ordering safe. `worker` is the underlying
136
+ * awaiting events that already happened. A thread can become terminal before the readiness
137
+ * promise continuation attaches dispatch listeners; the latch is what makes that ordering
138
+ * safe. `worker` is the underlying
155
139
  * `node:worker_threads` thread (its `postMessage` / `terminate` drive the protocol).
156
140
  */
157
141
  export declare interface NodeThread {
@@ -165,8 +149,9 @@ export declare interface NodeThread {
165
149
  *
166
150
  * @remarks
167
151
  * - `script` — the worker module each pooled thread runs; its module must call
168
- * `serveWorker(...)`. A `.ts` script requires Node 23.6 (native type-stripping); on
169
- * older Node point this at a built `.js` / `.mjs`.
152
+ * `serveWorker(...)`. Raw TypeScript is unflagged on Node 22.18+ and Node 23.6+;
153
+ * Node 22.12–22.17 and Node 23.0–23.5 require `--experimental-strip-types`. A built
154
+ * `.js` / `.mjs` script remains an alternative across supported Node versions.
170
155
  * - `input` — narrows the work payload BEFORE it crosses the structured-clone boundary
171
156
  * (fail-fast) and supplies the `TInput` inference, so call sites need no type argument.
172
157
  * - `result` — narrows every reply value coming back from a thread; an invalid reply
@@ -174,7 +159,8 @@ export declare interface NodeThread {
174
159
  * - `workerData` — opaque data cloned to every thread once at spawn (read there via
175
160
  * `serveWorker`'s host `workerData`); must be structured-cloneable.
176
161
  * - `concurrency` — the maximum jobs in flight at once; the thread pool's `max` matches
177
- * it, so at most this many threads exist. Defaults to `1`. Floored at `1`.
162
+ * it, so at most this many threads exist. Defaults to `1` and must be a positive safe
163
+ * integer, as validated by the underlying queue.
178
164
  * - `retries` — the default extra attempts per job on failure / timeout; defaults to `0`.
179
165
  * - `timeout` — the default per-attempt deadline in milliseconds; defaults to none.
180
166
  * - `store` — durable backing for outstanding jobs (survives a restart; `restore()`
@@ -202,11 +188,11 @@ export declare interface NodeWorkerOptions<TInput, TResult> {
202
188
  * @remarks
203
189
  * Internal plumbing rather than public call surface, but centralized here per AGENTS §5 (an
204
190
  * impl file holds only its class / functions). A reply is a discriminated union on `ok`: a
205
- * `true` carries any opaque `value` (narrowed at the boundary by the `result` {@link Guard},
191
+ * `true` carries any opaque `value` (narrowed at the boundary by the `result` guard,
206
192
  * with no `as`); a `false` carries a string `error`. The worker-side `serve.ts` cannot import
207
193
  * this (it loads as raw source in a spawned thread, AGENTS §5 exception) and posts the same
208
- * shape structurally. The `id` ties a reply to its job, so a stray / foreign-id message is
209
- * ignored.
194
+ * shape structurally. The `id` ties a reply to its job: id-less / foreign-id chatter is ignored,
195
+ * while a matching-id malformed envelope taints the thread and causes dispatch to terminate it.
210
196
  */
211
197
  export declare type Reply = {
212
198
  readonly id: string;
@@ -226,7 +212,10 @@ export declare type Reply = {
226
212
  * run/abort protocol: a `run` message narrows its `input` through `options.input` (an
227
213
  * invalid payload replies with an error envelope, never running the handler), then runs
228
214
  * `options.handler(input, { signal })` and replies `{ id, ok: true, value }` on success or
229
- * `{ id, ok: false, error }` on throw. Each in-flight job has its own `AbortController`,
215
+ * `{ id, ok: false, error }` on throw. Input-guard throws use the same failure envelope. If a
216
+ * success value cannot be cloned, the post is retried as a clone-safe failure; if that post also
217
+ * fails, the parent port closes so the main side observes thread exit instead of waiting forever.
218
+ * Each in-flight job has its own `AbortController`,
230
219
  * so an `abort` message for that id fires the handler's `signal` (cooperative — the main
231
220
  * side ALSO terminates the thread, so a handler that ignores its signal is still stopped).
232
221
  * Every inbound message is narrowed with the inlined guards — no `as`. On the main thread
@@ -239,7 +228,7 @@ export declare type Reply = {
239
228
  * @example
240
229
  * ```ts
241
230
  * // double.ts — a worker script
242
- * import { serveWorker } from '@src/server'
231
+ * import { serveWorker } from '@orkestrel/worker/server'
243
232
  *
244
233
  * serveWorker<number, number>({
245
234
  * input: (value): value is number => typeof value === 'number',
@@ -281,11 +270,11 @@ export declare interface ServeWorkerOptions<TInput, TResult> {
281
270
  * listeners that flip `alive` to `false` AND latch the first terminal event on
282
271
  * {@link NodeThread.death}: a crash is observable to an in-flight {@link dispatch} (via
283
272
  * its own listeners), to the pool's `validate` (via `alive`), and — crucially — to a
284
- * dispatch that attaches AFTER the death (via the latch). The latch closes a real race:
285
- * under event-loop pressure a dead thread's `online` + `error` + `exit` are delivered in
286
- * ONE synchronous exit-drain batch, so every death event fires before the microtask chain
287
- * resolving this spawn can hand the thread to `dispatch` without the latch that job
288
- * would await events that already fired, forever. The pool's `create` hook calls this.
273
+ * dispatch that attaches AFTER the death (via the latch). A `messageerror` is terminal too,
274
+ * so a thread whose inbound payload could not be deserialized is never reused. The latch
275
+ * closes a real race: a thread can become terminal before the readiness promise continuation
276
+ * hands it to `dispatch`, leaving no future death event for that dispatch to observe. Without
277
+ * the latch, that job would wait forever. The pool's `create` hook calls this.
289
278
  *
290
279
  * @param script - The worker module each thread runs (must call `serveWorker`)
291
280
  * @param workerData - Opaque, structured-cloneable data handed to the thread at spawn
@@ -1,4 +1,5 @@
1
1
  import { ContractShape } from '@orkestrel/contract';
2
+ import { Guard } from '@orkestrel/contract';
2
3
  import { Infer } from '@orkestrel/contract';
3
4
  import { QueueExecution } from '@orkestrel/queue';
4
5
  import { QueueStoreInterface } from '@orkestrel/queue';
@@ -25,8 +26,8 @@ import { WorkerInterface } from '../core/index.ts';
25
26
  *
26
27
  * @example
27
28
  * ```ts
28
- * import { stringShape } from '@src/core'
29
- * import { createJSONQueueStore } from '@src/server'
29
+ * import { stringShape } from '@orkestrel/contract'
30
+ * import { createJSONQueueStore } from '@orkestrel/worker/server'
30
31
  *
31
32
  * const store = createJSONQueueStore('data/queue.json', stringShape())
32
33
  * await store.save({ id: 'job-1', input: 'https://example.com', attempts: 0 })
@@ -65,7 +66,7 @@ export declare function createJSONQueueStore<TInput extends ContractShape>(path:
65
66
  *
66
67
  * @example
67
68
  * ```ts
68
- * import { createNodeWorker } from '@src/server'
69
+ * import { createNodeWorker } from '@orkestrel/worker/server'
69
70
  *
70
71
  * const worker = createNodeWorker({
71
72
  * script: new URL('./double.js', import.meta.url),
@@ -75,7 +76,7 @@ export declare function createJSONQueueStore<TInput extends ContractShape>(path:
75
76
  * })
76
77
  *
77
78
  * const doubled = await worker.enqueue(21) // 42, computed on a worker thread
78
- * worker.destroy() // terminates every thread
79
+ * await worker.destroy() // terminates every thread
79
80
  * ```
80
81
  */
81
82
  export declare function createNodeWorker<TInput, TResult>(options: NodeWorkerOptions<TInput, TResult>): WorkerInterface<TInput, TResult>;
@@ -88,15 +89,15 @@ export declare function createNodeWorker<TInput, TResult>(options: NodeWorkerOpt
88
89
  * that id: a success `value` is narrowed through `result` (a value that fails the guard
89
90
  * rejects — the zero-`as` type bridge), a failure rejects with the thread's error string.
90
91
  * A thread that ALREADY died rejects synchronously at entry from the latched
91
- * {@link NodeThread.death} — its death events fired before this dispatch existed (under
92
- * load they arrive in one batched exit drain) and will never fire again, so waiting on
93
- * the listeners below would dangle forever; the latch makes the death total across every
94
- * event ordering. If the thread `error`s / `exit`s mid-flight it is marked dead and the
95
- * job rejects. On `execution.signal` abort it posts an `abort` envelope (cooperative) AND
96
- * evicts the thread `alive = false` + `terminate()` — because CPU-bound work cannot
97
- * honour the signal; the freed pool slot then gets a fresh thread. Every listener (the
98
- * thread's `message` / `error` / `exit` and the signal's `abort`) is removed on settle,
99
- * and a `settled` guard prevents a double-settle.
92
+ * {@link NodeThread.death} — its death events fired before this dispatch existed and will
93
+ * never fire again, so waiting on the listeners below would dangle forever; the latch makes
94
+ * death total across every event ordering. If the thread `error`s / `exit`s mid-flight it is
95
+ * marked dead and the
96
+ * job rejects. An inbound `messageerror` also evicts and terminates the thread before
97
+ * rejection. On `execution.signal` abort it contains the cooperative `abort` post,
98
+ * evicts the thread, and observes `terminate()` settlement because CPU-bound work cannot
99
+ * honour the signal; the freed pool slot then gets a fresh thread. Every per-job listener
100
+ * (`message` / `messageerror` / `error` / `exit` / `abort`) is removed on settle.
100
101
  *
101
102
  * @typeParam TResult - The reply type the `result` guard narrows to
102
103
  * @param thread - The leased thread to run the job on
@@ -107,23 +108,6 @@ export declare function createNodeWorker<TInput, TResult>(options: NodeWorkerOpt
107
108
  */
108
109
  export declare function dispatch<TResult>(thread: NodeThread, input: unknown, execution: QueueExecution, result: Guard<TResult>): Promise<TResult>;
109
110
 
110
- /**
111
- * A runtime type predicate used to narrow a wire payload with no assertion.
112
- *
113
- * @remarks
114
- * Mirrors the core `Guard<T>` (a total `(value: unknown) => value is T` predicate,
115
- * AGENTS §14) but is re-declared here so the server workers surface is self-describing
116
- * and the worker-side `serve.ts` — which may not import from `@src/core` (it loads as
117
- * raw `.ts` inside a spawned thread) — shares the same vocabulary. A `Guard` NEVER
118
- * throws; adversarial input returns `false`. It is the zero-`as` bridge across the
119
- * structured-clone boundary: the main side narrows each reply value through the
120
- * `result` guard and the input through the `input` guard, so a generic `TInput` /
121
- * `TResult` is reconstructed by validation rather than asserted.
122
- *
123
- * @typeParam T - The type a value is narrowed to when the predicate holds
124
- */
125
- export declare type Guard<T> = (value: unknown) => value is T;
126
-
127
111
  /**
128
112
  * Narrow an inbound `message` to a {@link Reply} for a given job `id` — no assertion.
129
113
  *
@@ -142,16 +126,16 @@ export declare function isReply(value: unknown, id: string): value is Reply;
142
126
  * {@link createNodeWorker} leases per job.
143
127
  *
144
128
  * @remarks
145
- * `alive` starts `true` and flips to `false` the moment the thread `error`s, `exit`s, or
146
- * is evicted on abort; the pool's `validate` reads `alive && worker.threadId > 0`, so a
129
+ * `alive` starts `true` and flips to `false` when the thread `error`s, reports a
130
+ * `messageerror`, exits, or is evicted on abort; the pool's `validate` reads
131
+ * `alive && worker.threadId > 0`, so a
147
132
  * dead thread is destroyed and replaced rather than reused. `death` LATCHES the first
148
- * terminal event (`error`'s `Error`, or a synthesized one on `exit`) — the death-signal
133
+ * terminal event (`error` / `messageerror`, or a synthesized error on `exit`) — the death-signal
149
134
  * record a `dispatch` checks at entry, so a job dispatched AFTER the thread died (its
150
135
  * death events already fired and will never fire again) rejects immediately instead of
151
- * awaiting events that already happened. Under event-loop pressure Node delivers a dead
152
- * thread's `online` + `error` + `exit` in ONE synchronous exit-drain batch, starving the
153
- * microtask chain that attaches the dispatch listeners until after every death event —
154
- * the latch is what makes that ordering safe. `worker` is the underlying
136
+ * awaiting events that already happened. A thread can become terminal before the readiness
137
+ * promise continuation attaches dispatch listeners; the latch is what makes that ordering
138
+ * safe. `worker` is the underlying
155
139
  * `node:worker_threads` thread (its `postMessage` / `terminate` drive the protocol).
156
140
  */
157
141
  export declare interface NodeThread {
@@ -165,8 +149,9 @@ export declare interface NodeThread {
165
149
  *
166
150
  * @remarks
167
151
  * - `script` — the worker module each pooled thread runs; its module must call
168
- * `serveWorker(...)`. A `.ts` script requires Node 23.6 (native type-stripping); on
169
- * older Node point this at a built `.js` / `.mjs`.
152
+ * `serveWorker(...)`. Raw TypeScript is unflagged on Node 22.18+ and Node 23.6+;
153
+ * Node 22.12–22.17 and Node 23.0–23.5 require `--experimental-strip-types`. A built
154
+ * `.js` / `.mjs` script remains an alternative across supported Node versions.
170
155
  * - `input` — narrows the work payload BEFORE it crosses the structured-clone boundary
171
156
  * (fail-fast) and supplies the `TInput` inference, so call sites need no type argument.
172
157
  * - `result` — narrows every reply value coming back from a thread; an invalid reply
@@ -174,7 +159,8 @@ export declare interface NodeThread {
174
159
  * - `workerData` — opaque data cloned to every thread once at spawn (read there via
175
160
  * `serveWorker`'s host `workerData`); must be structured-cloneable.
176
161
  * - `concurrency` — the maximum jobs in flight at once; the thread pool's `max` matches
177
- * it, so at most this many threads exist. Defaults to `1`. Floored at `1`.
162
+ * it, so at most this many threads exist. Defaults to `1` and must be a positive safe
163
+ * integer, as validated by the underlying queue.
178
164
  * - `retries` — the default extra attempts per job on failure / timeout; defaults to `0`.
179
165
  * - `timeout` — the default per-attempt deadline in milliseconds; defaults to none.
180
166
  * - `store` — durable backing for outstanding jobs (survives a restart; `restore()`
@@ -202,11 +188,11 @@ export declare interface NodeWorkerOptions<TInput, TResult> {
202
188
  * @remarks
203
189
  * Internal plumbing rather than public call surface, but centralized here per AGENTS §5 (an
204
190
  * impl file holds only its class / functions). A reply is a discriminated union on `ok`: a
205
- * `true` carries any opaque `value` (narrowed at the boundary by the `result` {@link Guard},
191
+ * `true` carries any opaque `value` (narrowed at the boundary by the `result` guard,
206
192
  * with no `as`); a `false` carries a string `error`. The worker-side `serve.ts` cannot import
207
193
  * this (it loads as raw source in a spawned thread, AGENTS §5 exception) and posts the same
208
- * shape structurally. The `id` ties a reply to its job, so a stray / foreign-id message is
209
- * ignored.
194
+ * shape structurally. The `id` ties a reply to its job: id-less / foreign-id chatter is ignored,
195
+ * while a matching-id malformed envelope taints the thread and causes dispatch to terminate it.
210
196
  */
211
197
  export declare type Reply = {
212
198
  readonly id: string;
@@ -226,7 +212,10 @@ export declare type Reply = {
226
212
  * run/abort protocol: a `run` message narrows its `input` through `options.input` (an
227
213
  * invalid payload replies with an error envelope, never running the handler), then runs
228
214
  * `options.handler(input, { signal })` and replies `{ id, ok: true, value }` on success or
229
- * `{ id, ok: false, error }` on throw. Each in-flight job has its own `AbortController`,
215
+ * `{ id, ok: false, error }` on throw. Input-guard throws use the same failure envelope. If a
216
+ * success value cannot be cloned, the post is retried as a clone-safe failure; if that post also
217
+ * fails, the parent port closes so the main side observes thread exit instead of waiting forever.
218
+ * Each in-flight job has its own `AbortController`,
230
219
  * so an `abort` message for that id fires the handler's `signal` (cooperative — the main
231
220
  * side ALSO terminates the thread, so a handler that ignores its signal is still stopped).
232
221
  * Every inbound message is narrowed with the inlined guards — no `as`. On the main thread
@@ -239,7 +228,7 @@ export declare type Reply = {
239
228
  * @example
240
229
  * ```ts
241
230
  * // double.ts — a worker script
242
- * import { serveWorker } from '@src/server'
231
+ * import { serveWorker } from '@orkestrel/worker/server'
243
232
  *
244
233
  * serveWorker<number, number>({
245
234
  * input: (value): value is number => typeof value === 'number',
@@ -281,11 +270,11 @@ export declare interface ServeWorkerOptions<TInput, TResult> {
281
270
  * listeners that flip `alive` to `false` AND latch the first terminal event on
282
271
  * {@link NodeThread.death}: a crash is observable to an in-flight {@link dispatch} (via
283
272
  * its own listeners), to the pool's `validate` (via `alive`), and — crucially — to a
284
- * dispatch that attaches AFTER the death (via the latch). The latch closes a real race:
285
- * under event-loop pressure a dead thread's `online` + `error` + `exit` are delivered in
286
- * ONE synchronous exit-drain batch, so every death event fires before the microtask chain
287
- * resolving this spawn can hand the thread to `dispatch` without the latch that job
288
- * would await events that already fired, forever. The pool's `create` hook calls this.
273
+ * dispatch that attaches AFTER the death (via the latch). A `messageerror` is terminal too,
274
+ * so a thread whose inbound payload could not be deserialized is never reused. The latch
275
+ * closes a real race: a thread can become terminal before the readiness promise continuation
276
+ * hands it to `dispatch`, leaving no future death event for that dispatch to observe. Without
277
+ * the latch, that job would wait forever. The pool's `create` hook calls this.
289
278
  *
290
279
  * @param script - The worker module each thread runs (must call `serveWorker`)
291
280
  * @param workerData - Opaque, structured-cloneable data handed to the thread at spawn