@astriveone/plugin-queue 1.0.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.
@@ -0,0 +1,18 @@
1
+ import type { Plugin, ChangeEvent } from "@astriveone/core";
2
+ export type JobHandler = (event: ChangeEvent) => Promise<void> | void;
3
+ /**
4
+ * queuePlugin — an in-process job queue triggered by database writes, with
5
+ * retry-with-backoff. For multi-process workloads, swap the in-memory
6
+ * queue for Redis/SQS by implementing the same `enqueue`/processing loop
7
+ * against your broker of choice — this plugin defines the contract.
8
+ *
9
+ * await db.use(queuePlugin({
10
+ * concurrency: 4,
11
+ * on: { orders: { insert: async (e) => sendToFulfillment(e.doc) } },
12
+ * }));
13
+ */
14
+ export declare function queuePlugin(options: {
15
+ concurrency?: number;
16
+ maxAttempts?: number;
17
+ on: Record<string, Partial<Record<ChangeEvent["type"], JobHandler>>>;
18
+ }): Plugin;
package/dist/index.js ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * queuePlugin — an in-process job queue triggered by database writes, with
3
+ * retry-with-backoff. For multi-process workloads, swap the in-memory
4
+ * queue for Redis/SQS by implementing the same `enqueue`/processing loop
5
+ * against your broker of choice — this plugin defines the contract.
6
+ *
7
+ * await db.use(queuePlugin({
8
+ * concurrency: 4,
9
+ * on: { orders: { insert: async (e) => sendToFulfillment(e.doc) } },
10
+ * }));
11
+ */
12
+ export function queuePlugin(options) {
13
+ const concurrency = options.concurrency ?? 2;
14
+ const maxAttempts = options.maxAttempts ?? 3;
15
+ const queue = [];
16
+ let active = 0;
17
+ async function processNext() {
18
+ if (active >= concurrency)
19
+ return;
20
+ const job = queue.shift();
21
+ if (!job)
22
+ return;
23
+ active++;
24
+ try {
25
+ await job.handler(job.event);
26
+ }
27
+ catch (err) {
28
+ job.attempts++;
29
+ if (job.attempts < maxAttempts) {
30
+ setTimeout(() => {
31
+ queue.push(job);
32
+ processNext();
33
+ }, 2 ** job.attempts * 500); // exponential backoff
34
+ }
35
+ else {
36
+ console.error(`AstriveOne queue plugin: job failed permanently for ${job.event.collection}/${job.event.id}`, err);
37
+ }
38
+ }
39
+ finally {
40
+ active--;
41
+ processNext();
42
+ }
43
+ }
44
+ return {
45
+ name: "queue",
46
+ version: "1.0.0",
47
+ install(db) {
48
+ db.watchAll((event) => {
49
+ const handler = options.on[event.collection]?.[event.type];
50
+ if (!handler)
51
+ return;
52
+ queue.push({ event, handler, attempts: 0 });
53
+ processNext();
54
+ });
55
+ db.queue = { pending: () => queue.length, active: () => active };
56
+ },
57
+ };
58
+ }
package/package.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@astriveone/plugin-queue",
3
+ "version": "1.0.0",
4
+ "description": "AstriveOne queue plugin",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "scripts": { "build": "tsc -p tsconfig.json" },
9
+ "dependencies": { "@astriveone/core": "1.0.0" },
10
+ "files": ["dist"],
11
+ "license": "MIT"
12
+ }