@zdavison/matador 4.0.3 → 4.2.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.
Files changed (71) hide show
  1. package/dist/core/matador.d.ts.map +1 -1
  2. package/dist/core/matador.js +4 -1
  3. package/dist/index.cjs +156 -16
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/pipeline/pipeline.d.ts +30 -2
  7. package/dist/pipeline/pipeline.d.ts.map +1 -1
  8. package/dist/pipeline/pipeline.js +65 -9
  9. package/dist/retry/index.d.ts +1 -1
  10. package/dist/retry/index.d.ts.map +1 -1
  11. package/dist/retry/policy.d.ts +27 -0
  12. package/dist/retry/policy.d.ts.map +1 -1
  13. package/dist/retry/standard-policy.d.ts +33 -2
  14. package/dist/retry/standard-policy.d.ts.map +1 -1
  15. package/dist/retry/standard-policy.js +73 -9
  16. package/dist/topology/types.d.ts +14 -0
  17. package/dist/topology/types.d.ts.map +1 -1
  18. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -1
  19. package/dist/transport/rabbitmq/rabbitmq-transport.js +3 -0
  20. package/package.json +1 -1
  21. package/dist/checkpoint/context.test.d.ts +0 -2
  22. package/dist/checkpoint/context.test.d.ts.map +0 -1
  23. package/dist/checkpoint/context.test.js +0 -428
  24. package/dist/checkpoint/stores/stores.test.d.ts +0 -2
  25. package/dist/checkpoint/stores/stores.test.d.ts.map +0 -1
  26. package/dist/checkpoint/stores/stores.test.js +0 -146
  27. package/dist/codec/rabbitmq-codec.test.d.ts +0 -2
  28. package/dist/codec/rabbitmq-codec.test.d.ts.map +0 -1
  29. package/dist/codec/rabbitmq-codec.test.js +0 -428
  30. package/dist/core/fanout.test.d.ts +0 -2
  31. package/dist/core/fanout.test.d.ts.map +0 -1
  32. package/dist/core/fanout.test.js +0 -1970
  33. package/dist/core/matador.test.d.ts +0 -2
  34. package/dist/core/matador.test.d.ts.map +0 -1
  35. package/dist/core/matador.test.js +0 -813
  36. package/dist/core/shutdown.test.d.ts +0 -2
  37. package/dist/core/shutdown.test.d.ts.map +0 -1
  38. package/dist/core/shutdown.test.js +0 -599
  39. package/dist/errors/retry-errors.test.d.ts +0 -2
  40. package/dist/errors/retry-errors.test.d.ts.map +0 -1
  41. package/dist/errors/retry-errors.test.js +0 -136
  42. package/dist/pipeline/pipeline.test.d.ts +0 -2
  43. package/dist/pipeline/pipeline.test.d.ts.map +0 -1
  44. package/dist/pipeline/pipeline.test.js +0 -1124
  45. package/dist/retry/standard-policy.test.d.ts +0 -2
  46. package/dist/retry/standard-policy.test.d.ts.map +0 -1
  47. package/dist/retry/standard-policy.test.js +0 -196
  48. package/dist/schema/registry.test.d.ts +0 -2
  49. package/dist/schema/registry.test.d.ts.map +0 -1
  50. package/dist/schema/registry.test.js +0 -278
  51. package/dist/schema/types.test.d.ts +0 -2
  52. package/dist/schema/types.test.d.ts.map +0 -1
  53. package/dist/schema/types.test.js +0 -246
  54. package/dist/topology/builder.test.d.ts +0 -2
  55. package/dist/topology/builder.test.d.ts.map +0 -1
  56. package/dist/topology/builder.test.js +0 -547
  57. package/dist/transport/local/local-transport.test.d.ts +0 -2
  58. package/dist/transport/local/local-transport.test.d.ts.map +0 -1
  59. package/dist/transport/local/local-transport.test.js +0 -232
  60. package/dist/transport/multi/multi-transport.test.d.ts +0 -2
  61. package/dist/transport/multi/multi-transport.test.d.ts.map +0 -1
  62. package/dist/transport/multi/multi-transport.test.js +0 -322
  63. package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.d.ts +0 -2
  64. package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.d.ts.map +0 -1
  65. package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.js +0 -149
  66. package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts +0 -2
  67. package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts.map +0 -1
  68. package/dist/transport/rabbitmq/rabbitmq-transport.test.js +0 -230
  69. package/dist/types/event.test.d.ts +0 -2
  70. package/dist/types/event.test.d.ts.map +0 -1
  71. package/dist/types/event.test.js +0 -130
