@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.
- package/README.md +24 -47
- package/dist/_pool.cjs +1 -1
- package/dist/_pool.cjs.map +1 -1
- package/dist/_pool.d.ts +4 -12
- package/dist/_pool.d.ts.map +1 -1
- package/dist/_pool.js +163 -222
- package/dist/_pool.js.map +1 -1
- package/dist/_queue.cjs +1 -1
- package/dist/_queue.cjs.map +1 -1
- package/dist/_queue.d.ts +3 -39
- package/dist/_queue.d.ts.map +1 -1
- package/dist/_queue.js +27 -28
- package/dist/_queue.js.map +1 -1
- package/dist/_stream-pool.cjs +2 -0
- package/dist/_stream-pool.cjs.map +1 -0
- package/dist/_stream-pool.d.ts +11 -0
- package/dist/_stream-pool.d.ts.map +1 -0
- package/dist/_stream-pool.js +152 -0
- package/dist/_stream-pool.js.map +1 -0
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/familiar.cjs +1 -27
- package/dist/familiar.cjs.map +1 -1
- package/dist/familiar.iife.js +1 -27
- package/dist/familiar.iife.js.map +1 -1
- package/dist/familiar.js +1 -27
- package/dist/familiar.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +3 -2
- package/dist/protocol.cjs +1 -1
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.ts +40 -44
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +42 -27
- package/dist/protocol.js.map +1 -1
- package/dist/testing/testing.cjs +1 -1
- package/dist/testing/testing.cjs.map +1 -1
- package/dist/testing/testing.d.ts +15 -22
- package/dist/testing/testing.d.ts.map +1 -1
- package/dist/testing/testing.js +53 -35
- package/dist/testing/testing.js.map +1 -1
- package/dist/types.d.ts +49 -119
- package/dist/types.d.ts.map +1 -1
- package/dist/worker.cjs +1 -27
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.ts +11 -67
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +139 -187
- package/dist/worker.js.map +1 -1
- package/package.json +2 -2
- package/dist/_dev.cjs +0 -2
- package/dist/_dev.cjs.map +0 -1
- package/dist/_dev.js +0 -6
- 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,
|
|
1
|
+
{"version":3,"file":"testing.cjs","names":[],"sources":["../../src/testing/testing.ts"],"sourcesContent":["import type { SlotStrategy, WorkerOptions, WorkerPool } from '../types';\n\nimport { createPool } from '../_pool';\nimport {\n FamiliarInvalidOptionsError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n FamiliarTimeoutError,\n} from '../errors';\n\nexport type TestWorkerOptions = Omit<WorkerOptions, 'concurrency' | 'onSlotError'> & { concurrency?: number };\n\nexport type TestWorkerCall<TInput, TOutput> =\n { input: TInput; status: 'fulfilled'; value: TOutput } | { 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 type { WorkerPool } from '../types';\nexport {\n FamiliarError,\n FamiliarInvalidOptionsError,\n FamiliarQueueFullError,\n FamiliarRuntimeError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n FamiliarTimeoutError,\n} from '../errors';\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 {
|
|
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
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
25
|
-
|
|
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>;
|
|
18
|
+
export type { WorkerPool } from '../types';
|
|
26
19
|
export { FamiliarError, FamiliarInvalidOptionsError, FamiliarQueueFullError, FamiliarRuntimeError, FamiliarTaskError, FamiliarTerminatedError, FamiliarTimeoutError, } from '../errors';
|
|
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,
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/testing/testing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,aAAa,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAUxE,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,IACxC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC;AAElH,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,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EACL,aAAa,EACb,2BAA2B,EAC3B,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,WAAW,CAAC"}
|
package/dist/testing/testing.js
CHANGED
|
@@ -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:
|
|
6
|
-
if (!Number.isInteger(
|
|
7
|
-
if (
|
|
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
|
-
|
|
14
|
-
return Promise.resolve();
|
|
13
|
+
cancel(e) {
|
|
14
|
+
t?.reject(e), t = void 0;
|
|
15
15
|
},
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
prime: () => Promise.resolve(),
|
|
17
|
+
run(r, s, c) {
|
|
18
|
+
if (n) return Promise.reject(new a());
|
|
19
|
+
let l;
|
|
18
20
|
try {
|
|
19
|
-
|
|
20
|
-
return d.push({
|
|
21
|
-
input: n,
|
|
22
|
-
output: t
|
|
23
|
-
}), t;
|
|
21
|
+
l = structuredClone(r, { transfer: s });
|
|
24
22
|
} catch (e) {
|
|
25
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
58
|
+
n = !0, t?.reject(new a()), t = void 0;
|
|
39
59
|
}
|
|
40
60
|
};
|
|
41
61
|
}
|
|
42
|
-
let p = Array.from({ length:
|
|
43
|
-
concurrency:
|
|
44
|
-
defaultTimeout:
|
|
45
|
-
maxQueue:
|
|
46
|
-
onFull:
|
|
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(
|
|
68
|
+
return Object.defineProperty(p, "calls", {
|
|
49
69
|
enumerable: !0,
|
|
50
|
-
get()
|
|
51
|
-
|
|
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,
|
|
1
|
+
{"version":3,"file":"testing.js","names":[],"sources":["../../src/testing/testing.ts"],"sourcesContent":["import type { SlotStrategy, WorkerOptions, WorkerPool } from '../types';\n\nimport { createPool } from '../_pool';\nimport {\n FamiliarInvalidOptionsError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n FamiliarTimeoutError,\n} from '../errors';\n\nexport type TestWorkerOptions = Omit<WorkerOptions, 'concurrency' | 'onSlotError'> & { concurrency?: number };\n\nexport type TestWorkerCall<TInput, TOutput> =\n { input: TInput; status: 'fulfilled'; value: TOutput } | { 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 type { WorkerPool } from '../types';\nexport {\n FamiliarError,\n FamiliarInvalidOptionsError,\n FamiliarQueueFullError,\n FamiliarRuntimeError,\n FamiliarTaskError,\n FamiliarTerminatedError,\n FamiliarTimeoutError,\n} from '../errors';\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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
/**
|
|
24
|
+
/** Cancels queued, capacity-waiting, or executing work. */
|
|
27
25
|
signal?: AbortSignal;
|
|
28
|
-
/** Per-
|
|
26
|
+
/** Per-operation timeout in milliseconds. Overrides WorkerOptions.timeout. */
|
|
29
27
|
timeout?: number;
|
|
30
|
-
/**
|
|
28
|
+
/** Values transferred to the worker instead of structured-cloned. */
|
|
31
29
|
transferables?: Transferable[];
|
|
32
30
|
};
|
|
33
|
-
export type
|
|
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> {
|
|
73
36
|
[Symbol.asyncDispose](): Promise<void>;
|
|
74
|
-
/** Immediate terminate — delegates to `dispose()`. Enables `using` declarations. */
|
|
75
37
|
[Symbol.dispose](): void;
|
|
76
|
-
|
|
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). */
|
|
38
|
+
readonly disposed: boolean;
|
|
88
39
|
readonly disposalSignal: AbortSignal;
|
|
89
|
-
/** Terminate immediately, rejecting all in-flight and queued tasks. */
|
|
90
40
|
dispose(): void;
|
|
91
|
-
|
|
92
|
-
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. */
|
|
41
|
+
drain(options?: DrainOptions): Promise<void>;
|
|
102
42
|
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
43
|
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. */
|
|
44
|
+
readonly stats: WorkerStats;
|
|
117
45
|
readonly status: WorkerStatus;
|
|
118
46
|
}
|
|
119
|
-
export
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
47
|
+
export interface StreamWorkerPool<TInput, TChunk> {
|
|
48
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
49
|
+
[Symbol.dispose](): void;
|
|
50
|
+
readonly disposed: boolean;
|
|
51
|
+
readonly disposalSignal: AbortSignal;
|
|
52
|
+
dispose(): void;
|
|
53
|
+
drain(options?: DrainOptions): Promise<void>;
|
|
54
|
+
prime(): Promise<void>;
|
|
55
|
+
runStream(input: TInput, options?: RunOptions): AsyncIterable<TChunk>;
|
|
56
|
+
readonly stats: WorkerStats;
|
|
57
|
+
readonly status: WorkerStatus;
|
|
58
|
+
}
|
|
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
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;
|
|
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")
|
|
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("./errors.cjs"),t=require("./_timers.cjs"),n=require("./_pool.cjs"),r=require("./_stream-pool.cjs");require("./protocol.cjs");var i=512;function a(t={}){let{concurrency:n=1,maxQueue:r,onFull:a=`reject`,onSlotError:o,timeout:s}=t,c=n===`auto`?Math.max(1,globalThis.navigator?.hardwareConcurrency??1):n;if(!Number.isInteger(c)||c<1||c>i)throw new e.FamiliarInvalidOptionsError(`\`concurrency\` must be a positive integer ≤ ${i} or "auto"`);if(r!==void 0&&(!Number.isInteger(r)||r<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");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(t){let n=Error(t.message);return n.name=t.name,n.stack=t.stack,new e.FamiliarTaskError(t.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(t,n){let r=()=>this.cancel(new e.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(t,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 e.FamiliarTerminatedError)}#o(n,r,i,a,o){if(this.#n)return Promise.reject(new e.FamiliarTerminatedError);if(this.#r)return Promise.reject(new e.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 e.FamiliarTimeoutError(i)),i),t.unrefTimer(d.timer));try{s.postMessage({id:c,input:n,kind:a,version:1},r)}catch(t){this.cancel(new e.FamiliarRuntimeError(`Failed to post message to worker`,{cause:t}))}})}catch(e){return Promise.reject(e)}}#s(){if(this.#a)return this.#a;if(typeof Worker>`u`)throw new e.FamiliarRuntimeError(`Worker API is unavailable in this runtime`);try{let t=new Worker(this.#t,{type:`module`});return t.onmessage=e=>this.#c(e.data),t.onerror=t=>{let n=new e.FamiliarRuntimeError(t.message||`Worker failed`);this.#a=void 0,this.#e?.(n),this.#l(`reject`,n)},this.#a=t,t}catch(t){throw new e.FamiliarRuntimeError(`Failed to create Worker`,{cause:t})}}#c(t){if(this.#r){if(!o(t)||t.id!==this.#r.id){this.cancel(new e.FamiliarRuntimeError(`Worker returned an incompatible protocol response`));return}if(t.kind===`chunk`){this.#r.emit?.(t.value);return}if(t.kind===`error`){this.#l(`reject`,s(t.error));return}this.#l(`resolve`,t.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=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=n.batch,exports.createStreamWorker=d,exports.createTaskGroup=n.createTaskGroup,exports.createWorker=u;
|
|
28
2
|
//# sourceMappingURL=worker.cjs.map
|