@ceeblue/web-utils 7.0.1 → 7.2.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 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
- > 💡 **TIP**
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
- > ⚠️ **REMARKS**
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
- You can access the documentation by opening the index.html file in the docs folder with your browser (`./docs/index.html`).
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
 
@@ -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.abortSignal Optional `AbortSignal` to stop this or multiple subscriptions in same time
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;
@@ -950,8 +950,20 @@ declare function trimStart(value: string, chars?: string): string;
950
950
  * @returns string trimmed
951
951
  */
952
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;
953
964
 
954
965
  declare const Util_EMPTY_FUNCTION: typeof EMPTY_FUNCTION;
966
+ declare const Util_caseInsensitive: typeof caseInsensitive;
955
967
  declare const Util_equal: typeof equal;
956
968
  declare const Util_fetch: typeof fetch;
957
969
  declare const Util_fetchWithRTT: typeof fetchWithRTT;
@@ -971,7 +983,7 @@ declare const Util_trimEnd: typeof trimEnd;
971
983
  declare const Util_trimStart: typeof trimStart;
972
984
  declare const Util_unixTime: typeof unixTime;
973
985
  declare namespace Util {
974
- 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 };
975
987
  }
976
988
 
977
989
  /**
@@ -1139,24 +1151,22 @@ declare class WebSocketReliable extends EventEmitter {
1139
1151
  * @param {HTMLCanvasElement} canvas
1140
1152
  * @param {CanvasRenderingContext2D} context
1141
1153
  * @param {Date} now current date, new Date() by default
1142
- * @param {Number} blocksPerRow number of blocks in the line, 32 by default
1143
1154
  * @param {Number} tolerance percentage of tolerance for the black and white threshold, 0.2 by default
1144
1155
  * @returns {Number} The latency in millisecond between 'now' and the decoded timestamp, 0 if the timestamp cannot be decoded
1145
1156
  */
1146
- declare function getLatency(sourceEl: HTMLVideoElement, canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, now?: Date, blocksPerRow?: number, tolerance?: number): number;
1157
+ declare function getLatency(sourceEl: HTMLVideoElement, canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, now?: Date, tolerance?: number): number;
1147
1158
  /**
1148
1159
  * Decode a previously encoded timestamp from a canvas
1149
1160
  *
1150
1161
  * @param {CanvasRenderingContext2D} context
1151
1162
  * @param {Number} lineWidth width of the line in pixels
1152
- * @param {Number} blocksPerRow number of blocks in the line, 32 by default
1153
1163
  * @param {Number} tolerance percentage of tolerance for the black and white threshold, 0.2 by default
1154
1164
  * @returns {Date|null} The Date object representing the timestamp or null if the timestamp cannot be decoded
1155
1165
  */
1156
- declare function decodeTimestamp(context: CanvasRenderingContext2D, lineWidth: number, blocksPerRow?: number, tolerance?: number): Date | undefined;
1166
+ declare function decodeTimestamp(context: CanvasRenderingContext2D, lineWidth: number, tolerance?: number): Date | null;
1157
1167
  /**
1158
- * Encode the current timestamp into a line composed of blocks of black and white pixels
1159
- * 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.
1160
1170
  *
1161
1171
  * @param {CanvasRenderingContext2D} context
1162
1172
  * @param {Number} lineWidth width of the line in pixels
package/dist/web-utils.js CHANGED
@@ -586,7 +586,30 @@ function trimEnd(value, chars = ' ') {
586
586
  --i;
587
587
  }
588
588
  return value.substring(0, i);
589
- }var Util=/*#__PURE__*/Object.freeze({__proto__:null,EMPTY_FUNCTION:EMPTY_FUNCTION,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});/**
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});/**
590
613
  * Copyright 2024 Ceeblue B.V.
591
614
  * This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
592
615
  * See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
@@ -1449,7 +1472,7 @@ class EventEmitter extends Loggable {
1449
1472
  * Event subscription only one time, once time fired it's automatically unsubscribe
1450
1473
  * @param name Name of event without the `on` prefix (ex: `log` to `onLog` event declared)
1451
1474
  * @param event Subscriber Function
1452
- * @param options.abortSignal Optional `AbortSignal` to stop this or multiple subscriptions in same time
1475
+ * @param options.signal Optional `AbortSignal` to stop this or multiple subscriptions in same time
1453
1476
  */
