@horizon-republic/nestjs-jetstream 2.6.1 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -346,6 +346,13 @@ declare enum StreamKind {
346
346
  Broadcast = "broadcast",
347
347
  Ordered = "ordered"
348
348
  }
349
+ /**
350
+ * Subset of {@link StreamKind} used for direct subject building.
351
+ *
352
+ * Excludes `Broadcast` because broadcast subjects use a different
353
+ * naming convention (`broadcast.{pattern}` instead of `{service}.{kind}.{pattern}`).
354
+ */
355
+ type SubjectKind = Exclude<StreamKind, StreamKind.Broadcast>;
349
356
 
350
357
  /** @internal Grouped pattern lists by stream kind, used for stream/consumer setup. */
351
358
  interface PatternsByKind {
@@ -416,6 +423,12 @@ declare class EventBus {
416
423
  * @param args - Arguments matching the hook signature for this event.
417
424
  */
418
425
  emit<K extends keyof TransportHooks>(event: K, ...args: Parameters<TransportHooks[K]>): void;
426
+ /**
427
+ * Hot-path optimized emit for MessageRouted events.
428
+ * Avoids rest/spread overhead of the generic `emit()`.
429
+ */
430
+ emitMessageRouted(subject: string, kind: MessageKind): void;
431
+ private callHook;
419
432
  }
420
433
 
421
434
  /**
@@ -491,6 +504,10 @@ declare class PatternRegistry {
491
504
  private readonly logger;
492
505
  private readonly registry;
493
506
  private cachedPatterns;
507
+ private _hasEvents;
508
+ private _hasCommands;
509
+ private _hasBroadcasts;
510
+ private _hasOrdered;
494
511
  constructor(options: JetstreamModuleOptions);
495
512
  /**
496
513
  * Register all handlers from the NestJS strategy.
@@ -613,10 +630,10 @@ declare class EventRouter {
613
630
  private subscribeToStream;
614
631
  private getConcurrency;
615
632
  private getAckExtensionConfig;
616
- /** Handle a single event message: decode -> execute handler -> ack/nak. */
617
- private handle;
618
- /** Handle an ordered message: decode -> execute handler -> no ack/nak. */
619
- private handleOrdered;
633
+ /** Handle a single event message with error isolation. */
634
+ private handleSafe;
635
+ /** Handle an ordered message with error isolation. */
636
+ private handleOrderedSafe;
620
637
  /** Resolve handler, decode payload, and build context. Returns null on failure. */
621
638
  private decodeMessage;
622
639
  /** Execute handler, then ack on success or nak/dead-letter on failure. */
@@ -652,15 +669,16 @@ declare class RpcRouter {
652
669
  private readonly concurrency;
653
670
  private resolvedAckExtensionInterval;
654
671
  private subscription;
672
+ private cachedNc;
655
673
  constructor(messageProvider: MessageProvider, patternRegistry: PatternRegistry, connection: ConnectionProvider, codec: Codec, eventBus: EventBus, rpcOptions?: RpcRouterOptions | undefined, ackWaitMap?: Map<StreamKind, number> | undefined);
656
674
  /** Lazily resolve the ack extension interval (needs ackWaitMap populated at runtime). */
657
675
  private get ackExtensionInterval();
658
676
  /** Start routing command messages to handlers. */
659
- start(): void;
677
+ start(): Promise<void>;
660
678
  /** Stop routing and unsubscribe. */
661
679
  destroy(): void;
662
- /** Handle a single RPC command message. */
663
- private handle;
680
+ /** Handle a single RPC command message with error isolation. */
681
+ private handleSafe;
664
682
  /** Execute handler, publish response, settle message. */
665
683
  private executeHandler;
666
684
  }
@@ -1137,19 +1155,33 @@ type NatsMessage = JsMsg | Msg;
1137
1155
  * Execution context for RPC and event handlers.
1138
1156
  *
1139
1157
  * Provides convenient accessors for the NATS message, subject,
1140
- * and headers without needing to interact with the raw message directly.
1158
+ * headers, and JetStream metadata without needing to interact
1159
+ * with the raw message directly.
1160
+ *
1161
+ * Handlers can also control message settlement via {@link retry}
1162
+ * and {@link terminate} instead of throwing errors.
1141
1163
  *
1142
1164
  * @example
1143
1165
  * ```typescript
1144
- * @MessagePattern('get.user')
1145
- * getUser(data: GetUserDto, @Ctx() ctx: RpcContext) {
1146
- * const traceId = ctx.getHeader('x-trace-id');
1147
- * const subject = ctx.getSubject();
1148
- * return this.userService.findOne(data.id);
1166
+ * @EventPattern('order.process')
1167
+ * async handle(@Payload() data: OrderDto, @Ctx() ctx: RpcContext) {
1168
+ * if (ctx.getDeliveryCount()! >= 3) {
1169
+ * ctx.terminate('Max business retries exceeded');
1170
+ * return;
1171
+ * }
1172
+ * if (!this.isReady()) {
1173
+ * ctx.retry({ delay: 5000 });
1174
+ * return;
1175
+ * }
1176
+ * await this.process(data);
1149
1177
  * }
1150
1178
  * ```
1151
1179
  */
1152
1180
  declare class RpcContext extends BaseRpcContext<[NatsMessage]> {
1181
+ private _shouldRetry;
1182
+ private _retryDelay;
1183
+ private _shouldTerminate;
1184
+ private _terminateReason;
1153
1185
  /**
1154
1186
  * Get the underlying NATS message.
1155
1187
  *
@@ -1176,6 +1208,50 @@ declare class RpcContext extends BaseRpcContext<[NatsMessage]> {
1176
1208
  isJetStream(): this is RpcContext & {
1177
1209
  getMessage(): JsMsg;
1178
1210
  };
1211
+ /** How many times this message has been delivered. */
1212
+ getDeliveryCount(): number | undefined;
1213
+ /** The JetStream stream this message belongs to. */
1214
+ getStream(): string | undefined;
1215
+ /** The stream sequence number. */
1216
+ getSequence(): number | undefined;
1217
+ /** The message timestamp as a `Date` (derived from `info.timestampNanos`). */
1218
+ getTimestamp(): Date | undefined;
1219
+ /** The name of the service that published this message (from `x-caller-name` header). */
1220
+ getCallerName(): string | undefined;
1221
+ /**
1222
+ * Signal the transport to retry (nak) this message instead of acknowledging it.
1223
+ *
1224
+ * Use for business-level retries without throwing errors.
1225
+ * Only affects JetStream event handlers (workqueue/broadcast).
1226
+ *
1227
+ * @param opts - Optional delay in ms before redelivery.
1228
+ * @throws Error if {@link terminate} was already called.
1229
+ */
1230
+ retry(opts?: {
1231
+ delayMs?: number;
1232
+ }): void;
1233
+ /**
1234
+ * Signal the transport to permanently reject (term) this message.
1235
+ *
1236
+ * Use when a message is no longer relevant and should not be retried or sent to DLQ.
1237
+ * Only affects JetStream event handlers (workqueue/broadcast).
1238
+ *
1239
+ * @param reason - Optional reason for termination (logged by NATS).
1240
+ * @throws Error if {@link retry} was already called.
1241
+ */
1242
+ terminate(reason?: string): void;
1243
+ /** Narrow to JsMsg or return null for Core messages. Used by metadata getters. */
1244
+ private asJetStream;
1245
+ /** Ensure the message is JetStream — settlement actions are not available for Core NATS. */
1246
+ private assertJetStream;
1247
+ /** @internal */
1248
+ get shouldRetry(): boolean;
1249
+ /** @internal */
1250
+ get retryDelay(): number | undefined;
1251
+ /** @internal */
1252
+ get shouldTerminate(): boolean;
1253
+ /** @internal */
1254
+ get terminateReason(): string | undefined;
1179
1255
  }
1180
1256
 
1181
1257
  /**
@@ -1278,6 +1354,38 @@ declare enum JetstreamHeader {
1278
1354
  /** Set to `'true'` on error responses so the client can distinguish success from failure. */
1279
1355
  Error = "x-error"
1280
1356
  }
