UNPKG

@ipcom/asterisk-ari

Version:

JavaScript client for Asterisk REST Interface.

1,499 lines (1,483 loc) 148 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // node_modules/exponential-backoff/dist/options.js var require_options = __commonJS({ "node_modules/exponential-backoff/dist/options.js"(exports2) { "use strict"; var __assign = exports2 && exports2.__assign || function() { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports2, "__esModule", { value: true }); var defaultOptions = { delayFirstAttempt: false, jitter: "none", maxDelay: Infinity, numOfAttempts: 10, retry: function() { return true; }, startingDelay: 100, timeMultiple: 2 }; function getSanitizedOptions(options) { var sanitized = __assign(__assign({}, defaultOptions), options); if (sanitized.numOfAttempts < 1) { sanitized.numOfAttempts = 1; } return sanitized; } exports2.getSanitizedOptions = getSanitizedOptions; } }); // node_modules/exponential-backoff/dist/jitter/full/full.jitter.js var require_full_jitter = __commonJS({ "node_modules/exponential-backoff/dist/jitter/full/full.jitter.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); function fullJitter(delay) { var jitteredDelay = Math.random() * delay; return Math.round(jitteredDelay); } exports2.fullJitter = fullJitter; } }); // node_modules/exponential-backoff/dist/jitter/no/no.jitter.js var require_no_jitter = __commonJS({ "node_modules/exponential-backoff/dist/jitter/no/no.jitter.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); function noJitter(delay) { return delay; } exports2.noJitter = noJitter; } }); // node_modules/exponential-backoff/dist/jitter/jitter.factory.js var require_jitter_factory = __commonJS({ "node_modules/exponential-backoff/dist/jitter/jitter.factory.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); var full_jitter_1 = require_full_jitter(); var no_jitter_1 = require_no_jitter(); function JitterFactory(options) { switch (options.jitter) { case "full": return full_jitter_1.fullJitter; case "none": default: return no_jitter_1.noJitter; } } exports2.JitterFactory = JitterFactory; } }); // node_modules/exponential-backoff/dist/delay/delay.base.js var require_delay_base = __commonJS({ "node_modules/exponential-backoff/dist/delay/delay.base.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); var jitter_factory_1 = require_jitter_factory(); var Delay = ( /** @class */ function() { function Delay2(options) { this.options = options; this.attempt = 0; } Delay2.prototype.apply = function() { var _this = this; return new Promise(function(resolve) { return setTimeout(resolve, _this.jitteredDelay); }); }; Delay2.prototype.setAttemptNumber = function(attempt) { this.attempt = attempt; }; Object.defineProperty(Delay2.prototype, "jitteredDelay", { get: function() { var jitter = jitter_factory_1.JitterFactory(this.options); return jitter(this.delay); }, enumerable: true, configurable: true }); Object.defineProperty(Delay2.prototype, "delay", { get: function() { var constant = this.options.startingDelay; var base = this.options.timeMultiple; var power = this.numOfDelayedAttempts; var delay = constant * Math.pow(base, power); return Math.min(delay, this.options.maxDelay); }, enumerable: true, configurable: true }); Object.defineProperty(Delay2.prototype, "numOfDelayedAttempts", { get: function() { return this.attempt; }, enumerable: true, configurable: true }); return Delay2; }() ); exports2.Delay = Delay; } }); // node_modules/exponential-backoff/dist/delay/skip-first/skip-first.delay.js var require_skip_first_delay = __commonJS({ "node_modules/exponential-backoff/dist/delay/skip-first/skip-first.delay.js"(exports2) { "use strict"; var __extends = exports2 && exports2.__extends || /* @__PURE__ */ function() { var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d2, b2) { d2.__proto__ = b2; } || function(d2, b2) { for (var p in b2) if (b2.hasOwnProperty(p)) d2[p] = b2[p]; }; return extendStatics(d, b); }; return function(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; }(); var __awaiter = exports2 && exports2.__awaiter || function(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function(resolve) { resolve(value); }); } return new (P || (P = Promise))(function(resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = exports2 && exports2.__generator || function(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function(v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports2, "__esModule", { value: true }); var delay_base_1 = require_delay_base(); var SkipFirstDelay = ( /** @class */ function(_super) { __extends(SkipFirstDelay2, _super); function SkipFirstDelay2() { return _super !== null && _super.apply(this, arguments) || this; } SkipFirstDelay2.prototype.apply = function() { return __awaiter(this, void 0, void 0, function() { return __generator(this, function(_a) { return [2, this.isFirstAttempt ? true : _super.prototype.apply.call(this)]; }); }); }; Object.defineProperty(SkipFirstDelay2.prototype, "isFirstAttempt", { get: function() { return this.attempt === 0; }, enumerable: true, configurable: true }); Object.defineProperty(SkipFirstDelay2.prototype, "numOfDelayedAttempts", { get: function() { return this.attempt - 1; }, enumerable: true, configurable: true }); return SkipFirstDelay2; }(delay_base_1.Delay) ); exports2.SkipFirstDelay = SkipFirstDelay; } }); // node_modules/exponential-backoff/dist/delay/always/always.delay.js var require_always_delay = __commonJS({ "node_modules/exponential-backoff/dist/delay/always/always.delay.js"(exports2) { "use strict"; var __extends = exports2 && exports2.__extends || /* @__PURE__ */ function() { var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d2, b2) { d2.__proto__ = b2; } || function(d2, b2) { for (var p in b2) if (b2.hasOwnProperty(p)) d2[p] = b2[p]; }; return extendStatics(d, b); }; return function(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; }(); Object.defineProperty(exports2, "__esModule", { value: true }); var delay_base_1 = require_delay_base(); var AlwaysDelay = ( /** @class */ function(_super) { __extends(AlwaysDelay2, _super); function AlwaysDelay2() { return _super !== null && _super.apply(this, arguments) || this; } return AlwaysDelay2; }(delay_base_1.Delay) ); exports2.AlwaysDelay = AlwaysDelay; } }); // node_modules/exponential-backoff/dist/delay/delay.factory.js var require_delay_factory = __commonJS({ "node_modules/exponential-backoff/dist/delay/delay.factory.js"(exports2) { "use strict"; Object.defineProperty(exports2, "__esModule", { value: true }); var skip_first_delay_1 = require_skip_first_delay(); var always_delay_1 = require_always_delay(); function DelayFactory(options, attempt) { var delay = initDelayClass(options); delay.setAttemptNumber(attempt); return delay; } exports2.DelayFactory = DelayFactory; function initDelayClass(options) { if (!options.delayFirstAttempt) { return new skip_first_delay_1.SkipFirstDelay(options); } return new always_delay_1.AlwaysDelay(options); } } }); // node_modules/exponential-backoff/dist/backoff.js var require_backoff = __commonJS({ "node_modules/exponential-backoff/dist/backoff.js"(exports2) { "use strict"; var __awaiter = exports2 && exports2.__awaiter || function(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function(resolve) { resolve(value); }); } return new (P || (P = Promise))(function(resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = exports2 && exports2.__generator || function(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function(v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports2, "__esModule", { value: true }); var options_1 = require_options(); var delay_factory_1 = require_delay_factory(); function backOff2(request, options) { if (options === void 0) { options = {}; } return __awaiter(this, void 0, void 0, function() { var sanitizedOptions, backOff3; return __generator(this, function(_a) { switch (_a.label) { case 0: sanitizedOptions = options_1.getSanitizedOptions(options); backOff3 = new BackOff(request, sanitizedOptions); return [4, backOff3.execute()]; case 1: return [2, _a.sent()]; } }); }); } exports2.backOff = backOff2; var BackOff = ( /** @class */ function() { function BackOff2(request, options) { this.request = request; this.options = options; this.attemptNumber = 0; } BackOff2.prototype.execute = function() { return __awaiter(this, void 0, void 0, function() { var e_1, shouldRetry; return __generator(this, function(_a) { switch (_a.label) { case 0: if (!!this.attemptLimitReached) return [3, 7]; _a.label = 1; case 1: _a.trys.push([1, 4, , 6]); return [4, this.applyDelay()]; case 2: _a.sent(); return [4, this.request()]; case 3: return [2, _a.sent()]; case 4: e_1 = _a.sent(); this.attemptNumber++; return [4, this.options.retry(e_1, this.attemptNumber)]; case 5: shouldRetry = _a.sent(); if (!shouldRetry || this.attemptLimitReached) { throw e_1; } return [3, 6]; case 6: return [3, 0]; case 7: throw new Error("Something went wrong."); } }); }); }; Object.defineProperty(BackOff2.prototype, "attemptLimitReached", { get: function() { return this.attemptNumber >= this.options.numOfAttempts; }, enumerable: true, configurable: true }); BackOff2.prototype.applyDelay = function() { return __awaiter(this, void 0, void 0, function() { var delay; return __generator(this, function(_a) { switch (_a.label) { case 0: delay = delay_factory_1.DelayFactory(this.options, this.attemptNumber); return [4, delay.apply()]; case 1: _a.sent(); return [ 2 /*return*/ ]; } }); }); }; return BackOff2; }() ); } }); // src/index.ts var src_exports = {}; __export(src_exports, { Applications: () => Applications, AriClient: () => AriClient, Asterisk: () => Asterisk, BridgeInstance: () => BridgeInstance, Bridges: () => Bridges, ChannelInstance: () => ChannelInstance, Channels: () => Channels, Endpoints: () => Endpoints, PlaybackInstance: () => PlaybackInstance, Playbacks: () => Playbacks, Sounds: () => Sounds }); module.exports = __toCommonJS(src_exports); // src/ari-client/baseClient.ts var import_axios = __toESM(require("axios"), 1); var HTTPError = class extends Error { constructor(message, status, method, url) { super(message); this.status = status; this.method = method; this.url = url; this.name = "HTTPError"; } }; var BaseClient = class { /** * Creates a new BaseClient instance. * * @param {string} baseUrl - The base URL for the API * @param {string} username - Username for authentication * @param {string} password - Password for authentication * @param {boolean} [secure=false] - Whether to use secure connections (HTTPS/WSS) * @param {number} [timeout=5000] - Request timeout in milliseconds * @throws {Error} If the base URL format is invalid */ constructor(baseUrl, username, password, secure = false, timeout = 5e3) { this.baseUrl = baseUrl; this.username = username; this.password = password; this.secure = secure; if (!/^https?:\/\/.+/.test(baseUrl)) { throw new Error( "Invalid base URL. It must start with http:// or https://" ); } this.client = import_axios.default.create({ baseURL: baseUrl, auth: { username, password }, timeout, headers: { "Content-Type": "application/json" } }); this.addInterceptors(); } client; /** * Gets the base URL of the client. */ getBaseUrl() { return this.baseUrl; } /** * Gets the configured credentials including security settings. * Used by WebSocketClient to determine authentication method. */ getCredentials() { return { baseUrl: this.baseUrl, username: this.username, password: this.password, secure: this.secure }; } /** * Adds request and response interceptors to the Axios instance. */ addInterceptors() { this.client.interceptors.request.use( (config) => { return config; }, (error) => { const message = this.getErrorMessage(error); console.error("[Request Error]", message); return Promise.reject(new HTTPError(message)); } ); this.client.interceptors.response.use( (response) => { return response; }, (error) => { if ((0, import_axios.isAxiosError)(error)) { const status = error.response?.status ?? 0; const method = error.config?.method?.toUpperCase() ?? "UNKNOWN"; const url = error.config?.url ?? "unknown-url"; const message2 = error.response?.data?.message || error.message || "Unknown error"; if (status === 404) { console.warn(`[404] Not Found: ${url}`); } else if (status >= 500) { console.error(`[${status}] Server Error: ${url}`); } else if (status > 0) { console.warn(`[${status}] ${method} ${url}: ${message2}`); } else { console.error(`[Network] Request failed: ${message2}`); } throw new HTTPError(message2, status || void 0, method, url); } const message = this.getErrorMessage(error); console.error("[Unexpected Error]", message); throw new Error(message); } ); } /** * Executes a GET request. * * @param path - API endpoint path * @param config - Optional Axios request configuration * @returns Promise with the response data */ async get(path, config) { try { const response = await this.client.get(path, config); return response.data; } catch (error) { throw this.handleRequestError(error); } } /** * Executes a POST request. * * @param path - API endpoint path * @param data - Request payload * @param config - Optional Axios request configuration * @returns Promise with the response data */ async post(path, data, config) { try { const response = await this.client.post(path, data, config); return response.data; } catch (error) { throw this.handleRequestError(error); } } /** * Executes a PUT request. * * @param path - API endpoint path * @param data - Request payload * @param config - Optional Axios request configuration * @returns Promise with the response data */ async put(path, data, config) { try { const response = await this.client.put(path, data, config); return response.data; } catch (error) { throw this.handleRequestError(error); } } /** * Executes a DELETE request. * * @param path - API endpoint path * @param config - Optional Axios request configuration * @returns Promise with the response data */ async delete(path, config) { try { const response = await this.client.delete(path, config); return response.data; } catch (error) { throw this.handleRequestError(error); } } /** * Handles and formats error messages from various error types. */ getErrorMessage(error) { if ((0, import_axios.isAxiosError)(error)) { return error.response?.data?.message || error.message || "HTTP Error"; } if (error instanceof Error) { return error.message; } return "An unknown error occurred"; } /** * Handles errors from HTTP requests. */ handleRequestError(error) { const message = this.getErrorMessage(error); if ((0, import_axios.isAxiosError)(error)) { throw new HTTPError( message, error.response?.status, error.config?.method?.toUpperCase(), error.config?.url ); } throw new Error(message); } /** * Sets custom headers for the client instance. */ setHeaders(headers) { this.client.defaults.headers.common = { ...this.client.defaults.headers.common, ...headers }; } /** * Gets the current request timeout setting. */ getTimeout() { return this.client.defaults.timeout || 5e3; } /** * Updates the request timeout setting. */ setTimeout(timeout) { this.client.defaults.timeout = timeout; } }; // src/ari-client/resources/applications.ts var Applications = class { constructor(client) { this.client = client; } /** * Lists all applications. * * @returns A promise that resolves to an array of Application objects. * @throws {Error} If the API response is not an array. */ async list() { const applications = await this.client.get("/applications"); if (!Array.isArray(applications)) { throw new Error("Resposta da API /applications n\xE3o \xE9 um array."); } return applications; } /** * Retrieves details of a specific application. * * @param appName - The name of the application to retrieve details for. * @returns A promise that resolves to an ApplicationDetails object. * @throws {Error} If there's an error fetching the application details. */ async getDetails(appName) { try { return await this.client.get( `/applications/${appName}` ); } catch (error) { console.error(`Erro ao obter detalhes do aplicativo ${appName}:`, error); throw error; } } /** * Sends a message to a specific application. * * @param appName - The name of the application to send the message to. * @param body - The message to be sent, containing an event and optional data. * @returns A promise that resolves when the message is successfully sent. */ async sendMessage(appName, body) { await this.client.post(`/applications/${appName}/messages`, body); } }; // src/ari-client/resources/asterisk.ts function toQueryParams(options) { return new URLSearchParams( Object.entries(options).filter(([, value]) => value !== void 0).map(([key, value]) => [key, String(value)]) ).toString(); } var Asterisk = class { constructor(client) { this.client = client; } async ping() { return this.client.get("/asterisk/ping"); } /** * Retrieves information about the Asterisk server. */ async get() { return this.client.get("/asterisk/info"); } /** * Lists all loaded modules in the Asterisk server. */ async list() { return this.client.get("/asterisk/modules"); } /** * Manages a specific module in the Asterisk server. * * @param moduleName - The name of the module to manage. * @param action - The action to perform on the module: "load", "unload", or "reload". * @returns A promise that resolves when the action is completed successfully. * @throws {Error} Throws an error if the HTTP method or action is invalid. */ async manage(moduleName, action) { const url = `/asterisk/modules/${moduleName}`; switch (action) { case "load": await this.client.post(`${url}?action=load`); break; case "unload": await this.client.delete(url); break; case "reload": await this.client.put(url, {}); break; default: throw new Error(`A\xE7\xE3o inv\xE1lida: ${action}`); } } /** * Retrieves all configured logging channels. */ async listLoggingChannels() { return this.client.get("/asterisk/logging"); } /** * Adds or removes a log channel in the Asterisk server. */ async manageLogChannel(logChannelName, action, configuration) { const queryParams = toQueryParams(configuration || {}); return this.client.post( `/asterisk/logging/${logChannelName}?action=${encodeURIComponent(action)}&${queryParams}` ); } /** * Retrieves the value of a global variable. */ async getGlobalVariable(variableName) { return this.client.get( `/asterisk/variables?variable=${encodeURIComponent(variableName)}` ); } /** * Sets a global variable. */ async setGlobalVariable(variableName, value) { return this.client.post( `/asterisk/variables?variable=${encodeURIComponent(variableName)}&value=${encodeURIComponent(value)}` ); } }; // src/ari-client/resources/bridges.ts var import_events = require("events"); var import_axios2 = require("axios"); // src/ari-client/utils.ts function toQueryParams2(options) { return new URLSearchParams( Object.entries(options).filter(([, value]) => value !== void 0).map(([key, value]) => [key, value]) ).toString(); } // src/ari-client/resources/bridges.ts var getErrorMessage = (error) => { if ((0, import_axios2.isAxiosError)(error)) { return error.response?.data?.message || error.message || "Um erro do axios ocorreu"; } if (error instanceof Error) { return error.message; } return "Um erro desconhecido ocorreu"; }; var BridgeInstance = class { /** * Creates a new BridgeInstance. * * @param client - The AriClient instance for making API calls. * @param baseClient - The BaseClient instance for making HTTP requests. * @param bridgeId - Optional. The ID of the bridge. If not provided, a new ID will be generated. */ constructor(client, baseClient, bridgeId) { this.client = client; this.baseClient = baseClient; this.id = bridgeId || `bridge-${Date.now()}`; } eventEmitter = new import_events.EventEmitter(); listenersMap = /* @__PURE__ */ new Map(); // 🔹 Guarda listeners para remoção posterior bridgeData = null; id; /** * Registers a listener for specific bridge events. * * @param event - The type of event to listen for. * @param listener - The callback function to be called when the event occurs. */ /** * Registers a listener for specific bridge events. * * This method allows you to attach an event listener to the bridge instance for a specific event type. * When the specified event occurs, the provided listener function will be called with the event data. * * @template T - The specific type of WebSocketEvent to listen for. * It receives the event data as its parameter. * @returns {void} * * @example * bridge.on('BridgeCreated', (event) => { * * }); * @param event * @param listener */ on(event, listener) { if (!event) { throw new Error("Event type is required"); } const existingListeners = this.listenersMap.get(event) || []; if (existingListeners.includes(listener)) { console.warn( `Listener j\xE1 registrado para evento ${event}, reutilizando.` ); return; } const wrappedListener = (data) => { if ("bridge" in data && data.bridge?.id === this.id) { listener(data); } }; this.eventEmitter.on(event, wrappedListener); if (!this.listenersMap.has(event)) { this.listenersMap.set(event, []); } this.listenersMap.get(event).push(wrappedListener); } /** * Registers a one-time listener for specific bridge events. * * @param event - The type of event to listen for. * @param listener - The callback function to be called when the event occurs. */ once(event, listener) { if (!event) { throw new Error("Event type is required"); } const eventKey = `${event}-${this.id}`; const existingListeners = this.listenersMap.get(eventKey) || []; if (existingListeners.includes(listener)) { console.warn( `One-time listener j\xE1 registrado para evento ${eventKey}, reutilizando.` ); return; } const wrappedListener = (data) => { if ("bridge" in data && data.bridge?.id === this.id) { listener(data); this.off(event, wrappedListener); } }; this.eventEmitter.once(event, wrappedListener); if (!this.listenersMap.has(eventKey)) { this.listenersMap.set(eventKey, []); } this.listenersMap.get(eventKey).push(wrappedListener); } /** * Removes event listener(s) from the bridge. * * @param event - The type of event to remove listeners for. * @param listener - Optional. The specific listener to remove. If not provided, all listeners for the event will be removed. */ off(event, listener) { if (!event) { throw new Error("Event type is required"); } if (listener) { this.eventEmitter.off(event, listener); const storedListeners = this.listenersMap.get(event) || []; this.listenersMap.set( event, storedListeners.filter((l) => l !== listener) ); } else { this.eventEmitter.removeAllListeners(event); this.listenersMap.delete(event); } } /** * Cleans up the BridgeInstance, resetting its state and clearing resources. */ cleanup() { this.bridgeData = null; this.removeAllListeners(); this.listenersMap.clear(); console.log(`Bridge instance ${this.id} cleaned up`); } /** * Emits an event if it corresponds to the current bridge. * * @param event - The WebSocketEvent to emit. */ emitEvent(event) { if (!event) { console.warn("Invalid event received"); return; } if ("bridge" in event && event.bridge?.id === this.id) { this.eventEmitter.emit(event.type, event); } } /** * Removes all event listeners from this bridge instance. */ removeAllListeners() { console.log(`Removing all event listeners for bridge ${this.id}`); this.listenersMap.forEach((listeners, event) => { listeners.forEach((listener) => { this.eventEmitter.off( event, listener ); }); }); this.listenersMap.clear(); this.eventEmitter.removeAllListeners(); } /** * Retrieves the current details of the bridge. * * @returns A Promise that resolves to the Bridge object containing the current details. * @throws An error if the retrieval fails. */ async get() { try { if (!this.id) { throw new Error("No bridge associated with this instance"); } this.bridgeData = await this.baseClient.get( `/bridges/${this.id}` ); return this.bridgeData; } catch (error) { const message = getErrorMessage(error); console.error(`Error retrieving details for bridge ${this.id}:`, message); throw new Error(`Failed to get bridge details: ${message}`); } } /** * Adds channels to the bridge. * * @param request - The AddChannelRequest object containing the channels to add. * @throws An error if the operation fails. */ async add(request) { try { const queryParams = toQueryParams2({ channel: Array.isArray(request.channel) ? request.channel.join(",") : request.channel, ...request.role && { role: request.role } }); await this.baseClient.post( `/bridges/${this.id}/addChannel?${queryParams}` ); } catch (error) { const message = getErrorMessage(error); console.error(`Error adding channels to bridge ${this.id}:`, message); throw new Error(`Failed to add channels: ${message}`); } } /** * Removes channels from the bridge. * * @param request - The RemoveChannelRequest object containing the channels to remove. * @throws An error if the operation fails. */ async remove(request) { try { const queryParams = toQueryParams2({ channel: Array.isArray(request.channel) ? request.channel.join(",") : request.channel }); await this.baseClient.post( `/bridges/${this.id}/removeChannel?${queryParams}` ); } catch (error) { const message = getErrorMessage(error); console.error(`Error removing channels from bridge ${this.id}:`, message); throw new Error(`Failed to remove channels: ${message}`); } } /** * Plays media on the bridge. * * @param request - The PlayMediaRequest object containing the media details to play. * @returns A Promise that resolves to a BridgePlayback object. * @throws An error if the operation fails. */ async playMedia(request) { try { const queryParams = new URLSearchParams({ ...request.lang && { lang: request.lang }, ...request.offsetms && { offsetms: request.offsetms.toString() }, ...request.skipms && { skipms: request.skipms.toString() }, ...request.playbackId && { playbackId: request.playbackId } }).toString(); const result = await this.baseClient.post( `/bridges/${this.id}/play?${queryParams}`, { media: request.media } ); return result; } catch (error) { const message = getErrorMessage(error); console.error(`Error playing media on bridge ${this.id}:`, message); throw new Error(`Failed to play media: ${message}`); } } /** * Stops media playback on the bridge. * * @param playbackId - The ID of the playback to stop. * @throws An error if the operation fails. */ async stopPlayback(playbackId) { try { await this.baseClient.delete( `/bridges/${this.id}/play/${playbackId}` ); } catch (error) { const message = getErrorMessage(error); console.error(`Error stopping playback on bridge ${this.id}:`, message); throw new Error(`Failed to stop playback: ${message}`); } } /** * Sets the video source for the bridge. * * @param channelId - The ID of the channel to set as the video source. * @throws An error if the operation fails. */ async setVideoSource(channelId) { try { await this.baseClient.post( `/bridges/${this.id}/videoSource/${channelId}` ); } catch (error) { const message = getErrorMessage(error); console.error( `Error setting video source for bridge ${this.id}:`, message ); throw new Error(`Failed to set video source: ${message}`); } } /** * Removes the video source from the bridge. * * @throws An error if the operation fails. */ async clearVideoSource() { try { await this.baseClient.delete(`/bridges/${this.id}/videoSource`); } catch (error) { const message = getErrorMessage(error); console.error( `Error removing video source from bridge ${this.id}:`, message ); throw new Error(`Failed to remove video source: ${message}`); } } /** * Checks if the bridge has listeners for a specific event. * * @param event - The event type to check for listeners. * @returns A boolean indicating whether there are listeners for the event. */ hasListeners(event) { return this.eventEmitter.listenerCount(event) > 0; } /** * Retrieves the current bridge data without making an API call. * * @returns The current Bridge object or null if no data is available. */ getCurrentData() { return this.bridgeData; } }; var Bridges = class { constructor(baseClient, client) { this.baseClient = baseClient; this.client = client; } bridgeInstances = /* @__PURE__ */ new Map(); eventQueue = /* @__PURE__ */ new Map(); /** * Creates or retrieves a Bridge instance. * * This method manages the creation and retrieval of BridgeInstance objects. * If an ID is provided and an instance with that ID already exists, it returns the existing instance. * If an ID is provided but no instance exists, it creates a new instance with that ID. * If no ID is provided, it creates a new instance with a generated ID. * * @param {Object} params - The parameters for creating or retrieving a Bridge instance. * @param {string} [params.id] - Optional. The ID of the Bridge instance to create or retrieve. * * @returns {BridgeInstance} A BridgeInstance object, either newly created or retrieved from existing instances. * * @throws {Error} If there's an error in creating or retrieving the Bridge instance. */ Bridge({ id }) { try { if (!id) { const instance = new BridgeInstance(this.client, this.baseClient); this.bridgeInstances.set(instance.id, instance); return instance; } if (!this.bridgeInstances.has(id)) { const instance = new BridgeInstance(this.client, this.baseClient, id); this.bridgeInstances.set(id, instance); return instance; } return this.bridgeInstances.get(id); } catch (error) { const message = getErrorMessage(error); console.warn(`Error creating/retrieving bridge instance:`, message); throw new Error(`Failed to manage bridge instance: ${message}`); } } /** * Removes all bridge instances and cleans up their resources. * This method ensures proper cleanup of all bridges and their associated listeners. */ remove() { const bridgeIds = Array.from(this.bridgeInstances.keys()); for (const bridgeId of bridgeIds) { try { const instance = this.bridgeInstances.get(bridgeId); if (instance) { instance.cleanup(); this.bridgeInstances.delete(bridgeId); console.log(`Bridge instance ${bridgeId} removed and cleaned up`); } } catch (error) { console.error(`Error cleaning up bridge ${bridgeId}:`, error); } } this.bridgeInstances.clear(); console.log("All bridge instances have been removed and cleaned up"); } /** * Removes a bridge instance from the collection of managed bridges. * * This function removes the specified bridge instance, cleans up its event listeners, * and logs the removal. If the bridge instance doesn't exist, it logs a warning. * * @param {string} bridgeId - The unique identifier of the bridge instance to be removed. * @throws {Error} Throws an error if the bridgeId is not provided. * @returns {void} */ removeBridgeInstance(bridgeId) { if (!bridgeId) { throw new Error("Bridge ID is required"); } const instance = this.bridgeInstances.get(bridgeId); if (instance) { try { instance.cleanup(); this.bridgeInstances.delete(bridgeId); console.log(`Bridge instance ${bridgeId} removed from memory`); } catch (error) { console.error(`Error removing bridge instance ${bridgeId}:`, error); throw error; } } else { console.warn(`Attempt to remove non-existent instance: ${bridgeId}`); } } /** * Propagates a WebSocket event to a specific bridge instance. * * This function checks if the received event is valid and related to a bridge, * then emits the event to the corresponding bridge instance if it exists. * * @param {WebSocketEvent} event - The WebSocket event to be propagated. * This should be an object containing information about the event, * including the bridge ID and event type. * * @returns {void} * * @remarks * - If the event is invalid (null or undefined), a warning is logged and the function returns early. * - The function checks if the event is bridge-related and if the event contains a valid bridge ID. * - If a matching bridge instance is found, the event is emitted to that instance. * - If no matching bridge instance is found, a warning is logged. */ propagateEventToBridge(event) { if (!event || !("bridge" in event) || !event.bridge?.id) { console.warn("Invalid WebSocket event received"); return; } const key = `${event.type}-${event.bridge.id}`; const existing = this.eventQueue.get(key); if (existing) { clearTimeout(existing); } this.eventQueue.set( key, setTimeout(() => { const instance = this.bridgeInstances.get(event.bridge.id); if (instance) { instance.emitEvent(event); } else { console.warn( `No instance found for bridge ${event.bridge.id}. Event ignored.` ); } this.eventQueue.delete(key); }, 100) ); } /** * Performs a cleanup of the Bridges instance, clearing all event queues and removing all bridge instances. * * This method is responsible for: * 1. Clearing all pending timeouts in the event queue. * 2. Removing all bridge instances managed by this Bridges object. * * It should be called when the Bridges instance is no longer needed or before reinitializing * to ensure all resources are properly released. * * @returns {void} */ cleanup() { this.eventQueue.forEach((timeout) => clearTimeout(timeout)); this.eventQueue.clear(); this.remove(); } /** * Lists all active bridges in the system. * * This asynchronous function retrieves a list of all currently active bridges * by making a GET request to the "/bridges" endpoint using the base client. * * @returns {Promise<Bridge[]>} A promise that resolves to an array of Bridge objects. * Each Bridge object represents an active bridge in the system. * * @throws {Error} If there's an error in fetching the bridges or if the request fails. * * @example * try { * const bridges = await bridgesInstance.list(); * * } catch (error) { * console.error('Failed to fetch bridges:', error); * } */ async list() { return this.baseClient.get("/bridges"); } /** * Creates a new bridge in the system. * * This asynchronous function sends a POST request to create a new bridge * using the provided configuration details. * * @param request - The configuration details for creating the new bridge. * @param request.type - The type of bridge to create (e.g., 'mixing', 'holding'). * @param request.name - Optional. A custom name for the bridge. * @param request.bridgeId - Optional. A specific ID for the bridge. If not provided, one will be generated. * * @returns A Promise that resolves to a Bridge object representing the newly created bridge. * The Bridge object contains details such as id, technology, bridge_type, bridge_class, channels, etc. * * @throws Will throw an error if the bridge creation fails or if there's a network issue. */ async createBridge(request) { return this.baseClient.post("/bridges", request); } /** * Retrieves detailed information about a specific bridge. * * This asynchronous function fetches the complete details of a bridge * identified by its unique ID. It makes a GET request to the ARI endpoint * for the specified bridge. * * @param bridgeId - The unique identifier of the bridge to retrieve details for. * This should be a string that uniquely identifies the bridge in the system. * * @returns A Promise that resolves to a Bridge object containing all the details * of the specified bridge. This includes information such as the bridge's * ID, type, channels, and other relevant properties. * * @throws Will throw an error if the bridge cannot be found, if there's a network issue, * or if the server responds with an error. */ async get(bridgeId) { return this.baseClient.get(`/bridges/${bridgeId}`); } /** * Destroys (deletes) a specific bridge in the system. * * This asynchronous function sends a DELETE request to remove a bridge * identified by its unique ID. Once destroyed, the bridge and all its * associated resources are permanently removed from the system. * * @param bridgeId - The unique identifier of the bridge to be destroyed. * This should be a string that uniquely identifies the bridge in the system. * * @returns A Promise that resolves to void when the bridge is successfully destroyed. * If the operation is successful, the bridge no longer exists in the system. * * @throws Will throw an error if the bridge cannot be found, if there's a network issue, * or if the server responds with an error during the deletion