import * as bonjourFunc from "bonjour" ; import { CommunicationState , Configuration , Container , toLocalIsoString } from ".." ; /**
 * Node.js specific utility functions to be used by Coaty agent services.
 */ export class NodeUtils { param localConfigFile public static provideConfiguration ( localConfigFile = "app.config" ) { try { const path = require ( "path" ) ; if ( ! path . isAbsolute ( localConfigFile ) ) { localConfigFile = path . resolve ( "./" + localConfigFile ) ; } return require ( localConfigFile ) ; } catch ( e ) { const msg = `Couldn't resolve configuration file '${ localConfigFile }': ${ e }` ; throw new Error ( msg ) ; } } param configurationUrl public static provideConfigurationAsync ( configurationUrl ) { return new Promise < Configuration > ( ( resolve , reject ) => { const fetch = require ( "node-fetch" ) ; fetch ( configurationUrl ) . then ( response => resolve ( response . json ( ) ) ) . catch ( error => { reject ( new Error ( `Couldn't fetch configuration from '${ configurationUrl }': ${ error }` ) ) ; } ) ; } ) ; } param cleanup param cleanupAsync public static handleProcessTermination ( cleanup ? , cleanupAsync ? ) { process . on ( "uncaughtException" , err => { NodeUtils . logError ( err , "uncaughtException:" ) ; NodeUtils . logError ( err . stack , "       stacktrace:" ) ; process . exit ( 1 ) ; } ) ; process . on ( "SIGINT" , ( ) => { const prom = cleanupAsync ? cleanupAsync ( ) : Promise . resolve ( ) ; prom . then ( ( ) => process . exit ( ) ) . catch ( ( ) => process . exit ( 1 ) ) ; } ) ; process . on ( "exit" , code => { cleanup && cleanup ( ) ; NodeUtils . logInfo ( `Exiting with code: ${ code }` ) ; } ) ; } param container public static logCommunicationState ( container ) { container . communicationManager . observeCommunicationState ( ) . subscribe ( state => { NodeUtils . logInfo ( `[COM] ${ container . identity . name } ${ state !== CommunicationState . Online ? "offline" : "online" }` ) ; } ) ; } param message param isoDateString public static logInfo ( message , isoDateString ? ) { const date = isoDateString || toLocalIsoString ( new Date ( ) , true ) ; console . log ( `[${ date }] [info] ${ message }` ) ; } param error param message param isoDateString public static logError ( error , message ? , isoDateString ? ) { const date = isoDateString || toLocalIsoString ( new Date ( ) , true ) ; console . log ( `[${ date }] [error] ${ message || "" }${ message ? " " : "" }${ error }` ) ; } param message param eventName param eventDirection public static logEvent ( message , eventName ? , eventDirection = "In" ) { let output = eventName ? ( eventDirection === "In" ? "-> " : "<- " ) : "   " ; output += ( ( eventName || "" ) + " " . repeat ( 11 - ( eventName ? eventName . length : 0 ) ) ) ; output += "| " + message ; console . log ( output ) ; } /**
     * Gets the first IPv4 address of the specified network interface of the
     * local machine. If no network interface name is specified, the first IPv4
     * address of the first network interface that provides an IPv4 address is
     * returned. Returns `undefined` if no IPv4 address could be looked up.
     *
     * This function can only be used in a server-side environment (Node.js),
     * not in a browser runtime.
     */ public static getLocalIpV4Address ( networkInterface ? ) { return this . _getLocalIpAddress ( "IPv4" , networkInterface ) ; } /**
     * Gets the first IPv6 address of the specified network interface of the
     * local machine. If no network interface name is specified, the first IPv6
     * address of the first network interface that provides an IPv6 address is
     * returned. Returns `undefined` if no IPv6 address could be looked up.
     *
     * This function can only be used in a server-side environment (Node.js),
     * not in a browser runtime.
     */ public static getLocalIpV6Address ( networkInterface ? ) { return this . _getLocalIpAddress ( "IPv6" , networkInterface ) ; } private static _getLocalIpAddress ( version , networkInterface ? ) { const ifs = require ( "os" ) . networkInterfaces ( ) ; const addresses = Object . keys ( ifs ) . filter ( name => networkInterface ? name === networkInterface : true ) . map ( x => [ x , ifs [ x ] . filter ( y => y . family === version && ! y . internal ) [ 0 ] ] ) . filter ( x => x [ 1 ] ) . map ( x => x [ 1 ] . address ) ; if ( addresses . length === 0 ) { return undefined ; } return addresses [ 0 ] as string ; } } /**
 * Represents a multicast DNS service object as resolved by the `MulticastDnsDiscovery.findMulticastDnsService` and
 * `MulticastDnsDiscovery.publishMulticastDnsService` methods.
 *
 * Note: This interface just reexposes the members of the `bonjour.BaseService` interface defined
 * in the npm `bonjour` package.
 */ export interface MulticastDnsService extends bonjourFunc . BaseService { } /**
 * Provides static methods to publish and find multicast DNS services.
 */ export class MulticastDnsDiscovery { private static readonly bonjour = bonjourFunc ( ) ; param name param type param port param txtRecord param host public static publishMulticastDnsService ( name , type , port , txtRecord = { } , host ? ) { return new Promise < MulticastDnsService > ( ( resolve , reject ) => { const srv = MulticastDnsDiscovery . bonjour . publish ( { name : name , type : type , host : host || undefined , port : port , txt : txtRecord || { } , } ) ; srv . on ( "up" , ( ) => resolve ( srv ) ) ; srv . on ( "error" , error => reject ( error ) ) ; } ) ; } remarks param port param wsPort param name param type param host public static publishMqttBrokerService ( port ? , wsPort ? , name ? , type ? , host ? ) { return MulticastDnsDiscovery . publishMulticastDnsService ( name || "Coaty MQTT Broker" , type || "coaty-mqtt" , port || 1883 , { "ws-port" : ( wsPort || 9883 ) . toString ( ) } , host ) ; } remarks param path param realm param port param name param type param host public static publishWampRouterService ( path ? , realm ? , port ? , name ? , type ? , host ? ) { return MulticastDnsDiscovery . publishMulticastDnsService ( name || "Coaty WAMP Router" , type || "coaty-wamp" , port || 80 , { path : path || "/" , realm : realm || "coaty" } , host ) ; } /**
     * Unpublish all published multicast DNS services. Returns a promise that is resolved after all
     * services have been unpublished.
     *
     * This function can only be used in a server-side environment (Node.js),
     * not in a browser runtime.
     */ public static unpublishMulticastDnsServices ( ) { return new Promise < void > ( ( resolve , reject ) => { MulticastDnsDiscovery . bonjour . unpublishAll ( ( ) => { MulticastDnsDiscovery . bonjour . destroy ( ) ; resolve ( ) ; } ) ; } ) ; } param name param name param timeout public static findMulticastDnsService ( name , type , timeout = 2147483647 ) { return new Promise < any > ( ( resolve , reject ) => { let isResolved = false ; const timer = setTimeout ( ( ) => { if ( ! isResolved ) { isResolved = true ; browser . stop ( ) ; reject ( "mDNS timed out" ) ; } } , timeout ) ; const browser = MulticastDnsDiscovery . bonjour . find ( { type : type } , ( service ) => { if ( service . name === name ) { if ( ! isResolved ) { isResolved = true ; browser . stop ( ) ; clearTimeout ( timer ) ; resolve ( service ) ; } } } ) ; } ) ; } param timeout param name param type public static findMqttBrokerService ( timeout ? , name = "Coaty MQTT Broker" , type = "coaty-mqtt" ) { return MulticastDnsDiscovery . findMulticastDnsService ( name , type , timeout ) ; } param timeout param name param type public static findWampRouterService ( timeout ? , name = "Coaty WAMP Router" , type = "coaty-wamp" ) { return MulticastDnsDiscovery . findMulticastDnsService ( name , type , timeout ) ; } deprecated public static getLocalIpV4Address ( networkInterface ? ) { const ifs = require ( "os" ) . networkInterfaces ( ) ; const addresses = Object . keys ( ifs ) . filter ( name => networkInterface ? name === networkInterface : true ) . map ( x => [ x , ifs [ x ] . filter ( y => y . family === "IPv4" && ! y . internal ) [ 0 ] ] ) . filter ( x => x [ 1 ] ) . map ( x => x [ 1 ] . address ) ; if ( addresses . length === 0 ) { return undefined ; } return addresses [ 0 ] as string ; } }