@vielzeug/familiar 1.0.5 → 1.0.7

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.
package/dist/_dev.cjs CHANGED
@@ -1,2 +1,2 @@
1
- var e=!globalThis.__FAMILIAR_PROD__;function t(t){e&&console.warn(`[@vielzeug/familiar] ${t}`)}exports.warn=t;
1
+ function e(e){}exports.warn=e;
2
2
  //# sourceMappingURL=_dev.cjs.map
package/dist/_dev.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __FAMILIAR_PROD__?: boolean }).__FAMILIAR_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/familiar] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":"AAAA,IAAM,EAAQ,CAAE,WAA+C,kBAG/D,SAAgB,EAAK,EAAmB,CAClC,GAAO,QAAQ,KAAK,wBAAwB,GAAK,CACvD"}
1
+ {"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __FAMILIAR_PROD__?: boolean }).__FAMILIAR_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/familiar] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":"AAGA,SAAgB,EAAK,EAAmB,CAExC"}
package/dist/_dev.js CHANGED
@@ -1,9 +1,6 @@
1
1
  //#region src/_dev.ts
2
- var e = !globalThis.__FAMILIAR_PROD__;
3
- function t(t) {
4
- e && console.warn(`[@vielzeug/familiar] ${t}`);
5
- }
2
+ function e(e) {}
6
3
  //#endregion
7
- export { t as warn };
4
+ export { e as warn };
8
5
 
9
6
  //# sourceMappingURL=_dev.js.map
