Files
kosmos-storage-audit/scripts/core/path-utils.js
2026-04-20 20:33:13 +00:00

131 lines
3.9 KiB
JavaScript

export const RELEASE_PREFIXES = new Set(["canvas", "cards", "docs", "fonts", "icons", "lang", "nue", "scripts", "sounds", "toolclips", "tours", "ui"]);
const DATA_PREFIXES = new Set(["assets", "modules", "systems", "worlds"]);
const MEDIA_EXTENSIONS = {
image: new Set(["avif", "gif", "jpeg", "jpg", "png", "svg", "webp"]),
video: new Set(["m4v", "mkv", "mov", "mp4", "mpeg", "mpg", "webm"]),
audio: new Set(["flac", "m4a", "mid", "midi", "mp3", "ogg", "opus", "wav", "webm"])
};
export function normalizePath(path) {
const normalized = String(path ?? "")
.replace(/\\/g, "/")
.replace(/^[./]+/, "")
.replace(/^\/+/, "")
.replace(/\/{2,}/g, "/")
.trim()
.normalize("NFC");
try {
return decodeURI(normalized).normalize("NFC");
} catch {
return normalized;
}
}
export function splitResourceSuffix(path) {
const [withoutHash] = String(path ?? "").split("#", 1);
const [cleanPath] = withoutHash.split("?", 1);
return cleanPath;
}
export function parseStoragePath(rawValue) {
if (typeof rawValue !== "string") return null;
const trimmed = rawValue.trim();
if (!trimmed) return null;
if (/^(?:https?:|data:|blob:)/i.test(trimmed)) return null;
const clean = normalizePath(splitResourceSuffix(trimmed));
if (!clean) return null;
const first = clean.split("/", 1)[0];
let storage = null;
if (DATA_PREFIXES.has(first)) storage = "data";
else if (RELEASE_PREFIXES.has(first)) storage = "public";
if (!storage) return null;
return {
storage,
path: clean,
locator: `${storage}:${clean}`
};
}
export function hasWildcard(path) {
return /[*?[\]{}]/.test(String(path ?? ""));
}
export function createCanonicalLocator(storage, path) {
const cleanPath = normalizePath(path);
return `${storage}:${cleanPath}`.normalize("NFC");
}
export function getExtension(path) {
const clean = normalizePath(path);
const index = clean.lastIndexOf(".");
return index === -1 ? "" : clean.slice(index + 1).toLowerCase();
}
export function detectMediaKind(path) {
const ext = getExtension(path);
for (const [kind, extensions] of Object.entries(MEDIA_EXTENSIONS)) {
if (extensions.has(ext)) return kind;
}
return "other";
}
export function isMediaPath(path) {
return detectMediaKind(path) !== "other";
}
export function classifyRisk(locator) {
const parts = locator.path.split("/");
if (locator.storage === "public") return "release-public";
if (parts[0] === "modules") return "package-module";
if (parts[0] === "systems") return "package-system";
if (parts[0] === "assets") return "stable-user";
if (parts[0] === "worlds") return "stable-world";
return locator.storage === "data" ? "stable-user" : "unknown";
}
export function inferOwnerHint(locator) {
const parts = locator.path.split("/");
if (locator.storage === "public") {
return { ownerType: "public", ownerId: parts[0] ?? null };
}
if (parts[0] === "assets") {
return { ownerType: "user", ownerId: null };
}
if ((parts[0] === "worlds") && parts[1]) {
return { ownerType: "world", ownerId: parts[1] };
}
if ((parts[0] === "modules") && parts[1]) {
return { ownerType: "module", ownerId: parts[1] };
}
if ((parts[0] === "systems") && parts[1]) {
return { ownerType: "system", ownerId: parts[1] };
}
if (locator.storage === "data") {
return { ownerType: "user", ownerId: parts[0] ?? null };
}
return { ownerType: "unknown", ownerId: null };
}
export function isStorageAreaPath(locator) {
const parts = locator.path.split("/");
return ((parts[0] === "modules") || (parts[0] === "systems")) && (parts[2] === "storage");
}
export function isCorePublicPath(locator) {
if (locator.storage !== "public") return false;
const parts = locator.path.split("/");
return RELEASE_PREFIXES.has(parts[0] ?? "");
}
export function createFileLocator(storage, path) {
const cleanPath = normalizePath(path);
return {
storage,
path: cleanPath,
locator: createCanonicalLocator(storage, cleanPath)
};
}