@keeex/utils 7.1.0 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -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
- - `Queues` to create parallel queues of tasks
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
- ## log
83
+ ## line buffer
73
84
 
74
- Generic logging functionality.
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 { TaskFunction, CompleteTaskOptions } from "./types.js";
17
- /**
18
- * Manage a single scheduled task
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 readonly _cronDefinition;
22
- private readonly _func;
23
- private readonly _opts;
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
- * Cancel the task from the scheduling
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 { logError } from "./logger.js";
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
- _cronDefinition;
31
- _func;
32
- _opts;
33
- /**
34
- * Indicate that the task missed a previous tick
35
- */
36
- _restartImmediate = false;
37
- /**
38
- * If the task is canceled (prevent looping)
39
- */
40
- _canceled = false;
41
- /**
42
- * Timeout handler
43
- */
44
- _timeoutHandler;
45
- /**
46
- * Last run time (timestamp)
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._getNextSchedule() <= Date.now();
49
+ return this.#getNextSchedule() <= Date.now();
61
50
  }
62
- /**
63
- * Cancel the task from the scheduling
64
- */
65
- cancel() {
66
- if (this._timeoutHandler) {
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._canceled = true;
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 _run() {
79
- this._timeoutHandler = null;
65
+ #run = async () => {
66
+ this.#timeoutHandler = null;
80
67
  if (this.runnable) {
81
- this._lastRun = Date.now();
68
+ this.#lastRun = Date.now();
82
69
  try {
83
- await this._func();
70
+ await this.#func();
84
71
  }
85
72
  catch (error) {
86
- if (error instanceof Error) {
87
- logError(error);
88
- }
89
- logError(new Error(`Unknown error: ${error.toString()}`));
73
+ this.#logger.error(asError(error));
90
74
  }
91
75
  }
92
- this._reschedule();
93
- }
94
- /**
95
- * Reschedule the task for the next occurrence
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._opts.overrun) {
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._lastRun = Date.now();
112
- nextDelay = this._getTimeoutDelay();
92
+ this.#lastRun = Date.now();
93
+ nextDelay = this.#getTimeoutDelay();
113
94
  }
114
95
  }
115
- this._timeoutHandler = setTimeout(() => dropPromise(this._run()), Math.max(0, nextDelay));
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
- _getTimeoutDelay() {
104
+ #getTimeoutDelay = () => {
124
105
  const current = Date.now();
125
- const next = this._getNextSchedule();
106
+ const next = this.#getNextSchedule();
126
107
  return Math.min(next - current, maxTimeoutDelay);
127
- }
128
- /**
129
- * Return the next scheduled time starting from the given point in time
130
- */
131
- _getNextSchedule() {
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
  }
@@ -20,7 +20,6 @@
20
20
  * promise didn't complete
21
21
  */
22
22
  export type TaskFunction = () => Promise<void> | void;
23
- export type LoggerFunction = (error: Error) => void;
24
23
  /**
25
24
  * Behavior when a task is scheduled to start while it's already running
26
25
  */
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
- import type { LoggerFunction } from "./types.js";
16
+ /** Callback called on each line from `LineBuffer` */
17
+ export type OutputCallback = (msg: string) => void;
17
18
  /**
18
- * Change the function used to log errors
19
- * @param logFunc - Callback that will receive errors
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 const setLoggerFunction: (logFunc?: LoggerFunction) => void;
22
- export declare const logError: (error: Error) => void;
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
- * Change the function used to log errors
19
- * @param logFunc - Callback that will receive errors
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 declare const setLoggerFunction: (logFunc?: LoggerFunction) => void;
22
- export declare const logError: (error: Error) => void;
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/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@keeex/utils","version":"7.1.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.3.0","base64-arraybuffer":"^1.0.2","cron-parser":"^5.1.1","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"},"./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"},"./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"},"./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"},"./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.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"]}
@@ -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 { TaskFunction, CompleteTaskOptions } from "./types.js";
17
- /**
18
- * Manage a single scheduled task
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 readonly _cronDefinition;
22
- private readonly _func;
23
- private readonly _opts;
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
- * Cancel the task from the scheduling
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 { logError } from "./logger.js";
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
- _cronDefinition;
31
- _func;
32
- _opts;
33
- /**
34
- * Indicate that the task missed a previous tick
35
- */
36
- _restartImmediate = false;
37
- /**
38
- * If the task is canceled (prevent looping)
39
- */
40
- _canceled = false;
41
- /**
42
- * Timeout handler
43
- */
44
- _timeoutHandler;
45
- /**
46
- * Last run time (timestamp)
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._getNextSchedule() <= Date.now();
49
+ return this.#getNextSchedule() <= Date.now();
61
50
  }
62
- /**
63
- * Cancel the task from the scheduling
64
- */
65
- cancel() {
66
- if (this._timeoutHandler) {
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._canceled = true;
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 _run() {
79
- this._timeoutHandler = null;
65
+ #run = async () => {
66
+ this.#timeoutHandler = null;
80
67
  if (this.runnable) {
81
- this._lastRun = Date.now();
68
+ this.#lastRun = Date.now();
82
69
  try {
83
- await this._func();
70
+ await this.#func();
84
71
  } catch (error) {
85
- if (error instanceof Error) {
86
- logError(error);
87
- }
88
- logError(new Error(`Unknown error: ${error.toString()}`));
72
+ this.#logger.error(asError(error));
89
73
  }
90
74
  }
91
- this._reschedule();
92
- }
93
- /**
94
- * Reschedule the task for the next occurrence
95
- */
96
- _reschedule() {
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._opts.overrun) {
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._lastRun = Date.now();
111
- nextDelay = this._getTimeoutDelay();
90
+ this.#lastRun = Date.now();
91
+ nextDelay = this.#getTimeoutDelay();
112
92
  }
113
93
  }
114
- this._timeoutHandler = setTimeout(() => dropPromise(this._run()), Math.max(0, nextDelay));
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
- _getTimeoutDelay() {
102
+ #getTimeoutDelay = () => {
123
103
  const current = Date.now();
124
- const next = this._getNextSchedule();
104
+ const next = this.#getNextSchedule();
125
105
  return Math.min(next - current, maxTimeoutDelay);
126
- }
127
- /**
128
- * Return the next scheduled time starting from the given point in time
129
- */
130
- _getNextSchedule() {
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
  }
@@ -20,7 +20,6 @@
20
20
  * promise didn't complete
21
21
  */
22
22
  export type TaskFunction = () => Promise<void> | void;
23
- export type LoggerFunction = (error: Error) => void;
24
23
  /**
25
24
  * Behavior when a task is scheduled to start while it's already running
26
25
  */
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
- // eslint-disable-next-line no-console
17
- let loggerFunction = console.error;
16
+ /** Callback called on each line from `LineBuffer` */
17
+ export type OutputCallback = (msg: string) => void;
18
18
  /**
19
- * Change the function used to log errors
20
- * @param logFunc - Callback that will receive errors
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 const setLoggerFunction = (logFunc) => {
23
- if (logFunc) {
24
- loggerFunction = logFunc;
25
- }
26
- else {
27
- // eslint-disable-next-line no-console
28
- loggerFunction = console.error;
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
- * Change the function used to log errors
20
- * @param logFunc - Callback that will receive errors
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 const setLoggerFunction = logFunc => {
23
- if (logFunc) {
24
- loggerFunction = logFunc;
25
- } else {
26
- // eslint-disable-next-line no-console
27
- loggerFunction = console.error;
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
- export const logError = error => loggerFunction(error);
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
+ }