feat: first commit

This commit is contained in:
sittichok Ouamsiri
2026-06-15 21:25:57 +07:00
commit 3395ab6dd3
88 changed files with 10034 additions and 0 deletions
+66
View File
@@ -0,0 +1,66 @@
/**
* Short-code generation.
*
* Strategy (mirrors what the Go backend will do):
* - "random" → base62 encoding of a monotonically increasing counter. This
* yields the SHORTEST possible string that is still globally
* unique (no collision checks, no wasted length). A counter of
* N needs ceil(log62(N)) chars: 62^4 ≈ 14.7M, 62^5 ≈ 916M.
* To avoid leaking sequence/volume, the counter is passed
* through a reversible bit-scramble before encoding.
* - "memorable" → 3 short, easy words joined by hyphens.
* - "custom" → user supplied (validated, authenticated only).
*/
const BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
export function encodeBase62(num: number): string {
if (num === 0) return BASE62[0];
let n = num;
let out = "";
while (n > 0) {
out = BASE62[n % 62] + out;
n = Math.floor(n / 62);
}
return out;
}
/**
* Reversible scramble so sequential counter values don't produce sequential
* codes. Knuth multiplicative hash over a 31-bit space with a coprime
* multiplier — keeps codes short while looking random.
*/
function scramble(id: number): number {
const PRIME = 2654435761; // 2^32 golden-ratio prime
const MOD = 0x7fffffff; // 2^31 - 1
return ((id + 1) * PRIME) % MOD;
}
export function codeFromCounter(counter: number): string {
// Bump the base so even the first link gets a pleasant 4-char code.
return encodeBase62(scramble(counter + 100_000));
}
const CUSTOM_RE = /^[a-zA-Z0-9_-]{3,32}$/;
export function validateCustomAlias(alias: string): string | null {
if (!alias) return "Pick an alias for your link.";
if (alias.length < 3) return "At least 3 characters.";
if (alias.length > 32) return "Keep it under 32 characters.";
if (!CUSTOM_RE.test(alias))
return "Only letters, numbers, hyphens and underscores.";
return null;
}
const URL_RE = /^https?:\/\/[^\s.]+\.[^\s]{2,}$/i;
export function normalizeUrl(input: string): string {
const trimmed = input.trim();
if (!trimmed) return "";
if (/^https?:\/\//i.test(trimmed)) return trimmed;
return `https://${trimmed}`;
}
export function isValidUrl(input: string): boolean {
return URL_RE.test(normalizeUrl(input));
}