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/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 process6 from "node:process";
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 process8 from "process";
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 config2(nameOrOptions = { defaultConfig: {} }) {
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 deepMerge2(defaultConfig2, loadedConfig);
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 = process8.env[envKey] || process8.env[oldEnvKey];
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 || process8.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 configPatterns = [];
1711
- configPatterns.push(`${name}.config`);
1712
- configPatterns.push(`.${name}.config`);
1713
- configPatterns.push(name);
1714
- configPatterns.push(`.${name}`);
1715
- if (alias) {
1716
- configPatterns.push(`${alias}.config`);
1717
- configPatterns.push(`.${alias}.config`);
1718
- configPatterns.push(alias);
1719
- configPatterns.push(`.${alias}`);
1720
- }
1721
- for (const configPath of configPatterns) {
1722
- for (const ext of extensions) {
1723
- const fullPath = resolve3(baseDir, `${configPath}${ext}`);
1724
- const config3 = await tryLoadConfig2(fullPath, configWithEnvVars);
1725
- if (config3 !== null) {
1726
- if (verbose) {
1727
- log.success(`Configuration loaded from: ${configPath}${ext}`);
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 deepMerge2(configWithEnvVars, pkgConfig);
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(process8.cwd(), "config");
1768
- var defaultGeneratedDir2 = resolve3(process8.cwd(), "src/generated");
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 config3 = {
1860
+ var config2 = {
1772
1861
  "pre-commit": {
1773
1862
  "staged-lint": {
1774
1863
  "**/*.{js,ts}": [
1775
- "bunx --bun eslint --max-warnings=0",
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 = config3;
1872
+ var git_hooks_config_default = config2;
1784
1873
 
1785
1874
  // src/config.ts
1786
- var config5 = await config2({
1875
+ var config3 = await loadConfig3({
1787
1876
  name: "git-hooks",
1788
- cwd: process6.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 process11 from "node:process";
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 resolve22 } from "path";
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 resolve4 } from "path";
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 = resolve4(baseDir, `${configPath}${ext}`);
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 = resolve4(baseDir, "package.json");
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 = resolve4(process9.cwd(), "config");
1993
- var defaultGeneratedDir3 = resolve4(process9.cwd(), "src/generated");
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 relative2(process22.cwd(), finalPath);
2105
+ return relative3(process22.cwd(), finalPath);
2001
2106
  return finalPath;
2002
2107
  }
2003
- var defaultLogDirectory2 = process22.env.CLARITY_LOG_DIR || join3(getProjectRoot2(), "logs");
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 config6 = await loadConfig22();
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 = { ...config6 };
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: config6.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((resolve32) => setTimeout(resolve32, delay));
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((resolve32) => setTimeout(resolve32, delay));
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((resolve32, reject) => {
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", () => resolve32(Buffer2.from(Buffer2.concat(chunks))));
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((resolve32) => {
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
- resolve32(input === "y" || input === "yes");
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
- // src/git-hooks.ts
3263
- var execAsync = promisify(exec);
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
- let start = path.normalize(directory);
3315
- if (!start || start === path.sep || start === ".") {
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
- const fullPath = path.join(start, ".git");
3319
- if (fs.existsSync(fullPath)) {
3320
- if (!fs.lstatSync(fullPath).isDirectory()) {
3321
- const content = fs.readFileSync(fullPath, { encoding: "utf-8" });
3322
- const match = /^gitdir: (.*)\s*$/.exec(content);
3323
- if (match) {
3324
- const gitDir = match[1];
3325
- let commonDir = path.join(gitDir, "commondir");
3326
- if (fs.existsSync(commonDir)) {
3327
- commonDir = fs.readFileSync(commonDir, "utf8").trim();
3328
- return path.resolve(gitDir, commonDir);
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
- return path.normalize(fullPath);
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
- const parentDir = path.dirname(start);
3336
- if (parentDir === start) {
3337
- return;
3414
+ if (!isObject22(source) || !isObject22(target)) {
3415
+ return source;
3338
3416
  }
3339
- return getGitProjectRoot(parentDir);
3340
- }
3341
- function getProjectRootDirectoryFromNodeModules(projectPath) {
3342
- function _arraysAreEqual(a1, a2) {
3343
- return JSON.stringify(a1) === JSON.stringify(a2);
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
- const projDir = projectPath.split(/[\\/]/);
3346
- const indexOfStoreDir = projDir.indexOf(".store");
3347
- if (indexOfStoreDir > -1) {
3348
- return projDir.slice(0, indexOfStoreDir - 1).join("/");
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 (projDir.length > 3 && _arraysAreEqual(projDir.slice(-3), ["node_modules", ".bin", "bun-git-hooks"])) {
3351
- return projDir.slice(0, -3).join("/");
3468
+ if (Array.isArray(target)) {
3469
+ return strategy === "replace" ? source : deepMerge22(target, source);
3352
3470
  }
3353
- if (projDir.length > 2 && _arraysAreEqual(projDir.slice(-2), ["node_modules", "bun-git-hooks"])) {
3354
- return projDir.slice(0, -2).join("/");
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 checkBunGitHooksInDependencies(projectRootPath) {
3359
- if (typeof projectRootPath !== "string") {
3360
- throw new TypeError("Package json path is not a string!");
3361
- }
3362
- const { packageJsonContent } = _getPackageJson(projectRootPath);
3363
- if ("dependencies" in packageJsonContent && "bun-git-hooks" in packageJsonContent.dependencies) {
3364
- console.warn("[WARN] You should move `bun-git-hooks` to your devDependencies!");
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 (!("devDependencies" in packageJsonContent)) {
3368
- return false;
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 "bun-git-hooks" in packageJsonContent.devDependencies;
3520
+ return false;
3371
3521
  }
3372
- function _getPackageJson(projectPath = process11.cwd()) {
3373
- if (typeof projectPath !== "string") {
3374
- throw new TypeError("projectPath is not a string");
3375
- }
3376
- const targetPackageJson = path.normalize(`${projectPath}/package.json`);
3377
- if (!fs.statSync(targetPackageJson).isFile()) {
3378
- throw new Error("Package.json doesn't exist");
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 setHooksFromConfig(projectRootPath = process11.cwd(), options) {
3384
- if (!config5 || Object.keys(config5).length === 0)
3385
- 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");
3386
- const configFile = options?.configFile || { ...config5 };
3387
- _validateStagedLintConfig(configFile);
3388
- const hookKeys = Object.keys(configFile).filter((key) => !VALID_OPTIONS.includes(key));
3389
- const isValidConfig = hookKeys.every((key) => VALID_GIT_HOOKS.includes(key));
3390
- if (!isValidConfig)
3391
- throw new Error("[ERROR] Config was not in correct format. Please check git hooks or options name");
3392
- const preserveUnused = Array.isArray(configFile.preserveUnused) ? configFile.preserveUnused : configFile.preserveUnused ? VALID_GIT_HOOKS : [];
3393
- const logKeys = Object.keys(configFile).filter((key) => !VALID_OPTIONS.includes(key)).sort().map((key) => italic2(key)).join(", ");
3394
- log2.debug(`Hook Keys: ${logKeys}`);
3395
- for (const hook of VALID_GIT_HOOKS) {
3396
- if (Object.prototype.hasOwnProperty.call(configFile, hook)) {
3397
- if (!configFile[hook])
3398
- throw new Error(`[ERROR] Command for ${hook} is not set`);
3399
- _setHook(hook, configFile[hook], projectRootPath);
3400
- } else if (!preserveUnused.includes(hook)) {
3401
- _removeHook(hook, projectRootPath);
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
- async function getStagedFiles(projectRoot = process11.cwd()) {
3406
- try {
3407
- const { stdout } = await execAsync("git diff --cached --name-only --diff-filter=ACMR", { cwd: projectRoot });
3408
- const files = stdout.trim().split(`
3409
- `).filter(Boolean);
3410
- if (config5.verbose && files.length > 0) {
3411
- console.info("[INFO] Staged files found:", files);
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 files;
3414
- } catch (error) {
3415
- console.error("[ERROR] Failed to get staged files:", error);
3416
- return [];
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 expandBracePattern(pattern) {
3420
- const braceMatch = pattern.match(/{([^}]+)}/g);
3421
- if (!braceMatch)
3422
- return [pattern];
3423
- const results = [pattern];
3424
- braceMatch.forEach((brace) => {
3425
- const options = brace.slice(1, -1).split(",");
3426
- const newResults = [];
3427
- results.forEach((result) => {
3428
- options.forEach((option) => {
3429
- newResults.push(result.replace(brace, option.trim()));
3430
- });
3431
- });
3432
- results.length = 0;
3433
- results.push(...newResults);
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 matchesGlob(file, pattern) {
3438
- if (pattern.startsWith("!")) {
3439
- return !matchesGlob(file, pattern.slice(1));
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
- const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "(?:.*?)").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]");
3442
- const regex = new RegExp(`^${regexPattern}$`);
3443
- return regex.test(file);
5188
+ return "bun-git-hooks" in packageJsonContent.devDependencies;
3444
5189
  }
3445
- function filterFilesByPattern(files, pattern) {
3446
- const expandedPatterns = expandBracePattern(pattern);
3447
- const includePatterns = expandedPatterns.filter((p) => !p.startsWith("!"));
3448
- const excludePatterns = expandedPatterns.filter((p) => p.startsWith("!"));
3449
- return files.filter((file) => {
3450
- const isIncluded = includePatterns.some((p) => matchesGlob(file, p));
3451
- const isExcluded = excludePatterns.some((p) => matchesGlob(file, p.slice(1)));
3452
- return isIncluded && !isExcluded;
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
- async function runCommandOnStagedFiles(command, files, projectRoot = process11.cwd(), verbose = false) {
3456
- if (files.length === 0) {
3457
- if (verbose)
3458
- console.info("[INFO] No matching files for pattern");
3459
- return true;
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 commands = Array.isArray(command) ? command : [command];
3462
- const fileList = files.join(" ");
3463
- for (const cmd of commands) {
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 fullCommand = `${cmd} ${fileList}`;
3466
- if (verbose) {
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 (error) {
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
- async function processStagedLint(stagedLintConfig, projectRoot, verbose = false) {
3490
- const stagedFiles = await getStagedFiles(projectRoot);
3491
- if (stagedFiles.length === 0) {
3492
- if (verbose)
3493
- console.info("[INFO] No staged files found");
3494
- return true;
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
- let success = true;
3497
- for (const [pattern, task] of Object.entries(stagedLintConfig)) {
3498
- const matchedFiles = filterFilesByPattern(stagedFiles, pattern);
3499
- const taskResult = await runCommandOnStagedFiles(task, matchedFiles, projectRoot, verbose);
3500
- if (!taskResult) {
3501
- success = false;
3502
- break;
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 = process11.cwd()) {
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
- log2.debug(`${addOrModify} ${italic2(hook)} hook`);
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 = process11.cwd(), verbose = false) {
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 = process11.cwd(), verbose = false) {
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
- log2.debug(`Hook ${hook} is not set, removing!`);
5289
+ if (VERBOSE)
5290
+ log3.debug(`Hook ${hook} is not set, removing!`);
3540
5291
  fs.unlinkSync(hookPath);
3541
5292
  }
3542
5293
  if (verbose)
3543
- log2.success(`Successfully removed the ${hook} hook`);
5294
+ log3.success(`Successfully removed the ${hook} hook`);
3544
5295
  }
3545
- async function runStagedLint(hook) {
3546
- const projectRoot = process11.cwd();
3547
- const configFile = config5;
3548
- if (!configFile) {
3549
- console.error(`[ERROR] No configuration found`);
3550
- return false;
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
- if (hook in configFile) {
3553
- const hookConfig = configFile[hook];
3554
- if (typeof hookConfig === "object" && !Array.isArray(hookConfig)) {
3555
- const stagedLintConfig = hookConfig.stagedLint || hookConfig["staged-lint"];
3556
- if (stagedLintConfig) {
3557
- return processStagedLint(stagedLintConfig, projectRoot, configFile.verbose);
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
- if (configFile["staged-lint"]) {
3562
- return processStagedLint(configFile["staged-lint"], projectRoot, configFile.verbose);
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 _validateStagedLintConfig(config4) {
3568
- for (const hook of VALID_GIT_HOOKS) {
3569
- if (hook !== "pre-commit" && config4[hook] && typeof config4[hook] === "object") {
3570
- const hookConfig = config4[hook];
3571
- if (hookConfig["stagedLint"] || hookConfig["staged-lint"]) {
3572
- throw new Error(`staged-lint is only allowed in pre-commit hook. Found in ${hook} hook.`);
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
- config5 as config,
5596
+ config3 as config,
3584
5597
  checkBunGitHooksInDependencies,
5598
+ areHooksInstalled,
3585
5599
  VALID_OPTIONS,
3586
5600
  VALID_GIT_HOOKS,
3587
- PREPEND_SCRIPT
5601
+ StagedLintProcessor,
5602
+ PREPEND_SCRIPT,
5603
+ HOOK_NAME_MAP
3588
5604
  };