@fluojs/microservices 1.0.0-beta.1 → 1.0.0-beta.3

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/README.ko.md CHANGED
@@ -89,6 +89,8 @@ await microservice.listen();
89
89
  - Redis Streams는 `close()` 중 인스턴스별 response stream은 항상 삭제하지만, 활성 fleet 전체에서 ownership를 증명할 수 없으면 공유 request consumer group은 보수적으로 유지합니다. lease-capable listener는 coordination metadata만 정리하고, mixed/fallback fleet에서는 살아 있는 다른 listener가 여전히 필요로 할 수 있으므로 공유 request group을 제거하지 않습니다.
90
90
  - `messageRetentionMaxLen`과 `eventRetentionMaxLen`은 고급 opt-in 설정으로 남아 있습니다. 이를 켜면 Redis가 ACK 전 pending live-stream 엔트리를 먼저 trim할 수 있으므로 broker-managed recovery 보장을 일부 포기하는 운영 판단이 됩니다.
91
91
  - RabbitMQ 요청-응답은 기본적으로 인스턴스별 response queue를 사용합니다. 공유 reply topology를 의도적으로 운영할 때만 `responseQueue`를 명시적으로 지정하세요.
92
+ - caller-owned broker collaborator는 shutdown 중에도 caller-owned로 유지됩니다. NATS, Kafka, RabbitMQ transport는 subscription/consumer를 분리하고 in-flight 요청을 reject하지만, 애플리케이션이 넘긴 client, producer, consumer, publisher, 외부 connection 객체를 close/disconnect하지 않습니다.
93
+ - `AbortSignal`을 받는 요청-응답 transport는 이미 abort된 send를 publish 전에 reject하고, 나중에 abort된 in-flight send도 reject합니다. `close()`가 시작된 뒤에는 shutdown 중인 lifecycle에 새 작업을 publish하지 않고 `send()`/`emit()`을 reject합니다.
92
94
  - transport logger를 통해 이벤트 핸들러 실패를 기록하는 경로(`RedisPubSubMicroserviceTransport`, `RedisStreamsMicroserviceTransport`, `NatsMicroserviceTransport`, `MqttMicroserviceTransport`, gRPC event emit)는 끝까지 logger-driven observability를 유지합니다. transport logger를 주입하지 않으면 fluo는 해당 실패를 raw `console.error` fallback으로 복제하지 않습니다.
93
95
 
94
96
  ## 공통 패턴
package/README.md CHANGED
@@ -86,6 +86,8 @@ Microservice handlers fully support fluo's DI scopes. Request-scoped providers a
86
86
  - Redis Streams always deletes each per-consumer response stream during `close()`, but it retains the shared request consumer group conservatively once ownership cannot be proven across the active fleet. Lease-capable listeners clean up only their coordination metadata, and mixed or fallback listener fleets keep the shared request group in place so one peer cannot destroy a group that another live listener still needs.
87
87
  - `messageRetentionMaxLen` and `eventRetentionMaxLen` remain available as advanced opt-in knobs. Enabling them can trade away broker-managed recovery guarantees because Redis may trim pending live-stream entries before they are acknowledged.
88
88
  - RabbitMQ request/reply uses an instance-scoped response queue by default. Pass `responseQueue` explicitly only when you intentionally own and coordinate a shared reply topology.
89
+ - Caller-owned broker collaborators stay caller-owned during shutdown. NATS, Kafka, and RabbitMQ transports detach their subscriptions/consumers and reject in-flight requests, but they do not close or disconnect the client, producer, consumer, publisher, or external connection objects supplied by the application.
90
+ - Request-response transports that accept `AbortSignal` reject already-aborted sends before publishing and reject in-flight sends on later abort. Once `close()` starts, transports reject new `send()`/`emit()` calls instead of publishing work into a shutting-down lifecycle.
89
91
  - Event-handler failures that flow through the transport logger (`RedisPubSubMicroserviceTransport`, `RedisStreamsMicroserviceTransport`, `NatsMicroserviceTransport`, `MqttMicroserviceTransport`, and gRPC event emits) remain logger-driven. If you do not inject a transport logger, fluo does not mirror those failures through a raw `console.error` fallback.
