Release 0.0.7

This commit is contained in:
2026-04-20 21:13:27 +00:00
parent 456c656750
commit e998784ac6
6 changed files with 92 additions and 39 deletions

View File

@@ -111,7 +111,8 @@ export async function runRuntimeAnalysis({ onProgress }={}) {
return analyzeStorage({
listFiles: () => listFoundryFiles(onProgress),
listSources: () => listFoundrySources(onProgress),
onProgress
onProgress,
i18n: { format }
});
}

View File

@@ -2,24 +2,24 @@ import { buildFindings, createFileRecord } from "./finding-engine.js";
import { extractReferencesFromValue } from "./reference-extractor.js";
import { createFileLocator } from "./path-utils.js";
export async function analyzeStorage({ listFiles, listSources, onProgress }={}) {
export async function analyzeStorage({ listFiles, listSources, onProgress, i18n }={}) {
const files = [];
let fileCount = 0;
let sourceCount = 0;
let referenceCount = 0;
onProgress?.({ phase: "files", label: "Scanne Dateien", files: 0, sources: 0, references: 0 });
onProgress?.({ phase: "files", label: format(i18n, "KSA.Progress.ScanFiles"), files: 0, sources: 0, references: 0 });
for await (const entry of listFiles()) {
files.push(createFileRecord(createFileLocator(entry.storage, entry.path), entry.size ?? null));
fileCount += 1;
if ((fileCount % 100) === 0) {
onProgress?.({ phase: "files", label: "Scanne Dateien", files: fileCount, sources: sourceCount, references: referenceCount });
onProgress?.({ phase: "files", label: format(i18n, "KSA.Progress.ScanFiles"), files: fileCount, sources: sourceCount, references: referenceCount });
await yieldToUI();
}
}
const references = [];
onProgress?.({ phase: "sources", label: "Lese Referenzen", files: fileCount, sources: 0, references: 0 });
onProgress?.({ phase: "sources", label: format(i18n, "KSA.Progress.ReadReferences"), files: fileCount, sources: 0, references: 0 });
for await (const source of listSources()) {
sourceCount += 1;
const extracted = extractReferencesFromValue(source.value, {
@@ -34,7 +34,7 @@ export async function analyzeStorage({ listFiles, listSources, onProgress }={})
if ((sourceCount % 50) === 0) {
onProgress?.({
phase: "sources",
label: "Lese Referenzen",
label: format(i18n, "KSA.Progress.ReadReferences"),
files: fileCount,
sources: sourceCount,
references: referenceCount,
@@ -44,11 +44,11 @@ export async function analyzeStorage({ listFiles, listSources, onProgress }={})
}
}
onProgress?.({ phase: "findings", label: "Klassifiziere Findings", files: fileCount, sources: sourceCount, references: referenceCount });
const findings = buildFindings({ files, references });
onProgress?.({ phase: "findings", label: format(i18n, "KSA.Progress.ClassifyFindings"), files: fileCount, sources: sourceCount, references: referenceCount });
const findings = buildFindings({ files, references, i18n });
onProgress?.({
phase: "done",
label: "Analyse abgeschlossen",
label: format(i18n, "KSA.Progress.Completed"),
files: fileCount,
sources: sourceCount,
references: referenceCount,
@@ -60,3 +60,7 @@ export async function analyzeStorage({ listFiles, listSources, onProgress }={})
async function yieldToUI() {
await new Promise(resolve => setTimeout(resolve, 0));
}
function format(i18n, key, data = {}) {
return i18n?.format?.(key, data) ?? key;
}

View File

@@ -32,7 +32,7 @@ function compareOwner(sourceScope, targetOwner) {
return "allowed";
}
export function buildFindings({ files, references }) {
export function buildFindings({ files, references, i18n }={}) {
const fileByLocator = new Map(files.map(file => [createCanonicalLocator(file.storage, file.path), file]));
const fileLocators = files.map(file => ({
storage: file.storage,
@@ -71,11 +71,11 @@ export function buildFindings({ files, references }) {
target: { ...normalized, locator: normalizedLocator },
source: reference,
reason: normalized.targetKind === "wildcard"
? `Wildcard reference ${normalized.locator} did not match any files in the scanned roots.`
: `Referenced file ${normalized.locator} does not exist in the scanned roots.`,
? format(i18n, "KSA.FindingReason.BrokenWildcard", { locator: normalized.locator })
: format(i18n, "KSA.FindingReason.BrokenReference", { locator: normalized.locator }),
recommendation: normalized.targetKind === "wildcard"
? "Check whether the wildcard pattern is still correct and whether matching files still exist."
: "Check whether the file was moved, deleted, or should be copied into a stable location.",
? format(i18n, "KSA.FindingRecommendation.CheckWildcard")
: format(i18n, "KSA.FindingRecommendation.CheckMissingFile"),
confidence: "high"
});
continue;
@@ -89,8 +89,11 @@ export function buildFindings({ files, references }) {
severity: "high",
target: { ...normalized, locator: normalizedLocator },
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.",
reason: format(i18n, "KSA.FindingReason.UnanchoredPackageTarget", {
sourceOwner: `${reference.sourceScope.ownerType}:${reference.sourceScope.ownerId}`,
locator: normalized.locator
}),
recommendation: format(i18n, "KSA.FindingRecommendation.MoveToStableStorage"),
confidence: "high"
});
} else if (ownerRelation === "risky-public") {
@@ -100,8 +103,11 @@ export function buildFindings({ files, references }) {
severity: "high",
target: { ...normalized, locator: normalizedLocator },
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.",
reason: format(i18n, "KSA.FindingReason.RiskyPublicTarget", {
sourceOwner: `${reference.sourceScope.ownerType}:${reference.sourceScope.ownerId}`,
locator: normalized.locator
}),
recommendation: format(i18n, "KSA.FindingRecommendation.CopyToStableStorage"),
confidence: "high"
});
}
@@ -122,10 +128,10 @@ export function buildFindings({ files, references }) {
severity,
target: file,
source: null,
reason: `No incoming media reference was found for ${file.locator}.`,
reason: format(i18n, "KSA.FindingReason.OrphanFile", { locator: 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.",
? format(i18n, "KSA.FindingRecommendation.ReviewOrMoveOrphan")
: format(i18n, "KSA.FindingRecommendation.KeepAsReserve"),
confidence: "medium"
});
}
@@ -194,3 +200,7 @@ export function createFileRecord(locator, size = null) {
exists: true
};
}
function format(i18n, key, data = {}) {
return i18n?.format?.(key, data) ?? key;
}