@ipcom/asterisk-ari
Version:
JavaScript client for Asterisk REST Interface.
1,499 lines (1,483 loc) • 148 kB
JavaScript
"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