logxpert 1.0.5 → 2.0.3

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/main.js CHANGED
@@ -1,197 +1,375 @@
1
1
  const fs = require('fs');
2
- const moment = require('moment'); // Add moment as a dependency: npm install moment
2
+ const path = require('path');
3
+ const dayjs = require('dayjs');
3
4
  const { createLogger, format, transports } = require('winston');
4
5
  require('winston-daily-rotate-file');
5
6
 
6
- /**
7
- * Helper function to format the timestamp while preserving square brackets.
8
- * It identifies square brackets and formats only the contents within them,
9
- * preserving all other text (including the brackets) as literal.
10
- * If no brackets are provided, the entire string is treated as a Moment.js format.
11
- *
12
- * @param {string} fmt - The user-defined format string.
13
- * @returns {string} - The formatted timestamp.
14
- *
15
- * Example:
16
- * Input: "This is the date: [YYYY-MM-DD HH:mm:ss] - "
17
- * Output: "This is the date: [2025-03-21 15:30:45] - "
18
- */
19
- function formatCustomTimestamp(fmt) {
20
- // If the format contains both [ and ], we use "template mode".
21
- // This allows users to include literal text alongside bracketed date tokens.
22
- if (fmt.includes('[') && fmt.includes(']')) {
23
- return fmt.split(/(\[[^\]]*\])/).map(part => {
24
- if (part.startsWith('[') && part.endsWith(']')) {
25
- // Inside brackets: format the content while keeping the brackets literal.
26
- const inner = part.slice(1, -1);
27
- return '[' + (inner ? moment().format(inner) : '') + ']';
28
- }
29
- // Outside brackets: treat as literal text to avoid mangling letters.
30
- return part;
31
- }).join('');
7
+ const VALID_LEVELS = ['error', 'warn', 'info', 'http', 'verbose', 'debug', 'silly'];
8
+ const DEFAULT_CONSOLE_FORMAT = 'YYYY-MM-DD HH:mm:ss';
9
+
10
+ function pad2(n) {
11
+ return String(n).padStart(2, '0');
12
+ }
13
+
14
+ function formatDefaultTimestamp(d = new Date()) {
15
+ return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
16
+ }
17
+
18
+ function splitBrackets(fmt) {
19
+ const parts = [];
20
+ let current = '';
21
+ let i = 0;
22
+ while (i < fmt.length) {
23
+ if (fmt[i] === '[') {
24
+ const close = fmt.indexOf(']', i + 1);
25
+ if (close === -1) {
26
+ current += fmt.slice(i);
27
+ break;
28
+ }
29
+ if (current) {
30
+ parts.push(current);
31
+ current = '';
32
+ }
33
+ parts.push(fmt.slice(i, close + 1));
34
+ i = close + 1;
35
+ } else {
36
+ current += fmt[i];
37
+ i++;
32
38
  }
33
- // Standard mode: treat the whole string as a moment format string.
34
- return moment().format(fmt);
35
- }
36
-
37
-
38
- /**
39
- * @typedef {Object} ConsoleOptions
40
- * @property {boolean} [enableTimestamp=true] - Enable or disable timestamp in console logs.
41
- * @property {string} [timestampFormat='YYYY-MM-DD HH:mm:ss'] - Custom format for the timestamp.
42
- * Use square brackets (e.g., [YYYY-MM-DD]) to mark parts that should be formatted.
43
- * Text outside brackets is treated as literal.
44
- * @property {string} [timestampPrefix=''] - Optional text to prepend to the timestamp.
45
- * @property {string} [timestampSuffix=''] - Optional text to append to the timestamp.
46
- */
47
-
48
- /**
49
- * @typedef {Object} FilesOptions
50
- * @property {string} [folder='logs'] - Directory where log files will be stored.
51
- * @property {string} [filesName='YYYY-MM-DD'] - Date pattern used in the log file name.
52
- * @property {string} [maxFile='14d'] - Maximum file retention time.
53
- * @property {string} [maxSize='20m'] - Maximum file size per log file.
54
- * @property {boolean} [zippedArchive=false] - Whether to archive logs as zip.
55
- */
56
-
57
- /**
58
- * @typedef {Object} LogSettingsOptions
59
- * @property {ConsoleOptions} [console] - Options for customizing console output.
60
- * @property {FilesOptions} [files] - Options for configuring file logging.
61
- */
62
-
63
- // Custom format for logger output
64
- const customFormat = format.printf(({ timestamp, level, message }) => {
65
- return timestamp ? `${timestamp} [${level}]: ${message}` : `[${level}]: ${message}`;
66
- });
39
+ }
40
+ if (current) parts.push(current);
41
+ return parts;
42
+ }
67
43
 
