import { classifyRisk, detectMediaKind, inferOwnerHint, isCorePublicPath, isStorageAreaPath } from "./path-utils.js"; function compareOwner(sourceScope, targetOwner) { if (!sourceScope) return "unknown"; if ((sourceScope.ownerType === "world") && (targetOwner.ownerType === "system") && (sourceScope.systemId === targetOwner.ownerId)) { return "same-system"; } if ((sourceScope.ownerType === "world") && (targetOwner.ownerType === "public") && ["cards", "icons", "nue", "sounds", "ui"].includes(targetOwner.ownerId)) { return "core-public"; } if ((sourceScope.ownerType === "module") && (targetOwner.ownerType === "module")) { return sourceScope.ownerId === targetOwner.ownerId ? "same-package" : "foreign-module"; } if ((sourceScope.ownerType === "system") && (targetOwner.ownerType === "system")) { return sourceScope.ownerId === targetOwner.ownerId ? "same-package" : "cross-package"; } if ((sourceScope.ownerType === "world") && (targetOwner.ownerType === "world")) { return sourceScope.ownerId === targetOwner.ownerId ? "same-world" : "cross-world"; } if ((sourceScope.ownerType === "world") && ((targetOwner.ownerType === "module") || (targetOwner.ownerType === "system"))) { return "non-package-to-package"; } if ((sourceScope.ownerType === "world") && (targetOwner.ownerType === "public")) { return "risky-public"; } if (((sourceScope.ownerType === "module") || (sourceScope.ownerType === "system")) && ((targetOwner.ownerType === "module") || (targetOwner.ownerType === "system")) && ((sourceScope.ownerType !== targetOwner.ownerType) || (sourceScope.ownerId !== targetOwner.ownerId))) { return "cross-package"; } return "allowed"; } export function buildFindings({ files, references }) { const fileByLocator = new Map(files.map(file => [file.locator, file])); const refsByLocator = new Map(); const findings = []; for (const reference of references) { const normalized = reference.normalized; if (!normalized) continue; const bucket = refsByLocator.get(normalized.locator) ?? []; bucket.push(reference); refsByLocator.set(normalized.locator, bucket); } for (const reference of references) { const normalized = reference.normalized; if (!normalized) continue; const file = fileByLocator.get(normalized.locator); if (!file) { if (reference.sourceScope?.ownerType !== "world") continue; findings.push({ kind: "broken-reference", severity: reference.sourceScope?.ownerType === "world" ? "high" : "warning", target: normalized, source: reference, reason: `Referenced file ${normalized.locator} does not exist in the scanned roots.`, recommendation: "Check whether the file was moved, deleted, or should be copied into a stable location.", confidence: "high" }); continue; } const ownerRelation = compareOwner(reference.sourceScope, file.ownerHint); if (ownerRelation === "non-package-to-package") { if (isAnchoredInOwningPackage(file, refsByLocator.get(normalized.locator) ?? [])) continue; findings.push({ kind: "non-package-to-package-reference", severity: "high", target: normalized, source: reference, reason: `${reference.sourceScope.ownerType}:${reference.sourceScope.ownerId} references package-owned storage ${normalized.locator}, but the asset is not visibly referenced by its owning package.`, recommendation: "Review whether the file was manually placed into the package folder. If the package does not use it itself, prefer moving it into user-controlled storage.", confidence: "high" }); } else if (ownerRelation === "risky-public") { if (reference.sourceScope?.ownerType !== "world") continue; findings.push({ kind: "risky-public-reference", severity: "high", target: normalized, source: reference, reason: `${reference.sourceScope.ownerType}:${reference.sourceScope.ownerId} references release-public storage ${normalized.locator}.`, recommendation: "Prefer copying the asset into world or user-controlled storage.", confidence: "high" }); } } for (const file of files) { if (detectMediaKind(file.path) === "other") continue; const refs = refsByLocator.get(file.locator) ?? []; if (refs.length) continue; if (!shouldReportOrphan(file, references)) continue; const severity = (file.riskClass === "package-module") || (file.riskClass === "package-system") || (file.riskClass === "release-public") ? "warning" : "info"; findings.push({ kind: "orphan-file", severity, target: file, source: null, reason: `No incoming media reference was found for ${file.locator}.`, recommendation: severity === "warning" ? "Review whether the file is safe to remove or should be moved into a stable storage location." : "Review whether the file is intentionally kept as reserve content.", confidence: "medium" }); } return findings; } function isAnchoredInOwningPackage(file, references) { const ownerType = file.ownerHint.ownerType; const ownerId = file.ownerHint.ownerId; if (!["module", "system"].includes(ownerType) || !ownerId) return false; return references.some(reference => (reference.sourceScope?.ownerType === ownerType) && (reference.sourceScope?.ownerId === ownerId) ); } function shouldReportOrphan(file, references) { if (file.riskClass === "stable-world") { const worldId = file.ownerHint.ownerId; return references.some(reference => reference.sourceScope?.ownerType === "world" && reference.sourceScope.ownerId === worldId); } if ((file.riskClass === "package-module") || (file.riskClass === "package-system")) { return isStorageAreaPath(file); } if (file.riskClass === "release-public") { return !isCorePublicPath(file); } return false; } export function createFileRecord(locator, size = null) { return { ...locator, basename: locator.path.split("/").pop() ?? locator.path, extension: locator.path.includes(".") ? locator.path.split(".").pop().toLowerCase() : "", size, mediaKind: detectMediaKind(locator.path), ownerHint: inferOwnerHint(locator), riskClass: classifyRisk(locator), exists: true }; }