UNPKG

@kilpi/react-client

Version:

Kilpi React Client Components · Kilpi is the authorization framework for full-stack TypeScript applications, designed for flexible, powerful, agnostic, intuitive and developer friendly authorization.

154 lines (145 loc) 4.64 kB
// src/plugins/ReactClientComponentPlugin.ts import { createKilpiClientPlugin } from "@kilpi/client"; import "@kilpi/core"; // src/hooks/useIsAuthorized.ts import { useEffect as useEffect3, useState as useState2 } from "react"; // src/hooks/useCacheClearSignal.ts import { useEffect as useEffect2, useState } from "react"; // src/hooks/useWrapRef.ts import { useEffect, useRef } from "react"; function useWrapRef(t) { const ref = useRef(t); useEffect(() => { ref.current = t; }, [t]); return ref; } // src/hooks/useCacheClearSignal.ts function useCacheClearSignal(client, onCacheCleared) { const [signal, setSignal] = useState(0); const onCacheClearedRef = useWrapRef(onCacheCleared); useEffect2(() => { return client.onCacheClear(() => { setSignal(signal + 1); onCacheClearedRef.current?.(); }); }, [client, setSignal, onCacheClearedRef]); return signal; } // src/hooks/useIsAuthorized.ts function createUseIsAuthorized(KilpiClient) { return function useIsAuthorized(key, ...inputs) { const [value, setValue] = useState2({ status: "idle", isAuthorized: null, error: null }); const cacheSignal = useCacheClearSignal(KilpiClient, () => { setValue({ status: "idle", isAuthorized: null, error: null }); }); useEffect3(() => { let isMounted = true; const abortController = new AbortController(); async function fetchIsAuthorized() { if (isMounted) setValue({ status: "loading", error: null, isAuthorized: null }); try { const isAuthorized = await KilpiClient.fetchIsAuthorized({ key, resource: inputs[0], queryOptions: { signal: abortController.signal } }); if (isMounted) setValue({ status: "success", error: null, isAuthorized }); } catch (error) { if (isMounted) setValue({ status: "error", error, isAuthorized: null }); } } fetchIsAuthorized(); return () => { isMounted = false; abortController.abort(); }; }, [setValue, cacheSignal]); return Object.assign(value, { isLoading: value.status === "loading", isError: value.status === "error", isSuccess: value.status === "success", isIdle: value.status === "idle" }); }; } // src/components/ClientAccess.tsx function createClientAccess(KilpiClient) { const useIsAuthorized = createUseIsAuthorized(KilpiClient); function ClientAccess(props) { const query = useIsAuthorized(props.to, ...[props.to, props.on]); switch (query.status) { case "error": return props.Error ?? null; case "loading": case "idle": return props.Loading ?? null; case "success": return props.children; } } return ClientAccess; } // src/hooks/useSubject.ts import { useEffect as useEffect4, useState as useState3 } from "react"; function createUseSubject(KilpiClient) { return function useSubject() { const [value, setValue] = useState3({ status: "idle", subject: null, error: null }); const cacheSignal = useCacheClearSignal(KilpiClient, () => { setValue({ status: "idle", subject: null, error: null }); }); useEffect4(() => { let isMounted = true; const abortController = new AbortController(); async function fetchSubject() { if (isMounted) setValue({ status: "loading", subject: null, error: null }); try { const subject = await KilpiClient.fetchSubject({ queryOptions: { signal: abortController.signal } }); if (isMounted) setValue({ status: "success", subject, error: null }); } catch (error) { if (isMounted) setValue({ status: "error", subject: null, error }); } } fetchSubject(); return () => { isMounted = false; abortController.abort(); }; }, [setValue, cacheSignal]); return Object.assign(value, { isLoading: value.status === "loading", isError: value.status === "error", isSuccess: value.status === "success", isIdle: value.status === "idle" }); }; } // src/plugins/ReactClientComponentPlugin.ts function ReactClientComponentPlugin() { return createKilpiClientPlugin((Client) => { return { ReactClient: { createComponents() { return { useSubject: createUseSubject(Client), useIsAuthorized: createUseIsAuthorized(Client), ClientAccess: createClientAccess(Client) }; } } }; }); } export { ReactClientComponentPlugin };