@ceeblue/web-utils 1.2.0 → 1.4.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;
@@ -352,6 +341,310 @@ class BitReader {
352
341
  read32() {
353
342
  return this.read(32);
354
343
  }
344
+ }/******************************************************************************
345
+ Copyright (c) Microsoft Corporation.
346
+
347
+ Permission to use, copy, modify, and/or distribute this software for any
348
+ purpose with or without fee is hereby granted.
349
+
350
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
351
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
352
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
353
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
354
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
355
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
356
+ PERFORMANCE OF THIS SOFTWARE.
357
+ ***************************************************************************** */
358
+ /* global Reflect, Promise, SuppressedError, Symbol */
359
+
360
+
361
+ function __awaiter(thisArg, _arguments, P, generator) {
362
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
363
+ return new (P || (P = Promise))(function (resolve, reject) {
364
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
365
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
366
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
367
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
368
+ });
369
+ }
370
+
371
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
372
+ var e = new Error(message);
373
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
374
+ };/**
375
+ * Copyright 2024 Ceeblue B.V.
376
+ * This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
377
+ * See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
378
+ */
379
+ const _decoder = new TextDecoder();
380
+ const _encoder = new TextEncoder();
381
+ /* eslint-disable @typescript-eslint/no-explicit-any */
382
+ const _perf = performance; // to increase x10 now performance!
383
+ /**
384
+ * Some basic utility functions
385
+ */
386
+ /**
387
+ * An empty lambda function, pratical to disable default behavior of function or events which are not expected to be null
388
+ * @example
389
+ * console.log = Util.EMPTY_FUNCTION; // disable logs without breaking calls
390
+ */
391
+ const EMPTY_FUNCTION = () => { };
392
+ /**
393
+ * Efficient and high resolution timestamp in milliseconds elapsed since {@link Util.timeOrigin}
394
+ */
395
+ function time() {
396
+ return Math.floor(_perf.now());
397
+ }
398
+ /**
399
+ * Time origin represents the time when the application has started
400
+ */
401
+ function timeOrigin() {
402
+ return Math.floor(_perf.now() + _perf.timeOrigin);
403
+ }
404
+ /**
405
+ * Parse query and returns it in an easy-to-use Javascript object form
406
+ * @param urlOrQueryOrSearch string, url, or searchParams containing query. If not set it uses `location.search` to determinate query.
407
+ * @returns An javascript object containing each option
408
+ */
409
+ function options(urlOrQueryOrSearch = typeof location === 'undefined'
410
+ ? undefined
411
+ : location) {
412
+ if (!urlOrQueryOrSearch) {
413
+ return {};
414
+ }
415
+ try {
416
+ const url = urlOrQueryOrSearch;
417
+ urlOrQueryOrSearch = new URL(url).searchParams;
418
+ }
419
+ catch (e) {
420
+ if (typeof urlOrQueryOrSearch == 'string') {
421
+ if (urlOrQueryOrSearch.startsWith('?')) {
422
+ urlOrQueryOrSearch = urlOrQueryOrSearch.substring(1);
423
+ }
424
+ urlOrQueryOrSearch = new URLSearchParams(urlOrQueryOrSearch);
425
+ }
426
+ }
427
+ // works same if urlOrQueryOrSearch is null, integer, or a already object etc...
428
+ return objectFrom(urlOrQueryOrSearch, { withType: true, noEmptyString: true });
429
+ }
430
+ /**
431
+ * Returns an easy-to-use Javascript object something iterable, such as a Map, Set, or Array
432
+ * @param value iterable input
433
+ * @param params.withType `false`, if set it tries to cast string value to a JS number/boolean/undefined/null type.
434
+ * @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
435
+ * @returns An javascript object
436
+ */
437
+ function objectFrom(value, params) {
438
+ params = Object.assign({ withType: false, noEmptyString: false }, params);
439
+ const obj = {};
440
+ if (!value) {
441
+ return obj;
442
+ }
443
+ for (const [key, val] of objectEntries(value)) {
444
+ value = val;
445
+ if (params.withType && value != null && value.substring) {
446
+ if (value) {
447
+ const number = Number(value);
448
+ if (isNaN(number)) {
449
+ switch (value.toLowerCase()) {
450
+ case 'true':
451
+ value = true;
452
+ break;
453
+ case 'false':
454
+ value = false;
455
+ break;
456
+ case 'null':
457
+ value = null;
458
+ break;
459
+ case 'undefined':
460
+ value = undefined;
461
+ break;
462
+ }
463
+ }
464
+ else {
465
+ value = number;
466
+ }
467
+ }
468
+ else if (params.noEmptyString) {
469
+ // if empty string => TRUE to allow a if(options.key) check for example
470
+ value = true;
471
+ }
472
+ }
473
+ if (obj[key]) {
474
+ if (!Array.isArray(obj[key])) {
475
+ obj[key] = new Array(obj[key]);
476
+ }
477
+ obj[key].push(value);
478
+ }
479
+ else {
480
+ obj[key] = value;
481
+ }
482
+ }
483
+ return obj;
484
+ }
485
+ /**
486
+ * Returns entries from something iterable, such as a Map, Set, or Array
487
+ * @param value iterable input
488
+ * @returns An javascript object
489
+ */
490
+ function objectEntries(value) {
491
+ if (value.entries) {
492
+ return value.entries();
493
+ }
494
+ return Array.from({
495
+ [Symbol.iterator]: function* () {
496
+ for (const key in value) {
497
+ yield [key, value[key]];
498
+ }
499
+ }
500
+ });
501
+ }
502
+ /**
503
+ * Converts various data types, such as objects, strings, exceptions, errors,
504
+ * or numbers, into a string representation. Since it offers a more comprehensive format,
505
+ * this function is preferred to `JSON.stringify()`.
506
+ * @param obj Any objects, strings, exceptions, errors, or number
507
+ * @param params.space `''`, allows to configure space in the string representation
508
+ * @param params.decimal `2`, allows to choose the number of decimal to display in the string representation
509
+ * @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
510
+ * @param params.noBin `false`, when set skip binary encoding and write inplace a bin-length information
511
+ * @returns the final string representation
512
+ */
513
+ // Online Javascript Editor for free
514
+ // Write, Edit and Run your Javascript code using JS Online Compiler
515
+ function stringify(obj, params = {}) {
516
+ params = Object.assign({ space: ' ', decimal: 2, recursive: 1, noBin: false }, params);
517
+ if (obj == null) {
518
+ return String(obj);
519
+ }
520
+ const error = obj.error || obj.message;
521
+ if (error) {
522
+ // is a error!
523
+ obj = error;
524
+ }
525
+ // number
526
+ if (obj.toFixed) {
527
+ return obj.toFixed(Number(params.decimal) || 0);
528
+ }
529
+ // boolean or string type or stop recursivity
530
+ if (typeof obj === 'boolean' || obj.substring || !params.recursive) {
531
+ // is already a string OR has to be stringified
532
+ return String(obj);
533
+ }
534
+ const space = params.space || '';
535
+ if (Array.isArray(obj)) {
536
+ // Array!
537
+ let res = '';
538
+ for (const value of obj) {
539
+ res += (res ? ',' : '[') + space;
540
+ res += stringify(value, Object.assign(Object.assign({}, params), { recursive: params.recursive - 1 }));
541
+ }
542
+ return (res += space + ']');
543
+ }
544
+ if (obj.byteLength != null && (obj === null || obj === void 0 ? void 0 : obj[Symbol.iterator])) {
545
+ // Binary!
546
+ return _decoder.decode(obj);
547
+ }
548
+ let res = '';
549
+ if (params.noBin) {
550
+ return '[' + obj.byteLength + '#bytes]';
551
+ }
552
+ for (const name in obj) {
553
+ res += (res ? ',' : '{') + space + name + ':';
554
+ res += stringify(obj[name], Object.assign(Object.assign({}, params), { recursive: params.recursive - 1 }));
555
+ }
556
+ return (res += space + '}');
557
+ }
558
+ /**
559
+ * Encode a string to a binary representation
560
+ * @param value string value to convert
561
+ * @returns binary conversion
562
+ */
563
+ function toBin(value) {
564
+ return _encoder.encode(value);
565
+ }
566
+ /**
567
+ * Execute a promise in a safe way with a timeout if caller doesn't resolve it in the accurate time
568
+ */
569
+ function safePromise(timeout, promise) {
570
+ // Returns a race between our timeout and the passed in promise
571
+ let timer;
572
+ return Promise.race([
573
+ promise instanceof Promise ? promise : new Promise(promise),
574
+ new Promise((resolve, reject) => (timer = setTimeout(() => reject('timed out in ' + timeout + 'ms'), timeout)))
575
+ ]).finally(() => clearTimeout(timer));
576
+ }
577
+ /**
578
+ * Wait in milliseconds, requires a call with await keyword!
579
+ */
580
+ function sleep(ms) {
581
+ return new Promise(resolve => {
582
+ setTimeout(resolve, ms);
583
+ });
584
+ }
585
+ /**
586
+ * fetch help method with few usefull fix:
587
+ * - throw an string exception if response code is not 200 with the text of the response or uses statusText
588
+ */
589
+ function fetch(input, init) {
590
+ return __awaiter(this, void 0, void 0, function* () {
591
+ const response = yield self.fetch(input, init);
592
+ if (response.status >= 300) {
593
+ if (response.body) {
594
+ throw (yield response.text()) || response.statusText;
595
+ }
596
+ throw response.statusText;
597
+ }
598
+ return response;
599
+ });
600
+ }
601
+ /**
602
+ * Extension parser
603
+ * @param path path to parse
604
+ * @returns the extension
605
+ */
606
+ function parseExtension(path) {
607
+ const dot = path.lastIndexOf('.');
608
+ const ext = dot >= 0 && dot > path.lastIndexOf('/') ? path.substring(dot) : '';
609
+ return ext;
610
+ }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});/**
611
+ * Copyright 2024 Ceeblue B.V.
612
+ * This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
613
+ * See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
614
+ */
615
+ /**
616
+ * Compute ByteRate every delta time
617
+ */
618
+ class ByteRate {
619
+ onBytes(bytes) { }
620
+ get delta() {
621
+ return this._delta;
622
+ }
623
+ constructor(delta = 1000) {
624
+ this._time = time();
625
+ this._value = NaN;
626
+ this._delta = delta;
627
+ this._bytes = 0;
628
+ }
629
+ value() {
630
+ return Math.round(this.exact());
631
+ }
632
+ exact() {
633
+ const now = time();
634
+ const elapsed = now - this._time;
635
+ if (elapsed > this._delta || isNaN(this._value)) {
636
+ // wait "_delta" before next compute rate
637
+ this._value = (this._bytes * 1000) / elapsed;
638
+ this._bytes = 0;
639
+ this._time = now;
640
+ }
641
+ return this._value;
642
+ }
643
+ addBytes(bytes) {
644
+ this._bytes += bytes;
645
+ this.onBytes(bytes);
646
+ return this;
647
+ }
355
648
  }/**
356
649
  * Copyright 2024 Ceeblue B.V.
357
650
  * This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
@@ -486,11 +779,11 @@ class NetAddress {
486
779
  */
