@cedros/login-react 0.0.36 → 0.0.38

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.
Files changed (102) hide show
  1. package/dist/{AuthenticationSettings-DIVk0OP8.js → AuthenticationSettings-BWfMzQ30.js} +24 -24
  2. package/dist/{AuthenticationSettings-DIVk0OP8.js.map → AuthenticationSettings-BWfMzQ30.js.map} +1 -1
  3. package/dist/{AuthenticationSettings-C-aYDXNH.cjs → AuthenticationSettings-CaNdnqL2.cjs} +1 -1
  4. package/dist/{AuthenticationSettings-C-aYDXNH.cjs.map → AuthenticationSettings-CaNdnqL2.cjs.map} +1 -1
  5. package/dist/{AuthenticationSettings-CoTic-d_.cjs → AuthenticationSettings-REAsemKP.cjs} +1 -1
  6. package/dist/{AuthenticationSettings-CoTic-d_.cjs.map → AuthenticationSettings-REAsemKP.cjs.map} +1 -1
  7. package/dist/{AuthenticationSettings-CsPbxwP7.js → AuthenticationSettings-pR8sUc8u.js} +1 -1
  8. package/dist/{AuthenticationSettings-CsPbxwP7.js.map → AuthenticationSettings-pR8sUc8u.js.map} +1 -1
  9. package/dist/{AutosaveStatus-f-jw25Ay.js → AutosaveStatus-Bgts5i6l.js} +83 -72
  10. package/dist/AutosaveStatus-Bgts5i6l.js.map +1 -0
  11. package/dist/{AutosaveStatus-DGNI4lXn.cjs → AutosaveStatus-DNK2vjyX.cjs} +1 -1
  12. package/dist/AutosaveStatus-DNK2vjyX.cjs.map +1 -0
  13. package/dist/{CreditSystemSettings-BNkvsgsk.cjs → CreditSystemSettings-BP-DMr-u.cjs} +1 -1
  14. package/dist/{CreditSystemSettings-BNkvsgsk.cjs.map → CreditSystemSettings-BP-DMr-u.cjs.map} +1 -1
  15. package/dist/{CreditSystemSettings-C6ed3yp7.js → CreditSystemSettings-CLKgkXHi.js} +1 -1
  16. package/dist/{CreditSystemSettings-C6ed3yp7.js.map → CreditSystemSettings-CLKgkXHi.js.map} +1 -1
  17. package/dist/{CreditSystemSettings-uinhzoha.js → CreditSystemSettings-DBo-z1ti.js} +1 -1
  18. package/dist/{CreditSystemSettings-uinhzoha.js.map → CreditSystemSettings-DBo-z1ti.js.map} +1 -1
  19. package/dist/{CreditSystemSettings-DM9ep1TF.cjs → CreditSystemSettings-OHes0bEe.cjs} +1 -1
  20. package/dist/{CreditSystemSettings-DM9ep1TF.cjs.map → CreditSystemSettings-OHes0bEe.cjs.map} +1 -1
  21. package/dist/{EmailRegisterForm-CCEuQA-w.js → EmailRegisterForm-BiisUR7P.js} +1 -1
  22. package/dist/{EmailRegisterForm-CCEuQA-w.js.map → EmailRegisterForm-BiisUR7P.js.map} +1 -1
  23. package/dist/{EmailRegisterForm-B_TiJkD6.cjs → EmailRegisterForm-CBzHZxvg.cjs} +1 -1
  24. package/dist/{EmailRegisterForm-B_TiJkD6.cjs.map → EmailRegisterForm-CBzHZxvg.cjs.map} +1 -1
  25. package/dist/{EmailSettings-BKuXy8sc.js → EmailSettings-JKyF5uqz.js} +1 -1
  26. package/dist/{EmailSettings-BKuXy8sc.js.map → EmailSettings-JKyF5uqz.js.map} +1 -1
  27. package/dist/{EmailSettings-BAuQtEfM.js → EmailSettings-j1TW9Nph.js} +1 -1
  28. package/dist/{EmailSettings-BAuQtEfM.js.map → EmailSettings-j1TW9Nph.js.map} +1 -1
  29. package/dist/{EmailSettings-BF5EiPl9.cjs → EmailSettings-kqzTquHb.cjs} +1 -1
  30. package/dist/{EmailSettings-BF5EiPl9.cjs.map → EmailSettings-kqzTquHb.cjs.map} +1 -1
  31. package/dist/{EmailSettings-BC0f1PCI.cjs → EmailSettings-lrl43m3p.cjs} +1 -1
  32. package/dist/{EmailSettings-BC0f1PCI.cjs.map → EmailSettings-lrl43m3p.cjs.map} +1 -1
  33. package/dist/{EmbeddedWalletSettings-BRjt2PAj.cjs → EmbeddedWalletSettings-1ERxP6--.cjs} +1 -1
  34. package/dist/{EmbeddedWalletSettings-BRjt2PAj.cjs.map → EmbeddedWalletSettings-1ERxP6--.cjs.map} +1 -1
  35. package/dist/{EmbeddedWalletSettings-Dmi-EQ7W.js → EmbeddedWalletSettings-Bln1PHH4.js} +1 -1
  36. package/dist/{EmbeddedWalletSettings-Dmi-EQ7W.js.map → EmbeddedWalletSettings-Bln1PHH4.js.map} +1 -1
  37. package/dist/{EmbeddedWalletSettings-C27X9He2.js → EmbeddedWalletSettings-_45K-0PV.js} +1 -1
  38. package/dist/{EmbeddedWalletSettings-C27X9He2.js.map → EmbeddedWalletSettings-_45K-0PV.js.map} +1 -1
  39. package/dist/{EmbeddedWalletSettings-CJY39UZN.cjs → EmbeddedWalletSettings-tg6BTW4F.cjs} +1 -1
  40. package/dist/{EmbeddedWalletSettings-CJY39UZN.cjs.map → EmbeddedWalletSettings-tg6BTW4F.cjs.map} +1 -1
  41. package/dist/{GoogleLoginButton-BDCbxoCB.cjs → GoogleLoginButton-JtRViYWS.cjs} +1 -1
  42. package/dist/GoogleLoginButton-JtRViYWS.cjs.map +1 -0
  43. package/dist/{GoogleLoginButton-6ip-vudk.js → GoogleLoginButton-qf4A_A3G.js} +6 -6
  44. package/dist/GoogleLoginButton-qf4A_A3G.js.map +1 -0
  45. package/dist/{ServerSettings-CZfBdMxG.js → ServerSettings-BGI3YP_z.js} +1 -1
  46. package/dist/{ServerSettings-CZfBdMxG.js.map → ServerSettings-BGI3YP_z.js.map} +1 -1
  47. package/dist/{ServerSettings-BT9weFPz.js → ServerSettings-D2sqqBMZ.js} +1 -1
  48. package/dist/{ServerSettings-BT9weFPz.js.map → ServerSettings-D2sqqBMZ.js.map} +1 -1
  49. package/dist/{ServerSettings-CKfiLfXi.cjs → ServerSettings-DBc7opXq.cjs} +1 -1
  50. package/dist/{ServerSettings-CKfiLfXi.cjs.map → ServerSettings-DBc7opXq.cjs.map} +1 -1
  51. package/dist/{ServerSettings-rHrVN8O8.cjs → ServerSettings-etuaUiXh.cjs} +1 -1
  52. package/dist/{ServerSettings-rHrVN8O8.cjs.map → ServerSettings-etuaUiXh.cjs.map} +1 -1
  53. package/dist/{WebhookSettings-ufiGTmbG.js → WebhookSettings-B0_D5YIz.js} +1 -1
  54. package/dist/{WebhookSettings-ufiGTmbG.js.map → WebhookSettings-B0_D5YIz.js.map} +1 -1
  55. package/dist/{WebhookSettings-DnLk97Mr.cjs → WebhookSettings-BHgVRCin.cjs} +1 -1
  56. package/dist/{WebhookSettings-DnLk97Mr.cjs.map → WebhookSettings-BHgVRCin.cjs.map} +1 -1
  57. package/dist/{WebhookSettings-DXjnq-c7.cjs → WebhookSettings-CD8gptC3.cjs} +1 -1
  58. package/dist/{WebhookSettings-DXjnq-c7.cjs.map → WebhookSettings-CD8gptC3.cjs.map} +1 -1
  59. package/dist/{WebhookSettings-Bgld6D_T.js → WebhookSettings-CHe_D4Bd.js} +1 -1
  60. package/dist/{WebhookSettings-Bgld6D_T.js.map → WebhookSettings-CHe_D4Bd.js.map} +1 -1
  61. package/dist/admin-only.cjs +1 -1
  62. package/dist/admin-only.js +1 -1
  63. package/dist/email-only.cjs +1 -1
  64. package/dist/email-only.d.ts +3 -2
  65. package/dist/email-only.js +2 -2
  66. package/dist/google-only.cjs +1 -1
  67. package/dist/google-only.d.ts +5 -4
  68. package/dist/google-only.js +2 -2
  69. package/dist/index.cjs +12 -12
  70. package/dist/index.cjs.map +1 -1
  71. package/dist/index.d.ts +70 -4
  72. package/dist/index.js +2427 -2231
  73. package/dist/index.js.map +1 -1
  74. package/dist/login-react.css +1 -1
  75. package/dist/{plugin-BbExid4E.js → plugin-DD07LDez.js} +1 -1
  76. package/dist/{plugin-BbExid4E.js.map → plugin-DD07LDez.js.map} +1 -1
  77. package/dist/{plugin-Xca67fp7.cjs → plugin-DTP_0JDr.cjs} +1 -1
  78. package/dist/{plugin-Xca67fp7.cjs.map → plugin-DTP_0JDr.cjs.map} +1 -1
  79. package/dist/{shamir-DBpHm7WN.cjs → shamir-BNuiZ2ry.cjs} +1 -1
  80. package/dist/{shamir-DBpHm7WN.cjs.map → shamir-BNuiZ2ry.cjs.map} +1 -1
  81. package/dist/{shamir-R8ddesFt.js → shamir-BbDh58Zo.js} +1 -1
  82. package/dist/{shamir-R8ddesFt.js.map → shamir-BbDh58Zo.js.map} +1 -1
  83. package/dist/{silentWalletEnroll-HPvsbd2J.cjs → silentWalletEnroll-B6a-fPgB.cjs} +1 -1
  84. package/dist/{silentWalletEnroll-HPvsbd2J.cjs.map → silentWalletEnroll-B6a-fPgB.cjs.map} +1 -1
  85. package/dist/{silentWalletEnroll-Dp1GTeNr.js → silentWalletEnroll-KHso2-HB.js} +2 -2
  86. package/dist/{silentWalletEnroll-Dp1GTeNr.js.map → silentWalletEnroll-KHso2-HB.js.map} +1 -1
  87. package/dist/solana-only.cjs +1 -1
  88. package/dist/solana-only.d.ts +3 -2
  89. package/dist/solana-only.js +1 -1
  90. package/dist/{useAuth-CVLv2oKA.js → useAuth-CWBApIWg.js} +83 -83
  91. package/dist/useAuth-CWBApIWg.js.map +1 -0
  92. package/dist/useAuth-GzGopI4v.cjs +1 -0
  93. package/dist/useAuth-GzGopI4v.cjs.map +1 -0
  94. package/package.json +1 -1
  95. package/dist/AutosaveStatus-DGNI4lXn.cjs.map +0 -1
  96. package/dist/AutosaveStatus-f-jw25Ay.js.map +0 -1
  97. package/dist/GoogleLoginButton-6ip-vudk.js.map +0 -1
  98. package/dist/GoogleLoginButton-BDCbxoCB.cjs.map +0 -1
  99. package/dist/mockServiceWorker.js +0 -349
  100. package/dist/useAuth-CVLv2oKA.js.map +0 -1
  101. package/dist/useAuth-XZaciuLg.cjs +0 -1
  102. package/dist/useAuth-XZaciuLg.cjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"GoogleLoginButton-6ip-vudk.js","sources":["../src/hooks/useGoogleAuth.ts","../src/components/google/GoogleLoginButton.tsx"],"sourcesContent":["import { useState, useCallback, useEffect, useRef, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport type { AuthResponse, AuthError } from '../types';\n\n/**\n * Module-level singleton for Google script loading (P-01)\n *\n * Prevents race conditions when multiple components mount simultaneously.\n * Uses a promise queue pattern to ensure the script is only loaded once.\n *\n * ## SSR Limitations (F-08)\n *\n * This singleton persists across the module lifecycle. In SSR environments:\n * - Module state persists between requests (potential cross-request leakage)\n * - Google Sign-In requires browser APIs (document, window)\n * - The hook guards against SSR with `googleClientId` checks\n *\n * For SSR frameworks (Next.js, Remix), ensure this hook is only used\n * in client-side components.\n *\n * ## Test Isolation\n *\n * For test isolation, call `scriptLoader._reset()` in test setup/teardown.\n *\n * @internal\n */\nconst scriptLoader = {\n loading: false,\n loaded: false,\n error: null as Error | null,\n callbacks: [] as Array<{ resolve: () => void; reject: (err: Error) => void }>,\n\n load(): Promise<void> {\n // SSR guard: avoid touching browser globals when running in Node/SSR.\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return Promise.reject(new Error('Google Sign-In script loader cannot run in SSR'));\n }\n\n // Already loaded\n if (this.loaded) {\n return Promise.resolve();\n }\n\n // Loading in progress - queue callback\n if (this.loading) {\n return new Promise((resolve, reject) => {\n this.callbacks.push({ resolve, reject });\n });\n }\n\n // Start loading\n this.loading = true;\n return new Promise((resolve, reject) => {\n this.callbacks.push({ resolve, reject });\n\n // Check if script already exists (from previous session or SSR)\n const existingScript = document.getElementById('google-gsi-script');\n if (existingScript) {\n if (window.google?.accounts?.id) {\n this.loaded = true;\n this.loading = false;\n this.callbacks.forEach((cb) => cb.resolve());\n this.callbacks = [];\n } else {\n existingScript.addEventListener('load', () => {\n this.loaded = true;\n this.loading = false;\n this.callbacks.forEach((cb) => cb.resolve());\n this.callbacks = [];\n });\n }\n return;\n }\n\n const script = document.createElement('script');\n script.src = 'https://accounts.google.com/gsi/client';\n script.async = true;\n script.defer = true;\n script.id = 'google-gsi-script';\n\n script.onload = () => {\n this.loaded = true;\n this.loading = false;\n this.callbacks.forEach((cb) => cb.resolve());\n this.callbacks = [];\n };\n\n script.onerror = () => {\n this.loading = false;\n // M-02: Remove failed script from DOM to allow retry on next load() call.\n // Without removal, retry finds existingScript and waits for a load event\n // that will never fire on an already-failed script.\n script.remove();\n const error = new Error('Failed to load Google Sign-In script');\n this.callbacks.forEach((cb) => cb.reject(error));\n this.callbacks = [];\n };\n\n document.head.appendChild(script);\n });\n },\n\n /**\n * Reset singleton state for test isolation (F-08)\n * @internal - Only use in test setup/teardown\n */\n _reset(): void {\n this.loading = false;\n this.loaded = false;\n this.error = null;\n this.callbacks = [];\n },\n};\n\n/** @internal */\nexport const _internalGoogleScriptLoader = scriptLoader;\n\nexport interface UseGoogleAuthReturn {\n signIn: () => Promise<AuthResponse>;\n isLoading: boolean;\n isInitialized: boolean;\n error: AuthError | null;\n clearError: () => void;\n /** ID token saved when ACCOUNT_LINK_REQUIRED is returned. Pass to POST /auth/link-oauth with the user's password. */\n pendingLinkIdToken: string | null;\n /** Clear the pending link state */\n clearPendingLink: () => void;\n}\n\ninterface PromiseCallbacks {\n resolve: (value: AuthResponse) => void;\n reject: (error: AuthError) => void;\n}\n\n/**\n * Hook for Google OAuth authentication.\n *\n * @example\n * ```tsx\n * function GoogleButton() {\n * const { signIn, isLoading, isInitialized, error } = useGoogleAuth();\n *\n * return (\n * <button onClick={signIn} disabled={!isInitialized || isLoading}>\n * {isLoading ? 'Signing in...' : 'Sign in with Google'}\n * </button>\n * );\n * }\n * ```\n */\nexport function useGoogleAuth(): UseGoogleAuthReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [isInitialized, setIsInitialized] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n const [pendingLinkIdToken, setPendingLinkIdToken] = useState<string | null>(null);\n\n const promiseCallbacksRef = useRef<PromiseCallbacks | null>(null);\n const configRef = useRef(config);\n const tokenClientRef = useRef<GoogleTokenClient | null>(null);\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n // Keep config ref in sync\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n // Handle access token from OAuth popup (initTokenClient flow)\n const handleTokenResponse = useCallback(\n async (response: GoogleTokenResponse) => {\n const callbacks = promiseCallbacksRef.current;\n if (!callbacks) return;\n\n if (response.error) {\n const err: AuthError = {\n code: 'SERVER_ERROR',\n message: response.error === 'access_denied'\n ? 'Google sign-in was cancelled.'\n : 'Unable to sign in with Google. Please try again.',\n };\n setError(err);\n setIsLoading(false);\n promiseCallbacksRef.current = null;\n callbacks.reject(err);\n return;\n }\n\n try {\n const data = await apiClient.post<AuthResponse>('/google', {\n accessToken: response.access_token,\n });\n configRef.current.callbacks?.onLoginSuccess?.(data.user, 'google');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n setIsLoading(false);\n callbacks.resolve(data);\n } catch (err) {\n const authError = handleApiError(err, 'Unable to sign in with Google. Please try again.');\n if (authError.code === 'ACCOUNT_LINK_REQUIRED') {\n setPendingLinkIdToken(response.access_token ?? null);\n }\n setError(authError);\n setIsLoading(false);\n callbacks.reject(authError);\n } finally {\n promiseCallbacksRef.current = null;\n }\n },\n [apiClient, _internal]\n );\n\n // Handle popup closed / blocked without completing auth.\n // Google calls error_callback (not callback) when the user closes the popup.\n const handleTokenError = useCallback(\n (err: GoogleTokenErrorResponse) => {\n const callbacks = promiseCallbacksRef.current;\n if (!callbacks) return;\n\n const authError: AuthError = {\n code: 'SERVER_ERROR',\n message: err.type === 'popup_failed_to_open'\n ? 'Google sign-in popup was blocked. Please allow popups for this site.'\n : 'Google sign-in was cancelled.',\n };\n setError(authError);\n setIsLoading(false);\n promiseCallbacksRef.current = null;\n callbacks.reject(authError);\n },\n []\n );\n\n // P-01: Initialize Google OAuth token client using singleton loader.\n // Uses initTokenClient (OAuth popup) instead of One Tap prompt() which\n // has exponential cooldown after dismissal and is blocked by Brave.\n useEffect(() => {\n if (!config.googleClientId) {\n return;\n }\n\n let isMounted = true;\n\n scriptLoader\n .load()\n .then(() => {\n if (!isMounted) return;\n\n const client = window.google?.accounts?.oauth2?.initTokenClient({\n client_id: config.googleClientId!,\n scope: 'openid email profile',\n callback: handleTokenResponse,\n error_callback: handleTokenError,\n });\n\n if (client) {\n tokenClientRef.current = client;\n setIsInitialized(true);\n }\n })\n .catch(() => {\n if (isMounted) {\n setError({\n code: 'SERVER_ERROR',\n message: 'Unable to load Google sign-in. Please refresh and try again.',\n });\n }\n });\n\n return () => {\n isMounted = false;\n tokenClientRef.current = null;\n };\n }, [config.googleClientId, handleTokenResponse, handleTokenError]);\n\n const signIn = useCallback(async (): Promise<AuthResponse> => {\n if (!config.googleClientId) {\n const err: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Google Client ID not configured',\n };\n setError(err);\n throw err;\n }\n\n if (!isInitialized) {\n const err: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Google sign-in is not ready yet. Please wait a moment and try again.',\n };\n setError(err);\n throw err;\n }\n\n if (promiseCallbacksRef.current) {\n const err: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Google sign-in is already in progress.',\n };\n setError(err);\n throw err;\n }\n\n setIsLoading(true);\n setError(null);\n\n return new Promise<AuthResponse>((resolve, reject) => {\n promiseCallbacksRef.current = { resolve, reject };\n\n // Open the Google OAuth popup via initTokenClient.\n // This avoids One Tap's exponential cooldown after dismissal and works\n // in browsers that block One Tap (e.g., Brave).\n tokenClientRef.current?.requestAccessToken();\n });\n }, [config.googleClientId, isInitialized]);\n\n const clearError = useCallback(() => setError(null), []);\n const clearPendingLink = useCallback(() => setPendingLinkIdToken(null), []);\n\n return {\n signIn,\n isLoading,\n isInitialized,\n error,\n clearError,\n pendingLinkIdToken,\n clearPendingLink,\n };\n}\n\n/** Response from Google's initTokenClient callback */\ninterface GoogleTokenResponse {\n access_token?: string;\n error?: string;\n error_description?: string;\n}\n\n/** Error from initTokenClient error_callback (popup closed/blocked) */\ninterface GoogleTokenErrorResponse {\n type: 'popup_failed_to_open' | 'popup_closed' | 'unknown';\n}\n\n/** Token client returned by google.accounts.oauth2.initTokenClient */\ninterface GoogleTokenClient {\n requestAccessToken: () => void;\n}\n\n// Type declaration for Google Identity Services\ndeclare global {\n interface Window {\n google?: {\n accounts?: {\n /** Used only to detect if the GIS script has loaded */\n id?: object;\n oauth2?: {\n initTokenClient: (config: {\n client_id: string;\n scope: string;\n callback: (response: GoogleTokenResponse) => void;\n error_callback?: (error: GoogleTokenErrorResponse) => void;\n }) => GoogleTokenClient;\n };\n };\n };\n }\n}\n","import { useGoogleAuth } from '../../hooks/useGoogleAuth';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\n\nexport interface GoogleLoginButtonProps {\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n className?: string;\n variant?: 'default' | 'outline';\n size?: 'sm' | 'md' | 'lg';\n disabled?: boolean;\n}\n\n/**\n * Google OAuth login button\n */\nexport function GoogleLoginButton({\n onSuccess,\n onError,\n className = '',\n variant = 'default',\n size = 'md',\n disabled = false,\n}: GoogleLoginButtonProps) {\n const { signIn, isLoading, isInitialized } = useGoogleAuth();\n\n const handleClick = async () => {\n try {\n await signIn();\n onSuccess?.();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error);\n }\n };\n\n const sizeClasses = {\n sm: 'cedros-button-sm',\n md: 'cedros-button-md',\n lg: 'cedros-button-lg',\n };\n\n const variantClasses = {\n default: 'cedros-button-social',\n outline: 'cedros-button-social-outline',\n };\n\n return (\n <button\n type=\"button\"\n className={`cedros-button ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}\n onClick={handleClick}\n disabled={disabled || !isInitialized || isLoading}\n aria-label=\"Sign in with Google\"\n >\n {isLoading ? (\n <LoadingSpinner size=\"sm\" />\n ) : (\n <svg\n className=\"cedros-button-icon\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 18 18\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.874 2.684-6.615z\"\n fill=\"#4285F4\"\n />\n <path\n d=\"M9.003 18c2.43 0 4.467-.806 5.956-2.18l-2.909-2.26c-.806.54-1.836.86-3.047.86-2.344 0-4.328-1.584-5.036-3.711H.96v2.332A8.997 8.997 0 0 0 9.003 18z\"\n fill=\"#34A853\"\n />\n <path\n d=\"M3.964 10.712A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.71V4.958H.96A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.96 4.042l3.004-2.33z\"\n fill=\"#FBBC05\"\n />\n <path\n d=\"M9.003 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.464.891 11.428 0 9.002 0A8.997 8.997 0 0 0 .96 4.958l3.005 2.332c.708-2.127 2.692-3.71 5.036-3.71z\"\n fill=\"#EA4335\"\n />\n </svg>\n )}\n <span>Continue with Google</span>\n </button>\n );\n}\n"],"names":["scriptLoader","resolve","reject","existingScript","cb","script","error","useGoogleAuth","config","_internal","useCedrosLogin","isLoading","setIsLoading","useState","isInitialized","setIsInitialized","setError","pendingLinkIdToken","setPendingLinkIdToken","promiseCallbacksRef","useRef","configRef","tokenClientRef","apiClient","useMemo","ApiClient","useEffect","handleTokenResponse","useCallback","response","callbacks","err","data","authError","handleApiError","handleTokenError","isMounted","client","signIn","clearError","clearPendingLink","GoogleLoginButton","onSuccess","onError","className","variant","size","disabled","handleClick","sizeClasses","jsxs","jsx","LoadingSpinner"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"GoogleLoginButton-BDCbxoCB.cjs","sources":["../src/hooks/useGoogleAuth.ts","../src/components/google/GoogleLoginButton.tsx"],"sourcesContent":["import { useState, useCallback, useEffect, useRef, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport type { AuthResponse, AuthError } from '../types';\n\n/**\n * Module-level singleton for Google script loading (P-01)\n *\n * Prevents race conditions when multiple components mount simultaneously.\n * Uses a promise queue pattern to ensure the script is only loaded once.\n *\n * ## SSR Limitations (F-08)\n *\n * This singleton persists across the module lifecycle. In SSR environments:\n * - Module state persists between requests (potential cross-request leakage)\n * - Google Sign-In requires browser APIs (document, window)\n * - The hook guards against SSR with `googleClientId` checks\n *\n * For SSR frameworks (Next.js, Remix), ensure this hook is only used\n * in client-side components.\n *\n * ## Test Isolation\n *\n * For test isolation, call `scriptLoader._reset()` in test setup/teardown.\n *\n * @internal\n */\nconst scriptLoader = {\n loading: false,\n loaded: false,\n error: null as Error | null,\n callbacks: [] as Array<{ resolve: () => void; reject: (err: Error) => void }>,\n\n load(): Promise<void> {\n // SSR guard: avoid touching browser globals when running in Node/SSR.\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return Promise.reject(new Error('Google Sign-In script loader cannot run in SSR'));\n }\n\n // Already loaded\n if (this.loaded) {\n return Promise.resolve();\n }\n\n // Loading in progress - queue callback\n if (this.loading) {\n return new Promise((resolve, reject) => {\n this.callbacks.push({ resolve, reject });\n });\n }\n\n // Start loading\n this.loading = true;\n return new Promise((resolve, reject) => {\n this.callbacks.push({ resolve, reject });\n\n // Check if script already exists (from previous session or SSR)\n const existingScript = document.getElementById('google-gsi-script');\n if (existingScript) {\n if (window.google?.accounts?.id) {\n this.loaded = true;\n this.loading = false;\n this.callbacks.forEach((cb) => cb.resolve());\n this.callbacks = [];\n } else {\n existingScript.addEventListener('load', () => {\n this.loaded = true;\n this.loading = false;\n this.callbacks.forEach((cb) => cb.resolve());\n this.callbacks = [];\n });\n }\n return;\n }\n\n const script = document.createElement('script');\n script.src = 'https://accounts.google.com/gsi/client';\n script.async = true;\n script.defer = true;\n script.id = 'google-gsi-script';\n\n script.onload = () => {\n this.loaded = true;\n this.loading = false;\n this.callbacks.forEach((cb) => cb.resolve());\n this.callbacks = [];\n };\n\n script.onerror = () => {\n this.loading = false;\n // M-02: Remove failed script from DOM to allow retry on next load() call.\n // Without removal, retry finds existingScript and waits for a load event\n // that will never fire on an already-failed script.\n script.remove();\n const error = new Error('Failed to load Google Sign-In script');\n this.callbacks.forEach((cb) => cb.reject(error));\n this.callbacks = [];\n };\n\n document.head.appendChild(script);\n });\n },\n\n /**\n * Reset singleton state for test isolation (F-08)\n * @internal - Only use in test setup/teardown\n */\n _reset(): void {\n this.loading = false;\n this.loaded = false;\n this.error = null;\n this.callbacks = [];\n },\n};\n\n/** @internal */\nexport const _internalGoogleScriptLoader = scriptLoader;\n\nexport interface UseGoogleAuthReturn {\n signIn: () => Promise<AuthResponse>;\n isLoading: boolean;\n isInitialized: boolean;\n error: AuthError | null;\n clearError: () => void;\n /** ID token saved when ACCOUNT_LINK_REQUIRED is returned. Pass to POST /auth/link-oauth with the user's password. */\n pendingLinkIdToken: string | null;\n /** Clear the pending link state */\n clearPendingLink: () => void;\n}\n\ninterface PromiseCallbacks {\n resolve: (value: AuthResponse) => void;\n reject: (error: AuthError) => void;\n}\n\n/**\n * Hook for Google OAuth authentication.\n *\n * @example\n * ```tsx\n * function GoogleButton() {\n * const { signIn, isLoading, isInitialized, error } = useGoogleAuth();\n *\n * return (\n * <button onClick={signIn} disabled={!isInitialized || isLoading}>\n * {isLoading ? 'Signing in...' : 'Sign in with Google'}\n * </button>\n * );\n * }\n * ```\n */\nexport function useGoogleAuth(): UseGoogleAuthReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [isInitialized, setIsInitialized] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n const [pendingLinkIdToken, setPendingLinkIdToken] = useState<string | null>(null);\n\n const promiseCallbacksRef = useRef<PromiseCallbacks | null>(null);\n const configRef = useRef(config);\n const tokenClientRef = useRef<GoogleTokenClient | null>(null);\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n // Keep config ref in sync\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n // Handle access token from OAuth popup (initTokenClient flow)\n const handleTokenResponse = useCallback(\n async (response: GoogleTokenResponse) => {\n const callbacks = promiseCallbacksRef.current;\n if (!callbacks) return;\n\n if (response.error) {\n const err: AuthError = {\n code: 'SERVER_ERROR',\n message: response.error === 'access_denied'\n ? 'Google sign-in was cancelled.'\n : 'Unable to sign in with Google. Please try again.',\n };\n setError(err);\n setIsLoading(false);\n promiseCallbacksRef.current = null;\n callbacks.reject(err);\n return;\n }\n\n try {\n const data = await apiClient.post<AuthResponse>('/google', {\n accessToken: response.access_token,\n });\n configRef.current.callbacks?.onLoginSuccess?.(data.user, 'google');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n setIsLoading(false);\n callbacks.resolve(data);\n } catch (err) {\n const authError = handleApiError(err, 'Unable to sign in with Google. Please try again.');\n if (authError.code === 'ACCOUNT_LINK_REQUIRED') {\n setPendingLinkIdToken(response.access_token ?? null);\n }\n setError(authError);\n setIsLoading(false);\n callbacks.reject(authError);\n } finally {\n promiseCallbacksRef.current = null;\n }\n },\n [apiClient, _internal]\n );\n\n // Handle popup closed / blocked without completing auth.\n // Google calls error_callback (not callback) when the user closes the popup.\n const handleTokenError = useCallback(\n (err: GoogleTokenErrorResponse) => {\n const callbacks = promiseCallbacksRef.current;\n if (!callbacks) return;\n\n const authError: AuthError = {\n code: 'SERVER_ERROR',\n message: err.type === 'popup_failed_to_open'\n ? 'Google sign-in popup was blocked. Please allow popups for this site.'\n : 'Google sign-in was cancelled.',\n };\n setError(authError);\n setIsLoading(false);\n promiseCallbacksRef.current = null;\n callbacks.reject(authError);\n },\n []\n );\n\n // P-01: Initialize Google OAuth token client using singleton loader.\n // Uses initTokenClient (OAuth popup) instead of One Tap prompt() which\n // has exponential cooldown after dismissal and is blocked by Brave.\n useEffect(() => {\n if (!config.googleClientId) {\n return;\n }\n\n let isMounted = true;\n\n scriptLoader\n .load()\n .then(() => {\n if (!isMounted) return;\n\n const client = window.google?.accounts?.oauth2?.initTokenClient({\n client_id: config.googleClientId!,\n scope: 'openid email profile',\n callback: handleTokenResponse,\n error_callback: handleTokenError,\n });\n\n if (client) {\n tokenClientRef.current = client;\n setIsInitialized(true);\n }\n })\n .catch(() => {\n if (isMounted) {\n setError({\n code: 'SERVER_ERROR',\n message: 'Unable to load Google sign-in. Please refresh and try again.',\n });\n }\n });\n\n return () => {\n isMounted = false;\n tokenClientRef.current = null;\n };\n }, [config.googleClientId, handleTokenResponse, handleTokenError]);\n\n const signIn = useCallback(async (): Promise<AuthResponse> => {\n if (!config.googleClientId) {\n const err: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Google Client ID not configured',\n };\n setError(err);\n throw err;\n }\n\n if (!isInitialized) {\n const err: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Google sign-in is not ready yet. Please wait a moment and try again.',\n };\n setError(err);\n throw err;\n }\n\n if (promiseCallbacksRef.current) {\n const err: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Google sign-in is already in progress.',\n };\n setError(err);\n throw err;\n }\n\n setIsLoading(true);\n setError(null);\n\n return new Promise<AuthResponse>((resolve, reject) => {\n promiseCallbacksRef.current = { resolve, reject };\n\n // Open the Google OAuth popup via initTokenClient.\n // This avoids One Tap's exponential cooldown after dismissal and works\n // in browsers that block One Tap (e.g., Brave).\n tokenClientRef.current?.requestAccessToken();\n });\n }, [config.googleClientId, isInitialized]);\n\n const clearError = useCallback(() => setError(null), []);\n const clearPendingLink = useCallback(() => setPendingLinkIdToken(null), []);\n\n return {\n signIn,\n isLoading,\n isInitialized,\n error,\n clearError,\n pendingLinkIdToken,\n clearPendingLink,\n };\n}\n\n/** Response from Google's initTokenClient callback */\ninterface GoogleTokenResponse {\n access_token?: string;\n error?: string;\n error_description?: string;\n}\n\n/** Error from initTokenClient error_callback (popup closed/blocked) */\ninterface GoogleTokenErrorResponse {\n type: 'popup_failed_to_open' | 'popup_closed' | 'unknown';\n}\n\n/** Token client returned by google.accounts.oauth2.initTokenClient */\ninterface GoogleTokenClient {\n requestAccessToken: () => void;\n}\n\n// Type declaration for Google Identity Services\ndeclare global {\n interface Window {\n google?: {\n accounts?: {\n /** Used only to detect if the GIS script has loaded */\n id?: object;\n oauth2?: {\n initTokenClient: (config: {\n client_id: string;\n scope: string;\n callback: (response: GoogleTokenResponse) => void;\n error_callback?: (error: GoogleTokenErrorResponse) => void;\n }) => GoogleTokenClient;\n };\n };\n };\n }\n}\n","import { useGoogleAuth } from '../../hooks/useGoogleAuth';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\n\nexport interface GoogleLoginButtonProps {\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n className?: string;\n variant?: 'default' | 'outline';\n size?: 'sm' | 'md' | 'lg';\n disabled?: boolean;\n}\n\n/**\n * Google OAuth login button\n */\nexport function GoogleLoginButton({\n onSuccess,\n onError,\n className = '',\n variant = 'default',\n size = 'md',\n disabled = false,\n}: GoogleLoginButtonProps) {\n const { signIn, isLoading, isInitialized } = useGoogleAuth();\n\n const handleClick = async () => {\n try {\n await signIn();\n onSuccess?.();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error);\n }\n };\n\n const sizeClasses = {\n sm: 'cedros-button-sm',\n md: 'cedros-button-md',\n lg: 'cedros-button-lg',\n };\n\n const variantClasses = {\n default: 'cedros-button-social',\n outline: 'cedros-button-social-outline',\n };\n\n return (\n <button\n type=\"button\"\n className={`cedros-button ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}\n onClick={handleClick}\n disabled={disabled || !isInitialized || isLoading}\n aria-label=\"Sign in with Google\"\n >\n {isLoading ? (\n <LoadingSpinner size=\"sm\" />\n ) : (\n <svg\n className=\"cedros-button-icon\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 18 18\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.874 2.684-6.615z\"\n fill=\"#4285F4\"\n />\n <path\n d=\"M9.003 18c2.43 0 4.467-.806 5.956-2.18l-2.909-2.26c-.806.54-1.836.86-3.047.86-2.344 0-4.328-1.584-5.036-3.711H.96v2.332A8.997 8.997 0 0 0 9.003 18z\"\n fill=\"#34A853\"\n />\n <path\n d=\"M3.964 10.712A5.41 5.41 0 0 1 3.682 9c0-.593.102-1.17.282-1.71V4.958H.96A8.996 8.996 0 0 0 0 9c0 1.452.348 2.827.96 4.042l3.004-2.33z\"\n fill=\"#FBBC05\"\n />\n <path\n d=\"M9.003 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.464.891 11.428 0 9.002 0A8.997 8.997 0 0 0 .96 4.958l3.005 2.332c.708-2.127 2.692-3.71 5.036-3.71z\"\n fill=\"#EA4335\"\n />\n </svg>\n )}\n <span>Continue with Google</span>\n </button>\n );\n}\n"],"names":["scriptLoader","resolve","reject","existingScript","cb","script","error","useGoogleAuth","config","_internal","useCedrosLogin","isLoading","setIsLoading","useState","isInitialized","setIsInitialized","setError","pendingLinkIdToken","setPendingLinkIdToken","promiseCallbacksRef","useRef","configRef","tokenClientRef","apiClient","useMemo","ApiClient","useEffect","handleTokenResponse","useCallback","response","callbacks","err","data","authError","handleApiError","handleTokenError","isMounted","client","signIn","clearError","clearPendingLink","GoogleLoginButton","onSuccess","onError","className","variant","size","disabled","handleClick","sizeClasses","variantClasses","jsxs","jsx","LoadingSpinner"],"mappings":"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"}
