@horizon-republic/nestjs-jetstream 2.2.0 → 2.3.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.
Files changed (161) hide show
  1. package/README.md +39 -1
  2. package/dist/index.cjs +2015 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +993 -0
  5. package/dist/index.d.ts +993 -13
  6. package/dist/index.js +2012 -39
  7. package/dist/index.js.map +1 -1
  8. package/package.json +29 -19
  9. package/dist/client/index.d.ts +0 -3
  10. package/dist/client/index.d.ts.map +0 -1
  11. package/dist/client/index.js +0 -9
  12. package/dist/client/index.js.map +0 -1
  13. package/dist/client/jetstream.client.d.ts +0 -76
  14. package/dist/client/jetstream.client.d.ts.map +0 -1
  15. package/dist/client/jetstream.client.js +0 -325
  16. package/dist/client/jetstream.client.js.map +0 -1
  17. package/dist/client/jetstream.record.d.ts +0 -55
  18. package/dist/client/jetstream.record.d.ts.map +0 -1
  19. package/dist/client/jetstream.record.js +0 -84
  20. package/dist/client/jetstream.record.js.map +0 -1
  21. package/dist/codec/index.d.ts +0 -2
  22. package/dist/codec/index.d.ts.map +0 -1
  23. package/dist/codec/index.js +0 -6
  24. package/dist/codec/index.js.map +0 -1
  25. package/dist/codec/json.codec.d.ts +0 -20
  26. package/dist/codec/json.codec.d.ts.map +0 -1
  27. package/dist/codec/json.codec.js +0 -30
  28. package/dist/codec/json.codec.js.map +0 -1
  29. package/dist/connection/connection.provider.d.ts +0 -50
  30. package/dist/connection/connection.provider.d.ts.map +0 -1
  31. package/dist/connection/connection.provider.js +0 -141
  32. package/dist/connection/connection.provider.js.map +0 -1
  33. package/dist/connection/index.d.ts +0 -2
  34. package/dist/connection/index.d.ts.map +0 -1
  35. package/dist/connection/index.js +0 -6
  36. package/dist/connection/index.js.map +0 -1
  37. package/dist/context/index.d.ts +0 -2
  38. package/dist/context/index.d.ts.map +0 -1
  39. package/dist/context/index.js +0 -6
  40. package/dist/context/index.js.map +0 -1
  41. package/dist/context/rpc.context.d.ts +0 -35
  42. package/dist/context/rpc.context.d.ts.map +0 -1
  43. package/dist/context/rpc.context.js +0 -44
  44. package/dist/context/rpc.context.js.map +0 -1
  45. package/dist/health/index.d.ts +0 -3
  46. package/dist/health/index.d.ts.map +0 -1
  47. package/dist/health/index.js +0 -6
  48. package/dist/health/index.js.map +0 -1
  49. package/dist/health/jetstream.health-indicator.d.ts +0 -47
  50. package/dist/health/jetstream.health-indicator.d.ts.map +0 -1
  51. package/dist/health/jetstream.health-indicator.js +0 -85
  52. package/dist/health/jetstream.health-indicator.js.map +0 -1
  53. package/dist/hooks/event-bus.d.ts +0 -31
  54. package/dist/hooks/event-bus.d.ts.map +0 -1
  55. package/dist/hooks/event-bus.js +0 -79
  56. package/dist/hooks/event-bus.js.map +0 -1
  57. package/dist/hooks/index.d.ts +0 -2
  58. package/dist/hooks/index.d.ts.map +0 -1
  59. package/dist/hooks/index.js +0 -6
  60. package/dist/hooks/index.js.map +0 -1
  61. package/dist/index.d.ts.map +0 -1
  62. package/dist/interfaces/client.interface.d.ts +0 -14
  63. package/dist/interfaces/client.interface.d.ts.map +0 -1
  64. package/dist/interfaces/client.interface.js +0 -3
  65. package/dist/interfaces/client.interface.js.map +0 -1
  66. package/dist/interfaces/codec.interface.d.ts +0 -28
  67. package/dist/interfaces/codec.interface.d.ts.map +0 -1
  68. package/dist/interfaces/codec.interface.js +0 -3
  69. package/dist/interfaces/codec.interface.js.map +0 -1
  70. package/dist/interfaces/hooks.interface.d.ts +0 -71
  71. package/dist/interfaces/hooks.interface.d.ts.map +0 -1
  72. package/dist/interfaces/hooks.interface.js +0 -16
  73. package/dist/interfaces/hooks.interface.js.map +0 -1
  74. package/dist/interfaces/index.d.ts +0 -8
  75. package/dist/interfaces/index.d.ts.map +0 -1
  76. package/dist/interfaces/index.js +0 -6
  77. package/dist/interfaces/index.js.map +0 -1
  78. package/dist/interfaces/options.interface.d.ts +0 -142
  79. package/dist/interfaces/options.interface.d.ts.map +0 -1
  80. package/dist/interfaces/options.interface.js +0 -3
  81. package/dist/interfaces/options.interface.js.map +0 -1
  82. package/dist/interfaces/routing.interface.d.ts +0 -15
  83. package/dist/interfaces/routing.interface.d.ts.map +0 -1
  84. package/dist/interfaces/routing.interface.js +0 -3
  85. package/dist/interfaces/routing.interface.js.map +0 -1
  86. package/dist/interfaces/stream.interface.d.ts +0 -5
  87. package/dist/interfaces/stream.interface.d.ts.map +0 -1
  88. package/dist/interfaces/stream.interface.js +0 -3
  89. package/dist/interfaces/stream.interface.js.map +0 -1
  90. package/dist/jetstream.constants.d.ts +0 -58
  91. package/dist/jetstream.constants.d.ts.map +0 -1
  92. package/dist/jetstream.constants.js +0 -168
  93. package/dist/jetstream.constants.js.map +0 -1
  94. package/dist/jetstream.module.d.ts +0 -89
  95. package/dist/jetstream.module.d.ts.map +0 -1
  96. package/dist/jetstream.module.js +0 -410
  97. package/dist/jetstream.module.js.map +0 -1
  98. package/dist/server/core-rpc.server.d.ts +0 -31
  99. package/dist/server/core-rpc.server.d.ts.map +0 -1
  100. package/dist/server/core-rpc.server.js +0 -95
  101. package/dist/server/core-rpc.server.js.map +0 -1
  102. package/dist/server/index.d.ts +0 -5
  103. package/dist/server/index.d.ts.map +0 -1
  104. package/dist/server/index.js +0 -16
  105. package/dist/server/index.js.map +0 -1
  106. package/dist/server/infrastructure/consumer.provider.d.ts +0 -36
  107. package/dist/server/infrastructure/consumer.provider.d.ts.map +0 -1
  108. package/dist/server/infrastructure/consumer.provider.js +0 -123
  109. package/dist/server/infrastructure/consumer.provider.js.map +0 -1
  110. package/dist/server/infrastructure/index.d.ts +0 -4
  111. package/dist/server/infrastructure/index.d.ts.map +0 -1
  112. package/dist/server/infrastructure/index.js +0 -10
  113. package/dist/server/infrastructure/index.js.map +0 -1
  114. package/dist/server/infrastructure/message.provider.d.ts +0 -46
  115. package/dist/server/infrastructure/message.provider.d.ts.map +0 -1
  116. package/dist/server/infrastructure/message.provider.js +0 -100
  117. package/dist/server/infrastructure/message.provider.js.map +0 -1
  118. package/dist/server/infrastructure/stream.provider.d.ts +0 -38
  119. package/dist/server/infrastructure/stream.provider.d.ts.map +0 -1
  120. package/dist/server/infrastructure/stream.provider.js +0 -109
  121. package/dist/server/infrastructure/stream.provider.js.map +0 -1
  122. package/dist/server/routing/event.router.d.ts +0 -56
  123. package/dist/server/routing/event.router.d.ts.map +0 -1
  124. package/dist/server/routing/event.router.js +0 -132
  125. package/dist/server/routing/event.router.js.map +0 -1
  126. package/dist/server/routing/index.d.ts +0 -5
  127. package/dist/server/routing/index.d.ts.map +0 -1
  128. package/dist/server/routing/index.js +0 -10
  129. package/dist/server/routing/index.js.map +0 -1
  130. package/dist/server/routing/pattern-registry.d.ts +0 -39
  131. package/dist/server/routing/pattern-registry.d.ts.map +0 -1
  132. package/dist/server/routing/pattern-registry.js +0 -116
  133. package/dist/server/routing/pattern-registry.js.map +0 -1
  134. package/dist/server/routing/rpc.router.d.ts +0 -37
  135. package/dist/server/routing/rpc.router.d.ts.map +0 -1
  136. package/dist/server/routing/rpc.router.js +0 -121
  137. package/dist/server/routing/rpc.router.js.map +0 -1
  138. package/dist/server/strategy.d.ts +0 -55
  139. package/dist/server/strategy.d.ts.map +0 -1
  140. package/dist/server/strategy.js +0 -113
  141. package/dist/server/strategy.js.map +0 -1
  142. package/dist/shutdown/index.d.ts +0 -2
  143. package/dist/shutdown/index.d.ts.map +0 -1
  144. package/dist/shutdown/index.js +0 -6
  145. package/dist/shutdown/index.js.map +0 -1
  146. package/dist/shutdown/shutdown.manager.d.ts +0 -27
  147. package/dist/shutdown/shutdown.manager.d.ts.map +0 -1
  148. package/dist/shutdown/shutdown.manager.js +0 -45
  149. package/dist/shutdown/shutdown.manager.js.map +0 -1
  150. package/dist/utils/index.d.ts +0 -3
  151. package/dist/utils/index.d.ts.map +0 -1
  152. package/dist/utils/index.js +0 -8
  153. package/dist/utils/index.js.map +0 -1
  154. package/dist/utils/serialize-error.d.ts +0 -10
  155. package/dist/utils/serialize-error.d.ts.map +0 -1
  156. package/dist/utils/serialize-error.js +0 -21
  157. package/dist/utils/serialize-error.js.map +0 -1
  158. package/dist/utils/unwrap-result.d.ts +0 -15
  159. package/dist/utils/unwrap-result.d.ts.map +0 -1
  160. package/dist/utils/unwrap-result.js +0 -49
  161. package/dist/utils/unwrap-result.js.map +0 -1