90
92
 
91
93
  ## Common Patterns
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAuClD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,mBAAmB,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,CAKvH;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,GACb,KAAK,CAAC;IAAE,QAAQ,EAAE,eAAe,CAAC;IAAC,WAAW,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAkBxE;AAED,eAAO,MAAM,0BAA0B,QAAkC,CAAC"}
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA+BlD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,mBAAmB,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,CAKvH;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,GACb,KAAK,CAAC;IAAE,QAAQ,EAAE,eAAe,CAAC;IAAC,WAAW,EAAE,mBAAmB,CAAA;CAAE,CAAC,CAkBxE;AAED,eAAO,MAAM,0BAA0B,QAAkC,CAAC"}
package/dist/metadata.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ensureSymbolMetadataPolyfill, metadataSymbol } from '@fluojs/core/internal';
1
+ import { ensureSymbolMetadataPolyfill, getStandardConstructorMetadataBag } from '@fluojs/core/internal';
2
2
  void ensureSymbolMetadataPolyfill();
3
3
  const standardMicroserviceMetadataKey = Symbol.for('fluo.microservices.standard.handler');
4
4
  const handlerMetadataStore = new WeakMap();
@@ -8,12 +8,8 @@ function cloneHandlerMetadata(metadata) {
8
8
  pattern: metadata.pattern
9
9
  };
10
10
  }
11
- function getStandardMetadataBag(target) {
12
- return target[metadataSymbol];
13
- }
14
11
  function getStandardHandlerMap(target) {
15
- const constructor = target.constructor;
16
- return constructor ? getStandardMetadataBag(constructor)?.[standardMicroserviceMetadataKey] : undefined;
12
+ return getStandardConstructorMetadataBag(target)?.[standardMicroserviceMetadataKey];
17
13
  }
