@horizon-republic/nestjs-jetstream 2.9.1 → 2.11.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 +58 -71
- package/dist/index.cjs +2545 -607
- package/dist/index.d.cts +1562 -1082
- package/dist/index.d.ts +1562 -1082
- package/dist/index.js +2552 -611
- package/package.json +31 -17
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { ModuleMetadata, FactoryProvider, Type,
|
|
2
|
-
import { MsgHdrs,
|
|
3
|
-
import { StreamConfig, ConsumerConfig, ConsumeOptions, DeliverPolicy, ReplayPolicy,
|
|
4
|
-
import {
|
|
1
|
+
import { Logger, ModuleMetadata, FactoryProvider, Type, OnApplicationShutdown, DynamicModule } from '@nestjs/common';
|
|
2
|
+
import { MsgHdrs, NatsConnection, Status, ConnectionOptions, Msg } from '@nats-io/transport-node';
|
|
3
|
+
import { JetStreamManager, JetStreamClient, StreamConfig, ConsumerConfig, ConsumeOptions, DeliverPolicy, ReplayPolicy, ConsumerInfo, JsMsg } from '@nats-io/jetstream';
|
|
4
|
+
import { Registry } from 'prom-client';
|
|
5
|
+
import { Span } from '@opentelemetry/api';
|
|
6
|
+
import { ClientProxy, ReadPacket, WritePacket, MessageHandler, Server, CustomTransportStrategy, BaseRpcContext } from '@nestjs/microservices';
|
|
5
7
|
import { Observable } from 'rxjs';
|
|
6
8
|
|
|
7
9
|
/**
|
|
@@ -32,11 +34,47 @@ interface Codec {
|
|
|
32
34
|
decode(data: Uint8Array): unknown;
|
|
33
35
|
}
|
|
34
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Identifies a JetStream stream/consumer kind.
|
|
39
|
+
*
|
|
40
|
+
* - `Event` — Workqueue events (at-least-once delivery to one consumer).
|
|
41
|
+
* - `Command` — RPC commands (JetStream mode only).
|
|
42
|
+
* - `Broadcast` — Broadcast events (fan-out to all consumers).
|
|
43
|
+
* - `Ordered` — Ordered events (strict sequential delivery, Limits retention).
|
|
44
|
+
*/
|
|
45
|
+
declare enum StreamKind {
|
|
46
|
+
Event = "ev",
|
|
47
|
+
Command = "cmd",
|
|
48
|
+
Broadcast = "broadcast",
|
|
49
|
+
Ordered = "ordered"
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Subset of {@link StreamKind} used for direct subject building.
|
|
53
|
+
*
|
|
54
|
+
* Excludes `Broadcast` because broadcast subjects use a different
|
|
55
|
+
* naming convention (`broadcast.{pattern}` instead of `{service}.{kind}.{pattern}`).
|
|
56
|
+
*/
|
|
57
|
+
type SubjectKind = Exclude<StreamKind, StreamKind.Broadcast>;
|
|
58
|
+
|
|
35
59
|
/** Discriminates the kind of message routed through the transport. */
|
|
36
60
|
declare enum MessageKind {
|
|
37
61
|
Event = "event",
|
|
38
62
|
Rpc = "rpc"
|
|
39
63
|
}
|
|
64
|
+
/** Outcome of a handler invocation, used as a label on processing metrics. */
|
|
65
|
+
type HandlerStatus = 'success' | 'error' | 'retried' | 'terminated';
|
|
66
|
+
/**
|
|
67
|
+
* Outcome of a client publish (event emit or RPC publish leg). Outbound
|
|
68
|
+
* operations either acknowledge cleanly or surface a transport error — there
|
|
69
|
+
* is no retried/terminated dimension at the publish boundary.
|
|
70
|
+
*/
|
|
71
|
+
type PublishStatus = 'success' | 'error';
|
|
72
|
+
/**
|
|
73
|
+
* Outcome of a full RPC round-trip from the caller's perspective. Adds the
|
|
74
|
+
* `timeout` dimension on top of {@link PublishStatus} so percentile analysis
|
|
75
|
+
* can distinguish slow successes from deadline-exceeded calls.
|
|
76
|
+
*/
|
|
77
|
+
type RpcOutcomeStatus = 'success' | 'error' | 'timeout';
|
|
40
78
|
declare enum TransportEvent {
|
|
41
79
|
Connect = "connect",
|
|
42
80
|
Disconnect = "disconnect",
|
|
@@ -46,7 +84,11 @@ declare enum TransportEvent {
|
|
|
46
84
|
MessageRouted = "messageRouted",
|
|
47
85
|
ShutdownStart = "shutdownStart",
|
|
48
86
|
ShutdownComplete = "shutdownComplete",
|
|
49
|
-
DeadLetter = "deadLetter"
|
|
87
|
+
DeadLetter = "deadLetter",
|
|
88
|
+
ConsumerRecovered = "consumerRecovered",
|
|
89
|
+
HandlerCompleted = "handlerCompleted",
|
|
90
|
+
Published = "published",
|
|
91
|
+
RpcCompleted = "rpcCompleted"
|
|
50
92
|
}
|
|
51
93
|
/**
|
|
52
94
|
* Hook callbacks for transport lifecycle and operational events.
|
|
@@ -84,7 +126,59 @@ interface TransportHooks {
|
|
|
84
126
|
[TransportEvent.ShutdownComplete](): void;
|
|
85
127
|
/** Fired when a message exhausts all delivery attempts (dead letter). */
|
|
86
128
|
[TransportEvent.DeadLetter](info: DeadLetterInfo): void;
|
|
129
|
+
/**
|
|
130
|
+
* Fired when a consumer's self-healing flow successfully recovers after
|
|
131
|
+
* one or more failed restart attempts. Useful for "service is back" alerts
|
|
132
|
+
* and to balance the noise from preceding error/restart logs.
|
|
133
|
+
*
|
|
134
|
+
* @param label Stream kind label (`event`, `broadcast`, `ordered`, etc.)
|
|
135
|
+
* or consumer name passed to `createSelfHealingFlow`.
|
|
136
|
+
* @param attempts How many consecutive failed attempts preceded the recovery.
|
|
137
|
+
*/
|
|
138
|
+
[TransportEvent.ConsumerRecovered](label: string, attempts: number): void;
|
|
139
|
+
/**
|
|
140
|
+
* Fired immediately after a handler returns or throws.
|
|
141
|
+
*
|
|
142
|
+
* Used by built-in metrics; users can register their own handler for
|
|
143
|
+
* latency tracking, slow-handler alerting, or custom observability.
|
|
144
|
+
*
|
|
145
|
+
* @param subject The declared NATS pattern (from `@EventPattern` / `@MessagePattern`).
|
|
146
|
+
* @param kind Stream kind: `Event`, `Command`, `Broadcast`, `Ordered`.
|
|
147
|
+
* @param durationMs Wall-clock time in milliseconds from handler entry to settlement.
|
|
148
|
+
* @param status Outcome: `success`, `error`, `retried`, or `terminated`.
|
|
149
|
+
*/
|
|
150
|
+
[TransportEvent.HandlerCompleted](subject: string, kind: StreamKind, durationMs: number, status: HandlerStatus): void;
|
|
151
|
+
/**
|
|
152
|
+
* Fired after every client-side publish (event emit or RPC publish leg)
|
|
153
|
+
* completes, regardless of outcome.
|
|
154
|
+
*
|
|
155
|
+
* @param subject Declared user pattern (e.g. `orders.created`) — bounded
|
|
156
|
+
* by handler registration, safe for high-cardinality labels.
|
|
157
|
+
* @param kind Stream kind the publish targets: `Event`, `Broadcast`,
|
|
158
|
+
* `Ordered`, or `Command` (RPC publish leg).
|
|
159
|
+
* @param durationMs Wall-clock time from publish initiation to ack/error.
|
|
160
|
+
* @param status `success` when the publish acked, `error` otherwise.
|
|
161
|
+
*/
|
|
162
|
+
[TransportEvent.Published](subject: string, kind: StreamKind, durationMs: number, status: PublishStatus): void;
|
|
163
|
+
/**
|
|
164
|
+
* Fired after an RPC round-trip completes from the caller's perspective —
|
|
165
|
+
* either a reply is received, the call errors out, or the deadline expires.
|
|
166
|
+
*
|
|
167
|
+
* Distinct from {@link Published} which only covers the publish leg.
|
|
168
|
+
*
|
|
169
|
+
* @param subject Declared command pattern (e.g. `orders.get`).
|
|
170
|
+
* @param durationMs Wall-clock time from request initiation to settlement.
|
|
171
|
+
* @param status `success` for a successful reply, `error` for transport/
|
|
172
|
+
* handler errors, `timeout` when the deadline expired.
|
|
173
|
+
*/
|
|
174
|
+
[TransportEvent.RpcCompleted](subject: string, durationMs: number, status: RpcOutcomeStatus): void;
|
|
87
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Internal subscriber for a transport event. Multiple subscribers may be
|
|
178
|
+
* registered per event via `EventBus.subscribe()` — used by built-in
|
|
179
|
+
* observers (e.g. metrics) without overriding the user-provided hook.
|
|
180
|
+
*/
|
|
181
|
+
type TransportEventSubscriber<K extends keyof TransportHooks> = (...args: Parameters<TransportHooks[K]>) => unknown;
|
|
88
182
|
/**
|
|
89
183
|
* Context passed to the onDeadLetter callback when a message exhausts all delivery attempts.
|
|
90
184
|
*/
|
|
@@ -118,1372 +212,1758 @@ interface JetstreamHealthStatus {
|
|
|
118
212
|
}
|
|
119
213
|
|
|
120
214
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* `retention` is excluded because it is controlled by the transport layer
|
|
124
|
-
* (Workqueue for events/commands, Limits for broadcast/ordered).
|
|
125
|
-
* Any `retention` value provided at runtime is silently stripped.
|
|
215
|
+
* Labels (in seconds) for histogram bucket configuration.
|
|
216
|
+
* Override defaults via `JetstreamModuleOptions.metrics.buckets`.
|
|
126
217
|
*/
|
|
127
|
-
|
|
218
|
+
interface HistogramBuckets {
|
|
219
|
+
/** Buckets for `jetstream_handler_duration_seconds`. */
|
|
220
|
+
handlerDuration?: number[];
|
|
221
|
+
/** Buckets for `jetstream_publish_duration_seconds`. */
|
|
222
|
+
publishDuration?: number[];
|
|
223
|
+
/** Buckets for `jetstream_rpc_duration_seconds`. */
|
|
224
|
+
rpcDuration?: number[];
|
|
225
|
+
}
|
|
128
226
|
/**
|
|
129
|
-
*
|
|
227
|
+
* Configuration for built-in Prometheus metrics. All fields are optional;
|
|
228
|
+
* sensible production defaults are applied when missing.
|
|
229
|
+
*/
|
|
230
|
+
interface MetricsConfig {
|
|
231
|
+
/**
|
|
232
|
+
* `prom-client` registry to write metrics into. Defaults to the global
|
|
233
|
+
* `register` from `prom-client`, which is also what
|
|
234
|
+
* `@willsoto/nestjs-prometheus` exposes via `/metrics`.
|
|
235
|
+
*/
|
|
236
|
+
register?: Registry;
|
|
237
|
+
/** Metric name prefix. Defaults to `'jetstream_'`. */
|
|
238
|
+
prefix?: string;
|
|
239
|
+
/** Labels merged into every metric. Useful for service/env tagging. */
|
|
240
|
+
defaultLabels?: Record<string, string>;
|
|
241
|
+
/**
|
|
242
|
+
* Polling interval (ms) for gauge metrics that query `JetStreamManager`
|
|
243
|
+
* (consumer pending, stream messages, etc.). Default `15_000`.
|
|
244
|
+
* Set to `0` to disable polling — counter/histogram metrics still update
|
|
245
|
+
* via the event bus.
|
|
246
|
+
*/
|
|
247
|
+
pollInterval?: number;
|
|
248
|
+
/** Override default histogram buckets. */
|
|
249
|
+
buckets?: HistogramBuckets;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Shorthand for `MetricsConfig`. Pass `true` to enable with all defaults,
|
|
253
|
+
* `false` (or omit) to disable entirely.
|
|
254
|
+
*/
|
|
255
|
+
type MetricsOption = boolean | MetricsConfig;
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Instrumentation scope name reported on every span the library emits.
|
|
259
|
+
* Matches the npm package name — the convention used by
|
|
260
|
+
* `@opentelemetry/instrumentation-*` and most third-party instrumentations.
|
|
261
|
+
*/
|
|
262
|
+
declare const TRACER_NAME = "@horizon-republic/nestjs-jetstream";
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Enumeration of OpenTelemetry trace kinds this library can emit.
|
|
130
266
|
*
|
|
131
|
-
*
|
|
132
|
-
* -
|
|
133
|
-
*
|
|
267
|
+
* Each identifier is toggleable via `otel.traces`. Functional kinds (publish,
|
|
268
|
+
* consume, RPC client round-trip, dead letter) are enabled by default;
|
|
269
|
+
* infrastructure kinds (connection lifecycle, self-healing, provisioning,
|
|
270
|
+
* migration, shutdown) are opt-in.
|
|
134
271
|
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
272
|
+
* @example
|
|
273
|
+
* JetstreamModule.forRoot({
|
|
274
|
+
* otel: {
|
|
275
|
+
* traces: [JetstreamTrace.Publish, JetstreamTrace.Consume, JetstreamTrace.ConnectionLifecycle],
|
|
276
|
+
* },
|
|
277
|
+
* })
|
|
137
278
|
*/
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
timeout?: number;
|
|
142
|
-
} | {
|
|
143
|
-
mode: 'jetstream';
|
|
144
|
-
/** Handler timeout in ms. Default: 180_000 (3 min). */
|
|
145
|
-
timeout?: number;
|
|
146
|
-
/** Raw NATS StreamConfig overrides for the command stream. */
|
|
147
|
-
stream?: StreamConfigOverrides;
|
|
148
|
-
/** Raw NATS ConsumerConfig overrides for the command consumer. */
|
|
149
|
-
consumer?: Partial<ConsumerConfig>;
|
|
150
|
-
/** Options passed to the nats.js `consumer.consume()` call for the command consumer. */
|
|
151
|
-
consume?: Partial<ConsumeOptions>;
|
|
152
|
-
/** Maximum number of concurrent RPC handler executions. */
|
|
153
|
-
concurrency?: number;
|
|
279
|
+
declare enum JetstreamTrace {
|
|
280
|
+
/** `PRODUCER` span around each `emit()` / `send()` publish operation. Default: ON. */
|
|
281
|
+
Publish = "publish",
|
|
154
282
|
/**
|
|
155
|
-
*
|
|
156
|
-
*
|
|
283
|
+
* `CONSUMER` span around each message delivery to a handler. Retries
|
|
284
|
+
* produce separate spans with `messaging.nats.message.delivery_count > 1`.
|
|
285
|
+
* Default: ON.
|
|
157
286
|
*/
|
|
158
|
-
|
|
159
|
-
};
|
|
160
|
-
/** Overrides for JetStream stream and consumer configuration. */
|
|
161
|
-
interface StreamConsumerOverrides {
|
|
162
|
-
stream?: StreamConfigOverrides;
|
|
163
|
-
consumer?: Partial<ConsumerConfig>;
|
|
287
|
+
Consume = "consume",
|
|
164
288
|
/**
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
* nats.js supports two consumption modes (message-based and byte-based).
|
|
169
|
-
* Do not mix `max_bytes`/`threshold_bytes` with `threshold_messages` —
|
|
170
|
-
* use one mode or the other.
|
|
171
|
-
*
|
|
172
|
-
* @see https://github.com/nats-io/nats.js — ConsumeOptions
|
|
289
|
+
* `CLIENT` span covering a full RPC round-trip on the caller side
|
|
290
|
+
* (`client.send()` → reply received). Wraps the inner publish.
|
|
291
|
+
* Default: ON.
|
|
173
292
|
*/
|
|
174
|
-
|
|
293
|
+
RpcClientSend = "rpc.client.send",
|
|
175
294
|
/**
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
* Default:
|
|
179
|
-
* Set this to protect downstream systems from overload.
|
|
180
|
-
*
|
|
181
|
-
* **Important:** if `concurrency < max_ack_pending`, messages buffer in RxJS
|
|
182
|
-
* while their NATS ack timer ticks. Increase `ack_wait` proportionally to
|
|
183
|
-
* prevent unnecessary redeliveries.
|
|
295
|
+
* `INTERNAL` span emitted when a message exhausts `maxDeliver` and is
|
|
296
|
+
* dead-lettered. Captures the duration of the `onDeadLetter` callback.
|
|
297
|
+
* Default: ON.
|
|
184
298
|
*/
|
|
185
|
-
|
|
299
|
+
DeadLetter = "dead_letter",
|
|
186
300
|
/**
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* - `false` (default): disabled — NATS redelivers after `ack_wait` if not acked.
|
|
190
|
-
* - `true`: auto-extend at `ack_wait / 2` interval (calculated from consumer config).
|
|
191
|
-
* - `number`: explicit extension interval in milliseconds.
|
|
301
|
+
* `INTERNAL` span spanning one NATS connection session. Emits events on
|
|
302
|
+
* connect, reconnect, disconnect. Default: OFF.
|
|
192
303
|
*/
|
|
193
|
-
|
|
304
|
+
ConnectionLifecycle = "connection.lifecycle",
|
|
305
|
+
/**
|
|
306
|
+
* `INTERNAL` span per consumer recovery attempt following a transient
|
|
307
|
+
* failure (deleted consumer, missed heartbeat). Default: OFF.
|
|
308
|
+
*/
|
|
309
|
+
SelfHealing = "self_healing",
|
|
310
|
+
/** `INTERNAL` span per stream or consumer provisioning / update at startup. Default: OFF. */
|
|
311
|
+
Provisioning = "provisioning",
|
|
312
|
+
/** `INTERNAL` span covering a destructive migration flow. Default: OFF. */
|
|
313
|
+
Migration = "migration",
|
|
314
|
+
/** `INTERNAL` span covering the graceful shutdown sequence. Default: OFF. */
|
|
315
|
+
Shutdown = "shutdown"
|
|
194
316
|
}
|
|
195
317
|
/**
|
|
196
|
-
*
|
|
318
|
+
* The set of trace kinds enabled when `otel.traces` is not configured
|
|
319
|
+
* (or set to `'default'`). Covers message-flow operations; infrastructure
|
|
320
|
+
* spans are opt-in.
|
|
321
|
+
*/
|
|
322
|
+
declare const DEFAULT_TRACES: readonly JetstreamTrace[];
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Central event bus for transport lifecycle notifications.
|
|
197
326
|
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
327
|
+
* Two emission paths:
|
|
328
|
+
* - User hooks registered via `forRoot({ hooks })` — at most one per event.
|
|
329
|
+
* - Internal subscribers added via `subscribe()` — many per event, used by
|
|
330
|
+
* metrics and other built-in observers.
|
|
200
331
|
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
332
|
+
* Both fire on every `emit()` call. Subscriber failures are isolated and
|
|
333
|
+
* logged; they do not block other subscribers or the user hook.
|
|
203
334
|
*/
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
335
|
+
declare class EventBus {
|
|
336
|
+
private readonly hooks;
|
|
337
|
+
private readonly logger;
|
|
338
|
+
private readonly subscribers;
|
|
339
|
+
constructor(logger: Logger, hooks?: Partial<TransportHooks>);
|
|
207
340
|
/**
|
|
208
|
-
*
|
|
209
|
-
*
|
|
341
|
+
* Subscribe to a transport event. Used by built-in observers (e.g. metrics).
|
|
342
|
+
* Multiple subscribers per event are supported; each is called independently.
|
|
210
343
|
*/
|
|
211
|
-
|
|
344
|
+
subscribe<K extends keyof TransportHooks>(event: K, handler: TransportEventSubscriber<K>): void;
|
|
212
345
|
/**
|
|
213
|
-
*
|
|
346
|
+
* Emit a lifecycle event. Dispatches to all internal subscribers and the
|
|
347
|
+
* registered user hook (if any).
|
|
214
348
|
*/
|
|
215
|
-
|
|
349
|
+
emit<K extends keyof TransportHooks>(event: K, ...args: Parameters<TransportHooks[K]>): void;
|
|
216
350
|
/**
|
|
217
|
-
*
|
|
351
|
+
* Hot-path optimized emit for MessageRouted events.
|
|
352
|
+
* Avoids rest/spread overhead of the generic `emit()`.
|
|
218
353
|
*/
|
|
219
|
-
|
|
354
|
+
emitMessageRouted(subject: string, kind: MessageKind): void;
|
|
220
355
|
/**
|
|
221
|
-
*
|
|
222
|
-
*
|
|
356
|
+
* Check whether any listener (user hook or internal subscriber) is registered
|
|
357
|
+
* for the given event. Used by routing hot path to elide the emit call when
|
|
358
|
+
* no one is listening.
|
|
223
359
|
*/
|
|
224
|
-
|
|
360
|
+
hasHook(event: keyof TransportHooks): boolean;
|
|
361
|
+
private dispatch;
|
|
362
|
+
private callHook;
|
|
225
363
|
}
|
|
364
|
+
|
|
226
365
|
/**
|
|
227
|
-
*
|
|
366
|
+
* Manages the lifecycle of a single NATS connection shared across the application.
|
|
228
367
|
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
368
|
+
* Provides both Promise-based and Observable-based access to the connection:
|
|
369
|
+
* - `connect()` / `getConnection()` — async/await for one-time setup
|
|
370
|
+
* - `nc$` — cached observable (shareReplay) for reactive consumers
|
|
371
|
+
* - `status$` — live connection status event stream
|
|
232
372
|
*
|
|
233
|
-
*
|
|
373
|
+
* One instance per application, created by `JetstreamModule.forRoot()`.
|
|
234
374
|
*/
|
|
235
|
-
|
|
375
|
+
declare class ConnectionProvider {
|
|
376
|
+
private readonly options;
|
|
377
|
+
private readonly eventBus;
|
|
378
|
+
/** Cached observable that replays the established connection to new subscribers. */
|
|
379
|
+
readonly nc$: Observable<NatsConnection>;
|
|
380
|
+
/** Live stream of connection status events (no replay). */
|
|
381
|
+
readonly status$: Observable<Status>;
|
|
382
|
+
private readonly logger;
|
|
383
|
+
private connection;
|
|
384
|
+
private connectionPromise;
|
|
385
|
+
private jsClient;
|
|
386
|
+
private jsmInstance;
|
|
387
|
+
private jsmPromise;
|
|
388
|
+
private readonly otel;
|
|
389
|
+
private readonly otelServiceName;
|
|
390
|
+
private readonly otelEndpoint;
|
|
391
|
+
private lifecycleSpan;
|
|
392
|
+
constructor(options: JetstreamModuleOptions, eventBus: EventBus);
|
|
236
393
|
/**
|
|
237
|
-
*
|
|
238
|
-
*
|
|
394
|
+
* Establish NATS connection. Idempotent — returns cached connection on subsequent calls.
|
|
395
|
+
*
|
|
396
|
+
* @throws Error if connection is refused (fail fast).
|
|
239
397
|
*/
|
|
240
|
-
|
|
398
|
+
getConnection(): Promise<NatsConnection>;
|
|
241
399
|
/**
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
* @
|
|
400
|
+
* Get the JetStream manager. Cached after first call.
|
|
401
|
+
*
|
|
402
|
+
* @returns The JetStreamManager for stream/consumer administration.
|
|
245
403
|
*/
|
|
246
|
-
|
|
404
|
+
getJetStreamManager(): Promise<JetStreamManager>;
|
|
247
405
|
/**
|
|
248
|
-
*
|
|
406
|
+
* Get a cached JetStream client.
|
|
249
407
|
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
* When the pod stops (graceful or crash), entries expire after this duration.
|
|
408
|
+
* Invalidated automatically on reconnect and shutdown so consumers always
|
|
409
|
+
* operate against the live connection.
|
|
253
410
|
*
|
|
254
|
-
* @
|
|
411
|
+
* @returns The cached JetStreamClient.
|
|
412
|
+
* @throws Error if the connection has not been established yet.
|
|
255
413
|
*/
|
|
256
|
-
|
|
414
|
+
getJetStreamClient(): JetStreamClient;
|
|
415
|
+
/** Direct access to the raw NATS connection, or `null` if not yet connected. */
|
|
416
|
+
get unwrap(): NatsConnection | null;
|
|
417
|
+
/**
|
|
418
|
+
* Gracefully shut down the connection.
|
|
419
|
+
*
|
|
420
|
+
* Sequence: drain → wait for close. Falls back to force-close on error.
|
|
421
|
+
*/
|
|
422
|
+
shutdown(): Promise<void>;
|
|
423
|
+
private initJetStreamManager;
|
|
424
|
+
/** Internal: establish the physical connection with reconnect monitoring. */
|
|
425
|
+
private establish;
|
|
426
|
+
/** Handle a single `nc.status()` event, emitting hooks and span events. */
|
|
427
|
+
private handleStatusEvent;
|
|
428
|
+
/** Subscribe to connection status events and emit hooks. */
|
|
429
|
+
private monitorStatus;
|
|
257
430
|
}
|
|
431
|
+
|
|
258
432
|
/**
|
|
259
|
-
*
|
|
433
|
+
* NestJS ClientProxy implementation for the JetStream transport.
|
|
260
434
|
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
435
|
+
* Supports two operational modes:
|
|
436
|
+
* - **Core mode** (default): Uses `nc.request()` for RPC, `nc.publish()` for events.
|
|
437
|
+
* - **JetStream mode**: Uses `js.publish()` for RPC commands + inbox for responses.
|
|
438
|
+
*
|
|
439
|
+
* Events always go through JetStream publish for guaranteed delivery.
|
|
440
|
+
* The mode only affects RPC (request/reply) behavior.
|
|
441
|
+
*
|
|
442
|
+
* Clients are lightweight — they share the NATS connection from `forRoot()`.
|
|
263
443
|
*/
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
444
|
+
declare class JetstreamClient extends ClientProxy {
|
|
445
|
+
private readonly rootOptions;
|
|
446
|
+
private readonly connection;
|
|
447
|
+
private readonly codec;
|
|
448
|
+
private readonly eventBus;
|
|
449
|
+
private readonly logger;
|
|
450
|
+
/** Target service name this client sends messages to. */
|
|
451
|
+
private readonly targetName;
|
|
452
|
+
/** Pre-cached caller name derived from rootOptions.name, computed once in constructor. */
|
|
453
|
+
private readonly callerName;
|
|
269
454
|
/**
|
|
270
|
-
*
|
|
271
|
-
*
|
|
455
|
+
* Subject prefixes of the form `{serviceName}__microservice.{kind}.` — one
|
|
456
|
+
* per stream kind this client may publish to. Built once in the constructor
|
|
457
|
+
* so producing a full subject is a single string concat with the user pattern.
|
|
272
458
|
*/
|
|
273
|
-
|
|
459
|
+
private readonly eventSubjectPrefix;
|
|
460
|
+
private readonly commandSubjectPrefix;
|
|
461
|
+
private readonly orderedSubjectPrefix;
|
|
274
462
|
/**
|
|
275
|
-
* RPC
|
|
276
|
-
*
|
|
463
|
+
* RPC configuration snapshots. The values are derived from rootOptions at
|
|
464
|
+
* construction time so the publish hot path never has to re-run
|
|
465
|
+
* isCoreRpcMode / getRpcTimeout on every call.
|
|
277
466
|
*/
|
|
278
|
-
|
|
467
|
+
private readonly isCoreMode;
|
|
468
|
+
private readonly defaultRpcTimeout;
|
|
469
|
+
/** Resolved OpenTelemetry configuration, computed once in the constructor. */
|
|
470
|
+
private readonly otel;
|
|
471
|
+
/** Server endpoint parts used for `server.address` / `server.port` span attributes. */
|
|
472
|
+
private readonly serverEndpoint;
|
|
473
|
+
/** Shared inbox for JetStream-mode RPC responses. */
|
|
474
|
+
private inbox;
|
|
475
|
+
private inboxSubscription;
|
|
476
|
+
/** Pending JetStream-mode RPC callbacks, keyed by correlation ID. */
|
|
477
|
+
private readonly pendingMessages;
|
|
478
|
+
/** Pending JetStream-mode RPC timeouts, keyed by correlation ID. */
|
|
479
|
+
private readonly pendingTimeouts;
|
|
480
|
+
/** Subscription to connection status events for disconnect handling. */
|
|
481
|
+
private statusSubscription;
|
|
279
482
|
/**
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
483
|
+
* Cached readiness flag. Once `connect()` has wired the inbox and status
|
|
484
|
+
* subscription, subsequent publishes skip the `await connect()` microtask
|
|
485
|
+
* and reach for the underlying connection synchronously instead.
|
|
283
486
|
*/
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
events?: StreamConsumerOverrides;
|
|
287
|
-
/** Broadcast event stream/consumer overrides. */
|
|
288
|
-
broadcast?: StreamConsumerOverrides;
|
|
487
|
+
private readyForPublish;
|
|
488
|
+
constructor(rootOptions: JetstreamModuleOptions, targetServiceName: string, connection: ConnectionProvider, codec: Codec, eventBus: EventBus);
|
|
289
489
|
/**
|
|
290
|
-
*
|
|
490
|
+
* Establish connection. Called automatically by NestJS on first use.
|
|
291
491
|
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
492
|
+
* Sets up the JetStream RPC inbox (if in JetStream mode) and subscribes
|
|
493
|
+
* to connection status events for fail-fast disconnect handling.
|
|
294
494
|
*
|
|
295
|
-
* @
|
|
296
|
-
*/
|
|
297
|
-
ordered?: OrderedEventOverrides;
|
|
298
|
-
/**
|
|
299
|
-
* Transport lifecycle hook handlers.
|
|
300
|
-
* Unset hooks are silently ignored — no default logging.
|
|
495
|
+
* @returns The underlying NATS connection.
|
|
301
496
|
*/
|
|
302
|
-
|
|
497
|
+
connect(): Promise<NatsConnection>;
|
|
498
|
+
/** Clean up resources: reject pending RPCs, unsubscribe from status events. */
|
|
499
|
+
close(): Promise<void>;
|
|
303
500
|
/**
|
|
304
|
-
*
|
|
305
|
-
* Called before msg.term(). If it throws, the message is nak'd for retry.
|
|
306
|
-
*
|
|
307
|
-
* Use this to persist dead letters to an external store (DB, S3, another queue).
|
|
308
|
-
* The NATS connection is available via `JETSTREAM_CONNECTION` token in forRootAsync.
|
|
501
|
+
* Direct access to the raw NATS connection.
|
|
309
502
|
*
|
|
310
|
-
* @
|
|
311
|
-
* ```typescript
|
|
312
|
-
* JetstreamModule.forRootAsync({
|
|
313
|
-
* name: 'my-service',
|
|
314
|
-
* imports: [DlqModule],
|
|
315
|
-
* inject: [DlqService, JETSTREAM_CONNECTION],
|
|
316
|
-
* useFactory: (dlqService, connection) => ({
|
|
317
|
-
* servers: ['nats://localhost:4222'],
|
|
318
|
-
* onDeadLetter: async (info) => {
|
|
319
|
-
* await dlqService.persist(info);
|
|
320
|
-
* },
|
|
321
|
-
* }),
|
|
322
|
-
* })
|
|
323
|
-
* ```
|
|
503
|
+
* @throws Error if not connected.
|
|
324
504
|
*/
|
|
325
|
-
|
|
505
|
+
unwrap<T = NatsConnection>(): T;
|
|
326
506
|
/**
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
* servers: ['nats://localhost:4222'],
|
|
334
|
-
* dlq: {
|
|
335
|
-
* stream: {
|
|
336
|
-
* max_age: toNanos(30, 'days'),
|
|
337
|
-
* },
|
|
338
|
-
* },
|
|
339
|
-
* })
|
|
340
|
-
* ```
|
|
507
|
+
* Publish a fire-and-forget event to JetStream.
|
|
508
|
+
*
|
|
509
|
+
* Events are published to either the workqueue stream or broadcast stream
|
|
510
|
+
* depending on the subject prefix. When a schedule is present the message
|
|
511
|
+
* is published to a `_sch` subject within the same stream, with the target
|
|
512
|
+
* set to the original event subject.
|
|
341
513
|
*/
|
|
342
|
-
|
|
343
|
-
stream?: StreamConfigOverrides;
|
|
344
|
-
};
|
|
514
|
+
protected dispatchEvent<T = unknown>(packet: ReadPacket): Promise<T>;
|
|
345
515
|
/**
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
516
|
+
* Publish an RPC command and register callback for response.
|
|
517
|
+
*
|
|
518
|
+
* Core mode: uses nc.request() with timeout.
|
|
519
|
+
* JetStream mode: publishes to stream + waits for inbox response.
|
|
349
520
|
*/
|
|
350
|
-
|
|
521
|
+
protected publish(packet: ReadPacket, callback: (p: WritePacket) => void): () => void;
|
|
522
|
+
/** Core mode: nc.request() with timeout. */
|
|
523
|
+
private publishCoreRpc;
|
|
524
|
+
/** JetStream mode: publish to stream + wait for inbox response. */
|
|
525
|
+
private publishJetStreamRpc;
|
|
526
|
+
private reportPublished;
|
|
527
|
+
private reportRpcCompleted;
|
|
528
|
+
/** Fail-fast all pending JetStream RPC callbacks on connection loss. */
|
|
529
|
+
private handleDisconnect;
|
|
530
|
+
/** Reject all pending RPC callbacks, clear timeouts, and tear down inbox. */
|
|
531
|
+
private rejectPendingRpcs;
|
|
532
|
+
/** Setup shared inbox subscription for JetStream RPC responses. */
|
|
533
|
+
private setupInbox;
|
|
534
|
+
/** Route an inbox reply to the matching pending callback. */
|
|
535
|
+
private routeInboxReply;
|
|
351
536
|
/**
|
|
352
|
-
*
|
|
353
|
-
*
|
|
354
|
-
* When `true`, the transport will recreate streams (via blue-green sourcing)
|
|
355
|
-
* if immutable properties like `storage` differ from the running stream.
|
|
356
|
-
* Messages are preserved during migration.
|
|
357
|
-
*
|
|
358
|
-
* `retention` is NOT migratable — it is controlled by the transport
|
|
359
|
-
* (Workqueue for events, Limits for broadcast/ordered) and a mismatch
|
|
360
|
-
* is always treated as an error regardless of this flag.
|
|
361
|
-
*
|
|
362
|
-
* When `false` (default), immutable conflicts are logged as warnings and
|
|
363
|
-
* the stream continues with its existing configuration.
|
|
537
|
+
* Resolve a user pattern to a fully-qualified NATS subject, dispatching
|
|
538
|
+
* between the event, broadcast, and ordered prefixes.
|
|
364
539
|
*
|
|
365
|
-
*
|
|
540
|
+
* The leading-char check short-circuits the `startsWith` comparisons for
|
|
541
|
+
* patterns that cannot possibly carry a broadcast/ordered marker, which is
|
|
542
|
+
* the overwhelmingly common case.
|
|
366
543
|
*/
|
|
367
|
-
|
|
544
|
+
private buildEventSubject;
|
|
545
|
+
/** Build NATS headers merging custom headers with transport headers. */
|
|
546
|
+
private buildHeaders;
|
|
547
|
+
/** Extract data, headers, timeout, and schedule from raw packet data or JetstreamRecord. */
|
|
548
|
+
private extractRecordData;
|
|
368
549
|
/**
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
* When any handler has `meta` in its `@EventPattern` / `@MessagePattern` extras,
|
|
372
|
-
* the transport writes metadata to a NATS KV bucket at startup.
|
|
373
|
-
* External services (API gateways, dashboards, CLI tools) can read or watch
|
|
374
|
-
* the bucket for dynamic service discovery.
|
|
550
|
+
* Build a schedule-holder subject for NATS message scheduling.
|
|
375
551
|
*
|
|
376
|
-
*
|
|
377
|
-
*
|
|
552
|
+
* The schedule-holder subject resides in the same stream as the target but
|
|
553
|
+
* uses a separate `_sch` namespace that is NOT matched by any consumer filter.
|
|
554
|
+
* NATS holds the message and publishes it to the target subject after the delay.
|
|
378
555
|
*
|
|
379
|
-
*
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Raw NATS ConnectionOptions pass-through for advanced connection config.
|
|
384
|
-
* Allows setting tls, auth, reconnect behavior, maxReconnectAttempts, etc.
|
|
385
|
-
* Merged with `name` and `servers` — those take precedence.
|
|
386
|
-
*/
|
|
387
|
-
connectionOptions?: Partial<ConnectionOptions>;
|
|
388
|
-
}
|
|
389
|
-
/** Options for `JetstreamModule.forFeature()`. */
|
|
390
|
-
interface JetstreamFeatureOptions {
|
|
391
|
-
/** Target service name for subject construction. */
|
|
392
|
-
name: string;
|
|
393
|
-
/**
|
|
394
|
-
* Override the global codec for this client.
|
|
395
|
-
* Falls back to the root codec from `forRoot()` when omitted.
|
|
556
|
+
* Examples:
|
|
557
|
+
* - `{svc}__microservice.ev.order.reminder` → `{svc}__microservice._sch.order.reminder`
|
|
558
|
+
* - `broadcast.config.updated` → `broadcast._sch.config.updated`
|
|
396
559
|
*/
|
|
397
|
-
|
|
560
|
+
private buildScheduleSubject;
|
|
398
561
|
}
|
|
399
|
-
/**
|
|
400
|
-
* Async configuration for `JetstreamModule.forRootAsync()`.
|
|
401
|
-
*
|
|
402
|
-
* Supports three patterns: `useFactory`, `useExisting`, `useClass`.
|
|
403
|
-
*/
|
|
404
|
-
type JetstreamModuleAsyncOptions = {
|
|
405
|
-
/** Service name — required upfront for DI token generation. */
|
|
406
|
-
name: string;
|
|
407
|
-
/** Additional module imports (e.g., ConfigModule). */
|
|
408
|
-
imports?: ModuleMetadata['imports'];
|
|
409
|
-
} & ({
|
|
410
|
-
useFactory(...args: unknown[]): Promise<Omit<JetstreamModuleOptions, 'name'>> | Omit<JetstreamModuleOptions, 'name'>;
|
|
411
|
-
inject?: FactoryProvider['inject'];
|
|
412
|
-
useExisting?: never;
|
|
413
|
-
useClass?: never;
|
|
414
|
-
} | {
|
|
415
|
-
useExisting: Type<Omit<JetstreamModuleOptions, 'name'>>;
|
|
416
|
-
useFactory?: never;
|
|
417
|
-
inject?: never;
|
|
418
|
-
useClass?: never;
|
|
419
|
-
} | {
|
|
420
|
-
useClass: Type<Omit<JetstreamModuleOptions, 'name'>>;
|
|
421
|
-
useFactory?: never;
|
|
422
|
-
inject?: never;
|
|
423
|
-
useExisting?: never;
|
|
424
|
-
});
|
|
425
562
|
|
|
426
563
|
/**
|
|
427
|
-
*
|
|
564
|
+
* Immutable message record for JetStream transport.
|
|
428
565
|
*
|
|
429
|
-
*
|
|
430
|
-
*
|
|
431
|
-
*
|
|
432
|
-
*
|
|
566
|
+
* Compatible with NestJS record builder pattern (like RmqRecord, NatsRecord).
|
|
567
|
+
* Pass as the second argument to `client.send()` or `client.emit()`.
|
|
568
|
+
*
|
|
569
|
+
* @example
|
|
570
|
+
* ```typescript
|
|
571
|
+
* const record = new JetstreamRecordBuilder({ id: 1 })
|
|
572
|
+
* .setHeader('x-tenant', 'acme')
|
|
573
|
+
* .setTimeout(5000)
|
|
574
|
+
* .build();
|
|
575
|
+
*
|
|
576
|
+
* client.send('get.user', record);
|
|
577
|
+
* ```
|
|
433
578
|
*/
|
|
434
|
-
declare
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
579
|
+
declare class JetstreamRecord<TData = unknown> {
|
|
580
|
+
/** Message payload. */
|
|
581
|
+
readonly data: TData;
|
|
582
|
+
/** Custom headers set via {@link JetstreamRecordBuilder.setHeader}. */
|
|
583
|
+
readonly headers: ReadonlyMap<string, string>;
|
|
584
|
+
/** Per-request RPC timeout override in ms. */
|
|
585
|
+
readonly timeout?: number | undefined;
|
|
586
|
+
/** Custom message ID for JetStream deduplication. */
|
|
587
|
+
readonly messageId?: string | undefined;
|
|
588
|
+
/** Schedule options for delayed delivery. */
|
|
589
|
+
readonly schedule?: ScheduleRecordOptions | undefined;
|
|
590
|
+
/** Per-message TTL as Go duration string (e.g. "30s", "5m"). */
|
|
591
|
+
readonly ttl?: string | undefined;
|
|
592
|
+
constructor(
|
|
593
|
+
/** Message payload. */
|
|
594
|
+
data: TData,
|
|
595
|
+
/** Custom headers set via {@link JetstreamRecordBuilder.setHeader}. */
|
|
596
|
+
headers: ReadonlyMap<string, string>,
|
|
597
|
+
/** Per-request RPC timeout override in ms. */
|
|
598
|
+
timeout?: number | undefined,
|
|
599
|
+
/** Custom message ID for JetStream deduplication. */
|
|
600
|
+
messageId?: string | undefined,
|
|
601
|
+
/** Schedule options for delayed delivery. */
|
|
602
|
+
schedule?: ScheduleRecordOptions | undefined,
|
|
603
|
+
/** Per-message TTL as Go duration string (e.g. "30s", "5m"). */
|
|
604
|
+
ttl?: string | undefined);
|
|
439
605
|
}
|
|
440
606
|
/**
|
|
441
|
-
*
|
|
607
|
+
* Fluent builder for constructing JetstreamRecord instances.
|
|
442
608
|
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
609
|
+
* Protected headers (`correlation-id`, `reply-to`, `error`) cannot be
|
|
610
|
+
* set by the user — attempting to do so throws an error at build time.
|
|
445
611
|
*/
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
/** @internal Grouped pattern lists by stream kind, used for stream/consumer setup. */
|
|
455
|
-
interface PatternsByKind {
|
|
456
|
-
/** Workqueue event patterns. */
|
|
457
|
-
events: string[];
|
|
458
|
-
/** RPC command patterns. */
|
|
459
|
-
commands: string[];
|
|
460
|
-
/** Broadcast event patterns. */
|
|
461
|
-
broadcasts: string[];
|
|
462
|
-
/** Ordered event patterns (strict sequential delivery). */
|
|
463
|
-
ordered: string[];
|
|
464
|
-
}
|
|
465
|
-
/** Options for configuring event/broadcast processing behavior. */
|
|
466
|
-
interface EventProcessingConfig {
|
|
467
|
-
events?: {
|
|
468
|
-
concurrency?: number;
|
|
469
|
-
ackExtension?: boolean | number;
|
|
470
|
-
};
|
|
471
|
-
broadcast?: {
|
|
472
|
-
concurrency?: number;
|
|
473
|
-
ackExtension?: boolean | number;
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
/** Options for dead letter queue handling. */
|
|
477
|
-
interface DeadLetterConfig {
|
|
612
|
+
declare class JetstreamRecordBuilder<TData = unknown> {
|
|
613
|
+
private data;
|
|
614
|
+
private readonly headers;
|
|
615
|
+
private timeout;
|
|
616
|
+
private messageId;
|
|
617
|
+
private scheduleOptions;
|
|
618
|
+
private ttlDuration;
|
|
619
|
+
constructor(data?: TData);
|
|
478
620
|
/**
|
|
479
|
-
*
|
|
480
|
-
*
|
|
621
|
+
* Set the message payload.
|
|
622
|
+
*
|
|
623
|
+
* @param data - Payload to serialize via the configured {@link Codec}.
|
|
481
624
|
*/
|
|
482
|
-
|
|
483
|
-
/** Async callback invoked when a message exhausts all deliveries. */
|
|
484
|
-
onDeadLetter(info: DeadLetterInfo): Promise<void>;
|
|
485
|
-
}
|
|
486
|
-
/** Options for configuring RPC processing behavior. */
|
|
487
|
-
interface RpcRouterOptions {
|
|
488
|
-
timeout?: number;
|
|
489
|
-
concurrency?: number;
|
|
490
|
-
ackExtension?: boolean | number;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Central event bus for transport lifecycle notifications.
|
|
495
|
-
*
|
|
496
|
-
* Dispatches events to user-provided hooks. Events without a
|
|
497
|
-
* registered hook are silently ignored — no default logging.
|
|
498
|
-
*
|
|
499
|
-
* @example
|
|
500
|
-
* ```typescript
|
|
501
|
-
* const bus = new EventBus(logger, {
|
|
502
|
-
* [TransportEvent.Error]: (err) => sentry.captureException(err),
|
|
503
|
-
* });
|
|
504
|
-
*
|
|
505
|
-
* bus.emit(TransportEvent.Error, new Error('timeout'), 'rpc-router');
|
|
506
|
-
* // → calls sentry
|
|
507
|
-
*
|
|
508
|
-
* bus.emit(TransportEvent.Connect, 'nats://localhost:4222');
|
|
509
|
-
* // → no-op (no hook registered)
|
|
510
|
-
* ```
|
|
511
|
-
*/
|
|
512
|
-
declare class EventBus {
|
|
513
|
-
private readonly hooks;
|
|
514
|
-
private readonly logger;
|
|
515
|
-
constructor(logger: Logger, hooks?: Partial<TransportHooks>);
|
|
625
|
+
setData(data: TData): this;
|
|
516
626
|
/**
|
|
517
|
-
*
|
|
627
|
+
* Set a single custom header.
|
|
518
628
|
*
|
|
519
|
-
* @param
|
|
520
|
-
* @param
|
|
629
|
+
* @param key - Header name (e.g. `'x-tenant'`).
|
|
630
|
+
* @param value - Header value.
|
|
631
|
+
* @throws Error if the header name is reserved by the transport.
|
|
521
632
|
*/
|
|
522
|
-
|
|
633
|
+
setHeader(key: string, value: string): this;
|
|
523
634
|
/**
|
|
524
|
-
*
|
|
525
|
-
*
|
|
635
|
+
* Set multiple custom headers at once.
|
|
636
|
+
*
|
|
637
|
+
* @param headers - Key-value pairs to set as headers.
|
|
638
|
+
* @throws Error if any header name is reserved by the transport.
|
|
526
639
|
*/
|
|
527
|
-
|
|
640
|
+
setHeaders(headers: Record<string, string>): this;
|
|
528
641
|
/**
|
|
529
|
-
*
|
|
642
|
+
* Set a custom message ID for JetStream deduplication.
|
|
530
643
|
*
|
|
531
|
-
*
|
|
532
|
-
*
|
|
644
|
+
* NATS JetStream uses this ID to detect duplicate publishes within the
|
|
645
|
+
* stream's `duplicate_window`. If two messages with the same ID arrive
|
|
646
|
+
* within the window, the second is silently dropped.
|
|
647
|
+
*
|
|
648
|
+
* When not set, a random UUID is generated automatically.
|
|
649
|
+
*
|
|
650
|
+
* @param id - Unique message identifier (e.g. order ID, idempotency key).
|
|
651
|
+
*
|
|
652
|
+
* @example
|
|
653
|
+
* ```typescript
|
|
654
|
+
* new JetstreamRecordBuilder(data)
|
|
655
|
+
* .setMessageId(`order-${order.id}`)
|
|
656
|
+
* .build();
|
|
657
|
+
* ```
|
|
533
658
|
*/
|
|
534
|
-
|
|
535
|
-
private callHook;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* Manages the lifecycle of a single NATS connection shared across the application.
|
|
540
|
-
*
|
|
541
|
-
* Provides both Promise-based and Observable-based access to the connection:
|
|
542
|
-
* - `connect()` / `getConnection()` — async/await for one-time setup
|
|
543
|
-
* - `nc$` — cached observable (shareReplay) for reactive consumers
|
|
544
|
-
* - `status$` — live connection status event stream
|
|
545
|
-
*
|
|
546
|
-
* One instance per application, created by `JetstreamModule.forRoot()`.
|
|
547
|
-
*/
|
|
548
|
-
declare class ConnectionProvider {
|
|
549
|
-
private readonly options;
|
|
550
|
-
private readonly eventBus;
|
|
551
|
-
/** Cached observable that replays the established connection to new subscribers. */
|
|
552
|
-
readonly nc$: Observable<NatsConnection>;
|
|
553
|
-
/** Live stream of connection status events (no replay). */
|
|
554
|
-
readonly status$: Observable<Status>;
|
|
555
|
-
private readonly logger;
|
|
556
|
-
private connection;
|
|
557
|
-
private connectionPromise;
|
|
558
|
-
private jsClient;
|
|
559
|
-
private jsmInstance;
|
|
560
|
-
private jsmPromise;
|
|
561
|
-
constructor(options: JetstreamModuleOptions, eventBus: EventBus);
|
|
659
|
+
setMessageId(id: string): this;
|
|
562
660
|
/**
|
|
563
|
-
*
|
|
661
|
+
* Set per-request RPC timeout.
|
|
564
662
|
*
|
|
565
|
-
* @
|
|
663
|
+
* @param ms - Timeout in milliseconds. Overrides the global RPC timeout for this request only.
|
|
566
664
|
*/
|
|
567
|
-
|
|
665
|
+
setTimeout(ms: number): this;
|
|
568
666
|
/**
|
|
569
|
-
*
|
|
667
|
+
* Schedule one-shot delayed delivery.
|
|
570
668
|
*
|
|
571
|
-
*
|
|
669
|
+
* The message is held by NATS and delivered to the event consumer
|
|
670
|
+
* at the specified time. Requires NATS >= 2.12 and `allow_msg_schedules: true`
|
|
671
|
+
* on the event stream (via `events: { stream: { allow_msg_schedules: true } }`).
|
|
672
|
+
*
|
|
673
|
+
* Only meaningful for events (`client.emit()`). If used with RPC
|
|
674
|
+
* (`client.send()`), a warning is logged and the schedule is ignored.
|
|
675
|
+
*
|
|
676
|
+
* @param date - Delivery time. Must be in the future.
|
|
677
|
+
* @throws Error if the date is not in the future.
|
|
572
678
|
*/
|
|
573
|
-
|
|
679
|
+
scheduleAt(date: Date): this;
|
|
574
680
|
/**
|
|
575
|
-
*
|
|
681
|
+
* Set per-message TTL (time-to-live).
|
|
576
682
|
*
|
|
577
|
-
*
|
|
578
|
-
*
|
|
683
|
+
* The message expires individually after the specified duration,
|
|
684
|
+
* independent of the stream's `max_age`. Requires NATS >= 2.11 and
|
|
685
|
+
* `allow_msg_ttl: true` on the stream.
|
|
579
686
|
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
687
|
+
* Only meaningful for events (`client.emit()`). If used with RPC
|
|
688
|
+
* (`client.send()`), a warning is logged and the TTL is ignored.
|
|
689
|
+
*
|
|
690
|
+
* @param nanos - TTL in nanoseconds. Use {@link toNanos} for human-readable values.
|
|
691
|
+
*
|
|
692
|
+
* @example
|
|
693
|
+
* ```typescript
|
|
694
|
+
* import { toNanos } from '@horizon-republic/nestjs-jetstream';
|
|
695
|
+
*
|
|
696
|
+
* new JetstreamRecordBuilder(payload).ttl(toNanos(30, 'minutes')).build();
|
|
697
|
+
* new JetstreamRecordBuilder(payload).ttl(toNanos(24, 'hours')).build();
|
|
698
|
+
* ```
|
|
582
699
|
*/
|
|
583
|
-
|
|
584
|
-
/** Direct access to the raw NATS connection, or `null` if not yet connected. */
|
|
585
|
-
get unwrap(): NatsConnection | null;
|
|
700
|
+
ttl(nanos: number): this;
|
|
586
701
|
/**
|
|
587
|
-
*
|
|
702
|
+
* Build the immutable {@link JetstreamRecord}.
|
|
588
703
|
*
|
|
589
|
-
*
|
|
704
|
+
* @returns A frozen record ready to pass to `client.send()` or `client.emit()`.
|
|
590
705
|
*/
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
706
|
+
build(): JetstreamRecord<TData>;
|
|
707
|
+
/**
|
|
708
|
+
* Validate that a header key is not reserved. NATS treats header names
|
|
709
|
+
* case-insensitively, so the check is against the lowercase form to keep
|
|
710
|
+
* `'X-Correlation-ID'`, `'x-correlation-id'`, and any other casing in
|
|
711
|
+
* lockstep. `RESERVED_HEADERS` is defined as an all-lowercase set.
|
|
712
|
+
*/
|
|
713
|
+
private validateHeaderKey;
|
|
597
714
|
}
|
|
598
715
|
|
|
716
|
+
/** Tag attached to every outgoing publish span so consumers can distinguish event / RPC / broadcast / ordered flows. */
|
|
717
|
+
declare enum PublishKind {
|
|
718
|
+
Event = "event",
|
|
719
|
+
RpcRequest = "rpc.request",
|
|
720
|
+
Broadcast = "broadcast",
|
|
721
|
+
Ordered = "ordered"
|
|
722
|
+
}
|
|
723
|
+
/** Tag attached to every incoming consume span. `Rpc` covers both Core and JetStream RPC handlers. */
|
|
724
|
+
declare enum ConsumeKind {
|
|
725
|
+
Event = "event",
|
|
726
|
+
Rpc = "rpc",
|
|
727
|
+
Broadcast = "broadcast",
|
|
728
|
+
Ordered = "ordered"
|
|
729
|
+
}
|
|
599
730
|
/**
|
|
600
|
-
*
|
|
601
|
-
*
|
|
602
|
-
* Handles subject normalization and categorization:
|
|
603
|
-
* - Detects broadcast handlers via `extras.broadcast` metadata
|
|
604
|
-
* - Normalizes full NATS subjects back to user-facing patterns
|
|
605
|
-
* - Provides lists of patterns by category for stream/consumer setup
|
|
731
|
+
* Handler metadata for the dispatched message. `pattern` is always set;
|
|
732
|
+
* `className` / `methodName` come from NestJS reflection when available.
|
|
606
733
|
*/
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
734
|
+
interface HandlerMetadata {
|
|
735
|
+
readonly pattern: string;
|
|
736
|
+
readonly className?: string;
|
|
737
|
+
readonly methodName?: string;
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Host / port pair surfaced as `server.address` / `server.port` span
|
|
741
|
+
* attributes. `port` is optional — OTel semconv makes it conditional on
|
|
742
|
+
* being different from the protocol default, and we'd rather emit nothing
|
|
743
|
+
* than invent a number the user never configured.
|
|
744
|
+
*/
|
|
745
|
+
interface ServerEndpoint {
|
|
746
|
+
readonly host: string;
|
|
747
|
+
readonly port?: number;
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Minimum message shape the consume span helper requires. Both `JsMsg`
|
|
751
|
+
* (JetStream) and Core NATS `Msg` satisfy it structurally; JetStream-only
|
|
752
|
+
* delivery metadata (`info`) is read separately and omitted on the Core
|
|
753
|
+
* RPC path.
|
|
754
|
+
*/
|
|
755
|
+
interface ConsumeSourceMsg {
|
|
756
|
+
readonly subject: string;
|
|
757
|
+
readonly data: Uint8Array;
|
|
758
|
+
readonly headers?: MsgHdrs;
|
|
759
|
+
}
|
|
760
|
+
/** Context passed to {@link OtelOptions.publishHook} for an outgoing publish. */
|
|
761
|
+
interface JetstreamPublishContext {
|
|
762
|
+
/** Fully-resolved subject the message is being published to. */
|
|
763
|
+
readonly subject: string;
|
|
764
|
+
/** The record being published (payload, headers, builder-supplied options). */
|
|
765
|
+
readonly record: JetstreamRecord;
|
|
766
|
+
/** Classification of the outgoing operation. */
|
|
767
|
+
readonly kind: PublishKind;
|
|
768
|
+
}
|
|
769
|
+
/** Context passed to {@link OtelOptions.consumeHook} for an incoming dispatch. */
|
|
770
|
+
interface JetstreamConsumeContext {
|
|
771
|
+
/** Fully-resolved subject of the incoming message. */
|
|
772
|
+
readonly subject: string;
|
|
773
|
+
/**
|
|
774
|
+
* The incoming NATS message. JetStream messages (events, broadcast,
|
|
775
|
+
* ordered, JetStream RPC) satisfy `JsMsg`; Core RPC messages satisfy
|
|
776
|
+
* the looser {@link ConsumeSourceMsg} shape only. Narrow on `kind` to
|
|
777
|
+
* decide which fields are safe to access.
|
|
778
|
+
*/
|
|
779
|
+
readonly msg: ConsumeSourceMsg;
|
|
780
|
+
/** Handler metadata; see {@link HandlerMetadata}. */
|
|
781
|
+
readonly handlerMetadata: HandlerMetadata;
|
|
782
|
+
/** Classification of the incoming operation. */
|
|
783
|
+
readonly kind: ConsumeKind;
|
|
784
|
+
}
|
|
785
|
+
/** Context passed to {@link OtelOptions.responseHook} once an outcome is known, just before the span ends. */
|
|
786
|
+
interface JetstreamResponseContext {
|
|
787
|
+
/** Subject the operation targeted. */
|
|
788
|
+
readonly subject: string;
|
|
789
|
+
/** Wall-clock duration of the operation in milliseconds. */
|
|
790
|
+
readonly durationMs: number;
|
|
791
|
+
/** Decoded reply payload for RPC client spans when available. */
|
|
792
|
+
readonly reply?: unknown;
|
|
793
|
+
/** Error instance if the operation failed. */
|
|
794
|
+
readonly error?: Error;
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Classification outcome for a thrown error. Affects span status and
|
|
798
|
+
* attributes only — reply envelopes and internal logging are unchanged.
|
|
799
|
+
*/
|
|
800
|
+
type ErrorClassification = 'expected' | 'unexpected';
|
|
801
|
+
/** Object form of {@link OtelOptions.captureBody}. */
|
|
802
|
+
interface CaptureBodyOptions {
|
|
618
803
|
/**
|
|
619
|
-
*
|
|
804
|
+
* Maximum number of bytes to capture. Payloads longer than this are
|
|
805
|
+
* truncated; a `messaging.nats.message.body.truncated` attribute is
|
|
806
|
+
* added to indicate truncation occurred.
|
|
620
807
|
*
|
|
621
|
-
* @
|
|
808
|
+
* @default 4096
|
|
622
809
|
*/
|
|
623
|
-
|
|
624
|
-
/** Find handler for a full NATS subject. */
|
|
625
|
-
getHandler(subject: string): MessageHandler | null;
|
|
626
|
-
/** Get all registered broadcast patterns (for consumer filter_subject setup). */
|
|
627
|
-
getBroadcastPatterns(): string[];
|
|
628
|
-
hasBroadcastHandlers(): boolean;
|
|
629
|
-
hasRpcHandlers(): boolean;
|
|
630
|
-
hasEventHandlers(): boolean;
|
|
631
|
-
hasOrderedHandlers(): boolean;
|
|
632
|
-
/** Get fully-qualified NATS subjects for ordered handlers. */
|
|
633
|
-
getOrderedSubjects(): string[];
|
|
634
|
-
/** Check if any registered handler has metadata. */
|
|
635
|
-
hasMetadata(): boolean;
|
|
810
|
+
readonly maxBytes?: number;
|
|
636
811
|
/**
|
|
637
|
-
*
|
|
638
|
-
*
|
|
639
|
-
*
|
|
640
|
-
* Key format: `{serviceName}.{kind}.{pattern}`.
|
|
812
|
+
* Only capture body for subjects matching these glob patterns. Each
|
|
813
|
+
* pattern supports `*` wildcards and an optional leading `!` for
|
|
814
|
+
* exclusion. When omitted, body capture applies to all subjects.
|
|
641
815
|
*/
|
|
642
|
-
|
|
643
|
-
/** Get patterns grouped by kind (cached after registration). */
|
|
644
|
-
getPatternsByKind(): PatternsByKind;
|
|
645
|
-
/** Normalize a full NATS subject back to the user-facing pattern. */
|
|
646
|
-
normalizeSubject(subject: string): string;
|
|
647
|
-
private buildPatternsByKind;
|
|
648
|
-
private resolveStreamKind;
|
|
649
|
-
private logSummary;
|
|
816
|
+
readonly subjectAllowlist?: readonly string[];
|
|
650
817
|
}
|
|
651
|
-
|
|
652
818
|
/**
|
|
653
|
-
*
|
|
654
|
-
*
|
|
655
|
-
*
|
|
656
|
-
* Each request is processed and replied to directly via `msg.respond()`.
|
|
657
|
-
*
|
|
658
|
-
* This is the default RPC mode — lowest latency, no persistence overhead.
|
|
819
|
+
* OpenTelemetry configuration for `JetstreamModule.forRoot({ otel: … })`.
|
|
820
|
+
* All fields are optional; when the host app has not registered an OTel
|
|
821
|
+
* SDK, every call made by the library is a no-op regardless of config.
|
|
659
822
|
*/
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
823
|
+
interface OtelOptions {
|
|
824
|
+
/**
|
|
825
|
+
* Global kill switch. When `false`, no spans are emitted, no propagation
|
|
826
|
+
* is attempted, and no hooks fire.
|
|
827
|
+
*
|
|
828
|
+
* @default true
|
|
829
|
+
*/
|
|
830
|
+
readonly enabled?: boolean;
|
|
831
|
+
/**
|
|
832
|
+
* Which trace kinds to emit.
|
|
833
|
+
*
|
|
834
|
+
* - `'default'` — publish, consume, RPC client round-trip, dead letter
|
|
835
|
+
* - `'all'` — every trace kind defined in {@link JetstreamTrace}
|
|
836
|
+
* - `'none'` — emit no spans at all (useful with `enabled: true` for
|
|
837
|
+
* pure trace-context propagation without the span overhead)
|
|
838
|
+
* - `JetstreamTrace[]` — explicit selection
|
|
839
|
+
*
|
|
840
|
+
* @default 'default'
|
|
841
|
+
*/
|
|
842
|
+
readonly traces?: readonly JetstreamTrace[] | 'default' | 'all' | 'none';
|
|
843
|
+
/**
|
|
844
|
+
* Header names to capture as span attributes (`messaging.header.<name>`).
|
|
845
|
+
* Match is case-insensitive. Glob wildcards (`*`) and negation (`!prefix-*`)
|
|
846
|
+
* are supported in the per-pattern form.
|
|
847
|
+
*
|
|
848
|
+
* SECURITY / GDPR: headers may contain authentication tokens, session
|
|
849
|
+
* identifiers, or other sensitive data. Captured values are exported to
|
|
850
|
+
* the configured OTel backend. Use an explicit allowlist in production.
|
|
851
|
+
* Setting this to `true` captures every header and is intended for
|
|
852
|
+
* development environments only.
|
|
853
|
+
*
|
|
854
|
+
* Transport-internal headers (`x-correlation-id`, `x-reply-to`, `x-error`,
|
|
855
|
+
* `x-subject`, `x-caller-name`) and propagator-owned headers
|
|
856
|
+
* (`traceparent`, `tracestate`, `baggage`, `sentry-trace`, `b3`, …) are
|
|
857
|
+
* always suppressed regardless of the allowlist.
|
|
858
|
+
*
|
|
859
|
+
* @default ['x-request-id']
|
|
860
|
+
*/
|
|
861
|
+
readonly captureHeaders?: readonly string[] | boolean;
|
|
862
|
+
/**
|
|
863
|
+
* Capture the message payload as the `messaging.nats.message.body` span
|
|
864
|
+
* attribute. Defaults to `false` for privacy and cost reasons.
|
|
865
|
+
*
|
|
866
|
+
* Passing `true` captures up to 4 KiB per message. Use the object form
|
|
867
|
+
* for finer control over size and subject scope.
|
|
868
|
+
*
|
|
869
|
+
* SECURITY / GDPR: payloads commonly contain PII, credentials, financial
|
|
870
|
+
* data, or content regulated by HIPAA / PCI-DSS. Enabling body capture
|
|
871
|
+
* in production is almost always a policy violation. Prefer enabling
|
|
872
|
+
* only in development, or pair with a custom `SpanProcessor` that
|
|
873
|
+
* scrubs or drops the attribute before export.
|
|
874
|
+
*
|
|
875
|
+
* @default false
|
|
876
|
+
*/
|
|
877
|
+
readonly captureBody?: boolean | CaptureBodyOptions;
|
|
878
|
+
/**
|
|
879
|
+
* Invoked after a publish span has been started and before the actual
|
|
880
|
+
* publish call executes. Use to enrich the span with custom attributes.
|
|
881
|
+
* Must be synchronous — thrown errors are caught and logged at debug
|
|
882
|
+
* level without affecting the publish path.
|
|
883
|
+
*/
|
|
884
|
+
publishHook?(span: Span, ctx: JetstreamPublishContext): void;
|
|
885
|
+
/**
|
|
886
|
+
* Invoked after a consume span has been started and before the handler
|
|
887
|
+
* is dispatched. Use to enrich the span with custom attributes derived
|
|
888
|
+
* from the incoming message or handler metadata.
|
|
889
|
+
*/
|
|
890
|
+
consumeHook?(span: Span, ctx: JetstreamConsumeContext): void;
|
|
891
|
+
/**
|
|
892
|
+
* Invoked once an operation's outcome is known (success, error, or
|
|
893
|
+
* timeout) and the span status has been set, just before the span ends.
|
|
894
|
+
* Fires for publish, consume, and RPC client spans.
|
|
895
|
+
*/
|
|
896
|
+
responseHook?(span: Span, ctx: JetstreamResponseContext): void;
|
|
897
|
+
/**
|
|
898
|
+
* Predicate evaluated at the top of every outgoing publish. Returning
|
|
899
|
+
* `false` skips span creation for that publish; propagation of trace
|
|
900
|
+
* context through headers still occurs, so downstream consumers are
|
|
901
|
+
* unaffected. Useful for suppressing noise from health-check or
|
|
902
|
+
* internal-only subjects.
|
|
903
|
+
*/
|
|
904
|
+
shouldTracePublish?(subject: string, record: JetstreamRecord): boolean;
|
|
905
|
+
/**
|
|
906
|
+
* Predicate evaluated at the top of every incoming message delivery.
|
|
907
|
+
* Returning `false` skips span creation for that delivery; the handler
|
|
908
|
+
* still executes normally and trace context is still extracted from
|
|
909
|
+
* headers for downstream calls.
|
|
910
|
+
*/
|
|
911
|
+
shouldTraceConsume?(subject: string, msg: ConsumeSourceMsg): boolean;
|
|
912
|
+
/**
|
|
913
|
+
* Classify a thrown error as `'expected'` (business error, part of the
|
|
914
|
+
* RPC contract) or `'unexpected'` (infrastructure failure or bug). Drives
|
|
915
|
+
* OpenTelemetry span status and attributes only.
|
|
916
|
+
*
|
|
917
|
+
* - `'expected'` → span status `OK` with `jetstream.rpc.reply.has_error`
|
|
918
|
+
* and `jetstream.rpc.reply.error.code` attributes
|
|
919
|
+
* - `'unexpected'` → span status `ERROR` with `span.recordException(err)`
|
|
920
|
+
*
|
|
921
|
+
* Reply envelopes delivered to RPC clients are identical in both cases.
|
|
922
|
+
* This classification affects only the observability artifact.
|
|
923
|
+
*
|
|
924
|
+
* The default recognizes NestJS-idiomatic primitives for business errors:
|
|
925
|
+
* `RpcException` and `HttpException`. Teams with custom error hierarchies
|
|
926
|
+
* override to recognize their own types.
|
|
927
|
+
*
|
|
928
|
+
* @example
|
|
929
|
+
* errorClassifier: (err) => {
|
|
930
|
+
* if (err instanceof MyDomainError) return 'expected';
|
|
931
|
+
* if (err?.code?.startsWith('BIZ_')) return 'expected';
|
|
932
|
+
* return 'unexpected';
|
|
933
|
+
* }
|
|
934
|
+
*/
|
|
935
|
+
errorClassifier?(err: unknown): ErrorClassification;
|
|
677
936
|
}
|
|
678
937
|
|
|
679
938
|
/**
|
|
680
|
-
*
|
|
939
|
+
* Stream config overrides exposed to users.
|
|
681
940
|
*
|
|
682
|
-
*
|
|
683
|
-
*
|
|
684
|
-
*
|
|
685
|
-
|
|
941
|
+
* `retention` is excluded because it is controlled by the transport layer
|
|
942
|
+
* (Workqueue for events/commands, Limits for broadcast/ordered).
|
|
943
|
+
* Any `retention` value provided at runtime is silently stripped.
|
|
944
|
+
*/
|
|
945
|
+
type StreamConfigOverrides = Partial<Omit<StreamConfig, 'retention'>>;
|
|
946
|
+
/**
|
|
947
|
+
* RPC transport configuration.
|
|
686
948
|
*
|
|
687
|
-
*
|
|
949
|
+
* Discriminated union on `mode`:
|
|
950
|
+
* - `'core'` — NATS native request/reply. Lowest latency.
|
|
951
|
+
* - `'jetstream'` — Commands persisted in JetStream. Responses via Core NATS inbox.
|
|
952
|
+
*
|
|
953
|
+
* When `mode` is `'core'`, only `timeout` is available.
|
|
954
|
+
* When `mode` is `'jetstream'`, additional stream/consumer overrides are exposed.
|
|
688
955
|
*/
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
956
|
+
type RpcConfig = {
|
|
957
|
+
mode: 'core';
|
|
958
|
+
/** Request timeout in ms. Default: 30_000. */
|
|
959
|
+
timeout?: number;
|
|
960
|
+
} | {
|
|
961
|
+
mode: 'jetstream';
|
|
962
|
+
/** Handler timeout in ms. Default: 180_000 (3 min). */
|
|
963
|
+
timeout?: number;
|
|
964
|
+
/** Raw NATS StreamConfig overrides for the command stream. */
|
|
965
|
+
stream?: StreamConfigOverrides;
|
|
966
|
+
/** Raw NATS ConsumerConfig overrides for the command consumer. */
|
|
967
|
+
consumer?: Partial<ConsumerConfig>;
|
|
968
|
+
/** Options passed to the nats.js `consumer.consume()` call for the command consumer. */
|
|
969
|
+
consume?: Partial<ConsumeOptions>;
|
|
970
|
+
/** Maximum number of concurrent RPC handler executions. */
|
|
971
|
+
concurrency?: number;
|
|
695
972
|
/**
|
|
696
|
-
*
|
|
973
|
+
* Auto-extend ack deadline via `msg.working()` during RPC handler execution.
|
|
974
|
+
* The RPC handler timeout (`setTimeout` + `msg.term()`) still acts as the hard cap.
|
|
975
|
+
*/
|
|
976
|
+
ackExtension?: boolean | number;
|
|
977
|
+
};
|
|
978
|
+
/** Overrides for JetStream stream and consumer configuration. */
|
|
979
|
+
interface StreamConsumerOverrides {
|
|
980
|
+
stream?: StreamConfigOverrides;
|
|
981
|
+
consumer?: Partial<ConsumerConfig>;
|
|
982
|
+
/**
|
|
983
|
+
* Options passed to the nats.js `consumer.consume()` call.
|
|
984
|
+
* Controls prefetch buffer size, idle heartbeat interval, and auto-refill thresholds.
|
|
697
985
|
*
|
|
698
|
-
*
|
|
699
|
-
*
|
|
700
|
-
*
|
|
986
|
+
* nats.js supports two consumption modes (message-based and byte-based).
|
|
987
|
+
* Do not mix `max_bytes`/`threshold_bytes` with `threshold_messages` —
|
|
988
|
+
* use one mode or the other.
|
|
989
|
+
*
|
|
990
|
+
* @see https://github.com/nats-io/nats.js — ConsumeOptions
|
|
701
991
|
*/
|
|
702
|
-
|
|
703
|
-
/** Get the stream name for a given kind. */
|
|
704
|
-
getStreamName(kind: StreamKind): string;
|
|
705
|
-
/** Get the subjects pattern for a given kind. */
|
|
706
|
-
getSubjects(kind: StreamKind): string[];
|
|
707
|
-
/** Ensure a single stream exists, creating or updating as needed. */
|
|
708
|
-
private ensureStream;
|
|
709
|
-
/** Ensure a dead-letter queue stream exists, creating or updating as needed. */
|
|
710
|
-
private ensureDlqStream;
|
|
711
|
-
private handleExistingStream;
|
|
712
|
-
private buildMutableOnlyConfig;
|
|
713
|
-
private logChanges;
|
|
714
|
-
/** Build the full stream config by merging defaults with user overrides. */
|
|
715
|
-
private buildConfig;
|
|
992
|
+
consume?: Partial<ConsumeOptions>;
|
|
716
993
|
/**
|
|
717
|
-
*
|
|
994
|
+
* Maximum number of concurrent handler executions (RxJS `mergeMap` limit).
|
|
718
995
|
*
|
|
719
|
-
*
|
|
720
|
-
*
|
|
996
|
+
* Default: `undefined` (unlimited — naturally bounded by `max_ack_pending`).
|
|
997
|
+
* Set this to protect downstream systems from overload.
|
|
998
|
+
*
|
|
999
|
+
* **Important:** if `concurrency < max_ack_pending`, messages buffer in RxJS
|
|
1000
|
+
* while their NATS ack timer ticks. Increase `ack_wait` proportionally to
|
|
1001
|
+
* prevent unnecessary redeliveries.
|
|
721
1002
|
*/
|
|
722
|
-
|
|
723
|
-
/** Get default config for a stream kind. */
|
|
724
|
-
private getDefaults;
|
|
725
|
-
/** Check if scheduling is enabled for a stream kind via `allow_msg_schedules` override. */
|
|
726
|
-
private isSchedulingEnabled;
|
|
727
|
-
/** Get user-provided overrides for a stream kind, stripping transport-controlled properties. */
|
|
728
|
-
private getOverrides;
|
|
1003
|
+
concurrency?: number;
|
|
729
1004
|
/**
|
|
730
|
-
*
|
|
731
|
-
*
|
|
732
|
-
*
|
|
1005
|
+
* Auto-extend the NATS ack deadline via `msg.working()` during handler execution.
|
|
1006
|
+
*
|
|
1007
|
+
* - `false` (default): disabled — NATS redelivers after `ack_wait` if not acked.
|
|
1008
|
+
* - `true`: auto-extend at `ack_wait / 2` interval (calculated from consumer config).
|
|
1009
|
+
* - `number`: explicit extension interval in milliseconds.
|
|
733
1010
|
*/
|
|
734
|
-
|
|
1011
|
+
ackExtension?: boolean | number;
|
|
735
1012
|
}
|
|
736
|
-
|
|
737
1013
|
/**
|
|
738
|
-
*
|
|
739
|
-
*
|
|
740
|
-
* **Workqueue & Broadcast** — at-least-once delivery:
|
|
741
|
-
* - Success -> ack | Error -> nak (retry) | Dead letter -> term
|
|
1014
|
+
* Configuration for ordered event consumers.
|
|
742
1015
|
*
|
|
743
|
-
*
|
|
744
|
-
* - No ack/nak/DLQ
|
|
745
|
-
* - Handler errors are logged but do not affect delivery.
|
|
1016
|
+
* Ordered consumers use Limits retention and deliver messages in strict
|
|
1017
|
+
* sequential order with at-most-once delivery. No ack/nak/DLQ.
|
|
746
1018
|
*
|
|
747
|
-
*
|
|
748
|
-
*
|
|
749
|
-
* - The DLQ stream name is derived from the service name (e.g., `orders__microservice_dlq-stream`).
|
|
750
|
-
* - Original message data and metadata are preserved in the DLQ message, with additional headers indicating the reason for failure.
|
|
1019
|
+
* Only a subset of consumer options applies — ordered consumers are
|
|
1020
|
+
* ephemeral and auto-managed by nats.js.
|
|
751
1021
|
*/
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
private readonly codec;
|
|
756
|
-
private readonly eventBus;
|
|
757
|
-
private readonly deadLetterConfig?;
|
|
758
|
-
private readonly processingConfig?;
|
|
759
|
-
private readonly ackWaitMap?;
|
|
760
|
-
private readonly connection?;
|
|
761
|
-
private readonly options?;
|
|
762
|
-
private readonly logger;
|
|
763
|
-
private readonly subscriptions;
|
|
764
|
-
constructor(messageProvider: MessageProvider, patternRegistry: PatternRegistry, codec: Codec, eventBus: EventBus, deadLetterConfig?: DeadLetterConfig | undefined, processingConfig?: EventProcessingConfig | undefined, ackWaitMap?: Map<StreamKind, number> | undefined, connection?: ConnectionProvider | undefined, options?: JetstreamModuleOptions | undefined);
|
|
1022
|
+
interface OrderedEventOverrides {
|
|
1023
|
+
/** Stream overrides (e.g. `max_age`, `max_bytes`). */
|
|
1024
|
+
stream?: StreamConfigOverrides;
|
|
765
1025
|
/**
|
|
766
|
-
*
|
|
767
|
-
*
|
|
1026
|
+
* Where to start reading when the consumer is (re)created.
|
|
1027
|
+
* @default DeliverPolicy.All
|
|
768
1028
|
*/
|
|
769
|
-
|
|
770
|
-
/** Start routing event, broadcast, and ordered messages to handlers. */
|
|
771
|
-
start(): void;
|
|
772
|
-
/** Stop routing and unsubscribe from all streams. */
|
|
773
|
-
destroy(): void;
|
|
774
|
-
/** Subscribe to a message stream and route each message to its handler. */
|
|
775
|
-
private subscribeToStream;
|
|
776
|
-
private getConcurrency;
|
|
777
|
-
private getAckExtensionConfig;
|
|
778
|
-
/** Handle a dead letter: invoke callback, then term or nak based on result. */
|
|
1029
|
+
deliverPolicy?: DeliverPolicy;
|
|
779
1030
|
/**
|
|
780
|
-
*
|
|
781
|
-
* publishing to the DLQ stream fails (due to network or NATS errors).
|
|
782
|
-
*
|
|
783
|
-
* Triggers the user-provided `onDeadLetter` hook for logging/alerting.
|
|
784
|
-
* On success, terminates the message. On error, leaves it unacknowledged (nak)
|
|
785
|
-
* so NATS can retry the delivery on the next cycle.
|
|
1031
|
+
* Start sequence number. Only used when `deliverPolicy` is `StartSequence`.
|
|
786
1032
|
*/
|
|
787
|
-
|
|
1033
|
+
optStartSeq?: number;
|
|
788
1034
|
/**
|
|
789
|
-
*
|
|
790
|
-
*
|
|
791
|
-
* Appends diagnostic metadata headers to the original message and preserves
|
|
792
|
-
* the primary payload. If publishing succeeds, it notifies the standard
|
|
793
|
-
* `onDeadLetter` callback and terminates the message. If it fails, it falls
|
|
794
|
-
* back to the callback entirely to prevent silent data loss.
|
|
1035
|
+
* Start time (ISO string). Only used when `deliverPolicy` is `StartTime`.
|
|
795
1036
|
*/
|
|
796
|
-
|
|
1037
|
+
optStartTime?: string;
|
|
797
1038
|
/**
|
|
798
|
-
*
|
|
799
|
-
*
|
|
800
|
-
* Emits a system event and delegates either to the robust DLQ stream publisher
|
|
801
|
-
* or directly to the fallback callback based on the active module configuration.
|
|
1039
|
+
* Replay policy for historical messages.
|
|
1040
|
+
* @default ReplayPolicy.Instant
|
|
802
1041
|
*/
|
|
803
|
-
|
|
1042
|
+
replayPolicy?: ReplayPolicy;
|
|
804
1043
|
}
|
|
805
|
-
|
|
806
1044
|
/**
|
|
807
|
-
*
|
|
1045
|
+
* Configuration for the handler metadata KV registry.
|
|
808
1046
|
*
|
|
809
|
-
*
|
|
810
|
-
*
|
|
811
|
-
*
|
|
812
|
-
* - Handler error -> publish error to ReplyTo -> term (no redelivery)
|
|
813
|
-
* - Timeout -> no response -> term
|
|
814
|
-
* - No handler / decode error -> term immediately
|
|
1047
|
+
* When any handler has `meta` in its extras, the transport writes metadata
|
|
1048
|
+
* entries to a NATS KV bucket at startup. External services (API gateways,
|
|
1049
|
+
* dashboards) can watch the bucket for service discovery.
|
|
815
1050
|
*
|
|
816
|
-
*
|
|
1051
|
+
* All fields are optional — sensible defaults are applied.
|
|
817
1052
|
*/
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
1053
|
+
interface MetadataRegistryOptions {
|
|
1054
|
+
/**
|
|
1055
|
+
* KV bucket name.
|
|
1056
|
+
* @default 'handler_registry'
|
|
1057
|
+
*/
|
|
1058
|
+
bucket?: string;
|
|
1059
|
+
/**
|
|
1060
|
+
* Number of KV bucket replicas. Must be an odd number (1, 3, 5, 7, ...).
|
|
1061
|
+
* Requires a NATS cluster with at least this many nodes.
|
|
1062
|
+
* @default 1
|
|
1063
|
+
*/
|
|
1064
|
+
replicas?: number;
|
|
1065
|
+
/**
|
|
1066
|
+
* KV bucket TTL in milliseconds.
|
|
1067
|
+
*
|
|
1068
|
+
* Entries expire automatically unless refreshed by a heartbeat.
|
|
1069
|
+
* The transport refreshes entries every `ttl / 2` while the pod is alive.
|
|
1070
|
+
* When the pod stops (graceful or crash), entries expire after this duration.
|
|
1071
|
+
*
|
|
1072
|
+
* @default 30_000 (30 seconds)
|
|
1073
|
+
*/
|
|
1074
|
+
ttl?: number;
|
|
839
1075
|
}
|
|
840
|
-
|
|
841
1076
|
/**
|
|
842
|
-
*
|
|
1077
|
+
* Root module configuration for `JetstreamModule.forRoot()`.
|
|
843
1078
|
*
|
|
844
|
-
*
|
|
845
|
-
*
|
|
1079
|
+
* Minimal usage requires only `name` and `servers`.
|
|
1080
|
+
* All other fields have production-ready defaults.
|
|
846
1081
|
*/
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
private readonly logger;
|
|
853
|
-
constructor(options: JetstreamModuleOptions, connection: ConnectionProvider, streamProvider: StreamProvider, patternRegistry: PatternRegistry);
|
|
1082
|
+
interface JetstreamModuleOptions {
|
|
1083
|
+
/** Service name. Used for stream/consumer/subject naming. */
|
|
1084
|
+
name: string;
|
|
1085
|
+
/** NATS server URLs. */
|
|
1086
|
+
servers: string[];
|
|
854
1087
|
/**
|
|
855
|
-
*
|
|
1088
|
+
* Global message codec.
|
|
1089
|
+
* @default JsonCodec
|
|
1090
|
+
*/
|
|
1091
|
+
codec?: Codec;
|
|
1092
|
+
/**
|
|
1093
|
+
* RPC transport mode and configuration.
|
|
1094
|
+
* @default { mode: 'core' }
|
|
1095
|
+
*/
|
|
1096
|
+
rpc?: RpcConfig;
|
|
1097
|
+
/**
|
|
1098
|
+
* Enable consumer infrastructure (streams, consumers, message routing).
|
|
1099
|
+
* Set to `false` for publisher-only services (e.g., API gateways).
|
|
1100
|
+
* @default true
|
|
1101
|
+
*/
|
|
1102
|
+
consumer?: boolean;
|
|
1103
|
+
/** Workqueue event stream/consumer overrides. */
|
|
1104
|
+
events?: StreamConsumerOverrides;
|
|
1105
|
+
/** Broadcast event stream/consumer overrides. */
|
|
1106
|
+
broadcast?: StreamConsumerOverrides;
|
|
1107
|
+
/**
|
|
1108
|
+
* Ordered event consumer configuration.
|
|
856
1109
|
*
|
|
857
|
-
*
|
|
1110
|
+
* Ordered events use a separate stream with Limits retention and deliver
|
|
1111
|
+
* messages in strict sequential order. Use `ordered:` prefix when publishing.
|
|
1112
|
+
*
|
|
1113
|
+
* @see OrderedEventOverrides
|
|
858
1114
|
*/
|
|
859
|
-
|
|
860
|
-
/** Get the consumer name for a given kind. */
|
|
861
|
-
getConsumerName(kind: StreamKind): string;
|
|
1115
|
+
ordered?: OrderedEventOverrides;
|
|
862
1116
|
/**
|
|
863
|
-
*
|
|
864
|
-
*
|
|
865
|
-
* the current pod's configuration.
|
|
1117
|
+
* Transport lifecycle hook handlers.
|
|
1118
|
+
* Unset hooks are silently ignored — no default logging.
|
|
866
1119
|
*/
|
|
867
|
-
|
|
1120
|
+
hooks?: Partial<TransportHooks>;
|
|
868
1121
|
/**
|
|
869
|
-
*
|
|
870
|
-
*
|
|
1122
|
+
* Async callback invoked when an event message exhausts all delivery attempts.
|
|
1123
|
+
* Called before msg.term(). If it throws, the message is nak'd for retry.
|
|
871
1124
|
*
|
|
872
|
-
*
|
|
873
|
-
*
|
|
874
|
-
* completes and the backup is cleaned up.
|
|
1125
|
+
* Use this to persist dead letters to an external store (DB, S3, another queue).
|
|
1126
|
+
* The NATS connection is available via `JETSTREAM_CONNECTION` token in forRootAsync.
|
|
875
1127
|
*
|
|
876
|
-
*
|
|
877
|
-
*
|
|
878
|
-
*
|
|
879
|
-
*
|
|
1128
|
+
* @example
|
|
1129
|
+
* ```typescript
|
|
1130
|
+
* JetstreamModule.forRootAsync({
|
|
1131
|
+
* name: 'my-service',
|
|
1132
|
+
* imports: [DlqModule],
|
|
1133
|
+
* inject: [DlqService, JETSTREAM_CONNECTION],
|
|
1134
|
+
* useFactory: (dlqService, connection) => ({
|
|
1135
|
+
* servers: ['nats://localhost:4222'],
|
|
1136
|
+
* onDeadLetter: async (info) => {
|
|
1137
|
+
* await dlqService.persist(info);
|
|
1138
|
+
* },
|
|
1139
|
+
* }),
|
|
1140
|
+
* })
|
|
1141
|
+
* ```
|
|
1142
|
+
*/
|
|
1143
|
+
onDeadLetter?(info: DeadLetterInfo): Promise<void>;
|
|
1144
|
+
/**
|
|
1145
|
+
* Dead-letter queue (DLQ) configuration.
|
|
1146
|
+
* DLQ is a separate stream used to store messages that have exhausted all delivery attempts.
|
|
1147
|
+
* @example
|
|
1148
|
+
* ```typescript
|
|
1149
|
+
* JetstreamModule.forRootAsync({
|
|
1150
|
+
* name: 'my-service',
|
|
1151
|
+
* servers: ['nats://localhost:4222'],
|
|
1152
|
+
* dlq: {
|
|
1153
|
+
* stream: {
|
|
1154
|
+
* max_age: toNanos(30, 'days'),
|
|
1155
|
+
* },
|
|
1156
|
+
* },
|
|
1157
|
+
* })
|
|
1158
|
+
* ```
|
|
1159
|
+
*/
|
|
1160
|
+
dlq?: {
|
|
1161
|
+
stream?: StreamConfigOverrides;
|
|
1162
|
+
};
|
|
1163
|
+
/**
|
|
1164
|
+
* Graceful shutdown timeout in ms.
|
|
1165
|
+
* Handlers exceeding this are abandoned.
|
|
1166
|
+
* @default 10_000
|
|
1167
|
+
*/
|
|
1168
|
+
shutdownTimeout?: number;
|
|
1169
|
+
/**
|
|
1170
|
+
* Allow destructive stream migration when immutable config changes are detected.
|
|
1171
|
+
*
|
|
1172
|
+
* When `true`, the transport will recreate streams (via blue-green sourcing)
|
|
1173
|
+
* if immutable properties like `storage` differ from the running stream.
|
|
1174
|
+
* Messages are preserved during migration.
|
|
1175
|
+
*
|
|
1176
|
+
* `retention` is NOT migratable — it is controlled by the transport
|
|
1177
|
+
* (Workqueue for events, Limits for broadcast/ordered) and a mismatch
|
|
1178
|
+
* is always treated as an error regardless of this flag.
|
|
1179
|
+
*
|
|
1180
|
+
* When `false` (default), immutable conflicts are logged as warnings and
|
|
1181
|
+
* the stream continues with its existing configuration.
|
|
1182
|
+
*
|
|
1183
|
+
* @default false
|
|
1184
|
+
*/
|
|
1185
|
+
allowDestructiveMigration?: boolean;
|
|
1186
|
+
/**
|
|
1187
|
+
* Handler metadata KV registry configuration.
|
|
1188
|
+
*
|
|
1189
|
+
* When any handler has `meta` in its `@EventPattern` / `@MessagePattern` extras,
|
|
1190
|
+
* the transport writes metadata to a NATS KV bucket at startup.
|
|
1191
|
+
* External services (API gateways, dashboards, CLI tools) can read or watch
|
|
1192
|
+
* the bucket for dynamic service discovery.
|
|
1193
|
+
*
|
|
1194
|
+
* Auto-enabled when any handler has `meta`. Set to customize bucket name,
|
|
1195
|
+
* replicas, or TTL.
|
|
1196
|
+
*
|
|
1197
|
+
* @see MetadataRegistryOptions
|
|
1198
|
+
*/
|
|
1199
|
+
metadata?: MetadataRegistryOptions;
|
|
1200
|
+
/**
|
|
1201
|
+
* Raw NATS ConnectionOptions pass-through for advanced connection config.
|
|
1202
|
+
* Allows setting tls, auth, reconnect behavior, maxReconnectAttempts, etc.
|
|
1203
|
+
* Merged with `name` and `servers` — those take precedence.
|
|
1204
|
+
*/
|
|
1205
|
+
connectionOptions?: Partial<ConnectionOptions>;
|
|
1206
|
+
/**
|
|
1207
|
+
* Built-in Prometheus metrics.
|
|
1208
|
+
*
|
|
1209
|
+
* Pass `true` to enable with defaults, or a {@link MetricsConfig} object for
|
|
1210
|
+
* full control (custom registry, prefix, labels, polling, buckets).
|
|
1211
|
+
* When omitted or `false`, the metrics module is not registered and
|
|
1212
|
+
* `prom-client` is not imported — zero overhead.
|
|
1213
|
+
*
|
|
1214
|
+
* Requires `prom-client` peer dependency to be installed when enabled.
|
|
1215
|
+
* The service writes to `prom-client`'s global `register` by default,
|
|
1216
|
+
* making integration with `@willsoto/nestjs-prometheus` (or any
|
|
1217
|
+
* `prom-client`-based `/metrics` exporter) work without coordination.
|
|
1218
|
+
*
|
|
1219
|
+
* @see MetricsConfig
|
|
1220
|
+
* @example
|
|
1221
|
+
* ```typescript
|
|
1222
|
+
* JetstreamModule.forRoot({
|
|
1223
|
+
* name: 'orders',
|
|
1224
|
+
* servers: ['nats://localhost:4222'],
|
|
1225
|
+
* metrics: true,
|
|
1226
|
+
* })
|
|
1227
|
+
* ```
|
|
880
1228
|
*/
|
|
881
|
-
|
|
1229
|
+
metrics?: MetricsOption;
|
|
882
1230
|
/**
|
|
883
|
-
*
|
|
884
|
-
*
|
|
885
|
-
*
|
|
1231
|
+
* OpenTelemetry integration. When omitted, sensible defaults are applied:
|
|
1232
|
+
* tracing is enabled, default trace kinds are emitted, only standard
|
|
1233
|
+
* correlation headers are captured. If no OTel SDK is registered in the
|
|
1234
|
+
* consuming application, all tracer calls are no-ops — there is no
|
|
1235
|
+
* runtime cost.
|
|
1236
|
+
*
|
|
1237
|
+
* @see OtelOptions
|
|
886
1238
|
*/
|
|
887
|
-
|
|
1239
|
+
otel?: OtelOptions;
|
|
1240
|
+
}
|
|
1241
|
+
/** Options for `JetstreamModule.forFeature()`. */
|
|
1242
|
+
interface JetstreamFeatureOptions {
|
|
1243
|
+
/** Target service name for subject construction. */
|
|
1244
|
+
name: string;
|
|
888
1245
|
/**
|
|
889
|
-
*
|
|
1246
|
+
* Override the global codec for this client.
|
|
1247
|
+
* Falls back to the root codec from `forRoot()` when omitted.
|
|
890
1248
|
*/
|
|
891
|
-
|
|
892
|
-
/** Build consumer config by merging defaults with user overrides. */
|
|
893
|
-
private buildConfig;
|
|
894
|
-
/** Get default config for a consumer kind. */
|
|
895
|
-
private getDefaults;
|
|
896
|
-
/** Get user-provided overrides for a consumer kind. */
|
|
897
|
-
private getOverrides;
|
|
1249
|
+
codec?: Codec;
|
|
898
1250
|
}
|
|
899
|
-
|
|
900
|
-
/** Callback to recreate a consumer when it disappears. */
|
|
901
|
-
type ConsumerRecoveryFn = (kind: StreamKind) => Promise<ConsumerInfo>;
|
|
902
1251
|
/**
|
|
903
|
-
*
|
|
904
|
-
*
|
|
905
|
-
* Uses `defer()` + `repeat()` pattern for self-healing: when the async
|
|
906
|
-
* iterator completes (e.g., NATS restart), the consumer automatically
|
|
907
|
-
* re-establishes after a short delay.
|
|
1252
|
+
* Async configuration for `JetstreamModule.forRootAsync()`.
|
|
908
1253
|
*
|
|
909
|
-
*
|
|
1254
|
+
* Supports three patterns: `useFactory`, `useExisting`, `useClass`.
|
|
910
1255
|
*/
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
private readonly logger;
|
|
917
|
-
private readonly activeIterators;
|
|
918
|
-
private orderedReadyResolve;
|
|
919
|
-
private orderedReadyReject;
|
|
920
|
-
private destroy$;
|
|
921
|
-
private eventMessages$;
|
|
922
|
-
private commandMessages$;
|
|
923
|
-
private broadcastMessages$;
|
|
924
|
-
private orderedMessages$;
|
|
925
|
-
constructor(connection: ConnectionProvider, eventBus: EventBus, consumeOptionsMap?: Map<StreamKind, Partial<ConsumeOptions>>, consumerRecoveryFn?: ConsumerRecoveryFn | undefined);
|
|
926
|
-
/** Observable stream of workqueue event messages. */
|
|
927
|
-
get events$(): Observable<JsMsg>;
|
|
928
|
-
/** Observable stream of RPC command messages (jetstream mode). */
|
|
929
|
-
get commands$(): Observable<JsMsg>;
|
|
930
|
-
/** Observable stream of broadcast event messages. */
|
|
931
|
-
get broadcasts$(): Observable<JsMsg>;
|
|
932
|
-
/** Observable stream of ordered event messages (strict sequential delivery). */
|
|
933
|
-
get ordered$(): Observable<JsMsg>;
|
|
934
|
-
/**
|
|
935
|
-
* Start consuming messages from the given consumer infos.
|
|
936
|
-
*
|
|
937
|
-
* Each consumer gets an independent self-healing flow.
|
|
938
|
-
* Call `destroy()` to stop all consumers.
|
|
939
|
-
*/
|
|
940
|
-
start(consumers: Map<StreamKind, ConsumerInfo>): void;
|
|
1256
|
+
type JetstreamModuleAsyncOptions = {
|
|
1257
|
+
/** Service name — required upfront for DI token generation. */
|
|
1258
|
+
name: string;
|
|
1259
|
+
/** Additional module imports (e.g., ConfigModule). */
|
|
1260
|
+
imports?: ModuleMetadata['imports'];
|
|
941
1261
|
/**
|
|
942
|
-
*
|
|
943
|
-
*
|
|
944
|
-
*
|
|
945
|
-
*
|
|
1262
|
+
* Built-in Prometheus metrics. Specified at the async-options level (parallel
|
|
1263
|
+
* to {@link name}) because module composition is decided synchronously before
|
|
1264
|
+
* the async factory runs. Use `true` for defaults, a {@link MetricsConfig}
|
|
1265
|
+
* object for full control, or omit/`false` to disable entirely.
|
|
946
1266
|
*
|
|
947
|
-
* @
|
|
948
|
-
* @param filterSubjects - NATS subjects to filter on.
|
|
949
|
-
* @param orderedConfig - Optional overrides for ordered consumer options.
|
|
1267
|
+
* @see JetstreamModuleOptions.metrics
|
|
950
1268
|
*/
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1269
|
+
metrics?: MetricsOption;
|
|
1270
|
+
} & ({
|
|
1271
|
+
useFactory(...args: unknown[]): Promise<Omit<JetstreamModuleOptions, 'name'>> | Omit<JetstreamModuleOptions, 'name'>;
|
|
1272
|
+
inject?: FactoryProvider['inject'];
|
|
1273
|
+
useExisting?: never;
|
|
1274
|
+
useClass?: never;
|
|
1275
|
+
} | {
|
|
1276
|
+
useExisting: Type<Omit<JetstreamModuleOptions, 'name'>>;
|
|
1277
|
+
useFactory?: never;
|
|
1278
|
+
inject?: never;
|
|
1279
|
+
useClass?: never;
|
|
1280
|
+
} | {
|
|
1281
|
+
useClass: Type<Omit<JetstreamModuleOptions, 'name'>>;
|
|
1282
|
+
useFactory?: never;
|
|
1283
|
+
inject?: never;
|
|
1284
|
+
useExisting?: never;
|
|
1285
|
+
});
|
|
1286
|
+
|
|
1287
|
+
/** Options for one-shot delayed delivery via NATS 2.12 message scheduling. */
|
|
1288
|
+
interface ScheduleRecordOptions {
|
|
1289
|
+
/** When to deliver the message. Must be in the future. */
|
|
1290
|
+
at: Date;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
/** @internal Grouped pattern lists by stream kind, used for stream/consumer setup. */
|
|
1294
|
+
interface PatternsByKind {
|
|
1295
|
+
/** Workqueue event patterns. */
|
|
1296
|
+
events: string[];
|
|
1297
|
+
/** RPC command patterns. */
|
|
1298
|
+
commands: string[];
|
|
1299
|
+
/** Broadcast event patterns. */
|
|
1300
|
+
broadcasts: string[];
|
|
1301
|
+
/** Ordered event patterns (strict sequential delivery). */
|
|
1302
|
+
ordered: string[];
|
|
1303
|
+
}
|
|
1304
|
+
/** Options for configuring event/broadcast processing behavior. */
|
|
1305
|
+
interface EventProcessingConfig {
|
|
1306
|
+
events?: {
|
|
1307
|
+
concurrency?: number;
|
|
1308
|
+
ackExtension?: boolean | number;
|
|
1309
|
+
};
|
|
1310
|
+
broadcast?: {
|
|
1311
|
+
concurrency?: number;
|
|
1312
|
+
ackExtension?: boolean | number;
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
/** Options for dead letter queue handling. */
|
|
1316
|
+
interface DeadLetterConfig {
|
|
958
1317
|
/**
|
|
959
|
-
*
|
|
960
|
-
*
|
|
961
|
-
* Unlike JetStream Manager calls (which throw `JetStreamApiError`),
|
|
962
|
-
* the JetStream client's `consumers.get()` throws a plain `Error`
|
|
963
|
-
* with the error code embedded in the message text.
|
|
1318
|
+
* Map of stream name -> max_deliver value.
|
|
1319
|
+
* Used to detect when a message from a given stream has exhausted all delivery attempts.
|
|
964
1320
|
*/
|
|
965
|
-
|
|
966
|
-
/**
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
/** Single iteration: create ordered consumer -> push messages into the subject. */
|
|
975
|
-
private consumeOrderedOnce;
|
|
1321
|
+
maxDeliverByStream: Map<string, number>;
|
|
1322
|
+
/** Async callback invoked when a message exhausts all deliveries. */
|
|
1323
|
+
onDeadLetter(info: DeadLetterInfo): Promise<void>;
|
|
1324
|
+
}
|
|
1325
|
+
/** Options for configuring RPC processing behavior. */
|
|
1326
|
+
interface RpcRouterOptions {
|
|
1327
|
+
timeout?: number;
|
|
1328
|
+
concurrency?: number;
|
|
1329
|
+
ackExtension?: boolean | number;
|
|
976
1330
|
}
|
|
977
1331
|
|
|
978
1332
|
/**
|
|
979
|
-
*
|
|
980
|
-
*
|
|
981
|
-
* Uses TTL + heartbeat to manage entry lifecycle:
|
|
982
|
-
* - Entries are written on startup and refreshed every `ttl / 2`
|
|
983
|
-
* - When the pod stops (graceful or crash), heartbeat stops → entries expire via TTL
|
|
984
|
-
* - No explicit delete needed — NATS handles expiry automatically
|
|
1333
|
+
* Registry mapping NATS subjects to NestJS message handlers.
|
|
985
1334
|
*
|
|
986
|
-
*
|
|
987
|
-
*
|
|
1335
|
+
* Handles subject normalization and categorization:
|
|
1336
|
+
* - Detects broadcast handlers via `extras.broadcast` metadata
|
|
1337
|
+
* - Normalizes full NATS subjects back to user-facing patterns
|
|
1338
|
+
* - Provides lists of patterns by category for stream/consumer setup
|
|
988
1339
|
*/
|
|
989
|
-
declare class
|
|
990
|
-
private readonly
|
|
1340
|
+
declare class PatternRegistry {
|
|
1341
|
+
private readonly options;
|
|
991
1342
|
private readonly logger;
|
|
992
|
-
private readonly
|
|
993
|
-
private
|
|
994
|
-
private
|
|
995
|
-
private
|
|
996
|
-
private
|
|
997
|
-
private
|
|
998
|
-
|
|
1343
|
+
private readonly registry;
|
|
1344
|
+
private cachedPatterns;
|
|
1345
|
+
private _hasEvents;
|
|
1346
|
+
private _hasCommands;
|
|
1347
|
+
private _hasBroadcasts;
|
|
1348
|
+
private _hasOrdered;
|
|
1349
|
+
private _hasMetadata;
|
|
1350
|
+
constructor(options: JetstreamModuleOptions);
|
|
999
1351
|
/**
|
|
1000
|
-
*
|
|
1001
|
-
*
|
|
1002
|
-
* Creates the bucket if it doesn't exist (idempotent).
|
|
1003
|
-
* Skips silently when entries map is empty.
|
|
1004
|
-
* Starts a heartbeat interval that refreshes entries every `ttl / 2`
|
|
1005
|
-
* to prevent TTL expiry while the pod is alive.
|
|
1352
|
+
* Register all handlers from the NestJS strategy.
|
|
1006
1353
|
*
|
|
1007
|
-
*
|
|
1354
|
+
* @param handlers Map of pattern -> MessageHandler from `Server.getHandlers()`.
|
|
1355
|
+
*/
|
|
1356
|
+
registerHandlers(handlers: Map<string, MessageHandler>): void;
|
|
1357
|
+
/** Find handler for a full NATS subject. */
|
|
1358
|
+
getHandler(subject: string): MessageHandler | null;
|
|
1359
|
+
/**
|
|
1360
|
+
* Resolve the declared pattern and {@link StreamKind} for a full NATS subject.
|
|
1008
1361
|
*
|
|
1009
|
-
*
|
|
1362
|
+
* Returns `null` when the subject is not registered. The declared pattern is
|
|
1363
|
+
* the value the user passed to `@EventPattern`/`@MessagePattern` — stable and
|
|
1364
|
+
* bounded, suitable for use as a Prometheus label without cardinality risk.
|
|
1010
1365
|
*/
|
|
1011
|
-
|
|
1366
|
+
resolveDeclared(subject: string): {
|
|
1367
|
+
pattern: string;
|
|
1368
|
+
kind: StreamKind;
|
|
1369
|
+
} | null;
|
|
1370
|
+
/** Get all registered broadcast patterns (for consumer filter_subject setup). */
|
|
1371
|
+
getBroadcastPatterns(): string[];
|
|
1372
|
+
hasBroadcastHandlers(): boolean;
|
|
1373
|
+
hasRpcHandlers(): boolean;
|
|
1374
|
+
hasEventHandlers(): boolean;
|
|
1375
|
+
hasOrderedHandlers(): boolean;
|
|
1376
|
+
/** Get fully-qualified NATS subjects for ordered handlers. */
|
|
1377
|
+
getOrderedSubjects(): string[];
|
|
1378
|
+
/** Check if any registered handler has metadata. */
|
|
1379
|
+
hasMetadata(): boolean;
|
|
1012
1380
|
/**
|
|
1013
|
-
*
|
|
1381
|
+
* Get handler metadata entries for KV publishing.
|
|
1014
1382
|
*
|
|
1015
|
-
*
|
|
1016
|
-
*
|
|
1383
|
+
* Returns a map of KV key -> metadata object for all handlers that have `meta`.
|
|
1384
|
+
* Key format: `{serviceName}.{kind}.{pattern}`.
|
|
1017
1385
|
*/
|
|
1018
|
-
|
|
1019
|
-
/**
|
|
1020
|
-
|
|
1021
|
-
/**
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
private
|
|
1025
|
-
|
|
1026
|
-
private openBucket;
|
|
1386
|
+
getMetadataEntries(): Map<string, Record<string, unknown>>;
|
|
1387
|
+
/** Get patterns grouped by kind (cached after registration). */
|
|
1388
|
+
getPatternsByKind(): PatternsByKind;
|
|
1389
|
+
/** Normalize a full NATS subject back to the user-facing pattern. */
|
|
1390
|
+
normalizeSubject(subject: string): string;
|
|
1391
|
+
private buildPatternsByKind;
|
|
1392
|
+
private resolveStreamKind;
|
|
1393
|
+
private logSummary;
|
|
1027
1394
|
}
|
|
1028
1395
|
|
|
1029
1396
|
/**
|
|
1030
|
-
*
|
|
1397
|
+
* Handles RPC via NATS Core request/reply pattern.
|
|
1031
1398
|
*
|
|
1032
|
-
*
|
|
1033
|
-
*
|
|
1399
|
+
* Subscribes to `{service}.cmd.>` with a queue group for load balancing.
|
|
1400
|
+
* Each request is processed and replied to directly via `msg.respond()`.
|
|
1401
|
+
*
|
|
1402
|
+
* This is the default RPC mode — lowest latency, no persistence overhead.
|
|
1034
1403
|
*/
|
|
1035
|
-
declare
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1404
|
+
declare class CoreRpcServer {
|
|
1405
|
+
private readonly connection;
|
|
1406
|
+
private readonly patternRegistry;
|
|
1407
|
+
private readonly codec;
|
|
1408
|
+
private readonly eventBus;
|
|
1409
|
+
private readonly logger;
|
|
1410
|
+
private subscription;
|
|
1411
|
+
private readonly otel;
|
|
1412
|
+
private readonly serviceName;
|
|
1413
|
+
private readonly serverEndpoint;
|
|
1414
|
+
constructor(options: JetstreamModuleOptions, connection: ConnectionProvider, patternRegistry: PatternRegistry, codec: Codec, eventBus: EventBus);
|
|
1415
|
+
/** Start listening for RPC requests on the command subject. */
|
|
1416
|
+
start(): Promise<void>;
|
|
1417
|
+
/** Stop listening and clean up the subscription. */
|
|
1418
|
+
stop(): void;
|
|
1419
|
+
/** Handle an incoming Core NATS request. */
|
|
1420
|
+
private handleRequest;
|
|
1421
|
+
private reportHandlerCompleted;
|
|
1422
|
+
/** Send an error response back to the caller with x-error header. */
|
|
1423
|
+
private respondWithError;
|
|
1042
1424
|
}
|
|
1043
1425
|
|
|
1044
1426
|
/**
|
|
1045
|
-
*
|
|
1427
|
+
* Manages JetStream stream lifecycle: creation, updates, and idempotent ensures.
|
|
1046
1428
|
*
|
|
1047
|
-
*
|
|
1048
|
-
*
|
|
1049
|
-
*
|
|
1050
|
-
*
|
|
1051
|
-
* 4. Handles Core or JetStream RPC based on configuration
|
|
1429
|
+
* Creates up to three stream types depending on configuration:
|
|
1430
|
+
* - **Event stream** — workqueue events (always, when consumer enabled)
|
|
1431
|
+
* - **Command stream** — RPC commands (only in jetstream RPC mode)
|
|
1432
|
+
* - **Broadcast stream** — fan-out events (only if broadcast handlers exist)
|
|
1052
1433
|
*
|
|
1053
|
-
* All
|
|
1434
|
+
* All operations are idempotent: safe to call on every startup and reconnection.
|
|
1054
1435
|
*/
|
|
1055
|
-
declare class
|
|
1436
|
+
declare class StreamProvider {
|
|
1056
1437
|
private readonly options;
|
|
1057
1438
|
private readonly connection;
|
|
1058
|
-
private readonly
|
|
1059
|
-
private readonly
|
|
1060
|
-
private readonly
|
|
1061
|
-
private readonly
|
|
1062
|
-
private readonly
|
|
1063
|
-
|
|
1064
|
-
private readonly coreRpcServer;
|
|
1065
|
-
private readonly ackWaitMap;
|
|
1066
|
-
private readonly metadataProvider?;
|
|
1067
|
-
readonly transportId: symbol;
|
|
1068
|
-
private readonly listeners;
|
|
1069
|
-
private started;
|
|
1070
|
-
constructor(options: JetstreamModuleOptions, connection: ConnectionProvider, patternRegistry: PatternRegistry, streamProvider: StreamProvider, consumerProvider: ConsumerProvider, messageProvider: MessageProvider, eventRouter: EventRouter, rpcRouter: RpcRouter, coreRpcServer: CoreRpcServer, ackWaitMap?: Map<StreamKind, number>, metadataProvider?: MetadataProvider | undefined);
|
|
1439
|
+
private readonly logger;
|
|
1440
|
+
private readonly migration;
|
|
1441
|
+
private readonly otel;
|
|
1442
|
+
private readonly otelServiceName;
|
|
1443
|
+
private readonly otelEndpoint;
|
|
1444
|
+
constructor(options: JetstreamModuleOptions, connection: ConnectionProvider);
|
|
1071
1445
|
/**
|
|
1072
|
-
*
|
|
1446
|
+
* Ensure all required streams exist with correct configuration.
|
|
1073
1447
|
*
|
|
1074
|
-
*
|
|
1448
|
+
* @param kinds Which stream kinds to create. Determined by the module based
|
|
1449
|
+
* on RPC mode and registered handler patterns.
|
|
1450
|
+
* If the dlq option is enabled, also ensures the DLQ stream exists.
|
|
1075
1451
|
*/
|
|
1076
|
-
|
|
1077
|
-
/**
|
|
1078
|
-
|
|
1452
|
+
ensureStreams(kinds: StreamKind[]): Promise<void>;
|
|
1453
|
+
/** Get the stream name for a given kind. */
|
|
1454
|
+
getStreamName(kind: StreamKind): string;
|
|
1455
|
+
/** Get the subjects pattern for a given kind. */
|
|
1456
|
+
getSubjects(kind: StreamKind): string[];
|
|
1457
|
+
/** Ensure a single stream exists, creating or updating as needed. */
|
|
1458
|
+
private ensureStream;
|
|
1459
|
+
/** Ensure a dead-letter queue stream exists, creating or updating as needed. */
|
|
1460
|
+
private ensureDlqStream;
|
|
1461
|
+
private handleExistingStream;
|
|
1462
|
+
private buildMutableOnlyConfig;
|
|
1463
|
+
private logChanges;
|
|
1464
|
+
/** Build the full stream config by merging defaults with user overrides. */
|
|
1465
|
+
private buildConfig;
|
|
1079
1466
|
/**
|
|
1080
|
-
*
|
|
1467
|
+
* Build the stream configuration for the Dead-Letter Queue (DLQ).
|
|
1081
1468
|
*
|
|
1082
|
-
*
|
|
1083
|
-
* are
|
|
1469
|
+
* Merges the library default DLQ config with user-provided overrides.
|
|
1470
|
+
* Ensures transport-controlled settings like retention are safely decoupled.
|
|
1084
1471
|
*/
|
|
1085
|
-
|
|
1472
|
+
private buildDlqConfig;
|
|
1473
|
+
/** Get default config for a stream kind. */
|
|
1474
|
+
private getDefaults;
|
|
1475
|
+
/** Check if scheduling is enabled for a stream kind via `allow_msg_schedules` override. */
|
|
1476
|
+
private isSchedulingEnabled;
|
|
1477
|
+
/** Get user-provided overrides for a stream kind, stripping transport-controlled properties. */
|
|
1478
|
+
private getOverrides;
|
|
1086
1479
|
/**
|
|
1087
|
-
*
|
|
1088
|
-
*
|
|
1089
|
-
*
|
|
1480
|
+
* Remove transport-controlled properties from user overrides.
|
|
1481
|
+
* `retention` is managed by the transport (Workqueue/Limits per stream kind)
|
|
1482
|
+
* and silently stripped to protect users from misconfiguration.
|
|
1090
1483
|
*/
|
|
1091
|
-
|
|
1092
|
-
/** Access the pattern registry (for module-level introspection). */
|
|
1093
|
-
getPatternRegistry(): PatternRegistry;
|
|
1094
|
-
/** Determine which streams and durable consumers are needed. */
|
|
1095
|
-
private resolveRequiredKinds;
|
|
1096
|
-
/** Populate the shared ack_wait map from actual NATS consumer configs. */
|
|
1097
|
-
private populateAckWaitMap;
|
|
1098
|
-
/** Build max_deliver map from actual NATS consumer configs (not options). */
|
|
1099
|
-
private buildMaxDeliverMap;
|
|
1484
|
+
private stripTransportControlled;
|
|
1100
1485
|
}
|
|
1101
1486
|
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
/**
|
|
1107
|
-
* Orchestrates graceful transport shutdown.
|
|
1108
|
-
*
|
|
1109
|
-
* Shutdown sequence:
|
|
1110
|
-
* 1. Emit onShutdownStart hook
|
|
1111
|
-
* 2. Stop accepting new messages (close subscriptions, stop consumers)
|
|
1112
|
-
* 3. Drain and close NATS connection (with timeout safety net)
|
|
1113
|
-
* 4. Emit onShutdownComplete hook
|
|
1114
|
-
*
|
|
1115
|
-
* Idempotent — concurrent or repeated calls return the same promise.
|
|
1116
|
-
* This is critical because NestJS may call `onApplicationShutdown` on
|
|
1117
|
-
* multiple module instances (forRoot + forFeature) that share this
|
|
1118
|
-
* singleton, and the call order is not guaranteed.
|
|
1119
|
-
*/
|
|
1120
|
-
declare class ShutdownManager {
|
|
1121
|
-
private readonly connection;
|
|
1487
|
+
declare class EventRouter {
|
|
1488
|
+
private readonly messageProvider;
|
|
1489
|
+
private readonly patternRegistry;
|
|
1490
|
+
private readonly codec;
|
|
1122
1491
|
private readonly eventBus;
|
|
1123
|
-
private readonly
|
|
1492
|
+
private readonly deadLetterConfig?;
|
|
1493
|
+
private readonly processingConfig?;
|
|
1494
|
+
private readonly ackWaitMap?;
|
|
1495
|
+
private readonly connection?;
|
|
1496
|
+
private readonly options?;
|
|
1124
1497
|
private readonly logger;
|
|
1125
|
-
private
|
|
1126
|
-
|
|
1498
|
+
private readonly subscriptions;
|
|
1499
|
+
private readonly otel;
|
|
1500
|
+
private readonly serviceName;
|
|
1501
|
+
private readonly serverEndpoint;
|
|
1502
|
+
constructor(messageProvider: MessageProvider, patternRegistry: PatternRegistry, codec: Codec, eventBus: EventBus, deadLetterConfig?: DeadLetterConfig | undefined, processingConfig?: EventProcessingConfig | undefined, ackWaitMap?: Map<StreamKind, number> | undefined, connection?: ConnectionProvider | undefined, options?: JetstreamModuleOptions | undefined);
|
|
1127
1503
|
/**
|
|
1128
|
-
*
|
|
1504
|
+
* Update the max_deliver thresholds from actual NATS consumer configs.
|
|
1505
|
+
* Called after consumers are ensured so the DLQ map reflects reality.
|
|
1506
|
+
*/
|
|
1507
|
+
updateMaxDeliverMap(consumerMaxDelivers: Map<string, number>): void;
|
|
1508
|
+
/** Start routing event, broadcast, and ordered messages to handlers. */
|
|
1509
|
+
start(): void;
|
|
1510
|
+
/** Stop routing and unsubscribe from all streams. */
|
|
1511
|
+
destroy(): void;
|
|
1512
|
+
/** Subscribe to a message stream and route each message to its handler. */
|
|
1513
|
+
private subscribeToStream;
|
|
1514
|
+
private getConcurrency;
|
|
1515
|
+
private getAckExtensionConfig;
|
|
1516
|
+
/**
|
|
1517
|
+
* Last-resort path for a dead letter: invoke `onDeadLetter`, then `term` on
|
|
1518
|
+
* success or `nak` on hook failure so NATS retries on the next delivery
|
|
1519
|
+
* cycle. Used when DLQ stream isn't configured, or when publishing to it
|
|
1520
|
+
* failed and we still have to surface the message somewhere observable.
|
|
1521
|
+
*/
|
|
1522
|
+
private fallbackToOnDeadLetterCallback;
|
|
1523
|
+
/**
|
|
1524
|
+
* Publish a dead letter to the configured Dead-Letter Queue (DLQ) stream.
|
|
1129
1525
|
*
|
|
1130
|
-
*
|
|
1526
|
+
* Appends diagnostic metadata headers to the original message and preserves
|
|
1527
|
+
* the primary payload. If publishing succeeds, it notifies the standard
|
|
1528
|
+
* `onDeadLetter` callback and terminates the message. If it fails, it falls
|
|
1529
|
+
* back to the callback entirely to prevent silent data loss.
|
|
1530
|
+
*/
|
|
1531
|
+
private publishToDlq;
|
|
1532
|
+
/**
|
|
1533
|
+
* Orchestrates the handling of a message that has exhausted delivery limits.
|
|
1131
1534
|
*
|
|
1132
|
-
*
|
|
1535
|
+
* Emits a system event and delegates either to the robust DLQ stream publisher
|
|
1536
|
+
* or directly to the fallback callback based on the active module configuration.
|
|
1133
1537
|
*/
|
|
1134
|
-
|
|
1135
|
-
private doShutdown;
|
|
1538
|
+
private handleDeadLetter;
|
|
1136
1539
|
}
|
|
1137
1540
|
|
|
1138
1541
|
/**
|
|
1139
|
-
*
|
|
1140
|
-
*
|
|
1141
|
-
* - `forRoot()` / `forRootAsync()` — registers once in AppModule.
|
|
1142
|
-
* Creates shared NATS connection, codec, event bus, and optionally
|
|
1143
|
-
* the consumer infrastructure.
|
|
1542
|
+
* Routes RPC command messages in JetStream mode.
|
|
1144
1543
|
*
|
|
1145
|
-
*
|
|
1146
|
-
*
|
|
1544
|
+
* Delivery semantics:
|
|
1545
|
+
* - Handler must complete within timeout (default: 3 min)
|
|
1546
|
+
* - Success -> ack -> publish response to ReplyTo (publish failure does not affect ack)
|
|
1547
|
+
* - Handler error -> publish error to ReplyTo -> term (no redelivery)
|
|
1548
|
+
* - Timeout -> no response -> term
|
|
1549
|
+
* - No handler / decode error -> term immediately
|
|
1147
1550
|
*
|
|
1148
|
-
*
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1551
|
+
* Nak is never used for RPC — prevents duplicate side effects.
|
|
1552
|
+
*/
|
|
1553
|
+
declare class RpcRouter {
|
|
1554
|
+
private readonly messageProvider;
|
|
1555
|
+
private readonly patternRegistry;
|
|
1556
|
+
private readonly connection;
|
|
1557
|
+
private readonly codec;
|
|
1558
|
+
private readonly eventBus;
|
|
1559
|
+
private readonly rpcOptions?;
|
|
1560
|
+
private readonly ackWaitMap?;
|
|
1561
|
+
private readonly logger;
|
|
1562
|
+
private readonly timeout;
|
|
1563
|
+
private readonly concurrency;
|
|
1564
|
+
private resolvedAckExtensionInterval;
|
|
1565
|
+
private subscription;
|
|
1566
|
+
private cachedNc;
|
|
1567
|
+
private readonly otel;
|
|
1568
|
+
private readonly serviceName;
|
|
1569
|
+
private readonly serverEndpoint;
|
|
1570
|
+
constructor(messageProvider: MessageProvider, patternRegistry: PatternRegistry, connection: ConnectionProvider, codec: Codec, eventBus: EventBus, rpcOptions?: RpcRouterOptions | undefined, ackWaitMap?: Map<StreamKind, number> | undefined, options?: JetstreamModuleOptions);
|
|
1571
|
+
/** Lazily resolve the ack extension interval (needs ackWaitMap populated at runtime). */
|
|
1572
|
+
private get ackExtensionInterval();
|
|
1573
|
+
/** Start routing command messages to handlers. */
|
|
1574
|
+
start(): Promise<void>;
|
|
1575
|
+
/** Stop routing and unsubscribe. */
|
|
1576
|
+
destroy(): void;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
/**
|
|
1580
|
+
* Manages JetStream consumer lifecycle: creation and idempotent ensures.
|
|
1160
1581
|
*
|
|
1161
|
-
*
|
|
1162
|
-
*
|
|
1163
|
-
* imports: [
|
|
1164
|
-
* JetstreamModule.forFeature({ name: 'users' }),
|
|
1165
|
-
* JetstreamModule.forFeature({ name: 'payments' }),
|
|
1166
|
-
* ],
|
|
1167
|
-
* })
|
|
1168
|
-
* export class OrdersModule {}
|
|
1169
|
-
* ```
|
|
1582
|
+
* Creates durable pull-based consumers that survive restarts.
|
|
1583
|
+
* Consumer configuration is merged from defaults and user overrides.
|
|
1170
1584
|
*/
|
|
1171
|
-
declare class
|
|
1172
|
-
private readonly
|
|
1173
|
-
private readonly
|
|
1174
|
-
|
|
1585
|
+
declare class ConsumerProvider {
|
|
1586
|
+
private readonly options;
|
|
1587
|
+
private readonly connection;
|
|
1588
|
+
private readonly streamProvider;
|
|
1589
|
+
private readonly patternRegistry;
|
|
1590
|
+
private readonly logger;
|
|
1591
|
+
private readonly otel;
|
|
1592
|
+
private readonly otelServiceName;
|
|
1593
|
+
private readonly otelEndpoint;
|
|
1594
|
+
constructor(options: JetstreamModuleOptions, connection: ConnectionProvider, streamProvider: StreamProvider, patternRegistry: PatternRegistry);
|
|
1175
1595
|
/**
|
|
1176
|
-
*
|
|
1177
|
-
*
|
|
1178
|
-
* Creates a shared NATS connection, codec, event bus, and optionally
|
|
1179
|
-
* the full consumer infrastructure (streams, consumers, routers).
|
|
1596
|
+
* Ensure consumers exist for the specified kinds.
|
|
1180
1597
|
*
|
|
1181
|
-
* @
|
|
1182
|
-
* @returns Dynamic module ready to be imported.
|
|
1598
|
+
* @returns Map of kind -> ConsumerInfo for downstream use.
|
|
1183
1599
|
*/
|
|
1184
|
-
|
|
1600
|
+
ensureConsumers(kinds: StreamKind[]): Promise<Map<StreamKind, ConsumerInfo>>;
|
|
1601
|
+
/** Get the consumer name for a given kind. */
|
|
1602
|
+
getConsumerName(kind: StreamKind): string;
|
|
1185
1603
|
/**
|
|
1186
|
-
*
|
|
1187
|
-
*
|
|
1188
|
-
*
|
|
1189
|
-
* for loading configuration from ConfigService, environment, etc.
|
|
1190
|
-
*
|
|
1191
|
-
* @param asyncOptions Async configuration.
|
|
1192
|
-
* @returns Dynamic module ready to be imported.
|
|
1604
|
+
* Ensure a single consumer exists with the desired config.
|
|
1605
|
+
* Used at **startup** — creates or updates the consumer to match
|
|
1606
|
+
* the current pod's configuration.
|
|
1193
1607
|
*/
|
|
1194
|
-
|
|
1608
|
+
ensureConsumer(jsm: Awaited<ReturnType<ConnectionProvider['getJetStreamManager']>>, kind: StreamKind): Promise<ConsumerInfo>;
|
|
1195
1609
|
/**
|
|
1196
|
-
*
|
|
1610
|
+
* Recover a consumer that disappeared during runtime.
|
|
1611
|
+
* Used by **self-healing** — creates if missing, but NEVER updates config.
|
|
1197
1612
|
*
|
|
1198
|
-
*
|
|
1199
|
-
*
|
|
1613
|
+
* If a migration backup stream exists, another pod is mid-migration — we
|
|
1614
|
+
* throw so the self-healing retry loop waits with backoff until migration
|
|
1615
|
+
* completes and the backup is cleaned up.
|
|
1200
1616
|
*
|
|
1201
|
-
*
|
|
1202
|
-
*
|
|
1617
|
+
* This prevents old pods from:
|
|
1618
|
+
* - Overwriting a newer pod's consumer config during rolling updates
|
|
1619
|
+
* - Creating consumers during migration (which would consume and delete
|
|
1620
|
+
* workqueue messages while they're being restored)
|
|
1203
1621
|
*/
|
|
1204
|
-
|
|
1205
|
-
private static createCoreProviders;
|
|
1206
|
-
/** Create providers that depend on JETSTREAM_OPTIONS (shared by sync and async). */
|
|
1207
|
-
private static createCoreDependentProviders;
|
|
1208
|
-
/** Create async options provider from useFactory/useExisting/useClass. */
|
|
1209
|
-
private static createAsyncOptionsProvider;
|
|
1622
|
+
recoverConsumer(jsm: Awaited<ReturnType<ConnectionProvider['getJetStreamManager']>>, kind: StreamKind): Promise<ConsumerInfo>;
|
|
1210
1623
|
/**
|
|
1211
|
-
*
|
|
1624
|
+
* Throw if a migration backup stream exists for this stream.
|
|
1625
|
+
* The self-healing retry loop catches the error and retries with backoff,
|
|
1626
|
+
* naturally waiting until the migrating pod finishes and cleans up the backup.
|
|
1212
1627
|
*/
|
|
1213
|
-
|
|
1628
|
+
private assertNoMigrationInProgress;
|
|
1629
|
+
/**
|
|
1630
|
+
* Create a consumer, handling the race where another pod creates it first.
|
|
1631
|
+
*/
|
|
1632
|
+
private createConsumer;
|
|
1633
|
+
/** Build consumer config by merging defaults with user overrides. */
|
|
1634
|
+
private buildConfig;
|
|
1635
|
+
/** Get default config for a consumer kind. */
|
|
1636
|
+
private getDefaults;
|
|
1637
|
+
/** Get user-provided overrides for a consumer kind. */
|
|
1638
|
+
private getOverrides;
|
|
1214
1639
|
}
|
|
1215
1640
|
|
|
1641
|
+
/** Callback to recreate a consumer when it disappears. */
|
|
1642
|
+
type ConsumerRecoveryFn = (kind: StreamKind) => Promise<ConsumerInfo>;
|
|
1216
1643
|
/**
|
|
1217
|
-
*
|
|
1218
|
-
*
|
|
1219
|
-
* Supports two operational modes:
|
|
1220
|
-
* - **Core mode** (default): Uses `nc.request()` for RPC, `nc.publish()` for events.
|
|
1221
|
-
* - **JetStream mode**: Uses `js.publish()` for RPC commands + inbox for responses.
|
|
1644
|
+
* Manages pull-based message consumption from JetStream consumers.
|
|
1222
1645
|
*
|
|
1223
|
-
*
|
|
1224
|
-
*
|
|
1646
|
+
* Uses `defer()` + `repeat()` pattern for self-healing: when the async
|
|
1647
|
+
* iterator completes (e.g., NATS restart), the consumer automatically
|
|
1648
|
+
* re-establishes after a short delay.
|
|
1225
1649
|
*
|
|
1226
|
-
*
|
|
1650
|
+
* Emits messages to kind-specific RxJS subjects for downstream routing.
|
|
1227
1651
|
*/
|
|
1228
|
-
declare class
|
|
1229
|
-
private readonly rootOptions;
|
|
1652
|
+
declare class MessageProvider {
|
|
1230
1653
|
private readonly connection;
|
|
1231
|
-
private readonly codec;
|
|
1232
1654
|
private readonly eventBus;
|
|
1655
|
+
private readonly consumeOptionsMap;
|
|
1656
|
+
private readonly consumerRecoveryFn?;
|
|
1233
1657
|
private readonly logger;
|
|
1234
|
-
|
|
1235
|
-
private
|
|
1236
|
-
|
|
1237
|
-
private
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
private readonly isCoreMode;
|
|
1252
|
-
private readonly defaultRpcTimeout;
|
|
1253
|
-
/** Shared inbox for JetStream-mode RPC responses. */
|
|
1254
|
-
private inbox;
|
|
1255
|
-
private inboxSubscription;
|
|
1256
|
-
/** Pending JetStream-mode RPC callbacks, keyed by correlation ID. */
|
|
1257
|
-
private readonly pendingMessages;
|
|
1258
|
-
/** Pending JetStream-mode RPC timeouts, keyed by correlation ID. */
|
|
1259
|
-
private readonly pendingTimeouts;
|
|
1260
|
-
/** Subscription to connection status events for disconnect handling. */
|
|
1261
|
-
private statusSubscription;
|
|
1658
|
+
private readonly activeIterators;
|
|
1659
|
+
private orderedReadyResolve;
|
|
1660
|
+
private orderedReadyReject;
|
|
1661
|
+
private destroy$;
|
|
1662
|
+
private eventMessages$;
|
|
1663
|
+
private commandMessages$;
|
|
1664
|
+
private broadcastMessages$;
|
|
1665
|
+
private orderedMessages$;
|
|
1666
|
+
constructor(connection: ConnectionProvider, eventBus: EventBus, consumeOptionsMap?: Map<StreamKind, Partial<ConsumeOptions>>, consumerRecoveryFn?: ConsumerRecoveryFn | undefined);
|
|
1667
|
+
/** Observable stream of workqueue event messages. */
|
|
1668
|
+
get events$(): Observable<JsMsg>;
|
|
1669
|
+
/** Observable stream of RPC command messages (jetstream mode). */
|
|
1670
|
+
get commands$(): Observable<JsMsg>;
|
|
1671
|
+
/** Observable stream of broadcast event messages. */
|
|
1672
|
+
get broadcasts$(): Observable<JsMsg>;
|
|
1673
|
+
/** Observable stream of ordered event messages (strict sequential delivery). */
|
|
1674
|
+
get ordered$(): Observable<JsMsg>;
|
|
1262
1675
|
/**
|
|
1263
|
-
*
|
|
1264
|
-
*
|
|
1265
|
-
*
|
|
1676
|
+
* Start consuming messages from the given consumer infos.
|
|
1677
|
+
*
|
|
1678
|
+
* Each consumer gets an independent self-healing flow.
|
|
1679
|
+
* Call `destroy()` to stop all consumers.
|
|
1266
1680
|
*/
|
|
1267
|
-
|
|
1268
|
-
constructor(rootOptions: JetstreamModuleOptions, targetServiceName: string, connection: ConnectionProvider, codec: Codec, eventBus: EventBus);
|
|
1681
|
+
start(consumers: Map<StreamKind, ConsumerInfo>): void;
|
|
1269
1682
|
/**
|
|
1270
|
-
*
|
|
1683
|
+
* Start an ordered consumer for strict sequential delivery.
|
|
1271
1684
|
*
|
|
1272
|
-
*
|
|
1273
|
-
*
|
|
1685
|
+
* Unlike durable consumers, ordered consumers are ephemeral — created at
|
|
1686
|
+
* consumption time, no durable state. nats.js handles auto-recreation.
|
|
1274
1687
|
*
|
|
1275
|
-
* @
|
|
1688
|
+
* @param streamName - JetStream stream to consume from.
|
|
1689
|
+
* @param filterSubjects - NATS subjects to filter on.
|
|
1690
|
+
* @param orderedConfig - Optional overrides for ordered consumer options.
|
|
1276
1691
|
*/
|
|
1277
|
-
|
|
1278
|
-
/**
|
|
1279
|
-
|
|
1692
|
+
startOrdered(streamName: string, filterSubjects: string[], orderedConfig?: OrderedEventOverrides): Promise<void>;
|
|
1693
|
+
/** Stop all consumer flows and reinitialize subjects for potential restart. */
|
|
1694
|
+
destroy(): void;
|
|
1695
|
+
/** Create a self-healing consumer flow for a specific kind. */
|
|
1696
|
+
private createFlow;
|
|
1697
|
+
/** Single iteration: get consumer -> pull messages -> emit to subject. */
|
|
1698
|
+
private consumeOnce;
|
|
1280
1699
|
/**
|
|
1281
|
-
*
|
|
1700
|
+
* Detect "consumer not found" errors from `js.consumers.get()`.
|
|
1282
1701
|
*
|
|
1283
|
-
*
|
|
1702
|
+
* Unlike JetStream Manager calls (which throw `JetStreamApiError`),
|
|
1703
|
+
* the JetStream client's `consumers.get()` throws a plain `Error`
|
|
1704
|
+
* with the error code embedded in the message text.
|
|
1284
1705
|
*/
|
|
1285
|
-
|
|
1706
|
+
private isConsumerNotFound;
|
|
1707
|
+
/** Get the target subject for a consumer kind. */
|
|
1708
|
+
private getTargetSubject;
|
|
1709
|
+
/** Monitor heartbeats and restart the consumer iterator on prolonged silence. */
|
|
1710
|
+
private monitorConsumerHealth;
|
|
1711
|
+
/** Create a self-healing ordered consumer flow. */
|
|
1712
|
+
private createOrderedFlow;
|
|
1713
|
+
/** Shared self-healing flow: defer -> retry with exponential backoff on error/completion. */
|
|
1714
|
+
private createSelfHealingFlow;
|
|
1715
|
+
/** Single iteration: create ordered consumer -> push messages into the subject. */
|
|
1716
|
+
private consumeOrderedOnce;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
/**
|
|
1720
|
+
* Publishes handler metadata to a NATS KV bucket for external service discovery.
|
|
1721
|
+
*
|
|
1722
|
+
* Uses TTL + heartbeat to manage entry lifecycle:
|
|
1723
|
+
* - Entries are written on startup and refreshed every `ttl / 2`
|
|
1724
|
+
* - When the pod stops (graceful or crash), heartbeat stops → entries expire via TTL
|
|
1725
|
+
* - No explicit delete needed — NATS handles expiry automatically
|
|
1726
|
+
*
|
|
1727
|
+
* This provider is fully decoupled from stream/consumer infrastructure —
|
|
1728
|
+
* it only depends on the NATS connection and module options.
|
|
1729
|
+
*/
|
|
1730
|
+
declare class MetadataProvider {
|
|
1731
|
+
private readonly connection;
|
|
1732
|
+
private readonly logger;
|
|
1733
|
+
private readonly bucketName;
|
|
1734
|
+
private readonly replicas;
|
|
1735
|
+
private readonly ttl;
|
|
1736
|
+
private currentEntries?;
|
|
1737
|
+
private heartbeatTimer?;
|
|
1738
|
+
private cachedKv?;
|
|
1739
|
+
constructor(options: JetstreamModuleOptions, connection: ConnectionProvider);
|
|
1286
1740
|
/**
|
|
1287
|
-
*
|
|
1741
|
+
* Write handler metadata entries to the KV bucket and start heartbeat.
|
|
1288
1742
|
*
|
|
1289
|
-
*
|
|
1290
|
-
*
|
|
1291
|
-
*
|
|
1292
|
-
*
|
|
1293
|
-
*/
|
|
1294
|
-
protected dispatchEvent<T = unknown>(packet: ReadPacket): Promise<T>;
|
|
1295
|
-
/**
|
|
1296
|
-
* Publish an RPC command and register callback for response.
|
|
1743
|
+
* Creates the bucket if it doesn't exist (idempotent).
|
|
1744
|
+
* Skips silently when entries map is empty.
|
|
1745
|
+
* Starts a heartbeat interval that refreshes entries every `ttl / 2`
|
|
1746
|
+
* to prevent TTL expiry while the pod is alive.
|
|
1297
1747
|
*
|
|
1298
|
-
*
|
|
1299
|
-
* JetStream mode: publishes to stream + waits for inbox response.
|
|
1300
|
-
*/
|
|
1301
|
-
protected publish(packet: ReadPacket, callback: (p: WritePacket) => void): () => void;
|
|
1302
|
-
/** Core mode: nc.request() with timeout. */
|
|
1303
|
-
private publishCoreRpc;
|
|
1304
|
-
/** JetStream mode: publish to stream + wait for inbox response. */
|
|
1305
|
-
private publishJetStreamRpc;
|
|
1306
|
-
/** Fail-fast all pending JetStream RPC callbacks on connection loss. */
|
|
1307
|
-
private handleDisconnect;
|
|
1308
|
-
/** Reject all pending RPC callbacks, clear timeouts, and tear down inbox. */
|
|
1309
|
-
private rejectPendingRpcs;
|
|
1310
|
-
/** Setup shared inbox subscription for JetStream RPC responses. */
|
|
1311
|
-
private setupInbox;
|
|
1312
|
-
/** Route an inbox reply to the matching pending callback. */
|
|
1313
|
-
private routeInboxReply;
|
|
1314
|
-
/**
|
|
1315
|
-
* Resolve a user pattern to a fully-qualified NATS subject, dispatching
|
|
1316
|
-
* between the event, broadcast, and ordered prefixes.
|
|
1748
|
+
* Non-critical — errors are logged but do not prevent transport startup.
|
|
1317
1749
|
*
|
|
1318
|
-
*
|
|
1319
|
-
* patterns that cannot possibly carry a broadcast/ordered marker, which is
|
|
1320
|
-
* the overwhelmingly common case.
|
|
1750
|
+
* @param entries Map of KV key → metadata object.
|
|
1321
1751
|
*/
|
|
1322
|
-
|
|
1323
|
-
/** Build NATS headers merging custom headers with transport headers. */
|
|
1324
|
-
private buildHeaders;
|
|
1325
|
-
/** Extract data, headers, timeout, and schedule from raw packet data or JetstreamRecord. */
|
|
1326
|
-
private extractRecordData;
|
|
1752
|
+
publish(entries: Map<string, Record<string, unknown>>): Promise<void>;
|
|
1327
1753
|
/**
|
|
1328
|
-
*
|
|
1329
|
-
*
|
|
1330
|
-
* The schedule-holder subject resides in the same stream as the target but
|
|
1331
|
-
* uses a separate `_sch` namespace that is NOT matched by any consumer filter.
|
|
1332
|
-
* NATS holds the message and publishes it to the target subject after the delay.
|
|
1754
|
+
* Stop the heartbeat timer.
|
|
1333
1755
|
*
|
|
1334
|
-
*
|
|
1335
|
-
*
|
|
1336
|
-
* - `broadcast.config.updated` → `broadcast._sch.config.updated`
|
|
1756
|
+
* After this call, entries will expire via TTL once the heartbeat window passes.
|
|
1757
|
+
* Called during transport shutdown (strategy.close()).
|
|
1337
1758
|
*/
|
|
1338
|
-
|
|
1759
|
+
destroy(): void;
|
|
1760
|
+
/** Write entries to KV with per-entry error handling. */
|
|
1761
|
+
private writeEntries;
|
|
1762
|
+
/** Start heartbeat interval that refreshes entries every ttl/2. */
|
|
1763
|
+
private startHeartbeat;
|
|
1764
|
+
/** Refresh all current entries in KV (heartbeat tick). */
|
|
1765
|
+
private refreshEntries;
|
|
1766
|
+
/** Create or open the KV bucket (cached after first call). */
|
|
1767
|
+
private openBucket;
|
|
1339
1768
|
}
|
|
1340
1769
|
|
|
1341
|
-
/**
|
|
1342
|
-
*
|
|
1343
|
-
*
|
|
1344
|
-
* Compatible with NestJS record builder pattern (like RmqRecord, NatsRecord).
|
|
1345
|
-
* Pass as the second argument to `client.send()` or `client.emit()`.
|
|
1346
|
-
*
|
|
1347
|
-
* @example
|
|
1348
|
-
* ```typescript
|
|
1349
|
-
* const record = new JetstreamRecordBuilder({ id: 1 })
|
|
1350
|
-
* .setHeader('x-tenant', 'acme')
|
|
1351
|
-
* .setTimeout(5000)
|
|
1352
|
-
* .build();
|
|
1770
|
+
/**
|
|
1771
|
+
* NATS JetStream API error codes used by the transport.
|
|
1353
1772
|
*
|
|
1354
|
-
*
|
|
1355
|
-
*
|
|
1773
|
+
* Ref: https://github.com/nats-io/nats-server (server error definitions)
|
|
1774
|
+
* Verified on NATS 2.12.6 via integration tests (2026-04-02).
|
|
1356
1775
|
*/
|
|
1357
|
-
declare
|
|
1358
|
-
/**
|
|
1359
|
-
|
|
1360
|
-
/**
|
|
1361
|
-
|
|
1362
|
-
/**
|
|
1363
|
-
|
|
1364
|
-
/** Custom message ID for JetStream deduplication. */
|
|
1365
|
-
readonly messageId?: string | undefined;
|
|
1366
|
-
/** Schedule options for delayed delivery. */
|
|
1367
|
-
readonly schedule?: ScheduleRecordOptions | undefined;
|
|
1368
|
-
/** Per-message TTL as Go duration string (e.g. "30s", "5m"). */
|
|
1369
|
-
readonly ttl?: string | undefined;
|
|
1370
|
-
constructor(
|
|
1371
|
-
/** Message payload. */
|
|
1372
|
-
data: TData,
|
|
1373
|
-
/** Custom headers set via {@link JetstreamRecordBuilder.setHeader}. */
|
|
1374
|
-
headers: ReadonlyMap<string, string>,
|
|
1375
|
-
/** Per-request RPC timeout override in ms. */
|
|
1376
|
-
timeout?: number | undefined,
|
|
1377
|
-
/** Custom message ID for JetStream deduplication. */
|
|
1378
|
-
messageId?: string | undefined,
|
|
1379
|
-
/** Schedule options for delayed delivery. */
|
|
1380
|
-
schedule?: ScheduleRecordOptions | undefined,
|
|
1381
|
-
/** Per-message TTL as Go duration string (e.g. "30s", "5m"). */
|
|
1382
|
-
ttl?: string | undefined);
|
|
1776
|
+
declare enum NatsErrorCode {
|
|
1777
|
+
/** Consumer does not exist on the specified stream. */
|
|
1778
|
+
ConsumerNotFound = 10014,
|
|
1779
|
+
/** Consumer name already in use with different configuration (race condition on create). */
|
|
1780
|
+
ConsumerAlreadyExists = 10148,
|
|
1781
|
+
/** Stream does not exist. */
|
|
1782
|
+
StreamNotFound = 10059
|
|
1383
1783
|
}
|
|
1784
|
+
|
|
1384
1785
|
/**
|
|
1385
|
-
*
|
|
1786
|
+
* NestJS custom transport strategy for NATS JetStream.
|
|
1386
1787
|
*
|
|
1387
|
-
*
|
|
1388
|
-
*
|
|
1788
|
+
* Coordinates all server-side providers:
|
|
1789
|
+
* 1. Registers handlers from NestJS into PatternRegistry
|
|
1790
|
+
* 2. Creates required streams and consumers
|
|
1791
|
+
* 3. Starts message consumption and routing
|
|
1792
|
+
* 4. Handles Core or JetStream RPC based on configuration
|
|
1793
|
+
*
|
|
1794
|
+
* All dependencies are injected via the NestJS DI container.
|
|
1389
1795
|
*/
|
|
1390
|
-
declare class
|
|
1391
|
-
private
|
|
1392
|
-
private readonly
|
|
1393
|
-
private
|
|
1394
|
-
private
|
|
1395
|
-
private
|
|
1396
|
-
private
|
|
1397
|
-
|
|
1796
|
+
declare class JetstreamStrategy extends Server implements CustomTransportStrategy {
|
|
1797
|
+
private readonly options;
|
|
1798
|
+
private readonly connection;
|
|
1799
|
+
private readonly patternRegistry;
|
|
1800
|
+
private readonly streamProvider;
|
|
1801
|
+
private readonly consumerProvider;
|
|
1802
|
+
private readonly messageProvider;
|
|
1803
|
+
private readonly eventRouter;
|
|
1804
|
+
private readonly rpcRouter;
|
|
1805
|
+
private readonly coreRpcServer;
|
|
1806
|
+
private readonly ackWaitMap;
|
|
1807
|
+
private readonly metadataProvider?;
|
|
1808
|
+
readonly transportId: symbol;
|
|
1809
|
+
private readonly listeners;
|
|
1810
|
+
private started;
|
|
1811
|
+
constructor(options: JetstreamModuleOptions, connection: ConnectionProvider, patternRegistry: PatternRegistry, streamProvider: StreamProvider, consumerProvider: ConsumerProvider, messageProvider: MessageProvider, eventRouter: EventRouter, rpcRouter: RpcRouter, coreRpcServer: CoreRpcServer, ackWaitMap?: Map<StreamKind, number>, metadataProvider?: MetadataProvider | undefined);
|
|
1398
1812
|
/**
|
|
1399
|
-
*
|
|
1813
|
+
* Start the transport: register handlers, create infrastructure, begin consumption.
|
|
1400
1814
|
*
|
|
1401
|
-
*
|
|
1815
|
+
* Called by NestJS when `connectMicroservice()` is used, or internally by the module.
|
|
1402
1816
|
*/
|
|
1403
|
-
|
|
1817
|
+
listen(callback: () => void): Promise<void>;
|
|
1818
|
+
/** Stop all consumers, routers, subscriptions, and metadata heartbeat. Called during shutdown. */
|
|
1819
|
+
close(): void;
|
|
1404
1820
|
/**
|
|
1405
|
-
*
|
|
1821
|
+
* Override NestJS `Server.addHandler` to fail-fast on duplicate pattern registration.
|
|
1406
1822
|
*
|
|
1407
|
-
*
|
|
1408
|
-
*
|
|
1409
|
-
*
|
|
1823
|
+
* The base class silently overwrites duplicate RPC handlers (last wins) and appends
|
|
1824
|
+
* duplicate event handlers to a linked list. Both behaviors are hazardous in a
|
|
1825
|
+
* JetStream context: silent overwrite drops a handler the user wrote, and double
|
|
1826
|
+
* event dispatch double-acks/double-processes the same JetStream message.
|
|
1827
|
+
*
|
|
1828
|
+
* We treat any pattern collision as a fatal misconfiguration so it surfaces at
|
|
1829
|
+
* bootstrap instead of in production traffic.
|
|
1410
1830
|
*/
|
|
1411
|
-
|
|
1831
|
+
addHandler(pattern: unknown, callback: MessageHandler, isEventHandler?: boolean, extras?: Record<string, unknown>): void;
|
|
1412
1832
|
/**
|
|
1413
|
-
*
|
|
1833
|
+
* Register event listener (required by Server base class).
|
|
1414
1834
|
*
|
|
1415
|
-
*
|
|
1416
|
-
*
|
|
1835
|
+
* Stores callbacks for client use. Primary lifecycle events
|
|
1836
|
+
* are routed through EventBus.
|
|
1417
1837
|
*/
|
|
1418
|
-
|
|
1838
|
+
on(event: string, callback: Function): void;
|
|
1419
1839
|
/**
|
|
1420
|
-
*
|
|
1421
|
-
*
|
|
1422
|
-
* NATS JetStream uses this ID to detect duplicate publishes within the
|
|
1423
|
-
* stream's `duplicate_window`. If two messages with the same ID arrive
|
|
1424
|
-
* within the window, the second is silently dropped.
|
|
1425
|
-
*
|
|
1426
|
-
* When not set, a random UUID is generated automatically.
|
|
1427
|
-
*
|
|
1428
|
-
* @param id - Unique message identifier (e.g. order ID, idempotency key).
|
|
1840
|
+
* Unwrap the underlying NATS connection.
|
|
1429
1841
|
*
|
|
1430
|
-
* @
|
|
1431
|
-
* ```typescript
|
|
1432
|
-
* new JetstreamRecordBuilder(data)
|
|
1433
|
-
* .setMessageId(`order-${order.id}`)
|
|
1434
|
-
* .build();
|
|
1435
|
-
* ```
|
|
1842
|
+
* @throws Error if the transport has not started.
|
|
1436
1843
|
*/
|
|
1437
|
-
|
|
1844
|
+
unwrap<T>(): T;
|
|
1845
|
+
/** Access the pattern registry (for module-level introspection). */
|
|
1846
|
+
getPatternRegistry(): PatternRegistry;
|
|
1847
|
+
/** Determine which streams and durable consumers are needed. */
|
|
1848
|
+
private resolveRequiredKinds;
|
|
1849
|
+
/** Populate the shared ack_wait map from actual NATS consumer configs. */
|
|
1850
|
+
private populateAckWaitMap;
|
|
1851
|
+
/** Build max_deliver map from actual NATS consumer configs (not options). */
|
|
1852
|
+
private buildMaxDeliverMap;
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
/** Minimal interface for anything that can be stopped during shutdown. */
|
|
1856
|
+
interface Stoppable {
|
|
1857
|
+
close(): void;
|
|
1858
|
+
}
|
|
1859
|
+
/**
|
|
1860
|
+
* Orchestrates graceful transport shutdown.
|
|
1861
|
+
*
|
|
1862
|
+
* Shutdown sequence:
|
|
1863
|
+
* 1. Emit onShutdownStart hook
|
|
1864
|
+
* 2. Stop accepting new messages (close subscriptions, stop consumers)
|
|
1865
|
+
* 3. Drain and close NATS connection (with timeout safety net)
|
|
1866
|
+
* 4. Emit onShutdownComplete hook
|
|
1867
|
+
*
|
|
1868
|
+
* Idempotent — concurrent or repeated calls return the same promise.
|
|
1869
|
+
* This is critical because NestJS may call `onApplicationShutdown` on
|
|
1870
|
+
* multiple module instances (forRoot + forFeature) that share this
|
|
1871
|
+
* singleton, and the call order is not guaranteed.
|
|
1872
|
+
*/
|
|
1873
|
+
declare class ShutdownManager {
|
|
1874
|
+
private readonly connection;
|
|
1875
|
+
private readonly eventBus;
|
|
1876
|
+
private readonly timeout;
|
|
1877
|
+
private readonly logger;
|
|
1878
|
+
private shutdownPromise?;
|
|
1879
|
+
constructor(connection: ConnectionProvider, eventBus: EventBus, timeout: number);
|
|
1438
1880
|
/**
|
|
1439
|
-
*
|
|
1881
|
+
* Execute the full shutdown sequence.
|
|
1440
1882
|
*
|
|
1441
|
-
*
|
|
1883
|
+
* Idempotent — concurrent or repeated calls return the same promise.
|
|
1884
|
+
*
|
|
1885
|
+
* @param strategy Optional stoppable to close (stops consumers and subscriptions).
|
|
1442
1886
|
*/
|
|
1443
|
-
|
|
1887
|
+
shutdown(strategy?: Stoppable): Promise<void>;
|
|
1888
|
+
private doShutdown;
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
/**
|
|
1892
|
+
* Root module for the NestJS JetStream transport.
|
|
1893
|
+
*
|
|
1894
|
+
* - `forRoot()` / `forRootAsync()` — registers once in AppModule.
|
|
1895
|
+
* Creates shared NATS connection, codec, event bus, and optionally
|
|
1896
|
+
* the consumer infrastructure.
|
|
1897
|
+
*
|
|
1898
|
+
* - `forFeature()` — registers in feature modules.
|
|
1899
|
+
* Creates a lightweight client proxy targeting a specific service.
|
|
1900
|
+
*
|
|
1901
|
+
* @example
|
|
1902
|
+
* ```typescript
|
|
1903
|
+
* // AppModule — global setup
|
|
1904
|
+
* @Module({
|
|
1905
|
+
* imports: [
|
|
1906
|
+
* JetstreamModule.forRoot({
|
|
1907
|
+
* name: 'orders',
|
|
1908
|
+
* servers: ['nats://localhost:4222'],
|
|
1909
|
+
* }),
|
|
1910
|
+
* ],
|
|
1911
|
+
* })
|
|
1912
|
+
* export class AppModule {}
|
|
1913
|
+
*
|
|
1914
|
+
* // Feature module — per-service clients
|
|
1915
|
+
* @Module({
|
|
1916
|
+
* imports: [
|
|
1917
|
+
* JetstreamModule.forFeature({ name: 'users' }),
|
|
1918
|
+
* JetstreamModule.forFeature({ name: 'payments' }),
|
|
1919
|
+
* ],
|
|
1920
|
+
* })
|
|
1921
|
+
* export class OrdersModule {}
|
|
1922
|
+
* ```
|
|
1923
|
+
*/
|
|
1924
|
+
declare class JetstreamModule implements OnApplicationShutdown {
|
|
1925
|
+
private readonly shutdownManager?;
|
|
1926
|
+
private readonly strategy?;
|
|
1927
|
+
constructor(shutdownManager?: ShutdownManager | undefined, strategy?: (JetstreamStrategy | null) | undefined);
|
|
1444
1928
|
/**
|
|
1445
|
-
*
|
|
1446
|
-
*
|
|
1447
|
-
* The message is held by NATS and delivered to the event consumer
|
|
1448
|
-
* at the specified time. Requires NATS >= 2.12 and `allow_msg_schedules: true`
|
|
1449
|
-
* on the event stream (via `events: { stream: { allow_msg_schedules: true } }`).
|
|
1929
|
+
* Register the JetStream transport globally.
|
|
1450
1930
|
*
|
|
1451
|
-
*
|
|
1452
|
-
*
|
|
1931
|
+
* Creates a shared NATS connection, codec, event bus, and optionally
|
|
1932
|
+
* the full consumer infrastructure (streams, consumers, routers).
|
|
1453
1933
|
*
|
|
1454
|
-
* @param
|
|
1455
|
-
* @
|
|
1934
|
+
* @param options Module configuration.
|
|
1935
|
+
* @returns Dynamic module ready to be imported.
|
|
1456
1936
|
*/
|
|
1457
|
-
|
|
1937
|
+
static forRoot(options: JetstreamModuleOptions): DynamicModule;
|
|
1458
1938
|
/**
|
|
1459
|
-
*
|
|
1460
|
-
*
|
|
1461
|
-
* The message expires individually after the specified duration,
|
|
1462
|
-
* independent of the stream's `max_age`. Requires NATS >= 2.11 and
|
|
1463
|
-
* `allow_msg_ttl: true` on the stream.
|
|
1939
|
+
* Register the JetStream transport globally with async configuration.
|
|
1464
1940
|
*
|
|
1465
|
-
*
|
|
1466
|
-
*
|
|
1941
|
+
* Supports `useFactory`, `useExisting`, and `useClass` patterns
|
|
1942
|
+
* for loading configuration from ConfigService, environment, etc.
|
|
1467
1943
|
*
|
|
1468
|
-
* @param
|
|
1944
|
+
* @param asyncOptions Async configuration.
|
|
1945
|
+
* @returns Dynamic module ready to be imported.
|
|
1946
|
+
*/
|
|
1947
|
+
static forRootAsync(asyncOptions: JetstreamModuleAsyncOptions): DynamicModule;
|
|
1948
|
+
/**
|
|
1949
|
+
* Register a lightweight client proxy for a target service.
|
|
1469
1950
|
*
|
|
1470
|
-
*
|
|
1471
|
-
*
|
|
1472
|
-
* import { toNanos } from '@horizon-republic/nestjs-jetstream';
|
|
1951
|
+
* Reuses the shared NATS connection from `forRoot()`.
|
|
1952
|
+
* Import in each feature module that needs to communicate with a specific service.
|
|
1473
1953
|
*
|
|
1474
|
-
*
|
|
1475
|
-
*
|
|
1476
|
-
* ```
|
|
1954
|
+
* @param options Feature options with target service name.
|
|
1955
|
+
* @returns Dynamic module with the client provider.
|
|
1477
1956
|
*/
|
|
1478
|
-
|
|
1957
|
+
static forFeature(options: JetstreamFeatureOptions): DynamicModule;
|
|
1958
|
+
private static createCoreProviders;
|
|
1959
|
+
/** Create providers that depend on JETSTREAM_OPTIONS (shared by sync and async). */
|
|
1960
|
+
private static createCoreDependentProviders;
|
|
1961
|
+
/** Create async options provider from useFactory/useExisting/useClass. */
|
|
1962
|
+
private static createAsyncOptionsProvider;
|
|
1479
1963
|
/**
|
|
1480
|
-
*
|
|
1481
|
-
*
|
|
1482
|
-
* @returns A frozen record ready to pass to `client.send()` or `client.emit()`.
|
|
1964
|
+
* Gracefully shut down the transport on application termination.
|
|
1483
1965
|
*/
|
|
1484
|
-
|
|
1485
|
-
/** Validate that a header key is not reserved. */
|
|
1486
|
-
private validateHeaderKey;
|
|
1966
|
+
onApplicationShutdown(): Promise<void>;
|
|
1487
1967
|
}
|
|
1488
1968
|
|
|
1489
1969
|
/**
|
|
@@ -1873,4 +2353,4 @@ declare const isJetStreamRpcMode: (rpc: RpcConfig | undefined) => boolean;
|
|
|
1873
2353
|
/** Check if the RPC config specifies Core mode (default). */
|
|
1874
2354
|
declare const isCoreRpcMode: (rpc: RpcConfig | undefined) => boolean;
|
|
1875
2355
|
|
|
1876
|
-
export { type Codec, DEFAULT_BROADCAST_CONSUMER_CONFIG, DEFAULT_BROADCAST_STREAM_CONFIG, DEFAULT_COMMAND_CONSUMER_CONFIG, DEFAULT_COMMAND_STREAM_CONFIG, DEFAULT_DLQ_STREAM_CONFIG, DEFAULT_EVENT_CONSUMER_CONFIG, DEFAULT_EVENT_STREAM_CONFIG, DEFAULT_JETSTREAM_RPC_TIMEOUT, DEFAULT_METADATA_BUCKET, DEFAULT_METADATA_HISTORY, DEFAULT_METADATA_REPLICAS, DEFAULT_METADATA_TTL, DEFAULT_ORDERED_STREAM_CONFIG, DEFAULT_RPC_TIMEOUT, DEFAULT_SHUTDOWN_TIMEOUT, type DeadLetterInfo, JETSTREAM_CODEC, JETSTREAM_CONNECTION, JETSTREAM_OPTIONS, JetstreamClient, JetstreamDlqHeader, type JetstreamFeatureOptions, JetstreamHeader, JetstreamHealthIndicator, type JetstreamHealthStatus, JetstreamModule, type JetstreamModuleAsyncOptions, type JetstreamModuleOptions, JetstreamRecord, JetstreamRecordBuilder, JetstreamStrategy, JsonCodec, MIN_METADATA_TTL, MessageKind, type MetadataRegistryOptions, MsgpackCodec, NatsErrorCode, type OrderedEventOverrides, PatternPrefix, RESERVED_HEADERS, type RpcConfig, RpcContext, type ScheduleRecordOptions, type StreamConfigOverrides, type StreamConsumerOverrides, StreamKind, TransportEvent, type TransportHooks, buildBroadcastSubject, buildSubject, consumerName, dlqStreamName, getClientToken, internalName, isCoreRpcMode, isJetStreamRpcMode, metadataKey, streamName, toNanos };
|
|
2356
|
+
export { type CaptureBodyOptions, type Codec, ConsumeKind, type ConsumeSourceMsg, DEFAULT_BROADCAST_CONSUMER_CONFIG, DEFAULT_BROADCAST_STREAM_CONFIG, DEFAULT_COMMAND_CONSUMER_CONFIG, DEFAULT_COMMAND_STREAM_CONFIG, DEFAULT_DLQ_STREAM_CONFIG, DEFAULT_EVENT_CONSUMER_CONFIG, DEFAULT_EVENT_STREAM_CONFIG, DEFAULT_JETSTREAM_RPC_TIMEOUT, DEFAULT_METADATA_BUCKET, DEFAULT_METADATA_HISTORY, DEFAULT_METADATA_REPLICAS, DEFAULT_METADATA_TTL, DEFAULT_ORDERED_STREAM_CONFIG, DEFAULT_RPC_TIMEOUT, DEFAULT_SHUTDOWN_TIMEOUT, DEFAULT_TRACES, type DeadLetterInfo, type ErrorClassification, type HandlerMetadata, JETSTREAM_CODEC, JETSTREAM_CONNECTION, JETSTREAM_OPTIONS, JetstreamClient, type JetstreamConsumeContext, JetstreamDlqHeader, type JetstreamFeatureOptions, JetstreamHeader, JetstreamHealthIndicator, type JetstreamHealthStatus, JetstreamModule, type JetstreamModuleAsyncOptions, type JetstreamModuleOptions, type JetstreamPublishContext, JetstreamRecord, JetstreamRecordBuilder, type JetstreamResponseContext, JetstreamStrategy, JetstreamTrace, JsonCodec, MIN_METADATA_TTL, MessageKind, type MetadataRegistryOptions, MsgpackCodec, NatsErrorCode, type OrderedEventOverrides, type OtelOptions, PatternPrefix, PublishKind, RESERVED_HEADERS, type RpcConfig, RpcContext, type ScheduleRecordOptions, type ServerEndpoint, type StreamConfigOverrides, type StreamConsumerOverrides, StreamKind, TRACER_NAME, TransportEvent, type TransportHooks, buildBroadcastSubject, buildSubject, consumerName, dlqStreamName, getClientToken, internalName, isCoreRpcMode, isJetStreamRpcMode, metadataKey, streamName, toNanos };
|