UNPKG

@npio/internals

Version:

A free visual website editor, powered with your own SolidJS components.

121 lines (106 loc) 2.86 kB
import { Action, action, createAsync, reload as reload_, useAction, query, } from "@solidjs/router"; import { onCleanup, onMount } from "solid-js"; import { isServer } from "solid-js/web"; export type Auth<T, P> = { Session: { createdAt: number; csrf: string; data: T; }; Payload: P; }; export type PublicConfig = {}; export type HandlerInput<T> = | FormData | ({ type: "signin" } & T) | { type: "signout" } | { type: "refresh" } | { type: "session" }; export const csrfHeader = (name: string) => `x-csrf-${name.toLowerCase()}`; export const createClient = <T extends Auth<any, any>>( name: string, { revalidate = [], handler, }: { revalidate?: string[]; handler: (opts: HandlerInput<T["Payload"]>) => any; }, ) => { const getSession = query( () => handler({ type: "session" }) as Promise<T["Session"] | undefined>, name + "-session", ); const keys = [getSession.keyFor(), ...revalidate]; const reload = () => reload_({ revalidate: keys }); const withUrl = function <T extends (...args: any[]) => any>(fn: T): T { (fn as any).url = (handler as any).url; return fn; }; const refresh = action( withUrl(async () => { await handler({ type: "refresh" }); return reload_({ revalidate: "$$$nope" }); }), name + "-refresh", ); const signin = action( withUrl(async (args: T["Payload"]) => { const result = await handler({ type: "signin", ...args }); if (!result) return result; }), name + "-signin", ); const signout = action( withUrl(async () => { await handler({ type: "signout" }); return reload(); }), name + "-signout", ); const csrfHeader_ = csrfHeader(name); const usePatchFetch = () => { const session = createAsync(() => getSession(), { deferStream: true }); if (isServer) return; const serverUrl = import.meta.env.SERVER_BASE_URL + "/_server"; const origFetch = fetch; globalThis.fetch = (input, init) => { if ( typeof input === "string" && input.startsWith(serverUrl) && init?.headers && session() ) { (init.headers as Record<any, any>)[csrfHeader_] = session()?.csrf; } return origFetch(input, init); }; onCleanup(() => { globalThis.fetch = origFetch; }); }; return { signin, signout, getSession, refresh, reload, keys, usePatchFetch }; }; export const useRefresh = function ( refresh: Action<any, any>, { interval = 600, loggedIn, }: { interval?: number; loggedIn?: () => boolean } = {}, ) { const submitRefresh = useAction(refresh); onMount(() => { const intervalId = setInterval(() => { if (loggedIn && !loggedIn()) return; submitRefresh(); }, interval * 1000); onCleanup(() => clearInterval(intervalId)); }); };