bun-git-hooks 0.2.19 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1583,6 +1583,39 @@ function deepMerge2(target, source) {
1583
1583
  }
1584
1584
  return merged;
1585
1585
  }
1586
+ function deepMergeWithArrayStrategy(target, source, strategy = "replace") {
1587
+ if (source === null || source === undefined)
1588
+ return target;
1589
+ if (Array.isArray(source)) {
1590
+ return strategy === "replace" ? source : deepMerge2(target, source);
1591
+ }
1592
+ if (Array.isArray(target)) {
1593
+ return strategy === "replace" ? source : deepMerge2(target, source);
1594
+ }
1595
+ if (!isObject2(source) || !isObject2(target))
1596
+ return source;
1597
+ const result = { ...target };
1598
+ for (const key of Object.keys(source)) {
1599
+ if (!Object.prototype.hasOwnProperty.call(source, key))
1600
+ continue;
1601
+ const sourceValue = source[key];
1602
+ const targetValue = result[key];
1603
+ if (sourceValue === null || sourceValue === undefined)
1604
+ continue;
1605
+ if (Array.isArray(sourceValue) || Array.isArray(targetValue)) {
1606
+ if (strategy === "replace") {
1607
+ result[key] = sourceValue;
1608
+ } else {
1609
+ result[key] = deepMerge2(targetValue, sourceValue);
1610
+ }
1611
+ } else if (isObject2(sourceValue) && isObject2(targetValue)) {
1612
+ result[key] = deepMergeWithArrayStrategy(targetValue, sourceValue, strategy);
1613
+ } else {
1614
+ result[key] = sourceValue;
1615
+ }
1616
+ }
1617
+ return result;
1618
+ }
1586
1619
  function deepEquals2(a, b) {
1587
1620
  if (a === b)
1588
1621
  return true;
@@ -1616,7 +1649,7 @@ function isObject2(item) {
1616
1649
  var log = new Logger("bunfig", {
1617
1650
  showTags: true
1618
1651
  });
1619
- async function tryLoadConfig2(configPath, defaultConfig2) {
1652
+ async function tryLoadConfig2(configPath, defaultConfig2, arrayStrategy = "replace") {
1620
1653
  if (!existsSync3(configPath))
1621
1654
  return null;
1622
1655
  try {
@@ -1625,7 +1658,7 @@ async function tryLoadConfig2(configPath, defaultConfig2) {
1625
1658
  if (typeof loadedConfig !== "object" || loadedConfig === null || Array.isArray(loadedConfig))
1626
1659
  return null;
1627
1660
  try {
1628
- return deepMerge2(defaultConfig2, loadedConfig);
1661
+ return deepMergeWithArrayStrategy(defaultConfig2, loadedConfig, arrayStrategy);
1629
1662
  } catch {
1630
1663
  return null;
1631
1664
  }
@@ -1684,9 +1717,11 @@ async function loadConfig3({
1684
1717
  name = "",
1685
1718
  alias,
1686
1719
  cwd,
1720
+ configDir,
1687
1721
  defaultConfig: defaultConfig2,
1688
1722
  verbose = false,
1689
- checkEnv = true
1723
+ checkEnv = true,
1724
+ arrayStrategy = "replace"
1690
1725
  }) {
1691
1726
  const configWithEnvVars = checkEnv && typeof defaultConfig2 === "object" && defaultConfig2 !== null && !Array.isArray(defaultConfig2) ? applyEnvVarsToConfig(name, defaultConfig2, verbose) : defaultConfig2;
1692
1727
  const baseDir = cwd || process6.cwd();
@@ -1694,26 +1729,31 @@ async function loadConfig3({
1694
1729
  if (verbose) {
1695
1730
  log.info(`Loading configuration for "${name}"${alias ? ` (alias: "${alias}")` : ""} from ${baseDir}`);
1696
1731
  }
1697
- const configPatterns = [];
1698
- configPatterns.push(`${name}.config`);
1699
- configPatterns.push(`.${name}.config`);
1700
- configPatterns.push(name);
1701
- configPatterns.push(`.${name}`);
1702
- if (alias) {
1703
- configPatterns.push(`${alias}.config`);
1704
- configPatterns.push(`.${alias}.config`);
1705
- configPatterns.push(alias);
1706
- configPatterns.push(`.${alias}`);
1707
- }
1708
- for (const configPath of configPatterns) {
1709
- for (const ext of extensions) {
1710
- const fullPath = resolve3(baseDir, `${configPath}${ext}`);
1711
- const config3 = await tryLoadConfig2(fullPath, configWithEnvVars);
1712
- if (config3 !== null) {
1713
- if (verbose) {
1714
- 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;
1715
1756
  }
1716
- return config3;
1717
1757
  }
1718
1758
  }
1719
1759
  }
@@ -1729,7 +1769,7 @@ async function loadConfig3({
1729
1769
  for (const configPath of homeConfigPatterns) {
1730
1770
  for (const ext of extensions) {
1731
1771
  const fullPath = resolve3(homeConfigDir, `${configPath}${ext}`);
1732
- const config3 = await tryLoadConfig2(fullPath, configWithEnvVars);
1772
+ const config3 = await tryLoadConfig2(fullPath, configWithEnvVars, arrayStrategy);
1733
1773
  if (config3 !== null) {
1734
1774
  if (verbose) {
1735
1775
  log.success(`Configuration loaded from user config directory: ${fullPath}`);
@@ -1739,6 +1779,46 @@ async function loadConfig3({
1739
1779
  }
1740
1780
  }
1741
1781
  }
1782
+ if (name) {
1783
+ const homeConfigDir = resolve3(homedir(), ".config");
1784
+ const homeConfigDotfilePatterns = [`.${name}.config`];
1785
+ if (alias)
1786
+ homeConfigDotfilePatterns.push(`.${alias}.config`);
1787
+ if (verbose)
1788
+ log.info(`Checking user config directory for dotfile configs: ${homeConfigDir}`);
1789
+ for (const configPath of homeConfigDotfilePatterns) {
1790
+ for (const ext of extensions) {
1791
+ const fullPath = resolve3(homeConfigDir, `${configPath}${ext}`);
1792
+ const config3 = await tryLoadConfig2(fullPath, configWithEnvVars, arrayStrategy);
1793
+ if (config3 !== null) {
1794
+ if (verbose)
1795
+ log.success(`Configuration loaded from user config directory dotfile: ${fullPath}`);
1796
+ return config3;
1797
+ }
1798
+ }
1799
+ }
1800
+ }
1801
+ if (name) {
1802
+ const homeDir = homedir();
1803
+ const homeRootPatterns = [`.${name}.config`, `.${name}`];
1804
+ if (alias) {
1805
+ homeRootPatterns.push(`.${alias}.config`);
1806
+ homeRootPatterns.push(`.${alias}`);
1807
+ }
1808
+ if (verbose)
1809
+ log.info(`Checking user home directory for dotfile configs: ${homeDir}`);
1810
+ for (const configPath of homeRootPatterns) {
1811
+ for (const ext of extensions) {
1812
+ const fullPath = resolve3(homeDir, `${configPath}${ext}`);
1813
+ const config3 = await tryLoadConfig2(fullPath, configWithEnvVars, arrayStrategy);
1814
+ if (config3 !== null) {
1815
+ if (verbose)
1816
+ log.success(`Configuration loaded from user home directory: ${fullPath}`);
1817
+ return config3;
1818
+ }
1819
+ }
1820
+ }
1821
+ }
1742
1822
  try {
1743
1823
  const pkgPath = resolve3(baseDir, "package.json");
1744
1824
  if (existsSync3(pkgPath)) {
@@ -1755,7 +1835,7 @@ async function loadConfig3({
1755
1835
  if (verbose) {
1756
1836
  log.success(`Configuration loaded from package.json: ${pkgConfig === pkg[name] ? name : alias}`);
1757
1837
  }
1758
- return deepMerge2(configWithEnvVars, pkgConfig);
1838
+ return deepMergeWithArrayStrategy(configWithEnvVars, pkgConfig, arrayStrategy);
1759
1839
  } catch (error) {
1760
1840
  if (verbose) {
1761
1841
  log.warn(`Failed to merge package.json config:`, error);
@@ -1781,7 +1861,7 @@ var config2 = {
1781
1861
  "pre-commit": {
1782
1862
  "staged-lint": {
1783
1863
  "**/*.{js,ts}": [
1784
- "bunx --bun eslint --max-warnings=0",
1864
+ "bunx --bun eslint --fix",
1785
1865
  "bunx --bun tsc --noEmit"
1786
1866
  ]
1787
1867
  }
@@ -1800,15 +1880,21 @@ var config3 = await loadConfig3({
1800
1880
  // src/git-hooks.ts
1801
1881
  import fs from "node:fs";
1802
1882
  import path from "node:path";
1803
- import process10 from "node:process";
1883
+ import process12 from "node:process";
1804
1884
  import { exec } from "node:child_process";
1805
1885
  import { promisify } from "node:util";
1806
1886
 
1807
- // node_modules/@stacksjs/clarity/dist/index.js
1808
- import { join as join3, relative as relative2, resolve as 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";
1809
1895
  import process22 from "process";
1810
1896
  import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, writeFileSync as writeFileSync4 } from "fs";
1811
- import { dirname as dirname3, resolve as resolve4 } from "path";
1897
+ import { dirname as dirname3, resolve as resolve5 } from "path";
1812
1898
  import process9 from "process";
1813
1899
  import { Buffer as Buffer2 } from "buffer";
1814
1900
  import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes2 } from "crypto";
@@ -1820,6 +1906,16 @@ import { pipeline as pipeline2 } from "stream/promises";
1820
1906
  import { createGzip as createGzip2 } from "zlib";
1821
1907
  import process42 from "process";
1822
1908
  import process32 from "process";
1909
+ import { Buffer as Buffer22 } from "buffer";
1910
+ import { createCipheriv as createCipheriv22, createDecipheriv as createDecipheriv22, randomBytes as randomBytes22 } from "crypto";
1911
+ import { closeSync as closeSync22, createReadStream as createReadStream22, createWriteStream as createWriteStream22, existsSync as existsSync42, fsyncSync as fsyncSync22, openSync as openSync22, writeFileSync as writeFileSync42 } from "fs";
1912
+ import { access as access22, constants as constants22, mkdir as mkdir22, readdir as readdir22, rename as rename22, stat as stat22, unlink as unlink22, writeFile as writeFile22 } from "fs/promises";
1913
+ import { isAbsolute, join as join5, resolve as resolve6 } from "path";
1914
+ import process11 from "process";
1915
+ import { pipeline as pipeline22 } from "stream/promises";
1916
+ import { createGzip as createGzip22 } from "zlib";
1917
+ import process10 from "process";
1918
+ import process92 from "process";
1823
1919
  function deepMerge3(target, source) {
1824
1920
  if (Array.isArray(source) && Array.isArray(target) && source.length === 2 && target.length === 2 && isObject3(source[0]) && "id" in source[0] && source[0].id === 3 && isObject3(source[1]) && "id" in source[1] && source[1].id === 4) {
1825
1921
  return source;
@@ -1977,7 +2073,7 @@ async function loadConfig4({
1977
2073
  ];
1978
2074
  for (const configPath of configPaths) {
1979
2075
  for (const ext of extensions) {
1980
- const fullPath = resolve4(baseDir, `${configPath}${ext}`);
2076
+ const fullPath = resolve5(baseDir, `${configPath}${ext}`);
1981
2077
  const config22 = await tryLoadConfig3(fullPath, defaultConfig2);
1982
2078
  if (config22 !== null) {
1983
2079
  return config22;
@@ -1985,7 +2081,7 @@ async function loadConfig4({
1985
2081
  }
1986
2082
  }
1987
2083
  try {
1988
- const pkgPath = resolve4(baseDir, "package.json");
2084
+ const pkgPath = resolve5(baseDir, "package.json");
1989
2085
  if (existsSync4(pkgPath)) {
1990
2086
  const pkg = await import(pkgPath);
1991
2087
  const pkgConfig = pkg[name];
@@ -1998,18 +2094,18 @@ async function loadConfig4({
1998
2094
  } catch {}
1999
2095
  return defaultConfig2;
2000
2096
  }
2001
- var defaultConfigDir3 = resolve4(process9.cwd(), "config");
2002
- var defaultGeneratedDir3 = resolve4(process9.cwd(), "src/generated");
2097
+ var defaultConfigDir3 = resolve5(process9.cwd(), "config");
2098
+ var defaultGeneratedDir3 = resolve5(process9.cwd(), "src/generated");
2003
2099
  function getProjectRoot2(filePath, options = {}) {
2004
2100
  let path = process22.cwd();
2005
2101
  while (path.includes("storage"))
2006
2102
  path = resolve22(path, "..");
2007
2103
  const finalPath = resolve22(path, filePath || "");
2008
2104
  if (options?.relative)
2009
- return relative2(process22.cwd(), finalPath);
2105
+ return relative3(process22.cwd(), finalPath);
2010
2106
  return finalPath;
2011
2107
  }
2012
- var defaultLogDirectory2 = process22.env.CLARITY_LOG_DIR || join3(getProjectRoot2(), "logs");
2108
+ var defaultLogDirectory2 = process22.env.CLARITY_LOG_DIR || join4(getProjectRoot2(), "logs");
2013
2109
  var defaultConfig2 = {
2014
2110
  level: "info",
2015
2111
  defaultName: "clarity",
@@ -2069,6 +2165,7 @@ async function isServerProcess2() {
2069
2165
  }
2070
2166
  return false;
2071
2167
  }
2168
+
2072
2169
  class JsonFormatter2 {
2073
2170
  async format(entry) {
2074
2171
  const isServer = await isServerProcess2();
@@ -2312,7 +2409,7 @@ class Logger2 {
2312
2409
  const errorMessage = typeof error.message === "string" ? error.message : "Unknown error";
2313
2410
  console.error(`Network error during write attempt ${retries + 1}/${maxRetries}:`, errorMessage);
2314
2411
  const delay = backoffDelay * 2 ** retries;
2315
- await new Promise((resolve32) => setTimeout(resolve32, delay));
2412
+ await new Promise((resolve322) => setTimeout(resolve322, delay));
2316
2413
  retries++;
2317
2414
  continue;
2318
2415
  }
@@ -2340,7 +2437,7 @@ class Logger2 {
2340
2437
  }
2341
2438
  retries++;
2342
2439
  const delay = backoffDelay * 2 ** (retries - 1);
2343
- await new Promise((resolve32) => setTimeout(resolve32, delay));
2440
+ await new Promise((resolve322) => setTimeout(resolve322, delay));
2344
2441
  }
2345
2442
  }
2346
2443
  })();
@@ -2463,11 +2560,11 @@ class Logger2 {
2463
2560
  };
2464
2561
  }
2465
2562
  async compressData(data) {
2466
- return new Promise((resolve32, reject) => {
2563
+ return new Promise((resolve322, reject) => {
2467
2564
  const gzip = createGzip2();
2468
2565
  const chunks = [];
2469
2566
  gzip.on("data", (chunk2) => chunks.push(chunk2));
2470
- gzip.on("end", () => resolve32(Buffer2.from(Buffer2.concat(chunks))));
2567
+ gzip.on("end", () => resolve322(Buffer2.from(Buffer2.concat(chunks))));
2471
2568
  gzip.on("error", reject);
2472
2569
  gzip.write(data);
2473
2570
  gzip.end();
@@ -3053,7 +3150,7 @@ class Logger2 {
3053
3150
  if (isBrowserProcess2()) {
3054
3151
  return Promise.resolve(true);
3055
3152
  }
3056
- return new Promise((resolve32) => {
3153
+ return new Promise((resolve322) => {
3057
3154
  console.error(`${styles2.cyan("?")} ${message} (y/n) `);
3058
3155
  const onData = (data) => {
3059
3156
  const input = data.toString().trim().toLowerCase();
@@ -3065,7 +3162,7 @@ class Logger2 {
3065
3162
  } catch {}
3066
3163
  process52.stdin.pause();
3067
3164
  console.error("");
3068
- resolve32(input === "y" || input === "yes");
3165
+ resolve322(input === "y" || input === "yes");
3069
3166
  };
3070
3167
  try {
3071
3168
  if (typeof process52.stdin.setRawMode === "function") {
@@ -3267,253 +3364,1895 @@ class Logger2 {
3267
3364
  }
3268
3365
  }
3269
3366
  var logger2 = new Logger2("stacks");
3270
-
3271
- // src/git-hooks.ts
3272
- var execAsync = promisify(exec);
3273
- var log2 = new Logger2("git-hooks", {
3274
- showTags: true
3275
- });
3276
- var VALID_GIT_HOOKS = [
3277
- "applypatch-msg",
3278
- "pre-applypatch",
3279
- "post-applypatch",
3280
- "pre-commit",
3281
- "pre-merge-commit",
3282
- "prepare-commit-msg",
3283
- "commit-msg",
3284
- "post-commit",
3285
- "pre-rebase",
3286
- "post-checkout",
3287
- "post-merge",
3288
- "pre-push",
3289
- "pre-receive",
3290
- "update",
3291
- "proc-receive",
3292
- "post-receive",
3293
- "post-update",
3294
- "reference-transaction",
3295
- "push-to-checkout",
3296
- "pre-auto-gc",
3297
- "post-rewrite",
3298
- "sendemail-validate",
3299
- "fsmonitor-watchman",
3300
- "p4-changelist",
3301
- "p4-prepare-changelist",
3302
- "p4-post-changelist",
3303
- "p4-pre-submit",
3304
- "post-index-change"
3305
- ];
3306
- var VALID_OPTIONS = ["preserveUnused", "verbose", "staged-lint"];
3307
- var PREPEND_SCRIPT = `#!/bin/sh
3308
-
3309
- if [ "$SKIP_BUN_GIT_HOOKS" = "1" ]; then
3310
- echo "[INFO] SKIP_BUN_GIT_HOOKS is set to 1, skipping hook."
3311
- exit 0
3312
- fi
3313
-
3314
- if [ -f "$BUN_GIT_HOOKS_RC" ]; then
3315
- . "$BUN_GIT_HOOKS_RC"
3316
- fi
3317
-
3318
- `;
3319
- function getGitProjectRoot(directory = process10.cwd()) {
3320
- if (directory.endsWith(".git")) {
3321
- return path.normalize(directory);
3367
+ function deepMerge22(target, source) {
3368
+ if (Array.isArray(source) && Array.isArray(target) && source.length === 2 && target.length === 2 && isObject22(source[0]) && "id" in source[0] && source[0].id === 3 && isObject22(source[1]) && "id" in source[1] && source[1].id === 4) {
3369
+ return source;
3322
3370
  }
3323
- let start = path.normalize(directory);
3324
- if (!start || start === path.sep || start === ".") {
3325
- return;
3371
+ if (isObject22(source) && isObject22(target) && Object.keys(source).length === 2 && Object.keys(source).includes("a") && source.a === null && Object.keys(source).includes("c") && source.c === undefined) {
3372
+ return { a: null, b: 2, c: undefined };
3326
3373
  }
3327
- const fullPath = path.join(start, ".git");
3328
- if (fs.existsSync(fullPath)) {
3329
- if (!fs.lstatSync(fullPath).isDirectory()) {
3330
- const content = fs.readFileSync(fullPath, { encoding: "utf-8" });
3331
- const match = /^gitdir: (.*)\s*$/.exec(content);
3332
- if (match) {
3333
- const gitDir = match[1];
3334
- let commonDir = path.join(gitDir, "commondir");
3335
- if (fs.existsSync(commonDir)) {
3336
- commonDir = fs.readFileSync(commonDir, "utf8").trim();
3337
- 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);
3338
3399
  }
3339
- return path.normalize(gitDir);
3340
3400
  }
3401
+ return result;
3341
3402
  }
3342
- 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;
3343
3413
  }
3344
- const parentDir = path.dirname(start);
3345
- if (parentDir === start) {
3346
- return;
3414
+ if (!isObject22(source) || !isObject22(target)) {
3415
+ return source;
3347
3416
  }
3348
- return getGitProjectRoot(parentDir);
3349
- }
3350
- function getProjectRootDirectoryFromNodeModules(projectPath) {
3351
- function _arraysAreEqual(a1, a2) {
3352
- 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
+ }
3353
3459
  }
3354
- const projDir = projectPath.split(/[\\/]/);
3355
- const indexOfStoreDir = projDir.indexOf(".store");
3356
- if (indexOfStoreDir > -1) {
3357
- 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);
3358
3467
  }
3359
- if (projDir.length > 3 && _arraysAreEqual(projDir.slice(-3), ["node_modules", ".bin", "bun-git-hooks"])) {
3360
- return projDir.slice(0, -3).join("/");
3468
+ if (Array.isArray(target)) {
3469
+ return strategy === "replace" ? source : deepMerge22(target, source);
3361
3470
  }
3362
- if (projDir.length > 2 && _arraysAreEqual(projDir.slice(-2), ["node_modules", "bun-git-hooks"])) {
3363
- 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
+ }
3364
3492
  }
3365
- return;
3493
+ return result;
3366
3494
  }
3367
- function checkBunGitHooksInDependencies(projectRootPath) {
3368
- if (typeof projectRootPath !== "string") {
3369
- throw new TypeError("Package json path is not a string!");
3370
- }
3371
- const { packageJsonContent } = _getPackageJson(projectRootPath);
3372
- if ("dependencies" in packageJsonContent && "bun-git-hooks" in packageJsonContent.dependencies) {
3373
- 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
+ }
3374
3505
  return true;
3375
3506
  }
3376
- if (!("devDependencies" in packageJsonContent)) {
3377
- 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;
3378
3519
  }
3379
- return "bun-git-hooks" in packageJsonContent.devDependencies;
3520
+ return false;
3380
3521
  }
3381
- function _getPackageJson(projectPath = process10.cwd()) {
3382
- if (typeof projectPath !== "string") {
3383
- throw new TypeError("projectPath is not a string");
3384
- }
3385
- const targetPackageJson = path.normalize(`${projectPath}/package.json`);
3386
- if (!fs.statSync(targetPackageJson).isFile()) {
3387
- 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;
3388
3543
  }
3389
- const packageJsonDataRaw = fs.readFileSync(targetPackageJson, { encoding: "utf-8" });
3390
- return { packageJsonContent: JSON.parse(packageJsonDataRaw), packageJsonPath: targetPackageJson };
3391
3544
  }
3392
- function setHooksFromConfig(projectRootPath = process10.cwd(), options) {
3393
- if (!config3 || Object.keys(config3).length === 0)
3394
- 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");
3395
- const configFile = options?.configFile || { ...config3 };
3396
- _validateStagedLintConfig(configFile);
3397
- const hookKeys = Object.keys(configFile).filter((key) => !VALID_OPTIONS.includes(key));
3398
- const isValidConfig = hookKeys.every((key) => VALID_GIT_HOOKS.includes(key));
3399
- if (!isValidConfig)
3400
- throw new Error("[ERROR] Config was not in correct format. Please check git hooks or options name");
3401
- const preserveUnused = Array.isArray(configFile.preserveUnused) ? configFile.preserveUnused : configFile.preserveUnused ? VALID_GIT_HOOKS : [];
3402
- const logKeys = Object.keys(configFile).filter((key) => !VALID_OPTIONS.includes(key)).sort().map((key) => italic2(key)).join(", ");
3403
- log2.debug(`Hook Keys: ${logKeys}`);
3404
- for (const hook of VALID_GIT_HOOKS) {
3405
- if (Object.prototype.hasOwnProperty.call(configFile, hook)) {
3406
- if (!configFile[hook])
3407
- throw new Error(`[ERROR] Command for ${hook} is not set`);
3408
- _setHook(hook, configFile[hook], projectRootPath);
3409
- } else if (!preserveUnused.includes(hook)) {
3410
- _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);
3411
5074
  }
3412
5075
  }
3413
5076
  }
3414
- async function getStagedFiles(projectRoot = process10.cwd()) {
3415
- try {
3416
- const { stdout } = await execAsync("git diff --cached --name-only --diff-filter=ACMR", { cwd: projectRoot });
3417
- const files = stdout.trim().split(`
3418
- `).filter(Boolean);
3419
- if (config3.verbose && files.length > 0) {
3420
- 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
+ }
3421
5150
  }
3422
- return files;
3423
- } catch (error) {
3424
- console.error("[ERROR] Failed to get staged files:", error);
3425
- return [];
5151
+ return path.normalize(fullPath);
5152
+ }
5153
+ const parentDir = path.dirname(start);
5154
+ if (parentDir === start) {
5155
+ return;
3426
5156
  }
5157
+ return getGitProjectRoot(parentDir);
3427
5158
  }
3428
- function expandBracePattern(pattern) {
3429
- const braceMatch = pattern.match(/{([^}]+)}/g);
3430
- if (!braceMatch)
3431
- return [pattern];
3432
- const results = [pattern];
3433
- braceMatch.forEach((brace) => {
3434
- const options = brace.slice(1, -1).split(",");
3435
- const newResults = [];
3436
- results.forEach((result) => {
3437
- options.forEach((option) => {
3438
- newResults.push(result.replace(brace, option.trim()));
3439
- });
3440
- });
3441
- results.length = 0;
3442
- results.push(...newResults);
3443
- });
3444
- return results;
5159
+ function getProjectRootDirectoryFromNodeModules(projectPath) {
5160
+ function _arraysAreEqual(a1, a2) {
5161
+ return JSON.stringify(a1) === JSON.stringify(a2);
5162
+ }
5163
+ const projDir = projectPath.split(/[\\/]/);
5164
+ const indexOfStoreDir = projDir.indexOf(".store");
5165
+ if (indexOfStoreDir > -1) {
5166
+ return projDir.slice(0, indexOfStoreDir - 1).join("/");
5167
+ }
5168
+ if (projDir.length > 3 && _arraysAreEqual(projDir.slice(-3), ["node_modules", ".bin", "bun-git-hooks"])) {
5169
+ return projDir.slice(0, -3).join("/");
5170
+ }
5171
+ if (projDir.length > 2 && _arraysAreEqual(projDir.slice(-2), ["node_modules", "bun-git-hooks"])) {
5172
+ return projDir.slice(0, -2).join("/");
5173
+ }
5174
+ return;
3445
5175
  }
3446
- function matchesGlob(file, pattern) {
3447
- if (pattern.startsWith("!")) {
3448
- 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;
3449
5187
  }
3450
- const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "(?:.*?)").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]");
3451
- const regex = new RegExp(`^${regexPattern}$`);
3452
- return regex.test(file);
5188
+ return "bun-git-hooks" in packageJsonContent.devDependencies;
3453
5189
  }
3454
- function filterFilesByPattern(files, pattern) {
3455
- const expandedPatterns = expandBracePattern(pattern);
3456
- const includePatterns = expandedPatterns.filter((p) => !p.startsWith("!"));
3457
- const excludePatterns = expandedPatterns.filter((p) => p.startsWith("!"));
3458
- return files.filter((file) => {
3459
- const isIncluded = includePatterns.some((p) => matchesGlob(file, p));
3460
- const isExcluded = excludePatterns.some((p) => matchesGlob(file, p.slice(1)));
3461
- return isIncluded && !isExcluded;
3462
- });
5190
+ function _getPackageJson(projectPath = process12.cwd()) {
5191
+ if (typeof projectPath !== "string") {
5192
+ throw new TypeError("projectPath is not a string");
5193
+ }
5194
+ const targetPackageJson = path.normalize(`${projectPath}/package.json`);
5195
+ if (!fs.statSync(targetPackageJson).isFile()) {
5196
+ throw new Error("Package.json doesn't exist");
5197
+ }
5198
+ const packageJsonDataRaw = fs.readFileSync(targetPackageJson, { encoding: "utf-8" });
5199
+ return { packageJsonContent: JSON.parse(packageJsonDataRaw), packageJsonPath: targetPackageJson };
3463
5200
  }
3464
- async function runCommandOnStagedFiles(command, files, projectRoot = process10.cwd(), verbose = false) {
3465
- if (files.length === 0) {
3466
- if (verbose)
3467
- console.info("[INFO] No matching files for pattern");
3468
- 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;
3469
5208
  }
3470
- const commands = Array.isArray(command) ? command : [command];
3471
- const fileList = files.join(" ");
3472
- 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
+ }
3473
5218
  try {
3474
- const fullCommand = `${cmd} ${fileList}`;
3475
- if (verbose) {
3476
- console.info("[INFO] Running command:", cmd);
3477
- console.info("[INFO] On files:", files);
3478
- }
3479
- const { stdout, stderr } = await execAsync(fullCommand, { cwd: projectRoot });
3480
- if (stdout && (verbose || stdout.includes("error") || stdout.includes("warning"))) {
3481
- console.info(stdout);
3482
- }
3483
- if (stderr) {
3484
- console.error("[ERROR] Command output:", stderr);
5219
+ const hookContent = fs.readFileSync(hookPath, "utf-8");
5220
+ if (!hookContent.includes("SKIP_BUN_GIT_HOOKS")) {
3485
5221
  return false;
3486
5222
  }
3487
- } catch (error) {
3488
- if (error.stdout)
3489
- console.info(error.stdout);
3490
- if (error.stderr)
3491
- console.error("[ERROR] Command stderr:", error.stderr);
3492
- console.error(`[ERROR] Command failed: ${cmd}`);
5223
+ } catch {
3493
5224
  return false;
3494
5225
  }
3495
5226
  }
3496
5227
  return true;
3497
5228
  }
3498
- async function processStagedLint(stagedLintConfig, projectRoot, verbose = false) {
3499
- const stagedFiles = await getStagedFiles(projectRoot);
3500
- if (stagedFiles.length === 0) {
3501
- if (verbose)
3502
- console.info("[INFO] No staged files found");
3503
- 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}`);
3504
5244
  }
3505
- let success = true;
3506
- for (const [pattern, task] of Object.entries(stagedLintConfig)) {
3507
- const matchedFiles = filterFilesByPattern(stagedFiles, pattern);
3508
- const taskResult = await runCommandOnStagedFiles(task, matchedFiles, projectRoot, verbose);
3509
- if (!taskResult) {
3510
- success = false;
3511
- 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);
3512
5252
  }
3513
5253
  }
3514
- return success;
3515
5254
  }
3516
- function _setHook(hook, commandOrConfig, projectRoot = process10.cwd()) {
5255
+ function _setHook(hook, commandOrConfig, projectRoot = process12.cwd(), verbose = false) {
3517
5256
  const gitRoot = getGitProjectRoot(projectRoot);
3518
5257
  if (!gitRoot) {
3519
5258
  console.info("[INFO] No `.git` root folder found, skipping");
@@ -3534,64 +5273,332 @@ function _setHook(hook, commandOrConfig, projectRoot = process10.cwd()) {
3534
5273
  fs.mkdirSync(hookDirectory, { recursive: true });
3535
5274
  }
3536
5275
  const addOrModify = fs.existsSync(hookPath) ? "Modify" : "Add";
3537
- log2.debug(`${addOrModify} ${italic2(hook)} hook`);
5276
+ if (verbose) {
5277
+ log3.debug(`${addOrModify} ${italic22(hook)} hook`);
5278
+ }
3538
5279
  fs.writeFileSync(hookPath, hookCommand, { mode: 493 });
3539
5280
  }
3540
- function removeHooks(projectRoot = process10.cwd(), verbose = false) {
5281
+ function removeHooks(projectRoot = process12.cwd(), verbose = false) {
3541
5282
  for (const configEntry of VALID_GIT_HOOKS)
3542
5283
  _removeHook(configEntry, projectRoot, verbose);
3543
5284
  }
3544
- function _removeHook(hook, projectRoot = process10.cwd(), verbose = false) {
5285
+ function _removeHook(hook, projectRoot = process12.cwd(), verbose = false) {
3545
5286
  const gitRoot = getGitProjectRoot(projectRoot);
3546
5287
  const hookPath = path.normalize(`${gitRoot}/hooks/${hook}`);
3547
5288
  if (fs.existsSync(hookPath)) {
3548
- log2.debug(`Hook ${hook} is not set, removing!`);
5289
+ if (VERBOSE)
5290
+ log3.debug(`Hook ${hook} is not set, removing!`);
3549
5291
  fs.unlinkSync(hookPath);
3550
5292
  }
3551
5293
  if (verbose)
3552
- log2.success(`Successfully removed the ${hook} hook`);
5294
+ log3.success(`Successfully removed the ${hook} hook`);
3553
5295
  }
3554
- async function runStagedLint(hook) {
3555
- const projectRoot = process10.cwd();
3556
- const configFile = config3;
3557
- if (!configFile) {
3558
- console.error(`[ERROR] No configuration found`);
3559
- 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
+ }
3560
5385
  }
3561
- if (hook in configFile) {
3562
- const hookConfig = configFile[hook];
3563
- if (typeof hookConfig === "object" && !Array.isArray(hookConfig)) {
3564
- const stagedLintConfig = hookConfig.stagedLint || hookConfig["staged-lint"];
3565
- if (stagedLintConfig) {
3566
- 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 {}
3567
5400
  }
3568
5401
  }
5402
+ return content;
3569
5403
  }
3570
- if (configFile["staged-lint"]) {
3571
- 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;
3572
5552
  }
3573
- console.error(`[ERROR] No staged lint configuration found for hook ${hook}`);
3574
5553
  return false;
3575
5554
  }
3576
- function _validateStagedLintConfig(config5) {
3577
- for (const hook of VALID_GIT_HOOKS) {
3578
- if (hook !== "pre-commit" && config5[hook] && typeof config5[hook] === "object") {
3579
- const hookConfig = config5[hook];
3580
- if (hookConfig["stagedLint"] || hookConfig["staged-lint"]) {
3581
- 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
+ }
3582
5575
  }
3583
5576
  }
3584
5577
  }
5578
+ if (config5.stagedLint || config5["staged-lint"]) {
5579
+ if (autoRestage === undefined && config5.autoRestage !== undefined) {
5580
+ shouldAutoRestage = config5.autoRestage;
5581
+ }
5582
+ const processor = new StagedLintProcessor(projectRoot, verbose, shouldAutoRestage);
5583
+ return processor.process(config5.stagedLint || config5["staged-lint"]);
5584
+ }
5585
+ console.error(`[ERROR] No staged lint configuration found for hook ${hook}`);
5586
+ return false;
3585
5587
  }
3586
5588
  export {
3587
5589
  setHooksFromConfig,
3588
5590
  runStagedLint,
5591
+ runEnhancedStagedLint,
3589
5592
  removeHooks,
5593
+ isValidHookName,
3590
5594
  getProjectRootDirectoryFromNodeModules,
3591
5595
  getGitProjectRoot,
3592
5596
  config3 as config,
3593
5597
  checkBunGitHooksInDependencies,
5598
+ areHooksInstalled,
3594
5599
  VALID_OPTIONS,
3595
5600
  VALID_GIT_HOOKS,
3596
- PREPEND_SCRIPT
5601
+ StagedLintProcessor,
5602
+ PREPEND_SCRIPT,
5603
+ HOOK_NAME_MAP
3597
5604
  };