@horizon-republic/nestjs-jetstream 2.5.1 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -5
- package/dist/index.cjs +620 -307
- package/dist/index.d.cts +239 -62
- package/dist/index.d.ts +239 -62
- package/dist/index.js +621 -317
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -42,9 +42,14 @@ __export(index_exports, {
|
|
|
42
42
|
JetstreamRecordBuilder: () => JetstreamRecordBuilder,
|
|
43
43
|
JetstreamStrategy: () => JetstreamStrategy,
|
|
44
44
|
JsonCodec: () => JsonCodec,
|
|
45
|
+
MessageKind: () => MessageKind,
|
|
46
|
+
PatternPrefix: () => PatternPrefix,
|
|
45
47
|
RpcContext: () => RpcContext,
|
|
48
|
+
StreamKind: () => StreamKind,
|
|
46
49
|
TransportEvent: () => TransportEvent,
|
|
47
50
|
getClientToken: () => getClientToken,
|
|
51
|
+
isCoreRpcMode: () => isCoreRpcMode,
|
|
52
|
+
isJetStreamRpcMode: () => isJetStreamRpcMode,
|
|
48
53
|
toNanos: () => toNanos
|
|
49
54
|
});
|
|
50
55
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -58,6 +63,11 @@ var import_microservices = require("@nestjs/microservices");
|
|
|
58
63
|
var import_nats2 = require("nats");
|
|
59
64
|
|
|
60
65
|
// src/interfaces/hooks.interface.ts
|
|
66
|
+
var MessageKind = /* @__PURE__ */ ((MessageKind2) => {
|
|
67
|
+
MessageKind2["Event"] = "event";
|
|
68
|
+
MessageKind2["Rpc"] = "rpc";
|
|
69
|
+
return MessageKind2;
|
|
70
|
+
})(MessageKind || {});
|
|
61
71
|
var TransportEvent = /* @__PURE__ */ ((TransportEvent2) => {
|
|
62
72
|
TransportEvent2["Connect"] = "connect";
|
|
63
73
|
TransportEvent2["Disconnect"] = "disconnect";
|
|
@@ -71,6 +81,15 @@ var TransportEvent = /* @__PURE__ */ ((TransportEvent2) => {
|
|
|
71
81
|
return TransportEvent2;
|
|
72
82
|
})(TransportEvent || {});
|
|
73
83
|
|
|
84
|
+
// src/interfaces/stream.interface.ts
|
|
85
|
+
var StreamKind = /* @__PURE__ */ ((StreamKind2) => {
|
|
86
|
+
StreamKind2["Event"] = "ev";
|
|
87
|
+
StreamKind2["Command"] = "cmd";
|
|
88
|
+
StreamKind2["Broadcast"] = "broadcast";
|
|
89
|
+
StreamKind2["Ordered"] = "ordered";
|
|
90
|
+
return StreamKind2;
|
|
91
|
+
})(StreamKind || {});
|
|
92
|
+
|
|
74
93
|
// src/jetstream.constants.ts
|
|
75
94
|
var import_nats = require("nats");
|
|
76
95
|
var JETSTREAM_OPTIONS = /* @__PURE__ */ Symbol("JETSTREAM_OPTIONS");
|
|
@@ -95,7 +114,7 @@ var baseStreamConfig = {
|
|
|
95
114
|
num_replicas: 1,
|
|
96
115
|
discard: import_nats.DiscardPolicy.Old,
|
|
97
116
|
allow_direct: true,
|
|
98
|
-
compression: import_nats.StoreCompression.
|
|
117
|
+
compression: import_nats.StoreCompression.S2
|
|
99
118
|
};
|
|
100
119
|
var DEFAULT_EVENT_STREAM_CONFIG = {
|
|
101
120
|
...baseStreamConfig,
|
|
@@ -187,13 +206,20 @@ var internalName = (name) => `${name}__microservice`;
|
|
|
187
206
|
var buildSubject = (serviceName, kind, pattern) => `${internalName(serviceName)}.${kind}.${pattern}`;
|
|
188
207
|
var buildBroadcastSubject = (pattern) => `broadcast.${pattern}`;
|
|
189
208
|
var streamName = (serviceName, kind) => {
|
|
190
|
-
if (kind === "broadcast") return "broadcast-stream";
|
|
209
|
+
if (kind === "broadcast" /* Broadcast */) return "broadcast-stream";
|
|
191
210
|
return `${internalName(serviceName)}_${kind}-stream`;
|
|
192
211
|
};
|
|
193
212
|
var consumerName = (serviceName, kind) => {
|
|
194
|
-
if (kind === "broadcast") return `${internalName(serviceName)}_broadcast-consumer`;
|
|
213
|
+
if (kind === "broadcast" /* Broadcast */) return `${internalName(serviceName)}_broadcast-consumer`;
|
|
195
214
|
return `${internalName(serviceName)}_${kind}-consumer`;
|
|
196
215
|
};
|
|
216
|
+
var PatternPrefix = /* @__PURE__ */ ((PatternPrefix2) => {
|
|
217
|
+
PatternPrefix2["Broadcast"] = "broadcast:";
|
|
218
|
+
PatternPrefix2["Ordered"] = "ordered:";
|
|
219
|
+
return PatternPrefix2;
|
|
220
|
+
})(PatternPrefix || {});
|
|
221
|
+
var isJetStreamRpcMode = (rpc) => rpc?.mode === "jetstream";
|
|
222
|
+
var isCoreRpcMode = (rpc) => !rpc || rpc.mode === "core";
|
|
197
223
|
|
|
198
224
|
// src/client/jetstream.record.ts
|
|
199
225
|
var JetstreamRecord = class {
|
|
@@ -308,10 +334,13 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
|
|
|
308
334
|
this.codec = codec;
|
|
309
335
|
this.eventBus = eventBus;
|
|
310
336
|
this.targetName = targetServiceName;
|
|
337
|
+
this.callerName = internalName(this.rootOptions.name);
|
|
311
338
|
}
|
|
312
339
|
logger = new import_common.Logger("Jetstream:Client");
|
|
313
340
|
/** Target service name this client sends messages to. */
|
|
314
341
|
targetName;
|
|
342
|
+
/** Pre-cached caller name derived from rootOptions.name, computed once in constructor. */
|
|
343
|
+
callerName;
|
|
315
344
|
/** Shared inbox for JetStream-mode RPC responses. */
|
|
316
345
|
inbox = null;
|
|
317
346
|
inboxSubscription = null;
|
|
@@ -331,7 +360,7 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
|
|
|
331
360
|
*/
|
|
332
361
|
async connect() {
|
|
333
362
|
const nc = await this.connection.getConnection();
|
|
334
|
-
if (this.
|
|
363
|
+
if (isJetStreamRpcMode(this.rootOptions.rpc) && !this.inboxSubscription) {
|
|
335
364
|
this.setupInbox(nc);
|
|
336
365
|
}
|
|
337
366
|
this.statusSubscription ??= this.connection.status$.subscribe((status) => {
|
|
@@ -366,11 +395,11 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
|
|
|
366
395
|
* depending on the subject prefix.
|
|
367
396
|
*/
|
|
368
397
|
async dispatchEvent(packet) {
|
|
369
|
-
|
|
398
|
+
await this.connect();
|
|
370
399
|
const { data, hdrs, messageId } = this.extractRecordData(packet.data);
|
|
371
400
|
const subject = this.buildEventSubject(packet.pattern);
|
|
372
401
|
const msgHeaders = this.buildHeaders(hdrs, { subject });
|
|
373
|
-
const ack = await
|
|
402
|
+
const ack = await this.connection.getJetStreamClient().publish(subject, this.codec.encode(data), {
|
|
374
403
|
headers: msgHeaders,
|
|
375
404
|
msgID: messageId ?? crypto.randomUUID()
|
|
376
405
|
});
|
|
@@ -386,26 +415,23 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
|
|
|
386
415
|
* JetStream mode: publishes to stream + waits for inbox response.
|
|
387
416
|
*/
|
|
388
417
|
publish(packet, callback) {
|
|
389
|
-
const subject = buildSubject(this.targetName, "cmd"
|
|
418
|
+
const subject = buildSubject(this.targetName, "cmd" /* Command */, packet.pattern);
|
|
390
419
|
const { data, hdrs, timeout, messageId } = this.extractRecordData(packet.data);
|
|
391
420
|
const onUnhandled = (err) => {
|
|
392
421
|
this.logger.error("Unhandled publish error:", err);
|
|
393
422
|
callback({ err: new Error("Internal transport error"), response: null, isDisposed: true });
|
|
394
423
|
};
|
|
395
424
|
let jetStreamCorrelationId = null;
|
|
396
|
-
if (this.
|
|
425
|
+
if (isCoreRpcMode(this.rootOptions.rpc)) {
|
|
397
426
|
this.publishCoreRpc(subject, data, hdrs, timeout, callback).catch(onUnhandled);
|
|
398
427
|
} else {
|
|
399
428
|
jetStreamCorrelationId = crypto.randomUUID();
|
|
400
|
-
this.publishJetStreamRpc(
|
|
401
|
-
|
|
402
|
-
data,
|
|
403
|
-
hdrs,
|
|
429
|
+
this.publishJetStreamRpc(subject, data, callback, {
|
|
430
|
+
headers: hdrs,
|
|
404
431
|
timeout,
|
|
405
|
-
|
|
406
|
-
jetStreamCorrelationId,
|
|
432
|
+
correlationId: jetStreamCorrelationId,
|
|
407
433
|
messageId
|
|
408
|
-
).catch(onUnhandled);
|
|
434
|
+
}).catch(onUnhandled);
|
|
409
435
|
}
|
|
410
436
|
return () => {
|
|
411
437
|
if (jetStreamCorrelationId) {
|
|
@@ -442,35 +468,46 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
|
|
|
442
468
|
}
|
|
443
469
|
}
|
|
444
470
|
/** JetStream mode: publish to stream + wait for inbox response. */
|
|
445
|
-
async publishJetStreamRpc(subject, data,
|
|
446
|
-
const
|
|
471
|
+
async publishJetStreamRpc(subject, data, callback, options) {
|
|
472
|
+
const { headers: customHeaders, correlationId, messageId } = options;
|
|
473
|
+
const effectiveTimeout = options.timeout ?? this.getRpcTimeout();
|
|
447
474
|
this.pendingMessages.set(correlationId, callback);
|
|
448
|
-
const timeoutId = setTimeout(() => {
|
|
449
|
-
if (!this.pendingMessages.has(correlationId)) return;
|
|
450
|
-
this.pendingTimeouts.delete(correlationId);
|
|
451
|
-
this.pendingMessages.delete(correlationId);
|
|
452
|
-
this.logger.error(`JetStream RPC timeout (${effectiveTimeout}ms): ${subject}`);
|
|
453
|
-
this.eventBus.emit("rpcTimeout" /* RpcTimeout */, subject, correlationId);
|
|
454
|
-
callback({ err: new Error("RPC timeout"), response: null, isDisposed: true });
|
|
455
|
-
}, effectiveTimeout);
|
|
456
|
-
this.pendingTimeouts.set(correlationId, timeoutId);
|
|
457
475
|
try {
|
|
458
|
-
|
|
476
|
+
await this.connect();
|
|
477
|
+
if (!this.pendingMessages.has(correlationId)) return;
|
|
459
478
|
if (!this.inbox) {
|
|
460
|
-
|
|
479
|
+
this.pendingMessages.delete(correlationId);
|
|
480
|
+
callback({
|
|
481
|
+
err: new Error("Inbox not initialized \u2014 JetStream RPC mode requires a connected inbox"),
|
|
482
|
+
response: null,
|
|
483
|
+
isDisposed: true
|
|
484
|
+
});
|
|
485
|
+
return;
|
|
461
486
|
}
|
|
487
|
+
const timeoutId = setTimeout(() => {
|
|
488
|
+
if (!this.pendingMessages.has(correlationId)) return;
|
|
489
|
+
this.pendingTimeouts.delete(correlationId);
|
|
490
|
+
this.pendingMessages.delete(correlationId);
|
|
491
|
+
this.logger.error(`JetStream RPC timeout (${effectiveTimeout}ms): ${subject}`);
|
|
492
|
+
this.eventBus.emit("rpcTimeout" /* RpcTimeout */, subject, correlationId);
|
|
493
|
+
callback({ err: new Error("RPC timeout"), response: null, isDisposed: true });
|
|
494
|
+
}, effectiveTimeout);
|
|
495
|
+
this.pendingTimeouts.set(correlationId, timeoutId);
|
|
462
496
|
const hdrs = this.buildHeaders(customHeaders, {
|
|
463
497
|
subject,
|
|
464
498
|
correlationId,
|
|
465
499
|
replyTo: this.inbox
|
|
466
500
|
});
|
|
467
|
-
await
|
|
501
|
+
await this.connection.getJetStreamClient().publish(subject, this.codec.encode(data), {
|
|
468
502
|
headers: hdrs,
|
|
469
503
|
msgID: messageId ?? crypto.randomUUID()
|
|
470
504
|
});
|
|
471
505
|
} catch (err) {
|
|
472
|
-
|
|
473
|
-
|
|
506
|
+
const existingTimeout = this.pendingTimeouts.get(correlationId);
|
|
507
|
+
if (existingTimeout) {
|
|
508
|
+
clearTimeout(existingTimeout);
|
|
509
|
+
this.pendingTimeouts.delete(correlationId);
|
|
510
|
+
}
|
|
474
511
|
if (!this.pendingMessages.has(correlationId)) return;
|
|
475
512
|
this.pendingMessages.delete(correlationId);
|
|
476
513
|
const error = err instanceof Error ? err : new Error("Unknown error");
|
|
@@ -546,19 +583,23 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
|
|
|
546
583
|
}
|
|
547
584
|
/** Build event subject — workqueue, broadcast, or ordered. */
|
|
548
585
|
buildEventSubject(pattern) {
|
|
549
|
-
if (pattern.startsWith("broadcast:")) {
|
|
550
|
-
return buildBroadcastSubject(pattern.slice("broadcast:"
|
|
551
|
-
}
|
|
552
|
-
if (pattern.startsWith("ordered:")) {
|
|
553
|
-
return buildSubject(
|
|
586
|
+
if (pattern.startsWith("broadcast:" /* Broadcast */)) {
|
|
587
|
+
return buildBroadcastSubject(pattern.slice("broadcast:" /* Broadcast */.length));
|
|
588
|
+
}
|
|
589
|
+
if (pattern.startsWith("ordered:" /* Ordered */)) {
|
|
590
|
+
return buildSubject(
|
|
591
|
+
this.targetName,
|
|
592
|
+
"ordered" /* Ordered */,
|
|
593
|
+
pattern.slice("ordered:" /* Ordered */.length)
|
|
594
|
+
);
|
|
554
595
|
}
|
|
555
|
-
return buildSubject(this.targetName, "ev"
|
|
596
|
+
return buildSubject(this.targetName, "ev" /* Event */, pattern);
|
|
556
597
|
}
|
|
557
598
|
/** Build NATS headers merging custom headers with transport headers. */
|
|
558
599
|
buildHeaders(customHeaders, transport) {
|
|
559
600
|
const hdrs = (0, import_nats2.headers)();
|
|
560
601
|
hdrs.set("x-subject" /* Subject */, transport.subject);
|
|
561
|
-
hdrs.set("x-caller-name" /* CallerName */,
|
|
602
|
+
hdrs.set("x-caller-name" /* CallerName */, this.callerName);
|
|
562
603
|
if (transport.correlationId) {
|
|
563
604
|
hdrs.set("x-correlation-id" /* CorrelationId */, transport.correlationId);
|
|
564
605
|
}
|
|
@@ -584,15 +625,9 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
|
|
|
584
625
|
}
|
|
585
626
|
return { data: rawData, hdrs: null, timeout: void 0, messageId: void 0 };
|
|
586
627
|
}
|
|
587
|
-
isCoreRpcMode() {
|
|
588
|
-
return !this.rootOptions.rpc || this.rootOptions.rpc.mode === "core";
|
|
589
|
-
}
|
|
590
|
-
isJetStreamRpcMode() {
|
|
591
|
-
return this.rootOptions.rpc?.mode === "jetstream";
|
|
592
|
-
}
|
|
593
628
|
getRpcTimeout() {
|
|
594
629
|
if (!this.rootOptions.rpc) return DEFAULT_RPC_TIMEOUT;
|
|
595
|
-
const defaultTimeout = this.
|
|
630
|
+
const defaultTimeout = isJetStreamRpcMode(this.rootOptions.rpc) ? DEFAULT_JETSTREAM_RPC_TIMEOUT : DEFAULT_RPC_TIMEOUT;
|
|
596
631
|
return this.rootOptions.rpc.timeout ?? defaultTimeout;
|
|
597
632
|
}
|
|
598
633
|
};
|
|
@@ -613,6 +648,10 @@ var JsonCodec = class {
|
|
|
613
648
|
var import_common2 = require("@nestjs/common");
|
|
614
649
|
var import_nats4 = require("nats");
|
|
615
650
|
var import_rxjs = require("rxjs");
|
|
651
|
+
var DEFAULT_OPTIONS = {
|
|
652
|
+
maxReconnectAttempts: -1,
|
|
653
|
+
reconnectTimeWait: 1e3
|
|
654
|
+
};
|
|
616
655
|
var ConnectionProvider = class {
|
|
617
656
|
constructor(options, eventBus) {
|
|
618
657
|
this.options = options;
|
|
@@ -632,6 +671,7 @@ var ConnectionProvider = class {
|
|
|
632
671
|
logger = new import_common2.Logger("Jetstream:Connection");
|
|
633
672
|
connection = null;
|
|
634
673
|
connectionPromise = null;
|
|
674
|
+
jsClient = null;
|
|
635
675
|
jsmInstance = null;
|
|
636
676
|
jsmPromise = null;
|
|
637
677
|
/**
|
|
@@ -670,6 +710,22 @@ var ConnectionProvider = class {
|
|
|
670
710
|
});
|
|
671
711
|
return this.jsmPromise;
|
|
672
712
|
}
|
|
713
|
+
/**
|
|
714
|
+
* Get a cached JetStream client.
|
|
715
|
+
*
|
|
716
|
+
* Invalidated automatically on reconnect and shutdown so consumers always
|
|
717
|
+
* operate against the live connection.
|
|
718
|
+
*
|
|
719
|
+
* @returns The cached JetStreamClient.
|
|
720
|
+
* @throws Error if the connection has not been established yet.
|
|
721
|
+
*/
|
|
722
|
+
getJetStreamClient() {
|
|
723
|
+
if (!this.connection || this.connection.isClosed()) {
|
|
724
|
+
throw new Error("Not connected \u2014 call getConnection() before getJetStreamClient()");
|
|
725
|
+
}
|
|
726
|
+
this.jsClient ??= this.connection.jetstream();
|
|
727
|
+
return this.jsClient;
|
|
728
|
+
}
|
|
673
729
|
/** Direct access to the raw NATS connection, or `null` if not yet connected. */
|
|
674
730
|
get unwrap() {
|
|
675
731
|
return this.connection;
|
|
@@ -698,6 +754,7 @@ var ConnectionProvider = class {
|
|
|
698
754
|
} finally {
|
|
699
755
|
this.connection = null;
|
|
700
756
|
this.connectionPromise = null;
|
|
757
|
+
this.jsClient = null;
|
|
701
758
|
this.jsmInstance = null;
|
|
702
759
|
this.jsmPromise = null;
|
|
703
760
|
}
|
|
@@ -707,6 +764,7 @@ var ConnectionProvider = class {
|
|
|
707
764
|
const name = internalName(this.options.name);
|
|
708
765
|
try {
|
|
709
766
|
const nc = await (0, import_nats4.connect)({
|
|
767
|
+
...DEFAULT_OPTIONS,
|
|
710
768
|
...this.options.connectionOptions,
|
|
711
769
|
servers: this.options.servers,
|
|
712
770
|
name
|
|
@@ -732,6 +790,7 @@ var ConnectionProvider = class {
|
|
|
732
790
|
this.eventBus.emit("disconnect" /* Disconnect */);
|
|
733
791
|
break;
|
|
734
792
|
case import_nats4.Events.Reconnect:
|
|
793
|
+
this.jsClient = null;
|
|
735
794
|
this.jsmInstance = null;
|
|
736
795
|
this.jsmPromise = null;
|
|
737
796
|
this.eventBus.emit("reconnect" /* Reconnect */, nc.getServer());
|
|
@@ -771,6 +830,23 @@ var EventBus = class {
|
|
|
771
830
|
emit(event, ...args) {
|
|
772
831
|
const hook = this.hooks[event];
|
|
773
832
|
if (!hook) return;
|
|
833
|
+
this.callHook(event, hook, ...args);
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Hot-path optimized emit for MessageRouted events.
|
|
837
|
+
* Avoids rest/spread overhead of the generic `emit()`.
|
|
838
|
+
*/
|
|
839
|
+
emitMessageRouted(subject, kind) {
|
|
840
|
+
const hook = this.hooks["messageRouted" /* MessageRouted */];
|
|
841
|
+
if (!hook) return;
|
|
842
|
+
this.callHook(
|
|
843
|
+
"messageRouted" /* MessageRouted */,
|
|
844
|
+
hook,
|
|
845
|
+
subject,
|
|
846
|
+
kind
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
callHook(event, hook, ...args) {
|
|
774
850
|
try {
|
|
775
851
|
const result = hook(...args);
|
|
776
852
|
if (result && typeof result.catch === "function") {
|
|
@@ -850,7 +926,7 @@ JetstreamHealthIndicator = __decorateClass([
|
|
|
850
926
|
// src/server/strategy.ts
|
|
851
927
|
var import_microservices2 = require("@nestjs/microservices");
|
|
852
928
|
var JetstreamStrategy = class extends import_microservices2.Server {
|
|
853
|
-
constructor(options, connection, patternRegistry, streamProvider, consumerProvider, messageProvider, eventRouter, rpcRouter, coreRpcServer) {
|
|
929
|
+
constructor(options, connection, patternRegistry, streamProvider, consumerProvider, messageProvider, eventRouter, rpcRouter, coreRpcServer, ackWaitMap = /* @__PURE__ */ new Map()) {
|
|
854
930
|
super();
|
|
855
931
|
this.options = options;
|
|
856
932
|
this.connection = connection;
|
|
@@ -861,6 +937,7 @@ var JetstreamStrategy = class extends import_microservices2.Server {
|
|
|
861
937
|
this.eventRouter = eventRouter;
|
|
862
938
|
this.rpcRouter = rpcRouter;
|
|
863
939
|
this.coreRpcServer = coreRpcServer;
|
|
940
|
+
this.ackWaitMap = ackWaitMap;
|
|
864
941
|
}
|
|
865
942
|
transportId = /* @__PURE__ */ Symbol("jetstream-transport");
|
|
866
943
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
@@ -878,17 +955,17 @@ var JetstreamStrategy = class extends import_microservices2.Server {
|
|
|
878
955
|
}
|
|
879
956
|
this.started = true;
|
|
880
957
|
this.patternRegistry.registerHandlers(this.getHandlers());
|
|
881
|
-
const streamKinds = this.
|
|
882
|
-
const durableKinds = this.resolveDurableConsumerKinds();
|
|
958
|
+
const { streams: streamKinds, durableConsumers: durableKinds } = this.resolveRequiredKinds();
|
|
883
959
|
if (streamKinds.length > 0) {
|
|
884
960
|
await this.streamProvider.ensureStreams(streamKinds);
|
|
885
961
|
if (durableKinds.length > 0) {
|
|
886
962
|
const consumers = await this.consumerProvider.ensureConsumers(durableKinds);
|
|
963
|
+
this.populateAckWaitMap(consumers);
|
|
887
964
|
this.eventRouter.updateMaxDeliverMap(this.buildMaxDeliverMap(consumers));
|
|
888
965
|
this.messageProvider.start(consumers);
|
|
889
966
|
}
|
|
890
967
|
if (this.patternRegistry.hasOrderedHandlers()) {
|
|
891
|
-
const orderedStreamName = this.streamProvider.getStreamName("ordered");
|
|
968
|
+
const orderedStreamName = this.streamProvider.getStreamName("ordered" /* Ordered */);
|
|
892
969
|
await this.messageProvider.startOrdered(
|
|
893
970
|
orderedStreamName,
|
|
894
971
|
this.patternRegistry.getOrderedSubjects(),
|
|
@@ -898,11 +975,11 @@ var JetstreamStrategy = class extends import_microservices2.Server {
|
|
|
898
975
|
if (this.patternRegistry.hasEventHandlers() || this.patternRegistry.hasBroadcastHandlers() || this.patternRegistry.hasOrderedHandlers()) {
|
|
899
976
|
this.eventRouter.start();
|
|
900
977
|
}
|
|
901
|
-
if (this.
|
|
902
|
-
this.rpcRouter.start();
|
|
978
|
+
if (isJetStreamRpcMode(this.options.rpc) && this.patternRegistry.hasRpcHandlers()) {
|
|
979
|
+
await this.rpcRouter.start();
|
|
903
980
|
}
|
|
904
981
|
}
|
|
905
|
-
if (this.
|
|
982
|
+
if (isCoreRpcMode(this.options.rpc) && this.patternRegistry.hasRpcHandlers()) {
|
|
906
983
|
await this.coreRpcServer.start();
|
|
907
984
|
}
|
|
908
985
|
callback();
|
|
@@ -943,36 +1020,34 @@ var JetstreamStrategy = class extends import_microservices2.Server {
|
|
|
943
1020
|
getPatternRegistry() {
|
|
944
1021
|
return this.patternRegistry;
|
|
945
1022
|
}
|
|
946
|
-
/** Determine which
|
|
947
|
-
|
|
948
|
-
const
|
|
1023
|
+
/** Determine which streams and durable consumers are needed. */
|
|
1024
|
+
resolveRequiredKinds() {
|
|
1025
|
+
const streams = [];
|
|
1026
|
+
const durableConsumers = [];
|
|
949
1027
|
if (this.patternRegistry.hasEventHandlers()) {
|
|
950
|
-
|
|
1028
|
+
streams.push("ev" /* Event */);
|
|
1029
|
+
durableConsumers.push("ev" /* Event */);
|
|
951
1030
|
}
|
|
952
|
-
if (this.patternRegistry.
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
if (this.isJetStreamRpcMode() && this.patternRegistry.hasRpcHandlers()) {
|
|
956
|
-
kinds.push("cmd");
|
|
1031
|
+
if (isJetStreamRpcMode(this.options.rpc) && this.patternRegistry.hasRpcHandlers()) {
|
|
1032
|
+
streams.push("cmd" /* Command */);
|
|
1033
|
+
durableConsumers.push("cmd" /* Command */);
|
|
957
1034
|
}
|
|
958
1035
|
if (this.patternRegistry.hasBroadcastHandlers()) {
|
|
959
|
-
|
|
1036
|
+
streams.push("broadcast" /* Broadcast */);
|
|
1037
|
+
durableConsumers.push("broadcast" /* Broadcast */);
|
|
960
1038
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
/** Determine which stream kinds need durable consumers (ordered consumers are ephemeral). */
|
|
964
|
-
resolveDurableConsumerKinds() {
|
|
965
|
-
const kinds = [];
|
|
966
|
-
if (this.patternRegistry.hasEventHandlers()) {
|
|
967
|
-
kinds.push("ev");
|
|
968
|
-
}
|
|
969
|
-
if (this.isJetStreamRpcMode() && this.patternRegistry.hasRpcHandlers()) {
|
|
970
|
-
kinds.push("cmd");
|
|
1039
|
+
if (this.patternRegistry.hasOrderedHandlers()) {
|
|
1040
|
+
streams.push("ordered" /* Ordered */);
|
|
971
1041
|
}
|
|
972
|
-
|
|
973
|
-
|
|
1042
|
+
return { streams, durableConsumers };
|
|
1043
|
+
}
|
|
1044
|
+
/** Populate the shared ack_wait map from actual NATS consumer configs. */
|
|
1045
|
+
populateAckWaitMap(consumers) {
|
|
1046
|
+
for (const [kind, info] of consumers) {
|
|
1047
|
+
if (info.config.ack_wait) {
|
|
1048
|
+
this.ackWaitMap.set(kind, info.config.ack_wait);
|
|
1049
|
+
}
|
|
974
1050
|
}
|
|
975
|
-
return kinds;
|
|
976
1051
|
}
|
|
977
1052
|
/** Build max_deliver map from actual NATS consumer configs (not options). */
|
|
978
1053
|
buildMaxDeliverMap(consumers) {
|
|
@@ -986,12 +1061,6 @@ var JetstreamStrategy = class extends import_microservices2.Server {
|
|
|
986
1061
|
}
|
|
987
1062
|
return map;
|
|
988
1063
|
}
|
|
989
|
-
isCoreRpcMode() {
|
|
990
|
-
return !this.options.rpc || this.options.rpc.mode === "core";
|
|
991
|
-
}
|
|
992
|
-
isJetStreamRpcMode() {
|
|
993
|
-
return this.options.rpc?.mode === "jetstream";
|
|
994
|
-
}
|
|
995
1064
|
};
|
|
996
1065
|
|
|
997
1066
|
// src/server/core-rpc.server.ts
|
|
@@ -1001,6 +1070,13 @@ var import_nats5 = require("nats");
|
|
|
1001
1070
|
// src/context/rpc.context.ts
|
|
1002
1071
|
var import_microservices3 = require("@nestjs/microservices");
|
|
1003
1072
|
var RpcContext = class extends import_microservices3.BaseRpcContext {
|
|
1073
|
+
_shouldRetry = false;
|
|
1074
|
+
_retryDelay;
|
|
1075
|
+
_shouldTerminate = false;
|
|
1076
|
+
_terminateReason;
|
|
1077
|
+
// ---------------------------------------------------------------------------
|
|
1078
|
+
// Message accessors
|
|
1079
|
+
// ---------------------------------------------------------------------------
|
|
1004
1080
|
/**
|
|
1005
1081
|
* Get the underlying NATS message.
|
|
1006
1082
|
*
|
|
@@ -1035,6 +1111,122 @@ var RpcContext = class extends import_microservices3.BaseRpcContext {
|
|
|
1035
1111
|
isJetStream() {
|
|
1036
1112
|
return "ack" in this.args[0];
|
|
1037
1113
|
}
|
|
1114
|
+
// ---------------------------------------------------------------------------
|
|
1115
|
+
// JetStream metadata (return undefined for Core NATS messages)
|
|
1116
|
+
// ---------------------------------------------------------------------------
|
|
1117
|
+
/** How many times this message has been delivered. */
|
|
1118
|
+
getDeliveryCount() {
|
|
1119
|
+
return this.asJetStream()?.info.deliveryCount;
|
|
1120
|
+
}
|
|
1121
|
+
/** The JetStream stream this message belongs to. */
|
|
1122
|
+
getStream() {
|
|
1123
|
+
return this.asJetStream()?.info.stream;
|
|
1124
|
+
}
|
|
1125
|
+
/** The stream sequence number. */
|
|
1126
|
+
getSequence() {
|
|
1127
|
+
return this.asJetStream()?.seq;
|
|
1128
|
+
}
|
|
1129
|
+
/** The message timestamp as a `Date` (derived from `info.timestampNanos`). */
|
|
1130
|
+
getTimestamp() {
|
|
1131
|
+
const nanos = this.asJetStream()?.info.timestampNanos;
|
|
1132
|
+
return typeof nanos === "number" ? new Date(nanos / 1e6) : void 0;
|
|
1133
|
+
}
|
|
1134
|
+
/** The name of the service that published this message (from `x-caller-name` header). */
|
|
1135
|
+
getCallerName() {
|
|
1136
|
+
return this.getHeader("x-caller-name" /* CallerName */);
|
|
1137
|
+
}
|
|
1138
|
+
// ---------------------------------------------------------------------------
|
|
1139
|
+
// Handler-controlled settlement
|
|
1140
|
+
// ---------------------------------------------------------------------------
|
|
1141
|
+
/**
|
|
1142
|
+
* Signal the transport to retry (nak) this message instead of acknowledging it.
|
|
1143
|
+
*
|
|
1144
|
+
* Use for business-level retries without throwing errors.
|
|
1145
|
+
* Only affects JetStream event handlers (workqueue/broadcast).
|
|
1146
|
+
*
|
|
1147
|
+
* @param opts - Optional delay in ms before redelivery.
|
|
1148
|
+
* @throws Error if {@link terminate} was already called.
|
|
1149
|
+
*/
|
|
1150
|
+
retry(opts) {
|
|
1151
|
+
this.assertJetStream("retry");
|
|
1152
|
+
if (this._shouldTerminate) {
|
|
1153
|
+
throw new Error("Cannot retry \u2014 terminate() was already called");
|
|
1154
|
+
}
|
|
1155
|
+
this._shouldRetry = true;
|
|
1156
|
+
this._retryDelay = opts?.delayMs;
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Signal the transport to permanently reject (term) this message.
|
|
1160
|
+
*
|
|
1161
|
+
* Use when a message is no longer relevant and should not be retried or sent to DLQ.
|
|
1162
|
+
* Only affects JetStream event handlers (workqueue/broadcast).
|
|
1163
|
+
*
|
|
1164
|
+
* @param reason - Optional reason for termination (logged by NATS).
|
|
1165
|
+
* @throws Error if {@link retry} was already called.
|
|
1166
|
+
*/
|
|
1167
|
+
terminate(reason) {
|
|
1168
|
+
this.assertJetStream("terminate");
|
|
1169
|
+
if (this._shouldRetry) {
|
|
1170
|
+
throw new Error("Cannot terminate \u2014 retry() was already called");
|
|
1171
|
+
}
|
|
1172
|
+
this._shouldTerminate = true;
|
|
1173
|
+
this._terminateReason = reason;
|
|
1174
|
+
}
|
|
1175
|
+
/** Narrow to JsMsg or return null for Core messages. Used by metadata getters. */
|
|
1176
|
+
asJetStream() {
|
|
1177
|
+
return this.isJetStream() ? this.args[0] : null;
|
|
1178
|
+
}
|
|
1179
|
+
/** Ensure the message is JetStream — settlement actions are not available for Core NATS. */
|
|
1180
|
+
assertJetStream(method) {
|
|
1181
|
+
if (!this.isJetStream()) {
|
|
1182
|
+
throw new Error(`${method}() is only available for JetStream messages`);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
// ---------------------------------------------------------------------------
|
|
1186
|
+
// Transport-facing state (read by EventRouter)
|
|
1187
|
+
// ---------------------------------------------------------------------------
|
|
1188
|
+
/** @internal */
|
|
1189
|
+
get shouldRetry() {
|
|
1190
|
+
return this._shouldRetry;
|
|
1191
|
+
}
|
|
1192
|
+
/** @internal */
|
|
1193
|
+
get retryDelay() {
|
|
1194
|
+
return this._retryDelay;
|
|
1195
|
+
}
|
|
1196
|
+
/** @internal */
|
|
1197
|
+
get shouldTerminate() {
|
|
1198
|
+
return this._shouldTerminate;
|
|
1199
|
+
}
|
|
1200
|
+
/** @internal */
|
|
1201
|
+
get terminateReason() {
|
|
1202
|
+
return this._terminateReason;
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
|
|
1206
|
+
// src/utils/ack-extension.ts
|
|
1207
|
+
var DEFAULT_ACK_EXTENSION_INTERVAL = 5e3;
|
|
1208
|
+
var MIN_ACK_EXTENSION_INTERVAL = 500;
|
|
1209
|
+
var resolveAckExtensionInterval = (config, ackWaitNanos) => {
|
|
1210
|
+
if (config === false || config === void 0) return null;
|
|
1211
|
+
if (typeof config === "number") {
|
|
1212
|
+
if (!Number.isFinite(config) || config <= 0) return null;
|
|
1213
|
+
return Math.floor(config);
|
|
1214
|
+
}
|
|
1215
|
+
if (!ackWaitNanos) return DEFAULT_ACK_EXTENSION_INTERVAL;
|
|
1216
|
+
const interval = Math.floor(ackWaitNanos / 1e6 / 2);
|
|
1217
|
+
return Math.max(interval, MIN_ACK_EXTENSION_INTERVAL);
|
|
1218
|
+
};
|
|
1219
|
+
var startAckExtensionTimer = (msg, interval) => {
|
|
1220
|
+
if (interval === null || interval <= 0) return null;
|
|
1221
|
+
const timer2 = setInterval(() => {
|
|
1222
|
+
try {
|
|
1223
|
+
msg.working();
|
|
1224
|
+
} catch {
|
|
1225
|
+
}
|
|
1226
|
+
}, interval);
|
|
1227
|
+
return () => {
|
|
1228
|
+
clearInterval(timer2);
|
|
1229
|
+
};
|
|
1038
1230
|
};
|
|
1039
1231
|
|
|
1040
1232
|
// src/utils/serialize-error.ts
|
|
@@ -1047,15 +1239,20 @@ var serializeError = (err) => {
|
|
|
1047
1239
|
|
|
1048
1240
|
// src/utils/unwrap-result.ts
|
|
1049
1241
|
var import_rxjs2 = require("rxjs");
|
|
1050
|
-
var
|
|
1242
|
+
var RESOLVED_VOID = Promise.resolve(void 0);
|
|
1243
|
+
var RESOLVED_NULL = Promise.resolve(null);
|
|
1244
|
+
var unwrapResult = (result) => {
|
|
1245
|
+
if (result === void 0) return RESOLVED_VOID;
|
|
1246
|
+
if (result === null) return RESOLVED_NULL;
|
|
1051
1247
|
if ((0, import_rxjs2.isObservable)(result)) {
|
|
1052
1248
|
return subscribeToFirst(result);
|
|
1053
1249
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1250
|
+
if (typeof result.then === "function") {
|
|
1251
|
+
return result.then(
|
|
1252
|
+
(resolved) => (0, import_rxjs2.isObservable)(resolved) ? subscribeToFirst(resolved) : resolved
|
|
1253
|
+
);
|
|
1057
1254
|
}
|
|
1058
|
-
return
|
|
1255
|
+
return Promise.resolve(result);
|
|
1059
1256
|
};
|
|
1060
1257
|
var subscribeToFirst = (obs) => new Promise((resolve, reject) => {
|
|
1061
1258
|
let done = false;
|
|
@@ -1123,13 +1320,17 @@ var CoreRpcServer = class {
|
|
|
1123
1320
|
}
|
|
1124
1321
|
/** Handle an incoming Core NATS request. */
|
|
1125
1322
|
async handleRequest(msg) {
|
|
1323
|
+
if (!msg.reply) {
|
|
1324
|
+
this.logger.warn(`Ignoring fire-and-forget message on RPC subject: ${msg.subject}`);
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1126
1327
|
const handler = this.patternRegistry.getHandler(msg.subject);
|
|
1127
1328
|
if (!handler) {
|
|
1128
1329
|
this.logger.warn(`No handler for Core RPC: ${msg.subject}`);
|
|
1129
1330
|
this.respondWithError(msg, new Error(`No handler for subject: ${msg.subject}`));
|
|
1130
1331
|
return;
|
|
1131
1332
|
}
|
|
1132
|
-
this.eventBus.
|
|
1333
|
+
this.eventBus.emitMessageRouted(msg.subject, "rpc" /* Rpc */);
|
|
1133
1334
|
let data;
|
|
1134
1335
|
try {
|
|
1135
1336
|
data = this.codec.decode(msg.data);
|
|
@@ -1187,14 +1388,14 @@ var StreamProvider = class {
|
|
|
1187
1388
|
getSubjects(kind) {
|
|
1188
1389
|
const name = internalName(this.options.name);
|
|
1189
1390
|
switch (kind) {
|
|
1190
|
-
case "ev"
|
|
1191
|
-
return [`${name}
|
|
1192
|
-
case "cmd"
|
|
1193
|
-
return [`${name}
|
|
1194
|
-
case "broadcast"
|
|
1391
|
+
case "ev" /* Event */:
|
|
1392
|
+
return [`${name}.${"ev" /* Event */}.>`];
|
|
1393
|
+
case "cmd" /* Command */:
|
|
1394
|
+
return [`${name}.${"cmd" /* Command */}.>`];
|
|
1395
|
+
case "broadcast" /* Broadcast */:
|
|
1195
1396
|
return ["broadcast.>"];
|
|
1196
|
-
case "ordered"
|
|
1197
|
-
return [`${name}
|
|
1397
|
+
case "ordered" /* Ordered */:
|
|
1398
|
+
return [`${name}.${"ordered" /* Ordered */}.>`];
|
|
1198
1399
|
}
|
|
1199
1400
|
}
|
|
1200
1401
|
/** Ensure a single stream exists, creating or updating as needed. */
|
|
@@ -1231,26 +1432,26 @@ var StreamProvider = class {
|
|
|
1231
1432
|
/** Get default config for a stream kind. */
|
|
1232
1433
|
getDefaults(kind) {
|
|
1233
1434
|
switch (kind) {
|
|
1234
|
-
case "ev"
|
|
1435
|
+
case "ev" /* Event */:
|
|
1235
1436
|
return DEFAULT_EVENT_STREAM_CONFIG;
|
|
1236
|
-
case "cmd"
|
|
1437
|
+
case "cmd" /* Command */:
|
|
1237
1438
|
return DEFAULT_COMMAND_STREAM_CONFIG;
|
|
1238
|
-
case "broadcast"
|
|
1439
|
+
case "broadcast" /* Broadcast */:
|
|
1239
1440
|
return DEFAULT_BROADCAST_STREAM_CONFIG;
|
|
1240
|
-
case "ordered"
|
|
1441
|
+
case "ordered" /* Ordered */:
|
|
1241
1442
|
return DEFAULT_ORDERED_STREAM_CONFIG;
|
|
1242
1443
|
}
|
|
1243
1444
|
}
|
|
1244
1445
|
/** Get user-provided overrides for a stream kind. */
|
|
1245
1446
|
getOverrides(kind) {
|
|
1246
1447
|
switch (kind) {
|
|
1247
|
-
case "ev"
|
|
1448
|
+
case "ev" /* Event */:
|
|
1248
1449
|
return this.options.events?.stream ?? {};
|
|
1249
|
-
case "cmd"
|
|
1450
|
+
case "cmd" /* Command */:
|
|
1250
1451
|
return this.options.rpc?.mode === "jetstream" ? this.options.rpc.stream ?? {} : {};
|
|
1251
|
-
case "broadcast"
|
|
1452
|
+
case "broadcast" /* Broadcast */:
|
|
1252
1453
|
return this.options.broadcast?.stream ?? {};
|
|
1253
|
-
case "ordered"
|
|
1454
|
+
case "ordered" /* Ordered */:
|
|
1254
1455
|
return this.options.ordered?.stream ?? {};
|
|
1255
1456
|
}
|
|
1256
1457
|
}
|
|
@@ -1313,7 +1514,7 @@ var ConsumerProvider = class {
|
|
|
1313
1514
|
const serviceName = internalName(this.options.name);
|
|
1314
1515
|
const defaults = this.getDefaults(kind);
|
|
1315
1516
|
const overrides = this.getOverrides(kind);
|
|
1316
|
-
if (kind === "broadcast") {
|
|
1517
|
+
if (kind === "broadcast" /* Broadcast */) {
|
|
1317
1518
|
const broadcastPatterns = this.patternRegistry.getBroadcastPatterns();
|
|
1318
1519
|
if (broadcastPatterns.length === 0) {
|
|
1319
1520
|
throw new Error("Broadcast consumer requested but no broadcast patterns are registered");
|
|
@@ -1335,7 +1536,10 @@ var ConsumerProvider = class {
|
|
|
1335
1536
|
filter_subjects: broadcastPatterns
|
|
1336
1537
|
};
|
|
1337
1538
|
}
|
|
1338
|
-
|
|
1539
|
+
if (kind !== "ev" /* Event */ && kind !== "cmd" /* Command */) {
|
|
1540
|
+
throw new Error(`Unexpected durable consumer kind: ${kind}`);
|
|
1541
|
+
}
|
|
1542
|
+
const filter_subject = `${serviceName}.${kind}.>`;
|
|
1339
1543
|
return {
|
|
1340
1544
|
...defaults,
|
|
1341
1545
|
...overrides,
|
|
@@ -1347,26 +1551,26 @@ var ConsumerProvider = class {
|
|
|
1347
1551
|
/** Get default config for a consumer kind. */
|
|
1348
1552
|
getDefaults(kind) {
|
|
1349
1553
|
switch (kind) {
|
|
1350
|
-
case "ev"
|
|
1554
|
+
case "ev" /* Event */:
|
|
1351
1555
|
return DEFAULT_EVENT_CONSUMER_CONFIG;
|
|
1352
|
-
case "cmd"
|
|
1556
|
+
case "cmd" /* Command */:
|
|
1353
1557
|
return DEFAULT_COMMAND_CONSUMER_CONFIG;
|
|
1354
|
-
case "broadcast"
|
|
1558
|
+
case "broadcast" /* Broadcast */:
|
|
1355
1559
|
return DEFAULT_BROADCAST_CONSUMER_CONFIG;
|
|
1356
|
-
case "ordered"
|
|
1560
|
+
case "ordered" /* Ordered */:
|
|
1357
1561
|
throw new Error("Ordered consumers are ephemeral and should not use durable config");
|
|
1358
1562
|
}
|
|
1359
1563
|
}
|
|
1360
1564
|
/** Get user-provided overrides for a consumer kind. */
|
|
1361
1565
|
getOverrides(kind) {
|
|
1362
1566
|
switch (kind) {
|
|
1363
|
-
case "ev"
|
|
1567
|
+
case "ev" /* Event */:
|
|
1364
1568
|
return this.options.events?.consumer ?? {};
|
|
1365
|
-
case "cmd"
|
|
1569
|
+
case "cmd" /* Command */:
|
|
1366
1570
|
return this.options.rpc?.mode === "jetstream" ? this.options.rpc.consumer ?? {} : {};
|
|
1367
|
-
case "broadcast"
|
|
1571
|
+
case "broadcast" /* Broadcast */:
|
|
1368
1572
|
return this.options.broadcast?.consumer ?? {};
|
|
1369
|
-
case "ordered"
|
|
1573
|
+
case "ordered" /* Ordered */:
|
|
1370
1574
|
throw new Error("Ordered consumers are ephemeral and should not use durable config");
|
|
1371
1575
|
}
|
|
1372
1576
|
}
|
|
@@ -1377,9 +1581,10 @@ var import_common7 = require("@nestjs/common");
|
|
|
1377
1581
|
var import_nats8 = require("nats");
|
|
1378
1582
|
var import_rxjs3 = require("rxjs");
|
|
1379
1583
|
var MessageProvider = class {
|
|
1380
|
-
constructor(connection, eventBus) {
|
|
1584
|
+
constructor(connection, eventBus, consumeOptionsMap = /* @__PURE__ */ new Map()) {
|
|
1381
1585
|
this.connection = connection;
|
|
1382
1586
|
this.eventBus = eventBus;
|
|
1587
|
+
this.consumeOptionsMap = consumeOptionsMap;
|
|
1383
1588
|
}
|
|
1384
1589
|
logger = new import_common7.Logger("Jetstream:Message");
|
|
1385
1590
|
activeIterators = /* @__PURE__ */ new Set();
|
|
@@ -1429,6 +1634,7 @@ var MessageProvider = class {
|
|
|
1429
1634
|
*
|
|
1430
1635
|
* @param streamName - JetStream stream to consume from.
|
|
1431
1636
|
* @param filterSubjects - NATS subjects to filter on.
|
|
1637
|
+
* @param orderedConfig - Optional overrides for ordered consumer options.
|
|
1432
1638
|
*/
|
|
1433
1639
|
async startOrdered(streamName2, filterSubjects, orderedConfig) {
|
|
1434
1640
|
const consumerOpts = { filterSubjects };
|
|
@@ -1454,6 +1660,11 @@ var MessageProvider = class {
|
|
|
1454
1660
|
}
|
|
1455
1661
|
/** Stop all consumer flows and reinitialize subjects for potential restart. */
|
|
1456
1662
|
destroy() {
|
|
1663
|
+
if (this.orderedReadyReject) {
|
|
1664
|
+
this.orderedReadyReject(new Error("Destroyed before ordered consumer connected"));
|
|
1665
|
+
this.orderedReadyResolve = null;
|
|
1666
|
+
this.orderedReadyReject = null;
|
|
1667
|
+
}
|
|
1457
1668
|
this.destroy$.next();
|
|
1458
1669
|
this.destroy$.complete();
|
|
1459
1670
|
for (const messages of this.activeIterators) {
|
|
@@ -1473,42 +1684,17 @@ var MessageProvider = class {
|
|
|
1473
1684
|
/** Create a self-healing consumer flow for a specific kind. */
|
|
1474
1685
|
createFlow(kind, info) {
|
|
1475
1686
|
const target$ = this.getTargetSubject(kind);
|
|
1476
|
-
|
|
1477
|
-
let lastRunFailed = false;
|
|
1478
|
-
return (0, import_rxjs3.defer)(() => this.consumeOnce(info, target$)).pipe(
|
|
1479
|
-
(0, import_rxjs3.tap)(() => {
|
|
1480
|
-
lastRunFailed = false;
|
|
1481
|
-
}),
|
|
1482
|
-
(0, import_rxjs3.catchError)((err) => {
|
|
1483
|
-
consecutiveFailures++;
|
|
1484
|
-
lastRunFailed = true;
|
|
1485
|
-
this.logger.error(`Consumer ${info.name} error, will restart:`, err);
|
|
1486
|
-
this.eventBus.emit(
|
|
1487
|
-
"error" /* Error */,
|
|
1488
|
-
err instanceof Error ? err : new Error(String(err)),
|
|
1489
|
-
"message-provider"
|
|
1490
|
-
);
|
|
1491
|
-
return import_rxjs3.EMPTY;
|
|
1492
|
-
}),
|
|
1493
|
-
(0, import_rxjs3.repeat)({
|
|
1494
|
-
delay: () => {
|
|
1495
|
-
if (!lastRunFailed) {
|
|
1496
|
-
consecutiveFailures = 0;
|
|
1497
|
-
}
|
|
1498
|
-
const delay = Math.min(100 * Math.pow(2, consecutiveFailures), 3e4);
|
|
1499
|
-
this.logger.warn(`Consumer ${info.name} stream ended, restarting in ${delay}ms...`);
|
|
1500
|
-
return (0, import_rxjs3.timer)(delay);
|
|
1501
|
-
}
|
|
1502
|
-
}),
|
|
1503
|
-
(0, import_rxjs3.takeUntil)(this.destroy$)
|
|
1504
|
-
);
|
|
1687
|
+
return this.createSelfHealingFlow(() => this.consumeOnce(kind, info, target$), info.name);
|
|
1505
1688
|
}
|
|
1506
1689
|
/** Single iteration: get consumer -> pull messages -> emit to subject. */
|
|
1507
|
-
async consumeOnce(info, target$) {
|
|
1508
|
-
const js =
|
|
1690
|
+
async consumeOnce(kind, info, target$) {
|
|
1691
|
+
const js = this.connection.getJetStreamClient();
|
|
1509
1692
|
const consumer = await js.consumers.get(info.stream_name, info.name);
|
|
1510
|
-
const
|
|
1693
|
+
const defaults = { idle_heartbeat: 5e3 };
|
|
1694
|
+
const userOptions = this.consumeOptionsMap.get(kind) ?? {};
|
|
1695
|
+
const messages = await consumer.consume({ ...defaults, ...userOptions });
|
|
1511
1696
|
this.activeIterators.add(messages);
|
|
1697
|
+
this.monitorConsumerHealth(messages, info.name);
|
|
1512
1698
|
try {
|
|
1513
1699
|
for await (const msg of messages) {
|
|
1514
1700
|
target$.next(msg);
|
|
@@ -1520,47 +1706,73 @@ var MessageProvider = class {
|
|
|
1520
1706
|
/** Get the target subject for a consumer kind. */
|
|
1521
1707
|
getTargetSubject(kind) {
|
|
1522
1708
|
switch (kind) {
|
|
1523
|
-
case "ev"
|
|
1709
|
+
case "ev" /* Event */:
|
|
1524
1710
|
return this.eventMessages$;
|
|
1525
|
-
case "cmd"
|
|
1711
|
+
case "cmd" /* Command */:
|
|
1526
1712
|
return this.commandMessages$;
|
|
1527
|
-
case "broadcast"
|
|
1713
|
+
case "broadcast" /* Broadcast */:
|
|
1528
1714
|
return this.broadcastMessages$;
|
|
1529
|
-
case "ordered"
|
|
1715
|
+
case "ordered" /* Ordered */:
|
|
1530
1716
|
return this.orderedMessages$;
|
|
1717
|
+
default: {
|
|
1718
|
+
const _exhaustive = kind;
|
|
1719
|
+
throw new Error(`Unknown stream kind: ${_exhaustive}`);
|
|
1720
|
+
}
|
|
1531
1721
|
}
|
|
1532
1722
|
}
|
|
1723
|
+
/** Monitor heartbeats and restart the consumer iterator on prolonged silence. */
|
|
1724
|
+
monitorConsumerHealth(messages, name) {
|
|
1725
|
+
(async () => {
|
|
1726
|
+
for await (const status of await messages.status()) {
|
|
1727
|
+
if (status.type === import_nats8.ConsumerEvents.HeartbeatsMissed && status.data >= 2) {
|
|
1728
|
+
this.logger.warn(`Consumer ${name}: ${status.data} heartbeats missed, restarting`);
|
|
1729
|
+
messages.stop();
|
|
1730
|
+
break;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
})().catch((err) => {
|
|
1734
|
+
if (err) {
|
|
1735
|
+
this.logger.debug(`Consumer ${name} health monitor ended:`, err);
|
|
1736
|
+
}
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1533
1739
|
/** Create a self-healing ordered consumer flow. */
|
|
1534
1740
|
createOrderedFlow(streamName2, consumerOpts) {
|
|
1741
|
+
return this.createSelfHealingFlow(
|
|
1742
|
+
() => this.consumeOrderedOnce(streamName2, consumerOpts),
|
|
1743
|
+
"ordered" /* Ordered */,
|
|
1744
|
+
(err) => {
|
|
1745
|
+
if (this.orderedReadyReject) {
|
|
1746
|
+
this.orderedReadyReject(err);
|
|
1747
|
+
this.orderedReadyReject = null;
|
|
1748
|
+
this.orderedReadyResolve = null;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
);
|
|
1752
|
+
}
|
|
1753
|
+
/** Shared self-healing flow: defer -> retry with exponential backoff on error/completion. */
|
|
1754
|
+
createSelfHealingFlow(source, label, onFirstError) {
|
|
1535
1755
|
let consecutiveFailures = 0;
|
|
1536
|
-
|
|
1537
|
-
return (0, import_rxjs3.defer)(() => this.consumeOrderedOnce(streamName2, consumerOpts)).pipe(
|
|
1756
|
+
return (0, import_rxjs3.defer)(source).pipe(
|
|
1538
1757
|
(0, import_rxjs3.tap)(() => {
|
|
1539
|
-
|
|
1758
|
+
consecutiveFailures = 0;
|
|
1540
1759
|
}),
|
|
1541
1760
|
(0, import_rxjs3.catchError)((err) => {
|
|
1542
1761
|
consecutiveFailures++;
|
|
1543
|
-
|
|
1544
|
-
this.logger.error("Ordered consumer error, will restart:", err);
|
|
1762
|
+
this.logger.error(`Consumer ${label} error, will restart:`, err);
|
|
1545
1763
|
this.eventBus.emit(
|
|
1546
1764
|
"error" /* Error */,
|
|
1547
1765
|
err instanceof Error ? err : new Error(String(err)),
|
|
1548
1766
|
"message-provider"
|
|
1549
1767
|
);
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
this.orderedReadyReject = null;
|
|
1553
|
-
this.orderedReadyResolve = null;
|
|
1554
|
-
}
|
|
1768
|
+
onFirstError?.(err);
|
|
1769
|
+
onFirstError = void 0;
|
|
1555
1770
|
return import_rxjs3.EMPTY;
|
|
1556
1771
|
}),
|
|
1557
1772
|
(0, import_rxjs3.repeat)({
|
|
1558
1773
|
delay: () => {
|
|
1559
|
-
if (!lastRunFailed) {
|
|
1560
|
-
consecutiveFailures = 0;
|
|
1561
|
-
}
|
|
1562
1774
|
const delay = Math.min(100 * Math.pow(2, consecutiveFailures), 3e4);
|
|
1563
|
-
this.logger.warn(`
|
|
1775
|
+
this.logger.warn(`Consumer ${label} stream ended, restarting in ${delay}ms...`);
|
|
1564
1776
|
return (0, import_rxjs3.timer)(delay);
|
|
1565
1777
|
}
|
|
1566
1778
|
})
|
|
@@ -1568,7 +1780,7 @@ var MessageProvider = class {
|
|
|
1568
1780
|
}
|
|
1569
1781
|
/** Single iteration: create ordered consumer -> iterate messages. */
|
|
1570
1782
|
async consumeOrderedOnce(streamName2, consumerOpts) {
|
|
1571
|
-
const js =
|
|
1783
|
+
const js = this.connection.getJetStreamClient();
|
|
1572
1784
|
const consumer = await js.consumers.get(streamName2, consumerOpts);
|
|
1573
1785
|
const messages = await consumer.consume();
|
|
1574
1786
|
if (this.orderedReadyResolve) {
|
|
@@ -1589,12 +1801,24 @@ var MessageProvider = class {
|
|
|
1589
1801
|
|
|
1590
1802
|
// src/server/routing/pattern-registry.ts
|
|
1591
1803
|
var import_common8 = require("@nestjs/common");
|
|
1804
|
+
var HANDLER_LABELS = {
|
|
1805
|
+
["broadcast" /* Broadcast */]: "broadcast" /* Broadcast */,
|
|
1806
|
+
["ordered" /* Ordered */]: "ordered" /* Ordered */,
|
|
1807
|
+
["ev" /* Event */]: "event" /* Event */,
|
|
1808
|
+
["cmd" /* Command */]: "rpc" /* Rpc */
|
|
1809
|
+
};
|
|
1592
1810
|
var PatternRegistry = class {
|
|
1593
1811
|
constructor(options) {
|
|
1594
1812
|
this.options = options;
|
|
1595
1813
|
}
|
|
1596
1814
|
logger = new import_common8.Logger("Jetstream:PatternRegistry");
|
|
1597
1815
|
registry = /* @__PURE__ */ new Map();
|
|
1816
|
+
// Cached after registerHandlers() — the registry is immutable from that point
|
|
1817
|
+
cachedPatterns = null;
|
|
1818
|
+
_hasEvents = false;
|
|
1819
|
+
_hasCommands = false;
|
|
1820
|
+
_hasBroadcasts = false;
|
|
1821
|
+
_hasOrdered = false;
|
|
1598
1822
|
/**
|
|
1599
1823
|
* Register all handlers from the NestJS strategy.
|
|
1600
1824
|
*
|
|
@@ -1612,16 +1836,12 @@ var PatternRegistry = class {
|
|
|
1612
1836
|
`Handler "${pattern}" cannot be both broadcast and ordered. Use one or the other.`
|
|
1613
1837
|
);
|
|
1614
1838
|
}
|
|
1615
|
-
let
|
|
1616
|
-
if (isBroadcast)
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
fullSubject = buildSubject(serviceName, "ev", pattern);
|
|
1622
|
-
} else {
|
|
1623
|
-
fullSubject = buildSubject(serviceName, "cmd", pattern);
|
|
1624
|
-
}
|
|
1839
|
+
let kind;
|
|
1840
|
+
if (isBroadcast) kind = "broadcast" /* Broadcast */;
|
|
1841
|
+
else if (isOrdered) kind = "ordered" /* Ordered */;
|
|
1842
|
+
else if (isEvent) kind = "ev" /* Event */;
|
|
1843
|
+
else kind = "cmd" /* Command */;
|
|
1844
|
+
const fullSubject = kind === "broadcast" /* Broadcast */ ? buildBroadcastSubject(pattern) : buildSubject(serviceName, kind, pattern);
|
|
1625
1845
|
this.registry.set(fullSubject, {
|
|
1626
1846
|
handler,
|
|
1627
1847
|
pattern,
|
|
@@ -1629,18 +1849,13 @@ var PatternRegistry = class {
|
|
|
1629
1849
|
isBroadcast,
|
|
1630
1850
|
isOrdered
|
|
1631
1851
|
});
|
|
1632
|
-
|
|
1633
|
-
if (isBroadcast) {
|
|
1634
|
-
kind = "broadcast";
|
|
1635
|
-
} else if (isOrdered) {
|
|
1636
|
-
kind = "ordered";
|
|
1637
|
-
} else if (isEvent) {
|
|
1638
|
-
kind = "event";
|
|
1639
|
-
} else {
|
|
1640
|
-
kind = "rpc";
|
|
1641
|
-
}
|
|
1642
|
-
this.logger.debug(`Registered ${kind}: ${pattern} -> ${fullSubject}`);
|
|
1852
|
+
this.logger.debug(`Registered ${HANDLER_LABELS[kind]}: ${pattern} -> ${fullSubject}`);
|
|
1643
1853
|
}
|
|
1854
|
+
this.cachedPatterns = this.buildPatternsByKind();
|
|
1855
|
+
this._hasEvents = this.cachedPatterns.events.length > 0;
|
|
1856
|
+
this._hasCommands = this.cachedPatterns.commands.length > 0;
|
|
1857
|
+
this._hasBroadcasts = this.cachedPatterns.broadcasts.length > 0;
|
|
1858
|
+
this._hasOrdered = this.cachedPatterns.ordered.length > 0;
|
|
1644
1859
|
this.logSummary();
|
|
1645
1860
|
}
|
|
1646
1861
|
/** Find handler for a full NATS subject. */
|
|
@@ -1649,33 +1864,53 @@ var PatternRegistry = class {
|
|
|
1649
1864
|
}
|
|
1650
1865
|
/** Get all registered broadcast patterns (for consumer filter_subject setup). */
|
|
1651
1866
|
getBroadcastPatterns() {
|
|
1652
|
-
return
|
|
1867
|
+
return this.getPatternsByKind().broadcasts.map((p) => buildBroadcastSubject(p));
|
|
1653
1868
|
}
|
|
1654
|
-
/** Check if any broadcast handlers are registered. */
|
|
1655
1869
|
hasBroadcastHandlers() {
|
|
1656
|
-
return
|
|
1870
|
+
return this._hasBroadcasts;
|
|
1657
1871
|
}
|
|
1658
|
-
/** Check if any RPC (command) handlers are registered. */
|
|
1659
1872
|
hasRpcHandlers() {
|
|
1660
|
-
return
|
|
1661
|
-
(r) => !r.isEvent && !r.isBroadcast && !r.isOrdered
|
|
1662
|
-
);
|
|
1873
|
+
return this._hasCommands;
|
|
1663
1874
|
}
|
|
1664
|
-
/** Check if any workqueue event handlers are registered. */
|
|
1665
1875
|
hasEventHandlers() {
|
|
1666
|
-
return
|
|
1876
|
+
return this._hasEvents;
|
|
1667
1877
|
}
|
|
1668
|
-
/** Check if any ordered event handlers are registered. */
|
|
1669
1878
|
hasOrderedHandlers() {
|
|
1670
|
-
return
|
|
1879
|
+
return this._hasOrdered;
|
|
1671
1880
|
}
|
|
1672
1881
|
/** Get fully-qualified NATS subjects for ordered handlers. */
|
|
1673
1882
|
getOrderedSubjects() {
|
|
1674
|
-
|
|
1675
|
-
|
|
1883
|
+
return this.getPatternsByKind().ordered.map(
|
|
1884
|
+
(p) => buildSubject(this.options.name, "ordered" /* Ordered */, p)
|
|
1885
|
+
);
|
|
1676
1886
|
}
|
|
1677
|
-
/** Get patterns grouped by kind. */
|
|
1887
|
+
/** Get patterns grouped by kind (cached after registration). */
|
|
1678
1888
|
getPatternsByKind() {
|
|
1889
|
+
const patterns = this.cachedPatterns ?? this.buildPatternsByKind();
|
|
1890
|
+
return {
|
|
1891
|
+
events: [...patterns.events],
|
|
1892
|
+
commands: [...patterns.commands],
|
|
1893
|
+
broadcasts: [...patterns.broadcasts],
|
|
1894
|
+
ordered: [...patterns.ordered]
|
|
1895
|
+
};
|
|
1896
|
+
}
|
|
1897
|
+
/** Normalize a full NATS subject back to the user-facing pattern. */
|
|
1898
|
+
normalizeSubject(subject) {
|
|
1899
|
+
const name = internalName(this.options.name);
|
|
1900
|
+
const prefixes = [
|
|
1901
|
+
`${name}.${"cmd" /* Command */}.`,
|
|
1902
|
+
`${name}.${"ev" /* Event */}.`,
|
|
1903
|
+
`${name}.${"ordered" /* Ordered */}.`,
|
|
1904
|
+
`${"broadcast" /* Broadcast */}.`
|
|
1905
|
+
];
|
|
1906
|
+
for (const prefix of prefixes) {
|
|
1907
|
+
if (subject.startsWith(prefix)) {
|
|
1908
|
+
return subject.slice(prefix.length);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
return subject;
|
|
1912
|
+
}
|
|
1913
|
+
buildPatternsByKind() {
|
|
1679
1914
|
const events = [];
|
|
1680
1915
|
const commands = [];
|
|
1681
1916
|
const broadcasts = [];
|
|
@@ -1688,18 +1923,6 @@ var PatternRegistry = class {
|
|
|
1688
1923
|
}
|
|
1689
1924
|
return { events, commands, broadcasts, ordered };
|
|
1690
1925
|
}
|
|
1691
|
-
/** Normalize a full NATS subject back to the user-facing pattern. */
|
|
1692
|
-
normalizeSubject(subject) {
|
|
1693
|
-
const name = internalName(this.options.name);
|
|
1694
|
-
const prefixes = [`${name}.cmd.`, `${name}.ev.`, `${name}.ordered.`, "broadcast."];
|
|
1695
|
-
for (const prefix of prefixes) {
|
|
1696
|
-
if (subject.startsWith(prefix)) {
|
|
1697
|
-
return subject.slice(prefix.length);
|
|
1698
|
-
}
|
|
1699
|
-
}
|
|
1700
|
-
return subject;
|
|
1701
|
-
}
|
|
1702
|
-
/** Log a summary of all registered handlers. */
|
|
1703
1926
|
logSummary() {
|
|
1704
1927
|
const { events, commands, broadcasts, ordered } = this.getPatternsByKind();
|
|
1705
1928
|
const parts = [
|
|
@@ -1718,12 +1941,14 @@ var PatternRegistry = class {
|
|
|
1718
1941
|
var import_common9 = require("@nestjs/common");
|
|
1719
1942
|
var import_rxjs4 = require("rxjs");
|
|
1720
1943
|
var EventRouter = class {
|
|
1721
|
-
constructor(messageProvider, patternRegistry, codec, eventBus, deadLetterConfig) {
|
|
1944
|
+
constructor(messageProvider, patternRegistry, codec, eventBus, deadLetterConfig, processingConfig, ackWaitMap) {
|
|
1722
1945
|
this.messageProvider = messageProvider;
|
|
1723
1946
|
this.patternRegistry = patternRegistry;
|
|
1724
1947
|
this.codec = codec;
|
|
1725
1948
|
this.eventBus = eventBus;
|
|
1726
1949
|
this.deadLetterConfig = deadLetterConfig;
|
|
1950
|
+
this.processingConfig = processingConfig;
|
|
1951
|
+
this.ackWaitMap = ackWaitMap;
|
|
1727
1952
|
}
|
|
1728
1953
|
logger = new import_common9.Logger("Jetstream:EventRouter");
|
|
1729
1954
|
subscriptions = [];
|
|
@@ -1737,10 +1962,10 @@ var EventRouter = class {
|
|
|
1737
1962
|
}
|
|
1738
1963
|
/** Start routing event, broadcast, and ordered messages to handlers. */
|
|
1739
1964
|
start() {
|
|
1740
|
-
this.subscribeToStream(this.messageProvider.events$, "
|
|
1741
|
-
this.subscribeToStream(this.messageProvider.broadcasts$, "broadcast");
|
|
1965
|
+
this.subscribeToStream(this.messageProvider.events$, "ev" /* Event */);
|
|
1966
|
+
this.subscribeToStream(this.messageProvider.broadcasts$, "broadcast" /* Broadcast */);
|
|
1742
1967
|
if (this.patternRegistry.hasOrderedHandlers()) {
|
|
1743
|
-
this.subscribeToStream(this.messageProvider.ordered$, "ordered"
|
|
1968
|
+
this.subscribeToStream(this.messageProvider.ordered$, "ordered" /* Ordered */);
|
|
1744
1969
|
}
|
|
1745
1970
|
}
|
|
1746
1971
|
/** Stop routing and unsubscribe from all streams. */
|
|
@@ -1751,41 +1976,88 @@ var EventRouter = class {
|
|
|
1751
1976
|
this.subscriptions.length = 0;
|
|
1752
1977
|
}
|
|
1753
1978
|
/** Subscribe to a message stream and route each message. */
|
|
1754
|
-
subscribeToStream(stream$,
|
|
1755
|
-
const
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1979
|
+
subscribeToStream(stream$, kind) {
|
|
1980
|
+
const isOrdered = kind === "ordered" /* Ordered */;
|
|
1981
|
+
const ackExtensionInterval = isOrdered ? null : resolveAckExtensionInterval(this.getAckExtensionConfig(kind), this.ackWaitMap?.get(kind));
|
|
1982
|
+
const concurrency = this.getConcurrency(kind);
|
|
1983
|
+
const route = (msg) => (0, import_rxjs4.from)(
|
|
1984
|
+
isOrdered ? this.handleOrderedSafe(msg) : this.handleSafe(msg, ackExtensionInterval, kind)
|
|
1760
1985
|
);
|
|
1761
|
-
const subscription = stream$.pipe(isOrdered ? (0, import_rxjs4.concatMap)(route) : (0, import_rxjs4.mergeMap)(route)).subscribe();
|
|
1986
|
+
const subscription = stream$.pipe(isOrdered ? (0, import_rxjs4.concatMap)(route) : (0, import_rxjs4.mergeMap)(route, concurrency)).subscribe();
|
|
1762
1987
|
this.subscriptions.push(subscription);
|
|
1763
1988
|
}
|
|
1764
|
-
|
|
1765
|
-
|
|
1989
|
+
getConcurrency(kind) {
|
|
1990
|
+
if (kind === "ev" /* Event */) return this.processingConfig?.events?.concurrency;
|
|
1991
|
+
if (kind === "broadcast" /* Broadcast */) return this.processingConfig?.broadcast?.concurrency;
|
|
1992
|
+
return void 0;
|
|
1993
|
+
}
|
|
1994
|
+
getAckExtensionConfig(kind) {
|
|
1995
|
+
if (kind === "ev" /* Event */) return this.processingConfig?.events?.ackExtension;
|
|
1996
|
+
if (kind === "broadcast" /* Broadcast */) return this.processingConfig?.broadcast?.ackExtension;
|
|
1997
|
+
return void 0;
|
|
1998
|
+
}
|
|
1999
|
+
/** Handle a single event message with error isolation. */
|
|
2000
|
+
async handleSafe(msg, ackExtensionInterval, kind) {
|
|
2001
|
+
try {
|
|
2002
|
+
const resolved = this.decodeMessage(msg);
|
|
2003
|
+
if (!resolved) return;
|
|
2004
|
+
await this.executeHandler(
|
|
2005
|
+
resolved.handler,
|
|
2006
|
+
resolved.data,
|
|
2007
|
+
resolved.ctx,
|
|
2008
|
+
msg,
|
|
2009
|
+
ackExtensionInterval
|
|
2010
|
+
);
|
|
2011
|
+
} catch (err) {
|
|
2012
|
+
this.logger.error(`Unexpected error in ${kind} event router`, err);
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
/** Handle an ordered message with error isolation. */
|
|
2016
|
+
async handleOrderedSafe(msg) {
|
|
2017
|
+
try {
|
|
2018
|
+
const resolved = this.decodeMessage(msg, true);
|
|
2019
|
+
if (!resolved) return;
|
|
2020
|
+
await unwrapResult(resolved.handler(resolved.data, resolved.ctx));
|
|
2021
|
+
if (resolved.ctx.shouldRetry || resolved.ctx.shouldTerminate) {
|
|
2022
|
+
this.logger.warn(
|
|
2023
|
+
`retry()/terminate() ignored for ordered message ${msg.subject} \u2014 ordered consumers auto-acknowledge`
|
|
2024
|
+
);
|
|
2025
|
+
}
|
|
2026
|
+
} catch (err) {
|
|
2027
|
+
this.logger.error(`Ordered handler error (${msg.subject}):`, err);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
/** Resolve handler, decode payload, and build context. Returns null on failure. */
|
|
2031
|
+
decodeMessage(msg, isOrdered = false) {
|
|
1766
2032
|
const handler = this.patternRegistry.getHandler(msg.subject);
|
|
1767
2033
|
if (!handler) {
|
|
1768
|
-
msg.term(`No handler for event: ${msg.subject}`);
|
|
1769
|
-
this.logger.error(`No handler for
|
|
1770
|
-
return
|
|
2034
|
+
if (!isOrdered) msg.term(`No handler for event: ${msg.subject}`);
|
|
2035
|
+
this.logger.error(`No handler for subject: ${msg.subject}`);
|
|
2036
|
+
return null;
|
|
1771
2037
|
}
|
|
1772
2038
|
let data;
|
|
1773
2039
|
try {
|
|
1774
2040
|
data = this.codec.decode(msg.data);
|
|
1775
2041
|
} catch (err) {
|
|
1776
|
-
msg.term("Decode error");
|
|
2042
|
+
if (!isOrdered) msg.term("Decode error");
|
|
1777
2043
|
this.logger.error(`Decode error for ${msg.subject}:`, err);
|
|
1778
|
-
return
|
|
2044
|
+
return null;
|
|
1779
2045
|
}
|
|
1780
|
-
this.eventBus.
|
|
1781
|
-
|
|
1782
|
-
return (0, import_rxjs4.from)(this.executeHandler(handler, data, ctx, msg));
|
|
2046
|
+
this.eventBus.emitMessageRouted(msg.subject, "event" /* Event */);
|
|
2047
|
+
return { handler, data, ctx: new RpcContext([msg]) };
|
|
1783
2048
|
}
|
|
1784
2049
|
/** Execute handler, then ack on success or nak/dead-letter on failure. */
|
|
1785
|
-
async executeHandler(handler, data, ctx, msg) {
|
|
2050
|
+
async executeHandler(handler, data, ctx, msg, ackExtensionInterval) {
|
|
2051
|
+
const stopAckExtension = startAckExtensionTimer(msg, ackExtensionInterval);
|
|
1786
2052
|
try {
|
|
1787
2053
|
await unwrapResult(handler(data, ctx));
|
|
1788
|
-
|
|
2054
|
+
if (ctx.shouldTerminate) {
|
|
2055
|
+
msg.term(ctx.terminateReason);
|
|
2056
|
+
} else if (ctx.shouldRetry) {
|
|
2057
|
+
msg.nak(ctx.retryDelay);
|
|
2058
|
+
} else {
|
|
2059
|
+
msg.ack();
|
|
2060
|
+
}
|
|
1789
2061
|
} catch (err) {
|
|
1790
2062
|
this.logger.error(`Event handler error (${msg.subject}):`, err);
|
|
1791
2063
|
if (this.isDeadLetter(msg)) {
|
|
@@ -1793,30 +2065,10 @@ var EventRouter = class {
|
|
|
1793
2065
|
} else {
|
|
1794
2066
|
msg.nak();
|
|
1795
2067
|
}
|
|
2068
|
+
} finally {
|
|
2069
|
+
stopAckExtension?.();
|
|
1796
2070
|
}
|
|
1797
2071
|
}
|
|
1798
|
-
/** Handle an ordered message: decode -> execute handler -> no ack/nak. */
|
|
1799
|
-
handleOrdered(msg) {
|
|
1800
|
-
const handler = this.patternRegistry.getHandler(msg.subject);
|
|
1801
|
-
if (!handler) {
|
|
1802
|
-
this.logger.error(`No handler for ordered subject: ${msg.subject}`);
|
|
1803
|
-
return import_rxjs4.EMPTY;
|
|
1804
|
-
}
|
|
1805
|
-
let data;
|
|
1806
|
-
try {
|
|
1807
|
-
data = this.codec.decode(msg.data);
|
|
1808
|
-
} catch (err) {
|
|
1809
|
-
this.logger.error(`Decode error for ordered ${msg.subject}:`, err);
|
|
1810
|
-
return import_rxjs4.EMPTY;
|
|
1811
|
-
}
|
|
1812
|
-
this.eventBus.emit("messageRouted" /* MessageRouted */, msg.subject, "event");
|
|
1813
|
-
const ctx = new RpcContext([msg]);
|
|
1814
|
-
return (0, import_rxjs4.from)(
|
|
1815
|
-
unwrapResult(handler(data, ctx)).catch((err) => {
|
|
1816
|
-
this.logger.error(`Ordered handler error (${msg.subject}):`, err);
|
|
1817
|
-
})
|
|
1818
|
-
);
|
|
1819
|
-
}
|
|
1820
2072
|
/** Check if the message has exhausted all delivery attempts. */
|
|
1821
2073
|
isDeadLetter(msg) {
|
|
1822
2074
|
if (!this.deadLetterConfig) return false;
|
|
@@ -1837,7 +2089,10 @@ var EventRouter = class {
|
|
|
1837
2089
|
timestamp: new Date(msg.info.timestampNanos / 1e6).toISOString()
|
|
1838
2090
|
};
|
|
1839
2091
|
this.eventBus.emit("deadLetter" /* DeadLetter */, info);
|
|
1840
|
-
if (!this.deadLetterConfig)
|
|
2092
|
+
if (!this.deadLetterConfig) {
|
|
2093
|
+
msg.term("Dead letter config unavailable");
|
|
2094
|
+
return;
|
|
2095
|
+
}
|
|
1841
2096
|
try {
|
|
1842
2097
|
await this.deadLetterConfig.onDeadLetter(info);
|
|
1843
2098
|
msg.term("Dead letter processed");
|
|
@@ -1853,71 +2108,83 @@ var import_common10 = require("@nestjs/common");
|
|
|
1853
2108
|
var import_nats9 = require("nats");
|
|
1854
2109
|
var import_rxjs5 = require("rxjs");
|
|
1855
2110
|
var RpcRouter = class {
|
|
1856
|
-
constructor(messageProvider, patternRegistry, connection, codec, eventBus,
|
|
2111
|
+
constructor(messageProvider, patternRegistry, connection, codec, eventBus, rpcOptions, ackWaitMap) {
|
|
1857
2112
|
this.messageProvider = messageProvider;
|
|
1858
2113
|
this.patternRegistry = patternRegistry;
|
|
1859
2114
|
this.connection = connection;
|
|
1860
2115
|
this.codec = codec;
|
|
1861
2116
|
this.eventBus = eventBus;
|
|
1862
|
-
this.
|
|
2117
|
+
this.rpcOptions = rpcOptions;
|
|
2118
|
+
this.ackWaitMap = ackWaitMap;
|
|
2119
|
+
this.timeout = rpcOptions?.timeout ?? DEFAULT_JETSTREAM_RPC_TIMEOUT;
|
|
2120
|
+
this.concurrency = rpcOptions?.concurrency;
|
|
1863
2121
|
}
|
|
1864
2122
|
logger = new import_common10.Logger("Jetstream:RpcRouter");
|
|
1865
2123
|
timeout;
|
|
2124
|
+
concurrency;
|
|
2125
|
+
resolvedAckExtensionInterval;
|
|
1866
2126
|
subscription = null;
|
|
2127
|
+
cachedNc = null;
|
|
2128
|
+
/** Lazily resolve the ack extension interval (needs ackWaitMap populated at runtime). */
|
|
2129
|
+
get ackExtensionInterval() {
|
|
2130
|
+
if (this.resolvedAckExtensionInterval !== void 0) return this.resolvedAckExtensionInterval;
|
|
2131
|
+
this.resolvedAckExtensionInterval = resolveAckExtensionInterval(
|
|
2132
|
+
this.rpcOptions?.ackExtension,
|
|
2133
|
+
this.ackWaitMap?.get("cmd" /* Command */)
|
|
2134
|
+
);
|
|
2135
|
+
return this.resolvedAckExtensionInterval;
|
|
2136
|
+
}
|
|
1867
2137
|
/** Start routing command messages to handlers. */
|
|
1868
|
-
start() {
|
|
1869
|
-
this.
|
|
1870
|
-
|
|
1871
|
-
(msg) => (0, import_rxjs5.defer)(() => this.handle(msg)).pipe(
|
|
1872
|
-
(0, import_rxjs5.catchError)((err) => {
|
|
1873
|
-
this.logger.error("Unexpected error in RPC router", err);
|
|
1874
|
-
return import_rxjs5.EMPTY;
|
|
1875
|
-
})
|
|
1876
|
-
)
|
|
1877
|
-
)
|
|
1878
|
-
).subscribe();
|
|
2138
|
+
async start() {
|
|
2139
|
+
this.cachedNc = await this.connection.getConnection();
|
|
2140
|
+
this.subscription = this.messageProvider.commands$.pipe((0, import_rxjs5.mergeMap)((msg) => (0, import_rxjs5.from)(this.handleSafe(msg)), this.concurrency)).subscribe();
|
|
1879
2141
|
}
|
|
1880
2142
|
/** Stop routing and unsubscribe. */
|
|
1881
2143
|
destroy() {
|
|
1882
2144
|
this.subscription?.unsubscribe();
|
|
1883
2145
|
this.subscription = null;
|
|
1884
2146
|
}
|
|
1885
|
-
/** Handle a single RPC command message. */
|
|
1886
|
-
|
|
1887
|
-
const handler = this.patternRegistry.getHandler(msg.subject);
|
|
1888
|
-
if (!handler) {
|
|
1889
|
-
msg.term(`No handler for RPC: ${msg.subject}`);
|
|
1890
|
-
this.logger.error(`No handler for RPC subject: ${msg.subject}`);
|
|
1891
|
-
return import_rxjs5.EMPTY;
|
|
1892
|
-
}
|
|
1893
|
-
const replyTo = msg.headers?.get("x-reply-to" /* ReplyTo */);
|
|
1894
|
-
const correlationId = msg.headers?.get("x-correlation-id" /* CorrelationId */);
|
|
1895
|
-
if (!replyTo || !correlationId) {
|
|
1896
|
-
msg.term("Missing required headers (reply-to or correlation-id)");
|
|
1897
|
-
this.logger.error(`Missing headers for RPC: ${msg.subject}`);
|
|
1898
|
-
return import_rxjs5.EMPTY;
|
|
1899
|
-
}
|
|
1900
|
-
let data;
|
|
2147
|
+
/** Handle a single RPC command message with error isolation. */
|
|
2148
|
+
async handleSafe(msg) {
|
|
1901
2149
|
try {
|
|
1902
|
-
|
|
2150
|
+
const handler = this.patternRegistry.getHandler(msg.subject);
|
|
2151
|
+
if (!handler) {
|
|
2152
|
+
msg.term(`No handler for RPC: ${msg.subject}`);
|
|
2153
|
+
this.logger.error(`No handler for RPC subject: ${msg.subject}`);
|
|
2154
|
+
return;
|
|
2155
|
+
}
|
|
2156
|
+
const { headers: msgHeaders } = msg;
|
|
2157
|
+
const replyTo = msgHeaders?.get("x-reply-to" /* ReplyTo */);
|
|
2158
|
+
const correlationId = msgHeaders?.get("x-correlation-id" /* CorrelationId */);
|
|
2159
|
+
if (!replyTo || !correlationId) {
|
|
2160
|
+
msg.term("Missing required headers (reply-to or correlation-id)");
|
|
2161
|
+
this.logger.error(`Missing headers for RPC: ${msg.subject}`);
|
|
2162
|
+
return;
|
|
2163
|
+
}
|
|
2164
|
+
let data;
|
|
2165
|
+
try {
|
|
2166
|
+
data = this.codec.decode(msg.data);
|
|
2167
|
+
} catch (err) {
|
|
2168
|
+
msg.term("Decode error");
|
|
2169
|
+
this.logger.error(`Decode error for RPC ${msg.subject}:`, err);
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
this.eventBus.emitMessageRouted(msg.subject, "rpc" /* Rpc */);
|
|
2173
|
+
await this.executeHandler(handler, data, msg, replyTo, correlationId);
|
|
1903
2174
|
} catch (err) {
|
|
1904
|
-
|
|
1905
|
-
this.logger.error(`Decode error for RPC ${msg.subject}:`, err);
|
|
1906
|
-
return import_rxjs5.EMPTY;
|
|
2175
|
+
this.logger.error("Unexpected error in RPC router", err);
|
|
1907
2176
|
}
|
|
1908
|
-
this.eventBus.emit("messageRouted" /* MessageRouted */, msg.subject, "rpc");
|
|
1909
|
-
return (0, import_rxjs5.from)(this.executeHandler(handler, data, msg, replyTo, correlationId));
|
|
1910
2177
|
}
|
|
1911
2178
|
/** Execute handler, publish response, settle message. */
|
|
1912
2179
|
async executeHandler(handler, data, msg, replyTo, correlationId) {
|
|
1913
|
-
const nc = await this.connection.getConnection();
|
|
2180
|
+
const nc = this.cachedNc ?? await this.connection.getConnection();
|
|
1914
2181
|
const ctx = new RpcContext([msg]);
|
|
1915
|
-
const hdrs = (0, import_nats9.headers)();
|
|
1916
|
-
hdrs.set("x-correlation-id" /* CorrelationId */, correlationId);
|
|
1917
2182
|
let settled = false;
|
|
2183
|
+
const stopAckExtension = startAckExtensionTimer(msg, this.ackExtensionInterval);
|
|
1918
2184
|
const timeoutId = setTimeout(() => {
|
|
1919
2185
|
if (settled) return;
|
|
1920
2186
|
settled = true;
|
|
2187
|
+
stopAckExtension?.();
|
|
1921
2188
|
this.logger.error(`RPC timeout (${this.timeout}ms): ${msg.subject}`);
|
|
1922
2189
|
this.eventBus.emit("rpcTimeout" /* RpcTimeout */, msg.subject, correlationId);
|
|
1923
2190
|
msg.term("Handler timeout");
|
|
@@ -1927,8 +2194,11 @@ var RpcRouter = class {
|
|
|
1927
2194
|
if (settled) return;
|
|
1928
2195
|
settled = true;
|
|
1929
2196
|
clearTimeout(timeoutId);
|
|
2197
|
+
stopAckExtension?.();
|
|
1930
2198
|
msg.ack();
|
|
1931
2199
|
try {
|
|
2200
|
+
const hdrs = (0, import_nats9.headers)();
|
|
2201
|
+
hdrs.set("x-correlation-id" /* CorrelationId */, correlationId);
|
|
1932
2202
|
nc.publish(replyTo, this.codec.encode(result), { headers: hdrs });
|
|
1933
2203
|
} catch (publishErr) {
|
|
1934
2204
|
this.logger.error(`Failed to publish RPC response for ${msg.subject}`, publishErr);
|
|
@@ -1937,7 +2207,10 @@ var RpcRouter = class {
|
|
|
1937
2207
|
if (settled) return;
|
|
1938
2208
|
settled = true;
|
|
1939
2209
|
clearTimeout(timeoutId);
|
|
2210
|
+
stopAckExtension?.();
|
|
1940
2211
|
try {
|
|
2212
|
+
const hdrs = (0, import_nats9.headers)();
|
|
2213
|
+
hdrs.set("x-correlation-id" /* CorrelationId */, correlationId);
|
|
1941
2214
|
hdrs.set("x-error" /* Error */, "true");
|
|
1942
2215
|
nc.publish(replyTo, this.codec.encode(serializeError(err)), { headers: hdrs });
|
|
1943
2216
|
} catch (encodeErr) {
|
|
@@ -1960,7 +2233,7 @@ var ShutdownManager = class {
|
|
|
1960
2233
|
/**
|
|
1961
2234
|
* Execute the full shutdown sequence.
|
|
1962
2235
|
*
|
|
1963
|
-
* @param strategy Optional
|
|
2236
|
+
* @param strategy Optional stoppable to close (stops consumers and subscriptions).
|
|
1964
2237
|
*/
|
|
1965
2238
|
async shutdown(strategy) {
|
|
1966
2239
|
this.eventBus.emit("shutdownStart" /* ShutdownStart */);
|
|
@@ -1983,6 +2256,7 @@ var ShutdownManager = class {
|
|
|
1983
2256
|
};
|
|
1984
2257
|
|
|
1985
2258
|
// src/jetstream.module.ts
|
|
2259
|
+
var JETSTREAM_ACK_WAIT_MAP = /* @__PURE__ */ Symbol("JETSTREAM_ACK_WAIT_MAP");
|
|
1986
2260
|
var JetstreamModule = class {
|
|
1987
2261
|
constructor(shutdownManager, strategy) {
|
|
1988
2262
|
this.shutdownManager = shutdownManager;
|
|
@@ -2167,13 +2441,26 @@ var JetstreamModule = class {
|
|
|
2167
2441
|
return new ConsumerProvider(options, connection, streamProvider, patternRegistry);
|
|
2168
2442
|
}
|
|
2169
2443
|
},
|
|
2444
|
+
// Shared ack_wait map — populated by strategy after ensureConsumers()
|
|
2445
|
+
{
|
|
2446
|
+
provide: JETSTREAM_ACK_WAIT_MAP,
|
|
2447
|
+
useFactory: () => /* @__PURE__ */ new Map()
|
|
2448
|
+
},
|
|
2170
2449
|
// MessageProvider — pull-based message consumption
|
|
2171
2450
|
{
|
|
2172
2451
|
provide: MessageProvider,
|
|
2173
2452
|
inject: [JETSTREAM_OPTIONS, JETSTREAM_CONNECTION, JETSTREAM_EVENT_BUS],
|
|
2174
2453
|
useFactory: (options, connection, eventBus) => {
|
|
2175
2454
|
if (options.consumer === false) return null;
|
|
2176
|
-
|
|
2455
|
+
const consumeOptionsMap = /* @__PURE__ */ new Map();
|
|
2456
|
+
if (options.events?.consume)
|
|
2457
|
+
consumeOptionsMap.set("ev" /* Event */, options.events.consume);
|
|
2458
|
+
if (options.broadcast?.consume)
|
|
2459
|
+
consumeOptionsMap.set("broadcast" /* Broadcast */, options.broadcast.consume);
|
|
2460
|
+
if (options.rpc?.mode === "jetstream" && options.rpc.consume) {
|
|
2461
|
+
consumeOptionsMap.set("cmd" /* Command */, options.rpc.consume);
|
|
2462
|
+
}
|
|
2463
|
+
return new MessageProvider(connection, eventBus, consumeOptionsMap);
|
|
2177
2464
|
}
|
|
2178
2465
|
},
|
|
2179
2466
|
// EventRouter — routes event and broadcast messages to handlers
|
|
@@ -2184,20 +2471,33 @@ var JetstreamModule = class {
|
|
|
2184
2471
|
MessageProvider,
|
|
2185
2472
|
PatternRegistry,
|
|
2186
2473
|
JETSTREAM_CODEC,
|
|
2187
|
-
JETSTREAM_EVENT_BUS
|
|
2474
|
+
JETSTREAM_EVENT_BUS,
|
|
2475
|
+
JETSTREAM_ACK_WAIT_MAP
|
|
2188
2476
|
],
|
|
2189
|
-
useFactory: (options, messageProvider, patternRegistry, codec, eventBus) => {
|
|
2477
|
+
useFactory: (options, messageProvider, patternRegistry, codec, eventBus, ackWaitMap) => {
|
|
2190
2478
|
if (options.consumer === false) return null;
|
|
2191
2479
|
const deadLetterConfig = options.onDeadLetter ? {
|
|
2192
2480
|
maxDeliverByStream: /* @__PURE__ */ new Map(),
|
|
2193
2481
|
onDeadLetter: options.onDeadLetter
|
|
2194
2482
|
} : void 0;
|
|
2483
|
+
const processingConfig = {
|
|
2484
|
+
events: {
|
|
2485
|
+
concurrency: options.events?.concurrency,
|
|
2486
|
+
ackExtension: options.events?.ackExtension
|
|
2487
|
+
},
|
|
2488
|
+
broadcast: {
|
|
2489
|
+
concurrency: options.broadcast?.concurrency,
|
|
2490
|
+
ackExtension: options.broadcast?.ackExtension
|
|
2491
|
+
}
|
|
2492
|
+
};
|
|
2195
2493
|
return new EventRouter(
|
|
2196
2494
|
messageProvider,
|
|
2197
2495
|
patternRegistry,
|
|
2198
2496
|
codec,
|
|
2199
2497
|
eventBus,
|
|
2200
|
-
deadLetterConfig
|
|
2498
|
+
deadLetterConfig,
|
|
2499
|
+
processingConfig,
|
|
2500
|
+
ackWaitMap
|
|
2201
2501
|
);
|
|
2202
2502
|
}
|
|
2203
2503
|
},
|
|
@@ -2210,18 +2510,24 @@ var JetstreamModule = class {
|
|
|
2210
2510
|
PatternRegistry,
|
|
2211
2511
|
JETSTREAM_CONNECTION,
|
|
2212
2512
|
JETSTREAM_CODEC,
|
|
2213
|
-
JETSTREAM_EVENT_BUS
|
|
2513
|
+
JETSTREAM_EVENT_BUS,
|
|
2514
|
+
JETSTREAM_ACK_WAIT_MAP
|
|
2214
2515
|
],
|
|
2215
|
-
useFactory: (options, messageProvider, patternRegistry, connection, codec, eventBus) => {
|
|
2516
|
+
useFactory: (options, messageProvider, patternRegistry, connection, codec, eventBus, ackWaitMap) => {
|
|
2216
2517
|
if (options.consumer === false) return null;
|
|
2217
|
-
const
|
|
2518
|
+
const rpcOptions = options.rpc?.mode === "jetstream" ? {
|
|
2519
|
+
timeout: options.rpc.timeout,
|
|
2520
|
+
concurrency: options.rpc.concurrency,
|
|
2521
|
+
ackExtension: options.rpc.ackExtension
|
|
2522
|
+
} : void 0;
|
|
2218
2523
|
return new RpcRouter(
|
|
2219
2524
|
messageProvider,
|
|
2220
2525
|
patternRegistry,
|
|
2221
2526
|
connection,
|
|
2222
2527
|
codec,
|
|
2223
2528
|
eventBus,
|
|
2224
|
-
|
|
2529
|
+
rpcOptions,
|
|
2530
|
+
ackWaitMap
|
|
2225
2531
|
);
|
|
2226
2532
|
}
|
|
2227
2533
|
},
|
|
@@ -2252,9 +2558,10 @@ var JetstreamModule = class {
|
|
|
2252
2558
|
MessageProvider,
|
|
2253
2559
|
EventRouter,
|
|
2254
2560
|
RpcRouter,
|
|
2255
|
-
CoreRpcServer
|
|
2561
|
+
CoreRpcServer,
|
|
2562
|
+
JETSTREAM_ACK_WAIT_MAP
|
|
2256
2563
|
],
|
|
2257
|
-
useFactory: (options, connection, patternRegistry, streamProvider, consumerProvider, messageProvider, eventRouter, rpcRouter, coreRpcServer) => {
|
|
2564
|
+
useFactory: (options, connection, patternRegistry, streamProvider, consumerProvider, messageProvider, eventRouter, rpcRouter, coreRpcServer, ackWaitMap) => {
|
|
2258
2565
|
if (options.consumer === false) return null;
|
|
2259
2566
|
return new JetstreamStrategy(
|
|
2260
2567
|
options,
|
|
@@ -2265,7 +2572,8 @@ var JetstreamModule = class {
|
|
|
2265
2572
|
messageProvider,
|
|
2266
2573
|
eventRouter,
|
|
2267
2574
|
rpcRouter,
|
|
2268
|
-
coreRpcServer
|
|
2575
|
+
coreRpcServer,
|
|
2576
|
+
ackWaitMap
|
|
2269
2577
|
);
|
|
2270
2578
|
}
|
|
2271
2579
|
}
|
|
@@ -2346,8 +2654,13 @@ JetstreamModule = __decorateClass([
|
|
|
2346
2654
|
JetstreamRecordBuilder,
|
|
2347
2655
|
JetstreamStrategy,
|
|
2348
2656
|
JsonCodec,
|
|
2657
|
+
MessageKind,
|
|
2658
|
+
PatternPrefix,
|
|
2349
2659
|
RpcContext,
|
|
2660
|
+
StreamKind,
|
|
2350
2661
|
TransportEvent,
|
|
2351
2662
|
getClientToken,
|
|
2663
|
+
isCoreRpcMode,
|
|
2664
|
+
isJetStreamRpcMode,
|
|
2352
2665
|
toNanos
|
|
2353
2666
|
});
|