@ix-xs/node-comfort 1.2.0 → 2.0.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/CHANGELOG.md +190 -0
- package/README.md +136 -549
- package/index.js +80 -35
- package/package.json +160 -33
- package/src/Arr.js +887 -419
- package/src/Async.js +585 -0
- package/src/Cache.js +329 -0
- package/src/Checker.js +971 -539
- package/src/Cli.js +1034 -0
- package/src/Color.js +415 -0
- package/src/Crypto.js +707 -0
- package/src/Emitter.js +289 -0
- package/src/Env.js +555 -0
- package/src/FS.js +1483 -894
- package/src/Func.js +670 -353
- package/src/Http.js +538 -0
- package/src/Id.js +384 -201
- package/src/Logger.js +739 -378
- package/src/Num.js +844 -361
- package/src/Obj.js +855 -372
- package/src/SQLite.js +1308 -1005
- package/src/Schema.js +1586 -0
- package/src/Str.js +983 -436
- package/src/Sys.js +511 -0
- package/src/Time.js +1260 -422
- package/src/Utils.js +321 -325
- package/src/errors.js +292 -0
- package/types/index.d.ts +693 -526
- package/types/src/Arr.d.ts +455 -145
- package/types/src/Arr.d.ts.map +1 -1
- package/types/src/Async.d.ts +321 -0
- package/types/src/Async.d.ts.map +1 -0
- package/types/src/Cache.d.ts +195 -0
- package/types/src/Cache.d.ts.map +1 -0
- package/types/src/Checker.d.ts +501 -235
- package/types/src/Checker.d.ts.map +1 -1
- package/types/src/Cli.d.ts +582 -0
- package/types/src/Cli.d.ts.map +1 -0
- package/types/src/Color.d.ts +294 -0
- package/types/src/Color.d.ts.map +1 -0
- package/types/src/Crypto.d.ts +534 -0
- package/types/src/Crypto.d.ts.map +1 -0
- package/types/src/Emitter.d.ts +170 -0
- package/types/src/Emitter.d.ts.map +1 -0
- package/types/src/Env.d.ts +382 -0
- package/types/src/Env.d.ts.map +1 -0
- package/types/src/FS.d.ts +686 -238
- package/types/src/FS.d.ts.map +1 -1
- package/types/src/Func.d.ts +380 -135
- package/types/src/Func.d.ts.map +1 -1
- package/types/src/Http.d.ts +374 -0
- package/types/src/Http.d.ts.map +1 -0
- package/types/src/Id.d.ts +149 -68
- package/types/src/Id.d.ts.map +1 -1
- package/types/src/Logger.d.ts +363 -46
- package/types/src/Logger.d.ts.map +1 -1
- package/types/src/Num.d.ts +473 -137
- package/types/src/Num.d.ts.map +1 -1
- package/types/src/Obj.d.ts +390 -104
- package/types/src/Obj.d.ts.map +1 -1
- package/types/src/SQLite.d.ts +646 -309
- package/types/src/SQLite.d.ts.map +1 -1
- package/types/src/Schema.d.ts +940 -0
- package/types/src/Schema.d.ts.map +1 -0
- package/types/src/Str.d.ts +569 -166
- package/types/src/Str.d.ts.map +1 -1
- package/types/src/Sys.d.ts +269 -0
- package/types/src/Sys.d.ts.map +1 -0
- package/types/src/Time.d.ts +663 -204
- package/types/src/Time.d.ts.map +1 -1
- package/types/src/Utils.d.ts +154 -102
- package/types/src/Utils.d.ts.map +1 -1
- package/types/src/errors.d.ts +292 -0
- package/types/src/errors.d.ts.map +1 -0
- package/types/index.d.ts.map +0 -1
package/src/Utils.js
CHANGED
|
@@ -1,325 +1,321 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
JSONParse(value) {
|
|
323
|
-
return JSON.parse(value);
|
|
324
|
-
},
|
|
325
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Everyday helpers: `wait`, `when`, `dontCrash`, and JSON functions that
|
|
5
|
+
* don't throw. They are also available at the top level (`nc.wait()`).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* await nc.wait("1.5s");
|
|
9
|
+
* const settings = nc.JSONParse(text, {});
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { AbortError } = require("./errors.js");
|
|
13
|
+
const { types } = require("node:util");
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Options for `wait()`.
|
|
17
|
+
* @typedef {object} WaitOptions
|
|
18
|
+
* @property {AbortSignal} [signal] Cancels the wait; the promise rejects with an `AbortError`.
|
|
19
|
+
* @property {boolean} [unref] Let the process exit even if the timer is still pending.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Options for `when()`.
|
|
24
|
+
* @typedef {object} WhenOptions
|
|
25
|
+
* @property {number} [interval] Milliseconds between two checks. Defaults to `50`.
|
|
26
|
+
* @property {number | null} [timeout] Give up after this many milliseconds and emit `"timeout"`.
|
|
27
|
+
* @property {number | null} [max] Stop after this many triggers.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The task returned by `when()`. Every method returns the task, so calls chain.
|
|
32
|
+
* @typedef {object} WhenTask
|
|
33
|
+
* @property {() => WhenTask} start Starts checking. A stopped task can't be restarted.
|
|
34
|
+
* @property {() => WhenTask} stop Stops checking and clears the timers.
|
|
35
|
+
* @property {<E extends "trigger" | "error" | "timeout">(event: E, handler: E extends "error" ? (error: unknown) => void : (payload: any) => void) => WhenTask} on Listens to `"trigger"` (the condition is true), `"error"` (it threw) or `"timeout"`.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Events you can customize on `dontCrash()`.
|
|
40
|
+
* @typedef {"error" | "exit" | "sig" | "beforeExit"} DontCrashEvent
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Returned by `dontCrash()`.
|
|
45
|
+
* @typedef {object} DontCrashController
|
|
46
|
+
* @property {<E extends DontCrashEvent>(event: E, handler?: E extends "error" ? (error: unknown) => void : E extends "sig" ? (signal: NodeJS.Signals) => void : (code: number) => void) => DontCrashController} on Replaces the handler for an event. Call it without a handler to restore the default one.
|
|
47
|
+
* @property {() => void} dispose Removes everything `dontCrash()` installed.
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Resolves after a delay, given in milliseconds or as a duration string.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* await nc.wait(500);
|
|
55
|
+
* await nc.wait("2s");
|
|
56
|
+
* await nc.wait("1m", { signal: controller.signal });
|
|
57
|
+
*
|
|
58
|
+
* @param {number | string} duration Milliseconds, or a string like `"250ms"`, `"2s"`, `"1m30s"`.
|
|
59
|
+
* @param {WaitOptions} [options]
|
|
60
|
+
* @returns {Promise<void>}
|
|
61
|
+
* @throws {AbortError} If the signal is aborted.
|
|
62
|
+
* @throws {TypeError} If the duration can't be parsed.
|
|
63
|
+
*/
|
|
64
|
+
function wait(duration, options = {}) {
|
|
65
|
+
const ms = typeof duration === "number" ? duration : require("./Time.js").parseDuration(duration);
|
|
66
|
+
if (ms === null || !Number.isFinite(ms)) return Promise.reject(new TypeError(`Invalid duration: ${String(duration)}`));
|
|
67
|
+
const { signal } = options;
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
if (signal?.aborted) return reject(new AbortError(undefined, { cause: signal.reason }));
|
|
70
|
+
const onAbort = () => {
|
|
71
|
+
clearTimeout(timer);
|
|
72
|
+
reject(new AbortError(undefined, { cause: signal?.reason }));
|
|
73
|
+
};
|
|
74
|
+
const timer = setTimeout(() => {
|
|
75
|
+
signal?.removeEventListener("abort", onAbort);
|
|
76
|
+
resolve();
|
|
77
|
+
}, Math.max(0, ms));
|
|
78
|
+
if (options.unref) timer.unref();
|
|
79
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Checks a condition at a regular interval and tells you when it's true.
|
|
85
|
+
* Call `.start()` once your listeners are in place. If you just want to
|
|
86
|
+
* await a condition, `nc.async.poll()` is simpler.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* nc.when(() => queue.length > 0, { reason: "jobs waiting" }, { interval: 500, max: 1, timeout: 10_000 })
|
|
90
|
+
* .on("trigger", (payload) => nc.info(payload.reason))
|
|
91
|
+
* .on("timeout", () => nc.warn("Nothing happened in 10s"))
|
|
92
|
+
* .start();
|
|
93
|
+
*
|
|
94
|
+
* @param {boolean | PromiseLike<boolean> | (() => unknown)} predicate The condition, or a function (sync or async) that returns it.
|
|
95
|
+
* @param {any} [payload={}] Passed to the `"trigger"` and `"timeout"` listeners.
|
|
96
|
+
* @param {WhenOptions} [options]
|
|
97
|
+
* @returns {WhenTask}
|
|
98
|
+
*/
|
|
99
|
+
function when(predicate, payload = {}, options = {}) {
|
|
100
|
+
const interval = options.interval ?? 50;
|
|
101
|
+
const timeout = options.timeout ?? null;
|
|
102
|
+
const max = options.max ?? null;
|
|
103
|
+
/** @type {Record<"trigger" | "error" | "timeout", Set<Function>>} */
|
|
104
|
+
const listeners = { trigger: new Set(), error: new Set(), timeout: new Set() };
|
|
105
|
+
/** @type {NodeJS.Timeout | undefined} */
|
|
106
|
+
let timer;
|
|
107
|
+
/** @type {NodeJS.Timeout | undefined} */
|
|
108
|
+
let timeoutTimer;
|
|
109
|
+
let stopped = false;
|
|
110
|
+
let started = false;
|
|
111
|
+
let count = 0;
|
|
112
|
+
|
|
113
|
+
const emit = (/** @type {"trigger" | "error" | "timeout"} */ event, /** @type {unknown} */ value) => {
|
|
114
|
+
for (const fn of listeners[event]) {
|
|
115
|
+
try {
|
|
116
|
+
fn(value);
|
|
117
|
+
} catch {
|
|
118
|
+
// A failing listener must not stop the task.
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const halt = () => {
|
|
123
|
+
stopped = true;
|
|
124
|
+
clearTimeout(timer);
|
|
125
|
+
clearTimeout(timeoutTimer);
|
|
126
|
+
};
|
|
127
|
+
const tick = async () => {
|
|
128
|
+
if (stopped) return;
|
|
129
|
+
try {
|
|
130
|
+
const ok = typeof predicate === "function" ? await predicate() : await predicate;
|
|
131
|
+
if (stopped) return;
|
|
132
|
+
if (ok) {
|
|
133
|
+
count++;
|
|
134
|
+
emit("trigger", payload);
|
|
135
|
+
if (max !== null && count >= max) return halt();
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
emit("error", error);
|
|
139
|
+
}
|
|
140
|
+
if (!stopped) timer = setTimeout(tick, interval);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/** @type {WhenTask} */
|
|
144
|
+
const task = {
|
|
145
|
+
start() {
|
|
146
|
+
if (stopped || started) return task;
|
|
147
|
+
started = true;
|
|
148
|
+
timer = setTimeout(tick, 0);
|
|
149
|
+
if (timeout !== null) {
|
|
150
|
+
timeoutTimer = setTimeout(() => {
|
|
151
|
+
if (stopped) return;
|
|
152
|
+
emit("timeout", payload);
|
|
153
|
+
halt();
|
|
154
|
+
}, timeout);
|
|
155
|
+
}
|
|
156
|
+
return task;
|
|
157
|
+
},
|
|
158
|
+
stop() {
|
|
159
|
+
halt();
|
|
160
|
+
return task;
|
|
161
|
+
},
|
|
162
|
+
on(event, handler) {
|
|
163
|
+
listeners[event]?.add(handler);
|
|
164
|
+
return task;
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
return task;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Keeps the process alive when something goes wrong. Uncaught exceptions and
|
|
172
|
+
* unhandled rejections are logged instead of crashing, and `SIGINT`/`SIGTERM`
|
|
173
|
+
* are logged before a clean exit. Customize any of it with `.on()`.
|
|
174
|
+
*
|
|
175
|
+
* Only its own handlers are ever removed; listeners added by your code or by
|
|
176
|
+
* other libraries are left alone.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* nc.dontCrash()
|
|
180
|
+
* .on("error", (error) => sentry.captureException(error))
|
|
181
|
+
* .on("sig", async () => {
|
|
182
|
+
* await server.close();
|
|
183
|
+
* process.exit(0);
|
|
184
|
+
* });
|
|
185
|
+
*
|
|
186
|
+
* @returns {DontCrashController}
|
|
187
|
+
*/
|
|
188
|
+
function dontCrash() {
|
|
189
|
+
_dontCrashDispose?.();
|
|
190
|
+
const logger = require("./Logger.js");
|
|
191
|
+
/** @type {Array<[string, (...args: any[]) => void]>} */
|
|
192
|
+
let installed = [];
|
|
193
|
+
|
|
194
|
+
const defaults = {
|
|
195
|
+
error: (/** @type {unknown} */ error) => logger.error("[@ix-xs/node-comfort] Uncaught error:", error),
|
|
196
|
+
exit: (/** @type {number} */ code) => logger.log(`<% gray [@ix-xs/node-comfort] Process exit with code ${code} %>`),
|
|
197
|
+
sig: (/** @type {NodeJS.Signals} */ signal) => {
|
|
198
|
+
logger.warn(`[@ix-xs/node-comfort] Received ${signal}, exiting`);
|
|
199
|
+
process.exit(0);
|
|
200
|
+
},
|
|
201
|
+
beforeExit: () => {},
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* @param {string} event
|
|
206
|
+
* @param {(...args: any[]) => void} handler
|
|
207
|
+
*/
|
|
208
|
+
const listen = (event, handler) => {
|
|
209
|
+
try {
|
|
210
|
+
process.on(/** @type {any} */ (event), handler);
|
|
211
|
+
installed.push([event, handler]);
|
|
212
|
+
} catch {
|
|
213
|
+
// Signal not supported on this platform (for example SIGQUIT on Windows).
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
/** @param {string[]} events */
|
|
217
|
+
const unlisten = (events) => {
|
|
218
|
+
installed = installed.filter(([event, handler]) => {
|
|
219
|
+
if (!events.includes(event)) return true;
|
|
220
|
+
process.off(/** @type {any} */ (event), handler);
|
|
221
|
+
return false;
|
|
222
|
+
});
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
/** @type {DontCrashController} */
|
|
226
|
+
const controller = {
|
|
227
|
+
on(event, handler) {
|
|
228
|
+
if (event === "error") {
|
|
229
|
+
unlisten(["uncaughtException", "unhandledRejection"]);
|
|
230
|
+
const fn = /** @type {(error: unknown) => void} */ (handler ?? defaults.error);
|
|
231
|
+
listen("uncaughtException", fn);
|
|
232
|
+
listen("unhandledRejection", fn);
|
|
233
|
+
} else if (event === "exit") {
|
|
234
|
+
unlisten(["exit"]);
|
|
235
|
+
listen("exit", /** @type {(code: number) => void} */ (handler ?? defaults.exit));
|
|
236
|
+
} else if (event === "sig") {
|
|
237
|
+
const signals = ["SIGINT", "SIGTERM", "SIGQUIT"];
|
|
238
|
+
unlisten(signals);
|
|
239
|
+
for (const signal of signals) listen(signal, /** @type {(signal: NodeJS.Signals) => void} */ (handler ?? defaults.sig));
|
|
240
|
+
} else if (event === "beforeExit") {
|
|
241
|
+
unlisten(["beforeExit"]);
|
|
242
|
+
listen("beforeExit", /** @type {(code: number) => void} */ (handler ?? defaults.beforeExit));
|
|
243
|
+
}
|
|
244
|
+
return controller;
|
|
245
|
+
},
|
|
246
|
+
dispose() {
|
|
247
|
+
unlisten(installed.map(([event]) => event));
|
|
248
|
+
if (_dontCrashDispose === controller.dispose) _dontCrashDispose = undefined;
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
_dontCrashDispose = controller.dispose;
|
|
252
|
+
controller.on("error").on("exit").on("sig");
|
|
253
|
+
return controller;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** @type {(() => void) | undefined} */
|
|
257
|
+
let _dontCrashDispose;
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* `JSON.stringify` that never throws. Circular references become
|
|
261
|
+
* `"[Circular]"`, bigints become strings, Maps become objects and Sets become
|
|
262
|
+
* arrays.
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* nc.JSONString({ a: 1 }); // '{\n "a": 1\n}'
|
|
266
|
+
* nc.JSONString({ id: 10n, tags: new Set(["x"]) }, 0); // '{"id":"10","tags":["x"]}'
|
|
267
|
+
*
|
|
268
|
+
* @param {unknown} value
|
|
269
|
+
* @param {number} [spaces=4] Indentation. Use `0` for a single line.
|
|
270
|
+
* @returns {string} The JSON text, or `"undefined"` for values JSON can't represent.
|
|
271
|
+
*/
|
|
272
|
+
function JSONString(value, spaces = 4) {
|
|
273
|
+
/** @type {Array<[holder: object, original: object]>} ancestors of the value being serialized */
|
|
274
|
+
const stack = [];
|
|
275
|
+
const text = JSON.stringify(
|
|
276
|
+
value,
|
|
277
|
+
/** @this {any} */ function(_, raw) {
|
|
278
|
+
if (typeof raw === "bigint") return raw.toString();
|
|
279
|
+
if (raw === null || typeof raw !== "object") return raw;
|
|
280
|
+
while (stack.length && stack[stack.length - 1][0] !== this) stack.pop();
|
|
281
|
+
if (stack.some(([, original]) => original === raw)) return "[Circular]";
|
|
282
|
+
const v = types.isMap(raw) ? Object.fromEntries(raw) : types.isSet(raw) ? [...raw] : raw;
|
|
283
|
+
stack.push([v, raw]);
|
|
284
|
+
return v;
|
|
285
|
+
},
|
|
286
|
+
spaces,
|
|
287
|
+
);
|
|
288
|
+
return text ?? String(text);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* `JSON.parse` with an optional fallback. With a fallback, invalid input
|
|
293
|
+
* returns it instead of throwing.
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* nc.JSONParse('{"a":1}'); // { a: 1 }
|
|
297
|
+
* nc.JSONParse("oops", null); // null
|
|
298
|
+
* nc.JSONParse("oops"); // throws SyntaxError
|
|
299
|
+
*
|
|
300
|
+
* @template [T=any]
|
|
301
|
+
* @param {string} text
|
|
302
|
+
* @param {T} [fallback] Returned when `text` isn't valid JSON.
|
|
303
|
+
* @returns {T}
|
|
304
|
+
* @throws {SyntaxError} If `text` is invalid and there's no fallback.
|
|
305
|
+
*/
|
|
306
|
+
function JSONParse(text, fallback) {
|
|
307
|
+
if (arguments.length < 2) return JSON.parse(text);
|
|
308
|
+
try {
|
|
309
|
+
return JSON.parse(text);
|
|
310
|
+
} catch {
|
|
311
|
+
return /** @type {T} */ (fallback);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
module.exports = {
|
|
316
|
+
wait,
|
|
317
|
+
when,
|
|
318
|
+
dontCrash,
|
|
319
|
+
JSONString,
|
|
320
|
+
JSONParse,
|
|
321
|
+
};
|