/** * 共享规则加载模块 (M15) * * 替代 block-dangerous-commands.js 和 block-sensitive-files.js 中 * 重复的 loadCompiledCache + compilePatterns + loadRules 三件套。 */ const fs = require('fs'); const path = require('path'); const CLAUDE_ROOT = require('./root.js'); const RULES_DIR = path.join(CLAUDE_ROOT, 'hooks', 'rules'); const COMPILED_CACHE = path.join(RULES_DIR, 'rules-compiled.json'); let _compiledCache = null; /** * 加载编译缓存(带 mtime 新鲜度检查) * @returns {object|null} */ function loadCompiledCache() { try { if (!fs.existsSync(COMPILED_CACHE)) return null; const raw = JSON.parse(fs.readFileSync(COMPILED_CACHE, 'utf8')); if (!raw.sources) return raw; // mtime 新鲜度检查 for (const [filePath, savedMtime] of Object.entries(raw.sources)) { if (!fs.existsSync(filePath)) return null; const stat = fs.statSync(filePath); if (stat.mtimeMs > savedMtime) return null; } _compiledCache = raw; return raw; } catch { return null; } } /** * 编译正则模式数组 * @param {Array} patterns - [{regex, flags, reason}] * @returns {Array} [{pattern: RegExp, reason: string}] */ function compilePatterns(patterns) { return (patterns || []).map(p => ({ pattern: new RegExp(p.regex, (p.flags || '').replace(/g/g, '')), reason: p.reason, })); } /** * 加载规则文件(优先编译缓存,回退到源文件,最后使用硬编码后备) * @param {string} filename - 规则文件名 (如 'deny-patterns.json') * @param {object} [fallbackRules] - 硬编码后备规则 {key: [patterns]} * @returns {Array} 编译后的规则数组 */ function loadRules(filename, fallbackRules) { const key = filename.replace('.json', ''); const cache = loadCompiledCache(); if (cache && cache.rules && cache.rules[key]) { return compilePatterns(cache.rules[key]); } try { const filePath = path.join(RULES_DIR, filename); const data = JSON.parse(fs.readFileSync(filePath, 'utf8')); return compilePatterns(data.patterns); } catch { // 规则文件丢失时使用硬编码后备 if (fallbackRules && fallbackRules[key]) { return compilePatterns(fallbackRules[key]); } return []; } } module.exports = { loadCompiledCache, compilePatterns, loadRules, RULES_DIR };