@passkeyme/react-auth 1.0.0 → 1.1.1

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/index.js CHANGED
@@ -2,10 +2,351 @@
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var React = require('react');
5
- var reactCore = require('@passkeyme/react-core');
6
5
  var auth = require('@passkeyme/auth');
7
6
  var reactDom = require('react-dom');
8
7
 
8
+ /**
9
+ * React Context Provider for PasskeyMe Authentication (Core)
10
+ * This is the shared provider for React and React Native
11
+ */
12
+ const PasskeymeContext = React.createContext(null);
13
+ const usePasskeymeContext = () => {
14
+ const context = React.useContext(PasskeymeContext);
15
+ if (!context) {
16
+ throw new Error("usePasskeymeContext must be used within a PasskeymeProvider");
17
+ }
18
+ return context;
19
+ };
20
+ const PasskeymeProvider$1 = ({ config, children, loadingComponent = null, errorComponent, onAuthChange, onError, }) => {
21
+ const [auth$1] = React.useState(() => new auth.PasskeymeAuth(config));
22
+ const [authState, setAuthState] = React.useState({
23
+ user: null,
24
+ loading: true,
25
+ error: null,
26
+ isAuthenticated: false,
27
+ });
28
+ const [authLoading, setAuthLoading] = React.useState(false);
29
+ const [appConfig, setAppConfig] = React.useState(null);
30
+ React.useEffect(() => {
31
+ // Subscribe to state changes using the new subscription system
32
+ const unsubscribeState = auth$1.subscribe((newState) => {
33
+ setAuthState(newState);
34
+ // Call auth change callback
35
+ if (onAuthChange) {
36
+ onAuthChange(newState.user);
37
+ }
38
+ // Call error callback
39
+ if (newState.error && onError) {
40
+ onError(newState.error);
41
+ }
42
+ });
43
+ // Set up event listener for additional auth events
44
+ const unsubscribeEvents = auth$1.addEventListener((event) => {
45
+ // Handle specific events
46
+ switch (event.type) {
47
+ case "login":
48
+ if (onAuthChange && event.user) {
49
+ onAuthChange(event.user);
50
+ }
51
+ break;
52
+ case "logout":
53
+ if (onAuthChange) {
54
+ onAuthChange(null);
55
+ }
56
+ break;
57
+ case "error":
58
+ if (onError && event.error) {
59
+ onError(event.error);
60
+ }
61
+ break;
62
+ }
63
+ });
64
+ // Initialize auth
65
+ const initAuth = async () => {
66
+ try {
67
+ await auth$1.init();
68
+ // Load app configuration
69
+ try {
70
+ const config = await auth$1.getAppConfig();
71
+ setAppConfig(config);
72
+ }
73
+ catch (configError) {
74
+ console.warn("Failed to load app configuration:", configError);
75
+ }
76
+ }
77
+ catch (error) {
78
+ const errorMessage = error instanceof Error
79
+ ? error.message
80
+ : "Authentication initialization failed";
81
+ if (onError) {
82
+ onError(errorMessage);
83
+ }
84
+ }
85
+ };
86
+ initAuth();
87
+ // Cleanup
88
+ return () => {
89
+ unsubscribeState();
90
+ unsubscribeEvents();
91
+ };
92
+ }, [auth$1, onAuthChange, onError]);
93
+ const contextValue = {
94
+ auth: auth$1,
95
+ user: authState.user,
96
+ loading: authState.loading,
97
+ error: authState.error,
98
+ isAuthenticated: authState.isAuthenticated,
99
+ authLoading,
100
+ setAuthLoading,
101
+ config,
102
+ appConfig,
103
+ };
104
+ // Show loading component if provided
105
+ if (authState.loading && loadingComponent) {
106
+ return React.createElement(React.Fragment, null, loadingComponent);
107
+ }
108
+ // Show error component if provided
109
+ if (authState.error && !authState.isAuthenticated && errorComponent) {
110
+ return React.createElement(React.Fragment, null, errorComponent(authState.error));
111
+ }
112
+ return (React.createElement(PasskeymeContext.Provider, { value: contextValue }, children));
113
+ };
114
+
115
+ /**
116
+ * Core React hooks for PasskeyMe Authentication
117
+ * Shared between React and React Native
118
+ */
119
+ // Simple debug function for core package
120
+ function debugLog$1(config, ...args) {
121
+ if (config.debug) {
122
+ console.log("[PasskeymeAuth]", ...args);
123
+ }
124
+ }
125
+ const usePasskeyme = () => {
126
+ const { auth: auth$1, user, loading, error, isAuthenticated, authLoading, setAuthLoading, config, } = usePasskeymeContext();
127
+ if (!auth$1) {
128
+ throw new Error("PasskeyMe auth instance not available");
129
+ }
130
+ const redirectToLogin = React.useCallback((options) => {
131
+ return auth$1.redirectToLogin(options);
132
+ }, [auth$1]);
133
+ const redirectToOAuth = React.useCallback((provider, redirectUri) => {
134
+ return auth$1.redirectToOAuth(provider, redirectUri);
135
+ }, [auth$1]);
136
+ const handleAuthCallback = React.useCallback(async () => {
137
+ return auth$1.handleAuthCallback();
138
+ }, [auth$1]);
139
+ const handleCallback = React.useCallback(async () => {
140
+ return auth$1.handleCallback();
141
+ }, [auth$1]);
142
+ const handleTokenCallback = React.useCallback(async (token) => {
143
+ return auth$1.handleTokenCallback(token);
144
+ }, [auth$1]);
145
+ const logout = React.useCallback(async () => {
146
+ return auth$1.logout();
147
+ }, [auth$1]);
148
+ const getAccessToken = React.useCallback(async () => {
149
+ return auth$1.getAccessToken();
150
+ }, [auth$1]);
151
+ const refreshToken = React.useCallback(async () => {
152
+ return auth$1.refreshToken();
153
+ }, [auth$1]);
154
+ const getBaseUrl = React.useCallback(() => {
155
+ return auth$1.getBaseUrl();
156
+ }, [auth$1]);
157
+ const getAppId = React.useCallback(() => {
158
+ return auth$1.getAppId();
159
+ }, [auth$1]);
160
+ const isPasskeySupported = React.useCallback(() => {
161
+ return auth$1.isPasskeySupported();
162
+ }, [auth$1]);
163
+ const hasStoredPasskeys = React.useCallback(async (username) => {
164
+ return await auth$1.hasStoredPasskeys(username);
165
+ }, [auth$1]);
166
+ const passkeyLogin = React.useCallback(async (username, apiKey) => {
167
+ return auth$1.passkeyLogin(username, apiKey);
168
+ }, [auth$1]);
169
+ const smartLogin = React.useCallback(async (username, apiKey) => {
170
+ return auth$1.smartLogin(username, apiKey);
171
+ }, [auth$1]);
172
+ const getAppConfig = React.useCallback(async () => {
173
+ return auth$1.getAppConfig();
174
+ }, [auth$1]);
175
+ const triggerPasskeymeAuth = React.useCallback(async (options = {}) => {
176
+ var _a, _b;
177
+ const { username, apiKey, mode = "hosted", onSuccess, onError, onOAuthRequired, showNotifications = false, // Default to false for core package
178
+ forcePasskeyOnly = false, } = options;
179
+ debugLog$1(config, "triggerPasskeymeAuth", "Starting authentication with options:", {
180
+ username: username || "(discoverable)",
181
+ hasApiKey: !!(apiKey ||
182
+ config.passkeyApiKey ||
183
+ ((_a = config.passkey) === null || _a === void 0 ? void 0 : _a.apiKey)),
184
+ mode,
185
+ forcePasskeyOnly,
186
+ showNotifications,
187
+ isPasskeySupported: auth$1.isPasskeySupported(),
188
+ // Note: hasStoredPasskeys is now async, so we can't include it in this sync log
189
+ });
190
+ try {
191
+ setAuthLoading(true);
192
+ // Get effective API key
193
+ const effectiveApiKey = apiKey || config.passkeyApiKey || ((_b = config.passkey) === null || _b === void 0 ? void 0 : _b.apiKey);
194
+ if (forcePasskeyOnly) {
195
+ // Force passkey-only authentication
196
+ debugLog$1(config, "triggerPasskeymeAuth", "Force passkey-only mode");
197
+ if (!effectiveApiKey) {
198
+ throw new Error("API key required for passkey authentication");
199
+ }
200
+ const result = await auth$1.passkeyLogin(username || undefined, effectiveApiKey);
201
+ debugLog$1(config, "triggerPasskeymeAuth", "Passkey-only authentication successful");
202
+ if (onSuccess) {
203
+ onSuccess(result, "passkey");
204
+ }
205
+ }
206
+ else {
207
+ // Try passkey authentication first, then handle fallback based on mode
208
+ try {
209
+ debugLog$1(config, "triggerPasskeymeAuth", "Attempting passkey authentication");
210
+ // Validate that we have an API key for passkey authentication
211
+ if (!effectiveApiKey) {
212
+ debugLog$1(config, "triggerPasskeymeAuth", "No API key available for passkey auth, skipping to fallback");
213
+ throw new Error("No API key available for passkey authentication");
214
+ }
215
+ let authUsername = username;
216
+ // If no username provided, let smartLogin handle the logic
217
+ // (it will check app config, localStorage, discoverable credentials, etc.)
218
+ if (!authUsername) {
219
+ debugLog$1(config, "triggerPasskeymeAuth", "No username provided, letting smartLogin handle auto-detection");
220
+ }
221
+ // Attempt smart passkey authentication (handles localStorage, discoverable credentials, etc.)
222
+ debugLog$1(config, "triggerPasskeymeAuth", "Calling smartLogin with username:", authUsername || "(auto-detect)");
223
+ const result = await auth$1.smartLogin(authUsername, effectiveApiKey);
224
+ debugLog$1(config, "triggerPasskeymeAuth", "Smart authentication result:", result);
225
+ if (result.method === "passkey" && result.user) {
226
+ debugLog$1(config, "triggerPasskeymeAuth", "Passkey authentication successful");
227
+ if (onSuccess) {
228
+ onSuccess(result.user, "passkey");
229
+ }
230
+ }
231
+ else {
232
+ // smartLogin returned redirect method, which means it already redirected
233
+ debugLog$1(config, "triggerPasskeymeAuth", "smartLogin redirected to hosted auth");
234
+ return;
235
+ }
236
+ }
237
+ catch (passkeyError) {
238
+ // Passkey failed, handle fallback based on mode
239
+ debugLog$1(config, "triggerPasskeymeAuth", "Passkey authentication failed:", passkeyError);
240
+ // Enhanced error handling for PasskeymeError
241
+ if (passkeyError instanceof auth.PasskeymeError) {
242
+ debugLog$1(config, "triggerPasskeymeAuth", "PasskeymeError details:", {
243
+ code: passkeyError.code,
244
+ message: passkeyError.message,
245
+ userMessage: passkeyError.userMessage,
246
+ recoverable: passkeyError.recoverable,
247
+ retryable: passkeyError.retryable,
248
+ suggestedAction: passkeyError.suggestedAction,
249
+ });
250
+ // Handle specific error codes that should not trigger fallback
251
+ if (passkeyError.code === auth.PasskeymeErrorCode.USER_CANCELLED ||
252
+ passkeyError.code === auth.PasskeymeErrorCode.PASSKEY_CANCELLED) {
253
+ // User cancelled - don't fallback, just call error handler
254
+ if (onError) {
255
+ onError(passkeyError.userMessage);
256
+ }
257
+ return;
258
+ }
259
+ if (passkeyError.code === auth.PasskeymeErrorCode.PASSKEY_NOT_SUPPORTED) {
260
+ // Passkeys not supported - immediately fallback without retrying
261
+ debugLog$1(config, "triggerPasskeymeAuth", "Passkeys not supported, proceeding to fallback");
262
+ }
263
+ }
264
+ debugLog$1(config, "triggerPasskeymeAuth", "Handling fallback for mode:", mode);
265
+ if (mode === "hosted") {
266
+ // Hosted mode: redirect to hosted auth pages
267
+ debugLog$1(config, "triggerPasskeymeAuth", "Passkey failed, redirecting to hosted auth");
268
+ auth$1.redirectToLogin();
269
+ return;
270
+ }
271
+ else if (mode === "inline") {
272
+ // Inline mode: provide OAuth providers to developer
273
+ debugLog$1(config, "triggerPasskeymeAuth", "Passkey failed, providing OAuth options for inline mode");
274
+ if (onOAuthRequired) {
275
+ try {
276
+ // Get available OAuth providers from app config
277
+ const appConfig = await auth$1.getAppConfig();
278
+ const providers = appConfig.oauthProviders || [
279
+ "google",
280
+ "github",
281
+ ];
282
+ debugLog$1(config, "triggerPasskeymeAuth", "Available OAuth providers:", providers);
283
+ onOAuthRequired(providers);
284
+ return;
285
+ }
286
+ catch (configError) {
287
+ debugLog$1(config, "triggerPasskeymeAuth", "Failed to get app config, using default providers");
288
+ onOAuthRequired(["google", "github"]);
289
+ return;
290
+ }
291
+ }
292
+ else {
293
+ throw new Error("Passkey authentication failed and no onOAuthRequired callback provided for inline mode");
294
+ }
295
+ }
296
+ }
297
+ }
298
+ }
299
+ catch (error) {
300
+ console.error("[triggerPasskeymeAuth] Authentication error:", error);
301
+ let errorMessage = "Authentication failed";
302
+ // Enhanced error handling for PasskeymeError
303
+ if (error instanceof auth.PasskeymeError) {
304
+ errorMessage = error.userMessage;
305
+ debugLog$1(config, "triggerPasskeymeAuth", "Final error handling:", {
306
+ code: error.code,
307
+ userMessage: error.userMessage,
308
+ retryable: error.retryable,
309
+ suggestedAction: error.suggestedAction,
310
+ });
311
+ }
312
+ else {
313
+ errorMessage = (error === null || error === void 0 ? void 0 : error.message) || "Authentication failed";
314
+ }
315
+ if (onError) {
316
+ onError(errorMessage);
317
+ }
318
+ }
319
+ finally {
320
+ setAuthLoading(false);
321
+ }
322
+ }, [auth$1, config, setAuthLoading]);
323
+ return {
324
+ user,
325
+ loading,
326
+ error,
327
+ isAuthenticated,
328
+ config,
329
+ redirectToLogin,
330
+ redirectToOAuth,
331
+ handleAuthCallback,
332
+ handleCallback,
333
+ handleTokenCallback,
334
+ logout,
335
+ getAccessToken,
336
+ refreshToken,
337
+ getBaseUrl,
338
+ getAppId,
339
+ isPasskeySupported,
340
+ hasStoredPasskeys,
341
+ passkeyLogin,
342
+ smartLogin,
343
+ getAppConfig,
344
+ triggerPasskeymeAuth,
345
+ authLoading,
346
+ auth: auth$1,
347
+ };
348
+ };
349
+
9
350
  const NotificationContext = React.createContext(null);
