@horizon-republic/nestjs-jetstream 2.9.1 → 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.js CHANGED
@@ -14,19 +14,21 @@ var __decorateParam = (index, decorator) => (target, key) => decorator(target, k
14
14
  import {
15
15
  Global,
16
16
  Inject,
17
- Logger as Logger14,
17
+ Logger as Logger18,
18
18
  Module,
19
19
  Optional
20
20
  } from "@nestjs/common";
21
21
 
22
22
  // src/client/jetstream.client.ts
23
- import { Logger } from "@nestjs/common";
23
+ import { Logger as Logger5 } from "@nestjs/common";
24
24
  import { ClientProxy } from "@nestjs/microservices";
25
+ import { context as context6 } from "@opentelemetry/api";
26
+ import { nuid } from "@nats-io/nuid";
25
27
  import {
26
28
  createInbox,
27
- headers as natsHeaders
29
+ headers as natsHeaders,
30
+ TimeoutError
28
31
  } from "@nats-io/transport-node";
29
- import { nuid } from "@nats-io/nuid";
30
32
 
31
33
  // src/interfaces/hooks.interface.ts
32
34
  var MessageKind = /* @__PURE__ */ ((MessageKind2) => {
@@ -224,6 +226,920 @@ var PatternPrefix = /* @__PURE__ */ ((PatternPrefix2) => {
224
226
  var isJetStreamRpcMode = (rpc) => rpc?.mode === "jetstream";
225
227
  var isCoreRpcMode = (rpc) => !rpc || rpc.mode === "core";
226
228
 
229
+ // src/otel/constants.ts
230
+ var TRACER_NAME = "@horizon-republic/nestjs-jetstream";
231
+
232
+ // src/otel/attribute-keys.ts
233
+ var ATTR_MESSAGING_SYSTEM = "messaging.system";
234
+ var ATTR_MESSAGING_DESTINATION_NAME = "messaging.destination.name";
235
+ var ATTR_MESSAGING_DESTINATION_TEMPLATE = "messaging.destination.template";
236
+ var ATTR_MESSAGING_CLIENT_ID = "messaging.client.id";
237
+ var ATTR_MESSAGING_OPERATION_NAME = "messaging.operation.name";
238
+ var ATTR_MESSAGING_OPERATION_TYPE = "messaging.operation.type";
239
+ var ATTR_MESSAGING_MESSAGE_BODY_SIZE = "messaging.message.body.size";
240
+ var ATTR_MESSAGING_MESSAGE_ID = "messaging.message.id";
241
+ var ATTR_MESSAGING_MESSAGE_CONVERSATION_ID = "messaging.message.conversation_id";
242
+ var ATTR_MESSAGING_CONSUMER_GROUP_NAME = "messaging.consumer.group.name";
243
+ var ATTR_MESSAGING_HEADER_PREFIX = "messaging.header.";
244
+ var ATTR_MESSAGING_NATS_STREAM_NAME = "messaging.nats.stream.name";
245
+ var ATTR_MESSAGING_NATS_STREAM_SEQUENCE = "messaging.nats.message.stream_sequence";
246
+ var ATTR_MESSAGING_NATS_CONSUMER_SEQUENCE = "messaging.nats.message.consumer_sequence";
247
+ var ATTR_MESSAGING_NATS_DELIVERY_COUNT = "messaging.nats.message.delivery_count";
248
+ var ATTR_MESSAGING_NATS_BODY = "messaging.nats.message.body";
249
+ var ATTR_MESSAGING_NATS_BODY_TRUNCATED = "messaging.nats.message.body.truncated";
250
+ var ATTR_SERVER_ADDRESS = "server.address";
251
+ var ATTR_SERVER_PORT = "server.port";
252
+ var ATTR_JETSTREAM_SERVICE_NAME = "jetstream.service.name";
253
+ var ATTR_JETSTREAM_KIND = "jetstream.kind";
254
+ var ATTR_JETSTREAM_RPC_REPLY_HAS_ERROR = "jetstream.rpc.reply.has_error";
255
+ var ATTR_JETSTREAM_RPC_REPLY_ERROR_CODE = "jetstream.rpc.reply.error.code";
256
+ var ATTR_JETSTREAM_PROVISIONING_ENTITY = "jetstream.provisioning.entity";
257
+ var ATTR_JETSTREAM_PROVISIONING_ACTION = "jetstream.provisioning.action";
258
+ var ATTR_JETSTREAM_PROVISIONING_NAME = "jetstream.provisioning.name";
259
+ var ATTR_JETSTREAM_SELF_HEALING_REASON = "jetstream.self_healing.reason";
260
+ var ATTR_JETSTREAM_MIGRATION_REASON = "jetstream.migration.reason";
261
+ var ATTR_JETSTREAM_DEAD_LETTER_REASON = "jetstream.dead_letter.reason";
262
+ var ATTR_JETSTREAM_SCHEDULE_TARGET = "jetstream.schedule.target";
263
+ var ATTR_NATS_CONNECTION_SERVER = "nats.connection.server";
264
+ var NATS_MSG_ID_HEADER = "Nats-Msg-Id";
265
+ var HOOK_PUBLISH = "publishHook";
266
+ var HOOK_CONSUME = "consumeHook";
267
+ var HOOK_RESPONSE = "responseHook";
268
+ var SPAN_NAME_PUBLISH = "publish";
269
+ var SPAN_NAME_PROCESS = "process";
270
+ var SPAN_NAME_SEND = "send";
271
+ var SPAN_NAME_DEAD_LETTER = "dead_letter";
272
+ var SPAN_NAME_NATS_CONNECTION = "nats.connection";
273
+ var SPAN_NAME_JETSTREAM_SHUTDOWN = "jetstream.shutdown";
274
+ var SPAN_NAME_JETSTREAM_SELF_HEALING = "jetstream.self_healing";
275
+ var SPAN_NAME_JETSTREAM_MIGRATION = "jetstream.migration";
276
+ var SPAN_NAME_JETSTREAM_PROVISIONING_PREFIX = "jetstream.provisioning.";
277
+ var EVENT_CONNECTION_DISCONNECTED = "connection.disconnected";
278
+ var EVENT_CONNECTION_RECONNECTED = "connection.reconnected";
279
+ var messagingHeaderAttr = (headerName) => `${ATTR_MESSAGING_HEADER_PREFIX}${headerName.toLowerCase()}`;
280
+
281
+ // src/otel/trace-kinds.ts
282
+ var JetstreamTrace = /* @__PURE__ */ ((JetstreamTrace2) => {
283
+ JetstreamTrace2["Publish"] = "publish";
284
+ JetstreamTrace2["Consume"] = "consume";
285
+ JetstreamTrace2["RpcClientSend"] = "rpc.client.send";
286
+ JetstreamTrace2["DeadLetter"] = "dead_letter";
287
+ JetstreamTrace2["ConnectionLifecycle"] = "connection.lifecycle";
288
+ JetstreamTrace2["SelfHealing"] = "self_healing";
289
+ JetstreamTrace2["Provisioning"] = "provisioning";
290
+ JetstreamTrace2["Migration"] = "migration";
291
+ JetstreamTrace2["Shutdown"] = "shutdown";
292
+ return JetstreamTrace2;
293
+ })(JetstreamTrace || {});
294
+ var DEFAULT_TRACES = [
295
+ "publish" /* Publish */,
296
+ "consume" /* Consume */,
297
+ "rpc.client.send" /* RpcClientSend */,
298
+ "dead_letter" /* DeadLetter */
299
+ ];
300
+
301
+ // src/otel/capture.ts
302
+ var NEGATION_PREFIX = "!";
303
+ var REGEX_SPECIAL_RE = /[.+?^${}()|[\]\\*]/gu;
304
+ var escapeForRegex = (input) => input.replace(REGEX_SPECIAL_RE, "\\$&");
305
+ var globToRegex = (glob) => {
306
+ const escaped = escapeForRegex(glob.toLowerCase()).replace(/\\\*/gu, ".*");
307
+ return new RegExp(`^${escaped}$`, "u");
308
+ };
309
+ var compileHeaderAllowlist = (allowlist) => {
310
+ if (allowlist === true) return () => true;
311
+ if (allowlist === false) return () => false;
312
+ if (allowlist.length === 0) return () => false;
313
+ const includes = [];
314
+ const excludes = [];
315
+ for (const pattern of allowlist) {
316
+ const isExclude = pattern.startsWith(NEGATION_PREFIX);
317
+ const body = isExclude ? pattern.slice(NEGATION_PREFIX.length) : pattern;
318
+ const regex = globToRegex(body);
319
+ if (isExclude) excludes.push(regex);
320
+ else includes.push(regex);
321
+ }
322
+ return (name) => {
323
+ const lower = name.toLowerCase();
324
+ if (!includes.some((re) => re.test(lower))) return false;
325
+ if (excludes.some((re) => re.test(lower))) return false;
326
+ return true;
327
+ };
328
+ };
329
+ var subjectMatcherCache = /* @__PURE__ */ new WeakMap();
330
+ var compileSubjectAllowlist = (allowlist) => {
331
+ if (!allowlist || allowlist.length === 0) return () => true;
332
+ const cached = subjectMatcherCache.get(allowlist);
333
+ if (cached) return cached;
334
+ const regexes = allowlist.map(globToRegex);
335
+ const matcher = (subject) => {
336
+ const lower = subject.toLowerCase();
337
+ return regexes.some((re) => re.test(lower));
338
+ };
339
+ subjectMatcherCache.set(allowlist, matcher);
340
+ return matcher;
341
+ };
342
+ var subjectMatchesAllowlist = (subject, allowlist) => compileSubjectAllowlist(allowlist)(subject);
343
+ var HEADER_DENYLIST = /* @__PURE__ */ new Set([
344
+ "traceparent",
345
+ "tracestate",
346
+ "baggage",
347
+ "sentry-trace",
348
+ "b3",
349
+ "x-b3-traceid",
350
+ "x-b3-spanid",
351
+ "x-b3-parentspanid",
352
+ "x-b3-sampled",
353
+ "x-b3-flags",
354
+ "uber-trace-id",
355
+ "x-correlation-id",
356
+ "x-reply-to",
357
+ "x-error",
358
+ "x-subject",
359
+ "x-caller-name"
360
+ ]);
361
+ var isNatsServerHeader = (lower) => lower.startsWith("nats-");
362
+ var captureMatchingHeaders = (headers2, matcher) => {
363
+ if (!headers2) return {};
364
+ const out = {};
365
+ for (const key of headers2.keys()) {
366
+ const lower = key.toLowerCase();
367
+ if (HEADER_DENYLIST.has(lower)) continue;
368
+ if (isNatsServerHeader(lower)) continue;
369
+ if (!matcher(key)) continue;
370
+ const value = headers2.get(key);
371
+ if (value === "") continue;
372
+ out[messagingHeaderAttr(lower)] = value;
373
+ }
374
+ return out;
375
+ };
376
+ var tryUtf8Decode = (bytes) => {
377
+ try {
378
+ return new TextDecoder("utf-8", { fatal: false }).decode(bytes);
379
+ } catch {
380
+ return Buffer.from(bytes).toString("base64");
381
+ }
382
+ };
383
+ var captureBodyAttribute = (subject, payload, capture) => {
384
+ if (capture === false) return {};
385
+ if (payload.byteLength === 0) return {};
386
+ if (!subjectMatchesAllowlist(subject, capture.subjectAllowlist)) return {};
387
+ const { maxBytes } = capture;
388
+ const truncated = payload.byteLength > maxBytes;
389
+ const slice = truncated ? payload.subarray(0, maxBytes) : payload;
390
+ const decoded = tryUtf8Decode(slice);
391
+ const out = { [ATTR_MESSAGING_NATS_BODY]: decoded };
392
+ if (truncated) out[ATTR_MESSAGING_NATS_BODY_TRUNCATED] = true;
393
+ return out;
394
+ };
395
+
396
+ // src/otel/config.ts
397
+ var PublishKind = /* @__PURE__ */ ((PublishKind2) => {
398
+ PublishKind2["Event"] = "event";
399
+ PublishKind2["RpcRequest"] = "rpc.request";
400
+ PublishKind2["Broadcast"] = "broadcast";
401
+ PublishKind2["Ordered"] = "ordered";
402
+ return PublishKind2;
403
+ })(PublishKind || {});
404
+ var ConsumeKind = /* @__PURE__ */ ((ConsumeKind2) => {
405
+ ConsumeKind2["Event"] = "event";
406
+ ConsumeKind2["Rpc"] = "rpc";
407
+ ConsumeKind2["Broadcast"] = "broadcast";
408
+ ConsumeKind2["Ordered"] = "ordered";
409
+ return ConsumeKind2;
410
+ })(ConsumeKind || {});
411
+ var DEFAULT_CAPTURE_HEADERS = ["x-request-id"];
412
+ var DEFAULT_CAPTURE_BODY_MAX_BYTES = 4096;
413
+ var NESTJS_BARE_ERROR_MESSAGE = "Internal server error";
414
+ var isNestjsBareErrorSentinel = (obj) => obj.status === "error" && obj.message === NESTJS_BARE_ERROR_MESSAGE;
415
+ var defaultErrorClassifier = (err) => {
416
+ if (err === null || typeof err !== "object") return "unexpected";
417
+ if (!(err instanceof Error)) {
418
+ if (isNestjsBareErrorSentinel(err)) return "unexpected";
419
+ return "expected";
420
+ }
421
+ let proto = err;
422
+ while (proto) {
423
+ const name = proto.constructor?.name;
424
+ if (name === "RpcException" || name === "HttpException") return "expected";
425
+ proto = Object.getPrototypeOf(proto);
426
+ }
427
+ return "unexpected";
428
+ };
429
+ var expandTracesOption = (option) => {
430
+ if (option === void 0 || option === "default") return new Set(DEFAULT_TRACES);
431
+ if (option === "all") return new Set(Object.values(JetstreamTrace));
432
+ if (option === "none") return /* @__PURE__ */ new Set();
433
+ return new Set(option);
434
+ };
435
+ var compileHeaderMatcher = (option) => compileHeaderAllowlist(option ?? DEFAULT_CAPTURE_HEADERS);
436
+ var resolveCaptureBody = (option) => {
437
+ if (option === void 0 || option === false) return false;
438
+ if (option === true) return { maxBytes: DEFAULT_CAPTURE_BODY_MAX_BYTES };
439
+ return {
440
+ maxBytes: option.maxBytes ?? DEFAULT_CAPTURE_BODY_MAX_BYTES,
441
+ subjectAllowlist: option.subjectAllowlist
442
+ };
443
+ };
444
+ var resolveOtelOptions = (options = {}) => {
445
+ return {
446
+ enabled: options.enabled ?? true,
447
+ traces: expandTracesOption(options.traces),
448
+ captureHeaders: compileHeaderMatcher(options.captureHeaders),
449
+ captureBody: resolveCaptureBody(options.captureBody),
450
+ publishHook: options.publishHook,
451
+ consumeHook: options.consumeHook,
452
+ responseHook: options.responseHook,
453
+ shouldTracePublish: options.shouldTracePublish,
454
+ shouldTraceConsume: options.shouldTraceConsume,
455
+ errorClassifier: options.errorClassifier ?? defaultErrorClassifier
456
+ };
457
+ };
458
+
459
+ // src/otel/internal-utils.ts
460
+ import { Logger } from "@nestjs/common";
461
+ var logger = new Logger("Jetstream:Otel");
462
+ var safelyInvokeHook = (hookName, hook, ...args) => {
463
+ if (!hook) return;
464
+ const logHookFailure = (err) => {
465
+ const message = err instanceof Error ? err.message : String(err);
466
+ logger.debug(`OTel ${hookName} threw: ${message}`);
467
+ };
468
+ try {
469
+ const result = hook(...args);
470
+ if (result !== null && typeof result === "object" && "then" in result && typeof result.then === "function") {
471
+ result.then(void 0, logHookFailure);
472
+ }
473
+ } catch (err) {
474
+ logHookFailure(err);
475
+ }
476
+ };
477
+ var stripIpv6Brackets = (host) => host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
478
+ var parsePort = (portRaw) => {
479
+ if (!portRaw) return void 0;
480
+ const port = Number.parseInt(portRaw, 10);
481
+ return Number.isInteger(port) ? port : void 0;
482
+ };
483
+ var parseServerAddress = (servers) => {
484
+ const raw = servers[0];
485
+ if (!raw) return null;
486
+ if (raw.includes("://")) {
487
+ try {
488
+ const url = new URL(raw);
489
+ if (url.hostname.length === 0) return null;
490
+ const host2 = stripIpv6Brackets(url.hostname);
491
+ const port2 = parsePort(url.port || void 0);
492
+ return port2 === void 0 ? { host: host2 } : { host: host2, port: port2 };
493
+ } catch {
494
+ return null;
495
+ }
496
+ }
497
+ if (raw.startsWith("[")) {
498
+ const closeIdx = raw.indexOf("]");
499
+ if (closeIdx <= 0) return null;
500
+ const host2 = raw.slice(1, closeIdx);
501
+ const port2 = parsePort(raw.slice(closeIdx + 1).replace(/^:/u, ""));
502
+ return port2 === void 0 ? { host: host2 } : { host: host2, port: port2 };
503
+ }
504
+ const [host, portRaw] = raw.split(":");
505
+ if (!host) return null;
506
+ const port = parsePort(portRaw);
507
+ return port === void 0 ? { host } : { host, port };
508
+ };
509
+ var deriveOtelAttrs = (options) => ({
510
+ otel: resolveOtelOptions(options.otel),
511
+ serviceName: internalName(options.name),
512
+ serverEndpoint: parseServerAddress(options.servers)
513
+ });
514
+
515
+ // src/otel/propagator.ts
516
+ import {
517
+ propagation
518
+ } from "@opentelemetry/api";
519
+ var injectContext = (ctx, carrier, setter) => {
520
+ propagation.inject(ctx, carrier, setter);
521
+ };
522
+ var extractContext = (ctx, carrier, getter) => propagation.extract(ctx, carrier, getter);
523
+
524
+ // src/otel/tracer.ts
525
+ import { trace } from "@opentelemetry/api";
526
+ var PACKAGE_VERSION = true ? "2.10.0" : "0.0.0";
527
+ var getTracer = () => trace.getTracer(TRACER_NAME, PACKAGE_VERSION);
528
+
529
+ // src/otel/carrier.ts
530
+ var hdrsSetter = {
531
+ set: (headers2, key, value) => {
532
+ headers2.set(key, value);
533
+ }
534
+ };
535
+ var hdrsGetter = {
536
+ keys: (headers2) => headers2 ? headers2.keys() : [],
537
+ get: (headers2, key) => {
538
+ if (!headers2) return void 0;
539
+ const all = typeof headers2.values === "function" ? headers2.values(key) : void 0;
540
+ if (Array.isArray(all)) {
541
+ const nonEmpty = all.filter((value) => value !== "");
542
+ if (nonEmpty.length === 0) return void 0;
543
+ return nonEmpty.join(",");
544
+ }
545
+ const single = headers2.get(key);
546
+ return single === "" ? void 0 : single;
547
+ }
548
+ };
549
+
550
+ // src/otel/attributes.ts
551
+ var MESSAGING_SYSTEM = "nats";
552
+ var baseMessagingAttributes = (ctx) => {
553
+ const attrs = {
554
+ [ATTR_MESSAGING_SYSTEM]: MESSAGING_SYSTEM,
555
+ [ATTR_MESSAGING_DESTINATION_NAME]: ctx.subject,
556
+ [ATTR_MESSAGING_CLIENT_ID]: ctx.serviceName
557
+ };
558
+ if (ctx.serverAddress) attrs[ATTR_SERVER_ADDRESS] = ctx.serverAddress;
559
+ if (ctx.serverPort !== void 0) attrs[ATTR_SERVER_PORT] = ctx.serverPort;
560
+ if (ctx.pattern && ctx.pattern !== ctx.subject) {
561
+ attrs[ATTR_MESSAGING_DESTINATION_TEMPLATE] = ctx.pattern;
562
+ }
563
+ return attrs;
564
+ };
565
+ var jetstreamKindForPublish = (kind) => kind === "rpc.request" /* RpcRequest */ ? "rpc" /* Rpc */ : kind;
566
+ var buildPublishAttributes = (ctx) => {
567
+ const attrs = {
568
+ ...baseMessagingAttributes(ctx),
569
+ [ATTR_MESSAGING_OPERATION_NAME]: SPAN_NAME_PUBLISH,
570
+ [ATTR_MESSAGING_OPERATION_TYPE]: SPAN_NAME_SEND,
571
+ [ATTR_MESSAGING_MESSAGE_BODY_SIZE]: ctx.payloadBytes,
572
+ [ATTR_JETSTREAM_KIND]: jetstreamKindForPublish(ctx.kind)
573
+ };
574
+ if (ctx.messageId) attrs[ATTR_MESSAGING_MESSAGE_ID] = ctx.messageId;
575
+ if (ctx.correlationId) attrs[ATTR_MESSAGING_MESSAGE_CONVERSATION_ID] = ctx.correlationId;
576
+ return attrs;
577
+ };
578
+ var buildConsumeAttributes = (ctx) => {
579
+ const { msg, info, kind, payloadBytes } = ctx;
580
+ const attrs = {
581
+ ...baseMessagingAttributes(ctx),
582
+ [ATTR_MESSAGING_OPERATION_NAME]: SPAN_NAME_PROCESS,
583
+ [ATTR_MESSAGING_OPERATION_TYPE]: SPAN_NAME_PROCESS,
584
+ [ATTR_MESSAGING_MESSAGE_BODY_SIZE]: payloadBytes,
585
+ [ATTR_JETSTREAM_KIND]: kind
586
+ };
587
+ if (info) {
588
+ attrs[ATTR_MESSAGING_NATS_STREAM_NAME] = info.stream;
589
+ attrs[ATTR_MESSAGING_CONSUMER_GROUP_NAME] = info.consumer;
590
+ attrs[ATTR_MESSAGING_NATS_STREAM_SEQUENCE] = info.streamSequence;
591
+ attrs[ATTR_MESSAGING_NATS_CONSUMER_SEQUENCE] = info.deliverySequence;
592
+ attrs[ATTR_MESSAGING_NATS_DELIVERY_COUNT] = info.deliveryCount;
593
+ }
594
+ const messageId = msg.headers?.get(NATS_MSG_ID_HEADER);
595
+ if (messageId) attrs[ATTR_MESSAGING_MESSAGE_ID] = messageId;
596
+ return attrs;
597
+ };
598
+ var buildRpcClientAttributes = (ctx) => {
599
+ const attrs = {
600
+ ...baseMessagingAttributes(ctx),
601
+ [ATTR_MESSAGING_OPERATION_NAME]: SPAN_NAME_SEND,
602
+ [ATTR_MESSAGING_OPERATION_TYPE]: SPAN_NAME_SEND,
603
+ [ATTR_MESSAGING_MESSAGE_BODY_SIZE]: ctx.payloadBytes,
604
+ [ATTR_JETSTREAM_KIND]: "rpc" /* Rpc */
605
+ };
606
+ if (ctx.correlationId) attrs[ATTR_MESSAGING_MESSAGE_CONVERSATION_ID] = ctx.correlationId;
607
+ if (ctx.messageId) attrs[ATTR_MESSAGING_MESSAGE_ID] = ctx.messageId;
608
+ return attrs;
609
+ };
610
+ var buildDeadLetterAttributes = (ctx) => {
611
+ const attrs = {
612
+ ...baseMessagingAttributes(ctx),
613
+ [ATTR_MESSAGING_OPERATION_NAME]: SPAN_NAME_DEAD_LETTER,
614
+ [ATTR_MESSAGING_OPERATION_TYPE]: SPAN_NAME_PROCESS,
615
+ [ATTR_MESSAGING_NATS_DELIVERY_COUNT]: ctx.finalDeliveryCount
616
+ };
617
+ if (ctx.reason) attrs[ATTR_JETSTREAM_DEAD_LETTER_REASON] = ctx.reason;
618
+ return attrs;
619
+ };
620
+ var extractFromRpcException = (record) => {
621
+ const getError = record.getError;
622
+ if (typeof getError !== "function") return void 0;
623
+ return codeFromPayload(getError.call(record));
624
+ };
625
+ var extractFromHttpException = (record) => {
626
+ const getStatus = record.getStatus;
627
+ if (typeof getStatus !== "function") return void 0;
628
+ const status = getStatus.call(record);
629
+ return typeof status === "number" && Number.isFinite(status) ? `HTTP_${status}` : void 0;
630
+ };
631
+ var extractFromOwnCode = (record) => {
632
+ const code = record.code ?? record.errorCode;
633
+ return typeof code === "string" && isStableErrorCode(code) ? code : void 0;
634
+ };
635
+ var extractExpectedErrorCode = (err) => {
636
+ if (err === null || typeof err !== "object") return void 0;
637
+ const record = err;
638
+ if (hasAncestorNamed(err, "RpcException")) {
639
+ const fromPayload = extractFromRpcException(record);
640
+ if (fromPayload !== void 0) return fromPayload;
641
+ }
642
+ if (hasAncestorNamed(err, "HttpException")) {
643
+ const fromStatus = extractFromHttpException(record);
644
+ if (fromStatus !== void 0) return fromStatus;
645
+ }
646
+ return extractFromOwnCode(record);
647
+ };
648
+ var hasAncestorNamed = (err, name) => {
649
+ let proto = Object.getPrototypeOf(err);
650
+ while (proto) {
651
+ const ctorName = proto.constructor?.name;
652
+ if (ctorName === name) return true;
653
+ proto = Object.getPrototypeOf(proto);
654
+ }
655
+ return false;
656
+ };
657
+ var STABLE_ERROR_CODE_RE = /^[A-Z][A-Z0-9_]*$/u;
658
+ var isStableErrorCode = (value) => STABLE_ERROR_CODE_RE.test(value);
659
+ var codeFromPayload = (payload) => {
660
+ if (payload === null || payload === void 0) return void 0;
661
+ if (typeof payload === "string") return isStableErrorCode(payload) ? payload : void 0;
662
+ if (typeof payload === "object") {
663
+ const code = payload.code;
664
+ if (typeof code === "string" && isStableErrorCode(code)) return code;
665
+ }
666
+ return void 0;
667
+ };
668
+ var buildExpectedErrorAttributes = (err) => {
669
+ const attrs = {
670
+ [ATTR_JETSTREAM_RPC_REPLY_HAS_ERROR]: true
671
+ };
672
+ const code = extractExpectedErrorCode(err);
673
+ if (code !== void 0) attrs[ATTR_JETSTREAM_RPC_REPLY_ERROR_CODE] = code;
674
+ return attrs;
675
+ };
676
+ var applyExpectedErrorAttributes = (span, err) => {
677
+ span.setAttributes(buildExpectedErrorAttributes(err));
678
+ };
679
+
680
+ // src/otel/spans/publish.ts
681
+ import { Logger as Logger2 } from "@nestjs/common";
682
+ import { SpanKind, SpanStatusCode, context, trace as trace2 } from "@opentelemetry/api";
683
+ var logger2 = new Logger2("Jetstream:Otel");
684
+ var shouldTracePublishSafe = (predicate, subject, record) => {
685
+ if (!predicate) return true;
686
+ try {
687
+ return predicate(subject, record);
688
+ } catch (err) {
689
+ const message = err instanceof Error ? err.message : String(err);
690
+ logger2.debug(`OTel shouldTracePublish threw: ${message}`);
691
+ return true;
692
+ }
693
+ };
694
+ var withPublishSpan = async (ctx, config, fn) => {
695
+ if (!config.enabled) return fn();
696
+ const shouldCreateSpan = config.traces.has("publish" /* Publish */) && shouldTracePublishSafe(config.shouldTracePublish, ctx.subject, ctx.record);
697
+ if (!shouldCreateSpan) {
698
+ injectContext(context.active(), ctx.headers, hdrsSetter);
699
+ return fn();
700
+ }
701
+ const tracer = getTracer();
702
+ const span = tracer.startSpan(`${SPAN_NAME_PUBLISH} ${ctx.subject}`, {
703
+ kind: SpanKind.PRODUCER,
704
+ attributes: {
705
+ ...buildPublishAttributes({
706
+ subject: ctx.subject,
707
+ pattern: ctx.pattern,
708
+ serviceName: ctx.serviceName,
709
+ serverAddress: ctx.endpoint?.host,
710
+ serverPort: ctx.endpoint?.port,
711
+ kind: ctx.kind,
712
+ payloadBytes: ctx.payloadBytes,
713
+ messageId: ctx.messageId,
714
+ correlationId: ctx.correlationId
715
+ }),
716
+ ...ctx.scheduleTarget ? { [ATTR_JETSTREAM_SCHEDULE_TARGET]: ctx.scheduleTarget } : {},
717
+ ...captureMatchingHeaders(ctx.headers, config.captureHeaders),
718
+ ...captureBodyAttribute(ctx.subject, ctx.payload, config.captureBody)
719
+ }
720
+ });
721
+ const ctxWithSpan = trace2.setSpan(context.active(), span);
722
+ injectContext(ctxWithSpan, ctx.headers, hdrsSetter);
723
+ const start = Date.now();
724
+ const invokeResponseHook = (error) => {
725
+ context.with(ctxWithSpan, () => {
726
+ safelyInvokeHook(HOOK_RESPONSE, config.responseHook, span, {
727
+ subject: ctx.subject,
728
+ durationMs: Date.now() - start,
729
+ error
730
+ });
731
+ });
732
+ };
733
+ try {
734
+ const result = await context.with(ctxWithSpan, async () => {
735
+ safelyInvokeHook(HOOK_PUBLISH, config.publishHook, span, {
736
+ subject: ctx.subject,
737
+ record: ctx.record,
738
+ kind: ctx.kind
739
+ });
740
+ return fn();
741
+ });
742
+ span.setStatus({ code: SpanStatusCode.OK });
743
+ invokeResponseHook();
744
+ return result;
745
+ } catch (err) {
746
+ const error = err instanceof Error ? err : new Error(String(err));
747
+ span.recordException(error);
748
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
749
+ invokeResponseHook(error);
750
+ throw err;
751
+ } finally {
752
+ span.end();
753
+ }
754
+ };
755
+
756
+ // src/otel/spans/consume.ts
757
+ import {
758
+ ROOT_CONTEXT,
759
+ SpanKind as SpanKind2,
760
+ SpanStatusCode as SpanStatusCode2,
761
+ context as context2,
762
+ trace as trace3
763
+ } from "@opentelemetry/api";
764
+ var isPromiseLike = (value) => typeof value === "object" && value !== null && typeof value.then === "function";
765
+ var applyExpectedError = (span, err) => {
766
+ span.setStatus({ code: SpanStatusCode2.OK });
767
+ applyExpectedErrorAttributes(span, err);
768
+ };
769
+ var applyUnexpectedError = (span, err) => {
770
+ const error = err instanceof Error ? err : new Error(String(err));
771
+ span.recordException(error);
772
+ span.setStatus({ code: SpanStatusCode2.ERROR, message: error.message });
773
+ };
774
+ var withConsumeSpan = (ctx, config, fn, options = {}) => {
775
+ if (!config.enabled) return fn();
776
+ const parentCtx = extractContext(ROOT_CONTEXT, ctx.msg.headers, hdrsGetter);
777
+ const shouldCreateSpan = config.traces.has("consume" /* Consume */) && (config.shouldTraceConsume?.(ctx.subject, ctx.msg) ?? true);
778
+ if (!shouldCreateSpan) {
779
+ return context2.with(parentCtx, fn);
780
+ }
781
+ const tracer = getTracer();
782
+ const span = tracer.startSpan(
783
+ `${SPAN_NAME_PROCESS} ${ctx.subject}`,
784
+ {
785
+ kind: SpanKind2.CONSUMER,
786
+ attributes: {
787
+ ...buildConsumeAttributes({
788
+ subject: ctx.subject,
789
+ pattern: ctx.pattern,
790
+ msg: ctx.msg,
791
+ info: ctx.info,
792
+ kind: ctx.kind,
793
+ payloadBytes: ctx.payloadBytes,
794
+ serviceName: ctx.serviceName,
795
+ serverAddress: ctx.endpoint?.host,
796
+ serverPort: ctx.endpoint?.port
797
+ }),
798
+ ...captureMatchingHeaders(ctx.msg.headers, config.captureHeaders),
799
+ ...captureBodyAttribute(ctx.subject, ctx.msg.data, config.captureBody)
800
+ }
801
+ },
802
+ parentCtx
803
+ );
804
+ const ctxWithSpan = trace3.setSpan(parentCtx, span);
805
+ const start = Date.now();
806
+ let finalized = false;
807
+ const { signal, timeoutLabel = "handler.timeout" } = options;
808
+ let detachAbort = null;
809
+ const invokeResponseHook = (durationMs, error) => {
810
+ context2.with(ctxWithSpan, () => {
811
+ safelyInvokeHook(HOOK_RESPONSE, config.responseHook, span, {
812
+ subject: ctx.subject,
813
+ durationMs,
814
+ error
815
+ });
816
+ });
817
+ };
818
+ const finishOk2 = () => {
819
+ if (finalized) return;
820
+ finalized = true;
821
+ detachAbort?.();
822
+ span.setStatus({ code: SpanStatusCode2.OK });
823
+ invokeResponseHook(Date.now() - start);
824
+ span.end();
825
+ };
826
+ const finishError2 = (err) => {
827
+ if (finalized) return;
828
+ finalized = true;
829
+ detachAbort?.();
830
+ let classification = "unexpected";
831
+ try {
832
+ classification = config.errorClassifier(err);
833
+ } catch {
834
+ }
835
+ if (classification === "expected") {
836
+ applyExpectedError(span, err);
837
+ } else {
838
+ applyUnexpectedError(span, err);
839
+ }
840
+ invokeResponseHook(Date.now() - start, err instanceof Error ? err : new Error(String(err)));
841
+ span.end();
842
+ };
843
+ const onAbort = () => {
844
+ if (finalized) return;
845
+ finalized = true;
846
+ const error = new Error(timeoutLabel);
847
+ span.addEvent(timeoutLabel);
848
+ span.recordException(error);
849
+ span.setStatus({ code: SpanStatusCode2.ERROR, message: timeoutLabel });
850
+ invokeResponseHook(Date.now() - start, error);
851
+ span.end();
852
+ };
853
+ if (signal) {
854
+ if (signal.aborted) {
855
+ onAbort();
856
+ } else {
857
+ signal.addEventListener("abort", onAbort, { once: true });
858
+ detachAbort = () => {
859
+ signal.removeEventListener("abort", onAbort);
860
+ };
861
+ }
862
+ }
863
+ let result;
864
+ try {
865
+ result = context2.with(ctxWithSpan, () => {
866
+ safelyInvokeHook(HOOK_CONSUME, config.consumeHook, span, {
867
+ subject: ctx.subject,
868
+ msg: ctx.msg,
869
+ handlerMetadata: ctx.handlerMetadata,
870
+ kind: ctx.kind
871
+ });
872
+ return fn();
873
+ });
874
+ } catch (err) {
875
+ finishError2(err);
876
+ throw err;
877
+ }
878
+ if (isPromiseLike(result)) {
879
+ return Promise.resolve(result).then(
880
+ (value) => {
881
+ finishOk2();
882
+ return value;
883
+ },
884
+ (err) => {
885
+ finishError2(err);
886
+ throw err;
887
+ }
888
+ );
889
+ }
890
+ finishOk2();
891
+ return result;
892
+ };
893
+
894
+ // src/otel/spans/rpc-client.ts
895
+ import { Logger as Logger3 } from "@nestjs/common";
896
+ import { SpanKind as SpanKind3, SpanStatusCode as SpanStatusCode3, context as context3, trace as trace4 } from "@opentelemetry/api";
897
+ var logger3 = new Logger3("Jetstream:Otel");
898
+ var RPC_TIMEOUT_MESSAGE = "rpc.timeout";
899
+ var beginRpcClientSpan = (ctx, config) => {
900
+ if (!config.enabled) {
901
+ return {
902
+ activeContext: context3.active(),
903
+ finish: () => void 0
904
+ };
905
+ }
906
+ if (!config.traces.has("rpc.client.send" /* RpcClientSend */)) {
907
+ injectContext(context3.active(), ctx.headers, hdrsSetter);
908
+ return {
909
+ activeContext: context3.active(),
910
+ finish: () => void 0
911
+ };
912
+ }
913
+ const tracer = getTracer();
914
+ const span = tracer.startSpan(`${SPAN_NAME_SEND} ${ctx.subject}`, {
915
+ kind: SpanKind3.CLIENT,
916
+ attributes: {
917
+ ...buildRpcClientAttributes({
918
+ subject: ctx.subject,
919
+ pattern: ctx.pattern,
920
+ correlationId: ctx.correlationId,
921
+ payloadBytes: ctx.payloadBytes,
922
+ messageId: ctx.messageId,
923
+ serviceName: ctx.serviceName,
924
+ serverAddress: ctx.endpoint?.host,
925
+ serverPort: ctx.endpoint?.port
926
+ }),
927
+ ...captureMatchingHeaders(ctx.headers, config.captureHeaders),
928
+ ...captureBodyAttribute(ctx.subject, ctx.payload, config.captureBody)
929
+ }
930
+ });
931
+ const ctxWithSpan = trace4.setSpan(context3.active(), span);
932
+ injectContext(ctxWithSpan, ctx.headers, hdrsSetter);
933
+ const start = Date.now();
934
+ let finalized = false;
935
+ const finish = (outcome) => {
936
+ if (finalized) return;
937
+ finalized = true;
938
+ let reply;
939
+ let error;
940
+ switch (outcome.kind) {
941
+ case "ok" /* Ok */:
942
+ reply = outcome.reply;
943
+ span.setStatus({ code: SpanStatusCode3.OK });
944
+ break;
945
+ case "reply-error" /* ReplyError */:
946
+ reply = outcome.replyPayload;
947
+ applyExpectedErrorAttributes(span, outcome.replyPayload);
948
+ span.setStatus({ code: SpanStatusCode3.OK });
949
+ break;
950
+ case "timeout" /* Timeout */:
951
+ error = new Error(RPC_TIMEOUT_MESSAGE);
952
+ span.addEvent(RPC_TIMEOUT_MESSAGE);
953
+ span.setStatus({ code: SpanStatusCode3.ERROR, message: RPC_TIMEOUT_MESSAGE });
954
+ break;
955
+ case "error" /* Error */:
956
+ error = outcome.error;
957
+ span.recordException(outcome.error);
958
+ span.setStatus({ code: SpanStatusCode3.ERROR, message: outcome.error.message });
959
+ break;
960
+ default: {
961
+ const unknownOutcome = outcome;
962
+ logger3.error(`Unhandled RPC outcome: ${String(unknownOutcome.kind)}`);
963
+ span.setStatus({ code: SpanStatusCode3.ERROR, message: "unknown outcome" });
964
+ }
965
+ }
966
+ context3.with(ctxWithSpan, () => {
967
+ safelyInvokeHook(HOOK_RESPONSE, config.responseHook, span, {
968
+ subject: ctx.subject,
969
+ durationMs: Date.now() - start,
970
+ reply,
971
+ error
972
+ });
973
+ });
974
+ span.end();
975
+ };
976
+ return { activeContext: ctxWithSpan, finish };
977
+ };
978
+
979
+ // src/otel/spans/dead-letter.ts
980
+ import { ROOT_CONTEXT as ROOT_CONTEXT2, SpanKind as SpanKind4, SpanStatusCode as SpanStatusCode4, context as context4, trace as trace5 } from "@opentelemetry/api";
981
+ var withDeadLetterSpan = async (ctx, config, fn) => {
982
+ if (!config.enabled || !config.traces.has("dead_letter" /* DeadLetter */)) {
983
+ return fn();
984
+ }
985
+ const parentCtx = extractContext(ROOT_CONTEXT2, ctx.msg.headers, hdrsGetter);
986
+ const tracer = getTracer();
987
+ const span = tracer.startSpan(
988
+ `${SPAN_NAME_DEAD_LETTER} ${ctx.msg.subject}`,
989
+ {
990
+ kind: SpanKind4.INTERNAL,
991
+ attributes: buildDeadLetterAttributes({
992
+ subject: ctx.msg.subject,
993
+ pattern: ctx.pattern,
994
+ serviceName: ctx.serviceName,
995
+ serverAddress: ctx.endpoint?.host,
996
+ serverPort: ctx.endpoint?.port,
997
+ finalDeliveryCount: ctx.finalDeliveryCount,
998
+ reason: ctx.reason
999
+ })
1000
+ },
1001
+ parentCtx
1002
+ );
1003
+ const ctxWithSpan = trace5.setSpan(parentCtx, span);
1004
+ const start = Date.now();
1005
+ const invokeResponseHook = (error) => {
1006
+ context4.with(ctxWithSpan, () => {
1007
+ safelyInvokeHook(HOOK_RESPONSE, config.responseHook, span, {
1008
+ subject: ctx.msg.subject,
1009
+ durationMs: Date.now() - start,
1010
+ error
1011
+ });
1012
+ });
1013
+ };
1014
+ try {
1015
+ const result = await context4.with(ctxWithSpan, fn);
1016
+ span.setStatus({ code: SpanStatusCode4.OK });
1017
+ invokeResponseHook();
1018
+ return result;
1019
+ } catch (err) {
1020
+ const error = err instanceof Error ? err : new Error(String(err));
1021
+ span.recordException(error);
1022
+ span.setStatus({ code: SpanStatusCode4.ERROR, message: error.message });
1023
+ invokeResponseHook(error);
1024
+ throw err;
1025
+ } finally {
1026
+ span.end();
1027
+ }
1028
+ };
1029
+
1030
+ // src/otel/spans/infrastructure.ts
1031
+ import { Logger as Logger4 } from "@nestjs/common";
1032
+ import {
1033
+ SpanKind as SpanKind5,
1034
+ SpanStatusCode as SpanStatusCode5,
1035
+ context as context5,
1036
+ trace as trace6
1037
+ } from "@opentelemetry/api";
1038
+ var logger4 = new Logger4("Jetstream:Otel");
1039
+ var startInfraSpan = (config, traceKind, name, ctx, extraAttributes = {}) => {
1040
+ if (!config.enabled || !config.traces.has(traceKind)) return null;
1041
+ const tracer = getTracer();
1042
+ const attributes = {
1043
+ [ATTR_JETSTREAM_SERVICE_NAME]: ctx.serviceName
1044
+ };
1045
+ if (ctx.endpoint?.host) attributes[ATTR_SERVER_ADDRESS] = ctx.endpoint.host;
1046
+ if (ctx.endpoint?.port !== void 0) attributes[ATTR_SERVER_PORT] = ctx.endpoint.port;
1047
+ for (const [key, value] of Object.entries(extraAttributes)) {
1048
+ if (value !== void 0) attributes[key] = value;
1049
+ }
1050
+ return tracer.startSpan(name, { kind: SpanKind5.INTERNAL, attributes });
1051
+ };
1052
+ var finishOk = (span) => {
1053
+ span.setStatus({ code: SpanStatusCode5.OK });
1054
+ span.end();
1055
+ };
1056
+ var finishError = (span, err) => {
1057
+ const error = err instanceof Error ? err : new Error(String(err));
1058
+ span.recordException(error);
1059
+ span.setStatus({ code: SpanStatusCode5.ERROR, message: error.message });
1060
+ span.end();
1061
+ };
1062
+ var wrapInfra = async (config, traceKind, name, ctx, attributes, op) => {
1063
+ const span = startInfraSpan(config, traceKind, name, ctx, attributes);
1064
+ if (!span) return op();
1065
+ const ctxWithSpan = trace6.setSpan(context5.active(), span);
1066
+ try {
1067
+ const result = await context5.with(ctxWithSpan, op);
1068
+ finishOk(span);
1069
+ return result;
1070
+ } catch (err) {
1071
+ finishError(span, err);
1072
+ throw err;
1073
+ }
1074
+ };
1075
+ var beginConnectionLifecycleSpan = (config, ctx) => {
1076
+ const span = startInfraSpan(
1077
+ config,
1078
+ "connection.lifecycle" /* ConnectionLifecycle */,
1079
+ SPAN_NAME_NATS_CONNECTION,
1080
+ ctx,
1081
+ { [ATTR_NATS_CONNECTION_SERVER]: ctx.server }
1082
+ );
1083
+ if (!span) {
1084
+ return {
1085
+ recordEvent: () => void 0,
1086
+ finish: () => void 0
1087
+ };
1088
+ }
1089
+ let finalized = false;
1090
+ return {
1091
+ recordEvent: (name, attributes) => {
1092
+ if (finalized) {
1093
+ logger4.debug(`recordEvent('${name}') called after connection span finished`);
1094
+ return;
1095
+ }
1096
+ span.addEvent(name, attributes);
1097
+ },
1098
+ finish: (err) => {
1099
+ if (finalized) return;
1100
+ finalized = true;
1101
+ if (err === void 0) finishOk(span);
1102
+ else finishError(span, err);
1103
+ }
1104
+ };
1105
+ };
1106
+ var withSelfHealingSpan = (config, ctx, op) => wrapInfra(
1107
+ config,
1108
+ "self_healing" /* SelfHealing */,
1109
+ SPAN_NAME_JETSTREAM_SELF_HEALING,
1110
+ ctx,
1111
+ {
1112
+ [ATTR_MESSAGING_NATS_STREAM_NAME]: ctx.stream,
1113
+ [ATTR_MESSAGING_CONSUMER_GROUP_NAME]: ctx.consumer,
1114
+ [ATTR_JETSTREAM_SELF_HEALING_REASON]: ctx.reason
1115
+ },
1116
+ op
1117
+ );
1118
+ var withProvisioningSpan = (config, ctx, op) => wrapInfra(
1119
+ config,
1120
+ "provisioning" /* Provisioning */,
1121
+ `${SPAN_NAME_JETSTREAM_PROVISIONING_PREFIX}${ctx.entity}`,
1122
+ ctx,
1123
+ {
1124
+ [ATTR_JETSTREAM_PROVISIONING_ENTITY]: ctx.entity,
1125
+ [ATTR_JETSTREAM_PROVISIONING_ACTION]: ctx.action,
1126
+ [ATTR_JETSTREAM_PROVISIONING_NAME]: ctx.name
1127
+ },
1128
+ op
1129
+ );
1130
+ var withMigrationSpan = (config, ctx, op) => wrapInfra(
1131
+ config,
1132
+ "migration" /* Migration */,
1133
+ SPAN_NAME_JETSTREAM_MIGRATION,
1134
+ ctx,
1135
+ {
1136
+ [ATTR_MESSAGING_NATS_STREAM_NAME]: ctx.stream,
1137
+ [ATTR_JETSTREAM_MIGRATION_REASON]: ctx.reason
1138
+ },
1139
+ op
1140
+ );
1141
+ var withShutdownSpan = (config, ctx, op) => wrapInfra(config, "shutdown" /* Shutdown */, SPAN_NAME_JETSTREAM_SHUTDOWN, ctx, {}, op);
1142
+
227
1143
  // src/client/jetstream.record.ts
228
1144
  var JetstreamRecord = class {
229
1145
  constructor(data, headers2, timeout, messageId, schedule, ttl) {
@@ -376,9 +1292,14 @@ var JetstreamRecordBuilder = class {
376
1292
  this.ttlDuration
377
1293
  );
378
1294
  }
379
- /** Validate that a header key is not reserved. */
1295
+ /**
1296
+ * Validate that a header key is not reserved. NATS treats header names
1297
+ * case-insensitively, so the check is against the lowercase form to keep
1298
+ * `'X-Correlation-ID'`, `'x-correlation-id'`, and any other casing in
1299
+ * lockstep. `RESERVED_HEADERS` is defined as an all-lowercase set.
1300
+ */
380
1301
  validateHeaderKey(key) {
381
- if (RESERVED_HEADERS.has(key)) {
1302
+ if (RESERVED_HEADERS.has(key.toLowerCase())) {
382
1303
  throw new Error(
383
1304
  `Header "${key}" is reserved by the JetStream transport and cannot be set manually. Reserved headers: ${[...RESERVED_HEADERS].join(", ")}`
384
1305
  );
@@ -399,6 +1320,11 @@ var nanosToGoDuration = (nanos) => {
399
1320
 
400
1321
  // src/client/jetstream.client.ts
401
1322
  var BROADCAST_SUBJECT_PREFIX = "broadcast.";
1323
+ var detectEventKind = (pattern) => {
1324
+ if (pattern.startsWith("broadcast:" /* Broadcast */)) return "broadcast" /* Broadcast */;
1325
+ if (pattern.startsWith("ordered:" /* Ordered */)) return "ordered" /* Ordered */;
1326
+ return "event" /* Event */;
1327
+ };
402
1328
  var JetstreamClient = class extends ClientProxy {
403
1329
  constructor(rootOptions, targetServiceName, connection, codec, eventBus) {
404
1330
  super();
@@ -414,8 +1340,11 @@ var JetstreamClient = class extends ClientProxy {
414
1340
  this.orderedSubjectPrefix = `${targetInternal}.${"ordered" /* Ordered */}.`;
415
1341
  this.isCoreMode = isCoreRpcMode(this.rootOptions.rpc);
416
1342
  this.defaultRpcTimeout = isJetStreamRpcMode(this.rootOptions.rpc) ? this.rootOptions.rpc?.timeout ?? DEFAULT_JETSTREAM_RPC_TIMEOUT : this.rootOptions.rpc?.timeout ?? DEFAULT_RPC_TIMEOUT;
1343
+ const derived = deriveOtelAttrs(this.rootOptions);
1344
+ this.otel = derived.otel;
1345
+ this.serverEndpoint = derived.serverEndpoint;
417
1346
  }
418
- logger = new Logger("Jetstream:Client");
1347
+ logger = new Logger5("Jetstream:Client");
419
1348
  /** Target service name this client sends messages to. */
420
1349
  targetName;
421
1350
  /** Pre-cached caller name derived from rootOptions.name, computed once in constructor. */
@@ -435,6 +1364,10 @@ var JetstreamClient = class extends ClientProxy {
435
1364
  */
436
1365
  isCoreMode;
437
1366
  defaultRpcTimeout;
1367
+ /** Resolved OpenTelemetry configuration, computed once in the constructor. */
1368
+ otel;
1369
+ /** Server endpoint parts used for `server.address` / `server.port` span attributes. */
1370
+ serverEndpoint;
438
1371
  /** Shared inbox for JetStream-mode RPC responses. */
439
1372
  inbox = null;
440
1373
  inboxSubscription = null;
@@ -502,33 +1435,55 @@ var JetstreamClient = class extends ClientProxy {
502
1435
  if (!this.readyForPublish) await this.connect();
503
1436
  const { data, hdrs, messageId, schedule, ttl } = this.extractRecordData(packet.data);
504
1437
  const eventSubject = this.buildEventSubject(packet.pattern);
1438
+ const publishSubject = schedule ? this.buildScheduleSubject(eventSubject) : eventSubject;
505
1439
  const msgHeaders = this.buildHeaders(hdrs, { subject: eventSubject });
506
- if (schedule) {
507
- const scheduleSubject = this.buildScheduleSubject(eventSubject);
508
- const ack = await this.connection.getJetStreamClient().publish(scheduleSubject, this.codec.encode(data), {
1440
+ const encoded = this.codec.encode(data);
1441
+ const effectiveMsgId = messageId ?? nuid.next();
1442
+ const record = packet.data instanceof JetstreamRecord ? packet.data : new JetstreamRecord(data, /* @__PURE__ */ new Map());
1443
+ await withPublishSpan(
1444
+ {
1445
+ subject: publishSubject,
1446
+ pattern: packet.pattern,
1447
+ record,
1448
+ kind: detectEventKind(packet.pattern),
1449
+ payloadBytes: encoded.length,
1450
+ payload: encoded,
1451
+ messageId: effectiveMsgId,
509
1452
  headers: msgHeaders,
510
- msgID: messageId ?? nuid.next(),
511
- ttl,
512
- schedule: {
513
- specification: schedule.at,
514
- target: eventSubject
1453
+ serviceName: this.callerName,
1454
+ endpoint: this.serverEndpoint,
1455
+ scheduleTarget: schedule ? eventSubject : void 0
1456
+ },
1457
+ this.otel,
1458
+ async () => {
1459
+ const warnIfDuplicate = (kindLabel, ack2) => {
1460
+ if (ack2.duplicate) {
1461
+ this.logger.warn(
1462
+ `Duplicate ${kindLabel} publish detected: ${publishSubject} (seq: ${ack2.seq})`
1463
+ );
1464
+ }
1465
+ };
1466
+ if (schedule) {
1467
+ const ack2 = await this.connection.getJetStreamClient().publish(publishSubject, encoded, {
1468
+ headers: msgHeaders,
1469
+ msgID: effectiveMsgId,
1470
+ ttl,
1471
+ schedule: {
1472
+ specification: schedule.at,
1473
+ target: eventSubject
1474
+ }
1475
+ });
1476
+ warnIfDuplicate("scheduled", ack2);
1477
+ return;
515
1478
  }
516
- });
517
- if (ack.duplicate) {
518
- this.logger.warn(
519
- `Duplicate scheduled publish detected: ${scheduleSubject} (seq: ${ack.seq})`
520
- );
521
- }
522
- } else {
523
- const ack = await this.connection.getJetStreamClient().publish(eventSubject, this.codec.encode(data), {
524
- headers: msgHeaders,
525
- msgID: messageId ?? nuid.next(),
526
- ttl
527
- });
528
- if (ack.duplicate) {
529
- this.logger.warn(`Duplicate event publish detected: ${eventSubject} (seq: ${ack.seq})`);
1479
+ const ack = await this.connection.getJetStreamClient().publish(publishSubject, encoded, {
1480
+ headers: msgHeaders,
1481
+ msgID: effectiveMsgId,
1482
+ ttl
1483
+ });
1484
+ warnIfDuplicate("event", ack);
530
1485
  }
531
- }
1486
+ );
532
1487
  return void 0;
533
1488
  }
534
1489
  /**
@@ -579,24 +1534,46 @@ var JetstreamClient = class extends ClientProxy {
579
1534
  }
580
1535
  /** Core mode: nc.request() with timeout. */
581
1536
  async publishCoreRpc(subject, data, customHeaders, timeout, callback) {
1537
+ const effectiveTimeout = timeout ?? this.defaultRpcTimeout;
1538
+ const hdrs = this.buildHeaders(customHeaders, { subject });
1539
+ const encoded = this.codec.encode(data);
1540
+ const spanHandle = beginRpcClientSpan(
1541
+ {
1542
+ subject,
1543
+ payloadBytes: encoded.length,
1544
+ payload: encoded,
1545
+ headers: hdrs,
1546
+ serviceName: this.callerName,
1547
+ endpoint: this.serverEndpoint
1548
+ },
1549
+ this.otel
1550
+ );
582
1551
  try {
583
1552
  const nc = this.readyForPublish ? this.connection.unwrap : await this.connect();
584
- const effectiveTimeout = timeout ?? this.defaultRpcTimeout;
585
- const hdrs = this.buildHeaders(customHeaders, { subject });
586
- const response = await nc.request(subject, this.codec.encode(data), {
587
- timeout: effectiveTimeout,
588
- headers: hdrs
589
- });
1553
+ const response = await context6.with(
1554
+ spanHandle.activeContext,
1555
+ () => nc.request(subject, encoded, {
1556
+ timeout: effectiveTimeout,
1557
+ headers: hdrs
1558
+ })
1559
+ );
590
1560
  const decoded = this.codec.decode(response.data);
591
1561
  if (response.headers?.get("x-error" /* Error */)) {
1562
+ spanHandle.finish({ kind: "reply-error" /* ReplyError */, replyPayload: decoded });
592
1563
  callback({ err: decoded, response: null, isDisposed: true });
593
1564
  } else {
1565
+ spanHandle.finish({ kind: "ok" /* Ok */, reply: decoded });
594
1566
  callback({ err: null, response: decoded, isDisposed: true });
595
1567
  }
596
1568
  } catch (err) {
597
1569
  const error = err instanceof Error ? err : new Error("Unknown error");
598
- this.logger.error(`Core RPC error (${subject}):`, err);
599
- this.eventBus.emit("error" /* Error */, error, "client-rpc");
1570
+ if (error instanceof TimeoutError) {
1571
+ spanHandle.finish({ kind: "timeout" /* Timeout */ });
1572
+ this.eventBus.emit("rpcTimeout" /* RpcTimeout */, subject, "");
1573
+ } else {
1574
+ spanHandle.finish({ kind: "error" /* Error */, error });
1575
+ this.eventBus.emit("error" /* Error */, error, "client-rpc");
1576
+ }
600
1577
  callback({ err: error, response: null, isDisposed: true });
601
1578
  }
602
1579
  }
@@ -604,12 +1581,55 @@ var JetstreamClient = class extends ClientProxy {
604
1581
  async publishJetStreamRpc(subject, data, callback, options) {
605
1582
  const { headers: customHeaders, correlationId, messageId } = options;
606
1583
  const effectiveTimeout = options.timeout ?? this.defaultRpcTimeout;
607
- this.pendingMessages.set(correlationId, callback);
1584
+ const hdrs = this.buildHeaders(customHeaders, {
1585
+ subject,
1586
+ correlationId,
1587
+ replyTo: this.inbox ?? ""
1588
+ });
1589
+ const encoded = this.codec.encode(data);
1590
+ const spanHandle = beginRpcClientSpan(
1591
+ {
1592
+ subject,
1593
+ correlationId,
1594
+ payloadBytes: encoded.length,
1595
+ payload: encoded,
1596
+ messageId,
1597
+ headers: hdrs,
1598
+ serviceName: this.callerName,
1599
+ endpoint: this.serverEndpoint
1600
+ },
1601
+ this.otel
1602
+ );
1603
+ this.pendingMessages.set(correlationId, (packet) => {
1604
+ if (packet.err) {
1605
+ if (packet.err instanceof Error) {
1606
+ spanHandle.finish({ kind: "error" /* Error */, error: packet.err });
1607
+ } else {
1608
+ spanHandle.finish({ kind: "reply-error" /* ReplyError */, replyPayload: packet.err });
1609
+ }
1610
+ } else {
1611
+ spanHandle.finish({ kind: "ok" /* Ok */, reply: packet.response });
1612
+ }
1613
+ callback(packet);
1614
+ });
1615
+ const timeoutId = setTimeout(() => {
1616
+ if (!this.pendingMessages.has(correlationId)) return;
1617
+ this.pendingTimeouts.delete(correlationId);
1618
+ this.pendingMessages.delete(correlationId);
1619
+ spanHandle.finish({ kind: "timeout" /* Timeout */ });
1620
+ this.eventBus.emit("rpcTimeout" /* RpcTimeout */, subject, correlationId);
1621
+ callback({ err: new Error(RPC_TIMEOUT_MESSAGE), response: null, isDisposed: true });
1622
+ }, effectiveTimeout);
1623
+ this.pendingTimeouts.set(correlationId, timeoutId);
608
1624
  try {
609
1625
  if (!this.readyForPublish) await this.connect();
610
1626
  if (!this.pendingMessages.has(correlationId)) return;
611
1627
  if (!this.inbox) {
1628
+ clearTimeout(timeoutId);
1629
+ this.pendingTimeouts.delete(correlationId);
612
1630
  this.pendingMessages.delete(correlationId);
1631
+ const inboxError = new Error("Inbox not initialized");
1632
+ spanHandle.finish({ kind: "error" /* Error */, error: inboxError });
613
1633
  callback({
614
1634
  err: new Error("Inbox not initialized \u2014 JetStream RPC mode requires a connected inbox"),
615
1635
  response: null,
@@ -617,24 +1637,13 @@ var JetstreamClient = class extends ClientProxy {
617
1637
  });
618
1638
  return;
619
1639
  }
620
- const timeoutId = setTimeout(() => {
621
- if (!this.pendingMessages.has(correlationId)) return;
622
- this.pendingTimeouts.delete(correlationId);
623
- this.pendingMessages.delete(correlationId);
624
- this.logger.error(`JetStream RPC timeout (${effectiveTimeout}ms): ${subject}`);
625
- this.eventBus.emit("rpcTimeout" /* RpcTimeout */, subject, correlationId);
626
- callback({ err: new Error("RPC timeout"), response: null, isDisposed: true });
627
- }, effectiveTimeout);
628
- this.pendingTimeouts.set(correlationId, timeoutId);
629
- const hdrs = this.buildHeaders(customHeaders, {
630
- subject,
631
- correlationId,
632
- replyTo: this.inbox
633
- });
634
- await this.connection.getJetStreamClient().publish(subject, this.codec.encode(data), {
635
- headers: hdrs,
636
- msgID: messageId ?? nuid.next()
637
- });
1640
+ await context6.with(
1641
+ spanHandle.activeContext,
1642
+ () => this.connection.getJetStreamClient().publish(subject, encoded, {
1643
+ headers: hdrs,
1644
+ msgID: messageId ?? nuid.next()
1645
+ })
1646
+ );
638
1647
  } catch (err) {
639
1648
  const existingTimeout = this.pendingTimeouts.get(correlationId);
640
1649
  if (existingTimeout) {
@@ -644,7 +1653,8 @@ var JetstreamClient = class extends ClientProxy {
644
1653
  if (!this.pendingMessages.has(correlationId)) return;
645
1654
  this.pendingMessages.delete(correlationId);
646
1655
  const error = err instanceof Error ? err : new Error("Unknown error");
647
- this.logger.error(`JetStream RPC publish error (${subject}):`, err);
1656
+ spanHandle.finish({ kind: "error" /* Error */, error });
1657
+ this.eventBus.emit("error" /* Error */, error, `jetstream-rpc-publish:${subject}`);
648
1658
  callback({ err: error, response: null, isDisposed: true });
649
1659
  }
650
1660
  }
@@ -827,7 +1837,7 @@ var MsgpackCodec = class {
827
1837
  };
828
1838
 
829
1839
  // src/connection/connection.provider.ts
830
- import { Logger as Logger2 } from "@nestjs/common";
1840
+ import { Logger as Logger6 } from "@nestjs/common";
831
1841
  import {
832
1842
  connect
833
1843
  } from "@nats-io/transport-node";
@@ -844,6 +1854,10 @@ var ConnectionProvider = class {
844
1854
  constructor(options, eventBus) {
845
1855
  this.options = options;
846
1856
  this.eventBus = eventBus;
1857
+ const derived = deriveOtelAttrs(options);
1858
+ this.otel = derived.otel;
1859
+ this.otelServiceName = derived.serviceName;
1860
+ this.otelEndpoint = derived.serverEndpoint;
847
1861
  this.nc$ = defer(() => this.getConnection()).pipe(
848
1862
  shareReplay({ bufferSize: 1, refCount: false })
849
1863
  );
@@ -856,12 +1870,16 @@ var ConnectionProvider = class {
856
1870
  nc$;
857
1871
  /** Live stream of connection status events (no replay). */
858
1872
  status$;
859
- logger = new Logger2("Jetstream:Connection");
1873
+ logger = new Logger6("Jetstream:Connection");
860
1874
  connection = null;
861
1875
  connectionPromise = null;
862
1876
  jsClient = null;
863
1877
  jsmInstance = null;
864
1878
  jsmPromise = null;
1879
+ otel;
1880
+ otelServiceName;
1881
+ otelEndpoint;
1882
+ lifecycleSpan = null;
865
1883
  /**
866
1884
  * Establish NATS connection. Idempotent — returns cached connection on subsequent calls.
867
1885
  *
@@ -925,14 +1943,24 @@ var ConnectionProvider = class {
925
1943
  }
926
1944
  if (!this.connection || this.connection.isClosed()) return;
927
1945
  try {
928
- await this.connection.drain();
929
- await this.connection.closed();
930
- } catch {
931
- try {
932
- await this.connection.close();
933
- } catch {
934
- }
1946
+ await withShutdownSpan(
1947
+ this.otel,
1948
+ { serviceName: this.otelServiceName, endpoint: this.otelEndpoint },
1949
+ async () => {
1950
+ try {
1951
+ await this.connection?.drain();
1952
+ await this.connection?.closed();
1953
+ } catch {
1954
+ try {
1955
+ await this.connection?.close();
1956
+ } catch {
1957
+ }
1958
+ }
1959
+ }
1960
+ );
935
1961
  } finally {
1962
+ this.lifecycleSpan?.finish();
1963
+ this.lifecycleSpan = null;
936
1964
  this.connection = null;
937
1965
  this.connectionPromise = null;
938
1966
  this.jsClient = null;
@@ -952,17 +1980,25 @@ var ConnectionProvider = class {
952
1980
  }
953
1981
  /** Internal: establish the physical connection with reconnect monitoring. */
954
1982
  async establish() {
955
- const name = internalName(this.options.name);
956
1983
  try {
957
1984
  const nc = await connect({
958
1985
  ...DEFAULT_OPTIONS,
1986
+ // Default the NATS connection name to the OTel-derived service name so
1987
+ // `nats server info` lines up with span attributes, but let user-supplied
1988
+ // `connectionOptions.name` win when set.
1989
+ name: this.otelServiceName,
959
1990
  ...this.options.connectionOptions,
960
- servers: this.options.servers,
961
- name
1991
+ servers: this.options.servers
962
1992
  });
963
1993
  this.connection = nc;
964
1994
  this.logger.log(`NATS connection established: ${nc.getServer()}`);
965
1995
  this.eventBus.emit("connect" /* Connect */, nc.getServer());
1996
+ this.lifecycleSpan?.finish();
1997
+ this.lifecycleSpan = beginConnectionLifecycleSpan(this.otel, {
1998
+ serviceName: this.otelServiceName,
1999
+ endpoint: this.otelEndpoint,
2000
+ server: nc.getServer()
2001
+ });
966
2002
  this.monitorStatus(nc);
967
2003
  return nc;
968
2004
  } catch (err) {
@@ -972,37 +2008,55 @@ var ConnectionProvider = class {
972
2008
  throw err;
973
2009
  }
974
2010
  }
2011
+ /** Handle a single `nc.status()` event, emitting hooks and span events. */
2012
+ handleStatusEvent(status, nc) {
2013
+ switch (status.type) {
2014
+ case "disconnect":
2015
+ this.eventBus.emit("disconnect" /* Disconnect */);
2016
+ this.lifecycleSpan?.recordEvent(EVENT_CONNECTION_DISCONNECTED);
2017
+ break;
2018
+ case "reconnect":
2019
+ this.jsClient = null;
2020
+ this.jsmInstance = null;
2021
+ this.jsmPromise = null;
2022
+ this.eventBus.emit("reconnect" /* Reconnect */, nc.getServer());
2023
+ this.lifecycleSpan?.recordEvent(EVENT_CONNECTION_RECONNECTED, {
2024
+ [ATTR_NATS_CONNECTION_SERVER]: nc.getServer()
2025
+ });
2026
+ break;
2027
+ case "error":
2028
+ this.eventBus.emit(
2029
+ "error" /* Error */,
2030
+ status.error,
2031
+ "connection"
2032
+ );
2033
+ break;
2034
+ case "update":
2035
+ case "ldm":
2036
+ case "reconnecting":
2037
+ case "ping":
2038
+ case "staleConnection":
2039
+ case "forceReconnect":
2040
+ case "slowConsumer":
2041
+ case "close":
2042
+ break;
2043
+ default: {
2044
+ const _exhaustive = status;
2045
+ const unknown = _exhaustive.type ?? "unknown";
2046
+ this.logger.warn(`Unhandled NATS status event: ${unknown}`);
2047
+ }
2048
+ }
2049
+ }
975
2050
  /** Subscribe to connection status events and emit hooks. */
976
2051
  monitorStatus(nc) {
977
2052
  void (async () => {
978
- for await (const status of nc.status()) {
979
- switch (status.type) {
980
- case "disconnect":
981
- this.eventBus.emit("disconnect" /* Disconnect */);
982
- break;
983
- case "reconnect":
984
- this.jsClient = null;
985
- this.jsmInstance = null;
986
- this.jsmPromise = null;
987
- this.eventBus.emit("reconnect" /* Reconnect */, nc.getServer());
988
- break;
989
- case "error":
990
- this.eventBus.emit(
991
- "error" /* Error */,
992
- status.error,
993
- "connection"
994
- );
995
- break;
996
- case "update":
997
- case "ldm":
998
- case "reconnecting":
999
- case "ping":
1000
- case "staleConnection":
1001
- case "forceReconnect":
1002
- case "slowConsumer":
1003
- case "close":
1004
- break;
2053
+ try {
2054
+ for await (const status of nc.status()) {
2055
+ this.handleStatusEvent(status, nc);
1005
2056
  }
2057
+ } finally {
2058
+ this.lifecycleSpan?.finish();
2059
+ this.lifecycleSpan = null;
1006
2060
  }
1007
2061
  })().catch((err) => {
1008
2062
  this.logger.error("Status monitor error", err);
@@ -1014,8 +2068,8 @@ var ConnectionProvider = class {
1014
2068
  var EventBus = class {
1015
2069
  hooks;
1016
2070
  logger;
1017
- constructor(logger, hooks) {
1018
- this.logger = logger;
2071
+ constructor(logger5, hooks) {
2072
+ this.logger = logger5;
1019
2073
  this.hooks = hooks ?? {};
1020
2074
  }
1021
2075
  /**
@@ -1071,12 +2125,12 @@ var EventBus = class {
1071
2125
  };
1072
2126
 
1073
2127
  // src/health/jetstream.health-indicator.ts
1074
- import { Injectable, Logger as Logger3 } from "@nestjs/common";
2128
+ import { Injectable, Logger as Logger7 } from "@nestjs/common";
1075
2129
  var JetstreamHealthIndicator = class {
1076
2130
  constructor(connection) {
1077
2131
  this.connection = connection;
1078
2132
  }
1079
- logger = new Logger3("Jetstream:Health");
2133
+ logger = new Logger7("Jetstream:Health");
1080
2134
  /**
1081
2135
  * Plain health status check.
1082
2136
  *
@@ -1282,7 +2336,7 @@ var JetstreamStrategy = class extends Server {
1282
2336
  };
1283
2337
 
1284
2338
  // src/server/core-rpc.server.ts
1285
- import { Logger as Logger4 } from "@nestjs/common";
2339
+ import { Logger as Logger8 } from "@nestjs/common";
1286
2340
  import { headers as natsHeaders2 } from "@nats-io/transport-node";
1287
2341
 
1288
2342
  // src/context/rpc.context.ts
@@ -1292,9 +2346,6 @@ var RpcContext = class extends BaseRpcContext {
1292
2346
  _retryDelay;
1293
2347
  _shouldTerminate = false;
1294
2348
  _terminateReason;
1295
- // ---------------------------------------------------------------------------
1296
- // Message accessors
1297
- // ---------------------------------------------------------------------------
1298
2349
  /**
1299
2350
  * Get the underlying NATS message.
1300
2351
  *
@@ -1329,9 +2380,6 @@ var RpcContext = class extends BaseRpcContext {
1329
2380
  isJetStream() {
1330
2381
  return "ack" in this.args[0];
1331
2382
  }
1332
- // ---------------------------------------------------------------------------
1333
- // JetStream metadata (return undefined for Core NATS messages)
1334
- // ---------------------------------------------------------------------------
1335
2383
  /** How many times this message has been delivered. */
1336
2384
  getDeliveryCount() {
1337
2385
  return this.asJetStream()?.info.deliveryCount;
@@ -1353,9 +2401,6 @@ var RpcContext = class extends BaseRpcContext {
1353
2401
  getCallerName() {
1354
2402
  return this.getHeader("x-caller-name" /* CallerName */);
1355
2403
  }
1356
- // ---------------------------------------------------------------------------
1357
- // Handler-controlled settlement
1358
- // ---------------------------------------------------------------------------
1359
2404
  /**
1360
2405
  * Signal the transport to retry (nak) this message instead of acknowledging it.
1361
2406
  *
@@ -1400,9 +2445,6 @@ var RpcContext = class extends BaseRpcContext {
1400
2445
  throw new Error(`${method}() is only available for JetStream messages`);
1401
2446
  }
1402
2447
  }
1403
- // ---------------------------------------------------------------------------
1404
- // Transport-facing state (read by EventRouter)
1405
- // ---------------------------------------------------------------------------
1406
2448
  /** @internal */
1407
2449
  get shouldRetry() {
1408
2450
  return this._shouldRetry;
@@ -1524,7 +2566,7 @@ var unwrapResult = (result) => {
1524
2566
  }
1525
2567
  return result;
1526
2568
  };
1527
- var isPromiseLike = (value) => value !== null && typeof value === "object" && typeof value.then === "function";
2569
+ var isPromiseLike2 = (value) => value !== null && typeof value === "object" && typeof value.then === "function";
1528
2570
  var subscribeToFirst = (obs) => new Promise((resolve, reject) => {
1529
2571
  let done = false;
1530
2572
  let subscription = null;
@@ -1554,20 +2596,25 @@ var subscribeToFirst = (obs) => new Promise((resolve, reject) => {
1554
2596
  // src/server/core-rpc.server.ts
1555
2597
  var CoreRpcServer = class {
1556
2598
  constructor(options, connection, patternRegistry, codec, eventBus) {
1557
- this.options = options;
1558
2599
  this.connection = connection;
1559
2600
  this.patternRegistry = patternRegistry;
1560
2601
  this.codec = codec;
1561
2602
  this.eventBus = eventBus;
2603
+ const derived = deriveOtelAttrs(options);
2604
+ this.otel = derived.otel;
2605
+ this.serviceName = derived.serviceName;
2606
+ this.serverEndpoint = derived.serverEndpoint;
1562
2607
  }
1563
- logger = new Logger4("Jetstream:CoreRpc");
2608
+ logger = new Logger8("Jetstream:CoreRpc");
1564
2609
  subscription = null;
2610
+ otel;
2611
+ serviceName;
2612
+ serverEndpoint;
1565
2613
  /** Start listening for RPC requests on the command subject. */
1566
2614
  async start() {
1567
2615
  const nc = await this.connection.getConnection();
1568
- const serviceName = internalName(this.options.name);
1569
- const subject = `${serviceName}.cmd.>`;
1570
- const queue = `${serviceName}_cmd_queue`;
2616
+ const subject = `${this.serviceName}.cmd.>`;
2617
+ const queue = `${this.serviceName}_cmd_queue`;
1571
2618
  this.subscription = nc.subscribe(subject, {
1572
2619
  queue,
1573
2620
  callback: (err, msg) => {
@@ -1612,11 +2659,29 @@ var CoreRpcServer = class {
1612
2659
  }
1613
2660
  const ctx = new RpcContext([msg]);
1614
2661
  try {
1615
- const raw = unwrapResult(handler(data, ctx));
1616
- const result = isPromiseLike(raw) ? await raw : raw;
1617
- msg.respond(this.codec.encode(result));
2662
+ const raw = await withConsumeSpan(
2663
+ {
2664
+ subject: msg.subject,
2665
+ msg,
2666
+ kind: "rpc" /* Rpc */,
2667
+ payloadBytes: msg.data.length,
2668
+ handlerMetadata: { pattern: msg.subject },
2669
+ serviceName: this.serviceName,
2670
+ endpoint: this.serverEndpoint
2671
+ },
2672
+ this.otel,
2673
+ () => {
2674
+ const out = unwrapResult(handler(data, ctx));
2675
+ return isPromiseLike2(out) ? out : out;
2676
+ }
2677
+ );
2678
+ msg.respond(this.codec.encode(raw));
1618
2679
  } catch (err) {
1619
- this.logger.error(`Handler error for Core RPC ${msg.subject}:`, err);
2680
+ this.eventBus.emit(
2681
+ "error" /* Error */,
2682
+ err instanceof Error ? err : new Error(String(err)),
2683
+ `core-rpc-handler:${msg.subject}`
2684
+ );
1620
2685
  this.respondWithError(msg, err);
1621
2686
  }
1622
2687
  }
@@ -1633,7 +2698,7 @@ var CoreRpcServer = class {
1633
2698
  };
1634
2699
 
1635
2700
  // src/server/infrastructure/stream.provider.ts
1636
- import { Logger as Logger6 } from "@nestjs/common";
2701
+ import { Logger as Logger10 } from "@nestjs/common";
1637
2702
  import { JetStreamApiError as JetStreamApiError2 } from "@nats-io/jetstream";
1638
2703
 
1639
2704
  // src/server/infrastructure/nats-error-codes.ts
@@ -1700,7 +2765,7 @@ var isEqual = (a, b) => {
1700
2765
  };
1701
2766
 
1702
2767
  // src/server/infrastructure/stream-migration.ts
1703
- import { Logger as Logger5 } from "@nestjs/common";
2768
+ import { Logger as Logger9 } from "@nestjs/common";
1704
2769
  import { JetStreamApiError } from "@nats-io/jetstream";
1705
2770
  var MIGRATION_BACKUP_SUFFIX = "__migration_backup";
1706
2771
  var DEFAULT_SOURCING_TIMEOUT_MS = 3e4;
@@ -1709,7 +2774,7 @@ var StreamMigration = class {
1709
2774
  constructor(sourcingTimeoutMs = DEFAULT_SOURCING_TIMEOUT_MS) {
1710
2775
  this.sourcingTimeoutMs = sourcingTimeoutMs;
1711
2776
  }
1712
- logger = new Logger5("Jetstream:Stream");
2777
+ logger = new Logger9("Jetstream:Stream");
1713
2778
  async migrate(jsm, streamName2, newConfig) {
1714
2779
  const backupName = `${streamName2}${MIGRATION_BACKUP_SUFFIX}`;
1715
2780
  const startTime = Date.now();
@@ -1791,9 +2856,16 @@ var StreamProvider = class {
1791
2856
  constructor(options, connection) {
1792
2857
  this.options = options;
1793
2858
  this.connection = connection;
2859
+ const derived = deriveOtelAttrs(options);
2860
+ this.otel = derived.otel;
2861
+ this.otelServiceName = derived.serviceName;
2862
+ this.otelEndpoint = derived.serverEndpoint;
1794
2863
  }
1795
- logger = new Logger6("Jetstream:Stream");
2864
+ logger = new Logger10("Jetstream:Stream");
1796
2865
  migration = new StreamMigration();
2866
+ otel;
2867
+ otelServiceName;
2868
+ otelEndpoint;
1797
2869
  /**
1798
2870
  * Ensure all required streams exist with correct configuration.
1799
2871
  *
@@ -1839,32 +2911,56 @@ var StreamProvider = class {
1839
2911
  /** Ensure a single stream exists, creating or updating as needed. */
1840
2912
  async ensureStream(jsm, kind) {
1841
2913
  const config = this.buildConfig(kind);
1842
- this.logger.log(`Ensuring stream: ${config.name}`);
1843
- try {
1844
- const currentInfo = await jsm.streams.info(config.name);
1845
- return await this.handleExistingStream(jsm, currentInfo, config);
1846
- } catch (err) {
1847
- if (err instanceof JetStreamApiError2 && err.apiError().err_code === 10059 /* StreamNotFound */) {
1848
- this.logger.log(`Creating stream: ${config.name}`);
1849
- return await jsm.streams.add(config);
2914
+ return withProvisioningSpan(
2915
+ this.otel,
2916
+ {
2917
+ serviceName: this.otelServiceName,
2918
+ endpoint: this.otelEndpoint,
2919
+ entity: "stream",
2920
+ name: config.name,
2921
+ action: "ensure"
2922
+ },
2923
+ async () => {
2924
+ this.logger.log(`Ensuring stream: ${config.name}`);
2925
+ try {
2926
+ const currentInfo = await jsm.streams.info(config.name);
2927
+ return await this.handleExistingStream(jsm, currentInfo, config);
2928
+ } catch (err) {
2929
+ if (err instanceof JetStreamApiError2 && err.apiError().err_code === 10059 /* StreamNotFound */) {
2930
+ this.logger.log(`Creating stream: ${config.name}`);
2931
+ return await jsm.streams.add(config);
2932
+ }
2933
+ throw err;
2934
+ }
1850
2935
  }
1851
- throw err;
1852
- }
2936
+ );
1853
2937
  }
1854
2938
  /** Ensure a dead-letter queue stream exists, creating or updating as needed. */
1855
2939
  async ensureDlqStream(jsm) {
1856
2940
  const config = this.buildDlqConfig();
1857
- this.logger.log(`Ensuring DLQ stream: ${config.name}`);
1858
- try {
1859
- const currentInfo = await jsm.streams.info(config.name);
1860
- return await this.handleExistingStream(jsm, currentInfo, config);
1861
- } catch (err) {
1862
- if (err instanceof JetStreamApiError2 && err.apiError().err_code === 10059 /* StreamNotFound */) {
1863
- this.logger.log(`Creating DLQ stream: ${config.name}`);
1864
- return await jsm.streams.add(config);
2941
+ return withProvisioningSpan(
2942
+ this.otel,
2943
+ {
2944
+ serviceName: this.otelServiceName,
2945
+ endpoint: this.otelEndpoint,
2946
+ entity: "stream",
2947
+ name: config.name,
2948
+ action: "ensure"
2949
+ },
2950
+ async () => {
2951
+ this.logger.log(`Ensuring DLQ stream: ${config.name}`);
2952
+ try {
2953
+ const currentInfo = await jsm.streams.info(config.name);
2954
+ return await this.handleExistingStream(jsm, currentInfo, config);
2955
+ } catch (err) {
2956
+ if (err instanceof JetStreamApiError2 && err.apiError().err_code === 10059 /* StreamNotFound */) {
2957
+ this.logger.log(`Creating DLQ stream: ${config.name}`);
2958
+ return await jsm.streams.add(config);
2959
+ }
2960
+ throw err;
2961
+ }
1865
2962
  }
1866
- throw err;
1867
- }
2963
+ );
1868
2964
  }
1869
2965
  async handleExistingStream(jsm, currentInfo, config) {
1870
2966
  const diff = compareStreamConfig(currentInfo.config, config);
@@ -1893,7 +2989,18 @@ var StreamProvider = class {
1893
2989
  }
1894
2990
  return currentInfo;
1895
2991
  }
1896
- await this.migration.migrate(jsm, config.name, config);
2992
+ await withMigrationSpan(
2993
+ this.otel,
2994
+ {
2995
+ serviceName: this.otelServiceName,
2996
+ endpoint: this.otelEndpoint,
2997
+ stream: config.name,
2998
+ reason: diff.changes.filter((c) => c.mutability === "immutable").map((c) => c.property).join(", ")
2999
+ },
3000
+ async () => {
3001
+ await this.migration.migrate(jsm, config.name, config);
3002
+ }
3003
+ );
1897
3004
  return await jsm.streams.info(config.name);
1898
3005
  }
1899
3006
  buildMutableOnlyConfig(config, currentConfig, diff) {
@@ -2009,7 +3116,7 @@ var StreamProvider = class {
2009
3116
  };
2010
3117
 
2011
3118
  // src/server/infrastructure/consumer.provider.ts
2012
- import { Logger as Logger7 } from "@nestjs/common";
3119
+ import { Logger as Logger11 } from "@nestjs/common";
2013
3120
  import { JetStreamApiError as JetStreamApiError3 } from "@nats-io/jetstream";
2014
3121
  var ConsumerProvider = class {
2015
3122
  constructor(options, connection, streamProvider, patternRegistry) {
@@ -2017,8 +3124,15 @@ var ConsumerProvider = class {
2017
3124
  this.connection = connection;
2018
3125
  this.streamProvider = streamProvider;
2019
3126
  this.patternRegistry = patternRegistry;
2020
- }
2021
- logger = new Logger7("Jetstream:Consumer");
3127
+ const derived = deriveOtelAttrs(options);
3128
+ this.otel = derived.otel;
3129
+ this.otelServiceName = derived.serviceName;
3130
+ this.otelEndpoint = derived.serverEndpoint;
3131
+ }
3132
+ logger = new Logger11("Jetstream:Consumer");
3133
+ otel;
3134
+ otelServiceName;
3135
+ otelEndpoint;
2022
3136
  /**
2023
3137
  * Ensure consumers exist for the specified kinds.
2024
3138
  *
@@ -2048,17 +3162,29 @@ var ConsumerProvider = class {
2048
3162
  const stream = this.streamProvider.getStreamName(kind);
2049
3163
  const config = this.buildConfig(kind);
2050
3164
  const name = config.durable_name;
2051
- this.logger.log(`Ensuring consumer: ${name} on stream: ${stream}`);
2052
- try {
2053
- await jsm.consumers.info(stream, name);
2054
- this.logger.debug(`Consumer exists, updating: ${name}`);
2055
- return await jsm.consumers.update(stream, name, config);
2056
- } catch (err) {
2057
- if (!(err instanceof JetStreamApiError3) || err.apiError().err_code !== 10014 /* ConsumerNotFound */) {
2058
- throw err;
3165
+ return withProvisioningSpan(
3166
+ this.otel,
3167
+ {
3168
+ serviceName: this.otelServiceName,
3169
+ endpoint: this.otelEndpoint,
3170
+ entity: "consumer",
3171
+ name,
3172
+ action: "ensure"
3173
+ },
3174
+ async () => {
3175
+ this.logger.log(`Ensuring consumer: ${name} on stream: ${stream}`);
3176
+ try {
3177
+ await jsm.consumers.info(stream, name);
3178
+ this.logger.debug(`Consumer exists, updating: ${name}`);
3179
+ return await jsm.consumers.update(stream, name, config);
3180
+ } catch (err) {
3181
+ if (!(err instanceof JetStreamApiError3) || err.apiError().err_code !== 10014 /* ConsumerNotFound */) {
3182
+ throw err;
3183
+ }
3184
+ return await this.createConsumer(jsm, stream, name, config);
3185
+ }
2059
3186
  }
2060
- return await this.createConsumer(jsm, stream, name, config);
2061
- }
3187
+ );
2062
3188
  }
2063
3189
  /**
2064
3190
  * Recover a consumer that disappeared during runtime.
@@ -2077,16 +3203,28 @@ var ConsumerProvider = class {
2077
3203
  const stream = this.streamProvider.getStreamName(kind);
2078
3204
  const config = this.buildConfig(kind);
2079
3205
  const name = config.durable_name;
2080
- this.logger.log(`Recovering consumer: ${name} on stream: ${stream}`);
2081
- await this.assertNoMigrationInProgress(jsm, stream);
2082
- try {
2083
- return await jsm.consumers.info(stream, name);
2084
- } catch (err) {
2085
- if (!(err instanceof JetStreamApiError3) || err.apiError().err_code !== 10014 /* ConsumerNotFound */) {
2086
- throw err;
3206
+ return withProvisioningSpan(
3207
+ this.otel,
3208
+ {
3209
+ serviceName: this.otelServiceName,
3210
+ endpoint: this.otelEndpoint,
3211
+ entity: "consumer",
3212
+ name,
3213
+ action: "recover"
3214
+ },
3215
+ async () => {
3216
+ this.logger.log(`Recovering consumer: ${name} on stream: ${stream}`);
3217
+ await this.assertNoMigrationInProgress(jsm, stream);
3218
+ try {
3219
+ return await jsm.consumers.info(stream, name);
3220
+ } catch (err) {
3221
+ if (!(err instanceof JetStreamApiError3) || err.apiError().err_code !== 10014 /* ConsumerNotFound */) {
3222
+ throw err;
3223
+ }
3224
+ return await this.createConsumer(jsm, stream, name, config);
3225
+ }
2087
3226
  }
2088
- return await this.createConsumer(jsm, stream, name, config);
2089
- }
3227
+ );
2090
3228
  }
2091
3229
  /**
2092
3230
  * Throw if a migration backup stream exists for this stream.
@@ -2202,7 +3340,7 @@ var ConsumerProvider = class {
2202
3340
  };
2203
3341
 
2204
3342
  // src/server/infrastructure/message.provider.ts
2205
- import { Logger as Logger8 } from "@nestjs/common";
3343
+ import { Logger as Logger12 } from "@nestjs/common";
2206
3344
  import { DeliverPolicy as DeliverPolicy2 } from "@nats-io/jetstream";
2207
3345
  import {
2208
3346
  catchError,
@@ -2222,7 +3360,7 @@ var MessageProvider = class {
2222
3360
  this.consumeOptionsMap = consumeOptionsMap;
2223
3361
  this.consumerRecoveryFn = consumerRecoveryFn;
2224
3362
  }
2225
- logger = new Logger8("Jetstream:Message");
3363
+ logger = new Logger12("Jetstream:Message");
2226
3364
  activeIterators = /* @__PURE__ */ new Set();
2227
3365
  orderedReadyResolve = null;
2228
3366
  orderedReadyReject = null;
@@ -2469,7 +3607,7 @@ var MessageProvider = class {
2469
3607
  };
2470
3608
 
2471
3609
  // src/server/infrastructure/metadata.provider.ts
2472
- import { Logger as Logger9 } from "@nestjs/common";
3610
+ import { Logger as Logger13 } from "@nestjs/common";
2473
3611
  import { Kvm } from "@nats-io/kv";
2474
3612
  var MetadataProvider = class {
2475
3613
  constructor(options, connection) {
@@ -2478,7 +3616,7 @@ var MetadataProvider = class {
2478
3616
  this.replicas = options.metadata?.replicas ?? DEFAULT_METADATA_REPLICAS;
2479
3617
  this.ttl = Math.max(options.metadata?.ttl ?? DEFAULT_METADATA_TTL, MIN_METADATA_TTL);
2480
3618
  }
2481
- logger = new Logger9("Jetstream:Metadata");
3619
+ logger = new Logger13("Jetstream:Metadata");
2482
3620
  bucketName;
2483
3621
  replicas;
2484
3622
  ttl;
@@ -2571,7 +3709,7 @@ var MetadataProvider = class {
2571
3709
  };
2572
3710
 
2573
3711
  // src/server/routing/pattern-registry.ts
2574
- import { Logger as Logger10 } from "@nestjs/common";
3712
+ import { Logger as Logger14 } from "@nestjs/common";
2575
3713
  var HANDLER_LABELS = {
2576
3714
  ["broadcast" /* Broadcast */]: "broadcast" /* Broadcast */,
2577
3715
  ["ordered" /* Ordered */]: "ordered" /* Ordered */,
@@ -2582,7 +3720,7 @@ var PatternRegistry = class {
2582
3720
  constructor(options) {
2583
3721
  this.options = options;
2584
3722
  }
2585
- logger = new Logger10("Jetstream:PatternRegistry");
3723
+ logger = new Logger14("Jetstream:PatternRegistry");
2586
3724
  registry = /* @__PURE__ */ new Map();
2587
3725
  // Cached after registerHandlers() — the registry is immutable from that point
2588
3726
  cachedPatterns = null;
@@ -2739,8 +3877,13 @@ var PatternRegistry = class {
2739
3877
  };
2740
3878
 
2741
3879
  // src/server/routing/event.router.ts
2742
- import { Logger as Logger11 } from "@nestjs/common";
3880
+ import { Logger as Logger15 } from "@nestjs/common";
2743
3881
  import { headers as natsHeaders3 } from "@nats-io/transport-node";
3882
+ var eventConsumeKindFor = (kind) => {
3883
+ if (kind === "broadcast" /* Broadcast */) return "broadcast" /* Broadcast */;
3884
+ if (kind === "ordered" /* Ordered */) return "ordered" /* Ordered */;
3885
+ return "event" /* Event */;
3886
+ };
2744
3887
  var EventRouter = class {
2745
3888
  constructor(messageProvider, patternRegistry, codec, eventBus, deadLetterConfig, processingConfig, ackWaitMap, connection, options) {
2746
3889
  this.messageProvider = messageProvider;
@@ -2752,9 +3895,22 @@ var EventRouter = class {
2752
3895
  this.ackWaitMap = ackWaitMap;
2753
3896
  this.connection = connection;
2754
3897
  this.options = options;
3898
+ if (options) {
3899
+ const derived = deriveOtelAttrs(options);
3900
+ this.otel = derived.otel;
3901
+ this.serviceName = derived.serviceName;
3902
+ this.serverEndpoint = derived.serverEndpoint;
3903
+ } else {
3904
+ this.otel = resolveOtelOptions({ enabled: false });
3905
+ this.serviceName = "";
3906
+ this.serverEndpoint = null;
3907
+ }
2755
3908
  }
2756
- logger = new Logger11("Jetstream:EventRouter");
3909
+ logger = new Logger15("Jetstream:EventRouter");
2757
3910
  subscriptions = [];
3911
+ otel;
3912
+ serviceName;
3913
+ serverEndpoint;
2758
3914
  /**
2759
3915
  * Update the max_deliver thresholds from actual NATS consumer configs.
2760
3916
  * Called after consumers are ensured so the DLQ map reflects reality.
@@ -2784,8 +3940,12 @@ var EventRouter = class {
2784
3940
  const patternRegistry = this.patternRegistry;
2785
3941
  const codec = this.codec;
2786
3942
  const eventBus = this.eventBus;
2787
- const logger = this.logger;
3943
+ const logger5 = this.logger;
2788
3944
  const deadLetterConfig = this.deadLetterConfig;
3945
+ const otel = this.otel;
3946
+ const serviceName = this.serviceName;
3947
+ const serverEndpoint = this.serverEndpoint;
3948
+ const spanKind = eventConsumeKindFor(kind);
2789
3949
  const ackExtensionInterval = isOrdered ? null : resolveAckExtensionInterval(this.getAckExtensionConfig(kind), this.ackWaitMap?.get(kind));
2790
3950
  const hasAckExtension = ackExtensionInterval !== null && ackExtensionInterval > 0;
2791
3951
  const concurrency = this.getConcurrency(kind);
@@ -2816,7 +3976,7 @@ var EventRouter = class {
2816
3976
  const handler = patternRegistry.getHandler(subject);
2817
3977
  if (!handler) {
2818
3978
  msg.term(`No handler for event: ${subject}`);
2819
- logger.error(`No handler for subject: ${subject}`);
3979
+ logger5.error(`No handler for subject: ${subject}`);
2820
3980
  return null;
2821
3981
  }
2822
3982
  let data;
@@ -2824,17 +3984,17 @@ var EventRouter = class {
2824
3984
  data = codec.decode(msg.data);
2825
3985
  } catch (err) {
2826
3986
  msg.term("Decode error");
2827
- logger.error(`Decode error for ${subject}:`, err);
3987
+ logger5.error(`Decode error for ${subject}:`, err);
2828
3988
  return null;
2829
3989
  }
2830
3990
  if (emitRouted) eventBus.emitMessageRouted(subject, "event" /* Event */);
2831
3991
  return { handler, data };
2832
3992
  } catch (err) {
2833
- logger.error(`Unexpected error in ${kind} event router`, err);
3993
+ logger5.error(`Unexpected error in ${kind} event router`, err);
2834
3994
  try {
2835
3995
  msg.term("Unexpected router error");
2836
3996
  } catch (termErr) {
2837
- logger.error(`Failed to terminate message ${subject}:`, termErr);
3997
+ logger5.error(`Failed to terminate message ${subject}:`, termErr);
2838
3998
  }
2839
3999
  return null;
2840
4000
  }
@@ -2847,13 +4007,31 @@ var EventRouter = class {
2847
4007
  const stopAckExtension = hasAckExtension ? startAckExtensionTimer(msg, ackExtensionInterval) : null;
2848
4008
  let pending;
2849
4009
  try {
2850
- pending = unwrapResult(handler(data, ctx));
4010
+ pending = withConsumeSpan(
4011
+ {
4012
+ subject: msg.subject,
4013
+ msg,
4014
+ info: msg.info,
4015
+ kind: spanKind,
4016
+ payloadBytes: msg.data.length,
4017
+ handlerMetadata: { pattern: msg.subject },
4018
+ serviceName,
4019
+ endpoint: serverEndpoint
4020
+ },
4021
+ otel,
4022
+ () => unwrapResult(handler(data, ctx))
4023
+ );
2851
4024
  } catch (err) {
2852
- logger.error(`Event handler error (${msg.subject}) in ${kind} router:`, err);
2853
- if (stopAckExtension !== null) stopAckExtension();
2854
- return settleFailure(msg, data, err);
4025
+ eventBus.emit(
4026
+ "error" /* Error */,
4027
+ err instanceof Error ? err : new Error(String(err)),
4028
+ `${kind}-handler:${msg.subject}`
4029
+ );
4030
+ return settleFailure(msg, data, err).finally(() => {
4031
+ if (stopAckExtension !== null) stopAckExtension();
4032
+ });
2855
4033
  }
2856
- if (!isPromiseLike(pending)) {
4034
+ if (!isPromiseLike2(pending)) {
2857
4035
  settleSuccess(msg, ctx);
2858
4036
  if (stopAckExtension !== null) stopAckExtension();
2859
4037
  return void 0;
@@ -2864,7 +4042,11 @@ var EventRouter = class {
2864
4042
  if (stopAckExtension !== null) stopAckExtension();
2865
4043
  },
2866
4044
  async (err) => {
2867
- logger.error(`Event handler error (${msg.subject}) in ${kind} router:`, err);
4045
+ eventBus.emit(
4046
+ "error" /* Error */,
4047
+ err instanceof Error ? err : new Error(String(err)),
4048
+ `${kind}-handler:${msg.subject}`
4049
+ );
2868
4050
  try {
2869
4051
  await settleFailure(msg, data, err);
2870
4052
  } finally {
@@ -2880,41 +4062,54 @@ var EventRouter = class {
2880
4062
  try {
2881
4063
  handler = patternRegistry.getHandler(subject);
2882
4064
  if (!handler) {
2883
- logger.error(`No handler for subject: ${subject}`);
4065
+ logger5.error(`No handler for subject: ${subject}`);
2884
4066
  return void 0;
2885
4067
  }
2886
4068
  try {
2887
4069
  data = codec.decode(msg.data);
2888
4070
  } catch (err) {
2889
- logger.error(`Decode error for ${subject}:`, err);
4071
+ logger5.error(`Decode error for ${subject}:`, err);
2890
4072
  return void 0;
2891
4073
  }
2892
4074
  if (emitRouted) eventBus.emitMessageRouted(subject, "event" /* Event */);
2893
4075
  } catch (err) {
2894
- logger.error(`Ordered handler error (${subject}):`, err);
4076
+ logger5.error(`Ordered handler error (${subject}):`, err);
2895
4077
  return void 0;
2896
4078
  }
2897
4079
  const ctx = new RpcContext([msg]);
2898
4080
  const warnIfSettlementAttempted = () => {
2899
4081
  if (ctx.shouldRetry || ctx.shouldTerminate) {
2900
- logger.warn(
4082
+ logger5.warn(
2901
4083
  `retry()/terminate() ignored for ordered message ${subject} \u2014 ordered consumers auto-acknowledge`
2902
4084
  );
2903
4085
  }
2904
4086
  };
2905
4087
  let pending;
2906
4088
  try {
2907
- pending = unwrapResult(handler(data, ctx));
4089
+ pending = withConsumeSpan(
4090
+ {
4091
+ subject: msg.subject,
4092
+ msg,
4093
+ info: msg.info,
4094
+ kind: spanKind,
4095
+ payloadBytes: msg.data.length,
4096
+ handlerMetadata: { pattern: msg.subject },
4097
+ serviceName,
4098
+ endpoint: serverEndpoint
4099
+ },
4100
+ otel,
4101
+ () => unwrapResult(handler(data, ctx))
4102
+ );
2908
4103
  } catch (err) {
2909
- logger.error(`Ordered handler error (${subject}):`, err);
4104
+ logger5.error(`Ordered handler error (${subject}):`, err);
2910
4105
  return void 0;
2911
4106
  }
2912
- if (!isPromiseLike(pending)) {
4107
+ if (!isPromiseLike2(pending)) {
2913
4108
  warnIfSettlementAttempted();
2914
4109
  return void 0;
2915
4110
  }
2916
4111
  return pending.then(warnIfSettlementAttempted, (err) => {
2917
- logger.error(`Ordered handler error (${subject}):`, err);
4112
+ logger5.error(`Ordered handler error (${subject}):`, err);
2918
4113
  });
2919
4114
  };
2920
4115
  const route = isOrdered ? handleOrderedSafe : handleSafe;
@@ -2947,7 +4142,7 @@ var EventRouter = class {
2947
4142
  backlog.push(msg);
2948
4143
  if (!backlogWarned && backlog.length >= backlogWarnThreshold) {
2949
4144
  backlogWarned = true;
2950
- logger.warn(
4145
+ logger5.warn(
2951
4146
  `${kind} backlog reached ${backlog.length} messages \u2014 consumer may be falling behind`
2952
4147
  );
2953
4148
  }
@@ -2963,7 +4158,7 @@ var EventRouter = class {
2963
4158
  }
2964
4159
  },
2965
4160
  error: (err) => {
2966
- logger.error(`Stream error in ${kind} router`, err);
4161
+ logger5.error(`Stream error in ${kind} router`, err);
2967
4162
  }
2968
4163
  });
2969
4164
  this.subscriptions.push(subscription);
@@ -2978,14 +4173,11 @@ var EventRouter = class {
2978
4173
  if (kind === "broadcast" /* Broadcast */) return this.processingConfig?.broadcast?.ackExtension;
2979
4174
  return void 0;
2980
4175
  }
2981
- /** Handle a dead letter: invoke callback, then term or nak based on result. */
2982
4176
  /**
2983
- * Fallback execution for a dead letter when DLQ is disabled, or when
2984
- * publishing to the DLQ stream fails (due to network or NATS errors).
2985
- *
2986
- * Triggers the user-provided `onDeadLetter` hook for logging/alerting.
2987
- * On success, terminates the message. On error, leaves it unacknowledged (nak)
2988
- * so NATS can retry the delivery on the next cycle.
4177
+ * Last-resort path for a dead letter: invoke `onDeadLetter`, then `term` on
4178
+ * success or `nak` on hook failure so NATS retries on the next delivery
4179
+ * cycle. Used when DLQ stream isn't configured, or when publishing to it
4180
+ * failed and we still have to surface the message somewhere observable.
2989
4181
  */
2990
4182
  async fallbackToOnDeadLetterCallback(info, msg) {
2991
4183
  if (!this.deadLetterConfig) {
@@ -3077,20 +4269,37 @@ var EventRouter = class {
3077
4269
  streamSequence: msg.info.streamSequence,
3078
4270
  timestamp: new Date(msg.info.timestampNanos / 1e6).toISOString()
3079
4271
  };
3080
- this.eventBus.emit("deadLetter" /* DeadLetter */, info);
3081
- if (!this.options?.dlq) {
3082
- await this.fallbackToOnDeadLetterCallback(info, msg);
3083
- } else {
3084
- await this.publishToDlq(msg, info, error);
3085
- }
4272
+ await withDeadLetterSpan(
4273
+ {
4274
+ msg,
4275
+ // Pattern resolution mirrors event-routing: when a registered
4276
+ // pattern matches, surface it on the DLQ span so APM can filter
4277
+ // dead letters by handler without parsing the subject. Falls back
4278
+ // to the subject itself when no glob handler is in play.
4279
+ pattern: this.patternRegistry.getHandler(msg.subject) ? msg.subject : void 0,
4280
+ finalDeliveryCount: msg.info.deliveryCount,
4281
+ reason: error instanceof Error ? error.message : String(error),
4282
+ serviceName: this.serviceName,
4283
+ endpoint: this.serverEndpoint
4284
+ },
4285
+ this.otel,
4286
+ async () => {
4287
+ this.eventBus.emit("deadLetter" /* DeadLetter */, info);
4288
+ if (!this.options?.dlq) {
4289
+ await this.fallbackToOnDeadLetterCallback(info, msg);
4290
+ } else {
4291
+ await this.publishToDlq(msg, info, error);
4292
+ }
4293
+ }
4294
+ );
3086
4295
  }
3087
4296
  };
3088
4297
 
3089
4298
  // src/server/routing/rpc.router.ts
3090
- import { Logger as Logger12 } from "@nestjs/common";
4299
+ import { Logger as Logger16 } from "@nestjs/common";
3091
4300
  import { headers } from "@nats-io/transport-node";
3092
4301
  var RpcRouter = class {
3093
- constructor(messageProvider, patternRegistry, connection, codec, eventBus, rpcOptions, ackWaitMap) {
4302
+ constructor(messageProvider, patternRegistry, connection, codec, eventBus, rpcOptions, ackWaitMap, options) {
3094
4303
  this.messageProvider = messageProvider;
3095
4304
  this.patternRegistry = patternRegistry;
3096
4305
  this.connection = connection;
@@ -3100,13 +4309,26 @@ var RpcRouter = class {
3100
4309
  this.ackWaitMap = ackWaitMap;
3101
4310
  this.timeout = rpcOptions?.timeout ?? DEFAULT_JETSTREAM_RPC_TIMEOUT;
3102
4311
  this.concurrency = rpcOptions?.concurrency;
4312
+ if (options) {
4313
+ const derived = deriveOtelAttrs(options);
4314
+ this.otel = derived.otel;
4315
+ this.serviceName = derived.serviceName;
4316
+ this.serverEndpoint = derived.serverEndpoint;
4317
+ } else {
4318
+ this.otel = resolveOtelOptions({ enabled: false });
4319
+ this.serviceName = "";
4320
+ this.serverEndpoint = null;
4321
+ }
3103
4322
  }
3104
- logger = new Logger12("Jetstream:RpcRouter");
4323
+ logger = new Logger16("Jetstream:RpcRouter");
3105
4324
  timeout;
3106
4325
  concurrency;
3107
4326
  resolvedAckExtensionInterval;
3108
4327
  subscription = null;
3109
4328
  cachedNc = null;
4329
+ otel;
4330
+ serviceName;
4331
+ serverEndpoint;
3110
4332
  /** Lazily resolve the ack extension interval (needs ackWaitMap populated at runtime). */
3111
4333
  get ackExtensionInterval() {
3112
4334
  if (this.resolvedAckExtensionInterval !== void 0) return this.resolvedAckExtensionInterval;
@@ -3123,11 +4345,14 @@ var RpcRouter = class {
3123
4345
  const patternRegistry = this.patternRegistry;
3124
4346
  const codec = this.codec;
3125
4347
  const eventBus = this.eventBus;
3126
- const logger = this.logger;
4348
+ const logger5 = this.logger;
3127
4349
  const timeout = this.timeout;
3128
4350
  const ackExtensionInterval = this.ackExtensionInterval;
3129
4351
  const hasAckExtension = ackExtensionInterval !== null && ackExtensionInterval > 0;
3130
4352
  const maxActive = this.concurrency ?? Number.POSITIVE_INFINITY;
4353
+ const otel = this.otel;
4354
+ const serviceName = this.serviceName;
4355
+ const serverEndpoint = this.serverEndpoint;
3131
4356
  const emitRpcTimeout = (subject, correlationId) => {
3132
4357
  eventBus.emit("rpcTimeout" /* RpcTimeout */, subject, correlationId);
3133
4358
  };
@@ -3137,7 +4362,7 @@ var RpcRouter = class {
3137
4362
  hdrs.set("x-correlation-id" /* CorrelationId */, correlationId);
3138
4363
  nc.publish(replyTo, codec.encode(payload), { headers: hdrs });
3139
4364
  } catch (publishErr) {
3140
- logger.error(`Failed to publish RPC response`, publishErr);
4365
+ logger5.error(`Failed to publish RPC response`, publishErr);
3141
4366
  }
3142
4367
  };
3143
4368
  const publishErrorReply = (replyTo, correlationId, subject, err) => {
@@ -3147,7 +4372,7 @@ var RpcRouter = class {
3147
4372
  hdrs.set("x-error" /* Error */, "true");
3148
4373
  nc.publish(replyTo, codec.encode(serializeError(err)), { headers: hdrs });
3149
4374
  } catch (encodeErr) {
3150
- logger.error(`Failed to encode RPC error for ${subject}`, encodeErr);
4375
+ logger5.error(`Failed to encode RPC error for ${subject}`, encodeErr);
3151
4376
  }
3152
4377
  };
3153
4378
  const resolveCommand = (msg) => {
@@ -3156,7 +4381,7 @@ var RpcRouter = class {
3156
4381
  const handler = patternRegistry.getHandler(subject);
3157
4382
  if (!handler) {
3158
4383
  msg.term(`No handler for RPC: ${subject}`);
3159
- logger.error(`No handler for RPC subject: ${subject}`);
4384
+ logger5.error(`No handler for RPC subject: ${subject}`);
3160
4385
  return null;
3161
4386
  }
3162
4387
  const msgHeaders = msg.headers;
@@ -3164,7 +4389,7 @@ var RpcRouter = class {
3164
4389
  const correlationId = msgHeaders?.get("x-correlation-id" /* CorrelationId */);
3165
4390
  if (!replyTo || !correlationId) {
3166
4391
  msg.term("Missing required headers (reply-to or correlation-id)");
3167
- logger.error(`Missing headers for RPC: ${subject}`);
4392
+ logger5.error(`Missing headers for RPC: ${subject}`);
3168
4393
  return null;
3169
4394
  }
3170
4395
  let data;
@@ -3172,17 +4397,17 @@ var RpcRouter = class {
3172
4397
  data = codec.decode(msg.data);
3173
4398
  } catch (err) {
3174
4399
  msg.term("Decode error");
3175
- logger.error(`Decode error for RPC ${subject}:`, err);
4400
+ logger5.error(`Decode error for RPC ${subject}:`, err);
3176
4401
  return null;
3177
4402
  }
3178
4403
  eventBus.emitMessageRouted(subject, "rpc" /* Rpc */);
3179
4404
  return { handler, data, replyTo, correlationId };
3180
4405
  } catch (err) {
3181
- logger.error("Unexpected error in RPC router", err);
4406
+ logger5.error("Unexpected error in RPC router", err);
3182
4407
  try {
3183
4408
  msg.term("Unexpected router error");
3184
4409
  } catch (termErr) {
3185
- logger.error(`Failed to terminate RPC message ${subject}:`, termErr);
4410
+ logger5.error(`Failed to terminate RPC message ${subject}:`, termErr);
3186
4411
  }
3187
4412
  return null;
3188
4413
  }
@@ -3194,17 +4419,39 @@ var RpcRouter = class {
3194
4419
  const subject = msg.subject;
3195
4420
  const ctx = new RpcContext([msg]);
3196
4421
  const stopAckExtension = hasAckExtension ? startAckExtensionTimer(msg, ackExtensionInterval) : null;
4422
+ const reportHandlerError = (err) => {
4423
+ eventBus.emit(
4424
+ "error" /* Error */,
4425
+ err instanceof Error ? err : new Error(String(err)),
4426
+ `rpc-handler:${subject}`
4427
+ );
4428
+ publishErrorReply(replyTo, correlationId, subject, err);
4429
+ msg.term(`Handler error: ${subject}`);
4430
+ };
4431
+ const abortController = new AbortController();
3197
4432
  let pending;
3198
4433
  try {
3199
- pending = unwrapResult(handler(data, ctx));
4434
+ pending = withConsumeSpan(
4435
+ {
4436
+ subject,
4437
+ msg,
4438
+ info: msg.info,
4439
+ kind: "rpc" /* Rpc */,
4440
+ payloadBytes: msg.data.length,
4441
+ handlerMetadata: { pattern: subject },
4442
+ serviceName,
4443
+ endpoint: serverEndpoint
4444
+ },
4445
+ otel,
4446
+ () => unwrapResult(handler(data, ctx)),
4447
+ { signal: abortController.signal, timeoutLabel: "rpc.handler.timeout" }
4448
+ );
3200
4449
  } catch (err) {
3201
4450
  if (stopAckExtension !== null) stopAckExtension();
3202
- logger.error(`RPC handler error (${subject}):`, err);
3203
- publishErrorReply(replyTo, correlationId, subject, err);
3204
- msg.term(`Handler error: ${subject}`);
4451
+ reportHandlerError(err);
3205
4452
  return void 0;
3206
4453
  }
3207
- if (!isPromiseLike(pending)) {
4454
+ if (!isPromiseLike2(pending)) {
3208
4455
  if (stopAckExtension !== null) stopAckExtension();
3209
4456
  msg.ack();
3210
4457
  publishReply(replyTo, correlationId, pending);
@@ -3215,7 +4462,7 @@ var RpcRouter = class {
3215
4462
  if (settled) return;
3216
4463
  settled = true;
3217
4464
  if (stopAckExtension !== null) stopAckExtension();
3218
- logger.error(`RPC timeout (${timeout}ms): ${subject}`);
4465
+ abortController.abort();
3219
4466
  emitRpcTimeout(subject, correlationId);
3220
4467
  msg.term("Handler timeout");
3221
4468
  }, timeout);
@@ -3233,9 +4480,7 @@ var RpcRouter = class {
3233
4480
  settled = true;
3234
4481
  clearTimeout(timeoutId);
3235
4482
  if (stopAckExtension !== null) stopAckExtension();
3236
- logger.error(`RPC handler error (${subject}):`, err);
3237
- publishErrorReply(replyTo, correlationId, subject, err);
3238
- msg.term(`Handler error: ${subject}`);
4483
+ reportHandlerError(err);
3239
4484
  }
3240
4485
  );
3241
4486
  };
@@ -3267,7 +4512,7 @@ var RpcRouter = class {
3267
4512
  backlog.push(msg);
3268
4513
  if (!backlogWarned && backlog.length >= backlogWarnThreshold) {
3269
4514
  backlogWarned = true;
3270
- logger.warn(
4515
+ logger5.warn(
3271
4516
  `RPC backlog reached ${backlog.length} messages \u2014 consumer may be falling behind`
3272
4517
  );
3273
4518
  }
@@ -3283,7 +4528,7 @@ var RpcRouter = class {
3283
4528
  }
3284
4529
  },
3285
4530
  error: (err) => {
3286
- logger.error("Stream error in RPC router", err);
4531
+ logger5.error("Stream error in RPC router", err);
3287
4532
  }
3288
4533
  });
3289
4534
  }
@@ -3295,14 +4540,14 @@ var RpcRouter = class {
3295
4540
  };
3296
4541
 
3297
4542
  // src/shutdown/shutdown.manager.ts
3298
- import { Logger as Logger13 } from "@nestjs/common";
4543
+ import { Logger as Logger17 } from "@nestjs/common";
3299
4544
  var ShutdownManager = class {
3300
4545
  constructor(connection, eventBus, timeout) {
3301
4546
  this.connection = connection;
3302
4547
  this.eventBus = eventBus;
3303
4548
  this.timeout = timeout;
3304
4549
  }
3305
- logger = new Logger13("Jetstream:Shutdown");
4550
+ logger = new Logger17("Jetstream:Shutdown");
3306
4551
  shutdownPromise;
3307
4552
  /**
3308
4553
  * Execute the full shutdown sequence.
@@ -3342,9 +4587,6 @@ var JetstreamModule = class {
3342
4587
  this.shutdownManager = shutdownManager;
3343
4588
  this.strategy = strategy;
3344
4589
  }
3345
- // -------------------------------------------------------------------
3346
- // forRoot — global module registration
3347
- // -------------------------------------------------------------------
3348
4590
  /**
3349
4591
  * Register the JetStream transport globally.
3350
4592
  *
@@ -3371,9 +4613,6 @@ var JetstreamModule = class {
3371
4613
  ]
3372
4614
  };
3373
4615
  }
3374
- // -------------------------------------------------------------------
3375
- // forRootAsync — async global module registration
3376
- // -------------------------------------------------------------------
3377
4616
  /**
3378
4617
  * Register the JetStream transport globally with async configuration.
3379
4618
  *
@@ -3402,9 +4641,6 @@ var JetstreamModule = class {
3402
4641
  ]
3403
4642
  };
3404
4643
  }
3405
- // -------------------------------------------------------------------
3406
- // forFeature — per-module client registration
3407
- // -------------------------------------------------------------------
3408
4644
  /**
3409
4645
  * Register a lightweight client proxy for a target service.
3410
4646
  *
@@ -3430,9 +4666,6 @@ var JetstreamModule = class {
3430
4666
  exports: [clientToken]
3431
4667
  };
3432
4668
  }
3433
- // -------------------------------------------------------------------
3434
- // Provider factories
3435
- // -------------------------------------------------------------------
3436
4669
  static createCoreProviders(options) {
3437
4670
  return [
3438
4671
  {
@@ -3450,8 +4683,8 @@ var JetstreamModule = class {
3450
4683
  provide: JETSTREAM_EVENT_BUS,
3451
4684
  inject: [JETSTREAM_OPTIONS],
3452
4685
  useFactory: (options) => {
3453
- const logger = new Logger14("Jetstream:Module");
3454
- return new EventBus(logger, options.hooks);
4686
+ const logger5 = new Logger18("Jetstream:Module");
4687
+ return new EventBus(logger5, options.hooks);
3455
4688
  }
3456
4689
  },
3457
4690
  // Codec — global encode/decode
@@ -3490,10 +4723,8 @@ var JetstreamModule = class {
3490
4723
  );
3491
4724
  }
3492
4725
  },
3493
- // ---------------------------------------------------------------
3494
4726
  // Consumer infrastructure — only created when consumer !== false.
3495
4727
  // Providers return null when consumer is disabled (publisher-only mode).
3496
- // ---------------------------------------------------------------
3497
4728
  // PatternRegistry — subject-to-handler mapping
3498
4729
  {
3499
4730
  provide: PatternRegistry,
@@ -3529,8 +4760,14 @@ var JetstreamModule = class {
3529
4760
  // MessageProvider — pull-based message consumption
3530
4761
  {
3531
4762
  provide: MessageProvider,
3532
- inject: [JETSTREAM_OPTIONS, JETSTREAM_CONNECTION, JETSTREAM_EVENT_BUS, ConsumerProvider],
3533
- useFactory: (options, connection, eventBus, consumerProvider) => {
4763
+ inject: [
4764
+ JETSTREAM_OPTIONS,
4765
+ JETSTREAM_CONNECTION,
4766
+ JETSTREAM_EVENT_BUS,
4767
+ ConsumerProvider,
4768
+ StreamProvider
4769
+ ],
4770
+ useFactory: (options, connection, eventBus, consumerProvider, streamProvider) => {
3534
4771
  if (options.consumer === false) return null;
3535
4772
  const consumeOptionsMap = /* @__PURE__ */ new Map();
3536
4773
  if (options.events?.consume)
@@ -3540,10 +4777,22 @@ var JetstreamModule = class {
3540
4777
  if (options.rpc?.mode === "jetstream" && options.rpc.consume) {
3541
4778
  consumeOptionsMap.set("cmd" /* Command */, options.rpc.consume);
3542
4779
  }
3543
- const consumerRecoveryFn = consumerProvider ? async (kind) => {
3544
- const jsm = await connection.getJetStreamManager();
3545
- return consumerProvider.recoverConsumer(jsm, kind);
3546
- } : void 0;
4780
+ const derived = deriveOtelAttrs(options);
4781
+ const { otel, serverEndpoint: otelEndpoint, serviceName: otelServiceName } = derived;
4782
+ const consumerRecoveryFn = consumerProvider && streamProvider ? async (kind) => withSelfHealingSpan(
4783
+ otel,
4784
+ {
4785
+ serviceName: otelServiceName,
4786
+ endpoint: otelEndpoint,
4787
+ consumer: consumerProvider.getConsumerName(kind),
4788
+ stream: streamProvider.getStreamName(kind),
4789
+ reason: "consumer not found"
4790
+ },
4791
+ async () => {
4792
+ const jsm = await connection.getJetStreamManager();
4793
+ return consumerProvider.recoverConsumer(jsm, kind);
4794
+ }
4795
+ ) : void 0;
3547
4796
  return new MessageProvider(connection, eventBus, consumeOptionsMap, consumerRecoveryFn);
3548
4797
  }
3549
4798
  },
@@ -3614,7 +4863,8 @@ var JetstreamModule = class {
3614
4863
  codec,
3615
4864
  eventBus,
3616
4865
  rpcOptions,
3617
- ackWaitMap
4866
+ ackWaitMap,
4867
+ options
3618
4868
  );
3619
4869
  }
3620
4870
  },
@@ -3717,9 +4967,6 @@ var JetstreamModule = class {
3717
4967
  }
3718
4968
  ];
3719
4969
  }
3720
- // -------------------------------------------------------------------
3721
- // Lifecycle hooks
3722
- // -------------------------------------------------------------------
3723
4970
  /**
3724
4971
  * Gracefully shut down the transport on application termination.
3725
4972
  */
@@ -3738,6 +4985,7 @@ JetstreamModule = __decorateClass([
3738
4985
  __decorateParam(1, Inject(JetstreamStrategy))
3739
4986
  ], JetstreamModule);
3740
4987
  export {
4988
+ ConsumeKind,
3741
4989
  DEFAULT_BROADCAST_CONSUMER_CONFIG,
3742
4990
  DEFAULT_BROADCAST_STREAM_CONFIG,
3743
4991
  DEFAULT_COMMAND_CONSUMER_CONFIG,
@@ -3753,6 +5001,7 @@ export {
3753
5001
  DEFAULT_ORDERED_STREAM_CONFIG,
3754
5002
  DEFAULT_RPC_TIMEOUT,
3755
5003
  DEFAULT_SHUTDOWN_TIMEOUT,
5004
+ DEFAULT_TRACES,
3756
5005
  JETSTREAM_CODEC,
3757
5006
  JETSTREAM_CONNECTION,
3758
5007
  JETSTREAM_OPTIONS,
@@ -3764,15 +5013,18 @@ export {
3764
5013
  JetstreamRecord,
3765
5014
  JetstreamRecordBuilder,
3766
5015
  JetstreamStrategy,
5016
+ JetstreamTrace,
3767
5017
  JsonCodec,
3768
5018
  MIN_METADATA_TTL,
3769
5019
  MessageKind,
3770
5020
  MsgpackCodec,
3771
5021
  NatsErrorCode,
3772
5022
  PatternPrefix,
5023
+ PublishKind,
3773
5024
  RESERVED_HEADERS,
3774
5025
  RpcContext,
3775
5026
  StreamKind,
5027
+ TRACER_NAME,
3776
5028
  TransportEvent,
3777
5029
  buildBroadcastSubject,
3778
5030
  buildSubject,