1357
+ /**
1358
+ * Build the internal service name with microservice suffix.
1359
+ *
1360
+ * @param name - Service name from `forRoot({ name })`.
1361
+ * @returns `{name}__microservice`
1362
+ */
1363
+ declare const internalName: (name: string) => string;
1364
+ /**
1365
+ * Build a fully-qualified NATS subject for workqueue events, RPC commands, or ordered events.
1366
+ *
1367
+ * @param serviceName - Target service name.
1368
+ * @param kind - Subject kind (`'ev'`, `'cmd'`, or `'ordered'`).
1369
+ * @param pattern - The message pattern (e.g. `'user.created'`).
1370
+ * @returns `{serviceName}__microservice.{kind}.{pattern}`
1371
+ */
1372
+ declare const buildSubject: (serviceName: string, kind: SubjectKind, pattern: string) => string;
1373
+ /**
1374
+ * Build the JetStream stream name for a given service and kind.
1375
+ *
1376
+ * @param serviceName - Service name from `forRoot({ name })`.
1377
+ * @param kind - Stream kind (`'ev'`, `'cmd'`, or `'broadcast'`).
1378
+ * @returns Stream name (e.g. `orders__microservice_ev-stream` or `broadcast-stream`).
1379
+ */
1380
+ declare const streamName: (serviceName: string, kind: StreamKind) => string;
1381
+ /**
1382
+ * Build the JetStream consumer name for a given service and kind.
1383
+ *
1384
+ * @param serviceName - Service name from `forRoot({ name })`.
1385
+ * @param kind - Stream kind (`'ev'`, `'cmd'`, or `'broadcast'`).
1386
+ * @returns Consumer name (e.g. `orders__microservice_ev-consumer`).
1387
+ */
1388
+ declare const consumerName: (serviceName: string, kind: StreamKind) => string;
1281
1389
  /**
1282
1390
  * Prefixes used in event patterns to route to specific stream types.
1283
1391
  * Applied by the user when emitting events (e.g. `client.emit('broadcast:config.updated', data)`).
@@ -1293,4 +1401,4 @@ declare const isJetStreamRpcMode: (rpc: RpcConfig | undefined) => boolean;
1293
1401
  /** Check if the RPC config specifies Core mode (default). */
