@skyway-sdk/analytics-client
Version:
The official Next Generation JavaScript SDK for SkyWay
1,133 lines (1,075 loc) • 42.5 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __pow = Math.pow;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
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 __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// bundler/shims/process.js
var init_process = __esm({
"bundler/shims/process.js"() {
"use strict";
}
});
// ../../node_modules/.pnpm/isomorphic-ws@4.0.1_ws@8.18.2/node_modules/isomorphic-ws/browser.js
var require_browser = __commonJS({
"../../node_modules/.pnpm/isomorphic-ws@4.0.1_ws@8.18.2/node_modules/isomorphic-ws/browser.js"(exports, module) {
init_process();
var ws = null;
if (typeof WebSocket !== "undefined") {
ws = WebSocket;
} else if (typeof MozWebSocket !== "undefined") {
ws = MozWebSocket;
} else if (typeof global !== "undefined") {
ws = global.WebSocket || global.MozWebSocket;
} else if (typeof window !== "undefined") {
ws = window.WebSocket || window.MozWebSocket;
} else if (typeof self !== "undefined") {
ws = self.WebSocket || self.MozWebSocket;
}
module.exports = ws;
}
});
// src/index.ts
init_process();
// src/analyticsClient.ts
init_process();
// src/clientEvent.ts
init_process();
// ../../node_modules/.pnpm/uuid@9.0.1/node_modules/uuid/dist/esm-browser/index.js
init_process();
// ../../node_modules/.pnpm/uuid@9.0.1/node_modules/uuid/dist/esm-browser/rng.js
init_process();
var getRandomValues;
var rnds8 = new Uint8Array(16);
function rng() {
if (!getRandomValues) {
getRandomValues = typeof crypto !== "undefined" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto);
if (!getRandomValues) {
throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
}
}
return getRandomValues(rnds8);
}
// ../../node_modules/.pnpm/uuid@9.0.1/node_modules/uuid/dist/esm-browser/stringify.js
init_process();
var byteToHex = [];
for (let i = 0; i < 256; ++i) {
byteToHex.push((i + 256).toString(16).slice(1));
}
function unsafeStringify(arr, offset = 0) {
return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]];
}
// ../../node_modules/.pnpm/uuid@9.0.1/node_modules/uuid/dist/esm-browser/v4.js
init_process();
// ../../node_modules/.pnpm/uuid@9.0.1/node_modules/uuid/dist/esm-browser/native.js
init_process();
var randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
var native_default = {
randomUUID
};
// ../../node_modules/.pnpm/uuid@9.0.1/node_modules/uuid/dist/esm-browser/v4.js
function v4(options, buf, offset) {
if (native_default.randomUUID && !buf && !options) {
return native_default.randomUUID();
}
options = options || {};
const rnds = options.random || (options.rng || rng)();
rnds[6] = rnds[6] & 15 | 64;
rnds[8] = rnds[8] & 63 | 128;
if (buf) {
offset = offset || 0;
for (let i = 0; i < 16; ++i) {
buf[offset + i] = rnds[i];
}
return buf;
}
return unsafeStringify(rnds);
}
var v4_default = v4;
// src/clientEvent.ts
var ClientEvent = class {
constructor(type, payload) {
this.id = v4_default();
this.type = type;
this.payload = payload;
}
toJSON() {
return {
id: this.id,
type: this.type,
payload: this.payload
};
}
};
// src/payloadTypes.ts
init_process();
function isRecord(arg) {
if (typeof arg !== "object") return false;
if (arg === null) return false;
if (Array.isArray(arg)) return false;
return true;
}
function isOpenServerEventPayload(payload) {
if (!payload || typeof payload !== "object") return false;
if (!payload.statsRequest || typeof payload.statsRequest !== "object") return false;
if (!payload.statsRequest.intervalSec || typeof payload.statsRequest.intervalSec !== "number") return false;
if (!payload.statsRequest.types || !Array.isArray(payload.statsRequest.types)) return false;
for (const statsRequestType of payload.statsRequest.types) {
if (!statsRequestType.type || typeof statsRequestType.type !== "string") return false;
if (!statsRequestType.properties || !isRecord(statsRequestType.properties)) return false;
for (const key of Object.keys(statsRequestType.properties)) {
if (!("normalization" in statsRequestType.properties[key]) || typeof statsRequestType.properties[key].normalization !== "boolean")
return false;
if (!statsRequestType.properties[key].outputKey || typeof statsRequestType.properties[key].outputKey !== "string")
return false;
}
}
return true;
}
var AcknowledgeReason = ["invalidPayload", "unexpected"];
function isAcknowledgePayload(payload) {
if (!payload || typeof payload !== "object") return false;
if (typeof payload.eventId !== "string") return false;
if (typeof payload.ok !== "boolean") return false;
if (typeof payload.reason !== "undefined" && (typeof payload.reason !== "string" || !AcknowledgeReason.includes(payload.reason)))
return false;
return true;
}
// src/socket.ts
init_process();
var import_isomorphic_ws = __toESM(require_browser());
// src/utils/event.ts
init_process();
var Event = class {
constructor() {
this._listeners = /* @__PURE__ */ new Map();
this._listenerIndex = 0;
this.emit = (arg) => {
this._listeners.forEach((listener) => listener(arg));
};
this.removeAllListeners = () => {
this._listeners.clear();
};
this.addListener = (listener) => {
const id = this._listenerIndex;
this._listeners.set(id, listener);
this._listenerIndex++;
const removeListener = () => {
this._listeners.delete(id);
};
return { removeListener };
};
this.addOneTimeListener = (listener) => {
const off = this.addListener((arg) => {
off.removeListener();
listener(arg);
});
return off;
};
this.asPromise = (timeLimit) => new Promise((resolve, reject) => {
let removeListener = () => {
};
const timeout = timeLimit && setTimeout(() => {
reject("Event asPromise timeout");
removeListener();
}, timeLimit);
const off = this.addOneTimeListener((arg) => {
if (timeout) clearTimeout(timeout);
resolve(arg);
});
removeListener = off.removeListener;
});
}
};
// src/socket.ts
var ServerEventType = ["Open", "Acknowledge"];
var getReconnectWaitTime = (reconnectCount) => {
return (__pow(2, reconnectCount) + Math.random()) * 1e3;
};
var Socket = class {
constructor({ sessionEndpoint, token, logger, sdkVersion, contextId }) {
this._isOpen = false;
this._isClosed = false;
this._reconnectCount = 0;
this.connectionState = "connecting";
// コンストラクタ作成時点で繋ぎにいくので初期値はconnecting
this.onConnectionStateChanged = new Event();
this.onOpened = new Event();
this.onTokenExpired = new Event();
this.onEventReceived = new Event();
this.onConnectionFailed = new Event();
this._resendClientEvents = [];
this._sessionEndpoint = sessionEndpoint;
this._token = token;
this._logger = logger;
this._sdkVersion = sdkVersion;
this._contextId = contextId;
this._connect();
}
_setConnectionState(state) {
if (this.connectionState === state) return;
this._logger.debug(`connectionState changed : ${state}`);
this.connectionState = state;
this.onConnectionStateChanged.emit(state);
}
_connect() {
let ws;
try {
const subProtocol = `SkyWayAuthToken!${this._token}`;
const wsProperties = {
sdkPlatform: "js",
sdkVersion: this._sdkVersion,
contextId: this._contextId
};
const queryString = Object.entries(wsProperties).filter(([_, v]) => v !== void 0).map((pair) => pair.join("=")).join("&");
const wsURL = `${this._sessionEndpoint}?${queryString}`;
ws = new import_isomorphic_ws.default(wsURL, subProtocol);
this._logger.debug(`Connecting to analytics-logging-server: ${this._sessionEndpoint}`);
ws.onerror = (event) => {
this._logger.error("WebSocket error occurred", event.error);
ws.close(4202);
};
} catch (err) {
const error = err instanceof Error ? err : new Error();
this._logger.error("Failed to create WebSocket instance", error);
this.reconnect();
return;
}
ws.onopen = () => {
this._logger.debug("Connected to analytics-logging-server");
};
ws.onclose = (event) => {
const logMessage = "Close event fired: " + JSON.stringify({ code: event.code, reason: event.reason, type: event.type });
if (4100 <= event.code && event.code <= 4199 || event.code === 1009) {
this._logger.error(logMessage, new Error());
} else {
this._logger.debug(logMessage);
}
if (event.code !== 1e3 && event.code !== 1009 && !(4e3 <= event.code && event.code <= 4199)) {
if (4200 === event.code) {
this.onTokenExpired.emit();
} else {
this.reconnect();
}
return;
}
this._logger.debug("Closed the connection to analytics-logging-server");
this.onConnectionFailed.emit({ code: event.code, reason: event.reason });
this.close();
};
ws.onmessage = (event) => {
this._messageHandler(event.data);
};
this._ws = ws;
}
updateAuthToken(token) {
this._token = token;
}
reconnect() {
if (this._ws !== void 0) {
this._ws.close(4e3);
}
this._ws = void 0;
this._isOpen = false;
if (this._reconnectCount >= 5) {
this.onConnectionFailed.emit({});
this.close();
this._logger.error("Failed to reconnect for five times", new Error());
} else {
this._setConnectionState("reconnecting");
const waitTime = getReconnectWaitTime(this._reconnectCount);
this._reconnectTimer = setTimeout(() => {
this._connect();
this._reconnectCount++;
this._logger.debug(`Try to reconnect: count = ${this._reconnectCount}`);
}, waitTime);
}
}
close() {
this._isClosed = true;
this.destroy();
}
destroy() {
this._setConnectionState("closed");
this.onConnectionStateChanged.removeAllListeners();
this.onOpened.removeAllListeners();
this.onEventReceived.removeAllListeners();
this.onConnectionFailed.removeAllListeners();
if (this._reconnectTimer) {
clearTimeout(this._reconnectTimer);
}
if (this._ws !== void 0) {
this._ws.close(1e3);
}
}
send(clientEvent) {
return __async(this, null, function* () {
if (this._ws === void 0 || !this._isOpen || this._ws.readyState !== import_isomorphic_ws.default.OPEN) {
this._logger.debug("Try to reconnect because connection is lost");
this.resendAfterReconnect(clientEvent);
return;
}
const data = JSON.stringify(clientEvent.toJSON());
this._ws.send(data, (err) => {
if (err) {
this._logger.debug(`Try to reconnect because failed to send: ${err.message}`);
this.resendAfterReconnect(clientEvent);
return;
}
});
});
}
resendAfterReconnect(data) {
const isEventExist = this._resendClientEvents.some((event) => event.id === data.id);
if (!isEventExist) this._resendClientEvents.push(data);
if (this.connectionState !== "reconnecting") {
this.reconnect();
}
}
pushResendClientEventsQueue(data) {
this._resendClientEvents.push(data);
}
isClosed() {
return this._isClosed;
}
_messageHandler(data) {
if (typeof data !== "string") {
this._logger.error("Received invalid message: not string", new Error());
return;
}
let parsedData;
try {
parsedData = JSON.parse(data);
} catch (err) {
const error = err instanceof Error ? err : new Error();
this._logger.error("Received invalid message: parse error", error);
return;
}
if (!isServerEvent(parsedData)) {
this._logger.error(`Received invalid message: ${JSON.stringify(parsedData)}`, new Error());
return;
}
if (parsedData.type === "Open") {
if (!isOpenServerEventPayload(parsedData.payload)) {
this._logger.error(`Received invalid message: ${JSON.stringify(parsedData.payload)}`, new Error());
return;
}
this._logger.debug("Received a open event");
this._isOpen = true;
this._setConnectionState("connected");
if (this._reconnectCount !== 0) {
this._reconnectCount = 0;
this._logger.debug("Succeeded to reconnect");
}
if (this._resendClientEvents.length > 0) {
for (const event of this._resendClientEvents) {
if (this._ws === void 0 || !this._isOpen || this._ws.readyState !== import_isomorphic_ws.default.OPEN) {
this._logger.error(`Failed to resend event because connection lost after reconnect: ${event}`, new Error());
continue;
}
const data2 = JSON.stringify(event.toJSON());
this._ws.send(data2, (err) => {
if (err) {
this._logger.error(`Failed to resend event: ${event}`, err);
return;
}
this._logger.debug(`Succeed to resend ClientEvent: ${event}`);
});
}
this._logger.debug("Process of resending ClientEvents is completed");
this._resendClientEvents = [];
}
this.onOpened.emit(parsedData.payload);
} else {
this._logger.debug(`Received the event: ${parsedData.type}, payload: ${JSON.stringify(parsedData.payload)}`);
this.onEventReceived.emit(parsedData);
}
}
};
function isServerEvent(data) {
if (!data || typeof data !== "object") return false;
if (typeof data.type !== "string" || !ServerEventType.includes(data.type)) return false;
if (typeof data.id !== "string") return false;
if (data.payload && typeof data.payload !== "object") return false;
return true;
}
// src/utils/backoff.ts
init_process();
var BackOff = class {
/**20.4 sec {var sum=0;for(i=0;i<=8;i++){sum +=i ** 2 * 100}} */
constructor(props = {}) {
this.count = 0;
this.times = 8;
/**ms */
this.interval = 100;
/**ms */
this.jitter = 0;
Object.assign(this, props);
}
wait() {
return __async(this, null, function* () {
if (this.exceeded) {
return false;
}
const timeout = this.timeout;
this.count++;
yield new Promise((r) => setTimeout(r, timeout));
return true;
});
}
get timeout() {
const timeout = __pow(this.count, 2) * this.interval + __pow(this.count, 2) * this.jitter * Math.random();
return timeout;
}
get exceeded() {
return this.count >= this.times;
}
reset() {
this.count = 0;
}
stop() {
this.count = this.times;
}
};
// src/analyticsClient.ts
var ANALYTICS_LOGGING_SERVER_DOMAIN = "analytics-logging.skyway.ntt.com";
var API_VERSION = "v2";
var TIMEOUT_SEC = 5;
var _AnalyticsClient = class _AnalyticsClient {
constructor({ token, sdkVersion, contextId }, options) {
this.onConnectionStateChanged = new Event();
this.onConnectionFailed = new Event();
this.onAnalyticsNotEnabledError = new Event();
this._isClosed = false;
this._responseCallbacks = /* @__PURE__ */ new Map();
this._acknowledgeCallbacks = /* @__PURE__ */ new Map();
this._mediaDeviceVersion = /* @__PURE__ */ new Map();
this._encodingsVersion = /* @__PURE__ */ new Map();
this._preferredEncodingVersion = /* @__PURE__ */ new Map();
this._previousSubscriptionStats = /* @__PURE__ */ new Map();
this._statsRequest = {
// connect()時のopenServerEventPayload.statsRequest代入でそれぞれ値が入るが,一度初期値として定義しておく
intervalSec: 5,
types: []
};
this._pendingSdkLogs = [];
this._token = token;
this._newToken = void 0;
this._sdkVersion = sdkVersion;
this._contextId = contextId;
const defaultOptions = {
analyticsLoggingServerDomain: ANALYTICS_LOGGING_SERVER_DOMAIN,
secure: true,
logger: {
debug: (message, ...optionalParams) => {
console.debug(message, ...optionalParams);
},
warn: (message, ...optionalParams) => {
console.warn(message, ...optionalParams);
},
error: (error) => {
console.error(error);
}
}
};
this._options = Object.assign({}, defaultOptions, options != null ? options : {});
this._logger = this._options.logger;
this._logger.debug(`Created instance with the options: ${this._options}`);
this._sdkLogTimer = setInterval(() => {
if (this._pendingSdkLogs.length > 0) {
const logs = this._pendingSdkLogs.splice(0, this._pendingSdkLogs.length);
this.sendSdkLogReport(logs).catch((err) => {
this._logger.warn("sendSdkLogReport (interval) failed", err);
});
}
}, 5 * 1e3);
}
get connectionState() {
var _a, _b;
return (_b = (_a = this._socket) == null ? void 0 : _a.connectionState) != null ? _b : "closed";
}
connect() {
return __async(this, null, function* () {
const WSProtocol = this._options.secure ? "wss" : "ws";
const analyticsLoggingServerDomain = this._options.analyticsLoggingServerDomain || ANALYTICS_LOGGING_SERVER_DOMAIN;
this._socket = new Socket({
sessionEndpoint: `${WSProtocol}://${analyticsLoggingServerDomain}/${API_VERSION}/client/ws`,
contextId: this._contextId,
token: this._token,
logger: this._logger,
sdkVersion: this._sdkVersion
});
this._socket.onEventReceived.addListener((data) => {
try {
this._eventReceivedHandler(data);
} catch (error) {
this._logger.error("in _eventReceivedHandler", error);
}
});
this._socket.onConnectionFailed.addListener((data) => {
if (data.code === 4e3) {
this.onAnalyticsNotEnabledError.emit(data);
}
this.onConnectionFailed.emit();
this.dispose();
});
this._socket.onConnectionStateChanged.addListener((state) => {
var _a;
if (state === "closed" && !this.isClosed() && ((_a = this._socket) == null ? void 0 : _a.isClosed())) {
this._isClosed = true;
this.dispose();
}
this.onConnectionStateChanged.emit(state);
});
this._socket.onTokenExpired.addListener(() => {
void this._reconnectWithNewSkyWayAuthToken();
});
const openServerEventPayload = yield this._socket.onOpened.asPromise();
if (openServerEventPayload !== void 0) {
this._statsRequest = openServerEventPayload.statsRequest;
return;
} else {
this._logger.error("First time connection payload is undefined", new Error());
this.onConnectionFailed.emit();
return;
}
});
}
bufferOrSendSdkLog(log) {
const shouldImmediateSend = log.level === "warn" || log.level === "error";
this._pendingSdkLogs.push(log);
if (shouldImmediateSend || this._pendingSdkLogs.length >= _AnalyticsClient.MAX_PENDING_SDK_LOGS) {
const logsToSend = [...this._pendingSdkLogs];
this._pendingSdkLogs.length = 0;
this.sendSdkLogReport(logsToSend).catch((err) => {
this._logger.warn("sendSdkLogReport failed", err);
});
}
}
dispose() {
clearInterval(this._sdkLogTimer);
this._disconnect();
this._cleanupAnalyticsClientMaps();
}
setNewSkyWayAuthToken(token) {
if (this._socket !== void 0) {
this._newToken = token;
this._logger.debug("setNewSkyWayAuthToken is success");
}
}
cleanupOnUnpublished(publicationId) {
this._mediaDeviceVersion.delete(publicationId);
this._encodingsVersion.delete(publicationId);
}
cleanupOnUnsubscribed(subscriptionId) {
this._preferredEncodingVersion.delete(subscriptionId);
this._previousSubscriptionStats.delete(subscriptionId);
}
_disconnect() {
var _a;
(_a = this._socket) == null ? void 0 : _a.destroy();
this._socket = void 0;
this._responseCallbacks.clear();
this._acknowledgeCallbacks.clear();
}
sendMediaDeviceReport(report) {
return __async(this, null, function* () {
let currentMediaDeviceVersion = this._mediaDeviceVersion.get(report.publicationId);
if (currentMediaDeviceVersion === void 0) {
currentMediaDeviceVersion = 0;
} else {
currentMediaDeviceVersion++;
}
this._mediaDeviceVersion.set(report.publicationId, currentMediaDeviceVersion);
const payload = {
publicationId: report.publicationId,
mediaDeviceName: report.mediaDeviceName,
mediaDeviceVersion: currentMediaDeviceVersion,
mediaDeviceTrigger: report.mediaDeviceTrigger,
updatedAt: report.updatedAt
};
const clientEvent = new ClientEvent("MediaDeviceReport", payload);
yield this._sendClientEvent(clientEvent).catch((err) => {
this._logger.warn("_sendClientEvent in sendMediaDeviceReport is failed", err);
});
});
}
sendSdkLogReport(logs) {
return __async(this, null, function* () {
if (logs.length === 0) return;
const sdkLogs = logs.map((l) => ({
timestamp: l.timestamp,
level: l.level,
message: Array.isArray(l.message) ? l.message.map((m) => typeof m === "string" ? m : JSON.stringify(m)).join(",") : String(l.message)
}));
const clientEvent = new ClientEvent("SdkLog", {
sdkLogs,
contextId: this._contextId
});
yield this._sendClientEvent(clientEvent).catch((err) => {
this._logger.warn("_sendClientEvent in sendSdkLogReport is failed", err);
});
});
}
sendBindingRtcPeerConnectionToSubscription(bindingData) {
return __async(this, null, function* () {
const clientEvent = new ClientEvent("BindingRtcPeerConnectionToSubscription", bindingData);
yield this._sendClientEvent(clientEvent).catch((err) => {
this._logger.warn("_sendClientEvent in sendBindingRtcPeerConnectionToSubscription is failed", err);
});
});
}
/**
* RTCStatsReportにはcandidate-pair, local-candidate, remote-candidateが複数含まれる場合がある。
* 現在利用されているもののみを選出して返す。
*/
filterStatsReport(report) {
const connectedTransport = Array.from(report.values()).find(
(rtcStatsReportValue) => rtcStatsReportValue.type === "transport" && rtcStatsReportValue.dtlsState === "connected"
);
const candidatePairKeys = [];
if (connectedTransport) {
const nominatedCandidatePair = Array.from(report.values()).find(
(rtcStatsReportValue) => rtcStatsReportValue.type === "candidate-pair" && rtcStatsReportValue.nominated && rtcStatsReportValue.id === (connectedTransport == null ? void 0 : connectedTransport.selectedCandidatePairId)
);
if (nominatedCandidatePair) {
candidatePairKeys.push(
nominatedCandidatePair.id,
nominatedCandidatePair.localCandidateId,
nominatedCandidatePair.remoteCandidateId,
nominatedCandidatePair.transportId
);
}
} else {
const nominatedCandidatePair = Array.from(report.values()).find(
(rtcStatsReportValue) => rtcStatsReportValue.type === "candidate-pair" && rtcStatsReportValue.nominated && rtcStatsReportValue.selected
);
if (nominatedCandidatePair) {
candidatePairKeys.push(
nominatedCandidatePair.id,
nominatedCandidatePair.localCandidateId,
nominatedCandidatePair.remoteCandidateId,
nominatedCandidatePair.transportId
);
}
}
const filteredReport = /* @__PURE__ */ new Map();
const duplicatableTypes = ["candidate-pair", "local-candidate", "remote-candidate", "transport"];
for (const [key, rtcStatsReportValue] of report.entries()) {
if (duplicatableTypes.includes(rtcStatsReportValue.type)) {
if (candidatePairKeys.includes(rtcStatsReportValue.id)) {
filteredReport.set(key, rtcStatsReportValue);
}
} else {
filteredReport.set(key, rtcStatsReportValue);
}
}
return filteredReport;
}
bundleStatsReportByStatsType(report) {
const stats = {};
for (const v of report.values()) {
stats[v.type] = v;
}
return stats;
}
sendSubscriptionStatsReport(report, subscriptionParams) {
return __async(this, null, function* () {
var _a;
const previousSubscriptionStat = this._previousSubscriptionStats.get(subscriptionParams.subscriptionId);
this._previousSubscriptionStats.set(subscriptionParams.subscriptionId, {
stats: report,
createdAt: subscriptionParams.createdAt
});
if (previousSubscriptionStat === void 0) {
return;
}
const filteredPreviousSubscriptionStats = this.filterStatsReport(previousSubscriptionStat.stats);
const prevBundledSubscriptionStats = this.bundleStatsReportByStatsType(filteredPreviousSubscriptionStats);
const previousCreatedAt = previousSubscriptionStat.createdAt;
const duration = (subscriptionParams.createdAt - previousCreatedAt) / 1e3;
if (duration <= 0) {
throw new Error("duration must be greater than 0. also sendSubscriptionStatsReport was duplicated.");
}
const filteredStatsReport = this.filterStatsReport(report);
const bundledStatsReport = this.bundleStatsReportByStatsType(filteredStatsReport);
const subscriptionStats = {};
for (const { type, properties } of this._statsRequest.types) {
for (const [prop, { normalization: normRequired, outputKey, contentType }] of Object.entries(properties)) {
if (!contentType.includes(subscriptionParams.contentType)) {
continue;
}
const statsReport = bundledStatsReport[type];
if (statsReport === void 0 || statsReport[prop] === void 0) {
continue;
}
if (normRequired) {
const previousValue = (_a = prevBundledSubscriptionStats[type]) == null ? void 0 : _a[prop];
if (previousValue === void 0) {
this._logger.warn(`${type} in previous statsReport is undefined`);
continue;
}
const perSecondValue = (Number(statsReport[prop]) - Number(previousValue)) / duration;
subscriptionStats[type] = __spreadProps(__spreadValues({}, subscriptionStats[type]), {
[outputKey]: String(perSecondValue)
});
} else {
subscriptionStats[type] = __spreadProps(__spreadValues({}, subscriptionStats[type]), {
[outputKey]: String(statsReport[prop])
});
}
}
}
const payload = {
subscriptionId: subscriptionParams.subscriptionId,
stats: subscriptionStats,
role: subscriptionParams.role,
createdAt: subscriptionParams.createdAt
};
const clientEvent = new ClientEvent("SubscriptionStatsReport", payload);
yield this._sendClientEvent(clientEvent).catch((err) => {
this._logger.warn("_sendClientEvent in sendSubscriptionStatsReport is failed", err);
});
});
}
sendRtcPeerConnectionEventReport(report) {
return __async(this, null, function* () {
const clientEvent = new ClientEvent("RtcPeerConnectionEventReport", report);
yield this._sendClientEvent(clientEvent).catch((err) => {
this._logger.warn("_sendClientEvent in sendRtcPeerConnectionEventReport is failed", err);
});
});
}
sendPublicationUpdateEncodingsReport(report) {
return __async(this, null, function* () {
let currentEncodingsVersion = this._encodingsVersion.get(report.publicationId);
if (currentEncodingsVersion === void 0) {
currentEncodingsVersion = 0;
} else {
currentEncodingsVersion++;
}
this._encodingsVersion.set(report.publicationId, currentEncodingsVersion);
const payload = {
publicationId: report.publicationId,
encodings: report.encodings,
encodingsVersion: currentEncodingsVersion,
updatedAt: report.updatedAt
};
const clientEvent = new ClientEvent("PublicationUpdateEncodingsReport", payload);
yield this._sendClientEvent(clientEvent).catch((err) => {
this._logger.warn("_sendClientEvent in sendPublicationUpdateEncodingsReport is failed", err);
});
});
}
sendSubscriptionUpdatePreferredEncodingReport(report) {
return __async(this, null, function* () {
let currentPreferredEncodingVersion = this._preferredEncodingVersion.get(report.subscriptionId);
if (currentPreferredEncodingVersion === void 0) {
currentPreferredEncodingVersion = 0;
} else {
currentPreferredEncodingVersion++;
}
this._preferredEncodingVersion.set(report.subscriptionId, currentPreferredEncodingVersion);
const payload = {
subscriptionId: report.subscriptionId,
preferredEncodingIndex: report.preferredEncodingIndex,
preferredEncodingVersion: currentPreferredEncodingVersion,
updatedAt: report.updatedAt
};
const clientEvent = new ClientEvent("SubscriptionUpdatePreferredEncodingReport", payload);
yield this._sendClientEvent(clientEvent).catch((err) => {
this._logger.warn("_sendClientEvent in sendSubscriptionUpdatePreferredEncodingReport is failed", err);
});
});
}
sendJoinReport(report) {
return __async(this, null, function* () {
const clientEvent = new ClientEvent("JoinReport", report);
yield this._sendClientEvent(clientEvent).catch((err) => {
this._logger.warn("_sendClientEvent in sendJoinReport is failed", err);
});
});
}
_sendClientEvent(clientEvent) {
return __async(this, null, function* () {
return new Promise((resolve, reject) => __async(this, null, function* () {
if (this._socket === void 0 || this._socket.connectionState === "closed") {
reject(new Error("websocket is not connected"));
return;
}
if (this._socket.connectionState === "connecting") {
this._socket.pushResendClientEventsQueue(clientEvent);
this._setAcknowledgeCallback(clientEvent.id, (data) => __async(this, null, function* () {
if (data.ok) {
this._acknowledgeCallbacks.delete(clientEvent.id);
resolve();
} else {
this._acknowledgeCallbacks.delete(clientEvent.id);
reject(data);
}
}));
this._logger.debug(`pushResendClientEventsQueue and setAcknowledgeCallback. clientEvent.id: ${clientEvent.id}`);
reject(new Error("websocket is connecting now"));
return;
}
const backoff = new BackOff({ times: 6, interval: 500, jitter: 100 });
for (; !backoff.exceeded; ) {
const timer = setTimeout(() => __async(this, null, function* () {
if (this._socket === void 0) {
this._acknowledgeCallbacks.delete(clientEvent.id);
reject(new Error("Socket closed when trying to resend"));
return;
} else {
this._socket.resendAfterReconnect(clientEvent);
}
reject(new Error("Timeout to send data"));
return;
}), TIMEOUT_SEC * 1e3);
this._logger.debug(`send clientEvent, ${JSON.stringify(clientEvent)}`);
this._socket.send(clientEvent).catch((err) => {
this._acknowledgeCallbacks.delete(clientEvent.id);
clearTimeout(timer);
reject(err);
return;
});
const result = yield this._waitForAcknowledge(clientEvent.id).catch((err) => {
return err;
});
clearTimeout(timer);
if (isAcknowledgePayload(result)) {
if (result.reason === "unexpected") {
yield backoff.wait();
} else {
reject(result);
return;
}
} else {
resolve();
return;
}
}
reject(new Error("unexpected has occurred at server"));
return;
}));
});
}
_waitForAcknowledge(clientEventId) {
return __async(this, null, function* () {
return new Promise((resolve, reject) => {
this._setAcknowledgeCallback(clientEventId, (data) => __async(this, null, function* () {
if (data.ok) {
this._acknowledgeCallbacks.delete(clientEventId);
resolve();
} else {
this._acknowledgeCallbacks.delete(clientEventId);
reject(data);
}
}));
});
});
}
_reconnectWithNewSkyWayAuthToken() {
return __async(this, null, function* () {
this._disconnect();
if (this._newToken !== void 0) {
this._token = this._newToken;
this._newToken = void 0;
yield this.connect();
} else {
this._logger.warn("new token is not set. so not reconnect.");
}
});
}
_eventReceivedHandler(data) {
switch (data.type) {
case "Acknowledge":
this._acknowledgeHandler(data.payload);
break;
case "Open":
break;
// nop
default: {
const _ = data.type;
this._logger.warn(`Unknown event: ${data.type}`);
}
}
}
_acknowledgeHandler(payload) {
if (!isAcknowledgePayload(payload)) {
throw new Error("Invalid payload");
}
const { eventId } = payload;
if (!this._acknowledgeCallbacks.has(eventId)) {
throw new Error(`acknowledge event has unknown eventId: ${eventId}`);
}
const callback = this._acknowledgeCallbacks.get(eventId);
if (callback) {
this._acknowledgeCallbacks.delete(eventId);
callback(payload);
}
}
_setAcknowledgeCallback(eventId, callback) {
this._acknowledgeCallbacks.set(eventId, callback);
}
_cleanupAnalyticsClientMaps() {
this._mediaDeviceVersion.clear();
this._encodingsVersion.clear();
this._preferredEncodingVersion.clear();
this._previousSubscriptionStats.clear();
}
getIntervalSec() {
return this._statsRequest.intervalSec;
}
isConnectionEstablished() {
if (!this._socket || this._socket.connectionState === "connecting" || this._socket.connectionState === "closed") {
return false;
} else {
return true;
}
}
isClosed() {
return this._isClosed;
}
};
_AnalyticsClient.MAX_PENDING_SDK_LOGS = 50;
var AnalyticsClient = _AnalyticsClient;
// src/utils/logger.ts
init_process();
export {
AnalyticsClient,
Event
};
/*
@skyway-sdk/analytics-client@2.0.0
MIT
https://github.com/skyway/js-sdk
MIT License
Copyright (c) 2024 NTT DOCOMO BUSINESS, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
isomorphic-ws@4.0.1
MIT
https://github.com/heineiuo/isomorphic-ws
The MIT License (MIT)
Copyright (c) 2018 Zejin Zhuang <heineiuo@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
uuid@9.0.1
MIT
https://github.com/uuidjs/uuid
The MIT License (MIT)
Copyright (c) 2010-2020 Robert Kieffer and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
---
ws@8.18.2
MIT
https://github.com/websockets/ws
Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
Copyright (c) 2013 Arnout Kazemier and contributors
Copyright (c) 2016 Luigi Pinca and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
---
*/