@vielzeug/familiar 1.0.8 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +24 -47
  2. package/dist/_pool.cjs +1 -1
  3. package/dist/_pool.cjs.map +1 -1
  4. package/dist/_pool.d.ts +4 -12
  5. package/dist/_pool.d.ts.map +1 -1
  6. package/dist/_pool.js +163 -222
  7. package/dist/_pool.js.map +1 -1
  8. package/dist/_queue.cjs +1 -1
  9. package/dist/_queue.cjs.map +1 -1
  10. package/dist/_queue.d.ts +3 -39
  11. package/dist/_queue.d.ts.map +1 -1
  12. package/dist/_queue.js +27 -28
  13. package/dist/_queue.js.map +1 -1
  14. package/dist/_stream-pool.cjs +2 -0
  15. package/dist/_stream-pool.cjs.map +1 -0
  16. package/dist/_stream-pool.d.ts +11 -0
  17. package/dist/_stream-pool.d.ts.map +1 -0
  18. package/dist/_stream-pool.js +152 -0
  19. package/dist/_stream-pool.js.map +1 -0
  20. package/dist/errors.cjs.map +1 -1
  21. package/dist/errors.d.ts +1 -1
  22. package/dist/errors.d.ts.map +1 -1
  23. package/dist/errors.js.map +1 -1
  24. package/dist/familiar.cjs +1 -27
  25. package/dist/familiar.cjs.map +1 -1
  26. package/dist/familiar.iife.js +1 -27
  27. package/dist/familiar.iife.js.map +1 -1
  28. package/dist/familiar.js +1 -27
  29. package/dist/familiar.js.map +1 -1
  30. package/dist/index.cjs +1 -1
  31. package/dist/index.js +3 -2
  32. package/dist/protocol.cjs +1 -1
  33. package/dist/protocol.cjs.map +1 -1
  34. package/dist/protocol.d.ts +40 -44
  35. package/dist/protocol.d.ts.map +1 -1
  36. package/dist/protocol.js +42 -27
  37. package/dist/protocol.js.map +1 -1
  38. package/dist/testing/testing.cjs +1 -1
  39. package/dist/testing/testing.cjs.map +1 -1
  40. package/dist/testing/testing.d.ts +15 -22
  41. package/dist/testing/testing.d.ts.map +1 -1
  42. package/dist/testing/testing.js +53 -35
  43. package/dist/testing/testing.js.map +1 -1
  44. package/dist/types.d.ts +49 -119
  45. package/dist/types.d.ts.map +1 -1
  46. package/dist/worker.cjs +1 -27
  47. package/dist/worker.cjs.map +1 -1
  48. package/dist/worker.d.ts +11 -67
  49. package/dist/worker.d.ts.map +1 -1
  50. package/dist/worker.js +139 -187
  51. package/dist/worker.js.map +1 -1
  52. package/package.json +2 -2
  53. package/dist/_dev.cjs +0 -2
  54. package/dist/_dev.cjs.map +0 -1
  55. package/dist/_dev.js +0 -6
  56. package/dist/_dev.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"familiar.js","names":[],"sources":["../src/errors.ts","../src/_dev.ts","../../arsenal/dist/async/abortError.js","../src/_queue.ts","../src/_timers.ts","../src/_pool.ts","../src/worker.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","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","//#region src/async/abortError.ts\nfunction e(e) {\n\treturn e?.reason ?? new DOMException(\"Aborted\", \"AbortError\");\n}\n//#endregion\nexport { e as abortError };\n\n//# sourceMappingURL=abortError.js.map","/**\n * Internal priority-queue for @vielzeug/familiar.\n * Not part of the public API surface.\n *\n * Uses a binary max-heap ordered by priority (higher value = runs first), with\n * insertion-sequence tiebreaking to ensure FIFO ordering within the same priority.\n * Cancelled items are marked lazily and silently skipped by shift().\n *\n * Complexity:\n * enqueue O(log n)\n * shift O(log n) amortised (lazy cancellation may scan multiple cancelled tops)\n * remove O(n) scan + O(1) mark — adequate for typical queue depths\n */\n\nexport type QueueItem<TInput, TOutput> = {\n /** Marked true by remove(); the next shift() discards the item silently. */\n cancelled?: boolean;\n cleanupAbort?: () => void;\n input: TInput;\n /** Scheduling priority. Higher value = runs first. Default: 0. */\n priority: number;\n reject: (reason: unknown) => void;\n resolve: (value: TOutput) => void;\n signal?: AbortSignal;\n timeout?: number;\n transferables: Transferable[];\n};\n\n/**\n * Internal heap node: wraps a QueueItem with an insertion sequence for FIFO tiebreaking.\n * Using a wrapper instead of mutating QueueItem keeps queue items free of internal\n * scheduling metadata (_seq).\n */\ntype HeapEntry<TInput, TOutput> = { readonly _seq: number; readonly item: QueueItem<TInput, TOutput> };\n\nexport class TaskQueue<TInput, TOutput> {\n private readonly heap: HeapEntry<TInput, TOutput>[] = [];\n /** Accurate count of live (non-cancelled) items. */\n private liveCount = 0;\n private seq = 0;\n\n /** Number of live (non-cancelled) items. Always accurate. */\n get size(): number {\n return this.liveCount;\n }\n\n /**\n * Enqueue item. Returns false (without modifying state) when maxQueue is set and\n * liveCount has already reached it; returns true on success.\n */\n enqueue(item: QueueItem<TInput, TOutput>, maxQueue: number | undefined): boolean {\n if (maxQueue !== undefined && this.liveCount >= maxQueue) return false;\n\n this.heap.push({ _seq: this.seq++, item });\n this.siftUp(this.heap.length - 1);\n this.liveCount++;\n\n return true;\n }\n\n /**\n * Dequeue the highest-priority live item. Silently discards cancelled tombstones.\n * Returns undefined only when there are no live items left.\n */\n shift(): QueueItem<TInput, TOutput> | undefined {\n while (this.heap.length > 0) {\n const { item } = this.extractTop();\n\n if (!item.cancelled) {\n this.liveCount--;\n\n return item;\n }\n // Cancelled: liveCount was already decremented in remove(). Just discard and continue.\n }\n\n return undefined;\n }\n\n /**\n * Mark item as cancelled (O(n) scan). The heap slot is reclaimed lazily by the next shift().\n * Returns true when found and marked, false when the item is not in the queue.\n */\n remove(item: QueueItem<TInput, TOutput>): boolean {\n if (item.cancelled) return false;\n\n for (let i = 0; i < this.heap.length; i++) {\n if (this.heap[i]!.item === item) {\n item.cancelled = true;\n this.liveCount--;\n\n return true;\n }\n }\n\n return false;\n }\n\n // ─── Heap internals ────────────────────────────────────────────────────────\n\n private extractTop(): HeapEntry<TInput, TOutput> {\n const top = this.heap[0]!;\n const last = this.heap.pop()!;\n\n if (this.heap.length > 0) {\n this.heap[0] = last;\n this.siftDown(0);\n }\n\n return top;\n }\n\n /** Returns true when entry a should be dispatched before entry b. */\n private higher(a: HeapEntry<TInput, TOutput>, b: HeapEntry<TInput, TOutput>): boolean {\n if (a.item.priority !== b.item.priority) return a.item.priority > b.item.priority;\n\n return a._seq < b._seq; // earlier insertion wins for equal priority (FIFO)\n }\n\n private siftUp(i: number): void {\n while (i > 0) {\n const parent = (i - 1) >> 1;\n\n if (this.higher(this.heap[i]!, this.heap[parent]!)) {\n [this.heap[i], this.heap[parent]] = [this.heap[parent]!, this.heap[i]!];\n i = parent;\n } else {\n break;\n }\n }\n }\n\n private siftDown(i: number): void {\n const n = this.heap.length;\n\n for (;;) {\n let top = i;\n const left = 2 * i + 1;\n const right = 2 * i + 2;\n\n if (left < n && this.higher(this.heap[left]!, this.heap[top]!)) top = left;\n\n if (right < n && this.higher(this.heap[right]!, this.heap[top]!)) top = right;\n\n if (top === i) break;\n\n [this.heap[i], this.heap[top]] = [this.heap[top]!, this.heap[i]!];\n i = top;\n }\n }\n}\n","/**\n * Node compatibility helper: `unref()`s a timer so a pending per-task timeout/watchdog does not\n * keep a Node event loop (e.g. a Worker polyfill) alive. Browsers return a plain number from\n * `setTimeout`/`setInterval`, so this is a no-op there.\n *\n * Not part of the public API surface.\n */\nexport function unrefTimer(timer: ReturnType<typeof setTimeout> | ReturnType<typeof setInterval>): void {\n if (typeof timer === 'object' && timer !== null && 'unref' in timer) {\n (timer as { unref(): void }).unref();\n }\n}\n","/**\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","// Re-export all public types and classes so consumers only need one import.\nexport type {\n BatchOptions,\n GroupOptions,\n RunOptions,\n TaskFn,\n TaskGroup,\n WorkerHandle,\n WorkerOptions,\n WorkerStatus,\n} from './types';\nexport {\n FamiliarError,\n FamiliarInvalidOptionsError,\n FamiliarQueueFullError,\n FamiliarRuntimeError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n FamiliarTimeoutError,\n} from './errors';\n\nimport type { SlotStrategy, TaskFn, WorkerHandle, WorkerOptions } from './types';\n\nimport { warn } from './_dev';\nimport { createPool } from './_pool';\nimport { unrefTimer } from './_timers';\nimport {\n FamiliarInvalidOptionsError,\n FamiliarRuntimeError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n FamiliarTimeoutError,\n} from './errors';\n\n// ─── task() — optional validation helper ─────────────────────────────────────\n\n/**\n * Optional helper that validates a function is safe to serialize for use in `createWorker`.\n *\n * `createWorker` accepts any `TaskFn` directly — this helper exists only to catch the common\n * mistake of passing a bound or native function whose body cannot be serialized.\n *\n * IMPORTANT: The function is serialized via `.toString()` and runs in an isolated Worker scope.\n * It **cannot** close over variables from the surrounding module — any outer reference resolves\n * to `undefined` inside the worker.\n *\n * @throws FamiliarInvalidOptionsError if the function is bound or native.\n *\n * @example\n * // Without task() — works fine for plain arrow functions:\n * const worker = createWorker((n: number) => n * 2);\n *\n * // With task() — catches the mistake of passing Math.sqrt directly:\n * const worker = createWorker(task((n: number) => Math.sqrt(n)));\n */\nexport function task<TInput, TOutput>(fn: TaskFn<TInput, TOutput>): TaskFn<TInput, TOutput> {\n if (fn.toString().includes('[native code]')) {\n throw new FamiliarInvalidOptionsError('Task function cannot be a bound or native function');\n }\n\n return fn;\n}\n\n// ─── Options resolution ───────────────────────────────────────────────────────\n\n/** Upper bound on `concurrency`: generous headroom over realistic hardware/IO limits while still catching obvious misconfiguration (e.g. a typo like `50000`). */\nconst MAX_CONCURRENCY = 512;\n\nfunction resolveConcurrency(value: WorkerOptions['concurrency']): number {\n if (value === undefined) return 1;\n\n if (value === 'auto') {\n return Math.max(1, globalThis.navigator?.hardwareConcurrency ?? 1);\n }\n\n if (!Number.isInteger(value) || value < 1 || value > MAX_CONCURRENCY) {\n throw new FamiliarInvalidOptionsError(`\\`concurrency\\` must be a positive integer ≤ ${MAX_CONCURRENCY} or \"auto\"`);\n }\n\n return value;\n}\n\nfunction resolveOptions(options: WorkerOptions = {}): {\n concurrency: number;\n heartbeatWindow: number | undefined;\n maxQueue: number | undefined;\n onFull: 'reject' | 'wait';\n onSlotError: WorkerOptions['onSlotError'];\n timeout: number | undefined;\n} {\n const concurrency = resolveConcurrency(options.concurrency);\n const { heartbeatWindow, maxQueue, onFull = 'reject', onSlotError, timeout } = options;\n\n if (timeout !== undefined && (!Number.isFinite(timeout) || timeout <= 0)) {\n throw new FamiliarInvalidOptionsError('`timeout` must be a finite number greater than 0');\n }\n\n if (maxQueue !== undefined && (!Number.isInteger(maxQueue) || maxQueue < 1)) {\n throw new FamiliarInvalidOptionsError('`maxQueue` must be a positive integer');\n }\n\n if (heartbeatWindow !== undefined && (!Number.isFinite(heartbeatWindow) || heartbeatWindow <= 0)) {\n throw new FamiliarInvalidOptionsError('`heartbeatWindow` must be a finite number greater than 0');\n }\n\n return { concurrency, heartbeatWindow, maxQueue, onFull, onSlotError, timeout };\n}\n\n// ─── Worker script builder ────────────────────────────────────────────────────\n\n/**\n * @security `fn.toString()` is injected verbatim into the blob script. Any code embedded in the\n * serialized function executes only inside the isolated Worker scope — no shared memory, no access\n * to the host page's globals. Risk is therefore low, but callers should only pass plain functions\n * that do not close over module scope.\n */\nfunction buildWorkerScript(fn: TaskFn<unknown, unknown>, heartbeatInterval: number | undefined): string {\n return `\nconst __fn = (${fn.toString()});\n\nself.onmessage = async function (event) {\n const { id, input, stream } = event.data;\n\n // Automatically send heartbeats at half the heartbeatWindow interval.\n let heartbeatTimer = null;\n ${heartbeatInterval != null ? `heartbeatTimer = setInterval(() => self.postMessage({ id, heartbeat: true }), ${heartbeatInterval});` : ''}\n\n try {\n if (stream) {\n const iterable = await __fn(input);\n for await (const chunk of iterable) {\n self.postMessage({ id, chunk });\n }\n self.postMessage({ id, result: undefined });\n } else {\n const result = await __fn(input);\n self.postMessage({ id, result });\n }\n } catch (error) {\n self.postMessage({ id, error });\n } finally {\n if (heartbeatTimer) clearInterval(heartbeatTimer);\n }\n}`.trim();\n}\n\n// ─── SlotConfig discriminated union ──────────────────────────────────────────\n\ntype SlotConfig<TInput, TOutput> =\n | { fn: TaskFn<TInput, TOutput>; heartbeatInterval: number | undefined; kind: 'inline' }\n | { kind: 'module'; url: string };\n\n// ─── PendingTask ─────────────────────────────────────────────────────────────\n\ntype SlotMessage<TOutput> =\n | { error: unknown; id: number }\n | { chunk: TOutput; id: number }\n | { heartbeat: true; id: number }\n | { id: number; result: TOutput };\n\ntype PendingTask<TOutput> = {\n /** Emits intermediate stream chunks. Undefined for non-streaming tasks. */\n emit?: (chunk: TOutput) => void;\n /** Called with an error when the streaming dispatch is cancelled mid-flight, so the finish closure drains its waiters. */\n finishStream?: (err: unknown) => void;\n /**\n * Single-shot timer that fires if no heartbeat is received within watchdogMs.\n * Reset (cleared + recreated) on every incoming heartbeat message.\n * Uses setTimeout because the watchdog is conceptually one-shot with manual reset,\n * not a recurring interval.\n */\n heartbeatWatchdog?: ReturnType<typeof setTimeout>;\n id: number;\n reject: (reason: unknown) => void;\n resolve: (value: TOutput) => void;\n timer?: ReturnType<typeof setTimeout>;\n /** Watchdog window in ms (= heartbeatWindow). Stored on PendingTask to reset the timer on each heartbeat. */\n watchdogMs?: number;\n};\n\n// ─── Slot — implements SlotStrategy ──────────────────────────────────────────\n\nclass Slot<TInput, TOutput> implements SlotStrategy<TInput, TOutput> {\n private readonly config: SlotConfig<TInput, TOutput>;\n private readonly onSlotError: WorkerOptions['onSlotError'];\n private disposed = false;\n private pending: PendingTask<TOutput> | null = null;\n private taskId = 0;\n private worker: Worker | null = null;\n\n constructor(config: SlotConfig<TInput, TOutput>, onSlotError?: WorkerOptions['onSlotError']) {\n this.config = config;\n this.onSlotError = onSlotError;\n }\n\n prime(): Promise<void> {\n if (this.disposed) return Promise.resolve();\n\n try {\n this.ensureWorker();\n } catch {\n // Best-effort — errors surface on the first run() call.\n }\n\n return Promise.resolve();\n }\n\n run(input: TInput, transferables: Transferable[], timeout: number | undefined): Promise<TOutput> {\n return this.dispatch(input, transferables, timeout, false) as Promise<TOutput>;\n }\n\n runStream(input: TInput, transferables: Transferable[], timeout: number | undefined): AsyncIterable<TOutput> {\n const chunks: TOutput[] = [];\n let done = false;\n let error: unknown;\n const waiters: Array<() => void> = [];\n\n const emit = (chunk: TOutput) => {\n chunks.push(chunk);\n waiters.shift()?.();\n };\n\n const finish = (err?: unknown) => {\n done = true;\n error = err;\n\n for (const w of waiters.splice(0)) w();\n };\n\n // Dispatch the task in stream mode. The promise resolves when the worker signals done.\n // finishStream is stored on the PendingTask so cancel() can drain the waiters if the\n // consumer exits early (break/throw from for-await), preventing a permanently dangling Promise.\n // this.pending is set synchronously inside dispatch(), so this assignment is safe.\n this.dispatch(input, transferables, timeout, true, emit).then(() => finish(), finish);\n\n if (this.pending) this.pending.finishStream = finish;\n\n return {\n [Symbol.asyncIterator]() {\n let cursor = 0;\n\n return {\n async next(): Promise<IteratorResult<TOutput>> {\n while (cursor >= chunks.length && !done) {\n await new Promise<void>((resolve) => waiters.push(resolve));\n }\n\n if (cursor < chunks.length) {\n const value = chunks[cursor]!;\n\n // Null-out the consumed slot so GC can collect the value\n // without waiting for the entire stream to close.\n (chunks as (TOutput | null)[])[cursor] = null;\n cursor++;\n\n return { done: false, value };\n }\n\n if (error !== undefined) throw error;\n\n return { done: true, value: undefined as unknown as TOutput };\n },\n };\n },\n };\n }\n\n cancel(): void {\n const pending = this.pending;\n\n if (!pending) return;\n\n clearTimeout(pending.timer);\n clearTimeout(pending.heartbeatWatchdog);\n this.pending = null;\n // Terminate the worker: the streaming task may still be running and sending chunks.\n // A fresh worker is created on the next run() or runStream() call via ensureWorker().\n this.stopWorker();\n // Drain the stream finish closure so any pending .next() waiters resolve immediately\n // rather than leaking as permanently dangling Promises.\n pending.finishStream?.(new FamiliarTerminatedError('Stream was cancelled'));\n }\n\n terminate(): void {\n this.disposed = true;\n this.stopWorker();\n this.failPending(new FamiliarTerminatedError());\n }\n\n private dispatch(\n input: TInput,\n transferables: Transferable[],\n timeout: number | undefined,\n stream: boolean,\n emit?: (chunk: TOutput) => void,\n ): Promise<TOutput | void> {\n if (this.disposed) {\n return Promise.reject(new FamiliarTerminatedError());\n }\n\n let worker: Worker;\n\n try {\n worker = this.ensureWorker();\n } catch (error) {\n return Promise.reject(error);\n }\n\n // watchdogMs = heartbeatInterval * 2 (Nyquist margin: worker beats at interval, host allows 2× before firing).\n const watchdogMs =\n this.config.kind === 'inline' && this.config.heartbeatInterval != null\n ? this.config.heartbeatInterval * 2\n : undefined;\n\n return new Promise<TOutput | void>((resolve, reject) => {\n const id = this.taskId++;\n const pending: PendingTask<TOutput> = {\n emit,\n id,\n reject,\n resolve: resolve as (v: TOutput) => void,\n watchdogMs,\n };\n\n if (timeout !== undefined) {\n pending.timer = setTimeout(() => {\n this.restart(new FamiliarTimeoutError(timeout));\n }, timeout);\n unrefTimer(pending.timer);\n }\n\n if (watchdogMs !== undefined) {\n pending.heartbeatWatchdog = setTimeout(() => {\n this.restart(new FamiliarTimeoutError(watchdogMs));\n }, watchdogMs);\n unrefTimer(pending.heartbeatWatchdog);\n }\n\n this.pending = pending;\n\n try {\n worker.postMessage({ id, input, stream }, transferables);\n } catch (err) {\n this.failPending(new FamiliarRuntimeError(err instanceof Error ? err.message : String(err), { cause: err }));\n }\n });\n }\n\n private ensureWorker(): Worker {\n if (this.worker) return this.worker;\n\n if (typeof globalThis.Worker !== 'function') {\n throw new FamiliarRuntimeError('Worker API is unavailable in this runtime');\n }\n\n let worker: Worker;\n\n if (this.config.kind === 'module') {\n try {\n worker = new Worker(this.config.url, { type: 'module' });\n } catch (error) {\n throw new FamiliarRuntimeError('Failed to create Worker', { cause: error });\n }\n } else {\n try {\n const blob = new Blob(\n [buildWorkerScript(this.config.fn as TaskFn<unknown, unknown>, this.config.heartbeatInterval)],\n { type: 'application/javascript' },\n );\n const url = URL.createObjectURL(blob);\n\n try {\n worker = new Worker(url);\n } finally {\n URL.revokeObjectURL(url);\n }\n } catch (error) {\n throw new FamiliarRuntimeError('Failed to create Worker', { cause: error });\n }\n }\n\n worker.onmessage = (event: MessageEvent<SlotMessage<TOutput>>) => {\n const pending = this.pending;\n\n if (!pending || event.data.id !== pending.id) return;\n\n // Handle heartbeat message — reset the watchdog timer.\n if ('heartbeat' in event.data) {\n if (pending.watchdogMs !== undefined) {\n clearTimeout(pending.heartbeatWatchdog);\n pending.heartbeatWatchdog = setTimeout(() => {\n this.restart(new FamiliarTimeoutError(pending.watchdogMs!));\n }, pending.watchdogMs);\n unrefTimer(pending.heartbeatWatchdog);\n }\n\n return;\n }\n\n if ('chunk' in event.data) {\n pending.emit?.(event.data.chunk);\n\n return;\n }\n\n clearTimeout(pending.timer);\n clearTimeout(pending.heartbeatWatchdog);\n this.pending = null;\n\n if ('error' in event.data) {\n const cause = event.data.error instanceof Error ? event.data.error : new Error(String(event.data.error));\n\n pending.reject(new FamiliarTaskError(cause.message, { cause }));\n } else {\n pending.resolve(event.data.result);\n }\n };\n\n worker.onerror = (event: ErrorEvent) => {\n const error = new FamiliarRuntimeError(event.message);\n\n // Stop and fail before calling the external callback so it sees a clean state.\n this.stopWorker();\n this.failPending(error);\n\n this.onSlotError?.(error, () => void this.prime());\n };\n\n this.worker = worker;\n\n return worker;\n }\n\n private failPending(reason: unknown): void {\n const pending = this.pending;\n\n if (!pending) return;\n\n clearTimeout(pending.timer);\n clearTimeout(pending.heartbeatWatchdog);\n this.pending = null;\n pending.reject(reason);\n }\n\n private restart(reason: unknown): void {\n this.stopWorker();\n this.failPending(reason);\n }\n\n private stopWorker(): void {\n if (!this.worker) return;\n\n this.worker.terminate();\n this.worker = null;\n }\n}\n\n// ─── createWorker ─────────────────────────────────────────────────────────────\n\n/**\n * Creates a pool of Web Workers that run `fn` in parallel.\n *\n * The task function is serialized via `.toString()` and runs in a separate global scope.\n * It cannot close over variables from the surrounding module.\n *\n * Use the optional `task()` helper to validate that the function is not bound or native.\n * For workers that need imports, see `createModuleWorker`.\n *\n * @example\n * // Plain arrow function — most common case:\n * const worker = createWorker((n: number) => n * 2);\n *\n * // With task() for validation:\n * const worker = createWorker(task((n: number) => n * 2));\n */\nexport function createWorker<TInput, TOutput>(\n fn: TaskFn<TInput, TOutput>,\n options?: WorkerOptions,\n): WorkerHandle<TInput, TOutput> {\n const { concurrency, heartbeatWindow, maxQueue, onFull, onSlotError, timeout } = resolveOptions(options);\n const heartbeatInterval = heartbeatWindow != null ? Math.floor(heartbeatWindow / 2) : undefined;\n\n const slots = Array.from(\n { length: concurrency },\n () => new Slot<TInput, TOutput>({ fn, heartbeatInterval, kind: 'inline' }, onSlotError),\n );\n\n return createPool(slots, {\n concurrency,\n defaultTimeout: timeout,\n maxQueue,\n onFull,\n });\n}\n\n// ─── createModuleWorker ───────────────────────────────────────────────────────\n\n/**\n * Creates a pool of module-type Web Workers loaded from a real URL.\n *\n * Unlike `createWorker`, the worker file is a regular module — it can import utilities,\n * use top-level await, and reference module scope.\n *\n * Use `handleMessages` from `@vielzeug/familiar/protocol` in the worker file to implement\n * the message protocol without boilerplate.\n *\n * **Protocol**: The worker module must handle the `{ id, input }` message format and reply\n * with `{ id, result }` or `{ id, error: { name, message, stack } }`. For streaming, it must\n * send one or more `{ id, chunk }` messages followed by `{ id, result: undefined }`.\n * For heartbeat support, send `{ id, heartbeat: true }` at regular intervals.\n *\n * @example\n * ```ts\n * // my-worker.ts — use handleMessages for zero boilerplate:\n * import { handleMessages } from '@vielzeug/familiar/protocol';\n * handleMessages(async (input: number) => input * 2);\n *\n * // main.ts\n * const pool = createModuleWorker<number, number>(\n * new URL('./my-worker.ts', import.meta.url),\n * { concurrency: 4 },\n * );\n * ```\n */\nexport function createModuleWorker<TInput, TOutput>(\n url: URL | string,\n options?: WorkerOptions,\n): WorkerHandle<TInput, TOutput> {\n const { concurrency, heartbeatWindow, maxQueue, onFull, onSlotError, timeout } = resolveOptions(options);\n\n if (heartbeatWindow !== undefined) {\n warn(\n '`heartbeatWindow` has no effect on module workers — the worker script must implement the heartbeat protocol manually.',\n );\n }\n\n const href = typeof url === 'string' ? url : url.href;\n\n const slots = Array.from(\n { length: concurrency },\n () => new Slot<TInput, TOutput>({ kind: 'module', url: href }, onSlotError),\n );\n\n return createPool(slots, {\n concurrency,\n defaultTimeout: timeout,\n maxQueue,\n onFull,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"familiar.js","names":["#heap","#live","#sequence","#siftUp","#extract","#siftDown","#before","#onSlotError","#url","#worker","#settlePending","#disposed","#ensureWorker","#dispatch","#pending","#taskId","#onMessage"],"sources":["../src/errors.ts","../../arsenal/dist/async/abortError.js","../src/_queue.ts","../src/_timers.ts","../src/_pool.ts","../src/_stream-pool.ts","../src/worker.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 a Familiar pool factory. */\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","//#region src/async/abortError.ts\nfunction e(e) {\n\treturn e?.reason ?? new DOMException(\"Aborted\", \"AbortError\");\n}\n//#endregion\nexport { e as abortError };\n\n//# sourceMappingURL=abortError.js.map","export type QueueItem<TInput, TOutput> = {\n cancelled?: boolean;\n cleanupAbort?: () => void;\n input: TInput;\n priority: number;\n reject: (reason: unknown) => void;\n resolve: (value: TOutput) => void;\n signal?: AbortSignal;\n timeout?: number;\n transferables: Transferable[];\n};\n\ntype Entry<TInput, TOutput> = { readonly item: QueueItem<TInput, TOutput>; readonly sequence: number };\n\nexport class TaskQueue<TInput, TOutput> {\n readonly #heap: Entry<TInput, TOutput>[] = [];\n #live = 0;\n #sequence = 0;\n\n get size(): number {\n return this.#live;\n }\n\n enqueue(item: QueueItem<TInput, TOutput>, maxSize: number | undefined): boolean {\n if (maxSize !== undefined && this.#live >= maxSize) return false;\n\n this.#heap.push({ item, sequence: this.#sequence++ });\n this.#siftUp(this.#heap.length - 1);\n this.#live += 1;\n\n return true;\n }\n\n remove(item: QueueItem<TInput, TOutput>): boolean {\n if (item.cancelled) return false;\n\n for (const entry of this.#heap) {\n if (entry.item === item) {\n item.cancelled = true;\n this.#live -= 1;\n\n return true;\n }\n }\n\n return false;\n }\n\n shift(): QueueItem<TInput, TOutput> | undefined {\n while (this.#heap.length > 0) {\n const item = this.#extract().item;\n\n if (!item.cancelled) {\n this.#live -= 1;\n\n return item;\n }\n }\n\n return undefined;\n }\n\n #extract(): Entry<TInput, TOutput> {\n const top = this.#heap[0]!;\n const last = this.#heap.pop()!;\n\n if (this.#heap.length > 0) {\n this.#heap[0] = last;\n this.#siftDown(0);\n }\n\n return top;\n }\n\n #before(a: Entry<TInput, TOutput>, b: Entry<TInput, TOutput>): boolean {\n return a.item.priority === b.item.priority ? a.sequence < b.sequence : a.item.priority > b.item.priority;\n }\n\n #siftUp(index: number): void {\n while (index > 0) {\n const parent = (index - 1) >> 1;\n\n if (!this.#before(this.#heap[index]!, this.#heap[parent]!)) return;\n\n [this.#heap[index], this.#heap[parent]] = [this.#heap[parent]!, this.#heap[index]!];\n index = parent;\n }\n }\n\n #siftDown(index: number): void {\n for (;;) {\n const left = index * 2 + 1;\n const right = left + 1;\n let next = index;\n\n if (left < this.#heap.length && this.#before(this.#heap[left]!, this.#heap[next]!)) next = left;\n\n if (right < this.#heap.length && this.#before(this.#heap[right]!, this.#heap[next]!)) next = right;\n\n if (next === index) return;\n\n [this.#heap[index], this.#heap[next]] = [this.#heap[next]!, this.#heap[index]!];\n index = next;\n }\n }\n}\n","/**\n * Node compatibility helper: `unref()`s a timer so a pending per-task timeout/watchdog does not\n * keep a Node event loop (e.g. a Worker polyfill) alive. Browsers return a plain number from\n * `setTimeout`/`setInterval`, so this is a no-op there.\n *\n * Not part of the public API surface.\n */\nexport function unrefTimer(timer: ReturnType<typeof setTimeout> | ReturnType<typeof setInterval>): void {\n if (typeof timer === 'object' && timer !== null && 'unref' in timer) {\n (timer as { unref(): void }).unref();\n }\n}\n","import { abortError } from '@vielzeug/arsenal/async';\n\nimport type {\n BatchOptions,\n DrainOptions,\n RunOptions,\n SlotStrategy,\n TaskGroup,\n TaskGroupOptions,\n WorkerPool,\n WorkerStats,\n WorkerStatus,\n} from './types';\n\nimport { type QueueItem, TaskQueue } from './_queue';\nimport { unrefTimer } from './_timers';\nimport { FamiliarQueueFullError, 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\ntype IdleWaiter = {\n reject: (reason: unknown) => void;\n resolve: () => void;\n timer?: ReturnType<typeof setTimeout>;\n};\n\ntype CapacityWaiter = {\n cleanup(): void;\n reject(reason: unknown): void;\n resolve(): void;\n};\n\nexport function createPool<TInput, TOutput>(\n slots: SlotStrategy<TInput, TOutput>[],\n options: PoolOptions,\n): WorkerPool<TInput, TOutput> {\n const freeSlots = [...slots];\n const queue = new TaskQueue<TInput, TOutput>();\n const disposalController = new AbortController();\n const idleWaiters: IdleWaiter[] = [];\n const capacityWaiters: CapacityWaiter[] = [];\n let active = 0;\n let completed = 0;\n let failed = 0;\n let drainPromise: Promise<void> | undefined;\n let draining = false;\n let terminated = false;\n\n function isIdle(): boolean {\n return active === 0 && queue.size === 0;\n }\n\n function settleIdle(): void {\n if (!isIdle()) return;\n\n for (const waiter of idleWaiters.splice(0)) {\n if (waiter.timer) clearTimeout(waiter.timer);\n\n waiter.resolve();\n }\n }\n\n function waitForIdle(drainOptions: DrainOptions = {}): Promise<void> {\n if (isIdle()) return Promise.resolve();\n\n return new Promise<void>((resolve, reject) => {\n const waiter: IdleWaiter = { reject, resolve };\n\n if (drainOptions.timeout !== undefined) {\n waiter.timer = setTimeout(() => {\n const index = idleWaiters.indexOf(waiter);\n\n if (index !== -1) idleWaiters.splice(index, 1);\n\n reject(new FamiliarTimeoutError(drainOptions.timeout!));\n }, drainOptions.timeout);\n unrefTimer(waiter.timer);\n }\n\n idleWaiters.push(waiter);\n });\n }\n\n function releaseCapacity(): void {\n capacityWaiters.shift()?.resolve();\n }\n\n function rejectCapacity(reason: unknown): void {\n for (const waiter of capacityWaiters.splice(0)) waiter.reject(reason);\n }\n\n function waitForCapacity(signal: AbortSignal | undefined): Promise<void> {\n if (signal?.aborted) return Promise.reject(abortError(signal));\n\n return new Promise<void>((resolve, reject) => {\n let settled = false;\n const waiter: CapacityWaiter = {\n cleanup() {\n signal?.removeEventListener('abort', onAbort);\n },\n reject(reason) {\n if (settled) return;\n\n settled = true;\n waiter.cleanup();\n\n const index = capacityWaiters.indexOf(waiter);\n\n if (index !== -1) capacityWaiters.splice(index, 1);\n\n reject(reason);\n },\n resolve() {\n if (settled) return;\n\n settled = true;\n waiter.cleanup();\n resolve();\n },\n };\n const onAbort = () => waiter.reject(abortError(signal!));\n\n signal?.addEventListener('abort', onAbort, { once: true });\n capacityWaiters.push(waiter);\n });\n }\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 releaseCapacity();\n continue;\n }\n\n return item;\n }\n\n settleIdle();\n\n return undefined;\n }\n\n function drainQueue(): void {\n if (draining || terminated) return;\n\n draining = true;\n\n while (!terminated && freeSlots.length > 0 && queue.size > 0) {\n const next = nextItem();\n\n if (!next) break;\n\n const item = next;\n const slot = freeSlots.pop()!;\n const timeout = item.timeout ?? options.defaultTimeout;\n\n active += 1;\n releaseCapacity();\n\n const onAbort = () => slot.cancel(abortError(item.signal!));\n\n item.cleanupAbort = () => {\n item.signal?.removeEventListener('abort', onAbort);\n item.cleanupAbort = undefined;\n };\n item.signal?.addEventListener('abort', onAbort, { once: true });\n\n slot.run(item.input, item.transferables, timeout).then(\n (value) => finish(value),\n (error: unknown) => fail(error),\n );\n\n function finish(value: TOutput): void {\n item.cleanupAbort?.();\n freeSlots.push(slot);\n active -= 1;\n completed += 1;\n item.resolve(value);\n drainQueue();\n settleIdle();\n }\n\n function fail(error: unknown): void {\n item.cleanupAbort?.();\n freeSlots.push(slot);\n active -= 1;\n\n if (!(error instanceof FamiliarTerminatedError) && error?.constructor?.name !== 'AbortError') failed += 1;\n\n item.reject(error);\n drainQueue();\n settleIdle();\n }\n }\n\n draining = false;\n }\n\n async function run(input: TInput, runOptions: RunOptions = {}): Promise<TOutput> {\n const { priority = 0, signal, timeout, transferables = [] } = runOptions;\n\n if (terminated) throw new FamiliarTerminatedError();\n\n if (drainPromise) throw new FamiliarTerminatedError('Worker is draining');\n\n if (signal?.aborted) throw abortError(signal);\n\n while (options.onFull === 'wait' && options.maxQueue !== undefined && queue.size >= options.maxQueue) {\n await waitForCapacity(signal);\n\n if (terminated) throw new FamiliarTerminatedError();\n\n if (drainPromise) throw new FamiliarTerminatedError('Worker is draining');\n\n if (signal?.aborted) throw abortError(signal);\n }\n\n let resolve!: (value: TOutput) => void;\n let reject!: (reason: unknown) => void;\n const promise = new Promise<TOutput>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n const item: QueueItem<TInput, TOutput> = { input, priority, reject, resolve, signal, timeout, transferables };\n\n if (!queue.enqueue(item, options.onFull === 'wait' ? undefined : options.maxQueue)) {\n throw new FamiliarQueueFullError(options.maxQueue!);\n }\n\n if (signal) {\n const onAbort = () => {\n if (!queue.remove(item)) return;\n\n item.cleanupAbort?.();\n reject(abortError(signal));\n releaseCapacity();\n settleIdle();\n };\n\n item.cleanupAbort = () => {\n signal.removeEventListener('abort', onAbort);\n item.cleanupAbort = undefined;\n };\n signal.addEventListener('abort', onAbort, { once: true });\n }\n\n drainQueue();\n\n return promise;\n }\n\n function dispose(): void {\n if (terminated) return;\n\n terminated = true;\n disposalController.abort();\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 rejectCapacity(new FamiliarTerminatedError());\n settleIdle();\n }\n\n function drain(drainOptions: DrainOptions = {}): Promise<void> {\n if (terminated) return Promise.resolve();\n\n if (drainPromise) return drainPromise;\n\n rejectCapacity(new FamiliarTerminatedError('Worker is draining'));\n drainPromise = waitForIdle(drainOptions).then(\n () => dispose(),\n (error: unknown) => {\n dispose();\n throw error;\n },\n );\n\n return drainPromise;\n }\n\n return {\n get disposalSignal() {\n return disposalController.signal;\n },\n dispose,\n get disposed() {\n return terminated;\n },\n drain,\n async prime(): Promise<void> {\n await Promise.all(slots.map((slot) => slot.prime()));\n },\n run,\n get stats(): WorkerStats {\n return { active, completed, failed, queued: queue.size };\n },\n get status(): WorkerStatus {\n if (terminated) return 'terminated';\n\n return active === 0 ? 'idle' : 'running';\n },\n [Symbol.asyncDispose]: () => drain(),\n [Symbol.dispose]: dispose,\n };\n}\n\nexport async function* batch<TInput, TOutput>(\n pool: WorkerPool<TInput, TOutput>,\n inputs: readonly TInput[],\n options: BatchOptions = {},\n): AsyncIterable<TOutput> {\n const controller = new AbortController();\n const onAbort = () => controller.abort(options.signal!.reason);\n\n options.signal?.addEventListener('abort', onAbort, { once: true });\n\n const tasks = inputs.map((input) => pool.run(input, { ...options, signal: controller.signal }));\n\n try {\n for (const task of tasks) yield await task;\n } catch (error) {\n controller.abort(error);\n await Promise.allSettled(tasks);\n throw error;\n } finally {\n options.signal?.removeEventListener('abort', onAbort);\n controller.abort();\n await Promise.allSettled(tasks);\n }\n}\n\nexport function createTaskGroup<TInput, TOutput>(\n pool: WorkerPool<TInput, TOutput>,\n name: string | undefined = undefined,\n options: TaskGroupOptions = {},\n): TaskGroup<TInput, TOutput> {\n const controller = new AbortController();\n const tasks = new Set<Promise<TOutput>>();\n let size = 0;\n const onAbort = () => controller.abort(options.signal!.reason);\n\n options.signal?.addEventListener('abort', onAbort, { once: true });\n\n return {\n abort(reason?: unknown): void {\n controller.abort(reason);\n },\n async drain(): Promise<PromiseSettledResult<TOutput>[]> {\n return Promise.allSettled([...tasks]);\n },\n get name() {\n return name;\n },\n get pending() {\n return tasks.size;\n },\n run(input, runOptions = {}): Promise<TOutput> {\n const task = pool.run(input, { ...runOptions, signal: controller.signal });\n\n size += 1;\n tasks.add(task);\n void task.finally(() => tasks.delete(task));\n\n return task;\n },\n get size() {\n return size;\n },\n };\n}\n","import { abortError } from '@vielzeug/arsenal/async';\n\nimport type { PoolOptions } from './_pool';\nimport type { DrainOptions, RunOptions, StreamWorkerPool, WorkerStats, WorkerStatus } from './types';\nimport type { RunningStream } from './worker';\n\nimport { unrefTimer } from './_timers';\nimport { FamiliarQueueFullError, FamiliarTerminatedError, FamiliarTimeoutError } from './errors';\n\nexport type StreamSlot<TInput, TChunk> = {\n cancel(reason: unknown): void;\n prime(): Promise<void>;\n stream(input: TInput, options: RunOptions): RunningStream<TChunk>;\n terminate(): void;\n};\n\ntype CapacityWaiter = {\n cleanup(): void;\n reject(reason: unknown): void;\n resolve(): void;\n};\n\ntype Waiter<TSlot> = {\n cleanup(): void;\n priority: number;\n reject(reason: unknown): void;\n resolve(slot: TSlot): void;\n sequence: number;\n signal?: AbortSignal;\n};\n\nexport function createStreamPool<TInput, TChunk>(\n slots: StreamSlot<TInput, TChunk>[],\n options: PoolOptions,\n): StreamWorkerPool<TInput, TChunk> {\n const freeSlots = [...slots];\n const waiters: Waiter<StreamSlot<TInput, TChunk>>[] = [];\n const capacityWaiters: CapacityWaiter[] = [];\n const disposalController = new AbortController();\n const idleWaiters: Array<() => void> = [];\n let active = 0;\n let completed = 0;\n let failed = 0;\n let sequence = 0;\n let terminated = false;\n let drainPromise: Promise<void> | undefined;\n\n function idle(): boolean {\n return active === 0 && waiters.length === 0;\n }\n\n function settleIdle(): void {\n if (!idle()) return;\n\n for (const resolve of idleWaiters.splice(0)) resolve();\n }\n\n function waitForIdle(options: DrainOptions): Promise<void> {\n if (idle()) return Promise.resolve();\n\n return new Promise<void>((resolve, reject) => {\n idleWaiters.push(resolve);\n\n if (options.timeout !== undefined) {\n const timer = setTimeout(() => {\n const index = idleWaiters.indexOf(resolve);\n\n if (index !== -1) idleWaiters.splice(index, 1);\n\n reject(new FamiliarTimeoutError(options.timeout!));\n }, options.timeout);\n\n unrefTimer(timer);\n }\n });\n }\n\n function releaseCapacity(): void {\n capacityWaiters.shift()?.resolve();\n }\n\n function rejectCapacity(reason: unknown): void {\n for (const waiter of capacityWaiters.splice(0)) waiter.reject(reason);\n }\n\n function waitForCapacity(signal: AbortSignal | undefined): Promise<void> {\n if (signal?.aborted) return Promise.reject(abortError(signal));\n\n return new Promise<void>((resolve, reject) => {\n let settled = false;\n const waiter: CapacityWaiter = {\n cleanup() {\n signal?.removeEventListener('abort', onAbort);\n },\n reject(reason) {\n if (settled) return;\n\n settled = true;\n waiter.cleanup();\n\n const index = capacityWaiters.indexOf(waiter);\n\n if (index !== -1) capacityWaiters.splice(index, 1);\n\n reject(reason);\n },\n resolve() {\n if (settled) return;\n\n settled = true;\n waiter.cleanup();\n resolve();\n },\n };\n const onAbort = () => waiter.reject(abortError(signal!));\n\n signal?.addEventListener('abort', onAbort, { once: true });\n capacityWaiters.push(waiter);\n });\n }\n\n function release(slot: StreamSlot<TInput, TChunk>): void {\n const next = waiters.shift();\n\n if (next) {\n next.resolve(slot);\n releaseCapacity();\n } else {\n freeSlots.push(slot);\n }\n }\n\n async function acquire(runOptions: RunOptions): Promise<StreamSlot<TInput, TChunk>> {\n if (terminated) throw new FamiliarTerminatedError();\n\n if (drainPromise) throw new FamiliarTerminatedError('Worker is draining');\n\n if (runOptions.signal?.aborted) throw abortError(runOptions.signal);\n\n while (options.maxQueue !== undefined && waiters.length >= options.maxQueue) {\n if (options.onFull === 'reject') throw new FamiliarQueueFullError(options.maxQueue);\n\n await waitForCapacity(runOptions.signal);\n\n if (terminated) throw new FamiliarTerminatedError();\n\n if (drainPromise) throw new FamiliarTerminatedError('Worker is draining');\n\n if (runOptions.signal?.aborted) throw abortError(runOptions.signal);\n }\n\n const free = freeSlots.pop();\n\n if (free) return free;\n\n return new Promise<StreamSlot<TInput, TChunk>>((resolve, reject) => {\n const waiter: Waiter<StreamSlot<TInput, TChunk>> = {\n cleanup() {\n waiter.signal?.removeEventListener('abort', onAbort);\n },\n priority: runOptions.priority ?? 0,\n reject(reason) {\n waiter.cleanup();\n\n const index = waiters.indexOf(waiter);\n\n if (index !== -1) {\n waiters.splice(index, 1);\n releaseCapacity();\n }\n\n reject(reason);\n settleIdle();\n },\n resolve(slot) {\n waiter.cleanup();\n resolve(slot);\n },\n sequence: sequence++,\n signal: runOptions.signal,\n };\n const onAbort = () => waiter.reject(abortError(waiter.signal!));\n\n waiter.signal?.addEventListener('abort', onAbort, { once: true });\n waiters.push(waiter);\n waiters.sort((a, b) => b.priority - a.priority || a.sequence - b.sequence);\n });\n }\n\n function dispose(): void {\n if (terminated) return;\n\n terminated = true;\n disposalController.abort();\n for (const slot of slots) slot.terminate();\n for (const waiter of waiters.splice(0)) waiter.reject(new FamiliarTerminatedError());\n rejectCapacity(new FamiliarTerminatedError());\n settleIdle();\n }\n\n function drain(drainOptions: DrainOptions = {}): Promise<void> {\n if (terminated) return Promise.resolve();\n\n if (drainPromise) return drainPromise;\n\n for (const waiter of waiters.splice(0)) waiter.reject(new FamiliarTerminatedError('Worker is draining'));\n rejectCapacity(new FamiliarTerminatedError('Worker is draining'));\n drainPromise = waitForIdle(drainOptions).then(\n () => dispose(),\n (error: unknown) => {\n dispose();\n throw error;\n },\n );\n\n return drainPromise;\n }\n\n return {\n get disposalSignal() {\n return disposalController.signal;\n },\n dispose,\n get disposed() {\n return terminated;\n },\n drain,\n async prime(): Promise<void> {\n await Promise.all(slots.map((slot) => slot.prime()));\n },\n runStream(input, runOptions = {}): AsyncIterable<TChunk> {\n const controller = new AbortController();\n const onAbort = () => controller.abort(runOptions.signal!.reason);\n\n runOptions.signal?.addEventListener('abort', onAbort, { once: true });\n\n return {\n [Symbol.asyncIterator]: async function* () {\n let slot: StreamSlot<TInput, TChunk> | undefined;\n let onCancel: (() => void) | undefined;\n\n try {\n slot = await acquire({ ...runOptions, signal: controller.signal });\n active += 1;\n onCancel = () => slot?.cancel(abortError(controller.signal));\n controller.signal.addEventListener('abort', onCancel, { once: true });\n\n const running = slot.stream(input, {\n ...runOptions,\n signal: controller.signal,\n timeout: runOptions.timeout ?? options.defaultTimeout,\n });\n\n for await (const value of running.iterable) yield value;\n await running.done;\n completed += 1;\n } catch (error) {\n if (error?.constructor?.name !== 'AbortError' && !(error instanceof FamiliarTerminatedError)) failed += 1;\n\n throw error;\n } finally {\n runOptions.signal?.removeEventListener('abort', onAbort);\n\n if (slot) {\n if (onCancel) controller.signal.removeEventListener('abort', onCancel);\n\n active -= 1;\n release(slot);\n settleIdle();\n }\n }\n },\n };\n },\n get stats(): WorkerStats {\n return { active, completed, failed, queued: waiters.length };\n },\n get status(): WorkerStatus {\n if (terminated) return 'terminated';\n\n return active === 0 ? 'idle' : 'running';\n },\n [Symbol.asyncDispose]: () => drain(),\n [Symbol.dispose]: dispose,\n };\n}\n","export type {\n BatchOptions,\n DrainOptions,\n RunOptions,\n StreamWorkerPool,\n TaskGroup,\n TaskGroupOptions,\n WorkerOptions,\n WorkerPool,\n WorkerStats,\n WorkerStatus,\n} from './types';\nexport {\n FamiliarError,\n FamiliarInvalidOptionsError,\n FamiliarQueueFullError,\n FamiliarRuntimeError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n FamiliarTimeoutError,\n} from './errors';\nexport { batch, createTaskGroup } from './_pool';\n\nimport type { SerializedError, WorkerResponse } from './protocol';\nimport type { RunOptions, SlotStrategy, StreamWorkerPool, WorkerOptions, WorkerPool } from './types';\n\nimport { createPool } from './_pool';\nimport { createStreamPool, type StreamSlot } from './_stream-pool';\nimport { unrefTimer } from './_timers';\nimport {\n FamiliarInvalidOptionsError,\n FamiliarRuntimeError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n FamiliarTimeoutError,\n} from './errors';\nimport { PROTOCOL_VERSION } from './protocol';\n\nconst MAX_CONCURRENCY = 512;\n\ntype ResolvedOptions = {\n concurrency: number;\n maxQueue: number | undefined;\n onFull: 'reject' | 'wait';\n onSlotError: WorkerOptions['onSlotError'];\n timeout: number | undefined;\n};\n\ntype Pending<TOutput> = {\n emit?: (value: TOutput) => void;\n id: number;\n reject: (reason: unknown) => void;\n resolve: (value: TOutput) => void;\n timer?: ReturnType<typeof setTimeout>;\n};\n\nexport type RunningStream<TChunk> = {\n done: Promise<void>;\n iterable: AsyncIterable<TChunk>;\n};\n\nfunction resolveOptions(options: WorkerOptions = {}): ResolvedOptions {\n const { concurrency = 1, maxQueue, onFull = 'reject', onSlotError, timeout } = options;\n const resolvedConcurrency =\n concurrency === 'auto' ? Math.max(1, globalThis.navigator?.hardwareConcurrency ?? 1) : concurrency;\n\n if (!Number.isInteger(resolvedConcurrency) || resolvedConcurrency < 1 || resolvedConcurrency > MAX_CONCURRENCY) {\n throw new FamiliarInvalidOptionsError(`\\`concurrency\\` must be a positive integer ≤ ${MAX_CONCURRENCY} or \"auto\"`);\n }\n\n if (maxQueue !== undefined && (!Number.isInteger(maxQueue) || maxQueue < 1)) {\n throw new FamiliarInvalidOptionsError('`maxQueue` must be a positive integer');\n }\n\n if (timeout !== undefined && (!Number.isFinite(timeout) || timeout <= 0)) {\n throw new FamiliarInvalidOptionsError('`timeout` must be a finite number greater than 0');\n }\n\n return { concurrency: resolvedConcurrency, maxQueue, onFull, onSlotError, timeout };\n}\n\nfunction isWorkerResponse<TOutput>(value: unknown): value is WorkerResponse<TOutput> {\n if (typeof value !== 'object' || value === null) return false;\n\n const response = value as Partial<WorkerResponse<TOutput>>;\n\n return (\n response.version === PROTOCOL_VERSION &&\n typeof response.id === 'number' &&\n (response.kind === 'chunk' || response.kind === 'error' || response.kind === 'result')\n );\n}\n\nfunction taskError(error: SerializedError): FamiliarTaskError {\n const cause = new Error(error.message);\n\n cause.name = error.name;\n cause.stack = error.stack;\n\n return new FamiliarTaskError(error.message, { cause });\n}\n\nclass Slot<TInput, TOutput> implements SlotStrategy<TInput, TOutput>, StreamSlot<TInput, TOutput> {\n readonly #onSlotError: WorkerOptions['onSlotError'];\n readonly #url: URL | string;\n #disposed = false;\n #pending: Pending<TOutput> | undefined;\n #taskId = 0;\n #worker: Worker | undefined;\n\n constructor(url: URL | string, onSlotError: WorkerOptions['onSlotError']) {\n this.#url = url;\n this.#onSlotError = onSlotError;\n }\n\n cancel(reason: unknown): void {\n this.#worker?.terminate();\n this.#worker = undefined;\n this.#settlePending('reject', reason);\n }\n\n prime(): Promise<void> {\n if (!this.#disposed) this.#ensureWorker();\n\n return Promise.resolve();\n }\n\n run(input: TInput, transferables: Transferable[], timeout: number | undefined): Promise<TOutput> {\n return this.#dispatch(input, transferables, timeout, 'run') as Promise<TOutput>;\n }\n\n stream(input: TInput, options: RunOptions): RunningStream<TOutput> {\n const stop = () => this.cancel(new FamiliarTerminatedError('Stream consumer stopped'));\n const chunks: TOutput[] = [];\n const waiters: Array<(result: IteratorResult<TOutput>) => void> = [];\n let done = false;\n let error: unknown;\n const emit = (value: TOutput) => {\n const waiter = waiters.shift();\n\n if (waiter) waiter({ done: false, value });\n else chunks.push(value);\n };\n const finish = (reason?: unknown) => {\n done = true;\n error = reason;\n\n while (waiters.length > 0) {\n const waiter = waiters.shift()!;\n\n if (reason !== undefined) waiter(Promise.reject(reason) as never);\n else waiter({ done: true, value: undefined as never });\n }\n };\n const donePromise = this.#dispatch(input, options.transferables ?? [], options.timeout, 'stream', emit).then(\n () => finish(),\n (reason: unknown) => finish(reason),\n );\n\n return {\n done: donePromise,\n iterable: {\n [Symbol.asyncIterator]: () => ({\n async next(): Promise<IteratorResult<TOutput>> {\n if (chunks.length > 0) return { done: false, value: chunks.shift()! };\n\n if (error !== undefined) throw error;\n\n if (done) return { done: true, value: undefined as never };\n\n return new Promise<IteratorResult<TOutput>>((resolve, reject) => {\n waiters.push((result) => {\n if (result instanceof Promise) void result.then(resolve, reject);\n else resolve(result);\n });\n });\n },\n async return(): Promise<IteratorResult<TOutput>> {\n stop();\n\n return { done: true, value: undefined as never };\n },\n }),\n },\n };\n }\n\n terminate(): void {\n this.#disposed = true;\n this.cancel(new FamiliarTerminatedError());\n }\n\n #dispatch(\n input: TInput,\n transferables: Transferable[],\n timeout: number | undefined,\n kind: 'run' | 'stream',\n emit?: (value: TOutput) => void,\n ): Promise<TOutput> {\n if (this.#disposed) return Promise.reject(new FamiliarTerminatedError());\n\n if (this.#pending) return Promise.reject(new FamiliarRuntimeError('Worker slot is already busy'));\n\n try {\n const worker = this.#ensureWorker();\n const id = this.#taskId++;\n\n return new Promise<TOutput>((resolve, reject) => {\n const pending: Pending<TOutput> = { emit, id, reject, resolve };\n\n this.#pending = pending;\n\n if (timeout !== undefined) {\n pending.timer = setTimeout(() => this.cancel(new FamiliarTimeoutError(timeout)), timeout);\n unrefTimer(pending.timer);\n }\n\n try {\n worker.postMessage({ id, input, kind, version: PROTOCOL_VERSION }, transferables);\n } catch (error) {\n this.cancel(new FamiliarRuntimeError('Failed to post message to worker', { cause: error }));\n }\n });\n } catch (error) {\n return Promise.reject(error);\n }\n }\n\n #ensureWorker(): Worker {\n if (this.#worker) return this.#worker;\n\n if (typeof Worker === 'undefined') throw new FamiliarRuntimeError('Worker API is unavailable in this runtime');\n\n try {\n const worker = new Worker(this.#url, { type: 'module' });\n\n worker.onmessage = (event: MessageEvent<unknown>) => this.#onMessage(event.data);\n worker.onerror = (event) => {\n const error = new FamiliarRuntimeError(event.message || 'Worker failed');\n\n this.#worker = undefined;\n this.#onSlotError?.(error);\n this.#settlePending('reject', error);\n };\n this.#worker = worker;\n\n return worker;\n } catch (error) {\n throw new FamiliarRuntimeError('Failed to create Worker', { cause: error });\n }\n }\n\n #onMessage(message: unknown): void {\n if (!this.#pending) return;\n\n if (!isWorkerResponse<TOutput>(message) || message.id !== this.#pending.id) {\n this.cancel(new FamiliarRuntimeError('Worker returned an incompatible protocol response'));\n\n return;\n }\n\n if (message.kind === 'chunk') {\n this.#pending.emit?.(message.value);\n\n return;\n }\n\n if (message.kind === 'error') {\n this.#settlePending('reject', taskError(message.error));\n\n return;\n }\n\n this.#settlePending('resolve', message.value);\n }\n\n #settlePending(kind: 'resolve' | 'reject', value: unknown): void {\n const pending = this.#pending;\n\n if (!pending) return;\n\n this.#pending = undefined;\n\n if (pending.timer) clearTimeout(pending.timer);\n\n if (kind === 'resolve') pending.resolve(value as TOutput);\n else pending.reject(value);\n }\n}\n\nfunction slots<TInput, TOutput>(url: URL | string, options: ResolvedOptions): Slot<TInput, TOutput>[] {\n return Array.from({ length: options.concurrency }, () => new Slot<TInput, TOutput>(url, options.onSlotError));\n}\n\n/** Create a pool backed by an ES module worker registered with exposeTask(). */\nexport function createWorker<TInput, TOutput>(\n url: URL | string,\n options: WorkerOptions = {},\n): WorkerPool<TInput, TOutput> {\n const resolved = resolveOptions(options);\n\n return createPool(slots<TInput, TOutput>(url, resolved), {\n concurrency: resolved.concurrency,\n defaultTimeout: resolved.timeout,\n maxQueue: resolved.maxQueue,\n onFull: resolved.onFull,\n });\n}\n\n/** Create a stream-only pool backed by an ES module worker registered with exposeStream(). */\nexport function createStreamWorker<TInput, TChunk>(\n url: URL | string,\n options: WorkerOptions = {},\n): StreamWorkerPool<TInput, TChunk> {\n const resolved = resolveOptions(options);\n\n return createStreamPool(slots<TInput, TChunk>(url, resolved), {\n concurrency: resolved.concurrency,\n defaultTimeout: resolved.timeout,\n maxQueue: resolved.maxQueue,\n onFull: resolved.onFull,\n });\n}\n"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./worker.cjs");exports.FamiliarError=e.FamiliarError,exports.FamiliarInvalidOptionsError=e.FamiliarInvalidOptionsError,exports.FamiliarQueueFullError=e.FamiliarQueueFullError,exports.FamiliarRuntimeError=e.FamiliarRuntimeError,exports.FamiliarTaskError=e.FamiliarTaskError,exports.FamiliarTerminatedError=e.FamiliarTerminatedError,exports.FamiliarTimeoutError=e.FamiliarTimeoutError,exports.createModuleWorker=t.createModuleWorker,exports.createWorker=t.createWorker,exports.task=t.task;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./_pool.cjs"),n=require("./worker.cjs");exports.FamiliarError=e.FamiliarError,exports.FamiliarInvalidOptionsError=e.FamiliarInvalidOptionsError,exports.FamiliarQueueFullError=e.FamiliarQueueFullError,exports.FamiliarRuntimeError=e.FamiliarRuntimeError,exports.FamiliarTaskError=e.FamiliarTaskError,exports.FamiliarTerminatedError=e.FamiliarTerminatedError,exports.FamiliarTimeoutError=e.FamiliarTimeoutError,exports.batch=t.batch,exports.createStreamWorker=n.createStreamWorker,exports.createTaskGroup=t.createTaskGroup,exports.createWorker=n.createWorker;
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  import { FamiliarError as e, FamiliarInvalidOptionsError as t, FamiliarQueueFullError as n, FamiliarRuntimeError as r, FamiliarTaskError as i, FamiliarTerminatedError as a, FamiliarTimeoutError as o } from "./errors.js";
2
- import { createModuleWorker as s, createWorker as c, task as l } from "./worker.js";
3
- export { e as FamiliarError, t as FamiliarInvalidOptionsError, n as FamiliarQueueFullError, r as FamiliarRuntimeError, i as FamiliarTaskError, a as FamiliarTerminatedError, o as FamiliarTimeoutError, s as createModuleWorker, c as createWorker, l as task };
2
+ import { batch as s, createTaskGroup as c } from "./_pool.js";
3
+ import { createStreamWorker as l, createWorker as u } from "./worker.js";
4
+ export { e as FamiliarError, t as FamiliarInvalidOptionsError, n as FamiliarQueueFullError, r as FamiliarRuntimeError, i as FamiliarTaskError, a as FamiliarTerminatedError, o as FamiliarTimeoutError, s as batch, l as createStreamWorker, c as createTaskGroup, u as createWorker };
package/dist/protocol.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=2;function t(e){let t=e instanceof Error?e:Error(String(e));return{message:t.message,name:t.name,stack:t.stack}}function n(e){self.onmessage=async n=>{let{id:r,input:i}=n.data;try{let t=await e(i);self.postMessage({id:r,result:t})}catch(e){self.postMessage({error:t(e),id:r})}}}function r(e){let n=self;n.onmessage=async r=>{let{id:i,input:a}=r.data;try{let t=await e(a);for await(let e of t)n.postMessage({chunk:e,id:i});n.postMessage({id:i,result:void 0})}catch(e){n.postMessage({error:t(e),id:i})}}}exports.PROTOCOL_VERSION=e,exports.handleMessages=n,exports.handleStreamMessages=r;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=1;function t(e){let t=e instanceof Error?e:Error(String(e));return{message:t.message,name:t.name,stack:t.stack}}function n(e){if(typeof e!=`object`||!e)return!1;let t=e;return typeof t.id==`number`&&(t.kind===`run`||t.kind===`stream`)&&`input`in t}function r(e){self.postMessage(e)}function i(e,n){r({error:t(n),id:e,kind:`error`,version:1})}function a(e){self.onmessage=async t=>{if(!n(t.data)||t.data.version!==1||t.data.kind!==`run`)return;let{id:a,input:o}=t.data;try{r({id:a,kind:`result`,value:await e(o),version:1})}catch(e){i(a,e)}}}function o(e){self.onmessage=async t=>{if(!n(t.data)||t.data.version!==1||t.data.kind!==`stream`)return;let{id:a,input:o}=t.data;try{for await(let t of await e(o))r({id:a,kind:`chunk`,value:t,version:1});r({id:a,kind:`result`,value:void 0,version:1})}catch(e){i(a,e)}}}exports.PROTOCOL_VERSION=e,exports.exposeStream=o,exports.exposeTask=a;
2
2
  //# sourceMappingURL=protocol.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.cjs","names":[],"sources":["../src/protocol.ts"],"sourcesContent":["/**\n * Host↔worker message protocol helpers for module worker files.\n *\n * Import this sub-path in module worker files to implement the protocol\n * without manual boilerplate.\n *\n * @example\n * // my-worker.ts\n * import { handleMessages } from '@vielzeug/familiar/protocol';\n * handleMessages(async (input: number) => input * 2);\n */\n\n/**\n * Current host↔worker message protocol version.\n * Increment when the protocol changes in a breaking way.\n * The host does not validate this value at runtime it is a debugging convention only.\n * @internal\n */\nexport const PROTOCOL_VERSION = 2 as const;\n\ntype ProtocolMessage<TInput> = { id: number; input: TInput };\n\ntype ErrorPayload = { message: string; name: string; stack?: string };\n\nfunction serializeError(e: unknown): ErrorPayload {\n const err = e instanceof Error ? e : new Error(String(e));\n\n return { message: err.message, name: err.name, stack: err.stack };\n}\n\n/**\n * Sets up the `self.onmessage` handler for a module worker.\n * Handles the `{ id, input }` `{ id, result }` / `{ id, error }` protocol automatically.\n *\n * Errors from `fn` are caught and forwarded as structured `{ id, error }` messages so the\n * host can reconstruct them as `FamiliarTaskError`. Non-Error throws are wrapped in an Error.\n *\n * @example\n * // my-worker.ts\n * import { handleMessages } from '@vielzeug/familiar/protocol';\n *\n * handleMessages(async (input: { a: number; b: number }) => input.a + input.b);\n */\nexport function handleMessages<TInput, TOutput>(fn: (input: TInput) => TOutput | Promise<TOutput>): void {\n (self as unknown as { onmessage: (event: MessageEvent<ProtocolMessage<TInput>>) => void }).onmessage = async (\n event,\n ) => {\n const { id, input } = event.data;\n\n try {\n const result = await fn(input);\n\n (self as unknown as { postMessage: (data: unknown) => void }).postMessage({ id, result });\n } catch (e) {\n (self as unknown as { postMessage: (data: unknown) => void }).postMessage({ error: serializeError(e), id });\n }\n };\n}\n\ntype StreamProtocolMessage<TInput> = { id: number; input: TInput; stream: true };\n\n/**\n * Sets up the `self.onmessage` handler for a streaming module worker.\n * The task function must return an `AsyncIterable<TOutput>`; each yielded value is forwarded\n * as a `{ id, chunk }` message, followed by `{ id, result: undefined }` on completion.\n *\n * Mirrors the inline blob worker streaming protocol so `runStream()` works with module workers.\n * Errors are forwarded as `{ id, error }` messages.\n *\n * @example\n * // my-streaming-worker.ts\n * import { handleStreamMessages } from '@vielzeug/familiar/protocol';\n *\n * handleStreamMessages(async function* (n: number) {\n * for (let i = 0; i < n; i++) {\n * yield i;\n * }\n * });\n */\nexport function handleStreamMessages<TInput, TOutput>(\n fn: (input: TInput) => AsyncIterable<TOutput> | Promise<AsyncIterable<TOutput>>,\n): void {\n const _self = self as unknown as {\n onmessage: (event: MessageEvent<StreamProtocolMessage<TInput>>) => void;\n postMessage: (data: unknown) => void;\n };\n\n _self.onmessage = async (event) => {\n const { id, input } = event.data;\n\n try {\n const iterable = await fn(input);\n\n for await (const chunk of iterable) {\n _self.postMessage({ chunk, id });\n }\n\n _self.postMessage({ id, result: undefined });\n } catch (e) {\n _self.postMessage({ error: serializeError(e), id });\n }\n };\n}\n"],"mappings":"mEAkBA,IAAa,EAAmB,EAMhC,SAAS,EAAe,EAA0B,CAChD,IAAM,EAAM,aAAa,MAAQ,EAAQ,MAAM,OAAO,CAAC,CAAC,EAExD,MAAO,CAAE,QAAS,EAAI,QAAS,KAAM,EAAI,KAAM,MAAO,EAAI,KAAM,CAClE,CAeA,SAAgB,EAAgC,EAAyD,CACvG,KAA2F,UAAY,KACrG,IACG,CACH,GAAM,CAAE,KAAI,SAAU,EAAM,KAE5B,GAAI,CACF,IAAM,EAAS,MAAM,EAAG,CAAK,EAE7B,KAA8D,YAAY,CAAE,KAAI,QAAO,CAAC,CAC1F,OAAS,EAAG,CACV,KAA8D,YAAY,CAAE,MAAO,EAAe,CAAC,EAAG,IAAG,CAAC,CAC5G,CACF,CACF,CAsBA,SAAgB,EACd,EACM,CACN,IAAM,EAAQ,KAKd,EAAM,UAAY,KAAO,IAAU,CACjC,GAAM,CAAE,KAAI,SAAU,EAAM,KAE5B,GAAI,CACF,IAAM,EAAW,MAAM,EAAG,CAAK,EAE/B,UAAW,IAAM,KAAS,EACxB,EAAM,YAAY,CAAE,QAAO,IAAG,CAAC,EAGjC,EAAM,YAAY,CAAE,KAAI,OAAQ,IAAA,EAAU,CAAC,CAC7C,OAAS,EAAG,CACV,EAAM,YAAY,CAAE,MAAO,EAAe,CAAC,EAAG,IAAG,CAAC,CACpD,CACF,CACF"}
1
+ {"version":3,"file":"protocol.cjs","names":[],"sources":["../src/protocol.ts"],"sourcesContent":["/** Current host-to-worker protocol version. */\nexport const PROTOCOL_VERSION = 1 as const;\n\nexport type SerializedError = {\n message: string;\n name: string;\n stack?: string;\n};\n\nexport type WorkerRequest<TInput> =\n | { id: number; input: TInput; kind: 'run'; version: typeof PROTOCOL_VERSION }\n | { id: number; input: TInput; kind: 'stream'; version: typeof PROTOCOL_VERSION };\n\nexport type WorkerResponse<TOutput> =\n | { id: number; kind: 'chunk'; value: TOutput; version: typeof PROTOCOL_VERSION }\n | { error: SerializedError; id: number; kind: 'error'; version: typeof PROTOCOL_VERSION }\n | { id: number; kind: 'result'; value: TOutput; version: typeof PROTOCOL_VERSION };\n\nexport type TaskHandler<TInput, TOutput> = (input: TInput) => TOutput | Promise<TOutput>;\n\nexport type StreamHandler<TInput, TChunk> = (input: TInput) => AsyncIterable<TChunk> | Promise<AsyncIterable<TChunk>>;\n\nfunction serializeError(error: unknown): SerializedError {\n const value = error instanceof Error ? error : new Error(String(error));\n\n return { message: value.message, name: value.name, stack: value.stack };\n}\n\nfunction isRequest(value: unknown): value is WorkerRequest<unknown> {\n if (typeof value !== 'object' || value === null) return false;\n\n const request = value as Partial<WorkerRequest<unknown>>;\n\n return typeof request.id === 'number' && (request.kind === 'run' || request.kind === 'stream') && 'input' in request;\n}\n\nfunction post<TOutput>(message: WorkerResponse<TOutput>): void {\n (self as unknown as { postMessage(data: WorkerResponse<TOutput>): void }).postMessage(message);\n}\n\nfunction postError(id: number, error: unknown): void {\n post({ error: serializeError(error), id, kind: 'error', version: PROTOCOL_VERSION });\n}\n\n/** Register a module worker that handles one request and returns one result. */\nexport function exposeTask<TInput, TOutput>(handler: TaskHandler<TInput, TOutput>): void {\n (self as unknown as { onmessage: (event: MessageEvent<unknown>) => void }).onmessage = async (event) => {\n if (!isRequest(event.data) || event.data.version !== PROTOCOL_VERSION || event.data.kind !== 'run') return;\n\n const { id, input } = event.data;\n\n try {\n const value = await handler(input as TInput);\n\n post({ id, kind: 'result', value, version: PROTOCOL_VERSION });\n } catch (error) {\n postError(id, error);\n }\n };\n}\n\n/** Register a module worker that yields chunks for each request. */\nexport function exposeStream<TInput, TChunk>(handler: StreamHandler<TInput, TChunk>): void {\n (self as unknown as { onmessage: (event: MessageEvent<unknown>) => void }).onmessage = async (event) => {\n if (!isRequest(event.data) || event.data.version !== PROTOCOL_VERSION || event.data.kind !== 'stream') return;\n\n const { id, input } = event.data;\n\n try {\n for await (const value of await handler(input as TInput)) {\n post({ id, kind: 'chunk', value, version: PROTOCOL_VERSION });\n }\n\n post({ id, kind: 'result', value: undefined as never, version: PROTOCOL_VERSION });\n } catch (error) {\n postError(id, error);\n }\n };\n}\n"],"mappings":"mEACA,IAAa,EAAmB,EAqBhC,SAAS,EAAe,EAAiC,CACvD,IAAM,EAAQ,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAEtE,MAAO,CAAE,QAAS,EAAM,QAAS,KAAM,EAAM,KAAM,MAAO,EAAM,KAAM,CACxE,CAEA,SAAS,EAAU,EAAiD,CAClE,GAAI,OAAO,GAAU,WAAY,EAAgB,MAAO,GAExD,IAAM,EAAU,EAEhB,OAAO,OAAO,EAAQ,IAAO,WAAa,EAAQ,OAAS,OAAS,EAAQ,OAAS,WAAa,UAAW,CAC/G,CAEA,SAAS,EAAc,EAAwC,CAC7D,KAA0E,YAAY,CAAO,CAC/F,CAEA,SAAS,EAAU,EAAY,EAAsB,CACnD,EAAK,CAAE,MAAO,EAAe,CAAK,EAAG,KAAI,KAAM,QAAS,QAAA,CAA0B,CAAC,CACrF,CAGA,SAAgB,EAA4B,EAA6C,CACvF,KAA2E,UAAY,KAAO,IAAU,CACtG,GAAI,CAAC,EAAU,EAAM,IAAI,GAAK,EAAM,KAAK,UAAA,GAAgC,EAAM,KAAK,OAAS,MAAO,OAEpG,GAAM,CAAE,KAAI,SAAU,EAAM,KAE5B,GAAI,CAGF,EAAK,CAAE,KAAI,KAAM,SAAU,MAAA,MAFP,EAAQ,CAAe,EAET,QAAA,CAA0B,CAAC,CAC/D,OAAS,EAAO,CACd,EAAU,EAAI,CAAK,CACrB,CACF,CACF,CAGA,SAAgB,EAA6B,EAA8C,CACzF,KAA2E,UAAY,KAAO,IAAU,CACtG,GAAI,CAAC,EAAU,EAAM,IAAI,GAAK,EAAM,KAAK,UAAA,GAAgC,EAAM,KAAK,OAAS,SAAU,OAEvG,GAAM,CAAE,KAAI,SAAU,EAAM,KAE5B,GAAI,CACF,UAAW,IAAM,KAAS,MAAM,EAAQ,CAAe,EACrD,EAAK,CAAE,KAAI,KAAM,QAAS,QAAO,QAAA,CAA0B,CAAC,EAG9D,EAAK,CAAE,KAAI,KAAM,SAAU,MAAO,IAAA,GAAoB,QAAA,CAA0B,CAAC,CACnF,OAAS,EAAO,CACd,EAAU,EAAI,CAAK,CACrB,CACF,CACF"}
@@ -1,45 +1,41 @@
1
- /**
2
- * Host↔worker message protocol helpers for module worker files.
3
- *
4
- * Import this sub-path in module worker files to implement the protocol
5
- * without manual boilerplate.
6
- *
7
- * @example
8
- * // my-worker.ts
9
- * import { handleMessages } from '@vielzeug/familiar/protocol';
10
- * handleMessages(async (input: number) => input * 2);
11
- */
12
- /**
13
- * Sets up the `self.onmessage` handler for a module worker.
14
- * Handles the `{ id, input }` → `{ id, result }` / `{ id, error }` protocol automatically.
15
- *
16
- * Errors from `fn` are caught and forwarded as structured `{ id, error }` messages so the
17
- * host can reconstruct them as `FamiliarTaskError`. Non-Error throws are wrapped in an Error.
18
- *
19
- * @example
20
- * // my-worker.ts
21
- * import { handleMessages } from '@vielzeug/familiar/protocol';
22
- *
23
- * handleMessages(async (input: { a: number; b: number }) => input.a + input.b);
24
- */
25
- export declare function handleMessages<TInput, TOutput>(fn: (input: TInput) => TOutput | Promise<TOutput>): void;
26
- /**
27
- * Sets up the `self.onmessage` handler for a streaming module worker.
28
- * The task function must return an `AsyncIterable<TOutput>`; each yielded value is forwarded
29
- * as a `{ id, chunk }` message, followed by `{ id, result: undefined }` on completion.
30
- *
31
- * Mirrors the inline blob worker streaming protocol so `runStream()` works with module workers.
32
- * Errors are forwarded as `{ id, error }` messages.
33
- *
34
- * @example
35
- * // my-streaming-worker.ts
36
- * import { handleStreamMessages } from '@vielzeug/familiar/protocol';
37
- *
38
- * handleStreamMessages(async function* (n: number) {
39
- * for (let i = 0; i < n; i++) {
40
- * yield i;
41
- * }
42
- * });
43
- */
44
- export declare function handleStreamMessages<TInput, TOutput>(fn: (input: TInput) => AsyncIterable<TOutput> | Promise<AsyncIterable<TOutput>>): void;
1
+ /** Current host-to-worker protocol version. */
2
+ export declare const PROTOCOL_VERSION: 1;
3
+ export type SerializedError = {
4
+ message: string;
5
+ name: string;
6
+ stack?: string;
7
+ };
8
+ export type WorkerRequest<TInput> = {
9
+ id: number;
10
+ input: TInput;
11
+ kind: 'run';
12
+ version: typeof PROTOCOL_VERSION;
13
+ } | {
14
+ id: number;
15
+ input: TInput;
16
+ kind: 'stream';
17
+ version: typeof PROTOCOL_VERSION;
18
+ };
19
+ export type WorkerResponse<TOutput> = {
20
+ id: number;
21
+ kind: 'chunk';
22
+ value: TOutput;
23
+ version: typeof PROTOCOL_VERSION;
24
+ } | {
25
+ error: SerializedError;
26
+ id: number;
27
+ kind: 'error';
28
+ version: typeof PROTOCOL_VERSION;
29
+ } | {
30
+ id: number;
31
+ kind: 'result';
32
+ value: TOutput;
33
+ version: typeof PROTOCOL_VERSION;
34
+ };
35
+ export type TaskHandler<TInput, TOutput> = (input: TInput) => TOutput | Promise<TOutput>;
36
+ export type StreamHandler<TInput, TChunk> = (input: TInput) => AsyncIterable<TChunk> | Promise<AsyncIterable<TChunk>>;
37
+ /** Register a module worker that handles one request and returns one result. */
38
+ export declare function exposeTask<TInput, TOutput>(handler: TaskHandler<TInput, TOutput>): void;
39
+ /** Register a module worker that yields chunks for each request. */
40
+ export declare function exposeStream<TInput, TChunk>(handler: StreamHandler<TInput, TChunk>): void;
45
41
  //# sourceMappingURL=protocol.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAoBH;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAcvG;AAID;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAClD,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,aAAa,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,GAC9E,IAAI,CAqBN"}
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,eAAO,MAAM,gBAAgB,EAAG,CAAU,CAAC;AAE3C,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,MAAM,IAC5B;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,OAAO,gBAAgB,CAAA;CAAE,GAC5E;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,OAAO,gBAAgB,CAAA;CAAE,CAAC;AAEpF,MAAM,MAAM,cAAc,CAAC,OAAO,IAC9B;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,gBAAgB,CAAA;CAAE,GAC/E;IAAE,KAAK,EAAE,eAAe,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,gBAAgB,CAAA;CAAE,GACvF;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,gBAAgB,CAAA;CAAE,CAAC;AAErF,MAAM,MAAM,WAAW,CAAC,MAAM,EAAE,OAAO,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEzF,MAAM,MAAM,aAAa,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,aAAa,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;AAwBtH,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAcvF;AAED,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAgBzF"}
package/dist/protocol.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region src/protocol.ts
2
- var e = 2;
2
+ var e = 1;
3
3
  function t(e) {
4
4
  let t = e instanceof Error ? e : Error(String(e));
5
5
  return {
@@ -9,45 +9,60 @@ function t(e) {
9
9
  };
10
10
  }
11
11
  function n(e) {
12
- self.onmessage = async (n) => {
13
- let { id: r, input: i } = n.data;
12
+ if (typeof e != "object" || !e) return !1;
13
+ let t = e;
14
+ return typeof t.id == "number" && (t.kind === "run" || t.kind === "stream") && "input" in t;
15
+ }
16
+ function r(e) {
17
+ self.postMessage(e);
18
+ }
19
+ function i(e, n) {
20
+ r({
21
+ error: t(n),
22
+ id: e,
23
+ kind: "error",
24
+ version: 1
25
+ });
26
+ }
27
+ function a(e) {
28
+ self.onmessage = async (t) => {
29
+ if (!n(t.data) || t.data.version !== 1 || t.data.kind !== "run") return;
30
+ let { id: a, input: o } = t.data;
14
31
  try {
15
- let t = await e(i);
16
- self.postMessage({
17
- id: r,
18
- result: t
32
+ r({
33
+ id: a,
34
+ kind: "result",
35
+ value: await e(o),
36
+ version: 1
19
37
  });
20
38
  } catch (e) {
21
- self.postMessage({
22
- error: t(e),
23
- id: r
24
- });
39
+ i(a, e);
25
40
  }
26
41
  };
27
42
  }
28
- function r(e) {
29
- let n = self;
30
- n.onmessage = async (r) => {
31
- let { id: i, input: a } = r.data;
43
+ function o(e) {
44
+ self.onmessage = async (t) => {
45
+ if (!n(t.data) || t.data.version !== 1 || t.data.kind !== "stream") return;
46
+ let { id: a, input: o } = t.data;
32
47
  try {
33
- let t = await e(a);
34
- for await (let e of t) n.postMessage({
35
- chunk: e,
36
- id: i
48
+ for await (let t of await e(o)) r({
49
+ id: a,
50
+ kind: "chunk",
51
+ value: t,
52
+ version: 1
37
53
  });
38
- n.postMessage({
39
- id: i,
40
- result: void 0
54
+ r({
55
+ id: a,
56
+ kind: "result",
57
+ value: void 0,
58
+ version: 1
41
59
  });
42
60
  } catch (e) {
43
- n.postMessage({
44
- error: t(e),
45
- id: i
46
- });
61
+ i(a, e);
47
62
  }
48
63
  };
49
64
  }
50
65
  //#endregion
51
- export { e as PROTOCOL_VERSION, n as handleMessages, r as handleStreamMessages };
66
+ export { e as PROTOCOL_VERSION, o as exposeStream, a as exposeTask };
52
67
 
53
68
  //# sourceMappingURL=protocol.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","names":[],"sources":["../src/protocol.ts"],"sourcesContent":["/**\n * Host↔worker message protocol helpers for module worker files.\n *\n * Import this sub-path in module worker files to implement the protocol\n * without manual boilerplate.\n *\n * @example\n * // my-worker.ts\n * import { handleMessages } from '@vielzeug/familiar/protocol';\n * handleMessages(async (input: number) => input * 2);\n */\n\n/**\n * Current host↔worker message protocol version.\n * Increment when the protocol changes in a breaking way.\n * The host does not validate this value at runtime it is a debugging convention only.\n * @internal\n */\nexport const PROTOCOL_VERSION = 2 as const;\n\ntype ProtocolMessage<TInput> = { id: number; input: TInput };\n\ntype ErrorPayload = { message: string; name: string; stack?: string };\n\nfunction serializeError(e: unknown): ErrorPayload {\n const err = e instanceof Error ? e : new Error(String(e));\n\n return { message: err.message, name: err.name, stack: err.stack };\n}\n\n/**\n * Sets up the `self.onmessage` handler for a module worker.\n * Handles the `{ id, input }` `{ id, result }` / `{ id, error }` protocol automatically.\n *\n * Errors from `fn` are caught and forwarded as structured `{ id, error }` messages so the\n * host can reconstruct them as `FamiliarTaskError`. Non-Error throws are wrapped in an Error.\n *\n * @example\n * // my-worker.ts\n * import { handleMessages } from '@vielzeug/familiar/protocol';\n *\n * handleMessages(async (input: { a: number; b: number }) => input.a + input.b);\n */\nexport function handleMessages<TInput, TOutput>(fn: (input: TInput) => TOutput | Promise<TOutput>): void {\n (self as unknown as { onmessage: (event: MessageEvent<ProtocolMessage<TInput>>) => void }).onmessage = async (\n event,\n ) => {\n const { id, input } = event.data;\n\n try {\n const result = await fn(input);\n\n (self as unknown as { postMessage: (data: unknown) => void }).postMessage({ id, result });\n } catch (e) {\n (self as unknown as { postMessage: (data: unknown) => void }).postMessage({ error: serializeError(e), id });\n }\n };\n}\n\ntype StreamProtocolMessage<TInput> = { id: number; input: TInput; stream: true };\n\n/**\n * Sets up the `self.onmessage` handler for a streaming module worker.\n * The task function must return an `AsyncIterable<TOutput>`; each yielded value is forwarded\n * as a `{ id, chunk }` message, followed by `{ id, result: undefined }` on completion.\n *\n * Mirrors the inline blob worker streaming protocol so `runStream()` works with module workers.\n * Errors are forwarded as `{ id, error }` messages.\n *\n * @example\n * // my-streaming-worker.ts\n * import { handleStreamMessages } from '@vielzeug/familiar/protocol';\n *\n * handleStreamMessages(async function* (n: number) {\n * for (let i = 0; i < n; i++) {\n * yield i;\n * }\n * });\n */\nexport function handleStreamMessages<TInput, TOutput>(\n fn: (input: TInput) => AsyncIterable<TOutput> | Promise<AsyncIterable<TOutput>>,\n): void {\n const _self = self as unknown as {\n onmessage: (event: MessageEvent<StreamProtocolMessage<TInput>>) => void;\n postMessage: (data: unknown) => void;\n };\n\n _self.onmessage = async (event) => {\n const { id, input } = event.data;\n\n try {\n const iterable = await fn(input);\n\n for await (const chunk of iterable) {\n _self.postMessage({ chunk, id });\n }\n\n _self.postMessage({ id, result: undefined });\n } catch (e) {\n _self.postMessage({ error: serializeError(e), id });\n }\n };\n}\n"],"mappings":";AAkBA,IAAa,IAAmB;AAMhC,SAAS,EAAe,GAA0B;CAChD,IAAM,IAAM,aAAa,QAAQ,IAAQ,MAAM,OAAO,CAAC,CAAC;CAExD,OAAO;EAAE,SAAS,EAAI;EAAS,MAAM,EAAI;EAAM,OAAO,EAAI;CAAM;AAClE;AAeA,SAAgB,EAAgC,GAAyD;CACvG,KAA2F,YAAY,OACrG,MACG;EACH,IAAM,EAAE,OAAI,aAAU,EAAM;EAE5B,IAAI;GACF,IAAM,IAAS,MAAM,EAAG,CAAK;GAE7B,KAA8D,YAAY;IAAE;IAAI;GAAO,CAAC;EAC1F,SAAS,GAAG;GACV,KAA8D,YAAY;IAAE,OAAO,EAAe,CAAC;IAAG;GAAG,CAAC;EAC5G;CACF;AACF;AAsBA,SAAgB,EACd,GACM;CACN,IAAM,IAAQ;CAKd,EAAM,YAAY,OAAO,MAAU;EACjC,IAAM,EAAE,OAAI,aAAU,EAAM;EAE5B,IAAI;GACF,IAAM,IAAW,MAAM,EAAG,CAAK;GAE/B,WAAW,IAAM,KAAS,GACxB,EAAM,YAAY;IAAE;IAAO;GAAG,CAAC;GAGjC,EAAM,YAAY;IAAE;IAAI,QAAQ,KAAA;GAAU,CAAC;EAC7C,SAAS,GAAG;GACV,EAAM,YAAY;IAAE,OAAO,EAAe,CAAC;IAAG;GAAG,CAAC;EACpD;CACF;AACF"}
1
+ {"version":3,"file":"protocol.js","names":[],"sources":["../src/protocol.ts"],"sourcesContent":["/** Current host-to-worker protocol version. */\nexport const PROTOCOL_VERSION = 1 as const;\n\nexport type SerializedError = {\n message: string;\n name: string;\n stack?: string;\n};\n\nexport type WorkerRequest<TInput> =\n | { id: number; input: TInput; kind: 'run'; version: typeof PROTOCOL_VERSION }\n | { id: number; input: TInput; kind: 'stream'; version: typeof PROTOCOL_VERSION };\n\nexport type WorkerResponse<TOutput> =\n | { id: number; kind: 'chunk'; value: TOutput; version: typeof PROTOCOL_VERSION }\n | { error: SerializedError; id: number; kind: 'error'; version: typeof PROTOCOL_VERSION }\n | { id: number; kind: 'result'; value: TOutput; version: typeof PROTOCOL_VERSION };\n\nexport type TaskHandler<TInput, TOutput> = (input: TInput) => TOutput | Promise<TOutput>;\n\nexport type StreamHandler<TInput, TChunk> = (input: TInput) => AsyncIterable<TChunk> | Promise<AsyncIterable<TChunk>>;\n\nfunction serializeError(error: unknown): SerializedError {\n const value = error instanceof Error ? error : new Error(String(error));\n\n return { message: value.message, name: value.name, stack: value.stack };\n}\n\nfunction isRequest(value: unknown): value is WorkerRequest<unknown> {\n if (typeof value !== 'object' || value === null) return false;\n\n const request = value as Partial<WorkerRequest<unknown>>;\n\n return typeof request.id === 'number' && (request.kind === 'run' || request.kind === 'stream') && 'input' in request;\n}\n\nfunction post<TOutput>(message: WorkerResponse<TOutput>): void {\n (self as unknown as { postMessage(data: WorkerResponse<TOutput>): void }).postMessage(message);\n}\n\nfunction postError(id: number, error: unknown): void {\n post({ error: serializeError(error), id, kind: 'error', version: PROTOCOL_VERSION });\n}\n\n/** Register a module worker that handles one request and returns one result. */\nexport function exposeTask<TInput, TOutput>(handler: TaskHandler<TInput, TOutput>): void {\n (self as unknown as { onmessage: (event: MessageEvent<unknown>) => void }).onmessage = async (event) => {\n if (!isRequest(event.data) || event.data.version !== PROTOCOL_VERSION || event.data.kind !== 'run') return;\n\n const { id, input } = event.data;\n\n try {\n const value = await handler(input as TInput);\n\n post({ id, kind: 'result', value, version: PROTOCOL_VERSION });\n } catch (error) {\n postError(id, error);\n }\n };\n}\n\n/** Register a module worker that yields chunks for each request. */\nexport function exposeStream<TInput, TChunk>(handler: StreamHandler<TInput, TChunk>): void {\n (self as unknown as { onmessage: (event: MessageEvent<unknown>) => void }).onmessage = async (event) => {\n if (!isRequest(event.data) || event.data.version !== PROTOCOL_VERSION || event.data.kind !== 'stream') return;\n\n const { id, input } = event.data;\n\n try {\n for await (const value of await handler(input as TInput)) {\n post({ id, kind: 'chunk', value, version: PROTOCOL_VERSION });\n }\n\n post({ id, kind: 'result', value: undefined as never, version: PROTOCOL_VERSION });\n } catch (error) {\n postError(id, error);\n }\n };\n}\n"],"mappings":";AACA,IAAa,IAAmB;AAqBhC,SAAS,EAAe,GAAiC;CACvD,IAAM,IAAQ,aAAiB,QAAQ,IAAY,MAAM,OAAO,CAAK,CAAC;CAEtE,OAAO;EAAE,SAAS,EAAM;EAAS,MAAM,EAAM;EAAM,OAAO,EAAM;CAAM;AACxE;AAEA,SAAS,EAAU,GAAiD;CAClE,IAAI,OAAO,KAAU,aAAY,GAAgB,OAAO;CAExD,IAAM,IAAU;CAEhB,OAAO,OAAO,EAAQ,MAAO,aAAa,EAAQ,SAAS,SAAS,EAAQ,SAAS,aAAa,WAAW;AAC/G;AAEA,SAAS,EAAc,GAAwC;CAC7D,KAA0E,YAAY,CAAO;AAC/F;AAEA,SAAS,EAAU,GAAY,GAAsB;CACnD,EAAK;EAAE,OAAO,EAAe,CAAK;EAAG;EAAI,MAAM;EAAS,SAAA;CAA0B,CAAC;AACrF;AAGA,SAAgB,EAA4B,GAA6C;CACvF,KAA2E,YAAY,OAAO,MAAU;EACtG,IAAI,CAAC,EAAU,EAAM,IAAI,KAAK,EAAM,KAAK,YAAA,KAAgC,EAAM,KAAK,SAAS,OAAO;EAEpG,IAAM,EAAE,OAAI,aAAU,EAAM;EAE5B,IAAI;GAGF,EAAK;IAAE;IAAI,MAAM;IAAU,OAAA,MAFP,EAAQ,CAAe;IAET,SAAA;GAA0B,CAAC;EAC/D,SAAS,GAAO;GACd,EAAU,GAAI,CAAK;EACrB;CACF;AACF;AAGA,SAAgB,EAA6B,GAA8C;CACzF,KAA2E,YAAY,OAAO,MAAU;EACtG,IAAI,CAAC,EAAU,EAAM,IAAI,KAAK,EAAM,KAAK,YAAA,KAAgC,EAAM,KAAK,SAAS,UAAU;EAEvG,IAAM,EAAE,OAAI,aAAU,EAAM;EAE5B,IAAI;GACF,WAAW,IAAM,KAAS,MAAM,EAAQ,CAAe,GACrD,EAAK;IAAE;IAAI,MAAM;IAAS;IAAO,SAAA;GAA0B,CAAC;GAG9D,EAAK;IAAE;IAAI,MAAM;IAAU,OAAO,KAAA;IAAoB,SAAA;GAA0B,CAAC;EACnF,SAAS,GAAO;GACd,EAAU,GAAI,CAAK;EACrB;CACF;AACF"}
@@ -1,2 +1,2 @@
1
- const e=require("../errors.cjs"),t=require("../_pool.cjs");function n(n,r={}){let{concurrency:i=1,errorWrapping:a=!1,maxQueue:o,onFull:s=`reject`}=r;if(!Number.isInteger(i)||i<1)throw new e.FamiliarInvalidOptionsError("`concurrency` must be a positive integer");if(o!==void 0&&(!Number.isInteger(o)||o<1))throw new e.FamiliarInvalidOptionsError("`maxQueue` must be a positive integer");let c=[];function l(){let t=!1;return{cancel(){},prime(){return Promise.resolve()},async run(r,i,o){if(t)return Promise.reject(new e.FamiliarTerminatedError);try{let e=await n(r);return c.push({input:r,output:e}),e}catch(t){if(!a)throw t;let n=t instanceof Error?t:Error(String(t));throw new e.FamiliarTaskError(n.message,{cause:n})}},runStream(t,n,r){return{[Symbol.asyncIterator](){return{next(){return Promise.reject(new e.FamiliarRuntimeError(`runStream() is not supported by createTestWorker`))}}}}},terminate(){t=!0}}}let u=Array.from({length:i},l),d=t.createPool(u,{concurrency:i,defaultTimeout:void 0,maxQueue:o,onFull:s});return Object.defineProperty(d,"calls",{enumerable:!0,get(){return c}}),d}exports.FamiliarError=e.FamiliarError,exports.FamiliarInvalidOptionsError=e.FamiliarInvalidOptionsError,exports.FamiliarQueueFullError=e.FamiliarQueueFullError,exports.FamiliarRuntimeError=e.FamiliarRuntimeError,exports.FamiliarTaskError=e.FamiliarTaskError,exports.FamiliarTerminatedError=e.FamiliarTerminatedError,exports.FamiliarTimeoutError=e.FamiliarTimeoutError,exports.createTestWorker=n;
1
+ const e=require("../errors.cjs"),t=require("../_pool.cjs");function n(n,r={}){let{concurrency:i=1,maxQueue:a,onFull:o=`reject`,timeout:s}=r;if(!Number.isInteger(i)||i<1)throw new e.FamiliarInvalidOptionsError("`concurrency` must be a positive integer");if(a!==void 0&&(!Number.isInteger(a)||a<1))throw new e.FamiliarInvalidOptionsError("`maxQueue` must be a positive integer");if(s!==void 0&&(!Number.isFinite(s)||s<=0))throw new e.FamiliarInvalidOptionsError("`timeout` must be a finite number greater than 0");let c=[];function l(){let t,r=!1;return{cancel(e){t?.reject(e),t=void 0},prime:()=>Promise.resolve(),run(i,a,o){if(r)return Promise.reject(new e.FamiliarTerminatedError);let s;try{s=structuredClone(i,{transfer:a})}catch(t){return Promise.reject(new e.FamiliarTaskError(`Failed to clone task input`,{cause:t}))}return new Promise((r,i)=>{let a=Symbol(`task`),l,u=(e,n)=>{t?.token===a&&(t=void 0,l&&clearTimeout(l),e(n))},d=e=>{t?.token===a&&(t=void 0,l&&clearTimeout(l),c.push({input:s,reason:e,status:`rejected`}),i(e))};t={reject:d,token:a},o!==void 0&&(l=setTimeout(()=>d(new e.FamiliarTimeoutError(o)),o)),Promise.resolve().then(()=>n(s)).then(n=>{let i;try{i=structuredClone(n)}catch(t){d(new e.FamiliarTaskError(`Failed to clone task output`,{cause:t}));return}t?.token===a&&(c.push({input:s,status:`fulfilled`,value:i}),u(r,i))},t=>{let n=t instanceof Error?t:Error(String(t));d(new e.FamiliarTaskError(n.message,{cause:n}))})})},terminate(){r=!0,t?.reject(new e.FamiliarTerminatedError),t=void 0}}}let u=t.createPool(Array.from({length:i},l),{concurrency:i,defaultTimeout:s,maxQueue:a,onFull:o});return Object.defineProperty(u,"calls",{enumerable:!0,get:()=>c}),u}exports.FamiliarError=e.FamiliarError,exports.FamiliarInvalidOptionsError=e.FamiliarInvalidOptionsError,exports.FamiliarQueueFullError=e.FamiliarQueueFullError,exports.FamiliarRuntimeError=e.FamiliarRuntimeError,exports.FamiliarTaskError=e.FamiliarTaskError,exports.FamiliarTerminatedError=e.FamiliarTerminatedError,exports.FamiliarTimeoutError=e.FamiliarTimeoutError,exports.createTestWorker=n;
2
2
  //# sourceMappingURL=testing.cjs.map