@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 +2 -0
- package/README.md +2 -0
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +2 -6
- package/dist/transports/kafka-transport.d.ts.map +1 -1
- package/dist/transports/kafka-transport.js +3 -0
- package/dist/transports/nats-transport.d.ts +3 -3
- package/dist/transports/nats-transport.d.ts.map +1 -1
- package/dist/transports/nats-transport.js +29 -3
- package/dist/transports/rabbitmq-transport.d.ts +1 -0
- package/dist/transports/rabbitmq-transport.d.ts.map +1 -1
- package/dist/transports/rabbitmq-transport.js +54 -13
- package/package.json +4 -4
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
|
package/dist/metadata.d.ts.map
CHANGED
|
@@ -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;
|
|
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,
|
|
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
|
-
|
|
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;
|
|
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
|
|
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,
|
|
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
|
|
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;
|
|
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
|
-
|
|
100
|
-
|
|
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
|
-
|
|
112
|
-
|
|
126
|
+
settle(() => {
|
|
127
|
+
reject(error);
|
|
128
|
+
});
|
|
113
129
|
},
|
|
114
130
|
resolve: value => {
|
|
115
|
-
|
|
116
|
-
|
|
131
|
+
settle(() => {
|
|
132
|
+
resolve(value);
|
|
133
|
+
});
|
|
117
134
|
},
|
|
118
135
|
timeout
|
|
119
136
|
});
|
|
120
137
|
if (signal) {
|
|
121
138
|
if (signal.aborted) {
|
|
122
|
-
|
|
123
|
-
|
|
139
|
+
settle(() => {
|
|
140
|
+
reject(new Error('RabbitMQ request aborted before publish.'));
|
|
141
|
+
});
|
|
124
142
|
return;
|
|
125
143
|
}
|
|
126
144
|
abortHandler = () => {
|
|
127
|
-
|
|
128
|
-
|
|
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
|
|
135
|
-
|
|
136
|
-
|
|
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.
|
|
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.
|
|
72
|
-
"@fluojs/
|
|
73
|
-
"@fluojs/
|
|
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",
|