@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/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 += count;
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 byte;
81
- do {
82
- byte = this.read8();
83
- if (!--bytes) {
84
- return ((result << 8) | byte) >>> 0; // Use all 8 bits from the 5th byte
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
- result = (result << 7) | (byte & 0x7f);
87
- } while (byte & 0x80);
87
+ factor *= 128;
88
+ }
88
89
  return result;
89
90
  }
90
91
  readString() {
91
- return String.fromCharCode(...this.read(this.read7Bit()));
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 value = this._data.subarray(this._position, this._position + size);
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, bytes = 5) {
231
- if (bytes > 5) {
232
- throw Error("BinaryWriter in JS can't encode more than 32 usefull bits");
233
- }
234
- if (!(bytes > 0)) {
235
- // negation to catch NaN value
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
- let bits = --bytes * 7;
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.write7Bit(value.length).write(value);
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 new Error('buffer not writable');
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 new Error('writing exceeds maximum ' + this._data.byteLength + ' bytes limit');
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"] = "hesp";
490
- Type["WEBRTS"] = "webrts";
491
- Type["WEBRTC"] = "webrtc";
492
- Type["META"] = "meta";
493
- Type["DATA"] = "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: () => (...args) => {
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
- * Converts various data types, such as objects, strings, exceptions, errors,
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
- // Online Javascript Editor for free
1236
- // Write, Edit and Run your Javascript code using JS Online Compiler
1237
- function stringify(obj, params = {}) {
1238
- params = Object.assign({ space: ' ', decimal: 2, recursive: 1 }, params);
1239
- if (obj == null) {
1240
- return String(obj);
1241
- }
1242
- const error = obj.error || obj.message;
1243
- if (error) {
1244
- // is a error!
1245
- obj = error;
1246
- }
1247
- if (obj.toFixed) {
1248
- return obj.toFixed(Number(params.decimal) || 0);
1249
- }
1250
- if (obj.substring || !params.recursive) {
1251
- // is already a string OR has to be stringified
1252
- return String(obj);
1253
- }
1254
- const space = params.space || '';
1255
- if (Array.isArray(obj)) {
1256
- // Array!
1257
- let res = '';
1258
- for (const value of obj) {
1259
- res += (res ? ',' : '[') + space;
1260
- res += stringify(value, Object.assign(params, { recursive: params.recursive - 1 }));
1261
- }
1262
- return (res += space + ']');
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
- return (res += space + '}');
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 => this.onMessage(e.data);
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._ws.send(message);
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._ws.send(message);
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.2.0';export{BinaryReader,BinaryWriter,BitReader,Connect,EventEmitter,NetAddress,Numbers,Queue,SDP,Util,VERSION,WebSocketReliable};//# sourceMappingURL=web-utils.js.map
1617
+ const VERSION = '1.3.0';export{BinaryReader,BinaryWriter,BitReader,Connect,EventEmitter,NetAddress,Numbers,Queue,SDP,Util,VERSION,WebSocketReliable};//# sourceMappingURL=web-utils.js.map