bun-git-hooks 0.2.18 → 0.3.0
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 +4561 -2440
- 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 +2317 -301
- package/dist/staged-lint.d.ts +59 -0
- package/dist/types.d.ts +25 -12
- package/package.json +29 -14
- package/scripts/postinstall.ts +60 -0
- package/scripts/uninstall.ts +20 -0
package/dist/index.js
CHANGED
|
@@ -18,12 +18,13 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
18
18
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
19
|
|
|
20
20
|
// src/config.ts
|
|
21
|
-
import
|
|
21
|
+
import process7 from "node:process";
|
|
22
22
|
|
|
23
23
|
// node_modules/bunfig/dist/index.js
|
|
24
24
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync as readdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
25
|
+
import { homedir } from "os";
|
|
25
26
|
import { dirname as dirname2, resolve as resolve3 } from "path";
|
|
26
|
-
import
|
|
27
|
+
import process6 from "process";
|
|
27
28
|
import { join, relative, resolve as resolve2 } from "path";
|
|
28
29
|
import process2 from "process";
|
|
29
30
|
import { existsSync, mkdirSync, readdirSync, writeFileSync } from "fs";
|
|
@@ -1582,6 +1583,39 @@ function deepMerge2(target, source) {
|
|
|
1582
1583
|
}
|
|
1583
1584
|
return merged;
|
|
1584
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
|
+
}
|
|
1585
1619
|
function deepEquals2(a, b) {
|
|
1586
1620
|
if (a === b)
|
|
1587
1621
|
return true;
|
|
@@ -1615,21 +1649,7 @@ function isObject2(item) {
|
|
|
1615
1649
|
var log = new Logger("bunfig", {
|
|
1616
1650
|
showTags: true
|
|
1617
1651
|
});
|
|
1618
|
-
async function
|
|
1619
|
-
if (typeof nameOrOptions === "string") {
|
|
1620
|
-
const { cwd } = await import("process");
|
|
1621
|
-
return await loadConfig3({
|
|
1622
|
-
name: nameOrOptions,
|
|
1623
|
-
cwd: cwd(),
|
|
1624
|
-
generatedDir: "./generated",
|
|
1625
|
-
configDir: "./config",
|
|
1626
|
-
defaultConfig: {},
|
|
1627
|
-
checkEnv: true
|
|
1628
|
-
});
|
|
1629
|
-
}
|
|
1630
|
-
return await loadConfig3(nameOrOptions);
|
|
1631
|
-
}
|
|
1632
|
-
async function tryLoadConfig2(configPath, defaultConfig2) {
|
|
1652
|
+
async function tryLoadConfig2(configPath, defaultConfig2, arrayStrategy = "replace") {
|
|
1633
1653
|
if (!existsSync3(configPath))
|
|
1634
1654
|
return null;
|
|
1635
1655
|
try {
|
|
@@ -1638,7 +1658,7 @@ async function tryLoadConfig2(configPath, defaultConfig2) {
|
|
|
1638
1658
|
if (typeof loadedConfig !== "object" || loadedConfig === null || Array.isArray(loadedConfig))
|
|
1639
1659
|
return null;
|
|
1640
1660
|
try {
|
|
1641
|
-
return
|
|
1661
|
+
return deepMergeWithArrayStrategy(defaultConfig2, loadedConfig, arrayStrategy);
|
|
1642
1662
|
} catch {
|
|
1643
1663
|
return null;
|
|
1644
1664
|
}
|
|
@@ -1663,7 +1683,7 @@ function applyEnvVarsToConfig(name, config3, verbose = false) {
|
|
|
1663
1683
|
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
1664
1684
|
result2[key] = processObject(value, envPath);
|
|
1665
1685
|
} else {
|
|
1666
|
-
const envValue =
|
|
1686
|
+
const envValue = process6.env[envKey] || process6.env[oldEnvKey];
|
|
1667
1687
|
if (envValue !== undefined) {
|
|
1668
1688
|
if (verbose) {
|
|
1669
1689
|
log.info(`Using environment variable ${envValue ? envKey : oldEnvKey} for config ${name}.${envPath.join(".")}`);
|
|
@@ -1697,36 +1717,105 @@ async function loadConfig3({
|
|
|
1697
1717
|
name = "",
|
|
1698
1718
|
alias,
|
|
1699
1719
|
cwd,
|
|
1720
|
+
configDir,
|
|
1700
1721
|
defaultConfig: defaultConfig2,
|
|
1701
1722
|
verbose = false,
|
|
1702
|
-
checkEnv = true
|
|
1723
|
+
checkEnv = true,
|
|
1724
|
+
arrayStrategy = "replace"
|
|
1703
1725
|
}) {
|
|
1704
1726
|
const configWithEnvVars = checkEnv && typeof defaultConfig2 === "object" && defaultConfig2 !== null && !Array.isArray(defaultConfig2) ? applyEnvVarsToConfig(name, defaultConfig2, verbose) : defaultConfig2;
|
|
1705
|
-
const baseDir = cwd ||
|
|
1727
|
+
const baseDir = cwd || process6.cwd();
|
|
1706
1728
|
const extensions = [".ts", ".js", ".mjs", ".cjs", ".json"];
|
|
1707
1729
|
if (verbose) {
|
|
1708
1730
|
log.info(`Loading configuration for "${name}"${alias ? ` (alias: "${alias}")` : ""} from ${baseDir}`);
|
|
1709
1731
|
}
|
|
1710
|
-
const
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
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;
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
if (name) {
|
|
1761
|
+
const homeConfigDir = resolve3(homedir(), ".config", name);
|
|
1762
|
+
const homeConfigPatterns = ["config", `${name}.config`];
|
|
1763
|
+
if (alias) {
|
|
1764
|
+
homeConfigPatterns.push(`${alias}.config`);
|
|
1765
|
+
}
|
|
1766
|
+
if (verbose) {
|
|
1767
|
+
log.info(`Checking user config directory: ${homeConfigDir}`);
|
|
1768
|
+
}
|
|
1769
|
+
for (const configPath of homeConfigPatterns) {
|
|
1770
|
+
for (const ext of extensions) {
|
|
1771
|
+
const fullPath = resolve3(homeConfigDir, `${configPath}${ext}`);
|
|
1772
|
+
const config3 = await tryLoadConfig2(fullPath, configWithEnvVars, arrayStrategy);
|
|
1773
|
+
if (config3 !== null) {
|
|
1774
|
+
if (verbose) {
|
|
1775
|
+
log.success(`Configuration loaded from user config directory: ${fullPath}`);
|
|
1776
|
+
}
|
|
1777
|
+
return config3;
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
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;
|
|
1728
1818
|
}
|
|
1729
|
-
return config3;
|
|
1730
1819
|
}
|
|
1731
1820
|
}
|
|
1732
1821
|
}
|
|
@@ -1746,7 +1835,7 @@ async function loadConfig3({
|
|
|
1746
1835
|
if (verbose) {
|
|
1747
1836
|
log.success(`Configuration loaded from package.json: ${pkgConfig === pkg[name] ? name : alias}`);
|
|
1748
1837
|
}
|
|
1749
|
-
return
|
|
1838
|
+
return deepMergeWithArrayStrategy(configWithEnvVars, pkgConfig, arrayStrategy);
|
|
1750
1839
|
} catch (error) {
|
|
1751
1840
|
if (verbose) {
|
|
1752
1841
|
log.warn(`Failed to merge package.json config:`, error);
|
|
@@ -1764,15 +1853,15 @@ async function loadConfig3({
|
|
|
1764
1853
|
}
|
|
1765
1854
|
return configWithEnvVars;
|
|
1766
1855
|
}
|
|
1767
|
-
var defaultConfigDir2 = resolve3(
|
|
1768
|
-
var defaultGeneratedDir2 = resolve3(
|
|
1856
|
+
var defaultConfigDir2 = resolve3(process6.cwd(), "config");
|
|
1857
|
+
var defaultGeneratedDir2 = resolve3(process6.cwd(), "src/generated");
|
|
1769
1858
|
|
|
1770
1859
|
// git-hooks.config.ts
|
|
1771
|
-
var
|
|
1860
|
+
var config2 = {
|
|
1772
1861
|
"pre-commit": {
|
|
1773
1862
|
"staged-lint": {
|
|
1774
1863
|
"**/*.{js,ts}": [
|
|
1775
|
-
"bunx --bun eslint --
|
|
1864
|
+
"bunx --bun eslint --fix",
|
|
1776
1865
|
"bunx --bun tsc --noEmit"
|
|
1777
1866
|
]
|
|
1778
1867
|
}
|
|
@@ -1780,26 +1869,32 @@ var config3 = {
|
|
|
1780
1869
|
"commit-msg": "bunx gitlint .git/COMMIT_EDITMSG",
|
|
1781
1870
|
verbose: true
|
|
1782
1871
|
};
|
|
1783
|
-
var git_hooks_config_default =
|
|
1872
|
+
var git_hooks_config_default = config2;
|
|
1784
1873
|
|
|
1785
1874
|
// src/config.ts
|
|
1786
|
-
var
|
|
1875
|
+
var config3 = await loadConfig3({
|
|
1787
1876
|
name: "git-hooks",
|
|
1788
|
-
cwd:
|
|
1877
|
+
cwd: process7.cwd(),
|
|
1789
1878
|
defaultConfig: git_hooks_config_default
|
|
1790
1879
|
});
|
|
1791
1880
|
// src/git-hooks.ts
|
|
1792
1881
|
import fs from "node:fs";
|
|
1793
1882
|
import path from "node:path";
|
|
1794
|
-
import
|
|
1883
|
+
import process12 from "node:process";
|
|
1795
1884
|
import { exec } from "node:child_process";
|
|
1796
1885
|
import { promisify } from "node:util";
|
|
1797
1886
|
|
|
1798
|
-
// node_modules/@stacksjs/clarity/dist/index.js
|
|
1799
|
-
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";
|
|
1800
1895
|
import process22 from "process";
|
|
1801
1896
|
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
1802
|
-
import { dirname as dirname3, resolve as
|
|
1897
|
+
import { dirname as dirname3, resolve as resolve5 } from "path";
|
|
1803
1898
|
import process9 from "process";
|
|
1804
1899
|
import { Buffer as Buffer2 } from "buffer";
|
|
1805
1900
|
import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes2 } from "crypto";
|
|
@@ -1811,6 +1906,16 @@ import { pipeline as pipeline2 } from "stream/promises";
|
|
|
1811
1906
|
import { createGzip as createGzip2 } from "zlib";
|
|
1812
1907
|
import process42 from "process";
|
|
1813
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";
|
|
1814
1919
|
function deepMerge3(target, source) {
|
|
1815
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) {
|
|
1816
1921
|
return source;
|
|
@@ -1968,7 +2073,7 @@ async function loadConfig4({
|
|
|
1968
2073
|
];
|
|
1969
2074
|
for (const configPath of configPaths) {
|
|
1970
2075
|
for (const ext of extensions) {
|
|
1971
|
-
const fullPath =
|
|
2076
|
+
const fullPath = resolve5(baseDir, `${configPath}${ext}`);
|
|
1972
2077
|
const config22 = await tryLoadConfig3(fullPath, defaultConfig2);
|
|
1973
2078
|
if (config22 !== null) {
|
|
1974
2079
|
return config22;
|
|
@@ -1976,7 +2081,7 @@ async function loadConfig4({
|
|
|
1976
2081
|
}
|
|
1977
2082
|
}
|
|
1978
2083
|
try {
|
|
1979
|
-
const pkgPath =
|
|
2084
|
+
const pkgPath = resolve5(baseDir, "package.json");
|
|
1980
2085
|
if (existsSync4(pkgPath)) {
|
|
1981
2086
|
const pkg = await import(pkgPath);
|
|
1982
2087
|
const pkgConfig = pkg[name];
|
|
@@ -1989,18 +2094,18 @@ async function loadConfig4({
|
|
|
1989
2094
|
} catch {}
|
|
1990
2095
|
return defaultConfig2;
|
|
1991
2096
|
}
|
|
1992
|
-
var defaultConfigDir3 =
|
|
1993
|
-
var defaultGeneratedDir3 =
|
|
2097
|
+
var defaultConfigDir3 = resolve5(process9.cwd(), "config");
|
|
2098
|
+
var defaultGeneratedDir3 = resolve5(process9.cwd(), "src/generated");
|
|
1994
2099
|
function getProjectRoot2(filePath, options = {}) {
|
|
1995
2100
|
let path = process22.cwd();
|
|
1996
2101
|
while (path.includes("storage"))
|
|
1997
2102
|
path = resolve22(path, "..");
|
|
1998
2103
|
const finalPath = resolve22(path, filePath || "");
|
|
1999
2104
|
if (options?.relative)
|
|
2000
|
-
return
|
|
2105
|
+
return relative3(process22.cwd(), finalPath);
|
|
2001
2106
|
return finalPath;
|
|
2002
2107
|
}
|
|
2003
|
-
var defaultLogDirectory2 = process22.env.CLARITY_LOG_DIR ||
|
|
2108
|
+
var defaultLogDirectory2 = process22.env.CLARITY_LOG_DIR || join4(getProjectRoot2(), "logs");
|
|
2004
2109
|
var defaultConfig2 = {
|
|
2005
2110
|
level: "info",
|
|
2006
2111
|
defaultName: "clarity",
|
|
@@ -2037,7 +2142,7 @@ async function loadConfig22() {
|
|
|
2037
2142
|
return defaultConfig2;
|
|
2038
2143
|
}
|
|
2039
2144
|
}
|
|
2040
|
-
var
|
|
2145
|
+
var config4 = await loadConfig22();
|
|
2041
2146
|
function isBrowserProcess2() {
|
|
2042
2147
|
if (process32.env.NODE_ENV === "test" || process32.env.BUN_ENV === "test") {
|
|
2043
2148
|
return false;
|
|
@@ -2060,6 +2165,7 @@ async function isServerProcess2() {
|
|
|
2060
2165
|
}
|
|
2061
2166
|
return false;
|
|
2062
2167
|
}
|
|
2168
|
+
|
|
2063
2169
|
class JsonFormatter2 {
|
|
2064
2170
|
async format(entry) {
|
|
2065
2171
|
const isServer = await isServerProcess2();
|
|
@@ -2171,7 +2277,7 @@ class Logger2 {
|
|
|
2171
2277
|
activeProgressBar = null;
|
|
2172
2278
|
constructor(name, options = {}) {
|
|
2173
2279
|
this.name = name;
|
|
2174
|
-
this.config = { ...
|
|
2280
|
+
this.config = { ...config4 };
|
|
2175
2281
|
this.options = this.normalizeOptions(options);
|
|
2176
2282
|
this.formatter = this.options.formatter || new JsonFormatter2;
|
|
2177
2283
|
this.enabled = options.enabled ?? true;
|
|
@@ -2227,7 +2333,7 @@ class Logger2 {
|
|
|
2227
2333
|
const defaultOptions = {
|
|
2228
2334
|
format: "json",
|
|
2229
2335
|
level: "info",
|
|
2230
|
-
logDirectory:
|
|
2336
|
+
logDirectory: config4.logDirectory,
|
|
2231
2337
|
rotation: undefined,
|
|
2232
2338
|
timestamp: undefined,
|
|
2233
2339
|
fingersCrossed: {},
|
|
@@ -2303,7 +2409,7 @@ class Logger2 {
|
|
|
2303
2409
|
const errorMessage = typeof error.message === "string" ? error.message : "Unknown error";
|
|
2304
2410
|
console.error(`Network error during write attempt ${retries + 1}/${maxRetries}:`, errorMessage);
|
|
2305
2411
|
const delay = backoffDelay * 2 ** retries;
|
|
2306
|
-
await new Promise((
|
|
2412
|
+
await new Promise((resolve322) => setTimeout(resolve322, delay));
|
|
2307
2413
|
retries++;
|
|
2308
2414
|
continue;
|
|
2309
2415
|
}
|
|
@@ -2331,7 +2437,7 @@ class Logger2 {
|
|
|
2331
2437
|
}
|
|
2332
2438
|
retries++;
|
|
2333
2439
|
const delay = backoffDelay * 2 ** (retries - 1);
|
|
2334
|
-
await new Promise((
|
|
2440
|
+
await new Promise((resolve322) => setTimeout(resolve322, delay));
|
|
2335
2441
|
}
|
|
2336
2442
|
}
|
|
2337
2443
|
})();
|
|
@@ -2454,11 +2560,11 @@ class Logger2 {
|
|
|
2454
2560
|
};
|
|
2455
2561
|
}
|
|
2456
2562
|
async compressData(data) {
|
|
2457
|
-
return new Promise((
|
|
2563
|
+
return new Promise((resolve322, reject) => {
|
|
2458
2564
|
const gzip = createGzip2();
|
|
2459
2565
|
const chunks = [];
|
|
2460
2566
|
gzip.on("data", (chunk2) => chunks.push(chunk2));
|
|
2461
|
-
gzip.on("end", () =>
|
|
2567
|
+
gzip.on("end", () => resolve322(Buffer2.from(Buffer2.concat(chunks))));
|
|
2462
2568
|
gzip.on("error", reject);
|
|
2463
2569
|
gzip.write(data);
|
|
2464
2570
|
gzip.end();
|
|
@@ -3044,7 +3150,7 @@ class Logger2 {
|
|
|
3044
3150
|
if (isBrowserProcess2()) {
|
|
3045
3151
|
return Promise.resolve(true);
|
|
3046
3152
|
}
|
|
3047
|
-
return new Promise((
|
|
3153
|
+
return new Promise((resolve322) => {
|
|
3048
3154
|
console.error(`${styles2.cyan("?")} ${message} (y/n) `);
|
|
3049
3155
|
const onData = (data) => {
|
|
3050
3156
|
const input = data.toString().trim().toLowerCase();
|
|
@@ -3056,7 +3162,7 @@ class Logger2 {
|
|
|
3056
3162
|
} catch {}
|
|
3057
3163
|
process52.stdin.pause();
|
|
3058
3164
|
console.error("");
|
|
3059
|
-
|
|
3165
|
+
resolve322(input === "y" || input === "yes");
|
|
3060
3166
|
};
|
|
3061
3167
|
try {
|
|
3062
3168
|
if (typeof process52.stdin.setRawMode === "function") {
|
|
@@ -3258,253 +3364,1895 @@ class Logger2 {
|
|
|
3258
3364
|
}
|
|
3259
3365
|
}
|
|
3260
3366
|
var logger2 = new Logger2("stacks");
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
var log2 = new Logger2("git-hooks", {
|
|
3265
|
-
showTags: true
|
|
3266
|
-
});
|
|
3267
|
-
var VALID_GIT_HOOKS = [
|
|
3268
|
-
"applypatch-msg",
|
|
3269
|
-
"pre-applypatch",
|
|
3270
|
-
"post-applypatch",
|
|
3271
|
-
"pre-commit",
|
|
3272
|
-
"pre-merge-commit",
|
|
3273
|
-
"prepare-commit-msg",
|
|
3274
|
-
"commit-msg",
|
|
3275
|
-
"post-commit",
|
|
3276
|
-
"pre-rebase",
|
|
3277
|
-
"post-checkout",
|
|
3278
|
-
"post-merge",
|
|
3279
|
-
"pre-push",
|
|
3280
|
-
"pre-receive",
|
|
3281
|
-
"update",
|
|
3282
|
-
"proc-receive",
|
|
3283
|
-
"post-receive",
|
|
3284
|
-
"post-update",
|
|
3285
|
-
"reference-transaction",
|
|
3286
|
-
"push-to-checkout",
|
|
3287
|
-
"pre-auto-gc",
|
|
3288
|
-
"post-rewrite",
|
|
3289
|
-
"sendemail-validate",
|
|
3290
|
-
"fsmonitor-watchman",
|
|
3291
|
-
"p4-changelist",
|
|
3292
|
-
"p4-prepare-changelist",
|
|
3293
|
-
"p4-post-changelist",
|
|
3294
|
-
"p4-pre-submit",
|
|
3295
|
-
"post-index-change"
|
|
3296
|
-
];
|
|
3297
|
-
var VALID_OPTIONS = ["preserveUnused", "verbose", "staged-lint"];
|
|
3298
|
-
var PREPEND_SCRIPT = `#!/bin/sh
|
|
3299
|
-
|
|
3300
|
-
if [ "$SKIP_BUN_GIT_HOOKS" = "1" ]; then
|
|
3301
|
-
echo "[INFO] SKIP_BUN_GIT_HOOKS is set to 1, skipping hook."
|
|
3302
|
-
exit 0
|
|
3303
|
-
fi
|
|
3304
|
-
|
|
3305
|
-
if [ -f "$BUN_GIT_HOOKS_RC" ]; then
|
|
3306
|
-
. "$BUN_GIT_HOOKS_RC"
|
|
3307
|
-
fi
|
|
3308
|
-
|
|
3309
|
-
`;
|
|
3310
|
-
function getGitProjectRoot(directory = process11.cwd()) {
|
|
3311
|
-
if (directory.endsWith(".git")) {
|
|
3312
|
-
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;
|
|
3313
3370
|
}
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
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 };
|
|
3317
3373
|
}
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
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);
|
|
3329
3399
|
}
|
|
3330
|
-
return path.normalize(gitDir);
|
|
3331
3400
|
}
|
|
3401
|
+
return result;
|
|
3332
3402
|
}
|
|
3333
|
-
|
|
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;
|
|
3334
3413
|
}
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
return;
|
|
3414
|
+
if (!isObject22(source) || !isObject22(target)) {
|
|
3415
|
+
return source;
|
|
3338
3416
|
}
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
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
|
+
}
|
|
3344
3459
|
}
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
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);
|
|
3349
3467
|
}
|
|
3350
|
-
if (
|
|
3351
|
-
return
|
|
3468
|
+
if (Array.isArray(target)) {
|
|
3469
|
+
return strategy === "replace" ? source : deepMerge22(target, source);
|
|
3352
3470
|
}
|
|
3353
|
-
if (
|
|
3354
|
-
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
|
+
}
|
|
3355
3492
|
}
|
|
3356
|
-
return;
|
|
3493
|
+
return result;
|
|
3357
3494
|
}
|
|
3358
|
-
function
|
|
3359
|
-
if (
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
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
|
+
}
|
|
3365
3505
|
return true;
|
|
3366
3506
|
}
|
|
3367
|
-
if (
|
|
3368
|
-
|
|
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;
|
|
3369
3519
|
}
|
|
3370
|
-
return
|
|
3520
|
+
return false;
|
|
3371
3521
|
}
|
|
3372
|
-
function
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
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;
|
|
3379
3543
|
}
|
|
3380
|
-
const packageJsonDataRaw = fs.readFileSync(targetPackageJson, { encoding: "utf-8" });
|
|
3381
|
-
return { packageJsonContent: JSON.parse(packageJsonDataRaw), packageJsonPath: targetPackageJson };
|
|
3382
3544
|
}
|
|
3383
|
-
function
|
|
3384
|
-
if (!
|
|
3385
|
-
|
|
3386
|
-
const
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
if (!
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
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);
|
|
3402
5074
|
}
|
|
3403
5075
|
}
|
|
3404
5076
|
}
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
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
|
+
}
|
|
3412
5150
|
}
|
|
3413
|
-
return
|
|
3414
|
-
}
|
|
3415
|
-
|
|
3416
|
-
|
|
5151
|
+
return path.normalize(fullPath);
|
|
5152
|
+
}
|
|
5153
|
+
const parentDir = path.dirname(start);
|
|
5154
|
+
if (parentDir === start) {
|
|
5155
|
+
return;
|
|
3417
5156
|
}
|
|
5157
|
+
return getGitProjectRoot(parentDir);
|
|
3418
5158
|
}
|
|
3419
|
-
function
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
const
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
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;
|
|
3436
5175
|
}
|
|
3437
|
-
function
|
|
3438
|
-
if (
|
|
3439
|
-
|
|
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;
|
|
3440
5187
|
}
|
|
3441
|
-
|
|
3442
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
3443
|
-
return regex.test(file);
|
|
5188
|
+
return "bun-git-hooks" in packageJsonContent.devDependencies;
|
|
3444
5189
|
}
|
|
3445
|
-
function
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
});
|
|
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 };
|
|
3454
5200
|
}
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
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;
|
|
3460
5208
|
}
|
|
3461
|
-
const
|
|
3462
|
-
|
|
3463
|
-
|
|
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
|
+
}
|
|
3464
5218
|
try {
|
|
3465
|
-
const
|
|
3466
|
-
if (
|
|
3467
|
-
console.info("[INFO] Running command:", cmd);
|
|
3468
|
-
console.info("[INFO] On files:", files);
|
|
3469
|
-
}
|
|
3470
|
-
const { stdout, stderr } = await execAsync(fullCommand, { cwd: projectRoot });
|
|
3471
|
-
if (stdout && (verbose || stdout.includes("error") || stdout.includes("warning"))) {
|
|
3472
|
-
console.info(stdout);
|
|
3473
|
-
}
|
|
3474
|
-
if (stderr) {
|
|
3475
|
-
console.error("[ERROR] Command output:", stderr);
|
|
5219
|
+
const hookContent = fs.readFileSync(hookPath, "utf-8");
|
|
5220
|
+
if (!hookContent.includes("SKIP_BUN_GIT_HOOKS")) {
|
|
3476
5221
|
return false;
|
|
3477
5222
|
}
|
|
3478
|
-
} catch
|
|
3479
|
-
if (error.stdout)
|
|
3480
|
-
console.info(error.stdout);
|
|
3481
|
-
if (error.stderr)
|
|
3482
|
-
console.error("[ERROR] Command stderr:", error.stderr);
|
|
3483
|
-
console.error(`[ERROR] Command failed: ${cmd}`);
|
|
5223
|
+
} catch {
|
|
3484
5224
|
return false;
|
|
3485
5225
|
}
|
|
3486
5226
|
}
|
|
3487
5227
|
return true;
|
|
3488
5228
|
}
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
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}`);
|
|
3495
5244
|
}
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
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);
|
|
3503
5252
|
}
|
|
3504
5253
|
}
|
|
3505
|
-
return success;
|
|
3506
5254
|
}
|
|
3507
|
-
function _setHook(hook, commandOrConfig, projectRoot =
|
|
5255
|
+
function _setHook(hook, commandOrConfig, projectRoot = process12.cwd(), verbose = false) {
|
|
3508
5256
|
const gitRoot = getGitProjectRoot(projectRoot);
|
|
3509
5257
|
if (!gitRoot) {
|
|
3510
5258
|
console.info("[INFO] No `.git` root folder found, skipping");
|
|
@@ -3525,64 +5273,332 @@ function _setHook(hook, commandOrConfig, projectRoot = process11.cwd()) {
|
|
|
3525
5273
|
fs.mkdirSync(hookDirectory, { recursive: true });
|
|
3526
5274
|
}
|
|
3527
5275
|
const addOrModify = fs.existsSync(hookPath) ? "Modify" : "Add";
|
|
3528
|
-
|
|
5276
|
+
if (verbose) {
|
|
5277
|
+
log3.debug(`${addOrModify} ${italic22(hook)} hook`);
|
|
5278
|
+
}
|
|
3529
5279
|
fs.writeFileSync(hookPath, hookCommand, { mode: 493 });
|
|
3530
5280
|
}
|
|
3531
|
-
function removeHooks(projectRoot =
|
|
5281
|
+
function removeHooks(projectRoot = process12.cwd(), verbose = false) {
|
|
3532
5282
|
for (const configEntry of VALID_GIT_HOOKS)
|
|
3533
5283
|
_removeHook(configEntry, projectRoot, verbose);
|
|
3534
5284
|
}
|
|
3535
|
-
function _removeHook(hook, projectRoot =
|
|
5285
|
+
function _removeHook(hook, projectRoot = process12.cwd(), verbose = false) {
|
|
3536
5286
|
const gitRoot = getGitProjectRoot(projectRoot);
|
|
3537
5287
|
const hookPath = path.normalize(`${gitRoot}/hooks/${hook}`);
|
|
3538
5288
|
if (fs.existsSync(hookPath)) {
|
|
3539
|
-
|
|
5289
|
+
if (VERBOSE)
|
|
5290
|
+
log3.debug(`Hook ${hook} is not set, removing!`);
|
|
3540
5291
|
fs.unlinkSync(hookPath);
|
|
3541
5292
|
}
|
|
3542
5293
|
if (verbose)
|
|
3543
|
-
|
|
5294
|
+
log3.success(`Successfully removed the ${hook} hook`);
|
|
3544
5295
|
}
|
|
3545
|
-
|
|
3546
|
-
const
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
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
|
+
}
|
|
3551
5385
|
}
|
|
3552
|
-
|
|
3553
|
-
const
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
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 {}
|
|
3558
5400
|
}
|
|
3559
5401
|
}
|
|
5402
|
+
return content;
|
|
3560
5403
|
}
|
|
3561
|
-
|
|
3562
|
-
|
|
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;
|
|
3563
5552
|
}
|
|
3564
|
-
console.error(`[ERROR] No staged lint configuration found for hook ${hook}`);
|
|
3565
5553
|
return false;
|
|
3566
5554
|
}
|
|
3567
|
-
function
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
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
|
+
}
|
|
3573
5575
|
}
|
|
3574
5576
|
}
|
|
3575
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;
|
|
3576
5587
|
}
|
|
3577
5588
|
export {
|
|
3578
5589
|
setHooksFromConfig,
|
|
3579
5590
|
runStagedLint,
|
|
5591
|
+
runEnhancedStagedLint,
|
|
3580
5592
|
removeHooks,
|
|
5593
|
+
isValidHookName,
|
|
3581
5594
|
getProjectRootDirectoryFromNodeModules,
|
|
3582
5595
|
getGitProjectRoot,
|
|
3583
|
-
|
|
5596
|
+
config3 as config,
|
|
3584
5597
|
checkBunGitHooksInDependencies,
|
|
5598
|
+
areHooksInstalled,
|
|
3585
5599
|
VALID_OPTIONS,
|
|
3586
5600
|
VALID_GIT_HOOKS,
|
|
3587
|
-
|
|
5601
|
+
StagedLintProcessor,
|
|
5602
|
+
PREPEND_SCRIPT,
|
|
5603
|
+
HOOK_NAME_MAP
|
|
3588
5604
|
};
|