package/dist/_dev.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __FAMILIAR_PROD__?: boolean }).__FAMILIAR_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/familiar] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":";AAAA,IAAM,IAAQ,CAAE,WAA+C;AAG/D,SAAgB,EAAK,GAAmB;CACtC,AAAI,KAAO,QAAQ,KAAK,wBAAwB,GAAK;AACvD"}
1
+ {"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __FAMILIAR_PROD__?: boolean }).__FAMILIAR_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/familiar] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":";AAGA,SAAgB,EAAK,GAAmB,CAExC"}
package/dist/_pool.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./_queue.cjs"),n=require("./_timers.cjs");let r=require("@vielzeug/arsenal");function i(i,a){let{concurrency:o,defaultTimeout:s,maxQueue:c,onFull:l}=a,u=[...i],d=new t.TaskQueue,f=new AbortController,p=[],m=[],h=0,g,_=0,v=0,y=0,b=!1;function x(){return h===0&&d.size===0}function S(){if(!x()||p.length===0)return;let e=p.splice(0);for(let t of e)t()}function C(t){return x()?Promise.resolve():new Promise((r,i)=>{p.push(r),t!==void 0&&n.unrefTimer(setTimeout(()=>{let n=p.indexOf(r);n!==-1&&p.splice(n,1),i(new e.FamiliarTimeoutError(t))},t))})}function w(){let e=m.shift();e&&e()}let T=!1;function E(){if(!(b||T)){for(T=!0;!b&&u.length>0&&d.size>0;){let t=D();if(!t)break;let n=u.pop();t.cleanupAbort?.(),h+=1,w();let r=t.timeout??s;n.run(t.input,t.transferables,r).then(e=>{u.push(n),--h,_+=1,t.resolve(e),E(),x()&&S()},r=>{u.push(n),--h,r instanceof e.FamiliarTerminatedError||(v+=1),t.reject(r),E(),x()&&S()})}T=!1}}function D(){for(;d.size>0;){let e=d.shift();if(!e)break;if(e.signal?.aborted){e.cleanupAbort?.(),e.reject((0,r.abortError)(e.signal));continue}return e}S()}function O(){if(b)return;b=!0,f.abort();for(let e of i)e.terminate();for(;d.size>0;){let t=d.shift();if(!t)break;t.cleanupAbort?.(),t.reject(new e.FamiliarTerminatedError)}let t=p.splice(0);for(let e of t)e();for(let e of m.splice(0))e()}function k(e){return b?Promise.resolve():g||(g=C(e).then(O,e=>{throw O(),e}),g)}async function A(t,n={}){let{priority:i=0,signal:a,timeout:o,transferables:s=[]}=n;if(b)throw new e.FamiliarTerminatedError;if(g)throw new e.FamiliarTerminatedError(`Worker is draining`);if(a?.aborted)throw(0,r.abortError)(a);if(l===`wait`&&c!==void 0){for(;!b&&!g&&d.size>=c;)await new Promise(e=>m.push(e));if(b)throw new e.FamiliarTerminatedError;if(g)throw new e.FamiliarTerminatedError(`Worker is draining`)}let u,f,p=new Promise((e,t)=>{u=e,f=t}),h={input:t,priority:i,reject:f,resolve:u,signal:a,timeout:o,transferables:s};if(!d.enqueue(h,l===`wait`?void 0:c))throw new e.FamiliarQueueFullError(c);if(a){let e=()=>{d.remove(h)&&(h.cleanupAbort?.(),f((0,r.abortError)(a)),S())};h.cleanupAbort=()=>{a.removeEventListener(`abort`,e),h.cleanupAbort=void 0},a.addEventListener(`abort`,e,{once:!0})}return E(),p}function j(t,n={}){if(b)throw new e.FamiliarRuntimeError(`Worker was terminated`);let r=u.pop();if(!r)throw new e.FamiliarRuntimeError(`runStream() requires a free worker slot; all ${i.length} slot${i.length===1?``:`s`} are busy`);let{timeout:a,transferables:o=[]}=n,s=r.runStream(t,o,a);return{[Symbol.asyncIterator](){let e=s[Symbol.asyncIterator](),t=!1,n=()=>{t||(t=!0,u.push(r))};return{async next(){let t=await e.next();return t.done&&n(),t},async return(t){return r.cancel(),n(),e.return?.(t)??{done:!0,value:t}},async throw(t){if(r.cancel(),n(),e.throw)return e.throw(t);throw t}}}}}async function*M(e,t={}){if(e.length===0)return;let{ordered:n=!0,...r}=t,i=new AbortController;try{if(n){let t=e.map(e=>A(e,{...r,signal:i.signal}));for(let e of t)yield await e}else{let t=[],n=null,s=0,c=Math.max(1,o);function a(){if(s>=e.length)return;let a=e[s++];A(a,{...r,signal:i.signal}).then(e=>{t.push({value:e}),n?.(),n=null},e=>{t.push({error:e}),n?.(),n=null})}for(let t=0;t<c&&t<e.length;t++)a();for(let r=0;r<e.length;r++){for(;t.length===0;)await new Promise(e=>{n=e});let e=t.shift();if(`error`in e)throw e.error;yield e.value,a()}}}finally{i.abort()}}function N(e,t={}){let n=new AbortController;if(t.signal){let e=t.signal;e.aborted?n.abort(e.reason):e.addEventListener(`abort`,()=>n.abort(e.reason),{once:!0})}y+=1;let r=0,i=0,a=!1,o=[];function s(){!a&&r>0&&r===i&&(a=!0,--y)}return{abort(e){n.abort(e)},drain(){let e=o.splice(0);return a||(a=!0,--y),Promise.allSettled(e)},get name(){return e},get pending(){return r-i},run(e,t={}){r+=1;let a=A(e,{...t,signal:n.signal});return o.push(a),a.then(()=>{i+=1,s()},()=>{i+=1,s()}),a},get size(){return r}}}function P(){return b?`terminated`:h>0||d.size>0?`running`:`idle`}return{get active(){return h},batch:M,get completed(){return _},get concurrency(){return o},get disposalSignal(){return f.signal},dispose:O,get disposed(){return b},drain:k,get failed(){return v},group:N,get groupCount(){return y},prime(){return Promise.all(i.map(e=>e.prime())).then(()=>{})},get queued(){return d.size},run:A,runStream:j,get status(){return P()},[Symbol.asyncDispose]:()=>k(),[Symbol.dispose]:O}}exports.createPool=i;
1
+ const e=require("./errors.cjs"),t=require("./_queue.cjs"),n=require("./_timers.cjs");let r=require("@vielzeug/arsenal/async");function i(i,a){let{concurrency:o,defaultTimeout:s,maxQueue:c,onFull:l}=a,u=[...i],d=new t.TaskQueue,f=new AbortController,p=[],m=[],h=0,g,_=0,v=0,y=0,b=!1;function x(){return h===0&&d.size===0}function S(){if(!x()||p.length===0)return;let e=p.splice(0);for(let t of e)t()}function C(t){return x()?Promise.resolve():new Promise((r,i)=>{if(p.push(r),t!==void 0){let a=setTimeout(()=>{let n=p.indexOf(r);n!==-1&&p.splice(n,1),i(new e.FamiliarTimeoutError(t))},t);n.unrefTimer(a)}})}function w(){let e=m.shift();e&&e()}let T=!1;function E(){if(!(b||T)){for(T=!0;!b&&u.length>0&&d.size>0;){let t=D();if(!t)break;let n=u.pop();t.cleanupAbort?.(),h+=1,w();let r=t.timeout??s;n.run(t.input,t.transferables,r).then(e=>{u.push(n),--h,_+=1,t.resolve(e),E(),x()&&S()},r=>{u.push(n),--h,r instanceof e.FamiliarTerminatedError||(v+=1),t.reject(r),E(),x()&&S()})}T=!1}}function D(){for(;d.size>0;){let e=d.shift();if(!e)break;if(e.signal?.aborted){e.cleanupAbort?.(),e.reject((0,r.abortError)(e.signal));continue}return e}S()}function O(){if(b)return;b=!0,f.abort();for(let e of i)e.terminate();for(;d.size>0;){let t=d.shift();if(!t)break;t.cleanupAbort?.(),t.reject(new e.FamiliarTerminatedError)}let t=p.splice(0);for(let e of t)e();for(let e of m.splice(0))e()}function k(e){return b?Promise.resolve():g||(g=C(e).then(O,e=>{throw O(),e}),g)}async function A(t,n={}){let{priority:i=0,signal:a,timeout:o,transferables:s=[]}=n;if(b)throw new e.FamiliarTerminatedError;if(g)throw new e.FamiliarTerminatedError(`Worker is draining`);if(a?.aborted)throw(0,r.abortError)(a);if(l===`wait`&&c!==void 0){for(;!b&&!g&&d.size>=c;)await new Promise(e=>m.push(e));if(b)throw new e.FamiliarTerminatedError;if(g)throw new e.FamiliarTerminatedError(`Worker is draining`)}let u,f,p=new Promise((e,t)=>{u=e,f=t}),h={input:t,priority:i,reject:f,resolve:u,signal:a,timeout:o,transferables:s};if(!d.enqueue(h,l===`wait`?void 0:c))throw new e.FamiliarQueueFullError(c);if(a){let e=()=>{d.remove(h)&&(h.cleanupAbort?.(),f((0,r.abortError)(a)),S())};h.cleanupAbort=()=>{a.removeEventListener(`abort`,e),h.cleanupAbort=void 0},a.addEventListener(`abort`,e,{once:!0})}return E(),p}function j(t,n={}){if(b)throw new e.FamiliarRuntimeError(`Worker was terminated`);let r=u.pop();if(!r)throw new e.FamiliarRuntimeError(`runStream() requires a free worker slot; all ${i.length} slot${i.length===1?``:`s`} are busy`);let{timeout:a,transferables:o=[]}=n,s=r.runStream(t,o,a);return{[Symbol.asyncIterator](){let e=s[Symbol.asyncIterator](),t=!1,n=()=>{t||(t=!0,u.push(r))};return{async next(){let t=await e.next();return t.done&&n(),t},async return(t){return r.cancel(),n(),e.return?.(t)??{done:!0,value:t}},async throw(t){if(r.cancel(),n(),e.throw)return e.throw(t);throw t}}}}}async function*M(e,t={}){if(e.length===0)return;let{ordered:n=!0,...r}=t,i=new AbortController;try{if(n){let t=e.map(e=>A(e,{...r,signal:i.signal}));for(let e of t)yield await e}else{let t=[],n=null,s=0,c=Math.max(1,o);function a(){if(s>=e.length)return;let a=e[s++];A(a,{...r,signal:i.signal}).then(e=>{t.push({value:e}),n?.(),n=null},e=>{t.push({error:e}),n?.(),n=null})}for(let t=0;t<c&&t<e.length;t++)a();for(let r=0;r<e.length;r++){for(;t.length===0;)await new Promise(e=>{n=e});let e=t.shift();if(`error`in e)throw e.error;yield e.value,a()}}}finally{i.abort()}}function N(e,t={}){let n=new AbortController;if(t.signal){let e=t.signal;e.aborted?n.abort(e.reason):e.addEventListener(`abort`,()=>n.abort(e.reason),{once:!0})}y+=1;let r=0,i=0,a=!1,o=[];function s(){!a&&r>0&&r===i&&(a=!0,--y)}return{abort(e){n.abort(e)},drain(){let e=o.splice(0);return a||(a=!0,--y),Promise.allSettled(e)},get name(){return e},get pending(){return r-i},run(e,t={}){r+=1;let a=A(e,{...t,signal:n.signal});return o.push(a),a.then(()=>{i+=1,s()},()=>{i+=1,s()}),a},get size(){return r}}}function P(){return b?`terminated`:h>0||d.size>0?`running`:`idle`}return{get active(){return h},batch:M,get completed(){return _},get concurrency(){return o},get disposalSignal(){return f.signal},dispose:O,get disposed(){return b},drain:k,get failed(){return v},group:N,get groupCount(){return y},prime(){return Promise.all(i.map(e=>e.prime())).then(()=>{})},get queued(){return d.size},run:A,runStream:j,get status(){return P()},[Symbol.asyncDispose]:()=>k(),[Symbol.dispose]:O}}exports.createPool=i;
2
2
  //# sourceMappingURL=_pool.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_pool.cjs","names":[],"sources":["../src/_pool.ts"],"sourcesContent":["/**\n * Shared pool orchestration engine used by both the real worker implementation and the test\n * double. Handles queue management, iterative drain loop, abort handling, lifecycle (drain /\n * dispose), metrics, batch streaming, task groups, and backpressure.\n *\n * Callers provide a SlotStrategy array — each slot encapsulates run/runStream/prime/terminate.\n * The pool does not care whether slots use a Web Worker or run in-process.\n *\n * Not part of the public API surface.\n */\n\nimport { abortError } from '@vielzeug/arsenal';\n\nimport type {\n BatchOptions,\n GroupOptions,\n RunOptions,\n SlotStrategy,\n TaskGroup,\n WorkerHandle,\n WorkerStatus,\n} from './types';\n\nimport { type QueueItem, TaskQueue } from './_queue';\nimport { unrefTimer } from './_timers';\nimport { FamiliarQueueFullError, FamiliarRuntimeError, FamiliarTerminatedError, FamiliarTimeoutError } from './errors';\n\nexport type PoolOptions = {\n concurrency: number;\n defaultTimeout: number | undefined;\n maxQueue: number | undefined;\n onFull: 'reject' | 'wait';\n};\n\nexport function createPool<TInput, TOutput>(\n slots: SlotStrategy<TInput, TOutput>[],\n options: PoolOptions,\n): WorkerHandle<TInput, TOutput> {\n const { concurrency, defaultTimeout, maxQueue, onFull } = options;\n const freeSlots = [...slots];\n const queue = new TaskQueue<TInput, TOutput>();\n const disposeController = new AbortController();\n const idleResolvers: Array<() => void> = [];\n /** Waiters blocked on run() because the queue is full (onFull='wait' mode). */\n const fullWaiters: Array<() => void> = [];\n\n let activeCount = 0;\n let drainPromise: Promise<void> | undefined;\n let completedCount = 0;\n let failedCount = 0;\n let groupActiveCount = 0;\n let terminated = false;\n\n // ─── Idle tracking ────────────────────────────────────────────────────────────\n\n function isIdle(): boolean {\n return activeCount === 0 && queue.size === 0;\n }\n\n function notifyIdle(): void {\n if (!isIdle() || idleResolvers.length === 0) return;\n\n const resolvers = idleResolvers.splice(0);\n\n for (const resolve of resolvers) resolve();\n }\n\n function waitForIdle(timeoutMs?: number): Promise<void> {\n if (isIdle()) return Promise.resolve();\n\n return new Promise<void>((resolve, reject) => {\n idleResolvers.push(resolve);\n\n if (timeoutMs !== undefined) {\n const timer = setTimeout(() => {\n const idx = idleResolvers.indexOf(resolve);\n\n if (idx !== -1) idleResolvers.splice(idx, 1);\n\n reject(new FamiliarTimeoutError(timeoutMs));\n }, timeoutMs);\n\n unrefTimer(timer);\n }\n });\n }\n\n // ─── Full-queue backpressure (onFull='wait') ──────────────────────────────────\n\n function releaseOneFullWaiter(): void {\n const waiter = fullWaiters.shift();\n\n if (waiter) waiter();\n }\n\n // ─── Drain loop (iterative) ───────────────────────────────────────────────────\n\n let draining = false;\n\n function drainLoop(): void {\n if (terminated || draining) return;\n\n draining = true;\n\n while (!terminated && freeSlots.length > 0 && queue.size > 0) {\n const item = nextItem();\n\n if (!item) break;\n\n const slot = freeSlots.pop()!;\n\n item.cleanupAbort?.();\n activeCount += 1;\n releaseOneFullWaiter();\n\n const taskTimeout = item.timeout ?? defaultTimeout;\n\n slot.run(item.input, item.transferables, taskTimeout).then(\n (result) => {\n freeSlots.push(slot);\n activeCount -= 1;\n completedCount += 1;\n item.resolve(result);\n drainLoop();\n\n if (isIdle()) notifyIdle();\n },\n (error: unknown) => {\n freeSlots.push(slot);\n activeCount -= 1;\n\n if (!(error instanceof FamiliarTerminatedError)) failedCount += 1;\n\n item.reject(error);\n drainLoop();\n\n if (isIdle()) notifyIdle();\n },\n );\n }\n\n draining = false;\n }\n\n // ─── Queue helpers ────────────────────────────────────────────────────────────\n\n function nextItem(): QueueItem<TInput, TOutput> | undefined {\n while (queue.size > 0) {\n const item = queue.shift();\n\n if (!item) break;\n\n if (item.signal?.aborted) {\n item.cleanupAbort?.();\n item.reject(abortError(item.signal));\n continue;\n }\n\n return item;\n }\n\n // All remaining items were aborted and rejected above — the pool may now be idle.\n // Notify any drain() waiter so it does not hang.\n notifyIdle();\n\n return undefined;\n }\n\n // ─── Lifecycle ────────────────────────────────────────────────────────────────\n\n function dispose(): void {\n if (terminated) return;\n\n terminated = true;\n disposeController.abort();\n\n for (const slot of slots) slot.terminate();\n\n while (queue.size > 0) {\n const item = queue.shift();\n\n if (!item) break;\n\n item.cleanupAbort?.();\n item.reject(new FamiliarTerminatedError());\n }\n\n const resolvers = idleResolvers.splice(0);\n\n for (const resolve of resolvers) resolve();\n\n for (const waiter of fullWaiters.splice(0)) waiter();\n }\n\n function drain(timeoutMs?: number): Promise<void> {\n if (terminated) return Promise.resolve();\n\n if (drainPromise) return drainPromise;\n\n drainPromise = waitForIdle(timeoutMs).then(dispose, (err) => {\n dispose();\n throw err;\n });\n\n return drainPromise;\n }\n\n // ─── run() ───────────────────────────────────────────────────────────────────\n\n async function run(input: TInput, runOptions: RunOptions = {}): Promise<TOutput> {\n const { priority = 0, signal, timeout, transferables = [] } = runOptions;\n\n if (terminated) {\n throw new FamiliarTerminatedError();\n }\n\n if (drainPromise) {\n throw new FamiliarTerminatedError('Worker is draining');\n }\n\n if (signal?.aborted) {\n throw abortError(signal);\n }\n\n if (onFull === 'wait' && maxQueue !== undefined) {\n while (!terminated && !drainPromise && queue.size >= maxQueue) {\n await new Promise<void>((resolve) => fullWaiters.push(resolve));\n }\n\n if (terminated) throw new FamiliarTerminatedError();\n\n if (drainPromise) throw new FamiliarTerminatedError('Worker is draining');\n }\n\n let resolve!: (value: TOutput) => void;\n let reject!: (reason: unknown) => void;\n\n const promise = new Promise<TOutput>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n const item: QueueItem<TInput, TOutput> = {\n input,\n priority,\n reject,\n resolve,\n signal,\n timeout,\n transferables,\n };\n\n if (!queue.enqueue(item, onFull === 'wait' ? undefined : maxQueue)) {\n // maxQueue is guaranteed defined here: enqueue() only returns false when maxQueue is set.\n throw new FamiliarQueueFullError(maxQueue as number);\n }\n\n if (signal) {\n const onAbort = () => {\n if (!queue.remove(item)) return;\n\n item.cleanupAbort?.();\n reject(abortError(signal));\n notifyIdle();\n };\n\n item.cleanupAbort = () => {\n signal.removeEventListener('abort', onAbort);\n item.cleanupAbort = undefined;\n };\n\n signal.addEventListener('abort', onAbort, { once: true });\n }\n\n drainLoop();\n\n return promise;\n }\n\n // ─── runStream() ─────────────────────────────────────────────────────────────\n\n function runStream(input: TInput, options: Omit<RunOptions, 'signal'> = {}): AsyncIterable<TOutput> {\n if (terminated) {\n throw new FamiliarRuntimeError('Worker was terminated');\n }\n\n const slot = freeSlots.pop();\n\n if (!slot) {\n throw new FamiliarRuntimeError(\n `runStream() requires a free worker slot; all ${slots.length} slot${slots.length === 1 ? '' : 's'} are busy`,\n );\n }\n\n const { timeout, transferables = [] } = options;\n const iter = slot.runStream(input, transferables, timeout);\n\n return {\n [Symbol.asyncIterator]() {\n const inner = iter[Symbol.asyncIterator]();\n let released = false;\n\n const releaseSlot = () => {\n if (!released) {\n released = true;\n freeSlots.push(slot);\n }\n };\n\n return {\n async next() {\n const result = await inner.next();\n\n if (result.done) releaseSlot();\n\n return result;\n },\n\n async return(value?: unknown) {\n slot.cancel();\n releaseSlot();\n\n return inner.return?.(value) ?? { done: true as const, value };\n },\n\n async throw(error?: unknown) {\n slot.cancel();\n releaseSlot();\n\n if (inner.throw) return inner.throw(error);\n\n throw error;\n },\n };\n },\n };\n }\n\n // ─── batch() ─────────────────────────────────────────────────────────────────\n\n async function* batch(inputs: TInput[], batchOptions: BatchOptions = {}): AsyncIterable<TOutput> {\n if (inputs.length === 0) return;\n\n const { ordered = true, ...runOpts } = batchOptions;\n const ac = new AbortController();\n\n try {\n if (ordered) {\n const promises = inputs.map((input) => run(input, { ...runOpts, signal: ac.signal }));\n\n for (const p of promises) {\n yield await p;\n }\n } else {\n // As-completed: yield results in the order tasks finish, not submission order.\n // A single-slot notification channel wakes the consumer when the next result is ready.\n // Submission is windowed to `concurrency` outstanding (in-flight or buffered-but-unread)\n // tasks at a time — releasing capacity for one more submission each time the consumer\n // reads a result — so a slow consumer can't let an entire large batch settle in memory.\n type Completion = { error: unknown } | { value: TOutput };\n\n const completions: Completion[] = [];\n let notifier: (() => void) | null = null;\n let nextIndex = 0;\n const windowSize = Math.max(1, concurrency);\n\n function submitNext(): void {\n if (nextIndex >= inputs.length) return;\n\n const input = inputs[nextIndex++]!;\n\n run(input, { ...runOpts, signal: ac.signal }).then(\n (value) => {\n completions.push({ value });\n notifier?.();\n notifier = null;\n },\n (error: unknown) => {\n completions.push({ error });\n notifier?.();\n notifier = null;\n },\n );\n }\n\n for (let i = 0; i < windowSize && i < inputs.length; i++) submitNext();\n\n for (let i = 0; i < inputs.length; i++) {\n while (completions.length === 0) {\n await new Promise<void>((resolve) => {\n notifier = resolve;\n });\n }\n\n const next = completions.shift()!;\n\n if ('error' in next) throw next.error;\n\n yield next.value;\n\n // Only release capacity for another submission once the consumer has actually resumed\n // past this yield (i.e. did not break/return early) — submitting here unconditionally\n // would over-submit by one task on every early exit.\n submitNext();\n }\n }\n } finally {\n // Abort remaining in-flight tasks on any exit path (normal, consumer break, or error).\n ac.abort();\n }\n }\n\n // ─── group() ─────────────────────────────────────────────────────────────────\n\n function group(name?: string, options: GroupOptions = {}): TaskGroup<TInput, TOutput> {\n const ac = new AbortController();\n\n if (options.signal) {\n const sig = options.signal;\n\n if (sig.aborted) {\n ac.abort(sig.reason);\n } else {\n sig.addEventListener('abort', () => ac.abort(sig.reason), { once: true });\n }\n }\n\n groupActiveCount += 1;\n\n let submittedCount = 0;\n let settledCount = 0;\n let groupClosed = false;\n const pendingPromises: Promise<TOutput>[] = [];\n\n function decrementGroupIfDone(): void {\n if (!groupClosed && submittedCount > 0 && submittedCount === settledCount) {\n groupClosed = true;\n groupActiveCount -= 1;\n }\n }\n\n return {\n abort(reason?: unknown): void {\n ac.abort(reason);\n },\n\n drain(): Promise<PromiseSettledResult<TOutput>[]> {\n const snapshot = pendingPromises.splice(0);\n\n if (!groupClosed) {\n groupClosed = true;\n groupActiveCount -= 1;\n }\n\n return Promise.allSettled(snapshot);\n },\n\n get name() {\n return name;\n },\n\n get pending() {\n return submittedCount - settledCount;\n },\n\n run(input: TInput, runOpts: Omit<RunOptions, 'signal'> = {}): Promise<TOutput> {\n submittedCount += 1;\n\n const p = run(input, { ...runOpts, signal: ac.signal });\n\n pendingPromises.push(p);\n p.then(\n () => {\n settledCount += 1;\n decrementGroupIfDone();\n },\n () => {\n settledCount += 1;\n decrementGroupIfDone();\n },\n );\n\n return p;\n },\n\n get size() {\n return submittedCount;\n },\n };\n }\n\n // ─── Status ──────────────────────────────────────────────────────────────────\n\n function getStatus(): WorkerStatus {\n if (terminated) return 'terminated';\n\n return activeCount > 0 || queue.size > 0 ? 'running' : 'idle';\n }\n\n return {\n get active(): number {\n return activeCount;\n },\n batch,\n get completed(): number {\n return completedCount;\n },\n get concurrency(): number {\n return concurrency;\n },\n get disposalSignal(): AbortSignal {\n return disposeController.signal;\n },\n dispose,\n get disposed(): boolean {\n return terminated;\n },\n drain,\n get failed(): number {\n return failedCount;\n },\n group,\n get groupCount(): number {\n return groupActiveCount;\n },\n prime(): Promise<void> {\n return Promise.all(slots.map((s) => s.prime())).then(() => {});\n },\n get queued(): number {\n return queue.size;\n },\n run,\n runStream,\n get status(): WorkerStatus {\n return getStatus();\n },\n [Symbol.asyncDispose]: () => drain(),\n [Symbol.dispose]: dispose,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"_pool.cjs","names":[],"sources":["../src/_pool.ts"],"sourcesContent":["/**\n * Shared pool orchestration engine used by both the real worker implementation and the test\n * double. Handles queue management, iterative drain loop, abort handling, lifecycle (drain /\n * dispose), metrics, batch streaming, task groups, and backpressure.\n *\n * Callers provide a SlotStrategy array — each slot encapsulates run/runStream/prime/terminate.\n * The pool does not care whether slots use a Web Worker or run in-process.\n *\n * Not part of the public API surface.\n */\n\nimport { abortError } from '@vielzeug/arsenal/async';\n\nimport type {\n BatchOptions,\n GroupOptions,\n RunOptions,\n SlotStrategy,\n TaskGroup,\n WorkerHandle,\n WorkerStatus,\n} from './types';\n\nimport { type QueueItem, TaskQueue } from './_queue';\nimport { unrefTimer } from './_timers';\nimport { FamiliarQueueFullError, FamiliarRuntimeError, FamiliarTerminatedError, FamiliarTimeoutError } from './errors';\n\nexport type PoolOptions = {\n concurrency: number;\n defaultTimeout: number | undefined;\n maxQueue: number | undefined;\n onFull: 'reject' | 'wait';\n};\n\nexport function createPool<TInput, TOutput>(\n slots: SlotStrategy<TInput, TOutput>[],\n options: PoolOptions,\n): WorkerHandle<TInput, TOutput> {\n const { concurrency, defaultTimeout, maxQueue, onFull } = options;\n const freeSlots = [...slots];\n const queue = new TaskQueue<TInput, TOutput>();\n const disposeController = new AbortController();\n const idleResolvers: Array<() => void> = [];\n /** Waiters blocked on run() because the queue is full (onFull='wait' mode). */\n const fullWaiters: Array<() => void> = [];\n\n let activeCount = 0;\n let drainPromise: Promise<void> | undefined;\n let completedCount = 0;\n let failedCount = 0;\n let groupActiveCount = 0;\n let terminated = false;\n\n // ─── Idle tracking ────────────────────────────────────────────────────────────\n\n function isIdle(): boolean {\n return activeCount === 0 && queue.size === 0;\n }\n\n function notifyIdle(): void {\n if (!isIdle() || idleResolvers.length === 0) return;\n\n const resolvers = idleResolvers.splice(0);\n\n for (const resolve of resolvers) resolve();\n }\n\n function waitForIdle(timeoutMs?: number): Promise<void> {\n if (isIdle()) return Promise.resolve();\n\n return new Promise<void>((resolve, reject) => {\n idleResolvers.push(resolve);\n\n if (timeoutMs !== undefined) {\n const timer = setTimeout(() => {\n const idx = idleResolvers.indexOf(resolve);\n\n if (idx !== -1) idleResolvers.splice(idx, 1);\n\n reject(new FamiliarTimeoutError(timeoutMs));\n }, timeoutMs);\n\n unrefTimer(timer);\n }\n });\n }\n\n // ─── Full-queue backpressure (onFull='wait') ──────────────────────────────────\n\n function releaseOneFullWaiter(): void {\n const waiter = fullWaiters.shift();\n\n if (waiter) waiter();\n }\n\n // ─── Drain loop (iterative) ───────────────────────────────────────────────────\n\n let draining = false;\n\n function drainLoop(): void {\n if (terminated || draining) return;\n\n draining = true;\n\n while (!terminated && freeSlots.length > 0 && queue.size > 0) {\n const item = nextItem();\n\n if (!item) break;\n\n const slot = freeSlots.pop()!;\n\n item.cleanupAbort?.();\n activeCount += 1;\n releaseOneFullWaiter();\n\n const taskTimeout = item.timeout ?? defaultTimeout;\n\n slot.run(item.input, item.transferables, taskTimeout).then(\n (result) => {\n freeSlots.push(slot);\n activeCount -= 1;\n completedCount += 1;\n item.resolve(result);\n drainLoop();\n\n if (isIdle()) notifyIdle();\n },\n (error: unknown) => {\n freeSlots.push(slot);\n activeCount -= 1;\n\n if (!(error instanceof FamiliarTerminatedError)) failedCount += 1;\n\n item.reject(error);\n drainLoop();\n\n if (isIdle()) notifyIdle();\n },\n );\n }\n\n draining = false;\n }\n\n // ─── Queue helpers ────────────────────────────────────────────────────────────\n\n function nextItem(): QueueItem<TInput, TOutput> | undefined {\n while (queue.size > 0) {\n const item = queue.shift();\n\n if (!item) break;\n\n if (item.signal?.aborted) {\n item.cleanupAbort?.();\n item.reject(abortError(item.signal));\n continue;\n }\n\n return item;\n }\n\n // All remaining items were aborted and rejected above — the pool may now be idle.\n // Notify any drain() waiter so it does not hang.\n notifyIdle();\n\n return undefined;\n }\n\n // ─── Lifecycle ────────────────────────────────────────────────────────────────\n\n function dispose(): void {\n if (terminated) return;\n\n terminated = true;\n disposeController.abort();\n\n for (const slot of slots) slot.terminate();\n\n while (queue.size > 0) {\n const item = queue.shift();\n\n if (!item) break;\n\n item.cleanupAbort?.();\n item.reject(new FamiliarTerminatedError());\n }\n\n const resolvers = idleResolvers.splice(0);\n\n for (const resolve of resolvers) resolve();\n\n for (const waiter of fullWaiters.splice(0)) waiter();\n }\n\n function drain(timeoutMs?: number): Promise<void> {\n if (terminated) return Promise.resolve();\n\n if (drainPromise) return drainPromise;\n\n drainPromise = waitForIdle(timeoutMs).then(dispose, (err) => {\n dispose();\n throw err;\n });\n\n return drainPromise;\n }\n\n // ─── run() ───────────────────────────────────────────────────────────────────\n\n async function run(input: TInput, runOptions: RunOptions = {}): Promise<TOutput> {\n const { priority = 0, signal, timeout, transferables = [] } = runOptions;\n\n if (terminated) {\n throw new FamiliarTerminatedError();\n }\n\n if (drainPromise) {\n throw new FamiliarTerminatedError('Worker is draining');\n }\n\n if (signal?.aborted) {\n throw abortError(signal);\n }\n\n if (onFull === 'wait' && maxQueue !== undefined) {\n while (!terminated && !drainPromise && queue.size >= maxQueue) {\n await new Promise<void>((resolve) => fullWaiters.push(resolve));\n }\n\n if (terminated) throw new FamiliarTerminatedError();\n\n if (drainPromise) throw new FamiliarTerminatedError('Worker is draining');\n }\n\n let resolve!: (value: TOutput) => void;\n let reject!: (reason: unknown) => void;\n\n const promise = new Promise<TOutput>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n const item: QueueItem<TInput, TOutput> = {\n input,\n priority,\n reject,\n resolve,\n signal,\n timeout,\n transferables,\n };\n\n if (!queue.enqueue(item, onFull === 'wait' ? undefined : maxQueue)) {\n // maxQueue is guaranteed defined here: enqueue() only returns false when maxQueue is set.\n throw new FamiliarQueueFullError(maxQueue as number);\n }\n\n if (signal) {\n const onAbort = () => {\n if (!queue.remove(item)) return;\n\n item.cleanupAbort?.();\n reject(abortError(signal));\n notifyIdle();\n };\n\n item.cleanupAbort = () => {\n signal.removeEventListener('abort', onAbort);\n item.cleanupAbort = undefined;\n };\n\n signal.addEventListener('abort', onAbort, { once: true });\n }\n\n drainLoop();\n\n return promise;\n }\n\n // ─── runStream() ─────────────────────────────────────────────────────────────\n\n function runStream(input: TInput, options: Omit<RunOptions, 'signal'> = {}): AsyncIterable<TOutput> {\n if (terminated) {\n throw new FamiliarRuntimeError('Worker was terminated');\n }\n\n const slot = freeSlots.pop();\n\n if (!slot) {\n throw new FamiliarRuntimeError(\n `runStream() requires a free worker slot; all ${slots.length} slot${slots.length === 1 ? '' : 's'} are busy`,\n );\n }\n\n const { timeout, transferables = [] } = options;\n const iter = slot.runStream(input, transferables, timeout);\n\n return {\n [Symbol.asyncIterator]() {\n const inner = iter[Symbol.asyncIterator]();\n let released = false;\n\n const releaseSlot = () => {\n if (!released) {\n released = true;\n freeSlots.push(slot);\n }\n };\n\n return {\n async next() {\n const result = await inner.next();\n\n if (result.done) releaseSlot();\n\n return result;\n },\n\n async return(value?: unknown) {\n slot.cancel();\n releaseSlot();\n\n return inner.return?.(value) ?? { done: true as const, value };\n },\n\n async throw(error?: unknown) {\n slot.cancel();\n releaseSlot();\n\n if (inner.throw) return inner.throw(error);\n\n throw error;\n },\n };\n },\n };\n }\n\n // ─── batch() ─────────────────────────────────────────────────────────────────\n\n async function* batch(inputs: TInput[], batchOptions: BatchOptions = {}): AsyncIterable<TOutput> {\n if (inputs.length === 0) return;\n\n const { ordered = true, ...runOpts } = batchOptions;\n const ac = new AbortController();\n\n try {\n if (ordered) {\n const promises = inputs.map((input) => run(input, { ...runOpts, signal: ac.signal }));\n\n for (const p of promises) {\n yield await p;\n }\n } else {\n // As-completed: yield results in the order tasks finish, not submission order.\n // A single-slot notification channel wakes the consumer when the next result is ready.\n // Submission is windowed to `concurrency` outstanding (in-flight or buffered-but-unread)\n // tasks at a time — releasing capacity for one more submission each time the consumer\n // reads a result — so a slow consumer can't let an entire large batch settle in memory.\n type Completion = { error: unknown } | { value: TOutput };\n\n const completions: Completion[] = [];\n let notifier: (() => void) | null = null;\n let nextIndex = 0;\n const windowSize = Math.max(1, concurrency);\n\n function submitNext(): void {\n if (nextIndex >= inputs.length) return;\n\n const input = inputs[nextIndex++]!;\n\n run(input, { ...runOpts, signal: ac.signal }).then(\n (value) => {\n completions.push({ value });\n notifier?.();\n notifier = null;\n },\n (error: unknown) => {\n completions.push({ error });\n notifier?.();\n notifier = null;\n },\n );\n }\n\n for (let i = 0; i < windowSize && i < inputs.length; i++) submitNext();\n\n for (let i = 0; i < inputs.length; i++) {\n while (completions.length === 0) {\n await new Promise<void>((resolve) => {\n notifier = resolve;\n });\n }\n\n const next = completions.shift()!;\n\n if ('error' in next) throw next.error;\n\n yield next.value;\n\n // Only release capacity for another submission once the consumer has actually resumed\n // past this yield (i.e. did not break/return early) — submitting here unconditionally\n // would over-submit by one task on every early exit.\n submitNext();\n }\n }\n } finally {\n // Abort remaining in-flight tasks on any exit path (normal, consumer break, or error).\n ac.abort();\n }\n }\n\n // ─── group() ─────────────────────────────────────────────────────────────────\n\n function group(name?: string, options: GroupOptions = {}): TaskGroup<TInput, TOutput> {\n const ac = new AbortController();\n\n if (options.signal) {\n const sig = options.signal;\n\n if (sig.aborted) {\n ac.abort(sig.reason);\n } else {\n sig.addEventListener('abort', () => ac.abort(sig.reason), { once: true });\n }\n }\n\n groupActiveCount += 1;\n\n let submittedCount = 0;\n let settledCount = 0;\n let groupClosed = false;\n const pendingPromises: Promise<TOutput>[] = [];\n\n function decrementGroupIfDone(): void {\n if (!groupClosed && submittedCount > 0 && submittedCount === settledCount) {\n groupClosed = true;\n groupActiveCount -= 1;\n }\n }\n\n return {\n abort(reason?: unknown): void {\n ac.abort(reason);\n },\n\n drain(): Promise<PromiseSettledResult<TOutput>[]> {\n const snapshot = pendingPromises.splice(0);\n\n if (!groupClosed) {\n groupClosed = true;\n groupActiveCount -= 1;\n }\n\n return Promise.allSettled(snapshot);\n },\n\n get name() {\n return name;\n },\n\n get pending() {\n return submittedCount - settledCount;\n },\n\n run(input: TInput, runOpts: Omit<RunOptions, 'signal'> = {}): Promise<TOutput> {\n submittedCount += 1;\n\n const p = run(input, { ...runOpts, signal: ac.signal });\n\n pendingPromises.push(p);\n p.then(\n () => {\n settledCount += 1;\n decrementGroupIfDone();\n },\n () => {\n settledCount += 1;\n decrementGroupIfDone();\n },\n );\n\n return p;\n },\n\n get size() {\n return submittedCount;\n },\n };\n }\n\n // ─── Status ──────────────────────────────────────────────────────────────────\n\n function getStatus(): WorkerStatus {\n if (terminated) return 'terminated';\n\n return activeCount > 0 || queue.size > 0 ? 'running' : 'idle';\n }\n\n return {\n get active(): number {\n return activeCount;\n },\n batch,\n get completed(): number {\n return completedCount;\n },\n get concurrency(): number {\n return concurrency;\n },\n get disposalSignal(): AbortSignal {\n return disposeController.signal;\n },\n dispose,\n get disposed(): boolean {\n return terminated;\n },\n drain,\n get failed(): number {\n return failedCount;\n },\n group,\n get groupCount(): number {\n return groupActiveCount;\n },\n prime(): Promise<void> {\n return Promise.all(slots.map((s) => s.prime())).then(() => {});\n },\n get queued(): number {\n return queue.size;\n },\n run,\n runStream,\n get status(): WorkerStatus {\n return getStatus();\n },\n [Symbol.asyncDispose]: () => drain(),\n [Symbol.dispose]: dispose,\n };\n}\n"],"mappings":"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"}
