@fluojs/microservices 1.0.3 → 1.0.5

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 (34) hide show
  1. package/README.ko.md +50 -4
  2. package/README.md +50 -4
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/module.js +1 -1
  6. package/dist/service.d.ts +3 -2
  7. package/dist/service.d.ts.map +1 -1
  8. package/dist/service.js +7 -4
  9. package/dist/status.d.ts +2 -0
  10. package/dist/status.d.ts.map +1 -1
  11. package/dist/status.js +5 -3
  12. package/dist/transports/grpc-transport.d.ts +4 -0
  13. package/dist/transports/grpc-transport.d.ts.map +1 -1
  14. package/dist/transports/grpc-transport.js +226 -260
  15. package/dist/transports/kafka-transport.d.ts +1 -0
  16. package/dist/transports/kafka-transport.d.ts.map +1 -1
  17. package/dist/transports/kafka-transport.js +56 -36
  18. package/dist/transports/mqtt-transport.d.ts +4 -0
  19. package/dist/transports/mqtt-transport.d.ts.map +1 -1
  20. package/dist/transports/mqtt-transport.js +91 -32
  21. package/dist/transports/nats-transport.d.ts +1 -0
  22. package/dist/transports/nats-transport.d.ts.map +1 -1
  23. package/dist/transports/nats-transport.js +53 -23
  24. package/dist/transports/rabbitmq-transport.d.ts +1 -0
  25. package/dist/transports/rabbitmq-transport.d.ts.map +1 -1
  26. package/dist/transports/rabbitmq-transport.js +54 -29
  27. package/dist/transports/redis-streams-transport.d.ts.map +1 -1
  28. package/dist/transports/redis-streams-transport.js +22 -2
  29. package/dist/transports/tcp-transport.d.ts +8 -2
  30. package/dist/transports/tcp-transport.d.ts.map +1 -1
  31. package/dist/transports/tcp-transport.js +4 -0
  32. package/dist/types.d.ts +2 -0
  33. package/dist/types.d.ts.map +1 -1
  34. package/package.json +4 -4
@@ -7,6 +7,7 @@
7
7
  * replies without leaking cross-request state through the shared producer/consumer pair.
8
8
  */
