@ouija-dev/bus 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/dist/bullmq-event-bus.d.ts +51 -0
- package/dist/bullmq-event-bus.d.ts.map +1 -0
- package/dist/bullmq-event-bus.js +246 -0
- package/dist/bullmq-event-bus.js.map +1 -0
- package/dist/bullmq-job-queue.d.ts +26 -0
- package/dist/bullmq-job-queue.d.ts.map +1 -0
- package/dist/bullmq-job-queue.js +134 -0
- package/dist/bullmq-job-queue.js.map +1 -0
- package/dist/event-bus.d.ts +61 -0
- package/dist/event-bus.d.ts.map +1 -0
- package/dist/event-bus.js +2 -0
- package/dist/event-bus.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/job-queue.d.ts +84 -0
- package/dist/job-queue.d.ts.map +1 -0
- package/dist/job-queue.js +9 -0
- package/dist/job-queue.js.map +1 -0
- package/package.json +31 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type ConnectionOptions } from 'bullmq';
|
|
2
|
+
import type { OuijaEventMap, OuijaTopic } from '@ouija-dev/types';
|
|
3
|
+
import type { EventBus, EventHandler, PatternEventHandler, PublishOptions, Unsubscribe } from './event-bus.js';
|
|
4
|
+
/**
|
|
5
|
+
* BullMQ-backed EventBus implementation.
|
|
6
|
+
*
|
|
7
|
+
* Fan-out strategy (v1 — simple and correct):
|
|
8
|
+
* Each registered subscriber gets its own BullMQ queue named
|
|
9
|
+
* `ouija.event-bus.<subscriberKey>`. When an event is published, one job is
|
|
10
|
+
* enqueued per subscriber whose topic/pattern matches. The subscriber's Worker
|
|
11
|
+
* processes that queue and calls the handler.
|
|
12
|
+
*
|
|
13
|
+
* This is O(N subscribers) on publish, which is acceptable for v1 where
|
|
14
|
+
* subscriber counts are single digits. If N grows, swap to Redis Streams
|
|
15
|
+
* consumer groups without changing the EventBus interface.
|
|
16
|
+
*
|
|
17
|
+
* Assumptions:
|
|
18
|
+
* - Redis is configured with `maxmemory-policy noeviction`. Jobs must never
|
|
19
|
+
* be silently dropped due to memory pressure.
|
|
20
|
+
* - Connection is managed externally and passed in via `connection` option.
|
|
21
|
+
*/
|
|
22
|
+
export declare class BullMQEventBus implements EventBus {
|
|
23
|
+
private readonly connection;
|
|
24
|
+
private readonly subscribers;
|
|
25
|
+
private readonly deliveryQueues;
|
|
26
|
+
private readonly dispatchQueue;
|
|
27
|
+
private readonly dispatchWorker;
|
|
28
|
+
private closed;
|
|
29
|
+
constructor(connection: ConnectionOptions);
|
|
30
|
+
publish<TTopic extends OuijaTopic>(topic: TTopic, payload: OuijaEventMap[TTopic], options?: PublishOptions): Promise<string>;
|
|
31
|
+
subscribe<TTopic extends OuijaTopic>(topic: TTopic, handler: EventHandler<TTopic>): Promise<Unsubscribe>;
|
|
32
|
+
subscribePattern(pattern: string, handler: PatternEventHandler): Promise<Unsubscribe>;
|
|
33
|
+
/**
|
|
34
|
+
* Replay is not natively supported by BullMQ (jobs are ephemeral).
|
|
35
|
+
* A production implementation would query a persistent event store
|
|
36
|
+
* (Postgres `pipeline_events` table) and re-deliver events.
|
|
37
|
+
*
|
|
38
|
+
* This stub iterates completed jobs in the dispatch queue as a best-effort
|
|
39
|
+
* fallback. Callers should not rely on this for full catch-up — use the
|
|
40
|
+
* Postgres event log (Task 5) once available.
|
|
41
|
+
*/
|
|
42
|
+
replay(topic: OuijaTopic, from: string, to: string, handler: PatternEventHandler): Promise<void>;
|
|
43
|
+
close(): Promise<void>;
|
|
44
|
+
private addSubscriber;
|
|
45
|
+
/**
|
|
46
|
+
* Fan out a published event to all matching subscribers.
|
|
47
|
+
* Called by the dispatch worker — runs inside BullMQ.
|
|
48
|
+
*/
|
|
49
|
+
private fanOut;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=bullmq-event-bus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bullmq-event-bus.d.ts","sourceRoot":"","sources":["../src/bullmq-event-bus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAE/D,OAAO,KAAK,EAAc,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,WAAW,EACZ,MAAM,gBAAgB,CAAC;AA0DxB;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,cAAe,YAAW,QAAQ;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAC/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsC;IAElE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4B;IAE3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,MAAM,CAAS;gBAEX,UAAU,EAAE,iBAAiB;IAoCnC,OAAO,CAAC,MAAM,SAAS,UAAU,EACrC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAC9B,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC;IAuBZ,SAAS,CAAC,MAAM,SAAS,UAAU,EACvC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,GAC5B,OAAO,CAAC,WAAW,CAAC;IAajB,gBAAgB,CACpB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,WAAW,CAAC;IAQvB;;;;;;;;OAQG;IACG,MAAM,CACV,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC;IAgCV,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAwBd,aAAa;IA0D3B;;;OAGG;YACW,MAAM;CAwBrB"}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { Queue, Worker } from 'bullmq';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Glob pattern matching
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
/**
|
|
7
|
+
* Convert a glob-style pattern into a RegExp.
|
|
8
|
+
*
|
|
9
|
+
* Rules:
|
|
10
|
+
* - `*` matches any sequence of characters that does not contain `.`
|
|
11
|
+
* - `**` matches any sequence of characters including `.`
|
|
12
|
+
* - `.` is treated as a literal dot
|
|
13
|
+
*
|
|
14
|
+
* Examples:
|
|
15
|
+
* - `kanban.card.*` matches `kanban.card.moved`, `kanban.card.created`
|
|
16
|
+
* - `agent.**` matches `agent.work.progress`, `agent.work.pr_ready`
|
|
17
|
+
* - `git.*` matches `git.pr` but NOT `git.pr.opened`
|
|
18
|
+
*/
|
|
19
|
+
function globToRegex(pattern) {
|
|
20
|
+
// Escape all regex metacharacters except * which we handle specially
|
|
21
|
+
const escaped = pattern
|
|
22
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&') // escape regex special chars (. included)
|
|
23
|
+
.replace(/\\\.\\\./g, '\\.') // shouldn't occur but safety net
|
|
24
|
+
// Replace ** first (two stars), then single *
|
|
25
|
+
.replace(/\*\*/g, '\x00GLOBSTAR\x00')
|
|
26
|
+
.replace(/\*/g, '[^.]+')
|
|
27
|
+
.replace(/\x00GLOBSTAR\x00/g, '.+');
|
|
28
|
+
return new RegExp(`^${escaped}$`);
|
|
29
|
+
}
|
|
30
|
+
function topicMatchesPattern(topic, pattern) {
|
|
31
|
+
// Exact match short-circuit
|
|
32
|
+
if (topic === pattern)
|
|
33
|
+
return true;
|
|
34
|
+
try {
|
|
35
|
+
return globToRegex(pattern).test(topic);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// BullMQEventBus
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
/**
|
|
45
|
+
* BullMQ-backed EventBus implementation.
|
|
46
|
+
*
|
|
47
|
+
* Fan-out strategy (v1 — simple and correct):
|
|
48
|
+
* Each registered subscriber gets its own BullMQ queue named
|
|
49
|
+
* `ouija.event-bus.<subscriberKey>`. When an event is published, one job is
|
|
50
|
+
* enqueued per subscriber whose topic/pattern matches. The subscriber's Worker
|
|
51
|
+
* processes that queue and calls the handler.
|
|
52
|
+
*
|
|
53
|
+
* This is O(N subscribers) on publish, which is acceptable for v1 where
|
|
54
|
+
* subscriber counts are single digits. If N grows, swap to Redis Streams
|
|
55
|
+
* consumer groups without changing the EventBus interface.
|
|
56
|
+
*
|
|
57
|
+
* Assumptions:
|
|
58
|
+
* - Redis is configured with `maxmemory-policy noeviction`. Jobs must never
|
|
59
|
+
* be silently dropped due to memory pressure.
|
|
60
|
+
* - Connection is managed externally and passed in via `connection` option.
|
|
61
|
+
*/
|
|
62
|
+
export class BullMQEventBus {
|
|
63
|
+
connection;
|
|
64
|
+
subscribers = new Map();
|
|
65
|
+
// One Queue per subscriber key for fan-out delivery
|
|
66
|
+
deliveryQueues = new Map();
|
|
67
|
+
// Single publish queue used as the fan-out coordinator
|
|
68
|
+
dispatchQueue;
|
|
69
|
+
dispatchWorker;
|
|
70
|
+
closed = false;
|
|
71
|
+
constructor(connection) {
|
|
72
|
+
this.connection = connection;
|
|
73
|
+
// The dispatch queue receives one job per publish() call.
|
|
74
|
+
// Its worker fans out to per-subscriber delivery queues.
|
|
75
|
+
this.dispatchQueue = new Queue('ouija.event-bus.dispatch', {
|
|
76
|
+
connection,
|
|
77
|
+
defaultJobOptions: {
|
|
78
|
+
removeOnComplete: { count: 1000 },
|
|
79
|
+
removeOnFail: { count: 500 },
|
|
80
|
+
attempts: 3,
|
|
81
|
+
backoff: { type: 'exponential', delay: 1000 },
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
this.dispatchWorker = new Worker('ouija.event-bus.dispatch', async (job) => {
|
|
85
|
+
const event = job.data;
|
|
86
|
+
await this.fanOut(event);
|
|
87
|
+
}, { connection, concurrency: 10 });
|
|
88
|
+
this.dispatchWorker.on('failed', (job, err) => {
|
|
89
|
+
console.error(`[BullMQEventBus] dispatch job ${job?.id ?? 'unknown'} failed:`, err);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// -------------------------------------------------------------------------
|
|
93
|
+
// publish
|
|
94
|
+
// -------------------------------------------------------------------------
|
|
95
|
+
async publish(topic, payload, options = {}) {
|
|
96
|
+
if (this.closed)
|
|
97
|
+
throw new Error('BullMQEventBus is closed');
|
|
98
|
+
const event = {
|
|
99
|
+
id: randomUUID(),
|
|
100
|
+
topic,
|
|
101
|
+
payload,
|
|
102
|
+
timestamp: new Date().toISOString(),
|
|
103
|
+
sourcePlugin: options.sourcePlugin ?? 'unknown',
|
|
104
|
+
correlationId: options.correlationId ?? randomUUID(),
|
|
105
|
+
};
|
|
106
|
+
await this.dispatchQueue.add(`event:${topic}`, event, {
|
|
107
|
+
jobId: `event:${event.id}`,
|
|
108
|
+
});
|
|
109
|
+
return event.id;
|
|
110
|
+
}
|
|
111
|
+
// -------------------------------------------------------------------------
|
|
112
|
+
// subscribe (exact topic, fully typed)
|
|
113
|
+
// -------------------------------------------------------------------------
|
|
114
|
+
async subscribe(topic, handler) {
|
|
115
|
+
// Wrap typed handler as PatternEventHandler so we can store it uniformly
|
|
116
|
+
const patternHandler = async (event) => {
|
|
117
|
+
await handler(event);
|
|
118
|
+
};
|
|
119
|
+
return this.addSubscriber(topic, false, patternHandler);
|
|
120
|
+
}
|
|
121
|
+
// -------------------------------------------------------------------------
|
|
122
|
+
// subscribePattern (glob, intentionally type-unsafe)
|
|
123
|
+
// -------------------------------------------------------------------------
|
|
124
|
+
async subscribePattern(pattern, handler) {
|
|
125
|
+
return this.addSubscriber(pattern, true, handler);
|
|
126
|
+
}
|
|
127
|
+
// -------------------------------------------------------------------------
|
|
128
|
+
// replay
|
|
129
|
+
// -------------------------------------------------------------------------
|
|
130
|
+
/**
|
|
131
|
+
* Replay is not natively supported by BullMQ (jobs are ephemeral).
|
|
132
|
+
* A production implementation would query a persistent event store
|
|
133
|
+
* (Postgres `pipeline_events` table) and re-deliver events.
|
|
134
|
+
*
|
|
135
|
+
* This stub iterates completed jobs in the dispatch queue as a best-effort
|
|
136
|
+
* fallback. Callers should not rely on this for full catch-up — use the
|
|
137
|
+
* Postgres event log (Task 5) once available.
|
|
138
|
+
*/
|
|
139
|
+
async replay(topic, from, to, handler) {
|
|
140
|
+
if (this.closed)
|
|
141
|
+
throw new Error('BullMQEventBus is closed');
|
|
142
|
+
const fromMs = new Date(from).getTime();
|
|
143
|
+
const toMs = new Date(to).getTime();
|
|
144
|
+
// BullMQ stores completed jobs temporarily. Fetch and filter.
|
|
145
|
+
const completed = await this.dispatchQueue.getCompleted(0, 1000);
|
|
146
|
+
const inRange = completed.filter((job) => {
|
|
147
|
+
const event = job.data;
|
|
148
|
+
if (event.topic !== topic)
|
|
149
|
+
return false;
|
|
150
|
+
const ts = new Date(event.timestamp).getTime();
|
|
151
|
+
return ts >= fromMs && ts <= toMs;
|
|
152
|
+
});
|
|
153
|
+
// Sort ascending by timestamp
|
|
154
|
+
inRange.sort((a, b) => {
|
|
155
|
+
const aTs = new Date(a.data.timestamp).getTime();
|
|
156
|
+
const bTs = new Date(b.data.timestamp).getTime();
|
|
157
|
+
return aTs - bTs;
|
|
158
|
+
});
|
|
159
|
+
for (const job of inRange) {
|
|
160
|
+
await handler(job.data);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// -------------------------------------------------------------------------
|
|
164
|
+
// close
|
|
165
|
+
// -------------------------------------------------------------------------
|
|
166
|
+
async close() {
|
|
167
|
+
if (this.closed)
|
|
168
|
+
return;
|
|
169
|
+
this.closed = true;
|
|
170
|
+
await this.dispatchWorker.close();
|
|
171
|
+
await this.dispatchQueue.close();
|
|
172
|
+
const closePromises = [];
|
|
173
|
+
for (const entry of this.subscribers.values()) {
|
|
174
|
+
closePromises.push(entry.worker.close());
|
|
175
|
+
}
|
|
176
|
+
for (const queue of this.deliveryQueues.values()) {
|
|
177
|
+
closePromises.push(queue.close());
|
|
178
|
+
}
|
|
179
|
+
await Promise.all(closePromises);
|
|
180
|
+
this.subscribers.clear();
|
|
181
|
+
this.deliveryQueues.clear();
|
|
182
|
+
}
|
|
183
|
+
// -------------------------------------------------------------------------
|
|
184
|
+
// Private helpers
|
|
185
|
+
// -------------------------------------------------------------------------
|
|
186
|
+
async addSubscriber(topicOrPattern, isPattern, handler) {
|
|
187
|
+
if (this.closed)
|
|
188
|
+
throw new Error('BullMQEventBus is closed');
|
|
189
|
+
const key = `sub:${randomUUID()}`;
|
|
190
|
+
const deliveryQueueName = `ouija.event-bus.delivery.${key}`;
|
|
191
|
+
const deliveryQueue = new Queue(deliveryQueueName, {
|
|
192
|
+
connection: this.connection,
|
|
193
|
+
defaultJobOptions: {
|
|
194
|
+
removeOnComplete: { count: 500 },
|
|
195
|
+
removeOnFail: { count: 100 },
|
|
196
|
+
attempts: 3,
|
|
197
|
+
backoff: { type: 'exponential', delay: 500 },
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
const worker = new Worker(deliveryQueueName, async (job) => {
|
|
201
|
+
const event = job.data;
|
|
202
|
+
await handler(event);
|
|
203
|
+
}, { connection: this.connection, concurrency: 1 });
|
|
204
|
+
worker.on('failed', (job, err) => {
|
|
205
|
+
console.error(`[BullMQEventBus] delivery job ${job?.id ?? 'unknown'} for subscriber ${key} failed:`, err);
|
|
206
|
+
});
|
|
207
|
+
const entry = {
|
|
208
|
+
key,
|
|
209
|
+
topic: topicOrPattern,
|
|
210
|
+
isPattern,
|
|
211
|
+
handler,
|
|
212
|
+
worker,
|
|
213
|
+
};
|
|
214
|
+
this.subscribers.set(key, entry);
|
|
215
|
+
this.deliveryQueues.set(key, deliveryQueue);
|
|
216
|
+
const unsubscribe = async () => {
|
|
217
|
+
this.subscribers.delete(key);
|
|
218
|
+
const q = this.deliveryQueues.get(key);
|
|
219
|
+
this.deliveryQueues.delete(key);
|
|
220
|
+
await worker.close();
|
|
221
|
+
if (q)
|
|
222
|
+
await q.close();
|
|
223
|
+
};
|
|
224
|
+
return unsubscribe;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Fan out a published event to all matching subscribers.
|
|
228
|
+
* Called by the dispatch worker — runs inside BullMQ.
|
|
229
|
+
*/
|
|
230
|
+
async fanOut(event) {
|
|
231
|
+
const enqueuePromises = [];
|
|
232
|
+
for (const [key, entry] of this.subscribers) {
|
|
233
|
+
const matches = entry.isPattern
|
|
234
|
+
? topicMatchesPattern(event.topic, entry.topic)
|
|
235
|
+
: event.topic === entry.topic;
|
|
236
|
+
if (!matches)
|
|
237
|
+
continue;
|
|
238
|
+
const deliveryQueue = this.deliveryQueues.get(key);
|
|
239
|
+
if (!deliveryQueue)
|
|
240
|
+
continue;
|
|
241
|
+
enqueuePromises.push(deliveryQueue.add(`deliver:${event.id}:${key}`, event, { jobId: `deliver:${event.id}:${key}` }));
|
|
242
|
+
}
|
|
243
|
+
await Promise.all(enqueuePromises);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=bullmq-event-bus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bullmq-event-bus.js","sourceRoot":"","sources":["../src/bullmq-event-bus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAA0B,MAAM,QAAQ,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAsBzC,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E;;;;;;;;;;;;GAYG;AACH,SAAS,WAAW,CAAC,OAAe;IAClC,qEAAqE;IACrE,MAAM,OAAO,GAAG,OAAO;SACpB,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,0CAA0C;SAC/E,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,iCAAiC;QAC9D,8CAA8C;SAC7C,OAAO,CAAC,OAAO,EAAE,kBAAkB,CAAC;SACpC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC;SACvB,OAAO,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC;IAEtC,OAAO,IAAI,MAAM,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAa,EAAE,OAAe;IACzD,4BAA4B;IAC5B,IAAI,KAAK,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,cAAc;IACR,UAAU,CAAoB;IAC9B,WAAW,GAAG,IAAI,GAAG,EAA2B,CAAC;IAClE,oDAAoD;IACnC,cAAc,GAAG,IAAI,GAAG,EAAiB,CAAC;IAC3D,uDAAuD;IACtC,aAAa,CAAQ;IACrB,cAAc,CAAS;IAChC,MAAM,GAAG,KAAK,CAAC;IAEvB,YAAY,UAA6B;QACvC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAE7B,0DAA0D;QAC1D,yDAAyD;QACzD,IAAI,CAAC,aAAa,GAAG,IAAI,KAAK,CAAC,0BAA0B,EAAE;YACzD,UAAU;YACV,iBAAiB,EAAE;gBACjB,gBAAgB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;gBACjC,YAAY,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;gBAC5B,QAAQ,EAAE,CAAC;gBACX,OAAO,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE;aAC9C;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,cAAc,GAAG,IAAI,MAAM,CAC9B,0BAA0B,EAC1B,KAAK,EAAE,GAAG,EAAE,EAAE;YACZ,MAAM,KAAK,GAAG,GAAG,CAAC,IAAkB,CAAC;YACrC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC,EACD,EAAE,UAAU,EAAE,WAAW,EAAE,EAAE,EAAE,CAChC,CAAC;QAEF,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC5C,OAAO,CAAC,KAAK,CACX,iCAAiC,GAAG,EAAE,EAAE,IAAI,SAAS,UAAU,EAC/D,GAAG,CACJ,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED,4EAA4E;IAC5E,UAAU;IACV,4EAA4E;IAE5E,KAAK,CAAC,OAAO,CACX,KAAa,EACb,OAA8B,EAC9B,UAA0B,EAAE;QAE5B,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAE7D,MAAM,KAAK,GAAuB;YAChC,EAAE,EAAE,UAAU,EAAE;YAChB,KAAK;YACL,OAAO;YACP,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,SAAS;YAC/C,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,UAAU,EAAE;SACrD,CAAC;QAEF,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,KAAK,EAAE,EAAE,KAAK,EAAE;YACpD,KAAK,EAAE,SAAS,KAAK,CAAC,EAAE,EAAE;SAC3B,CAAC,CAAC;QAEH,OAAO,KAAK,CAAC,EAAE,CAAC;IAClB,CAAC;IAED,4EAA4E;IAC5E,uCAAuC;IACvC,4EAA4E;IAE5E,KAAK,CAAC,SAAS,CACb,KAAa,EACb,OAA6B;QAE7B,yEAAyE;QACzE,MAAM,cAAc,GAAwB,KAAK,EAAE,KAAK,EAAE,EAAE;YAC1D,MAAM,OAAO,CAAC,KAA2B,CAAC,CAAC;QAC7C,CAAC,CAAC;QAEF,OAAO,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC;IAC1D,CAAC;IAED,4EAA4E;IAC5E,qDAAqD;IACrD,4EAA4E;IAE5E,KAAK,CAAC,gBAAgB,CACpB,OAAe,EACf,OAA4B;QAE5B,OAAO,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;IAED,4EAA4E;IAC5E,SAAS;IACT,4EAA4E;IAE5E;;;;;;;;OAQG;IACH,KAAK,CAAC,MAAM,CACV,KAAiB,EACjB,IAAY,EACZ,EAAU,EACV,OAA4B;QAE5B,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAE7D,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;QAEpC,8DAA8D;QAC9D,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAEjE,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;YACvC,MAAM,KAAK,GAAG,GAAG,CAAC,IAAkB,CAAC;YACrC,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK;gBAAE,OAAO,KAAK,CAAC;YACxC,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;YAC/C,OAAO,EAAE,IAAI,MAAM,IAAI,EAAE,IAAI,IAAI,CAAC;QACpC,CAAC,CAAC,CAAC;QAEH,8BAA8B;QAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACpB,MAAM,GAAG,GAAG,IAAI,IAAI,CAAE,CAAC,CAAC,IAAmB,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;YACjE,MAAM,GAAG,GAAG,IAAI,IAAI,CAAE,CAAC,CAAC,IAAmB,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;YACjE,OAAO,GAAG,GAAG,GAAG,CAAC;QACnB,CAAC,CAAC,CAAC;QAEH,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,OAAO,CAAC,GAAG,CAAC,IAAkB,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,QAAQ;IACR,4EAA4E;IAE5E,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QAEnB,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAClC,MAAM,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QAEjC,MAAM,aAAa,GAAoB,EAAE,CAAC;QAC1C,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;YAC9C,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QAC3C,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,EAAE,CAAC;YACjD,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QAEjC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,4EAA4E;IAC5E,kBAAkB;IAClB,4EAA4E;IAEpE,KAAK,CAAC,aAAa,CACzB,cAAsB,EACtB,SAAkB,EAClB,OAA4B;QAE5B,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAE7D,MAAM,GAAG,GAAG,OAAO,UAAU,EAAE,EAAE,CAAC;QAClC,MAAM,iBAAiB,GAAG,4BAA4B,GAAG,EAAE,CAAC;QAE5D,MAAM,aAAa,GAAG,IAAI,KAAK,CAAC,iBAAiB,EAAE;YACjD,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,iBAAiB,EAAE;gBACjB,gBAAgB,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;gBAChC,YAAY,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;gBAC5B,QAAQ,EAAE,CAAC;gBACX,OAAO,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,EAAE;aAC7C;SACF,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,iBAAiB,EACjB,KAAK,EAAE,GAAG,EAAE,EAAE;YACZ,MAAM,KAAK,GAAG,GAAG,CAAC,IAAkB,CAAC;YACrC,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC,EACD,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,EAAE,CAAC,EAAE,CAChD,CAAC;QAEF,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC/B,OAAO,CAAC,KAAK,CACX,iCAAiC,GAAG,EAAE,EAAE,IAAI,SAAS,mBAAmB,GAAG,UAAU,EACrF,GAAG,CACJ,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,MAAM,KAAK,GAAoB;YAC7B,GAAG;YACH,KAAK,EAAE,cAAc;YACrB,SAAS;YACT,OAAO;YACP,MAAM;SACP,CAAC;QAEF,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACjC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAE5C,MAAM,WAAW,GAAgB,KAAK,IAAI,EAAE;YAC1C,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACvC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAChC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACrB,IAAI,CAAC;gBAAE,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;QACzB,CAAC,CAAC;QAEF,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,MAAM,CAAC,KAAiB;QACpC,MAAM,eAAe,GAAuB,EAAE,CAAC;QAE/C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC5C,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS;gBAC7B,CAAC,CAAC,mBAAmB,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC;gBAC/C,CAAC,CAAC,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC;YAEhC,IAAI,CAAC,OAAO;gBAAE,SAAS;YAEvB,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACnD,IAAI,CAAC,aAAa;gBAAE,SAAS;YAE7B,eAAe,CAAC,IAAI,CAClB,aAAa,CAAC,GAAG,CACf,WAAW,KAAK,CAAC,EAAE,IAAI,GAAG,EAAE,EAC5B,KAAK,EACL,EAAE,KAAK,EAAE,WAAW,KAAK,CAAC,EAAE,IAAI,GAAG,EAAE,EAAE,CACxC,CACF,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACrC,CAAC;CACF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ConnectionOptions } from 'bullmq';
|
|
2
|
+
import type { EnqueueOptions, JobHandler, JobQueue, QueueDataMap, QueueName } from './job-queue.js';
|
|
3
|
+
/**
|
|
4
|
+
* BullMQ-backed JobQueue implementation.
|
|
5
|
+
*
|
|
6
|
+
* One BullMQ Queue per queue name. Workers are created lazily on the first
|
|
7
|
+
* `process()` call for a given queue name.
|
|
8
|
+
*
|
|
9
|
+
* Assumptions:
|
|
10
|
+
* - Redis is configured with `maxmemory-policy noeviction`.
|
|
11
|
+
* - All queue names are treated as static. Dynamic queue creation at runtime
|
|
12
|
+
* is not supported in v1.
|
|
13
|
+
*/
|
|
14
|
+
export declare class BullMQJobQueue implements JobQueue {
|
|
15
|
+
private readonly connection;
|
|
16
|
+
private readonly queues;
|
|
17
|
+
private readonly workers;
|
|
18
|
+
private closed;
|
|
19
|
+
constructor(connection: ConnectionOptions);
|
|
20
|
+
enqueue<TQueue extends QueueName>(queue: TQueue, data: QueueDataMap[TQueue], options?: EnqueueOptions): Promise<string>;
|
|
21
|
+
process<TQueue extends QueueName>(queue: TQueue, handler: JobHandler<QueueDataMap[TQueue]>, concurrency?: number): Promise<void>;
|
|
22
|
+
cancelJob(queue: QueueName, jobId: string): Promise<void>;
|
|
23
|
+
close(): Promise<void>;
|
|
24
|
+
private getOrCreateQueue;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=bullmq-job-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bullmq-job-queue.d.ts","sourceRoot":"","sources":["../src/bullmq-job-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC/D,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,SAAS,EACV,MAAM,gBAAgB,CAAC;AAExB;;;;;;;;;;GAUG;AACH,qBAAa,cAAe,YAAW,QAAQ;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,MAAM,CAAS;gBAEX,UAAU,EAAE,iBAAiB;IAQnC,OAAO,CAAC,MAAM,SAAS,SAAS,EACpC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,EAC1B,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC;IAgCZ,OAAO,CAAC,MAAM,SAAS,SAAS,EACpC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,UAAU,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EACzC,WAAW,SAAI,GACd,OAAO,CAAC,IAAI,CAAC;IAuCV,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BzD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB5B,OAAO,CAAC,gBAAgB;CAezB"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { Queue, Worker } from 'bullmq';
|
|
2
|
+
/**
|
|
3
|
+
* BullMQ-backed JobQueue implementation.
|
|
4
|
+
*
|
|
5
|
+
* One BullMQ Queue per queue name. Workers are created lazily on the first
|
|
6
|
+
* `process()` call for a given queue name.
|
|
7
|
+
*
|
|
8
|
+
* Assumptions:
|
|
9
|
+
* - Redis is configured with `maxmemory-policy noeviction`.
|
|
10
|
+
* - All queue names are treated as static. Dynamic queue creation at runtime
|
|
11
|
+
* is not supported in v1.
|
|
12
|
+
*/
|
|
13
|
+
export class BullMQJobQueue {
|
|
14
|
+
connection;
|
|
15
|
+
queues = new Map();
|
|
16
|
+
workers = new Map();
|
|
17
|
+
closed = false;
|
|
18
|
+
constructor(connection) {
|
|
19
|
+
this.connection = connection;
|
|
20
|
+
}
|
|
21
|
+
// -------------------------------------------------------------------------
|
|
22
|
+
// enqueue
|
|
23
|
+
// -------------------------------------------------------------------------
|
|
24
|
+
async enqueue(queue, data, options = {}) {
|
|
25
|
+
if (this.closed)
|
|
26
|
+
throw new Error('BullMQJobQueue is closed');
|
|
27
|
+
const q = this.getOrCreateQueue(queue);
|
|
28
|
+
// Build options conditionally to satisfy exactOptionalPropertyTypes:
|
|
29
|
+
// never assign `undefined` to an optional property.
|
|
30
|
+
const jobOptions = {
|
|
31
|
+
attempts: options.attempts ?? 3,
|
|
32
|
+
backoff: options.backoff
|
|
33
|
+
? { type: options.backoff.type, delay: options.backoff.delay }
|
|
34
|
+
: { type: 'exponential', delay: 1000 },
|
|
35
|
+
priority: options.priority ?? 0,
|
|
36
|
+
removeOnComplete: { count: 1000 },
|
|
37
|
+
removeOnFail: { count: 500 },
|
|
38
|
+
};
|
|
39
|
+
if (options.jobId !== undefined)
|
|
40
|
+
jobOptions['jobId'] = options.jobId;
|
|
41
|
+
if (options.delayMs !== undefined)
|
|
42
|
+
jobOptions['delay'] = options.delayMs;
|
|
43
|
+
const job = await q.add(queue, data, jobOptions);
|
|
44
|
+
if (!job.id) {
|
|
45
|
+
throw new Error(`BullMQ did not assign an ID to job on queue "${queue}"`);
|
|
46
|
+
}
|
|
47
|
+
return job.id;
|
|
48
|
+
}
|
|
49
|
+
// -------------------------------------------------------------------------
|
|
50
|
+
// process
|
|
51
|
+
// -------------------------------------------------------------------------
|
|
52
|
+
async process(queue, handler, concurrency = 1) {
|
|
53
|
+
if (this.closed)
|
|
54
|
+
throw new Error('BullMQJobQueue is closed');
|
|
55
|
+
// Close any existing worker for this queue before replacing the handler
|
|
56
|
+
const existing = this.workers.get(queue);
|
|
57
|
+
if (existing) {
|
|
58
|
+
await existing.close();
|
|
59
|
+
this.workers.delete(queue);
|
|
60
|
+
}
|
|
61
|
+
// Ensure the queue exists
|
|
62
|
+
this.getOrCreateQueue(queue);
|
|
63
|
+
const worker = new Worker(queue, async (job) => {
|
|
64
|
+
const jobId = job.id ?? 'unknown';
|
|
65
|
+
await handler(job.data, jobId);
|
|
66
|
+
}, {
|
|
67
|
+
connection: this.connection,
|
|
68
|
+
concurrency,
|
|
69
|
+
});
|
|
70
|
+
worker.on('failed', (job, err) => {
|
|
71
|
+
console.error(`[BullMQJobQueue] job ${job?.id ?? 'unknown'} on queue "${queue}" failed:`, err);
|
|
72
|
+
});
|
|
73
|
+
this.workers.set(queue, worker);
|
|
74
|
+
}
|
|
75
|
+
// -------------------------------------------------------------------------
|
|
76
|
+
// cancelJob
|
|
77
|
+
// -------------------------------------------------------------------------
|
|
78
|
+
async cancelJob(queue, jobId) {
|
|
79
|
+
if (this.closed)
|
|
80
|
+
return;
|
|
81
|
+
const q = this.queues.get(queue);
|
|
82
|
+
if (!q)
|
|
83
|
+
return;
|
|
84
|
+
const job = await q.getJob(jobId);
|
|
85
|
+
if (!job)
|
|
86
|
+
return;
|
|
87
|
+
// Remove the job regardless of its state (waiting, delayed, etc.)
|
|
88
|
+
// For active jobs, this will fail silently — a running job cannot be
|
|
89
|
+
// force-removed; the worker must complete or fail it naturally.
|
|
90
|
+
try {
|
|
91
|
+
await job.remove();
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Job may already be processing — log and move on
|
|
95
|
+
console.warn(`[BullMQJobQueue] could not cancel job "${jobId}" on queue "${queue}" — it may be active`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// -------------------------------------------------------------------------
|
|
99
|
+
// close
|
|
100
|
+
// -------------------------------------------------------------------------
|
|
101
|
+
async close() {
|
|
102
|
+
if (this.closed)
|
|
103
|
+
return;
|
|
104
|
+
this.closed = true;
|
|
105
|
+
const closePromises = [];
|
|
106
|
+
for (const worker of this.workers.values()) {
|
|
107
|
+
closePromises.push(worker.close());
|
|
108
|
+
}
|
|
109
|
+
for (const queue of this.queues.values()) {
|
|
110
|
+
closePromises.push(queue.close());
|
|
111
|
+
}
|
|
112
|
+
await Promise.all(closePromises);
|
|
113
|
+
this.workers.clear();
|
|
114
|
+
this.queues.clear();
|
|
115
|
+
}
|
|
116
|
+
// -------------------------------------------------------------------------
|
|
117
|
+
// Private helpers
|
|
118
|
+
// -------------------------------------------------------------------------
|
|
119
|
+
getOrCreateQueue(name) {
|
|
120
|
+
const existing = this.queues.get(name);
|
|
121
|
+
if (existing)
|
|
122
|
+
return existing;
|
|
123
|
+
const queue = new Queue(name, {
|
|
124
|
+
connection: this.connection,
|
|
125
|
+
defaultJobOptions: {
|
|
126
|
+
removeOnComplete: { count: 1000 },
|
|
127
|
+
removeOnFail: { count: 500 },
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
this.queues.set(name, queue);
|
|
131
|
+
return queue;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=bullmq-job-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bullmq-job-queue.js","sourceRoot":"","sources":["../src/bullmq-job-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { OuijaEvent, OuijaEventMap, OuijaTopic } from '@ouija-dev/types';
|
|
2
|
+
/**
|
|
3
|
+
* Handler for a specific typed topic. Receives the full OuijaEvent envelope.
|
|
4
|
+
*/
|
|
5
|
+
export type EventHandler<TTopic extends OuijaTopic> = (event: OuijaEvent<TTopic>) => Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* Handler for pattern subscriptions. Type-unsafe by design — wildcards cannot
|
|
8
|
+
* be statically constrained to a single payload shape.
|
|
9
|
+
*/
|
|
10
|
+
export type PatternEventHandler = (event: OuijaEvent) => Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Unsubscribe function returned by subscribe / subscribePattern.
|
|
13
|
+
* Calling it deregisters the handler and cleans up any BullMQ workers.
|
|
14
|
+
*/
|
|
15
|
+
export type Unsubscribe = () => Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* EventBus — pub/sub interface for Ouija domain events.
|
|
18
|
+
*
|
|
19
|
+
* Design rules (Decision 2 in spec §2.4):
|
|
20
|
+
* - Separate from JobQueue at the interface level even though both use BullMQ.
|
|
21
|
+
* - `subscribe` is typed: the handler receives the exact payload for TTopic.
|
|
22
|
+
* - `subscribePattern` is intentionally type-unsafe: wildcards span multiple
|
|
23
|
+
* topics and therefore multiple payload shapes. Callers must narrow.
|
|
24
|
+
* - `replay` allows catch-up processing from persistent event storage.
|
|
25
|
+
*/
|
|
26
|
+
export interface EventBus {
|
|
27
|
+
/**
|
|
28
|
+
* Publish an event to all subscribers of `topic`.
|
|
29
|
+
* The bus generates the event envelope (id, timestamp, correlationId).
|
|
30
|
+
* Returns the generated event id.
|
|
31
|
+
*/
|
|
32
|
+
publish<TTopic extends OuijaTopic>(topic: TTopic, payload: OuijaEventMap[TTopic], options?: PublishOptions): Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Subscribe to an exact topic. Returns an unsubscribe function.
|
|
35
|
+
* The handler is called once per published event.
|
|
36
|
+
*/
|
|
37
|
+
subscribe<TTopic extends OuijaTopic>(topic: TTopic, handler: EventHandler<TTopic>): Promise<Unsubscribe>;
|
|
38
|
+
/**
|
|
39
|
+
* Subscribe to events matching a glob-style pattern, e.g. `kanban.card.*`
|
|
40
|
+
* or `agent.**`. Intentionally type-unsafe — callers must narrow the payload
|
|
41
|
+
* themselves. Returns an unsubscribe function.
|
|
42
|
+
*/
|
|
43
|
+
subscribePattern(pattern: string, handler: PatternEventHandler): Promise<Unsubscribe>;
|
|
44
|
+
/**
|
|
45
|
+
* Replay stored events for a topic between `from` and `to` (ISO strings).
|
|
46
|
+
* Events are delivered to `handler` in chronological order.
|
|
47
|
+
* Used for catch-up processing and debugging.
|
|
48
|
+
*/
|
|
49
|
+
replay(topic: OuijaTopic, from: string, to: string, handler: PatternEventHandler): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Gracefully shut down the bus, draining in-flight handlers.
|
|
52
|
+
*/
|
|
53
|
+
close(): Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
export interface PublishOptions {
|
|
56
|
+
/** Explicit correlationId to propagate across services. Defaults to a new UUID. */
|
|
57
|
+
correlationId?: string;
|
|
58
|
+
/** Source plugin identifier. Defaults to 'unknown'. */
|
|
59
|
+
sourcePlugin?: string;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=event-bus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../src/event-bus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9E;;GAEG;AACH,MAAM,MAAM,YAAY,CAAC,MAAM,SAAS,UAAU,IAAI,CACpD,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,KACtB,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEvE;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAE9C;;;;;;;;;GASG;AACH,MAAM,WAAW,QAAQ;IACvB;;;;OAIG;IACH,OAAO,CAAC,MAAM,SAAS,UAAU,EAC/B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAC9B,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnB;;;OAGG;IACH,SAAS,CAAC,MAAM,SAAS,UAAU,EACjC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,GAC5B,OAAO,CAAC,WAAW,CAAC,CAAC;IAExB;;;;OAIG;IACH,gBAAgB,CACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,WAAW,CAAC,CAAC;IAExB;;;;OAIG;IACH,MAAM,CACJ,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,mFAAmF;IACnF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../src/event-bus.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { EventBus, EventHandler, PatternEventHandler, PublishOptions, Unsubscribe, } from './event-bus.js';
|
|
2
|
+
export type { JobQueue, JobHandler, QueueName, QueueDataMap, EnqueueOptions, AgentDispatchJobData, StallCheckJobData, EventBusJobData, } from './job-queue.js';
|
|
3
|
+
export { QUEUE_NAMES } from './job-queue.js';
|
|
4
|
+
export { BullMQEventBus } from './bullmq-event-bus.js';
|
|
5
|
+
export { BullMQJobQueue } from './bullmq-job-queue.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,WAAW,GACZ,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EACV,QAAQ,EACR,UAAU,EACV,SAAS,EACT,YAAY,EACZ,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,yBAAyB;AACzB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { OuijaEvent } from '@ouija-dev/types';
|
|
2
|
+
export declare const QUEUE_NAMES: {
|
|
3
|
+
readonly agentDispatch: "ouija.agent-dispatch";
|
|
4
|
+
readonly stallCheck: "ouija.stall-check";
|
|
5
|
+
readonly eventBus: "ouija.event-bus";
|
|
6
|
+
};
|
|
7
|
+
export type QueueName = (typeof QUEUE_NAMES)[keyof typeof QUEUE_NAMES];
|
|
8
|
+
export interface AgentDispatchJobData {
|
|
9
|
+
instanceId: string;
|
|
10
|
+
dispatchId: string;
|
|
11
|
+
agentId: string;
|
|
12
|
+
cardId: string;
|
|
13
|
+
projectId: string;
|
|
14
|
+
workOrderDescription: string;
|
|
15
|
+
/** ISO timestamp of when this dispatch was requested */
|
|
16
|
+
dispatchedAt: string;
|
|
17
|
+
}
|
|
18
|
+
export interface StallCheckJobData {
|
|
19
|
+
instanceId: string;
|
|
20
|
+
dispatchId: string;
|
|
21
|
+
/** ISO timestamp the stall check should fire at (BullMQ delay handles this) */
|
|
22
|
+
expectedBy: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Internal job data used by BullMQEventBus to fan-out events to subscribers.
|
|
26
|
+
* The subscriberKey uniquely identifies the registered handler.
|
|
27
|
+
*/
|
|
28
|
+
export interface EventBusJobData {
|
|
29
|
+
event: OuijaEvent;
|
|
30
|
+
subscriberKey: string;
|
|
31
|
+
}
|
|
32
|
+
export interface QueueDataMap {
|
|
33
|
+
[QUEUE_NAMES.agentDispatch]: AgentDispatchJobData;
|
|
34
|
+
[QUEUE_NAMES.stallCheck]: StallCheckJobData;
|
|
35
|
+
[QUEUE_NAMES.eventBus]: EventBusJobData;
|
|
36
|
+
}
|
|
37
|
+
export interface EnqueueOptions {
|
|
38
|
+
/** BullMQ job ID. If provided, duplicate jobs with same ID are deduplicated. */
|
|
39
|
+
jobId?: string;
|
|
40
|
+
/** Delay in milliseconds before the job becomes active. */
|
|
41
|
+
delayMs?: number;
|
|
42
|
+
/** Number of retry attempts on failure. Defaults to 3. */
|
|
43
|
+
attempts?: number;
|
|
44
|
+
/** Backoff strategy for retries. */
|
|
45
|
+
backoff?: {
|
|
46
|
+
type: 'exponential' | 'fixed';
|
|
47
|
+
delay: number;
|
|
48
|
+
};
|
|
49
|
+
/** Priority (lower number = higher priority). Defaults to 0. */
|
|
50
|
+
priority?: number;
|
|
51
|
+
}
|
|
52
|
+
export type JobHandler<TData> = (data: TData, jobId: string) => Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* JobQueue — durable task dispatch interface.
|
|
55
|
+
*
|
|
56
|
+
* Design rules (Decision 2 in spec §2.4):
|
|
57
|
+
* - Separate from EventBus at the interface level.
|
|
58
|
+
* - Typed via QueueDataMap: `enqueue<TQueue>` ensures data matches queue.
|
|
59
|
+
* - One queue per concern (agentDispatch, stallCheck, eventBus).
|
|
60
|
+
* - BullMQ implementation uses noeviction Redis — jobs must never be evicted.
|
|
61
|
+
*/
|
|
62
|
+
export interface JobQueue {
|
|
63
|
+
/**
|
|
64
|
+
* Enqueue a job on the specified queue.
|
|
65
|
+
* Returns the BullMQ job ID.
|
|
66
|
+
*/
|
|
67
|
+
enqueue<TQueue extends QueueName>(queue: TQueue, data: QueueDataMap[TQueue], options?: EnqueueOptions): Promise<string>;
|
|
68
|
+
/**
|
|
69
|
+
* Register a processor for the specified queue.
|
|
70
|
+
* Workers are created lazily on the first `process()` call per queue.
|
|
71
|
+
* Calling process() twice on the same queue replaces the handler.
|
|
72
|
+
*/
|
|
73
|
+
process<TQueue extends QueueName>(queue: TQueue, handler: JobHandler<QueueDataMap[TQueue]>, concurrency?: number): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Remove a job by ID from the specified queue.
|
|
76
|
+
* No-ops if the job does not exist or is already completed.
|
|
77
|
+
*/
|
|
78
|
+
cancelJob(queue: QueueName, jobId: string): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Gracefully shut down all workers, waiting for in-flight jobs to complete.
|
|
81
|
+
*/
|
|
82
|
+
close(): Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=job-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-queue.d.ts","sourceRoot":"","sources":["../src/job-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,kBAAkB,CAAC;AAM/D,eAAO,MAAM,WAAW;;;;CAId,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC;AAMvE,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,wDAAwD;IACxD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,UAAU,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB;AAMD,MAAM,WAAW,YAAY;IAC3B,CAAC,WAAW,CAAC,aAAa,CAAC,EAAE,oBAAoB,CAAC;IAClD,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC5C,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC;CACzC;AAMD,MAAM,WAAW,cAAc;IAC7B,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,OAAO,CAAC,EAAE;QACR,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC;QAC9B,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAMD,MAAM,MAAM,UAAU,CAAC,KAAK,IAAI,CAC9B,IAAI,EAAE,KAAK,EACX,KAAK,EAAE,MAAM,KACV,OAAO,CAAC,IAAI,CAAC,CAAC;AAMnB;;;;;;;;GAQG;AACH,MAAM,WAAW,QAAQ;IACvB;;;OAGG;IACH,OAAO,CAAC,MAAM,SAAS,SAAS,EAC9B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,EAC1B,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnB;;;;OAIG;IACH,OAAO,CAAC,MAAM,SAAS,SAAS,EAC9B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,UAAU,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EACzC,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;OAGG;IACH,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1D;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Queue names — single source of truth
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
export const QUEUE_NAMES = {
|
|
5
|
+
agentDispatch: 'ouija.agent-dispatch',
|
|
6
|
+
stallCheck: 'ouija.stall-check',
|
|
7
|
+
eventBus: 'ouija.event-bus',
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=job-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-queue.js","sourceRoot":"","sources":["../src/job-queue.ts"],"names":[],"mappings":"AAEA,8EAA8E;AAC9E,uCAAuC;AACvC,8EAA8E;AAE9E,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,aAAa,EAAE,sBAAsB;IACrC,UAAU,EAAE,mBAAmB;IAC/B,QAAQ,EAAE,iBAAiB;CACnB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ouija-dev/bus",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": ["dist/", "README.md"],
|
|
8
|
+
"publishConfig": { "access": "public" },
|
|
9
|
+
"repository": { "type": "git", "url": "https://github.com/muhammadkh4n/ouija.git" },
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"typecheck": "tsc --noEmit",
|
|
19
|
+
"test": "vitest run"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@ouija-dev/types": "*",
|
|
23
|
+
"bullmq": "^5.0.0",
|
|
24
|
+
"ioredis": "^5.3.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^22.0.0",
|
|
28
|
+
"typescript": "^5.5.0",
|
|
29
|
+
"vitest": "^3.0.0"
|
|
30
|
+
}
|
|
31
|
+
}
|