enqiu 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/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +245 -0
- package/dist/api.d.ts +236 -0
- package/dist/api.js +519 -0
- package/dist/codec.d.ts +8 -0
- package/dist/codec.js +74 -0
- package/dist/cron.d.ts +19 -0
- package/dist/cron.js +217 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -0
- package/dist/memory-scheduler.d.ts +24 -0
- package/dist/memory-scheduler.js +163 -0
- package/dist/memory.d.ts +344 -0
- package/dist/memory.js +1201 -0
- package/dist/redis.d.ts +202 -0
- package/dist/redis.js +2180 -0
- package/package.json +72 -0
package/dist/memory.d.ts
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
export type MaybePromise<T> = T | PromiseLike<T>;
|
|
2
|
+
export type JobStatus = "queued" | "scheduled" | "running" | "succeeded" | "failed" | "cancelled" | "expired";
|
|
3
|
+
export interface JobContext<Name extends string = string> {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly name: Name;
|
|
6
|
+
readonly attempt: number;
|
|
7
|
+
readonly signal: AbortSignal;
|
|
8
|
+
progress(value: unknown): void;
|
|
9
|
+
log(entry: JobLogEntry): void;
|
|
10
|
+
}
|
|
11
|
+
export type JobLogLevel = "debug" | "info" | "warn" | "error";
|
|
12
|
+
export interface JobLogEntry {
|
|
13
|
+
readonly timestamp: number;
|
|
14
|
+
readonly level: JobLogLevel;
|
|
15
|
+
readonly message: string;
|
|
16
|
+
readonly fields?: Readonly<Record<string, unknown>>;
|
|
17
|
+
}
|
|
18
|
+
export type JobHandler<Input = unknown, Output = unknown, Name extends string = string> = (input: Input, context: JobContext<Name>) => MaybePromise<Output>;
|
|
19
|
+
export type JobMap = Record<string, JobHandler<any, any, any>>;
|
|
20
|
+
export type JobName<Jobs extends JobMap> = Extract<keyof Jobs, string>;
|
|
21
|
+
export type JobInput<Jobs extends JobMap, Name extends JobName<Jobs>> = Parameters<Jobs[Name]>[0];
|
|
22
|
+
export type JobOutput<Jobs extends JobMap, Name extends JobName<Jobs>> = Awaited<ReturnType<Jobs[Name]>>;
|
|
23
|
+
export interface BackoffOptions {
|
|
24
|
+
type?: "fixed" | "exponential";
|
|
25
|
+
delay: number;
|
|
26
|
+
/**
|
|
27
|
+
* Randomize each delay by up to this fraction.
|
|
28
|
+
* `1` is full jitter; `0.2` produces a value between 80–100%.
|
|
29
|
+
*/
|
|
30
|
+
jitter?: number;
|
|
31
|
+
}
|
|
32
|
+
export type BackoffStrategy = number | BackoffOptions | ((attempt: number, error: Error) => MaybePromise<number>);
|
|
33
|
+
export interface RetryOptions {
|
|
34
|
+
/** Number of retries after the first attempt. */
|
|
35
|
+
retries: number;
|
|
36
|
+
backoff?: BackoffStrategy;
|
|
37
|
+
/** Return `false` to fail immediately instead of retrying this error. */
|
|
38
|
+
when?: (error: Error, attempt: number) => MaybePromise<boolean>;
|
|
39
|
+
}
|
|
40
|
+
export interface RateLimitOptions {
|
|
41
|
+
/** Maximum number of job starts in the interval. */
|
|
42
|
+
limit: number;
|
|
43
|
+
/** Rolling-window duration in milliseconds. */
|
|
44
|
+
interval: number;
|
|
45
|
+
}
|
|
46
|
+
export interface KeyedConcurrencyOptions {
|
|
47
|
+
limit: number;
|
|
48
|
+
key: string;
|
|
49
|
+
}
|
|
50
|
+
export interface ThrottleOptions {
|
|
51
|
+
limit: number;
|
|
52
|
+
interval: number;
|
|
53
|
+
burst: number;
|
|
54
|
+
key: string;
|
|
55
|
+
}
|
|
56
|
+
export interface DebounceOptions {
|
|
57
|
+
wait: number;
|
|
58
|
+
mode: "leading" | "trailing";
|
|
59
|
+
key: string;
|
|
60
|
+
}
|
|
61
|
+
export interface QueueOptions {
|
|
62
|
+
/** Used in generated job IDs and diagnostics. */
|
|
63
|
+
name?: string;
|
|
64
|
+
/** Maximum number of handlers running at once. @default Infinity */
|
|
65
|
+
concurrency?: number;
|
|
66
|
+
/** Keep jobs queued until `start()` is called. @default true */
|
|
67
|
+
autoStart?: boolean;
|
|
68
|
+
/** Default retry policy. A number means retry that many times. */
|
|
69
|
+
retry?: number | RetryOptions;
|
|
70
|
+
/** Default per-attempt timeout in milliseconds. */
|
|
71
|
+
timeout?: number;
|
|
72
|
+
/** Optional strict rolling-window rate limit. */
|
|
73
|
+
rateLimit?: RateLimitOptions;
|
|
74
|
+
/** Number of finished jobs retained for inspection. @default 1000 */
|
|
75
|
+
historyLimit?: number;
|
|
76
|
+
/** Maximum structured log entries retained per job. @default 100 */
|
|
77
|
+
logLimit?: number;
|
|
78
|
+
}
|
|
79
|
+
export interface AddOptions {
|
|
80
|
+
/** Custom job ID. Duplicate IDs throw. */
|
|
81
|
+
id?: string;
|
|
82
|
+
/**
|
|
83
|
+
* Single-flight key. While a matching job is unfinished, `add` returns it
|
|
84
|
+
* instead of creating duplicate work.
|
|
85
|
+
*/
|
|
86
|
+
key?: string;
|
|
87
|
+
/** Delay in milliseconds, or an exact future date. */
|
|
88
|
+
delay?: number | Date;
|
|
89
|
+
/** Higher values run first. Equal priorities remain FIFO. */
|
|
90
|
+
priority?: number;
|
|
91
|
+
/** Override the queue retry policy. */
|
|
92
|
+
retry?: number | RetryOptions;
|
|
93
|
+
/** Override the queue per-attempt timeout. */
|
|
94
|
+
timeout?: number;
|
|
95
|
+
/** Expire before execution after this many milliseconds. */
|
|
96
|
+
expiresIn?: number;
|
|
97
|
+
/** Internal resolved per-key execution limit. */
|
|
98
|
+
concurrency?: KeyedConcurrencyOptions;
|
|
99
|
+
/** Internal resolved token-bucket policy. */
|
|
100
|
+
throttle?: ThrottleOptions;
|
|
101
|
+
/** Internal resolved debounce policy. */
|
|
102
|
+
debounce?: DebounceOptions;
|
|
103
|
+
/** Retain an idempotency key after completion for this duration. */
|
|
104
|
+
keyRetention?: number;
|
|
105
|
+
/** Cancels the job when aborted. */
|
|
106
|
+
signal?: AbortSignal;
|
|
107
|
+
}
|
|
108
|
+
export interface SerializedError {
|
|
109
|
+
name: string;
|
|
110
|
+
message: string;
|
|
111
|
+
stack?: string | undefined;
|
|
112
|
+
}
|
|
113
|
+
export interface JobSnapshot<Input = unknown, Output = unknown, Name extends string = string> {
|
|
114
|
+
id: string;
|
|
115
|
+
name: Name;
|
|
116
|
+
input: Input;
|
|
117
|
+
status: JobStatus;
|
|
118
|
+
priority: number;
|
|
119
|
+
attempt: number;
|
|
120
|
+
retries: number;
|
|
121
|
+
createdAt: number;
|
|
122
|
+
runAt: number;
|
|
123
|
+
expiresAt?: number | undefined;
|
|
124
|
+
startedAt?: number | undefined;
|
|
125
|
+
finishedAt?: number | undefined;
|
|
126
|
+
progress?: unknown;
|
|
127
|
+
output?: Output | undefined;
|
|
128
|
+
error?: SerializedError | undefined;
|
|
129
|
+
logs?: readonly JobLogEntry[] | undefined;
|
|
130
|
+
}
|
|
131
|
+
export interface QueueStats {
|
|
132
|
+
queued: number;
|
|
133
|
+
scheduled: number;
|
|
134
|
+
running: number;
|
|
135
|
+
succeeded: number;
|
|
136
|
+
failed: number;
|
|
137
|
+
cancelled: number;
|
|
138
|
+
expired: number;
|
|
139
|
+
total: number;
|
|
140
|
+
}
|
|
141
|
+
export interface CleanupOptions {
|
|
142
|
+
/** Only remove jobs finished at least this many milliseconds ago. @default 0 */
|
|
143
|
+
olderThan?: number;
|
|
144
|
+
/** Maximum number of jobs to remove. @default Infinity */
|
|
145
|
+
limit?: number;
|
|
146
|
+
}
|
|
147
|
+
export interface CloseOptions {
|
|
148
|
+
/** Finish queued and scheduled work before closing. @default true */
|
|
149
|
+
drain?: boolean;
|
|
150
|
+
}
|
|
151
|
+
export interface QueueEventMap {
|
|
152
|
+
added: JobSnapshot;
|
|
153
|
+
started: JobSnapshot;
|
|
154
|
+
progress: JobSnapshot;
|
|
155
|
+
log: {
|
|
156
|
+
job: JobSnapshot;
|
|
157
|
+
entry: JobLogEntry;
|
|
158
|
+
};
|
|
159
|
+
retry: {
|
|
160
|
+
job: JobSnapshot;
|
|
161
|
+
error: Error;
|
|
162
|
+
delay: number;
|
|
163
|
+
};
|
|
164
|
+
succeeded: JobSnapshot;
|
|
165
|
+
failed: JobSnapshot;
|
|
166
|
+
cancelled: JobSnapshot;
|
|
167
|
+
expired: JobSnapshot;
|
|
168
|
+
idle: QueueStats;
|
|
169
|
+
}
|
|
170
|
+
export declare class JobFailedError extends Error {
|
|
171
|
+
readonly jobId: string;
|
|
172
|
+
constructor(jobId: string, message: string, options?: ErrorOptions);
|
|
173
|
+
}
|
|
174
|
+
export declare class JobCancelledError extends Error {
|
|
175
|
+
readonly jobId: string;
|
|
176
|
+
constructor(jobId: string, message?: string);
|
|
177
|
+
}
|
|
178
|
+
export declare class JobTimeoutError extends Error {
|
|
179
|
+
readonly jobId: string;
|
|
180
|
+
readonly timeout: number;
|
|
181
|
+
constructor(jobId: string, timeout: number);
|
|
182
|
+
}
|
|
183
|
+
export declare class JobExpiredError extends Error {
|
|
184
|
+
readonly jobId: string;
|
|
185
|
+
constructor(jobId: string);
|
|
186
|
+
}
|
|
187
|
+
export declare class QueueClosedError extends Error {
|
|
188
|
+
constructor(name: string);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* An awaitable handle returned synchronously by `queue.add()`.
|
|
192
|
+
*
|
|
193
|
+
* Ignoring a handle is safe: MemoryQueue does not create a rejecting promise until
|
|
194
|
+
* the handle is awaited or `.result` is read.
|
|
195
|
+
*/
|
|
196
|
+
export interface Job<Output = unknown, Input = unknown, Name extends string = string> extends PromiseLike<Output> {
|
|
197
|
+
readonly id: string;
|
|
198
|
+
readonly name: Name;
|
|
199
|
+
readonly input: Input;
|
|
200
|
+
readonly status: JobStatus;
|
|
201
|
+
readonly deduplicated: boolean;
|
|
202
|
+
/** Resolves once the queue has accepted and stored the job. */
|
|
203
|
+
readonly accepted: Promise<void>;
|
|
204
|
+
readonly result: Promise<Output>;
|
|
205
|
+
cancel(reason?: string): boolean;
|
|
206
|
+
snapshot(): JobSnapshot<Input, Output, Name>;
|
|
207
|
+
catch<Result = never>(onRejected?: ((reason: unknown) => Result | PromiseLike<Result>) | null): Promise<Output | Result>;
|
|
208
|
+
finally(onFinally?: (() => void) | null): Promise<Output>;
|
|
209
|
+
}
|
|
210
|
+
interface Deferred<T> {
|
|
211
|
+
promise: Promise<T>;
|
|
212
|
+
resolve(value: T): void;
|
|
213
|
+
}
|
|
214
|
+
interface NormalizedRetry {
|
|
215
|
+
retries: number;
|
|
216
|
+
backoff: BackoffStrategy | undefined;
|
|
217
|
+
when: RetryOptions["when"] | undefined;
|
|
218
|
+
}
|
|
219
|
+
interface InternalJob {
|
|
220
|
+
id: string;
|
|
221
|
+
name: string;
|
|
222
|
+
input: unknown;
|
|
223
|
+
status: JobStatus;
|
|
224
|
+
priority: number;
|
|
225
|
+
attempt: number;
|
|
226
|
+
retry: NormalizedRetry;
|
|
227
|
+
timeout: number | undefined;
|
|
228
|
+
key: string | undefined;
|
|
229
|
+
keyRetention: number;
|
|
230
|
+
keyExpiresAt: number | undefined;
|
|
231
|
+
concurrency: KeyedConcurrencyOptions | undefined;
|
|
232
|
+
throttle: ThrottleOptions | undefined;
|
|
233
|
+
debounceKey: string | undefined;
|
|
234
|
+
createdAt: number;
|
|
235
|
+
runAt: number;
|
|
236
|
+
expiresAt: number | undefined;
|
|
237
|
+
startedAt: number | undefined;
|
|
238
|
+
finishedAt: number | undefined;
|
|
239
|
+
progress: unknown;
|
|
240
|
+
output: unknown;
|
|
241
|
+
error: SerializedError | undefined;
|
|
242
|
+
errorCause: Error | undefined;
|
|
243
|
+
logs: JobLogEntry[];
|
|
244
|
+
sequence: number;
|
|
245
|
+
controller: AbortController | undefined;
|
|
246
|
+
completion: Deferred<JobSnapshot>;
|
|
247
|
+
externalSignal: AbortSignal | undefined;
|
|
248
|
+
abortListener: (() => void) | undefined;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* A zero-dependency, single-process job queue with strongly typed named jobs.
|
|
252
|
+
*
|
|
253
|
+
* State is intentionally kept in memory. MemoryQueue is ideal for local background
|
|
254
|
+
* work, concurrency control, API throttling, and tests. It is not durable or
|
|
255
|
+
* distributed; use a database-backed queue when jobs must survive restarts.
|
|
256
|
+
*/
|
|
257
|
+
export declare class MemoryQueue<Jobs extends JobMap> {
|
|
258
|
+
readonly name: string;
|
|
259
|
+
private readonly handlers;
|
|
260
|
+
private readonly records;
|
|
261
|
+
private readonly keys;
|
|
262
|
+
private readonly activeKeys;
|
|
263
|
+
private readonly throttleStates;
|
|
264
|
+
private readonly debounceStates;
|
|
265
|
+
private readonly ready;
|
|
266
|
+
private readonly delayed;
|
|
267
|
+
private readonly listeners;
|
|
268
|
+
private readonly defaultRetry;
|
|
269
|
+
private readonly defaultTimeout;
|
|
270
|
+
private readonly rateLimit;
|
|
271
|
+
private readonly historyLimit;
|
|
272
|
+
private readonly logLimit;
|
|
273
|
+
private readonly starts;
|
|
274
|
+
private readonly idleWaiters;
|
|
275
|
+
private readonly sizeWaiters;
|
|
276
|
+
private _concurrency;
|
|
277
|
+
private runningCount;
|
|
278
|
+
private sequence;
|
|
279
|
+
private started;
|
|
280
|
+
private closed;
|
|
281
|
+
private idleNotified;
|
|
282
|
+
private pumpQueued;
|
|
283
|
+
private timer;
|
|
284
|
+
private policyWakeAt;
|
|
285
|
+
constructor(handlers: Jobs, options?: QueueOptions);
|
|
286
|
+
get concurrency(): number;
|
|
287
|
+
set concurrency(value: number);
|
|
288
|
+
/** Jobs waiting to start, including scheduled jobs. */
|
|
289
|
+
get size(): number;
|
|
290
|
+
/** Jobs currently running. */
|
|
291
|
+
get pending(): number;
|
|
292
|
+
get isPaused(): boolean;
|
|
293
|
+
get isRateLimited(): boolean;
|
|
294
|
+
get isSaturated(): boolean;
|
|
295
|
+
get stats(): QueueStats;
|
|
296
|
+
add<Name extends JobName<Jobs>>(name: Name, input: JobInput<Jobs, Name>, options?: AddOptions): Job<JobOutput<Jobs, Name>, JobInput<Jobs, Name>, Name>;
|
|
297
|
+
private updateTrailingDebounce;
|
|
298
|
+
addMany<Name extends JobName<Jobs>>(name: Name, inputs: readonly JobInput<Jobs, Name>[], options?: AddOptions): Array<Job<JobOutput<Jobs, Name>, JobInput<Jobs, Name>, Name>>;
|
|
299
|
+
get(id: string): JobSnapshot | undefined;
|
|
300
|
+
list(status?: JobStatus): JobSnapshot[];
|
|
301
|
+
cancel(id: string, reason?: string): boolean;
|
|
302
|
+
clear(reason?: string): number;
|
|
303
|
+
retry(id: string): Job<unknown> | undefined;
|
|
304
|
+
pause(): this;
|
|
305
|
+
start(): this;
|
|
306
|
+
onIdle(): Promise<void>;
|
|
307
|
+
onSizeLessThan(limit: number): Promise<void>;
|
|
308
|
+
cleanup(options?: CleanupOptions): string[];
|
|
309
|
+
close(options?: CloseOptions): Promise<void>;
|
|
310
|
+
on<Event extends keyof QueueEventMap>(event: Event, listener: (payload: QueueEventMap[Event]) => void): () => void;
|
|
311
|
+
/** @internal Used by the awaitable job handle. */
|
|
312
|
+
resultFor(job: InternalJob): Promise<unknown>;
|
|
313
|
+
private handle;
|
|
314
|
+
private createId;
|
|
315
|
+
private connectSignal;
|
|
316
|
+
private disconnectSignal;
|
|
317
|
+
private requestPump;
|
|
318
|
+
private pump;
|
|
319
|
+
private promoteDelayed;
|
|
320
|
+
private expireWaiting;
|
|
321
|
+
private peekDelayed;
|
|
322
|
+
private popReady;
|
|
323
|
+
private canStart;
|
|
324
|
+
private beginExecutionPolicy;
|
|
325
|
+
private releaseExecutionPolicy;
|
|
326
|
+
private refillThrottle;
|
|
327
|
+
private hasReady;
|
|
328
|
+
private hasRateCapacity;
|
|
329
|
+
private pruneStarts;
|
|
330
|
+
private scheduleNextWake;
|
|
331
|
+
private execute;
|
|
332
|
+
private handleFailure;
|
|
333
|
+
private finish;
|
|
334
|
+
private pruneHistory;
|
|
335
|
+
private prunePolicyState;
|
|
336
|
+
private notifyIdle;
|
|
337
|
+
private notifySizeWaiters;
|
|
338
|
+
private emit;
|
|
339
|
+
private clearTimer;
|
|
340
|
+
private assertOpen;
|
|
341
|
+
}
|
|
342
|
+
/** Create a strongly typed queue from a map of named handlers. */
|
|
343
|
+
export declare function memoryQueue<const Jobs extends JobMap>(handlers: Jobs, options?: QueueOptions): MemoryQueue<Jobs>;
|
|
344
|
+
export {};
|