@vielzeug/familiar 1.0.8 → 2.0.1

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 +142 -190
  51. package/dist/worker.js.map +1 -1
  52. package/package.json +37 -32
  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":"testing.cjs","names":[],"sources":["../../src/testing/testing.ts"],"sourcesContent":["import type { SlotStrategy, WorkerHandle, WorkerStatus } from '../types';\n\nimport { createPool } from '../_pool';\nimport {\n FamiliarInvalidOptionsError,\n FamiliarRuntimeError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n} from '../errors';\n\nexport type TestWorkerOptions = {\n /**\n * Number of concurrent in-process execution slots. Default: 1 for deterministic test ordering.\n * Increase only when testing concurrency-specific behavior.\n */\n concurrency?: number;\n /**\n * When true, errors from fn are wrapped in FamiliarTaskError/FamiliarRuntimeError, mirroring\n * real worker behavior. Default: false (errors propagate unwrapped for better test DX).\n */\n errorWrapping?: boolean;\n maxQueue?: number;\n /** 'wait' suspends run() callers when the queue is full instead of rejecting. */\n onFull?: 'reject' | 'wait';\n};\n\nexport type TestWorkerHandle<TInput, TOutput> = WorkerHandle<TInput, TOutput> & {\n /** Recorded { input, output } pairs for every successful run(), in call order. */\n readonly calls: ReadonlyArray<{ input: TInput; output: TOutput }>;\n};\n\nexport function createTestWorker<TInput, TOutput>(\n fn: (input: TInput) => TOutput | Promise<TOutput>,\n options: TestWorkerOptions = {},\n): TestWorkerHandle<TInput, TOutput> {\n const { concurrency = 1, errorWrapping = false, maxQueue, onFull = 'reject' } = options;\n\n if (!Number.isInteger(concurrency) || concurrency < 1) {\n throw new FamiliarInvalidOptionsError('`concurrency` must be a positive integer');\n }\n\n if (maxQueue !== undefined && (!Number.isInteger(maxQueue) || maxQueue < 1)) {\n throw new FamiliarInvalidOptionsError('`maxQueue` must be a positive integer');\n }\n\n const calls: { input: TInput; output: TOutput }[] = [];\n\n /**\n * In-process SlotStrategy. Errors propagate unwrapped by default (better test DX:\n * vitest AssertionErrors surface directly). Set errorWrapping: true to mirror real worker\n * behavior (useful when testing code that checks `error instanceof FamiliarError`).\n */\n function makeSlot(): SlotStrategy<TInput, TOutput> {\n let terminated = false;\n\n return {\n cancel(): void {\n // No-op: in-process tasks cannot be cancelled mid-flight.\n },\n\n prime(): Promise<void> {\n return Promise.resolve();\n },\n\n async run(input: TInput, _transferables: Transferable[], _timeout: number | undefined): Promise<TOutput> {\n if (terminated) return Promise.reject(new FamiliarTerminatedError());\n\n try {\n const output = await fn(input);\n\n calls.push({ input, output });\n\n return output;\n } catch (e) {\n if (!errorWrapping) throw e;\n\n const err = e instanceof Error ? e : new Error(String(e));\n\n throw new FamiliarTaskError(err.message, { cause: err });\n }\n },\n\n runStream(_input: TInput, _transferables: Transferable[], _timeout: number | undefined): AsyncIterable<TOutput> {\n return {\n [Symbol.asyncIterator]() {\n return {\n next(): Promise<IteratorResult<TOutput>> {\n return Promise.reject(new FamiliarRuntimeError('runStream() is not supported by createTestWorker'));\n },\n };\n },\n };\n },\n\n terminate(): void {\n terminated = true;\n },\n };\n }\n\n const slots = Array.from({ length: concurrency }, makeSlot);\n\n const pool = createPool(slots, {\n concurrency,\n defaultTimeout: undefined,\n maxQueue,\n onFull,\n });\n\n // Use Object.defineProperty so the `calls` getter is a true accessor descriptor.\n Object.defineProperty(pool, 'calls', {\n enumerable: true,\n get(): ReadonlyArray<{ input: TInput; output: TOutput }> {\n return calls;\n },\n });\n\n return pool as unknown as TestWorkerHandle<TInput, TOutput>;\n}\n\n// Re-export types consumed by test files so they don't need to import from two places.\nexport type { WorkerHandle, WorkerStatus };\nexport {\n FamiliarError,\n FamiliarInvalidOptionsError,\n FamiliarQueueFullError,\n FamiliarRuntimeError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n FamiliarTimeoutError,\n} from '../errors';\n"],"mappings":"2DA+BA,SAAgB,EACd,EACA,EAA6B,CAAC,EACK,CACnC,GAAM,CAAE,cAAc,EAAG,gBAAgB,GAAO,WAAU,SAAS,UAAa,EAEhF,GAAI,CAAC,OAAO,UAAU,CAAW,GAAK,EAAc,EAClD,MAAM,IAAI,EAAA,4BAA4B,0CAA0C,EAGlF,GAAI,IAAa,IAAA,KAAc,CAAC,OAAO,UAAU,CAAQ,GAAK,EAAW,GACvE,MAAM,IAAI,EAAA,4BAA4B,uCAAuC,EAG/E,IAAM,EAA8C,CAAC,EAOrD,SAAS,GAA0C,CACjD,IAAI,EAAa,GAEjB,MAAO,CACL,QAAe,CAEf,EAEA,OAAuB,CACrB,OAAO,QAAQ,QAAQ,CACzB,EAEA,MAAM,IAAI,EAAe,EAAgC,EAAgD,CACvG,GAAI,EAAY,OAAO,QAAQ,OAAO,IAAI,EAAA,uBAAyB,EAEnE,GAAI,CACF,IAAM,EAAS,MAAM,EAAG,CAAK,EAI7B,OAFA,EAAM,KAAK,CAAE,QAAO,QAAO,CAAC,EAErB,CACT,OAAS,EAAG,CACV,GAAI,CAAC,EAAe,MAAM,EAE1B,IAAM,EAAM,aAAa,MAAQ,EAAQ,MAAM,OAAO,CAAC,CAAC,EAExD,MAAM,IAAI,EAAA,kBAAkB,EAAI,QAAS,CAAE,MAAO,CAAI,CAAC,CACzD,CACF,EAEA,UAAU,EAAgB,EAAgC,EAAsD,CAC9G,MAAO,CACL,CAAC,OAAO,gBAAiB,CACvB,MAAO,CACL,MAAyC,CACvC,OAAO,QAAQ,OAAO,IAAI,EAAA,qBAAqB,kDAAkD,CAAC,CACpG,CACF,CACF,CACF,CACF,EAEA,WAAkB,CAChB,EAAa,EACf,CACF,CACF,CAEA,IAAM,EAAQ,MAAM,KAAK,CAAE,OAAQ,CAAY,EAAG,CAAQ,EAEpD,EAAO,EAAA,WAAW,EAAO,CAC7B,cACA,eAAgB,IAAA,GAChB,WACA,QACF,CAAC,EAUD,OAPA,OAAO,eAAe,EAAM,QAAS,CACnC,WAAY,GACZ,KAAyD,CACvD,OAAO,CACT,CACF,CAAC,EAEM,CACT"}
1
+ {"version":3,"file":"testing.cjs","names":[],"sources":["../../src/testing/testing.ts"],"sourcesContent":["import { createPool } from '../_pool';\nimport {\n FamiliarInvalidOptionsError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n FamiliarTimeoutError,\n} from '../errors';\nimport type { SlotStrategy, WorkerOptions, WorkerPool } from '../types';\n\nexport type TestWorkerOptions = Omit<WorkerOptions, 'concurrency' | 'onSlotError'> & { concurrency?: number };\n\nexport type TestWorkerCall<TInput, TOutput> =\n | { input: TInput; status: 'fulfilled'; value: TOutput }\n | { input: TInput; reason: unknown; status: 'rejected' };\n\nexport type TestWorkerHandle<TInput, TOutput> = WorkerPool<TInput, TOutput> & {\n readonly calls: ReadonlyArray<TestWorkerCall<TInput, TOutput>>;\n};\n\nexport function createTestWorker<TInput, TOutput>(\n handler: (input: TInput) => TOutput | Promise<TOutput>,\n options: TestWorkerOptions = {},\n): TestWorkerHandle<TInput, TOutput> {\n const { concurrency = 1, maxQueue, onFull = 'reject', timeout } = options;\n\n if (!Number.isInteger(concurrency) || concurrency < 1) {\n throw new FamiliarInvalidOptionsError('`concurrency` must be a positive integer');\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 const calls: TestWorkerCall<TInput, TOutput>[] = [];\n\n function makeSlot(): SlotStrategy<TInput, TOutput> {\n let current: { reject(reason: unknown): void; token: symbol } | undefined;\n let terminated = false;\n\n return {\n cancel(reason: unknown): void {\n current?.reject(reason);\n current = undefined;\n },\n prime: () => Promise.resolve(),\n run(input, transferables, timeoutMs): Promise<TOutput> {\n if (terminated) return Promise.reject(new FamiliarTerminatedError());\n\n let clonedInput: TInput;\n\n try {\n clonedInput = structuredClone(input, { transfer: transferables });\n } catch (error) {\n return Promise.reject(new FamiliarTaskError('Failed to clone task input', { cause: error }));\n }\n\n return new Promise<TOutput>((resolve, reject) => {\n const token = Symbol('task');\n let timer: ReturnType<typeof setTimeout> | undefined;\n const settle = (fn: (value: TOutput) => void, value: TOutput): void => {\n if (current?.token !== token) return;\n\n current = undefined;\n\n if (timer) clearTimeout(timer);\n\n fn(value);\n };\n const rejectTask = (reason: unknown): void => {\n if (current?.token !== token) return;\n\n current = undefined;\n\n if (timer) clearTimeout(timer);\n\n calls.push({ input: clonedInput, reason, status: 'rejected' });\n reject(reason);\n };\n\n current = { reject: rejectTask, token };\n\n if (timeoutMs !== undefined) {\n timer = setTimeout(() => rejectTask(new FamiliarTimeoutError(timeoutMs)), timeoutMs);\n }\n\n void Promise.resolve()\n .then(() => handler(clonedInput))\n .then(\n (output) => {\n let clonedOutput: TOutput;\n\n try {\n clonedOutput = structuredClone(output);\n } catch (error) {\n rejectTask(new FamiliarTaskError('Failed to clone task output', { cause: error }));\n\n return;\n }\n\n if (current?.token !== token) return;\n\n calls.push({ input: clonedInput, status: 'fulfilled', value: clonedOutput });\n settle(resolve, clonedOutput);\n },\n (error: unknown) => {\n const cause = error instanceof Error ? error : new Error(String(error));\n\n rejectTask(new FamiliarTaskError(cause.message, { cause }));\n },\n );\n });\n },\n terminate(): void {\n terminated = true;\n current?.reject(new FamiliarTerminatedError());\n current = undefined;\n },\n };\n }\n\n const pool = createPool(Array.from({ length: concurrency }, makeSlot), {\n concurrency,\n defaultTimeout: timeout,\n maxQueue,\n onFull,\n });\n\n Object.defineProperty(pool, 'calls', {\n enumerable: true,\n get: () => calls as ReadonlyArray<TestWorkerCall<TInput, TOutput>>,\n });\n\n return pool as TestWorkerHandle<TInput, TOutput>;\n}\n\nexport {\n FamiliarError,\n FamiliarInvalidOptionsError,\n FamiliarQueueFullError,\n FamiliarRuntimeError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n FamiliarTimeoutError,\n} from '../errors';\nexport type { WorkerPool } from '../types';\n"],"mappings":"2DAmBA,SAAgB,EACd,EACA,EAA6B,CAAC,EACK,CACnC,GAAM,CAAE,cAAc,EAAG,WAAU,SAAS,SAAU,WAAY,EAElE,GAAI,CAAC,OAAO,UAAU,CAAW,GAAK,EAAc,EAClD,MAAM,IAAI,EAAA,4BAA4B,0CAA0C,EAGlF,GAAI,IAAa,IAAA,KAAc,CAAC,OAAO,UAAU,CAAQ,GAAK,EAAW,GACvE,MAAM,IAAI,EAAA,4BAA4B,uCAAuC,EAG/E,GAAI,IAAY,IAAA,KAAc,CAAC,OAAO,SAAS,CAAO,GAAK,GAAW,GACpE,MAAM,IAAI,EAAA,4BAA4B,kDAAkD,EAG1F,IAAM,EAA2C,CAAC,EAElD,SAAS,GAA0C,CACjD,IAAI,EACA,EAAa,GAEjB,MAAO,CACL,OAAO,EAAuB,CAC5B,GAAS,OAAO,CAAM,EACtB,EAAU,IAAA,EACZ,EACA,UAAa,QAAQ,QAAQ,EAC7B,IAAI,EAAO,EAAe,EAA6B,CACrD,GAAI,EAAY,OAAO,QAAQ,OAAO,IAAI,EAAA,uBAAyB,EAEnE,IAAI,EAEJ,GAAI,CACF,EAAc,gBAAgB,EAAO,CAAE,SAAU,CAAc,CAAC,CAClE,OAAS,EAAO,CACd,OAAO,QAAQ,OAAO,IAAI,EAAA,kBAAkB,6BAA8B,CAAE,MAAO,CAAM,CAAC,CAAC,CAC7F,CAEA,OAAO,IAAI,SAAkB,EAAS,IAAW,CAC/C,IAAM,EAAQ,OAAO,MAAM,EACvB,EACE,GAAU,EAA8B,IAAyB,CACjE,GAAS,QAAU,IAEvB,EAAU,IAAA,GAEN,GAAO,aAAa,CAAK,EAE7B,EAAG,CAAK,EACV,EACM,EAAc,GAA0B,CACxC,GAAS,QAAU,IAEvB,EAAU,IAAA,GAEN,GAAO,aAAa,CAAK,EAE7B,EAAM,KAAK,CAAE,MAAO,EAAa,SAAQ,OAAQ,UAAW,CAAC,EAC7D,EAAO,CAAM,EACf,EAEA,EAAU,CAAE,OAAQ,EAAY,OAAM,EAElC,IAAc,IAAA,KAChB,EAAQ,eAAiB,EAAW,IAAI,EAAA,qBAAqB,CAAS,CAAC,EAAG,CAAS,GAGrF,QAAa,QAAQ,CAAC,CACnB,SAAW,EAAQ,CAAW,CAAC,CAAC,CAChC,KACE,GAAW,CACV,IAAI,EAEJ,GAAI,CACF,EAAe,gBAAgB,CAAM,CACvC,OAAS,EAAO,CACd,EAAW,IAAI,EAAA,kBAAkB,8BAA+B,CAAE,MAAO,CAAM,CAAC,CAAC,EAEjF,MACF,CAEI,GAAS,QAAU,IAEvB,EAAM,KAAK,CAAE,MAAO,EAAa,OAAQ,YAAa,MAAO,CAAa,CAAC,EAC3E,EAAO,EAAS,CAAY,EAC9B,EACC,GAAmB,CAClB,IAAM,EAAQ,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAEtE,EAAW,IAAI,EAAA,kBAAkB,EAAM,QAAS,CAAE,OAAM,CAAC,CAAC,CAC5D,CACF,CACJ,CAAC,CACH,EACA,WAAkB,CAChB,EAAa,GACb,GAAS,OAAO,IAAI,EAAA,uBAAyB,EAC7C,EAAU,IAAA,EACZ,CACF,CACF,CAEA,IAAM,EAAO,EAAA,WAAW,MAAM,KAAK,CAAE,OAAQ,CAAY,EAAG,CAAQ,EAAG,CACrE,cACA,eAAgB,EAChB,WACA,QACF,CAAC,EAOD,OALA,OAAO,eAAe,EAAM,QAAS,CACnC,WAAY,GACZ,QAAW,CACb,CAAC,EAEM,CACT"}
@@ -1,27 +1,20 @@
1
- import type { WorkerHandle, WorkerStatus } from '../types';
2
- export type TestWorkerOptions = {
3
- /**
4
- * Number of concurrent in-process execution slots. Default: 1 for deterministic test ordering.
5
- * Increase only when testing concurrency-specific behavior.
6
- */
1
+ import type { WorkerOptions, WorkerPool } from '../types';
2
+ export type TestWorkerOptions = Omit<WorkerOptions, 'concurrency' | 'onSlotError'> & {
7
3
  concurrency?: number;
8
- /**
9
- * When true, errors from fn are wrapped in FamiliarTaskError/FamiliarRuntimeError, mirroring
10
- * real worker behavior. Default: false (errors propagate unwrapped for better test DX).
11
- */
12
- errorWrapping?: boolean;
13
- maxQueue?: number;
14
- /** 'wait' suspends run() callers when the queue is full instead of rejecting. */
15
- onFull?: 'reject' | 'wait';
16
4
  };
17
- export type TestWorkerHandle<TInput, TOutput> = WorkerHandle<TInput, TOutput> & {
18
- /** Recorded { input, output } pairs for every successful run(), in call order. */
19
- readonly calls: ReadonlyArray<{
20
- input: TInput;
21
- output: TOutput;
22
- }>;
5
+ export type TestWorkerCall<TInput, TOutput> = {
6
+ input: TInput;
7
+ status: 'fulfilled';
8
+ value: TOutput;
9
+ } | {
10
+ input: TInput;
11
+ reason: unknown;
12
+ status: 'rejected';
23
13
  };
24
- export declare function createTestWorker<TInput, TOutput>(fn: (input: TInput) => TOutput | Promise<TOutput>, options?: TestWorkerOptions): TestWorkerHandle<TInput, TOutput>;
25
- export type { WorkerHandle, WorkerStatus };
14
+ export type TestWorkerHandle<TInput, TOutput> = WorkerPool<TInput, TOutput> & {
15
+ readonly calls: ReadonlyArray<TestWorkerCall<TInput, TOutput>>;
16
+ };
17
+ export declare function createTestWorker<TInput, TOutput>(handler: (input: TInput) => TOutput | Promise<TOutput>, options?: TestWorkerOptions): TestWorkerHandle<TInput, TOutput>;
26
18
  export { FamiliarError, FamiliarInvalidOptionsError, FamiliarQueueFullError, FamiliarRuntimeError, FamiliarTaskError, FamiliarTerminatedError, FamiliarTimeoutError, } from '../errors';
19
+ export type { WorkerPool } from '../types';
27
20
  //# sourceMappingURL=testing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/testing/testing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAUzE,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,MAAM,EAAE,OAAO,IAAI,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAC9E,kFAAkF;IAClF,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACnE,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAC9C,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,EACjD,OAAO,GAAE,iBAAsB,GAC9B,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAoFnC;AAGD,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;AAC3C,OAAO,EACL,aAAa,EACb,2BAA2B,EAC3B,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/testing/testing.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAgB,aAAa,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAExE,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,aAAa,EAAE,aAAa,GAAG,aAAa,CAAC,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9G,MAAM,MAAM,cAAc,CAAC,MAAM,EAAE,OAAO,IACtC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GACtD;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC;AAE3D,MAAM,MAAM,gBAAgB,CAAC,MAAM,EAAE,OAAO,IAAI,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAC5E,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAChE,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAC9C,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,EACtD,OAAO,GAAE,iBAAsB,GAC9B,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAmHnC;AAED,OAAO,EACL,aAAa,EACb,2BAA2B,EAC3B,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC"}
@@ -2,55 +2,73 @@ import { FamiliarError as e, FamiliarInvalidOptionsError as t, FamiliarQueueFull
2
2
  import { createPool as s } from "../_pool.js";
3
3
  //#region src/testing/testing.ts
4
4
  function c(e, n = {}) {
5
- let { concurrency: o = 1, errorWrapping: c = !1, maxQueue: l, onFull: u = "reject" } = n;
6
- if (!Number.isInteger(o) || o < 1) throw new t("`concurrency` must be a positive integer");
7
- if (l !== void 0 && (!Number.isInteger(l) || l < 1)) throw new t("`maxQueue` must be a positive integer");
5
+ let { concurrency: r = 1, maxQueue: c, onFull: l = "reject", timeout: u } = n;
6
+ if (!Number.isInteger(r) || r < 1) throw new t("`concurrency` must be a positive integer");
7
+ if (c !== void 0 && (!Number.isInteger(c) || c < 1)) throw new t("`maxQueue` must be a positive integer");
8
+ if (u !== void 0 && (!Number.isFinite(u) || u <= 0)) throw new t("`timeout` must be a finite number greater than 0");
8
9
  let d = [];
9
10
  function f() {
10
- let t = !1;
11
+ let t, n = !1;
11
12
  return {
12
- cancel() {},
13
- prime() {
14
- return Promise.resolve();
13
+ cancel(e) {
14
+ t?.reject(e), t = void 0;
15
15
  },
16
- async run(n, r, o) {
17
- if (t) return Promise.reject(new a());
16
+ prime: () => Promise.resolve(),
17
+ run(r, s, c) {
18
+ if (n) return Promise.reject(new a());
19
+ let l;
18
20
  try {
19
- let t = await e(n);
20
- return d.push({
21
- input: n,
22
- output: t
23
- }), t;
21
+ l = structuredClone(r, { transfer: s });
24
22
  } catch (e) {
25
- if (!c) throw e;
26
- let t = e instanceof Error ? e : Error(String(e));
27
- throw new i(t.message, { cause: t });
23
+ return Promise.reject(new i("Failed to clone task input", { cause: e }));
28
24
  }
29
- },
30
- runStream(e, t, n) {
31
- return { [Symbol.asyncIterator]() {
32
- return { next() {
33
- return Promise.reject(new r("runStream() is not supported by createTestWorker"));
34
- } };
35
- } };
25
+ return new Promise((n, r) => {
26
+ let a = Symbol("task"), s, u = (e, n) => {
27
+ t?.token === a && (t = void 0, s && clearTimeout(s), e(n));
28
+ }, f = (e) => {
29
+ t?.token === a && (t = void 0, s && clearTimeout(s), d.push({
30
+ input: l,
31
+ reason: e,
32
+ status: "rejected"
33
+ }), r(e));
34
+ };
35
+ t = {
36
+ reject: f,
37
+ token: a
38
+ }, c !== void 0 && (s = setTimeout(() => f(new o(c)), c)), Promise.resolve().then(() => e(l)).then((e) => {
39
+ let r;
40
+ try {
41
+ r = structuredClone(e);
42
+ } catch (e) {
43
+ f(new i("Failed to clone task output", { cause: e }));
44
+ return;
45
+ }
46
+ t?.token === a && (d.push({
47
+ input: l,
48
+ status: "fulfilled",
49
+ value: r
50
+ }), u(n, r));
51
+ }, (e) => {
52
+ let t = e instanceof Error ? e : Error(String(e));
53
+ f(new i(t.message, { cause: t }));
54
+ });
55
+ });
36
56
  },
37
57
  terminate() {
38
- t = !0;
58
+ n = !0, t?.reject(new a()), t = void 0;
39
59
  }
40
60
  };
41
61
  }
42
- let p = Array.from({ length: o }, f), m = s(p, {
43
- concurrency: o,
44
- defaultTimeout: void 0,
45
- maxQueue: l,
46
- onFull: u
62
+ let p = s(Array.from({ length: r }, f), {
63
+ concurrency: r,
64
+ defaultTimeout: u,
65
+ maxQueue: c,
66
+ onFull: l
47
67
  });
48
- return Object.defineProperty(m, "calls", {
68
+ return Object.defineProperty(p, "calls", {
49
69
  enumerable: !0,
50
- get() {
51
- return d;
52
- }
53
- }), m;
70
+ get: () => d
71
+ }), p;
54
72
  }
55
73
  //#endregion
56
74
  export { e as FamiliarError, t as FamiliarInvalidOptionsError, n as FamiliarQueueFullError, r as FamiliarRuntimeError, i as FamiliarTaskError, a as FamiliarTerminatedError, o as FamiliarTimeoutError, c as createTestWorker };
@@ -1 +1 @@
1
- {"version":3,"file":"testing.js","names":[],"sources":["../../src/testing/testing.ts"],"sourcesContent":["import type { SlotStrategy, WorkerHandle, WorkerStatus } from '../types';\n\nimport { createPool } from '../_pool';\nimport {\n FamiliarInvalidOptionsError,\n FamiliarRuntimeError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n} from '../errors';\n\nexport type TestWorkerOptions = {\n /**\n * Number of concurrent in-process execution slots. Default: 1 for deterministic test ordering.\n * Increase only when testing concurrency-specific behavior.\n */\n concurrency?: number;\n /**\n * When true, errors from fn are wrapped in FamiliarTaskError/FamiliarRuntimeError, mirroring\n * real worker behavior. Default: false (errors propagate unwrapped for better test DX).\n */\n errorWrapping?: boolean;\n maxQueue?: number;\n /** 'wait' suspends run() callers when the queue is full instead of rejecting. */\n onFull?: 'reject' | 'wait';\n};\n\nexport type TestWorkerHandle<TInput, TOutput> = WorkerHandle<TInput, TOutput> & {\n /** Recorded { input, output } pairs for every successful run(), in call order. */\n readonly calls: ReadonlyArray<{ input: TInput; output: TOutput }>;\n};\n\nexport function createTestWorker<TInput, TOutput>(\n fn: (input: TInput) => TOutput | Promise<TOutput>,\n options: TestWorkerOptions = {},\n): TestWorkerHandle<TInput, TOutput> {\n const { concurrency = 1, errorWrapping = false, maxQueue, onFull = 'reject' } = options;\n\n if (!Number.isInteger(concurrency) || concurrency < 1) {\n throw new FamiliarInvalidOptionsError('`concurrency` must be a positive integer');\n }\n\n if (maxQueue !== undefined && (!Number.isInteger(maxQueue) || maxQueue < 1)) {\n throw new FamiliarInvalidOptionsError('`maxQueue` must be a positive integer');\n }\n\n const calls: { input: TInput; output: TOutput }[] = [];\n\n /**\n * In-process SlotStrategy. Errors propagate unwrapped by default (better test DX:\n * vitest AssertionErrors surface directly). Set errorWrapping: true to mirror real worker\n * behavior (useful when testing code that checks `error instanceof FamiliarError`).\n */\n function makeSlot(): SlotStrategy<TInput, TOutput> {\n let terminated = false;\n\n return {\n cancel(): void {\n // No-op: in-process tasks cannot be cancelled mid-flight.\n },\n\n prime(): Promise<void> {\n return Promise.resolve();\n },\n\n async run(input: TInput, _transferables: Transferable[], _timeout: number | undefined): Promise<TOutput> {\n if (terminated) return Promise.reject(new FamiliarTerminatedError());\n\n try {\n const output = await fn(input);\n\n calls.push({ input, output });\n\n return output;\n } catch (e) {\n if (!errorWrapping) throw e;\n\n const err = e instanceof Error ? e : new Error(String(e));\n\n throw new FamiliarTaskError(err.message, { cause: err });\n }\n },\n\n runStream(_input: TInput, _transferables: Transferable[], _timeout: number | undefined): AsyncIterable<TOutput> {\n return {\n [Symbol.asyncIterator]() {\n return {\n next(): Promise<IteratorResult<TOutput>> {\n return Promise.reject(new FamiliarRuntimeError('runStream() is not supported by createTestWorker'));\n },\n };\n },\n };\n },\n\n terminate(): void {\n terminated = true;\n },\n };\n }\n\n const slots = Array.from({ length: concurrency }, makeSlot);\n\n const pool = createPool(slots, {\n concurrency,\n defaultTimeout: undefined,\n maxQueue,\n onFull,\n });\n\n // Use Object.defineProperty so the `calls` getter is a true accessor descriptor.\n Object.defineProperty(pool, 'calls', {\n enumerable: true,\n get(): ReadonlyArray<{ input: TInput; output: TOutput }> {\n return calls;\n },\n });\n\n return pool as unknown as TestWorkerHandle<TInput, TOutput>;\n}\n\n// Re-export types consumed by test files so they don't need to import from two places.\nexport type { WorkerHandle, WorkerStatus };\nexport {\n FamiliarError,\n FamiliarInvalidOptionsError,\n FamiliarQueueFullError,\n FamiliarRuntimeError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n FamiliarTimeoutError,\n} from '../errors';\n"],"mappings":";;;AA+BA,SAAgB,EACd,GACA,IAA6B,CAAC,GACK;CACnC,IAAM,EAAE,iBAAc,GAAG,mBAAgB,IAAO,aAAU,YAAS,aAAa;CAEhF,IAAI,CAAC,OAAO,UAAU,CAAW,KAAK,IAAc,GAClD,MAAM,IAAI,EAA4B,0CAA0C;CAGlF,IAAI,MAAa,KAAA,MAAc,CAAC,OAAO,UAAU,CAAQ,KAAK,IAAW,IACvE,MAAM,IAAI,EAA4B,uCAAuC;CAG/E,IAAM,IAA8C,CAAC;CAOrD,SAAS,IAA0C;EACjD,IAAI,IAAa;EAEjB,OAAO;GACL,SAAe,CAEf;GAEA,QAAuB;IACrB,OAAO,QAAQ,QAAQ;GACzB;GAEA,MAAM,IAAI,GAAe,GAAgC,GAAgD;IACvG,IAAI,GAAY,OAAO,QAAQ,OAAO,IAAI,EAAwB,CAAC;IAEnE,IAAI;KACF,IAAM,IAAS,MAAM,EAAG,CAAK;KAI7B,OAFA,EAAM,KAAK;MAAE;MAAO;KAAO,CAAC,GAErB;IACT,SAAS,GAAG;KACV,IAAI,CAAC,GAAe,MAAM;KAE1B,IAAM,IAAM,aAAa,QAAQ,IAAQ,MAAM,OAAO,CAAC,CAAC;KAExD,MAAM,IAAI,EAAkB,EAAI,SAAS,EAAE,OAAO,EAAI,CAAC;IACzD;GACF;GAEA,UAAU,GAAgB,GAAgC,GAAsD;IAC9G,OAAO,EACL,CAAC,OAAO,iBAAiB;KACvB,OAAO,EACL,OAAyC;MACvC,OAAO,QAAQ,OAAO,IAAI,EAAqB,kDAAkD,CAAC;KACpG,EACF;IACF,EACF;GACF;GAEA,YAAkB;IAChB,IAAa;GACf;EACF;CACF;CAEA,IAAM,IAAQ,MAAM,KAAK,EAAE,QAAQ,EAAY,GAAG,CAAQ,GAEpD,IAAO,EAAW,GAAO;EAC7B;EACA,gBAAgB,KAAA;EAChB;EACA;CACF,CAAC;CAUD,OAPA,OAAO,eAAe,GAAM,SAAS;EACnC,YAAY;EACZ,MAAyD;GACvD,OAAO;EACT;CACF,CAAC,GAEM;AACT"}
1
+ {"version":3,"file":"testing.js","names":[],"sources":["../../src/testing/testing.ts"],"sourcesContent":["import { createPool } from '../_pool';\nimport {\n FamiliarInvalidOptionsError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n FamiliarTimeoutError,\n} from '../errors';\nimport type { SlotStrategy, WorkerOptions, WorkerPool } from '../types';\n\nexport type TestWorkerOptions = Omit<WorkerOptions, 'concurrency' | 'onSlotError'> & { concurrency?: number };\n\nexport type TestWorkerCall<TInput, TOutput> =\n | { input: TInput; status: 'fulfilled'; value: TOutput }\n | { input: TInput; reason: unknown; status: 'rejected' };\n\nexport type TestWorkerHandle<TInput, TOutput> = WorkerPool<TInput, TOutput> & {\n readonly calls: ReadonlyArray<TestWorkerCall<TInput, TOutput>>;\n};\n\nexport function createTestWorker<TInput, TOutput>(\n handler: (input: TInput) => TOutput | Promise<TOutput>,\n options: TestWorkerOptions = {},\n): TestWorkerHandle<TInput, TOutput> {\n const { concurrency = 1, maxQueue, onFull = 'reject', timeout } = options;\n\n if (!Number.isInteger(concurrency) || concurrency < 1) {\n throw new FamiliarInvalidOptionsError('`concurrency` must be a positive integer');\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 const calls: TestWorkerCall<TInput, TOutput>[] = [];\n\n function makeSlot(): SlotStrategy<TInput, TOutput> {\n let current: { reject(reason: unknown): void; token: symbol } | undefined;\n let terminated = false;\n\n return {\n cancel(reason: unknown): void {\n current?.reject(reason);\n current = undefined;\n },\n prime: () => Promise.resolve(),\n run(input, transferables, timeoutMs): Promise<TOutput> {\n if (terminated) return Promise.reject(new FamiliarTerminatedError());\n\n let clonedInput: TInput;\n\n try {\n clonedInput = structuredClone(input, { transfer: transferables });\n } catch (error) {\n return Promise.reject(new FamiliarTaskError('Failed to clone task input', { cause: error }));\n }\n\n return new Promise<TOutput>((resolve, reject) => {\n const token = Symbol('task');\n let timer: ReturnType<typeof setTimeout> | undefined;\n const settle = (fn: (value: TOutput) => void, value: TOutput): void => {\n if (current?.token !== token) return;\n\n current = undefined;\n\n if (timer) clearTimeout(timer);\n\n fn(value);\n };\n const rejectTask = (reason: unknown): void => {\n if (current?.token !== token) return;\n\n current = undefined;\n\n if (timer) clearTimeout(timer);\n\n calls.push({ input: clonedInput, reason, status: 'rejected' });\n reject(reason);\n };\n\n current = { reject: rejectTask, token };\n\n if (timeoutMs !== undefined) {\n timer = setTimeout(() => rejectTask(new FamiliarTimeoutError(timeoutMs)), timeoutMs);\n }\n\n void Promise.resolve()\n .then(() => handler(clonedInput))\n .then(\n (output) => {\n let clonedOutput: TOutput;\n\n try {\n clonedOutput = structuredClone(output);\n } catch (error) {\n rejectTask(new FamiliarTaskError('Failed to clone task output', { cause: error }));\n\n return;\n }\n\n if (current?.token !== token) return;\n\n calls.push({ input: clonedInput, status: 'fulfilled', value: clonedOutput });\n settle(resolve, clonedOutput);\n },\n (error: unknown) => {\n const cause = error instanceof Error ? error : new Error(String(error));\n\n rejectTask(new FamiliarTaskError(cause.message, { cause }));\n },\n );\n });\n },\n terminate(): void {\n terminated = true;\n current?.reject(new FamiliarTerminatedError());\n current = undefined;\n },\n };\n }\n\n const pool = createPool(Array.from({ length: concurrency }, makeSlot), {\n concurrency,\n defaultTimeout: timeout,\n maxQueue,\n onFull,\n });\n\n Object.defineProperty(pool, 'calls', {\n enumerable: true,\n get: () => calls as ReadonlyArray<TestWorkerCall<TInput, TOutput>>,\n });\n\n return pool as TestWorkerHandle<TInput, TOutput>;\n}\n\nexport {\n FamiliarError,\n FamiliarInvalidOptionsError,\n FamiliarQueueFullError,\n FamiliarRuntimeError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n FamiliarTimeoutError,\n} from '../errors';\nexport type { WorkerPool } from '../types';\n"],"mappings":";;;AAmBA,SAAgB,EACd,GACA,IAA6B,CAAC,GACK;CACnC,IAAM,EAAE,iBAAc,GAAG,aAAU,YAAS,UAAU,eAAY;CAElE,IAAI,CAAC,OAAO,UAAU,CAAW,KAAK,IAAc,GAClD,MAAM,IAAI,EAA4B,0CAA0C;CAGlF,IAAI,MAAa,KAAA,MAAc,CAAC,OAAO,UAAU,CAAQ,KAAK,IAAW,IACvE,MAAM,IAAI,EAA4B,uCAAuC;CAG/E,IAAI,MAAY,KAAA,MAAc,CAAC,OAAO,SAAS,CAAO,KAAK,KAAW,IACpE,MAAM,IAAI,EAA4B,kDAAkD;CAG1F,IAAM,IAA2C,CAAC;CAElD,SAAS,IAA0C;EACjD,IAAI,GACA,IAAa;EAEjB,OAAO;GACL,OAAO,GAAuB;IAE5B,AADA,GAAS,OAAO,CAAM,GACtB,IAAU,KAAA;GACZ;GACA,aAAa,QAAQ,QAAQ;GAC7B,IAAI,GAAO,GAAe,GAA6B;IACrD,IAAI,GAAY,OAAO,QAAQ,OAAO,IAAI,EAAwB,CAAC;IAEnE,IAAI;IAEJ,IAAI;KACF,IAAc,gBAAgB,GAAO,EAAE,UAAU,EAAc,CAAC;IAClE,SAAS,GAAO;KACd,OAAO,QAAQ,OAAO,IAAI,EAAkB,8BAA8B,EAAE,OAAO,EAAM,CAAC,CAAC;IAC7F;IAEA,OAAO,IAAI,SAAkB,GAAS,MAAW;KAC/C,IAAM,IAAQ,OAAO,MAAM,GACvB,GACE,KAAU,GAA8B,MAAyB;MACjE,GAAS,UAAU,MAEvB,IAAU,KAAA,GAEN,KAAO,aAAa,CAAK,GAE7B,EAAG,CAAK;KACV,GACM,KAAc,MAA0B;MACxC,GAAS,UAAU,MAEvB,IAAU,KAAA,GAEN,KAAO,aAAa,CAAK,GAE7B,EAAM,KAAK;OAAE,OAAO;OAAa;OAAQ,QAAQ;MAAW,CAAC,GAC7D,EAAO,CAAM;KACf;KAQA,AANA,IAAU;MAAE,QAAQ;MAAY;KAAM,GAElC,MAAc,KAAA,MAChB,IAAQ,iBAAiB,EAAW,IAAI,EAAqB,CAAS,CAAC,GAAG,CAAS,IAGrF,QAAa,QAAQ,CAAC,CACnB,WAAW,EAAQ,CAAW,CAAC,CAAC,CAChC,MACE,MAAW;MACV,IAAI;MAEJ,IAAI;OACF,IAAe,gBAAgB,CAAM;MACvC,SAAS,GAAO;OACd,EAAW,IAAI,EAAkB,+BAA+B,EAAE,OAAO,EAAM,CAAC,CAAC;OAEjF;MACF;MAEI,GAAS,UAAU,MAEvB,EAAM,KAAK;OAAE,OAAO;OAAa,QAAQ;OAAa,OAAO;MAAa,CAAC,GAC3E,EAAO,GAAS,CAAY;KAC9B,IACC,MAAmB;MAClB,IAAM,IAAQ,aAAiB,QAAQ,IAAY,MAAM,OAAO,CAAK,CAAC;MAEtE,EAAW,IAAI,EAAkB,EAAM,SAAS,EAAE,SAAM,CAAC,CAAC;KAC5D,CACF;IACJ,CAAC;GACH;GACA,YAAkB;IAGhB,AAFA,IAAa,IACb,GAAS,OAAO,IAAI,EAAwB,CAAC,GAC7C,IAAU,KAAA;GACZ;EACF;CACF;CAEA,IAAM,IAAO,EAAW,MAAM,KAAK,EAAE,QAAQ,EAAY,GAAG,CAAQ,GAAG;EACrE;EACA,gBAAgB;EAChB;EACA;CACF,CAAC;CAOD,OALA,OAAO,eAAe,GAAM,SAAS;EACnC,YAAY;EACZ,WAAW;CACb,CAAC,GAEM;AACT"}
package/dist/types.d.ts CHANGED
@@ -1,147 +1,77 @@
1
1
  import type { FamiliarRuntimeError } from './errors';
2
- export type TaskFn<TInput, TOutput> = (input: TInput) => TOutput | Promise<TOutput>;
3
2
  export type WorkerStatus = 'idle' | 'running' | 'terminated';
4
- /**
5
- * Execution abstraction consumed by `createPool`.
6
- * Implementors: `Slot` (real worker) and in-process executor (test double).
7
- */
8
- export type SlotStrategy<TInput, TOutput> = {
9
- /**
10
- * Cancel the current in-flight task and stop the underlying worker without marking the slot as
11
- * permanently disposed. The slot can be reused immediately a fresh worker will be created on
12
- * the next run() or runStream() call. Used for streaming early consumer exit (break/throw).
13
- */
14
- cancel(): void;
15
- prime(): Promise<void>;
16
- run(input: TInput, transferables: Transferable[], timeout: number | undefined): Promise<TOutput>;
17
- runStream(input: TInput, transferables: Transferable[], timeout: number | undefined): AsyncIterable<TOutput>;
18
- terminate(): void;
3
+ export type WorkerStats = {
4
+ readonly active: number;
5
+ readonly completed: number;
6
+ readonly failed: number;
7
+ readonly queued: number;
8
+ };
9
+ export type WorkerOptions = {
10
+ /** Number of worker slots. Default: 1. Pass 'auto' to use navigator.hardwareConcurrency. */
11
+ concurrency?: number | 'auto';
12
+ /** Maximum queued operations. Default: unlimited. */
13
+ maxQueue?: number;
14
+ /** Reject new work or wait for queue capacity. Default: 'reject'. */
15
+ onFull?: 'reject' | 'wait';
16
+ /** Called after an unhandled worker runtime error. The failed slot is replaced lazily. */
17
+ onSlotError?: (error: FamiliarRuntimeError) => void;
18
+ /** Default timeout in milliseconds. Timed-out work terminates and replaces its worker slot. */
19
+ timeout?: number;
19
20
  };
20
21
  export type RunOptions = {
21
- /**
22
- * Task scheduling priority. Higher values run before lower values when tasks queue up.
23
- * Within the same priority, tasks run FIFO. Default: 0.
24
- */
22
+ /** Higher values run before lower values. Equal values run FIFO. Default: 0. */
25
23
  priority?: number;
26
- /** AbortSignal to cancel a queued task. Note: in-flight tasks cannot be cancelled. */
24
+ /** Cancels queued, capacity-waiting, or executing work. */
27
25
  signal?: AbortSignal;
28
- /** Per-run timeout in milliseconds. Overrides the pool-level timeout for this task only. */
26
+ /** Per-operation timeout in milliseconds. Overrides WorkerOptions.timeout. */
29
27
  timeout?: number;
30
- /** Transferable objects to move to the worker thread (avoids structured-clone copy). */
28
+ /** Values transferred to the worker instead of structured-cloned. */
31
29
  transferables?: Transferable[];
32
30
  };
33
- export type BatchOptions = Omit<RunOptions, 'signal'> & {
34
- /**
35
- * When false, results are yielded as each task completes (out-of-submission order, maximum
36
- * throughput). Default: true (results are yielded in submission order).
37
- */
38
- ordered?: boolean;
39
- };
40
- export type WorkerOptions = {
41
- /** Number of concurrent worker slots. Default: 1. Pass 'auto' to use navigator.hardwareConcurrency. */
42
- concurrency?: number | 'auto';
43
- /**
44
- * Watchdog window in milliseconds applied to every task in the pool.
45
- * If the worker does not send a heartbeat message within this window, the task is
46
- * killed with FamiliarTimeoutError. Useful for long-running CPU tasks that must stay responsive.
47
- * For inline workers the heartbeat is sent automatically at heartbeatWindow / 2 intervals.
48
- * Module workers must implement the heartbeat protocol manually.
49
- */
50
- heartbeatWindow?: number;
51
- /** Maximum queued tasks. When onFull='reject', exceeding this limit rejects with FamiliarQueueFullError. Default: unlimited. */
52
- maxQueue?: number;
53
- /**
54
- * When 'wait', run() suspends the caller when the queue is full instead of rejecting.
55
- * Useful for large producer→consumer pipelines to apply natural backpressure. Default: 'reject'.
56
- */
57
- onFull?: 'reject' | 'wait';
58
- /**
59
- * Called when a Worker slot encounters an unhandled runtime error (worker.onerror).
60
- * The slot stops automatically; call restart() to pre-warm the replacement Worker.
61
- * If omitted, errors are handled silently and the slot restarts on the next run() call.
62
- */
63
- onSlotError?: (error: FamiliarRuntimeError, restart: () => void) => void;
64
- /** Default task timeout in milliseconds. Can be overridden per-run via RunOptions. Default: none. */
31
+ export type DrainOptions = {
32
+ /** Maximum time to wait before terminating remaining work. */
65
33
  timeout?: number;
66
34
  };
67
- /**
68
- * Full handle returned by `createWorker` and `createModuleWorker`.
69
- * All capabilities are on one flat interface — no need to cross-reference mixin types.
70
- */
71
- export interface WorkerHandle<TInput, TOutput> {
72
- /** Graceful drain — delegates to `drain()`. Enables `await using` declarations. */
35
+ export interface WorkerPool<TInput, TOutput> {
36
+ readonly disposalSignal: AbortSignal;
37
+ dispose(): void;
38
+ readonly disposed: boolean;
39
+ drain(options?: DrainOptions): Promise<void>;
40
+ prime(): Promise<void>;
41
+ run(input: TInput, options?: RunOptions): Promise<TOutput>;
42
+ readonly stats: WorkerStats;
43
+ readonly status: WorkerStatus;
73
44
  [Symbol.asyncDispose](): Promise<void>;
74
- /** Immediate terminate — delegates to `dispose()`. Enables `using` declarations. */
75
45
  [Symbol.dispose](): void;
76
- /** Number of slots currently executing a task. */
77
- readonly active: number;
78
- /**
79
- * Run all inputs through the pool and yield results.
80
- * By default yields in submission order. Pass ordered: false to yield as-completed.
81
- */
82
- batch(inputs: TInput[], options?: BatchOptions): AsyncIterable<TOutput>;
83
- /** Number of successfully completed tasks since creation. */
84
- readonly completed: number;
85
- /** Number of worker slots. */
86
- readonly concurrency: number;
87
- /** `AbortSignal` aborted when the pool is terminated (via `dispose()` or `drain()` settling). */
46
+ }
47
+ export interface StreamWorkerPool<TInput, TChunk> {
88
48
  readonly disposalSignal: AbortSignal;
89
- /** Terminate immediately, rejecting all in-flight and queued tasks. */
90
49
  dispose(): void;
91
- /** `true` after `dispose()` has been called or `drain()` has settled. */
92
50
  readonly disposed: boolean;
93
- /** Gracefully drain queued/in-flight tasks then terminate workers. Rejects if timeoutMs elapses. */
94
- drain(timeoutMs?: number): Promise<void>;
95
- /** Number of tasks that failed with a task / timeout / worker error (excludes aborts and terminations). */
96
- readonly failed: number;
97
- /** Create a task group. All tasks share an AbortController and can be drained together. */
98
- group(name?: string, options?: GroupOptions): TaskGroup<TInput, TOutput>;
99
- /** Number of active groups (created but not yet fully drained or aborted). */
100
- readonly groupCount: number;
101
- /** Pre-initialize all worker slots to reduce first-task latency. */
51
+ drain(options?: DrainOptions): Promise<void>;
102
52
  prime(): Promise<void>;
103
- /** Number of queued tasks waiting to run (excludes cancelled/aborted items). */
104
- readonly queued: number;
105
- /** Execute the task. Tasks are queued when all slots are busy. */
106
- run(input: TInput, options?: RunOptions): Promise<TOutput>;
107
- /**
108
- * Run a streaming task and yield partial results as they arrive.
109
- * The worker function must return an async iterable; each yielded value is forwarded as a chunk.
110
- *
111
- * Unlike run(), streaming tasks cannot be queued — they require an immediately available slot.
112
- * Throws FamiliarRuntimeError synchronously if all slots are busy.
113
- * Note: `signal` is not supported for streaming tasks (cannot be queued); use `break` to stop early.
114
- */
115
- runStream(input: TInput, options?: Omit<RunOptions, 'signal'>): AsyncIterable<TOutput>;
116
- /** Current lifecycle state of the pool. */
53
+ runStream(input: TInput, options?: RunOptions): AsyncIterable<TChunk>;
54
+ readonly stats: WorkerStats;
117
55
  readonly status: WorkerStatus;
56
+ [Symbol.asyncDispose](): Promise<void>;
57
+ [Symbol.dispose](): void;
118
58
  }
119
- export type GroupOptions = {
120
- /**
121
- * When this signal is aborted, the group is aborted automatically.
122
- * Composable with `WorkerHandle.disposalSignal` to tie the group lifetime to the pool.
123
- */
124
- signal?: AbortSignal;
125
- };
59
+ export type BatchOptions = RunOptions;
126
60
  export type TaskGroup<TInput, TOutput> = {
127
- /** Cancel all pending tasks in this group. In-flight tasks run to natural completion. */
128
61
  abort(reason?: unknown): void;
129
- /**
130
- * Wait for all tasks submitted so far to settle.
131
- * Returns settled results — both fulfilled values and rejection reasons.
132
- * Tasks added after drain() starts are not included in this call.
133
- */
134
62
  drain(): Promise<PromiseSettledResult<TOutput>[]>;
135
- /** Optional name provided when the group was created. */
136
63
  readonly name: string | undefined;
137
- /** Number of tasks not yet settled (decrements as tasks complete). */
138
64
  readonly pending: number;
139
- /**
140
- * Submit a task to the pool, associating it with this group.
141
- * Throws `FamiliarTerminatedError` synchronously if the pool has been disposed or is closing.
142
- */
143
65
  run(input: TInput, options?: Omit<RunOptions, 'signal'>): Promise<TOutput>;
144
- /** Total number of tasks ever submitted to this group (never decrements). */
145
66
  readonly size: number;
146
67
  };
68
+ export type TaskGroupOptions = {
69
+ signal?: AbortSignal;
70
+ };
71
+ export type SlotStrategy<TInput, TOutput> = {
72
+ cancel(reason: unknown): void;
73
+ prime(): Promise<void>;
74
+ run(input: TInput, transferables: Transferable[], timeout: number | undefined): Promise<TOutput>;
75
+ terminate(): void;
76
+ };
147
77
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/worker.cjs CHANGED
@@ -1,28 +1,2 @@
1
- const e=require("./errors.cjs");require("./_dev.cjs");const t=require("./_timers.cjs"),n=require("./_pool.cjs");function r(t){if(t.toString().includes(`[native code]`))throw new e.FamiliarInvalidOptionsError(`Task function cannot be a bound or native function`);return t}var i=512;function a(t){if(t===void 0)return 1;if(t===`auto`)return Math.max(1,globalThis.navigator?.hardwareConcurrency??1);if(!Number.isInteger(t)||t<1||t>i)throw new e.FamiliarInvalidOptionsError(`\`concurrency\` must be a positive integer ≤ ${i} or "auto"`);return t}function o(t={}){let n=a(t.concurrency),{heartbeatWindow:r,maxQueue:i,onFull:o=`reject`,onSlotError:s,timeout:c}=t;if(c!==void 0&&(!Number.isFinite(c)||c<=0))throw new e.FamiliarInvalidOptionsError("`timeout` must be a finite number greater than 0");if(i!==void 0&&(!Number.isInteger(i)||i<1))throw new e.FamiliarInvalidOptionsError("`maxQueue` must be a positive integer");if(r!==void 0&&(!Number.isFinite(r)||r<=0))throw new e.FamiliarInvalidOptionsError("`heartbeatWindow` must be a finite number greater than 0");return{concurrency:n,heartbeatWindow:r,maxQueue:i,onFull:o,onSlotError:s,timeout:c}}function s(e,t){return`
2
- const __fn = (${e.toString()});
3
-
4
- self.onmessage = async function (event) {
5
- const { id, input, stream } = event.data;
6
-
7
- // Automatically send heartbeats at half the heartbeatWindow interval.
8
- let heartbeatTimer = null;
9
- ${t==null?``:`heartbeatTimer = setInterval(() => self.postMessage({ id, heartbeat: true }), ${t});`}
10
-
11
- try {
12
- if (stream) {
13
- const iterable = await __fn(input);
14
- for await (const chunk of iterable) {
15
- self.postMessage({ id, chunk });
16
- }
17
- self.postMessage({ id, result: undefined });
18
- } else {
19
- const result = await __fn(input);
20
- self.postMessage({ id, result });
21
- }
22
- } catch (error) {
23
- self.postMessage({ id, error });
24
- } finally {
25
- if (heartbeatTimer) clearInterval(heartbeatTimer);
26
- }
27
- }`.trim()}var c=class{config;onSlotError;disposed=!1;pending=null;taskId=0;worker=null;constructor(e,t){this.config=e,this.onSlotError=t}prime(){if(this.disposed)return Promise.resolve();try{this.ensureWorker()}catch{}return Promise.resolve()}run(e,t,n){return this.dispatch(e,t,n,!1)}runStream(e,t,n){let r=[],i=!1,a,o=[],s=e=>{r.push(e),o.shift()?.()},c=e=>{i=!0,a=e;for(let e of o.splice(0))e()};return this.dispatch(e,t,n,!0,s).then(()=>c(),c),this.pending&&(this.pending.finishStream=c),{[Symbol.asyncIterator](){let e=0;return{async next(){for(;e>=r.length&&!i;)await new Promise(e=>o.push(e));if(e<r.length){let t=r[e];return r[e]=null,e++,{done:!1,value:t}}if(a!==void 0)throw a;return{done:!0,value:void 0}}}}}}cancel(){let t=this.pending;t&&(clearTimeout(t.timer),clearTimeout(t.heartbeatWatchdog),this.pending=null,this.stopWorker(),t.finishStream?.(new e.FamiliarTerminatedError(`Stream was cancelled`)))}terminate(){this.disposed=!0,this.stopWorker(),this.failPending(new e.FamiliarTerminatedError)}dispatch(n,r,i,a,o){if(this.disposed)return Promise.reject(new e.FamiliarTerminatedError);let s;try{s=this.ensureWorker()}catch(e){return Promise.reject(e)}let c=this.config.kind===`inline`&&this.config.heartbeatInterval!=null?this.config.heartbeatInterval*2:void 0;return new Promise((l,u)=>{let d=this.taskId++,f={emit:o,id:d,reject:u,resolve:l,watchdogMs:c};i!==void 0&&(f.timer=setTimeout(()=>{this.restart(new e.FamiliarTimeoutError(i))},i),t.unrefTimer(f.timer)),c!==void 0&&(f.heartbeatWatchdog=setTimeout(()=>{this.restart(new e.FamiliarTimeoutError(c))},c),t.unrefTimer(f.heartbeatWatchdog)),this.pending=f;try{s.postMessage({id:d,input:n,stream:a},r)}catch(t){this.failPending(new e.FamiliarRuntimeError(t instanceof Error?t.message:String(t),{cause:t}))}})}ensureWorker(){if(this.worker)return this.worker;if(typeof globalThis.Worker!=`function`)throw new e.FamiliarRuntimeError(`Worker API is unavailable in this runtime`);let n;if(this.config.kind===`module`)try{n=new Worker(this.config.url,{type:`module`})}catch(t){throw new e.FamiliarRuntimeError(`Failed to create Worker`,{cause:t})}else try{let e=new Blob([s(this.config.fn,this.config.heartbeatInterval)],{type:`application/javascript`}),t=URL.createObjectURL(e);try{n=new Worker(t)}finally{URL.revokeObjectURL(t)}}catch(t){throw new e.FamiliarRuntimeError(`Failed to create Worker`,{cause:t})}return n.onmessage=n=>{let r=this.pending;if(!(!r||n.data.id!==r.id)){if(`heartbeat`in n.data){r.watchdogMs!==void 0&&(clearTimeout(r.heartbeatWatchdog),r.heartbeatWatchdog=setTimeout(()=>{this.restart(new e.FamiliarTimeoutError(r.watchdogMs))},r.watchdogMs),t.unrefTimer(r.heartbeatWatchdog));return}if(`chunk`in n.data){r.emit?.(n.data.chunk);return}if(clearTimeout(r.timer),clearTimeout(r.heartbeatWatchdog),this.pending=null,`error`in n.data){let t=n.data.error instanceof Error?n.data.error:Error(String(n.data.error));r.reject(new e.FamiliarTaskError(t.message,{cause:t}))}else r.resolve(n.data.result)}},n.onerror=t=>{let n=new e.FamiliarRuntimeError(t.message);this.stopWorker(),this.failPending(n),this.onSlotError?.(n,()=>void this.prime())},this.worker=n,n}failPending(e){let t=this.pending;t&&(clearTimeout(t.timer),clearTimeout(t.heartbeatWatchdog),this.pending=null,t.reject(e))}restart(e){this.stopWorker(),this.failPending(e)}stopWorker(){this.worker&&=(this.worker.terminate(),null)}};function l(e,t){let{concurrency:r,heartbeatWindow:i,maxQueue:a,onFull:s,onSlotError:l,timeout:u}=o(t),d=i==null?void 0:Math.floor(i/2),f=Array.from({length:r},()=>new c({fn:e,heartbeatInterval:d,kind:`inline`},l));return n.createPool(f,{concurrency:r,defaultTimeout:u,maxQueue:a,onFull:s})}function u(e,t){let{concurrency:r,heartbeatWindow:i,maxQueue:a,onFull:s,onSlotError:l,timeout:u}=o(t),d=typeof e==`string`?e:e.href,f=Array.from({length:r},()=>new c({kind:`module`,url:d},l));return n.createPool(f,{concurrency:r,defaultTimeout:u,maxQueue:a,onFull:s})}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=u,exports.createWorker=l,exports.task=r;
1
+ const e=require("./_timers.cjs"),t=require("./errors.cjs"),n=require("./_pool.cjs"),r=require("./_stream-pool.cjs");require("./protocol.cjs");var i=512;function a(e={}){let{concurrency:n=1,maxQueue:r,onFull:a=`reject`,onSlotError:o,timeout:s}=e,c=n===`auto`?Math.max(1,globalThis.navigator?.hardwareConcurrency??1):n;if(!Number.isInteger(c)||c<1||c>i)throw new t.FamiliarInvalidOptionsError(`\`concurrency\` must be a positive integer ≤ ${i} or "auto"`);if(r!==void 0&&(!Number.isInteger(r)||r<1))throw new t.FamiliarInvalidOptionsError("`maxQueue` must be a positive integer");if(s!==void 0&&(!Number.isFinite(s)||s<=0))throw new t.FamiliarInvalidOptionsError("`timeout` must be a finite number greater than 0");return{concurrency:c,maxQueue:r,onFull:a,onSlotError:o,timeout:s}}function o(e){if(typeof e!=`object`||!e)return!1;let t=e;return t.version===1&&typeof t.id==`number`&&(t.kind===`chunk`||t.kind===`error`||t.kind===`result`)}function s(e){let n=Error(e.message);return n.name=e.name,n.stack=e.stack,new t.FamiliarTaskError(e.message,{cause:n})}var c=class{#e;#t;#n=!1;#r;#i=0;#a;constructor(e,t){this.#t=e,this.#e=t}cancel(e){this.#a?.terminate(),this.#a=void 0,this.#l(`reject`,e)}prime(){return this.#n||this.#s(),Promise.resolve()}run(e,t,n){return this.#o(e,t,n,`run`)}stream(e,n){let r=()=>this.cancel(new t.FamiliarTerminatedError(`Stream consumer stopped`)),i=[],a=[],o=!1,s,c=e=>{let t=a.shift();t?t({done:!1,value:e}):i.push(e)},l=e=>{for(o=!0,s=e;a.length>0;)a.shift()(e===void 0?{done:!0,value:void 0}:Promise.reject(e))};return{done:this.#o(e,n.transferables??[],n.timeout,`stream`,c).then(()=>l(),e=>l(e)),iterable:{[Symbol.asyncIterator]:()=>({async next(){if(i.length>0)return{done:!1,value:i.shift()};if(s!==void 0)throw s;return o?{done:!0,value:void 0}:new Promise((e,t)=>{a.push(n=>{n instanceof Promise?n.then(e,t):e(n)})})},async return(){return r(),{done:!0,value:void 0}}})}}}terminate(){this.#n=!0,this.cancel(new t.FamiliarTerminatedError)}#o(n,r,i,a,o){if(this.#n)return Promise.reject(new t.FamiliarTerminatedError);if(this.#r)return Promise.reject(new t.FamiliarRuntimeError(`Worker slot is already busy`));try{let s=this.#s(),c=this.#i++;return new Promise((l,u)=>{let d={emit:o,id:c,reject:u,resolve:l};this.#r=d,i!==void 0&&(d.timer=setTimeout(()=>this.cancel(new t.FamiliarTimeoutError(i)),i),e.unrefTimer(d.timer));try{s.postMessage({id:c,input:n,kind:a,version:1},r)}catch(e){this.cancel(new t.FamiliarRuntimeError(`Failed to post message to worker`,{cause:e}))}})}catch(e){return Promise.reject(e)}}#s(){if(this.#a)return this.#a;if(typeof Worker>`u`)throw new t.FamiliarRuntimeError(`Worker API is unavailable in this runtime`);try{let e=new Worker(this.#t,{type:`module`});return e.onmessage=e=>this.#c(e.data),e.onerror=e=>{let n=new t.FamiliarRuntimeError(e.message||`Worker failed`);this.#a=void 0,this.#e?.(n),this.#l(`reject`,n)},this.#a=e,e}catch(e){throw new t.FamiliarRuntimeError(`Failed to create Worker`,{cause:e})}}#c(e){if(this.#r){if(!o(e)||e.id!==this.#r.id){this.cancel(new t.FamiliarRuntimeError(`Worker returned an incompatible protocol response`));return}if(e.kind===`chunk`){this.#r.emit?.(e.value);return}if(e.kind===`error`){this.#l(`reject`,s(e.error));return}this.#l(`resolve`,e.value)}}#l(e,t){let n=this.#r;n&&(this.#r=void 0,n.timer&&clearTimeout(n.timer),e===`resolve`?n.resolve(t):n.reject(t))}};function l(e,t){return Array.from({length:t.concurrency},()=>new c(e,t.onSlotError))}function u(e,t={}){let r=a(t);return n.createPool(l(e,r),{concurrency:r.concurrency,defaultTimeout:r.timeout,maxQueue:r.maxQueue,onFull:r.onFull})}function d(e,t={}){let n=a(t);return r.createStreamPool(l(e,n),{concurrency:n.concurrency,defaultTimeout:n.timeout,maxQueue:n.maxQueue,onFull:n.onFull})}exports.FamiliarError=t.FamiliarError,exports.FamiliarInvalidOptionsError=t.FamiliarInvalidOptionsError,exports.FamiliarQueueFullError=t.FamiliarQueueFullError,exports.FamiliarRuntimeError=t.FamiliarRuntimeError,exports.FamiliarTaskError=t.FamiliarTaskError,exports.FamiliarTerminatedError=t.FamiliarTerminatedError,exports.FamiliarTimeoutError=t.FamiliarTimeoutError,exports.batch=n.batch,exports.createStreamWorker=d,exports.createTaskGroup=n.createTaskGroup,exports.createWorker=u;
28
2
  //# sourceMappingURL=worker.cjs.map