/**
 * Small, dependency-free security helpers shared across packages.
 */

const HTML_ESCAPES: Record<string, string> = {
  "&": "&amp;",
  "<": "&lt;",
  ">": "&gt;",
  '"': "&quot;",
  "'": "&#39;",
};

/**
 * Escape a string for safe interpolation into HTML text or attributes.
 * Used for page metadata (title/description) so untrusted values can't
 * break out of an attribute or inject markup.
 */
export function escapeHtml(value: string): string {
  return value.replace(/[&<>"']/g, (ch) => HTML_ESCAPES[ch]!);
}

/**
 * Client island names come from `data-client="..."` attributes and from
 * filenames in `app/client`. We only ever allow a conservative charset so a
 * name can never be used to traverse the filesystem or inject code.
 */
const SAFE_NAME = /^[A-Za-z0-9_-]+$/;

export function isSafeIslandName(name: string): boolean {
  return SAFE_NAME.test(name);
}

/**
 * Reject obvious path-traversal in a request path before it is ever used to
 * resolve a file. The router never builds file paths from request input
 * (routes are resolved against a pre-scanned table), but this is a cheap
 * defense-in-depth guard.
 */
export function isSafeRequestPath(pathname: string): boolean {
  if (pathname.includes("\0")) return false;
  // Reject `..` segments and backslashes that could escape a directory.
  const decoded = safeDecode(pathname);
  if (decoded === null) return false;
  return !/(^|\/)\.\.(\/|$)/.test(decoded) && !decoded.includes("\\");
}

function safeDecode(value: string): string | null {
  try {
    return decodeURIComponent(value);
  } catch {
    return null;
  }
}
