@brightchain/brightchain-api-lib 0.25.0 → 0.26.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/package.json +4 -3
- package/src/lib/application.d.ts +3 -14
- package/src/lib/application.d.ts.map +1 -1
- package/src/lib/application.js +89 -34
- package/src/lib/application.js.map +1 -1
- package/src/lib/databaseInit.d.ts +7 -11
- package/src/lib/databaseInit.d.ts.map +1 -1
- package/src/lib/databaseInit.js +41 -107
- package/src/lib/databaseInit.js.map +1 -1
- package/src/lib/datastore/block-document-store-factory.d.ts +3 -0
- package/src/lib/datastore/block-document-store-factory.d.ts.map +1 -1
- package/src/lib/datastore/block-document-store-factory.js +15 -18
- package/src/lib/datastore/block-document-store-factory.js.map +1 -1
- package/src/lib/datastore/block-document-store.d.ts +2 -191
- package/src/lib/datastore/block-document-store.d.ts.map +1 -1
- package/src/lib/datastore/block-document-store.js +4 -628
- package/src/lib/datastore/block-document-store.js.map +1 -1
- package/src/lib/datastore/document-store.d.ts +1 -62
- package/src/lib/datastore/document-store.d.ts.map +1 -1
- package/src/lib/datastore/memory-document-store.d.ts +1 -8
- package/src/lib/datastore/memory-document-store.d.ts.map +1 -1
- package/src/lib/datastore/memory-document-store.js +3 -214
- package/src/lib/datastore/memory-document-store.js.map +1 -1
- package/src/lib/environment.d.ts +4 -47
- package/src/lib/environment.d.ts.map +1 -1
- package/src/lib/environment.js +2 -136
- package/src/lib/environment.js.map +1 -1
- package/src/lib/interfaces/environment.d.ts +3 -25
- package/src/lib/interfaces/environment.d.ts.map +1 -1
- package/src/lib/middleware/index.d.ts +1 -1
- package/src/lib/middleware/index.d.ts.map +1 -1
- package/src/lib/middleware/index.js +3 -2
- package/src/lib/middleware/index.js.map +1 -1
- package/src/lib/middleware/validateBody.d.ts +1 -12
- package/src/lib/middleware/validateBody.d.ts.map +1 -1
- package/src/lib/middleware/validateBody.js +4 -32
- package/src/lib/middleware/validateBody.js.map +1 -1
- package/src/lib/middlewares.d.ts.map +1 -1
- package/src/lib/middlewares.js +7 -1
- package/src/lib/middlewares.js.map +1 -1
- package/src/lib/plugins/brightchain-database-plugin.d.ts +27 -79
- package/src/lib/plugins/brightchain-database-plugin.d.ts.map +1 -1
- package/src/lib/plugins/brightchain-database-plugin.js +27 -97
- package/src/lib/plugins/brightchain-database-plugin.js.map +1 -1
- package/src/lib/services/emailGateway/antiSpamFilter.d.ts +229 -0
- package/src/lib/services/emailGateway/antiSpamFilter.d.ts.map +1 -0
- package/src/lib/services/emailGateway/antiSpamFilter.js +325 -0
- package/src/lib/services/emailGateway/antiSpamFilter.js.map +1 -0
- package/src/lib/services/emailGateway/bounceProcessor.d.ts +171 -0
- package/src/lib/services/emailGateway/bounceProcessor.d.ts.map +1 -0
- package/src/lib/services/emailGateway/bounceProcessor.js +378 -0
- package/src/lib/services/emailGateway/bounceProcessor.js.map +1 -0
- package/src/lib/services/emailGateway/emailAuthVerifier.d.ts +99 -0
- package/src/lib/services/emailGateway/emailAuthVerifier.d.ts.map +1 -0
- package/src/lib/services/emailGateway/emailAuthVerifier.js +202 -0
- package/src/lib/services/emailGateway/emailAuthVerifier.js.map +1 -0
- package/src/lib/services/emailGateway/emailGatewayConfig.d.ts +73 -0
- package/src/lib/services/emailGateway/emailGatewayConfig.d.ts.map +1 -0
- package/src/lib/services/emailGateway/emailGatewayConfig.js +107 -0
- package/src/lib/services/emailGateway/emailGatewayConfig.js.map +1 -0
- package/src/lib/services/emailGateway/emailGatewayService.d.ts +152 -0
- package/src/lib/services/emailGateway/emailGatewayService.d.ts.map +1 -0
- package/src/lib/services/emailGateway/emailGatewayService.js +201 -0
- package/src/lib/services/emailGateway/emailGatewayService.js.map +1 -0
- package/src/lib/services/emailGateway/gatewayObservability.d.ts +123 -0
- package/src/lib/services/emailGateway/gatewayObservability.d.ts.map +1 -0
- package/src/lib/services/emailGateway/gatewayObservability.js +186 -0
- package/src/lib/services/emailGateway/gatewayObservability.js.map +1 -0
- package/src/lib/services/emailGateway/inboundProcessor.d.ts +113 -0
- package/src/lib/services/emailGateway/inboundProcessor.d.ts.map +1 -0
- package/src/lib/services/emailGateway/inboundProcessor.js +298 -0
- package/src/lib/services/emailGateway/inboundProcessor.js.map +1 -0
- package/src/lib/services/emailGateway/index.d.ts +23 -0
- package/src/lib/services/emailGateway/index.d.ts.map +1 -0
- package/src/lib/services/emailGateway/index.js +26 -0
- package/src/lib/services/emailGateway/index.js.map +1 -0
- package/src/lib/services/emailGateway/outboundDeliveryWorker.d.ts +111 -0
- package/src/lib/services/emailGateway/outboundDeliveryWorker.d.ts.map +1 -0
- package/src/lib/services/emailGateway/outboundDeliveryWorker.js +97 -0
- package/src/lib/services/emailGateway/outboundDeliveryWorker.js.map +1 -0
- package/src/lib/services/emailGateway/outboundQueue.d.ts +135 -0
- package/src/lib/services/emailGateway/outboundQueue.d.ts.map +1 -0
- package/src/lib/services/emailGateway/outboundQueue.js +227 -0
- package/src/lib/services/emailGateway/outboundQueue.js.map +1 -0
- package/src/lib/services/emailGateway/outboundQueueStore.d.ts +110 -0
- package/src/lib/services/emailGateway/outboundQueueStore.d.ts.map +1 -0
- package/src/lib/services/emailGateway/outboundQueueStore.js +131 -0
- package/src/lib/services/emailGateway/outboundQueueStore.js.map +1 -0
- package/src/lib/services/emailGateway/recipientLookupService.d.ts +135 -0
- package/src/lib/services/emailGateway/recipientLookupService.d.ts.map +1 -0
- package/src/lib/services/emailGateway/recipientLookupService.js +294 -0
- package/src/lib/services/emailGateway/recipientLookupService.js.map +1 -0
- package/src/lib/services/emailGateway/retryBackoff.d.ts +79 -0
- package/src/lib/services/emailGateway/retryBackoff.d.ts.map +1 -0
- package/src/lib/services/emailGateway/retryBackoff.js +77 -0
- package/src/lib/services/emailGateway/retryBackoff.js.map +1 -0
- package/src/lib/services/index.d.ts +1 -0
- package/src/lib/services/index.d.ts.map +1 -1
- package/src/lib/services/index.js +1 -0
- package/src/lib/services/index.js.map +1 -1
- package/src/lib/services/quorumDatabaseAdapter.d.ts +7 -1
- package/src/lib/services/quorumDatabaseAdapter.d.ts.map +1 -1
- package/src/lib/services/quorumDatabaseAdapter.js +83 -0
- package/src/lib/services/quorumDatabaseAdapter.js.map +1 -1
- package/src/lib/services/sessionAdapter.d.ts +2 -61
- package/src/lib/services/sessionAdapter.d.ts.map +1 -1
- package/src/lib/services/sessionAdapter.js +2 -102
- package/src/lib/services/sessionAdapter.js.map +1 -1
- package/src/lib/shared-types.d.ts +7 -15
- package/src/lib/shared-types.d.ts.map +1 -1
- package/src/lib/types/backend-id.d.ts +1 -2
- package/src/lib/types/backend-id.d.ts.map +1 -1
- package/src/lib/validation/userValidation.d.ts +2 -43
- package/src/lib/validation/userValidation.d.ts.map +1 -1
- package/src/lib/validation/userValidation.js +6 -144
- package/src/lib/validation/userValidation.js.map +1 -1
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OutboundQueue — persistent FIFO queue with concurrency control for
|
|
3
|
+
* outbound email delivery.
|
|
4
|
+
*
|
|
5
|
+
* Wraps an `IOutboundQueueStore` for persistence and enforces a configurable
|
|
6
|
+
* concurrency limit on simultaneous deliveries. The processing loop dequeues
|
|
7
|
+
* items and passes them to a handler callback.
|
|
8
|
+
*
|
|
9
|
+
* On `start()`, the queue resumes processing any previously queued messages
|
|
10
|
+
* that have not exceeded the maximum retry duration (Req 9.2).
|
|
11
|
+
*
|
|
12
|
+
* @see Requirements 9.1, 9.2, 9.3, 9.4, 9.5
|
|
13
|
+
* @module outboundQueue
|
|
14
|
+
*/
|
|
15
|
+
import type { IEmailGatewayConfig } from './emailGatewayConfig';
|
|
16
|
+
import type { IOutboundQueue, IOutboundQueueItem } from './emailGatewayService';
|
|
17
|
+
import type { IOutboundQueueStore } from './outboundQueueStore';
|
|
18
|
+
/**
|
|
19
|
+
* Callback invoked for each dequeued item. The handler is responsible for
|
|
20
|
+
* performing the actual SMTP delivery (or delegating to a worker).
|
|
21
|
+
*
|
|
22
|
+
* @param item - The dequeued outbound queue item
|
|
23
|
+
* @returns Resolves when the handler has finished processing the item
|
|
24
|
+
*/
|
|
25
|
+
export type OutboundQueueHandler = (item: IOutboundQueueItem) => Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Persistent FIFO outbound email queue with concurrency control.
|
|
28
|
+
*
|
|
29
|
+
* Implements `IOutboundQueue` so the `EmailGatewayService` orchestrator
|
|
30
|
+
* can enqueue messages without knowing about concurrency or persistence
|
|
31
|
+
* internals.
|
|
32
|
+
*
|
|
33
|
+
* @see Requirements 9.1, 9.2, 9.3, 9.4, 9.5
|
|
34
|
+
*/
|
|
35
|
+
export declare class OutboundQueue implements IOutboundQueue {
|
|
36
|
+
private readonly store;
|
|
37
|
+
private readonly config;
|
|
38
|
+
private handler?;
|
|
39
|
+
/** Whether the processing loop is active. */
|
|
40
|
+
private running;
|
|
41
|
+
/** Number of items currently being processed by the handler. */
|
|
42
|
+
private activeCount;
|
|
43
|
+
/** Handle for the polling interval so we can clear it on stop(). */
|
|
44
|
+
private pollTimer;
|
|
45
|
+
/** Polling interval in milliseconds for the processing loop. */
|
|
46
|
+
private readonly pollIntervalMs;
|
|
47
|
+
/**
|
|
48
|
+
* @param store - Persistence backend for queue items
|
|
49
|
+
* @param config - Gateway configuration (concurrency, retry settings)
|
|
50
|
+
* @param handler - Callback invoked for each dequeued item
|
|
51
|
+
* @param pollIntervalMs - How often to poll the store for ready items (default: 1000ms)
|
|
52
|
+
*/
|
|
53
|
+
constructor(store: IOutboundQueueStore, config: IEmailGatewayConfig, handler?: OutboundQueueHandler | undefined, pollIntervalMs?: number);
|
|
54
|
+
/**
|
|
55
|
+
* Set or replace the delivery handler callback.
|
|
56
|
+
*/
|
|
57
|
+
setHandler(handler: OutboundQueueHandler): void;
|
|
58
|
+
/**
|
|
59
|
+
* Enqueue a message for outbound delivery.
|
|
60
|
+
*
|
|
61
|
+
* Persists the item to the store. Rejects with an error if the store
|
|
62
|
+
* is unavailable (Req 9.5).
|
|
63
|
+
*
|
|
64
|
+
* @param item - The outbound queue item to enqueue
|
|
65
|
+
* @throws Error if the store is unavailable
|
|
66
|
+
*
|
|
67
|
+
* @see Requirement 9.5
|
|
68
|
+
*/
|
|
69
|
+
enqueue(item: IOutboundQueueItem): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Start the processing loop.
|
|
72
|
+
*
|
|
73
|
+
* On startup, resumes processing any previously queued messages that
|
|
74
|
+
* have not exceeded the maximum retry duration (Req 9.2). Then begins
|
|
75
|
+
* polling the store for new items.
|
|
76
|
+
*
|
|
77
|
+
* @see Requirements 9.2, 9.4
|
|
78
|
+
*/
|
|
79
|
+
start(): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Stop the processing loop.
|
|
82
|
+
*
|
|
83
|
+
* Clears the polling timer. In-flight handler invocations are allowed
|
|
84
|
+
* to complete but no new items will be dequeued.
|
|
85
|
+
*/
|
|
86
|
+
stop(): void;
|
|
87
|
+
/**
|
|
88
|
+
* Whether the queue processing loop is currently running.
|
|
89
|
+
*/
|
|
90
|
+
isRunning(): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* The number of items currently being processed (in-flight).
|
|
93
|
+
*/
|
|
94
|
+
getActiveCount(): number;
|
|
95
|
+
/**
|
|
96
|
+
* The current queue depth (delegated to the store).
|
|
97
|
+
*/
|
|
98
|
+
getQueueDepth(): Promise<number>;
|
|
99
|
+
/**
|
|
100
|
+
* Resume processing items that were queued before the last shutdown.
|
|
101
|
+
*
|
|
102
|
+
* Walks the store and expires any items that have exceeded the maximum
|
|
103
|
+
* retry duration, then triggers a processing pass for the rest.
|
|
104
|
+
*
|
|
105
|
+
* @see Requirement 9.2
|
|
106
|
+
*/
|
|
107
|
+
private resumeExistingItems;
|
|
108
|
+
/**
|
|
109
|
+
* Dequeue and process items up to the concurrency limit.
|
|
110
|
+
*
|
|
111
|
+
* Each dequeued item is handed to the handler callback. The concurrency
|
|
112
|
+
* limit (Req 9.4) is enforced by tracking `activeCount`.
|
|
113
|
+
*/
|
|
114
|
+
private processAvailable;
|
|
115
|
+
/**
|
|
116
|
+
* Process a single dequeued item by invoking the handler.
|
|
117
|
+
*
|
|
118
|
+
* If the handler throws, the item is checked against retry limits.
|
|
119
|
+
* If eligible for retry, it is requeued with an exponential back-off
|
|
120
|
+
* delay. If limits are exceeded, it is marked as permanently failed.
|
|
121
|
+
*
|
|
122
|
+
* @see Requirements 3.1, 3.3, 3.4
|
|
123
|
+
*/
|
|
124
|
+
private processItem;
|
|
125
|
+
/**
|
|
126
|
+
* Determine whether a queue item has exceeded the maximum retry duration.
|
|
127
|
+
*
|
|
128
|
+
* @param item - The queue item to check
|
|
129
|
+
* @returns `true` if the item's age exceeds `config.retryMaxDurationMs`
|
|
130
|
+
*
|
|
131
|
+
* @see Requirement 9.2
|
|
132
|
+
*/
|
|
133
|
+
private isExpired;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=outboundQueue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outboundQueue.d.ts","sourceRoot":"","sources":["../../../../../../brightchain-api-lib/src/lib/services/emailGateway/outboundQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAGhE;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,IAAI,EAAE,kBAAkB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE/E;;;;;;;;GAQG;AACH,qBAAa,aAAc,YAAW,cAAc;IAoBhD,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,OAAO,CAAC;IArBlB,6CAA6C;IAC7C,OAAO,CAAC,OAAO,CAAS;IAExB,gEAAgE;IAChE,OAAO,CAAC,WAAW,CAAK;IAExB,oEAAoE;IACpE,OAAO,CAAC,SAAS,CAA+C;IAEhE,gEAAgE;IAChE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IAExC;;;;;OAKG;gBAEgB,KAAK,EAAE,mBAAmB,EAC1B,MAAM,EAAE,mBAAmB,EACpC,OAAO,CAAC,EAAE,oBAAoB,YAAA,EACtC,cAAc,CAAC,EAAE,MAAM;IAOzB;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI;IAI/C;;;;;;;;;;OAUG;IACG,OAAO,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUtD;;;;;;;;OAQG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAe5B;;;;;OAKG;IACH,IAAI,IAAI,IAAI;IAWZ;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,cAAc,IAAI,MAAM;IAIxB;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAMtC;;;;;;;OAOG;YACW,mBAAmB;IAMjC;;;;;OAKG;YACW,gBAAgB;IA6B9B;;;;;;;;OAQG;YACW,WAAW;IAmCzB;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS;CAIlB"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OutboundQueue — persistent FIFO queue with concurrency control for
|
|
4
|
+
* outbound email delivery.
|
|
5
|
+
*
|
|
6
|
+
* Wraps an `IOutboundQueueStore` for persistence and enforces a configurable
|
|
7
|
+
* concurrency limit on simultaneous deliveries. The processing loop dequeues
|
|
8
|
+
* items and passes them to a handler callback.
|
|
9
|
+
*
|
|
10
|
+
* On `start()`, the queue resumes processing any previously queued messages
|
|
11
|
+
* that have not exceeded the maximum retry duration (Req 9.2).
|
|
12
|
+
*
|
|
13
|
+
* @see Requirements 9.1, 9.2, 9.3, 9.4, 9.5
|
|
14
|
+
* @module outboundQueue
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.OutboundQueue = void 0;
|
|
18
|
+
const brightchain_lib_1 = require("@brightchain/brightchain-lib");
|
|
19
|
+
const retryBackoff_1 = require("./retryBackoff");
|
|
20
|
+
/**
|
|
21
|
+
* Persistent FIFO outbound email queue with concurrency control.
|
|
22
|
+
*
|
|
23
|
+
* Implements `IOutboundQueue` so the `EmailGatewayService` orchestrator
|
|
24
|
+
* can enqueue messages without knowing about concurrency or persistence
|
|
25
|
+
* internals.
|
|
26
|
+
*
|
|
27
|
+
* @see Requirements 9.1, 9.2, 9.3, 9.4, 9.5
|
|
28
|
+
*/
|
|
29
|
+
class OutboundQueue {
|
|
30
|
+
store;
|
|
31
|
+
config;
|
|
32
|
+
handler;
|
|
33
|
+
/** Whether the processing loop is active. */
|
|
34
|
+
running = false;
|
|
35
|
+
/** Number of items currently being processed by the handler. */
|
|
36
|
+
activeCount = 0;
|
|
37
|
+
/** Handle for the polling interval so we can clear it on stop(). */
|
|
38
|
+
pollTimer = null;
|
|
39
|
+
/** Polling interval in milliseconds for the processing loop. */
|
|
40
|
+
pollIntervalMs;
|
|
41
|
+
/**
|
|
42
|
+
* @param store - Persistence backend for queue items
|
|
43
|
+
* @param config - Gateway configuration (concurrency, retry settings)
|
|
44
|
+
* @param handler - Callback invoked for each dequeued item
|
|
45
|
+
* @param pollIntervalMs - How often to poll the store for ready items (default: 1000ms)
|
|
46
|
+
*/
|
|
47
|
+
constructor(store, config, handler, pollIntervalMs) {
|
|
48
|
+
this.store = store;
|
|
49
|
+
this.config = config;
|
|
50
|
+
this.handler = handler;
|
|
51
|
+
this.pollIntervalMs = pollIntervalMs ?? 1000;
|
|
52
|
+
}
|
|
53
|
+
// ─── Public API ─────────────────────────────────────────────────────
|
|
54
|
+
/**
|
|
55
|
+
* Set or replace the delivery handler callback.
|
|
56
|
+
*/
|
|
57
|
+
setHandler(handler) {
|
|
58
|
+
this.handler = handler;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Enqueue a message for outbound delivery.
|
|
62
|
+
*
|
|
63
|
+
* Persists the item to the store. Rejects with an error if the store
|
|
64
|
+
* is unavailable (Req 9.5).
|
|
65
|
+
*
|
|
66
|
+
* @param item - The outbound queue item to enqueue
|
|
67
|
+
* @throws Error if the store is unavailable
|
|
68
|
+
*
|
|
69
|
+
* @see Requirement 9.5
|
|
70
|
+
*/
|
|
71
|
+
async enqueue(item) {
|
|
72
|
+
try {
|
|
73
|
+
await this.store.enqueue(item);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
throw new Error(`Outbound queue store unavailable: ${err instanceof Error ? err.message : String(err)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Start the processing loop.
|
|
81
|
+
*
|
|
82
|
+
* On startup, resumes processing any previously queued messages that
|
|
83
|
+
* have not exceeded the maximum retry duration (Req 9.2). Then begins
|
|
84
|
+
* polling the store for new items.
|
|
85
|
+
*
|
|
86
|
+
* @see Requirements 9.2, 9.4
|
|
87
|
+
*/
|
|
88
|
+
async start() {
|
|
89
|
+
if (this.running) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this.running = true;
|
|
93
|
+
// Resume: process any items already in the store that are still valid.
|
|
94
|
+
await this.resumeExistingItems();
|
|
95
|
+
// Start the polling loop.
|
|
96
|
+
this.pollTimer = setInterval(() => {
|
|
97
|
+
void this.processAvailable();
|
|
98
|
+
}, this.pollIntervalMs);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Stop the processing loop.
|
|
102
|
+
*
|
|
103
|
+
* Clears the polling timer. In-flight handler invocations are allowed
|
|
104
|
+
* to complete but no new items will be dequeued.
|
|
105
|
+
*/
|
|
106
|
+
stop() {
|
|
107
|
+
if (!this.running) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
this.running = false;
|
|
111
|
+
if (this.pollTimer !== null) {
|
|
112
|
+
clearInterval(this.pollTimer);
|
|
113
|
+
this.pollTimer = null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Whether the queue processing loop is currently running.
|
|
118
|
+
*/
|
|
119
|
+
isRunning() {
|
|
120
|
+
return this.running;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The number of items currently being processed (in-flight).
|
|
124
|
+
*/
|
|
125
|
+
getActiveCount() {
|
|
126
|
+
return this.activeCount;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The current queue depth (delegated to the store).
|
|
130
|
+
*/
|
|
131
|
+
async getQueueDepth() {
|
|
132
|
+
return this.store.getQueueDepth();
|
|
133
|
+
}
|
|
134
|
+
// ─── Internal Processing ────────────────────────────────────────────
|
|
135
|
+
/**
|
|
136
|
+
* Resume processing items that were queued before the last shutdown.
|
|
137
|
+
*
|
|
138
|
+
* Walks the store and expires any items that have exceeded the maximum
|
|
139
|
+
* retry duration, then triggers a processing pass for the rest.
|
|
140
|
+
*
|
|
141
|
+
* @see Requirement 9.2
|
|
142
|
+
*/
|
|
143
|
+
async resumeExistingItems() {
|
|
144
|
+
// We process available items which will naturally skip expired ones
|
|
145
|
+
// via the dequeue + expiry check in processItem.
|
|
146
|
+
await this.processAvailable();
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Dequeue and process items up to the concurrency limit.
|
|
150
|
+
*
|
|
151
|
+
* Each dequeued item is handed to the handler callback. The concurrency
|
|
152
|
+
* limit (Req 9.4) is enforced by tracking `activeCount`.
|
|
153
|
+
*/
|
|
154
|
+
async processAvailable() {
|
|
155
|
+
if (!this.running || !this.handler) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
while (this.activeCount < this.config.queueConcurrency && this.running) {
|
|
159
|
+
const item = await this.store.dequeue();
|
|
160
|
+
if (!item) {
|
|
161
|
+
// No more items ready for processing.
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
// Check if the item has exceeded the max retry duration (Req 9.2).
|
|
165
|
+
if (this.isExpired(item)) {
|
|
166
|
+
await this.store.markFailed(item.messageId, 'Maximum retry duration exceeded');
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
// Process the item with concurrency tracking.
|
|
170
|
+
this.activeCount++;
|
|
171
|
+
void this.processItem(item).finally(() => {
|
|
172
|
+
this.activeCount--;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Process a single dequeued item by invoking the handler.
|
|
178
|
+
*
|
|
179
|
+
* If the handler throws, the item is checked against retry limits.
|
|
180
|
+
* If eligible for retry, it is requeued with an exponential back-off
|
|
181
|
+
* delay. If limits are exceeded, it is marked as permanently failed.
|
|
182
|
+
*
|
|
183
|
+
* @see Requirements 3.1, 3.3, 3.4
|
|
184
|
+
*/
|
|
185
|
+
async processItem(item) {
|
|
186
|
+
if (!this.handler) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
await this.handler(item);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// Handler failed — check if the item is eligible for retry.
|
|
194
|
+
const nextRetryCount = item.retryCount + 1;
|
|
195
|
+
const retryCandidate = { retryCount: nextRetryCount, enqueuedAt: item.enqueuedAt };
|
|
196
|
+
if ((0, retryBackoff_1.shouldRetry)(retryCandidate, this.config)) {
|
|
197
|
+
// Eligible for retry — requeue with back-off delay.
|
|
198
|
+
const nextAttemptAt = (0, retryBackoff_1.computeNextAttemptAt)(this.config.retryBaseIntervalMs, item.retryCount);
|
|
199
|
+
const requeued = {
|
|
200
|
+
...item,
|
|
201
|
+
status: brightchain_lib_1.OutboundDeliveryStatus.Queued,
|
|
202
|
+
retryCount: nextRetryCount,
|
|
203
|
+
nextAttemptAt,
|
|
204
|
+
};
|
|
205
|
+
await this.store.requeue(requeued);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
// Retry limits exceeded — mark permanently failed (Req 3.4).
|
|
209
|
+
await this.store.markFailed(item.messageId, `Retry limits exceeded (retryCount=${nextRetryCount}, maxCount=${this.config.retryMaxCount}, maxDuration=${this.config.retryMaxDurationMs}ms)`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Determine whether a queue item has exceeded the maximum retry duration.
|
|
215
|
+
*
|
|
216
|
+
* @param item - The queue item to check
|
|
217
|
+
* @returns `true` if the item's age exceeds `config.retryMaxDurationMs`
|
|
218
|
+
*
|
|
219
|
+
* @see Requirement 9.2
|
|
220
|
+
*/
|
|
221
|
+
isExpired(item) {
|
|
222
|
+
const age = Date.now() - item.enqueuedAt.getTime();
|
|
223
|
+
return age > this.config.retryMaxDurationMs;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
exports.OutboundQueue = OutboundQueue;
|
|
227
|
+
//# sourceMappingURL=outboundQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outboundQueue.js","sourceRoot":"","sources":["../../../../../../brightchain-api-lib/src/lib/services/emailGateway/outboundQueue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outbound Queue Store — persistence interface and in-memory implementation
|
|
3
|
+
* for the outbound email delivery queue.
|
|
4
|
+
*
|
|
5
|
+
* The `IOutboundQueueStore` interface defines the contract for queue persistence,
|
|
6
|
+
* allowing different backends (in-memory for testing, database/filesystem for
|
|
7
|
+
* production) to be swapped without changing the queue logic.
|
|
8
|
+
*
|
|
9
|
+
* The `InMemoryOutboundQueueStore` provides a FIFO-ordered, Map-backed
|
|
10
|
+
* implementation suitable for unit/integration testing.
|
|
11
|
+
*
|
|
12
|
+
* @see Requirements 9.1, 9.3, 9.4
|
|
13
|
+
* @module outboundQueueStore
|
|
14
|
+
*/
|
|
15
|
+
import type { IOutboundQueueItem } from './emailGatewayService';
|
|
16
|
+
/**
|
|
17
|
+
* Persistence interface for the outbound email delivery queue.
|
|
18
|
+
*
|
|
19
|
+
* Implementations must maintain FIFO ordering (Req 9.3) and support
|
|
20
|
+
* concurrent access patterns (Req 9.4). The store is responsible only
|
|
21
|
+
* for persistence — concurrency limiting and processing loops are
|
|
22
|
+
* handled by the `OutboundQueue` class (task 4.2).
|
|
23
|
+
*
|
|
24
|
+
* @see Requirements 9.1, 9.3, 9.4
|
|
25
|
+
*/
|
|
26
|
+
export interface IOutboundQueueStore {
|
|
27
|
+
/**
|
|
28
|
+
* Add a message to the end of the queue.
|
|
29
|
+
*
|
|
30
|
+
* @param item - The outbound queue item to enqueue
|
|
31
|
+
* @throws If the store is unavailable (Req 9.5)
|
|
32
|
+
*/
|
|
33
|
+
enqueue(item: IOutboundQueueItem): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Remove and return the next item from the front of the queue
|
|
36
|
+
* that is in `Queued` status and ready for delivery.
|
|
37
|
+
*
|
|
38
|
+
* @returns The next queue item, or `undefined` if the queue is empty
|
|
39
|
+
* or no items are ready
|
|
40
|
+
*/
|
|
41
|
+
dequeue(): Promise<IOutboundQueueItem | undefined>;
|
|
42
|
+
/**
|
|
43
|
+
* Return the next item from the front of the queue without removing it.
|
|
44
|
+
*
|
|
45
|
+
* @returns The next queue item, or `undefined` if the queue is empty
|
|
46
|
+
*/
|
|
47
|
+
peek(): Promise<IOutboundQueueItem | undefined>;
|
|
48
|
+
/**
|
|
49
|
+
* Mark a message as successfully delivered and remove it from the queue.
|
|
50
|
+
*
|
|
51
|
+
* @param messageId - The RFC 5322 Message-ID of the completed message
|
|
52
|
+
*/
|
|
53
|
+
markCompleted(messageId: string): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Mark a message as permanently failed and remove it from the active queue.
|
|
56
|
+
*
|
|
57
|
+
* @param messageId - The RFC 5322 Message-ID of the failed message
|
|
58
|
+
* @param reason - Human-readable failure reason
|
|
59
|
+
*/
|
|
60
|
+
markFailed(messageId: string, reason: string): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Re-enqueue a message for retry with updated retry count and status.
|
|
63
|
+
*
|
|
64
|
+
* The item is placed at the end of the queue to maintain FIFO fairness
|
|
65
|
+
* among messages at the same priority level (Req 9.3).
|
|
66
|
+
*
|
|
67
|
+
* @param item - The updated queue item with incremented retryCount
|
|
68
|
+
*/
|
|
69
|
+
requeue(item: IOutboundQueueItem): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Return the current number of items in the queue.
|
|
72
|
+
*
|
|
73
|
+
* @returns The queue depth (number of pending items)
|
|
74
|
+
*/
|
|
75
|
+
getQueueDepth(): Promise<number>;
|
|
76
|
+
/**
|
|
77
|
+
* Retrieve a queue item by its Message-ID.
|
|
78
|
+
*
|
|
79
|
+
* @param messageId - The RFC 5322 Message-ID to look up
|
|
80
|
+
* @returns The queue item, or `undefined` if not found
|
|
81
|
+
*/
|
|
82
|
+
getByMessageId(messageId: string): Promise<IOutboundQueueItem | undefined>;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* In-memory implementation of `IOutboundQueueStore` for testing.
|
|
86
|
+
*
|
|
87
|
+
* Uses a `Map` keyed by messageId for O(1) lookups and an insertion-ordered
|
|
88
|
+
* array (`queue`) to maintain strict FIFO ordering (Req 9.3).
|
|
89
|
+
*
|
|
90
|
+
* This implementation is NOT suitable for production — it does not survive
|
|
91
|
+
* process restarts (Req 9.1 requires durable storage). Use a database-backed
|
|
92
|
+
* or filesystem-backed implementation for production deployments.
|
|
93
|
+
*
|
|
94
|
+
* @see Requirements 9.1, 9.3, 9.4
|
|
95
|
+
*/
|
|
96
|
+
export declare class InMemoryOutboundQueueStore implements IOutboundQueueStore {
|
|
97
|
+
/** Ordered list of message IDs representing FIFO queue order. */
|
|
98
|
+
private readonly queue;
|
|
99
|
+
/** Map from messageId → queue item for O(1) lookups. */
|
|
100
|
+
private readonly items;
|
|
101
|
+
enqueue(item: IOutboundQueueItem): Promise<void>;
|
|
102
|
+
dequeue(): Promise<IOutboundQueueItem | undefined>;
|
|
103
|
+
peek(): Promise<IOutboundQueueItem | undefined>;
|
|
104
|
+
markCompleted(messageId: string): Promise<void>;
|
|
105
|
+
markFailed(messageId: string, _reason: string): Promise<void>;
|
|
106
|
+
requeue(item: IOutboundQueueItem): Promise<void>;
|
|
107
|
+
getQueueDepth(): Promise<number>;
|
|
108
|
+
getByMessageId(messageId: string): Promise<IOutboundQueueItem | undefined>;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=outboundQueueStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outboundQueueStore.d.ts","sourceRoot":"","sources":["../../../../../../brightchain-api-lib/src/lib/services/emailGateway/outboundQueueStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,OAAO,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjD;;;;;;OAMG;IACH,OAAO,IAAI,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAC;IAEnD;;;;OAIG;IACH,IAAI,IAAI,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAC;IAEhD;;;;OAIG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhD;;;;;OAKG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7D;;;;;;;OAOG;IACH,OAAO,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjD;;;;OAIG;IACH,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAEjC;;;;;OAKG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAC;CAC5E;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,0BAA2B,YAAW,mBAAmB;IACpE,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IAEtC,wDAAwD;IACxD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA8C;IAE9D,OAAO,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAShD,OAAO,IAAI,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;IAoClD,IAAI,IAAI,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;IAW/C,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY/C,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY7D,OAAO,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUhD,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAWhC,cAAc,CAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;CAI3C"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Outbound Queue Store — persistence interface and in-memory implementation
|
|
4
|
+
* for the outbound email delivery queue.
|
|
5
|
+
*
|
|
6
|
+
* The `IOutboundQueueStore` interface defines the contract for queue persistence,
|
|
7
|
+
* allowing different backends (in-memory for testing, database/filesystem for
|
|
8
|
+
* production) to be swapped without changing the queue logic.
|
|
9
|
+
*
|
|
10
|
+
* The `InMemoryOutboundQueueStore` provides a FIFO-ordered, Map-backed
|
|
11
|
+
* implementation suitable for unit/integration testing.
|
|
12
|
+
*
|
|
13
|
+
* @see Requirements 9.1, 9.3, 9.4
|
|
14
|
+
* @module outboundQueueStore
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.InMemoryOutboundQueueStore = void 0;
|
|
18
|
+
const brightchain_lib_1 = require("@brightchain/brightchain-lib");
|
|
19
|
+
/**
|
|
20
|
+
* In-memory implementation of `IOutboundQueueStore` for testing.
|
|
21
|
+
*
|
|
22
|
+
* Uses a `Map` keyed by messageId for O(1) lookups and an insertion-ordered
|
|
23
|
+
* array (`queue`) to maintain strict FIFO ordering (Req 9.3).
|
|
24
|
+
*
|
|
25
|
+
* This implementation is NOT suitable for production — it does not survive
|
|
26
|
+
* process restarts (Req 9.1 requires durable storage). Use a database-backed
|
|
27
|
+
* or filesystem-backed implementation for production deployments.
|
|
28
|
+
*
|
|
29
|
+
* @see Requirements 9.1, 9.3, 9.4
|
|
30
|
+
*/
|
|
31
|
+
class InMemoryOutboundQueueStore {
|
|
32
|
+
/** Ordered list of message IDs representing FIFO queue order. */
|
|
33
|
+
queue = [];
|
|
34
|
+
/** Map from messageId → queue item for O(1) lookups. */
|
|
35
|
+
items = new Map();
|
|
36
|
+
async enqueue(item) {
|
|
37
|
+
// If an item with the same messageId already exists, replace it
|
|
38
|
+
// (this can happen on requeue after status update).
|
|
39
|
+
if (!this.items.has(item.messageId)) {
|
|
40
|
+
this.queue.push(item.messageId);
|
|
41
|
+
}
|
|
42
|
+
this.items.set(item.messageId, { ...item });
|
|
43
|
+
}
|
|
44
|
+
async dequeue() {
|
|
45
|
+
// Walk the queue front-to-back to find the first item in Queued status
|
|
46
|
+
// that is ready for processing (nextAttemptAt <= now or not set).
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
for (let i = 0; i < this.queue.length; i++) {
|
|
49
|
+
const messageId = this.queue[i];
|
|
50
|
+
const item = this.items.get(messageId);
|
|
51
|
+
if (!item) {
|
|
52
|
+
// Stale entry — item was removed (markCompleted/markFailed).
|
|
53
|
+
this.queue.splice(i, 1);
|
|
54
|
+
i--;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (item.status !== brightchain_lib_1.OutboundDeliveryStatus.Queued) {
|
|
58
|
+
// Item exists but is not in Queued status — remove stale entry.
|
|
59
|
+
this.queue.splice(i, 1);
|
|
60
|
+
i--;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
// Respect nextAttemptAt: skip items that are not yet ready.
|
|
64
|
+
if (item.nextAttemptAt && item.nextAttemptAt.getTime() > now) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
// Item is ready — remove from queue and return.
|
|
68
|
+
this.queue.splice(i, 1);
|
|
69
|
+
this.items.delete(messageId);
|
|
70
|
+
return { ...item };
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
async peek() {
|
|
75
|
+
// Walk the queue front-to-back to find the first item in Queued status.
|
|
76
|
+
for (const messageId of this.queue) {
|
|
77
|
+
const item = this.items.get(messageId);
|
|
78
|
+
if (item && item.status === brightchain_lib_1.OutboundDeliveryStatus.Queued) {
|
|
79
|
+
return { ...item };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
async markCompleted(messageId) {
|
|
85
|
+
const item = this.items.get(messageId);
|
|
86
|
+
if (item) {
|
|
87
|
+
item.status = brightchain_lib_1.OutboundDeliveryStatus.Delivered;
|
|
88
|
+
this.items.delete(messageId);
|
|
89
|
+
const idx = this.queue.indexOf(messageId);
|
|
90
|
+
if (idx !== -1) {
|
|
91
|
+
this.queue.splice(idx, 1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async markFailed(messageId, _reason) {
|
|
96
|
+
const item = this.items.get(messageId);
|
|
97
|
+
if (item) {
|
|
98
|
+
item.status = brightchain_lib_1.OutboundDeliveryStatus.PermanentFailure;
|
|
99
|
+
this.items.delete(messageId);
|
|
100
|
+
const idx = this.queue.indexOf(messageId);
|
|
101
|
+
if (idx !== -1) {
|
|
102
|
+
this.queue.splice(idx, 1);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async requeue(item) {
|
|
107
|
+
// Remove old position if present, then append to end for FIFO fairness.
|
|
108
|
+
const existingIdx = this.queue.indexOf(item.messageId);
|
|
109
|
+
if (existingIdx !== -1) {
|
|
110
|
+
this.queue.splice(existingIdx, 1);
|
|
111
|
+
}
|
|
112
|
+
this.queue.push(item.messageId);
|
|
113
|
+
this.items.set(item.messageId, { ...item });
|
|
114
|
+
}
|
|
115
|
+
async getQueueDepth() {
|
|
116
|
+
// Count only items that are still in the items map (not stale references).
|
|
117
|
+
let count = 0;
|
|
118
|
+
for (const messageId of this.queue) {
|
|
119
|
+
if (this.items.has(messageId)) {
|
|
120
|
+
count++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return count;
|
|
124
|
+
}
|
|
125
|
+
async getByMessageId(messageId) {
|
|
126
|
+
const item = this.items.get(messageId);
|
|
127
|
+
return item ? { ...item } : undefined;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
exports.InMemoryOutboundQueueStore = InMemoryOutboundQueueStore;
|
|
131
|
+
//# sourceMappingURL=outboundQueueStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outboundQueueStore.js","sourceRoot":"","sources":["../../../../../../brightchain-api-lib/src/lib/services/emailGateway/outboundQueueStore.ts"],"names":[],"mappings":"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"}
|