const INFO_JSON_SUFFIX = ".info.json"; function safeTrim(value) { return typeof value === "string" ? value.trim() : ""; } export function normalizeStoragePointer(value) { return safeTrim(value); } function parseStoragePointer(value) { const normalized = normalizeStoragePointer(value); if (!normalized) { return null; } const separatorIndex = normalized.indexOf(":"); if (separatorIndex <= 0) { return null; } const provider = normalized.slice(0, separatorIndex).trim().toLowerCase(); const prefix = normalized.slice(separatorIndex + 1).trim(); if (!provider || !prefix) { return null; } return { provider, prefix }; } export function buildStoragePointerValue({ provider, prefix } = {}) { const normalizedProvider = safeTrim(provider).toLowerCase(); const normalizedPrefix = safeTrim(prefix); if (!normalizedProvider || !normalizedPrefix) { return ""; } return `${normalizedProvider}:${normalizedPrefix}`; } function deriveStoragePrefixFromUrl(url) { const trimmed = safeTrim(url); if (!trimmed || !/^https?:\/\//i.test(trimmed)) { return ""; } try { const parsed = new URL(trimmed); const path = parsed.pathname || ""; if (!path || path === "/") { return ""; } const normalizedPath = path.replace(/\/+$/, ""); const withoutExtension = normalizedPath.replace(/\.[^/.]+$/, ""); const cleanedPath = withoutExtension.startsWith("/") ? withoutExtension : `/${withoutExtension}`; return `${parsed.origin}${cleanedPath}`; } catch (error) { return ""; } } export function deriveStoragePointerFromUrl(url, provider = "url") { const prefix = deriveStoragePrefixFromUrl(url); return buildStoragePointerValue({ provider, prefix }); } export function buildStoragePrefixFromKey({ publicBaseUrl, key } = {}) { const base = safeTrim(publicBaseUrl).replace(/\/+$/, ""); const normalizedKey = safeTrim(key).replace(/^\//, ""); if (!base || !normalizedKey) { return ""; } const baseKey = normalizedKey.replace(/\.[^/.]+$/, ""); if (!baseKey) { return ""; } return `${base}/${baseKey}`; } export function getStoragePointerFromTags(tags = []) { if (!Array.isArray(tags)) { return ""; } const tag = tags.find( (entry) => Array.isArray(entry) && entry[0] === "s" && typeof entry[1] === "string" ); return normalizeStoragePointer(tag?.[1]); } function normalizeInfoJsonBase(base) { const trimmed = safeTrim(base); if (!trimmed) { return ""; } if (trimmed.toLowerCase().endsWith(INFO_JSON_SUFFIX)) { return trimmed; } return `${trimmed.replace(/\/+$/, "")}${INFO_JSON_SUFFIX}`; } export function resolveInfoJsonUrl({ storagePointer, url } = {}) { const parsed = parseStoragePointer(storagePointer); if (!parsed) { return ""; } const prefix = parsed.prefix; if (/^https?:\/\//i.test(prefix)) { return normalizeInfoJsonBase(prefix); } const fallbackPrefix = deriveStoragePrefixFromUrl(url); if (fallbackPrefix) { return normalizeInfoJsonBase(fallbackPrefix); } return normalizeInfoJsonBase(prefix); } export function resolveStoragePointerValue({ storagePointer, url, infoHash, fallbackId, provider, } = {}) { const normalized = normalizeStoragePointer(storagePointer); if (normalized) { return normalized; } const derivedFromUrl = deriveStoragePointerFromUrl(url, provider || "url"); if (derivedFromUrl) { return derivedFromUrl; } if (typeof infoHash === "string" && infoHash.trim()) { return buildStoragePointerValue({ provider: "btih", prefix: infoHash.trim().toLowerCase(), }); } if (typeof fallbackId === "string" && fallbackId.trim()) { return buildStoragePointerValue({ provider: "nostr", prefix: fallbackId.trim(), }); } return ""; }