@tern-secure/nextjs 3.2.35 → 3.2.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/boundary/TernSecureClientProvider.js +35 -20
- package/dist/cjs/boundary/TernSecureClientProvider.js.map +1 -1
- package/dist/esm/boundary/TernSecureClientProvider.js +35 -20
- package/dist/esm/boundary/TernSecureClientProvider.js.map +1 -1
- package/dist/types/boundary/TernSecureClientProvider.d.ts +2 -1
- package/dist/types/boundary/TernSecureClientProvider.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -32,7 +32,8 @@ var import_sessionTernSecure = require("../app-router/server/sessionTernSecure")
|
|
|
32
32
|
function TernSecureClientProvider({
|
|
33
33
|
children,
|
|
34
34
|
onUserChanged,
|
|
35
|
-
loginPath = "/sign-in"
|
|
35
|
+
loginPath = "/sign-in",
|
|
36
|
+
loadingComponent
|
|
36
37
|
}) {
|
|
37
38
|
const auth = (0, import_react.useMemo)(() => import_client_init.ternSecureAuth, []);
|
|
38
39
|
const router = (0, import_navigation.useRouter)();
|
|
@@ -88,25 +89,36 @@ function TernSecureClientProvider({
|
|
|
88
89
|
}
|
|
89
90
|
}, [auth, checkTokenValidity, loginPath, pathname]);
|
|
90
91
|
const handleAuthStateChange = (0, import_react.useCallback)(async (user) => {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
92
|
+
try {
|
|
93
|
+
const { isValid, token, userId } = await checkTokenValidity(user);
|
|
94
|
+
setAuthState({
|
|
95
|
+
isLoaded: true,
|
|
96
|
+
userId,
|
|
97
|
+
isValid,
|
|
98
|
+
token,
|
|
99
|
+
error: null
|
|
100
|
+
});
|
|
101
|
+
if (onUserChanged) {
|
|
102
|
+
await onUserChanged(user);
|
|
103
|
+
}
|
|
104
|
+
if (!isValid && pathname !== loginPath) {
|
|
105
|
+
router.push(loginPath);
|
|
106
|
+
}
|
|
107
|
+
if (user && isValid) {
|
|
108
|
+
startTokenCheckInterval();
|
|
109
|
+
} else if (intervalRef.current) {
|
|
110
|
+
clearInterval(intervalRef.current);
|
|
111
|
+
intervalRef.current = null;
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error("Auth state change error:", error);
|
|
115
|
+
setAuthState({
|
|
116
|
+
isLoaded: true,
|
|
117
|
+
userId: null,
|
|
118
|
+
isValid: false,
|
|
119
|
+
token: null,
|
|
120
|
+
error: error instanceof Error ? error : new Error("An unknown error occurred")
|
|
121
|
+
});
|
|
110
122
|
}
|
|
111
123
|
}, [checkTokenValidity, onUserChanged, router, loginPath, pathname, startTokenCheckInterval]);
|
|
112
124
|
(0, import_react.useEffect)(() => {
|
|
@@ -123,6 +135,9 @@ function TernSecureClientProvider({
|
|
|
123
135
|
checkTokenValidity: () => handleAuthStateChange(auth.currentUser),
|
|
124
136
|
signOut: handleSignOut
|
|
125
137
|
}), [authState, handleAuthStateChange, auth, handleSignOut]);
|
|
138
|
+
if (!authState.isLoaded) {
|
|
139
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TernSecureCtx.TernSecureCtx.Provider, { value: contextValue, children: loadingComponent || /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "aria-live": "polite", "aria-busy": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "sr-only", children: "Loading authentication state..." }) }) });
|
|
140
|
+
}
|
|
126
141
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TernSecureCtx.TernSecureCtx.Provider, { value: contextValue, children });
|
|
127
142
|
}
|
|
128
143
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/boundary/TernSecureClientProvider.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport React, { useState, useEffect, useMemo, useCallback, useRef } from 'react'\r\nimport { ternSecureAuth } from '../utils/client-init'\r\nimport { User, onAuthStateChanged } from \"firebase/auth\"\r\nimport { TernSecureCtx, TernSecureState, TernSecureCtxValue } from './TernSecureCtx'\r\nimport { useRouter, usePathname } from 'next/navigation'\r\nimport { verifyTernIdToken } from '../app-router/server/sessionTernSecure'\r\n\r\ninterface TernSecureClientProviderProps {\r\n children: React.ReactNode;\r\n onUserChanged?: (user: User | null) => Promise<void>;\r\n loginPath?: string;\r\n}\r\n\r\nexport function TernSecureClientProvider({ \r\n children, \r\n onUserChanged,\r\n loginPath = '/sign-in'\r\n}: TernSecureClientProviderProps) {\r\n const auth = useMemo(() => ternSecureAuth, []);\r\n const router = useRouter();\r\n const pathname = usePathname();\r\n const intervalRef = useRef<NodeJS.Timeout | null>(null);\r\n\r\n const [authState, setAuthState] = useState<TernSecureState>(() => ({\r\n userId: null,\r\n isLoaded: false,\r\n error: null,\r\n isValid: false,\r\n token: null\r\n }));\r\n\r\n const handleSignOut = useCallback(async (error?: Error) => {\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n intervalRef.current = null;\r\n }\r\n await auth.signOut();\r\n setAuthState({\r\n isLoaded: true,\r\n userId: null,\r\n error: error || null,\r\n isValid: false,\r\n token: null\r\n });\r\n router.push(loginPath);\r\n }, [auth, router, loginPath]);\r\n\r\n const checkTokenValidity = useCallback(async (user: User | null) => {\r\n if (user) {\r\n try {\r\n const token = await user.getIdToken(true);\r\n const decodedToken = await verifyTernIdToken(token);\r\n const isValid = decodedToken.valid\r\n\r\n if(isValid) {\r\n return { isValid: true, token, userId: user.uid };\r\n }\r\n } catch (error) {\r\n console.error('Token validation error:', error);\r\n await handleSignOut(error instanceof Error ? error : new Error('Authentication token is invalid'));\r\n return { isValid: false, token: null, userId: null };\r\n }\r\n }\r\n return { isValid: false, token: null, userId: null };\r\n }, [handleSignOut]);\r\n\r\n const startTokenCheckInterval = useCallback(() => {\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n }\r\n if (pathname !== loginPath) {\r\n intervalRef.current = setInterval(() => {\r\n checkTokenValidity(auth.currentUser);\r\n }, 30000); // Check every 30 seconds\r\n }\r\n }, [auth, checkTokenValidity, loginPath, pathname]);\r\n\r\n const handleAuthStateChange = useCallback(async (user: User | null) => {\r\n const { isValid, token, userId } = await checkTokenValidity(user);\r\n
|
|
1
|
+
{"version":3,"sources":["../../../src/boundary/TernSecureClientProvider.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport React, { useState, useEffect, useMemo, useCallback, useRef } from 'react'\r\nimport { ternSecureAuth } from '../utils/client-init'\r\nimport { User, onAuthStateChanged } from \"firebase/auth\"\r\nimport { TernSecureCtx, TernSecureState, TernSecureCtxValue } from './TernSecureCtx'\r\nimport { useRouter, usePathname } from 'next/navigation'\r\nimport { verifyTernIdToken } from '../app-router/server/sessionTernSecure'\r\n\r\ninterface TernSecureClientProviderProps {\r\n children: React.ReactNode;\r\n onUserChanged?: (user: User | null) => Promise<void>;\r\n loginPath?: string;\r\n loadingComponent?: React.ReactNode;\r\n}\r\n\r\nexport function TernSecureClientProvider({ \r\n children, \r\n onUserChanged,\r\n loginPath = '/sign-in',\r\n loadingComponent\r\n}: TernSecureClientProviderProps) {\r\n const auth = useMemo(() => ternSecureAuth, []);\r\n const router = useRouter();\r\n const pathname = usePathname();\r\n const intervalRef = useRef<NodeJS.Timeout | null>(null);\r\n\r\n const [authState, setAuthState] = useState<TernSecureState>(() => ({\r\n userId: null,\r\n isLoaded: false,\r\n error: null,\r\n isValid: false,\r\n token: null\r\n }));\r\n\r\n const handleSignOut = useCallback(async (error?: Error) => {\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n intervalRef.current = null;\r\n }\r\n await auth.signOut();\r\n setAuthState({\r\n isLoaded: true,\r\n userId: null,\r\n error: error || null,\r\n isValid: false,\r\n token: null\r\n });\r\n router.push(loginPath);\r\n }, [auth, router, loginPath]);\r\n\r\n const checkTokenValidity = useCallback(async (user: User | null) => {\r\n if (user) {\r\n try {\r\n const token = await user.getIdToken(true);\r\n const decodedToken = await verifyTernIdToken(token);\r\n const isValid = decodedToken.valid\r\n\r\n if(isValid) {\r\n return { isValid: true, token, userId: user.uid };\r\n }\r\n } catch (error) {\r\n console.error('Token validation error:', error);\r\n await handleSignOut(error instanceof Error ? error : new Error('Authentication token is invalid'));\r\n return { isValid: false, token: null, userId: null };\r\n }\r\n }\r\n return { isValid: false, token: null, userId: null };\r\n }, [handleSignOut]);\r\n\r\n const startTokenCheckInterval = useCallback(() => {\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n }\r\n if (pathname !== loginPath) {\r\n intervalRef.current = setInterval(() => {\r\n checkTokenValidity(auth.currentUser);\r\n }, 30000); // Check every 30 seconds\r\n }\r\n }, [auth, checkTokenValidity, loginPath, pathname]);\r\n\r\n const handleAuthStateChange = useCallback(async (user: User | null) => {\r\n try {\r\n const { isValid, token, userId } = await checkTokenValidity(user);\r\n \r\n setAuthState({\r\n isLoaded: true,\r\n userId,\r\n isValid,\r\n token,\r\n error: null\r\n });\r\n\r\n if (onUserChanged) {\r\n await onUserChanged(user);\r\n }\r\n\r\n if (!isValid && pathname !== loginPath) {\r\n router.push(loginPath);\r\n }\r\n\r\n if (user && isValid) {\r\n startTokenCheckInterval();\r\n } else if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n intervalRef.current = null;\r\n }\r\n } catch (error) {\r\n console.error('Auth state change error:', error);\r\n setAuthState({\r\n isLoaded: true,\r\n userId: null,\r\n isValid: false,\r\n token: null,\r\n error: error instanceof Error ? error : new Error('An unknown error occurred')\r\n });\r\n }\r\n }, [checkTokenValidity, onUserChanged, router, loginPath, pathname, startTokenCheckInterval]);\r\n\r\n useEffect(() => {\r\n const unsubscribeAuthState = onAuthStateChanged(auth, handleAuthStateChange);\r\n \r\n return () => {\r\n unsubscribeAuthState();\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n }\r\n };\r\n }, [auth, handleAuthStateChange]);\r\n\r\n const contextValue: TernSecureCtxValue = useMemo(() => ({\r\n ...authState,\r\n checkTokenValidity: () => handleAuthStateChange(auth.currentUser),\r\n signOut: handleSignOut,\r\n }), [authState, handleAuthStateChange, auth, handleSignOut]);\r\n \r\n if (!authState.isLoaded) {\r\n return (\r\n <TernSecureCtx.Provider value={contextValue}>\r\n {loadingComponent || (\r\n <div aria-live=\"polite\" aria-busy=\"true\">\r\n <span className=\"sr-only\">Loading authentication state...</span>\r\n </div>\r\n )}\r\n </TernSecureCtx.Provider>\r\n );\r\n }\r\n\r\n return (\r\n <TernSecureCtx.Provider value={contextValue}>\r\n {children}\r\n </TernSecureCtx.Provider>\r\n );\r\n}\r\n\r\n"],"mappings":"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","names":[]}
|
|
@@ -9,7 +9,8 @@ import { verifyTernIdToken } from "../app-router/server/sessionTernSecure";
|
|
|
9
9
|
function TernSecureClientProvider({
|
|
10
10
|
children,
|
|
11
11
|
onUserChanged,
|
|
12
|
-
loginPath = "/sign-in"
|
|
12
|
+
loginPath = "/sign-in",
|
|
13
|
+
loadingComponent
|
|
13
14
|
}) {
|
|
14
15
|
const auth = useMemo(() => ternSecureAuth, []);
|
|
15
16
|
const router = useRouter();
|
|
@@ -65,25 +66,36 @@ function TernSecureClientProvider({
|
|
|
65
66
|
}
|
|
66
67
|
}, [auth, checkTokenValidity, loginPath, pathname]);
|
|
67
68
|
const handleAuthStateChange = useCallback(async (user) => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
69
|
+
try {
|
|
70
|
+
const { isValid, token, userId } = await checkTokenValidity(user);
|
|
71
|
+
setAuthState({
|
|
72
|
+
isLoaded: true,
|
|
73
|
+
userId,
|
|
74
|
+
isValid,
|
|
75
|
+
token,
|
|
76
|
+
error: null
|
|
77
|
+
});
|
|
78
|
+
if (onUserChanged) {
|
|
79
|
+
await onUserChanged(user);
|
|
80
|
+
}
|
|
81
|
+
if (!isValid && pathname !== loginPath) {
|
|
82
|
+
router.push(loginPath);
|
|
83
|
+
}
|
|
84
|
+
if (user && isValid) {
|
|
85
|
+
startTokenCheckInterval();
|
|
86
|
+
} else if (intervalRef.current) {
|
|
87
|
+
clearInterval(intervalRef.current);
|
|
88
|
+
intervalRef.current = null;
|
|
89
|
+
}
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error("Auth state change error:", error);
|
|
92
|
+
setAuthState({
|
|
93
|
+
isLoaded: true,
|
|
94
|
+
userId: null,
|
|
95
|
+
isValid: false,
|
|
96
|
+
token: null,
|
|
97
|
+
error: error instanceof Error ? error : new Error("An unknown error occurred")
|
|
98
|
+
});
|
|
87
99
|
}
|
|
88
100
|
}, [checkTokenValidity, onUserChanged, router, loginPath, pathname, startTokenCheckInterval]);
|
|
89
101
|
useEffect(() => {
|
|
@@ -100,6 +112,9 @@ function TernSecureClientProvider({
|
|
|
100
112
|
checkTokenValidity: () => handleAuthStateChange(auth.currentUser),
|
|
101
113
|
signOut: handleSignOut
|
|
102
114
|
}), [authState, handleAuthStateChange, auth, handleSignOut]);
|
|
115
|
+
if (!authState.isLoaded) {
|
|
116
|
+
return /* @__PURE__ */ jsx(TernSecureCtx.Provider, { value: contextValue, children: loadingComponent || /* @__PURE__ */ jsx("div", { "aria-live": "polite", "aria-busy": "true", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading authentication state..." }) }) });
|
|
117
|
+
}
|
|
103
118
|
return /* @__PURE__ */ jsx(TernSecureCtx.Provider, { value: contextValue, children });
|
|
104
119
|
}
|
|
105
120
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/boundary/TernSecureClientProvider.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport React, { useState, useEffect, useMemo, useCallback, useRef } from 'react'\r\nimport { ternSecureAuth } from '../utils/client-init'\r\nimport { User, onAuthStateChanged } from \"firebase/auth\"\r\nimport { TernSecureCtx, TernSecureState, TernSecureCtxValue } from './TernSecureCtx'\r\nimport { useRouter, usePathname } from 'next/navigation'\r\nimport { verifyTernIdToken } from '../app-router/server/sessionTernSecure'\r\n\r\ninterface TernSecureClientProviderProps {\r\n children: React.ReactNode;\r\n onUserChanged?: (user: User | null) => Promise<void>;\r\n loginPath?: string;\r\n}\r\n\r\nexport function TernSecureClientProvider({ \r\n children, \r\n onUserChanged,\r\n loginPath = '/sign-in'\r\n}: TernSecureClientProviderProps) {\r\n const auth = useMemo(() => ternSecureAuth, []);\r\n const router = useRouter();\r\n const pathname = usePathname();\r\n const intervalRef = useRef<NodeJS.Timeout | null>(null);\r\n\r\n const [authState, setAuthState] = useState<TernSecureState>(() => ({\r\n userId: null,\r\n isLoaded: false,\r\n error: null,\r\n isValid: false,\r\n token: null\r\n }));\r\n\r\n const handleSignOut = useCallback(async (error?: Error) => {\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n intervalRef.current = null;\r\n }\r\n await auth.signOut();\r\n setAuthState({\r\n isLoaded: true,\r\n userId: null,\r\n error: error || null,\r\n isValid: false,\r\n token: null\r\n });\r\n router.push(loginPath);\r\n }, [auth, router, loginPath]);\r\n\r\n const checkTokenValidity = useCallback(async (user: User | null) => {\r\n if (user) {\r\n try {\r\n const token = await user.getIdToken(true);\r\n const decodedToken = await verifyTernIdToken(token);\r\n const isValid = decodedToken.valid\r\n\r\n if(isValid) {\r\n return { isValid: true, token, userId: user.uid };\r\n }\r\n } catch (error) {\r\n console.error('Token validation error:', error);\r\n await handleSignOut(error instanceof Error ? error : new Error('Authentication token is invalid'));\r\n return { isValid: false, token: null, userId: null };\r\n }\r\n }\r\n return { isValid: false, token: null, userId: null };\r\n }, [handleSignOut]);\r\n\r\n const startTokenCheckInterval = useCallback(() => {\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n }\r\n if (pathname !== loginPath) {\r\n intervalRef.current = setInterval(() => {\r\n checkTokenValidity(auth.currentUser);\r\n }, 30000); // Check every 30 seconds\r\n }\r\n }, [auth, checkTokenValidity, loginPath, pathname]);\r\n\r\n const handleAuthStateChange = useCallback(async (user: User | null) => {\r\n const { isValid, token, userId } = await checkTokenValidity(user);\r\n
|
|
1
|
+
{"version":3,"sources":["../../../src/boundary/TernSecureClientProvider.tsx"],"sourcesContent":["\"use client\"\r\n\r\nimport React, { useState, useEffect, useMemo, useCallback, useRef } from 'react'\r\nimport { ternSecureAuth } from '../utils/client-init'\r\nimport { User, onAuthStateChanged } from \"firebase/auth\"\r\nimport { TernSecureCtx, TernSecureState, TernSecureCtxValue } from './TernSecureCtx'\r\nimport { useRouter, usePathname } from 'next/navigation'\r\nimport { verifyTernIdToken } from '../app-router/server/sessionTernSecure'\r\n\r\ninterface TernSecureClientProviderProps {\r\n children: React.ReactNode;\r\n onUserChanged?: (user: User | null) => Promise<void>;\r\n loginPath?: string;\r\n loadingComponent?: React.ReactNode;\r\n}\r\n\r\nexport function TernSecureClientProvider({ \r\n children, \r\n onUserChanged,\r\n loginPath = '/sign-in',\r\n loadingComponent\r\n}: TernSecureClientProviderProps) {\r\n const auth = useMemo(() => ternSecureAuth, []);\r\n const router = useRouter();\r\n const pathname = usePathname();\r\n const intervalRef = useRef<NodeJS.Timeout | null>(null);\r\n\r\n const [authState, setAuthState] = useState<TernSecureState>(() => ({\r\n userId: null,\r\n isLoaded: false,\r\n error: null,\r\n isValid: false,\r\n token: null\r\n }));\r\n\r\n const handleSignOut = useCallback(async (error?: Error) => {\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n intervalRef.current = null;\r\n }\r\n await auth.signOut();\r\n setAuthState({\r\n isLoaded: true,\r\n userId: null,\r\n error: error || null,\r\n isValid: false,\r\n token: null\r\n });\r\n router.push(loginPath);\r\n }, [auth, router, loginPath]);\r\n\r\n const checkTokenValidity = useCallback(async (user: User | null) => {\r\n if (user) {\r\n try {\r\n const token = await user.getIdToken(true);\r\n const decodedToken = await verifyTernIdToken(token);\r\n const isValid = decodedToken.valid\r\n\r\n if(isValid) {\r\n return { isValid: true, token, userId: user.uid };\r\n }\r\n } catch (error) {\r\n console.error('Token validation error:', error);\r\n await handleSignOut(error instanceof Error ? error : new Error('Authentication token is invalid'));\r\n return { isValid: false, token: null, userId: null };\r\n }\r\n }\r\n return { isValid: false, token: null, userId: null };\r\n }, [handleSignOut]);\r\n\r\n const startTokenCheckInterval = useCallback(() => {\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n }\r\n if (pathname !== loginPath) {\r\n intervalRef.current = setInterval(() => {\r\n checkTokenValidity(auth.currentUser);\r\n }, 30000); // Check every 30 seconds\r\n }\r\n }, [auth, checkTokenValidity, loginPath, pathname]);\r\n\r\n const handleAuthStateChange = useCallback(async (user: User | null) => {\r\n try {\r\n const { isValid, token, userId } = await checkTokenValidity(user);\r\n \r\n setAuthState({\r\n isLoaded: true,\r\n userId,\r\n isValid,\r\n token,\r\n error: null\r\n });\r\n\r\n if (onUserChanged) {\r\n await onUserChanged(user);\r\n }\r\n\r\n if (!isValid && pathname !== loginPath) {\r\n router.push(loginPath);\r\n }\r\n\r\n if (user && isValid) {\r\n startTokenCheckInterval();\r\n } else if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n intervalRef.current = null;\r\n }\r\n } catch (error) {\r\n console.error('Auth state change error:', error);\r\n setAuthState({\r\n isLoaded: true,\r\n userId: null,\r\n isValid: false,\r\n token: null,\r\n error: error instanceof Error ? error : new Error('An unknown error occurred')\r\n });\r\n }\r\n }, [checkTokenValidity, onUserChanged, router, loginPath, pathname, startTokenCheckInterval]);\r\n\r\n useEffect(() => {\r\n const unsubscribeAuthState = onAuthStateChanged(auth, handleAuthStateChange);\r\n \r\n return () => {\r\n unsubscribeAuthState();\r\n if (intervalRef.current) {\r\n clearInterval(intervalRef.current);\r\n }\r\n };\r\n }, [auth, handleAuthStateChange]);\r\n\r\n const contextValue: TernSecureCtxValue = useMemo(() => ({\r\n ...authState,\r\n checkTokenValidity: () => handleAuthStateChange(auth.currentUser),\r\n signOut: handleSignOut,\r\n }), [authState, handleAuthStateChange, auth, handleSignOut]);\r\n \r\n if (!authState.isLoaded) {\r\n return (\r\n <TernSecureCtx.Provider value={contextValue}>\r\n {loadingComponent || (\r\n <div aria-live=\"polite\" aria-busy=\"true\">\r\n <span className=\"sr-only\">Loading authentication state...</span>\r\n </div>\r\n )}\r\n </TernSecureCtx.Provider>\r\n );\r\n }\r\n\r\n return (\r\n <TernSecureCtx.Provider value={contextValue}>\r\n {children}\r\n </TernSecureCtx.Provider>\r\n );\r\n}\r\n\r\n"],"mappings":"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","names":[]}
|
|
@@ -4,7 +4,8 @@ interface TernSecureClientProviderProps {
|
|
|
4
4
|
children: React.ReactNode;
|
|
5
5
|
onUserChanged?: (user: User | null) => Promise<void>;
|
|
6
6
|
loginPath?: string;
|
|
7
|
+
loadingComponent?: React.ReactNode;
|
|
7
8
|
}
|
|
8
|
-
export declare function TernSecureClientProvider({ children, onUserChanged, loginPath }: TernSecureClientProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare function TernSecureClientProvider({ children, onUserChanged, loginPath, loadingComponent }: TernSecureClientProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
9
10
|
export {};
|
|
10
11
|
//# sourceMappingURL=TernSecureClientProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TernSecureClientProvider.d.ts","sourceRoot":"","sources":["../../../src/boundary/TernSecureClientProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4D,MAAM,OAAO,CAAA;AAEhF,OAAO,EAAE,IAAI,EAAsB,MAAM,eAAe,CAAA;AAKxD,UAAU,6BAA6B;IACrC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"TernSecureClientProvider.d.ts","sourceRoot":"","sources":["../../../src/boundary/TernSecureClientProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA4D,MAAM,OAAO,CAAA;AAEhF,OAAO,EAAE,IAAI,EAAsB,MAAM,eAAe,CAAA;AAKxD,UAAU,6BAA6B;IACrC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACpC;AAED,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,aAAa,EACb,SAAsB,EACtB,gBAAgB,EACjB,EAAE,6BAA6B,2CAoI/B"}
|