@zdavison/matador 4.0.0 → 4.0.2
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/core/fanout.d.ts +11 -0
- package/dist/core/fanout.d.ts.map +1 -1
- package/dist/core/fanout.js +103 -14
- package/dist/core/fanout.test.js +377 -5
- package/dist/core/matador.d.ts.map +1 -1
- package/dist/core/matador.js +1 -0
- package/dist/core/matador.test.js +1 -1
- package/dist/index.cjs +161 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/topology/builder.d.ts +4 -2
- package/dist/topology/builder.d.ts.map +1 -1
- package/dist/topology/builder.js +2 -0
- package/dist/topology/builder.test.js +9 -0
- package/dist/topology/index.d.ts +1 -1
- package/dist/topology/index.d.ts.map +1 -1
- package/dist/topology/types.d.ts +18 -6
- package/dist/topology/types.d.ts.map +1 -1
- package/dist/transport/multi/multi-transport.d.ts +6 -0
- package/dist/transport/multi/multi-transport.d.ts.map +1 -1
- package/dist/transport/multi/multi-transport.js +17 -0
- package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.js +45 -6
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts +6 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -1
- package/dist/transport/rabbitmq/rabbitmq-transport.js +52 -13
- package/dist/transport/transport.d.ts +6 -0
- package/dist/transport/transport.d.ts.map +1 -1
- package/dist/types/event.d.ts +17 -0
- package/dist/types/event.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -472,6 +472,8 @@ var TopologyBuilder = class _TopologyBuilder {
|
|
|
472
472
|
* Only applies to default names — a {@link withNaming} override fully owns
|
|
473
473
|
* its output and is never prefixed.
|
|
474
474
|
*
|
|
475
|
+
* Pass `null` or `undefined` to disable prefixing.
|
|
476
|
+
*
|
|
475
477
|
* @default 'matador'
|
|
476
478
|
* @example
|
|
477
479
|
* TopologyBuilder.create().withNamespace('myapp') // matador.myapp.events
|
|
@@ -806,12 +808,22 @@ var FanoutEngine = class {
|
|
|
806
808
|
topology;
|
|
807
809
|
defaultQueue;
|
|
808
810
|
enqueuingCount = 0;
|
|
811
|
+
retryBuffer = [];
|
|
812
|
+
maxRetryBufferSize;
|
|
813
|
+
disposeOnConnected;
|
|
809
814
|
constructor(config) {
|
|
810
815
|
this.transport = config.transport;
|
|
811
816
|
this.schema = config.schema;
|
|
812
817
|
this.hooks = config.hooks;
|
|
813
818
|
this.topology = config.topology;
|
|
814
819
|
this.defaultQueue = config.defaultQueue;
|
|
820
|
+
this.maxRetryBufferSize = config.maxRetryBufferSize ?? 5e3;
|
|
821
|
+
this.disposeOnConnected = this.transport.onConnected?.(() => {
|
|
822
|
+
void this.flushRetryBuffer();
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
dispose() {
|
|
826
|
+
this.disposeOnConnected?.();
|
|
815
827
|
}
|
|
816
828
|
/**
|
|
817
829
|
* Current count of events being enqueued.
|
|
@@ -849,12 +861,13 @@ var FanoutEngine = class {
|
|
|
849
861
|
universalMetadata,
|
|
850
862
|
delayMs: options.delayMs
|
|
851
863
|
});
|
|
864
|
+
const sendOptions = options.delayMs !== void 0 ? { delay: options.delayMs } : void 0;
|
|
852
865
|
this.enqueuingCount++;
|
|
853
866
|
try {
|
|
854
867
|
const usedTransport = await this.transport.send(
|
|
855
868
|
qualifiedQueue,
|
|
856
869
|
envelope,
|
|
857
|
-
|
|
870
|
+
sendOptions
|
|
858
871
|
);
|
|
859
872
|
sent++;
|
|
860
873
|
await this.hooks.onEnqueueSuccess({
|
|
@@ -864,17 +877,48 @@ var FanoutEngine = class {
|
|
|
864
877
|
});
|
|
865
878
|
} catch (error) {
|
|
866
879
|
const cause = error instanceof Error ? error : new Error(String(error));
|
|
867
|
-
const
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
880
|
+
const shouldBuffer = options.buffer !== false;
|
|
881
|
+
if (shouldBuffer && this.retryBuffer.length < this.maxRetryBufferSize) {
|
|
882
|
+
this.retryBuffer.push({
|
|
883
|
+
queue: qualifiedQueue,
|
|
884
|
+
envelope,
|
|
885
|
+
sendOptions,
|
|
886
|
+
subscriberName: subscriber.name
|
|
887
|
+
});
|
|
888
|
+
this.hooks.logger.warn(
|
|
889
|
+
`[Matador] \u{1F7E1} Message for '${subscriber.name}' buffered for retry on reconnect (buffer: ${this.retryBuffer.length}/${this.maxRetryBufferSize}).`
|
|
890
|
+
);
|
|
891
|
+
if (options.throwOnBufferedFailure) {
|
|
892
|
+
const err = new TransportSendError(qualifiedQueue, cause);
|
|
893
|
+
errors.push({
|
|
894
|
+
subscriberName: subscriber.name,
|
|
895
|
+
queue: qualifiedQueue,
|
|
896
|
+
error: err
|
|
897
|
+
});
|
|
898
|
+
await this.hooks.onEnqueueError({
|
|
899
|
+
envelope,
|
|
900
|
+
error: err,
|
|
901
|
+
transport: this.transport.name
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
} else {
|
|
905
|
+
if (shouldBuffer) {
|
|
906
|
+
this.hooks.logger.error(
|
|
907
|
+
`[Matador] \u{1F534} Retry buffer full (${this.maxRetryBufferSize}). Message for '${subscriber.name}' dropped and will not be retried.`
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
const err = new TransportSendError(qualifiedQueue, cause);
|
|
911
|
+
errors.push({
|
|
912
|
+
subscriberName: subscriber.name,
|
|
913
|
+
queue: qualifiedQueue,
|
|
914
|
+
error: err
|
|
915
|
+
});
|
|
916
|
+
await this.hooks.onEnqueueError({
|
|
917
|
+
envelope,
|
|
918
|
+
error: err,
|
|
919
|
+
transport: this.transport.name
|
|
920
|
+
});
|
|
921
|
+
}
|
|
878
922
|
} finally {
|
|
879
923
|
this.enqueuingCount--;
|
|
880
924
|
}
|
|
@@ -886,6 +930,57 @@ var FanoutEngine = class {
|
|
|
886
930
|
errors
|
|
887
931
|
};
|
|
888
932
|
}
|
|
933
|
+
/**
|
|
934
|
+
* Flushes the retry buffer
|
|
935
|
+
*
|
|
936
|
+
* This is called when the transport reconnects, and is used to retry any messages that were buffered while the transport was disconnected
|
|
937
|
+
*/
|
|
938
|
+
async flushRetryBuffer() {
|
|
939
|
+
if (this.retryBuffer.length === 0) return;
|
|
940
|
+
this.hooks.logger.info(
|
|
941
|
+
`[Matador] \u23F3 Flushing ${this.retryBuffer.length} buffered message(s)...`
|
|
942
|
+
);
|
|
943
|
+
const toFlush = this.retryBuffer.splice(0);
|
|
944
|
+
for (const item of toFlush) {
|
|
945
|
+
this.enqueuingCount++;
|
|
946
|
+
try {
|
|
947
|
+
const usedTransport = await this.transport.send(
|
|
948
|
+
item.queue,
|
|
949
|
+
item.envelope,
|
|
950
|
+
item.sendOptions
|
|
951
|
+
);
|
|
952
|
+
await this.hooks.onEnqueueSuccess({
|
|
953
|
+
envelope: item.envelope,
|
|
954
|
+
queue: item.queue,
|
|
955
|
+
transport: usedTransport
|
|
956
|
+
});
|
|
957
|
+
} catch (error) {
|
|
958
|
+
if (this.retryBuffer.length < this.maxRetryBufferSize) {
|
|
959
|
+
this.retryBuffer.push(item);
|
|
960
|
+
} else {
|
|
961
|
+
const cause = error instanceof Error ? error : new Error(String(error));
|
|
962
|
+
const err = new TransportSendError(item.queue, cause);
|
|
963
|
+
await this.hooks.onEnqueueError({
|
|
964
|
+
envelope: item.envelope,
|
|
965
|
+
error: err,
|
|
966
|
+
transport: this.transport.name
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
} finally {
|
|
970
|
+
this.enqueuingCount--;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
const remaining = this.retryBuffer.length;
|
|
974
|
+
if (remaining > 0) {
|
|
975
|
+
this.hooks.logger.warn(
|
|
976
|
+
`[Matador] \u{1F7E1} ${remaining} buffered message(s) could not be flushed; will retry on next reconnect.`
|
|
977
|
+
);
|
|
978
|
+
} else {
|
|
979
|
+
this.hooks.logger.info(
|
|
980
|
+
"[Matador] \u{1F7E2} All buffered messages flushed successfully."
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
889
984
|
async isSubscriberEnabled(subscriber) {
|
|
890
985
|
if (!subscriber.enabled) {
|
|
891
986
|
return true;
|
|
@@ -2352,6 +2447,7 @@ var Matador = class {
|
|
|
2352
2447
|
if (!this.started) {
|
|
2353
2448
|
return;
|
|
2354
2449
|
}
|
|
2450
|
+
this.fanout.dispose();
|
|
2355
2451
|
await this.shutdownManager.shutdown();
|
|
2356
2452
|
this.started = false;
|
|
2357
2453
|
}
|
|
@@ -2790,6 +2886,19 @@ var MultiTransport = class {
|
|
|
2790
2886
|
isConnected() {
|
|
2791
2887
|
return this.connected && this.primary.isConnected();
|
|
2792
2888
|
}
|
|
2889
|
+
/**
|
|
2890
|
+
* Registers a callback to fire each time the transport successfully (re)connects
|
|
2891
|
+
* @param callback - The callback to fire when the transport successfully (re)connects
|
|
2892
|
+
* @returns A function to unsubscribe from the callback
|
|
2893
|
+
*/
|
|
2894
|
+
onConnected(callback) {
|
|
2895
|
+
const unsubFunctions = this.transports.map((t) => t.onConnected?.(callback)).filter(Boolean);
|
|
2896
|
+
return () => {
|
|
2897
|
+
for (const unsub of unsubFunctions) {
|
|
2898
|
+
unsub();
|
|
2899
|
+
}
|
|
2900
|
+
};
|
|
2901
|
+
}
|
|
2793
2902
|
async applyTopology(topology) {
|
|
2794
2903
|
await Promise.all(this.transports.map((t) => t.applyTopology(topology)));
|
|
2795
2904
|
}
|
|
@@ -2939,6 +3048,18 @@ var RabbitMQTransport = class {
|
|
|
2939
3048
|
isConnected() {
|
|
2940
3049
|
return this.connectionManager.isConnected();
|
|
2941
3050
|
}
|
|
3051
|
+
/**
|
|
3052
|
+
* Registers a callback to fire each time the transport (here RabbitMQ) successfully (re)connects
|
|
3053
|
+
* @param callback - The callback to fire when the transport (here RabbitMQ) successfully (re)connects
|
|
3054
|
+
* @returns A function to unsubscribe from the callback
|
|
3055
|
+
*/
|
|
3056
|
+
onConnected(callback) {
|
|
3057
|
+
return this.connectionManager.onStateChange((state) => {
|
|
3058
|
+
if (state.status === "connected") {
|
|
3059
|
+
callback();
|
|
3060
|
+
}
|
|
3061
|
+
});
|
|
3062
|
+
}
|
|
2942
3063
|
async applyTopology(topology) {
|
|
2943
3064
|
this.topology = topology;
|
|
2944
3065
|
if (!this.publishChannel) {
|
|
@@ -3217,6 +3338,14 @@ var RabbitMQTransport = class {
|
|
|
3217
3338
|
return queueChannel;
|
|
3218
3339
|
}
|
|
3219
3340
|
async doConnect() {
|
|
3341
|
+
if (this.connection) {
|
|
3342
|
+
try {
|
|
3343
|
+
await this.connection.close();
|
|
3344
|
+
} catch {
|
|
3345
|
+
}
|
|
3346
|
+
this.connection = null;
|
|
3347
|
+
this.publishChannel = null;
|
|
3348
|
+
}
|
|
3220
3349
|
this.queueChannels.clear();
|
|
3221
3350
|
this.logger.info(
|
|
3222
3351
|
`[Matador] \u23F3 Connecting to RabbitMQ at '${redactAmqpUrl(this.config.url)}'.`
|
|
@@ -3234,21 +3363,33 @@ var RabbitMQTransport = class {
|
|
|
3234
3363
|
consumer.active = false;
|
|
3235
3364
|
}
|
|
3236
3365
|
}
|
|
3366
|
+
this.connection = null;
|
|
3367
|
+
this.publishChannel = null;
|
|
3237
3368
|
if (this.connectionManager.isConnected()) {
|
|
3238
3369
|
this.connectionManager.handleConnectionLost(
|
|
3239
3370
|
new Error("Connection closed unexpectedly")
|
|
3240
3371
|
);
|
|
3241
3372
|
}
|
|
3242
3373
|
});
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
this.
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3374
|
+
try {
|
|
3375
|
+
this.publishChannel = await connection.createConfirmChannel();
|
|
3376
|
+
this.publishChannel.on("error", (err) => {
|
|
3377
|
+
this.logger.error("[Matador] \u{1F534} RabbitMQ publish channel error", err);
|
|
3378
|
+
});
|
|
3379
|
+
if (this.topology) {
|
|
3380
|
+
await this.applyTopology(this.topology);
|
|
3381
|
+
for (const intent of this.subscriptionIntents) {
|
|
3382
|
+
await this.activateIntent(intent);
|
|
3383
|
+
}
|
|
3251
3384
|
}
|
|
3385
|
+
} catch (err) {
|
|
3386
|
+
try {
|
|
3387
|
+
await connection.close();
|
|
3388
|
+
} catch {
|
|
3389
|
+
}
|
|
3390
|
+
this.connection = null;
|
|
3391
|
+
this.publishChannel = null;
|
|
3392
|
+
throw err;
|
|
3252
3393
|
}
|
|
3253
3394
|
this.logger.info("[Matador] \u{1F50C} Connected to RabbitMQ");
|
|
3254
3395
|
}
|