@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.cjs +1515 -272
- package/dist/index.d.cts +965 -654
- package/dist/index.d.ts +965 -654
- package/dist/index.js +1515 -263
- package/package.json +16 -7
package/dist/index.cjs
CHANGED
|
@@ -29,6 +29,7 @@ var __decorateParam = (index, decorator) => (target, key) => decorator(target, k
|
|
|
29
29
|
// src/index.ts
|
|
30
30
|
var index_exports = {};
|
|
31
31
|
__export(index_exports, {
|
|
32
|
+
ConsumeKind: () => ConsumeKind,
|
|
32
33
|
DEFAULT_BROADCAST_CONSUMER_CONFIG: () => DEFAULT_BROADCAST_CONSUMER_CONFIG,
|
|
33
34
|
DEFAULT_BROADCAST_STREAM_CONFIG: () => DEFAULT_BROADCAST_STREAM_CONFIG,
|
|
34
35
|
DEFAULT_COMMAND_CONSUMER_CONFIG: () => DEFAULT_COMMAND_CONSUMER_CONFIG,
|
|
@@ -44,6 +45,7 @@ __export(index_exports, {
|
|
|
44
45
|
DEFAULT_ORDERED_STREAM_CONFIG: () => DEFAULT_ORDERED_STREAM_CONFIG,
|
|
45
46
|
DEFAULT_RPC_TIMEOUT: () => DEFAULT_RPC_TIMEOUT,
|
|
46
47
|
DEFAULT_SHUTDOWN_TIMEOUT: () => DEFAULT_SHUTDOWN_TIMEOUT,
|
|
48
|
+
DEFAULT_TRACES: () => DEFAULT_TRACES,
|
|
47
49
|
JETSTREAM_CODEC: () => JETSTREAM_CODEC,
|
|
48
50
|
JETSTREAM_CONNECTION: () => JETSTREAM_CONNECTION,
|
|
49
51
|
JETSTREAM_OPTIONS: () => JETSTREAM_OPTIONS,
|
|
@@ -55,15 +57,18 @@ __export(index_exports, {
|
|
|
55
57
|
JetstreamRecord: () => JetstreamRecord,
|
|
56
58
|
JetstreamRecordBuilder: () => JetstreamRecordBuilder,
|
|
57
59
|
JetstreamStrategy: () => JetstreamStrategy,
|
|
60
|
+
JetstreamTrace: () => JetstreamTrace,
|
|
58
61
|
JsonCodec: () => JsonCodec,
|
|
59
62
|
MIN_METADATA_TTL: () => MIN_METADATA_TTL,
|
|
60
63
|
MessageKind: () => MessageKind,
|
|
61
64
|
MsgpackCodec: () => MsgpackCodec,
|
|
62
65
|
NatsErrorCode: () => NatsErrorCode,
|
|
63
66
|
PatternPrefix: () => PatternPrefix,
|
|
67
|
+
PublishKind: () => PublishKind,
|
|
64
68
|
RESERVED_HEADERS: () => RESERVED_HEADERS,
|
|
65
69
|
RpcContext: () => RpcContext,
|
|
66
70
|
StreamKind: () => StreamKind,
|
|
71
|
+
TRACER_NAME: () => TRACER_NAME,
|
|
67
72
|
TransportEvent: () => TransportEvent,
|
|
68
73
|
buildBroadcastSubject: () => buildBroadcastSubject,
|
|
69
74
|
buildSubject: () => buildSubject,
|
|
@@ -80,13 +85,14 @@ __export(index_exports, {
|
|
|
80
85
|
module.exports = __toCommonJS(index_exports);
|
|
81
86
|
|
|
82
87
|
// src/jetstream.module.ts
|
|
83
|
-
var
|
|
88
|
+
var import_common18 = require("@nestjs/common");
|
|
84
89
|
|
|
85
90
|
// src/client/jetstream.client.ts
|
|
86
|
-
var
|
|
91
|
+
var import_common5 = require("@nestjs/common");
|
|
87
92
|
var import_microservices = require("@nestjs/microservices");
|
|
88
|
-
var
|
|
93
|
+
var import_api8 = require("@opentelemetry/api");
|
|
89
94
|
var import_nuid = require("@nats-io/nuid");
|
|
95
|
+
var import_transport_node = require("@nats-io/transport-node");
|
|
90
96
|
|
|
91
97
|
// src/interfaces/hooks.interface.ts
|
|
92
98
|
var MessageKind = /* @__PURE__ */ ((MessageKind2) => {
|
|
@@ -276,6 +282,907 @@ var PatternPrefix = /* @__PURE__ */ ((PatternPrefix2) => {
|
|
|
276
282
|
var isJetStreamRpcMode = (rpc) => rpc?.mode === "jetstream";
|
|
277
283
|
var isCoreRpcMode = (rpc) => !rpc || rpc.mode === "core";
|
|
278
284
|
|
|
285
|
+
// src/otel/constants.ts
|
|
286
|
+
var TRACER_NAME = "@horizon-republic/nestjs-jetstream";
|
|
287
|
+
|
|
288
|
+
// src/otel/attribute-keys.ts
|
|
289
|
+
var ATTR_MESSAGING_SYSTEM = "messaging.system";
|
|
290
|
+
var ATTR_MESSAGING_DESTINATION_NAME = "messaging.destination.name";
|
|
291
|
+
var ATTR_MESSAGING_DESTINATION_TEMPLATE = "messaging.destination.template";
|
|
292
|
+
var ATTR_MESSAGING_CLIENT_ID = "messaging.client.id";
|
|
293
|
+
var ATTR_MESSAGING_OPERATION_NAME = "messaging.operation.name";
|
|
294
|
+
var ATTR_MESSAGING_OPERATION_TYPE = "messaging.operation.type";
|
|
295
|
+
var ATTR_MESSAGING_MESSAGE_BODY_SIZE = "messaging.message.body.size";
|
|
296
|
+
var ATTR_MESSAGING_MESSAGE_ID = "messaging.message.id";
|
|
297
|
+
var ATTR_MESSAGING_MESSAGE_CONVERSATION_ID = "messaging.message.conversation_id";
|
|
298
|
+
var ATTR_MESSAGING_CONSUMER_GROUP_NAME = "messaging.consumer.group.name";
|
|
299
|
+
var ATTR_MESSAGING_HEADER_PREFIX = "messaging.header.";
|
|
300
|
+
var ATTR_MESSAGING_NATS_STREAM_NAME = "messaging.nats.stream.name";
|
|
301
|
+
var ATTR_MESSAGING_NATS_STREAM_SEQUENCE = "messaging.nats.message.stream_sequence";
|
|
302
|
+
var ATTR_MESSAGING_NATS_CONSUMER_SEQUENCE = "messaging.nats.message.consumer_sequence";
|
|
303
|
+
var ATTR_MESSAGING_NATS_DELIVERY_COUNT = "messaging.nats.message.delivery_count";
|
|
304
|
+
var ATTR_MESSAGING_NATS_BODY = "messaging.nats.message.body";
|
|
305
|
+
var ATTR_MESSAGING_NATS_BODY_TRUNCATED = "messaging.nats.message.body.truncated";
|
|
306
|
+
var ATTR_SERVER_ADDRESS = "server.address";
|
|
307
|
+
var ATTR_SERVER_PORT = "server.port";
|
|
308
|
+
var ATTR_JETSTREAM_SERVICE_NAME = "jetstream.service.name";
|
|
309
|
+
var ATTR_JETSTREAM_KIND = "jetstream.kind";
|
|
310
|
+
var ATTR_JETSTREAM_RPC_REPLY_HAS_ERROR = "jetstream.rpc.reply.has_error";
|
|
311
|
+
var ATTR_JETSTREAM_RPC_REPLY_ERROR_CODE = "jetstream.rpc.reply.error.code";
|
|
312
|
+
var ATTR_JETSTREAM_PROVISIONING_ENTITY = "jetstream.provisioning.entity";
|
|
313
|
+
var ATTR_JETSTREAM_PROVISIONING_ACTION = "jetstream.provisioning.action";
|
|
314
|
+
var ATTR_JETSTREAM_PROVISIONING_NAME = "jetstream.provisioning.name";
|
|
315
|
+
var ATTR_JETSTREAM_SELF_HEALING_REASON = "jetstream.self_healing.reason";
|
|
316
|
+
var ATTR_JETSTREAM_MIGRATION_REASON = "jetstream.migration.reason";
|
|
317
|
+
var ATTR_JETSTREAM_DEAD_LETTER_REASON = "jetstream.dead_letter.reason";
|
|
318
|
+
var ATTR_JETSTREAM_SCHEDULE_TARGET = "jetstream.schedule.target";
|
|
319
|
+
var ATTR_NATS_CONNECTION_SERVER = "nats.connection.server";
|
|
320
|
+
var NATS_MSG_ID_HEADER = "Nats-Msg-Id";
|
|
321
|
+
var HOOK_PUBLISH = "publishHook";
|
|
322
|
+
var HOOK_CONSUME = "consumeHook";
|
|
323
|
+
var HOOK_RESPONSE = "responseHook";
|
|
324
|
+
var SPAN_NAME_PUBLISH = "publish";
|
|
325
|
+
var SPAN_NAME_PROCESS = "process";
|
|
326
|
+
var SPAN_NAME_SEND = "send";
|
|
327
|
+
var SPAN_NAME_DEAD_LETTER = "dead_letter";
|
|
328
|
+
var SPAN_NAME_NATS_CONNECTION = "nats.connection";
|
|
329
|
+
var SPAN_NAME_JETSTREAM_SHUTDOWN = "jetstream.shutdown";
|
|
330
|
+
var SPAN_NAME_JETSTREAM_SELF_HEALING = "jetstream.self_healing";
|
|
331
|
+
var SPAN_NAME_JETSTREAM_MIGRATION = "jetstream.migration";
|
|
332
|
+
var SPAN_NAME_JETSTREAM_PROVISIONING_PREFIX = "jetstream.provisioning.";
|
|
333
|
+
var EVENT_CONNECTION_DISCONNECTED = "connection.disconnected";
|
|
334
|
+
var EVENT_CONNECTION_RECONNECTED = "connection.reconnected";
|
|
335
|
+
var messagingHeaderAttr = (headerName) => `${ATTR_MESSAGING_HEADER_PREFIX}${headerName.toLowerCase()}`;
|
|
336
|
+
|
|
337
|
+
// src/otel/trace-kinds.ts
|
|
338
|
+
var JetstreamTrace = /* @__PURE__ */ ((JetstreamTrace2) => {
|
|
339
|
+
JetstreamTrace2["Publish"] = "publish";
|
|
340
|
+
JetstreamTrace2["Consume"] = "consume";
|
|
341
|
+
JetstreamTrace2["RpcClientSend"] = "rpc.client.send";
|
|
342
|
+
JetstreamTrace2["DeadLetter"] = "dead_letter";
|
|
343
|
+
JetstreamTrace2["ConnectionLifecycle"] = "connection.lifecycle";
|
|
344
|
+
JetstreamTrace2["SelfHealing"] = "self_healing";
|
|
345
|
+
JetstreamTrace2["Provisioning"] = "provisioning";
|
|
346
|
+
JetstreamTrace2["Migration"] = "migration";
|
|
347
|
+
JetstreamTrace2["Shutdown"] = "shutdown";
|
|
348
|
+
return JetstreamTrace2;
|
|
349
|
+
})(JetstreamTrace || {});
|
|
350
|
+
var DEFAULT_TRACES = [
|
|
351
|
+
"publish" /* Publish */,
|
|
352
|
+
"consume" /* Consume */,
|
|
353
|
+
"rpc.client.send" /* RpcClientSend */,
|
|
354
|
+
"dead_letter" /* DeadLetter */
|
|
355
|
+
];
|
|
356
|
+
|
|
357
|
+
// src/otel/capture.ts
|
|
358
|
+
var NEGATION_PREFIX = "!";
|
|
359
|
+
var REGEX_SPECIAL_RE = /[.+?^${}()|[\]\\*]/gu;
|
|
360
|
+
var escapeForRegex = (input) => input.replace(REGEX_SPECIAL_RE, "\\$&");
|
|
361
|
+
var globToRegex = (glob) => {
|
|
362
|
+
const escaped = escapeForRegex(glob.toLowerCase()).replace(/\\\*/gu, ".*");
|
|
363
|
+
return new RegExp(`^${escaped}$`, "u");
|
|
364
|
+
};
|
|
365
|
+
var compileHeaderAllowlist = (allowlist) => {
|
|
366
|
+
if (allowlist === true) return () => true;
|
|
367
|
+
if (allowlist === false) return () => false;
|
|
368
|
+
if (allowlist.length === 0) return () => false;
|
|
369
|
+
const includes = [];
|
|
370
|
+
const excludes = [];
|
|
371
|
+
for (const pattern of allowlist) {
|
|
372
|
+
const isExclude = pattern.startsWith(NEGATION_PREFIX);
|
|
373
|
+
const body = isExclude ? pattern.slice(NEGATION_PREFIX.length) : pattern;
|
|
374
|
+
const regex = globToRegex(body);
|
|
375
|
+
if (isExclude) excludes.push(regex);
|
|
376
|
+
else includes.push(regex);
|
|
377
|
+
}
|
|
378
|
+
return (name) => {
|
|
379
|
+
const lower = name.toLowerCase();
|
|
380
|
+
if (!includes.some((re) => re.test(lower))) return false;
|
|
381
|
+
if (excludes.some((re) => re.test(lower))) return false;
|
|
382
|
+
return true;
|
|
383
|
+
};
|
|
384
|
+
};
|
|
385
|
+
var subjectMatcherCache = /* @__PURE__ */ new WeakMap();
|
|
386
|
+
var compileSubjectAllowlist = (allowlist) => {
|
|
387
|
+
if (!allowlist || allowlist.length === 0) return () => true;
|
|
388
|
+
const cached = subjectMatcherCache.get(allowlist);
|
|
389
|
+
if (cached) return cached;
|
|
390
|
+
const regexes = allowlist.map(globToRegex);
|
|
391
|
+
const matcher = (subject) => {
|
|
392
|
+
const lower = subject.toLowerCase();
|
|
393
|
+
return regexes.some((re) => re.test(lower));
|
|
394
|
+
};
|
|
395
|
+
subjectMatcherCache.set(allowlist, matcher);
|
|
396
|
+
return matcher;
|
|
397
|
+
};
|
|
398
|
+
var subjectMatchesAllowlist = (subject, allowlist) => compileSubjectAllowlist(allowlist)(subject);
|
|
399
|
+
var HEADER_DENYLIST = /* @__PURE__ */ new Set([
|
|
400
|
+
"traceparent",
|
|
401
|
+
"tracestate",
|
|
402
|
+
"baggage",
|
|
403
|
+
"sentry-trace",
|
|
404
|
+
"b3",
|
|
405
|
+
"x-b3-traceid",
|
|
406
|
+
"x-b3-spanid",
|
|
407
|
+
"x-b3-parentspanid",
|
|
408
|
+
"x-b3-sampled",
|
|
409
|
+
"x-b3-flags",
|
|
410
|
+
"uber-trace-id",
|
|
411
|
+
"x-correlation-id",
|
|
412
|
+
"x-reply-to",
|
|
413
|
+
"x-error",
|
|
414
|
+
"x-subject",
|
|
415
|
+
"x-caller-name"
|
|
416
|
+
]);
|
|
417
|
+
var isNatsServerHeader = (lower) => lower.startsWith("nats-");
|
|
418
|
+
var captureMatchingHeaders = (headers2, matcher) => {
|
|
419
|
+
if (!headers2) return {};
|
|
420
|
+
const out = {};
|
|
421
|
+
for (const key of headers2.keys()) {
|
|
422
|
+
const lower = key.toLowerCase();
|
|
423
|
+
if (HEADER_DENYLIST.has(lower)) continue;
|
|
424
|
+
if (isNatsServerHeader(lower)) continue;
|
|
425
|
+
if (!matcher(key)) continue;
|
|
426
|
+
const value = headers2.get(key);
|
|
427
|
+
if (value === "") continue;
|
|
428
|
+
out[messagingHeaderAttr(lower)] = value;
|
|
429
|
+
}
|
|
430
|
+
return out;
|
|
431
|
+
};
|
|
432
|
+
var tryUtf8Decode = (bytes) => {
|
|
433
|
+
try {
|
|
434
|
+
return new TextDecoder("utf-8", { fatal: false }).decode(bytes);
|
|
435
|
+
} catch {
|
|
436
|
+
return Buffer.from(bytes).toString("base64");
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
var captureBodyAttribute = (subject, payload, capture) => {
|
|
440
|
+
if (capture === false) return {};
|
|
441
|
+
if (payload.byteLength === 0) return {};
|
|
442
|
+
if (!subjectMatchesAllowlist(subject, capture.subjectAllowlist)) return {};
|
|
443
|
+
const { maxBytes } = capture;
|
|
444
|
+
const truncated = payload.byteLength > maxBytes;
|
|
445
|
+
const slice = truncated ? payload.subarray(0, maxBytes) : payload;
|
|
446
|
+
const decoded = tryUtf8Decode(slice);
|
|
447
|
+
const out = { [ATTR_MESSAGING_NATS_BODY]: decoded };
|
|
448
|
+
if (truncated) out[ATTR_MESSAGING_NATS_BODY_TRUNCATED] = true;
|
|
449
|
+
return out;
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
// src/otel/config.ts
|
|
453
|
+
var PublishKind = /* @__PURE__ */ ((PublishKind2) => {
|
|
454
|
+
PublishKind2["Event"] = "event";
|
|
455
|
+
PublishKind2["RpcRequest"] = "rpc.request";
|
|
456
|
+
PublishKind2["Broadcast"] = "broadcast";
|
|
457
|
+
PublishKind2["Ordered"] = "ordered";
|
|
458
|
+
return PublishKind2;
|
|
459
|
+
})(PublishKind || {});
|
|
460
|
+
var ConsumeKind = /* @__PURE__ */ ((ConsumeKind2) => {
|
|
461
|
+
ConsumeKind2["Event"] = "event";
|
|
462
|
+
ConsumeKind2["Rpc"] = "rpc";
|
|
463
|
+
ConsumeKind2["Broadcast"] = "broadcast";
|
|
464
|
+
ConsumeKind2["Ordered"] = "ordered";
|
|
465
|
+
return ConsumeKind2;
|
|
466
|
+
})(ConsumeKind || {});
|
|
467
|
+
var DEFAULT_CAPTURE_HEADERS = ["x-request-id"];
|
|
468
|
+
var DEFAULT_CAPTURE_BODY_MAX_BYTES = 4096;
|
|
469
|
+
var NESTJS_BARE_ERROR_MESSAGE = "Internal server error";
|
|
470
|
+
var isNestjsBareErrorSentinel = (obj) => obj.status === "error" && obj.message === NESTJS_BARE_ERROR_MESSAGE;
|
|
471
|
+
var defaultErrorClassifier = (err) => {
|
|
472
|
+
if (err === null || typeof err !== "object") return "unexpected";
|
|
473
|
+
if (!(err instanceof Error)) {
|
|
474
|
+
if (isNestjsBareErrorSentinel(err)) return "unexpected";
|
|
475
|
+
return "expected";
|
|
476
|
+
}
|
|
477
|
+
let proto = err;
|
|
478
|
+
while (proto) {
|
|
479
|
+
const name = proto.constructor?.name;
|
|
480
|
+
if (name === "RpcException" || name === "HttpException") return "expected";
|
|
481
|
+
proto = Object.getPrototypeOf(proto);
|
|
482
|
+
}
|
|
483
|
+
return "unexpected";
|
|
484
|
+
};
|
|
485
|
+
var expandTracesOption = (option) => {
|
|
486
|
+
if (option === void 0 || option === "default") return new Set(DEFAULT_TRACES);
|
|
487
|
+
if (option === "all") return new Set(Object.values(JetstreamTrace));
|
|
488
|
+
if (option === "none") return /* @__PURE__ */ new Set();
|
|
489
|
+
return new Set(option);
|
|
490
|
+
};
|
|
491
|
+
var compileHeaderMatcher = (option) => compileHeaderAllowlist(option ?? DEFAULT_CAPTURE_HEADERS);
|
|
492
|
+
var resolveCaptureBody = (option) => {
|
|
493
|
+
if (option === void 0 || option === false) return false;
|
|
494
|
+
if (option === true) return { maxBytes: DEFAULT_CAPTURE_BODY_MAX_BYTES };
|
|
495
|
+
return {
|
|
496
|
+
maxBytes: option.maxBytes ?? DEFAULT_CAPTURE_BODY_MAX_BYTES,
|
|
497
|
+
subjectAllowlist: option.subjectAllowlist
|
|
498
|
+
};
|
|
499
|
+
};
|
|
500
|
+
var resolveOtelOptions = (options = {}) => {
|
|
501
|
+
return {
|
|
502
|
+
enabled: options.enabled ?? true,
|
|
503
|
+
traces: expandTracesOption(options.traces),
|
|
504
|
+
captureHeaders: compileHeaderMatcher(options.captureHeaders),
|
|
505
|
+
captureBody: resolveCaptureBody(options.captureBody),
|
|
506
|
+
publishHook: options.publishHook,
|
|
507
|
+
consumeHook: options.consumeHook,
|
|
508
|
+
responseHook: options.responseHook,
|
|
509
|
+
shouldTracePublish: options.shouldTracePublish,
|
|
510
|
+
shouldTraceConsume: options.shouldTraceConsume,
|
|
511
|
+
errorClassifier: options.errorClassifier ?? defaultErrorClassifier
|
|
512
|
+
};
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
// src/otel/internal-utils.ts
|
|
516
|
+
var import_common = require("@nestjs/common");
|
|
517
|
+
var logger = new import_common.Logger("Jetstream:Otel");
|
|
518
|
+
var safelyInvokeHook = (hookName, hook, ...args) => {
|
|
519
|
+
if (!hook) return;
|
|
520
|
+
const logHookFailure = (err) => {
|
|
521
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
522
|
+
logger.debug(`OTel ${hookName} threw: ${message}`);
|
|
523
|
+
};
|
|
524
|
+
try {
|
|
525
|
+
const result = hook(...args);
|
|
526
|
+
if (result !== null && typeof result === "object" && "then" in result && typeof result.then === "function") {
|
|
527
|
+
result.then(void 0, logHookFailure);
|
|
528
|
+
}
|
|
529
|
+
} catch (err) {
|
|
530
|
+
logHookFailure(err);
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
var stripIpv6Brackets = (host) => host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
|
|
534
|
+
var parsePort = (portRaw) => {
|
|
535
|
+
if (!portRaw) return void 0;
|
|
536
|
+
const port = Number.parseInt(portRaw, 10);
|
|
537
|
+
return Number.isInteger(port) ? port : void 0;
|
|
538
|
+
};
|
|
539
|
+
var parseServerAddress = (servers) => {
|
|
540
|
+
const raw = servers[0];
|
|
541
|
+
if (!raw) return null;
|
|
542
|
+
if (raw.includes("://")) {
|
|
543
|
+
try {
|
|
544
|
+
const url = new URL(raw);
|
|
545
|
+
if (url.hostname.length === 0) return null;
|
|
546
|
+
const host2 = stripIpv6Brackets(url.hostname);
|
|
547
|
+
const port2 = parsePort(url.port || void 0);
|
|
548
|
+
return port2 === void 0 ? { host: host2 } : { host: host2, port: port2 };
|
|
549
|
+
} catch {
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if (raw.startsWith("[")) {
|
|
554
|
+
const closeIdx = raw.indexOf("]");
|
|
555
|
+
if (closeIdx <= 0) return null;
|
|
556
|
+
const host2 = raw.slice(1, closeIdx);
|
|
557
|
+
const port2 = parsePort(raw.slice(closeIdx + 1).replace(/^:/u, ""));
|
|
558
|
+
return port2 === void 0 ? { host: host2 } : { host: host2, port: port2 };
|
|
559
|
+
}
|
|
560
|
+
const [host, portRaw] = raw.split(":");
|
|
561
|
+
if (!host) return null;
|
|
562
|
+
const port = parsePort(portRaw);
|
|
563
|
+
return port === void 0 ? { host } : { host, port };
|
|
564
|
+
};
|
|
565
|
+
var deriveOtelAttrs = (options) => ({
|
|
566
|
+
otel: resolveOtelOptions(options.otel),
|
|
567
|
+
serviceName: internalName(options.name),
|
|
568
|
+
serverEndpoint: parseServerAddress(options.servers)
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
// src/otel/propagator.ts
|
|
572
|
+
var import_api = require("@opentelemetry/api");
|
|
573
|
+
var injectContext = (ctx, carrier, setter) => {
|
|
574
|
+
import_api.propagation.inject(ctx, carrier, setter);
|
|
575
|
+
};
|
|
576
|
+
var extractContext = (ctx, carrier, getter) => import_api.propagation.extract(ctx, carrier, getter);
|
|
577
|
+
|
|
578
|
+
// src/otel/tracer.ts
|
|
579
|
+
var import_api2 = require("@opentelemetry/api");
|
|
580
|
+
var PACKAGE_VERSION = true ? "2.10.0" : "0.0.0";
|
|
581
|
+
var getTracer = () => import_api2.trace.getTracer(TRACER_NAME, PACKAGE_VERSION);
|
|
582
|
+
|
|
583
|
+
// src/otel/carrier.ts
|
|
584
|
+
var hdrsSetter = {
|
|
585
|
+
set: (headers2, key, value) => {
|
|
586
|
+
headers2.set(key, value);
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
var hdrsGetter = {
|
|
590
|
+
keys: (headers2) => headers2 ? headers2.keys() : [],
|
|
591
|
+
get: (headers2, key) => {
|
|
592
|
+
if (!headers2) return void 0;
|
|
593
|
+
const all = typeof headers2.values === "function" ? headers2.values(key) : void 0;
|
|
594
|
+
if (Array.isArray(all)) {
|
|
595
|
+
const nonEmpty = all.filter((value) => value !== "");
|
|
596
|
+
if (nonEmpty.length === 0) return void 0;
|
|
597
|
+
return nonEmpty.join(",");
|
|
598
|
+
}
|
|
599
|
+
const single = headers2.get(key);
|
|
600
|
+
return single === "" ? void 0 : single;
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
// src/otel/attributes.ts
|
|
605
|
+
var MESSAGING_SYSTEM = "nats";
|
|
606
|
+
var baseMessagingAttributes = (ctx) => {
|
|
607
|
+
const attrs = {
|
|
608
|
+
[ATTR_MESSAGING_SYSTEM]: MESSAGING_SYSTEM,
|
|
609
|
+
[ATTR_MESSAGING_DESTINATION_NAME]: ctx.subject,
|
|
610
|
+
[ATTR_MESSAGING_CLIENT_ID]: ctx.serviceName
|
|
611
|
+
};
|
|
612
|
+
if (ctx.serverAddress) attrs[ATTR_SERVER_ADDRESS] = ctx.serverAddress;
|
|
613
|
+
if (ctx.serverPort !== void 0) attrs[ATTR_SERVER_PORT] = ctx.serverPort;
|
|
614
|
+
if (ctx.pattern && ctx.pattern !== ctx.subject) {
|
|
615
|
+
attrs[ATTR_MESSAGING_DESTINATION_TEMPLATE] = ctx.pattern;
|
|
616
|
+
}
|
|
617
|
+
return attrs;
|
|
618
|
+
};
|
|
619
|
+
var jetstreamKindForPublish = (kind) => kind === "rpc.request" /* RpcRequest */ ? "rpc" /* Rpc */ : kind;
|
|
620
|
+
var buildPublishAttributes = (ctx) => {
|
|
621
|
+
const attrs = {
|
|
622
|
+
...baseMessagingAttributes(ctx),
|
|
623
|
+
[ATTR_MESSAGING_OPERATION_NAME]: SPAN_NAME_PUBLISH,
|
|
624
|
+
[ATTR_MESSAGING_OPERATION_TYPE]: SPAN_NAME_SEND,
|
|
625
|
+
[ATTR_MESSAGING_MESSAGE_BODY_SIZE]: ctx.payloadBytes,
|
|
626
|
+
[ATTR_JETSTREAM_KIND]: jetstreamKindForPublish(ctx.kind)
|
|
627
|
+
};
|
|
628
|
+
if (ctx.messageId) attrs[ATTR_MESSAGING_MESSAGE_ID] = ctx.messageId;
|
|
629
|
+
if (ctx.correlationId) attrs[ATTR_MESSAGING_MESSAGE_CONVERSATION_ID] = ctx.correlationId;
|
|
630
|
+
return attrs;
|
|
631
|
+
};
|
|
632
|
+
var buildConsumeAttributes = (ctx) => {
|
|
633
|
+
const { msg, info, kind, payloadBytes } = ctx;
|
|
634
|
+
const attrs = {
|
|
635
|
+
...baseMessagingAttributes(ctx),
|
|
636
|
+
[ATTR_MESSAGING_OPERATION_NAME]: SPAN_NAME_PROCESS,
|
|
637
|
+
[ATTR_MESSAGING_OPERATION_TYPE]: SPAN_NAME_PROCESS,
|
|
638
|
+
[ATTR_MESSAGING_MESSAGE_BODY_SIZE]: payloadBytes,
|
|
639
|
+
[ATTR_JETSTREAM_KIND]: kind
|
|
640
|
+
};
|
|
641
|
+
if (info) {
|
|
642
|
+
attrs[ATTR_MESSAGING_NATS_STREAM_NAME] = info.stream;
|
|
643
|
+
attrs[ATTR_MESSAGING_CONSUMER_GROUP_NAME] = info.consumer;
|
|
644
|
+
attrs[ATTR_MESSAGING_NATS_STREAM_SEQUENCE] = info.streamSequence;
|
|
645
|
+
attrs[ATTR_MESSAGING_NATS_CONSUMER_SEQUENCE] = info.deliverySequence;
|
|
646
|
+
attrs[ATTR_MESSAGING_NATS_DELIVERY_COUNT] = info.deliveryCount;
|
|
647
|
+
}
|
|
648
|
+
const messageId = msg.headers?.get(NATS_MSG_ID_HEADER);
|
|
649
|
+
if (messageId) attrs[ATTR_MESSAGING_MESSAGE_ID] = messageId;
|
|
650
|
+
return attrs;
|
|
651
|
+
};
|
|
652
|
+
var buildRpcClientAttributes = (ctx) => {
|
|
653
|
+
const attrs = {
|
|
654
|
+
...baseMessagingAttributes(ctx),
|
|
655
|
+
[ATTR_MESSAGING_OPERATION_NAME]: SPAN_NAME_SEND,
|
|
656
|
+
[ATTR_MESSAGING_OPERATION_TYPE]: SPAN_NAME_SEND,
|
|
657
|
+
[ATTR_MESSAGING_MESSAGE_BODY_SIZE]: ctx.payloadBytes,
|
|
658
|
+
[ATTR_JETSTREAM_KIND]: "rpc" /* Rpc */
|
|
659
|
+
};
|
|
660
|
+
if (ctx.correlationId) attrs[ATTR_MESSAGING_MESSAGE_CONVERSATION_ID] = ctx.correlationId;
|
|
661
|
+
if (ctx.messageId) attrs[ATTR_MESSAGING_MESSAGE_ID] = ctx.messageId;
|
|
662
|
+
return attrs;
|
|
663
|
+
};
|
|
664
|
+
var buildDeadLetterAttributes = (ctx) => {
|
|
665
|
+
const attrs = {
|
|
666
|
+
...baseMessagingAttributes(ctx),
|
|
667
|
+
[ATTR_MESSAGING_OPERATION_NAME]: SPAN_NAME_DEAD_LETTER,
|
|
668
|
+
[ATTR_MESSAGING_OPERATION_TYPE]: SPAN_NAME_PROCESS,
|
|
669
|
+
[ATTR_MESSAGING_NATS_DELIVERY_COUNT]: ctx.finalDeliveryCount
|
|
670
|
+
};
|
|
671
|
+
if (ctx.reason) attrs[ATTR_JETSTREAM_DEAD_LETTER_REASON] = ctx.reason;
|
|
672
|
+
return attrs;
|
|
673
|
+
};
|
|
674
|
+
var extractFromRpcException = (record) => {
|
|
675
|
+
const getError = record.getError;
|
|
676
|
+
if (typeof getError !== "function") return void 0;
|
|
677
|
+
return codeFromPayload(getError.call(record));
|
|
678
|
+
};
|
|
679
|
+
var extractFromHttpException = (record) => {
|
|
680
|
+
const getStatus = record.getStatus;
|
|
681
|
+
if (typeof getStatus !== "function") return void 0;
|
|
682
|
+
const status = getStatus.call(record);
|
|
683
|
+
return typeof status === "number" && Number.isFinite(status) ? `HTTP_${status}` : void 0;
|
|
684
|
+
};
|
|
685
|
+
var extractFromOwnCode = (record) => {
|
|
686
|
+
const code = record.code ?? record.errorCode;
|
|
687
|
+
return typeof code === "string" && isStableErrorCode(code) ? code : void 0;
|
|
688
|
+
};
|
|
689
|
+
var extractExpectedErrorCode = (err) => {
|
|
690
|
+
if (err === null || typeof err !== "object") return void 0;
|
|
691
|
+
const record = err;
|
|
692
|
+
if (hasAncestorNamed(err, "RpcException")) {
|
|
693
|
+
const fromPayload = extractFromRpcException(record);
|
|
694
|
+
if (fromPayload !== void 0) return fromPayload;
|
|
695
|
+
}
|
|
696
|
+
if (hasAncestorNamed(err, "HttpException")) {
|
|
697
|
+
const fromStatus = extractFromHttpException(record);
|
|
698
|
+
if (fromStatus !== void 0) return fromStatus;
|
|
699
|
+
}
|
|
700
|
+
return extractFromOwnCode(record);
|
|
701
|
+
};
|
|
702
|
+
var hasAncestorNamed = (err, name) => {
|
|
703
|
+
let proto = Object.getPrototypeOf(err);
|
|
704
|
+
while (proto) {
|
|
705
|
+
const ctorName = proto.constructor?.name;
|
|
706
|
+
if (ctorName === name) return true;
|
|
707
|
+
proto = Object.getPrototypeOf(proto);
|
|
708
|
+
}
|
|
709
|
+
return false;
|
|
710
|
+
};
|
|
711
|
+
var STABLE_ERROR_CODE_RE = /^[A-Z][A-Z0-9_]*$/u;
|
|
712
|
+
var isStableErrorCode = (value) => STABLE_ERROR_CODE_RE.test(value);
|
|
713
|
+
var codeFromPayload = (payload) => {
|
|
714
|
+
if (payload === null || payload === void 0) return void 0;
|
|
715
|
+
if (typeof payload === "string") return isStableErrorCode(payload) ? payload : void 0;
|
|
716
|
+
if (typeof payload === "object") {
|
|
717
|
+
const code = payload.code;
|
|
718
|
+
if (typeof code === "string" && isStableErrorCode(code)) return code;
|
|
719
|
+
}
|
|
720
|
+
return void 0;
|
|
721
|
+
};
|
|
722
|
+
var buildExpectedErrorAttributes = (err) => {
|
|
723
|
+
const attrs = {
|
|
724
|
+
[ATTR_JETSTREAM_RPC_REPLY_HAS_ERROR]: true
|
|
725
|
+
};
|
|
726
|
+
const code = extractExpectedErrorCode(err);
|
|
727
|
+
if (code !== void 0) attrs[ATTR_JETSTREAM_RPC_REPLY_ERROR_CODE] = code;
|
|
728
|
+
return attrs;
|
|
729
|
+
};
|
|
730
|
+
var applyExpectedErrorAttributes = (span, err) => {
|
|
731
|
+
span.setAttributes(buildExpectedErrorAttributes(err));
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
// src/otel/spans/publish.ts
|
|
735
|
+
var import_common2 = require("@nestjs/common");
|
|
736
|
+
var import_api3 = require("@opentelemetry/api");
|
|
737
|
+
var logger2 = new import_common2.Logger("Jetstream:Otel");
|
|
738
|
+
var shouldTracePublishSafe = (predicate, subject, record) => {
|
|
739
|
+
if (!predicate) return true;
|
|
740
|
+
try {
|
|
741
|
+
return predicate(subject, record);
|
|
742
|
+
} catch (err) {
|
|
743
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
744
|
+
logger2.debug(`OTel shouldTracePublish threw: ${message}`);
|
|
745
|
+
return true;
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
var withPublishSpan = async (ctx, config, fn) => {
|
|
749
|
+
if (!config.enabled) return fn();
|
|
750
|
+
const shouldCreateSpan = config.traces.has("publish" /* Publish */) && shouldTracePublishSafe(config.shouldTracePublish, ctx.subject, ctx.record);
|
|
751
|
+
if (!shouldCreateSpan) {
|
|
752
|
+
injectContext(import_api3.context.active(), ctx.headers, hdrsSetter);
|
|
753
|
+
return fn();
|
|
754
|
+
}
|
|
755
|
+
const tracer = getTracer();
|
|
756
|
+
const span = tracer.startSpan(`${SPAN_NAME_PUBLISH} ${ctx.subject}`, {
|
|
757
|
+
kind: import_api3.SpanKind.PRODUCER,
|
|
758
|
+
attributes: {
|
|
759
|
+
...buildPublishAttributes({
|
|
760
|
+
subject: ctx.subject,
|
|
761
|
+
pattern: ctx.pattern,
|
|
762
|
+
serviceName: ctx.serviceName,
|
|
763
|
+
serverAddress: ctx.endpoint?.host,
|
|
764
|
+
serverPort: ctx.endpoint?.port,
|
|
765
|
+
kind: ctx.kind,
|
|
766
|
+
payloadBytes: ctx.payloadBytes,
|
|
767
|
+
messageId: ctx.messageId,
|
|
768
|
+
correlationId: ctx.correlationId
|
|
769
|
+
}),
|
|
770
|
+
...ctx.scheduleTarget ? { [ATTR_JETSTREAM_SCHEDULE_TARGET]: ctx.scheduleTarget } : {},
|
|
771
|
+
...captureMatchingHeaders(ctx.headers, config.captureHeaders),
|
|
772
|
+
...captureBodyAttribute(ctx.subject, ctx.payload, config.captureBody)
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
const ctxWithSpan = import_api3.trace.setSpan(import_api3.context.active(), span);
|
|
776
|
+
injectContext(ctxWithSpan, ctx.headers, hdrsSetter);
|
|
777
|
+
const start = Date.now();
|
|
778
|
+
const invokeResponseHook = (error) => {
|
|
779
|
+
import_api3.context.with(ctxWithSpan, () => {
|
|
780
|
+
safelyInvokeHook(HOOK_RESPONSE, config.responseHook, span, {
|
|
781
|
+
subject: ctx.subject,
|
|
782
|
+
durationMs: Date.now() - start,
|
|
783
|
+
error
|
|
784
|
+
});
|
|
785
|
+
});
|
|
786
|
+
};
|
|
787
|
+
try {
|
|
788
|
+
const result = await import_api3.context.with(ctxWithSpan, async () => {
|
|
789
|
+
safelyInvokeHook(HOOK_PUBLISH, config.publishHook, span, {
|
|
790
|
+
subject: ctx.subject,
|
|
791
|
+
record: ctx.record,
|
|
792
|
+
kind: ctx.kind
|
|
793
|
+
});
|
|
794
|
+
return fn();
|
|
795
|
+
});
|
|
796
|
+
span.setStatus({ code: import_api3.SpanStatusCode.OK });
|
|
797
|
+
invokeResponseHook();
|
|
798
|
+
return result;
|
|
799
|
+
} catch (err) {
|
|
800
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
801
|
+
span.recordException(error);
|
|
802
|
+
span.setStatus({ code: import_api3.SpanStatusCode.ERROR, message: error.message });
|
|
803
|
+
invokeResponseHook(error);
|
|
804
|
+
throw err;
|
|
805
|
+
} finally {
|
|
806
|
+
span.end();
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
|
|
810
|
+
// src/otel/spans/consume.ts
|
|
811
|
+
var import_api4 = require("@opentelemetry/api");
|
|
812
|
+
var isPromiseLike = (value) => typeof value === "object" && value !== null && typeof value.then === "function";
|
|
813
|
+
var applyExpectedError = (span, err) => {
|
|
814
|
+
span.setStatus({ code: import_api4.SpanStatusCode.OK });
|
|
815
|
+
applyExpectedErrorAttributes(span, err);
|
|
816
|
+
};
|
|
817
|
+
var applyUnexpectedError = (span, err) => {
|
|
818
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
819
|
+
span.recordException(error);
|
|
820
|
+
span.setStatus({ code: import_api4.SpanStatusCode.ERROR, message: error.message });
|
|
821
|
+
};
|
|
822
|
+
var withConsumeSpan = (ctx, config, fn, options = {}) => {
|
|
823
|
+
if (!config.enabled) return fn();
|
|
824
|
+
const parentCtx = extractContext(import_api4.ROOT_CONTEXT, ctx.msg.headers, hdrsGetter);
|
|
825
|
+
const shouldCreateSpan = config.traces.has("consume" /* Consume */) && (config.shouldTraceConsume?.(ctx.subject, ctx.msg) ?? true);
|
|
826
|
+
if (!shouldCreateSpan) {
|
|
827
|
+
return import_api4.context.with(parentCtx, fn);
|
|
828
|
+
}
|
|
829
|
+
const tracer = getTracer();
|
|
830
|
+
const span = tracer.startSpan(
|
|
831
|
+
`${SPAN_NAME_PROCESS} ${ctx.subject}`,
|
|
832
|
+
{
|
|
833
|
+
kind: import_api4.SpanKind.CONSUMER,
|
|
834
|
+
attributes: {
|
|
835
|
+
...buildConsumeAttributes({
|
|
836
|
+
subject: ctx.subject,
|
|
837
|
+
pattern: ctx.pattern,
|
|
838
|
+
msg: ctx.msg,
|
|
839
|
+
info: ctx.info,
|
|
840
|
+
kind: ctx.kind,
|
|
841
|
+
payloadBytes: ctx.payloadBytes,
|
|
842
|
+
serviceName: ctx.serviceName,
|
|
843
|
+
serverAddress: ctx.endpoint?.host,
|
|
844
|
+
serverPort: ctx.endpoint?.port
|
|
845
|
+
}),
|
|
846
|
+
...captureMatchingHeaders(ctx.msg.headers, config.captureHeaders),
|
|
847
|
+
...captureBodyAttribute(ctx.subject, ctx.msg.data, config.captureBody)
|
|
848
|
+
}
|
|
849
|
+
},
|
|
850
|
+
parentCtx
|
|
851
|
+
);
|
|
852
|
+
const ctxWithSpan = import_api4.trace.setSpan(parentCtx, span);
|
|
853
|
+
const start = Date.now();
|
|
854
|
+
let finalized = false;
|
|
855
|
+
const { signal, timeoutLabel = "handler.timeout" } = options;
|
|
856
|
+
let detachAbort = null;
|
|
857
|
+
const invokeResponseHook = (durationMs, error) => {
|
|
858
|
+
import_api4.context.with(ctxWithSpan, () => {
|
|
859
|
+
safelyInvokeHook(HOOK_RESPONSE, config.responseHook, span, {
|
|
860
|
+
subject: ctx.subject,
|
|
861
|
+
durationMs,
|
|
862
|
+
error
|
|
863
|
+
});
|
|
864
|
+
});
|
|
865
|
+
};
|
|
866
|
+
const finishOk2 = () => {
|
|
867
|
+
if (finalized) return;
|
|
868
|
+
finalized = true;
|
|
869
|
+
detachAbort?.();
|
|
870
|
+
span.setStatus({ code: import_api4.SpanStatusCode.OK });
|
|
871
|
+
invokeResponseHook(Date.now() - start);
|
|
872
|
+
span.end();
|
|
873
|
+
};
|
|
874
|
+
const finishError2 = (err) => {
|
|
875
|
+
if (finalized) return;
|
|
876
|
+
finalized = true;
|
|
877
|
+
detachAbort?.();
|
|
878
|
+
let classification = "unexpected";
|
|
879
|
+
try {
|
|
880
|
+
classification = config.errorClassifier(err);
|
|
881
|
+
} catch {
|
|
882
|
+
}
|
|
883
|
+
if (classification === "expected") {
|
|
884
|
+
applyExpectedError(span, err);
|
|
885
|
+
} else {
|
|
886
|
+
applyUnexpectedError(span, err);
|
|
887
|
+
}
|
|
888
|
+
invokeResponseHook(Date.now() - start, err instanceof Error ? err : new Error(String(err)));
|
|
889
|
+
span.end();
|
|
890
|
+
};
|
|
891
|
+
const onAbort = () => {
|
|
892
|
+
if (finalized) return;
|
|
893
|
+
finalized = true;
|
|
894
|
+
const error = new Error(timeoutLabel);
|
|
895
|
+
span.addEvent(timeoutLabel);
|
|
896
|
+
span.recordException(error);
|
|
897
|
+
span.setStatus({ code: import_api4.SpanStatusCode.ERROR, message: timeoutLabel });
|
|
898
|
+
invokeResponseHook(Date.now() - start, error);
|
|
899
|
+
span.end();
|
|
900
|
+
};
|
|
901
|
+
if (signal) {
|
|
902
|
+
if (signal.aborted) {
|
|
903
|
+
onAbort();
|
|
904
|
+
} else {
|
|
905
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
906
|
+
detachAbort = () => {
|
|
907
|
+
signal.removeEventListener("abort", onAbort);
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
let result;
|
|
912
|
+
try {
|
|
913
|
+
result = import_api4.context.with(ctxWithSpan, () => {
|
|
914
|
+
safelyInvokeHook(HOOK_CONSUME, config.consumeHook, span, {
|
|
915
|
+
subject: ctx.subject,
|
|
916
|
+
msg: ctx.msg,
|
|
917
|
+
handlerMetadata: ctx.handlerMetadata,
|
|
918
|
+
kind: ctx.kind
|
|
919
|
+
});
|
|
920
|
+
return fn();
|
|
921
|
+
});
|
|
922
|
+
} catch (err) {
|
|
923
|
+
finishError2(err);
|
|
924
|
+
throw err;
|
|
925
|
+
}
|
|
926
|
+
if (isPromiseLike(result)) {
|
|
927
|
+
return Promise.resolve(result).then(
|
|
928
|
+
(value) => {
|
|
929
|
+
finishOk2();
|
|
930
|
+
return value;
|
|
931
|
+
},
|
|
932
|
+
(err) => {
|
|
933
|
+
finishError2(err);
|
|
934
|
+
throw err;
|
|
935
|
+
}
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
finishOk2();
|
|
939
|
+
return result;
|
|
940
|
+
};
|
|
941
|
+
|
|
942
|
+
// src/otel/spans/rpc-client.ts
|
|
943
|
+
var import_common3 = require("@nestjs/common");
|
|
944
|
+
var import_api5 = require("@opentelemetry/api");
|
|
945
|
+
var logger3 = new import_common3.Logger("Jetstream:Otel");
|
|
946
|
+
var RPC_TIMEOUT_MESSAGE = "rpc.timeout";
|
|
947
|
+
var beginRpcClientSpan = (ctx, config) => {
|
|
948
|
+
if (!config.enabled) {
|
|
949
|
+
return {
|
|
950
|
+
activeContext: import_api5.context.active(),
|
|
951
|
+
finish: () => void 0
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
if (!config.traces.has("rpc.client.send" /* RpcClientSend */)) {
|
|
955
|
+
injectContext(import_api5.context.active(), ctx.headers, hdrsSetter);
|
|
956
|
+
return {
|
|
957
|
+
activeContext: import_api5.context.active(),
|
|
958
|
+
finish: () => void 0
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
const tracer = getTracer();
|
|
962
|
+
const span = tracer.startSpan(`${SPAN_NAME_SEND} ${ctx.subject}`, {
|
|
963
|
+
kind: import_api5.SpanKind.CLIENT,
|
|
964
|
+
attributes: {
|
|
965
|
+
...buildRpcClientAttributes({
|
|
966
|
+
subject: ctx.subject,
|
|
967
|
+
pattern: ctx.pattern,
|
|
968
|
+
correlationId: ctx.correlationId,
|
|
969
|
+
payloadBytes: ctx.payloadBytes,
|
|
970
|
+
messageId: ctx.messageId,
|
|
971
|
+
serviceName: ctx.serviceName,
|
|
972
|
+
serverAddress: ctx.endpoint?.host,
|
|
973
|
+
serverPort: ctx.endpoint?.port
|
|
974
|
+
}),
|
|
975
|
+
...captureMatchingHeaders(ctx.headers, config.captureHeaders),
|
|
976
|
+
...captureBodyAttribute(ctx.subject, ctx.payload, config.captureBody)
|
|
977
|
+
}
|
|
978
|
+
});
|
|
979
|
+
const ctxWithSpan = import_api5.trace.setSpan(import_api5.context.active(), span);
|
|
980
|
+
injectContext(ctxWithSpan, ctx.headers, hdrsSetter);
|
|
981
|
+
const start = Date.now();
|
|
982
|
+
let finalized = false;
|
|
983
|
+
const finish = (outcome) => {
|
|
984
|
+
if (finalized) return;
|
|
985
|
+
finalized = true;
|
|
986
|
+
let reply;
|
|
987
|
+
let error;
|
|
988
|
+
switch (outcome.kind) {
|
|
989
|
+
case "ok" /* Ok */:
|
|
990
|
+
reply = outcome.reply;
|
|
991
|
+
span.setStatus({ code: import_api5.SpanStatusCode.OK });
|
|
992
|
+
break;
|
|
993
|
+
case "reply-error" /* ReplyError */:
|
|
994
|
+
reply = outcome.replyPayload;
|
|
995
|
+
applyExpectedErrorAttributes(span, outcome.replyPayload);
|
|
996
|
+
span.setStatus({ code: import_api5.SpanStatusCode.OK });
|
|
997
|
+
break;
|
|
998
|
+
case "timeout" /* Timeout */:
|
|
999
|
+
error = new Error(RPC_TIMEOUT_MESSAGE);
|
|
1000
|
+
span.addEvent(RPC_TIMEOUT_MESSAGE);
|
|
1001
|
+
span.setStatus({ code: import_api5.SpanStatusCode.ERROR, message: RPC_TIMEOUT_MESSAGE });
|
|
1002
|
+
break;
|
|
1003
|
+
case "error" /* Error */:
|
|
1004
|
+
error = outcome.error;
|
|
1005
|
+
span.recordException(outcome.error);
|
|
1006
|
+
span.setStatus({ code: import_api5.SpanStatusCode.ERROR, message: outcome.error.message });
|
|
1007
|
+
break;
|
|
1008
|
+
default: {
|
|
1009
|
+
const unknownOutcome = outcome;
|
|
1010
|
+
logger3.error(`Unhandled RPC outcome: ${String(unknownOutcome.kind)}`);
|
|
1011
|
+
span.setStatus({ code: import_api5.SpanStatusCode.ERROR, message: "unknown outcome" });
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
import_api5.context.with(ctxWithSpan, () => {
|
|
1015
|
+
safelyInvokeHook(HOOK_RESPONSE, config.responseHook, span, {
|
|
1016
|
+
subject: ctx.subject,
|
|
1017
|
+
durationMs: Date.now() - start,
|
|
1018
|
+
reply,
|
|
1019
|
+
error
|
|
1020
|
+
});
|
|
1021
|
+
});
|
|
1022
|
+
span.end();
|
|
1023
|
+
};
|
|
1024
|
+
return { activeContext: ctxWithSpan, finish };
|
|
1025
|
+
};
|
|
1026
|
+
|
|
1027
|
+
// src/otel/spans/dead-letter.ts
|
|
1028
|
+
var import_api6 = require("@opentelemetry/api");
|
|
1029
|
+
var withDeadLetterSpan = async (ctx, config, fn) => {
|
|
1030
|
+
if (!config.enabled || !config.traces.has("dead_letter" /* DeadLetter */)) {
|
|
1031
|
+
return fn();
|
|
1032
|
+
}
|
|
1033
|
+
const parentCtx = extractContext(import_api6.ROOT_CONTEXT, ctx.msg.headers, hdrsGetter);
|
|
1034
|
+
const tracer = getTracer();
|
|
1035
|
+
const span = tracer.startSpan(
|
|
1036
|
+
`${SPAN_NAME_DEAD_LETTER} ${ctx.msg.subject}`,
|
|
1037
|
+
{
|
|
1038
|
+
kind: import_api6.SpanKind.INTERNAL,
|
|
1039
|
+
attributes: buildDeadLetterAttributes({
|
|
1040
|
+
subject: ctx.msg.subject,
|
|
1041
|
+
pattern: ctx.pattern,
|
|
1042
|
+
serviceName: ctx.serviceName,
|
|
1043
|
+
serverAddress: ctx.endpoint?.host,
|
|
1044
|
+
serverPort: ctx.endpoint?.port,
|
|
1045
|
+
finalDeliveryCount: ctx.finalDeliveryCount,
|
|
1046
|
+
reason: ctx.reason
|
|
1047
|
+
})
|
|
1048
|
+
},
|
|
1049
|
+
parentCtx
|
|
1050
|
+
);
|
|
1051
|
+
const ctxWithSpan = import_api6.trace.setSpan(parentCtx, span);
|
|
1052
|
+
const start = Date.now();
|
|
1053
|
+
const invokeResponseHook = (error) => {
|
|
1054
|
+
import_api6.context.with(ctxWithSpan, () => {
|
|
1055
|
+
safelyInvokeHook(HOOK_RESPONSE, config.responseHook, span, {
|
|
1056
|
+
subject: ctx.msg.subject,
|
|
1057
|
+
durationMs: Date.now() - start,
|
|
1058
|
+
error
|
|
1059
|
+
});
|
|
1060
|
+
});
|
|
1061
|
+
};
|
|
1062
|
+
try {
|
|
1063
|
+
const result = await import_api6.context.with(ctxWithSpan, fn);
|
|
1064
|
+
span.setStatus({ code: import_api6.SpanStatusCode.OK });
|
|
1065
|
+
invokeResponseHook();
|
|
1066
|
+
return result;
|
|
1067
|
+
} catch (err) {
|
|
1068
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1069
|
+
span.recordException(error);
|
|
1070
|
+
span.setStatus({ code: import_api6.SpanStatusCode.ERROR, message: error.message });
|
|
1071
|
+
invokeResponseHook(error);
|
|
1072
|
+
throw err;
|
|
1073
|
+
} finally {
|
|
1074
|
+
span.end();
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
|
|
1078
|
+
// src/otel/spans/infrastructure.ts
|
|
1079
|
+
var import_common4 = require("@nestjs/common");
|
|
1080
|
+
var import_api7 = require("@opentelemetry/api");
|
|
1081
|
+
var logger4 = new import_common4.Logger("Jetstream:Otel");
|
|
1082
|
+
var startInfraSpan = (config, traceKind, name, ctx, extraAttributes = {}) => {
|
|
1083
|
+
if (!config.enabled || !config.traces.has(traceKind)) return null;
|
|
1084
|
+
const tracer = getTracer();
|
|
1085
|
+
const attributes = {
|
|
1086
|
+
[ATTR_JETSTREAM_SERVICE_NAME]: ctx.serviceName
|
|
1087
|
+
};
|
|
1088
|
+
if (ctx.endpoint?.host) attributes[ATTR_SERVER_ADDRESS] = ctx.endpoint.host;
|
|
1089
|
+
if (ctx.endpoint?.port !== void 0) attributes[ATTR_SERVER_PORT] = ctx.endpoint.port;
|
|
1090
|
+
for (const [key, value] of Object.entries(extraAttributes)) {
|
|
1091
|
+
if (value !== void 0) attributes[key] = value;
|
|
1092
|
+
}
|
|
1093
|
+
return tracer.startSpan(name, { kind: import_api7.SpanKind.INTERNAL, attributes });
|
|
1094
|
+
};
|
|
1095
|
+
var finishOk = (span) => {
|
|
1096
|
+
span.setStatus({ code: import_api7.SpanStatusCode.OK });
|
|
1097
|
+
span.end();
|
|
1098
|
+
};
|
|
1099
|
+
var finishError = (span, err) => {
|
|
1100
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1101
|
+
span.recordException(error);
|
|
1102
|
+
span.setStatus({ code: import_api7.SpanStatusCode.ERROR, message: error.message });
|
|
1103
|
+
span.end();
|
|
1104
|
+
};
|
|
1105
|
+
var wrapInfra = async (config, traceKind, name, ctx, attributes, op) => {
|
|
1106
|
+
const span = startInfraSpan(config, traceKind, name, ctx, attributes);
|
|
1107
|
+
if (!span) return op();
|
|
1108
|
+
const ctxWithSpan = import_api7.trace.setSpan(import_api7.context.active(), span);
|
|
1109
|
+
try {
|
|
1110
|
+
const result = await import_api7.context.with(ctxWithSpan, op);
|
|
1111
|
+
finishOk(span);
|
|
1112
|
+
return result;
|
|
1113
|
+
} catch (err) {
|
|
1114
|
+
finishError(span, err);
|
|
1115
|
+
throw err;
|
|
1116
|
+
}
|
|
1117
|
+
};
|
|
1118
|
+
var beginConnectionLifecycleSpan = (config, ctx) => {
|
|
1119
|
+
const span = startInfraSpan(
|
|
1120
|
+
config,
|
|
1121
|
+
"connection.lifecycle" /* ConnectionLifecycle */,
|
|
1122
|
+
SPAN_NAME_NATS_CONNECTION,
|
|
1123
|
+
ctx,
|
|
1124
|
+
{ [ATTR_NATS_CONNECTION_SERVER]: ctx.server }
|
|
1125
|
+
);
|
|
1126
|
+
if (!span) {
|
|
1127
|
+
return {
|
|
1128
|
+
recordEvent: () => void 0,
|
|
1129
|
+
finish: () => void 0
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
let finalized = false;
|
|
1133
|
+
return {
|
|
1134
|
+
recordEvent: (name, attributes) => {
|
|
1135
|
+
if (finalized) {
|
|
1136
|
+
logger4.debug(`recordEvent('${name}') called after connection span finished`);
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
span.addEvent(name, attributes);
|
|
1140
|
+
},
|
|
1141
|
+
finish: (err) => {
|
|
1142
|
+
if (finalized) return;
|
|
1143
|
+
finalized = true;
|
|
1144
|
+
if (err === void 0) finishOk(span);
|
|
1145
|
+
else finishError(span, err);
|
|
1146
|
+
}
|
|
1147
|
+
};
|
|
1148
|
+
};
|
|
1149
|
+
var withSelfHealingSpan = (config, ctx, op) => wrapInfra(
|
|
1150
|
+
config,
|
|
1151
|
+
"self_healing" /* SelfHealing */,
|
|
1152
|
+
SPAN_NAME_JETSTREAM_SELF_HEALING,
|
|
1153
|
+
ctx,
|
|
1154
|
+
{
|
|
1155
|
+
[ATTR_MESSAGING_NATS_STREAM_NAME]: ctx.stream,
|
|
1156
|
+
[ATTR_MESSAGING_CONSUMER_GROUP_NAME]: ctx.consumer,
|
|
1157
|
+
[ATTR_JETSTREAM_SELF_HEALING_REASON]: ctx.reason
|
|
1158
|
+
},
|
|
1159
|
+
op
|
|
1160
|
+
);
|
|
1161
|
+
var withProvisioningSpan = (config, ctx, op) => wrapInfra(
|
|
1162
|
+
config,
|
|
1163
|
+
"provisioning" /* Provisioning */,
|
|
1164
|
+
`${SPAN_NAME_JETSTREAM_PROVISIONING_PREFIX}${ctx.entity}`,
|
|
1165
|
+
ctx,
|
|
1166
|
+
{
|
|
1167
|
+
[ATTR_JETSTREAM_PROVISIONING_ENTITY]: ctx.entity,
|
|
1168
|
+
[ATTR_JETSTREAM_PROVISIONING_ACTION]: ctx.action,
|
|
1169
|
+
[ATTR_JETSTREAM_PROVISIONING_NAME]: ctx.name
|
|
1170
|
+
},
|
|
1171
|
+
op
|
|
1172
|
+
);
|
|
1173
|
+
var withMigrationSpan = (config, ctx, op) => wrapInfra(
|
|
1174
|
+
config,
|
|
1175
|
+
"migration" /* Migration */,
|
|
1176
|
+
SPAN_NAME_JETSTREAM_MIGRATION,
|
|
1177
|
+
ctx,
|
|
1178
|
+
{
|
|
1179
|
+
[ATTR_MESSAGING_NATS_STREAM_NAME]: ctx.stream,
|
|
1180
|
+
[ATTR_JETSTREAM_MIGRATION_REASON]: ctx.reason
|
|
1181
|
+
},
|
|
1182
|
+
op
|
|
1183
|
+
);
|
|
1184
|
+
var withShutdownSpan = (config, ctx, op) => wrapInfra(config, "shutdown" /* Shutdown */, SPAN_NAME_JETSTREAM_SHUTDOWN, ctx, {}, op);
|
|
1185
|
+
|
|
279
1186
|
// src/client/jetstream.record.ts
|
|
280
1187
|
var JetstreamRecord = class {
|
|
281
1188
|
constructor(data, headers2, timeout, messageId, schedule, ttl) {
|
|
@@ -428,9 +1335,14 @@ var JetstreamRecordBuilder = class {
|
|
|
428
1335
|
this.ttlDuration
|
|
429
1336
|
);
|
|
430
1337
|
}
|
|
431
|
-
/**
|
|
1338
|
+
/**
|
|
1339
|
+
* Validate that a header key is not reserved. NATS treats header names
|
|
1340
|
+
* case-insensitively, so the check is against the lowercase form to keep
|
|
1341
|
+
* `'X-Correlation-ID'`, `'x-correlation-id'`, and any other casing in
|
|
1342
|
+
* lockstep. `RESERVED_HEADERS` is defined as an all-lowercase set.
|
|
1343
|
+
*/
|
|
432
1344
|
validateHeaderKey(key) {
|
|
433
|
-
if (RESERVED_HEADERS.has(key)) {
|
|
1345
|
+
if (RESERVED_HEADERS.has(key.toLowerCase())) {
|
|
434
1346
|
throw new Error(
|
|
435
1347
|
`Header "${key}" is reserved by the JetStream transport and cannot be set manually. Reserved headers: ${[...RESERVED_HEADERS].join(", ")}`
|
|
436
1348
|
);
|
|
@@ -451,6 +1363,11 @@ var nanosToGoDuration = (nanos) => {
|
|
|
451
1363
|
|
|
452
1364
|
// src/client/jetstream.client.ts
|
|
453
1365
|
var BROADCAST_SUBJECT_PREFIX = "broadcast.";
|
|
1366
|
+
var detectEventKind = (pattern) => {
|
|
1367
|
+
if (pattern.startsWith("broadcast:" /* Broadcast */)) return "broadcast" /* Broadcast */;
|
|
1368
|
+
if (pattern.startsWith("ordered:" /* Ordered */)) return "ordered" /* Ordered */;
|
|
1369
|
+
return "event" /* Event */;
|
|
1370
|
+
};
|
|
454
1371
|
var JetstreamClient = class extends import_microservices.ClientProxy {
|
|
455
1372
|
constructor(rootOptions, targetServiceName, connection, codec, eventBus) {
|
|
456
1373
|
super();
|
|
@@ -466,8 +1383,11 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
|
|
|
466
1383
|
this.orderedSubjectPrefix = `${targetInternal}.${"ordered" /* Ordered */}.`;
|
|
467
1384
|
this.isCoreMode = isCoreRpcMode(this.rootOptions.rpc);
|
|
468
1385
|
this.defaultRpcTimeout = isJetStreamRpcMode(this.rootOptions.rpc) ? this.rootOptions.rpc?.timeout ?? DEFAULT_JETSTREAM_RPC_TIMEOUT : this.rootOptions.rpc?.timeout ?? DEFAULT_RPC_TIMEOUT;
|
|
1386
|
+
const derived = deriveOtelAttrs(this.rootOptions);
|
|
1387
|
+
this.otel = derived.otel;
|
|
1388
|
+
this.serverEndpoint = derived.serverEndpoint;
|
|
469
1389
|
}
|
|
470
|
-
logger = new
|
|
1390
|
+
logger = new import_common5.Logger("Jetstream:Client");
|
|
471
1391
|
/** Target service name this client sends messages to. */
|
|
472
1392
|
targetName;
|
|
473
1393
|
/** Pre-cached caller name derived from rootOptions.name, computed once in constructor. */
|
|
@@ -487,6 +1407,10 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
|
|
|
487
1407
|
*/
|
|
488
1408
|
isCoreMode;
|
|
489
1409
|
defaultRpcTimeout;
|
|
1410
|
+
/** Resolved OpenTelemetry configuration, computed once in the constructor. */
|
|
1411
|
+
otel;
|
|
1412
|
+
/** Server endpoint parts used for `server.address` / `server.port` span attributes. */
|
|
1413
|
+
serverEndpoint;
|
|
490
1414
|
/** Shared inbox for JetStream-mode RPC responses. */
|
|
491
1415
|
inbox = null;
|
|
492
1416
|
inboxSubscription = null;
|
|
@@ -554,33 +1478,55 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
|
|
|
554
1478
|
if (!this.readyForPublish) await this.connect();
|
|
555
1479
|
const { data, hdrs, messageId, schedule, ttl } = this.extractRecordData(packet.data);
|
|
556
1480
|
const eventSubject = this.buildEventSubject(packet.pattern);
|
|
1481
|
+
const publishSubject = schedule ? this.buildScheduleSubject(eventSubject) : eventSubject;
|
|
557
1482
|
const msgHeaders = this.buildHeaders(hdrs, { subject: eventSubject });
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
1483
|
+
const encoded = this.codec.encode(data);
|
|
1484
|
+
const effectiveMsgId = messageId ?? import_nuid.nuid.next();
|
|
1485
|
+
const record = packet.data instanceof JetstreamRecord ? packet.data : new JetstreamRecord(data, /* @__PURE__ */ new Map());
|
|
1486
|
+
await withPublishSpan(
|
|
1487
|
+
{
|
|
1488
|
+
subject: publishSubject,
|
|
1489
|
+
pattern: packet.pattern,
|
|
1490
|
+
record,
|
|
1491
|
+
kind: detectEventKind(packet.pattern),
|
|
1492
|
+
payloadBytes: encoded.length,
|
|
1493
|
+
payload: encoded,
|
|
1494
|
+
messageId: effectiveMsgId,
|
|
561
1495
|
headers: msgHeaders,
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
schedule:
|
|
565
|
-
|
|
566
|
-
|
|
1496
|
+
serviceName: this.callerName,
|
|
1497
|
+
endpoint: this.serverEndpoint,
|
|
1498
|
+
scheduleTarget: schedule ? eventSubject : void 0
|
|
1499
|
+
},
|
|
1500
|
+
this.otel,
|
|
1501
|
+
async () => {
|
|
1502
|
+
const warnIfDuplicate = (kindLabel, ack2) => {
|
|
1503
|
+
if (ack2.duplicate) {
|
|
1504
|
+
this.logger.warn(
|
|
1505
|
+
`Duplicate ${kindLabel} publish detected: ${publishSubject} (seq: ${ack2.seq})`
|
|
1506
|
+
);
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
1509
|
+
if (schedule) {
|
|
1510
|
+
const ack2 = await this.connection.getJetStreamClient().publish(publishSubject, encoded, {
|
|
1511
|
+
headers: msgHeaders,
|
|
1512
|
+
msgID: effectiveMsgId,
|
|
1513
|
+
ttl,
|
|
1514
|
+
schedule: {
|
|
1515
|
+
specification: schedule.at,
|
|
1516
|
+
target: eventSubject
|
|
1517
|
+
}
|
|
1518
|
+
});
|
|
1519
|
+
warnIfDuplicate("scheduled", ack2);
|
|
1520
|
+
return;
|
|
567
1521
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
);
|
|
573
|
-
|
|
574
|
-
} else {
|
|
575
|
-
const ack = await this.connection.getJetStreamClient().publish(eventSubject, this.codec.encode(data), {
|
|
576
|
-
headers: msgHeaders,
|
|
577
|
-
msgID: messageId ?? import_nuid.nuid.next(),
|
|
578
|
-
ttl
|
|
579
|
-
});
|
|
580
|
-
if (ack.duplicate) {
|
|
581
|
-
this.logger.warn(`Duplicate event publish detected: ${eventSubject} (seq: ${ack.seq})`);
|
|
1522
|
+
const ack = await this.connection.getJetStreamClient().publish(publishSubject, encoded, {
|
|
1523
|
+
headers: msgHeaders,
|
|
1524
|
+
msgID: effectiveMsgId,
|
|
1525
|
+
ttl
|
|
1526
|
+
});
|
|
1527
|
+
warnIfDuplicate("event", ack);
|
|
582
1528
|
}
|
|
583
|
-
|
|
1529
|
+
);
|
|
584
1530
|
return void 0;
|
|
585
1531
|
}
|
|
586
1532
|
/**
|
|
@@ -631,24 +1577,46 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
|
|
|
631
1577
|
}
|
|
632
1578
|
/** Core mode: nc.request() with timeout. */
|
|
633
1579
|
async publishCoreRpc(subject, data, customHeaders, timeout, callback) {
|
|
1580
|
+
const effectiveTimeout = timeout ?? this.defaultRpcTimeout;
|
|
1581
|
+
const hdrs = this.buildHeaders(customHeaders, { subject });
|
|
1582
|
+
const encoded = this.codec.encode(data);
|
|
1583
|
+
const spanHandle = beginRpcClientSpan(
|
|
1584
|
+
{
|
|
1585
|
+
subject,
|
|
1586
|
+
payloadBytes: encoded.length,
|
|
1587
|
+
payload: encoded,
|
|
1588
|
+
headers: hdrs,
|
|
1589
|
+
serviceName: this.callerName,
|
|
1590
|
+
endpoint: this.serverEndpoint
|
|
1591
|
+
},
|
|
1592
|
+
this.otel
|
|
1593
|
+
);
|
|
634
1594
|
try {
|
|
635
1595
|
const nc = this.readyForPublish ? this.connection.unwrap : await this.connect();
|
|
636
|
-
const
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
1596
|
+
const response = await import_api8.context.with(
|
|
1597
|
+
spanHandle.activeContext,
|
|
1598
|
+
() => nc.request(subject, encoded, {
|
|
1599
|
+
timeout: effectiveTimeout,
|
|
1600
|
+
headers: hdrs
|
|
1601
|
+
})
|
|
1602
|
+
);
|
|
642
1603
|
const decoded = this.codec.decode(response.data);
|
|
643
1604
|
if (response.headers?.get("x-error" /* Error */)) {
|
|
1605
|
+
spanHandle.finish({ kind: "reply-error" /* ReplyError */, replyPayload: decoded });
|
|
644
1606
|
callback({ err: decoded, response: null, isDisposed: true });
|
|
645
1607
|
} else {
|
|
1608
|
+
spanHandle.finish({ kind: "ok" /* Ok */, reply: decoded });
|
|
646
1609
|
callback({ err: null, response: decoded, isDisposed: true });
|
|
647
1610
|
}
|
|
648
1611
|
} catch (err) {
|
|
649
1612
|
const error = err instanceof Error ? err : new Error("Unknown error");
|
|
650
|
-
|
|
651
|
-
|
|
1613
|
+
if (error instanceof import_transport_node.TimeoutError) {
|
|
1614
|
+
spanHandle.finish({ kind: "timeout" /* Timeout */ });
|
|
1615
|
+
this.eventBus.emit("rpcTimeout" /* RpcTimeout */, subject, "");
|
|
1616
|
+
} else {
|
|
1617
|
+
spanHandle.finish({ kind: "error" /* Error */, error });
|
|
1618
|
+
this.eventBus.emit("error" /* Error */, error, "client-rpc");
|
|
1619
|
+
}
|
|
652
1620
|
callback({ err: error, response: null, isDisposed: true });
|
|
653
1621
|
}
|
|
654
1622
|
}
|
|
@@ -656,12 +1624,55 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
|
|
|
656
1624
|
async publishJetStreamRpc(subject, data, callback, options) {
|
|
657
1625
|
const { headers: customHeaders, correlationId, messageId } = options;
|
|
658
1626
|
const effectiveTimeout = options.timeout ?? this.defaultRpcTimeout;
|
|
659
|
-
this.
|
|
1627
|
+
const hdrs = this.buildHeaders(customHeaders, {
|
|
1628
|
+
subject,
|
|
1629
|
+
correlationId,
|
|
1630
|
+
replyTo: this.inbox ?? ""
|
|
1631
|
+
});
|
|
1632
|
+
const encoded = this.codec.encode(data);
|
|
1633
|
+
const spanHandle = beginRpcClientSpan(
|
|
1634
|
+
{
|
|
1635
|
+
subject,
|
|
1636
|
+
correlationId,
|
|
1637
|
+
payloadBytes: encoded.length,
|
|
1638
|
+
payload: encoded,
|
|
1639
|
+
messageId,
|
|
1640
|
+
headers: hdrs,
|
|
1641
|
+
serviceName: this.callerName,
|
|
1642
|
+
endpoint: this.serverEndpoint
|
|
1643
|
+
},
|
|
1644
|
+
this.otel
|
|
1645
|
+
);
|
|
1646
|
+
this.pendingMessages.set(correlationId, (packet) => {
|
|
1647
|
+
if (packet.err) {
|
|
1648
|
+
if (packet.err instanceof Error) {
|
|
1649
|
+
spanHandle.finish({ kind: "error" /* Error */, error: packet.err });
|
|
1650
|
+
} else {
|
|
1651
|
+
spanHandle.finish({ kind: "reply-error" /* ReplyError */, replyPayload: packet.err });
|
|
1652
|
+
}
|
|
1653
|
+
} else {
|
|
1654
|
+
spanHandle.finish({ kind: "ok" /* Ok */, reply: packet.response });
|
|
1655
|
+
}
|
|
1656
|
+
callback(packet);
|
|
1657
|
+
});
|
|
1658
|
+
const timeoutId = setTimeout(() => {
|
|
1659
|
+
if (!this.pendingMessages.has(correlationId)) return;
|
|
1660
|
+
this.pendingTimeouts.delete(correlationId);
|
|
1661
|
+
this.pendingMessages.delete(correlationId);
|
|
1662
|
+
spanHandle.finish({ kind: "timeout" /* Timeout */ });
|
|
1663
|
+
this.eventBus.emit("rpcTimeout" /* RpcTimeout */, subject, correlationId);
|
|
1664
|
+
callback({ err: new Error(RPC_TIMEOUT_MESSAGE), response: null, isDisposed: true });
|
|
1665
|
+
}, effectiveTimeout);
|
|
1666
|
+
this.pendingTimeouts.set(correlationId, timeoutId);
|
|
660
1667
|
try {
|
|
661
1668
|
if (!this.readyForPublish) await this.connect();
|
|
662
1669
|
if (!this.pendingMessages.has(correlationId)) return;
|
|
663
1670
|
if (!this.inbox) {
|
|
1671
|
+
clearTimeout(timeoutId);
|
|
1672
|
+
this.pendingTimeouts.delete(correlationId);
|
|
664
1673
|
this.pendingMessages.delete(correlationId);
|
|
1674
|
+
const inboxError = new Error("Inbox not initialized");
|
|
1675
|
+
spanHandle.finish({ kind: "error" /* Error */, error: inboxError });
|
|
665
1676
|
callback({
|
|
666
1677
|
err: new Error("Inbox not initialized \u2014 JetStream RPC mode requires a connected inbox"),
|
|
667
1678
|
response: null,
|
|
@@ -669,24 +1680,13 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
|
|
|
669
1680
|
});
|
|
670
1681
|
return;
|
|
671
1682
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
this.
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
}, effectiveTimeout);
|
|
680
|
-
this.pendingTimeouts.set(correlationId, timeoutId);
|
|
681
|
-
const hdrs = this.buildHeaders(customHeaders, {
|
|
682
|
-
subject,
|
|
683
|
-
correlationId,
|
|
684
|
-
replyTo: this.inbox
|
|
685
|
-
});
|
|
686
|
-
await this.connection.getJetStreamClient().publish(subject, this.codec.encode(data), {
|
|
687
|
-
headers: hdrs,
|
|
688
|
-
msgID: messageId ?? import_nuid.nuid.next()
|
|
689
|
-
});
|
|
1683
|
+
await import_api8.context.with(
|
|
1684
|
+
spanHandle.activeContext,
|
|
1685
|
+
() => this.connection.getJetStreamClient().publish(subject, encoded, {
|
|
1686
|
+
headers: hdrs,
|
|
1687
|
+
msgID: messageId ?? import_nuid.nuid.next()
|
|
1688
|
+
})
|
|
1689
|
+
);
|
|
690
1690
|
} catch (err) {
|
|
691
1691
|
const existingTimeout = this.pendingTimeouts.get(correlationId);
|
|
692
1692
|
if (existingTimeout) {
|
|
@@ -696,7 +1696,8 @@ var JetstreamClient = class extends import_microservices.ClientProxy {
|
|
|
696
1696
|
if (!this.pendingMessages.has(correlationId)) return;
|
|
697
1697
|
this.pendingMessages.delete(correlationId);
|
|
698
1698
|
const error = err instanceof Error ? err : new Error("Unknown error");
|
|
699
|
-
|
|
1699
|
+
spanHandle.finish({ kind: "error" /* Error */, error });
|
|
1700
|
+
this.eventBus.emit("error" /* Error */, error, `jetstream-rpc-publish:${subject}`);
|
|
700
1701
|
callback({ err: error, response: null, isDisposed: true });
|
|
701
1702
|
}
|
|
702
1703
|
}
|
|
@@ -879,9 +1880,9 @@ var MsgpackCodec = class {
|
|
|
879
1880
|
};
|
|
880
1881
|
|
|
881
1882
|
// src/connection/connection.provider.ts
|
|
882
|
-
var
|
|
1883
|
+
var import_common6 = require("@nestjs/common");
|
|
883
1884
|
var import_transport_node2 = require("@nats-io/transport-node");
|
|
884
|
-
var
|
|
1885
|
+
var import_jetstream8 = require("@nats-io/jetstream");
|
|
885
1886
|
var import_rxjs = require("rxjs");
|
|
886
1887
|
var DEFAULT_OPTIONS = {
|
|
887
1888
|
maxReconnectAttempts: -1,
|
|
@@ -891,6 +1892,10 @@ var ConnectionProvider = class {
|
|
|
891
1892
|
constructor(options, eventBus) {
|
|
892
1893
|
this.options = options;
|
|
893
1894
|
this.eventBus = eventBus;
|
|
1895
|
+
const derived = deriveOtelAttrs(options);
|
|
1896
|
+
this.otel = derived.otel;
|
|
1897
|
+
this.otelServiceName = derived.serviceName;
|
|
1898
|
+
this.otelEndpoint = derived.serverEndpoint;
|
|
894
1899
|
this.nc$ = (0, import_rxjs.defer)(() => this.getConnection()).pipe(
|
|
895
1900
|
(0, import_rxjs.shareReplay)({ bufferSize: 1, refCount: false })
|
|
896
1901
|
);
|
|
@@ -903,12 +1908,16 @@ var ConnectionProvider = class {
|
|
|
903
1908
|
nc$;
|
|
904
1909
|
/** Live stream of connection status events (no replay). */
|
|
905
1910
|
status$;
|
|
906
|
-
logger = new
|
|
1911
|
+
logger = new import_common6.Logger("Jetstream:Connection");
|
|
907
1912
|
connection = null;
|
|
908
1913
|
connectionPromise = null;
|
|
909
1914
|
jsClient = null;
|
|
910
1915
|
jsmInstance = null;
|
|
911
1916
|
jsmPromise = null;
|
|
1917
|
+
otel;
|
|
1918
|
+
otelServiceName;
|
|
1919
|
+
otelEndpoint;
|
|
1920
|
+
lifecycleSpan = null;
|
|
912
1921
|
/**
|
|
913
1922
|
* Establish NATS connection. Idempotent — returns cached connection on subsequent calls.
|
|
914
1923
|
*
|
|
@@ -951,7 +1960,7 @@ var ConnectionProvider = class {
|
|
|
951
1960
|
if (!this.connection || this.connection.isClosed()) {
|
|
952
1961
|
throw new Error("Not connected \u2014 call getConnection() before getJetStreamClient()");
|
|
953
1962
|
}
|
|
954
|
-
this.jsClient ??= (0,
|
|
1963
|
+
this.jsClient ??= (0, import_jetstream8.jetstream)(this.connection);
|
|
955
1964
|
return this.jsClient;
|
|
956
1965
|
}
|
|
957
1966
|
/** Direct access to the raw NATS connection, or `null` if not yet connected. */
|
|
@@ -972,14 +1981,24 @@ var ConnectionProvider = class {
|
|
|
972
1981
|
}
|
|
973
1982
|
if (!this.connection || this.connection.isClosed()) return;
|
|
974
1983
|
try {
|
|
975
|
-
await
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1984
|
+
await withShutdownSpan(
|
|
1985
|
+
this.otel,
|
|
1986
|
+
{ serviceName: this.otelServiceName, endpoint: this.otelEndpoint },
|
|
1987
|
+
async () => {
|
|
1988
|
+
try {
|
|
1989
|
+
await this.connection?.drain();
|
|
1990
|
+
await this.connection?.closed();
|
|
1991
|
+
} catch {
|
|
1992
|
+
try {
|
|
1993
|
+
await this.connection?.close();
|
|
1994
|
+
} catch {
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
);
|
|
982
1999
|
} finally {
|
|
2000
|
+
this.lifecycleSpan?.finish();
|
|
2001
|
+
this.lifecycleSpan = null;
|
|
983
2002
|
this.connection = null;
|
|
984
2003
|
this.connectionPromise = null;
|
|
985
2004
|
this.jsClient = null;
|
|
@@ -990,7 +2009,7 @@ var ConnectionProvider = class {
|
|
|
990
2009
|
async initJetStreamManager() {
|
|
991
2010
|
try {
|
|
992
2011
|
const nc = await this.getConnection();
|
|
993
|
-
this.jsmInstance = await (0,
|
|
2012
|
+
this.jsmInstance = await (0, import_jetstream8.jetstreamManager)(nc);
|
|
994
2013
|
this.logger.log("JetStream manager initialized");
|
|
995
2014
|
return this.jsmInstance;
|
|
996
2015
|
} finally {
|
|
@@ -999,17 +2018,25 @@ var ConnectionProvider = class {
|
|
|
999
2018
|
}
|
|
1000
2019
|
/** Internal: establish the physical connection with reconnect monitoring. */
|
|
1001
2020
|
async establish() {
|
|
1002
|
-
const name = internalName(this.options.name);
|
|
1003
2021
|
try {
|
|
1004
2022
|
const nc = await (0, import_transport_node2.connect)({
|
|
1005
2023
|
...DEFAULT_OPTIONS,
|
|
2024
|
+
// Default the NATS connection name to the OTel-derived service name so
|
|
2025
|
+
// `nats server info` lines up with span attributes, but let user-supplied
|
|
2026
|
+
// `connectionOptions.name` win when set.
|
|
2027
|
+
name: this.otelServiceName,
|
|
1006
2028
|
...this.options.connectionOptions,
|
|
1007
|
-
servers: this.options.servers
|
|
1008
|
-
name
|
|
2029
|
+
servers: this.options.servers
|
|
1009
2030
|
});
|
|
1010
2031
|
this.connection = nc;
|
|
1011
2032
|
this.logger.log(`NATS connection established: ${nc.getServer()}`);
|
|
1012
2033
|
this.eventBus.emit("connect" /* Connect */, nc.getServer());
|
|
2034
|
+
this.lifecycleSpan?.finish();
|
|
2035
|
+
this.lifecycleSpan = beginConnectionLifecycleSpan(this.otel, {
|
|
2036
|
+
serviceName: this.otelServiceName,
|
|
2037
|
+
endpoint: this.otelEndpoint,
|
|
2038
|
+
server: nc.getServer()
|
|
2039
|
+
});
|
|
1013
2040
|
this.monitorStatus(nc);
|
|
1014
2041
|
return nc;
|
|
1015
2042
|
} catch (err) {
|
|
@@ -1019,37 +2046,55 @@ var ConnectionProvider = class {
|
|
|
1019
2046
|
throw err;
|
|
1020
2047
|
}
|
|
1021
2048
|
}
|
|
2049
|
+
/** Handle a single `nc.status()` event, emitting hooks and span events. */
|
|
2050
|
+
handleStatusEvent(status, nc) {
|
|
2051
|
+
switch (status.type) {
|
|
2052
|
+
case "disconnect":
|
|
2053
|
+
this.eventBus.emit("disconnect" /* Disconnect */);
|
|
2054
|
+
this.lifecycleSpan?.recordEvent(EVENT_CONNECTION_DISCONNECTED);
|
|
2055
|
+
break;
|
|
2056
|
+
case "reconnect":
|
|
2057
|
+
this.jsClient = null;
|
|
2058
|
+
this.jsmInstance = null;
|
|
2059
|
+
this.jsmPromise = null;
|
|
2060
|
+
this.eventBus.emit("reconnect" /* Reconnect */, nc.getServer());
|
|
2061
|
+
this.lifecycleSpan?.recordEvent(EVENT_CONNECTION_RECONNECTED, {
|
|
2062
|
+
[ATTR_NATS_CONNECTION_SERVER]: nc.getServer()
|
|
2063
|
+
});
|
|
2064
|
+
break;
|
|
2065
|
+
case "error":
|
|
2066
|
+
this.eventBus.emit(
|
|
2067
|
+
"error" /* Error */,
|
|
2068
|
+
status.error,
|
|
2069
|
+
"connection"
|
|
2070
|
+
);
|
|
2071
|
+
break;
|
|
2072
|
+
case "update":
|
|
2073
|
+
case "ldm":
|
|
2074
|
+
case "reconnecting":
|
|
2075
|
+
case "ping":
|
|
2076
|
+
case "staleConnection":
|
|
2077
|
+
case "forceReconnect":
|
|
2078
|
+
case "slowConsumer":
|
|
2079
|
+
case "close":
|
|
2080
|
+
break;
|
|
2081
|
+
default: {
|
|
2082
|
+
const _exhaustive = status;
|
|
2083
|
+
const unknown = _exhaustive.type ?? "unknown";
|
|
2084
|
+
this.logger.warn(`Unhandled NATS status event: ${unknown}`);
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
1022
2088
|
/** Subscribe to connection status events and emit hooks. */
|
|
1023
2089
|
monitorStatus(nc) {
|
|
1024
2090
|
void (async () => {
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
this.eventBus.emit("disconnect" /* Disconnect */);
|
|
1029
|
-
break;
|
|
1030
|
-
case "reconnect":
|
|
1031
|
-
this.jsClient = null;
|
|
1032
|
-
this.jsmInstance = null;
|
|
1033
|
-
this.jsmPromise = null;
|
|
1034
|
-
this.eventBus.emit("reconnect" /* Reconnect */, nc.getServer());
|
|
1035
|
-
break;
|
|
1036
|
-
case "error":
|
|
1037
|
-
this.eventBus.emit(
|
|
1038
|
-
"error" /* Error */,
|
|
1039
|
-
status.error,
|
|
1040
|
-
"connection"
|
|
1041
|
-
);
|
|
1042
|
-
break;
|
|
1043
|
-
case "update":
|
|
1044
|
-
case "ldm":
|
|
1045
|
-
case "reconnecting":
|
|
1046
|
-
case "ping":
|
|
1047
|
-
case "staleConnection":
|
|
1048
|
-
case "forceReconnect":
|
|
1049
|
-
case "slowConsumer":
|
|
1050
|
-
case "close":
|
|
1051
|
-
break;
|
|
2091
|
+
try {
|
|
2092
|
+
for await (const status of nc.status()) {
|
|
2093
|
+
this.handleStatusEvent(status, nc);
|
|
1052
2094
|
}
|
|
2095
|
+
} finally {
|
|
2096
|
+
this.lifecycleSpan?.finish();
|
|
2097
|
+
this.lifecycleSpan = null;
|
|
1053
2098
|
}
|
|
1054
2099
|
})().catch((err) => {
|
|
1055
2100
|
this.logger.error("Status monitor error", err);
|
|
@@ -1061,8 +2106,8 @@ var ConnectionProvider = class {
|
|
|
1061
2106
|
var EventBus = class {
|
|
1062
2107
|
hooks;
|
|
1063
2108
|
logger;
|
|
1064
|
-
constructor(
|
|
1065
|
-
this.logger =
|
|
2109
|
+
constructor(logger5, hooks) {
|
|
2110
|
+
this.logger = logger5;
|
|
1066
2111
|
this.hooks = hooks ?? {};
|
|
1067
2112
|
}
|
|
1068
2113
|
/**
|
|
@@ -1118,12 +2163,12 @@ var EventBus = class {
|
|
|
1118
2163
|
};
|
|
1119
2164
|
|
|
1120
2165
|
// src/health/jetstream.health-indicator.ts
|
|
1121
|
-
var
|
|
2166
|
+
var import_common7 = require("@nestjs/common");
|
|
1122
2167
|
var JetstreamHealthIndicator = class {
|
|
1123
2168
|
constructor(connection) {
|
|
1124
2169
|
this.connection = connection;
|
|
1125
2170
|
}
|
|
1126
|
-
logger = new
|
|
2171
|
+
logger = new import_common7.Logger("Jetstream:Health");
|
|
1127
2172
|
/**
|
|
1128
2173
|
* Plain health status check.
|
|
1129
2174
|
*
|
|
@@ -1180,7 +2225,7 @@ var JetstreamHealthIndicator = class {
|
|
|
1180
2225
|
}
|
|
1181
2226
|
};
|
|
1182
2227
|
JetstreamHealthIndicator = __decorateClass([
|
|
1183
|
-
(0,
|
|
2228
|
+
(0, import_common7.Injectable)()
|
|
1184
2229
|
], JetstreamHealthIndicator);
|
|
1185
2230
|
|
|
1186
2231
|
// src/server/strategy.ts
|
|
@@ -1329,7 +2374,7 @@ var JetstreamStrategy = class extends import_microservices2.Server {
|
|
|
1329
2374
|
};
|
|
1330
2375
|
|
|
1331
2376
|
// src/server/core-rpc.server.ts
|
|
1332
|
-
var
|
|
2377
|
+
var import_common8 = require("@nestjs/common");
|
|
1333
2378
|
var import_transport_node3 = require("@nats-io/transport-node");
|
|
1334
2379
|
|
|
1335
2380
|
// src/context/rpc.context.ts
|
|
@@ -1339,9 +2384,6 @@ var RpcContext = class extends import_microservices3.BaseRpcContext {
|
|
|
1339
2384
|
_retryDelay;
|
|
1340
2385
|
_shouldTerminate = false;
|
|
1341
2386
|
_terminateReason;
|
|
1342
|
-
// ---------------------------------------------------------------------------
|
|
1343
|
-
// Message accessors
|
|
1344
|
-
// ---------------------------------------------------------------------------
|
|
1345
2387
|
/**
|
|
1346
2388
|
* Get the underlying NATS message.
|
|
1347
2389
|
*
|
|
@@ -1376,9 +2418,6 @@ var RpcContext = class extends import_microservices3.BaseRpcContext {
|
|
|
1376
2418
|
isJetStream() {
|
|
1377
2419
|
return "ack" in this.args[0];
|
|
1378
2420
|
}
|
|
1379
|
-
// ---------------------------------------------------------------------------
|
|
1380
|
-
// JetStream metadata (return undefined for Core NATS messages)
|
|
1381
|
-
// ---------------------------------------------------------------------------
|
|
1382
2421
|
/** How many times this message has been delivered. */
|
|
1383
2422
|
getDeliveryCount() {
|
|
1384
2423
|
return this.asJetStream()?.info.deliveryCount;
|
|
@@ -1400,9 +2439,6 @@ var RpcContext = class extends import_microservices3.BaseRpcContext {
|
|
|
1400
2439
|
getCallerName() {
|
|
1401
2440
|
return this.getHeader("x-caller-name" /* CallerName */);
|
|
1402
2441
|
}
|
|
1403
|
-
// ---------------------------------------------------------------------------
|
|
1404
|
-
// Handler-controlled settlement
|
|
1405
|
-
// ---------------------------------------------------------------------------
|
|
1406
2442
|
/**
|
|
1407
2443
|
* Signal the transport to retry (nak) this message instead of acknowledging it.
|
|
1408
2444
|
*
|
|
@@ -1447,9 +2483,6 @@ var RpcContext = class extends import_microservices3.BaseRpcContext {
|
|
|
1447
2483
|
throw new Error(`${method}() is only available for JetStream messages`);
|
|
1448
2484
|
}
|
|
1449
2485
|
}
|
|
1450
|
-
// ---------------------------------------------------------------------------
|
|
1451
|
-
// Transport-facing state (read by EventRouter)
|
|
1452
|
-
// ---------------------------------------------------------------------------
|
|
1453
2486
|
/** @internal */
|
|
1454
2487
|
get shouldRetry() {
|
|
1455
2488
|
return this._shouldRetry;
|
|
@@ -1571,7 +2604,7 @@ var unwrapResult = (result) => {
|
|
|
1571
2604
|
}
|
|
1572
2605
|
return result;
|
|
1573
2606
|
};
|
|
1574
|
-
var
|
|
2607
|
+
var isPromiseLike2 = (value) => value !== null && typeof value === "object" && typeof value.then === "function";
|
|
1575
2608
|
var subscribeToFirst = (obs) => new Promise((resolve, reject) => {
|
|
1576
2609
|
let done = false;
|
|
1577
2610
|
let subscription = null;
|
|
@@ -1601,20 +2634,25 @@ var subscribeToFirst = (obs) => new Promise((resolve, reject) => {
|
|
|
1601
2634
|
// src/server/core-rpc.server.ts
|
|
1602
2635
|
var CoreRpcServer = class {
|
|
1603
2636
|
constructor(options, connection, patternRegistry, codec, eventBus) {
|
|
1604
|
-
this.options = options;
|
|
1605
2637
|
this.connection = connection;
|
|
1606
2638
|
this.patternRegistry = patternRegistry;
|
|
1607
2639
|
this.codec = codec;
|
|
1608
2640
|
this.eventBus = eventBus;
|
|
2641
|
+
const derived = deriveOtelAttrs(options);
|
|
2642
|
+
this.otel = derived.otel;
|
|
2643
|
+
this.serviceName = derived.serviceName;
|
|
2644
|
+
this.serverEndpoint = derived.serverEndpoint;
|
|
1609
2645
|
}
|
|
1610
|
-
logger = new
|
|
2646
|
+
logger = new import_common8.Logger("Jetstream:CoreRpc");
|
|
1611
2647
|
subscription = null;
|
|
2648
|
+
otel;
|
|
2649
|
+
serviceName;
|
|
2650
|
+
serverEndpoint;
|
|
1612
2651
|
/** Start listening for RPC requests on the command subject. */
|
|
1613
2652
|
async start() {
|
|
1614
2653
|
const nc = await this.connection.getConnection();
|
|
1615
|
-
const
|
|
1616
|
-
const
|
|
1617
|
-
const queue = `${serviceName}_cmd_queue`;
|
|
2654
|
+
const subject = `${this.serviceName}.cmd.>`;
|
|
2655
|
+
const queue = `${this.serviceName}_cmd_queue`;
|
|
1618
2656
|
this.subscription = nc.subscribe(subject, {
|
|
1619
2657
|
queue,
|
|
1620
2658
|
callback: (err, msg) => {
|
|
@@ -1659,11 +2697,29 @@ var CoreRpcServer = class {
|
|
|
1659
2697
|
}
|
|
1660
2698
|
const ctx = new RpcContext([msg]);
|
|
1661
2699
|
try {
|
|
1662
|
-
const raw =
|
|
1663
|
-
|
|
1664
|
-
|
|
2700
|
+
const raw = await withConsumeSpan(
|
|
2701
|
+
{
|
|
2702
|
+
subject: msg.subject,
|
|
2703
|
+
msg,
|
|
2704
|
+
kind: "rpc" /* Rpc */,
|
|
2705
|
+
payloadBytes: msg.data.length,
|
|
2706
|
+
handlerMetadata: { pattern: msg.subject },
|
|
2707
|
+
serviceName: this.serviceName,
|
|
2708
|
+
endpoint: this.serverEndpoint
|
|
2709
|
+
},
|
|
2710
|
+
this.otel,
|
|
2711
|
+
() => {
|
|
2712
|
+
const out = unwrapResult(handler(data, ctx));
|
|
2713
|
+
return isPromiseLike2(out) ? out : out;
|
|
2714
|
+
}
|
|
2715
|
+
);
|
|
2716
|
+
msg.respond(this.codec.encode(raw));
|
|
1665
2717
|
} catch (err) {
|
|
1666
|
-
this.
|
|
2718
|
+
this.eventBus.emit(
|
|
2719
|
+
"error" /* Error */,
|
|
2720
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
2721
|
+
`core-rpc-handler:${msg.subject}`
|
|
2722
|
+
);
|
|
1667
2723
|
this.respondWithError(msg, err);
|
|
1668
2724
|
}
|
|
1669
2725
|
}
|
|
@@ -1680,7 +2736,7 @@ var CoreRpcServer = class {
|
|
|
1680
2736
|
};
|
|
1681
2737
|
|
|
1682
2738
|
// src/server/infrastructure/stream.provider.ts
|
|
1683
|
-
var
|
|
2739
|
+
var import_common10 = require("@nestjs/common");
|
|
1684
2740
|
var import_jetstream14 = require("@nats-io/jetstream");
|
|
1685
2741
|
|
|
1686
2742
|
// src/server/infrastructure/nats-error-codes.ts
|
|
@@ -1747,7 +2803,7 @@ var isEqual = (a, b) => {
|
|
|
1747
2803
|
};
|
|
1748
2804
|
|
|
1749
2805
|
// src/server/infrastructure/stream-migration.ts
|
|
1750
|
-
var
|
|
2806
|
+
var import_common9 = require("@nestjs/common");
|
|
1751
2807
|
var import_jetstream13 = require("@nats-io/jetstream");
|
|
1752
2808
|
var MIGRATION_BACKUP_SUFFIX = "__migration_backup";
|
|
1753
2809
|
var DEFAULT_SOURCING_TIMEOUT_MS = 3e4;
|
|
@@ -1756,7 +2812,7 @@ var StreamMigration = class {
|
|
|
1756
2812
|
constructor(sourcingTimeoutMs = DEFAULT_SOURCING_TIMEOUT_MS) {
|
|
1757
2813
|
this.sourcingTimeoutMs = sourcingTimeoutMs;
|
|
1758
2814
|
}
|
|
1759
|
-
logger = new
|
|
2815
|
+
logger = new import_common9.Logger("Jetstream:Stream");
|
|
1760
2816
|
async migrate(jsm, streamName2, newConfig) {
|
|
1761
2817
|
const backupName = `${streamName2}${MIGRATION_BACKUP_SUFFIX}`;
|
|
1762
2818
|
const startTime = Date.now();
|
|
@@ -1838,9 +2894,16 @@ var StreamProvider = class {
|
|
|
1838
2894
|
constructor(options, connection) {
|
|
1839
2895
|
this.options = options;
|
|
1840
2896
|
this.connection = connection;
|
|
2897
|
+
const derived = deriveOtelAttrs(options);
|
|
2898
|
+
this.otel = derived.otel;
|
|
2899
|
+
this.otelServiceName = derived.serviceName;
|
|
2900
|
+
this.otelEndpoint = derived.serverEndpoint;
|
|
1841
2901
|
}
|
|
1842
|
-
logger = new
|
|
2902
|
+
logger = new import_common10.Logger("Jetstream:Stream");
|
|
1843
2903
|
migration = new StreamMigration();
|
|
2904
|
+
otel;
|
|
2905
|
+
otelServiceName;
|
|
2906
|
+
otelEndpoint;
|
|
1844
2907
|
/**
|
|
1845
2908
|
* Ensure all required streams exist with correct configuration.
|
|
1846
2909
|
*
|
|
@@ -1886,32 +2949,56 @@ var StreamProvider = class {
|
|
|
1886
2949
|
/** Ensure a single stream exists, creating or updating as needed. */
|
|
1887
2950
|
async ensureStream(jsm, kind) {
|
|
1888
2951
|
const config = this.buildConfig(kind);
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
2952
|
+
return withProvisioningSpan(
|
|
2953
|
+
this.otel,
|
|
2954
|
+
{
|
|
2955
|
+
serviceName: this.otelServiceName,
|
|
2956
|
+
endpoint: this.otelEndpoint,
|
|
2957
|
+
entity: "stream",
|
|
2958
|
+
name: config.name,
|
|
2959
|
+
action: "ensure"
|
|
2960
|
+
},
|
|
2961
|
+
async () => {
|
|
2962
|
+
this.logger.log(`Ensuring stream: ${config.name}`);
|
|
2963
|
+
try {
|
|
2964
|
+
const currentInfo = await jsm.streams.info(config.name);
|
|
2965
|
+
return await this.handleExistingStream(jsm, currentInfo, config);
|
|
2966
|
+
} catch (err) {
|
|
2967
|
+
if (err instanceof import_jetstream14.JetStreamApiError && err.apiError().err_code === 10059 /* StreamNotFound */) {
|
|
2968
|
+
this.logger.log(`Creating stream: ${config.name}`);
|
|
2969
|
+
return await jsm.streams.add(config);
|
|
2970
|
+
}
|
|
2971
|
+
throw err;
|
|
2972
|
+
}
|
|
1897
2973
|
}
|
|
1898
|
-
|
|
1899
|
-
}
|
|
2974
|
+
);
|
|
1900
2975
|
}
|
|
1901
2976
|
/** Ensure a dead-letter queue stream exists, creating or updating as needed. */
|
|
1902
2977
|
async ensureDlqStream(jsm) {
|
|
1903
2978
|
const config = this.buildDlqConfig();
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
2979
|
+
return withProvisioningSpan(
|
|
2980
|
+
this.otel,
|
|
2981
|
+
{
|
|
2982
|
+
serviceName: this.otelServiceName,
|
|
2983
|
+
endpoint: this.otelEndpoint,
|
|
2984
|
+
entity: "stream",
|
|
2985
|
+
name: config.name,
|
|
2986
|
+
action: "ensure"
|
|
2987
|
+
},
|
|
2988
|
+
async () => {
|
|
2989
|
+
this.logger.log(`Ensuring DLQ stream: ${config.name}`);
|
|
2990
|
+
try {
|
|
2991
|
+
const currentInfo = await jsm.streams.info(config.name);
|
|
2992
|
+
return await this.handleExistingStream(jsm, currentInfo, config);
|
|
2993
|
+
} catch (err) {
|
|
2994
|
+
if (err instanceof import_jetstream14.JetStreamApiError && err.apiError().err_code === 10059 /* StreamNotFound */) {
|
|
2995
|
+
this.logger.log(`Creating DLQ stream: ${config.name}`);
|
|
2996
|
+
return await jsm.streams.add(config);
|
|
2997
|
+
}
|
|
2998
|
+
throw err;
|
|
2999
|
+
}
|
|
1912
3000
|
}
|
|
1913
|
-
|
|
1914
|
-
}
|
|
3001
|
+
);
|
|
1915
3002
|
}
|
|
1916
3003
|
async handleExistingStream(jsm, currentInfo, config) {
|
|
1917
3004
|
const diff = compareStreamConfig(currentInfo.config, config);
|
|
@@ -1940,7 +3027,18 @@ var StreamProvider = class {
|
|
|
1940
3027
|
}
|
|
1941
3028
|
return currentInfo;
|
|
1942
3029
|
}
|
|
1943
|
-
await
|
|
3030
|
+
await withMigrationSpan(
|
|
3031
|
+
this.otel,
|
|
3032
|
+
{
|
|
3033
|
+
serviceName: this.otelServiceName,
|
|
3034
|
+
endpoint: this.otelEndpoint,
|
|
3035
|
+
stream: config.name,
|
|
3036
|
+
reason: diff.changes.filter((c) => c.mutability === "immutable").map((c) => c.property).join(", ")
|
|
3037
|
+
},
|
|
3038
|
+
async () => {
|
|
3039
|
+
await this.migration.migrate(jsm, config.name, config);
|
|
3040
|
+
}
|
|
3041
|
+
);
|
|
1944
3042
|
return await jsm.streams.info(config.name);
|
|
1945
3043
|
}
|
|
1946
3044
|
buildMutableOnlyConfig(config, currentConfig, diff) {
|
|
@@ -2056,7 +3154,7 @@ var StreamProvider = class {
|
|
|
2056
3154
|
};
|
|
2057
3155
|
|
|
2058
3156
|
// src/server/infrastructure/consumer.provider.ts
|
|
2059
|
-
var
|
|
3157
|
+
var import_common11 = require("@nestjs/common");
|
|
2060
3158
|
var import_jetstream16 = require("@nats-io/jetstream");
|
|
2061
3159
|
var ConsumerProvider = class {
|
|
2062
3160
|
constructor(options, connection, streamProvider, patternRegistry) {
|
|
@@ -2064,8 +3162,15 @@ var ConsumerProvider = class {
|
|
|
2064
3162
|
this.connection = connection;
|
|
2065
3163
|
this.streamProvider = streamProvider;
|
|
2066
3164
|
this.patternRegistry = patternRegistry;
|
|
2067
|
-
|
|
2068
|
-
|
|
3165
|
+
const derived = deriveOtelAttrs(options);
|
|
3166
|
+
this.otel = derived.otel;
|
|
3167
|
+
this.otelServiceName = derived.serviceName;
|
|
3168
|
+
this.otelEndpoint = derived.serverEndpoint;
|
|
3169
|
+
}
|
|
3170
|
+
logger = new import_common11.Logger("Jetstream:Consumer");
|
|
3171
|
+
otel;
|
|
3172
|
+
otelServiceName;
|
|
3173
|
+
otelEndpoint;
|
|
2069
3174
|
/**
|
|
2070
3175
|
* Ensure consumers exist for the specified kinds.
|
|
2071
3176
|
*
|
|
@@ -2095,17 +3200,29 @@ var ConsumerProvider = class {
|
|
|
2095
3200
|
const stream = this.streamProvider.getStreamName(kind);
|
|
2096
3201
|
const config = this.buildConfig(kind);
|
|
2097
3202
|
const name = config.durable_name;
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
3203
|
+
return withProvisioningSpan(
|
|
3204
|
+
this.otel,
|
|
3205
|
+
{
|
|
3206
|
+
serviceName: this.otelServiceName,
|
|
3207
|
+
endpoint: this.otelEndpoint,
|
|
3208
|
+
entity: "consumer",
|
|
3209
|
+
name,
|
|
3210
|
+
action: "ensure"
|
|
3211
|
+
},
|
|
3212
|
+
async () => {
|
|
3213
|
+
this.logger.log(`Ensuring consumer: ${name} on stream: ${stream}`);
|
|
3214
|
+
try {
|
|
3215
|
+
await jsm.consumers.info(stream, name);
|
|
3216
|
+
this.logger.debug(`Consumer exists, updating: ${name}`);
|
|
3217
|
+
return await jsm.consumers.update(stream, name, config);
|
|
3218
|
+
} catch (err) {
|
|
3219
|
+
if (!(err instanceof import_jetstream16.JetStreamApiError) || err.apiError().err_code !== 10014 /* ConsumerNotFound */) {
|
|
3220
|
+
throw err;
|
|
3221
|
+
}
|
|
3222
|
+
return await this.createConsumer(jsm, stream, name, config);
|
|
3223
|
+
}
|
|
2106
3224
|
}
|
|
2107
|
-
|
|
2108
|
-
}
|
|
3225
|
+
);
|
|
2109
3226
|
}
|
|
2110
3227
|
/**
|
|
2111
3228
|
* Recover a consumer that disappeared during runtime.
|
|
@@ -2124,16 +3241,28 @@ var ConsumerProvider = class {
|
|
|
2124
3241
|
const stream = this.streamProvider.getStreamName(kind);
|
|
2125
3242
|
const config = this.buildConfig(kind);
|
|
2126
3243
|
const name = config.durable_name;
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
3244
|
+
return withProvisioningSpan(
|
|
3245
|
+
this.otel,
|
|
3246
|
+
{
|
|
3247
|
+
serviceName: this.otelServiceName,
|
|
3248
|
+
endpoint: this.otelEndpoint,
|
|
3249
|
+
entity: "consumer",
|
|
3250
|
+
name,
|
|
3251
|
+
action: "recover"
|
|
3252
|
+
},
|
|
3253
|
+
async () => {
|
|
3254
|
+
this.logger.log(`Recovering consumer: ${name} on stream: ${stream}`);
|
|
3255
|
+
await this.assertNoMigrationInProgress(jsm, stream);
|
|
3256
|
+
try {
|
|
3257
|
+
return await jsm.consumers.info(stream, name);
|
|
3258
|
+
} catch (err) {
|
|
3259
|
+
if (!(err instanceof import_jetstream16.JetStreamApiError) || err.apiError().err_code !== 10014 /* ConsumerNotFound */) {
|
|
3260
|
+
throw err;
|
|
3261
|
+
}
|
|
3262
|
+
return await this.createConsumer(jsm, stream, name, config);
|
|
3263
|
+
}
|
|
2134
3264
|
}
|
|
2135
|
-
|
|
2136
|
-
}
|
|
3265
|
+
);
|
|
2137
3266
|
}
|
|
2138
3267
|
/**
|
|
2139
3268
|
* Throw if a migration backup stream exists for this stream.
|
|
@@ -2249,7 +3378,7 @@ var ConsumerProvider = class {
|
|
|
2249
3378
|
};
|
|
2250
3379
|
|
|
2251
3380
|
// src/server/infrastructure/message.provider.ts
|
|
2252
|
-
var
|
|
3381
|
+
var import_common12 = require("@nestjs/common");
|
|
2253
3382
|
var import_jetstream18 = require("@nats-io/jetstream");
|
|
2254
3383
|
var import_rxjs3 = require("rxjs");
|
|
2255
3384
|
var MessageProvider = class {
|
|
@@ -2259,7 +3388,7 @@ var MessageProvider = class {
|
|
|
2259
3388
|
this.consumeOptionsMap = consumeOptionsMap;
|
|
2260
3389
|
this.consumerRecoveryFn = consumerRecoveryFn;
|
|
2261
3390
|
}
|
|
2262
|
-
logger = new
|
|
3391
|
+
logger = new import_common12.Logger("Jetstream:Message");
|
|
2263
3392
|
activeIterators = /* @__PURE__ */ new Set();
|
|
2264
3393
|
orderedReadyResolve = null;
|
|
2265
3394
|
orderedReadyReject = null;
|
|
@@ -2506,7 +3635,7 @@ var MessageProvider = class {
|
|
|
2506
3635
|
};
|
|
2507
3636
|
|
|
2508
3637
|
// src/server/infrastructure/metadata.provider.ts
|
|
2509
|
-
var
|
|
3638
|
+
var import_common13 = require("@nestjs/common");
|
|
2510
3639
|
var import_kv = require("@nats-io/kv");
|
|
2511
3640
|
var MetadataProvider = class {
|
|
2512
3641
|
constructor(options, connection) {
|
|
@@ -2515,7 +3644,7 @@ var MetadataProvider = class {
|
|
|
2515
3644
|
this.replicas = options.metadata?.replicas ?? DEFAULT_METADATA_REPLICAS;
|
|
2516
3645
|
this.ttl = Math.max(options.metadata?.ttl ?? DEFAULT_METADATA_TTL, MIN_METADATA_TTL);
|
|
2517
3646
|
}
|
|
2518
|
-
logger = new
|
|
3647
|
+
logger = new import_common13.Logger("Jetstream:Metadata");
|
|
2519
3648
|
bucketName;
|
|
2520
3649
|
replicas;
|
|
2521
3650
|
ttl;
|
|
@@ -2608,7 +3737,7 @@ var MetadataProvider = class {
|
|
|
2608
3737
|
};
|
|
2609
3738
|
|
|
2610
3739
|
// src/server/routing/pattern-registry.ts
|
|
2611
|
-
var
|
|
3740
|
+
var import_common14 = require("@nestjs/common");
|
|
2612
3741
|
var HANDLER_LABELS = {
|
|
2613
3742
|
["broadcast" /* Broadcast */]: "broadcast" /* Broadcast */,
|
|
2614
3743
|
["ordered" /* Ordered */]: "ordered" /* Ordered */,
|
|
@@ -2619,7 +3748,7 @@ var PatternRegistry = class {
|
|
|
2619
3748
|
constructor(options) {
|
|
2620
3749
|
this.options = options;
|
|
2621
3750
|
}
|
|
2622
|
-
logger = new
|
|
3751
|
+
logger = new import_common14.Logger("Jetstream:PatternRegistry");
|
|
2623
3752
|
registry = /* @__PURE__ */ new Map();
|
|
2624
3753
|
// Cached after registerHandlers() — the registry is immutable from that point
|
|
2625
3754
|
cachedPatterns = null;
|
|
@@ -2776,8 +3905,13 @@ var PatternRegistry = class {
|
|
|
2776
3905
|
};
|
|
2777
3906
|
|
|
2778
3907
|
// src/server/routing/event.router.ts
|
|
2779
|
-
var
|
|
3908
|
+
var import_common15 = require("@nestjs/common");
|
|
2780
3909
|
var import_transport_node4 = require("@nats-io/transport-node");
|
|
3910
|
+
var eventConsumeKindFor = (kind) => {
|
|
3911
|
+
if (kind === "broadcast" /* Broadcast */) return "broadcast" /* Broadcast */;
|
|
3912
|
+
if (kind === "ordered" /* Ordered */) return "ordered" /* Ordered */;
|
|
3913
|
+
return "event" /* Event */;
|
|
3914
|
+
};
|
|
2781
3915
|
var EventRouter = class {
|
|
2782
3916
|
constructor(messageProvider, patternRegistry, codec, eventBus, deadLetterConfig, processingConfig, ackWaitMap, connection, options) {
|
|
2783
3917
|
this.messageProvider = messageProvider;
|
|
@@ -2789,9 +3923,22 @@ var EventRouter = class {
|
|
|
2789
3923
|
this.ackWaitMap = ackWaitMap;
|
|
2790
3924
|
this.connection = connection;
|
|
2791
3925
|
this.options = options;
|
|
3926
|
+
if (options) {
|
|
3927
|
+
const derived = deriveOtelAttrs(options);
|
|
3928
|
+
this.otel = derived.otel;
|
|
3929
|
+
this.serviceName = derived.serviceName;
|
|
3930
|
+
this.serverEndpoint = derived.serverEndpoint;
|
|
3931
|
+
} else {
|
|
3932
|
+
this.otel = resolveOtelOptions({ enabled: false });
|
|
3933
|
+
this.serviceName = "";
|
|
3934
|
+
this.serverEndpoint = null;
|
|
3935
|
+
}
|
|
2792
3936
|
}
|
|
2793
|
-
logger = new
|
|
3937
|
+
logger = new import_common15.Logger("Jetstream:EventRouter");
|
|
2794
3938
|
subscriptions = [];
|
|
3939
|
+
otel;
|
|
3940
|
+
serviceName;
|
|
3941
|
+
serverEndpoint;
|
|
2795
3942
|
/**
|
|
2796
3943
|
* Update the max_deliver thresholds from actual NATS consumer configs.
|
|
2797
3944
|
* Called after consumers are ensured so the DLQ map reflects reality.
|
|
@@ -2821,8 +3968,12 @@ var EventRouter = class {
|
|
|
2821
3968
|
const patternRegistry = this.patternRegistry;
|
|
2822
3969
|
const codec = this.codec;
|
|
2823
3970
|
const eventBus = this.eventBus;
|
|
2824
|
-
const
|
|
3971
|
+
const logger5 = this.logger;
|
|
2825
3972
|
const deadLetterConfig = this.deadLetterConfig;
|
|
3973
|
+
const otel = this.otel;
|
|
3974
|
+
const serviceName = this.serviceName;
|
|
3975
|
+
const serverEndpoint = this.serverEndpoint;
|
|
3976
|
+
const spanKind = eventConsumeKindFor(kind);
|
|
2826
3977
|
const ackExtensionInterval = isOrdered ? null : resolveAckExtensionInterval(this.getAckExtensionConfig(kind), this.ackWaitMap?.get(kind));
|
|
2827
3978
|
const hasAckExtension = ackExtensionInterval !== null && ackExtensionInterval > 0;
|
|
2828
3979
|
const concurrency = this.getConcurrency(kind);
|
|
@@ -2853,7 +4004,7 @@ var EventRouter = class {
|
|
|
2853
4004
|
const handler = patternRegistry.getHandler(subject);
|
|
2854
4005
|
if (!handler) {
|
|
2855
4006
|
msg.term(`No handler for event: ${subject}`);
|
|
2856
|
-
|
|
4007
|
+
logger5.error(`No handler for subject: ${subject}`);
|
|
2857
4008
|
return null;
|
|
2858
4009
|
}
|
|
2859
4010
|
let data;
|
|
@@ -2861,17 +4012,17 @@ var EventRouter = class {
|
|
|
2861
4012
|
data = codec.decode(msg.data);
|
|
2862
4013
|
} catch (err) {
|
|
2863
4014
|
msg.term("Decode error");
|
|
2864
|
-
|
|
4015
|
+
logger5.error(`Decode error for ${subject}:`, err);
|
|
2865
4016
|
return null;
|
|
2866
4017
|
}
|
|
2867
4018
|
if (emitRouted) eventBus.emitMessageRouted(subject, "event" /* Event */);
|
|
2868
4019
|
return { handler, data };
|
|
2869
4020
|
} catch (err) {
|
|
2870
|
-
|
|
4021
|
+
logger5.error(`Unexpected error in ${kind} event router`, err);
|
|
2871
4022
|
try {
|
|
2872
4023
|
msg.term("Unexpected router error");
|
|
2873
4024
|
} catch (termErr) {
|
|
2874
|
-
|
|
4025
|
+
logger5.error(`Failed to terminate message ${subject}:`, termErr);
|
|
2875
4026
|
}
|
|
2876
4027
|
return null;
|
|
2877
4028
|
}
|
|
@@ -2884,13 +4035,31 @@ var EventRouter = class {
|
|
|
2884
4035
|
const stopAckExtension = hasAckExtension ? startAckExtensionTimer(msg, ackExtensionInterval) : null;
|
|
2885
4036
|
let pending;
|
|
2886
4037
|
try {
|
|
2887
|
-
pending =
|
|
4038
|
+
pending = withConsumeSpan(
|
|
4039
|
+
{
|
|
4040
|
+
subject: msg.subject,
|
|
4041
|
+
msg,
|
|
4042
|
+
info: msg.info,
|
|
4043
|
+
kind: spanKind,
|
|
4044
|
+
payloadBytes: msg.data.length,
|
|
4045
|
+
handlerMetadata: { pattern: msg.subject },
|
|
4046
|
+
serviceName,
|
|
4047
|
+
endpoint: serverEndpoint
|
|
4048
|
+
},
|
|
4049
|
+
otel,
|
|
4050
|
+
() => unwrapResult(handler(data, ctx))
|
|
4051
|
+
);
|
|
2888
4052
|
} catch (err) {
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
4053
|
+
eventBus.emit(
|
|
4054
|
+
"error" /* Error */,
|
|
4055
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
4056
|
+
`${kind}-handler:${msg.subject}`
|
|
4057
|
+
);
|
|
4058
|
+
return settleFailure(msg, data, err).finally(() => {
|
|
4059
|
+
if (stopAckExtension !== null) stopAckExtension();
|
|
4060
|
+
});
|
|
2892
4061
|
}
|
|
2893
|
-
if (!
|
|
4062
|
+
if (!isPromiseLike2(pending)) {
|
|
2894
4063
|
settleSuccess(msg, ctx);
|
|
2895
4064
|
if (stopAckExtension !== null) stopAckExtension();
|
|
2896
4065
|
return void 0;
|
|
@@ -2901,7 +4070,11 @@ var EventRouter = class {
|
|
|
2901
4070
|
if (stopAckExtension !== null) stopAckExtension();
|
|
2902
4071
|
},
|
|
2903
4072
|
async (err) => {
|
|
2904
|
-
|
|
4073
|
+
eventBus.emit(
|
|
4074
|
+
"error" /* Error */,
|
|
4075
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
4076
|
+
`${kind}-handler:${msg.subject}`
|
|
4077
|
+
);
|
|
2905
4078
|
try {
|
|
2906
4079
|
await settleFailure(msg, data, err);
|
|
2907
4080
|
} finally {
|
|
@@ -2917,41 +4090,54 @@ var EventRouter = class {
|
|
|
2917
4090
|
try {
|
|
2918
4091
|
handler = patternRegistry.getHandler(subject);
|
|
2919
4092
|
if (!handler) {
|
|
2920
|
-
|
|
4093
|
+
logger5.error(`No handler for subject: ${subject}`);
|
|
2921
4094
|
return void 0;
|
|
2922
4095
|
}
|
|
2923
4096
|
try {
|
|
2924
4097
|
data = codec.decode(msg.data);
|
|
2925
4098
|
} catch (err) {
|
|
2926
|
-
|
|
4099
|
+
logger5.error(`Decode error for ${subject}:`, err);
|
|
2927
4100
|
return void 0;
|
|
2928
4101
|
}
|
|
2929
4102
|
if (emitRouted) eventBus.emitMessageRouted(subject, "event" /* Event */);
|
|
2930
4103
|
} catch (err) {
|
|
2931
|
-
|
|
4104
|
+
logger5.error(`Ordered handler error (${subject}):`, err);
|
|
2932
4105
|
return void 0;
|
|
2933
4106
|
}
|
|
2934
4107
|
const ctx = new RpcContext([msg]);
|
|
2935
4108
|
const warnIfSettlementAttempted = () => {
|
|
2936
4109
|
if (ctx.shouldRetry || ctx.shouldTerminate) {
|
|
2937
|
-
|
|
4110
|
+
logger5.warn(
|
|
2938
4111
|
`retry()/terminate() ignored for ordered message ${subject} \u2014 ordered consumers auto-acknowledge`
|
|
2939
4112
|
);
|
|
2940
4113
|
}
|
|
2941
4114
|
};
|
|
2942
4115
|
let pending;
|
|
2943
4116
|
try {
|
|
2944
|
-
pending =
|
|
4117
|
+
pending = withConsumeSpan(
|
|
4118
|
+
{
|
|
4119
|
+
subject: msg.subject,
|
|
4120
|
+
msg,
|
|
4121
|
+
info: msg.info,
|
|
4122
|
+
kind: spanKind,
|
|
4123
|
+
payloadBytes: msg.data.length,
|
|
4124
|
+
handlerMetadata: { pattern: msg.subject },
|
|
4125
|
+
serviceName,
|
|
4126
|
+
endpoint: serverEndpoint
|
|
4127
|
+
},
|
|
4128
|
+
otel,
|
|
4129
|
+
() => unwrapResult(handler(data, ctx))
|
|
4130
|
+
);
|
|
2945
4131
|
} catch (err) {
|
|
2946
|
-
|
|
4132
|
+
logger5.error(`Ordered handler error (${subject}):`, err);
|
|
2947
4133
|
return void 0;
|
|
2948
4134
|
}
|
|
2949
|
-
if (!
|
|
4135
|
+
if (!isPromiseLike2(pending)) {
|
|
2950
4136
|
warnIfSettlementAttempted();
|
|
2951
4137
|
return void 0;
|
|
2952
4138
|
}
|
|
2953
4139
|
return pending.then(warnIfSettlementAttempted, (err) => {
|
|
2954
|
-
|
|
4140
|
+
logger5.error(`Ordered handler error (${subject}):`, err);
|
|
2955
4141
|
});
|
|
2956
4142
|
};
|
|
2957
4143
|
const route = isOrdered ? handleOrderedSafe : handleSafe;
|
|
@@ -2984,7 +4170,7 @@ var EventRouter = class {
|
|
|
2984
4170
|
backlog.push(msg);
|
|
2985
4171
|
if (!backlogWarned && backlog.length >= backlogWarnThreshold) {
|
|
2986
4172
|
backlogWarned = true;
|
|
2987
|
-
|
|
4173
|
+
logger5.warn(
|
|
2988
4174
|
`${kind} backlog reached ${backlog.length} messages \u2014 consumer may be falling behind`
|
|
2989
4175
|
);
|
|
2990
4176
|
}
|
|
@@ -3000,7 +4186,7 @@ var EventRouter = class {
|
|
|
3000
4186
|
}
|
|
3001
4187
|
},
|
|
3002
4188
|
error: (err) => {
|
|
3003
|
-
|
|
4189
|
+
logger5.error(`Stream error in ${kind} router`, err);
|
|
3004
4190
|
}
|
|
3005
4191
|
});
|
|
3006
4192
|
this.subscriptions.push(subscription);
|
|
@@ -3015,14 +4201,11 @@ var EventRouter = class {
|
|
|
3015
4201
|
if (kind === "broadcast" /* Broadcast */) return this.processingConfig?.broadcast?.ackExtension;
|
|
3016
4202
|
return void 0;
|
|
3017
4203
|
}
|
|
3018
|
-
/** Handle a dead letter: invoke callback, then term or nak based on result. */
|
|
3019
4204
|
/**
|
|
3020
|
-
*
|
|
3021
|
-
*
|
|
3022
|
-
*
|
|
3023
|
-
*
|
|
3024
|
-
* On success, terminates the message. On error, leaves it unacknowledged (nak)
|
|
3025
|
-
* so NATS can retry the delivery on the next cycle.
|
|
4205
|
+
* Last-resort path for a dead letter: invoke `onDeadLetter`, then `term` on
|
|
4206
|
+
* success or `nak` on hook failure so NATS retries on the next delivery
|
|
4207
|
+
* cycle. Used when DLQ stream isn't configured, or when publishing to it
|
|
4208
|
+
* failed and we still have to surface the message somewhere observable.
|
|
3026
4209
|
*/
|
|
3027
4210
|
async fallbackToOnDeadLetterCallback(info, msg) {
|
|
3028
4211
|
if (!this.deadLetterConfig) {
|
|
@@ -3114,20 +4297,37 @@ var EventRouter = class {
|
|
|
3114
4297
|
streamSequence: msg.info.streamSequence,
|
|
3115
4298
|
timestamp: new Date(msg.info.timestampNanos / 1e6).toISOString()
|
|
3116
4299
|
};
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
4300
|
+
await withDeadLetterSpan(
|
|
4301
|
+
{
|
|
4302
|
+
msg,
|
|
4303
|
+
// Pattern resolution mirrors event-routing: when a registered
|
|
4304
|
+
// pattern matches, surface it on the DLQ span so APM can filter
|
|
4305
|
+
// dead letters by handler without parsing the subject. Falls back
|
|
4306
|
+
// to the subject itself when no glob handler is in play.
|
|
4307
|
+
pattern: this.patternRegistry.getHandler(msg.subject) ? msg.subject : void 0,
|
|
4308
|
+
finalDeliveryCount: msg.info.deliveryCount,
|
|
4309
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
4310
|
+
serviceName: this.serviceName,
|
|
4311
|
+
endpoint: this.serverEndpoint
|
|
4312
|
+
},
|
|
4313
|
+
this.otel,
|
|
4314
|
+
async () => {
|
|
4315
|
+
this.eventBus.emit("deadLetter" /* DeadLetter */, info);
|
|
4316
|
+
if (!this.options?.dlq) {
|
|
4317
|
+
await this.fallbackToOnDeadLetterCallback(info, msg);
|
|
4318
|
+
} else {
|
|
4319
|
+
await this.publishToDlq(msg, info, error);
|
|
4320
|
+
}
|
|
4321
|
+
}
|
|
4322
|
+
);
|
|
3123
4323
|
}
|
|
3124
4324
|
};
|
|
3125
4325
|
|
|
3126
4326
|
// src/server/routing/rpc.router.ts
|
|
3127
|
-
var
|
|
4327
|
+
var import_common16 = require("@nestjs/common");
|
|
3128
4328
|
var import_transport_node5 = require("@nats-io/transport-node");
|
|
3129
4329
|
var RpcRouter = class {
|
|
3130
|
-
constructor(messageProvider, patternRegistry, connection, codec, eventBus, rpcOptions, ackWaitMap) {
|
|
4330
|
+
constructor(messageProvider, patternRegistry, connection, codec, eventBus, rpcOptions, ackWaitMap, options) {
|
|
3131
4331
|
this.messageProvider = messageProvider;
|
|
3132
4332
|
this.patternRegistry = patternRegistry;
|
|
3133
4333
|
this.connection = connection;
|
|
@@ -3137,13 +4337,26 @@ var RpcRouter = class {
|
|
|
3137
4337
|
this.ackWaitMap = ackWaitMap;
|
|
3138
4338
|
this.timeout = rpcOptions?.timeout ?? DEFAULT_JETSTREAM_RPC_TIMEOUT;
|
|
3139
4339
|
this.concurrency = rpcOptions?.concurrency;
|
|
4340
|
+
if (options) {
|
|
4341
|
+
const derived = deriveOtelAttrs(options);
|
|
4342
|
+
this.otel = derived.otel;
|
|
4343
|
+
this.serviceName = derived.serviceName;
|
|
4344
|
+
this.serverEndpoint = derived.serverEndpoint;
|
|
4345
|
+
} else {
|
|
4346
|
+
this.otel = resolveOtelOptions({ enabled: false });
|
|
4347
|
+
this.serviceName = "";
|
|
4348
|
+
this.serverEndpoint = null;
|
|
4349
|
+
}
|
|
3140
4350
|
}
|
|
3141
|
-
logger = new
|
|
4351
|
+
logger = new import_common16.Logger("Jetstream:RpcRouter");
|
|
3142
4352
|
timeout;
|
|
3143
4353
|
concurrency;
|
|
3144
4354
|
resolvedAckExtensionInterval;
|
|
3145
4355
|
subscription = null;
|
|
3146
4356
|
cachedNc = null;
|
|
4357
|
+
otel;
|
|
4358
|
+
serviceName;
|
|
4359
|
+
serverEndpoint;
|
|
3147
4360
|
/** Lazily resolve the ack extension interval (needs ackWaitMap populated at runtime). */
|
|
3148
4361
|
get ackExtensionInterval() {
|
|
3149
4362
|
if (this.resolvedAckExtensionInterval !== void 0) return this.resolvedAckExtensionInterval;
|
|
@@ -3160,11 +4373,14 @@ var RpcRouter = class {
|
|
|
3160
4373
|
const patternRegistry = this.patternRegistry;
|
|
3161
4374
|
const codec = this.codec;
|
|
3162
4375
|
const eventBus = this.eventBus;
|
|
3163
|
-
const
|
|
4376
|
+
const logger5 = this.logger;
|
|
3164
4377
|
const timeout = this.timeout;
|
|
3165
4378
|
const ackExtensionInterval = this.ackExtensionInterval;
|
|
3166
4379
|
const hasAckExtension = ackExtensionInterval !== null && ackExtensionInterval > 0;
|
|
3167
4380
|
const maxActive = this.concurrency ?? Number.POSITIVE_INFINITY;
|
|
4381
|
+
const otel = this.otel;
|
|
4382
|
+
const serviceName = this.serviceName;
|
|
4383
|
+
const serverEndpoint = this.serverEndpoint;
|
|
3168
4384
|
const emitRpcTimeout = (subject, correlationId) => {
|
|
3169
4385
|
eventBus.emit("rpcTimeout" /* RpcTimeout */, subject, correlationId);
|
|
3170
4386
|
};
|
|
@@ -3174,7 +4390,7 @@ var RpcRouter = class {
|
|
|
3174
4390
|
hdrs.set("x-correlation-id" /* CorrelationId */, correlationId);
|
|
3175
4391
|
nc.publish(replyTo, codec.encode(payload), { headers: hdrs });
|
|
3176
4392
|
} catch (publishErr) {
|
|
3177
|
-
|
|
4393
|
+
logger5.error(`Failed to publish RPC response`, publishErr);
|
|
3178
4394
|
}
|
|
3179
4395
|
};
|
|
3180
4396
|
const publishErrorReply = (replyTo, correlationId, subject, err) => {
|
|
@@ -3184,7 +4400,7 @@ var RpcRouter = class {
|
|
|
3184
4400
|
hdrs.set("x-error" /* Error */, "true");
|
|
3185
4401
|
nc.publish(replyTo, codec.encode(serializeError(err)), { headers: hdrs });
|
|
3186
4402
|
} catch (encodeErr) {
|
|
3187
|
-
|
|
4403
|
+
logger5.error(`Failed to encode RPC error for ${subject}`, encodeErr);
|
|
3188
4404
|
}
|
|
3189
4405
|
};
|
|
3190
4406
|
const resolveCommand = (msg) => {
|
|
@@ -3193,7 +4409,7 @@ var RpcRouter = class {
|
|
|
3193
4409
|
const handler = patternRegistry.getHandler(subject);
|
|
3194
4410
|
if (!handler) {
|
|
3195
4411
|
msg.term(`No handler for RPC: ${subject}`);
|
|
3196
|
-
|
|
4412
|
+
logger5.error(`No handler for RPC subject: ${subject}`);
|
|
3197
4413
|
return null;
|
|
3198
4414
|
}
|
|
3199
4415
|
const msgHeaders = msg.headers;
|
|
@@ -3201,7 +4417,7 @@ var RpcRouter = class {
|
|
|
3201
4417
|
const correlationId = msgHeaders?.get("x-correlation-id" /* CorrelationId */);
|
|
3202
4418
|
if (!replyTo || !correlationId) {
|
|
3203
4419
|
msg.term("Missing required headers (reply-to or correlation-id)");
|
|
3204
|
-
|
|
4420
|
+
logger5.error(`Missing headers for RPC: ${subject}`);
|
|
3205
4421
|
return null;
|
|
3206
4422
|
}
|
|
3207
4423
|
let data;
|
|
@@ -3209,17 +4425,17 @@ var RpcRouter = class {
|
|
|
3209
4425
|
data = codec.decode(msg.data);
|
|
3210
4426
|
} catch (err) {
|
|
3211
4427
|
msg.term("Decode error");
|
|
3212
|
-
|
|
4428
|
+
logger5.error(`Decode error for RPC ${subject}:`, err);
|
|
3213
4429
|
return null;
|
|
3214
4430
|
}
|
|
3215
4431
|
eventBus.emitMessageRouted(subject, "rpc" /* Rpc */);
|
|
3216
4432
|
return { handler, data, replyTo, correlationId };
|
|
3217
4433
|
} catch (err) {
|
|
3218
|
-
|
|
4434
|
+
logger5.error("Unexpected error in RPC router", err);
|
|
3219
4435
|
try {
|
|
3220
4436
|
msg.term("Unexpected router error");
|
|
3221
4437
|
} catch (termErr) {
|
|
3222
|
-
|
|
4438
|
+
logger5.error(`Failed to terminate RPC message ${subject}:`, termErr);
|
|
3223
4439
|
}
|
|
3224
4440
|
return null;
|
|
3225
4441
|
}
|
|
@@ -3231,17 +4447,39 @@ var RpcRouter = class {
|
|
|
3231
4447
|
const subject = msg.subject;
|
|
3232
4448
|
const ctx = new RpcContext([msg]);
|
|
3233
4449
|
const stopAckExtension = hasAckExtension ? startAckExtensionTimer(msg, ackExtensionInterval) : null;
|
|
4450
|
+
const reportHandlerError = (err) => {
|
|
4451
|
+
eventBus.emit(
|
|
4452
|
+
"error" /* Error */,
|
|
4453
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
4454
|
+
`rpc-handler:${subject}`
|
|
4455
|
+
);
|
|
4456
|
+
publishErrorReply(replyTo, correlationId, subject, err);
|
|
4457
|
+
msg.term(`Handler error: ${subject}`);
|
|
4458
|
+
};
|
|
4459
|
+
const abortController = new AbortController();
|
|
3234
4460
|
let pending;
|
|
3235
4461
|
try {
|
|
3236
|
-
pending =
|
|
4462
|
+
pending = withConsumeSpan(
|
|
4463
|
+
{
|
|
4464
|
+
subject,
|
|
4465
|
+
msg,
|
|
4466
|
+
info: msg.info,
|
|
4467
|
+
kind: "rpc" /* Rpc */,
|
|
4468
|
+
payloadBytes: msg.data.length,
|
|
4469
|
+
handlerMetadata: { pattern: subject },
|
|
4470
|
+
serviceName,
|
|
4471
|
+
endpoint: serverEndpoint
|
|
4472
|
+
},
|
|
4473
|
+
otel,
|
|
4474
|
+
() => unwrapResult(handler(data, ctx)),
|
|
4475
|
+
{ signal: abortController.signal, timeoutLabel: "rpc.handler.timeout" }
|
|
4476
|
+
);
|
|
3237
4477
|
} catch (err) {
|
|
3238
4478
|
if (stopAckExtension !== null) stopAckExtension();
|
|
3239
|
-
|
|
3240
|
-
publishErrorReply(replyTo, correlationId, subject, err);
|
|
3241
|
-
msg.term(`Handler error: ${subject}`);
|
|
4479
|
+
reportHandlerError(err);
|
|
3242
4480
|
return void 0;
|
|
3243
4481
|
}
|
|
3244
|
-
if (!
|
|
4482
|
+
if (!isPromiseLike2(pending)) {
|
|
3245
4483
|
if (stopAckExtension !== null) stopAckExtension();
|
|
3246
4484
|
msg.ack();
|
|
3247
4485
|
publishReply(replyTo, correlationId, pending);
|
|
@@ -3252,7 +4490,7 @@ var RpcRouter = class {
|
|
|
3252
4490
|
if (settled) return;
|
|
3253
4491
|
settled = true;
|
|
3254
4492
|
if (stopAckExtension !== null) stopAckExtension();
|
|
3255
|
-
|
|
4493
|
+
abortController.abort();
|
|
3256
4494
|
emitRpcTimeout(subject, correlationId);
|
|
3257
4495
|
msg.term("Handler timeout");
|
|
3258
4496
|
}, timeout);
|
|
@@ -3270,9 +4508,7 @@ var RpcRouter = class {
|
|
|
3270
4508
|
settled = true;
|
|
3271
4509
|
clearTimeout(timeoutId);
|
|
3272
4510
|
if (stopAckExtension !== null) stopAckExtension();
|
|
3273
|
-
|
|
3274
|
-
publishErrorReply(replyTo, correlationId, subject, err);
|
|
3275
|
-
msg.term(`Handler error: ${subject}`);
|
|
4511
|
+
reportHandlerError(err);
|
|
3276
4512
|
}
|
|
3277
4513
|
);
|
|
3278
4514
|
};
|
|
@@ -3304,7 +4540,7 @@ var RpcRouter = class {
|
|
|
3304
4540
|
backlog.push(msg);
|
|
3305
4541
|
if (!backlogWarned && backlog.length >= backlogWarnThreshold) {
|
|
3306
4542
|
backlogWarned = true;
|
|
3307
|
-
|
|
4543
|
+
logger5.warn(
|
|
3308
4544
|
`RPC backlog reached ${backlog.length} messages \u2014 consumer may be falling behind`
|
|
3309
4545
|
);
|
|
3310
4546
|
}
|
|
@@ -3320,7 +4556,7 @@ var RpcRouter = class {
|
|
|
3320
4556
|
}
|
|
3321
4557
|
},
|
|
3322
4558
|
error: (err) => {
|
|
3323
|
-
|
|
4559
|
+
logger5.error("Stream error in RPC router", err);
|
|
3324
4560
|
}
|
|
3325
4561
|
});
|
|
3326
4562
|
}
|
|
@@ -3332,14 +4568,14 @@ var RpcRouter = class {
|
|
|
3332
4568
|
};
|
|
3333
4569
|
|
|
3334
4570
|
// src/shutdown/shutdown.manager.ts
|
|
3335
|
-
var
|
|
4571
|
+
var import_common17 = require("@nestjs/common");
|
|
3336
4572
|
var ShutdownManager = class {
|
|
3337
4573
|
constructor(connection, eventBus, timeout) {
|
|
3338
4574
|
this.connection = connection;
|
|
3339
4575
|
this.eventBus = eventBus;
|
|
3340
4576
|
this.timeout = timeout;
|
|
3341
4577
|
}
|
|
3342
|
-
logger = new
|
|
4578
|
+
logger = new import_common17.Logger("Jetstream:Shutdown");
|
|
3343
4579
|
shutdownPromise;
|
|
3344
4580
|
/**
|
|
3345
4581
|
* Execute the full shutdown sequence.
|
|
@@ -3379,9 +4615,6 @@ var JetstreamModule = class {
|
|
|
3379
4615
|
this.shutdownManager = shutdownManager;
|
|
3380
4616
|
this.strategy = strategy;
|
|
3381
4617
|
}
|
|
3382
|
-
// -------------------------------------------------------------------
|
|
3383
|
-
// forRoot — global module registration
|
|
3384
|
-
// -------------------------------------------------------------------
|
|
3385
4618
|
/**
|
|
3386
4619
|
* Register the JetStream transport globally.
|
|
3387
4620
|
*
|
|
@@ -3408,9 +4641,6 @@ var JetstreamModule = class {
|
|
|
3408
4641
|
]
|
|
3409
4642
|
};
|
|
3410
4643
|
}
|
|
3411
|
-
// -------------------------------------------------------------------
|
|
3412
|
-
// forRootAsync — async global module registration
|
|
3413
|
-
// -------------------------------------------------------------------
|
|
3414
4644
|
/**
|
|
3415
4645
|
* Register the JetStream transport globally with async configuration.
|
|
3416
4646
|
*
|
|
@@ -3439,9 +4669,6 @@ var JetstreamModule = class {
|
|
|
3439
4669
|
]
|
|
3440
4670
|
};
|
|
3441
4671
|
}
|
|
3442
|
-
// -------------------------------------------------------------------
|
|
3443
|
-
// forFeature — per-module client registration
|
|
3444
|
-
// -------------------------------------------------------------------
|
|
3445
4672
|
/**
|
|
3446
4673
|
* Register a lightweight client proxy for a target service.
|
|
3447
4674
|
*
|
|
@@ -3467,9 +4694,6 @@ var JetstreamModule = class {
|
|
|
3467
4694
|
exports: [clientToken]
|
|
3468
4695
|
};
|
|
3469
4696
|
}
|
|
3470
|
-
// -------------------------------------------------------------------
|
|
3471
|
-
// Provider factories
|
|
3472
|
-
// -------------------------------------------------------------------
|
|
3473
4697
|
static createCoreProviders(options) {
|
|
3474
4698
|
return [
|
|
3475
4699
|
{
|
|
@@ -3487,8 +4711,8 @@ var JetstreamModule = class {
|
|
|
3487
4711
|
provide: JETSTREAM_EVENT_BUS,
|
|
3488
4712
|
inject: [JETSTREAM_OPTIONS],
|
|
3489
4713
|
useFactory: (options) => {
|
|
3490
|
-
const
|
|
3491
|
-
return new EventBus(
|
|
4714
|
+
const logger5 = new import_common18.Logger("Jetstream:Module");
|
|
4715
|
+
return new EventBus(logger5, options.hooks);
|
|
3492
4716
|
}
|
|
3493
4717
|
},
|
|
3494
4718
|
// Codec — global encode/decode
|
|
@@ -3527,10 +4751,8 @@ var JetstreamModule = class {
|
|
|
3527
4751
|
);
|
|
3528
4752
|
}
|
|
3529
4753
|
},
|
|
3530
|
-
// ---------------------------------------------------------------
|
|
3531
4754
|
// Consumer infrastructure — only created when consumer !== false.
|
|
3532
4755
|
// Providers return null when consumer is disabled (publisher-only mode).
|
|
3533
|
-
// ---------------------------------------------------------------
|
|
3534
4756
|
// PatternRegistry — subject-to-handler mapping
|
|
3535
4757
|
{
|
|
3536
4758
|
provide: PatternRegistry,
|
|
@@ -3566,8 +4788,14 @@ var JetstreamModule = class {
|
|
|
3566
4788
|
// MessageProvider — pull-based message consumption
|
|
3567
4789
|
{
|
|
3568
4790
|
provide: MessageProvider,
|
|
3569
|
-
inject: [
|
|
3570
|
-
|
|
4791
|
+
inject: [
|
|
4792
|
+
JETSTREAM_OPTIONS,
|
|
4793
|
+
JETSTREAM_CONNECTION,
|
|
4794
|
+
JETSTREAM_EVENT_BUS,
|
|
4795
|
+
ConsumerProvider,
|
|
4796
|
+
StreamProvider
|
|
4797
|
+
],
|
|
4798
|
+
useFactory: (options, connection, eventBus, consumerProvider, streamProvider) => {
|
|
3571
4799
|
if (options.consumer === false) return null;
|
|
3572
4800
|
const consumeOptionsMap = /* @__PURE__ */ new Map();
|
|
3573
4801
|
if (options.events?.consume)
|
|
@@ -3577,10 +4805,22 @@ var JetstreamModule = class {
|
|
|
3577
4805
|
if (options.rpc?.mode === "jetstream" && options.rpc.consume) {
|
|
3578
4806
|
consumeOptionsMap.set("cmd" /* Command */, options.rpc.consume);
|
|
3579
4807
|
}
|
|
3580
|
-
const
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
4808
|
+
const derived = deriveOtelAttrs(options);
|
|
4809
|
+
const { otel, serverEndpoint: otelEndpoint, serviceName: otelServiceName } = derived;
|
|
4810
|
+
const consumerRecoveryFn = consumerProvider && streamProvider ? async (kind) => withSelfHealingSpan(
|
|
4811
|
+
otel,
|
|
4812
|
+
{
|
|
4813
|
+
serviceName: otelServiceName,
|
|
4814
|
+
endpoint: otelEndpoint,
|
|
4815
|
+
consumer: consumerProvider.getConsumerName(kind),
|
|
4816
|
+
stream: streamProvider.getStreamName(kind),
|
|
4817
|
+
reason: "consumer not found"
|
|
4818
|
+
},
|
|
4819
|
+
async () => {
|
|
4820
|
+
const jsm = await connection.getJetStreamManager();
|
|
4821
|
+
return consumerProvider.recoverConsumer(jsm, kind);
|
|
4822
|
+
}
|
|
4823
|
+
) : void 0;
|
|
3584
4824
|
return new MessageProvider(connection, eventBus, consumeOptionsMap, consumerRecoveryFn);
|
|
3585
4825
|
}
|
|
3586
4826
|
},
|
|
@@ -3651,7 +4891,8 @@ var JetstreamModule = class {
|
|
|
3651
4891
|
codec,
|
|
3652
4892
|
eventBus,
|
|
3653
4893
|
rpcOptions,
|
|
3654
|
-
ackWaitMap
|
|
4894
|
+
ackWaitMap,
|
|
4895
|
+
options
|
|
3655
4896
|
);
|
|
3656
4897
|
}
|
|
3657
4898
|
},
|
|
@@ -3754,9 +4995,6 @@ var JetstreamModule = class {
|
|
|
3754
4995
|
}
|
|
3755
4996
|
];
|
|
3756
4997
|
}
|
|
3757
|
-
// -------------------------------------------------------------------
|
|
3758
|
-
// Lifecycle hooks
|
|
3759
|
-
// -------------------------------------------------------------------
|
|
3760
4998
|
/**
|
|
3761
4999
|
* Gracefully shut down the transport on application termination.
|
|
3762
5000
|
*/
|
|
@@ -3767,15 +5005,16 @@ var JetstreamModule = class {
|
|
|
3767
5005
|
}
|
|
3768
5006
|
};
|
|
3769
5007
|
JetstreamModule = __decorateClass([
|
|
3770
|
-
(0,
|
|
3771
|
-
(0,
|
|
3772
|
-
__decorateParam(0, (0,
|
|
3773
|
-
__decorateParam(0, (0,
|
|
3774
|
-
__decorateParam(1, (0,
|
|
3775
|
-
__decorateParam(1, (0,
|
|
5008
|
+
(0, import_common18.Global)(),
|
|
5009
|
+
(0, import_common18.Module)({}),
|
|
5010
|
+
__decorateParam(0, (0, import_common18.Optional)()),
|
|
5011
|
+
__decorateParam(0, (0, import_common18.Inject)(ShutdownManager)),
|
|
5012
|
+
__decorateParam(1, (0, import_common18.Optional)()),
|
|
5013
|
+
__decorateParam(1, (0, import_common18.Inject)(JetstreamStrategy))
|
|
3776
5014
|
], JetstreamModule);
|
|
3777
5015
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3778
5016
|
0 && (module.exports = {
|
|
5017
|
+
ConsumeKind,
|
|
3779
5018
|
DEFAULT_BROADCAST_CONSUMER_CONFIG,
|
|
3780
5019
|
DEFAULT_BROADCAST_STREAM_CONFIG,
|
|
3781
5020
|
DEFAULT_COMMAND_CONSUMER_CONFIG,
|
|
@@ -3791,6 +5030,7 @@ JetstreamModule = __decorateClass([
|
|
|
3791
5030
|
DEFAULT_ORDERED_STREAM_CONFIG,
|
|
3792
5031
|
DEFAULT_RPC_TIMEOUT,
|
|
3793
5032
|
DEFAULT_SHUTDOWN_TIMEOUT,
|
|
5033
|
+
DEFAULT_TRACES,
|
|
3794
5034
|
JETSTREAM_CODEC,
|
|
3795
5035
|
JETSTREAM_CONNECTION,
|
|
3796
5036
|
JETSTREAM_OPTIONS,
|
|
@@ -3802,15 +5042,18 @@ JetstreamModule = __decorateClass([
|
|
|
3802
5042
|
JetstreamRecord,
|
|
3803
5043
|
JetstreamRecordBuilder,
|
|
3804
5044
|
JetstreamStrategy,
|
|
5045
|
+
JetstreamTrace,
|
|
3805
5046
|
JsonCodec,
|
|
3806
5047
|
MIN_METADATA_TTL,
|
|
3807
5048
|
MessageKind,
|
|
3808
5049
|
MsgpackCodec,
|
|
3809
5050
|
NatsErrorCode,
|
|
3810
5051
|
PatternPrefix,
|
|
5052
|
+
PublishKind,
|
|
3811
5053
|
RESERVED_HEADERS,
|
|
3812
5054
|
RpcContext,
|
|
3813
5055
|
StreamKind,
|
|
5056
|
+
TRACER_NAME,
|
|
3814
5057
|
TransportEvent,
|
|
3815
5058
|
buildBroadcastSubject,
|
|
3816
5059
|
buildSubject,
|