@molecule/app-react 1.0.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 +115 -0
- package/README.md +2645 -0
- package/dist/agent-identity.d.ts +40 -0
- package/dist/agent-identity.d.ts.map +1 -0
- package/dist/agent-identity.js +31 -0
- package/dist/agent-identity.js.map +1 -0
- package/dist/contexts.d.ts +69 -0
- package/dist/contexts.d.ts.map +1 -0
- package/dist/contexts.js +70 -0
- package/dist/contexts.js.map +1 -0
- package/dist/hooks/index.d.ts +32 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +32 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/useAIModels.d.ts +48 -0
- package/dist/hooks/useAIModels.d.ts.map +1 -0
- package/dist/hooks/useAIModels.js +145 -0
- package/dist/hooks/useAIModels.js.map +1 -0
- package/dist/hooks/useAsyncState.d.ts +41 -0
- package/dist/hooks/useAsyncState.d.ts.map +1 -0
- package/dist/hooks/useAsyncState.js +77 -0
- package/dist/hooks/useAsyncState.js.map +1 -0
- package/dist/hooks/useAuth.d.ts +43 -0
- package/dist/hooks/useAuth.d.ts.map +1 -0
- package/dist/hooks/useAuth.js +106 -0
- package/dist/hooks/useAuth.js.map +1 -0
- package/dist/hooks/useCapacitorApp.d.ts +35 -0
- package/dist/hooks/useCapacitorApp.d.ts.map +1 -0
- package/dist/hooks/useCapacitorApp.js +61 -0
- package/dist/hooks/useCapacitorApp.js.map +1 -0
- package/dist/hooks/useChangePassword.d.ts +34 -0
- package/dist/hooks/useChangePassword.d.ts.map +1 -0
- package/dist/hooks/useChangePassword.js +37 -0
- package/dist/hooks/useChangePassword.js.map +1 -0
- package/dist/hooks/useChat.d.ts +30 -0
- package/dist/hooks/useChat.d.ts.map +1 -0
- package/dist/hooks/useChat.js +1510 -0
- package/dist/hooks/useChat.js.map +1 -0
- package/dist/hooks/useDevice.d.ts +39 -0
- package/dist/hooks/useDevice.d.ts.map +1 -0
- package/dist/hooks/useDevice.js +41 -0
- package/dist/hooks/useDevice.js.map +1 -0
- package/dist/hooks/useEditor.d.ts +20 -0
- package/dist/hooks/useEditor.d.ts.map +1 -0
- package/dist/hooks/useEditor.js +98 -0
- package/dist/hooks/useEditor.js.map +1 -0
- package/dist/hooks/useForm.d.ts +87 -0
- package/dist/hooks/useForm.d.ts.map +1 -0
- package/dist/hooks/useForm.js +148 -0
- package/dist/hooks/useForm.js.map +1 -0
- package/dist/hooks/useHttp.d.ts +103 -0
- package/dist/hooks/useHttp.d.ts.map +1 -0
- package/dist/hooks/useHttp.js +161 -0
- package/dist/hooks/useHttp.js.map +1 -0
- package/dist/hooks/useLogger.d.ts +68 -0
- package/dist/hooks/useLogger.d.ts.map +1 -0
- package/dist/hooks/useLogger.js +95 -0
- package/dist/hooks/useLogger.js.map +1 -0
- package/dist/hooks/useLogin.d.ts +35 -0
- package/dist/hooks/useLogin.d.ts.map +1 -0
- package/dist/hooks/useLogin.js +37 -0
- package/dist/hooks/useLogin.js.map +1 -0
- package/dist/hooks/useOAuth.d.ts +83 -0
- package/dist/hooks/useOAuth.d.ts.map +1 -0
- package/dist/hooks/useOAuth.js +348 -0
- package/dist/hooks/useOAuth.js.map +1 -0
- package/dist/hooks/usePasswordReset.d.ts +43 -0
- package/dist/hooks/usePasswordReset.d.ts.map +1 -0
- package/dist/hooks/usePasswordReset.js +50 -0
- package/dist/hooks/usePasswordReset.js.map +1 -0
- package/dist/hooks/usePlatform.d.ts +38 -0
- package/dist/hooks/usePlatform.d.ts.map +1 -0
- package/dist/hooks/usePlatform.js +40 -0
- package/dist/hooks/usePlatform.js.map +1 -0
- package/dist/hooks/usePreview.d.ts +20 -0
- package/dist/hooks/usePreview.d.ts.map +1 -0
- package/dist/hooks/usePreview.js +62 -0
- package/dist/hooks/usePreview.js.map +1 -0
- package/dist/hooks/usePromise.d.ts +39 -0
- package/dist/hooks/usePromise.d.ts.map +1 -0
- package/dist/hooks/usePromise.js +92 -0
- package/dist/hooks/usePromise.js.map +1 -0
- package/dist/hooks/usePush.d.ts +50 -0
- package/dist/hooks/usePush.d.ts.map +1 -0
- package/dist/hooks/usePush.js +89 -0
- package/dist/hooks/usePush.js.map +1 -0
- package/dist/hooks/useRouter.d.ts +65 -0
- package/dist/hooks/useRouter.d.ts.map +1 -0
- package/dist/hooks/useRouter.js +120 -0
- package/dist/hooks/useRouter.js.map +1 -0
- package/dist/hooks/useSignup.d.ts +35 -0
- package/dist/hooks/useSignup.d.ts.map +1 -0
- package/dist/hooks/useSignup.js +37 -0
- package/dist/hooks/useSignup.js.map +1 -0
- package/dist/hooks/useStorage.d.ts +72 -0
- package/dist/hooks/useStorage.d.ts.map +1 -0
- package/dist/hooks/useStorage.js +144 -0
- package/dist/hooks/useStorage.js.map +1 -0
- package/dist/hooks/useStore.d.ts +51 -0
- package/dist/hooks/useStore.d.ts.map +1 -0
- package/dist/hooks/useStore.js +86 -0
- package/dist/hooks/useStore.js.map +1 -0
- package/dist/hooks/useTheme.d.ts +50 -0
- package/dist/hooks/useTheme.d.ts.map +1 -0
- package/dist/hooks/useTheme.js +135 -0
- package/dist/hooks/useTheme.js.map +1 -0
- package/dist/hooks/useTranslation.d.ts +69 -0
- package/dist/hooks/useTranslation.d.ts.map +1 -0
- package/dist/hooks/useTranslation.js +131 -0
- package/dist/hooks/useTranslation.js.map +1 -0
- package/dist/hooks/useVersion.d.ts +41 -0
- package/dist/hooks/useVersion.d.ts.map +1 -0
- package/dist/hooks/useVersion.js +73 -0
- package/dist/hooks/useVersion.js.map +1 -0
- package/dist/hooks/useWorkspace.d.ts +18 -0
- package/dist/hooks/useWorkspace.d.ts.map +1 -0
- package/dist/hooks/useWorkspace.js +67 -0
- package/dist/hooks/useWorkspace.js.map +1 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -0
- package/dist/providers.d.ts +189 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +248 -0
- package/dist/providers.js.map +1 -0
- package/dist/types.d.ts +381 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +82 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hook for OAuth authentication.
|
|
3
|
+
*
|
|
4
|
+
* Supports two initiation modes that share ONE callback path:
|
|
5
|
+
* - {@link UseOAuthReturn.redirect} — full-page redirect (the default; unchanged).
|
|
6
|
+
* - {@link UseOAuthReturn.loginViaPopup} — opens the provider in a popup so the
|
|
7
|
+
* opener page never navigates (used by in-app auth modals). On completion the
|
|
8
|
+
* popup relays the result to the opener via a STRICTLY-validated `postMessage`
|
|
9
|
+
* and closes itself; the opener establishes the session in place.
|
|
10
|
+
*
|
|
11
|
+
* Security model for the popup relay (see inline notes at each gate):
|
|
12
|
+
* - The popup posts ONLY to its own origin (`targetOrigin = location.origin`),
|
|
13
|
+
* never `'*'`, so the bearer token can never be delivered to another origin.
|
|
14
|
+
* - The opener accepts a result ONLY when ALL hold: `event.origin === location.origin`,
|
|
15
|
+
* `event.source === thePopupWindowWeOpened`, and `event.data.type` matches our
|
|
16
|
+
* private message type. Anything else is ignored — a forged cross-origin (or
|
|
17
|
+
* wrong-window) message cannot inject a session.
|
|
18
|
+
* - The popup-relay path is gated on `window.name === OAUTH_POPUP_WINDOW_NAME`
|
|
19
|
+
* AND `window.opener`, so an ordinary full-page callback is completely
|
|
20
|
+
* unaffected (it still establishes the session inline as before).
|
|
21
|
+
* - The opener tears down its listener + timers on result, on user-close, and on
|
|
22
|
+
* a hard timeout, so no stale handler lingers.
|
|
23
|
+
*
|
|
24
|
+
* @module
|
|
25
|
+
*/
|
|
26
|
+
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
27
|
+
import { useAuthClient } from './useAuth.js';
|
|
28
|
+
/**
|
|
29
|
+
* `window.open` name for the OAuth popup. The callback page relays its result to
|
|
30
|
+
* the opener ONLY when its own `window.name` equals this AND `window.opener`
|
|
31
|
+
* exists — so a normal (full-page) callback never takes the relay path.
|
|
32
|
+
*/
|
|
33
|
+
const OAUTH_POPUP_WINDOW_NAME = 'molecule-oauth-popup';
|
|
34
|
+
/** Private `postMessage` type for the popup→opener result relay. */
|
|
35
|
+
const OAUTH_RESULT_MESSAGE_TYPE = 'molecule:oauth-result';
|
|
36
|
+
/** Hard cap on how long the opener waits for the popup before giving up. */
|
|
37
|
+
const OAUTH_POPUP_TIMEOUT_MS = 5 * 60 * 1000;
|
|
38
|
+
/**
|
|
39
|
+
* Hook for OAuth authentication.
|
|
40
|
+
*
|
|
41
|
+
* Reads OAuth configuration from the provided config and provides
|
|
42
|
+
* helpers to build OAuth URLs and start a login (full-page or popup).
|
|
43
|
+
* Automatically handles OAuth callbacks by detecting `code` and `state`
|
|
44
|
+
* URL parameters and exchanging them for a session — and, when the callback is
|
|
45
|
+
* running inside a popup we opened, relaying the result to the opener instead.
|
|
46
|
+
*
|
|
47
|
+
* @param config - Optional OAuth configuration override.
|
|
48
|
+
* @param config.baseURL - Base URL for the API server (e.g. "https://api.example.com").
|
|
49
|
+
* @param config.oauthProviders - List of supported OAuth provider names (e.g. ["google", "github"]).
|
|
50
|
+
* @param config.oauthEndpoint - Path prefix for OAuth routes (defaults to "/oauth").
|
|
51
|
+
* @param config.loginEndpoint - Path for the OAuth login POST endpoint (defaults to "/users/log-in/oauth").
|
|
52
|
+
* @param config.onSuccess - Callback after successful OAuth login.
|
|
53
|
+
* @param config.onError - Callback on OAuth login failure.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```tsx
|
|
57
|
+
* const { providers, redirect, loginViaPopup } = useOAuth()
|
|
58
|
+
*
|
|
59
|
+
* return (
|
|
60
|
+
* <div>
|
|
61
|
+
* {providers.map(provider => (
|
|
62
|
+
* <button key={provider} onClick={() => loginViaPopup(provider)}>
|
|
63
|
+
* Login with {provider}
|
|
64
|
+
* </button>
|
|
65
|
+
* ))}
|
|
66
|
+
* </div>
|
|
67
|
+
* )
|
|
68
|
+
* ```
|
|
69
|
+
* @returns OAuth helpers: providers, getOAuthUrl, redirect, and loginViaPopup.
|
|
70
|
+
*/
|
|
71
|
+
export function useOAuth(config) {
|
|
72
|
+
const authClient = useAuthClient();
|
|
73
|
+
const providers = useMemo(() => config?.oauthProviders ?? [], [config?.oauthProviders]);
|
|
74
|
+
const baseURL = config?.baseURL ?? '';
|
|
75
|
+
const oauthEndpoint = config?.oauthEndpoint ?? '/oauth';
|
|
76
|
+
const loginEndpoint = config?.loginEndpoint ?? '/users/log-in/oauth';
|
|
77
|
+
const getOAuthUrl = useCallback((provider) => {
|
|
78
|
+
// Encode the provider as a single path segment so a (future) caller passing an
|
|
79
|
+
// untrusted value can never inject a path/query/scheme into the opened URL —
|
|
80
|
+
// defense-in-depth on top of the upstream static provider allowlist. Plain
|
|
81
|
+
// provider names ('github', 'google') pass through unchanged.
|
|
82
|
+
return `${baseURL}${oauthEndpoint}/${encodeURIComponent(provider)}`;
|
|
83
|
+
}, [baseURL, oauthEndpoint]);
|
|
84
|
+
/** Stash which provider initiated the flow so the callback knows it's ours. */
|
|
85
|
+
const stashProvider = (provider) => {
|
|
86
|
+
try {
|
|
87
|
+
sessionStorage.setItem('oauth_provider', provider);
|
|
88
|
+
}
|
|
89
|
+
catch (_error) {
|
|
90
|
+
/* SSR-safe — sessionStorage is unavailable in SSR/sandboxed environments */
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Append the current page's PATH as `redirect_to` so the api's initiation
|
|
95
|
+
* endpoint (`GET /users/oauth/:provider`) builds a provider `redirect_uri`
|
|
96
|
+
* that lands the callback back on THIS page — the one with `useOAuth`
|
|
97
|
+
* mounted (e.g. `/login`) — instead of the app root, where nothing may be
|
|
98
|
+
* listening for the `code` param. Path only (no origin/query): the server
|
|
99
|
+
* validates it as a same-origin absolute path, so it can never become an
|
|
100
|
+
* open redirect. Servers that predate the param simply ignore it.
|
|
101
|
+
*/
|
|
102
|
+
const withReturnPath = (url) => {
|
|
103
|
+
const path = typeof window !== 'undefined' ? window.location.pathname : '';
|
|
104
|
+
if (!path || path === '/')
|
|
105
|
+
return url;
|
|
106
|
+
return `${url}${url.includes('?') ? '&' : '?'}redirect_to=${encodeURIComponent(path)}`;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Establish the authenticated session from an exchange result: seed the access
|
|
110
|
+
* token + user and let `initialize()` flip `authenticated`. Mirrors what
|
|
111
|
+
* `authClient.login()` does for password login. Returns whether a user was set
|
|
112
|
+
* (a result with no user is not a real session).
|
|
113
|
+
*/
|
|
114
|
+
const establishSession = useCallback(async (token, user) => {
|
|
115
|
+
// Seed the token via the auth client's configured token storage adapter
|
|
116
|
+
// (in-memory by default) — NOT localStorage directly. Writing the bearer
|
|
117
|
+
// token to localStorage violates the in-memory-default storage contract and
|
|
118
|
+
// makes it JS-readable (XSS-exfiltratable). [P5FE-31 secure-by-default]
|
|
119
|
+
if (token)
|
|
120
|
+
authClient.setAccessToken(token);
|
|
121
|
+
if (user && typeof user === 'object') {
|
|
122
|
+
authClient.setUser(user);
|
|
123
|
+
await authClient.initialize();
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
}, [authClient]);
|
|
128
|
+
const redirect = useCallback((provider) => {
|
|
129
|
+
stashProvider(provider);
|
|
130
|
+
window.location.href = withReturnPath(getOAuthUrl(provider));
|
|
131
|
+
}, [getOAuthUrl]);
|
|
132
|
+
// The currently-open OAuth popup, so a second click focuses it instead of
|
|
133
|
+
// stacking a second flow (the relay gates already fail safe, but one window is
|
|
134
|
+
// cleaner and avoids an orphaned listener until the timeout reaps it).
|
|
135
|
+
const activePopupRef = useRef(null);
|
|
136
|
+
const loginViaPopup = useCallback((provider) => {
|
|
137
|
+
// A flow is already open — focus it rather than starting another.
|
|
138
|
+
if (activePopupRef.current && !activePopupRef.current.closed) {
|
|
139
|
+
try {
|
|
140
|
+
activePopupRef.current.focus();
|
|
141
|
+
}
|
|
142
|
+
catch (_error) {
|
|
143
|
+
/* best-effort focus */
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
stashProvider(provider);
|
|
148
|
+
const url = withReturnPath(getOAuthUrl(provider));
|
|
149
|
+
// A named popup. We intentionally do NOT pass `noopener` — the popup needs
|
|
150
|
+
// `window.opener` to relay its result back. The window only ever navigates
|
|
151
|
+
// to OUR api → the trusted provider → OUR origin, so this is the standard
|
|
152
|
+
// OAuth-popup trade-off (a malicious *provider* could touch `window.opener`,
|
|
153
|
+
// which is out of scope; we don't open attacker-controlled URLs here).
|
|
154
|
+
const features = 'popup=yes,width=520,height=680,menubar=no,toolbar=no,location=yes';
|
|
155
|
+
const popup = window.open(url, OAUTH_POPUP_WINDOW_NAME, features);
|
|
156
|
+
// Popup blocked (or unavailable) → fall back to a full-page redirect so login
|
|
157
|
+
// still works. No listener is registered in this branch.
|
|
158
|
+
if (!popup) {
|
|
159
|
+
window.location.href = url;
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
activePopupRef.current = popup;
|
|
163
|
+
let settled = false;
|
|
164
|
+
const timers = {};
|
|
165
|
+
const cleanup = () => {
|
|
166
|
+
window.removeEventListener('message', onMessage);
|
|
167
|
+
if (timers.closed)
|
|
168
|
+
clearInterval(timers.closed);
|
|
169
|
+
if (timers.timeout)
|
|
170
|
+
clearTimeout(timers.timeout);
|
|
171
|
+
if (activePopupRef.current === popup)
|
|
172
|
+
activePopupRef.current = null;
|
|
173
|
+
};
|
|
174
|
+
const onMessage = (event) => {
|
|
175
|
+
// SECURITY — accept the result ONLY from our own origin, ONLY from the exact
|
|
176
|
+
// popup window we opened, and ONLY when it carries our private type. Any one
|
|
177
|
+
// failing means a forged / unrelated message: ignore it silently. This is
|
|
178
|
+
// what prevents a malicious page from injecting a session.
|
|
179
|
+
if (event.origin !== window.location.origin)
|
|
180
|
+
return;
|
|
181
|
+
if (event.source !== popup)
|
|
182
|
+
return;
|
|
183
|
+
const data = event.data;
|
|
184
|
+
if (!data || typeof data !== 'object' || data.type !== OAUTH_RESULT_MESSAGE_TYPE)
|
|
185
|
+
return;
|
|
186
|
+
if (settled)
|
|
187
|
+
return;
|
|
188
|
+
settled = true;
|
|
189
|
+
cleanup();
|
|
190
|
+
try {
|
|
191
|
+
popup.close();
|
|
192
|
+
}
|
|
193
|
+
catch (_error) {
|
|
194
|
+
/* already closed — nothing to do */
|
|
195
|
+
}
|
|
196
|
+
if (data.ok) {
|
|
197
|
+
void establishSession(data.token, data.user).then((ok) => {
|
|
198
|
+
if (ok)
|
|
199
|
+
config?.onSuccess?.();
|
|
200
|
+
else
|
|
201
|
+
config?.onError?.('OAuth login failed');
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
config?.onError?.(typeof data.error === 'string' ? data.error : 'OAuth login failed');
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
window.addEventListener('message', onMessage);
|
|
209
|
+
// The user closed the popup without completing — silently stop waiting (no
|
|
210
|
+
// error toast for a deliberate cancel) and release the listener.
|
|
211
|
+
timers.closed = setInterval(() => {
|
|
212
|
+
if (popup.closed && !settled) {
|
|
213
|
+
settled = true;
|
|
214
|
+
cleanup();
|
|
215
|
+
}
|
|
216
|
+
}, 500);
|
|
217
|
+
// Hard safety timeout so a wedged popup never leaks a listener forever.
|
|
218
|
+
timers.timeout = setTimeout(() => {
|
|
219
|
+
if (!settled) {
|
|
220
|
+
settled = true;
|
|
221
|
+
cleanup();
|
|
222
|
+
try {
|
|
223
|
+
popup.close();
|
|
224
|
+
}
|
|
225
|
+
catch (_error) {
|
|
226
|
+
/* best-effort */
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}, OAUTH_POPUP_TIMEOUT_MS);
|
|
230
|
+
}, [getOAuthUrl, establishSession, config]);
|
|
231
|
+
// Handle OAuth callback — detect code + state in URL params
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
const params = new URLSearchParams(window.location.search);
|
|
234
|
+
const code = params.get('code');
|
|
235
|
+
const state = params.get('state');
|
|
236
|
+
if (!code)
|
|
237
|
+
return;
|
|
238
|
+
// Retrieve the provider that initiated the flow
|
|
239
|
+
let provider = null;
|
|
240
|
+
try {
|
|
241
|
+
provider = sessionStorage.getItem('oauth_provider');
|
|
242
|
+
}
|
|
243
|
+
catch (_error) {
|
|
244
|
+
/* SSR-safe — sessionStorage is unavailable in SSR/sandboxed environments */
|
|
245
|
+
}
|
|
246
|
+
if (!provider)
|
|
247
|
+
return;
|
|
248
|
+
// Are we the OAuth popup we opened? Relay the result to the opener instead of
|
|
249
|
+
// establishing the session in this (about-to-close) window. Gated on BOTH the
|
|
250
|
+
// window name AND window.opener so a normal full-page callback never relays.
|
|
251
|
+
const inPopup = typeof window !== 'undefined' && !!window.opener && window.name === OAUTH_POPUP_WINDOW_NAME;
|
|
252
|
+
// Clean up URL (remove code/state params) and sessionStorage
|
|
253
|
+
const cleanUrl = new URL(window.location.href);
|
|
254
|
+
cleanUrl.searchParams.delete('code');
|
|
255
|
+
cleanUrl.searchParams.delete('state');
|
|
256
|
+
window.history.replaceState({}, '', cleanUrl.toString());
|
|
257
|
+
try {
|
|
258
|
+
sessionStorage.removeItem('oauth_provider');
|
|
259
|
+
}
|
|
260
|
+
catch (_error) {
|
|
261
|
+
/* SSR-safe — sessionStorage is unavailable in SSR/sandboxed environments */
|
|
262
|
+
}
|
|
263
|
+
/** Relay a result to the opener (popup path) — origin-pinned, never `'*'`. */
|
|
264
|
+
const relayToOpener = (message) => {
|
|
265
|
+
try {
|
|
266
|
+
// targetOrigin is our OWN origin: the browser delivers the message (and the
|
|
267
|
+
// token it carries) ONLY if the opener is still on this origin. Never '*'.
|
|
268
|
+
window.opener?.postMessage(message, window.location.origin);
|
|
269
|
+
}
|
|
270
|
+
catch (_error) {
|
|
271
|
+
/* opener gone / cross-origin — the opener's close-poll will recover */
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
window.close();
|
|
275
|
+
}
|
|
276
|
+
catch (_error) {
|
|
277
|
+
/* best-effort */
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
// Exchange code for session
|
|
281
|
+
const exchangeCode = async () => {
|
|
282
|
+
try {
|
|
283
|
+
const response = await fetch(`${baseURL}${loginEndpoint}`, {
|
|
284
|
+
method: 'POST',
|
|
285
|
+
headers: { 'Content-Type': 'application/json' },
|
|
286
|
+
credentials: 'include', // Send cookies (oauth_state) for state validation
|
|
287
|
+
body: JSON.stringify({
|
|
288
|
+
server: provider,
|
|
289
|
+
code,
|
|
290
|
+
state: state || undefined,
|
|
291
|
+
redirect_uri: window.location.origin,
|
|
292
|
+
}),
|
|
293
|
+
});
|
|
294
|
+
if (!response.ok) {
|
|
295
|
+
const data = await response.json().catch(() => ({ error: 'OAuth login failed' }));
|
|
296
|
+
const error = data.error || 'OAuth login failed';
|
|
297
|
+
if (inPopup) {
|
|
298
|
+
relayToOpener({ type: OAUTH_RESULT_MESSAGE_TYPE, ok: false, error });
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
config?.onError?.(error);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const data = await response.json().catch(() => null);
|
|
305
|
+
// Store the token from the response
|
|
306
|
+
const authHeader = response.headers.get('authorization') || response.headers.get('set-authorization');
|
|
307
|
+
const token = authHeader ? authHeader.replace('Bearer ', '') : null;
|
|
308
|
+
// The authenticated user is `props` (molecule resource convention) or `user`
|
|
309
|
+
// (generic AuthResult). OAuth issues no refresh token, so we carry the token +
|
|
310
|
+
// user to whoever will own the session.
|
|
311
|
+
const user = data && typeof data === 'object'
|
|
312
|
+
? (data.props ??
|
|
313
|
+
data.user ??
|
|
314
|
+
null)
|
|
315
|
+
: null;
|
|
316
|
+
if (inPopup) {
|
|
317
|
+
// Relay to the opener — it (same origin) establishes the session in place.
|
|
318
|
+
// Do NOT establish it here; this window is about to close.
|
|
319
|
+
relayToOpener({ type: OAUTH_RESULT_MESSAGE_TYPE, ok: true, token, user });
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
// Full-page callback: establish the session here, then fire onSuccess —
|
|
323
|
+
// unchanged behavior.
|
|
324
|
+
const ok = await establishSession(token, user);
|
|
325
|
+
if (ok)
|
|
326
|
+
config?.onSuccess?.();
|
|
327
|
+
else
|
|
328
|
+
config?.onError?.('OAuth login failed');
|
|
329
|
+
}
|
|
330
|
+
catch (err) {
|
|
331
|
+
const error = err instanceof Error ? err.message : 'OAuth login failed';
|
|
332
|
+
if (inPopup) {
|
|
333
|
+
relayToOpener({ type: OAUTH_RESULT_MESSAGE_TYPE, ok: false, error });
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
config?.onError?.(error);
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
exchangeCode();
|
|
340
|
+
}, [baseURL, loginEndpoint, config, establishSession]);
|
|
341
|
+
return {
|
|
342
|
+
providers,
|
|
343
|
+
getOAuthUrl,
|
|
344
|
+
redirect,
|
|
345
|
+
loginViaPopup,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=useOAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOAuth.js","sourceRoot":"","sources":["../../src/hooks/useOAuth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hook for password reset with async state tracking.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { PasswordResetConfirm, PasswordResetRequest } from '@molecule/app-auth';
|
|
7
|
+
import { type UsePromiseState } from './usePromise.js';
|
|
8
|
+
/**
|
|
9
|
+
* Return type for usePasswordReset hook.
|
|
10
|
+
*/
|
|
11
|
+
export interface UsePasswordResetReturn {
|
|
12
|
+
requestStatus: UsePromiseState<void>['status'];
|
|
13
|
+
requestError: UsePromiseState<void>['error'];
|
|
14
|
+
confirmStatus: UsePromiseState<void>['status'];
|
|
15
|
+
confirmError: UsePromiseState<void>['error'];
|
|
16
|
+
requestReset: (data: PasswordResetRequest) => Promise<void>;
|
|
17
|
+
confirmReset: (data: PasswordResetConfirm) => Promise<void>;
|
|
18
|
+
reset: () => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Hook for password reset flow with async state tracking.
|
|
22
|
+
*
|
|
23
|
+
* Provides separate tracking for the request and confirm steps.
|
|
24
|
+
*
|
|
25
|
+
* @returns Password reset state and actions
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* const { requestStatus, requestError, requestReset, confirmReset } = usePasswordReset()
|
|
30
|
+
*
|
|
31
|
+
* // Step 1: Request reset
|
|
32
|
+
* <button onClick={() => requestReset({ email })} disabled={requestStatus === 'pending'}>
|
|
33
|
+
* Send Reset Email
|
|
34
|
+
* </button>
|
|
35
|
+
*
|
|
36
|
+
* // Step 2: Confirm reset
|
|
37
|
+
* <button onClick={() => confirmReset({ token, password })}>
|
|
38
|
+
* Set New Password
|
|
39
|
+
* </button>
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function usePasswordReset(): UsePasswordResetReturn;
|
|
43
|
+
//# sourceMappingURL=usePasswordReset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePasswordReset.d.ts","sourceRoot":"","sources":["../../src/hooks/usePasswordReset.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAGpF,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAElE;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAA;IAC9C,YAAY,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAA;IAC5C,aAAa,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAA;IAC9C,YAAY,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAA;IAC5C,YAAY,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3D,YAAY,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3D,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,IAAI,sBAAsB,CA4BzD"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hook for password reset with async state tracking.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { useCallback } from 'react';
|
|
7
|
+
import { useAuthClient } from './useAuth.js';
|
|
8
|
+
import { usePromise } from './usePromise.js';
|
|
9
|
+
/**
|
|
10
|
+
* Hook for password reset flow with async state tracking.
|
|
11
|
+
*
|
|
12
|
+
* Provides separate tracking for the request and confirm steps.
|
|
13
|
+
*
|
|
14
|
+
* @returns Password reset state and actions
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* const { requestStatus, requestError, requestReset, confirmReset } = usePasswordReset()
|
|
19
|
+
*
|
|
20
|
+
* // Step 1: Request reset
|
|
21
|
+
* <button onClick={() => requestReset({ email })} disabled={requestStatus === 'pending'}>
|
|
22
|
+
* Send Reset Email
|
|
23
|
+
* </button>
|
|
24
|
+
*
|
|
25
|
+
* // Step 2: Confirm reset
|
|
26
|
+
* <button onClick={() => confirmReset({ token, password })}>
|
|
27
|
+
* Set New Password
|
|
28
|
+
* </button>
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function usePasswordReset() {
|
|
32
|
+
const client = useAuthClient();
|
|
33
|
+
const requestFn = useCallback((data) => client.requestPasswordReset(data), [client]);
|
|
34
|
+
const confirmFn = useCallback((data) => client.confirmPasswordReset(data), [client]);
|
|
35
|
+
const [requestState, requestCall] = usePromise(requestFn);
|
|
36
|
+
const [confirmState, confirmCall] = usePromise(confirmFn);
|
|
37
|
+
return {
|
|
38
|
+
requestStatus: requestState.status,
|
|
39
|
+
requestError: requestState.error,
|
|
40
|
+
confirmStatus: confirmState.status,
|
|
41
|
+
confirmError: confirmState.error,
|
|
42
|
+
requestReset: requestCall,
|
|
43
|
+
confirmReset: confirmCall,
|
|
44
|
+
reset: () => {
|
|
45
|
+
requestState.reset();
|
|
46
|
+
confirmState.reset();
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=usePasswordReset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePasswordReset.js","sourceRoot":"","sources":["../../src/hooks/usePasswordReset.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAInC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAwB,MAAM,iBAAiB,CAAA;AAelE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,MAAM,GAAG,aAAa,EAAE,CAAA;IAE9B,MAAM,SAAS,GAAG,WAAW,CAC3B,CAAC,IAA0B,EAAE,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,IAAI,CAAC,EACjE,CAAC,MAAM,CAAC,CACT,CAAA;IAED,MAAM,SAAS,GAAG,WAAW,CAC3B,CAAC,IAA0B,EAAE,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,IAAI,CAAC,EACjE,CAAC,MAAM,CAAC,CACT,CAAA;IAED,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC,GAAG,UAAU,CAAC,SAAS,CAAC,CAAA;IACzD,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC,GAAG,UAAU,CAAC,SAAS,CAAC,CAAA;IAEzD,OAAO;QACL,aAAa,EAAE,YAAY,CAAC,MAAM;QAClC,YAAY,EAAE,YAAY,CAAC,KAAK;QAChC,aAAa,EAAE,YAAY,CAAC,MAAM;QAClC,YAAY,EAAE,YAAY,CAAC,KAAK;QAChC,YAAY,EAAE,WAAW;QACzB,YAAY,EAAE,WAAW;QACzB,KAAK,EAAE,GAAG,EAAE;YACV,YAAY,CAAC,KAAK,EAAE,CAAA;YACpB,YAAY,CAAC,KAAK,EAAE,CAAA;QACtB,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hook for platform detection.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { Platform } from '@molecule/app-platform';
|
|
7
|
+
/**
|
|
8
|
+
* Hook return type.
|
|
9
|
+
*/
|
|
10
|
+
export interface UsePlatformResult {
|
|
11
|
+
platform: Platform;
|
|
12
|
+
isNative: boolean;
|
|
13
|
+
isMobile: boolean;
|
|
14
|
+
isDesktop: boolean;
|
|
15
|
+
isWeb: boolean;
|
|
16
|
+
isDevelopment: boolean;
|
|
17
|
+
isProduction: boolean;
|
|
18
|
+
isPlatform: (...platforms: Platform[]) => boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Hook for platform detection.
|
|
22
|
+
*
|
|
23
|
+
* Uses module-level functions — platform is a singleton, not context-provided.
|
|
24
|
+
* Platform info is static and doesn't change at runtime, so this uses `useMemo`.
|
|
25
|
+
*
|
|
26
|
+
* @returns Platform information and utility methods
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```tsx
|
|
30
|
+
* const { isWeb, isMobile, isPlatform } = usePlatform()
|
|
31
|
+
*
|
|
32
|
+
* if (isPlatform('ios', 'android')) {
|
|
33
|
+
* return <NativeLayout />
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function usePlatform(): UsePlatformResult;
|
|
38
|
+
//# sourceMappingURL=usePlatform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePlatform.d.ts","sourceRoot":"","sources":["../../src/hooks/usePlatform.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAgB,MAAM,wBAAwB,CAAA;AAGpE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,QAAQ,CAAA;IAClB,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,OAAO,CAAA;IACd,aAAa,EAAE,OAAO,CAAA;IACtB,YAAY,EAAE,OAAO,CAAA;IACrB,UAAU,EAAE,CAAC,GAAG,SAAS,EAAE,QAAQ,EAAE,KAAK,OAAO,CAAA;CAClD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,IAAI,iBAAiB,CAe/C"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hook for platform detection.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { useMemo } from 'react';
|
|
7
|
+
import { isPlatform, platform } from '@molecule/app-platform';
|
|
8
|
+
/**
|
|
9
|
+
* Hook for platform detection.
|
|
10
|
+
*
|
|
11
|
+
* Uses module-level functions — platform is a singleton, not context-provided.
|
|
12
|
+
* Platform info is static and doesn't change at runtime, so this uses `useMemo`.
|
|
13
|
+
*
|
|
14
|
+
* @returns Platform information and utility methods
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* const { isWeb, isMobile, isPlatform } = usePlatform()
|
|
19
|
+
*
|
|
20
|
+
* if (isPlatform('ios', 'android')) {
|
|
21
|
+
* return <NativeLayout />
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function usePlatform() {
|
|
26
|
+
return useMemo(() => {
|
|
27
|
+
const info = platform();
|
|
28
|
+
return {
|
|
29
|
+
platform: info.platform,
|
|
30
|
+
isNative: info.isNative,
|
|
31
|
+
isMobile: info.isMobile,
|
|
32
|
+
isDesktop: info.isDesktop,
|
|
33
|
+
isWeb: info.isWeb,
|
|
34
|
+
isDevelopment: info.isDevelopment,
|
|
35
|
+
isProduction: info.isProduction,
|
|
36
|
+
isPlatform: (...platforms) => isPlatform(...platforms),
|
|
37
|
+
};
|
|
38
|
+
}, []);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=usePlatform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePlatform.js","sourceRoot":"","sources":["../../src/hooks/usePlatform.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAG/B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAgB7D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,OAAO,CAAC,GAAG,EAAE;QAClB,MAAM,IAAI,GAAiB,QAAQ,EAAE,CAAA;QAErC,OAAO;YACL,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,UAAU,EAAE,CAAC,GAAG,SAAqB,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;SACnE,CAAA;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;AACR,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hook for live preview provider.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { PreviewProvider } from '@molecule/app-live-preview';
|
|
7
|
+
import type { UsePreviewResult } from '../types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Access the preview provider from context.
|
|
10
|
+
* @returns The PreviewProvider instance from the nearest PreviewContext.
|
|
11
|
+
* @throws {Error} If called outside a PreviewProvider.
|
|
12
|
+
*/
|
|
13
|
+
export declare function usePreviewProvider(): PreviewProvider;
|
|
14
|
+
/**
|
|
15
|
+
* Hook for live preview management.
|
|
16
|
+
*
|
|
17
|
+
* @returns Preview state and controls: state (url, isLoading, device, error, isConnected), setUrl, refresh, setDevice, and openExternal.
|
|
18
|
+
*/
|
|
19
|
+
export declare function usePreview(): UsePreviewResult;
|
|
20
|
+
//# sourceMappingURL=usePreview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePreview.d.ts","sourceRoot":"","sources":["../../src/hooks/usePreview.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAe,eAAe,EAAgB,MAAM,4BAA4B,CAAA;AAG5F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEnD;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CAUpD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,gBAAgB,CA8C7C"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hook for live preview provider.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { useCallback, useContext, useRef, useSyncExternalStore } from 'react';
|
|
7
|
+
import { t } from '@molecule/app-i18n';
|
|
8
|
+
import { PreviewContext } from '../contexts.js';
|
|
9
|
+
/**
|
|
10
|
+
* Access the preview provider from context.
|
|
11
|
+
* @returns The PreviewProvider instance from the nearest PreviewContext.
|
|
12
|
+
* @throws {Error} If called outside a PreviewProvider.
|
|
13
|
+
*/
|
|
14
|
+
export function usePreviewProvider() {
|
|
15
|
+
const provider = useContext(PreviewContext);
|
|
16
|
+
if (!provider) {
|
|
17
|
+
throw new Error(t('react.error.usePreviewOutsideProvider', undefined, {
|
|
18
|
+
defaultValue: 'usePreviewProvider must be used within a PreviewProvider',
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
return provider;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Hook for live preview management.
|
|
25
|
+
*
|
|
26
|
+
* @returns Preview state and controls: state (url, isLoading, device, error, isConnected), setUrl, refresh, setDevice, and openExternal.
|
|
27
|
+
*/
|
|
28
|
+
export function usePreview() {
|
|
29
|
+
const provider = usePreviewProvider();
|
|
30
|
+
const cachedRef = useRef(null);
|
|
31
|
+
const getSnapshot = useCallback(() => {
|
|
32
|
+
const next = provider.getState();
|
|
33
|
+
const prev = cachedRef.current;
|
|
34
|
+
if (prev &&
|
|
35
|
+
prev.url === next.url &&
|
|
36
|
+
prev.currentUrl === next.currentUrl &&
|
|
37
|
+
prev.isLoading === next.isLoading &&
|
|
38
|
+
prev.device === next.device &&
|
|
39
|
+
prev.error === next.error &&
|
|
40
|
+
prev.isConnected === next.isConnected &&
|
|
41
|
+
prev.canGoBack === next.canGoBack &&
|
|
42
|
+
prev.canGoForward === next.canGoForward &&
|
|
43
|
+
prev.loadNonce === next.loadNonce) {
|
|
44
|
+
return prev;
|
|
45
|
+
}
|
|
46
|
+
cachedRef.current = next;
|
|
47
|
+
return next;
|
|
48
|
+
}, [provider]);
|
|
49
|
+
const subscribe = useCallback((onStoreChange) => {
|
|
50
|
+
return provider.subscribe(() => onStoreChange());
|
|
51
|
+
}, [provider]);
|
|
52
|
+
const state = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
53
|
+
const setUrl = useCallback((url) => provider.setUrl(url), [provider]);
|
|
54
|
+
const refresh = useCallback(() => provider.refresh(), [provider]);
|
|
55
|
+
const setDevice = useCallback((device) => provider.setDevice(device), [provider]);
|
|
56
|
+
const openExternal = useCallback(() => provider.openExternal(), [provider]);
|
|
57
|
+
const recordNavigation = useCallback((url, isReplace) => provider.recordNavigation(url, isReplace), [provider]);
|
|
58
|
+
const back = useCallback(() => provider.back(), [provider]);
|
|
59
|
+
const forward = useCallback(() => provider.forward(), [provider]);
|
|
60
|
+
return { state, setUrl, refresh, setDevice, openExternal, recordNavigation, back, forward };
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=usePreview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePreview.js","sourceRoot":"","sources":["../../src/hooks/usePreview.ts"],"names":[],"mappings":"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"}
|