初始化项目

This commit is contained in:
2026-01-18 10:35:27 +08:00
parent 85042841ae
commit 00ca4c1b0d
116 changed files with 11569 additions and 2 deletions

View File

@@ -0,0 +1,49 @@
export function normalizeUrl(input) {
try {
const url = new URL(input);
url.hash = "";
url.protocol = url.protocol.toLowerCase();
url.hostname = url.hostname.toLowerCase();
// Remove default ports
if ((url.protocol === "http:" && url.port === "80") || (url.protocol === "https:" && url.port === "443")) {
url.port = "";
}
// Trim trailing slash on pathname (but keep root '/')
if (url.pathname.length > 1 && url.pathname.endsWith("/")) {
url.pathname = url.pathname.slice(0, -1);
}
// Drop common tracking params
const trackingPrefixes = ["utm_", "spm", "gclid", "fbclid"];
for (const key of [...url.searchParams.keys()]) {
const lowerKey = key.toLowerCase();
if (trackingPrefixes.some((p) => lowerKey.startsWith(p))) {
url.searchParams.delete(key);
}
}
// Sort params for stable output
const sorted = [...url.searchParams.entries()].sort(([a], [b]) => a.localeCompare(b));
url.search = "";
for (const [k, v] of sorted) url.searchParams.append(k, v);
return url.toString();
} catch {
return input;
}
}
export function computeUrlHash(normalizedUrl) {
// Lightweight hash (non-crypto) for dedupe key; server may replace with crypto later.
let hash = 2166136261;
for (let i = 0; i < normalizedUrl.length; i++) {
hash ^= normalizedUrl.charCodeAt(i);
hash = Math.imul(hash, 16777619);
}
return (hash >>> 0).toString(16);
}
export { parseNetscapeBookmarkHtml } from "./bookmarkHtml.js";