Files
brain/tools/registry-load.mjs
T

83 lines
2.7 KiB
JavaScript

// tools/registry-load.mjs
// Pure module — загрузка и индексация реестра узлов.
// Никаких сетевых вызовов, никакого фс-исключения кроме чтения.
import { readFileSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import yaml from 'js-yaml';
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
const __dirname = dirname(fileURLToPath(import.meta.url));
const DEFAULT_REGISTRY_PATH = join(__dirname, '..', 'docs', 'registry', 'nodes.yaml');
const DEFAULT_SCHEMA_PATH = join(__dirname, '..', 'docs', 'registry', 'schema.json');
let _cached = null;
export function loadRegistry({ registryPath = DEFAULT_REGISTRY_PATH, schemaPath = DEFAULT_SCHEMA_PATH, useCache = true } = {}) {
if (useCache && _cached) return _cached;
const schema = JSON.parse(readFileSync(schemaPath, 'utf-8'));
const data = yaml.load(readFileSync(registryPath, 'utf-8'));
const ajv = new Ajv({ allErrors: true });
addFormats(ajv);
const validate = ajv.compile(schema);
if (!validate(data)) {
throw new Error(`Registry schema violation:\n${JSON.stringify(validate.errors, null, 2)}`);
}
const indexById = new Map();
for (const node of data.nodes) indexById.set(node.id, node);
const indexByTrigger = new Map();
for (const node of data.nodes) {
if (node.status !== 'active') continue;
for (const t of node.triggers || []) {
const key = t.classification ? `cls:${t.classification}` :
t.keyword ? `kw:${t.keyword.toLowerCase()}` :
t.file_pattern ? `fp:${t.file_pattern}` : null;
if (!key) continue;
if (!indexByTrigger.has(key)) indexByTrigger.set(key, []);
indexByTrigger.get(key).push({ node, weight: t.weight ?? 1.0 });
}
}
const result = {
nodes: data.nodes,
chains: data.chains,
version: data.version,
indexById,
indexByTrigger,
};
if (useCache) _cached = result;
return result;
}
export function clearCache() { _cached = null; }
export function findByClassification(registry, classification) {
return (registry.indexByTrigger.get(`cls:${classification}`) || [])
.sort((a, b) => b.weight - a.weight);
}
export function findByKeyword(registry, keyword) {
return (registry.indexByTrigger.get(`kw:${keyword.toLowerCase()}`) || [])
.sort((a, b) => b.weight - a.weight);
}
export function findActiveNodes(registry) {
return registry.nodes.filter(n => n.status === 'active');
}
export function findChainsByNode(registry, nodeIdOrSlug) {
const chains = [];
for (const [chainId, chain] of Object.entries(registry.chains)) {
if (chain.sequence.some(s => s === nodeIdOrSlug)) {
chains.push({ chainId, ...chain });
}
}
return chains;
}