@keeex/utils 7.1.0 → 7.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -4
- package/lib/cron/scheduledtask.d.ts +8 -53
- package/lib/cron/scheduledtask.js +56 -77
- package/lib/cron/types.d.ts +0 -1
- package/lib/cron.d.ts +3 -2
- package/lib/cron.js +3 -4
- package/lib/{cron/logger.d.ts → linebuffer.d.ts} +13 -5
- package/{web/cron/logger.d.ts → lib/linebuffer.js} +30 -5
- package/lib/promise.js +6 -2
- package/lib/units.js +6 -5
- package/package.json +1 -1
- package/web/cron/scheduledtask.d.ts +8 -53
- package/web/cron/scheduledtask.js +56 -78
- package/web/cron/types.d.ts +0 -1
- package/web/cron.d.ts +3 -2
- package/web/cron.js +3 -4
- package/{lib/cron/logger.js → web/linebuffer.d.ts} +13 -14
- package/web/{cron/logger.js → linebuffer.js} +29 -12
- package/web/promise.js +6 -2
- package/web/units.js +6 -5
package/README.md
CHANGED
|
@@ -9,13 +9,19 @@ This is a general feature list overview.
|
|
|
9
9
|
|
|
10
10
|
## Async utility
|
|
11
11
|
|
|
12
|
+
Stuff related to promises and async operations.
|
|
13
|
+
|
|
12
14
|
- `AsyncTrigger`, for easy debouncing
|
|
13
15
|
- `DeferredPromise` to manipulate the usual `resolve()` and `reject()` functions outside of a
|
|
14
16
|
promise block
|
|
15
17
|
- `KeyCache` to create a basic key-value cache with asynchronous fetch
|
|
18
|
+
- `Queues` to create parallel queues of tasks
|
|
16
19
|
- `TimeCache` to create a cache that keeps a value for a given amount of time and can automatically
|
|
17
20
|
refresh it
|
|
18
|
-
|
|
21
|
+
|
|
22
|
+
## CRON
|
|
23
|
+
|
|
24
|
+
Time-based task scheduling.
|
|
19
25
|
|
|
20
26
|
## Marshalling
|
|
21
27
|
|
|
@@ -50,6 +56,7 @@ Helper wrapping JavaScript DataView into convenient functions.
|
|
|
50
56
|
## dict
|
|
51
57
|
|
|
52
58
|
Functions to copy records with primitive types (deep copy).
|
|
59
|
+
These functions can probably be replaced with `Object.assign()` or custom code.
|
|
53
60
|
|
|
54
61
|
## error
|
|
55
62
|
|
|
@@ -60,6 +67,10 @@ Coerce anything (`unknown`) into an `Error` object for easier error handling.
|
|
|
60
67
|
Functions to use a JavaScript environment "global" dataset.
|
|
61
68
|
Handles multiple environment somewhat gracefully.
|
|
62
69
|
|
|
70
|
+
## hex
|
|
71
|
+
|
|
72
|
+
Hex string manipulation.
|
|
73
|
+
|
|
63
74
|
## idx
|
|
64
75
|
|
|
65
76
|
IDX check and manipulation functions
|
|
@@ -69,10 +80,9 @@ IDX check and manipulation functions
|
|
|
69
80
|
JSON parsing with type safety.
|
|
70
81
|
Also, a "canonical" JSON encoder that ensure property order is consistent.
|
|
71
82
|
|
|
72
|
-
##
|
|
83
|
+
## line buffer
|
|
73
84
|
|
|
74
|
-
|
|
75
|
-
Supports various log level, timestamping, error chaining, stacktrace stripping.
|
|
85
|
+
Buffer string inputs and output full lines only.
|
|
76
86
|
|
|
77
87
|
## number
|
|
78
88
|
|
|
@@ -90,6 +100,7 @@ Helpers around promises.
|
|
|
90
100
|
- controlled async delay
|
|
91
101
|
- retry a promise until it succeed (with max tries)
|
|
92
102
|
- run an array of promise, stop at the first that succeed
|
|
103
|
+
- silently drop a promise without crashing the VM
|
|
93
104
|
|
|
94
105
|
## starttime
|
|
95
106
|
|
|
@@ -13,59 +13,14 @@
|
|
|
13
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
14
|
*
|
|
15
15
|
*/
|
|
16
|
-
import
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*/
|
|
16
|
+
import * as log from "@keeex/log";
|
|
17
|
+
import * as types from "./types.js";
|
|
18
|
+
/** Manage a single scheduled task */
|
|
20
19
|
export default class ScheduledTask {
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Indicate that the task missed a previous tick
|
|
26
|
-
*/
|
|
27
|
-
private readonly _restartImmediate;
|
|
28
|
-
/**
|
|
29
|
-
* If the task is canceled (prevent looping)
|
|
30
|
-
*/
|
|
31
|
-
private _canceled;
|
|
32
|
-
/**
|
|
33
|
-
* Timeout handler
|
|
34
|
-
*/
|
|
35
|
-
private _timeoutHandler;
|
|
36
|
-
/**
|
|
37
|
-
* Last run time (timestamp)
|
|
38
|
-
*/
|
|
39
|
-
private _lastRun;
|
|
40
|
-
constructor(cronDefinition: string, func: TaskFunction, opts: CompleteTaskOptions);
|
|
41
|
-
/**
|
|
42
|
-
* Determine if the task should be running right now
|
|
43
|
-
*/
|
|
20
|
+
#private;
|
|
21
|
+
constructor(cronDefinition: string, func: types.TaskFunction, opts: types.CompleteTaskOptions, logger?: log.Logger);
|
|
22
|
+
/** Determine if the task should be running right now */
|
|
44
23
|
get runnable(): boolean;
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
*/
|
|
48
|
-
cancel(): void;
|
|
49
|
-
/**
|
|
50
|
-
* Run the task
|
|
51
|
-
*
|
|
52
|
-
* All special cases are handled there (task already running).
|
|
53
|
-
* Updating the next value is also handled here.
|
|
54
|
-
*/
|
|
55
|
-
private _run;
|
|
56
|
-
/**
|
|
57
|
-
* Reschedule the task for the next occurrence
|
|
58
|
-
*/
|
|
59
|
-
private _reschedule;
|
|
60
|
-
/**
|
|
61
|
-
* Return the timeout delay before the next task scheduling.
|
|
62
|
-
*
|
|
63
|
-
* This can return a lower value if the task is scheduled in the distant
|
|
64
|
-
* future.
|
|
65
|
-
*/
|
|
66
|
-
private _getTimeoutDelay;
|
|
67
|
-
/**
|
|
68
|
-
* Return the next scheduled time starting from the given point in time
|
|
69
|
-
*/
|
|
70
|
-
private _getNextSchedule;
|
|
24
|
+
/** Cancel the task from the scheduling */
|
|
25
|
+
cancel: () => void;
|
|
71
26
|
}
|
|
@@ -13,125 +13,104 @@
|
|
|
13
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
14
|
*
|
|
15
15
|
*/
|
|
16
|
+
import * as log from "@keeex/log";
|
|
16
17
|
import { CronExpressionParser } from "cron-parser";
|
|
18
|
+
import { asError } from "../error.js";
|
|
17
19
|
import { dropPromise } from "../promise.js";
|
|
18
|
-
import
|
|
19
|
-
import { Overrun } from "./types.js";
|
|
20
|
+
import * as types from "./types.js";
|
|
20
21
|
/**
|
|
21
22
|
* Maximum delay for a timeout.
|
|
22
23
|
* Done because setTimeout() sometimes can only accept a 32bit integer
|
|
23
24
|
*/
|
|
24
25
|
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
|
25
26
|
const maxTimeoutDelay = 2 ** 31;
|
|
26
|
-
/**
|
|
27
|
-
* Manage a single scheduled task
|
|
28
|
-
*/
|
|
27
|
+
/** Manage a single scheduled task */
|
|
29
28
|
export default class ScheduledTask {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
*/
|
|
48
|
-
_lastRun;
|
|
49
|
-
constructor(cronDefinition, func, opts) {
|
|
50
|
-
this._cronDefinition = cronDefinition;
|
|
51
|
-
this._func = func;
|
|
52
|
-
this._opts = opts;
|
|
53
|
-
this._lastRun = Date.now();
|
|
54
|
-
this._timeoutHandler = setTimeout(() => dropPromise(this._run()), Math.max(0, this._getTimeoutDelay()));
|
|
29
|
+
#cronDefinition;
|
|
30
|
+
#func;
|
|
31
|
+
#opts;
|
|
32
|
+
/** If the task is canceled (prevent looping) */
|
|
33
|
+
#canceled = false;
|
|
34
|
+
/** Timeout handler */
|
|
35
|
+
#timeoutHandler;
|
|
36
|
+
/** Last run time (timestamp) */
|
|
37
|
+
#lastRun;
|
|
38
|
+
#logger;
|
|
39
|
+
constructor(cronDefinition, func, opts, logger = log.logger) {
|
|
40
|
+
this.#logger = logger;
|
|
41
|
+
this.#cronDefinition = cronDefinition;
|
|
42
|
+
this.#func = func;
|
|
43
|
+
this.#opts = opts;
|
|
44
|
+
this.#lastRun = Date.now();
|
|
45
|
+
this.#timeoutHandler = setTimeout(() => dropPromise(this.#run()), Math.max(0, this.#getTimeoutDelay()));
|
|
55
46
|
}
|
|
56
|
-
/**
|
|
57
|
-
* Determine if the task should be running right now
|
|
58
|
-
*/
|
|
47
|
+
/** Determine if the task should be running right now */
|
|
59
48
|
get runnable() {
|
|
60
|
-
return this
|
|
49
|
+
return this.#getNextSchedule() <= Date.now();
|
|
61
50
|
}
|
|
62
|
-
/**
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
clearTimeout(this._timeoutHandler);
|
|
68
|
-
this._timeoutHandler = null;
|
|
51
|
+
/** Cancel the task from the scheduling */
|
|
52
|
+
cancel = () => {
|
|
53
|
+
if (this.#timeoutHandler) {
|
|
54
|
+
clearTimeout(this.#timeoutHandler);
|
|
55
|
+
this.#timeoutHandler = null;
|
|
69
56
|
}
|
|
70
|
-
this
|
|
71
|
-
}
|
|
57
|
+
this.#canceled = true;
|
|
58
|
+
};
|
|
72
59
|
/**
|
|
73
60
|
* Run the task
|
|
74
61
|
*
|
|
75
62
|
* All special cases are handled there (task already running).
|
|
76
63
|
* Updating the next value is also handled here.
|
|
77
64
|
*/
|
|
78
|
-
async
|
|
79
|
-
this
|
|
65
|
+
#run = async () => {
|
|
66
|
+
this.#timeoutHandler = null;
|
|
80
67
|
if (this.runnable) {
|
|
81
|
-
this
|
|
68
|
+
this.#lastRun = Date.now();
|
|
82
69
|
try {
|
|
83
|
-
await this
|
|
70
|
+
await this.#func();
|
|
84
71
|
}
|
|
85
72
|
catch (error) {
|
|
86
|
-
|
|
87
|
-
logError(error);
|
|
88
|
-
}
|
|
89
|
-
logError(new Error(`Unknown error: ${error.toString()}`));
|
|
73
|
+
this.#logger.error(asError(error));
|
|
90
74
|
}
|
|
91
75
|
}
|
|
92
|
-
this
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
_reschedule() {
|
|
98
|
-
if (this._canceled) {
|
|
76
|
+
this.#reschedule();
|
|
77
|
+
};
|
|
78
|
+
/** Reschedule the task for the next occurrence */
|
|
79
|
+
#reschedule = () => {
|
|
80
|
+
if (this.#canceled)
|
|
99
81
|
return;
|
|
100
|
-
|
|
101
|
-
let nextDelay = this._getTimeoutDelay();
|
|
82
|
+
let nextDelay = this.#getTimeoutDelay();
|
|
102
83
|
if (nextDelay < 0) {
|
|
103
84
|
// We overshot
|
|
104
|
-
switch (this.
|
|
105
|
-
case Overrun.AFTER:
|
|
85
|
+
switch (this.#opts.overrun) {
|
|
86
|
+
case types.Overrun.AFTER:
|
|
106
87
|
// Reschedule immediately
|
|
107
88
|
nextDelay = 0;
|
|
108
89
|
break;
|
|
109
|
-
case Overrun.SKIP:
|
|
90
|
+
case types.Overrun.SKIP:
|
|
110
91
|
// Reschedule for the next time slot
|
|
111
|
-
this
|
|
112
|
-
nextDelay = this
|
|
92
|
+
this.#lastRun = Date.now();
|
|
93
|
+
nextDelay = this.#getTimeoutDelay();
|
|
113
94
|
}
|
|
114
95
|
}
|
|
115
|
-
this
|
|
116
|
-
}
|
|
96
|
+
this.#timeoutHandler = setTimeout(() => dropPromise(this.#run()), Math.max(0, nextDelay));
|
|
97
|
+
};
|
|
117
98
|
/**
|
|
118
99
|
* Return the timeout delay before the next task scheduling.
|
|
119
100
|
*
|
|
120
101
|
* This can return a lower value if the task is scheduled in the distant
|
|
121
102
|
* future.
|
|
122
103
|
*/
|
|
123
|
-
|
|
104
|
+
#getTimeoutDelay = () => {
|
|
124
105
|
const current = Date.now();
|
|
125
|
-
const next = this
|
|
106
|
+
const next = this.#getNextSchedule();
|
|
126
107
|
return Math.min(next - current, maxTimeoutDelay);
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const nextOccurrence = CronExpressionParser.parse(this._cronDefinition, {
|
|
133
|
-
currentDate: this._lastRun,
|
|
108
|
+
};
|
|
109
|
+
/** Return the next scheduled time starting from the given point in time */
|
|
110
|
+
#getNextSchedule = () => {
|
|
111
|
+
const nextOccurrence = CronExpressionParser.parse(this.#cronDefinition, {
|
|
112
|
+
currentDate: this.#lastRun,
|
|
134
113
|
}).next();
|
|
135
114
|
return nextOccurrence.getTime();
|
|
136
|
-
}
|
|
115
|
+
};
|
|
137
116
|
}
|
package/lib/cron/types.d.ts
CHANGED
package/lib/cron.d.ts
CHANGED
|
@@ -13,17 +13,18 @@
|
|
|
13
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
14
|
*
|
|
15
15
|
*/
|
|
16
|
+
import { Logger } from "@keeex/log";
|
|
16
17
|
import ScheduledTask from "./cron/scheduledtask.js";
|
|
17
18
|
import { TaskFunction, TaskOptions, Overrun, CompleteTaskOptions } from "./cron/types.js";
|
|
18
19
|
export { Overrun };
|
|
19
20
|
export type { ScheduledTask, TaskFunction, TaskOptions, CompleteTaskOptions };
|
|
20
|
-
export declare const setLoggerFunction: (logFunc?: import("./cron/types.js").LoggerFunction) => void;
|
|
21
21
|
/**
|
|
22
22
|
* Schedule a task for execution
|
|
23
23
|
*
|
|
24
24
|
* @param cronDefinition - CRON timing for the task
|
|
25
25
|
* @param task - The task function to run at the scheduled time
|
|
26
26
|
* @param taskOptions - The behavior of the task
|
|
27
|
+
* @param logger - Logger for errors
|
|
27
28
|
*/
|
|
28
|
-
export declare const schedule: (cronDefinition: string, task: TaskFunction, taskOptions?: TaskOptions) => ScheduledTask;
|
|
29
|
+
export declare const schedule: (cronDefinition: string, task: TaskFunction, taskOptions?: TaskOptions, logger?: Logger) => ScheduledTask;
|
|
29
30
|
export declare const stop: () => void;
|
package/lib/cron.js
CHANGED
|
@@ -13,12 +13,10 @@
|
|
|
13
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
14
|
*
|
|
15
15
|
*/
|
|
16
|
-
import { setLoggerFunction as bitsSetLoggerFunction } from "./cron/logger.js";
|
|
17
16
|
import ScheduledTask from "./cron/scheduledtask.js";
|
|
18
17
|
import { Overrun } from "./cron/types.js";
|
|
19
18
|
const scheduledTasks = [];
|
|
20
19
|
export { Overrun };
|
|
21
|
-
export const setLoggerFunction = bitsSetLoggerFunction;
|
|
22
20
|
/**
|
|
23
21
|
* Set default values for task options
|
|
24
22
|
*/
|
|
@@ -35,9 +33,10 @@ const getDefaultTaskOptions = (taskOptions) => {
|
|
|
35
33
|
* @param cronDefinition - CRON timing for the task
|
|
36
34
|
* @param task - The task function to run at the scheduled time
|
|
37
35
|
* @param taskOptions - The behavior of the task
|
|
36
|
+
* @param logger - Logger for errors
|
|
38
37
|
*/
|
|
39
|
-
export const schedule = (cronDefinition, task, taskOptions) => {
|
|
40
|
-
const newTask = new ScheduledTask(cronDefinition, task, getDefaultTaskOptions(taskOptions));
|
|
38
|
+
export const schedule = (cronDefinition, task, taskOptions, logger) => {
|
|
39
|
+
const newTask = new ScheduledTask(cronDefinition, task, getDefaultTaskOptions(taskOptions), logger);
|
|
41
40
|
scheduledTasks.push(newTask);
|
|
42
41
|
return newTask;
|
|
43
42
|
};
|
|
@@ -13,10 +13,18 @@
|
|
|
13
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
14
|
*
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
/** Callback called on each line from `LineBuffer` */
|
|
17
|
+
export type OutputCallback = (msg: string) => void;
|
|
17
18
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
19
|
+
* Bufferize input and produce output on newlines.
|
|
20
|
+
*
|
|
21
|
+
* The provided callback will be called on all lines, but will not get the newline character.
|
|
20
22
|
*/
|
|
21
|
-
export declare
|
|
22
|
-
|
|
23
|
+
export declare class LineBuffer {
|
|
24
|
+
#private;
|
|
25
|
+
constructor(cb: OutputCallback, separator?: string);
|
|
26
|
+
/** Call this method to add data to the buffer, and output all complete lines */
|
|
27
|
+
feed: (msg: string) => void;
|
|
28
|
+
/** Call this method to purge all currently buffered data */
|
|
29
|
+
purge: () => void;
|
|
30
|
+
}
|
|
@@ -13,10 +13,35 @@
|
|
|
13
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
14
|
*
|
|
15
15
|
*/
|
|
16
|
-
import type { LoggerFunction } from "./types.js";
|
|
17
16
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* Bufferize input and produce output on newlines.
|
|
18
|
+
*
|
|
19
|
+
* The provided callback will be called on all lines, but will not get the newline character.
|
|
20
20
|
*/
|
|
21
|
-
export
|
|
22
|
-
|
|
21
|
+
export class LineBuffer {
|
|
22
|
+
#bufferedInput = "";
|
|
23
|
+
#callback;
|
|
24
|
+
#separator;
|
|
25
|
+
constructor(cb, separator = "\n") {
|
|
26
|
+
this.#callback = cb;
|
|
27
|
+
this.#separator = separator;
|
|
28
|
+
}
|
|
29
|
+
/** Call this method to add data to the buffer, and output all complete lines */
|
|
30
|
+
feed = (msg) => {
|
|
31
|
+
this.#bufferedInput += msg;
|
|
32
|
+
this.#processInput();
|
|
33
|
+
};
|
|
34
|
+
/** Call this method to purge all currently buffered data */
|
|
35
|
+
purge = () => {
|
|
36
|
+
this.#callback(this.#bufferedInput);
|
|
37
|
+
this.#bufferedInput = "";
|
|
38
|
+
};
|
|
39
|
+
/** Filter the buffer, and output all full lines */
|
|
40
|
+
#processInput = () => {
|
|
41
|
+
for (let i = this.#bufferedInput.indexOf(this.#separator); i !== -1; i = this.#bufferedInput.indexOf(this.#separator)) {
|
|
42
|
+
const line = this.#bufferedInput.substring(0, i);
|
|
43
|
+
this.#callback(line);
|
|
44
|
+
this.#bufferedInput = this.#bufferedInput.substring(i + this.#separator.length);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
package/lib/promise.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
*/
|
|
16
16
|
/* eslint-env node */
|
|
17
|
+
import * as log from "@keeex/log";
|
|
17
18
|
import { timeConvert } from "./units.js";
|
|
18
19
|
/**
|
|
19
20
|
* Create a promise that resolves after a given delay.
|
|
@@ -67,6 +68,7 @@ export const firstTruthy = async (promiseFuncs) => {
|
|
|
67
68
|
}
|
|
68
69
|
throw new Error("No result");
|
|
69
70
|
};
|
|
71
|
+
const logger = log.createLogger("dropPromise");
|
|
70
72
|
/**
|
|
71
73
|
* Drop a promise and silence any exception.
|
|
72
74
|
*
|
|
@@ -78,7 +80,9 @@ export const dropPromise = (promise) => {
|
|
|
78
80
|
return;
|
|
79
81
|
}
|
|
80
82
|
if (promise instanceof Promise) {
|
|
81
|
-
// eslint-disable-next-line
|
|
82
|
-
promise.catch(() => {
|
|
83
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
84
|
+
promise.catch((e) => {
|
|
85
|
+
logger.error("Dropped promise rejected:", e);
|
|
86
|
+
});
|
|
83
87
|
}
|
|
84
88
|
};
|
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
|
|
96
|
-
const regexAfter =
|
|
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
|
|
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 =
|
|
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 =
|
|
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.1
|
|
1
|
+
{"name":"@keeex/utils","version":"7.2.1","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"]}
|
|
@@ -13,59 +13,14 @@
|
|
|
13
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
14
|
*
|
|
15
15
|
*/
|
|
16
|
-
import
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*/
|
|
16
|
+
import * as log from "@keeex/log";
|
|
17
|
+
import * as types from "./types.js";
|
|
18
|
+
/** Manage a single scheduled task */
|
|
20
19
|
export default class ScheduledTask {
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Indicate that the task missed a previous tick
|
|
26
|
-
*/
|
|
27
|
-
private readonly _restartImmediate;
|
|
28
|
-
/**
|
|
29
|
-
* If the task is canceled (prevent looping)
|
|
30
|
-
*/
|
|
31
|
-
private _canceled;
|
|
32
|
-
/**
|
|
33
|
-
* Timeout handler
|
|
34
|
-
*/
|
|
35
|
-
private _timeoutHandler;
|
|
36
|
-
/**
|
|
37
|
-
* Last run time (timestamp)
|
|
38
|
-
*/
|
|
39
|
-
private _lastRun;
|
|
40
|
-
constructor(cronDefinition: string, func: TaskFunction, opts: CompleteTaskOptions);
|
|
41
|
-
/**
|
|
42
|
-
* Determine if the task should be running right now
|
|
43
|
-
*/
|
|
20
|
+
#private;
|
|
21
|
+
constructor(cronDefinition: string, func: types.TaskFunction, opts: types.CompleteTaskOptions, logger?: log.Logger);
|
|
22
|
+
/** Determine if the task should be running right now */
|
|
44
23
|
get runnable(): boolean;
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
*/
|
|
48
|
-
cancel(): void;
|
|
49
|
-
/**
|
|
50
|
-
* Run the task
|
|
51
|
-
*
|
|
52
|
-
* All special cases are handled there (task already running).
|
|
53
|
-
* Updating the next value is also handled here.
|
|
54
|
-
*/
|
|
55
|
-
private _run;
|
|
56
|
-
/**
|
|
57
|
-
* Reschedule the task for the next occurrence
|
|
58
|
-
*/
|
|
59
|
-
private _reschedule;
|
|
60
|
-
/**
|
|
61
|
-
* Return the timeout delay before the next task scheduling.
|
|
62
|
-
*
|
|
63
|
-
* This can return a lower value if the task is scheduled in the distant
|
|
64
|
-
* future.
|
|
65
|
-
*/
|
|
66
|
-
private _getTimeoutDelay;
|
|
67
|
-
/**
|
|
68
|
-
* Return the next scheduled time starting from the given point in time
|
|
69
|
-
*/
|
|
70
|
-
private _getNextSchedule;
|
|
24
|
+
/** Cancel the task from the scheduling */
|
|
25
|
+
cancel: () => void;
|
|
71
26
|
}
|
|
@@ -13,124 +13,102 @@
|
|
|
13
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
14
|
*
|
|
15
15
|
*/
|
|
16
|
+
import * as log from "@keeex/log";
|
|
16
17
|
import { CronExpressionParser } from "cron-parser";
|
|
18
|
+
import { asError } from "../error.js";
|
|
17
19
|
import { dropPromise } from "../promise.js";
|
|
18
|
-
import
|
|
19
|
-
import { Overrun } from "./types.js";
|
|
20
|
+
import * as types from "./types.js";
|
|
20
21
|
/**
|
|
21
22
|
* Maximum delay for a timeout.
|
|
22
23
|
* Done because setTimeout() sometimes can only accept a 32bit integer
|
|
23
24
|
*/
|
|
24
25
|
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
|
25
26
|
const maxTimeoutDelay = 2 ** 31;
|
|
26
|
-
/**
|
|
27
|
-
* Manage a single scheduled task
|
|
28
|
-
*/
|
|
27
|
+
/** Manage a single scheduled task */
|
|
29
28
|
export default class ScheduledTask {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
*/
|
|
48
|
-
_lastRun;
|
|
49
|
-
constructor(cronDefinition, func, opts) {
|
|
50
|
-
this._cronDefinition = cronDefinition;
|
|
51
|
-
this._func = func;
|
|
52
|
-
this._opts = opts;
|
|
53
|
-
this._lastRun = Date.now();
|
|
54
|
-
this._timeoutHandler = setTimeout(() => dropPromise(this._run()), Math.max(0, this._getTimeoutDelay()));
|
|
29
|
+
#cronDefinition;
|
|
30
|
+
#func;
|
|
31
|
+
#opts;
|
|
32
|
+
/** If the task is canceled (prevent looping) */
|
|
33
|
+
#canceled = false;
|
|
34
|
+
/** Timeout handler */
|
|
35
|
+
#timeoutHandler;
|
|
36
|
+
/** Last run time (timestamp) */
|
|
37
|
+
#lastRun;
|
|
38
|
+
#logger;
|
|
39
|
+
constructor(cronDefinition, func, opts, logger = log.logger) {
|
|
40
|
+
this.#logger = logger;
|
|
41
|
+
this.#cronDefinition = cronDefinition;
|
|
42
|
+
this.#func = func;
|
|
43
|
+
this.#opts = opts;
|
|
44
|
+
this.#lastRun = Date.now();
|
|
45
|
+
this.#timeoutHandler = setTimeout(() => dropPromise(this.#run()), Math.max(0, this.#getTimeoutDelay()));
|
|
55
46
|
}
|
|
56
|
-
/**
|
|
57
|
-
* Determine if the task should be running right now
|
|
58
|
-
*/
|
|
47
|
+
/** Determine if the task should be running right now */
|
|
59
48
|
get runnable() {
|
|
60
|
-
return this
|
|
49
|
+
return this.#getNextSchedule() <= Date.now();
|
|
61
50
|
}
|
|
62
|
-
/**
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
clearTimeout(this._timeoutHandler);
|
|
68
|
-
this._timeoutHandler = null;
|
|
51
|
+
/** Cancel the task from the scheduling */
|
|
52
|
+
cancel = () => {
|
|
53
|
+
if (this.#timeoutHandler) {
|
|
54
|
+
clearTimeout(this.#timeoutHandler);
|
|
55
|
+
this.#timeoutHandler = null;
|
|
69
56
|
}
|
|
70
|
-
this
|
|
71
|
-
}
|
|
57
|
+
this.#canceled = true;
|
|
58
|
+
};
|
|
72
59
|
/**
|
|
73
60
|
* Run the task
|
|
74
61
|
*
|
|
75
62
|
* All special cases are handled there (task already running).
|
|
76
63
|
* Updating the next value is also handled here.
|
|
77
64
|
*/
|
|
78
|
-
async
|
|
79
|
-
this
|
|
65
|
+
#run = async () => {
|
|
66
|
+
this.#timeoutHandler = null;
|
|
80
67
|
if (this.runnable) {
|
|
81
|
-
this
|
|
68
|
+
this.#lastRun = Date.now();
|
|
82
69
|
try {
|
|
83
|
-
await this
|
|
70
|
+
await this.#func();
|
|
84
71
|
} catch (error) {
|
|
85
|
-
|
|
86
|
-
logError(error);
|
|
87
|
-
}
|
|
88
|
-
logError(new Error(`Unknown error: ${error.toString()}`));
|
|
72
|
+
this.#logger.error(asError(error));
|
|
89
73
|
}
|
|
90
74
|
}
|
|
91
|
-
this
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (this._canceled) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
let nextDelay = this._getTimeoutDelay();
|
|
75
|
+
this.#reschedule();
|
|
76
|
+
};
|
|
77
|
+
/** Reschedule the task for the next occurrence */
|
|
78
|
+
#reschedule = () => {
|
|
79
|
+
if (this.#canceled) return;
|
|
80
|
+
let nextDelay = this.#getTimeoutDelay();
|
|
101
81
|
if (nextDelay < 0) {
|
|
102
82
|
// We overshot
|
|
103
|
-
switch (this.
|
|
104
|
-
case Overrun.AFTER:
|
|
83
|
+
switch (this.#opts.overrun) {
|
|
84
|
+
case types.Overrun.AFTER:
|
|
105
85
|
// Reschedule immediately
|
|
106
86
|
nextDelay = 0;
|
|
107
87
|
break;
|
|
108
|
-
case Overrun.SKIP:
|
|
88
|
+
case types.Overrun.SKIP:
|
|
109
89
|
// Reschedule for the next time slot
|
|
110
|
-
this
|
|
111
|
-
nextDelay = this
|
|
90
|
+
this.#lastRun = Date.now();
|
|
91
|
+
nextDelay = this.#getTimeoutDelay();
|
|
112
92
|
}
|
|
113
93
|
}
|
|
114
|
-
this
|
|
115
|
-
}
|
|
94
|
+
this.#timeoutHandler = setTimeout(() => dropPromise(this.#run()), Math.max(0, nextDelay));
|
|
95
|
+
};
|
|
116
96
|
/**
|
|
117
97
|
* Return the timeout delay before the next task scheduling.
|
|
118
98
|
*
|
|
119
99
|
* This can return a lower value if the task is scheduled in the distant
|
|
120
100
|
* future.
|
|
121
101
|
*/
|
|
122
|
-
|
|
102
|
+
#getTimeoutDelay = () => {
|
|
123
103
|
const current = Date.now();
|
|
124
|
-
const next = this
|
|
104
|
+
const next = this.#getNextSchedule();
|
|
125
105
|
return Math.min(next - current, maxTimeoutDelay);
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const nextOccurrence = CronExpressionParser.parse(this._cronDefinition, {
|
|
132
|
-
currentDate: this._lastRun
|
|
106
|
+
};
|
|
107
|
+
/** Return the next scheduled time starting from the given point in time */
|
|
108
|
+
#getNextSchedule = () => {
|
|
109
|
+
const nextOccurrence = CronExpressionParser.parse(this.#cronDefinition, {
|
|
110
|
+
currentDate: this.#lastRun
|
|
133
111
|
}).next();
|
|
134
112
|
return nextOccurrence.getTime();
|
|
135
|
-
}
|
|
113
|
+
};
|
|
136
114
|
}
|
package/web/cron/types.d.ts
CHANGED
package/web/cron.d.ts
CHANGED
|
@@ -13,17 +13,18 @@
|
|
|
13
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
14
|
*
|
|
15
15
|
*/
|
|
16
|
+
import { Logger } from "@keeex/log";
|
|
16
17
|
import ScheduledTask from "./cron/scheduledtask.js";
|
|
17
18
|
import { TaskFunction, TaskOptions, Overrun, CompleteTaskOptions } from "./cron/types.js";
|
|
18
19
|
export { Overrun };
|
|
19
20
|
export type { ScheduledTask, TaskFunction, TaskOptions, CompleteTaskOptions };
|
|
20
|
-
export declare const setLoggerFunction: (logFunc?: import("./cron/types.js").LoggerFunction) => void;
|
|
21
21
|
/**
|
|
22
22
|
* Schedule a task for execution
|
|
23
23
|
*
|
|
24
24
|
* @param cronDefinition - CRON timing for the task
|
|
25
25
|
* @param task - The task function to run at the scheduled time
|
|
26
26
|
* @param taskOptions - The behavior of the task
|
|
27
|
+
* @param logger - Logger for errors
|
|
27
28
|
*/
|
|
28
|
-
export declare const schedule: (cronDefinition: string, task: TaskFunction, taskOptions?: TaskOptions) => ScheduledTask;
|
|
29
|
+
export declare const schedule: (cronDefinition: string, task: TaskFunction, taskOptions?: TaskOptions, logger?: Logger) => ScheduledTask;
|
|
29
30
|
export declare const stop: () => void;
|
package/web/cron.js
CHANGED
|
@@ -13,12 +13,10 @@
|
|
|
13
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
14
|
*
|
|
15
15
|
*/
|
|
16
|
-
import { setLoggerFunction as bitsSetLoggerFunction } from "./cron/logger.js";
|
|
17
16
|
import ScheduledTask from "./cron/scheduledtask.js";
|
|
18
17
|
import { Overrun } from "./cron/types.js";
|
|
19
18
|
const scheduledTasks = [];
|
|
20
19
|
export { Overrun };
|
|
21
|
-
export const setLoggerFunction = bitsSetLoggerFunction;
|
|
22
20
|
/**
|
|
23
21
|
* Set default values for task options
|
|
24
22
|
*/
|
|
@@ -35,9 +33,10 @@ const getDefaultTaskOptions = taskOptions => {
|
|
|
35
33
|
* @param cronDefinition - CRON timing for the task
|
|
36
34
|
* @param task - The task function to run at the scheduled time
|
|
37
35
|
* @param taskOptions - The behavior of the task
|
|
36
|
+
* @param logger - Logger for errors
|
|
38
37
|
*/
|
|
39
|
-
export const schedule = (cronDefinition, task, taskOptions) => {
|
|
40
|
-
const newTask = new ScheduledTask(cronDefinition, task, getDefaultTaskOptions(taskOptions));
|
|
38
|
+
export const schedule = (cronDefinition, task, taskOptions, logger) => {
|
|
39
|
+
const newTask = new ScheduledTask(cronDefinition, task, getDefaultTaskOptions(taskOptions), logger);
|
|
41
40
|
scheduledTasks.push(newTask);
|
|
42
41
|
return newTask;
|
|
43
42
|
};
|
|
@@ -13,19 +13,18 @@
|
|
|
13
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
14
|
*
|
|
15
15
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
/** Callback called on each line from `LineBuffer` */
|
|
17
|
+
export type OutputCallback = (msg: string) => void;
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* Bufferize input and produce output on newlines.
|
|
20
|
+
*
|
|
21
|
+
* The provided callback will be called on all lines, but will not get the newline character.
|
|
21
22
|
*/
|
|
22
|
-
export
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
};
|
|
31
|
-
export const logError = (error) => loggerFunction(error);
|
|
23
|
+
export declare class LineBuffer {
|
|
24
|
+
#private;
|
|
25
|
+
constructor(cb: OutputCallback, separator?: string);
|
|
26
|
+
/** Call this method to add data to the buffer, and output all complete lines */
|
|
27
|
+
feed: (msg: string) => void;
|
|
28
|
+
/** Call this method to purge all currently buffered data */
|
|
29
|
+
purge: () => void;
|
|
30
|
+
}
|
|
@@ -13,18 +13,35 @@
|
|
|
13
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
14
|
*
|
|
15
15
|
*/
|
|
16
|
-
// eslint-disable-next-line no-console
|
|
17
|
-
let loggerFunction = console.error;
|
|
18
16
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* Bufferize input and produce output on newlines.
|
|
18
|
+
*
|
|
19
|
+
* The provided callback will be called on all lines, but will not get the newline character.
|
|
21
20
|
*/
|
|
22
|
-
export
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
export class LineBuffer {
|
|
22
|
+
#bufferedInput = "";
|
|
23
|
+
#callback;
|
|
24
|
+
#separator;
|
|
25
|
+
constructor(cb, separator = "\n") {
|
|
26
|
+
this.#callback = cb;
|
|
27
|
+
this.#separator = separator;
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
/** Call this method to add data to the buffer, and output all complete lines */
|
|
30
|
+
feed = msg => {
|
|
31
|
+
this.#bufferedInput += msg;
|
|
32
|
+
this.#processInput();
|
|
33
|
+
};
|
|
34
|
+
/** Call this method to purge all currently buffered data */
|
|
35
|
+
purge = () => {
|
|
36
|
+
this.#callback(this.#bufferedInput);
|
|
37
|
+
this.#bufferedInput = "";
|
|
38
|
+
};
|
|
39
|
+
/** Filter the buffer, and output all full lines */
|
|
40
|
+
#processInput = () => {
|
|
41
|
+
for (let i = this.#bufferedInput.indexOf(this.#separator); i !== -1; i = this.#bufferedInput.indexOf(this.#separator)) {
|
|
42
|
+
const line = this.#bufferedInput.substring(0, i);
|
|
43
|
+
this.#callback(line);
|
|
44
|
+
this.#bufferedInput = this.#bufferedInput.substring(i + this.#separator.length);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
package/web/promise.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
*/
|
|
16
16
|
/* eslint-env node */
|
|
17
|
+
import * as log from "@keeex/log";
|
|
17
18
|
import { timeConvert } from "./units.js";
|
|
18
19
|
/**
|
|
19
20
|
* Create a promise that resolves after a given delay.
|
|
@@ -63,6 +64,7 @@ export const firstTruthy = async promiseFuncs => {
|
|
|
63
64
|
}
|
|
64
65
|
throw new Error("No result");
|
|
65
66
|
};
|
|
67
|
+
const logger = log.createLogger("dropPromise");
|
|
66
68
|
/**
|
|
67
69
|
* Drop a promise and silence any exception.
|
|
68
70
|
*
|
|
@@ -74,7 +76,9 @@ export const dropPromise = promise => {
|
|
|
74
76
|
return;
|
|
75
77
|
}
|
|
76
78
|
if (promise instanceof Promise) {
|
|
77
|
-
// eslint-disable-next-line
|
|
78
|
-
promise.catch(
|
|
79
|
+
// eslint-disable-next-line promise/prefer-await-to-then
|
|
80
|
+
promise.catch(e => {
|
|
81
|
+
logger.error("Dropped promise rejected:", e);
|
|
82
|
+
});
|
|
79
83
|
}
|
|
80
84
|
};
|
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
|
|
129
|
-
const regexAfter =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
};
|