@igniter-js/jobs 0.1.12 → 0.1.14
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 +117 -243
- package/README.md +311 -153
- package/dist/{adapter-CXZxomI9.d.mts → adapter-Cax_40HW.d.mts} +27 -6
- package/dist/{adapter-CXZxomI9.d.ts → adapter-Cax_40HW.d.ts} +27 -6
- package/dist/adapters/bun.d.mts +93 -0
- package/dist/adapters/bun.d.ts +93 -0
- package/dist/adapters/bun.js +914 -0
- package/dist/adapters/bun.js.map +1 -0
- package/dist/adapters/bun.mjs +912 -0
- package/dist/adapters/bun.mjs.map +1 -0
- package/dist/adapters/{memory.adapter.d.ts → mock.d.mts} +1 -3
- package/dist/adapters/{memory.adapter.d.mts → mock.d.ts} +1 -3
- package/dist/adapters/{memory.adapter.js → mock.js} +58 -25
- package/dist/adapters/mock.js.map +1 -0
- package/dist/adapters/{memory.adapter.mjs → mock.mjs} +58 -25
- package/dist/adapters/mock.mjs.map +1 -0
- package/dist/adapters/{bullmq.adapter.d.ts → node.d.mts} +1 -3
- package/dist/adapters/{bullmq.adapter.d.mts → node.d.ts} +1 -3
- package/dist/adapters/{bullmq.adapter.js → node.js} +117 -40
- package/dist/adapters/node.js.map +1 -0
- package/dist/adapters/{bullmq.adapter.mjs → node.mjs} +117 -40
- package/dist/adapters/node.mjs.map +1 -0
- package/dist/index.d.mts +17 -21
- package/dist/index.d.ts +17 -21
- package/dist/index.js +20 -1854
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +21 -1852
- package/dist/index.mjs.map +1 -1
- package/package.json +28 -40
- package/CHANGELOG.md +0 -13
- package/dist/adapters/bullmq.adapter.js.map +0 -1
- package/dist/adapters/bullmq.adapter.mjs.map +0 -1
- package/dist/adapters/index.d.mts +0 -143
- package/dist/adapters/index.d.ts +0 -143
- package/dist/adapters/index.js +0 -1891
- package/dist/adapters/index.js.map +0 -1
- package/dist/adapters/index.mjs +0 -1887
- package/dist/adapters/index.mjs.map +0 -1
- package/dist/adapters/memory.adapter.js.map +0 -1
- package/dist/adapters/memory.adapter.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ bun add @igniter-js/jobs zod
|
|
|
57
57
|
|
|
58
58
|
```typescript
|
|
59
59
|
import { IgniterJobs, IgniterQueue } from "@igniter-js/jobs";
|
|
60
|
-
import { IgniterJobsMemoryAdapter } from "@igniter-js/jobs/adapters";
|
|
60
|
+
import { IgniterJobsMemoryAdapter } from "@igniter-js/jobs/adapters/mock";
|
|
61
61
|
import { z } from "zod";
|
|
62
62
|
|
|
63
63
|
type AppContext = { mailer: { sendWelcome: (email: string) => Promise<void> } };
|
|
@@ -109,7 +109,7 @@ await jobs.email.sendWelcome.dispatch({ input: { email: "user@example.com" } });
|
|
|
109
109
|
▼
|
|
110
110
|
┌─────────────────────────────────────────────────────────────┐
|
|
111
111
|
│ Adapter Layer │
|
|
112
|
-
│ Memory Adapter • SQLite Adapter • BullMQ Adapter
|
|
112
|
+
│ Memory Adapter • Bun SQLite Adapter • BullMQ Adapter │
|
|
113
113
|
└────────────┬────────────────────────────────────────────────┘
|
|
114
114
|
│
|
|
115
115
|
▼
|
|
@@ -184,8 +184,8 @@ await jobs.email.sendWelcome.dispatch({ input: { email: "user@example.com" } });
|
|
|
184
184
|
48. Graceful shutdown in process
|
|
185
185
|
49. Global events to analytics
|
|
186
186
|
50. Event filtering pattern
|
|
187
|
-
51. SQLite adapter usage
|
|
188
|
-
52. SQLite adapter persistence
|
|
187
|
+
51. Bun SQLite adapter usage
|
|
188
|
+
52. Bun SQLite adapter persistence
|
|
189
189
|
53. Memory adapter testing
|
|
190
190
|
54. BullMQ adapter usage
|
|
191
191
|
55. Custom adapter skeleton
|
|
@@ -229,7 +229,7 @@ const uploadQueue = IgniterQueue.create("uploads")
|
|
|
229
229
|
|
|
230
230
|
```typescript
|
|
231
231
|
import { IgniterJobs } from "@igniter-js/jobs";
|
|
232
|
-
import { IgniterJobsMemoryAdapter } from "@igniter-js/jobs/adapters";
|
|
232
|
+
import { IgniterJobsMemoryAdapter } from "@igniter-js/jobs/adapters/mock";
|
|
233
233
|
|
|
234
234
|
const jobs = IgniterJobs.create()
|
|
235
235
|
.withAdapter(IgniterJobsMemoryAdapter.create())
|
|
@@ -402,16 +402,7 @@ await jobs.email.sendWelcome.many(["job-1", "job-2"]).retry();
|
|
|
402
402
|
await jobs.email.sendWelcome.many(["job-1", "job-2"]).remove();
|
|
403
403
|
```
|
|
404
404
|
|
|
405
|
-
### 22)
|
|
406
|
-
|
|
407
|
-
```typescript
|
|
408
|
-
await jobs.email.sendWelcome.pause();
|
|
409
|
-
await jobs.email.sendWelcome.resume();
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
> Note: The BullMQ adapter throws `JOBS_QUEUE_OPERATION_FAILED` for pause/resume job type.
|
|
413
|
-
|
|
414
|
-
### 23) Create a Worker
|
|
405
|
+
### 22) Create a Worker
|
|
415
406
|
|
|
416
407
|
```typescript
|
|
417
408
|
const worker = await jobs.worker
|
|
@@ -712,21 +703,24 @@ const unsubscribe = await jobs.subscribe((event) => {
|
|
|
712
703
|
});
|
|
713
704
|
```
|
|
714
705
|
|
|
715
|
-
### 51) SQLite Adapter Usage
|
|
706
|
+
### 51) Bun SQLite Adapter Usage
|
|
716
707
|
|
|
717
708
|
```typescript
|
|
718
|
-
const adapter =
|
|
709
|
+
const adapter = IgniterJobsBunSQLiteAdapter.create({
|
|
719
710
|
path: "./jobs.sqlite",
|
|
720
|
-
|
|
721
|
-
enableWAL: true,
|
|
711
|
+
durable: true,
|
|
722
712
|
});
|
|
723
713
|
```
|
|
724
714
|
|
|
725
|
-
### 52) SQLite Adapter Persistence
|
|
715
|
+
### 52) Bun SQLite Adapter Persistence
|
|
726
716
|
|
|
727
717
|
```typescript
|
|
728
|
-
const adapter =
|
|
729
|
-
await adapter.dispatch({
|
|
718
|
+
const adapter = IgniterJobsBunSQLiteAdapter.create({ path: "./jobs.sqlite" });
|
|
719
|
+
await adapter.dispatch({
|
|
720
|
+
queue: "emails",
|
|
721
|
+
jobName: "send",
|
|
722
|
+
input: { id: "1" },
|
|
723
|
+
});
|
|
730
724
|
await adapter.shutdown();
|
|
731
725
|
```
|
|
732
726
|
|
|
@@ -748,39 +742,120 @@ const adapter = IgniterJobsBullMQAdapter.create({ redis });
|
|
|
748
742
|
class CustomAdapter implements IgniterJobsAdapter {
|
|
749
743
|
readonly client = {};
|
|
750
744
|
readonly queues = null as any;
|
|
751
|
-
async dispatch(params: IgniterJobsAdapterDispatchParams) {
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
async
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
async
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
async
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
async
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
async
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
async
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
async
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
async
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
745
|
+
async dispatch(params: IgniterJobsAdapterDispatchParams) {
|
|
746
|
+
return "job-id";
|
|
747
|
+
}
|
|
748
|
+
async schedule(params: IgniterJobsAdapterScheduleParams) {
|
|
749
|
+
return "job-id";
|
|
750
|
+
}
|
|
751
|
+
async getJob(jobId: string) {
|
|
752
|
+
return null;
|
|
753
|
+
}
|
|
754
|
+
async getJobState(jobId: string) {
|
|
755
|
+
return null;
|
|
756
|
+
}
|
|
757
|
+
async getJobLogs(jobId: string) {
|
|
758
|
+
return [];
|
|
759
|
+
}
|
|
760
|
+
async getJobProgress(jobId: string) {
|
|
761
|
+
return 0;
|
|
762
|
+
}
|
|
763
|
+
async retryJob(jobId: string) {
|
|
764
|
+
/* ... */
|
|
765
|
+
}
|
|
766
|
+
async removeJob(jobId: string) {
|
|
767
|
+
/* ... */
|
|
768
|
+
}
|
|
769
|
+
async promoteJob(jobId: string) {
|
|
770
|
+
/* ... */
|
|
771
|
+
}
|
|
772
|
+
async moveJobToFailed(jobId: string, reason: string) {
|
|
773
|
+
/* ... */
|
|
774
|
+
}
|
|
775
|
+
async retryManyJobs(jobIds: string[]) {
|
|
776
|
+
/* ... */
|
|
777
|
+
}
|
|
778
|
+
async removeManyJobs(jobIds: string[]) {
|
|
779
|
+
/* ... */
|
|
780
|
+
}
|
|
781
|
+
async getQueueInfo(queue: string) {
|
|
782
|
+
return null;
|
|
783
|
+
}
|
|
784
|
+
async getQueueJobCounts(queue: string) {
|
|
785
|
+
return {
|
|
786
|
+
waiting: 0,
|
|
787
|
+
active: 0,
|
|
788
|
+
completed: 0,
|
|
789
|
+
failed: 0,
|
|
790
|
+
delayed: 0,
|
|
791
|
+
paused: 0,
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
async listQueues() {
|
|
795
|
+
return [];
|
|
796
|
+
}
|
|
797
|
+
async pauseQueue(queue: string) {
|
|
798
|
+
/* ... */
|
|
799
|
+
}
|
|
800
|
+
async resumeQueue(queue: string) {
|
|
801
|
+
/* ... */
|
|
802
|
+
}
|
|
803
|
+
async drainQueue(queue: string) {
|
|
804
|
+
return 0;
|
|
805
|
+
}
|
|
806
|
+
async cleanQueue(queue: string, options: IgniterJobsQueueCleanOptions) {
|
|
807
|
+
return 0;
|
|
808
|
+
}
|
|
809
|
+
async obliterateQueue(queue: string, options?: { force?: boolean }) {
|
|
810
|
+
/* ... */
|
|
811
|
+
}
|
|
812
|
+
async retryAllInQueue(queue: string) {
|
|
813
|
+
return 0;
|
|
814
|
+
}
|
|
815
|
+
async pauseJobType(queue: string, jobName: string) {
|
|
816
|
+
/* ... */
|
|
817
|
+
}
|
|
818
|
+
async resumeJobType(queue: string, jobName: string) {
|
|
819
|
+
/* ... */
|
|
820
|
+
}
|
|
821
|
+
async searchJobs(filter: Record<string, unknown>) {
|
|
822
|
+
return [];
|
|
823
|
+
}
|
|
824
|
+
async searchQueues(filter: Record<string, unknown>) {
|
|
825
|
+
return [];
|
|
826
|
+
}
|
|
827
|
+
async searchWorkers(filter: Record<string, unknown>) {
|
|
828
|
+
return [];
|
|
829
|
+
}
|
|
830
|
+
async createWorker(config: IgniterJobsWorkerBuilderConfig) {
|
|
831
|
+
return {} as IgniterJobsWorkerHandle;
|
|
832
|
+
}
|
|
833
|
+
getWorkers() {
|
|
834
|
+
return new Map();
|
|
835
|
+
}
|
|
836
|
+
async publishEvent(channel: string, payload: unknown) {
|
|
837
|
+
/* ... */
|
|
838
|
+
}
|
|
839
|
+
async subscribeEvent(channel: string, handler: IgniterJobsEventHandler) {
|
|
840
|
+
return async () => {};
|
|
841
|
+
}
|
|
842
|
+
registerJob(
|
|
843
|
+
queueName: string,
|
|
844
|
+
jobName: string,
|
|
845
|
+
definition: IgniterJobDefinition<any, any, any>,
|
|
846
|
+
) {
|
|
847
|
+
/* ... */
|
|
848
|
+
}
|
|
849
|
+
registerCron(
|
|
850
|
+
queueName: string,
|
|
851
|
+
cronName: string,
|
|
852
|
+
definition: IgniterCronDefinition<any, any>,
|
|
853
|
+
) {
|
|
854
|
+
/* ... */
|
|
855
|
+
}
|
|
856
|
+
async shutdown() {
|
|
857
|
+
/* ... */
|
|
858
|
+
}
|
|
784
859
|
}
|
|
785
860
|
```
|
|
786
861
|
|
|
@@ -798,7 +873,9 @@ const telemetry = IgniterTelemetry.create()
|
|
|
798
873
|
|
|
799
874
|
```typescript
|
|
800
875
|
export async function POST() {
|
|
801
|
-
const id = await jobs.email.sendWelcome.dispatch({
|
|
876
|
+
const id = await jobs.email.sendWelcome.dispatch({
|
|
877
|
+
input: { email: "user@example.com" },
|
|
878
|
+
});
|
|
802
879
|
return NextResponse.json({ jobId: id });
|
|
803
880
|
}
|
|
804
881
|
```
|
|
@@ -807,7 +884,9 @@ export async function POST() {
|
|
|
807
884
|
|
|
808
885
|
```typescript
|
|
809
886
|
app.post("/send", async (_req, res) => {
|
|
810
|
-
const jobId = await jobs.email.sendWelcome.dispatch({
|
|
887
|
+
const jobId = await jobs.email.sendWelcome.dispatch({
|
|
888
|
+
input: { email: "user@example.com" },
|
|
889
|
+
});
|
|
811
890
|
res.json({ jobId });
|
|
812
891
|
});
|
|
813
892
|
```
|
|
@@ -816,7 +895,9 @@ app.post("/send", async (_req, res) => {
|
|
|
816
895
|
|
|
817
896
|
```typescript
|
|
818
897
|
app.post("/send", async (_req, res) => {
|
|
819
|
-
const jobId = await jobs.email.sendWelcome.dispatch({
|
|
898
|
+
const jobId = await jobs.email.sendWelcome.dispatch({
|
|
899
|
+
input: { email: "user@example.com" },
|
|
900
|
+
});
|
|
820
901
|
return res.send({ jobId });
|
|
821
902
|
});
|
|
822
903
|
```
|
|
@@ -1019,44 +1100,45 @@ const jobs = IgniterJobs.create()
|
|
|
1019
1100
|
|
|
1020
1101
|
## 🔌 Adapters
|
|
1021
1102
|
|
|
1022
|
-
Adapters implement the `IgniterJobsAdapter` interface and are
|
|
1103
|
+
Adapters implement the `IgniterJobsAdapter` interface and are grouped by runtime:
|
|
1023
1104
|
|
|
1024
1105
|
```typescript
|
|
1025
|
-
import { IgniterJobsMemoryAdapter
|
|
1106
|
+
import { IgniterJobsMemoryAdapter } from "@igniter-js/jobs/adapters/mock";
|
|
1107
|
+
import { IgniterJobsBullMQAdapter } from "@igniter-js/jobs/adapters/node";
|
|
1108
|
+
import { IgniterJobsBunSQLiteAdapter } from "@igniter-js/jobs/adapters/bun";
|
|
1026
1109
|
```
|
|
1027
1110
|
|
|
1028
1111
|
### Adapter Comparison
|
|
1029
1112
|
|
|
1030
|
-
| Adapter | Persistence | Multi-process
|
|
1031
|
-
|
|
1032
|
-
| Memory
|
|
1033
|
-
| SQLite
|
|
1034
|
-
| BullMQ
|
|
1113
|
+
| Adapter | Persistence | Multi-process | Use Case |
|
|
1114
|
+
| ------- | ----------- | ------------------- | --------------------- |
|
|
1115
|
+
| Memory | ❌ | ❌ | Unit tests, local dev |
|
|
1116
|
+
| SQLite | ✅ | ⚠️ (single process) | Desktop, CLI, local |
|
|
1117
|
+
| BullMQ | ✅ | ✅ | Production scale |
|
|
1035
1118
|
|
|
1036
1119
|
### Memory Adapter
|
|
1037
1120
|
|
|
1038
1121
|
```typescript
|
|
1039
|
-
import { IgniterJobsMemoryAdapter } from "@igniter-js/jobs/adapters";
|
|
1122
|
+
import { IgniterJobsMemoryAdapter } from "@igniter-js/jobs/adapters/mock";
|
|
1040
1123
|
|
|
1041
1124
|
const adapter = IgniterJobsMemoryAdapter.create();
|
|
1042
1125
|
```
|
|
1043
1126
|
|
|
1044
|
-
### SQLite Adapter
|
|
1127
|
+
### Bun SQLite Adapter
|
|
1045
1128
|
|
|
1046
1129
|
```typescript
|
|
1047
|
-
import {
|
|
1130
|
+
import { IgniterJobsBunSQLiteAdapter } from "@igniter-js/jobs/adapters/bun";
|
|
1048
1131
|
|
|
1049
|
-
const adapter =
|
|
1132
|
+
const adapter = IgniterJobsBunSQLiteAdapter.create({
|
|
1050
1133
|
path: "./data/jobs.sqlite",
|
|
1051
|
-
|
|
1052
|
-
enableWAL: true,
|
|
1134
|
+
durable: true,
|
|
1053
1135
|
});
|
|
1054
1136
|
```
|
|
1055
1137
|
|
|
1056
1138
|
### BullMQ Adapter
|
|
1057
1139
|
|
|
1058
1140
|
```typescript
|
|
1059
|
-
import { IgniterJobsBullMQAdapter } from "@igniter-js/jobs/adapters";
|
|
1141
|
+
import { IgniterJobsBullMQAdapter } from "@igniter-js/jobs/adapters/node";
|
|
1060
1142
|
import Redis from "ioredis";
|
|
1061
1143
|
|
|
1062
1144
|
const redis = new Redis(process.env.REDIS_URL);
|
|
@@ -1067,12 +1149,23 @@ const adapter = IgniterJobsBullMQAdapter.create({ redis });
|
|
|
1067
1149
|
|
|
1068
1150
|
## 🧪 Testing
|
|
1069
1151
|
|
|
1152
|
+
### Bun SQLite Adapter
|
|
1153
|
+
|
|
1154
|
+
```typescript
|
|
1155
|
+
import { IgniterJobsBunSQLiteAdapter } from "@igniter-js/jobs/adapters/bun";
|
|
1156
|
+
|
|
1157
|
+
const adapter = IgniterJobsBunSQLiteAdapter.create({
|
|
1158
|
+
path: "./data/jobs.sqlite",
|
|
1159
|
+
durable: true,
|
|
1160
|
+
});
|
|
1161
|
+
```
|
|
1162
|
+
|
|
1070
1163
|
### Unit Testing with Memory Adapter
|
|
1071
1164
|
|
|
1072
1165
|
```typescript
|
|
1073
1166
|
import { describe, it, expect } from "vitest";
|
|
1074
1167
|
import { IgniterJobs, IgniterQueue } from "@igniter-js/jobs";
|
|
1075
|
-
import { IgniterJobsMemoryAdapter } from "@igniter-js/jobs/adapters";
|
|
1168
|
+
import { IgniterJobsMemoryAdapter } from "@igniter-js/jobs/adapters/mock";
|
|
1076
1169
|
|
|
1077
1170
|
describe("jobs", () => {
|
|
1078
1171
|
it("dispatches and processes a job", async () => {
|
|
@@ -1121,22 +1214,21 @@ describe("jobs", () => {
|
|
|
1121
1214
|
const adapter = IgniterJobsMemoryAdapter.create({ maxJobHistory: 1000 });
|
|
1122
1215
|
```
|
|
1123
1216
|
|
|
1124
|
-
### SQLite Adapter
|
|
1217
|
+
### Bun SQLite Adapter
|
|
1125
1218
|
|
|
1126
1219
|
Use for desktop apps, CLI tools, or local persistence without Redis.
|
|
1127
1220
|
|
|
1128
1221
|
Key traits:
|
|
1129
1222
|
|
|
1130
1223
|
- Persistent storage on disk
|
|
1131
|
-
- Single-process (
|
|
1224
|
+
- Single-process local runtime (persistent, but not distributed)
|
|
1132
1225
|
- Polling-based worker loop
|
|
1133
1226
|
- Great for edge or MCP servers
|
|
1134
1227
|
|
|
1135
1228
|
```typescript
|
|
1136
|
-
const adapter =
|
|
1229
|
+
const adapter = IgniterJobsBunSQLiteAdapter.create({
|
|
1137
1230
|
path: "./data/jobs.sqlite",
|
|
1138
|
-
|
|
1139
|
-
enableWAL: true,
|
|
1231
|
+
durable: true,
|
|
1140
1232
|
});
|
|
1141
1233
|
```
|
|
1142
1234
|
|
|
@@ -1195,10 +1287,10 @@ describe("jobs", () => {
|
|
|
1195
1287
|
- Use `withLimiter()` for API-bound workloads.
|
|
1196
1288
|
- Shard by queue when jobs have very different resource needs.
|
|
1197
1289
|
|
|
1198
|
-
###
|
|
1290
|
+
### Local Bun SQLite Runtime
|
|
1199
1291
|
|
|
1200
|
-
-
|
|
1201
|
-
-
|
|
1292
|
+
- Use `durable: true` only for jobs that must survive abrupt exits.
|
|
1293
|
+
- Tune `batchSize`, `pollTimeout`, and `heartbeatInterval` for throughput-sensitive workloads.
|
|
1202
1294
|
|
|
1203
1295
|
---
|
|
1204
1296
|
|
|
@@ -1705,17 +1797,40 @@ export const IgniterJobs: {
|
|
|
1705
1797
|
class IgniterJobsBuilder<TContext, TQueues, TScope> {
|
|
1706
1798
|
static create(): IgniterJobsBuilder<unknown>;
|
|
1707
1799
|
|
|
1708
|
-
withAdapter(
|
|
1800
|
+
withAdapter(
|
|
1801
|
+
adapter: IgniterJobsAdapter,
|
|
1802
|
+
): IgniterJobsBuilder<TContext, TQueues, TScope>;
|
|
1709
1803
|
withService(service: string): IgniterJobsBuilder<TContext, TQueues, TScope>;
|
|
1710
|
-
withEnvironment(
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1804
|
+
withEnvironment(
|
|
1805
|
+
environment: string,
|
|
1806
|
+
): IgniterJobsBuilder<TContext, TQueues, TScope>;
|
|
1807
|
+
withContext<TNewContext>(
|
|
1808
|
+
factory: () => TNewContext | Promise<TNewContext>,
|
|
1809
|
+
): IgniterJobsBuilder<TNewContext, {}, TScope>;
|
|
1810
|
+
addScope(
|
|
1811
|
+
name: string,
|
|
1812
|
+
options?: IgniterJobsScopeOptions,
|
|
1813
|
+
): IgniterJobsBuilder<TContext, TQueues, TScope | string>;
|
|
1814
|
+
addQueue(
|
|
1815
|
+
queue: IgniterJobsQueue<TContext, any, any> & { name: string },
|
|
1816
|
+
): IgniterJobsBuilder<TContext, TQueues & Record<string, any>, TScope>;
|
|
1817
|
+
withQueueDefaults(
|
|
1818
|
+
defaults: Partial<IgniterJobDefinition<TContext, any, any>>,
|
|
1819
|
+
): IgniterJobsBuilder<TContext, TQueues, TScope>;
|
|
1820
|
+
withWorkerDefaults(
|
|
1821
|
+
defaults: Partial<IgniterJobsWorkerBuilderConfig>,
|
|
1822
|
+
): IgniterJobsBuilder<TContext, TQueues, TScope>;
|
|
1823
|
+
withAutoStartWorker(config: {
|
|
1824
|
+
queues: (keyof TQueues)[];
|
|
1825
|
+
concurrency?: number;
|
|
1826
|
+
limiter?: IgniterJobsLimiter;
|
|
1827
|
+
}): IgniterJobsBuilder<TContext, TQueues, TScope>;
|
|
1828
|
+
withTelemetry(
|
|
1829
|
+
telemetry: IgniterJobsTelemetry,
|
|
1830
|
+
): IgniterJobsBuilder<TContext, TQueues, TScope>;
|
|
1831
|
+
withLogger(
|
|
1832
|
+
logger: IgniterLogger,
|
|
1833
|
+
): IgniterJobsBuilder<TContext, TQueues, TScope>;
|
|
1719
1834
|
build(): IgniterJobsRuntime<IgniterJobsConfig<TContext, TQueues, TScope>>;
|
|
1720
1835
|
}
|
|
1721
1836
|
```
|
|
@@ -1726,7 +1841,9 @@ class IgniterJobsBuilder<TContext, TQueues, TScope> {
|
|
|
1726
1841
|
|
|
1727
1842
|
```typescript
|
|
1728
1843
|
class IgniterQueue {
|
|
1729
|
-
static create<const TName extends string>(
|
|
1844
|
+
static create<const TName extends string>(
|
|
1845
|
+
name: TName,
|
|
1846
|
+
): IgniterQueueBuilder<unknown, {}, {}, TName>;
|
|
1730
1847
|
}
|
|
1731
1848
|
```
|
|
1732
1849
|
|
|
@@ -1736,13 +1853,23 @@ class IgniterQueue {
|
|
|
1736
1853
|
class IgniterQueueBuilder<TContext, TJobs, TCron, TName> {
|
|
1737
1854
|
addJob<TJobName extends string, TInput, TResult>(
|
|
1738
1855
|
jobName: TJobName,
|
|
1739
|
-
definition: IgniterJobDefinition<TContext, TInput, TResult
|
|
1740
|
-
): IgniterQueueBuilder<
|
|
1856
|
+
definition: IgniterJobDefinition<TContext, TInput, TResult>,
|
|
1857
|
+
): IgniterQueueBuilder<
|
|
1858
|
+
TContext,
|
|
1859
|
+
TJobs & Record<TJobName, IgniterJobDefinition<TContext, TInput, TResult>>,
|
|
1860
|
+
TCron,
|
|
1861
|
+
TName
|
|
1862
|
+
>;
|
|
1741
1863
|
|
|
1742
1864
|
addCron<TCronName extends string, TResult>(
|
|
1743
1865
|
cronName: TCronName,
|
|
1744
|
-
definition: IgniterCronDefinition<TContext, TResult
|
|
1745
|
-
): IgniterQueueBuilder<
|
|
1866
|
+
definition: IgniterCronDefinition<TContext, TResult>,
|
|
1867
|
+
): IgniterQueueBuilder<
|
|
1868
|
+
TContext,
|
|
1869
|
+
TJobs,
|
|
1870
|
+
TCron & Record<TCronName, IgniterCronDefinition<TContext, TResult>>,
|
|
1871
|
+
TName
|
|
1872
|
+
>;
|
|
1746
1873
|
|
|
1747
1874
|
build(): IgniterJobsQueue<TContext, TJobs, TCron> & { name: TName };
|
|
1748
1875
|
}
|
|
@@ -1754,10 +1881,17 @@ class IgniterQueueBuilder<TContext, TJobs, TCron, TName> {
|
|
|
1754
1881
|
interface IgniterJobsRuntime<TConfig> {
|
|
1755
1882
|
config: TConfig;
|
|
1756
1883
|
subscribe(handler: IgniterJobsEventHandler): Promise<() => Promise<void>>;
|
|
1757
|
-
search(
|
|
1884
|
+
search(
|
|
1885
|
+
target: "jobs" | "queues" | "workers",
|
|
1886
|
+
filter: Record<string, unknown>,
|
|
1887
|
+
): Promise<unknown[]>;
|
|
1758
1888
|
shutdown(): Promise<void>;
|
|
1759
1889
|
worker: { create(): IgniterWorkerBuilder<keyof TConfig["queues"] & string> };
|
|
1760
|
-
scope(
|
|
1890
|
+
scope(
|
|
1891
|
+
type: string,
|
|
1892
|
+
id: string | number,
|
|
1893
|
+
tags?: Record<string, unknown>,
|
|
1894
|
+
): IgniterJobsRuntime<TConfig>;
|
|
1761
1895
|
|
|
1762
1896
|
// Queue accessors (dynamic)
|
|
1763
1897
|
[queueName: string]: IgniterJobsQueueAccessor<any>;
|
|
@@ -1777,7 +1911,11 @@ interface IgniterJobsQueueAccessor {
|
|
|
1777
1911
|
obliterate(options?: { force?: boolean }): Promise<void>;
|
|
1778
1912
|
retryAll(): Promise<number>;
|
|
1779
1913
|
};
|
|
1780
|
-
list(filter?: {
|
|
1914
|
+
list(filter?: {
|
|
1915
|
+
status?: IgniterJobStatus[];
|
|
1916
|
+
limit?: number;
|
|
1917
|
+
offset?: number;
|
|
1918
|
+
}): Promise<IgniterJobSearchResult[]>;
|
|
1781
1919
|
subscribe(handler: IgniterJobsEventHandler): Promise<() => Promise<void>>;
|
|
1782
1920
|
jobs: Record<string, IgniterJobsJobAccessor>;
|
|
1783
1921
|
}
|
|
@@ -1800,8 +1938,6 @@ interface IgniterJobsJobAccessor<TInput = unknown> {
|
|
|
1800
1938
|
logs(): Promise<IgniterJobsJobLog[]>;
|
|
1801
1939
|
};
|
|
1802
1940
|
many(ids: string[]): { retry(): Promise<void>; remove(): Promise<void> };
|
|
1803
|
-
pause(): Promise<void>;
|
|
1804
|
-
resume(): Promise<void>;
|
|
1805
1941
|
subscribe(handler: IgniterJobsEventHandler): Promise<() => Promise<void>>;
|
|
1806
1942
|
}
|
|
1807
1943
|
```
|
|
@@ -1811,12 +1947,30 @@ interface IgniterJobsJobAccessor<TInput = unknown> {
|
|
|
1811
1947
|
```typescript
|
|
1812
1948
|
interface IgniterJobsWorkerFluentBuilder<TQueueNames extends string> {
|
|
1813
1949
|
addQueue(queue: TQueueNames): IgniterJobsWorkerFluentBuilder<TQueueNames>;
|
|
1814
|
-
withConcurrency(
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1950
|
+
withConcurrency(
|
|
1951
|
+
concurrency: number,
|
|
1952
|
+
): IgniterJobsWorkerFluentBuilder<TQueueNames>;
|
|
1953
|
+
withLimiter(
|
|
1954
|
+
limiter: IgniterJobsLimiter,
|
|
1955
|
+
): IgniterJobsWorkerFluentBuilder<TQueueNames>;
|
|
1956
|
+
onActive(
|
|
1957
|
+
handler: (ctx: { job: IgniterJobSearchResult }) => void | Promise<void>,
|
|
1958
|
+
): IgniterJobsWorkerFluentBuilder<TQueueNames>;
|
|
1959
|
+
onSuccess(
|
|
1960
|
+
handler: (ctx: {
|
|
1961
|
+
job: IgniterJobSearchResult;
|
|
1962
|
+
result: unknown;
|
|
1963
|
+
}) => void | Promise<void>,
|
|
1964
|
+
): IgniterJobsWorkerFluentBuilder<TQueueNames>;
|
|
1965
|
+
onFailure(
|
|
1966
|
+
handler: (ctx: {
|
|
1967
|
+
job: IgniterJobSearchResult;
|
|
1968
|
+
error: Error;
|
|
1969
|
+
}) => void | Promise<void>,
|
|
1970
|
+
): IgniterJobsWorkerFluentBuilder<TQueueNames>;
|
|
1971
|
+
onIdle(
|
|
1972
|
+
handler: () => void | Promise<void>,
|
|
1973
|
+
): IgniterJobsWorkerFluentBuilder<TQueueNames>;
|
|
1820
1974
|
start(): Promise<IgniterJobsWorkerHandle>;
|
|
1821
1975
|
}
|
|
1822
1976
|
```
|
|
@@ -1825,13 +1979,17 @@ interface IgniterJobsWorkerFluentBuilder<TQueueNames extends string> {
|
|
|
1825
1979
|
|
|
1826
1980
|
## ⚙️ Configuration Reference
|
|
1827
1981
|
|
|
1828
|
-
###
|
|
1982
|
+
### IgniterJobsBunSQLiteAdapterOptions
|
|
1829
1983
|
|
|
1830
1984
|
```typescript
|
|
1831
|
-
interface
|
|
1985
|
+
interface IgniterJobsBunSQLiteAdapterOptions {
|
|
1832
1986
|
path: string;
|
|
1833
|
-
|
|
1834
|
-
|
|
1987
|
+
durable?: boolean;
|
|
1988
|
+
heartbeatInterval?: number;
|
|
1989
|
+
pollTimeout?: number;
|
|
1990
|
+
batchSize?: number;
|
|
1991
|
+
lockDuration?: number;
|
|
1992
|
+
maxStalledCount?: number;
|
|
1835
1993
|
}
|
|
1836
1994
|
```
|
|
1837
1995
|
|
|
@@ -1857,22 +2015,22 @@ interface IgniterJobsScheduleOptions {
|
|
|
1857
2015
|
|
|
1858
2016
|
## ✅ Best Practices
|
|
1859
2017
|
|
|
1860
|
-
| Do
|
|
1861
|
-
|
|
1862
|
-
| ✅ Use input schemas
|
|
1863
|
-
| ✅ Keep payloads small | Faster serialization | `{ id: "order_1" }`
|
|
1864
|
-
| ✅ Use scopes
|
|
1865
|
-
| ✅ Use retries
|
|
1866
|
-
| ✅ Use worker hooks
|
|
2018
|
+
| Do | Why | Example |
|
|
2019
|
+
| ---------------------- | -------------------- | ---------------------------- |
|
|
2020
|
+
| ✅ Use input schemas | Prevent invalid jobs | `input: z.object({ ... })` |
|
|
2021
|
+
| ✅ Keep payloads small | Faster serialization | `{ id: "order_1" }` |
|
|
2022
|
+
| ✅ Use scopes | Tenant isolation | `jobs.scope("org", "org_1")` |
|
|
2023
|
+
| ✅ Use retries | Resilience | `attempts: 5` |
|
|
2024
|
+
| ✅ Use worker hooks | Observability | `onFailure(...)` |
|
|
1867
2025
|
|
|
1868
2026
|
### Anti-Patterns
|
|
1869
2027
|
|
|
1870
|
-
| Don’t
|
|
1871
|
-
|
|
1872
|
-
| ❌ Store PII in metadata
|
|
1873
|
-
| ❌ Use sync I/O in handlers
|
|
1874
|
-
| ❌ Dispatch without schema
|
|
1875
|
-
| ❌ Long-running jobs without progress | No visibility
|
|
2028
|
+
| Don’t | Why | Alternative |
|
|
2029
|
+
| ------------------------------------- | ---------------------- | ------------------ |
|
|
2030
|
+
| ❌ Store PII in metadata | Metadata is observable | Store IDs only |
|
|
2031
|
+
| ❌ Use sync I/O in handlers | Blocks workers | Use async I/O |
|
|
2032
|
+
| ❌ Dispatch without schema | Runtime surprises | Add `input` schema |
|
|
2033
|
+
| ❌ Long-running jobs without progress | No visibility | Use `onProgress` |
|
|
1876
2034
|
|
|
1877
2035
|
---
|
|
1878
2036
|
|
|
@@ -1966,9 +2124,9 @@ Do not import this package in client-side bundles.
|
|
|
1966
2124
|
### Memory → SQLite
|
|
1967
2125
|
|
|
1968
2126
|
```typescript
|
|
1969
|
-
import {
|
|
2127
|
+
import { IgniterJobsBunSQLiteAdapter } from "@igniter-js/jobs/adapters/bun";
|
|
1970
2128
|
|
|
1971
|
-
const adapter =
|
|
2129
|
+
const adapter = IgniterJobsBunSQLiteAdapter.create({
|
|
1972
2130
|
path: "./jobs.sqlite",
|
|
1973
2131
|
});
|
|
1974
2132
|
```
|
|
@@ -1976,7 +2134,7 @@ const adapter = IgniterJobsSQLiteAdapter.create({
|
|
|
1976
2134
|
### SQLite → BullMQ
|
|
1977
2135
|
|
|
1978
2136
|
```typescript
|
|
1979
|
-
import { IgniterJobsBullMQAdapter } from "@igniter-js/jobs/adapters";
|
|
2137
|
+
import { IgniterJobsBullMQAdapter } from "@igniter-js/jobs/adapters/node";
|
|
1980
2138
|
import Redis from "ioredis";
|
|
1981
2139
|
|
|
1982
2140
|
const adapter = IgniterJobsBullMQAdapter.create({
|
|
@@ -2012,7 +2170,7 @@ Yes. The adapter interface is stable and jobs/queues remain unchanged.
|
|
|
2012
2170
|
|
|
2013
2171
|
```typescript
|
|
2014
2172
|
import { IgniterJobs, IgniterQueue } from "@igniter-js/jobs";
|
|
2015
|
-
import {
|
|
2173
|
+
import { IgniterJobsBunSQLiteAdapter } from "@igniter-js/jobs/adapters/bun";
|
|
2016
2174
|
import { z } from "zod";
|
|
2017
2175
|
|
|
2018
2176
|
type AppContext = { uploads: { process: (id: string) => Promise<void> } };
|
|
@@ -2027,7 +2185,7 @@ const queue = IgniterQueue.create("uploads")
|
|
|
2027
2185
|
.build();
|
|
2028
2186
|
|
|
2029
2187
|
const jobs = IgniterJobs.create()
|
|
2030
|
-
.withAdapter(
|
|
2188
|
+
.withAdapter(IgniterJobsBunSQLiteAdapter.create({ path: "./jobs.sqlite" }))
|
|
2031
2189
|
.withService("uploader")
|
|
2032
2190
|
.withEnvironment("local")
|
|
2033
2191
|
.withContext(async () => ({ uploads }))
|
|
@@ -2054,14 +2212,14 @@ await jobs.shutdown();
|
|
|
2054
2212
|
|
|
2055
2213
|
### Job Events (Runtime)
|
|
2056
2214
|
|
|
2057
|
-
| Event
|
|
2058
|
-
|
|
2059
|
-
| `enqueued`
|
|
2060
|
-
| `scheduled` | After schedule
|
|
2061
|
-
| `started`
|
|
2062
|
-
| `completed` | After handler
|
|
2063
|
-
| `failed`
|
|
2064
|
-
| `progress`
|
|
2215
|
+
| Event | When | Payload Keys |
|
|
2216
|
+
| ----------- | ---------------- | ---------------------------------------------------------------------------------------------- |
|
|
2217
|
+
| `enqueued` | After dispatch | `jobId`, `queue`, `jobName` |
|
|
2218
|
+
| `scheduled` | After schedule | `jobId`, `queue`, `jobName` |
|
|
2219
|
+
| `started` | Before handler | `jobId`, `jobName`, `queue`, `attemptsMade`, `startedAt` |
|
|
2220
|
+
| `completed` | After handler | `jobId`, `jobName`, `queue`, `result`, `duration`, `completedAt` |
|
|
2221
|
+
| `failed` | On error | `jobId`, `jobName`, `queue`, `error`, `attemptsMade`, `isFinalAttempt`, `duration`, `failedAt` |
|
|
2222
|
+
| `progress` | On progress hook | `jobId`, `jobName`, `queue`, `progress`, `message`, `timestamp` |
|
|
2065
2223
|
|
|
2066
2224
|
### Telemetry Event Attributes
|
|
2067
2225
|
|
|
@@ -2104,22 +2262,22 @@ await jobs.shutdown();
|
|
|
2104
2262
|
|
|
2105
2263
|
## 📑 Appendix: Adapter API Matrix
|
|
2106
2264
|
|
|
2107
|
-
| API
|
|
2108
|
-
|
|
2109
|
-
| `dispatch()`
|
|
2110
|
-
| `schedule()`
|
|
2111
|
-
| `getJob()`
|
|
2112
|
-
| `getJobState()`
|
|
2113
|
-
| `getJobLogs()`
|
|
2114
|
-
| `getJobProgress()`
|
|
2115
|
-
| `pauseJobType()`
|
|
2116
|
-
| `resumeJobType()`
|
|
2117
|
-
| `pauseQueue()`
|
|
2118
|
-
| `resumeQueue()`
|
|
2119
|
-
| `drainQueue()`
|
|
2120
|
-
| `cleanQueue()`
|
|
2121
|
-
| `obliterateQueue()` | ✅
|
|
2122
|
-
| `retryAllInQueue()` | ✅
|
|
2265
|
+
| API | Memory | SQLite | BullMQ |
|
|
2266
|
+
| ------------------- | ------ | ------ | ------ |
|
|
2267
|
+
| `dispatch()` | ✅ | ✅ | ✅ |
|
|
2268
|
+
| `schedule()` | ✅ | ✅ | ✅ |
|
|
2269
|
+
| `getJob()` | ✅ | ✅ | ✅ |
|
|
2270
|
+
| `getJobState()` | ✅ | ✅ | ✅ |
|
|
2271
|
+
| `getJobLogs()` | ✅ | ✅ | ✅ |
|
|
2272
|
+
| `getJobProgress()` | ✅ | ✅ | ✅ |
|
|
2273
|
+
| `pauseJobType()` | ✅ | ✅ | ❌ |
|
|
2274
|
+
| `resumeJobType()` | ✅ | ✅ | ❌ |
|
|
2275
|
+
| `pauseQueue()` | ✅ | ✅ | ✅ |
|
|
2276
|
+
| `resumeQueue()` | ✅ | ✅ | ✅ |
|
|
2277
|
+
| `drainQueue()` | ✅ | ✅ | ✅ |
|
|
2278
|
+
| `cleanQueue()` | ✅ | ✅ | ✅ |
|
|
2279
|
+
| `obliterateQueue()` | ✅ | ✅ | ✅ |
|
|
2280
|
+
| `retryAllInQueue()` | ✅ | ✅ | ✅ |
|
|
2123
2281
|
|
|
2124
2282
|
---
|
|
2125
2283
|
|