487
780
  var Type;
488
781
  (function (Type) {
489
- Type["HESP"] = "hesp";
490
- Type["WEBRTS"] = "webrts";
491
- Type["WEBRTC"] = "webrtc";
492
- Type["META"] = "meta";
493
- Type["DATA"] = "data";
782
+ Type["HESP"] = "HESP";
783
+ Type["WEBRTS"] = "WebRTS";
784
+ Type["WEBRTC"] = "WebRTC";
785
+ Type["META"] = "Meta";
786
+ Type["DATA"] = "Data";
494
787
  })(Type || (Type = {}));
495
788
  /**
496
789
  * Some connection utility functions
@@ -533,6 +826,8 @@ function buildURL(type, params, protocol = 'wss') {
533
826
  for (const key in params.query) {
534
827
  url.searchParams.set(key, params.query[key]);
535
828
  }
829
+ // Remove possible extension of streamName
830
+ params.streamName.substring(0, params.streamName.length - parseExtension(params.streamName).length);
536
831
  return url;
537
832
  }var Connect=/*#__PURE__*/Object.freeze({__proto__:null,get Type(){return Type},buildURL:buildURL});/**
538
833
  * Copyright 2024 Ceeblue B.V.
@@ -597,18 +892,19 @@ class EventEmitter {
597
892
  const events = new Set();
598
893
  this._events.set(name.substring(2).toLowerCase(), events);
599
894
  let defaultEvent = proto[name];
895
+ const raise = (...args) => {
896
+ // Call default event if not null (can happen in JS usage)
897
+ if (defaultEvent) {
898
+ defaultEvent.call(this, ...args);
899
+ }
900
+ // Call subscribers
901
+ for (const event of events) {
902
+ event(...args);
903
+ }
904
+ };
600
905
  Object.defineProperties(this, {
601
906
  [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
- },
907
+ get: () => raise,
612
908
  set: (value) => {
613
909
  // Assign a default behavior!
614
910
  defaultEvent = value;
@@ -633,9 +929,7 @@ class EventEmitter {
633
929
  const events = this._event(name);
634
930
  events.add(event);
635
931
  if (abort) {
636
- abort.signal.addEventListener('abort', () => {
637
- events.delete(event);
638
- });
932
+ abort.signal.addEventListener('abort', () => events.delete(event), { once: true });
639
933
  }
640
934
  }
641
935
  /**
@@ -649,14 +943,12 @@ class EventEmitter {
649
943
  throw Error('event to subscribe cannot be null');
650
944
  }
651
945
  const events = this._event(name);
652
- events.add(() => {
946
+ events.add((...args) => {
653
947
  events.delete(event); // delete from events
654
- event(); // execute event
948
+ event(...args); // execute event
655
949
  });
656
950
  if (abort) {
657
- abort.signal.addEventListener('abort', () => {
658
- events.delete(event);
659
- });
951
+ abort.signal.addEventListener('abort', () => events.delete(event), { once: true });
660
952
  }
661
953
  }
662
954
  /**
@@ -682,6 +974,55 @@ class EventEmitter {
682
974
  * This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
683
975
  * See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
684
976
  */
977
+ /**
978
+ * Some fix for JS MAP:
979
+ * - find(key) search an item in the map and returns undefined if not found
980
+ * - get(key) return the item if exists or otherwise create and returns it
981
+ * - set(key, value) returns the value of the item (rather the MAP)
982
+ */
983
+ class FixMap {
984
+ [Symbol.iterator]() {
985
+ return this._map[Symbol.iterator]();
986
+ }
987
+ get size() {
988
+ return this._map.size;
989
+ }
990
+ constructor(_initValue) {
991
+ this._initValue = _initValue;
992
+ this._map = new Map();
993
+ }
994
+ get(key) {
995
+ let value = this.find(key);
996
+ if (value === undefined) {
997
+ this._map.set(key, (value = this._initValue()));
998
+ }
999
+ return value;
1000
+ }
1001
+ find(key) {
1002
+ return this._map.get(key);
1003
+ }
1004
+ has(key) {
1005
+ return this._map.has(key);
1006
+ }
1007
+ clear() {
1008
+ this._map.clear();
1009
+ }
1010
+ delete(key) {
1011
+ return this._map.delete(key);
1012
+ }
1013
+ set(key, value) {
1014
+ this._map.set(key, value);
1015
+ return value;
1016
+ }
1017
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1018
+ forEach(callbackfn, thisArg) {
1019
+ this._map.forEach(callbackfn, thisArg);
1020
+ }
1021
+ }/**
1022
+ * Copyright 2024 Ceeblue B.V.
1023
+ * This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
1024
+ * See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
1025
+ */
685
1026
  /**
686
1027
  * Queue typed similar to a {@link https://en.cppreference.com/w/cpp/container/queue | std::queue<Type>} with possibility to limit the capacity like a FIFO
687
1028
  * @example
@@ -1099,202 +1440,6 @@ Object.freeze(SDP);/**
1099
1440
  * This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
1100
1441
  * See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
1101
1442
  */
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
- /**
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
1234
- */
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 }));
1272
- }
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});/**
1294
- * Copyright 2024 Ceeblue B.V.
1295
- * This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
1296
- * See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
1297
- */
1298
1443
  /**
1299
1444
  * The WebSocketReliable class extends WebSocket to bring up the following improvements:
1300
1445
  * - Fix all possible unintentional closing ways to get always a related error message, {@link onClose | onClose(error?) event}
@@ -1346,6 +1491,12 @@ class WebSocketReliable extends EventEmitter {
1346
1491
  get binaryType() {
1347
1492
  return 'arraybuffer';
1348
1493
  }
1494
+ get recvByteRate() {
1495
+ return this._recvByteRate.value();
1496
+ }
1497
+ get sendByteRate() {
1498
+ return this._sendByteRate.value();
1499
+ }
1349
1500
  /**
1350
1501
  * url of connection
1351
1502
  */
@@ -1409,6 +1560,8 @@ class WebSocketReliable extends EventEmitter {
1409
1560
  this._queueingBytes = 0;
1410
1561
  this._opened = false;
1411
1562
  this._closed = true;
1563
+ this._recvByteRate = new ByteRate();
1564
+ this._sendByteRate = new ByteRate();
1412
1565
  if (url) {
1413
1566
  this.open(url, protocols);
1414
1567
  }
@@ -1422,7 +1575,11 @@ class WebSocketReliable extends EventEmitter {
1422
1575
  this._closed = false;
1423
1576
  const ws = (this._ws = new WebSocket(url, protocols));
1424
1577
  ws.binaryType = this.binaryType;
1425
- ws.onmessage = e => this.onMessage(e.data);
1578
+ ws.onmessage = e => {
1579
+ var _a;
1580
+ this._recvByteRate.addBytes((_a = e.data.byteLength) !== null && _a !== void 0 ? _a : e.data.length);
1581
+ this.onMessage(e.data);
1582
+ };
1426
1583
  // Add details and fix close ways
1427
1584
  ws.onclose = (e) => {
1428
1585
  if (!this._opened) {
@@ -1461,7 +1618,7 @@ class WebSocketReliable extends EventEmitter {
1461
1618
  this._queueingBytes += typeof message === 'string' ? message.length : message.byteLength;
1462
1619
  }
1463
1620
  else {
1464
- this._ws.send(message);
1621
+ this._send(message);
1465
1622
  }
1466
1623
  return this;
1467
1624
  }
@@ -1471,7 +1628,7 @@ class WebSocketReliable extends EventEmitter {
1471
1628
  flush() {
1472
1629
  if (this._ws) {
1473
1630
  for (const message of this._queueing) {
1474
- this._ws.send(message);
1631
+ this._send(message);
1475
1632
  }
1476
1633
  }
1477
1634
  this._queueing.length = 0;
@@ -1494,9 +1651,16 @@ class WebSocketReliable extends EventEmitter {
1494
1651
  this._queueingBytes = 0;
1495
1652
  this.onClose(error);
1496
1653
  }
1654
+ _send(message) {
1655
+ if (!this._ws) {
1656
+ return;
1657
+ }
1658
+ this._sendByteRate.addBytes(typeof message === 'string' ? message.length : message.byteLength);
1659
+ this._ws.send(message);
1660
+ }
1497
1661
  }/**
1498
1662
  * Copyright 2024 Ceeblue B.V.
1499
1663
  * This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
1500
1664
  * See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
1501
1665
  */
1502
- const VERSION = '1.2.0';export{BinaryReader,BinaryWriter,BitReader,Connect,EventEmitter,NetAddress,Numbers,Queue,SDP,Util,VERSION,WebSocketReliable};//# sourceMappingURL=web-utils.js.map
1666
+ const VERSION = '1.4.0';export{BinaryReader,BinaryWriter,BitReader,ByteRate,Connect,EventEmitter,FixMap,NetAddress,Numbers,Queue,SDP,Util,VERSION,WebSocketReliable};//# sourceMappingURL=web-utils.js.map