package/dist/_pool.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { FamiliarQueueFullError as e, FamiliarRuntimeError as t, FamiliarTerminatedError as n, FamiliarTimeoutError as r } from "./errors.js";
2
2
  import { TaskQueue as i } from "./_queue.js";
3
3
  import { unrefTimer as a } from "./_timers.js";
4
- import { abortError as o } from "@vielzeug/arsenal";
4
+ import { abortError as o } from "@vielzeug/arsenal/async";
5
5
  //#region src/_pool.ts
6
6
  function s(s, c) {
7
7
  let { concurrency: l, defaultTimeout: u, maxQueue: d, onFull: f } = c, p = [...s], m = new i(), h = new AbortController(), g = [], _ = [], v = 0, y, b = 0, x = 0, S = 0, C = !1;
@@ -15,10 +15,13 @@ function s(s, c) {
15
15
  }
16
16
  function E(e) {
17
17
  return w() ? Promise.resolve() : new Promise((t, n) => {
18
- g.push(t), e !== void 0 && a(setTimeout(() => {
19
- let i = g.indexOf(t);
20
- i !== -1 && g.splice(i, 1), n(new r(e));
21
- }, e));
18
+ if (g.push(t), e !== void 0) {
19
+ let i = setTimeout(() => {
20
+ let i = g.indexOf(t);
21
+ i !== -1 && g.splice(i, 1), n(new r(e));
22
+ }, e);
23
+ a(i);
24
+ }
22
25
  });
23
26
  }
24
27
  function D() {
package/dist/_pool.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"_pool.js","names":[],"sources":["../src/_pool.ts"],"sourcesContent":["/**\n * Shared pool orchestration engine used by both the real worker implementation and the test\n * double. Handles queue management, iterative drain loop, abort handling, lifecycle (drain /\n * dispose), metrics, batch streaming, task groups, and backpressure.\n *\n * Callers provide a SlotStrategy array — each slot encapsulates run/runStream/prime/terminate.\n * The pool does not care whether slots use a Web Worker or run in-process.\n *\n * Not part of the public API surface.\n */\n\nimport { abortError } from '@vielzeug/arsenal';\n\nimport type {\n BatchOptions,\n GroupOptions,\n RunOptions,\n SlotStrategy,\n TaskGroup,\n WorkerHandle,\n WorkerStatus,\n} from './types';\n\nimport { type QueueItem, TaskQueue } from './_queue';\nimport { unrefTimer } from './_timers';\nimport { FamiliarQueueFullError, FamiliarRuntimeError, FamiliarTerminatedError, FamiliarTimeoutError } from './errors';\n\nexport type PoolOptions = {\n concurrency: number;\n defaultTimeout: number | undefined;\n maxQueue: number | undefined;\n onFull: 'reject' | 'wait';\n};\n\nexport function createPool<TInput, TOutput>(\n slots: SlotStrategy<TInput, TOutput>[],\n options: PoolOptions,\n): WorkerHandle<TInput, TOutput> {\n const { concurrency, defaultTimeout, maxQueue, onFull } = options;\n const freeSlots = [...slots];\n const queue = new TaskQueue<TInput, TOutput>();\n const disposeController = new AbortController();\n const idleResolvers: Array<() => void> = [];\n /** Waiters blocked on run() because the queue is full (onFull='wait' mode). */\n const fullWaiters: Array<() => void> = [];\n\n let activeCount = 0;\n let drainPromise: Promise<void> | undefined;\n let completedCount = 0;\n let failedCount = 0;\n let groupActiveCount = 0;\n let terminated = false;\n\n // ─── Idle tracking ────────────────────────────────────────────────────────────\n\n function isIdle(): boolean {\n return activeCount === 0 && queue.size === 0;\n }\n\n function notifyIdle(): void {\n if (!isIdle() || idleResolvers.length === 0) return;\n\n const resolvers = idleResolvers.splice(0);\n\n for (const resolve of resolvers) resolve();\n }\n\n function waitForIdle(timeoutMs?: number): Promise<void> {\n if (isIdle()) return Promise.resolve();\n\n return new Promise<void>((resolve, reject) => {\n idleResolvers.push(resolve);\n\n if (timeoutMs !== undefined) {\n const timer = setTimeout(() => {\n const idx = idleResolvers.indexOf(resolve);\n\n if (idx !== -1) idleResolvers.splice(idx, 1);\n\n reject(new FamiliarTimeoutError(timeoutMs));\n }, timeoutMs);\n\n unrefTimer(timer);\n }\n });\n }\n\n // ─── Full-queue backpressure (onFull='wait') ──────────────────────────────────\n\n function releaseOneFullWaiter(): void {\n const waiter = fullWaiters.shift();\n\n if (waiter) waiter();\n }\n\n // ─── Drain loop (iterative) ───────────────────────────────────────────────────\n\n let draining = false;\n\n function drainLoop(): void {\n if (terminated || draining) return;\n\n draining = true;\n\n while (!terminated && freeSlots.length > 0 && queue.size > 0) {\n const item = nextItem();\n\n if (!item) break;\n\n const slot = freeSlots.pop()!;\n\n item.cleanupAbort?.();\n activeCount += 1;\n releaseOneFullWaiter();\n\n const taskTimeout = item.timeout ?? defaultTimeout;\n\n slot.run(item.input, item.transferables, taskTimeout).then(\n (result) => {\n freeSlots.push(slot);\n activeCount -= 1;\n completedCount += 1;\n item.resolve(result);\n drainLoop();\n\n if (isIdle()) notifyIdle();\n },\n (error: unknown) => {\n freeSlots.push(slot);\n activeCount -= 1;\n\n if (!(error instanceof FamiliarTerminatedError)) failedCount += 1;\n\n item.reject(error);\n drainLoop();\n\n if (isIdle()) notifyIdle();\n },\n );\n }\n\n draining = false;\n }\n\n // ─── Queue helpers ────────────────────────────────────────────────────────────\n\n function nextItem(): QueueItem<TInput, TOutput> | undefined {\n while (queue.size > 0) {\n const item = queue.shift();\n\n if (!item) break;\n\n if (item.signal?.aborted) {\n item.cleanupAbort?.();\n item.reject(abortError(item.signal));\n continue;\n }\n\n return item;\n }\n\n // All remaining items were aborted and rejected above — the pool may now be idle.\n // Notify any drain() waiter so it does not hang.\n notifyIdle();\n\n return undefined;\n }\n\n // ─── Lifecycle ────────────────────────────────────────────────────────────────\n\n function dispose(): void {\n if (terminated) return;\n\n terminated = true;\n disposeController.abort();\n\n for (const slot of slots) slot.terminate();\n\n while (queue.size > 0) {\n const item = queue.shift();\n\n if (!item) break;\n\n item.cleanupAbort?.();\n item.reject(new FamiliarTerminatedError());\n }\n\n const resolvers = idleResolvers.splice(0);\n\n for (const resolve of resolvers) resolve();\n\n for (const waiter of fullWaiters.splice(0)) waiter();\n }\n\n function drain(timeoutMs?: number): Promise<void> {\n if (terminated) return Promise.resolve();\n\n if (drainPromise) return drainPromise;\n\n drainPromise = waitForIdle(timeoutMs).then(dispose, (err) => {\n dispose();\n throw err;\n });\n\n return drainPromise;\n }\n\n // ─── run() ───────────────────────────────────────────────────────────────────\n\n async function run(input: TInput, runOptions: RunOptions = {}): Promise<TOutput> {\n const { priority = 0, signal, timeout, transferables = [] } = runOptions;\n\n if (terminated) {\n throw new FamiliarTerminatedError();\n }\n\n if (drainPromise) {\n throw new FamiliarTerminatedError('Worker is draining');\n }\n\n if (signal?.aborted) {\n throw abortError(signal);\n }\n\n if (onFull === 'wait' && maxQueue !== undefined) {\n while (!terminated && !drainPromise && queue.size >= maxQueue) {\n await new Promise<void>((resolve) => fullWaiters.push(resolve));\n }\n\n if (terminated) throw new FamiliarTerminatedError();\n\n if (drainPromise) throw new FamiliarTerminatedError('Worker is draining');\n }\n\n let resolve!: (value: TOutput) => void;\n let reject!: (reason: unknown) => void;\n\n const promise = new Promise<TOutput>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n const item: QueueItem<TInput, TOutput> = {\n input,\n priority,\n reject,\n resolve,\n signal,\n timeout,\n transferables,\n };\n\n if (!queue.enqueue(item, onFull === 'wait' ? undefined : maxQueue)) {\n // maxQueue is guaranteed defined here: enqueue() only returns false when maxQueue is set.\n throw new FamiliarQueueFullError(maxQueue as number);\n }\n\n if (signal) {\n const onAbort = () => {\n if (!queue.remove(item)) return;\n\n item.cleanupAbort?.();\n reject(abortError(signal));\n notifyIdle();\n };\n\n item.cleanupAbort = () => {\n signal.removeEventListener('abort', onAbort);\n item.cleanupAbort = undefined;\n };\n\n signal.addEventListener('abort', onAbort, { once: true });\n }\n\n drainLoop();\n\n return promise;\n }\n\n // ─── runStream() ─────────────────────────────────────────────────────────────\n\n function runStream(input: TInput, options: Omit<RunOptions, 'signal'> = {}): AsyncIterable<TOutput> {\n if (terminated) {\n throw new FamiliarRuntimeError('Worker was terminated');\n }\n\n const slot = freeSlots.pop();\n\n if (!slot) {\n throw new FamiliarRuntimeError(\n `runStream() requires a free worker slot; all ${slots.length} slot${slots.length === 1 ? '' : 's'} are busy`,\n );\n }\n\n const { timeout, transferables = [] } = options;\n const iter = slot.runStream(input, transferables, timeout);\n\n return {\n [Symbol.asyncIterator]() {\n const inner = iter[Symbol.asyncIterator]();\n let released = false;\n\n const releaseSlot = () => {\n if (!released) {\n released = true;\n freeSlots.push(slot);\n }\n };\n\n return {\n async next() {\n const result = await inner.next();\n\n if (result.done) releaseSlot();\n\n return result;\n },\n\n async return(value?: unknown) {\n slot.cancel();\n releaseSlot();\n\n return inner.return?.(value) ?? { done: true as const, value };\n },\n\n async throw(error?: unknown) {\n slot.cancel();\n releaseSlot();\n\n if (inner.throw) return inner.throw(error);\n\n throw error;\n },\n };\n },\n };\n }\n\n // ─── batch() ─────────────────────────────────────────────────────────────────\n\n async function* batch(inputs: TInput[], batchOptions: BatchOptions = {}): AsyncIterable<TOutput> {\n if (inputs.length === 0) return;\n\n const { ordered = true, ...runOpts } = batchOptions;\n const ac = new AbortController();\n\n try {\n if (ordered) {\n const promises = inputs.map((input) => run(input, { ...runOpts, signal: ac.signal }));\n\n for (const p of promises) {\n yield await p;\n }\n } else {\n // As-completed: yield results in the order tasks finish, not submission order.\n // A single-slot notification channel wakes the consumer when the next result is ready.\n // Submission is windowed to `concurrency` outstanding (in-flight or buffered-but-unread)\n // tasks at a time — releasing capacity for one more submission each time the consumer\n // reads a result — so a slow consumer can't let an entire large batch settle in memory.\n type Completion = { error: unknown } | { value: TOutput };\n\n const completions: Completion[] = [];\n let notifier: (() => void) | null = null;\n let nextIndex = 0;\n const windowSize = Math.max(1, concurrency);\n\n function submitNext(): void {\n if (nextIndex >= inputs.length) return;\n\n const input = inputs[nextIndex++]!;\n\n run(input, { ...runOpts, signal: ac.signal }).then(\n (value) => {\n completions.push({ value });\n notifier?.();\n notifier = null;\n },\n (error: unknown) => {\n completions.push({ error });\n notifier?.();\n notifier = null;\n },\n );\n }\n\n for (let i = 0; i < windowSize && i < inputs.length; i++) submitNext();\n\n for (let i = 0; i < inputs.length; i++) {\n while (completions.length === 0) {\n await new Promise<void>((resolve) => {\n notifier = resolve;\n });\n }\n\n const next = completions.shift()!;\n\n if ('error' in next) throw next.error;\n\n yield next.value;\n\n // Only release capacity for another submission once the consumer has actually resumed\n // past this yield (i.e. did not break/return early) — submitting here unconditionally\n // would over-submit by one task on every early exit.\n submitNext();\n }\n }\n } finally {\n // Abort remaining in-flight tasks on any exit path (normal, consumer break, or error).\n ac.abort();\n }\n }\n\n // ─── group() ─────────────────────────────────────────────────────────────────\n\n function group(name?: string, options: GroupOptions = {}): TaskGroup<TInput, TOutput> {\n const ac = new AbortController();\n\n if (options.signal) {\n const sig = options.signal;\n\n if (sig.aborted) {\n ac.abort(sig.reason);\n } else {\n sig.addEventListener('abort', () => ac.abort(sig.reason), { once: true });\n }\n }\n\n groupActiveCount += 1;\n\n let submittedCount = 0;\n let settledCount = 0;\n let groupClosed = false;\n const pendingPromises: Promise<TOutput>[] = [];\n\n function decrementGroupIfDone(): void {\n if (!groupClosed && submittedCount > 0 && submittedCount === settledCount) {\n groupClosed = true;\n groupActiveCount -= 1;\n }\n }\n\n return {\n abort(reason?: unknown): void {\n ac.abort(reason);\n },\n\n drain(): Promise<PromiseSettledResult<TOutput>[]> {\n const snapshot = pendingPromises.splice(0);\n\n if (!groupClosed) {\n groupClosed = true;\n groupActiveCount -= 1;\n }\n\n return Promise.allSettled(snapshot);\n },\n\n get name() {\n return name;\n },\n\n get pending() {\n return submittedCount - settledCount;\n },\n\n run(input: TInput, runOpts: Omit<RunOptions, 'signal'> = {}): Promise<TOutput> {\n submittedCount += 1;\n\n const p = run(input, { ...runOpts, signal: ac.signal });\n\n pendingPromises.push(p);\n p.then(\n () => {\n settledCount += 1;\n decrementGroupIfDone();\n },\n () => {\n settledCount += 1;\n decrementGroupIfDone();\n },\n );\n\n return p;\n },\n\n get size() {\n return submittedCount;\n },\n };\n }\n\n // ─── Status ──────────────────────────────────────────────────────────────────\n\n function getStatus(): WorkerStatus {\n if (terminated) return 'terminated';\n\n return activeCount > 0 || queue.size > 0 ? 'running' : 'idle';\n }\n\n return {\n get active(): number {\n return activeCount;\n },\n batch,\n get completed(): number {\n return completedCount;\n },\n get concurrency(): number {\n return concurrency;\n },\n get disposalSignal(): AbortSignal {\n return disposeController.signal;\n },\n dispose,\n get disposed(): boolean {\n return terminated;\n },\n drain,\n get failed(): number {\n return failedCount;\n },\n group,\n get groupCount(): number {\n return groupActiveCount;\n },\n prime(): Promise<void> {\n return Promise.all(slots.map((s) => s.prime())).then(() => {});\n },\n get queued(): number {\n return queue.size;\n },\n run,\n runStream,\n get status(): WorkerStatus {\n return getStatus();\n },\n [Symbol.asyncDispose]: () => drain(),\n [Symbol.dispose]: dispose,\n };\n}\n"],"mappings":";;;;;AAkCA,SAAgB,EACd,GACA,GAC+B;CAC/B,IAAM,EAAE,gBAAa,mBAAgB,aAAU,cAAW,GACpD,IAAY,CAAC,GAAG,CAAK,GACrB,IAAQ,IAAI,EAA2B,GACvC,IAAoB,IAAI,gBAAgB,GACxC,IAAmC,CAAC,GAEpC,IAAiC,CAAC,GAEpC,IAAc,GACd,GACA,IAAiB,GACjB,IAAc,GACd,IAAmB,GACnB,IAAa;CAIjB,SAAS,IAAkB;EACzB,OAAO,MAAgB,KAAK,EAAM,SAAS;CAC7C;CAEA,SAAS,IAAmB;EAC1B,IAAI,CAAC,EAAO,KAAK,EAAc,WAAW,GAAG;EAE7C,IAAM,IAAY,EAAc,OAAO,CAAC;EAExC,KAAK,IAAM,KAAW,GAAW,EAAQ;CAC3C;CAEA,SAAS,EAAY,GAAmC;EAGtD,OAFI,EAAO,IAAU,QAAQ,QAAQ,IAE9B,IAAI,SAAe,GAAS,MAAW;GAG5C,AAFA,EAAc,KAAK,CAAO,GAEtB,MAAc,KAAA,KAShB,EARc,iBAAiB;IAC7B,IAAM,IAAM,EAAc,QAAQ,CAAO;IAIzC,AAFI,MAAQ,MAAI,EAAc,OAAO,GAAK,CAAC,GAE3C,EAAO,IAAI,EAAqB,CAAS,CAAC;GAC5C,GAAG,CAEQ,CAAK;EAEpB,CAAC;CACH;CAIA,SAAS,IAA6B;EACpC,IAAM,IAAS,EAAY,MAAM;EAEjC,AAAI,KAAQ,EAAO;CACrB;CAIA,IAAI,IAAW;CAEf,SAAS,IAAkB;EACrB,WAAc,IAIlB;QAFA,IAAW,IAEJ,CAAC,KAAc,EAAU,SAAS,KAAK,EAAM,OAAO,IAAG;IAC5D,IAAM,IAAO,EAAS;IAEtB,IAAI,CAAC,GAAM;IAEX,IAAM,IAAO,EAAU,IAAI;IAI3B,AAFA,EAAK,eAAe,GACpB,KAAe,GACf,EAAqB;IAErB,IAAM,IAAc,EAAK,WAAW;IAEpC,EAAK,IAAI,EAAK,OAAO,EAAK,eAAe,CAAW,CAAC,CAAC,MACnD,MAAW;KAOV,AANA,EAAU,KAAK,CAAI,GACnB,KACA,KAAkB,GAClB,EAAK,QAAQ,CAAM,GACnB,EAAU,GAEN,EAAO,KAAG,EAAW;IAC3B,IACC,MAAmB;KASlB,AARA,EAAU,KAAK,CAAI,GACnB,KAEM,aAAiB,MAA0B,KAAe,IAEhE,EAAK,OAAO,CAAK,GACjB,EAAU,GAEN,EAAO,KAAG,EAAW;IAC3B,CACF;GACF;GAEA,IAAW;EAFX;CAGF;CAIA,SAAS,IAAmD;EAC1D,OAAO,EAAM,OAAO,IAAG;GACrB,IAAM,IAAO,EAAM,MAAM;GAEzB,IAAI,CAAC,GAAM;GAEX,IAAI,EAAK,QAAQ,SAAS;IAExB,AADA,EAAK,eAAe,GACpB,EAAK,OAAO,EAAW,EAAK,MAAM,CAAC;IACnC;GACF;GAEA,OAAO;EACT;EAIA,EAAW;CAGb;CAIA,SAAS,IAAgB;EACvB,IAAI,GAAY;EAGhB,AADA,IAAa,IACb,EAAkB,MAAM;EAExB,KAAK,IAAM,KAAQ,GAAO,EAAK,UAAU;EAEzC,OAAO,EAAM,OAAO,IAAG;GACrB,IAAM,IAAO,EAAM,MAAM;GAEzB,IAAI,CAAC,GAAM;GAGX,AADA,EAAK,eAAe,GACpB,EAAK,OAAO,IAAI,EAAwB,CAAC;EAC3C;EAEA,IAAM,IAAY,EAAc,OAAO,CAAC;EAExC,KAAK,IAAM,KAAW,GAAW,EAAQ;EAEzC,KAAK,IAAM,KAAU,EAAY,OAAO,CAAC,GAAG,EAAO;CACrD;CAEA,SAAS,EAAM,GAAmC;EAUhD,OATI,IAAmB,QAAQ,QAAQ,IAEnC,MAEJ,IAAe,EAAY,CAAS,CAAC,CAAC,KAAK,IAAU,MAAQ;GAE3D,MADA,EAAQ,GACF;EACR,CAAC,GAEM;CACT;CAIA,eAAe,EAAI,GAAe,IAAyB,CAAC,GAAqB;EAC/E,IAAM,EAAE,cAAW,GAAG,WAAQ,YAAS,mBAAgB,CAAC,MAAM;EAE9D,IAAI,GACF,MAAM,IAAI,EAAwB;EAGpC,IAAI,GACF,MAAM,IAAI,EAAwB,oBAAoB;EAGxD,IAAI,GAAQ,SACV,MAAM,EAAW,CAAM;EAGzB,IAAI,MAAW,UAAU,MAAa,KAAA,GAAW;GAC/C,OAAO,CAAC,KAAc,CAAC,KAAgB,EAAM,QAAQ,IACnD,MAAM,IAAI,SAAe,MAAY,EAAY,KAAK,CAAO,CAAC;GAGhE,IAAI,GAAY,MAAM,IAAI,EAAwB;GAElD,IAAI,GAAc,MAAM,IAAI,EAAwB,oBAAoB;EAC1E;EAEA,IAAI,GACA,GAEE,IAAU,IAAI,SAAkB,GAAK,MAAQ;GAEjD,AADA,IAAU,GACV,IAAS;EACX,CAAC,GAEK,IAAmC;GACvC;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EAEA,IAAI,CAAC,EAAM,QAAQ,GAAM,MAAW,SAAS,KAAA,IAAY,CAAQ,GAE/D,MAAM,IAAI,EAAuB,CAAkB;EAGrD,IAAI,GAAQ;GACV,IAAM,UAAgB;IACf,EAAM,OAAO,CAAI,MAEtB,EAAK,eAAe,GACpB,EAAO,EAAW,CAAM,CAAC,GACzB,EAAW;GACb;GAOA,AALA,EAAK,qBAAqB;IAExB,AADA,EAAO,oBAAoB,SAAS,CAAO,GAC3C,EAAK,eAAe,KAAA;GACtB,GAEA,EAAO,iBAAiB,SAAS,GAAS,EAAE,MAAM,GAAK,CAAC;EAC1D;EAIA,OAFA,EAAU,GAEH;CACT;CAIA,SAAS,EAAU,GAAe,IAAsC,CAAC,GAA2B;EAClG,IAAI,GACF,MAAM,IAAI,EAAqB,uBAAuB;EAGxD,IAAM,IAAO,EAAU,IAAI;EAE3B,IAAI,CAAC,GACH,MAAM,IAAI,EACR,gDAAgD,EAAM,OAAO,OAAO,EAAM,WAAW,IAAI,KAAK,IAAI,UACpG;EAGF,IAAM,EAAE,YAAS,mBAAgB,CAAC,MAAM,GAClC,IAAO,EAAK,UAAU,GAAO,GAAe,CAAO;EAEzD,OAAO,EACL,CAAC,OAAO,iBAAiB;GACvB,IAAM,IAAQ,EAAK,OAAO,cAAc,CAAC,GACrC,IAAW,IAET,UAAoB;IACxB,AAAK,MACH,IAAW,IACX,EAAU,KAAK,CAAI;GAEvB;GAEA,OAAO;IACL,MAAM,OAAO;KACX,IAAM,IAAS,MAAM,EAAM,KAAK;KAIhC,OAFI,EAAO,QAAM,EAAY,GAEtB;IACT;IAEA,MAAM,OAAO,GAAiB;KAI5B,OAHA,EAAK,OAAO,GACZ,EAAY,GAEL,EAAM,SAAS,CAAK,KAAK;MAAE,MAAM;MAAe;KAAM;IAC/D;IAEA,MAAM,MAAM,GAAiB;KAI3B,IAHA,EAAK,OAAO,GACZ,EAAY,GAER,EAAM,OAAO,OAAO,EAAM,MAAM,CAAK;KAEzC,MAAM;IACR;GACF;EACF,EACF;CACF;CAIA,gBAAgB,EAAM,GAAkB,IAA6B,CAAC,GAA2B;EAC/F,IAAI,EAAO,WAAW,GAAG;EAEzB,IAAM,EAAE,aAAU,IAAM,GAAG,MAAY,GACjC,IAAK,IAAI,gBAAgB;EAE/B,IAAI;GACF,IAAI,GAAS;IACX,IAAM,IAAW,EAAO,KAAK,MAAU,EAAI,GAAO;KAAE,GAAG;KAAS,QAAQ,EAAG;IAAO,CAAC,CAAC;IAEpF,KAAK,IAAM,KAAK,GACd,MAAM,MAAM;GAEhB,OAAO;IAQL,IAAM,IAA4B,CAAC,GAC/B,IAAgC,MAChC,IAAY,GACV,IAAa,KAAK,IAAI,GAAG,CAAW;IAE1C,SAAS,IAAmB;KAC1B,IAAI,KAAa,EAAO,QAAQ;KAEhC,IAAM,IAAQ,EAAO;KAErB,EAAI,GAAO;MAAE,GAAG;MAAS,QAAQ,EAAG;KAAO,CAAC,CAAC,CAAC,MAC3C,MAAU;MAGT,AAFA,EAAY,KAAK,EAAE,SAAM,CAAC,GAC1B,IAAW,GACX,IAAW;KACb,IACC,MAAmB;MAGlB,AAFA,EAAY,KAAK,EAAE,SAAM,CAAC,GAC1B,IAAW,GACX,IAAW;KACb,CACF;IACF;IAEA,KAAK,IAAI,IAAI,GAAG,IAAI,KAAc,IAAI,EAAO,QAAQ,KAAK,EAAW;IAErE,KAAK,IAAI,IAAI,GAAG,IAAI,EAAO,QAAQ,KAAK;KACtC,OAAO,EAAY,WAAW,IAC5B,MAAM,IAAI,SAAe,MAAY;MACnC,IAAW;KACb,CAAC;KAGH,IAAM,IAAO,EAAY,MAAM;KAE/B,IAAI,WAAW,GAAM,MAAM,EAAK;KAOhC,AALA,MAAM,EAAK,OAKX,EAAW;IACb;GACF;EACF,UAAU;GAER,EAAG,MAAM;EACX;CACF;CAIA,SAAS,EAAM,GAAe,IAAwB,CAAC,GAA+B;EACpF,IAAM,IAAK,IAAI,gBAAgB;EAE/B,IAAI,EAAQ,QAAQ;GAClB,IAAM,IAAM,EAAQ;GAEpB,AAAI,EAAI,UACN,EAAG,MAAM,EAAI,MAAM,IAEnB,EAAI,iBAAiB,eAAe,EAAG,MAAM,EAAI,MAAM,GAAG,EAAE,MAAM,GAAK,CAAC;EAE5E;EAEA,KAAoB;EAEpB,IAAI,IAAiB,GACjB,IAAe,GACf,IAAc,IACZ,IAAsC,CAAC;EAE7C,SAAS,IAA6B;GACpC,AAAI,CAAC,KAAe,IAAiB,KAAK,MAAmB,MAC3D,IAAc,IACd;EAEJ;EAEA,OAAO;GACL,MAAM,GAAwB;IAC5B,EAAG,MAAM,CAAM;GACjB;GAEA,QAAkD;IAChD,IAAM,IAAW,EAAgB,OAAO,CAAC;IAOzC,OALK,MACH,IAAc,IACd,MAGK,QAAQ,WAAW,CAAQ;GACpC;GAEA,IAAI,OAAO;IACT,OAAO;GACT;GAEA,IAAI,UAAU;IACZ,OAAO,IAAiB;GAC1B;GAEA,IAAI,GAAe,IAAsC,CAAC,GAAqB;IAC7E,KAAkB;IAElB,IAAM,IAAI,EAAI,GAAO;KAAE,GAAG;KAAS,QAAQ,EAAG;IAAO,CAAC;IActD,OAZA,EAAgB,KAAK,CAAC,GACtB,EAAE,WACM;KAEJ,AADA,KAAgB,GAChB,EAAqB;IACvB,SACM;KAEJ,AADA,KAAgB,GAChB,EAAqB;IACvB,CACF,GAEO;GACT;GAEA,IAAI,OAAO;IACT,OAAO;GACT;EACF;CACF;CAIA,SAAS,IAA0B;EAGjC,OAFI,IAAmB,eAEhB,IAAc,KAAK,EAAM,OAAO,IAAI,YAAY;CACzD;CAEA,OAAO;EACL,IAAI,SAAiB;GACnB,OAAO;EACT;EACA;EACA,IAAI,YAAoB;GACtB,OAAO;EACT;EACA,IAAI,cAAsB;GACxB,OAAO;EACT;EACA,IAAI,iBAA8B;GAChC,OAAO,EAAkB;EAC3B;EACA;EACA,IAAI,WAAoB;GACtB,OAAO;EACT;EACA;EACA,IAAI,SAAiB;GACnB,OAAO;EACT;EACA;EACA,IAAI,aAAqB;GACvB,OAAO;EACT;EACA,QAAuB;GACrB,OAAO,QAAQ,IAAI,EAAM,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;EAC/D;EACA,IAAI,SAAiB;GACnB,OAAO,EAAM;EACf;EACA;EACA;EACA,IAAI,SAAuB;GACzB,OAAO,EAAU;EACnB;GACC,OAAO,qBAAqB,EAAM;GAClC,OAAO,UAAU;CACpB;AACF"}
1
+ {"version":3,"file":"_pool.js","names":[],"sources":["../src/_pool.ts"],"sourcesContent":["/**\n * Shared pool orchestration engine used by both the real worker implementation and the test\n * double. Handles queue management, iterative drain loop, abort handling, lifecycle (drain /\n * dispose), metrics, batch streaming, task groups, and backpressure.\n *\n * Callers provide a SlotStrategy array — each slot encapsulates run/runStream/prime/terminate.\n * The pool does not care whether slots use a Web Worker or run in-process.\n *\n * Not part of the public API surface.\n */\n\nimport { abortError } from '@vielzeug/arsenal/async';\n\nimport type {\n BatchOptions,\n GroupOptions,\n RunOptions,\n SlotStrategy,\n TaskGroup,\n WorkerHandle,\n WorkerStatus,\n} from './types';\n\nimport { type QueueItem, TaskQueue } from './_queue';\nimport { unrefTimer } from './_timers';\nimport { FamiliarQueueFullError, FamiliarRuntimeError, FamiliarTerminatedError, FamiliarTimeoutError } from './errors';\n\nexport type PoolOptions = {\n concurrency: number;\n defaultTimeout: number | undefined;\n maxQueue: number | undefined;\n onFull: 'reject' | 'wait';\n};\n\nexport function createPool<TInput, TOutput>(\n slots: SlotStrategy<TInput, TOutput>[],\n options: PoolOptions,\n): WorkerHandle<TInput, TOutput> {\n const { concurrency, defaultTimeout, maxQueue, onFull } = options;\n const freeSlots = [...slots];\n const queue = new TaskQueue<TInput, TOutput>();\n const disposeController = new AbortController();\n const idleResolvers: Array<() => void> = [];\n /** Waiters blocked on run() because the queue is full (onFull='wait' mode). */\n const fullWaiters: Array<() => void> = [];\n\n let activeCount = 0;\n let drainPromise: Promise<void> | undefined;\n let completedCount = 0;\n let failedCount = 0;\n let groupActiveCount = 0;\n let terminated = false;\n\n // ─── Idle tracking ────────────────────────────────────────────────────────────\n\n function isIdle(): boolean {\n return activeCount === 0 && queue.size === 0;\n }\n\n function notifyIdle(): void {\n if (!isIdle() || idleResolvers.length === 0) return;\n\n const resolvers = idleResolvers.splice(0);\n\n for (const resolve of resolvers) resolve();\n }\n\n function waitForIdle(timeoutMs?: number): Promise<void> {\n if (isIdle()) return Promise.resolve();\n\n return new Promise<void>((resolve, reject) => {\n idleResolvers.push(resolve);\n\n if (timeoutMs !== undefined) {\n const timer = setTimeout(() => {\n const idx = idleResolvers.indexOf(resolve);\n\n if (idx !== -1) idleResolvers.splice(idx, 1);\n\n reject(new FamiliarTimeoutError(timeoutMs));\n }, timeoutMs);\n\n unrefTimer(timer);\n }\n });\n }\n\n // ─── Full-queue backpressure (onFull='wait') ──────────────────────────────────\n\n function releaseOneFullWaiter(): void {\n const waiter = fullWaiters.shift();\n\n if (waiter) waiter();\n }\n\n // ─── Drain loop (iterative) ───────────────────────────────────────────────────\n\n let draining = false;\n\n function drainLoop(): void {\n if (terminated || draining) return;\n\n draining = true;\n\n while (!terminated && freeSlots.length > 0 && queue.size > 0) {\n const item = nextItem();\n\n if (!item) break;\n\n const slot = freeSlots.pop()!;\n\n item.cleanupAbort?.();\n activeCount += 1;\n releaseOneFullWaiter();\n\n const taskTimeout = item.timeout ?? defaultTimeout;\n\n slot.run(item.input, item.transferables, taskTimeout).then(\n (result) => {\n freeSlots.push(slot);\n activeCount -= 1;\n completedCount += 1;\n item.resolve(result);\n drainLoop();\n\n if (isIdle()) notifyIdle();\n },\n (error: unknown) => {\n freeSlots.push(slot);\n activeCount -= 1;\n\n if (!(error instanceof FamiliarTerminatedError)) failedCount += 1;\n\n item.reject(error);\n drainLoop();\n\n if (isIdle()) notifyIdle();\n },\n );\n }\n\n draining = false;\n }\n\n // ─── Queue helpers ────────────────────────────────────────────────────────────\n\n function nextItem(): QueueItem<TInput, TOutput> | undefined {\n while (queue.size > 0) {\n const item = queue.shift();\n\n if (!item) break;\n\n if (item.signal?.aborted) {\n item.cleanupAbort?.();\n item.reject(abortError(item.signal));\n continue;\n }\n\n return item;\n }\n\n // All remaining items were aborted and rejected above — the pool may now be idle.\n // Notify any drain() waiter so it does not hang.\n notifyIdle();\n\n return undefined;\n }\n\n // ─── Lifecycle ────────────────────────────────────────────────────────────────\n\n function dispose(): void {\n if (terminated) return;\n\n terminated = true;\n disposeController.abort();\n\n for (const slot of slots) slot.terminate();\n\n while (queue.size > 0) {\n const item = queue.shift();\n\n if (!item) break;\n\n item.cleanupAbort?.();\n item.reject(new FamiliarTerminatedError());\n }\n\n const resolvers = idleResolvers.splice(0);\n\n for (const resolve of resolvers) resolve();\n\n for (const waiter of fullWaiters.splice(0)) waiter();\n }\n\n function drain(timeoutMs?: number): Promise<void> {\n if (terminated) return Promise.resolve();\n\n if (drainPromise) return drainPromise;\n\n drainPromise = waitForIdle(timeoutMs).then(dispose, (err) => {\n dispose();\n throw err;\n });\n\n return drainPromise;\n }\n\n // ─── run() ───────────────────────────────────────────────────────────────────\n\n async function run(input: TInput, runOptions: RunOptions = {}): Promise<TOutput> {\n const { priority = 0, signal, timeout, transferables = [] } = runOptions;\n\n if (terminated) {\n throw new FamiliarTerminatedError();\n }\n\n if (drainPromise) {\n throw new FamiliarTerminatedError('Worker is draining');\n }\n\n if (signal?.aborted) {\n throw abortError(signal);\n }\n\n if (onFull === 'wait' && maxQueue !== undefined) {\n while (!terminated && !drainPromise && queue.size >= maxQueue) {\n await new Promise<void>((resolve) => fullWaiters.push(resolve));\n }\n\n if (terminated) throw new FamiliarTerminatedError();\n\n if (drainPromise) throw new FamiliarTerminatedError('Worker is draining');\n }\n\n let resolve!: (value: TOutput) => void;\n let reject!: (reason: unknown) => void;\n\n const promise = new Promise<TOutput>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n const item: QueueItem<TInput, TOutput> = {\n input,\n priority,\n reject,\n resolve,\n signal,\n timeout,\n transferables,\n };\n\n if (!queue.enqueue(item, onFull === 'wait' ? undefined : maxQueue)) {\n // maxQueue is guaranteed defined here: enqueue() only returns false when maxQueue is set.\n throw new FamiliarQueueFullError(maxQueue as number);\n }\n\n if (signal) {\n const onAbort = () => {\n if (!queue.remove(item)) return;\n\n item.cleanupAbort?.();\n reject(abortError(signal));\n notifyIdle();\n };\n\n item.cleanupAbort = () => {\n signal.removeEventListener('abort', onAbort);\n item.cleanupAbort = undefined;\n };\n\n signal.addEventListener('abort', onAbort, { once: true });\n }\n\n drainLoop();\n\n return promise;\n }\n\n // ─── runStream() ─────────────────────────────────────────────────────────────\n\n function runStream(input: TInput, options: Omit<RunOptions, 'signal'> = {}): AsyncIterable<TOutput> {\n if (terminated) {\n throw new FamiliarRuntimeError('Worker was terminated');\n }\n\n const slot = freeSlots.pop();\n\n if (!slot) {\n throw new FamiliarRuntimeError(\n `runStream() requires a free worker slot; all ${slots.length} slot${slots.length === 1 ? '' : 's'} are busy`,\n );\n }\n\n const { timeout, transferables = [] } = options;\n const iter = slot.runStream(input, transferables, timeout);\n\n return {\n [Symbol.asyncIterator]() {\n const inner = iter[Symbol.asyncIterator]();\n let released = false;\n\n const releaseSlot = () => {\n if (!released) {\n released = true;\n freeSlots.push(slot);\n }\n };\n\n return {\n async next() {\n const result = await inner.next();\n\n if (result.done) releaseSlot();\n\n return result;\n },\n\n async return(value?: unknown) {\n slot.cancel();\n releaseSlot();\n\n return inner.return?.(value) ?? { done: true as const, value };\n },\n\n async throw(error?: unknown) {\n slot.cancel();\n releaseSlot();\n\n if (inner.throw) return inner.throw(error);\n\n throw error;\n },\n };\n },\n };\n }\n\n // ─── batch() ─────────────────────────────────────────────────────────────────\n\n async function* batch(inputs: TInput[], batchOptions: BatchOptions = {}): AsyncIterable<TOutput> {\n if (inputs.length === 0) return;\n\n const { ordered = true, ...runOpts } = batchOptions;\n const ac = new AbortController();\n\n try {\n if (ordered) {\n const promises = inputs.map((input) => run(input, { ...runOpts, signal: ac.signal }));\n\n for (const p of promises) {\n yield await p;\n }\n } else {\n // As-completed: yield results in the order tasks finish, not submission order.\n // A single-slot notification channel wakes the consumer when the next result is ready.\n // Submission is windowed to `concurrency` outstanding (in-flight or buffered-but-unread)\n // tasks at a time — releasing capacity for one more submission each time the consumer\n // reads a result — so a slow consumer can't let an entire large batch settle in memory.\n type Completion = { error: unknown } | { value: TOutput };\n\n const completions: Completion[] = [];\n let notifier: (() => void) | null = null;\n let nextIndex = 0;\n const windowSize = Math.max(1, concurrency);\n\n function submitNext(): void {\n if (nextIndex >= inputs.length) return;\n\n const input = inputs[nextIndex++]!;\n\n run(input, { ...runOpts, signal: ac.signal }).then(\n (value) => {\n completions.push({ value });\n notifier?.();\n notifier = null;\n },\n (error: unknown) => {\n completions.push({ error });\n notifier?.();\n notifier = null;\n },\n );\n }\n\n for (let i = 0; i < windowSize && i < inputs.length; i++) submitNext();\n\n for (let i = 0; i < inputs.length; i++) {\n while (completions.length === 0) {\n await new Promise<void>((resolve) => {\n notifier = resolve;\n });\n }\n\n const next = completions.shift()!;\n\n if ('error' in next) throw next.error;\n\n yield next.value;\n\n // Only release capacity for another submission once the consumer has actually resumed\n // past this yield (i.e. did not break/return early) — submitting here unconditionally\n // would over-submit by one task on every early exit.\n submitNext();\n }\n }\n } finally {\n // Abort remaining in-flight tasks on any exit path (normal, consumer break, or error).\n ac.abort();\n }\n }\n\n // ─── group() ─────────────────────────────────────────────────────────────────\n\n function group(name?: string, options: GroupOptions = {}): TaskGroup<TInput, TOutput> {\n const ac = new AbortController();\n\n if (options.signal) {\n const sig = options.signal;\n\n if (sig.aborted) {\n ac.abort(sig.reason);\n } else {\n sig.addEventListener('abort', () => ac.abort(sig.reason), { once: true });\n }\n }\n\n groupActiveCount += 1;\n\n let submittedCount = 0;\n let settledCount = 0;\n let groupClosed = false;\n const pendingPromises: Promise<TOutput>[] = [];\n\n function decrementGroupIfDone(): void {\n if (!groupClosed && submittedCount > 0 && submittedCount === settledCount) {\n groupClosed = true;\n groupActiveCount -= 1;\n }\n }\n\n return {\n abort(reason?: unknown): void {\n ac.abort(reason);\n },\n\n drain(): Promise<PromiseSettledResult<TOutput>[]> {\n const snapshot = pendingPromises.splice(0);\n\n if (!groupClosed) {\n groupClosed = true;\n groupActiveCount -= 1;\n }\n\n return Promise.allSettled(snapshot);\n },\n\n get name() {\n return name;\n },\n\n get pending() {\n return submittedCount - settledCount;\n },\n\n run(input: TInput, runOpts: Omit<RunOptions, 'signal'> = {}): Promise<TOutput> {\n submittedCount += 1;\n\n const p = run(input, { ...runOpts, signal: ac.signal });\n\n pendingPromises.push(p);\n p.then(\n () => {\n settledCount += 1;\n decrementGroupIfDone();\n },\n () => {\n settledCount += 1;\n decrementGroupIfDone();\n },\n );\n\n return p;\n },\n\n get size() {\n return submittedCount;\n },\n };\n }\n\n // ─── Status ──────────────────────────────────────────────────────────────────\n\n function getStatus(): WorkerStatus {\n if (terminated) return 'terminated';\n\n return activeCount > 0 || queue.size > 0 ? 'running' : 'idle';\n }\n\n return {\n get active(): number {\n return activeCount;\n },\n batch,\n get completed(): number {\n return completedCount;\n },\n get concurrency(): number {\n return concurrency;\n },\n get disposalSignal(): AbortSignal {\n return disposeController.signal;\n },\n dispose,\n get disposed(): boolean {\n return terminated;\n },\n drain,\n get failed(): number {\n return failedCount;\n },\n group,\n get groupCount(): number {\n return groupActiveCount;\n },\n prime(): Promise<void> {\n return Promise.all(slots.map((s) => s.prime())).then(() => {});\n },\n get queued(): number {\n return queue.size;\n },\n run,\n runStream,\n get status(): WorkerStatus {\n return getStatus();\n },\n [Symbol.asyncDispose]: () => drain(),\n [Symbol.dispose]: dispose,\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all familiar errors. Use `instanceof FamiliarError` to catch any familiar-originated error. */\nexport class FamiliarError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is FamiliarError {\n return err instanceof FamiliarError;\n }\n}\n\n/** Thrown when invalid options are passed to `createWorker` or `createModuleWorker`. */\nexport class FamiliarInvalidOptionsError extends FamiliarError {}\n\n/** Thrown when run() is called and the queue is full (`onFull='reject'`). */\nexport class FamiliarQueueFullError extends FamiliarError {\n /** The configured `maxQueue` value. */\n readonly maxQueue: number;\n\n constructor(maxQueue: number) {\n super(`Queue is full (maxQueue=${maxQueue})`);\n this.maxQueue = maxQueue;\n }\n}\n\n/** Thrown when the task function throws. The original error is available as `.cause`. */\nexport class FamiliarTaskError extends FamiliarError {}\n\n/** Thrown when a task or operation is rejected because the worker was terminated. */\nexport class FamiliarTerminatedError extends FamiliarError {\n constructor(message = 'Worker was terminated') {\n super(message);\n }\n}\n\n/** Thrown when a task exceeds its timeout or `drain()` times out. */\nexport class FamiliarTimeoutError extends FamiliarError {\n /** The configured timeout in milliseconds. */\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Task timed out after ${timeoutMs}ms`);\n this.timeoutMs = timeoutMs;\n }\n}\n\n/** Thrown when the Worker API is unavailable or an unhandled error occurs in the worker thread. */\nexport class FamiliarRuntimeError extends FamiliarError {}\n"],"mappings":"AACA,IAAa,EAAb,MAAa,UAAsB,KAAM,CACvC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,IAAI,OAAO,KACvB,OAAO,eAAe,KAAM,IAAI,OAAO,SAAS,CAClD,CAEA,OAAO,GAAG,EAAoC,CAC5C,OAAO,aAAe,CACxB,CACF,EAGa,EAAb,cAAiD,CAAc,CAAC,EAGnD,EAAb,cAA4C,CAAc,CAExD,SAEA,YAAY,EAAkB,CAC5B,MAAM,2BAA2B,EAAS,EAAE,EAC5C,KAAK,SAAW,CAClB,CACF,EAGa,EAAb,cAAuC,CAAc,CAAC,EAGzC,EAAb,cAA6C,CAAc,CACzD,YAAY,EAAU,wBAAyB,CAC7C,MAAM,CAAO,CACf,CACF,EAGa,EAAb,cAA0C,CAAc,CAEtD,UAEA,YAAY,EAAmB,CAC7B,MAAM,wBAAwB,EAAU,GAAG,EAC3C,KAAK,UAAY,CACnB,CACF,EAGa,EAAb,cAA0C,CAAc,CAAC"}
1
+ {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all familiar errors. Use `instanceof FamiliarError` to catch any familiar-originated error. */\nexport class FamiliarError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is FamiliarError {\n return err instanceof FamiliarError;\n }\n}\n\n/** Thrown when invalid options are passed to `createWorker` or `createModuleWorker`. */\nexport class FamiliarInvalidOptionsError extends FamiliarError {}\n\n/** Thrown when run() is called and the queue is full (`onFull='reject'`). */\nexport class FamiliarQueueFullError extends FamiliarError {\n /** The configured `maxQueue` value. */\n readonly maxQueue: number;\n\n constructor(maxQueue: number) {\n super(`Queue is full (maxQueue=${maxQueue})`);\n this.maxQueue = maxQueue;\n }\n}\n\n/** Thrown when the task function throws. The original error is available as `.cause`. */\nexport class FamiliarTaskError extends FamiliarError {}\n\n/** Thrown when a task or operation is rejected because the worker was terminated. */\nexport class FamiliarTerminatedError extends FamiliarError {\n constructor(message = 'Worker was terminated') {\n super(message);\n }\n}\n\n/** Thrown when a task exceeds its timeout or `drain()` times out. */\nexport class FamiliarTimeoutError extends FamiliarError {\n /** The configured timeout in milliseconds. */\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Task timed out after ${timeoutMs}ms`);\n this.timeoutMs = timeoutMs;\n }\n}\n\n/** Thrown when the Worker API is unavailable or an unhandled error occurs in the worker thread. */\nexport class FamiliarRuntimeError extends FamiliarError {}\n"],"mappings":"AACA,IAAa,EAAb,MAAa,UAAsB,KAAM,CACvC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CAEA,OAAO,GAAG,EAAoC,CAC5C,OAAO,aAAe,CACxB,CACF,EAGa,EAAb,cAAiD,CAAc,CAAC,EAGnD,EAAb,cAA4C,CAAc,CAExD,SAEA,YAAY,EAAkB,CAC5B,MAAM,2BAA2B,EAAS,EAAE,EAC5C,KAAK,SAAW,CAClB,CACF,EAGa,EAAb,cAAuC,CAAc,CAAC,EAGzC,EAAb,cAA6C,CAAc,CACzD,YAAY,EAAU,wBAAyB,CAC7C,MAAM,CAAO,CACf,CACF,EAGa,EAAb,cAA0C,CAAc,CAEtD,UAEA,YAAY,EAAmB,CAC7B,MAAM,wBAAwB,EAAU,GAAG,EAC3C,KAAK,UAAY,CACnB,CACF,EAGa,EAAb,cAA0C,CAAc,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all familiar errors. Use `instanceof FamiliarError` to catch any familiar-originated error. */\nexport class FamiliarError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is FamiliarError {\n return err instanceof FamiliarError;\n }\n}\n\n/** Thrown when invalid options are passed to `createWorker` or `createModuleWorker`. */\nexport class FamiliarInvalidOptionsError extends FamiliarError {}\n\n/** Thrown when run() is called and the queue is full (`onFull='reject'`). */\nexport class FamiliarQueueFullError extends FamiliarError {\n /** The configured `maxQueue` value. */\n readonly maxQueue: number;\n\n constructor(maxQueue: number) {\n super(`Queue is full (maxQueue=${maxQueue})`);\n this.maxQueue = maxQueue;\n }\n}\n\n/** Thrown when the task function throws. The original error is available as `.cause`. */\nexport class FamiliarTaskError extends FamiliarError {}\n\n/** Thrown when a task or operation is rejected because the worker was terminated. */\nexport class FamiliarTerminatedError extends FamiliarError {\n constructor(message = 'Worker was terminated') {\n super(message);\n }\n}\n\n/** Thrown when a task exceeds its timeout or `drain()` times out. */\nexport class FamiliarTimeoutError extends FamiliarError {\n /** The configured timeout in milliseconds. */\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Task timed out after ${timeoutMs}ms`);\n this.timeoutMs = timeoutMs;\n }\n}\n\n/** Thrown when the Worker API is unavailable or an unhandled error occurs in the worker thread. */\nexport class FamiliarRuntimeError extends FamiliarError {}\n"],"mappings":";AACA,IAAa,IAAb,MAAa,UAAsB,MAAM;CACvC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,IAAI,OAAO,MACvB,OAAO,eAAe,MAAM,IAAI,OAAO,SAAS;CAClD;CAEA,OAAO,GAAG,GAAoC;EAC5C,OAAO,aAAe;CACxB;AACF,GAGa,IAAb,cAAiD,EAAc,CAAC,GAGnD,IAAb,cAA4C,EAAc;CAExD;CAEA,YAAY,GAAkB;EAE5B,AADA,MAAM,2BAA2B,EAAS,EAAE,GAC5C,KAAK,WAAW;CAClB;AACF,GAGa,IAAb,cAAuC,EAAc,CAAC,GAGzC,IAAb,cAA6C,EAAc;CACzD,YAAY,IAAU,yBAAyB;EAC7C,MAAM,CAAO;CACf;AACF,GAGa,IAAb,cAA0C,EAAc;CAEtD;CAEA,YAAY,GAAmB;EAE7B,AADA,MAAM,wBAAwB,EAAU,GAAG,GAC3C,KAAK,YAAY;CACnB;AACF,GAGa,IAAb,cAA0C,EAAc,CAAC"}
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all familiar errors. Use `instanceof FamiliarError` to catch any familiar-originated error. */\nexport class FamiliarError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is FamiliarError {\n return err instanceof FamiliarError;\n }\n}\n\n/** Thrown when invalid options are passed to `createWorker` or `createModuleWorker`. */\nexport class FamiliarInvalidOptionsError extends FamiliarError {}\n\n/** Thrown when run() is called and the queue is full (`onFull='reject'`). */\nexport class FamiliarQueueFullError extends FamiliarError {\n /** The configured `maxQueue` value. */\n readonly maxQueue: number;\n\n constructor(maxQueue: number) {\n super(`Queue is full (maxQueue=${maxQueue})`);\n this.maxQueue = maxQueue;\n }\n}\n\n/** Thrown when the task function throws. The original error is available as `.cause`. */\nexport class FamiliarTaskError extends FamiliarError {}\n\n/** Thrown when a task or operation is rejected because the worker was terminated. */\nexport class FamiliarTerminatedError extends FamiliarError {\n constructor(message = 'Worker was terminated') {\n super(message);\n }\n}\n\n/** Thrown when a task exceeds its timeout or `drain()` times out. */\nexport class FamiliarTimeoutError extends FamiliarError {\n /** The configured timeout in milliseconds. */\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Task timed out after ${timeoutMs}ms`);\n this.timeoutMs = timeoutMs;\n }\n}\n\n/** Thrown when the Worker API is unavailable or an unhandled error occurs in the worker thread. */\nexport class FamiliarRuntimeError extends FamiliarError {}\n"],"mappings":";AACA,IAAa,IAAb,MAAa,UAAsB,MAAM;CACvC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;CAEA,OAAO,GAAG,GAAoC;EAC5C,OAAO,aAAe;CACxB;AACF,GAGa,IAAb,cAAiD,EAAc,CAAC,GAGnD,IAAb,cAA4C,EAAc;CAExD;CAEA,YAAY,GAAkB;EAE5B,AADA,MAAM,2BAA2B,EAAS,EAAE,GAC5C,KAAK,WAAW;CAClB;AACF,GAGa,IAAb,cAAuC,EAAc,CAAC,GAGzC,IAAb,cAA6C,EAAc;CACzD,YAAY,IAAU,yBAAyB;EAC7C,MAAM,CAAO;CACf;AACF,GAGa,IAAb,cAA0C,EAAc;CAEtD;CAEA,YAAY,GAAmB;EAE7B,AADA,MAAM,wBAAwB,EAAU,GAAG,GAC3C,KAAK,YAAY;CACnB;AACF,GAGa,IAAb,cAA0C,EAAc,CAAC"}
package/dist/familiar.cjs CHANGED
@@ -1,4 +1,4 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@vielzeug/arsenal");var t=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},n=class extends t{},r=class extends t{maxQueue;constructor(e){super(`Queue is full (maxQueue=${e})`),this.maxQueue=e}},i=class extends t{},a=class extends t{constructor(e=`Worker was terminated`){super(e)}},o=class extends t{timeoutMs;constructor(e){super(`Task timed out after ${e}ms`),this.timeoutMs=e}},s=class extends t{},c=!globalThis.__FAMILIAR_PROD__;function l(e){c&&console.warn(`[@vielzeug/familiar] ${e}`)}var u=class{heap=[];liveCount=0;seq=0;get size(){return this.liveCount}enqueue(e,t){return t!==void 0&&this.liveCount>=t?!1:(this.heap.push({_seq:this.seq++,item:e}),this.siftUp(this.heap.length-1),this.liveCount++,!0)}shift(){for(;this.heap.length>0;){let{item:e}=this.extractTop();if(!e.cancelled)return this.liveCount--,e}}remove(e){if(e.cancelled)return!1;for(let t=0;t<this.heap.length;t++)if(this.heap[t].item===e)return e.cancelled=!0,this.liveCount--,!0;return!1}extractTop(){let e=this.heap[0],t=this.heap.pop();return this.heap.length>0&&(this.heap[0]=t,this.siftDown(0)),e}higher(e,t){return e.item.priority===t.item.priority?e._seq<t._seq:e.item.priority>t.item.priority}siftUp(e){for(;e>0;){let t=e-1>>1;if(this.higher(this.heap[e],this.heap[t]))[this.heap[e],this.heap[t]]=[this.heap[t],this.heap[e]],e=t;else break}}siftDown(e){let t=this.heap.length;for(;;){let n=e,r=2*e+1,i=2*e+2;if(r<t&&this.higher(this.heap[r],this.heap[n])&&(n=r),i<t&&this.higher(this.heap[i],this.heap[n])&&(n=i),n===e)break;[this.heap[e],this.heap[n]]=[this.heap[n],this.heap[e]],e=n}}};function d(e){typeof e==`object`&&e&&`unref`in e&&e.unref()}function f(t,n){let{concurrency:i,defaultTimeout:c,maxQueue:l,onFull:f}=n,p=[...t],m=new u,h=new AbortController,g=[],_=[],v=0,y,b=0,x=0,S=0,C=!1;function w(){return v===0&&m.size===0}function T(){if(!w()||g.length===0)return;let e=g.splice(0);for(let t of e)t()}function E(e){return w()?Promise.resolve():new Promise((t,n)=>{g.push(t),e!==void 0&&d(setTimeout(()=>{let r=g.indexOf(t);r!==-1&&g.splice(r,1),n(new o(e))},e))})}function D(){let e=_.shift();e&&e()}let O=!1;function k(){if(!(C||O)){for(O=!0;!C&&p.length>0&&m.size>0;){let e=A();if(!e)break;let t=p.pop();e.cleanupAbort?.(),v+=1,D();let n=e.timeout??c;t.run(e.input,e.transferables,n).then(n=>{p.push(t),--v,b+=1,e.resolve(n),k(),w()&&T()},n=>{p.push(t),--v,n instanceof a||(x+=1),e.reject(n),k(),w()&&T()})}O=!1}}function A(){for(;m.size>0;){let t=m.shift();if(!t)break;if(t.signal?.aborted){t.cleanupAbort?.(),t.reject((0,e.abortError)(t.signal));continue}return t}T()}function j(){if(C)return;C=!0,h.abort();for(let e of t)e.terminate();for(;m.size>0;){let e=m.shift();if(!e)break;e.cleanupAbort?.(),e.reject(new a)}let e=g.splice(0);for(let t of e)t();for(let e of _.splice(0))e()}function M(e){return C?Promise.resolve():y||(y=E(e).then(j,e=>{throw j(),e}),y)}async function N(t,n={}){let{priority:i=0,signal:o,timeout:s,transferables:c=[]}=n;if(C)throw new a;if(y)throw new a(`Worker is draining`);if(o?.aborted)throw(0,e.abortError)(o);if(f===`wait`&&l!==void 0){for(;!C&&!y&&m.size>=l;)await new Promise(e=>_.push(e));if(C)throw new a;if(y)throw new a(`Worker is draining`)}let u,d,p=new Promise((e,t)=>{u=e,d=t}),h={input:t,priority:i,reject:d,resolve:u,signal:o,timeout:s,transferables:c};if(!m.enqueue(h,f===`wait`?void 0:l))throw new r(l);if(o){let t=()=>{m.remove(h)&&(h.cleanupAbort?.(),d((0,e.abortError)(o)),T())};h.cleanupAbort=()=>{o.removeEventListener(`abort`,t),h.cleanupAbort=void 0},o.addEventListener(`abort`,t,{once:!0})}return k(),p}function P(e,n={}){if(C)throw new s(`Worker was terminated`);let r=p.pop();if(!r)throw new s(`runStream() requires a free worker slot; all ${t.length} slot${t.length===1?``:`s`} are busy`);let{timeout:i,transferables:a=[]}=n,o=r.runStream(e,a,i);return{[Symbol.asyncIterator](){let e=o[Symbol.asyncIterator](),t=!1,n=()=>{t||(t=!0,p.push(r))};return{async next(){let t=await e.next();return t.done&&n(),t},async return(t){return r.cancel(),n(),e.return?.(t)??{done:!0,value:t}},async throw(t){if(r.cancel(),n(),e.throw)return e.throw(t);throw t}}}}}async function*F(e,t={}){if(e.length===0)return;let{ordered:n=!0,...r}=t,a=new AbortController;try{if(n){let t=e.map(e=>N(e,{...r,signal:a.signal}));for(let e of t)yield await e}else{let t=[],n=null,s=0,c=Math.max(1,i);function o(){if(s>=e.length)return;let i=e[s++];N(i,{...r,signal:a.signal}).then(e=>{t.push({value:e}),n?.(),n=null},e=>{t.push({error:e}),n?.(),n=null})}for(let t=0;t<c&&t<e.length;t++)o();for(let r=0;r<e.length;r++){for(;t.length===0;)await new Promise(e=>{n=e});let e=t.shift();if(`error`in e)throw e.error;yield e.value,o()}}}finally{a.abort()}}function I(e,t={}){let n=new AbortController;if(t.signal){let e=t.signal;e.aborted?n.abort(e.reason):e.addEventListener(`abort`,()=>n.abort(e.reason),{once:!0})}S+=1;let r=0,i=0,a=!1,o=[];function s(){!a&&r>0&&r===i&&(a=!0,--S)}return{abort(e){n.abort(e)},drain(){let e=o.splice(0);return a||(a=!0,--S),Promise.allSettled(e)},get name(){return e},get pending(){return r-i},run(e,t={}){r+=1;let a=N(e,{...t,signal:n.signal});return o.push(a),a.then(()=>{i+=1,s()},()=>{i+=1,s()}),a},get size(){return r}}}function L(){return C?`terminated`:v>0||m.size>0?`running`:`idle`}return{get active(){return v},batch:F,get completed(){return b},get concurrency(){return i},get disposalSignal(){return h.signal},dispose:j,get disposed(){return C},drain:M,get failed(){return x},group:I,get groupCount(){return S},prime(){return Promise.all(t.map(e=>e.prime())).then(()=>{})},get queued(){return m.size},run:N,runStream:P,get status(){return L()},[Symbol.asyncDispose]:()=>M(),[Symbol.dispose]:j}}function p(e){if(e.toString().includes(`[native code]`))throw new n(`Task function cannot be a bound or native function`);return e}var m=512;function h(e){if(e===void 0)return 1;if(e===`auto`)return Math.max(1,globalThis.navigator?.hardwareConcurrency??1);if(!Number.isInteger(e)||e<1||e>m)throw new n(`\`concurrency\` must be a positive integer ≤ ${m} or "auto"`);return e}function g(e={}){let t=h(e.concurrency),{heartbeatWindow:r,maxQueue:i,onFull:a=`reject`,onSlotError:o,timeout:s}=e;if(s!==void 0&&(!Number.isFinite(s)||s<=0))throw new n("`timeout` must be a finite number greater than 0");if(i!==void 0&&(!Number.isInteger(i)||i<1))throw new n("`maxQueue` must be a positive integer");if(r!==void 0&&(!Number.isFinite(r)||r<=0))throw new n("`heartbeatWindow` must be a finite number greater than 0");return{concurrency:t,heartbeatWindow:r,maxQueue:i,onFull:a,onSlotError:o,timeout:s}}function _(e,t){return`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},t=class extends e{},n=class extends e{maxQueue;constructor(e){super(`Queue is full (maxQueue=${e})`),this.maxQueue=e}},r=class extends e{},i=class extends e{constructor(e=`Worker was terminated`){super(e)}},a=class extends e{timeoutMs;constructor(e){super(`Task timed out after ${e}ms`),this.timeoutMs=e}},o=class extends e{};function s(e){return e?.reason??new DOMException(`Aborted`,`AbortError`)}var c=class{heap=[];liveCount=0;seq=0;get size(){return this.liveCount}enqueue(e,t){return t!==void 0&&this.liveCount>=t?!1:(this.heap.push({_seq:this.seq++,item:e}),this.siftUp(this.heap.length-1),this.liveCount++,!0)}shift(){for(;this.heap.length>0;){let{item:e}=this.extractTop();if(!e.cancelled)return this.liveCount--,e}}remove(e){if(e.cancelled)return!1;for(let t=0;t<this.heap.length;t++)if(this.heap[t].item===e)return e.cancelled=!0,this.liveCount--,!0;return!1}extractTop(){let e=this.heap[0],t=this.heap.pop();return this.heap.length>0&&(this.heap[0]=t,this.siftDown(0)),e}higher(e,t){return e.item.priority===t.item.priority?e._seq<t._seq:e.item.priority>t.item.priority}siftUp(e){for(;e>0;){let t=e-1>>1;if(this.higher(this.heap[e],this.heap[t]))[this.heap[e],this.heap[t]]=[this.heap[t],this.heap[e]],e=t;else break}}siftDown(e){let t=this.heap.length;for(;;){let n=e,r=2*e+1,i=2*e+2;if(r<t&&this.higher(this.heap[r],this.heap[n])&&(n=r),i<t&&this.higher(this.heap[i],this.heap[n])&&(n=i),n===e)break;[this.heap[e],this.heap[n]]=[this.heap[n],this.heap[e]],e=n}}};function l(e){typeof e==`object`&&e&&`unref`in e&&e.unref()}function u(e,t){let{concurrency:r,defaultTimeout:u,maxQueue:d,onFull:f}=t,p=[...e],m=new c,h=new AbortController,g=[],_=[],v=0,y,b=0,x=0,S=0,C=!1;function w(){return v===0&&m.size===0}function T(){if(!w()||g.length===0)return;let e=g.splice(0);for(let t of e)t()}function E(e){return w()?Promise.resolve():new Promise((t,n)=>{g.push(t),e!==void 0&&l(setTimeout(()=>{let r=g.indexOf(t);r!==-1&&g.splice(r,1),n(new a(e))},e))})}function D(){let e=_.shift();e&&e()}let O=!1;function k(){if(!(C||O)){for(O=!0;!C&&p.length>0&&m.size>0;){let e=A();if(!e)break;let t=p.pop();e.cleanupAbort?.(),v+=1,D();let n=e.timeout??u;t.run(e.input,e.transferables,n).then(n=>{p.push(t),--v,b+=1,e.resolve(n),k(),w()&&T()},n=>{p.push(t),--v,n instanceof i||(x+=1),e.reject(n),k(),w()&&T()})}O=!1}}function A(){for(;m.size>0;){let e=m.shift();if(!e)break;if(e.signal?.aborted){e.cleanupAbort?.(),e.reject(s(e.signal));continue}return e}T()}function j(){if(C)return;C=!0,h.abort();for(let t of e)t.terminate();for(;m.size>0;){let e=m.shift();if(!e)break;e.cleanupAbort?.(),e.reject(new i)}let t=g.splice(0);for(let e of t)e();for(let e of _.splice(0))e()}function M(e){return C?Promise.resolve():y||(y=E(e).then(j,e=>{throw j(),e}),y)}async function N(e,t={}){let{priority:r=0,signal:a,timeout:o,transferables:c=[]}=t;if(C)throw new i;if(y)throw new i(`Worker is draining`);if(a?.aborted)throw s(a);if(f===`wait`&&d!==void 0){for(;!C&&!y&&m.size>=d;)await new Promise(e=>_.push(e));if(C)throw new i;if(y)throw new i(`Worker is draining`)}let l,u,p=new Promise((e,t)=>{l=e,u=t}),h={input:e,priority:r,reject:u,resolve:l,signal:a,timeout:o,transferables:c};if(!m.enqueue(h,f===`wait`?void 0:d))throw new n(d);if(a){let e=()=>{m.remove(h)&&(h.cleanupAbort?.(),u(s(a)),T())};h.cleanupAbort=()=>{a.removeEventListener(`abort`,e),h.cleanupAbort=void 0},a.addEventListener(`abort`,e,{once:!0})}return k(),p}function P(t,n={}){if(C)throw new o(`Worker was terminated`);let r=p.pop();if(!r)throw new o(`runStream() requires a free worker slot; all ${e.length} slot${e.length===1?``:`s`} are busy`);let{timeout:i,transferables:a=[]}=n,s=r.runStream(t,a,i);return{[Symbol.asyncIterator](){let e=s[Symbol.asyncIterator](),t=!1,n=()=>{t||(t=!0,p.push(r))};return{async next(){let t=await e.next();return t.done&&n(),t},async return(t){return r.cancel(),n(),e.return?.(t)??{done:!0,value:t}},async throw(t){if(r.cancel(),n(),e.throw)return e.throw(t);throw t}}}}}async function*F(e,t={}){if(e.length===0)return;let{ordered:n=!0,...i}=t,a=new AbortController;try{if(n){let t=e.map(e=>N(e,{...i,signal:a.signal}));for(let e of t)yield await e}else{let t=[],n=null,s=0,c=Math.max(1,r);function o(){if(s>=e.length)return;let r=e[s++];N(r,{...i,signal:a.signal}).then(e=>{t.push({value:e}),n?.(),n=null},e=>{t.push({error:e}),n?.(),n=null})}for(let t=0;t<c&&t<e.length;t++)o();for(let r=0;r<e.length;r++){for(;t.length===0;)await new Promise(e=>{n=e});let e=t.shift();if(`error`in e)throw e.error;yield e.value,o()}}}finally{a.abort()}}function I(e,t={}){let n=new AbortController;if(t.signal){let e=t.signal;e.aborted?n.abort(e.reason):e.addEventListener(`abort`,()=>n.abort(e.reason),{once:!0})}S+=1;let r=0,i=0,a=!1,o=[];function s(){!a&&r>0&&r===i&&(a=!0,--S)}return{abort(e){n.abort(e)},drain(){let e=o.splice(0);return a||(a=!0,--S),Promise.allSettled(e)},get name(){return e},get pending(){return r-i},run(e,t={}){r+=1;let a=N(e,{...t,signal:n.signal});return o.push(a),a.then(()=>{i+=1,s()},()=>{i+=1,s()}),a},get size(){return r}}}function L(){return C?`terminated`:v>0||m.size>0?`running`:`idle`}return{get active(){return v},batch:F,get completed(){return b},get concurrency(){return r},get disposalSignal(){return h.signal},dispose:j,get disposed(){return C},drain:M,get failed(){return x},group:I,get groupCount(){return S},prime(){return Promise.all(e.map(e=>e.prime())).then(()=>{})},get queued(){return m.size},run:N,runStream:P,get status(){return L()},[Symbol.asyncDispose]:()=>M(),[Symbol.dispose]:j}}function d(e){if(e.toString().includes(`[native code]`))throw new t(`Task function cannot be a bound or native function`);return e}var f=512;function p(e){if(e===void 0)return 1;if(e===`auto`)return Math.max(1,globalThis.navigator?.hardwareConcurrency??1);if(!Number.isInteger(e)||e<1||e>f)throw new t(`\`concurrency\` must be a positive integer ≤ ${f} or "auto"`);return e}function m(e={}){let n=p(e.concurrency),{heartbeatWindow:r,maxQueue:i,onFull:a=`reject`,onSlotError:o,timeout:s}=e;if(s!==void 0&&(!Number.isFinite(s)||s<=0))throw new t("`timeout` must be a finite number greater than 0");if(i!==void 0&&(!Number.isInteger(i)||i<1))throw new t("`maxQueue` must be a positive integer");if(r!==void 0&&(!Number.isFinite(r)||r<=0))throw new t("`heartbeatWindow` must be a finite number greater than 0");return{concurrency:n,heartbeatWindow:r,maxQueue:i,onFull:a,onSlotError:o,timeout:s}}function h(e,t){return`
2
2
  const __fn = (${e.toString()});
3
3
 
4
4
  self.onmessage = async function (event) {
@@ -24,5 +24,5 @@ self.onmessage = async function (event) {
24
24
  } finally {
25
25
  if (heartbeatTimer) clearInterval(heartbeatTimer);
26
26
  }
27
- }`.trim()}var v=class{config;onSlotError;disposed=!1;pending=null;taskId=0;worker=null;constructor(e,t){this.config=e,this.onSlotError=t}prime(){if(this.disposed)return Promise.resolve();try{this.ensureWorker()}catch{}return Promise.resolve()}run(e,t,n){return this.dispatch(e,t,n,!1)}runStream(e,t,n){let r=[],i=!1,a,o=[],s=e=>{r.push(e),o.shift()?.()},c=e=>{i=!0,a=e;for(let e of o.splice(0))e()};return this.dispatch(e,t,n,!0,s).then(()=>c(),c),this.pending&&(this.pending.finishStream=c),{[Symbol.asyncIterator](){let e=0;return{async next(){for(;e>=r.length&&!i;)await new Promise(e=>o.push(e));if(e<r.length){let t=r[e];return r[e]=null,e++,{done:!1,value:t}}if(a!==void 0)throw a;return{done:!0,value:void 0}}}}}}cancel(){let e=this.pending;e&&(clearTimeout(e.timer),clearTimeout(e.heartbeatWatchdog),this.pending=null,this.stopWorker(),e.finishStream?.(new a(`Stream was cancelled`)))}terminate(){this.disposed=!0,this.stopWorker(),this.failPending(new a)}dispatch(e,t,n,r,i){if(this.disposed)return Promise.reject(new a);let c;try{c=this.ensureWorker()}catch(e){return Promise.reject(e)}let l=this.config.kind===`inline`&&this.config.heartbeatInterval!=null?this.config.heartbeatInterval*2:void 0;return new Promise((a,u)=>{let f=this.taskId++,p={emit:i,id:f,reject:u,resolve:a,watchdogMs:l};n!==void 0&&(p.timer=setTimeout(()=>{this.restart(new o(n))},n),d(p.timer)),l!==void 0&&(p.heartbeatWatchdog=setTimeout(()=>{this.restart(new o(l))},l),d(p.heartbeatWatchdog)),this.pending=p;try{c.postMessage({id:f,input:e,stream:r},t)}catch(e){this.failPending(new s(e instanceof Error?e.message:String(e),{cause:e}))}})}ensureWorker(){if(this.worker)return this.worker;if(typeof globalThis.Worker!=`function`)throw new s(`Worker API is unavailable in this runtime`);let e;if(this.config.kind===`module`)try{e=new Worker(this.config.url,{type:`module`})}catch(e){throw new s(`Failed to create Worker`,{cause:e})}else try{let t=new Blob([_(this.config.fn,this.config.heartbeatInterval)],{type:`application/javascript`}),n=URL.createObjectURL(t);try{e=new Worker(n)}finally{URL.revokeObjectURL(n)}}catch(e){throw new s(`Failed to create Worker`,{cause:e})}return e.onmessage=e=>{let t=this.pending;if(!(!t||e.data.id!==t.id)){if(`heartbeat`in e.data){t.watchdogMs!==void 0&&(clearTimeout(t.heartbeatWatchdog),t.heartbeatWatchdog=setTimeout(()=>{this.restart(new o(t.watchdogMs))},t.watchdogMs),d(t.heartbeatWatchdog));return}if(`chunk`in e.data){t.emit?.(e.data.chunk);return}if(clearTimeout(t.timer),clearTimeout(t.heartbeatWatchdog),this.pending=null,`error`in e.data){let n=e.data.error instanceof Error?e.data.error:Error(String(e.data.error));t.reject(new i(n.message,{cause:n}))}else t.resolve(e.data.result)}},e.onerror=e=>{let t=new s(e.message);this.stopWorker(),this.failPending(t),this.onSlotError?.(t,()=>void this.prime())},this.worker=e,e}failPending(e){let t=this.pending;t&&(clearTimeout(t.timer),clearTimeout(t.heartbeatWatchdog),this.pending=null,t.reject(e))}restart(e){this.stopWorker(),this.failPending(e)}stopWorker(){this.worker&&=(this.worker.terminate(),null)}};function y(e,t){let{concurrency:n,heartbeatWindow:r,maxQueue:i,onFull:a,onSlotError:o,timeout:s}=g(t),c=r==null?void 0:Math.floor(r/2);return f(Array.from({length:n},()=>new v({fn:e,heartbeatInterval:c,kind:`inline`},o)),{concurrency:n,defaultTimeout:s,maxQueue:i,onFull:a})}function b(e,t){let{concurrency:n,heartbeatWindow:r,maxQueue:i,onFull:a,onSlotError:o,timeout:s}=g(t);r!==void 0&&l("`heartbeatWindow` has no effect on module workers — the worker script must implement the heartbeat protocol manually.");let c=typeof e==`string`?e:e.href;return f(Array.from({length:n},()=>new v({kind:`module`,url:c},o)),{concurrency:n,defaultTimeout:s,maxQueue:i,onFull:a})}exports.FamiliarError=t,exports.FamiliarInvalidOptionsError=n,exports.FamiliarQueueFullError=r,exports.FamiliarRuntimeError=s,exports.FamiliarTaskError=i,exports.FamiliarTerminatedError=a,exports.FamiliarTimeoutError=o,exports.createModuleWorker=b,exports.createWorker=y,exports.task=p;
27
+ }`.trim()}var g=class{config;onSlotError;disposed=!1;pending=null;taskId=0;worker=null;constructor(e,t){this.config=e,this.onSlotError=t}prime(){if(this.disposed)return Promise.resolve();try{this.ensureWorker()}catch{}return Promise.resolve()}run(e,t,n){return this.dispatch(e,t,n,!1)}runStream(e,t,n){let r=[],i=!1,a,o=[],s=e=>{r.push(e),o.shift()?.()},c=e=>{i=!0,a=e;for(let e of o.splice(0))e()};return this.dispatch(e,t,n,!0,s).then(()=>c(),c),this.pending&&(this.pending.finishStream=c),{[Symbol.asyncIterator](){let e=0;return{async next(){for(;e>=r.length&&!i;)await new Promise(e=>o.push(e));if(e<r.length){let t=r[e];return r[e]=null,e++,{done:!1,value:t}}if(a!==void 0)throw a;return{done:!0,value:void 0}}}}}}cancel(){let e=this.pending;e&&(clearTimeout(e.timer),clearTimeout(e.heartbeatWatchdog),this.pending=null,this.stopWorker(),e.finishStream?.(new i(`Stream was cancelled`)))}terminate(){this.disposed=!0,this.stopWorker(),this.failPending(new i)}dispatch(e,t,n,r,s){if(this.disposed)return Promise.reject(new i);let c;try{c=this.ensureWorker()}catch(e){return Promise.reject(e)}let u=this.config.kind===`inline`&&this.config.heartbeatInterval!=null?this.config.heartbeatInterval*2:void 0;return new Promise((i,d)=>{let f=this.taskId++,p={emit:s,id:f,reject:d,resolve:i,watchdogMs:u};n!==void 0&&(p.timer=setTimeout(()=>{this.restart(new a(n))},n),l(p.timer)),u!==void 0&&(p.heartbeatWatchdog=setTimeout(()=>{this.restart(new a(u))},u),l(p.heartbeatWatchdog)),this.pending=p;try{c.postMessage({id:f,input:e,stream:r},t)}catch(e){this.failPending(new o(e instanceof Error?e.message:String(e),{cause:e}))}})}ensureWorker(){if(this.worker)return this.worker;if(typeof globalThis.Worker!=`function`)throw new o(`Worker API is unavailable in this runtime`);let e;if(this.config.kind===`module`)try{e=new Worker(this.config.url,{type:`module`})}catch(e){throw new o(`Failed to create Worker`,{cause:e})}else try{let t=new Blob([h(this.config.fn,this.config.heartbeatInterval)],{type:`application/javascript`}),n=URL.createObjectURL(t);try{e=new Worker(n)}finally{URL.revokeObjectURL(n)}}catch(e){throw new o(`Failed to create Worker`,{cause:e})}return e.onmessage=e=>{let t=this.pending;if(!(!t||e.data.id!==t.id)){if(`heartbeat`in e.data){t.watchdogMs!==void 0&&(clearTimeout(t.heartbeatWatchdog),t.heartbeatWatchdog=setTimeout(()=>{this.restart(new a(t.watchdogMs))},t.watchdogMs),l(t.heartbeatWatchdog));return}if(`chunk`in e.data){t.emit?.(e.data.chunk);return}if(clearTimeout(t.timer),clearTimeout(t.heartbeatWatchdog),this.pending=null,`error`in e.data){let n=e.data.error instanceof Error?e.data.error:Error(String(e.data.error));t.reject(new r(n.message,{cause:n}))}else t.resolve(e.data.result)}},e.onerror=e=>{let t=new o(e.message);this.stopWorker(),this.failPending(t),this.onSlotError?.(t,()=>void this.prime())},this.worker=e,e}failPending(e){let t=this.pending;t&&(clearTimeout(t.timer),clearTimeout(t.heartbeatWatchdog),this.pending=null,t.reject(e))}restart(e){this.stopWorker(),this.failPending(e)}stopWorker(){this.worker&&=(this.worker.terminate(),null)}};function _(e,t){let{concurrency:n,heartbeatWindow:r,maxQueue:i,onFull:a,onSlotError:o,timeout:s}=m(t),c=r==null?void 0:Math.floor(r/2);return u(Array.from({length:n},()=>new g({fn:e,heartbeatInterval:c,kind:`inline`},o)),{concurrency:n,defaultTimeout:s,maxQueue:i,onFull:a})}function v(e,t){let{concurrency:n,heartbeatWindow:r,maxQueue:i,onFull:a,onSlotError:o,timeout:s}=m(t),c=typeof e==`string`?e:e.href;return u(Array.from({length:n},()=>new g({kind:`module`,url:c},o)),{concurrency:n,defaultTimeout:s,maxQueue:i,onFull:a})}exports.FamiliarError=e,exports.FamiliarInvalidOptionsError=t,exports.FamiliarQueueFullError=n,exports.FamiliarRuntimeError=o,exports.FamiliarTaskError=r,exports.FamiliarTerminatedError=i,exports.FamiliarTimeoutError=a,exports.createModuleWorker=v,exports.createWorker=_,exports.task=d;
28
28
  //# sourceMappingURL=familiar.cjs.map