@furlow/pipes 1.0.1 → 1.0.3

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 +1 @@
1
- {"version":3,"sources":["../../src/mqtt/index.ts"],"sourcesContent":["/**\n * MQTT pipe for IoT and real-time integrations\n */\n\nimport mqtt, { MqttClient, IClientOptions, IClientPublishOptions } from 'mqtt';\nimport type { Pipe, PipeResponse, MqttPipeConfig, MqttQoS } from '../types.js';\n\nexport interface MqttPipeOptions {\n name: string;\n config: MqttPipeConfig;\n}\n\nexport type MqttMessageHandler = (\n topic: string,\n payload: Buffer | string | Record<string, unknown>,\n packet: unknown\n) => void | Promise<void>;\n\nexport class MqttPipe implements Pipe {\n public readonly name: string;\n public readonly type = 'mqtt';\n private client: MqttClient | null = null;\n private config: MqttPipeConfig;\n private connected = false;\n private reconnectAttempts = 0;\n private messageHandlers: Map<string, MqttMessageHandler[]> = new Map();\n private wildcardHandlers: Map<string, MqttMessageHandler[]> = new Map();\n\n constructor(options: MqttPipeOptions) {\n this.name = options.name;\n this.config = options.config;\n }\n\n /**\n * Connect to the MQTT broker\n */\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const protocol = this.config.protocol ?? 'mqtt';\n const port = this.config.port ?? (protocol === 'mqtts' || protocol === 'wss' ? 8883 : 1883);\n const url = `${protocol}://${this.config.broker}:${port}`;\n\n const options: IClientOptions = {\n keepalive: this.config.keepalive ?? 60,\n clean: this.config.clean ?? true,\n reconnectPeriod: this.config.reconnect?.enabled !== false\n ? this.parseDuration(this.config.reconnect?.delay ?? '5s')\n : 0,\n };\n\n // Authentication\n if (this.config.auth) {\n if (this.config.auth.username) options.username = this.config.auth.username;\n if (this.config.auth.password) options.password = this.config.auth.password;\n if (this.config.auth.clientId) options.clientId = this.config.auth.clientId;\n }\n\n // Last Will and Testament\n if (this.config.will) {\n options.will = {\n topic: this.config.will.topic,\n payload: Buffer.from(this.config.will.payload),\n qos: this.config.will.qos ?? 0,\n retain: this.config.will.retain ?? false,\n };\n }\n\n try {\n this.client = mqtt.connect(url, options);\n\n this.client.on('connect', () => {\n this.connected = true;\n this.reconnectAttempts = 0;\n this.emit('connected', {});\n resolve();\n });\n\n this.client.on('message', (topic, payload, packet) => {\n this.handleMessage(topic, payload, packet);\n });\n\n this.client.on('error', (error) => {\n if (!this.connected) {\n reject(error);\n }\n this.emit('error', error);\n });\n\n this.client.on('close', () => {\n this.connected = false;\n this.emit('disconnected', {});\n });\n\n this.client.on('reconnect', () => {\n this.reconnectAttempts++;\n this.emit('reconnecting', { attempts: this.reconnectAttempts });\n });\n\n this.client.on('offline', () => {\n this.connected = false;\n this.emit('offline', {});\n });\n\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Disconnect from the broker\n */\n async disconnect(): Promise<void> {\n return new Promise((resolve) => {\n if (this.client) {\n this.client.end(false, {}, () => {\n this.client = null;\n this.connected = false;\n resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * Check if connected\n */\n isConnected(): boolean {\n return this.connected && this.client?.connected === true;\n }\n\n /**\n * Subscribe to a topic\n */\n async subscribe(\n topic: string,\n options: { qos?: MqttQoS } = {}\n ): Promise<PipeResponse> {\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n return new Promise((resolve) => {\n this.client!.subscribe(topic, { qos: options.qos ?? 0 }, (error, granted) => {\n if (error) {\n resolve({ success: false, error: error.message });\n } else {\n resolve({ success: true, data: granted });\n }\n });\n });\n }\n\n /**\n * Unsubscribe from a topic\n */\n async unsubscribe(topic: string): Promise<PipeResponse> {\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n return new Promise((resolve) => {\n this.client!.unsubscribe(topic, {}, (error) => {\n if (error) {\n resolve({ success: false, error: error.message });\n } else {\n resolve({ success: true });\n }\n });\n });\n }\n\n /**\n * Publish a message to a topic\n */\n async publish(\n topic: string,\n message: string | Buffer | Record<string, unknown>,\n options: { qos?: MqttQoS; retain?: boolean } = {}\n ): Promise<PipeResponse> {\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n const payload = typeof message === 'object' && !Buffer.isBuffer(message)\n ? JSON.stringify(message)\n : message;\n\n const publishOptions: IClientPublishOptions = {\n qos: options.qos ?? 0,\n retain: options.retain ?? false,\n };\n\n return new Promise((resolve) => {\n this.client!.publish(topic, payload, publishOptions, (error) => {\n if (error) {\n resolve({ success: false, error: error.message });\n } else {\n resolve({ success: true });\n }\n });\n });\n }\n\n /**\n * Register a handler for a specific topic\n * Supports MQTT wildcards: + (single level), # (multi level)\n */\n on(topic: string, handler: MqttMessageHandler): void {\n if (topic.includes('+') || topic.includes('#')) {\n // Wildcard topic - store in wildcardHandlers\n const handlers = this.wildcardHandlers.get(topic) ?? [];\n handlers.push(handler);\n this.wildcardHandlers.set(topic, handlers);\n } else {\n // Exact topic\n const handlers = this.messageHandlers.get(topic) ?? [];\n handlers.push(handler);\n this.messageHandlers.set(topic, handlers);\n }\n }\n\n /**\n * Remove a handler\n */\n off(topic: string, handler: MqttMessageHandler): void {\n const map = topic.includes('+') || topic.includes('#')\n ? this.wildcardHandlers\n : this.messageHandlers;\n\n const handlers = map.get(topic) ?? [];\n const index = handlers.indexOf(handler);\n if (index !== -1) {\n handlers.splice(index, 1);\n }\n }\n\n /**\n * Emit to handlers\n */\n private emit(event: string, data: unknown): void {\n const handlers = this.messageHandlers.get(event) ?? [];\n for (const handler of handlers) {\n try {\n handler(event, data as Buffer, {});\n } catch (error) {\n console.error(`MQTT handler error for \"${event}\":`, error);\n }\n }\n }\n\n /**\n * Handle incoming messages\n */\n private handleMessage(topic: string, payload: Buffer, packet: unknown): void {\n // Parse payload\n let parsedPayload: Buffer | string | Record<string, unknown> = payload;\n try {\n const str = payload.toString();\n parsedPayload = JSON.parse(str);\n } catch {\n parsedPayload = payload.toString();\n }\n\n // Call exact topic handlers\n const exactHandlers = this.messageHandlers.get(topic) ?? [];\n for (const handler of exactHandlers) {\n try {\n handler(topic, parsedPayload, packet);\n } catch (error) {\n console.error(`MQTT handler error for topic \"${topic}\":`, error);\n }\n }\n\n // Check wildcard handlers\n for (const [pattern, handlers] of this.wildcardHandlers) {\n if (this.topicMatches(pattern, topic)) {\n for (const handler of handlers) {\n try {\n handler(topic, parsedPayload, packet);\n } catch (error) {\n console.error(`MQTT wildcard handler error for pattern \"${pattern}\":`, error);\n }\n }\n }\n }\n\n // Emit to generic message handlers\n this.emit('message', { topic, payload: parsedPayload, packet });\n }\n\n /**\n * Check if a topic matches a pattern with MQTT wildcards\n */\n private topicMatches(pattern: string, topic: string): boolean {\n const patternParts = pattern.split('/');\n const topicParts = topic.split('/');\n\n for (let i = 0; i < patternParts.length; i++) {\n const patternPart = patternParts[i];\n\n // Multi-level wildcard matches everything remaining\n if (patternPart === '#') {\n return true;\n }\n\n // Topic is shorter than pattern\n if (i >= topicParts.length) {\n return false;\n }\n\n // Single-level wildcard matches any single level\n if (patternPart === '+') {\n continue;\n }\n\n // Exact match required\n if (patternPart !== topicParts[i]) {\n return false;\n }\n }\n\n // Pattern and topic should be same length (unless # was used)\n return patternParts.length === topicParts.length;\n }\n\n /**\n * Parse duration string to milliseconds\n */\n private parseDuration(duration: string): number {\n const match = duration.match(/^(\\d+)(ms|s|m|h)?$/);\n if (!match) return 5000;\n\n const value = parseInt(match[1]!, 10);\n const unit = match[2] ?? 's';\n\n switch (unit) {\n case 'ms':\n return value;\n case 's':\n return value * 1000;\n case 'm':\n return value * 60 * 1000;\n case 'h':\n return value * 60 * 60 * 1000;\n default:\n return value * 1000;\n }\n }\n}\n\n/**\n * Create an MQTT pipe\n */\nexport function createMqttPipe(options: MqttPipeOptions): MqttPipe {\n return new MqttPipe(options);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/mqtt/index.ts"],"sourcesContent":["/**\n * MQTT pipe for IoT and real-time integrations\n */\n\nimport mqtt, { MqttClient, IClientOptions, IClientPublishOptions } from 'mqtt';\nimport type { Pipe, PipeResponse, MqttPipeConfig, MqttQoS } from '../types.js';\n\nexport interface MqttPipeOptions {\n name: string;\n config: MqttPipeConfig;\n}\n\nexport type MqttMessageHandler = (\n topic: string,\n payload: Buffer | string | Record<string, unknown>,\n packet: unknown\n) => void | Promise<void>;\n\nexport class MqttPipe implements Pipe {\n public readonly name: string;\n public readonly type = 'mqtt';\n private client: MqttClient | null = null;\n private config: MqttPipeConfig;\n private connected = false;\n private reconnectAttempts = 0;\n private messageHandlers: Map<string, MqttMessageHandler[]> = new Map();\n private wildcardHandlers: Map<string, MqttMessageHandler[]> = new Map();\n\n constructor(options: MqttPipeOptions) {\n this.name = options.name;\n this.config = options.config;\n }\n\n /**\n * Connect to the MQTT broker\n */\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const protocol = this.config.protocol ?? 'mqtt';\n const port = this.config.port ?? (protocol === 'mqtts' || protocol === 'wss' ? 8883 : 1883);\n const url = `${protocol}://${this.config.broker}:${port}`;\n\n const options: IClientOptions = {\n keepalive: this.config.keepalive ?? 60,\n clean: this.config.clean ?? true,\n reconnectPeriod: this.config.reconnect?.enabled !== false\n ? this.parseDuration(this.config.reconnect?.delay ?? '5s')\n : 0,\n };\n\n // Authentication\n if (this.config.auth) {\n if (this.config.auth.username) options.username = this.config.auth.username;\n if (this.config.auth.password) options.password = this.config.auth.password;\n if (this.config.auth.clientId) options.clientId = this.config.auth.clientId;\n }\n\n // Last Will and Testament\n if (this.config.will) {\n options.will = {\n topic: this.config.will.topic,\n payload: Buffer.from(this.config.will.payload),\n qos: this.config.will.qos ?? 0,\n retain: this.config.will.retain ?? false,\n };\n }\n\n try {\n this.client = mqtt.connect(url, options);\n\n this.client.on('connect', () => {\n this.connected = true;\n this.reconnectAttempts = 0;\n this.emit('connected', {});\n resolve();\n });\n\n this.client.on('message', (topic, payload, packet) => {\n this.handleMessage(topic, payload, packet);\n });\n\n this.client.on('error', (error) => {\n if (!this.connected) {\n reject(error);\n }\n this.emit('error', error);\n });\n\n this.client.on('close', () => {\n this.connected = false;\n this.emit('disconnected', {});\n });\n\n this.client.on('reconnect', () => {\n this.reconnectAttempts++;\n this.emit('reconnecting', { attempts: this.reconnectAttempts });\n });\n\n this.client.on('offline', () => {\n this.connected = false;\n this.emit('offline', {});\n });\n\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Disconnect from the broker\n */\n async disconnect(): Promise<void> {\n return new Promise((resolve) => {\n if (this.client) {\n this.client.end(false, {}, () => {\n this.client = null;\n this.connected = false;\n resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * Check if connected\n */\n isConnected(): boolean {\n return this.connected && this.client?.connected === true;\n }\n\n /**\n * Subscribe to a topic\n */\n async subscribe(\n topic: string,\n options: { qos?: MqttQoS } = {}\n ): Promise<PipeResponse> {\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n return new Promise((resolve) => {\n this.client!.subscribe(topic, { qos: options.qos ?? 0 }, (error, granted) => {\n if (error) {\n resolve({ success: false, error: error.message });\n } else {\n resolve({ success: true, data: granted });\n }\n });\n });\n }\n\n /**\n * Unsubscribe from a topic\n */\n async unsubscribe(topic: string): Promise<PipeResponse> {\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n return new Promise((resolve) => {\n this.client!.unsubscribe(topic, {}, (error) => {\n if (error) {\n resolve({ success: false, error: error.message });\n } else {\n resolve({ success: true });\n }\n });\n });\n }\n\n /**\n * Publish a message to a topic\n */\n async publish(\n topic: string,\n message: string | Buffer | Record<string, unknown>,\n options: { qos?: MqttQoS; retain?: boolean } = {}\n ): Promise<PipeResponse> {\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n const payload = typeof message === 'object' && !Buffer.isBuffer(message)\n ? JSON.stringify(message)\n : message;\n\n const publishOptions: IClientPublishOptions = {\n qos: options.qos ?? 0,\n retain: options.retain ?? false,\n };\n\n return new Promise((resolve) => {\n this.client!.publish(topic, payload, publishOptions, (error) => {\n if (error) {\n resolve({ success: false, error: error.message });\n } else {\n resolve({ success: true });\n }\n });\n });\n }\n\n /**\n * Register a handler for a specific topic\n * Supports MQTT wildcards: + (single level), # (multi level)\n */\n on(topic: string, handler: MqttMessageHandler): void {\n if (topic.includes('+') || topic.includes('#')) {\n // Wildcard topic - store in wildcardHandlers\n const handlers = this.wildcardHandlers.get(topic) ?? [];\n handlers.push(handler);\n this.wildcardHandlers.set(topic, handlers);\n } else {\n // Exact topic\n const handlers = this.messageHandlers.get(topic) ?? [];\n handlers.push(handler);\n this.messageHandlers.set(topic, handlers);\n }\n }\n\n /**\n * Remove a handler\n */\n off(topic: string, handler: MqttMessageHandler): void {\n const map = topic.includes('+') || topic.includes('#')\n ? this.wildcardHandlers\n : this.messageHandlers;\n\n const handlers = map.get(topic) ?? [];\n const index = handlers.indexOf(handler);\n if (index !== -1) {\n handlers.splice(index, 1);\n }\n }\n\n /**\n * Emit to handlers\n */\n private emit(event: string, data: unknown): void {\n const handlers = this.messageHandlers.get(event) ?? [];\n for (const handler of handlers) {\n try {\n const result = handler(event, data as Buffer, {});\n // Handle async handlers properly\n if (result instanceof Promise) {\n result.catch((error) => {\n console.error(`MQTT async handler error for \"${event}\":`, error);\n });\n }\n } catch (error) {\n console.error(`MQTT handler error for \"${event}\":`, error);\n }\n }\n }\n\n /**\n * Handle incoming messages\n */\n private handleMessage(topic: string, payload: Buffer, packet: unknown): void {\n // Parse payload\n let parsedPayload: Buffer | string | Record<string, unknown> = payload;\n try {\n const str = payload.toString();\n parsedPayload = JSON.parse(str);\n } catch {\n parsedPayload = payload.toString();\n }\n\n // Call exact topic handlers\n const exactHandlers = this.messageHandlers.get(topic) ?? [];\n for (const handler of exactHandlers) {\n try {\n const result = handler(topic, parsedPayload, packet);\n // Handle async handlers properly\n if (result instanceof Promise) {\n result.catch((error) => {\n console.error(`MQTT async handler error for topic \"${topic}\":`, error);\n });\n }\n } catch (error) {\n console.error(`MQTT handler error for topic \"${topic}\":`, error);\n }\n }\n\n // Check wildcard handlers\n for (const [pattern, handlers] of this.wildcardHandlers) {\n if (this.topicMatches(pattern, topic)) {\n for (const handler of handlers) {\n try {\n const result = handler(topic, parsedPayload, packet);\n // Handle async handlers properly\n if (result instanceof Promise) {\n result.catch((error) => {\n console.error(`MQTT async wildcard handler error for pattern \"${pattern}\":`, error);\n });\n }\n } catch (error) {\n console.error(`MQTT wildcard handler error for pattern \"${pattern}\":`, error);\n }\n }\n }\n }\n\n // Emit to generic message handlers\n this.emit('message', { topic, payload: parsedPayload, packet });\n }\n\n /**\n * Check if a topic matches a pattern with MQTT wildcards\n */\n private topicMatches(pattern: string, topic: string): boolean {\n const patternParts = pattern.split('/');\n const topicParts = topic.split('/');\n\n for (let i = 0; i < patternParts.length; i++) {\n const patternPart = patternParts[i];\n\n // Multi-level wildcard matches everything remaining\n if (patternPart === '#') {\n return true;\n }\n\n // Topic is shorter than pattern\n if (i >= topicParts.length) {\n return false;\n }\n\n // Single-level wildcard matches any single level\n if (patternPart === '+') {\n continue;\n }\n\n // Exact match required\n if (patternPart !== topicParts[i]) {\n return false;\n }\n }\n\n // Pattern and topic should be same length (unless # was used)\n return patternParts.length === topicParts.length;\n }\n\n /**\n * Parse duration string to milliseconds\n */\n private parseDuration(duration: string): number {\n const match = duration.match(/^(\\d+)(ms|s|m|h)?$/);\n if (!match) return 5000;\n\n const value = parseInt(match[1]!, 10);\n const unit = match[2] ?? 's';\n\n switch (unit) {\n case 'ms':\n return value;\n case 's':\n return value * 1000;\n case 'm':\n return value * 60 * 1000;\n case 'h':\n return value * 60 * 60 * 1000;\n default:\n return value * 1000;\n }\n }\n}\n\n/**\n * Create an MQTT pipe\n */\nexport function createMqttPipe(options: MqttPipeOptions): MqttPipe {\n return new MqttPipe(options);\n}\n"],"mappings":"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","names":[]}
@@ -97,6 +97,7 @@ declare class TcpPipe implements Pipe {
97
97
  private connected;
98
98
  private reconnectAttempts;
99
99
  private reconnecting;
100
+ private reconnectTimer;
100
101
  private dataHandlers;
101
102
  private eventHandlers;
102
103
  constructor(options: TcpPipeOptions);
package/dist/tcp/index.js CHANGED
@@ -164,7 +164,12 @@ var UdpPipe = class {
164
164
  const handlers = this.eventHandlers.get(event) ?? [];
165
165
  for (const handler of handlers) {
166
166
  try {
167
- handler(data);
167
+ const result = handler(data);
168
+ if (result instanceof Promise) {
169
+ result.catch((error) => {
170
+ console.error(`UDP async handler error for "${event}":`, error);
171
+ });
172
+ }
168
173
  } catch (error) {
169
174
  console.error(`UDP handler error for "${event}":`, error);
170
175
  }
@@ -177,7 +182,12 @@ var UdpPipe = class {
177
182
  const msg = { data, rinfo };
178
183
  for (const handler of this.messageHandlers) {
179
184
  try {
180
- handler(msg);
185
+ const result = handler(msg);
186
+ if (result instanceof Promise) {
187
+ result.catch((error) => {
188
+ console.error("UDP async message handler error:", error);
189
+ });
190
+ }
181
191
  } catch (error) {
182
192
  console.error("UDP message handler error:", error);
183
193
  }
@@ -199,6 +209,7 @@ var TcpPipe = class {
199
209
  connected = false;
200
210
  reconnectAttempts = 0;
201
211
  reconnecting = false;
212
+ reconnectTimer = null;
202
213
  dataHandlers = [];
203
214
  eventHandlers = /* @__PURE__ */ new Map();
204
215
  constructor(options) {
@@ -280,6 +291,10 @@ var TcpPipe = class {
280
291
  */
281
292
  async disconnect() {
282
293
  this.reconnecting = false;
294
+ if (this.reconnectTimer) {
295
+ clearTimeout(this.reconnectTimer);
296
+ this.reconnectTimer = null;
297
+ }
283
298
  return new Promise((resolve) => {
284
299
  if (this.socket) {
285
300
  this.socket.end(() => {
@@ -331,13 +346,21 @@ var TcpPipe = class {
331
346
  return { success: false, error: "Not connected" };
332
347
  }
333
348
  return new Promise((resolve) => {
334
- const timer = setTimeout(() => {
349
+ let resolved = false;
350
+ const cleanup = () => {
335
351
  this.offData(handler);
352
+ };
353
+ const timer = setTimeout(() => {
354
+ if (resolved) return;
355
+ resolved = true;
356
+ cleanup();
336
357
  resolve({ success: false, error: "Request timeout" });
337
358
  }, timeout);
338
359
  const handler = (response) => {
360
+ if (resolved) return;
361
+ resolved = true;
339
362
  clearTimeout(timer);
340
- this.offData(handler);
363
+ cleanup();
341
364
  resolve({ success: true, data: response });
342
365
  };
343
366
  this.onData(handler);
@@ -384,7 +407,12 @@ var TcpPipe = class {
384
407
  const handlers = this.eventHandlers.get(event) ?? [];
385
408
  for (const handler of handlers) {
386
409
  try {
387
- handler(data);
410
+ const result = handler(data);
411
+ if (result instanceof Promise) {
412
+ result.catch((error) => {
413
+ console.error(`TCP async handler error for "${event}":`, error);
414
+ });
415
+ }
388
416
  } catch (error) {
389
417
  console.error(`TCP handler error for "${event}":`, error);
390
418
  }
@@ -396,7 +424,12 @@ var TcpPipe = class {
396
424
  handleData(data) {
397
425
  for (const handler of this.dataHandlers) {
398
426
  try {
399
- handler(data);
427
+ const result = handler(data);
428
+ if (result instanceof Promise) {
429
+ result.catch((error) => {
430
+ console.error("TCP async data handler error:", error);
431
+ });
432
+ }
400
433
  } catch (error) {
401
434
  console.error("TCP data handler error:", error);
402
435
  }
@@ -418,14 +451,14 @@ var TcpPipe = class {
418
451
  }
419
452
  this.reconnecting = true;
420
453
  this.reconnectAttempts++;
421
- setTimeout(async () => {
422
- try {
423
- await this.connect();
454
+ this.reconnectTimer = setTimeout(() => {
455
+ this.reconnectTimer = null;
456
+ this.connect().then(() => {
424
457
  this.emit("reconnected", { attempts: this.reconnectAttempts });
425
- } catch {
458
+ }).catch(() => {
426
459
  this.reconnecting = false;
427
460
  this.handleDisconnect();
428
- }
461
+ });
429
462
  }, delay);
430
463
  }
431
464
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/tcp/index.ts","../../src/tcp/udp.ts"],"sourcesContent":["/**\n * TCP pipe for raw socket communication\n */\n\nimport { Socket, createConnection, createServer, Server } from 'net';\nimport type { Pipe, PipeResponse, TcpPipeConfig } from '../types.js';\n\nexport interface TcpPipeOptions {\n name: string;\n config: TcpPipeConfig;\n}\n\nexport type TcpDataHandler = (data: Buffer | string) => void | Promise<void>;\nexport type TcpEventHandler = (data?: unknown) => void | Promise<void>;\n\nexport class TcpPipe implements Pipe {\n public readonly name: string;\n public readonly type = 'tcp';\n private socket: Socket | null = null;\n private server: Server | null = null;\n private config: TcpPipeConfig;\n private connected = false;\n private reconnectAttempts = 0;\n private reconnecting = false;\n private dataHandlers: TcpDataHandler[] = [];\n private eventHandlers: Map<string, TcpEventHandler[]> = new Map();\n\n constructor(options: TcpPipeOptions) {\n this.name = options.name;\n this.config = options.config;\n }\n\n /**\n * Connect to a TCP server (client mode)\n */\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.socket = createConnection({\n host: this.config.host,\n port: this.config.port,\n });\n\n this.socket.setEncoding(this.config.encoding ?? 'utf8');\n\n this.socket.on('connect', () => {\n this.connected = true;\n this.reconnectAttempts = 0;\n this.reconnecting = false;\n this.emit('connected');\n resolve();\n });\n\n this.socket.on('data', (data) => {\n this.handleData(data);\n });\n\n this.socket.on('close', () => {\n this.connected = false;\n this.emit('disconnected');\n this.handleDisconnect();\n });\n\n this.socket.on('error', (error) => {\n if (!this.connected) {\n reject(error);\n }\n this.emit('error', error);\n });\n\n this.socket.on('end', () => {\n this.connected = false;\n this.emit('end');\n });\n\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Start a TCP server (server mode)\n */\n async listen(port?: number): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.server = createServer((socket) => {\n socket.setEncoding(this.config.encoding ?? 'utf8');\n\n socket.on('data', (data) => {\n this.handleData(data);\n });\n\n socket.on('error', (error) => {\n this.emit('client_error', error);\n });\n\n this.emit('connection', socket);\n });\n\n this.server.on('error', (error) => {\n reject(error);\n });\n\n this.server.listen(port ?? this.config.port, () => {\n this.connected = true;\n this.emit('listening');\n resolve();\n });\n\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Disconnect / close\n */\n async disconnect(): Promise<void> {\n this.reconnecting = false;\n\n return new Promise((resolve) => {\n if (this.socket) {\n this.socket.end(() => {\n this.socket?.destroy();\n this.socket = null;\n this.connected = false;\n resolve();\n });\n } else if (this.server) {\n this.server.close(() => {\n this.server = null;\n this.connected = false;\n resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * Check if connected\n */\n isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Send data\n */\n async send(data: Buffer | string): Promise<PipeResponse> {\n if (!this.socket || !this.connected) {\n return { success: false, error: 'Not connected' };\n }\n\n return new Promise((resolve) => {\n this.socket!.write(data, (error) => {\n if (error) {\n resolve({ success: false, error: error.message });\n } else {\n resolve({ success: true });\n }\n });\n });\n }\n\n /**\n * Send and wait for response (request-response pattern)\n */\n async request<T = string>(\n data: Buffer | string,\n options: { timeout?: number } = {}\n ): Promise<PipeResponse<T>> {\n const { timeout = 30000 } = options;\n\n if (!this.socket || !this.connected) {\n return { success: false, error: 'Not connected' };\n }\n\n return new Promise((resolve) => {\n const timer = setTimeout(() => {\n this.offData(handler);\n resolve({ success: false, error: 'Request timeout' });\n }, timeout);\n\n const handler = (response: Buffer | string) => {\n clearTimeout(timer);\n this.offData(handler);\n resolve({ success: true, data: response as T });\n };\n\n this.onData(handler);\n this.send(data);\n });\n }\n\n /**\n * Register a data handler\n */\n onData(handler: TcpDataHandler): void {\n this.dataHandlers.push(handler);\n }\n\n /**\n * Remove a data handler\n */\n offData(handler: TcpDataHandler): void {\n const index = this.dataHandlers.indexOf(handler);\n if (index !== -1) {\n this.dataHandlers.splice(index, 1);\n }\n }\n\n /**\n * Register an event handler\n */\n on(event: string, handler: TcpEventHandler): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n handlers.push(handler);\n this.eventHandlers.set(event, handlers);\n }\n\n /**\n * Remove an event handler\n */\n off(event: string, handler: TcpEventHandler): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n const index = handlers.indexOf(handler);\n if (index !== -1) {\n handlers.splice(index, 1);\n }\n }\n\n /**\n * Emit an event\n */\n private emit(event: string, data?: unknown): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n for (const handler of handlers) {\n try {\n handler(data);\n } catch (error) {\n console.error(`TCP handler error for \"${event}\":`, error);\n }\n }\n }\n\n /**\n * Handle incoming data\n */\n private handleData(data: Buffer | string): void {\n for (const handler of this.dataHandlers) {\n try {\n handler(data);\n } catch (error) {\n console.error('TCP data handler error:', error);\n }\n }\n this.emit('data', data);\n }\n\n /**\n * Handle disconnection with reconnect logic\n */\n private handleDisconnect(): void {\n if (!this.config.reconnect?.enabled || this.reconnecting) {\n return;\n }\n\n const maxAttempts = this.config.reconnect.max_attempts ?? 10;\n const delay = this.parseDuration(this.config.reconnect.delay ?? '5s');\n\n if (this.reconnectAttempts >= maxAttempts) {\n this.emit('reconnect_failed', { attempts: this.reconnectAttempts });\n return;\n }\n\n this.reconnecting = true;\n this.reconnectAttempts++;\n\n setTimeout(async () => {\n try {\n await this.connect();\n this.emit('reconnected', { attempts: this.reconnectAttempts });\n } catch {\n this.reconnecting = false;\n this.handleDisconnect();\n }\n }, delay);\n }\n\n /**\n * Parse duration string to milliseconds\n */\n private parseDuration(duration: string): number {\n const match = duration.match(/^(\\d+)(ms|s|m|h)?$/);\n if (!match) return 5000;\n\n const value = parseInt(match[1]!, 10);\n const unit = match[2] ?? 's';\n\n switch (unit) {\n case 'ms':\n return value;\n case 's':\n return value * 1000;\n case 'm':\n return value * 60 * 1000;\n case 'h':\n return value * 60 * 60 * 1000;\n default:\n return value * 1000;\n }\n }\n}\n\n/**\n * Create a TCP pipe\n */\nexport function createTcpPipe(options: TcpPipeOptions): TcpPipe {\n return new TcpPipe(options);\n}\n\n// Re-export UDP pipe\nexport * from './udp.js';\n","/**\n * UDP pipe for datagram communication\n */\n\nimport { createSocket, Socket, RemoteInfo } from 'dgram';\nimport type { Pipe, PipeResponse, UdpPipeConfig } from '../types.js';\n\nexport interface UdpPipeOptions {\n name: string;\n config: UdpPipeConfig;\n}\n\nexport interface UdpMessage {\n data: Buffer;\n rinfo: RemoteInfo;\n}\n\nexport type UdpMessageHandler = (msg: UdpMessage) => void | Promise<void>;\nexport type UdpEventHandler = (data?: unknown) => void | Promise<void>;\n\nexport class UdpPipe implements Pipe {\n public readonly name: string;\n public readonly type = 'udp';\n private socket: Socket | null = null;\n private config: UdpPipeConfig;\n private bound = false;\n private messageHandlers: UdpMessageHandler[] = [];\n private eventHandlers: Map<string, UdpEventHandler[]> = new Map();\n\n constructor(options: UdpPipeOptions) {\n this.name = options.name;\n this.config = options.config;\n }\n\n /**\n * Bind to a port to receive messages\n */\n async bind(port?: number): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.socket = createSocket('udp4');\n\n this.socket.on('message', (msg, rinfo) => {\n this.handleMessage(msg, rinfo);\n });\n\n this.socket.on('error', (error) => {\n if (!this.bound) {\n reject(error);\n }\n this.emit('error', error);\n });\n\n this.socket.on('listening', () => {\n this.bound = true;\n const address = this.socket!.address();\n this.emit('listening', address);\n resolve();\n });\n\n // Setup broadcast if configured\n if (this.config.broadcast) {\n this.socket.setBroadcast(true);\n }\n\n // Bind to port\n const bindPort = port ?? this.config.port;\n const bindHost = this.config.host ?? '0.0.0.0';\n this.socket.bind(bindPort, bindHost, () => {\n // Join multicast group if configured\n if (this.config.multicast) {\n this.socket!.addMembership(this.config.multicast);\n }\n });\n\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Close the socket\n */\n async disconnect(): Promise<void> {\n return new Promise((resolve) => {\n if (this.socket) {\n this.socket.close(() => {\n this.socket = null;\n this.bound = false;\n resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * Check if bound\n */\n isConnected(): boolean {\n return this.bound;\n }\n\n /**\n * Send data to a specific host and port\n */\n async send(\n data: Buffer | string,\n host: string,\n port: number\n ): Promise<PipeResponse> {\n if (!this.socket) {\n // Create socket if not exists (for send-only mode)\n this.socket = createSocket('udp4');\n if (this.config.broadcast) {\n this.socket.setBroadcast(true);\n }\n }\n\n const buffer = typeof data === 'string' ? Buffer.from(data) : data;\n\n return new Promise((resolve) => {\n this.socket!.send(buffer, port, host, (error) => {\n if (error) {\n resolve({ success: false, error: error.message });\n } else {\n resolve({ success: true });\n }\n });\n });\n }\n\n /**\n * Broadcast data to all hosts on the network\n */\n async broadcast(\n data: Buffer | string,\n port: number,\n address = '255.255.255.255'\n ): Promise<PipeResponse> {\n if (!this.socket) {\n this.socket = createSocket('udp4');\n this.socket.setBroadcast(true);\n }\n\n const buffer = typeof data === 'string' ? Buffer.from(data) : data;\n\n return new Promise((resolve) => {\n this.socket!.send(buffer, port, address, (error) => {\n if (error) {\n resolve({ success: false, error: error.message });\n } else {\n resolve({ success: true });\n }\n });\n });\n }\n\n /**\n * Send to multicast group\n */\n async multicast(\n data: Buffer | string,\n port: number,\n group?: string\n ): Promise<PipeResponse> {\n const multicastGroup = group ?? this.config.multicast;\n if (!multicastGroup) {\n return { success: false, error: 'No multicast group configured' };\n }\n\n return this.send(data, multicastGroup, port);\n }\n\n /**\n * Register a message handler\n */\n onMessage(handler: UdpMessageHandler): void {\n this.messageHandlers.push(handler);\n }\n\n /**\n * Remove a message handler\n */\n offMessage(handler: UdpMessageHandler): void {\n const index = this.messageHandlers.indexOf(handler);\n if (index !== -1) {\n this.messageHandlers.splice(index, 1);\n }\n }\n\n /**\n * Register an event handler\n */\n on(event: string, handler: UdpEventHandler): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n handlers.push(handler);\n this.eventHandlers.set(event, handlers);\n }\n\n /**\n * Remove an event handler\n */\n off(event: string, handler: UdpEventHandler): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n const index = handlers.indexOf(handler);\n if (index !== -1) {\n handlers.splice(index, 1);\n }\n }\n\n /**\n * Emit an event\n */\n private emit(event: string, data?: unknown): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n for (const handler of handlers) {\n try {\n handler(data);\n } catch (error) {\n console.error(`UDP handler error for \"${event}\":`, error);\n }\n }\n }\n\n /**\n * Handle incoming messages\n */\n private handleMessage(data: Buffer, rinfo: RemoteInfo): void {\n const msg: UdpMessage = { data, rinfo };\n\n for (const handler of this.messageHandlers) {\n try {\n handler(msg);\n } catch (error) {\n console.error('UDP message handler error:', error);\n }\n }\n\n this.emit('message', msg);\n }\n}\n\n/**\n * Create a UDP pipe\n */\nexport function createUdpPipe(options: UdpPipeOptions): UdpPipe {\n return new UdpPipe(options);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/tcp/index.ts","../../src/tcp/udp.ts"],"sourcesContent":["/**\n * TCP pipe for raw socket communication\n */\n\nimport { Socket, createConnection, createServer, Server } from 'net';\nimport type { Pipe, PipeResponse, TcpPipeConfig } from '../types.js';\n\nexport interface TcpPipeOptions {\n name: string;\n config: TcpPipeConfig;\n}\n\nexport type TcpDataHandler = (data: Buffer | string) => void | Promise<void>;\nexport type TcpEventHandler = (data?: unknown) => void | Promise<void>;\n\nexport class TcpPipe implements Pipe {\n public readonly name: string;\n public readonly type = 'tcp';\n private socket: Socket | null = null;\n private server: Server | null = null;\n private config: TcpPipeConfig;\n private connected = false;\n private reconnectAttempts = 0;\n private reconnecting = false;\n private reconnectTimer: NodeJS.Timeout | null = null;\n private dataHandlers: TcpDataHandler[] = [];\n private eventHandlers: Map<string, TcpEventHandler[]> = new Map();\n\n constructor(options: TcpPipeOptions) {\n this.name = options.name;\n this.config = options.config;\n }\n\n /**\n * Connect to a TCP server (client mode)\n */\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.socket = createConnection({\n host: this.config.host,\n port: this.config.port,\n });\n\n this.socket.setEncoding(this.config.encoding ?? 'utf8');\n\n this.socket.on('connect', () => {\n this.connected = true;\n this.reconnectAttempts = 0;\n this.reconnecting = false;\n this.emit('connected');\n resolve();\n });\n\n this.socket.on('data', (data) => {\n this.handleData(data);\n });\n\n this.socket.on('close', () => {\n this.connected = false;\n this.emit('disconnected');\n this.handleDisconnect();\n });\n\n this.socket.on('error', (error) => {\n if (!this.connected) {\n reject(error);\n }\n this.emit('error', error);\n });\n\n this.socket.on('end', () => {\n this.connected = false;\n this.emit('end');\n });\n\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Start a TCP server (server mode)\n */\n async listen(port?: number): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.server = createServer((socket) => {\n socket.setEncoding(this.config.encoding ?? 'utf8');\n\n socket.on('data', (data) => {\n this.handleData(data);\n });\n\n socket.on('error', (error) => {\n this.emit('client_error', error);\n });\n\n this.emit('connection', socket);\n });\n\n this.server.on('error', (error) => {\n reject(error);\n });\n\n this.server.listen(port ?? this.config.port, () => {\n this.connected = true;\n this.emit('listening');\n resolve();\n });\n\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Disconnect / close\n */\n async disconnect(): Promise<void> {\n this.reconnecting = false;\n\n // Clear any pending reconnect timer to prevent memory leaks\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n return new Promise((resolve) => {\n if (this.socket) {\n this.socket.end(() => {\n this.socket?.destroy();\n this.socket = null;\n this.connected = false;\n resolve();\n });\n } else if (this.server) {\n this.server.close(() => {\n this.server = null;\n this.connected = false;\n resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * Check if connected\n */\n isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Send data\n */\n async send(data: Buffer | string): Promise<PipeResponse> {\n if (!this.socket || !this.connected) {\n return { success: false, error: 'Not connected' };\n }\n\n return new Promise((resolve) => {\n this.socket!.write(data, (error) => {\n if (error) {\n resolve({ success: false, error: error.message });\n } else {\n resolve({ success: true });\n }\n });\n });\n }\n\n /**\n * Send and wait for response (request-response pattern)\n */\n async request<T = string>(\n data: Buffer | string,\n options: { timeout?: number } = {}\n ): Promise<PipeResponse<T>> {\n const { timeout = 30000 } = options;\n\n if (!this.socket || !this.connected) {\n return { success: false, error: 'Not connected' };\n }\n\n return new Promise((resolve) => {\n let resolved = false;\n\n const cleanup = () => {\n this.offData(handler);\n };\n\n const timer = setTimeout(() => {\n if (resolved) return;\n resolved = true;\n cleanup();\n resolve({ success: false, error: 'Request timeout' });\n }, timeout);\n\n const handler = (response: Buffer | string) => {\n if (resolved) return;\n resolved = true;\n clearTimeout(timer);\n cleanup();\n resolve({ success: true, data: response as T });\n };\n\n this.onData(handler);\n this.send(data);\n });\n }\n\n /**\n * Register a data handler\n */\n onData(handler: TcpDataHandler): void {\n this.dataHandlers.push(handler);\n }\n\n /**\n * Remove a data handler\n */\n offData(handler: TcpDataHandler): void {\n const index = this.dataHandlers.indexOf(handler);\n if (index !== -1) {\n this.dataHandlers.splice(index, 1);\n }\n }\n\n /**\n * Register an event handler\n */\n on(event: string, handler: TcpEventHandler): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n handlers.push(handler);\n this.eventHandlers.set(event, handlers);\n }\n\n /**\n * Remove an event handler\n */\n off(event: string, handler: TcpEventHandler): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n const index = handlers.indexOf(handler);\n if (index !== -1) {\n handlers.splice(index, 1);\n }\n }\n\n /**\n * Emit an event\n */\n private emit(event: string, data?: unknown): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n for (const handler of handlers) {\n try {\n const result = handler(data);\n // Handle async handlers properly\n if (result instanceof Promise) {\n result.catch((error) => {\n console.error(`TCP async handler error for \"${event}\":`, error);\n });\n }\n } catch (error) {\n console.error(`TCP handler error for \"${event}\":`, error);\n }\n }\n }\n\n /**\n * Handle incoming data\n */\n private handleData(data: Buffer | string): void {\n for (const handler of this.dataHandlers) {\n try {\n const result = handler(data);\n // Handle async handlers properly\n if (result instanceof Promise) {\n result.catch((error) => {\n console.error('TCP async data handler error:', error);\n });\n }\n } catch (error) {\n console.error('TCP data handler error:', error);\n }\n }\n this.emit('data', data);\n }\n\n /**\n * Handle disconnection with reconnect logic\n */\n private handleDisconnect(): void {\n if (!this.config.reconnect?.enabled || this.reconnecting) {\n return;\n }\n\n const maxAttempts = this.config.reconnect.max_attempts ?? 10;\n const delay = this.parseDuration(this.config.reconnect.delay ?? '5s');\n\n if (this.reconnectAttempts >= maxAttempts) {\n this.emit('reconnect_failed', { attempts: this.reconnectAttempts });\n return;\n }\n\n this.reconnecting = true;\n this.reconnectAttempts++;\n\n // Track the timer so it can be cleared on disconnect\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.connect()\n .then(() => {\n this.emit('reconnected', { attempts: this.reconnectAttempts });\n })\n .catch(() => {\n this.reconnecting = false;\n this.handleDisconnect();\n });\n }, delay);\n }\n\n /**\n * Parse duration string to milliseconds\n */\n private parseDuration(duration: string): number {\n const match = duration.match(/^(\\d+)(ms|s|m|h)?$/);\n if (!match) return 5000;\n\n const value = parseInt(match[1]!, 10);\n const unit = match[2] ?? 's';\n\n switch (unit) {\n case 'ms':\n return value;\n case 's':\n return value * 1000;\n case 'm':\n return value * 60 * 1000;\n case 'h':\n return value * 60 * 60 * 1000;\n default:\n return value * 1000;\n }\n }\n}\n\n/**\n * Create a TCP pipe\n */\nexport function createTcpPipe(options: TcpPipeOptions): TcpPipe {\n return new TcpPipe(options);\n}\n\n// Re-export UDP pipe\nexport * from './udp.js';\n","/**\n * UDP pipe for datagram communication\n */\n\nimport { createSocket, Socket, RemoteInfo } from 'dgram';\nimport type { Pipe, PipeResponse, UdpPipeConfig } from '../types.js';\n\nexport interface UdpPipeOptions {\n name: string;\n config: UdpPipeConfig;\n}\n\nexport interface UdpMessage {\n data: Buffer;\n rinfo: RemoteInfo;\n}\n\nexport type UdpMessageHandler = (msg: UdpMessage) => void | Promise<void>;\nexport type UdpEventHandler = (data?: unknown) => void | Promise<void>;\n\nexport class UdpPipe implements Pipe {\n public readonly name: string;\n public readonly type = 'udp';\n private socket: Socket | null = null;\n private config: UdpPipeConfig;\n private bound = false;\n private messageHandlers: UdpMessageHandler[] = [];\n private eventHandlers: Map<string, UdpEventHandler[]> = new Map();\n\n constructor(options: UdpPipeOptions) {\n this.name = options.name;\n this.config = options.config;\n }\n\n /**\n * Bind to a port to receive messages\n */\n async bind(port?: number): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.socket = createSocket('udp4');\n\n this.socket.on('message', (msg, rinfo) => {\n this.handleMessage(msg, rinfo);\n });\n\n this.socket.on('error', (error) => {\n if (!this.bound) {\n reject(error);\n }\n this.emit('error', error);\n });\n\n this.socket.on('listening', () => {\n this.bound = true;\n const address = this.socket!.address();\n this.emit('listening', address);\n resolve();\n });\n\n // Setup broadcast if configured\n if (this.config.broadcast) {\n this.socket.setBroadcast(true);\n }\n\n // Bind to port\n const bindPort = port ?? this.config.port;\n const bindHost = this.config.host ?? '0.0.0.0';\n this.socket.bind(bindPort, bindHost, () => {\n // Join multicast group if configured\n if (this.config.multicast) {\n this.socket!.addMembership(this.config.multicast);\n }\n });\n\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Close the socket\n */\n async disconnect(): Promise<void> {\n return new Promise((resolve) => {\n if (this.socket) {\n this.socket.close(() => {\n this.socket = null;\n this.bound = false;\n resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * Check if bound\n */\n isConnected(): boolean {\n return this.bound;\n }\n\n /**\n * Send data to a specific host and port\n */\n async send(\n data: Buffer | string,\n host: string,\n port: number\n ): Promise<PipeResponse> {\n if (!this.socket) {\n // Create socket if not exists (for send-only mode)\n this.socket = createSocket('udp4');\n if (this.config.broadcast) {\n this.socket.setBroadcast(true);\n }\n }\n\n const buffer = typeof data === 'string' ? Buffer.from(data) : data;\n\n return new Promise((resolve) => {\n this.socket!.send(buffer, port, host, (error) => {\n if (error) {\n resolve({ success: false, error: error.message });\n } else {\n resolve({ success: true });\n }\n });\n });\n }\n\n /**\n * Broadcast data to all hosts on the network\n */\n async broadcast(\n data: Buffer | string,\n port: number,\n address = '255.255.255.255'\n ): Promise<PipeResponse> {\n if (!this.socket) {\n this.socket = createSocket('udp4');\n this.socket.setBroadcast(true);\n }\n\n const buffer = typeof data === 'string' ? Buffer.from(data) : data;\n\n return new Promise((resolve) => {\n this.socket!.send(buffer, port, address, (error) => {\n if (error) {\n resolve({ success: false, error: error.message });\n } else {\n resolve({ success: true });\n }\n });\n });\n }\n\n /**\n * Send to multicast group\n */\n async multicast(\n data: Buffer | string,\n port: number,\n group?: string\n ): Promise<PipeResponse> {\n const multicastGroup = group ?? this.config.multicast;\n if (!multicastGroup) {\n return { success: false, error: 'No multicast group configured' };\n }\n\n return this.send(data, multicastGroup, port);\n }\n\n /**\n * Register a message handler\n */\n onMessage(handler: UdpMessageHandler): void {\n this.messageHandlers.push(handler);\n }\n\n /**\n * Remove a message handler\n */\n offMessage(handler: UdpMessageHandler): void {\n const index = this.messageHandlers.indexOf(handler);\n if (index !== -1) {\n this.messageHandlers.splice(index, 1);\n }\n }\n\n /**\n * Register an event handler\n */\n on(event: string, handler: UdpEventHandler): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n handlers.push(handler);\n this.eventHandlers.set(event, handlers);\n }\n\n /**\n * Remove an event handler\n */\n off(event: string, handler: UdpEventHandler): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n const index = handlers.indexOf(handler);\n if (index !== -1) {\n handlers.splice(index, 1);\n }\n }\n\n /**\n * Emit an event\n */\n private emit(event: string, data?: unknown): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n for (const handler of handlers) {\n try {\n const result = handler(data);\n // Handle async handlers properly\n if (result instanceof Promise) {\n result.catch((error) => {\n console.error(`UDP async handler error for \"${event}\":`, error);\n });\n }\n } catch (error) {\n console.error(`UDP handler error for \"${event}\":`, error);\n }\n }\n }\n\n /**\n * Handle incoming messages\n */\n private handleMessage(data: Buffer, rinfo: RemoteInfo): void {\n const msg: UdpMessage = { data, rinfo };\n\n for (const handler of this.messageHandlers) {\n try {\n const result = handler(msg);\n // Handle async handlers properly\n if (result instanceof Promise) {\n result.catch((error) => {\n console.error('UDP async message handler error:', error);\n });\n }\n } catch (error) {\n console.error('UDP message handler error:', error);\n }\n }\n\n this.emit('message', msg);\n }\n}\n\n/**\n * Create a UDP pipe\n */\nexport function createUdpPipe(options: UdpPipeOptions): UdpPipe {\n return new UdpPipe(options);\n}\n"],"mappings":"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","names":[]}
@@ -80,10 +80,14 @@ var WebhookPipe = class {
80
80
  return receivedSignature === secret;
81
81
  }
82
82
  case "signature": {
83
- return true;
83
+ const signatureValue = headers[signatureHeader.toLowerCase()];
84
+ if (!signatureValue) {
85
+ return false;
86
+ }
87
+ return this.timingSafeEqual(signatureValue, secret);
84
88
  }
85
89
  default:
86
- return true;
90
+ return false;
87
91
  }
88
92
  }
89
93
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/webhook/index.ts"],"sourcesContent":["/**\n * Webhook pipe for incoming and outgoing webhooks\n */\n\nimport { createHmac } from 'node:crypto';\nimport type { Pipe, PipeResponse, WebhookPipeConfig } from '../types.js';\n\nexport interface WebhookPipeOptions {\n name: string;\n config: WebhookPipeConfig;\n}\n\nexport type WebhookHandler = (\n body: unknown,\n headers: Record<string, string>\n) => void | Promise<void>;\n\nexport class WebhookPipe implements Pipe {\n public readonly name: string;\n public readonly type = 'webhook';\n private config: WebhookPipeConfig;\n private handlers: WebhookHandler[] = [];\n\n constructor(options: WebhookPipeOptions) {\n this.name = options.name;\n this.config = options.config;\n }\n\n isConnected(): boolean {\n return true;\n }\n\n /**\n * Get the webhook path\n */\n getPath(): string {\n return this.config.path;\n }\n\n /**\n * Get the expected method\n */\n getMethod(): string {\n return this.config.method ?? 'POST';\n }\n\n /**\n * Register a handler for incoming webhooks\n */\n onWebhook(handler: WebhookHandler): () => void {\n this.handlers.push(handler);\n return () => {\n const index = this.handlers.indexOf(handler);\n if (index !== -1) {\n this.handlers.splice(index, 1);\n }\n };\n }\n\n /**\n * Handle an incoming webhook request\n */\n async handleRequest(\n body: unknown,\n headers: Record<string, string>\n ): Promise<PipeResponse> {\n // Verify signature if configured\n if (this.config.verification) {\n const valid = this.verifySignature(body, headers);\n if (!valid) {\n return { success: false, error: 'Invalid signature', status: 401 };\n }\n }\n\n // Call handlers\n for (const handler of this.handlers) {\n try {\n await handler(body, headers);\n } catch (error) {\n console.error(`Webhook handler error:`, error);\n }\n }\n\n // Check for config handlers\n if (this.config.handlers) {\n // These would be processed by the action executor\n }\n\n return { success: true, status: 200 };\n }\n\n /**\n * Verify webhook signature\n */\n private verifySignature(\n body: unknown,\n headers: Record<string, string>\n ): boolean {\n if (!this.config.verification) return true;\n\n const { type, secret, header, algorithm } = this.config.verification;\n const signatureHeader = header ?? 'x-signature';\n const receivedSignature = headers[signatureHeader.toLowerCase()];\n\n if (!receivedSignature || !secret) {\n return false;\n }\n\n const bodyString = typeof body === 'string' ? body : JSON.stringify(body);\n\n switch (type) {\n case 'hmac': {\n const algo = algorithm ?? 'sha256';\n const expectedSignature = createHmac(algo, secret)\n .update(bodyString)\n .digest('hex');\n\n // Handle both raw and prefixed signatures\n const cleanReceived = receivedSignature.replace(/^sha\\d+=/, '');\n return this.timingSafeEqual(cleanReceived, expectedSignature);\n }\n\n case 'token': {\n return receivedSignature === secret;\n }\n\n case 'signature': {\n // Custom signature verification\n // This would depend on the specific service\n return true;\n }\n\n default:\n return true;\n }\n }\n\n /**\n * Timing-safe string comparison\n */\n private timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) {\n return false;\n }\n\n let result = 0;\n for (let i = 0; i < a.length; i++) {\n result |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return result === 0;\n }\n}\n\n/**\n * Outgoing webhook sender\n */\nexport class WebhookSender {\n /**\n * Send a Discord webhook\n */\n static async sendDiscordWebhook(\n url: string,\n options: {\n content?: string;\n username?: string;\n avatar_url?: string;\n embeds?: Record<string, unknown>[];\n }\n ): Promise<PipeResponse> {\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(options),\n });\n\n if (!response.ok) {\n return {\n success: false,\n error: `HTTP ${response.status}`,\n status: response.status,\n };\n }\n\n return { success: true, status: response.status };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n /**\n * Send a generic webhook\n */\n static async send(\n url: string,\n body: unknown,\n options: { headers?: Record<string, string>; method?: string } = {}\n ): Promise<PipeResponse> {\n try {\n const response = await fetch(url, {\n method: options.method ?? 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n body: typeof body === 'string' ? body : JSON.stringify(body),\n });\n\n let data: unknown;\n try {\n data = await response.json();\n } catch {\n data = await response.text();\n }\n\n return {\n success: response.ok,\n data,\n status: response.status,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n}\n\n/**\n * Create a webhook pipe\n */\nexport function createWebhookPipe(options: WebhookPipeOptions): WebhookPipe {\n return new WebhookPipe(options);\n}\n"],"mappings":";AAIA,SAAS,kBAAkB;AAapB,IAAM,cAAN,MAAkC;AAAA,EACvB;AAAA,EACA,OAAO;AAAA,EACf;AAAA,EACA,WAA6B,CAAC;AAAA,EAEtC,YAAY,SAA6B;AACvC,SAAK,OAAO,QAAQ;AACpB,SAAK,SAAS,QAAQ;AAAA,EACxB;AAAA,EAEA,cAAuB;AACrB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,UAAkB;AAChB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKA,YAAoB;AAClB,WAAO,KAAK,OAAO,UAAU;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,SAAqC;AAC7C,SAAK,SAAS,KAAK,OAAO;AAC1B,WAAO,MAAM;AACX,YAAM,QAAQ,KAAK,SAAS,QAAQ,OAAO;AAC3C,UAAI,UAAU,IAAI;AAChB,aAAK,SAAS,OAAO,OAAO,CAAC;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cACJ,MACA,SACuB;AAEvB,QAAI,KAAK,OAAO,cAAc;AAC5B,YAAM,QAAQ,KAAK,gBAAgB,MAAM,OAAO;AAChD,UAAI,CAAC,OAAO;AACV,eAAO,EAAE,SAAS,OAAO,OAAO,qBAAqB,QAAQ,IAAI;AAAA,MACnE;AAAA,IACF;AAGA,eAAW,WAAW,KAAK,UAAU;AACnC,UAAI;AACF,cAAM,QAAQ,MAAM,OAAO;AAAA,MAC7B,SAAS,OAAO;AACd,gBAAQ,MAAM,0BAA0B,KAAK;AAAA,MAC/C;AAAA,IACF;AAGA,QAAI,KAAK,OAAO,UAAU;AAAA,IAE1B;AAEA,WAAO,EAAE,SAAS,MAAM,QAAQ,IAAI;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKQ,gBACN,MACA,SACS;AACT,QAAI,CAAC,KAAK,OAAO,aAAc,QAAO;AAEtC,UAAM,EAAE,MAAM,QAAQ,QAAQ,UAAU,IAAI,KAAK,OAAO;AACxD,UAAM,kBAAkB,UAAU;AAClC,UAAM,oBAAoB,QAAQ,gBAAgB,YAAY,CAAC;AAE/D,QAAI,CAAC,qBAAqB,CAAC,QAAQ;AACjC,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,IAAI;AAExE,YAAQ,MAAM;AAAA,MACZ,KAAK,QAAQ;AACX,cAAM,OAAO,aAAa;AAC1B,cAAM,oBAAoB,WAAW,MAAM,MAAM,EAC9C,OAAO,UAAU,EACjB,OAAO,KAAK;AAGf,cAAM,gBAAgB,kBAAkB,QAAQ,YAAY,EAAE;AAC9D,eAAO,KAAK,gBAAgB,eAAe,iBAAiB;AAAA,MAC9D;AAAA,MAEA,KAAK,SAAS;AACZ,eAAO,sBAAsB;AAAA,MAC/B;AAAA,MAEA,KAAK,aAAa;AAGhB,eAAO;AAAA,MACT;AAAA,MAEA;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB,GAAW,GAAoB;AACrD,QAAI,EAAE,WAAW,EAAE,QAAQ;AACzB,aAAO;AAAA,IACT;AAEA,QAAI,SAAS;AACb,aAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,gBAAU,EAAE,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC;AAAA,IAC5C;AACA,WAAO,WAAW;AAAA,EACpB;AACF;AAKO,IAAM,gBAAN,MAAoB;AAAA;AAAA;AAAA;AAAA,EAIzB,aAAa,mBACX,KACA,SAMuB;AACvB,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,MAC9B,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,OAAO,QAAQ,SAAS,MAAM;AAAA,UAC9B,QAAQ,SAAS;AAAA,QACnB;AAAA,MACF;AAEA,aAAO,EAAE,SAAS,MAAM,QAAQ,SAAS,OAAO;AAAA,IAClD,SAAS,OAAO;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,KACX,KACA,MACA,UAAiE,CAAC,GAC3C;AACvB,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ,QAAQ,UAAU;AAAA,QAC1B,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,GAAG,QAAQ;AAAA,QACb;AAAA,QACA,MAAM,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,IAAI;AAAA,MAC7D,CAAC;AAED,UAAI;AACJ,UAAI;AACF,eAAO,MAAM,SAAS,KAAK;AAAA,MAC7B,QAAQ;AACN,eAAO,MAAM,SAAS,KAAK;AAAA,MAC7B;AAEA,aAAO;AAAA,QACL,SAAS,SAAS;AAAA,QAClB;AAAA,QACA,QAAQ,SAAS;AAAA,MACnB;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AACF;AAKO,SAAS,kBAAkB,SAA0C;AAC1E,SAAO,IAAI,YAAY,OAAO;AAChC;","names":[]}
1
+ {"version":3,"sources":["../../src/webhook/index.ts"],"sourcesContent":["/**\n * Webhook pipe for incoming and outgoing webhooks\n */\n\nimport { createHmac } from 'node:crypto';\nimport type { Pipe, PipeResponse, WebhookPipeConfig } from '../types.js';\n\nexport interface WebhookPipeOptions {\n name: string;\n config: WebhookPipeConfig;\n}\n\nexport type WebhookHandler = (\n body: unknown,\n headers: Record<string, string>\n) => void | Promise<void>;\n\nexport class WebhookPipe implements Pipe {\n public readonly name: string;\n public readonly type = 'webhook';\n private config: WebhookPipeConfig;\n private handlers: WebhookHandler[] = [];\n\n constructor(options: WebhookPipeOptions) {\n this.name = options.name;\n this.config = options.config;\n }\n\n isConnected(): boolean {\n return true;\n }\n\n /**\n * Get the webhook path\n */\n getPath(): string {\n return this.config.path;\n }\n\n /**\n * Get the expected method\n */\n getMethod(): string {\n return this.config.method ?? 'POST';\n }\n\n /**\n * Register a handler for incoming webhooks\n */\n onWebhook(handler: WebhookHandler): () => void {\n this.handlers.push(handler);\n return () => {\n const index = this.handlers.indexOf(handler);\n if (index !== -1) {\n this.handlers.splice(index, 1);\n }\n };\n }\n\n /**\n * Handle an incoming webhook request\n */\n async handleRequest(\n body: unknown,\n headers: Record<string, string>\n ): Promise<PipeResponse> {\n // Verify signature if configured\n if (this.config.verification) {\n const valid = this.verifySignature(body, headers);\n if (!valid) {\n return { success: false, error: 'Invalid signature', status: 401 };\n }\n }\n\n // Call handlers\n for (const handler of this.handlers) {\n try {\n await handler(body, headers);\n } catch (error) {\n console.error(`Webhook handler error:`, error);\n }\n }\n\n // Check for config handlers\n if (this.config.handlers) {\n // These would be processed by the action executor\n }\n\n return { success: true, status: 200 };\n }\n\n /**\n * Verify webhook signature\n */\n private verifySignature(\n body: unknown,\n headers: Record<string, string>\n ): boolean {\n if (!this.config.verification) return true;\n\n const { type, secret, header, algorithm } = this.config.verification;\n const signatureHeader = header ?? 'x-signature';\n const receivedSignature = headers[signatureHeader.toLowerCase()];\n\n if (!receivedSignature || !secret) {\n return false;\n }\n\n const bodyString = typeof body === 'string' ? body : JSON.stringify(body);\n\n switch (type) {\n case 'hmac': {\n const algo = algorithm ?? 'sha256';\n const expectedSignature = createHmac(algo, secret)\n .update(bodyString)\n .digest('hex');\n\n // Handle both raw and prefixed signatures\n const cleanReceived = receivedSignature.replace(/^sha\\d+=/, '');\n return this.timingSafeEqual(cleanReceived, expectedSignature);\n }\n\n case 'token': {\n return receivedSignature === secret;\n }\n\n case 'signature': {\n // Custom signature verification - fail closed by default\n // Requires both a header configuration and a secret to be provided\n const signatureValue = headers[signatureHeader.toLowerCase()];\n if (!signatureValue) {\n return false;\n }\n // Compare the provided signature with the secret using timing-safe comparison\n return this.timingSafeEqual(signatureValue, secret);\n }\n\n default:\n // Unknown verification type - fail closed for security\n return false;\n }\n }\n\n /**\n * Timing-safe string comparison\n */\n private timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) {\n return false;\n }\n\n let result = 0;\n for (let i = 0; i < a.length; i++) {\n result |= a.charCodeAt(i) ^ b.charCodeAt(i);\n }\n return result === 0;\n }\n}\n\n/**\n * Outgoing webhook sender\n */\nexport class WebhookSender {\n /**\n * Send a Discord webhook\n */\n static async sendDiscordWebhook(\n url: string,\n options: {\n content?: string;\n username?: string;\n avatar_url?: string;\n embeds?: Record<string, unknown>[];\n }\n ): Promise<PipeResponse> {\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(options),\n });\n\n if (!response.ok) {\n return {\n success: false,\n error: `HTTP ${response.status}`,\n status: response.status,\n };\n }\n\n return { success: true, status: response.status };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n /**\n * Send a generic webhook\n */\n static async send(\n url: string,\n body: unknown,\n options: { headers?: Record<string, string>; method?: string } = {}\n ): Promise<PipeResponse> {\n try {\n const response = await fetch(url, {\n method: options.method ?? 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n body: typeof body === 'string' ? body : JSON.stringify(body),\n });\n\n let data: unknown;\n try {\n data = await response.json();\n } catch {\n data = await response.text();\n }\n\n return {\n success: response.ok,\n data,\n status: response.status,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n}\n\n/**\n * Create a webhook pipe\n */\nexport function createWebhookPipe(options: WebhookPipeOptions): WebhookPipe {\n return new WebhookPipe(options);\n}\n"],"mappings":";AAIA,SAAS,kBAAkB;AAapB,IAAM,cAAN,MAAkC;AAAA,EACvB;AAAA,EACA,OAAO;AAAA,EACf;AAAA,EACA,WAA6B,CAAC;AAAA,EAEtC,YAAY,SAA6B;AACvC,SAAK,OAAO,QAAQ;AACpB,SAAK,SAAS,QAAQ;AAAA,EACxB;AAAA,EAEA,cAAuB;AACrB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,UAAkB;AAChB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKA,YAAoB;AAClB,WAAO,KAAK,OAAO,UAAU;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,SAAqC;AAC7C,SAAK,SAAS,KAAK,OAAO;AAC1B,WAAO,MAAM;AACX,YAAM,QAAQ,KAAK,SAAS,QAAQ,OAAO;AAC3C,UAAI,UAAU,IAAI;AAChB,aAAK,SAAS,OAAO,OAAO,CAAC;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cACJ,MACA,SACuB;AAEvB,QAAI,KAAK,OAAO,cAAc;AAC5B,YAAM,QAAQ,KAAK,gBAAgB,MAAM,OAAO;AAChD,UAAI,CAAC,OAAO;AACV,eAAO,EAAE,SAAS,OAAO,OAAO,qBAAqB,QAAQ,IAAI;AAAA,MACnE;AAAA,IACF;AAGA,eAAW,WAAW,KAAK,UAAU;AACnC,UAAI;AACF,cAAM,QAAQ,MAAM,OAAO;AAAA,MAC7B,SAAS,OAAO;AACd,gBAAQ,MAAM,0BAA0B,KAAK;AAAA,MAC/C;AAAA,IACF;AAGA,QAAI,KAAK,OAAO,UAAU;AAAA,IAE1B;AAEA,WAAO,EAAE,SAAS,MAAM,QAAQ,IAAI;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKQ,gBACN,MACA,SACS;AACT,QAAI,CAAC,KAAK,OAAO,aAAc,QAAO;AAEtC,UAAM,EAAE,MAAM,QAAQ,QAAQ,UAAU,IAAI,KAAK,OAAO;AACxD,UAAM,kBAAkB,UAAU;AAClC,UAAM,oBAAoB,QAAQ,gBAAgB,YAAY,CAAC;AAE/D,QAAI,CAAC,qBAAqB,CAAC,QAAQ;AACjC,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,IAAI;AAExE,YAAQ,MAAM;AAAA,MACZ,KAAK,QAAQ;AACX,cAAM,OAAO,aAAa;AAC1B,cAAM,oBAAoB,WAAW,MAAM,MAAM,EAC9C,OAAO,UAAU,EACjB,OAAO,KAAK;AAGf,cAAM,gBAAgB,kBAAkB,QAAQ,YAAY,EAAE;AAC9D,eAAO,KAAK,gBAAgB,eAAe,iBAAiB;AAAA,MAC9D;AAAA,MAEA,KAAK,SAAS;AACZ,eAAO,sBAAsB;AAAA,MAC/B;AAAA,MAEA,KAAK,aAAa;AAGhB,cAAM,iBAAiB,QAAQ,gBAAgB,YAAY,CAAC;AAC5D,YAAI,CAAC,gBAAgB;AACnB,iBAAO;AAAA,QACT;AAEA,eAAO,KAAK,gBAAgB,gBAAgB,MAAM;AAAA,MACpD;AAAA,MAEA;AAEE,eAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB,GAAW,GAAoB;AACrD,QAAI,EAAE,WAAW,EAAE,QAAQ;AACzB,aAAO;AAAA,IACT;AAEA,QAAI,SAAS;AACb,aAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,gBAAU,EAAE,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC;AAAA,IAC5C;AACA,WAAO,WAAW;AAAA,EACpB;AACF;AAKO,IAAM,gBAAN,MAAoB;AAAA;AAAA;AAAA;AAAA,EAIzB,aAAa,mBACX,KACA,SAMuB;AACvB,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,MAC9B,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,OAAO,QAAQ,SAAS,MAAM;AAAA,UAC9B,QAAQ,SAAS;AAAA,QACnB;AAAA,MACF;AAEA,aAAO,EAAE,SAAS,MAAM,QAAQ,SAAS,OAAO;AAAA,IAClD,SAAS,OAAO;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,KACX,KACA,MACA,UAAiE,CAAC,GAC3C;AACvB,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ,QAAQ,UAAU;AAAA,QAC1B,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,GAAG,QAAQ;AAAA,QACb;AAAA,QACA,MAAM,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,IAAI;AAAA,MAC7D,CAAC;AAED,UAAI;AACJ,UAAI;AACF,eAAO,MAAM,SAAS,KAAK;AAAA,MAC7B,QAAQ;AACN,eAAO,MAAM,SAAS,KAAK;AAAA,MAC7B;AAEA,aAAO;AAAA,QACL,SAAS,SAAS;AAAA,QAClB;AAAA,QACA,QAAQ,SAAS;AAAA,MACnB;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AACF;AAKO,SAAS,kBAAkB,SAA0C;AAC1E,SAAO,IAAI,YAAY,OAAO;AAChC;","names":[]}
@@ -16,6 +16,7 @@ declare class WebSocketPipe implements Pipe {
16
16
  private config;
17
17
  private reconnectAttempts;
18
18
  private reconnecting;
19
+ private reconnectTimer;
19
20
  private heartbeatInterval;
20
21
  private messageHandlers;
21
22
  private connected;
@@ -7,6 +7,7 @@ var WebSocketPipe = class {
7
7
  config;
8
8
  reconnectAttempts = 0;
9
9
  reconnecting = false;
10
+ reconnectTimer = null;
10
11
  heartbeatInterval = null;
11
12
  messageHandlers = /* @__PURE__ */ new Map();
12
13
  connected = false;
@@ -54,6 +55,10 @@ var WebSocketPipe = class {
54
55
  async disconnect() {
55
56
  this.stopHeartbeat();
56
57
  this.reconnecting = false;
58
+ if (this.reconnectTimer) {
59
+ clearTimeout(this.reconnectTimer);
60
+ this.reconnectTimer = null;
61
+ }
57
62
  if (this.ws) {
58
63
  this.ws.close();
59
64
  this.ws = null;
@@ -86,13 +91,21 @@ var WebSocketPipe = class {
86
91
  return { success: false, error: "Not connected" };
87
92
  }
88
93
  return new Promise((resolve) => {
89
- const timer = setTimeout(() => {
94
+ let resolved = false;
95
+ const cleanup = () => {
90
96
  this.off(responseEvent, handler);
97
+ };
98
+ const timer = setTimeout(() => {
99
+ if (resolved) return;
100
+ resolved = true;
101
+ cleanup();
91
102
  resolve({ success: false, error: "Request timeout" });
92
103
  }, timeout);
93
104
  const handler = (response) => {
105
+ if (resolved) return;
106
+ resolved = true;
94
107
  clearTimeout(timer);
95
- this.off(responseEvent, handler);
108
+ cleanup();
96
109
  resolve({ success: true, data: response });
97
110
  };
98
111
  this.on(responseEvent, handler);
@@ -124,7 +137,12 @@ var WebSocketPipe = class {
124
137
  const handlers = this.messageHandlers.get(event) ?? [];
125
138
  for (const handler of handlers) {
126
139
  try {
127
- handler(data);
140
+ const result = handler(data);
141
+ if (result instanceof Promise) {
142
+ result.catch((error) => {
143
+ console.error(`WebSocket async handler error for "${event}":`, error);
144
+ });
145
+ }
128
146
  } catch (error) {
129
147
  console.error(`WebSocket handler error for "${event}":`, error);
130
148
  }
@@ -162,14 +180,14 @@ var WebSocketPipe = class {
162
180
  }
163
181
  this.reconnecting = true;
164
182
  this.reconnectAttempts++;
165
- setTimeout(async () => {
166
- try {
167
- await this.connect();
183
+ this.reconnectTimer = setTimeout(() => {
184
+ this.reconnectTimer = null;
185
+ this.connect().then(() => {
168
186
  this.emit("reconnected", { attempts: this.reconnectAttempts });
169
- } catch {
187
+ }).catch(() => {
170
188
  this.reconnecting = false;
171
189
  this.handleDisconnect();
172
- }
190
+ });
173
191
  }, delay);
174
192
  }
175
193
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/websocket/index.ts"],"sourcesContent":["/**\n * WebSocket pipe for bidirectional communication\n */\n\nimport WebSocket from 'ws';\nimport type { Pipe, PipeResponse, WebSocketPipeConfig } from '../types.js';\n\nexport interface WebSocketPipeOptions {\n name: string;\n config: WebSocketPipeConfig;\n}\n\nexport type WebSocketMessageHandler = (data: unknown) => void | Promise<void>;\n\nexport class WebSocketPipe implements Pipe {\n public readonly name: string;\n public readonly type = 'websocket';\n private ws: WebSocket | null = null;\n private config: WebSocketPipeConfig;\n private reconnectAttempts = 0;\n private reconnecting = false;\n private heartbeatInterval: NodeJS.Timeout | null = null;\n private messageHandlers: Map<string, WebSocketMessageHandler[]> = new Map();\n private connected = false;\n\n constructor(options: WebSocketPipeOptions) {\n this.name = options.name;\n this.config = options.config;\n }\n\n /**\n * Connect to the WebSocket server\n */\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n const headers = this.config.headers as Record<string, string>;\n this.ws = new WebSocket(this.config.url, { headers });\n\n this.ws.on('open', () => {\n this.connected = true;\n this.reconnectAttempts = 0;\n this.reconnecting = false;\n this.startHeartbeat();\n resolve();\n });\n\n this.ws.on('message', (data) => {\n this.handleMessage(data);\n });\n\n this.ws.on('close', () => {\n this.connected = false;\n this.stopHeartbeat();\n this.handleDisconnect();\n });\n\n this.ws.on('error', (error) => {\n if (!this.connected) {\n reject(error);\n }\n this.emit('error', error);\n });\n\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Disconnect from the server\n */\n async disconnect(): Promise<void> {\n this.stopHeartbeat();\n this.reconnecting = false;\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n this.connected = false;\n }\n\n /**\n * Check if connected\n */\n isConnected(): boolean {\n return this.connected && this.ws?.readyState === WebSocket.OPEN;\n }\n\n /**\n * Send a message\n */\n send(data: unknown): boolean {\n if (!this.isConnected()) {\n return false;\n }\n\n const message = typeof data === 'string' ? data : JSON.stringify(data);\n this.ws!.send(message);\n return true;\n }\n\n /**\n * Send and wait for a response (request-response pattern)\n */\n async request<T = unknown>(\n data: unknown,\n options: { timeout?: number; responseEvent?: string } = {}\n ): Promise<PipeResponse<T>> {\n const { timeout = 30000, responseEvent = 'response' } = options;\n\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n return new Promise((resolve) => {\n const timer = setTimeout(() => {\n this.off(responseEvent, handler);\n resolve({ success: false, error: 'Request timeout' });\n }, timeout);\n\n const handler = (response: unknown) => {\n clearTimeout(timer);\n this.off(responseEvent, handler);\n resolve({ success: true, data: response as T });\n };\n\n this.on(responseEvent, handler);\n this.send(data);\n });\n }\n\n /**\n * Register a message handler\n */\n on(event: string, handler: WebSocketMessageHandler): void {\n const handlers = this.messageHandlers.get(event) ?? [];\n handlers.push(handler);\n this.messageHandlers.set(event, handlers);\n }\n\n /**\n * Remove a message handler\n */\n off(event: string, handler: WebSocketMessageHandler): void {\n const handlers = this.messageHandlers.get(event) ?? [];\n const index = handlers.indexOf(handler);\n if (index !== -1) {\n handlers.splice(index, 1);\n }\n }\n\n /**\n * Emit an event to handlers\n */\n private emit(event: string, data: unknown): void {\n const handlers = this.messageHandlers.get(event) ?? [];\n for (const handler of handlers) {\n try {\n handler(data);\n } catch (error) {\n console.error(`WebSocket handler error for \"${event}\":`, error);\n }\n }\n }\n\n /**\n * Handle incoming messages\n */\n private handleMessage(rawData: WebSocket.RawData): void {\n let data: unknown;\n\n try {\n const str = rawData.toString();\n data = JSON.parse(str);\n } catch {\n data = rawData.toString();\n }\n\n // Emit to 'message' handlers\n this.emit('message', data);\n\n // Check for event field in JSON messages\n if (typeof data === 'object' && data !== null && 'event' in data) {\n const event = (data as { event: string }).event;\n this.emit(event, data);\n }\n }\n\n /**\n * Handle disconnection\n */\n private handleDisconnect(): void {\n if (!this.config.reconnect?.enabled || this.reconnecting) {\n return;\n }\n\n const maxAttempts = this.config.reconnect.max_attempts ?? 10;\n const delay = this.parseDuration(this.config.reconnect.delay ?? '5s');\n\n if (this.reconnectAttempts >= maxAttempts) {\n this.emit('reconnect_failed', { attempts: this.reconnectAttempts });\n return;\n }\n\n this.reconnecting = true;\n this.reconnectAttempts++;\n\n setTimeout(async () => {\n try {\n await this.connect();\n this.emit('reconnected', { attempts: this.reconnectAttempts });\n } catch {\n this.reconnecting = false;\n this.handleDisconnect();\n }\n }, delay);\n }\n\n /**\n * Start heartbeat\n */\n private startHeartbeat(): void {\n if (!this.config.heartbeat?.interval) return;\n\n const interval = this.parseDuration(this.config.heartbeat.interval);\n const message = this.config.heartbeat.message ?? 'ping';\n\n this.heartbeatInterval = setInterval(() => {\n if (this.isConnected()) {\n this.send(message);\n }\n }, interval);\n }\n\n /**\n * Stop heartbeat\n */\n private stopHeartbeat(): void {\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = null;\n }\n }\n\n /**\n * Parse duration string to milliseconds\n */\n private parseDuration(duration: string): number {\n const match = duration.match(/^(\\d+)(ms|s|m|h)?$/);\n if (!match) return 5000;\n\n const value = parseInt(match[1]!, 10);\n const unit = match[2] ?? 's';\n\n switch (unit) {\n case 'ms':\n return value;\n case 's':\n return value * 1000;\n case 'm':\n return value * 60 * 1000;\n case 'h':\n return value * 60 * 60 * 1000;\n default:\n return value * 1000;\n }\n }\n}\n\n/**\n * Create a WebSocket pipe\n */\nexport function createWebSocketPipe(options: WebSocketPipeOptions): WebSocketPipe {\n return new WebSocketPipe(options);\n}\n"],"mappings":";AAIA,OAAO,eAAe;AAUf,IAAM,gBAAN,MAAoC;AAAA,EACzB;AAAA,EACA,OAAO;AAAA,EACf,KAAuB;AAAA,EACvB;AAAA,EACA,oBAAoB;AAAA,EACpB,eAAe;AAAA,EACf,oBAA2C;AAAA,EAC3C,kBAA0D,oBAAI,IAAI;AAAA,EAClE,YAAY;AAAA,EAEpB,YAAY,SAA+B;AACzC,SAAK,OAAO,QAAQ;AACpB,SAAK,SAAS,QAAQ;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAyB;AAC7B,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI;AACF,cAAM,UAAU,KAAK,OAAO;AAC5B,aAAK,KAAK,IAAI,UAAU,KAAK,OAAO,KAAK,EAAE,QAAQ,CAAC;AAEpD,aAAK,GAAG,GAAG,QAAQ,MAAM;AACvB,eAAK,YAAY;AACjB,eAAK,oBAAoB;AACzB,eAAK,eAAe;AACpB,eAAK,eAAe;AACpB,kBAAQ;AAAA,QACV,CAAC;AAED,aAAK,GAAG,GAAG,WAAW,CAAC,SAAS;AAC9B,eAAK,cAAc,IAAI;AAAA,QACzB,CAAC;AAED,aAAK,GAAG,GAAG,SAAS,MAAM;AACxB,eAAK,YAAY;AACjB,eAAK,cAAc;AACnB,eAAK,iBAAiB;AAAA,QACxB,CAAC;AAED,aAAK,GAAG,GAAG,SAAS,CAAC,UAAU;AAC7B,cAAI,CAAC,KAAK,WAAW;AACnB,mBAAO,KAAK;AAAA,UACd;AACA,eAAK,KAAK,SAAS,KAAK;AAAA,QAC1B,CAAC;AAAA,MAEH,SAAS,OAAO;AACd,eAAO,KAAK;AAAA,MACd;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAA4B;AAChC,SAAK,cAAc;AACnB,SAAK,eAAe;AAEpB,QAAI,KAAK,IAAI;AACX,WAAK,GAAG,MAAM;AACd,WAAK,KAAK;AAAA,IACZ;AAEA,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,cAAuB;AACrB,WAAO,KAAK,aAAa,KAAK,IAAI,eAAe,UAAU;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,MAAwB;AAC3B,QAAI,CAAC,KAAK,YAAY,GAAG;AACvB,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,IAAI;AACrE,SAAK,GAAI,KAAK,OAAO;AACrB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QACJ,MACA,UAAwD,CAAC,GAC/B;AAC1B,UAAM,EAAE,UAAU,KAAO,gBAAgB,WAAW,IAAI;AAExD,QAAI,CAAC,KAAK,YAAY,GAAG;AACvB,aAAO,EAAE,SAAS,OAAO,OAAO,gBAAgB;AAAA,IAClD;AAEA,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,YAAM,QAAQ,WAAW,MAAM;AAC7B,aAAK,IAAI,eAAe,OAAO;AAC/B,gBAAQ,EAAE,SAAS,OAAO,OAAO,kBAAkB,CAAC;AAAA,MACtD,GAAG,OAAO;AAEV,YAAM,UAAU,CAAC,aAAsB;AACrC,qBAAa,KAAK;AAClB,aAAK,IAAI,eAAe,OAAO;AAC/B,gBAAQ,EAAE,SAAS,MAAM,MAAM,SAAc,CAAC;AAAA,MAChD;AAEA,WAAK,GAAG,eAAe,OAAO;AAC9B,WAAK,KAAK,IAAI;AAAA,IAChB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,GAAG,OAAe,SAAwC;AACxD,UAAM,WAAW,KAAK,gBAAgB,IAAI,KAAK,KAAK,CAAC;AACrD,aAAS,KAAK,OAAO;AACrB,SAAK,gBAAgB,IAAI,OAAO,QAAQ;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,OAAe,SAAwC;AACzD,UAAM,WAAW,KAAK,gBAAgB,IAAI,KAAK,KAAK,CAAC;AACrD,UAAM,QAAQ,SAAS,QAAQ,OAAO;AACtC,QAAI,UAAU,IAAI;AAChB,eAAS,OAAO,OAAO,CAAC;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,KAAK,OAAe,MAAqB;AAC/C,UAAM,WAAW,KAAK,gBAAgB,IAAI,KAAK,KAAK,CAAC;AACrD,eAAW,WAAW,UAAU;AAC9B,UAAI;AACF,gBAAQ,IAAI;AAAA,MACd,SAAS,OAAO;AACd,gBAAQ,MAAM,gCAAgC,KAAK,MAAM,KAAK;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,SAAkC;AACtD,QAAI;AAEJ,QAAI;AACF,YAAM,MAAM,QAAQ,SAAS;AAC7B,aAAO,KAAK,MAAM,GAAG;AAAA,IACvB,QAAQ;AACN,aAAO,QAAQ,SAAS;AAAA,IAC1B;AAGA,SAAK,KAAK,WAAW,IAAI;AAGzB,QAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,WAAW,MAAM;AAChE,YAAM,QAAS,KAA2B;AAC1C,WAAK,KAAK,OAAO,IAAI;AAAA,IACvB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAyB;AAC/B,QAAI,CAAC,KAAK,OAAO,WAAW,WAAW,KAAK,cAAc;AACxD;AAAA,IACF;AAEA,UAAM,cAAc,KAAK,OAAO,UAAU,gBAAgB;AAC1D,UAAM,QAAQ,KAAK,cAAc,KAAK,OAAO,UAAU,SAAS,IAAI;AAEpE,QAAI,KAAK,qBAAqB,aAAa;AACzC,WAAK,KAAK,oBAAoB,EAAE,UAAU,KAAK,kBAAkB,CAAC;AAClE;AAAA,IACF;AAEA,SAAK,eAAe;AACpB,SAAK;AAEL,eAAW,YAAY;AACrB,UAAI;AACF,cAAM,KAAK,QAAQ;AACnB,aAAK,KAAK,eAAe,EAAE,UAAU,KAAK,kBAAkB,CAAC;AAAA,MAC/D,QAAQ;AACN,aAAK,eAAe;AACpB,aAAK,iBAAiB;AAAA,MACxB;AAAA,IACF,GAAG,KAAK;AAAA,EACV;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAuB;AAC7B,QAAI,CAAC,KAAK,OAAO,WAAW,SAAU;AAEtC,UAAM,WAAW,KAAK,cAAc,KAAK,OAAO,UAAU,QAAQ;AAClE,UAAM,UAAU,KAAK,OAAO,UAAU,WAAW;AAEjD,SAAK,oBAAoB,YAAY,MAAM;AACzC,UAAI,KAAK,YAAY,GAAG;AACtB,aAAK,KAAK,OAAO;AAAA,MACnB;AAAA,IACF,GAAG,QAAQ;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC5B,QAAI,KAAK,mBAAmB;AAC1B,oBAAc,KAAK,iBAAiB;AACpC,WAAK,oBAAoB;AAAA,IAC3B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,UAA0B;AAC9C,UAAM,QAAQ,SAAS,MAAM,oBAAoB;AACjD,QAAI,CAAC,MAAO,QAAO;AAEnB,UAAM,QAAQ,SAAS,MAAM,CAAC,GAAI,EAAE;AACpC,UAAM,OAAO,MAAM,CAAC,KAAK;AAEzB,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO,QAAQ;AAAA,MACjB,KAAK;AACH,eAAO,QAAQ,KAAK;AAAA,MACtB,KAAK;AACH,eAAO,QAAQ,KAAK,KAAK;AAAA,MAC3B;AACE,eAAO,QAAQ;AAAA,IACnB;AAAA,EACF;AACF;AAKO,SAAS,oBAAoB,SAA8C;AAChF,SAAO,IAAI,cAAc,OAAO;AAClC;","names":[]}
1
+ {"version":3,"sources":["../../src/websocket/index.ts"],"sourcesContent":["/**\n * WebSocket pipe for bidirectional communication\n */\n\nimport WebSocket from 'ws';\nimport type { Pipe, PipeResponse, WebSocketPipeConfig } from '../types.js';\n\nexport interface WebSocketPipeOptions {\n name: string;\n config: WebSocketPipeConfig;\n}\n\nexport type WebSocketMessageHandler = (data: unknown) => void | Promise<void>;\n\nexport class WebSocketPipe implements Pipe {\n public readonly name: string;\n public readonly type = 'websocket';\n private ws: WebSocket | null = null;\n private config: WebSocketPipeConfig;\n private reconnectAttempts = 0;\n private reconnecting = false;\n private reconnectTimer: NodeJS.Timeout | null = null;\n private heartbeatInterval: NodeJS.Timeout | null = null;\n private messageHandlers: Map<string, WebSocketMessageHandler[]> = new Map();\n private connected = false;\n\n constructor(options: WebSocketPipeOptions) {\n this.name = options.name;\n this.config = options.config;\n }\n\n /**\n * Connect to the WebSocket server\n */\n async connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n const headers = this.config.headers as Record<string, string>;\n this.ws = new WebSocket(this.config.url, { headers });\n\n this.ws.on('open', () => {\n this.connected = true;\n this.reconnectAttempts = 0;\n this.reconnecting = false;\n this.startHeartbeat();\n resolve();\n });\n\n this.ws.on('message', (data) => {\n this.handleMessage(data);\n });\n\n this.ws.on('close', () => {\n this.connected = false;\n this.stopHeartbeat();\n this.handleDisconnect();\n });\n\n this.ws.on('error', (error) => {\n if (!this.connected) {\n reject(error);\n }\n this.emit('error', error);\n });\n\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Disconnect from the server\n */\n async disconnect(): Promise<void> {\n this.stopHeartbeat();\n this.reconnecting = false;\n\n // Clear any pending reconnect timer to prevent memory leaks\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n\n this.connected = false;\n }\n\n /**\n * Check if connected\n */\n isConnected(): boolean {\n return this.connected && this.ws?.readyState === WebSocket.OPEN;\n }\n\n /**\n * Send a message\n */\n send(data: unknown): boolean {\n if (!this.isConnected()) {\n return false;\n }\n\n const message = typeof data === 'string' ? data : JSON.stringify(data);\n this.ws!.send(message);\n return true;\n }\n\n /**\n * Send and wait for a response (request-response pattern)\n */\n async request<T = unknown>(\n data: unknown,\n options: { timeout?: number; responseEvent?: string } = {}\n ): Promise<PipeResponse<T>> {\n const { timeout = 30000, responseEvent = 'response' } = options;\n\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n return new Promise((resolve) => {\n let resolved = false;\n\n const cleanup = () => {\n this.off(responseEvent, handler);\n };\n\n const timer = setTimeout(() => {\n if (resolved) return;\n resolved = true;\n cleanup();\n resolve({ success: false, error: 'Request timeout' });\n }, timeout);\n\n const handler = (response: unknown) => {\n if (resolved) return;\n resolved = true;\n clearTimeout(timer);\n cleanup();\n resolve({ success: true, data: response as T });\n };\n\n this.on(responseEvent, handler);\n this.send(data);\n });\n }\n\n /**\n * Register a message handler\n */\n on(event: string, handler: WebSocketMessageHandler): void {\n const handlers = this.messageHandlers.get(event) ?? [];\n handlers.push(handler);\n this.messageHandlers.set(event, handlers);\n }\n\n /**\n * Remove a message handler\n */\n off(event: string, handler: WebSocketMessageHandler): void {\n const handlers = this.messageHandlers.get(event) ?? [];\n const index = handlers.indexOf(handler);\n if (index !== -1) {\n handlers.splice(index, 1);\n }\n }\n\n /**\n * Emit an event to handlers\n */\n private emit(event: string, data: unknown): void {\n const handlers = this.messageHandlers.get(event) ?? [];\n for (const handler of handlers) {\n try {\n const result = handler(data);\n // Handle async handlers properly\n if (result instanceof Promise) {\n result.catch((error) => {\n console.error(`WebSocket async handler error for \"${event}\":`, error);\n });\n }\n } catch (error) {\n console.error(`WebSocket handler error for \"${event}\":`, error);\n }\n }\n }\n\n /**\n * Handle incoming messages\n */\n private handleMessage(rawData: WebSocket.RawData): void {\n let data: unknown;\n\n try {\n const str = rawData.toString();\n data = JSON.parse(str);\n } catch {\n data = rawData.toString();\n }\n\n // Emit to 'message' handlers\n this.emit('message', data);\n\n // Check for event field in JSON messages\n if (typeof data === 'object' && data !== null && 'event' in data) {\n const event = (data as { event: string }).event;\n this.emit(event, data);\n }\n }\n\n /**\n * Handle disconnection\n */\n private handleDisconnect(): void {\n if (!this.config.reconnect?.enabled || this.reconnecting) {\n return;\n }\n\n const maxAttempts = this.config.reconnect.max_attempts ?? 10;\n const delay = this.parseDuration(this.config.reconnect.delay ?? '5s');\n\n if (this.reconnectAttempts >= maxAttempts) {\n this.emit('reconnect_failed', { attempts: this.reconnectAttempts });\n return;\n }\n\n this.reconnecting = true;\n this.reconnectAttempts++;\n\n // Track the timer so it can be cleared on disconnect\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.connect()\n .then(() => {\n this.emit('reconnected', { attempts: this.reconnectAttempts });\n })\n .catch(() => {\n this.reconnecting = false;\n this.handleDisconnect();\n });\n }, delay);\n }\n\n /**\n * Start heartbeat\n */\n private startHeartbeat(): void {\n if (!this.config.heartbeat?.interval) return;\n\n const interval = this.parseDuration(this.config.heartbeat.interval);\n const message = this.config.heartbeat.message ?? 'ping';\n\n this.heartbeatInterval = setInterval(() => {\n if (this.isConnected()) {\n this.send(message);\n }\n }, interval);\n }\n\n /**\n * Stop heartbeat\n */\n private stopHeartbeat(): void {\n if (this.heartbeatInterval) {\n clearInterval(this.heartbeatInterval);\n this.heartbeatInterval = null;\n }\n }\n\n /**\n * Parse duration string to milliseconds\n */\n private parseDuration(duration: string): number {\n const match = duration.match(/^(\\d+)(ms|s|m|h)?$/);\n if (!match) return 5000;\n\n const value = parseInt(match[1]!, 10);\n const unit = match[2] ?? 's';\n\n switch (unit) {\n case 'ms':\n return value;\n case 's':\n return value * 1000;\n case 'm':\n return value * 60 * 1000;\n case 'h':\n return value * 60 * 60 * 1000;\n default:\n return value * 1000;\n }\n }\n}\n\n/**\n * Create a WebSocket pipe\n */\nexport function createWebSocketPipe(options: WebSocketPipeOptions): WebSocketPipe {\n return new WebSocketPipe(options);\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@furlow/pipes",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "External integration pipes for FURLOW bot framework",
5
5
  "keywords": [
6
6
  "furlow",
@@ -62,7 +62,7 @@
62
62
  "chokidar": "^3.5.0",
63
63
  "mqtt": "^5.3.0",
64
64
  "ws": "^8.16.0",
65
- "@furlow/core": "1.0.1"
65
+ "@furlow/core": "1.0.3"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@types/better-sqlite3": "^7.6.8",