import type { User } from "./types"; export const encoder = new TextEncoder(); export const decoder = new TextDecoder(); export function id(): string { return crypto.randomUUID(); } export function tokenString(bytes: number): string { return base64Url(crypto.getRandomValues(new Uint8Array(bytes))); } export function concatBytes(...parts: Uint8Array[]): Uint8Array { let total = 0; for (const part of parts) total += part.length; const out = new Uint8Array(total); let offset = 0; for (const part of parts) { out.set(part, offset); offset += part.length; } return out; } export function base64(bytes: Uint8Array): string { let binary = ""; for (const byte of bytes) binary += String.fromCharCode(byte); return btoa(binary); } export function base64Decode(value: string): Uint8Array { const binary = atob(value); return Uint8Array.from(binary, (char) => char.charCodeAt(0)); } export function base64Url(bytes: Uint8Array): string { return base64(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, ""); } export function base64UrlDecode(value: string): Uint8Array { const padded = value.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - (value.length % 4)) % 4); return base64Decode(padded); } export function bytesToArrayBuffer(bytes: Uint8Array): ArrayBuffer { return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer; } export function escapeHtml(value: string): string { return value.replace(/[&<>"']/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[char]!); } export function safeFileName(value: string): string { return value.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 120) || "upload"; } export function htmlContent(text: string, mentions: { acct: string; url: string }[] = [], hashtags: string[] = []): string { let escaped = escapeHtml(text); for (const mention of mentions) { const at = escapeHtml(`@${mention.acct}`); const url = escapeHtml(mention.url); const localName = mention.acct.split("@")[0]; const span = `@${escapeHtml(localName)}`; escaped = escaped.replaceAll(at, span); } for (const tag of hashtags) { const pattern = new RegExp(`#${escapeHtml(tag)}\\b`, "g"); escaped = escaped.replace(pattern, `#${escapeHtml(tag)}`); } return `
${escaped.replace(/\n{2,}/g, "
").replace(/\n/g, "
")}