@softwarepatterns/am-react 0.1.0 → 0.2.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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Headless React auth adapter for **AccountMaker (Am)**.
4
4
 
5
5
  This package exposes the React provider and hook for an `Am` instance and its
6
- current `AuthSession`.
6
+ current session.
7
7
 
8
8
  Examples live in [`examples/`](./examples).
9
9
 
@@ -37,6 +37,20 @@ export function App() {
37
37
  }
38
38
  ```
39
39
 
40
+ ## Callback props
41
+
42
+ `AuthProvider` exposes auth lifecycle callbacks that mirror the core `am`
43
+ events:
44
+
45
+ - `onSignedIn`
46
+ - `onTokensUpdated`
47
+ - `onProfileUpdated`
48
+ - `onAuthLost`
49
+ - `onReloadRequired`
50
+
51
+ `onAuthLost` is for recoverable client-side auth loss. `onReloadRequired` is
52
+ the terminal transition that should end in a hard navigation.
53
+
40
54
  ## Exports
41
55
 
42
56
  - `AuthProvider`
@@ -1,12 +1,44 @@
1
+ /**
2
+ * Provides the headless React auth context for an `Am` instance.
3
+ *
4
+ * This file owns the provider and hooks that expose the current session to a
5
+ * React tree. It also owns the translation from `am` auth events into React
6
+ * state and callback props.
7
+ *
8
+ * Local invariants:
9
+ * - sessions are obtained from `Am`, never constructed here
10
+ * - `signedIn` is the only event that replaces React session state
11
+ * - `authLost` and `reloadRequired` are forwarded without clearing React state
12
+ * - startup restores or refreshes the current session before marking ready
13
+ */
1
14
  import React from 'react';
2
- import { Am, AuthSession, AuthError } from '@softwarepatterns/am';
15
+ import { Am, AuthError, type AuthSession } from '@softwarepatterns/am';
3
16
  import type { SessionProfile, SessionTokens } from '@softwarepatterns/am';
4
17
  export type AuthProviderProps = {
18
+ /**
19
+ * `Am` instance whose session state drives this provider.
20
+ */
5
21
  am: Am;
6
- onRefresh?: (newTokens: SessionTokens) => void;
7
- onProfileChange?: (profile: SessionProfile) => void;
8
- onUnauthenticated?: (e: AuthError) => void;
9
- onSessionChange?: (session: AuthSession | null) => void;
22
+ /**
23
+ * Called when the current session rotates tokens.
24
+ */
25
+ onTokensUpdated?: (newTokens: SessionTokens) => void;
26
+ /**
27
+ * Called when the current session refreshes profile data.
28
+ */
29
+ onProfileUpdated?: (profile: SessionProfile) => void;
30
+ /**
31
+ * Called when auth is lost without requiring a reload.
32
+ */
33
+ onAuthLost?: (e: AuthError) => void;
34
+ /**
35
+ * Called after `Am` establishes the signed-in session.
36
+ */
37
+ onSignedIn?: (session: AuthSession) => void | Promise<void>;
38
+ /**
39
+ * Called when the current session can continue only after a hard reload.
40
+ */
41
+ onReloadRequired?: () => void;
10
42
  };
11
43
  export type AuthContextValue = {
12
44
  auth: Am;
@@ -1,6 +1,19 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Provides the headless React auth context for an `Am` instance.
4
+ *
5
+ * This file owns the provider and hooks that expose the current session to a
6
+ * React tree. It also owns the translation from `am` auth events into React
7
+ * state and callback props.
8
+ *
9
+ * Local invariants:
10
+ * - sessions are obtained from `Am`, never constructed here
11
+ * - `signedIn` is the only event that replaces React session state
12
+ * - `authLost` and `reloadRequired` are forwarded without clearing React state
13
+ * - startup restores or refreshes the current session before marking ready
14
+ */
2
15
  import React, { useMemo, createContext, useContext, useEffect, useState, } from 'react';
3
- import { Am, AuthSession, AuthError, } from '@softwarepatterns/am'; // adjust import paths
16
+ import { Am, AuthError, } from '@softwarepatterns/am'; // adjust import paths
4
17
  const AuthContext = createContext(null);
5
18
  /**
6
19
  * Hook to access authentication state and actions.
@@ -57,37 +70,41 @@ export function useRequiredAuth() {
57
70
  * ```;
