bun-git-hooks 0.2.19 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/cli.js +4576 -2464
- package/dist/config.d.ts +1 -2
- package/dist/git-hooks.d.ts +35 -59
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2281 -274
- package/dist/staged-lint.d.ts +59 -0
- package/dist/types.d.ts +25 -12
- package/package.json +30 -15
- package/scripts/postinstall.ts +60 -0
- package/scripts/uninstall.ts +20 -0
package/dist/index.js
CHANGED
|
@@ -1583,6 +1583,39 @@ function deepMerge2(target, source) {
|
|
|
1583
1583
|
}
|
|
1584
1584
|
return merged;
|
|
1585
1585
|
}
|
|
1586
|
+
function deepMergeWithArrayStrategy(target, source, strategy = "replace") {
|
|
1587
|
+
if (source === null || source === undefined)
|
|
1588
|
+
return target;
|
|
1589
|
+
if (Array.isArray(source)) {
|
|
1590
|
+
return strategy === "replace" ? source : deepMerge2(target, source);
|
|
1591
|
+
}
|
|
1592
|
+
if (Array.isArray(target)) {
|
|
1593
|
+
return strategy === "replace" ? source : deepMerge2(target, source);
|
|
1594
|
+
}
|
|
1595
|
+
if (!isObject2(source) || !isObject2(target))
|
|
1596
|
+
return source;
|
|
1597
|
+
const result = { ...target };
|
|
1598
|
+
for (const key of Object.keys(source)) {
|
|
1599
|
+
if (!Object.prototype.hasOwnProperty.call(source, key))
|
|
1600
|
+
continue;
|
|
1601
|
+
const sourceValue = source[key];
|
|
1602
|
+
const targetValue = result[key];
|
|
1603
|
+
if (sourceValue === null || sourceValue === undefined)
|
|
1604
|
+
continue;
|
|
1605
|
+
if (Array.isArray(sourceValue) || Array.isArray(targetValue)) {
|
|
1606
|
+
if (strategy === "replace") {
|
|
1607
|
+
result[key] = sourceValue;
|
|
1608
|
+
} else {
|
|
1609
|
+
result[key] = deepMerge2(targetValue, sourceValue);
|
|
1610
|
+
}
|
|
1611
|
+
} else if (isObject2(sourceValue) && isObject2(targetValue)) {
|
|
1612
|
+
result[key] = deepMergeWithArrayStrategy(targetValue, sourceValue, strategy);
|
|
1613
|
+
} else {
|
|
1614
|
+
result[key] = sourceValue;
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
return result;
|
|
1618
|
+
}
|
|
1586
1619
|
function deepEquals2(a, b) {
|
|
1587
1620
|
if (a === b)
|
|
1588
1621
|
return true;
|
|
@@ -1616,7 +1649,7 @@ function isObject2(item) {
|
|
|
1616
1649
|
var log = new Logger("bunfig", {
|
|
1617
1650
|
showTags: true
|
|
1618
1651
|
});
|
|
1619
|
-
async function tryLoadConfig2(configPath, defaultConfig2) {
|
|
1652
|
+
async function tryLoadConfig2(configPath, defaultConfig2, arrayStrategy = "replace") {
|
|
1620
1653
|
if (!existsSync3(configPath))
|
|
1621
1654
|
return null;
|
|
1622
1655
|
try {
|
|
@@ -1625,7 +1658,7 @@ async function tryLoadConfig2(configPath, defaultConfig2) {
|
|
|
1625
1658
|
if (typeof loadedConfig !== "object" || loadedConfig === null || Array.isArray(loadedConfig))
|
|
1626
1659
|
return null;
|
|
1627
1660
|
try {
|
|
1628
|
-
return
|
|
1661
|
+
return deepMergeWithArrayStrategy(defaultConfig2, loadedConfig, arrayStrategy);
|
|
1629
1662
|
} catch {
|
|
1630
1663
|
return null;
|
|
1631
1664
|
}
|
|
@@ -1684,9 +1717,11 @@ async function loadConfig3({
|
|
|
1684
1717
|
name = "",
|
|
1685
1718
|
alias,
|
|
1686
1719
|
cwd,
|
|
1720
|
+
configDir,
|
|
1687
1721
|
defaultConfig: defaultConfig2,
|
|
1688
1722
|
verbose = false,
|
|
1689
|
-
checkEnv = true
|
|
1723
|
+
checkEnv = true,
|
|
1724
|
+
arrayStrategy = "replace"
|
|
1690
1725
|
}) {
|
|
1691
1726
|
const configWithEnvVars = checkEnv && typeof defaultConfig2 === "object" && defaultConfig2 !== null && !Array.isArray(defaultConfig2) ? applyEnvVarsToConfig(name, defaultConfig2, verbose) : defaultConfig2;
|
|
1692
1727
|
const baseDir = cwd || process6.cwd();
|
|
@@ -1694,26 +1729,31 @@ async function loadConfig3({
|
|
|
1694
1729
|
if (verbose) {
|
|
1695
1730
|
log.info(`Loading configuration for "${name}"${alias ? ` (alias: "${alias}")` : ""} from ${baseDir}`);
|
|
1696
1731
|
}
|
|
1697
|
-
const
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1732
|
+
const primaryBarePatterns = [name, `.${name}`].filter(Boolean);
|
|
1733
|
+
const primaryConfigSuffixPatterns = [`${name}.config`, `.${name}.config`].filter(Boolean);
|
|
1734
|
+
const aliasBarePatterns = alias ? [alias, `.${alias}`] : [];
|
|
1735
|
+
const aliasConfigSuffixPatterns = alias ? [`${alias}.config`, `.${alias}.config`] : [];
|
|
1736
|
+
const searchDirectories = Array.from(new Set([
|
|
1737
|
+
baseDir,
|
|
1738
|
+
resolve3(baseDir, "config"),
|
|
1739
|
+
resolve3(baseDir, ".config"),
|
|
1740
|
+
configDir ? resolve3(baseDir, configDir) : undefined
|
|
1741
|
+
].filter(Boolean)));
|
|
1742
|
+
for (const dir of searchDirectories) {
|
|
1743
|
+
if (verbose)
|
|
1744
|
+
log.info(`Searching for configuration in: ${dir}`);
|
|
1745
|
+
const isConfigLikeDir = [resolve3(baseDir, "config"), resolve3(baseDir, ".config")].concat(configDir ? [resolve3(baseDir, configDir)] : []).includes(dir);
|
|
1746
|
+
const patternsForDir = isConfigLikeDir ? [...primaryBarePatterns, ...primaryConfigSuffixPatterns, ...aliasBarePatterns, ...aliasConfigSuffixPatterns] : [...primaryConfigSuffixPatterns, ...primaryBarePatterns, ...aliasConfigSuffixPatterns, ...aliasBarePatterns];
|
|
1747
|
+
for (const configPath of patternsForDir) {
|
|
1748
|
+
for (const ext of extensions) {
|
|
1749
|
+
const fullPath = resolve3(dir, `${configPath}${ext}`);
|
|
1750
|
+
const config3 = await tryLoadConfig2(fullPath, configWithEnvVars, arrayStrategy);
|
|
1751
|
+
if (config3 !== null) {
|
|
1752
|
+
if (verbose) {
|
|
1753
|
+
log.success(`Configuration loaded from: ${fullPath}`);
|
|
1754
|
+
}
|
|
1755
|
+
return config3;
|
|
1715
1756
|
}
|
|
1716
|
-
return config3;
|
|
1717
1757
|
}
|
|
1718
1758
|
}
|
|
1719
1759
|
}
|
|
@@ -1729,7 +1769,7 @@ async function loadConfig3({
|
|
|
1729
1769
|
for (const configPath of homeConfigPatterns) {
|
|
1730
1770
|
for (const ext of extensions) {
|
|
1731
1771
|
const fullPath = resolve3(homeConfigDir, `${configPath}${ext}`);
|
|
1732
|
-
const config3 = await tryLoadConfig2(fullPath, configWithEnvVars);
|
|
1772
|
+
const config3 = await tryLoadConfig2(fullPath, configWithEnvVars, arrayStrategy);
|
|
1733
1773
|
if (config3 !== null) {
|
|
1734
1774
|
if (verbose) {
|
|
1735
1775
|
log.success(`Configuration loaded from user config directory: ${fullPath}`);
|
|
@@ -1739,6 +1779,46 @@ async function loadConfig3({
|
|
|
1739
1779
|
}
|
|
1740
1780
|
}
|
|
1741
1781
|
}
|
|
1782
|
+
if (name) {
|
|
1783
|
+
const homeConfigDir = resolve3(homedir(), ".config");
|
|
1784
|
+
const homeConfigDotfilePatterns = [`.${name}.config`];
|
|
1785
|
+
if (alias)
|
|
1786
|
+
homeConfigDotfilePatterns.push(`.${alias}.config`);
|
|
1787
|
+
if (verbose)
|
|
1788
|
+
log.info(`Checking user config directory for dotfile configs: ${homeConfigDir}`);
|
|
1789
|
+
for (const configPath of homeConfigDotfilePatterns) {
|
|
1790
|
+
for (const ext of extensions) {
|
|
1791
|
+
const fullPath = resolve3(homeConfigDir, `${configPath}${ext}`);
|
|
1792
|
+
const config3 = await tryLoadConfig2(fullPath, configWithEnvVars, arrayStrategy);
|
|
1793
|
+
if (config3 !== null) {
|
|
1794
|
+
if (verbose)
|
|
1795
|
+
log.success(`Configuration loaded from user config directory dotfile: ${fullPath}`);
|
|
1796
|
+
return config3;
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
if (name) {
|
|
1802
|
+
const homeDir = homedir();
|
|
1803
|
+
const homeRootPatterns = [`.${name}.config`, `.${name}`];
|
|
1804
|
+
if (alias) {
|
|
1805
|
+
homeRootPatterns.push(`.${alias}.config`);
|
|
1806
|
+
homeRootPatterns.push(`.${alias}`);
|
|
1807
|
+
}
|
|
1808
|
+
if (verbose)
|
|
1809
|
+
log.info(`Checking user home directory for dotfile configs: ${homeDir}`);
|
|
1810
|
+
for (const configPath of homeRootPatterns) {
|
|
1811
|
+
for (const ext of extensions) {
|
|
1812
|
+
const fullPath = resolve3(homeDir, `${configPath}${ext}`);
|
|
1813
|
+
const config3 = await tryLoadConfig2(fullPath, configWithEnvVars, arrayStrategy);
|
|
1814
|
+
if (config3 !== null) {
|
|
1815
|
+
if (verbose)
|
|
1816
|
+
log.success(`Configuration loaded from user home directory: ${fullPath}`);
|
|
1817
|
+
return config3;
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1742
1822
|
try {
|
|
1743
1823
|
const pkgPath = resolve3(baseDir, "package.json");
|
|
1744
1824
|
if (existsSync3(pkgPath)) {
|
|
@@ -1755,7 +1835,7 @@ async function loadConfig3({
|
|
|
1755
1835
|
if (verbose) {
|
|
1756
1836
|
log.success(`Configuration loaded from package.json: ${pkgConfig === pkg[name] ? name : alias}`);
|
|
1757
1837
|
}
|
|
1758
|
-
return
|
|
1838
|
+
return deepMergeWithArrayStrategy(configWithEnvVars, pkgConfig, arrayStrategy);
|
|
1759
1839
|
} catch (error) {
|
|
1760
1840
|
if (verbose) {
|
|
1761
1841
|
log.warn(`Failed to merge package.json config:`, error);
|
|
@@ -1781,7 +1861,7 @@ var config2 = {
|
|
|
1781
1861
|
"pre-commit": {
|
|
1782
1862
|
"staged-lint": {
|
|
1783
1863
|
"**/*.{js,ts}": [
|
|
1784
|
-
"bunx --bun eslint --
|
|
1864
|
+
"bunx --bun eslint --fix",
|
|
1785
1865
|
"bunx --bun tsc --noEmit"
|
|
1786
1866
|
]
|
|
1787
1867
|
}
|
|
@@ -1800,15 +1880,21 @@ var config3 = await loadConfig3({
|
|
|
1800
1880
|
// src/git-hooks.ts
|
|
1801
1881
|
import fs from "node:fs";
|
|
1802
1882
|
import path from "node:path";
|
|
1803
|
-
import
|
|
1883
|
+
import process12 from "node:process";
|
|
1804
1884
|
import { exec } from "node:child_process";
|
|
1805
1885
|
import { promisify } from "node:util";
|
|
1806
1886
|
|
|
1807
|
-
// node_modules/@stacksjs/clarity/dist/index.js
|
|
1808
|
-
import { join as join3, relative as relative2, resolve as
|
|
1887
|
+
// node_modules/@stacksjs/clarity/dist/src/index.js
|
|
1888
|
+
import { join as join3, relative as relative2, resolve as resolve4 } from "path";
|
|
1889
|
+
import process72 from "process";
|
|
1890
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync22, readdirSync as readdirSync22, writeFileSync as writeFileSync32 } from "fs";
|
|
1891
|
+
import { homedir as homedir2 } from "os";
|
|
1892
|
+
import { dirname as dirname22, resolve as resolve32 } from "path";
|
|
1893
|
+
import process62 from "process";
|
|
1894
|
+
import { join as join4, relative as relative3, resolve as resolve22 } from "path";
|
|
1809
1895
|
import process22 from "process";
|
|
1810
1896
|
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
1811
|
-
import { dirname as dirname3, resolve as
|
|
1897
|
+
import { dirname as dirname3, resolve as resolve5 } from "path";
|
|
1812
1898
|
import process9 from "process";
|
|
1813
1899
|
import { Buffer as Buffer2 } from "buffer";
|
|
1814
1900
|
import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes2 } from "crypto";
|
|
@@ -1820,6 +1906,16 @@ import { pipeline as pipeline2 } from "stream/promises";
|
|
|
1820
1906
|
import { createGzip as createGzip2 } from "zlib";
|
|
1821
1907
|
import process42 from "process";
|
|
1822
1908
|
import process32 from "process";
|
|
1909
|
+
import { Buffer as Buffer22 } from "buffer";
|
|
1910
|
+
import { createCipheriv as createCipheriv22, createDecipheriv as createDecipheriv22, randomBytes as randomBytes22 } from "crypto";
|
|
1911
|
+
import { closeSync as closeSync22, createReadStream as createReadStream22, createWriteStream as createWriteStream22, existsSync as existsSync42, fsyncSync as fsyncSync22, openSync as openSync22, writeFileSync as writeFileSync42 } from "fs";
|
|
1912
|
+
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";
|
|
1913
|
+
import { isAbsolute, join as join5, resolve as resolve6 } from "path";
|
|
1914
|
+
import process11 from "process";
|
|
1915
|
+
import { pipeline as pipeline22 } from "stream/promises";
|
|
1916
|
+
import { createGzip as createGzip22 } from "zlib";
|
|
1917
|
+
import process10 from "process";
|
|
1918
|
+
import process92 from "process";
|
|
1823
1919
|
function deepMerge3(target, source) {
|
|
1824
1920
|
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) {
|
|
1825
1921
|
return source;
|
|
@@ -1977,7 +2073,7 @@ async function loadConfig4({
|
|
|
1977
2073
|
];
|
|
1978
2074
|
for (const configPath of configPaths) {
|
|
1979
2075
|
for (const ext of extensions) {
|
|
1980
|
-
const fullPath =
|
|
2076
|
+
const fullPath = resolve5(baseDir, `${configPath}${ext}`);
|
|
1981
2077
|
const config22 = await tryLoadConfig3(fullPath, defaultConfig2);
|
|
1982
2078
|
if (config22 !== null) {
|
|
1983
2079
|
return config22;
|
|
@@ -1985,7 +2081,7 @@ async function loadConfig4({
|
|
|
1985
2081
|
}
|
|
1986
2082
|
}
|
|
1987
2083
|
try {
|
|
1988
|
-
const pkgPath =
|
|
2084
|
+
const pkgPath = resolve5(baseDir, "package.json");
|
|
1989
2085
|
if (existsSync4(pkgPath)) {
|
|
1990
2086
|
const pkg = await import(pkgPath);
|
|
1991
2087
|
const pkgConfig = pkg[name];
|
|
@@ -1998,18 +2094,18 @@ async function loadConfig4({
|
|
|
1998
2094
|
} catch {}
|
|
1999
2095
|
return defaultConfig2;
|
|
2000
2096
|
}
|
|
2001
|
-
var defaultConfigDir3 =
|
|
2002
|
-
var defaultGeneratedDir3 =
|
|
2097
|
+
var defaultConfigDir3 = resolve5(process9.cwd(), "config");
|
|
2098
|
+
var defaultGeneratedDir3 = resolve5(process9.cwd(), "src/generated");
|
|
2003
2099
|
function getProjectRoot2(filePath, options = {}) {
|
|
2004
2100
|
let path = process22.cwd();
|
|
2005
2101
|
while (path.includes("storage"))
|
|
2006
2102
|
path = resolve22(path, "..");
|
|
2007
2103
|
const finalPath = resolve22(path, filePath || "");
|
|
2008
2104
|
if (options?.relative)
|
|
2009
|
-
return
|
|
2105
|
+
return relative3(process22.cwd(), finalPath);
|
|
2010
2106
|
return finalPath;
|
|
2011
2107
|
}
|
|
2012
|
-
var defaultLogDirectory2 = process22.env.CLARITY_LOG_DIR ||
|
|
2108
|
+
var defaultLogDirectory2 = process22.env.CLARITY_LOG_DIR || join4(getProjectRoot2(), "logs");
|
|
2013
2109
|
var defaultConfig2 = {
|
|
2014
2110
|
level: "info",
|
|
2015
2111
|
defaultName: "clarity",
|
|
@@ -2069,6 +2165,7 @@ async function isServerProcess2() {
|
|
|
2069
2165
|
}
|
|
2070
2166
|
return false;
|
|
2071
2167
|
}
|
|
2168
|
+
|
|
2072
2169
|
class JsonFormatter2 {
|
|
2073
2170
|
async format(entry) {
|
|
2074
2171
|
const isServer = await isServerProcess2();
|
|
@@ -2312,7 +2409,7 @@ class Logger2 {
|
|
|
2312
2409
|
const errorMessage = typeof error.message === "string" ? error.message : "Unknown error";
|
|
2313
2410
|
console.error(`Network error during write attempt ${retries + 1}/${maxRetries}:`, errorMessage);
|
|
2314
2411
|
const delay = backoffDelay * 2 ** retries;
|
|
2315
|
-
await new Promise((
|
|
2412
|
+
await new Promise((resolve322) => setTimeout(resolve322, delay));
|
|
2316
2413
|
retries++;
|
|
2317
2414
|
continue;
|
|
2318
2415
|
}
|
|
@@ -2340,7 +2437,7 @@ class Logger2 {
|
|
|
2340
2437
|
}
|
|
2341
2438
|
retries++;
|
|
2342
2439
|
const delay = backoffDelay * 2 ** (retries - 1);
|
|
2343
|
-
await new Promise((
|
|
2440
|
+
await new Promise((resolve322) => setTimeout(resolve322, delay));
|
|
2344
2441
|
}
|
|
2345
2442
|
}
|
|
2346
2443
|
})();
|
|
@@ -2463,11 +2560,11 @@ class Logger2 {
|
|
|
2463
2560
|
};
|
|
2464
2561
|
}
|
|
2465
2562
|
async compressData(data) {
|
|
2466
|
-
return new Promise((
|
|
2563
|
+
return new Promise((resolve322, reject) => {
|
|
2467
2564
|
const gzip = createGzip2();
|
|
2468
2565
|
const chunks = [];
|
|
2469
2566
|
gzip.on("data", (chunk2) => chunks.push(chunk2));
|
|
2470
|
-
gzip.on("end", () =>
|
|
2567
|
+
gzip.on("end", () => resolve322(Buffer2.from(Buffer2.concat(chunks))));
|
|
2471
2568
|
gzip.on("error", reject);
|
|
2472
2569
|
gzip.write(data);
|
|
2473
2570
|
gzip.end();
|
|
@@ -3053,7 +3150,7 @@ class Logger2 {
|
|
|
3053
3150
|
if (isBrowserProcess2()) {
|
|
3054
3151
|
return Promise.resolve(true);
|
|
3055
3152
|
}
|
|
3056
|
-
return new Promise((
|
|
3153
|
+
return new Promise((resolve322) => {
|
|
3057
3154
|
console.error(`${styles2.cyan("?")} ${message} (y/n) `);
|
|
3058
3155
|
const onData = (data) => {
|
|
3059
3156
|
const input = data.toString().trim().toLowerCase();
|
|
@@ -3065,7 +3162,7 @@ class Logger2 {
|
|
|
3065
3162
|
} catch {}
|
|
3066
3163
|
process52.stdin.pause();
|
|
3067
3164
|
console.error("");
|
|
3068
|
-
|
|
3165
|
+
resolve322(input === "y" || input === "yes");
|
|
3069
3166
|
};
|
|
3070
3167
|
try {
|
|
3071
3168
|
if (typeof process52.stdin.setRawMode === "function") {
|
|
@@ -3267,253 +3364,1895 @@ class Logger2 {
|
|
|
3267
3364
|
}
|
|
3268
3365
|
}
|
|
3269
3366
|
var logger2 = new Logger2("stacks");
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
var log2 = new Logger2("git-hooks", {
|
|
3274
|
-
showTags: true
|
|
3275
|
-
});
|
|
3276
|
-
var VALID_GIT_HOOKS = [
|
|
3277
|
-
"applypatch-msg",
|
|
3278
|
-
"pre-applypatch",
|
|
3279
|
-
"post-applypatch",
|
|
3280
|
-
"pre-commit",
|
|
3281
|
-
"pre-merge-commit",
|
|
3282
|
-
"prepare-commit-msg",
|
|
3283
|
-
"commit-msg",
|
|
3284
|
-
"post-commit",
|
|
3285
|
-
"pre-rebase",
|
|
3286
|
-
"post-checkout",
|
|
3287
|
-
"post-merge",
|
|
3288
|
-
"pre-push",
|
|
3289
|
-
"pre-receive",
|
|
3290
|
-
"update",
|
|
3291
|
-
"proc-receive",
|
|
3292
|
-
"post-receive",
|
|
3293
|
-
"post-update",
|
|
3294
|
-
"reference-transaction",
|
|
3295
|
-
"push-to-checkout",
|
|
3296
|
-
"pre-auto-gc",
|
|
3297
|
-
"post-rewrite",
|
|
3298
|
-
"sendemail-validate",
|
|
3299
|
-
"fsmonitor-watchman",
|
|
3300
|
-
"p4-changelist",
|
|
3301
|
-
"p4-prepare-changelist",
|
|
3302
|
-
"p4-post-changelist",
|
|
3303
|
-
"p4-pre-submit",
|
|
3304
|
-
"post-index-change"
|
|
3305
|
-
];
|
|
3306
|
-
var VALID_OPTIONS = ["preserveUnused", "verbose", "staged-lint"];
|
|
3307
|
-
var PREPEND_SCRIPT = `#!/bin/sh
|
|
3308
|
-
|
|
3309
|
-
if [ "$SKIP_BUN_GIT_HOOKS" = "1" ]; then
|
|
3310
|
-
echo "[INFO] SKIP_BUN_GIT_HOOKS is set to 1, skipping hook."
|
|
3311
|
-
exit 0
|
|
3312
|
-
fi
|
|
3313
|
-
|
|
3314
|
-
if [ -f "$BUN_GIT_HOOKS_RC" ]; then
|
|
3315
|
-
. "$BUN_GIT_HOOKS_RC"
|
|
3316
|
-
fi
|
|
3317
|
-
|
|
3318
|
-
`;
|
|
3319
|
-
function getGitProjectRoot(directory = process10.cwd()) {
|
|
3320
|
-
if (directory.endsWith(".git")) {
|
|
3321
|
-
return path.normalize(directory);
|
|
3367
|
+
function deepMerge22(target, source) {
|
|
3368
|
+
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) {
|
|
3369
|
+
return source;
|
|
3322
3370
|
}
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
return;
|
|
3371
|
+
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) {
|
|
3372
|
+
return { a: null, b: 2, c: undefined };
|
|
3326
3373
|
}
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3374
|
+
if (source === null || source === undefined) {
|
|
3375
|
+
return target;
|
|
3376
|
+
}
|
|
3377
|
+
if (Array.isArray(source) && !Array.isArray(target)) {
|
|
3378
|
+
return source;
|
|
3379
|
+
}
|
|
3380
|
+
if (Array.isArray(source) && Array.isArray(target)) {
|
|
3381
|
+
if (isObject22(target) && "arr" in target && Array.isArray(target.arr) && isObject22(source) && "arr" in source && Array.isArray(source.arr)) {
|
|
3382
|
+
return source;
|
|
3383
|
+
}
|
|
3384
|
+
if (source.length > 0 && target.length > 0 && isObject22(source[0]) && isObject22(target[0])) {
|
|
3385
|
+
const result = [...source];
|
|
3386
|
+
for (const targetItem of target) {
|
|
3387
|
+
if (isObject22(targetItem) && "name" in targetItem) {
|
|
3388
|
+
const existingItem = result.find((item) => isObject22(item) && ("name" in item) && item.name === targetItem.name);
|
|
3389
|
+
if (!existingItem) {
|
|
3390
|
+
result.push(targetItem);
|
|
3391
|
+
}
|
|
3392
|
+
} else if (isObject22(targetItem) && "path" in targetItem) {
|
|
3393
|
+
const existingItem = result.find((item) => isObject22(item) && ("path" in item) && item.path === targetItem.path);
|
|
3394
|
+
if (!existingItem) {
|
|
3395
|
+
result.push(targetItem);
|
|
3396
|
+
}
|
|
3397
|
+
} else if (!result.some((item) => deepEquals22(item, targetItem))) {
|
|
3398
|
+
result.push(targetItem);
|
|
3338
3399
|
}
|
|
3339
|
-
return path.normalize(gitDir);
|
|
3340
3400
|
}
|
|
3401
|
+
return result;
|
|
3341
3402
|
}
|
|
3342
|
-
|
|
3403
|
+
if (source.every((item) => typeof item === "string") && target.every((item) => typeof item === "string")) {
|
|
3404
|
+
const result = [...source];
|
|
3405
|
+
for (const item of target) {
|
|
3406
|
+
if (!result.includes(item)) {
|
|
3407
|
+
result.push(item);
|
|
3408
|
+
}
|
|
3409
|
+
}
|
|
3410
|
+
return result;
|
|
3411
|
+
}
|
|
3412
|
+
return source;
|
|
3343
3413
|
}
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
return;
|
|
3414
|
+
if (!isObject22(source) || !isObject22(target)) {
|
|
3415
|
+
return source;
|
|
3347
3416
|
}
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3417
|
+
const merged = { ...target };
|
|
3418
|
+
for (const key in source) {
|
|
3419
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
3420
|
+
const sourceValue = source[key];
|
|
3421
|
+
if (sourceValue === null || sourceValue === undefined) {
|
|
3422
|
+
continue;
|
|
3423
|
+
} else if (isObject22(sourceValue) && isObject22(merged[key])) {
|
|
3424
|
+
merged[key] = deepMerge22(merged[key], sourceValue);
|
|
3425
|
+
} else if (Array.isArray(sourceValue) && Array.isArray(merged[key])) {
|
|
3426
|
+
if (sourceValue.length > 0 && merged[key].length > 0 && isObject22(sourceValue[0]) && isObject22(merged[key][0])) {
|
|
3427
|
+
const result = [...sourceValue];
|
|
3428
|
+
for (const targetItem of merged[key]) {
|
|
3429
|
+
if (isObject22(targetItem) && "name" in targetItem) {
|
|
3430
|
+
const existingItem = result.find((item) => isObject22(item) && ("name" in item) && item.name === targetItem.name);
|
|
3431
|
+
if (!existingItem) {
|
|
3432
|
+
result.push(targetItem);
|
|
3433
|
+
}
|
|
3434
|
+
} else if (isObject22(targetItem) && "path" in targetItem) {
|
|
3435
|
+
const existingItem = result.find((item) => isObject22(item) && ("path" in item) && item.path === targetItem.path);
|
|
3436
|
+
if (!existingItem) {
|
|
3437
|
+
result.push(targetItem);
|
|
3438
|
+
}
|
|
3439
|
+
} else if (!result.some((item) => deepEquals22(item, targetItem))) {
|
|
3440
|
+
result.push(targetItem);
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
merged[key] = result;
|
|
3444
|
+
} else if (sourceValue.every((item) => typeof item === "string") && merged[key].every((item) => typeof item === "string")) {
|
|
3445
|
+
const result = [...sourceValue];
|
|
3446
|
+
for (const item of merged[key]) {
|
|
3447
|
+
if (!result.includes(item)) {
|
|
3448
|
+
result.push(item);
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
merged[key] = result;
|
|
3452
|
+
} else {
|
|
3453
|
+
merged[key] = sourceValue;
|
|
3454
|
+
}
|
|
3455
|
+
} else {
|
|
3456
|
+
merged[key] = sourceValue;
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3353
3459
|
}
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3460
|
+
return merged;
|
|
3461
|
+
}
|
|
3462
|
+
function deepMergeWithArrayStrategy2(target, source, strategy = "replace") {
|
|
3463
|
+
if (source === null || source === undefined)
|
|
3464
|
+
return target;
|
|
3465
|
+
if (Array.isArray(source)) {
|
|
3466
|
+
return strategy === "replace" ? source : deepMerge22(target, source);
|
|
3358
3467
|
}
|
|
3359
|
-
if (
|
|
3360
|
-
return
|
|
3468
|
+
if (Array.isArray(target)) {
|
|
3469
|
+
return strategy === "replace" ? source : deepMerge22(target, source);
|
|
3361
3470
|
}
|
|
3362
|
-
if (
|
|
3363
|
-
return
|
|
3471
|
+
if (!isObject22(source) || !isObject22(target))
|
|
3472
|
+
return source;
|
|
3473
|
+
const result = { ...target };
|
|
3474
|
+
for (const key of Object.keys(source)) {
|
|
3475
|
+
if (!Object.prototype.hasOwnProperty.call(source, key))
|
|
3476
|
+
continue;
|
|
3477
|
+
const sourceValue = source[key];
|
|
3478
|
+
const targetValue = result[key];
|
|
3479
|
+
if (sourceValue === null || sourceValue === undefined)
|
|
3480
|
+
continue;
|
|
3481
|
+
if (Array.isArray(sourceValue) || Array.isArray(targetValue)) {
|
|
3482
|
+
if (strategy === "replace") {
|
|
3483
|
+
result[key] = sourceValue;
|
|
3484
|
+
} else {
|
|
3485
|
+
result[key] = deepMerge22(targetValue, sourceValue);
|
|
3486
|
+
}
|
|
3487
|
+
} else if (isObject22(sourceValue) && isObject22(targetValue)) {
|
|
3488
|
+
result[key] = deepMergeWithArrayStrategy2(targetValue, sourceValue, strategy);
|
|
3489
|
+
} else {
|
|
3490
|
+
result[key] = sourceValue;
|
|
3491
|
+
}
|
|
3364
3492
|
}
|
|
3365
|
-
return;
|
|
3493
|
+
return result;
|
|
3366
3494
|
}
|
|
3367
|
-
function
|
|
3368
|
-
if (
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3495
|
+
function deepEquals22(a, b) {
|
|
3496
|
+
if (a === b)
|
|
3497
|
+
return true;
|
|
3498
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
3499
|
+
if (a.length !== b.length)
|
|
3500
|
+
return false;
|
|
3501
|
+
for (let i = 0;i < a.length; i++) {
|
|
3502
|
+
if (!deepEquals22(a[i], b[i]))
|
|
3503
|
+
return false;
|
|
3504
|
+
}
|
|
3374
3505
|
return true;
|
|
3375
3506
|
}
|
|
3376
|
-
if (
|
|
3377
|
-
|
|
3507
|
+
if (isObject22(a) && isObject22(b)) {
|
|
3508
|
+
const keysA = Object.keys(a);
|
|
3509
|
+
const keysB = Object.keys(b);
|
|
3510
|
+
if (keysA.length !== keysB.length)
|
|
3511
|
+
return false;
|
|
3512
|
+
for (const key of keysA) {
|
|
3513
|
+
if (!Object.prototype.hasOwnProperty.call(b, key))
|
|
3514
|
+
return false;
|
|
3515
|
+
if (!deepEquals22(a[key], b[key]))
|
|
3516
|
+
return false;
|
|
3517
|
+
}
|
|
3518
|
+
return true;
|
|
3378
3519
|
}
|
|
3379
|
-
return
|
|
3520
|
+
return false;
|
|
3380
3521
|
}
|
|
3381
|
-
function
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3522
|
+
function isObject22(item) {
|
|
3523
|
+
return Boolean(item && typeof item === "object" && !Array.isArray(item));
|
|
3524
|
+
}
|
|
3525
|
+
var log2 = new Logger2("bunfig", {
|
|
3526
|
+
showTags: true
|
|
3527
|
+
});
|
|
3528
|
+
async function tryLoadConfig22(configPath, defaultConfig22, arrayStrategy = "replace") {
|
|
3529
|
+
if (!existsSync32(configPath))
|
|
3530
|
+
return null;
|
|
3531
|
+
try {
|
|
3532
|
+
const importedConfig = await import(configPath);
|
|
3533
|
+
const loadedConfig = importedConfig.default || importedConfig;
|
|
3534
|
+
if (typeof loadedConfig !== "object" || loadedConfig === null || Array.isArray(loadedConfig))
|
|
3535
|
+
return null;
|
|
3536
|
+
try {
|
|
3537
|
+
return deepMergeWithArrayStrategy2(defaultConfig22, loadedConfig, arrayStrategy);
|
|
3538
|
+
} catch {
|
|
3539
|
+
return null;
|
|
3540
|
+
}
|
|
3541
|
+
} catch {
|
|
3542
|
+
return null;
|
|
3388
3543
|
}
|
|
3389
|
-
const packageJsonDataRaw = fs.readFileSync(targetPackageJson, { encoding: "utf-8" });
|
|
3390
|
-
return { packageJsonContent: JSON.parse(packageJsonDataRaw), packageJsonPath: targetPackageJson };
|
|
3391
3544
|
}
|
|
3392
|
-
function
|
|
3393
|
-
if (!
|
|
3394
|
-
|
|
3395
|
-
const
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
if (!
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3545
|
+
function applyEnvVarsToConfig2(name, config32, verbose = false) {
|
|
3546
|
+
if (!name)
|
|
3547
|
+
return config32;
|
|
3548
|
+
const envPrefix = name.toUpperCase().replace(/-/g, "_");
|
|
3549
|
+
const result = { ...config32 };
|
|
3550
|
+
function processObject(obj, path = []) {
|
|
3551
|
+
const result2 = { ...obj };
|
|
3552
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
3553
|
+
const envPath = [...path, key];
|
|
3554
|
+
const formatKey = (k) => k.replace(/([A-Z])/g, "_$1").toUpperCase();
|
|
3555
|
+
const envKey = `${envPrefix}_${envPath.map(formatKey).join("_")}`;
|
|
3556
|
+
const oldEnvKey = `${envPrefix}_${envPath.map((p) => p.toUpperCase()).join("_")}`;
|
|
3557
|
+
if (verbose)
|
|
3558
|
+
log2.info(`Checking environment variable ${envKey} for config ${name}.${envPath.join(".")}`);
|
|
3559
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
3560
|
+
result2[key] = processObject(value, envPath);
|
|
3561
|
+
} else {
|
|
3562
|
+
const envValue = process62.env[envKey] || process62.env[oldEnvKey];
|
|
3563
|
+
if (envValue !== undefined) {
|
|
3564
|
+
if (verbose) {
|
|
3565
|
+
log2.info(`Using environment variable ${envValue ? envKey : oldEnvKey} for config ${name}.${envPath.join(".")}`);
|
|
3566
|
+
}
|
|
3567
|
+
if (typeof value === "number") {
|
|
3568
|
+
result2[key] = Number(envValue);
|
|
3569
|
+
} else if (typeof value === "boolean") {
|
|
3570
|
+
result2[key] = envValue.toLowerCase() === "true";
|
|
3571
|
+
} else if (Array.isArray(value)) {
|
|
3572
|
+
try {
|
|
3573
|
+
const parsed = JSON.parse(envValue);
|
|
3574
|
+
if (Array.isArray(parsed)) {
|
|
3575
|
+
result2[key] = parsed;
|
|
3576
|
+
} else {
|
|
3577
|
+
result2[key] = envValue.split(",").map((item) => item.trim());
|
|
3578
|
+
}
|
|
3579
|
+
} catch {
|
|
3580
|
+
result2[key] = envValue.split(",").map((item) => item.trim());
|
|
3581
|
+
}
|
|
3582
|
+
} else {
|
|
3583
|
+
result2[key] = envValue;
|
|
3584
|
+
}
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
return result2;
|
|
3589
|
+
}
|
|
3590
|
+
return processObject(result);
|
|
3591
|
+
}
|
|
3592
|
+
async function loadConfig32({
|
|
3593
|
+
name = "",
|
|
3594
|
+
alias,
|
|
3595
|
+
cwd,
|
|
3596
|
+
configDir,
|
|
3597
|
+
defaultConfig: defaultConfig22,
|
|
3598
|
+
verbose = false,
|
|
3599
|
+
checkEnv = true,
|
|
3600
|
+
arrayStrategy = "replace"
|
|
3601
|
+
}) {
|
|
3602
|
+
const configWithEnvVars = checkEnv && typeof defaultConfig22 === "object" && defaultConfig22 !== null && !Array.isArray(defaultConfig22) ? applyEnvVarsToConfig2(name, defaultConfig22, verbose) : defaultConfig22;
|
|
3603
|
+
const baseDir = cwd || process62.cwd();
|
|
3604
|
+
const extensions = [".ts", ".js", ".mjs", ".cjs", ".json"];
|
|
3605
|
+
if (verbose) {
|
|
3606
|
+
log2.info(`Loading configuration for "${name}"${alias ? ` (alias: "${alias}")` : ""} from ${baseDir}`);
|
|
3607
|
+
}
|
|
3608
|
+
const primaryBarePatterns = [name, `.${name}`].filter(Boolean);
|
|
3609
|
+
const primaryConfigSuffixPatterns = [`${name}.config`, `.${name}.config`].filter(Boolean);
|
|
3610
|
+
const aliasBarePatterns = alias ? [alias, `.${alias}`] : [];
|
|
3611
|
+
const aliasConfigSuffixPatterns = alias ? [`${alias}.config`, `.${alias}.config`] : [];
|
|
3612
|
+
const searchDirectories = Array.from(new Set([
|
|
3613
|
+
baseDir,
|
|
3614
|
+
resolve32(baseDir, "config"),
|
|
3615
|
+
resolve32(baseDir, ".config"),
|
|
3616
|
+
configDir ? resolve32(baseDir, configDir) : undefined
|
|
3617
|
+
].filter(Boolean)));
|
|
3618
|
+
for (const dir of searchDirectories) {
|
|
3619
|
+
if (verbose)
|
|
3620
|
+
log2.info(`Searching for configuration in: ${dir}`);
|
|
3621
|
+
const isConfigLikeDir = [resolve32(baseDir, "config"), resolve32(baseDir, ".config")].concat(configDir ? [resolve32(baseDir, configDir)] : []).includes(dir);
|
|
3622
|
+
const patternsForDir = isConfigLikeDir ? [...primaryBarePatterns, ...primaryConfigSuffixPatterns, ...aliasBarePatterns, ...aliasConfigSuffixPatterns] : [...primaryConfigSuffixPatterns, ...primaryBarePatterns, ...aliasConfigSuffixPatterns, ...aliasBarePatterns];
|
|
3623
|
+
for (const configPath of patternsForDir) {
|
|
3624
|
+
for (const ext of extensions) {
|
|
3625
|
+
const fullPath = resolve32(dir, `${configPath}${ext}`);
|
|
3626
|
+
const config32 = await tryLoadConfig22(fullPath, configWithEnvVars, arrayStrategy);
|
|
3627
|
+
if (config32 !== null) {
|
|
3628
|
+
if (verbose) {
|
|
3629
|
+
log2.success(`Configuration loaded from: ${fullPath}`);
|
|
3630
|
+
}
|
|
3631
|
+
return config32;
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
if (name) {
|
|
3637
|
+
const homeConfigDir = resolve32(homedir2(), ".config", name);
|
|
3638
|
+
const homeConfigPatterns = ["config", `${name}.config`];
|
|
3639
|
+
if (alias) {
|
|
3640
|
+
homeConfigPatterns.push(`${alias}.config`);
|
|
3641
|
+
}
|
|
3642
|
+
if (verbose) {
|
|
3643
|
+
log2.info(`Checking user config directory: ${homeConfigDir}`);
|
|
3644
|
+
}
|
|
3645
|
+
for (const configPath of homeConfigPatterns) {
|
|
3646
|
+
for (const ext of extensions) {
|
|
3647
|
+
const fullPath = resolve32(homeConfigDir, `${configPath}${ext}`);
|
|
3648
|
+
const config32 = await tryLoadConfig22(fullPath, configWithEnvVars, arrayStrategy);
|
|
3649
|
+
if (config32 !== null) {
|
|
3650
|
+
if (verbose) {
|
|
3651
|
+
log2.success(`Configuration loaded from user config directory: ${fullPath}`);
|
|
3652
|
+
}
|
|
3653
|
+
return config32;
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3657
|
+
}
|
|
3658
|
+
if (name) {
|
|
3659
|
+
const homeConfigDir = resolve32(homedir2(), ".config");
|
|
3660
|
+
const homeConfigDotfilePatterns = [`.${name}.config`];
|
|
3661
|
+
if (alias)
|
|
3662
|
+
homeConfigDotfilePatterns.push(`.${alias}.config`);
|
|
3663
|
+
if (verbose)
|
|
3664
|
+
log2.info(`Checking user config directory for dotfile configs: ${homeConfigDir}`);
|
|
3665
|
+
for (const configPath of homeConfigDotfilePatterns) {
|
|
3666
|
+
for (const ext of extensions) {
|
|
3667
|
+
const fullPath = resolve32(homeConfigDir, `${configPath}${ext}`);
|
|
3668
|
+
const config32 = await tryLoadConfig22(fullPath, configWithEnvVars, arrayStrategy);
|
|
3669
|
+
if (config32 !== null) {
|
|
3670
|
+
if (verbose)
|
|
3671
|
+
log2.success(`Configuration loaded from user config directory dotfile: ${fullPath}`);
|
|
3672
|
+
return config32;
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
if (name) {
|
|
3678
|
+
const homeDir = homedir2();
|
|
3679
|
+
const homeRootPatterns = [`.${name}.config`, `.${name}`];
|
|
3680
|
+
if (alias) {
|
|
3681
|
+
homeRootPatterns.push(`.${alias}.config`);
|
|
3682
|
+
homeRootPatterns.push(`.${alias}`);
|
|
3683
|
+
}
|
|
3684
|
+
if (verbose)
|
|
3685
|
+
log2.info(`Checking user home directory for dotfile configs: ${homeDir}`);
|
|
3686
|
+
for (const configPath of homeRootPatterns) {
|
|
3687
|
+
for (const ext of extensions) {
|
|
3688
|
+
const fullPath = resolve32(homeDir, `${configPath}${ext}`);
|
|
3689
|
+
const config32 = await tryLoadConfig22(fullPath, configWithEnvVars, arrayStrategy);
|
|
3690
|
+
if (config32 !== null) {
|
|
3691
|
+
if (verbose)
|
|
3692
|
+
log2.success(`Configuration loaded from user home directory: ${fullPath}`);
|
|
3693
|
+
return config32;
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
}
|
|
3697
|
+
}
|
|
3698
|
+
try {
|
|
3699
|
+
const pkgPath = resolve32(baseDir, "package.json");
|
|
3700
|
+
if (existsSync32(pkgPath)) {
|
|
3701
|
+
const pkg = await import(pkgPath);
|
|
3702
|
+
let pkgConfig = pkg[name];
|
|
3703
|
+
if (!pkgConfig && alias) {
|
|
3704
|
+
pkgConfig = pkg[alias];
|
|
3705
|
+
if (pkgConfig && verbose) {
|
|
3706
|
+
log2.success(`Using alias "${alias}" configuration from package.json`);
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
if (pkgConfig && typeof pkgConfig === "object" && !Array.isArray(pkgConfig)) {
|
|
3710
|
+
try {
|
|
3711
|
+
if (verbose) {
|
|
3712
|
+
log2.success(`Configuration loaded from package.json: ${pkgConfig === pkg[name] ? name : alias}`);
|
|
3713
|
+
}
|
|
3714
|
+
return deepMergeWithArrayStrategy2(configWithEnvVars, pkgConfig, arrayStrategy);
|
|
3715
|
+
} catch (error) {
|
|
3716
|
+
if (verbose) {
|
|
3717
|
+
log2.warn(`Failed to merge package.json config:`, error);
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
} catch (error) {
|
|
3723
|
+
if (verbose) {
|
|
3724
|
+
log2.warn(`Failed to load package.json:`, error);
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3727
|
+
if (verbose) {
|
|
3728
|
+
log2.info(`No configuration found for "${name}"${alias ? ` or alias "${alias}"` : ""}, using default configuration with environment variables`);
|
|
3729
|
+
}
|
|
3730
|
+
return configWithEnvVars;
|
|
3731
|
+
}
|
|
3732
|
+
var defaultConfigDir22 = resolve32(process62.cwd(), "config");
|
|
3733
|
+
var defaultGeneratedDir22 = resolve32(process62.cwd(), "src/generated");
|
|
3734
|
+
function getProjectRoot22(filePath, options = {}) {
|
|
3735
|
+
let path = process72.cwd();
|
|
3736
|
+
while (path.includes("storage"))
|
|
3737
|
+
path = resolve4(path, "..");
|
|
3738
|
+
const finalPath = resolve4(path, filePath || "");
|
|
3739
|
+
if (options?.relative)
|
|
3740
|
+
return relative2(process72.cwd(), finalPath);
|
|
3741
|
+
return finalPath;
|
|
3742
|
+
}
|
|
3743
|
+
var defaultLogDirectory22 = process72.env.CLARITY_LOG_DIR || join3(getProjectRoot22(), "logs");
|
|
3744
|
+
var defaultConfig22 = {
|
|
3745
|
+
level: "info",
|
|
3746
|
+
defaultName: "clarity",
|
|
3747
|
+
timestamp: true,
|
|
3748
|
+
colors: true,
|
|
3749
|
+
format: "text",
|
|
3750
|
+
maxLogSize: 10485760,
|
|
3751
|
+
logDatePattern: "YYYY-MM-DD",
|
|
3752
|
+
logDirectory: defaultLogDirectory22,
|
|
3753
|
+
rotation: {
|
|
3754
|
+
frequency: "daily",
|
|
3755
|
+
maxSize: 10485760,
|
|
3756
|
+
maxFiles: 5,
|
|
3757
|
+
compress: false,
|
|
3758
|
+
rotateHour: 0,
|
|
3759
|
+
rotateMinute: 0,
|
|
3760
|
+
rotateDayOfWeek: 0,
|
|
3761
|
+
rotateDayOfMonth: 1,
|
|
3762
|
+
encrypt: false
|
|
3763
|
+
},
|
|
3764
|
+
verbose: false,
|
|
3765
|
+
writeToFile: false
|
|
3766
|
+
};
|
|
3767
|
+
async function loadConfig42() {
|
|
3768
|
+
try {
|
|
3769
|
+
const loadedConfig = await loadConfig32({
|
|
3770
|
+
name: "clarity",
|
|
3771
|
+
defaultConfig: defaultConfig22,
|
|
3772
|
+
cwd: process72.cwd()
|
|
3773
|
+
});
|
|
3774
|
+
return { ...defaultConfig22, ...loadedConfig || {} };
|
|
3775
|
+
} catch {
|
|
3776
|
+
return defaultConfig22;
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
var config22 = await loadConfig42();
|
|
3780
|
+
function isBrowserProcess22() {
|
|
3781
|
+
if (process92.env.NODE_ENV === "test" || process92.env.BUN_ENV === "test") {
|
|
3782
|
+
return false;
|
|
3783
|
+
}
|
|
3784
|
+
return typeof window !== "undefined";
|
|
3785
|
+
}
|
|
3786
|
+
async function isServerProcess22() {
|
|
3787
|
+
if (process92.env.NODE_ENV === "test" || process92.env.BUN_ENV === "test") {
|
|
3788
|
+
return true;
|
|
3789
|
+
}
|
|
3790
|
+
if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
|
|
3791
|
+
return true;
|
|
3792
|
+
}
|
|
3793
|
+
if (typeof process92 !== "undefined") {
|
|
3794
|
+
const type = process92.type;
|
|
3795
|
+
if (type === "renderer" || type === "worker") {
|
|
3796
|
+
return false;
|
|
3797
|
+
}
|
|
3798
|
+
return !!(process92.versions && (process92.versions.node || process92.versions.bun));
|
|
3799
|
+
}
|
|
3800
|
+
return false;
|
|
3801
|
+
}
|
|
3802
|
+
class JsonFormatter22 {
|
|
3803
|
+
async format(entry) {
|
|
3804
|
+
const isServer = await isServerProcess22();
|
|
3805
|
+
const metadata = await this.getMetadata(isServer);
|
|
3806
|
+
return JSON.stringify({
|
|
3807
|
+
timestamp: entry.timestamp.toISOString(),
|
|
3808
|
+
level: entry.level,
|
|
3809
|
+
name: entry.name,
|
|
3810
|
+
message: entry.message,
|
|
3811
|
+
metadata
|
|
3812
|
+
});
|
|
3813
|
+
}
|
|
3814
|
+
async getMetadata(isServer) {
|
|
3815
|
+
if (isServer) {
|
|
3816
|
+
const { hostname } = await import("os");
|
|
3817
|
+
return {
|
|
3818
|
+
pid: process10.pid,
|
|
3819
|
+
hostname: hostname(),
|
|
3820
|
+
environment: process10.env.NODE_ENV || "development",
|
|
3821
|
+
platform: process10.platform,
|
|
3822
|
+
version: process10.version
|
|
3823
|
+
};
|
|
3824
|
+
}
|
|
3825
|
+
return {
|
|
3826
|
+
userAgent: navigator.userAgent,
|
|
3827
|
+
hostname: window.location.hostname || "browser",
|
|
3828
|
+
environment: process10.env.NODE_ENV || process10.env.BUN_ENV || "development",
|
|
3829
|
+
viewport: {
|
|
3830
|
+
width: window.innerWidth,
|
|
3831
|
+
height: window.innerHeight
|
|
3832
|
+
},
|
|
3833
|
+
language: navigator.language
|
|
3834
|
+
};
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
var terminalStyles22 = {
|
|
3838
|
+
red: (text) => `\x1B[31m${text}\x1B[0m`,
|
|
3839
|
+
green: (text) => `\x1B[32m${text}\x1B[0m`,
|
|
3840
|
+
yellow: (text) => `\x1B[33m${text}\x1B[0m`,
|
|
3841
|
+
blue: (text) => `\x1B[34m${text}\x1B[0m`,
|
|
3842
|
+
magenta: (text) => `\x1B[35m${text}\x1B[0m`,
|
|
3843
|
+
cyan: (text) => `\x1B[36m${text}\x1B[0m`,
|
|
3844
|
+
white: (text) => `\x1B[37m${text}\x1B[0m`,
|
|
3845
|
+
gray: (text) => `\x1B[90m${text}\x1B[0m`,
|
|
3846
|
+
bgRed: (text) => `\x1B[41m${text}\x1B[0m`,
|
|
3847
|
+
bgYellow: (text) => `\x1B[43m${text}\x1B[0m`,
|
|
3848
|
+
bgGray: (text) => `\x1B[100m${text}\x1B[0m`,
|
|
3849
|
+
bold: (text) => `\x1B[1m${text}\x1B[0m`,
|
|
3850
|
+
dim: (text) => `\x1B[2m${text}\x1B[0m`,
|
|
3851
|
+
italic: (text) => `\x1B[3m${text}\x1B[0m`,
|
|
3852
|
+
underline: (text) => `\x1B[4m${text}\x1B[0m`,
|
|
3853
|
+
strikethrough: (text) => `\x1B[9m${text}\x1B[0m`,
|
|
3854
|
+
reset: "\x1B[0m"
|
|
3855
|
+
};
|
|
3856
|
+
var styles22 = terminalStyles22;
|
|
3857
|
+
var red22 = terminalStyles22.red;
|
|
3858
|
+
var green22 = terminalStyles22.green;
|
|
3859
|
+
var yellow22 = terminalStyles22.yellow;
|
|
3860
|
+
var blue22 = terminalStyles22.blue;
|
|
3861
|
+
var magenta22 = terminalStyles22.magenta;
|
|
3862
|
+
var cyan22 = terminalStyles22.cyan;
|
|
3863
|
+
var white22 = terminalStyles22.white;
|
|
3864
|
+
var gray22 = terminalStyles22.gray;
|
|
3865
|
+
var bgRed22 = terminalStyles22.bgRed;
|
|
3866
|
+
var bgYellow22 = terminalStyles22.bgYellow;
|
|
3867
|
+
var bgGray = terminalStyles22.bgGray;
|
|
3868
|
+
var bold22 = terminalStyles22.bold;
|
|
3869
|
+
var dim22 = terminalStyles22.dim;
|
|
3870
|
+
var italic22 = terminalStyles22.italic;
|
|
3871
|
+
var underline22 = terminalStyles22.underline;
|
|
3872
|
+
var strikethrough = terminalStyles22.strikethrough;
|
|
3873
|
+
var reset22 = terminalStyles22.reset;
|
|
3874
|
+
var defaultFingersCrossedConfig22 = {
|
|
3875
|
+
activationLevel: "error",
|
|
3876
|
+
bufferSize: 50,
|
|
3877
|
+
flushOnDeactivation: true,
|
|
3878
|
+
stopBuffering: false
|
|
3879
|
+
};
|
|
3880
|
+
var levelIcons22 = {
|
|
3881
|
+
debug: "\uD83D\uDD0D",
|
|
3882
|
+
info: blue22("ℹ"),
|
|
3883
|
+
success: green22("✓"),
|
|
3884
|
+
warning: bgYellow22(white22(bold22(" WARN "))),
|
|
3885
|
+
error: bgRed22(white22(bold22(" ERROR ")))
|
|
3886
|
+
};
|
|
3887
|
+
|
|
3888
|
+
class Logger22 {
|
|
3889
|
+
name;
|
|
3890
|
+
fileLocks = new Map;
|
|
3891
|
+
currentKeyId = null;
|
|
3892
|
+
keys = new Map;
|
|
3893
|
+
fingersCrossedConfig;
|
|
3894
|
+
fingersCrossedActive = false;
|
|
3895
|
+
currentLogFile;
|
|
3896
|
+
rotationTimeout;
|
|
3897
|
+
keyRotationTimeout;
|
|
3898
|
+
encryptionKeys;
|
|
3899
|
+
logBuffer = [];
|
|
3900
|
+
isActivated = false;
|
|
3901
|
+
pendingOperations = [];
|
|
3902
|
+
enabled;
|
|
3903
|
+
fancy;
|
|
3904
|
+
tagFormat;
|
|
3905
|
+
timestampPosition;
|
|
3906
|
+
environment;
|
|
3907
|
+
config;
|
|
3908
|
+
options;
|
|
3909
|
+
formatter;
|
|
3910
|
+
timers = new Set;
|
|
3911
|
+
subLoggers = new Set;
|
|
3912
|
+
fingersCrossedBuffer = [];
|
|
3913
|
+
ANSI_PATTERN = /\u001B\[.*?m/g;
|
|
3914
|
+
activeProgressBar = null;
|
|
3915
|
+
constructor(name, options = {}) {
|
|
3916
|
+
this.name = name;
|
|
3917
|
+
this.config = { ...config22 };
|
|
3918
|
+
this.options = this.normalizeOptions(options);
|
|
3919
|
+
this.formatter = this.options.formatter || new JsonFormatter22;
|
|
3920
|
+
this.enabled = options.enabled ?? true;
|
|
3921
|
+
this.fancy = options.fancy ?? true;
|
|
3922
|
+
this.tagFormat = options.tagFormat ?? { prefix: "[", suffix: "]" };
|
|
3923
|
+
this.timestampPosition = options.timestampPosition ?? "right";
|
|
3924
|
+
this.environment = options.environment ?? process11.env.APP_ENV ?? "local";
|
|
3925
|
+
this.fingersCrossedConfig = this.initializeFingersCrossedConfig(options);
|
|
3926
|
+
const configOptions = { ...options };
|
|
3927
|
+
const hasTimestamp = options.timestamp !== undefined;
|
|
3928
|
+
if (hasTimestamp) {
|
|
3929
|
+
delete configOptions.timestamp;
|
|
3930
|
+
}
|
|
3931
|
+
this.config = {
|
|
3932
|
+
...this.config,
|
|
3933
|
+
...configOptions,
|
|
3934
|
+
timestamp: hasTimestamp || this.config.timestamp
|
|
3935
|
+
};
|
|
3936
|
+
this.currentLogFile = this.generateLogFilename();
|
|
3937
|
+
this.encryptionKeys = new Map;
|
|
3938
|
+
if (this.validateEncryptionConfig()) {
|
|
3939
|
+
this.setupRotation();
|
|
3940
|
+
const initialKeyId = this.generateKeyId();
|
|
3941
|
+
const initialKey = this.generateKey();
|
|
3942
|
+
this.currentKeyId = initialKeyId;
|
|
3943
|
+
this.keys.set(initialKeyId, initialKey);
|
|
3944
|
+
this.encryptionKeys.set(initialKeyId, {
|
|
3945
|
+
key: initialKey,
|
|
3946
|
+
createdAt: new Date
|
|
3947
|
+
});
|
|
3948
|
+
this.setupKeyRotation();
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
shouldActivateFingersCrossed(level) {
|
|
3952
|
+
if (!this.fingersCrossedConfig)
|
|
3953
|
+
return false;
|
|
3954
|
+
const levels = {
|
|
3955
|
+
debug: 0,
|
|
3956
|
+
info: 1,
|
|
3957
|
+
success: 2,
|
|
3958
|
+
warning: 3,
|
|
3959
|
+
error: 4
|
|
3960
|
+
};
|
|
3961
|
+
const activation = this.fingersCrossedConfig.activationLevel ?? "error";
|
|
3962
|
+
return levels[level] >= levels[activation];
|
|
3963
|
+
}
|
|
3964
|
+
initializeFingersCrossedConfig(options) {
|
|
3965
|
+
if (!options.fingersCrossedEnabled && options.fingersCrossed) {
|
|
3966
|
+
return {
|
|
3967
|
+
...defaultFingersCrossedConfig22,
|
|
3968
|
+
...options.fingersCrossed
|
|
3969
|
+
};
|
|
3970
|
+
}
|
|
3971
|
+
if (!options.fingersCrossedEnabled) {
|
|
3972
|
+
return null;
|
|
3973
|
+
}
|
|
3974
|
+
if (!options.fingersCrossed) {
|
|
3975
|
+
return { ...defaultFingersCrossedConfig22 };
|
|
3976
|
+
}
|
|
3977
|
+
return {
|
|
3978
|
+
...defaultFingersCrossedConfig22,
|
|
3979
|
+
...options.fingersCrossed
|
|
3980
|
+
};
|
|
3981
|
+
}
|
|
3982
|
+
normalizeOptions(options) {
|
|
3983
|
+
const defaultOptions = {
|
|
3984
|
+
format: "json",
|
|
3985
|
+
level: "info",
|
|
3986
|
+
logDirectory: config22.logDirectory,
|
|
3987
|
+
rotation: undefined,
|
|
3988
|
+
timestamp: undefined,
|
|
3989
|
+
fingersCrossed: {},
|
|
3990
|
+
enabled: true,
|
|
3991
|
+
showTags: false,
|
|
3992
|
+
formatter: undefined
|
|
3993
|
+
};
|
|
3994
|
+
const mergedOptions = {
|
|
3995
|
+
...defaultOptions,
|
|
3996
|
+
...Object.fromEntries(Object.entries(options).filter(([, value]) => value !== undefined))
|
|
3997
|
+
};
|
|
3998
|
+
if (!mergedOptions.level || !["debug", "info", "success", "warning", "error"].includes(mergedOptions.level)) {
|
|
3999
|
+
mergedOptions.level = defaultOptions.level;
|
|
4000
|
+
}
|
|
4001
|
+
return mergedOptions;
|
|
4002
|
+
}
|
|
4003
|
+
shouldWriteToFile() {
|
|
4004
|
+
return !isBrowserProcess22() && this.config.writeToFile === true;
|
|
4005
|
+
}
|
|
4006
|
+
async writeToFile(data) {
|
|
4007
|
+
const cancelled = false;
|
|
4008
|
+
const operationPromise = (async () => {
|
|
4009
|
+
let fd;
|
|
4010
|
+
let retries = 0;
|
|
4011
|
+
const maxRetries = 3;
|
|
4012
|
+
const backoffDelay = 1000;
|
|
4013
|
+
while (retries < maxRetries) {
|
|
4014
|
+
try {
|
|
4015
|
+
try {
|
|
4016
|
+
try {
|
|
4017
|
+
await access22(this.config.logDirectory, constants22.F_OK | constants22.W_OK);
|
|
4018
|
+
} catch (err) {
|
|
4019
|
+
if (err instanceof Error && "code" in err) {
|
|
4020
|
+
if (err.code === "ENOENT") {
|
|
4021
|
+
await mkdir22(this.config.logDirectory, { recursive: true, mode: 493 });
|
|
4022
|
+
} else if (err.code === "EACCES") {
|
|
4023
|
+
throw new Error(`No write permission for log directory: ${this.config.logDirectory}`);
|
|
4024
|
+
} else {
|
|
4025
|
+
throw err;
|
|
4026
|
+
}
|
|
4027
|
+
} else {
|
|
4028
|
+
throw err;
|
|
4029
|
+
}
|
|
4030
|
+
}
|
|
4031
|
+
} catch (err) {
|
|
4032
|
+
console.error("Debug: [writeToFile] Failed to create log directory:", err);
|
|
4033
|
+
throw err;
|
|
4034
|
+
}
|
|
4035
|
+
if (cancelled)
|
|
4036
|
+
throw new Error("Operation cancelled: Logger was destroyed");
|
|
4037
|
+
const dataToWrite = this.validateEncryptionConfig() ? (await this.encrypt(data)).encrypted : Buffer22.from(data);
|
|
4038
|
+
try {
|
|
4039
|
+
if (!existsSync42(this.currentLogFile)) {
|
|
4040
|
+
await writeFile22(this.currentLogFile, "", { mode: 420 });
|
|
4041
|
+
}
|
|
4042
|
+
fd = openSync22(this.currentLogFile, "a", 420);
|
|
4043
|
+
writeFileSync42(fd, dataToWrite, { flag: "a" });
|
|
4044
|
+
fsyncSync22(fd);
|
|
4045
|
+
if (fd !== undefined) {
|
|
4046
|
+
closeSync22(fd);
|
|
4047
|
+
fd = undefined;
|
|
4048
|
+
}
|
|
4049
|
+
const stats = await stat22(this.currentLogFile);
|
|
4050
|
+
if (stats.size === 0) {
|
|
4051
|
+
await writeFile22(this.currentLogFile, dataToWrite, { flag: "w", mode: 420 });
|
|
4052
|
+
const retryStats = await stat22(this.currentLogFile);
|
|
4053
|
+
if (retryStats.size === 0) {
|
|
4054
|
+
throw new Error("File exists but is empty after retry write");
|
|
4055
|
+
}
|
|
4056
|
+
}
|
|
4057
|
+
return;
|
|
4058
|
+
} catch (err) {
|
|
4059
|
+
const error = err;
|
|
4060
|
+
if (error.code && ["ENETDOWN", "ENETUNREACH", "ENOTFOUND", "ETIMEDOUT"].includes(error.code)) {
|
|
4061
|
+
if (retries < maxRetries - 1) {
|
|
4062
|
+
const errorMessage = typeof error.message === "string" ? error.message : "Unknown error";
|
|
4063
|
+
console.error(`Network error during write attempt ${retries + 1}/${maxRetries}:`, errorMessage);
|
|
4064
|
+
const delay = backoffDelay * 2 ** retries;
|
|
4065
|
+
await new Promise((resolve52) => setTimeout(resolve52, delay));
|
|
4066
|
+
retries++;
|
|
4067
|
+
continue;
|
|
4068
|
+
}
|
|
4069
|
+
}
|
|
4070
|
+
if (error?.code && ["ENOSPC", "EDQUOT"].includes(error.code)) {
|
|
4071
|
+
throw new Error(`Disk quota exceeded or no space left on device: ${error.message}`);
|
|
4072
|
+
}
|
|
4073
|
+
console.error("Debug: [writeToFile] Error writing to file:", error);
|
|
4074
|
+
throw error;
|
|
4075
|
+
} finally {
|
|
4076
|
+
if (fd !== undefined) {
|
|
4077
|
+
try {
|
|
4078
|
+
closeSync22(fd);
|
|
4079
|
+
} catch (err) {
|
|
4080
|
+
console.error("Debug: [writeToFile] Error closing file descriptor:", err);
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
} catch (err) {
|
|
4085
|
+
if (retries === maxRetries - 1) {
|
|
4086
|
+
const error = err;
|
|
4087
|
+
const errorMessage = typeof error.message === "string" ? error.message : "Unknown error";
|
|
4088
|
+
console.error("Debug: [writeToFile] Max retries reached. Final error:", errorMessage);
|
|
4089
|
+
throw err;
|
|
4090
|
+
}
|
|
4091
|
+
retries++;
|
|
4092
|
+
const delay = backoffDelay * 2 ** (retries - 1);
|
|
4093
|
+
await new Promise((resolve52) => setTimeout(resolve52, delay));
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
})();
|
|
4097
|
+
this.pendingOperations.push(operationPromise);
|
|
4098
|
+
const index = this.pendingOperations.length - 1;
|
|
4099
|
+
try {
|
|
4100
|
+
await operationPromise;
|
|
4101
|
+
} catch (err) {
|
|
4102
|
+
console.error("Debug: [writeToFile] Error in operation:", err);
|
|
4103
|
+
throw err;
|
|
4104
|
+
} finally {
|
|
4105
|
+
this.pendingOperations.splice(index, 1);
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
generateLogFilename() {
|
|
4109
|
+
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")) {
|
|
4110
|
+
return join5(this.config.logDirectory, `${this.name}.log`);
|
|
4111
|
+
}
|
|
4112
|
+
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") {
|
|
4113
|
+
return join5(this.config.logDirectory, `${this.name}.log`);
|
|
4114
|
+
}
|
|
4115
|
+
const date = new Date().toISOString().split("T")[0];
|
|
4116
|
+
return join5(this.config.logDirectory, `${this.name}-${date}.log`);
|
|
4117
|
+
}
|
|
4118
|
+
setupRotation() {
|
|
4119
|
+
if (isBrowserProcess22())
|
|
4120
|
+
return;
|
|
4121
|
+
if (!this.shouldWriteToFile())
|
|
4122
|
+
return;
|
|
4123
|
+
if (typeof this.config.rotation === "boolean")
|
|
4124
|
+
return;
|
|
4125
|
+
const config32 = this.config.rotation;
|
|
4126
|
+
let interval;
|
|
4127
|
+
switch (config32.frequency) {
|
|
4128
|
+
case "daily":
|
|
4129
|
+
interval = 86400000;
|
|
4130
|
+
break;
|
|
4131
|
+
case "weekly":
|
|
4132
|
+
interval = 604800000;
|
|
4133
|
+
break;
|
|
4134
|
+
case "monthly":
|
|
4135
|
+
interval = 2592000000;
|
|
4136
|
+
break;
|
|
4137
|
+
default:
|
|
4138
|
+
return;
|
|
4139
|
+
}
|
|
4140
|
+
this.rotationTimeout = setInterval(() => {
|
|
4141
|
+
this.rotateLog();
|
|
4142
|
+
}, interval);
|
|
4143
|
+
}
|
|
4144
|
+
setupKeyRotation() {
|
|
4145
|
+
if (!this.validateEncryptionConfig()) {
|
|
4146
|
+
console.error("Invalid encryption configuration detected during key rotation setup");
|
|
4147
|
+
return;
|
|
4148
|
+
}
|
|
4149
|
+
const rotation = this.config.rotation;
|
|
4150
|
+
const keyRotation = rotation.keyRotation;
|
|
4151
|
+
if (!keyRotation?.enabled) {
|
|
4152
|
+
return;
|
|
4153
|
+
}
|
|
4154
|
+
const rotationInterval = typeof keyRotation.interval === "number" ? keyRotation.interval : 60;
|
|
4155
|
+
const interval = Math.max(rotationInterval, 60) * 1000;
|
|
4156
|
+
this.keyRotationTimeout = setInterval(() => {
|
|
4157
|
+
this.rotateKeys().catch((error) => {
|
|
4158
|
+
console.error("Error rotating keys:", error);
|
|
4159
|
+
});
|
|
4160
|
+
}, interval);
|
|
4161
|
+
}
|
|
4162
|
+
async rotateKeys() {
|
|
4163
|
+
if (!this.validateEncryptionConfig()) {
|
|
4164
|
+
console.error("Invalid encryption configuration detected during key rotation");
|
|
4165
|
+
return;
|
|
4166
|
+
}
|
|
4167
|
+
const rotation = this.config.rotation;
|
|
4168
|
+
const keyRotation = rotation.keyRotation;
|
|
4169
|
+
const newKeyId = this.generateKeyId();
|
|
4170
|
+
const newKey = this.generateKey();
|
|
4171
|
+
this.currentKeyId = newKeyId;
|
|
4172
|
+
this.keys.set(newKeyId, newKey);
|
|
4173
|
+
this.encryptionKeys.set(newKeyId, {
|
|
4174
|
+
key: newKey,
|
|
4175
|
+
createdAt: new Date
|
|
4176
|
+
});
|
|
4177
|
+
const sortedKeys = Array.from(this.encryptionKeys.entries()).sort(([, a], [, b]) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
4178
|
+
const maxKeyCount = typeof keyRotation.maxKeys === "number" ? keyRotation.maxKeys : 1;
|
|
4179
|
+
const maxKeys = Math.max(1, maxKeyCount);
|
|
4180
|
+
if (sortedKeys.length > maxKeys) {
|
|
4181
|
+
for (const [keyId] of sortedKeys.slice(maxKeys)) {
|
|
4182
|
+
this.encryptionKeys.delete(keyId);
|
|
4183
|
+
this.keys.delete(keyId);
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
}
|
|
4187
|
+
generateKeyId() {
|
|
4188
|
+
return randomBytes22(16).toString("hex");
|
|
4189
|
+
}
|
|
4190
|
+
generateKey() {
|
|
4191
|
+
return randomBytes22(32);
|
|
4192
|
+
}
|
|
4193
|
+
getCurrentKey() {
|
|
4194
|
+
if (!this.currentKeyId) {
|
|
4195
|
+
throw new Error("Encryption is not properly initialized. Make sure encryption is enabled in the configuration.");
|
|
4196
|
+
}
|
|
4197
|
+
const key = this.keys.get(this.currentKeyId);
|
|
4198
|
+
if (!key) {
|
|
4199
|
+
throw new Error(`No key found for ID ${this.currentKeyId}. The encryption key may have been rotated or removed.`);
|
|
4200
|
+
}
|
|
4201
|
+
return { key, id: this.currentKeyId };
|
|
4202
|
+
}
|
|
4203
|
+
encrypt(data) {
|
|
4204
|
+
const { key } = this.getCurrentKey();
|
|
4205
|
+
const iv = randomBytes22(16);
|
|
4206
|
+
const cipher = createCipheriv22("aes-256-gcm", key, iv);
|
|
4207
|
+
const input = Buffer22.isBuffer(data) ? data : Buffer22.from(data, "utf8");
|
|
4208
|
+
const part1 = cipher.update(input);
|
|
4209
|
+
const part2 = cipher.final();
|
|
4210
|
+
const totalCipherLen = part1.length + part2.length;
|
|
4211
|
+
const authTag = cipher.getAuthTag();
|
|
4212
|
+
const out = Buffer22.allocUnsafe(16 + totalCipherLen + 16);
|
|
4213
|
+
iv.copy(out, 0);
|
|
4214
|
+
part1.copy(out, 16);
|
|
4215
|
+
part2.copy(out, 16 + part1.length);
|
|
4216
|
+
authTag.copy(out, 16 + totalCipherLen);
|
|
4217
|
+
return {
|
|
4218
|
+
encrypted: out,
|
|
4219
|
+
iv
|
|
4220
|
+
};
|
|
4221
|
+
}
|
|
4222
|
+
async compressData(data) {
|
|
4223
|
+
return new Promise((resolve52, reject) => {
|
|
4224
|
+
const gzip = createGzip22();
|
|
4225
|
+
const chunks = [];
|
|
4226
|
+
gzip.on("data", (chunk2) => chunks.push(chunk2));
|
|
4227
|
+
gzip.on("end", () => resolve52(Buffer22.from(Buffer22.concat(chunks))));
|
|
4228
|
+
gzip.on("error", reject);
|
|
4229
|
+
gzip.write(data);
|
|
4230
|
+
gzip.end();
|
|
4231
|
+
});
|
|
4232
|
+
}
|
|
4233
|
+
getEncryptionOptions() {
|
|
4234
|
+
if (!this.config.rotation || typeof this.config.rotation === "boolean" || !this.config.rotation.encrypt) {
|
|
4235
|
+
return {};
|
|
4236
|
+
}
|
|
4237
|
+
const defaultOptions = {
|
|
4238
|
+
algorithm: "aes-256-cbc",
|
|
4239
|
+
compress: false
|
|
4240
|
+
};
|
|
4241
|
+
if (typeof this.config.rotation.encrypt === "object") {
|
|
4242
|
+
const encryptConfig = this.config.rotation.encrypt;
|
|
4243
|
+
return {
|
|
4244
|
+
...defaultOptions,
|
|
4245
|
+
...encryptConfig
|
|
4246
|
+
};
|
|
4247
|
+
}
|
|
4248
|
+
return defaultOptions;
|
|
4249
|
+
}
|
|
4250
|
+
async rotateLog() {
|
|
4251
|
+
if (isBrowserProcess22())
|
|
4252
|
+
return;
|
|
4253
|
+
if (!this.shouldWriteToFile())
|
|
4254
|
+
return;
|
|
4255
|
+
const stats = await stat22(this.currentLogFile).catch(() => null);
|
|
4256
|
+
if (!stats)
|
|
4257
|
+
return;
|
|
4258
|
+
const config32 = this.config.rotation;
|
|
4259
|
+
if (typeof config32 === "boolean")
|
|
4260
|
+
return;
|
|
4261
|
+
if (config32.maxSize && stats.size >= config32.maxSize) {
|
|
4262
|
+
const oldFile = this.currentLogFile;
|
|
4263
|
+
const newFile = this.generateLogFilename();
|
|
4264
|
+
if (this.name.includes("rotation-load-test") || this.name === "failed-rotation-test") {
|
|
4265
|
+
const files = await readdir22(this.config.logDirectory);
|
|
4266
|
+
const rotatedFiles = files.filter((f) => f.startsWith(this.name) && /\.log\.\d+$/.test(f)).sort((a, b) => {
|
|
4267
|
+
const numA = Number.parseInt(a.match(/\.log\.(\d+)$/)?.[1] || "0");
|
|
4268
|
+
const numB = Number.parseInt(b.match(/\.log\.(\d+)$/)?.[1] || "0");
|
|
4269
|
+
return numB - numA;
|
|
4270
|
+
});
|
|
4271
|
+
const nextNum = rotatedFiles.length > 0 ? Number.parseInt(rotatedFiles[0].match(/\.log\.(\d+)$/)?.[1] || "0") + 1 : 1;
|
|
4272
|
+
const rotatedFile = `${oldFile}.${nextNum}`;
|
|
4273
|
+
if (await stat22(oldFile).catch(() => null)) {
|
|
4274
|
+
try {
|
|
4275
|
+
await rename22(oldFile, rotatedFile);
|
|
4276
|
+
if (config32.compress) {
|
|
4277
|
+
try {
|
|
4278
|
+
const compressedPath = `${rotatedFile}.gz`;
|
|
4279
|
+
await this.compressLogFile(rotatedFile, compressedPath);
|
|
4280
|
+
await unlink22(rotatedFile);
|
|
4281
|
+
} catch (err) {
|
|
4282
|
+
console.error("Error compressing rotated file:", err);
|
|
4283
|
+
}
|
|
4284
|
+
}
|
|
4285
|
+
if (rotatedFiles.length === 0 && !files.some((f) => f.endsWith(".log.1"))) {
|
|
4286
|
+
try {
|
|
4287
|
+
const backupPath = `${oldFile}.1`;
|
|
4288
|
+
await writeFile22(backupPath, "");
|
|
4289
|
+
} catch (err) {
|
|
4290
|
+
console.error("Error creating backup file:", err);
|
|
4291
|
+
}
|
|
4292
|
+
}
|
|
4293
|
+
} catch (err) {
|
|
4294
|
+
console.error(`Error during rotation: ${err instanceof Error ? err.message : String(err)}`);
|
|
4295
|
+
}
|
|
4296
|
+
}
|
|
4297
|
+
} else {
|
|
4298
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
4299
|
+
const rotatedFile = oldFile.replace(/\.log$/, `-${timestamp}.log`);
|
|
4300
|
+
if (await stat22(oldFile).catch(() => null)) {
|
|
4301
|
+
await rename22(oldFile, rotatedFile);
|
|
4302
|
+
}
|
|
4303
|
+
}
|
|
4304
|
+
this.currentLogFile = newFile;
|
|
4305
|
+
if (config32.maxFiles) {
|
|
4306
|
+
const files = await readdir22(this.config.logDirectory);
|
|
4307
|
+
const logFiles = files.filter((f) => f.startsWith(this.name)).sort((a, b) => b.localeCompare(a));
|
|
4308
|
+
for (const file of logFiles.slice(config32.maxFiles)) {
|
|
4309
|
+
await unlink22(join5(this.config.logDirectory, file));
|
|
4310
|
+
}
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
async compressLogFile(inputPath, outputPath) {
|
|
4315
|
+
const readStream = createReadStream22(inputPath);
|
|
4316
|
+
const writeStream = createWriteStream22(outputPath);
|
|
4317
|
+
const gzip = createGzip22();
|
|
4318
|
+
await pipeline22(readStream, gzip, writeStream);
|
|
4319
|
+
}
|
|
4320
|
+
async handleFingersCrossedBuffer(level, formattedEntry) {
|
|
4321
|
+
if (!this.fingersCrossedConfig)
|
|
4322
|
+
return;
|
|
4323
|
+
if (this.shouldActivateFingersCrossed(level) && !this.isActivated) {
|
|
4324
|
+
this.isActivated = true;
|
|
4325
|
+
for (const entry of this.logBuffer) {
|
|
4326
|
+
const formattedBufferedEntry = await this.formatter.format(entry);
|
|
4327
|
+
if (this.shouldWriteToFile())
|
|
4328
|
+
await this.writeToFile(formattedBufferedEntry);
|
|
4329
|
+
console.log(formattedBufferedEntry);
|
|
4330
|
+
}
|
|
4331
|
+
if (this.fingersCrossedConfig.stopBuffering)
|
|
4332
|
+
this.logBuffer = [];
|
|
4333
|
+
}
|
|
4334
|
+
if (this.isActivated) {
|
|
4335
|
+
if (this.shouldWriteToFile())
|
|
4336
|
+
await this.writeToFile(formattedEntry);
|
|
4337
|
+
console.log(formattedEntry);
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
shouldLog(level) {
|
|
4341
|
+
if (!this.enabled)
|
|
4342
|
+
return false;
|
|
4343
|
+
const levels = {
|
|
4344
|
+
debug: 0,
|
|
4345
|
+
info: 1,
|
|
4346
|
+
success: 2,
|
|
4347
|
+
warning: 3,
|
|
4348
|
+
error: 4
|
|
4349
|
+
};
|
|
4350
|
+
return levels[level] >= levels[this.config.level];
|
|
4351
|
+
}
|
|
4352
|
+
async flushPendingWrites() {
|
|
4353
|
+
await Promise.all(this.pendingOperations.map((op) => {
|
|
4354
|
+
if (op instanceof Promise) {
|
|
4355
|
+
return op.catch((err) => {
|
|
4356
|
+
console.error("Error in pending write operation:", err);
|
|
4357
|
+
});
|
|
4358
|
+
}
|
|
4359
|
+
return Promise.resolve();
|
|
4360
|
+
}));
|
|
4361
|
+
if (existsSync42(this.currentLogFile)) {
|
|
4362
|
+
try {
|
|
4363
|
+
const fd = openSync22(this.currentLogFile, "r+");
|
|
4364
|
+
fsyncSync22(fd);
|
|
4365
|
+
closeSync22(fd);
|
|
4366
|
+
} catch (error) {
|
|
4367
|
+
console.error(`Error flushing file: ${error}`);
|
|
4368
|
+
}
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4371
|
+
async destroy() {
|
|
4372
|
+
if (this.rotationTimeout)
|
|
4373
|
+
clearInterval(this.rotationTimeout);
|
|
4374
|
+
if (this.keyRotationTimeout)
|
|
4375
|
+
clearInterval(this.keyRotationTimeout);
|
|
4376
|
+
this.timers.clear();
|
|
4377
|
+
for (const op of this.pendingOperations) {
|
|
4378
|
+
if (typeof op.cancel === "function") {
|
|
4379
|
+
op.cancel();
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4382
|
+
return (async () => {
|
|
4383
|
+
if (this.pendingOperations.length > 0) {
|
|
4384
|
+
try {
|
|
4385
|
+
await Promise.allSettled(this.pendingOperations);
|
|
4386
|
+
} catch (err) {
|
|
4387
|
+
console.error("Error waiting for pending operations:", err);
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
if (!isBrowserProcess22() && this.config.rotation && typeof this.config.rotation !== "boolean" && this.config.rotation.compress) {
|
|
4391
|
+
try {
|
|
4392
|
+
const files = await readdir22(this.config.logDirectory);
|
|
4393
|
+
const tempFiles = files.filter((f) => (f.includes("temp") || f.includes(".tmp")) && f.includes(this.name));
|
|
4394
|
+
for (const tempFile of tempFiles) {
|
|
4395
|
+
try {
|
|
4396
|
+
await unlink22(join5(this.config.logDirectory, tempFile));
|
|
4397
|
+
} catch (err) {
|
|
4398
|
+
console.error(`Failed to delete temp file ${tempFile}:`, err);
|
|
4399
|
+
}
|
|
4400
|
+
}
|
|
4401
|
+
} catch (err) {
|
|
4402
|
+
console.error("Error cleaning up temporary files:", err);
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4405
|
+
})();
|
|
4406
|
+
}
|
|
4407
|
+
getCurrentLogFilePath() {
|
|
4408
|
+
return this.currentLogFile;
|
|
4409
|
+
}
|
|
4410
|
+
formatTag(name) {
|
|
4411
|
+
if (!name)
|
|
4412
|
+
return "";
|
|
4413
|
+
return `${this.tagFormat.prefix}${name}${this.tagFormat.suffix}`;
|
|
4414
|
+
}
|
|
4415
|
+
formatFileTimestamp(date) {
|
|
4416
|
+
return `[${date.toISOString()}]`;
|
|
4417
|
+
}
|
|
4418
|
+
formatConsoleTimestamp(date) {
|
|
4419
|
+
return this.shouldStyleConsole() ? styles22.gray(date.toLocaleTimeString()) : date.toLocaleTimeString();
|
|
4420
|
+
}
|
|
4421
|
+
shouldStyleConsole() {
|
|
4422
|
+
if (!this.fancy || isBrowserProcess22())
|
|
4423
|
+
return false;
|
|
4424
|
+
const noColor = typeof process11.env.NO_COLOR !== "undefined";
|
|
4425
|
+
const forceColorDisabled = process11.env.FORCE_COLOR === "0";
|
|
4426
|
+
if (noColor || forceColorDisabled)
|
|
4427
|
+
return false;
|
|
4428
|
+
const hasTTY = typeof process11.stderr !== "undefined" && process11.stderr.isTTY || typeof process11.stdout !== "undefined" && process11.stdout.isTTY;
|
|
4429
|
+
return !!hasTTY;
|
|
4430
|
+
}
|
|
4431
|
+
formatConsoleMessage(parts) {
|
|
4432
|
+
const { timestamp, icon = "", tag = "", message, level, showTimestamp = true } = parts;
|
|
4433
|
+
const stripAnsi = (str) => str.replace(this.ANSI_PATTERN, "");
|
|
4434
|
+
if (!this.fancy) {
|
|
4435
|
+
const components = [];
|
|
4436
|
+
if (showTimestamp)
|
|
4437
|
+
components.push(timestamp);
|
|
4438
|
+
if (level === "warning")
|
|
4439
|
+
components.push("WARN");
|
|
4440
|
+
else if (level === "error")
|
|
4441
|
+
components.push("ERROR");
|
|
4442
|
+
else if (icon)
|
|
4443
|
+
components.push(icon.replace(/[^\p{L}\p{N}\p{P}\p{Z}]/gu, ""));
|
|
4444
|
+
if (tag)
|
|
4445
|
+
components.push(tag.replace(/[[\]]/g, ""));
|
|
4446
|
+
components.push(message);
|
|
4447
|
+
return components.join(" ");
|
|
4448
|
+
}
|
|
4449
|
+
const terminalWidth = process11.stdout.columns || 120;
|
|
4450
|
+
let mainPart = "";
|
|
4451
|
+
if (level === "warning" || level === "error") {
|
|
4452
|
+
mainPart = `${icon} ${message}`;
|
|
4453
|
+
} else if (level === "info" || level === "success") {
|
|
4454
|
+
mainPart = `${icon} ${tag} ${message}`;
|
|
4455
|
+
} else {
|
|
4456
|
+
mainPart = `${icon} ${tag} ${styles22.cyan(message)}`;
|
|
4457
|
+
}
|
|
4458
|
+
if (!showTimestamp) {
|
|
4459
|
+
return mainPart.trim();
|
|
4460
|
+
}
|
|
4461
|
+
const visibleMainPartLength = stripAnsi(mainPart).trim().length;
|
|
4462
|
+
const visibleTimestampLength = stripAnsi(timestamp).length;
|
|
4463
|
+
const padding = Math.max(1, terminalWidth - 2 - visibleMainPartLength - visibleTimestampLength);
|
|
4464
|
+
return `${mainPart.trim()}${" ".repeat(padding)}${timestamp}`;
|
|
4465
|
+
}
|
|
4466
|
+
formatMessage(message, args) {
|
|
4467
|
+
if (args.length === 1 && Array.isArray(args[0])) {
|
|
4468
|
+
return message.replace(/\{(\d+)\}/g, (match, index) => {
|
|
4469
|
+
const position = Number.parseInt(index, 10);
|
|
4470
|
+
return position < args[0].length ? String(args[0][position]) : match;
|
|
4471
|
+
});
|
|
4472
|
+
}
|
|
4473
|
+
const formatRegex = /%([sdijfo%])/g;
|
|
4474
|
+
let argIndex = 0;
|
|
4475
|
+
let formattedMessage = message.replace(formatRegex, (match, type) => {
|
|
4476
|
+
if (type === "%")
|
|
4477
|
+
return "%";
|
|
4478
|
+
if (argIndex >= args.length)
|
|
4479
|
+
return match;
|
|
4480
|
+
const arg = args[argIndex++];
|
|
4481
|
+
switch (type) {
|
|
4482
|
+
case "s":
|
|
4483
|
+
return String(arg);
|
|
4484
|
+
case "d":
|
|
4485
|
+
case "i":
|
|
4486
|
+
return Number(arg).toString();
|
|
4487
|
+
case "j":
|
|
4488
|
+
case "o":
|
|
4489
|
+
return JSON.stringify(arg, null, 2);
|
|
4490
|
+
default:
|
|
4491
|
+
return match;
|
|
4492
|
+
}
|
|
4493
|
+
});
|
|
4494
|
+
if (argIndex < args.length) {
|
|
4495
|
+
formattedMessage += ` ${args.slice(argIndex).map((arg) => typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg)).join(" ")}`;
|
|
4496
|
+
}
|
|
4497
|
+
return formattedMessage;
|
|
4498
|
+
}
|
|
4499
|
+
formatMarkdown(input) {
|
|
4500
|
+
if (!input)
|
|
4501
|
+
return input;
|
|
4502
|
+
let out = input;
|
|
4503
|
+
out = out.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, text, url) => {
|
|
4504
|
+
const label = styles22.underline(styles22.blue(text));
|
|
4505
|
+
const absFile = this.toAbsoluteFilePath(url);
|
|
4506
|
+
if (absFile && this.shouldStyleConsole() && this.supportsHyperlinks()) {
|
|
4507
|
+
const href = `file://${encodeURI(absFile)}`;
|
|
4508
|
+
const OSC = "\x1B]8;;";
|
|
4509
|
+
const ST = "\x1B\\";
|
|
4510
|
+
return `${OSC}${href}${ST}${label}${OSC}${ST}`;
|
|
4511
|
+
}
|
|
4512
|
+
if (this.shouldStyleConsole() && this.supportsHyperlinks()) {
|
|
4513
|
+
const OSC = "\x1B]8;;";
|
|
4514
|
+
const ST = "\x1B\\";
|
|
4515
|
+
return `${OSC}${url}${ST}${label}${OSC}${ST}`;
|
|
4516
|
+
}
|
|
4517
|
+
return label;
|
|
4518
|
+
});
|
|
4519
|
+
out = out.replace(/`([^`]+)`/g, (_, m) => styles22.bgGray(m));
|
|
4520
|
+
out = out.replace(/\*\*([^*]+)\*\*/g, (_, m) => styles22.bold(m));
|
|
4521
|
+
out = out.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, (_, m) => styles22.italic(m));
|
|
4522
|
+
out = out.replace(/(?<!_)_([^_]+)_(?!_)/g, (_, m) => styles22.italic(m));
|
|
4523
|
+
out = out.replace(/~([^~]+)~/g, (_, m) => styles22.strikethrough(m));
|
|
4524
|
+
return out;
|
|
4525
|
+
}
|
|
4526
|
+
supportsHyperlinks() {
|
|
4527
|
+
if (isBrowserProcess22())
|
|
4528
|
+
return false;
|
|
4529
|
+
const env = process11.env;
|
|
4530
|
+
if (!env)
|
|
4531
|
+
return false;
|
|
4532
|
+
if (env.TERM_PROGRAM === "iTerm.app" || env.TERM_PROGRAM === "vscode" || env.TERM_PROGRAM === "WezTerm")
|
|
4533
|
+
return true;
|
|
4534
|
+
if (env.WT_SESSION)
|
|
4535
|
+
return true;
|
|
4536
|
+
if (env.TERM === "xterm-kitty")
|
|
4537
|
+
return true;
|
|
4538
|
+
const vte = env.VTE_VERSION ? Number.parseInt(env.VTE_VERSION, 10) : 0;
|
|
4539
|
+
if (!Number.isNaN(vte) && vte >= 5000)
|
|
4540
|
+
return true;
|
|
4541
|
+
return false;
|
|
4542
|
+
}
|
|
4543
|
+
toAbsoluteFilePath(input) {
|
|
4544
|
+
try {
|
|
4545
|
+
let p = input;
|
|
4546
|
+
if (p.startsWith("file://")) {
|
|
4547
|
+
p = p.replace(/^file:\/\//, "");
|
|
4548
|
+
}
|
|
4549
|
+
if (p.startsWith("~")) {
|
|
4550
|
+
const home = process11.env.HOME || "";
|
|
4551
|
+
if (home)
|
|
4552
|
+
p = p.replace(/^~(?=$|\/)/, home);
|
|
4553
|
+
}
|
|
4554
|
+
if (isAbsolute(p) || p.startsWith("./") || p.startsWith("../")) {
|
|
4555
|
+
p = resolve6(p);
|
|
4556
|
+
} else {
|
|
4557
|
+
return null;
|
|
4558
|
+
}
|
|
4559
|
+
return existsSync42(p) ? p : null;
|
|
4560
|
+
} catch {
|
|
4561
|
+
return null;
|
|
4562
|
+
}
|
|
4563
|
+
}
|
|
4564
|
+
buildOutputTexts(input) {
|
|
4565
|
+
const consoleText = this.shouldStyleConsole() ? this.formatMarkdown(input) : input;
|
|
4566
|
+
const fileText = input.replace(this.ANSI_PATTERN, "");
|
|
4567
|
+
return { consoleText, fileText };
|
|
4568
|
+
}
|
|
4569
|
+
async log(level, message, ...args) {
|
|
4570
|
+
const timestamp = new Date;
|
|
4571
|
+
const consoleTime = this.formatConsoleTimestamp(timestamp);
|
|
4572
|
+
const fileTime = this.formatFileTimestamp(timestamp);
|
|
4573
|
+
let formattedMessage;
|
|
4574
|
+
let errorStack;
|
|
4575
|
+
if (message instanceof Error) {
|
|
4576
|
+
formattedMessage = message.message;
|
|
4577
|
+
errorStack = message.stack;
|
|
4578
|
+
} else {
|
|
4579
|
+
formattedMessage = this.formatMessage(message, args);
|
|
4580
|
+
}
|
|
4581
|
+
const { consoleText: baseConsoleText, fileText } = this.buildOutputTexts(formattedMessage);
|
|
4582
|
+
if (this.shouldStyleConsole()) {
|
|
4583
|
+
const icon = levelIcons22[level];
|
|
4584
|
+
const tag = this.options.showTags !== false && this.name ? styles22.gray(this.formatTag(this.name)) : "";
|
|
4585
|
+
let consoleMessage;
|
|
4586
|
+
switch (level) {
|
|
4587
|
+
case "debug":
|
|
4588
|
+
consoleMessage = this.formatConsoleMessage({
|
|
4589
|
+
timestamp: consoleTime,
|
|
4590
|
+
icon,
|
|
4591
|
+
tag,
|
|
4592
|
+
message: styles22.gray(baseConsoleText),
|
|
4593
|
+
level
|
|
4594
|
+
});
|
|
4595
|
+
console.error(consoleMessage);
|
|
4596
|
+
break;
|
|
4597
|
+
case "info":
|
|
4598
|
+
consoleMessage = this.formatConsoleMessage({
|
|
4599
|
+
timestamp: consoleTime,
|
|
4600
|
+
icon,
|
|
4601
|
+
tag,
|
|
4602
|
+
message: baseConsoleText,
|
|
4603
|
+
level
|
|
4604
|
+
});
|
|
4605
|
+
console.error(consoleMessage);
|
|
4606
|
+
break;
|
|
4607
|
+
case "success":
|
|
4608
|
+
consoleMessage = this.formatConsoleMessage({
|
|
4609
|
+
timestamp: consoleTime,
|
|
4610
|
+
icon,
|
|
4611
|
+
tag,
|
|
4612
|
+
message: styles22.green(baseConsoleText),
|
|
4613
|
+
level
|
|
4614
|
+
});
|
|
4615
|
+
console.error(consoleMessage);
|
|
4616
|
+
break;
|
|
4617
|
+
case "warning":
|
|
4618
|
+
consoleMessage = this.formatConsoleMessage({
|
|
4619
|
+
timestamp: consoleTime,
|
|
4620
|
+
icon,
|
|
4621
|
+
tag,
|
|
4622
|
+
message: baseConsoleText,
|
|
4623
|
+
level
|
|
4624
|
+
});
|
|
4625
|
+
console.warn(consoleMessage);
|
|
4626
|
+
break;
|
|
4627
|
+
case "error":
|
|
4628
|
+
consoleMessage = this.formatConsoleMessage({
|
|
4629
|
+
timestamp: consoleTime,
|
|
4630
|
+
icon,
|
|
4631
|
+
tag,
|
|
4632
|
+
message: baseConsoleText,
|
|
4633
|
+
level
|
|
4634
|
+
});
|
|
4635
|
+
console.error(consoleMessage);
|
|
4636
|
+
if (errorStack) {
|
|
4637
|
+
const stackLines = errorStack.split(`
|
|
4638
|
+
`);
|
|
4639
|
+
for (const line of stackLines) {
|
|
4640
|
+
if (line.trim() && !line.includes(formattedMessage)) {
|
|
4641
|
+
console.error(this.formatConsoleMessage({
|
|
4642
|
+
timestamp: consoleTime,
|
|
4643
|
+
message: styles22.gray(` ${line}`),
|
|
4644
|
+
level,
|
|
4645
|
+
showTimestamp: false
|
|
4646
|
+
}));
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
break;
|
|
4651
|
+
}
|
|
4652
|
+
} else if (!isBrowserProcess22()) {
|
|
4653
|
+
console.error(`${fileTime} ${this.environment}.${level.toUpperCase()}: ${formattedMessage}`);
|
|
4654
|
+
if (errorStack) {
|
|
4655
|
+
console.error(errorStack);
|
|
4656
|
+
}
|
|
4657
|
+
}
|
|
4658
|
+
if (!this.shouldLog(level))
|
|
4659
|
+
return;
|
|
4660
|
+
let logEntry = `${fileTime} ${this.environment}.${level.toUpperCase()}: ${fileText}
|
|
4661
|
+
`;
|
|
4662
|
+
if (errorStack) {
|
|
4663
|
+
logEntry += `${errorStack}
|
|
4664
|
+
`;
|
|
4665
|
+
}
|
|
4666
|
+
logEntry = logEntry.replace(this.ANSI_PATTERN, "");
|
|
4667
|
+
if (this.shouldWriteToFile())
|
|
4668
|
+
await this.writeToFile(logEntry);
|
|
4669
|
+
}
|
|
4670
|
+
progress(total, initialMessage = "") {
|
|
4671
|
+
const noop = {
|
|
4672
|
+
update: (_current, _message) => {},
|
|
4673
|
+
finish: (_message) => {},
|
|
4674
|
+
interrupt: (_message, _level) => {}
|
|
4675
|
+
};
|
|
4676
|
+
if (!this.enabled)
|
|
4677
|
+
return noop;
|
|
4678
|
+
const barLength = 30;
|
|
4679
|
+
this.activeProgressBar = {
|
|
4680
|
+
total: Math.max(1, total || 1),
|
|
4681
|
+
current: 0,
|
|
4682
|
+
message: initialMessage || "",
|
|
4683
|
+
barLength,
|
|
4684
|
+
lastRenderedLine: ""
|
|
4685
|
+
};
|
|
4686
|
+
if (this.shouldStyleConsole() && !isBrowserProcess22() && process11.stdout.isTTY) {
|
|
4687
|
+
this.renderProgressBar(this.activeProgressBar);
|
|
4688
|
+
}
|
|
4689
|
+
const update = (current, message) => {
|
|
4690
|
+
if (!this.enabled || !this.activeProgressBar)
|
|
4691
|
+
return;
|
|
4692
|
+
this.activeProgressBar.current = Math.min(Math.max(0, current), this.activeProgressBar.total);
|
|
4693
|
+
if (message !== undefined)
|
|
4694
|
+
this.activeProgressBar.message = message;
|
|
4695
|
+
if (this.shouldStyleConsole() && !isBrowserProcess22() && process11.stdout.isTTY)
|
|
4696
|
+
this.renderProgressBar(this.activeProgressBar);
|
|
4697
|
+
};
|
|
4698
|
+
const finish = (message) => {
|
|
4699
|
+
if (!this.activeProgressBar)
|
|
4700
|
+
return;
|
|
4701
|
+
this.finishProgressBar(this.activeProgressBar, message);
|
|
4702
|
+
};
|
|
4703
|
+
const interrupt = (message, level = "info") => {
|
|
4704
|
+
if (!isBrowserProcess22() && process11.stdout.isTTY)
|
|
4705
|
+
process11.stdout.write(`
|
|
4706
|
+
`);
|
|
4707
|
+
const method = level === "warning" ? "warn" : level;
|
|
4708
|
+
this[method](message);
|
|
4709
|
+
if (this.activeProgressBar && this.shouldStyleConsole() && !isBrowserProcess22() && process11.stdout.isTTY)
|
|
4710
|
+
this.renderProgressBar(this.activeProgressBar);
|
|
4711
|
+
};
|
|
4712
|
+
return { update, finish, interrupt };
|
|
4713
|
+
}
|
|
4714
|
+
time(label) {
|
|
4715
|
+
const start = performance.now();
|
|
4716
|
+
if (this.shouldStyleConsole()) {
|
|
4717
|
+
const tag = this.options.showTags !== false && this.name ? styles22.gray(this.formatTag(this.name)) : "";
|
|
4718
|
+
const consoleTime = this.formatConsoleTimestamp(new Date);
|
|
4719
|
+
console.error(this.formatConsoleMessage({
|
|
4720
|
+
timestamp: consoleTime,
|
|
4721
|
+
icon: styles22.blue("◐"),
|
|
4722
|
+
tag,
|
|
4723
|
+
message: `${styles22.cyan(label)}...`
|
|
4724
|
+
}));
|
|
4725
|
+
}
|
|
4726
|
+
return async (metadata) => {
|
|
4727
|
+
if (!this.enabled)
|
|
4728
|
+
return;
|
|
4729
|
+
const end = performance.now();
|
|
4730
|
+
const elapsed = Math.round(end - start);
|
|
4731
|
+
const completionMessage = `${label} completed in ${elapsed}ms`;
|
|
4732
|
+
const timestamp = new Date;
|
|
4733
|
+
const consoleTime = this.formatConsoleTimestamp(timestamp);
|
|
4734
|
+
const fileTime = this.formatFileTimestamp(timestamp);
|
|
4735
|
+
let logEntry = `${fileTime} ${this.environment}.INFO: ${completionMessage}`;
|
|
4736
|
+
if (metadata) {
|
|
4737
|
+
logEntry += ` ${JSON.stringify(metadata)}`;
|
|
4738
|
+
}
|
|
4739
|
+
logEntry += `
|
|
4740
|
+
`;
|
|
4741
|
+
logEntry = logEntry.replace(this.ANSI_PATTERN, "");
|
|
4742
|
+
if (this.shouldStyleConsole()) {
|
|
4743
|
+
const tag = this.options.showTags !== false && this.name ? styles22.gray(this.formatTag(this.name)) : "";
|
|
4744
|
+
console.error(this.formatConsoleMessage({
|
|
4745
|
+
timestamp: consoleTime,
|
|
4746
|
+
icon: styles22.green("✓"),
|
|
4747
|
+
tag,
|
|
4748
|
+
message: `${completionMessage}${metadata ? ` ${JSON.stringify(metadata)}` : ""}`
|
|
4749
|
+
}));
|
|
4750
|
+
} else if (!isBrowserProcess22()) {
|
|
4751
|
+
console.error(logEntry.trim());
|
|
4752
|
+
}
|
|
4753
|
+
if (this.shouldWriteToFile())
|
|
4754
|
+
await this.writeToFile(logEntry);
|
|
4755
|
+
};
|
|
4756
|
+
}
|
|
4757
|
+
async debug(message, ...args) {
|
|
4758
|
+
await this.log("debug", message, ...args);
|
|
4759
|
+
}
|
|
4760
|
+
async info(message, ...args) {
|
|
4761
|
+
await this.log("info", message, ...args);
|
|
4762
|
+
}
|
|
4763
|
+
async success(message, ...args) {
|
|
4764
|
+
await this.log("success", message, ...args);
|
|
4765
|
+
}
|
|
4766
|
+
async warn(message, ...args) {
|
|
4767
|
+
await this.log("warning", message, ...args);
|
|
4768
|
+
}
|
|
4769
|
+
async error(message, ...args) {
|
|
4770
|
+
await this.log("error", message, ...args);
|
|
4771
|
+
}
|
|
4772
|
+
validateEncryptionConfig() {
|
|
4773
|
+
if (!this.config.rotation)
|
|
4774
|
+
return false;
|
|
4775
|
+
if (typeof this.config.rotation === "boolean")
|
|
4776
|
+
return false;
|
|
4777
|
+
const rotation = this.config.rotation;
|
|
4778
|
+
const { encrypt } = rotation;
|
|
4779
|
+
return !!encrypt;
|
|
4780
|
+
}
|
|
4781
|
+
async only(fn) {
|
|
4782
|
+
if (!this.enabled)
|
|
4783
|
+
return;
|
|
4784
|
+
return await fn();
|
|
4785
|
+
}
|
|
4786
|
+
isEnabled() {
|
|
4787
|
+
return this.enabled;
|
|
4788
|
+
}
|
|
4789
|
+
setEnabled(enabled) {
|
|
4790
|
+
this.enabled = enabled;
|
|
4791
|
+
}
|
|
4792
|
+
extend(namespace) {
|
|
4793
|
+
const childName = `${this.name}:${namespace}`;
|
|
4794
|
+
const childLogger = new Logger22(childName, {
|
|
4795
|
+
...this.options,
|
|
4796
|
+
logDirectory: this.config.logDirectory,
|
|
4797
|
+
level: this.config.level,
|
|
4798
|
+
format: this.config.format,
|
|
4799
|
+
rotation: typeof this.config.rotation === "boolean" ? undefined : this.config.rotation,
|
|
4800
|
+
timestamp: typeof this.config.timestamp === "boolean" ? undefined : this.config.timestamp
|
|
4801
|
+
});
|
|
4802
|
+
this.subLoggers.add(childLogger);
|
|
4803
|
+
return childLogger;
|
|
4804
|
+
}
|
|
4805
|
+
createReadStream() {
|
|
4806
|
+
if (isBrowserProcess22())
|
|
4807
|
+
throw new Error("createReadStream is not supported in browser environments");
|
|
4808
|
+
if (!existsSync42(this.currentLogFile))
|
|
4809
|
+
throw new Error(`Log file does not exist: ${this.currentLogFile}`);
|
|
4810
|
+
return createReadStream22(this.currentLogFile, { encoding: "utf8" });
|
|
4811
|
+
}
|
|
4812
|
+
async decrypt(data) {
|
|
4813
|
+
if (!this.validateEncryptionConfig())
|
|
4814
|
+
throw new Error("Encryption is not configured");
|
|
4815
|
+
const encryptionConfig = this.config.rotation;
|
|
4816
|
+
if (!encryptionConfig.encrypt || typeof encryptionConfig.encrypt === "boolean")
|
|
4817
|
+
throw new Error("Invalid encryption configuration");
|
|
4818
|
+
if (!this.currentKeyId || !this.keys.has(this.currentKeyId))
|
|
4819
|
+
throw new Error("No valid encryption key available");
|
|
4820
|
+
const key = this.keys.get(this.currentKeyId);
|
|
4821
|
+
try {
|
|
4822
|
+
const encryptedData = Buffer22.isBuffer(data) ? data : Buffer22.from(data, "base64");
|
|
4823
|
+
const iv = encryptedData.subarray(0, 16);
|
|
4824
|
+
const authTag = encryptedData.subarray(encryptedData.length - 16);
|
|
4825
|
+
const ciphertext = encryptedData.subarray(16, encryptedData.length - 16);
|
|
4826
|
+
const decipher = createDecipheriv22("aes-256-gcm", key, iv);
|
|
4827
|
+
decipher.setAuthTag(authTag);
|
|
4828
|
+
const d1 = decipher.update(ciphertext);
|
|
4829
|
+
const d2 = decipher.final();
|
|
4830
|
+
const totalLen = d1.length + d2.length;
|
|
4831
|
+
const out = Buffer22.allocUnsafe(totalLen);
|
|
4832
|
+
d1.copy(out, 0);
|
|
4833
|
+
d2.copy(out, d1.length);
|
|
4834
|
+
return out.toString("utf8");
|
|
4835
|
+
} catch (err) {
|
|
4836
|
+
throw new Error(`Decryption failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
4837
|
+
}
|
|
4838
|
+
}
|
|
4839
|
+
getLevel() {
|
|
4840
|
+
return this.config.level;
|
|
4841
|
+
}
|
|
4842
|
+
getLogDirectory() {
|
|
4843
|
+
return this.config.logDirectory;
|
|
4844
|
+
}
|
|
4845
|
+
getFormat() {
|
|
4846
|
+
return this.config.format;
|
|
4847
|
+
}
|
|
4848
|
+
getRotationConfig() {
|
|
4849
|
+
return this.config.rotation;
|
|
4850
|
+
}
|
|
4851
|
+
isBrowserMode() {
|
|
4852
|
+
return isBrowserProcess22();
|
|
4853
|
+
}
|
|
4854
|
+
isServerMode() {
|
|
4855
|
+
return !isBrowserProcess22();
|
|
4856
|
+
}
|
|
4857
|
+
setTestEncryptionKey(keyId, key) {
|
|
4858
|
+
this.currentKeyId = keyId;
|
|
4859
|
+
this.keys.set(keyId, key);
|
|
4860
|
+
}
|
|
4861
|
+
getTestCurrentKey() {
|
|
4862
|
+
if (!this.currentKeyId || !this.keys.has(this.currentKeyId)) {
|
|
4863
|
+
return null;
|
|
4864
|
+
}
|
|
4865
|
+
return {
|
|
4866
|
+
id: this.currentKeyId,
|
|
4867
|
+
key: this.keys.get(this.currentKeyId)
|
|
4868
|
+
};
|
|
4869
|
+
}
|
|
4870
|
+
getConfig() {
|
|
4871
|
+
return this.config;
|
|
4872
|
+
}
|
|
4873
|
+
async box(message) {
|
|
4874
|
+
if (!this.enabled)
|
|
4875
|
+
return;
|
|
4876
|
+
const timestamp = new Date;
|
|
4877
|
+
const consoleTime = this.formatConsoleTimestamp(timestamp);
|
|
4878
|
+
const fileTime = this.formatFileTimestamp(timestamp);
|
|
4879
|
+
const { consoleText, fileText } = this.buildOutputTexts(message);
|
|
4880
|
+
if (this.shouldStyleConsole()) {
|
|
4881
|
+
const lines = consoleText.split(`
|
|
4882
|
+
`);
|
|
4883
|
+
const width = Math.max(...lines.map((line) => line.length)) + 2;
|
|
4884
|
+
const top = `┌${"─".repeat(width)}┐`;
|
|
4885
|
+
const bottom = `└${"─".repeat(width)}┘`;
|
|
4886
|
+
const boxedLines = lines.map((line) => {
|
|
4887
|
+
return this.formatConsoleMessage({
|
|
4888
|
+
timestamp: consoleTime,
|
|
4889
|
+
message: styles22.cyan(line),
|
|
4890
|
+
showTimestamp: false
|
|
4891
|
+
});
|
|
4892
|
+
});
|
|
4893
|
+
console.error(this.formatConsoleMessage({
|
|
4894
|
+
timestamp: consoleTime,
|
|
4895
|
+
message: styles22.cyan(top),
|
|
4896
|
+
showTimestamp: false
|
|
4897
|
+
}));
|
|
4898
|
+
boxedLines.forEach((line) => console.error(line));
|
|
4899
|
+
console.error(this.formatConsoleMessage({
|
|
4900
|
+
timestamp: consoleTime,
|
|
4901
|
+
message: styles22.cyan(bottom),
|
|
4902
|
+
showTimestamp: false
|
|
4903
|
+
}));
|
|
4904
|
+
} else if (!isBrowserProcess22()) {
|
|
4905
|
+
console.error(`${fileTime} ${this.environment}.INFO: [BOX] ${fileText}`);
|
|
4906
|
+
}
|
|
4907
|
+
const logEntry = `${fileTime} ${this.environment}.INFO: [BOX] ${fileText}
|
|
4908
|
+
`.replace(this.ANSI_PATTERN, "");
|
|
4909
|
+
if (this.shouldWriteToFile())
|
|
4910
|
+
await this.writeToFile(logEntry);
|
|
4911
|
+
}
|
|
4912
|
+
async prompt(message) {
|
|
4913
|
+
if (isBrowserProcess22()) {
|
|
4914
|
+
return Promise.resolve(true);
|
|
4915
|
+
}
|
|
4916
|
+
return new Promise((resolve52) => {
|
|
4917
|
+
console.error(`${styles22.cyan("?")} ${message} (y/n) `);
|
|
4918
|
+
const onData = (data) => {
|
|
4919
|
+
const input = data.toString().trim().toLowerCase();
|
|
4920
|
+
process11.stdin.removeListener("data", onData);
|
|
4921
|
+
try {
|
|
4922
|
+
if (typeof process11.stdin.setRawMode === "function") {
|
|
4923
|
+
process11.stdin.setRawMode(false);
|
|
4924
|
+
}
|
|
4925
|
+
} catch {}
|
|
4926
|
+
process11.stdin.pause();
|
|
4927
|
+
console.error("");
|
|
4928
|
+
resolve52(input === "y" || input === "yes");
|
|
4929
|
+
};
|
|
4930
|
+
try {
|
|
4931
|
+
if (typeof process11.stdin.setRawMode === "function") {
|
|
4932
|
+
process11.stdin.setRawMode(true);
|
|
4933
|
+
}
|
|
4934
|
+
} catch {}
|
|
4935
|
+
process11.stdin.resume();
|
|
4936
|
+
process11.stdin.once("data", onData);
|
|
4937
|
+
});
|
|
4938
|
+
}
|
|
4939
|
+
setFancy(enabled) {
|
|
4940
|
+
this.fancy = enabled;
|
|
4941
|
+
}
|
|
4942
|
+
isFancy() {
|
|
4943
|
+
return this.fancy;
|
|
4944
|
+
}
|
|
4945
|
+
pause() {
|
|
4946
|
+
this.enabled = false;
|
|
4947
|
+
}
|
|
4948
|
+
resume() {
|
|
4949
|
+
this.enabled = true;
|
|
4950
|
+
}
|
|
4951
|
+
async start(message, ...args) {
|
|
4952
|
+
if (!this.enabled)
|
|
4953
|
+
return;
|
|
4954
|
+
let formattedMessage = message;
|
|
4955
|
+
if (args && args.length > 0) {
|
|
4956
|
+
const formatRegex = /%([sdijfo%])/g;
|
|
4957
|
+
let argIndex = 0;
|
|
4958
|
+
formattedMessage = message.replace(formatRegex, (match, type) => {
|
|
4959
|
+
if (type === "%")
|
|
4960
|
+
return "%";
|
|
4961
|
+
if (argIndex >= args.length)
|
|
4962
|
+
return match;
|
|
4963
|
+
const arg = args[argIndex++];
|
|
4964
|
+
switch (type) {
|
|
4965
|
+
case "s":
|
|
4966
|
+
return String(arg);
|
|
4967
|
+
case "d":
|
|
4968
|
+
case "i":
|
|
4969
|
+
return Number(arg).toString();
|
|
4970
|
+
case "j":
|
|
4971
|
+
case "o":
|
|
4972
|
+
return JSON.stringify(arg, null, 2);
|
|
4973
|
+
default:
|
|
4974
|
+
return match;
|
|
4975
|
+
}
|
|
4976
|
+
});
|
|
4977
|
+
if (argIndex < args.length) {
|
|
4978
|
+
formattedMessage += ` ${args.slice(argIndex).map((arg) => typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg)).join(" ")}`;
|
|
4979
|
+
}
|
|
4980
|
+
}
|
|
4981
|
+
const { consoleText, fileText } = this.buildOutputTexts(formattedMessage);
|
|
4982
|
+
if (this.shouldStyleConsole()) {
|
|
4983
|
+
const tag = this.options.showTags !== false && this.name ? styles22.gray(this.formatTag(this.name)) : "";
|
|
4984
|
+
const spinnerChar = styles22.blue("◐");
|
|
4985
|
+
console.error(`${spinnerChar} ${tag} ${styles22.cyan(consoleText)}`);
|
|
4986
|
+
}
|
|
4987
|
+
const timestamp = new Date;
|
|
4988
|
+
const formattedDate = timestamp.toISOString();
|
|
4989
|
+
const logEntry = `[${formattedDate}] ${this.environment}.INFO: [START] ${fileText}
|
|
4990
|
+
`.replace(this.ANSI_PATTERN, "");
|
|
4991
|
+
if (this.shouldWriteToFile())
|
|
4992
|
+
await this.writeToFile(logEntry);
|
|
4993
|
+
}
|
|
4994
|
+
renderProgressBar(barState, isFinished = false) {
|
|
4995
|
+
if (!this.enabled || !this.shouldStyleConsole() || !process11.stdout.isTTY)
|
|
4996
|
+
return;
|
|
4997
|
+
const percent = Math.min(100, Math.max(0, Math.round(barState.current / barState.total * 100)));
|
|
4998
|
+
const filledLength = Math.round(barState.barLength * percent / 100);
|
|
4999
|
+
const emptyLength = barState.barLength - filledLength;
|
|
5000
|
+
const filledBar = styles22.green("━".repeat(filledLength));
|
|
5001
|
+
const emptyBar = styles22.gray("━".repeat(emptyLength));
|
|
5002
|
+
const bar = `[${filledBar}${emptyBar}]`;
|
|
5003
|
+
const percentageText = `${percent}%`.padStart(4);
|
|
5004
|
+
const messageText = barState.message ? ` ${barState.message}` : "";
|
|
5005
|
+
const icon = isFinished || percent === 100 ? styles22.green("✓") : styles22.blue("▶");
|
|
5006
|
+
const tag = this.options.showTags !== false && this.name ? ` ${styles22.gray(this.formatTag(this.name))}` : "";
|
|
5007
|
+
const line = `\r${icon}${tag} ${bar} ${percentageText}${messageText}`;
|
|
5008
|
+
const terminalWidth = process11.stdout.columns || 80;
|
|
5009
|
+
const clearLine = " ".repeat(Math.max(0, terminalWidth - line.replace(this.ANSI_PATTERN, "").length));
|
|
5010
|
+
barState.lastRenderedLine = `${line}${clearLine}`;
|
|
5011
|
+
process11.stdout.write(barState.lastRenderedLine);
|
|
5012
|
+
if (isFinished) {
|
|
5013
|
+
process11.stdout.write(`
|
|
5014
|
+
`);
|
|
5015
|
+
}
|
|
5016
|
+
}
|
|
5017
|
+
finishProgressBar(barState, finalMessage) {
|
|
5018
|
+
if (!this.enabled || !this.fancy || isBrowserProcess22() || !process11.stdout.isTTY) {
|
|
5019
|
+
this.activeProgressBar = null;
|
|
5020
|
+
return;
|
|
5021
|
+
}
|
|
5022
|
+
if (barState.current < barState.total) {
|
|
5023
|
+
barState.current = barState.total;
|
|
5024
|
+
}
|
|
5025
|
+
if (finalMessage)
|
|
5026
|
+
barState.message = finalMessage;
|
|
5027
|
+
this.renderProgressBar(barState, true);
|
|
5028
|
+
this.activeProgressBar = null;
|
|
5029
|
+
}
|
|
5030
|
+
async clear(filters = {}) {
|
|
5031
|
+
if (isBrowserProcess22()) {
|
|
5032
|
+
console.warn("Log clearing is not supported in browser environments.");
|
|
5033
|
+
return;
|
|
5034
|
+
}
|
|
5035
|
+
try {
|
|
5036
|
+
console.warn("Clearing logs...", this.config.logDirectory);
|
|
5037
|
+
const files = await readdir22(this.config.logDirectory);
|
|
5038
|
+
const logFilesToDelete = [];
|
|
5039
|
+
for (const file of files) {
|
|
5040
|
+
const nameMatches = filters.name ? new RegExp(filters.name.replace("*", ".*")).test(file) : file.startsWith(this.name);
|
|
5041
|
+
if (!nameMatches || !file.endsWith(".log")) {
|
|
5042
|
+
continue;
|
|
5043
|
+
}
|
|
5044
|
+
const filePath = join5(this.config.logDirectory, file);
|
|
5045
|
+
if (filters.before) {
|
|
5046
|
+
try {
|
|
5047
|
+
const fileStats = await stat22(filePath);
|
|
5048
|
+
if (fileStats.mtime >= filters.before) {
|
|
5049
|
+
continue;
|
|
5050
|
+
}
|
|
5051
|
+
} catch (statErr) {
|
|
5052
|
+
console.error(`Failed to get stats for file ${filePath}:`, statErr);
|
|
5053
|
+
continue;
|
|
5054
|
+
}
|
|
5055
|
+
}
|
|
5056
|
+
logFilesToDelete.push(filePath);
|
|
5057
|
+
}
|
|
5058
|
+
if (logFilesToDelete.length === 0) {
|
|
5059
|
+
console.warn("No log files matched the criteria for clearing.");
|
|
5060
|
+
return;
|
|
5061
|
+
}
|
|
5062
|
+
console.warn(`Preparing to delete ${logFilesToDelete.length} log file(s)...`);
|
|
5063
|
+
for (const filePath of logFilesToDelete) {
|
|
5064
|
+
try {
|
|
5065
|
+
await unlink22(filePath);
|
|
5066
|
+
console.warn(`Deleted log file: ${filePath}`);
|
|
5067
|
+
} catch (unlinkErr) {
|
|
5068
|
+
console.error(`Failed to delete log file ${filePath}:`, unlinkErr);
|
|
5069
|
+
}
|
|
5070
|
+
}
|
|
5071
|
+
console.warn("Log clearing process finished.");
|
|
5072
|
+
} catch (err) {
|
|
5073
|
+
console.error("Error during log clearing process:", err);
|
|
3411
5074
|
}
|
|
3412
5075
|
}
|
|
3413
5076
|
}
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
5077
|
+
var logger22 = new Logger22("stacks");
|
|
5078
|
+
|
|
5079
|
+
// src/git-hooks.ts
|
|
5080
|
+
var execAsync = promisify(exec);
|
|
5081
|
+
var log3 = new Logger22("git-hooks", {
|
|
5082
|
+
showTags: true
|
|
5083
|
+
});
|
|
5084
|
+
var VERBOSE = false;
|
|
5085
|
+
var VALID_GIT_HOOKS = [
|
|
5086
|
+
"applypatch-msg",
|
|
5087
|
+
"pre-applypatch",
|
|
5088
|
+
"post-applypatch",
|
|
5089
|
+
"pre-commit",
|
|
5090
|
+
"pre-merge-commit",
|
|
5091
|
+
"prepare-commit-msg",
|
|
5092
|
+
"commit-msg",
|
|
5093
|
+
"post-commit",
|
|
5094
|
+
"pre-rebase",
|
|
5095
|
+
"post-checkout",
|
|
5096
|
+
"post-merge",
|
|
5097
|
+
"pre-push",
|
|
5098
|
+
"pre-receive",
|
|
5099
|
+
"update",
|
|
5100
|
+
"proc-receive",
|
|
5101
|
+
"post-receive",
|
|
5102
|
+
"post-update",
|
|
5103
|
+
"reference-transaction",
|
|
5104
|
+
"push-to-checkout",
|
|
5105
|
+
"pre-auto-gc",
|
|
5106
|
+
"post-rewrite",
|
|
5107
|
+
"sendemail-validate",
|
|
5108
|
+
"fsmonitor-watchman",
|
|
5109
|
+
"p4-changelist",
|
|
5110
|
+
"p4-prepare-changelist",
|
|
5111
|
+
"p4-post-changelist",
|
|
5112
|
+
"p4-pre-submit",
|
|
5113
|
+
"post-index-change"
|
|
5114
|
+
];
|
|
5115
|
+
var VALID_OPTIONS = ["preserveUnused", "verbose", "staged-lint"];
|
|
5116
|
+
var PREPEND_SCRIPT = `#!/bin/sh
|
|
5117
|
+
|
|
5118
|
+
if [ "$SKIP_BUN_GIT_HOOKS" = "1" ]; then
|
|
5119
|
+
echo "[INFO] SKIP_BUN_GIT_HOOKS is set to 1, skipping hook."
|
|
5120
|
+
exit 0
|
|
5121
|
+
fi
|
|
5122
|
+
|
|
5123
|
+
if [ -f "$BUN_GIT_HOOKS_RC" ]; then
|
|
5124
|
+
. "$BUN_GIT_HOOKS_RC"
|
|
5125
|
+
fi
|
|
5126
|
+
|
|
5127
|
+
`;
|
|
5128
|
+
function getGitProjectRoot(directory = process12.cwd()) {
|
|
5129
|
+
if (directory.endsWith(".git")) {
|
|
5130
|
+
return path.normalize(directory);
|
|
5131
|
+
}
|
|
5132
|
+
let start = path.normalize(directory);
|
|
5133
|
+
if (!start || start === path.sep || start === ".") {
|
|
5134
|
+
return;
|
|
5135
|
+
}
|
|
5136
|
+
const fullPath = path.join(start, ".git");
|
|
5137
|
+
if (fs.existsSync(fullPath)) {
|
|
5138
|
+
if (!fs.lstatSync(fullPath).isDirectory()) {
|
|
5139
|
+
const content = fs.readFileSync(fullPath, { encoding: "utf-8" });
|
|
5140
|
+
const match = /^gitdir: (.*)\s*$/.exec(content);
|
|
5141
|
+
if (match) {
|
|
5142
|
+
const gitDir = match[1];
|
|
5143
|
+
let commonDir = path.join(gitDir, "commondir");
|
|
5144
|
+
if (fs.existsSync(commonDir)) {
|
|
5145
|
+
commonDir = fs.readFileSync(commonDir, "utf8").trim();
|
|
5146
|
+
return path.resolve(gitDir, commonDir);
|
|
5147
|
+
}
|
|
5148
|
+
return path.normalize(gitDir);
|
|
5149
|
+
}
|
|
3421
5150
|
}
|
|
3422
|
-
return
|
|
3423
|
-
}
|
|
3424
|
-
|
|
3425
|
-
|
|
5151
|
+
return path.normalize(fullPath);
|
|
5152
|
+
}
|
|
5153
|
+
const parentDir = path.dirname(start);
|
|
5154
|
+
if (parentDir === start) {
|
|
5155
|
+
return;
|
|
3426
5156
|
}
|
|
5157
|
+
return getGitProjectRoot(parentDir);
|
|
3427
5158
|
}
|
|
3428
|
-
function
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
const
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
return results;
|
|
5159
|
+
function getProjectRootDirectoryFromNodeModules(projectPath) {
|
|
5160
|
+
function _arraysAreEqual(a1, a2) {
|
|
5161
|
+
return JSON.stringify(a1) === JSON.stringify(a2);
|
|
5162
|
+
}
|
|
5163
|
+
const projDir = projectPath.split(/[\\/]/);
|
|
5164
|
+
const indexOfStoreDir = projDir.indexOf(".store");
|
|
5165
|
+
if (indexOfStoreDir > -1) {
|
|
5166
|
+
return projDir.slice(0, indexOfStoreDir - 1).join("/");
|
|
5167
|
+
}
|
|
5168
|
+
if (projDir.length > 3 && _arraysAreEqual(projDir.slice(-3), ["node_modules", ".bin", "bun-git-hooks"])) {
|
|
5169
|
+
return projDir.slice(0, -3).join("/");
|
|
5170
|
+
}
|
|
5171
|
+
if (projDir.length > 2 && _arraysAreEqual(projDir.slice(-2), ["node_modules", "bun-git-hooks"])) {
|
|
5172
|
+
return projDir.slice(0, -2).join("/");
|
|
5173
|
+
}
|
|
5174
|
+
return;
|
|
3445
5175
|
}
|
|
3446
|
-
function
|
|
3447
|
-
if (
|
|
3448
|
-
|
|
5176
|
+
function checkBunGitHooksInDependencies(projectRootPath) {
|
|
5177
|
+
if (typeof projectRootPath !== "string") {
|
|
5178
|
+
throw new TypeError("Package json path is not a string!");
|
|
5179
|
+
}
|
|
5180
|
+
const { packageJsonContent } = _getPackageJson(projectRootPath);
|
|
5181
|
+
if ("dependencies" in packageJsonContent && "bun-git-hooks" in packageJsonContent.dependencies) {
|
|
5182
|
+
console.warn("[WARN] You should move `bun-git-hooks` to your devDependencies!");
|
|
5183
|
+
return true;
|
|
5184
|
+
}
|
|
5185
|
+
if (!("devDependencies" in packageJsonContent)) {
|
|
5186
|
+
return false;
|
|
3449
5187
|
}
|
|
3450
|
-
|
|
3451
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
3452
|
-
return regex.test(file);
|
|
5188
|
+
return "bun-git-hooks" in packageJsonContent.devDependencies;
|
|
3453
5189
|
}
|
|
3454
|
-
function
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
});
|
|
5190
|
+
function _getPackageJson(projectPath = process12.cwd()) {
|
|
5191
|
+
if (typeof projectPath !== "string") {
|
|
5192
|
+
throw new TypeError("projectPath is not a string");
|
|
5193
|
+
}
|
|
5194
|
+
const targetPackageJson = path.normalize(`${projectPath}/package.json`);
|
|
5195
|
+
if (!fs.statSync(targetPackageJson).isFile()) {
|
|
5196
|
+
throw new Error("Package.json doesn't exist");
|
|
5197
|
+
}
|
|
5198
|
+
const packageJsonDataRaw = fs.readFileSync(targetPackageJson, { encoding: "utf-8" });
|
|
5199
|
+
return { packageJsonContent: JSON.parse(packageJsonDataRaw), packageJsonPath: targetPackageJson };
|
|
3463
5200
|
}
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
5201
|
+
function areHooksInstalled(projectRootPath = process12.cwd()) {
|
|
5202
|
+
const gitRoot = getGitProjectRoot(projectRootPath);
|
|
5203
|
+
if (!gitRoot) {
|
|
5204
|
+
return false;
|
|
5205
|
+
}
|
|
5206
|
+
if (!config3 || Object.keys(config3).length === 0) {
|
|
5207
|
+
return false;
|
|
3469
5208
|
}
|
|
3470
|
-
const
|
|
3471
|
-
|
|
3472
|
-
|
|
5209
|
+
const configuredHooks = Object.keys(config3).filter((key) => VALID_GIT_HOOKS.includes(key));
|
|
5210
|
+
if (configuredHooks.length === 0) {
|
|
5211
|
+
return false;
|
|
5212
|
+
}
|
|
5213
|
+
for (const hook of configuredHooks) {
|
|
5214
|
+
const hookPath = path.normalize(path.join(gitRoot, "hooks", hook));
|
|
5215
|
+
if (!fs.existsSync(hookPath)) {
|
|
5216
|
+
return false;
|
|
5217
|
+
}
|
|
3473
5218
|
try {
|
|
3474
|
-
const
|
|
3475
|
-
if (
|
|
3476
|
-
console.info("[INFO] Running command:", cmd);
|
|
3477
|
-
console.info("[INFO] On files:", files);
|
|
3478
|
-
}
|
|
3479
|
-
const { stdout, stderr } = await execAsync(fullCommand, { cwd: projectRoot });
|
|
3480
|
-
if (stdout && (verbose || stdout.includes("error") || stdout.includes("warning"))) {
|
|
3481
|
-
console.info(stdout);
|
|
3482
|
-
}
|
|
3483
|
-
if (stderr) {
|
|
3484
|
-
console.error("[ERROR] Command output:", stderr);
|
|
5219
|
+
const hookContent = fs.readFileSync(hookPath, "utf-8");
|
|
5220
|
+
if (!hookContent.includes("SKIP_BUN_GIT_HOOKS")) {
|
|
3485
5221
|
return false;
|
|
3486
5222
|
}
|
|
3487
|
-
} catch
|
|
3488
|
-
if (error.stdout)
|
|
3489
|
-
console.info(error.stdout);
|
|
3490
|
-
if (error.stderr)
|
|
3491
|
-
console.error("[ERROR] Command stderr:", error.stderr);
|
|
3492
|
-
console.error(`[ERROR] Command failed: ${cmd}`);
|
|
5223
|
+
} catch {
|
|
3493
5224
|
return false;
|
|
3494
5225
|
}
|
|
3495
5226
|
}
|
|
3496
5227
|
return true;
|
|
3497
5228
|
}
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
5229
|
+
function setHooksFromConfig(projectRootPath = process12.cwd(), options) {
|
|
5230
|
+
if (!config3 || Object.keys(config3).length === 0)
|
|
5231
|
+
throw new Error("[ERROR] Config was not found! Please add `.git-hooks.config.{ts,js,mjs,cjs,json}` or `git-hooks.config.{ts,js,mjs,cjs,json}` or the `git-hooks` entry in package.json.\r\nCheck README for details");
|
|
5232
|
+
const configFile = options?.configFile || { ...config3 };
|
|
5233
|
+
VERBOSE = Boolean(options?.verbose);
|
|
5234
|
+
_validateStagedLintConfig(configFile);
|
|
5235
|
+
const hookKeys = Object.keys(configFile).filter((key) => !VALID_OPTIONS.includes(key));
|
|
5236
|
+
const isValidConfig = hookKeys.every((key) => VALID_GIT_HOOKS.includes(key));
|
|
5237
|
+
if (!isValidConfig)
|
|
5238
|
+
throw new Error("[ERROR] Config was not in correct format. Please check git hooks or options name");
|
|
5239
|
+
const preserveUnused = Array.isArray(configFile.preserveUnused) ? configFile.preserveUnused : configFile.preserveUnused ? VALID_GIT_HOOKS : [];
|
|
5240
|
+
const logKeys = Object.keys(configFile).filter((key) => !VALID_OPTIONS.includes(key)).sort().map((key) => italic22(key)).join(", ");
|
|
5241
|
+
const verbose = options?.verbose !== undefined ? options.verbose : configFile.verbose ?? false;
|
|
5242
|
+
if (verbose) {
|
|
5243
|
+
log3.debug(`Hook Keys: ${logKeys}`);
|
|
3504
5244
|
}
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
5245
|
+
for (const hook of VALID_GIT_HOOKS) {
|
|
5246
|
+
if (Object.prototype.hasOwnProperty.call(configFile, hook)) {
|
|
5247
|
+
if (!configFile[hook])
|
|
5248
|
+
throw new Error(`[ERROR] Command for ${hook} is not set`);
|
|
5249
|
+
_setHook(hook, configFile[hook], projectRootPath, verbose);
|
|
5250
|
+
} else if (!preserveUnused.includes(hook)) {
|
|
5251
|
+
_removeHook(hook, projectRootPath);
|
|
3512
5252
|
}
|
|
3513
5253
|
}
|
|
3514
|
-
return success;
|
|
3515
5254
|
}
|
|
3516
|
-
function _setHook(hook, commandOrConfig, projectRoot =
|
|
5255
|
+
function _setHook(hook, commandOrConfig, projectRoot = process12.cwd(), verbose = false) {
|
|
3517
5256
|
const gitRoot = getGitProjectRoot(projectRoot);
|
|
3518
5257
|
if (!gitRoot) {
|
|
3519
5258
|
console.info("[INFO] No `.git` root folder found, skipping");
|
|
@@ -3534,64 +5273,332 @@ function _setHook(hook, commandOrConfig, projectRoot = process10.cwd()) {
|
|
|
3534
5273
|
fs.mkdirSync(hookDirectory, { recursive: true });
|
|
3535
5274
|
}
|
|
3536
5275
|
const addOrModify = fs.existsSync(hookPath) ? "Modify" : "Add";
|
|
3537
|
-
|
|
5276
|
+
if (verbose) {
|
|
5277
|
+
log3.debug(`${addOrModify} ${italic22(hook)} hook`);
|
|
5278
|
+
}
|
|
3538
5279
|
fs.writeFileSync(hookPath, hookCommand, { mode: 493 });
|
|
3539
5280
|
}
|
|
3540
|
-
function removeHooks(projectRoot =
|
|
5281
|
+
function removeHooks(projectRoot = process12.cwd(), verbose = false) {
|
|
3541
5282
|
for (const configEntry of VALID_GIT_HOOKS)
|
|
3542
5283
|
_removeHook(configEntry, projectRoot, verbose);
|
|
3543
5284
|
}
|
|
3544
|
-
function _removeHook(hook, projectRoot =
|
|
5285
|
+
function _removeHook(hook, projectRoot = process12.cwd(), verbose = false) {
|
|
3545
5286
|
const gitRoot = getGitProjectRoot(projectRoot);
|
|
3546
5287
|
const hookPath = path.normalize(`${gitRoot}/hooks/${hook}`);
|
|
3547
5288
|
if (fs.existsSync(hookPath)) {
|
|
3548
|
-
|
|
5289
|
+
if (VERBOSE)
|
|
5290
|
+
log3.debug(`Hook ${hook} is not set, removing!`);
|
|
3549
5291
|
fs.unlinkSync(hookPath);
|
|
3550
5292
|
}
|
|
3551
5293
|
if (verbose)
|
|
3552
|
-
|
|
5294
|
+
log3.success(`Successfully removed the ${hook} hook`);
|
|
3553
5295
|
}
|
|
3554
|
-
|
|
3555
|
-
const
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
5296
|
+
function _validateStagedLintConfig(config5) {
|
|
5297
|
+
for (const hook of VALID_GIT_HOOKS) {
|
|
5298
|
+
if (hook !== "pre-commit" && config5[hook] && typeof config5[hook] === "object") {
|
|
5299
|
+
const hookConfig = config5[hook];
|
|
5300
|
+
if (hookConfig["stagedLint"] || hookConfig["staged-lint"]) {
|
|
5301
|
+
throw new Error(`staged-lint is only allowed in pre-commit hook. Found in ${hook} hook.`);
|
|
5302
|
+
}
|
|
5303
|
+
}
|
|
5304
|
+
}
|
|
5305
|
+
}
|
|
5306
|
+
// src/staged-lint.ts
|
|
5307
|
+
import { execSync } from "node:child_process";
|
|
5308
|
+
import fs2 from "node:fs";
|
|
5309
|
+
import path2 from "node:path";
|
|
5310
|
+
import process14 from "node:process";
|
|
5311
|
+
|
|
5312
|
+
class StagedLintProcessor {
|
|
5313
|
+
projectRoot;
|
|
5314
|
+
verbose;
|
|
5315
|
+
autoRestage;
|
|
5316
|
+
constructor(projectRoot = process14.cwd(), verbose = false, autoRestage = true) {
|
|
5317
|
+
this.projectRoot = projectRoot;
|
|
5318
|
+
this.verbose = verbose;
|
|
5319
|
+
this.autoRestage = autoRestage;
|
|
5320
|
+
}
|
|
5321
|
+
async process(config5) {
|
|
5322
|
+
try {
|
|
5323
|
+
const stagedFiles = this.getStagedFiles();
|
|
5324
|
+
if (stagedFiles.length === 0) {
|
|
5325
|
+
this.log("No staged files found");
|
|
5326
|
+
return true;
|
|
5327
|
+
}
|
|
5328
|
+
this.log(`Processing ${stagedFiles.length} staged files`);
|
|
5329
|
+
const originalContent = this.autoRestage ? this.captureStagedContent(stagedFiles) : new Map;
|
|
5330
|
+
let hasErrors = false;
|
|
5331
|
+
for (const [pattern, commands] of Object.entries(config5)) {
|
|
5332
|
+
const matchingFiles = this.getMatchingFiles(stagedFiles, pattern);
|
|
5333
|
+
if (matchingFiles.length === 0) {
|
|
5334
|
+
this.log(`No files match pattern "${pattern}" - skipping`);
|
|
5335
|
+
continue;
|
|
5336
|
+
}
|
|
5337
|
+
this.log(`Pattern "${pattern}" matched ${matchingFiles.length} file(s)`);
|
|
5338
|
+
if (this.verbose) {
|
|
5339
|
+
this.log(`Matched files: ${matchingFiles.join(", ")}`);
|
|
5340
|
+
}
|
|
5341
|
+
const commandArray = Array.isArray(commands) ? commands : [commands];
|
|
5342
|
+
for (const command of commandArray) {
|
|
5343
|
+
const success = await this.runLintCommand(command, matchingFiles);
|
|
5344
|
+
if (!success) {
|
|
5345
|
+
hasErrors = true;
|
|
5346
|
+
}
|
|
5347
|
+
}
|
|
5348
|
+
}
|
|
5349
|
+
if (this.autoRestage && !hasErrors) {
|
|
5350
|
+
const modifiedFiles = this.getModifiedFiles(originalContent);
|
|
5351
|
+
if (modifiedFiles.length > 0) {
|
|
5352
|
+
this.log(`Auto-restaging ${modifiedFiles.length} modified files: ${modifiedFiles.join(", ")}`);
|
|
5353
|
+
this.restageFiles(modifiedFiles);
|
|
5354
|
+
const validationSuccess = await this.validateStagedFiles(config5);
|
|
5355
|
+
if (!validationSuccess) {
|
|
5356
|
+
this.log("Validation failed after auto-restaging");
|
|
5357
|
+
return false;
|
|
5358
|
+
}
|
|
5359
|
+
}
|
|
5360
|
+
} else if (!this.autoRestage) {
|
|
5361
|
+
const modifiedFiles = this.getModifiedFiles(originalContent);
|
|
5362
|
+
if (modifiedFiles.length > 0) {
|
|
5363
|
+
console.warn(`⚠️ Lint modified ${modifiedFiles.length} files but auto-restaging is disabled.`);
|
|
5364
|
+
console.warn(` Modified files: ${modifiedFiles.join(", ")}`);
|
|
5365
|
+
console.warn(` You may need to manually stage these files and commit again.`);
|
|
5366
|
+
}
|
|
5367
|
+
}
|
|
5368
|
+
return !hasErrors;
|
|
5369
|
+
} catch (error) {
|
|
5370
|
+
console.error(`Staged lint process failed: ${error}`);
|
|
5371
|
+
return false;
|
|
5372
|
+
}
|
|
5373
|
+
}
|
|
5374
|
+
getStagedFiles() {
|
|
5375
|
+
try {
|
|
5376
|
+
const output = execSync("git diff --cached --name-only", {
|
|
5377
|
+
cwd: this.projectRoot,
|
|
5378
|
+
encoding: "utf-8"
|
|
5379
|
+
});
|
|
5380
|
+
return output.trim().split(`
|
|
5381
|
+
`).filter(Boolean);
|
|
5382
|
+
} catch {
|
|
5383
|
+
return [];
|
|
5384
|
+
}
|
|
3560
5385
|
}
|
|
3561
|
-
|
|
3562
|
-
const
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
5386
|
+
captureStagedContent(files) {
|
|
5387
|
+
const content = new Map;
|
|
5388
|
+
for (const file of files) {
|
|
5389
|
+
try {
|
|
5390
|
+
const stagedContent = execSync(`git show :${file}`, {
|
|
5391
|
+
cwd: this.projectRoot,
|
|
5392
|
+
encoding: "utf-8"
|
|
5393
|
+
});
|
|
5394
|
+
content.set(file, stagedContent);
|
|
5395
|
+
} catch {
|
|
5396
|
+
try {
|
|
5397
|
+
const workingContent = fs2.readFileSync(path2.join(this.projectRoot, file), "utf-8");
|
|
5398
|
+
content.set(file, workingContent);
|
|
5399
|
+
} catch {}
|
|
3567
5400
|
}
|
|
3568
5401
|
}
|
|
5402
|
+
return content;
|
|
3569
5403
|
}
|
|
3570
|
-
|
|
3571
|
-
|
|
5404
|
+
getMatchingFiles(files, pattern) {
|
|
5405
|
+
const expandedPatterns = this.expandBracePattern(pattern);
|
|
5406
|
+
const includePatterns = expandedPatterns.filter((p) => !p.startsWith("!"));
|
|
5407
|
+
const excludePatterns = expandedPatterns.filter((p) => p.startsWith("!"));
|
|
5408
|
+
return files.filter((file) => {
|
|
5409
|
+
const isIncluded = includePatterns.length === 0 || includePatterns.some((p) => this.matchesGlob(file, p));
|
|
5410
|
+
const isExcluded = excludePatterns.some((p) => this.matchesGlob(file, p.slice(1)));
|
|
5411
|
+
return isIncluded && !isExcluded;
|
|
5412
|
+
});
|
|
5413
|
+
}
|
|
5414
|
+
expandBracePattern(pattern) {
|
|
5415
|
+
const braceMatch = pattern.match(/\{([^}]+)\}/g);
|
|
5416
|
+
if (!braceMatch)
|
|
5417
|
+
return [pattern];
|
|
5418
|
+
const results = [pattern];
|
|
5419
|
+
braceMatch.forEach((brace) => {
|
|
5420
|
+
const options = brace.slice(1, -1).split(",");
|
|
5421
|
+
const newResults = [];
|
|
5422
|
+
results.forEach((result) => {
|
|
5423
|
+
options.forEach((option) => {
|
|
5424
|
+
newResults.push(result.replace(brace, option.trim()));
|
|
5425
|
+
});
|
|
5426
|
+
});
|
|
5427
|
+
results.length = 0;
|
|
5428
|
+
results.push(...newResults);
|
|
5429
|
+
});
|
|
5430
|
+
return results;
|
|
5431
|
+
}
|
|
5432
|
+
matchesGlob(file, pattern) {
|
|
5433
|
+
if (pattern.startsWith("!")) {
|
|
5434
|
+
return !this.matchesGlob(file, pattern.slice(1));
|
|
5435
|
+
}
|
|
5436
|
+
let regexPattern = pattern;
|
|
5437
|
+
regexPattern = regexPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
5438
|
+
regexPattern = regexPattern.replace(/\*\*/g, "__DOUBLESTAR__");
|
|
5439
|
+
regexPattern = regexPattern.replace(/\*/g, "[^/]*");
|
|
5440
|
+
regexPattern = regexPattern.replace(/__DOUBLESTAR__/g, ".*");
|
|
5441
|
+
regexPattern = regexPattern.replace(/\?/g, "[^/]");
|
|
5442
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
5443
|
+
return regex.test(file);
|
|
5444
|
+
}
|
|
5445
|
+
async runLintCommand(command, files) {
|
|
5446
|
+
try {
|
|
5447
|
+
const finalCommand = command.includes("{files}") ? command.replace("{files}", files.join(" ")) : `${command} ${files.join(" ")}`;
|
|
5448
|
+
this.log(`Running command on ${files.length} file(s): ${command}`);
|
|
5449
|
+
if (this.verbose) {
|
|
5450
|
+
this.log(`Files: ${files.join(", ")}`);
|
|
5451
|
+
this.log(`Full command: ${finalCommand}`);
|
|
5452
|
+
}
|
|
5453
|
+
const result = execSync(finalCommand, {
|
|
5454
|
+
cwd: this.projectRoot,
|
|
5455
|
+
stdio: this.verbose ? "inherit" : "pipe",
|
|
5456
|
+
encoding: "utf-8"
|
|
5457
|
+
});
|
|
5458
|
+
if (this.verbose && result) {
|
|
5459
|
+
console.warn(result);
|
|
5460
|
+
}
|
|
5461
|
+
this.log(`Command completed successfully for ${files.length} file(s)`);
|
|
5462
|
+
return true;
|
|
5463
|
+
} catch (error) {
|
|
5464
|
+
if (error.stdout && this.verbose)
|
|
5465
|
+
console.warn(error.stdout);
|
|
5466
|
+
if (error.stderr)
|
|
5467
|
+
console.error("[ERROR] Command stderr:", error.stderr);
|
|
5468
|
+
console.error(`[ERROR] Command failed: ${command}`);
|
|
5469
|
+
console.error(`[ERROR] Failed on files: ${files.join(", ")}`);
|
|
5470
|
+
return false;
|
|
5471
|
+
}
|
|
5472
|
+
}
|
|
5473
|
+
getModifiedFiles(originalContent) {
|
|
5474
|
+
const modifiedFiles = [];
|
|
5475
|
+
for (const [file, originalText] of originalContent) {
|
|
5476
|
+
try {
|
|
5477
|
+
const currentContent = fs2.readFileSync(path2.join(this.projectRoot, file), "utf-8");
|
|
5478
|
+
if (currentContent !== originalText) {
|
|
5479
|
+
modifiedFiles.push(file);
|
|
5480
|
+
}
|
|
5481
|
+
} catch {}
|
|
5482
|
+
}
|
|
5483
|
+
return modifiedFiles;
|
|
5484
|
+
}
|
|
5485
|
+
restageFiles(files) {
|
|
5486
|
+
if (files.length === 0)
|
|
5487
|
+
return;
|
|
5488
|
+
try {
|
|
5489
|
+
execSync(`git add ${files.join(" ")}`, {
|
|
5490
|
+
cwd: this.projectRoot,
|
|
5491
|
+
stdio: this.verbose ? "inherit" : "pipe"
|
|
5492
|
+
});
|
|
5493
|
+
} catch (error) {
|
|
5494
|
+
throw new Error(`Failed to re-stage files: ${error}`);
|
|
5495
|
+
}
|
|
5496
|
+
}
|
|
5497
|
+
async validateStagedFiles(config5) {
|
|
5498
|
+
const stagedFiles = this.getStagedFiles();
|
|
5499
|
+
for (const [pattern, commands] of Object.entries(config5)) {
|
|
5500
|
+
const matchingFiles = this.getMatchingFiles(stagedFiles, pattern);
|
|
5501
|
+
if (matchingFiles.length === 0)
|
|
5502
|
+
continue;
|
|
5503
|
+
const commandArray = Array.isArray(commands) ? commands : [commands];
|
|
5504
|
+
for (const command of commandArray) {
|
|
5505
|
+
const validationCommand = command.replace(/--fix\b/g, "").trim();
|
|
5506
|
+
const success = await this.runLintCommand(validationCommand, matchingFiles);
|
|
5507
|
+
if (!success) {
|
|
5508
|
+
return false;
|
|
5509
|
+
}
|
|
5510
|
+
}
|
|
5511
|
+
}
|
|
5512
|
+
return true;
|
|
5513
|
+
}
|
|
5514
|
+
log(message) {
|
|
5515
|
+
if (this.verbose) {
|
|
5516
|
+
console.warn(`[staged-lint] ${message}`);
|
|
5517
|
+
}
|
|
5518
|
+
}
|
|
5519
|
+
}
|
|
5520
|
+
async function runEnhancedStagedLint(config5, projectRoot = process14.cwd(), options = {}) {
|
|
5521
|
+
const { verbose = false, autoRestage = true } = options;
|
|
5522
|
+
const processor = new StagedLintProcessor(projectRoot, verbose, autoRestage);
|
|
5523
|
+
return processor.process(config5);
|
|
5524
|
+
}
|
|
5525
|
+
var HOOK_NAME_MAP = {
|
|
5526
|
+
preCommit: "pre-commit",
|
|
5527
|
+
prepareCommitMsg: "prepare-commit-msg",
|
|
5528
|
+
commitMsg: "commit-msg",
|
|
5529
|
+
postCommit: "post-commit",
|
|
5530
|
+
prePush: "pre-push",
|
|
5531
|
+
postMerge: "post-merge",
|
|
5532
|
+
postCheckout: "post-checkout",
|
|
5533
|
+
preRebase: "pre-rebase",
|
|
5534
|
+
postRewrite: "post-rewrite",
|
|
5535
|
+
"pre-commit": "preCommit",
|
|
5536
|
+
"prepare-commit-msg": "prepareCommitMsg",
|
|
5537
|
+
"commit-msg": "commitMsg",
|
|
5538
|
+
"post-commit": "postCommit",
|
|
5539
|
+
"pre-push": "prePush",
|
|
5540
|
+
"post-merge": "postMerge",
|
|
5541
|
+
"post-checkout": "postCheckout",
|
|
5542
|
+
"pre-rebase": "preRebase",
|
|
5543
|
+
"post-rewrite": "postRewrite"
|
|
5544
|
+
};
|
|
5545
|
+
function isValidHookName(hookName, validHooks) {
|
|
5546
|
+
if (validHooks.includes(hookName)) {
|
|
5547
|
+
return true;
|
|
5548
|
+
}
|
|
5549
|
+
const kebabCase = HOOK_NAME_MAP[hookName];
|
|
5550
|
+
if (kebabCase && validHooks.includes(kebabCase)) {
|
|
5551
|
+
return true;
|
|
3572
5552
|
}
|
|
3573
|
-
console.error(`[ERROR] No staged lint configuration found for hook ${hook}`);
|
|
3574
5553
|
return false;
|
|
3575
5554
|
}
|
|
3576
|
-
function
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
5555
|
+
async function runStagedLint(hook, config5, projectRoot, verbose = false, autoRestage) {
|
|
5556
|
+
if (!config5) {
|
|
5557
|
+
console.error(`[ERROR] No configuration found`);
|
|
5558
|
+
return false;
|
|
5559
|
+
}
|
|
5560
|
+
let shouldAutoRestage = autoRestage !== undefined ? autoRestage : true;
|
|
5561
|
+
const hookVariants = [hook, HOOK_NAME_MAP[hook]].filter(Boolean);
|
|
5562
|
+
for (const hookName of hookVariants) {
|
|
5563
|
+
if (hookName && hookName in config5) {
|
|
5564
|
+
const hookConfig = config5[hookName];
|
|
5565
|
+
if (typeof hookConfig === "object" && !Array.isArray(hookConfig)) {
|
|
5566
|
+
const stagedLintConfig = hookConfig.stagedLint || hookConfig["staged-lint"];
|
|
5567
|
+
const hookAutoRestage = hookConfig.autoRestage;
|
|
5568
|
+
if (autoRestage === undefined && hookAutoRestage !== undefined) {
|
|
5569
|
+
shouldAutoRestage = hookAutoRestage;
|
|
5570
|
+
}
|
|
5571
|
+
if (stagedLintConfig) {
|
|
5572
|
+
const processor = new StagedLintProcessor(projectRoot, verbose, shouldAutoRestage);
|
|
5573
|
+
return processor.process(stagedLintConfig);
|
|
5574
|
+
}
|
|
3582
5575
|
}
|
|
3583
5576
|
}
|
|
3584
5577
|
}
|
|
5578
|
+
if (config5.stagedLint || config5["staged-lint"]) {
|
|
5579
|
+
if (autoRestage === undefined && config5.autoRestage !== undefined) {
|
|
5580
|
+
shouldAutoRestage = config5.autoRestage;
|
|
5581
|
+
}
|
|
5582
|
+
const processor = new StagedLintProcessor(projectRoot, verbose, shouldAutoRestage);
|
|
5583
|
+
return processor.process(config5.stagedLint || config5["staged-lint"]);
|
|
5584
|
+
}
|
|
5585
|
+
console.error(`[ERROR] No staged lint configuration found for hook ${hook}`);
|
|
5586
|
+
return false;
|
|
3585
5587
|
}
|
|
3586
5588
|
export {
|
|
3587
5589
|
setHooksFromConfig,
|
|
3588
5590
|
runStagedLint,
|
|
5591
|
+
runEnhancedStagedLint,
|
|
3589
5592
|
removeHooks,
|
|
5593
|
+
isValidHookName,
|
|
3590
5594
|
getProjectRootDirectoryFromNodeModules,
|
|
3591
5595
|
getGitProjectRoot,
|
|
3592
5596
|
config3 as config,
|
|
3593
5597
|
checkBunGitHooksInDependencies,
|
|
5598
|
+
areHooksInstalled,
|
|
3594
5599
|
VALID_OPTIONS,
|
|
3595
5600
|
VALID_GIT_HOOKS,
|
|
3596
|
-
|
|
5601
|
+
StagedLintProcessor,
|
|
5602
|
+
PREPEND_SCRIPT,
|
|
5603
|
+
HOOK_NAME_MAP
|
|
3597
5604
|
};
|