1294
1402
  declare const isCoreRpcMode: (rpc: RpcConfig | undefined) => boolean;
1295
1403
 
1296
- export { type Codec, type DeadLetterInfo, EventBus, JETSTREAM_CODEC, JETSTREAM_CONNECTION, JETSTREAM_EVENT_BUS, JETSTREAM_OPTIONS, JetstreamClient, type JetstreamFeatureOptions, JetstreamHeader, JetstreamHealthIndicator, type JetstreamHealthStatus, JetstreamModule, type JetstreamModuleAsyncOptions, type JetstreamModuleOptions, JetstreamRecord, JetstreamRecordBuilder, JetstreamStrategy, JsonCodec, MessageKind, type OrderedEventOverrides, PatternPrefix, type RpcConfig, RpcContext, type StreamConsumerOverrides, StreamKind, TransportEvent, type TransportHooks, getClientToken, isCoreRpcMode, isJetStreamRpcMode, toNanos };
1404
+ export { type Codec, type DeadLetterInfo, EventBus, JETSTREAM_CODEC, JETSTREAM_CONNECTION, JETSTREAM_EVENT_BUS, JETSTREAM_OPTIONS, JetstreamClient, type JetstreamFeatureOptions, JetstreamHeader, JetstreamHealthIndicator, type JetstreamHealthStatus, JetstreamModule, type JetstreamModuleAsyncOptions, type JetstreamModuleOptions, JetstreamRecord, JetstreamRecordBuilder, JetstreamStrategy, JsonCodec, MessageKind, type OrderedEventOverrides, PatternPrefix, type RpcConfig, RpcContext, type StreamConsumerOverrides, StreamKind, TransportEvent, type TransportHooks, buildSubject, consumerName, getClientToken, internalName, isCoreRpcMode, isJetStreamRpcMode, streamName, toNanos };
package/dist/index.d.ts CHANGED
@@ -346,6 +346,13 @@ declare enum StreamKind {
346
346
  Broadcast = "broadcast",
347
347
  Ordered = "ordered"
348
348
  }
