"use strict"; function sanitizeUserId(userId) { return String(userId || "") .trim() .toLowerCase() .replace(/[^a-z0-9._-]/g, "-") .replace(/-+/g, "-") .replace(/^-|-$/g, ""); } function resolveEmailFromUserId(userId) { const raw = String(userId || "").trim(); if (raw.includes("@")) { return raw.toLowerCase(); } const safe = sanitizeUserId(raw) || "user"; return `${safe}@xartaudio.local`; } function extractLegacyUserCookie(cookieHeader) { const match = String(cookieHeader || "").match(/(?:^|;\s*)xartaudio_user=([^;]+)/); if (!match) { return null; } try { return decodeURIComponent(match[1]); } catch { return match[1]; } } function headersFromObject(rawHeaders = {}) { const headers = new Headers(); for (const [key, value] of Object.entries(rawHeaders)) { if (value === undefined || value === null) { continue; } if (Array.isArray(value)) { for (const item of value) { headers.append(key, String(item)); } continue; } headers.set(key, String(value)); } return headers; } function responseHeadersToObject(responseHeaders) { const headers = {}; for (const [key, value] of responseHeaders.entries()) { headers[key] = value; } if (typeof responseHeaders.getSetCookie === "function") { const cookies = responseHeaders.getSetCookie(); if (cookies && cookies.length > 0) { headers["set-cookie"] = cookies.length === 1 ? cookies[0] : cookies; } } else { const cookie = responseHeaders.get("set-cookie"); if (cookie) { headers["set-cookie"] = cookie; } } return headers; } function createBetterAuthAdapter({ appBaseUrl, basePath = "/api/auth", secret, devPassword = "xartaudio-dev-password", authHandler, logger = console, } = {}) { const normalizedBasePath = basePath.startsWith("/") ? basePath : `/${basePath}`; const memoryDb = { user: [], session: [], account: [], verification: [], }; let handlerPromise = null; async function resolveHandler() { if (authHandler) { return authHandler; } if (!handlerPromise) { handlerPromise = (async () => { const [{ betterAuth }, { memoryAdapter }] = await Promise.all([ import("better-auth"), import("better-auth/adapters/memory"), ]); const auth = betterAuth({ appName: "XArtAudio", baseURL: appBaseUrl, basePath: normalizedBasePath, secret, trustedOrigins: [appBaseUrl], database: memoryAdapter(memoryDb), emailAndPassword: { enabled: true, autoSignIn: true, requireEmailVerification: false, minPasswordLength: 8, }, }); return auth.handler; })().catch((error) => { logger.error({ err: error }, "failed to initialize better-auth"); handlerPromise = null; throw error; }); } return handlerPromise; } async function invoke({ method, path, headers, rawBody = "" }) { const handler = await resolveHandler(); const requestHeaders = headersFromObject(headers || {}); const url = new URL(path, appBaseUrl).toString(); const body = method === "GET" || method === "HEAD" ? undefined : rawBody; const response = await handler(new Request(url, { method, headers: requestHeaders, body, })); return response; } return { isConfigured() { return Boolean(appBaseUrl && secret); }, handlesPath(path) { return path === normalizedBasePath || path.startsWith(`${normalizedBasePath}/`); }, async handleRoute({ method, path, headers, rawBody }) { const response = await invoke({ method, path, headers, rawBody }); const responseBody = await response.text(); return { status: response.status, headers: responseHeadersToObject(response.headers), body: responseBody, }; }, async getAuthenticatedUserId(headers = {}) { if (headers["x-user-id"]) { return String(headers["x-user-id"]); } const cookieHeader = headers.cookie || ""; if (!cookieHeader) { return null; } try { const response = await invoke({ method: "GET", path: `${normalizedBasePath}/get-session`, headers: { cookie: cookieHeader, accept: "application/json", }, }); if (!response.ok) { return null; } const payload = await response.json().catch(() => null); const user = payload && payload.user ? payload.user : null; if (!user) { return extractLegacyUserCookie(cookieHeader); } return user.name || user.email || user.id || null; } catch { return extractLegacyUserCookie(cookieHeader); } }, async signInDevUser(userId) { const email = resolveEmailFromUserId(userId); const signInBody = JSON.stringify({ email, password: devPassword, rememberMe: true, }); let response = await invoke({ method: "POST", path: `${normalizedBasePath}/sign-in/email`, headers: { "content-type": "application/json", accept: "application/json", }, rawBody: signInBody, }); if (!response.ok) { response = await invoke({ method: "POST", path: `${normalizedBasePath}/sign-up/email`, headers: { "content-type": "application/json", accept: "application/json", }, rawBody: JSON.stringify({ name: String(userId), email, password: devPassword, rememberMe: true, }), }); } if (!response.ok) { const details = await response.text().catch(() => ""); throw new Error(`auth_sign_in_failed:${response.status}:${details}`); } const responseHeaders = responseHeadersToObject(response.headers); if (!responseHeaders["set-cookie"]) { throw new Error("auth_set_cookie_missing"); } return { setCookie: responseHeaders["set-cookie"], }; }, async signOut(headers = {}) { const response = await invoke({ method: "POST", path: `${normalizedBasePath}/sign-out`, headers: { cookie: headers.cookie || "", accept: "application/json", }, }); const responseHeaders = responseHeadersToObject(response.headers); return { ok: response.ok, setCookie: responseHeaders["set-cookie"] || null, }; }, }; } module.exports = { createBetterAuthAdapter, };