@ngn-net/nestjs-telescope 0.3.10 → 0.3.11

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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ngn-net/nestjs-telescope",
3
- "version": "0.3.10",
4
- "builtAt": "2026-06-13T11:16:48.902Z"
3
+ "version": "0.3.11",
4
+ "builtAt": "2026-06-13T11:21:01.736Z"
5
5
  }
@@ -5,5 +5,6 @@ export declare class RabbitMQWatcher implements OnModuleInit {
5
5
  private readonly logger;
6
6
  constructor(telescope: TelescopeService);
7
7
  onModuleInit(): void;
8
+ private patchExistingChannels;
8
9
  private patchAmqpConnectionPublish;
9
10
  }
@@ -14,35 +14,33 @@ exports.RabbitMQWatcher = void 0;
14
14
  const common_1 = require("@nestjs/common");
15
15
  const telescope_service_1 = require("../telescope.service");
16
16
  const entry_type_enum_1 = require("../enums/entry-type.enum");
17
- // Patch amqplib.connect at file load time (before any NestJS module initializes)
18
- // This ensures we intercept channels BEFORE @golevelup/nestjs-rabbitmq creates them
19
17
  let telescopeRef = null;
20
- let amqplibPatched = false;
21
- function patchAmqplibEarly() {
22
- if (amqplibPatched)
23
- return;
24
- try {
25
- const amqplib = require('amqplib');
26
- if (!amqplib || typeof amqplib.connect !== 'function')
27
- return;
28
- const originalConnect = amqplib.connect;
29
- amqplib.connect = async function (...args) {
30
- const conn = await originalConnect.apply(this, args);
31
- if (conn && typeof conn.createChannel === 'function') {
32
- const originalCreateChannel = conn.createChannel.bind(conn);
33
- conn.createChannel = async function (...channelArgs) {
34
- const channel = await originalCreateChannel(...channelArgs);
35
- if (channel && typeof channel.consume === 'function') {
36
- instrumentChannel(channel);
37
- }
38
- return channel;
39
- };
40
- }
41
- return conn;
42
- };
43
- amqplibPatched = true;
18
+ function sanitizeMessage(msg) {
19
+ if (!msg)
20
+ return null;
21
+ if (typeof msg === 'string') {
22
+ try {
23
+ return sanitizeMessage(JSON.parse(msg));
24
+ }
25
+ catch {
26
+ return msg.length > 1000 ? msg.substring(0, 1000) + '...[truncated]' : msg;
27
+ }
44
28
  }
45
- catch { }
29
+ if (Buffer.isBuffer(msg))
30
+ return '[Buffer]';
31
+ if (typeof msg === 'object') {
32
+ const sanitized = {};
33
+ for (const [key, value] of Object.entries(msg)) {
34
+ if (/password|secret|token/i.test(key))
35
+ sanitized[key] = '[REDACTED]';
36
+ else if (typeof value === 'string' && value.length > 500)
37
+ sanitized[key] = value.substring(0, 500) + '...[truncated]';
38
+ else
39
+ sanitized[key] = value;
40
+ }
41
+ return sanitized;
42
+ }
43
+ return msg;
46
44
  }
47
45
  function instrumentChannel(channel) {
48
46
  if (channel.__telescopePatched)
@@ -56,7 +54,6 @@ function instrumentChannel(channel) {
56
54
  if (!msg || !msg.content)
57
55
  return onMessage(msg);
58
56
  if (telescopeRef) {
59
- const startTime = Date.now();
60
57
  let parsed;
61
58
  try {
62
59
  parsed = JSON.parse(msg.content.toString());
@@ -82,39 +79,46 @@ function instrumentChannel(channel) {
82
79
  return originalConsume.call(this, queue, wrappedOnMessage, options);
83
80
  };
84
81
  }
85
- function sanitizeMessage(msg) {
86
- if (!msg)
87
- return null;
88
- if (typeof msg === 'string') {
89
- try {
90
- const parsed = JSON.parse(msg);
91
- return sanitizeMessage(parsed);
82
+ // Patch at file load time — intercept AmqpConnection.createChannel
83
+ try {
84
+ const { AmqpConnection } = require('@golevelup/nestjs-rabbitmq');
85
+ if (AmqpConnection && AmqpConnection.prototype) {
86
+ const origCreateChannel = AmqpConnection.prototype.createChannel;
87
+ if (origCreateChannel) {
88
+ AmqpConnection.prototype.createChannel = async function (...args) {
89
+ const channel = await origCreateChannel.apply(this, args);
90
+ if (channel && typeof channel.consume === 'function') {
91
+ instrumentChannel(channel);
92
+ }
93
+ return channel;
94
+ };
92
95
  }
93
- catch {
94
- return msg.length > 1000 ? msg.substring(0, 1000) + '...[truncated]' : msg;
96
+ // Also patch the internal init method that creates channels
97
+ const origInit = AmqpConnection.prototype.init;
98
+ if (origInit) {
99
+ AmqpConnection.prototype.init = async function (...args) {
100
+ const result = await origInit.apply(this, args);
101
+ // After init, patch any existing channel
102
+ if (this.channel && typeof this.channel.consume === 'function') {
103
+ instrumentChannel(this.channel);
104
+ }
105
+ return result;
106
+ };
95
107
  }
96
- }
97
- if (Buffer.isBuffer(msg))
98
- return '[Buffer]';
99
- if (typeof msg === 'object') {
100
- const sanitized = {};
101
- for (const [key, value] of Object.entries(msg)) {
102
- if (key.toLowerCase().includes('password') || key.toLowerCase().includes('secret') || key.toLowerCase().includes('token')) {
103
- sanitized[key] = '[REDACTED]';
104
- }
105
- else if (typeof value === 'string' && value.length > 500) {
106
- sanitized[key] = value.substring(0, 500) + '...[truncated]';
107
- }
108
- else {
109
- sanitized[key] = value;
110
- }
108
+ // Patch the 'managedConnection' setter to catch channel creation
109
+ const origConnect = AmqpConnection.prototype.connect;
110
+ if (origConnect) {
111
+ AmqpConnection.prototype.connect = async function (...args) {
112
+ const result = await origConnect.apply(this, args);
113
+ if (this.channel && typeof this.channel.consume === 'function') {
114
+ instrumentChannel(this.channel);
115
+ }
116
+ return result;
117
+ };
111
118
  }
112
- return sanitized;
113
119
  }
114
- return msg;
115
120
  }
116
- // Execute the patch immediately when this file is imported
117
- patchAmqplibEarly();
121
+ catch { }
118
122
  let RabbitMQWatcher = RabbitMQWatcher_1 = class RabbitMQWatcher {
119
123
  telescope;
120
124
  logger = new common_1.Logger(RabbitMQWatcher_1.name);
@@ -124,8 +128,25 @@ let RabbitMQWatcher = RabbitMQWatcher_1 = class RabbitMQWatcher {
124
128
  onModuleInit() {
125
129
  telescopeRef = this.telescope;
126
130
  this.patchAmqpConnectionPublish();
131
+ this.patchExistingChannels();
127
132
  this.logger.log('RabbitMQWatcher initialized — incoming & outgoing messages will be recorded');
128
133
  }
134
+ patchExistingChannels() {
135
+ try {
136
+ const { AmqpConnection } = require('@golevelup/nestjs-rabbitmq');
137
+ if (!AmqpConnection)
138
+ return;
139
+ // Try to get the instance from the global scope
140
+ // @golevelup/nestjs-rabbitmq stores it as a module provider
141
+ const instances = AmqpConnection.__instances || [];
142
+ for (const inst of instances) {
143
+ if (inst?.channel && typeof inst.channel.consume === 'function') {
144
+ instrumentChannel(inst.channel);
145
+ }
146
+ }
147
+ }
148
+ catch { }
149
+ }
129
150
  patchAmqpConnectionPublish() {
130
151
  try {
131
152
  const { AmqpConnection } = require('@golevelup/nestjs-rabbitmq');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngn-net/nestjs-telescope",
3
- "version": "0.3.10",
3
+ "version": "0.3.11",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },