@ceeblue/web-utils 7.0.0 → 7.1.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 +8 -5
- package/dist/web-utils.d.ts +20 -8
- package/dist/web-utils.js +60 -26
- 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 +5 -4
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Then [import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modu
|
|
|
14
14
|
```javascript
|
|
15
15
|
import { Util, ILog } from '@ceeblue/web-utils';
|
|
16
16
|
```
|
|
17
|
-
>
|
|
17
|
+
> [!IMPORTANT]
|
|
18
18
|
>
|
|
19
19
|
> If your project uses TypeScript, it is recommended that you set target: "ES6" in your configuration to match our use of ES6 features and ensure that your build will succeed (for those requiring a backward-compatible UMD version, a local build is recommended).
|
|
20
20
|
> Then define the "moduleResolution" compiler option: "Node" in tsconfig.json helps with import failures by ensuring that TypeScript uses the correct import resolution strategy based on the targeted Node.js version.
|
|
@@ -27,13 +27,11 @@ import { Util, ILog } from '@ceeblue/web-utils';
|
|
|
27
27
|
> }
|
|
28
28
|
> ```
|
|
29
29
|
|
|
30
|
-
>
|
|
30
|
+
> [!TIP]
|
|
31
31
|
>
|
|
32
32
|
> To debug production code without modifying it, the library can use special query parameter of the main page's URL:
|
|
33
33
|
> - __!cb-override-log-level__ : allows to override the log level for the entire library, see [Log.ts](./src/Log.ts) for details on handling log levels.
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
35
|
## Building locally
|
|
38
36
|
|
|
39
37
|
1. [Clone](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) this repository
|
|
@@ -58,7 +56,12 @@ This monorepo also contains built-in documentation about the APIs in the library
|
|
|
58
56
|
```
|
|
59
57
|
npm run build:docs
|
|
60
58
|
```
|
|
61
|
-
|
|
59
|
+
|
|
60
|
+
Once generated, open the `index.html` file located in the `docs` folder (`./docs/index.html`) with your browser.
|
|
61
|
+
|
|
62
|
+
> [!NOTE]
|
|
63
|
+
>
|
|
64
|
+
> An online, continuously maintained version of the latest released documentation is available at https://ceebluetv.github.io/web-utils/
|
|
62
65
|
|
|
63
66
|
## Contribution
|
|
64
67
|
|
package/dist/web-utils.d.ts
CHANGED
|
@@ -467,7 +467,7 @@ declare class EventEmitter extends Loggable {
|
|
|
467
467
|
* Event subscription only one time, once time fired it's automatically unsubscribe
|
|
468
468
|
* @param name Name of event without the `on` prefix (ex: `log` to `onLog` event declared)
|
|
469
469
|
* @param event Subscriber Function
|
|
470
|
-
* @param options.
|
|
470
|
+
* @param options.signal Optional `AbortSignal` to stop this or multiple subscriptions in same time
|
|
471
471
|
*/
|
|
472
472
|
once(name: EventKeys<this>, event: Function, options?: {
|
|
473
473
|
signal?: AbortSignal;
|
|
@@ -904,6 +904,8 @@ declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Re
|
|
|
904
904
|
* Fetch help method adding an explicit error property when Response is NOK, with the more accurate textual error inside
|
|
905
905
|
* Also measure the rtt of fetching and returns it in the property Response.rtt (guaranteed to be ≥ 1),
|
|
906
906
|
* supports subtracting server processing time using either the Response-Delay or CMSD-rd header when available
|
|
907
|
+
*
|
|
908
|
+
* WIP => replace the current implementation to use Resource Timing API
|
|
907
909
|
*/
|
|
908
910
|
declare function fetchWithRTT(input: RequestInfo | URL, init?: RequestInit): Promise<Response & {
|
|
909
911
|
rtt: number;
|
|
@@ -948,8 +950,20 @@ declare function trimStart(value: string, chars?: string): string;
|
|
|
948
950
|
* @returns string trimmed
|
|
949
951
|
*/
|
|
950
952
|
declare function trimEnd(value: string, chars?: string): string;
|
|
953
|
+
/**
|
|
954
|
+
* Wraps an object with a Proxy that makes property access case-insensitive.
|
|
955
|
+
*
|
|
956
|
+
* Property lookup (e.g. `obj.Foo` or `obj.foo`) will resolve to the same underlying key, regardless of casing.
|
|
957
|
+
* Only affects string-based property access (not symbols).
|
|
958
|
+
*
|
|
959
|
+
* @template T
|
|
960
|
+
* @param {T} obj - The original object.
|
|
961
|
+
* @returns {T} A proxied object with case-insensitive property access.
|
|
962
|
+
*/
|
|
963
|
+
declare function caseInsensitive<T extends Record<string, any>>(obj: any): T;
|
|
951
964
|
|
|
952
965
|
declare const Util_EMPTY_FUNCTION: typeof EMPTY_FUNCTION;
|
|
966
|
+
declare const Util_caseInsensitive: typeof caseInsensitive;
|
|
953
967
|
declare const Util_equal: typeof equal;
|
|
954
968
|
declare const Util_fetch: typeof fetch;
|
|
955
969
|
declare const Util_fetchWithRTT: typeof fetchWithRTT;
|
|
@@ -969,7 +983,7 @@ declare const Util_trimEnd: typeof trimEnd;
|
|
|
969
983
|
declare const Util_trimStart: typeof trimStart;
|
|
970
984
|
declare const Util_unixTime: typeof unixTime;
|
|
971
985
|
declare namespace Util {
|
|
972
|
-
export { Util_EMPTY_FUNCTION as EMPTY_FUNCTION, Util_equal as equal, Util_fetch as fetch, Util_fetchWithRTT as fetchWithRTT, Util_getBaseFile as getBaseFile, Util_getExtension as getExtension, Util_getFile as getFile, Util_iterableEntries as iterableEntries, Util_objectFrom as objectFrom, Util_options as options, Util_safePromise as safePromise, Util_sleep as sleep, Util_stringify as stringify, Util_time as time, Util_toBin as toBin, Util_trim as trim, Util_trimEnd as trimEnd, Util_trimStart as trimStart, Util_unixTime as unixTime };
|
|
986
|
+
export { Util_EMPTY_FUNCTION as EMPTY_FUNCTION, Util_caseInsensitive as caseInsensitive, Util_equal as equal, Util_fetch as fetch, Util_fetchWithRTT as fetchWithRTT, Util_getBaseFile as getBaseFile, Util_getExtension as getExtension, Util_getFile as getFile, Util_iterableEntries as iterableEntries, Util_objectFrom as objectFrom, Util_options as options, Util_safePromise as safePromise, Util_sleep as sleep, Util_stringify as stringify, Util_time as time, Util_toBin as toBin, Util_trim as trim, Util_trimEnd as trimEnd, Util_trimStart as trimStart, Util_unixTime as unixTime };
|
|
973
987
|
}
|
|
974
988
|
|
|
975
989
|
/**
|
|
@@ -1137,24 +1151,22 @@ declare class WebSocketReliable extends EventEmitter {
|
|
|
1137
1151
|
* @param {HTMLCanvasElement} canvas
|
|
1138
1152
|
* @param {CanvasRenderingContext2D} context
|
|
1139
1153
|
* @param {Date} now current date, new Date() by default
|
|
1140
|
-
* @param {Number} blocksPerRow number of blocks in the line, 32 by default
|
|
1141
1154
|
* @param {Number} tolerance percentage of tolerance for the black and white threshold, 0.2 by default
|
|
1142
1155
|
* @returns {Number} The latency in millisecond between 'now' and the decoded timestamp, 0 if the timestamp cannot be decoded
|
|
1143
1156
|
*/
|
|
1144
|
-
declare function getLatency(sourceEl: HTMLVideoElement, canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, now?: Date,
|
|
1157
|
+
declare function getLatency(sourceEl: HTMLVideoElement, canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, now?: Date, tolerance?: number): number;
|
|
1145
1158
|
/**
|
|
1146
1159
|
* Decode a previously encoded timestamp from a canvas
|
|
1147
1160
|
*
|
|
1148
1161
|
* @param {CanvasRenderingContext2D} context
|
|
1149
1162
|
* @param {Number} lineWidth width of the line in pixels
|
|
1150
|
-
* @param {Number} blocksPerRow number of blocks in the line, 32 by default
|
|
1151
1163
|
* @param {Number} tolerance percentage of tolerance for the black and white threshold, 0.2 by default
|
|
1152
1164
|
* @returns {Date|null} The Date object representing the timestamp or null if the timestamp cannot be decoded
|
|
1153
1165
|
*/
|
|
1154
|
-
declare function decodeTimestamp(context: CanvasRenderingContext2D, lineWidth: number,
|
|
1166
|
+
declare function decodeTimestamp(context: CanvasRenderingContext2D, lineWidth: number, tolerance?: number): Date | null;
|
|
1155
1167
|
/**
|
|
1156
|
-
* Encode the
|
|
1157
|
-
* written on the top of the canvas.
|
|
1168
|
+
* Encode the given date (excluding year and month) into a line composed of blocks
|
|
1169
|
+
* of black and white pixels written on the top of the canvas.
|
|
1158
1170
|
*
|
|
1159
1171
|
* @param {CanvasRenderingContext2D} context
|
|
1160
1172
|
* @param {Number} lineWidth width of the line in pixels
|
package/dist/web-utils.js
CHANGED
|
@@ -476,12 +476,14 @@ function fetch(input, init) {
|
|
|
476
476
|
* Fetch help method adding an explicit error property when Response is NOK, with the more accurate textual error inside
|
|
477
477
|
* Also measure the rtt of fetching and returns it in the property Response.rtt (guaranteed to be ≥ 1),
|
|
478
478
|
* supports subtracting server processing time using either the Response-Delay or CMSD-rd header when available
|
|
479
|
+
*
|
|
480
|
+
* WIP => replace the current implementation to use Resource Timing API
|
|
479
481
|
*/
|
|
480
482
|
function fetchWithRTT(input, init) {
|
|
481
483
|
return __awaiter(this, void 0, void 0, function* () {
|
|
482
|
-
//
|
|
483
|
-
yield fetch(input, Object.assign(Object.assign({}, init), { method: '
|
|
484
|
-
//
|
|
484
|
+
// A first OPTIONS request to establish a connection (keep-alive)
|
|
485
|
+
yield fetch(input, Object.assign(Object.assign({}, init), { method: 'OPTIONS' }));
|
|
486
|
+
// Actual RTT measurement
|
|
485
487
|
const startTime = time();
|
|
486
488
|
const response = (yield fetch(input, init));
|
|
487
489
|
response.rtt = time() - startTime;
|
|
@@ -584,7 +586,30 @@ function trimEnd(value, chars = ' ') {
|
|
|
584
586
|
--i;
|
|
585
587
|
}
|
|
586
588
|
return value.substring(0, i);
|
|
587
|
-
}
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Wraps an object with a Proxy that makes property access case-insensitive.
|
|
592
|
+
*
|
|
593
|
+
* Property lookup (e.g. `obj.Foo` or `obj.foo`) will resolve to the same underlying key, regardless of casing.
|
|
594
|
+
* Only affects string-based property access (not symbols).
|
|
595
|
+
*
|
|
596
|
+
* @template T
|
|
597
|
+
* @param {T} obj - The original object.
|
|
598
|
+
* @returns {T} A proxied object with case-insensitive property access.
|
|
599
|
+
*/
|
|
600
|
+
function caseInsensitive(obj) {
|
|
601
|
+
return new Proxy(obj, {
|
|
602
|
+
get(target, prop, receiver) {
|
|
603
|
+
if (typeof prop === 'string') {
|
|
604
|
+
const key = Object.keys(target).find(k => k.toLowerCase() === prop.toLowerCase());
|
|
605
|
+
if (key !== undefined) {
|
|
606
|
+
return Reflect.get(target, key, receiver);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return Reflect.get(target, prop, receiver);
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
}var Util=/*#__PURE__*/Object.freeze({__proto__:null,EMPTY_FUNCTION:EMPTY_FUNCTION,caseInsensitive:caseInsensitive,equal:equal,fetch:fetch,fetchWithRTT:fetchWithRTT,getBaseFile:getBaseFile,getExtension:getExtension,getFile:getFile,iterableEntries:iterableEntries,objectFrom:objectFrom,options:options,safePromise:safePromise,sleep:sleep,stringify:stringify,time:time,toBin:toBin,trim:trim,trimEnd:trimEnd,trimStart:trimStart,unixTime:unixTime});/**
|
|
588
613
|
* Copyright 2024 Ceeblue B.V.
|
|
589
614
|
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
|
|
590
615
|
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
|
|
@@ -1447,7 +1472,7 @@ class EventEmitter extends Loggable {
|
|
|
1447
1472
|
* Event subscription only one time, once time fired it's automatically unsubscribe
|
|
1448
1473
|
* @param name Name of event without the `on` prefix (ex: `log` to `onLog` event declared)
|
|
1449
1474
|
* @param event Subscriber Function
|
|
1450
|
-
* @param options.
|
|
1475
|
+
* @param options.signal Optional `AbortSignal` to stop this or multiple subscriptions in same time
|
|
1451
1476
|
*/
|
|
1452
1477
|
once(name, event, options) {
|
|
1453
1478
|
var _a;
|
|
@@ -2195,6 +2220,8 @@ class WebSocketReliable extends EventEmitter {
|
|
|
2195
2220
|
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
|
|
2196
2221
|
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
|
|
2197
2222
|
*/
|
|
2223
|
+
// Fixed blocks to 32 because timestamp is encoded on 32 bits
|
|
2224
|
+
const _blocksPerRow = 32;
|
|
2198
2225
|
/**
|
|
2199
2226
|
* Decode the timestamp from a video element and compute the latency between the timestamp and the current date.
|
|
2200
2227
|
*
|
|
@@ -2202,19 +2229,18 @@ class WebSocketReliable extends EventEmitter {
|
|
|
2202
2229
|
* @param {HTMLCanvasElement} canvas
|
|
2203
2230
|
* @param {CanvasRenderingContext2D} context
|
|
2204
2231
|
* @param {Date} now current date, new Date() by default
|
|
2205
|
-
* @param {Number} blocksPerRow number of blocks in the line, 32 by default
|
|
2206
2232
|
* @param {Number} tolerance percentage of tolerance for the black and white threshold, 0.2 by default
|
|
2207
2233
|
* @returns {Number} The latency in millisecond between 'now' and the decoded timestamp, 0 if the timestamp cannot be decoded
|
|
2208
2234
|
*/
|
|
2209
|
-
function getLatency(sourceEl, canvas, context, now = new Date(),
|
|
2235
|
+
function getLatency(sourceEl, canvas, context, now = new Date(), tolerance = 0.2) {
|
|
2210
2236
|
canvas.width = sourceEl.videoWidth;
|
|
2211
|
-
canvas.height = Math.floor(canvas.width /
|
|
2237
|
+
canvas.height = Math.floor(canvas.width / _blocksPerRow);
|
|
2212
2238
|
if (!canvas.width || !canvas.height) {
|
|
2213
2239
|
// No pixel to parse!
|
|
2214
2240
|
return 0;
|
|
2215
2241
|
}
|
|
2216
2242
|
context.drawImage(sourceEl, 0, 0, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
|
|
2217
|
-
const timestamp = decodeTimestamp(context, canvas.width,
|
|
2243
|
+
const timestamp = decodeTimestamp(context, canvas.width, tolerance);
|
|
2218
2244
|
return timestamp == null ? 0 : now.getTime() - timestamp.getTime();
|
|
2219
2245
|
}
|
|
2220
2246
|
/**
|
|
@@ -2222,20 +2248,25 @@ function getLatency(sourceEl, canvas, context, now = new Date(), blocksPerRow =
|
|
|
2222
2248
|
*
|
|
2223
2249
|
* @param {CanvasRenderingContext2D} context
|
|
2224
2250
|
* @param {Number} lineWidth width of the line in pixels
|
|
2225
|
-
* @param {Number} blocksPerRow number of blocks in the line, 32 by default
|
|
2226
2251
|
* @param {Number} tolerance percentage of tolerance for the black and white threshold, 0.2 by default
|
|
2227
2252
|
* @returns {Date|null} The Date object representing the timestamp or null if the timestamp cannot be decoded
|
|
2228
2253
|
*/
|
|
2229
|
-
function decodeTimestamp(context, lineWidth,
|
|
2230
|
-
|
|
2254
|
+
function decodeTimestamp(context, lineWidth, tolerance = 0.2) {
|
|
2255
|
+
// Integer block width and ignore the right-hand remainder.
|
|
2256
|
+
const blockSize = Math.floor(lineWidth / _blocksPerRow);
|
|
2231
2257
|
let binaryTime = '';
|
|
2232
|
-
|
|
2233
|
-
|
|
2258
|
+
if (blockSize < 1) {
|
|
2259
|
+
return null;
|
|
2260
|
+
}
|
|
2261
|
+
const effectiveWidth = blockSize * _blocksPerRow;
|
|
2262
|
+
const midBlock = Math.floor(blockSize / 2);
|
|
2263
|
+
const data = context.getImageData(0, midBlock, effectiveWidth, 1).data;
|
|
2234
2264
|
const pixels = new Uint32Array(data.buffer);
|
|
2235
2265
|
const blackThreshold = 0xff * tolerance;
|
|
2236
2266
|
const whiteThreshold = 0xff * (1 - tolerance);
|
|
2237
|
-
|
|
2238
|
-
|
|
2267
|
+
// Sample the center pixel of each integer-sized block that the encoder painted.
|
|
2268
|
+
for (let i = 0; i < effectiveWidth; i += blockSize) {
|
|
2269
|
+
const pixel = pixels[i + midBlock] & 0xffffff;
|
|
2239
2270
|
// Extract luminance from RGB
|
|
2240
2271
|
const Y = 0.299 * ((pixel >> 16) & 0xff) + 0.587 * ((pixel >> 8) & 0xff) + 0.114 * (pixel & 0xff);
|
|
2241
2272
|
if (Y < blackThreshold) {
|
|
@@ -2247,21 +2278,24 @@ function decodeTimestamp(context, lineWidth, blocksPerRow = 32, tolerance = 0.2)
|
|
|
2247
2278
|
binaryTime += '0';
|
|
2248
2279
|
}
|
|
2249
2280
|
else {
|
|
2250
|
-
return;
|
|
2281
|
+
return null;
|
|
2251
2282
|
}
|
|
2252
|
-
i += blockSize;
|
|
2253
2283
|
}
|
|
2254
2284
|
const day = parseInt(binaryTime.slice(0, 5), 2);
|
|
2255
2285
|
const hour = parseInt(binaryTime.slice(5, 10), 2);
|
|
2256
2286
|
const minute = parseInt(binaryTime.slice(10, 16), 2);
|
|
2257
2287
|
const second = parseInt(binaryTime.slice(16, 22), 2);
|
|
2258
2288
|
const millisecond = parseInt(binaryTime.slice(22, 32), 2);
|
|
2289
|
+
// Basic sanity checks (match encoder ranges)
|
|
2290
|
+
if (day < 1 || day > 31 || hour > 23 || minute > 59 || second > 59 || millisecond > 999) {
|
|
2291
|
+
return null;
|
|
2292
|
+
}
|
|
2259
2293
|
const now = new Date();
|
|
2260
2294
|
return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), day, hour, minute, second, millisecond));
|
|
2261
2295
|
}
|
|
2262
2296
|
/**
|
|
2263
|
-
* Encode the
|
|
2264
|
-
* written on the top of the canvas.
|
|
2297
|
+
* Encode the given date (excluding year and month) into a line composed of blocks
|
|
2298
|
+
* of black and white pixels written on the top of the canvas.
|
|
2265
2299
|
*
|
|
2266
2300
|
* @param {CanvasRenderingContext2D} context
|
|
2267
2301
|
* @param {Number} lineWidth width of the line in pixels
|
|
@@ -2275,11 +2309,11 @@ function encodeTimestamp(context, lineWidth, blocksPerRow = 32, now = new Date()
|
|
|
2275
2309
|
const minute = now.getUTCMinutes();
|
|
2276
2310
|
const second = now.getUTCSeconds();
|
|
2277
2311
|
const millisecond = now.getUTCMilliseconds();
|
|
2278
|
-
const binaryDay = day.toString(2).padStart(5, '0');
|
|
2279
|
-
const binaryHour = hour.toString(2).padStart(5, '0');
|
|
2280
|
-
const binaryMinute = minute.toString(2).padStart(6, '0');
|
|
2281
|
-
const binarySecond = second.toString(2).padStart(6, '0');
|
|
2282
|
-
const binaryMillisecond = millisecond.toString(2).padStart(10, '0');
|
|
2312
|
+
const binaryDay = day.toString(2).padStart(5, '0'); // 31 possible days/32
|
|
2313
|
+
const binaryHour = hour.toString(2).padStart(5, '0'); // 24 possible hours/32
|
|
2314
|
+
const binaryMinute = minute.toString(2).padStart(6, '0'); // 60 possible minutes/64
|
|
2315
|
+
const binarySecond = second.toString(2).padStart(6, '0'); // 60 possible seconds/64
|
|
2316
|
+
const binaryMillisecond = millisecond.toString(2).padStart(10, '0'); // 1000 possible milliseconds/1024
|
|
2283
2317
|
const binaryTime = binaryDay + binaryHour + binaryMinute + binarySecond + binaryMillisecond;
|
|
2284
2318
|
for (let i = 0; i < binaryTime.length; i++) {
|
|
2285
2319
|
const x = (i % blocksPerRow) * blockSize;
|
|
@@ -2546,4 +2580,4 @@ class UIMetrics {
|
|
|
2546
2580
|
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
|
|
2547
2581
|
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
|
|
2548
2582
|
*/
|
|
2549
|
-
const VERSION = '7.
|
|
2583
|
+
const VERSION = '7.1.0';export{BinaryReader,BinaryWriter,BitReader,ByteRate,Connect,EpochTime,EventEmitter,FixMap,Log,LogLevel,Loggable,NetAddress,Numbers,Queue,SDP,UIMetrics,Util,VERSION,WebSocketReliable,log};//# sourceMappingURL=web-utils.js.map
|