68
- // Default console timestamp format
69
- const defaultConsoleFormat = 'YYYY-MM-DD HH:mm:ss';
44
+ function compileTimestampFormatter(fmt) {
45
+ if (typeof fmt !== 'string' || !fmt) return formatDefaultTimestamp;
46
+ if (fmt === DEFAULT_CONSOLE_FORMAT) return formatDefaultTimestamp;
47
+ if (!fmt.includes('[') || !fmt.includes(']')) return () => dayjs().format(fmt);
48
+ const parts = splitBrackets(fmt).map((part) => {
49
+ if (part.startsWith('[') && part.endsWith(']')) {
50
+ const inner = part.slice(1, -1);
51
+ return inner ? () => '[' + dayjs().format(inner) + ']' : () => '[]';
52
+ }
53
+ return () => part;
54
+ });
55
+ return () => parts.map((f) => f()).join('');
56
+ }
70
57
 
71
- // Create the global logger. Its default format will be overridden by the console transport settings.
72
- const logger = createLogger({
73
- level: 'debug',
74
- format: format.combine(
75
- format.timestamp({ format: () => moment().format(defaultConsoleFormat) }),
76
- customFormat
77
- )
78
- });
58
+ function formatCustomTimestamp(fmt) {
59
+ return compileTimestampFormatter(fmt)();
60
+ }
61
+
62
+ function formatExtra(meta) {
63
+ const keys = Object.keys(meta).filter((k) => k !== 'splat' && k !== 'ms');
64
+ if (!keys.length) return '';
65
+ try {
66
+ const picked = {};
67
+ for (const k of keys) picked[k] = meta[k];
68
+ return JSON.stringify(picked);
69
+ } catch {
70
+ return '';
71
+ }
72
+ }
79
73
 