349
+ /**
350
+ * Subset of {@link StreamKind} used for direct subject building.
351
+ *
352
+ * Excludes `Broadcast` because broadcast subjects use a different
353
+ * naming convention (`broadcast.{pattern}` instead of `{service}.{kind}.{pattern}`).
354
+ */
355
+ type SubjectKind = Exclude<StreamKind, StreamKind.Broadcast>;
349
356
 
350
357
  /** @internal Grouped pattern lists by stream kind, used for stream/consumer setup. */
351
358
  interface PatternsByKind {
@@ -416,6 +423,12 @@ declare class EventBus {
416
423
  * @param args - Arguments matching the hook signature for this event.
417
424
  */
418
425
  emit<K extends keyof TransportHooks>(event: K, ...args: Parameters<TransportHooks[K]>): void;
426
+ /**
427
+ * Hot-path optimized emit for MessageRouted events.
428
+ * Avoids rest/spread overhead of the generic `emit()`.
429
+ */
430
+ emitMessageRouted(subject: string, kind: MessageKind): void;
431
+ private callHook;
419
432
  }
420
433
 
421
434
  /**
@@ -491,6 +504,10 @@ declare class PatternRegistry {
491
504
  private readonly logger;
492
505
  private readonly registry;
493
506
  private cachedPatterns;
507
+ private _hasEvents;
508
+ private _hasCommands;
509
+ private _hasBroadcasts;
510
+ private _hasOrdered;
494
511
  constructor(options: JetstreamModuleOptions);
495
512
  /**
496
513
  * Register all handlers from the NestJS strategy.
@@ -613,10 +630,10 @@ declare class EventRouter {
613
630
  private subscribeToStream;
614
631
  private getConcurrency;
615
632
  private getAckExtensionConfig;
616
- /** Handle a single event message: decode -> execute handler -> ack/nak. */
617
- private handle;
618
- /** Handle an ordered message: decode -> execute handler -> no ack/nak. */
619
- private handleOrdered;
633
+ /** Handle a single event message with error isolation. */
634
+ private handleSafe;
635
+ /** Handle an ordered message with error isolation. */
636
+ private handleOrderedSafe;
620
637
  /** Resolve handler, decode payload, and build context. Returns null on failure. */
621
638
  private decodeMessage;
622
639
  /** Execute handler, then ack on success or nak/dead-letter on failure. */
@@ -652,15 +669,16 @@ declare class RpcRouter {
652
669
  private readonly concurrency;
653
670
  private resolvedAckExtensionInterval;
654
671
  private subscription;
672
+ private cachedNc;
655
673
  constructor(messageProvider: MessageProvider, patternRegistry: PatternRegistry, connection: ConnectionProvider, codec: Codec, eventBus: EventBus, rpcOptions?: RpcRouterOptions | undefined, ackWaitMap?: Map<StreamKind, number> | undefined);
656
674
  /** Lazily resolve the ack extension interval (needs ackWaitMap populated at runtime). */
657
675
  private get ackExtensionInterval();
658
676
  /** Start routing command messages to handlers. */
659
- start(): void;
677
+ start(): Promise<void>;
660
678
  /** Stop routing and unsubscribe. */
661
679
  destroy(): void;
662
- /** Handle a single RPC command message. */
663
- private handle;
680
+ /** Handle a single RPC command message with error isolation. */
681
+ private handleSafe;
664
682
  /** Execute handler, publish response, settle message. */
665
683
  private executeHandler;
666
684
  }
@@ -1137,19 +1155,33 @@ type NatsMessage = JsMsg | Msg;
1137
1155
  * Execution context for RPC and event handlers.
1138
1156
  *
1139
1157
  * Provides convenient accessors for the NATS message, subject,
1140
- * and headers without needing to interact with the raw message directly.
1158
+ * headers, and JetStream metadata without needing to interact
1159
+ * with the raw message directly.
1160
+ *
1161
+ * Handlers can also control message settlement via {@link retry}
1162
+ * and {@link terminate} instead of throwing errors.
1141
1163
  *
1142
1164
  * @example
1143
1165
  * ```typescript
1144
- * @MessagePattern('get.user')
1145
- * getUser(data: GetUserDto, @Ctx() ctx: RpcContext) {
1146
- * const traceId = ctx.getHeader('x-trace-id');
1147
- * const subject = ctx.getSubject();
1148
- * return this.userService.findOne(data.id);
1166
+ * @EventPattern('order.process')
1167
+ * async handle(@Payload() data: OrderDto, @Ctx() ctx: RpcContext) {
1168
+ * if (ctx.getDeliveryCount()! >= 3) {
1169
+ * ctx.terminate('Max business retries exceeded');
1170
+ * return;
1171
+ * }
1172
+ * if (!this.isReady()) {
1173
+ * ctx.retry({ delay: 5000 });
1174
+ * return;
1175
+ * }
1176
+ * await this.process(data);
1149
1177
  * }
1150
1178
  * ```
1151
1179
  */
1152
1180
  declare class RpcContext extends BaseRpcContext<[NatsMessage]> {
1181
+ private _shouldRetry;
1182
+ private _retryDelay;
1183
+ private _shouldTerminate;
1184
+ private _terminateReason;
1153
1185
  /**
1154
1186
  * Get the underlying NATS message.
1155
1187
  *
@@ -1176,6 +1208,50 @@ declare class RpcContext extends BaseRpcContext<[NatsMessage]> {
1176
1208
  isJetStream(): this is RpcContext & {
1177
1209
  getMessage(): JsMsg;
1178
1210
  };
1211
+ /** How many times this message has been delivered. */
1212
+ getDeliveryCount(): number | undefined;
1213
+ /** The JetStream stream this message belongs to. */
1214
+ getStream(): string | undefined;
1215
+ /** The stream sequence number. */
1216
+ getSequence(): number | undefined;
1217
+ /** The message timestamp as a `Date` (derived from `info.timestampNanos`). */
1218
+ getTimestamp(): Date | undefined;
1219
+ /** The name of the service that published this message (from `x-caller-name` header). */
1220
+ getCallerName(): string | undefined;
1221
+ /**
1222
+ * Signal the transport to retry (nak) this message instead of acknowledging it.
1223
+ *
1224
+ * Use for business-level retries without throwing errors.
1225
+ * Only affects JetStream event handlers (workqueue/broadcast).
1226
+ *
1227
+ * @param opts - Optional delay in ms before redelivery.
1228
+ * @throws Error if {@link terminate} was already called.
1229
+ */
1230
+ retry(opts?: {
1231
+ delayMs?: number;
1232
+ }): void;
1233
+ /**
1234
+ * Signal the transport to permanently reject (term) this message.
1235
+ *
1236
+ * Use when a message is no longer relevant and should not be retried or sent to DLQ.
1237
+ * Only affects JetStream event handlers (workqueue/broadcast).
1238
+ *
1239
+ * @param reason - Optional reason for termination (logged by NATS).
1240
+ * @throws Error if {@link retry} was already called.
1241
+ */
1242
+ terminate(reason?: string): void;
1243
+ /** Narrow to JsMsg or return null for Core messages. Used by metadata getters. */
1244
+ private asJetStream;
1245
+ /** Ensure the message is JetStream — settlement actions are not available for Core NATS. */
1246
+ private assertJetStream;
1247
+ /** @internal */
1248
+ get shouldRetry(): boolean;
1249
+ /** @internal */
1250
+ get retryDelay(): number | undefined;
1251
+ /** @internal */
1252
+ get shouldTerminate(): boolean;
1253
+ /** @internal */
1254
+ get terminateReason(): string | undefined;
1179
1255
  }
1180
1256
 
1181
1257
  /**
@@ -1278,6 +1354,38 @@ declare enum JetstreamHeader {
1278
1354
  /** Set to `'true'` on error responses so the client can distinguish success from failure. */
1279
1355
  Error = "x-error"
1280
1356
  }