10
351
  const useNotifications = () => {
11
352
  const context = React.useContext(NotificationContext);
@@ -295,10 +636,10 @@ const PasskeymeProvider = ({ children, config, enableNotifications = true, ...co
295
636
  };
296
637
  }, [config]);
297
638
  if (enableNotifications) {
298
- return (jsxRuntime.jsx(reactCore.PasskeymeProvider, { ...coreProviderProps, config: enhancedConfig, children: jsxRuntime.jsx(NotificationProvider, { children: children }) }));
639
+ return (jsxRuntime.jsx(PasskeymeProvider$1, { ...coreProviderProps, config: enhancedConfig, children: jsxRuntime.jsx(NotificationProvider, { children: children }) }));
299
640
  }
300
641
  // If notifications are disabled, just use the core provider
301
- return (jsxRuntime.jsx(reactCore.PasskeymeProvider, { ...coreProviderProps, config: enhancedConfig, children: children }));
642
+ return (jsxRuntime.jsx(PasskeymeProvider$1, { ...coreProviderProps, config: enhancedConfig, children: children }));
302
643
  };
303
644
 
304
645
  /**
@@ -578,7 +919,7 @@ const useLoadingState = (options = {}) => {
578
919
  * Hook to get only the authentication state
579
920
  */
580
921
  const useAuthState = () => {
581
- const { user, loading, error, isAuthenticated } = reactCore.usePasskeyme();
922
+ const { user, loading, error, isAuthenticated } = usePasskeyme();
582
923
  return {
583
924
  user,
584
925
  loading,
@@ -590,7 +931,7 @@ const useAuthState = () => {
590
931
  * Hook to get only the authentication methods
591
932
  */
592
933
  const useAuth = () => {
593
- const { redirectToLogin, redirectToOAuth, handleAuthCallback, handleCallback, logout, getAccessToken, refreshToken, } = reactCore.usePasskeyme();
934
+ const { redirectToLogin, redirectToOAuth, handleAuthCallback, handleCallback, logout, getAccessToken, refreshToken, } = usePasskeyme();
594
935
  return {
595
936
  redirectToLogin,
596
937
  redirectToOAuth,
@@ -629,7 +970,7 @@ const DEFAULT_STORAGE_KEY = "passkeyme_last_username";
629
970
  * ```
630
971
  */
631
972
  function useUsernameManager({ initialUsername = "", rememberUsername: initialRememberUsername = true, storageKey = DEFAULT_STORAGE_KEY, appId, } = {}) {
632
- const { config } = reactCore.usePasskeyme();
973
+ const { config } = usePasskeyme();
633
974
  // Use app ID from config if not provided
634
975
  const effectiveAppId = appId || config.appId;
635
976
  // Create app-specific storage key
@@ -726,7 +1067,7 @@ const providerNames = {
726
1067
  facebook: "Facebook",
727
1068
  };
728
1069
  const PasskeymeOAuthButtonComponent = ({ provider, variant = "default", size = "medium", redirectUri, children, className = "", style = {}, disabled = false, onClick, loading: externalLoading = false, }) => {
729
- const { redirectToOAuth, loading: authLoading } = reactCore.usePasskeyme();
1070
+ const { redirectToOAuth, loading: authLoading } = usePasskeyme();
730
1071
  const [internalLoading, setInternalLoading] = React.useState(false);
731
1072
  // Memoize loading state calculation
732
1073
  const isLoading = React.useMemo(() => authLoading || externalLoading || internalLoading, [authLoading, externalLoading, internalLoading]);
@@ -1493,7 +1834,7 @@ const PasskeymeButtonComponent = ({ username, apiKey, children, className, style
1493
1834
  // Accessibility props
1494
1835
  "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-labelledby": ariaLabelledBy, "aria-pressed": ariaPressed, id, tabIndex, onKeyDown, onFocus, onBlur, autoFocus = false, }) => {
1495
1836
  var _a;
1496
- const { smartLogin, isPasskeySupported, redirectToLogin, config } = reactCore.usePasskeyme();
1837
+ const { smartLogin, isPasskeySupported, redirectToLogin, config } = usePasskeyme();
1497
1838
  const notifications = useNotifications();
1498
1839
  const [loading, setLoading] = React.useState(false);
1499
1840
  const [, setIsFocused] = React.useState(false);
@@ -1902,7 +2243,7 @@ onSuccess, onError, onProviderSelect, onPasskeyAttempt, onOAuthRequired, onLogou
1902
2243
  // Advanced options
1903
2244
  debugMode = false, passkeyOptions = {}, }) => {
1904
2245
  var _a, _b, _c;
1905
- const { user, isAuthenticated, logout, triggerPasskeymeAuth, authLoading, isPasskeySupported, } = reactCore.usePasskeyme();
2246
+ const { user, isAuthenticated, logout, triggerPasskeymeAuth, authLoading, isPasskeySupported, } = usePasskeyme();
1906
2247
  const [showOAuthOptions, setShowOAuthOptions] = React.useState(!hideProvidersInitially);
1907
2248
  const [availableProviders, setAvailableProviders] = React.useState(providers);
1908
2249
  const [passkeyAttempted, setPasskeyAttempted] = React.useState(false);
@@ -5925,7 +6266,7 @@ const DefaultPasskeyPrompt = ({ user, onRegister, onSkip, loading, }) => (jsxRun
5925
6266
  */
5926
6267
  const PasskeymeCallbackHandler = ({ loadingComponent: LoadingComponent, errorComponent: ErrorComponent, successRedirect = "/", errorRedirect = "/login", onSuccess, onError, passkey, }) => {
5927
6268
  var _a, _b, _c, _d, _e;
5928
- const { handleCallback, user, error: authError, getBaseUrl, handleTokenCallback, config, auth, } = reactCore.usePasskeyme();
6269
+ const { handleCallback, user, error: authError, getBaseUrl, handleTokenCallback, config, auth, } = usePasskeyme();
5929
6270
  const notifications = useNotifications();
5930
6271
  const [state, setState] = React.useState({
5931
6272
  loading: true,
@@ -6039,7 +6380,7 @@ const PasskeymeCallbackHandler = ({ loadingComponent: LoadingComponent, errorCom
6039
6380
  updateState({ passkeyRegistering: true });
6040
6381
  // Create axios instance for PasskeyMe API
6041
6382
  const axiosInstance = axios$1.create({
6042
- baseURL: `${getBaseUrl()}/api/webauthn/${config.appId}`,
6383
+ baseURL: `${getBaseUrl()}/webauthn/${config.appId}`,
6043
6384
  headers: {
6044
6385
  "x-api-key": effectivePasskeyConfig.apiKey,
6045
6386
  "Content-Type": "application/json",
@@ -6601,7 +6942,7 @@ var PasskeymeErrorDisplay$1 = /*#__PURE__*/Object.freeze({
6601
6942
  });
6602
6943
 
6603
6944
  const PasskeymeProtectedRoute = React.memo(({ children, fallback = jsxRuntime.jsx("div", { children: "Please log in to access this content." }), redirectTo, requiredRoles = [], hasAccess }) => {
6604
- const { user, loading, isAuthenticated } = reactCore.usePasskeyme();
6945
+ const { user, loading, isAuthenticated } = usePasskeyme();
6605
6946
  const accessResult = React.useMemo(() => {
6606
6947
  // Show loading state
6607
6948
  if (loading) {
@@ -8154,7 +8495,7 @@ const MockPasskeymeProvider = ({ children, config = {}, initialUser = null, init
8154
8495
  redirectUri: "http://localhost:3000/callback",
8155
8496
  ...config,
8156
8497
  };
8157
- return jsxRuntime.jsx(reactCore.PasskeymeProvider, { config: mockConfig, children: children });
8498
+ return jsxRuntime.jsx(PasskeymeProvider$1, { config: mockConfig, children: children });
8158
8499
  };
8159
8500
  /**
8160
8501
  * Custom render function with PasskeyMe provider
@@ -8398,7 +8739,7 @@ const createMockConfig = (overrides = {}) => ({
8398
8739
  });
8399
8740
 
8400
8741
  const DevToolsDashboard = ({ show = process.env.NODE_ENV === "development", position = "bottom-right", collapsed: initialCollapsed = true, }) => {
8401
- const { user, isAuthenticated, config } = reactCore.usePasskeyme();
8742
+ const { user, isAuthenticated, config } = usePasskeyme();
8402
8743
  const [isCollapsed, setIsCollapsed] = React.useState(initialCollapsed);
8403
8744
  const [logs, setLogs] = React.useState([]);
8404
8745
  const [activeTab, setActiveTab] = React.useState("state");
@@ -8730,14 +9071,6 @@ const useVirtualScrollList = (initialItems = [], itemHeight = 50, containerHeigh
8730
9071
  };
8731
9072
  };
8732
9073
 
8733
- Object.defineProperty(exports, 'usePasskeyme', {
8734
- enumerable: true,
8735
- get: function () { return reactCore.usePasskeyme; }
8736
- });
8737
- Object.defineProperty(exports, 'usePasskeymeContext', {
8738
- enumerable: true,
8739
- get: function () { return reactCore.usePasskeymeContext; }
8740
- });
8741
9074
  Object.defineProperty(exports, 'ErrorHandler', {
8742
9075
  enumerable: true,
8743
9076
  get: function () { return auth.ErrorHandler; }
@@ -8811,6 +9144,8 @@ exports.useLoadingState = useLoadingState;
8811
9144
  exports.useMemoryMonitor = useMemoryMonitor;
8812
9145
  exports.useNotifications = useNotifications;
8813
9146
  exports.useOptimizedStorage = useOptimizedStorage;
9147
+ exports.usePasskeyme = usePasskeyme;
9148
+ exports.usePasskeymeContext = usePasskeymeContext;
8814
9149
  exports.usePerformanceMonitor = usePerformanceMonitor;
8815
9150
  exports.usePerformanceStats = usePerformanceStats;
8816
9151
  exports.useRequestCache = useRequestCache;