@npio/internals
Version:
A free visual website editor, powered with your own SolidJS components.
121 lines (106 loc) • 2.86 kB
text/typescript
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));
});
};