@ix-xs/node-comfort 1.0.57 → 1.1.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 +533 -0
- package/index.js +10 -12
- package/package.json +16 -6
- package/src/Checker.js +188 -0
- package/src/FS.js +768 -0
- package/src/Logger.js +307 -0
- package/src/SQLite.js +449 -0
- package/src/Utils.js +325 -0
- package/src/checker.js +0 -76
- package/src/cli.js +0 -86
- package/src/fs.js +0 -656
- package/src/localStorage.js +0 -293
- package/src/logger.js +0 -243
- package/src/utils.js +0 -225
package/src/Utils.js
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
const logger = require("./Logger");
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Miscellaneous async and process helpers.
|
|
5
|
+
*/
|
|
6
|
+
module.exports = {
|
|
7
|
+
/**
|
|
8
|
+
* Waits for a given number of milliseconds.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* await nodeComfort.wait(500); // waits 500 ms
|
|
12
|
+
*
|
|
13
|
+
* @param {number} ms - Delay in milliseconds.
|
|
14
|
+
* @returns {Promise<void>} A promise that resolves after the delay.
|
|
15
|
+
*/
|
|
16
|
+
wait(ms) {
|
|
17
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Polls a predicate at a given interval and emits events when it matches or times out.
|
|
22
|
+
*
|
|
23
|
+
* The predicate is evaluated every `options.interval` milliseconds (default: 50 ms).
|
|
24
|
+
* Be careful with the default interval: 50 ms is very frequent polling and can be
|
|
25
|
+
* unnecessarily CPU‑intensive if your predicate or trigger handlers are heavy.
|
|
26
|
+
* When `options.max` is `null`, the predicate can match indefinitely (infinite triggers),
|
|
27
|
+
* so you should choose an interval that makes sense for your use‑case.
|
|
28
|
+
*
|
|
29
|
+
* A `when(...)` task is one‑shot: once it has been stopped (manually or via `max`/`timeout`),
|
|
30
|
+
* calling `.start()` again on the same task will not restart it.
|
|
31
|
+
*
|
|
32
|
+
* @param {boolean|Promise<boolean>|(() => boolean|Promise<boolean>)} predicate
|
|
33
|
+
* Condition to evaluate at each tick, or a function returning it.
|
|
34
|
+
* @param {*} [payload={}]
|
|
35
|
+
* Value passed to listeners when events are emitted (e.g. an object or message).
|
|
36
|
+
* @param {object} [options]
|
|
37
|
+
* @param {number} [options.interval=50]
|
|
38
|
+
* Polling interval in milliseconds. Increase this value if your predicate is
|
|
39
|
+
* expensive or you expect many triggers without a `max` limit.
|
|
40
|
+
* @param {number|null} [options.timeout=null]
|
|
41
|
+
* Maximum time in milliseconds before emitting a "timeout" event and stopping.
|
|
42
|
+
* If `null`, no timeout is applied.
|
|
43
|
+
* @param {number|null} [options.max=null]
|
|
44
|
+
* Maximum number of successful predicate evaluations ("trigger" events) before
|
|
45
|
+
* automatically stopping. If `null`, triggers are infinite until `stop()` is called.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* // Basic usage: infinite triggers every 50 ms (default interval) – use with caution.
|
|
49
|
+
* const task = nodeComfort.when(() => true, { message: "Ok" });
|
|
50
|
+
*
|
|
51
|
+
* task
|
|
52
|
+
* .on("trigger", (payload) => {
|
|
53
|
+
* console.log(payload.message);
|
|
54
|
+
* // task.stop(); // stop when you’re done
|
|
55
|
+
* })
|
|
56
|
+
* .start();
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // Safer usage: limit triggers and/or lower polling frequency.
|
|
60
|
+
* nodeComfort.when(() => true, { message: "Ok" }, { max: 1, interval: 500 })
|
|
61
|
+
* .on("trigger", (payload) => {
|
|
62
|
+
* console.log(payload.message);
|
|
63
|
+
* })
|
|
64
|
+
* .on("timeout", (payload) => {
|
|
65
|
+
* console.log("Timed out", payload);
|
|
66
|
+
* })
|
|
67
|
+
* .start();
|
|
68
|
+
*
|
|
69
|
+
* @returns {{
|
|
70
|
+
* start: () => any,
|
|
71
|
+
* stop: () => any,
|
|
72
|
+
* on: <E extends "error"|"trigger"|"timeout">(event: E, handler: E extends "error"
|
|
73
|
+
* ? (error: unknown) => void|Promise<void>
|
|
74
|
+
* : (payload: any) => void|Promise<void>) => any
|
|
75
|
+
* }} A small task controller with `start`, `stop` and `on`.
|
|
76
|
+
*/
|
|
77
|
+
when(predicate, payload = {}, options) {
|
|
78
|
+
const interval = options?.interval ?? 50;
|
|
79
|
+
const timeout = options?.timeout ?? null;
|
|
80
|
+
const max = options?.max ?? null;
|
|
81
|
+
|
|
82
|
+
/** @type {{ trigger: Set<Function>, error: Set<Function>, timeout: Set<Function> }} */
|
|
83
|
+
const listeners = {
|
|
84
|
+
trigger: new Set(),
|
|
85
|
+
error: new Set(),
|
|
86
|
+
timeout: new Set(),
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
let _timer = null;
|
|
90
|
+
let _timeoutTimer = null;
|
|
91
|
+
let _stopped = false;
|
|
92
|
+
let _count = 0;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Emits an event to all registered listeners.
|
|
96
|
+
* Errors in listeners are caught and ignored to avoid breaking the polling loop.
|
|
97
|
+
*
|
|
98
|
+
* @param {"trigger"|"error"|"timeout"} event
|
|
99
|
+
* @param {*} p
|
|
100
|
+
*/
|
|
101
|
+
function emit(event, p) {
|
|
102
|
+
for (const fn of listeners[event]) {
|
|
103
|
+
try {
|
|
104
|
+
fn(p);
|
|
105
|
+
} catch {
|
|
106
|
+
// Best effort: we don't re-throw here to keep the task alive.
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function evalPredicate() {
|
|
112
|
+
if (typeof predicate === "function") {
|
|
113
|
+
return await predicate();
|
|
114
|
+
}
|
|
115
|
+
return Boolean(await predicate);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function tick() {
|
|
119
|
+
if (_stopped) return;
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const ok = await evalPredicate();
|
|
123
|
+
|
|
124
|
+
if (ok) {
|
|
125
|
+
_count++;
|
|
126
|
+
emit("trigger", payload);
|
|
127
|
+
|
|
128
|
+
if (max != null && _count >= max) {
|
|
129
|
+
_stopInternal();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!_stopped) {
|
|
135
|
+
_timer = setTimeout(tick, interval);
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
emit("error", error);
|
|
139
|
+
if (!_stopped) {
|
|
140
|
+
_timer = setTimeout(tick, interval);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function _stopInternal() {
|
|
146
|
+
_stopped = true;
|
|
147
|
+
if (_timer) {
|
|
148
|
+
clearTimeout(_timer);
|
|
149
|
+
_timer = null;
|
|
150
|
+
}
|
|
151
|
+
if (_timeoutTimer) {
|
|
152
|
+
clearTimeout(_timeoutTimer);
|
|
153
|
+
_timeoutTimer = null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const controller = {
|
|
158
|
+
/**
|
|
159
|
+
* Starts the polling task (one-shot: no effect if it was already stopped).
|
|
160
|
+
* @returns {typeof controller}
|
|
161
|
+
*/
|
|
162
|
+
start() {
|
|
163
|
+
if (_stopped) return controller;
|
|
164
|
+
|
|
165
|
+
if (!_timer) {
|
|
166
|
+
_timer = setTimeout(tick, 0);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (timeout != null && !_timeoutTimer) {
|
|
170
|
+
_timeoutTimer = setTimeout(() => {
|
|
171
|
+
if (_stopped) return;
|
|
172
|
+
emit("timeout", payload);
|
|
173
|
+
_stopInternal();
|
|
174
|
+
}, timeout);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return controller;
|
|
178
|
+
},
|
|
179
|
+
/**
|
|
180
|
+
* Stops the polling task and clears all timers.
|
|
181
|
+
* @returns {typeof controller}
|
|
182
|
+
*/
|
|
183
|
+
stop() {
|
|
184
|
+
_stopInternal();
|
|
185
|
+
return controller;
|
|
186
|
+
},
|
|
187
|
+
/**
|
|
188
|
+
* Registers an event handler.
|
|
189
|
+
*
|
|
190
|
+
* @typedef { "error" | "trigger" | "timeout" } EventNames
|
|
191
|
+
* @typedef {{
|
|
192
|
+
* error: (error: unknown) => void|Promise<void>,
|
|
193
|
+
* trigger: (payload: any) => void|Promise<void>,
|
|
194
|
+
* timeout: (payload: any) => void|Promise<void>,
|
|
195
|
+
* }} EventHandlers
|
|
196
|
+
*
|
|
197
|
+
* @template {EventNames} E
|
|
198
|
+
* @param {E} event - Event name.
|
|
199
|
+
* @param {EventHandlers[E]} handler - Handler function.
|
|
200
|
+
* @returns {typeof controller}
|
|
201
|
+
*/
|
|
202
|
+
on(event, handler) {
|
|
203
|
+
listeners[event]?.add(handler);
|
|
204
|
+
return controller;
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
return controller;
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Installs process-level safety nets for errors, exits and signals.
|
|
213
|
+
*
|
|
214
|
+
* By default, it:
|
|
215
|
+
* - logs uncaught exceptions and unhandled rejections,
|
|
216
|
+
* - logs process exit codes,
|
|
217
|
+
* - logs SIGINT/SIGTERM/SIGQUIT and exits with code 0.
|
|
218
|
+
*
|
|
219
|
+
* You can override each handler with `.on(event, handler)`.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* // Use defaults:
|
|
223
|
+
* nodeComfort.dontCrash();
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* // Custom error handler:
|
|
227
|
+
* nodeComfort.dontCrash().on("error", (err) => {
|
|
228
|
+
* console.error("Global error:", err);
|
|
229
|
+
* process.exit(1);
|
|
230
|
+
* });
|
|
231
|
+
*
|
|
232
|
+
* @returns {{
|
|
233
|
+
* on: <E extends "error"|"exit"|"sig"|"beforeExit">(event: E, handler?: (
|
|
234
|
+
* E extends "error" ? (error: Error|unknown) => void|Promise<void> :
|
|
235
|
+
* E extends "exit" ? (code: number) => void|Promise<void> :
|
|
236
|
+
* E extends "sig" ? (signal: "SIGINT"|"SIGTERM"|"SIGQUIT") => void|Promise<void> :
|
|
237
|
+
* (code: number) => void|Promise<void>
|
|
238
|
+
* )) => any
|
|
239
|
+
* }} A small controller to configure global handlers.
|
|
240
|
+
*/
|
|
241
|
+
dontCrash() {
|
|
242
|
+
const defaults = {
|
|
243
|
+
error: (error) => {
|
|
244
|
+
logger.log(
|
|
245
|
+
`<% red [@ix-xs/node-comfort] Process Error %>\n<% gray ${error.stack ?? error} %>`,
|
|
246
|
+
);
|
|
247
|
+
},
|
|
248
|
+
exit: (code) => {
|
|
249
|
+
logger.log(
|
|
250
|
+
`<% yellow [@ix-xs/node-comfort] Process Exit %>\n→ Code: <% gray ${code} %>`,
|
|
251
|
+
);
|
|
252
|
+
},
|
|
253
|
+
sig: (signal) => {
|
|
254
|
+
logger.log(
|
|
255
|
+
`<% yellow [@ix-xs/node-comfort] Process SIG* %>\n→ Signal: <% gray ${signal} %>`,
|
|
256
|
+
);
|
|
257
|
+
process.exit(0);
|
|
258
|
+
},
|
|
259
|
+
beforeExit: (code) => {},
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const controller = {
|
|
263
|
+
/**
|
|
264
|
+
* Registers global process handlers.
|
|
265
|
+
*
|
|
266
|
+
* @template { "error" | "exit" | "sig" | "beforeExit" } E
|
|
267
|
+
* @param {E} event - Event name.
|
|
268
|
+
* @param {{
|
|
269
|
+
* error: (error: Error | unknown) => void | Promise<void>,
|
|
270
|
+
* exit: (code: number) => void | Promise<void>,
|
|
271
|
+
* sig: (signal: "SIGINT" | "SIGTERM" | "SIGQUIT") => void | Promise<void>,
|
|
272
|
+
* beforeExit: (code: number) => void|Promise<void>
|
|
273
|
+
* }[E]} [handler] - Custom handler, or use the default when omitted.
|
|
274
|
+
* @returns {typeof controller}
|
|
275
|
+
*/
|
|
276
|
+
on(event, handler) {
|
|
277
|
+
if (event === "error") {
|
|
278
|
+
process.removeAllListeners("uncaughtException");
|
|
279
|
+
process.removeAllListeners("unhandledRejection");
|
|
280
|
+
const fn = handler ?? defaults.error;
|
|
281
|
+
process.on("uncaughtException", fn);
|
|
282
|
+
process.on("unhandledRejection", fn);
|
|
283
|
+
} else if (event === "exit") {
|
|
284
|
+
process.removeAllListeners("exit");
|
|
285
|
+
process.on("exit", handler ?? defaults.exit);
|
|
286
|
+
} else if (event === "sig") {
|
|
287
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGQUIT"]) {
|
|
288
|
+
process.removeAllListeners(signal);
|
|
289
|
+
process.on(signal, handler ?? defaults.sig);
|
|
290
|
+
}
|
|
291
|
+
} else if (event === "beforeExit") {
|
|
292
|
+
process.removeAllListeners("beforeExit");
|
|
293
|
+
process.on("beforeExit", handler ?? defaults.beforeExit);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return controller;
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// Install default handlers for error, exit, sig
|
|
301
|
+
controller.on("error").on("exit").on("sig");
|
|
302
|
+
|
|
303
|
+
return controller;
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Safely stringifies a value to pretty-printed JSON (4 spaces indent).
|
|
308
|
+
*
|
|
309
|
+
* @param {*} value - Value to stringify.
|
|
310
|
+
* @returns {string} JSON string representation.
|
|
311
|
+
*/
|
|
312
|
+
JSONString(value) {
|
|
313
|
+
return JSON.stringify(value, null, 4);
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Parses a JSON string.
|
|
318
|
+
*
|
|
319
|
+
* @param {string} value - JSON string to parse.
|
|
320
|
+
* @returns {*} Parsed value.
|
|
321
|
+
*/
|
|
322
|
+
JSONParse(value) {
|
|
323
|
+
return JSON.parse(value);
|
|
324
|
+
},
|
|
325
|
+
};
|
package/src/checker.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
const { types } = require("node:util");
|
|
2
|
-
|
|
3
|
-
module.exports = {
|
|
4
|
-
/**
|
|
5
|
-
* @param {*} value
|
|
6
|
-
*/
|
|
7
|
-
isArray(value) {
|
|
8
|
-
return Array.isArray(value);
|
|
9
|
-
},
|
|
10
|
-
/**
|
|
11
|
-
* @param {*} value
|
|
12
|
-
*/
|
|
13
|
-
isNumber(value) {
|
|
14
|
-
return typeof value === "number" && !Number.isNaN(value);
|
|
15
|
-
},
|
|
16
|
-
/**
|
|
17
|
-
* @param {*} value
|
|
18
|
-
*/
|
|
19
|
-
isFunction(value) {
|
|
20
|
-
return typeof value === "function";
|
|
21
|
-
},
|
|
22
|
-
/**
|
|
23
|
-
* @param {*} value
|
|
24
|
-
*/
|
|
25
|
-
isObject(value) {
|
|
26
|
-
return typeof value === "object" && value !== null;
|
|
27
|
-
},
|
|
28
|
-
/**
|
|
29
|
-
* @param {*} value
|
|
30
|
-
*/
|
|
31
|
-
isBoolean(value) {
|
|
32
|
-
return typeof value === "boolean";
|
|
33
|
-
},
|
|
34
|
-
/**
|
|
35
|
-
* @param {*} value
|
|
36
|
-
*/
|
|
37
|
-
isString(value) {
|
|
38
|
-
return typeof value === "string";
|
|
39
|
-
},
|
|
40
|
-
/**
|
|
41
|
-
* @param {*} value
|
|
42
|
-
*/
|
|
43
|
-
isUndefined(value) {
|
|
44
|
-
return typeof value === "undefined";
|
|
45
|
-
},
|
|
46
|
-
/**
|
|
47
|
-
* @param {*} value
|
|
48
|
-
*/
|
|
49
|
-
isNull(value) {
|
|
50
|
-
return value === null;
|
|
51
|
-
},
|
|
52
|
-
/**
|
|
53
|
-
* @param {*} value
|
|
54
|
-
*/
|
|
55
|
-
isDate(value) {
|
|
56
|
-
return types.isDate(value);
|
|
57
|
-
},
|
|
58
|
-
/**
|
|
59
|
-
* @param {*} value
|
|
60
|
-
*/
|
|
61
|
-
isMap(value) {
|
|
62
|
-
return types.isMap(value);
|
|
63
|
-
},
|
|
64
|
-
/**
|
|
65
|
-
* @param {*} value
|
|
66
|
-
*/
|
|
67
|
-
isSet(value) {
|
|
68
|
-
return types.isSet(value);
|
|
69
|
-
},
|
|
70
|
-
/**
|
|
71
|
-
* @param {*} value
|
|
72
|
-
*/
|
|
73
|
-
isError(value) {
|
|
74
|
-
return types.isNativeError(value);
|
|
75
|
-
},
|
|
76
|
-
};
|
package/src/cli.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const { execSync, spawn } = require("node:child_process");
|
|
2
|
-
const EventEmitter = require("node:events");
|
|
3
|
-
|
|
4
|
-
module.exports = {
|
|
5
|
-
exec(command) {
|
|
6
|
-
return execSync(command, {
|
|
7
|
-
shell: true,
|
|
8
|
-
stdio: "pipe",
|
|
9
|
-
});
|
|
10
|
-
},
|
|
11
|
-
stream: class {
|
|
12
|
-
#processus;
|
|
13
|
-
#events = new EventEmitter();
|
|
14
|
-
|
|
15
|
-
constructor(command, args) {
|
|
16
|
-
this.#processus = spawn(command, args, {
|
|
17
|
-
shell: true,
|
|
18
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
this.#processus.stdout.on("data", (data) => {
|
|
22
|
-
this.#events.emit("data", data.toString());
|
|
23
|
-
});
|
|
24
|
-
this.#processus.stderr.on("data", (data) => {
|
|
25
|
-
this.#events.emit("data", data.toString());
|
|
26
|
-
});
|
|
27
|
-
this.#processus.on("error", (error) => {
|
|
28
|
-
this.#events.emit("error", error);
|
|
29
|
-
});
|
|
30
|
-
this.#processus.on("exit", async (code, signal) => {
|
|
31
|
-
await this.stop();
|
|
32
|
-
});
|
|
33
|
-
this.#processus.on("close", (code, signal) => {
|
|
34
|
-
this.#events.emit("stopped", code, signal);
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* @typedef {"error"|"data"|"stopped"} EventName
|
|
40
|
-
* @typedef {{
|
|
41
|
-
* error: (error: Error) => void|Promise<void>,
|
|
42
|
-
* data: (chunk: string) => void|Promise<void>,
|
|
43
|
-
* stopped: (code: number|null, signal: keyof import("node:child_process").ChildProcessEventMap) => void|Promise<void>,
|
|
44
|
-
* }} EventHandler
|
|
45
|
-
*/
|
|
46
|
-
/**
|
|
47
|
-
* @template {EventName} E
|
|
48
|
-
* @param {E} event
|
|
49
|
-
* @param {EventHandler[E]} handler
|
|
50
|
-
*/
|
|
51
|
-
on(event, handler) {
|
|
52
|
-
this.#events.on(event, handler);
|
|
53
|
-
return this;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async stop() {
|
|
57
|
-
return new Promise((resolve) => {
|
|
58
|
-
if (!this.#processus || this.#processus.killed) {
|
|
59
|
-
this.#processus = null;
|
|
60
|
-
return resolve();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
let resolved = false;
|
|
64
|
-
|
|
65
|
-
const done = () => {
|
|
66
|
-
if (!resolved) {
|
|
67
|
-
resolved = true;
|
|
68
|
-
this.#processus = null;
|
|
69
|
-
resolve();
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
this.#processus.once("exit", done);
|
|
74
|
-
|
|
75
|
-
this.#processus.kill("SIGTERM");
|
|
76
|
-
|
|
77
|
-
setTimeout(() => {
|
|
78
|
-
if (this.#processus && !this.#processus.killed && !resolved) {
|
|
79
|
-
this.#processus.kill("SIGKILL");
|
|
80
|
-
}
|
|
81
|
-
done();
|
|
82
|
-
}, 5000);
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
};
|