@horizon-republic/nestjs-jetstream 2.9.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -7,11 +7,11 @@ var __export = (target, all) => {
7
7
  for (var name in all)
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
- var __copyProps = (to, from4, except, desc) => {
11
- if (from4 && typeof from4 === "object" || typeof from4 === "function") {
12
- for (let key of __getOwnPropNames(from4))
10
+ var __copyProps = (to, from2, except, desc) => {
11
+ if (from2 && typeof from2 === "object" || typeof from2 === "function") {
12
+ for (let key of __getOwnPropNames(from2))
13
13
  if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from4[key], enumerable: !(desc = __getOwnPropDesc(from4, key)) || desc.enumerable });
14
+ __defProp(to, key, { get: () => from2[key], enumerable: !(desc = __getOwnPropDesc(from2, key)) || desc.enumerable });
15
15
  }
16
16
  return to;
17
17
  };
@@ -29,14 +29,25 @@ var __decorateParam = (index, decorator) => (target, key) => decorator(target, k
29
29
  // src/index.ts
30
30
  var index_exports = {};
31
31
  __export(index_exports, {
32
+ ConsumeKind: () => ConsumeKind,
33
+ DEFAULT_BROADCAST_CONSUMER_CONFIG: () => DEFAULT_BROADCAST_CONSUMER_CONFIG,
34
+ DEFAULT_BROADCAST_STREAM_CONFIG: () => DEFAULT_BROADCAST_STREAM_CONFIG,
35
+ DEFAULT_COMMAND_CONSUMER_CONFIG: () => DEFAULT_COMMAND_CONSUMER_CONFIG,
36
+ DEFAULT_COMMAND_STREAM_CONFIG: () => DEFAULT_COMMAND_STREAM_CONFIG,
37
+ DEFAULT_DLQ_STREAM_CONFIG: () => DEFAULT_DLQ_STREAM_CONFIG,
38
+ DEFAULT_EVENT_CONSUMER_CONFIG: () => DEFAULT_EVENT_CONSUMER_CONFIG,
39
+ DEFAULT_EVENT_STREAM_CONFIG: () => DEFAULT_EVENT_STREAM_CONFIG,
40
+ DEFAULT_JETSTREAM_RPC_TIMEOUT: () => DEFAULT_JETSTREAM_RPC_TIMEOUT,
32
41
  DEFAULT_METADATA_BUCKET: () => DEFAULT_METADATA_BUCKET,
33
42
  DEFAULT_METADATA_HISTORY: () => DEFAULT_METADATA_HISTORY,
34
43
  DEFAULT_METADATA_REPLICAS: () => DEFAULT_METADATA_REPLICAS,
35
44
  DEFAULT_METADATA_TTL: () => DEFAULT_METADATA_TTL,
36
- EventBus: () => EventBus,
45
+ DEFAULT_ORDERED_STREAM_CONFIG: () => DEFAULT_ORDERED_STREAM_CONFIG,
46
+ DEFAULT_RPC_TIMEOUT: () => DEFAULT_RPC_TIMEOUT,
47
+ DEFAULT_SHUTDOWN_TIMEOUT: () => DEFAULT_SHUTDOWN_TIMEOUT,
48
+ DEFAULT_TRACES: () => DEFAULT_TRACES,
37
49
  JETSTREAM_CODEC: () => JETSTREAM_CODEC,
38
50
  JETSTREAM_CONNECTION: () => JETSTREAM_CONNECTION,
39
- JETSTREAM_EVENT_BUS: () => JETSTREAM_EVENT_BUS,
40
51
  JETSTREAM_OPTIONS: () => JETSTREAM_OPTIONS,
41
52
  JetstreamClient: () => JetstreamClient,
42
53
  JetstreamDlqHeader: () => JetstreamDlqHeader,
@@ -46,12 +57,18 @@ __export(index_exports, {
46
57
  JetstreamRecord: () => JetstreamRecord,
47
58
  JetstreamRecordBuilder: () => JetstreamRecordBuilder,
48
59
  JetstreamStrategy: () => JetstreamStrategy,
60
+ JetstreamTrace: () => JetstreamTrace,
49
61
  JsonCodec: () => JsonCodec,
50
62
  MIN_METADATA_TTL: () => MIN_METADATA_TTL,
51
63
  MessageKind: () => MessageKind,
64
+ MsgpackCodec: () => MsgpackCodec,
65
+ NatsErrorCode: () => NatsErrorCode,
52
66
  PatternPrefix: () => PatternPrefix,
67
+ PublishKind: () => PublishKind,
68
+ RESERVED_HEADERS: () => RESERVED_HEADERS,
53
69
  RpcContext: () => RpcContext,
54
70
  StreamKind: () => StreamKind,
71
+ TRACER_NAME: () => TRACER_NAME,
55
72
  TransportEvent: () => TransportEvent,
56
73
  buildBroadcastSubject: () => buildBroadcastSubject,
57
74
  buildSubject: () => buildSubject,
@@ -68,13 +85,14 @@ __export(index_exports, {
68
85
  module.exports = __toCommonJS(index_exports);
69
86
 
70
87
  // src/jetstream.module.ts
71
- var import_common14 = require("@nestjs/common");
88
+ var import_common18 = require("@nestjs/common");
72
89
 
73
90
  // src/client/jetstream.client.ts
74
- var import_common = require("@nestjs/common");
91
+ var import_common5 = require("@nestjs/common");
75
92
  var import_microservices = require("@nestjs/microservices");
76
- var import_transport_node = require("@nats-io/transport-node");
93
+ var import_api8 = require("@opentelemetry/api");
77
94
  var import_nuid = require("@nats-io/nuid");
95
+ var import_transport_node = require("@nats-io/transport-node");
78
96
 
79
97
  // src/interfaces/hooks.interface.ts
80
98
  var MessageKind = /* @__PURE__ */ ((MessageKind2) => {
@@ -264,6 +282,907 @@ var PatternPrefix = /* @__PURE__ */ ((PatternPrefix2) => {
264
282
  var isJetStreamRpcMode = (rpc) => rpc?.mode === "jetstream";
265
283
  var isCoreRpcMode = (rpc) => !rpc || rpc.mode === "core";
266
284
 
285
+ // src/otel/constants.ts
286
+ var TRACER_NAME = "@horizon-republic/nestjs-jetstream";
287
+
288
+ // src/otel/attribute-keys.ts
289
+ var ATTR_MESSAGING_SYSTEM = "messaging.system";
290
+ var ATTR_MESSAGING_DESTINATION_NAME = "messaging.destination.name";
291
+ var ATTR_MESSAGING_DESTINATION_TEMPLATE = "messaging.destination.template";
292
+ var ATTR_MESSAGING_CLIENT_ID = "messaging.client.id";
293
+ var ATTR_MESSAGING_OPERATION_NAME = "messaging.operation.name";
294
+ var ATTR_MESSAGING_OPERATION_TYPE = "messaging.operation.type";
295
+ var ATTR_MESSAGING_MESSAGE_BODY_SIZE = "messaging.message.body.size";
296
+ var ATTR_MESSAGING_MESSAGE_ID = "messaging.message.id";
297
+ var ATTR_MESSAGING_MESSAGE_CONVERSATION_ID = "messaging.message.conversation_id";
298
+ var ATTR_MESSAGING_CONSUMER_GROUP_NAME = "messaging.consumer.group.name";
299
+ var ATTR_MESSAGING_HEADER_PREFIX = "messaging.header.";
300
+ var ATTR_MESSAGING_NATS_STREAM_NAME = "messaging.nats.stream.name";
301
+ var ATTR_MESSAGING_NATS_STREAM_SEQUENCE = "messaging.nats.message.stream_sequence";
302
+ var ATTR_MESSAGING_NATS_CONSUMER_SEQUENCE = "messaging.nats.message.consumer_sequence";
303
+ var ATTR_MESSAGING_NATS_DELIVERY_COUNT = "messaging.nats.message.delivery_count";
304
+ var ATTR_MESSAGING_NATS_BODY = "messaging.nats.message.body";
305
+ var ATTR_MESSAGING_NATS_BODY_TRUNCATED = "messaging.nats.message.body.truncated";
306
+ var ATTR_SERVER_ADDRESS = "server.address";
307
+ var ATTR_SERVER_PORT = "server.port";
308
+ var ATTR_JETSTREAM_SERVICE_NAME = "jetstream.service.name";
309
+ var ATTR_JETSTREAM_KIND = "jetstream.kind";
310
+ var ATTR_JETSTREAM_RPC_REPLY_HAS_ERROR = "jetstream.rpc.reply.has_error";
311
+ var ATTR_JETSTREAM_RPC_REPLY_ERROR_CODE = "jetstream.rpc.reply.error.code";
312
+ var ATTR_JETSTREAM_PROVISIONING_ENTITY = "jetstream.provisioning.entity";
313
+ var ATTR_JETSTREAM_PROVISIONING_ACTION = "jetstream.provisioning.action";
314
+ var ATTR_JETSTREAM_PROVISIONING_NAME = "jetstream.provisioning.name";
315
+ var ATTR_JETSTREAM_SELF_HEALING_REASON = "jetstream.self_healing.reason";
316
+ var ATTR_JETSTREAM_MIGRATION_REASON = "jetstream.migration.reason";
317
+ var ATTR_JETSTREAM_DEAD_LETTER_REASON = "jetstream.dead_letter.reason";
318
+ var ATTR_JETSTREAM_SCHEDULE_TARGET = "jetstream.schedule.target";
319
+ var ATTR_NATS_CONNECTION_SERVER = "nats.connection.server";
320
+ var NATS_MSG_ID_HEADER = "Nats-Msg-Id";
321
+ var HOOK_PUBLISH = "publishHook";
322
+ var HOOK_CONSUME = "consumeHook";
323
+ var HOOK_RESPONSE = "responseHook";
324
+ var SPAN_NAME_PUBLISH = "publish";
325
+ var SPAN_NAME_PROCESS = "process";
326
+ var SPAN_NAME_SEND = "send";
327
+ var SPAN_NAME_DEAD_LETTER = "dead_letter";
328
+ var SPAN_NAME_NATS_CONNECTION = "nats.connection";
329
+ var SPAN_NAME_JETSTREAM_SHUTDOWN = "jetstream.shutdown";
330
+ var SPAN_NAME_JETSTREAM_SELF_HEALING = "jetstream.self_healing";
331
+ var SPAN_NAME_JETSTREAM_MIGRATION = "jetstream.migration";
332
+ var SPAN_NAME_JETSTREAM_PROVISIONING_PREFIX = "jetstream.provisioning.";
333
+ var EVENT_CONNECTION_DISCONNECTED = "connection.disconnected";
334
+ var EVENT_CONNECTION_RECONNECTED = "connection.reconnected";
335
+ var messagingHeaderAttr = (headerName) => `${ATTR_MESSAGING_HEADER_PREFIX}${headerName.toLowerCase()}`;
336
+
337
+ // src/otel/trace-kinds.ts
338
+ var JetstreamTrace = /* @__PURE__ */ ((JetstreamTrace2) => {
339
+ JetstreamTrace2["Publish"] = "publish";
340
+ JetstreamTrace2["Consume"] = "consume";
341
+ JetstreamTrace2["RpcClientSend"] = "rpc.client.send";
342
+ JetstreamTrace2["DeadLetter"] = "dead_letter";
343
+ JetstreamTrace2["ConnectionLifecycle"] = "connection.lifecycle";
344
+ JetstreamTrace2["SelfHealing"] = "self_healing";
345
+ JetstreamTrace2["Provisioning"] = "provisioning";
346
+ JetstreamTrace2["Migration"] = "migration";
347
+ JetstreamTrace2["Shutdown"] = "shutdown";
348
+ return JetstreamTrace2;
349
+ })(JetstreamTrace || {});
350
+ var DEFAULT_TRACES = [
351
+ "publish" /* Publish */,
352
+ "consume" /* Consume */,
353
+ "rpc.client.send" /* RpcClientSend */,
354
+ "dead_letter" /* DeadLetter */
355
+ ];
356
+
357
+ // src/otel/capture.ts
358
+ var NEGATION_PREFIX = "!";
359
+ var REGEX_SPECIAL_RE = /[.+?^${}()|[\]\\*]/gu;
360
+ var escapeForRegex = (input) => input.replace(REGEX_SPECIAL_RE, "\\$&");
361
+ var globToRegex = (glob) => {
362
+ const escaped = escapeForRegex(glob.toLowerCase()).replace(/\\\*/gu, ".*");
363
+ return new RegExp(`^${escaped}$`, "u");
364
+ };
365
+ var compileHeaderAllowlist = (allowlist) => {
366
+ if (allowlist === true) return () => true;
367
+ if (allowlist === false) return () => false;
368
+ if (allowlist.length === 0) return () => false;
369
+ const includes = [];
370
+ const excludes = [];
371
+ for (const pattern of allowlist) {
372
+ const isExclude = pattern.startsWith(NEGATION_PREFIX);
373
+ const body = isExclude ? pattern.slice(NEGATION_PREFIX.length) : pattern;
374
+ const regex = globToRegex(body);
375
+ if (isExclude) excludes.push(regex);
376
+ else includes.push(regex);
377
+ }
378
+ return (name) => {
379
+ const lower = name.toLowerCase();
380
+ if (!includes.some((re) => re.test(lower))) return false;
381
+ if (excludes.some((re) => re.test(lower))) return false;
382
+ return true;
383
+ };
384
+ };
385
+ var subjectMatcherCache = /* @__PURE__ */ new WeakMap();
386
+ var compileSubjectAllowlist = (allowlist) => {
387
+ if (!allowlist || allowlist.length === 0) return () => true;
388
+ const cached = subjectMatcherCache.get(allowlist);
389
+ if (cached) return cached;
390
+ const regexes = allowlist.map(globToRegex);
391
+ const matcher = (subject) => {
392
+ const lower = subject.toLowerCase();
393
+ return regexes.some((re) => re.test(lower));
394
+ };
395
+ subjectMatcherCache.set(allowlist, matcher);
396
+ return matcher;
397
+ };
398
+ var subjectMatchesAllowlist = (subject, allowlist) => compileSubjectAllowlist(allowlist)(subject);
399
+ var HEADER_DENYLIST = /* @__PURE__ */ new Set([
400
+ "traceparent",
401
+ "tracestate",
402
+ "baggage",
403
+ "sentry-trace",
404
+ "b3",
405
+ "x-b3-traceid",
406
+ "x-b3-spanid",
407
+ "x-b3-parentspanid",
408
+ "x-b3-sampled",
409
+ "x-b3-flags",
410
+ "uber-trace-id",
411
+ "x-correlation-id",
412
+ "x-reply-to",
413
+ "x-error",
414
+ "x-subject",
415
+ "x-caller-name"
416
+ ]);
417
+ var isNatsServerHeader = (lower) => lower.startsWith("nats-");
418
+ var captureMatchingHeaders = (headers2, matcher) => {
419
+ if (!headers2) return {};
420
+ const out = {};
421
+ for (const key of headers2.keys()) {
422
+ const lower = key.toLowerCase();
423
+ if (HEADER_DENYLIST.has(lower)) continue;
424
+ if (isNatsServerHeader(lower)) continue;
425
+ if (!matcher(key)) continue;
426
+ const value = headers2.get(key);
427
+ if (value === "") continue;
428
+ out[messagingHeaderAttr(lower)] = value;
429
+ }
430
+ return out;
431
+ };
432
+ var tryUtf8Decode = (bytes) => {
433
+ try {
434
+ return new TextDecoder("utf-8", { fatal: false }).decode(bytes);
435
+ } catch {
436
+ return Buffer.from(bytes).toString("base64");
437
+ }
438
+ };
439
+ var captureBodyAttribute = (subject, payload, capture) => {
440
+ if (capture === false) return {};
441
+ if (payload.byteLength === 0) return {};
442
+ if (!subjectMatchesAllowlist(subject, capture.subjectAllowlist)) return {};
443
+ const { maxBytes } = capture;
444
+ const truncated = payload.byteLength > maxBytes;
445
+ const slice = truncated ? payload.subarray(0, maxBytes) : payload;
446
+ const decoded = tryUtf8Decode(slice);
447
+ const out = { [ATTR_MESSAGING_NATS_BODY]: decoded };
448
+ if (truncated) out[ATTR_MESSAGING_NATS_BODY_TRUNCATED] = true;
449
+ return out;
450
+ };
451
+
452
+ // src/otel/config.ts
453
+ var PublishKind = /* @__PURE__ */ ((PublishKind2) => {
454
+ PublishKind2["Event"] = "event";
455
+ PublishKind2["RpcRequest"] = "rpc.request";
456
+ PublishKind2["Broadcast"] = "broadcast";
457
+ PublishKind2["Ordered"] = "ordered";
458
+ return PublishKind2;
459
+ })(PublishKind || {});
460
+ var ConsumeKind = /* @__PURE__ */ ((ConsumeKind2) => {
461
+ ConsumeKind2["Event"] = "event";
462
+ ConsumeKind2["Rpc"] = "rpc";
463
+ ConsumeKind2["Broadcast"] = "broadcast";
464
+ ConsumeKind2["Ordered"] = "ordered";
465
+ return ConsumeKind2;
466
+ })(ConsumeKind || {});
467
+ var DEFAULT_CAPTURE_HEADERS = ["x-request-id"];
468
+ var DEFAULT_CAPTURE_BODY_MAX_BYTES = 4096;
469
+ var NESTJS_BARE_ERROR_MESSAGE = "Internal server error";
470
+ var isNestjsBareErrorSentinel = (obj) => obj.status === "error" && obj.message === NESTJS_BARE_ERROR_MESSAGE;
471
+ var defaultErrorClassifier = (err) => {
472
+ if (err === null || typeof err !== "object") return "unexpected";
473
+ if (!(err instanceof Error)) {
474
+ if (isNestjsBareErrorSentinel(err)) return "unexpected";
475
+ return "expected";
476
+ }
477
+ let proto = err;
478
+ while (proto) {
479
+ const name = proto.constructor?.name;
480
+ if (name === "RpcException" || name === "HttpException") return "expected";
481
+ proto = Object.getPrototypeOf(proto);
482
+ }
483
+ return "unexpected";
484
+ };
485
+ var expandTracesOption = (option) => {
486
+ if (option === void 0 || option === "default") return new Set(DEFAULT_TRACES);
487
+ if (option === "all") return new Set(Object.values(JetstreamTrace));
488
+ if (option === "none") return /* @__PURE__ */ new Set();
489
+ return new Set(option);
490
+ };
491
+ var compileHeaderMatcher = (option) => compileHeaderAllowlist(option ?? DEFAULT_CAPTURE_HEADERS);
492
+ var resolveCaptureBody = (option) => {
493
+ if (option === void 0 || option === false) return false;
494
+ if (option === true) return { maxBytes: DEFAULT_CAPTURE_BODY_MAX_BYTES };
495
+ return {
496
+ maxBytes: option.maxBytes ?? DEFAULT_CAPTURE_BODY_MAX_BYTES,
497
+ subjectAllowlist: option.subjectAllowlist
498
+ };
499
+ };
500
+ var resolveOtelOptions = (options = {}) => {
501
+ return {
502
+ enabled: options.enabled ?? true,
503
+ traces: expandTracesOption(options.traces),
504
+ captureHeaders: compileHeaderMatcher(options.captureHeaders),
505
+ captureBody: resolveCaptureBody(options.captureBody),
506
+ publishHook: options.publishHook,
507
+ consumeHook: options.consumeHook,
508
+ responseHook: options.responseHook,
509
+ shouldTracePublish: options.shouldTracePublish,
510
+ shouldTraceConsume: options.shouldTraceConsume,
511
+ errorClassifier: options.errorClassifier ?? defaultErrorClassifier
512
+ };
513
+ };
514
+
515
+ // src/otel/internal-utils.ts
516
+ var import_common = require("@nestjs/common");
517
+ var logger = new import_common.Logger("Jetstream:Otel");
518
+ var safelyInvokeHook = (hookName, hook, ...args) => {
519
+ if (!hook) return;
520
+ const logHookFailure = (err) => {
521
+ const message = err instanceof Error ? err.message : String(err);
522
+ logger.debug(`OTel ${hookName} threw: ${message}`);
523
+ };
524
+ try {
525
+ const result = hook(...args);
526
+ if (result !== null && typeof result === "object" && "then" in result && typeof result.then === "function") {
527
+ result.then(void 0, logHookFailure);
528
+ }
529
+ } catch (err) {
530
+ logHookFailure(err);
531
+ }
532
+ };
533
+ var stripIpv6Brackets = (host) => host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
534
+ var parsePort = (portRaw) => {
535
+ if (!portRaw) return void 0;
536
+ const port = Number.parseInt(portRaw, 10);
537
+ return Number.isInteger(port) ? port : void 0;
538
+ };
539
+ var parseServerAddress = (servers) => {
540
+ const raw = servers[0];
541
+ if (!raw) return null;
542
+ if (raw.includes("://")) {
543
+ try {
544
+ const url = new URL(raw);
545
+ if (url.hostname.length === 0) return null;
546
+ const host2 = stripIpv6Brackets(url.hostname);
547
+ const port2 = parsePort(url.port || void 0);
548
+ return port2 === void 0 ? { host: host2 } : { host: host2, port: port2 };
549
+ } catch {
550
+ return null;
551
+ }
552
+ }
553
+ if (raw.startsWith("[")) {
554
+ const closeIdx = raw.indexOf("]");
555
+ if (closeIdx <= 0) return null;
556
+ const host2 = raw.slice(1, closeIdx);
557
+ const port2 = parsePort(raw.slice(closeIdx + 1).replace(/^:/u, ""));
558
+ return port2 === void 0 ? { host: host2 } : { host: host2, port: port2 };
559
+ }
560
+ const [host, portRaw] = raw.split(":");
561
+ if (!host) return null;
562
+ const port = parsePort(portRaw);
563
+ return port === void 0 ? { host } : { host, port };
564
+ };
565
+ var deriveOtelAttrs = (options) => ({
566
+ otel: resolveOtelOptions(options.otel),
567
+ serviceName: internalName(options.name),
568
+ serverEndpoint: parseServerAddress(options.servers)
569
+ });
570
+
571
+ // src/otel/propagator.ts
572
+ var import_api = require("@opentelemetry/api");
573
+ var injectContext = (ctx, carrier, setter) => {
574
+ import_api.propagation.inject(ctx, carrier, setter);
575
+ };
576
+ var extractContext = (ctx, carrier, getter) => import_api.propagation.extract(ctx, carrier, getter);
577
+
578
+ // src/otel/tracer.ts
579
+ var import_api2 = require("@opentelemetry/api");
580
+ var PACKAGE_VERSION = true ? "2.10.0" : "0.0.0";
581
+ var getTracer = () => import_api2.trace.getTracer(TRACER_NAME, PACKAGE_VERSION);
582
+
583
+ // src/otel/carrier.ts
584
+ var hdrsSetter = {
585
+ set: (headers2, key, value) => {
586
+ headers2.set(key, value);
587
+ }
588
+ };
589
+ var hdrsGetter = {
590
+ keys: (headers2) => headers2 ? headers2.keys() : [],
591
+ get: (headers2, key) => {
592
+ if (!headers2) return void 0;
593
+ const all = typeof headers2.values === "function" ? headers2.values(key) : void 0;
594
+ if (Array.isArray(all)) {
595
+ const nonEmpty = all.filter((value) => value !== "");
596
+ if (nonEmpty.length === 0) return void 0;
597
+ return nonEmpty.join(",");
598
+ }
599
+ const single = headers2.get(key);
600
+ return single === "" ? void 0 : single;
601
+ }
602
+ };
603
+
604
+ // src/otel/attributes.ts
605
+ var MESSAGING_SYSTEM = "nats";
606
+ var baseMessagingAttributes = (ctx) => {
607
+ const attrs = {
608
+ [ATTR_MESSAGING_SYSTEM]: MESSAGING_SYSTEM,
609
+ [ATTR_MESSAGING_DESTINATION_NAME]: ctx.subject,
610
+ [ATTR_MESSAGING_CLIENT_ID]: ctx.serviceName
611
+ };
612
+ if (ctx.serverAddress) attrs[ATTR_SERVER_ADDRESS] = ctx.serverAddress;
613
+ if (ctx.serverPort !== void 0) attrs[ATTR_SERVER_PORT] = ctx.serverPort;
614
+ if (ctx.pattern && ctx.pattern !== ctx.subject) {
615
+ attrs[ATTR_MESSAGING_DESTINATION_TEMPLATE] = ctx.pattern;
616
+ }
617
+ return attrs;
618
+ };
619
+ var jetstreamKindForPublish = (kind) => kind === "rpc.request" /* RpcRequest */ ? "rpc" /* Rpc */ : kind;
620
+ var buildPublishAttributes = (ctx) => {
621
+ const attrs = {
622
+ ...baseMessagingAttributes(ctx),
623
+ [ATTR_MESSAGING_OPERATION_NAME]: SPAN_NAME_PUBLISH,
624
+ [ATTR_MESSAGING_OPERATION_TYPE]: SPAN_NAME_SEND,
625
+ [ATTR_MESSAGING_MESSAGE_BODY_SIZE]: ctx.payloadBytes,
626
+ [ATTR_JETSTREAM_KIND]: jetstreamKindForPublish(ctx.kind)
627
+ };
628
+ if (ctx.messageId) attrs[ATTR_MESSAGING_MESSAGE_ID] = ctx.messageId;
629
+ if (ctx.correlationId) attrs[ATTR_MESSAGING_MESSAGE_CONVERSATION_ID] = ctx.correlationId;
630
+ return attrs;
631
+ };
632
+ var buildConsumeAttributes = (ctx) => {
633
+ const { msg, info, kind, payloadBytes } = ctx;
634
+ const attrs = {
635
+ ...baseMessagingAttributes(ctx),
636
+ [ATTR_MESSAGING_OPERATION_NAME]: SPAN_NAME_PROCESS,
637
+ [ATTR_MESSAGING_OPERATION_TYPE]: SPAN_NAME_PROCESS,
638
+ [ATTR_MESSAGING_MESSAGE_BODY_SIZE]: payloadBytes,
639
+ [ATTR_JETSTREAM_KIND]: kind
640
+ };
641
+ if (info) {
642
+ attrs[ATTR_MESSAGING_NATS_STREAM_NAME] = info.stream;
643
+ attrs[ATTR_MESSAGING_CONSUMER_GROUP_NAME] = info.consumer;
644
+ attrs[ATTR_MESSAGING_NATS_STREAM_SEQUENCE] = info.streamSequence;
645
+ attrs[ATTR_MESSAGING_NATS_CONSUMER_SEQUENCE] = info.deliverySequence;
646
+ attrs[ATTR_MESSAGING_NATS_DELIVERY_COUNT] = info.deliveryCount;
647
+ }
648
+ const messageId = msg.headers?.get(NATS_MSG_ID_HEADER);
649
+ if (messageId) attrs[ATTR_MESSAGING_MESSAGE_ID] = messageId;
650
+ return attrs;
651
+ };
652
+ var buildRpcClientAttributes = (ctx) => {
653
+ const attrs = {
654
+ ...baseMessagingAttributes(ctx),
655
+ [ATTR_MESSAGING_OPERATION_NAME]: SPAN_NAME_SEND,
656
+ [ATTR_MESSAGING_OPERATION_TYPE]: SPAN_NAME_SEND,
657
+ [ATTR_MESSAGING_MESSAGE_BODY_SIZE]: ctx.payloadBytes,
658
+ [ATTR_JETSTREAM_KIND]: "rpc" /* Rpc */
659
+ };
660
+ if (ctx.correlationId) attrs[ATTR_MESSAGING_MESSAGE_CONVERSATION_ID] = ctx.correlationId;
661
+ if (ctx.messageId) attrs[ATTR_MESSAGING_MESSAGE_ID] = ctx.messageId;
662
+ return attrs;
663
+ };
664
+ var buildDeadLetterAttributes = (ctx) => {
665
+ const attrs = {
666
+ ...baseMessagingAttributes(ctx),
667
+ [ATTR_MESSAGING_OPERATION_NAME]: SPAN_NAME_DEAD_LETTER,
668
+ [ATTR_MESSAGING_OPERATION_TYPE]: SPAN_NAME_PROCESS,
669
+ [ATTR_MESSAGING_NATS_DELIVERY_COUNT]: ctx.finalDeliveryCount
670
+ };
671
+ if (ctx.reason) attrs[ATTR_JETSTREAM_DEAD_LETTER_REASON] = ctx.reason;
672
+ return attrs;
673
+ };
674
+ var extractFromRpcException = (record) => {
675
+ const getError = record.getError;
676
+ if (typeof getError !== "function") return void 0;
677
+ return codeFromPayload(getError.call(record));
678
+ };
679
+ var extractFromHttpException = (record) => {
680
+ const getStatus = record.getStatus;
681
+ if (typeof getStatus !== "function") return void 0;
682
+ const status = getStatus.call(record);
683
+ return typeof status === "number" && Number.isFinite(status) ? `HTTP_${status}` : void 0;
684
+ };
685
+ var extractFromOwnCode = (record) => {
686
+ const code = record.code ?? record.errorCode;
687
+ return typeof code === "string" && isStableErrorCode(code) ? code : void 0;
688
+ };
689
+ var extractExpectedErrorCode = (err) => {
690
+ if (err === null || typeof err !== "object") return void 0;
691
+ const record = err;
692
+ if (hasAncestorNamed(err, "RpcException")) {
693
+ const fromPayload = extractFromRpcException(record);
694
+ if (fromPayload !== void 0) return fromPayload;
695
+ }
696
+ if (hasAncestorNamed(err, "HttpException")) {
697
+ const fromStatus = extractFromHttpException(record);
698
+ if (fromStatus !== void 0) return fromStatus;
699
+ }
700
+ return extractFromOwnCode(record);
701
+ };
702
+ var hasAncestorNamed = (err, name) => {
703
+ let proto = Object.getPrototypeOf(err);
704
+ while (proto) {
705
+ const ctorName = proto.constructor?.name;
706
+ if (ctorName === name) return true;
707
+ proto = Object.getPrototypeOf(proto);
708
+ }
709
+ return false;
710
+ };
711
+ var STABLE_ERROR_CODE_RE = /^[A-Z][A-Z0-9_]*$/u;
712
+ var isStableErrorCode = (value) => STABLE_ERROR_CODE_RE.test(value);
713
+ var codeFromPayload = (payload) => {
714
+ if (payload === null || payload === void 0) return void 0;
715
+ if (typeof payload === "string") return isStableErrorCode(payload) ? payload : void 0;
716
+ if (typeof payload === "object") {
717
+ const code = payload.code;
718
+ if (typeof code === "string" && isStableErrorCode(code)) return code;
719
+ }
720
+ return void 0;
721
+ };
722
+ var buildExpectedErrorAttributes = (err) => {
723
+ const attrs = {
724
+ [ATTR_JETSTREAM_RPC_REPLY_HAS_ERROR]: true
725
+ };
726
+ const code = extractExpectedErrorCode(err);
727
+ if (code !== void 0) attrs[ATTR_JETSTREAM_RPC_REPLY_ERROR_CODE] = code;
728
+ return attrs;
729
+ };
730
+ var applyExpectedErrorAttributes = (span, err) => {
731
+ span.setAttributes(buildExpectedErrorAttributes(err));
732
+ };
733
+
734
+ // src/otel/spans/publish.ts
735
+ var import_common2 = require("@nestjs/common");
736
+ var import_api3 = require("@opentelemetry/api");
737
+ var logger2 = new import_common2.Logger("Jetstream:Otel");
738
+ var shouldTracePublishSafe = (predicate, subject, record) => {
739
+ if (!predicate) return true;
740
+ try {
741
+ return predicate(subject, record);
742
+ } catch (err) {
743
+ const message = err instanceof Error ? err.message : String(err);
744
+ logger2.debug(`OTel shouldTracePublish threw: ${message}`);
745
+ return true;
746
+ }
747
+ };
748
+ var withPublishSpan = async (ctx, config, fn) => {
749
+ if (!config.enabled) return fn();
750
+ const shouldCreateSpan = config.traces.has("publish" /* Publish */) && shouldTracePublishSafe(config.shouldTracePublish, ctx.subject, ctx.record);
751
+ if (!shouldCreateSpan) {
752
+ injectContext(import_api3.context.active(), ctx.headers, hdrsSetter);
753
+ return fn();
754
+ }
755
+ const tracer = getTracer();
756
+ const span = tracer.startSpan(`${SPAN_NAME_PUBLISH} ${ctx.subject}`, {
757
+ kind: import_api3.SpanKind.PRODUCER,
758
+ attributes: {
759
+ ...buildPublishAttributes({
760
+ subject: ctx.subject,
761
+ pattern: ctx.pattern,
762
+ serviceName: ctx.serviceName,
763
+ serverAddress: ctx.endpoint?.host,
764
+ serverPort: ctx.endpoint?.port,
765
+ kind: ctx.kind,
766
+ payloadBytes: ctx.payloadBytes,
767
+ messageId: ctx.messageId,
768
+ correlationId: ctx.correlationId
769
+ }),
770
+ ...ctx.scheduleTarget ? { [ATTR_JETSTREAM_SCHEDULE_TARGET]: ctx.scheduleTarget } : {},
771
+ ...captureMatchingHeaders(ctx.headers, config.captureHeaders),
772
+ ...captureBodyAttribute(ctx.subject, ctx.payload, config.captureBody)
773
+ }
774
+ });
775
+ const ctxWithSpan = import_api3.trace.setSpan(import_api3.context.active(), span);
776
+ injectContext(ctxWithSpan, ctx.headers, hdrsSetter);
777
+ const start = Date.now();
778
+ const invokeResponseHook = (error) => {
779
+ import_api3.context.with(ctxWithSpan, () => {
780
+ safelyInvokeHook(HOOK_RESPONSE, config.responseHook, span, {
781
+ subject: ctx.subject,
782
+ durationMs: Date.now() - start,
783
+ error
784
+ });
785
+ });
786
+ };
787
+ try {
788
+ const result = await import_api3.context.with(ctxWithSpan, async () => {
789
+ safelyInvokeHook(HOOK_PUBLISH, config.publishHook, span, {
790
+ subject: ctx.subject,
791
+ record: ctx.record,
792
+ kind: ctx.kind
793
+ });
794
+ return fn();
795
+ });
796
+ span.setStatus({ code: import_api3.SpanStatusCode.OK });
797
+ invokeResponseHook();
798
+ return result;
799
+ } catch (err) {
800
+ const error = err instanceof Error ? err : new Error(String(err));
801
+ span.recordException(error);
802
+ span.setStatus({ code: import_api3.SpanStatusCode.ERROR, message: error.message });
803
+ invokeResponseHook(error);
804
+ throw err;
805
+ } finally {
806
+ span.end();
807
+ }
808
+ };
809
+
810
+ // src/otel/spans/consume.ts
811
+ var import_api4 = require("@opentelemetry/api");
812
+ var isPromiseLike = (value) => typeof value === "object" && value !== null && typeof value.then === "function";
813
+ var applyExpectedError = (span, err) => {
814
+ span.setStatus({ code: import_api4.SpanStatusCode.OK });
815
+ applyExpectedErrorAttributes(span, err);
816
+ };
817
+ var applyUnexpectedError = (span, err) => {
818
+ const error = err instanceof Error ? err : new Error(String(err));
819
+ span.recordException(error);
820
+ span.setStatus({ code: import_api4.SpanStatusCode.ERROR, message: error.message });
821
+ };
822
+ var withConsumeSpan = (ctx, config, fn, options = {}) => {
823
+ if (!config.enabled) return fn();
824
+ const parentCtx = extractContext(import_api4.ROOT_CONTEXT, ctx.msg.headers, hdrsGetter);
825
+ const shouldCreateSpan = config.traces.has("consume" /* Consume */) && (config.shouldTraceConsume?.(ctx.subject, ctx.msg) ?? true);
826
+ if (!shouldCreateSpan) {
827
+ return import_api4.context.with(parentCtx, fn);
828
+ }
829
+ const tracer = getTracer();
830
+ const span = tracer.startSpan(
831
+ `${SPAN_NAME_PROCESS} ${ctx.subject}`,
832
+ {
833
+ kind: import_api4.SpanKind.CONSUMER,
834
+ attributes: {
835
+ ...buildConsumeAttributes({
836
+ subject: ctx.subject,
837
+ pattern: ctx.pattern,
838
+ msg: ctx.msg,
839
+ info: ctx.info,
840
+ kind: ctx.kind,
841
+ payloadBytes: ctx.payloadBytes,
842
+ serviceName: ctx.serviceName,
843
+ serverAddress: ctx.endpoint?.host,
844
+ serverPort: ctx.endpoint?.port
845
+ }),
846
+ ...captureMatchingHeaders(ctx.msg.headers, config.captureHeaders),
847
+ ...captureBodyAttribute(ctx.subject, ctx.msg.data, config.captureBody)
848
+ }
849
+ },
850
+ parentCtx
851
+ );
852
+ const ctxWithSpan = import_api4.trace.setSpan(parentCtx, span);
853
+ const start = Date.now();
854
+ let finalized = false;
855
+ const { signal, timeoutLabel = "handler.timeout" } = options;
856
+ let detachAbort = null;
857
+ const invokeResponseHook = (durationMs, error) => {
858
+ import_api4.context.with(ctxWithSpan, () => {
859
+ safelyInvokeHook(HOOK_RESPONSE, config.responseHook, span, {
860
+ subject: ctx.subject,
861
+ durationMs,
862
+ error
863
+ });
864
+ });
865
+ };
866
+ const finishOk2 = () => {
867
+ if (finalized) return;
868
+ finalized = true;
869
+ detachAbort?.();
870
+ span.setStatus({ code: import_api4.SpanStatusCode.OK });
871
+ invokeResponseHook(Date.now() - start);
872
+ span.end();
873
+ };
874
+ const finishError2 = (err) => {
875
+ if (finalized) return;
876
+ finalized = true;
877
+ detachAbort?.();
878
+ let classification = "unexpected";
879
+ try {
880
+ classification = config.errorClassifier(err);
881
+ } catch {
882
+ }
883
+ if (classification === "expected") {
884
+ applyExpectedError(span, err);
885
+ } else {
886
+ applyUnexpectedError(span, err);
887
+ }
888
+ invokeResponseHook(Date.now() - start, err instanceof Error ? err : new Error(String(err)));
889
+ span.end();
890
+ };
891
+ const onAbort = () => {
892
+ if (finalized) return;
893
+ finalized = true;
894
+ const error = new Error(timeoutLabel);
895
+ span.addEvent(timeoutLabel);
896
+ span.recordException(error);
897
+ span.setStatus({ code: import_api4.SpanStatusCode.ERROR, message: timeoutLabel });
898
+ invokeResponseHook(Date.now() - start, error);
899
+ span.end();
900
+ };
901
+ if (signal) {
902
+ if (signal.aborted) {
903
+ onAbort();
904
+ } else {
905
+ signal.addEventListener("abort", onAbort, { once: true });
906
+ detachAbort = () => {
907
+ signal.removeEventListener("abort", onAbort);
908
+ };
909
+ }
910
+ }
911
+ let result;
912
+ try {
913
+ result = import_api4.context.with(ctxWithSpan, () => {
914
+ safelyInvokeHook(HOOK_CONSUME, config.consumeHook, span, {
915
+ subject: ctx.subject,
916
+ msg: ctx.msg,
917
+ handlerMetadata: ctx.handlerMetadata,
918
+ kind: ctx.kind
919
+ });
920
+ return fn();
921
+ });
922
+ } catch (err) {
923
+ finishError2(err);
924
+ throw err;
925
+ }
926
+ if (isPromiseLike(result)) {
927
+ return Promise.resolve(result).then(
928
+ (value) => {
929
+ finishOk2();
930
+ return value;
931
+ },
932
+ (err) => {
933
+ finishError2(err);
934
+ throw err;
935
+ }
936
+ );
937
+ }
938
+ finishOk2();
939
+ return result;
940
+ };
941
+
942
+ // src/otel/spans/rpc-client.ts
943
+ var import_common3 = require("@nestjs/common");
944
+ var import_api5 = require("@opentelemetry/api");
945
+ var logger3 = new import_common3.Logger("Jetstream:Otel");
946
+ var RPC_TIMEOUT_MESSAGE = "rpc.timeout";
947
+ var beginRpcClientSpan = (ctx, config) => {
948
+ if (!config.enabled) {
949
+ return {
950
+ activeContext: import_api5.context.active(),
951
+ finish: () => void 0
952
+ };
953
+ }
954
+ if (!config.traces.has("rpc.client.send" /* RpcClientSend */)) {
955
+ injectContext(import_api5.context.active(), ctx.headers, hdrsSetter);
956
+ return {
957
+ activeContext: import_api5.context.active(),
958
+ finish: () => void 0
959
+ };
960
+ }
961
+ const tracer = getTracer();
962
+ const span = tracer.startSpan(`${SPAN_NAME_SEND} ${ctx.subject}`, {
963
+ kind: import_api5.SpanKind.CLIENT,
964
+ attributes: {
965
+ ...buildRpcClientAttributes({
966
+ subject: ctx.subject,
967
+ pattern: ctx.pattern,
968
+ correlationId: ctx.correlationId,
969
+ payloadBytes: ctx.payloadBytes,
970
+ messageId: ctx.messageId,
971
+ serviceName: ctx.serviceName,
972
+ serverAddress: ctx.endpoint?.host,
973
+ serverPort: ctx.endpoint?.port
974
+ }),
975
+ ...captureMatchingHeaders(ctx.headers, config.captureHeaders),
976
+ ...captureBodyAttribute(ctx.subject, ctx.payload, config.captureBody)
977
+ }
978
+ });
979
+ const ctxWithSpan = import_api5.trace.setSpan(import_api5.context.active(), span);
980
+ injectContext(ctxWithSpan, ctx.headers, hdrsSetter);
981
+ const start = Date.now();
982
+ let finalized = false;
983
+ const finish = (outcome) => {
984
+ if (finalized) return;
985
+ finalized = true;
986
+ let reply;
987
+ let error;
988
+ switch (outcome.kind) {
989
+ case "ok" /* Ok */:
990
+ reply = outcome.reply;
991
+ span.setStatus({ code: import_api5.SpanStatusCode.OK });
992
+ break;
993
+ case "reply-error" /* ReplyError */:
994
+ reply = outcome.replyPayload;
995
+ applyExpectedErrorAttributes(span, outcome.replyPayload);
996
+ span.setStatus({ code: import_api5.SpanStatusCode.OK });
997
+ break;
998
+ case "timeout" /* Timeout */:
999
+ error = new Error(RPC_TIMEOUT_MESSAGE);
1000
+ span.addEvent(RPC_TIMEOUT_MESSAGE);
1001
+ span.setStatus({ code: import_api5.SpanStatusCode.ERROR, message: RPC_TIMEOUT_MESSAGE });
1002
+ break;
1003
+ case "error" /* Error */:
1004
+ error = outcome.error;
1005
+ span.recordException(outcome.error);
1006
+ span.setStatus({ code: import_api5.SpanStatusCode.ERROR, message: outcome.error.message });
1007
+ break;
1008
+ default: {
1009
+ const unknownOutcome = outcome;
1010
+ logger3.error(`Unhandled RPC outcome: ${String(unknownOutcome.kind)}`);
1011
+ span.setStatus({ code: import_api5.SpanStatusCode.ERROR, message: "unknown outcome" });
1012
+ }
1013
+ }
1014
+ import_api5.context.with(ctxWithSpan, () => {
1015
+ safelyInvokeHook(HOOK_RESPONSE, config.responseHook, span, {
1016
+ subject: ctx.subject,
1017
+ durationMs: Date.now() - start,
1018
+ reply,
1019
+ error
1020
+ });
1021
+ });
1022
+ span.end();
1023
+ };
1024
+ return { activeContext: ctxWithSpan, finish };
1025
+ };
1026
+
1027
+ // src/otel/spans/dead-letter.ts
1028
+ var import_api6 = require("@opentelemetry/api");
1029
+ var withDeadLetterSpan = async (ctx, config, fn) => {
1030
+ if (!config.enabled || !config.traces.has("dead_letter" /* DeadLetter */)) {
1031
+ return fn();
1032
+ }
1033
+ const parentCtx = extractContext(import_api6.ROOT_CONTEXT, ctx.msg.headers, hdrsGetter);
1034
+ const tracer = getTracer();
1035
+ const span = tracer.startSpan(
1036
+ `${SPAN_NAME_DEAD_LETTER} ${ctx.msg.subject}`,
1037
+ {
1038
+ kind: import_api6.SpanKind.INTERNAL,
1039
+ attributes: buildDeadLetterAttributes({
1040
+ subject: ctx.msg.subject,
1041
+ pattern: ctx.pattern,
1042
+ serviceName: ctx.serviceName,
1043
+ serverAddress: ctx.endpoint?.host,
1044
+ serverPort: ctx.endpoint?.port,
1045
+ finalDeliveryCount: ctx.finalDeliveryCount,
1046
+ reason: ctx.reason
1047
+ })
1048
+ },
1049
+ parentCtx
1050
+ );
1051
+ const ctxWithSpan = import_api6.trace.setSpan(parentCtx, span);
1052
+ const start = Date.now();
1053
+ const invokeResponseHook = (error) => {
1054
+ import_api6.context.with(ctxWithSpan, () => {
1055
+ safelyInvokeHook(HOOK_RESPONSE, config.responseHook, span, {
1056
+ subject: ctx.msg.subject,
1057
+ durationMs: Date.now() - start,
1058
+ error
1059
+ });
1060
+ });
1061
+ };
1062
+ try {
1063
+ const result = await import_api6.context.with(ctxWithSpan, fn);
1064
+ span.setStatus({ code: import_api6.SpanStatusCode.OK });
1065
+ invokeResponseHook();
1066
+ return result;
1067
+ } catch (err) {
1068
+ const error = err instanceof Error ? err : new Error(String(err));
1069
+ span.recordException(error);
1070
+ span.setStatus({ code: import_api6.SpanStatusCode.ERROR, message: error.message });
1071
+ invokeResponseHook(error);
1072
+ throw err;
1073
+ } finally {
1074
+ span.end();
1075
+ }
1076
+ };
1077
+
1078
+ // src/otel/spans/infrastructure.ts
1079
+ var import_common4 = require("@nestjs/common");
1080
+ var import_api7 = require("@opentelemetry/api");
1081
+ var logger4 = new import_common4.Logger("Jetstream:Otel");
1082
+ var startInfraSpan = (config, traceKind, name, ctx, extraAttributes = {}) => {
1083
+ if (!config.enabled || !config.traces.has(traceKind)) return null;
1084
+ const tracer = getTracer();
1085
+ const attributes = {
1086
+ [ATTR_JETSTREAM_SERVICE_NAME]: ctx.serviceName
1087
+ };
1088
+ if (ctx.endpoint?.host) attributes[ATTR_SERVER_ADDRESS] = ctx.endpoint.host;
1089
+ if (ctx.endpoint?.port !== void 0) attributes[ATTR_SERVER_PORT] = ctx.endpoint.port;
1090
+ for (const [key, value] of Object.entries(extraAttributes)) {
1091
+ if (value !== void 0) attributes[key] = value;
1092
+ }
1093
+ return tracer.startSpan(name, { kind: import_api7.SpanKind.INTERNAL, attributes });
1094
+ };
1095
+ var finishOk = (span) => {
1096
+ span.setStatus({ code: import_api7.SpanStatusCode.OK });
1097
+ span.end();
1098
+ };
1099
+ var finishError = (span, err) => {
1100
+ const error = err instanceof Error ? err : new Error(String(err));
1101
+ span.recordException(error);
1102
+ span.setStatus({ code: import_api7.SpanStatusCode.ERROR, message: error.message });
1103
+ span.end();
1104
+ };
1105
+ var wrapInfra = async (config, traceKind, name, ctx, attributes, op) => {
1106
+ const span = startInfraSpan(config, traceKind, name, ctx, attributes);
1107
+ if (!span) return op();
1108
+ const ctxWithSpan = import_api7.trace.setSpan(import_api7.context.active(), span);
1109
+ try {
1110
+ const result = await import_api7.context.with(ctxWithSpan, op);
1111
+ finishOk(span);
1112
+ return result;
1113
+ } catch (err) {
1114
+ finishError(span, err);
1115
+ throw err;
1116
+ }
1117
+ };
1118
+ var beginConnectionLifecycleSpan = (config, ctx) => {
1119
+ const span = startInfraSpan(
1120
+ config,
1121
+ "connection.lifecycle" /* ConnectionLifecycle */,
1122
+ SPAN_NAME_NATS_CONNECTION,
1123
+ ctx,
1124
+ { [ATTR_NATS_CONNECTION_SERVER]: ctx.server }
1125
+ );
1126
+ if (!span) {
1127
+ return {
1128
+ recordEvent: () => void 0,
1129
+ finish: () => void 0
1130
+ };
1131
+ }
1132
+ let finalized = false;
1133
+ return {
1134
+ recordEvent: (name, attributes) => {
1135
+ if (finalized) {
1136
+ logger4.debug(`recordEvent('${name}') called after connection span finished`);
1137
+ return;
1138
+ }
1139
+ span.addEvent(name, attributes);
1140
+ },
1141
+ finish: (err) => {
1142
+ if (finalized) return;
1143
+ finalized = true;
1144
+ if (err === void 0) finishOk(span);
1145
+ else finishError(span, err);
1146
+ }
1147
+ };
1148
+ };
1149
+ var withSelfHealingSpan = (config, ctx, op) => wrapInfra(
1150
+ config,
1151
+ "self_healing" /* SelfHealing */,
1152
+ SPAN_NAME_JETSTREAM_SELF_HEALING,
1153
+ ctx,
1154
+ {
1155
+ [ATTR_MESSAGING_NATS_STREAM_NAME]: ctx.stream,
1156
+ [ATTR_MESSAGING_CONSUMER_GROUP_NAME]: ctx.consumer,
1157
+ [ATTR_JETSTREAM_SELF_HEALING_REASON]: ctx.reason
1158
+ },
1159
+ op
1160
+ );
1161
+ var withProvisioningSpan = (config, ctx, op) => wrapInfra(
1162
+ config,
1163
+ "provisioning" /* Provisioning */,
1164
+ `${SPAN_NAME_JETSTREAM_PROVISIONING_PREFIX}${ctx.entity}`,
1165
+ ctx,
1166
+ {
1167
+ [ATTR_JETSTREAM_PROVISIONING_ENTITY]: ctx.entity,
1168
+ [ATTR_JETSTREAM_PROVISIONING_ACTION]: ctx.action,
1169
+ [ATTR_JETSTREAM_PROVISIONING_NAME]: ctx.name
1170
+ },
1171
+ op
1172
+ );
1173
+ var withMigrationSpan = (config, ctx, op) => wrapInfra(
1174
+ config,
1175
+ "migration" /* Migration */,
1176
+ SPAN_NAME_JETSTREAM_MIGRATION,
1177
+ ctx,
1178
+ {
1179
+ [ATTR_MESSAGING_NATS_STREAM_NAME]: ctx.stream,
1180
+ [ATTR_JETSTREAM_MIGRATION_REASON]: ctx.reason
1181
+ },
1182
+ op
1183
+ );
1184
+ var withShutdownSpan = (config, ctx, op) => wrapInfra(config, "shutdown" /* Shutdown */, SPAN_NAME_JETSTREAM_SHUTDOWN, ctx, {}, op);
1185
+
267
1186
  // src/client/jetstream.record.ts
268
1187
  var JetstreamRecord = class {
269
1188
  constructor(data, headers2, timeout, messageId, schedule, ttl) {
@@ -416,9 +1335,14 @@ var JetstreamRecordBuilder = class {
416
1335
  this.ttlDuration
417
1336
  );
418
1337
  }
419
- /** Validate that a header key is not reserved. */
1338
+ /**
1339
+ * Validate that a header key is not reserved. NATS treats header names
1340
+ * case-insensitively, so the check is against the lowercase form to keep
1341
+ * `'X-Correlation-ID'`, `'x-correlation-id'`, and any other casing in
1342
+ * lockstep. `RESERVED_HEADERS` is defined as an all-lowercase set.
1343
+ */
420
1344
  validateHeaderKey(key) {
421
- if (RESERVED_HEADERS.has(key)) {
1345
+ if (RESERVED_HEADERS.has(key.toLowerCase())) {
422
1346
  throw new Error(
423
1347
  `Header "${key}" is reserved by the JetStream transport and cannot be set manually. Reserved headers: ${[...RESERVED_HEADERS].join(", ")}`
424
1348
  );
@@ -438,6 +1362,12 @@ var nanosToGoDuration = (nanos) => {
438
1362
  };
439
1363
 
440
1364
  // src/client/jetstream.client.ts
1365
+ var BROADCAST_SUBJECT_PREFIX = "broadcast.";
1366
+ var detectEventKind = (pattern) => {
1367
+ if (pattern.startsWith("broadcast:" /* Broadcast */)) return "broadcast" /* Broadcast */;
1368
+ if (pattern.startsWith("ordered:" /* Ordered */)) return "ordered" /* Ordered */;
1369
+ return "event" /* Event */;
1370
+ };
441
1371
  var JetstreamClient = class extends import_microservices.ClientProxy {
442
1372
  constructor(rootOptions, targetServiceName, connection, codec, eventBus) {
443
1373
  super();
@@ -447,12 +1377,40 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
447
1377
  this.eventBus = eventBus;
448
1378
  this.targetName = targetServiceName;
449
1379
  this.callerName = internalName(this.rootOptions.name);
450
- }
451
- logger = new import_common.Logger("Jetstream:Client");
1380
+ const targetInternal = internalName(targetServiceName);
1381
+ this.eventSubjectPrefix = `${targetInternal}.${"ev" /* Event */}.`;
1382
+ this.commandSubjectPrefix = `${targetInternal}.${"cmd" /* Command */}.`;
1383
+ this.orderedSubjectPrefix = `${targetInternal}.${"ordered" /* Ordered */}.`;
1384
+ this.isCoreMode = isCoreRpcMode(this.rootOptions.rpc);
1385
+ this.defaultRpcTimeout = isJetStreamRpcMode(this.rootOptions.rpc) ? this.rootOptions.rpc?.timeout ?? DEFAULT_JETSTREAM_RPC_TIMEOUT : this.rootOptions.rpc?.timeout ?? DEFAULT_RPC_TIMEOUT;
1386
+ const derived = deriveOtelAttrs(this.rootOptions);
1387
+ this.otel = derived.otel;
1388
+ this.serverEndpoint = derived.serverEndpoint;
1389
+ }
1390
+ logger = new import_common5.Logger("Jetstream:Client");
452
1391
  /** Target service name this client sends messages to. */
453
1392
  targetName;
454
1393
  /** Pre-cached caller name derived from rootOptions.name, computed once in constructor. */
455
1394
  callerName;
1395
+ /**
1396
+ * Subject prefixes of the form `{serviceName}__microservice.{kind}.` — one
1397
+ * per stream kind this client may publish to. Built once in the constructor
1398
+ * so producing a full subject is a single string concat with the user pattern.
1399
+ */
1400
+ eventSubjectPrefix;
1401
+ commandSubjectPrefix;
1402
+ orderedSubjectPrefix;
1403
+ /**
1404
+ * RPC configuration snapshots. The values are derived from rootOptions at
1405
+ * construction time so the publish hot path never has to re-run
1406
+ * isCoreRpcMode / getRpcTimeout on every call.
1407
+ */
1408
+ isCoreMode;
1409
+ defaultRpcTimeout;
1410
+ /** Resolved OpenTelemetry configuration, computed once in the constructor. */
1411
+ otel;
1412
+ /** Server endpoint parts used for `server.address` / `server.port` span attributes. */
1413
+ serverEndpoint;
456
1414
  /** Shared inbox for JetStream-mode RPC responses. */
457
1415
  inbox = null;
458
1416
  inboxSubscription = null;
@@ -462,6 +1420,12 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
462
1420
  pendingTimeouts = /* @__PURE__ */ new Map();
463
1421
  /** Subscription to connection status events for disconnect handling. */
464
1422
  statusSubscription = null;
1423
+ /**
1424
+ * Cached readiness flag. Once `connect()` has wired the inbox and status
1425
+ * subscription, subsequent publishes skip the `await connect()` microtask
1426
+ * and reach for the underlying connection synchronously instead.
1427
+ */
1428
+ readyForPublish = false;
465
1429
  /**
466
1430
  * Establish connection. Called automatically by NestJS on first use.
467
1431
  *
@@ -472,7 +1436,7 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
472
1436
  */
473
1437
  async connect() {
474
1438
  const nc = await this.connection.getConnection();
475
- if (isJetStreamRpcMode(this.rootOptions.rpc) && !this.inboxSubscription) {
1439
+ if (!this.isCoreMode && !this.inboxSubscription) {
476
1440
  this.setupInbox(nc);
477
1441
  }
478
1442
  this.statusSubscription ??= this.connection.status$.subscribe((status) => {
@@ -480,12 +1444,14 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
480
1444
  this.handleDisconnect();
481
1445
  }
482
1446
  });
1447
+ this.readyForPublish = true;
483
1448
  return nc;
484
1449
  }
485
1450
  /** Clean up resources: reject pending RPCs, unsubscribe from status events. */
486
1451
  async close() {
487
1452
  this.statusSubscription?.unsubscribe();
488
1453
  this.statusSubscription = null;
1454
+ this.readyForPublish = false;
489
1455
  this.rejectPendingRpcs(new Error("Client closed"));
490
1456
  }
491
1457
  /**
@@ -509,36 +1475,58 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
509
1475
  * set to the original event subject.
510
1476
  */
511
1477
  async dispatchEvent(packet) {
512
- await this.connect();
1478
+ if (!this.readyForPublish) await this.connect();
513
1479
  const { data, hdrs, messageId, schedule, ttl } = this.extractRecordData(packet.data);
514
1480
  const eventSubject = this.buildEventSubject(packet.pattern);
1481
+ const publishSubject = schedule ? this.buildScheduleSubject(eventSubject) : eventSubject;
515
1482
  const msgHeaders = this.buildHeaders(hdrs, { subject: eventSubject });
516
- if (schedule) {
517
- const scheduleSubject = this.buildScheduleSubject(eventSubject);
518
- const ack = await this.connection.getJetStreamClient().publish(scheduleSubject, this.codec.encode(data), {
1483
+ const encoded = this.codec.encode(data);
1484
+ const effectiveMsgId = messageId ?? import_nuid.nuid.next();
1485
+ const record = packet.data instanceof JetstreamRecord ? packet.data : new JetstreamRecord(data, /* @__PURE__ */ new Map());
1486
+ await withPublishSpan(
1487
+ {
1488
+ subject: publishSubject,
1489
+ pattern: packet.pattern,
1490
+ record,
1491
+ kind: detectEventKind(packet.pattern),
1492
+ payloadBytes: encoded.length,
1493
+ payload: encoded,
1494
+ messageId: effectiveMsgId,
519
1495
  headers: msgHeaders,
520
- msgID: messageId ?? import_nuid.nuid.next(),
521
- ttl,
522
- schedule: {
523
- specification: schedule.at,
524
- target: eventSubject
1496
+ serviceName: this.callerName,
1497
+ endpoint: this.serverEndpoint,
1498
+ scheduleTarget: schedule ? eventSubject : void 0
1499
+ },
1500
+ this.otel,
1501
+ async () => {
1502
+ const warnIfDuplicate = (kindLabel, ack2) => {
1503
+ if (ack2.duplicate) {
1504
+ this.logger.warn(
1505
+ `Duplicate ${kindLabel} publish detected: ${publishSubject} (seq: ${ack2.seq})`
1506
+ );
1507
+ }
1508
+ };
1509
+ if (schedule) {
1510
+ const ack2 = await this.connection.getJetStreamClient().publish(publishSubject, encoded, {
1511
+ headers: msgHeaders,
1512
+ msgID: effectiveMsgId,
1513
+ ttl,
1514
+ schedule: {
1515
+ specification: schedule.at,
1516
+ target: eventSubject
1517
+ }
1518
+ });
1519
+ warnIfDuplicate("scheduled", ack2);
1520
+ return;
525
1521
  }
526
- });
527
- if (ack.duplicate) {
528
- this.logger.warn(
529
- `Duplicate scheduled publish detected: ${scheduleSubject} (seq: ${ack.seq})`
530
- );
531
- }
532
- } else {
533
- const ack = await this.connection.getJetStreamClient().publish(eventSubject, this.codec.encode(data), {
534
- headers: msgHeaders,
535
- msgID: messageId ?? import_nuid.nuid.next(),
536
- ttl
537
- });
538
- if (ack.duplicate) {
539
- this.logger.warn(`Duplicate event publish detected: ${eventSubject} (seq: ${ack.seq})`);
1522
+ const ack = await this.connection.getJetStreamClient().publish(publishSubject, encoded, {
1523
+ headers: msgHeaders,
1524
+ msgID: effectiveMsgId,
1525
+ ttl
1526
+ });
1527
+ warnIfDuplicate("event", ack);
540
1528
  }
541
- }
1529
+ );
542
1530
  return void 0;
543
1531
  }
544
1532
  /**
@@ -548,7 +1536,7 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
548
1536
  * JetStream mode: publishes to stream + waits for inbox response.
549
1537
  */
550
1538
  publish(packet, callback) {
551
- const subject = buildSubject(this.targetName, "cmd" /* Command */, packet.pattern);
1539
+ const subject = this.commandSubjectPrefix + packet.pattern;
552
1540
  const { data, hdrs, timeout, messageId, schedule, ttl } = this.extractRecordData(packet.data);
553
1541
  if (schedule) {
554
1542
  this.logger.warn(
@@ -565,7 +1553,7 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
565
1553
  callback({ err: new Error("Internal transport error"), response: null, isDisposed: true });
566
1554
  };
567
1555
  let jetStreamCorrelationId = null;
568
- if (isCoreRpcMode(this.rootOptions.rpc)) {
1556
+ if (this.isCoreMode) {
569
1557
  this.publishCoreRpc(subject, data, hdrs, timeout, callback).catch(onUnhandled);
570
1558
  } else {
571
1559
  jetStreamCorrelationId = import_nuid.nuid.next();
@@ -589,37 +1577,102 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
589
1577
  }
590
1578
  /** Core mode: nc.request() with timeout. */
591
1579
  async publishCoreRpc(subject, data, customHeaders, timeout, callback) {
1580
+ const effectiveTimeout = timeout ?? this.defaultRpcTimeout;
1581
+ const hdrs = this.buildHeaders(customHeaders, { subject });
1582
+ const encoded = this.codec.encode(data);
1583
+ const spanHandle = beginRpcClientSpan(
1584
+ {
1585
+ subject,
1586
+ payloadBytes: encoded.length,
1587
+ payload: encoded,
1588
+ headers: hdrs,
1589
+ serviceName: this.callerName,
1590
+ endpoint: this.serverEndpoint
1591
+ },
1592
+ this.otel
1593
+ );
592
1594
  try {
593
- const nc = await this.connect();
594
- const effectiveTimeout = timeout ?? this.getRpcTimeout();
595
- const hdrs = this.buildHeaders(customHeaders, { subject });
596
- const response = await nc.request(subject, this.codec.encode(data), {
597
- timeout: effectiveTimeout,
598
- headers: hdrs
599
- });
1595
+ const nc = this.readyForPublish ? this.connection.unwrap : await this.connect();
1596
+ const response = await import_api8.context.with(
1597
+ spanHandle.activeContext,
1598
+ () => nc.request(subject, encoded, {
1599
+ timeout: effectiveTimeout,
1600
+ headers: hdrs
1601
+ })
1602
+ );
600
1603
  const decoded = this.codec.decode(response.data);
601
1604
  if (response.headers?.get("x-error" /* Error */)) {
1605
+ spanHandle.finish({ kind: "reply-error" /* ReplyError */, replyPayload: decoded });
602
1606
  callback({ err: decoded, response: null, isDisposed: true });
603
1607
  } else {
1608
+ spanHandle.finish({ kind: "ok" /* Ok */, reply: decoded });
604
1609
  callback({ err: null, response: decoded, isDisposed: true });
605
1610
  }
606
1611
  } catch (err) {
607
1612
  const error = err instanceof Error ? err : new Error("Unknown error");
608
- this.logger.error(`Core RPC error (${subject}):`, err);
609
- this.eventBus.emit("error" /* Error */, error, "client-rpc");
1613
+ if (error instanceof import_transport_node.TimeoutError) {
1614
+ spanHandle.finish({ kind: "timeout" /* Timeout */ });
1615
+ this.eventBus.emit("rpcTimeout" /* RpcTimeout */, subject, "");
1616
+ } else {
1617
+ spanHandle.finish({ kind: "error" /* Error */, error });
1618
+ this.eventBus.emit("error" /* Error */, error, "client-rpc");
1619
+ }
610
1620
  callback({ err: error, response: null, isDisposed: true });
611
1621
  }
612
1622
  }
613
1623
  /** JetStream mode: publish to stream + wait for inbox response. */
614
1624
  async publishJetStreamRpc(subject, data, callback, options) {
615
1625
  const { headers: customHeaders, correlationId, messageId } = options;
616
- const effectiveTimeout = options.timeout ?? this.getRpcTimeout();
617
- this.pendingMessages.set(correlationId, callback);
1626
+ const effectiveTimeout = options.timeout ?? this.defaultRpcTimeout;
1627
+ const hdrs = this.buildHeaders(customHeaders, {
1628
+ subject,
1629
+ correlationId,
1630
+ replyTo: this.inbox ?? ""
1631
+ });
1632
+ const encoded = this.codec.encode(data);
1633
+ const spanHandle = beginRpcClientSpan(
1634
+ {
1635
+ subject,
1636
+ correlationId,
1637
+ payloadBytes: encoded.length,
1638
+ payload: encoded,
1639
+ messageId,
1640
+ headers: hdrs,
1641
+ serviceName: this.callerName,
1642
+ endpoint: this.serverEndpoint
1643
+ },
1644
+ this.otel
1645
+ );
1646
+ this.pendingMessages.set(correlationId, (packet) => {
1647
+ if (packet.err) {
1648
+ if (packet.err instanceof Error) {
1649
+ spanHandle.finish({ kind: "error" /* Error */, error: packet.err });
1650
+ } else {
1651
+ spanHandle.finish({ kind: "reply-error" /* ReplyError */, replyPayload: packet.err });
1652
+ }
1653
+ } else {
1654
+ spanHandle.finish({ kind: "ok" /* Ok */, reply: packet.response });
1655
+ }
1656
+ callback(packet);
1657
+ });
1658
+ const timeoutId = setTimeout(() => {
1659
+ if (!this.pendingMessages.has(correlationId)) return;
1660
+ this.pendingTimeouts.delete(correlationId);
1661
+ this.pendingMessages.delete(correlationId);
1662
+ spanHandle.finish({ kind: "timeout" /* Timeout */ });
1663
+ this.eventBus.emit("rpcTimeout" /* RpcTimeout */, subject, correlationId);
1664
+ callback({ err: new Error(RPC_TIMEOUT_MESSAGE), response: null, isDisposed: true });
1665
+ }, effectiveTimeout);
1666
+ this.pendingTimeouts.set(correlationId, timeoutId);
618
1667
  try {
619
- await this.connect();
1668
+ if (!this.readyForPublish) await this.connect();
620
1669
  if (!this.pendingMessages.has(correlationId)) return;
621
1670
  if (!this.inbox) {
1671
+ clearTimeout(timeoutId);
1672
+ this.pendingTimeouts.delete(correlationId);
622
1673
  this.pendingMessages.delete(correlationId);
1674
+ const inboxError = new Error("Inbox not initialized");
1675
+ spanHandle.finish({ kind: "error" /* Error */, error: inboxError });
623
1676
  callback({
624
1677
  err: new Error("Inbox not initialized \u2014 JetStream RPC mode requires a connected inbox"),
625
1678
  response: null,
@@ -627,24 +1680,13 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
627
1680
  });
628
1681
  return;
629
1682
  }
630
- const timeoutId = setTimeout(() => {
631
- if (!this.pendingMessages.has(correlationId)) return;
632
- this.pendingTimeouts.delete(correlationId);
633
- this.pendingMessages.delete(correlationId);
634
- this.logger.error(`JetStream RPC timeout (${effectiveTimeout}ms): ${subject}`);
635
- this.eventBus.emit("rpcTimeout" /* RpcTimeout */, subject, correlationId);
636
- callback({ err: new Error("RPC timeout"), response: null, isDisposed: true });
637
- }, effectiveTimeout);
638
- this.pendingTimeouts.set(correlationId, timeoutId);
639
- const hdrs = this.buildHeaders(customHeaders, {
640
- subject,
641
- correlationId,
642
- replyTo: this.inbox
643
- });
644
- await this.connection.getJetStreamClient().publish(subject, this.codec.encode(data), {
645
- headers: hdrs,
646
- msgID: messageId ?? import_nuid.nuid.next()
647
- });
1683
+ await import_api8.context.with(
1684
+ spanHandle.activeContext,
1685
+ () => this.connection.getJetStreamClient().publish(subject, encoded, {
1686
+ headers: hdrs,
1687
+ msgID: messageId ?? import_nuid.nuid.next()
1688
+ })
1689
+ );
648
1690
  } catch (err) {
649
1691
  const existingTimeout = this.pendingTimeouts.get(correlationId);
650
1692
  if (existingTimeout) {
@@ -654,7 +1696,8 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
654
1696
  if (!this.pendingMessages.has(correlationId)) return;
655
1697
  this.pendingMessages.delete(correlationId);
656
1698
  const error = err instanceof Error ? err : new Error("Unknown error");
657
- this.logger.error(`JetStream RPC publish error (${subject}):`, err);
1699
+ spanHandle.finish({ kind: "error" /* Error */, error });
1700
+ this.eventBus.emit("error" /* Error */, error, `jetstream-rpc-publish:${subject}`);
658
1701
  callback({ err: error, response: null, isDisposed: true });
659
1702
  }
660
1703
  }
@@ -662,6 +1705,7 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
662
1705
  handleDisconnect() {
663
1706
  this.rejectPendingRpcs(new Error("Connection lost"));
664
1707
  this.inbox = null;
1708
+ this.readyForPublish = false;
665
1709
  }
666
1710
  /** Reject all pending RPC callbacks, clear timeouts, and tear down inbox. */
667
1711
  rejectPendingRpcs(error) {
@@ -725,19 +1769,22 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
725
1769
  this.pendingMessages.delete(correlationId);
726
1770
  }
727
1771
  }
728
- /** Build event subject — workqueue, broadcast, or ordered. */
1772
+ /**
1773
+ * Resolve a user pattern to a fully-qualified NATS subject, dispatching
1774
+ * between the event, broadcast, and ordered prefixes.
1775
+ *
1776
+ * The leading-char check short-circuits the `startsWith` comparisons for
1777
+ * patterns that cannot possibly carry a broadcast/ordered marker, which is
1778
+ * the overwhelmingly common case.
1779
+ */
729
1780
  buildEventSubject(pattern) {
730
- if (pattern.startsWith("broadcast:" /* Broadcast */)) {
731
- return buildBroadcastSubject(pattern.slice("broadcast:" /* Broadcast */.length));
732
- }
733
- if (pattern.startsWith("ordered:" /* Ordered */)) {
734
- return buildSubject(
735
- this.targetName,
736
- "ordered" /* Ordered */,
737
- pattern.slice("ordered:" /* Ordered */.length)
738
- );
1781
+ if (pattern.charCodeAt(0) === 98 && pattern.startsWith("broadcast:" /* Broadcast */)) {
1782
+ return BROADCAST_SUBJECT_PREFIX + pattern.slice("broadcast:" /* Broadcast */.length);
739
1783
  }
740
- return buildSubject(this.targetName, "ev" /* Event */, pattern);
1784
+ if (pattern.charCodeAt(0) === 111 && pattern.startsWith("ordered:" /* Ordered */)) {
1785
+ return this.orderedSubjectPrefix + pattern.slice("ordered:" /* Ordered */.length);
1786
+ }
1787
+ return this.eventSubjectPrefix + pattern;
741
1788
  }
742
1789
  /** Build NATS headers merging custom headers with transport headers. */
743
1790
  buildHeaders(customHeaders, transport) {
@@ -762,7 +1809,7 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
762
1809
  if (rawData instanceof JetstreamRecord) {
763
1810
  return {
764
1811
  data: rawData.data,
765
- hdrs: rawData.headers.size > 0 ? new Map(rawData.headers) : null,
1812
+ hdrs: rawData.headers.size > 0 ? rawData.headers : null,
766
1813
  timeout: rawData.timeout,
767
1814
  messageId: rawData.messageId,
768
1815
  schedule: rawData.schedule,
@@ -805,11 +1852,6 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
805
1852
  const pattern = withoutPrefix.slice(dotIndex + 1);
806
1853
  return `${targetPrefix}_sch.${pattern}`;
807
1854
  }
808
- getRpcTimeout() {
809
- if (!this.rootOptions.rpc) return DEFAULT_RPC_TIMEOUT;
810
- const defaultTimeout = isJetStreamRpcMode(this.rootOptions.rpc) ? DEFAULT_JETSTREAM_RPC_TIMEOUT : DEFAULT_RPC_TIMEOUT;
811
- return this.rootOptions.rpc.timeout ?? defaultTimeout;
812
- }
813
1855
  };
814
1856
 
815
1857
  // src/codec/json.codec.ts
@@ -824,10 +1866,23 @@ var JsonCodec = class {
824
1866
  }
825
1867
  };
826
1868
 
1869
+ // src/codec/msgpack.codec.ts
1870
+ var MsgpackCodec = class {
1871
+ constructor(packr) {
1872
+ this.packr = packr;
1873
+ }
1874
+ encode(data) {
1875
+ return this.packr.pack(data);
1876
+ }
1877
+ decode(data) {
1878
+ return this.packr.unpack(data);
1879
+ }
1880
+ };
1881
+
827
1882
  // src/connection/connection.provider.ts
828
- var import_common2 = require("@nestjs/common");
1883
+ var import_common6 = require("@nestjs/common");
829
1884
  var import_transport_node2 = require("@nats-io/transport-node");
830
- var import_jetstream7 = require("@nats-io/jetstream");
1885
+ var import_jetstream8 = require("@nats-io/jetstream");
831
1886
  var import_rxjs = require("rxjs");
832
1887
  var DEFAULT_OPTIONS = {
833
1888
  maxReconnectAttempts: -1,
@@ -837,6 +1892,10 @@ var ConnectionProvider = class {
837
1892
  constructor(options, eventBus) {
838
1893
  this.options = options;
839
1894
  this.eventBus = eventBus;
1895
+ const derived = deriveOtelAttrs(options);
1896
+ this.otel = derived.otel;
1897
+ this.otelServiceName = derived.serviceName;
1898
+ this.otelEndpoint = derived.serverEndpoint;
840
1899
  this.nc$ = (0, import_rxjs.defer)(() => this.getConnection()).pipe(
841
1900
  (0, import_rxjs.shareReplay)({ bufferSize: 1, refCount: false })
842
1901
  );
@@ -849,12 +1908,16 @@ var ConnectionProvider = class {
849
1908
  nc$;
850
1909
  /** Live stream of connection status events (no replay). */
851
1910
  status$;
852
- logger = new import_common2.Logger("Jetstream:Connection");
1911
+ logger = new import_common6.Logger("Jetstream:Connection");
853
1912
  connection = null;
854
1913
  connectionPromise = null;
855
1914
  jsClient = null;
856
1915
  jsmInstance = null;
857
1916
  jsmPromise = null;
1917
+ otel;
1918
+ otelServiceName;
1919
+ otelEndpoint;
1920
+ lifecycleSpan = null;
858
1921
  /**
859
1922
  * Establish NATS connection. Idempotent — returns cached connection on subsequent calls.
860
1923
  *
@@ -897,7 +1960,7 @@ var ConnectionProvider = class {
897
1960
  if (!this.connection || this.connection.isClosed()) {
898
1961
  throw new Error("Not connected \u2014 call getConnection() before getJetStreamClient()");
899
1962
  }
900
- this.jsClient ??= (0, import_jetstream7.jetstream)(this.connection);
1963
+ this.jsClient ??= (0, import_jetstream8.jetstream)(this.connection);
901
1964
  return this.jsClient;
902
1965
  }
903
1966
  /** Direct access to the raw NATS connection, or `null` if not yet connected. */
@@ -918,14 +1981,24 @@ var ConnectionProvider = class {
918
1981
  }
919
1982
  if (!this.connection || this.connection.isClosed()) return;
920
1983
  try {
921
- await this.connection.drain();
922
- await this.connection.closed();
923
- } catch {
924
- try {
925
- await this.connection.close();
926
- } catch {
927
- }
1984
+ await withShutdownSpan(
1985
+ this.otel,
1986
+ { serviceName: this.otelServiceName, endpoint: this.otelEndpoint },
1987
+ async () => {
1988
+ try {
1989
+ await this.connection?.drain();
1990
+ await this.connection?.closed();
1991
+ } catch {
1992
+ try {
1993
+ await this.connection?.close();
1994
+ } catch {
1995
+ }
1996
+ }
1997
+ }
1998
+ );
928
1999
  } finally {
2000
+ this.lifecycleSpan?.finish();
2001
+ this.lifecycleSpan = null;
929
2002
  this.connection = null;
930
2003
  this.connectionPromise = null;
931
2004
  this.jsClient = null;
@@ -936,7 +2009,7 @@ var ConnectionProvider = class {
936
2009
  async initJetStreamManager() {
937
2010
  try {
938
2011
  const nc = await this.getConnection();
939
- this.jsmInstance = await (0, import_jetstream7.jetstreamManager)(nc);
2012
+ this.jsmInstance = await (0, import_jetstream8.jetstreamManager)(nc);
940
2013
  this.logger.log("JetStream manager initialized");
941
2014
  return this.jsmInstance;
942
2015
  } finally {
@@ -945,17 +2018,25 @@ var ConnectionProvider = class {
945
2018
  }
946
2019
  /** Internal: establish the physical connection with reconnect monitoring. */
947
2020
  async establish() {
948
- const name = internalName(this.options.name);
949
2021
  try {
950
2022
  const nc = await (0, import_transport_node2.connect)({
951
2023
  ...DEFAULT_OPTIONS,
2024
+ // Default the NATS connection name to the OTel-derived service name so
2025
+ // `nats server info` lines up with span attributes, but let user-supplied
2026
+ // `connectionOptions.name` win when set.
2027
+ name: this.otelServiceName,
952
2028
  ...this.options.connectionOptions,
953
- servers: this.options.servers,
954
- name
2029
+ servers: this.options.servers
955
2030
  });
956
2031
  this.connection = nc;
957
2032
  this.logger.log(`NATS connection established: ${nc.getServer()}`);
958
2033
  this.eventBus.emit("connect" /* Connect */, nc.getServer());
2034
+ this.lifecycleSpan?.finish();
2035
+ this.lifecycleSpan = beginConnectionLifecycleSpan(this.otel, {
2036
+ serviceName: this.otelServiceName,
2037
+ endpoint: this.otelEndpoint,
2038
+ server: nc.getServer()
2039
+ });
959
2040
  this.monitorStatus(nc);
960
2041
  return nc;
961
2042
  } catch (err) {
@@ -965,37 +2046,55 @@ var ConnectionProvider = class {
965
2046
  throw err;
966
2047
  }
967
2048
  }
2049
+ /** Handle a single `nc.status()` event, emitting hooks and span events. */
2050
+ handleStatusEvent(status, nc) {
2051
+ switch (status.type) {
2052
+ case "disconnect":
2053
+ this.eventBus.emit("disconnect" /* Disconnect */);
2054
+ this.lifecycleSpan?.recordEvent(EVENT_CONNECTION_DISCONNECTED);
2055
+ break;
2056
+ case "reconnect":
2057
+ this.jsClient = null;
2058
+ this.jsmInstance = null;
2059
+ this.jsmPromise = null;
2060
+ this.eventBus.emit("reconnect" /* Reconnect */, nc.getServer());
2061
+ this.lifecycleSpan?.recordEvent(EVENT_CONNECTION_RECONNECTED, {
2062
+ [ATTR_NATS_CONNECTION_SERVER]: nc.getServer()
2063
+ });
2064
+ break;
2065
+ case "error":
2066
+ this.eventBus.emit(
2067
+ "error" /* Error */,
2068
+ status.error,
2069
+ "connection"
2070
+ );
2071
+ break;
2072
+ case "update":
2073
+ case "ldm":
2074
+ case "reconnecting":
2075
+ case "ping":
2076
+ case "staleConnection":
2077
+ case "forceReconnect":
2078
+ case "slowConsumer":
2079
+ case "close":
2080
+ break;
2081
+ default: {
2082
+ const _exhaustive = status;
2083
+ const unknown = _exhaustive.type ?? "unknown";
2084
+ this.logger.warn(`Unhandled NATS status event: ${unknown}`);
2085
+ }
2086
+ }
2087
+ }
968
2088
  /** Subscribe to connection status events and emit hooks. */
969
2089
  monitorStatus(nc) {
970
2090
  void (async () => {
971
- for await (const status of nc.status()) {
972
- switch (status.type) {
973
- case "disconnect":
974
- this.eventBus.emit("disconnect" /* Disconnect */);
975
- break;
976
- case "reconnect":
977
- this.jsClient = null;
978
- this.jsmInstance = null;
979
- this.jsmPromise = null;
980
- this.eventBus.emit("reconnect" /* Reconnect */, nc.getServer());
981
- break;
982
- case "error":
983
- this.eventBus.emit(
984
- "error" /* Error */,
985
- status.error,
986
- "connection"
987
- );
988
- break;
989
- case "update":
990
- case "ldm":
991
- case "reconnecting":
992
- case "ping":
993
- case "staleConnection":
994
- case "forceReconnect":
995
- case "slowConsumer":
996
- case "close":
997
- break;
2091
+ try {
2092
+ for await (const status of nc.status()) {
2093
+ this.handleStatusEvent(status, nc);
998
2094
  }
2095
+ } finally {
2096
+ this.lifecycleSpan?.finish();
2097
+ this.lifecycleSpan = null;
999
2098
  }
1000
2099
  })().catch((err) => {
1001
2100
  this.logger.error("Status monitor error", err);
@@ -1007,8 +2106,8 @@ var ConnectionProvider = class {
1007
2106
  var EventBus = class {
1008
2107
  hooks;
1009
2108
  logger;
1010
- constructor(logger, hooks) {
1011
- this.logger = logger;
2109
+ constructor(logger5, hooks) {
2110
+ this.logger = logger5;
1012
2111
  this.hooks = hooks ?? {};
1013
2112
  }
1014
2113
  /**
@@ -1036,6 +2135,15 @@ var EventBus = class {
1036
2135
  kind
1037
2136
  );
1038
2137
  }
2138
+ /**
2139
+ * Check whether a hook is registered for the given event.
2140
+ *
2141
+ * Used by the routing hot path to elide the emit call entirely when the
2142
+ * transport owner did not register a listener.
2143
+ */
2144
+ hasHook(event) {
2145
+ return this.hooks[event] !== void 0;
2146
+ }
1039
2147
  callHook(event, hook, ...args) {
1040
2148
  try {
1041
2149
  const result = hook(...args);
@@ -1055,12 +2163,12 @@ var EventBus = class {
1055
2163
  };
1056
2164
 
1057
2165
  // src/health/jetstream.health-indicator.ts
1058
- var import_common3 = require("@nestjs/common");
2166
+ var import_common7 = require("@nestjs/common");
1059
2167
  var JetstreamHealthIndicator = class {
1060
2168
  constructor(connection) {
1061
2169
  this.connection = connection;
1062
2170
  }
1063
- logger = new import_common3.Logger("Jetstream:Health");
2171
+ logger = new import_common7.Logger("Jetstream:Health");
1064
2172
  /**
1065
2173
  * Plain health status check.
1066
2174
  *
@@ -1117,7 +2225,7 @@ var JetstreamHealthIndicator = class {
1117
2225
  }
1118
2226
  };
1119
2227
  JetstreamHealthIndicator = __decorateClass([
1120
- (0, import_common3.Injectable)()
2228
+ (0, import_common7.Injectable)()
1121
2229
  ], JetstreamHealthIndicator);
1122
2230
 
1123
2231
  // src/server/strategy.ts
@@ -1266,7 +2374,7 @@ var JetstreamStrategy = class extends import_microservices2.Server {
1266
2374
  };
1267
2375
 
1268
2376
  // src/server/core-rpc.server.ts
1269
- var import_common4 = require("@nestjs/common");
2377
+ var import_common8 = require("@nestjs/common");
1270
2378
  var import_transport_node3 = require("@nats-io/transport-node");
1271
2379
 
1272
2380
  // src/context/rpc.context.ts
@@ -1276,9 +2384,6 @@ var RpcContext = class extends import_microservices3.BaseRpcContext {
1276
2384
  _retryDelay;
1277
2385
  _shouldTerminate = false;
1278
2386
  _terminateReason;
1279
- // ---------------------------------------------------------------------------
1280
- // Message accessors
1281
- // ---------------------------------------------------------------------------
1282
2387
  /**
1283
2388
  * Get the underlying NATS message.
1284
2389
  *
@@ -1313,9 +2418,6 @@ var RpcContext = class extends import_microservices3.BaseRpcContext {
1313
2418
  isJetStream() {
1314
2419
  return "ack" in this.args[0];
1315
2420
  }
1316
- // ---------------------------------------------------------------------------
1317
- // JetStream metadata (return undefined for Core NATS messages)
1318
- // ---------------------------------------------------------------------------
1319
2421
  /** How many times this message has been delivered. */
1320
2422
  getDeliveryCount() {
1321
2423
  return this.asJetStream()?.info.deliveryCount;
@@ -1337,9 +2439,6 @@ var RpcContext = class extends import_microservices3.BaseRpcContext {
1337
2439
  getCallerName() {
1338
2440
  return this.getHeader("x-caller-name" /* CallerName */);
1339
2441
  }
1340
- // ---------------------------------------------------------------------------
1341
- // Handler-controlled settlement
1342
- // ---------------------------------------------------------------------------
1343
2442
  /**
1344
2443
  * Signal the transport to retry (nak) this message instead of acknowledging it.
1345
2444
  *
@@ -1384,9 +2483,6 @@ var RpcContext = class extends import_microservices3.BaseRpcContext {
1384
2483
  throw new Error(`${method}() is only available for JetStream messages`);
1385
2484
  }
1386
2485
  }
1387
- // ---------------------------------------------------------------------------
1388
- // Transport-facing state (read by EventRouter)
1389
- // ---------------------------------------------------------------------------
1390
2486
  /** @internal */
1391
2487
  get shouldRetry() {
1392
2488
  return this._shouldRetry;
@@ -1418,16 +2514,71 @@ var resolveAckExtensionInterval = (config, ackWaitNanos) => {
1418
2514
  const interval = Math.floor(ackWaitNanos / 1e6 / 2);
1419
2515
  return Math.max(interval, MIN_ACK_EXTENSION_INTERVAL);
1420
2516
  };
2517
+ var AckExtensionPool = class {
2518
+ entries = /* @__PURE__ */ new Set();
2519
+ handle = null;
2520
+ handleFireAt = 0;
2521
+ schedule(msg, interval) {
2522
+ const entry = {
2523
+ msg,
2524
+ interval,
2525
+ nextFireAt: Date.now() + interval,
2526
+ active: true
2527
+ };
2528
+ this.entries.add(entry);
2529
+ this.ensureWake(entry.nextFireAt);
2530
+ return entry;
2531
+ }
2532
+ cancel(entry) {
2533
+ if (!entry.active) return;
2534
+ entry.active = false;
2535
+ this.entries.delete(entry);
2536
+ if (this.entries.size === 0 && this.handle !== null) {
2537
+ clearTimeout(this.handle);
2538
+ this.handle = null;
2539
+ this.handleFireAt = 0;
2540
+ }
2541
+ }
2542
+ /**
2543
+ * Ensure the shared timer will fire no later than `dueAt`. If an earlier
2544
+ * wake is already scheduled, leave it; otherwise replace it with a tighter one.
2545
+ */
2546
+ ensureWake(dueAt) {
2547
+ if (this.handle !== null && this.handleFireAt <= dueAt) return;
2548
+ if (this.handle !== null) clearTimeout(this.handle);
2549
+ const delay = Math.max(0, dueAt - Date.now());
2550
+ const handle = setTimeout(() => {
2551
+ this.tick();
2552
+ }, delay);
2553
+ if (typeof handle.unref === "function") handle.unref();
2554
+ this.handle = handle;
2555
+ this.handleFireAt = dueAt;
2556
+ }
2557
+ tick() {
2558
+ this.handle = null;
2559
+ this.handleFireAt = 0;
2560
+ const now = Date.now();
2561
+ let earliest = Infinity;
2562
+ for (const entry of this.entries) {
2563
+ if (!entry.active) continue;
2564
+ if (entry.nextFireAt <= now) {
2565
+ try {
2566
+ entry.msg.working();
2567
+ } catch {
2568
+ }
2569
+ entry.nextFireAt = now + entry.interval;
2570
+ }
2571
+ if (entry.nextFireAt < earliest) earliest = entry.nextFireAt;
2572
+ }
2573
+ if (earliest !== Infinity) this.ensureWake(earliest);
2574
+ }
2575
+ };
2576
+ var pool = new AckExtensionPool();
1421
2577
  var startAckExtensionTimer = (msg, interval) => {
1422
2578
  if (interval === null || interval <= 0) return null;
1423
- const timer2 = setInterval(() => {
1424
- try {
1425
- msg.working();
1426
- } catch {
1427
- }
1428
- }, interval);
2579
+ const entry = pool.schedule(msg, interval);
1429
2580
  return () => {
1430
- clearInterval(timer2);
2581
+ pool.cancel(entry);
1431
2582
  };
1432
2583
  };
1433
2584
 
@@ -1441,11 +2592,8 @@ var serializeError = (err) => {
1441
2592
 
1442
2593
  // src/utils/unwrap-result.ts
1443
2594
  var import_rxjs2 = require("rxjs");
1444
- var RESOLVED_VOID = Promise.resolve(void 0);
1445
- var RESOLVED_NULL = Promise.resolve(null);
1446
2595
  var unwrapResult = (result) => {
1447
- if (result === void 0) return RESOLVED_VOID;
1448
- if (result === null) return RESOLVED_NULL;
2596
+ if (result === void 0 || result === null) return result;
1449
2597
  if ((0, import_rxjs2.isObservable)(result)) {
1450
2598
  return subscribeToFirst(result);
1451
2599
  }
@@ -1454,8 +2602,9 @@ var unwrapResult = (result) => {
1454
2602
  (resolved) => (0, import_rxjs2.isObservable)(resolved) ? subscribeToFirst(resolved) : resolved
1455
2603
  );
1456
2604
  }
1457
- return Promise.resolve(result);
2605
+ return result;
1458
2606
  };
2607
+ var isPromiseLike2 = (value) => value !== null && typeof value === "object" && typeof value.then === "function";
1459
2608
  var subscribeToFirst = (obs) => new Promise((resolve, reject) => {
1460
2609
  let done = false;
1461
2610
  let subscription = null;
@@ -1485,20 +2634,25 @@ var subscribeToFirst = (obs) => new Promise((resolve, reject) => {
1485
2634
  // src/server/core-rpc.server.ts
1486
2635
  var CoreRpcServer = class {
1487
2636
  constructor(options, connection, patternRegistry, codec, eventBus) {
1488
- this.options = options;
1489
2637
  this.connection = connection;
1490
2638
  this.patternRegistry = patternRegistry;
1491
2639
  this.codec = codec;
1492
2640
  this.eventBus = eventBus;
2641
+ const derived = deriveOtelAttrs(options);
2642
+ this.otel = derived.otel;
2643
+ this.serviceName = derived.serviceName;
2644
+ this.serverEndpoint = derived.serverEndpoint;
1493
2645
  }
1494
- logger = new import_common4.Logger("Jetstream:CoreRpc");
2646
+ logger = new import_common8.Logger("Jetstream:CoreRpc");
1495
2647
  subscription = null;
2648
+ otel;
2649
+ serviceName;
2650
+ serverEndpoint;
1496
2651
  /** Start listening for RPC requests on the command subject. */
1497
2652
  async start() {
1498
2653
  const nc = await this.connection.getConnection();
1499
- const serviceName = internalName(this.options.name);
1500
- const subject = `${serviceName}.cmd.>`;
1501
- const queue = `${serviceName}_cmd_queue`;
2654
+ const subject = `${this.serviceName}.cmd.>`;
2655
+ const queue = `${this.serviceName}_cmd_queue`;
1502
2656
  this.subscription = nc.subscribe(subject, {
1503
2657
  queue,
1504
2658
  callback: (err, msg) => {
@@ -1543,10 +2697,29 @@ var CoreRpcServer = class {
1543
2697
  }
1544
2698
  const ctx = new RpcContext([msg]);
1545
2699
  try {
1546
- const result = await unwrapResult(handler(data, ctx));
1547
- msg.respond(this.codec.encode(result));
2700
+ const raw = await withConsumeSpan(
2701
+ {
2702
+ subject: msg.subject,
2703
+ msg,
2704
+ kind: "rpc" /* Rpc */,
2705
+ payloadBytes: msg.data.length,
2706
+ handlerMetadata: { pattern: msg.subject },
2707
+ serviceName: this.serviceName,
2708
+ endpoint: this.serverEndpoint
2709
+ },
2710
+ this.otel,
2711
+ () => {
2712
+ const out = unwrapResult(handler(data, ctx));
2713
+ return isPromiseLike2(out) ? out : out;
2714
+ }
2715
+ );
2716
+ msg.respond(this.codec.encode(raw));
1548
2717
  } catch (err) {
1549
- this.logger.error(`Handler error for Core RPC ${msg.subject}:`, err);
2718
+ this.eventBus.emit(
2719
+ "error" /* Error */,
2720
+ err instanceof Error ? err : new Error(String(err)),
2721
+ `core-rpc-handler:${msg.subject}`
2722
+ );
1550
2723
  this.respondWithError(msg, err);
1551
2724
  }
1552
2725
  }
@@ -1563,9 +2736,17 @@ var CoreRpcServer = class {
1563
2736
  };
1564
2737
 
1565
2738
  // src/server/infrastructure/stream.provider.ts
1566
- var import_common6 = require("@nestjs/common");
2739
+ var import_common10 = require("@nestjs/common");
1567
2740
  var import_jetstream14 = require("@nats-io/jetstream");
1568
2741
 
2742
+ // src/server/infrastructure/nats-error-codes.ts
2743
+ var NatsErrorCode = /* @__PURE__ */ ((NatsErrorCode2) => {
2744
+ NatsErrorCode2[NatsErrorCode2["ConsumerNotFound"] = 10014] = "ConsumerNotFound";
2745
+ NatsErrorCode2[NatsErrorCode2["ConsumerAlreadyExists"] = 10148] = "ConsumerAlreadyExists";
2746
+ NatsErrorCode2[NatsErrorCode2["StreamNotFound"] = 10059] = "StreamNotFound";
2747
+ return NatsErrorCode2;
2748
+ })(NatsErrorCode || {});
2749
+
1569
2750
  // src/server/infrastructure/stream-config-diff.ts
1570
2751
  var TRANSPORT_CONTROLLED_PROPERTIES = /* @__PURE__ */ new Set([
1571
2752
  "retention"
@@ -1622,7 +2803,7 @@ var isEqual = (a, b) => {
1622
2803
  };
1623
2804
 
1624
2805
  // src/server/infrastructure/stream-migration.ts
1625
- var import_common5 = require("@nestjs/common");
2806
+ var import_common9 = require("@nestjs/common");
1626
2807
  var import_jetstream13 = require("@nats-io/jetstream");
1627
2808
  var MIGRATION_BACKUP_SUFFIX = "__migration_backup";
1628
2809
  var DEFAULT_SOURCING_TIMEOUT_MS = 3e4;
@@ -1631,7 +2812,7 @@ var StreamMigration = class {
1631
2812
  constructor(sourcingTimeoutMs = DEFAULT_SOURCING_TIMEOUT_MS) {
1632
2813
  this.sourcingTimeoutMs = sourcingTimeoutMs;
1633
2814
  }
1634
- logger = new import_common5.Logger("Jetstream:Stream");
2815
+ logger = new import_common9.Logger("Jetstream:Stream");
1635
2816
  async migrate(jsm, streamName2, newConfig) {
1636
2817
  const backupName = `${streamName2}${MIGRATION_BACKUP_SUFFIX}`;
1637
2818
  const startTime = Date.now();
@@ -1713,9 +2894,16 @@ var StreamProvider = class {
1713
2894
  constructor(options, connection) {
1714
2895
  this.options = options;
1715
2896
  this.connection = connection;
2897
+ const derived = deriveOtelAttrs(options);
2898
+ this.otel = derived.otel;
2899
+ this.otelServiceName = derived.serviceName;
2900
+ this.otelEndpoint = derived.serverEndpoint;
1716
2901
  }
1717
- logger = new import_common6.Logger("Jetstream:Stream");
2902
+ logger = new import_common10.Logger("Jetstream:Stream");
1718
2903
  migration = new StreamMigration();
2904
+ otel;
2905
+ otelServiceName;
2906
+ otelEndpoint;
1719
2907
  /**
1720
2908
  * Ensure all required streams exist with correct configuration.
1721
2909
  *
@@ -1761,32 +2949,56 @@ var StreamProvider = class {
1761
2949
  /** Ensure a single stream exists, creating or updating as needed. */
1762
2950
  async ensureStream(jsm, kind) {
1763
2951
  const config = this.buildConfig(kind);
1764
- this.logger.log(`Ensuring stream: ${config.name}`);
1765
- try {
1766
- const currentInfo = await jsm.streams.info(config.name);
1767
- return await this.handleExistingStream(jsm, currentInfo, config);
1768
- } catch (err) {
1769
- if (err instanceof import_jetstream14.JetStreamApiError && err.apiError().err_code === 10059 /* StreamNotFound */) {
1770
- this.logger.log(`Creating stream: ${config.name}`);
1771
- return await jsm.streams.add(config);
2952
+ return withProvisioningSpan(
2953
+ this.otel,
2954
+ {
2955
+ serviceName: this.otelServiceName,
2956
+ endpoint: this.otelEndpoint,
2957
+ entity: "stream",
2958
+ name: config.name,
2959
+ action: "ensure"
2960
+ },
2961
+ async () => {
2962
+ this.logger.log(`Ensuring stream: ${config.name}`);
2963
+ try {
2964
+ const currentInfo = await jsm.streams.info(config.name);
2965
+ return await this.handleExistingStream(jsm, currentInfo, config);
2966
+ } catch (err) {
2967
+ if (err instanceof import_jetstream14.JetStreamApiError && err.apiError().err_code === 10059 /* StreamNotFound */) {
2968
+ this.logger.log(`Creating stream: ${config.name}`);
2969
+ return await jsm.streams.add(config);
2970
+ }
2971
+ throw err;
2972
+ }
1772
2973
  }
1773
- throw err;
1774
- }
2974
+ );
1775
2975
  }
1776
2976
  /** Ensure a dead-letter queue stream exists, creating or updating as needed. */
1777
2977
  async ensureDlqStream(jsm) {
1778
2978
  const config = this.buildDlqConfig();
1779
- this.logger.log(`Ensuring DLQ stream: ${config.name}`);
1780
- try {
1781
- const currentInfo = await jsm.streams.info(config.name);
1782
- return await this.handleExistingStream(jsm, currentInfo, config);
1783
- } catch (err) {
1784
- if (err instanceof import_jetstream14.JetStreamApiError && err.apiError().err_code === 10059 /* StreamNotFound */) {
1785
- this.logger.log(`Creating DLQ stream: ${config.name}`);
1786
- return await jsm.streams.add(config);
2979
+ return withProvisioningSpan(
2980
+ this.otel,
2981
+ {
2982
+ serviceName: this.otelServiceName,
2983
+ endpoint: this.otelEndpoint,
2984
+ entity: "stream",
2985
+ name: config.name,
2986
+ action: "ensure"
2987
+ },
2988
+ async () => {
2989
+ this.logger.log(`Ensuring DLQ stream: ${config.name}`);
2990
+ try {
2991
+ const currentInfo = await jsm.streams.info(config.name);
2992
+ return await this.handleExistingStream(jsm, currentInfo, config);
2993
+ } catch (err) {
2994
+ if (err instanceof import_jetstream14.JetStreamApiError && err.apiError().err_code === 10059 /* StreamNotFound */) {
2995
+ this.logger.log(`Creating DLQ stream: ${config.name}`);
2996
+ return await jsm.streams.add(config);
2997
+ }
2998
+ throw err;
2999
+ }
1787
3000
  }
1788
- throw err;
1789
- }
3001
+ );
1790
3002
  }
1791
3003
  async handleExistingStream(jsm, currentInfo, config) {
1792
3004
  const diff = compareStreamConfig(currentInfo.config, config);
@@ -1815,7 +3027,18 @@ var StreamProvider = class {
1815
3027
  }
1816
3028
  return currentInfo;
1817
3029
  }
1818
- await this.migration.migrate(jsm, config.name, config);
3030
+ await withMigrationSpan(
3031
+ this.otel,
3032
+ {
3033
+ serviceName: this.otelServiceName,
3034
+ endpoint: this.otelEndpoint,
3035
+ stream: config.name,
3036
+ reason: diff.changes.filter((c) => c.mutability === "immutable").map((c) => c.property).join(", ")
3037
+ },
3038
+ async () => {
3039
+ await this.migration.migrate(jsm, config.name, config);
3040
+ }
3041
+ );
1819
3042
  return await jsm.streams.info(config.name);
1820
3043
  }
1821
3044
  buildMutableOnlyConfig(config, currentConfig, diff) {
@@ -1931,7 +3154,7 @@ var StreamProvider = class {
1931
3154
  };
1932
3155
 
1933
3156
  // src/server/infrastructure/consumer.provider.ts
1934
- var import_common7 = require("@nestjs/common");
3157
+ var import_common11 = require("@nestjs/common");
1935
3158
  var import_jetstream16 = require("@nats-io/jetstream");
1936
3159
  var ConsumerProvider = class {
1937
3160
  constructor(options, connection, streamProvider, patternRegistry) {
@@ -1939,8 +3162,15 @@ var ConsumerProvider = class {
1939
3162
  this.connection = connection;
1940
3163
  this.streamProvider = streamProvider;
1941
3164
  this.patternRegistry = patternRegistry;
1942
- }
1943
- logger = new import_common7.Logger("Jetstream:Consumer");
3165
+ const derived = deriveOtelAttrs(options);
3166
+ this.otel = derived.otel;
3167
+ this.otelServiceName = derived.serviceName;
3168
+ this.otelEndpoint = derived.serverEndpoint;
3169
+ }
3170
+ logger = new import_common11.Logger("Jetstream:Consumer");
3171
+ otel;
3172
+ otelServiceName;
3173
+ otelEndpoint;
1944
3174
  /**
1945
3175
  * Ensure consumers exist for the specified kinds.
1946
3176
  *
@@ -1970,17 +3200,29 @@ var ConsumerProvider = class {
1970
3200
  const stream = this.streamProvider.getStreamName(kind);
1971
3201
  const config = this.buildConfig(kind);
1972
3202
  const name = config.durable_name;
1973
- this.logger.log(`Ensuring consumer: ${name} on stream: ${stream}`);
1974
- try {
1975
- await jsm.consumers.info(stream, name);
1976
- this.logger.debug(`Consumer exists, updating: ${name}`);
1977
- return await jsm.consumers.update(stream, name, config);
1978
- } catch (err) {
1979
- if (!(err instanceof import_jetstream16.JetStreamApiError) || err.apiError().err_code !== 10014 /* ConsumerNotFound */) {
1980
- throw err;
3203
+ return withProvisioningSpan(
3204
+ this.otel,
3205
+ {
3206
+ serviceName: this.otelServiceName,
3207
+ endpoint: this.otelEndpoint,
3208
+ entity: "consumer",
3209
+ name,
3210
+ action: "ensure"
3211
+ },
3212
+ async () => {
3213
+ this.logger.log(`Ensuring consumer: ${name} on stream: ${stream}`);
3214
+ try {
3215
+ await jsm.consumers.info(stream, name);
3216
+ this.logger.debug(`Consumer exists, updating: ${name}`);
3217
+ return await jsm.consumers.update(stream, name, config);
3218
+ } catch (err) {
3219
+ if (!(err instanceof import_jetstream16.JetStreamApiError) || err.apiError().err_code !== 10014 /* ConsumerNotFound */) {
3220
+ throw err;
3221
+ }
3222
+ return await this.createConsumer(jsm, stream, name, config);
3223
+ }
1981
3224
  }
1982
- return await this.createConsumer(jsm, stream, name, config);
1983
- }
3225
+ );
1984
3226
  }
1985
3227
  /**
1986
3228
  * Recover a consumer that disappeared during runtime.
@@ -1999,16 +3241,28 @@ var ConsumerProvider = class {
1999
3241
  const stream = this.streamProvider.getStreamName(kind);
2000
3242
  const config = this.buildConfig(kind);
2001
3243
  const name = config.durable_name;
2002
- this.logger.log(`Recovering consumer: ${name} on stream: ${stream}`);
2003
- await this.assertNoMigrationInProgress(jsm, stream);
2004
- try {
2005
- return await jsm.consumers.info(stream, name);
2006
- } catch (err) {
2007
- if (!(err instanceof import_jetstream16.JetStreamApiError) || err.apiError().err_code !== 10014 /* ConsumerNotFound */) {
2008
- throw err;
3244
+ return withProvisioningSpan(
3245
+ this.otel,
3246
+ {
3247
+ serviceName: this.otelServiceName,
3248
+ endpoint: this.otelEndpoint,
3249
+ entity: "consumer",
3250
+ name,
3251
+ action: "recover"
3252
+ },
3253
+ async () => {
3254
+ this.logger.log(`Recovering consumer: ${name} on stream: ${stream}`);
3255
+ await this.assertNoMigrationInProgress(jsm, stream);
3256
+ try {
3257
+ return await jsm.consumers.info(stream, name);
3258
+ } catch (err) {
3259
+ if (!(err instanceof import_jetstream16.JetStreamApiError) || err.apiError().err_code !== 10014 /* ConsumerNotFound */) {
3260
+ throw err;
3261
+ }
3262
+ return await this.createConsumer(jsm, stream, name, config);
3263
+ }
2009
3264
  }
2010
- return await this.createConsumer(jsm, stream, name, config);
2011
- }
3265
+ );
2012
3266
  }
2013
3267
  /**
2014
3268
  * Throw if a migration backup stream exists for this stream.
@@ -2124,7 +3378,7 @@ var ConsumerProvider = class {
2124
3378
  };
2125
3379
 
2126
3380
  // src/server/infrastructure/message.provider.ts
2127
- var import_common8 = require("@nestjs/common");
3381
+ var import_common12 = require("@nestjs/common");
2128
3382
  var import_jetstream18 = require("@nats-io/jetstream");
2129
3383
  var import_rxjs3 = require("rxjs");
2130
3384
  var MessageProvider = class {
@@ -2134,7 +3388,7 @@ var MessageProvider = class {
2134
3388
  this.consumeOptionsMap = consumeOptionsMap;
2135
3389
  this.consumerRecoveryFn = consumerRecoveryFn;
2136
3390
  }
2137
- logger = new import_common8.Logger("Jetstream:Message");
3391
+ logger = new import_common12.Logger("Jetstream:Message");
2138
3392
  activeIterators = /* @__PURE__ */ new Set();
2139
3393
  orderedReadyResolve = null;
2140
3394
  orderedReadyReject = null;
@@ -2254,13 +3508,17 @@ var MessageProvider = class {
2254
3508
  }
2255
3509
  const defaults = { idle_heartbeat: 5e3 };
2256
3510
  const userOptions = this.consumeOptionsMap.get(kind) ?? {};
2257
- const messages = await consumer.consume({ ...defaults, ...userOptions });
3511
+ const messages = await consumer.consume({
3512
+ ...defaults,
3513
+ ...userOptions,
3514
+ callback: (msg) => {
3515
+ target$.next(msg);
3516
+ }
3517
+ });
2258
3518
  this.activeIterators.add(messages);
2259
3519
  this.monitorConsumerHealth(messages, consumerName2);
2260
3520
  try {
2261
- for await (const msg of messages) {
2262
- target$.next(msg);
2263
- }
3521
+ await messages.closed();
2264
3522
  } finally {
2265
3523
  this.activeIterators.delete(messages);
2266
3524
  }
@@ -2352,11 +3610,16 @@ var MessageProvider = class {
2352
3610
  })
2353
3611
  );
2354
3612
  }
2355
- /** Single iteration: create ordered consumer -> iterate messages. */
3613
+ /** Single iteration: create ordered consumer -> push messages into the subject. */
2356
3614
  async consumeOrderedOnce(streamName2, consumerOpts) {
2357
3615
  const js = this.connection.getJetStreamClient();
2358
3616
  const consumer = await js.consumers.get(streamName2, consumerOpts);
2359
- const messages = await consumer.consume();
3617
+ const orderedMessages$ = this.orderedMessages$;
3618
+ const messages = await consumer.consume({
3619
+ callback: (msg) => {
3620
+ orderedMessages$.next(msg);
3621
+ }
3622
+ });
2360
3623
  if (this.orderedReadyResolve) {
2361
3624
  this.orderedReadyResolve();
2362
3625
  this.orderedReadyResolve = null;
@@ -2364,9 +3627,7 @@ var MessageProvider = class {
2364
3627
  }
2365
3628
  this.activeIterators.add(messages);
2366
3629
  try {
2367
- for await (const msg of messages) {
2368
- this.orderedMessages$.next(msg);
2369
- }
3630
+ await messages.closed();
2370
3631
  } finally {
2371
3632
  this.activeIterators.delete(messages);
2372
3633
  }
@@ -2374,7 +3635,7 @@ var MessageProvider = class {
2374
3635
  };
2375
3636
 
2376
3637
  // src/server/infrastructure/metadata.provider.ts
2377
- var import_common9 = require("@nestjs/common");
3638
+ var import_common13 = require("@nestjs/common");
2378
3639
  var import_kv = require("@nats-io/kv");
2379
3640
  var MetadataProvider = class {
2380
3641
  constructor(options, connection) {
@@ -2383,7 +3644,7 @@ var MetadataProvider = class {
2383
3644
  this.replicas = options.metadata?.replicas ?? DEFAULT_METADATA_REPLICAS;
2384
3645
  this.ttl = Math.max(options.metadata?.ttl ?? DEFAULT_METADATA_TTL, MIN_METADATA_TTL);
2385
3646
  }
2386
- logger = new import_common9.Logger("Jetstream:Metadata");
3647
+ logger = new import_common13.Logger("Jetstream:Metadata");
2387
3648
  bucketName;
2388
3649
  replicas;
2389
3650
  ttl;
@@ -2476,7 +3737,7 @@ var MetadataProvider = class {
2476
3737
  };
2477
3738
 
2478
3739
  // src/server/routing/pattern-registry.ts
2479
- var import_common10 = require("@nestjs/common");
3740
+ var import_common14 = require("@nestjs/common");
2480
3741
  var HANDLER_LABELS = {
2481
3742
  ["broadcast" /* Broadcast */]: "broadcast" /* Broadcast */,
2482
3743
  ["ordered" /* Ordered */]: "ordered" /* Ordered */,
@@ -2487,7 +3748,7 @@ var PatternRegistry = class {
2487
3748
  constructor(options) {
2488
3749
  this.options = options;
2489
3750
  }
2490
- logger = new import_common10.Logger("Jetstream:PatternRegistry");
3751
+ logger = new import_common14.Logger("Jetstream:PatternRegistry");
2491
3752
  registry = /* @__PURE__ */ new Map();
2492
3753
  // Cached after registerHandlers() — the registry is immutable from that point
2493
3754
  cachedPatterns = null;
@@ -2644,9 +3905,13 @@ var PatternRegistry = class {
2644
3905
  };
2645
3906
 
2646
3907
  // src/server/routing/event.router.ts
2647
- var import_common11 = require("@nestjs/common");
2648
- var import_rxjs4 = require("rxjs");
3908
+ var import_common15 = require("@nestjs/common");
2649
3909
  var import_transport_node4 = require("@nats-io/transport-node");
3910
+ var eventConsumeKindFor = (kind) => {
3911
+ if (kind === "broadcast" /* Broadcast */) return "broadcast" /* Broadcast */;
3912
+ if (kind === "ordered" /* Ordered */) return "ordered" /* Ordered */;
3913
+ return "event" /* Event */;
3914
+ };
2650
3915
  var EventRouter = class {
2651
3916
  constructor(messageProvider, patternRegistry, codec, eventBus, deadLetterConfig, processingConfig, ackWaitMap, connection, options) {
2652
3917
  this.messageProvider = messageProvider;
@@ -2658,9 +3923,22 @@ var EventRouter = class {
2658
3923
  this.ackWaitMap = ackWaitMap;
2659
3924
  this.connection = connection;
2660
3925
  this.options = options;
3926
+ if (options) {
3927
+ const derived = deriveOtelAttrs(options);
3928
+ this.otel = derived.otel;
3929
+ this.serviceName = derived.serviceName;
3930
+ this.serverEndpoint = derived.serverEndpoint;
3931
+ } else {
3932
+ this.otel = resolveOtelOptions({ enabled: false });
3933
+ this.serviceName = "";
3934
+ this.serverEndpoint = null;
3935
+ }
2661
3936
  }
2662
- logger = new import_common11.Logger("Jetstream:EventRouter");
3937
+ logger = new import_common15.Logger("Jetstream:EventRouter");
2663
3938
  subscriptions = [];
3939
+ otel;
3940
+ serviceName;
3941
+ serverEndpoint;
2664
3942
  /**
2665
3943
  * Update the max_deliver thresholds from actual NATS consumer configs.
2666
3944
  * Called after consumers are ensured so the DLQ map reflects reality.
@@ -2684,15 +3962,233 @@ var EventRouter = class {
2684
3962
  }
2685
3963
  this.subscriptions.length = 0;
2686
3964
  }
2687
- /** Subscribe to a message stream and route each message. */
3965
+ /** Subscribe to a message stream and route each message to its handler. */
2688
3966
  subscribeToStream(stream$, kind) {
2689
3967
  const isOrdered = kind === "ordered" /* Ordered */;
3968
+ const patternRegistry = this.patternRegistry;
3969
+ const codec = this.codec;
3970
+ const eventBus = this.eventBus;
3971
+ const logger5 = this.logger;
3972
+ const deadLetterConfig = this.deadLetterConfig;
3973
+ const otel = this.otel;
3974
+ const serviceName = this.serviceName;
3975
+ const serverEndpoint = this.serverEndpoint;
3976
+ const spanKind = eventConsumeKindFor(kind);
2690
3977
  const ackExtensionInterval = isOrdered ? null : resolveAckExtensionInterval(this.getAckExtensionConfig(kind), this.ackWaitMap?.get(kind));
3978
+ const hasAckExtension = ackExtensionInterval !== null && ackExtensionInterval > 0;
2691
3979
  const concurrency = this.getConcurrency(kind);
2692
- const route = (msg) => (0, import_rxjs4.from)(
2693
- isOrdered ? this.handleOrderedSafe(msg) : this.handleSafe(msg, ackExtensionInterval, kind)
2694
- );
2695
- const subscription = stream$.pipe(isOrdered ? (0, import_rxjs4.concatMap)(route) : (0, import_rxjs4.mergeMap)(route, concurrency)).subscribe();
3980
+ const hasDlqCheck = deadLetterConfig !== void 0;
3981
+ const emitRouted = eventBus.hasHook("messageRouted" /* MessageRouted */);
3982
+ const isDeadLetter = (msg) => {
3983
+ if (!hasDlqCheck) return false;
3984
+ const maxDeliver = deadLetterConfig.maxDeliverByStream?.get(msg.info.stream);
3985
+ if (maxDeliver === void 0 || maxDeliver <= 0) return false;
3986
+ return msg.info.deliveryCount >= maxDeliver;
3987
+ };
3988
+ const handleDeadLetter = hasDlqCheck ? (msg, data, err) => this.handleDeadLetter(msg, data, err) : null;
3989
+ const settleSuccess = (msg, ctx) => {
3990
+ if (ctx.shouldTerminate) msg.term(ctx.terminateReason);
3991
+ else if (ctx.shouldRetry) msg.nak(ctx.retryDelay);
3992
+ else msg.ack();
3993
+ };
3994
+ const settleFailure = async (msg, data, err) => {
3995
+ if (handleDeadLetter !== null && isDeadLetter(msg)) {
3996
+ await handleDeadLetter(msg, data, err);
3997
+ return;
3998
+ }
3999
+ msg.nak();
4000
+ };
4001
+ const resolveEvent = (msg) => {
4002
+ const subject = msg.subject;
4003
+ try {
4004
+ const handler = patternRegistry.getHandler(subject);
4005
+ if (!handler) {
4006
+ msg.term(`No handler for event: ${subject}`);
4007
+ logger5.error(`No handler for subject: ${subject}`);
4008
+ return null;
4009
+ }
4010
+ let data;
4011
+ try {
4012
+ data = codec.decode(msg.data);
4013
+ } catch (err) {
4014
+ msg.term("Decode error");
4015
+ logger5.error(`Decode error for ${subject}:`, err);
4016
+ return null;
4017
+ }
4018
+ if (emitRouted) eventBus.emitMessageRouted(subject, "event" /* Event */);
4019
+ return { handler, data };
4020
+ } catch (err) {
4021
+ logger5.error(`Unexpected error in ${kind} event router`, err);
4022
+ try {
4023
+ msg.term("Unexpected router error");
4024
+ } catch (termErr) {
4025
+ logger5.error(`Failed to terminate message ${subject}:`, termErr);
4026
+ }
4027
+ return null;
4028
+ }
4029
+ };
4030
+ const handleSafe = (msg) => {
4031
+ const resolved = resolveEvent(msg);
4032
+ if (resolved === null) return void 0;
4033
+ const { handler, data } = resolved;
4034
+ const ctx = new RpcContext([msg]);
4035
+ const stopAckExtension = hasAckExtension ? startAckExtensionTimer(msg, ackExtensionInterval) : null;
4036
+ let pending;
4037
+ try {
4038
+ pending = withConsumeSpan(
4039
+ {
4040
+ subject: msg.subject,
4041
+ msg,
4042
+ info: msg.info,
4043
+ kind: spanKind,
4044
+ payloadBytes: msg.data.length,
4045
+ handlerMetadata: { pattern: msg.subject },
4046
+ serviceName,
4047
+ endpoint: serverEndpoint
4048
+ },
4049
+ otel,
4050
+ () => unwrapResult(handler(data, ctx))
4051
+ );
4052
+ } catch (err) {
4053
+ eventBus.emit(
4054
+ "error" /* Error */,
4055
+ err instanceof Error ? err : new Error(String(err)),
4056
+ `${kind}-handler:${msg.subject}`
4057
+ );
4058
+ return settleFailure(msg, data, err).finally(() => {
4059
+ if (stopAckExtension !== null) stopAckExtension();
4060
+ });
4061
+ }
4062
+ if (!isPromiseLike2(pending)) {
4063
+ settleSuccess(msg, ctx);
4064
+ if (stopAckExtension !== null) stopAckExtension();
4065
+ return void 0;
4066
+ }
4067
+ return pending.then(
4068
+ () => {
4069
+ settleSuccess(msg, ctx);
4070
+ if (stopAckExtension !== null) stopAckExtension();
4071
+ },
4072
+ async (err) => {
4073
+ eventBus.emit(
4074
+ "error" /* Error */,
4075
+ err instanceof Error ? err : new Error(String(err)),
4076
+ `${kind}-handler:${msg.subject}`
4077
+ );
4078
+ try {
4079
+ await settleFailure(msg, data, err);
4080
+ } finally {
4081
+ if (stopAckExtension !== null) stopAckExtension();
4082
+ }
4083
+ }
4084
+ );
4085
+ };
4086
+ const handleOrderedSafe = (msg) => {
4087
+ const subject = msg.subject;
4088
+ let handler;
4089
+ let data;
4090
+ try {
4091
+ handler = patternRegistry.getHandler(subject);
4092
+ if (!handler) {
4093
+ logger5.error(`No handler for subject: ${subject}`);
4094
+ return void 0;
4095
+ }
4096
+ try {
4097
+ data = codec.decode(msg.data);
4098
+ } catch (err) {
4099
+ logger5.error(`Decode error for ${subject}:`, err);
4100
+ return void 0;
4101
+ }
4102
+ if (emitRouted) eventBus.emitMessageRouted(subject, "event" /* Event */);
4103
+ } catch (err) {
4104
+ logger5.error(`Ordered handler error (${subject}):`, err);
4105
+ return void 0;
4106
+ }
4107
+ const ctx = new RpcContext([msg]);
4108
+ const warnIfSettlementAttempted = () => {
4109
+ if (ctx.shouldRetry || ctx.shouldTerminate) {
4110
+ logger5.warn(
4111
+ `retry()/terminate() ignored for ordered message ${subject} \u2014 ordered consumers auto-acknowledge`
4112
+ );
4113
+ }
4114
+ };
4115
+ let pending;
4116
+ try {
4117
+ pending = withConsumeSpan(
4118
+ {
4119
+ subject: msg.subject,
4120
+ msg,
4121
+ info: msg.info,
4122
+ kind: spanKind,
4123
+ payloadBytes: msg.data.length,
4124
+ handlerMetadata: { pattern: msg.subject },
4125
+ serviceName,
4126
+ endpoint: serverEndpoint
4127
+ },
4128
+ otel,
4129
+ () => unwrapResult(handler(data, ctx))
4130
+ );
4131
+ } catch (err) {
4132
+ logger5.error(`Ordered handler error (${subject}):`, err);
4133
+ return void 0;
4134
+ }
4135
+ if (!isPromiseLike2(pending)) {
4136
+ warnIfSettlementAttempted();
4137
+ return void 0;
4138
+ }
4139
+ return pending.then(warnIfSettlementAttempted, (err) => {
4140
+ logger5.error(`Ordered handler error (${subject}):`, err);
4141
+ });
4142
+ };
4143
+ const route = isOrdered ? handleOrderedSafe : handleSafe;
4144
+ const maxActive = isOrdered ? 1 : concurrency ?? Number.POSITIVE_INFINITY;
4145
+ const backlogWarnThreshold = 1e3;
4146
+ let active = 0;
4147
+ let backlogWarned = false;
4148
+ const backlog = [];
4149
+ const onAsyncDone = () => {
4150
+ active--;
4151
+ drainBacklog();
4152
+ };
4153
+ const drainBacklog = () => {
4154
+ while (active < maxActive) {
4155
+ const next = backlog.shift();
4156
+ if (next === void 0) return;
4157
+ active++;
4158
+ const result = route(next);
4159
+ if (result !== void 0) {
4160
+ void result.finally(onAsyncDone);
4161
+ } else {
4162
+ active--;
4163
+ }
4164
+ }
4165
+ if (backlog.length < backlogWarnThreshold) backlogWarned = false;
4166
+ };
4167
+ const subscription = stream$.subscribe({
4168
+ next: (msg) => {
4169
+ if (active >= maxActive) {
4170
+ backlog.push(msg);
4171
+ if (!backlogWarned && backlog.length >= backlogWarnThreshold) {
4172
+ backlogWarned = true;
4173
+ logger5.warn(
4174
+ `${kind} backlog reached ${backlog.length} messages \u2014 consumer may be falling behind`
4175
+ );
4176
+ }
4177
+ return;
4178
+ }
4179
+ active++;
4180
+ const result = route(msg);
4181
+ if (result !== void 0) {
4182
+ void result.finally(onAsyncDone);
4183
+ } else {
4184
+ active--;
4185
+ if (backlog.length > 0) drainBacklog();
4186
+ }
4187
+ },
4188
+ error: (err) => {
4189
+ logger5.error(`Stream error in ${kind} router`, err);
4190
+ }
4191
+ });
2696
4192
  this.subscriptions.push(subscription);
2697
4193
  }
2698
4194
  getConcurrency(kind) {
@@ -2705,94 +4201,11 @@ var EventRouter = class {
2705
4201
  if (kind === "broadcast" /* Broadcast */) return this.processingConfig?.broadcast?.ackExtension;
2706
4202
  return void 0;
2707
4203
  }
2708
- /** Handle a single event message with error isolation. */
2709
- async handleSafe(msg, ackExtensionInterval, kind) {
2710
- try {
2711
- const resolved = this.decodeMessage(msg);
2712
- if (!resolved) return;
2713
- await this.executeHandler(
2714
- resolved.handler,
2715
- resolved.data,
2716
- resolved.ctx,
2717
- msg,
2718
- ackExtensionInterval
2719
- );
2720
- } catch (err) {
2721
- this.logger.error(`Unexpected error in ${kind} event router`, err);
2722
- }
2723
- }
2724
- /** Handle an ordered message with error isolation. */
2725
- async handleOrderedSafe(msg) {
2726
- try {
2727
- const resolved = this.decodeMessage(msg, true);
2728
- if (!resolved) return;
2729
- await unwrapResult(resolved.handler(resolved.data, resolved.ctx));
2730
- if (resolved.ctx.shouldRetry || resolved.ctx.shouldTerminate) {
2731
- this.logger.warn(
2732
- `retry()/terminate() ignored for ordered message ${msg.subject} \u2014 ordered consumers auto-acknowledge`
2733
- );
2734
- }
2735
- } catch (err) {
2736
- this.logger.error(`Ordered handler error (${msg.subject}):`, err);
2737
- }
2738
- }
2739
- /** Resolve handler, decode payload, and build context. Returns null on failure. */
2740
- decodeMessage(msg, isOrdered = false) {
2741
- const handler = this.patternRegistry.getHandler(msg.subject);
2742
- if (!handler) {
2743
- if (!isOrdered) msg.term(`No handler for event: ${msg.subject}`);
2744
- this.logger.error(`No handler for subject: ${msg.subject}`);
2745
- return null;
2746
- }
2747
- let data;
2748
- try {
2749
- data = this.codec.decode(msg.data);
2750
- } catch (err) {
2751
- if (!isOrdered) msg.term("Decode error");
2752
- this.logger.error(`Decode error for ${msg.subject}:`, err);
2753
- return null;
2754
- }
2755
- this.eventBus.emitMessageRouted(msg.subject, "event" /* Event */);
2756
- return { handler, data, ctx: new RpcContext([msg]) };
2757
- }
2758
- /** Execute handler, then ack on success or nak/dead-letter on failure. */
2759
- async executeHandler(handler, data, ctx, msg, ackExtensionInterval) {
2760
- const stopAckExtension = startAckExtensionTimer(msg, ackExtensionInterval);
2761
- try {
2762
- await unwrapResult(handler(data, ctx));
2763
- if (ctx.shouldTerminate) {
2764
- msg.term(ctx.terminateReason);
2765
- } else if (ctx.shouldRetry) {
2766
- msg.nak(ctx.retryDelay);
2767
- } else {
2768
- msg.ack();
2769
- }
2770
- } catch (err) {
2771
- this.logger.error(`Event handler error (${msg.subject}):`, err);
2772
- if (this.isDeadLetter(msg)) {
2773
- await this.handleDeadLetter(msg, data, err);
2774
- } else {
2775
- msg.nak();
2776
- }
2777
- } finally {
2778
- stopAckExtension?.();
2779
- }
2780
- }
2781
- /** Check if the message has exhausted all delivery attempts. */
2782
- isDeadLetter(msg) {
2783
- if (!this.deadLetterConfig) return false;
2784
- const maxDeliver = this.deadLetterConfig.maxDeliverByStream.get(msg.info.stream);
2785
- if (maxDeliver === void 0 || maxDeliver <= 0) return false;
2786
- return msg.info.deliveryCount >= maxDeliver;
2787
- }
2788
- /** Handle a dead letter: invoke callback, then term or nak based on result. */
2789
4204
  /**
2790
- * Fallback execution for a dead letter when DLQ is disabled, or when
2791
- * publishing to the DLQ stream fails (due to network or NATS errors).
2792
- *
2793
- * Triggers the user-provided `onDeadLetter` hook for logging/alerting.
2794
- * On success, terminates the message. On error, leaves it unacknowledged (nak)
2795
- * so NATS can retry the delivery on the next cycle.
4205
+ * Last-resort path for a dead letter: invoke `onDeadLetter`, then `term` on
4206
+ * success or `nak` on hook failure so NATS retries on the next delivery
4207
+ * cycle. Used when DLQ stream isn't configured, or when publishing to it
4208
+ * failed and we still have to surface the message somewhere observable.
2796
4209
  */
2797
4210
  async fallbackToOnDeadLetterCallback(info, msg) {
2798
4211
  if (!this.deadLetterConfig) {
@@ -2884,21 +4297,37 @@ var EventRouter = class {
2884
4297
  streamSequence: msg.info.streamSequence,
2885
4298
  timestamp: new Date(msg.info.timestampNanos / 1e6).toISOString()
2886
4299
  };
2887
- this.eventBus.emit("deadLetter" /* DeadLetter */, info);
2888
- if (!this.options?.dlq) {
2889
- await this.fallbackToOnDeadLetterCallback(info, msg);
2890
- } else {
2891
- await this.publishToDlq(msg, info, error);
2892
- }
4300
+ await withDeadLetterSpan(
4301
+ {
4302
+ msg,
4303
+ // Pattern resolution mirrors event-routing: when a registered
4304
+ // pattern matches, surface it on the DLQ span so APM can filter
4305
+ // dead letters by handler without parsing the subject. Falls back
4306
+ // to the subject itself when no glob handler is in play.
4307
+ pattern: this.patternRegistry.getHandler(msg.subject) ? msg.subject : void 0,
4308
+ finalDeliveryCount: msg.info.deliveryCount,
4309
+ reason: error instanceof Error ? error.message : String(error),
4310
+ serviceName: this.serviceName,
4311
+ endpoint: this.serverEndpoint
4312
+ },
4313
+ this.otel,
4314
+ async () => {
4315
+ this.eventBus.emit("deadLetter" /* DeadLetter */, info);
4316
+ if (!this.options?.dlq) {
4317
+ await this.fallbackToOnDeadLetterCallback(info, msg);
4318
+ } else {
4319
+ await this.publishToDlq(msg, info, error);
4320
+ }
4321
+ }
4322
+ );
2893
4323
  }
2894
4324
  };
2895
4325
 
2896
4326
  // src/server/routing/rpc.router.ts
2897
- var import_common12 = require("@nestjs/common");
4327
+ var import_common16 = require("@nestjs/common");
2898
4328
  var import_transport_node5 = require("@nats-io/transport-node");
2899
- var import_rxjs5 = require("rxjs");
2900
4329
  var RpcRouter = class {
2901
- constructor(messageProvider, patternRegistry, connection, codec, eventBus, rpcOptions, ackWaitMap) {
4330
+ constructor(messageProvider, patternRegistry, connection, codec, eventBus, rpcOptions, ackWaitMap, options) {
2902
4331
  this.messageProvider = messageProvider;
2903
4332
  this.patternRegistry = patternRegistry;
2904
4333
  this.connection = connection;
@@ -2908,13 +4337,26 @@ var RpcRouter = class {
2908
4337
  this.ackWaitMap = ackWaitMap;
2909
4338
  this.timeout = rpcOptions?.timeout ?? DEFAULT_JETSTREAM_RPC_TIMEOUT;
2910
4339
  this.concurrency = rpcOptions?.concurrency;
4340
+ if (options) {
4341
+ const derived = deriveOtelAttrs(options);
4342
+ this.otel = derived.otel;
4343
+ this.serviceName = derived.serviceName;
4344
+ this.serverEndpoint = derived.serverEndpoint;
4345
+ } else {
4346
+ this.otel = resolveOtelOptions({ enabled: false });
4347
+ this.serviceName = "";
4348
+ this.serverEndpoint = null;
4349
+ }
2911
4350
  }
2912
- logger = new import_common12.Logger("Jetstream:RpcRouter");
4351
+ logger = new import_common16.Logger("Jetstream:RpcRouter");
2913
4352
  timeout;
2914
4353
  concurrency;
2915
4354
  resolvedAckExtensionInterval;
2916
4355
  subscription = null;
2917
4356
  cachedNc = null;
4357
+ otel;
4358
+ serviceName;
4359
+ serverEndpoint;
2918
4360
  /** Lazily resolve the ack extension interval (needs ackWaitMap populated at runtime). */
2919
4361
  get ackExtensionInterval() {
2920
4362
  if (this.resolvedAckExtensionInterval !== void 0) return this.resolvedAckExtensionInterval;
@@ -2927,99 +4369,213 @@ var RpcRouter = class {
2927
4369
  /** Start routing command messages to handlers. */
2928
4370
  async start() {
2929
4371
  this.cachedNc = await this.connection.getConnection();
2930
- this.subscription = this.messageProvider.commands$.pipe((0, import_rxjs5.mergeMap)((msg) => (0, import_rxjs5.from)(this.handleSafe(msg)), this.concurrency)).subscribe();
2931
- }
2932
- /** Stop routing and unsubscribe. */
2933
- destroy() {
2934
- this.subscription?.unsubscribe();
2935
- this.subscription = null;
2936
- }
2937
- /** Handle a single RPC command message with error isolation. */
2938
- async handleSafe(msg) {
2939
- try {
2940
- const handler = this.patternRegistry.getHandler(msg.subject);
2941
- if (!handler) {
2942
- msg.term(`No handler for RPC: ${msg.subject}`);
2943
- this.logger.error(`No handler for RPC subject: ${msg.subject}`);
2944
- return;
2945
- }
2946
- const { headers: msgHeaders } = msg;
2947
- const replyTo = msgHeaders?.get("x-reply-to" /* ReplyTo */);
2948
- const correlationId = msgHeaders?.get("x-correlation-id" /* CorrelationId */);
2949
- if (!replyTo || !correlationId) {
2950
- msg.term("Missing required headers (reply-to or correlation-id)");
2951
- this.logger.error(`Missing headers for RPC: ${msg.subject}`);
2952
- return;
2953
- }
2954
- let data;
2955
- try {
2956
- data = this.codec.decode(msg.data);
2957
- } catch (err) {
2958
- msg.term("Decode error");
2959
- this.logger.error(`Decode error for RPC ${msg.subject}:`, err);
2960
- return;
2961
- }
2962
- this.eventBus.emitMessageRouted(msg.subject, "rpc" /* Rpc */);
2963
- await this.executeHandler(handler, data, msg, replyTo, correlationId);
2964
- } catch (err) {
2965
- this.logger.error("Unexpected error in RPC router", err);
2966
- }
2967
- }
2968
- /** Execute handler, publish response, settle message. */
2969
- async executeHandler(handler, data, msg, replyTo, correlationId) {
2970
- const nc = this.cachedNc ?? await this.connection.getConnection();
2971
- const ctx = new RpcContext([msg]);
2972
- let settled = false;
2973
- const stopAckExtension = startAckExtensionTimer(msg, this.ackExtensionInterval);
2974
- const timeoutId = setTimeout(() => {
2975
- if (settled) return;
2976
- settled = true;
2977
- stopAckExtension?.();
2978
- this.logger.error(`RPC timeout (${this.timeout}ms): ${msg.subject}`);
2979
- this.eventBus.emit("rpcTimeout" /* RpcTimeout */, msg.subject, correlationId);
2980
- msg.term("Handler timeout");
2981
- }, this.timeout);
2982
- try {
2983
- const result = await unwrapResult(handler(data, ctx));
2984
- if (settled) return;
2985
- settled = true;
2986
- clearTimeout(timeoutId);
2987
- stopAckExtension?.();
2988
- msg.ack();
4372
+ const nc = this.cachedNc;
4373
+ const patternRegistry = this.patternRegistry;
4374
+ const codec = this.codec;
4375
+ const eventBus = this.eventBus;
4376
+ const logger5 = this.logger;
4377
+ const timeout = this.timeout;
4378
+ const ackExtensionInterval = this.ackExtensionInterval;
4379
+ const hasAckExtension = ackExtensionInterval !== null && ackExtensionInterval > 0;
4380
+ const maxActive = this.concurrency ?? Number.POSITIVE_INFINITY;
4381
+ const otel = this.otel;
4382
+ const serviceName = this.serviceName;
4383
+ const serverEndpoint = this.serverEndpoint;
4384
+ const emitRpcTimeout = (subject, correlationId) => {
4385
+ eventBus.emit("rpcTimeout" /* RpcTimeout */, subject, correlationId);
4386
+ };
4387
+ const publishReply = (replyTo, correlationId, payload) => {
2989
4388
  try {
2990
4389
  const hdrs = (0, import_transport_node5.headers)();
2991
4390
  hdrs.set("x-correlation-id" /* CorrelationId */, correlationId);
2992
- nc.publish(replyTo, this.codec.encode(result), { headers: hdrs });
4391
+ nc.publish(replyTo, codec.encode(payload), { headers: hdrs });
2993
4392
  } catch (publishErr) {
2994
- this.logger.error(`Failed to publish RPC response for ${msg.subject}`, publishErr);
4393
+ logger5.error(`Failed to publish RPC response`, publishErr);
2995
4394
  }
2996
- } catch (err) {
2997
- if (settled) return;
2998
- settled = true;
2999
- clearTimeout(timeoutId);
3000
- stopAckExtension?.();
4395
+ };
4396
+ const publishErrorReply = (replyTo, correlationId, subject, err) => {
3001
4397
  try {
3002
4398
  const hdrs = (0, import_transport_node5.headers)();
3003
4399
  hdrs.set("x-correlation-id" /* CorrelationId */, correlationId);
3004
4400
  hdrs.set("x-error" /* Error */, "true");
3005
- nc.publish(replyTo, this.codec.encode(serializeError(err)), { headers: hdrs });
4401
+ nc.publish(replyTo, codec.encode(serializeError(err)), { headers: hdrs });
3006
4402
  } catch (encodeErr) {
3007
- this.logger.error(`Failed to encode RPC error for ${msg.subject}`, encodeErr);
4403
+ logger5.error(`Failed to encode RPC error for ${subject}`, encodeErr);
3008
4404
  }
3009
- msg.term(`Handler error: ${msg.subject}`);
3010
- }
4405
+ };
4406
+ const resolveCommand = (msg) => {
4407
+ const subject = msg.subject;
4408
+ try {
4409
+ const handler = patternRegistry.getHandler(subject);
4410
+ if (!handler) {
4411
+ msg.term(`No handler for RPC: ${subject}`);
4412
+ logger5.error(`No handler for RPC subject: ${subject}`);
4413
+ return null;
4414
+ }
4415
+ const msgHeaders = msg.headers;
4416
+ const replyTo = msgHeaders?.get("x-reply-to" /* ReplyTo */);
4417
+ const correlationId = msgHeaders?.get("x-correlation-id" /* CorrelationId */);
4418
+ if (!replyTo || !correlationId) {
4419
+ msg.term("Missing required headers (reply-to or correlation-id)");
4420
+ logger5.error(`Missing headers for RPC: ${subject}`);
4421
+ return null;
4422
+ }
4423
+ let data;
4424
+ try {
4425
+ data = codec.decode(msg.data);
4426
+ } catch (err) {
4427
+ msg.term("Decode error");
4428
+ logger5.error(`Decode error for RPC ${subject}:`, err);
4429
+ return null;
4430
+ }
4431
+ eventBus.emitMessageRouted(subject, "rpc" /* Rpc */);
4432
+ return { handler, data, replyTo, correlationId };
4433
+ } catch (err) {
4434
+ logger5.error("Unexpected error in RPC router", err);
4435
+ try {
4436
+ msg.term("Unexpected router error");
4437
+ } catch (termErr) {
4438
+ logger5.error(`Failed to terminate RPC message ${subject}:`, termErr);
4439
+ }
4440
+ return null;
4441
+ }
4442
+ };
4443
+ const handleSafe = (msg) => {
4444
+ const resolved = resolveCommand(msg);
4445
+ if (resolved === null) return void 0;
4446
+ const { handler, data, replyTo, correlationId } = resolved;
4447
+ const subject = msg.subject;
4448
+ const ctx = new RpcContext([msg]);
4449
+ const stopAckExtension = hasAckExtension ? startAckExtensionTimer(msg, ackExtensionInterval) : null;
4450
+ const reportHandlerError = (err) => {
4451
+ eventBus.emit(
4452
+ "error" /* Error */,
4453
+ err instanceof Error ? err : new Error(String(err)),
4454
+ `rpc-handler:${subject}`
4455
+ );
4456
+ publishErrorReply(replyTo, correlationId, subject, err);
4457
+ msg.term(`Handler error: ${subject}`);
4458
+ };
4459
+ const abortController = new AbortController();
4460
+ let pending;
4461
+ try {
4462
+ pending = withConsumeSpan(
4463
+ {
4464
+ subject,
4465
+ msg,
4466
+ info: msg.info,
4467
+ kind: "rpc" /* Rpc */,
4468
+ payloadBytes: msg.data.length,
4469
+ handlerMetadata: { pattern: subject },
4470
+ serviceName,
4471
+ endpoint: serverEndpoint
4472
+ },
4473
+ otel,
4474
+ () => unwrapResult(handler(data, ctx)),
4475
+ { signal: abortController.signal, timeoutLabel: "rpc.handler.timeout" }
4476
+ );
4477
+ } catch (err) {
4478
+ if (stopAckExtension !== null) stopAckExtension();
4479
+ reportHandlerError(err);
4480
+ return void 0;
4481
+ }
4482
+ if (!isPromiseLike2(pending)) {
4483
+ if (stopAckExtension !== null) stopAckExtension();
4484
+ msg.ack();
4485
+ publishReply(replyTo, correlationId, pending);
4486
+ return void 0;
4487
+ }
4488
+ let settled = false;
4489
+ const timeoutId = setTimeout(() => {
4490
+ if (settled) return;
4491
+ settled = true;
4492
+ if (stopAckExtension !== null) stopAckExtension();
4493
+ abortController.abort();
4494
+ emitRpcTimeout(subject, correlationId);
4495
+ msg.term("Handler timeout");
4496
+ }, timeout);
4497
+ return pending.then(
4498
+ (result) => {
4499
+ if (settled) return;
4500
+ settled = true;
4501
+ clearTimeout(timeoutId);
4502
+ if (stopAckExtension !== null) stopAckExtension();
4503
+ msg.ack();
4504
+ publishReply(replyTo, correlationId, result);
4505
+ },
4506
+ (err) => {
4507
+ if (settled) return;
4508
+ settled = true;
4509
+ clearTimeout(timeoutId);
4510
+ if (stopAckExtension !== null) stopAckExtension();
4511
+ reportHandlerError(err);
4512
+ }
4513
+ );
4514
+ };
4515
+ const backlogWarnThreshold = 1e3;
4516
+ let active = 0;
4517
+ let backlogWarned = false;
4518
+ const backlog = [];
4519
+ const onAsyncDone = () => {
4520
+ active--;
4521
+ drainBacklog();
4522
+ };
4523
+ const drainBacklog = () => {
4524
+ while (active < maxActive) {
4525
+ const next = backlog.shift();
4526
+ if (next === void 0) return;
4527
+ active++;
4528
+ const result = handleSafe(next);
4529
+ if (result !== void 0) {
4530
+ void result.finally(onAsyncDone);
4531
+ } else {
4532
+ active--;
4533
+ }
4534
+ }
4535
+ if (backlog.length < backlogWarnThreshold) backlogWarned = false;
4536
+ };
4537
+ this.subscription = this.messageProvider.commands$.subscribe({
4538
+ next: (msg) => {
4539
+ if (active >= maxActive) {
4540
+ backlog.push(msg);
4541
+ if (!backlogWarned && backlog.length >= backlogWarnThreshold) {
4542
+ backlogWarned = true;
4543
+ logger5.warn(
4544
+ `RPC backlog reached ${backlog.length} messages \u2014 consumer may be falling behind`
4545
+ );
4546
+ }
4547
+ return;
4548
+ }
4549
+ active++;
4550
+ const result = handleSafe(msg);
4551
+ if (result !== void 0) {
4552
+ void result.finally(onAsyncDone);
4553
+ } else {
4554
+ active--;
4555
+ if (backlog.length > 0) drainBacklog();
4556
+ }
4557
+ },
4558
+ error: (err) => {
4559
+ logger5.error("Stream error in RPC router", err);
4560
+ }
4561
+ });
4562
+ }
4563
+ /** Stop routing and unsubscribe. */
4564
+ destroy() {
4565
+ this.subscription?.unsubscribe();
4566
+ this.subscription = null;
3011
4567
  }
3012
4568
  };
3013
4569
 
3014
4570
  // src/shutdown/shutdown.manager.ts
3015
- var import_common13 = require("@nestjs/common");
4571
+ var import_common17 = require("@nestjs/common");
3016
4572
  var ShutdownManager = class {
3017
4573
  constructor(connection, eventBus, timeout) {
3018
4574
  this.connection = connection;
3019
4575
  this.eventBus = eventBus;
3020
4576
  this.timeout = timeout;
3021
4577
  }
3022
- logger = new import_common13.Logger("Jetstream:Shutdown");
4578
+ logger = new import_common17.Logger("Jetstream:Shutdown");
3023
4579
  shutdownPromise;
3024
4580
  /**
3025
4581
  * Execute the full shutdown sequence.
@@ -3059,9 +4615,6 @@ var JetstreamModule = class {
3059
4615
  this.shutdownManager = shutdownManager;
3060
4616
  this.strategy = strategy;
3061
4617
  }
3062
- // -------------------------------------------------------------------
3063
- // forRoot — global module registration
3064
- // -------------------------------------------------------------------
3065
4618
  /**
3066
4619
  * Register the JetStream transport globally.
3067
4620
  *
@@ -3088,9 +4641,6 @@ var JetstreamModule = class {
3088
4641
  ]
3089
4642
  };
3090
4643
  }
3091
- // -------------------------------------------------------------------
3092
- // forRootAsync — async global module registration
3093
- // -------------------------------------------------------------------
3094
4644
  /**
3095
4645
  * Register the JetStream transport globally with async configuration.
3096
4646
  *
@@ -3119,9 +4669,6 @@ var JetstreamModule = class {
3119
4669
  ]
3120
4670
  };
3121
4671
  }
3122
- // -------------------------------------------------------------------
3123
- // forFeature — per-module client registration
3124
- // -------------------------------------------------------------------
3125
4672
  /**
3126
4673
  * Register a lightweight client proxy for a target service.
3127
4674
  *
@@ -3147,9 +4694,6 @@ var JetstreamModule = class {
3147
4694
  exports: [clientToken]
3148
4695
  };
3149
4696
  }
3150
- // -------------------------------------------------------------------
3151
- // Provider factories
3152
- // -------------------------------------------------------------------
3153
4697
  static createCoreProviders(options) {
3154
4698
  return [
3155
4699
  {
@@ -3167,8 +4711,8 @@ var JetstreamModule = class {
3167
4711
  provide: JETSTREAM_EVENT_BUS,
3168
4712
  inject: [JETSTREAM_OPTIONS],
3169
4713
  useFactory: (options) => {
3170
- const logger = new import_common14.Logger("Jetstream:Module");
3171
- return new EventBus(logger, options.hooks);
4714
+ const logger5 = new import_common18.Logger("Jetstream:Module");
4715
+ return new EventBus(logger5, options.hooks);
3172
4716
  }
3173
4717
  },
3174
4718
  // Codec — global encode/decode
@@ -3207,10 +4751,8 @@ var JetstreamModule = class {
3207
4751
  );
3208
4752
  }
3209
4753
  },
3210
- // ---------------------------------------------------------------
3211
4754
  // Consumer infrastructure — only created when consumer !== false.
3212
4755
  // Providers return null when consumer is disabled (publisher-only mode).
3213
- // ---------------------------------------------------------------
3214
4756
  // PatternRegistry — subject-to-handler mapping
3215
4757
  {
3216
4758
  provide: PatternRegistry,
@@ -3246,8 +4788,14 @@ var JetstreamModule = class {
3246
4788
  // MessageProvider — pull-based message consumption
3247
4789
  {
3248
4790
  provide: MessageProvider,
3249
- inject: [JETSTREAM_OPTIONS, JETSTREAM_CONNECTION, JETSTREAM_EVENT_BUS, ConsumerProvider],
3250
- useFactory: (options, connection, eventBus, consumerProvider) => {
4791
+ inject: [
4792
+ JETSTREAM_OPTIONS,
4793
+ JETSTREAM_CONNECTION,
4794
+ JETSTREAM_EVENT_BUS,
4795
+ ConsumerProvider,
4796
+ StreamProvider
4797
+ ],
4798
+ useFactory: (options, connection, eventBus, consumerProvider, streamProvider) => {
3251
4799
  if (options.consumer === false) return null;
3252
4800
  const consumeOptionsMap = /* @__PURE__ */ new Map();
3253
4801
  if (options.events?.consume)
@@ -3257,10 +4805,22 @@ var JetstreamModule = class {
3257
4805
  if (options.rpc?.mode === "jetstream" && options.rpc.consume) {
3258
4806
  consumeOptionsMap.set("cmd" /* Command */, options.rpc.consume);
3259
4807
  }
3260
- const consumerRecoveryFn = consumerProvider ? async (kind) => {
3261
- const jsm = await connection.getJetStreamManager();
3262
- return consumerProvider.recoverConsumer(jsm, kind);
3263
- } : void 0;
4808
+ const derived = deriveOtelAttrs(options);
4809
+ const { otel, serverEndpoint: otelEndpoint, serviceName: otelServiceName } = derived;
4810
+ const consumerRecoveryFn = consumerProvider && streamProvider ? async (kind) => withSelfHealingSpan(
4811
+ otel,
4812
+ {
4813
+ serviceName: otelServiceName,
4814
+ endpoint: otelEndpoint,
4815
+ consumer: consumerProvider.getConsumerName(kind),
4816
+ stream: streamProvider.getStreamName(kind),
4817
+ reason: "consumer not found"
4818
+ },
4819
+ async () => {
4820
+ const jsm = await connection.getJetStreamManager();
4821
+ return consumerProvider.recoverConsumer(jsm, kind);
4822
+ }
4823
+ ) : void 0;
3264
4824
  return new MessageProvider(connection, eventBus, consumeOptionsMap, consumerRecoveryFn);
3265
4825
  }
3266
4826
  },
@@ -3331,7 +4891,8 @@ var JetstreamModule = class {
3331
4891
  codec,
3332
4892
  eventBus,
3333
4893
  rpcOptions,
3334
- ackWaitMap
4894
+ ackWaitMap,
4895
+ options
3335
4896
  );
3336
4897
  }
3337
4898
  },
@@ -3434,9 +4995,6 @@ var JetstreamModule = class {
3434
4995
  }
3435
4996
  ];
3436
4997
  }
3437
- // -------------------------------------------------------------------
3438
- // Lifecycle hooks
3439
- // -------------------------------------------------------------------
3440
4998
  /**
3441
4999
  * Gracefully shut down the transport on application termination.
3442
5000
  */
@@ -3447,23 +5005,34 @@ var JetstreamModule = class {
3447
5005
  }
3448
5006
  };
3449
5007
  JetstreamModule = __decorateClass([
3450
- (0, import_common14.Global)(),
3451
- (0, import_common14.Module)({}),
3452
- __decorateParam(0, (0, import_common14.Optional)()),
3453
- __decorateParam(0, (0, import_common14.Inject)(ShutdownManager)),
3454
- __decorateParam(1, (0, import_common14.Optional)()),
3455
- __decorateParam(1, (0, import_common14.Inject)(JetstreamStrategy))
5008
+ (0, import_common18.Global)(),
5009
+ (0, import_common18.Module)({}),
5010
+ __decorateParam(0, (0, import_common18.Optional)()),
5011
+ __decorateParam(0, (0, import_common18.Inject)(ShutdownManager)),
5012
+ __decorateParam(1, (0, import_common18.Optional)()),
5013
+ __decorateParam(1, (0, import_common18.Inject)(JetstreamStrategy))
3456
5014
  ], JetstreamModule);
3457
5015
  // Annotate the CommonJS export names for ESM import in node:
3458
5016
  0 && (module.exports = {
5017
+ ConsumeKind,
5018
+ DEFAULT_BROADCAST_CONSUMER_CONFIG,
5019
+ DEFAULT_BROADCAST_STREAM_CONFIG,
5020
+ DEFAULT_COMMAND_CONSUMER_CONFIG,
5021
+ DEFAULT_COMMAND_STREAM_CONFIG,
5022
+ DEFAULT_DLQ_STREAM_CONFIG,
5023
+ DEFAULT_EVENT_CONSUMER_CONFIG,
5024
+ DEFAULT_EVENT_STREAM_CONFIG,
5025
+ DEFAULT_JETSTREAM_RPC_TIMEOUT,
3459
5026
  DEFAULT_METADATA_BUCKET,
3460
5027
  DEFAULT_METADATA_HISTORY,
3461
5028
  DEFAULT_METADATA_REPLICAS,
3462
5029
  DEFAULT_METADATA_TTL,
3463
- EventBus,
5030
+ DEFAULT_ORDERED_STREAM_CONFIG,
5031
+ DEFAULT_RPC_TIMEOUT,
5032
+ DEFAULT_SHUTDOWN_TIMEOUT,
5033
+ DEFAULT_TRACES,
3464
5034
  JETSTREAM_CODEC,
3465
5035
  JETSTREAM_CONNECTION,
3466
- JETSTREAM_EVENT_BUS,
3467
5036
  JETSTREAM_OPTIONS,
3468
5037
  JetstreamClient,
3469
5038
  JetstreamDlqHeader,
@@ -3473,12 +5042,18 @@ JetstreamModule = __decorateClass([
3473
5042
  JetstreamRecord,
3474
5043
  JetstreamRecordBuilder,
3475
5044
  JetstreamStrategy,
5045
+ JetstreamTrace,
3476
5046
  JsonCodec,
3477
5047
  MIN_METADATA_TTL,
3478
5048
  MessageKind,
5049
+ MsgpackCodec,
5050
+ NatsErrorCode,
3479
5051
  PatternPrefix,
5052
+ PublishKind,
5053
+ RESERVED_HEADERS,
3480
5054
  RpcContext,
3481
5055
  StreamKind,
5056
+ TRACER_NAME,
3482
5057
  TransportEvent,
3483
5058
  buildBroadcastSubject,
3484
5059
  buildSubject,