@horizon-republic/nestjs-jetstream 2.5.0 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ModuleMetadata, FactoryProvider, Type, Logger, OnApplicationShutdown, DynamicModule } from '@nestjs/common';
2
- import { MsgHdrs, StreamConfig, ConsumerConfig, DeliverPolicy, ReplayPolicy, ConnectionOptions, NatsConnection, Status, JetStreamManager, ConsumerInfo, JsMsg, Msg } from 'nats';
2
+ import { MsgHdrs, StreamConfig, ConsumerConfig, ConsumeOptions, DeliverPolicy, ReplayPolicy, ConnectionOptions, NatsConnection, Status, JetStreamManager, JetStreamClient, ConsumerInfo, JsMsg, Msg } from 'nats';
3
3
  import { MessageHandler, Server, CustomTransportStrategy, ClientProxy, ReadPacket, WritePacket, BaseRpcContext } from '@nestjs/microservices';
4
4
  import { Observable } from 'rxjs';
5
5
 
@@ -31,6 +31,11 @@ interface Codec {
31
31
  decode(data: Uint8Array): unknown;
32
32
  }
33
33
 
34
+ /** Discriminates the kind of message routed through the transport. */
35
+ declare enum MessageKind {
36
+ Event = "event",
37
+ Rpc = "rpc"
38
+ }
34
39
  declare enum TransportEvent {
35
40
  Connect = "connect",
36
41
  Disconnect = "disconnect",
@@ -71,7 +76,7 @@ interface TransportHooks {
71
76
  /** Fired when an RPC handler exceeds its timeout. */
72
77
  [TransportEvent.RpcTimeout](subject: string, correlationId: string): void;
73
78
  /** Fired after a message is successfully routed to its handler. */
74
- [TransportEvent.MessageRouted](subject: string, kind: 'rpc' | 'event'): void;
79
+ [TransportEvent.MessageRouted](subject: string, kind: MessageKind): void;
75
80
  /** Fired at the start of the graceful shutdown sequence. */
76
81
  [TransportEvent.ShutdownStart](): void;
77
82
  /** Fired after graceful shutdown completes. */
@@ -101,6 +106,16 @@ interface DeadLetterInfo {
101
106
  timestamp: string;
102
107
  }
103
108
 
109
+ /** Health status returned by the JetStream health indicator. */
110
+ interface JetstreamHealthStatus {
111
+ /** Whether the NATS connection is alive. */
112
+ connected: boolean;
113
+ /** NATS server URL, or `null` if not connected. */
114
+ server: string | null;
115
+ /** Round-trip latency in ms, or `null` if disconnected. */
116
+ latency: number | null;
117
+ }
118
+
104
119
  /**
105
120
  * RPC transport configuration.
106
121
  *
@@ -123,11 +138,50 @@ type RpcConfig = {
123
138
  stream?: Partial<StreamConfig>;
124
139
  /** Raw NATS ConsumerConfig overrides for the command consumer. */
125
140
  consumer?: Partial<ConsumerConfig>;
141
+ /** Options passed to the nats.js `consumer.consume()` call for the command consumer. */
142
+ consume?: Partial<ConsumeOptions>;
143
+ /** Maximum number of concurrent RPC handler executions. */
144
+ concurrency?: number;
145
+ /**
146
+ * Auto-extend ack deadline via `msg.working()` during RPC handler execution.
147
+ * The RPC handler timeout (`setTimeout` + `msg.term()`) still acts as the hard cap.
148
+ */
149
+ ackExtension?: boolean | number;
126
150
  };
127
151
  /** Overrides for JetStream stream and consumer configuration. */
128
152
  interface StreamConsumerOverrides {
129
153
  stream?: Partial<StreamConfig>;
130
154
  consumer?: Partial<ConsumerConfig>;
155
+ /**
156
+ * Options passed to the nats.js `consumer.consume()` call.
157
+ * Controls prefetch buffer size, idle heartbeat interval, and auto-refill thresholds.
158
+ *
159
+ * nats.js supports two consumption modes (message-based and byte-based).
160
+ * Do not mix `max_bytes`/`threshold_bytes` with `threshold_messages` —
161
+ * use one mode or the other.
162
+ *
163
+ * @see https://github.com/nats-io/nats.js — ConsumeOptions
164
+ */
165
+ consume?: Partial<ConsumeOptions>;
166
+ /**
167
+ * Maximum number of concurrent handler executions (RxJS `mergeMap` limit).
168
+ *
169
+ * Default: `undefined` (unlimited — naturally bounded by `max_ack_pending`).
170
+ * Set this to protect downstream systems from overload.
171
+ *
172
+ * **Important:** if `concurrency < max_ack_pending`, messages buffer in RxJS
173
+ * while their NATS ack timer ticks. Increase `ack_wait` proportionally to
174
+ * prevent unnecessary redeliveries.
175
+ */
176
+ concurrency?: number;
177
+ /**
178
+ * Auto-extend the NATS ack deadline via `msg.working()` during handler execution.
179
+ *
180
+ * - `false` (default): disabled — NATS redelivers after `ack_wait` if not acked.
181
+ * - `true`: auto-extend at `ack_wait / 2` interval (calculated from consumer config).
182
+ * - `number`: explicit extension interval in milliseconds.
183
+ */
184
+ ackExtension?: boolean | number;
131
185
  }
132
186
  /**
133
187
  * Configuration for ordered event consumers.
@@ -281,12 +335,17 @@ type JetstreamModuleAsyncOptions = {
281
335
  /**
282
336
  * Identifies a JetStream stream/consumer kind.
283
337
  *
284
- * - `'ev'` — Workqueue events (at-least-once delivery to one consumer).
285
- * - `'cmd'` — RPC commands (JetStream mode only).
286
- * - `'broadcast'` — Broadcast events (fan-out to all consumers).
287
- * - `'ordered'` — Ordered events (strict sequential delivery, Limits retention).
338
+ * - `Event` — Workqueue events (at-least-once delivery to one consumer).
339
+ * - `Command` — RPC commands (JetStream mode only).
340
+ * - `Broadcast` — Broadcast events (fan-out to all consumers).
341
+ * - `Ordered` — Ordered events (strict sequential delivery, Limits retention).
288
342
  */
289
- type StreamKind = 'ev' | 'cmd' | 'broadcast' | 'ordered';
343
+ declare enum StreamKind {
344
+ Event = "ev",
345
+ Command = "cmd",
346
+ Broadcast = "broadcast",
347
+ Ordered = "ordered"
348
+ }
290
349
 
291
350
  /** @internal Grouped pattern lists by stream kind, used for stream/consumer setup. */
292
351
  interface PatternsByKind {
@@ -299,6 +358,33 @@ interface PatternsByKind {
299
358
  /** Ordered event patterns (strict sequential delivery). */
300
359
  ordered: string[];
301
360
  }
361
+ /** Options for configuring event/broadcast processing behavior. */
362
+ interface EventProcessingConfig {
363
+ events?: {
364
+ concurrency?: number;
365
+ ackExtension?: boolean | number;
366
+ };
367
+ broadcast?: {
368
+ concurrency?: number;
369
+ ackExtension?: boolean | number;
370
+ };
371
+ }
372
+ /** Options for dead letter queue handling. */
373
+ interface DeadLetterConfig {
374
+ /**
375
+ * Map of stream name -> max_deliver value.
376
+ * Used to detect when a message from a given stream has exhausted all delivery attempts.
377
+ */
378
+ maxDeliverByStream: Map<string, number>;
379
+ /** Async callback invoked when a message exhausts all deliveries. */
380
+ onDeadLetter(info: DeadLetterInfo): Promise<void>;
381
+ }
382
+ /** Options for configuring RPC processing behavior. */
383
+ interface RpcRouterOptions {
384
+ timeout?: number;
385
+ concurrency?: number;
386
+ ackExtension?: boolean | number;
387
+ }
302
388
 
303
389
  /**
304
390
  * Central event bus for transport lifecycle notifications.
@@ -352,6 +438,7 @@ declare class ConnectionProvider {
352
438
  private readonly logger;
353
439
  private connection;
354
440
  private connectionPromise;
441
+ private jsClient;
355
442
  private jsmInstance;
356
443
  private jsmPromise;
357
444
  constructor(options: JetstreamModuleOptions, eventBus: EventBus);
@@ -367,6 +454,16 @@ declare class ConnectionProvider {
367
454
  * @returns The JetStreamManager for stream/consumer administration.
368
455
  */
369
456
  getJetStreamManager(): Promise<JetStreamManager>;
457
+ /**
458
+ * Get a cached JetStream client.
459
+ *
460
+ * Invalidated automatically on reconnect and shutdown so consumers always
461
+ * operate against the live connection.
462
+ *
463
+ * @returns The cached JetStreamClient.
464
+ * @throws Error if the connection has not been established yet.
465
+ */
466
+ getJetStreamClient(): JetStreamClient;
370
467
  /** Direct access to the raw NATS connection, or `null` if not yet connected. */
371
468
  get unwrap(): NatsConnection | null;
372
469
  /**
@@ -393,6 +490,7 @@ declare class PatternRegistry {
393
490
  private readonly options;
394
491
  private readonly logger;
395
492
  private readonly registry;
493
+ private cachedPatterns;
396
494
  constructor(options: JetstreamModuleOptions);
397
495
  /**
398
496
  * Register all handlers from the NestJS strategy.
@@ -404,21 +502,17 @@ declare class PatternRegistry {
404
502
  getHandler(subject: string): MessageHandler | null;
405
503
  /** Get all registered broadcast patterns (for consumer filter_subject setup). */
406
504
  getBroadcastPatterns(): string[];
407
- /** Check if any broadcast handlers are registered. */
408
505
  hasBroadcastHandlers(): boolean;
409
- /** Check if any RPC (command) handlers are registered. */
410
506
  hasRpcHandlers(): boolean;
411
- /** Check if any workqueue event handlers are registered. */
412
507
  hasEventHandlers(): boolean;
413
- /** Check if any ordered event handlers are registered. */
414
508
  hasOrderedHandlers(): boolean;
415
509
  /** Get fully-qualified NATS subjects for ordered handlers. */
416
510
  getOrderedSubjects(): string[];
417
- /** Get patterns grouped by kind. */
511
+ /** Get patterns grouped by kind (cached after registration). */
418
512
  getPatternsByKind(): PatternsByKind;
419
513
  /** Normalize a full NATS subject back to the user-facing pattern. */
420
514
  normalizeSubject(subject: string): string;
421
- /** Log a summary of all registered handlers. */
515
+ private buildPatternsByKind;
422
516
  private logSummary;
423
517
  }
424
518
 
@@ -485,16 +579,6 @@ declare class StreamProvider {
485
579
  private getOverrides;
486
580
  }
487
581
 
488
- /** Options for dead letter queue handling. */
489
- interface DeadLetterConfig {
490
- /**
491
- * Map of stream name -> max_deliver value.
492
- * Used to detect when a message from a given stream has exhausted all delivery attempts.
493
- */
494
- maxDeliverByStream: Map<string, number>;
495
- /** Async callback invoked when a message exhausts all deliveries. */
496
- onDeadLetter(info: DeadLetterInfo): Promise<void>;
497
- }
498
582
  /**
499
583
  * Routes incoming event messages (workqueue, broadcast, and ordered) to NestJS handlers.
500
584
  *
@@ -511,9 +595,11 @@ declare class EventRouter {
511
595
  private readonly codec;
512
596
  private readonly eventBus;
513
597
  private readonly deadLetterConfig?;
598
+ private readonly processingConfig?;
599
+ private readonly ackWaitMap?;
514
600
  private readonly logger;
515
601
  private readonly subscriptions;
516
- constructor(messageProvider: MessageProvider, patternRegistry: PatternRegistry, codec: Codec, eventBus: EventBus, deadLetterConfig?: DeadLetterConfig | undefined);
602
+ constructor(messageProvider: MessageProvider, patternRegistry: PatternRegistry, codec: Codec, eventBus: EventBus, deadLetterConfig?: DeadLetterConfig | undefined, processingConfig?: EventProcessingConfig | undefined, ackWaitMap?: Map<StreamKind, number> | undefined);
517
603
  /**
518
604
  * Update the max_deliver thresholds from actual NATS consumer configs.
519
605
  * Called after consumers are ensured so the DLQ map reflects reality.
@@ -525,12 +611,16 @@ declare class EventRouter {
525
611
  destroy(): void;
526
612
  /** Subscribe to a message stream and route each message. */
527
613
  private subscribeToStream;
614
+ private getConcurrency;
615
+ private getAckExtensionConfig;
528
616
  /** Handle a single event message: decode -> execute handler -> ack/nak. */
529
617
  private handle;
530
- /** Execute handler, then ack on success or nak/dead-letter on failure. */
531
- private executeHandler;
532
618
  /** Handle an ordered message: decode -> execute handler -> no ack/nak. */
533
619
  private handleOrdered;
620
+ /** Resolve handler, decode payload, and build context. Returns null on failure. */
621
+ private decodeMessage;
622
+ /** Execute handler, then ack on success or nak/dead-letter on failure. */
623
+ private executeHandler;
534
624
  /** Check if the message has exhausted all delivery attempts. */
535
625
  private isDeadLetter;
536
626
  /** Handle a dead letter: invoke callback, then term or nak based on result. */
@@ -555,10 +645,16 @@ declare class RpcRouter {
555
645
  private readonly connection;
556
646
  private readonly codec;
557
647
  private readonly eventBus;
648
+ private readonly rpcOptions?;
649
+ private readonly ackWaitMap?;
558
650
  private readonly logger;
559
651
  private readonly timeout;
652
+ private readonly concurrency;
653
+ private resolvedAckExtensionInterval;
560
654
  private subscription;
561
- constructor(messageProvider: MessageProvider, patternRegistry: PatternRegistry, connection: ConnectionProvider, codec: Codec, eventBus: EventBus, timeout?: number);
655
+ constructor(messageProvider: MessageProvider, patternRegistry: PatternRegistry, connection: ConnectionProvider, codec: Codec, eventBus: EventBus, rpcOptions?: RpcRouterOptions | undefined, ackWaitMap?: Map<StreamKind, number> | undefined);
656
+ /** Lazily resolve the ack extension interval (needs ackWaitMap populated at runtime). */
657
+ private get ackExtensionInterval();
562
658
  /** Start routing command messages to handlers. */
563
659
  start(): void;
564
660
  /** Stop routing and unsubscribe. */
@@ -612,6 +708,7 @@ declare class ConsumerProvider {
612
708
  declare class MessageProvider {
613
709
  private readonly connection;
614
710
  private readonly eventBus;
711
+ private readonly consumeOptionsMap;
615
712
  private readonly logger;
616
713
  private readonly activeIterators;
617
714
  private orderedReadyResolve;
@@ -621,7 +718,7 @@ declare class MessageProvider {
621
718
  private commandMessages$;
622
719
  private broadcastMessages$;
623
720
  private orderedMessages$;
624
- constructor(connection: ConnectionProvider, eventBus: EventBus);
721
+ constructor(connection: ConnectionProvider, eventBus: EventBus, consumeOptionsMap?: Map<StreamKind, Partial<ConsumeOptions>>);
625
722
  /** Observable stream of workqueue event messages. */
626
723
  get events$(): Observable<JsMsg>;
627
724
  /** Observable stream of RPC command messages (jetstream mode). */
@@ -645,6 +742,7 @@ declare class MessageProvider {
645
742
  *
646
743
  * @param streamName - JetStream stream to consume from.
647
744
  * @param filterSubjects - NATS subjects to filter on.
745
+ * @param orderedConfig - Optional overrides for ordered consumer options.
648
746
  */
649
747
  startOrdered(streamName: string, filterSubjects: string[], orderedConfig?: OrderedEventOverrides): Promise<void>;
650
748
  /** Stop all consumer flows and reinitialize subjects for potential restart. */
@@ -655,8 +753,12 @@ declare class MessageProvider {
655
753
  private consumeOnce;
656
754
  /** Get the target subject for a consumer kind. */
657
755
  private getTargetSubject;
756
+ /** Monitor heartbeats and restart the consumer iterator on prolonged silence. */
757
+ private monitorConsumerHealth;
658
758
  /** Create a self-healing ordered consumer flow. */
659
759
  private createOrderedFlow;
760
+ /** Shared self-healing flow: defer -> retry with exponential backoff on error/completion. */
761
+ private createSelfHealingFlow;
660
762
  /** Single iteration: create ordered consumer -> iterate messages. */
661
763
  private consumeOrderedOnce;
662
764
  }
@@ -682,10 +784,11 @@ declare class JetstreamStrategy extends Server implements CustomTransportStrateg
682
784
  private readonly eventRouter;
683
785
  private readonly rpcRouter;
684
786
  private readonly coreRpcServer;
787
+ private readonly ackWaitMap;
685
788
  readonly transportId: symbol;
686
789
  private readonly listeners;
687
790
  private started;
688
- constructor(options: JetstreamModuleOptions, connection: ConnectionProvider, patternRegistry: PatternRegistry, streamProvider: StreamProvider, consumerProvider: ConsumerProvider, messageProvider: MessageProvider, eventRouter: EventRouter, rpcRouter: RpcRouter, coreRpcServer: CoreRpcServer);
791
+ constructor(options: JetstreamModuleOptions, connection: ConnectionProvider, patternRegistry: PatternRegistry, streamProvider: StreamProvider, consumerProvider: ConsumerProvider, messageProvider: MessageProvider, eventRouter: EventRouter, rpcRouter: RpcRouter, coreRpcServer: CoreRpcServer, ackWaitMap?: Map<StreamKind, number>);
689
792
  /**
690
793
  * Start the transport: register handlers, create infrastructure, begin consumption.
691
794
  *
@@ -709,16 +812,18 @@ declare class JetstreamStrategy extends Server implements CustomTransportStrateg
709
812
  unwrap<T>(): T;
710
813
  /** Access the pattern registry (for module-level introspection). */
711
814
  getPatternRegistry(): PatternRegistry;
712
- /** Determine which JetStream streams are needed. */
713
- private resolveStreamKinds;
714
- /** Determine which stream kinds need durable consumers (ordered consumers are ephemeral). */
715
- private resolveDurableConsumerKinds;
815
+ /** Determine which streams and durable consumers are needed. */
816
+ private resolveRequiredKinds;
817
+ /** Populate the shared ack_wait map from actual NATS consumer configs. */
818
+ private populateAckWaitMap;
716
819
  /** Build max_deliver map from actual NATS consumer configs (not options). */
717
820
  private buildMaxDeliverMap;
718
- private isCoreRpcMode;
719
- private isJetStreamRpcMode;
720
821
  }
721
822
 
823
+ /** Minimal interface for anything that can be stopped during shutdown. */
824
+ interface Stoppable {
825
+ close(): void;
826
+ }
722
827
  /**
723
828
  * Orchestrates graceful transport shutdown.
724
829
  *
@@ -738,9 +843,9 @@ declare class ShutdownManager {
738
843
  /**
739
844
  * Execute the full shutdown sequence.
740
845
  *
741
- * @param strategy Optional strategy to close (stops consumers and subscriptions).
846
+ * @param strategy Optional stoppable to close (stops consumers and subscriptions).
742
847
  */
743
- shutdown(strategy?: JetstreamStrategy): Promise<void>;
848
+ shutdown(strategy?: Stoppable): Promise<void>;
744
849
  }
745
850
 
746
851
  /**
@@ -841,6 +946,8 @@ declare class JetstreamClient extends ClientProxy {
841
946
  private readonly logger;
842
947
  /** Target service name this client sends messages to. */
843
948
  private readonly targetName;
949
+ /** Pre-cached caller name derived from rootOptions.name, computed once in constructor. */
950
+ private readonly callerName;
844
951
  /** Shared inbox for JetStream-mode RPC responses. */
845
952
  private inbox;
846
953
  private inboxSubscription;
@@ -900,8 +1007,6 @@ declare class JetstreamClient extends ClientProxy {
900
1007
  private buildHeaders;
901
1008
  /** Extract data, headers, and timeout from raw packet data or JetstreamRecord. */
902
1009
  private extractRecordData;
903
- private isCoreRpcMode;
904
- private isJetStreamRpcMode;
905
1010
  private getRpcTimeout;
906
1011
  }
907
1012
 
@@ -1073,17 +1178,6 @@ declare class RpcContext extends BaseRpcContext<[NatsMessage]> {
1073
1178
  };
1074
1179
  }
1075
1180
 
1076
- /**
1077
- * Health status returned by {@link JetstreamHealthIndicator.check}.
1078
- */
1079
- interface JetstreamHealthStatus {
1080
- /** Whether the NATS connection is alive. */
1081
- connected: boolean;
1082
- /** NATS server URL, or `null` if not connected. */
1083
- server: string | null;
1084
- /** Round-trip latency in ms, or `null` if disconnected. */
1085
- latency: number | null;
1086
- }
1087
1181
  /**
1088
1182
  * Health indicator result compatible with @nestjs/terminus.
1089
1183
  *
@@ -1184,5 +1278,19 @@ declare enum JetstreamHeader {
1184
1278
  /** Set to `'true'` on error responses so the client can distinguish success from failure. */
1185
1279
  Error = "x-error"
1186
1280
  }
1281
+ /**
1282
+ * Prefixes used in event patterns to route to specific stream types.
1283
+ * Applied by the user when emitting events (e.g. `client.emit('broadcast:config.updated', data)`).
1284
+ */
1285
+ declare enum PatternPrefix {
1286
+ /** Route to the shared broadcast stream. */
1287
+ Broadcast = "broadcast:",
1288
+ /** Route to the ordered stream. */
1289
+ Ordered = "ordered:"
1290
+ }
1291
+ /** Check if the RPC config specifies JetStream mode. */
1292
+ declare const isJetStreamRpcMode: (rpc: RpcConfig | undefined) => boolean;
1293
+ /** Check if the RPC config specifies Core mode (default). */
1294
+ declare const isCoreRpcMode: (rpc: RpcConfig | undefined) => boolean;
1187
1295
 
1188
- export { type Codec, type DeadLetterInfo, EventBus, JETSTREAM_CODEC, JETSTREAM_CONNECTION, JETSTREAM_EVENT_BUS, JETSTREAM_OPTIONS, JetstreamClient, type JetstreamFeatureOptions, JetstreamHeader, JetstreamHealthIndicator, type JetstreamHealthStatus, JetstreamModule, type JetstreamModuleAsyncOptions, type JetstreamModuleOptions, JetstreamRecord, JetstreamRecordBuilder, JetstreamStrategy, JsonCodec, type OrderedEventOverrides, type RpcConfig, RpcContext, type StreamConsumerOverrides, TransportEvent, type TransportHooks, getClientToken, toNanos };
1296
+ export { type Codec, type DeadLetterInfo, EventBus, JETSTREAM_CODEC, JETSTREAM_CONNECTION, JETSTREAM_EVENT_BUS, JETSTREAM_OPTIONS, JetstreamClient, type JetstreamFeatureOptions, JetstreamHeader, JetstreamHealthIndicator, type JetstreamHealthStatus, JetstreamModule, type JetstreamModuleAsyncOptions, type JetstreamModuleOptions, JetstreamRecord, JetstreamRecordBuilder, JetstreamStrategy, JsonCodec, MessageKind, type OrderedEventOverrides, PatternPrefix, type RpcConfig, RpcContext, type StreamConsumerOverrides, StreamKind, TransportEvent, type TransportHooks, getClientToken, isCoreRpcMode, isJetStreamRpcMode, toNanos };