80
- // Create default console transport (will be replaced via settings)
81
- let consoleTransport = new transports.Console({
82
- format: format.combine(
83
- format.colorize(),
84
- format.timestamp({ format: () => moment().format(defaultConsoleFormat) }),
85
- customFormat
86
- )
74
+ const prettyFormat = format.printf(({ timestamp, level, message, ...meta }) => {
75
+ const msg = typeof message === 'string' ? message : message == null ? '' : JSON.stringify(message);
76
+ const extra = formatExtra(meta);
77
+ const body = [msg, extra].filter(Boolean).join(' ');
78
+ return timestamp ? `${timestamp} [${level}]: ${body}` : `[${level}]: ${body}`;
87
79
  });
88
- logger.add(consoleTransport);
89
-
90
- // Reference to file transport (added via settings)
91
- let fileTransport;
92
-
93
- /**
94
- * Configures logging settings for both console and file transports.
95
- *
96
- * @param {LogSettingsOptions} options - The settings for both console and file logging.
97
- * @example
98
- * // Example: Fully custom console timestamp that preserves decorational brackets,
99
- * // and enabling file logging.
100
- * log.settings({
101
- * console: {
102
- * enableTimestamp: true,
103
- * timestampFormat: "This is the date: [YYYY-MM-DD HH:mm:ss] - "
104
- * },
105
- * files: {
106
- * folder: 'logs',
107
- * filesName: 'YYYY-MM-DD',
108
- * maxFile: '14d',
109
- * maxSize: '20m',
110
- * zippedArchive: false
111
- * }
112
- * });
113
- */
114
- function applySettings(options = {}) {
115
- if (options.console) {
116
- const enableTimestamp = options.console.enableTimestamp !== undefined ? options.console.enableTimestamp : true;
117
- const timestampFormat = options.console.timestampFormat || defaultConsoleFormat;
118
- const timestampPrefix = options.console.timestampPrefix || '';
119
- const timestampSuffix = options.console.timestampSuffix || '';
120
-
121
- // Remove the current console transport
122
- logger.remove(consoleTransport);
123
-
124
- // Build the console format based on the enableTimestamp flag.
125
- const consoleFormat = enableTimestamp
126
- ? format.combine(
127
- format.colorize(),
128
- format.timestamp({ format: () => timestampPrefix + formatCustomTimestamp(timestampFormat) + timestampSuffix }),
129
- customFormat
130
- )
131
- : format.combine(
132
- format((info) => { delete info.timestamp; return info; })(),
133
- format.colorize(),
134
- customFormat
135
- );
136
-
137
- consoleTransport = new transports.Console({
138
- format: consoleFormat
139
- });
140
- logger.add(consoleTransport);
80
+
81
+ function assertLevel(level) {
82
+ if (!VALID_LEVELS.includes(level)) throw new Error(`Invalid log level "${level}". Use one of: ${VALID_LEVELS.join(', ')}`);
83
+ }
84
+
85
+ function stripAnsi(str) {
86
+ let out = '';
87
+ let i = 0;
88
+ while (i < str.length) {
89
+ if (str[i] !== '\x1b') {
90
+ out += str[i];
91
+ i++;
92
+ continue;
93
+ }
94
+ const next = str[i + 1];
95
+ if (next === ']') {
96
+ let j = i + 2;
97
+ while (j < str.length) {
98
+ if (str[j] === '\x07') { j++; break; }
99
+ if (str[j] === '\x1b' && str[j + 1] === '\\') { j += 2; break; }
100
+ j++;
101
+ }
102
+ i = j;
103
+ } else if (next === '[') {
104
+ let j = i + 2;
105
+ while (j < str.length && str[j] >= ' ' && str[j] <= '?') j++;
106
+ if (j < str.length) j++;
107
+ i = j;
108
+ } else if (next === '(' || next === ')') {
109
+ i += 3;
110
+ } else {
111
+ i += next === undefined ? 1 : 2;
141
112
  }
113
+ }
114
+ return out;
115
+ }
116
+ const CONTROL_RE = /[\x00-\x08\x0b\x0c\x0d\x0e-\x1f\x7f]/g;
142
117
 
143
- if (options.files) {
144
- const folder = options.files.folder || 'logs';
145
- const datePattern = options.files.filesName || 'YYYY-MM-DD';
146
- const maxFiles = options.files.maxFile || '14d';
147
- const maxSize = options.files.maxSize || '20m';
148
- const zippedArchive = options.files.zippedArchive !== undefined ? options.files.zippedArchive : false;
149
-
150
- if (!fs.existsSync(folder)) {
151
- fs.mkdirSync(folder, { recursive: true });
152
- }
153
-
154
- if (fileTransport) {
155
- logger.remove(fileTransport);
156
- }
157
-
158
- fileTransport = new transports.DailyRotateFile({
159
- filename: `${folder}/application-%DATE%.log`,
160
- datePattern,
161
- zippedArchive,
162
- maxSize,
163
- maxFiles
164
- });
165
- logger.add(fileTransport);
118
+ function sanitizeMessage(msg) {
119
+ if (typeof msg !== 'string') return msg;
120
+ return stripAnsi(msg).replace(CONTROL_RE, '');
121
+ }
122
+
123
+ function assertFilenameSafe(filename) {
124
+ if (typeof filename !== 'string' || !filename.trim() || filename.includes('\0')) {
125
+ throw new Error('files.filename must be a non-empty string');
126
+ }
127
+ if (path.isAbsolute(filename) || filename.split(/[\\/]/).includes('..')) {
128
+ throw new Error('files.filename must not escape the log folder (.. and absolute paths are rejected)');
129
+ }
130
+ }
131
+
132
+ function isSafeChar(c) {
133
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c === '.' || c === '_' || c === '-';
134
+ }
135
+
136
+ function isPadChar(c) {
137
+ return c === '-' || c === '_' || c === '.';
138
+ }
139
+
140
+ function sanitizeAppName(name) {
141
+ if (typeof name !== 'string' || !name.trim()) return null;
142
+ const base = name.trim().slice(0, 200).split('/').pop();
143
+ let out = '';
144
+ for (let i = 0; i < base.length; i++) {
145
+ out += isSafeChar(base[i]) ? base[i] : '-';
146
+ }
147
+ let start = 0;
148
+ while (start < out.length && isPadChar(out[start])) start++;
149
+ let end = out.length;
150
+ while (end > start && isPadChar(out[end - 1])) end--;
151
+ const safe = out.slice(start, end);
152
+ return safe || null;
153
+ }
154
+
155
+ let cachedAppName = null;
156
+ function getDefaultAppName() {
157
+ if (cachedAppName) return cachedAppName;
158
+ try {
159
+ const pkgPath = path.resolve(process.cwd(), 'package.json');
160
+ const raw = fs.readFileSync(pkgPath, 'utf8');
161
+ cachedAppName = sanitizeAppName(JSON.parse(raw)?.name) ?? 'application';
162
+ } catch {
163
+ cachedAppName = 'application';
164
+ }
165
+ return cachedAppName;
166
+ }
167
+
168
+ function resolveRunNumber(folder, prefix, opt) {
169
+ let enabled = false;
170
+ let separator = '-';
171
+ let padding = 0;
172
+ let startAt = 1;
173
+ if (opt === true) {
174
+ enabled = true;
175
+ } else if (opt && typeof opt === 'object') {
176
+ enabled = opt.enabled ?? true;
177
+ if (opt.separator !== undefined) {
178
+ if (typeof opt.separator !== 'string' || !opt.separator) throw new Error('files.runNumber.separator must be a non-empty string');
179
+ separator = opt.separator;
180
+ }
181
+ if (opt.padding !== undefined) {
182
+ if (!Number.isInteger(opt.padding) || opt.padding < 0) throw new Error('files.runNumber.padding must be a non-negative integer');
183
+ padding = opt.padding;
166
184
  }
185
+ if (opt.startAt !== undefined) {
186
+ if (!Number.isInteger(opt.startAt) || opt.startAt < 0) throw new Error('files.runNumber.startAt must be a non-negative integer');
187
+ startAt = opt.startAt;
188
+ }
189
+ } else if (opt !== undefined && opt !== false) {
190
+ throw new Error('files.runNumber must be a boolean or an object');
191
+ }
192
+ if (!enabled) return null;
193
+ const counterFile = path.join(folder, `.${prefix}.run`);
194
+ let next = startAt;
195
+ try {
196
+ const raw = fs.readFileSync(counterFile, 'utf8').trim();
197
+ const prev = parseInt(raw, 10);
198
+ next = Number.isInteger(prev) ? prev + 1 : startAt;
199
+ } catch {}
200
+ try {
201
+ fs.writeFileSync(counterFile, String(next), 'utf8');
202
+ } catch {}
203
+ return separator + String(next).padStart(padding, '0');
204
+ }
205
+
206
+ function normalizeFilesOptions(files = {}) {
207
+ const rawFolder = files.folder ?? 'logs';
208
+ if (typeof rawFolder !== 'string' || !rawFolder.trim() || rawFolder.includes('\0')) {
209
+ throw new Error('files.folder must be a non-empty string');
210
+ }
211
+ const folder = path.normalize(rawFolder);
212
+ const datePattern = files.datePattern ?? files.dateFormat ?? files.filesName ?? 'YYYY_MM_DD';
213
+ if (typeof datePattern !== 'string' || !datePattern.trim()) {
214
+ throw new Error('files.datePattern must be a non-empty string');
215
+ }
216
+ const explicitPattern = files.filePattern ?? files.pattern ?? files.filename;
217
+ const explicitPrefix = files.prefix ?? files.appName;
218
+ if (explicitPattern !== undefined && explicitPrefix !== undefined) {
219
+ throw new Error('Do not combine "prefix" with "filePattern", pick one: prefix builds "<prefix>-%DATE%.log" for you, filePattern takes full control');
220
+ }
221
+ let filename = explicitPattern ?? `${sanitizeAppName(explicitPrefix) ?? getDefaultAppName()}-%DATE%.log`;
222
+ filename = filename.split('%datePattern%').join('%DATE%');
223
+ assertFilenameSafe(filename);
224
+ if (!filename.includes('%DATE%')) {
225
+ const ext = path.extname(filename) || '.log';
226
+ const base = path.basename(filename, ext);
227
+ filename = `${base}-%DATE%${ext}`;
228
+ }
229
+ assertFilenameSafe(filename);
230
+ if (!fs.existsSync(folder)) fs.mkdirSync(folder, { recursive: true });
231
+ const rawPrefix = path.basename(filename).split('%DATE%')[0];
232
+ let prefixEnd = rawPrefix.length;
233
+ while (prefixEnd > 0 && isPadChar(rawPrefix[prefixEnd - 1])) prefixEnd--;
234
+ const prefix = rawPrefix.slice(0, prefixEnd) || 'application';
235
+ const runSuffix = resolveRunNumber(folder, sanitizeAppName(prefix) ?? 'application', files.runNumber ?? false);
236
+ if (runSuffix) {
237
+ const ext = path.extname(filename) || '.log';
238
+ const base = filename.slice(0, -ext.length);
239
+ filename = `${base}${runSuffix}${ext}`;
240
+ }
241
+ return {
242
+ folder,
243
+ datePattern,
244
+ filename,
245
+ maxFiles: files.maxFile ?? files.maxFiles ?? '14d',
246
+ maxSize: files.maxSize ?? '20m',
247
+ zippedArchive: files.zippedArchive ?? false,
248
+ level: files.level,
249
+ };
167
250
  }
168
251
 
169
- /**
170
- * Logs a general message using the info level.
171
- *
172
- * @param {string} message - The message to be logged.
173
- */
174
- function log(message) {
175
- logger.info(message);
252
+ function buildConsoleTransport({ enableTimestamp = true, timestampFormat = DEFAULT_CONSOLE_FORMAT, timestampPrefix = '', timestampSuffix = '', colorize = true, level } = {}) {
253
+ const parts = [];
254
+ if (colorize) parts.push(format.colorize());
255
+ if (enableTimestamp) {
256
+ const stamp = compileTimestampFormatter(timestampFormat);
257
+ parts.push(format.timestamp({ format: () => `${timestampPrefix}${stamp()}${timestampSuffix}` }));
258
+ } else {
259
+ parts.push(format((info) => { delete info.timestamp; return info; })());
260
+ }
261
+ parts.push(format.errors({ stack: true }), format.splat(), prettyFormat);
262
+ return new transports.Console({ format: format.combine(...parts), level });
176
263
  }
177
264
 
178
- log.error = function (message) {
179
- logger.error(message);
180
- };
265
+ function buildFileTransport({ folder, datePattern, filename, maxFiles, maxSize, zippedArchive, level }) {
266
+ if (!fs.existsSync(folder)) fs.mkdirSync(folder, { recursive: true });
267
+ return new transports.DailyRotateFile({
268
+ filename: path.join(folder, filename),
269
+ datePattern,
270
+ zippedArchive,
271
+ maxSize,
272
+ maxFiles,
273
+ level,
274
+ format: format.combine(format.errors({ stack: true }), format.splat(), format.timestamp(), prettyFormat),
275
+ });
276
+ }
181
277
 
182
- log.warn = function (message) {
183
- logger.warn(message);
184
- };
278
+ function createLoggerInstance(initial = {}) {
279
+ const globalLevel = process.env.LOG_LEVEL || initial.level || 'debug';
280
+ assertLevel(globalLevel);
185
281
 
186
- log.info = function (message) {
187
- logger.info(message);
188
- };
282
+ const logger = createLogger({
283
+ level: globalLevel,
284
+ format: format.combine(format.errors({ stack: true }), format.splat()),
285
+ });
189
286
 
190
- log.debug = function (message) {
191
- logger.debug(message);
192
- };
287
+ let consoleTransport = buildConsoleTransport(initial.console);
288
+ consoleTransport.level = initial.console?.level;
289
+ logger.add(consoleTransport);
193
290
 
194
- // Expose the settings function for configuring both file logging and console output.
195
- log.settings = applySettings;
291
+ let fileTransport = null;
292
+ let consoleOpts = { ...(initial.console || {}) };
293
+ let filesOpts = null;
294
+
295
+ function applySettings(options = {}) {
296
+ if (options.level !== undefined) {
297
+ assertLevel(options.level);
298
+ logger.level = options.level;
299
+ }
300
+ if (options.console) {
301
+ if (options.console.level !== undefined && options.console.level !== null) assertLevel(options.console.level);
302
+ consoleOpts = { ...consoleOpts, ...options.console };
303
+ logger.remove(consoleTransport);
304
+ consoleTransport = buildConsoleTransport(consoleOpts);
305
+ consoleTransport.level = consoleOpts.level;
306
+ logger.add(consoleTransport);
307
+ }
308
+ if (options.files) {
309
+ const normalized = normalizeFilesOptions(options.files);
310
+ if (normalized.level !== undefined && normalized.level !== null) assertLevel(normalized.level);
311
+ filesOpts = normalized;
312
+ if (fileTransport) logger.remove(fileTransport);
313
+ fileTransport = buildFileTransport(normalized);
314
+ logger.add(fileTransport);
315
+ }
316
+ }
317
+
318
+ function isEnabled(level) {
319
+ const want = logger.levels[level];
320
+ const current = logger.levels[logger.level];
321
+ return want !== undefined && current !== undefined && want <= current;
322
+ }
323
+
324
+ function write(level, message, ...meta) {
325
+ if (typeof message === 'function') {
326
+ if (!isEnabled(level)) return;
327
+ message = message();
328
+ }
329
+ if (message instanceof Error) {
330
+ logger.log(level, sanitizeMessage(message.message), { stack: message.stack, ...meta[0] });
331
+ return;
332
+ }
333
+ logger.log(level, sanitizeMessage(message), ...meta);
334
+ }
335
+
336
+ function callable(message, ...meta) {
337
+ write('info', message, ...meta);
338
+ }
339
+
340
+ callable.error = (m, ...a) => write('error', m, ...a);
341
+ callable.warn = (m, ...a) => write('warn', m, ...a);
342
+ callable.info = (m, ...a) => write('info', m, ...a);
343
+ callable.debug = (m, ...a) => write('debug', m, ...a);
344
+ callable.http = (m, ...a) => write('http', m, ...a);
345
+ callable.verbose = (m, ...a) => write('verbose', m, ...a);
346
+ callable.silly = (m, ...a) => write('silly', m, ...a);
347
+ callable.log = (level, m, ...a) => { assertLevel(level); write(level, m, ...a); };
348
+
349
+ callable.settings = applySettings;
350
+ callable.setLevel = (level) => { assertLevel(level); logger.level = level; };
351
+ callable.getLevel = () => logger.level;
352
+ callable.child = (meta = {}) => {
353
+ const child = logger.child(meta);
354
+ return {
355
+ error: (m, ...a) => child.error(m, ...a),
356
+ warn: (m, ...a) => child.warn(m, ...a),
357
+ info: (m, ...a) => child.info(m, ...a),
358
+ debug: (m, ...a) => child.debug(m, ...a),
359
+ log: child.log.bind(child),
360
+ };
361
+ };
362
+ callable.close = () => {
363
+ if (fileTransport) { logger.remove(fileTransport); try { fileTransport.close?.(); } catch {} fileTransport = null; }
364
+ };
365
+ callable.createLogger = (opts = {}) => createLoggerInstance(opts);
366
+ callable._winston = logger;
367
+
368
+ if (initial.files) applySettings({ files: initial.files });
369
+ if (initial.console || initial.level) applySettings({ console: initial.console, level: initial.level });
370
+
371
+ return callable;
372
+ }
196
373
 
197
- module.exports = log;
374
+ module.exports = createLoggerInstance();
375
+ module.exports.createLogger = createLoggerInstance;
package/main.mjs ADDED
@@ -0,0 +1,14 @@
1
+ import log from './main.js';
2
+
3
+ export const settings = log.settings;
4
+ export const setLevel = log.setLevel;
5
+ export const getLevel = log.getLevel;
6
+ export const child = log.child;
7
+ export const close = log.close;
8
+ export const createLogger = log.createLogger;
9
+ export const error = log.error;
10
+ export const warn = log.warn;
11
+ export const info = log.info;
12
+ export const debug = log.debug;
13
+
14
+ export default log;
package/package.json CHANGED
@@ -1,14 +1,63 @@
1
1
  {
2
2
  "name": "logxpert",
3
- "version": "1.0.5",
4
- "description": "A powerful logging library for Node.js",
3
+ "version": "2.0.3",
4
+ "description": "A powerful logging library for Node.js with colorful output and daily file rotation",
5
5
  "main": "main.js",
6
- "repository": "git+https://github.com/lighscent/LogXpert.git",
6
+ "types": "index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": {
10
+ "types": "./index.d.mts",
11
+ "default": "./main.mjs"
12
+ },
13
+ "require": {
14
+ "types": "./index.d.ts",
15
+ "default": "./main.js"
16
+ }
17
+ },
18
+ "./package.json": "./package.json"
19
+ },
20
+ "files": [
21
+ "main.js",
22
+ "main.mjs",
23
+ "index.d.ts",
24
+ "index.d.mts",
25
+ "README.md",
26
+ "LICENSE",
27
+ "CHANGELOG.md"
28
+ ],
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/lighscent/LogXpert.git"
32
+ },
33
+ "bugs": {
34
+ "url": "https://github.com/lighscent/LogXpert/issues"
35
+ },
36
+ "homepage": "https://github.com/lighscent/LogXpert#readme",
7
37
  "author": "https://github.com/lighscent",
8
- "license": "MIT",
38
+ "license": "Apache-2.0",
39
+ "keywords": [
40
+ "logging",
41
+ "logger",
42
+ "winston",
43
+ "daily-rotate",
44
+ "nodejs"
45
+ ],
46
+ "engines": {
47
+ "node": ">=18"
48
+ },
49
+ "scripts": {
50
+ "test": "node --test test/logger.test.js",
51
+ "check:types": "tsc -p test-d --noEmit",
52
+ "attw": "attw --pack"
53
+ },
9
54
  "dependencies": {
10
- "moment": "^2.30.1",
55
+ "dayjs": "^1.11.23",
11
56
  "winston": "^3.19.0",
12
57
  "winston-daily-rotate-file": "^5.0.0"
58
+ },
59
+ "devDependencies": {
60
+ "@arethetypeswrong/cli": "^0.18.5",
61
+ "typescript": "^7.0.2"
13
62
  }
14
- }
63
+ }