homebridge-plugin-utils 1.33.0 → 1.35.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/build/tsconfig.json +6 -4
- package/dist/backpressure.d.ts +79 -0
- package/dist/backpressure.js +137 -0
- package/dist/backpressure.js.map +1 -0
- package/dist/featureoptions.d.ts +3 -3
- package/dist/featureoptions.js +122 -97
- package/dist/featureoptions.js.map +1 -1
- package/dist/ffmpeg/codecs.js +26 -20
- package/dist/ffmpeg/codecs.js.map +1 -1
- package/dist/ffmpeg/exec.d.ts +1 -1
- package/dist/ffmpeg/exec.js.map +1 -1
- package/dist/ffmpeg/fmp4.d.ts +21 -2
- package/dist/ffmpeg/fmp4.js +55 -2
- package/dist/ffmpeg/fmp4.js.map +1 -1
- package/dist/ffmpeg/options.d.ts +6 -1
- package/dist/ffmpeg/options.js +13 -14
- package/dist/ffmpeg/options.js.map +1 -1
- package/dist/ffmpeg/process.d.ts +26 -14
- package/dist/ffmpeg/process.js +60 -36
- package/dist/ffmpeg/process.js.map +1 -1
- package/dist/ffmpeg/record.d.ts +121 -122
- package/dist/ffmpeg/record.js +363 -285
- package/dist/ffmpeg/record.js.map +1 -1
- package/dist/ffmpeg/rtp.d.ts +2 -2
- package/dist/ffmpeg/rtp.js +9 -2
- package/dist/ffmpeg/rtp.js.map +1 -1
- package/dist/ffmpeg/settings.d.ts +1 -0
- package/dist/ffmpeg/settings.js +3 -0
- package/dist/ffmpeg/settings.js.map +1 -1
- package/dist/ffmpeg/stream.d.ts +1 -1
- package/dist/ffmpeg/stream.js +6 -4
- package/dist/ffmpeg/stream.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mqttclient.js +6 -5
- package/dist/mqttclient.js.map +1 -1
- package/dist/service.d.ts +1 -1
- package/dist/service.js +10 -4
- package/dist/service.js.map +1 -1
- package/dist/ui/featureoptions.js +122 -97
- package/dist/ui/featureoptions.js.map +1 -1
- package/dist/util.d.ts +18 -19
- package/dist/util.js +43 -21
- package/dist/util.js.map +1 -1
- package/package.json +7 -8
|
@@ -51,6 +51,7 @@ export class FeatureOptions {
|
|
|
51
51
|
_configuredOptions;
|
|
52
52
|
_groups;
|
|
53
53
|
_options;
|
|
54
|
+
configLookup;
|
|
54
55
|
defaults;
|
|
55
56
|
valueOptions;
|
|
56
57
|
/**
|
|
@@ -72,6 +73,7 @@ export class FeatureOptions {
|
|
|
72
73
|
this._configuredOptions = [];
|
|
73
74
|
this._groups = {};
|
|
74
75
|
this._options = {};
|
|
76
|
+
this.configLookup = new Map();
|
|
75
77
|
this.defaultReturnValue = false;
|
|
76
78
|
this.defaults = {};
|
|
77
79
|
this.valueOptions = {};
|
|
@@ -121,8 +123,7 @@ export class FeatureOptions {
|
|
|
121
123
|
* @returns Returns true if the option has been explicitly configured, false otherwise.
|
|
122
124
|
*/
|
|
123
125
|
exists(option, id) {
|
|
124
|
-
|
|
125
|
-
return this.configuredOptions.some(x => regex.test(x));
|
|
126
|
+
return this.configLookup.has(option.toLowerCase() + (id ? "." + id.toLowerCase() : ""));
|
|
126
127
|
}
|
|
127
128
|
/**
|
|
128
129
|
* Return a fully formed feature option string.
|
|
@@ -238,53 +239,22 @@ export class FeatureOptions {
|
|
|
238
239
|
if (!this.isValue(option)) {
|
|
239
240
|
return null;
|
|
240
241
|
}
|
|
241
|
-
//
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
for (const entry of this.configuredOptions) {
|
|
247
|
-
const regexMatch = regex.exec(entry);
|
|
248
|
-
if (regexMatch) {
|
|
249
|
-
// If the option is enabled, return the value. Otherwise, we have nothing.
|
|
250
|
-
return (regexMatch[1].toLowerCase() === "enable") ? regexMatch[2] : null;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
return undefined;
|
|
254
|
-
};
|
|
255
|
-
// Check to see if we have a device-level value first.
|
|
256
|
-
if (device) {
|
|
257
|
-
const value = getValue(option, device);
|
|
258
|
-
// The option's been explicitly disabled.
|
|
259
|
-
if (value === null) {
|
|
260
|
-
return null;
|
|
261
|
-
}
|
|
262
|
-
if (value) {
|
|
263
|
-
return value;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
// Now check to see if we have an controller-level value.
|
|
267
|
-
if (controller) {
|
|
268
|
-
const value = getValue(option, controller);
|
|
269
|
-
// The option's been explicitly disabled.
|
|
270
|
-
if (value === null) {
|
|
271
|
-
return null;
|
|
272
|
-
}
|
|
273
|
-
if (value) {
|
|
274
|
-
return value;
|
|
275
|
-
}
|
|
242
|
+
// Resolve the option through the scope hierarchy in a single traversal. This gives us the scope, enabled state, and raw value in one pass.
|
|
243
|
+
const resolved = this.resolveScope(option, device, controller);
|
|
244
|
+
// If the option has been explicitly disabled at any scope, or wasn't configured and its default is disabled, there's no value.
|
|
245
|
+
if (!resolved.enabled) {
|
|
246
|
+
return null;
|
|
276
247
|
}
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
return value;
|
|
248
|
+
// If we found an explicit value in the index, return it.
|
|
249
|
+
if (resolved.optionValue) {
|
|
250
|
+
return resolved.optionValue;
|
|
281
251
|
}
|
|
282
|
-
// The option'
|
|
283
|
-
if (
|
|
284
|
-
return null;
|
|
252
|
+
// The option is enabled but has no explicit value. If it wasn't configured at any scope (scope is "none"), fall back to the registered default value.
|
|
253
|
+
if (resolved.scope === "none") {
|
|
254
|
+
return this.valueOptions[option.toLowerCase()]?.toString() ?? null;
|
|
285
255
|
}
|
|
286
|
-
//
|
|
287
|
-
return
|
|
256
|
+
// The option is enabled at an explicit scope but no value was provided...return undefined to indicate "enabled, no value."
|
|
257
|
+
return undefined;
|
|
288
258
|
}
|
|
289
259
|
/**
|
|
290
260
|
* Return the list of available feature option categories.
|
|
@@ -301,6 +271,8 @@ export class FeatureOptions {
|
|
|
301
271
|
*/
|
|
302
272
|
set categories(category) {
|
|
303
273
|
this._categories = category;
|
|
274
|
+
// Regenerate defaults and the lookup index.
|
|
275
|
+
this.generateDefaults();
|
|
304
276
|
}
|
|
305
277
|
/**
|
|
306
278
|
* Return the list of currently configured feature options.
|
|
@@ -318,6 +290,8 @@ export class FeatureOptions {
|
|
|
318
290
|
set configuredOptions(options) {
|
|
319
291
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
320
292
|
this._configuredOptions = options ?? [];
|
|
293
|
+
// Regenerate defaults and the lookup index.
|
|
294
|
+
this.generateDefaults();
|
|
321
295
|
}
|
|
322
296
|
/**
|
|
323
297
|
* Return the list of available feature option groups.
|
|
@@ -343,10 +317,11 @@ export class FeatureOptions {
|
|
|
343
317
|
set options(options) {
|
|
344
318
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
345
319
|
this._options = options ?? {};
|
|
346
|
-
// Regenerate
|
|
320
|
+
// Regenerate defaults and the lookup index.
|
|
347
321
|
this.generateDefaults();
|
|
348
322
|
}
|
|
349
|
-
//
|
|
323
|
+
// Rebuild the defaults, groups, value options, and lookup index from the current categories, options, and configured options. All three property setters call this so
|
|
324
|
+
// that state is always consistent regardless of which setter is called or in what order.
|
|
350
325
|
generateDefaults() {
|
|
351
326
|
this.defaults = {};
|
|
352
327
|
this._groups = {};
|
|
@@ -375,58 +350,47 @@ export class FeatureOptions {
|
|
|
375
350
|
}
|
|
376
351
|
}
|
|
377
352
|
}
|
|
353
|
+
// Rebuild the lookup index now that we know which options are value-centric.
|
|
354
|
+
this.buildConfigIndex();
|
|
378
355
|
}
|
|
379
|
-
//
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
356
|
+
// Resolves a feature option through the scope hierarchy in a single traversal. Returns the scope where the option was found, whether it's enabled, and the raw string
|
|
357
|
+
// value for value-centric options. This is the core resolution method that both test()/scope() and value() build on, eliminating the need for separate traversals.
|
|
358
|
+
//
|
|
359
|
+
// There are a couple of ways to enable and disable options. The rules of the road are:
|
|
360
|
+
//
|
|
361
|
+
// 1. Explicitly disabling or enabling an option on the controller propagates to all the devices that are managed by that controller.
|
|
362
|
+
//
|
|
363
|
+
// 2. Explicitly disabling or enabling an option on a device always overrides the above. This means that it's possible to disable an option for a controller, and all
|
|
364
|
+
// the devices that are managed by it, and then override that behavior on a single device that it's managing.
|
|
365
|
+
resolveScope(option, device, controller) {
|
|
366
|
+
const normalizedOption = option.toLowerCase();
|
|
367
|
+
let entry;
|
|
388
368
|
// Check to see if we have a device-level option first.
|
|
389
|
-
if (device
|
|
390
|
-
|
|
391
|
-
if (
|
|
392
|
-
return {
|
|
369
|
+
if (device) {
|
|
370
|
+
entry = this.configLookup.get(normalizedOption + "." + device.toLowerCase());
|
|
371
|
+
if (entry) {
|
|
372
|
+
return { enabled: entry.enabled, optionValue: entry.value, scope: "device" };
|
|
393
373
|
}
|
|
394
374
|
}
|
|
395
|
-
// Now check to see if we have
|
|
396
|
-
if (controller
|
|
397
|
-
|
|
398
|
-
if (
|
|
399
|
-
return {
|
|
375
|
+
// Now check to see if we have a controller-level option.
|
|
376
|
+
if (controller) {
|
|
377
|
+
entry = this.configLookup.get(normalizedOption + "." + controller.toLowerCase());
|
|
378
|
+
if (entry) {
|
|
379
|
+
return { enabled: entry.enabled, optionValue: entry.value, scope: "controller" };
|
|
400
380
|
}
|
|
401
381
|
}
|
|
402
382
|
// Finally, we check for a global-level value.
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
return { scope: "global", value: value };
|
|
407
|
-
}
|
|
383
|
+
entry = this.configLookup.get(normalizedOption);
|
|
384
|
+
if (entry) {
|
|
385
|
+
return { enabled: entry.enabled, optionValue: entry.value, scope: "global" };
|
|
408
386
|
}
|
|
409
387
|
// The option hasn't been set at any scope, return our default value.
|
|
410
|
-
return {
|
|
388
|
+
return { enabled: this.defaultValue(option), scope: "none" };
|
|
411
389
|
}
|
|
412
|
-
//
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
// Get the option value, if we have one.
|
|
417
|
-
for (const entry of this.configuredOptions) {
|
|
418
|
-
const regexMatch = regex.exec(entry);
|
|
419
|
-
if (regexMatch) {
|
|
420
|
-
return regexMatch[1].toLowerCase() === "enable";
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
return undefined;
|
|
424
|
-
}
|
|
425
|
-
// Regular expression test for feature options.
|
|
426
|
-
optionRegex(option, id) {
|
|
427
|
-
// This regular expression is a bit more intricate than you might think it should be due to the need to ensure we capture values at the very end of the option. We
|
|
428
|
-
// also need to escape out our option to ensure we have no inadvertent issues in matching the regular expression.
|
|
429
|
-
return new RegExp("^(Enable|Disable)\\." + option.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + (!id ? "" : "\\." + id) + "$", "gi");
|
|
390
|
+
// Thin wrapper over resolveScope() that returns the OptionInfoEntry shape expected by test() and scope().
|
|
391
|
+
optionInfo(option, device, controller) {
|
|
392
|
+
const resolved = this.resolveScope(option, device, controller);
|
|
393
|
+
return { scope: resolved.scope, value: resolved.enabled };
|
|
430
394
|
}
|
|
431
395
|
// Utility function to parse and return a numeric configuration parameter.
|
|
432
396
|
parseOptionNumeric(option, convert) {
|
|
@@ -443,13 +407,74 @@ export class FeatureOptions {
|
|
|
443
407
|
// Return the value.
|
|
444
408
|
return convertedValue;
|
|
445
409
|
}
|
|
446
|
-
//
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
//
|
|
452
|
-
|
|
410
|
+
// Build a lookup index from the configured option strings. Each entry is keyed by its normalized lookup path (option name, or option name + scope id) and stores
|
|
411
|
+
// whether the option is enabled along with the extracted value for value-centric options. The index is built once when configured options or option definitions change,
|
|
412
|
+
// and all subsequent lookups are O(1).
|
|
413
|
+
buildConfigIndex() {
|
|
414
|
+
this.configLookup = new Map();
|
|
415
|
+
// Collect known value option names, sorted longest first for greedy prefix matching. This ensures that when option names overlap (e.g., a category "audio" and an
|
|
416
|
+
// option "audio.volume"), the more specific name matches first.
|
|
417
|
+
const valueOptionNames = Object.keys(this.valueOptions).sort((a, b) => b.length - a.length);
|
|
418
|
+
for (const rawEntry of this._configuredOptions) {
|
|
419
|
+
// Parse the action prefix (Enable or Disable).
|
|
420
|
+
const dotIndex = rawEntry.indexOf(".");
|
|
421
|
+
if (dotIndex === -1) {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
const action = rawEntry.slice(0, dotIndex).toLowerCase();
|
|
425
|
+
if ((action !== "enable") && (action !== "disable")) {
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
const enabled = action === "enable";
|
|
429
|
+
const tailOriginal = rawEntry.slice(dotIndex + 1);
|
|
430
|
+
const tail = tailOriginal.toLowerCase();
|
|
431
|
+
// Register the exact tail as a lookup key. First-write-wins...if the same option appears multiple times, the earliest entry in the array takes precedence.
|
|
432
|
+
if (!this.configLookup.has(tail)) {
|
|
433
|
+
this.configLookup.set(tail, { enabled });
|
|
434
|
+
}
|
|
435
|
+
// For Enable entries on value-centric options, extract the trailing value segment and register under the base key (option or option.id) so that value lookups
|
|
436
|
+
// resolve in O(1) instead of requiring regex matching and array scanning.
|
|
437
|
+
if (!enabled) {
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
for (const optName of valueOptionNames) {
|
|
441
|
+
if (!tail.startsWith(optName)) {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
const remainder = tail.slice(optName.length);
|
|
445
|
+
// Exact match on the option name with no trailing segments...there's no value to extract.
|
|
446
|
+
if (!remainder.length) {
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
// The next character must be a dot separator. If not, this option name is merely a prefix of a longer unrelated token.
|
|
450
|
+
if (!remainder.startsWith(".")) {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
const extra = remainder.slice(1);
|
|
454
|
+
const extraOriginal = tailOriginal.slice(optName.length + 1);
|
|
455
|
+
const separatorIndex = extra.indexOf(".");
|
|
456
|
+
if (separatorIndex === -1) {
|
|
457
|
+
// Single trailing segment after the option name. At global scope this is the value; at scoped scope it's the id. Register under the option name as the
|
|
458
|
+
// base key so that global value lookups find it.
|
|
459
|
+
if (!this.configLookup.has(optName)) {
|
|
460
|
+
this.configLookup.set(optName, { enabled: true, value: extraOriginal });
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
// Two trailing segments: the first is the scope id and the second is the value.
|
|
465
|
+
const idLower = extra.slice(0, separatorIndex);
|
|
466
|
+
const valueOriginal = extraOriginal.slice(separatorIndex + 1);
|
|
467
|
+
// Only register if the value portion is a single segment (no additional dots).
|
|
468
|
+
if (!valueOriginal.includes(".")) {
|
|
469
|
+
const baseKey = optName + "." + idLower;
|
|
470
|
+
if (!this.configLookup.has(baseKey)) {
|
|
471
|
+
this.configLookup.set(baseKey, { enabled: true, value: valueOriginal });
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
break;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
453
478
|
}
|
|
454
479
|
}
|
|
455
480
|
//# sourceMappingURL=featureoptions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"featureoptions.js","sourceRoot":"","sources":["../src/featureoptions.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"featureoptions.js","sourceRoot":"","sources":["../src/featureoptions.ts"],"names":[],"mappings":"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"}
|
package/dist/util.d.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TypeScript Utilities.
|
|
3
|
-
*
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
1
|
/**
|
|
7
2
|
* A utility type that recursively makes all properties of an object, including nested objects, optional.
|
|
8
3
|
*
|
|
@@ -81,9 +76,10 @@ export type DeepReadonly<T> = {
|
|
|
81
76
|
*/
|
|
82
77
|
export type Nullable<T> = T | null;
|
|
83
78
|
/**
|
|
84
|
-
* Makes all properties in `T` optional except for `
|
|
79
|
+
* Makes all properties in `T` optional except for those specified by `K`, which remain required.
|
|
85
80
|
*
|
|
86
|
-
* @
|
|
81
|
+
* @typeParam T - The base interface or type.
|
|
82
|
+
* @typeParam K - The keys of `T` that should remain required.
|
|
87
83
|
*
|
|
88
84
|
* @example
|
|
89
85
|
*
|
|
@@ -95,7 +91,7 @@ export type Nullable<T> = T | null;
|
|
|
95
91
|
* mac: string;
|
|
96
92
|
* }
|
|
97
93
|
*
|
|
98
|
-
* type
|
|
94
|
+
* type DeviceUpdate = PartialWithId<Device, "id">;
|
|
99
95
|
*
|
|
100
96
|
* // Valid: Only 'id' is required, others are optional.
|
|
101
97
|
* const update: DeviceUpdate = { id: "123" };
|
|
@@ -104,7 +100,7 @@ export type Nullable<T> = T | null;
|
|
|
104
100
|
* const another: DeviceUpdate = { id: "456", name: "SomeDevice" };
|
|
105
101
|
*
|
|
106
102
|
* // Error: 'id' is missing.
|
|
107
|
-
* const
|
|
103
|
+
* const invalid: DeviceUpdate = { name: "SomeOtherDevice" }; // TypeScript error
|
|
108
104
|
* ```
|
|
109
105
|
*
|
|
110
106
|
* @category Utilities
|
|
@@ -213,7 +209,8 @@ export declare function retry(operation: () => Promise<boolean>, retryInterval:
|
|
|
213
209
|
* @param promise - The promise you want to run.
|
|
214
210
|
* @param timeout - The amount of time, in milliseconds, to wait for the promise to resolve.
|
|
215
211
|
*
|
|
216
|
-
* @returns Returns the result of resolving the promise it's been passed if it completes before timeout, or null if the timeout expires.
|
|
212
|
+
* @returns Returns the result of resolving the promise it's been passed if it completes before timeout, or null if the timeout expires. The internal timer is cleaned
|
|
213
|
+
* up regardless of which outcome wins the race. Note that the underlying promise is not cancelled...it continues to run, but its result is ignored.
|
|
217
214
|
*
|
|
218
215
|
* @example
|
|
219
216
|
* ```ts
|
|
@@ -246,23 +243,25 @@ export declare function runWithTimeout<T>(promise: Promise<T>, timeout: number):
|
|
|
246
243
|
*
|
|
247
244
|
* @category Utilities
|
|
248
245
|
*/
|
|
249
|
-
export declare function sleep(sleepTimer: number): Promise<
|
|
246
|
+
export declare function sleep(sleepTimer: number): Promise<void>;
|
|
250
247
|
/**
|
|
251
|
-
*
|
|
248
|
+
* Start case a string, capitalizing the first letter of each word unconditionally.
|
|
252
249
|
*
|
|
253
|
-
* @param input - The string to
|
|
250
|
+
* @param input - The string to start case.
|
|
254
251
|
*
|
|
255
|
-
* @returns Returns the
|
|
252
|
+
* @returns Returns the start cased string.
|
|
256
253
|
*
|
|
257
254
|
* @example
|
|
255
|
+
*
|
|
258
256
|
* ```ts
|
|
259
|
-
*
|
|
257
|
+
* toStartCase("this is a test");
|
|
260
258
|
* ```
|
|
261
259
|
*
|
|
262
|
-
* Returns: `This Is A Test
|
|
260
|
+
* Returns: `This Is A Test`.
|
|
261
|
+
*
|
|
263
262
|
* @category Utilities
|
|
264
263
|
*/
|
|
265
|
-
export declare function
|
|
264
|
+
export declare function toStartCase(input: string): string;
|
|
266
265
|
/**
|
|
267
266
|
* Sanitize an accessory name according to HomeKit naming conventions.
|
|
268
267
|
*
|
|
@@ -280,7 +279,7 @@ export declare function toCamelCase(input: string): string;
|
|
|
280
279
|
* @example
|
|
281
280
|
* ```ts
|
|
282
281
|
* sanitizeName("Test|Switch")
|
|
283
|
-
* ```
|
|
282
|
+
* ```
|
|
284
283
|
*
|
|
285
284
|
* Returns: `Test Switch`, replacing the pipe (an invalid character in HomeKit's naming ruleset) with a space.
|
|
286
285
|
*
|
|
@@ -305,7 +304,7 @@ export declare function sanitizeName(name: string): string;
|
|
|
305
304
|
* @example
|
|
306
305
|
* ```ts
|
|
307
306
|
* validateName("Test|Switch")
|
|
308
|
-
* ```
|
|
307
|
+
* ```
|
|
309
308
|
*
|
|
310
309
|
* Returns: `false`.
|
|
311
310
|
*
|
package/dist/util.js
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
*
|
|
3
3
|
* util.ts: Useful utility functions when writing TypeScript.
|
|
4
4
|
*/
|
|
5
|
+
/**
|
|
6
|
+
* TypeScript Utilities.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
// Validates a name against HomeKit's naming conventions. Compiled once at module scope since this sits on the fast path of sanitizeName().
|
|
11
|
+
const VALID_HOMEKIT_NAME = /^(?!.*\p{Extended_Pictographic})(?!.* {2})(?=^[\p{L}\p{N}].*[\p{L}\p{N}.]$)[\p{L}\p{N}\-"'.,#& ]+$/u;
|
|
5
12
|
/**
|
|
6
13
|
* A utility method that formats a bitrate value into a human-readable form as kbps or Mbps.
|
|
7
14
|
*
|
|
@@ -61,17 +68,24 @@ export function formatBps(value) {
|
|
|
61
68
|
* @category Utilities
|
|
62
69
|
*/
|
|
63
70
|
export async function retry(operation, retryInterval, totalRetries) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
let remainingRetries = totalRetries;
|
|
72
|
+
for (;;) {
|
|
73
|
+
// If we've exhausted our retries, we're done.
|
|
74
|
+
if ((remainingRetries !== undefined) && (remainingRetries <= 0)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
// Try the operation that was requested.
|
|
78
|
+
// eslint-disable-next-line no-await-in-loop
|
|
79
|
+
if (await operation()) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
// If the operation wasn't successful, sleep for the requested interval and try again.
|
|
83
|
+
if (remainingRetries !== undefined) {
|
|
84
|
+
remainingRetries--;
|
|
85
|
+
}
|
|
86
|
+
// eslint-disable-next-line no-await-in-loop
|
|
70
87
|
await sleep(retryInterval);
|
|
71
|
-
return retry(operation, retryInterval, (totalRetries === undefined) ? undefined : --totalRetries);
|
|
72
88
|
}
|
|
73
|
-
// We were successful - we're done.
|
|
74
|
-
return true;
|
|
75
89
|
}
|
|
76
90
|
/**
|
|
77
91
|
* Run a promise with a guaranteed timeout to complete.
|
|
@@ -80,7 +94,8 @@ export async function retry(operation, retryInterval, totalRetries) {
|
|
|
80
94
|
* @param promise - The promise you want to run.
|
|
81
95
|
* @param timeout - The amount of time, in milliseconds, to wait for the promise to resolve.
|
|
82
96
|
*
|
|
83
|
-
* @returns Returns the result of resolving the promise it's been passed if it completes before timeout, or null if the timeout expires.
|
|
97
|
+
* @returns Returns the result of resolving the promise it's been passed if it completes before timeout, or null if the timeout expires. The internal timer is cleaned
|
|
98
|
+
* up regardless of which outcome wins the race. Note that the underlying promise is not cancelled...it continues to run, but its result is ignored.
|
|
84
99
|
*
|
|
85
100
|
* @example
|
|
86
101
|
* ```ts
|
|
@@ -97,8 +112,9 @@ export async function retry(operation, retryInterval, totalRetries) {
|
|
|
97
112
|
* @category Utilities
|
|
98
113
|
*/
|
|
99
114
|
export async function runWithTimeout(promise, timeout) {
|
|
100
|
-
|
|
101
|
-
|
|
115
|
+
let timer;
|
|
116
|
+
const timeoutPromise = new Promise((resolve) => { timer = setTimeout(() => { resolve(null); }, timeout); });
|
|
117
|
+
return Promise.race([promise, timeoutPromise]).finally(() => { clearTimeout(timer); });
|
|
102
118
|
}
|
|
103
119
|
/**
|
|
104
120
|
* Emulate a sleep function.
|
|
@@ -120,21 +136,23 @@ export async function sleep(sleepTimer) {
|
|
|
120
136
|
return new Promise(resolve => setTimeout(resolve, sleepTimer));
|
|
121
137
|
}
|
|
122
138
|
/**
|
|
123
|
-
*
|
|
139
|
+
* Start case a string, capitalizing the first letter of each word unconditionally.
|
|
124
140
|
*
|
|
125
|
-
* @param input - The string to
|
|
141
|
+
* @param input - The string to start case.
|
|
126
142
|
*
|
|
127
|
-
* @returns Returns the
|
|
143
|
+
* @returns Returns the start cased string.
|
|
128
144
|
*
|
|
129
145
|
* @example
|
|
146
|
+
*
|
|
130
147
|
* ```ts
|
|
131
|
-
*
|
|
148
|
+
* toStartCase("this is a test");
|
|
132
149
|
* ```
|
|
133
150
|
*
|
|
134
|
-
* Returns: `This Is A Test
|
|
151
|
+
* Returns: `This Is A Test`.
|
|
152
|
+
*
|
|
135
153
|
* @category Utilities
|
|
136
154
|
*/
|
|
137
|
-
export function
|
|
155
|
+
export function toStartCase(input) {
|
|
138
156
|
return input.replace(/(^\w|\s+\w)/g, match => match.toUpperCase());
|
|
139
157
|
}
|
|
140
158
|
/**
|
|
@@ -154,13 +172,17 @@ export function toCamelCase(input) {
|
|
|
154
172
|
* @example
|
|
155
173
|
* ```ts
|
|
156
174
|
* sanitizeName("Test|Switch")
|
|
157
|
-
* ```
|
|
175
|
+
* ```
|
|
158
176
|
*
|
|
159
177
|
* Returns: `Test Switch`, replacing the pipe (an invalid character in HomeKit's naming ruleset) with a space.
|
|
160
178
|
*
|
|
161
179
|
* @category Utilities
|
|
162
180
|
*/
|
|
163
181
|
export function sanitizeName(name) {
|
|
182
|
+
// Fast path: if the name already conforms to HomeKit's naming rules, skip the replacement chain entirely.
|
|
183
|
+
if (validateName(name)) {
|
|
184
|
+
return name;
|
|
185
|
+
}
|
|
164
186
|
// Here are the steps we're taking to sanitize names for HomeKit:
|
|
165
187
|
//
|
|
166
188
|
// - Replace any disallowed char (including emojis) with a space.
|
|
@@ -190,13 +212,13 @@ export function sanitizeName(name) {
|
|
|
190
212
|
* @example
|
|
191
213
|
* ```ts
|
|
192
214
|
* validateName("Test|Switch")
|
|
193
|
-
* ```
|
|
215
|
+
* ```
|
|
194
216
|
*
|
|
195
217
|
* Returns: `false`.
|
|
196
218
|
*
|
|
197
219
|
* @category Utilities
|
|
198
220
|
*/
|
|
199
221
|
export function validateName(name) {
|
|
200
|
-
return
|
|
222
|
+
return VALID_HOMEKIT_NAME.test(name);
|
|
201
223
|
}
|
|
202
224
|
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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"}
|