@keeex/utils 7.2.0 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { Logger } from "@keeex/log";
17
+ import * as types from "./types.js";
18
+ /**
19
+ * Base implementation for all triggers.
20
+ */
21
+ export declare abstract class BaseTrigger implements types.Trigger {
22
+ #private;
23
+ constructor(handler: types.TriggerHandler, delayMs: number, logger?: Logger | string);
24
+ protected get state(): types.TriggerState;
25
+ protected set state(value: types.TriggerState);
26
+ protected get logger(): Logger;
27
+ abstract trigger(): void;
28
+ cancel: () => void;
29
+ stop: () => void;
30
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { createLogger } from "@keeex/log";
17
+ import { dropPromise } from "../../promise.js";
18
+ import * as types from "./types.js";
19
+ /**
20
+ * Base implementation for all triggers.
21
+ */
22
+ export class BaseTrigger {
23
+ #state = types.TriggerState.inactive;
24
+ #timeout = null;
25
+ #delay;
26
+ #handler;
27
+ #logger;
28
+ constructor(handler, delayMs, logger) {
29
+ this.#handler = handler;
30
+ this.#delay = delayMs;
31
+ if (typeof logger === "string") {
32
+ this.#logger = createLogger({ tag: logger, numbered: true });
33
+ }
34
+ else if (logger) {
35
+ this.#logger = logger;
36
+ }
37
+ else {
38
+ this.#logger = createLogger({ tag: "PromiseTrigger", numbered: true });
39
+ }
40
+ this.#logger.debug(`Initialized with timer of ${delayMs}`);
41
+ }
42
+ get state() {
43
+ return this.#state;
44
+ }
45
+ set state(value) {
46
+ const oldState = this.#state;
47
+ this.#logger.debug(`State transition: ${oldState} → ${value}`);
48
+ this.#state = value;
49
+ switch (oldState) {
50
+ case types.TriggerState.inactive:
51
+ this.#stateFromInactive(value);
52
+ break;
53
+ case types.TriggerState.pending:
54
+ this.#stateFromPending(value);
55
+ break;
56
+ case types.TriggerState.running:
57
+ this.#stateFromRunning(value);
58
+ break;
59
+ case types.TriggerState.runningRetrigger:
60
+ this.#stateFromRunningRetrigger(value);
61
+ break;
62
+ case types.TriggerState.runningStop:
63
+ this.#stateFromRunningStop(value);
64
+ break;
65
+ case types.TriggerState.stop:
66
+ this.#stateFromStop(value);
67
+ break;
68
+ }
69
+ }
70
+ get logger() {
71
+ return this.#logger;
72
+ }
73
+ cancel = () => {
74
+ this.state = types.TriggerState.inactive;
75
+ };
76
+ stop = () => {
77
+ this.state = types.TriggerState.stop;
78
+ };
79
+ #stateFromInactive = (value) => {
80
+ switch (value) {
81
+ case types.TriggerState.pending:
82
+ this.#armTimeout();
83
+ break;
84
+ case types.TriggerState.running:
85
+ case types.TriggerState.runningRetrigger:
86
+ throw new Error("Cannot transition from inactive to running");
87
+ }
88
+ };
89
+ #stateFromPending = (value) => {
90
+ switch (value) {
91
+ case types.TriggerState.inactive:
92
+ this.#disarmTimeout();
93
+ break;
94
+ case types.TriggerState.running:
95
+ this.#timeout = null;
96
+ break;
97
+ case types.TriggerState.stop:
98
+ this.#disarmTimeout();
99
+ this.#runHandler();
100
+ }
101
+ };
102
+ #stateFromRunning = (value) => {
103
+ switch (value) {
104
+ case types.TriggerState.pending:
105
+ this.state = types.TriggerState.runningRetrigger;
106
+ }
107
+ };
108
+ #stateFromRunningRetrigger = (value) => {
109
+ switch (value) {
110
+ case types.TriggerState.inactive:
111
+ this.state = types.TriggerState.pending;
112
+ break;
113
+ case types.TriggerState.pending:
114
+ this.state = types.TriggerState.runningRetrigger;
115
+ break;
116
+ case types.TriggerState.running:
117
+ throw new Error("Cannot transition from retrigger to running");
118
+ case types.TriggerState.stop:
119
+ this.#runHandler();
120
+ }
121
+ };
122
+ #stateFromRunningStop = (value) => {
123
+ switch (value) {
124
+ case types.TriggerState.inactive:
125
+ this.state = types.TriggerState.stop;
126
+ break;
127
+ case types.TriggerState.pending:
128
+ case types.TriggerState.running:
129
+ case types.TriggerState.runningRetrigger:
130
+ case types.TriggerState.runningStop:
131
+ throw new Error("Invalid state transition from runningStop");
132
+ }
133
+ };
134
+ #stateFromStop = (value) => {
135
+ switch (value) {
136
+ case types.TriggerState.inactive:
137
+ case types.TriggerState.pending:
138
+ case types.TriggerState.runningRetrigger:
139
+ this.state = types.TriggerState.stop;
140
+ break;
141
+ case types.TriggerState.running:
142
+ this.state = types.TriggerState.runningStop;
143
+ break;
144
+ }
145
+ };
146
+ #armTimeout = () => {
147
+ this.#disarmTimeout();
148
+ this.#logger.debug("Arming timeout");
149
+ this.#timeout = setTimeout(this.#runHandler, this.#delay);
150
+ };
151
+ #disarmTimeout = () => {
152
+ if (this.#timeout === null)
153
+ return;
154
+ this.#logger.debug("Disarming previous timeout");
155
+ clearTimeout(this.#timeout);
156
+ this.#timeout = null;
157
+ };
158
+ #runHandler = () => {
159
+ this.#logger.debug("Running handler");
160
+ this.state = types.TriggerState.running;
161
+ dropPromise(this.#runHandlerPromise, this.#logger);
162
+ };
163
+ #runHandlerPromise = async () => {
164
+ try {
165
+ await this.#handler();
166
+ }
167
+ finally {
168
+ this.state = types.TriggerState.inactive;
169
+ }
170
+ };
171
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { Logger } from "@keeex/log";
17
+ import { BaseTrigger } from "./base.js";
18
+ import * as types from "./types.js";
19
+ /**
20
+ * Debouncing trigger.
21
+ *
22
+ * Each time the trigger is triggered, the timer rearms itself, delaying the callback ever more.
23
+ * Once there is no trigger for the duration of the trigger, the callback is finally called.
24
+ *
25
+ * Useful for debouncing.
26
+ */
27
+ export declare class DebounceTrigger extends BaseTrigger {
28
+ constructor(handler: types.TriggerHandler, delayMs: number, logger?: Logger | string);
29
+ trigger: () => void;
30
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { BaseTrigger } from "./base.js";
17
+ import * as types from "./types.js";
18
+ /**
19
+ * Debouncing trigger.
20
+ *
21
+ * Each time the trigger is triggered, the timer rearms itself, delaying the callback ever more.
22
+ * Once there is no trigger for the duration of the trigger, the callback is finally called.
23
+ *
24
+ * Useful for debouncing.
25
+ */
26
+ export class DebounceTrigger extends BaseTrigger {
27
+ constructor(handler, delayMs, logger = "DebounceTrigger") {
28
+ super(handler, delayMs, logger);
29
+ }
30
+ trigger = () => {
31
+ if (this.state === types.TriggerState.pending) {
32
+ this.state = types.TriggerState.inactive;
33
+ }
34
+ this.state = types.TriggerState.pending;
35
+ };
36
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { Logger } from "@keeex/log";
17
+ import { BaseTrigger } from "./base.js";
18
+ import * as types from "./types.js";
19
+ /**
20
+ * Trigger that *will* fire its event after the time is elapsed, no matter how many times it is
21
+ * triggered afterward.
22
+ *
23
+ * After a call to `trigger()`, the timer is started.
24
+ * When it runs out, the even function is called.
25
+ * Multiple calls to `trigger()` will not delay the call, and they will not cause additional calls
26
+ * to occur.
27
+ * Calling `trigger()` while the callback is running will reschedule a call *after* the callback
28
+ * completes.
29
+ */
30
+ export declare class GrenadeTrigger extends BaseTrigger {
31
+ constructor(handler: types.TriggerHandler, delayMs: number, logger?: Logger | string);
32
+ trigger: () => void;
33
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { BaseTrigger } from "./base.js";
17
+ import * as types from "./types.js";
18
+ /**
19
+ * Trigger that *will* fire its event after the time is elapsed, no matter how many times it is
20
+ * triggered afterward.
21
+ *
22
+ * After a call to `trigger()`, the timer is started.
23
+ * When it runs out, the even function is called.
24
+ * Multiple calls to `trigger()` will not delay the call, and they will not cause additional calls
25
+ * to occur.
26
+ * Calling `trigger()` while the callback is running will reschedule a call *after* the callback
27
+ * completes.
28
+ */
29
+ export class GrenadeTrigger extends BaseTrigger {
30
+ constructor(handler, delayMs, logger = "GrenadeTrigger") {
31
+ super(handler, delayMs, logger);
32
+ }
33
+ trigger = () => {
34
+ this.logger.debug("Triggered");
35
+ this.state = types.TriggerState.pending;
36
+ };
37
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { Awaitable } from "../../types/types.js";
17
+ export type TriggerHandler = () => Awaitable<void>;
18
+ export declare enum TriggerState {
19
+ /** Trigger was not triggered */
20
+ inactive = "inactive",
21
+ /** Trigger was triggered, fuse timer is counting down */
22
+ pending = "pending",
23
+ /** Trigger was triggered and timer expired, the handler is running */
24
+ running = "running",
25
+ /** Handler is running, and trigger was called again */
26
+ runningRetrigger = "runningTrigger",
27
+ /** Last run after stop */
28
+ runningStop = "runningStop",
29
+ /** Handler is killed, will never run except for currently triggered call */
30
+ stop = "stop"
31
+ }
32
+ /** Generic interface for delayed triggers */
33
+ export interface Trigger {
34
+ /** Call when the trigger should start (or restart, depending on the implementation) */
35
+ trigger: () => void;
36
+ /** Call when the trigger should be canceled */
37
+ cancel: () => void;
38
+ /**
39
+ * Call when the trigger should stop (it can't be triggered again).
40
+ * If there is an execution pending, it is run immediately.
41
+ */
42
+ stop: () => void;
43
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ export var TriggerState;
17
+ (function (TriggerState) {
18
+ /** Trigger was not triggered */
19
+ TriggerState["inactive"] = "inactive";
20
+ /** Trigger was triggered, fuse timer is counting down */
21
+ TriggerState["pending"] = "pending";
22
+ /** Trigger was triggered and timer expired, the handler is running */
23
+ TriggerState["running"] = "running";
24
+ /** Handler is running, and trigger was called again */
25
+ TriggerState["runningRetrigger"] = "runningTrigger";
26
+ /** Last run after stop */
27
+ TriggerState["runningStop"] = "runningStop";
28
+ /** Handler is killed, will never run except for currently triggered call */
29
+ TriggerState["stop"] = "stop";
30
+ })(TriggerState || (TriggerState = {}));
package/lib/promise.d.ts CHANGED
@@ -36,10 +36,29 @@ export type PromiseFunc<T> = () => Promise<T | void>;
36
36
  */
37
37
  export declare const firstTruthy: <T>(promiseFuncs: Array<PromiseFunc<T>>) => Promise<T>;
38
38
  type AwaitableFn<T> = () => Awaitable<T>;
39
+ type ErrorHandler = boolean | ((e: Error) => void) | {
40
+ error: (e: unknown) => void;
41
+ };
39
42
  /**
40
43
  * Drop a promise and silence any exception.
41
44
  *
42
45
  * This is to be used *only* on dropped promises to explicitly silence any warning.
46
+ *
47
+ * @param errorHandler - How to handle rejection
48
+ * If not provided or true, an error message is printed on the error output.
49
+ * If false, the error is silently ignored.
50
+ * If a function is provided, it is called with an Error object.
51
+ * If an object with an `error()` function is provided, this error function is called.
52
+ */
53
+ export declare const dropPromise: <T>(promise: Awaitable<T> | AwaitableFn<T>, errorHandler?: ErrorHandler) => void;
54
+ /**
55
+ * Wait for a promise to complete, but ignore rejection.
56
+
57
+ * @param errorHandler - How to handle rejection
58
+ * If not provided or false, the error is silently ignored.
59
+ * If true, an error message is printed on the error output.
60
+ * If a function is provided, it is called with an Error object.
61
+ * If an object with an `error()` function is provided, this error function is called.
43
62
  */
44
- export declare const dropPromise: <T>(promise: Awaitable<T> | AwaitableFn<T>) => void;
63
+ export declare const ignorePromise: <T>(promise: Awaitable<T> | AwaitableFn<T>, errorHandler?: ErrorHandler) => Promise<void>;
45
64
  export {};
package/lib/promise.js CHANGED
@@ -14,6 +14,8 @@
14
14
  *
15
15
  */
16
16
  /* eslint-env node */
17
+ import * as log from "@keeex/log";
18
+ import { asError } from "./error.js";
17
19
  import { timeConvert } from "./units.js";
18
20
  /**
19
21
  * Create a promise that resolves after a given delay.
@@ -67,18 +69,59 @@ export const firstTruthy = async (promiseFuncs) => {
67
69
  }
68
70
  throw new Error("No result");
69
71
  };
72
+ const logger = log.createLogger("dropPromise");
73
+ const handleError = (handler, outputDefault, error) => {
74
+ if (handler === undefined) {
75
+ if (outputDefault)
76
+ logger.error("Dropped promise rejected:", error);
77
+ }
78
+ else if (handler === true) {
79
+ logger.error("Dropped promise rejected:", error);
80
+ }
81
+ else if (typeof handler === "function") {
82
+ handler(asError(error));
83
+ }
84
+ else if (handler !== false) {
85
+ handler.error(error);
86
+ }
87
+ };
70
88
  /**
71
89
  * Drop a promise and silence any exception.
72
90
  *
73
91
  * This is to be used *only* on dropped promises to explicitly silence any warning.
92
+ *
93
+ * @param errorHandler - How to handle rejection
94
+ * If not provided or true, an error message is printed on the error output.
95
+ * If false, the error is silently ignored.
96
+ * If a function is provided, it is called with an Error object.
97
+ * If an object with an `error()` function is provided, this error function is called.
74
98
  */
75
- export const dropPromise = (promise) => {
99
+ export const dropPromise = (promise, errorHandler) => {
76
100
  if (typeof promise === "function") {
77
101
  dropPromise(promise());
78
102
  return;
79
103
  }
80
104
  if (promise instanceof Promise) {
81
- // eslint-disable-next-line @typescript-eslint/no-empty-function, promise/prefer-await-to-then
82
- promise.catch(() => { });
105
+ // eslint-disable-next-line promise/prefer-await-to-then
106
+ promise.catch((e) => handleError(errorHandler, true, e));
107
+ }
108
+ };
109
+ /**
110
+ * Wait for a promise to complete, but ignore rejection.
111
+
112
+ * @param errorHandler - How to handle rejection
113
+ * If not provided or false, the error is silently ignored.
114
+ * If true, an error message is printed on the error output.
115
+ * If a function is provided, it is called with an Error object.
116
+ * If an object with an `error()` function is provided, this error function is called.
117
+ */
118
+ export const ignorePromise = async (promise, errorHandler) => {
119
+ try {
120
+ if (typeof promise === "function")
121
+ return await ignorePromise(promise());
122
+ await promise;
123
+ }
124
+ catch (e) {
125
+ handleError(errorHandler, false, e);
83
126
  }
84
127
  };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { Logger } from "@keeex/log";
17
+ import { Trigger, TriggerHandler } from "./bits/triggers/types.js";
18
+ /** Return a simple trigger that is run after the initial delay is passed after a trigger call */
19
+ export declare const simpleTrigger: (handler: TriggerHandler, delayMs: number, logger?: Logger) => Trigger;
20
+ /** Return a debouncing trigger that will rearm as long as it is triggered during the grace delay */
21
+ export declare const debouncingTrigger: (handler: TriggerHandler, delayMs: number, logger?: Logger) => Trigger;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { DebounceTrigger } from "./bits/triggers/debounce.js";
17
+ import { GrenadeTrigger } from "./bits/triggers/grenade.js";
18
+ /** Return a simple trigger that is run after the initial delay is passed after a trigger call */
19
+ export const simpleTrigger = (handler, delayMs, logger) => new GrenadeTrigger(handler, delayMs, logger);
20
+ /** Return a debouncing trigger that will rearm as long as it is triggered during the grace delay */
21
+ export const debouncingTrigger = (handler, delayMs, logger) => new DebounceTrigger(handler, delayMs, logger);
package/lib/units.js CHANGED
@@ -92,8 +92,8 @@ const convertUnit = (unitDescription, fromUnit, toUnit) => {
92
92
  }
93
93
  return { from, to };
94
94
  };
95
- const regexBefore = /^(?<unit>[^\d]+)\d+$/u;
96
- const regexAfter = /^\d+(?<unit>[^\d]+)$/u;
95
+ const regexBefore = /^(?<unit>[^\d,.]+)(?:(?:\d+)|(?:\d*[.,]\d+)|(?:\d+[.,]\d*))$/u;
96
+ const regexAfter = /^(?:(?:\d+)|(?:\d*[.,]\d+)|(?:\d+[.,]\d*))(?<unit>[^\d,.]+)$/u;
97
97
  /** Get the unit text from an input value */
98
98
  const getUnitFromValue = (value, unitDesc) => {
99
99
  if (typeof value !== "string")
@@ -105,12 +105,13 @@ const getUnitFromValue = (value, unitDesc) => {
105
105
  const unit = res.groups.unit;
106
106
  return unit.trim();
107
107
  };
108
- const regexValue = /^(?:[^\d]*)(?<value>\d+)(?:[^\d]*)$/u;
108
+ const regexFloatValue = /^(?:[^\d,.]*)(?<value>(?:\d+)|(?:\d*[.,]\d+)|(?:\d+[.,]\d*))(?:[^\d,.]*)$/u;
109
+ const regexIntOnlyValue = /^(?:[^\d,.]*)(?<value>\d+)(?:[^\d,.]*)$/u;
109
110
  /** Get the numeric value, stripping units */
110
111
  const getNumberValue = (value) => {
111
112
  if (typeof value === "number")
112
113
  return value;
113
- const res = regexValue.exec(value);
114
+ const res = regexFloatValue.exec(value);
114
115
  if (!res?.groups)
115
116
  throw new Error(`Missing value (${value})`);
116
117
  return parseFloat(res.groups.value);
@@ -121,7 +122,7 @@ const getBigintValue = (value) => {
121
122
  return value;
122
123
  if (typeof value === "number")
123
124
  return BigInt(value);
124
- const res = regexValue.exec(value);
125
+ const res = regexIntOnlyValue.exec(value);
125
126
  if (!res?.groups)
126
127
  throw new Error(`Missing value (${value})`);
127
128
  return BigInt(res.groups.value);
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@keeex/utils","version":"7.2.0","description":"Various utility functions for pure JavaScript","scripts":{},"author":"KeeeX SAS","contributors":[{"name":"Gabriel Paul \"Cley Faye\" Risterucci","email":"gabriel@keeex.net"}],"homepage":"https://keeex.me/oss","keywords":["utility"],"type":"module","license":"MIT","dependencies":{"@keeex/bubble_babble":"^3.0.1","@keeex/log":"^1.7.1","base64-arraybuffer":"^1.0.2","cron-parser":"^5.2.0","ms":"^2.1.3","text-encoding-shim":"^1.0.5"},"exports":{"./units.js":{"node":"./lib/units.js","browser":"./web/units.js","default":"./lib/units.js"},"./uint8array.js":{"node":"./lib/uint8array.js","browser":"./web/uint8array.js","default":"./lib/uint8array.js"},"./string.js":{"node":"./lib/string.js","browser":"./web/string.js","default":"./lib/string.js"},"./starttime.js":{"node":"./lib/starttime.js","browser":"./web/starttime.js","default":"./lib/starttime.js"},"./promise.js":{"node":"./lib/promise.js","browser":"./web/promise.js","default":"./lib/promise.js"},"./path.js":{"node":"./lib/path.js","browser":"./web/path.js","default":"./lib/path.js"},"./number.js":{"node":"./lib/number.js","browser":"./web/number.js","default":"./lib/number.js"},"./linebuffer.js":{"node":"./lib/linebuffer.js","browser":"./web/linebuffer.js","default":"./lib/linebuffer.js"},"./json.js":{"node":"./lib/json.js","browser":"./web/json.js","default":"./lib/json.js"},"./idx.js":{"node":"./lib/idx.js","browser":"./web/idx.js","default":"./lib/idx.js"},"./hex.js":{"node":"./lib/hex.js","browser":"./web/hex.js","default":"./lib/hex.js"},"./global.js":{"node":"./lib/global.js","browser":"./web/global.js","default":"./lib/global.js"},"./error.js":{"node":"./lib/error.js","browser":"./web/error.js","default":"./lib/error.js"},"./dict.js":{"node":"./lib/dict.js","browser":"./web/dict.js","default":"./lib/dict.js"},"./cron.js":{"node":"./lib/cron.js","browser":"./web/cron.js","default":"./lib/cron.js"},"./consts.js":{"node":"./lib/consts.js","browser":"./web/consts.js","default":"./lib/consts.js"},"./bytebuffer.js":{"node":"./lib/bytebuffer.js","browser":"./web/bytebuffer.js","default":"./lib/bytebuffer.js"},"./benchmark.js":{"node":"./lib/benchmark.js","browser":"./web/benchmark.js","default":"./lib/benchmark.js"},"./base64.js":{"node":"./lib/base64.js","browser":"./web/base64.js","default":"./lib/base64.js"},"./base58.js":{"node":"./lib/base58.js","browser":"./web/base58.js","default":"./lib/base58.js"},"./arraybuffer.js":{"node":"./lib/arraybuffer.js","browser":"./web/arraybuffer.js","default":"./lib/arraybuffer.js"},"./array.js":{"node":"./lib/array.js","browser":"./web/array.js","default":"./lib/array.js"},"./types/utils.js":{"node":"./lib/types/utils.js","browser":"./web/types/utils.js","default":"./lib/types/utils.js"},"./types/types.js":{"node":"./lib/types/types.js","browser":"./web/types/types.js","default":"./lib/types/types.js"},"./types/record.js":{"node":"./lib/types/record.js","browser":"./web/types/record.js","default":"./lib/types/record.js"},"./types/primitive.js":{"node":"./lib/types/primitive.js","browser":"./web/types/primitive.js","default":"./lib/types/primitive.js"},"./types/predicateerror.js":{"node":"./lib/types/predicateerror.js","browser":"./web/types/predicateerror.js","default":"./lib/types/predicateerror.js"},"./types/enum.js":{"node":"./lib/types/enum.js","browser":"./web/types/enum.js","default":"./lib/types/enum.js"},"./types/array.js":{"node":"./lib/types/array.js","browser":"./web/types/array.js","default":"./lib/types/array.js"},"./marshalling/unmarshaller.js":{"node":"./lib/marshalling/unmarshaller.js","browser":"./web/marshalling/unmarshaller.js","default":"./lib/marshalling/unmarshaller.js"},"./marshalling/marshaller.js":{"node":"./lib/marshalling/marshaller.js","browser":"./web/marshalling/marshaller.js","default":"./lib/marshalling/marshaller.js"},"./async/timecache.js":{"node":"./lib/async/timecache.js","browser":"./web/async/timecache.js","default":"./lib/async/timecache.js"},"./async/queues.js":{"node":"./lib/async/queues.js","browser":"./web/async/queues.js","default":"./lib/async/queues.js"},"./async/keycache.js":{"node":"./lib/async/keycache.js","browser":"./web/async/keycache.js","default":"./lib/async/keycache.js"},"./async/deferredpromise.js":{"node":"./lib/async/deferredpromise.js","browser":"./web/async/deferredpromise.js","default":"./lib/async/deferredpromise.js"},"./async/asynctrigger.js":{"node":"./lib/async/asynctrigger.js","browser":"./web/async/asynctrigger.js","default":"./lib/async/asynctrigger.js"}},"files":["/lib","/web"]}
1
+ {"name":"@keeex/utils","version":"7.3.0","description":"Various utility functions for pure JavaScript","scripts":{},"author":"KeeeX SAS","contributors":[{"name":"Gabriel Paul \"Cley Faye\" Risterucci","email":"gabriel@keeex.net"}],"homepage":"https://keeex.me/oss","keywords":["utility"],"type":"module","license":"MIT","dependencies":{"@keeex/bubble_babble":"^3.0.1","@keeex/log":"^1.7.1","base64-arraybuffer":"^1.0.2","cron-parser":"^5.3.0","ms":"^2.1.3","text-encoding-shim":"^1.0.5"},"exports":{"./array.js":{"node":"./lib/array.js","browser":"./web/array.js","react-native":"./web/array.js","default":"./lib/array.js"},"./arraybuffer.js":{"node":"./lib/arraybuffer.js","browser":"./web/arraybuffer.js","react-native":"./web/arraybuffer.js","default":"./lib/arraybuffer.js"},"./async/asynctrigger.js":{"node":"./lib/async/asynctrigger.js","browser":"./web/async/asynctrigger.js","react-native":"./web/async/asynctrigger.js","default":"./lib/async/asynctrigger.js"},"./async/deferredpromise.js":{"node":"./lib/async/deferredpromise.js","browser":"./web/async/deferredpromise.js","react-native":"./web/async/deferredpromise.js","default":"./lib/async/deferredpromise.js"},"./async/keycache.js":{"node":"./lib/async/keycache.js","browser":"./web/async/keycache.js","react-native":"./web/async/keycache.js","default":"./lib/async/keycache.js"},"./async/queues.js":{"node":"./lib/async/queues.js","browser":"./web/async/queues.js","react-native":"./web/async/queues.js","default":"./lib/async/queues.js"},"./async/timecache.js":{"node":"./lib/async/timecache.js","browser":"./web/async/timecache.js","react-native":"./web/async/timecache.js","default":"./lib/async/timecache.js"},"./base58.js":{"node":"./lib/base58.js","browser":"./web/base58.js","react-native":"./web/base58.js","default":"./lib/base58.js"},"./base64.js":{"node":"./lib/base64.js","browser":"./web/base64.js","react-native":"./web/base64.js","default":"./lib/base64.js"},"./benchmark.js":{"node":"./lib/benchmark.js","browser":"./web/benchmark.js","react-native":"./web/benchmark.js","default":"./lib/benchmark.js"},"./bytebuffer.js":{"node":"./lib/bytebuffer.js","browser":"./web/bytebuffer.js","react-native":"./web/bytebuffer.js","default":"./lib/bytebuffer.js"},"./consts.js":{"node":"./lib/consts.js","browser":"./web/consts.js","react-native":"./web/consts.js","default":"./lib/consts.js"},"./cron.js":{"node":"./lib/cron.js","browser":"./web/cron.js","react-native":"./web/cron.js","default":"./lib/cron.js"},"./dict.js":{"node":"./lib/dict.js","browser":"./web/dict.js","react-native":"./web/dict.js","default":"./lib/dict.js"},"./error.js":{"node":"./lib/error.js","browser":"./web/error.js","react-native":"./web/error.js","default":"./lib/error.js"},"./global.js":{"node":"./lib/global.js","browser":"./web/global.js","react-native":"./web/global.js","default":"./lib/global.js"},"./hex.js":{"node":"./lib/hex.js","browser":"./web/hex.js","react-native":"./web/hex.js","default":"./lib/hex.js"},"./idx.js":{"node":"./lib/idx.js","browser":"./web/idx.js","react-native":"./web/idx.js","default":"./lib/idx.js"},"./json.js":{"node":"./lib/json.js","browser":"./web/json.js","react-native":"./web/json.js","default":"./lib/json.js"},"./linebuffer.js":{"node":"./lib/linebuffer.js","browser":"./web/linebuffer.js","react-native":"./web/linebuffer.js","default":"./lib/linebuffer.js"},"./marshalling/marshaller.js":{"node":"./lib/marshalling/marshaller.js","browser":"./web/marshalling/marshaller.js","react-native":"./web/marshalling/marshaller.js","default":"./lib/marshalling/marshaller.js"},"./marshalling/unmarshaller.js":{"node":"./lib/marshalling/unmarshaller.js","browser":"./web/marshalling/unmarshaller.js","react-native":"./web/marshalling/unmarshaller.js","default":"./lib/marshalling/unmarshaller.js"},"./number.js":{"node":"./lib/number.js","browser":"./web/number.js","react-native":"./web/number.js","default":"./lib/number.js"},"./path.js":{"node":"./lib/path.js","browser":"./web/path.js","react-native":"./web/path.js","default":"./lib/path.js"},"./promise.js":{"node":"./lib/promise.js","browser":"./web/promise.js","react-native":"./web/promise.js","default":"./lib/promise.js"},"./starttime.js":{"node":"./lib/starttime.js","browser":"./web/starttime.js","react-native":"./web/starttime.js","default":"./lib/starttime.js"},"./string.js":{"node":"./lib/string.js","browser":"./web/string.js","react-native":"./web/string.js","default":"./lib/string.js"},"./triggers.js":{"node":"./lib/triggers.js","browser":"./web/triggers.js","react-native":"./web/triggers.js","default":"./lib/triggers.js"},"./types/array.js":{"node":"./lib/types/array.js","browser":"./web/types/array.js","react-native":"./web/types/array.js","default":"./lib/types/array.js"},"./types/enum.js":{"node":"./lib/types/enum.js","browser":"./web/types/enum.js","react-native":"./web/types/enum.js","default":"./lib/types/enum.js"},"./types/predicateerror.js":{"node":"./lib/types/predicateerror.js","browser":"./web/types/predicateerror.js","react-native":"./web/types/predicateerror.js","default":"./lib/types/predicateerror.js"},"./types/primitive.js":{"node":"./lib/types/primitive.js","browser":"./web/types/primitive.js","react-native":"./web/types/primitive.js","default":"./lib/types/primitive.js"},"./types/record.js":{"node":"./lib/types/record.js","browser":"./web/types/record.js","react-native":"./web/types/record.js","default":"./lib/types/record.js"},"./types/types.js":{"node":"./lib/types/types.js","browser":"./web/types/types.js","react-native":"./web/types/types.js","default":"./lib/types/types.js"},"./types/utils.js":{"node":"./lib/types/utils.js","browser":"./web/types/utils.js","react-native":"./web/types/utils.js","default":"./lib/types/utils.js"},"./uint8array.js":{"node":"./lib/uint8array.js","browser":"./web/uint8array.js","react-native":"./web/uint8array.js","default":"./lib/uint8array.js"},"./units.js":{"node":"./lib/units.js","browser":"./web/units.js","react-native":"./web/units.js","default":"./lib/units.js"}},"files":["/lib","/web"]}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { Logger } from "@keeex/log";
17
+ import * as types from "./types.js";
18
+ /**
19
+ * Base implementation for all triggers.
20
+ */
21
+ export declare abstract class BaseTrigger implements types.Trigger {
22
+ #private;
23
+ constructor(handler: types.TriggerHandler, delayMs: number, logger?: Logger | string);
24
+ protected get state(): types.TriggerState;
25
+ protected set state(value: types.TriggerState);
26
+ protected get logger(): Logger;
27
+ abstract trigger(): void;
28
+ cancel: () => void;
29
+ stop: () => void;
30
+ }
@@ -0,0 +1,173 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { createLogger } from "@keeex/log";
17
+ import { dropPromise } from "../../promise.js";
18
+ import * as types from "./types.js";
19
+ /**
20
+ * Base implementation for all triggers.
21
+ */
22
+ export class BaseTrigger {
23
+ #state = types.TriggerState.inactive;
24
+ #timeout = null;
25
+ #delay;
26
+ #handler;
27
+ #logger;
28
+ constructor(handler, delayMs, logger) {
29
+ this.#handler = handler;
30
+ this.#delay = delayMs;
31
+ if (typeof logger === "string") {
32
+ this.#logger = createLogger({
33
+ tag: logger,
34
+ numbered: true
35
+ });
36
+ } else if (logger) {
37
+ this.#logger = logger;
38
+ } else {
39
+ this.#logger = createLogger({
40
+ tag: "PromiseTrigger",
41
+ numbered: true
42
+ });
43
+ }
44
+ this.#logger.debug(`Initialized with timer of ${delayMs}`);
45
+ }
46
+ get state() {
47
+ return this.#state;
48
+ }
49
+ set state(value) {
50
+ const oldState = this.#state;
51
+ this.#logger.debug(`State transition: ${oldState} → ${value}`);
52
+ this.#state = value;
53
+ switch (oldState) {
54
+ case types.TriggerState.inactive:
55
+ this.#stateFromInactive(value);
56
+ break;
57
+ case types.TriggerState.pending:
58
+ this.#stateFromPending(value);
59
+ break;
60
+ case types.TriggerState.running:
61
+ this.#stateFromRunning(value);
62
+ break;
63
+ case types.TriggerState.runningRetrigger:
64
+ this.#stateFromRunningRetrigger(value);
65
+ break;
66
+ case types.TriggerState.runningStop:
67
+ this.#stateFromRunningStop(value);
68
+ break;
69
+ case types.TriggerState.stop:
70
+ this.#stateFromStop(value);
71
+ break;
72
+ }
73
+ }
74
+ get logger() {
75
+ return this.#logger;
76
+ }
77
+ cancel = () => {
78
+ this.state = types.TriggerState.inactive;
79
+ };
80
+ stop = () => {
81
+ this.state = types.TriggerState.stop;
82
+ };
83
+ #stateFromInactive = value => {
84
+ switch (value) {
85
+ case types.TriggerState.pending:
86
+ this.#armTimeout();
87
+ break;
88
+ case types.TriggerState.running:
89
+ case types.TriggerState.runningRetrigger:
90
+ throw new Error("Cannot transition from inactive to running");
91
+ }
92
+ };
93
+ #stateFromPending = value => {
94
+ switch (value) {
95
+ case types.TriggerState.inactive:
96
+ this.#disarmTimeout();
97
+ break;
98
+ case types.TriggerState.running:
99
+ this.#timeout = null;
100
+ break;
101
+ case types.TriggerState.stop:
102
+ this.#disarmTimeout();
103
+ this.#runHandler();
104
+ }
105
+ };
106
+ #stateFromRunning = value => {
107
+ switch (value) {
108
+ case types.TriggerState.pending:
109
+ this.state = types.TriggerState.runningRetrigger;
110
+ }
111
+ };
112
+ #stateFromRunningRetrigger = value => {
113
+ switch (value) {
114
+ case types.TriggerState.inactive:
115
+ this.state = types.TriggerState.pending;
116
+ break;
117
+ case types.TriggerState.pending:
118
+ this.state = types.TriggerState.runningRetrigger;
119
+ break;
120
+ case types.TriggerState.running:
121
+ throw new Error("Cannot transition from retrigger to running");
122
+ case types.TriggerState.stop:
123
+ this.#runHandler();
124
+ }
125
+ };
126
+ #stateFromRunningStop = value => {
127
+ switch (value) {
128
+ case types.TriggerState.inactive:
129
+ this.state = types.TriggerState.stop;
130
+ break;
131
+ case types.TriggerState.pending:
132
+ case types.TriggerState.running:
133
+ case types.TriggerState.runningRetrigger:
134
+ case types.TriggerState.runningStop:
135
+ throw new Error("Invalid state transition from runningStop");
136
+ }
137
+ };
138
+ #stateFromStop = value => {
139
+ switch (value) {
140
+ case types.TriggerState.inactive:
141
+ case types.TriggerState.pending:
142
+ case types.TriggerState.runningRetrigger:
143
+ this.state = types.TriggerState.stop;
144
+ break;
145
+ case types.TriggerState.running:
146
+ this.state = types.TriggerState.runningStop;
147
+ break;
148
+ }
149
+ };
150
+ #armTimeout = () => {
151
+ this.#disarmTimeout();
152
+ this.#logger.debug("Arming timeout");
153
+ this.#timeout = setTimeout(this.#runHandler, this.#delay);
154
+ };
155
+ #disarmTimeout = () => {
156
+ if (this.#timeout === null) return;
157
+ this.#logger.debug("Disarming previous timeout");
158
+ clearTimeout(this.#timeout);
159
+ this.#timeout = null;
160
+ };
161
+ #runHandler = () => {
162
+ this.#logger.debug("Running handler");
163
+ this.state = types.TriggerState.running;
164
+ dropPromise(this.#runHandlerPromise, this.#logger);
165
+ };
166
+ #runHandlerPromise = async () => {
167
+ try {
168
+ await this.#handler();
169
+ } finally {
170
+ this.state = types.TriggerState.inactive;
171
+ }
172
+ };
173
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { Logger } from "@keeex/log";
17
+ import { BaseTrigger } from "./base.js";
18
+ import * as types from "./types.js";
19
+ /**
20
+ * Debouncing trigger.
21
+ *
22
+ * Each time the trigger is triggered, the timer rearms itself, delaying the callback ever more.
23
+ * Once there is no trigger for the duration of the trigger, the callback is finally called.
24
+ *
25
+ * Useful for debouncing.
26
+ */
27
+ export declare class DebounceTrigger extends BaseTrigger {
28
+ constructor(handler: types.TriggerHandler, delayMs: number, logger?: Logger | string);
29
+ trigger: () => void;
30
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { BaseTrigger } from "./base.js";
17
+ import * as types from "./types.js";
18
+ /**
19
+ * Debouncing trigger.
20
+ *
21
+ * Each time the trigger is triggered, the timer rearms itself, delaying the callback ever more.
22
+ * Once there is no trigger for the duration of the trigger, the callback is finally called.
23
+ *
24
+ * Useful for debouncing.
25
+ */
26
+ export class DebounceTrigger extends BaseTrigger {
27
+ constructor(handler, delayMs, logger = "DebounceTrigger") {
28
+ super(handler, delayMs, logger);
29
+ }
30
+ trigger = () => {
31
+ if (this.state === types.TriggerState.pending) {
32
+ this.state = types.TriggerState.inactive;
33
+ }
34
+ this.state = types.TriggerState.pending;
35
+ };
36
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { Logger } from "@keeex/log";
17
+ import { BaseTrigger } from "./base.js";
18
+ import * as types from "./types.js";
19
+ /**
20
+ * Trigger that *will* fire its event after the time is elapsed, no matter how many times it is
21
+ * triggered afterward.
22
+ *
23
+ * After a call to `trigger()`, the timer is started.
24
+ * When it runs out, the even function is called.
25
+ * Multiple calls to `trigger()` will not delay the call, and they will not cause additional calls
26
+ * to occur.
27
+ * Calling `trigger()` while the callback is running will reschedule a call *after* the callback
28
+ * completes.
29
+ */
30
+ export declare class GrenadeTrigger extends BaseTrigger {
31
+ constructor(handler: types.TriggerHandler, delayMs: number, logger?: Logger | string);
32
+ trigger: () => void;
33
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { BaseTrigger } from "./base.js";
17
+ import * as types from "./types.js";
18
+ /**
19
+ * Trigger that *will* fire its event after the time is elapsed, no matter how many times it is
20
+ * triggered afterward.
21
+ *
22
+ * After a call to `trigger()`, the timer is started.
23
+ * When it runs out, the even function is called.
24
+ * Multiple calls to `trigger()` will not delay the call, and they will not cause additional calls
25
+ * to occur.
26
+ * Calling `trigger()` while the callback is running will reschedule a call *after* the callback
27
+ * completes.
28
+ */
29
+ export class GrenadeTrigger extends BaseTrigger {
30
+ constructor(handler, delayMs, logger = "GrenadeTrigger") {
31
+ super(handler, delayMs, logger);
32
+ }
33
+ trigger = () => {
34
+ this.logger.debug("Triggered");
35
+ this.state = types.TriggerState.pending;
36
+ };
37
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { Awaitable } from "../../types/types.js";
17
+ export type TriggerHandler = () => Awaitable<void>;
18
+ export declare enum TriggerState {
19
+ /** Trigger was not triggered */
20
+ inactive = "inactive",
21
+ /** Trigger was triggered, fuse timer is counting down */
22
+ pending = "pending",
23
+ /** Trigger was triggered and timer expired, the handler is running */
24
+ running = "running",
25
+ /** Handler is running, and trigger was called again */
26
+ runningRetrigger = "runningTrigger",
27
+ /** Last run after stop */
28
+ runningStop = "runningStop",
29
+ /** Handler is killed, will never run except for currently triggered call */
30
+ stop = "stop"
31
+ }
32
+ /** Generic interface for delayed triggers */
33
+ export interface Trigger {
34
+ /** Call when the trigger should start (or restart, depending on the implementation) */
35
+ trigger: () => void;
36
+ /** Call when the trigger should be canceled */
37
+ cancel: () => void;
38
+ /**
39
+ * Call when the trigger should stop (it can't be triggered again).
40
+ * If there is an execution pending, it is run immediately.
41
+ */
42
+ stop: () => void;
43
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ export var TriggerState;
17
+ (function (TriggerState) {
18
+ /** Trigger was not triggered */
19
+ TriggerState["inactive"] = "inactive";
20
+ /** Trigger was triggered, fuse timer is counting down */
21
+ TriggerState["pending"] = "pending";
22
+ /** Trigger was triggered and timer expired, the handler is running */
23
+ TriggerState["running"] = "running";
24
+ /** Handler is running, and trigger was called again */
25
+ TriggerState["runningRetrigger"] = "runningTrigger";
26
+ /** Last run after stop */
27
+ TriggerState["runningStop"] = "runningStop";
28
+ /** Handler is killed, will never run except for currently triggered call */
29
+ TriggerState["stop"] = "stop";
30
+ })(TriggerState || (TriggerState = {}));
package/web/promise.d.ts CHANGED
@@ -36,10 +36,29 @@ export type PromiseFunc<T> = () => Promise<T | void>;
36
36
  */
37
37
  export declare const firstTruthy: <T>(promiseFuncs: Array<PromiseFunc<T>>) => Promise<T>;
38
38
  type AwaitableFn<T> = () => Awaitable<T>;
39
+ type ErrorHandler = boolean | ((e: Error) => void) | {
40
+ error: (e: unknown) => void;
41
+ };
39
42
  /**
40
43
  * Drop a promise and silence any exception.
41
44
  *
42
45
  * This is to be used *only* on dropped promises to explicitly silence any warning.
46
+ *
47
+ * @param errorHandler - How to handle rejection
48
+ * If not provided or true, an error message is printed on the error output.
49
+ * If false, the error is silently ignored.
50
+ * If a function is provided, it is called with an Error object.
51
+ * If an object with an `error()` function is provided, this error function is called.
52
+ */
53
+ export declare const dropPromise: <T>(promise: Awaitable<T> | AwaitableFn<T>, errorHandler?: ErrorHandler) => void;
54
+ /**
55
+ * Wait for a promise to complete, but ignore rejection.
56
+
57
+ * @param errorHandler - How to handle rejection
58
+ * If not provided or false, the error is silently ignored.
59
+ * If true, an error message is printed on the error output.
60
+ * If a function is provided, it is called with an Error object.
61
+ * If an object with an `error()` function is provided, this error function is called.
43
62
  */
44
- export declare const dropPromise: <T>(promise: Awaitable<T> | AwaitableFn<T>) => void;
63
+ export declare const ignorePromise: <T>(promise: Awaitable<T> | AwaitableFn<T>, errorHandler?: ErrorHandler) => Promise<void>;
45
64
  export {};
package/web/promise.js CHANGED
@@ -14,6 +14,8 @@
14
14
  *
15
15
  */
16
16
  /* eslint-env node */
17
+ import * as log from "@keeex/log";
18
+ import { asError } from "./error.js";
17
19
  import { timeConvert } from "./units.js";
18
20
  /**
19
21
  * Create a promise that resolves after a given delay.
@@ -63,18 +65,53 @@ export const firstTruthy = async promiseFuncs => {
63
65
  }
64
66
  throw new Error("No result");
65
67
  };
68
+ const logger = log.createLogger("dropPromise");
69
+ const handleError = (handler, outputDefault, error) => {
70
+ if (handler === undefined) {
71
+ if (outputDefault) logger.error("Dropped promise rejected:", error);
72
+ } else if (handler === true) {
73
+ logger.error("Dropped promise rejected:", error);
74
+ } else if (typeof handler === "function") {
75
+ handler(asError(error));
76
+ } else if (handler !== false) {
77
+ handler.error(error);
78
+ }
79
+ };
66
80
  /**
67
81
  * Drop a promise and silence any exception.
68
82
  *
69
83
  * This is to be used *only* on dropped promises to explicitly silence any warning.
84
+ *
85
+ * @param errorHandler - How to handle rejection
86
+ * If not provided or true, an error message is printed on the error output.
87
+ * If false, the error is silently ignored.
88
+ * If a function is provided, it is called with an Error object.
89
+ * If an object with an `error()` function is provided, this error function is called.
70
90
  */
71
- export const dropPromise = promise => {
91
+ export const dropPromise = (promise, errorHandler) => {
72
92
  if (typeof promise === "function") {
73
93
  dropPromise(promise());
74
94
  return;
75
95
  }
76
96
  if (promise instanceof Promise) {
77
- // eslint-disable-next-line @typescript-eslint/no-empty-function, promise/prefer-await-to-then
78
- promise.catch(() => {});
97
+ // eslint-disable-next-line promise/prefer-await-to-then
98
+ promise.catch(e => handleError(errorHandler, true, e));
99
+ }
100
+ };
101
+ /**
102
+ * Wait for a promise to complete, but ignore rejection.
103
+
104
+ * @param errorHandler - How to handle rejection
105
+ * If not provided or false, the error is silently ignored.
106
+ * If true, an error message is printed on the error output.
107
+ * If a function is provided, it is called with an Error object.
108
+ * If an object with an `error()` function is provided, this error function is called.
109
+ */
110
+ export const ignorePromise = async (promise, errorHandler) => {
111
+ try {
112
+ if (typeof promise === "function") return await ignorePromise(promise());
113
+ await promise;
114
+ } catch (e) {
115
+ handleError(errorHandler, false, e);
79
116
  }
80
117
  };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { Logger } from "@keeex/log";
17
+ import { Trigger, TriggerHandler } from "./bits/triggers/types.js";
18
+ /** Return a simple trigger that is run after the initial delay is passed after a trigger call */
19
+ export declare const simpleTrigger: (handler: TriggerHandler, delayMs: number, logger?: Logger) => Trigger;
20
+ /** Return a debouncing trigger that will rearm as long as it is triggered during the grace delay */
21
+ export declare const debouncingTrigger: (handler: TriggerHandler, delayMs: number, logger?: Logger) => Trigger;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @license
3
+ * @preserve
4
+ *
5
+ * MIT License
6
+ *
7
+ * Copyright (c) 2023 KeeeX SAS
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14
+ *
15
+ */
16
+ import { DebounceTrigger } from "./bits/triggers/debounce.js";
17
+ import { GrenadeTrigger } from "./bits/triggers/grenade.js";
18
+ /** Return a simple trigger that is run after the initial delay is passed after a trigger call */
19
+ export const simpleTrigger = (handler, delayMs, logger) => new GrenadeTrigger(handler, delayMs, logger);
20
+ /** Return a debouncing trigger that will rearm as long as it is triggered during the grace delay */
21
+ export const debouncingTrigger = (handler, delayMs, logger) => new DebounceTrigger(handler, delayMs, logger);
package/web/units.js CHANGED
@@ -125,8 +125,8 @@ const convertUnit = (unitDescription, fromUnit, toUnit) => {
125
125
  to
126
126
  };
127
127
  };
128
- const regexBefore = /^(?<unit>[^\d]+)\d+$/u;
129
- const regexAfter = /^\d+(?<unit>[^\d]+)$/u;
128
+ const regexBefore = /^(?<unit>[^\d,.]+)(?:(?:\d+)|(?:\d*[.,]\d+)|(?:\d+[.,]\d*))$/u;
129
+ const regexAfter = /^(?:(?:\d+)|(?:\d*[.,]\d+)|(?:\d+[.,]\d*))(?<unit>[^\d,.]+)$/u;
130
130
  /** Get the unit text from an input value */
131
131
  const getUnitFromValue = (value, unitDesc) => {
132
132
  if (typeof value !== "string") return unitDesc.basename;
@@ -136,11 +136,12 @@ const getUnitFromValue = (value, unitDesc) => {
136
136
  const unit = res.groups.unit;
137
137
  return unit.trim();
138
138
  };
139
- const regexValue = /^(?:[^\d]*)(?<value>\d+)(?:[^\d]*)$/u;
139
+ const regexFloatValue = /^(?:[^\d,.]*)(?<value>(?:\d+)|(?:\d*[.,]\d+)|(?:\d+[.,]\d*))(?:[^\d,.]*)$/u;
140
+ const regexIntOnlyValue = /^(?:[^\d,.]*)(?<value>\d+)(?:[^\d,.]*)$/u;
140
141
  /** Get the numeric value, stripping units */
141
142
  const getNumberValue = value => {
142
143
  if (typeof value === "number") return value;
143
- const res = regexValue.exec(value);
144
+ const res = regexFloatValue.exec(value);
144
145
  if (!res?.groups) throw new Error(`Missing value (${value})`);
145
146
  return parseFloat(res.groups.value);
146
147
  };
@@ -148,7 +149,7 @@ const getNumberValue = value => {
148
149
  const getBigintValue = value => {
149
150
  if (typeof value === "bigint") return value;
150
151
  if (typeof value === "number") return BigInt(value);
151
- const res = regexValue.exec(value);
152
+ const res = regexIntOnlyValue.exec(value);
152
153
  if (!res?.groups) throw new Error(`Missing value (${value})`);
153
154
  return BigInt(res.groups.value);
154
155
  };