9
9
  export class KafkaMicroserviceTransport {
10
+ closePromise;
10
11
  closing = false;
11
12
  handler;
12
13
  listening = false;
@@ -37,7 +38,12 @@ export class KafkaMicroserviceTransport {
37
38
  * @returns A promise that resolves once all subscriptions are active.
38
39
  */
39
40
  async listen(handler) {
40
- this.closing = false;
41
+ if (this.closing) {
42
+ if (this.closePromise) {
43
+ throw new Error('KafkaMicroserviceTransport is closing. Wait for close() to complete before listen().');
44
+ }
45
+ this.closing = false;
46
+ }
41
47
  this.handler = handler;
42
48
  if (this.listening) {
43
49
  return;
@@ -49,17 +55,11 @@ export class KafkaMicroserviceTransport {
49
55
  this.listenPromise = (async () => {
50
56
  const subscribed = [];
51
57
  try {
52
- await this.options.consumer.subscribe(this.eventTopic, message => {
53
- void this.handleInboundEvent(message).catch(() => undefined);
54
- });
58
+ await this.options.consumer.subscribe(this.eventTopic, message => this.handleInboundEvent(message));
55
59
  subscribed.push(this.eventTopic);
56
- await this.options.consumer.subscribe(this.messageTopic, message => {
57
- void this.handleInboundRequest(message).catch(() => undefined);
58
- });
60
+ await this.options.consumer.subscribe(this.messageTopic, message => this.handleInboundRequest(message));
59
61
  subscribed.push(this.messageTopic);
60
- await this.options.consumer.subscribe(this.responseTopic, message => {
61
- void this.handleInboundResponse(message).catch(() => undefined);
62
- });
62
+ await this.options.consumer.subscribe(this.responseTopic, message => this.handleInboundResponse(message));
63
63
  subscribed.push(this.responseTopic);
64
64
  } catch (error) {
65
65
  for (const topic of subscribed) {
@@ -138,6 +138,13 @@ export class KafkaMicroserviceTransport {
138
138
  });
139
139
  }
140
140
  void Promise.resolve().then(async () => {
141
+ if (!this.pending.has(requestId)) {
142
+ return;
143
+ }
144
+ if (signal?.aborted) {
145
+ entry.reject(new Error('Kafka request aborted before publish.'));
146
+ return;
147
+ }
141
148
  if (this.closing) {
142
149
  entry.reject(new Error('Kafka microservice transport closed before request dispatch.'));
143
150
  return;
@@ -174,30 +181,41 @@ export class KafkaMicroserviceTransport {
174
181
  * @returns A promise that resolves once shutdown cleanup completes.
175
182
  */
176
183
  async close() {
177
- this.closing = true;
178
- let closeError;
179
- if (this.listenPromise) {
180
- await this.listenPromise;
184
+ if (this.closePromise) {
185
+ await this.closePromise;
186
+ return;
181
187
  }
182
- try {
183
- if (this.listening) {
184
- for (const topic of new Set([this.eventTopic, this.messageTopic, this.responseTopic])) {
185
- try {
186
- await this.options.consumer.unsubscribe(topic);
187
- } catch (error) {
188
- closeError ??= error;
188
+ this.closing = true;
189
+ this.closePromise = (async () => {
190
+ let closeError;
191
+ if (this.listenPromise) {
192
+ await this.listenPromise;
193
+ }
194
+ try {
195
+ if (this.listening) {
196
+ for (const topic of new Set([this.eventTopic, this.messageTopic, this.responseTopic])) {
197
+ try {
198
+ await this.options.consumer.unsubscribe(topic);
199
+ } catch (error) {
200
+ closeError ??= error;
201
+ }
189
202
  }
190
203
  }
204
+ } finally {
205
+ this.handler = undefined;
206
+ this.listening = false;
207
+ for (const pending of [...this.pending.values()]) {
208
+ pending.reject(new Error('Kafka microservice transport closed before response.'));
209
+ }
191
210
  }
192
- } finally {
193
- this.handler = undefined;
194
- this.listening = false;
195
- for (const pending of [...this.pending.values()]) {
196
- pending.reject(new Error('Kafka microservice transport closed before response.'));
211
+ if (closeError) {
212
+ throw closeError;
197
213
  }
198
- }
199
- if (closeError) {
200
- throw closeError;
214
+ })();
215
+ try {
216
+ await this.closePromise;
217
+ } finally {
218
+ this.closePromise = undefined;
201
219
  }
202
220
  }
203
221
  async handleInboundEvent(rawMessage) {
@@ -223,19 +241,14 @@ export class KafkaMicroserviceTransport {
223
241
  return;
224
242
  }
225
243
  const replyTopic = message.replyTopic && message.replyTopic.length > 0 ? message.replyTopic : this.responseTopic;
244
+ let payload;
226
245
  try {
227
- const payload = await this.handler({
246
+ payload = await this.handler({
228
247
  kind: 'message',
229
248
  pattern: message.pattern,
230
249
  payload: message.payload,
231
250
  requestId: message.requestId
232
251
  });
233
- await this.options.producer.publish(replyTopic, JSON.stringify({
234
- kind: 'response',
235
- pattern: message.pattern,
236
- payload,
237
- requestId: message.requestId
238
- }));
239
252
  } catch (error) {
240
253
  const errorMessage = error instanceof Error ? error.message : 'Unhandled microservice error';
241
254
  await this.options.producer.publish(replyTopic, JSON.stringify({
@@ -244,7 +257,14 @@ export class KafkaMicroserviceTransport {
244
257
  pattern: message.pattern,
245
258
  requestId: message.requestId
246
259
  }));
260
+ return;
247
261
  }
262
+ await this.options.producer.publish(replyTopic, JSON.stringify({
263
+ kind: 'response',
264
+ pattern: message.pattern,
265
+ payload,
266
+ requestId: message.requestId
267
+ }));
248
268
  }
249
269
  async handleInboundResponse(rawMessage) {
250
270
  const message = this.parseMessage(rawMessage);
@@ -53,9 +53,12 @@ export interface MqttMicroserviceTransportOptions {
53
53
  export declare class MqttMicroserviceTransport implements MicroserviceTransport {
54
54
  private readonly options;
55
55
  private client;
56
+ private closePromise;
56
57
  private closing;
57
58
  private handler;
58
59
  private readonly internallyOwnedClient;
60
+ /** Indicates whether MQTT owns the client instance it resolves and closes. */
61
+ readonly ownsResources: boolean;
59
62
  private logger;
60
63
  private listening;
61
64
  private listenPromise;
@@ -118,6 +121,7 @@ export declare class MqttMicroserviceTransport implements MicroserviceTransport
118
121
  private subscribeTopic;
119
122
  private unsubscribeTopic;
120
123
  private endClient;
124
+ private disposeOwnedClientAfterListenFailure;
121
125
  private logEventHandlerFailure;
122
126
  }
123
127
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"mqtt-transport.d.ts","sourceRoot":"","sources":["../../src/transports/mqtt-transport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mqtt-transport.d.ts","sourceRoot":"","sources":["../../src/transports/mqtt-transport.ts"],"names":[],"mappings":"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"}
@@ -15,9 +15,12 @@ const mqttKinds = {
15
15
  */
16
16
  export class MqttMicroserviceTransport {
17
17
  client;
18
+ closePromise;
18
19
  closing = false;
19
20
  handler;
20
21
  internallyOwnedClient;
22
+ /** Indicates whether MQTT owns the client instance it resolves and closes. */
23
+ ownsResources;
21
24
  logger;
22
25
  listening = false;
23
26
  listenPromise;
@@ -56,6 +59,7 @@ export class MqttMicroserviceTransport {
56
59
  this.requestTimeoutMs = options.requestTimeoutMs ?? 3_000;
57
60
  this.client = options.client;
58
61
  this.internallyOwnedClient = !options.client;
62
+ this.ownsResources = this.internallyOwnedClient;
59
63
  }
60
64
  setLogger(logger) {
61
65
  this.logger = logger;
@@ -68,7 +72,12 @@ export class MqttMicroserviceTransport {
68
72
  * @returns A promise that resolves once all subscriptions are active.
69
73
  */
70
74
  async listen(handler) {
71
- this.closing = false;
75
+ if (this.closing) {
76
+ if (this.closePromise) {
77
+ throw new Error('MqttMicroserviceTransport is closing. Wait for close() to complete before listen().');
78
+ }
79
+ this.closing = false;
80
+ }
72
81
  this.handler = handler;
73
82
  if (this.listening) {
74
83
  return;
@@ -93,6 +102,9 @@ export class MqttMicroserviceTransport {
93
102
  await this.unsubscribeTopic(client, topic).catch(() => undefined);
94
103
  }
95
104
  client.off?.('message', this.messageListener);
105
+ this.handler = undefined;
106
+ this.listening = false;
107
+ await this.disposeOwnedClientAfterListenFailure(client);
96
108
  throw error;
97
109
  }
98
110
  this.listening = true;
@@ -175,11 +187,29 @@ export class MqttMicroserviceTransport {
175
187
  });
176
188
  }
177
189
  void Promise.resolve().then(async () => {
190
+ if (!this.pending.has(requestId)) {
191
+ return;
192
+ }
193
+ if (signal?.aborted) {
194
+ entry.reject(new Error('MQTT request aborted before publish.'));
195
+ return;
196
+ }
178
197
  if (this.closing) {
179
198
  entry.reject(new Error('MQTT microservice transport closed before response.'));
180
199
  return;
181
200
  }
182
201
  const client = await this.resolveClient();
202
+ if (!this.pending.has(requestId)) {
203
+ return;
204
+ }
205
+ if (signal?.aborted) {
206
+ entry.reject(new Error('MQTT request aborted before publish.'));
207
+ return;
208
+ }
209
+ if (this.closing) {
210
+ entry.reject(new Error('MQTT microservice transport closed before response.'));
211
+ return;
212
+ }
183
213
  await this.publish(client, this.messageTopic, frame, {
184
214
  qos: this.messageQos,
185
215
  retain: this.messageRetain
@@ -219,44 +249,59 @@ export class MqttMicroserviceTransport {
219
249
  * @returns A promise that resolves once shutdown cleanup completes.
220
250
  */
221
251
  async close() {
222
- this.closing = true;
223
- let closeError;
224
- if (this.listenPromise) {
225
- await this.listenPromise;
252
+ if (this.closePromise) {
253
+ await this.closePromise;
254
+ return;
226
255
  }
227
- try {
228
- const client = this.client;
229
- if (client && this.listening) {
230
- for (const topic of [this.eventTopic, this.messageTopic, this.replyTopic]) {
231
- try {
232
- await this.unsubscribeTopic(client, topic);
233
- } catch (error) {
234
- closeError ??= error;
235
- }
256
+ this.closing = true;
257
+ this.closePromise = (async () => {
258
+ let closeError;
259
+ if (this.listenPromise) {
260
+ try {
261
+ await this.listenPromise;
262
+ } catch (error) {
263
+ closeError = error;
236
264
  }
237
265
  }
238
- if (client) {
239
- client.off?.('message', this.messageListener);
240
- if (this.internallyOwnedClient) {
241
- try {
242
- await this.endClient(client);
243
- } catch (error) {
244
- closeError ??= error;
266
+ try {
267
+ const client = this.client;
268
+ if (client && this.listening) {
269
+ for (const topic of [this.eventTopic, this.messageTopic, this.replyTopic]) {
270
+ try {
271
+ await this.unsubscribeTopic(client, topic);
272
+ } catch (error) {
273
+ closeError ??= error;
274
+ }
245
275
  }
246
276
  }
277
+ if (client) {
278
+ client.off?.('message', this.messageListener);
279
+ if (this.internallyOwnedClient) {
280
+ try {
281
+ await this.endClient(client);
282
+ } catch (error) {
283
+ closeError ??= error;
284
+ }
285
+ }
286
+ }
287
+ } finally {
288
+ this.handler = undefined;
289
+ this.listening = false;
290
+ if (this.internallyOwnedClient) {
291
+ this.client = undefined;
292
+ }
293
+ for (const pending of [...this.pending.values()]) {
294
+ pending.reject(new Error('MQTT microservice transport closed before response.'));
295
+ }
247
296
  }
248
- } finally {
249
- this.handler = undefined;
250
- this.listening = false;
251
- if (this.internallyOwnedClient) {
252
- this.client = undefined;
253
- }
254
- for (const pending of [...this.pending.values()]) {
255
- pending.reject(new Error('MQTT microservice transport closed before response.'));
297
+ if (closeError) {
298
+ throw closeError;
256
299
  }
257
- }
258
- if (closeError) {
259
- throw closeError;
300
+ })();
301
+ try {
302
+ await this.closePromise;
303
+ } finally {
304
+ this.closePromise = undefined;
260
305
  }
261
306
  }
262
307
  async handleInbound(topic, rawMessage) {
@@ -419,6 +464,20 @@ export class MqttMicroserviceTransport {
419
464
  });
420
465
  });
421
466
  }
467
+ async disposeOwnedClientAfterListenFailure(client) {
468
+ if (!this.internallyOwnedClient) {
469
+ return;
470
+ }
471
+ try {
472
+ await this.endClient(client);
473
+ } catch {
474
+ // Preserve the original startup failure for callers; shutdown errors are best-effort here.
475
+ } finally {
476
+ if (this.client === client) {
477
+ this.client = undefined;
478
+ }
479
+ }
480
+ }
422
481
  logEventHandlerFailure(error) {
423
482
  logTransportEventHandlerFailure(this.logger, 'MqttMicroserviceTransport', error);
424
483
  }
@@ -35,6 +35,7 @@ export interface NatsMicroserviceTransportOptions {
35
35
  */
36
36
  export declare class NatsMicroserviceTransport implements MicroserviceTransport {
37
37
  private readonly options;
38
+ private closePromise;
38
39
  private closing;
39
40
  private handler;
40
41
  private logger;
@@ -1 +1 @@
1
- {"version":3,"file":"nats-transport.d.ts","sourceRoot":"","sources":["../../src/transports/nats-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGxG,UAAU,eAAe;IACvB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CACjC;AAED,UAAU,aAAa;IACrB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;IACjC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAAC;CACnC;AAED,UAAU,oBAAoB;IAC5B,WAAW,IAAI,IAAI,CAAC;CACrB;AAED,UAAU,QAAQ;IAChB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;IACpD,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,UAAU,CAAA;KAAE,CAAC,CAAC;IAC7G,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAAG,oBAAoB,CAAC;CAC/F;AAED,+DAA+D;AAC/D,MAAM,WAAW,gCAAgC;IAC/C,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAkBD;;;;;GAKG;AACH,qBAAa,yBAA0B,YAAW,qBAAqB;IA0BzD,OAAO,CAAC,QAAQ,CAAC,OAAO;IAzBpC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,MAAM,CAA0C;IACxD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,aAAa,CAA8B;IAEnD,OAAO,CAAC,sBAAsB;IAI9B,OAAO,CAAC,wBAAwB;IAMhC;;;;OAIG;gBAC0B,OAAO,EAAE,gCAAgC;IAMtE,SAAS,CAAC,MAAM,EAAE,2BAA2B,GAAG,IAAI;IAIpD;;;;;OAKG;IACG,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBtD;;;;;;;OAOG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IA+FrF;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAc5D;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YA0Bd,kBAAkB;YAsBlB,oBAAoB;IAwBlC,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,MAAM;CAGf"}
1
+ {"version":3,"file":"nats-transport.d.ts","sourceRoot":"","sources":["../../src/transports/nats-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGxG,UAAU,eAAe;IACvB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CACjC;AAED,UAAU,aAAa;IACrB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;IACjC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAAC;CACnC;AAED,UAAU,oBAAoB;IAC5B,WAAW,IAAI,IAAI,CAAC;CACrB;AAED,UAAU,QAAQ;IAChB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;IACpD,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,UAAU,CAAA;KAAE,CAAC,CAAC;IAC7G,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAAG,oBAAoB,CAAC;CAC/F;AAED,+DAA+D;AAC/D,MAAM,WAAW,gCAAgC;IAC/C,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAkBD;;;;;GAKG;AACH,qBAAa,yBAA0B,YAAW,qBAAqB;IA2BzD,OAAO,CAAC,QAAQ,CAAC,OAAO;IA1BpC,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,MAAM,CAA0C;IACxD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,aAAa,CAA8B;IAEnD,OAAO,CAAC,sBAAsB;IAI9B,OAAO,CAAC,wBAAwB;IAMhC;;;;OAIG;gBAC0B,OAAO,EAAE,gCAAgC;IAMtE,SAAS,CAAC,MAAM,EAAE,2BAA2B,GAAG,IAAI;IAIpD;;;;;OAKG;IACG,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAwCtD;;;;;;;OAOG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IA+FrF;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAc5D;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAuCd,kBAAkB;YAsBlB,oBAAoB;IAwBlC,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,MAAM;CAGf"}
@@ -9,6 +9,7 @@ import { logTransportEventHandlerFailure } from './event-handler-logger.js';
9
9
  * preserving JSON framing and NATS request timeout behavior.
10
10
  */
11
11
  export class NatsMicroserviceTransport {
12
+ closePromise;
12
13
  closing = false;
13
14
  handler;
14
15
  logger;
@@ -49,18 +50,36 @@ export class NatsMicroserviceTransport {
49
50
  * @returns A promise that resolves once subscriptions are active.
50
51
  */
51
52
  async listen(handler) {
52
- this.closing = false;
53
+ if (this.closing) {
54
+ if (this.closePromise) {
55
+ throw new Error('NATS microservice transport is closing. Wait for close() to complete before listen().');
56
+ }
57
+ this.closing = false;
58
+ }
53
59
  this.handler = handler;
54
60
  if (this.listening) {
55
61
  return;
56
62
  }
57
- const eventSubscription = this.options.client.subscribe(this.eventSubject, message => {
58
- this.handleEventMessageSafely(message);
59
- });
60
- const messageSubscription = this.options.client.subscribe(this.messageSubject, message => {
61
- void this.handleRequestMessage(message);
62
- });
63
- this.subscriptions = [eventSubscription, messageSubscription];
63
+ try {
64
+ this.subscriptions.push(this.options.client.subscribe(this.eventSubject, message => {
65
+ this.handleEventMessageSafely(message);
66
+ }));
67
+ this.subscriptions.push(this.options.client.subscribe(this.messageSubject, message => {
68
+ void this.handleRequestMessage(message);
69
+ }));
70
+ } catch (error) {
71
+ for (const subscription of this.subscriptions.reverse()) {
72
+ try {
73
+ subscription.unsubscribe();
74
+ } catch {
75
+ // Preserve the original subscription failure; partial setup cleanup is best-effort.
76
+ }
77
+ }
78
+ this.subscriptions = [];
79
+ this.handler = undefined;
80
+ this.listening = false;
81
+ throw error;
82
+ }
64
83
  this.listening = true;
65
84
  }
66
85
 
@@ -173,24 +192,35 @@ export class NatsMicroserviceTransport {
173
192
  * @returns A promise that resolves once shutdown cleanup completes.
174
193
  */
175
194
  async close() {
195
+ if (this.closePromise) {
196
+ await this.closePromise;
197
+ return;
198
+ }
176
199
  this.closing = true;
177
- let closeError;
178
- try {
179
- for (const subscription of this.subscriptions) {
180
- subscription.unsubscribe();
200
+ this.closePromise = (async () => {
201
+ let closeError;
202
+ try {
203
+ for (const subscription of this.subscriptions) {
204
+ subscription.unsubscribe();
205
+ }
206
+ } catch (error) {
207
+ closeError = error;
208
+ } finally {
209
+ this.subscriptions = [];
210
+ this.listening = false;
211
+ this.handler = undefined;
212
+ for (const pending of [...this.pending.values()]) {
213
+ pending.reject(new Error('NATS microservice transport closed before response.'));
214
+ }
181
215
  }
182
- } catch (error) {
183
- closeError = error;
184
- } finally {
185
- this.subscriptions = [];
186
- this.listening = false;
187
- this.handler = undefined;
188
- for (const pending of [...this.pending.values()]) {
189
- pending.reject(new Error('NATS microservice transport closed before response.'));
216
+ if (closeError) {
217
+ throw closeError;
190
218
  }
191
- }
192
- if (closeError) {
193
- throw closeError;
219
+ })();
220
+ try {
221
+ await this.closePromise;
222
+ } finally {
223
+ this.closePromise = undefined;
194
224
  }
195
225
  }
196
226
  async handleEventMessage(message) {
@@ -23,6 +23,7 @@ export interface RabbitMqMicroserviceTransportOptions {
23
23
  */
24
24
  export declare class RabbitMqMicroserviceTransport implements MicroserviceTransport {
25
25
  private readonly options;
26
+ private closePromise;
26
27
  private closing;
27
28
  private handler;
28
29
  private listening;
@@ -1 +1 @@
1
- {"version":3,"file":"rabbitmq-transport.d.ts","sourceRoot":"","sources":["../../src/transports/rabbitmq-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE3E,UAAU,oBAAoB;IAC5B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1F,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED,UAAU,qBAAqB;IAC7B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAWD,mEAAmE;AACnE,MAAM,WAAW,oCAAoC;IACnD,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,qBAAqB,CAAC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,qBAAa,6BAA8B,YAAW,qBAAqB;IAqB7D,OAAO,CAAC,QAAQ,CAAC,OAAO;IApBpC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAInB;IACL,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IAEtD;;;;OAIG;gBAC0B,OAAO,EAAE,oCAAoC;IAO1E;;;;;OAKG;IACG,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA4CtD;;;;;;;OAOG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IA6GrF;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAc5D;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YA8Bd,oBAAoB;YAiCpB,aAAa;IAmC3B,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,qBAAqB;CAO9B"}
1
+ {"version":3,"file":"rabbitmq-transport.d.ts","sourceRoot":"","sources":["../../src/transports/rabbitmq-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE3E,UAAU,oBAAoB;IAC5B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1F,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED,UAAU,qBAAqB;IAC7B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAWD,mEAAmE;AACnE,MAAM,WAAW,oCAAoC;IACnD,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,qBAAqB,CAAC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,qBAAa,6BAA8B,YAAW,qBAAqB;IAsB7D,OAAO,CAAC,QAAQ,CAAC,OAAO;IArBpC,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAInB;IACL,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IAEtD;;;;OAIG;gBAC0B,OAAO,EAAE,oCAAoC;IAO1E;;;;;OAKG;IACG,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAiDtD;;;;;;;OAOG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IA6GrF;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAc5D;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAoDd,oBAAoB;YAiCpB,aAAa;IAsC3B,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,qBAAqB;CAO9B"}
@@ -7,6 +7,7 @@
7
7
  * a queue-oriented topology while still consuming the generic Fluo transport API.
8
8
  */
9
9
  export class RabbitMqMicroserviceTransport {
10
+ closePromise;
10
11
  closing = false;
11
12
  handler;
12
13
  listening = false;
@@ -38,7 +39,12 @@ export class RabbitMqMicroserviceTransport {
38
39
  * @returns A promise that resolves once queue consumers are registered.
39
40
  */
40
41
  async listen(handler) {
41
- this.closing = false;
42
+ if (this.closing) {
43
+ if (this.closePromise) {
44
+ throw new Error('RabbitMqMicroserviceTransport is closing. Wait for close() to complete before listen().');
45
+ }
46
+ this.closing = false;
47
+ }
42
48
  this.handler = handler;
43
49
  if (this.listening) {
44
50
  return;
@@ -51,9 +57,7 @@ export class RabbitMqMicroserviceTransport {
51
57
  const queues = new Set([this.eventQueue, this.messageQueue, this.responseQueue]);
52
58
  try {
53
59
  for (const queue of queues) {
54
- await this.options.consumer.consume(queue, message => {
55
- void this.handleInboundMessage(message).catch(() => undefined);
56
- });
60
+ await this.options.consumer.consume(queue, message => this.handleInboundMessage(message));
57
61
  this.subscribedQueues.add(queue);
58
62
  }
59
63
  } catch (error) {
@@ -200,29 +204,48 @@ export class RabbitMqMicroserviceTransport {
200
204
  * @returns A promise that resolves once shutdown cleanup completes.
201
205
  */
202
206
  async close() {
203
- this.closing = true;
204
- let closeError;
205
- if (this.listenPromise) {
206
- await this.listenPromise;
207
+ if (this.closePromise) {
208
+ await this.closePromise;
209
+ return;
207
210
  }
208
- try {
209
- if (this.listening) {
210
- for (const queue of this.subscribedQueues) {
211
- try {
212
- await this.options.consumer.cancel(queue);
213
- } catch (error) {
214
- closeError ??= error;
211
+ this.closing = true;
212
+ this.closePromise = (async () => {
213
+ let closeError;
214
+ let listenError;
215
+ if (this.listenPromise) {
216
+ try {
217
+ await this.listenPromise;
218
+ } catch (error) {
219
+ listenError = error;
220
+ }
221
+ }
222
+ try {
223
+ if (this.listening) {
224
+ for (const queue of this.subscribedQueues) {
225
+ try {
226
+ await this.options.consumer.cancel(queue);
227
+ } catch (error) {
228
+ closeError ??= error;
229
+ }
215
230
  }
216
231
  }
232
+ } finally {
233
+ this.subscribedQueues.clear();
234
+ this.rejectPendingRequests(new Error('RabbitMQ microservice transport closed before response.'));
235
+ this.listening = false;
236
+ this.handler = undefined;
237
+ }
238
+ if (listenError) {
239
+ throw listenError;
217
240
  }
241
+ if (closeError) {
242
+ throw closeError;
243
+ }
244
+ })();
245
+ try {
246
+ await this.closePromise;
218
247
  } finally {
219
- this.subscribedQueues.clear();
220
- this.rejectPendingRequests(new Error('RabbitMQ microservice transport closed before response.'));
221
- this.listening = false;
222
- this.handler = undefined;
223
- }
224
- if (closeError) {
225
- throw closeError;
248
+ this.closePromise = undefined;
226
249
  }
227
250
  }
228
251
  async handleInboundMessage(rawMessage) {
@@ -256,19 +279,14 @@ export class RabbitMqMicroserviceTransport {
256
279
  return;
257
280
  }
258
281
  const replyQueue = typeof message.replyTo === 'string' && message.replyTo.length > 0 ? message.replyTo : this.responseQueue;
282
+ let payload;
259
283
  try {
260
- const payload = await this.handler({
284
+ payload = await this.handler({
261
285
  kind: 'message',
262
286
  pattern: message.pattern,
263
287
  payload: message.payload,
264
288
  requestId: message.requestId
265
289
  });
266
- await this.options.publisher.publish(replyQueue, JSON.stringify({
267
- kind: 'response',
268
- pattern: message.pattern,
269
- payload,
270
- requestId: message.requestId
271
- }));
272
290
  } catch (error) {
273
291
  const errorMessage = error instanceof Error ? error.message : 'Unhandled microservice error';
274
292
  await this.options.publisher.publish(replyQueue, JSON.stringify({
@@ -277,7 +295,14 @@ export class RabbitMqMicroserviceTransport {
277
295
  pattern: message.pattern,
278
296
  requestId: message.requestId
279
297
  }));
298
+ return;
280
299
  }
300
+ await this.options.publisher.publish(replyQueue, JSON.stringify({
301
+ kind: 'response',
302
+ pattern: message.pattern,
303
+ payload,
304
+ requestId: message.requestId
305
+ }));
281
306
  }
282
307
  handleResponse(message) {
283
308
  if (!message.requestId) {