58
71
  */
59
72
  export function AuthProvider(props) {
60
- const { children, am, onUnauthenticated, onProfileChange, onRefresh, onSessionChange, } = props;
73
+ const { children, am, onAuthLost, onProfileUpdated, onTokensUpdated, onSignedIn, onReloadRequired, } = props;
61
74
  const [session, setSession] = useState(null);
62
75
  const [isReady, setIsReady] = useState(false);
63
76
  useEffect(() => {
64
77
  const unsubs = [];
65
- unsubs.push(am.on('sessionChange', (session) => {
78
+ unsubs.push(am.on('signedIn', (session) => {
66
79
  const syncSessionState = async () => {
67
- if (onSessionChange) {
68
- await Promise.resolve(onSessionChange(session));
80
+ if (onSignedIn) {
81
+ await Promise.resolve(onSignedIn(session));
69
82
  }
70
83
  setSession(session);
71
84
  };
72
85
  syncSessionState().catch((error) => {
73
- console.error('Failed to apply session change', error);
86
+ console.error('Failed to apply signed-in state', error);
74
87
  setSession(session);
75
88
  });
76
89
  }));
77
- unsubs.push(am.on('unauthenticated', (e) => {
78
- // Clear the session to prevent further refresh attempts, but do NOT
79
- // update React state. The page continues to render during navigation,
80
- // and clearing the session from React would cause components to crash.
81
- am.session?.clear();
82
- if (onUnauthenticated) {
83
- onUnauthenticated(e);
84
- }
85
- }));
86
- if (onProfileChange) {
87
- unsubs.push(am.on('profileChange', onProfileChange));
90
+ if (onAuthLost) {
91
+ // authLost is recoverable. Forward it to the app without changing the
92
+ // current session state in React.
93
+ unsubs.push(am.on('authLost', onAuthLost));
94
+ }
95
+ if (onProfileUpdated) {
96
+ unsubs.push(am.on('profileUpdated', onProfileUpdated));
97
+ }
98
+ if (onTokensUpdated) {
99
+ unsubs.push(am.on('tokensUpdated', onTokensUpdated));
88
100
  }
89
- if (onRefresh) {
90
- unsubs.push(am.on('refresh', onRefresh));
101
+ if (onReloadRequired) {
102
+ unsubs.push(am.on('reloadRequired', () => {
103
+ // Keep the current session in React while the app performs a hard
104
+ // navigation. Clearing it early can crash components during the
105
+ // transition.
106
+ onReloadRequired();
107
+ }));
91
108
  }
92
109
  (async () => {
93
110
  const currentSession = am.restoreSession() ?? am.session;
@@ -97,7 +114,7 @@ export function AuthProvider(props) {
97
114
  setSession(currentSession);
98
115
  }
99
116
  catch {
100
- currentSession.clear();
117
+ setSession(null);
101
118
  }
102
119
  }
103
120
  else {
@@ -107,7 +124,7 @@ export function AuthProvider(props) {
107
124
  setIsReady(true);
108
125
  })().catch(console.error);
109
126
  return () => unsubs.forEach((u) => u());
110
- }, [am, onProfileChange, onRefresh, onUnauthenticated, onSessionChange]);
127
+ }, [am, onAuthLost, onProfileUpdated, onReloadRequired, onSignedIn, onTokensUpdated]);
111
128
  const value = useMemo(() => ({ auth: am, session, isReady }), [am, session, isReady]);
112
129
  return _jsx(AuthContext.Provider, { value: value, children: children });
113
130
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AuthProvider.js","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AuthProvider.js","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softwarepatterns/am-react",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Headless React auth adapter for AccountMaker (Am)",
5
5
  "keywords": [
6
6
  "authentication",
@@ -47,7 +47,7 @@
47
47
  "lint": "oxlint"
48
48
  },
49
49
  "dependencies": {
50
- "@softwarepatterns/am": "^0.3.1"
50
+ "@softwarepatterns/am": "^0.4.0"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "react": ">=19"