1357
+ /**
1358
+ * Build the internal service name with microservice suffix.
1359
+ *
1360
+ * @param name - Service name from `forRoot({ name })`.
1361
+ * @returns `{name}__microservice`
1362
+ */
1363
+ declare const internalName: (name: string) => string;
1364
+ /**
1365
+ * Build a fully-qualified NATS subject for workqueue events, RPC commands, or ordered events.
1366
+ *
1367
+ * @param serviceName - Target service name.
1368
+ * @param kind - Subject kind (`'ev'`, `'cmd'`, or `'ordered'`).
1369
+ * @param pattern - The message pattern (e.g. `'user.created'`).
1370
+ * @returns `{serviceName}__microservice.{kind}.{pattern}`
1371
+ */
1372
+ declare const buildSubject: (serviceName: string, kind: SubjectKind, pattern: string) => string;
1373
+ /**
1374
+ * Build the JetStream stream name for a given service and kind.
1375
+ *
1376
+ * @param serviceName - Service name from `forRoot({ name })`.
1377
+ * @param kind - Stream kind (`'ev'`, `'cmd'`, or `'broadcast'`).
1378
+ * @returns Stream name (e.g. `orders__microservice_ev-stream` or `broadcast-stream`).
1379
+ */
1380
+ declare const streamName: (serviceName: string, kind: StreamKind) => string;
1381
+ /**
1382
+ * Build the JetStream consumer name for a given service and kind.
1383
+ *
1384
+ * @param serviceName - Service name from `forRoot({ name })`.
1385
+ * @param kind - Stream kind (`'ev'`, `'cmd'`, or `'broadcast'`).
1386
+ * @returns Consumer name (e.g. `orders__microservice_ev-consumer`).
1387
+ */
1388
+ declare const consumerName: (serviceName: string, kind: StreamKind) => string;
1281
1389
  /**
1282
1390
  * Prefixes used in event patterns to route to specific stream types.
1283
1391
  * Applied by the user when emitting events (e.g. `client.emit('broadcast:config.updated', data)`).
@@ -1293,4 +1401,4 @@ declare const isJetStreamRpcMode: (rpc: RpcConfig | undefined) => boolean;
1293
1401
  /** Check if the RPC config specifies Core mode (default). */
