75 lines
2.3 KiB
JavaScript
75 lines
2.3 KiB
JavaScript
|
|
/**
|
|||
|
|
* 共享规则加载模块 (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 };
|