{"version":3,"file":"index.cjs","sources":["../src/lib/BrokerError.js","../src/lib/onceMultiple.js","../src/lib/BrokerBase.js","../src/lib/RawRequest.js","../src/lib/BrokerClient.js","../src/lib/consoleLogger.js"],"sourcesContent":["// Copyright (c) 2019-2021 Zero Density Inc.\n//\n// This file is part of realityhub-api.\n//\n// realityhub-api is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License version 2, as published by\n// the Free Software Foundation.\n//\n// realityhub-api is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with realityhub-api. If not, see <https://www.gnu.org/licenses/>.\n\nexport default class BrokerError extends Error {}\n","// Copyright (c) 2019-2021 Zero Density Inc.\n//\n// This file is part of realityhub-api.\n//\n// realityhub-api is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License version 2, as published by\n// the Free Software Foundation.\n//\n// realityhub-api is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with realityhub-api. If not, see <https://www.gnu.org/licenses/>.\n\nclass TimeoutError extends Error {\n  constructor(params) {\n    super(params);\n    this.code = 'TIMEOUT';\n    this.name = this.constructor.name;\n  }\n}\n\nexport default function onceMultiple(target, eventNames, timeout = null) {\n  return new Promise((resolve, reject) => {\n    let timer;\n    let handler;\n    let removeListeners;\n\n    removeListeners = () => {\n      for (const eventName of eventNames) {\n        target.removeListener(eventName, handler);\n      }\n    };\n\n    handler = (...args) => {\n      removeListeners();\n      clearTimeout(timer);\n      resolve(...args);\n    };\n\n    for (const eventName of eventNames) {\n      target.once(eventName, handler);\n    }\n\n    if (timeout) {\n      timer = setTimeout(() => {\n        removeListeners();\n        reject(new TimeoutError('Timeout exceeded.'));\n      }, timeout);\n    }\n  });\n}\n","// Copyright (c) 2019-2021 Zero Density Inc.\n//\n// This file is part of realityhub-api.\n//\n// realityhub-api is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License version 2, as published by\n// the Free Software Foundation.\n//\n// realityhub-api is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with realityhub-api. If not, see <https://www.gnu.org/licenses/>.\n\nimport { v4 as uuid } from 'uuid';\nimport EventEmitter from 'events';\nimport BrokerError from './BrokerError.js';\nimport onceMultiple from './onceMultiple.js';\n\nconst DEFAULT_MAX_WS_PACKET_SIZE = 50 /*MB*/ * 1024 * 1024;\n\n/**\n * BrokerBase constructor\n * @param {object} params Parameters\n * @param {string} [params.moduleName] Module name\n * @param {number} [params.maxPacketSize] Maximum websocket packet size\n * @param {Logger} [params.logger] Logger instance\n */\nexport default class BrokerBase extends EventEmitter {\n  constructor(params) {\n    super();\n    this.moduleName = params.moduleName;\n    this.maxPacketSize = params.maxPacketSize || DEFAULT_MAX_WS_PACKET_SIZE;\n\n    this.logger = params.logger;\n\n    this.events = new Map();\n    this.apiHandlers = new Map();\n\n    this.messageTimeout = 2000;\n\n    this.overridenTimeout = NaN; // NaN = use the implementation\n\n    try {\n      if (typeof window != 'undefined' && typeof localStorage != 'undefined') {\n        this.overridenTimeout = Number(localStorage.getItem('BROKER_TIMEOUT')) || NaN;\n      } else if (process && process.env) {\n        this.overridenTimeout = Number(process.env.BROKER_TIMEOUT) || NaN;\n      }\n    } catch (ex) {}\n\n    if (this.overridenTimeout) {\n      const logger = this.logger || console;\n      logger.warn(`Broker Timeout is overriden to ${this.overridenTimeout} milliseconds!`);\n    }\n\n    let maxPacketSizeRead = DEFAULT_MAX_WS_PACKET_SIZE;\n\n    try {\n      if (typeof window != 'undefined' && typeof localStorage != 'undefined') {\n        maxPacketSizeRead ||= Number(localStorage.getItem('MAX_WS_PACKET_SIZE'));\n      } else if (process && process.env) {\n        maxPacketSizeRead ||= Number(process.env.MAX_WS_PACKET_SIZE);\n      }\n    } catch (ex) {\n      const logger = this.logger || console;\n      logger.error(`Cannot read maxPacketSize from either localStorage or env, defaulting to ${maxPacketSizeRead}`);\n    }\n\n    this.maxPacketSize = Math.max(this.maxPacketSize, maxPacketSizeRead);\n\n    if (this.maxPacketSize !== DEFAULT_MAX_WS_PACKET_SIZE) {\n      const logger = this.logger || console;\n      logger.log(`BrokerBase is created with maxPacketSize: ${this.maxPacketSize}`);\n    }\n\n    this.initProxy();\n  }\n\n  getMethodProxy(vendorName, moduleName, options) {\n    options = {\n      timeout: 2000,\n      excludedClients: [],\n      ...options,\n    };\n\n    return new Proxy(\n      {},\n      {\n        get: (_, methodName) => {\n          if (methodName === 'emit' && this.moduleName !== `${vendorName}.${moduleName}`) {\n            throw new Error('A module can only emit its own events.');\n          }\n\n          return (...args) => {\n            switch (methodName) {\n              case 'emit': {\n                this.emitMessage(args, vendorName, moduleName, options);\n                break;\n              }\n\n              case 'on': {\n                const eventName = args.shift();\n                const eventHandler = args.shift();\n\n                if (typeof eventName !== 'string') {\n                  throw new Error('eventName must be a string');\n                }\n\n                if (typeof eventHandler !== 'function') {\n                  throw new Error('eventHandler must be a function');\n                }\n\n                const fullyQualifiedName = `${vendorName}.${moduleName}.${eventName}`;\n\n                this.subscribeToAPIEvent(fullyQualifiedName, eventHandler).catch((err) => {\n                  console.error(`Couldn't subscribe to ${fullyQualifiedName}`);\n\n                  if (err.code !== 'TIMEOUT') {\n                    console.trace(err);\n                  }\n                });\n\n                break;\n              }\n\n              case 'once': {\n                const eventName = args.shift();\n                const eventHandler = args.shift();\n\n                /**\n                 * If the subscribed event is not emitted within the given timeout then the\n                 * event handler will be removed automatically to prevent memory leak.\n                 * If a timeout is not provided by caller than a default timeout of 5 minutes is set.\n                 */\n                const timeout = args.shift() || 60 * 1000 * 5;\n\n                if (typeof eventName !== 'string') {\n                  throw new Error('eventName must be a string');\n                }\n\n                if (typeof eventHandler !== 'function') {\n                  throw new Error('eventHandler must be a function');\n                }\n\n                if (typeof timeout !== 'number' || isNaN(timeout)) {\n                  throw new Error('timeout must be a number');\n                }\n\n                const fullyQualifiedName = `${vendorName}.${moduleName}.${eventName}`;\n\n                this.subscribeToAPIEvent(fullyQualifiedName, eventHandler, {\n                  once: true,\n                }).catch((err) => {\n                  console.error(`Couldn't subscribe to ${fullyQualifiedName}`);\n\n                  if (err.code !== 'TIMEOUT') {\n                    console.trace(err);\n                  }\n                });\n                break;\n              }\n\n              case 'off': {\n                const eventName = args.shift();\n                const eventHandler = args.shift();\n                const fullyQualifiedName = `${vendorName}.${moduleName}.${eventName}`;\n\n                if (typeof eventName !== 'string') {\n                  throw new Error('eventName must be a string');\n                }\n\n                if (eventHandler && typeof eventHandler !== 'function') {\n                  throw new Error('eventHandler must be a function');\n                }\n\n                this.unsubscribeFromAPIEvent(fullyQualifiedName, eventHandler).catch((err) => {\n                  console.error(`Couldn't unsubscribe from ${fullyQualifiedName}`);\n\n                  if (err.code !== 'TIMEOUT') {\n                    console.trace(err);\n                  }\n                });\n\n                break;\n              }\n\n              case 'callTimeout': {\n                const timeout = args[0];\n\n                if (typeof timeout !== 'number') {\n                  throw new Error('callTimeout: timeout is required.');\n                }\n\n                const clonedOptions = JSON.parse(JSON.stringify(options));\n                clonedOptions.timeout = timeout;\n                return this.getMethodProxy(vendorName, moduleName, clonedOptions);\n              }\n\n              case 'excludeClients': {\n                const excludedClients = args[0] || [];\n\n                if (!(excludedClients instanceof Array)) {\n                  throw new Error('excludedClients requires 1 parameter: an array of strings');\n                }\n\n                const clonedOptions = JSON.parse(JSON.stringify(options));\n                clonedOptions.excludedClients = clonedOptions.excludedClients.concat(excludedClients);\n                return this.getMethodProxy(vendorName, moduleName, clonedOptions);\n              }\n\n              default: {\n                return this.sendMessage({\n                  data: args,\n                  timeout: options.timeout,\n                  type: `${vendorName}.${moduleName}.${methodName}`,\n                  targetModuleName: `${vendorName}.${moduleName}`,\n                  excludedClients: options.excludedClients,\n                });\n              }\n            }\n          };\n        },\n        set: (_, methodName, handler) => {\n          if (this.moduleName !== `${vendorName}.${moduleName}`) {\n            throw new Error('Cannot register methods to other modules.');\n          }\n\n          if (typeof handler !== 'function') {\n            throw new Error('Handler must be a function.');\n          }\n\n          if (['emit', 'on', 'off'].includes(methodName)) {\n            throw new Error(`${methodName} is a reserved method name.`);\n          }\n\n          return this.registerAPIHandler(methodName, handler);\n        },\n      }\n    );\n  }\n\n  /**\n   * Initializes the Proxy object.\n   * @private\n   */\n  initProxy() {\n    // These nested proxies allow us to get vendorName, moduleName and methodName.\n    // e.g. const pong = await this.api.hub.core.ping();\n    this.api = new Proxy(\n      {},\n      {\n        get: (_, vendorName) => {\n          return new Proxy(\n            {},\n            {\n              get: (_, moduleName) => {\n                return this.getMethodProxy(vendorName, moduleName);\n              },\n              set: (_, moduleName, api) => {\n                if (this.moduleName !== `${vendorName}.${moduleName}`) {\n                  throw new Error('Cannot register methods to other modules.');\n                }\n\n                if (typeof api !== 'object') {\n                  throw new Error('API must be set to an object.');\n                }\n\n                for (const [methodName, handler] of Object.entries(api)) {\n                  if (typeof handler !== 'function') {\n                    throw new Error('Handler must be a function.');\n                  }\n\n                  if (['emit', 'on', 'off'].includes(methodName)) {\n                    throw new Error(`${methodName} is a reserved method name.`);\n                  }\n\n                  this.registerAPIHandler(methodName, handler);\n                }\n\n                return true;\n              },\n            }\n          );\n        },\n        set: function () {\n          console.warn('Module name and method name are required.');\n          return false;\n        },\n      }\n    );\n  }\n\n  /**\n   * Send a response message through `socket`.\n   * @private\n   * @param {WebSocket} socket Target socket.\n   * @param {object} message The message to respond.\n   * @param {boolean} success Whether the request was successfully processed or not.\n   * @param {array} [data] Additional payload\n   * @param {boolean} [relayedMessage=false]\n   * @returns {Promise.<array, Error>}\n   */\n  sendResponse(socket, message, success, data = [], relayedMessage = false) {\n    if (!socket) return;\n\n    const { id: requestId, moduleName: targetModuleName, timeout, instigatorId } = message;\n    const websocketMessage = {\n      type: 'response',\n      targetModuleName,\n      instigatorId,\n      requestId,\n      timeout,\n      success,\n      data,\n    };\n\n    if (relayedMessage) {\n      websocketMessage.moduleName = message.targetModuleName;\n    }\n\n    return this.sendMessage(websocketMessage, socket, relayedMessage);\n  }\n\n  /**\n   * Registers an API request handler.\n   * @param {string} messageType Message type.\n   * @param {function} messageHandler Handler function.\n   * @returns {boolean} `false` if a handler has already been assigned to the `messageType`.\n   */\n  registerAPIHandler(messageType, messageHandler) {\n    messageType = `${this.moduleName}.${messageType}`;\n\n    if (this.apiHandlers.has(messageType)) return false;\n\n    this.apiHandlers.set(messageType, {\n      relay: false,\n      messageHandler,\n    });\n\n    return true;\n  }\n\n  /**\n   * Subscribe to an API event.\n   * @param {string} eventName Fully qualified event name.\n   * @param {function} eventHandler A function which will be called when the event is received.\n   * @param {object} [options] Options\n   * @param {boolean} [options.sendMessage=true] If set to `true`, it will send a subscription message over WebSocket.\n   * Otherwise the message will only be registered internally.\n   * @param {boolean} [options.once=false] If true then the handler will be invoked only once and it won't be invoked for\n   * the future events that are emitted.\n   * @returns {Promise.<array, Error>}\n   */\n  subscribeToAPIEvent(eventName, eventHandler, options) {\n    options = {\n      sendMessage: true,\n      once: false,\n      ...options,\n    };\n\n    // Add handler to handlers map\n    const handlerArray = this.events.get(eventName) || [];\n    handlerArray.push({ eventHandler, once: options.once });\n    this.events.set(eventName, handlerArray);\n\n    // Send a subscription message over WebSocket\n    if (options.sendMessage) {\n      const targetModuleName = eventName.split('.').slice(0, 2).join('.');\n\n      return this.sendMessage({\n        type: 'subscribe',\n        eventName,\n        targetModuleName,\n      });\n    }\n  }\n\n  /**\n   * Unsubscribe from an API event.\n   * @param {string} eventName Fully qualified event name.\n   * @param {function} [eventHandler] A previously registered handler function. All handlers of the event will\n   * be removed unless `eventHandler` is provided.\n   * @param {boolean} [sendMessage=true] Will send an unsubscription request when set to `true`.\n   * @returns {Promise.<array, Error>}\n   */\n  unsubscribeFromAPIEvent(eventName, eventHandler, sendMessage = true) {\n    if (eventHandler) {\n      const handlerArray = (this.events.get(eventName) || []).filter((entry) => entry.eventHandler !== eventHandler);\n      this.events.set(eventName, handlerArray);\n    } else {\n      this.events.delete(eventName);\n    }\n\n    if (sendMessage) {\n      const targetModuleName = eventName.split('.').slice(0, 2).join('.');\n\n      return this.sendMessage({\n        type: 'unsubscribe',\n        eventName,\n        targetModuleName,\n      });\n    }\n  }\n\n  /**\n   * Send an API message through a socket.\n   * @async\n   * @private\n   * @param {object} message Message object. `time`, `id`, `moduleName` and `data` keys will be added\n   * to the message object. Unlike other mentioned fields `data` will not get overridden when provided.\n   * @param {object} socket Socket instance.\n   * @param {boolean} [relayedMessage=false]\n   * @returns {Promise.<array, Error>}\n   */\n  async sendMessage(message, socket, relayedMessage = false) {\n    message.id = uuid();\n\n    if (!relayedMessage) {\n      message.moduleName = this.moduleName;\n    }\n\n    message.time = new Date().valueOf();\n    const packet = JSON.stringify(message);\n\n    if (packet.length > this.maxPacketSize) {\n      this.logger.trace(new Error('MAX_WS_PACKET_SIZE'));\n    }\n\n    socket.send(packet);\n\n    if (!['event', 'response'].includes(message.type)) {\n      let responseMessage;\n\n      try {\n        responseMessage = await onceMultiple(\n          this,\n          [`response::${message.id}`],\n          this.overridenTimeout || message.timeout || this.messageTimeout\n        );\n      } catch (ex) {\n        const logger = this.logger || console;\n        logger.debug(`${this.moduleName} failed to send message ${message.type} to ${message.targetModuleName || ''}`);\n        return;\n      }\n\n      if (!responseMessage) return;\n\n      if (responseMessage.success) {\n        return responseMessage.data;\n      } else {\n        let errorMessage = `${message.moduleName}'s \"${message.type}\" request has failed.`;\n\n        if (responseMessage.data instanceof Array && responseMessage.data.length && responseMessage.data[0].error) {\n          errorMessage = responseMessage.data[0].error;\n        }\n\n        this.logger.error(errorMessage);\n        throw new BrokerError(errorMessage);\n      }\n    }\n  }\n\n  /**\n   * Send a ping request.\n   * @param {string} targetModuleName\n   * @private\n   */\n  ping(targetModuleName) {\n    return this.sendMessage({ type: `${targetModuleName}.ping` });\n  }\n\n  /**\n   * @private\n   * @param {array} args\n   * @param {string} vendorName\n   * @param {string} moduleName\n   */\n  emitMessage(args, vendorName, moduleName, options = {}) {\n    const eventName = args.shift();\n\n    if (typeof eventName !== 'string') {\n      throw new Error('eventName must be a string');\n    }\n\n    const fullyQualifiedName = `${vendorName}.${moduleName}.${eventName}`;\n\n    this.sendMessage({\n      type: 'event',\n      eventName: fullyQualifiedName,\n      data: args,\n      excludedClients: options.excludedClients || [],\n    }).catch((err) => {\n      console.error(`Couldn't emit ${fullyQualifiedName}`);\n\n      if (err.code !== 'TIMEOUT') {\n        console.trace(err);\n      }\n    });\n  }\n\n  destroy() {\n    this.removeAllListeners();\n  }\n}\n","// Copyright (c) 2019-2021 Zero Density Inc.\n//\n// This file is part of realityhub-api.\n//\n// realityhub-api is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License version 2, as published by\n// the Free Software Foundation.\n//\n// realityhub-api is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with realityhub-api. If not, see <https://www.gnu.org/licenses/>.\n\nexport default class RawRequest {\n  setCallback(callback) {\n    if (typeof callback !== 'function') {\n      throw new Error('callback must be a function.');\n    }\n\n    this.callback = callback;\n  }\n\n  setAncillaryData(ancillaryData) {\n    this.ancillaryData = ancillaryData || {};\n  }\n\n  getAncillaryData() {\n    return this.ancillaryData || {};\n  }\n\n  call(...args) {\n    if (this.callback) {\n      return this.callback(...args);\n    }\n  }\n}\n","// Copyright (c) 2019-2021 Zero Density Inc.\n//\n// This file is part of realityhub-api.\n//\n// realityhub-api is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License version 2, as published by\n// the Free Software Foundation.\n//\n// realityhub-api is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with realityhub-api. If not, see <https://www.gnu.org/licenses/>.\n\nimport { v4 as uuid } from 'uuid';\nimport BrokerBase from './BrokerBase.js';\nimport BrokerError from './BrokerError.js';\nimport RawRequest from './RawRequest.js';\nimport consoleLogger from './consoleLogger.js';\nimport onceMultiple from './onceMultiple.js';\nimport WebSocket from 'ws';\n\nconst WS = typeof window !== 'undefined' ? window.WebSocket : WebSocket;\n\nexport default class BrokerClient extends BrokerBase {\n  /**\n   * BrokerClient constructor\n   * @param {object} params Parameters\n   * @param {string} params.webSocketURL WebSocket URL\n   * @param {string} [params.moduleName] Module name\n   * @param {number} [params.maxPacketSize] Maximum websocket packet size\n   * @param {Logger} [params.logger] Logger instance\n   * @param {boolean} [params.isDuplicate] [Private property, used internally]\n   * @param {boolean} [params.parent] [Private property, used internally]\n   */\n  constructor(params = {}) {\n    super(params);\n\n    this.setMaxListeners(20);\n\n    for (const func of [this.onOpen, this.onClose, this.onError, this.connect, this.onSocketMessage]) {\n      const name = func.name;\n      this[name] = func.bind(this);\n    }\n\n    this.logger = params.logger || consoleLogger(this.moduleName, { silent: true });\n\n    this.isDuplicate = params.isDuplicate;\n    this.parent = params.parent;\n    this.ssl = params.ssl;\n    this.duplicates = new Set();\n\n    // moduleName of the server (will be set when we receive a ping message)\n    this.serverModuleName = null;\n\n    /**\n     * A set of module names\n     * Each time registerHandlersToRemote is called, the `remote` is added to this set.\n     * BrokerClient uses the list of the remote endpoints for re-registering after a\n     * reconnect.\n     * @type {Set<string>}\n     */\n    this.registrars = new Set();\n\n    this.webSocketURL = params.webSocketURL;\n    this.connected = false;\n\n    if (this.isDuplicate) {\n      this.connected = this.isConnected();\n    }\n  }\n\n  /**\n   * Returns a `Promise` that will resolve once a `connect` event is emitted.\n   * If BrokerClient is already connected then it will resolve in the next\n   * event loop.\n   * @returns {Promise}\n   */\n  getConnectPromise() {\n    return new Promise((resolve) => {\n      const looper = setInterval(() => {\n        if (this.connected) {\n          clearInterval(looper);\n          resolve();\n        }\n      }, 0);\n    });\n  }\n\n  forceReconnect() {\n    this.removeSocketListeners();\n    this.socket = null;\n    this.connect(this.connectOptions);\n  }\n\n  /**\n   * Connect to server.\n   * @param {object} options Options\n   * @param {string} options.host Hostname or IP of the target server.\n   * @param {number} options.port WebSocket port of the target server.\n   */\n  connect(options) {\n    if (this.isDuplicate) return;\n\n    this.connectOptions = options;\n    let url;\n\n    if (!options) {\n      url = new URL(location.href);\n    } else {\n      url = { hostname: options.host, port: options.port };\n    }\n\n    const scheme = url.protocol === 'https:' || this.ssl ? 'wss' : 'ws';\n\n    const webSocketURL = url.port\n      ? `${scheme}://${url.hostname}:${url.port}${this.webSocketURL}`\n      : `${scheme}://${url.hostname}${this.webSocketURL}`;\n\n    this.logger.info(`BrokerClient is connecting to ${webSocketURL}`);\n\n    if (typeof process !== 'undefined' && process.versions != null && process.versions.node != null) {\n      const NODE_TLS_REJECT_UNAUTHORIZED = process.env.NODE_TLS_REJECT_UNAUTHORIZED;\n      process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;\n      this.socket = new WS(`${webSocketURL}?module=true`);\n      process.env.NODE_TLS_REJECT_UNAUTHORIZED = NODE_TLS_REJECT_UNAUTHORIZED;\n    } else {\n      this.socket = new WS(webSocketURL);\n    }\n\n    this.addSocketListeners();\n  }\n\n  /**\n   * Duplicates a BrokerClient in order to share the same WebSocket.\n   * @param {object} params Parameters\n   * @param {string} params.moduleName Module name of the duplicate BrokerClient (a duplicate\n   * can have a different name than its parent)\n   * @returns {BrokerClient}\n   */\n  duplicate(params) {\n    const { moduleName } = params;\n    let duplicates;\n    let duplicate;\n\n    if (this.isDuplicate) {\n      duplicates = this.parent.duplicates;\n      duplicate = new BrokerClient({\n        parent: this.parent,\n        isDuplicate: true,\n        webSocketURL: this.webSocketURL,\n        logger: this.logger,\n        moduleName,\n      });\n    } else {\n      duplicates = this.duplicates;\n      duplicate = new BrokerClient({\n        parent: this,\n        isDuplicate: true,\n        webSocketURL: this.webSocketURL,\n        logger: this.logger,\n        moduleName,\n      });\n    }\n\n    duplicate.on('destroy', () => duplicates.delete(duplicate));\n    duplicates.add(duplicate);\n\n    duplicate.ping().catch((ex) => {\n      console.error(`Failed to send ping`);\n      console.trace(ex.message);\n    });\n\n    return duplicate;\n  }\n\n  /**\n   * Returns `true` if WebSocket connection is established.\n   * @returns {boolean}\n   */\n  isConnected() {\n    if (this.isDuplicate) {\n      return this.parent.isConnected();\n    }\n\n    return this.connected;\n  }\n\n  /**\n   * Sends a ping message to server.\n   * @private\n   */\n  ping() {\n    return this.sendMessage({ type: 'ping' });\n  }\n\n  /**\n   * Returns the WebSocket instance.\n   * @private\n   */\n  getSocket() {\n    return this.socket;\n  }\n\n  async onSocketMessage(event) {\n    try {\n      await this.handleMessage(event.data, this.socket);\n    } catch (ex) {\n      console.trace(ex);\n    }\n  }\n\n  /**\n   * Handles incoming messages.\n   * @param {string} rawMessage Raw message\n   * @async\n   * @private\n   */\n  async handleMessage(rawMessage) {\n    let message;\n\n    try {\n      message = JSON.parse(rawMessage);\n      const socket = this.isDuplicate ? this.parent.getSocket() : this.socket;\n\n      switch (message.type) {\n        case 'response': {\n          this.emit(`response::${message.requestId}`, message);\n\n          // Send the response to other duplicates (if we are parent)\n          if (!this.isDuplicate) {\n            for (const duplicate of this.duplicates) {\n              duplicate.handleMessage(rawMessage);\n            }\n          }\n\n          break;\n        }\n\n        case 'event': {\n          // Run previously registered event handlers\n          for (const [subscribedEvent, entries] of this.events.entries()) {\n            if (subscribedEvent === message.eventName) {\n              for (const entry of entries) {\n                try {\n                  entry.eventHandler(...message.data);\n                } catch (ex) {\n                  this.logger.warn(ex);\n                } finally {\n                  if (entry.once) {\n                    this.unsubscribeFromAPIEvent(subscribedEvent, entry.eventHandler);\n                  }\n                }\n              }\n            }\n          }\n\n          // Send the event to other duplicates (if we are parent)\n          if (!this.isDuplicate) {\n            for (const duplicate of this.duplicates) {\n              duplicate.handleMessage(rawMessage);\n            }\n          }\n\n          break;\n        }\n\n        case 'subscribe': {\n          const [vendor, moduleName, ...rest] = message.eventName.split('.');\n          const eventName = rest.join('.');\n          const targetModuleName = [vendor, moduleName].join('.');\n\n          if (this.moduleName === targetModuleName) {\n            this.emit('subscribe', { eventName });\n            await this.sendResponse(socket, message, true);\n          } else {\n            // Check if the target is one of the duplicates (if we are parent)\n            if (!this.isDuplicate) {\n              for (const duplicate of this.duplicates) {\n                if (duplicate.moduleName === targetModuleName) {\n                  duplicate.handleMessage(rawMessage);\n                  return;\n                }\n              }\n            }\n\n            await this.sendResponse(socket, message, false, [\n              {\n                error: `${message.eventName} sent to ${this.moduleName}. This is probably a mistake.`,\n              },\n            ]);\n          }\n\n          break;\n        }\n\n        case 'unsubscribe': {\n          const [vendor, moduleName, ...rest] = message.eventName.split('.');\n          const eventName = rest.join('.');\n          const targetModuleName = [vendor, moduleName].join('.');\n\n          if (this.moduleName === targetModuleName) {\n            this.emit('unsubscribe', { eventName });\n            await this.sendResponse(socket, message, true);\n          } else {\n            // Check if the target is one of the duplicates (if we are parent)\n            if (!this.isDuplicate) {\n              for (const duplicate of this.duplicates) {\n                if (duplicate.moduleName === targetModuleName) {\n                  duplicate.handleMessage(rawMessage);\n                  return;\n                }\n              }\n            }\n\n            await this.sendResponse(socket, message, false, [\n              {\n                error: `${message.eventName} sent to ${this.moduleName}. This is probably a mistake.`,\n              },\n            ]);\n          }\n\n          break;\n        }\n\n        case 'ping': {\n          this.serverModuleName = message.moduleName;\n\n          if (!this.isDuplicate && message.targetModuleName !== this.moduleName) {\n            for (const duplicate of this.duplicates) {\n              if (duplicate.moduleName === message.targetModuleName) {\n                duplicate.handleMessage(rawMessage);\n                return;\n              }\n            }\n          }\n\n          await Promise.all([\n            this.sendResponse(socket, message, true),\n            this.resubscribeModuleEvents(),\n            this.subscribeToAPIEvent(`${message.moduleName}.moduleconnect`, ({ moduleName }) => {\n              this.emit('moduleconnect', { moduleName });\n              this.resubscribeModuleEvents();\n            }),\n            this.subscribeToAPIEvent(`${message.moduleName}.moduledisconnect`, ({ moduleName }) => {\n              this.emit('moduledisconnect', { moduleName });\n            }),\n          ]);\n\n          break;\n        }\n\n        default: {\n          if (!this.apiHandlers.has(message.type)) {\n            await this.sendResponse(socket, message, false, [\n              {\n                error: `There is no handler registered for this type of message: ${message.type}`,\n              },\n            ]);\n            return;\n          }\n\n          const { messageHandler, relay } = this.apiHandlers.get(message.type);\n\n          try {\n            let responseMessage = await messageHandler(...message.data);\n\n            if (responseMessage instanceof RawRequest) {\n              const rawRequest = responseMessage;\n              rawRequest.setAncillaryData({\n                ...message.ancillaryData,\n                caller: {\n                  moduleName: message.moduleName,\n                },\n              });\n              responseMessage = await rawRequest.call(...message.data);\n            }\n\n            await this.sendResponse(socket, message, true, responseMessage, relay);\n          } catch (ex) {\n            if (ex instanceof BrokerError) {\n              this.logger.error(ex.message);\n              await this.sendResponse(socket, message, false, [{ error: ex.message }], relay);\n              return;\n            }\n\n            this.logger.trace(ex);\n            await this.sendResponse(socket, message, false, [{ error: 'ERROR' }], relay);\n          }\n\n          break;\n        }\n      }\n    } catch (ex) {\n      if (ex.code === 'TIMEOUT') {\n        console.warn('Message timed out.');\n        console.log(message);\n        return;\n      }\n\n      console.trace(ex);\n    }\n  }\n\n  /**\n   * Sends a message.\n   * @async\n   * @param {object} message\n   * @returns {Promise.<Array, Error>}\n   */\n  async sendMessage(message) {\n    const id = uuid();\n    const socket = this.isDuplicate ? this.parent.getSocket() : this.socket;\n    const webSocketMessage = Object.assign({}, message, {\n      id,\n      time: new Date().valueOf(),\n      moduleName: this.moduleName,\n    });\n\n    if (socket.readyState !== WS.OPEN) {\n      try {\n        await onceMultiple(this, ['connect'], webSocketMessage.timeout || this.messageTimeout);\n      } catch (ex) {\n        console.error(`Timeout: Socket is not ready`);\n        throw ex;\n      }\n    }\n\n    let ret;\n\n    try {\n      ret = super.sendMessage(webSocketMessage, socket);\n    } catch (ex) {\n      console.error(`BrokerBase::sendMessage throwed an exception`);\n      throw ex;\n    }\n\n    return ret;\n  }\n\n  /**\n   * @private\n   */\n  addSocketListeners() {\n    if (this.isDuplicate) return;\n\n    this.socket.addEventListener('open', this.onOpen);\n    this.socket.addEventListener('message', this.onSocketMessage);\n    this.socket.addEventListener('error', this.onError);\n    this.socket.addEventListener('close', this.onClose);\n  }\n\n  removeSocketListeners() {\n    if (!this.socket) return;\n\n    this.socket.removeEventListener('open', this.onOpen);\n    this.socket.removeEventListener('message', this.onSocketMessage);\n    this.socket.removeEventListener('error', this.onError);\n    this.socket.removeEventListener('close', this.onClose);\n  }\n\n  /**\n   * @private\n   */\n  resubscribeModuleEvents() {\n    for (const eventName of this.events.keys()) {\n      const moduleName = eventName.split('.').slice(0, 2).join('.');\n\n      this.sendMessage({\n        type: 'subscribe',\n        eventName,\n        targetModuleName: moduleName,\n      }).catch(new Function());\n    }\n  }\n\n  /**\n   * @async\n   * @private\n   */\n  async onOpen() {\n    try {\n      this.connected = true;\n      this.emit('connect');\n\n      if (this.isDuplicate) {\n        await this.ping();\n      }\n\n      for (const registrar of this.registrars) {\n        await this.registerHandlersToRemote(registrar);\n      }\n\n      if (!this.isDuplicate) {\n        for (const duplicate of this.duplicates) {\n          duplicate.onOpen();\n        }\n      }\n    } catch (ex) {\n      console.trace(ex.message);\n    }\n  }\n\n  /**\n   * @private\n   * @param {ErrorEvent} err\n   */\n  onError(err) {\n    if (err.error instanceof Error) {\n      err = err.error.message;\n    }\n\n    if (!this.isDuplicate) {\n      this.logger.trace(err);\n    }\n\n    if (!this.isDuplicate) {\n      this.logger.warn(`${this.moduleName} couldn't connect to WebSocket server.`);\n\n      for (const duplicate of this.duplicates) {\n        duplicate.onError(err);\n      }\n    }\n  }\n\n  /**\n   * @private\n   */\n  onClose(e) {\n    if (this.connected) {\n      this.connected = false;\n      this.emit('disconnect', e);\n      this.events.delete(`${this.serverModuleName}.moduleconnect`);\n\n      if (!this.isDuplicate) {\n        for (const duplicate of this.duplicates) {\n          duplicate.onClose();\n        }\n      }\n    } else {\n      this.emit('reconnectfailure');\n    }\n\n    setTimeout(() => this.connect(this.connectOptions), 1000);\n  }\n\n  /**\n   * Send the local API handler list to the `targetModuleName` so it will relay\n   * messages targeting those handlers.\n   * @param {string} targetModuleName Module name of the API Server\n   * @returns {Promise.<Array, Error>}\n   */\n  registerHandlersToRemote(targetModuleName) {\n    this.registrars.add(targetModuleName);\n\n    return this.sendMessage({\n      type: `${targetModuleName}.registerAPIHandlers`,\n      data: Array.from(this.apiHandlers.keys()),\n      targetModuleName,\n    });\n  }\n\n  /**\n   * Send a message to all remote endpoints telling them not to relay any messages\n   * to this module anymore.\n   * @async\n   * @returns {Promise.<Array, Error>[]}\n   */\n  async deregisterHandlersFromRemotes() {\n    try {\n      const promises = [];\n\n      for (const registrar of this.registrars) {\n        promises.push(\n          this.sendMessage({\n            type: `${targetModuleName}.deregisterAPIHandlers`,\n            data: Array.from(this.apiHandlers.keys()),\n            targetModuleName: registrar,\n          })\n        );\n      }\n\n      return Promise.all(promises);\n    } catch (ex) {\n      console.trace(ex.message);\n    }\n  }\n\n  /**\n   * Unsubscribes from all subscriptions.\n   * @returns {Promise.<Array, Error>[]}\n   */\n  unsubscribeFromAllEvents() {\n    const promises = [];\n\n    for (const eventName of this.events.keys()) {\n      promises.push(this.unsubscribeFromAPIEvent(eventName));\n    }\n\n    return Promise.all(promises);\n  }\n\n  /**\n   * Performs cleanup.\n   * @async\n   */\n  async destroy() {\n    try {\n      if (this.isDuplicate) {\n        await Promise.all([\n          this.deregisterHandlersFromRemotes(),\n          this.unsubscribeFromAllEvents(),\n          this.sendMessage({\n            type: 'event',\n            eventName: `${this.moduleName}.disconnect`,\n            targetModuleName: this.serverModuleName,\n          }),\n        ]);\n        this.emit('destroy');\n      } else {\n        this.socket.close();\n        this.removeSocketListeners();\n        super.destroy();\n      }\n    } catch (ex) {\n      console.trace(ex.message);\n    }\n  }\n\n  /**\n   * Register a module's API handlers to RealityHub\n   * @async\n   * @param {Object.<string, function>} handlers Key will be registered to the API tree.\n   * @param {*} [context=null] Handlers' `this` will be set to this context.\n   * The value (function) will handle the API calls.\n   * @param {string} [remote='hub.core'] Remote\n   * @example\n   * // server.js\n   * brokerClient.registerAPIHandlers(this, {\n   *   addNumbers: function (number1, number2) {\n   *     return number1 + number2;\n   *   },\n   * }).catch((ex) => console.trace(ex));\n   *\n   * // client.js\n   * brokerClient.api.moduleVendor.moduleName.addNumber(3, 5)\n   *   .then((result) => {\n   *     // Will log 15\n   *     console.log(result);\n   *   })\n   *   .catch((ex) => console.trace(ex));\n   * @returns {Promise}\n   */\n  async registerAPIHandlers(handlers, context = null, remote = 'hub.core') {\n    for (const [handlerName, handler] of Object.entries(handlers)) {\n      this.registerAPIHandler(handlerName, handler.bind(context));\n    }\n\n    return this.registerHandlersToRemote(remote);\n  }\n\n  /**\n   * Third-party modules can use this method to initialize a BrokerClient\n   * and register themselves to RealityHub.\n   * @async\n   * @static\n   * @param {{ clientModuleName?: string, menuTitle?: string, moduleName: string, serverURL: string, webSocketURL?: string, hub: {host: string, port: number }}} params Parameters\n   * @param {string} [params.clientModuleName] Client Module Name (`<vendor>.<client module name>`)\n   * @param {string} [params.menuTitle] Menu Title\n   * @param {string} params.moduleName Backend Module Name (`<vendor>.<backend module name>`)\n   * @param {string} params.serverURL Your module has to serve your client files over HTTP or HTTPS.\n   * RealityHub will look for an `index.js` file in this path. This script file will be imported\n   * by RealityHub's `index.html` via a `<script type=\"module\">` tag. Relative paths in your scripts\n   * will be proxied by RealityHub.\n   * @param {string} [params.webSocketURL=\"/core\"] WebSocket URL to connect. RealityHub's API Server\n   * is serving at `/core` by default. *(Default: /core)*\n   * @param {{ host: string, port: number }} params.hub RealityHub connection parameters\n   * @param {string} params.hub.host RealityHub hostname or IP address\n   * @param {string} params.hub.port RealityHub port\n   * @returns {Promise<BrokerClient, Error>} A BrokerClient instance.\n   */\n  static async initModule(params) {\n    const { moduleName, serverURL, hub, webSocketURL = '/core', clientModuleName, menuTitle } = params;\n    const hubClient = new BrokerClient({ moduleName, webSocketURL });\n\n    hubClient.connect(hub);\n    await hubClient.getConnectPromise();\n    if (!serverURL) return hubClient;\n\n    await hubClient.api.hub.core.registerProxyURL({\n      moduleName,\n      serverURL,\n      clientModuleName,\n      menuTitle,\n    });\n    return hubClient;\n  }\n}\n","// Copyright (c) 2019-2021 Zero Density Inc.\n//\n// This file is part of realityhub-api.\n//\n// realityhub-api is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License version 2, as published by\n// the Free Software Foundation.\n//\n// realityhub-api is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with realityhub-api. If not, see <https://www.gnu.org/licenses/>.\n\nexport default function consoleLogger(moduleName, options = { silent: false }) {\n  const { silent } = options;\n\n  return ['log', 'info', 'warn', 'error', 'trace', 'debug'].reduce((o, item) => {\n    o[item] = (...args) => {\n      if (!silent) {\n        args = [\n          `NOTICE (${item}): Current module: ${moduleName} has no Logger object available, outputting to the console`,\n          ...args,\n        ];\n      }\n\n      if (['trace', 'debug'].includes(item)) {\n        for (const arg of args || []) {\n          console.log(arg);\n        }\n      }\n\n      return console[item](...args);\n    };\n    return o;\n  }, {});\n}\n"],"names":["BrokerError","Error","TimeoutError","constructor","params","super","this","code","name","onceMultiple","target","eventNames","timeout","Promise","resolve","reject","timer","handler","removeListeners","eventName","removeListener","args","clearTimeout","once","setTimeout","DEFAULT_MAX_WS_PACKET_SIZE","BrokerBase","EventEmitter","moduleName","maxPacketSize","logger","events","Map","apiHandlers","messageTimeout","overridenTimeout","NaN","window","localStorage","Number","getItem","process","env","BROKER_TIMEOUT","ex","console","warn","maxPacketSizeRead","MAX_WS_PACKET_SIZE","error","Math","max","log","initProxy","getMethodProxy","vendorName","options","excludedClients","Proxy","get","_","methodName","emitMessage","shift","eventHandler","fullyQualifiedName","subscribeToAPIEvent","catch","err","trace","isNaN","unsubscribeFromAPIEvent","clonedOptions","JSON","parse","stringify","Array","concat","sendMessage","data","type","targetModuleName","set","includes","registerAPIHandler","api","Object","entries","sendResponse","socket","message","success","relayedMessage","id","requestId","instigatorId","websocketMessage","messageType","messageHandler","has","relay","handlerArray","push","split","slice","join","filter","entry","delete","uuid","time","Date","valueOf","packet","length","send","responseMessage","debug","errorMessage","ping","destroy","removeAllListeners","RawRequest","setCallback","callback","setAncillaryData","ancillaryData","getAncillaryData","call","WS","WebSocket","BrokerClient","setMaxListeners","func","onOpen","onClose","onError","connect","onSocketMessage","bind","silent","reduce","o","item","arg","consoleLogger","isDuplicate","parent","ssl","duplicates","Set","serverModuleName","registrars","webSocketURL","connected","isConnected","getConnectPromise","looper","setInterval","clearInterval","forceReconnect","removeSocketListeners","connectOptions","url","hostname","host","port","URL","location","href","scheme","protocol","info","versions","node","NODE_TLS_REJECT_UNAUTHORIZED","addSocketListeners","duplicate","on","add","getSocket","event","handleMessage","rawMessage","emit","subscribedEvent","vendor","rest","all","resubscribeModuleEvents","rawRequest","caller","webSocketMessage","assign","readyState","OPEN","ret","addEventListener","removeEventListener","keys","Function","registrar","registerHandlersToRemote","e","from","deregisterHandlersFromRemotes","promises","unsubscribeFromAllEvents","close","registerAPIHandlers","handlers","context","remote","handlerName","initModule","serverURL","hub","clientModuleName","menuTitle","hubClient","core","registerProxyURL"],"mappings":"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"}