@eventuras/fides-auth-react 0.1.0
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/LICENSE +21 -0
- package/README.md +56 -0
- package/dist/hooks.d.ts +63 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +78 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/use-heartbeat.d.ts +27 -0
- package/dist/use-heartbeat.d.ts.map +1 -0
- package/dist/use-heartbeat.js +58 -0
- package/dist/use-heartbeat.js.map +1 -0
- package/dist/use-session-monitor.d.ts +26 -0
- package/dist/use-session-monitor.d.ts.map +1 -0
- package/dist/use-session-monitor.js +40 -0
- package/dist/use-session-monitor.js.map +1 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Losol AS
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# @eventuras/fides-auth-react
|
|
2
|
+
|
|
3
|
+
React hooks for [`@eventuras/fides-auth`](../fides-auth) — thin bindings over the
|
|
4
|
+
framework-agnostic building blocks:
|
|
5
|
+
|
|
6
|
+
- **`createAuthStoreHooks(store)`** → `useAuthStore` / `useAuthActions` for an
|
|
7
|
+
[`@eventuras/fides-auth-store`](../fides-auth-store) auth store.
|
|
8
|
+
- **`useSessionMonitor(store, checkAuthStatus, config?)`** → runs
|
|
9
|
+
`startSessionMonitor` with `useEffect` cleanup.
|
|
10
|
+
- **`useHeartbeat(config?)`** → runs the `@eventuras/fides-auth` heartbeat engine
|
|
11
|
+
to keep the session alive for active users.
|
|
12
|
+
|
|
13
|
+
The heavy logic lives in the packages these wrap (the heartbeat engine in
|
|
14
|
+
`@eventuras/fides-auth`, the store and session monitor in
|
|
15
|
+
`@eventuras/fides-auth-store`); this package only adds the React lifecycle glue.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add @eventuras/fides-auth-react
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`react`, `@xstate/store`, and `@xstate/store-react` are peer dependencies.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
'use client';
|
|
29
|
+
import {
|
|
30
|
+
createAuthStoreHooks,
|
|
31
|
+
useSessionMonitor,
|
|
32
|
+
useHeartbeat,
|
|
33
|
+
} from '@eventuras/fides-auth-react';
|
|
34
|
+
import { authStore, checkAuthStatus } from './auth';
|
|
35
|
+
|
|
36
|
+
export const { useAuthStore, useAuthActions } = createAuthStoreHooks(authStore);
|
|
37
|
+
|
|
38
|
+
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
39
|
+
useSessionMonitor(authStore, checkAuthStatus);
|
|
40
|
+
useHeartbeat({
|
|
41
|
+
onSessionExpired: () => authStore.send({ type: 'sessionExpired' }),
|
|
42
|
+
});
|
|
43
|
+
return <>{children}</>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function Profile() {
|
|
47
|
+
const auth = useAuthStore();
|
|
48
|
+
const { logout } = useAuthActions();
|
|
49
|
+
if (auth.isInitializing) return null;
|
|
50
|
+
return auth.isAuthenticated ? (
|
|
51
|
+
<button onClick={logout}>Log out {auth.user?.name}</button>
|
|
52
|
+
) : (
|
|
53
|
+
<a href='/login'>Log in</a>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
```
|
package/dist/hooks.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { SessionUser, createAuthStore } from '@eventuras/fides-auth-store';
|
|
2
|
+
/**
|
|
3
|
+
* Return type for store-based auth hooks
|
|
4
|
+
*/
|
|
5
|
+
export type AuthStoreSelector = {
|
|
6
|
+
isAuthenticated: boolean;
|
|
7
|
+
isAdmin: boolean;
|
|
8
|
+
user: SessionUser | null;
|
|
9
|
+
error: string | null;
|
|
10
|
+
lastChecked: Date | null;
|
|
11
|
+
isInitializing: boolean;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Actions for store-based auth
|
|
15
|
+
*/
|
|
16
|
+
export type AuthStoreActions = {
|
|
17
|
+
loginSuccess: (user: SessionUser) => void;
|
|
18
|
+
logout: () => void;
|
|
19
|
+
sessionExpired: () => void;
|
|
20
|
+
authSuccess: (user: SessionUser) => void;
|
|
21
|
+
authFailed: (error?: string) => void;
|
|
22
|
+
setError: (error: string) => void;
|
|
23
|
+
clearError: () => void;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Creates React hooks for interacting with the auth store
|
|
27
|
+
*
|
|
28
|
+
* @param store - The XState store instance
|
|
29
|
+
* @returns Object containing useAuthStore hook
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const authStore = createAuthStore({
|
|
34
|
+
* checkAuthStatus: getAuthStatus,
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* export const { useAuthStore } = createAuthStoreHooks(authStore);
|
|
38
|
+
*
|
|
39
|
+
* // In component
|
|
40
|
+
* function MyComponent() {
|
|
41
|
+
* const auth = useAuthStore();
|
|
42
|
+
*
|
|
43
|
+
* if (auth.error) {
|
|
44
|
+
* toast.error(auth.error);
|
|
45
|
+
* }
|
|
46
|
+
*
|
|
47
|
+
* if (auth.isInitializing) {
|
|
48
|
+
* return <Spinner />;
|
|
49
|
+
* }
|
|
50
|
+
*
|
|
51
|
+
* return (
|
|
52
|
+
* <div>
|
|
53
|
+
* {auth.isAuthenticated ? `Hello ${auth.user?.name}` : 'Please login'}
|
|
54
|
+
* </div>
|
|
55
|
+
* );
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare function createAuthStoreHooks(store: ReturnType<typeof createAuthStore>): {
|
|
60
|
+
useAuthStore: () => AuthStoreSelector;
|
|
61
|
+
useAuthActions: () => AuthStoreActions;
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEhF;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,eAAe,EAAE,OAAO,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,cAAc,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IAC1C,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,WAAW,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACzC,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC;wBAInD,iBAAiB;0BAQf,gBAAgB;EAgB5C"}
|
package/dist/hooks.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { useSelector } from "@xstate/store-react";
|
|
2
|
+
//#region src/hooks.ts
|
|
3
|
+
/**
|
|
4
|
+
* Creates React hooks for interacting with the auth store
|
|
5
|
+
*
|
|
6
|
+
* @param store - The XState store instance
|
|
7
|
+
* @returns Object containing useAuthStore hook
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const authStore = createAuthStore({
|
|
12
|
+
* checkAuthStatus: getAuthStatus,
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* export const { useAuthStore } = createAuthStoreHooks(authStore);
|
|
16
|
+
*
|
|
17
|
+
* // In component
|
|
18
|
+
* function MyComponent() {
|
|
19
|
+
* const auth = useAuthStore();
|
|
20
|
+
*
|
|
21
|
+
* if (auth.error) {
|
|
22
|
+
* toast.error(auth.error);
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* if (auth.isInitializing) {
|
|
26
|
+
* return <Spinner />;
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* return (
|
|
30
|
+
* <div>
|
|
31
|
+
* {auth.isAuthenticated ? `Hello ${auth.user?.name}` : 'Please login'}
|
|
32
|
+
* </div>
|
|
33
|
+
* );
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
function createAuthStoreHooks(store) {
|
|
38
|
+
/**
|
|
39
|
+
* Hook to access the auth store state
|
|
40
|
+
*/
|
|
41
|
+
function useAuthStore() {
|
|
42
|
+
return useSelector(store, (snapshot) => snapshot).context;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Hook to get auth store actions
|
|
46
|
+
*/
|
|
47
|
+
function useAuthActions() {
|
|
48
|
+
return {
|
|
49
|
+
loginSuccess: (user) => store.send({
|
|
50
|
+
type: "loginSuccess",
|
|
51
|
+
user
|
|
52
|
+
}),
|
|
53
|
+
logout: () => store.send({ type: "logout" }),
|
|
54
|
+
sessionExpired: () => store.send({ type: "sessionExpired" }),
|
|
55
|
+
authSuccess: (user) => store.send({
|
|
56
|
+
type: "authSuccess",
|
|
57
|
+
user
|
|
58
|
+
}),
|
|
59
|
+
authFailed: (error) => store.send({
|
|
60
|
+
type: "authFailed",
|
|
61
|
+
error
|
|
62
|
+
}),
|
|
63
|
+
setError: (error) => store.send({
|
|
64
|
+
type: "setError",
|
|
65
|
+
error
|
|
66
|
+
}),
|
|
67
|
+
clearError: () => store.send({ type: "clearError" })
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
useAuthStore,
|
|
72
|
+
useAuthActions
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { createAuthStoreHooks };
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","names":[],"sources":["../src/hooks.ts"],"sourcesContent":["import { useSelector as useStoreSelector } from '@xstate/store-react';\nimport type { SessionUser, createAuthStore } from '@eventuras/fides-auth-store';\n\n/**\n * Return type for store-based auth hooks\n */\nexport type AuthStoreSelector = {\n isAuthenticated: boolean;\n isAdmin: boolean;\n user: SessionUser | null;\n error: string | null;\n lastChecked: Date | null;\n isInitializing: boolean;\n};\n\n/**\n * Actions for store-based auth\n */\nexport type AuthStoreActions = {\n loginSuccess: (user: SessionUser) => void;\n logout: () => void;\n sessionExpired: () => void;\n authSuccess: (user: SessionUser) => void;\n authFailed: (error?: string) => void;\n setError: (error: string) => void;\n clearError: () => void;\n};\n\n/**\n * Creates React hooks for interacting with the auth store\n *\n * @param store - The XState store instance\n * @returns Object containing useAuthStore hook\n *\n * @example\n * ```typescript\n * const authStore = createAuthStore({\n * checkAuthStatus: getAuthStatus,\n * });\n *\n * export const { useAuthStore } = createAuthStoreHooks(authStore);\n *\n * // In component\n * function MyComponent() {\n * const auth = useAuthStore();\n *\n * if (auth.error) {\n * toast.error(auth.error);\n * }\n *\n * if (auth.isInitializing) {\n * return <Spinner />;\n * }\n *\n * return (\n * <div>\n * {auth.isAuthenticated ? `Hello ${auth.user?.name}` : 'Please login'}\n * </div>\n * );\n * }\n * ```\n */\nexport function createAuthStoreHooks(store: ReturnType<typeof createAuthStore>) {\n /**\n * Hook to access the auth store state\n */\n function useAuthStore(): AuthStoreSelector {\n const snapshot = useStoreSelector(store, (snapshot) => snapshot);\n return snapshot.context;\n }\n\n /**\n * Hook to get auth store actions\n */\n function useAuthActions(): AuthStoreActions {\n return {\n loginSuccess: (user: SessionUser) => store.send({ type: 'loginSuccess', user }),\n logout: () => store.send({ type: 'logout' }),\n sessionExpired: () => store.send({ type: 'sessionExpired' }),\n authSuccess: (user: SessionUser) => store.send({ type: 'authSuccess', user }),\n authFailed: (error?: string) => store.send({ type: 'authFailed', error }),\n setError: (error: string) => store.send({ type: 'setError', error }),\n clearError: () => store.send({ type: 'clearError' }),\n };\n }\n\n return {\n useAuthStore,\n useAuthActions,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,SAAgB,qBAAqB,OAA2C;;;;CAI9E,SAAS,eAAkC;EAEzC,OADiB,YAAiB,QAAQ,aAAa,QAChD,EAAS;CAClB;;;;CAKA,SAAS,iBAAmC;EAC1C,OAAO;GACL,eAAe,SAAsB,MAAM,KAAK;IAAE,MAAM;IAAgB;GAAK,CAAC;GAC9E,cAAc,MAAM,KAAK,EAAE,MAAM,SAAS,CAAC;GAC3C,sBAAsB,MAAM,KAAK,EAAE,MAAM,iBAAiB,CAAC;GAC3D,cAAc,SAAsB,MAAM,KAAK;IAAE,MAAM;IAAe;GAAK,CAAC;GAC5E,aAAa,UAAmB,MAAM,KAAK;IAAE,MAAM;IAAc;GAAM,CAAC;GACxE,WAAW,UAAkB,MAAM,KAAK;IAAE,MAAM;IAAY;GAAM,CAAC;GACnE,kBAAkB,MAAM,KAAK,EAAE,MAAM,aAAa,CAAC;EACrD;CACF;CAEA,OAAO;EACL;EACA;CACF;AACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React bindings for @eventuras/fides-auth.
|
|
3
|
+
*
|
|
4
|
+
* Thin React hooks over the framework-agnostic building blocks:
|
|
5
|
+
* - {@link createAuthStoreHooks} — `useAuthStore` / `useAuthActions` over an
|
|
6
|
+
* `@eventuras/fides-auth-store` auth store.
|
|
7
|
+
* - {@link useSessionMonitor} — runs `startSessionMonitor` with `useEffect` cleanup.
|
|
8
|
+
* - {@link useHeartbeat} — runs the `@eventuras/fides-auth` heartbeat engine.
|
|
9
|
+
*
|
|
10
|
+
* Pair these with `@eventuras/fides-auth-store` (the store + status checks) and a
|
|
11
|
+
* server adapter such as `@eventuras/fides-auth-next` (cookies, route handlers).
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* 'use client';
|
|
16
|
+
* import { createAuthStoreHooks, useSessionMonitor, useHeartbeat } from '@eventuras/fides-auth-react';
|
|
17
|
+
* import { authStore, checkAuthStatus } from './auth';
|
|
18
|
+
*
|
|
19
|
+
* export const { useAuthStore, useAuthActions } = createAuthStoreHooks(authStore);
|
|
20
|
+
*
|
|
21
|
+
* export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
22
|
+
* useSessionMonitor(authStore, checkAuthStatus);
|
|
23
|
+
* useHeartbeat({ onSessionExpired: () => authStore.send({ type: 'sessionExpired' }) });
|
|
24
|
+
* return <>{children}</>;
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export { createAuthStoreHooks, type AuthStoreSelector, type AuthStoreActions, } from './hooks';
|
|
29
|
+
export { useSessionMonitor } from './use-session-monitor';
|
|
30
|
+
export { useHeartbeat, type HeartbeatConfig } from './use-heartbeat';
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EACL,oBAAoB,EACpB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { HeartbeatConfig } from '@eventuras/fides-auth/heartbeat';
|
|
2
|
+
export type { HeartbeatConfig };
|
|
3
|
+
/**
|
|
4
|
+
* React wrapper around the framework-agnostic {@link createHeartbeat} engine.
|
|
5
|
+
*
|
|
6
|
+
* Starts the heartbeat on mount and stops it on unmount. Callbacks are read
|
|
7
|
+
* through a ref so changing their identity between renders does not restart the
|
|
8
|
+
* engine; the primitive options are the effect's dependencies.
|
|
9
|
+
*
|
|
10
|
+
* Intended to be paired with {@link useSessionMonitor}: the monitor *detects*
|
|
11
|
+
* session loss; this hook *prevents* it for active users. See
|
|
12
|
+
* {@link HeartbeatConfig} for the tuning knobs.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* 'use client';
|
|
17
|
+
* import { useHeartbeat, useSessionMonitor } from '@eventuras/fides-auth-react';
|
|
18
|
+
*
|
|
19
|
+
* function AuthProvider({ children }) {
|
|
20
|
+
* useSessionMonitor(authStore, checkAuthStatus);
|
|
21
|
+
* useHeartbeat({ onSessionExpired: () => authStore.send({ type: 'sessionExpired' }) });
|
|
22
|
+
* return <>{children}</>;
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function useHeartbeat(config?: HeartbeatConfig): void;
|
|
27
|
+
//# sourceMappingURL=use-heartbeat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-heartbeat.d.ts","sourceRoot":"","sources":["../src/use-heartbeat.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAExF,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAE,eAAoB,GAAG,IAAI,CAsC/D"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { createHeartbeat } from "@eventuras/fides-auth/heartbeat";
|
|
4
|
+
//#region src/use-heartbeat.ts
|
|
5
|
+
/**
|
|
6
|
+
* React wrapper around the framework-agnostic {@link createHeartbeat} engine.
|
|
7
|
+
*
|
|
8
|
+
* Starts the heartbeat on mount and stops it on unmount. Callbacks are read
|
|
9
|
+
* through a ref so changing their identity between renders does not restart the
|
|
10
|
+
* engine; the primitive options are the effect's dependencies.
|
|
11
|
+
*
|
|
12
|
+
* Intended to be paired with {@link useSessionMonitor}: the monitor *detects*
|
|
13
|
+
* session loss; this hook *prevents* it for active users. See
|
|
14
|
+
* {@link HeartbeatConfig} for the tuning knobs.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* 'use client';
|
|
19
|
+
* import { useHeartbeat, useSessionMonitor } from '@eventuras/fides-auth-react';
|
|
20
|
+
*
|
|
21
|
+
* function AuthProvider({ children }) {
|
|
22
|
+
* useSessionMonitor(authStore, checkAuthStatus);
|
|
23
|
+
* useHeartbeat({ onSessionExpired: () => authStore.send({ type: 'sessionExpired' }) });
|
|
24
|
+
* return <>{children}</>;
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
function useHeartbeat(config = {}) {
|
|
29
|
+
const { endpoint, fraction, minSkewMs, minRefreshIntervalMs, idleThresholdMs, initialExpiresAt, loggerNamespace } = config;
|
|
30
|
+
const callbacksRef = useRef(config);
|
|
31
|
+
callbacksRef.current = config;
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
return createHeartbeat({
|
|
34
|
+
endpoint,
|
|
35
|
+
fraction,
|
|
36
|
+
minSkewMs,
|
|
37
|
+
minRefreshIntervalMs,
|
|
38
|
+
idleThresholdMs,
|
|
39
|
+
initialExpiresAt,
|
|
40
|
+
loggerNamespace,
|
|
41
|
+
onSessionExpired: () => callbacksRef.current.onSessionExpired?.(),
|
|
42
|
+
onRefreshed: () => callbacksRef.current.onRefreshed?.(),
|
|
43
|
+
onError: (error) => callbacksRef.current.onError?.(error)
|
|
44
|
+
}).stop;
|
|
45
|
+
}, [
|
|
46
|
+
endpoint,
|
|
47
|
+
fraction,
|
|
48
|
+
minSkewMs,
|
|
49
|
+
minRefreshIntervalMs,
|
|
50
|
+
idleThresholdMs,
|
|
51
|
+
initialExpiresAt,
|
|
52
|
+
loggerNamespace
|
|
53
|
+
]);
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { useHeartbeat };
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=use-heartbeat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-heartbeat.js","names":[],"sources":["../src/use-heartbeat.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef } from 'react';\nimport { createHeartbeat, type HeartbeatConfig } from '@eventuras/fides-auth/heartbeat';\n\nexport type { HeartbeatConfig };\n\n/**\n * React wrapper around the framework-agnostic {@link createHeartbeat} engine.\n *\n * Starts the heartbeat on mount and stops it on unmount. Callbacks are read\n * through a ref so changing their identity between renders does not restart the\n * engine; the primitive options are the effect's dependencies.\n *\n * Intended to be paired with {@link useSessionMonitor}: the monitor *detects*\n * session loss; this hook *prevents* it for active users. See\n * {@link HeartbeatConfig} for the tuning knobs.\n *\n * @example\n * ```tsx\n * 'use client';\n * import { useHeartbeat, useSessionMonitor } from '@eventuras/fides-auth-react';\n *\n * function AuthProvider({ children }) {\n * useSessionMonitor(authStore, checkAuthStatus);\n * useHeartbeat({ onSessionExpired: () => authStore.send({ type: 'sessionExpired' }) });\n * return <>{children}</>;\n * }\n * ```\n */\nexport function useHeartbeat(config: HeartbeatConfig = {}): void {\n const {\n endpoint,\n fraction,\n minSkewMs,\n minRefreshIntervalMs,\n idleThresholdMs,\n initialExpiresAt,\n loggerNamespace,\n } = config;\n\n // Keep callbacks current without re-running the effect when their identities change.\n const callbacksRef = useRef(config);\n callbacksRef.current = config;\n\n useEffect(() => {\n const handle = createHeartbeat({\n endpoint,\n fraction,\n minSkewMs,\n minRefreshIntervalMs,\n idleThresholdMs,\n initialExpiresAt,\n loggerNamespace,\n onSessionExpired: () => callbacksRef.current.onSessionExpired?.(),\n onRefreshed: () => callbacksRef.current.onRefreshed?.(),\n onError: (error) => callbacksRef.current.onError?.(error),\n });\n return handle.stop;\n }, [\n endpoint,\n fraction,\n minSkewMs,\n minRefreshIntervalMs,\n idleThresholdMs,\n initialExpiresAt,\n loggerNamespace,\n ]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,aAAa,SAA0B,CAAC,GAAS;CAC/D,MAAM,EACJ,UACA,UACA,WACA,sBACA,iBACA,kBACA,oBACE;CAGJ,MAAM,eAAe,OAAO,MAAM;CAClC,aAAa,UAAU;CAEvB,gBAAgB;EAad,OAZe,gBAAgB;GAC7B;GACA;GACA;GACA;GACA;GACA;GACA;GACA,wBAAwB,aAAa,QAAQ,mBAAmB;GAChE,mBAAmB,aAAa,QAAQ,cAAc;GACtD,UAAU,UAAU,aAAa,QAAQ,UAAU,KAAK;EAC1D,CACO,EAAO;CAChB,GAAG;EACD;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;AACH"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { SessionMonitorConfig, AuthStatus, createAuthStore } from '@eventuras/fides-auth-store';
|
|
2
|
+
/**
|
|
3
|
+
* React hook for session monitoring with automatic cleanup
|
|
4
|
+
*
|
|
5
|
+
* @param store - The auth store to update
|
|
6
|
+
* @param checkAuthStatus - Function to check authentication status
|
|
7
|
+
* @param config - Optional configuration
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { useSessionMonitor } from '@eventuras/fides-auth-react';
|
|
12
|
+
*
|
|
13
|
+
* function App() {
|
|
14
|
+
* useSessionMonitor(authStore, checkAuthStatus, {
|
|
15
|
+
* interval: 30_000,
|
|
16
|
+
* onSessionExpired: () => {
|
|
17
|
+
* toast.warn('Your session has expired');
|
|
18
|
+
* }
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* return <div>...</div>;
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function useSessionMonitor(store: ReturnType<typeof createAuthStore>, checkAuthStatus: () => Promise<AuthStatus>, config?: SessionMonitorConfig): void;
|
|
26
|
+
//# sourceMappingURL=use-session-monitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-session-monitor.d.ts","sourceRoot":"","sources":["../src/use-session-monitor.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,UAAU,EACf,KAAK,eAAe,EACrB,MAAM,6BAA6B,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,UAAU,CAAC,OAAO,eAAe,CAAC,EACzC,eAAe,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,EAC1C,MAAM,GAAE,oBAAyB,GAChC,IAAI,CAIN"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
import { startSessionMonitor } from "@eventuras/fides-auth-store";
|
|
4
|
+
//#region src/use-session-monitor.ts
|
|
5
|
+
/**
|
|
6
|
+
* React hook for session monitoring with automatic cleanup
|
|
7
|
+
*
|
|
8
|
+
* @param store - The auth store to update
|
|
9
|
+
* @param checkAuthStatus - Function to check authentication status
|
|
10
|
+
* @param config - Optional configuration
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { useSessionMonitor } from '@eventuras/fides-auth-react';
|
|
15
|
+
*
|
|
16
|
+
* function App() {
|
|
17
|
+
* useSessionMonitor(authStore, checkAuthStatus, {
|
|
18
|
+
* interval: 30_000,
|
|
19
|
+
* onSessionExpired: () => {
|
|
20
|
+
* toast.warn('Your session has expired');
|
|
21
|
+
* }
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* return <div>...</div>;
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
function useSessionMonitor(store, checkAuthStatus, config = {}) {
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
return startSessionMonitor(store, checkAuthStatus, config);
|
|
31
|
+
}, [
|
|
32
|
+
store,
|
|
33
|
+
checkAuthStatus,
|
|
34
|
+
config
|
|
35
|
+
]);
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { useSessionMonitor };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=use-session-monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-session-monitor.js","names":[],"sources":["../src/use-session-monitor.ts"],"sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\nimport {\n startSessionMonitor,\n type SessionMonitorConfig,\n type AuthStatus,\n type createAuthStore,\n} from '@eventuras/fides-auth-store';\n\n/**\n * React hook for session monitoring with automatic cleanup\n *\n * @param store - The auth store to update\n * @param checkAuthStatus - Function to check authentication status\n * @param config - Optional configuration\n *\n * @example\n * ```typescript\n * import { useSessionMonitor } from '@eventuras/fides-auth-react';\n *\n * function App() {\n * useSessionMonitor(authStore, checkAuthStatus, {\n * interval: 30_000,\n * onSessionExpired: () => {\n * toast.warn('Your session has expired');\n * }\n * });\n *\n * return <div>...</div>;\n * }\n * ```\n */\nexport function useSessionMonitor(\n store: ReturnType<typeof createAuthStore>,\n checkAuthStatus: () => Promise<AuthStatus>,\n config: SessionMonitorConfig = {}\n): void {\n useEffect(() => {\n return startSessionMonitor(store, checkAuthStatus, config);\n }, [store, checkAuthStatus, config]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,SAAgB,kBACd,OACA,iBACA,SAA+B,CAAC,GAC1B;CACN,gBAAgB;EACd,OAAO,oBAAoB,OAAO,iBAAiB,MAAM;CAC3D,GAAG;EAAC;EAAO;EAAiB;CAAM,CAAC;AACrC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eventuras/fides-auth-react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "React hooks for @eventuras/fides-auth — auth store bindings, session monitoring, and heartbeat",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"authentication",
|
|
7
|
+
"oauth",
|
|
8
|
+
"oidc",
|
|
9
|
+
"session",
|
|
10
|
+
"react",
|
|
11
|
+
"hooks",
|
|
12
|
+
"xstate"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/losol/fides-auth/tree/main/packages/fides-auth-react#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/losol/fides-auth/issues"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/losol/fides-auth.git",
|
|
21
|
+
"directory": "packages/fides-auth-react"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": "losol <losol@users.noreply.github.com>",
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"main": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"LICENSE",
|
|
38
|
+
"README.md"
|
|
39
|
+
],
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@eventuras/logger": "^0.8.1",
|
|
42
|
+
"@xstate/store-react": "^2.0.0",
|
|
43
|
+
"@eventuras/fides-auth": "0.9.0",
|
|
44
|
+
"@eventuras/fides-auth-store": "0.1.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"@xstate/store": "^4.0.0",
|
|
48
|
+
"@xstate/store-react": "^2.0.0",
|
|
49
|
+
"react": "^19.0.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/react": "^19.2.14",
|
|
53
|
+
"@xstate/store": "^4.0.0",
|
|
54
|
+
"@xstate/store-react": "^2.0.0",
|
|
55
|
+
"react": "^19.2.0",
|
|
56
|
+
"vite": "^8.0.8",
|
|
57
|
+
"vitest": "^4.1.6",
|
|
58
|
+
"@eventuras/typescript-config": "1.0.0",
|
|
59
|
+
"@eventuras/vite-config": "0.2.2"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=18"
|
|
63
|
+
},
|
|
64
|
+
"scripts": {
|
|
65
|
+
"build": "vite build",
|
|
66
|
+
"dev": "vite build --watch",
|
|
67
|
+
"test": "vitest run",
|
|
68
|
+
"test:watch": "vitest"
|
|
69
|
+
}
|
|
70
|
+
}
|