UNPKG

kitcn

Version:

kitcn - React Query integration and CLI tools for Convex

136 lines (134 loc) 4.53 kB
import { t as getToken } from "../../../token-oA2EMtPA.js"; import { stripIndent } from "common-tags"; import { getRequestHeaders } from "@tanstack/react-start/server"; import { ConvexHttpClient } from "convex/browser"; import React from "react"; //#region src/auth-start/server.ts const fallbackCache = (fn) => fn; const cache = React.cache ?? fallbackCache; const TRAILING_COLON_RE = /:$/; const requestCanHaveBody = (method) => method !== "GET" && method !== "HEAD" && method !== "OPTIONS"; const stripHopByHopHeaders = (headers) => { headers.delete("connection"); headers.delete("content-length"); headers.delete("transfer-encoding"); }; function setupClient(options) { const client = new ConvexHttpClient(options.convexUrl); if (options.token !== void 0) client.setAuth(options.token); client.setFetchOptions?.({ cache: "no-store" }); return client; } const parseConvexSiteUrl = (url) => { if (!url) throw new Error(stripIndent` CONVEX_SITE_URL is not set. This is automatically set in the Convex backend, but must be set in the TanStack Start environment. For local development, this can be set in the .env.local file. `); if (url.endsWith(".convex.cloud")) throw new Error(stripIndent` CONVEX_SITE_URL should be set to your Convex Site URL, which ends in .convex.site. Currently set to ${url}. `); return url; }; const appendSetCookieHeaders = (target, source) => { const getSetCookie = source.getSetCookie; if (typeof getSetCookie === "function") { const values = getSetCookie.call(source); for (const value of values) target.append("set-cookie", value); return; } const value = source.get("set-cookie"); if (value) target.append("set-cookie", value); }; const cloneAuthHandlerResponse = (response) => { const headers = new Headers(); for (const [key, value] of response.headers.entries()) { if (key.toLowerCase() === "set-cookie") continue; headers.append(key, value); } appendSetCookieHeaders(headers, response.headers); return new Response(response.body, { headers, status: response.status, statusText: response.statusText }); }; const handler = async (request, opts) => { const requestUrl = new URL(request.url); const nextUrl = `${opts.convexSiteUrl}${requestUrl.pathname}${requestUrl.search}`; const headers = new Headers(request.headers); const proto = requestUrl.protocol.replace(TRAILING_COLON_RE, ""); stripHopByHopHeaders(headers); headers.set("accept-encoding", "application/json"); headers.set("host", new URL(opts.convexSiteUrl).host); headers.set("x-forwarded-host", requestUrl.host); headers.set("x-forwarded-proto", proto); headers.set("x-better-auth-forwarded-host", requestUrl.host); headers.set("x-better-auth-forwarded-proto", proto); let body; if (requestCanHaveBody(request.method)) { const bufferedBody = await request.arrayBuffer(); if (bufferedBody.byteLength > 0) body = bufferedBody; } return fetch(nextUrl, { body, headers, method: request.method, redirect: "manual" }); }; const convexBetterAuthReactStart = (opts) => { const siteUrl = parseConvexSiteUrl(opts.convexSiteUrl); const cachedGetToken = cache(async (opts) => { const headers = getRequestHeaders(); const mutableHeaders = new Headers(headers); stripHopByHopHeaders(mutableHeaders); mutableHeaders.set("accept-encoding", "identity"); return getToken(siteUrl, mutableHeaders, opts); }); const callWithToken = async (fn) => { const token = await cachedGetToken(opts) ?? {}; try { return await fn(token?.token); } catch (error) { if (!opts?.jwtCache?.enabled || token.isFresh || !opts.jwtCache?.isAuthError(error)) throw error; return await fn((await cachedGetToken({ ...opts, forceRefresh: true })).token); } }; return { getToken: async () => { return (await cachedGetToken(opts)).token; }, handler: async (request) => cloneAuthHandlerResponse(await handler(request, opts)), fetchAuthQuery: async (query, ...args) => { return callWithToken((token) => { return setupClient({ ...opts, token }).query(query, ...args); }); }, fetchAuthMutation: async (mutation, ...args) => { return callWithToken((token) => { return setupClient({ ...opts, token }).mutation(mutation, ...args); }); }, fetchAuthAction: async (action, ...args) => { return callWithToken((token) => { return setupClient({ ...opts, token }).action(action, ...args); }); } }; }; //#endregion export { convexBetterAuthReactStart };