@@ -1,349 +0,0 @@
1
- /* eslint-disable */
2
- /* tslint:disable */
3
-
4
- /**
5
- * Mock Service Worker.
6
- * @see https://github.com/mswjs/msw
7
- * - Please do NOT modify this file.
8
- */
9
-
10
- const PACKAGE_VERSION = '2.12.4'
11
- const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
12
- const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
13
- const activeClientIds = new Set()
14
-
15
- addEventListener('install', function () {
16
- self.skipWaiting()
17
- })
18
-
19
- addEventListener('activate', function (event) {
20
- event.waitUntil(self.clients.claim())
21
- })
22
-
23
- addEventListener('message', async function (event) {
24
- const clientId = Reflect.get(event.source || {}, 'id')
25
-
26
- if (!clientId || !self.clients) {
27
- return
28
- }
29
-
30
- const client = await self.clients.get(clientId)
31
-
32
- if (!client) {
33
- return
34
- }
35
-
36
- const allClients = await self.clients.matchAll({
37
- type: 'window',
38
- })
39
-
40
- switch (event.data) {
41
- case 'KEEPALIVE_REQUEST': {
42
- sendToClient(client, {
43
- type: 'KEEPALIVE_RESPONSE',
44
- })
45
- break
46
- }
47
-
48
- case 'INTEGRITY_CHECK_REQUEST': {
49
- sendToClient(client, {
50
- type: 'INTEGRITY_CHECK_RESPONSE',
51
- payload: {
52
- packageVersion: PACKAGE_VERSION,
53
- checksum: INTEGRITY_CHECKSUM,
54
- },
55
- })
56
- break
57
- }
58
-
59
- case 'MOCK_ACTIVATE': {
60
- activeClientIds.add(clientId)
61
-
62
- sendToClient(client, {
63
- type: 'MOCKING_ENABLED',
64
- payload: {
65
- client: {
66
- id: client.id,
67
- frameType: client.frameType,
68
- },
69
- },
70
- })
71
- break
72
- }
73
-
74
- case 'CLIENT_CLOSED': {
75
- activeClientIds.delete(clientId)
76
-
77
- const remainingClients = allClients.filter((client) => {
78
- return client.id !== clientId
79
- })
80
-
81
- // Unregister itself when there are no more clients
82
- if (remainingClients.length === 0) {
83
- self.registration.unregister()
84
- }
85
-
86
- break
87
- }
88
- }
89
- })
90
-
91
- addEventListener('fetch', function (event) {
92
- const requestInterceptedAt = Date.now()
93
-
94
- // Bypass navigation requests.
95
- if (event.request.mode === 'navigate') {
96
- return
97
- }
98
-
99
- // Opening the DevTools triggers the "only-if-cached" request
100
- // that cannot be handled by the worker. Bypass such requests.
101
- if (
102
- event.request.cache === 'only-if-cached' &&
103
- event.request.mode !== 'same-origin'
104
- ) {
105
- return
106
- }
107
-
108
- // Bypass all requests when there are no active clients.
109
- // Prevents the self-unregistered worked from handling requests
110
- // after it's been terminated (still remains active until the next reload).
111
- if (activeClientIds.size === 0) {
112
- return
113
- }
114
-
115
- const requestId = crypto.randomUUID()
116
- event.respondWith(handleRequest(event, requestId, requestInterceptedAt))
117
- })
118
-
119
- /**
120
- * @param {FetchEvent} event
121
- * @param {string} requestId
122
- * @param {number} requestInterceptedAt
123
- */
124
- async function handleRequest(event, requestId, requestInterceptedAt) {
125
- const client = await resolveMainClient(event)
126
- const requestCloneForEvents = event.request.clone()
127
- const response = await getResponse(
128
- event,
129
- client,
130
- requestId,
131
- requestInterceptedAt,
132
- )
133
-
134
- // Send back the response clone for the "response:*" life-cycle events.
135
- // Ensure MSW is active and ready to handle the message, otherwise
136
- // this message will pend indefinitely.
137
- if (client && activeClientIds.has(client.id)) {
138
- const serializedRequest = await serializeRequest(requestCloneForEvents)
139
-
140
- // Clone the response so both the client and the library could consume it.
141
- const responseClone = response.clone()
142
-
143
- sendToClient(
144
- client,
145
- {
146
- type: 'RESPONSE',
147
- payload: {
148
- isMockedResponse: IS_MOCKED_RESPONSE in response,
149
- request: {
150
- id: requestId,
151
- ...serializedRequest,
152
- },
153
- response: {
154
- type: responseClone.type,
155
- status: responseClone.status,
156
- statusText: responseClone.statusText,
157
- headers: Object.fromEntries(responseClone.headers.entries()),
158
- body: responseClone.body,
159
- },
160
- },
161
- },
162
- responseClone.body ? [serializedRequest.body, responseClone.body] : [],
163
- )
164
- }
165
-
166
- return response
167
- }
168
-
169
- /**
170
- * Resolve the main client for the given event.
171
- * Client that issues a request doesn't necessarily equal the client
172
- * that registered the worker. It's with the latter the worker should
173
- * communicate with during the response resolving phase.
174
- * @param {FetchEvent} event
175
- * @returns {Promise<Client | undefined>}
176
- */
177
- async function resolveMainClient(event) {
178
- const client = await self.clients.get(event.clientId)
179
-
180
- if (activeClientIds.has(event.clientId)) {
181
- return client
182
- }
183
-
184
- if (client?.frameType === 'top-level') {
185
- return client
186
- }
187
-
188
- const allClients = await self.clients.matchAll({
189
- type: 'window',
190
- })
191
-
192
- return allClients
193
- .filter((client) => {
194
- // Get only those clients that are currently visible.
195
- return client.visibilityState === 'visible'
196
- })
197
- .find((client) => {
198
- // Find the client ID that's recorded in the
199
- // set of clients that have registered the worker.
200
- return activeClientIds.has(client.id)
201
- })
202
- }
203
-
204
- /**
205
- * @param {FetchEvent} event
206
- * @param {Client | undefined} client
207
- * @param {string} requestId
208
- * @param {number} requestInterceptedAt
209
- * @returns {Promise<Response>}
210
- */
211
- async function getResponse(event, client, requestId, requestInterceptedAt) {
212
- // Clone the request because it might've been already used
213
- // (i.e. its body has been read and sent to the client).
214
- const requestClone = event.request.clone()
215
-
216
- function passthrough() {
217
- // Cast the request headers to a new Headers instance
218
- // so the headers can be manipulated with.
219
- const headers = new Headers(requestClone.headers)
220
-
221
- // Remove the "accept" header value that marked this request as passthrough.
222
- // This prevents request alteration and also keeps it compliant with the
223
- // user-defined CORS policies.
224
- const acceptHeader = headers.get('accept')
225
- if (acceptHeader) {
226
- const values = acceptHeader.split(',').map((value) => value.trim())
227
- const filteredValues = values.filter(
228
- (value) => value !== 'msw/passthrough',
229
- )
230
-
231
- if (filteredValues.length > 0) {
232
- headers.set('accept', filteredValues.join(', '))
233
- } else {
234
- headers.delete('accept')
235
- }
236
- }
237
-
238
- return fetch(requestClone, { headers })
239
- }
240
-
241
- // Bypass mocking when the client is not active.
242
- if (!client) {
243
- return passthrough()
244
- }
245
-
246
- // Bypass initial page load requests (i.e. static assets).
247
- // The absence of the immediate/parent client in the map of the active clients
248
- // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
249
- // and is not ready to handle requests.
250
- if (!activeClientIds.has(client.id)) {
251
- return passthrough()
252
- }
253
-
254
- // Notify the client that a request has been intercepted.
255
- const serializedRequest = await serializeRequest(event.request)
256
- const clientMessage = await sendToClient(
257
- client,
258
- {
259
- type: 'REQUEST',
260
- payload: {
261
- id: requestId,
262
- interceptedAt: requestInterceptedAt,
263
- ...serializedRequest,
264
- },
265
- },
266
- [serializedRequest.body],
267
- )
268
-
269
- switch (clientMessage.type) {
270
- case 'MOCK_RESPONSE': {
271
- return respondWithMock(clientMessage.data)
272
- }
273
-
274
- case 'PASSTHROUGH': {
275
- return passthrough()
276
- }
277
- }
278
-
279
- return passthrough()
280
- }
281
-
282
- /**
283
- * @param {Client} client
284
- * @param {any} message
285
- * @param {Array<Transferable>} transferrables
286
- * @returns {Promise<any>}
287
- */
288
- function sendToClient(client, message, transferrables = []) {
289
- return new Promise((resolve, reject) => {
290
- const channel = new MessageChannel()
291
-
292
- channel.port1.onmessage = (event) => {
293
- if (event.data && event.data.error) {
294
- return reject(event.data.error)
295
- }
296
-
297
- resolve(event.data)
298
- }
299
-
300
- client.postMessage(message, [
301
- channel.port2,
302
- ...transferrables.filter(Boolean),
303
- ])
304
- })
305
- }
306
-
307
- /**
308
- * @param {Response} response
309
- * @returns {Response}
310
- */
311
- function respondWithMock(response) {
312
- // Setting response status code to 0 is a no-op.
313
- // However, when responding with a "Response.error()", the produced Response
314
- // instance will have status code set to 0. Since it's not possible to create
315
- // a Response instance with status code 0, handle that use-case separately.
316
- if (response.status === 0) {
317
- return Response.error()
318
- }
319
-
320
- const mockedResponse = new Response(response.body, response)
321
-
322
- Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
323
- value: true,
324
- enumerable: true,
325
- })
326
-
327
- return mockedResponse
328
- }
329
-
330
- /**
331
- * @param {Request} request
332
- */
333
- async function serializeRequest(request) {
334
- return {
335
- url: request.url,
336
- mode: request.mode,
337
- method: request.method,
338
- headers: Object.fromEntries(request.headers.entries()),
339
- cache: request.cache,
340
- credentials: request.credentials,
341
- destination: request.destination,
342
- integrity: request.integrity,
343
- redirect: request.redirect,
344
- referrer: request.referrer,
345
- referrerPolicy: request.referrerPolicy,
346
- body: await request.arrayBuffer(),
347
- keepalive: request.keepalive,
348
- }
349
- }