Files
kosmos-storage-audit/scripts/core/reference-extractor.js
2026-04-20 20:48:37 +00:00

107 lines
3.2 KiB
JavaScript

import { createFileLocator, hasWildcard, isMediaPath, normalizePath, parseStoragePath } from "./path-utils.js";
const ATTRIBUTE_PATTERNS = [
/\b(?:src|href|poster)\s*=\s*["']([^"'<>]+)["']/gi,
/url\(\s*["']?([^"')]+)["']?\s*\)/gi
];
export function collectStringCandidates(value, visit) {
if (typeof value === "string") {
visit(value);
return;
}
if (Array.isArray(value)) {
for (const entry of value) collectStringCandidates(entry, visit);
return;
}
if ((value !== null) && (typeof value === "object")) {
for (const entry of Object.values(value)) collectStringCandidates(entry, visit);
}
}
export function extractReferencesFromValue(value, source) {
const references = [];
collectStringCandidates(value, candidate => {
const direct = resolveReference(candidate, source);
if (direct && isMediaPath(direct.path)) {
references.push({
sourceType: source.sourceType,
sourceScope: source.sourceScope,
sourceLabel: source.sourceLabel,
sourceName: source.sourceName,
sourceUuid: source.sourceUuid,
rawValue: candidate,
normalized: {
...direct,
targetKind: hasWildcard(direct.path) ? "wildcard" : "local-file"
}
});
}
for (const pattern of ATTRIBUTE_PATTERNS) {
pattern.lastIndex = 0;
let match;
while ((match = pattern.exec(candidate))) {
const nested = resolveReference(match[1], source);
if (!nested || !isMediaPath(nested.path)) continue;
references.push({
sourceType: source.sourceType,
sourceScope: source.sourceScope,
sourceLabel: source.sourceLabel,
sourceName: source.sourceName,
sourceUuid: source.sourceUuid,
rawValue: match[1],
normalized: {
...nested,
targetKind: hasWildcard(nested.path) ? "wildcard" : "local-file"
}
});
}
}
});
return dedupeReferences(references);
}
function resolveReference(rawValue, source) {
const direct = parseStoragePath(rawValue);
if (direct) return direct;
return resolvePackageRelativeReference(rawValue, source);
}
function resolvePackageRelativeReference(rawValue, source) {
if (typeof rawValue !== "string") return null;
if (/^(?:https?:|data:|blob:)/i.test(rawValue.trim())) return null;
const path = normalizePath(rawValue);
if (!path || !path.includes("/")) return null;
const ownerType = source.sourceScope?.ownerType;
const ownerId = source.sourceScope?.ownerId;
if (!ownerType || !ownerId) return null;
if (ownerType === "module") {
return createFileLocator("data", `modules/${ownerId}/${path}`);
}
if (ownerType === "system") {
return createFileLocator("data", `systems/${ownerId}/${path}`);
}
if (ownerType === "world") {
return createFileLocator("data", path);
}
return null;
}
function dedupeReferences(references) {
const seen = new Set();
return references.filter(reference => {
const key = [
reference.sourceType,
reference.sourceLabel,
reference.normalized?.locator ?? "",
reference.rawValue
].join("|");
if (seen.has(key)) return false;
seen.add(key);
return true;
});
}