@ceeblue/web-utils 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -11
- package/dist/web-utils.d.ts +41 -18
- package/dist/web-utils.js +390 -275
- package/dist/web-utils.js.map +1 -1
- package/dist/web-utils.min.js +1 -1
- package/dist/web-utils.min.js.map +1 -1
- package/package.json +1 -1
package/dist/web-utils.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* BinaryReader allows to read binary data
|
|
8
8
|
*/
|
|
9
|
+
const _decoder$1 = new TextDecoder();
|
|
9
10
|
class BinaryReader {
|
|
10
11
|
constructor(data) {
|
|
11
12
|
this._data =
|
|
@@ -30,7 +31,7 @@ class BinaryReader {
|
|
|
30
31
|
return this._position;
|
|
31
32
|
}
|
|
32
33
|
reset(position = 0) {
|
|
33
|
-
this._position = position > this._size ? this._size : position;
|
|
34
|
+
this._position = Math.max(0, position > this._size ? this._size : position);
|
|
34
35
|
}
|
|
35
36
|
shrink(available) {
|
|
36
37
|
const rest = this._size - this._position;
|
|
@@ -45,7 +46,7 @@ class BinaryReader {
|
|
|
45
46
|
if (count > rest) {
|
|
46
47
|
count = rest;
|
|
47
48
|
}
|
|
48
|
-
this._position
|
|
49
|
+
this._position = Math.max(0, this._position + count);
|
|
49
50
|
return count;
|
|
50
51
|
}
|
|
51
52
|
read8() {
|
|
@@ -62,6 +63,12 @@ class BinaryReader {
|
|
|
62
63
|
read32() {
|
|
63
64
|
return this.next(4) === 4 ? this._view.getUint32(this._position - 4) : 0;
|
|
64
65
|
}
|
|
66
|
+
read64() {
|
|
67
|
+
if (this.next(8) !== 8) {
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
return this._view.getUint32(this._position - 8) * 4294967296 + this._view.getUint32(this._position - 4);
|
|
71
|
+
}
|
|
65
72
|
readFloat() {
|
|
66
73
|
return this.next(4) === 4 ? this._view.getFloat32(this._position - 4) : 0;
|
|
67
74
|
}
|
|
@@ -69,51 +76,51 @@ class BinaryReader {
|
|
|
69
76
|
return this.next(8) === 8 ? this._view.getFloat64(this._position - 8) : 0;
|
|
70
77
|
}
|
|
71
78
|
read7Bit(bytes = 5) {
|
|
72
|
-
if (bytes > 5) {
|
|
73
|
-
throw Error("BinaryReader in JS can't decode more than 32 usefull bits");
|
|
74
|
-
}
|
|
75
|
-
if (!(bytes > 0)) {
|
|
76
|
-
// negation to catch NaN value
|
|
77
|
-
throw Error('Have to indicate a positive number of bytes to decode');
|
|
78
|
-
}
|
|
79
79
|
let result = 0;
|
|
80
|
-
let
|
|
81
|
-
|
|
82
|
-
byte = this.read8();
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
let factor = 1;
|
|
81
|
+
while (this.available()) {
|
|
82
|
+
const byte = this.read8();
|
|
83
|
+
result += (byte & 0x7f) * factor;
|
|
84
|
+
if (!(byte & 0x80)) {
|
|
85
|
+
break;
|
|
85
86
|
}
|
|
86
|
-
|
|
87
|
-
}
|
|
87
|
+
factor *= 128;
|
|
88
|
+
}
|
|
88
89
|
return result;
|
|
89
90
|
}
|
|
90
91
|
readString() {
|
|
91
|
-
|
|
92
|
+
let i = this._position;
|
|
93
|
+
while (i < this._size && this._data[i]) {
|
|
94
|
+
++i;
|
|
95
|
+
}
|
|
96
|
+
const result = this.read(i - this._position);
|
|
97
|
+
this.next(); // skip the 0 termination
|
|
98
|
+
return _decoder$1.decode(result);
|
|
92
99
|
}
|
|
93
100
|
readHex(size) {
|
|
94
101
|
let hex = '';
|
|
95
|
-
while (size--) {
|
|
102
|
+
while (size-- > 0) {
|
|
96
103
|
hex += ('0' + this.read8().toString(16)).slice(-2);
|
|
97
104
|
}
|
|
98
105
|
return hex;
|
|
99
106
|
}
|
|
100
107
|
/**
|
|
101
|
-
* Read bytes, to convert bytes to string use String.fromCharCode(...reader.read(size))
|
|
108
|
+
* Read bytes, to convert bytes to string use String.fromCharCode(...reader.read(size)) or Util.stringify
|
|
102
109
|
* @param {UInt32} size
|
|
103
110
|
*/
|
|
104
111
|
read(size = this.available()) {
|
|
105
112
|
if (this.available() < size) {
|
|
106
113
|
return new Uint8Array(size); // default value = empty bytearray!
|
|
107
114
|
}
|
|
108
|
-
const
|
|
109
|
-
this._position += size;
|
|
110
|
-
return value;
|
|
115
|
+
const pos = this._position;
|
|
116
|
+
return this._data.subarray(pos, Math.max(pos, (this._position += size)));
|
|
111
117
|
}
|
|
112
118
|
}/**
|
|
113
119
|
* Copyright 2024 Ceeblue B.V.
|
|
114
120
|
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
|
|
115
121
|
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
|
|
116
122
|
*/
|
|
123
|
+
const _encoder$1 = new TextEncoder();
|
|
117
124
|
/**
|
|
118
125
|
* BinaryWriter allows to write data in its binary form
|
|
119
126
|
*/
|
|
@@ -161,16 +168,12 @@ class BinaryWriter {
|
|
|
161
168
|
clear(size = 0) {
|
|
162
169
|
return this.reserve((this._size = size));
|
|
163
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Write binary data
|
|
173
|
+
* @param data
|
|
174
|
+
*/
|
|
164
175
|
write(data) {
|
|
165
176
|
this.reserve(this._size + data.length);
|
|
166
|
-
if (typeof data === 'string') {
|
|
167
|
-
// beware here support just the 255 first bytes (compatible Latin-1)
|
|
168
|
-
for (let i = 0; i < data.length; ++i) {
|
|
169
|
-
const value = data.charCodeAt(i);
|
|
170
|
-
this._data[this._size++] = value > 255 ? 32 : value;
|
|
171
|
-
}
|
|
172
|
-
return this;
|
|
173
|
-
}
|
|
174
177
|
this._data.set(data, this._size);
|
|
175
178
|
this._size += data.length;
|
|
176
179
|
return this;
|
|
@@ -215,6 +218,10 @@ class BinaryWriter {
|
|
|
215
218
|
this._size += 4;
|
|
216
219
|
return this;
|
|
217
220
|
}
|
|
221
|
+
write64(value) {
|
|
222
|
+
this.write32(value / 4294967296);
|
|
223
|
+
return this.write32(value & 0xffffffff);
|
|
224
|
+
}
|
|
218
225
|
writeFloat(value) {
|
|
219
226
|
this.reserve(this._size + 4);
|
|
220
227
|
this.view.setFloat32(this._size, value);
|
|
@@ -227,35 +234,17 @@ class BinaryWriter {
|
|
|
227
234
|
this._size += 8;
|
|
228
235
|
return this;
|
|
229
236
|
}
|
|
230
|
-
write7Bit(value
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
throw Error('Have to indicate a positive number of bytes to encode');
|
|
237
|
+
write7Bit(value) {
|
|
238
|
+
let byte = value & 0x7f;
|
|
239
|
+
while ((value = Math.floor(value / 0x80))) {
|
|
240
|
+
// equivalent to >>=7 for JS!
|
|
241
|
+
this.write8(0x80 | byte);
|
|
242
|
+
byte = value & 0x7f;
|
|
237
243
|
}
|
|
238
|
-
|
|
239
|
-
const front = value > 0xffffffff ? 0x100 : value >>> bits;
|
|
240
|
-
if (front) {
|
|
241
|
-
++bits;
|
|
242
|
-
if (front > 0xff) {
|
|
243
|
-
value = 0xffffffff;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
else {
|
|
247
|
-
while ((bits -= 7) && !(value >>> bits)) {
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
while (bits > 1) {
|
|
252
|
-
this.write8(0x80 | ((value >>> bits) & 0xff));
|
|
253
|
-
bits -= 7;
|
|
254
|
-
}
|
|
255
|
-
return this.write8(value & (bits ? 0xff : 0x7f));
|
|
244
|
+
return this.write8(byte);
|
|
256
245
|
}
|
|
257
246
|
writeString(value) {
|
|
258
|
-
return this.
|
|
247
|
+
return this.write(_encoder$1.encode(value)).write8(0);
|
|
259
248
|
}
|
|
260
249
|
writeHex(value) {
|
|
261
250
|
for (let i = 0; i < value.length; i += 2) {
|
|
@@ -265,13 +254,13 @@ class BinaryWriter {
|
|
|
265
254
|
}
|
|
266
255
|
reserve(size) {
|
|
267
256
|
if (!this._data) {
|
|
268
|
-
throw
|
|
257
|
+
throw Error('buffer not writable');
|
|
269
258
|
}
|
|
270
259
|
if (size <= this._data.byteLength) {
|
|
271
260
|
return this;
|
|
272
261
|
}
|
|
273
262
|
if (this._isConst) {
|
|
274
|
-
throw
|
|
263
|
+
throw Error('writing exceeds maximum ' + this._data.byteLength + ' bytes limit');
|
|
275
264
|
}
|
|
276
265
|
--size;
|
|
277
266
|
size |= size >> 1;
|
|
@@ -476,7 +465,273 @@ class NetAddress {
|
|
|
476
465
|
}
|
|
477
466
|
}
|
|
478
467
|
}
|
|
479
|
-
}
|
|
468
|
+
}/******************************************************************************
|
|
469
|
+
Copyright (c) Microsoft Corporation.
|
|
470
|
+
|
|
471
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
472
|
+
purpose with or without fee is hereby granted.
|
|
473
|
+
|
|
474
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
475
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
476
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
477
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
478
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
479
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
480
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
481
|
+
***************************************************************************** */
|
|
482
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
486
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
487
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
488
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
489
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
490
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
491
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
496
|
+
var e = new Error(message);
|
|
497
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
498
|
+
};/**
|
|
499
|
+
* Copyright 2024 Ceeblue B.V.
|
|
500
|
+
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
|
|
501
|
+
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
|
|
502
|
+
*/
|
|
503
|
+
const _decoder = new TextDecoder();
|
|
504
|
+
const _encoder = new TextEncoder();
|
|
505
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
506
|
+
const _perf = performance; // to increase x10 now performance!
|
|
507
|
+
/**
|
|
508
|
+
* Some basic utility functions
|
|
509
|
+
*/
|
|
510
|
+
/**
|
|
511
|
+
* An empty lambda function, pratical to disable default behavior of function or events which are not expected to be null
|
|
512
|
+
* @example
|
|
513
|
+
* console.log = Util.EMPTY_FUNCTION; // disable logs without breaking calls
|
|
514
|
+
*/
|
|
515
|
+
const EMPTY_FUNCTION = () => { };
|
|
516
|
+
/**
|
|
517
|
+
* Efficient and high resolution timestamp in milliseconds elapsed since {@link Util.timeOrigin}
|
|
518
|
+
*/
|
|
519
|
+
function time() {
|
|
520
|
+
return Math.floor(_perf.now());
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Time origin represents the time when the application has started
|
|
524
|
+
*/
|
|
525
|
+
function timeOrigin() {
|
|
526
|
+
return Math.floor(_perf.now() + _perf.timeOrigin);
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Parse query and returns it in an easy-to-use Javascript object form
|
|
530
|
+
* @param urlOrQueryOrSearch string, url, or searchParams containing query. If not set it uses `location.search` to determinate query.
|
|
531
|
+
* @returns An javascript object containing each option
|
|
532
|
+
*/
|
|
533
|
+
function options(urlOrQueryOrSearch = typeof location === 'undefined'
|
|
534
|
+
? undefined
|
|
535
|
+
: location) {
|
|
536
|
+
if (!urlOrQueryOrSearch) {
|
|
537
|
+
return {};
|
|
538
|
+
}
|
|
539
|
+
try {
|
|
540
|
+
const url = urlOrQueryOrSearch;
|
|
541
|
+
urlOrQueryOrSearch = new URL(url).searchParams;
|
|
542
|
+
}
|
|
543
|
+
catch (e) {
|
|
544
|
+
if (typeof urlOrQueryOrSearch == 'string') {
|
|
545
|
+
if (urlOrQueryOrSearch.startsWith('?')) {
|
|
546
|
+
urlOrQueryOrSearch = urlOrQueryOrSearch.substring(1);
|
|
547
|
+
}
|
|
548
|
+
urlOrQueryOrSearch = new URLSearchParams(urlOrQueryOrSearch);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
// works same if urlOrQueryOrSearch is null, integer, or a already object etc...
|
|
552
|
+
return objectFrom(urlOrQueryOrSearch, { withType: true, noEmptyString: true });
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Returns an easy-to-use Javascript object something iterable, such as a Map, Set, or Array
|
|
556
|
+
* @param value iterable input
|
|
557
|
+
* @param params.withType `false`, if set it tries to cast string value to a JS number/boolean/undefined/null type.
|
|
558
|
+
* @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
|
|
559
|
+
* @returns An javascript object
|
|
560
|
+
*/
|
|
561
|
+
function objectFrom(value, params) {
|
|
562
|
+
params = Object.assign({ withType: false, noEmptyString: false }, params);
|
|
563
|
+
const obj = {};
|
|
564
|
+
if (!value) {
|
|
565
|
+
return obj;
|
|
566
|
+
}
|
|
567
|
+
for (const [key, val] of objectEntries(value)) {
|
|
568
|
+
value = val;
|
|
569
|
+
if (params.withType && value != null && value.substring) {
|
|
570
|
+
if (value) {
|
|
571
|
+
const number = Number(value);
|
|
572
|
+
if (isNaN(number)) {
|
|
573
|
+
switch (value.toLowerCase()) {
|
|
574
|
+
case 'true':
|
|
575
|
+
value = true;
|
|
576
|
+
break;
|
|
577
|
+
case 'false':
|
|
578
|
+
value = false;
|
|
579
|
+
break;
|
|
580
|
+
case 'null':
|
|
581
|
+
value = null;
|
|
582
|
+
break;
|
|
583
|
+
case 'undefined':
|
|
584
|
+
value = undefined;
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
value = number;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
else if (params.noEmptyString) {
|
|
593
|
+
// if empty string => TRUE to allow a if(options.key) check for example
|
|
594
|
+
value = true;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (obj[key]) {
|
|
598
|
+
if (!Array.isArray(obj[key])) {
|
|
599
|
+
obj[key] = new Array(obj[key]);
|
|
600
|
+
}
|
|
601
|
+
obj[key].push(value);
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
obj[key] = value;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return obj;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Returns entries from something iterable, such as a Map, Set, or Array
|
|
611
|
+
* @param value iterable input
|
|
612
|
+
* @returns An javascript object
|
|
613
|
+
*/
|
|
614
|
+
function objectEntries(value) {
|
|
615
|
+
if (value.entries) {
|
|
616
|
+
return value.entries();
|
|
617
|
+
}
|
|
618
|
+
return Array.from({
|
|
619
|
+
[Symbol.iterator]: function* () {
|
|
620
|
+
for (const key in value) {
|
|
621
|
+
yield [key, value[key]];
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Converts various data types, such as objects, strings, exceptions, errors,
|
|
628
|
+
* or numbers, into a string representation. Since it offers a more comprehensive format,
|
|
629
|
+
* this function is preferred to `JSON.stringify()`.
|
|
630
|
+
* @param obj Any objects, strings, exceptions, errors, or number
|
|
631
|
+
* @param params.space `''`, allows to configure space in the string representation
|
|
632
|
+
* @param params.decimal `2`, allows to choose the number of decimal to display in the string representation
|
|
633
|
+
* @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
|
|
634
|
+
* @param params.noBin `false`, when set skip binary encoding and write inplace a bin-length information
|
|
635
|
+
* @returns the final string representation
|
|
636
|
+
*/
|
|
637
|
+
// Online Javascript Editor for free
|
|
638
|
+
// Write, Edit and Run your Javascript code using JS Online Compiler
|
|
639
|
+
function stringify(obj, params = {}) {
|
|
640
|
+
params = Object.assign({ space: ' ', decimal: 2, recursive: 1, noBin: false }, params);
|
|
641
|
+
if (obj == null) {
|
|
642
|
+
return String(obj);
|
|
643
|
+
}
|
|
644
|
+
const error = obj.error || obj.message;
|
|
645
|
+
if (error) {
|
|
646
|
+
// is a error!
|
|
647
|
+
obj = error;
|
|
648
|
+
}
|
|
649
|
+
// number
|
|
650
|
+
if (obj.toFixed) {
|
|
651
|
+
return obj.toFixed(Number(params.decimal) || 0);
|
|
652
|
+
}
|
|
653
|
+
// boolean or string type or stop recursivity
|
|
654
|
+
if (typeof obj === 'boolean' || obj.substring || !params.recursive) {
|
|
655
|
+
// is already a string OR has to be stringified
|
|
656
|
+
return String(obj);
|
|
657
|
+
}
|
|
658
|
+
const space = params.space || '';
|
|
659
|
+
if (Array.isArray(obj)) {
|
|
660
|
+
// Array!
|
|
661
|
+
let res = '';
|
|
662
|
+
for (const value of obj) {
|
|
663
|
+
res += (res ? ',' : '[') + space;
|
|
664
|
+
res += stringify(value, Object.assign(Object.assign({}, params), { recursive: params.recursive - 1 }));
|
|
665
|
+
}
|
|
666
|
+
return (res += space + ']');
|
|
667
|
+
}
|
|
668
|
+
if (obj.byteLength != null && (obj === null || obj === void 0 ? void 0 : obj[Symbol.iterator])) {
|
|
669
|
+
// Binary!
|
|
670
|
+
return _decoder.decode(obj);
|
|
671
|
+
}
|
|
672
|
+
let res = '';
|
|
673
|
+
if (params.noBin) {
|
|
674
|
+
return '[' + obj.byteLength + '#bytes]';
|
|
675
|
+
}
|
|
676
|
+
for (const name in obj) {
|
|
677
|
+
res += (res ? ',' : '{') + space + name + ':';
|
|
678
|
+
res += stringify(obj[name], Object.assign(Object.assign({}, params), { recursive: params.recursive - 1 }));
|
|
679
|
+
}
|
|
680
|
+
return (res += space + '}');
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Encode a string to a binary representation
|
|
684
|
+
* @param value string value to convert
|
|
685
|
+
* @returns binary conversion
|
|
686
|
+
*/
|
|
687
|
+
function toBin(value) {
|
|
688
|
+
return _encoder.encode(value);
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Execute a promise in a safe way with a timeout if caller doesn't resolve it in the accurate time
|
|
692
|
+
*/
|
|
693
|
+
function safePromise(timeout, promise) {
|
|
694
|
+
// Returns a race between our timeout and the passed in promise
|
|
695
|
+
let timer;
|
|
696
|
+
return Promise.race([
|
|
697
|
+
promise instanceof Promise ? promise : new Promise(promise),
|
|
698
|
+
new Promise((resolve, reject) => (timer = setTimeout(() => reject('timed out in ' + timeout + 'ms'), timeout)))
|
|
699
|
+
]).finally(() => clearTimeout(timer));
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Wait in milliseconds, requires a call with await keyword!
|
|
703
|
+
*/
|
|
704
|
+
function sleep(ms) {
|
|
705
|
+
return new Promise(resolve => {
|
|
706
|
+
setTimeout(resolve, ms);
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* fetch help method with few usefull fix:
|
|
711
|
+
* - throw an string exception if response code is not 200 with the text of the response or uses statusText
|
|
712
|
+
*/
|
|
713
|
+
function fetch(input, init) {
|
|
714
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
715
|
+
const response = yield self.fetch(input, init);
|
|
716
|
+
if (response.status >= 300) {
|
|
717
|
+
if (response.body) {
|
|
718
|
+
throw (yield response.text()) || response.statusText;
|
|
719
|
+
}
|
|
720
|
+
throw response.statusText;
|
|
721
|
+
}
|
|
722
|
+
return response;
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Extension parser
|
|
727
|
+
* @param path path to parse
|
|
728
|
+
* @returns the extension
|
|
729
|
+
*/
|
|
730
|
+
function parseExtension(path) {
|
|
731
|
+
const dot = path.lastIndexOf('.');
|
|
732
|
+
const ext = dot >= 0 && dot > path.lastIndexOf('/') ? path.substring(dot) : '';
|
|
733
|
+
return ext;
|
|
734
|
+
}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});/**
|
|
480
735
|
* Copyright 2024 Ceeblue B.V.
|
|
481
736
|
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
|
|
482
737
|
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
|
|
@@ -486,11 +741,11 @@ class NetAddress {
|
|
|
486
741
|
*/
|
|
487
742
|
var Type;
|
|
488
743
|
(function (Type) {
|
|
489
|
-
Type["HESP"] = "
|
|
490
|
-
Type["WEBRTS"] = "
|
|
491
|
-
Type["WEBRTC"] = "
|
|
492
|
-
Type["META"] = "
|
|
493
|
-
Type["DATA"] = "
|
|
744
|
+
Type["HESP"] = "HESP";
|
|
745
|
+
Type["WEBRTS"] = "WebRTS";
|
|
746
|
+
Type["WEBRTC"] = "WebRTC";
|
|
747
|
+
Type["META"] = "Meta";
|
|
748
|
+
Type["DATA"] = "Data";
|
|
494
749
|
})(Type || (Type = {}));
|
|
495
750
|
/**
|
|
496
751
|
* Some connection utility functions
|
|
@@ -533,6 +788,8 @@ function buildURL(type, params, protocol = 'wss') {
|
|
|
533
788
|
for (const key in params.query) {
|
|
534
789
|
url.searchParams.set(key, params.query[key]);
|
|
535
790
|
}
|
|
791
|
+
// Remove possible extension of streamName
|
|
792
|
+
params.streamName.substring(0, params.streamName.length - parseExtension(params.streamName).length);
|
|
536
793
|
return url;
|
|
537
794
|
}var Connect=/*#__PURE__*/Object.freeze({__proto__:null,get Type(){return Type},buildURL:buildURL});/**
|
|
538
795
|
* Copyright 2024 Ceeblue B.V.
|
|
@@ -597,18 +854,19 @@ class EventEmitter {
|
|
|
597
854
|
const events = new Set();
|
|
598
855
|
this._events.set(name.substring(2).toLowerCase(), events);
|
|
599
856
|
let defaultEvent = proto[name];
|
|
857
|
+
const raise = (...args) => {
|
|
858
|
+
// Call default event if not null (can happen in JS usage)
|
|
859
|
+
if (defaultEvent) {
|
|
860
|
+
defaultEvent.call(this, ...args);
|
|
861
|
+
}
|
|
862
|
+
// Call subscribers
|
|
863
|
+
for (const event of events) {
|
|
864
|
+
event(...args);
|
|
865
|
+
}
|
|
866
|
+
};
|
|
600
867
|
Object.defineProperties(this, {
|
|
601
868
|
[name]: {
|
|
602
|
-
get: () =>
|
|
603
|
-
// Call default event if not null (can happen in JS usage)
|
|
604
|
-
if (defaultEvent) {
|
|
605
|
-
defaultEvent.call(this, ...args);
|
|
606
|
-
}
|
|
607
|
-
// Call subscribers
|
|
608
|
-
for (const event of events) {
|
|
609
|
-
event(...args);
|
|
610
|
-
}
|
|
611
|
-
},
|
|
869
|
+
get: () => raise,
|
|
612
870
|
set: (value) => {
|
|
613
871
|
// Assign a default behavior!
|
|
614
872
|
defaultEvent = value;
|
|
@@ -633,9 +891,7 @@ class EventEmitter {
|
|
|
633
891
|
const events = this._event(name);
|
|
634
892
|
events.add(event);
|
|
635
893
|
if (abort) {
|
|
636
|
-
abort.signal.addEventListener('abort', () => {
|
|
637
|
-
events.delete(event);
|
|
638
|
-
});
|
|
894
|
+
abort.signal.addEventListener('abort', () => events.delete(event), { once: true });
|
|
639
895
|
}
|
|
640
896
|
}
|
|
641
897
|
/**
|
|
@@ -649,14 +905,12 @@ class EventEmitter {
|
|
|
649
905
|
throw Error('event to subscribe cannot be null');
|
|
650
906
|
}
|
|
651
907
|
const events = this._event(name);
|
|
652
|
-
events.add(() => {
|
|
908
|
+
events.add((...args) => {
|
|
653
909
|
events.delete(event); // delete from events
|
|
654
|
-
event(); // execute event
|
|
910
|
+
event(...args); // execute event
|
|
655
911
|
});
|
|
656
912
|
if (abort) {
|
|
657
|
-
abort.signal.addEventListener('abort', () => {
|
|
658
|
-
events.delete(event);
|
|
659
|
-
});
|
|
913
|
+
abort.signal.addEventListener('abort', () => events.delete(event), { once: true });
|
|
660
914
|
}
|
|
661
915
|
}
|
|
662
916
|
/**
|
|
@@ -1099,198 +1353,40 @@ Object.freeze(SDP);/**
|
|
|
1099
1353
|
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
|
|
1100
1354
|
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
|
|
1101
1355
|
*/
|
|
1102
|
-
const _decoder = new TextDecoder();
|
|
1103
|
-
const _encoder = new TextEncoder();
|
|
1104
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1105
|
-
const _perf = performance; // to increase x10 now performance!
|
|
1106
|
-
/**
|
|
1107
|
-
* Some basic utility functions
|
|
1108
|
-
*/
|
|
1109
|
-
/**
|
|
1110
|
-
* An empty lambda function, pratical to disable default behavior of function or events which are not expected to be null
|
|
1111
|
-
* @example
|
|
1112
|
-
* console.log = Util.EMPTY_FUNCTION; // disable logs without breaking calls
|
|
1113
|
-
*/
|
|
1114
|
-
const EMPTY_FUNCTION = () => { };
|
|
1115
|
-
/**
|
|
1116
|
-
* Efficient and high resolution timestamp in milliseconds elapsed since {@link Util.timeOrigin}
|
|
1117
|
-
*/
|
|
1118
|
-
function time() {
|
|
1119
|
-
return Math.floor(_perf.now());
|
|
1120
|
-
}
|
|
1121
|
-
/**
|
|
1122
|
-
* Time origin represents the time when the application has started
|
|
1123
|
-
*/
|
|
1124
|
-
function timeOrigin() {
|
|
1125
|
-
return Math.floor(_perf.now() + _perf.timeOrigin);
|
|
1126
|
-
}
|
|
1127
|
-
/**
|
|
1128
|
-
* Parse query and returns it in an easy-to-use Javascript object form
|
|
1129
|
-
* @param urlOrQueryOrSearch string, url, or searchParams containing query. If not set it uses `location.search` to determinate query.
|
|
1130
|
-
* @returns An javascript object containing each option
|
|
1131
|
-
*/
|
|
1132
|
-
function options(urlOrQueryOrSearch = typeof location === 'undefined'
|
|
1133
|
-
? undefined
|
|
1134
|
-
: location) {
|
|
1135
|
-
if (!urlOrQueryOrSearch) {
|
|
1136
|
-
return {};
|
|
1137
|
-
}
|
|
1138
|
-
try {
|
|
1139
|
-
const url = urlOrQueryOrSearch;
|
|
1140
|
-
urlOrQueryOrSearch = new URL(url).searchParams;
|
|
1141
|
-
}
|
|
1142
|
-
catch (e) {
|
|
1143
|
-
if (typeof urlOrQueryOrSearch == 'string') {
|
|
1144
|
-
if (urlOrQueryOrSearch.startsWith('?')) {
|
|
1145
|
-
urlOrQueryOrSearch = urlOrQueryOrSearch.substring(1);
|
|
1146
|
-
}
|
|
1147
|
-
urlOrQueryOrSearch = new URLSearchParams(urlOrQueryOrSearch);
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
// works same if urlOrQueryOrSearch is null, integer, or a already object etc...
|
|
1151
|
-
return objectFrom(urlOrQueryOrSearch, { withType: true, noEmptyString: true });
|
|
1152
|
-
}
|
|
1153
|
-
/**
|
|
1154
|
-
* Returns an easy-to-use Javascript object something iterable, such as a Map, Set, or Array
|
|
1155
|
-
* @param value iterable input
|
|
1156
|
-
* @param params.withType `false`, if set it tries to cast string value to a JS number/boolean/undefined/null type.
|
|
1157
|
-
* @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
|
|
1158
|
-
* @returns An javascript object
|
|
1159
|
-
*/
|
|
1160
|
-
function objectFrom(value, params) {
|
|
1161
|
-
params = Object.assign({ withType: false, noEmptyString: false }, params);
|
|
1162
|
-
const obj = {};
|
|
1163
|
-
if (!value) {
|
|
1164
|
-
return obj;
|
|
1165
|
-
}
|
|
1166
|
-
for (const [key, val] of objectEntries(value)) {
|
|
1167
|
-
value = val;
|
|
1168
|
-
if (params.withType && value != null && value.substring) {
|
|
1169
|
-
if (value) {
|
|
1170
|
-
const number = Number(value);
|
|
1171
|
-
if (isNaN(number)) {
|
|
1172
|
-
switch (value.toLowerCase()) {
|
|
1173
|
-
case 'true':
|
|
1174
|
-
value = true;
|
|
1175
|
-
break;
|
|
1176
|
-
case 'false':
|
|
1177
|
-
value = false;
|
|
1178
|
-
break;
|
|
1179
|
-
case 'null':
|
|
1180
|
-
value = null;
|
|
1181
|
-
break;
|
|
1182
|
-
case 'undefined':
|
|
1183
|
-
value = undefined;
|
|
1184
|
-
break;
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
else {
|
|
1188
|
-
value = number;
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
else if (params.noEmptyString) {
|
|
1192
|
-
// if empty string => TRUE to allow a if(options.key) check for example
|
|
1193
|
-
value = true;
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
if (obj[key]) {
|
|
1197
|
-
if (!Array.isArray(obj[key])) {
|
|
1198
|
-
obj[key] = new Array(obj[key]);
|
|
1199
|
-
}
|
|
1200
|
-
obj[key].push(value);
|
|
1201
|
-
}
|
|
1202
|
-
else {
|
|
1203
|
-
obj[key] = value;
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
return obj;
|
|
1207
|
-
}
|
|
1208
|
-
/**
|
|
1209
|
-
* Returns entries from something iterable, such as a Map, Set, or Array
|
|
1210
|
-
* @param value iterable input
|
|
1211
|
-
* @returns An javascript object
|
|
1212
|
-
*/
|
|
1213
|
-
function objectEntries(value) {
|
|
1214
|
-
if (value.entries) {
|
|
1215
|
-
return value.entries();
|
|
1216
|
-
}
|
|
1217
|
-
return Array.from({
|
|
1218
|
-
[Symbol.iterator]: function* () {
|
|
1219
|
-
for (const key in value) {
|
|
1220
|
-
yield [key, value[key]];
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
});
|
|
1224
|
-
}
|
|
1225
1356
|
/**
|
|
1226
|
-
*
|
|
1227
|
-
* or numbers, into a string representation. Since it offers a more comprehensive format,
|
|
1228
|
-
* this function is preferred to `JSON.stringify()`.
|
|
1229
|
-
* @param obj Any objects, strings, exceptions, errors, or number
|
|
1230
|
-
* @param params.space `''`, allows to configure space in the string representation
|
|
1231
|
-
* @param params.decimal `2`, allows to choose the number of decimal to display in the string representation
|
|
1232
|
-
* @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.
|
|
1233
|
-
* @returns the final string representation
|
|
1357
|
+
* Compute ByteRate every delta time
|
|
1234
1358
|
*/
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
if (obj.byteLength != null && (obj === null || obj === void 0 ? void 0 : obj[Symbol.iterator])) {
|
|
1265
|
-
// Binary!
|
|
1266
|
-
return _decoder.decode(obj);
|
|
1267
|
-
}
|
|
1268
|
-
let res = '';
|
|
1269
|
-
for (const name in obj) {
|
|
1270
|
-
res += (res ? ',' : '{') + space + name + ':';
|
|
1271
|
-
res += stringify(obj[name], Object.assign(params, { recursive: params.recursive - 1 }));
|
|
1359
|
+
class ByteRate {
|
|
1360
|
+
onBytes(bytes) { }
|
|
1361
|
+
get delta() {
|
|
1362
|
+
return this._delta;
|
|
1363
|
+
}
|
|
1364
|
+
constructor(delta = 1000) {
|
|
1365
|
+
this._time = time();
|
|
1366
|
+
this._value = NaN;
|
|
1367
|
+
this._delta = delta;
|
|
1368
|
+
this._bytes = 0;
|
|
1369
|
+
}
|
|
1370
|
+
value() {
|
|
1371
|
+
return Math.round(this.exact());
|
|
1372
|
+
}
|
|
1373
|
+
exact() {
|
|
1374
|
+
const now = time();
|
|
1375
|
+
const elapsed = now - this._time;
|
|
1376
|
+
if (elapsed > this._delta || isNaN(this._value)) {
|
|
1377
|
+
// wait "_delta" before next compute rate
|
|
1378
|
+
this._value = (this._bytes * 1000) / elapsed;
|
|
1379
|
+
this._bytes = 0;
|
|
1380
|
+
this._time = now;
|
|
1381
|
+
}
|
|
1382
|
+
return this._value;
|
|
1383
|
+
}
|
|
1384
|
+
addBytes(bytes) {
|
|
1385
|
+
this._bytes += bytes;
|
|
1386
|
+
this.onBytes(bytes);
|
|
1387
|
+
return this;
|
|
1272
1388
|
}
|
|
1273
|
-
|
|
1274
|
-
}
|
|
1275
|
-
/**
|
|
1276
|
-
* Encode a string to a binary representation
|
|
1277
|
-
* @param value string value to convert
|
|
1278
|
-
* @returns binary conversion
|
|
1279
|
-
*/
|
|
1280
|
-
function toBin(value) {
|
|
1281
|
-
return _encoder.encode(value);
|
|
1282
|
-
}
|
|
1283
|
-
/**
|
|
1284
|
-
* Execute a promise in a safe way with a timeout if caller doesn't resolve it in the accurate time
|
|
1285
|
-
*/
|
|
1286
|
-
function safePromise(timeout, promise) {
|
|
1287
|
-
// Returns a race between our timeout and the passed in promise
|
|
1288
|
-
let timer;
|
|
1289
|
-
return Promise.race([
|
|
1290
|
-
promise instanceof Promise ? promise : new Promise(promise),
|
|
1291
|
-
new Promise((resolve, reject) => (timer = setTimeout(() => reject('timed out in ' + timeout + 'ms'), timeout)))
|
|
1292
|
-
]).finally(() => clearTimeout(timer));
|
|
1293
|
-
}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});/**
|
|
1389
|
+
}/**
|
|
1294
1390
|
* Copyright 2024 Ceeblue B.V.
|
|
1295
1391
|
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
|
|
1296
1392
|
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
|
|
@@ -1346,6 +1442,12 @@ class WebSocketReliable extends EventEmitter {
|
|
|
1346
1442
|
get binaryType() {
|
|
1347
1443
|
return 'arraybuffer';
|
|
1348
1444
|
}
|
|
1445
|
+
get recvByteRate() {
|
|
1446
|
+
return this._recvByteRate.value();
|
|
1447
|
+
}
|
|
1448
|
+
get sendByteRate() {
|
|
1449
|
+
return this._sendByteRate.value();
|
|
1450
|
+
}
|
|
1349
1451
|
/**
|
|
1350
1452
|
* url of connection
|
|
1351
1453
|
*/
|
|
@@ -1409,6 +1511,8 @@ class WebSocketReliable extends EventEmitter {
|
|
|
1409
1511
|
this._queueingBytes = 0;
|
|
1410
1512
|
this._opened = false;
|
|
1411
1513
|
this._closed = true;
|
|
1514
|
+
this._recvByteRate = new ByteRate();
|
|
1515
|
+
this._sendByteRate = new ByteRate();
|
|
1412
1516
|
if (url) {
|
|
1413
1517
|
this.open(url, protocols);
|
|
1414
1518
|
}
|
|
@@ -1422,7 +1526,11 @@ class WebSocketReliable extends EventEmitter {
|
|
|
1422
1526
|
this._closed = false;
|
|
1423
1527
|
const ws = (this._ws = new WebSocket(url, protocols));
|
|
1424
1528
|
ws.binaryType = this.binaryType;
|
|
1425
|
-
ws.onmessage = e =>
|
|
1529
|
+
ws.onmessage = e => {
|
|
1530
|
+
var _a;
|
|
1531
|
+
this._recvByteRate.addBytes((_a = e.data.byteLength) !== null && _a !== void 0 ? _a : e.data.length);
|
|
1532
|
+
this.onMessage(e.data);
|
|
1533
|
+
};
|
|
1426
1534
|
// Add details and fix close ways
|
|
1427
1535
|
ws.onclose = (e) => {
|
|
1428
1536
|
if (!this._opened) {
|
|
@@ -1461,7 +1569,7 @@ class WebSocketReliable extends EventEmitter {
|
|
|
1461
1569
|
this._queueingBytes += typeof message === 'string' ? message.length : message.byteLength;
|
|
1462
1570
|
}
|
|
1463
1571
|
else {
|
|
1464
|
-
this.
|
|
1572
|
+
this._send(message);
|
|
1465
1573
|
}
|
|
1466
1574
|
return this;
|
|
1467
1575
|
}
|
|
@@ -1471,7 +1579,7 @@ class WebSocketReliable extends EventEmitter {
|
|
|
1471
1579
|
flush() {
|
|
1472
1580
|
if (this._ws) {
|
|
1473
1581
|
for (const message of this._queueing) {
|
|
1474
|
-
this.
|
|
1582
|
+
this._send(message);
|
|
1475
1583
|
}
|
|
1476
1584
|
}
|
|
1477
1585
|
this._queueing.length = 0;
|
|
@@ -1494,9 +1602,16 @@ class WebSocketReliable extends EventEmitter {
|
|
|
1494
1602
|
this._queueingBytes = 0;
|
|
1495
1603
|
this.onClose(error);
|
|
1496
1604
|
}
|
|
1605
|
+
_send(message) {
|
|
1606
|
+
if (!this._ws) {
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
this._sendByteRate.addBytes(typeof message === 'string' ? message.length : message.byteLength);
|
|
1610
|
+
this._ws.send(message);
|
|
1611
|
+
}
|
|
1497
1612
|
}/**
|
|
1498
1613
|
* Copyright 2024 Ceeblue B.V.
|
|
1499
1614
|
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
|
|
1500
1615
|
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
|
|
1501
1616
|
*/
|
|
1502
|
-
const VERSION = '1.
|
|
1617
|
+
const VERSION = '1.3.0';export{BinaryReader,BinaryWriter,BitReader,Connect,EventEmitter,NetAddress,Numbers,Queue,SDP,Util,VERSION,WebSocketReliable};//# sourceMappingURL=web-utils.js.map
|