@horizon-republic/nestjs-jetstream 2.10.0 → 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 +985 -290
- package/dist/index.d.cts +214 -45
- package/dist/index.d.ts +214 -45
- package/dist/index.js +970 -281
- package/package.json +25 -20
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Logger, ModuleMetadata, FactoryProvider, Type, OnApplicationShutdown, DynamicModule } from '@nestjs/common';
|
|
2
2
|
import { MsgHdrs, NatsConnection, Status, ConnectionOptions, Msg } from '@nats-io/transport-node';
|
|
3
3
|
import { JetStreamManager, JetStreamClient, StreamConfig, ConsumerConfig, ConsumeOptions, DeliverPolicy, ReplayPolicy, ConsumerInfo, JsMsg } from '@nats-io/jetstream';
|
|
4
|
+
import { Registry } from 'prom-client';
|
|
4
5
|
import { Span } from '@opentelemetry/api';
|
|
5
6
|
import { ClientProxy, ReadPacket, WritePacket, MessageHandler, Server, CustomTransportStrategy, BaseRpcContext } from '@nestjs/microservices';
|
|
6
7
|
import { Observable } from 'rxjs';
|
|
@@ -33,11 +34,47 @@ interface Codec {
|
|
|
33
34
|
decode(data: Uint8Array): unknown;
|
|
34
35
|
}
|
|
35
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
|
+
|
|
36
59
|
/** Discriminates the kind of message routed through the transport. */
|
|
37
60
|
declare enum MessageKind {
|
|
38
61
|
Event = "event",
|
|
39
62
|
Rpc = "rpc"
|
|
40
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';
|
|
41
78
|
declare enum TransportEvent {
|
|
42
79
|
Connect = "connect",
|
|
43
80
|
Disconnect = "disconnect",
|
|
@@ -47,7 +84,11 @@ declare enum TransportEvent {
|
|
|
47
84
|
MessageRouted = "messageRouted",
|
|
48
85
|
ShutdownStart = "shutdownStart",
|
|
49
86
|
ShutdownComplete = "shutdownComplete",
|
|
50
|
-
DeadLetter = "deadLetter"
|
|
87
|
+
DeadLetter = "deadLetter",
|
|
88
|
+
ConsumerRecovered = "consumerRecovered",
|
|
89
|
+
HandlerCompleted = "handlerCompleted",
|
|
90
|
+
Published = "published",
|
|
91
|
+
RpcCompleted = "rpcCompleted"
|
|
51
92
|
}
|
|
52
93
|
/**
|
|
53
94
|
* Hook callbacks for transport lifecycle and operational events.
|
|
@@ -85,7 +126,59 @@ interface TransportHooks {
|
|
|
85
126
|
[TransportEvent.ShutdownComplete](): void;
|
|
86
127
|
/** Fired when a message exhausts all delivery attempts (dead letter). */
|
|
87
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;
|
|
88
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;
|
|
89
182
|
/**
|
|
90
183
|
* Context passed to the onDeadLetter callback when a message exhausts all delivery attempts.
|
|
91
184
|
*/
|
|
@@ -118,6 +211,49 @@ interface JetstreamHealthStatus {
|
|
|
118
211
|
latency: number | null;
|
|
119
212
|
}
|
|
120
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Labels (in seconds) for histogram bucket configuration.
|
|
216
|
+
* Override defaults via `JetstreamModuleOptions.metrics.buckets`.
|
|
217
|
+
*/
|
|
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
|
+
}
|
|
226
|
+
/**
|
|
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
|
+
|
|
121
257
|
/**
|
|
122
258
|
* Instrumentation scope name reported on every span the library emits.
|
|
123
259
|
* Matches the npm package name — the convention used by
|
|
@@ -188,31 +324,27 @@ declare const DEFAULT_TRACES: readonly JetstreamTrace[];
|
|
|
188
324
|
/**
|
|
189
325
|
* Central event bus for transport lifecycle notifications.
|
|
190
326
|
*
|
|
191
|
-
*
|
|
192
|
-
* registered
|
|
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.
|
|
193
331
|
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
* const bus = new EventBus(logger, {
|
|
197
|
-
* [TransportEvent.Error]: (err) => sentry.captureException(err),
|
|
198
|
-
* });
|
|
199
|
-
*
|
|
200
|
-
* bus.emit(TransportEvent.Error, new Error('timeout'), 'rpc-router');
|
|
201
|
-
* // → calls sentry
|
|
202
|
-
*
|
|
203
|
-
* bus.emit(TransportEvent.Connect, 'nats://localhost:4222');
|
|
204
|
-
* // → no-op (no hook registered)
|
|
205
|
-
* ```
|
|
332
|
+
* Both fire on every `emit()` call. Subscriber failures are isolated and
|
|
333
|
+
* logged; they do not block other subscribers or the user hook.
|
|
206
334
|
*/
|
|
207
335
|
declare class EventBus {
|
|
208
336
|
private readonly hooks;
|
|
209
337
|
private readonly logger;
|
|
338
|
+
private readonly subscribers;
|
|
210
339
|
constructor(logger: Logger, hooks?: Partial<TransportHooks>);
|
|
211
340
|
/**
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
|
|
215
|
-
|
|
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.
|
|
343
|
+
*/
|
|
344
|
+
subscribe<K extends keyof TransportHooks>(event: K, handler: TransportEventSubscriber<K>): void;
|
|
345
|
+
/**
|
|
346
|
+
* Emit a lifecycle event. Dispatches to all internal subscribers and the
|
|
347
|
+
* registered user hook (if any).
|
|
216
348
|
*/
|
|
217
349
|
emit<K extends keyof TransportHooks>(event: K, ...args: Parameters<TransportHooks[K]>): void;
|
|
218
350
|
/**
|
|
@@ -221,12 +353,12 @@ declare class EventBus {
|
|
|
221
353
|
*/
|
|
222
354
|
emitMessageRouted(subject: string, kind: MessageKind): void;
|
|
223
355
|
/**
|
|
224
|
-
* Check whether
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
* transport owner did not register a listener.
|
|
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.
|
|
228
359
|
*/
|
|
229
360
|
hasHook(event: keyof TransportHooks): boolean;
|
|
361
|
+
private dispatch;
|
|
230
362
|
private callHook;
|
|
231
363
|
}
|
|
232
364
|
|
|
@@ -391,6 +523,8 @@ declare class JetstreamClient extends ClientProxy {
|
|
|
391
523
|
private publishCoreRpc;
|
|
392
524
|
/** JetStream mode: publish to stream + wait for inbox response. */
|
|
393
525
|
private publishJetStreamRpc;
|
|
526
|
+
private reportPublished;
|
|
527
|
+
private reportRpcCompleted;
|
|
394
528
|
/** Fail-fast all pending JetStream RPC callbacks on connection loss. */
|
|
395
529
|
private handleDisconnect;
|
|
396
530
|
/** Reject all pending RPC callbacks, clear timeouts, and tear down inbox. */
|
|
@@ -1069,6 +1203,30 @@ interface JetstreamModuleOptions {
|
|
|
1069
1203
|
* Merged with `name` and `servers` — those take precedence.
|
|
1070
1204
|
*/
|
|
1071
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
|
+
* ```
|
|
1228
|
+
*/
|
|
1229
|
+
metrics?: MetricsOption;
|
|
1072
1230
|
/**
|
|
1073
1231
|
* OpenTelemetry integration. When omitted, sensible defaults are applied:
|
|
1074
1232
|
* tracing is enabled, default trace kinds are emitted, only standard
|
|
@@ -1100,6 +1258,15 @@ type JetstreamModuleAsyncOptions = {
|
|
|
1100
1258
|
name: string;
|
|
1101
1259
|
/** Additional module imports (e.g., ConfigModule). */
|
|
1102
1260
|
imports?: ModuleMetadata['imports'];
|
|
1261
|
+
/**
|
|
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.
|
|
1266
|
+
*
|
|
1267
|
+
* @see JetstreamModuleOptions.metrics
|
|
1268
|
+
*/
|
|
1269
|
+
metrics?: MetricsOption;
|
|
1103
1270
|
} & ({
|
|
1104
1271
|
useFactory(...args: unknown[]): Promise<Omit<JetstreamModuleOptions, 'name'>> | Omit<JetstreamModuleOptions, 'name'>;
|
|
1105
1272
|
inject?: FactoryProvider['inject'];
|
|
@@ -1117,28 +1284,6 @@ type JetstreamModuleAsyncOptions = {
|
|
|
1117
1284
|
useExisting?: never;
|
|
1118
1285
|
});
|
|
1119
1286
|
|
|
1120
|
-
/**
|
|
1121
|
-
* Identifies a JetStream stream/consumer kind.
|
|
1122
|
-
*
|
|
1123
|
-
* - `Event` — Workqueue events (at-least-once delivery to one consumer).
|
|
1124
|
-
* - `Command` — RPC commands (JetStream mode only).
|
|
1125
|
-
* - `Broadcast` — Broadcast events (fan-out to all consumers).
|
|
1126
|
-
* - `Ordered` — Ordered events (strict sequential delivery, Limits retention).
|
|
1127
|
-
*/
|
|
1128
|
-
declare enum StreamKind {
|
|
1129
|
-
Event = "ev",
|
|
1130
|
-
Command = "cmd",
|
|
1131
|
-
Broadcast = "broadcast",
|
|
1132
|
-
Ordered = "ordered"
|
|
1133
|
-
}
|
|
1134
|
-
/**
|
|
1135
|
-
* Subset of {@link StreamKind} used for direct subject building.
|
|
1136
|
-
*
|
|
1137
|
-
* Excludes `Broadcast` because broadcast subjects use a different
|
|
1138
|
-
* naming convention (`broadcast.{pattern}` instead of `{service}.{kind}.{pattern}`).
|
|
1139
|
-
*/
|
|
1140
|
-
type SubjectKind = Exclude<StreamKind, StreamKind.Broadcast>;
|
|
1141
|
-
|
|
1142
1287
|
/** Options for one-shot delayed delivery via NATS 2.12 message scheduling. */
|
|
1143
1288
|
interface ScheduleRecordOptions {
|
|
1144
1289
|
/** When to deliver the message. Must be in the future. */
|
|
@@ -1211,6 +1356,17 @@ declare class PatternRegistry {
|
|
|
1211
1356
|
registerHandlers(handlers: Map<string, MessageHandler>): void;
|
|
1212
1357
|
/** Find handler for a full NATS subject. */
|
|
1213
1358
|
getHandler(subject: string): MessageHandler | null;
|
|
1359
|
+
/**
|
|
1360
|
+
* Resolve the declared pattern and {@link StreamKind} for a full NATS subject.
|
|
1361
|
+
*
|
|
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.
|
|
1365
|
+
*/
|
|
1366
|
+
resolveDeclared(subject: string): {
|
|
1367
|
+
pattern: string;
|
|
1368
|
+
kind: StreamKind;
|
|
1369
|
+
} | null;
|
|
1214
1370
|
/** Get all registered broadcast patterns (for consumer filter_subject setup). */
|
|
1215
1371
|
getBroadcastPatterns(): string[];
|
|
1216
1372
|
hasBroadcastHandlers(): boolean;
|
|
@@ -1262,6 +1418,7 @@ declare class CoreRpcServer {
|
|
|
1262
1418
|
stop(): void;
|
|
1263
1419
|
/** Handle an incoming Core NATS request. */
|
|
1264
1420
|
private handleRequest;
|
|
1421
|
+
private reportHandlerCompleted;
|
|
1265
1422
|
/** Send an error response back to the caller with x-error header. */
|
|
1266
1423
|
private respondWithError;
|
|
1267
1424
|
}
|
|
@@ -1660,6 +1817,18 @@ declare class JetstreamStrategy extends Server implements CustomTransportStrateg
|
|
|
1660
1817
|
listen(callback: () => void): Promise<void>;
|
|
1661
1818
|
/** Stop all consumers, routers, subscriptions, and metadata heartbeat. Called during shutdown. */
|
|
1662
1819
|
close(): void;
|
|
1820
|
+
/**
|
|
1821
|
+
* Override NestJS `Server.addHandler` to fail-fast on duplicate pattern registration.
|
|
1822
|
+
*
|
|
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.
|
|
1830
|
+
*/
|
|
1831
|
+
addHandler(pattern: unknown, callback: MessageHandler, isEventHandler?: boolean, extras?: Record<string, unknown>): void;
|
|
1663
1832
|
/**
|
|
1664
1833
|
* Register event listener (required by Server base class).
|
|
1665
1834
|
*
|