@amqp-contract/core 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -308,7 +308,242 @@ var AmqpClient = class {
308
308
  }
309
309
  };
310
310
 
311
+ //#endregion
312
+ //#region src/telemetry.ts
313
+ /**
314
+ * SpanKind values from OpenTelemetry.
315
+ * Defined as constants to avoid runtime dependency when types are used.
316
+ * @see https://opentelemetry.io/docs/specs/otel/trace/api/#spankind
317
+ */
318
+ const SpanKind = {
319
+ PRODUCER: 3,
320
+ CONSUMER: 4
321
+ };
322
+ /**
323
+ * Semantic conventions for AMQP messaging following OpenTelemetry standards.
324
+ * @see https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/
325
+ */
326
+ const MessagingSemanticConventions = {
327
+ MESSAGING_SYSTEM: "messaging.system",
328
+ MESSAGING_DESTINATION: "messaging.destination.name",
329
+ MESSAGING_DESTINATION_KIND: "messaging.destination.kind",
330
+ MESSAGING_OPERATION: "messaging.operation",
331
+ MESSAGING_MESSAGE_ID: "messaging.message.id",
332
+ MESSAGING_MESSAGE_PAYLOAD_SIZE: "messaging.message.body.size",
333
+ MESSAGING_MESSAGE_CONVERSATION_ID: "messaging.message.conversation_id",
334
+ MESSAGING_RABBITMQ_ROUTING_KEY: "messaging.rabbitmq.destination.routing_key",
335
+ MESSAGING_RABBITMQ_MESSAGE_DELIVERY_TAG: "messaging.rabbitmq.message.delivery_tag",
336
+ ERROR_TYPE: "error.type",
337
+ MESSAGING_SYSTEM_RABBITMQ: "rabbitmq",
338
+ MESSAGING_DESTINATION_KIND_EXCHANGE: "exchange",
339
+ MESSAGING_DESTINATION_KIND_QUEUE: "queue",
340
+ MESSAGING_OPERATION_PUBLISH: "publish",
341
+ MESSAGING_OPERATION_RECEIVE: "receive",
342
+ MESSAGING_OPERATION_PROCESS: "process"
343
+ };
344
+ /**
345
+ * Instrumentation scope name for amqp-contract.
346
+ */
347
+ const INSTRUMENTATION_SCOPE_NAME = "@amqp-contract";
348
+ const INSTRUMENTATION_SCOPE_VERSION = "0.1.0";
349
+ let otelApi;
350
+ let cachedTracer;
351
+ let cachedPublishCounter;
352
+ let cachedConsumeCounter;
353
+ let cachedPublishLatencyHistogram;
354
+ let cachedConsumeLatencyHistogram;
355
+ /**
356
+ * Try to load the OpenTelemetry API module.
357
+ * Returns null if the module is not available.
358
+ */
359
+ function tryLoadOpenTelemetryApi() {
360
+ if (otelApi === void 0) try {
361
+ otelApi = require("@opentelemetry/api");
362
+ } catch {
363
+ otelApi = null;
364
+ }
365
+ return otelApi;
366
+ }
367
+ /**
368
+ * Get or create a tracer instance.
369
+ */
370
+ function getTracer() {
371
+ if (cachedTracer !== void 0) return cachedTracer;
372
+ const api = tryLoadOpenTelemetryApi();
373
+ if (!api) return;
374
+ cachedTracer = api.trace.getTracer(INSTRUMENTATION_SCOPE_NAME, INSTRUMENTATION_SCOPE_VERSION);
375
+ return cachedTracer;
376
+ }
377
+ /**
378
+ * Get or create a meter and its instruments.
379
+ */
380
+ function getMeterInstruments() {
381
+ if (cachedPublishCounter !== void 0) return {
382
+ publishCounter: cachedPublishCounter,
383
+ consumeCounter: cachedConsumeCounter,
384
+ publishLatencyHistogram: cachedPublishLatencyHistogram,
385
+ consumeLatencyHistogram: cachedConsumeLatencyHistogram
386
+ };
387
+ const api = tryLoadOpenTelemetryApi();
388
+ if (!api) return {
389
+ publishCounter: void 0,
390
+ consumeCounter: void 0,
391
+ publishLatencyHistogram: void 0,
392
+ consumeLatencyHistogram: void 0
393
+ };
394
+ const meter = api.metrics.getMeter(INSTRUMENTATION_SCOPE_NAME, INSTRUMENTATION_SCOPE_VERSION);
395
+ cachedPublishCounter = meter.createCounter("amqp.client.messages.published", {
396
+ description: "Number of messages published to AMQP broker",
397
+ unit: "{message}"
398
+ });
399
+ cachedConsumeCounter = meter.createCounter("amqp.worker.messages.consumed", {
400
+ description: "Number of messages consumed from AMQP broker",
401
+ unit: "{message}"
402
+ });
403
+ cachedPublishLatencyHistogram = meter.createHistogram("amqp.client.publish.duration", {
404
+ description: "Duration of message publish operations",
405
+ unit: "ms"
406
+ });
407
+ cachedConsumeLatencyHistogram = meter.createHistogram("amqp.worker.process.duration", {
408
+ description: "Duration of message processing operations",
409
+ unit: "ms"
410
+ });
411
+ return {
412
+ publishCounter: cachedPublishCounter,
413
+ consumeCounter: cachedConsumeCounter,
414
+ publishLatencyHistogram: cachedPublishLatencyHistogram,
415
+ consumeLatencyHistogram: cachedConsumeLatencyHistogram
416
+ };
417
+ }
418
+ /**
419
+ * Default telemetry provider that uses OpenTelemetry API if available.
420
+ */
421
+ const defaultTelemetryProvider = {
422
+ getTracer,
423
+ getPublishCounter: () => getMeterInstruments().publishCounter,
424
+ getConsumeCounter: () => getMeterInstruments().consumeCounter,
425
+ getPublishLatencyHistogram: () => getMeterInstruments().publishLatencyHistogram,
426
+ getConsumeLatencyHistogram: () => getMeterInstruments().consumeLatencyHistogram
427
+ };
428
+ /**
429
+ * Create a span for a publish operation.
430
+ * Returns undefined if OpenTelemetry is not available.
431
+ */
432
+ function startPublishSpan(provider, exchangeName, routingKey, attributes) {
433
+ const tracer = provider.getTracer();
434
+ if (!tracer) return;
435
+ const spanName = `${exchangeName} publish`;
436
+ return tracer.startSpan(spanName, {
437
+ kind: SpanKind.PRODUCER,
438
+ attributes: {
439
+ [MessagingSemanticConventions.MESSAGING_SYSTEM]: MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,
440
+ [MessagingSemanticConventions.MESSAGING_DESTINATION]: exchangeName,
441
+ [MessagingSemanticConventions.MESSAGING_DESTINATION_KIND]: MessagingSemanticConventions.MESSAGING_DESTINATION_KIND_EXCHANGE,
442
+ [MessagingSemanticConventions.MESSAGING_OPERATION]: MessagingSemanticConventions.MESSAGING_OPERATION_PUBLISH,
443
+ ...routingKey ? { [MessagingSemanticConventions.MESSAGING_RABBITMQ_ROUTING_KEY]: routingKey } : {},
444
+ ...attributes
445
+ }
446
+ });
447
+ }
448
+ /**
449
+ * Create a span for a consume/process operation.
450
+ * Returns undefined if OpenTelemetry is not available.
451
+ */
452
+ function startConsumeSpan(provider, queueName, consumerName, attributes) {
453
+ const tracer = provider.getTracer();
454
+ if (!tracer) return;
455
+ const spanName = `${queueName} process`;
456
+ return tracer.startSpan(spanName, {
457
+ kind: SpanKind.CONSUMER,
458
+ attributes: {
459
+ [MessagingSemanticConventions.MESSAGING_SYSTEM]: MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,
460
+ [MessagingSemanticConventions.MESSAGING_DESTINATION]: queueName,
461
+ [MessagingSemanticConventions.MESSAGING_DESTINATION_KIND]: MessagingSemanticConventions.MESSAGING_DESTINATION_KIND_QUEUE,
462
+ [MessagingSemanticConventions.MESSAGING_OPERATION]: MessagingSemanticConventions.MESSAGING_OPERATION_PROCESS,
463
+ "amqp.consumer.name": consumerName,
464
+ ...attributes
465
+ }
466
+ });
467
+ }
468
+ /**
469
+ * End a span with success status.
470
+ */
471
+ function endSpanSuccess(span) {
472
+ if (!span) return;
473
+ const api = tryLoadOpenTelemetryApi();
474
+ if (api) span.setStatus({ code: api.SpanStatusCode.OK });
475
+ span.end();
476
+ }
477
+ /**
478
+ * End a span with error status.
479
+ */
480
+ function endSpanError(span, error) {
481
+ if (!span) return;
482
+ const api = tryLoadOpenTelemetryApi();
483
+ if (api) {
484
+ span.setStatus({
485
+ code: api.SpanStatusCode.ERROR,
486
+ message: error.message
487
+ });
488
+ span.recordException(error);
489
+ span.setAttribute(MessagingSemanticConventions.ERROR_TYPE, error.name);
490
+ }
491
+ span.end();
492
+ }
493
+ /**
494
+ * Record a publish metric.
495
+ */
496
+ function recordPublishMetric(provider, exchangeName, routingKey, success, durationMs) {
497
+ const publishCounter = provider.getPublishCounter();
498
+ const publishLatencyHistogram = provider.getPublishLatencyHistogram();
499
+ const attributes = {
500
+ [MessagingSemanticConventions.MESSAGING_SYSTEM]: MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,
501
+ [MessagingSemanticConventions.MESSAGING_DESTINATION]: exchangeName,
502
+ ...routingKey ? { [MessagingSemanticConventions.MESSAGING_RABBITMQ_ROUTING_KEY]: routingKey } : {},
503
+ success
504
+ };
505
+ publishCounter?.add(1, attributes);
506
+ publishLatencyHistogram?.record(durationMs, attributes);
507
+ }
508
+ /**
509
+ * Record a consume metric.
510
+ */
511
+ function recordConsumeMetric(provider, queueName, consumerName, success, durationMs) {
512
+ const consumeCounter = provider.getConsumeCounter();
513
+ const consumeLatencyHistogram = provider.getConsumeLatencyHistogram();
514
+ const attributes = {
515
+ [MessagingSemanticConventions.MESSAGING_SYSTEM]: MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,
516
+ [MessagingSemanticConventions.MESSAGING_DESTINATION]: queueName,
517
+ "amqp.consumer.name": consumerName,
518
+ success
519
+ };
520
+ consumeCounter?.add(1, attributes);
521
+ consumeLatencyHistogram?.record(durationMs, attributes);
522
+ }
523
+ /**
524
+ * Reset the cached OpenTelemetry API module and instruments.
525
+ * For testing purposes only.
526
+ * @internal
527
+ */
528
+ function _resetTelemetryCacheForTesting() {
529
+ otelApi = void 0;
530
+ cachedTracer = void 0;
531
+ cachedPublishCounter = void 0;
532
+ cachedConsumeCounter = void 0;
533
+ cachedPublishLatencyHistogram = void 0;
534
+ cachedConsumeLatencyHistogram = void 0;
535
+ }
536
+
311
537
  //#endregion
