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/CHANGELOG.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The project follows
|
|
4
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
|
+
|
|
6
|
+
## [0.1.0] - Unreleased
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- Renamed the package and primary API to `enqiu`.
|
|
11
|
+
- Replaced the BullMQ wrapper with first-party memory and Redis drivers.
|
|
12
|
+
- Reworked the API around inferred named handlers and direct job calls.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Typed heterogeneous job maps.
|
|
17
|
+
- Concurrency control with priority scheduling.
|
|
18
|
+
- Delayed jobs and exact-date scheduling.
|
|
19
|
+
- Per-queue and per-job retries with fixed, exponential, custom, and jittered
|
|
20
|
+
backoff.
|
|
21
|
+
- Cooperative cancellation and per-attempt timeouts using `AbortSignal`.
|
|
22
|
+
- Strict rolling-window rate limiting and producer backpressure.
|
|
23
|
+
- Single-flight deduplication keys.
|
|
24
|
+
- Bulk enqueueing, progress reporting, typed lifecycle events, history,
|
|
25
|
+
cleanup, pause/start, inspection, and graceful close.
|
|
26
|
+
- Safe fire-and-forget jobs without implicit unhandled promise rejections.
|
|
27
|
+
- Durable Redis delivery, cron schedules, keyed concurrency, throttling,
|
|
28
|
+
debouncing, expiration, structured logs, and telemetry hooks.
|
|
29
|
+
|
|
30
|
+
### Removed
|
|
31
|
+
|
|
32
|
+
- BullMQ and its transitive runtime dependency tree.
|
|
33
|
+
- The mismatched legacy constructor and undocumented aliases.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Enqiu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# Enqiu
|
|
2
|
+
|
|
3
|
+
A small, type-safe job queue for Node.js and Bun. Start in memory, move to
|
|
4
|
+
Redis without changing your job API.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pnpm add enqiu
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Enqiu has no runtime dependencies. Redis, schema, Hono, and telemetry packages
|
|
11
|
+
remain your choice.
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
Define each job once, then call it like a function. The name, input, and result
|
|
16
|
+
types are inferred.
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { enqiu, job } from "enqiu";
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
|
|
22
|
+
const jobs = enqiu({
|
|
23
|
+
sendEmail: job({
|
|
24
|
+
input: z.object({
|
|
25
|
+
to: z.email(),
|
|
26
|
+
subject: z.string(),
|
|
27
|
+
}),
|
|
28
|
+
run: async (email, { signal, log }) => {
|
|
29
|
+
log.info("Sending email", { to: email.to });
|
|
30
|
+
|
|
31
|
+
const response = await fetch("https://example.com/email", {
|
|
32
|
+
method: "POST",
|
|
33
|
+
body: JSON.stringify(email),
|
|
34
|
+
signal,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return { delivered: response.ok };
|
|
38
|
+
},
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const delivery = await jobs.sendEmail({
|
|
43
|
+
to: "hello@example.com",
|
|
44
|
+
subject: "Welcome",
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const result = await delivery.result;
|
|
48
|
+
console.log(result.delivered);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`await jobs.sendEmail(input)` waits until the queue accepts the job and returns
|
|
52
|
+
a handle. It does not wait for the handler. Await `handle.result` only when the
|
|
53
|
+
caller needs the result. Ignoring a handle is safe and does not create an
|
|
54
|
+
unhandled rejected promise.
|
|
55
|
+
|
|
56
|
+
Schemas are optional. A plain handler also infers its result:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const jobs = enqiu({
|
|
60
|
+
resizeImage: async (input: { key: string; width: number }) => {
|
|
61
|
+
return { key: input.key, width: input.width };
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Redis
|
|
67
|
+
|
|
68
|
+
Inject an existing client; Enqiu does not create connections or install a Redis
|
|
69
|
+
library. It accepts Bun's `send(command, args)` client shape and node-redis'
|
|
70
|
+
`sendCommand(args)` shape.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { createClient } from "redis";
|
|
74
|
+
import { enqiu, redis } from "enqiu";
|
|
75
|
+
|
|
76
|
+
const client = createClient({ url: process.env.REDIS_URL });
|
|
77
|
+
await client.connect();
|
|
78
|
+
|
|
79
|
+
const jobs = enqiu(definitions, {
|
|
80
|
+
name: "notifications",
|
|
81
|
+
driver: redis(client),
|
|
82
|
+
worker: { concurrency: 20 },
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Use the same definitions in a producer-only process:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
const jobs = enqiu(definitions, {
|
|
90
|
+
name: "notifications",
|
|
91
|
+
driver: redis(client),
|
|
92
|
+
worker: false,
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Redis jobs use atomic Lua transitions, visibility leases, and deterministic
|
|
97
|
+
recovery so multiple Node.js or Bun workers can safely share a queue.
|
|
98
|
+
|
|
99
|
+
## Job policies
|
|
100
|
+
|
|
101
|
+
Policies live beside the handler and keep call sites clean. Durations are
|
|
102
|
+
numbers in milliseconds, so applications may use plain numbers or a helper
|
|
103
|
+
such as `ms("30s")` without making it an Enqiu dependency.
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
const jobs = enqiu({
|
|
107
|
+
syncAccount: job({
|
|
108
|
+
input: z.object({
|
|
109
|
+
tenantId: z.string(),
|
|
110
|
+
accountId: z.string(),
|
|
111
|
+
}),
|
|
112
|
+
retry: {
|
|
113
|
+
attempts: 5,
|
|
114
|
+
backoff: { type: "exponential", delay: 250, jitter: 0.2 },
|
|
115
|
+
},
|
|
116
|
+
timeout: 30_000,
|
|
117
|
+
expiresIn: 5 * 60_000,
|
|
118
|
+
concurrency: {
|
|
119
|
+
limit: 2,
|
|
120
|
+
by: (input) => input.tenantId,
|
|
121
|
+
},
|
|
122
|
+
throttle: {
|
|
123
|
+
limit: 100,
|
|
124
|
+
per: 60_000,
|
|
125
|
+
burst: 10,
|
|
126
|
+
by: (input) => input.tenantId,
|
|
127
|
+
},
|
|
128
|
+
run: async (input, context) => {
|
|
129
|
+
return syncAccount(input, { signal: context.signal });
|
|
130
|
+
},
|
|
131
|
+
}),
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
- `concurrency` limits simultaneous work globally or by a key such as tenant.
|
|
136
|
+
- `throttle` limits starts over time; `burst` allows short spikes.
|
|
137
|
+
- `debounce: { mode: "leading" }` keeps the first call in a window.
|
|
138
|
+
- `debounce: { mode: "trailing" }` keeps the most recent call in a window.
|
|
139
|
+
- `expiresIn` prevents stale jobs from starting.
|
|
140
|
+
- `idempotencyKey` makes repeated submissions return the same job.
|
|
141
|
+
|
|
142
|
+
Per-call delivery options are available when needed:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
const handle = await jobs.syncAccount(input, {
|
|
146
|
+
idempotencyKey: `sync:${input.accountId}`,
|
|
147
|
+
idempotencyTtl: 24 * 60 * 60_000,
|
|
148
|
+
delay: 5_000,
|
|
149
|
+
priority: "high",
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Progress and logs
|
|
154
|
+
|
|
155
|
+
Progress uses real units rather than an ambiguous fraction:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
const jobs = enqiu({
|
|
159
|
+
importRows: async (rows: string[], context) => {
|
|
160
|
+
for (let index = 0; index < rows.length; index += 1) {
|
|
161
|
+
await importRow(rows[index]);
|
|
162
|
+
await context.reportProgress({
|
|
163
|
+
completed: index + 1,
|
|
164
|
+
total: rows.length,
|
|
165
|
+
message: "Importing rows",
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
context.log.info("Import complete", { rows: rows.length });
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Subscribe to lifecycle events with `jobs.queue.on(...)`. Memory events stay
|
|
175
|
+
inside the process; Redis events are shared between producers and workers.
|
|
176
|
+
|
|
177
|
+
## Cron schedules
|
|
178
|
+
|
|
179
|
+
Schedules use standard five-field cron expressions and IANA time zones:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
const schedule = await jobs.sendDigest.schedule({
|
|
183
|
+
id: "weekday-digest",
|
|
184
|
+
cron: "0 9 * * 1-5",
|
|
185
|
+
timezone: "Europe/Nicosia",
|
|
186
|
+
input: { audience: "daily" },
|
|
187
|
+
catchUp: true,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
await schedule.pause();
|
|
191
|
+
await schedule.resume();
|
|
192
|
+
await schedule.remove();
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Memory schedules live for the process lifetime. Redis schedules are durable
|
|
196
|
+
and use deterministic occurrence IDs to avoid duplicate runs.
|
|
197
|
+
|
|
198
|
+
## Hono
|
|
199
|
+
|
|
200
|
+
Enqiu uses Standard Schema and exposes each job's input schema, so the same
|
|
201
|
+
schema can validate an HTTP route without redefining a type:
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
import { sValidator } from "@hono/standard-validator";
|
|
205
|
+
|
|
206
|
+
app.post(
|
|
207
|
+
"/emails",
|
|
208
|
+
sValidator("json", jobs.sendEmail.input),
|
|
209
|
+
async (c) => {
|
|
210
|
+
const handle = await jobs.sendEmail(c.req.valid("json"));
|
|
211
|
+
return c.json({ id: handle.id }, 202);
|
|
212
|
+
},
|
|
213
|
+
);
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Hono and `@hono/standard-validator` are optional application dependencies.
|
|
217
|
+
|
|
218
|
+
## Queue and worker controls
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
await jobs.queue.pause();
|
|
222
|
+
await jobs.queue.resume();
|
|
223
|
+
await jobs.queue.setConcurrency(50);
|
|
224
|
+
|
|
225
|
+
const page = await jobs.queue.list({ status: "failed", limit: 100 });
|
|
226
|
+
const snapshot = await jobs.queue.get(handle.id);
|
|
227
|
+
await jobs.queue.redrive(handle.id);
|
|
228
|
+
await jobs.queue.cleanup({ olderThan: Date.now() - 7 * 24 * 60 * 60_000 });
|
|
229
|
+
|
|
230
|
+
await jobs.worker.pause();
|
|
231
|
+
await jobs.worker.resume();
|
|
232
|
+
await jobs.worker.onIdle();
|
|
233
|
+
await jobs.worker.close();
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Runtime support
|
|
237
|
+
|
|
238
|
+
- Node.js 20 and newer
|
|
239
|
+
- Current stable Bun
|
|
240
|
+
- Memory and Redis drivers
|
|
241
|
+
- ESM and TypeScript declarations
|
|
242
|
+
|
|
243
|
+
## License
|
|
244
|
+
|
|
245
|
+
MIT
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { JobCancelledError, JobExpiredError, JobFailedError, JobTimeoutError, QueueClosedError } from "./memory.js";
|
|
2
|
+
import type { JobSnapshot, JobStatus, MaybePromise, QueueEventMap, QueueStats, RetryOptions } from "./memory.js";
|
|
3
|
+
import { type RedisDriver } from "./redis.js";
|
|
4
|
+
import { JobSerializationError } from "./codec.js";
|
|
5
|
+
declare const definitionMarker: unique symbol;
|
|
6
|
+
export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
7
|
+
readonly "~standard": {
|
|
8
|
+
readonly version: 1;
|
|
9
|
+
readonly vendor: string;
|
|
10
|
+
readonly validate: (value: unknown) => MaybePromise<{
|
|
11
|
+
readonly value: Output;
|
|
12
|
+
readonly issues?: undefined;
|
|
13
|
+
} | {
|
|
14
|
+
readonly value?: undefined;
|
|
15
|
+
readonly issues: readonly StandardSchemaIssue[];
|
|
16
|
+
}>;
|
|
17
|
+
readonly types?: {
|
|
18
|
+
readonly input: Input;
|
|
19
|
+
readonly output: Output;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface StandardSchemaIssue {
|
|
24
|
+
readonly message: string;
|
|
25
|
+
readonly path?: ReadonlyArray<PropertyKey | {
|
|
26
|
+
readonly key: PropertyKey;
|
|
27
|
+
}>;
|
|
28
|
+
}
|
|
29
|
+
export type InferSchemaInput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["input"];
|
|
30
|
+
export type InferSchemaOutput<Schema extends StandardSchemaV1> = NonNullable<Schema["~standard"]["types"]>["output"];
|
|
31
|
+
export interface Progress {
|
|
32
|
+
readonly completed: number;
|
|
33
|
+
readonly total: number;
|
|
34
|
+
readonly message?: string;
|
|
35
|
+
readonly details?: Readonly<Record<string, unknown>>;
|
|
36
|
+
}
|
|
37
|
+
export interface JobLogger {
|
|
38
|
+
debug(message: string, fields?: Readonly<Record<string, unknown>>): void;
|
|
39
|
+
info(message: string, fields?: Readonly<Record<string, unknown>>): void;
|
|
40
|
+
warn(message: string, fields?: Readonly<Record<string, unknown>>): void;
|
|
41
|
+
error(message: string, fields?: Readonly<Record<string, unknown>>): void;
|
|
42
|
+
}
|
|
43
|
+
export interface JobContext<Name extends string = string> {
|
|
44
|
+
readonly id: string;
|
|
45
|
+
readonly name: Name;
|
|
46
|
+
readonly attempt: number;
|
|
47
|
+
readonly signal: AbortSignal;
|
|
48
|
+
reportProgress(progress: Progress): Promise<void>;
|
|
49
|
+
readonly log: JobLogger;
|
|
50
|
+
}
|
|
51
|
+
export type JobHandler<Input = unknown, Output = unknown, Name extends string = string> = (input: Input, context: JobContext<Name>) => MaybePromise<Output>;
|
|
52
|
+
export interface RetryPolicy extends Omit<RetryOptions, "retries"> {
|
|
53
|
+
/** Total number of attempts, including the first. */
|
|
54
|
+
attempts: number;
|
|
55
|
+
}
|
|
56
|
+
export interface ConcurrencyPolicy<Input> {
|
|
57
|
+
limit: number;
|
|
58
|
+
by?: (input: Input) => string;
|
|
59
|
+
}
|
|
60
|
+
export interface ThrottlePolicy<Input> {
|
|
61
|
+
limit: number;
|
|
62
|
+
per: number;
|
|
63
|
+
burst?: number;
|
|
64
|
+
by?: (input: Input) => string;
|
|
65
|
+
}
|
|
66
|
+
export interface DebouncePolicy<Input> {
|
|
67
|
+
wait: number;
|
|
68
|
+
mode: "leading" | "trailing";
|
|
69
|
+
by: (input: Input) => string;
|
|
70
|
+
}
|
|
71
|
+
export interface JobPolicyOptions<Input> {
|
|
72
|
+
retry?: number | RetryPolicy;
|
|
73
|
+
timeout?: number;
|
|
74
|
+
expiresIn?: number;
|
|
75
|
+
concurrency?: number | ConcurrencyPolicy<Input>;
|
|
76
|
+
throttle?: ThrottlePolicy<Input>;
|
|
77
|
+
debounce?: DebouncePolicy<Input>;
|
|
78
|
+
}
|
|
79
|
+
export interface SchemaJobDefinition<Schema extends StandardSchemaV1 = StandardSchemaV1, Output = unknown> extends JobPolicyOptions<InferSchemaOutput<Schema>> {
|
|
80
|
+
readonly [definitionMarker]: true;
|
|
81
|
+
readonly input: Schema;
|
|
82
|
+
readonly run: JobHandler<InferSchemaOutput<Schema>, Output>;
|
|
83
|
+
}
|
|
84
|
+
export type HandlerJobDefinition<Input = unknown, Output = unknown> = JobHandler<Input, Output>;
|
|
85
|
+
export type JobDefinition = SchemaJobDefinition<StandardSchemaV1<unknown, unknown>, unknown> | HandlerJobDefinition<unknown, unknown>;
|
|
86
|
+
export type JobDefinitions = Record<string, JobDefinition>;
|
|
87
|
+
export declare function job<const Schema extends StandardSchemaV1, Output>(definition: Omit<SchemaJobDefinition<Schema, Output>, typeof definitionMarker>): SchemaJobDefinition<Schema, Output>;
|
|
88
|
+
type DefinitionInput<Definition> = Definition extends SchemaJobDefinition<infer Schema, unknown> ? InferSchemaInput<Schema> : Definition extends JobHandler<infer Input, unknown, string> ? Input : never;
|
|
89
|
+
type DefinitionRunInput<Definition> = Definition extends SchemaJobDefinition<infer Schema, unknown> ? InferSchemaOutput<Schema> : Definition extends JobHandler<infer Input, unknown, string> ? Input : never;
|
|
90
|
+
type DefinitionOutput<Definition> = Definition extends SchemaJobDefinition<StandardSchemaV1, infer Output> ? Awaited<Output> : Definition extends JobHandler<unknown, infer Output, string> ? Awaited<Output> : never;
|
|
91
|
+
export interface SubmitOptions {
|
|
92
|
+
id?: string;
|
|
93
|
+
idempotencyKey?: string;
|
|
94
|
+
/** Keep returning the same completed job for this duration. @default 24h */
|
|
95
|
+
idempotencyTtl?: number;
|
|
96
|
+
delay?: number | Date;
|
|
97
|
+
priority?: number | "low" | "normal" | "high";
|
|
98
|
+
retry?: number | RetryPolicy;
|
|
99
|
+
timeout?: number;
|
|
100
|
+
expiresIn?: number;
|
|
101
|
+
signal?: AbortSignal;
|
|
102
|
+
}
|
|
103
|
+
export interface BulkOptions extends Omit<SubmitOptions, "id"> {
|
|
104
|
+
ids?: readonly string[];
|
|
105
|
+
}
|
|
106
|
+
export interface ScheduleOptions<Input> {
|
|
107
|
+
id?: string;
|
|
108
|
+
cron: string;
|
|
109
|
+
timezone?: string;
|
|
110
|
+
input: Input;
|
|
111
|
+
catchUp?: boolean;
|
|
112
|
+
}
|
|
113
|
+
export interface ScheduleHandle {
|
|
114
|
+
readonly id: string;
|
|
115
|
+
readonly nextRunAt: number;
|
|
116
|
+
pause(): Promise<void>;
|
|
117
|
+
resume(): Promise<void>;
|
|
118
|
+
remove(): Promise<void>;
|
|
119
|
+
refresh(): Promise<ScheduleSnapshot>;
|
|
120
|
+
}
|
|
121
|
+
export interface ScheduleSnapshot {
|
|
122
|
+
id: string;
|
|
123
|
+
jobName: string;
|
|
124
|
+
cron: string;
|
|
125
|
+
timezone: string;
|
|
126
|
+
status: "active" | "paused";
|
|
127
|
+
nextRunAt: number;
|
|
128
|
+
input: unknown;
|
|
129
|
+
catchUp: boolean;
|
|
130
|
+
}
|
|
131
|
+
export interface JobHandle<Output = unknown, Input = unknown, Name extends string = string> {
|
|
132
|
+
readonly id: string;
|
|
133
|
+
readonly name: Name;
|
|
134
|
+
readonly input: Input;
|
|
135
|
+
readonly status: JobStatus;
|
|
136
|
+
readonly deduplicated: boolean;
|
|
137
|
+
readonly result: Promise<Output>;
|
|
138
|
+
cancel(reason?: string): Promise<boolean>;
|
|
139
|
+
refresh(): Promise<JobSnapshot<Input, Output, Name>>;
|
|
140
|
+
}
|
|
141
|
+
export interface JobCallable<Input, RunInput, Output, Name extends string, Schema extends StandardSchemaV1 | undefined = undefined> {
|
|
142
|
+
(input: Input, options?: SubmitOptions): Promise<JobHandle<Output, RunInput, Name>>;
|
|
143
|
+
bulk(inputs: readonly Input[], options?: BulkOptions): Promise<Array<JobHandle<Output, RunInput, Name>>>;
|
|
144
|
+
schedule(options: ScheduleOptions<Input>): Promise<ScheduleHandle>;
|
|
145
|
+
readonly input: Schema;
|
|
146
|
+
}
|
|
147
|
+
type DefinitionSchema<Definition> = Definition extends SchemaJobDefinition<infer Schema, unknown> ? Schema : undefined;
|
|
148
|
+
export type JobsApi<Definitions extends JobDefinitions> = {
|
|
149
|
+
readonly [Name in keyof Definitions]: JobCallable<DefinitionInput<Definitions[Name]>, DefinitionRunInput<Definitions[Name]>, DefinitionOutput<Definitions[Name]>, Extract<Name, string>, DefinitionSchema<Definitions[Name]>>;
|
|
150
|
+
} & {
|
|
151
|
+
readonly queue: QueueApi<Definitions>;
|
|
152
|
+
readonly worker: WorkerApi;
|
|
153
|
+
};
|
|
154
|
+
export type AnyJobSnapshot<Definitions extends JobDefinitions> = {
|
|
155
|
+
[Name in keyof Definitions]: JobSnapshot<DefinitionRunInput<Definitions[Name]>, DefinitionOutput<Definitions[Name]>, Extract<Name, string>>;
|
|
156
|
+
}[keyof Definitions];
|
|
157
|
+
export interface JobListQuery {
|
|
158
|
+
status?: JobStatus;
|
|
159
|
+
name?: string;
|
|
160
|
+
before?: number;
|
|
161
|
+
after?: number;
|
|
162
|
+
limit?: number;
|
|
163
|
+
cursor?: string;
|
|
164
|
+
}
|
|
165
|
+
export interface JobListPage<Job = JobSnapshot> {
|
|
166
|
+
jobs: Job[];
|
|
167
|
+
cursor?: string;
|
|
168
|
+
}
|
|
169
|
+
export interface CleanupQuery {
|
|
170
|
+
status?: JobStatus | readonly JobStatus[];
|
|
171
|
+
olderThan?: number;
|
|
172
|
+
limit?: number;
|
|
173
|
+
}
|
|
174
|
+
export interface QueueApi<Definitions extends JobDefinitions> {
|
|
175
|
+
get(id: string): Promise<AnyJobSnapshot<Definitions> | undefined>;
|
|
176
|
+
list(query?: JobListQuery): Promise<JobListPage<AnyJobSnapshot<Definitions>>>;
|
|
177
|
+
stats(): Promise<QueueStats>;
|
|
178
|
+
pause(): Promise<void>;
|
|
179
|
+
resume(): Promise<void>;
|
|
180
|
+
setConcurrency(limit: number): Promise<void>;
|
|
181
|
+
redrive(id: string): Promise<JobHandle>;
|
|
182
|
+
cleanup(query?: CleanupQuery): Promise<string[]>;
|
|
183
|
+
on<Event extends keyof QueueEventMap>(event: Event, listener: (payload: QueueEventMap[Event]) => void): () => void;
|
|
184
|
+
}
|
|
185
|
+
export interface WorkerStartOptions {
|
|
186
|
+
concurrency?: number;
|
|
187
|
+
}
|
|
188
|
+
export interface WorkerApi {
|
|
189
|
+
readonly running: boolean;
|
|
190
|
+
start(options?: WorkerStartOptions): Promise<void>;
|
|
191
|
+
pause(): Promise<void>;
|
|
192
|
+
resume(): Promise<void>;
|
|
193
|
+
onIdle(): Promise<void>;
|
|
194
|
+
close(options?: {
|
|
195
|
+
drain?: boolean;
|
|
196
|
+
}): Promise<void>;
|
|
197
|
+
}
|
|
198
|
+
export interface WorkerOptions {
|
|
199
|
+
concurrency?: number;
|
|
200
|
+
autoStart?: boolean;
|
|
201
|
+
}
|
|
202
|
+
export interface TelemetryEvent {
|
|
203
|
+
readonly type: string;
|
|
204
|
+
readonly queue: string;
|
|
205
|
+
readonly timestamp: number;
|
|
206
|
+
readonly job?: JobSnapshot;
|
|
207
|
+
readonly fields?: Readonly<Record<string, unknown>>;
|
|
208
|
+
}
|
|
209
|
+
export interface Telemetry {
|
|
210
|
+
emit(event: TelemetryEvent): void;
|
|
211
|
+
}
|
|
212
|
+
export interface SharedEnqiuOptions {
|
|
213
|
+
name?: string;
|
|
214
|
+
worker?: false | WorkerOptions;
|
|
215
|
+
retry?: number | RetryPolicy;
|
|
216
|
+
timeout?: number;
|
|
217
|
+
historyLimit?: number;
|
|
218
|
+
logLimit?: number;
|
|
219
|
+
telemetry?: Telemetry;
|
|
220
|
+
}
|
|
221
|
+
export interface MemoryEnqiuOptions extends SharedEnqiuOptions {
|
|
222
|
+
driver?: undefined;
|
|
223
|
+
}
|
|
224
|
+
export interface RedisEnqiuOptions extends SharedEnqiuOptions {
|
|
225
|
+
driver: RedisDriver;
|
|
226
|
+
/** Redis processes must explicitly choose producer-only or worker mode. */
|
|
227
|
+
worker: false | WorkerOptions;
|
|
228
|
+
}
|
|
229
|
+
export type EnqiuOptions = MemoryEnqiuOptions | RedisEnqiuOptions;
|
|
230
|
+
export declare class JobValidationError extends TypeError {
|
|
231
|
+
readonly issues: readonly StandardSchemaIssue[];
|
|
232
|
+
constructor(name: string, issues: readonly StandardSchemaIssue[]);
|
|
233
|
+
}
|
|
234
|
+
export declare function enqiu<const Definitions extends JobDefinitions>(definitions: Definitions, options?: MemoryEnqiuOptions): JobsApi<Definitions>;
|
|
235
|
+
export declare function enqiu<const Definitions extends JobDefinitions>(definitions: Definitions, options: RedisEnqiuOptions): JobsApi<Definitions>;
|
|
236
|
+
export { JobCancelledError, JobExpiredError, JobFailedError, JobSerializationError, JobTimeoutError, QueueClosedError, };
|