Update package layout

This commit is contained in:
2026-03-16 23:36:38 -04:00
parent b345982ab1
commit 8fe121951b
24 changed files with 383 additions and 39 deletions

View File

@@ -0,0 +1,77 @@
import { createContext, useCallback, useContext, useMemo, useRef } from "react";
import { defaultTokenData, fetchToken, parseToken, type TokenData } from "./TokenUtils";
export interface TokenState {
getToken: () => Promise<string | null | undefined>;
}
const initialState: TokenState = {
getToken: () => new Promise(() => {})
}
const TokenContext = createContext<TokenState>(initialState);
export default function TokenProvider({
apiUrl,
children
}: Readonly<{
apiUrl: string;
children: React.ReactNode;
}>){
const tokenRef = useRef<TokenData>(defaultTokenData);
const refreshPromise = useRef<Promise<string | null | undefined>>(null);
const getToken = useCallback(async () => {
if(refreshPromise.current){
return refreshPromise.current;
}
const { accessToken, expires } = tokenRef.current;
const isExpired = Date.now() > (expires - 5000); //Give a 5 second buffer
if(!accessToken || isExpired){
refreshPromise.current = (async () => {
try {
const rawToken = (await fetchToken(apiUrl)).token;
const parsedToken = parseToken(rawToken);
tokenRef.current = parsedToken;
return rawToken;
}
catch(error){
tokenRef.current = defaultTokenData;
throw error;
}
finally {
refreshPromise.current = null;
}
})();
return refreshPromise.current;
}
return accessToken;
}, [apiUrl]);
const value: TokenState = useMemo(() => ({
getToken
}), [getToken]);
return (
<TokenContext.Provider value={value}>
{children}
</TokenContext.Provider>
);
}
// eslint-disable-next-line react-refresh/only-export-components
export function useToken(){
const context = useContext(TokenContext);
if(!context){
throw new Error("useTOken must be called inside a TokenProvider");
}
return context;
}