104 lines
3.8 KiB
TypeScript
104 lines
3.8 KiB
TypeScript
import { app } from "electron";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import type { Catalog, CatalogComponent } from "../shared/types";
|
|
|
|
let cached: Catalog | null = null;
|
|
let excludedIds: Set<string> | null = null;
|
|
let duplicateMap: Map<string, string> | null = null;
|
|
|
|
function resolveCatalogPath(): string {
|
|
const candidates = [
|
|
process.env.RPA_CATALOG_PATH,
|
|
path.join(process.resourcesPath, "catalog.json"),
|
|
path.join(app.getAppPath(), "resources", "catalog.json"),
|
|
path.join(__dirname, "../../resources/catalog.json")
|
|
].filter(Boolean) as string[];
|
|
for (const candidate of candidates) {
|
|
if (fs.existsSync(candidate)) return candidate;
|
|
}
|
|
throw new Error(`catalog.json not found; tried: ${candidates.join(", ")}`);
|
|
}
|
|
|
|
function duplicatesPath(): string {
|
|
const candidates = [
|
|
process.env.RPA_DUPLICATE_PATH,
|
|
path.join(process.resourcesPath, "capability-duplicates.json"),
|
|
path.join(app.getAppPath(), "resources", "capability-duplicates.json"),
|
|
path.join(__dirname, "../../resources/capability-duplicates.json")
|
|
].filter(Boolean) as string[];
|
|
return candidates.find((candidate) => fs.existsSync(candidate)) || "";
|
|
}
|
|
|
|
function loadDuplicateMap(): Map<string, string> {
|
|
const map = new Map<string, string>();
|
|
const file = duplicatesPath();
|
|
if (!file) return map;
|
|
try {
|
|
const parsed = JSON.parse(fs.readFileSync(file, "utf8")) as { decisions?: Array<{ id?: string; status?: string; canonical?: string }> };
|
|
for (const decision of parsed.decisions || []) {
|
|
if (decision.id && decision.status === "hide" && decision.canonical) map.set(decision.id, decision.canonical);
|
|
}
|
|
} catch {
|
|
// 审计文件损坏时保留完整目录
|
|
}
|
|
return map;
|
|
}
|
|
|
|
function excludedPath(): string {
|
|
const candidates = [
|
|
process.env.RPA_EXCLUDED_PATH,
|
|
path.join(process.resourcesPath, "excluded.json"),
|
|
path.join(app.getAppPath(), "resources", "excluded.json"),
|
|
path.join(__dirname, "../../resources/excluded.json")
|
|
].filter(Boolean) as string[];
|
|
const found = candidates.find((candidate) => fs.existsSync(candidate));
|
|
return found || "";
|
|
}
|
|
|
|
function loadExcludedIds(): Set<string> {
|
|
if (excludedIds) return excludedIds;
|
|
excludedIds = new Set();
|
|
const file = excludedPath();
|
|
if (!file) return excludedIds;
|
|
try {
|
|
const parsed = JSON.parse(fs.readFileSync(file, "utf8")) as { ids?: string[] };
|
|
for (const id of parsed.ids || []) excludedIds.add(id);
|
|
} catch {
|
|
// 备份文件损坏时不阻塞目录加载
|
|
}
|
|
return excludedIds;
|
|
}
|
|
|
|
export function catalogPath(): string {
|
|
return resolveCatalogPath();
|
|
}
|
|
|
|
export function loadCatalog(): Catalog {
|
|
if (cached) return cached;
|
|
const raw = fs.readFileSync(catalogPath(), "utf8");
|
|
const catalog = JSON.parse(raw) as Catalog;
|
|
const excluded = loadExcludedIds();
|
|
const duplicates = duplicateMap || loadDuplicateMap();
|
|
duplicateMap = duplicates;
|
|
catalog.components = catalog.components
|
|
.filter((component) => !excluded.has(component.id))
|
|
.map((component: CatalogComponent) => ({
|
|
...component,
|
|
duplicateOf: duplicates.get(component.id)
|
|
}));
|
|
const visibleComponents = catalog.components.filter((component) => !component.duplicateOf);
|
|
catalog.counts.total = visibleComponents.length;
|
|
catalog.counts.vendor = visibleComponents.filter((component) => component.kind === "vendor").length;
|
|
catalog.counts.native = visibleComponents.filter((component) => component.kind === "native").length;
|
|
catalog.counts.triggers = visibleComponents.filter((component) => component.kind === "trigger").length;
|
|
catalog.counts.fields = visibleComponents.reduce((sum, component) => sum + component.fields.length, 0);
|
|
for (const group of catalog.groups) group.count = visibleComponents.filter((component) => component.group === group.id).length;
|
|
cached = catalog;
|
|
return catalog;
|
|
}
|
|
|
|
export function invalidateCatalog(): void {
|
|
cached = null;
|
|
}
|