@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/Logger.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text styles for console ANSI formatting
|
|
3
|
+
* @private
|
|
4
|
+
*/
|
|
5
|
+
const _style = {
|
|
6
|
+
normal: 0,
|
|
7
|
+
bold: 1,
|
|
8
|
+
italic: 3,
|
|
9
|
+
underline: 4,
|
|
10
|
+
overline: 9,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Text colors for console ANSI formatting
|
|
15
|
+
* @private
|
|
16
|
+
*/
|
|
17
|
+
const _color = {
|
|
18
|
+
fg: {
|
|
19
|
+
black: 30,
|
|
20
|
+
red: 31,
|
|
21
|
+
green: 32,
|
|
22
|
+
yellow: 33,
|
|
23
|
+
blue: 34,
|
|
24
|
+
magenta: 35,
|
|
25
|
+
cyan: 36,
|
|
26
|
+
white: 37,
|
|
27
|
+
gray: 90,
|
|
28
|
+
redBright: 91,
|
|
29
|
+
greenBright: 92,
|
|
30
|
+
yellowBright: 93,
|
|
31
|
+
blueBright: 94,
|
|
32
|
+
magentaBright: 95,
|
|
33
|
+
cyanBright: 96,
|
|
34
|
+
whiteBright: 97,
|
|
35
|
+
rgb: 38,
|
|
36
|
+
},
|
|
37
|
+
bg: {
|
|
38
|
+
black: 40,
|
|
39
|
+
red: 41,
|
|
40
|
+
green: 42,
|
|
41
|
+
yellow: 43,
|
|
42
|
+
blue: 44,
|
|
43
|
+
magenta: 45,
|
|
44
|
+
cyan: 46,
|
|
45
|
+
white: 47,
|
|
46
|
+
gray: 100,
|
|
47
|
+
redBright: 101,
|
|
48
|
+
greenBright: 102,
|
|
49
|
+
yellowBright: 103,
|
|
50
|
+
blueBright: 104,
|
|
51
|
+
magentaBright: 105,
|
|
52
|
+
cyanBright: 106,
|
|
53
|
+
whiteBright: 107,
|
|
54
|
+
rgb: 48,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Default logger options
|
|
60
|
+
* @private
|
|
61
|
+
*/
|
|
62
|
+
const _options = {
|
|
63
|
+
delimiter: {
|
|
64
|
+
open: "<%",
|
|
65
|
+
close: "%>",
|
|
66
|
+
},
|
|
67
|
+
timestamp: true,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Array to manage log groups (indentation)
|
|
72
|
+
* @private
|
|
73
|
+
*/
|
|
74
|
+
const _groups = [];
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Returns the ANSI code for a given style, color, or bg
|
|
78
|
+
* @private
|
|
79
|
+
* @param {string} value - The style/color name (e.g., "bold", "red", "rgb(255, 0, 0)")
|
|
80
|
+
* @returns {number|string|null} The ANSI code, rgb marker, or null if unknown
|
|
81
|
+
*/
|
|
82
|
+
const _getCode = (value) => {
|
|
83
|
+
if (_style[value] !== undefined) return _style[value];
|
|
84
|
+
if (_color.fg[value] !== undefined) return _color.fg[value];
|
|
85
|
+
if (value.startsWith("bg")) {
|
|
86
|
+
const bg = value.charAt(2).toLowerCase() + value.slice(3);
|
|
87
|
+
if (_color.bg[bg] !== undefined) return _color.bg[bg];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const rgbMatch = value.match(/^rgb\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\)$/);
|
|
91
|
+
if (rgbMatch) {
|
|
92
|
+
const r = parseInt(rgbMatch[1], 10);
|
|
93
|
+
const g = parseInt(rgbMatch[2], 10);
|
|
94
|
+
const b = parseInt(rgbMatch[3], 10);
|
|
95
|
+
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
|
|
96
|
+
return `rgb:${r},${g},${b}`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return null;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Parses content between delimiters to extract styles and text
|
|
105
|
+
* @private
|
|
106
|
+
* @param {string} input - Content between delimiters (e.g., "red bold hello" or "rgb(255, 0, 0) hello")
|
|
107
|
+
* @returns {{ codes: number[], text: string }} ANSI codes and text to display
|
|
108
|
+
*/
|
|
109
|
+
const _parse = (input) => {
|
|
110
|
+
const rgbMatch = input.match(/rgb\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\)/);
|
|
111
|
+
const rgbValue = rgbMatch ? rgbMatch[0] : null;
|
|
112
|
+
const cleanedInput = input.replace(/rgb\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\)/, "").trim();
|
|
113
|
+
const tokens = cleanedInput.trim().split(/\s+/);
|
|
114
|
+
const styles = [];
|
|
115
|
+
const fgs = [];
|
|
116
|
+
const bgs = [];
|
|
117
|
+
const txts = [];
|
|
118
|
+
|
|
119
|
+
for (const token of tokens) {
|
|
120
|
+
if (!token) continue;
|
|
121
|
+
const code = _getCode(token);
|
|
122
|
+
if (code === null) {
|
|
123
|
+
txts.push(token);
|
|
124
|
+
} else {
|
|
125
|
+
if (_style[token] !== undefined) {
|
|
126
|
+
styles.push(code);
|
|
127
|
+
} else if (token.startsWith("bg")) {
|
|
128
|
+
bgs.push(code);
|
|
129
|
+
} else {
|
|
130
|
+
fgs.push(code);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const codes = [...styles, ...bgs, ...fgs];
|
|
136
|
+
|
|
137
|
+
if (rgbValue) {
|
|
138
|
+
const rgbParts = rgbValue.match(/rgb\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\)/);
|
|
139
|
+
if (rgbParts) {
|
|
140
|
+
const r = parseInt(rgbParts[1], 10);
|
|
141
|
+
const g = parseInt(rgbParts[2], 10);
|
|
142
|
+
const b = parseInt(rgbParts[3], 10);
|
|
143
|
+
codes.push(38, 2, r, g, b);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const text = cleanedInput
|
|
148
|
+
.replace(
|
|
149
|
+
/^\s*((redBright|greenBright|yellowBright|blueBright|magentaBright|cyanBright|whiteBright|red|green|yellow|blue|magenta|cyan|white|gray|black|bold|italic|underline|overline|bg\w+|rgb\(\d+\s*,\s*\d+\s*,\s*\d+\)|\s+)*)/gi,
|
|
150
|
+
"",
|
|
151
|
+
)
|
|
152
|
+
.trimStart();
|
|
153
|
+
return { codes, text };
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Builds the ANSI sequence from codes
|
|
158
|
+
* @private
|
|
159
|
+
* @param {number[]} codes - Array of ANSI codes
|
|
160
|
+
* @returns {string} The ANSI sequence (e.g., "\x1B[1;31m")
|
|
161
|
+
*/
|
|
162
|
+
const _build = (codes) => {
|
|
163
|
+
if (codes.length === 0) return "";
|
|
164
|
+
return `\x1B[${codes.join(";")}m`;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Applies ANSI formatting to a string, replacing content between delimiters
|
|
169
|
+
* @private
|
|
170
|
+
* @param {string} str - String to format (e.g., "<%red hello%>")
|
|
171
|
+
* @returns {string} String with ANSI sequences
|
|
172
|
+
*/
|
|
173
|
+
const _apply = (str) => {
|
|
174
|
+
const esc_start = _options.delimiter.open.replace(
|
|
175
|
+
/[.*+?^${}()|[\]\\]/g,
|
|
176
|
+
"\\$&",
|
|
177
|
+
);
|
|
178
|
+
const esc_end = _options.delimiter.close.replace(
|
|
179
|
+
/[.*+?^${}()|[\]\\]/g,
|
|
180
|
+
"\\$&",
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
return str.replace(
|
|
184
|
+
new RegExp(`${esc_start}([\\s\\S]*?)${esc_end}`, "g"),
|
|
185
|
+
(match, content) => {
|
|
186
|
+
const { codes, text } = _parse(content);
|
|
187
|
+
if (codes.length === 0 || text === "") return text;
|
|
188
|
+
|
|
189
|
+
if (text.includes("\n")) {
|
|
190
|
+
return text
|
|
191
|
+
.split(/\r?\n/)
|
|
192
|
+
.filter((line) => line.trim())
|
|
193
|
+
.map((line) => `${_build(codes)}${line}\x1B[0m`)
|
|
194
|
+
.join("\n");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return `${_build(codes)}${text}\x1B[0m`;
|
|
198
|
+
},
|
|
199
|
+
);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Generates the formatted timestamp using the current delimiter
|
|
204
|
+
* @private
|
|
205
|
+
* @returns {string} Formatted timestamp (e.g., "<%gray [08:21:54]%>") or "" if disabled
|
|
206
|
+
*/
|
|
207
|
+
const _timestamp = () => {
|
|
208
|
+
if (!_options.timestamp) return "";
|
|
209
|
+
return _apply(
|
|
210
|
+
`${_options.delimiter.open}gray [${new Date().toLocaleTimeString(undefined, { hour12: false })}]${_options.delimiter.close}`,
|
|
211
|
+
);
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Returns the current indentation based on the number of active groups
|
|
216
|
+
* @private
|
|
217
|
+
* @returns {string} Indentation spaces (2 spaces per group)
|
|
218
|
+
*/
|
|
219
|
+
const _indent = () => {
|
|
220
|
+
return " ".repeat(_groups.length);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
module.exports = {
|
|
224
|
+
/**
|
|
225
|
+
* Enables or disables timestamp in logs
|
|
226
|
+
* @param {boolean} value - True to enable, false to disable
|
|
227
|
+
* @returns {Logger} The same Logger instance (for chaining)
|
|
228
|
+
*/
|
|
229
|
+
setTimestamp(value) {
|
|
230
|
+
_options.timestamp = value;
|
|
231
|
+
return /** @type {Logger} */ (this);
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Changes the delimiters used for text formatting
|
|
236
|
+
* @param {object} options - Delimiter options
|
|
237
|
+
* @param {string} [options.open] - New opening delimiter
|
|
238
|
+
* @param {string} [options.close] - New closing delimiter
|
|
239
|
+
* @returns {Logger} The same Logger instance (for chaining)
|
|
240
|
+
*/
|
|
241
|
+
setDelimiter(options = {}) {
|
|
242
|
+
_options.delimiter = {
|
|
243
|
+
..._options.delimiter,
|
|
244
|
+
...options,
|
|
245
|
+
};
|
|
246
|
+
return /** @type {Logger} */ (this);
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Prints a message to the console with ANSI formatting
|
|
251
|
+
* @param {*} content - Content to print (string, object, or other)
|
|
252
|
+
* @returns {Logger} The same Logger instance (for chaining)
|
|
253
|
+
*/
|
|
254
|
+
log(content) {
|
|
255
|
+
let str;
|
|
256
|
+
if (typeof content === "string") {
|
|
257
|
+
str = content;
|
|
258
|
+
} else if (typeof content === "object") {
|
|
259
|
+
str = JSON.stringify(content, null, 2);
|
|
260
|
+
} else {
|
|
261
|
+
str = String(content);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const prefix = `${_timestamp()}${_indent()}`;
|
|
265
|
+
const fullStr = prefix ? `${prefix} ${str}` : str;
|
|
266
|
+
|
|
267
|
+
const formatted = _apply(fullStr);
|
|
268
|
+
|
|
269
|
+
if (formatted.includes("\n")) {
|
|
270
|
+
const lines = formatted.split("\n");
|
|
271
|
+
|
|
272
|
+
if (lines[0].trim()) console.log(lines[0]);
|
|
273
|
+
|
|
274
|
+
const indentOnly = _indent();
|
|
275
|
+
for (let i = 1; i < lines.length; i++) {
|
|
276
|
+
if (lines[i].trim()) {
|
|
277
|
+
console.log(`${indentOnly}${lines[i]}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
} else {
|
|
281
|
+
console.log(formatted);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return /** @type {Logger} */ (this);
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Start a log group (displays a label and adds indentation)
|
|
289
|
+
* @param {string} label - The group name to display
|
|
290
|
+
* @returns {Logger} The same Logger instance (for chaining)
|
|
291
|
+
*/
|
|
292
|
+
group(label) {
|
|
293
|
+
_groups.push(label);
|
|
294
|
+
const prefix = `${_timestamp()}${_indent()}`;
|
|
295
|
+
console.log(prefix ? `${prefix}▼ ${label}` : `▼ ${label}`);
|
|
296
|
+
return /** @type {Logger} */ (this);
|
|
297
|
+
},
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* End the current log group
|
|
301
|
+
* @returns {Logger} The same Logger instance (for chaining)
|
|
302
|
+
*/
|
|
303
|
+
groupEnd() {
|
|
304
|
+
if (_groups.length > 0) _groups.pop();
|
|
305
|
+
return /** @type {Logger} */ (this);
|
|
306
|
+
},
|
|
307
|
+
};
|