312
538
  exports.AmqpClient = AmqpClient;
313
539
  exports.ConnectionManagerSingleton = ConnectionManagerSingleton;
314
- exports.setupAmqpTopology = setupAmqpTopology;
540
+ exports.MessagingSemanticConventions = MessagingSemanticConventions;
541
+ exports._resetTelemetryCacheForTesting = _resetTelemetryCacheForTesting;
542
+ exports.defaultTelemetryProvider = defaultTelemetryProvider;
543
+ exports.endSpanError = endSpanError;
544
+ exports.endSpanSuccess = endSpanSuccess;
545
+ exports.recordConsumeMetric = recordConsumeMetric;
546
+ exports.recordPublishMetric = recordPublishMetric;
547
+ exports.setupAmqpTopology = setupAmqpTopology;
548
+ exports.startConsumeSpan = startConsumeSpan;
549
+ exports.startPublishSpan = startPublishSpan;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { AmqpConnectionManager, AmqpConnectionManagerOptions, ChannelWrapper, ConnectionUrl, CreateChannelOpts } from "amqp-connection-manager";
2
2
  import { ContractDefinition } from "@amqp-contract/contract";
3
3
  import { Channel } from "amqplib";
4
+ import { Attributes, Counter, Histogram, Span, Tracer } from "@opentelemetry/api";
4
5
 
5
6
  //#region src/logger.d.ts
6
7
 
