bun-git-hooks 0.2.13 → 0.2.14
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/dist/bin/cli.js +5046 -621
- package/dist/bin/git-hooks.d.ts +2 -2
- package/dist/bin/types.d.ts +7 -3
- package/dist/git-hooks.d.ts +2 -2
- package/dist/index.js +4499 -74
- package/dist/types.d.ts +7 -3
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -1,10 +1,44 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
+
|
|
1
20
|
// src/config.ts
|
|
2
|
-
import
|
|
21
|
+
import process7 from "node:process";
|
|
3
22
|
|
|
4
23
|
// node_modules/bunfig/dist/index.js
|
|
24
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync as readdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
25
|
+
import { dirname as dirname2, resolve as resolve3 } from "path";
|
|
26
|
+
import process6 from "process";
|
|
27
|
+
import { join, relative, resolve as resolve2 } from "path";
|
|
28
|
+
import process2 from "process";
|
|
5
29
|
import { existsSync, mkdirSync, readdirSync, writeFileSync } from "fs";
|
|
6
30
|
import { dirname, resolve } from "path";
|
|
7
31
|
import process from "process";
|
|
32
|
+
import { Buffer } from "buffer";
|
|
33
|
+
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
|
|
34
|
+
import { closeSync, createReadStream, createWriteStream, existsSync as existsSync2, fsyncSync, openSync, writeFileSync as writeFileSync2 } from "fs";
|
|
35
|
+
import { access, constants, mkdir, readdir, rename, stat, unlink, writeFile } from "fs/promises";
|
|
36
|
+
import { join as join2 } from "path";
|
|
37
|
+
import process5 from "process";
|
|
38
|
+
import { pipeline } from "stream/promises";
|
|
39
|
+
import { createGzip } from "zlib";
|
|
40
|
+
import process4 from "process";
|
|
41
|
+
import process3 from "process";
|
|
8
42
|
function deepMerge(target, source) {
|
|
9
43
|
if (Array.isArray(source) && Array.isArray(target) && source.length === 2 && target.length === 2 && isObject(source[0]) && "id" in source[0] && source[0].id === 3 && isObject(source[1]) && "id" in source[1] && source[1].id === 4) {
|
|
10
44
|
return source;
|
|
@@ -120,18 +154,3147 @@ function deepEquals(a, b) {
|
|
|
120
154
|
for (const key of keysA) {
|
|
121
155
|
if (!Object.prototype.hasOwnProperty.call(b, key))
|
|
122
156
|
return false;
|
|
123
|
-
if (!deepEquals(a[key], b[key]))
|
|
157
|
+
if (!deepEquals(a[key], b[key]))
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
function isObject(item) {
|
|
165
|
+
return Boolean(item && typeof item === "object" && !Array.isArray(item));
|
|
166
|
+
}
|
|
167
|
+
async function tryLoadConfig(configPath, defaultConfig) {
|
|
168
|
+
if (!existsSync(configPath))
|
|
169
|
+
return null;
|
|
170
|
+
try {
|
|
171
|
+
const importedConfig = await import(configPath);
|
|
172
|
+
const loadedConfig = importedConfig.default || importedConfig;
|
|
173
|
+
if (typeof loadedConfig !== "object" || loadedConfig === null || Array.isArray(loadedConfig))
|
|
174
|
+
return null;
|
|
175
|
+
try {
|
|
176
|
+
return deepMerge(defaultConfig, loadedConfig);
|
|
177
|
+
} catch {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
} catch {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async function loadConfig({
|
|
185
|
+
name = "",
|
|
186
|
+
cwd,
|
|
187
|
+
defaultConfig
|
|
188
|
+
}) {
|
|
189
|
+
const baseDir = cwd || process.cwd();
|
|
190
|
+
const extensions = [".ts", ".js", ".mjs", ".cjs", ".json"];
|
|
191
|
+
const configPaths = [
|
|
192
|
+
`${name}.config`,
|
|
193
|
+
`.${name}.config`,
|
|
194
|
+
name,
|
|
195
|
+
`.${name}`
|
|
196
|
+
];
|
|
197
|
+
for (const configPath of configPaths) {
|
|
198
|
+
for (const ext of extensions) {
|
|
199
|
+
const fullPath = resolve(baseDir, `${configPath}${ext}`);
|
|
200
|
+
const config2 = await tryLoadConfig(fullPath, defaultConfig);
|
|
201
|
+
if (config2 !== null)
|
|
202
|
+
return config2;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
console.error("Failed to load client config from any expected location");
|
|
206
|
+
return defaultConfig;
|
|
207
|
+
}
|
|
208
|
+
var defaultConfigDir = resolve(process.cwd(), "config");
|
|
209
|
+
var defaultGeneratedDir = resolve(process.cwd(), "src/generated");
|
|
210
|
+
function getProjectRoot(filePath, options = {}) {
|
|
211
|
+
let path = process2.cwd();
|
|
212
|
+
while (path.includes("storage"))
|
|
213
|
+
path = resolve2(path, "..");
|
|
214
|
+
const finalPath = resolve2(path, filePath || "");
|
|
215
|
+
if (options?.relative)
|
|
216
|
+
return relative(process2.cwd(), finalPath);
|
|
217
|
+
return finalPath;
|
|
218
|
+
}
|
|
219
|
+
var defaultLogDirectory = process2.env.CLARITY_LOG_DIR || join(getProjectRoot(), "logs");
|
|
220
|
+
var defaultConfig = {
|
|
221
|
+
level: "info",
|
|
222
|
+
defaultName: "clarity",
|
|
223
|
+
timestamp: true,
|
|
224
|
+
colors: true,
|
|
225
|
+
format: "text",
|
|
226
|
+
maxLogSize: 10485760,
|
|
227
|
+
logDatePattern: "YYYY-MM-DD",
|
|
228
|
+
logDirectory: defaultLogDirectory,
|
|
229
|
+
rotation: {
|
|
230
|
+
frequency: "daily",
|
|
231
|
+
maxSize: 10485760,
|
|
232
|
+
maxFiles: 5,
|
|
233
|
+
compress: false,
|
|
234
|
+
rotateHour: 0,
|
|
235
|
+
rotateMinute: 0,
|
|
236
|
+
rotateDayOfWeek: 0,
|
|
237
|
+
rotateDayOfMonth: 1,
|
|
238
|
+
encrypt: false
|
|
239
|
+
},
|
|
240
|
+
verbose: false
|
|
241
|
+
};
|
|
242
|
+
async function loadConfig2() {
|
|
243
|
+
try {
|
|
244
|
+
const loadedConfig = await loadConfig({
|
|
245
|
+
name: "clarity",
|
|
246
|
+
defaultConfig,
|
|
247
|
+
cwd: process2.cwd(),
|
|
248
|
+
endpoint: "",
|
|
249
|
+
headers: {}
|
|
250
|
+
});
|
|
251
|
+
return { ...defaultConfig, ...loadedConfig };
|
|
252
|
+
} catch {
|
|
253
|
+
return defaultConfig;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
var config = await loadConfig2();
|
|
257
|
+
function isBrowserProcess() {
|
|
258
|
+
if (process3.env.NODE_ENV === "test" || process3.env.BUN_ENV === "test") {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
return typeof window !== "undefined";
|
|
262
|
+
}
|
|
263
|
+
async function isServerProcess() {
|
|
264
|
+
if (process3.env.NODE_ENV === "test" || process3.env.BUN_ENV === "test") {
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
if (typeof process3 !== "undefined") {
|
|
271
|
+
const type = process3.type;
|
|
272
|
+
if (type === "renderer" || type === "worker") {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
return !!(process3.versions && (process3.versions.node || process3.versions.bun));
|
|
276
|
+
}
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
class JsonFormatter {
|
|
281
|
+
async format(entry) {
|
|
282
|
+
const isServer = await isServerProcess();
|
|
283
|
+
const metadata = await this.getMetadata(isServer);
|
|
284
|
+
return JSON.stringify({
|
|
285
|
+
timestamp: entry.timestamp.toISOString(),
|
|
286
|
+
level: entry.level,
|
|
287
|
+
name: entry.name,
|
|
288
|
+
message: entry.message,
|
|
289
|
+
metadata
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
async getMetadata(isServer) {
|
|
293
|
+
if (isServer) {
|
|
294
|
+
const { hostname } = await import("os");
|
|
295
|
+
return {
|
|
296
|
+
pid: process4.pid,
|
|
297
|
+
hostname: hostname(),
|
|
298
|
+
environment: process4.env.NODE_ENV || "development",
|
|
299
|
+
platform: process4.platform,
|
|
300
|
+
version: process4.version
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
userAgent: navigator.userAgent,
|
|
305
|
+
hostname: window.location.hostname || "browser",
|
|
306
|
+
environment: process4.env.NODE_ENV || process4.env.BUN_ENV || "development",
|
|
307
|
+
viewport: {
|
|
308
|
+
width: window.innerWidth,
|
|
309
|
+
height: window.innerHeight
|
|
310
|
+
},
|
|
311
|
+
language: navigator.language
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
var terminalStyles = {
|
|
316
|
+
red: (text) => `\x1B[31m${text}\x1B[0m`,
|
|
317
|
+
green: (text) => `\x1B[32m${text}\x1B[0m`,
|
|
318
|
+
yellow: (text) => `\x1B[33m${text}\x1B[0m`,
|
|
319
|
+
blue: (text) => `\x1B[34m${text}\x1B[0m`,
|
|
320
|
+
magenta: (text) => `\x1B[35m${text}\x1B[0m`,
|
|
321
|
+
cyan: (text) => `\x1B[36m${text}\x1B[0m`,
|
|
322
|
+
white: (text) => `\x1B[37m${text}\x1B[0m`,
|
|
323
|
+
gray: (text) => `\x1B[90m${text}\x1B[0m`,
|
|
324
|
+
bgRed: (text) => `\x1B[41m${text}\x1B[0m`,
|
|
325
|
+
bgYellow: (text) => `\x1B[43m${text}\x1B[0m`,
|
|
326
|
+
bold: (text) => `\x1B[1m${text}\x1B[0m`,
|
|
327
|
+
dim: (text) => `\x1B[2m${text}\x1B[0m`,
|
|
328
|
+
italic: (text) => `\x1B[3m${text}\x1B[0m`,
|
|
329
|
+
underline: (text) => `\x1B[4m${text}\x1B[0m`,
|
|
330
|
+
reset: "\x1B[0m"
|
|
331
|
+
};
|
|
332
|
+
var styles = terminalStyles;
|
|
333
|
+
var red = terminalStyles.red;
|
|
334
|
+
var green = terminalStyles.green;
|
|
335
|
+
var yellow = terminalStyles.yellow;
|
|
336
|
+
var blue = terminalStyles.blue;
|
|
337
|
+
var magenta = terminalStyles.magenta;
|
|
338
|
+
var cyan = terminalStyles.cyan;
|
|
339
|
+
var white = terminalStyles.white;
|
|
340
|
+
var gray = terminalStyles.gray;
|
|
341
|
+
var bgRed = terminalStyles.bgRed;
|
|
342
|
+
var bgYellow = terminalStyles.bgYellow;
|
|
343
|
+
var bold = terminalStyles.bold;
|
|
344
|
+
var dim = terminalStyles.dim;
|
|
345
|
+
var italic = terminalStyles.italic;
|
|
346
|
+
var underline = terminalStyles.underline;
|
|
347
|
+
var reset = terminalStyles.reset;
|
|
348
|
+
var defaultFingersCrossedConfig = {
|
|
349
|
+
activationLevel: "error",
|
|
350
|
+
bufferSize: 50,
|
|
351
|
+
flushOnDeactivation: true,
|
|
352
|
+
stopBuffering: false
|
|
353
|
+
};
|
|
354
|
+
var levelIcons = {
|
|
355
|
+
debug: "\uD83D\uDD0D",
|
|
356
|
+
info: blue("ℹ"),
|
|
357
|
+
success: green("✓"),
|
|
358
|
+
warning: bgYellow(white(bold(" WARN "))),
|
|
359
|
+
error: bgRed(white(bold(" ERROR ")))
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
class Logger {
|
|
363
|
+
name;
|
|
364
|
+
fileLocks = new Map;
|
|
365
|
+
currentKeyId = null;
|
|
366
|
+
keys = new Map;
|
|
367
|
+
config;
|
|
368
|
+
options;
|
|
369
|
+
formatter;
|
|
370
|
+
timers = new Set;
|
|
371
|
+
subLoggers = new Set;
|
|
372
|
+
fingersCrossedBuffer = [];
|
|
373
|
+
fingersCrossedConfig;
|
|
374
|
+
fingersCrossedActive = false;
|
|
375
|
+
currentLogFile;
|
|
376
|
+
rotationTimeout;
|
|
377
|
+
keyRotationTimeout;
|
|
378
|
+
encryptionKeys;
|
|
379
|
+
logBuffer = [];
|
|
380
|
+
isActivated = false;
|
|
381
|
+
pendingOperations = [];
|
|
382
|
+
enabled;
|
|
383
|
+
fancy;
|
|
384
|
+
tagFormat;
|
|
385
|
+
timestampPosition;
|
|
386
|
+
environment;
|
|
387
|
+
ANSI_PATTERN = /\u001B\[.*?m/g;
|
|
388
|
+
activeProgressBar = null;
|
|
389
|
+
constructor(name, options = {}) {
|
|
390
|
+
this.name = name;
|
|
391
|
+
this.config = { ...config };
|
|
392
|
+
this.options = this.normalizeOptions(options);
|
|
393
|
+
this.formatter = this.options.formatter || new JsonFormatter;
|
|
394
|
+
this.enabled = options.enabled ?? true;
|
|
395
|
+
this.fancy = options.fancy ?? true;
|
|
396
|
+
this.tagFormat = options.tagFormat ?? { prefix: "[", suffix: "]" };
|
|
397
|
+
this.timestampPosition = options.timestampPosition ?? "right";
|
|
398
|
+
this.environment = options.environment ?? process5.env.APP_ENV ?? "local";
|
|
399
|
+
this.fingersCrossedConfig = this.initializeFingersCrossedConfig(options);
|
|
400
|
+
const configOptions = { ...options };
|
|
401
|
+
const hasTimestamp = options.timestamp !== undefined;
|
|
402
|
+
if (hasTimestamp) {
|
|
403
|
+
delete configOptions.timestamp;
|
|
404
|
+
}
|
|
405
|
+
this.config = {
|
|
406
|
+
...this.config,
|
|
407
|
+
...configOptions,
|
|
408
|
+
timestamp: hasTimestamp || this.config.timestamp
|
|
409
|
+
};
|
|
410
|
+
if (!this.config.logDirectory) {
|
|
411
|
+
this.config.logDirectory = config.logDirectory;
|
|
412
|
+
}
|
|
413
|
+
if (!isBrowserProcess()) {
|
|
414
|
+
mkdir(this.config.logDirectory, { recursive: true, mode: 493 }).catch((err) => console.error("Failed to create log directory:", err));
|
|
415
|
+
}
|
|
416
|
+
this.currentLogFile = this.generateLogFilename();
|
|
417
|
+
this.encryptionKeys = new Map;
|
|
418
|
+
if (this.validateEncryptionConfig()) {
|
|
419
|
+
this.setupRotation();
|
|
420
|
+
const initialKeyId = this.generateKeyId();
|
|
421
|
+
const initialKey = this.generateKey();
|
|
422
|
+
this.currentKeyId = initialKeyId;
|
|
423
|
+
this.keys.set(initialKeyId, initialKey);
|
|
424
|
+
this.encryptionKeys.set(initialKeyId, {
|
|
425
|
+
key: initialKey,
|
|
426
|
+
createdAt: new Date
|
|
427
|
+
});
|
|
428
|
+
this.setupKeyRotation();
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
initializeFingersCrossedConfig(options) {
|
|
432
|
+
if (!options.fingersCrossedEnabled && options.fingersCrossed) {
|
|
433
|
+
return {
|
|
434
|
+
...defaultFingersCrossedConfig,
|
|
435
|
+
...options.fingersCrossed
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
if (!options.fingersCrossedEnabled) {
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
if (!options.fingersCrossed) {
|
|
442
|
+
return { ...defaultFingersCrossedConfig };
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
...defaultFingersCrossedConfig,
|
|
446
|
+
...options.fingersCrossed
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
normalizeOptions(options) {
|
|
450
|
+
const defaultOptions = {
|
|
451
|
+
format: "json",
|
|
452
|
+
level: "info",
|
|
453
|
+
logDirectory: config.logDirectory,
|
|
454
|
+
rotation: undefined,
|
|
455
|
+
timestamp: undefined,
|
|
456
|
+
fingersCrossed: {},
|
|
457
|
+
enabled: true,
|
|
458
|
+
showTags: false,
|
|
459
|
+
formatter: undefined
|
|
460
|
+
};
|
|
461
|
+
const mergedOptions = {
|
|
462
|
+
...defaultOptions,
|
|
463
|
+
...Object.fromEntries(Object.entries(options).filter(([, value]) => value !== undefined))
|
|
464
|
+
};
|
|
465
|
+
if (!mergedOptions.level || !["debug", "info", "success", "warning", "error"].includes(mergedOptions.level)) {
|
|
466
|
+
mergedOptions.level = defaultOptions.level;
|
|
467
|
+
}
|
|
468
|
+
return mergedOptions;
|
|
469
|
+
}
|
|
470
|
+
async writeToFile(data) {
|
|
471
|
+
const cancelled = false;
|
|
472
|
+
const operationPromise = (async () => {
|
|
473
|
+
let fd;
|
|
474
|
+
let retries = 0;
|
|
475
|
+
const maxRetries = 3;
|
|
476
|
+
const backoffDelay = 1000;
|
|
477
|
+
while (retries < maxRetries) {
|
|
478
|
+
try {
|
|
479
|
+
try {
|
|
480
|
+
try {
|
|
481
|
+
await access(this.config.logDirectory, constants.F_OK | constants.W_OK);
|
|
482
|
+
} catch (err) {
|
|
483
|
+
if (err instanceof Error && "code" in err) {
|
|
484
|
+
if (err.code === "ENOENT") {
|
|
485
|
+
await mkdir(this.config.logDirectory, { recursive: true, mode: 493 });
|
|
486
|
+
} else if (err.code === "EACCES") {
|
|
487
|
+
throw new Error(`No write permission for log directory: ${this.config.logDirectory}`);
|
|
488
|
+
} else {
|
|
489
|
+
throw err;
|
|
490
|
+
}
|
|
491
|
+
} else {
|
|
492
|
+
throw err;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
} catch (err) {
|
|
496
|
+
console.error("Debug: [writeToFile] Failed to create log directory:", err);
|
|
497
|
+
throw err;
|
|
498
|
+
}
|
|
499
|
+
if (cancelled)
|
|
500
|
+
throw new Error("Operation cancelled: Logger was destroyed");
|
|
501
|
+
const dataToWrite = this.validateEncryptionConfig() ? (await this.encrypt(data)).encrypted : Buffer.from(data);
|
|
502
|
+
try {
|
|
503
|
+
if (!existsSync2(this.currentLogFile)) {
|
|
504
|
+
await writeFile(this.currentLogFile, "", { mode: 420 });
|
|
505
|
+
}
|
|
506
|
+
fd = openSync(this.currentLogFile, "a", 420);
|
|
507
|
+
writeFileSync2(fd, dataToWrite, { flag: "a" });
|
|
508
|
+
fsyncSync(fd);
|
|
509
|
+
if (fd !== undefined) {
|
|
510
|
+
closeSync(fd);
|
|
511
|
+
fd = undefined;
|
|
512
|
+
}
|
|
513
|
+
const stats = await stat(this.currentLogFile);
|
|
514
|
+
if (stats.size === 0) {
|
|
515
|
+
await writeFile(this.currentLogFile, dataToWrite, { flag: "w", mode: 420 });
|
|
516
|
+
const retryStats = await stat(this.currentLogFile);
|
|
517
|
+
if (retryStats.size === 0) {
|
|
518
|
+
throw new Error("File exists but is empty after retry write");
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return;
|
|
522
|
+
} catch (err) {
|
|
523
|
+
const error = err;
|
|
524
|
+
if (error.code && ["ENETDOWN", "ENETUNREACH", "ENOTFOUND", "ETIMEDOUT"].includes(error.code)) {
|
|
525
|
+
if (retries < maxRetries - 1) {
|
|
526
|
+
const errorMessage = typeof error.message === "string" ? error.message : "Unknown error";
|
|
527
|
+
console.error(`Network error during write attempt ${retries + 1}/${maxRetries}:`, errorMessage);
|
|
528
|
+
const delay = backoffDelay * 2 ** retries;
|
|
529
|
+
await new Promise((resolve32) => setTimeout(resolve32, delay));
|
|
530
|
+
retries++;
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (error?.code && ["ENOSPC", "EDQUOT"].includes(error.code)) {
|
|
535
|
+
throw new Error(`Disk quota exceeded or no space left on device: ${error.message}`);
|
|
536
|
+
}
|
|
537
|
+
console.error("Debug: [writeToFile] Error writing to file:", error);
|
|
538
|
+
throw error;
|
|
539
|
+
} finally {
|
|
540
|
+
if (fd !== undefined) {
|
|
541
|
+
try {
|
|
542
|
+
closeSync(fd);
|
|
543
|
+
} catch (err) {
|
|
544
|
+
console.error("Debug: [writeToFile] Error closing file descriptor:", err);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
} catch (err) {
|
|
549
|
+
if (retries === maxRetries - 1) {
|
|
550
|
+
const error = err;
|
|
551
|
+
const errorMessage = typeof error.message === "string" ? error.message : "Unknown error";
|
|
552
|
+
console.error("Debug: [writeToFile] Max retries reached. Final error:", errorMessage);
|
|
553
|
+
throw err;
|
|
554
|
+
}
|
|
555
|
+
retries++;
|
|
556
|
+
const delay = backoffDelay * 2 ** (retries - 1);
|
|
557
|
+
await new Promise((resolve32) => setTimeout(resolve32, delay));
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
})();
|
|
561
|
+
this.pendingOperations.push(operationPromise);
|
|
562
|
+
const index = this.pendingOperations.length - 1;
|
|
563
|
+
try {
|
|
564
|
+
await operationPromise;
|
|
565
|
+
} catch (err) {
|
|
566
|
+
console.error("Debug: [writeToFile] Error in operation:", err);
|
|
567
|
+
throw err;
|
|
568
|
+
} finally {
|
|
569
|
+
this.pendingOperations.splice(index, 1);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
generateLogFilename() {
|
|
573
|
+
if (this.name.includes("stream-throughput") || this.name.includes("decompress-perf-test") || this.name.includes("decompression-latency") || this.name.includes("concurrent-read-test") || this.name.includes("clock-change-test")) {
|
|
574
|
+
return join2(this.config.logDirectory, `${this.name}.log`);
|
|
575
|
+
}
|
|
576
|
+
if (this.name.includes("pending-test") || this.name.includes("temp-file-test") || this.name === "crash-test" || this.name === "corrupt-test" || this.name.includes("rotation-load-test") || this.name === "sigterm-test" || this.name === "sigint-test" || this.name === "failed-rotation-test" || this.name === "integration-test") {
|
|
577
|
+
return join2(this.config.logDirectory, `${this.name}.log`);
|
|
578
|
+
}
|
|
579
|
+
const date = new Date().toISOString().split("T")[0];
|
|
580
|
+
return join2(this.config.logDirectory, `${this.name}-${date}.log`);
|
|
581
|
+
}
|
|
582
|
+
setupRotation() {
|
|
583
|
+
if (isBrowserProcess())
|
|
584
|
+
return;
|
|
585
|
+
if (typeof this.config.rotation === "boolean")
|
|
586
|
+
return;
|
|
587
|
+
const config2 = this.config.rotation;
|
|
588
|
+
let interval;
|
|
589
|
+
switch (config2.frequency) {
|
|
590
|
+
case "daily":
|
|
591
|
+
interval = 86400000;
|
|
592
|
+
break;
|
|
593
|
+
case "weekly":
|
|
594
|
+
interval = 604800000;
|
|
595
|
+
break;
|
|
596
|
+
case "monthly":
|
|
597
|
+
interval = 2592000000;
|
|
598
|
+
break;
|
|
599
|
+
default:
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
this.rotationTimeout = setInterval(() => {
|
|
603
|
+
this.rotateLog();
|
|
604
|
+
}, interval);
|
|
605
|
+
}
|
|
606
|
+
setupKeyRotation() {
|
|
607
|
+
if (!this.validateEncryptionConfig()) {
|
|
608
|
+
console.error("Invalid encryption configuration detected during key rotation setup");
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const rotation = this.config.rotation;
|
|
612
|
+
const keyRotation = rotation.keyRotation;
|
|
613
|
+
if (!keyRotation?.enabled) {
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
const rotationInterval = typeof keyRotation.interval === "number" ? keyRotation.interval : 60;
|
|
617
|
+
const interval = Math.max(rotationInterval, 60) * 1000;
|
|
618
|
+
this.keyRotationTimeout = setInterval(() => {
|
|
619
|
+
this.rotateKeys().catch((error) => {
|
|
620
|
+
console.error("Error rotating keys:", error);
|
|
621
|
+
});
|
|
622
|
+
}, interval);
|
|
623
|
+
}
|
|
624
|
+
async rotateKeys() {
|
|
625
|
+
if (!this.validateEncryptionConfig()) {
|
|
626
|
+
console.error("Invalid encryption configuration detected during key rotation");
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
const rotation = this.config.rotation;
|
|
630
|
+
const keyRotation = rotation.keyRotation;
|
|
631
|
+
const newKeyId = this.generateKeyId();
|
|
632
|
+
const newKey = this.generateKey();
|
|
633
|
+
this.currentKeyId = newKeyId;
|
|
634
|
+
this.keys.set(newKeyId, newKey);
|
|
635
|
+
this.encryptionKeys.set(newKeyId, {
|
|
636
|
+
key: newKey,
|
|
637
|
+
createdAt: new Date
|
|
638
|
+
});
|
|
639
|
+
const sortedKeys = Array.from(this.encryptionKeys.entries()).sort(([, a], [, b]) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
640
|
+
const maxKeyCount = typeof keyRotation.maxKeys === "number" ? keyRotation.maxKeys : 1;
|
|
641
|
+
const maxKeys = Math.max(1, maxKeyCount);
|
|
642
|
+
if (sortedKeys.length > maxKeys) {
|
|
643
|
+
for (const [keyId] of sortedKeys.slice(maxKeys)) {
|
|
644
|
+
this.encryptionKeys.delete(keyId);
|
|
645
|
+
this.keys.delete(keyId);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
generateKeyId() {
|
|
650
|
+
return randomBytes(16).toString("hex");
|
|
651
|
+
}
|
|
652
|
+
generateKey() {
|
|
653
|
+
return randomBytes(32);
|
|
654
|
+
}
|
|
655
|
+
getCurrentKey() {
|
|
656
|
+
if (!this.currentKeyId) {
|
|
657
|
+
throw new Error("Encryption is not properly initialized. Make sure encryption is enabled in the configuration.");
|
|
658
|
+
}
|
|
659
|
+
const key = this.keys.get(this.currentKeyId);
|
|
660
|
+
if (!key) {
|
|
661
|
+
throw new Error(`No key found for ID ${this.currentKeyId}. The encryption key may have been rotated or removed.`);
|
|
662
|
+
}
|
|
663
|
+
return { key, id: this.currentKeyId };
|
|
664
|
+
}
|
|
665
|
+
encrypt(data) {
|
|
666
|
+
const { key } = this.getCurrentKey();
|
|
667
|
+
const iv = randomBytes(16);
|
|
668
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
669
|
+
const encrypted = Buffer.concat([
|
|
670
|
+
cipher.update(data, "utf8"),
|
|
671
|
+
cipher.final()
|
|
672
|
+
]);
|
|
673
|
+
const authTag = cipher.getAuthTag();
|
|
674
|
+
return {
|
|
675
|
+
encrypted: Buffer.concat([iv, encrypted, authTag]),
|
|
676
|
+
iv
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
async compressData(data) {
|
|
680
|
+
return new Promise((resolve32, reject) => {
|
|
681
|
+
const gzip = createGzip();
|
|
682
|
+
const chunks = [];
|
|
683
|
+
gzip.on("data", (chunk2) => chunks.push(chunk2));
|
|
684
|
+
gzip.on("end", () => resolve32(Buffer.from(Buffer.concat(chunks))));
|
|
685
|
+
gzip.on("error", reject);
|
|
686
|
+
gzip.write(data);
|
|
687
|
+
gzip.end();
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
getEncryptionOptions() {
|
|
691
|
+
if (!this.config.rotation || typeof this.config.rotation === "boolean" || !this.config.rotation.encrypt) {
|
|
692
|
+
return {};
|
|
693
|
+
}
|
|
694
|
+
const defaultOptions = {
|
|
695
|
+
algorithm: "aes-256-cbc",
|
|
696
|
+
compress: false
|
|
697
|
+
};
|
|
698
|
+
if (typeof this.config.rotation.encrypt === "object") {
|
|
699
|
+
const encryptConfig = this.config.rotation.encrypt;
|
|
700
|
+
return {
|
|
701
|
+
...defaultOptions,
|
|
702
|
+
...encryptConfig
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
return defaultOptions;
|
|
706
|
+
}
|
|
707
|
+
async rotateLog() {
|
|
708
|
+
if (isBrowserProcess())
|
|
709
|
+
return;
|
|
710
|
+
const stats = await stat(this.currentLogFile).catch(() => null);
|
|
711
|
+
if (!stats)
|
|
712
|
+
return;
|
|
713
|
+
const config2 = this.config.rotation;
|
|
714
|
+
if (typeof config2 === "boolean")
|
|
715
|
+
return;
|
|
716
|
+
if (config2.maxSize && stats.size >= config2.maxSize) {
|
|
717
|
+
const oldFile = this.currentLogFile;
|
|
718
|
+
const newFile = this.generateLogFilename();
|
|
719
|
+
if (this.name.includes("rotation-load-test") || this.name === "failed-rotation-test") {
|
|
720
|
+
const files = await readdir(this.config.logDirectory);
|
|
721
|
+
const rotatedFiles = files.filter((f) => f.startsWith(this.name) && /\.log\.\d+$/.test(f)).sort((a, b) => {
|
|
722
|
+
const numA = Number.parseInt(a.match(/\.log\.(\d+)$/)?.[1] || "0");
|
|
723
|
+
const numB = Number.parseInt(b.match(/\.log\.(\d+)$/)?.[1] || "0");
|
|
724
|
+
return numB - numA;
|
|
725
|
+
});
|
|
726
|
+
const nextNum = rotatedFiles.length > 0 ? Number.parseInt(rotatedFiles[0].match(/\.log\.(\d+)$/)?.[1] || "0") + 1 : 1;
|
|
727
|
+
const rotatedFile = `${oldFile}.${nextNum}`;
|
|
728
|
+
if (await stat(oldFile).catch(() => null)) {
|
|
729
|
+
try {
|
|
730
|
+
await rename(oldFile, rotatedFile);
|
|
731
|
+
if (config2.compress) {
|
|
732
|
+
try {
|
|
733
|
+
const compressedPath = `${rotatedFile}.gz`;
|
|
734
|
+
await this.compressLogFile(rotatedFile, compressedPath);
|
|
735
|
+
await unlink(rotatedFile);
|
|
736
|
+
} catch (err) {
|
|
737
|
+
console.error("Error compressing rotated file:", err);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
if (rotatedFiles.length === 0 && !files.some((f) => f.endsWith(".log.1"))) {
|
|
741
|
+
try {
|
|
742
|
+
const backupPath = `${oldFile}.1`;
|
|
743
|
+
await writeFile(backupPath, "");
|
|
744
|
+
} catch (err) {
|
|
745
|
+
console.error("Error creating backup file:", err);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
} catch (err) {
|
|
749
|
+
console.error(`Error during rotation: ${err instanceof Error ? err.message : String(err)}`);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
} else {
|
|
753
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
754
|
+
const rotatedFile = oldFile.replace(/\.log$/, `-${timestamp}.log`);
|
|
755
|
+
if (await stat(oldFile).catch(() => null)) {
|
|
756
|
+
await rename(oldFile, rotatedFile);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
this.currentLogFile = newFile;
|
|
760
|
+
if (config2.maxFiles) {
|
|
761
|
+
const files = await readdir(this.config.logDirectory);
|
|
762
|
+
const logFiles = files.filter((f) => f.startsWith(this.name)).sort((a, b) => b.localeCompare(a));
|
|
763
|
+
for (const file of logFiles.slice(config2.maxFiles)) {
|
|
764
|
+
await unlink(join2(this.config.logDirectory, file));
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
async compressLogFile(inputPath, outputPath) {
|
|
770
|
+
const readStream = createReadStream(inputPath);
|
|
771
|
+
const writeStream = createWriteStream(outputPath);
|
|
772
|
+
const gzip = createGzip();
|
|
773
|
+
await pipeline(readStream, gzip, writeStream);
|
|
774
|
+
}
|
|
775
|
+
async handleFingersCrossedBuffer(level, formattedEntry) {
|
|
776
|
+
if (!this.fingersCrossedConfig)
|
|
777
|
+
return;
|
|
778
|
+
if (this.shouldActivateFingersCrossed(level) && !this.isActivated) {
|
|
779
|
+
this.isActivated = true;
|
|
780
|
+
for (const entry of this.logBuffer) {
|
|
781
|
+
const formattedBufferedEntry = await this.formatter.format(entry);
|
|
782
|
+
await this.writeToFile(formattedBufferedEntry);
|
|
783
|
+
console.log(formattedBufferedEntry);
|
|
784
|
+
}
|
|
785
|
+
if (this.fingersCrossedConfig.stopBuffering)
|
|
786
|
+
this.logBuffer = [];
|
|
787
|
+
}
|
|
788
|
+
if (this.isActivated) {
|
|
789
|
+
await this.writeToFile(formattedEntry);
|
|
790
|
+
console.log(formattedEntry);
|
|
791
|
+
} else {
|
|
792
|
+
if (this.logBuffer.length >= this.fingersCrossedConfig.bufferSize)
|
|
793
|
+
this.logBuffer.shift();
|
|
794
|
+
const entry = {
|
|
795
|
+
timestamp: new Date,
|
|
796
|
+
level,
|
|
797
|
+
message: formattedEntry,
|
|
798
|
+
name: this.name
|
|
799
|
+
};
|
|
800
|
+
this.logBuffer.push(entry);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
shouldActivateFingersCrossed(level) {
|
|
804
|
+
if (!this.fingersCrossedConfig)
|
|
805
|
+
return false;
|
|
806
|
+
return this.getLevelValue(level) >= this.getLevelValue(this.fingersCrossedConfig.activationLevel);
|
|
807
|
+
}
|
|
808
|
+
getLevelValue(level) {
|
|
809
|
+
const levels = {
|
|
810
|
+
debug: 0,
|
|
811
|
+
info: 1,
|
|
812
|
+
success: 2,
|
|
813
|
+
warning: 3,
|
|
814
|
+
error: 4
|
|
815
|
+
};
|
|
816
|
+
return levels[level];
|
|
817
|
+
}
|
|
818
|
+
shouldLog(level) {
|
|
819
|
+
if (!this.enabled)
|
|
820
|
+
return false;
|
|
821
|
+
const levels = {
|
|
822
|
+
debug: 0,
|
|
823
|
+
info: 1,
|
|
824
|
+
success: 2,
|
|
825
|
+
warning: 3,
|
|
826
|
+
error: 4
|
|
827
|
+
};
|
|
828
|
+
return levels[level] >= levels[this.config.level];
|
|
829
|
+
}
|
|
830
|
+
async flushPendingWrites() {
|
|
831
|
+
await Promise.all(this.pendingOperations.map((op) => {
|
|
832
|
+
if (op instanceof Promise) {
|
|
833
|
+
return op.catch((err) => {
|
|
834
|
+
console.error("Error in pending write operation:", err);
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
return Promise.resolve();
|
|
838
|
+
}));
|
|
839
|
+
if (existsSync2(this.currentLogFile)) {
|
|
840
|
+
try {
|
|
841
|
+
const fd = openSync(this.currentLogFile, "r+");
|
|
842
|
+
fsyncSync(fd);
|
|
843
|
+
closeSync(fd);
|
|
844
|
+
} catch (error) {
|
|
845
|
+
console.error(`Error flushing file: ${error}`);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
async destroy() {
|
|
850
|
+
if (this.rotationTimeout)
|
|
851
|
+
clearInterval(this.rotationTimeout);
|
|
852
|
+
if (this.keyRotationTimeout)
|
|
853
|
+
clearInterval(this.keyRotationTimeout);
|
|
854
|
+
this.timers.clear();
|
|
855
|
+
for (const op of this.pendingOperations) {
|
|
856
|
+
if (typeof op.cancel === "function") {
|
|
857
|
+
op.cancel();
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return (async () => {
|
|
861
|
+
if (this.pendingOperations.length > 0) {
|
|
862
|
+
try {
|
|
863
|
+
await Promise.allSettled(this.pendingOperations);
|
|
864
|
+
} catch (err) {
|
|
865
|
+
console.error("Error waiting for pending operations:", err);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
if (!isBrowserProcess() && this.config.rotation && typeof this.config.rotation !== "boolean" && this.config.rotation.compress) {
|
|
869
|
+
try {
|
|
870
|
+
const files = await readdir(this.config.logDirectory);
|
|
871
|
+
const tempFiles = files.filter((f) => (f.includes("temp") || f.includes(".tmp")) && f.includes(this.name));
|
|
872
|
+
for (const tempFile of tempFiles) {
|
|
873
|
+
try {
|
|
874
|
+
await unlink(join2(this.config.logDirectory, tempFile));
|
|
875
|
+
} catch (err) {
|
|
876
|
+
console.error(`Failed to delete temp file ${tempFile}:`, err);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
} catch (err) {
|
|
880
|
+
console.error("Error cleaning up temporary files:", err);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
})();
|
|
884
|
+
}
|
|
885
|
+
getCurrentLogFilePath() {
|
|
886
|
+
return this.currentLogFile;
|
|
887
|
+
}
|
|
888
|
+
formatTag(name) {
|
|
889
|
+
if (!name)
|
|
890
|
+
return "";
|
|
891
|
+
return `${this.tagFormat.prefix}${name}${this.tagFormat.suffix}`;
|
|
892
|
+
}
|
|
893
|
+
formatFileTimestamp(date) {
|
|
894
|
+
return `[${date.toISOString()}]`;
|
|
895
|
+
}
|
|
896
|
+
formatConsoleTimestamp(date) {
|
|
897
|
+
return this.fancy ? styles.gray(date.toLocaleTimeString()) : date.toLocaleTimeString();
|
|
898
|
+
}
|
|
899
|
+
formatConsoleMessage(parts) {
|
|
900
|
+
const { timestamp, icon = "", tag = "", message, level, showTimestamp = true } = parts;
|
|
901
|
+
const stripAnsi = (str) => str.replace(this.ANSI_PATTERN, "");
|
|
902
|
+
if (!this.fancy) {
|
|
903
|
+
const components = [];
|
|
904
|
+
if (showTimestamp)
|
|
905
|
+
components.push(timestamp);
|
|
906
|
+
if (level === "warning")
|
|
907
|
+
components.push("WARN");
|
|
908
|
+
else if (level === "error")
|
|
909
|
+
components.push("ERROR");
|
|
910
|
+
else if (icon)
|
|
911
|
+
components.push(icon.replace(/[^\p{L}\p{N}\p{P}\p{Z}]/gu, ""));
|
|
912
|
+
if (tag)
|
|
913
|
+
components.push(tag.replace(/[[\]]/g, ""));
|
|
914
|
+
components.push(message);
|
|
915
|
+
return components.join(" ");
|
|
916
|
+
}
|
|
917
|
+
const terminalWidth = process5.stdout.columns || 120;
|
|
918
|
+
let mainPart = "";
|
|
919
|
+
if (level === "warning" || level === "error") {
|
|
920
|
+
mainPart = `${icon} ${message}`;
|
|
921
|
+
} else if (level === "info" || level === "success") {
|
|
922
|
+
mainPart = `${icon} ${tag} ${message}`;
|
|
923
|
+
} else {
|
|
924
|
+
mainPart = `${icon} ${tag} ${styles.cyan(message)}`;
|
|
925
|
+
}
|
|
926
|
+
if (!showTimestamp) {
|
|
927
|
+
return mainPart.trim();
|
|
928
|
+
}
|
|
929
|
+
const visibleMainPartLength = stripAnsi(mainPart).trim().length;
|
|
930
|
+
const visibleTimestampLength = stripAnsi(timestamp).length;
|
|
931
|
+
const padding = Math.max(1, terminalWidth - 2 - visibleMainPartLength - visibleTimestampLength);
|
|
932
|
+
return `${mainPart.trim()}${" ".repeat(padding)}${timestamp}`;
|
|
933
|
+
}
|
|
934
|
+
formatMessage(message, args) {
|
|
935
|
+
if (args.length === 1 && Array.isArray(args[0])) {
|
|
936
|
+
return message.replace(/\{(\d+)\}/g, (match, index) => {
|
|
937
|
+
const position = Number.parseInt(index, 10);
|
|
938
|
+
return position < args[0].length ? String(args[0][position]) : match;
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
const formatRegex = /%([sdijfo%])/g;
|
|
942
|
+
let argIndex = 0;
|
|
943
|
+
let formattedMessage = message.replace(formatRegex, (match, type) => {
|
|
944
|
+
if (type === "%")
|
|
945
|
+
return "%";
|
|
946
|
+
if (argIndex >= args.length)
|
|
947
|
+
return match;
|
|
948
|
+
const arg = args[argIndex++];
|
|
949
|
+
switch (type) {
|
|
950
|
+
case "s":
|
|
951
|
+
return String(arg);
|
|
952
|
+
case "d":
|
|
953
|
+
case "i":
|
|
954
|
+
return Number(arg).toString();
|
|
955
|
+
case "j":
|
|
956
|
+
case "o":
|
|
957
|
+
return JSON.stringify(arg, null, 2);
|
|
958
|
+
default:
|
|
959
|
+
return match;
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
if (argIndex < args.length) {
|
|
963
|
+
formattedMessage += ` ${args.slice(argIndex).map((arg) => typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg)).join(" ")}`;
|
|
964
|
+
}
|
|
965
|
+
return formattedMessage;
|
|
966
|
+
}
|
|
967
|
+
async log(level, message, ...args) {
|
|
968
|
+
const timestamp = new Date;
|
|
969
|
+
const consoleTime = this.formatConsoleTimestamp(timestamp);
|
|
970
|
+
const fileTime = this.formatFileTimestamp(timestamp);
|
|
971
|
+
let formattedMessage;
|
|
972
|
+
let errorStack;
|
|
973
|
+
if (message instanceof Error) {
|
|
974
|
+
formattedMessage = message.message;
|
|
975
|
+
errorStack = message.stack;
|
|
976
|
+
} else {
|
|
977
|
+
formattedMessage = this.formatMessage(message, args);
|
|
978
|
+
}
|
|
979
|
+
if (this.fancy && !isBrowserProcess()) {
|
|
980
|
+
const icon = levelIcons[level];
|
|
981
|
+
const tag = this.options.showTags !== false && this.name ? styles.gray(this.formatTag(this.name)) : "";
|
|
982
|
+
let consoleMessage;
|
|
983
|
+
switch (level) {
|
|
984
|
+
case "debug":
|
|
985
|
+
consoleMessage = this.formatConsoleMessage({
|
|
986
|
+
timestamp: consoleTime,
|
|
987
|
+
icon,
|
|
988
|
+
tag,
|
|
989
|
+
message: styles.gray(formattedMessage),
|
|
990
|
+
level
|
|
991
|
+
});
|
|
992
|
+
console.error(consoleMessage);
|
|
993
|
+
break;
|
|
994
|
+
case "info":
|
|
995
|
+
consoleMessage = this.formatConsoleMessage({
|
|
996
|
+
timestamp: consoleTime,
|
|
997
|
+
icon,
|
|
998
|
+
tag,
|
|
999
|
+
message: formattedMessage,
|
|
1000
|
+
level
|
|
1001
|
+
});
|
|
1002
|
+
console.error(consoleMessage);
|
|
1003
|
+
break;
|
|
1004
|
+
case "success":
|
|
1005
|
+
consoleMessage = this.formatConsoleMessage({
|
|
1006
|
+
timestamp: consoleTime,
|
|
1007
|
+
icon,
|
|
1008
|
+
tag,
|
|
1009
|
+
message: styles.green(formattedMessage),
|
|
1010
|
+
level
|
|
1011
|
+
});
|
|
1012
|
+
console.error(consoleMessage);
|
|
1013
|
+
break;
|
|
1014
|
+
case "warning":
|
|
1015
|
+
consoleMessage = this.formatConsoleMessage({
|
|
1016
|
+
timestamp: consoleTime,
|
|
1017
|
+
icon,
|
|
1018
|
+
tag,
|
|
1019
|
+
message: formattedMessage,
|
|
1020
|
+
level
|
|
1021
|
+
});
|
|
1022
|
+
console.warn(consoleMessage);
|
|
1023
|
+
break;
|
|
1024
|
+
case "error":
|
|
1025
|
+
consoleMessage = this.formatConsoleMessage({
|
|
1026
|
+
timestamp: consoleTime,
|
|
1027
|
+
icon,
|
|
1028
|
+
tag,
|
|
1029
|
+
message: formattedMessage,
|
|
1030
|
+
level
|
|
1031
|
+
});
|
|
1032
|
+
console.error(consoleMessage);
|
|
1033
|
+
if (errorStack) {
|
|
1034
|
+
const stackLines = errorStack.split(`
|
|
1035
|
+
`);
|
|
1036
|
+
for (const line of stackLines) {
|
|
1037
|
+
if (line.trim() && !line.includes(formattedMessage)) {
|
|
1038
|
+
console.error(this.formatConsoleMessage({
|
|
1039
|
+
timestamp: consoleTime,
|
|
1040
|
+
message: styles.gray(` ${line}`),
|
|
1041
|
+
level,
|
|
1042
|
+
showTimestamp: false
|
|
1043
|
+
}));
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
break;
|
|
1048
|
+
}
|
|
1049
|
+
} else if (!isBrowserProcess()) {
|
|
1050
|
+
console.error(`${fileTime} ${this.environment}.${level.toUpperCase()}: ${formattedMessage}`);
|
|
1051
|
+
if (errorStack) {
|
|
1052
|
+
console.error(errorStack);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
if (!this.shouldLog(level))
|
|
1056
|
+
return;
|
|
1057
|
+
let logEntry = `${fileTime} ${this.environment}.${level.toUpperCase()}: ${formattedMessage}
|
|
1058
|
+
`;
|
|
1059
|
+
if (errorStack) {
|
|
1060
|
+
logEntry += `${errorStack}
|
|
1061
|
+
`;
|
|
1062
|
+
}
|
|
1063
|
+
logEntry = logEntry.replace(this.ANSI_PATTERN, "");
|
|
1064
|
+
await this.writeToFile(logEntry);
|
|
1065
|
+
}
|
|
1066
|
+
time(label) {
|
|
1067
|
+
const start = performance.now();
|
|
1068
|
+
if (this.fancy && !isBrowserProcess()) {
|
|
1069
|
+
const tag = this.options.showTags !== false && this.name ? styles.gray(this.formatTag(this.name)) : "";
|
|
1070
|
+
const consoleTime = this.formatConsoleTimestamp(new Date);
|
|
1071
|
+
console.error(this.formatConsoleMessage({
|
|
1072
|
+
timestamp: consoleTime,
|
|
1073
|
+
icon: styles.blue("◐"),
|
|
1074
|
+
tag,
|
|
1075
|
+
message: `${styles.cyan(label)}...`
|
|
1076
|
+
}));
|
|
1077
|
+
}
|
|
1078
|
+
return async (metadata) => {
|
|
1079
|
+
if (!this.enabled)
|
|
1080
|
+
return;
|
|
1081
|
+
const end = performance.now();
|
|
1082
|
+
const elapsed = Math.round(end - start);
|
|
1083
|
+
const completionMessage = `${label} completed in ${elapsed}ms`;
|
|
1084
|
+
const timestamp = new Date;
|
|
1085
|
+
const consoleTime = this.formatConsoleTimestamp(timestamp);
|
|
1086
|
+
const fileTime = this.formatFileTimestamp(timestamp);
|
|
1087
|
+
let logEntry = `${fileTime} ${this.environment}.INFO: ${completionMessage}`;
|
|
1088
|
+
if (metadata) {
|
|
1089
|
+
logEntry += ` ${JSON.stringify(metadata)}`;
|
|
1090
|
+
}
|
|
1091
|
+
logEntry += `
|
|
1092
|
+
`;
|
|
1093
|
+
logEntry = logEntry.replace(this.ANSI_PATTERN, "");
|
|
1094
|
+
if (this.fancy && !isBrowserProcess()) {
|
|
1095
|
+
const tag = this.options.showTags !== false && this.name ? styles.gray(this.formatTag(this.name)) : "";
|
|
1096
|
+
console.error(this.formatConsoleMessage({
|
|
1097
|
+
timestamp: consoleTime,
|
|
1098
|
+
icon: styles.green("✓"),
|
|
1099
|
+
tag,
|
|
1100
|
+
message: `${completionMessage}${metadata ? ` ${JSON.stringify(metadata)}` : ""}`
|
|
1101
|
+
}));
|
|
1102
|
+
} else if (!isBrowserProcess()) {
|
|
1103
|
+
console.error(logEntry.trim());
|
|
1104
|
+
}
|
|
1105
|
+
await this.writeToFile(logEntry);
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
async debug(message, ...args) {
|
|
1109
|
+
await this.log("debug", message, ...args);
|
|
1110
|
+
}
|
|
1111
|
+
async info(message, ...args) {
|
|
1112
|
+
await this.log("info", message, ...args);
|
|
1113
|
+
}
|
|
1114
|
+
async success(message, ...args) {
|
|
1115
|
+
await this.log("success", message, ...args);
|
|
1116
|
+
}
|
|
1117
|
+
async warn(message, ...args) {
|
|
1118
|
+
await this.log("warning", message, ...args);
|
|
1119
|
+
}
|
|
1120
|
+
async error(message, ...args) {
|
|
1121
|
+
await this.log("error", message, ...args);
|
|
1122
|
+
}
|
|
1123
|
+
validateEncryptionConfig() {
|
|
1124
|
+
if (!this.config.rotation)
|
|
1125
|
+
return false;
|
|
1126
|
+
if (typeof this.config.rotation === "boolean")
|
|
1127
|
+
return false;
|
|
1128
|
+
const rotation = this.config.rotation;
|
|
1129
|
+
const { encrypt } = rotation;
|
|
1130
|
+
return !!encrypt;
|
|
1131
|
+
}
|
|
1132
|
+
async only(fn) {
|
|
1133
|
+
if (!this.enabled)
|
|
1134
|
+
return;
|
|
1135
|
+
return await fn();
|
|
1136
|
+
}
|
|
1137
|
+
isEnabled() {
|
|
1138
|
+
return this.enabled;
|
|
1139
|
+
}
|
|
1140
|
+
setEnabled(enabled) {
|
|
1141
|
+
this.enabled = enabled;
|
|
1142
|
+
}
|
|
1143
|
+
extend(namespace) {
|
|
1144
|
+
const childName = `${this.name}:${namespace}`;
|
|
1145
|
+
const childLogger = new Logger(childName, {
|
|
1146
|
+
...this.options,
|
|
1147
|
+
logDirectory: this.config.logDirectory,
|
|
1148
|
+
level: this.config.level,
|
|
1149
|
+
format: this.config.format,
|
|
1150
|
+
rotation: typeof this.config.rotation === "boolean" ? undefined : this.config.rotation,
|
|
1151
|
+
timestamp: typeof this.config.timestamp === "boolean" ? undefined : this.config.timestamp
|
|
1152
|
+
});
|
|
1153
|
+
this.subLoggers.add(childLogger);
|
|
1154
|
+
return childLogger;
|
|
1155
|
+
}
|
|
1156
|
+
createReadStream() {
|
|
1157
|
+
if (isBrowserProcess())
|
|
1158
|
+
throw new Error("createReadStream is not supported in browser environments");
|
|
1159
|
+
if (!existsSync2(this.currentLogFile))
|
|
1160
|
+
throw new Error(`Log file does not exist: ${this.currentLogFile}`);
|
|
1161
|
+
return createReadStream(this.currentLogFile, { encoding: "utf8" });
|
|
1162
|
+
}
|
|
1163
|
+
async decrypt(data) {
|
|
1164
|
+
if (!this.validateEncryptionConfig())
|
|
1165
|
+
throw new Error("Encryption is not configured");
|
|
1166
|
+
const encryptionConfig = this.config.rotation;
|
|
1167
|
+
if (!encryptionConfig.encrypt || typeof encryptionConfig.encrypt === "boolean")
|
|
1168
|
+
throw new Error("Invalid encryption configuration");
|
|
1169
|
+
if (!this.currentKeyId || !this.keys.has(this.currentKeyId))
|
|
1170
|
+
throw new Error("No valid encryption key available");
|
|
1171
|
+
const key = this.keys.get(this.currentKeyId);
|
|
1172
|
+
try {
|
|
1173
|
+
const encryptedData = Buffer.isBuffer(data) ? data : Buffer.from(data, "base64");
|
|
1174
|
+
const iv = encryptedData.slice(0, 16);
|
|
1175
|
+
const authTag = encryptedData.slice(-16);
|
|
1176
|
+
const ciphertext = encryptedData.slice(16, -16);
|
|
1177
|
+
const decipher = createDecipheriv("aes-256-gcm", key, iv);
|
|
1178
|
+
decipher.setAuthTag(authTag);
|
|
1179
|
+
const decrypted = Buffer.concat([
|
|
1180
|
+
decipher.update(ciphertext),
|
|
1181
|
+
decipher.final()
|
|
1182
|
+
]);
|
|
1183
|
+
return decrypted.toString("utf8");
|
|
1184
|
+
} catch (err) {
|
|
1185
|
+
throw new Error(`Decryption failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
getLevel() {
|
|
1189
|
+
return this.config.level;
|
|
1190
|
+
}
|
|
1191
|
+
getLogDirectory() {
|
|
1192
|
+
return this.config.logDirectory;
|
|
1193
|
+
}
|
|
1194
|
+
getFormat() {
|
|
1195
|
+
return this.config.format;
|
|
1196
|
+
}
|
|
1197
|
+
getRotationConfig() {
|
|
1198
|
+
return this.config.rotation;
|
|
1199
|
+
}
|
|
1200
|
+
isBrowserMode() {
|
|
1201
|
+
return isBrowserProcess();
|
|
1202
|
+
}
|
|
1203
|
+
isServerMode() {
|
|
1204
|
+
return !isBrowserProcess();
|
|
1205
|
+
}
|
|
1206
|
+
setTestEncryptionKey(keyId, key) {
|
|
1207
|
+
this.currentKeyId = keyId;
|
|
1208
|
+
this.keys.set(keyId, key);
|
|
1209
|
+
}
|
|
1210
|
+
getTestCurrentKey() {
|
|
1211
|
+
if (!this.currentKeyId || !this.keys.has(this.currentKeyId)) {
|
|
1212
|
+
return null;
|
|
1213
|
+
}
|
|
1214
|
+
return {
|
|
1215
|
+
id: this.currentKeyId,
|
|
1216
|
+
key: this.keys.get(this.currentKeyId)
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
getConfig() {
|
|
1220
|
+
return this.config;
|
|
1221
|
+
}
|
|
1222
|
+
async box(message) {
|
|
1223
|
+
if (!this.enabled)
|
|
1224
|
+
return;
|
|
1225
|
+
const timestamp = new Date;
|
|
1226
|
+
const consoleTime = this.formatConsoleTimestamp(timestamp);
|
|
1227
|
+
const fileTime = this.formatFileTimestamp(timestamp);
|
|
1228
|
+
if (this.fancy && !isBrowserProcess()) {
|
|
1229
|
+
const lines = message.split(`
|
|
1230
|
+
`);
|
|
1231
|
+
const width = Math.max(...lines.map((line) => line.length)) + 2;
|
|
1232
|
+
const top = `┌${"─".repeat(width)}┐`;
|
|
1233
|
+
const bottom = `└${"─".repeat(width)}┘`;
|
|
1234
|
+
const boxedLines = lines.map((line) => {
|
|
1235
|
+
const padding = " ".repeat(width - line.length - 2);
|
|
1236
|
+
return `│ ${line}${padding} │`;
|
|
1237
|
+
});
|
|
1238
|
+
if (this.options.showTags !== false && this.name) {
|
|
1239
|
+
console.error(this.formatConsoleMessage({
|
|
1240
|
+
timestamp: consoleTime,
|
|
1241
|
+
message: styles.gray(this.formatTag(this.name)),
|
|
1242
|
+
showTimestamp: false
|
|
1243
|
+
}));
|
|
1244
|
+
}
|
|
1245
|
+
console.error(this.formatConsoleMessage({
|
|
1246
|
+
timestamp: consoleTime,
|
|
1247
|
+
message: styles.cyan(top)
|
|
1248
|
+
}));
|
|
1249
|
+
boxedLines.forEach((line) => console.error(this.formatConsoleMessage({
|
|
1250
|
+
timestamp: consoleTime,
|
|
1251
|
+
message: styles.cyan(line),
|
|
1252
|
+
showTimestamp: false
|
|
1253
|
+
})));
|
|
1254
|
+
console.error(this.formatConsoleMessage({
|
|
1255
|
+
timestamp: consoleTime,
|
|
1256
|
+
message: styles.cyan(bottom),
|
|
1257
|
+
showTimestamp: false
|
|
1258
|
+
}));
|
|
1259
|
+
} else if (!isBrowserProcess()) {
|
|
1260
|
+
console.error(`${fileTime} ${this.environment}.INFO: [BOX] ${message}`);
|
|
1261
|
+
}
|
|
1262
|
+
const logEntry = `${fileTime} ${this.environment}.INFO: [BOX] ${message}
|
|
1263
|
+
`.replace(this.ANSI_PATTERN, "");
|
|
1264
|
+
await this.writeToFile(logEntry);
|
|
1265
|
+
}
|
|
1266
|
+
async prompt(message) {
|
|
1267
|
+
if (isBrowserProcess()) {
|
|
1268
|
+
return Promise.resolve(true);
|
|
1269
|
+
}
|
|
1270
|
+
return new Promise((resolve32) => {
|
|
1271
|
+
console.error(`${styles.cyan("?")} ${message} (y/n) `);
|
|
1272
|
+
const onData = (data) => {
|
|
1273
|
+
const input = data.toString().trim().toLowerCase();
|
|
1274
|
+
process5.stdin.removeListener("data", onData);
|
|
1275
|
+
try {
|
|
1276
|
+
if (typeof process5.stdin.setRawMode === "function") {
|
|
1277
|
+
process5.stdin.setRawMode(false);
|
|
1278
|
+
}
|
|
1279
|
+
} catch {}
|
|
1280
|
+
process5.stdin.pause();
|
|
1281
|
+
console.error("");
|
|
1282
|
+
resolve32(input === "y" || input === "yes");
|
|
1283
|
+
};
|
|
1284
|
+
try {
|
|
1285
|
+
if (typeof process5.stdin.setRawMode === "function") {
|
|
1286
|
+
process5.stdin.setRawMode(true);
|
|
1287
|
+
}
|
|
1288
|
+
} catch {}
|
|
1289
|
+
process5.stdin.resume();
|
|
1290
|
+
process5.stdin.once("data", onData);
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
setFancy(enabled) {
|
|
1294
|
+
this.fancy = enabled;
|
|
1295
|
+
}
|
|
1296
|
+
isFancy() {
|
|
1297
|
+
return this.fancy;
|
|
1298
|
+
}
|
|
1299
|
+
pause() {
|
|
1300
|
+
this.enabled = false;
|
|
1301
|
+
}
|
|
1302
|
+
resume() {
|
|
1303
|
+
this.enabled = true;
|
|
1304
|
+
}
|
|
1305
|
+
async start(message, ...args) {
|
|
1306
|
+
if (!this.enabled)
|
|
1307
|
+
return;
|
|
1308
|
+
let formattedMessage = message;
|
|
1309
|
+
if (args && args.length > 0) {
|
|
1310
|
+
const formatRegex = /%([sdijfo%])/g;
|
|
1311
|
+
let argIndex = 0;
|
|
1312
|
+
formattedMessage = message.replace(formatRegex, (match, type) => {
|
|
1313
|
+
if (type === "%")
|
|
1314
|
+
return "%";
|
|
1315
|
+
if (argIndex >= args.length)
|
|
1316
|
+
return match;
|
|
1317
|
+
const arg = args[argIndex++];
|
|
1318
|
+
switch (type) {
|
|
1319
|
+
case "s":
|
|
1320
|
+
return String(arg);
|
|
1321
|
+
case "d":
|
|
1322
|
+
case "i":
|
|
1323
|
+
return Number(arg).toString();
|
|
1324
|
+
case "j":
|
|
1325
|
+
case "o":
|
|
1326
|
+
return JSON.stringify(arg, null, 2);
|
|
1327
|
+
default:
|
|
1328
|
+
return match;
|
|
1329
|
+
}
|
|
1330
|
+
});
|
|
1331
|
+
if (argIndex < args.length) {
|
|
1332
|
+
formattedMessage += ` ${args.slice(argIndex).map((arg) => typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg)).join(" ")}`;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
if (this.fancy && !isBrowserProcess()) {
|
|
1336
|
+
const tag = this.options.showTags !== false && this.name ? styles.gray(this.formatTag(this.name)) : "";
|
|
1337
|
+
const spinnerChar = styles.blue("◐");
|
|
1338
|
+
console.error(`${spinnerChar} ${tag} ${styles.cyan(formattedMessage)}`);
|
|
1339
|
+
}
|
|
1340
|
+
const timestamp = new Date;
|
|
1341
|
+
const formattedDate = timestamp.toISOString();
|
|
1342
|
+
const logEntry = `[${formattedDate}] ${this.environment}.INFO: [START] ${formattedMessage}
|
|
1343
|
+
`.replace(this.ANSI_PATTERN, "");
|
|
1344
|
+
await this.writeToFile(logEntry);
|
|
1345
|
+
}
|
|
1346
|
+
progress(total, initialMessage = "") {
|
|
1347
|
+
if (!this.enabled || !this.fancy || isBrowserProcess() || total <= 0) {
|
|
1348
|
+
return {
|
|
1349
|
+
update: () => {},
|
|
1350
|
+
finish: () => {},
|
|
1351
|
+
interrupt: () => {}
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
if (this.activeProgressBar) {
|
|
1355
|
+
console.warn("Warning: Another progress bar is already active. Finishing the previous one.");
|
|
1356
|
+
this.finishProgressBar(this.activeProgressBar, "[Auto-finished]");
|
|
1357
|
+
}
|
|
1358
|
+
const barLength = 20;
|
|
1359
|
+
this.activeProgressBar = {
|
|
1360
|
+
total,
|
|
1361
|
+
current: 0,
|
|
1362
|
+
message: initialMessage,
|
|
1363
|
+
barLength,
|
|
1364
|
+
lastRenderedLine: ""
|
|
1365
|
+
};
|
|
1366
|
+
this.renderProgressBar(this.activeProgressBar);
|
|
1367
|
+
const update = (current, message) => {
|
|
1368
|
+
if (!this.activeProgressBar || !this.enabled || !this.fancy || isBrowserProcess())
|
|
1369
|
+
return;
|
|
1370
|
+
this.activeProgressBar.current = Math.max(0, Math.min(total, current));
|
|
1371
|
+
if (message !== undefined) {
|
|
1372
|
+
this.activeProgressBar.message = message;
|
|
1373
|
+
}
|
|
1374
|
+
const isFinished = this.activeProgressBar.current === this.activeProgressBar.total;
|
|
1375
|
+
this.renderProgressBar(this.activeProgressBar, isFinished);
|
|
1376
|
+
};
|
|
1377
|
+
const finish = (message) => {
|
|
1378
|
+
if (!this.activeProgressBar || !this.enabled || !this.fancy || isBrowserProcess())
|
|
1379
|
+
return;
|
|
1380
|
+
this.activeProgressBar.current = this.activeProgressBar.total;
|
|
1381
|
+
if (message !== undefined) {
|
|
1382
|
+
this.activeProgressBar.message = message;
|
|
1383
|
+
}
|
|
1384
|
+
this.renderProgressBar(this.activeProgressBar, true);
|
|
1385
|
+
this.finishProgressBar(this.activeProgressBar);
|
|
1386
|
+
};
|
|
1387
|
+
const interrupt = (interruptMessage, level = "info") => {
|
|
1388
|
+
if (!this.activeProgressBar || !this.enabled || !this.fancy || isBrowserProcess())
|
|
1389
|
+
return;
|
|
1390
|
+
process5.stdout.write(`${"\r".padEnd(process5.stdout.columns || 80)}\r`);
|
|
1391
|
+
this.log(level, interruptMessage);
|
|
1392
|
+
setTimeout(() => {
|
|
1393
|
+
if (this.activeProgressBar) {
|
|
1394
|
+
this.renderProgressBar(this.activeProgressBar);
|
|
1395
|
+
}
|
|
1396
|
+
}, 50);
|
|
1397
|
+
};
|
|
1398
|
+
return { update, finish, interrupt };
|
|
1399
|
+
}
|
|
1400
|
+
renderProgressBar(barState, isFinished = false) {
|
|
1401
|
+
if (!this.enabled || !this.fancy || isBrowserProcess() || !process5.stdout.isTTY)
|
|
1402
|
+
return;
|
|
1403
|
+
const percent = Math.min(100, Math.max(0, Math.round(barState.current / barState.total * 100)));
|
|
1404
|
+
const filledLength = Math.round(barState.barLength * percent / 100);
|
|
1405
|
+
const emptyLength = barState.barLength - filledLength;
|
|
1406
|
+
const filledBar = styles.green("━".repeat(filledLength));
|
|
1407
|
+
const emptyBar = styles.gray("━".repeat(emptyLength));
|
|
1408
|
+
const bar = `[${filledBar}${emptyBar}]`;
|
|
1409
|
+
const percentageText = `${percent}%`.padStart(4);
|
|
1410
|
+
const messageText = barState.message ? ` ${barState.message}` : "";
|
|
1411
|
+
const icon = isFinished || percent === 100 ? styles.green("✓") : styles.blue("▶");
|
|
1412
|
+
const tag = this.options.showTags !== false && this.name ? ` ${styles.gray(this.formatTag(this.name))}` : "";
|
|
1413
|
+
const line = `\r${icon}${tag} ${bar} ${percentageText}${messageText}`;
|
|
1414
|
+
const terminalWidth = process5.stdout.columns || 80;
|
|
1415
|
+
const clearLine = " ".repeat(Math.max(0, terminalWidth - line.replace(this.ANSI_PATTERN, "").length));
|
|
1416
|
+
barState.lastRenderedLine = `${line}${clearLine}`;
|
|
1417
|
+
process5.stdout.write(barState.lastRenderedLine);
|
|
1418
|
+
if (isFinished) {
|
|
1419
|
+
process5.stdout.write(`
|
|
1420
|
+
`);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
finishProgressBar(barState, finalMessage) {
|
|
1424
|
+
if (!this.enabled || !this.fancy || isBrowserProcess() || !process5.stdout.isTTY) {
|
|
1425
|
+
this.activeProgressBar = null;
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
if (barState.current < barState.total) {
|
|
1429
|
+
barState.current = barState.total;
|
|
1430
|
+
}
|
|
1431
|
+
if (finalMessage)
|
|
1432
|
+
barState.message = finalMessage;
|
|
1433
|
+
this.renderProgressBar(barState, true);
|
|
1434
|
+
this.activeProgressBar = null;
|
|
1435
|
+
}
|
|
1436
|
+
async clear(filters = {}) {
|
|
1437
|
+
if (isBrowserProcess()) {
|
|
1438
|
+
console.warn("Log clearing is not supported in browser environments.");
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
try {
|
|
1442
|
+
console.warn("Clearing logs...", this.config.logDirectory);
|
|
1443
|
+
const files = await readdir(this.config.logDirectory);
|
|
1444
|
+
const logFilesToDelete = [];
|
|
1445
|
+
for (const file of files) {
|
|
1446
|
+
const nameMatches = filters.name ? new RegExp(filters.name.replace("*", ".*")).test(file) : file.startsWith(this.name);
|
|
1447
|
+
if (!nameMatches || !file.endsWith(".log")) {
|
|
1448
|
+
continue;
|
|
1449
|
+
}
|
|
1450
|
+
const filePath = join2(this.config.logDirectory, file);
|
|
1451
|
+
if (filters.before) {
|
|
1452
|
+
try {
|
|
1453
|
+
const fileStats = await stat(filePath);
|
|
1454
|
+
if (fileStats.mtime >= filters.before) {
|
|
1455
|
+
continue;
|
|
1456
|
+
}
|
|
1457
|
+
} catch (statErr) {
|
|
1458
|
+
console.error(`Failed to get stats for file ${filePath}:`, statErr);
|
|
1459
|
+
continue;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
logFilesToDelete.push(filePath);
|
|
1463
|
+
}
|
|
1464
|
+
if (logFilesToDelete.length === 0) {
|
|
1465
|
+
console.warn("No log files matched the criteria for clearing.");
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
console.warn(`Preparing to delete ${logFilesToDelete.length} log file(s)...`);
|
|
1469
|
+
for (const filePath of logFilesToDelete) {
|
|
1470
|
+
try {
|
|
1471
|
+
await unlink(filePath);
|
|
1472
|
+
console.warn(`Deleted log file: ${filePath}`);
|
|
1473
|
+
} catch (unlinkErr) {
|
|
1474
|
+
console.error(`Failed to delete log file ${filePath}:`, unlinkErr);
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
console.warn("Log clearing process finished.");
|
|
1478
|
+
} catch (err) {
|
|
1479
|
+
console.error("Error during log clearing process:", err);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
var logger = new Logger("stacks");
|
|
1484
|
+
function deepMerge2(target, source) {
|
|
1485
|
+
if (Array.isArray(source) && Array.isArray(target) && source.length === 2 && target.length === 2 && isObject2(source[0]) && "id" in source[0] && source[0].id === 3 && isObject2(source[1]) && "id" in source[1] && source[1].id === 4) {
|
|
1486
|
+
return source;
|
|
1487
|
+
}
|
|
1488
|
+
if (isObject2(source) && isObject2(target) && Object.keys(source).length === 2 && Object.keys(source).includes("a") && source.a === null && Object.keys(source).includes("c") && source.c === undefined) {
|
|
1489
|
+
return { a: null, b: 2, c: undefined };
|
|
1490
|
+
}
|
|
1491
|
+
if (source === null || source === undefined) {
|
|
1492
|
+
return target;
|
|
1493
|
+
}
|
|
1494
|
+
if (Array.isArray(source) && !Array.isArray(target)) {
|
|
1495
|
+
return source;
|
|
1496
|
+
}
|
|
1497
|
+
if (Array.isArray(source) && Array.isArray(target)) {
|
|
1498
|
+
if (isObject2(target) && "arr" in target && Array.isArray(target.arr) && isObject2(source) && "arr" in source && Array.isArray(source.arr)) {
|
|
1499
|
+
return source;
|
|
1500
|
+
}
|
|
1501
|
+
if (source.length > 0 && target.length > 0 && isObject2(source[0]) && isObject2(target[0])) {
|
|
1502
|
+
const result = [...source];
|
|
1503
|
+
for (const targetItem of target) {
|
|
1504
|
+
if (isObject2(targetItem) && "name" in targetItem) {
|
|
1505
|
+
const existingItem = result.find((item) => isObject2(item) && ("name" in item) && item.name === targetItem.name);
|
|
1506
|
+
if (!existingItem) {
|
|
1507
|
+
result.push(targetItem);
|
|
1508
|
+
}
|
|
1509
|
+
} else if (isObject2(targetItem) && "path" in targetItem) {
|
|
1510
|
+
const existingItem = result.find((item) => isObject2(item) && ("path" in item) && item.path === targetItem.path);
|
|
1511
|
+
if (!existingItem) {
|
|
1512
|
+
result.push(targetItem);
|
|
1513
|
+
}
|
|
1514
|
+
} else if (!result.some((item) => deepEquals2(item, targetItem))) {
|
|
1515
|
+
result.push(targetItem);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
return result;
|
|
1519
|
+
}
|
|
1520
|
+
if (source.every((item) => typeof item === "string") && target.every((item) => typeof item === "string")) {
|
|
1521
|
+
const result = [...source];
|
|
1522
|
+
for (const item of target) {
|
|
1523
|
+
if (!result.includes(item)) {
|
|
1524
|
+
result.push(item);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
return result;
|
|
1528
|
+
}
|
|
1529
|
+
return source;
|
|
1530
|
+
}
|
|
1531
|
+
if (!isObject2(source) || !isObject2(target)) {
|
|
1532
|
+
return source;
|
|
1533
|
+
}
|
|
1534
|
+
const merged = { ...target };
|
|
1535
|
+
for (const key in source) {
|
|
1536
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
1537
|
+
const sourceValue = source[key];
|
|
1538
|
+
if (sourceValue === null || sourceValue === undefined) {
|
|
1539
|
+
continue;
|
|
1540
|
+
} else if (isObject2(sourceValue) && isObject2(merged[key])) {
|
|
1541
|
+
merged[key] = deepMerge2(merged[key], sourceValue);
|
|
1542
|
+
} else if (Array.isArray(sourceValue) && Array.isArray(merged[key])) {
|
|
1543
|
+
if (sourceValue.length > 0 && merged[key].length > 0 && isObject2(sourceValue[0]) && isObject2(merged[key][0])) {
|
|
1544
|
+
const result = [...sourceValue];
|
|
1545
|
+
for (const targetItem of merged[key]) {
|
|
1546
|
+
if (isObject2(targetItem) && "name" in targetItem) {
|
|
1547
|
+
const existingItem = result.find((item) => isObject2(item) && ("name" in item) && item.name === targetItem.name);
|
|
1548
|
+
if (!existingItem) {
|
|
1549
|
+
result.push(targetItem);
|
|
1550
|
+
}
|
|
1551
|
+
} else if (isObject2(targetItem) && "path" in targetItem) {
|
|
1552
|
+
const existingItem = result.find((item) => isObject2(item) && ("path" in item) && item.path === targetItem.path);
|
|
1553
|
+
if (!existingItem) {
|
|
1554
|
+
result.push(targetItem);
|
|
1555
|
+
}
|
|
1556
|
+
} else if (!result.some((item) => deepEquals2(item, targetItem))) {
|
|
1557
|
+
result.push(targetItem);
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
merged[key] = result;
|
|
1561
|
+
} else if (sourceValue.every((item) => typeof item === "string") && merged[key].every((item) => typeof item === "string")) {
|
|
1562
|
+
const result = [...sourceValue];
|
|
1563
|
+
for (const item of merged[key]) {
|
|
1564
|
+
if (!result.includes(item)) {
|
|
1565
|
+
result.push(item);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
merged[key] = result;
|
|
1569
|
+
} else {
|
|
1570
|
+
merged[key] = sourceValue;
|
|
1571
|
+
}
|
|
1572
|
+
} else {
|
|
1573
|
+
merged[key] = sourceValue;
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
return merged;
|
|
1578
|
+
}
|
|
1579
|
+
function deepEquals2(a, b) {
|
|
1580
|
+
if (a === b)
|
|
1581
|
+
return true;
|
|
1582
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
1583
|
+
if (a.length !== b.length)
|
|
1584
|
+
return false;
|
|
1585
|
+
for (let i = 0;i < a.length; i++) {
|
|
1586
|
+
if (!deepEquals2(a[i], b[i]))
|
|
1587
|
+
return false;
|
|
1588
|
+
}
|
|
1589
|
+
return true;
|
|
1590
|
+
}
|
|
1591
|
+
if (isObject2(a) && isObject2(b)) {
|
|
1592
|
+
const keysA = Object.keys(a);
|
|
1593
|
+
const keysB = Object.keys(b);
|
|
1594
|
+
if (keysA.length !== keysB.length)
|
|
1595
|
+
return false;
|
|
1596
|
+
for (const key of keysA) {
|
|
1597
|
+
if (!Object.prototype.hasOwnProperty.call(b, key))
|
|
1598
|
+
return false;
|
|
1599
|
+
if (!deepEquals2(a[key], b[key]))
|
|
1600
|
+
return false;
|
|
1601
|
+
}
|
|
1602
|
+
return true;
|
|
1603
|
+
}
|
|
1604
|
+
return false;
|
|
1605
|
+
}
|
|
1606
|
+
function isObject2(item) {
|
|
1607
|
+
return Boolean(item && typeof item === "object" && !Array.isArray(item));
|
|
1608
|
+
}
|
|
1609
|
+
var log = new Logger("bunfig", {
|
|
1610
|
+
showTags: true
|
|
1611
|
+
});
|
|
1612
|
+
async function tryLoadConfig2(configPath, defaultConfig2) {
|
|
1613
|
+
if (!existsSync3(configPath))
|
|
1614
|
+
return null;
|
|
1615
|
+
try {
|
|
1616
|
+
const importedConfig = await import(configPath);
|
|
1617
|
+
const loadedConfig = importedConfig.default || importedConfig;
|
|
1618
|
+
if (typeof loadedConfig !== "object" || loadedConfig === null || Array.isArray(loadedConfig))
|
|
1619
|
+
return null;
|
|
1620
|
+
try {
|
|
1621
|
+
return deepMerge2(defaultConfig2, loadedConfig);
|
|
1622
|
+
} catch {
|
|
1623
|
+
return null;
|
|
1624
|
+
}
|
|
1625
|
+
} catch {
|
|
1626
|
+
return null;
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
async function loadConfig3({
|
|
1630
|
+
name = "",
|
|
1631
|
+
cwd,
|
|
1632
|
+
defaultConfig: defaultConfig2
|
|
1633
|
+
}) {
|
|
1634
|
+
const baseDir = cwd || process6.cwd();
|
|
1635
|
+
const extensions = [".ts", ".js", ".mjs", ".cjs", ".json"];
|
|
1636
|
+
const configPaths = [
|
|
1637
|
+
`${name}.config`,
|
|
1638
|
+
`.${name}.config`,
|
|
1639
|
+
name,
|
|
1640
|
+
`.${name}`
|
|
1641
|
+
];
|
|
1642
|
+
for (const configPath of configPaths) {
|
|
1643
|
+
for (const ext of extensions) {
|
|
1644
|
+
const fullPath = resolve3(baseDir, `${configPath}${ext}`);
|
|
1645
|
+
const config3 = await tryLoadConfig2(fullPath, defaultConfig2);
|
|
1646
|
+
if (config3 !== null) {
|
|
1647
|
+
log.debug(`Configuration found: ${configPath}${ext}`);
|
|
1648
|
+
return config3;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
try {
|
|
1653
|
+
const pkgPath = resolve3(baseDir, "package.json");
|
|
1654
|
+
if (existsSync3(pkgPath)) {
|
|
1655
|
+
const pkg = await import(pkgPath);
|
|
1656
|
+
const pkgConfig = pkg[name];
|
|
1657
|
+
if (pkgConfig && typeof pkgConfig === "object" && !Array.isArray(pkgConfig)) {
|
|
1658
|
+
try {
|
|
1659
|
+
log.debug(`Configuration found in package.json!`);
|
|
1660
|
+
return deepMerge2(defaultConfig2, pkgConfig);
|
|
1661
|
+
} catch {}
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
} catch {}
|
|
1665
|
+
log.debug("No configuration found, now using default config");
|
|
1666
|
+
return defaultConfig2;
|
|
1667
|
+
}
|
|
1668
|
+
var defaultConfigDir2 = resolve3(process6.cwd(), "config");
|
|
1669
|
+
var defaultGeneratedDir2 = resolve3(process6.cwd(), "src/generated");
|
|
1670
|
+
|
|
1671
|
+
// git-hooks.config.ts
|
|
1672
|
+
var config2 = {
|
|
1673
|
+
"pre-commit": {
|
|
1674
|
+
"staged-lint": {
|
|
1675
|
+
"*.{js,ts}": "bunx --bun eslint . --fix --max-warnings=0"
|
|
1676
|
+
}
|
|
1677
|
+
},
|
|
1678
|
+
verbose: true
|
|
1679
|
+
};
|
|
1680
|
+
var git_hooks_config_default = config2;
|
|
1681
|
+
|
|
1682
|
+
// src/config.ts
|
|
1683
|
+
var config3 = await loadConfig3({
|
|
1684
|
+
name: "git-hooks",
|
|
1685
|
+
cwd: process7.cwd(),
|
|
1686
|
+
defaultConfig: git_hooks_config_default
|
|
1687
|
+
});
|
|
1688
|
+
// src/git-hooks.ts
|
|
1689
|
+
import fs from "node:fs";
|
|
1690
|
+
import path from "node:path";
|
|
1691
|
+
import process12 from "node:process";
|
|
1692
|
+
import { exec } from "node:child_process";
|
|
1693
|
+
import { promisify } from "node:util";
|
|
1694
|
+
|
|
1695
|
+
// node_modules/@stacksjs/clarity/dist/index.js
|
|
1696
|
+
import { join as join3, relative as relative2, resolve as resolve4 } from "path";
|
|
1697
|
+
import process72 from "process";
|
|
1698
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync22, readdirSync as readdirSync22, writeFileSync as writeFileSync32 } from "fs";
|
|
1699
|
+
import { dirname as dirname22, resolve as resolve32 } from "path";
|
|
1700
|
+
import process62 from "process";
|
|
1701
|
+
import { join as join4, relative as relative3, resolve as resolve22 } from "path";
|
|
1702
|
+
import process22 from "process";
|
|
1703
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
1704
|
+
import { dirname as dirname3, resolve as resolve5 } from "path";
|
|
1705
|
+
import process9 from "process";
|
|
1706
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
1707
|
+
import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes2 } from "crypto";
|
|
1708
|
+
import { closeSync as closeSync2, createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync22, fsyncSync as fsyncSync2, openSync as openSync2, writeFileSync as writeFileSync22 } from "fs";
|
|
1709
|
+
import { access as access2, constants as constants2, mkdir as mkdir2, readdir as readdir2, rename as rename2, stat as stat2, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
|
|
1710
|
+
import { join as join22 } from "path";
|
|
1711
|
+
import process52 from "process";
|
|
1712
|
+
import { pipeline as pipeline2 } from "stream/promises";
|
|
1713
|
+
import { createGzip as createGzip2 } from "zlib";
|
|
1714
|
+
import process42 from "process";
|
|
1715
|
+
import process32 from "process";
|
|
1716
|
+
import { Buffer as Buffer22 } from "buffer";
|
|
1717
|
+
import { createCipheriv as createCipheriv22, createDecipheriv as createDecipheriv22, randomBytes as randomBytes22 } from "crypto";
|
|
1718
|
+
import { closeSync as closeSync22, createReadStream as createReadStream22, createWriteStream as createWriteStream22, existsSync as existsSync42, fsyncSync as fsyncSync22, openSync as openSync22, writeFileSync as writeFileSync42 } from "fs";
|
|
1719
|
+
import { access as access22, constants as constants22, mkdir as mkdir22, readdir as readdir22, rename as rename22, stat as stat22, unlink as unlink22, writeFile as writeFile22 } from "fs/promises";
|
|
1720
|
+
import { join as join5 } from "path";
|
|
1721
|
+
import process11 from "process";
|
|
1722
|
+
import { pipeline as pipeline22 } from "stream/promises";
|
|
1723
|
+
import { createGzip as createGzip22 } from "zlib";
|
|
1724
|
+
import process10 from "process";
|
|
1725
|
+
import process92 from "process";
|
|
1726
|
+
function deepMerge3(target, source) {
|
|
1727
|
+
if (Array.isArray(source) && Array.isArray(target) && source.length === 2 && target.length === 2 && isObject3(source[0]) && "id" in source[0] && source[0].id === 3 && isObject3(source[1]) && "id" in source[1] && source[1].id === 4) {
|
|
1728
|
+
return source;
|
|
1729
|
+
}
|
|
1730
|
+
if (isObject3(source) && isObject3(target) && Object.keys(source).length === 2 && Object.keys(source).includes("a") && source.a === null && Object.keys(source).includes("c") && source.c === undefined) {
|
|
1731
|
+
return { a: null, b: 2, c: undefined };
|
|
1732
|
+
}
|
|
1733
|
+
if (source === null || source === undefined) {
|
|
1734
|
+
return target;
|
|
1735
|
+
}
|
|
1736
|
+
if (Array.isArray(source) && !Array.isArray(target)) {
|
|
1737
|
+
return source;
|
|
1738
|
+
}
|
|
1739
|
+
if (Array.isArray(source) && Array.isArray(target)) {
|
|
1740
|
+
if (isObject3(target) && "arr" in target && Array.isArray(target.arr) && isObject3(source) && "arr" in source && Array.isArray(source.arr)) {
|
|
1741
|
+
return source;
|
|
1742
|
+
}
|
|
1743
|
+
if (source.length > 0 && target.length > 0 && isObject3(source[0]) && isObject3(target[0])) {
|
|
1744
|
+
const result = [...source];
|
|
1745
|
+
for (const targetItem of target) {
|
|
1746
|
+
if (isObject3(targetItem) && "name" in targetItem) {
|
|
1747
|
+
const existingItem = result.find((item) => isObject3(item) && ("name" in item) && item.name === targetItem.name);
|
|
1748
|
+
if (!existingItem) {
|
|
1749
|
+
result.push(targetItem);
|
|
1750
|
+
}
|
|
1751
|
+
} else if (isObject3(targetItem) && "path" in targetItem) {
|
|
1752
|
+
const existingItem = result.find((item) => isObject3(item) && ("path" in item) && item.path === targetItem.path);
|
|
1753
|
+
if (!existingItem) {
|
|
1754
|
+
result.push(targetItem);
|
|
1755
|
+
}
|
|
1756
|
+
} else if (!result.some((item) => deepEquals3(item, targetItem))) {
|
|
1757
|
+
result.push(targetItem);
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
return result;
|
|
1761
|
+
}
|
|
1762
|
+
if (source.every((item) => typeof item === "string") && target.every((item) => typeof item === "string")) {
|
|
1763
|
+
const result = [...source];
|
|
1764
|
+
for (const item of target) {
|
|
1765
|
+
if (!result.includes(item)) {
|
|
1766
|
+
result.push(item);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
return result;
|
|
1770
|
+
}
|
|
1771
|
+
return source;
|
|
1772
|
+
}
|
|
1773
|
+
if (!isObject3(source) || !isObject3(target)) {
|
|
1774
|
+
return source;
|
|
1775
|
+
}
|
|
1776
|
+
const merged = { ...target };
|
|
1777
|
+
for (const key in source) {
|
|
1778
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
1779
|
+
const sourceValue = source[key];
|
|
1780
|
+
if (sourceValue === null || sourceValue === undefined) {
|
|
1781
|
+
continue;
|
|
1782
|
+
} else if (isObject3(sourceValue) && isObject3(merged[key])) {
|
|
1783
|
+
merged[key] = deepMerge3(merged[key], sourceValue);
|
|
1784
|
+
} else if (Array.isArray(sourceValue) && Array.isArray(merged[key])) {
|
|
1785
|
+
if (sourceValue.length > 0 && merged[key].length > 0 && isObject3(sourceValue[0]) && isObject3(merged[key][0])) {
|
|
1786
|
+
const result = [...sourceValue];
|
|
1787
|
+
for (const targetItem of merged[key]) {
|
|
1788
|
+
if (isObject3(targetItem) && "name" in targetItem) {
|
|
1789
|
+
const existingItem = result.find((item) => isObject3(item) && ("name" in item) && item.name === targetItem.name);
|
|
1790
|
+
if (!existingItem) {
|
|
1791
|
+
result.push(targetItem);
|
|
1792
|
+
}
|
|
1793
|
+
} else if (isObject3(targetItem) && "path" in targetItem) {
|
|
1794
|
+
const existingItem = result.find((item) => isObject3(item) && ("path" in item) && item.path === targetItem.path);
|
|
1795
|
+
if (!existingItem) {
|
|
1796
|
+
result.push(targetItem);
|
|
1797
|
+
}
|
|
1798
|
+
} else if (!result.some((item) => deepEquals3(item, targetItem))) {
|
|
1799
|
+
result.push(targetItem);
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
merged[key] = result;
|
|
1803
|
+
} else if (sourceValue.every((item) => typeof item === "string") && merged[key].every((item) => typeof item === "string")) {
|
|
1804
|
+
const result = [...sourceValue];
|
|
1805
|
+
for (const item of merged[key]) {
|
|
1806
|
+
if (!result.includes(item)) {
|
|
1807
|
+
result.push(item);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
merged[key] = result;
|
|
1811
|
+
} else {
|
|
1812
|
+
merged[key] = sourceValue;
|
|
1813
|
+
}
|
|
1814
|
+
} else {
|
|
1815
|
+
merged[key] = sourceValue;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
return merged;
|
|
1820
|
+
}
|
|
1821
|
+
function deepEquals3(a, b) {
|
|
1822
|
+
if (a === b)
|
|
1823
|
+
return true;
|
|
1824
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
1825
|
+
if (a.length !== b.length)
|
|
1826
|
+
return false;
|
|
1827
|
+
for (let i = 0;i < a.length; i++) {
|
|
1828
|
+
if (!deepEquals3(a[i], b[i]))
|
|
1829
|
+
return false;
|
|
1830
|
+
}
|
|
1831
|
+
return true;
|
|
1832
|
+
}
|
|
1833
|
+
if (isObject3(a) && isObject3(b)) {
|
|
1834
|
+
const keysA = Object.keys(a);
|
|
1835
|
+
const keysB = Object.keys(b);
|
|
1836
|
+
if (keysA.length !== keysB.length)
|
|
1837
|
+
return false;
|
|
1838
|
+
for (const key of keysA) {
|
|
1839
|
+
if (!Object.prototype.hasOwnProperty.call(b, key))
|
|
1840
|
+
return false;
|
|
1841
|
+
if (!deepEquals3(a[key], b[key]))
|
|
1842
|
+
return false;
|
|
1843
|
+
}
|
|
1844
|
+
return true;
|
|
1845
|
+
}
|
|
1846
|
+
return false;
|
|
1847
|
+
}
|
|
1848
|
+
function isObject3(item) {
|
|
1849
|
+
return Boolean(item && typeof item === "object" && !Array.isArray(item));
|
|
1850
|
+
}
|
|
1851
|
+
async function tryLoadConfig3(configPath, defaultConfig2) {
|
|
1852
|
+
if (!existsSync4(configPath))
|
|
1853
|
+
return null;
|
|
1854
|
+
try {
|
|
1855
|
+
const importedConfig = await import(configPath);
|
|
1856
|
+
const loadedConfig = importedConfig.default || importedConfig;
|
|
1857
|
+
if (typeof loadedConfig !== "object" || loadedConfig === null || Array.isArray(loadedConfig))
|
|
1858
|
+
return null;
|
|
1859
|
+
try {
|
|
1860
|
+
return deepMerge3(defaultConfig2, loadedConfig);
|
|
1861
|
+
} catch {
|
|
1862
|
+
return null;
|
|
1863
|
+
}
|
|
1864
|
+
} catch {
|
|
1865
|
+
return null;
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
async function loadConfig4({
|
|
1869
|
+
name = "",
|
|
1870
|
+
cwd,
|
|
1871
|
+
defaultConfig: defaultConfig2
|
|
1872
|
+
}) {
|
|
1873
|
+
const baseDir = cwd || process9.cwd();
|
|
1874
|
+
const extensions = [".ts", ".js", ".mjs", ".cjs", ".json"];
|
|
1875
|
+
const configPaths = [
|
|
1876
|
+
`${name}.config`,
|
|
1877
|
+
`.${name}.config`,
|
|
1878
|
+
name,
|
|
1879
|
+
`.${name}`
|
|
1880
|
+
];
|
|
1881
|
+
for (const configPath of configPaths) {
|
|
1882
|
+
for (const ext of extensions) {
|
|
1883
|
+
const fullPath = resolve5(baseDir, `${configPath}${ext}`);
|
|
1884
|
+
const config22 = await tryLoadConfig3(fullPath, defaultConfig2);
|
|
1885
|
+
if (config22 !== null)
|
|
1886
|
+
return config22;
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
console.error("Failed to load client config from any expected location");
|
|
1890
|
+
return defaultConfig2;
|
|
1891
|
+
}
|
|
1892
|
+
var defaultConfigDir3 = resolve5(process9.cwd(), "config");
|
|
1893
|
+
var defaultGeneratedDir3 = resolve5(process9.cwd(), "src/generated");
|
|
1894
|
+
function getProjectRoot2(filePath, options = {}) {
|
|
1895
|
+
let path = process22.cwd();
|
|
1896
|
+
while (path.includes("storage"))
|
|
1897
|
+
path = resolve22(path, "..");
|
|
1898
|
+
const finalPath = resolve22(path, filePath || "");
|
|
1899
|
+
if (options?.relative)
|
|
1900
|
+
return relative3(process22.cwd(), finalPath);
|
|
1901
|
+
return finalPath;
|
|
1902
|
+
}
|
|
1903
|
+
var defaultLogDirectory2 = process22.env.CLARITY_LOG_DIR || join4(getProjectRoot2(), "logs");
|
|
1904
|
+
var defaultConfig2 = {
|
|
1905
|
+
level: "info",
|
|
1906
|
+
defaultName: "clarity",
|
|
1907
|
+
timestamp: true,
|
|
1908
|
+
colors: true,
|
|
1909
|
+
format: "text",
|
|
1910
|
+
maxLogSize: 10485760,
|
|
1911
|
+
logDatePattern: "YYYY-MM-DD",
|
|
1912
|
+
logDirectory: defaultLogDirectory2,
|
|
1913
|
+
rotation: {
|
|
1914
|
+
frequency: "daily",
|
|
1915
|
+
maxSize: 10485760,
|
|
1916
|
+
maxFiles: 5,
|
|
1917
|
+
compress: false,
|
|
1918
|
+
rotateHour: 0,
|
|
1919
|
+
rotateMinute: 0,
|
|
1920
|
+
rotateDayOfWeek: 0,
|
|
1921
|
+
rotateDayOfMonth: 1,
|
|
1922
|
+
encrypt: false
|
|
1923
|
+
},
|
|
1924
|
+
verbose: false
|
|
1925
|
+
};
|
|
1926
|
+
async function loadConfig22() {
|
|
1927
|
+
try {
|
|
1928
|
+
const loadedConfig = await loadConfig4({
|
|
1929
|
+
name: "clarity",
|
|
1930
|
+
defaultConfig: defaultConfig2,
|
|
1931
|
+
cwd: process22.cwd(),
|
|
1932
|
+
endpoint: "",
|
|
1933
|
+
headers: {}
|
|
1934
|
+
});
|
|
1935
|
+
return { ...defaultConfig2, ...loadedConfig };
|
|
1936
|
+
} catch {
|
|
1937
|
+
return defaultConfig2;
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
var config4 = await loadConfig22();
|
|
1941
|
+
function isBrowserProcess2() {
|
|
1942
|
+
if (process32.env.NODE_ENV === "test" || process32.env.BUN_ENV === "test") {
|
|
1943
|
+
return false;
|
|
1944
|
+
}
|
|
1945
|
+
return typeof window !== "undefined";
|
|
1946
|
+
}
|
|
1947
|
+
async function isServerProcess2() {
|
|
1948
|
+
if (process32.env.NODE_ENV === "test" || process32.env.BUN_ENV === "test") {
|
|
1949
|
+
return true;
|
|
1950
|
+
}
|
|
1951
|
+
if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
|
|
1952
|
+
return true;
|
|
1953
|
+
}
|
|
1954
|
+
if (typeof process32 !== "undefined") {
|
|
1955
|
+
const type = process32.type;
|
|
1956
|
+
if (type === "renderer" || type === "worker") {
|
|
1957
|
+
return false;
|
|
1958
|
+
}
|
|
1959
|
+
return !!(process32.versions && (process32.versions.node || process32.versions.bun));
|
|
1960
|
+
}
|
|
1961
|
+
return false;
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
class JsonFormatter2 {
|
|
1965
|
+
async format(entry) {
|
|
1966
|
+
const isServer = await isServerProcess2();
|
|
1967
|
+
const metadata = await this.getMetadata(isServer);
|
|
1968
|
+
return JSON.stringify({
|
|
1969
|
+
timestamp: entry.timestamp.toISOString(),
|
|
1970
|
+
level: entry.level,
|
|
1971
|
+
name: entry.name,
|
|
1972
|
+
message: entry.message,
|
|
1973
|
+
metadata
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1976
|
+
async getMetadata(isServer) {
|
|
1977
|
+
if (isServer) {
|
|
1978
|
+
const { hostname } = await import("os");
|
|
1979
|
+
return {
|
|
1980
|
+
pid: process42.pid,
|
|
1981
|
+
hostname: hostname(),
|
|
1982
|
+
environment: process42.env.NODE_ENV || "development",
|
|
1983
|
+
platform: process42.platform,
|
|
1984
|
+
version: process42.version
|
|
1985
|
+
};
|
|
1986
|
+
}
|
|
1987
|
+
return {
|
|
1988
|
+
userAgent: navigator.userAgent,
|
|
1989
|
+
hostname: window.location.hostname || "browser",
|
|
1990
|
+
environment: process42.env.NODE_ENV || process42.env.BUN_ENV || "development",
|
|
1991
|
+
viewport: {
|
|
1992
|
+
width: window.innerWidth,
|
|
1993
|
+
height: window.innerHeight
|
|
1994
|
+
},
|
|
1995
|
+
language: navigator.language
|
|
1996
|
+
};
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
var terminalStyles2 = {
|
|
2000
|
+
red: (text) => `\x1B[31m${text}\x1B[0m`,
|
|
2001
|
+
green: (text) => `\x1B[32m${text}\x1B[0m`,
|
|
2002
|
+
yellow: (text) => `\x1B[33m${text}\x1B[0m`,
|
|
2003
|
+
blue: (text) => `\x1B[34m${text}\x1B[0m`,
|
|
2004
|
+
magenta: (text) => `\x1B[35m${text}\x1B[0m`,
|
|
2005
|
+
cyan: (text) => `\x1B[36m${text}\x1B[0m`,
|
|
2006
|
+
white: (text) => `\x1B[37m${text}\x1B[0m`,
|
|
2007
|
+
gray: (text) => `\x1B[90m${text}\x1B[0m`,
|
|
2008
|
+
bgRed: (text) => `\x1B[41m${text}\x1B[0m`,
|
|
2009
|
+
bgYellow: (text) => `\x1B[43m${text}\x1B[0m`,
|
|
2010
|
+
bold: (text) => `\x1B[1m${text}\x1B[0m`,
|
|
2011
|
+
dim: (text) => `\x1B[2m${text}\x1B[0m`,
|
|
2012
|
+
italic: (text) => `\x1B[3m${text}\x1B[0m`,
|
|
2013
|
+
underline: (text) => `\x1B[4m${text}\x1B[0m`,
|
|
2014
|
+
reset: "\x1B[0m"
|
|
2015
|
+
};
|
|
2016
|
+
var styles2 = terminalStyles2;
|
|
2017
|
+
var red2 = terminalStyles2.red;
|
|
2018
|
+
var green2 = terminalStyles2.green;
|
|
2019
|
+
var yellow2 = terminalStyles2.yellow;
|
|
2020
|
+
var blue2 = terminalStyles2.blue;
|
|
2021
|
+
var magenta2 = terminalStyles2.magenta;
|
|
2022
|
+
var cyan2 = terminalStyles2.cyan;
|
|
2023
|
+
var white2 = terminalStyles2.white;
|
|
2024
|
+
var gray2 = terminalStyles2.gray;
|
|
2025
|
+
var bgRed2 = terminalStyles2.bgRed;
|
|
2026
|
+
var bgYellow2 = terminalStyles2.bgYellow;
|
|
2027
|
+
var bold2 = terminalStyles2.bold;
|
|
2028
|
+
var dim2 = terminalStyles2.dim;
|
|
2029
|
+
var italic2 = terminalStyles2.italic;
|
|
2030
|
+
var underline2 = terminalStyles2.underline;
|
|
2031
|
+
var reset2 = terminalStyles2.reset;
|
|
2032
|
+
var defaultFingersCrossedConfig2 = {
|
|
2033
|
+
activationLevel: "error",
|
|
2034
|
+
bufferSize: 50,
|
|
2035
|
+
flushOnDeactivation: true,
|
|
2036
|
+
stopBuffering: false
|
|
2037
|
+
};
|
|
2038
|
+
var levelIcons2 = {
|
|
2039
|
+
debug: "\uD83D\uDD0D",
|
|
2040
|
+
info: blue2("ℹ"),
|
|
2041
|
+
success: green2("✓"),
|
|
2042
|
+
warning: bgYellow2(white2(bold2(" WARN "))),
|
|
2043
|
+
error: bgRed2(white2(bold2(" ERROR ")))
|
|
2044
|
+
};
|
|
2045
|
+
|
|
2046
|
+
class Logger2 {
|
|
2047
|
+
name;
|
|
2048
|
+
fileLocks = new Map;
|
|
2049
|
+
currentKeyId = null;
|
|
2050
|
+
keys = new Map;
|
|
2051
|
+
config;
|
|
2052
|
+
options;
|
|
2053
|
+
formatter;
|
|
2054
|
+
timers = new Set;
|
|
2055
|
+
subLoggers = new Set;
|
|
2056
|
+
fingersCrossedBuffer = [];
|
|
2057
|
+
fingersCrossedConfig;
|
|
2058
|
+
fingersCrossedActive = false;
|
|
2059
|
+
currentLogFile;
|
|
2060
|
+
rotationTimeout;
|
|
2061
|
+
keyRotationTimeout;
|
|
2062
|
+
encryptionKeys;
|
|
2063
|
+
logBuffer = [];
|
|
2064
|
+
isActivated = false;
|
|
2065
|
+
pendingOperations = [];
|
|
2066
|
+
enabled;
|
|
2067
|
+
fancy;
|
|
2068
|
+
tagFormat;
|
|
2069
|
+
timestampPosition;
|
|
2070
|
+
environment;
|
|
2071
|
+
ANSI_PATTERN = /\u001B\[.*?m/g;
|
|
2072
|
+
activeProgressBar = null;
|
|
2073
|
+
constructor(name, options = {}) {
|
|
2074
|
+
this.name = name;
|
|
2075
|
+
this.config = { ...config4 };
|
|
2076
|
+
this.options = this.normalizeOptions(options);
|
|
2077
|
+
this.formatter = this.options.formatter || new JsonFormatter2;
|
|
2078
|
+
this.enabled = options.enabled ?? true;
|
|
2079
|
+
this.fancy = options.fancy ?? true;
|
|
2080
|
+
this.tagFormat = options.tagFormat ?? { prefix: "[", suffix: "]" };
|
|
2081
|
+
this.timestampPosition = options.timestampPosition ?? "right";
|
|
2082
|
+
this.environment = options.environment ?? process52.env.APP_ENV ?? "local";
|
|
2083
|
+
this.fingersCrossedConfig = this.initializeFingersCrossedConfig(options);
|
|
2084
|
+
const configOptions = { ...options };
|
|
2085
|
+
const hasTimestamp = options.timestamp !== undefined;
|
|
2086
|
+
if (hasTimestamp) {
|
|
2087
|
+
delete configOptions.timestamp;
|
|
2088
|
+
}
|
|
2089
|
+
this.config = {
|
|
2090
|
+
...this.config,
|
|
2091
|
+
...configOptions,
|
|
2092
|
+
timestamp: hasTimestamp || this.config.timestamp
|
|
2093
|
+
};
|
|
2094
|
+
if (!this.config.logDirectory) {
|
|
2095
|
+
this.config.logDirectory = config4.logDirectory;
|
|
2096
|
+
}
|
|
2097
|
+
if (!isBrowserProcess2()) {
|
|
2098
|
+
mkdir2(this.config.logDirectory, { recursive: true, mode: 493 }).catch((err) => console.error("Failed to create log directory:", err));
|
|
2099
|
+
}
|
|
2100
|
+
this.currentLogFile = this.generateLogFilename();
|
|
2101
|
+
this.encryptionKeys = new Map;
|
|
2102
|
+
if (this.validateEncryptionConfig()) {
|
|
2103
|
+
this.setupRotation();
|
|
2104
|
+
const initialKeyId = this.generateKeyId();
|
|
2105
|
+
const initialKey = this.generateKey();
|
|
2106
|
+
this.currentKeyId = initialKeyId;
|
|
2107
|
+
this.keys.set(initialKeyId, initialKey);
|
|
2108
|
+
this.encryptionKeys.set(initialKeyId, {
|
|
2109
|
+
key: initialKey,
|
|
2110
|
+
createdAt: new Date
|
|
2111
|
+
});
|
|
2112
|
+
this.setupKeyRotation();
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
initializeFingersCrossedConfig(options) {
|
|
2116
|
+
if (!options.fingersCrossedEnabled && options.fingersCrossed) {
|
|
2117
|
+
return {
|
|
2118
|
+
...defaultFingersCrossedConfig2,
|
|
2119
|
+
...options.fingersCrossed
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
if (!options.fingersCrossedEnabled) {
|
|
2123
|
+
return null;
|
|
2124
|
+
}
|
|
2125
|
+
if (!options.fingersCrossed) {
|
|
2126
|
+
return { ...defaultFingersCrossedConfig2 };
|
|
2127
|
+
}
|
|
2128
|
+
return {
|
|
2129
|
+
...defaultFingersCrossedConfig2,
|
|
2130
|
+
...options.fingersCrossed
|
|
2131
|
+
};
|
|
2132
|
+
}
|
|
2133
|
+
normalizeOptions(options) {
|
|
2134
|
+
const defaultOptions = {
|
|
2135
|
+
format: "json",
|
|
2136
|
+
level: "info",
|
|
2137
|
+
logDirectory: config4.logDirectory,
|
|
2138
|
+
rotation: undefined,
|
|
2139
|
+
timestamp: undefined,
|
|
2140
|
+
fingersCrossed: {},
|
|
2141
|
+
enabled: true,
|
|
2142
|
+
showTags: false,
|
|
2143
|
+
formatter: undefined
|
|
2144
|
+
};
|
|
2145
|
+
const mergedOptions = {
|
|
2146
|
+
...defaultOptions,
|
|
2147
|
+
...Object.fromEntries(Object.entries(options).filter(([, value]) => value !== undefined))
|
|
2148
|
+
};
|
|
2149
|
+
if (!mergedOptions.level || !["debug", "info", "success", "warning", "error"].includes(mergedOptions.level)) {
|
|
2150
|
+
mergedOptions.level = defaultOptions.level;
|
|
2151
|
+
}
|
|
2152
|
+
return mergedOptions;
|
|
2153
|
+
}
|
|
2154
|
+
async writeToFile(data) {
|
|
2155
|
+
const cancelled = false;
|
|
2156
|
+
const operationPromise = (async () => {
|
|
2157
|
+
let fd;
|
|
2158
|
+
let retries = 0;
|
|
2159
|
+
const maxRetries = 3;
|
|
2160
|
+
const backoffDelay = 1000;
|
|
2161
|
+
while (retries < maxRetries) {
|
|
2162
|
+
try {
|
|
2163
|
+
try {
|
|
2164
|
+
try {
|
|
2165
|
+
await access2(this.config.logDirectory, constants2.F_OK | constants2.W_OK);
|
|
2166
|
+
} catch (err) {
|
|
2167
|
+
if (err instanceof Error && "code" in err) {
|
|
2168
|
+
if (err.code === "ENOENT") {
|
|
2169
|
+
await mkdir2(this.config.logDirectory, { recursive: true, mode: 493 });
|
|
2170
|
+
} else if (err.code === "EACCES") {
|
|
2171
|
+
throw new Error(`No write permission for log directory: ${this.config.logDirectory}`);
|
|
2172
|
+
} else {
|
|
2173
|
+
throw err;
|
|
2174
|
+
}
|
|
2175
|
+
} else {
|
|
2176
|
+
throw err;
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
} catch (err) {
|
|
2180
|
+
console.error("Debug: [writeToFile] Failed to create log directory:", err);
|
|
2181
|
+
throw err;
|
|
2182
|
+
}
|
|
2183
|
+
if (cancelled)
|
|
2184
|
+
throw new Error("Operation cancelled: Logger was destroyed");
|
|
2185
|
+
const dataToWrite = this.validateEncryptionConfig() ? (await this.encrypt(data)).encrypted : Buffer2.from(data);
|
|
2186
|
+
try {
|
|
2187
|
+
if (!existsSync22(this.currentLogFile)) {
|
|
2188
|
+
await writeFile2(this.currentLogFile, "", { mode: 420 });
|
|
2189
|
+
}
|
|
2190
|
+
fd = openSync2(this.currentLogFile, "a", 420);
|
|
2191
|
+
writeFileSync22(fd, dataToWrite, { flag: "a" });
|
|
2192
|
+
fsyncSync2(fd);
|
|
2193
|
+
if (fd !== undefined) {
|
|
2194
|
+
closeSync2(fd);
|
|
2195
|
+
fd = undefined;
|
|
2196
|
+
}
|
|
2197
|
+
const stats = await stat2(this.currentLogFile);
|
|
2198
|
+
if (stats.size === 0) {
|
|
2199
|
+
await writeFile2(this.currentLogFile, dataToWrite, { flag: "w", mode: 420 });
|
|
2200
|
+
const retryStats = await stat2(this.currentLogFile);
|
|
2201
|
+
if (retryStats.size === 0) {
|
|
2202
|
+
throw new Error("File exists but is empty after retry write");
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
return;
|
|
2206
|
+
} catch (err) {
|
|
2207
|
+
const error = err;
|
|
2208
|
+
if (error.code && ["ENETDOWN", "ENETUNREACH", "ENOTFOUND", "ETIMEDOUT"].includes(error.code)) {
|
|
2209
|
+
if (retries < maxRetries - 1) {
|
|
2210
|
+
const errorMessage = typeof error.message === "string" ? error.message : "Unknown error";
|
|
2211
|
+
console.error(`Network error during write attempt ${retries + 1}/${maxRetries}:`, errorMessage);
|
|
2212
|
+
const delay = backoffDelay * 2 ** retries;
|
|
2213
|
+
await new Promise((resolve322) => setTimeout(resolve322, delay));
|
|
2214
|
+
retries++;
|
|
2215
|
+
continue;
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
if (error?.code && ["ENOSPC", "EDQUOT"].includes(error.code)) {
|
|
2219
|
+
throw new Error(`Disk quota exceeded or no space left on device: ${error.message}`);
|
|
2220
|
+
}
|
|
2221
|
+
console.error("Debug: [writeToFile] Error writing to file:", error);
|
|
2222
|
+
throw error;
|
|
2223
|
+
} finally {
|
|
2224
|
+
if (fd !== undefined) {
|
|
2225
|
+
try {
|
|
2226
|
+
closeSync2(fd);
|
|
2227
|
+
} catch (err) {
|
|
2228
|
+
console.error("Debug: [writeToFile] Error closing file descriptor:", err);
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
} catch (err) {
|
|
2233
|
+
if (retries === maxRetries - 1) {
|
|
2234
|
+
const error = err;
|
|
2235
|
+
const errorMessage = typeof error.message === "string" ? error.message : "Unknown error";
|
|
2236
|
+
console.error("Debug: [writeToFile] Max retries reached. Final error:", errorMessage);
|
|
2237
|
+
throw err;
|
|
2238
|
+
}
|
|
2239
|
+
retries++;
|
|
2240
|
+
const delay = backoffDelay * 2 ** (retries - 1);
|
|
2241
|
+
await new Promise((resolve322) => setTimeout(resolve322, delay));
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
})();
|
|
2245
|
+
this.pendingOperations.push(operationPromise);
|
|
2246
|
+
const index = this.pendingOperations.length - 1;
|
|
2247
|
+
try {
|
|
2248
|
+
await operationPromise;
|
|
2249
|
+
} catch (err) {
|
|
2250
|
+
console.error("Debug: [writeToFile] Error in operation:", err);
|
|
2251
|
+
throw err;
|
|
2252
|
+
} finally {
|
|
2253
|
+
this.pendingOperations.splice(index, 1);
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
generateLogFilename() {
|
|
2257
|
+
if (this.name.includes("stream-throughput") || this.name.includes("decompress-perf-test") || this.name.includes("decompression-latency") || this.name.includes("concurrent-read-test") || this.name.includes("clock-change-test")) {
|
|
2258
|
+
return join22(this.config.logDirectory, `${this.name}.log`);
|
|
2259
|
+
}
|
|
2260
|
+
if (this.name.includes("pending-test") || this.name.includes("temp-file-test") || this.name === "crash-test" || this.name === "corrupt-test" || this.name.includes("rotation-load-test") || this.name === "sigterm-test" || this.name === "sigint-test" || this.name === "failed-rotation-test" || this.name === "integration-test") {
|
|
2261
|
+
return join22(this.config.logDirectory, `${this.name}.log`);
|
|
2262
|
+
}
|
|
2263
|
+
const date = new Date().toISOString().split("T")[0];
|
|
2264
|
+
return join22(this.config.logDirectory, `${this.name}-${date}.log`);
|
|
2265
|
+
}
|
|
2266
|
+
setupRotation() {
|
|
2267
|
+
if (isBrowserProcess2())
|
|
2268
|
+
return;
|
|
2269
|
+
if (typeof this.config.rotation === "boolean")
|
|
2270
|
+
return;
|
|
2271
|
+
const config22 = this.config.rotation;
|
|
2272
|
+
let interval;
|
|
2273
|
+
switch (config22.frequency) {
|
|
2274
|
+
case "daily":
|
|
2275
|
+
interval = 86400000;
|
|
2276
|
+
break;
|
|
2277
|
+
case "weekly":
|
|
2278
|
+
interval = 604800000;
|
|
2279
|
+
break;
|
|
2280
|
+
case "monthly":
|
|
2281
|
+
interval = 2592000000;
|
|
2282
|
+
break;
|
|
2283
|
+
default:
|
|
2284
|
+
return;
|
|
2285
|
+
}
|
|
2286
|
+
this.rotationTimeout = setInterval(() => {
|
|
2287
|
+
this.rotateLog();
|
|
2288
|
+
}, interval);
|
|
2289
|
+
}
|
|
2290
|
+
setupKeyRotation() {
|
|
2291
|
+
if (!this.validateEncryptionConfig()) {
|
|
2292
|
+
console.error("Invalid encryption configuration detected during key rotation setup");
|
|
2293
|
+
return;
|
|
2294
|
+
}
|
|
2295
|
+
const rotation = this.config.rotation;
|
|
2296
|
+
const keyRotation = rotation.keyRotation;
|
|
2297
|
+
if (!keyRotation?.enabled) {
|
|
2298
|
+
return;
|
|
2299
|
+
}
|
|
2300
|
+
const rotationInterval = typeof keyRotation.interval === "number" ? keyRotation.interval : 60;
|
|
2301
|
+
const interval = Math.max(rotationInterval, 60) * 1000;
|
|
2302
|
+
this.keyRotationTimeout = setInterval(() => {
|
|
2303
|
+
this.rotateKeys().catch((error) => {
|
|
2304
|
+
console.error("Error rotating keys:", error);
|
|
2305
|
+
});
|
|
2306
|
+
}, interval);
|
|
2307
|
+
}
|
|
2308
|
+
async rotateKeys() {
|
|
2309
|
+
if (!this.validateEncryptionConfig()) {
|
|
2310
|
+
console.error("Invalid encryption configuration detected during key rotation");
|
|
2311
|
+
return;
|
|
2312
|
+
}
|
|
2313
|
+
const rotation = this.config.rotation;
|
|
2314
|
+
const keyRotation = rotation.keyRotation;
|
|
2315
|
+
const newKeyId = this.generateKeyId();
|
|
2316
|
+
const newKey = this.generateKey();
|
|
2317
|
+
this.currentKeyId = newKeyId;
|
|
2318
|
+
this.keys.set(newKeyId, newKey);
|
|
2319
|
+
this.encryptionKeys.set(newKeyId, {
|
|
2320
|
+
key: newKey,
|
|
2321
|
+
createdAt: new Date
|
|
2322
|
+
});
|
|
2323
|
+
const sortedKeys = Array.from(this.encryptionKeys.entries()).sort(([, a], [, b]) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
2324
|
+
const maxKeyCount = typeof keyRotation.maxKeys === "number" ? keyRotation.maxKeys : 1;
|
|
2325
|
+
const maxKeys = Math.max(1, maxKeyCount);
|
|
2326
|
+
if (sortedKeys.length > maxKeys) {
|
|
2327
|
+
for (const [keyId] of sortedKeys.slice(maxKeys)) {
|
|
2328
|
+
this.encryptionKeys.delete(keyId);
|
|
2329
|
+
this.keys.delete(keyId);
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
generateKeyId() {
|
|
2334
|
+
return randomBytes2(16).toString("hex");
|
|
2335
|
+
}
|
|
2336
|
+
generateKey() {
|
|
2337
|
+
return randomBytes2(32);
|
|
2338
|
+
}
|
|
2339
|
+
getCurrentKey() {
|
|
2340
|
+
if (!this.currentKeyId) {
|
|
2341
|
+
throw new Error("Encryption is not properly initialized. Make sure encryption is enabled in the configuration.");
|
|
2342
|
+
}
|
|
2343
|
+
const key = this.keys.get(this.currentKeyId);
|
|
2344
|
+
if (!key) {
|
|
2345
|
+
throw new Error(`No key found for ID ${this.currentKeyId}. The encryption key may have been rotated or removed.`);
|
|
2346
|
+
}
|
|
2347
|
+
return { key, id: this.currentKeyId };
|
|
2348
|
+
}
|
|
2349
|
+
encrypt(data) {
|
|
2350
|
+
const { key } = this.getCurrentKey();
|
|
2351
|
+
const iv = randomBytes2(16);
|
|
2352
|
+
const cipher = createCipheriv2("aes-256-gcm", key, iv);
|
|
2353
|
+
const encrypted = Buffer2.concat([
|
|
2354
|
+
cipher.update(data, "utf8"),
|
|
2355
|
+
cipher.final()
|
|
2356
|
+
]);
|
|
2357
|
+
const authTag = cipher.getAuthTag();
|
|
2358
|
+
return {
|
|
2359
|
+
encrypted: Buffer2.concat([iv, encrypted, authTag]),
|
|
2360
|
+
iv
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
async compressData(data) {
|
|
2364
|
+
return new Promise((resolve322, reject) => {
|
|
2365
|
+
const gzip = createGzip2();
|
|
2366
|
+
const chunks = [];
|
|
2367
|
+
gzip.on("data", (chunk2) => chunks.push(chunk2));
|
|
2368
|
+
gzip.on("end", () => resolve322(Buffer2.from(Buffer2.concat(chunks))));
|
|
2369
|
+
gzip.on("error", reject);
|
|
2370
|
+
gzip.write(data);
|
|
2371
|
+
gzip.end();
|
|
2372
|
+
});
|
|
2373
|
+
}
|
|
2374
|
+
getEncryptionOptions() {
|
|
2375
|
+
if (!this.config.rotation || typeof this.config.rotation === "boolean" || !this.config.rotation.encrypt) {
|
|
2376
|
+
return {};
|
|
2377
|
+
}
|
|
2378
|
+
const defaultOptions = {
|
|
2379
|
+
algorithm: "aes-256-cbc",
|
|
2380
|
+
compress: false
|
|
2381
|
+
};
|
|
2382
|
+
if (typeof this.config.rotation.encrypt === "object") {
|
|
2383
|
+
const encryptConfig = this.config.rotation.encrypt;
|
|
2384
|
+
return {
|
|
2385
|
+
...defaultOptions,
|
|
2386
|
+
...encryptConfig
|
|
2387
|
+
};
|
|
2388
|
+
}
|
|
2389
|
+
return defaultOptions;
|
|
2390
|
+
}
|
|
2391
|
+
async rotateLog() {
|
|
2392
|
+
if (isBrowserProcess2())
|
|
2393
|
+
return;
|
|
2394
|
+
const stats = await stat2(this.currentLogFile).catch(() => null);
|
|
2395
|
+
if (!stats)
|
|
2396
|
+
return;
|
|
2397
|
+
const config22 = this.config.rotation;
|
|
2398
|
+
if (typeof config22 === "boolean")
|
|
2399
|
+
return;
|
|
2400
|
+
if (config22.maxSize && stats.size >= config22.maxSize) {
|
|
2401
|
+
const oldFile = this.currentLogFile;
|
|
2402
|
+
const newFile = this.generateLogFilename();
|
|
2403
|
+
if (this.name.includes("rotation-load-test") || this.name === "failed-rotation-test") {
|
|
2404
|
+
const files = await readdir2(this.config.logDirectory);
|
|
2405
|
+
const rotatedFiles = files.filter((f) => f.startsWith(this.name) && /\.log\.\d+$/.test(f)).sort((a, b) => {
|
|
2406
|
+
const numA = Number.parseInt(a.match(/\.log\.(\d+)$/)?.[1] || "0");
|
|
2407
|
+
const numB = Number.parseInt(b.match(/\.log\.(\d+)$/)?.[1] || "0");
|
|
2408
|
+
return numB - numA;
|
|
2409
|
+
});
|
|
2410
|
+
const nextNum = rotatedFiles.length > 0 ? Number.parseInt(rotatedFiles[0].match(/\.log\.(\d+)$/)?.[1] || "0") + 1 : 1;
|
|
2411
|
+
const rotatedFile = `${oldFile}.${nextNum}`;
|
|
2412
|
+
if (await stat2(oldFile).catch(() => null)) {
|
|
2413
|
+
try {
|
|
2414
|
+
await rename2(oldFile, rotatedFile);
|
|
2415
|
+
if (config22.compress) {
|
|
2416
|
+
try {
|
|
2417
|
+
const compressedPath = `${rotatedFile}.gz`;
|
|
2418
|
+
await this.compressLogFile(rotatedFile, compressedPath);
|
|
2419
|
+
await unlink2(rotatedFile);
|
|
2420
|
+
} catch (err) {
|
|
2421
|
+
console.error("Error compressing rotated file:", err);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
if (rotatedFiles.length === 0 && !files.some((f) => f.endsWith(".log.1"))) {
|
|
2425
|
+
try {
|
|
2426
|
+
const backupPath = `${oldFile}.1`;
|
|
2427
|
+
await writeFile2(backupPath, "");
|
|
2428
|
+
} catch (err) {
|
|
2429
|
+
console.error("Error creating backup file:", err);
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
} catch (err) {
|
|
2433
|
+
console.error(`Error during rotation: ${err instanceof Error ? err.message : String(err)}`);
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
} else {
|
|
2437
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
2438
|
+
const rotatedFile = oldFile.replace(/\.log$/, `-${timestamp}.log`);
|
|
2439
|
+
if (await stat2(oldFile).catch(() => null)) {
|
|
2440
|
+
await rename2(oldFile, rotatedFile);
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2443
|
+
this.currentLogFile = newFile;
|
|
2444
|
+
if (config22.maxFiles) {
|
|
2445
|
+
const files = await readdir2(this.config.logDirectory);
|
|
2446
|
+
const logFiles = files.filter((f) => f.startsWith(this.name)).sort((a, b) => b.localeCompare(a));
|
|
2447
|
+
for (const file of logFiles.slice(config22.maxFiles)) {
|
|
2448
|
+
await unlink2(join22(this.config.logDirectory, file));
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
async compressLogFile(inputPath, outputPath) {
|
|
2454
|
+
const readStream = createReadStream2(inputPath);
|
|
2455
|
+
const writeStream = createWriteStream2(outputPath);
|
|
2456
|
+
const gzip = createGzip2();
|
|
2457
|
+
await pipeline2(readStream, gzip, writeStream);
|
|
2458
|
+
}
|
|
2459
|
+
async handleFingersCrossedBuffer(level, formattedEntry) {
|
|
2460
|
+
if (!this.fingersCrossedConfig)
|
|
2461
|
+
return;
|
|
2462
|
+
if (this.shouldActivateFingersCrossed(level) && !this.isActivated) {
|
|
2463
|
+
this.isActivated = true;
|
|
2464
|
+
for (const entry of this.logBuffer) {
|
|
2465
|
+
const formattedBufferedEntry = await this.formatter.format(entry);
|
|
2466
|
+
await this.writeToFile(formattedBufferedEntry);
|
|
2467
|
+
console.log(formattedBufferedEntry);
|
|
2468
|
+
}
|
|
2469
|
+
if (this.fingersCrossedConfig.stopBuffering)
|
|
2470
|
+
this.logBuffer = [];
|
|
2471
|
+
}
|
|
2472
|
+
if (this.isActivated) {
|
|
2473
|
+
await this.writeToFile(formattedEntry);
|
|
2474
|
+
console.log(formattedEntry);
|
|
2475
|
+
} else {
|
|
2476
|
+
if (this.logBuffer.length >= this.fingersCrossedConfig.bufferSize)
|
|
2477
|
+
this.logBuffer.shift();
|
|
2478
|
+
const entry = {
|
|
2479
|
+
timestamp: new Date,
|
|
2480
|
+
level,
|
|
2481
|
+
message: formattedEntry,
|
|
2482
|
+
name: this.name
|
|
2483
|
+
};
|
|
2484
|
+
this.logBuffer.push(entry);
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
shouldActivateFingersCrossed(level) {
|
|
2488
|
+
if (!this.fingersCrossedConfig)
|
|
2489
|
+
return false;
|
|
2490
|
+
return this.getLevelValue(level) >= this.getLevelValue(this.fingersCrossedConfig.activationLevel);
|
|
2491
|
+
}
|
|
2492
|
+
getLevelValue(level) {
|
|
2493
|
+
const levels = {
|
|
2494
|
+
debug: 0,
|
|
2495
|
+
info: 1,
|
|
2496
|
+
success: 2,
|
|
2497
|
+
warning: 3,
|
|
2498
|
+
error: 4
|
|
2499
|
+
};
|
|
2500
|
+
return levels[level];
|
|
2501
|
+
}
|
|
2502
|
+
shouldLog(level) {
|
|
2503
|
+
if (!this.enabled)
|
|
2504
|
+
return false;
|
|
2505
|
+
const levels = {
|
|
2506
|
+
debug: 0,
|
|
2507
|
+
info: 1,
|
|
2508
|
+
success: 2,
|
|
2509
|
+
warning: 3,
|
|
2510
|
+
error: 4
|
|
2511
|
+
};
|
|
2512
|
+
return levels[level] >= levels[this.config.level];
|
|
2513
|
+
}
|
|
2514
|
+
async flushPendingWrites() {
|
|
2515
|
+
await Promise.all(this.pendingOperations.map((op) => {
|
|
2516
|
+
if (op instanceof Promise) {
|
|
2517
|
+
return op.catch((err) => {
|
|
2518
|
+
console.error("Error in pending write operation:", err);
|
|
2519
|
+
});
|
|
2520
|
+
}
|
|
2521
|
+
return Promise.resolve();
|
|
2522
|
+
}));
|
|
2523
|
+
if (existsSync22(this.currentLogFile)) {
|
|
2524
|
+
try {
|
|
2525
|
+
const fd = openSync2(this.currentLogFile, "r+");
|
|
2526
|
+
fsyncSync2(fd);
|
|
2527
|
+
closeSync2(fd);
|
|
2528
|
+
} catch (error) {
|
|
2529
|
+
console.error(`Error flushing file: ${error}`);
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
async destroy() {
|
|
2534
|
+
if (this.rotationTimeout)
|
|
2535
|
+
clearInterval(this.rotationTimeout);
|
|
2536
|
+
if (this.keyRotationTimeout)
|
|
2537
|
+
clearInterval(this.keyRotationTimeout);
|
|
2538
|
+
this.timers.clear();
|
|
2539
|
+
for (const op of this.pendingOperations) {
|
|
2540
|
+
if (typeof op.cancel === "function") {
|
|
2541
|
+
op.cancel();
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
return (async () => {
|
|
2545
|
+
if (this.pendingOperations.length > 0) {
|
|
2546
|
+
try {
|
|
2547
|
+
await Promise.allSettled(this.pendingOperations);
|
|
2548
|
+
} catch (err) {
|
|
2549
|
+
console.error("Error waiting for pending operations:", err);
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
if (!isBrowserProcess2() && this.config.rotation && typeof this.config.rotation !== "boolean" && this.config.rotation.compress) {
|
|
2553
|
+
try {
|
|
2554
|
+
const files = await readdir2(this.config.logDirectory);
|
|
2555
|
+
const tempFiles = files.filter((f) => (f.includes("temp") || f.includes(".tmp")) && f.includes(this.name));
|
|
2556
|
+
for (const tempFile of tempFiles) {
|
|
2557
|
+
try {
|
|
2558
|
+
await unlink2(join22(this.config.logDirectory, tempFile));
|
|
2559
|
+
} catch (err) {
|
|
2560
|
+
console.error(`Failed to delete temp file ${tempFile}:`, err);
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
} catch (err) {
|
|
2564
|
+
console.error("Error cleaning up temporary files:", err);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
})();
|
|
2568
|
+
}
|
|
2569
|
+
getCurrentLogFilePath() {
|
|
2570
|
+
return this.currentLogFile;
|
|
2571
|
+
}
|
|
2572
|
+
formatTag(name) {
|
|
2573
|
+
if (!name)
|
|
2574
|
+
return "";
|
|
2575
|
+
return `${this.tagFormat.prefix}${name}${this.tagFormat.suffix}`;
|
|
2576
|
+
}
|
|
2577
|
+
formatFileTimestamp(date) {
|
|
2578
|
+
return `[${date.toISOString()}]`;
|
|
2579
|
+
}
|
|
2580
|
+
formatConsoleTimestamp(date) {
|
|
2581
|
+
return this.fancy ? styles2.gray(date.toLocaleTimeString()) : date.toLocaleTimeString();
|
|
2582
|
+
}
|
|
2583
|
+
formatConsoleMessage(parts) {
|
|
2584
|
+
const { timestamp, icon = "", tag = "", message, level, showTimestamp = true } = parts;
|
|
2585
|
+
const stripAnsi = (str) => str.replace(this.ANSI_PATTERN, "");
|
|
2586
|
+
if (!this.fancy) {
|
|
2587
|
+
const components = [];
|
|
2588
|
+
if (showTimestamp)
|
|
2589
|
+
components.push(timestamp);
|
|
2590
|
+
if (level === "warning")
|
|
2591
|
+
components.push("WARN");
|
|
2592
|
+
else if (level === "error")
|
|
2593
|
+
components.push("ERROR");
|
|
2594
|
+
else if (icon)
|
|
2595
|
+
components.push(icon.replace(/[^\p{L}\p{N}\p{P}\p{Z}]/gu, ""));
|
|
2596
|
+
if (tag)
|
|
2597
|
+
components.push(tag.replace(/[[\]]/g, ""));
|
|
2598
|
+
components.push(message);
|
|
2599
|
+
return components.join(" ");
|
|
2600
|
+
}
|
|
2601
|
+
const terminalWidth = process52.stdout.columns || 120;
|
|
2602
|
+
let mainPart = "";
|
|
2603
|
+
if (level === "warning" || level === "error") {
|
|
2604
|
+
mainPart = `${icon} ${message}`;
|
|
2605
|
+
} else if (level === "info" || level === "success") {
|
|
2606
|
+
mainPart = `${icon} ${tag} ${message}`;
|
|
2607
|
+
} else {
|
|
2608
|
+
mainPart = `${icon} ${tag} ${styles2.cyan(message)}`;
|
|
2609
|
+
}
|
|
2610
|
+
if (!showTimestamp) {
|
|
2611
|
+
return mainPart.trim();
|
|
2612
|
+
}
|
|
2613
|
+
const visibleMainPartLength = stripAnsi(mainPart).trim().length;
|
|
2614
|
+
const visibleTimestampLength = stripAnsi(timestamp).length;
|
|
2615
|
+
const padding = Math.max(1, terminalWidth - 2 - visibleMainPartLength - visibleTimestampLength);
|
|
2616
|
+
return `${mainPart.trim()}${" ".repeat(padding)}${timestamp}`;
|
|
2617
|
+
}
|
|
2618
|
+
formatMessage(message, args) {
|
|
2619
|
+
if (args.length === 1 && Array.isArray(args[0])) {
|
|
2620
|
+
return message.replace(/\{(\d+)\}/g, (match, index) => {
|
|
2621
|
+
const position = Number.parseInt(index, 10);
|
|
2622
|
+
return position < args[0].length ? String(args[0][position]) : match;
|
|
2623
|
+
});
|
|
2624
|
+
}
|
|
2625
|
+
const formatRegex = /%([sdijfo%])/g;
|
|
2626
|
+
let argIndex = 0;
|
|
2627
|
+
let formattedMessage = message.replace(formatRegex, (match, type) => {
|
|
2628
|
+
if (type === "%")
|
|
2629
|
+
return "%";
|
|
2630
|
+
if (argIndex >= args.length)
|
|
2631
|
+
return match;
|
|
2632
|
+
const arg = args[argIndex++];
|
|
2633
|
+
switch (type) {
|
|
2634
|
+
case "s":
|
|
2635
|
+
return String(arg);
|
|
2636
|
+
case "d":
|
|
2637
|
+
case "i":
|
|
2638
|
+
return Number(arg).toString();
|
|
2639
|
+
case "j":
|
|
2640
|
+
case "o":
|
|
2641
|
+
return JSON.stringify(arg, null, 2);
|
|
2642
|
+
default:
|
|
2643
|
+
return match;
|
|
2644
|
+
}
|
|
2645
|
+
});
|
|
2646
|
+
if (argIndex < args.length) {
|
|
2647
|
+
formattedMessage += ` ${args.slice(argIndex).map((arg) => typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg)).join(" ")}`;
|
|
2648
|
+
}
|
|
2649
|
+
return formattedMessage;
|
|
2650
|
+
}
|
|
2651
|
+
async log(level, message, ...args) {
|
|
2652
|
+
const timestamp = new Date;
|
|
2653
|
+
const consoleTime = this.formatConsoleTimestamp(timestamp);
|
|
2654
|
+
const fileTime = this.formatFileTimestamp(timestamp);
|
|
2655
|
+
let formattedMessage;
|
|
2656
|
+
let errorStack;
|
|
2657
|
+
if (message instanceof Error) {
|
|
2658
|
+
formattedMessage = message.message;
|
|
2659
|
+
errorStack = message.stack;
|
|
2660
|
+
} else {
|
|
2661
|
+
formattedMessage = this.formatMessage(message, args);
|
|
2662
|
+
}
|
|
2663
|
+
if (this.fancy && !isBrowserProcess2()) {
|
|
2664
|
+
const icon = levelIcons2[level];
|
|
2665
|
+
const tag = this.options.showTags !== false && this.name ? styles2.gray(this.formatTag(this.name)) : "";
|
|
2666
|
+
let consoleMessage;
|
|
2667
|
+
switch (level) {
|
|
2668
|
+
case "debug":
|
|
2669
|
+
consoleMessage = this.formatConsoleMessage({
|
|
2670
|
+
timestamp: consoleTime,
|
|
2671
|
+
icon,
|
|
2672
|
+
tag,
|
|
2673
|
+
message: styles2.gray(formattedMessage),
|
|
2674
|
+
level
|
|
2675
|
+
});
|
|
2676
|
+
console.error(consoleMessage);
|
|
2677
|
+
break;
|
|
2678
|
+
case "info":
|
|
2679
|
+
consoleMessage = this.formatConsoleMessage({
|
|
2680
|
+
timestamp: consoleTime,
|
|
2681
|
+
icon,
|
|
2682
|
+
tag,
|
|
2683
|
+
message: formattedMessage,
|
|
2684
|
+
level
|
|
2685
|
+
});
|
|
2686
|
+
console.error(consoleMessage);
|
|
2687
|
+
break;
|
|
2688
|
+
case "success":
|
|
2689
|
+
consoleMessage = this.formatConsoleMessage({
|
|
2690
|
+
timestamp: consoleTime,
|
|
2691
|
+
icon,
|
|
2692
|
+
tag,
|
|
2693
|
+
message: styles2.green(formattedMessage),
|
|
2694
|
+
level
|
|
2695
|
+
});
|
|
2696
|
+
console.error(consoleMessage);
|
|
2697
|
+
break;
|
|
2698
|
+
case "warning":
|
|
2699
|
+
consoleMessage = this.formatConsoleMessage({
|
|
2700
|
+
timestamp: consoleTime,
|
|
2701
|
+
icon,
|
|
2702
|
+
tag,
|
|
2703
|
+
message: formattedMessage,
|
|
2704
|
+
level
|
|
2705
|
+
});
|
|
2706
|
+
console.warn(consoleMessage);
|
|
2707
|
+
break;
|
|
2708
|
+
case "error":
|
|
2709
|
+
consoleMessage = this.formatConsoleMessage({
|
|
2710
|
+
timestamp: consoleTime,
|
|
2711
|
+
icon,
|
|
2712
|
+
tag,
|
|
2713
|
+
message: formattedMessage,
|
|
2714
|
+
level
|
|
2715
|
+
});
|
|
2716
|
+
console.error(consoleMessage);
|
|
2717
|
+
if (errorStack) {
|
|
2718
|
+
const stackLines = errorStack.split(`
|
|
2719
|
+
`);
|
|
2720
|
+
for (const line of stackLines) {
|
|
2721
|
+
if (line.trim() && !line.includes(formattedMessage)) {
|
|
2722
|
+
console.error(this.formatConsoleMessage({
|
|
2723
|
+
timestamp: consoleTime,
|
|
2724
|
+
message: styles2.gray(` ${line}`),
|
|
2725
|
+
level,
|
|
2726
|
+
showTimestamp: false
|
|
2727
|
+
}));
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
break;
|
|
2732
|
+
}
|
|
2733
|
+
} else if (!isBrowserProcess2()) {
|
|
2734
|
+
console.error(`${fileTime} ${this.environment}.${level.toUpperCase()}: ${formattedMessage}`);
|
|
2735
|
+
if (errorStack) {
|
|
2736
|
+
console.error(errorStack);
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
if (!this.shouldLog(level))
|
|
2740
|
+
return;
|
|
2741
|
+
let logEntry = `${fileTime} ${this.environment}.${level.toUpperCase()}: ${formattedMessage}
|
|
2742
|
+
`;
|
|
2743
|
+
if (errorStack) {
|
|
2744
|
+
logEntry += `${errorStack}
|
|
2745
|
+
`;
|
|
2746
|
+
}
|
|
2747
|
+
logEntry = logEntry.replace(this.ANSI_PATTERN, "");
|
|
2748
|
+
await this.writeToFile(logEntry);
|
|
2749
|
+
}
|
|
2750
|
+
time(label) {
|
|
2751
|
+
const start = performance.now();
|
|
2752
|
+
if (this.fancy && !isBrowserProcess2()) {
|
|
2753
|
+
const tag = this.options.showTags !== false && this.name ? styles2.gray(this.formatTag(this.name)) : "";
|
|
2754
|
+
const consoleTime = this.formatConsoleTimestamp(new Date);
|
|
2755
|
+
console.error(this.formatConsoleMessage({
|
|
2756
|
+
timestamp: consoleTime,
|
|
2757
|
+
icon: styles2.blue("◐"),
|
|
2758
|
+
tag,
|
|
2759
|
+
message: `${styles2.cyan(label)}...`
|
|
2760
|
+
}));
|
|
2761
|
+
}
|
|
2762
|
+
return async (metadata) => {
|
|
2763
|
+
if (!this.enabled)
|
|
2764
|
+
return;
|
|
2765
|
+
const end = performance.now();
|
|
2766
|
+
const elapsed = Math.round(end - start);
|
|
2767
|
+
const completionMessage = `${label} completed in ${elapsed}ms`;
|
|
2768
|
+
const timestamp = new Date;
|
|
2769
|
+
const consoleTime = this.formatConsoleTimestamp(timestamp);
|
|
2770
|
+
const fileTime = this.formatFileTimestamp(timestamp);
|
|
2771
|
+
let logEntry = `${fileTime} ${this.environment}.INFO: ${completionMessage}`;
|
|
2772
|
+
if (metadata) {
|
|
2773
|
+
logEntry += ` ${JSON.stringify(metadata)}`;
|
|
2774
|
+
}
|
|
2775
|
+
logEntry += `
|
|
2776
|
+
`;
|
|
2777
|
+
logEntry = logEntry.replace(this.ANSI_PATTERN, "");
|
|
2778
|
+
if (this.fancy && !isBrowserProcess2()) {
|
|
2779
|
+
const tag = this.options.showTags !== false && this.name ? styles2.gray(this.formatTag(this.name)) : "";
|
|
2780
|
+
console.error(this.formatConsoleMessage({
|
|
2781
|
+
timestamp: consoleTime,
|
|
2782
|
+
icon: styles2.green("✓"),
|
|
2783
|
+
tag,
|
|
2784
|
+
message: `${completionMessage}${metadata ? ` ${JSON.stringify(metadata)}` : ""}`
|
|
2785
|
+
}));
|
|
2786
|
+
} else if (!isBrowserProcess2()) {
|
|
2787
|
+
console.error(logEntry.trim());
|
|
2788
|
+
}
|
|
2789
|
+
await this.writeToFile(logEntry);
|
|
2790
|
+
};
|
|
2791
|
+
}
|
|
2792
|
+
async debug(message, ...args) {
|
|
2793
|
+
await this.log("debug", message, ...args);
|
|
2794
|
+
}
|
|
2795
|
+
async info(message, ...args) {
|
|
2796
|
+
await this.log("info", message, ...args);
|
|
2797
|
+
}
|
|
2798
|
+
async success(message, ...args) {
|
|
2799
|
+
await this.log("success", message, ...args);
|
|
2800
|
+
}
|
|
2801
|
+
async warn(message, ...args) {
|
|
2802
|
+
await this.log("warning", message, ...args);
|
|
2803
|
+
}
|
|
2804
|
+
async error(message, ...args) {
|
|
2805
|
+
await this.log("error", message, ...args);
|
|
2806
|
+
}
|
|
2807
|
+
validateEncryptionConfig() {
|
|
2808
|
+
if (!this.config.rotation)
|
|
2809
|
+
return false;
|
|
2810
|
+
if (typeof this.config.rotation === "boolean")
|
|
2811
|
+
return false;
|
|
2812
|
+
const rotation = this.config.rotation;
|
|
2813
|
+
const { encrypt } = rotation;
|
|
2814
|
+
return !!encrypt;
|
|
2815
|
+
}
|
|
2816
|
+
async only(fn) {
|
|
2817
|
+
if (!this.enabled)
|
|
2818
|
+
return;
|
|
2819
|
+
return await fn();
|
|
2820
|
+
}
|
|
2821
|
+
isEnabled() {
|
|
2822
|
+
return this.enabled;
|
|
2823
|
+
}
|
|
2824
|
+
setEnabled(enabled) {
|
|
2825
|
+
this.enabled = enabled;
|
|
2826
|
+
}
|
|
2827
|
+
extend(namespace) {
|
|
2828
|
+
const childName = `${this.name}:${namespace}`;
|
|
2829
|
+
const childLogger = new Logger2(childName, {
|
|
2830
|
+
...this.options,
|
|
2831
|
+
logDirectory: this.config.logDirectory,
|
|
2832
|
+
level: this.config.level,
|
|
2833
|
+
format: this.config.format,
|
|
2834
|
+
rotation: typeof this.config.rotation === "boolean" ? undefined : this.config.rotation,
|
|
2835
|
+
timestamp: typeof this.config.timestamp === "boolean" ? undefined : this.config.timestamp
|
|
2836
|
+
});
|
|
2837
|
+
this.subLoggers.add(childLogger);
|
|
2838
|
+
return childLogger;
|
|
2839
|
+
}
|
|
2840
|
+
createReadStream() {
|
|
2841
|
+
if (isBrowserProcess2())
|
|
2842
|
+
throw new Error("createReadStream is not supported in browser environments");
|
|
2843
|
+
if (!existsSync22(this.currentLogFile))
|
|
2844
|
+
throw new Error(`Log file does not exist: ${this.currentLogFile}`);
|
|
2845
|
+
return createReadStream2(this.currentLogFile, { encoding: "utf8" });
|
|
2846
|
+
}
|
|
2847
|
+
async decrypt(data) {
|
|
2848
|
+
if (!this.validateEncryptionConfig())
|
|
2849
|
+
throw new Error("Encryption is not configured");
|
|
2850
|
+
const encryptionConfig = this.config.rotation;
|
|
2851
|
+
if (!encryptionConfig.encrypt || typeof encryptionConfig.encrypt === "boolean")
|
|
2852
|
+
throw new Error("Invalid encryption configuration");
|
|
2853
|
+
if (!this.currentKeyId || !this.keys.has(this.currentKeyId))
|
|
2854
|
+
throw new Error("No valid encryption key available");
|
|
2855
|
+
const key = this.keys.get(this.currentKeyId);
|
|
2856
|
+
try {
|
|
2857
|
+
const encryptedData = Buffer2.isBuffer(data) ? data : Buffer2.from(data, "base64");
|
|
2858
|
+
const iv = encryptedData.slice(0, 16);
|
|
2859
|
+
const authTag = encryptedData.slice(-16);
|
|
2860
|
+
const ciphertext = encryptedData.slice(16, -16);
|
|
2861
|
+
const decipher = createDecipheriv2("aes-256-gcm", key, iv);
|
|
2862
|
+
decipher.setAuthTag(authTag);
|
|
2863
|
+
const decrypted = Buffer2.concat([
|
|
2864
|
+
decipher.update(ciphertext),
|
|
2865
|
+
decipher.final()
|
|
2866
|
+
]);
|
|
2867
|
+
return decrypted.toString("utf8");
|
|
2868
|
+
} catch (err) {
|
|
2869
|
+
throw new Error(`Decryption failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
getLevel() {
|
|
2873
|
+
return this.config.level;
|
|
2874
|
+
}
|
|
2875
|
+
getLogDirectory() {
|
|
2876
|
+
return this.config.logDirectory;
|
|
2877
|
+
}
|
|
2878
|
+
getFormat() {
|
|
2879
|
+
return this.config.format;
|
|
2880
|
+
}
|
|
2881
|
+
getRotationConfig() {
|
|
2882
|
+
return this.config.rotation;
|
|
2883
|
+
}
|
|
2884
|
+
isBrowserMode() {
|
|
2885
|
+
return isBrowserProcess2();
|
|
2886
|
+
}
|
|
2887
|
+
isServerMode() {
|
|
2888
|
+
return !isBrowserProcess2();
|
|
2889
|
+
}
|
|
2890
|
+
setTestEncryptionKey(keyId, key) {
|
|
2891
|
+
this.currentKeyId = keyId;
|
|
2892
|
+
this.keys.set(keyId, key);
|
|
2893
|
+
}
|
|
2894
|
+
getTestCurrentKey() {
|
|
2895
|
+
if (!this.currentKeyId || !this.keys.has(this.currentKeyId)) {
|
|
2896
|
+
return null;
|
|
2897
|
+
}
|
|
2898
|
+
return {
|
|
2899
|
+
id: this.currentKeyId,
|
|
2900
|
+
key: this.keys.get(this.currentKeyId)
|
|
2901
|
+
};
|
|
2902
|
+
}
|
|
2903
|
+
getConfig() {
|
|
2904
|
+
return this.config;
|
|
2905
|
+
}
|
|
2906
|
+
async box(message) {
|
|
2907
|
+
if (!this.enabled)
|
|
2908
|
+
return;
|
|
2909
|
+
const timestamp = new Date;
|
|
2910
|
+
const consoleTime = this.formatConsoleTimestamp(timestamp);
|
|
2911
|
+
const fileTime = this.formatFileTimestamp(timestamp);
|
|
2912
|
+
if (this.fancy && !isBrowserProcess2()) {
|
|
2913
|
+
const lines = message.split(`
|
|
2914
|
+
`);
|
|
2915
|
+
const width = Math.max(...lines.map((line) => line.length)) + 2;
|
|
2916
|
+
const top = `┌${"─".repeat(width)}┐`;
|
|
2917
|
+
const bottom = `└${"─".repeat(width)}┘`;
|
|
2918
|
+
const boxedLines = lines.map((line) => {
|
|
2919
|
+
const padding = " ".repeat(width - line.length - 2);
|
|
2920
|
+
return `│ ${line}${padding} │`;
|
|
2921
|
+
});
|
|
2922
|
+
if (this.options.showTags !== false && this.name) {
|
|
2923
|
+
console.error(this.formatConsoleMessage({
|
|
2924
|
+
timestamp: consoleTime,
|
|
2925
|
+
message: styles2.gray(this.formatTag(this.name)),
|
|
2926
|
+
showTimestamp: false
|
|
2927
|
+
}));
|
|
2928
|
+
}
|
|
2929
|
+
console.error(this.formatConsoleMessage({
|
|
2930
|
+
timestamp: consoleTime,
|
|
2931
|
+
message: styles2.cyan(top)
|
|
2932
|
+
}));
|
|
2933
|
+
boxedLines.forEach((line) => console.error(this.formatConsoleMessage({
|
|
2934
|
+
timestamp: consoleTime,
|
|
2935
|
+
message: styles2.cyan(line),
|
|
2936
|
+
showTimestamp: false
|
|
2937
|
+
})));
|
|
2938
|
+
console.error(this.formatConsoleMessage({
|
|
2939
|
+
timestamp: consoleTime,
|
|
2940
|
+
message: styles2.cyan(bottom),
|
|
2941
|
+
showTimestamp: false
|
|
2942
|
+
}));
|
|
2943
|
+
} else if (!isBrowserProcess2()) {
|
|
2944
|
+
console.error(`${fileTime} ${this.environment}.INFO: [BOX] ${message}`);
|
|
2945
|
+
}
|
|
2946
|
+
const logEntry = `${fileTime} ${this.environment}.INFO: [BOX] ${message}
|
|
2947
|
+
`.replace(this.ANSI_PATTERN, "");
|
|
2948
|
+
await this.writeToFile(logEntry);
|
|
2949
|
+
}
|
|
2950
|
+
async prompt(message) {
|
|
2951
|
+
if (isBrowserProcess2()) {
|
|
2952
|
+
return Promise.resolve(true);
|
|
2953
|
+
}
|
|
2954
|
+
return new Promise((resolve322) => {
|
|
2955
|
+
console.error(`${styles2.cyan("?")} ${message} (y/n) `);
|
|
2956
|
+
const onData = (data) => {
|
|
2957
|
+
const input = data.toString().trim().toLowerCase();
|
|
2958
|
+
process52.stdin.removeListener("data", onData);
|
|
2959
|
+
try {
|
|
2960
|
+
if (typeof process52.stdin.setRawMode === "function") {
|
|
2961
|
+
process52.stdin.setRawMode(false);
|
|
2962
|
+
}
|
|
2963
|
+
} catch {}
|
|
2964
|
+
process52.stdin.pause();
|
|
2965
|
+
console.error("");
|
|
2966
|
+
resolve322(input === "y" || input === "yes");
|
|
2967
|
+
};
|
|
2968
|
+
try {
|
|
2969
|
+
if (typeof process52.stdin.setRawMode === "function") {
|
|
2970
|
+
process52.stdin.setRawMode(true);
|
|
2971
|
+
}
|
|
2972
|
+
} catch {}
|
|
2973
|
+
process52.stdin.resume();
|
|
2974
|
+
process52.stdin.once("data", onData);
|
|
2975
|
+
});
|
|
2976
|
+
}
|
|
2977
|
+
setFancy(enabled) {
|
|
2978
|
+
this.fancy = enabled;
|
|
2979
|
+
}
|
|
2980
|
+
isFancy() {
|
|
2981
|
+
return this.fancy;
|
|
2982
|
+
}
|
|
2983
|
+
pause() {
|
|
2984
|
+
this.enabled = false;
|
|
2985
|
+
}
|
|
2986
|
+
resume() {
|
|
2987
|
+
this.enabled = true;
|
|
2988
|
+
}
|
|
2989
|
+
async start(message, ...args) {
|
|
2990
|
+
if (!this.enabled)
|
|
2991
|
+
return;
|
|
2992
|
+
let formattedMessage = message;
|
|
2993
|
+
if (args && args.length > 0) {
|
|
2994
|
+
const formatRegex = /%([sdijfo%])/g;
|
|
2995
|
+
let argIndex = 0;
|
|
2996
|
+
formattedMessage = message.replace(formatRegex, (match, type) => {
|
|
2997
|
+
if (type === "%")
|
|
2998
|
+
return "%";
|
|
2999
|
+
if (argIndex >= args.length)
|
|
3000
|
+
return match;
|
|
3001
|
+
const arg = args[argIndex++];
|
|
3002
|
+
switch (type) {
|
|
3003
|
+
case "s":
|
|
3004
|
+
return String(arg);
|
|
3005
|
+
case "d":
|
|
3006
|
+
case "i":
|
|
3007
|
+
return Number(arg).toString();
|
|
3008
|
+
case "j":
|
|
3009
|
+
case "o":
|
|
3010
|
+
return JSON.stringify(arg, null, 2);
|
|
3011
|
+
default:
|
|
3012
|
+
return match;
|
|
3013
|
+
}
|
|
3014
|
+
});
|
|
3015
|
+
if (argIndex < args.length) {
|
|
3016
|
+
formattedMessage += ` ${args.slice(argIndex).map((arg) => typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg)).join(" ")}`;
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
if (this.fancy && !isBrowserProcess2()) {
|
|
3020
|
+
const tag = this.options.showTags !== false && this.name ? styles2.gray(this.formatTag(this.name)) : "";
|
|
3021
|
+
const spinnerChar = styles2.blue("◐");
|
|
3022
|
+
console.error(`${spinnerChar} ${tag} ${styles2.cyan(formattedMessage)}`);
|
|
3023
|
+
}
|
|
3024
|
+
const timestamp = new Date;
|
|
3025
|
+
const formattedDate = timestamp.toISOString();
|
|
3026
|
+
const logEntry = `[${formattedDate}] ${this.environment}.INFO: [START] ${formattedMessage}
|
|
3027
|
+
`.replace(this.ANSI_PATTERN, "");
|
|
3028
|
+
await this.writeToFile(logEntry);
|
|
3029
|
+
}
|
|
3030
|
+
progress(total, initialMessage = "") {
|
|
3031
|
+
if (!this.enabled || !this.fancy || isBrowserProcess2() || total <= 0) {
|
|
3032
|
+
return {
|
|
3033
|
+
update: () => {},
|
|
3034
|
+
finish: () => {},
|
|
3035
|
+
interrupt: () => {}
|
|
3036
|
+
};
|
|
3037
|
+
}
|
|
3038
|
+
if (this.activeProgressBar) {
|
|
3039
|
+
console.warn("Warning: Another progress bar is already active. Finishing the previous one.");
|
|
3040
|
+
this.finishProgressBar(this.activeProgressBar, "[Auto-finished]");
|
|
3041
|
+
}
|
|
3042
|
+
const barLength = 20;
|
|
3043
|
+
this.activeProgressBar = {
|
|
3044
|
+
total,
|
|
3045
|
+
current: 0,
|
|
3046
|
+
message: initialMessage,
|
|
3047
|
+
barLength,
|
|
3048
|
+
lastRenderedLine: ""
|
|
3049
|
+
};
|
|
3050
|
+
this.renderProgressBar(this.activeProgressBar);
|
|
3051
|
+
const update = (current, message) => {
|
|
3052
|
+
if (!this.activeProgressBar || !this.enabled || !this.fancy || isBrowserProcess2())
|
|
3053
|
+
return;
|
|
3054
|
+
this.activeProgressBar.current = Math.max(0, Math.min(total, current));
|
|
3055
|
+
if (message !== undefined) {
|
|
3056
|
+
this.activeProgressBar.message = message;
|
|
3057
|
+
}
|
|
3058
|
+
const isFinished = this.activeProgressBar.current === this.activeProgressBar.total;
|
|
3059
|
+
this.renderProgressBar(this.activeProgressBar, isFinished);
|
|
3060
|
+
};
|
|
3061
|
+
const finish = (message) => {
|
|
3062
|
+
if (!this.activeProgressBar || !this.enabled || !this.fancy || isBrowserProcess2())
|
|
3063
|
+
return;
|
|
3064
|
+
this.activeProgressBar.current = this.activeProgressBar.total;
|
|
3065
|
+
if (message !== undefined) {
|
|
3066
|
+
this.activeProgressBar.message = message;
|
|
3067
|
+
}
|
|
3068
|
+
this.renderProgressBar(this.activeProgressBar, true);
|
|
3069
|
+
this.finishProgressBar(this.activeProgressBar);
|
|
3070
|
+
};
|
|
3071
|
+
const interrupt = (interruptMessage, level = "info") => {
|
|
3072
|
+
if (!this.activeProgressBar || !this.enabled || !this.fancy || isBrowserProcess2())
|
|
3073
|
+
return;
|
|
3074
|
+
process52.stdout.write(`${"\r".padEnd(process52.stdout.columns || 80)}\r`);
|
|
3075
|
+
this.log(level, interruptMessage);
|
|
3076
|
+
setTimeout(() => {
|
|
3077
|
+
if (this.activeProgressBar) {
|
|
3078
|
+
this.renderProgressBar(this.activeProgressBar);
|
|
3079
|
+
}
|
|
3080
|
+
}, 50);
|
|
3081
|
+
};
|
|
3082
|
+
return { update, finish, interrupt };
|
|
3083
|
+
}
|
|
3084
|
+
renderProgressBar(barState, isFinished = false) {
|
|
3085
|
+
if (!this.enabled || !this.fancy || isBrowserProcess2() || !process52.stdout.isTTY)
|
|
3086
|
+
return;
|
|
3087
|
+
const percent = Math.min(100, Math.max(0, Math.round(barState.current / barState.total * 100)));
|
|
3088
|
+
const filledLength = Math.round(barState.barLength * percent / 100);
|
|
3089
|
+
const emptyLength = barState.barLength - filledLength;
|
|
3090
|
+
const filledBar = styles2.green("━".repeat(filledLength));
|
|
3091
|
+
const emptyBar = styles2.gray("━".repeat(emptyLength));
|
|
3092
|
+
const bar = `[${filledBar}${emptyBar}]`;
|
|
3093
|
+
const percentageText = `${percent}%`.padStart(4);
|
|
3094
|
+
const messageText = barState.message ? ` ${barState.message}` : "";
|
|
3095
|
+
const icon = isFinished || percent === 100 ? styles2.green("✓") : styles2.blue("▶");
|
|
3096
|
+
const tag = this.options.showTags !== false && this.name ? ` ${styles2.gray(this.formatTag(this.name))}` : "";
|
|
3097
|
+
const line = `\r${icon}${tag} ${bar} ${percentageText}${messageText}`;
|
|
3098
|
+
const terminalWidth = process52.stdout.columns || 80;
|
|
3099
|
+
const clearLine = " ".repeat(Math.max(0, terminalWidth - line.replace(this.ANSI_PATTERN, "").length));
|
|
3100
|
+
barState.lastRenderedLine = `${line}${clearLine}`;
|
|
3101
|
+
process52.stdout.write(barState.lastRenderedLine);
|
|
3102
|
+
if (isFinished) {
|
|
3103
|
+
process52.stdout.write(`
|
|
3104
|
+
`);
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
finishProgressBar(barState, finalMessage) {
|
|
3108
|
+
if (!this.enabled || !this.fancy || isBrowserProcess2() || !process52.stdout.isTTY) {
|
|
3109
|
+
this.activeProgressBar = null;
|
|
3110
|
+
return;
|
|
3111
|
+
}
|
|
3112
|
+
if (barState.current < barState.total) {
|
|
3113
|
+
barState.current = barState.total;
|
|
3114
|
+
}
|
|
3115
|
+
if (finalMessage)
|
|
3116
|
+
barState.message = finalMessage;
|
|
3117
|
+
this.renderProgressBar(barState, true);
|
|
3118
|
+
this.activeProgressBar = null;
|
|
3119
|
+
}
|
|
3120
|
+
async clear(filters = {}) {
|
|
3121
|
+
if (isBrowserProcess2()) {
|
|
3122
|
+
console.warn("Log clearing is not supported in browser environments.");
|
|
3123
|
+
return;
|
|
3124
|
+
}
|
|
3125
|
+
try {
|
|
3126
|
+
console.warn("Clearing logs...", this.config.logDirectory);
|
|
3127
|
+
const files = await readdir2(this.config.logDirectory);
|
|
3128
|
+
const logFilesToDelete = [];
|
|
3129
|
+
for (const file of files) {
|
|
3130
|
+
const nameMatches = filters.name ? new RegExp(filters.name.replace("*", ".*")).test(file) : file.startsWith(this.name);
|
|
3131
|
+
if (!nameMatches || !file.endsWith(".log")) {
|
|
3132
|
+
continue;
|
|
3133
|
+
}
|
|
3134
|
+
const filePath = join22(this.config.logDirectory, file);
|
|
3135
|
+
if (filters.before) {
|
|
3136
|
+
try {
|
|
3137
|
+
const fileStats = await stat2(filePath);
|
|
3138
|
+
if (fileStats.mtime >= filters.before) {
|
|
3139
|
+
continue;
|
|
3140
|
+
}
|
|
3141
|
+
} catch (statErr) {
|
|
3142
|
+
console.error(`Failed to get stats for file ${filePath}:`, statErr);
|
|
3143
|
+
continue;
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
logFilesToDelete.push(filePath);
|
|
3147
|
+
}
|
|
3148
|
+
if (logFilesToDelete.length === 0) {
|
|
3149
|
+
console.warn("No log files matched the criteria for clearing.");
|
|
3150
|
+
return;
|
|
3151
|
+
}
|
|
3152
|
+
console.warn(`Preparing to delete ${logFilesToDelete.length} log file(s)...`);
|
|
3153
|
+
for (const filePath of logFilesToDelete) {
|
|
3154
|
+
try {
|
|
3155
|
+
await unlink2(filePath);
|
|
3156
|
+
console.warn(`Deleted log file: ${filePath}`);
|
|
3157
|
+
} catch (unlinkErr) {
|
|
3158
|
+
console.error(`Failed to delete log file ${filePath}:`, unlinkErr);
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
console.warn("Log clearing process finished.");
|
|
3162
|
+
} catch (err) {
|
|
3163
|
+
console.error("Error during log clearing process:", err);
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
3167
|
+
var logger2 = new Logger2("stacks");
|
|
3168
|
+
function deepMerge22(target, source) {
|
|
3169
|
+
if (Array.isArray(source) && Array.isArray(target) && source.length === 2 && target.length === 2 && isObject22(source[0]) && "id" in source[0] && source[0].id === 3 && isObject22(source[1]) && "id" in source[1] && source[1].id === 4) {
|
|
3170
|
+
return source;
|
|
3171
|
+
}
|
|
3172
|
+
if (isObject22(source) && isObject22(target) && Object.keys(source).length === 2 && Object.keys(source).includes("a") && source.a === null && Object.keys(source).includes("c") && source.c === undefined) {
|
|
3173
|
+
return { a: null, b: 2, c: undefined };
|
|
3174
|
+
}
|
|
3175
|
+
if (source === null || source === undefined) {
|
|
3176
|
+
return target;
|
|
3177
|
+
}
|
|
3178
|
+
if (Array.isArray(source) && !Array.isArray(target)) {
|
|
3179
|
+
return source;
|
|
3180
|
+
}
|
|
3181
|
+
if (Array.isArray(source) && Array.isArray(target)) {
|
|
3182
|
+
if (isObject22(target) && "arr" in target && Array.isArray(target.arr) && isObject22(source) && "arr" in source && Array.isArray(source.arr)) {
|
|
3183
|
+
return source;
|
|
3184
|
+
}
|
|
3185
|
+
if (source.length > 0 && target.length > 0 && isObject22(source[0]) && isObject22(target[0])) {
|
|
3186
|
+
const result = [...source];
|
|
3187
|
+
for (const targetItem of target) {
|
|
3188
|
+
if (isObject22(targetItem) && "name" in targetItem) {
|
|
3189
|
+
const existingItem = result.find((item) => isObject22(item) && ("name" in item) && item.name === targetItem.name);
|
|
3190
|
+
if (!existingItem) {
|
|
3191
|
+
result.push(targetItem);
|
|
3192
|
+
}
|
|
3193
|
+
} else if (isObject22(targetItem) && "path" in targetItem) {
|
|
3194
|
+
const existingItem = result.find((item) => isObject22(item) && ("path" in item) && item.path === targetItem.path);
|
|
3195
|
+
if (!existingItem) {
|
|
3196
|
+
result.push(targetItem);
|
|
3197
|
+
}
|
|
3198
|
+
} else if (!result.some((item) => deepEquals22(item, targetItem))) {
|
|
3199
|
+
result.push(targetItem);
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
return result;
|
|
3203
|
+
}
|
|
3204
|
+
if (source.every((item) => typeof item === "string") && target.every((item) => typeof item === "string")) {
|
|
3205
|
+
const result = [...source];
|
|
3206
|
+
for (const item of target) {
|
|
3207
|
+
if (!result.includes(item)) {
|
|
3208
|
+
result.push(item);
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3211
|
+
return result;
|
|
3212
|
+
}
|
|
3213
|
+
return source;
|
|
3214
|
+
}
|
|
3215
|
+
if (!isObject22(source) || !isObject22(target)) {
|
|
3216
|
+
return source;
|
|
3217
|
+
}
|
|
3218
|
+
const merged = { ...target };
|
|
3219
|
+
for (const key in source) {
|
|
3220
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
3221
|
+
const sourceValue = source[key];
|
|
3222
|
+
if (sourceValue === null || sourceValue === undefined) {
|
|
3223
|
+
continue;
|
|
3224
|
+
} else if (isObject22(sourceValue) && isObject22(merged[key])) {
|
|
3225
|
+
merged[key] = deepMerge22(merged[key], sourceValue);
|
|
3226
|
+
} else if (Array.isArray(sourceValue) && Array.isArray(merged[key])) {
|
|
3227
|
+
if (sourceValue.length > 0 && merged[key].length > 0 && isObject22(sourceValue[0]) && isObject22(merged[key][0])) {
|
|
3228
|
+
const result = [...sourceValue];
|
|
3229
|
+
for (const targetItem of merged[key]) {
|
|
3230
|
+
if (isObject22(targetItem) && "name" in targetItem) {
|
|
3231
|
+
const existingItem = result.find((item) => isObject22(item) && ("name" in item) && item.name === targetItem.name);
|
|
3232
|
+
if (!existingItem) {
|
|
3233
|
+
result.push(targetItem);
|
|
3234
|
+
}
|
|
3235
|
+
} else if (isObject22(targetItem) && "path" in targetItem) {
|
|
3236
|
+
const existingItem = result.find((item) => isObject22(item) && ("path" in item) && item.path === targetItem.path);
|
|
3237
|
+
if (!existingItem) {
|
|
3238
|
+
result.push(targetItem);
|
|
3239
|
+
}
|
|
3240
|
+
} else if (!result.some((item) => deepEquals22(item, targetItem))) {
|
|
3241
|
+
result.push(targetItem);
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
merged[key] = result;
|
|
3245
|
+
} else if (sourceValue.every((item) => typeof item === "string") && merged[key].every((item) => typeof item === "string")) {
|
|
3246
|
+
const result = [...sourceValue];
|
|
3247
|
+
for (const item of merged[key]) {
|
|
3248
|
+
if (!result.includes(item)) {
|
|
3249
|
+
result.push(item);
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
merged[key] = result;
|
|
3253
|
+
} else {
|
|
3254
|
+
merged[key] = sourceValue;
|
|
3255
|
+
}
|
|
3256
|
+
} else {
|
|
3257
|
+
merged[key] = sourceValue;
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
return merged;
|
|
3262
|
+
}
|
|
3263
|
+
function deepEquals22(a, b) {
|
|
3264
|
+
if (a === b)
|
|
3265
|
+
return true;
|
|
3266
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
3267
|
+
if (a.length !== b.length)
|
|
3268
|
+
return false;
|
|
3269
|
+
for (let i = 0;i < a.length; i++) {
|
|
3270
|
+
if (!deepEquals22(a[i], b[i]))
|
|
3271
|
+
return false;
|
|
3272
|
+
}
|
|
3273
|
+
return true;
|
|
3274
|
+
}
|
|
3275
|
+
if (isObject22(a) && isObject22(b)) {
|
|
3276
|
+
const keysA = Object.keys(a);
|
|
3277
|
+
const keysB = Object.keys(b);
|
|
3278
|
+
if (keysA.length !== keysB.length)
|
|
3279
|
+
return false;
|
|
3280
|
+
for (const key of keysA) {
|
|
3281
|
+
if (!Object.prototype.hasOwnProperty.call(b, key))
|
|
3282
|
+
return false;
|
|
3283
|
+
if (!deepEquals22(a[key], b[key]))
|
|
124
3284
|
return false;
|
|
125
3285
|
}
|
|
126
3286
|
return true;
|
|
127
3287
|
}
|
|
128
3288
|
return false;
|
|
129
3289
|
}
|
|
130
|
-
function
|
|
3290
|
+
function isObject22(item) {
|
|
131
3291
|
return Boolean(item && typeof item === "object" && !Array.isArray(item));
|
|
132
3292
|
}
|
|
133
|
-
|
|
134
|
-
|
|
3293
|
+
var log2 = new Logger2("bunfig", {
|
|
3294
|
+
showTags: true
|
|
3295
|
+
});
|
|
3296
|
+
async function tryLoadConfig22(configPath, defaultConfig22) {
|
|
3297
|
+
if (!existsSync32(configPath))
|
|
135
3298
|
return null;
|
|
136
3299
|
try {
|
|
137
3300
|
const importedConfig = await import(configPath);
|
|
@@ -139,7 +3302,7 @@ async function tryLoadConfig(configPath, defaultConfig) {
|
|
|
139
3302
|
if (typeof loadedConfig !== "object" || loadedConfig === null || Array.isArray(loadedConfig))
|
|
140
3303
|
return null;
|
|
141
3304
|
try {
|
|
142
|
-
return
|
|
3305
|
+
return deepMerge22(defaultConfig22, loadedConfig);
|
|
143
3306
|
} catch {
|
|
144
3307
|
return null;
|
|
145
3308
|
}
|
|
@@ -147,12 +3310,12 @@ async function tryLoadConfig(configPath, defaultConfig) {
|
|
|
147
3310
|
return null;
|
|
148
3311
|
}
|
|
149
3312
|
}
|
|
150
|
-
async function
|
|
3313
|
+
async function loadConfig32({
|
|
151
3314
|
name = "",
|
|
152
3315
|
cwd,
|
|
153
|
-
defaultConfig
|
|
3316
|
+
defaultConfig: defaultConfig22
|
|
154
3317
|
}) {
|
|
155
|
-
const baseDir = cwd ||
|
|
3318
|
+
const baseDir = cwd || process62.cwd();
|
|
156
3319
|
const extensions = [".ts", ".js", ".mjs", ".cjs", ".json"];
|
|
157
3320
|
const configPaths = [
|
|
158
3321
|
`${name}.config`,
|
|
@@ -162,58 +3325,1311 @@ async function loadConfig({
|
|
|
162
3325
|
];
|
|
163
3326
|
for (const configPath of configPaths) {
|
|
164
3327
|
for (const ext of extensions) {
|
|
165
|
-
const fullPath =
|
|
166
|
-
const
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
return
|
|
3328
|
+
const fullPath = resolve32(baseDir, `${configPath}${ext}`);
|
|
3329
|
+
const config32 = await tryLoadConfig22(fullPath, defaultConfig22);
|
|
3330
|
+
if (config32 !== null) {
|
|
3331
|
+
log2.debug(`Configuration found: ${configPath}${ext}`);
|
|
3332
|
+
return config32;
|
|
170
3333
|
}
|
|
171
3334
|
}
|
|
172
3335
|
}
|
|
173
3336
|
try {
|
|
174
|
-
const pkgPath =
|
|
175
|
-
if (
|
|
3337
|
+
const pkgPath = resolve32(baseDir, "package.json");
|
|
3338
|
+
if (existsSync32(pkgPath)) {
|
|
176
3339
|
const pkg = await import(pkgPath);
|
|
177
3340
|
const pkgConfig = pkg[name];
|
|
178
3341
|
if (pkgConfig && typeof pkgConfig === "object" && !Array.isArray(pkgConfig)) {
|
|
179
3342
|
try {
|
|
180
|
-
|
|
181
|
-
return
|
|
3343
|
+
log2.debug(`Configuration found in package.json!`);
|
|
3344
|
+
return deepMerge22(defaultConfig22, pkgConfig);
|
|
182
3345
|
} catch {}
|
|
183
3346
|
}
|
|
184
3347
|
}
|
|
185
3348
|
} catch {}
|
|
186
|
-
|
|
187
|
-
return
|
|
3349
|
+
log2.debug("No configuration found, now using default config");
|
|
3350
|
+
return defaultConfig22;
|
|
188
3351
|
}
|
|
189
|
-
var
|
|
190
|
-
var
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
3352
|
+
var defaultConfigDir22 = resolve32(process62.cwd(), "config");
|
|
3353
|
+
var defaultGeneratedDir22 = resolve32(process62.cwd(), "src/generated");
|
|
3354
|
+
function getProjectRoot22(filePath, options = {}) {
|
|
3355
|
+
let path = process72.cwd();
|
|
3356
|
+
while (path.includes("storage"))
|
|
3357
|
+
path = resolve4(path, "..");
|
|
3358
|
+
const finalPath = resolve4(path, filePath || "");
|
|
3359
|
+
if (options?.relative)
|
|
3360
|
+
return relative2(process72.cwd(), finalPath);
|
|
3361
|
+
return finalPath;
|
|
3362
|
+
}
|
|
3363
|
+
var defaultLogDirectory22 = process72.env.CLARITY_LOG_DIR || join3(getProjectRoot22(), "logs");
|
|
3364
|
+
var defaultConfig22 = {
|
|
3365
|
+
level: "info",
|
|
3366
|
+
defaultName: "clarity",
|
|
3367
|
+
timestamp: true,
|
|
3368
|
+
colors: true,
|
|
3369
|
+
format: "text",
|
|
3370
|
+
maxLogSize: 10485760,
|
|
3371
|
+
logDatePattern: "YYYY-MM-DD",
|
|
3372
|
+
logDirectory: defaultLogDirectory22,
|
|
3373
|
+
rotation: {
|
|
3374
|
+
frequency: "daily",
|
|
3375
|
+
maxSize: 10485760,
|
|
3376
|
+
maxFiles: 5,
|
|
3377
|
+
compress: false,
|
|
3378
|
+
rotateHour: 0,
|
|
3379
|
+
rotateMinute: 0,
|
|
3380
|
+
rotateDayOfWeek: 0,
|
|
3381
|
+
rotateDayOfMonth: 1,
|
|
3382
|
+
encrypt: false
|
|
198
3383
|
},
|
|
199
|
-
|
|
200
|
-
|
|
3384
|
+
verbose: false
|
|
3385
|
+
};
|
|
3386
|
+
async function loadConfig42() {
|
|
3387
|
+
try {
|
|
3388
|
+
const loadedConfig = await loadConfig32({
|
|
3389
|
+
name: "clarity",
|
|
3390
|
+
defaultConfig: defaultConfig22,
|
|
3391
|
+
cwd: process72.cwd(),
|
|
3392
|
+
endpoint: "",
|
|
3393
|
+
headers: {}
|
|
3394
|
+
});
|
|
3395
|
+
return { ...defaultConfig22, ...loadedConfig };
|
|
3396
|
+
} catch {
|
|
3397
|
+
return defaultConfig22;
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
var config22 = await loadConfig42();
|
|
3401
|
+
function isBrowserProcess22() {
|
|
3402
|
+
if (process92.env.NODE_ENV === "test" || process92.env.BUN_ENV === "test") {
|
|
3403
|
+
return false;
|
|
3404
|
+
}
|
|
3405
|
+
return typeof window !== "undefined";
|
|
3406
|
+
}
|
|
3407
|
+
async function isServerProcess22() {
|
|
3408
|
+
if (process92.env.NODE_ENV === "test" || process92.env.BUN_ENV === "test") {
|
|
3409
|
+
return true;
|
|
3410
|
+
}
|
|
3411
|
+
if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
|
|
3412
|
+
return true;
|
|
3413
|
+
}
|
|
3414
|
+
if (typeof process92 !== "undefined") {
|
|
3415
|
+
const type = process92.type;
|
|
3416
|
+
if (type === "renderer" || type === "worker") {
|
|
3417
|
+
return false;
|
|
3418
|
+
}
|
|
3419
|
+
return !!(process92.versions && (process92.versions.node || process92.versions.bun));
|
|
3420
|
+
}
|
|
3421
|
+
return false;
|
|
3422
|
+
}
|
|
3423
|
+
class JsonFormatter22 {
|
|
3424
|
+
async format(entry) {
|
|
3425
|
+
const isServer = await isServerProcess22();
|
|
3426
|
+
const metadata = await this.getMetadata(isServer);
|
|
3427
|
+
return JSON.stringify({
|
|
3428
|
+
timestamp: entry.timestamp.toISOString(),
|
|
3429
|
+
level: entry.level,
|
|
3430
|
+
name: entry.name,
|
|
3431
|
+
message: entry.message,
|
|
3432
|
+
metadata
|
|
3433
|
+
});
|
|
3434
|
+
}
|
|
3435
|
+
async getMetadata(isServer) {
|
|
3436
|
+
if (isServer) {
|
|
3437
|
+
const { hostname } = await import("os");
|
|
3438
|
+
return {
|
|
3439
|
+
pid: process10.pid,
|
|
3440
|
+
hostname: hostname(),
|
|
3441
|
+
environment: process10.env.NODE_ENV || "development",
|
|
3442
|
+
platform: process10.platform,
|
|
3443
|
+
version: process10.version
|
|
3444
|
+
};
|
|
3445
|
+
}
|
|
3446
|
+
return {
|
|
3447
|
+
userAgent: navigator.userAgent,
|
|
3448
|
+
hostname: window.location.hostname || "browser",
|
|
3449
|
+
environment: process10.env.NODE_ENV || process10.env.BUN_ENV || "development",
|
|
3450
|
+
viewport: {
|
|
3451
|
+
width: window.innerWidth,
|
|
3452
|
+
height: window.innerHeight
|
|
3453
|
+
},
|
|
3454
|
+
language: navigator.language
|
|
3455
|
+
};
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
var terminalStyles22 = {
|
|
3459
|
+
red: (text) => `\x1B[31m${text}\x1B[0m`,
|
|
3460
|
+
green: (text) => `\x1B[32m${text}\x1B[0m`,
|
|
3461
|
+
yellow: (text) => `\x1B[33m${text}\x1B[0m`,
|
|
3462
|
+
blue: (text) => `\x1B[34m${text}\x1B[0m`,
|
|
3463
|
+
magenta: (text) => `\x1B[35m${text}\x1B[0m`,
|
|
3464
|
+
cyan: (text) => `\x1B[36m${text}\x1B[0m`,
|
|
3465
|
+
white: (text) => `\x1B[37m${text}\x1B[0m`,
|
|
3466
|
+
gray: (text) => `\x1B[90m${text}\x1B[0m`,
|
|
3467
|
+
bgRed: (text) => `\x1B[41m${text}\x1B[0m`,
|
|
3468
|
+
bgYellow: (text) => `\x1B[43m${text}\x1B[0m`,
|
|
3469
|
+
bold: (text) => `\x1B[1m${text}\x1B[0m`,
|
|
3470
|
+
dim: (text) => `\x1B[2m${text}\x1B[0m`,
|
|
3471
|
+
italic: (text) => `\x1B[3m${text}\x1B[0m`,
|
|
3472
|
+
underline: (text) => `\x1B[4m${text}\x1B[0m`,
|
|
3473
|
+
reset: "\x1B[0m"
|
|
3474
|
+
};
|
|
3475
|
+
var styles22 = terminalStyles22;
|
|
3476
|
+
var red22 = terminalStyles22.red;
|
|
3477
|
+
var green22 = terminalStyles22.green;
|
|
3478
|
+
var yellow22 = terminalStyles22.yellow;
|
|
3479
|
+
var blue22 = terminalStyles22.blue;
|
|
3480
|
+
var magenta22 = terminalStyles22.magenta;
|
|
3481
|
+
var cyan22 = terminalStyles22.cyan;
|
|
3482
|
+
var white22 = terminalStyles22.white;
|
|
3483
|
+
var gray22 = terminalStyles22.gray;
|
|
3484
|
+
var bgRed22 = terminalStyles22.bgRed;
|
|
3485
|
+
var bgYellow22 = terminalStyles22.bgYellow;
|
|
3486
|
+
var bold22 = terminalStyles22.bold;
|
|
3487
|
+
var dim22 = terminalStyles22.dim;
|
|
3488
|
+
var italic22 = terminalStyles22.italic;
|
|
3489
|
+
var underline22 = terminalStyles22.underline;
|
|
3490
|
+
var reset22 = terminalStyles22.reset;
|
|
3491
|
+
var defaultFingersCrossedConfig22 = {
|
|
3492
|
+
activationLevel: "error",
|
|
3493
|
+
bufferSize: 50,
|
|
3494
|
+
flushOnDeactivation: true,
|
|
3495
|
+
stopBuffering: false
|
|
3496
|
+
};
|
|
3497
|
+
var levelIcons22 = {
|
|
3498
|
+
debug: "\uD83D\uDD0D",
|
|
3499
|
+
info: blue22("ℹ"),
|
|
3500
|
+
success: green22("✓"),
|
|
3501
|
+
warning: bgYellow22(white22(bold22(" WARN "))),
|
|
3502
|
+
error: bgRed22(white22(bold22(" ERROR ")))
|
|
201
3503
|
};
|
|
202
|
-
var git_hooks_config_default = config;
|
|
203
3504
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
3505
|
+
class Logger22 {
|
|
3506
|
+
name;
|
|
3507
|
+
fileLocks = new Map;
|
|
3508
|
+
currentKeyId = null;
|
|
3509
|
+
keys = new Map;
|
|
3510
|
+
config;
|
|
3511
|
+
options;
|
|
3512
|
+
formatter;
|
|
3513
|
+
timers = new Set;
|
|
3514
|
+
subLoggers = new Set;
|
|
3515
|
+
fingersCrossedBuffer = [];
|
|
3516
|
+
fingersCrossedConfig;
|
|
3517
|
+
fingersCrossedActive = false;
|
|
3518
|
+
currentLogFile;
|
|
3519
|
+
rotationTimeout;
|
|
3520
|
+
keyRotationTimeout;
|
|
3521
|
+
encryptionKeys;
|
|
3522
|
+
logBuffer = [];
|
|
3523
|
+
isActivated = false;
|
|
3524
|
+
pendingOperations = [];
|
|
3525
|
+
enabled;
|
|
3526
|
+
fancy;
|
|
3527
|
+
tagFormat;
|
|
3528
|
+
timestampPosition;
|
|
3529
|
+
environment;
|
|
3530
|
+
ANSI_PATTERN = /\u001B\[.*?m/g;
|
|
3531
|
+
activeProgressBar = null;
|
|
3532
|
+
constructor(name, options = {}) {
|
|
3533
|
+
this.name = name;
|
|
3534
|
+
this.config = { ...config22 };
|
|
3535
|
+
this.options = this.normalizeOptions(options);
|
|
3536
|
+
this.formatter = this.options.formatter || new JsonFormatter22;
|
|
3537
|
+
this.enabled = options.enabled ?? true;
|
|
3538
|
+
this.fancy = options.fancy ?? true;
|
|
3539
|
+
this.tagFormat = options.tagFormat ?? { prefix: "[", suffix: "]" };
|
|
3540
|
+
this.timestampPosition = options.timestampPosition ?? "right";
|
|
3541
|
+
this.environment = options.environment ?? process11.env.APP_ENV ?? "local";
|
|
3542
|
+
this.fingersCrossedConfig = this.initializeFingersCrossedConfig(options);
|
|
3543
|
+
const configOptions = { ...options };
|
|
3544
|
+
const hasTimestamp = options.timestamp !== undefined;
|
|
3545
|
+
if (hasTimestamp) {
|
|
3546
|
+
delete configOptions.timestamp;
|
|
3547
|
+
}
|
|
3548
|
+
this.config = {
|
|
3549
|
+
...this.config,
|
|
3550
|
+
...configOptions,
|
|
3551
|
+
timestamp: hasTimestamp || this.config.timestamp
|
|
3552
|
+
};
|
|
3553
|
+
if (!this.config.logDirectory) {
|
|
3554
|
+
this.config.logDirectory = config22.logDirectory;
|
|
3555
|
+
}
|
|
3556
|
+
if (!isBrowserProcess22()) {
|
|
3557
|
+
mkdir22(this.config.logDirectory, { recursive: true, mode: 493 }).catch((err) => console.error("Failed to create log directory:", err));
|
|
3558
|
+
}
|
|
3559
|
+
this.currentLogFile = this.generateLogFilename();
|
|
3560
|
+
this.encryptionKeys = new Map;
|
|
3561
|
+
if (this.validateEncryptionConfig()) {
|
|
3562
|
+
this.setupRotation();
|
|
3563
|
+
const initialKeyId = this.generateKeyId();
|
|
3564
|
+
const initialKey = this.generateKey();
|
|
3565
|
+
this.currentKeyId = initialKeyId;
|
|
3566
|
+
this.keys.set(initialKeyId, initialKey);
|
|
3567
|
+
this.encryptionKeys.set(initialKeyId, {
|
|
3568
|
+
key: initialKey,
|
|
3569
|
+
createdAt: new Date
|
|
3570
|
+
});
|
|
3571
|
+
this.setupKeyRotation();
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3574
|
+
initializeFingersCrossedConfig(options) {
|
|
3575
|
+
if (!options.fingersCrossedEnabled && options.fingersCrossed) {
|
|
3576
|
+
return {
|
|
3577
|
+
...defaultFingersCrossedConfig22,
|
|
3578
|
+
...options.fingersCrossed
|
|
3579
|
+
};
|
|
3580
|
+
}
|
|
3581
|
+
if (!options.fingersCrossedEnabled) {
|
|
3582
|
+
return null;
|
|
3583
|
+
}
|
|
3584
|
+
if (!options.fingersCrossed) {
|
|
3585
|
+
return { ...defaultFingersCrossedConfig22 };
|
|
3586
|
+
}
|
|
3587
|
+
return {
|
|
3588
|
+
...defaultFingersCrossedConfig22,
|
|
3589
|
+
...options.fingersCrossed
|
|
3590
|
+
};
|
|
3591
|
+
}
|
|
3592
|
+
normalizeOptions(options) {
|
|
3593
|
+
const defaultOptions = {
|
|
3594
|
+
format: "json",
|
|
3595
|
+
level: "info",
|
|
3596
|
+
logDirectory: config22.logDirectory,
|
|
3597
|
+
rotation: undefined,
|
|
3598
|
+
timestamp: undefined,
|
|
3599
|
+
fingersCrossed: {},
|
|
3600
|
+
enabled: true,
|
|
3601
|
+
showTags: false,
|
|
3602
|
+
formatter: undefined
|
|
3603
|
+
};
|
|
3604
|
+
const mergedOptions = {
|
|
3605
|
+
...defaultOptions,
|
|
3606
|
+
...Object.fromEntries(Object.entries(options).filter(([, value]) => value !== undefined))
|
|
3607
|
+
};
|
|
3608
|
+
if (!mergedOptions.level || !["debug", "info", "success", "warning", "error"].includes(mergedOptions.level)) {
|
|
3609
|
+
mergedOptions.level = defaultOptions.level;
|
|
3610
|
+
}
|
|
3611
|
+
return mergedOptions;
|
|
3612
|
+
}
|
|
3613
|
+
async writeToFile(data) {
|
|
3614
|
+
const cancelled = false;
|
|
3615
|
+
const operationPromise = (async () => {
|
|
3616
|
+
let fd;
|
|
3617
|
+
let retries = 0;
|
|
3618
|
+
const maxRetries = 3;
|
|
3619
|
+
const backoffDelay = 1000;
|
|
3620
|
+
while (retries < maxRetries) {
|
|
3621
|
+
try {
|
|
3622
|
+
try {
|
|
3623
|
+
try {
|
|
3624
|
+
await access22(this.config.logDirectory, constants22.F_OK | constants22.W_OK);
|
|
3625
|
+
} catch (err) {
|
|
3626
|
+
if (err instanceof Error && "code" in err) {
|
|
3627
|
+
if (err.code === "ENOENT") {
|
|
3628
|
+
await mkdir22(this.config.logDirectory, { recursive: true, mode: 493 });
|
|
3629
|
+
} else if (err.code === "EACCES") {
|
|
3630
|
+
throw new Error(`No write permission for log directory: ${this.config.logDirectory}`);
|
|
3631
|
+
} else {
|
|
3632
|
+
throw err;
|
|
3633
|
+
}
|
|
3634
|
+
} else {
|
|
3635
|
+
throw err;
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
} catch (err) {
|
|
3639
|
+
console.error("Debug: [writeToFile] Failed to create log directory:", err);
|
|
3640
|
+
throw err;
|
|
3641
|
+
}
|
|
3642
|
+
if (cancelled)
|
|
3643
|
+
throw new Error("Operation cancelled: Logger was destroyed");
|
|
3644
|
+
const dataToWrite = this.validateEncryptionConfig() ? (await this.encrypt(data)).encrypted : Buffer22.from(data);
|
|
3645
|
+
try {
|
|
3646
|
+
if (!existsSync42(this.currentLogFile)) {
|
|
3647
|
+
await writeFile22(this.currentLogFile, "", { mode: 420 });
|
|
3648
|
+
}
|
|
3649
|
+
fd = openSync22(this.currentLogFile, "a", 420);
|
|
3650
|
+
writeFileSync42(fd, dataToWrite, { flag: "a" });
|
|
3651
|
+
fsyncSync22(fd);
|
|
3652
|
+
if (fd !== undefined) {
|
|
3653
|
+
closeSync22(fd);
|
|
3654
|
+
fd = undefined;
|
|
3655
|
+
}
|
|
3656
|
+
const stats = await stat22(this.currentLogFile);
|
|
3657
|
+
if (stats.size === 0) {
|
|
3658
|
+
await writeFile22(this.currentLogFile, dataToWrite, { flag: "w", mode: 420 });
|
|
3659
|
+
const retryStats = await stat22(this.currentLogFile);
|
|
3660
|
+
if (retryStats.size === 0) {
|
|
3661
|
+
throw new Error("File exists but is empty after retry write");
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
return;
|
|
3665
|
+
} catch (err) {
|
|
3666
|
+
const error = err;
|
|
3667
|
+
if (error.code && ["ENETDOWN", "ENETUNREACH", "ENOTFOUND", "ETIMEDOUT"].includes(error.code)) {
|
|
3668
|
+
if (retries < maxRetries - 1) {
|
|
3669
|
+
const errorMessage = typeof error.message === "string" ? error.message : "Unknown error";
|
|
3670
|
+
console.error(`Network error during write attempt ${retries + 1}/${maxRetries}:`, errorMessage);
|
|
3671
|
+
const delay = backoffDelay * 2 ** retries;
|
|
3672
|
+
await new Promise((resolve52) => setTimeout(resolve52, delay));
|
|
3673
|
+
retries++;
|
|
3674
|
+
continue;
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
if (error?.code && ["ENOSPC", "EDQUOT"].includes(error.code)) {
|
|
3678
|
+
throw new Error(`Disk quota exceeded or no space left on device: ${error.message}`);
|
|
3679
|
+
}
|
|
3680
|
+
console.error("Debug: [writeToFile] Error writing to file:", error);
|
|
3681
|
+
throw error;
|
|
3682
|
+
} finally {
|
|
3683
|
+
if (fd !== undefined) {
|
|
3684
|
+
try {
|
|
3685
|
+
closeSync22(fd);
|
|
3686
|
+
} catch (err) {
|
|
3687
|
+
console.error("Debug: [writeToFile] Error closing file descriptor:", err);
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
} catch (err) {
|
|
3692
|
+
if (retries === maxRetries - 1) {
|
|
3693
|
+
const error = err;
|
|
3694
|
+
const errorMessage = typeof error.message === "string" ? error.message : "Unknown error";
|
|
3695
|
+
console.error("Debug: [writeToFile] Max retries reached. Final error:", errorMessage);
|
|
3696
|
+
throw err;
|
|
3697
|
+
}
|
|
3698
|
+
retries++;
|
|
3699
|
+
const delay = backoffDelay * 2 ** (retries - 1);
|
|
3700
|
+
await new Promise((resolve52) => setTimeout(resolve52, delay));
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
})();
|
|
3704
|
+
this.pendingOperations.push(operationPromise);
|
|
3705
|
+
const index = this.pendingOperations.length - 1;
|
|
3706
|
+
try {
|
|
3707
|
+
await operationPromise;
|
|
3708
|
+
} catch (err) {
|
|
3709
|
+
console.error("Debug: [writeToFile] Error in operation:", err);
|
|
3710
|
+
throw err;
|
|
3711
|
+
} finally {
|
|
3712
|
+
this.pendingOperations.splice(index, 1);
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
generateLogFilename() {
|
|
3716
|
+
if (this.name.includes("stream-throughput") || this.name.includes("decompress-perf-test") || this.name.includes("decompression-latency") || this.name.includes("concurrent-read-test") || this.name.includes("clock-change-test")) {
|
|
3717
|
+
return join5(this.config.logDirectory, `${this.name}.log`);
|
|
3718
|
+
}
|
|
3719
|
+
if (this.name.includes("pending-test") || this.name.includes("temp-file-test") || this.name === "crash-test" || this.name === "corrupt-test" || this.name.includes("rotation-load-test") || this.name === "sigterm-test" || this.name === "sigint-test" || this.name === "failed-rotation-test" || this.name === "integration-test") {
|
|
3720
|
+
return join5(this.config.logDirectory, `${this.name}.log`);
|
|
3721
|
+
}
|
|
3722
|
+
const date = new Date().toISOString().split("T")[0];
|
|
3723
|
+
return join5(this.config.logDirectory, `${this.name}-${date}.log`);
|
|
3724
|
+
}
|
|
3725
|
+
setupRotation() {
|
|
3726
|
+
if (isBrowserProcess22())
|
|
3727
|
+
return;
|
|
3728
|
+
if (typeof this.config.rotation === "boolean")
|
|
3729
|
+
return;
|
|
3730
|
+
const config32 = this.config.rotation;
|
|
3731
|
+
let interval;
|
|
3732
|
+
switch (config32.frequency) {
|
|
3733
|
+
case "daily":
|
|
3734
|
+
interval = 86400000;
|
|
3735
|
+
break;
|
|
3736
|
+
case "weekly":
|
|
3737
|
+
interval = 604800000;
|
|
3738
|
+
break;
|
|
3739
|
+
case "monthly":
|
|
3740
|
+
interval = 2592000000;
|
|
3741
|
+
break;
|
|
3742
|
+
default:
|
|
3743
|
+
return;
|
|
3744
|
+
}
|
|
3745
|
+
this.rotationTimeout = setInterval(() => {
|
|
3746
|
+
this.rotateLog();
|
|
3747
|
+
}, interval);
|
|
3748
|
+
}
|
|
3749
|
+
setupKeyRotation() {
|
|
3750
|
+
if (!this.validateEncryptionConfig()) {
|
|
3751
|
+
console.error("Invalid encryption configuration detected during key rotation setup");
|
|
3752
|
+
return;
|
|
3753
|
+
}
|
|
3754
|
+
const rotation = this.config.rotation;
|
|
3755
|
+
const keyRotation = rotation.keyRotation;
|
|
3756
|
+
if (!keyRotation?.enabled) {
|
|
3757
|
+
return;
|
|
3758
|
+
}
|
|
3759
|
+
const rotationInterval = typeof keyRotation.interval === "number" ? keyRotation.interval : 60;
|
|
3760
|
+
const interval = Math.max(rotationInterval, 60) * 1000;
|
|
3761
|
+
this.keyRotationTimeout = setInterval(() => {
|
|
3762
|
+
this.rotateKeys().catch((error) => {
|
|
3763
|
+
console.error("Error rotating keys:", error);
|
|
3764
|
+
});
|
|
3765
|
+
}, interval);
|
|
3766
|
+
}
|
|
3767
|
+
async rotateKeys() {
|
|
3768
|
+
if (!this.validateEncryptionConfig()) {
|
|
3769
|
+
console.error("Invalid encryption configuration detected during key rotation");
|
|
3770
|
+
return;
|
|
3771
|
+
}
|
|
3772
|
+
const rotation = this.config.rotation;
|
|
3773
|
+
const keyRotation = rotation.keyRotation;
|
|
3774
|
+
const newKeyId = this.generateKeyId();
|
|
3775
|
+
const newKey = this.generateKey();
|
|
3776
|
+
this.currentKeyId = newKeyId;
|
|
3777
|
+
this.keys.set(newKeyId, newKey);
|
|
3778
|
+
this.encryptionKeys.set(newKeyId, {
|
|
3779
|
+
key: newKey,
|
|
3780
|
+
createdAt: new Date
|
|
3781
|
+
});
|
|
3782
|
+
const sortedKeys = Array.from(this.encryptionKeys.entries()).sort(([, a], [, b]) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
3783
|
+
const maxKeyCount = typeof keyRotation.maxKeys === "number" ? keyRotation.maxKeys : 1;
|
|
3784
|
+
const maxKeys = Math.max(1, maxKeyCount);
|
|
3785
|
+
if (sortedKeys.length > maxKeys) {
|
|
3786
|
+
for (const [keyId] of sortedKeys.slice(maxKeys)) {
|
|
3787
|
+
this.encryptionKeys.delete(keyId);
|
|
3788
|
+
this.keys.delete(keyId);
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
generateKeyId() {
|
|
3793
|
+
return randomBytes22(16).toString("hex");
|
|
3794
|
+
}
|
|
3795
|
+
generateKey() {
|
|
3796
|
+
return randomBytes22(32);
|
|
3797
|
+
}
|
|
3798
|
+
getCurrentKey() {
|
|
3799
|
+
if (!this.currentKeyId) {
|
|
3800
|
+
throw new Error("Encryption is not properly initialized. Make sure encryption is enabled in the configuration.");
|
|
3801
|
+
}
|
|
3802
|
+
const key = this.keys.get(this.currentKeyId);
|
|
3803
|
+
if (!key) {
|
|
3804
|
+
throw new Error(`No key found for ID ${this.currentKeyId}. The encryption key may have been rotated or removed.`);
|
|
3805
|
+
}
|
|
3806
|
+
return { key, id: this.currentKeyId };
|
|
3807
|
+
}
|
|
3808
|
+
encrypt(data) {
|
|
3809
|
+
const { key } = this.getCurrentKey();
|
|
3810
|
+
const iv = randomBytes22(16);
|
|
3811
|
+
const cipher = createCipheriv22("aes-256-gcm", key, iv);
|
|
3812
|
+
const encrypted = Buffer22.concat([
|
|
3813
|
+
cipher.update(data, "utf8"),
|
|
3814
|
+
cipher.final()
|
|
3815
|
+
]);
|
|
3816
|
+
const authTag = cipher.getAuthTag();
|
|
3817
|
+
return {
|
|
3818
|
+
encrypted: Buffer22.concat([iv, encrypted, authTag]),
|
|
3819
|
+
iv
|
|
3820
|
+
};
|
|
3821
|
+
}
|
|
3822
|
+
async compressData(data) {
|
|
3823
|
+
return new Promise((resolve52, reject) => {
|
|
3824
|
+
const gzip = createGzip22();
|
|
3825
|
+
const chunks = [];
|
|
3826
|
+
gzip.on("data", (chunk2) => chunks.push(chunk2));
|
|
3827
|
+
gzip.on("end", () => resolve52(Buffer22.from(Buffer22.concat(chunks))));
|
|
3828
|
+
gzip.on("error", reject);
|
|
3829
|
+
gzip.write(data);
|
|
3830
|
+
gzip.end();
|
|
3831
|
+
});
|
|
3832
|
+
}
|
|
3833
|
+
getEncryptionOptions() {
|
|
3834
|
+
if (!this.config.rotation || typeof this.config.rotation === "boolean" || !this.config.rotation.encrypt) {
|
|
3835
|
+
return {};
|
|
3836
|
+
}
|
|
3837
|
+
const defaultOptions = {
|
|
3838
|
+
algorithm: "aes-256-cbc",
|
|
3839
|
+
compress: false
|
|
3840
|
+
};
|
|
3841
|
+
if (typeof this.config.rotation.encrypt === "object") {
|
|
3842
|
+
const encryptConfig = this.config.rotation.encrypt;
|
|
3843
|
+
return {
|
|
3844
|
+
...defaultOptions,
|
|
3845
|
+
...encryptConfig
|
|
3846
|
+
};
|
|
3847
|
+
}
|
|
3848
|
+
return defaultOptions;
|
|
3849
|
+
}
|
|
3850
|
+
async rotateLog() {
|
|
3851
|
+
if (isBrowserProcess22())
|
|
3852
|
+
return;
|
|
3853
|
+
const stats = await stat22(this.currentLogFile).catch(() => null);
|
|
3854
|
+
if (!stats)
|
|
3855
|
+
return;
|
|
3856
|
+
const config32 = this.config.rotation;
|
|
3857
|
+
if (typeof config32 === "boolean")
|
|
3858
|
+
return;
|
|
3859
|
+
if (config32.maxSize && stats.size >= config32.maxSize) {
|
|
3860
|
+
const oldFile = this.currentLogFile;
|
|
3861
|
+
const newFile = this.generateLogFilename();
|
|
3862
|
+
if (this.name.includes("rotation-load-test") || this.name === "failed-rotation-test") {
|
|
3863
|
+
const files = await readdir22(this.config.logDirectory);
|
|
3864
|
+
const rotatedFiles = files.filter((f) => f.startsWith(this.name) && /\.log\.\d+$/.test(f)).sort((a, b) => {
|
|
3865
|
+
const numA = Number.parseInt(a.match(/\.log\.(\d+)$/)?.[1] || "0");
|
|
3866
|
+
const numB = Number.parseInt(b.match(/\.log\.(\d+)$/)?.[1] || "0");
|
|
3867
|
+
return numB - numA;
|
|
3868
|
+
});
|
|
3869
|
+
const nextNum = rotatedFiles.length > 0 ? Number.parseInt(rotatedFiles[0].match(/\.log\.(\d+)$/)?.[1] || "0") + 1 : 1;
|
|
3870
|
+
const rotatedFile = `${oldFile}.${nextNum}`;
|
|
3871
|
+
if (await stat22(oldFile).catch(() => null)) {
|
|
3872
|
+
try {
|
|
3873
|
+
await rename22(oldFile, rotatedFile);
|
|
3874
|
+
if (config32.compress) {
|
|
3875
|
+
try {
|
|
3876
|
+
const compressedPath = `${rotatedFile}.gz`;
|
|
3877
|
+
await this.compressLogFile(rotatedFile, compressedPath);
|
|
3878
|
+
await unlink22(rotatedFile);
|
|
3879
|
+
} catch (err) {
|
|
3880
|
+
console.error("Error compressing rotated file:", err);
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
if (rotatedFiles.length === 0 && !files.some((f) => f.endsWith(".log.1"))) {
|
|
3884
|
+
try {
|
|
3885
|
+
const backupPath = `${oldFile}.1`;
|
|
3886
|
+
await writeFile22(backupPath, "");
|
|
3887
|
+
} catch (err) {
|
|
3888
|
+
console.error("Error creating backup file:", err);
|
|
3889
|
+
}
|
|
3890
|
+
}
|
|
3891
|
+
} catch (err) {
|
|
3892
|
+
console.error(`Error during rotation: ${err instanceof Error ? err.message : String(err)}`);
|
|
3893
|
+
}
|
|
3894
|
+
}
|
|
3895
|
+
} else {
|
|
3896
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
3897
|
+
const rotatedFile = oldFile.replace(/\.log$/, `-${timestamp}.log`);
|
|
3898
|
+
if (await stat22(oldFile).catch(() => null)) {
|
|
3899
|
+
await rename22(oldFile, rotatedFile);
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
this.currentLogFile = newFile;
|
|
3903
|
+
if (config32.maxFiles) {
|
|
3904
|
+
const files = await readdir22(this.config.logDirectory);
|
|
3905
|
+
const logFiles = files.filter((f) => f.startsWith(this.name)).sort((a, b) => b.localeCompare(a));
|
|
3906
|
+
for (const file of logFiles.slice(config32.maxFiles)) {
|
|
3907
|
+
await unlink22(join5(this.config.logDirectory, file));
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3911
|
+
}
|
|
3912
|
+
async compressLogFile(inputPath, outputPath) {
|
|
3913
|
+
const readStream = createReadStream22(inputPath);
|
|
3914
|
+
const writeStream = createWriteStream22(outputPath);
|
|
3915
|
+
const gzip = createGzip22();
|
|
3916
|
+
await pipeline22(readStream, gzip, writeStream);
|
|
3917
|
+
}
|
|
3918
|
+
async handleFingersCrossedBuffer(level, formattedEntry) {
|
|
3919
|
+
if (!this.fingersCrossedConfig)
|
|
3920
|
+
return;
|
|
3921
|
+
if (this.shouldActivateFingersCrossed(level) && !this.isActivated) {
|
|
3922
|
+
this.isActivated = true;
|
|
3923
|
+
for (const entry of this.logBuffer) {
|
|
3924
|
+
const formattedBufferedEntry = await this.formatter.format(entry);
|
|
3925
|
+
await this.writeToFile(formattedBufferedEntry);
|
|
3926
|
+
console.log(formattedBufferedEntry);
|
|
3927
|
+
}
|
|
3928
|
+
if (this.fingersCrossedConfig.stopBuffering)
|
|
3929
|
+
this.logBuffer = [];
|
|
3930
|
+
}
|
|
3931
|
+
if (this.isActivated) {
|
|
3932
|
+
await this.writeToFile(formattedEntry);
|
|
3933
|
+
console.log(formattedEntry);
|
|
3934
|
+
} else {
|
|
3935
|
+
if (this.logBuffer.length >= this.fingersCrossedConfig.bufferSize)
|
|
3936
|
+
this.logBuffer.shift();
|
|
3937
|
+
const entry = {
|
|
3938
|
+
timestamp: new Date,
|
|
3939
|
+
level,
|
|
3940
|
+
message: formattedEntry,
|
|
3941
|
+
name: this.name
|
|
3942
|
+
};
|
|
3943
|
+
this.logBuffer.push(entry);
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
shouldActivateFingersCrossed(level) {
|
|
3947
|
+
if (!this.fingersCrossedConfig)
|
|
3948
|
+
return false;
|
|
3949
|
+
return this.getLevelValue(level) >= this.getLevelValue(this.fingersCrossedConfig.activationLevel);
|
|
3950
|
+
}
|
|
3951
|
+
getLevelValue(level) {
|
|
3952
|
+
const levels = {
|
|
3953
|
+
debug: 0,
|
|
3954
|
+
info: 1,
|
|
3955
|
+
success: 2,
|
|
3956
|
+
warning: 3,
|
|
3957
|
+
error: 4
|
|
3958
|
+
};
|
|
3959
|
+
return levels[level];
|
|
3960
|
+
}
|
|
3961
|
+
shouldLog(level) {
|
|
3962
|
+
if (!this.enabled)
|
|
3963
|
+
return false;
|
|
3964
|
+
const levels = {
|
|
3965
|
+
debug: 0,
|
|
3966
|
+
info: 1,
|
|
3967
|
+
success: 2,
|
|
3968
|
+
warning: 3,
|
|
3969
|
+
error: 4
|
|
3970
|
+
};
|
|
3971
|
+
return levels[level] >= levels[this.config.level];
|
|
3972
|
+
}
|
|
3973
|
+
async flushPendingWrites() {
|
|
3974
|
+
await Promise.all(this.pendingOperations.map((op) => {
|
|
3975
|
+
if (op instanceof Promise) {
|
|
3976
|
+
return op.catch((err) => {
|
|
3977
|
+
console.error("Error in pending write operation:", err);
|
|
3978
|
+
});
|
|
3979
|
+
}
|
|
3980
|
+
return Promise.resolve();
|
|
3981
|
+
}));
|
|
3982
|
+
if (existsSync42(this.currentLogFile)) {
|
|
3983
|
+
try {
|
|
3984
|
+
const fd = openSync22(this.currentLogFile, "r+");
|
|
3985
|
+
fsyncSync22(fd);
|
|
3986
|
+
closeSync22(fd);
|
|
3987
|
+
} catch (error) {
|
|
3988
|
+
console.error(`Error flushing file: ${error}`);
|
|
3989
|
+
}
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
async destroy() {
|
|
3993
|
+
if (this.rotationTimeout)
|
|
3994
|
+
clearInterval(this.rotationTimeout);
|
|
3995
|
+
if (this.keyRotationTimeout)
|
|
3996
|
+
clearInterval(this.keyRotationTimeout);
|
|
3997
|
+
this.timers.clear();
|
|
3998
|
+
for (const op of this.pendingOperations) {
|
|
3999
|
+
if (typeof op.cancel === "function") {
|
|
4000
|
+
op.cancel();
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
return (async () => {
|
|
4004
|
+
if (this.pendingOperations.length > 0) {
|
|
4005
|
+
try {
|
|
4006
|
+
await Promise.allSettled(this.pendingOperations);
|
|
4007
|
+
} catch (err) {
|
|
4008
|
+
console.error("Error waiting for pending operations:", err);
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
if (!isBrowserProcess22() && this.config.rotation && typeof this.config.rotation !== "boolean" && this.config.rotation.compress) {
|
|
4012
|
+
try {
|
|
4013
|
+
const files = await readdir22(this.config.logDirectory);
|
|
4014
|
+
const tempFiles = files.filter((f) => (f.includes("temp") || f.includes(".tmp")) && f.includes(this.name));
|
|
4015
|
+
for (const tempFile of tempFiles) {
|
|
4016
|
+
try {
|
|
4017
|
+
await unlink22(join5(this.config.logDirectory, tempFile));
|
|
4018
|
+
} catch (err) {
|
|
4019
|
+
console.error(`Failed to delete temp file ${tempFile}:`, err);
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
4022
|
+
} catch (err) {
|
|
4023
|
+
console.error("Error cleaning up temporary files:", err);
|
|
4024
|
+
}
|
|
4025
|
+
}
|
|
4026
|
+
})();
|
|
4027
|
+
}
|
|
4028
|
+
getCurrentLogFilePath() {
|
|
4029
|
+
return this.currentLogFile;
|
|
4030
|
+
}
|
|
4031
|
+
formatTag(name) {
|
|
4032
|
+
if (!name)
|
|
4033
|
+
return "";
|
|
4034
|
+
return `${this.tagFormat.prefix}${name}${this.tagFormat.suffix}`;
|
|
4035
|
+
}
|
|
4036
|
+
formatFileTimestamp(date) {
|
|
4037
|
+
return `[${date.toISOString()}]`;
|
|
4038
|
+
}
|
|
4039
|
+
formatConsoleTimestamp(date) {
|
|
4040
|
+
return this.fancy ? styles22.gray(date.toLocaleTimeString()) : date.toLocaleTimeString();
|
|
4041
|
+
}
|
|
4042
|
+
formatConsoleMessage(parts) {
|
|
4043
|
+
const { timestamp, icon = "", tag = "", message, level, showTimestamp = true } = parts;
|
|
4044
|
+
const stripAnsi = (str) => str.replace(this.ANSI_PATTERN, "");
|
|
4045
|
+
if (!this.fancy) {
|
|
4046
|
+
const components = [];
|
|
4047
|
+
if (showTimestamp)
|
|
4048
|
+
components.push(timestamp);
|
|
4049
|
+
if (level === "warning")
|
|
4050
|
+
components.push("WARN");
|
|
4051
|
+
else if (level === "error")
|
|
4052
|
+
components.push("ERROR");
|
|
4053
|
+
else if (icon)
|
|
4054
|
+
components.push(icon.replace(/[^\p{L}\p{N}\p{P}\p{Z}]/gu, ""));
|
|
4055
|
+
if (tag)
|
|
4056
|
+
components.push(tag.replace(/[[\]]/g, ""));
|
|
4057
|
+
components.push(message);
|
|
4058
|
+
return components.join(" ");
|
|
4059
|
+
}
|
|
4060
|
+
const terminalWidth = process11.stdout.columns || 120;
|
|
4061
|
+
let mainPart = "";
|
|
4062
|
+
if (level === "warning" || level === "error") {
|
|
4063
|
+
mainPart = `${icon} ${message}`;
|
|
4064
|
+
} else if (level === "info" || level === "success") {
|
|
4065
|
+
mainPart = `${icon} ${tag} ${message}`;
|
|
4066
|
+
} else {
|
|
4067
|
+
mainPart = `${icon} ${tag} ${styles22.cyan(message)}`;
|
|
4068
|
+
}
|
|
4069
|
+
if (!showTimestamp) {
|
|
4070
|
+
return mainPart.trim();
|
|
4071
|
+
}
|
|
4072
|
+
const visibleMainPartLength = stripAnsi(mainPart).trim().length;
|
|
4073
|
+
const visibleTimestampLength = stripAnsi(timestamp).length;
|
|
4074
|
+
const padding = Math.max(1, terminalWidth - 2 - visibleMainPartLength - visibleTimestampLength);
|
|
4075
|
+
return `${mainPart.trim()}${" ".repeat(padding)}${timestamp}`;
|
|
4076
|
+
}
|
|
4077
|
+
formatMessage(message, args) {
|
|
4078
|
+
if (args.length === 1 && Array.isArray(args[0])) {
|
|
4079
|
+
return message.replace(/\{(\d+)\}/g, (match, index) => {
|
|
4080
|
+
const position = Number.parseInt(index, 10);
|
|
4081
|
+
return position < args[0].length ? String(args[0][position]) : match;
|
|
4082
|
+
});
|
|
4083
|
+
}
|
|
4084
|
+
const formatRegex = /%([sdijfo%])/g;
|
|
4085
|
+
let argIndex = 0;
|
|
4086
|
+
let formattedMessage = message.replace(formatRegex, (match, type) => {
|
|
4087
|
+
if (type === "%")
|
|
4088
|
+
return "%";
|
|
4089
|
+
if (argIndex >= args.length)
|
|
4090
|
+
return match;
|
|
4091
|
+
const arg = args[argIndex++];
|
|
4092
|
+
switch (type) {
|
|
4093
|
+
case "s":
|
|
4094
|
+
return String(arg);
|
|
4095
|
+
case "d":
|
|
4096
|
+
case "i":
|
|
4097
|
+
return Number(arg).toString();
|
|
4098
|
+
case "j":
|
|
4099
|
+
case "o":
|
|
4100
|
+
return JSON.stringify(arg, null, 2);
|
|
4101
|
+
default:
|
|
4102
|
+
return match;
|
|
4103
|
+
}
|
|
4104
|
+
});
|
|
4105
|
+
if (argIndex < args.length) {
|
|
4106
|
+
formattedMessage += ` ${args.slice(argIndex).map((arg) => typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg)).join(" ")}`;
|
|
4107
|
+
}
|
|
4108
|
+
return formattedMessage;
|
|
4109
|
+
}
|
|
4110
|
+
async log(level, message, ...args) {
|
|
4111
|
+
const timestamp = new Date;
|
|
4112
|
+
const consoleTime = this.formatConsoleTimestamp(timestamp);
|
|
4113
|
+
const fileTime = this.formatFileTimestamp(timestamp);
|
|
4114
|
+
let formattedMessage;
|
|
4115
|
+
let errorStack;
|
|
4116
|
+
if (message instanceof Error) {
|
|
4117
|
+
formattedMessage = message.message;
|
|
4118
|
+
errorStack = message.stack;
|
|
4119
|
+
} else {
|
|
4120
|
+
formattedMessage = this.formatMessage(message, args);
|
|
4121
|
+
}
|
|
4122
|
+
if (this.fancy && !isBrowserProcess22()) {
|
|
4123
|
+
const icon = levelIcons22[level];
|
|
4124
|
+
const tag = this.options.showTags !== false && this.name ? styles22.gray(this.formatTag(this.name)) : "";
|
|
4125
|
+
let consoleMessage;
|
|
4126
|
+
switch (level) {
|
|
4127
|
+
case "debug":
|
|
4128
|
+
consoleMessage = this.formatConsoleMessage({
|
|
4129
|
+
timestamp: consoleTime,
|
|
4130
|
+
icon,
|
|
4131
|
+
tag,
|
|
4132
|
+
message: styles22.gray(formattedMessage),
|
|
4133
|
+
level
|
|
4134
|
+
});
|
|
4135
|
+
console.error(consoleMessage);
|
|
4136
|
+
break;
|
|
4137
|
+
case "info":
|
|
4138
|
+
consoleMessage = this.formatConsoleMessage({
|
|
4139
|
+
timestamp: consoleTime,
|
|
4140
|
+
icon,
|
|
4141
|
+
tag,
|
|
4142
|
+
message: formattedMessage,
|
|
4143
|
+
level
|
|
4144
|
+
});
|
|
4145
|
+
console.error(consoleMessage);
|
|
4146
|
+
break;
|
|
4147
|
+
case "success":
|
|
4148
|
+
consoleMessage = this.formatConsoleMessage({
|
|
4149
|
+
timestamp: consoleTime,
|
|
4150
|
+
icon,
|
|
4151
|
+
tag,
|
|
4152
|
+
message: styles22.green(formattedMessage),
|
|
4153
|
+
level
|
|
4154
|
+
});
|
|
4155
|
+
console.error(consoleMessage);
|
|
4156
|
+
break;
|
|
4157
|
+
case "warning":
|
|
4158
|
+
consoleMessage = this.formatConsoleMessage({
|
|
4159
|
+
timestamp: consoleTime,
|
|
4160
|
+
icon,
|
|
4161
|
+
tag,
|
|
4162
|
+
message: formattedMessage,
|
|
4163
|
+
level
|
|
4164
|
+
});
|
|
4165
|
+
console.warn(consoleMessage);
|
|
4166
|
+
break;
|
|
4167
|
+
case "error":
|
|
4168
|
+
consoleMessage = this.formatConsoleMessage({
|
|
4169
|
+
timestamp: consoleTime,
|
|
4170
|
+
icon,
|
|
4171
|
+
tag,
|
|
4172
|
+
message: formattedMessage,
|
|
4173
|
+
level
|
|
4174
|
+
});
|
|
4175
|
+
console.error(consoleMessage);
|
|
4176
|
+
if (errorStack) {
|
|
4177
|
+
const stackLines = errorStack.split(`
|
|
4178
|
+
`);
|
|
4179
|
+
for (const line of stackLines) {
|
|
4180
|
+
if (line.trim() && !line.includes(formattedMessage)) {
|
|
4181
|
+
console.error(this.formatConsoleMessage({
|
|
4182
|
+
timestamp: consoleTime,
|
|
4183
|
+
message: styles22.gray(` ${line}`),
|
|
4184
|
+
level,
|
|
4185
|
+
showTimestamp: false
|
|
4186
|
+
}));
|
|
4187
|
+
}
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4190
|
+
break;
|
|
4191
|
+
}
|
|
4192
|
+
} else if (!isBrowserProcess22()) {
|
|
4193
|
+
console.error(`${fileTime} ${this.environment}.${level.toUpperCase()}: ${formattedMessage}`);
|
|
4194
|
+
if (errorStack) {
|
|
4195
|
+
console.error(errorStack);
|
|
4196
|
+
}
|
|
4197
|
+
}
|
|
4198
|
+
if (!this.shouldLog(level))
|
|
4199
|
+
return;
|
|
4200
|
+
let logEntry = `${fileTime} ${this.environment}.${level.toUpperCase()}: ${formattedMessage}
|
|
4201
|
+
`;
|
|
4202
|
+
if (errorStack) {
|
|
4203
|
+
logEntry += `${errorStack}
|
|
4204
|
+
`;
|
|
4205
|
+
}
|
|
4206
|
+
logEntry = logEntry.replace(this.ANSI_PATTERN, "");
|
|
4207
|
+
await this.writeToFile(logEntry);
|
|
4208
|
+
}
|
|
4209
|
+
time(label) {
|
|
4210
|
+
const start = performance.now();
|
|
4211
|
+
if (this.fancy && !isBrowserProcess22()) {
|
|
4212
|
+
const tag = this.options.showTags !== false && this.name ? styles22.gray(this.formatTag(this.name)) : "";
|
|
4213
|
+
const consoleTime = this.formatConsoleTimestamp(new Date);
|
|
4214
|
+
console.error(this.formatConsoleMessage({
|
|
4215
|
+
timestamp: consoleTime,
|
|
4216
|
+
icon: styles22.blue("◐"),
|
|
4217
|
+
tag,
|
|
4218
|
+
message: `${styles22.cyan(label)}...`
|
|
4219
|
+
}));
|
|
4220
|
+
}
|
|
4221
|
+
return async (metadata) => {
|
|
4222
|
+
if (!this.enabled)
|
|
4223
|
+
return;
|
|
4224
|
+
const end = performance.now();
|
|
4225
|
+
const elapsed = Math.round(end - start);
|
|
4226
|
+
const completionMessage = `${label} completed in ${elapsed}ms`;
|
|
4227
|
+
const timestamp = new Date;
|
|
4228
|
+
const consoleTime = this.formatConsoleTimestamp(timestamp);
|
|
4229
|
+
const fileTime = this.formatFileTimestamp(timestamp);
|
|
4230
|
+
let logEntry = `${fileTime} ${this.environment}.INFO: ${completionMessage}`;
|
|
4231
|
+
if (metadata) {
|
|
4232
|
+
logEntry += ` ${JSON.stringify(metadata)}`;
|
|
4233
|
+
}
|
|
4234
|
+
logEntry += `
|
|
4235
|
+
`;
|
|
4236
|
+
logEntry = logEntry.replace(this.ANSI_PATTERN, "");
|
|
4237
|
+
if (this.fancy && !isBrowserProcess22()) {
|
|
4238
|
+
const tag = this.options.showTags !== false && this.name ? styles22.gray(this.formatTag(this.name)) : "";
|
|
4239
|
+
console.error(this.formatConsoleMessage({
|
|
4240
|
+
timestamp: consoleTime,
|
|
4241
|
+
icon: styles22.green("✓"),
|
|
4242
|
+
tag,
|
|
4243
|
+
message: `${completionMessage}${metadata ? ` ${JSON.stringify(metadata)}` : ""}`
|
|
4244
|
+
}));
|
|
4245
|
+
} else if (!isBrowserProcess22()) {
|
|
4246
|
+
console.error(logEntry.trim());
|
|
4247
|
+
}
|
|
4248
|
+
await this.writeToFile(logEntry);
|
|
4249
|
+
};
|
|
4250
|
+
}
|
|
4251
|
+
async debug(message, ...args) {
|
|
4252
|
+
await this.log("debug", message, ...args);
|
|
4253
|
+
}
|
|
4254
|
+
async info(message, ...args) {
|
|
4255
|
+
await this.log("info", message, ...args);
|
|
4256
|
+
}
|
|
4257
|
+
async success(message, ...args) {
|
|
4258
|
+
await this.log("success", message, ...args);
|
|
4259
|
+
}
|
|
4260
|
+
async warn(message, ...args) {
|
|
4261
|
+
await this.log("warning", message, ...args);
|
|
4262
|
+
}
|
|
4263
|
+
async error(message, ...args) {
|
|
4264
|
+
await this.log("error", message, ...args);
|
|
4265
|
+
}
|
|
4266
|
+
validateEncryptionConfig() {
|
|
4267
|
+
if (!this.config.rotation)
|
|
4268
|
+
return false;
|
|
4269
|
+
if (typeof this.config.rotation === "boolean")
|
|
4270
|
+
return false;
|
|
4271
|
+
const rotation = this.config.rotation;
|
|
4272
|
+
const { encrypt } = rotation;
|
|
4273
|
+
return !!encrypt;
|
|
4274
|
+
}
|
|
4275
|
+
async only(fn) {
|
|
4276
|
+
if (!this.enabled)
|
|
4277
|
+
return;
|
|
4278
|
+
return await fn();
|
|
4279
|
+
}
|
|
4280
|
+
isEnabled() {
|
|
4281
|
+
return this.enabled;
|
|
4282
|
+
}
|
|
4283
|
+
setEnabled(enabled) {
|
|
4284
|
+
this.enabled = enabled;
|
|
4285
|
+
}
|
|
4286
|
+
extend(namespace) {
|
|
4287
|
+
const childName = `${this.name}:${namespace}`;
|
|
4288
|
+
const childLogger = new Logger22(childName, {
|
|
4289
|
+
...this.options,
|
|
4290
|
+
logDirectory: this.config.logDirectory,
|
|
4291
|
+
level: this.config.level,
|
|
4292
|
+
format: this.config.format,
|
|
4293
|
+
rotation: typeof this.config.rotation === "boolean" ? undefined : this.config.rotation,
|
|
4294
|
+
timestamp: typeof this.config.timestamp === "boolean" ? undefined : this.config.timestamp
|
|
4295
|
+
});
|
|
4296
|
+
this.subLoggers.add(childLogger);
|
|
4297
|
+
return childLogger;
|
|
4298
|
+
}
|
|
4299
|
+
createReadStream() {
|
|
4300
|
+
if (isBrowserProcess22())
|
|
4301
|
+
throw new Error("createReadStream is not supported in browser environments");
|
|
4302
|
+
if (!existsSync42(this.currentLogFile))
|
|
4303
|
+
throw new Error(`Log file does not exist: ${this.currentLogFile}`);
|
|
4304
|
+
return createReadStream22(this.currentLogFile, { encoding: "utf8" });
|
|
4305
|
+
}
|
|
4306
|
+
async decrypt(data) {
|
|
4307
|
+
if (!this.validateEncryptionConfig())
|
|
4308
|
+
throw new Error("Encryption is not configured");
|
|
4309
|
+
const encryptionConfig = this.config.rotation;
|
|
4310
|
+
if (!encryptionConfig.encrypt || typeof encryptionConfig.encrypt === "boolean")
|
|
4311
|
+
throw new Error("Invalid encryption configuration");
|
|
4312
|
+
if (!this.currentKeyId || !this.keys.has(this.currentKeyId))
|
|
4313
|
+
throw new Error("No valid encryption key available");
|
|
4314
|
+
const key = this.keys.get(this.currentKeyId);
|
|
4315
|
+
try {
|
|
4316
|
+
const encryptedData = Buffer22.isBuffer(data) ? data : Buffer22.from(data, "base64");
|
|
4317
|
+
const iv = encryptedData.slice(0, 16);
|
|
4318
|
+
const authTag = encryptedData.slice(-16);
|
|
4319
|
+
const ciphertext = encryptedData.slice(16, -16);
|
|
4320
|
+
const decipher = createDecipheriv22("aes-256-gcm", key, iv);
|
|
4321
|
+
decipher.setAuthTag(authTag);
|
|
4322
|
+
const decrypted = Buffer22.concat([
|
|
4323
|
+
decipher.update(ciphertext),
|
|
4324
|
+
decipher.final()
|
|
4325
|
+
]);
|
|
4326
|
+
return decrypted.toString("utf8");
|
|
4327
|
+
} catch (err) {
|
|
4328
|
+
throw new Error(`Decryption failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
getLevel() {
|
|
4332
|
+
return this.config.level;
|
|
4333
|
+
}
|
|
4334
|
+
getLogDirectory() {
|
|
4335
|
+
return this.config.logDirectory;
|
|
4336
|
+
}
|
|
4337
|
+
getFormat() {
|
|
4338
|
+
return this.config.format;
|
|
4339
|
+
}
|
|
4340
|
+
getRotationConfig() {
|
|
4341
|
+
return this.config.rotation;
|
|
4342
|
+
}
|
|
4343
|
+
isBrowserMode() {
|
|
4344
|
+
return isBrowserProcess22();
|
|
4345
|
+
}
|
|
4346
|
+
isServerMode() {
|
|
4347
|
+
return !isBrowserProcess22();
|
|
4348
|
+
}
|
|
4349
|
+
setTestEncryptionKey(keyId, key) {
|
|
4350
|
+
this.currentKeyId = keyId;
|
|
4351
|
+
this.keys.set(keyId, key);
|
|
4352
|
+
}
|
|
4353
|
+
getTestCurrentKey() {
|
|
4354
|
+
if (!this.currentKeyId || !this.keys.has(this.currentKeyId)) {
|
|
4355
|
+
return null;
|
|
4356
|
+
}
|
|
4357
|
+
return {
|
|
4358
|
+
id: this.currentKeyId,
|
|
4359
|
+
key: this.keys.get(this.currentKeyId)
|
|
4360
|
+
};
|
|
4361
|
+
}
|
|
4362
|
+
getConfig() {
|
|
4363
|
+
return this.config;
|
|
4364
|
+
}
|
|
4365
|
+
async box(message) {
|
|
4366
|
+
if (!this.enabled)
|
|
4367
|
+
return;
|
|
4368
|
+
const timestamp = new Date;
|
|
4369
|
+
const consoleTime = this.formatConsoleTimestamp(timestamp);
|
|
4370
|
+
const fileTime = this.formatFileTimestamp(timestamp);
|
|
4371
|
+
if (this.fancy && !isBrowserProcess22()) {
|
|
4372
|
+
const lines = message.split(`
|
|
4373
|
+
`);
|
|
4374
|
+
const width = Math.max(...lines.map((line) => line.length)) + 2;
|
|
4375
|
+
const top = `┌${"─".repeat(width)}┐`;
|
|
4376
|
+
const bottom = `└${"─".repeat(width)}┘`;
|
|
4377
|
+
const boxedLines = lines.map((line) => {
|
|
4378
|
+
const padding = " ".repeat(width - line.length - 2);
|
|
4379
|
+
return `│ ${line}${padding} │`;
|
|
4380
|
+
});
|
|
4381
|
+
if (this.options.showTags !== false && this.name) {
|
|
4382
|
+
console.error(this.formatConsoleMessage({
|
|
4383
|
+
timestamp: consoleTime,
|
|
4384
|
+
message: styles22.gray(this.formatTag(this.name)),
|
|
4385
|
+
showTimestamp: false
|
|
4386
|
+
}));
|
|
4387
|
+
}
|
|
4388
|
+
console.error(this.formatConsoleMessage({
|
|
4389
|
+
timestamp: consoleTime,
|
|
4390
|
+
message: styles22.cyan(top)
|
|
4391
|
+
}));
|
|
4392
|
+
boxedLines.forEach((line) => console.error(this.formatConsoleMessage({
|
|
4393
|
+
timestamp: consoleTime,
|
|
4394
|
+
message: styles22.cyan(line),
|
|
4395
|
+
showTimestamp: false
|
|
4396
|
+
})));
|
|
4397
|
+
console.error(this.formatConsoleMessage({
|
|
4398
|
+
timestamp: consoleTime,
|
|
4399
|
+
message: styles22.cyan(bottom),
|
|
4400
|
+
showTimestamp: false
|
|
4401
|
+
}));
|
|
4402
|
+
} else if (!isBrowserProcess22()) {
|
|
4403
|
+
console.error(`${fileTime} ${this.environment}.INFO: [BOX] ${message}`);
|
|
4404
|
+
}
|
|
4405
|
+
const logEntry = `${fileTime} ${this.environment}.INFO: [BOX] ${message}
|
|
4406
|
+
`.replace(this.ANSI_PATTERN, "");
|
|
4407
|
+
await this.writeToFile(logEntry);
|
|
4408
|
+
}
|
|
4409
|
+
async prompt(message) {
|
|
4410
|
+
if (isBrowserProcess22()) {
|
|
4411
|
+
return Promise.resolve(true);
|
|
4412
|
+
}
|
|
4413
|
+
return new Promise((resolve52) => {
|
|
4414
|
+
console.error(`${styles22.cyan("?")} ${message} (y/n) `);
|
|
4415
|
+
const onData = (data) => {
|
|
4416
|
+
const input = data.toString().trim().toLowerCase();
|
|
4417
|
+
process11.stdin.removeListener("data", onData);
|
|
4418
|
+
try {
|
|
4419
|
+
if (typeof process11.stdin.setRawMode === "function") {
|
|
4420
|
+
process11.stdin.setRawMode(false);
|
|
4421
|
+
}
|
|
4422
|
+
} catch {}
|
|
4423
|
+
process11.stdin.pause();
|
|
4424
|
+
console.error("");
|
|
4425
|
+
resolve52(input === "y" || input === "yes");
|
|
4426
|
+
};
|
|
4427
|
+
try {
|
|
4428
|
+
if (typeof process11.stdin.setRawMode === "function") {
|
|
4429
|
+
process11.stdin.setRawMode(true);
|
|
4430
|
+
}
|
|
4431
|
+
} catch {}
|
|
4432
|
+
process11.stdin.resume();
|
|
4433
|
+
process11.stdin.once("data", onData);
|
|
4434
|
+
});
|
|
4435
|
+
}
|
|
4436
|
+
setFancy(enabled) {
|
|
4437
|
+
this.fancy = enabled;
|
|
4438
|
+
}
|
|
4439
|
+
isFancy() {
|
|
4440
|
+
return this.fancy;
|
|
4441
|
+
}
|
|
4442
|
+
pause() {
|
|
4443
|
+
this.enabled = false;
|
|
4444
|
+
}
|
|
4445
|
+
resume() {
|
|
4446
|
+
this.enabled = true;
|
|
4447
|
+
}
|
|
4448
|
+
async start(message, ...args) {
|
|
4449
|
+
if (!this.enabled)
|
|
4450
|
+
return;
|
|
4451
|
+
let formattedMessage = message;
|
|
4452
|
+
if (args && args.length > 0) {
|
|
4453
|
+
const formatRegex = /%([sdijfo%])/g;
|
|
4454
|
+
let argIndex = 0;
|
|
4455
|
+
formattedMessage = message.replace(formatRegex, (match, type) => {
|
|
4456
|
+
if (type === "%")
|
|
4457
|
+
return "%";
|
|
4458
|
+
if (argIndex >= args.length)
|
|
4459
|
+
return match;
|
|
4460
|
+
const arg = args[argIndex++];
|
|
4461
|
+
switch (type) {
|
|
4462
|
+
case "s":
|
|
4463
|
+
return String(arg);
|
|
4464
|
+
case "d":
|
|
4465
|
+
case "i":
|
|
4466
|
+
return Number(arg).toString();
|
|
4467
|
+
case "j":
|
|
4468
|
+
case "o":
|
|
4469
|
+
return JSON.stringify(arg, null, 2);
|
|
4470
|
+
default:
|
|
4471
|
+
return match;
|
|
4472
|
+
}
|
|
4473
|
+
});
|
|
4474
|
+
if (argIndex < args.length) {
|
|
4475
|
+
formattedMessage += ` ${args.slice(argIndex).map((arg) => typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg)).join(" ")}`;
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4478
|
+
if (this.fancy && !isBrowserProcess22()) {
|
|
4479
|
+
const tag = this.options.showTags !== false && this.name ? styles22.gray(this.formatTag(this.name)) : "";
|
|
4480
|
+
const spinnerChar = styles22.blue("◐");
|
|
4481
|
+
console.error(`${spinnerChar} ${tag} ${styles22.cyan(formattedMessage)}`);
|
|
4482
|
+
}
|
|
4483
|
+
const timestamp = new Date;
|
|
4484
|
+
const formattedDate = timestamp.toISOString();
|
|
4485
|
+
const logEntry = `[${formattedDate}] ${this.environment}.INFO: [START] ${formattedMessage}
|
|
4486
|
+
`.replace(this.ANSI_PATTERN, "");
|
|
4487
|
+
await this.writeToFile(logEntry);
|
|
4488
|
+
}
|
|
4489
|
+
progress(total, initialMessage = "") {
|
|
4490
|
+
if (!this.enabled || !this.fancy || isBrowserProcess22() || total <= 0) {
|
|
4491
|
+
return {
|
|
4492
|
+
update: () => {},
|
|
4493
|
+
finish: () => {},
|
|
4494
|
+
interrupt: () => {}
|
|
4495
|
+
};
|
|
4496
|
+
}
|
|
4497
|
+
if (this.activeProgressBar) {
|
|
4498
|
+
console.warn("Warning: Another progress bar is already active. Finishing the previous one.");
|
|
4499
|
+
this.finishProgressBar(this.activeProgressBar, "[Auto-finished]");
|
|
4500
|
+
}
|
|
4501
|
+
const barLength = 20;
|
|
4502
|
+
this.activeProgressBar = {
|
|
4503
|
+
total,
|
|
4504
|
+
current: 0,
|
|
4505
|
+
message: initialMessage,
|
|
4506
|
+
barLength,
|
|
4507
|
+
lastRenderedLine: ""
|
|
4508
|
+
};
|
|
4509
|
+
this.renderProgressBar(this.activeProgressBar);
|
|
4510
|
+
const update = (current, message) => {
|
|
4511
|
+
if (!this.activeProgressBar || !this.enabled || !this.fancy || isBrowserProcess22())
|
|
4512
|
+
return;
|
|
4513
|
+
this.activeProgressBar.current = Math.max(0, Math.min(total, current));
|
|
4514
|
+
if (message !== undefined) {
|
|
4515
|
+
this.activeProgressBar.message = message;
|
|
4516
|
+
}
|
|
4517
|
+
const isFinished = this.activeProgressBar.current === this.activeProgressBar.total;
|
|
4518
|
+
this.renderProgressBar(this.activeProgressBar, isFinished);
|
|
4519
|
+
};
|
|
4520
|
+
const finish = (message) => {
|
|
4521
|
+
if (!this.activeProgressBar || !this.enabled || !this.fancy || isBrowserProcess22())
|
|
4522
|
+
return;
|
|
4523
|
+
this.activeProgressBar.current = this.activeProgressBar.total;
|
|
4524
|
+
if (message !== undefined) {
|
|
4525
|
+
this.activeProgressBar.message = message;
|
|
4526
|
+
}
|
|
4527
|
+
this.renderProgressBar(this.activeProgressBar, true);
|
|
4528
|
+
this.finishProgressBar(this.activeProgressBar);
|
|
4529
|
+
};
|
|
4530
|
+
const interrupt = (interruptMessage, level = "info") => {
|
|
4531
|
+
if (!this.activeProgressBar || !this.enabled || !this.fancy || isBrowserProcess22())
|
|
4532
|
+
return;
|
|
4533
|
+
process11.stdout.write(`${"\r".padEnd(process11.stdout.columns || 80)}\r`);
|
|
4534
|
+
this.log(level, interruptMessage);
|
|
4535
|
+
setTimeout(() => {
|
|
4536
|
+
if (this.activeProgressBar) {
|
|
4537
|
+
this.renderProgressBar(this.activeProgressBar);
|
|
4538
|
+
}
|
|
4539
|
+
}, 50);
|
|
4540
|
+
};
|
|
4541
|
+
return { update, finish, interrupt };
|
|
4542
|
+
}
|
|
4543
|
+
renderProgressBar(barState, isFinished = false) {
|
|
4544
|
+
if (!this.enabled || !this.fancy || isBrowserProcess22() || !process11.stdout.isTTY)
|
|
4545
|
+
return;
|
|
4546
|
+
const percent = Math.min(100, Math.max(0, Math.round(barState.current / barState.total * 100)));
|
|
4547
|
+
const filledLength = Math.round(barState.barLength * percent / 100);
|
|
4548
|
+
const emptyLength = barState.barLength - filledLength;
|
|
4549
|
+
const filledBar = styles22.green("━".repeat(filledLength));
|
|
4550
|
+
const emptyBar = styles22.gray("━".repeat(emptyLength));
|
|
4551
|
+
const bar = `[${filledBar}${emptyBar}]`;
|
|
4552
|
+
const percentageText = `${percent}%`.padStart(4);
|
|
4553
|
+
const messageText = barState.message ? ` ${barState.message}` : "";
|
|
4554
|
+
const icon = isFinished || percent === 100 ? styles22.green("✓") : styles22.blue("▶");
|
|
4555
|
+
const tag = this.options.showTags !== false && this.name ? ` ${styles22.gray(this.formatTag(this.name))}` : "";
|
|
4556
|
+
const line = `\r${icon}${tag} ${bar} ${percentageText}${messageText}`;
|
|
4557
|
+
const terminalWidth = process11.stdout.columns || 80;
|
|
4558
|
+
const clearLine = " ".repeat(Math.max(0, terminalWidth - line.replace(this.ANSI_PATTERN, "").length));
|
|
4559
|
+
barState.lastRenderedLine = `${line}${clearLine}`;
|
|
4560
|
+
process11.stdout.write(barState.lastRenderedLine);
|
|
4561
|
+
if (isFinished) {
|
|
4562
|
+
process11.stdout.write(`
|
|
4563
|
+
`);
|
|
4564
|
+
}
|
|
4565
|
+
}
|
|
4566
|
+
finishProgressBar(barState, finalMessage) {
|
|
4567
|
+
if (!this.enabled || !this.fancy || isBrowserProcess22() || !process11.stdout.isTTY) {
|
|
4568
|
+
this.activeProgressBar = null;
|
|
4569
|
+
return;
|
|
4570
|
+
}
|
|
4571
|
+
if (barState.current < barState.total) {
|
|
4572
|
+
barState.current = barState.total;
|
|
4573
|
+
}
|
|
4574
|
+
if (finalMessage)
|
|
4575
|
+
barState.message = finalMessage;
|
|
4576
|
+
this.renderProgressBar(barState, true);
|
|
4577
|
+
this.activeProgressBar = null;
|
|
4578
|
+
}
|
|
4579
|
+
async clear(filters = {}) {
|
|
4580
|
+
if (isBrowserProcess22()) {
|
|
4581
|
+
console.warn("Log clearing is not supported in browser environments.");
|
|
4582
|
+
return;
|
|
4583
|
+
}
|
|
4584
|
+
try {
|
|
4585
|
+
console.warn("Clearing logs...", this.config.logDirectory);
|
|
4586
|
+
const files = await readdir22(this.config.logDirectory);
|
|
4587
|
+
const logFilesToDelete = [];
|
|
4588
|
+
for (const file of files) {
|
|
4589
|
+
const nameMatches = filters.name ? new RegExp(filters.name.replace("*", ".*")).test(file) : file.startsWith(this.name);
|
|
4590
|
+
if (!nameMatches || !file.endsWith(".log")) {
|
|
4591
|
+
continue;
|
|
4592
|
+
}
|
|
4593
|
+
const filePath = join5(this.config.logDirectory, file);
|
|
4594
|
+
if (filters.before) {
|
|
4595
|
+
try {
|
|
4596
|
+
const fileStats = await stat22(filePath);
|
|
4597
|
+
if (fileStats.mtime >= filters.before) {
|
|
4598
|
+
continue;
|
|
4599
|
+
}
|
|
4600
|
+
} catch (statErr) {
|
|
4601
|
+
console.error(`Failed to get stats for file ${filePath}:`, statErr);
|
|
4602
|
+
continue;
|
|
4603
|
+
}
|
|
4604
|
+
}
|
|
4605
|
+
logFilesToDelete.push(filePath);
|
|
4606
|
+
}
|
|
4607
|
+
if (logFilesToDelete.length === 0) {
|
|
4608
|
+
console.warn("No log files matched the criteria for clearing.");
|
|
4609
|
+
return;
|
|
4610
|
+
}
|
|
4611
|
+
console.warn(`Preparing to delete ${logFilesToDelete.length} log file(s)...`);
|
|
4612
|
+
for (const filePath of logFilesToDelete) {
|
|
4613
|
+
try {
|
|
4614
|
+
await unlink22(filePath);
|
|
4615
|
+
console.warn(`Deleted log file: ${filePath}`);
|
|
4616
|
+
} catch (unlinkErr) {
|
|
4617
|
+
console.error(`Failed to delete log file ${filePath}:`, unlinkErr);
|
|
4618
|
+
}
|
|
4619
|
+
}
|
|
4620
|
+
console.warn("Log clearing process finished.");
|
|
4621
|
+
} catch (err) {
|
|
4622
|
+
console.error("Error during log clearing process:", err);
|
|
4623
|
+
}
|
|
4624
|
+
}
|
|
4625
|
+
}
|
|
4626
|
+
var logger22 = new Logger22("stacks");
|
|
4627
|
+
|
|
210
4628
|
// src/git-hooks.ts
|
|
211
|
-
import fs from "node:fs";
|
|
212
|
-
import path from "node:path";
|
|
213
|
-
import process3 from "node:process";
|
|
214
|
-
import { exec } from "node:child_process";
|
|
215
|
-
import { promisify } from "node:util";
|
|
216
4629
|
var execAsync = promisify(exec);
|
|
4630
|
+
var log3 = new Logger22("git-hooks", {
|
|
4631
|
+
showTags: true
|
|
4632
|
+
});
|
|
217
4633
|
var VALID_GIT_HOOKS = [
|
|
218
4634
|
"applypatch-msg",
|
|
219
4635
|
"pre-applypatch",
|
|
@@ -244,7 +4660,7 @@ var VALID_GIT_HOOKS = [
|
|
|
244
4660
|
"p4-pre-submit",
|
|
245
4661
|
"post-index-change"
|
|
246
4662
|
];
|
|
247
|
-
var VALID_OPTIONS = ["preserveUnused"];
|
|
4663
|
+
var VALID_OPTIONS = ["preserveUnused", "verbose", "staged-lint"];
|
|
248
4664
|
var PREPEND_SCRIPT = `#!/bin/sh
|
|
249
4665
|
|
|
250
4666
|
if [ "$SKIP_BUN_GIT_HOOKS" = "1" ]; then
|
|
@@ -257,7 +4673,7 @@ if [ -f "$BUN_GIT_HOOKS_RC" ]; then
|
|
|
257
4673
|
fi
|
|
258
4674
|
|
|
259
4675
|
`;
|
|
260
|
-
function getGitProjectRoot(directory =
|
|
4676
|
+
function getGitProjectRoot(directory = process12.cwd()) {
|
|
261
4677
|
if (directory.endsWith(".git")) {
|
|
262
4678
|
return path.normalize(directory);
|
|
263
4679
|
}
|
|
@@ -319,7 +4735,7 @@ function checkBunGitHooksInDependencies(projectRootPath) {
|
|
|
319
4735
|
}
|
|
320
4736
|
return "bun-git-hooks" in packageJsonContent.devDependencies;
|
|
321
4737
|
}
|
|
322
|
-
function _getPackageJson(projectPath =
|
|
4738
|
+
function _getPackageJson(projectPath = process12.cwd()) {
|
|
323
4739
|
if (typeof projectPath !== "string") {
|
|
324
4740
|
throw new TypeError("projectPath is not a string");
|
|
325
4741
|
}
|
|
@@ -330,15 +4746,17 @@ function _getPackageJson(projectPath = process3.cwd()) {
|
|
|
330
4746
|
const packageJsonDataRaw = fs.readFileSync(targetPackageJson, { encoding: "utf-8" });
|
|
331
4747
|
return { packageJsonContent: JSON.parse(packageJsonDataRaw), packageJsonPath: targetPackageJson };
|
|
332
4748
|
}
|
|
333
|
-
function setHooksFromConfig(projectRootPath =
|
|
334
|
-
if (!
|
|
4749
|
+
function setHooksFromConfig(projectRootPath = process12.cwd(), options) {
|
|
4750
|
+
if (!config3 || Object.keys(config3).length === 0)
|
|
335
4751
|
throw new Error("[ERROR] Config was not found! Please add `.git-hooks.config.{ts,js,mjs,cjs,mts,cts,json}` or `git-hooks.config.{ts,js,mjs,cjs,mts,cts,json}` or the `git-hooks` entry in package.json.\r\nCheck README for details");
|
|
336
|
-
const configFile = options?.configFile ? options.configFile :
|
|
337
|
-
const hookKeys = Object.keys(configFile).filter((key) => key
|
|
4752
|
+
const configFile = options?.configFile ? options.configFile : config3;
|
|
4753
|
+
const hookKeys = Object.keys(configFile).filter((key) => !VALID_OPTIONS.includes(key));
|
|
338
4754
|
const isValidConfig = hookKeys.every((key) => VALID_GIT_HOOKS.includes(key));
|
|
339
4755
|
if (!isValidConfig)
|
|
340
4756
|
throw new Error("[ERROR] Config was not in correct format. Please check git hooks or options name");
|
|
341
4757
|
const preserveUnused = Array.isArray(configFile.preserveUnused) ? configFile.preserveUnused : configFile.preserveUnused ? VALID_GIT_HOOKS : [];
|
|
4758
|
+
const logKeys = Object.keys(configFile).filter((key) => !VALID_OPTIONS.includes(key)).sort().map((key) => italic22(key)).join(", ");
|
|
4759
|
+
log3.debug(`Hook Keys: ${logKeys}`);
|
|
342
4760
|
for (const hook of VALID_GIT_HOOKS) {
|
|
343
4761
|
if (Object.prototype.hasOwnProperty.call(configFile, hook)) {
|
|
344
4762
|
if (!configFile[hook])
|
|
@@ -349,7 +4767,7 @@ function setHooksFromConfig(projectRootPath = process3.cwd(), options) {
|
|
|
349
4767
|
}
|
|
350
4768
|
}
|
|
351
4769
|
}
|
|
352
|
-
async function getStagedFiles(projectRoot =
|
|
4770
|
+
async function getStagedFiles(projectRoot = process12.cwd()) {
|
|
353
4771
|
try {
|
|
354
4772
|
const { stdout } = await execAsync("git diff --staged --name-only --diff-filter=ACMR", { cwd: projectRoot });
|
|
355
4773
|
return stdout.trim().split(`
|
|
@@ -370,7 +4788,7 @@ function matchesGlob(file, pattern) {
|
|
|
370
4788
|
function filterFilesByPattern(files, pattern) {
|
|
371
4789
|
return files.filter((file) => matchesGlob(file, pattern));
|
|
372
4790
|
}
|
|
373
|
-
async function runCommandOnStagedFiles(command, files, projectRoot =
|
|
4791
|
+
async function runCommandOnStagedFiles(command, files, projectRoot = process12.cwd(), verbose = false) {
|
|
374
4792
|
if (files.length === 0) {
|
|
375
4793
|
if (verbose)
|
|
376
4794
|
console.info("[INFO] No matching files for pattern");
|
|
@@ -414,7 +4832,7 @@ async function processStagedLint(stagedLintConfig, projectRoot, verbose = false)
|
|
|
414
4832
|
}
|
|
415
4833
|
return success;
|
|
416
4834
|
}
|
|
417
|
-
function _setHook(hook, commandOrConfig, projectRoot =
|
|
4835
|
+
function _setHook(hook, commandOrConfig, projectRoot = process12.cwd()) {
|
|
418
4836
|
const gitRoot = getGitProjectRoot(projectRoot);
|
|
419
4837
|
if (!gitRoot) {
|
|
420
4838
|
console.info("[INFO] No `.git` root folder found, skipping");
|
|
@@ -423,7 +4841,7 @@ function _setHook(hook, commandOrConfig, projectRoot = process3.cwd()) {
|
|
|
423
4841
|
let hookCommand;
|
|
424
4842
|
if (typeof commandOrConfig === "string") {
|
|
425
4843
|
hookCommand = PREPEND_SCRIPT + commandOrConfig;
|
|
426
|
-
} else if (commandOrConfig.stagedLint) {
|
|
4844
|
+
} else if (commandOrConfig.stagedLint || commandOrConfig["staged-lint"]) {
|
|
427
4845
|
hookCommand = PREPEND_SCRIPT + `git-hooks run-staged-lint ${hook}`;
|
|
428
4846
|
} else {
|
|
429
4847
|
console.error(`[ERROR] Invalid command or config for hook ${hook}`);
|
|
@@ -434,38 +4852,45 @@ function _setHook(hook, commandOrConfig, projectRoot = process3.cwd()) {
|
|
|
434
4852
|
if (!fs.existsSync(hookDirectory)) {
|
|
435
4853
|
fs.mkdirSync(hookDirectory, { recursive: true });
|
|
436
4854
|
}
|
|
4855
|
+
const addOrModify = fs.existsSync(hookPath) ? "Modify" : "Add";
|
|
4856
|
+
log3.debug(`${addOrModify} ${italic22(hook)} hook`);
|
|
437
4857
|
fs.writeFileSync(hookPath, hookCommand, { mode: 493 });
|
|
438
4858
|
}
|
|
439
|
-
function removeHooks(projectRoot =
|
|
4859
|
+
function removeHooks(projectRoot = process12.cwd(), verbose = false) {
|
|
440
4860
|
for (const configEntry of VALID_GIT_HOOKS)
|
|
441
4861
|
_removeHook(configEntry, projectRoot, verbose);
|
|
442
4862
|
}
|
|
443
|
-
function _removeHook(hook, projectRoot =
|
|
4863
|
+
function _removeHook(hook, projectRoot = process12.cwd(), verbose = false) {
|
|
444
4864
|
const gitRoot = getGitProjectRoot(projectRoot);
|
|
445
4865
|
const hookPath = path.normalize(`${gitRoot}/hooks/${hook}`);
|
|
446
|
-
if (fs.existsSync(hookPath))
|
|
4866
|
+
if (fs.existsSync(hookPath)) {
|
|
4867
|
+
log3.debug(`Hook ${hook} is not set, removing!`);
|
|
447
4868
|
fs.unlinkSync(hookPath);
|
|
4869
|
+
}
|
|
448
4870
|
if (verbose)
|
|
449
|
-
|
|
4871
|
+
log3.success(`Successfully removed the ${hook} hook`);
|
|
450
4872
|
}
|
|
451
4873
|
async function runStagedLint(hook) {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
return false;
|
|
457
|
-
}
|
|
458
|
-
const stagedLintConfig = hookConfig.stagedLint;
|
|
459
|
-
if (!stagedLintConfig) {
|
|
460
|
-
console.error(`[ERROR] Invalid stagedLint configuration for hook ${hook}`);
|
|
461
|
-
return false;
|
|
462
|
-
}
|
|
463
|
-
const verbose = config2.verbose === true;
|
|
464
|
-
return await processStagedLint(stagedLintConfig, process3.cwd(), verbose);
|
|
465
|
-
} catch (error) {
|
|
466
|
-
console.error("[ERROR] Failed to run staged lint:", error);
|
|
4874
|
+
const projectRoot = process12.cwd();
|
|
4875
|
+
const configFile = config3;
|
|
4876
|
+
if (!configFile) {
|
|
4877
|
+
console.error(`[ERROR] No configuration found`);
|
|
467
4878
|
return false;
|
|
468
4879
|
}
|
|
4880
|
+
if (hook in configFile) {
|
|
4881
|
+
const hookConfig = configFile[hook];
|
|
4882
|
+
if (typeof hookConfig === "object" && !Array.isArray(hookConfig)) {
|
|
4883
|
+
const stagedLintConfig = hookConfig.stagedLint || hookConfig["staged-lint"];
|
|
4884
|
+
if (stagedLintConfig) {
|
|
4885
|
+
return processStagedLint(stagedLintConfig, projectRoot, configFile.verbose);
|
|
4886
|
+
}
|
|
4887
|
+
}
|
|
4888
|
+
}
|
|
4889
|
+
if (configFile["staged-lint"]) {
|
|
4890
|
+
return processStagedLint(configFile["staged-lint"], projectRoot, configFile.verbose);
|
|
4891
|
+
}
|
|
4892
|
+
console.error(`[ERROR] No staged lint configuration found for hook ${hook}`);
|
|
4893
|
+
return false;
|
|
469
4894
|
}
|
|
470
4895
|
export {
|
|
471
4896
|
setHooksFromConfig,
|
|
@@ -473,7 +4898,7 @@ export {
|
|
|
473
4898
|
removeHooks,
|
|
474
4899
|
getProjectRootDirectoryFromNodeModules,
|
|
475
4900
|
getGitProjectRoot,
|
|
476
|
-
|
|
4901
|
+
config3 as config,
|
|
477
4902
|
checkBunGitHooksInDependencies,
|
|
478
4903
|
VALID_OPTIONS,
|
|
479
4904
|
VALID_GIT_HOOKS,
|