package/dist/index.cjs ADDED
@@ -0,0 +1,2015 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from4, except, desc) => {
11
+ if (from4 && typeof from4 === "object" || typeof from4 === "function") {
12
+ for (let key of __getOwnPropNames(from4))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from4[key], enumerable: !(desc = __getOwnPropDesc(from4, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var __decorateClass = (decorators, target, key, kind) => {
20
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
21
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
22
+ if (decorator = decorators[i])
23
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
24
+ if (kind && result) __defProp(target, key, result);
25
+ return result;
26
+ };
27
+ var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
28
+
29
+ // src/index.ts
30
+ var index_exports = {};
31
+ __export(index_exports, {
32
+ EventBus: () => EventBus,
33
+ JETSTREAM_CODEC: () => JETSTREAM_CODEC,
34
+ JETSTREAM_CONNECTION: () => JETSTREAM_CONNECTION,
35
+ JETSTREAM_EVENT_BUS: () => JETSTREAM_EVENT_BUS,
36
+ JETSTREAM_OPTIONS: () => JETSTREAM_OPTIONS,
37
+ JetstreamClient: () => JetstreamClient,
38
+ JetstreamHeader: () => JetstreamHeader,
39
+ JetstreamHealthIndicator: () => JetstreamHealthIndicator,
40
+ JetstreamModule: () => JetstreamModule,
41
+ JetstreamRecord: () => JetstreamRecord,
42
+ JetstreamRecordBuilder: () => JetstreamRecordBuilder,
43
+ JetstreamStrategy: () => JetstreamStrategy,
44
+ JsonCodec: () => JsonCodec,
45
+ RpcContext: () => RpcContext,
46
+ TransportEvent: () => TransportEvent,
47
+ getClientToken: () => getClientToken,
48
+ nanos: () => nanos
49
+ });
50
+ module.exports = __toCommonJS(index_exports);
51
+
52
+ // src/jetstream.module.ts
53
+ var import_common12 = require("@nestjs/common");
54
+
55
+ // src/client/jetstream.client.ts
56
+ var import_common = require("@nestjs/common");
57
+ var import_microservices = require("@nestjs/microservices");
58
+ var import_nats2 = require("nats");
59
+
60
+ // src/interfaces/hooks.interface.ts
61
+ var TransportEvent = /* @__PURE__ */ ((TransportEvent2) => {
62
+ TransportEvent2["Connect"] = "connect";
63
+ TransportEvent2["Disconnect"] = "disconnect";
64
+ TransportEvent2["Reconnect"] = "reconnect";
65
+ TransportEvent2["Error"] = "error";
66
+ TransportEvent2["RpcTimeout"] = "rpcTimeout";
67
+ TransportEvent2["MessageRouted"] = "messageRouted";
68
+ TransportEvent2["ShutdownStart"] = "shutdownStart";
69
+ TransportEvent2["ShutdownComplete"] = "shutdownComplete";
70
+ TransportEvent2["DeadLetter"] = "deadLetter";
71
+ return TransportEvent2;
72
+ })(TransportEvent || {});
73
+
74
+ // src/jetstream.constants.ts
75
+ var import_nats = require("nats");
76
+ var JETSTREAM_OPTIONS = /* @__PURE__ */ Symbol("JETSTREAM_OPTIONS");
77
+ var JETSTREAM_CONNECTION = /* @__PURE__ */ Symbol("JETSTREAM_CONNECTION");
78
+ var JETSTREAM_CODEC = /* @__PURE__ */ Symbol("JETSTREAM_CODEC");
79
+ var JETSTREAM_EVENT_BUS = /* @__PURE__ */ Symbol("JETSTREAM_EVENT_BUS");
80
+ var getClientToken = (name) => name;
81
+ var KB = 1024;
82
+ var MB = 1024 * KB;
83
+ var GB = 1024 * MB;
84
+ var nanos = (ms) => ms * 1e6;
85
+ var baseStreamConfig = {
86
+ retention: import_nats.RetentionPolicy.Workqueue,
87
+ storage: import_nats.StorageType.File,
88
+ num_replicas: 1,
89
+ discard: import_nats.DiscardPolicy.Old,
90
+ allow_direct: true,
91
+ compression: import_nats.StoreCompression.None
92
+ };
93
+ var DEFAULT_EVENT_STREAM_CONFIG = {
94
+ ...baseStreamConfig,
95
+ allow_rollup_hdrs: true,
96
+ max_consumers: 100,
97
+ max_msg_size: 10 * MB,
98
+ max_msgs_per_subject: 5e6,
99
+ max_msgs: 5e7,
100
+ max_bytes: 5 * GB,
101
+ max_age: nanos(7 * 24 * 60 * 60 * 1e3),
102
+ // 7 days
103
+ duplicate_window: nanos(2 * 60 * 1e3)
104
+ // 2 min
105
+ };
106
+ var DEFAULT_COMMAND_STREAM_CONFIG = {
107
+ ...baseStreamConfig,
108
+ allow_rollup_hdrs: false,
109
+ max_consumers: 50,
110
+ max_msg_size: 5 * MB,
111
+ max_msgs_per_subject: 1e5,
112
+ max_msgs: 1e6,
113
+ max_bytes: 100 * MB,
114
+ max_age: nanos(3 * 60 * 1e3),
115
+ // 3 min
116
+ duplicate_window: nanos(30 * 1e3)
117
+ // 30s
118
+ };
119
+ var DEFAULT_BROADCAST_STREAM_CONFIG = {
120
+ ...baseStreamConfig,
121
+ retention: import_nats.RetentionPolicy.Limits,
122
+ allow_rollup_hdrs: true,
123
+ max_consumers: 200,
124
+ max_msg_size: 10 * MB,
125
+ max_msgs_per_subject: 1e6,
126
+ max_msgs: 1e7,
127
+ max_bytes: 2 * GB,
128
+ max_age: nanos(24 * 60 * 60 * 1e3),
129
+ // 1 day
130
+ duplicate_window: nanos(2 * 60 * 1e3)
131
+ // 2 min
132
+ };
133
+ var DEFAULT_EVENT_CONSUMER_CONFIG = {
134
+ ack_wait: nanos(10 * 1e3),
135
+ // 10s
136
+ max_deliver: 3,
137
+ max_ack_pending: 100,
138
+ ack_policy: import_nats.AckPolicy.Explicit,
139
+ deliver_policy: import_nats.DeliverPolicy.All,
140
+ replay_policy: import_nats.ReplayPolicy.Instant
141
+ };
142
+ var DEFAULT_COMMAND_CONSUMER_CONFIG = {
143
+ ack_wait: nanos(5 * 60 * 1e3),
144
+ // 5 min
145
+ max_deliver: 1,
146
+ max_ack_pending: 100,
147
+ ack_policy: import_nats.AckPolicy.Explicit,
148
+ deliver_policy: import_nats.DeliverPolicy.All,
149
+ replay_policy: import_nats.ReplayPolicy.Instant
150
+ };
151
+ var DEFAULT_BROADCAST_CONSUMER_CONFIG = {
152
+ ack_wait: nanos(10 * 1e3),
153
+ // 10s
154
+ max_deliver: 3,
155
+ max_ack_pending: 100,
156
+ ack_policy: import_nats.AckPolicy.Explicit,
157
+ deliver_policy: import_nats.DeliverPolicy.All,
158
+ replay_policy: import_nats.ReplayPolicy.Instant
159
+ };
160
+ var DEFAULT_RPC_TIMEOUT = 3e4;
161
+ var DEFAULT_JETSTREAM_RPC_TIMEOUT = 18e4;
162
+ var DEFAULT_SHUTDOWN_TIMEOUT = 1e4;
163
+ var JetstreamHeader = /* @__PURE__ */ ((JetstreamHeader2) => {
164
+ JetstreamHeader2["CorrelationId"] = "x-correlation-id";
165
+ JetstreamHeader2["ReplyTo"] = "x-reply-to";
166
+ JetstreamHeader2["Subject"] = "x-subject";
167
+ JetstreamHeader2["CallerName"] = "x-caller-name";
168
+ JetstreamHeader2["RequestId"] = "x-request-id";
169
+ JetstreamHeader2["TraceId"] = "x-trace-id";
170
+ JetstreamHeader2["SpanId"] = "x-span-id";
171
+ JetstreamHeader2["Error"] = "x-error";
172
+ return JetstreamHeader2;
173
+ })(JetstreamHeader || {});
174
+ var RESERVED_HEADERS = /* @__PURE__ */ new Set([
175
+ "x-correlation-id" /* CorrelationId */,
176
+ "x-reply-to" /* ReplyTo */,
177
+ "x-error" /* Error */
178
+ ]);
179
+ var internalName = (name) => `${name}__microservice`;
180
+ var buildSubject = (serviceName, kind, pattern) => `${internalName(serviceName)}.${kind}.${pattern}`;
181
+ var buildBroadcastSubject = (pattern) => `broadcast.${pattern}`;
182
+ var streamName = (serviceName, kind) => {
183
+ if (kind === "broadcast") return "broadcast-stream";
184
+ return `${internalName(serviceName)}_${kind}-stream`;
185
+ };
186
+ var consumerName = (serviceName, kind) => {
187
+ if (kind === "broadcast") return `${internalName(serviceName)}_broadcast-consumer`;
188
+ return `${internalName(serviceName)}_${kind}-consumer`;
189
+ };
190
+
191
+ // src/client/jetstream.record.ts
192
+ var JetstreamRecord = class {
193
+ constructor(data, headers2, timeout) {
194
+ this.data = data;
195
+ this.headers = headers2;
196
+ this.timeout = timeout;
197
+ }
198
+ };
199
+ var JetstreamRecordBuilder = class {
200
+ data;
201
+ headers = /* @__PURE__ */ new Map();
202
+ timeout;
203
+ constructor(data) {
204
+ this.data = data;
205
+ }
206
+ /** Set the message payload. */
207
+ setData(data) {
208
+ this.data = data;
209
+ return this;
210
+ }
211
+ /**
212
+ * Set a single custom header.
213
+ *
214
+ * @throws Error if the header name is reserved by the transport.
215
+ */
216
+ setHeader(key, value) {
217
+ this.validateHeaderKey(key);
218
+ this.headers.set(key, value);
219
+ return this;
220
+ }
221
+ /**
222
+ * Set multiple custom headers at once.
223
+ *
224
+ * @throws Error if any header name is reserved by the transport.
225
+ */
226
+ setHeaders(headers2) {
227
+ for (const [key, value] of Object.entries(headers2)) {
228
+ this.setHeader(key, value);
229
+ }
230
+ return this;
231
+ }
232
+ /** Set RPC request timeout in milliseconds. */
233
+ setTimeout(ms) {
234
+ this.timeout = ms;
235
+ return this;
236
+ }
237
+ /** Build the immutable JetstreamRecord. */
238
+ build() {
239
+ return new JetstreamRecord(this.data, new Map(this.headers), this.timeout);
240
+ }
241
+ /** Validate that a header key is not reserved. */
242
+ validateHeaderKey(key) {
243
+ if (RESERVED_HEADERS.has(key)) {
244
+ throw new Error(
245
+ `Header "${key}" is reserved by the JetStream transport and cannot be set manually. Reserved headers: ${[...RESERVED_HEADERS].join(", ")}`
246
+ );
247
+ }
248
+ }
249
+ };
250
+
251
+ // src/client/jetstream.client.ts
252
+ var JetstreamClient = class extends import_microservices.ClientProxy {
253
+ constructor(rootOptions, targetServiceName, connection, codec, eventBus) {
254
+ super();
255
+ this.rootOptions = rootOptions;
256
+ this.connection = connection;
257
+ this.codec = codec;
258
+ this.eventBus = eventBus;
259
+ this.targetName = targetServiceName;
260
+ }
261
+ logger = new import_common.Logger("Jetstream:Client");
262
+ /** Target service name this client sends messages to. */
263
+ targetName;
264
+ /** Shared inbox for JetStream-mode RPC responses. */
265
+ inbox = null;
266
+ inboxSubscription = null;
267
+ /** Pending JetStream-mode RPC callbacks, keyed by correlation ID. */
268
+ pendingMessages = /* @__PURE__ */ new Map();
269
+ /** Pending JetStream-mode RPC timeouts, keyed by correlation ID. */
270
+ pendingTimeouts = /* @__PURE__ */ new Map();
271
+ /** Subscription to connection status events for disconnect handling. */
272
+ statusSubscription = null;
273
+ /** Establish connection. Called automatically by NestJS on first use. */
274
+ async connect() {
275
+ const nc = await this.connection.getConnection();
276
+ if (this.isJetStreamRpcMode() && !this.inboxSubscription) {
277
+ this.setupInbox(nc);
278
+ }
279
+ this.statusSubscription ??= this.connection.status$.subscribe((status) => {
280
+ if (status.type === import_nats2.Events.Disconnect) {
281
+ this.handleDisconnect();
282
+ }
283
+ });
284
+ return nc;
285
+ }
286
+ /** Clean up resources. */
287
+ async close() {
288
+ this.statusSubscription?.unsubscribe();
289
+ this.statusSubscription = null;
290
+ this.rejectPendingRpcs(new Error("Client closed"));
291
+ }
292
+ /** Direct access to the raw NATS connection. */
293
+ unwrap() {
294
+ return this.connection.unwrap;
295
+ }
296
+ /**
297
+ * Publish a fire-and-forget event to JetStream.
298
+ *
299
+ * Events are published to either the workqueue stream or broadcast stream
300
+ * depending on the subject prefix.
301
+ */
302
+ async dispatchEvent(packet) {
303
+ const nc = await this.connect();
304
+ const { data, hdrs } = this.extractRecordData(packet.data);
305
+ const subject = this.buildEventSubject(packet.pattern);
306
+ const msgHeaders = this.buildHeaders(hdrs, { subject });
307
+ await nc.jetstream().publish(subject, this.codec.encode(data), {
308
+ headers: msgHeaders,
309
+ msgID: crypto.randomUUID()
310
+ });
311
+ return void 0;
312
+ }
313
+ /**
314
+ * Publish an RPC command and register callback for response.
315
+ *
316
+ * Core mode: uses nc.request() with timeout.
317
+ * JetStream mode: publishes to stream + waits for inbox response.
318
+ */
319
+ publish(packet, callback) {
320
+ const subject = buildSubject(this.targetName, "cmd", packet.pattern);
321
+ const { data, hdrs, timeout } = this.extractRecordData(packet.data);
322
+ const onUnhandled = (err) => {
323
+ this.logger.error("Unhandled publish error:", err);
324
+ callback({ err: new Error("Internal transport error"), response: null, isDisposed: true });
325
+ };
326
+ let jetStreamCorrelationId = null;
327
+ if (this.isCoreRpcMode()) {
328
+ this.publishCoreRpc(subject, data, hdrs, timeout, callback).catch(onUnhandled);
329
+ } else {
330
+ jetStreamCorrelationId = crypto.randomUUID();
331
+ this.publishJetStreamRpc(
332
+ subject,
333
+ data,
334
+ hdrs,
335
+ timeout,
336
+ callback,
337
+ jetStreamCorrelationId
338
+ ).catch(onUnhandled);
339
+ }
340
+ return () => {
341
+ if (jetStreamCorrelationId) {
342
+ const timeoutId = this.pendingTimeouts.get(jetStreamCorrelationId);
343
+ if (timeoutId) {
344
+ clearTimeout(timeoutId);
345
+ this.pendingTimeouts.delete(jetStreamCorrelationId);
346
+ }
347
+ this.pendingMessages.delete(jetStreamCorrelationId);
348
+ }
349
+ };
350
+ }
351
+ /** Core mode: nc.request() with timeout. */
352
+ async publishCoreRpc(subject, data, customHeaders, timeout, callback) {
353
+ try {
354
+ const nc = await this.connect();
355
+ const effectiveTimeout = timeout ?? this.getRpcTimeout();
356
+ const hdrs = this.buildHeaders(customHeaders, { subject });
357
+ const response = await nc.request(subject, this.codec.encode(data), {
358
+ timeout: effectiveTimeout,
359
+ headers: hdrs
360
+ });
361
+ const decoded = this.codec.decode(response.data);
362
+ if (response.headers?.get("x-error" /* Error */)) {
363
+ callback({ err: decoded, response: null, isDisposed: true });
364
+ } else {
365
+ callback({ err: null, response: decoded, isDisposed: true });
366
+ }
367
+ } catch (err) {
368
+ const error = err instanceof Error ? err : new Error("Unknown error");
369
+ this.logger.error(`Core RPC error (${subject}):`, err);
370
+ this.eventBus.emit("error" /* Error */, error, "client-rpc");
371
+ callback({ err: error, response: null, isDisposed: true });
372
+ }
373
+ }
374
+ /** JetStream mode: publish to stream + wait for inbox response. */
375
+ async publishJetStreamRpc(subject, data, customHeaders, timeout, callback, correlationId = crypto.randomUUID()) {
376
+ const effectiveTimeout = timeout ?? this.getRpcTimeout();
377
+ this.pendingMessages.set(correlationId, callback);
378
+ const timeoutId = setTimeout(() => {
379
+ if (!this.pendingMessages.has(correlationId)) return;
380
+ this.pendingTimeouts.delete(correlationId);
381
+ this.pendingMessages.delete(correlationId);
382
+ this.logger.error(`JetStream RPC timeout (${effectiveTimeout}ms): ${subject}`);
383
+ this.eventBus.emit("rpcTimeout" /* RpcTimeout */, subject, correlationId);
384
+ callback({ err: new Error("RPC timeout"), response: null, isDisposed: true });
385
+ }, effectiveTimeout);
386
+ this.pendingTimeouts.set(correlationId, timeoutId);
387
+ try {
388
+ const nc = await this.connect();
389
+ if (!this.inbox) {
390
+ throw new Error("Inbox not initialized \u2014 JetStream RPC mode requires a connected inbox");
391
+ }
392
+ const hdrs = this.buildHeaders(customHeaders, {
393
+ subject,
394
+ correlationId,
395
+ replyTo: this.inbox
396
+ });
397
+ await nc.jetstream().publish(subject, this.codec.encode(data), {
398
+ headers: hdrs,
399
+ msgID: crypto.randomUUID()
400
+ });
401
+ } catch (err) {
402
+ clearTimeout(timeoutId);
403
+ this.pendingTimeouts.delete(correlationId);
404
+ if (!this.pendingMessages.has(correlationId)) return;
405
+ this.pendingMessages.delete(correlationId);
406
+ const error = err instanceof Error ? err : new Error("Unknown error");
407
+ this.logger.error(`JetStream RPC publish error (${subject}):`, err);
408
+ callback({ err: error, response: null, isDisposed: true });
409
+ }
410
+ }
411
+ /** Fail-fast all pending JetStream RPC callbacks on connection loss. */
412
+ handleDisconnect() {
413
+ this.rejectPendingRpcs(new Error("Connection lost"));
414
+ this.inbox = null;
415
+ }
416
+ /** Reject all pending RPC callbacks, clear timeouts, and tear down inbox. */
417
+ rejectPendingRpcs(error) {
418
+ for (const callback of this.pendingMessages.values()) {
419
+ callback({ err: error, response: null, isDisposed: true });
420
+ }
421
+ for (const timeoutId of this.pendingTimeouts.values()) {
422
+ clearTimeout(timeoutId);
423
+ }
424
+ this.pendingMessages.clear();
425
+ this.pendingTimeouts.clear();
426
+ this.inboxSubscription?.unsubscribe();
427
+ this.inboxSubscription = null;
428
+ }
429
+ /** Setup shared inbox subscription for JetStream RPC responses. */
430
+ setupInbox(nc) {
431
+ this.inbox = (0, import_nats2.createInbox)(internalName(this.rootOptions.name));
432
+ this.inboxSubscription = nc.subscribe(this.inbox, {
433
+ callback: (err, msg) => {
434
+ if (err) {
435
+ this.logger.error("Inbox subscription error:", err);
436
+ return;
437
+ }
438
+ this.routeInboxReply(msg);
439
+ }
440
+ });
441
+ this.logger.debug(`Inbox subscription: ${this.inbox}`);
442
+ }
443
+ /** Route an inbox reply to the matching pending callback. */
444
+ routeInboxReply(msg) {
445
+ const correlationId = msg.headers?.get("x-correlation-id" /* CorrelationId */);
446
+ if (!correlationId) {
447
+ this.logger.warn("Inbox reply without correlation-id, ignoring");
448
+ return;
449
+ }
450
+ const callback = this.pendingMessages.get(correlationId);
451
+ if (!callback) {
452
+ this.logger.warn(`No pending handler for correlation-id: ${correlationId}`);
453
+ return;
454
+ }
455
+ const timeoutId = this.pendingTimeouts.get(correlationId);
456
+ if (timeoutId) {
457
+ clearTimeout(timeoutId);
458
+ this.pendingTimeouts.delete(correlationId);
459
+ }
460
+ try {
461
+ const decoded = this.codec.decode(msg.data);
462
+ if (msg.headers?.get("x-error" /* Error */)) {
463
+ callback({ err: decoded, response: null, isDisposed: true });
464
+ } else {
465
+ callback({ err: null, response: decoded, isDisposed: true });
466
+ }
467
+ } catch (err) {
468
+ callback({
469
+ err: err instanceof Error ? err : new Error("Decode error"),
470
+ response: null,
471
+ isDisposed: true
472
+ });
473
+ } finally {
474
+ this.pendingMessages.delete(correlationId);
475
+ }
476
+ }
477
+ /** Build event subject — workqueue or broadcast. */
478
+ buildEventSubject(pattern) {
479
+ if (pattern.startsWith("broadcast:")) {
480
+ return buildBroadcastSubject(pattern.slice("broadcast:".length));
481
+ }
482
+ return buildSubject(this.targetName, "ev", pattern);
483
+ }
484
+ /** Build NATS headers merging custom headers with transport headers. */
485
+ buildHeaders(customHeaders, transport) {
486
+ const hdrs = (0, import_nats2.headers)();
487
+ hdrs.set("x-subject" /* Subject */, transport.subject);
488
+ hdrs.set("x-caller-name" /* CallerName */, internalName(this.rootOptions.name));
489
+ if (transport.correlationId) {
490
+ hdrs.set("x-correlation-id" /* CorrelationId */, transport.correlationId);
491
+ }
492
+ if (transport.replyTo) {
493
+ hdrs.set("x-reply-to" /* ReplyTo */, transport.replyTo);
494
+ }
495
+ if (customHeaders) {
496
+ for (const [key, value] of customHeaders) {
497
+ hdrs.set(key, value);
498
+ }
499
+ }
500
+ return hdrs;
501
+ }
502
+ /** Extract data, headers, and timeout from raw packet data or JetstreamRecord. */
503
+ extractRecordData(rawData) {
504
+ if (rawData instanceof JetstreamRecord) {
505
+ return {
506
+ data: rawData.data,
507
+ hdrs: rawData.headers.size > 0 ? new Map(rawData.headers) : null,
508
+ timeout: rawData.timeout
509
+ };
510
+ }
511
+ return { data: rawData, hdrs: null, timeout: void 0 };
512
+ }
513
+ isCoreRpcMode() {
514
+ return !this.rootOptions.rpc || this.rootOptions.rpc.mode === "core";
515
+ }
516
+ isJetStreamRpcMode() {
517
+ return this.rootOptions.rpc?.mode === "jetstream";
518
+ }
519
+ getRpcTimeout() {
520
+ if (!this.rootOptions.rpc) return DEFAULT_RPC_TIMEOUT;
521
+ const defaultTimeout = this.isJetStreamRpcMode() ? DEFAULT_JETSTREAM_RPC_TIMEOUT : DEFAULT_RPC_TIMEOUT;
522
+ return this.rootOptions.rpc.timeout ?? defaultTimeout;
523
+ }
524
+ };
525
+
526
+ // src/codec/json.codec.ts
527
+ var import_nats3 = require("nats");
528
+ var JsonCodec = class {
529
+ inner = (0, import_nats3.JSONCodec)();
530
+ encode(data) {
531
+ return this.inner.encode(data);
532
+ }
533
+ decode(data) {
534
+ return this.inner.decode(data);
535
+ }
536
+ };
537
+
538
+ // src/connection/connection.provider.ts
539
+ var import_common2 = require("@nestjs/common");
540
+ var import_nats4 = require("nats");
541
+ var import_rxjs = require("rxjs");
542
+ var ConnectionProvider = class {
543
+ constructor(options, eventBus) {
544
+ this.options = options;
545
+ this.eventBus = eventBus;
546
+ this.nc$ = (0, import_rxjs.defer)(() => this.getConnection()).pipe(
547
+ (0, import_rxjs.shareReplay)({ bufferSize: 1, refCount: false })
548
+ );
549
+ this.status$ = this.nc$.pipe(
550
+ (0, import_rxjs.switchMap)((nc) => (0, import_rxjs.from)(nc.status())),
551
+ (0, import_rxjs.share)()
552
+ );
553
+ }
554
+ /** Cached observable that replays the established connection to new subscribers. */
555
+ nc$;
556
+ /** Live stream of connection status events (no replay). */
557
+ status$;
558
+ logger = new import_common2.Logger("Jetstream:Connection");
559
+ connection = null;
560
+ connectionPromise = null;
561
+ jsmInstance = null;
562
+ /**
563
+ * Establish NATS connection. Idempotent — returns cached connection on subsequent calls.
564
+ *
565
+ * @throws Error if connection is refused (fail fast).
566
+ */
567
+ async getConnection() {
568
+ if (this.connection && !this.connection.isClosed()) {
569
+ return this.connection;
570
+ }
571
+ if (this.connectionPromise) {
572
+ return this.connectionPromise;
573
+ }
574
+ this.connectionPromise = this.establish();
575
+ return this.connectionPromise;
576
+ }
577
+ /**
578
+ * Get JetStream manager. Cached after first call.
579
+ */
580
+ async getJetStreamManager() {
581
+ if (this.jsmInstance) return this.jsmInstance;
582
+ const nc = await this.getConnection();
583
+ this.jsmInstance = await nc.jetstreamManager();
584
+ this.logger.log("JetStream manager initialized");
585
+ return this.jsmInstance;
586
+ }
587
+ /** Direct access to the raw NATS connection (assumes already connected). */
588
+ get unwrap() {
589
+ return this.connection;
590
+ }
591
+ /**
592
+ * Gracefully shut down the connection.
593
+ *
594
+ * Sequence: drain → wait for close. Falls back to force-close on error.
595
+ */
596
+ async shutdown() {
597
+ if (!this.connection || this.connection.isClosed()) return;
598
+ try {
599
+ await this.connection.drain();
600
+ await this.connection.closed();
601
+ } catch {
602
+ try {
603
+ await this.connection.close();
604
+ } catch {
605
+ }
606
+ } finally {
607
+ this.connection = null;
608
+ this.connectionPromise = null;
609
+ this.jsmInstance = null;
610
+ }
611
+ }
612
+ /** Internal: establish the physical connection with reconnect monitoring. */
613
+ async establish() {
614
+ const name = internalName(this.options.name);
615
+ try {
616
+ const nc = await (0, import_nats4.connect)({
617
+ ...this.options.connectionOptions,
618
+ servers: this.options.servers,
619
+ name
620
+ });
621
+ this.connection = nc;
622
+ this.logger.log(`NATS connection established: ${nc.getServer()}`);
623
+ this.eventBus.emit("connect" /* Connect */, nc.getServer());
624
+ this.monitorStatus(nc);
625
+ return nc;
626
+ } catch (err) {
627
+ const natsErr = err;
628
+ if (natsErr.code === "CONNECTION_REFUSED") {
629
+ throw new Error(`NATS connection refused: ${this.options.servers.join(", ")}`);
630
+ }
631
+ throw err;
632
+ }
633
+ }
634
+ /** Subscribe to connection status events and emit hooks. */
635
+ monitorStatus(nc) {
636
+ (async () => {
637
+ for await (const status of nc.status()) {
638
+ switch (status.type) {
639
+ case import_nats4.Events.Disconnect:
640
+ this.eventBus.emit("disconnect" /* Disconnect */);
641
+ break;
642
+ case import_nats4.Events.Reconnect:
643
+ this.jsmInstance = null;
644
+ this.eventBus.emit("reconnect" /* Reconnect */, nc.getServer());
645
+ break;
646
+ case import_nats4.Events.Error:
647
+ this.eventBus.emit("error" /* Error */, new Error(String(status.data)), "connection");
648
+ break;
649
+ case import_nats4.Events.Update:
650
+ case import_nats4.Events.LDM:
651
+ case import_nats4.DebugEvents.Reconnecting:
652
+ case import_nats4.DebugEvents.PingTimer:
653
+ case import_nats4.DebugEvents.StaleConnection:
654
+ case import_nats4.DebugEvents.ClientInitiatedReconnect:
655
+ break;
656
+ }
657
+ }
658
+ })().catch((err) => {
659
+ this.logger.error("Status monitor error", err);
660
+ });
661
+ }
662
+ };
663
+
664
+ // src/hooks/event-bus.ts
665
+ var EventBus = class {
666
+ hooks;
667
+ logger;
668
+ constructor(logger, hooks) {
669
+ this.logger = logger;
670
+ this.hooks = hooks ?? {};
671
+ }
672
+ /** Emit a lifecycle event. Dispatches to custom hook or Logger fallback. */
673
+ emit(event, ...args) {
674
+ const hook = this.hooks[event];
675
+ if (hook) {
676
+ try {
677
+ hook(...args);
678
+ } catch (err) {
679
+ this.logger.error(
680
+ `Hook "${event}" threw an error: ${err instanceof Error ? err.message : err}`
681
+ );
682
+ }
683
+ return;
684
+ }
685
+ this.defaultHandler(event, args);
686
+ }
687
+ /** Default Logger-based handlers for each event type. */
688
+ defaultHandler(event, args) {
689
+ switch (event) {
690
+ case "connect" /* Connect */:
691
+ this.logger.log(`Connected to NATS: ${args[0]}`);
692
+ break;
693
+ case "disconnect" /* Disconnect */:
694
+ this.logger.warn("NATS connection lost");
695
+ break;
696
+ case "reconnect" /* Reconnect */:
697
+ this.logger.log(`Reconnected to NATS: ${args[0]}`);
698
+ break;
699
+ case "error" /* Error */:
700
+ this.logger.error(`Transport error: ${args[0]}`, args[1] ?? "");
701
+ break;
702
+ case "rpcTimeout" /* RpcTimeout */:
703
+ this.logger.warn(`RPC timeout: ${args[0]} (cid: ${args[1]})`);
704
+ break;
705
+ case "messageRouted" /* MessageRouted */:
706
+ this.logger.debug(`Message routed: ${args[0]} [${args[1]}]`);
707
+ break;
708
+ case "shutdownStart" /* ShutdownStart */:
709
+ this.logger.log("Graceful shutdown initiated");
710
+ break;
711
+ case "shutdownComplete" /* ShutdownComplete */:
712
+ this.logger.log("Graceful shutdown complete");
713
+ break;
714
+ case "deadLetter" /* DeadLetter */: {
715
+ const info = args[0];
716
+ this.logger.warn(`Dead letter: ${info?.subject ?? "unknown"}`);
717
+ break;
718
+ }
719
+ }
720
+ }
721
+ };
722
+
723
+ // src/health/jetstream.health-indicator.ts
724
+ var import_common3 = require("@nestjs/common");
725
+ var JetstreamHealthIndicator = class {
726
+ constructor(connection) {
727
+ this.connection = connection;
728
+ }
729
+ logger = new import_common3.Logger("Jetstream:Health");
730
+ /**
731
+ * Plain health status check.
732
+ *
733
+ * Returns the current connection status without throwing.
734
+ * Use this for custom health endpoints or monitoring integrations.
735
+ */
736
+ async check() {
737
+ const nc = this.connection.unwrap;
738
+ if (!nc || nc.isClosed()) {
739
+ return { connected: false, server: null, latency: null };
740
+ }
741
+ try {
742
+ const start = performance.now();
743
+ await nc.rtt();
744
+ const latency = Math.round(performance.now() - start);
745
+ return { connected: true, server: nc.getServer(), latency };
746
+ } catch (err) {
747
+ this.logger.warn(`Health check failed: ${err instanceof Error ? err.message : err}`);
748
+ return { connected: false, server: nc.getServer(), latency: null };
749
+ }
750
+ }
751
+ /**
752
+ * Terminus-compatible health check.
753
+ *
754
+ * Returns `{ [key]: { status: 'up', ... } }` on success.
755
+ * Throws an error with `{ [key]: { status: 'down', ... } }` on failure.
756
+ *
757
+ * @param key Health indicator key (default: 'jetstream')
758
+ */
759
+ async isHealthy(key = "jetstream") {
760
+ const status = await this.check();
761
+ const details = {
762
+ status: status.connected ? "up" : "down",
763
+ server: status.server,
764
+ latency: status.latency
765
+ };
766
+ if (!status.connected) {
767
+ throw Object.assign(new Error("Jetstream health check failed"), {
768
+ [key]: details
769
+ });
770
+ }
771
+ return { [key]: details };
772
+ }
773
+ };
774
+ JetstreamHealthIndicator = __decorateClass([
775
+ (0, import_common3.Injectable)()
776
+ ], JetstreamHealthIndicator);
777
+
778
+ // src/server/strategy.ts
779
+ var import_microservices2 = require("@nestjs/microservices");
780
+ var JetstreamStrategy = class extends import_microservices2.Server {
781
+ constructor(options, connection, patternRegistry, streamProvider, consumerProvider, messageProvider, eventRouter, rpcRouter, coreRpcServer) {
782
+ super();
783
+ this.options = options;
784
+ this.connection = connection;
785
+ this.patternRegistry = patternRegistry;
786
+ this.streamProvider = streamProvider;
787
+ this.consumerProvider = consumerProvider;
788
+ this.messageProvider = messageProvider;
789
+ this.eventRouter = eventRouter;
790
+ this.rpcRouter = rpcRouter;
791
+ this.coreRpcServer = coreRpcServer;
792
+ }
793
+ transportId = /* @__PURE__ */ Symbol("jetstream-transport");
794
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
795
+ listeners = /* @__PURE__ */ new Map();
796
+ /**
797
+ * Start the transport: register handlers, create infrastructure, begin consumption.
798
+ *
799
+ * Called by NestJS when `connectMicroservice()` is used, or internally by the module.
800
+ */
801
+ async listen(callback) {
802
+ this.patternRegistry.registerHandlers(this.getHandlers());
803
+ const streamKinds = this.resolveStreamKinds();
804
+ if (streamKinds.length > 0) {
805
+ await this.streamProvider.ensureStreams(streamKinds);
806
+ const consumers = await this.consumerProvider.ensureConsumers(streamKinds);
807
+ this.messageProvider.start(consumers);
808
+ if (this.patternRegistry.hasEventHandlers() || this.patternRegistry.hasBroadcastHandlers()) {
809
+ this.eventRouter.start();
810
+ }
811
+ if (this.isJetStreamRpcMode() && this.patternRegistry.hasRpcHandlers()) {
812
+ this.rpcRouter.start();
813
+ }
814
+ }
815
+ if (this.isCoreRpcMode() && this.patternRegistry.hasRpcHandlers()) {
816
+ await this.coreRpcServer.start();
817
+ }
818
+ callback();
819
+ }
820
+ /** Gracefully stop the transport. */
821
+ close() {
822
+ this.eventRouter.destroy();
823
+ this.rpcRouter.destroy();
824
+ this.coreRpcServer.stop();
825
+ this.messageProvider.destroy();
826
+ }
827
+ /**
828
+ * Register event listener (required by Server base class).
829
+ *
830
+ * Stores callbacks for potential use. Primary lifecycle events
831
+ * are routed through EventBus.
832
+ */
833
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
834
+ on(event, callback) {
835
+ const existing = this.listeners.get(event) ?? [];
836
+ existing.push(callback);
837
+ this.listeners.set(event, existing);
838
+ }
839
+ /** Unwrap the underlying NATS connection. */
840
+ unwrap() {
841
+ return this.connection.unwrap;
842
+ }
843
+ /** Access the pattern registry (for module-level introspection). */
844
+ getPatternRegistry() {
845
+ return this.patternRegistry;
846
+ }
847
+ /** Determine which JetStream stream kinds are needed. */
848
+ resolveStreamKinds() {
849
+ const kinds = [];
850
+ if (this.patternRegistry.hasEventHandlers()) {
851
+ kinds.push("ev");
852
+ }
853
+ if (this.isJetStreamRpcMode() && this.patternRegistry.hasRpcHandlers()) {
854
+ kinds.push("cmd");
855
+ }
856
+ if (this.patternRegistry.hasBroadcastHandlers()) {
857
+ kinds.push("broadcast");
858
+ }
859
+ return kinds;
860
+ }
861
+ isCoreRpcMode() {
862
+ return !this.options.rpc || this.options.rpc.mode === "core";
863
+ }
864
+ isJetStreamRpcMode() {
865
+ return this.options.rpc?.mode === "jetstream";
866
+ }
867
+ };
868
+
869
+ // src/server/core-rpc.server.ts
870
+ var import_common4 = require("@nestjs/common");
871
+ var import_nats5 = require("nats");
872
+
873
+ // src/context/rpc.context.ts
874
+ var import_microservices3 = require("@nestjs/microservices");
875
+ var RpcContext = class extends import_microservices3.BaseRpcContext {
876
+ /** Get the underlying NATS message (JsMsg for JetStream, Msg for Core). */
877
+ getMessage() {
878
+ return this.args[0];
879
+ }
880
+ /** Get the NATS subject this message was published to. */
881
+ getSubject() {
882
+ return this.args[0].subject;
883
+ }
884
+ /** Get all NATS message headers, or undefined if none are present. */
885
+ getHeaders() {
886
+ return this.args[0].headers;
887
+ }
888
+ /** Get a single header value by key. Returns undefined if the header or headers object is missing. */
889
+ getHeader(key) {
890
+ return this.args[0].headers?.get(key);
891
+ }
892
+ /** Type guard: narrows getMessage() return type to JsMsg when true. */
893
+ isJetStream() {
894
+ return "ack" in this.args[0];
895
+ }
896
+ };
897
+
898
+ // src/utils/serialize-error.ts
899
+ var import_microservices4 = require("@nestjs/microservices");
900
+ var serializeError = (err) => {
901
+ if (err instanceof import_microservices4.RpcException) return err.getError();
902
+ if (err instanceof Error) return { message: err.message };
903
+ return err;
904
+ };
905
+
906
+ // src/utils/unwrap-result.ts
907
+ var import_rxjs2 = require("rxjs");
908
+ var unwrapResult = async (result) => {
909
+ if ((0, import_rxjs2.isObservable)(result)) {
910
+ return subscribeToFirst(result);
911
+ }
912
+ const resolved = await result;
913
+ if ((0, import_rxjs2.isObservable)(resolved)) {
914
+ return subscribeToFirst(resolved);
915
+ }
916
+ return resolved;
917
+ };
918
+ var subscribeToFirst = (obs) => new Promise((resolve, reject) => {
919
+ let done = false;
920
+ obs.subscribe({
921
+ next: (val) => {
922
+ if (!done) {
923
+ done = true;
924
+ resolve(val);
925
+ }
926
+ },
927
+ error: reject,
928
+ complete: () => {
929
+ if (!done) resolve(void 0);
930
+ }
931
+ });
932
+ });
933
+
934
+ // src/server/core-rpc.server.ts
935
+ var CoreRpcServer = class {
936
+ constructor(options, connection, patternRegistry, codec, eventBus) {
937
+ this.options = options;
938
+ this.connection = connection;
939
+ this.patternRegistry = patternRegistry;
940
+ this.codec = codec;
941
+ this.eventBus = eventBus;
942
+ }
943
+ logger = new import_common4.Logger("Jetstream:CoreRpc");
944
+ subscription = null;
945
+ /** Start listening for RPC requests on the command subject. */
946
+ async start() {
947
+ const nc = await this.connection.getConnection();
948
+ const serviceName = internalName(this.options.name);
949
+ const subject = `${serviceName}.cmd.>`;
950
+ const queue = `${serviceName}_cmd_queue`;
951
+ this.subscription = nc.subscribe(subject, {
952
+ queue,
953
+ callback: (err, msg) => {
954
+ if (err) {
955
+ this.logger.error("Core RPC subscription error:", err);
956
+ return;
957
+ }
958
+ this.handleRequest(msg).catch((err2) => {
959
+ this.logger.error("Unhandled request error:", err2);
960
+ });
961
+ }
962
+ });
963
+ this.logger.log(`Core RPC listening: ${subject} (queue: ${queue})`);
964
+ }
965
+ /** Stop listening and clean up the subscription. */
966
+ stop() {
967
+ if (this.subscription) {
968
+ this.subscription.unsubscribe();
969
+ this.subscription = null;
970
+ }
971
+ }
972
+ /** Handle an incoming Core NATS request. */
973
+ async handleRequest(msg) {
974
+ const handler = this.patternRegistry.getHandler(msg.subject);
975
+ if (!handler) {
976
+ this.logger.warn(`No handler for Core RPC: ${msg.subject}`);
977
+ return;
978
+ }
979
+ this.eventBus.emit("messageRouted" /* MessageRouted */, msg.subject, "rpc");
980
+ let data;
981
+ try {
982
+ data = this.codec.decode(msg.data);
983
+ } catch (err) {
984
+ this.logger.error(`Decode error for Core RPC ${msg.subject}:`, err);
985
+ this.respondWithError(msg, err);
986
+ return;
987
+ }
988
+ const ctx = new RpcContext([msg]);
989
+ try {
990
+ const result = await unwrapResult(handler(data, ctx));
991
+ msg.respond(this.codec.encode(result));
992
+ } catch (err) {
993
+ this.logger.error(`Handler error for Core RPC ${msg.subject}:`, err);
994
+ this.respondWithError(msg, err);
995
+ }
996
+ }
997
+ /** Send an error response back to the caller with x-error header. */
998
+ respondWithError(msg, error) {
999
+ try {
1000
+ const hdrs = (0, import_nats5.headers)();
1001
+ hdrs.set("x-error" /* Error */, "true");
1002
+ msg.respond(this.codec.encode(serializeError(error)), { headers: hdrs });
1003
+ } catch {
1004
+ this.logger.error("Failed to encode error response");
1005
+ }
1006
+ }
1007
+ };
1008
+
1009
+ // src/server/infrastructure/stream.provider.ts
1010
+ var import_common5 = require("@nestjs/common");
1011
+ var STREAM_NOT_FOUND = 10059;
1012
+ var StreamProvider = class {
1013
+ constructor(options, connection) {
1014
+ this.options = options;
1015
+ this.connection = connection;
1016
+ }
1017
+ logger = new import_common5.Logger("Jetstream:Stream");
1018
+ /**
1019
+ * Ensure all required streams exist with correct configuration.
1020
+ *
1021
+ * @param kinds Which stream kinds to create. Determined by the module based
1022
+ * on RPC mode and registered handler patterns.
1023
+ */
1024
+ async ensureStreams(kinds) {
1025
+ const jsm = await this.connection.getJetStreamManager();
1026
+ await Promise.all(kinds.map((kind) => this.ensureStream(jsm, kind)));
1027
+ }
1028
+ /** Get the stream name for a given kind. */
1029
+ getStreamName(kind) {
1030
+ return streamName(this.options.name, kind);
1031
+ }
1032
+ /** Get the subjects pattern for a given kind. */
1033
+ getSubjects(kind) {
1034
+ const name = internalName(this.options.name);
1035
+ switch (kind) {
1036
+ case "ev":
1037
+ return [`${name}.ev.>`];
1038
+ case "cmd":
1039
+ return [`${name}.cmd.>`];
1040
+ case "broadcast":
1041
+ return ["broadcast.>"];
1042
+ }
1043
+ }
1044
+ /** Ensure a single stream exists, creating or updating as needed. */
1045
+ async ensureStream(jsm, kind) {
1046
+ const config = this.buildConfig(kind);
1047
+ this.logger.log(`Ensuring stream: ${config.name}`);
1048
+ try {
1049
+ await jsm.streams.info(config.name);
1050
+ this.logger.debug(`Stream exists, updating: ${config.name}`);
1051
+ return await jsm.streams.update(config.name, config);
1052
+ } catch (err) {
1053
+ const natsErr = err;
1054
+ if (natsErr.api_error?.err_code === STREAM_NOT_FOUND) {
1055
+ this.logger.log(`Creating stream: ${config.name}`);
1056
+ return await jsm.streams.add(config);
1057
+ }
1058
+ throw err;
1059
+ }
1060
+ }
1061
+ /** Build the full stream config by merging defaults with user overrides. */
1062
+ buildConfig(kind) {
1063
+ const name = this.getStreamName(kind);
1064
+ const subjects = this.getSubjects(kind);
1065
+ const description = `JetStream ${kind} stream for ${this.options.name}`;
1066
+ const defaults = this.getDefaults(kind);
1067
+ const overrides = this.getOverrides(kind);
1068
+ return {
1069
+ ...defaults,
1070
+ ...overrides,
1071
+ name,
1072
+ subjects,
1073
+ description
1074
+ };
1075
+ }
1076
+ /** Get default config for a stream kind. */
1077
+ getDefaults(kind) {
1078
+ switch (kind) {
1079
+ case "ev":
1080
+ return DEFAULT_EVENT_STREAM_CONFIG;
1081
+ case "cmd":
1082
+ return DEFAULT_COMMAND_STREAM_CONFIG;
1083
+ case "broadcast":
1084
+ return DEFAULT_BROADCAST_STREAM_CONFIG;
1085
+ }
1086
+ }
1087
+ /** Get user-provided overrides for a stream kind. */
1088
+ getOverrides(kind) {
1089
+ switch (kind) {
1090
+ case "ev":
1091
+ return this.options.events?.stream ?? {};
1092
+ case "cmd":
1093
+ return this.options.rpc?.mode === "jetstream" ? this.options.rpc.stream ?? {} : {};
1094
+ case "broadcast":
1095
+ return this.options.broadcast?.stream ?? {};
1096
+ }
1097
+ }
1098
+ };
1099
+
1100
+ // src/server/infrastructure/consumer.provider.ts
1101
+ var import_common6 = require("@nestjs/common");
1102
+ var CONSUMER_NOT_FOUND = 10014;
1103
+ var ConsumerProvider = class {
1104
+ constructor(options, connection, streamProvider, patternRegistry) {
1105
+ this.options = options;
1106
+ this.connection = connection;
1107
+ this.streamProvider = streamProvider;
1108
+ this.patternRegistry = patternRegistry;
1109
+ }
1110
+ logger = new import_common6.Logger("Jetstream:Consumer");
1111
+ /**
1112
+ * Ensure consumers exist for the specified kinds.
1113
+ *
1114
+ * @returns Map of kind -> ConsumerInfo for downstream use.
1115
+ */
1116
+ async ensureConsumers(kinds) {
1117
+ const jsm = await this.connection.getJetStreamManager();
1118
+ const results = /* @__PURE__ */ new Map();
1119
+ await Promise.all(
1120
+ kinds.map(async (kind) => {
1121
+ const info = await this.ensureConsumer(jsm, kind);
1122
+ results.set(kind, info);
1123
+ })
1124
+ );
1125
+ return results;
1126
+ }
1127
+ /** Get the consumer name for a given kind. */
1128
+ getConsumerName(kind) {
1129
+ return consumerName(this.options.name, kind);
1130
+ }
1131
+ /** Ensure a single consumer exists, creating if needed. */
1132
+ async ensureConsumer(jsm, kind) {
1133
+ const stream = this.streamProvider.getStreamName(kind);
1134
+ const config = this.buildConfig(kind);
1135
+ const name = config.durable_name;
1136
+ this.logger.log(`Ensuring consumer: ${name} on stream: ${stream}`);
1137
+ try {
1138
+ const info = await jsm.consumers.info(stream, name);
1139
+ this.logger.debug(`Consumer exists: ${name}`);
1140
+ return info;
1141
+ } catch (err) {
1142
+ const natsErr = err;
1143
+ if (natsErr.api_error?.err_code === CONSUMER_NOT_FOUND) {
1144
+ this.logger.log(`Creating consumer: ${name}`);
1145
+ return await jsm.consumers.add(stream, config);
1146
+ }
1147
+ throw err;
1148
+ }
1149
+ }
1150
+ /** Build consumer config by merging defaults with user overrides. */
1151
+ // eslint-disable-next-line @typescript-eslint/naming-convention -- NATS API uses snake_case
1152
+ buildConfig(kind) {
1153
+ const name = this.getConsumerName(kind);
1154
+ const serviceName = internalName(this.options.name);
1155
+ const defaults = this.getDefaults(kind);
1156
+ const overrides = this.getOverrides(kind);
1157
+ if (kind === "broadcast") {
1158
+ const broadcastPatterns = this.patternRegistry.getBroadcastPatterns();
1159
+ if (broadcastPatterns.length === 1) {
1160
+ return {
1161
+ ...defaults,
1162
+ ...overrides,
1163
+ name,
1164
+ durable_name: name,
1165
+ filter_subject: broadcastPatterns[0]
1166
+ };
1167
+ }
1168
+ return {
1169
+ ...defaults,
1170
+ ...overrides,
1171
+ name,
1172
+ durable_name: name,
1173
+ filter_subjects: broadcastPatterns
1174
+ };
1175
+ }
1176
+ const filter_subject = kind === "ev" ? `${serviceName}.ev.>` : `${serviceName}.cmd.>`;
1177
+ return {
1178
+ ...defaults,
1179
+ ...overrides,
1180
+ name,
1181
+ durable_name: name,
1182
+ filter_subject
1183
+ };
1184
+ }
1185
+ /** Get default config for a consumer kind. */
1186
+ getDefaults(kind) {
1187
+ switch (kind) {
1188
+ case "ev":
1189
+ return DEFAULT_EVENT_CONSUMER_CONFIG;
1190
+ case "cmd":
1191
+ return DEFAULT_COMMAND_CONSUMER_CONFIG;
1192
+ case "broadcast":
1193
+ return DEFAULT_BROADCAST_CONSUMER_CONFIG;
1194
+ }
1195
+ }
1196
+ /** Get user-provided overrides for a consumer kind. */
1197
+ getOverrides(kind) {
1198
+ switch (kind) {
1199
+ case "ev":
1200
+ return this.options.events?.consumer ?? {};
1201
+ case "cmd":
1202
+ return this.options.rpc?.mode === "jetstream" ? this.options.rpc.consumer ?? {} : {};
1203
+ case "broadcast":
1204
+ return this.options.broadcast?.consumer ?? {};
1205
+ }
1206
+ }
1207
+ };
1208
+
1209
+ // src/server/infrastructure/message.provider.ts
1210
+ var import_common7 = require("@nestjs/common");
1211
+ var import_rxjs3 = require("rxjs");
1212
+ var MessageProvider = class {
1213
+ constructor(connection, eventBus) {
1214
+ this.connection = connection;
1215
+ this.eventBus = eventBus;
1216
+ }
1217
+ logger = new import_common7.Logger("Jetstream:Message");
1218
+ destroy$ = new import_rxjs3.Subject();
1219
+ eventMessages$ = new import_rxjs3.Subject();
1220
+ commandMessages$ = new import_rxjs3.Subject();
1221
+ broadcastMessages$ = new import_rxjs3.Subject();
1222
+ /** Observable stream of workqueue event messages. */
1223
+ get events$() {
1224
+ return this.eventMessages$.asObservable();
1225
+ }
1226
+ /** Observable stream of RPC command messages (jetstream mode). */
1227
+ get commands$() {
1228
+ return this.commandMessages$.asObservable();
1229
+ }
1230
+ /** Observable stream of broadcast event messages. */
1231
+ get broadcasts$() {
1232
+ return this.broadcastMessages$.asObservable();
1233
+ }
1234
+ /**
1235
+ * Start consuming messages from the given consumer infos.
1236
+ *
1237
+ * Each consumer gets an independent self-healing flow.
1238
+ * Call `destroy()` to stop all consumers.
1239
+ */
1240
+ start(consumers) {
1241
+ const flows = [];
1242
+ for (const [kind, info] of consumers) {
1243
+ flows.push(this.createFlow(kind, info));
1244
+ }
1245
+ if (flows.length > 0) {
1246
+ (0, import_rxjs3.merge)(...flows).pipe((0, import_rxjs3.takeUntil)(this.destroy$)).subscribe();
1247
+ }
1248
+ }
1249
+ /** Stop all consumer flows and complete all subjects. */
1250
+ destroy() {
1251
+ this.destroy$.next();
1252
+ this.destroy$.complete();
1253
+ this.eventMessages$.complete();
1254
+ this.commandMessages$.complete();
1255
+ this.broadcastMessages$.complete();
1256
+ }
1257
+ /** Create a self-healing consumer flow for a specific kind. */
1258
+ createFlow(kind, info) {
1259
+ const target$ = this.getTargetSubject(kind);
1260
+ return (0, import_rxjs3.defer)(() => this.consumeOnce(info, target$)).pipe(
1261
+ (0, import_rxjs3.catchError)((err) => {
1262
+ this.logger.error(`Consumer ${info.name} error, will restart:`, err);
1263
+ this.eventBus.emit(
1264
+ "error" /* Error */,
1265
+ err instanceof Error ? err : new Error(String(err)),
1266
+ "message-provider"
1267
+ );
1268
+ return import_rxjs3.EMPTY;
1269
+ }),
1270
+ (0, import_rxjs3.repeat)({
1271
+ delay: () => {
1272
+ this.logger.warn(`Consumer ${info.name} stream ended, restarting...`);
1273
+ this.eventBus.emit(
1274
+ "error" /* Error */,
1275
+ new Error(`Consumer ${info.name} stream ended`),
1276
+ "message-provider"
1277
+ );
1278
+ return (0, import_rxjs3.timer)(100);
1279
+ }
1280
+ }),
1281
+ (0, import_rxjs3.takeUntil)(this.destroy$)
1282
+ );
1283
+ }
1284
+ /** Single iteration: get consumer -> pull messages -> emit to subject. */
1285
+ async consumeOnce(info, target$) {
1286
+ const js = (await this.connection.getConnection()).jetstream();
1287
+ const consumer = await js.consumers.get(info.stream_name, info.name);
1288
+ const messages = await consumer.consume();
1289
+ for await (const msg of messages) {
1290
+ target$.next(msg);
1291
+ }
1292
+ }
1293
+ /** Get the target subject for a consumer kind. */
1294
+ getTargetSubject(kind) {
1295
+ switch (kind) {
1296
+ case "ev":
1297
+ return this.eventMessages$;
1298
+ case "cmd":
1299
+ return this.commandMessages$;
1300
+ case "broadcast":
1301
+ return this.broadcastMessages$;
1302
+ }
1303
+ }
1304
+ };
1305
+
1306
+ // src/server/routing/pattern-registry.ts
1307
+ var import_common8 = require("@nestjs/common");
1308
+ var PatternRegistry = class {
1309
+ constructor(options) {
1310
+ this.options = options;
1311
+ }
1312
+ logger = new import_common8.Logger("Jetstream:PatternRegistry");
1313
+ registry = /* @__PURE__ */ new Map();
1314
+ /**
1315
+ * Register all handlers from the NestJS strategy.
1316
+ *
1317
+ * @param handlers Map of pattern -> MessageHandler from `Server.getHandlers()`.
1318
+ */
1319
+ registerHandlers(handlers) {
1320
+ const serviceName = this.options.name;
1321
+ for (const [pattern, handler] of handlers) {
1322
+ const isEvent = handler.isEventHandler ?? false;
1323
+ const isBroadcast = !!handler.extras?.broadcast;
1324
+ let fullSubject;
1325
+ if (isBroadcast) {
1326
+ fullSubject = buildBroadcastSubject(pattern);
1327
+ } else if (isEvent) {
1328
+ fullSubject = buildSubject(serviceName, "ev", pattern);
1329
+ } else {
1330
+ fullSubject = buildSubject(serviceName, "cmd", pattern);
1331
+ }
1332
+ this.registry.set(fullSubject, {
1333
+ handler,
1334
+ pattern,
1335
+ isEvent,
1336
+ isBroadcast
1337
+ });
1338
+ let kind;
1339
+ if (isBroadcast) {
1340
+ kind = "broadcast";
1341
+ } else if (isEvent) {
1342
+ kind = "event";
1343
+ } else {
1344
+ kind = "rpc";
1345
+ }
1346
+ this.logger.debug(`Registered ${kind}: ${pattern} -> ${fullSubject}`);
1347
+ }
1348
+ this.logSummary();
1349
+ }
1350
+ /** Find handler for a full NATS subject. */
1351
+ getHandler(subject) {
1352
+ return this.registry.get(subject)?.handler ?? null;
1353
+ }
1354
+ /** Get all registered broadcast patterns (for consumer filter_subject setup). */
1355
+ getBroadcastPatterns() {
1356
+ return Array.from(this.registry.values()).filter((r) => r.isBroadcast).map((r) => `broadcast.${r.pattern}`);
1357
+ }
1358
+ /** Check if any broadcast handlers are registered. */
1359
+ hasBroadcastHandlers() {
1360
+ return Array.from(this.registry.values()).some((r) => r.isBroadcast);
1361
+ }
1362
+ /** Check if any RPC (command) handlers are registered. */
1363
+ hasRpcHandlers() {
1364
+ return Array.from(this.registry.values()).some((r) => !r.isEvent && !r.isBroadcast);
1365
+ }
1366
+ /** Check if any workqueue event handlers are registered. */
1367
+ hasEventHandlers() {
1368
+ return Array.from(this.registry.values()).some((r) => r.isEvent && !r.isBroadcast);
1369
+ }
1370
+ /** Get patterns grouped by kind. */
1371
+ getPatternsByKind() {
1372
+ const events = [];
1373
+ const commands = [];
1374
+ const broadcasts = [];
1375
+ for (const entry of this.registry.values()) {
1376
+ if (entry.isBroadcast) broadcasts.push(entry.pattern);
1377
+ else if (entry.isEvent) events.push(entry.pattern);
1378
+ else commands.push(entry.pattern);
1379
+ }
1380
+ return { events, commands, broadcasts };
1381
+ }
1382
+ /** Normalize a full NATS subject back to the user-facing pattern. */
1383
+ normalizeSubject(subject) {
1384
+ const name = internalName(this.options.name);
1385
+ const prefixes = [`${name}.cmd.`, `${name}.ev.`, "broadcast."];
1386
+ for (const prefix of prefixes) {
1387
+ if (subject.startsWith(prefix)) {
1388
+ return subject.slice(prefix.length);
1389
+ }
1390
+ }
1391
+ return subject;
1392
+ }
1393
+ /** Log a summary of all registered handlers. */
1394
+ logSummary() {
1395
+ const { events, commands, broadcasts } = this.getPatternsByKind();
1396
+ this.logger.log(
1397
+ `Registered handlers: ${commands.length} RPC, ${events.length} events, ${broadcasts.length} broadcasts`
1398
+ );
1399
+ }
1400
+ };
1401
+
1402
+ // src/server/routing/event.router.ts
1403
+ var import_common9 = require("@nestjs/common");
1404
+ var import_rxjs4 = require("rxjs");
1405
+ var EventRouter = class {
1406
+ constructor(messageProvider, patternRegistry, codec, eventBus, deadLetterConfig) {
1407
+ this.messageProvider = messageProvider;
1408
+ this.patternRegistry = patternRegistry;
1409
+ this.codec = codec;
1410
+ this.eventBus = eventBus;
1411
+ this.deadLetterConfig = deadLetterConfig;
1412
+ }
1413
+ logger = new import_common9.Logger("Jetstream:EventRouter");
1414
+ subscriptions = [];
1415
+ /** Start routing event and broadcast messages to handlers. */
1416
+ start() {
1417
+ this.subscribeToStream(this.messageProvider.events$, "workqueue");
1418
+ this.subscribeToStream(this.messageProvider.broadcasts$, "broadcast");
1419
+ }
1420
+ /** Stop routing and unsubscribe from all streams. */
1421
+ destroy() {
1422
+ for (const sub of this.subscriptions) {
1423
+ sub.unsubscribe();
1424
+ }
1425
+ this.subscriptions.length = 0;
1426
+ }
1427
+ /** Subscribe to a message stream and route each message. */
1428
+ subscribeToStream(stream$, label) {
1429
+ const subscription = stream$.pipe(
1430
+ (0, import_rxjs4.mergeMap)((msg) => this.handle(msg)),
1431
+ (0, import_rxjs4.catchError)((err, caught) => {
1432
+ this.logger.error(`Unexpected error in ${label} event router`, err);
1433
+ return caught;
1434
+ })
1435
+ ).subscribe();
1436
+ this.subscriptions.push(subscription);
1437
+ }
1438
+ /** Handle a single event message: decode -> execute handler -> ack/nak. */
1439
+ handle(msg) {
1440
+ const handler = this.patternRegistry.getHandler(msg.subject);
1441
+ if (!handler) {
1442
+ msg.term(`No handler for event: ${msg.subject}`);
1443
+ this.logger.error(`No handler for event subject: ${msg.subject}`);
1444
+ return import_rxjs4.EMPTY;
1445
+ }
1446
+ let data;
1447
+ try {
1448
+ data = this.codec.decode(msg.data);
1449
+ } catch (err) {
1450
+ msg.term("Decode error");
1451
+ this.logger.error(`Decode error for ${msg.subject}:`, err);
1452
+ return import_rxjs4.EMPTY;
1453
+ }
1454
+ this.eventBus.emit("messageRouted" /* MessageRouted */, msg.subject, "event");
1455
+ const ctx = new RpcContext([msg]);
1456
+ return (0, import_rxjs4.from)(this.executeHandler(handler, data, ctx, msg));
1457
+ }
1458
+ /** Execute handler, then ack on success or nak/dead-letter on failure. */
1459
+ async executeHandler(handler, data, ctx, msg) {
1460
+ try {
1461
+ const result = await handler(data, ctx);
1462
+ if ((0, import_rxjs4.isObservable)(result)) {
1463
+ await (0, import_rxjs4.lastValueFrom)(result, { defaultValue: void 0 });
1464
+ }
1465
+ msg.ack();
1466
+ } catch (err) {
1467
+ this.logger.error(`Event handler error (${msg.subject}):`, err);
1468
+ if (this.isDeadLetter(msg)) {
1469
+ await this.handleDeadLetter(msg, data, err);
1470
+ } else {
1471
+ msg.nak();
1472
+ }
1473
+ }
1474
+ }
1475
+ /** Check if the message has exhausted all delivery attempts. */
1476
+ isDeadLetter(msg) {
1477
+ if (!this.deadLetterConfig) return false;
1478
+ const maxDeliver = this.deadLetterConfig.maxDeliverByStream.get(msg.info.stream);
1479
+ if (maxDeliver === void 0) return false;
1480
+ return msg.info.deliveryCount >= maxDeliver;
1481
+ }
1482
+ /** Handle a dead letter: invoke callback, then term or nak based on result. */
1483
+ async handleDeadLetter(msg, data, error) {
1484
+ const info = {
1485
+ subject: msg.subject,
1486
+ data,
1487
+ headers: msg.headers,
1488
+ error,
1489
+ deliveryCount: msg.info.deliveryCount,
1490
+ stream: msg.info.stream,
1491
+ streamSequence: msg.info.streamSequence,
1492
+ timestamp: new Date(msg.info.timestampNanos / 1e6).toISOString()
1493
+ };
1494
+ this.eventBus.emit("deadLetter" /* DeadLetter */, info);
1495
+ if (!this.deadLetterConfig) return;
1496
+ try {
1497
+ await this.deadLetterConfig.onDeadLetter(info);
1498
+ msg.term("Dead letter processed");
1499
+ } catch (hookErr) {
1500
+ this.logger.error(`onDeadLetter callback failed for ${msg.subject}, nak for retry:`, hookErr);
1501
+ msg.nak();
1502
+ }
1503
+ }
1504
+ };
1505
+
1506
+ // src/server/routing/rpc.router.ts
1507
+ var import_common10 = require("@nestjs/common");
1508
+ var import_nats6 = require("nats");
1509
+ var import_rxjs5 = require("rxjs");
1510
+ var RpcRouter = class {
1511
+ constructor(messageProvider, patternRegistry, connection, codec, eventBus, timeout) {
1512
+ this.messageProvider = messageProvider;
1513
+ this.patternRegistry = patternRegistry;
1514
+ this.connection = connection;
1515
+ this.codec = codec;
1516
+ this.eventBus = eventBus;
1517
+ this.timeout = timeout ?? DEFAULT_JETSTREAM_RPC_TIMEOUT;
1518
+ }
1519
+ logger = new import_common10.Logger("Jetstream:RpcRouter");
1520
+ timeout;
1521
+ subscription = null;
1522
+ /** Start routing command messages to handlers. */
1523
+ start() {
1524
+ this.subscription = this.messageProvider.commands$.pipe(
1525
+ (0, import_rxjs5.mergeMap)((msg) => this.handle(msg)),
1526
+ (0, import_rxjs5.catchError)((err, caught) => {
1527
+ this.logger.error("Unexpected error in RPC router", err);
1528
+ return caught;
1529
+ })
1530
+ ).subscribe();
1531
+ }
1532
+ /** Stop routing and unsubscribe. */
1533
+ destroy() {
1534
+ this.subscription?.unsubscribe();
1535
+ this.subscription = null;
1536
+ }
1537
+ /** Handle a single RPC command message. */
1538
+ handle(msg) {
1539
+ const handler = this.patternRegistry.getHandler(msg.subject);
1540
+ if (!handler) {
1541
+ msg.term(`No handler for RPC: ${msg.subject}`);
1542
+ this.logger.error(`No handler for RPC subject: ${msg.subject}`);
1543
+ return import_rxjs5.EMPTY;
1544
+ }
1545
+ const replyTo = msg.headers?.get("x-reply-to" /* ReplyTo */);
1546
+ const correlationId = msg.headers?.get("x-correlation-id" /* CorrelationId */);
1547
+ if (!replyTo || !correlationId) {
1548
+ msg.term("Missing required headers (reply-to or correlation-id)");
1549
+ this.logger.error(`Missing headers for RPC: ${msg.subject}`);
1550
+ return import_rxjs5.EMPTY;
1551
+ }
1552
+ let data;
1553
+ try {
1554
+ data = this.codec.decode(msg.data);
1555
+ } catch (err) {
1556
+ msg.term("Decode error");
1557
+ this.logger.error(`Decode error for RPC ${msg.subject}:`, err);
1558
+ return import_rxjs5.EMPTY;
1559
+ }
1560
+ this.eventBus.emit("messageRouted" /* MessageRouted */, msg.subject, "rpc");
1561
+ return (0, import_rxjs5.from)(this.executeHandler(handler, data, msg, replyTo, correlationId));
1562
+ }
1563
+ /** Execute handler, publish response, settle message. */
1564
+ async executeHandler(handler, data, msg, replyTo, correlationId) {
1565
+ const nc = await this.connection.getConnection();
1566
+ const ctx = new RpcContext([msg]);
1567
+ const hdrs = (0, import_nats6.headers)();
1568
+ hdrs.set("x-correlation-id" /* CorrelationId */, correlationId);
1569
+ let settled = false;
1570
+ const timeoutId = setTimeout(() => {
1571
+ if (settled) return;
1572
+ settled = true;
1573
+ this.logger.error(`RPC timeout (${this.timeout}ms): ${msg.subject}`);
1574
+ this.eventBus.emit("rpcTimeout" /* RpcTimeout */, msg.subject, correlationId);
1575
+ msg.term("Handler timeout");
1576
+ }, this.timeout);
1577
+ try {
1578
+ const result = await unwrapResult(handler(data, ctx));
1579
+ if (settled) return;
1580
+ settled = true;
1581
+ clearTimeout(timeoutId);
1582
+ nc.publish(replyTo, this.codec.encode(result), { headers: hdrs });
1583
+ msg.ack();
1584
+ } catch (err) {
1585
+ if (settled) return;
1586
+ settled = true;
1587
+ clearTimeout(timeoutId);
1588
+ try {
1589
+ hdrs.set("x-error" /* Error */, "true");
1590
+ nc.publish(replyTo, this.codec.encode(serializeError(err)), { headers: hdrs });
1591
+ } catch (encodeErr) {
1592
+ this.logger.error(`Failed to encode RPC error for ${msg.subject}`, encodeErr);
1593
+ }
1594
+ msg.term(`Handler error: ${msg.subject}`);
1595
+ }
1596
+ }
1597
+ };
1598
+
1599
+ // src/shutdown/shutdown.manager.ts
1600
+ var import_common11 = require("@nestjs/common");
1601
+ var ShutdownManager = class {
1602
+ constructor(connection, eventBus, timeout) {
1603
+ this.connection = connection;
1604
+ this.eventBus = eventBus;
1605
+ this.timeout = timeout;
1606
+ }
1607
+ logger = new import_common11.Logger("Jetstream:Shutdown");
1608
+ /**
1609
+ * Execute the full shutdown sequence.
1610
+ *
1611
+ * @param strategy Optional strategy to close (stops consumers and subscriptions).
1612
+ */
1613
+ async shutdown(strategy) {
1614
+ this.eventBus.emit("shutdownStart" /* ShutdownStart */);
1615
+ this.logger.log(`Graceful shutdown started (timeout: ${this.timeout}ms)`);
1616
+ strategy?.close();
1617
+ await Promise.race([
1618
+ this.connection.shutdown(),
1619
+ new Promise((resolve) => setTimeout(resolve, this.timeout))
1620
+ ]);
1621
+ this.eventBus.emit("shutdownComplete" /* ShutdownComplete */);
1622
+ this.logger.log("Graceful shutdown complete");
1623
+ }
1624
+ };
1625
+
1626
+ // src/jetstream.module.ts
1627
+ var JetstreamModule = class {
1628
+ constructor(shutdownManager, strategy) {
1629
+ this.shutdownManager = shutdownManager;
1630
+ this.strategy = strategy;
1631
+ }
1632
+ // -------------------------------------------------------------------
1633
+ // forRoot — global module registration
1634
+ // -------------------------------------------------------------------
1635
+ /**
1636
+ * Register the JetStream transport globally.
1637
+ *
1638
+ * Creates a shared NATS connection, codec, event bus, and optionally
1639
+ * the full consumer infrastructure (streams, consumers, routers).
1640
+ *
1641
+ * @param options Module configuration.
1642
+ * @returns Dynamic module ready to be imported.
1643
+ */
1644
+ static forRoot(options) {
1645
+ const providers = this.createCoreProviders(options);
1646
+ return {
1647
+ module: JetstreamModule,
1648
+ global: true,
1649
+ providers,
1650
+ exports: [
1651
+ JETSTREAM_CONNECTION,
1652
+ JETSTREAM_CODEC,
1653
+ JETSTREAM_EVENT_BUS,
1654
+ JETSTREAM_OPTIONS,
1655
+ ShutdownManager,
1656
+ JetstreamStrategy,
1657
+ JetstreamHealthIndicator
1658
+ ]
1659
+ };
1660
+ }
1661
+ // -------------------------------------------------------------------
1662
+ // forRootAsync — async global module registration
1663
+ // -------------------------------------------------------------------
1664
+ /**
1665
+ * Register the JetStream transport globally with async configuration.
1666
+ *
1667
+ * Supports `useFactory`, `useExisting`, and `useClass` patterns
1668
+ * for loading configuration from ConfigService, environment, etc.
1669
+ *
1670
+ * @param asyncOptions Async configuration.
1671
+ * @returns Dynamic module ready to be imported.
1672
+ */
1673
+ static forRootAsync(asyncOptions) {
1674
+ const asyncProviders = this.createAsyncOptionsProvider(asyncOptions);
1675
+ const coreProviders = this.createCoreDependentProviders();
1676
+ return {
1677
+ module: JetstreamModule,
1678
+ global: true,
1679
+ imports: asyncOptions.imports ?? [],
1680
+ providers: [...asyncProviders, ...coreProviders],
1681
+ exports: [
1682
+ JETSTREAM_CONNECTION,
1683
+ JETSTREAM_CODEC,
1684
+ JETSTREAM_EVENT_BUS,
1685
+ JETSTREAM_OPTIONS,
1686
+ ShutdownManager,
1687
+ JetstreamStrategy,
1688
+ JetstreamHealthIndicator
1689
+ ]
1690
+ };
1691
+ }
1692
+ // -------------------------------------------------------------------
1693
+ // forFeature — per-module client registration
1694
+ // -------------------------------------------------------------------
1695
+ /**
1696
+ * Register a lightweight client proxy for a target service.
1697
+ *
1698
+ * Reuses the shared NATS connection from `forRoot()`.
1699
+ * Import in each feature module that needs to communicate with a specific service.
1700
+ *
1701
+ * @param options Feature options with target service name.
1702
+ * @returns Dynamic module with the client provider.
1703
+ */
1704
+ static forFeature(options) {
1705
+ const clientToken = getClientToken(options.name);
1706
+ const clientProvider = {
1707
+ provide: clientToken,
1708
+ inject: [JETSTREAM_OPTIONS, JETSTREAM_CONNECTION, JETSTREAM_CODEC, JETSTREAM_EVENT_BUS],
1709
+ useFactory: (rootOptions, connection, rootCodec, eventBus) => {
1710
+ const codec = options.codec ?? rootCodec;
1711
+ return new JetstreamClient(rootOptions, options.name, connection, codec, eventBus);
1712
+ }
1713
+ };
1714
+ return {
1715
+ module: JetstreamModule,
1716
+ providers: [clientProvider],
1717
+ exports: [clientToken]
1718
+ };
1719
+ }
1720
+ // -------------------------------------------------------------------
1721
+ // Provider factories
1722
+ // -------------------------------------------------------------------
1723
+ /** Create all providers for synchronous forRoot(). */
1724
+ /**
1725
+ * Build a map of stream name -> max_deliver for dead letter detection.
1726
+ * Each stream kind (ev, broadcast) has its own consumer config with potentially
1727
+ * different max_deliver values.
1728
+ */
1729
+ static buildMaxDeliverMap(options) {
1730
+ const map = /* @__PURE__ */ new Map();
1731
+ const defaultEventMax = DEFAULT_EVENT_CONSUMER_CONFIG.max_deliver ?? 3;
1732
+ const defaultBroadcastMax = DEFAULT_BROADCAST_CONSUMER_CONFIG.max_deliver ?? 3;
1733
+ map.set(
1734
+ streamName(options.name, "ev"),
1735
+ options.events?.consumer?.max_deliver ?? defaultEventMax
1736
+ );
1737
+ map.set(
1738
+ streamName(options.name, "broadcast"),
1739
+ options.broadcast?.consumer?.max_deliver ?? defaultBroadcastMax
1740
+ );
1741
+ return map;
1742
+ }
1743
+ static createCoreProviders(options) {
1744
+ return [
1745
+ {
1746
+ provide: JETSTREAM_OPTIONS,
1747
+ useValue: options
1748
+ },
1749
+ ...this.createCoreDependentProviders()
1750
+ ];
1751
+ }
1752
+ /** Create providers that depend on JETSTREAM_OPTIONS (shared by sync and async). */
1753
+ static createCoreDependentProviders() {
1754
+ return [
1755
+ // EventBus — hook system with Logger fallback
1756
+ {
1757
+ provide: JETSTREAM_EVENT_BUS,
1758
+ inject: [JETSTREAM_OPTIONS],
1759
+ useFactory: (options) => {
1760
+ const logger = new import_common12.Logger("Jetstream:Module");
1761
+ return new EventBus(logger, options.hooks);
1762
+ }
1763
+ },
1764
+ // Codec — global encode/decode
1765
+ {
1766
+ provide: JETSTREAM_CODEC,
1767
+ inject: [JETSTREAM_OPTIONS],
1768
+ useFactory: (options) => {
1769
+ return options.codec ?? new JsonCodec();
1770
+ }
1771
+ },
1772
+ // ConnectionProvider — single NATS connection
1773
+ {
1774
+ provide: JETSTREAM_CONNECTION,
1775
+ inject: [JETSTREAM_OPTIONS, JETSTREAM_EVENT_BUS],
1776
+ useFactory: (options, eventBus) => {
1777
+ return new ConnectionProvider(options, eventBus);
1778
+ }
1779
+ },
1780
+ // JetstreamHealthIndicator — health check for NATS connection
1781
+ {
1782
+ provide: JetstreamHealthIndicator,
1783
+ inject: [JETSTREAM_CONNECTION],
1784
+ useFactory: (connection) => {
1785
+ return new JetstreamHealthIndicator(connection);
1786
+ }
1787
+ },
1788
+ // ShutdownManager — graceful shutdown orchestration
1789
+ {
1790
+ provide: ShutdownManager,
1791
+ inject: [JETSTREAM_CONNECTION, JETSTREAM_EVENT_BUS, JETSTREAM_OPTIONS],
1792
+ useFactory: (connection, eventBus, options) => {
1793
+ return new ShutdownManager(
1794
+ connection,
1795
+ eventBus,
1796
+ options.shutdownTimeout ?? DEFAULT_SHUTDOWN_TIMEOUT
1797
+ );
1798
+ }
1799
+ },
1800
+ // ---------------------------------------------------------------
1801
+ // Consumer infrastructure — only created when consumer !== false.
1802
+ // Providers return null when consumer is disabled (publisher-only mode).
1803
+ // ---------------------------------------------------------------
1804
+ // PatternRegistry — subject-to-handler mapping
1805
+ {
1806
+ provide: PatternRegistry,
1807
+ inject: [JETSTREAM_OPTIONS],
1808
+ useFactory: (options) => {
1809
+ if (options.consumer === false) return null;
1810
+ return new PatternRegistry(options);
1811
+ }
1812
+ },
1813
+ // StreamProvider — JetStream stream lifecycle
1814
+ {
1815
+ provide: StreamProvider,
1816
+ inject: [JETSTREAM_OPTIONS, JETSTREAM_CONNECTION],
1817
+ useFactory: (options, connection) => {
1818
+ if (options.consumer === false) return null;
1819
+ return new StreamProvider(options, connection);
1820
+ }
1821
+ },
1822
+ // ConsumerProvider — JetStream consumer lifecycle (receives PatternRegistry for broadcast filtering)
1823
+ {
1824
+ provide: ConsumerProvider,
1825
+ inject: [JETSTREAM_OPTIONS, JETSTREAM_CONNECTION, StreamProvider, PatternRegistry],
1826
+ useFactory: (options, connection, streamProvider, patternRegistry) => {
1827
+ if (options.consumer === false) return null;
1828
+ return new ConsumerProvider(options, connection, streamProvider, patternRegistry);
1829
+ }
1830
+ },
1831
+ // MessageProvider — pull-based message consumption
1832
+ {
1833
+ provide: MessageProvider,
1834
+ inject: [JETSTREAM_OPTIONS, JETSTREAM_CONNECTION, JETSTREAM_EVENT_BUS],
1835
+ useFactory: (options, connection, eventBus) => {
1836
+ if (options.consumer === false) return null;
1837
+ return new MessageProvider(connection, eventBus);
1838
+ }
1839
+ },
1840
+ // EventRouter — routes event and broadcast messages to handlers
1841
+ {
1842
+ provide: EventRouter,
1843
+ inject: [
1844
+ JETSTREAM_OPTIONS,
1845
+ MessageProvider,
1846
+ PatternRegistry,
1847
+ JETSTREAM_CODEC,
1848
+ JETSTREAM_EVENT_BUS
1849
+ ],
1850
+ useFactory: (options, messageProvider, patternRegistry, codec, eventBus) => {
1851
+ if (options.consumer === false) return null;
1852
+ const deadLetterConfig = options.onDeadLetter ? {
1853
+ maxDeliverByStream: JetstreamModule.buildMaxDeliverMap(options),
1854
+ onDeadLetter: options.onDeadLetter
1855
+ } : void 0;
1856
+ return new EventRouter(
1857
+ messageProvider,
1858
+ patternRegistry,
1859
+ codec,
1860
+ eventBus,
1861
+ deadLetterConfig
1862
+ );
1863
+ }
1864
+ },
1865
+ // RpcRouter — routes RPC command messages in JetStream mode
1866
+ {
1867
+ provide: RpcRouter,
1868
+ inject: [
1869
+ JETSTREAM_OPTIONS,
1870
+ MessageProvider,
1871
+ PatternRegistry,
1872
+ JETSTREAM_CONNECTION,
1873
+ JETSTREAM_CODEC,
1874
+ JETSTREAM_EVENT_BUS
1875
+ ],
1876
+ useFactory: (options, messageProvider, patternRegistry, connection, codec, eventBus) => {
1877
+ if (options.consumer === false) return null;
1878
+ const timeout = options.rpc?.mode === "jetstream" ? options.rpc.timeout : void 0;
1879
+ return new RpcRouter(
1880
+ messageProvider,
1881
+ patternRegistry,
1882
+ connection,
1883
+ codec,
1884
+ eventBus,
1885
+ timeout
1886
+ );
1887
+ }
1888
+ },
1889
+ // CoreRpcServer — RPC via NATS Core request/reply
1890
+ {
1891
+ provide: CoreRpcServer,
1892
+ inject: [
1893
+ JETSTREAM_OPTIONS,
1894
+ JETSTREAM_CONNECTION,
1895
+ PatternRegistry,
1896
+ JETSTREAM_CODEC,
1897
+ JETSTREAM_EVENT_BUS
1898
+ ],
1899
+ useFactory: (options, connection, patternRegistry, codec, eventBus) => {
1900
+ if (options.consumer === false) return null;
1901
+ return new CoreRpcServer(options, connection, patternRegistry, codec, eventBus);
1902
+ }
1903
+ },
1904
+ // JetstreamStrategy — server-side transport (only when consumer enabled)
1905
+ {
1906
+ provide: JetstreamStrategy,
1907
+ inject: [
1908
+ JETSTREAM_OPTIONS,
1909
+ JETSTREAM_CONNECTION,
1910
+ PatternRegistry,
1911
+ StreamProvider,
1912
+ ConsumerProvider,
1913
+ MessageProvider,
1914
+ EventRouter,
1915
+ RpcRouter,
1916
+ CoreRpcServer
1917
+ ],
1918
+ useFactory: (options, connection, patternRegistry, streamProvider, consumerProvider, messageProvider, eventRouter, rpcRouter, coreRpcServer) => {
1919
+ if (options.consumer === false) return null;
1920
+ return new JetstreamStrategy(
1921
+ options,
1922
+ connection,
1923
+ patternRegistry,
1924
+ streamProvider,
1925
+ consumerProvider,
1926
+ messageProvider,
1927
+ eventRouter,
1928
+ rpcRouter,
1929
+ coreRpcServer
1930
+ );
1931
+ }
1932
+ }
1933
+ ];
1934
+ }
1935
+ /** Create async options provider from useFactory/useExisting/useClass. */
1936
+ static createAsyncOptionsProvider(asyncOptions) {
1937
+ if (asyncOptions.useFactory) {
1938
+ const factory = asyncOptions.useFactory;
1939
+ return [
1940
+ {
1941
+ provide: JETSTREAM_OPTIONS,
1942
+ useFactory: async (...args) => {
1943
+ const partial = await factory(...args);
1944
+ return { ...partial, name: asyncOptions.name };
1945
+ },
1946
+ inject: asyncOptions.inject ?? []
1947
+ }
1948
+ ];
1949
+ }
1950
+ if (asyncOptions.useExisting) {
1951
+ return [
1952
+ {
1953
+ provide: JETSTREAM_OPTIONS,
1954
+ useFactory: (config) => ({
1955
+ ...config,
1956
+ name: asyncOptions.name
1957
+ }),
1958
+ inject: [asyncOptions.useExisting]
1959
+ }
1960
+ ];
1961
+ }
1962
+ const useClass = asyncOptions.useClass;
1963
+ return [
1964
+ { provide: useClass, useClass },
1965
+ {
1966
+ provide: JETSTREAM_OPTIONS,
1967
+ useFactory: (config) => ({
1968
+ ...config,
1969
+ name: asyncOptions.name
1970
+ }),
1971
+ inject: [useClass]
1972
+ }
1973
+ ];
1974
+ }
1975
+ // -------------------------------------------------------------------
1976
+ // Lifecycle hooks
1977
+ // -------------------------------------------------------------------
1978
+ /**
1979
+ * Gracefully shut down the transport on application termination.
1980
+ */
1981
+ async onApplicationShutdown() {
1982
+ if (this.shutdownManager) {
1983
+ await this.shutdownManager.shutdown(this.strategy ?? void 0);
1984
+ }
1985
+ }
1986
+ };
1987
+ JetstreamModule = __decorateClass([
1988
+ (0, import_common12.Global)(),
1989
+ (0, import_common12.Module)({}),
1990
+ __decorateParam(0, (0, import_common12.Optional)()),
1991
+ __decorateParam(0, (0, import_common12.Inject)(ShutdownManager)),
1992
+ __decorateParam(1, (0, import_common12.Optional)()),
1993
+ __decorateParam(1, (0, import_common12.Inject)(JetstreamStrategy))
1994
+ ], JetstreamModule);
1995
+ // Annotate the CommonJS export names for ESM import in node:
1996
+ 0 && (module.exports = {
1997
+ EventBus,
1998
+ JETSTREAM_CODEC,
1999
+ JETSTREAM_CONNECTION,
2000
+ JETSTREAM_EVENT_BUS,
2001
+ JETSTREAM_OPTIONS,
2002
+ JetstreamClient,
2003
+ JetstreamHeader,
2004
+ JetstreamHealthIndicator,
2005
+ JetstreamModule,
2006
+ JetstreamRecord,
2007
+ JetstreamRecordBuilder,
2008
+ JetstreamStrategy,
2009
+ JsonCodec,
2010
+ RpcContext,
2011
+ TransportEvent,
2012
+ getClientToken,
2013
+ nanos
2014
+ });
2015
+ //# sourceMappingURL=index.cjs.map