@ix-xs/node-comfort 1.0.3 → 1.0.5

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.
Files changed (41) hide show
  1. package/index.js +12 -14
  2. package/package.json +17 -17
  3. package/src/checker.js +76 -0
  4. package/src/cli.js +86 -0
  5. package/{core/FS.js → src/fs.js} +121 -230
  6. package/src/localStorage.js +242 -0
  7. package/{core/Logger.js → src/logger.js} +23 -97
  8. package/src/utils.js +225 -0
  9. package/tsconfig.json +1 -1
  10. package/types/index.d.ts +73 -50
  11. package/types/src/checker.d.ts +49 -0
  12. package/types/src/checker.d.ts.map +1 -0
  13. package/types/src/cli.d.ts +26 -0
  14. package/types/src/cli.d.ts.map +1 -0
  15. package/types/src/fs.d.ts +210 -0
  16. package/types/src/fs.d.ts.map +1 -0
  17. package/types/src/localStorage.d.ts +97 -0
  18. package/types/src/localStorage.d.ts.map +1 -0
  19. package/types/src/logger.d.ts +132 -0
  20. package/types/src/logger.d.ts.map +1 -0
  21. package/types/src/utils.d.ts +92 -0
  22. package/types/src/utils.d.ts.map +1 -0
  23. package/core/CLI.js +0 -95
  24. package/core/Checker.js +0 -127
  25. package/core/Stepper.js +0 -115
  26. package/core/Storage.js +0 -348
  27. package/core/Utils.js +0 -206
  28. package/types/core/CLI.d.ts +0 -25
  29. package/types/core/CLI.d.ts.map +0 -1
  30. package/types/core/Checker.d.ts +0 -73
  31. package/types/core/Checker.d.ts.map +0 -1
  32. package/types/core/FS.d.ts +0 -211
  33. package/types/core/FS.d.ts.map +0 -1
  34. package/types/core/Logger.d.ts +0 -253
  35. package/types/core/Logger.d.ts.map +0 -1
  36. package/types/core/Stepper.d.ts +0 -49
  37. package/types/core/Stepper.d.ts.map +0 -1
  38. package/types/core/Storage.d.ts +0 -111
  39. package/types/core/Storage.d.ts.map +0 -1
  40. package/types/core/Utils.d.ts +0 -55
  41. package/types/core/Utils.d.ts.map +0 -1