18
14
  function getOrCreateHandlerMap(target) {
19
15
  let map = handlerMetadataStore.get(target);
@@ -1 +1 @@
1
- {"version":3,"file":"kafka-transport.d.ts","sourceRoot":"","sources":["../../src/transports/kafka-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE3E,UAAU,iBAAiB;IACzB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5F,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAED,UAAU,iBAAiB;IACzB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAgBD,gEAAgE;AAChE,MAAM,WAAW,iCAAiC;IAChD,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,qBAAa,0BAA2B,YAAW,qBAAqB;IAgB1D,OAAO,CAAC,QAAQ,CAAC,OAAO;IAfpC,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,CAAqC;IAC7D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC;;;;OAIG;gBAC0B,OAAO,EAAE,iCAAiC;IAOvE;;;;;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;IA6ErF;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5D;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAgCd,kBAAkB;YAkBlB,oBAAoB;YAwCpB,qBAAqB;IAqBnC,OAAO,CAAC,YAAY;CAqCrB"}
1
+ {"version":3,"file":"kafka-transport.d.ts","sourceRoot":"","sources":["../../src/transports/kafka-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE3E,UAAU,iBAAiB;IACzB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5F,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAED,UAAU,iBAAiB;IACzB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAgBD,gEAAgE;AAChE,MAAM,WAAW,iCAAiC;IAChD,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,qBAAa,0BAA2B,YAAW,qBAAqB;IAgB1D,OAAO,CAAC,QAAQ,CAAC,OAAO;IAfpC,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,CAAqC;IAC7D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC;;;;OAIG;gBAC0B,OAAO,EAAE,iCAAiC;IAOvE;;;;;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;IA6ErF;;;;;;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;YAgCd,kBAAkB;YAkBlB,oBAAoB;YAwCpB,qBAAqB;IAqBnC,OAAO,CAAC,YAAY;CAqCrB"}
@@ -157,6 +157,9 @@ export class KafkaMicroserviceTransport {
157
157
  * @returns A promise that resolves once the event is published.
158
158
  */
159
159
  async emit(pattern, payload) {
160
+ if (this.closing) {
161
+ throw new Error('KafkaMicroserviceTransport is closing. Wait for close() to complete before emit().');
162
+ }
160
163
  const message = {
161
164
  kind: 'event',
162
165
  pattern,
@@ -11,7 +11,6 @@ interface NatsSubscriptionLike {
11
11
  unsubscribe(): void;
12
12
  }
13
13
  interface NatsLike {
14
- close?(): void;
15
14
  publish(subject: string, payload: Uint8Array): void;
16
15
  request(subject: string, payload: Uint8Array, options?: {
17
16
  timeout?: number;
@@ -66,9 +65,10 @@ export declare class NatsMicroserviceTransport implements MicroserviceTransport
66
65
  *
67
66
  * @param pattern Pattern identifying the remote message handler.
68
67
  * @param payload Serializable request payload.
68
+ * @param signal Optional abort signal used to cancel the request.
69
69
  * @returns The decoded remote handler response payload.
70
70
  */
71
- send(pattern: string, payload: unknown): Promise<unknown>;
71
+ send(pattern: string, payload: unknown, signal?: AbortSignal): Promise<unknown>;
72
72
  /**
73
73
  * Emits one fire-and-forget event through the configured NATS event subject.
74
74
  *
@@ -78,7 +78,7 @@ export declare class NatsMicroserviceTransport implements MicroserviceTransport
78
78
  */
79
79
  emit(pattern: string, payload: unknown): Promise<void>;
80
80
  /**
81
- * Unsubscribes from NATS subjects and closes the client when supported.
81
+ * Unsubscribes from NATS subjects without closing the caller-owned client.
82
82
  *
83
83
  * @returns A promise that resolves once shutdown cleanup completes.
84
84
  */
@@ -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,KAAK,CAAC,IAAI,IAAI,CAAC;IACf,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;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAmE/D;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5D;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YA2Bd,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;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"}
@@ -69,9 +69,10 @@ export class NatsMicroserviceTransport {
69
69
  *
70
70
  * @param pattern Pattern identifying the remote message handler.
71
71
  * @param payload Serializable request payload.
72
+ * @param signal Optional abort signal used to cancel the request.
72
73
  * @returns The decoded remote handler response payload.
73
74
  */
74
- async send(pattern, payload) {
75
+ async send(pattern, payload, signal) {
75
76
  if (this.closing) {
76
77
  throw new Error('NATS microservice transport is closing. Wait for close() to complete before send().');
77
78
  }
@@ -85,12 +86,16 @@ export class NatsMicroserviceTransport {
85
86
  };
86
87
  const requestId = crypto.randomUUID();
87
88
  return await new Promise((resolve, reject) => {
89
+ let abortHandler;
88
90
  let settled = false;
89
91
  const cleanup = () => {
90
92
  if (settled) {
91
93
  return;
92
94
  }
93
95
  settled = true;
96
+ if (signal && abortHandler) {
97
+ signal.removeEventListener('abort', abortHandler);
98
+ }
94
99
  this.pending.delete(requestId);
95
100
  };
96
101
  const entry = {
@@ -104,7 +109,26 @@ export class NatsMicroserviceTransport {
104
109
  }
105
110
  };
106
111
  this.pending.set(requestId, entry);
112
+ if (signal) {
113
+ if (signal.aborted) {
114
+ entry.reject(new Error('NATS request aborted before publish.'));
115
+ return;
116
+ }
117
+ abortHandler = () => {
118
+ entry.reject(new Error('NATS request aborted.'));
119
+ };
120
+ signal.addEventListener('abort', abortHandler, {
121
+ once: true
122
+ });
123
+ }
107
124
  void Promise.resolve().then(async () => {
125
+ if (settled) {
126
+ return;
127
+ }
128
+ if (signal?.aborted) {
129
+ entry.reject(new Error('NATS request aborted before publish.'));
130
+ return;
131
+ }
108
132
  if (this.closing) {
109
133
  entry.reject(new Error('NATS microservice transport closed before request dispatch.'));
110
134
  return;
@@ -132,6 +156,9 @@ export class NatsMicroserviceTransport {
132
156
  * @returns A promise that resolves once the event is published.
133
157
  */
134
158
  async emit(pattern, payload) {
159
+ if (this.closing) {
160
+ throw new Error('NATS microservice transport is closing. Wait for close() to complete before emit().');
161
+ }
135
162
  const event = {
136
163
  kind: 'event',
137
164
  pattern,
@@ -141,7 +168,7 @@ export class NatsMicroserviceTransport {
141
168
  }
142
169
 
143
170
  /**
144
- * Unsubscribes from NATS subjects and closes the client when supported.
171
+ * Unsubscribes from NATS subjects without closing the caller-owned client.
145
172
  *
146
173
  * @returns A promise that resolves once shutdown cleanup completes.
147
174
  */
@@ -152,7 +179,6 @@ export class NatsMicroserviceTransport {
152
179
  for (const subscription of this.subscriptions) {
153
180
  subscription.unsubscribe();
154
181
  }
155
- this.options.client.close?.();
156
182
  } catch (error) {
157
183
  closeError = error;
158
184
  } finally {
@@ -23,6 +23,7 @@ export interface RabbitMqMicroserviceTransportOptions {
23
23
  */
24
24
  export declare class RabbitMqMicroserviceTransport implements MicroserviceTransport {
25
25
  private readonly options;
26
+ private closing;
26
27
  private handler;
27
28
  private listening;
28
29
  private listenPromise;
@@ -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;IAoB7D,OAAO,CAAC,QAAQ,CAAC,OAAO;IAnBpC,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;IA2CtD;;;;;;;OAOG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAoErF;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5D;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YA6Bd,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;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"}
@@ -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
+ closing = false;
10
11
  handler;
11
12
  listening = false;
12
13
  listenPromise;
@@ -37,6 +38,7 @@ export class RabbitMqMicroserviceTransport {
37
38
  * @returns A promise that resolves once queue consumers are registered.
38
39
  */
39
40
  async listen(handler) {
41
+ this.closing = false;
40
42
  this.handler = handler;
41
43
  if (this.listening) {
42
44
  return;
@@ -79,6 +81,9 @@ export class RabbitMqMicroserviceTransport {
79
81
  * @returns The remote handler response payload.
80
82
  */
81
83
  async send(pattern, payload, signal) {
84
+ if (this.closing) {
85
+ throw new Error('RabbitMqMicroserviceTransport is closing. Wait for close() to complete before send().');
86
+ }
82
87
  if (this.listenPromise) {
83
88
  await this.listenPromise;
84
89
  }
@@ -95,9 +100,11 @@ export class RabbitMqMicroserviceTransport {
95
100
  };
96
101
  return await new Promise((resolve, reject) => {
97
102
  let abortHandler;
103
+ let settled = false;
98
104
  const timeout = setTimeout(() => {
99
- cleanup();
100
- reject(new Error(`RabbitMQ request timed out after ${this.requestTimeoutMs}ms waiting for pattern "${pattern}".`));
105
+ settle(() => {
106
+ reject(new Error(`RabbitMQ request timed out after ${this.requestTimeoutMs}ms waiting for pattern "${pattern}".`));
107
+ });
101
108
  }, this.requestTimeoutMs);
102
109
  const cleanup = () => {
103
110
  clearTimeout(timeout);
@@ -106,34 +113,64 @@ export class RabbitMqMicroserviceTransport {
106
113
  signal.removeEventListener('abort', abortHandler);
107
114
  }
108
115
  };
116
+ const settle = callback => {
117
+ if (settled) {
118
+ return;
119
+ }
120
+ settled = true;
121
+ cleanup();
122
+ callback();
123
+ };
109
124
  this.pending.set(requestId, {
110
125
  reject: error => {
111
- cleanup();
112
- reject(error);
126
+ settle(() => {
127
+ reject(error);
128
+ });
113
129
  },
114
130
  resolve: value => {
115
- cleanup();
116
- resolve(value);
131
+ settle(() => {
132
+ resolve(value);
133
+ });
117
134
  },
118
135
  timeout
119
136
  });
120
137
  if (signal) {
121
138
  if (signal.aborted) {
122
- cleanup();
123
- reject(new Error('RabbitMQ request aborted before publish.'));
139
+ settle(() => {
140
+ reject(new Error('RabbitMQ request aborted before publish.'));
141
+ });
124
142
  return;
125
143
  }
126
144
  abortHandler = () => {
127
- cleanup();
128
- reject(new Error('RabbitMQ request aborted.'));
145
+ settle(() => {
146
+ reject(new Error('RabbitMQ request aborted.'));
147
+ });
129
148
  };
130
149
  signal.addEventListener('abort', abortHandler, {
131
150
  once: true
132
151
  });
133
152
  }
134
- void this.options.publisher.publish(this.messageQueue, JSON.stringify(message)).catch(error => {
135
- cleanup();
136
- reject(error instanceof Error ? error : new Error('Failed to publish RabbitMQ request.'));
153
+ void Promise.resolve().then(async () => {
154
+ if (settled) {
155
+ return;
156
+ }
157
+ if (signal?.aborted) {
158
+ settle(() => {
159
+ reject(new Error('RabbitMQ request aborted before publish.'));
160
+ });
161
+ return;
162
+ }
163
+ if (this.closing) {
164
+ settle(() => {
165
+ reject(new Error('RabbitMQ microservice transport closed before request dispatch.'));
166
+ });
167
+ return;
168
+ }
169
+ await this.options.publisher.publish(this.messageQueue, JSON.stringify(message));
170
+ }).catch(error => {
171
+ settle(() => {
172
+ reject(error instanceof Error ? error : new Error('Failed to publish RabbitMQ request.'));
173
+ });
137
174
  });
138
175
  });
139
176
  }
@@ -146,6 +183,9 @@ export class RabbitMqMicroserviceTransport {
146
183
  * @returns A promise that resolves once the event is published.
147
184
  */
148
185
  async emit(pattern, payload) {
186
+ if (this.closing) {
187
+ throw new Error('RabbitMqMicroserviceTransport is closing. Wait for close() to complete before emit().');
188
+ }
149
189
  const message = {
150
190
  kind: 'event',
151
191
  pattern,
@@ -160,6 +200,7 @@ export class RabbitMqMicroserviceTransport {
160
200
  * @returns A promise that resolves once shutdown cleanup completes.
161
201
  */
162
202
  async close() {
203
+ this.closing = true;
163
204
  let closeError;
164
205
  if (this.listenPromise) {
165
206
  await this.listenPromise;
package/package.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "grpc",
14
14
  "mqtt"
15
15
  ],
16
- "version": "1.0.0-beta.1",
16
+ "version": "1.0.0-beta.3",
17
17
  "private": false,
18
18
  "license": "MIT",
19
19
  "repository": {
@@ -68,9 +68,9 @@
68
68
  "dist"
69
69
  ],
70
70
  "dependencies": {
71
- "@fluojs/core": "^1.0.0-beta.1",
72
- "@fluojs/runtime": "^1.0.0-beta.1",
73
- "@fluojs/di": "^1.0.0-beta.1"
71
+ "@fluojs/core": "^1.0.0-beta.2",
72
+ "@fluojs/di": "^1.0.0-beta.3",
73
+ "@fluojs/runtime": "^1.0.0-beta.3"
74
74
  },
75
75
  "peerDependencies": {
76
76
  "@grpc/grpc-js": "^1.0.0",