Mark references into missing module folders

This commit is contained in:
2026-04-21 14:20:25 +00:00
parent 7d574f7c1b
commit 61a2343d9f
5 changed files with 87 additions and 17 deletions

View File

@@ -42,7 +42,8 @@
}, },
"Notice": { "Notice": {
"Title": "Hinweise", "Title": "Hinweise",
"InactiveModuleReferences": "Die aktive Welt referenziert Dateien aus inaktiven Modulen. Diese Modulziele wurden nicht als unverankert gewertet: {modules}" "InactiveModuleReferences": "Die aktive Welt referenziert Dateien aus inaktiven Modulen: {modules}",
"MissingModuleReferences": "Die aktive Welt referenziert Dateien aus Modulordnern, die von Foundry derzeit nicht als Module erkannt werden: {modules}"
}, },
"Summary": { "Summary": {
"NoAnalysis": "Noch keine Analyse ausgeführt.", "NoAnalysis": "Noch keine Analyse ausgeführt.",

View File

@@ -42,7 +42,8 @@
}, },
"Notice": { "Notice": {
"Title": "Notes", "Title": "Notes",
"InactiveModuleReferences": "The active world references files from inactive modules. These module targets were not treated as unanchored: {modules}" "InactiveModuleReferences": "The active world references files from inactive modules: {modules}",
"MissingModuleReferences": "The active world references files from module folders that Foundry does not currently recognize as installed modules: {modules}"
}, },
"Summary": { "Summary": {
"NoAnalysis": "No analysis has been run yet.", "NoAnalysis": "No analysis has been run yet.",

View File

@@ -2,7 +2,7 @@
"id": "kosmos-storage-audit", "id": "kosmos-storage-audit",
"title": "Kosmos Storage Audit", "title": "Kosmos Storage Audit",
"description": "Analyzes media references and risky storage locations across Foundry data and public roots.", "description": "Analyzes media references and risky storage locations across Foundry data and public roots.",
"version": "0.0.24", "version": "0.0.25",
"compatibility": { "compatibility": {
"minimum": "13", "minimum": "13",
"verified": "13" "verified": "13"

View File

@@ -44,6 +44,7 @@ export function buildFindings({ files, references, i18n, packageActivity }={}) {
const wildcardReferences = []; const wildcardReferences = [];
const findings = []; const findings = [];
const inactiveModuleReferenceIds = new Set(); const inactiveModuleReferenceIds = new Set();
const missingModuleReferenceIds = new Set();
for (const reference of resolvedReferences) { for (const reference of resolvedReferences) {
const normalized = reference.normalized; const normalized = reference.normalized;
@@ -85,6 +86,10 @@ export function buildFindings({ files, references, i18n, packageActivity }={}) {
const ownerRelation = compareOwner(reference.sourceScope, file.ownerHint); const ownerRelation = compareOwner(reference.sourceScope, file.ownerHint);
if (ownerRelation === "non-package-to-package") { if (ownerRelation === "non-package-to-package") {
if (isMissingModuleTarget(file, packageActivity)) {
missingModuleReferenceIds.add(file.ownerHint.ownerId);
continue;
}
if (isInactiveModuleTarget(file, packageActivity)) { if (isInactiveModuleTarget(file, packageActivity)) {
inactiveModuleReferenceIds.add(file.ownerHint.ownerId); inactiveModuleReferenceIds.add(file.ownerHint.ownerId);
continue; continue;
@@ -144,7 +149,7 @@ export function buildFindings({ files, references, i18n, packageActivity }={}) {
return { return {
findings, findings,
notices: createNotices({ inactiveModuleReferenceIds, packageActivity, i18n }) notices: createNotices({ inactiveModuleReferenceIds, missingModuleReferenceIds, packageActivity, i18n })
}; };
} }
@@ -239,19 +244,42 @@ function isInactiveModuleTarget(file, packageActivity) {
return active === false; return active === false;
} }
function createNotices({ inactiveModuleReferenceIds, packageActivity, i18n }) { function isMissingModuleTarget(file, packageActivity) {
const moduleIds = [...inactiveModuleReferenceIds].sort((a, b) => a.localeCompare(b)); if (file.ownerHint?.ownerType !== "module") return false;
if (!moduleIds.length) return []; return packageActivity?.modules?.has?.(file.ownerHint.ownerId) === false;
const moduleLabels = moduleIds.map(id => packageActivity?.moduleLabels?.get?.(id) ?? id); }
return [{
kind: "inactive-module-references", function createNotices({ inactiveModuleReferenceIds, missingModuleReferenceIds, packageActivity, i18n }) {
severity: "info", const notices = [];
moduleIds,
moduleLabels, const inactiveModuleIds = [...inactiveModuleReferenceIds].sort((a, b) => a.localeCompare(b));
message: format(i18n, "KSA.Notice.InactiveModuleReferences", { if (inactiveModuleIds.length) {
modules: moduleLabels.join(", ") const moduleLabels = inactiveModuleIds.map(id => packageActivity?.moduleLabels?.get?.(id) ?? id);
}) notices.push({
}]; kind: "inactive-module-references",
severity: "info",
moduleIds: inactiveModuleIds,
moduleLabels,
message: format(i18n, "KSA.Notice.InactiveModuleReferences", {
modules: moduleLabels.join(", ")
})
});
}
const missingModuleIds = [...missingModuleReferenceIds].sort((a, b) => a.localeCompare(b));
if (missingModuleIds.length) {
notices.push({
kind: "missing-module-references",
severity: "info",
moduleIds: missingModuleIds,
moduleLabels: missingModuleIds,
message: format(i18n, "KSA.Notice.MissingModuleReferences", {
modules: missingModuleIds.join(", ")
})
});
}
return notices;
} }
function isDerivedSceneThumbnail(file) { function isDerivedSceneThumbnail(file) {

View File

@@ -0,0 +1,40 @@
import assert from "node:assert/strict";
import { buildFindings, createFileRecord } from "../scripts/core/finding-engine.js";
import { createFileLocator } from "../scripts/core/path-utils.js";
const files = [
createFileRecord(createFileLocator("data", "modules/legacy-module/icons/token.webp"), 1234)
];
const references = [
{
sourceType: "world-document",
sourceScope: { ownerType: "world", ownerId: "demo-world", systemId: "demo-system", subtype: "actors" },
sourceLabel: "Actor demo",
normalized: {
...createFileLocator("data", "modules/legacy-module/icons/token.webp"),
targetKind: "local-file"
}
}
];
const packageActivity = {
modules: new Map(),
moduleLabels: new Map()
};
const result = buildFindings({ files, references, packageActivity, i18n: { format: (key, data={}) => `${key}:${data.modules ?? ""}` } });
assert.equal(
result.findings.some(finding => finding.kind === "non-package-to-package-reference"),
false,
"world references into module folders that Foundry does not recognize should not be reported as unanchored package targets"
);
assert.equal(
result.notices.some(notice => notice.kind === "missing-module-references" && notice.moduleIds.includes("legacy-module")),
true,
"missing module references should create a dedicated report notice"
);
console.log("missing-module-reference-test: ok");