@@ -246,5 +247,96 @@ declare class ConnectionManagerSingleton {
246
247
  */
247
248
  declare function setupAmqpTopology(channel: Channel, contract: ContractDefinition): Promise<void>;
248
249
  //#endregion
249
- export { AmqpClient, type AmqpClientOptions, ConnectionManagerSingleton, type Logger, type LoggerContext, setupAmqpTopology };
250
+ //#region src/telemetry.d.ts
251
+ /**
252
+ * Semantic conventions for AMQP messaging following OpenTelemetry standards.
253
+ * @see https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/
254
+ */
255
+ declare const MessagingSemanticConventions: {
256
+ readonly MESSAGING_SYSTEM: "messaging.system";
257
+ readonly MESSAGING_DESTINATION: "messaging.destination.name";
258
+ readonly MESSAGING_DESTINATION_KIND: "messaging.destination.kind";
259
+ readonly MESSAGING_OPERATION: "messaging.operation";
260
+ readonly MESSAGING_MESSAGE_ID: "messaging.message.id";
261
+ readonly MESSAGING_MESSAGE_PAYLOAD_SIZE: "messaging.message.body.size";
262
+ readonly MESSAGING_MESSAGE_CONVERSATION_ID: "messaging.message.conversation_id";
263
+ readonly MESSAGING_RABBITMQ_ROUTING_KEY: "messaging.rabbitmq.destination.routing_key";
264
+ readonly MESSAGING_RABBITMQ_MESSAGE_DELIVERY_TAG: "messaging.rabbitmq.message.delivery_tag";
265
+ readonly ERROR_TYPE: "error.type";
266
+ readonly MESSAGING_SYSTEM_RABBITMQ: "rabbitmq";
267
+ readonly MESSAGING_DESTINATION_KIND_EXCHANGE: "exchange";
268
+ readonly MESSAGING_DESTINATION_KIND_QUEUE: "queue";
269
+ readonly MESSAGING_OPERATION_PUBLISH: "publish";
270
+ readonly MESSAGING_OPERATION_RECEIVE: "receive";
271
+ readonly MESSAGING_OPERATION_PROCESS: "process";
272
+ };
273
+ /**
274
+ * Telemetry provider for AMQP operations.
275
+ * Uses lazy loading to gracefully handle cases where OpenTelemetry is not installed.
276
+ */
277
+ type TelemetryProvider = {
278
+ /**
279
+ * Get a tracer instance for creating spans.
280
+ * Returns undefined if OpenTelemetry is not available.
281
+ */
282
+ getTracer: () => Tracer | undefined;
283
+ /**
284
+ * Get a counter for messages published.
285
+ * Returns undefined if OpenTelemetry is not available.
286
+ */
287
+ getPublishCounter: () => Counter | undefined;
288
+ /**
289
+ * Get a counter for messages consumed.
290
+ * Returns undefined if OpenTelemetry is not available.
291
+ */
292
+ getConsumeCounter: () => Counter | undefined;
293
+ /**
294
+ * Get a histogram for publish latency.
295
+ * Returns undefined if OpenTelemetry is not available.
296
+ */
297
+ getPublishLatencyHistogram: () => Histogram | undefined;
298
+ /**
299
+ * Get a histogram for consume/process latency.
300
+ * Returns undefined if OpenTelemetry is not available.
301
+ */
302
+ getConsumeLatencyHistogram: () => Histogram | undefined;
303
+ };
304
+ /**
305
+ * Default telemetry provider that uses OpenTelemetry API if available.
306
+ */
307
+ declare const defaultTelemetryProvider: TelemetryProvider;
308
+ /**
309
+ * Create a span for a publish operation.
310
+ * Returns undefined if OpenTelemetry is not available.
311
+ */
312
+ declare function startPublishSpan(provider: TelemetryProvider, exchangeName: string, routingKey: string | undefined, attributes?: Attributes): Span | undefined;
313
+ /**
314
+ * Create a span for a consume/process operation.
315
+ * Returns undefined if OpenTelemetry is not available.
316
+ */
317
+ declare function startConsumeSpan(provider: TelemetryProvider, queueName: string, consumerName: string, attributes?: Attributes): Span | undefined;
318
+ /**
319
+ * End a span with success status.
320
+ */
321
+ declare function endSpanSuccess(span: Span | undefined): void;
322
+ /**
323
+ * End a span with error status.
324
+ */
325
+ declare function endSpanError(span: Span | undefined, error: Error): void;
326
+ /**
327
+ * Record a publish metric.
328
+ */
329
+ declare function recordPublishMetric(provider: TelemetryProvider, exchangeName: string, routingKey: string | undefined, success: boolean, durationMs: number): void;
330
+ /**
331
+ * Record a consume metric.
332
+ */
333
+ declare function recordConsumeMetric(provider: TelemetryProvider, queueName: string, consumerName: string, success: boolean, durationMs: number): void;
334
+ /**
335
+ * Reset the cached OpenTelemetry API module and instruments.
336
+ * For testing purposes only.
337
+ * @internal
338
+ */
339
+ declare function _resetTelemetryCacheForTesting(): void;
340
+ //#endregion
341
+ export { AmqpClient, type AmqpClientOptions, ConnectionManagerSingleton, type Logger, type LoggerContext, MessagingSemanticConventions, type TelemetryProvider, _resetTelemetryCacheForTesting, defaultTelemetryProvider, endSpanError, endSpanSuccess, recordConsumeMetric, recordPublishMetric, setupAmqpTopology, startConsumeSpan, startPublishSpan };
250
342
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/logger.ts","../src/amqp-client.ts","../src/connection-manager.ts","../src/setup.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;AAQA;AAqBA;;AAakC,KAlCtB,aAAA,GAAgB,MAkCM,CAAA,MAAA,EAAA,OAAA,CAAA,GAAA;EAOA,KAAA,CAAA,EAAA,OAAA;CAOC;;;;;ACrCnC;;;;;;AA6BA;;;;;;;AAyGyD,KD5H7C,MAAA,GC4H6C;;;;ACnIzD;;EA8BU,KAAA,CAAA,OAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EFjByB,aEiBzB,CAAA,EAAA,IAAA;EACc;;;;;EAgHI,IAAA,CAAA,OAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EF3HM,aE2HN,CAAA,EAAA,IAAA;EAAO;;;;AC9InC;EACW,IAAA,CAAA,OAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EHyBuB,aGzBvB,CAAA,EAAA,IAAA;EACC;;;;;mCH+BuB;;;;;;;AAhDnC;AAqBA;;;AAoBkC,KC9BtB,iBAAA,GD8BsB;EAOC,IAAA,ECpC3B,aDoC2B,EAAA;EAAa,iBAAA,CAAA,ECnC1B,4BDmC0B,GAAA,SAAA;mBClC7B,QAAQ;;;AAH3B;;;;;;AA6BA;;;;;;;;;;;AC1BA;;;;;AA2DU,cDjCG,UAAA,CCiCH;EACc,iBAAA,QAAA;EACnB,iBAAA,UAAA;EAkFuB,SAAA,OAAA,EDnHD,cCmHC;EAAO,iBAAA,IAAA;;;;AC9InC;;;;;;;;;wBF2C+B,6BAClB;;;;;;;;;;mBA6DM;;;;;;;;;;;WAcF;;;;;4CAWiC;;;;;;;;ADjJlD;AAqBA;;;;;;;;;ACVA;;AAEsB,cCCT,0BAAA,CDDS;EACK,eAAA,QAAA;EAAR,QAAA,WAAA;EAAO,QAAA,SAAA;EA0Bb,QAAA,WAAU,CAAA;EAEI;;;;;EAuGuB,OAAA,WAAA,CAAA,CAAA,ECvH1B,0BDuH0B;EAAO;;;;ACnIzD;;;;;;EA4DwB,aAAA,CAAA,IAAA,EA9Bd,aA8Bc,EAAA,EAAA,iBAAA,CAAA,EA7BA,4BA6BA,CAAA,EA5BnB,qBA4BmB;EACnB;;;;;;AC5DL;;;;EAGU,iBAAA,CAAA,IAAA,EDuDA,aCvDA,EAAA,EAAA,iBAAA,CAAA,EDwDc,4BCxDd,CAAA,EDyDL,OCzDK,CAAA,IAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBD2IkB;;;;;;;AF7J5B;AAqBA;;;;;;;;;ACVA;;;;;;AA6BA;AAE2B,iBE3BL,iBAAA,CF2BK,OAAA,EE1BhB,OF0BgB,EAAA,QAAA,EEzBf,kBFyBe,CAAA,EExBxB,OFwBwB,CAAA,IAAA,CAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/logger.ts","../src/amqp-client.ts","../src/connection-manager.ts","../src/setup.ts","../src/telemetry.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { AmqpConnectionManager, AmqpConnectionManagerOptions, ChannelWrapper, ConnectionUrl, CreateChannelOpts } from "amqp-connection-manager";
2
+ import { Attributes, Counter, Histogram, Span, Tracer } from "@opentelemetry/api";
2
3
  import { ContractDefinition } from "@amqp-contract/contract";
3
4
  import { Channel } from "amqplib";
4
5
 
@@ -246,5 +247,96 @@ declare class ConnectionManagerSingleton {
246
247
  */
247
248
  declare function setupAmqpTopology(channel: Channel, contract: ContractDefinition): Promise<void>;
248
249
  //#endregion
249
- export { AmqpClient, type AmqpClientOptions, ConnectionManagerSingleton, type Logger, type LoggerContext, setupAmqpTopology };
250
+ //#region src/telemetry.d.ts
251
+ /**
252
+ * Semantic conventions for AMQP messaging following OpenTelemetry standards.
253
+ * @see https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/
254
+ */
255
+ declare const MessagingSemanticConventions: {
256
+ readonly MESSAGING_SYSTEM: "messaging.system";
257
+ readonly MESSAGING_DESTINATION: "messaging.destination.name";
258
+ readonly MESSAGING_DESTINATION_KIND: "messaging.destination.kind";
259
+ readonly MESSAGING_OPERATION: "messaging.operation";
260
+ readonly MESSAGING_MESSAGE_ID: "messaging.message.id";
261
+ readonly MESSAGING_MESSAGE_PAYLOAD_SIZE: "messaging.message.body.size";
262
+ readonly MESSAGING_MESSAGE_CONVERSATION_ID: "messaging.message.conversation_id";
263
+ readonly MESSAGING_RABBITMQ_ROUTING_KEY: "messaging.rabbitmq.destination.routing_key";
264
+ readonly MESSAGING_RABBITMQ_MESSAGE_DELIVERY_TAG: "messaging.rabbitmq.message.delivery_tag";
265
+ readonly ERROR_TYPE: "error.type";
266
+ readonly MESSAGING_SYSTEM_RABBITMQ: "rabbitmq";
267
+ readonly MESSAGING_DESTINATION_KIND_EXCHANGE: "exchange";
268
+ readonly MESSAGING_DESTINATION_KIND_QUEUE: "queue";
269
+ readonly MESSAGING_OPERATION_PUBLISH: "publish";
270
+ readonly MESSAGING_OPERATION_RECEIVE: "receive";
271
+ readonly MESSAGING_OPERATION_PROCESS: "process";
272
+ };
273
+ /**
274
+ * Telemetry provider for AMQP operations.
275
+ * Uses lazy loading to gracefully handle cases where OpenTelemetry is not installed.
276
+ */
277
+ type TelemetryProvider = {
278
+ /**
279
+ * Get a tracer instance for creating spans.
280
+ * Returns undefined if OpenTelemetry is not available.
281
+ */
282
+ getTracer: () => Tracer | undefined;
283
+ /**
284
+ * Get a counter for messages published.
285
+ * Returns undefined if OpenTelemetry is not available.
286
+ */
287
+ getPublishCounter: () => Counter | undefined;
288
+ /**
289
+ * Get a counter for messages consumed.
290
+ * Returns undefined if OpenTelemetry is not available.
291
+ */
292
+ getConsumeCounter: () => Counter | undefined;
293
+ /**
294
+ * Get a histogram for publish latency.
295
+ * Returns undefined if OpenTelemetry is not available.
296
+ */
297
+ getPublishLatencyHistogram: () => Histogram | undefined;
298
+ /**
299
+ * Get a histogram for consume/process latency.
300
+ * Returns undefined if OpenTelemetry is not available.
301
+ */
302
+ getConsumeLatencyHistogram: () => Histogram | undefined;
303
+ };
304
+ /**
305
+ * Default telemetry provider that uses OpenTelemetry API if available.
306
+ */
307
+ declare const defaultTelemetryProvider: TelemetryProvider;
308
+ /**
309
+ * Create a span for a publish operation.
310
+ * Returns undefined if OpenTelemetry is not available.
311
+ */
312
+ declare function startPublishSpan(provider: TelemetryProvider, exchangeName: string, routingKey: string | undefined, attributes?: Attributes): Span | undefined;
313
+ /**
314
+ * Create a span for a consume/process operation.
315
+ * Returns undefined if OpenTelemetry is not available.
316
+ */
317
+ declare function startConsumeSpan(provider: TelemetryProvider, queueName: string, consumerName: string, attributes?: Attributes): Span | undefined;
318
+ /**
319
+ * End a span with success status.
320
+ */
321
+ declare function endSpanSuccess(span: Span | undefined): void;
322
+ /**
323
+ * End a span with error status.
324
+ */
325
+ declare function endSpanError(span: Span | undefined, error: Error): void;
326
+ /**
327
+ * Record a publish metric.
328
+ */
329
+ declare function recordPublishMetric(provider: TelemetryProvider, exchangeName: string, routingKey: string | undefined, success: boolean, durationMs: number): void;
330
+ /**
331
+ * Record a consume metric.
332
+ */
333
+ declare function recordConsumeMetric(provider: TelemetryProvider, queueName: string, consumerName: string, success: boolean, durationMs: number): void;
334
+ /**
335
+ * Reset the cached OpenTelemetry API module and instruments.
336
+ * For testing purposes only.
337
+ * @internal
338
+ */
339
+ declare function _resetTelemetryCacheForTesting(): void;
340
+ //#endregion
341
+ export { AmqpClient, type AmqpClientOptions, ConnectionManagerSingleton, type Logger, type LoggerContext, MessagingSemanticConventions, type TelemetryProvider, _resetTelemetryCacheForTesting, defaultTelemetryProvider, endSpanError, endSpanSuccess, recordConsumeMetric, recordPublishMetric, setupAmqpTopology, startConsumeSpan, startPublishSpan };
250
342
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/logger.ts","../src/amqp-client.ts","../src/connection-manager.ts","../src/setup.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;AAQA;AAqBA;;AAakC,KAlCtB,aAAA,GAAgB,MAkCM,CAAA,MAAA,EAAA,OAAA,CAAA,GAAA;EAOA,KAAA,CAAA,EAAA,OAAA;CAOC;;;;;ACrCnC;;;;;;AA6BA;;;;;;;AAyGyD,KD5H7C,MAAA,GC4H6C;;;;ACnIzD;;EA8BU,KAAA,CAAA,OAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EFjByB,aEiBzB,CAAA,EAAA,IAAA;EACc;;;;;EAgHI,IAAA,CAAA,OAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EF3HM,aE2HN,CAAA,EAAA,IAAA;EAAO;;;;AC9InC;EACW,IAAA,CAAA,OAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EHyBuB,aGzBvB,CAAA,EAAA,IAAA;EACC;;;;;mCH+BuB;;;;;;;AAhDnC;AAqBA;;;AAoBkC,KC9BtB,iBAAA,GD8BsB;EAOC,IAAA,ECpC3B,aDoC2B,EAAA;EAAa,iBAAA,CAAA,ECnC1B,4BDmC0B,GAAA,SAAA;mBClC7B,QAAQ;;;AAH3B;;;;;;AA6BA;;;;;;;;;;;AC1BA;;;;;AA2DU,cDjCG,UAAA,CCiCH;EACc,iBAAA,QAAA;EACnB,iBAAA,UAAA;EAkFuB,SAAA,OAAA,EDnHD,cCmHC;EAAO,iBAAA,IAAA;;;;AC9InC;;;;;;;;;wBF2C+B,6BAClB;;;;;;;;;;mBA6DM;;;;;;;;;;;WAcF;;;;;4CAWiC;;;;;;;;ADjJlD;AAqBA;;;;;;;;;ACVA;;AAEsB,cCCT,0BAAA,CDDS;EACK,eAAA,QAAA;EAAR,QAAA,WAAA;EAAO,QAAA,SAAA;EA0Bb,QAAA,WAAU,CAAA;EAEI;;;;;EAuGuB,OAAA,WAAA,CAAA,CAAA,ECvH1B,0BDuH0B;EAAO;;;;ACnIzD;;;;;;EA4DwB,aAAA,CAAA,IAAA,EA9Bd,aA8Bc,EAAA,EAAA,iBAAA,CAAA,EA7BA,4BA6BA,CAAA,EA5BnB,qBA4BmB;EACnB;;;;;;AC5DL;;;;EAGU,iBAAA,CAAA,IAAA,EDuDA,aCvDA,EAAA,EAAA,iBAAA,CAAA,EDwDc,4BCxDd,CAAA,EDyDL,OCzDK,CAAA,IAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBD2IkB;;;;;;;AF7J5B;AAqBA;;;;;;;;;ACVA;;;;;;AA6BA;AAE2B,iBE3BL,iBAAA,CF2BK,OAAA,EE1BhB,OF0BgB,EAAA,QAAA,EEzBf,kBFyBe,CAAA,EExBxB,OFwBwB,CAAA,IAAA,CAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/logger.ts","../src/amqp-client.ts","../src/connection-manager.ts","../src/setup.ts","../src/telemetry.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,10 @@
1
+ import { createRequire } from "node:module";
1
2
  import amqp from "amqp-connection-manager";
2
3
 
4
+ //#region rolldown:runtime
5
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
6
+
7
+ //#endregion
3
8
  //#region src/connection-manager.ts
4
9
  /**
5
10
  * Connection manager singleton for sharing AMQP connections across clients.
@@ -281,5 +286,231 @@ var AmqpClient = class {
281
286
  };
282
287
 
283
288
  //#endregion
284
- export { AmqpClient, ConnectionManagerSingleton, setupAmqpTopology };
289
+ //#region src/telemetry.ts
290
+ /**
291
+ * SpanKind values from OpenTelemetry.
292
+ * Defined as constants to avoid runtime dependency when types are used.
293
+ * @see https://opentelemetry.io/docs/specs/otel/trace/api/#spankind
294
+ */
295
+ const SpanKind = {
296
+ PRODUCER: 3,
297
+ CONSUMER: 4
298
+ };
299
+ /**
300
+ * Semantic conventions for AMQP messaging following OpenTelemetry standards.
301
+ * @see https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/
302
+ */
303
+ const MessagingSemanticConventions = {
304
+ MESSAGING_SYSTEM: "messaging.system",
305
+ MESSAGING_DESTINATION: "messaging.destination.name",
306
+ MESSAGING_DESTINATION_KIND: "messaging.destination.kind",
307
+ MESSAGING_OPERATION: "messaging.operation",
308
+ MESSAGING_MESSAGE_ID: "messaging.message.id",
309
+ MESSAGING_MESSAGE_PAYLOAD_SIZE: "messaging.message.body.size",
310
+ MESSAGING_MESSAGE_CONVERSATION_ID: "messaging.message.conversation_id",
311
+ MESSAGING_RABBITMQ_ROUTING_KEY: "messaging.rabbitmq.destination.routing_key",
312
+ MESSAGING_RABBITMQ_MESSAGE_DELIVERY_TAG: "messaging.rabbitmq.message.delivery_tag",
313
+ ERROR_TYPE: "error.type",
314
+ MESSAGING_SYSTEM_RABBITMQ: "rabbitmq",
315
+ MESSAGING_DESTINATION_KIND_EXCHANGE: "exchange",
316
+ MESSAGING_DESTINATION_KIND_QUEUE: "queue",
317
+ MESSAGING_OPERATION_PUBLISH: "publish",
318
+ MESSAGING_OPERATION_RECEIVE: "receive",
319
+ MESSAGING_OPERATION_PROCESS: "process"
320
+ };
321
+ /**
322
+ * Instrumentation scope name for amqp-contract.
323
+ */
324
+ const INSTRUMENTATION_SCOPE_NAME = "@amqp-contract";
325
+ const INSTRUMENTATION_SCOPE_VERSION = "0.1.0";
326
+ let otelApi;
327
+ let cachedTracer;
328
+ let cachedPublishCounter;
329
+ let cachedConsumeCounter;
330
+ let cachedPublishLatencyHistogram;
331
+ let cachedConsumeLatencyHistogram;
332
+ /**
333
+ * Try to load the OpenTelemetry API module.
334
+ * Returns null if the module is not available.
335
+ */
336
+ function tryLoadOpenTelemetryApi() {
337
+ if (otelApi === void 0) try {
338
+ otelApi = __require("@opentelemetry/api");
339
+ } catch {
340
+ otelApi = null;
341
+ }
342
+ return otelApi;
343
+ }
344
+ /**
345
+ * Get or create a tracer instance.
346
+ */
347
+ function getTracer() {
348
+ if (cachedTracer !== void 0) return cachedTracer;
349
+ const api = tryLoadOpenTelemetryApi();
350
+ if (!api) return;
351
+ cachedTracer = api.trace.getTracer(INSTRUMENTATION_SCOPE_NAME, INSTRUMENTATION_SCOPE_VERSION);
352
+ return cachedTracer;
353
+ }
354
+ /**
355
+ * Get or create a meter and its instruments.
356
+ */
357
+ function getMeterInstruments() {
358
+ if (cachedPublishCounter !== void 0) return {
359
+ publishCounter: cachedPublishCounter,
360
+ consumeCounter: cachedConsumeCounter,
361
+ publishLatencyHistogram: cachedPublishLatencyHistogram,
362
+ consumeLatencyHistogram: cachedConsumeLatencyHistogram
363
+ };
364
+ const api = tryLoadOpenTelemetryApi();
365
+ if (!api) return {
366
+ publishCounter: void 0,
367
+ consumeCounter: void 0,
368
+ publishLatencyHistogram: void 0,
369
+ consumeLatencyHistogram: void 0
370
+ };
371
+ const meter = api.metrics.getMeter(INSTRUMENTATION_SCOPE_NAME, INSTRUMENTATION_SCOPE_VERSION);
372
+ cachedPublishCounter = meter.createCounter("amqp.client.messages.published", {
373
+ description: "Number of messages published to AMQP broker",
374
+ unit: "{message}"
375
+ });
376
+ cachedConsumeCounter = meter.createCounter("amqp.worker.messages.consumed", {
377
+ description: "Number of messages consumed from AMQP broker",
378
+ unit: "{message}"
379
+ });
380
+ cachedPublishLatencyHistogram = meter.createHistogram("amqp.client.publish.duration", {
381
+ description: "Duration of message publish operations",
382
+ unit: "ms"
383
+ });
384
+ cachedConsumeLatencyHistogram = meter.createHistogram("amqp.worker.process.duration", {
385
+ description: "Duration of message processing operations",
386
+ unit: "ms"
387
+ });
388
+ return {
389
+ publishCounter: cachedPublishCounter,
390
+ consumeCounter: cachedConsumeCounter,
391
+ publishLatencyHistogram: cachedPublishLatencyHistogram,
392
+ consumeLatencyHistogram: cachedConsumeLatencyHistogram
393
+ };
394
+ }
395
+ /**
396
+ * Default telemetry provider that uses OpenTelemetry API if available.
397
+ */
398
+ const defaultTelemetryProvider = {
399
+ getTracer,
400
+ getPublishCounter: () => getMeterInstruments().publishCounter,
401
+ getConsumeCounter: () => getMeterInstruments().consumeCounter,
402
+ getPublishLatencyHistogram: () => getMeterInstruments().publishLatencyHistogram,
403
+ getConsumeLatencyHistogram: () => getMeterInstruments().consumeLatencyHistogram
404
+ };
405
+ /**
406
+ * Create a span for a publish operation.
407
+ * Returns undefined if OpenTelemetry is not available.
408
+ */
409
+ function startPublishSpan(provider, exchangeName, routingKey, attributes) {
410
+ const tracer = provider.getTracer();
411
+ if (!tracer) return;
412
+ const spanName = `${exchangeName} publish`;
413
+ return tracer.startSpan(spanName, {
414
+ kind: SpanKind.PRODUCER,
415
+ attributes: {
416
+ [MessagingSemanticConventions.MESSAGING_SYSTEM]: MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,
417
+ [MessagingSemanticConventions.MESSAGING_DESTINATION]: exchangeName,
418
+ [MessagingSemanticConventions.MESSAGING_DESTINATION_KIND]: MessagingSemanticConventions.MESSAGING_DESTINATION_KIND_EXCHANGE,
419
+ [MessagingSemanticConventions.MESSAGING_OPERATION]: MessagingSemanticConventions.MESSAGING_OPERATION_PUBLISH,
420
+ ...routingKey ? { [MessagingSemanticConventions.MESSAGING_RABBITMQ_ROUTING_KEY]: routingKey } : {},
421
+ ...attributes
422
+ }
423
+ });
424
+ }
425
+ /**
426
+ * Create a span for a consume/process operation.
427
+ * Returns undefined if OpenTelemetry is not available.
428
+ */
429
+ function startConsumeSpan(provider, queueName, consumerName, attributes) {
430
+ const tracer = provider.getTracer();
431
+ if (!tracer) return;
432
+ const spanName = `${queueName} process`;
433
+ return tracer.startSpan(spanName, {
434
+ kind: SpanKind.CONSUMER,
435
+ attributes: {
436
+ [MessagingSemanticConventions.MESSAGING_SYSTEM]: MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,
437
+ [MessagingSemanticConventions.MESSAGING_DESTINATION]: queueName,
438
+ [MessagingSemanticConventions.MESSAGING_DESTINATION_KIND]: MessagingSemanticConventions.MESSAGING_DESTINATION_KIND_QUEUE,
439
+ [MessagingSemanticConventions.MESSAGING_OPERATION]: MessagingSemanticConventions.MESSAGING_OPERATION_PROCESS,
440
+ "amqp.consumer.name": consumerName,
441
+ ...attributes
442
+ }
443
+ });
444
+ }
445
+ /**
446
+ * End a span with success status.
447
+ */
448
+ function endSpanSuccess(span) {
449
+ if (!span) return;
450
+ const api = tryLoadOpenTelemetryApi();
451
+ if (api) span.setStatus({ code: api.SpanStatusCode.OK });
452
+ span.end();
453
+ }
454
+ /**
455
+ * End a span with error status.
456
+ */
457
+ function endSpanError(span, error) {
458
+ if (!span) return;
459
+ const api = tryLoadOpenTelemetryApi();
460
+ if (api) {
461
+ span.setStatus({
462
+ code: api.SpanStatusCode.ERROR,
463
+ message: error.message
464
+ });
465
+ span.recordException(error);
466
+ span.setAttribute(MessagingSemanticConventions.ERROR_TYPE, error.name);
467
+ }
468
+ span.end();
469
+ }
470
+ /**
471
+ * Record a publish metric.
472
+ */
473
+ function recordPublishMetric(provider, exchangeName, routingKey, success, durationMs) {
474
+ const publishCounter = provider.getPublishCounter();
475
+ const publishLatencyHistogram = provider.getPublishLatencyHistogram();
476
+ const attributes = {
477
+ [MessagingSemanticConventions.MESSAGING_SYSTEM]: MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,
478
+ [MessagingSemanticConventions.MESSAGING_DESTINATION]: exchangeName,
479
+ ...routingKey ? { [MessagingSemanticConventions.MESSAGING_RABBITMQ_ROUTING_KEY]: routingKey } : {},
480
+ success
481
+ };
482
+ publishCounter?.add(1, attributes);
483
+ publishLatencyHistogram?.record(durationMs, attributes);
484
+ }
485
+ /**
486
+ * Record a consume metric.
487
+ */
488
+ function recordConsumeMetric(provider, queueName, consumerName, success, durationMs) {
489
+ const consumeCounter = provider.getConsumeCounter();
490
+ const consumeLatencyHistogram = provider.getConsumeLatencyHistogram();
491
+ const attributes = {
492
+ [MessagingSemanticConventions.MESSAGING_SYSTEM]: MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,
493
+ [MessagingSemanticConventions.MESSAGING_DESTINATION]: queueName,
494
+ "amqp.consumer.name": consumerName,
495
+ success
496
+ };
497
+ consumeCounter?.add(1, attributes);
498
+ consumeLatencyHistogram?.record(durationMs, attributes);
499
+ }
500
+ /**
501
+ * Reset the cached OpenTelemetry API module and instruments.
502
+ * For testing purposes only.
503
+ * @internal
504
+ */
505
+ function _resetTelemetryCacheForTesting() {
506
+ otelApi = void 0;
507
+ cachedTracer = void 0;
508
+ cachedPublishCounter = void 0;
509
+ cachedConsumeCounter = void 0;
510
+ cachedPublishLatencyHistogram = void 0;
511
+ cachedConsumeLatencyHistogram = void 0;
512
+ }
513
+
514
+ //#endregion
515
+ export { AmqpClient, ConnectionManagerSingleton, MessagingSemanticConventions, _resetTelemetryCacheForTesting, defaultTelemetryProvider, endSpanError, endSpanSuccess, recordConsumeMetric, recordPublishMetric, setupAmqpTopology, startConsumeSpan, startPublishSpan };
285
516
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/connection-manager.ts","../src/setup.ts","../src/amqp-client.ts"],"sourcesContent":["import amqp, {\n AmqpConnectionManager,\n AmqpConnectionManagerOptions,\n ConnectionUrl,\n} from \"amqp-connection-manager\";\n\n/**\n * Connection manager singleton for sharing AMQP connections across clients.\n *\n * This singleton implements connection pooling to avoid creating multiple connections\n * to the same broker, which is a RabbitMQ best practice. Connections are identified\n * by their URLs and connection options, and reference counting ensures connections\n * are only closed when all clients have released them.\n *\n * @example\n * ```typescript\n * const manager = ConnectionManagerSingleton.getInstance();\n * const connection = manager.getConnection(['amqp://localhost']);\n * // ... use connection ...\n * await manager.releaseConnection(['amqp://localhost']);\n * ```\n */\nexport class ConnectionManagerSingleton {\n private static instance: ConnectionManagerSingleton;\n private connections: Map<string, AmqpConnectionManager> = new Map();\n private refCounts: Map<string, number> = new Map();\n\n private constructor() {}\n\n /**\n * Get the singleton instance of the connection manager.\n *\n * @returns The singleton instance\n */\n static getInstance(): ConnectionManagerSingleton {\n if (!ConnectionManagerSingleton.instance) {\n ConnectionManagerSingleton.instance = new ConnectionManagerSingleton();\n }\n return ConnectionManagerSingleton.instance;\n }\n\n /**\n * Get or create a connection for the given URLs and options.\n *\n * If a connection already exists with the same URLs and options, it is reused\n * and its reference count is incremented. Otherwise, a new connection is created.\n *\n * @param urls - AMQP broker URL(s)\n * @param connectionOptions - Optional connection configuration\n * @returns The AMQP connection manager instance\n */\n getConnection(\n urls: ConnectionUrl[],\n connectionOptions?: AmqpConnectionManagerOptions,\n ): AmqpConnectionManager {\n // Create a key based on URLs and connection options\n const key = this.createConnectionKey(urls, connectionOptions);\n\n if (!this.connections.has(key)) {\n const connection = amqp.connect(urls, connectionOptions);\n this.connections.set(key, connection);\n this.refCounts.set(key, 0);\n }\n\n // Increment reference count\n this.refCounts.set(key, (this.refCounts.get(key) ?? 0) + 1);\n\n return this.connections.get(key)!;\n }\n\n /**\n * Release a connection reference.\n *\n * Decrements the reference count for the connection. If the count reaches zero,\n * the connection is closed and removed from the pool.\n *\n * @param urls - AMQP broker URL(s) used to identify the connection\n * @param connectionOptions - Optional connection configuration used to identify the connection\n * @returns A promise that resolves when the connection is released (and closed if necessary)\n */\n async releaseConnection(\n urls: ConnectionUrl[],\n connectionOptions?: AmqpConnectionManagerOptions,\n ): Promise<void> {\n const key = this.createConnectionKey(urls, connectionOptions);\n const refCount = this.refCounts.get(key) ?? 0;\n\n if (refCount <= 1) {\n // Last reference - close and remove connection\n const connection = this.connections.get(key);\n if (connection) {\n await connection.close();\n this.connections.delete(key);\n this.refCounts.delete(key);\n }\n } else {\n // Decrement reference count\n this.refCounts.set(key, refCount - 1);\n }\n }\n\n /**\n * Create a unique key for a connection based on URLs and options.\n *\n * The key is deterministic: same URLs and options always produce the same key,\n * enabling connection reuse.\n *\n * @param urls - AMQP broker URL(s)\n * @param connectionOptions - Optional connection configuration\n * @returns A unique string key identifying the connection\n */\n private createConnectionKey(\n urls: ConnectionUrl[],\n connectionOptions?: AmqpConnectionManagerOptions,\n ): string {\n // Create a deterministic key from URLs and options\n // Use JSON.stringify for URLs to avoid ambiguity (e.g., ['a,b'] vs ['a', 'b'])\n const urlsStr = JSON.stringify(urls);\n // Sort object keys for deterministic serialization of connection options\n const optsStr = connectionOptions ? this.serializeOptions(connectionOptions) : \"\";\n return `${urlsStr}::${optsStr}`;\n }\n\n /**\n * Serialize connection options to a deterministic string.\n *\n * @param options - Connection options to serialize\n * @returns A JSON string with sorted keys for deterministic comparison\n */\n private serializeOptions(options: AmqpConnectionManagerOptions): string {\n // Create a deterministic string representation by deeply sorting all object keys\n const sorted = this.deepSort(options);\n return JSON.stringify(sorted);\n }\n\n /**\n * Deep sort an object's keys for deterministic serialization.\n *\n * @param value - The value to deep sort (can be object, array, or primitive)\n * @returns The value with all object keys sorted alphabetically\n */\n private deepSort(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => this.deepSort(item));\n }\n\n if (value !== null && typeof value === \"object\") {\n const obj = value as Record<string, unknown>;\n const sortedKeys = Object.keys(obj).sort();\n const result: Record<string, unknown> = {};\n\n for (const key of sortedKeys) {\n result[key] = this.deepSort(obj[key]);\n }\n\n return result;\n }\n\n return value;\n }\n\n /**\n * Reset all cached connections (for testing purposes)\n * @internal\n */\n async _resetForTesting(): Promise<void> {\n // Close all connections before clearing\n const closePromises = Array.from(this.connections.values()).map((conn) => conn.close());\n await Promise.all(closePromises);\n this.connections.clear();\n this.refCounts.clear();\n }\n}\n","import type { Channel } from \"amqplib\";\nimport type { ContractDefinition } from \"@amqp-contract/contract\";\n\n/**\n * Setup AMQP topology (exchanges, queues, and bindings) from a contract definition.\n *\n * This function sets up the complete AMQP topology in the correct order:\n * 1. Assert all exchanges defined in the contract\n * 2. Validate dead letter exchanges are declared before referencing them\n * 3. Assert all queues with their configurations (including dead letter settings)\n * 4. Create all bindings (queue-to-exchange and exchange-to-exchange)\n *\n * @param channel - The AMQP channel to use for topology setup\n * @param contract - The contract definition containing the topology specification\n * @throws {AggregateError} If any exchanges, queues, or bindings fail to be created\n * @throws {Error} If a queue references a dead letter exchange not declared in the contract\n *\n * @example\n * ```typescript\n * const channel = await connection.createChannel();\n * await setupAmqpTopology(channel, contract);\n * ```\n */\nexport async function setupAmqpTopology(\n channel: Channel,\n contract: ContractDefinition,\n): Promise<void> {\n // Setup exchanges\n const exchangeResults = await Promise.allSettled(\n Object.values(contract.exchanges ?? {}).map((exchange) =>\n channel.assertExchange(exchange.name, exchange.type, {\n durable: exchange.durable,\n autoDelete: exchange.autoDelete,\n internal: exchange.internal,\n arguments: exchange.arguments,\n }),\n ),\n );\n const exchangeErrors = exchangeResults.filter(\n (result): result is PromiseRejectedResult => result.status === \"rejected\",\n );\n if (exchangeErrors.length > 0) {\n throw new AggregateError(\n exchangeErrors.map(({ reason }) => reason),\n \"Failed to setup exchanges\",\n );\n }\n\n // Validate dead letter exchanges before setting up queues\n for (const queue of Object.values(contract.queues ?? {})) {\n if (queue.deadLetter) {\n const dlxName = queue.deadLetter.exchange.name;\n const exchangeExists = Object.values(contract.exchanges ?? {}).some(\n (exchange) => exchange.name === dlxName,\n );\n\n if (!exchangeExists) {\n throw new Error(\n `Queue \"${queue.name}\" references dead letter exchange \"${dlxName}\" which is not declared in the contract. ` +\n `Add the exchange to contract.exchanges to ensure it is created before the queue.`,\n );\n }\n }\n }\n\n // Setup queues\n const queueResults = await Promise.allSettled(\n Object.values(contract.queues ?? {}).map((queue) => {\n // Build queue arguments, merging dead letter configuration if present\n const queueArguments = { ...queue.arguments };\n if (queue.deadLetter) {\n queueArguments[\"x-dead-letter-exchange\"] = queue.deadLetter.exchange.name;\n if (queue.deadLetter.routingKey) {\n queueArguments[\"x-dead-letter-routing-key\"] = queue.deadLetter.routingKey;\n }\n }\n\n return channel.assertQueue(queue.name, {\n durable: queue.durable,\n exclusive: queue.exclusive,\n autoDelete: queue.autoDelete,\n arguments: queueArguments,\n });\n }),\n );\n const queueErrors = queueResults.filter(\n (result): result is PromiseRejectedResult => result.status === \"rejected\",\n );\n if (queueErrors.length > 0) {\n throw new AggregateError(\n queueErrors.map(({ reason }) => reason),\n \"Failed to setup queues\",\n );\n }\n\n // Setup bindings\n const bindingResults = await Promise.allSettled(\n Object.values(contract.bindings ?? {}).map((binding) => {\n if (binding.type === \"queue\") {\n return channel.bindQueue(\n binding.queue.name,\n binding.exchange.name,\n binding.routingKey ?? \"\",\n binding.arguments,\n );\n }\n\n return channel.bindExchange(\n binding.destination.name,\n binding.source.name,\n binding.routingKey ?? \"\",\n binding.arguments,\n );\n }),\n );\n const bindingErrors = bindingResults.filter(\n (result): result is PromiseRejectedResult => result.status === \"rejected\",\n );\n if (bindingErrors.length > 0) {\n throw new AggregateError(\n bindingErrors.map(({ reason }) => reason),\n \"Failed to setup bindings\",\n );\n }\n}\n","import type {\n AmqpConnectionManager,\n AmqpConnectionManagerOptions,\n ChannelWrapper,\n ConnectionUrl,\n CreateChannelOpts,\n} from \"amqp-connection-manager\";\nimport type { Channel } from \"amqplib\";\nimport { ConnectionManagerSingleton } from \"./connection-manager.js\";\nimport type { ContractDefinition } from \"@amqp-contract/contract\";\nimport { setupAmqpTopology } from \"./setup.js\";\n\n/**\n * Options for creating an AMQP client.\n *\n * @property urls - AMQP broker URL(s). Multiple URLs provide failover support.\n * @property connectionOptions - Optional connection configuration (heartbeat, reconnect settings, etc.).\n * @property channelOptions - Optional channel configuration options.\n */\nexport type AmqpClientOptions = {\n urls: ConnectionUrl[];\n connectionOptions?: AmqpConnectionManagerOptions | undefined;\n channelOptions?: Partial<CreateChannelOpts> | undefined;\n};\n\n/**\n * AMQP client that manages connections and channels with automatic topology setup.\n *\n * This class handles:\n * - Connection management with automatic reconnection via amqp-connection-manager\n * - Connection pooling and sharing across instances with the same URLs\n * - Automatic AMQP topology setup (exchanges, queues, bindings) from contract\n * - Channel creation with JSON serialization enabled by default\n *\n * @example\n * ```typescript\n * const client = new AmqpClient(contract, {\n * urls: ['amqp://localhost'],\n * connectionOptions: { heartbeatIntervalInSeconds: 30 }\n * });\n *\n * // Use the channel to publish messages\n * await client.channel.publish('exchange', 'routingKey', { data: 'value' });\n *\n * // Close when done\n * await client.close();\n * ```\n */\nexport class AmqpClient {\n private readonly connection: AmqpConnectionManager;\n public readonly channel: ChannelWrapper;\n private readonly urls: ConnectionUrl[];\n private readonly connectionOptions?: AmqpConnectionManagerOptions;\n\n /**\n * Create a new AMQP client instance.\n *\n * The client will automatically:\n * - Get or create a shared connection using the singleton pattern\n * - Set up AMQP topology (exchanges, queues, bindings) from the contract\n * - Create a channel with JSON serialization enabled\n *\n * @param contract - The contract definition specifying the AMQP topology\n * @param options - Client configuration options\n */\n constructor(\n private readonly contract: ContractDefinition,\n options: AmqpClientOptions,\n ) {\n // Store for cleanup\n this.urls = options.urls;\n if (options.connectionOptions !== undefined) {\n this.connectionOptions = options.connectionOptions;\n }\n\n // Always use singleton to get/create connection\n const singleton = ConnectionManagerSingleton.getInstance();\n this.connection = singleton.getConnection(options.urls, options.connectionOptions);\n\n // Create default setup function that calls setupAmqpTopology\n const defaultSetup = (channel: Channel) => setupAmqpTopology(channel, this.contract);\n\n // Destructure setup from channelOptions to handle it separately\n const { setup: userSetup, ...otherChannelOptions } = options.channelOptions ?? {};\n\n // Merge user-provided channel options with defaults\n const channelOpts: CreateChannelOpts = {\n json: true,\n setup: defaultSetup,\n ...otherChannelOptions,\n };\n\n // If user provided a custom setup, wrap it to call both\n if (userSetup) {\n channelOpts.setup = async (channel: Channel) => {\n // First run the topology setup\n await defaultSetup(channel);\n // Then run user's setup - check arity to determine if it expects a callback\n if (userSetup.length === 2) {\n // Callback-based setup function\n await new Promise<void>((resolve, reject) => {\n (userSetup as (channel: Channel, callback: (error?: Error) => void) => void)(\n channel,\n (error?: Error) => {\n if (error) reject(error);\n else resolve();\n },\n );\n });\n } else {\n // Promise-based setup function\n await (userSetup as (channel: Channel) => Promise<void>)(channel);\n }\n };\n }\n\n this.channel = this.connection.createChannel(channelOpts);\n }\n\n /**\n * Get the underlying connection manager\n *\n * This method exposes the AmqpConnectionManager instance that this client uses.\n * The connection is automatically shared across all AmqpClient instances that\n * use the same URLs and connection options.\n *\n * @returns The AmqpConnectionManager instance used by this client\n */\n getConnection(): AmqpConnectionManager {\n return this.connection;\n }\n\n /**\n * Close the channel and release the connection reference.\n *\n * This will:\n * - Close the channel wrapper\n * - Decrease the reference count on the shared connection\n * - Close the connection if this was the last client using it\n *\n * @returns A promise that resolves when the channel and connection are closed\n */\n async close(): Promise<void> {\n await this.channel.close();\n // Release connection reference - will close connection if this was the last reference\n const singleton = ConnectionManagerSingleton.getInstance();\n await singleton.releaseConnection(this.urls, this.connectionOptions);\n }\n\n /**\n * Reset connection singleton cache (for testing only)\n * @internal\n */\n static async _resetConnectionCacheForTesting(): Promise<void> {\n await ConnectionManagerSingleton.getInstance()._resetForTesting();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/connection-manager.ts","../src/setup.ts","../src/amqp-client.ts","../src/telemetry.ts"],"sourcesContent":["import amqp, {\n AmqpConnectionManager,\n AmqpConnectionManagerOptions,\n ConnectionUrl,\n} from \"amqp-connection-manager\";\n\n/**\n * Connection manager singleton for sharing AMQP connections across clients.\n *\n * This singleton implements connection pooling to avoid creating multiple connections\n * to the same broker, which is a RabbitMQ best practice. Connections are identified\n * by their URLs and connection options, and reference counting ensures connections\n * are only closed when all clients have released them.\n *\n * @example\n * ```typescript\n * const manager = ConnectionManagerSingleton.getInstance();\n * const connection = manager.getConnection(['amqp://localhost']);\n * // ... use connection ...\n * await manager.releaseConnection(['amqp://localhost']);\n * ```\n */\nexport class ConnectionManagerSingleton {\n private static instance: ConnectionManagerSingleton;\n private connections: Map<string, AmqpConnectionManager> = new Map();\n private refCounts: Map<string, number> = new Map();\n\n private constructor() {}\n\n /**\n * Get the singleton instance of the connection manager.\n *\n * @returns The singleton instance\n */\n static getInstance(): ConnectionManagerSingleton {\n if (!ConnectionManagerSingleton.instance) {\n ConnectionManagerSingleton.instance = new ConnectionManagerSingleton();\n }\n return ConnectionManagerSingleton.instance;\n }\n\n /**\n * Get or create a connection for the given URLs and options.\n *\n * If a connection already exists with the same URLs and options, it is reused\n * and its reference count is incremented. Otherwise, a new connection is created.\n *\n * @param urls - AMQP broker URL(s)\n * @param connectionOptions - Optional connection configuration\n * @returns The AMQP connection manager instance\n */\n getConnection(\n urls: ConnectionUrl[],\n connectionOptions?: AmqpConnectionManagerOptions,\n ): AmqpConnectionManager {\n // Create a key based on URLs and connection options\n const key = this.createConnectionKey(urls, connectionOptions);\n\n if (!this.connections.has(key)) {\n const connection = amqp.connect(urls, connectionOptions);\n this.connections.set(key, connection);\n this.refCounts.set(key, 0);\n }\n\n // Increment reference count\n this.refCounts.set(key, (this.refCounts.get(key) ?? 0) + 1);\n\n return this.connections.get(key)!;\n }\n\n /**\n * Release a connection reference.\n *\n * Decrements the reference count for the connection. If the count reaches zero,\n * the connection is closed and removed from the pool.\n *\n * @param urls - AMQP broker URL(s) used to identify the connection\n * @param connectionOptions - Optional connection configuration used to identify the connection\n * @returns A promise that resolves when the connection is released (and closed if necessary)\n */\n async releaseConnection(\n urls: ConnectionUrl[],\n connectionOptions?: AmqpConnectionManagerOptions,\n ): Promise<void> {\n const key = this.createConnectionKey(urls, connectionOptions);\n const refCount = this.refCounts.get(key) ?? 0;\n\n if (refCount <= 1) {\n // Last reference - close and remove connection\n const connection = this.connections.get(key);\n if (connection) {\n await connection.close();\n this.connections.delete(key);\n this.refCounts.delete(key);\n }\n } else {\n // Decrement reference count\n this.refCounts.set(key, refCount - 1);\n }\n }\n\n /**\n * Create a unique key for a connection based on URLs and options.\n *\n * The key is deterministic: same URLs and options always produce the same key,\n * enabling connection reuse.\n *\n * @param urls - AMQP broker URL(s)\n * @param connectionOptions - Optional connection configuration\n * @returns A unique string key identifying the connection\n */\n private createConnectionKey(\n urls: ConnectionUrl[],\n connectionOptions?: AmqpConnectionManagerOptions,\n ): string {\n // Create a deterministic key from URLs and options\n // Use JSON.stringify for URLs to avoid ambiguity (e.g., ['a,b'] vs ['a', 'b'])\n const urlsStr = JSON.stringify(urls);\n // Sort object keys for deterministic serialization of connection options\n const optsStr = connectionOptions ? this.serializeOptions(connectionOptions) : \"\";\n return `${urlsStr}::${optsStr}`;\n }\n\n /**\n * Serialize connection options to a deterministic string.\n *\n * @param options - Connection options to serialize\n * @returns A JSON string with sorted keys for deterministic comparison\n */\n private serializeOptions(options: AmqpConnectionManagerOptions): string {\n // Create a deterministic string representation by deeply sorting all object keys\n const sorted = this.deepSort(options);\n return JSON.stringify(sorted);\n }\n\n /**\n * Deep sort an object's keys for deterministic serialization.\n *\n * @param value - The value to deep sort (can be object, array, or primitive)\n * @returns The value with all object keys sorted alphabetically\n */\n private deepSort(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => this.deepSort(item));\n }\n\n if (value !== null && typeof value === \"object\") {\n const obj = value as Record<string, unknown>;\n const sortedKeys = Object.keys(obj).sort();\n const result: Record<string, unknown> = {};\n\n for (const key of sortedKeys) {\n result[key] = this.deepSort(obj[key]);\n }\n\n return result;\n }\n\n return value;\n }\n\n /**\n * Reset all cached connections (for testing purposes)\n * @internal\n */\n async _resetForTesting(): Promise<void> {\n // Close all connections before clearing\n const closePromises = Array.from(this.connections.values()).map((conn) => conn.close());\n await Promise.all(closePromises);\n this.connections.clear();\n this.refCounts.clear();\n }\n}\n","import type { Channel } from \"amqplib\";\nimport type { ContractDefinition } from \"@amqp-contract/contract\";\n\n/**\n * Setup AMQP topology (exchanges, queues, and bindings) from a contract definition.\n *\n * This function sets up the complete AMQP topology in the correct order:\n * 1. Assert all exchanges defined in the contract\n * 2. Validate dead letter exchanges are declared before referencing them\n * 3. Assert all queues with their configurations (including dead letter settings)\n * 4. Create all bindings (queue-to-exchange and exchange-to-exchange)\n *\n * @param channel - The AMQP channel to use for topology setup\n * @param contract - The contract definition containing the topology specification\n * @throws {AggregateError} If any exchanges, queues, or bindings fail to be created\n * @throws {Error} If a queue references a dead letter exchange not declared in the contract\n *\n * @example\n * ```typescript\n * const channel = await connection.createChannel();\n * await setupAmqpTopology(channel, contract);\n * ```\n */\nexport async function setupAmqpTopology(\n channel: Channel,\n contract: ContractDefinition,\n): Promise<void> {\n // Setup exchanges\n const exchangeResults = await Promise.allSettled(\n Object.values(contract.exchanges ?? {}).map((exchange) =>\n channel.assertExchange(exchange.name, exchange.type, {\n durable: exchange.durable,\n autoDelete: exchange.autoDelete,\n internal: exchange.internal,\n arguments: exchange.arguments,\n }),\n ),\n );\n const exchangeErrors = exchangeResults.filter(\n (result): result is PromiseRejectedResult => result.status === \"rejected\",\n );\n if (exchangeErrors.length > 0) {\n throw new AggregateError(\n exchangeErrors.map(({ reason }) => reason),\n \"Failed to setup exchanges\",\n );\n }\n\n // Validate dead letter exchanges before setting up queues\n for (const queue of Object.values(contract.queues ?? {})) {\n if (queue.deadLetter) {\n const dlxName = queue.deadLetter.exchange.name;\n const exchangeExists = Object.values(contract.exchanges ?? {}).some(\n (exchange) => exchange.name === dlxName,\n );\n\n if (!exchangeExists) {\n throw new Error(\n `Queue \"${queue.name}\" references dead letter exchange \"${dlxName}\" which is not declared in the contract. ` +\n `Add the exchange to contract.exchanges to ensure it is created before the queue.`,\n );\n }\n }\n }\n\n // Setup queues\n const queueResults = await Promise.allSettled(\n Object.values(contract.queues ?? {}).map((queue) => {\n // Build queue arguments, merging dead letter configuration if present\n const queueArguments = { ...queue.arguments };\n if (queue.deadLetter) {\n queueArguments[\"x-dead-letter-exchange\"] = queue.deadLetter.exchange.name;\n if (queue.deadLetter.routingKey) {\n queueArguments[\"x-dead-letter-routing-key\"] = queue.deadLetter.routingKey;\n }\n }\n\n return channel.assertQueue(queue.name, {\n durable: queue.durable,\n exclusive: queue.exclusive,\n autoDelete: queue.autoDelete,\n arguments: queueArguments,\n });\n }),\n );\n const queueErrors = queueResults.filter(\n (result): result is PromiseRejectedResult => result.status === \"rejected\",\n );\n if (queueErrors.length > 0) {\n throw new AggregateError(\n queueErrors.map(({ reason }) => reason),\n \"Failed to setup queues\",\n );\n }\n\n // Setup bindings\n const bindingResults = await Promise.allSettled(\n Object.values(contract.bindings ?? {}).map((binding) => {\n if (binding.type === \"queue\") {\n return channel.bindQueue(\n binding.queue.name,\n binding.exchange.name,\n binding.routingKey ?? \"\",\n binding.arguments,\n );\n }\n\n return channel.bindExchange(\n binding.destination.name,\n binding.source.name,\n binding.routingKey ?? \"\",\n binding.arguments,\n );\n }),\n );\n const bindingErrors = bindingResults.filter(\n (result): result is PromiseRejectedResult => result.status === \"rejected\",\n );\n if (bindingErrors.length > 0) {\n throw new AggregateError(\n bindingErrors.map(({ reason }) => reason),\n \"Failed to setup bindings\",\n );\n }\n}\n","import type {\n AmqpConnectionManager,\n AmqpConnectionManagerOptions,\n ChannelWrapper,\n ConnectionUrl,\n CreateChannelOpts,\n} from \"amqp-connection-manager\";\nimport type { Channel } from \"amqplib\";\nimport { ConnectionManagerSingleton } from \"./connection-manager.js\";\nimport type { ContractDefinition } from \"@amqp-contract/contract\";\nimport { setupAmqpTopology } from \"./setup.js\";\n\n/**\n * Options for creating an AMQP client.\n *\n * @property urls - AMQP broker URL(s). Multiple URLs provide failover support.\n * @property connectionOptions - Optional connection configuration (heartbeat, reconnect settings, etc.).\n * @property channelOptions - Optional channel configuration options.\n */\nexport type AmqpClientOptions = {\n urls: ConnectionUrl[];\n connectionOptions?: AmqpConnectionManagerOptions | undefined;\n channelOptions?: Partial<CreateChannelOpts> | undefined;\n};\n\n/**\n * AMQP client that manages connections and channels with automatic topology setup.\n *\n * This class handles:\n * - Connection management with automatic reconnection via amqp-connection-manager\n * - Connection pooling and sharing across instances with the same URLs\n * - Automatic AMQP topology setup (exchanges, queues, bindings) from contract\n * - Channel creation with JSON serialization enabled by default\n *\n * @example\n * ```typescript\n * const client = new AmqpClient(contract, {\n * urls: ['amqp://localhost'],\n * connectionOptions: { heartbeatIntervalInSeconds: 30 }\n * });\n *\n * // Use the channel to publish messages\n * await client.channel.publish('exchange', 'routingKey', { data: 'value' });\n *\n * // Close when done\n * await client.close();\n * ```\n */\nexport class AmqpClient {\n private readonly connection: AmqpConnectionManager;\n public readonly channel: ChannelWrapper;\n private readonly urls: ConnectionUrl[];\n private readonly connectionOptions?: AmqpConnectionManagerOptions;\n\n /**\n * Create a new AMQP client instance.\n *\n * The client will automatically:\n * - Get or create a shared connection using the singleton pattern\n * - Set up AMQP topology (exchanges, queues, bindings) from the contract\n * - Create a channel with JSON serialization enabled\n *\n * @param contract - The contract definition specifying the AMQP topology\n * @param options - Client configuration options\n */\n constructor(\n private readonly contract: ContractDefinition,\n options: AmqpClientOptions,\n ) {\n // Store for cleanup\n this.urls = options.urls;\n if (options.connectionOptions !== undefined) {\n this.connectionOptions = options.connectionOptions;\n }\n\n // Always use singleton to get/create connection\n const singleton = ConnectionManagerSingleton.getInstance();\n this.connection = singleton.getConnection(options.urls, options.connectionOptions);\n\n // Create default setup function that calls setupAmqpTopology\n const defaultSetup = (channel: Channel) => setupAmqpTopology(channel, this.contract);\n\n // Destructure setup from channelOptions to handle it separately\n const { setup: userSetup, ...otherChannelOptions } = options.channelOptions ?? {};\n\n // Merge user-provided channel options with defaults\n const channelOpts: CreateChannelOpts = {\n json: true,\n setup: defaultSetup,\n ...otherChannelOptions,\n };\n\n // If user provided a custom setup, wrap it to call both\n if (userSetup) {\n channelOpts.setup = async (channel: Channel) => {\n // First run the topology setup\n await defaultSetup(channel);\n // Then run user's setup - check arity to determine if it expects a callback\n if (userSetup.length === 2) {\n // Callback-based setup function\n await new Promise<void>((resolve, reject) => {\n (userSetup as (channel: Channel, callback: (error?: Error) => void) => void)(\n channel,\n (error?: Error) => {\n if (error) reject(error);\n else resolve();\n },\n );\n });\n } else {\n // Promise-based setup function\n await (userSetup as (channel: Channel) => Promise<void>)(channel);\n }\n };\n }\n\n this.channel = this.connection.createChannel(channelOpts);\n }\n\n /**\n * Get the underlying connection manager\n *\n * This method exposes the AmqpConnectionManager instance that this client uses.\n * The connection is automatically shared across all AmqpClient instances that\n * use the same URLs and connection options.\n *\n * @returns The AmqpConnectionManager instance used by this client\n */\n getConnection(): AmqpConnectionManager {\n return this.connection;\n }\n\n /**\n * Close the channel and release the connection reference.\n *\n * This will:\n * - Close the channel wrapper\n * - Decrease the reference count on the shared connection\n * - Close the connection if this was the last client using it\n *\n * @returns A promise that resolves when the channel and connection are closed\n */\n async close(): Promise<void> {\n await this.channel.close();\n // Release connection reference - will close connection if this was the last reference\n const singleton = ConnectionManagerSingleton.getInstance();\n await singleton.releaseConnection(this.urls, this.connectionOptions);\n }\n\n /**\n * Reset connection singleton cache (for testing only)\n * @internal\n */\n static async _resetConnectionCacheForTesting(): Promise<void> {\n await ConnectionManagerSingleton.getInstance()._resetForTesting();\n }\n}\n","import {\n type Attributes,\n type Counter,\n type Histogram,\n type Span,\n type Tracer,\n} from \"@opentelemetry/api\";\n\n/**\n * SpanKind values from OpenTelemetry.\n * Defined as constants to avoid runtime dependency when types are used.\n * @see https://opentelemetry.io/docs/specs/otel/trace/api/#spankind\n */\nconst SpanKind = {\n /** Producer span represents a message producer */\n PRODUCER: 3,\n /** Consumer span represents a message consumer */\n CONSUMER: 4,\n} as const;\n\n/**\n * Semantic conventions for AMQP messaging following OpenTelemetry standards.\n * @see https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/\n */\nexport const MessagingSemanticConventions = {\n // Messaging attributes\n MESSAGING_SYSTEM: \"messaging.system\",\n MESSAGING_DESTINATION: \"messaging.destination.name\",\n MESSAGING_DESTINATION_KIND: \"messaging.destination.kind\",\n MESSAGING_OPERATION: \"messaging.operation\",\n MESSAGING_MESSAGE_ID: \"messaging.message.id\",\n MESSAGING_MESSAGE_PAYLOAD_SIZE: \"messaging.message.body.size\",\n MESSAGING_MESSAGE_CONVERSATION_ID: \"messaging.message.conversation_id\",\n\n // AMQP specific attributes\n MESSAGING_RABBITMQ_ROUTING_KEY: \"messaging.rabbitmq.destination.routing_key\",\n MESSAGING_RABBITMQ_MESSAGE_DELIVERY_TAG: \"messaging.rabbitmq.message.delivery_tag\",\n\n // Error attributes\n ERROR_TYPE: \"error.type\",\n\n // Values\n MESSAGING_SYSTEM_RABBITMQ: \"rabbitmq\",\n MESSAGING_DESTINATION_KIND_EXCHANGE: \"exchange\",\n MESSAGING_DESTINATION_KIND_QUEUE: \"queue\",\n MESSAGING_OPERATION_PUBLISH: \"publish\",\n MESSAGING_OPERATION_RECEIVE: \"receive\",\n MESSAGING_OPERATION_PROCESS: \"process\",\n} as const;\n\n/**\n * Telemetry provider for AMQP operations.\n * Uses lazy loading to gracefully handle cases where OpenTelemetry is not installed.\n */\nexport type TelemetryProvider = {\n /**\n * Get a tracer instance for creating spans.\n * Returns undefined if OpenTelemetry is not available.\n */\n getTracer: () => Tracer | undefined;\n\n /**\n * Get a counter for messages published.\n * Returns undefined if OpenTelemetry is not available.\n */\n getPublishCounter: () => Counter | undefined;\n\n /**\n * Get a counter for messages consumed.\n * Returns undefined if OpenTelemetry is not available.\n */\n getConsumeCounter: () => Counter | undefined;\n\n /**\n * Get a histogram for publish latency.\n * Returns undefined if OpenTelemetry is not available.\n */\n getPublishLatencyHistogram: () => Histogram | undefined;\n\n /**\n * Get a histogram for consume/process latency.\n * Returns undefined if OpenTelemetry is not available.\n */\n getConsumeLatencyHistogram: () => Histogram | undefined;\n};\n\n/**\n * Instrumentation scope name for amqp-contract.\n */\nconst INSTRUMENTATION_SCOPE_NAME = \"@amqp-contract\";\nconst INSTRUMENTATION_SCOPE_VERSION = \"0.1.0\";\n\n// Cache for OpenTelemetry API module and instruments\nlet otelApi: typeof import(\"@opentelemetry/api\") | null | undefined;\nlet cachedTracer: Tracer | undefined;\nlet cachedPublishCounter: Counter | undefined;\nlet cachedConsumeCounter: Counter | undefined;\nlet cachedPublishLatencyHistogram: Histogram | undefined;\nlet cachedConsumeLatencyHistogram: Histogram | undefined;\n\n/**\n * Try to load the OpenTelemetry API module.\n * Returns null if the module is not available.\n */\nfunction tryLoadOpenTelemetryApi(): typeof import(\"@opentelemetry/api\") | null {\n if (otelApi === undefined) {\n try {\n // Dynamic import using require to avoid bundler issues\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n otelApi = require(\"@opentelemetry/api\") as typeof import(\"@opentelemetry/api\");\n } catch {\n otelApi = null;\n }\n }\n return otelApi;\n}\n\n/**\n * Get or create a tracer instance.\n */\nfunction getTracer(): Tracer | undefined {\n if (cachedTracer !== undefined) {\n return cachedTracer;\n }\n\n const api = tryLoadOpenTelemetryApi();\n if (!api) {\n return undefined;\n }\n\n cachedTracer = api.trace.getTracer(INSTRUMENTATION_SCOPE_NAME, INSTRUMENTATION_SCOPE_VERSION);\n return cachedTracer;\n}\n\n/**\n * Get or create a meter and its instruments.\n */\nfunction getMeterInstruments(): {\n publishCounter: Counter | undefined;\n consumeCounter: Counter | undefined;\n publishLatencyHistogram: Histogram | undefined;\n consumeLatencyHistogram: Histogram | undefined;\n} {\n if (cachedPublishCounter !== undefined) {\n return {\n publishCounter: cachedPublishCounter,\n consumeCounter: cachedConsumeCounter,\n publishLatencyHistogram: cachedPublishLatencyHistogram,\n consumeLatencyHistogram: cachedConsumeLatencyHistogram,\n };\n }\n\n const api = tryLoadOpenTelemetryApi();\n if (!api) {\n return {\n publishCounter: undefined,\n consumeCounter: undefined,\n publishLatencyHistogram: undefined,\n consumeLatencyHistogram: undefined,\n };\n }\n\n const meter = api.metrics.getMeter(INSTRUMENTATION_SCOPE_NAME, INSTRUMENTATION_SCOPE_VERSION);\n\n cachedPublishCounter = meter.createCounter(\"amqp.client.messages.published\", {\n description: \"Number of messages published to AMQP broker\",\n unit: \"{message}\",\n });\n\n cachedConsumeCounter = meter.createCounter(\"amqp.worker.messages.consumed\", {\n description: \"Number of messages consumed from AMQP broker\",\n unit: \"{message}\",\n });\n\n cachedPublishLatencyHistogram = meter.createHistogram(\"amqp.client.publish.duration\", {\n description: \"Duration of message publish operations\",\n unit: \"ms\",\n });\n\n cachedConsumeLatencyHistogram = meter.createHistogram(\"amqp.worker.process.duration\", {\n description: \"Duration of message processing operations\",\n unit: \"ms\",\n });\n\n return {\n publishCounter: cachedPublishCounter,\n consumeCounter: cachedConsumeCounter,\n publishLatencyHistogram: cachedPublishLatencyHistogram,\n consumeLatencyHistogram: cachedConsumeLatencyHistogram,\n };\n}\n\n/**\n * Default telemetry provider that uses OpenTelemetry API if available.\n */\nexport const defaultTelemetryProvider: TelemetryProvider = {\n getTracer,\n getPublishCounter: () => getMeterInstruments().publishCounter,\n getConsumeCounter: () => getMeterInstruments().consumeCounter,\n getPublishLatencyHistogram: () => getMeterInstruments().publishLatencyHistogram,\n getConsumeLatencyHistogram: () => getMeterInstruments().consumeLatencyHistogram,\n};\n\n/**\n * Create a span for a publish operation.\n * Returns undefined if OpenTelemetry is not available.\n */\nexport function startPublishSpan(\n provider: TelemetryProvider,\n exchangeName: string,\n routingKey: string | undefined,\n attributes?: Attributes,\n): Span | undefined {\n const tracer = provider.getTracer();\n if (!tracer) {\n return undefined;\n }\n\n const spanName = `${exchangeName} publish`;\n\n return tracer.startSpan(spanName, {\n kind: SpanKind.PRODUCER,\n attributes: {\n [MessagingSemanticConventions.MESSAGING_SYSTEM]:\n MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,\n [MessagingSemanticConventions.MESSAGING_DESTINATION]: exchangeName,\n [MessagingSemanticConventions.MESSAGING_DESTINATION_KIND]:\n MessagingSemanticConventions.MESSAGING_DESTINATION_KIND_EXCHANGE,\n [MessagingSemanticConventions.MESSAGING_OPERATION]:\n MessagingSemanticConventions.MESSAGING_OPERATION_PUBLISH,\n ...(routingKey\n ? { [MessagingSemanticConventions.MESSAGING_RABBITMQ_ROUTING_KEY]: routingKey }\n : {}),\n ...attributes,\n },\n });\n}\n\n/**\n * Create a span for a consume/process operation.\n * Returns undefined if OpenTelemetry is not available.\n */\nexport function startConsumeSpan(\n provider: TelemetryProvider,\n queueName: string,\n consumerName: string,\n attributes?: Attributes,\n): Span | undefined {\n const tracer = provider.getTracer();\n if (!tracer) {\n return undefined;\n }\n\n const spanName = `${queueName} process`;\n\n return tracer.startSpan(spanName, {\n kind: SpanKind.CONSUMER,\n attributes: {\n [MessagingSemanticConventions.MESSAGING_SYSTEM]:\n MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,\n [MessagingSemanticConventions.MESSAGING_DESTINATION]: queueName,\n [MessagingSemanticConventions.MESSAGING_DESTINATION_KIND]:\n MessagingSemanticConventions.MESSAGING_DESTINATION_KIND_QUEUE,\n [MessagingSemanticConventions.MESSAGING_OPERATION]:\n MessagingSemanticConventions.MESSAGING_OPERATION_PROCESS,\n \"amqp.consumer.name\": consumerName,\n ...attributes,\n },\n });\n}\n\n/**\n * End a span with success status.\n */\nexport function endSpanSuccess(span: Span | undefined): void {\n if (!span) {\n return;\n }\n\n const api = tryLoadOpenTelemetryApi();\n if (api) {\n span.setStatus({ code: api.SpanStatusCode.OK });\n }\n span.end();\n}\n\n/**\n * End a span with error status.\n */\nexport function endSpanError(span: Span | undefined, error: Error): void {\n if (!span) {\n return;\n }\n\n const api = tryLoadOpenTelemetryApi();\n if (api) {\n span.setStatus({ code: api.SpanStatusCode.ERROR, message: error.message });\n span.recordException(error);\n span.setAttribute(MessagingSemanticConventions.ERROR_TYPE, error.name);\n }\n span.end();\n}\n\n/**\n * Record a publish metric.\n */\nexport function recordPublishMetric(\n provider: TelemetryProvider,\n exchangeName: string,\n routingKey: string | undefined,\n success: boolean,\n durationMs: number,\n): void {\n const publishCounter = provider.getPublishCounter();\n const publishLatencyHistogram = provider.getPublishLatencyHistogram();\n\n const attributes: Attributes = {\n [MessagingSemanticConventions.MESSAGING_SYSTEM]:\n MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,\n [MessagingSemanticConventions.MESSAGING_DESTINATION]: exchangeName,\n ...(routingKey\n ? { [MessagingSemanticConventions.MESSAGING_RABBITMQ_ROUTING_KEY]: routingKey }\n : {}),\n success: success,\n };\n\n publishCounter?.add(1, attributes);\n publishLatencyHistogram?.record(durationMs, attributes);\n}\n\n/**\n * Record a consume metric.\n */\nexport function recordConsumeMetric(\n provider: TelemetryProvider,\n queueName: string,\n consumerName: string,\n success: boolean,\n durationMs: number,\n): void {\n const consumeCounter = provider.getConsumeCounter();\n const consumeLatencyHistogram = provider.getConsumeLatencyHistogram();\n\n const attributes: Attributes = {\n [MessagingSemanticConventions.MESSAGING_SYSTEM]:\n MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,\n [MessagingSemanticConventions.MESSAGING_DESTINATION]: queueName,\n \"amqp.consumer.name\": consumerName,\n success: success,\n };\n\n consumeCounter?.add(1, attributes);\n consumeLatencyHistogram?.record(durationMs, attributes);\n}\n\n/**\n * Reset the cached OpenTelemetry API module and instruments.\n * For testing purposes only.\n * @internal\n */\nexport function _resetTelemetryCacheForTesting(): void {\n otelApi = undefined;\n cachedTracer = undefined;\n cachedPublishCounter = undefined;\n cachedConsumeCounter = undefined;\n cachedPublishLatencyHistogram = undefined;\n cachedConsumeLatencyHistogram = undefined;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amqp-contract/core",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Core utilities for AMQP setup and management in amqp-contract",
5
5
  "keywords": [
6
6
  "amqp",
@@ -12,7 +12,10 @@
12
12
  "messaging",
13
13
  "type-safe",
14
14
  "message-broker",
15
- "message-queue"
15
+ "message-queue",
16
+ "opentelemetry",
17
+ "tracing",
18
+ "metrics"
16
19
  ],
17
20
  "homepage": "https://github.com/btravers/amqp-contract#readme",
18
21
  "bugs": {
@@ -48,9 +51,10 @@
48
51
  "dependencies": {
49
52
  "amqp-connection-manager": "5.0.0",
50
53
  "amqplib": "0.10.9",
51
- "@amqp-contract/contract": "0.8.0"
54
+ "@amqp-contract/contract": "0.9.0"
52
55
  },
53
56
  "devDependencies": {
57
+ "@opentelemetry/api": "1.9.0",
54
58
  "@types/amqplib": "0.10.8",
55
59
  "@vitest/coverage-v8": "4.0.16",
56
60
  "tsdown": "0.19.0",
@@ -58,10 +62,18 @@
58
62
  "typescript": "5.9.3",
59
63
  "vitest": "4.0.16",
60
64
  "zod": "4.3.5",
61
- "@amqp-contract/testing": "0.8.0",
65
+ "@amqp-contract/testing": "0.9.0",
62
66
  "@amqp-contract/tsconfig": "0.1.0",
63
67
  "@amqp-contract/typedoc": "0.1.0"
64
68
  },
69
+ "peerDependencies": {
70
+ "@opentelemetry/api": "^1.0.0"
71
+ },
72
+ "peerDependenciesMeta": {
73
+ "@opentelemetry/api": {
74
+ "optional": true
75
+ }
76
+ },
65
77
  "scripts": {
66
78
  "build": "tsdown src/index.ts --format cjs,esm --dts --clean",
67
79
  "build:docs": "typedoc",