1294
1402
  declare const isCoreRpcMode: (rpc: RpcConfig | undefined) => boolean;
1295
1403
 
1296
- export { type Codec, type DeadLetterInfo, EventBus, JETSTREAM_CODEC, JETSTREAM_CONNECTION, JETSTREAM_EVENT_BUS, JETSTREAM_OPTIONS, JetstreamClient, type JetstreamFeatureOptions, JetstreamHeader, JetstreamHealthIndicator, type JetstreamHealthStatus, JetstreamModule, type JetstreamModuleAsyncOptions, type JetstreamModuleOptions, JetstreamRecord, JetstreamRecordBuilder, JetstreamStrategy, JsonCodec, MessageKind, type OrderedEventOverrides, PatternPrefix, type RpcConfig, RpcContext, type StreamConsumerOverrides, StreamKind, TransportEvent, type TransportHooks, getClientToken, isCoreRpcMode, isJetStreamRpcMode, toNanos };
1404
+ export { type Codec, type DeadLetterInfo, EventBus, JETSTREAM_CODEC, JETSTREAM_CONNECTION, JETSTREAM_EVENT_BUS, JETSTREAM_OPTIONS, JetstreamClient, type JetstreamFeatureOptions, JetstreamHeader, JetstreamHealthIndicator, type JetstreamHealthStatus, JetstreamModule, type JetstreamModuleAsyncOptions, type JetstreamModuleOptions, JetstreamRecord, JetstreamRecordBuilder, JetstreamStrategy, JsonCodec, MessageKind, type OrderedEventOverrides, PatternPrefix, type RpcConfig, RpcContext, type StreamConsumerOverrides, StreamKind, TransportEvent, type TransportHooks, buildSubject, consumerName, getClientToken, internalName, isCoreRpcMode, isJetStreamRpcMode, streamName, toNanos };