@@ -0,0 +1,242 @@
1
+ const fs = require("./fs");
2
+ const checker = require("./checker");
3
+
4
+ const originalEmitWarning = process.emitWarning;
5
+ process.emitWarning = (warning, ...args) => {
6
+ if ((warning.name === "ExperimentalWarning" && warning.message.includes("SQLite")) ||
7
+ (typeof warning === "string" && warning.startsWith("SQLite is an experimental feature"))) {
8
+ return;
9
+ }
10
+ return originalEmitWarning.call(process, warning, ...args);
11
+ };
12
+
13
+ const { DatabaseSync } = require("node:sqlite");
14
+
15
+ module.exports = class localStorage {
16
+ #$;
17
+ #models = new Map();
18
+
19
+ constructor(path = fs.createPath("localStorage/localStorage.sqlite")) {
20
+ if (path.includes("/")) {
21
+ const split = path.split("/");
22
+ const file = split[split.length - 1];
23
+ const folder = fs.createPath(path.replace(file, ""));
24
+ fs.createFolder(folder);
25
+ }
26
+
27
+ this.#$ = new DatabaseSync(path);
28
+ this.#exec("PRAGMA journal_mode = WAL");
29
+ this.#exec("PRAGMA foreign_keys = ON");
30
+ }
31
+
32
+ #exec(sql, options = []) {
33
+ try {
34
+ const stmt = this.#$.prepare(sql);
35
+ const result = stmt.run(...checker.isArray(options) ? options : [options]);
36
+ return {
37
+ changes: result.changes,
38
+ lastInsertRowid: result.lastInsertRowid,
39
+ };
40
+ } catch (error) {
41
+ throw error;
42
+ }
43
+ }
44
+ #buildCreateSQL(tableName, options) {
45
+ const columns = Object.entries(options).map(([col, def]) => {
46
+ let colDef = `${col} ${def.type}`;
47
+
48
+ if (def.primaryKey) {
49
+ colDef += " PRIMARY KEY";
50
+ if (def.autoincrement) colDef += " AUTOINCREMENT";
51
+ }
52
+ if (def.notNull) colDef += " NOT NULL";
53
+ if (def.unique) colDef += " UNIQUE";
54
+ if (def.defaultValue !== undefined) colDef += ` DEFAULT ${JSON.stringify(def.defaultValue)}`;
55
+ if (def.values?.length) {
56
+ colDef += ` CHECK(${col} IN (${def.values.map(v => JSON.stringify(v)).join(", ")}))`;
57
+ }
58
+
59
+ return colDef;
60
+ }).join(", ");
61
+
62
+ return `CREATE TABLE IF NOT EXISTS ${tableName} (${columns})`;
63
+ }
64
+ #defineModel(tableName, options) {
65
+ if (this.#models.has(tableName)) return;
66
+
67
+ const createSQL = this.#buildCreateSQL(tableName, options);
68
+ this.#exec(createSQL);
69
+ this.#models.set(tableName, { name: tableName, options });
70
+ }
71
+ #buildWhere(where) {
72
+ if (Object.keys(where).length === 0) {
73
+ return { where: "", params: [] };
74
+ }
75
+
76
+ const conditions = [];
77
+ const params = [];
78
+
79
+ for (const [key, value] of Object.entries(where)) {
80
+ conditions.push(`${key} = ?`);
81
+ params.push(value);
82
+ }
83
+
84
+ return {
85
+ where: `WHERE ${conditions.join(" AND ")}`,
86
+ params,
87
+ };
88
+ }
89
+ #getOrderBy(tableName) {
90
+ const model = this.#models.get(tableName);
91
+ if (!model) return "id DESC";
92
+ const firstCol = Object.keys(model.options)[0];
93
+ return `${firstCol} DESC`;
94
+ }
95
+
96
+ /**
97
+ * @param {{name: string, options: Record<string, {
98
+ * type: "TEXT"|"INTEGER"|"REAL"|"BLOB"|"NUMERIC"|"DATE"|"DATETIME"|"TIMESTAMP"|"TIME"|"VARCHAR"|"CHAR"|"CLOB",
99
+ * primaryKey?: boolean,
100
+ * unique?: boolean,
101
+ * notNull: boolean,
102
+ * defaultValue?: any,
103
+ * values?: Array<string>
104
+ * autoincrement?: boolean,
105
+ * }>}} table
106
+ */
107
+ async createTable(table) {
108
+ try {
109
+ this.#defineModel(table.name, table.options);
110
+ return { ok: true };
111
+ } catch (error) {
112
+ return { error: error.message };
113
+ }
114
+ }
115
+ /**
116
+ * @param {string} tableName
117
+ */
118
+ async deleteTable(tableName) {
119
+ try {
120
+ if (!this.#models.has(tableName)) return { error: `Table ${tableName} doesn't exist` };
121
+
122
+ this.#exec(`DROP TABLE IF EXISTS ${tableName}`);
123
+ this.#models.delete(tableName);
124
+ return { ok: true };
125
+ } catch (error) {
126
+ return { error: error.message };
127
+ }
128
+ }
129
+ /**
130
+ * @param {string} tableName
131
+ */
132
+ async clearTable(tableName) {
133
+ try {
134
+ if (!this.#models.has(tableName)) return { error: `Table ${tableName} doesn't exist` };
135
+
136
+ this.#exec(`DELETE FROM ${tableName}`);
137
+ return { ok: true };
138
+ } catch (error) {
139
+ return { error: error.message };
140
+ }
141
+ }
142
+ /**
143
+ * @param {string} tableName
144
+ * @param {object} [where]
145
+ */
146
+ async get(tableName, where) {
147
+ try {
148
+ if (!this.#models.has(tableName)) return undefined;
149
+
150
+ const whereClause = this.#buildWhere(where);
151
+ const sql = `SELECT * FROM ${tableName} ${whereClause.where} LIMIT 1`;
152
+ const stmt = this.#$.prepare(sql);
153
+ const record = stmt.get(whereClause.params);
154
+ return record || undefined;
155
+ } catch (error) {
156
+ return { error: error.message };
157
+ }
158
+ }
159
+ /**
160
+ * @param {string} tableName
161
+ * @param {object} [where]
162
+ * @param {number} [limit]
163
+ * @param {number} [offset]
164
+ */
165
+ async getAll(tableName, where = {}, limit = 50, offset = 0) {
166
+ try {
167
+ if (!this.#models.has(tableName)) return [];
168
+
169
+ const whereClause = this.#buildWhere(where);
170
+ const sql = `SELECT * FROM ${tableName} ${whereClause.where} ORDER BY ${this.#getOrderBy(tableName)} LIMIT ? OFFSET ?`;
171
+ const stmt = this.#$.prepare(sql);
172
+ const records = stmt.all(...whereClause.params, limit, offset);
173
+ return records;
174
+ } catch (error) {
175
+ return { error: error.message };
176
+ }
177
+ }
178
+ /**
179
+ * @param {string} tableName
180
+ * @param {*} data
181
+ * @param {object} [where]
182
+ */
183
+ async set(tableName, data, where = null) {
184
+ try {
185
+ if (!this.#models.has(tableName)) return { error: `Table ${tableName} doesn't exist` };
186
+
187
+ const lookup = where ?? (data.id ? { id: data.id } : null);
188
+
189
+ if (lookup) {
190
+ const columns = Object.keys(data).join(", ");
191
+ const placeholders = Object.keys(data).map(() => "?").join(", ");
192
+ const sql = `INSERT OR REPLACE INTO ${tableName} (${columns}) VALUES (${placeholders})`;
193
+ const stmt = this.#$.prepare(sql);
194
+ stmt.run(...Object.values(data));
195
+ return { ok: true };
196
+ } else {
197
+ const columns = Object.keys(data).join(", ");
198
+ const placeholders = Object.keys(data).map(() => "?").join(", ");
199
+ const sql = `INSERT INTO ${tableName} (${columns}) VALUES (${placeholders})`;
200
+ const stmt = this.#$.prepare(sql);
201
+ stmt.run(...Object.values(data));
202
+ return { ok: true };
203
+ }
204
+ } catch (error) {
205
+ return { error: error.message };
206
+ }
207
+ }
208
+ /**
209
+ * @param {string} tableName
210
+ * @param {object} where
211
+ */
212
+ async delete(tableName, where) {
213
+ try {
214
+ if (!this.#models.has(tableName)) return { error: `Table ${tableName} doesn't exist` };
215
+
216
+ const whereClause = this.#buildWhere(where);
217
+ const sql = `DELETE FROM ${tableName} ${whereClause.where}`;
218
+ const stmt = this.#$.prepare(sql);
219
+ const result = stmt.run(...whereClause.params);
220
+ return { ok: true, changes: result.changes };
221
+ } catch (error) {
222
+ return { error: error.message };
223
+ }
224
+ }
225
+ /**
226
+ * @param {string} tableName
227
+ * @param {object} where
228
+ */
229
+ async count(tableName, where = {}) {
230
+ try {
231
+ if (!this.#models.has(tableName)) return { error: `Table ${tableName} doesn't exist` };
232
+
233
+ const whereClause = this.#buildWhere(where);
234
+ const sql = `SELECT COUNT(*) as total FROM ${tableName} ${whereClause.where}`;
235
+ const stmt = this.#$.prepare(sql);
236
+ const result = stmt.get(...whereClause.params);
237
+ return result.total;
238
+ } catch (error) {
239
+ return { error: error.message };
240
+ }
241
+ }
242
+ };
@@ -56,11 +56,9 @@ const _options = {
56
56
  const _groups = [];
57
57
 
58
58
  /**
59
- * Gets ANSI escape code for style/color.
60
- * @private
61
- * @param {string} name - Style or color name.
62
- * @returns {number|null} ANSI code or null.
63
- */
59
+ * @param {string} name
60
+ * @returns {number|null}
61
+ */
64
62
  const _getCode = (name) => {
65
63
  if (_style[name] !== undefined) return _style[name];
66
64
  if (_color.fg[name] !== undefined) return _color.fg[name];
@@ -70,12 +68,9 @@ const _getCode = (name) => {
70
68
  }
71
69
  return null;
72
70
  };
73
-
74
- /**
75
- * Parses template tokens into styles and text.
76
- * @private
77
- * @param {string} input - Template string.
78
- * @returns {{codes: number[], text: string}} Parsed result.
71
+ /**
72
+ * @param {string} input
73
+ * @returns {{codes: number[], text: string}}
79
74
  */
80
75
  const _parse = (input) => {
81
76
  const tokens = input.trim().split(/\s+/);
@@ -108,10 +103,7 @@ const _parse = (input) => {
108
103
  .trimStart();
109
104
  return { codes, text };
110
105
  };
111
-
112
- /**
113
- * Builds ANSI escape sequence from codes.
114
- * @private
106
+ /**
115
107
  * @param {number[]} codes - ANSI codes.
116
108
  * @returns {string} Escape sequence.
117
109
  */
@@ -119,7 +111,6 @@ const _build = (codes) => {
119
111
  if (codes.length === 0) return "";
120
112
  return `${_x}[${codes.join(";")}m`;
121
113
  };
122
-
123
114
  const _apply = (str) => {
124
115
  const esc_start = _options.delimiters.start.replace(
125
116
  /[.*+?^${}()|[\]\\]/g,
@@ -148,9 +139,7 @@ const _apply = (str) => {
148
139
  },
149
140
  );
150
141
  };
151
-
152
- /**
153
- * @private
142
+ /**
154
143
  * @returns {string}
155
144
  */
156
145
  const _timestamp = () => {
@@ -159,54 +148,17 @@ const _timestamp = () => {
159
148
  `<%gray [${new Date().toLocaleTimeString(undefined, { hour12: false })}]%>`,
160
149
  );
161
150
  };
162
-
163
- /**
164
- * Generates indentation based on group depth.
165
- * @private
166
- * @returns {string} Indentation spaces.
151
+ /**
152
+ * @returns {string}
167
153
  */
168
154
  const _indent = () => {
169
155
  return " ".repeat(0 + _groups.length);
170
156
  };
171
157
 
172
- /**
173
- * @module logger
174
- * @description
175
- * Beautiful ANSI-colored console logger with grouping, timestamps, and template syntax.
176
- *
177
- * Supports 16+ colors, styles (bold, italic, underline), background colors,
178
- * nested groups with indentation, and customizable delimiters.
179
- *
180
- * ## Syntax
181
- * ```
182
- * <%red bold This is red and bold%>
183
- * <%bgBlue whiteBright ERROR%>
184
- * <%yellowBright [WARN]%>
185
- * ```
186
- *
187
- * Available styles: `normal`, `bold`, `italic`, `underline`, `overline`
188
- * Colors: `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`, `gray`
189
- * Bright colors: `redBright`, `greenBright`, `yellowBright`, `blueBright`, `magentaBright`, `cyanBright`, `whiteBright`
190
- * Backgrounds: `bgBlack`, `bgRed`, `bgGreen`, etc.
191
- *
192
- * @example
193
- * const nodeComfort = require("@ix-xs/node-comfort");
194
- *
195
- * nodeComfort.log("<%greenBright ✓ Success%> Operation completed!");
196
- * nodeComfort.group("<%blue Processing files%>").log("File 1").groupEnd();
197
- * const log = nodeComfort.logify({ users: 42 }); // → ANSI string
198
- */
199
158
  module.exports = {
200
159
  /**
201
- * Logs content with colors, timestamp, and group indentation.
202
- * Supports strings, objects (pretty JSON), and chaining.
203
- *
204
- * @param {any} content - Content to log (string, object, or primitive).
205
- * @example
206
- * nodeComfort.log("<%greenBright ✓ Done%>");
207
- * nodeComfort.log({ users: 42, active: true });
208
- * nodeComfort.group("Files").log("Processing...").groupEnd();
209
- */
160
+ * @param {*} content
161
+ */
210
162
  log(content) {
211
163
  let str;
212
164
  if (typeof content === "string") {
@@ -239,16 +191,10 @@ module.exports = {
239
191
 
240
192
  return this;
241
193
  },
242
-
243
194
  /**
244
- * Formats content with colors, timestamp, and group indentation (no console output).
245
- *
246
- * @param {any} content - Content to format.
247
- * @returns {string} Formatted string.
248
- * @example
249
- * const msg = nodeComfort.logify("<%redBright ERROR%> Invalid input");
250
- * console.log(msg);
251
- */
195
+ * @param {any} content - Content to format.
196
+ * @returns {string} Formatted string.
197
+ */
252
198
  logify(content) {
253
199
  let str;
254
200
  if (typeof content === "string") {
@@ -262,46 +208,26 @@ module.exports = {
262
208
  const prefix = `${_timestamp()}${_indent()}`;
263
209
  return _apply(prefix ? `${prefix} ${str}` : str);
264
210
  },
265
-
266
211
  /**
267
- * Starts a new log group with ▼ indicator and increased indentation.
268
- *
269
- * @param {string} label - Group label (supports colors).
270
- * @example
271
- * nodeComfort.group("<%yellow Processing%>").log("Step 1").groupEnd();
272
- */
212
+ * @param {string} label
213
+ */
273
214
  group(label) {
274
215
  _groups.push(label);
275
216
  const prefix = `${_timestamp()}${_indent()}`;
276
217
  console.log(prefix ? `${prefix}▼ ${label}` : `▼ ${label}`);
277
218
  return this;
278
219
  },
279
-
280
- /**
281
- * Ends current group and reduces indentation.
282
- *
283
- * @example
284
- * nodeComfort.group("Work").groupEnd();
285
- */
286
220
  groupEnd() {
287
221
  if (_groups.length > 0) _groups.pop();
288
222
  return this;
289
223
  },
290
-
291
224
  /**
292
- * Configures logger options (delimiters and timestamp).
293
- *
294
- * @param {object} [options] - Logger options.
295
- * @param {object} [options.delimiters] - Template delimiters.
296
- * @param {string} [options.delimiters.start="<%="] - Start delimiter.
297
- * @param {string} [options.delimiters.end="%>"] - End delimiter.
298
- * @param {boolean} [options.timestamp=true] - Show timestamps.
299
- * @example
300
- * nodeComfort.setLogger({
301
- * delimiters: { start: "{{", end: "}}" },
302
- * timestamp: false
303
- * }).log("{{ bgGreen new delimiters }}");
304
- */
225
+ * @param {object} [options]
226
+ * @param {object} [options.delimiters]
227
+ * @param {string} [options.delimiters.start="<%="]
228
+ * @param {string} [options.delimiters.end="%>"]
229
+ * @param {boolean} [options.timestamp=true]
230
+ */
305
231
  setLogger(options = _options) {
306
232
  options = {
307
233
  delimiters: {
package/src/utils.js ADDED
@@ -0,0 +1,225 @@
1
+ const logger = require("./logger");
2
+
3
+ module.exports = {
4
+ /**
5
+ * @param {number} ms
6
+ */
7
+ async wait(ms) {
8
+ return new Promise((resolve) => setTimeout(resolve, ms));
9
+ },
10
+ /**
11
+ * Polls a predicate at a given interval and emits events when it matches or times out.
12
+ *
13
+ * The predicate is evaluated every `options.interval` milliseconds (default: 50 ms).
14
+ * Be careful with the default interval: 50 ms is very frequent polling and can be
15
+ * unnecessarily CPU‑intensive if your predicate or trigger handlers are heavy.
16
+ * When `options.max` is `null`, the predicate can match indefinitely (infinite triggers),
17
+ * so you should choose an interval that makes sense for your use‑case.
18
+ *
19
+ * @param {boolean|Promise<boolean>|(() => boolean|Promise<boolean>)} predicate
20
+ * Condition to evaluate at each tick, or a function returning it.
21
+ * @param {*} payload
22
+ * Value passed to listeners when events are emitted (e.g. an object or message).
23
+ * @param {object} [options]
24
+ * @param {number} [options.interval=50]
25
+ * Polling interval in milliseconds. Increase this value if your predicate is
26
+ * expensive or you expect many triggers without a `max` limit.
27
+ * @param {number|null} [options.timeout=null]
28
+ * Maximum time in milliseconds before emitting a "timeout" event and stopping.
29
+ * If `null`, no timeout is applied.
30
+ * @param {number|null} [options.max=null]
31
+ * Maximum number of successful predicate evaluations ("trigger" events) before
32
+ * automatically stopping. If `null`, triggers are infinite until `stop()` is called.
33
+ *
34
+ * @example
35
+ * // Basic usage: infinite triggers every 50 ms (default interval) – use with caution.
36
+ * const task = when(() => true, { message: "Ok" });
37
+ *
38
+ * task.on("trigger", (payload) => {
39
+ * console.log(payload.message);
40
+ * // task.stop(); // stop when you’re done
41
+ * });
42
+ *
43
+ * task.start();
44
+ *
45
+ * @example
46
+ * // Safer usage: limit triggers and/or lower polling frequency.
47
+ * when(() => true, { message: "Ok" }, { max: 1, interval: 500 }) // one trigger max, every 500 ms
48
+ * .on("trigger", (payload) => {
49
+ * console.log(payload.message);
50
+ * })
51
+ * .start();
52
+ */
53
+ when(predicate, payload = {}, options) {
54
+ const interval = options?.interval ?? 50;
55
+ const timeout = options?.timeout ?? null;
56
+ const max = options?.max ?? null;
57
+
58
+ const listeners = {
59
+ trigger: new Set(),
60
+ error: new Set(),
61
+ timeout: new Set(),
62
+ };
63
+
64
+ let _timer = null;
65
+ let _timeoutTimer = null;
66
+ let _stopped = false;
67
+ let _count = 0;
68
+
69
+ function emit(event, p) {
70
+ for (const fn of listeners[event]) {
71
+ try {
72
+ fn(p);
73
+ } catch (error) {
74
+ throw error;
75
+ }
76
+ }
77
+ }
78
+
79
+ async function evalPredicate() {
80
+ if (typeof predicate === "function") {
81
+ return await predicate();
82
+ }
83
+ return Boolean(predicate);
84
+ }
85
+
86
+ async function tick() {
87
+ if (_stopped) return;
88
+
89
+ try {
90
+ const ok = await evalPredicate();
91
+
92
+ if (ok) {
93
+ _count++;
94
+ emit("trigger", payload);
95
+
96
+ if (max != null && _count >= max) {
97
+ _stopInternal();
98
+ return;
99
+ }
100
+ }
101
+
102
+ if (!_stopped) {
103
+ _timer = setTimeout(tick, interval);
104
+ }
105
+ } catch (error) {
106
+ emit("error", error);
107
+ if (!_stopped) {
108
+ _timer = setTimeout(tick, interval);
109
+ }
110
+ }
111
+ }
112
+
113
+ function _stopInternal() {
114
+ _stopped = true;
115
+ if (_timer) {
116
+ clearTimeout(_timer);
117
+ _timer = null;
118
+ }
119
+ if (_timeoutTimer) {
120
+ clearTimeout(_timeoutTimer);
121
+ _timeoutTimer = null;
122
+ }
123
+ }
124
+
125
+ const _ = {
126
+ start() {
127
+ if (_stopped) return _;
128
+
129
+ if (!_timer) {
130
+ _timer = setTimeout(tick, 0);
131
+ }
132
+
133
+ if (timeout != null && !_timeoutTimer) {
134
+ _timeoutTimer = setTimeout(() => {
135
+ if (_stopped) return;
136
+ emit("timeout", payload);
137
+ _stopInternal();
138
+ }, timeout);
139
+ }
140
+
141
+ return _;
142
+ },
143
+ stop() {
144
+ _stopInternal();
145
+ return _;
146
+ },
147
+ /**
148
+ * @typedef { "error" | "trigger" | "timeout" } EventNames
149
+ * @typedef {{
150
+ * error: (error) => void|Promise<void>,
151
+ * trigger: (payload) => void|Promise<void>,
152
+ * timeout: (payload) => void|Promise<void>,
153
+ * }} EventHandlers
154
+ */
155
+ /**
156
+ * @template {EventNames} E
157
+ * @param {E} event
158
+ * @param {EventHandlers[E]} handler
159
+ */
160
+ on(event, handler) {
161
+ listeners[event]?.add(handler);
162
+ return _;
163
+ },
164
+ };
165
+
166
+ return _;
167
+ },
168
+ dontCrash() {
169
+ const defaults = {
170
+ error: (error) => {
171
+ logger.log(`<% red [@ix-xs/node-comfort] Process Error %>\n<% gray ${error.stack ?? error} %>`);
172
+ },
173
+ exit: (code) => {
174
+ logger.log(`<% yellow [@ix-xs/node-comfort] Process Exit %>\n→ Code: <% gray ${code} %>`);
175
+ },
176
+ sig: (signal) => {
177
+ logger.log(`<% yellow [@ix-xs/node-comfort] Process SIG* %>\n→ Signal: <% gray ${signal} %>`);
178
+ process.exit(0);
179
+ },
180
+ beforeExit: (code) => {},
181
+ };
182
+
183
+ const _ = {
184
+ /**
185
+ * @template { "error" | "exit" | "sig" | "beforeExit" } E
186
+ * @param { E } event
187
+ * @param { { error: (error: Error | unknown) => void | Promise<void>, exit: (code: number) => void | Promise<void>, sig: (signal: "SIGINT" | "SIGTERM" | "SIGQUIT") => void | Promise<void>, beforeExit: (code: number) => void|Promise<void> }[E] } handler
188
+ */
189
+ on(event, handler) {
190
+ if (event === "error") {
191
+ process.removeAllListeners("uncaughtException");
192
+ process.removeAllListeners("unhandledRejection");
193
+ process.on("uncaughtException", handler ?? defaults.error);
194
+ process.on("unhandledRejection", handler ?? defaults.error);
195
+ } else if (event === "exit") {
196
+ process.removeAllListeners("exit");
197
+ process.on("exit", handler ?? defaults.exit);
198
+ } else if (event === "sig") {
199
+ for (const signal of ["SIGINT", "SIGTERM", "SIGQUIT"]) {
200
+ process.removeAllListeners(signal);
201
+ process.on(signal, handler ?? defaults.sig);
202
+ }
203
+ } else if (event === "beforeExit") {
204
+ process.removeAllListeners("beforeExit");
205
+ process.on("beforeExit", handler ?? defaults.beforeExit);
206
+ }
207
+
208
+ return _;
209
+ },
210
+ };
211
+
212
+ _.on("error").on("exit").on("sig");
213
+
214
+ return _;
215
+ },
216
+ /**
217
+ * @param {*} value
218
+ */
219
+ JSONString(value) {
220
+ return JSON.stringify(value, null, 4);
221
+ },
222
+ JSONParse(value) {
223
+ return JSON.parse(value);
224
+ },
225
+ };
package/tsconfig.json CHANGED
@@ -13,6 +13,6 @@
13
13
  "esModuleInterop": true,
14
14
  "resolveJsonModule": true
15
15
  },
16
- "include": ["**/*"],
16
+ "include": ["**/*", "test.js"],
17
17
  "exclude": ["node_modules", "types"]
18
18
  }