@@ -1 +1 @@
1
- {"version":3,"file":"matador.d.ts","sourceRoot":"","sources":["../../src/core/matador.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"matador.d.ts","sourceRoot":"","sources":["../../src/core/matador.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAQ/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAKrD,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EACV,aAAa,EACb,UAAU,EACV,KAAK,EACL,UAAU,EACV,YAAY,EACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGnE;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAE9B,6BAA6B;IAC7B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAE5B,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAE/B,sDAAsD;IACtD,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAErD,sCAAsC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IAEnC,0BAA0B;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAE/C,6BAA6B;IAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAE9D;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IAEvD;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAEjD;;;;;OAKG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE/C;;;;;;;OAOG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAE9C;;;;;;;;OAQG;IACH,QAAQ,CAAC,2BAA2B,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3D;AAED;;;;;;;;;GASG;AACH,qBAAa,OAAQ,YAAW,UAAU;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsB;IACpD,OAAO,CAAC,OAAO,CAAS;IAExB;;;;;;OAMG;gBACS,MAAM,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,YAAY;IAgDvD;;OAEG;IACH,QAAQ,CAAC,CAAC,EACR,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,WAAW,EAAE,SAAS,aAAa,EAAE,GACpC,IAAI;IAKP;;;;;;;;;;;;;;;;;OAiBG;IACH,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAc3C;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoE5B;;;;;;;;;;;;OAYG;IACG,IAAI,CAAC,CAAC,EACV,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,IAAI,EAAE,CAAC,EACP,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,UAAU,CAAC;IAChB,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IAuC3E;;OAEG;IACH,gBAAgB,IAAI,aAAa;IAIjC;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACG,WAAW,CAAC,SAAS,SAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;IAatD;;;;;;;;;;;OAWG;IACG,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IAQvC;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAU/B;;OAEG;IACH,WAAW,IAAI,OAAO;YAIR,cAAc;CAM7B"}
@@ -141,6 +141,10 @@ export class Matador {
141
141
  else {
142
142
  this.hooks.logger.info('[Matador] 🟡 Worker not subscribing to any queues (consumeFrom is empty).');
143
143
  }
144
+ // Mark as started
145
+ // Needed for the subscribe calls below, as we might start getting messages
146
+ // that could send events before the code here finishes subscribing to all the queues
147
+ this.started = true;
144
148
  for (const queueName of this.consumeFrom) {
145
149
  const qualifiedName = resolveTargetQueueName(this.topology, queueName);
146
150
  const queueDef = findQueueDefinition(this.topology, queueName);
@@ -158,7 +162,6 @@ export class Matador {
158
162
  : undefined);
159
163
  this.subscriptions.push(subscription);
160
164
  }
161
- this.started = true;
162
165
  }
163
166
  async send(eventOrClass, dataOrOptions, maybeOptions) {
164
167
  if (!this.started) {
package/dist/index.cjs CHANGED
@@ -1893,10 +1893,20 @@ var ProcessingPipeline = class {
1893
1893
  durationMs: performance.now() - startTime
1894
1894
  };
1895
1895
  }
1896
+ const isResumable = isResumableSubscriber(subscriber);
1897
+ const shouldProcessResult = await this.handleShouldProcess(
1898
+ envelope,
1899
+ subscriberDef,
1900
+ isResumable,
1901
+ receipt,
1902
+ startTime
1903
+ );
1904
+ if (!shouldProcessResult.success) {
1905
+ return shouldProcessResult;
1906
+ }
1896
1907
  let result;
1897
1908
  let error;
1898
1909
  let context;
1899
- const isResumable = isResumableSubscriber(subscriber);
1900
1910
  let existingCheckpoint;
1901
1911
  if (isResumable) {
1902
1912
  existingCheckpoint = await this.checkpointStore.get(envelope.id);
@@ -1972,10 +1982,69 @@ var ProcessingPipeline = class {
1972
1982
  subscriber: subscriberDef,
1973
1983
  receipt
1974
1984
  });
1985
+ return await this.handleFailure(
1986
+ error,
1987
+ decision,
1988
+ envelope,
1989
+ subscriberDef,
1990
+ isResumable,
1991
+ receipt,
1992
+ durationMs
1993
+ );
1994
+ }
1995
+ /**
1996
+ * Runs the retry policy's pre-processing check.
1997
+ *
1998
+ * This returns a terminal `ProcessResult` if the message should be skipped (dead-lettered/discarded) without ever invoking the callback,
1999
+ * or a success result if shouldProcess passes and we should continue processing the message.
2000
+ * @param envelope - The message envelope
2001
+ * @param subscriberDef - The subscriber definition
2002
+ * @param isResumable - Whether the subscriber is resumable
2003
+ * @param receipt - The message receipt
2004
+ * @param startTime - The start time of the processing
2005
+ * @returns A `ProcessResult` indicating if we should continue with processing the message, or fail it preemptively
2006
+ */
2007
+ async handleShouldProcess(envelope, subscriberDef, isResumable, receipt, startTime) {
2008
+ const decision = this.retryPolicy.shouldProcess({
2009
+ envelope,
2010
+ subscriber: subscriberDef,
2011
+ receipt
2012
+ });
2013
+ if (decision.action === "process") {
2014
+ return {
2015
+ success: true,
2016
+ durationMs: 0
2017
+ };
2018
+ }
2019
+ const durationMs = performance.now() - startTime;
2020
+ const error = new Error(decision.reason);
2021
+ return await this.handleFailure(
2022
+ error,
2023
+ decision,
2024
+ envelope,
2025
+ subscriberDef,
2026
+ isResumable,
2027
+ receipt,
2028
+ durationMs
2029
+ );
2030
+ }
2031
+ /**
2032
+ * Performs failure handling and cleanup based on retry decision (e.g. dead-letter).
2033
+ *
2034
+ * @param error - The error from the processing attempt
2035
+ * @param decision - The decision from executing the appropriate retry policy check
2036
+ * @param envelope - The message envelope
2037
+ * @param subscriberDef - The subscriber definition
2038
+ * @param isResumable - Whether the subscriber is resumable
2039
+ * @param receipt - The message receipt
2040
+ * @param durationMs - The duration of processing
2041
+ * @returns A `ProcessResult` indicating failure
2042
+ */
2043
+ async handleFailure(error, decision, envelope, subscriberDef, isResumable, receipt, durationMs) {
1975
2044
  envelope.docket.lastError = error.message;
1976
2045
  envelope.docket.firstError ??= error.message;
1977
- if (decision.action === "dead-letter" && context) {
1978
- await context.clear();
2046
+ if (decision.action === "dead-letter" && isResumable) {
2047
+ await this.checkpointStore.delete(envelope.id);
1979
2048
  await this.hooks.onCheckpointCleared?.({
1980
2049
  envelope,
1981
2050
  subscriber: subscriberDef,
@@ -2081,15 +2150,46 @@ var StandardRetryPolicy = class {
2081
2150
  constructor(config = {}) {
2082
2151
  this.config = { ...defaultRetryConfig, ...config };
2083
2152
  }
2153
+ /**
2154
+ * Check if the message should be dead-lettered before the subscriber callback is invoked.
2155
+ * This is needed when a subscriber crashes and the `shouldRetry` policy is never executed.
2156
+ *
2157
+ * Uses the same delivery-count threshold as `shouldRetry` poison check, so an
2158
+ * already-poisoned message is dead-lettered without ever running the callback again.
2159
+ *
2160
+ * Checks the subscriber idempotency setting to prevent processing when a previous attempt crashed.
2161
+ *
2162
+ * @param context - The shouldProcess context.
2163
+ * @returns A 'process' decision if subscriber processing should be attempted; a dead-letter decision otherwise.
2164
+ */
2165
+ shouldProcess(context) {
2166
+ const poisonError = this.checkPoisoned(context.envelope, context.receipt);
2167
+ if (poisonError) {
2168
+ return {
2169
+ action: "dead-letter",
2170
+ queue: "undeliverable",
2171
+ reason: poisonError.message
2172
+ };
2173
+ }
2174
+ const idempotencyError = this.checkIdempotency(
2175
+ context.envelope,
2176
+ context.receipt,
2177
+ context.subscriber
2178
+ );
2179
+ if (idempotencyError) {
2180
+ return {
2181
+ action: "dead-letter",
2182
+ queue: "undeliverable",
2183
+ reason: idempotencyError.message
2184
+ };
2185
+ }
2186
+ return { action: "process" };
2187
+ }
2084
2188
  shouldRetry(context) {
2085
2189
  const { envelope, error, subscriber, receipt } = context;
2086
2190
  const errorMessage = error.message;
2087
- if (receipt.deliveryCount >= this.config.maxDeliveries) {
2088
- const poisonError = new MessageMaybePoisonedError(
2089
- envelope.id,
2090
- receipt.deliveryCount,
2091
- this.config.maxDeliveries
2092
- );
2191
+ const poisonError = this.checkPoisoned(envelope, receipt);
2192
+ if (poisonError) {
2093
2193
  return {
2094
2194
  action: "dead-letter",
2095
2195
  queue: "undeliverable",
@@ -2130,15 +2230,17 @@ var StandardRetryPolicy = class {
2130
2230
  reason: `max attempts exceeded (${this.config.maxAttempts})`
2131
2231
  };
2132
2232
  }
2133
- if (receipt.redelivered && (subscriber.idempotent === "no" || subscriber.idempotent === "unknown")) {
2134
- const idempotentError = new IdempotentMessageCannotRetryError(
2135
- envelope.id,
2136
- subscriber.name
2137
- );
2233
+ const idempotencyError = this.checkIdempotency(
2234
+ envelope,
2235
+ receipt,
2236
+ subscriber,
2237
+ error
2238
+ );
2239
+ if (idempotencyError) {
2138
2240
  return {
2139
2241
  action: "dead-letter",
2140
2242
  queue: "undeliverable",
2141
- reason: idempotentError.message
2243
+ reason: idempotencyError.message
2142
2244
  };
2143
2245
  }
2144
2246
  return {
@@ -2151,6 +2253,41 @@ var StandardRetryPolicy = class {
2151
2253
  const delay = this.config.baseDelay * this.config.backoffMultiplier ** (attempt - 1);
2152
2254
  return Math.min(delay, this.config.maxDelay);
2153
2255
  }
2256
+ /**
2257
+ * Checks the native delivery count against the poison threshold
2258
+ *
2259
+ * @param envelope - The message envelope.
2260
+ * @param receipt - The message receipt.
2261
+ * @returns A MessageMaybePoisonedError if the message is poisoned; null otherwise
2262
+ */
2263
+ checkPoisoned(envelope, receipt) {
2264
+ if (receipt.deliveryCount >= this.config.maxDeliveries) {
2265
+ return new MessageMaybePoisonedError(
2266
+ envelope.id,
2267
+ receipt.deliveryCount,
2268
+ this.config.maxDeliveries
2269
+ );
2270
+ }
2271
+ return null;
2272
+ }
2273
+ /**
2274
+ * Checks whether the subscriber allows retries based on its idempotency setting
2275
+ *
2276
+ * @param envelope - The message envelope.
2277
+ * @param receipt - The message receipt.
2278
+ * @param subscriber - The subscriber definition
2279
+ * @param error - The processing error, if it exists
2280
+ * @returns A IdempotentMessageCannotRetryError if the message is poisoned; null otherwise
2281
+ */
2282
+ checkIdempotency(envelope, receipt, subscriber, error) {
2283
+ if ((error || receipt.redelivered) && (subscriber.idempotent === "no" || subscriber.idempotent === "unknown")) {
2284
+ return new IdempotentMessageCannotRetryError(
2285
+ envelope.id,
2286
+ subscriber.name
2287
+ );
2288
+ }
2289
+ return null;
2290
+ }
2154
2291
  };
2155
2292
 
2156
2293
  // src/schema/types.ts
@@ -2461,6 +2598,7 @@ var Matador = class {
2461
2598
  "[Matador] \u{1F7E1} Worker not subscribing to any queues (consumeFrom is empty)."
2462
2599
  );
2463
2600
  }
2601
+ this.started = true;
2464
2602
  for (const queueName of this.consumeFrom) {
2465
2603
  const qualifiedName = resolveTargetQueueName(this.topology, queueName);
2466
2604
  const queueDef = findQueueDefinition(this.topology, queueName);
@@ -2479,7 +2617,6 @@ var Matador = class {
2479
2617
  );
2480
2618
  this.subscriptions.push(subscription);
2481
2619
  }
2482
- this.started = true;
2483
2620
  }
2484
2621
  async send(eventOrClass, dataOrOptions, maybeOptions) {
2485
2622
  if (!this.started) {
@@ -3626,6 +3763,9 @@ var RabbitMQTransport = class {
3626
3763
  if (queueDef.consumerTimeout) {
3627
3764
  queueOptions.arguments["x-consumer-timeout"] = queueDef.consumerTimeout;
3628
3765
  }
3766
+ if (queueDef.singleActiveConsumer) {
3767
+ queueOptions.arguments["x-single-active-consumer"] = true;
3768
+ }
3629
3769
  return queueOptions;
3630
3770
  }
3631
3771
  async assertWorkQueue(channel, topology, queueDef) {