fyflow-scheduler 0.1.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/AGENTS.md +577 -0
- package/LICENSE +21 -0
- package/README.md +415 -0
- package/dist/browser/index.js +2378 -0
- package/dist/node/index.js +2380 -0
- package/dist/types/core/FyflowScheduler.d.ts +279 -0
- package/dist/types/core/FyflowScheduler.d.ts.map +1 -0
- package/dist/types/core/inlineWrapper.d.ts +45 -0
- package/dist/types/core/inlineWrapper.d.ts.map +1 -0
- package/dist/types/core/threadWrapper.d.ts +59 -0
- package/dist/types/core/threadWrapper.d.ts.map +1 -0
- package/dist/types/core/workerInterface.d.ts +166 -0
- package/dist/types/core/workerInterface.d.ts.map +1 -0
- package/dist/types/core/workerManager.d.ts +141 -0
- package/dist/types/core/workerManager.d.ts.map +1 -0
- package/dist/types/core/workerWrapper.d.ts +2 -0
- package/dist/types/core/workerWrapper.d.ts.map +1 -0
- package/dist/types/core/workerWrapperUrl.bundled.d.ts +2 -0
- package/dist/types/core/workerWrapperUrl.bundled.d.ts.map +1 -0
- package/dist/types/core/workerWrapperUrl.d.ts +2 -0
- package/dist/types/core/workerWrapperUrl.d.ts.map +1 -0
- package/dist/types/groups/concurrentLimitGroup.d.ts +33 -0
- package/dist/types/groups/concurrentLimitGroup.d.ts.map +1 -0
- package/dist/types/groups/keyedRateLimitGroup.d.ts +98 -0
- package/dist/types/groups/keyedRateLimitGroup.d.ts.map +1 -0
- package/dist/types/groups/rateLimitGroup.d.ts +79 -0
- package/dist/types/groups/rateLimitGroup.d.ts.map +1 -0
- package/dist/types/groups/resourceGroup.d.ts +41 -0
- package/dist/types/groups/resourceGroup.d.ts.map +1 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/index.d.ts.map +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { ThreadWrapper } from "./threadWrapper.js";
|
|
2
|
+
import { InlineWrapper } from "./inlineWrapper.js";
|
|
3
|
+
import { WorkerStatus } from "./workerInterface.js";
|
|
4
|
+
type WorkerInstance = ThreadWrapper | InlineWrapper;
|
|
5
|
+
export interface WorkerManagerOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Maximum worker instances in this pool. Default 2.
|
|
8
|
+
*
|
|
9
|
+
* Despite the name this is a count of worker *instances*, not OS threads.
|
|
10
|
+
* With `inline: false` each instance is a real worker thread. With
|
|
11
|
+
* `inline: true` they all live in the main process, and the name is a
|
|
12
|
+
* misnomer - see {@link WorkerManagerOptions.inline}.
|
|
13
|
+
*/
|
|
14
|
+
maxThreads?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Tasks each worker instance may run at once. Default 1. Total pool capacity
|
|
17
|
+
* is `maxThreads x maxConcurrentTasks` for both pool types. Raise this for
|
|
18
|
+
* async/IO workers; leave it at 1 for CPU-bound ones.
|
|
19
|
+
*/
|
|
20
|
+
maxConcurrentTasks?: number;
|
|
21
|
+
/** Ms an idle worker is kept before termination. Default 5000. `0` never terminates. */
|
|
22
|
+
idleTimeout?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Run workers in the main process instead of worker threads. Default false.
|
|
25
|
+
* Inline suits async/IO work; threaded suits CPU-bound work, since an inline
|
|
26
|
+
* worker shares the event loop and a synchronous task blocks the scheduler.
|
|
27
|
+
*
|
|
28
|
+
* For an inline pool, `maxThreads` creates that many instances of your worker
|
|
29
|
+
* class in the same process - no threads are involved. Concurrency comes from
|
|
30
|
+
* overlapping `await`s, so only the PRODUCT matters for throughput:
|
|
31
|
+
* `maxThreads: 4, maxConcurrentTasks: 5` and
|
|
32
|
+
* `maxThreads: 1, maxConcurrentTasks: 20` both cap at 20 in-flight tasks and
|
|
33
|
+
* measure the same.
|
|
34
|
+
*
|
|
35
|
+
* What the split does change is state isolation: each instance is constructed
|
|
36
|
+
* separately, so 4 instances means 4 connection pools, caches or whatever
|
|
37
|
+
* per-instance state your worker holds, while 1 instance funnels all 20
|
|
38
|
+
* concurrent tasks through one object.
|
|
39
|
+
*/
|
|
40
|
+
inline?: boolean;
|
|
41
|
+
/** Passed as the first constructor argument to every worker instance in this pool. */
|
|
42
|
+
config?: any;
|
|
43
|
+
/** Resource group ids every task in this pool must acquire before running. */
|
|
44
|
+
groups?: string[];
|
|
45
|
+
/** Requeue a worker's in-flight tasks when it fails, rather than failing them. Default true. */
|
|
46
|
+
requeueFailedTasks?: boolean;
|
|
47
|
+
/** Restarts allowed before the pool gives up and emits `worker.restart_limit_exceeded`. Default 3. */
|
|
48
|
+
maxWorkerRestarts?: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A pool of workers of one type, created from a single worker script.
|
|
52
|
+
*
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const pool = new WorkerManager(workerUrl, {
|
|
55
|
+
* maxThreads: 4,
|
|
56
|
+
* maxConcurrentTasks: 1,
|
|
57
|
+
* groups: ['cpu']
|
|
58
|
+
* });
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* Emits: `task.started`, `task.completed`, `task.failed`, `task.progress`,
|
|
62
|
+
* `task.spawn_request`, `task.requeue_required`, `worker.failed`,
|
|
63
|
+
* `worker.self_terminated`, `worker.restart_limit_exceeded`, and the worker
|
|
64
|
+
* lifecycle events `worker.initialization.started|completed|failed`,
|
|
65
|
+
* `worker.setup.started|completed` and
|
|
66
|
+
* `worker.teardown.started|completed|failed`.
|
|
67
|
+
*
|
|
68
|
+
* Lifecycle events carry `{ workerId, workerType, timestamp }` plus `duration`
|
|
69
|
+
* on `*.completed` and `error` on `*.failed`. They fire on every worker
|
|
70
|
+
* creation and idle-timeout teardown, so keep their listeners cheap.
|
|
71
|
+
*
|
|
72
|
+
* Workers are created lazily on first use, up to `maxThreads`.
|
|
73
|
+
*/
|
|
74
|
+
export declare class WorkerManager extends EventTarget {
|
|
75
|
+
threads: WorkerInstance[];
|
|
76
|
+
maxThreads: number;
|
|
77
|
+
maxConcurrentTasks: number;
|
|
78
|
+
scriptUrl: string;
|
|
79
|
+
idleTimeout: number;
|
|
80
|
+
inline: boolean;
|
|
81
|
+
config: any;
|
|
82
|
+
groups: string[];
|
|
83
|
+
requeueFailedTasks: boolean;
|
|
84
|
+
maxWorkerRestarts: number;
|
|
85
|
+
private poolRestartCount;
|
|
86
|
+
private shuttingDown;
|
|
87
|
+
private allListeners;
|
|
88
|
+
private initializingThreads;
|
|
89
|
+
private idleCheckTimer;
|
|
90
|
+
private readonly IDLE_CHECK_INTERVAL;
|
|
91
|
+
constructor(scriptUrl: string, options?: WorkerManagerOptions);
|
|
92
|
+
enqueueNoQueue(taskId: string, payload: any): Promise<any>;
|
|
93
|
+
private _createPredictiveThread;
|
|
94
|
+
addEventListener(event: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
95
|
+
private _addInternalListener;
|
|
96
|
+
private _trackListener;
|
|
97
|
+
private _removeAllListeners;
|
|
98
|
+
private _setupWorkerEventListeners;
|
|
99
|
+
private _handleWorkerTerminationRequest;
|
|
100
|
+
private _setupWorkerFailureHandling;
|
|
101
|
+
private _handleWorkerFailure;
|
|
102
|
+
private _markWorkerAsFailed;
|
|
103
|
+
private _releaseWorkerGroupResources;
|
|
104
|
+
private _removeAndReplaceWorker;
|
|
105
|
+
/** Ids of the workers that currently exist in this pool (created lazily). */
|
|
106
|
+
getWorkerIds(): string[];
|
|
107
|
+
/**
|
|
108
|
+
* Health and activity of one worker, or `null` if no such worker exists.
|
|
109
|
+
* Useful for building supervision on top of `worker.failed`.
|
|
110
|
+
*/
|
|
111
|
+
getWorkerStatus(workerId: string): WorkerStatus | null;
|
|
112
|
+
/** `getWorkerStatus` for every live worker, keyed by worker id. */
|
|
113
|
+
getAllWorkerStatuses(): Map<string, WorkerStatus>;
|
|
114
|
+
/**
|
|
115
|
+
* Terminate a worker and create a replacement, optionally merging `newConfig`
|
|
116
|
+
* over the pool's `config`. The replacement gets a new id.
|
|
117
|
+
*
|
|
118
|
+
* @returns false if no worker with that id exists.
|
|
119
|
+
*/
|
|
120
|
+
restartWorker(workerId: string, newConfig?: any): Promise<boolean>;
|
|
121
|
+
/** Alias for {@link WorkerManager.restartWorker}. */
|
|
122
|
+
replaceWorker(workerId: string, newConfig?: any): Promise<boolean>;
|
|
123
|
+
/**
|
|
124
|
+
* Merge `config` into a live worker's config object.
|
|
125
|
+
*
|
|
126
|
+
* Note this mutates the config a worker instance already holds - a worker that
|
|
127
|
+
* copied values in its constructor will not see the change. Use
|
|
128
|
+
* {@link WorkerManager.restartWorker} when the worker must be rebuilt.
|
|
129
|
+
*
|
|
130
|
+
* @returns false if no worker with that id exists.
|
|
131
|
+
*/
|
|
132
|
+
updateWorkerConfig(workerId: string, config: any): Promise<boolean>;
|
|
133
|
+
private startIdleTimer;
|
|
134
|
+
private stopIdleTimer;
|
|
135
|
+
private checkIdleWorkers;
|
|
136
|
+
private _removeIdleWorker;
|
|
137
|
+
/** Terminate every worker in the pool and drop its listeners. */
|
|
138
|
+
shutdown(): Promise<void>;
|
|
139
|
+
}
|
|
140
|
+
export {};
|
|
141
|
+
//# sourceMappingURL=workerManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workerManager.d.ts","sourceRoot":"","sources":["../../../core/workerManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workerWrapper.d.ts","sourceRoot":"","sources":["../../../core/workerWrapper.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workerWrapperUrl.bundled.d.ts","sourceRoot":"","sources":["../../../core/workerWrapperUrl.bundled.ts"],"names":[],"mappings":"AAcA,wBAA8B,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAM5D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workerWrapperUrl.d.ts","sourceRoot":"","sources":["../../../core/workerWrapperUrl.ts"],"names":[],"mappings":"AACA,wBAA8B,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAE5D"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ResourceGroup, ResourceGroupMetrics, ResourceGroupStats } from './resourceGroup.ts';
|
|
2
|
+
/**
|
|
3
|
+
* ConcurrentLimitGroup - Optimistic resource limits with soft concurrency control
|
|
4
|
+
*
|
|
5
|
+
* Uses optimistic allocation: checks capacity at dispatch, acquires at execution.
|
|
6
|
+
* May temporarily exceed limit by up to maxThreads × maxConcurrentTasks due to race conditions.
|
|
7
|
+
*
|
|
8
|
+
* Best for:
|
|
9
|
+
* - CPU scheduling (soft limit, temporary overshoot acceptable)
|
|
10
|
+
* - Throughput limiting (approximate rate control)
|
|
11
|
+
* - Load balancing (advisory limits)
|
|
12
|
+
*
|
|
13
|
+
* Note: May temporarily exceed limit by up to maxThreads × maxConcurrentTasks due to race conditions.
|
|
14
|
+
*
|
|
15
|
+
* Example:
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const cpuLimit = new ConcurrentLimitGroup(8, 'cpu'); // ~8 concurrent, may briefly exceed
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare class ConcurrentLimitGroup extends EventTarget implements ResourceGroup {
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly type: "concurrent";
|
|
23
|
+
limit: number;
|
|
24
|
+
running: number;
|
|
25
|
+
private stats;
|
|
26
|
+
constructor(limit: number, id?: string);
|
|
27
|
+
canRun(): boolean;
|
|
28
|
+
getMetrics(): ResourceGroupMetrics;
|
|
29
|
+
getStats(): ResourceGroupStats;
|
|
30
|
+
onStart(): void;
|
|
31
|
+
onFinish(): void;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=concurrentLimitGroup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrentLimitGroup.d.ts","sourceRoot":"","sources":["../../../groups/concurrentLimitGroup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAElG;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,oBAAqB,SAAQ,WAAY,YAAW,aAAa;IAC1E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAU;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,SAAK;IACZ,OAAO,CAAC,KAAK,CAGX;gBAEU,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM;IAMtC,MAAM,IAAI,OAAO;IAIjB,UAAU,IAAI,oBAAoB;IASlC,QAAQ,IAAI,kBAAkB;IAO9B,OAAO;IAeP,QAAQ;CAkBT"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { ResourceGroup, ResourceGroupMetrics, ResourceGroupStats } from './resourceGroup.ts';
|
|
2
|
+
import type { RateWindow } from './rateLimitGroup.ts';
|
|
3
|
+
/** Shape the group needs from a task in order to derive its key. */
|
|
4
|
+
export interface KeyedTaskLike {
|
|
5
|
+
id?: string;
|
|
6
|
+
limitKey?: string;
|
|
7
|
+
payload?: any;
|
|
8
|
+
}
|
|
9
|
+
export interface KeyedRateLimitGroupOptions {
|
|
10
|
+
/** Stable id used in `workerGroups` / pool `groups`. Generated if omitted. */
|
|
11
|
+
id?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Which bucket a task belongs to. Defaults to `task.limitKey`, so tasks can
|
|
14
|
+
* carry the key directly instead of the group deriving it:
|
|
15
|
+
*
|
|
16
|
+
* ```typescript
|
|
17
|
+
* new KeyedRateLimitGroup(windows, { id: 'api', keyFrom: t => t.payload.endpoint })
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Returning undefined or an empty string makes `addTask` throw.
|
|
21
|
+
*/
|
|
22
|
+
keyFrom?: (task: KeyedTaskLike) => string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* How long a key with no running tasks is kept after its last activity.
|
|
25
|
+
* Defaults to twice the largest window, which is the point past which the key
|
|
26
|
+
* can no longer affect any limit. Without eviction, high-cardinality keys grow
|
|
27
|
+
* without bound.
|
|
28
|
+
*/
|
|
29
|
+
idleKeyTtlMs?: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Rate limiting applied independently per key, for when different endpoints,
|
|
33
|
+
* tenants or accounts each have their own quota.
|
|
34
|
+
*
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const api = new KeyedRateLimitGroup(
|
|
37
|
+
* [{ limit: 10, windowMs: 1000 }], // 10/sec PER KEY, not in total
|
|
38
|
+
* { id: 'api', keyFrom: t => t.payload.endpoint }
|
|
39
|
+
* );
|
|
40
|
+
*
|
|
41
|
+
* const pool = new WorkerManager(url, { groups: ['api'], inline: true });
|
|
42
|
+
* new FyflowScheduler({ ApiWorker: pool }, { api });
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* Every window must have room in that key's bucket before a task runs. Tasks
|
|
46
|
+
* over the limit wait in a per-key blocked queue, so a saturated key never
|
|
47
|
+
* delays tasks belonging to another key.
|
|
48
|
+
*
|
|
49
|
+
* Like the other groups this is optimistic: a key's limit can be briefly
|
|
50
|
+
* exceeded by up to `maxThreads x maxConcurrentTasks` under race conditions.
|
|
51
|
+
*/
|
|
52
|
+
export declare class KeyedRateLimitGroup extends EventTarget implements ResourceGroup {
|
|
53
|
+
readonly id: string;
|
|
54
|
+
readonly type: "keyed-rate-limit";
|
|
55
|
+
readonly keyed = true;
|
|
56
|
+
private windows;
|
|
57
|
+
private keyFrom;
|
|
58
|
+
private idleKeyTtlMs;
|
|
59
|
+
private keys;
|
|
60
|
+
private stats;
|
|
61
|
+
constructor(windows: RateWindow[], options?: KeyedRateLimitGroupOptions);
|
|
62
|
+
/** Bucket this task belongs to. The scheduler throws if this returns nothing. */
|
|
63
|
+
keyFor(task: unknown): string | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Whether one more task may start.
|
|
66
|
+
*
|
|
67
|
+
* With a key, checks that key's bucket. Without one - which is how the
|
|
68
|
+
* scheduler pre-filters before walking blocked queues - reports whether any
|
|
69
|
+
* known key has capacity, treating an unknown key as free.
|
|
70
|
+
*/
|
|
71
|
+
canRun(key?: string): boolean;
|
|
72
|
+
onStart(key?: string): void;
|
|
73
|
+
onFinish(key?: string): void;
|
|
74
|
+
/**
|
|
75
|
+
* Aggregate view across keys. `limit` is the per-key limit of the most
|
|
76
|
+
* restrictive window, `running` is summed over every key. Use
|
|
77
|
+
* {@link KeyedRateLimitGroup.getKeyMetrics} for one bucket.
|
|
78
|
+
*/
|
|
79
|
+
getMetrics(): ResourceGroupMetrics & {
|
|
80
|
+
activeKeys: number;
|
|
81
|
+
};
|
|
82
|
+
/** Per-bucket view. Returns zeroes for a key that has not been used. */
|
|
83
|
+
getKeyMetrics(key: string): ResourceGroupMetrics;
|
|
84
|
+
/** Keys currently holding state, busiest first. */
|
|
85
|
+
getActiveKeys(): string[];
|
|
86
|
+
getStats(): ResourceGroupStats;
|
|
87
|
+
private _tightestLimit;
|
|
88
|
+
private _stateFor;
|
|
89
|
+
private _keyCanRun;
|
|
90
|
+
/**
|
|
91
|
+
* Drop keys that hold no running tasks and have been quiet longer than
|
|
92
|
+
* `idleKeyTtlMs`. Their windows can no longer constrain anything, so the state
|
|
93
|
+
* is pure memory. Swept lazily rather than on a timer, so an idle group costs
|
|
94
|
+
* nothing.
|
|
95
|
+
*/
|
|
96
|
+
private _evictIdleKeys;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=keyedRateLimitGroup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyedRateLimitGroup.d.ts","sourceRoot":"","sources":["../../../groups/keyedRateLimitGroup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAClG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,GAAG,CAAC;CACf;AAED,MAAM,WAAW,0BAA0B;IACzC,8EAA8E;IAC9E,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,MAAM,GAAG,SAAS,CAAC;IACtD;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAWD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,mBAAoB,SAAQ,WAAY,YAAW,aAAa;IAC3E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAG,kBAAkB,CAAU;IAC5C,QAAQ,CAAC,KAAK,QAAQ;IAEtB,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,OAAO,CAA8C;IAC7D,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,IAAI,CAA+B;IAC3C,OAAO,CAAC,KAAK,CAGX;gBAEU,OAAO,EAAE,UAAU,EAAE,EAAE,OAAO,GAAE,0BAA+B;IAU3E,iFAAiF;IACjF,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS;IAKzC;;;;;;OAMG;IACH,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO;IAc7B,OAAO,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;IAgB3B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI;IAqB5B;;;;OAIG;IACH,UAAU,IAAI,oBAAoB,GAAG;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE;IAiB3D,wEAAwE;IACxE,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB;IAahD,mDAAmD;IACnD,aAAa,IAAI,MAAM,EAAE;IAOzB,QAAQ,IAAI,kBAAkB;IAO9B,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,UAAU;IAsBlB;;;;;OAKG;IACH,OAAO,CAAC,cAAc;CAUvB"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { ResourceGroup, ResourceGroupMetrics, ResourceGroupStats } from './resourceGroup.ts';
|
|
2
|
+
/** Per-window view returned by {@link RateLimitGroup.getStatus}. */
|
|
3
|
+
export interface RateWindowStatus {
|
|
4
|
+
limit: number;
|
|
5
|
+
windowMs: number;
|
|
6
|
+
current: number;
|
|
7
|
+
completed: number;
|
|
8
|
+
running: number;
|
|
9
|
+
remaining: number;
|
|
10
|
+
resetTime: number;
|
|
11
|
+
}
|
|
12
|
+
/** Snapshot returned by {@link RateLimitGroup.getStatus}. */
|
|
13
|
+
export interface RateLimitStatus {
|
|
14
|
+
running: number;
|
|
15
|
+
windows: RateWindowStatus[];
|
|
16
|
+
canAcceptNew: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface RateWindow {
|
|
19
|
+
limit: number;
|
|
20
|
+
windowMs: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Time-window throttling, for API quotas and similar limits.
|
|
24
|
+
*
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const api = new RateLimitGroup([
|
|
27
|
+
* { limit: 10, windowMs: 1000 }, // 10 per second
|
|
28
|
+
* { limit: 100, windowMs: 60_000 } // and 100 per minute
|
|
29
|
+
* ], 'api');
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* Every window must have room before a task runs. Tasks over the limit wait in
|
|
33
|
+
* the scheduler's blocked queue and are retried once a window rolls over - they
|
|
34
|
+
* are never dropped.
|
|
35
|
+
*/
|
|
36
|
+
export declare class RateLimitGroup extends EventTarget implements ResourceGroup {
|
|
37
|
+
readonly id: string;
|
|
38
|
+
readonly type: "rate-limit";
|
|
39
|
+
private windows;
|
|
40
|
+
private requestCounts;
|
|
41
|
+
private runningStartTimes;
|
|
42
|
+
private running;
|
|
43
|
+
private stats;
|
|
44
|
+
constructor(windows: RateWindow[], id?: string);
|
|
45
|
+
/**
|
|
46
|
+
* Check if a new request can be accepted based on all rate limit windows
|
|
47
|
+
*/
|
|
48
|
+
canRun(): boolean;
|
|
49
|
+
getMetrics(): ResourceGroupMetrics;
|
|
50
|
+
getStats(): ResourceGroupStats;
|
|
51
|
+
/**
|
|
52
|
+
* Called when a task starts - tracks running count and reserves slot in rate limit
|
|
53
|
+
*/
|
|
54
|
+
onStart(): void;
|
|
55
|
+
/**
|
|
56
|
+
* Called when a task finishes - records completion in rate limit windows and removes from running
|
|
57
|
+
*/
|
|
58
|
+
onFinish(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Get current status for monitoring
|
|
61
|
+
*/
|
|
62
|
+
getStatus(): RateLimitStatus;
|
|
63
|
+
/**
|
|
64
|
+
* Clear all rate limit history (useful for testing or reset)
|
|
65
|
+
*/
|
|
66
|
+
reset(): void;
|
|
67
|
+
/**
|
|
68
|
+
* Helper to create common rate limit configurations
|
|
69
|
+
*/
|
|
70
|
+
static createWindows(configs: Array<{
|
|
71
|
+
limit: number;
|
|
72
|
+
seconds: number;
|
|
73
|
+
}>): RateWindow[];
|
|
74
|
+
}
|
|
75
|
+
export declare function createRateLimitGroup(configs: Array<{
|
|
76
|
+
limit: number;
|
|
77
|
+
seconds: number;
|
|
78
|
+
}>): RateLimitGroup;
|
|
79
|
+
//# sourceMappingURL=rateLimitGroup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimitGroup.d.ts","sourceRoot":"","sources":["../../../groups/rateLimitGroup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAElG,oEAAoE;AACpE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,6DAA6D;AAC7D,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,cAAe,SAAQ,WAAY,YAAW,aAAa;IACtE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAU;IACtC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,aAAa,CAA8C;IACnE,OAAO,CAAC,iBAAiB,CAAgB;IACzC,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,KAAK,CAGX;gBAEU,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM;IAU9C;;OAEG;IACH,MAAM,IAAI,OAAO;IA6BjB,UAAU,IAAI,oBAAoB;IAYlC,QAAQ,IAAI,kBAAkB;IAO9B;;OAEG;IACH,OAAO,IAAI,IAAI;IAOf;;OAEG;IACH,QAAQ,IAAI,IAAI;IAkBhB;;OAEG;IACH,SAAS,IAAI,eAAe;IAkC5B;;OAEG;IACH,KAAK,IAAI,IAAI;IASb;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAC,CAAC,GAAG,UAAU,EAAE;CAMrF;AAGD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAAC,GAAG,cAAc,CAErG"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource Group Interfaces
|
|
3
|
+
*
|
|
4
|
+
* Unified interfaces for all resource group types (ConcurrentLimitGroup, RateLimitGroup)
|
|
5
|
+
*/
|
|
6
|
+
export interface ResourceGroupMetrics {
|
|
7
|
+
limit: number;
|
|
8
|
+
running: number;
|
|
9
|
+
available: number;
|
|
10
|
+
utilization: number;
|
|
11
|
+
}
|
|
12
|
+
export interface ResourceGroupStats {
|
|
13
|
+
totalAcquired: number;
|
|
14
|
+
totalReleased: number;
|
|
15
|
+
}
|
|
16
|
+
export interface ResourceGroup extends EventTarget {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly type: 'concurrent' | 'rate-limit' | 'keyed-rate-limit';
|
|
19
|
+
/**
|
|
20
|
+
* True when this group limits per key rather than globally. The scheduler
|
|
21
|
+
* resolves a key with {@link ResourceGroup.keyFor} and passes it to
|
|
22
|
+
* `canRun`/`onStart`/`onFinish`, and queues blocked tasks per key so a
|
|
23
|
+
* saturated key cannot stall the others.
|
|
24
|
+
*/
|
|
25
|
+
readonly keyed?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Key this task belongs to, for keyed groups. Returning undefined or an empty
|
|
28
|
+
* string makes `addTask` throw - a task with no key would otherwise silently
|
|
29
|
+
* bypass or share a bucket.
|
|
30
|
+
*/
|
|
31
|
+
keyFor?(task: unknown): string | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Capacity check (synchronous). `key` is supplied only for keyed groups; a
|
|
34
|
+
* keyed group called without one reports whether ANY key has capacity, which
|
|
35
|
+
* the scheduler uses as a cheap pre-filter.
|
|
36
|
+
*/
|
|
37
|
+
canRun(key?: string): boolean;
|
|
38
|
+
getMetrics(): ResourceGroupMetrics;
|
|
39
|
+
getStats?(): ResourceGroupStats;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=resourceGroup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resourceGroup.d.ts","sourceRoot":"","sources":["../../../groups/resourceGroup.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY,GAAG,kBAAkB,CAAC;IAEhE;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;OAIG;IACH,MAAM,CAAC,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IAE3C;;;;OAIG;IACH,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAG9B,UAAU,IAAI,oBAAoB,CAAC;IAGnC,QAAQ,CAAC,IAAI,kBAAkB,CAAC;CAKjC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { FyflowScheduler, FyflowTask } from "./core/FyflowScheduler.js";
|
|
2
|
+
export type { FyflowSchedulerOptions, AddTaskOptions } from "./core/FyflowScheduler.js";
|
|
3
|
+
export { ConcurrentLimitGroup } from "./groups/concurrentLimitGroup.js";
|
|
4
|
+
export { RateLimitGroup } from "./groups/rateLimitGroup.js";
|
|
5
|
+
export type { RateWindow } from "./groups/rateLimitGroup.js";
|
|
6
|
+
export { KeyedRateLimitGroup } from "./groups/keyedRateLimitGroup.js";
|
|
7
|
+
export type { KeyedRateLimitGroupOptions, KeyedTaskLike } from "./groups/keyedRateLimitGroup.js";
|
|
8
|
+
export type { ResourceGroup, ResourceGroupMetrics, ResourceGroupStats } from "./groups/resourceGroup.js";
|
|
9
|
+
export { WorkerManager } from "./core/workerManager.js";
|
|
10
|
+
export { ThreadWrapper } from "./core/threadWrapper.js";
|
|
11
|
+
export { InlineWrapper } from "./core/inlineWrapper.js";
|
|
12
|
+
export type { WorkerManagerOptions } from "./core/workerManager.js";
|
|
13
|
+
export type { WorkerInterface, WorkerConfig, BaseWorkerContext, TaskWorkerContext, WorkerContext, SpawnTaskConfig, ProgressData, WorkerStatus, WorkerInstanceState } from "./core/workerInterface.js";
|
|
14
|
+
export { BaseWorker, WorkerTerminationError } from "./core/workerInterface.js";
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACxE,YAAY,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAMxF,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,YAAY,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,YAAY,EAAE,0BAA0B,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACjG,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAGzG,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAGxD,YAAY,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACpE,YAAY,EACV,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACpB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fyflow-scheduler",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Parallel task scheduler with resource management, worker pools, and cross-platform support",
|
|
5
|
+
"main": "./dist/node/index.js",
|
|
6
|
+
"browser": "./dist/browser/index.js",
|
|
7
|
+
"types": "./dist/types/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "node esbuild.config.js",
|
|
11
|
+
"build:dev": "node esbuild.config.js --dev",
|
|
12
|
+
"prepare": "npm run build",
|
|
13
|
+
"prepublishOnly": "npm run build",
|
|
14
|
+
"test": "npm run build:dev && node dev-dist/node/tests/runner.js",
|
|
15
|
+
"test:core": "npm run build:dev && node dev-dist/node/tests/runner.js core",
|
|
16
|
+
"test:error": "npm run build:dev && node dev-dist/node/tests/runner.js error",
|
|
17
|
+
"test:spawning": "npm run build:dev && node dev-dist/node/tests/runner.js spawning",
|
|
18
|
+
"test:settlement": "npm run build:dev && node dev-dist/node/tests/runner.js settlement",
|
|
19
|
+
"test:docs": "npm run build:dev && node dev-dist/node/tests/runner.js docs",
|
|
20
|
+
"test:performance": "npm run build:dev && node dev-dist/node/tests/runner.js performance",
|
|
21
|
+
"test:deno": "deno task test",
|
|
22
|
+
"test:deno:core": "deno task test:core",
|
|
23
|
+
"test:deno:error": "deno task test:error",
|
|
24
|
+
"test:deno:spawning": "deno task test:spawning",
|
|
25
|
+
"test:browser": "npm run build:dev && npx playwright test",
|
|
26
|
+
"test:browser:headed": "npm run build:dev && npx playwright test --headed",
|
|
27
|
+
"test:browser:debug": "npm run build:dev && npx playwright test --debug",
|
|
28
|
+
"test:browser:chromium": "npm run build:dev && npx playwright test --project=chromium",
|
|
29
|
+
"test:browser:firefox": "npm run build:dev && npx playwright test --project=firefox",
|
|
30
|
+
"test:browser:webkit": "npm run build:dev && npx playwright test --project=webkit",
|
|
31
|
+
"test:all": "npm run test && npm run test:browser",
|
|
32
|
+
"serve:test": "npx http-server . -p 3000 -c-1 --cors",
|
|
33
|
+
"benchmark": "npm run build:dev && node dev-dist/node/benchmark/runBenchmarks.js",
|
|
34
|
+
"benchmark:quick": "npm run build:dev && node dev-dist/node/benchmark/runBenchmarks.js --quick",
|
|
35
|
+
"benchmark:baseline": "npm run build:dev && node dev-dist/node/benchmark/runBenchmarks.js --categories baseline",
|
|
36
|
+
"benchmark:variance": "npm run build:dev && node dev-dist/node/benchmark/runBenchmarks.js --quick --runs 3",
|
|
37
|
+
"benchmark:startup": "npm run build:dev && node dev-dist/node/benchmark/runBenchmarks.js --categories startup",
|
|
38
|
+
"benchmark:contention": "npm run build:dev && node dev-dist/node/benchmark/runBenchmarks.js --categories contention",
|
|
39
|
+
"benchmark:overlapping": "npm run build:dev && node dev-dist/node/benchmark/runBenchmarks.js --categories overlapping",
|
|
40
|
+
"benchmark:help": "npm run build:dev && node dev-dist/node/benchmark/runBenchmarks.js --help"
|
|
41
|
+
},
|
|
42
|
+
"keywords": ["scheduler", "task", "worker", "async", "concurrency", "workflow", "parallel", "typescript"],
|
|
43
|
+
"author": "FyFlow Team",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"homepage": "https://github.com/fyflow/fyflow-scheduler#readme",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "git+https://github.com/fyflow/fyflow-scheduler.git"
|
|
49
|
+
},
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/fyflow/fyflow-scheduler/issues"
|
|
52
|
+
},
|
|
53
|
+
"exports": {
|
|
54
|
+
".": {
|
|
55
|
+
"node": {
|
|
56
|
+
"types": "./dist/types/index.d.ts",
|
|
57
|
+
"default": "./dist/node/index.js"
|
|
58
|
+
},
|
|
59
|
+
"browser": {
|
|
60
|
+
"types": "./dist/types/index.d.ts",
|
|
61
|
+
"default": "./dist/browser/index.js"
|
|
62
|
+
},
|
|
63
|
+
"types": "./dist/types/index.d.ts",
|
|
64
|
+
"default": "./dist/node/index.js"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"files": [
|
|
68
|
+
"dist/**/*",
|
|
69
|
+
"README.md",
|
|
70
|
+
"AGENTS.md"
|
|
71
|
+
],
|
|
72
|
+
"packageManager": "pnpm@10.12.1",
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": ">=22"
|
|
75
|
+
},
|
|
76
|
+
"devDependencies": {
|
|
77
|
+
"@playwright/test": "^1.55.1",
|
|
78
|
+
"@types/node": "^22.0.0",
|
|
79
|
+
"esbuild": "^0.25.10",
|
|
80
|
+
"http-server": "^14.1.1",
|
|
81
|
+
"typescript": "^5.9.2"
|
|
82
|
+
}
|
|
83
|
+
}
|