Initial module scaffold
This commit is contained in:
120
scripts/core/path-utils.js
Normal file
120
scripts/core/path-utils.js
Normal file
@@ -0,0 +1,120 @@
|
||||
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();
|
||||
try {
|
||||
return decodeURI(normalized);
|
||||
} 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 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: `${storage}:${cleanPath}`
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user