@ceeblue/web-utils
Advanced tools
Comparing version 1.2.0 to 1.3.0
@@ -1,9 +0,1 @@ | ||
/** | ||
* Copyright 2024 Ceeblue B.V. | ||
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License. | ||
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details. | ||
*/ | ||
/** | ||
* BinaryReader allows to read binary data | ||
*/ | ||
declare class BinaryReader { | ||
@@ -27,2 +19,3 @@ private _data; | ||
read32(): number; | ||
read64(): number; | ||
readFloat(): number; | ||
@@ -34,3 +27,3 @@ readDouble(): number; | ||
/** | ||
* Read bytes, to convert bytes to string use String.fromCharCode(...reader.read(size)) | ||
* Read bytes, to convert bytes to string use String.fromCharCode(...reader.read(size)) or Util.stringify | ||
* @param {UInt32} size | ||
@@ -61,3 +54,7 @@ */ | ||
clear(size?: number): BinaryWriter; | ||
write(data: string | ArrayLike<number>): BinaryWriter; | ||
/** | ||
* Write binary data | ||
* @param data | ||
*/ | ||
write(data: ArrayLike<number>): BinaryWriter; | ||
write8(value: number): BinaryWriter; | ||
@@ -67,5 +64,6 @@ write16(value: number): BinaryWriter; | ||
write32(value: number): BinaryWriter; | ||
write64(value: number): BinaryWriter; | ||
writeFloat(value: number): BinaryWriter; | ||
writeDouble(value: number): BinaryWriter; | ||
write7Bit(value: number, bytes?: number): BinaryWriter; | ||
write7Bit(value: number): BinaryWriter; | ||
writeString(value: string): BinaryWriter; | ||
@@ -131,7 +129,7 @@ writeHex(value: string): BinaryWriter; | ||
declare enum Type { | ||
HESP = "hesp", | ||
WEBRTS = "webrts", | ||
WEBRTC = "webrtc", | ||
META = "meta", | ||
DATA = "data" | ||
HESP = "HESP", | ||
WEBRTS = "WebRTS", | ||
WEBRTC = "WebRTC", | ||
META = "Meta", | ||
DATA = "Data" | ||
} | ||
@@ -601,3 +599,4 @@ /** | ||
* @param params.decimal `2`, allows to choose the number of decimal to display in the string representation | ||
* @param params.recursive `false`, allows to serialize recursively every object value, beware if a value refers to a already parsed value an infinite loop will occur. | ||
* @param params.recursive `false`, allows to serialize recursively every object value, beware if a value refers to a already parsed value an infinite loop will occur | ||
* @param params.noBin `false`, when set skip binary encoding and write inplace a bin-length information | ||
* @returns the final string representation | ||
@@ -609,2 +608,3 @@ */ | ||
recursive?: number; | ||
noBin?: boolean; | ||
}): string; | ||
@@ -621,8 +621,26 @@ /** | ||
declare function safePromise<T>(timeout: number, promise: Promise<T>): Promise<unknown>; | ||
/** | ||
* Wait in milliseconds, requires a call with await keyword! | ||
*/ | ||
declare function sleep(ms: number): Promise<unknown>; | ||
/** | ||
* fetch help method with few usefull fix: | ||
* - throw an string exception if response code is not 200 with the text of the response or uses statusText | ||
*/ | ||
declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>; | ||
/** | ||
* Extension parser | ||
* @param path path to parse | ||
* @returns the extension | ||
*/ | ||
declare function parseExtension(path: string): string; | ||
declare const Util_EMPTY_FUNCTION: typeof EMPTY_FUNCTION; | ||
declare const Util_fetch: typeof fetch; | ||
declare const Util_objectEntries: typeof objectEntries; | ||
declare const Util_objectFrom: typeof objectFrom; | ||
declare const Util_options: typeof options; | ||
declare const Util_parseExtension: typeof parseExtension; | ||
declare const Util_safePromise: typeof safePromise; | ||
declare const Util_sleep: typeof sleep; | ||
declare const Util_stringify: typeof stringify; | ||
@@ -633,3 +651,3 @@ declare const Util_time: typeof time; | ||
declare namespace Util { | ||
export { Util_EMPTY_FUNCTION as EMPTY_FUNCTION, Util_objectEntries as objectEntries, Util_objectFrom as objectFrom, Util_options as options, Util_safePromise as safePromise, Util_stringify as stringify, Util_time as time, Util_timeOrigin as timeOrigin, Util_toBin as toBin }; | ||
export { Util_EMPTY_FUNCTION as EMPTY_FUNCTION, Util_fetch as fetch, Util_objectEntries as objectEntries, Util_objectFrom as objectFrom, Util_options as options, Util_parseExtension as parseExtension, Util_safePromise as safePromise, Util_sleep as sleep, Util_stringify as stringify, Util_time as time, Util_timeOrigin as timeOrigin, Util_toBin as toBin }; | ||
} | ||
@@ -688,2 +706,4 @@ | ||
get binaryType(): BinaryType; | ||
get recvByteRate(): number; | ||
get sendByteRate(): number; | ||
/** | ||
@@ -727,2 +747,4 @@ * url of connection | ||
private _ws?; | ||
private _recvByteRate; | ||
private _sendByteRate; | ||
/** | ||
@@ -755,2 +777,3 @@ * Create a WebSocketReliable object, and open it if an url is passed in argument | ||
close(error?: string): void; | ||
private _send; | ||
} | ||
@@ -757,0 +780,0 @@ |
@@ -9,2 +9,3 @@ /** | ||
*/ | ||
const _decoder$1 = new TextDecoder(); | ||
class BinaryReader { | ||
@@ -34,3 +35,3 @@ constructor(data) { | ||
reset(position = 0) { | ||
this._position = position > this._size ? this._size : position; | ||
this._position = Math.max(0, position > this._size ? this._size : position); | ||
} | ||
@@ -50,3 +51,3 @@ shrink(available) { | ||
} | ||
this._position += count; | ||
this._position = Math.max(0, this._position + count); | ||
return count; | ||
@@ -68,2 +69,8 @@ } | ||
} | ||
read64() { | ||
if (this.next(8) !== 8) { | ||
return 0; | ||
} | ||
return this._view.getUint32(this._position - 8) * 4294967296 + this._view.getUint32(this._position - 4); | ||
} | ||
readFloat() { | ||
@@ -76,26 +83,26 @@ return this.next(4) === 4 ? this._view.getFloat32(this._position - 4) : 0; | ||
read7Bit(bytes = 5) { | ||
if (bytes > 5) { | ||
throw Error("BinaryReader in JS can't decode more than 32 usefull bits"); | ||
} | ||
if (!(bytes > 0)) { | ||
// negation to catch NaN value | ||
throw Error('Have to indicate a positive number of bytes to decode'); | ||
} | ||
let result = 0; | ||
let byte; | ||
do { | ||
byte = this.read8(); | ||
if (!--bytes) { | ||
return ((result << 8) | byte) >>> 0; // Use all 8 bits from the 5th byte | ||
let factor = 1; | ||
while (this.available()) { | ||
const byte = this.read8(); | ||
result += (byte & 0x7f) * factor; | ||
if (!(byte & 0x80)) { | ||
break; | ||
} | ||
result = (result << 7) | (byte & 0x7f); | ||
} while (byte & 0x80); | ||
factor *= 128; | ||
} | ||
return result; | ||
} | ||
readString() { | ||
return String.fromCharCode(...this.read(this.read7Bit())); | ||
let i = this._position; | ||
while (i < this._size && this._data[i]) { | ||
++i; | ||
} | ||
const result = this.read(i - this._position); | ||
this.next(); // skip the 0 termination | ||
return _decoder$1.decode(result); | ||
} | ||
readHex(size) { | ||
let hex = ''; | ||
while (size--) { | ||
while (size-- > 0) { | ||
hex += ('0' + this.read8().toString(16)).slice(-2); | ||
@@ -106,3 +113,3 @@ } | ||
/** | ||
* Read bytes, to convert bytes to string use String.fromCharCode(...reader.read(size)) | ||
* Read bytes, to convert bytes to string use String.fromCharCode(...reader.read(size)) or Util.stringify | ||
* @param {UInt32} size | ||
@@ -114,5 +121,4 @@ */ | ||
} | ||
const value = this._data.subarray(this._position, this._position + size); | ||
this._position += size; | ||
return value; | ||
const pos = this._position; | ||
return this._data.subarray(pos, Math.max(pos, (this._position += size))); | ||
} | ||
@@ -124,2 +130,3 @@ }/** | ||
*/ | ||
const _encoder$1 = new TextEncoder(); | ||
/** | ||
@@ -172,12 +179,8 @@ * BinaryWriter allows to write data in its binary form | ||
} | ||
/** | ||
* Write binary data | ||
* @param data | ||
*/ | ||
write(data) { | ||
this.reserve(this._size + data.length); | ||
if (typeof data === 'string') { | ||
// beware here support just the 255 first bytes (compatible Latin-1) | ||
for (let i = 0; i < data.length; ++i) { | ||
const value = data.charCodeAt(i); | ||
this._data[this._size++] = value > 255 ? 32 : value; | ||
} | ||
return this; | ||
} | ||
this._data.set(data, this._size); | ||
@@ -227,2 +230,6 @@ this._size += data.length; | ||
} | ||
write64(value) { | ||
this.write32(value / 4294967296); | ||
return this.write32(value & 0xffffffff); | ||
} | ||
writeFloat(value) { | ||
@@ -240,31 +247,13 @@ this.reserve(this._size + 4); | ||
} | ||
write7Bit(value, bytes = 5) { | ||
if (bytes > 5) { | ||
throw Error("BinaryWriter in JS can't encode more than 32 usefull bits"); | ||
write7Bit(value) { | ||
let byte = value & 0x7f; | ||
while ((value = Math.floor(value / 0x80))) { | ||
// equivalent to >>=7 for JS! | ||
this.write8(0x80 | byte); | ||
byte = value & 0x7f; | ||
} | ||
if (!(bytes > 0)) { | ||
// negation to catch NaN value | ||
throw Error('Have to indicate a positive number of bytes to encode'); | ||
} | ||
let bits = --bytes * 7; | ||
const front = value > 0xffffffff ? 0x100 : value >>> bits; | ||
if (front) { | ||
++bits; | ||
if (front > 0xff) { | ||
value = 0xffffffff; | ||
} | ||
} | ||
else { | ||
while ((bits -= 7) && !(value >>> bits)) { | ||
continue; | ||
} | ||
} | ||
while (bits > 1) { | ||
this.write8(0x80 | ((value >>> bits) & 0xff)); | ||
bits -= 7; | ||
} | ||
return this.write8(value & (bits ? 0xff : 0x7f)); | ||
return this.write8(byte); | ||
} | ||
writeString(value) { | ||
return this.write7Bit(value.length).write(value); | ||
return this.write(_encoder$1.encode(value)).write8(0); | ||
} | ||
@@ -279,3 +268,3 @@ writeHex(value) { | ||
if (!this._data) { | ||
throw new Error('buffer not writable'); | ||
throw Error('buffer not writable'); | ||
} | ||
@@ -286,3 +275,3 @@ if (size <= this._data.byteLength) { | ||
if (this._isConst) { | ||
throw new Error('writing exceeds maximum ' + this._data.byteLength + ' bytes limit'); | ||
throw Error('writing exceeds maximum ' + this._data.byteLength + ' bytes limit'); | ||
} | ||
@@ -492,3 +481,33 @@ --size; | ||
} | ||
}/** | ||
}/****************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
/* global Reflect, Promise, SuppressedError, Symbol */ | ||
function __awaiter(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()); | ||
}); | ||
} | ||
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { | ||
var e = new Error(message); | ||
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; | ||
};/** | ||
* Copyright 2024 Ceeblue B.V. | ||
@@ -498,3 +517,239 @@ * This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License. | ||
*/ | ||
const _decoder = new TextDecoder(); | ||
const _encoder = new TextEncoder(); | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
const _perf = performance; // to increase x10 now performance! | ||
/** | ||
* Some basic utility functions | ||
*/ | ||
/** | ||
* An empty lambda function, pratical to disable default behavior of function or events which are not expected to be null | ||
* @example | ||
* console.log = Util.EMPTY_FUNCTION; // disable logs without breaking calls | ||
*/ | ||
const EMPTY_FUNCTION = () => { }; | ||
/** | ||
* Efficient and high resolution timestamp in milliseconds elapsed since {@link Util.timeOrigin} | ||
*/ | ||
function time() { | ||
return Math.floor(_perf.now()); | ||
} | ||
/** | ||
* Time origin represents the time when the application has started | ||
*/ | ||
function timeOrigin() { | ||
return Math.floor(_perf.now() + _perf.timeOrigin); | ||
} | ||
/** | ||
* Parse query and returns it in an easy-to-use Javascript object form | ||
* @param urlOrQueryOrSearch string, url, or searchParams containing query. If not set it uses `location.search` to determinate query. | ||
* @returns An javascript object containing each option | ||
*/ | ||
function options(urlOrQueryOrSearch = typeof location === 'undefined' | ||
? undefined | ||
: location) { | ||
if (!urlOrQueryOrSearch) { | ||
return {}; | ||
} | ||
try { | ||
const url = urlOrQueryOrSearch; | ||
urlOrQueryOrSearch = new URL(url).searchParams; | ||
} | ||
catch (e) { | ||
if (typeof urlOrQueryOrSearch == 'string') { | ||
if (urlOrQueryOrSearch.startsWith('?')) { | ||
urlOrQueryOrSearch = urlOrQueryOrSearch.substring(1); | ||
} | ||
urlOrQueryOrSearch = new URLSearchParams(urlOrQueryOrSearch); | ||
} | ||
} | ||
// works same if urlOrQueryOrSearch is null, integer, or a already object etc... | ||
return objectFrom(urlOrQueryOrSearch, { withType: true, noEmptyString: true }); | ||
} | ||
/** | ||
* Returns an easy-to-use Javascript object something iterable, such as a Map, Set, or Array | ||
* @param value iterable input | ||
* @param params.withType `false`, if set it tries to cast string value to a JS number/boolean/undefined/null type. | ||
* @param params.noEmptyString `false`, if set it converts empty string value to a true boolean, usefull to allow a `if(result.key)` check for example | ||
* @returns An javascript object | ||
*/ | ||
function objectFrom(value, params) { | ||
params = Object.assign({ withType: false, noEmptyString: false }, params); | ||
const obj = {}; | ||
if (!value) { | ||
return obj; | ||
} | ||
for (const [key, val] of objectEntries(value)) { | ||
value = val; | ||
if (params.withType && value != null && value.substring) { | ||
if (value) { | ||
const number = Number(value); | ||
if (isNaN(number)) { | ||
switch (value.toLowerCase()) { | ||
case 'true': | ||
value = true; | ||
break; | ||
case 'false': | ||
value = false; | ||
break; | ||
case 'null': | ||
value = null; | ||
break; | ||
case 'undefined': | ||
value = undefined; | ||
break; | ||
} | ||
} | ||
else { | ||
value = number; | ||
} | ||
} | ||
else if (params.noEmptyString) { | ||
// if empty string => TRUE to allow a if(options.key) check for example | ||
value = true; | ||
} | ||
} | ||
if (obj[key]) { | ||
if (!Array.isArray(obj[key])) { | ||
obj[key] = new Array(obj[key]); | ||
} | ||
obj[key].push(value); | ||
} | ||
else { | ||
obj[key] = value; | ||
} | ||
} | ||
return obj; | ||
} | ||
/** | ||
* Returns entries from something iterable, such as a Map, Set, or Array | ||
* @param value iterable input | ||
* @returns An javascript object | ||
*/ | ||
function objectEntries(value) { | ||
if (value.entries) { | ||
return value.entries(); | ||
} | ||
return Array.from({ | ||
[Symbol.iterator]: function* () { | ||
for (const key in value) { | ||
yield [key, value[key]]; | ||
} | ||
} | ||
}); | ||
} | ||
/** | ||
* Converts various data types, such as objects, strings, exceptions, errors, | ||
* or numbers, into a string representation. Since it offers a more comprehensive format, | ||
* this function is preferred to `JSON.stringify()`. | ||
* @param obj Any objects, strings, exceptions, errors, or number | ||
* @param params.space `''`, allows to configure space in the string representation | ||
* @param params.decimal `2`, allows to choose the number of decimal to display in the string representation | ||
* @param params.recursive `false`, allows to serialize recursively every object value, beware if a value refers to a already parsed value an infinite loop will occur | ||
* @param params.noBin `false`, when set skip binary encoding and write inplace a bin-length information | ||
* @returns the final string representation | ||
*/ | ||
// Online Javascript Editor for free | ||
// Write, Edit and Run your Javascript code using JS Online Compiler | ||
function stringify(obj, params = {}) { | ||
params = Object.assign({ space: ' ', decimal: 2, recursive: 1, noBin: false }, params); | ||
if (obj == null) { | ||
return String(obj); | ||
} | ||
const error = obj.error || obj.message; | ||
if (error) { | ||
// is a error! | ||
obj = error; | ||
} | ||
// number | ||
if (obj.toFixed) { | ||
return obj.toFixed(Number(params.decimal) || 0); | ||
} | ||
// boolean or string type or stop recursivity | ||
if (typeof obj === 'boolean' || obj.substring || !params.recursive) { | ||
// is already a string OR has to be stringified | ||
return String(obj); | ||
} | ||
const space = params.space || ''; | ||
if (Array.isArray(obj)) { | ||
// Array! | ||
let res = ''; | ||
for (const value of obj) { | ||
res += (res ? ',' : '[') + space; | ||
res += stringify(value, Object.assign(Object.assign({}, params), { recursive: params.recursive - 1 })); | ||
} | ||
return (res += space + ']'); | ||
} | ||
if (obj.byteLength != null && (obj === null || obj === void 0 ? void 0 : obj[Symbol.iterator])) { | ||
// Binary! | ||
return _decoder.decode(obj); | ||
} | ||
let res = ''; | ||
if (params.noBin) { | ||
return '[' + obj.byteLength + '#bytes]'; | ||
} | ||
for (const name in obj) { | ||
res += (res ? ',' : '{') + space + name + ':'; | ||
res += stringify(obj[name], Object.assign(Object.assign({}, params), { recursive: params.recursive - 1 })); | ||
} | ||
return (res += space + '}'); | ||
} | ||
/** | ||
* Encode a string to a binary representation | ||
* @param value string value to convert | ||
* @returns binary conversion | ||
*/ | ||
function toBin(value) { | ||
return _encoder.encode(value); | ||
} | ||
/** | ||
* Execute a promise in a safe way with a timeout if caller doesn't resolve it in the accurate time | ||
*/ | ||
function safePromise(timeout, promise) { | ||
// Returns a race between our timeout and the passed in promise | ||
let timer; | ||
return Promise.race([ | ||
promise instanceof Promise ? promise : new Promise(promise), | ||
new Promise((resolve, reject) => (timer = setTimeout(() => reject('timed out in ' + timeout + 'ms'), timeout))) | ||
]).finally(() => clearTimeout(timer)); | ||
} | ||
/** | ||
* Wait in milliseconds, requires a call with await keyword! | ||
*/ | ||
function sleep(ms) { | ||
return new Promise(resolve => { | ||
setTimeout(resolve, ms); | ||
}); | ||
} | ||
/** | ||
* fetch help method with few usefull fix: | ||
* - throw an string exception if response code is not 200 with the text of the response or uses statusText | ||
*/ | ||
function fetch(input, init) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const response = yield self.fetch(input, init); | ||
if (response.status >= 300) { | ||
if (response.body) { | ||
throw (yield response.text()) || response.statusText; | ||
} | ||
throw response.statusText; | ||
} | ||
return response; | ||
}); | ||
} | ||
/** | ||
* Extension parser | ||
* @param path path to parse | ||
* @returns the extension | ||
*/ | ||
function parseExtension(path) { | ||
const dot = path.lastIndexOf('.'); | ||
const ext = dot >= 0 && dot > path.lastIndexOf('/') ? path.substring(dot) : ''; | ||
return ext; | ||
}var Util=/*#__PURE__*/Object.freeze({__proto__:null,EMPTY_FUNCTION:EMPTY_FUNCTION,fetch:fetch,objectEntries:objectEntries,objectFrom:objectFrom,options:options,parseExtension:parseExtension,safePromise:safePromise,sleep:sleep,stringify:stringify,time:time,timeOrigin:timeOrigin,toBin:toBin});/** | ||
* Copyright 2024 Ceeblue B.V. | ||
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License. | ||
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details. | ||
*/ | ||
/** | ||
* Type of connection | ||
@@ -504,7 +759,7 @@ */ | ||
(function (Type) { | ||
Type["HESP"] = "hesp"; | ||
Type["WEBRTS"] = "webrts"; | ||
Type["WEBRTC"] = "webrtc"; | ||
Type["META"] = "meta"; | ||
Type["DATA"] = "data"; | ||
Type["HESP"] = "HESP"; | ||
Type["WEBRTS"] = "WebRTS"; | ||
Type["WEBRTC"] = "WebRTC"; | ||
Type["META"] = "Meta"; | ||
Type["DATA"] = "Data"; | ||
})(Type || (Type = {})); | ||
@@ -552,2 +807,4 @@ /** | ||
} | ||
// Remove possible extension of streamName | ||
params.streamName.substring(0, params.streamName.length - parseExtension(params.streamName).length); | ||
return url; | ||
@@ -617,14 +874,15 @@ }var Connect=/*#__PURE__*/Object.freeze({__proto__:null,get Type(){return Type},buildURL:buildURL});/** | ||
let defaultEvent = proto[name]; | ||
const raise = (...args) => { | ||
// Call default event if not null (can happen in JS usage) | ||
if (defaultEvent) { | ||
defaultEvent.call(this, ...args); | ||
} | ||
// Call subscribers | ||
for (const event of events) { | ||
event(...args); | ||
} | ||
}; | ||
Object.defineProperties(this, { | ||
[name]: { | ||
get: () => (...args) => { | ||
// Call default event if not null (can happen in JS usage) | ||
if (defaultEvent) { | ||
defaultEvent.call(this, ...args); | ||
} | ||
// Call subscribers | ||
for (const event of events) { | ||
event(...args); | ||
} | ||
}, | ||
get: () => raise, | ||
set: (value) => { | ||
@@ -654,5 +912,3 @@ // Assign a default behavior! | ||
if (abort) { | ||
abort.signal.addEventListener('abort', () => { | ||
events.delete(event); | ||
}); | ||
abort.signal.addEventListener('abort', () => events.delete(event), { once: true }); | ||
} | ||
@@ -671,10 +927,8 @@ } | ||
const events = this._event(name); | ||
events.add(() => { | ||
events.add((...args) => { | ||
events.delete(event); // delete from events | ||
event(); // execute event | ||
event(...args); // execute event | ||
}); | ||
if (abort) { | ||
abort.signal.addEventListener('abort', () => { | ||
events.delete(event); | ||
}); | ||
abort.signal.addEventListener('abort', () => events.delete(event), { once: true }); | ||
} | ||
@@ -1122,194 +1376,36 @@ } | ||
*/ | ||
const _decoder = new TextDecoder(); | ||
const _encoder = new TextEncoder(); | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
const _perf = performance; // to increase x10 now performance! | ||
/** | ||
* Some basic utility functions | ||
* Compute ByteRate every delta time | ||
*/ | ||
/** | ||
* An empty lambda function, pratical to disable default behavior of function or events which are not expected to be null | ||
* @example | ||
* console.log = Util.EMPTY_FUNCTION; // disable logs without breaking calls | ||
*/ | ||
const EMPTY_FUNCTION = () => { }; | ||
/** | ||
* Efficient and high resolution timestamp in milliseconds elapsed since {@link Util.timeOrigin} | ||
*/ | ||
function time() { | ||
return Math.floor(_perf.now()); | ||
} | ||
/** | ||
* Time origin represents the time when the application has started | ||
*/ | ||
function timeOrigin() { | ||
return Math.floor(_perf.now() + _perf.timeOrigin); | ||
} | ||
/** | ||
* Parse query and returns it in an easy-to-use Javascript object form | ||
* @param urlOrQueryOrSearch string, url, or searchParams containing query. If not set it uses `location.search` to determinate query. | ||
* @returns An javascript object containing each option | ||
*/ | ||
function options(urlOrQueryOrSearch = typeof location === 'undefined' | ||
? undefined | ||
: location) { | ||
if (!urlOrQueryOrSearch) { | ||
return {}; | ||
class ByteRate { | ||
onBytes(bytes) { } | ||
get delta() { | ||
return this._delta; | ||
} | ||
try { | ||
const url = urlOrQueryOrSearch; | ||
urlOrQueryOrSearch = new URL(url).searchParams; | ||
constructor(delta = 1000) { | ||
this._time = time(); | ||
this._value = NaN; | ||
this._delta = delta; | ||
this._bytes = 0; | ||
} | ||
catch (e) { | ||
if (typeof urlOrQueryOrSearch == 'string') { | ||
if (urlOrQueryOrSearch.startsWith('?')) { | ||
urlOrQueryOrSearch = urlOrQueryOrSearch.substring(1); | ||
} | ||
urlOrQueryOrSearch = new URLSearchParams(urlOrQueryOrSearch); | ||
} | ||
value() { | ||
return Math.round(this.exact()); | ||
} | ||
// works same if urlOrQueryOrSearch is null, integer, or a already object etc... | ||
return objectFrom(urlOrQueryOrSearch, { withType: true, noEmptyString: true }); | ||
} | ||
/** | ||
* Returns an easy-to-use Javascript object something iterable, such as a Map, Set, or Array | ||
* @param value iterable input | ||
* @param params.withType `false`, if set it tries to cast string value to a JS number/boolean/undefined/null type. | ||
* @param params.noEmptyString `false`, if set it converts empty string value to a true boolean, usefull to allow a `if(result.key)` check for example | ||
* @returns An javascript object | ||
*/ | ||
function objectFrom(value, params) { | ||
params = Object.assign({ withType: false, noEmptyString: false }, params); | ||
const obj = {}; | ||
if (!value) { | ||
return obj; | ||
} | ||
for (const [key, val] of objectEntries(value)) { | ||
value = val; | ||
if (params.withType && value != null && value.substring) { | ||
if (value) { | ||
const number = Number(value); | ||
if (isNaN(number)) { | ||
switch (value.toLowerCase()) { | ||
case 'true': | ||
value = true; | ||
break; | ||
case 'false': | ||
value = false; | ||
break; | ||
case 'null': | ||
value = null; | ||
break; | ||
case 'undefined': | ||
value = undefined; | ||
break; | ||
} | ||
} | ||
else { | ||
value = number; | ||
} | ||
} | ||
else if (params.noEmptyString) { | ||
// if empty string => TRUE to allow a if(options.key) check for example | ||
value = true; | ||
} | ||
exact() { | ||
const now = time(); | ||
const elapsed = now - this._time; | ||
if (elapsed > this._delta || isNaN(this._value)) { | ||
// wait "_delta" before next compute rate | ||
this._value = (this._bytes * 1000) / elapsed; | ||
this._bytes = 0; | ||
this._time = now; | ||
} | ||
if (obj[key]) { | ||
if (!Array.isArray(obj[key])) { | ||
obj[key] = new Array(obj[key]); | ||
} | ||
obj[key].push(value); | ||
} | ||
else { | ||
obj[key] = value; | ||
} | ||
return this._value; | ||
} | ||
return obj; | ||
} | ||
/** | ||
* Returns entries from something iterable, such as a Map, Set, or Array | ||
* @param value iterable input | ||
* @returns An javascript object | ||
*/ | ||
function objectEntries(value) { | ||
if (value.entries) { | ||
return value.entries(); | ||
addBytes(bytes) { | ||
this._bytes += bytes; | ||
this.onBytes(bytes); | ||
return this; | ||
} | ||
return Array.from({ | ||
[Symbol.iterator]: function* () { | ||
for (const key in value) { | ||
yield [key, value[key]]; | ||
} | ||
} | ||
}); | ||
} | ||
/** | ||
* Converts various data types, such as objects, strings, exceptions, errors, | ||
* or numbers, into a string representation. Since it offers a more comprehensive format, | ||
* this function is preferred to `JSON.stringify()`. | ||
* @param obj Any objects, strings, exceptions, errors, or number | ||
* @param params.space `''`, allows to configure space in the string representation | ||
* @param params.decimal `2`, allows to choose the number of decimal to display in the string representation | ||
* @param params.recursive `false`, allows to serialize recursively every object value, beware if a value refers to a already parsed value an infinite loop will occur. | ||
* @returns the final string representation | ||
*/ | ||
// Online Javascript Editor for free | ||
// Write, Edit and Run your Javascript code using JS Online Compiler | ||
function stringify(obj, params = {}) { | ||
params = Object.assign({ space: ' ', decimal: 2, recursive: 1 }, params); | ||
if (obj == null) { | ||
return String(obj); | ||
} | ||
const error = obj.error || obj.message; | ||
if (error) { | ||
// is a error! | ||
obj = error; | ||
} | ||
if (obj.toFixed) { | ||
return obj.toFixed(Number(params.decimal) || 0); | ||
} | ||
if (obj.substring || !params.recursive) { | ||
// is already a string OR has to be stringified | ||
return String(obj); | ||
} | ||
const space = params.space || ''; | ||
if (Array.isArray(obj)) { | ||
// Array! | ||
let res = ''; | ||
for (const value of obj) { | ||
res += (res ? ',' : '[') + space; | ||
res += stringify(value, Object.assign(params, { recursive: params.recursive - 1 })); | ||
} | ||
return (res += space + ']'); | ||
} | ||
if (obj.byteLength != null && (obj === null || obj === void 0 ? void 0 : obj[Symbol.iterator])) { | ||
// Binary! | ||
return _decoder.decode(obj); | ||
} | ||
let res = ''; | ||
for (const name in obj) { | ||
res += (res ? ',' : '{') + space + name + ':'; | ||
res += stringify(obj[name], Object.assign(params, { recursive: params.recursive - 1 })); | ||
} | ||
return (res += space + '}'); | ||
} | ||
/** | ||
* Encode a string to a binary representation | ||
* @param value string value to convert | ||
* @returns binary conversion | ||
*/ | ||
function toBin(value) { | ||
return _encoder.encode(value); | ||
} | ||
/** | ||
* Execute a promise in a safe way with a timeout if caller doesn't resolve it in the accurate time | ||
*/ | ||
function safePromise(timeout, promise) { | ||
// Returns a race between our timeout and the passed in promise | ||
let timer; | ||
return Promise.race([ | ||
promise instanceof Promise ? promise : new Promise(promise), | ||
new Promise((resolve, reject) => (timer = setTimeout(() => reject('timed out in ' + timeout + 'ms'), timeout))) | ||
]).finally(() => clearTimeout(timer)); | ||
}var Util=/*#__PURE__*/Object.freeze({__proto__:null,EMPTY_FUNCTION:EMPTY_FUNCTION,objectEntries:objectEntries,objectFrom:objectFrom,options:options,safePromise:safePromise,stringify:stringify,time:time,timeOrigin:timeOrigin,toBin:toBin});/** | ||
}/** | ||
* Copyright 2024 Ceeblue B.V. | ||
@@ -1370,2 +1466,8 @@ * This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License. | ||
} | ||
get recvByteRate() { | ||
return this._recvByteRate.value(); | ||
} | ||
get sendByteRate() { | ||
return this._sendByteRate.value(); | ||
} | ||
/** | ||
@@ -1434,2 +1536,4 @@ * url of connection | ||
this._closed = true; | ||
this._recvByteRate = new ByteRate(); | ||
this._sendByteRate = new ByteRate(); | ||
if (url) { | ||
@@ -1448,3 +1552,7 @@ this.open(url, protocols); | ||
ws.binaryType = this.binaryType; | ||
ws.onmessage = e => this.onMessage(e.data); | ||
ws.onmessage = e => { | ||
var _a; | ||
this._recvByteRate.addBytes((_a = e.data.byteLength) !== null && _a !== void 0 ? _a : e.data.length); | ||
this.onMessage(e.data); | ||
}; | ||
// Add details and fix close ways | ||
@@ -1488,3 +1596,3 @@ ws.onclose = (e) => { | ||
else { | ||
this._ws.send(message); | ||
this._send(message); | ||
} | ||
@@ -1499,3 +1607,3 @@ return this; | ||
for (const message of this._queueing) { | ||
this._ws.send(message); | ||
this._send(message); | ||
} | ||
@@ -1523,2 +1631,9 @@ } | ||
} | ||
_send(message) { | ||
if (!this._ws) { | ||
return; | ||
} | ||
this._sendByteRate.addBytes(typeof message === 'string' ? message.length : message.byteLength); | ||
this._ws.send(message); | ||
} | ||
}/** | ||
@@ -1529,2 +1644,2 @@ * Copyright 2024 Ceeblue B.V. | ||
*/ | ||
const VERSION = '1.2.0';export{BinaryReader,BinaryWriter,BitReader,Connect,EventEmitter,NetAddress,Numbers,Queue,SDP,Util,VERSION,WebSocketReliable};//# sourceMappingURL=web-utils.js.map | ||
const VERSION = '1.3.0';export{BinaryReader,BinaryWriter,BitReader,Connect,EventEmitter,NetAddress,Numbers,Queue,SDP,Util,VERSION,WebSocketReliable};//# sourceMappingURL=web-utils.js.map |
@@ -1,1 +0,1 @@ | ||
class t{constructor(t){this._data="buffer"in t?new Uint8Array(t.buffer,t.byteOffset,t.byteLength):new Uint8Array(t),this._size=this._data.byteLength,this._position=0,this._view=new DataView(this._data.buffer,this._data.byteOffset,this._size)}data(){return this._data}size(){return this._size}available(){return this._size-this._position}value(t=this._position){return this._data[t]}position(){return this._position}reset(t=0){this._position=t>this._size?this._size:t}shrink(t){const e=this._size-this._position;return t>e?e:(this._size=this._position+t,t)}next(t=1){const e=this._size-this._position;return t>e&&(t=e),this._position+=t,t}read8(){return 1===this.next(1)?this._view.getUint8(this._position-1):0}read16(){return 2===this.next(2)?this._view.getUint16(this._position-2):0}read24(){return 3===this.next(3)?this._view.getUint16(this._position-3)<<8|255&this._view.getUint8(this._position-1):0}read32(){return 4===this.next(4)?this._view.getUint32(this._position-4):0}readFloat(){return 4===this.next(4)?this._view.getFloat32(this._position-4):0}readDouble(){return 8===this.next(8)?this._view.getFloat64(this._position-8):0}read7Bit(t=5){if(t>5)throw Error("BinaryReader in JS can't decode more than 32 usefull bits");if(!(t>0))throw Error("Have to indicate a positive number of bytes to decode");let e,i=0;do{if(e=this.read8(),! --t)return(i<<8|e)>>>0;i=i<<7|127&e}while(128&e);return i}readString(){return String.fromCharCode(...this.read(this.read7Bit()))}readHex(t){let e="";for(;t--;)e+=("0"+this.read8().toString(16)).slice(-2);return e}read(t=this.available()){if(this.available()<t)return new Uint8Array(t);const e=this._data.subarray(this._position,this._position+t);return this._position+=t,e}}class e{get view(){return this._view||(this._view=new DataView(this._data.buffer,this._data.byteOffset,this._data.byteLength)),this._view}get capacity(){return this._data.byteLength}constructor(t=64,e=0,i){"number"==typeof t?(this._data=new Uint8Array(t),this._size=0):"buffer"in t?(this._data=new Uint8Array(t.buffer,t.byteOffset,t.byteLength),this._size=t.byteLength):(this._isConst=!0,null==i&&(i=t.byteLength),this._data=new Uint8Array(t,e,i),this._size=0)}data(){return new Uint8Array(this._data.buffer,this._data.byteOffset,this._size)}size(){return this._size||0}next(t=1){return this.reserve(this._size+=t)}clear(t=0){return this.reserve(this._size=t)}write(t){if(this.reserve(this._size+t.length),"string"==typeof t){for(let e=0;e<t.length;++e){const i=t.charCodeAt(e);this._data[this._size++]=i>255?32:i}return this}return this._data.set(t,this._size),this._size+=t.length,this}write8(t){return t>255&&(t=255),this.reserve(this._size+1),this._data[this._size++]=t,this}write16(t){return t>65535&&(t=65535),this.reserve(this._size+2),this.view.setUint16(this._size,t),this._size+=2,this}write24(t){return t>16777215&&(t=16777215),this.reserve(this._size+3),this.view.setUint16(this._size,t>>8),this.view.setUint8(this._size+=2,255&t),++this._size,this}write32(t){return t>4294967295&&(t=4294967295),this.reserve(this._size+4),this.view.setUint32(this._size,t),this._size+=4,this}writeFloat(t){return this.reserve(this._size+4),this.view.setFloat32(this._size,t),this._size+=4,this}writeDouble(t){return this.reserve(this._size+8),this.view.setFloat64(this._size,t),this._size+=8,this}write7Bit(t,e=5){if(e>5)throw Error("BinaryWriter in JS can't encode more than 32 usefull bits");if(!(e>0))throw Error("Have to indicate a positive number of bytes to encode");let i=7*--e;const s=t>4294967295?256:t>>>i;if(s)++i,s>255&&(t=4294967295);else for(;(i-=7)&&!(t>>>i););for(;i>1;)this.write8(128|t>>>i&255),i-=7;return this.write8(t&(i?255:127))}writeString(t){return this.write7Bit(t.length).write(t)}writeHex(t){for(let e=0;e<t.length;e+=2)this.write8(parseInt(t.substring(e,e+2),16));return this}reserve(t){if(!this._data)throw new Error("buffer not writable");if(t<=this._data.byteLength)return this;if(this._isConst)throw new Error("writing exceeds maximum "+this._data.byteLength+" bytes limit");--t,t|=t>>1,t|=t>>2,t|=t>>4,t|=t>>8,t|=t>>16,++t;const e=new Uint8Array(t);return e.set(this._data),this._data=e,this._view=void 0,this}}class i{constructor(t){this._data="buffer"in t?new Uint8Array(t.buffer,t.byteOffset,t.byteLength):new Uint8Array(t),this._size=this._data.byteLength,this._position=0,this._bit=0}data(){return this._data}size(){return this._size}available(){return 8*(this._size-this._position)-this._bit}next(t=1){let e=0;for(;this._position!==this._size&&t--;)++e,8==++this._bit&&(this._bit=0,++this._position);return e}read(t=1){let e=0;for(;this._position!==this._size&&t--;)e<<=1,this._data[this._position]&128>>this._bit++&&(e|=1),8===this._bit&&(this._bit=0,++this._position);return e}read8(){return this.read(8)}read16(){return this.read(16)}read24(){return this.read(24)}read32(){return this.read(32)}}class s{static fixProtocol(t,e){const i=e.indexOf("://");return i>=0&&(i>2&&"s"===e.charAt(i-1).toLowerCase()?(t.length<=2||!t.endsWith("s"))&&(t+="s"):t.length>2&&t.endsWith("s")&&(t=t.slice(0,-1)),e=e.substring(i+3)),t+"://"+e}get domain(){return this._domain}get port(){return this._port}toString(){return this._address}valueOf(){return this._address}constructor(t,e){this._address=t;let i=t.indexOf("/");if(i>=0&&(47===t.charCodeAt(i+1)?i>0?58===t.charCodeAt(i-1)&&(t=t.substring(i+2)):t=t.substring(2):i||(t=t.substring(1))),this._domain=t,this._port=e,i=t.lastIndexOf(":"),i>=0){const e=parseInt(t.substring(i+1));e&&e<=65535&&(this._port=e,this._domain=t.substring(0,i))}else i=t.indexOf("/"),i>=0&&(this._domain=t.substring(0,i))}}var r;!function(t){t.HESP="hesp",t.WEBRTS="webrts",t.WEBRTC="webrtc",t.META="meta",t.DATA="data"}(r||(r={}));var n=Object.freeze({__proto__:null,get Type(){return r},buildURL:function(t,e,i="wss"){const n=new URL(s.fixProtocol(i,e.host));if(n.pathname.length<=1)switch(t){case r.HESP:n.pathname="/hesp/"+e.streamName+"/index.json";break;case r.WEBRTC:n.pathname="/webrtc/"+e.streamName;break;case r.WEBRTS:n.pathname="/webrts/"+e.streamName;break;case r.META:n.pathname="/json_"+e.streamName+".js";break;case r.DATA:n.pathname="/"+e.streamName+".json";break;default:console.warn("Unknown url type "+t)}e.accessToken&&n.searchParams.set("id",e.accessToken);for(const t in e.query)n.searchParams.set(t,e.query[t]);return n}});class o{constructor(){this._events=new Map;let t=Object.getPrototypeOf(this);for(;t&&t!==Object.prototype;){for(const e of Object.getOwnPropertyNames(t))if(!(e.length<3)&&e.startsWith("on")&&t[e]instanceof Function){const i=new Set;this._events.set(e.substring(2).toLowerCase(),i);let s=t[e];Object.defineProperties(this,{[e]:{get:()=>(...t)=>{s&&s.call(this,...t);for(const e of i)e(...t)},set:t=>{s=t}}})}t=Object.getPrototypeOf(t)}}on(t,e,i){if(!e)throw Error("event to subscribe cannot be null");const s=this._event(t);s.add(e),i&&i.signal.addEventListener("abort",(()=>{s.delete(e)}))}once(t,e,i){if(!e)throw Error("event to subscribe cannot be null");const s=this._event(t);s.add((()=>{s.delete(e),e()})),i&&i.signal.addEventListener("abort",(()=>{s.delete(e)}))}off(t,e){if(!e)throw Error("event to unsubscribe cannot be null");this._event(t).delete(e)}_event(t){const e=this._events.get(t.toLowerCase());if(!e)throw Error("No event on"+t+" on class "+this.constructor.name);return e}}class h{get size(){return this._queue.length}get capacity(){return this._capacity}set capacity(t){this._capacity=t,null!=t&&this._queue.length>t&&this._queue.splice(0,this._queue.length-t)}get front(){return this._queue[0]}get back(){return this._queue[this._queue.length-1]}[Symbol.iterator](){return this._queue[Symbol.iterator]()}constructor(t){this._capacity=t,this._queue=new Array}push(t){return null!=this._capacity&&this._queue.push(t)>this._capacity&&this.pop(),this}pop(){return this._queue.shift()}clear(){return this._queue.length=0,this}}class a extends h{get minimum(){return this._min}get maximum(){return this._max}get average(){return null==this._average&&(this._average=this.size?this._sum/this.size:0),this._average}constructor(t){super(t),this._sum=0,this._min=0,this._max=0}push(t){return t>this._max?this._max=t:t<this._min&&(this._min=t),this._average=void 0,this._sum+=t,super.push(t),this}pop(){const t=super.pop();return t===this._max?this._max=Math.max(0,...this):t===this._min&&(this._min=Math.min(0,...this)),this._average=void 0,this._sum-=t||0,t}clear(){return this._min=this._max=this._sum=0,super.clear(),this}}const u={fromString(t){if(Array.isArray(t))return t;const e=new Array;let i,s=e;for(let r of t.toString().split("\n")){if(r=r.trim(),!r)continue;let t=r[0];const n=r.substring(r.indexOf("=")+1).trim();switch(t.toLowerCase()){case"a":if(!n)continue;t=this.addAttribute(s,n),e===s&&"fingerprint"===t.toLowerCase()&&(i=s.fingerprint);break;case"m":e.length&&i&&!e[e.length-1].fingerprint&&(s.fingerprint=i),e.push(s={m:n});break;default:s[t]=n}}return e.length&&i&&!e[e.length-1].fingerprint&&(s.fingerprint=i),e},toString(t){if("string"==typeof t)return t;const e=[];let i="v"in t?"v="+t.v+"\n":"";"o"in t&&(i+="o="+t.o+"\n"),"s"in t&&(i+="s="+t.s+"\n");const s=t;for(const r of Object.keys(t)){if("v"===r||"o"===r||"s"===r)continue;const t=s[r];if(null==t)continue;const n=parseInt(r);if(!isNaN(n)){e[n]=t;continue}const o=Array.isArray(t)&&t.length||1;for(let e=0;e<o;++e){const s=Array.isArray(t)&&t.length?t[e]:t;r.length>1?(i+="a="+r,s&&(i+=":")):i+=r+"=",i+=s+"\n"}}for(const t of e)i+=this.toString(t);return i},addAttribute(t,e){var i;const s=u.parseAttribute(e),r=null!==(i=s.value)&&void 0!==i?i:"",n=t,o=n[s.key];return o?Array.isArray(o)?o.push(r):r!==o&&(n[s.key]=[o,r]):n[s.key]=r,s.key},removeAttribute(t,e){const i=u.parseAttribute(e),s=t;if(void 0===i.value)return delete s[e],e;const r=s[e];if(Array.isArray(i.value)){const t=r.findIndex((t=>t===i.value));t>=0&&r.splice(t,1)}else r===i.value&&delete s[e];return i.key},parseAttribute(t){const e=t.indexOf(":");return{key:(e>=0?t.substring(0,e):t).trim(),value:e>=0?t.substring(e+1).trim():void 0}}};Object.freeze(u);const c=new TextDecoder,_=new TextEncoder,l=performance;function d(t,e){e=Object.assign({withType:!1,noEmptyString:!1},e);const i={};if(!t)return i;for(const[s,r]of f(t)){if(t=r,e.withType&&null!=t&&t.substring)if(t){const e=Number(t);if(isNaN(e))switch(t.toLowerCase()){case"true":t=!0;break;case"false":t=!1;break;case"null":t=null;break;case"undefined":t=void 0}else t=e}else e.noEmptyString&&(t=!0);i[s]?(Array.isArray(i[s])||(i[s]=new Array(i[s])),i[s].push(t)):i[s]=t}return i}function f(t){return t.entries?t.entries():Array.from({[Symbol.iterator]:function*(){for(const e in t)yield[e,t[e]]}})}var g=Object.freeze({__proto__:null,EMPTY_FUNCTION:()=>{},objectEntries:f,objectFrom:d,options:function(t=("undefined"==typeof location?void 0:location)){if(!t)return{};try{t=new URL(t).searchParams}catch(e){"string"==typeof t&&(t.startsWith("?")&&(t=t.substring(1)),t=new URLSearchParams(t))}return d(t,{withType:!0,noEmptyString:!0})},safePromise:function(t,e){let i;return Promise.race([e instanceof Promise?e:new Promise(e),new Promise(((e,s)=>i=setTimeout((()=>s("timed out in "+t+"ms")),t)))]).finally((()=>clearTimeout(i)))},stringify:function t(e,i={}){if(i=Object.assign({space:" ",decimal:2,recursive:1},i),null==e)return String(e);const s=e.error||e.message;if(s&&(e=s),e.toFixed)return e.toFixed(Number(i.decimal)||0);if(e.substring||!i.recursive)return String(e);const r=i.space||"";if(Array.isArray(e)){let s="";for(const n of e)s+=(s?",":"[")+r,s+=t(n,Object.assign(i,{recursive:i.recursive-1}));return s+=r+"]"}if(null!=e.byteLength&&(null==e?void 0:e[Symbol.iterator]))return c.decode(e);let n="";for(const s in e)n+=(n?",":"{")+r+s+":",n+=t(e[s],Object.assign(i,{recursive:i.recursive-1}));return n+(r+"}")},time:function(){return Math.floor(l.now())},timeOrigin:function(){return Math.floor(l.now()+l.timeOrigin)},toBin:function(t){return _.encode(t)}});class p extends o{onOpen(){}onMessage(t){}onClose(t){t&&console.error(t)}get binaryType(){return"arraybuffer"}get url(){var t,e;return null!==(e=null===(t=this._ws)||void 0===t?void 0:t.url)&&void 0!==e?e:""}get extensions(){var t,e;return null!==(e=null===(t=this._ws)||void 0===t?void 0:t.extensions)&&void 0!==e?e:""}get protocol(){var t,e;return null!==(e=null===(t=this._ws)||void 0===t?void 0:t.protocol)&&void 0!==e?e:""}get opened(){return this._opened}get readyState(){return this._ws?this._ws.readyState:3}get closed(){return this._closed}get bufferedAmount(){var t;return this._queueingBytes+((null===(t=this._ws)||void 0===t?void 0:t.bufferedAmount)||0)}get queueing(){return this._queueing}constructor(t,e){super(),this._queueing=[],this._queueingBytes=0,this._opened=!1,this._closed=!0,t&&this.open(t,e)}open(t,e){this._closed=!1;const i=this._ws=new WebSocket(t,e);return i.binaryType=this.binaryType,i.onmessage=t=>this.onMessage(t.data),i.onclose=e=>{this._opened?1e3===e.code||1005===e.code?this.close(t.toString()+" shutdown"):this.close(t.toString()+" disconnection ("+String(e.reason||e.code)+")"):this.close(t.toString()+" connection failed ("+String(e.reason||e.code)+")")},i.onopen=t=>{this._opened=!0,this.flush(),this.onOpen()},this}send(t,e=!1){if(!this._ws)throw Error("Open socket before to send data");return e||!this._opened?(this._queueing.push(t),this._queueingBytes+="string"==typeof t?t.length:t.byteLength):this._ws.send(t),this}flush(){if(this._ws)for(const t of this._queueing)this._ws.send(t);this._queueing.length=0,this._queueingBytes=0}close(t){this._ws&&!this._closed&&(this._closed=!0,this._ws.onopen=this._ws.onclose=this._ws.onmessage=null,this._ws.close(),this._opened=!1,this._queueing.length=0,this._queueingBytes=0,this.onClose(t))}}const b="1.2.0";export{t as BinaryReader,e as BinaryWriter,i as BitReader,n as Connect,o as EventEmitter,s as NetAddress,a as Numbers,h as Queue,u as SDP,g as Util,b as VERSION,p as WebSocketReliable};//# sourceMappingURL=web-utils.min.js.map | ||
const t=new TextDecoder;class e{constructor(t){this._data="buffer"in t?new Uint8Array(t.buffer,t.byteOffset,t.byteLength):new Uint8Array(t),this._size=this._data.byteLength,this._position=0,this._view=new DataView(this._data.buffer,this._data.byteOffset,this._size)}data(){return this._data}size(){return this._size}available(){return this._size-this._position}value(t=this._position){return this._data[t]}position(){return this._position}reset(t=0){this._position=Math.max(0,t>this._size?this._size:t)}shrink(t){const e=this._size-this._position;return t>e?e:(this._size=this._position+t,t)}next(t=1){const e=this._size-this._position;return t>e&&(t=e),this._position=Math.max(0,this._position+t),t}read8(){return 1===this.next(1)?this._view.getUint8(this._position-1):0}read16(){return 2===this.next(2)?this._view.getUint16(this._position-2):0}read24(){return 3===this.next(3)?this._view.getUint16(this._position-3)<<8|255&this._view.getUint8(this._position-1):0}read32(){return 4===this.next(4)?this._view.getUint32(this._position-4):0}read64(){return 8!==this.next(8)?0:4294967296*this._view.getUint32(this._position-8)+this._view.getUint32(this._position-4)}readFloat(){return 4===this.next(4)?this._view.getFloat32(this._position-4):0}readDouble(){return 8===this.next(8)?this._view.getFloat64(this._position-8):0}read7Bit(t=5){let e=0,i=1;for(;this.available();){const t=this.read8();if(e+=(127&t)*i,!(128&t))break;i*=128}return e}readString(){let e=this._position;for(;e<this._size&&this._data[e];)++e;const i=this.read(e-this._position);return this.next(),t.decode(i)}readHex(t){let e="";for(;t-- >0;)e+=("0"+this.read8().toString(16)).slice(-2);return e}read(t=this.available()){if(this.available()<t)return new Uint8Array(t);const e=this._position;return this._data.subarray(e,Math.max(e,this._position+=t))}}const i=new TextEncoder;class s{get view(){return this._view||(this._view=new DataView(this._data.buffer,this._data.byteOffset,this._data.byteLength)),this._view}get capacity(){return this._data.byteLength}constructor(t=64,e=0,i){"number"==typeof t?(this._data=new Uint8Array(t),this._size=0):"buffer"in t?(this._data=new Uint8Array(t.buffer,t.byteOffset,t.byteLength),this._size=t.byteLength):(this._isConst=!0,null==i&&(i=t.byteLength),this._data=new Uint8Array(t,e,i),this._size=0)}data(){return new Uint8Array(this._data.buffer,this._data.byteOffset,this._size)}size(){return this._size||0}next(t=1){return this.reserve(this._size+=t)}clear(t=0){return this.reserve(this._size=t)}write(t){return this.reserve(this._size+t.length),this._data.set(t,this._size),this._size+=t.length,this}write8(t){return t>255&&(t=255),this.reserve(this._size+1),this._data[this._size++]=t,this}write16(t){return t>65535&&(t=65535),this.reserve(this._size+2),this.view.setUint16(this._size,t),this._size+=2,this}write24(t){return t>16777215&&(t=16777215),this.reserve(this._size+3),this.view.setUint16(this._size,t>>8),this.view.setUint8(this._size+=2,255&t),++this._size,this}write32(t){return t>4294967295&&(t=4294967295),this.reserve(this._size+4),this.view.setUint32(this._size,t),this._size+=4,this}write64(t){return this.write32(t/4294967296),this.write32(4294967295&t)}writeFloat(t){return this.reserve(this._size+4),this.view.setFloat32(this._size,t),this._size+=4,this}writeDouble(t){return this.reserve(this._size+8),this.view.setFloat64(this._size,t),this._size+=8,this}write7Bit(t){let e=127&t;for(;t=Math.floor(t/128);)this.write8(128|e),e=127&t;return this.write8(e)}writeString(t){return this.write(i.encode(t)).write8(0)}writeHex(t){for(let e=0;e<t.length;e+=2)this.write8(parseInt(t.substring(e,e+2),16));return this}reserve(t){if(!this._data)throw Error("buffer not writable");if(t<=this._data.byteLength)return this;if(this._isConst)throw Error("writing exceeds maximum "+this._data.byteLength+" bytes limit");--t,t|=t>>1,t|=t>>2,t|=t>>4,t|=t>>8,t|=t>>16,++t;const e=new Uint8Array(t);return e.set(this._data),this._data=e,this._view=void 0,this}}class r{constructor(t){this._data="buffer"in t?new Uint8Array(t.buffer,t.byteOffset,t.byteLength):new Uint8Array(t),this._size=this._data.byteLength,this._position=0,this._bit=0}data(){return this._data}size(){return this._size}available(){return 8*(this._size-this._position)-this._bit}next(t=1){let e=0;for(;this._position!==this._size&&t--;)++e,8==++this._bit&&(this._bit=0,++this._position);return e}read(t=1){let e=0;for(;this._position!==this._size&&t--;)e<<=1,this._data[this._position]&128>>this._bit++&&(e|=1),8===this._bit&&(this._bit=0,++this._position);return e}read8(){return this.read(8)}read16(){return this.read(16)}read24(){return this.read(24)}read32(){return this.read(32)}}class n{static fixProtocol(t,e){const i=e.indexOf("://");return i>=0&&(i>2&&"s"===e.charAt(i-1).toLowerCase()?(t.length<=2||!t.endsWith("s"))&&(t+="s"):t.length>2&&t.endsWith("s")&&(t=t.slice(0,-1)),e=e.substring(i+3)),t+"://"+e}get domain(){return this._domain}get port(){return this._port}toString(){return this._address}valueOf(){return this._address}constructor(t,e){this._address=t;let i=t.indexOf("/");if(i>=0&&(47===t.charCodeAt(i+1)?i>0?58===t.charCodeAt(i-1)&&(t=t.substring(i+2)):t=t.substring(2):i||(t=t.substring(1))),this._domain=t,this._port=e,i=t.lastIndexOf(":"),i>=0){const e=parseInt(t.substring(i+1));e&&e<=65535&&(this._port=e,this._domain=t.substring(0,i))}else i=t.indexOf("/"),i>=0&&(this._domain=t.substring(0,i))}}function o(t,e,i,s){return new(i||(i=Promise))((function(r,n){function o(t){try{h(s.next(t))}catch(t){n(t)}}function a(t){try{h(s.throw(t))}catch(t){n(t)}}function h(t){var e;t.done?r(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(o,a)}h((s=s.apply(t,e||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;const a=new TextDecoder,h=new TextEncoder,u=performance;function c(){return Math.floor(u.now())}function _(t,e){e=Object.assign({withType:!1,noEmptyString:!1},e);const i={};if(!t)return i;for(const[s,r]of l(t)){if(t=r,e.withType&&null!=t&&t.substring)if(t){const e=Number(t);if(isNaN(e))switch(t.toLowerCase()){case"true":t=!0;break;case"false":t=!1;break;case"null":t=null;break;case"undefined":t=void 0}else t=e}else e.noEmptyString&&(t=!0);i[s]?(Array.isArray(i[s])||(i[s]=new Array(i[s])),i[s].push(t)):i[s]=t}return i}function l(t){return t.entries?t.entries():Array.from({[Symbol.iterator]:function*(){for(const e in t)yield[e,t[e]]}})}function d(t){const e=t.lastIndexOf(".");return e>=0&&e>t.lastIndexOf("/")?t.substring(e):""}var f,g=Object.freeze({__proto__:null,EMPTY_FUNCTION:()=>{},fetch:function(t,e){return o(this,void 0,void 0,(function*(){const i=yield self.fetch(t,e);if(i.status>=300){if(i.body)throw(yield i.text())||i.statusText;throw i.statusText}return i}))},objectEntries:l,objectFrom:_,options:function(t=("undefined"==typeof location?void 0:location)){if(!t)return{};try{t=new URL(t).searchParams}catch(e){"string"==typeof t&&(t.startsWith("?")&&(t=t.substring(1)),t=new URLSearchParams(t))}return _(t,{withType:!0,noEmptyString:!0})},parseExtension:d,safePromise:function(t,e){let i;return Promise.race([e instanceof Promise?e:new Promise(e),new Promise(((e,s)=>i=setTimeout((()=>s("timed out in "+t+"ms")),t)))]).finally((()=>clearTimeout(i)))},sleep:function(t){return new Promise((e=>{setTimeout(e,t)}))},stringify:function t(e,i={}){if(i=Object.assign({space:" ",decimal:2,recursive:1,noBin:!1},i),null==e)return String(e);const s=e.error||e.message;if(s&&(e=s),e.toFixed)return e.toFixed(Number(i.decimal)||0);if("boolean"==typeof e||e.substring||!i.recursive)return String(e);const r=i.space||"";if(Array.isArray(e)){let s="";for(const n of e)s+=(s?",":"[")+r,s+=t(n,Object.assign(Object.assign({},i),{recursive:i.recursive-1}));return s+=r+"]"}if(null!=e.byteLength&&(null==e?void 0:e[Symbol.iterator]))return a.decode(e);let n="";if(i.noBin)return"["+e.byteLength+"#bytes]";for(const s in e)n+=(n?",":"{")+r+s+":",n+=t(e[s],Object.assign(Object.assign({},i),{recursive:i.recursive-1}));return n+(r+"}")},time:c,timeOrigin:function(){return Math.floor(u.now()+u.timeOrigin)},toBin:function(t){return h.encode(t)}});!function(t){t.HESP="HESP",t.WEBRTS="WebRTS",t.WEBRTC="WebRTC",t.META="Meta",t.DATA="Data"}(f||(f={}));var p=Object.freeze({__proto__:null,get Type(){return f},buildURL:function(t,e,i="wss"){const s=new URL(n.fixProtocol(i,e.host));if(s.pathname.length<=1)switch(t){case f.HESP:s.pathname="/hesp/"+e.streamName+"/index.json";break;case f.WEBRTC:s.pathname="/webrtc/"+e.streamName;break;case f.WEBRTS:s.pathname="/webrts/"+e.streamName;break;case f.META:s.pathname="/json_"+e.streamName+".js";break;case f.DATA:s.pathname="/"+e.streamName+".json";break;default:console.warn("Unknown url type "+t)}e.accessToken&&s.searchParams.set("id",e.accessToken);for(const t in e.query)s.searchParams.set(t,e.query[t]);return e.streamName.substring(0,e.streamName.length-d(e.streamName).length),s}});class b{constructor(){this._events=new Map;let t=Object.getPrototypeOf(this);for(;t&&t!==Object.prototype;){for(const e of Object.getOwnPropertyNames(t))if(!(e.length<3)&&e.startsWith("on")&&t[e]instanceof Function){const i=new Set;this._events.set(e.substring(2).toLowerCase(),i);let s=t[e];const r=(...t)=>{s&&s.call(this,...t);for(const e of i)e(...t)};Object.defineProperties(this,{[e]:{get:()=>r,set:t=>{s=t}}})}t=Object.getPrototypeOf(t)}}on(t,e,i){if(!e)throw Error("event to subscribe cannot be null");const s=this._event(t);s.add(e),i&&i.signal.addEventListener("abort",(()=>s.delete(e)),{once:!0})}once(t,e,i){if(!e)throw Error("event to subscribe cannot be null");const s=this._event(t);s.add(((...t)=>{s.delete(e),e(...t)})),i&&i.signal.addEventListener("abort",(()=>s.delete(e)),{once:!0})}off(t,e){if(!e)throw Error("event to unsubscribe cannot be null");this._event(t).delete(e)}_event(t){const e=this._events.get(t.toLowerCase());if(!e)throw Error("No event on"+t+" on class "+this.constructor.name);return e}}class y{get size(){return this._queue.length}get capacity(){return this._capacity}set capacity(t){this._capacity=t,null!=t&&this._queue.length>t&&this._queue.splice(0,this._queue.length-t)}get front(){return this._queue[0]}get back(){return this._queue[this._queue.length-1]}[Symbol.iterator](){return this._queue[Symbol.iterator]()}constructor(t){this._capacity=t,this._queue=new Array}push(t){return null!=this._capacity&&this._queue.push(t)>this._capacity&&this.pop(),this}pop(){return this._queue.shift()}clear(){return this._queue.length=0,this}}class w extends y{get minimum(){return this._min}get maximum(){return this._max}get average(){return null==this._average&&(this._average=this.size?this._sum/this.size:0),this._average}constructor(t){super(t),this._sum=0,this._min=0,this._max=0}push(t){return t>this._max?this._max=t:t<this._min&&(this._min=t),this._average=void 0,this._sum+=t,super.push(t),this}pop(){const t=super.pop();return t===this._max?this._max=Math.max(0,...this):t===this._min&&(this._min=Math.min(0,...this)),this._average=void 0,this._sum-=t||0,t}clear(){return this._min=this._max=this._sum=0,super.clear(),this}}const v={fromString(t){if(Array.isArray(t))return t;const e=new Array;let i,s=e;for(let r of t.toString().split("\n")){if(r=r.trim(),!r)continue;let t=r[0];const n=r.substring(r.indexOf("=")+1).trim();switch(t.toLowerCase()){case"a":if(!n)continue;t=this.addAttribute(s,n),e===s&&"fingerprint"===t.toLowerCase()&&(i=s.fingerprint);break;case"m":e.length&&i&&!e[e.length-1].fingerprint&&(s.fingerprint=i),e.push(s={m:n});break;default:s[t]=n}}return e.length&&i&&!e[e.length-1].fingerprint&&(s.fingerprint=i),e},toString(t){if("string"==typeof t)return t;const e=[];let i="v"in t?"v="+t.v+"\n":"";"o"in t&&(i+="o="+t.o+"\n"),"s"in t&&(i+="s="+t.s+"\n");const s=t;for(const r of Object.keys(t)){if("v"===r||"o"===r||"s"===r)continue;const t=s[r];if(null==t)continue;const n=parseInt(r);if(!isNaN(n)){e[n]=t;continue}const o=Array.isArray(t)&&t.length||1;for(let e=0;e<o;++e){const s=Array.isArray(t)&&t.length?t[e]:t;r.length>1?(i+="a="+r,s&&(i+=":")):i+=r+"=",i+=s+"\n"}}for(const t of e)i+=this.toString(t);return i},addAttribute(t,e){var i;const s=v.parseAttribute(e),r=null!==(i=s.value)&&void 0!==i?i:"",n=t,o=n[s.key];return o?Array.isArray(o)?o.push(r):r!==o&&(n[s.key]=[o,r]):n[s.key]=r,s.key},removeAttribute(t,e){const i=v.parseAttribute(e),s=t;if(void 0===i.value)return delete s[e],e;const r=s[e];if(Array.isArray(i.value)){const t=r.findIndex((t=>t===i.value));t>=0&&r.splice(t,1)}else r===i.value&&delete s[e];return i.key},parseAttribute(t){const e=t.indexOf(":");return{key:(e>=0?t.substring(0,e):t).trim(),value:e>=0?t.substring(e+1).trim():void 0}}};Object.freeze(v);class m{onBytes(t){}get delta(){return this._delta}constructor(t=1e3){this._time=c(),this._value=NaN,this._delta=t,this._bytes=0}value(){return Math.round(this.exact())}exact(){const t=c(),e=t-this._time;return(e>this._delta||isNaN(this._value))&&(this._value=1e3*this._bytes/e,this._bytes=0,this._time=t),this._value}addBytes(t){return this._bytes+=t,this.onBytes(t),this}}class x extends b{onOpen(){}onMessage(t){}onClose(t){t&&console.error(t)}get binaryType(){return"arraybuffer"}get recvByteRate(){return this._recvByteRate.value()}get sendByteRate(){return this._sendByteRate.value()}get url(){var t,e;return null!==(e=null===(t=this._ws)||void 0===t?void 0:t.url)&&void 0!==e?e:""}get extensions(){var t,e;return null!==(e=null===(t=this._ws)||void 0===t?void 0:t.extensions)&&void 0!==e?e:""}get protocol(){var t,e;return null!==(e=null===(t=this._ws)||void 0===t?void 0:t.protocol)&&void 0!==e?e:""}get opened(){return this._opened}get readyState(){return this._ws?this._ws.readyState:3}get closed(){return this._closed}get bufferedAmount(){var t;return this._queueingBytes+((null===(t=this._ws)||void 0===t?void 0:t.bufferedAmount)||0)}get queueing(){return this._queueing}constructor(t,e){super(),this._queueing=[],this._queueingBytes=0,this._opened=!1,this._closed=!0,this._recvByteRate=new m,this._sendByteRate=new m,t&&this.open(t,e)}open(t,e){this._closed=!1;const i=this._ws=new WebSocket(t,e);return i.binaryType=this.binaryType,i.onmessage=t=>{var e;this._recvByteRate.addBytes(null!==(e=t.data.byteLength)&&void 0!==e?e:t.data.length),this.onMessage(t.data)},i.onclose=e=>{this._opened?1e3===e.code||1005===e.code?this.close(t.toString()+" shutdown"):this.close(t.toString()+" disconnection ("+String(e.reason||e.code)+")"):this.close(t.toString()+" connection failed ("+String(e.reason||e.code)+")")},i.onopen=t=>{this._opened=!0,this.flush(),this.onOpen()},this}send(t,e=!1){if(!this._ws)throw Error("Open socket before to send data");return e||!this._opened?(this._queueing.push(t),this._queueingBytes+="string"==typeof t?t.length:t.byteLength):this._send(t),this}flush(){if(this._ws)for(const t of this._queueing)this._send(t);this._queueing.length=0,this._queueingBytes=0}close(t){this._ws&&!this._closed&&(this._closed=!0,this._ws.onopen=this._ws.onclose=this._ws.onmessage=null,this._ws.close(),this._opened=!1,this._queueing.length=0,this._queueingBytes=0,this.onClose(t))}_send(t){this._ws&&(this._sendByteRate.addBytes("string"==typeof t?t.length:t.byteLength),this._ws.send(t))}}const z="1.3.0";export{e as BinaryReader,s as BinaryWriter,r as BitReader,p as Connect,b as EventEmitter,n as NetAddress,w as Numbers,y as Queue,v as SDP,g as Util,z as VERSION,x as WebSocketReliable};//# sourceMappingURL=web-utils.min.js.map |
{ | ||
"name": "@ceeblue/web-utils", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "Ceeblue web framework", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -1,21 +0,65 @@ | ||
# Ceeblue web framework | ||
[Usage](#usage) | [Building locally](#building-locally) | [Documentation](#documentation) | [Contribution](#contribution) | [License](#license) | ||
This is a web framework for CeeblueTV's web libraries. It is a collection of tools and utilities that are used across all of CeeblueTV's web projects. | ||
# Ceeblue Web Utilities | ||
## Installation | ||
This is a Web base compoments for CeeblueTV's web libraries : a collection of tools and utilities used across all of CeeblueTV's web projects. | ||
You can install the package using npm: | ||
## Usage | ||
Add the library as a dependency to your npm project using: | ||
```bash | ||
npm install @ceeblue/web-utils | ||
``` | ||
Then [import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) the library into your project, for example: | ||
```javascript | ||
import { Util, ILog } from '@ceeblue/web-utils'; | ||
``` | ||
> 💡 **TIP** | ||
> | ||
> If your project uses [TypeScript](https://www.typescriptlang.org/), it is recommended to set `"target": "ES6"` in your configuration to align with our usage of ES6 features and ensures that your build will succeed (for those requiring a backwards-compatible [UMD](https://github.com/umdjs/umd) version, a [local build](#building-locally) is advised). | ||
> Then Defining the compiler option `"moduleResolution": "Node"` in **tsconfig.json** helps with import errors by ensuring that TypeScript uses the correct strategy for resolving imports based on the targeted Node.js version. | ||
> ```json | ||
> { | ||
> "compilerOptions": { | ||
> "target": "ES6", | ||
> "moduleResolution": "Node" | ||
> } | ||
> } | ||
> ``` | ||
Or manually add it to your `package.json` file: | ||
## Building locally | ||
```json | ||
{ | ||
"dependencies": { | ||
"@ceeblue/web-utils": "latest" | ||
} | ||
} | ||
1. [Clone](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) this repository | ||
2. Enter the `webr-utils` folder and run `npm install` to install packages dependencies. | ||
3. Execute `npm run build`. The output will be five files placed in the **/dist/** folder: | ||
- **web-utils.d.ts** Typescript definitions file | ||
- **web-utils.js**: Bundled JavaScript library | ||
- **web-utils.js.map**: Source map that associates the bundled library with the original source files | ||
- **web-utils.min.js** Minified version of the library, optimized for size | ||
- **web-utils.min.js.map** Source map that associates the minified library with the original source files | ||
``` | ||
git clone https://github.com/CeeblueTV/web-utils.git | ||
cd web-utils | ||
npm install | ||
npm run build | ||
``` | ||
## Documentation | ||
This monorepo also contains built-in documentation about the APIs in the library, which can be built using the following npm command: | ||
``` | ||
npm run build:docs | ||
``` | ||
You can access the documentation by opening the index.html file in the docs folder with your browser (`./docs/index.html`), or if you have installed and started the [http-server package](https://www.npmjs.com/package/http-server) by navigating to: | ||
``` | ||
http://localhost:8080/docs/ | ||
``` | ||
## Contribution | ||
All contributions are welcome. Please see [our contribution guide](/CONTRIBUTING.md) for details. | ||
## License | ||
By contributing code to this project, you agree to license your contribution under the [GNU Affero General Public License](/LICENSE). |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
342023
2432
65
1