@onlineapps/conn-orch-registry 1.1.23 → 1.1.25

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/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  ## 🚀 Features
10
10
 
11
- * Automatic queue management (`workflow`, `<serviceName>.registry`, `api_services_queuer`, `registry_office`)
11
+ * Automatic queue management (`workflow`, `<serviceName>.registry`, `api_services_queuer`, `registry.register`)
12
12
  * Periodic heartbeat messages with metadata
13
13
  * API description request/response flow
14
14
  * Event-driven API using `EventEmitter`
@@ -64,7 +64,7 @@ Configuration can be provided via environment variables or constructor options:
64
64
  | `SERVICE_VERSION` | Service version in SemVer format (required) | — |
65
65
  | `HEARTBEAT_INTERVAL` | Interval in ms between heartbeats | `10000` |
66
66
  | `API_QUEUE` | Queue name for heartbeat/API messages | `api_services_queuer` |
67
- | `REGISTRY_QUEUE` | Queue name for registry requests/descriptions | `registry_office` |
67
+ | `REGISTRY_QUEUE` | Queue name for registry requests/descriptions | `registry.register` |
68
68
 
69
69
  ## 📨 Message Formats
70
70
 
@@ -124,7 +124,7 @@ async function shutdown() {
124
124
  | `specificationEndpoint` | string | '/api/v1/specification' | Endpoint for API spec |
125
125
  | `heartbeatInterval` | number | 10000 | Heartbeat interval in ms |
126
126
  | `apiQueue` | string | 'api_services_queuer' | Queue for API traffic |
127
- | `registryQueue` | string | 'registry_office' | Registry queue name |
127
+ | `registryQueue` | string | 'registry.register' | Registry queue name |
128
128
 
129
129
  ### Methods
130
130
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlineapps/conn-orch-registry",
3
- "version": "1.1.23",
3
+ "version": "1.1.25",
4
4
  "license": "MIT",
5
5
  "description": "Connector-registry-client provides the core communication mechanism for microservices in this environment. It enables them to interact with a services_registry to receive and fulfill tasks by submitting heartbeats or their API descriptions.",
6
6
  "keywords": [
@@ -41,12 +41,44 @@ class QueueManager {
41
41
  /**
42
42
  * Initializes the connection and channel to RabbitMQ.
43
43
  * @returns {Promise<void>}
44
+ * @throws {Error} If connection or channel creation fails or times out
44
45
  */
45
46
  async init() {
46
- this.conn = await amqp.connect(this.amqpUrl);
47
+ const CONNECT_TIMEOUT = 10000; // 10 seconds
48
+ const connectPromise = amqp.connect(this.amqpUrl);
49
+ const connectTimeoutPromise = new Promise((_, reject) => {
50
+ setTimeout(() => {
51
+ reject(new Error(`RabbitMQ connection timeout after ${CONNECT_TIMEOUT}ms. RabbitMQ may be unavailable at ${this.amqpUrl}`));
52
+ }, CONNECT_TIMEOUT);
53
+ });
54
+
55
+ try {
56
+ this.conn = await Promise.race([connectPromise, connectTimeoutPromise]);
57
+ } catch (error) {
58
+ throw new Error(`[QueueManager] Failed to connect to RabbitMQ: ${error.message}`);
59
+ }
60
+
47
61
  // Use regular channel instead of ConfirmChannel to avoid RPC reply queue issues
48
62
  // ConfirmChannel uses RPC pattern which requires reply queues that may not exist
49
- this.channel = await this.conn.createChannel();
63
+ const CHANNEL_TIMEOUT = 5000; // 5 seconds
64
+ const channelPromise = this.conn.createChannel();
65
+ const channelTimeoutPromise = new Promise((_, reject) => {
66
+ setTimeout(() => {
67
+ reject(new Error(`Channel creation timeout after ${CHANNEL_TIMEOUT}ms`));
68
+ }, CHANNEL_TIMEOUT);
69
+ });
70
+
71
+ try {
72
+ this.channel = await Promise.race([channelPromise, channelTimeoutPromise]);
73
+ } catch (error) {
74
+ // Clean up connection on channel creation failure
75
+ try {
76
+ await this.conn.close();
77
+ } catch (closeErr) {
78
+ // Ignore close errors
79
+ }
80
+ throw new Error(`[QueueManager] Failed to create channel: ${error.message}`);
81
+ }
50
82
  }
51
83
 
52
84
  /**
@@ -70,19 +102,20 @@ class QueueManager {
70
102
  // CRITICAL: Use queueConfig.js to get correct parameters (TTL, max-length, etc.)
71
103
  // This prevents 406 PRECONDITION-FAILED errors from TTL mismatches
72
104
  let queueOptions = { durable: true };
105
+ const isInfrastructureQueue = queueConfig?.isInfrastructureQueue
106
+ ? queueConfig.isInfrastructureQueue(q)
107
+ : ['workflow.', 'registry.', 'infrastructure.', 'validation.', 'monitoring.', 'delivery.'].some(prefix => q.startsWith(prefix));
108
+
109
+ if (isInfrastructureQueue) {
110
+ const message = `[QueueManager] [REGISTRY] Refusing to assert infrastructure queue '${q}'. ` +
111
+ 'These queues are owned by infrastructure services and must be created before business services start.';
112
+ console.error(message);
113
+ throw new Error(message);
114
+ }
73
115
 
74
116
  if (queueConfig) {
75
117
  try {
76
- if (queueConfig.isInfrastructureQueue(q)) {
77
- const infraConfig = queueConfig.getInfrastructureQueueConfig(q);
78
- queueOptions = {
79
- durable: infraConfig.durable !== false,
80
- arguments: { ...infraConfig.arguments }
81
- };
82
- console.log(`[QueueManager] [REGISTRY] [QUEUE] Using infrastructure queue config for ${q}:`, JSON.stringify(queueOptions));
83
- } else {
84
- console.warn(`[QueueManager] [REGISTRY] [QUEUE] Queue ${q} is not an infrastructure queue, using default options`);
85
- }
118
+ console.warn(`[QueueManager] [REGISTRY] [QUEUE] Queue ${q} is not an infrastructure queue, using default options`);
86
119
  } catch (configErr) {
87
120
  console.warn(`[QueueManager] [REGISTRY] [QUEUE] Failed to get config for ${q}, using defaults:`, configErr.message);
88
121
  }
@@ -93,13 +126,22 @@ class QueueManager {
93
126
  // Directly assert queue with canonical configuration (idempotent)
94
127
  try {
95
128
  const assertStartTime = Date.now();
96
- await this.channel.assertQueue(q, queueOptions);
129
+ const ASSERT_TIMEOUT = 5000; // 5 seconds
130
+ const assertPromise = this.channel.assertQueue(q, queueOptions);
131
+ const assertTimeoutPromise = new Promise((_, reject) => {
132
+ setTimeout(() => {
133
+ reject(new Error(`assertQueue timeout after ${ASSERT_TIMEOUT}ms`));
134
+ }, ASSERT_TIMEOUT);
135
+ });
136
+
137
+ await Promise.race([assertPromise, assertTimeoutPromise]);
97
138
  const assertEndTime = Date.now();
98
139
  console.log(`[QueueManager] [REGISTRY] [QUEUE] ✓ Queue ${q} asserted (took ${assertEndTime - assertStartTime}ms)`);
99
140
  } catch (assertErr) {
100
- console.error(`[QueueManager] [REGISTRY] [QUEUE] Failed to assert queue ${q}:`, assertErr.message);
101
- console.error(`[QueueManager] [REGISTRY] [QUEUE] Error code: ${assertErr.code}`);
102
- throw assertErr;
141
+ const errorMsg = assertErr.message || String(assertErr);
142
+ console.error(`[QueueManager] [REGISTRY] [QUEUE] Failed to assert queue ${q}: ${errorMsg}`);
143
+ console.error(`[QueueManager] [REGISTRY] [QUEUE] Error code: ${assertErr.code || 'N/A'}`);
144
+ throw new Error(`[QueueManager] Failed to assert queue ${q}: ${errorMsg}`);
103
145
  }
104
146
  }
105
147
  }
@@ -75,42 +75,34 @@ class ServiceRegistryClient extends EventEmitter {
75
75
  console.log(`[RegistryClient] ${this.serviceName}: ⚠️ No validation proof - will use Tier 2 validation`);
76
76
  }
77
77
 
78
- // Create service-specific response queue
79
- this.serviceResponseQueue = `${this.serviceName}.responses`;
80
-
81
- // Create service-specific registry event queue (for certificate delivery)
78
+ // FÁZE 0.5: Vytvoření service-specific front
79
+ const queueCreationStartTime = Date.now();
82
80
  this.serviceRegistryQueue = `${this.serviceName}.registry`;
81
+ console.log(`[FÁZE 0.5] Service Queue Creation - STARTING`);
82
+
83
+ try {
84
+ // Ensure existence of service-specific queue only (infrastructure queues must already exist)
85
+ await this.queueManager.ensureQueues([this.serviceRegistryQueue]);
86
+ console.log(`[FÁZE 0.5] Service Queue Creation - PASSED (${Date.now() - queueCreationStartTime}ms)`);
87
+ } catch (error) {
88
+ console.error(`[FÁZE 0.5] Service Queue Creation - FAILED: ${error.message}`);
89
+ throw error;
90
+ }
83
91
 
84
- // Ensure existence of service-specific queues only (infrastructure queues must already exist)
85
- await this.queueManager.ensureQueues([this.serviceResponseQueue, this.serviceRegistryQueue]);
86
-
92
+ // FÁZE 0.6: Spuštění konzumerů
93
+ const consumerStartTime = Date.now();
94
+ console.log(`[FÁZE 0.6] Consumer Startup - STARTING`);
95
+
87
96
  // CRITICAL: Before consume(), we must assertQueue with correct parameters
88
97
  // amqplib's channel.consume() may internally call assertQueue() WITHOUT parameters
89
98
  // This causes 406 PRECONDITION-FAILED if queue exists with different arguments
90
- console.log(`[RegistryClient] [CONSUMER] About to consume from ${this.serviceResponseQueue}`);
91
- console.log(`[RegistryClient] [CONSUMER] ⚠ WARNING: amqplib's channel.consume() may internally call assertQueue() WITHOUT parameters`);
92
- console.log(`[RegistryClient] [CONSUMER] ⚠ WARNING: Queue should already be asserted by ensureQueues() above with correct parameters from queueConfig.js`);
93
-
94
- // Start consuming service response queue for registry responses
95
- await this.queueManager.channel.consume(
96
- this.serviceResponseQueue,
97
- msg => {
98
- // Handle null message (queue deleted, connection closed, consumer canceled)
99
- if (!msg) {
100
- console.warn(`[RegistryClient] ${this.serviceName}: Received null message from ${this.serviceResponseQueue} (queue may be deleted or connection closed)`);
101
- return;
102
- }
103
- this._handleRegistryMessage(msg);
104
- },
105
- { noAck: false }
106
- );
107
-
108
- // CRITICAL: Also listen on registry event queue for certificate delivery
109
99
  console.log(`[RegistryClient] [CONSUMER] About to consume from ${this.serviceRegistryQueue}`);
110
100
  console.log(`[RegistryClient] [CONSUMER] ⚠ WARNING: amqplib's channel.consume() may internally call assertQueue() WITHOUT parameters`);
111
101
  console.log(`[RegistryClient] [CONSUMER] ⚠ WARNING: Queue should already be asserted by ensureQueues() above with correct parameters from queueConfig.js`);
112
102
 
113
- await this.queueManager.channel.consume(
103
+ // Start consuming service registry queue for registry responses and events
104
+ const CONSUME_TIMEOUT = 5000; // 5 seconds
105
+ const consumeRegistryPromise = this.queueManager.channel.consume(
114
106
  this.serviceRegistryQueue,
115
107
  msg => {
116
108
  // Handle null message (queue deleted, connection closed, consumer canceled)
@@ -118,10 +110,33 @@ class ServiceRegistryClient extends EventEmitter {
118
110
  console.warn(`[RegistryClient] ${this.serviceName}: Received null message from ${this.serviceRegistryQueue} (queue may be deleted or connection closed)`);
119
111
  return;
120
112
  }
121
- this._handleRegistryMessage(msg);
113
+ try {
114
+ this._handleRegistryMessage(msg);
115
+ } catch (error) {
116
+ console.error(`[RegistryClient] ${this.serviceName}: Error in consume callback:`, error);
117
+ // Nack message on error
118
+ try {
119
+ this.queueManager.channel.nack(msg, false, false);
120
+ } catch (nackErr) {
121
+ console.error(`[RegistryClient] ${this.serviceName}: Failed to nack message:`, nackErr);
122
+ }
123
+ }
122
124
  },
123
125
  { noAck: false }
124
126
  );
127
+ const consumeRegistryTimeoutPromise = new Promise((_, reject) => {
128
+ setTimeout(() => {
129
+ reject(new Error(`consume() timeout for ${this.serviceRegistryQueue} after ${CONSUME_TIMEOUT}ms`));
130
+ }, CONSUME_TIMEOUT);
131
+ });
132
+
133
+ try {
134
+ await Promise.race([consumeRegistryPromise, consumeRegistryTimeoutPromise]);
135
+ console.log(`[FÁZE 0.6] Consumer Startup - PASSED (${Date.now() - consumerStartTime}ms)`);
136
+ } catch (consumeErr) {
137
+ console.error(`[FÁZE 0.6] Consumer Startup - FAILED: ${consumeErr.message}`);
138
+ throw new Error(`[RegistryClient] ${this.serviceName}: Failed to start consumer on ${this.serviceRegistryQueue}: ${consumeErr.message}`);
139
+ }
125
140
  }
126
141
 
127
142
  /**
@@ -138,10 +153,16 @@ class ServiceRegistryClient extends EventEmitter {
138
153
  return; // Don't try to parse or ack null message
139
154
  }
140
155
 
156
+ // Log which queue this message came from (if available in msg properties)
157
+ const queueName = msg.fields?.routingKey || msg.fields?.exchange || 'unknown';
158
+ console.log(`[RegistryClient] ${this.serviceName}: [MESSAGE] Processing message from queue: ${queueName}`);
159
+
141
160
  let payload;
142
161
  try {
143
162
  payload = JSON.parse(msg.content.toString());
163
+ console.log(`[RegistryClient] ${this.serviceName}: [MESSAGE] Parsed payload type: ${payload.type}, requestId: ${payload.requestId}`);
144
164
  } catch (err) {
165
+ console.error(`[RegistryClient] ${this.serviceName}: [MESSAGE] Failed to parse message:`, err);
145
166
  this.emit('error', err);
146
167
  // Only nack if message is not null
147
168
  if (msg) {
@@ -165,22 +186,34 @@ class ServiceRegistryClient extends EventEmitter {
165
186
  // Handle registration response from registry
166
187
  // Registry sends 'register.confirmed' after validation
167
188
  if ((payload.type === 'registerResponse' || payload.type === 'register.confirmed') && payload.requestId) {
168
- console.log(`[RegistryClient] ${this.serviceName}: Received ${payload.type} message`, {
189
+ console.log(`[RegistryClient] ${this.serviceName}: [REGISTRATION] Received ${payload.type} message`, {
169
190
  requestId: payload.requestId,
170
191
  hasPendingRegistrations: !!(this.pendingRegistrations),
171
192
  pendingCount: this.pendingRegistrations ? this.pendingRegistrations.size : 0,
172
193
  hasMatchingRequest: !!(this.pendingRegistrations && this.pendingRegistrations.has(payload.requestId)),
173
- pendingKeys: this.pendingRegistrations ? Array.from(this.pendingRegistrations.keys()) : []
194
+ pendingKeys: this.pendingRegistrations ? Array.from(this.pendingRegistrations.keys()) : [],
195
+ queueName
174
196
  });
175
197
 
176
198
  if (this.pendingRegistrations && this.pendingRegistrations.has(payload.requestId)) {
199
+ console.log(`[RegistryClient] ${this.serviceName}: [REGISTRATION] Found matching pending registration, extracting resolve function...`);
177
200
  const { resolve } = this.pendingRegistrations.get(payload.requestId);
178
201
  this.pendingRegistrations.delete(payload.requestId);
179
-
180
- console.log(`[RegistryClient] ${this.serviceName}: ✓ Resolving registration promise for requestId: ${payload.requestId}`);
202
+ console.log(`[RegistryClient] ${this.serviceName}: [REGISTRATION] ✓ Resolve function extracted, pending registration removed`);
203
+
204
+ // CRITICAL: Resolve promise synchronously to ensure it's called before any async operations
205
+ console.log(`[RegistryClient] ${this.serviceName}: [RESOLVE] Starting resolve process for requestId: ${payload.requestId}`);
206
+ console.log(`[RegistryClient] ${this.serviceName}: [RESOLVE] Payload keys:`, Object.keys(payload));
207
+ console.log(`[RegistryClient] ${this.serviceName}: [RESOLVE] Payload.success:`, payload.success);
208
+ console.log(`[RegistryClient] ${this.serviceName}: [RESOLVE] Payload.validated:`, payload.validated);
209
+ console.log(`[RegistryClient] ${this.serviceName}: [RESOLVE] Payload.hasCertificate:`, !!payload.certificate);
210
+ if (payload.certificate) {
211
+ console.log(`[RegistryClient] ${this.serviceName}: [RESOLVE] Payload.certificate.id:`, payload.certificate.id);
212
+ console.log(`[RegistryClient] ${this.serviceName}: [RESOLVE] Payload.certificate.serviceName:`, payload.certificate.serviceName);
213
+ }
181
214
 
182
215
  // Resolve the registration promise with the response
183
- resolve({
216
+ const registrationResult = {
184
217
  success: payload.success || false,
185
218
  message: payload.message || 'Registration processed',
186
219
  serviceName: this.serviceName,
@@ -188,7 +221,31 @@ class ServiceRegistryClient extends EventEmitter {
188
221
  registrationId: payload.registrationId,
189
222
  validated: payload.validated || payload.success, // Consider successful registration as validated
190
223
  certificate: payload.certificate || null // Include certificate from Registry
191
- });
224
+ };
225
+
226
+ console.log(`[RegistryClient] ${this.serviceName}: [RESOLVE] Created registrationResult:`, JSON.stringify({
227
+ success: registrationResult.success,
228
+ validated: registrationResult.validated,
229
+ hasCertificate: !!registrationResult.certificate,
230
+ certificateId: registrationResult.certificate?.id || 'none',
231
+ serviceName: registrationResult.serviceName,
232
+ version: registrationResult.version
233
+ }));
234
+
235
+ console.log(`[RegistryClient] ${this.serviceName}: [RESOLVE] About to call resolve() function...`);
236
+ console.log(`[RegistryClient] ${this.serviceName}: [RESOLVE] resolve function type:`, typeof resolve);
237
+ console.log(`[RegistryClient] ${this.serviceName}: [RESOLVE] resolve function exists:`, !!resolve);
238
+
239
+ try {
240
+ // CRITICAL: Call resolve synchronously - this must happen immediately
241
+ resolve(registrationResult);
242
+ console.log(`[RegistryClient] ${this.serviceName}: [RESOLVE] ✓ resolve() called successfully`);
243
+ console.log(`[RegistryClient] ${this.serviceName}: [RESOLVE] Promise should now be resolved`);
244
+ } catch (resolveError) {
245
+ console.error(`[RegistryClient] ${this.serviceName}: [RESOLVE] ✗ Error calling resolve():`, resolveError);
246
+ console.error(`[RegistryClient] ${this.serviceName}: [RESOLVE] Error stack:`, resolveError.stack);
247
+ throw resolveError;
248
+ }
192
249
  } else {
193
250
  console.warn(`[RegistryClient] ${this.serviceName}: ⚠️ Received ${payload.type} with requestId ${payload.requestId}, but no matching pending registration found`);
194
251
  }
@@ -225,25 +282,42 @@ class ServiceRegistryClient extends EventEmitter {
225
282
  }
226
283
 
227
284
  if (!this.queueManager?.conn) {
228
- console.warn(`[RegistryClient] ${this.serviceName}: MQ connection not ready, cannot verify infrastructure queue ${queueName}`);
229
- return;
285
+ throw new Error(`[RegistryClient] ${this.serviceName}: MQ connection not ready, cannot verify infrastructure queue ${queueName}. Ensure RabbitMQ is accessible.`);
230
286
  }
231
287
 
232
- const verificationChannel = await this.queueManager.conn.createChannel();
288
+ // Add timeout to prevent hanging
289
+ const QUEUE_VERIFY_TIMEOUT = 10000; // 10 seconds
290
+ const verifyPromise = (async () => {
291
+ const verificationChannel = await this.queueManager.conn.createChannel();
292
+ try {
293
+ await verificationChannel.checkQueue(queueName);
294
+ this._verifiedInfraQueues.add(queueName);
295
+ } catch (error) {
296
+ if (error.code === 404) {
297
+ throw new Error(`[RegistryClient] Infrastructure queue '${queueName}' is missing. Ensure the responsible infrastructure service has initialized it.`);
298
+ }
299
+ throw error;
300
+ } finally {
301
+ try {
302
+ await verificationChannel.close();
303
+ } catch (closeErr) {
304
+ console.warn(`[RegistryClient] ${this.serviceName}: Failed to close verification channel for ${queueName}:`, closeErr.message);
305
+ }
306
+ }
307
+ })();
308
+
309
+ const timeoutPromise = new Promise((_, reject) => {
310
+ setTimeout(() => {
311
+ reject(new Error(`[RegistryClient] ${this.serviceName}: Timeout verifying infrastructure queue '${queueName}' after ${QUEUE_VERIFY_TIMEOUT}ms. Queue may not exist or RabbitMQ is unresponsive.`));
312
+ }, QUEUE_VERIFY_TIMEOUT);
313
+ });
314
+
233
315
  try {
234
- await verificationChannel.checkQueue(queueName);
235
- this._verifiedInfraQueues.add(queueName);
316
+ await Promise.race([verifyPromise, timeoutPromise]);
236
317
  } catch (error) {
237
- if (error.code === 404) {
238
- throw new Error(`[RegistryClient] Infrastructure queue '${queueName}' is missing. Ensure the responsible infrastructure service has initialized it.`);
239
- }
318
+ // Clear verification cache on error so we can retry
319
+ this._verifiedInfraQueues.delete(queueName);
240
320
  throw error;
241
- } finally {
242
- try {
243
- await verificationChannel.close();
244
- } catch (closeErr) {
245
- console.warn(`[RegistryClient] ${this.serviceName}: Failed to close verification channel for ${queueName}:`, closeErr.message);
246
- }
247
321
  }
248
322
  }
249
323
 
@@ -282,7 +356,7 @@ class ServiceRegistryClient extends EventEmitter {
282
356
  spec: serviceInfo.spec || null,
283
357
  validationToken: serviceInfo.token || serviceInfo.validationToken,
284
358
  tokenSecret: serviceInfo.secret || serviceInfo.tokenSecret,
285
- responseQueue: this.serviceResponseQueue, // Queue for registry to send response
359
+ responseQueue: this.serviceRegistryQueue, // Queue for registry to send response
286
360
  timestamp: new Date().toISOString()
287
361
  };
288
362
 
@@ -323,21 +397,57 @@ class ServiceRegistryClient extends EventEmitter {
323
397
  // CRITICAL: Use queueConfig.js to get correct parameters (TTL, max-length, etc.)
324
398
  // This prevents 406 PRECONDITION-FAILED errors from TTL mismatches
325
399
  console.log(`[RegistryClient] [PUBLISH] Preparing to publish to ${this.registryQueue}`);
326
- await this._ensureInfrastructureQueue(this.registryQueue);
400
+
401
+ try {
402
+ await this._ensureInfrastructureQueue(this.registryQueue);
403
+ } catch (queueError) {
404
+ const errorMsg = `[RegistryClient] ${this.serviceName}: Cannot register - infrastructure queue verification failed: ${queueError.message}`;
405
+ console.error(errorMsg);
406
+ throw new Error(errorMsg);
407
+ }
408
+
327
409
  console.log(`[RegistryClient] ${this.serviceName}: Sending registration message to queue: ${this.registryQueue}`);
328
410
  console.log(`[RegistryClient] ${this.serviceName}: Message type: ${msg.type}, serviceName: ${msg.serviceName}, version: ${msg.version}`);
329
- this.queueManager.channel.sendToQueue(
330
- this.registryQueue,
331
- Buffer.from(JSON.stringify(msg)),
332
- { persistent: true }
333
- );
334
- console.log(`[RegistryClient] ${this.serviceName}: ✓ Registration message sent, waiting for response...`);
411
+
412
+ // Send message synchronously (sendToQueue is synchronous, returns boolean)
413
+ // But check channel state first to fail fast
414
+ if (!this.queueManager.channel) {
415
+ throw new Error(`[RegistryClient] ${this.serviceName}: Cannot publish - channel not initialized. Ensure MQ connection is established.`);
416
+ }
417
+
418
+ if (this.queueManager.channel.closed) {
419
+ throw new Error(`[RegistryClient] ${this.serviceName}: Cannot publish - channel is closed. MQ connection may be lost.`);
420
+ }
421
+
422
+ try {
423
+ const sent = this.queueManager.channel.sendToQueue(
424
+ this.registryQueue,
425
+ Buffer.from(JSON.stringify(msg)),
426
+ { persistent: true }
427
+ );
428
+ if (!sent) {
429
+ throw new Error(`[RegistryClient] ${this.serviceName}: sendToQueue returned false - queue may be full or channel backpressure active.`);
430
+ }
431
+ console.log(`[RegistryClient] ${this.serviceName}: ✓ Registration message sent, waiting for response...`);
432
+ } catch (sendError) {
433
+ const errorMsg = `[RegistryClient] ${this.serviceName}: Failed to send registration message: ${sendError.message}`;
434
+ console.error(errorMsg);
435
+ throw new Error(errorMsg);
436
+ }
335
437
 
336
438
  this.emit('registerSent', msg);
337
439
 
338
440
  // Wait for registration response from registry
339
441
  try {
442
+ console.log(`[RegistryClient] ${this.serviceName}: [AWAIT] Waiting for registration response promise...`);
340
443
  const response = await responsePromise;
444
+ console.log(`[RegistryClient] ${this.serviceName}: [AWAIT] ✓ Promise resolved, response received:`, {
445
+ hasResponse: !!response,
446
+ responseType: typeof response,
447
+ responseKeys: response ? Object.keys(response) : [],
448
+ hasSuccess: !!response?.success,
449
+ hasCertificate: !!response?.certificate
450
+ });
341
451
  return response;
342
452
  } catch (error) {
343
453
  return {