@zdavison/matador 4.0.3 → 4.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.
@@ -36,8 +36,10 @@ export interface ProcessResult {
36
36
  * Handles the complete message lifecycle:
37
37
  * 1. Decode envelope from raw bytes
38
38
  * 2. Lookup subscriber from schema
39
- * 3. Execute subscriber callback with hooks
40
- * 4. Handle success/failure with retry policy
39
+ * 3. Pre-execution poison check (retry policy, before running the callback)
40
+ * 4. Execute subscriber callback with hooks
41
+ * 5. Handle success
42
+ * 6. Handle failure with retry policy
41
43
  */
42
44
  export declare class ProcessingPipeline {
43
45
  private readonly transport;
@@ -52,6 +54,32 @@ export declare class ProcessingPipeline {
52
54
  * Processes a raw message from the transport.
53
55
  */
54
56
  process(rawMessage: Uint8Array, receipt: MessageReceipt): Promise<ProcessResult>;
57
+ /**
58
+ * Runs the retry policy's pre-processing check.
59
+ *
60
+ * This returns a terminal `ProcessResult` if the message should be skipped (dead-lettered/discarded) without ever invoking the callback,
61
+ * or a success result if shouldProcess passes and we should continue processing the message.
62
+ * @param envelope - The message envelope
63
+ * @param subscriberDef - The subscriber definition
64
+ * @param isResumable - Whether the subscriber is resumable
65
+ * @param receipt - The message receipt
66
+ * @param startTime - The start time of the processing
67
+ * @returns A `ProcessResult` indicating if we should continue with processing the message, or fail it preemptively
68
+ */
69
+ private handleShouldProcess;
70
+ /**
71
+ * Performs failure handling and cleanup based on retry decision (e.g. dead-letter).
72
+ *
73
+ * @param error - The error from the processing attempt
74
+ * @param decision - The decision from executing the appropriate retry policy check
75
+ * @param envelope - The message envelope
76
+ * @param subscriberDef - The subscriber definition
77
+ * @param isResumable - Whether the subscriber is resumable
78
+ * @param receipt - The message receipt
79
+ * @param durationMs - The duration of processing
80
+ * @returns A `ProcessResult` indicating failure
81
+ */
82
+ private handleFailure;
55
83
  private handleRetryDecision;
56
84
  private sendToDeadLetter;
57
85
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/pipeline/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE1E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAM/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,KAAK,EAEV,UAAU,EACV,QAAQ,EAER,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAG3B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,0DAA0D;IAC1D,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IACvD,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;gBAE5B,MAAM,EAAE,cAAc;IAUlC;;OAEG;IACG,OAAO,CACX,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC;YA4NX,mBAAmB;YAqCnB,gBAAgB;IAkB9B;;;OAGG;IACH,OAAO,CAAC,yBAAyB;CA+BlC"}
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/pipeline/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE1E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAM/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,KAAK,EAEV,UAAU,EACV,QAAQ,EAER,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAG3B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,0DAA0D;IAC1D,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IACvD,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;IACvD,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;;;GAUG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;gBAE5B,MAAM,EAAE,cAAc;IAUlC;;OAEG;IACG,OAAO,CACX,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC;IAoNzB;;;;;;;;;;;OAWG;YACW,mBAAmB;IAiCjC;;;;;;;;;;;OAWG;YACW,aAAa;YA4Cb,mBAAmB;YAqCnB,gBAAgB;IAkB9B;;;OAGG;IACH,OAAO,CAAC,yBAAyB;CA+BlC"}
@@ -8,8 +8,10 @@ import { isResumableSubscriber } from '../types/index.js';
8
8
  * Handles the complete message lifecycle:
9
9
  * 1. Decode envelope from raw bytes
10
10
  * 2. Lookup subscriber from schema
11
- * 3. Execute subscriber callback with hooks
12
- * 4. Handle success/failure with retry policy
11
+ * 3. Pre-execution poison check (retry policy, before running the callback)
12
+ * 4. Execute subscriber callback with hooks
13
+ * 5. Handle success
14
+ * 6. Handle failure with retry policy
13
15
  */
14
16
  export class ProcessingPipeline {
15
17
  transport;
@@ -87,13 +89,22 @@ export class ProcessingPipeline {
87
89
  durationMs: performance.now() - startTime,
88
90
  };
89
91
  }
90
- // 3. Execute subscriber callback with hooks
92
+ const isResumable = isResumableSubscriber(subscriber);
93
+ // 3. Pre-execution poison check, based on delivery metadata alone.
94
+ //
95
+ // Catches a message that's already known-poisoned (e.g. delivery count at/above the threshold)
96
+ // before running the callback
97
+ const shouldProcessResult = await this.handleShouldProcess(envelope, subscriberDef, isResumable, receipt, startTime);
98
+ // If the message is poisoned, return the result immediately
99
+ if (!shouldProcessResult.success) {
100
+ return shouldProcessResult;
101
+ }
102
+ // 4. Execute subscriber callback with hooks
91
103
  let result;
92
104
  let error;
93
105
  let context;
94
- // For resumable subscribers, load existing checkpoint and create context
95
- const isResumable = isResumableSubscriber(subscriber);
96
106
  let existingCheckpoint;
107
+ // For resumable subscribers, load existing checkpoint and create context
97
108
  if (isResumable) {
98
109
  existingCheckpoint = await this.checkpointStore.get(envelope.id);
99
110
  if (existingCheckpoint) {
@@ -145,7 +156,7 @@ export class ProcessingPipeline {
145
156
  }
146
157
  });
147
158
  const durationMs = performance.now() - startTime;
148
- // 4. Handle success
159
+ // 5. Handle success
149
160
  if (!error) {
150
161
  // Clear checkpoint on success for resumable subscribers
151
162
  if (context) {
@@ -171,19 +182,60 @@ export class ProcessingPipeline {
171
182
  durationMs,
172
183
  };
173
184
  }
174
- // 5. Handle failure - consult retry policy
185
+ // 6. Handle failure - consult retry policy
175
186
  const decision = this.retryPolicy.shouldRetry({
176
187
  envelope,
177
188
  error,
178
189
  subscriber: subscriberDef,
179
190
  receipt,
180
191
  });
192
+ return await this.handleFailure(error, decision, envelope, subscriberDef, isResumable, receipt, durationMs);
193
+ }
194
+ /**
195
+ * Runs the retry policy's pre-processing check.
196
+ *
197
+ * This returns a terminal `ProcessResult` if the message should be skipped (dead-lettered/discarded) without ever invoking the callback,
198
+ * or a success result if shouldProcess passes and we should continue processing the message.
199
+ * @param envelope - The message envelope
200
+ * @param subscriberDef - The subscriber definition
201
+ * @param isResumable - Whether the subscriber is resumable
202
+ * @param receipt - The message receipt
203
+ * @param startTime - The start time of the processing
204
+ * @returns A `ProcessResult` indicating if we should continue with processing the message, or fail it preemptively
205
+ */
206
+ async handleShouldProcess(envelope, subscriberDef, isResumable, receipt, startTime) {
207
+ const decision = this.retryPolicy.shouldProcess({ envelope, receipt });
208
+ // If shouldProcess passes, we can proceed with normal processing
209
+ if (decision.action === 'process') {
210
+ return {
211
+ success: true,
212
+ durationMs: 0,
213
+ };
214
+ }
215
+ // Otherwise, the message needs to be handled accordingly (e.g. dead-lettered or discarded)
216
+ const durationMs = performance.now() - startTime;
217
+ const error = new Error(decision.reason);
218
+ return await this.handleFailure(error, decision, envelope, subscriberDef, isResumable, receipt, durationMs);
219
+ }
220
+ /**
221
+ * Performs failure handling and cleanup based on retry decision (e.g. dead-letter).
222
+ *
223
+ * @param error - The error from the processing attempt
224
+ * @param decision - The decision from executing the appropriate retry policy check
225
+ * @param envelope - The message envelope
226
+ * @param subscriberDef - The subscriber definition
227
+ * @param isResumable - Whether the subscriber is resumable
228
+ * @param receipt - The message receipt
229
+ * @param durationMs - The duration of processing
230
+ * @returns A `ProcessResult` indicating failure
231
+ */
232
+ async handleFailure(error, decision, envelope, subscriberDef, isResumable, receipt, durationMs) {
181
233
  // Update envelope with error info
182
234
  envelope.docket.lastError = error.message;
183
235
  envelope.docket.firstError ??= error.message;
184
236
  // Clear checkpoint on dead-letter (terminal state)
185
- if (decision.action === 'dead-letter' && context) {
186
- await context.clear();
237
+ if (decision.action === 'dead-letter' && isResumable) {
238
+ await this.checkpointStore.delete(envelope.id);
187
239
  await this.hooks.onCheckpointCleared?.({
188
240
  envelope,
189
241
  subscriber: subscriberDef,
@@ -846,6 +846,126 @@ describe('ProcessingPipeline', () => {
846
846
  expect(completeMock).toHaveBeenCalledWith(receipt);
847
847
  });
848
848
  });
849
+ describe('pre-execution poison check', () => {
850
+ it('should call retryPolicy.shouldProcess before invoking the callback', async () => {
851
+ const envelope = createTestEnvelope();
852
+ const callbackMock = mock(async () => { });
853
+ const shouldProcessMock = mock(() => ({ action: 'process' }));
854
+ const config = createMockConfig({
855
+ codec: {
856
+ decode: mock(() => envelope),
857
+ },
858
+ schema: {
859
+ getSubscriberDefinition: mock(() => createSubscriberDefinition()),
860
+ getExecutableSubscriber: mock(() => ({
861
+ name: 'test-subscriber',
862
+ description: 'Test subscriber',
863
+ idempotent: 'unknown',
864
+ callback: callbackMock,
865
+ })),
866
+ },
867
+ retryPolicy: {
868
+ shouldRetry: mock(() => ({ action: 'discard', reason: '' })),
869
+ getDelay: mock(() => 1000),
870
+ shouldProcess: shouldProcessMock,
871
+ },
872
+ });
873
+ const pipeline = new ProcessingPipeline(config);
874
+ const receipt = createReceipt({ deliveryCount: 1 });
875
+ await pipeline.process(new Uint8Array(), receipt);
876
+ expect(shouldProcessMock).toHaveBeenCalledWith({ envelope, receipt });
877
+ expect(callbackMock).toHaveBeenCalled();
878
+ });
879
+ it('should dead-letter without invoking the callback when shouldProcess flags the message as poisoned', async () => {
880
+ const envelope = createTestEnvelope();
881
+ const callbackMock = mock(async () => { });
882
+ const sendToDeadLetterMock = mock(async () => { });
883
+ const onWorkerErrorMock = mock(async () => { });
884
+ const config = createMockConfig({
885
+ codec: {
886
+ decode: mock(() => envelope),
887
+ },
888
+ schema: {
889
+ getSubscriberDefinition: mock(() => createSubscriberDefinition()),
890
+ getExecutableSubscriber: mock(() => ({
891
+ name: 'test-subscriber',
892
+ description: 'Test subscriber',
893
+ idempotent: 'unknown',
894
+ callback: callbackMock,
895
+ })),
896
+ },
897
+ retryPolicy: {
898
+ shouldRetry: mock(() => ({ action: 'discard', reason: '' })),
899
+ getDelay: mock(() => 1000),
900
+ shouldProcess: mock(() => ({
901
+ action: 'dead-letter',
902
+ queue: 'undeliverable',
903
+ reason: 'Message delivered 5 times (max: 5). Possible poison message.',
904
+ })),
905
+ },
906
+ transport: {
907
+ sendToDeadLetter: sendToDeadLetterMock,
908
+ },
909
+ hooks: {
910
+ onWorkerError: onWorkerErrorMock,
911
+ },
912
+ });
913
+ const pipeline = new ProcessingPipeline(config);
914
+ const receipt = createReceipt({ deliveryCount: 5 });
915
+ const result = await pipeline.process(new Uint8Array(), receipt);
916
+ // The callback must never run for an already-poisoned message.
917
+ expect(callbackMock).not.toHaveBeenCalled();
918
+ expect(sendToDeadLetterMock).toHaveBeenCalledWith(receipt, 'undeliverable', expect.objectContaining({ id: envelope.id }), expect.stringContaining('Possible poison message'));
919
+ expect(onWorkerErrorMock).toHaveBeenCalledWith(expect.objectContaining({
920
+ envelope,
921
+ error: expect.any(Error),
922
+ decision: expect.objectContaining({ action: 'dead-letter' }),
923
+ }));
924
+ expect(result.success).toBe(false);
925
+ expect(result.decision).toEqual(expect.objectContaining({ action: 'dead-letter' }));
926
+ });
927
+ it('should clear the checkpoint before dead-lettering a poisoned resumable subscriber', async () => {
928
+ const envelope = createTestEnvelope();
929
+ const callbackMock = mock(async () => { });
930
+ const deleteMock = mock(async () => { });
931
+ const config = createMockConfig({
932
+ codec: {
933
+ decode: mock(() => envelope),
934
+ },
935
+ schema: {
936
+ getSubscriberDefinition: mock(() => createSubscriberDefinition()),
937
+ getExecutableSubscriber: mock(() => ({
938
+ name: 'test-subscriber',
939
+ description: 'Test subscriber',
940
+ idempotent: 'resumable',
941
+ callback: callbackMock,
942
+ })),
943
+ },
944
+ retryPolicy: {
945
+ shouldRetry: mock(() => ({ action: 'discard', reason: '' })),
946
+ getDelay: mock(() => 1000),
947
+ shouldProcess: mock(() => ({
948
+ action: 'dead-letter',
949
+ queue: 'undeliverable',
950
+ reason: 'poisoned',
951
+ })),
952
+ },
953
+ });
954
+ const pipelineConfig = {
955
+ ...config,
956
+ checkpointStore: {
957
+ get: mock(async () => undefined),
958
+ set: mock(async () => { }),
959
+ delete: deleteMock,
960
+ },
961
+ };
962
+ const pipeline = new ProcessingPipeline(pipelineConfig);
963
+ const receipt = createReceipt({ deliveryCount: 5 });
964
+ await pipeline.process(new Uint8Array(), receipt);
965
+ expect(callbackMock).not.toHaveBeenCalled();
966
+ expect(deleteMock).toHaveBeenCalledWith(envelope.id);
967
+ });
968
+ });
849
969
  describe('process result', () => {
850
970
  it('should return success result with duration', async () => {
851
971
  const envelope = createTestEnvelope();
@@ -1059,6 +1179,9 @@ function createMockConfig(overrides = {}) {
1059
1179
  const defaultRetryPolicy = isFullRetryPolicy(overrides.retryPolicy)
1060
1180
  ? overrides.retryPolicy
1061
1181
  : {
1182
+ shouldProcess: mock(() => ({
1183
+ action: 'process',
1184
+ })),
1062
1185
  shouldRetry: mock(() => ({
1063
1186
  action: 'discard',
1064
1187
  reason: 'default',
@@ -1,4 +1,4 @@
1
- export type { RetryContext, RetryDecision, RetryPolicy } from './policy.js';
1
+ export type { ProcessContext, ProcessDecision, RetryContext, RetryDecision, RetryPolicy, } from './policy.js';
2
2
  export type { StandardRetryPolicyConfig } from './standard-policy.js';
3
3
  export { defaultRetryConfig, StandardRetryPolicy } from './standard-policy.js';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/retry/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE5E,YAAY,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/retry/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,cAAc,EACd,eAAe,EACf,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -27,10 +27,35 @@ export type RetryDecision = {
27
27
  readonly action: 'discard';
28
28
  readonly reason: string;
29
29
  };
30
+ /**
31
+ * Decision returned by pre-processing check
32
+ */
33
+ export type ProcessDecision = {
34
+ action: 'process';
35
+ } | Extract<RetryDecision, {
36
+ action: 'dead-letter' | 'discard';
37
+ }>;
38
+ /**
39
+ * Context provided to retry policy for a pre-processing check, i.e. before the
40
+ * subscriber callback has run and before any error exists.
41
+ */
42
+ export interface ProcessContext {
43
+ /** The message envelope */
44
+ readonly envelope: Envelope;
45
+ /** Message receipt with delivery information */
46
+ readonly receipt: MessageReceipt;
47
+ }
30
48
  /**
31
49
  * Interface for retry policies.
32
50
  */
33
51
  export interface RetryPolicy {
52
+ /**
53
+ * Check if the message should be dead-lettered or discarded before the subscriber callback is invoked.
54
+ *
55
+ * Return a 'process' decision to proceed with normal processing; otherwise return a decision to handle the
56
+ * message accordingly (e.g. dead-letter or discard).
57
+ */
58
+ shouldProcess(context: ProcessContext): ProcessDecision;
34
59
  /**
35
60
  * Determines what to do with a failed message.
36
61
  */
@@ -1 +1 @@
1
- {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/retry/policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAExE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,2BAA2B;IAC3B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAE5B,wCAAwC;IACxC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAEtB,gCAAgC;IAChC,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAC;IAE1C,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpD;IACE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GACD;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,aAAa,CAAC;IAElD;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAAC;CACzC"}
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/retry/policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAExE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,2BAA2B;IAC3B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAE5B,wCAAwC;IACxC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAEtB,gCAAgC;IAChC,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAC;IAE1C,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpD;IACE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GACD;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D;;GAEG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB,OAAO,CAAC,aAAa,EAAE;IAAE,MAAM,EAAE,aAAa,GAAG,SAAS,CAAA;CAAE,CAAC,CAAC;AAElE;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,2BAA2B;IAC3B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAE5B,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,eAAe,CAAC;IAExD;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,aAAa,CAAC;IAElD;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAAC;CACzC"}
@@ -1,4 +1,4 @@
1
- import type { RetryContext, RetryDecision, RetryPolicy } from './policy.js';
1
+ import type { ProcessContext, ProcessDecision, RetryContext, RetryDecision, RetryPolicy } from './policy.js';
2
2
  /**
3
3
  * Configuration for the standard retry policy.
4
4
  */
@@ -38,7 +38,25 @@ export declare const defaultRetryConfig: StandardRetryPolicyConfig;
38
38
  export declare class StandardRetryPolicy implements RetryPolicy {
39
39
  private readonly config;
40
40
  constructor(config?: Partial<StandardRetryPolicyConfig>);
41
+ /**
42
+ * Check if the message should be dead-lettered before the subscriber callback is invoked
43
+ *
44
+ * Uses the same delivery-count threshold as `shouldRetry` poison check, so an
45
+ * already-poisoned message is dead-lettered without ever running the callback again.
46
+ *
47
+ * @param context - The shouldProcess context.
48
+ * @returns A 'process' decision if the message is not poisoned; a dead-letter decision otherwise.
49
+ */
50
+ shouldProcess(context: ProcessContext): ProcessDecision;
41
51
  shouldRetry(context: RetryContext): RetryDecision;
42
52
  getDelay(context: RetryContext): number;
53
+ /**
54
+ * Checks the native delivery count against the poison threshold
55
+ *
56
+ * @param envelope - The message envelope.
57
+ * @param receipt - The message receipt.
58
+ * @returns A MessageMaybePoisonedError if the message is poisoned; null otherwise
59
+ */
60
+ private checkPoisoned;
43
61
  }
44
62
  //# sourceMappingURL=standard-policy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"standard-policy.d.ts","sourceRoot":"","sources":["../../src/retry/standard-policy.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE5E;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,uDAAuD;IACvD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,yCAAyC;IACzC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IAEnC;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,yBAMhC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,qBAAa,mBAAoB,YAAW,WAAW;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;gBAEvC,MAAM,GAAE,OAAO,CAAC,yBAAyB,CAAM;IAI3D,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,aAAa;IAoFjD,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM;CAMxC"}
1
+ {"version":3,"file":"standard-policy.d.ts","sourceRoot":"","sources":["../../src/retry/standard-policy.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,YAAY,EACZ,aAAa,EACb,WAAW,EACZ,MAAM,aAAa,CAAC;AAErB;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,uDAAuD;IACvD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,yCAAyC;IACzC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IAEnC;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,yBAMhC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,qBAAa,mBAAoB,YAAW,WAAW;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;gBAEvC,MAAM,GAAE,OAAO,CAAC,yBAAyB,CAAM;IAI3D;;;;;;;;OAQG;IACH,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,eAAe;IAiBvD,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,aAAa;IAkFjD,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM;IAOvC;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;CAatB"}
@@ -26,12 +26,36 @@ export class StandardRetryPolicy {
26
26
  constructor(config = {}) {
27
27
  this.config = { ...defaultRetryConfig, ...config };
28
28
  }
29
+ /**
30
+ * Check if the message should be dead-lettered before the subscriber callback is invoked
31
+ *
32
+ * Uses the same delivery-count threshold as `shouldRetry` poison check, so an
33
+ * already-poisoned message is dead-lettered without ever running the callback again.
34
+ *
35
+ * @param context - The shouldProcess context.
36
+ * @returns A 'process' decision if the message is not poisoned; a dead-letter decision otherwise.
37
+ */
38
+ shouldProcess(context) {
39
+ // Poison message detection - prevent crash loops
40
+ const poisonError = this.checkPoisoned(context.envelope, context.receipt);
41
+ // If the message is poisoned, dead-letter it
42
+ if (poisonError) {
43
+ return {
44
+ action: 'dead-letter',
45
+ queue: 'undeliverable',
46
+ reason: poisonError.message,
47
+ };
48
+ }
49
+ // Allow processing to continue
50
+ return { action: 'process' };
51
+ }
29
52
  shouldRetry(context) {
30
53
  const { envelope, error, subscriber, receipt } = context;
31
54
  const errorMessage = error.message;
32
55
  // 1. Poison message detection - prevent crash loops
33
- if (receipt.deliveryCount >= this.config.maxDeliveries) {
34
- const poisonError = new MessageMaybePoisonedError(envelope.id, receipt.deliveryCount, this.config.maxDeliveries);
56
+ const poisonError = this.checkPoisoned(envelope, receipt);
57
+ // If the message is poisoned, dead-letter it
58
+ if (poisonError) {
35
59
  return {
36
60
  action: 'dead-letter',
37
61
  queue: 'undeliverable',
@@ -98,4 +122,17 @@ export class StandardRetryPolicy {
98
122
  const delay = this.config.baseDelay * this.config.backoffMultiplier ** (attempt - 1);
99
123
  return Math.min(delay, this.config.maxDelay);
100
124
  }
125
+ /**
126
+ * Checks the native delivery count against the poison threshold
127
+ *
128
+ * @param envelope - The message envelope.
129
+ * @param receipt - The message receipt.
130
+ * @returns A MessageMaybePoisonedError if the message is poisoned; null otherwise
131
+ */
132
+ checkPoisoned(envelope, receipt) {
133
+ if (receipt.deliveryCount >= this.config.maxDeliveries) {
134
+ return new MessageMaybePoisonedError(envelope.id, receipt.deliveryCount, this.config.maxDeliveries);
135
+ }
136
+ return null;
137
+ }
101
138
  }
@@ -97,6 +97,67 @@ describe('StandardRetryPolicy', () => {
97
97
  assertRetry(decision);
98
98
  expect(decision.delay).toBeDefined();
99
99
  });
100
+ it('should dead-letter as poisoned when delivery count reaches the threshold', () => {
101
+ const policy = new StandardRetryPolicy({ maxDeliveries: 5 });
102
+ const context = createContext(new Error('Generic error'), {
103
+ attemptNumber: 1,
104
+ deliveryCount: 5,
105
+ });
106
+ const decision = policy.shouldRetry(context);
107
+ expect(decision.action).toBe('dead-letter');
108
+ assertDeadLetter(decision);
109
+ expect(decision.queue).toBe('undeliverable');
110
+ expect(decision.reason).toContain('Possible poison message');
111
+ });
112
+ it('should take priority over other rules once poisoned', () => {
113
+ // Even a DontRetry-worthy error should still report as poisoned once
114
+ // delivery count is at/above the threshold, since poison detection
115
+ // runs first.
116
+ const policy = new StandardRetryPolicy({ maxDeliveries: 5 });
117
+ const context = createContext(new DoRetry('Temporary failure'), {
118
+ attemptNumber: 1,
119
+ deliveryCount: 5,
120
+ });
121
+ const decision = policy.shouldRetry(context);
122
+ assertDeadLetter(decision);
123
+ expect(decision.reason).toContain('Possible poison message');
124
+ });
125
+ });
126
+ describe('shouldProcess', () => {
127
+ it('should return pass when delivery count is under the threshold', () => {
128
+ const policy = new StandardRetryPolicy({ maxDeliveries: 5 });
129
+ const { envelope, receipt } = createProcessContext({
130
+ deliveryCount: 4,
131
+ });
132
+ const decision = policy.shouldProcess({ envelope, receipt });
133
+ expect(decision.action).toBe('process');
134
+ });
135
+ it('should dead-letter when delivery count is at/above the threshold, without an error', () => {
136
+ const policy = new StandardRetryPolicy({ maxDeliveries: 5 });
137
+ const { envelope, receipt } = createProcessContext({
138
+ deliveryCount: 5,
139
+ });
140
+ const decision = policy.shouldProcess({ envelope, receipt });
141
+ expect(decision.action).toBe('dead-letter');
142
+ if (decision.action !== 'dead-letter') {
143
+ throw new Error(`Expected dead-letter action, got ${decision.action}`);
144
+ }
145
+ expect(decision.queue).toBe('undeliverable');
146
+ expect(decision.reason).toContain('Possible poison message');
147
+ });
148
+ it('should agree with shouldRetry on the same poisoned receipt', () => {
149
+ // shouldProcess and shouldRetry share the same poison-detection logic, so
150
+ // a message flagged by one must be flagged the same way by the other.
151
+ const policy = new StandardRetryPolicy({ maxDeliveries: 5 });
152
+ const receiptOverrides = { attemptNumber: 1, deliveryCount: 5 };
153
+ const context = createContext(new Error('Some error'), receiptOverrides);
154
+ const processDecision = policy.shouldProcess({
155
+ envelope: context.envelope,
156
+ receipt: context.receipt,
157
+ });
158
+ const shouldRetryDecision = policy.shouldRetry(context);
159
+ expect(processDecision).toEqual(shouldRetryDecision);
160
+ });
100
161
  });
101
162
  describe('getDelay', () => {
102
163
  it('should calculate exponential backoff', () => {
@@ -194,3 +255,7 @@ function createContext(error, receiptOverrides = {}, subscriberOverrides = {}) {
194
255
  subscriber,
195
256
  };
196
257
  }
258
+ function createProcessContext(receiptOverrides = {}) {
259
+ const { envelope, receipt } = createContext(new Error('unused'), receiptOverrides);
260
+ return { envelope, receipt };
261
+ }
@@ -93,6 +93,13 @@ describe('TopologyBuilder', () => {
93
93
  .addQueue('events', { consumerTimeout: -1 });
94
94
  expect(() => builder.build()).toThrow('consumer timeout must be non-negative');
95
95
  });
96
+ it('should allow singleActiveConsumer on its own', () => {
97
+ const topology = TopologyBuilder.create()
98
+ .withNamespace('test')
99
+ .addQueue('events', { singleActiveConsumer: true })
100
+ .build();
101
+ expect(topology.queues[0]?.singleActiveConsumer).toBe(true);
102
+ });
96
103
  });
97
104
  describe('withDeadLetter', () => {
98
105
  it('should configure dead letter settings', () => {
@@ -113,6 +113,20 @@ export interface QueueDefinition {
113
113
  readonly consumerTimeout?: number | undefined;
114
114
  /** Enable priority support if transport allows */
115
115
  readonly priorities?: boolean | undefined;
116
+ /**
117
+ * Restricts the queue to one active consumer at a time.
118
+ *
119
+ * Only RabbitMQ supports this option, with the `x-single-active-consumer`
120
+ * argument. Other transports ignore the option. RabbitMQ also ignores the
121
+ * option when you set a custom `transport.rabbitmq.options` override on the
122
+ * queue.
123
+ *
124
+ * With `concurrency: 1`, RabbitMQ delivers the queue in order, one message
125
+ * at a time, to the active consumer.
126
+ *
127
+ * @default false
128
+ */
129
+ readonly singleActiveConsumer?: boolean | undefined;
116
130
  /**
117
131
  * When true, the queue name is used exactly as provided without any
118
132
  * modification. The namespace prefix will NOT be added, and no other
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/topology/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/topology/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"rabbitmq-transport.d.ts","sourceRoot":"","sources":["../../../src/transport/rabbitmq/rabbitmq-transport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rabbitmq-transport.d.ts","sourceRoot":"","sources":["../../../src/transport/rabbitmq/rabbitmq-transport.ts"],"names":[],"mappings":"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"}
@@ -539,6 +539,9 @@ export class RabbitMQTransport {
539
539
  if (queueDef.consumerTimeout) {
540
540
  queueOptions.arguments['x-consumer-timeout'] = queueDef.consumerTimeout;
541
541
  }
542
+ if (queueDef.singleActiveConsumer) {
543
+ queueOptions.arguments['x-single-active-consumer'] = true;
544
+ }
542
545
  return queueOptions;
543
546
  }
544
547
  async assertWorkQueue(channel, topology, queueDef) {