1454
1477
  once(name, event, options) {
1455
1478
  var _a;
@@ -2197,6 +2220,8 @@ class WebSocketReliable extends EventEmitter {
2197
2220
  * This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
2198
2221
  * See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
2199
2222
  */
2223
+ // Fixed blocks to 32 because timestamp is encoded on 32 bits
2224
+ const _blocksPerRow = 32;
2200
2225
  /**
2201
2226
  * Decode the timestamp from a video element and compute the latency between the timestamp and the current date.
2202
2227
  *
@@ -2204,19 +2229,18 @@ class WebSocketReliable extends EventEmitter {
2204
2229
  * @param {HTMLCanvasElement} canvas
2205
2230
  * @param {CanvasRenderingContext2D} context
2206
2231
  * @param {Date} now current date, new Date() by default
2207
- * @param {Number} blocksPerRow number of blocks in the line, 32 by default
2208
2232
  * @param {Number} tolerance percentage of tolerance for the black and white threshold, 0.2 by default
2209
2233
  * @returns {Number} The latency in millisecond between 'now' and the decoded timestamp, 0 if the timestamp cannot be decoded
2210
2234
  */
2211
- function getLatency(sourceEl, canvas, context, now = new Date(), blocksPerRow = 32, tolerance = 0.2) {
2235
+ function getLatency(sourceEl, canvas, context, now = new Date(), tolerance = 0.2) {
2212
2236
  canvas.width = sourceEl.videoWidth;
2213
- canvas.height = Math.floor(canvas.width / blocksPerRow);
2237
+ canvas.height = Math.floor(canvas.width / _blocksPerRow);
2214
2238
  if (!canvas.width || !canvas.height) {
2215
2239
  // No pixel to parse!
2216
2240
  return 0;
2217
2241
  }
2218
2242
  context.drawImage(sourceEl, 0, 0, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
2219
- const timestamp = decodeTimestamp(context, canvas.width, blocksPerRow, tolerance);
2243
+ const timestamp = decodeTimestamp(context, canvas.width, tolerance);
2220
2244
  return timestamp == null ? 0 : now.getTime() - timestamp.getTime();
2221
2245
  }
2222
2246
  /**
@@ -2224,20 +2248,25 @@ function getLatency(sourceEl, canvas, context, now = new Date(), blocksPerRow =
2224
2248
  *
2225
2249
  * @param {CanvasRenderingContext2D} context
2226
2250
  * @param {Number} lineWidth width of the line in pixels
2227
- * @param {Number} blocksPerRow number of blocks in the line, 32 by default
2228
2251
  * @param {Number} tolerance percentage of tolerance for the black and white threshold, 0.2 by default
2229
2252
  * @returns {Date|null} The Date object representing the timestamp or null if the timestamp cannot be decoded
2230
2253
  */
2231
- function decodeTimestamp(context, lineWidth, blocksPerRow = 32, tolerance = 0.2) {
2232
- const blockSize = lineWidth / blocksPerRow;
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);
2233
2257
  let binaryTime = '';
2234
- let i = blockSize / 2;
2235
- const data = context.getImageData(0, Math.round(i), lineWidth, 1).data;
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;
2236
2264
  const pixels = new Uint32Array(data.buffer);
2237
2265
  const blackThreshold = 0xff * tolerance;
2238
2266
  const whiteThreshold = 0xff * (1 - tolerance);
2239
- while (i < pixels.length) {
2240
- const pixel = pixels[Math.round(i)] & 0xffffff;
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;
2241
2270
  // Extract luminance from RGB
2242
2271
  const Y = 0.299 * ((pixel >> 16) & 0xff) + 0.587 * ((pixel >> 8) & 0xff) + 0.114 * (pixel & 0xff);
2243
2272
  if (Y < blackThreshold) {
@@ -2249,21 +2278,24 @@ function decodeTimestamp(context, lineWidth, blocksPerRow = 32, tolerance = 0.2)
2249
2278
  binaryTime += '0';
2250
2279
  }
2251
2280
  else {
2252
- return;
2281
+ return null;
2253
2282
  }
2254
- i += blockSize;
2255
2283
  }
2256
2284
  const day = parseInt(binaryTime.slice(0, 5), 2);
2257
2285
  const hour = parseInt(binaryTime.slice(5, 10), 2);
2258
2286
  const minute = parseInt(binaryTime.slice(10, 16), 2);
2259
2287
  const second = parseInt(binaryTime.slice(16, 22), 2);
2260
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
+ }
2261
2293
  const now = new Date();
2262
2294
  return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), day, hour, minute, second, millisecond));
2263
2295
  }
2264
2296
  /**
2265
- * Encode the current timestamp into a line composed of blocks of black and white pixels
2266
- * 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.
2267
2299
  *
2268
2300
  * @param {CanvasRenderingContext2D} context
2269
2301
  * @param {Number} lineWidth width of the line in pixels
@@ -2277,11 +2309,11 @@ function encodeTimestamp(context, lineWidth, blocksPerRow = 32, now = new Date()
2277
2309
  const minute = now.getUTCMinutes();
2278
2310
  const second = now.getUTCSeconds();
2279
2311
  const millisecond = now.getUTCMilliseconds();
2280
- const binaryDay = day.toString(2).padStart(5, '0');
2281
- const binaryHour = hour.toString(2).padStart(5, '0');
2282
- const binaryMinute = minute.toString(2).padStart(6, '0');
2283
- const binarySecond = second.toString(2).padStart(6, '0');
2284
- 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
2285
2317
  const binaryTime = binaryDay + binaryHour + binaryMinute + binarySecond + binaryMillisecond;
2286
2318
  for (let i = 0; i < binaryTime.length; i++) {
2287
2319
  const x = (i % blocksPerRow) * blockSize;
@@ -2548,4 +2580,4 @@ class UIMetrics {
2548
2580
  * This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
2549
2581
  * See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
2550
2582
  */
2551
- const VERSION = '7.0.1';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
2583
+ const VERSION = '7.2.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