@cedros/login-react 0.0.12 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/dist/AdminDepositList-CyT4VBH8.js +311 -0
- package/dist/AdminDepositList-CyT4VBH8.js.map +1 -0
- package/dist/AdminDepositList-b2AXtLg0.cjs +1 -0
- package/dist/AdminDepositList-b2AXtLg0.cjs.map +1 -0
- package/dist/AdminWithdrawalHistory-Cud-yuWy.js +916 -0
- package/dist/AdminWithdrawalHistory-Cud-yuWy.js.map +1 -0
- package/dist/AdminWithdrawalHistory-DL9zbu2b.cjs +1 -0
- package/dist/AdminWithdrawalHistory-DL9zbu2b.cjs.map +1 -0
- package/dist/AuthenticationSettings-D739nNul.cjs +1 -0
- package/dist/AuthenticationSettings-D739nNul.cjs.map +1 -0
- package/dist/AuthenticationSettings-DtLoxQ2z.cjs +1 -0
- package/dist/AuthenticationSettings-DtLoxQ2z.cjs.map +1 -0
- package/dist/AuthenticationSettings-Dx3JCI3m.js +9 -0
- package/dist/AuthenticationSettings-Dx3JCI3m.js.map +1 -0
- package/dist/AuthenticationSettings-vowmQPXz.js +525 -0
- package/dist/AuthenticationSettings-vowmQPXz.js.map +1 -0
- package/dist/AutosaveStatus-CCrZszKA.cjs +1 -0
- package/dist/AutosaveStatus-CCrZszKA.cjs.map +1 -0
- package/dist/AutosaveStatus-Ciyt350A.js +1497 -0
- package/dist/AutosaveStatus-Ciyt350A.js.map +1 -0
- package/dist/CreditSystemSettings-BQ3h4CyM.js +210 -0
- package/dist/CreditSystemSettings-BQ3h4CyM.js.map +1 -0
- package/dist/CreditSystemSettings-C6dUsu72.cjs +1 -0
- package/dist/CreditSystemSettings-C6dUsu72.cjs.map +1 -0
- package/dist/CreditSystemSettings-CBot8EPW.cjs +1 -0
- package/dist/CreditSystemSettings-CBot8EPW.cjs.map +1 -0
- package/dist/CreditSystemSettings-bVuNLsqp.js +9 -0
- package/dist/CreditSystemSettings-bVuNLsqp.js.map +1 -0
- package/dist/DepositsSection-BkKUS4vk.cjs +1 -0
- package/dist/DepositsSection-BkKUS4vk.cjs.map +1 -0
- package/dist/{DepositsSection-0Hqr0yDm.js → DepositsSection-DD9MKUFt.js} +20 -14
- package/dist/DepositsSection-DD9MKUFt.js.map +1 -0
- package/dist/EmailRegisterForm-Pvm3I8GP.cjs +1 -0
- package/dist/EmailRegisterForm-Pvm3I8GP.cjs.map +1 -0
- package/dist/EmailRegisterForm-nI0BOIxR.js +927 -0
- package/dist/EmailRegisterForm-nI0BOIxR.js.map +1 -0
- package/dist/EmailSettings-B9qwPSiM.cjs +1 -0
- package/dist/EmailSettings-B9qwPSiM.cjs.map +1 -0
- package/dist/EmailSettings-CCA8dNCi.js +17 -0
- package/dist/EmailSettings-CCA8dNCi.js.map +1 -0
- package/dist/EmailSettings-RbgAq9FB.cjs +1 -0
- package/dist/EmailSettings-RbgAq9FB.cjs.map +1 -0
- package/dist/{AuthenticationSettings-BSoIQ58T.js → EmailSettings-mxlKNcPl.js} +3 -3
- package/dist/EmailSettings-mxlKNcPl.js.map +1 -0
- package/dist/EmbeddedWalletSettings-B4h-8rxB.cjs +1 -0
- package/dist/EmbeddedWalletSettings-B4h-8rxB.cjs.map +1 -0
- package/dist/EmbeddedWalletSettings-BInZvFZf.js +79 -0
- package/dist/EmbeddedWalletSettings-BInZvFZf.js.map +1 -0
- package/dist/{EmbeddedWalletSettings-iVf_3pn3.js → EmbeddedWalletSettings-CXlZFFDw.js} +3 -3
- package/dist/EmbeddedWalletSettings-CXlZFFDw.js.map +1 -0
- package/dist/EmbeddedWalletSettings-D5JXae1L.cjs +1 -0
- package/dist/EmbeddedWalletSettings-D5JXae1L.cjs.map +1 -0
- package/dist/ErrorMessage-CHbYbVi2.cjs +1 -0
- package/dist/ErrorMessage-CHbYbVi2.cjs.map +1 -0
- package/dist/ErrorMessage-CcEK0pYO.js +64 -0
- package/dist/ErrorMessage-CcEK0pYO.js.map +1 -0
- package/dist/{GoogleLoginButton-BzHr545L.js → GoogleLoginButton-CXwp4LsQ.js} +4 -3
- package/dist/{GoogleLoginButton-BzHr545L.js.map → GoogleLoginButton-CXwp4LsQ.js.map} +1 -1
- package/dist/GoogleLoginButton-zS_69-KV.cjs +1 -0
- package/dist/{GoogleLoginButton-DoAdOqb3.cjs.map → GoogleLoginButton-zS_69-KV.cjs.map} +1 -1
- package/dist/LoadingSpinner-6vml-zwr.js +79 -0
- package/dist/LoadingSpinner-6vml-zwr.js.map +1 -0
- package/dist/LoadingSpinner-d6sSxgQN.cjs +1 -0
- package/dist/LoadingSpinner-d6sSxgQN.cjs.map +1 -0
- package/dist/PermissionsSection-BPbE-hNx.cjs +1 -0
- package/dist/PermissionsSection-BPbE-hNx.cjs.map +1 -0
- package/dist/PermissionsSection-CighC1p6.js +1026 -0
- package/dist/PermissionsSection-CighC1p6.js.map +1 -0
- package/dist/ServerSettings-BE8fsE5k.js +84 -0
- package/dist/ServerSettings-BE8fsE5k.js.map +1 -0
- package/dist/ServerSettings-CYTlQ2xy.cjs +1 -0
- package/dist/ServerSettings-CYTlQ2xy.cjs.map +1 -0
- package/dist/ServerSettings-DFs9jQ3f.cjs +1 -0
- package/dist/ServerSettings-DFs9jQ3f.cjs.map +1 -0
- package/dist/{ServerSettings-C_osdbiX.js → ServerSettings-pSmWDC1d.js} +3 -3
- package/dist/ServerSettings-pSmWDC1d.js.map +1 -0
- package/dist/SettingsPageLayout--GZ_iHLc.js +50 -0
- package/dist/SettingsPageLayout--GZ_iHLc.js.map +1 -0
- package/dist/SettingsPageLayout-j5lMjEID.cjs +1 -0
- package/dist/SettingsPageLayout-j5lMjEID.cjs.map +1 -0
- package/dist/SolanaLoginButton-BjOxpE1C.cjs +1 -0
- package/dist/{SolanaLoginButton-CPzms0kf.cjs.map → SolanaLoginButton-BjOxpE1C.cjs.map} +1 -1
- package/dist/{SolanaLoginButton-C-CfZxAZ.js → SolanaLoginButton-P22QjBaO.js} +5 -4
- package/dist/{SolanaLoginButton-C-CfZxAZ.js.map → SolanaLoginButton-P22QjBaO.js.map} +1 -1
- package/dist/StatsBar-BX-hHtTq.js +66 -0
- package/dist/StatsBar-BX-hHtTq.js.map +1 -0
- package/dist/StatsBar-DTUZCwDD.cjs +1 -0
- package/dist/StatsBar-DTUZCwDD.cjs.map +1 -0
- package/dist/{TeamSection-Bjt72upx.js → TeamSection-BIECkp7g.js} +27 -26
- package/dist/TeamSection-BIECkp7g.js.map +1 -0
- package/dist/TeamSection-BOH9pv_E.cjs +1 -0
- package/dist/TeamSection-BOH9pv_E.cjs.map +1 -0
- package/dist/UsersSection-e6q7FHzx.cjs +1 -0
- package/dist/UsersSection-e6q7FHzx.cjs.map +1 -0
- package/dist/UsersSection-t-zm0jZW.js +33 -0
- package/dist/UsersSection-t-zm0jZW.js.map +1 -0
- package/dist/{WebhookSettings-IyhyTFes.js → WebhookSettings-Bb70MbFj.js} +4 -4
- package/dist/WebhookSettings-Bb70MbFj.js.map +1 -0
- package/dist/WebhookSettings-C-gvNAW1.cjs +1 -0
- package/dist/WebhookSettings-C-gvNAW1.cjs.map +1 -0
- package/dist/WebhookSettings-CGyDKjrm.cjs +1 -0
- package/dist/WebhookSettings-CGyDKjrm.cjs.map +1 -0
- package/dist/WebhookSettings-kIstSjZi.js +17 -0
- package/dist/WebhookSettings-kIstSjZi.js.map +1 -0
- package/dist/WithdrawalsSection-sljIyeaz.cjs +1 -0
- package/dist/WithdrawalsSection-sljIyeaz.cjs.map +1 -0
- package/dist/WithdrawalsSection-yRDTVFsb.js +27 -0
- package/dist/WithdrawalsSection-yRDTVFsb.js.map +1 -0
- package/dist/admin-only.cjs +1 -0
- package/dist/admin-only.cjs.map +1 -0
- package/dist/{admin/types.d.ts → admin-only.d.ts} +228 -167
- package/dist/admin-only.js +8 -0
- package/dist/admin-only.js.map +1 -0
- package/dist/assets/argon2Worker-Bi5TuQvD.js.map +1 -1
- package/dist/email-only.cjs +1 -1
- package/dist/email-only.d.ts +645 -16
- package/dist/email-only.js +16 -13
- package/dist/email-only.js.map +1 -1
- package/dist/google-only.cjs +1 -1
- package/dist/google-only.d.ts +554 -13
- package/dist/google-only.js +11 -8
- package/dist/google-only.js.map +1 -1
- package/dist/index.cjs +2061 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +5030 -144
- package/dist/index.js +13482 -117
- package/dist/index.js.map +1 -1
- package/dist/login-react.css +1 -1
- package/dist/plugin-8_dpq7tC.cjs +1 -0
- package/dist/plugin-8_dpq7tC.cjs.map +1 -0
- package/dist/plugin-D1NdppqC.js +596 -0
- package/dist/plugin-D1NdppqC.js.map +1 -0
- package/dist/sanitization-Bo_tn-L2.cjs +1 -0
- package/dist/sanitization-Bo_tn-L2.cjs.map +1 -0
- package/dist/sanitization-CQ-H1MSg.js +39 -0
- package/dist/sanitization-CQ-H1MSg.js.map +1 -0
- package/dist/shamir-4DyQMJCk.cjs +1 -0
- package/dist/shamir-4DyQMJCk.cjs.map +1 -0
- package/dist/{solanaKeypair-BlXol3nh.js → shamir-L-s-Tp1Z.js} +765 -757
- package/dist/shamir-L-s-Tp1Z.js.map +1 -0
- package/dist/silentWalletEnroll-BgTb4H5I.cjs +1 -0
- package/dist/silentWalletEnroll-BgTb4H5I.cjs.map +1 -0
- package/dist/silentWalletEnroll-DWt6Pr3B.js +41 -0
- package/dist/silentWalletEnroll-DWt6Pr3B.js.map +1 -0
- package/dist/solana-only.cjs +1 -1
- package/dist/solana-only.d.ts +605 -13
- package/dist/solana-only.js +12 -9
- package/dist/solana-only.js.map +1 -1
- package/dist/style.d.ts +1 -0
- package/dist/useAdminDeposits-BTSyeAfg.js +151 -0
- package/dist/useAdminDeposits-BTSyeAfg.js.map +1 -0
- package/dist/useAdminDeposits-BkkCwHWp.cjs +1 -0
- package/dist/useAdminDeposits-BkkCwHWp.cjs.map +1 -0
- package/dist/{ErrorMessage-DU2VBb85.js → useAuth-C3dpk0po.js} +1005 -1241
- package/dist/useAuth-C3dpk0po.js.map +1 -0
- package/dist/useAuth-D3Pk_H3z.cjs +1 -0
- package/dist/useAuth-D3Pk_H3z.cjs.map +1 -0
- package/dist/useCedrosLogin-C9MrcZvh.cjs +1 -0
- package/dist/useCedrosLogin-C9MrcZvh.cjs.map +1 -0
- package/dist/useCedrosLogin-_94MmGGq.js +216 -0
- package/dist/useCedrosLogin-_94MmGGq.js.map +1 -0
- package/dist/useOrgs-C3pzMA9h.js +215 -0
- package/dist/useOrgs-C3pzMA9h.js.map +1 -0
- package/dist/useOrgs-DDVRCaVi.cjs +1 -0
- package/dist/useOrgs-DDVRCaVi.cjs.map +1 -0
- package/dist/useSystemSettings-DBlAMjFi.js +97 -0
- package/dist/useSystemSettings-DBlAMjFi.js.map +1 -0
- package/dist/useSystemSettings-DRrreszl.cjs +1 -0
- package/dist/useSystemSettings-DRrreszl.cjs.map +1 -0
- package/dist/useUsersStatsSummary-8qY7iP4G.cjs +1 -0
- package/dist/useUsersStatsSummary-8qY7iP4G.cjs.map +1 -0
- package/dist/useUsersStatsSummary-NjEFvWuz.js +1246 -0
- package/dist/useUsersStatsSummary-NjEFvWuz.js.map +1 -0
- package/dist/{validation-BebL7hMF.js → validation-B8kMV3BL.js} +1 -1
- package/dist/{validation-BebL7hMF.js.map → validation-B8kMV3BL.js.map} +1 -1
- package/dist/{validation-BeXIfuHB.cjs → validation-BuGQrA-K.cjs} +1 -1
- package/dist/{validation-BeXIfuHB.cjs.map → validation-BuGQrA-K.cjs.map} +1 -1
- package/package.json +11 -1
- package/dist/AuthenticationSettings-BSoIQ58T.js.map +0 -1
- package/dist/AuthenticationSettings-CNmWEPFV.cjs +0 -1
- package/dist/AuthenticationSettings-CNmWEPFV.cjs.map +0 -1
- package/dist/CreditSystemSettings-BYxoFwaP.js +0 -9
- package/dist/CreditSystemSettings-BYxoFwaP.js.map +0 -1
- package/dist/CreditSystemSettings-D2fLorNx.cjs +0 -1
- package/dist/CreditSystemSettings-D2fLorNx.cjs.map +0 -1
- package/dist/DepositsSection-0Hqr0yDm.js.map +0 -1
- package/dist/DepositsSection-CGKCzM-H.cjs +0 -1
- package/dist/DepositsSection-CGKCzM-H.cjs.map +0 -1
- package/dist/EmailRegisterForm-C0PStlj5.js +0 -961
- package/dist/EmailRegisterForm-C0PStlj5.js.map +0 -1
- package/dist/EmailRegisterForm-DaPLGws2.cjs +0 -1
- package/dist/EmailRegisterForm-DaPLGws2.cjs.map +0 -1
- package/dist/EmailSettings-CiLDwzNb.cjs +0 -1
- package/dist/EmailSettings-CiLDwzNb.cjs.map +0 -1
- package/dist/EmailSettings-DTjpO65J.js +0 -9
- package/dist/EmailSettings-DTjpO65J.js.map +0 -1
- package/dist/EmbeddedWalletSettings-CX7sYiPy.cjs +0 -1
- package/dist/EmbeddedWalletSettings-CX7sYiPy.cjs.map +0 -1
- package/dist/EmbeddedWalletSettings-iVf_3pn3.js.map +0 -1
- package/dist/ErrorMessage-BXHiYibN.cjs +0 -1
- package/dist/ErrorMessage-BXHiYibN.cjs.map +0 -1
- package/dist/ErrorMessage-DU2VBb85.js.map +0 -1
- package/dist/GoogleLoginButton-DoAdOqb3.cjs +0 -1
- package/dist/ServerSettings-C_osdbiX.js.map +0 -1
- package/dist/ServerSettings-HqSb_VDj.cjs +0 -1
- package/dist/ServerSettings-HqSb_VDj.cjs.map +0 -1
- package/dist/SolanaLoginButton-CPzms0kf.cjs +0 -1
- package/dist/TeamSection-Bjt72upx.js.map +0 -1
- package/dist/TeamSection-DZeWHkLd.cjs +0 -1
- package/dist/TeamSection-DZeWHkLd.cjs.map +0 -1
- package/dist/UsersSection-DU86czWP.cjs +0 -1
- package/dist/UsersSection-DU86czWP.cjs.map +0 -1
- package/dist/UsersSection-hAcyfSZk.js +0 -81
- package/dist/UsersSection-hAcyfSZk.js.map +0 -1
- package/dist/WebhookSettings-Br9bakAL.cjs +0 -1
- package/dist/WebhookSettings-Br9bakAL.cjs.map +0 -1
- package/dist/WebhookSettings-IyhyTFes.js.map +0 -1
- package/dist/WithdrawalsSection-B1415d-G.js +0 -20
- package/dist/WithdrawalsSection-B1415d-G.js.map +0 -1
- package/dist/WithdrawalsSection-iZJBuZ7O.cjs +0 -1
- package/dist/WithdrawalsSection-iZJBuZ7O.cjs.map +0 -1
- package/dist/admin/AdminShell.d.ts +0 -38
- package/dist/admin/icons.d.ts +0 -2
- package/dist/admin/index.d.ts +0 -10
- package/dist/admin/plugin.d.ts +0 -3
- package/dist/admin/sections/AuthenticationSettings.d.ts +0 -3
- package/dist/admin/sections/CreditSystemSettings.d.ts +0 -3
- package/dist/admin/sections/DepositsSection.d.ts +0 -3
- package/dist/admin/sections/EmailSettings.d.ts +0 -3
- package/dist/admin/sections/EmbeddedWalletSettings.d.ts +0 -3
- package/dist/admin/sections/FeatureSettings.d.ts +0 -3
- package/dist/admin/sections/InvitesSection.d.ts +0 -3
- package/dist/admin/sections/MembersSection.d.ts +0 -3
- package/dist/admin/sections/ServerSettings.d.ts +0 -3
- package/dist/admin/sections/SettingsSections.d.ts +0 -7
- package/dist/admin/sections/TeamSection.d.ts +0 -3
- package/dist/admin/sections/UsersSection.d.ts +0 -3
- package/dist/admin/sections/WebhookSettings.d.ts +0 -3
- package/dist/admin/sections/WithdrawalsSection.d.ts +0 -3
- package/dist/components/LoginButton.d.ts +0 -23
- package/dist/components/LoginForm.d.ts +0 -9
- package/dist/components/LoginModal.d.ts +0 -9
- package/dist/components/admin/AdminUserDetail.d.ts +0 -23
- package/dist/components/admin/AdminUserList.d.ts +0 -21
- package/dist/components/admin/CedrosAdminDashboard.d.ts +0 -52
- package/dist/components/admin/PermissionsSection.d.ts +0 -7
- package/dist/components/admin/ProfileDropdown.d.ts +0 -34
- package/dist/components/admin/SetupWizard.d.ts +0 -30
- package/dist/components/admin/StatsBar.d.ts +0 -17
- package/dist/components/admin/SystemSettings.d.ts +0 -19
- package/dist/components/admin/settings/AuthenticationSettings.d.ts +0 -4
- package/dist/components/admin/settings/AutosaveStatus.d.ts +0 -6
- package/dist/components/admin/settings/CreditSystemSettings.d.ts +0 -6
- package/dist/components/admin/settings/EmailSettings.d.ts +0 -4
- package/dist/components/admin/settings/EmbeddedWalletSettings.d.ts +0 -4
- package/dist/components/admin/settings/FeatureSettings.d.ts +0 -4
- package/dist/components/admin/settings/MessagingSettings.d.ts +0 -4
- package/dist/components/admin/settings/SecuritySettings.d.ts +0 -4
- package/dist/components/admin/settings/ServerSettings.d.ts +0 -4
- package/dist/components/admin/settings/SettingsPageLayout.d.ts +0 -16
- package/dist/components/admin/settings/SsoProvidersSettings.d.ts +0 -4
- package/dist/components/admin/settings/WebhookSettings.d.ts +0 -4
- package/dist/components/admin/settings/index.d.ts +0 -31
- package/dist/components/admin/settings/settingsInputs.d.ts +0 -79
- package/dist/components/admin/settings/settingsMetadata.d.ts +0 -18
- package/dist/components/apple/AppleLoginButton.d.ts +0 -29
- package/dist/components/deposit/CreditBalance.d.ts +0 -19
- package/dist/components/deposit/DepositFlow.d.ts +0 -118
- package/dist/components/deposit/FeeConfigDisplay.d.ts +0 -15
- package/dist/components/deposit/History.d.ts +0 -21
- package/dist/components/deposit/TieredAmountSlider.d.ts +0 -19
- package/dist/components/deposit/TokenSelector.d.ts +0 -23
- package/dist/components/deposit/admin/AdminDepositList.d.ts +0 -21
- package/dist/components/deposit/admin/AdminDepositStats.d.ts +0 -15
- package/dist/components/deposit/admin/AdminPrivacyPeriodDeposits.d.ts +0 -19
- package/dist/components/deposit/admin/AdminWithdrawalHistory.d.ts +0 -19
- package/dist/components/deposit/admin/AdminWithdrawalQueue.d.ts +0 -23
- package/dist/components/deposit/admin/AdminWithdrawalStats.d.ts +0 -10
- package/dist/components/deposit/admin/FeatureDisabledMessage.d.ts +0 -15
- package/dist/components/deposit/admin/PrivacySystemStatus.d.ts +0 -15
- package/dist/components/deposit/admin/featureDisabled.d.ts +0 -14
- package/dist/components/deposit/admin/index.d.ts +0 -22
- package/dist/components/deposit/index.d.ts +0 -12
- package/dist/components/deposit/tierUtils.d.ts +0 -8
- package/dist/components/deposit/tokens.d.ts +0 -19
- package/dist/components/email/EmailLoginForm.d.ts +0 -11
- package/dist/components/email/EmailRegisterForm.d.ts +0 -14
- package/dist/components/email/ForgotPasswordForm.d.ts +0 -17
- package/dist/components/email/PasswordInput.d.ts +0 -14
- package/dist/components/email/ResetPasswordForm.d.ts +0 -22
- package/dist/components/google/GoogleLoginButton.d.ts +0 -12
- package/dist/components/invites/InviteForm.d.ts +0 -38
- package/dist/components/invites/InviteList.d.ts +0 -40
- package/dist/components/members/MemberList.d.ts +0 -47
- package/dist/components/org/CreateOrgForm.d.ts +0 -7
- package/dist/components/org/OrgAvatar.d.ts +0 -7
- package/dist/components/org/OrgListView.d.ts +0 -9
- package/dist/components/org/OrgSelector.d.ts +0 -52
- package/dist/components/org/OrgSwitcher.d.ts +0 -47
- package/dist/components/org/icons.d.ts +0 -8
- package/dist/components/profile/UserProfileSettings.d.ts +0 -35
- package/dist/components/profile/index.d.ts +0 -2
- package/dist/components/sessions/SessionList.d.ts +0 -33
- package/dist/components/shared/ErrorBoundary.d.ts +0 -38
- package/dist/components/shared/ErrorMessage.d.ts +0 -14
- package/dist/components/shared/LoadingSpinner.d.ts +0 -16
- package/dist/components/solana/SolanaLoginButton.d.ts +0 -49
- package/dist/components/templates/FullPageLayout.d.ts +0 -40
- package/dist/components/templates/SplitPageLayout.d.ts +0 -44
- package/dist/components/templates/index.d.ts +0 -4
- package/dist/components/totp/OtpInput.d.ts +0 -32
- package/dist/components/totp/QrCode.d.ts +0 -21
- package/dist/components/totp/TotpSettings.d.ts +0 -38
- package/dist/components/totp/TotpSetup.d.ts +0 -23
- package/dist/components/totp/TotpVerify.d.ts +0 -25
- package/dist/components/totp/index.d.ts +0 -10
- package/dist/components/wallet/CapabilityWarning.d.ts +0 -11
- package/dist/components/wallet/PasskeyPrompt.d.ts +0 -34
- package/dist/components/wallet/RecoveryPhraseDisplay.d.ts +0 -18
- package/dist/components/wallet/RecoveryPhraseInput.d.ts +0 -21
- package/dist/components/wallet/WalletAddressRow.d.ts +0 -10
- package/dist/components/wallet/WalletEnrollment.d.ts +0 -15
- package/dist/components/wallet/WalletManager.d.ts +0 -9
- package/dist/components/wallet/WalletRecovery.d.ts +0 -19
- package/dist/components/wallet/WalletStatus.d.ts +0 -28
- package/dist/components/wallet/WalletUnlock.d.ts +0 -23
- package/dist/components/wallet/index.d.ts +0 -23
- package/dist/components/webauthn/PasskeyLoginButton.d.ts +0 -8
- package/dist/context/CedrosLoginContext.d.ts +0 -24
- package/dist/context/CedrosLoginProvider.d.ts +0 -17
- package/dist/context/EmbeddedWalletExposure.d.ts +0 -19
- package/dist/context/useCedrosLogin.d.ts +0 -12
- package/dist/crypto/aesGcm.d.ts +0 -89
- package/dist/crypto/argon2.d.ts +0 -65
- package/dist/crypto/argon2Worker.d.ts +0 -1
- package/dist/crypto/argon2WorkerClient.d.ts +0 -28
- package/dist/crypto/bip39.d.ts +0 -106
- package/dist/crypto/capabilities.d.ts +0 -35
- package/dist/crypto/entropy.d.ts +0 -56
- package/dist/crypto/hkdf.d.ts +0 -38
- package/dist/crypto/index.d.ts +0 -30
- package/dist/crypto/secureWipe.d.ts +0 -71
- package/dist/crypto/shamir.d.ts +0 -52
- package/dist/crypto/solanaKeypair.d.ts +0 -63
- package/dist/crypto/types.d.ts +0 -134
- package/dist/crypto/webauthnPrf.d.ts +0 -108
- package/dist/hooks/useAdminDeposits.d.ts +0 -10
- package/dist/hooks/useAdminUsers.d.ts +0 -28
- package/dist/hooks/useAppleAuth.d.ts +0 -52
- package/dist/hooks/useAuth.d.ts +0 -34
- package/dist/hooks/useAuthSession.d.ts +0 -19
- package/dist/hooks/useAuthorize.d.ts +0 -62
- package/dist/hooks/useCredits.d.ts +0 -11
- package/dist/hooks/useDashboardPermissions.d.ts +0 -45
- package/dist/hooks/useDeposit.d.ts +0 -16
- package/dist/hooks/useEmailAuth.d.ts +0 -60
- package/dist/hooks/useGoogleAuth.d.ts +0 -67
- package/dist/hooks/useInstantLink.d.ts +0 -42
- package/dist/hooks/useInvites.d.ts +0 -57
- package/dist/hooks/useMembers.d.ts +0 -52
- package/dist/hooks/useOrgs.d.ts +0 -49
- package/dist/hooks/usePasswordReset.d.ts +0 -32
- package/dist/hooks/usePendingRecovery.d.ts +0 -34
- package/dist/hooks/useProfile.d.ts +0 -35
- package/dist/hooks/useRateLimiter.d.ts +0 -58
- package/dist/hooks/useServerFeatures.d.ts +0 -55
- package/dist/hooks/useSessions.d.ts +0 -45
- package/dist/hooks/useSettingsAutosave.d.ts +0 -29
- package/dist/hooks/useSetup.d.ts +0 -25
- package/dist/hooks/useSolanaAuth.d.ts +0 -30
- package/dist/hooks/useSsoProviders.d.ts +0 -52
- package/dist/hooks/useSystemSettings.d.ts +0 -47
- package/dist/hooks/useThemeManager.d.ts +0 -11
- package/dist/hooks/useTotp.d.ts +0 -52
- package/dist/hooks/useTotpVerify.d.ts +0 -38
- package/dist/hooks/useTransactionSigning.d.ts +0 -45
- package/dist/hooks/useWallet.d.ts +0 -10
- package/dist/hooks/useWalletDiscovery.d.ts +0 -24
- package/dist/hooks/useWalletEnrollment.d.ts +0 -9
- package/dist/hooks/useWalletMaterial.d.ts +0 -10
- package/dist/hooks/useWalletRecovery.d.ts +0 -9
- package/dist/hooks/useWalletSigning.d.ts +0 -31
- package/dist/hooks/useWebAuthn.d.ts +0 -25
- package/dist/i18n/I18nProvider.d.ts +0 -16
- package/dist/i18n/context.d.ts +0 -6
- package/dist/i18n/index.d.ts +0 -5
- package/dist/i18n/translations.d.ts +0 -66
- package/dist/i18n/useI18n.d.ts +0 -9
- package/dist/index-ZS9AwAal.cjs +0 -2061
- package/dist/index-ZS9AwAal.cjs.map +0 -1
- package/dist/index-dgg5tlO7.js +0 -19650
- package/dist/index-dgg5tlO7.js.map +0 -1
- package/dist/silentWalletEnroll-C3JRAD9_.js +0 -42
- package/dist/silentWalletEnroll-C3JRAD9_.js.map +0 -1
- package/dist/silentWalletEnroll-DRYQAQfg.cjs +0 -1
- package/dist/silentWalletEnroll-DRYQAQfg.cjs.map +0 -1
- package/dist/solanaKeypair-BlXol3nh.js.map +0 -1
- package/dist/solanaKeypair-C-lIR1JY.cjs +0 -1
- package/dist/solanaKeypair-C-lIR1JY.cjs.map +0 -1
- package/dist/types/adminUser.d.ts +0 -168
- package/dist/types/auth.d.ts +0 -122
- package/dist/types/config.d.ts +0 -266
- package/dist/types/deposit.d.ts +0 -490
- package/dist/types/index.d.ts +0 -13
- package/dist/types/invite.d.ts +0 -71
- package/dist/types/member.d.ts +0 -45
- package/dist/types/org.d.ts +0 -133
- package/dist/types/profile.d.ts +0 -56
- package/dist/types/session.d.ts +0 -28
- package/dist/types/setup.d.ts +0 -47
- package/dist/types/systemSettings.d.ts +0 -85
- package/dist/types/totp.d.ts +0 -52
- package/dist/types/wallet.d.ts +0 -309
- package/dist/utils/adminUserApi.d.ts +0 -60
- package/dist/utils/apiClient.d.ts +0 -78
- package/dist/utils/cryptoShim.d.ts +0 -17
- package/dist/utils/csrf.d.ts +0 -1
- package/dist/utils/deviceDetection.d.ts +0 -17
- package/dist/utils/embeddedWallet.d.ts +0 -75
- package/dist/utils/inviteApi.d.ts +0 -31
- package/dist/utils/memberApi.d.ts +0 -23
- package/dist/utils/orgApi.d.ts +0 -36
- package/dist/utils/profileApi.d.ts +0 -26
- package/dist/utils/sanitization.d.ts +0 -66
- package/dist/utils/sessionApi.d.ts +0 -16
- package/dist/utils/setupApi.d.ts +0 -21
- package/dist/utils/silentWalletEnroll.d.ts +0 -41
- package/dist/utils/systemSettingsApi.d.ts +0 -18
- package/dist/utils/tabSync.d.ts +0 -46
- package/dist/utils/tokenManager.d.ts +0 -107
- package/dist/utils/unlockCredential.d.ts +0 -5
- package/dist/utils/validation.d.ts +0 -48
- package/dist/utils/walletDetection.d.ts +0 -23
- package/dist/utils/webauthnJson.d.ts +0 -21
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const h=require("react"),m=require("./LoadingSpinner-d6sSxgQN.cjs"),w=32;function C(){if(typeof document>"u")return null;const t=document.querySelector('meta[name="csrf-token"]');if(t){const r=t.getAttribute("content");if(r&&r.length>=w)return r}const e=document.cookie.split(";");for(const r of e){const[s,...i]=r.trim().split("="),a=i.join("="),l=s.toLowerCase();if(l==="xsrf-token"||l==="csrf-token")try{const u=decodeURIComponent(a.trim());if(u.length>=w)return u}catch{continue}}return null}const b=1e4,S=2;function v(t,e){return{code:t.code||"SERVER_ERROR",message:t.message||e,details:t.details}}function L(){return{code:"NETWORK_ERROR",message:"Unable to connect to server"}}async function k(t,e,r){const s=new AbortController,i=setTimeout(()=>s.abort(),r);try{return await fetch(t,{...e,signal:s.signal})}finally{clearTimeout(i)}}function x(t){if(t instanceof Error){if(t.retryable)return!0;if(t.name==="AbortError")return!1;if(t.message.includes("fetch"))return!0}return!1}function U(t){return new Promise(e=>setTimeout(e,t))}class O{baseUrl;timeoutMs;retryAttempts;getAccessToken;constructor(e){this.baseUrl=e.baseUrl,this.timeoutMs=e.timeoutMs??b,this.retryAttempts=e.retryAttempts??S,this.getAccessToken=e.getAccessToken}async request(e){const{method:r,path:s,body:i,credentials:a="include",skipRetry:l=!1,validator:u}=e,R=`${this.baseUrl}${s}`,E=l||!(r==="GET"||r==="HEAD"||r==="PUT")?1:this.retryAttempts+1,d={};i!==void 0&&(d["Content-Type"]="application/json");const A=this.getAccessToken?.();A&&(d.Authorization=`Bearer ${A}`);const T=C();T&&(d["X-CSRF-Token"]=T);let g;for(let f=1;f<=E;f++)try{const n=await k(R,{method:r,headers:d,credentials:a,body:i!==void 0?JSON.stringify(i):void 0},this.timeoutMs),y=n.headers.get("content-type")||"";let c={};if(y.includes("application/json")){if(n.status!==204)try{c=await n.json()}catch(o){const p=o instanceof Error?o.message:"parse failed";throw new Error(`Invalid JSON response: ${p}`)}}else{const o=await n.text();if(o){const p=o.length>200?o.slice(0,200)+"...":o;c={message:y.includes("text/html")||o.trimStart().startsWith("<")?`Unexpected HTML response (${n.status}). The server may be unavailable.`:p}}}if(!n.ok){if(n.status>=400&&n.status<500)throw{isApiError:!0,data:c,status:n.status};const o=new Error(`Server error: ${n.status}`);throw o.retryable=!0,o}if(u)try{return u(c)}catch(o){throw new Error(`Response validation failed: ${o instanceof Error?o.message:"Invalid response shape"}`)}return c}catch(n){if(g=n,typeof n=="object"&&n!==null&&"isApiError"in n)throw n;if(f<E&&x(n)){await U(100*Math.pow(2,f-1));continue}throw n}throw g}async post(e,r,s){return this.request({method:"POST",path:e,body:r,...s})}async get(e,r){return this.request({method:"GET",path:e,...r})}async patch(e,r,s){return this.request({method:"PATCH",path:e,body:r,...s})}async delete(e,r){return this.request({method:"DELETE",path:e,...r})}}function I(t){return typeof t=="object"&&t!==null&&"isApiError"in t}function _(t){return typeof t=="object"&&t!==null&&"code"in t&&"message"in t}function q(t,e){if(_(t))return t;if(I(t))return v(t.data,e);if(t instanceof Error){if(t.name==="AbortError")return{code:"NETWORK_ERROR",message:"Request timed out"};if(t.message.startsWith("Server error:")||t.message.startsWith("Invalid JSON response"))return{code:"SERVER_ERROR",message:e}}return L()}function M(){const t=h.useContext(m.CedrosLoginContext);if(!t)throw new Error("useCedrosLogin must be used within a CedrosLoginProvider");return t}function N(){return h.useContext(m.CedrosLoginContext)}function P(){const t=h.useContext(m.AuthStateContext);if(!t)throw new Error("useAuthState must be used within a CedrosLoginProvider");return t}function j(){const t=h.useContext(m.AuthUIContext);if(!t)throw new Error("useAuthUI must be used within a CedrosLoginProvider");return t}exports.ApiClient=O;exports.getCsrfToken=C;exports.handleApiError=q;exports.useAuthState=P;exports.useAuthUI=j;exports.useCedrosLogin=M;exports.useCedrosLoginOptional=N;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCedrosLogin-C9MrcZvh.cjs","sources":["../src/utils/csrf.ts","../src/utils/apiClient.ts","../src/context/useCedrosLogin.ts"],"sourcesContent":["// UI-CSRF: Minimum CSRF token length to prevent weak/trivial tokens\n// UI-07: Raised from 20 to 32 bytes to meet minimum entropy requirements\nconst MIN_CSRF_TOKEN_LENGTH = 32;\n\nexport function getCsrfToken(): string | null {\n if (typeof document === 'undefined') return null;\n\n const metaTag = document.querySelector('meta[name=\"csrf-token\"]');\n if (metaTag) {\n const content = metaTag.getAttribute('content');\n // UI-CSRF: Reject weak tokens\n if (content && content.length >= MIN_CSRF_TOKEN_LENGTH) {\n return content;\n }\n }\n\n // UI-2 FIX: Use case-insensitive comparison for cookie names.\n // Server may set cookie with different casing (XSRF-TOKEN, xsrf-token, etc.)\n const cookies = document.cookie.split(';');\n for (const cookie of cookies) {\n const [name, ...rest] = cookie.trim().split('=');\n const value = rest.join('=');\n const nameLower = name.toLowerCase();\n if (nameLower === 'xsrf-token' || nameLower === 'csrf-token') {\n try {\n const decoded = decodeURIComponent(value.trim());\n // UI-CSRF: Reject weak tokens\n if (decoded.length >= MIN_CSRF_TOKEN_LENGTH) {\n return decoded;\n }\n } catch {\n // Malformed URL-encoded value - skip this cookie\n continue;\n }\n }\n }\n\n return null;\n}\n","import type { AuthError, AuthErrorCode } from '../types';\nimport { getCsrfToken } from './csrf';\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_RETRY_ATTEMPTS = 2;\n\nexport interface ApiClientConfig {\n baseUrl: string;\n timeoutMs?: number;\n retryAttempts?: number;\n getAccessToken?: () => string | null;\n}\n\n/**\n * M-02: Response validator function type.\n * Returns the validated data or throws on invalid shape.\n */\nexport type ResponseValidator<T> = (data: unknown) => T;\n\nexport interface RequestOptions<T = unknown> {\n method: 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n path: string;\n body?: unknown;\n credentials?: RequestCredentials;\n skipRetry?: boolean;\n /** M-02: Optional validator to verify response shape at runtime */\n validator?: ResponseValidator<T>;\n}\n\n/**\n * Creates an authentication error from response data\n */\nexport function createAuthError(\n data: { code?: string; message?: string; details?: Record<string, unknown> },\n fallbackMessage: string\n): AuthError {\n return {\n code: (data.code as AuthErrorCode) || 'SERVER_ERROR',\n message: data.message || fallbackMessage,\n details: data.details,\n };\n}\n\n/**\n * Creates a network error\n */\nexport function createNetworkError(): AuthError {\n return {\n code: 'NETWORK_ERROR',\n message: 'Unable to connect to server',\n };\n}\n\n/**\n * Fetch with timeout support\n */\nasync function fetchWithTimeout(\n url: string,\n options: RequestInit,\n timeoutMs: number\n): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n });\n return response;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Determines if an error is retryable\n * UI-8 FIX: AbortError (timeout) should NOT be retried - server may have processed request\n */\nfunction isRetryableError(error: unknown): boolean {\n if (error instanceof Error) {\n if ((error as { retryable?: boolean }).retryable) return true;\n // UI-8: AbortError from timeout should NOT be retried\n // Server may have processed the request (just responded slowly)\n // Retrying could cause duplicate operations\n if (error.name === 'AbortError') return false;\n // Network errors (connection failed) are safe to retry\n if (error.message.includes('fetch')) return true;\n }\n return false;\n}\n\n/**\n * Delays execution for the specified duration\n */\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * API client for making authenticated requests with timeout and retry support\n */\nexport class ApiClient {\n private baseUrl: string;\n private timeoutMs: number;\n private retryAttempts: number;\n private getAccessToken?: () => string | null;\n\n constructor(config: ApiClientConfig) {\n this.baseUrl = config.baseUrl;\n this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.retryAttempts = config.retryAttempts ?? DEFAULT_RETRY_ATTEMPTS;\n this.getAccessToken = config.getAccessToken;\n }\n\n /**\n * Make an API request with timeout and optional retry\n */\n async request<T>(options: RequestOptions<T>): Promise<T> {\n const { method, path, body, credentials = 'include', skipRetry = false, validator } = options;\n const url = `${this.baseUrl}${path}`;\n // S-10: DELETE excluded — retrying mid-flight DELETE failures risks double-deletion\n const isIdempotent = method === 'GET' || method === 'HEAD' || method === 'PUT';\n const maxAttempts = skipRetry || !isIdempotent ? 1 : this.retryAttempts + 1;\n\n // Build headers with CSRF token if available\n const headers: Record<string, string> = {};\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n const accessToken = this.getAccessToken?.();\n if (accessToken) {\n headers.Authorization = `Bearer ${accessToken}`;\n }\n const csrfToken = getCsrfToken();\n if (csrfToken) {\n headers['X-CSRF-Token'] = csrfToken;\n }\n\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const response = await fetchWithTimeout(\n url,\n {\n method,\n headers,\n credentials,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n },\n this.timeoutMs\n );\n\n const contentType = response.headers.get('content-type') || '';\n let data: { code?: string; message?: string; details?: Record<string, unknown> } = {};\n\n if (contentType.includes('application/json')) {\n if (response.status !== 204) {\n try {\n data = (await response.json()) as {\n code?: string;\n message?: string;\n details?: Record<string, unknown>;\n };\n } catch (e) {\n // UI-JSON: Include actual parse error for easier debugging\n const parseError = e instanceof Error ? e.message : 'parse failed';\n throw new Error(`Invalid JSON response: ${parseError}`);\n }\n }\n } else {\n // U-01: Handle non-JSON responses with informative error messages\n // Proxies/load balancers may return HTML error pages (502, 503)\n const text = await response.text();\n if (text) {\n // Truncate very long responses (e.g., HTML pages) for readability\n const truncated = text.length > 200 ? text.slice(0, 200) + '...' : text;\n const isHtml = contentType.includes('text/html') || text.trimStart().startsWith('<');\n data = {\n message: isHtml\n ? `Unexpected HTML response (${response.status}). The server may be unavailable.`\n : truncated,\n };\n }\n }\n\n if (!response.ok) {\n // Don't retry 4xx errors (client errors)\n if (response.status >= 400 && response.status < 500) {\n throw { isApiError: true, data, status: response.status };\n }\n // Retry 5xx errors\n const err = new Error(`Server error: ${response.status}`);\n (err as { retryable?: boolean }).retryable = true;\n throw err;\n }\n\n // M-02: Apply response validation if provided\n if (validator) {\n try {\n return validator(data);\n } catch (validationError) {\n throw new Error(\n `Response validation failed: ${validationError instanceof Error ? validationError.message : 'Invalid response shape'}`\n );\n }\n }\n\n return data as T;\n } catch (error) {\n lastError = error;\n\n // Don't retry API errors (4xx responses)\n if (typeof error === 'object' && error !== null && 'isApiError' in error) {\n throw error;\n }\n\n // Check if we should retry\n if (attempt < maxAttempts && isRetryableError(error)) {\n // Exponential backoff: 100ms, 200ms, 400ms...\n await delay(100 * Math.pow(2, attempt - 1));\n continue;\n }\n\n throw error;\n }\n }\n\n throw lastError;\n }\n\n /**\n * POST request helper\n */\n async post<T>(path: string, body: unknown, options?: Partial<RequestOptions<T>>): Promise<T> {\n return this.request<T>({ method: 'POST', path, body, ...options });\n }\n\n /**\n * GET request helper\n */\n async get<T>(path: string, options?: Partial<RequestOptions<T>>): Promise<T> {\n return this.request<T>({ method: 'GET', path, ...options });\n }\n\n /**\n * PATCH request helper\n */\n async patch<T>(path: string, body: unknown, options?: Partial<RequestOptions<T>>): Promise<T> {\n return this.request<T>({ method: 'PATCH', path, body, ...options });\n }\n\n /**\n * DELETE request helper\n */\n async delete<T>(path: string, options?: Partial<RequestOptions<T>>): Promise<T> {\n return this.request<T>({ method: 'DELETE', path, ...options });\n }\n}\n\ninterface ApiErrorResponse {\n isApiError: true;\n data: { code?: string; message?: string; details?: Record<string, unknown> };\n status: number;\n}\n\nfunction isApiErrorResponse(err: unknown): err is ApiErrorResponse {\n return typeof err === 'object' && err !== null && 'isApiError' in err;\n}\n\nfunction isAuthError(err: unknown): err is AuthError {\n return typeof err === 'object' && err !== null && 'code' in err && 'message' in err;\n}\n\n/**\n * M-02: Helper to create a basic object shape validator.\n * Checks that required keys exist and are of expected types.\n * @example\n * const validateUser = createValidator<User>({\n * id: 'string',\n * email: 'string',\n * role: 'string',\n * });\n */\nexport function createValidator<T>(\n shape: Record<keyof T & string, 'string' | 'number' | 'boolean' | 'object'>\n): ResponseValidator<T> {\n return (data: unknown): T => {\n if (typeof data !== 'object' || data === null) {\n throw new Error('Expected object response');\n }\n const obj = data as Record<string, unknown>;\n for (const [key, expectedType] of Object.entries(shape)) {\n if (!(key in obj)) {\n throw new Error(`Missing required field: ${key}`);\n }\n const actualType = typeof obj[key];\n if (actualType !== expectedType) {\n throw new Error(`Invalid type for ${key}: expected ${expectedType}, got ${actualType}`);\n }\n }\n return data as T;\n };\n}\n\n/**\n * Converts API errors to AuthError format\n */\nexport function handleApiError(err: unknown, fallbackMessage: string): AuthError {\n // Already an AuthError\n if (isAuthError(err)) {\n return err;\n }\n\n // API error response (4xx/5xx)\n if (isApiErrorResponse(err)) {\n return createAuthError(err.data, fallbackMessage);\n }\n\n if (err instanceof Error) {\n if (err.name === 'AbortError') {\n return {\n code: 'NETWORK_ERROR',\n message: 'Request timed out',\n };\n }\n if (\n err.message.startsWith('Server error:') ||\n err.message.startsWith('Invalid JSON response')\n ) {\n return {\n code: 'SERVER_ERROR',\n message: fallbackMessage,\n };\n }\n }\n\n // Network or timeout error\n return createNetworkError();\n}\n","import { useContext } from 'react';\nimport {\n AuthStateContext,\n AuthUIContext,\n CedrosLoginContext,\n type AuthStateContextValue,\n type AuthUIContextValue,\n type CedrosLoginContextValue,\n} from './CedrosLoginContext';\n\n/**\n * Hook to access the full Cedros Login context.\n * Must be used within a CedrosLoginProvider.\n *\n * For better performance, prefer `useAuthState()` or `useAuthUI()` when you\n * only need a subset of the context. This hook re-renders on any change.\n */\nexport function useCedrosLogin(): CedrosLoginContextValue {\n const context = useContext(CedrosLoginContext);\n if (!context) {\n throw new Error('useCedrosLogin must be used within a CedrosLoginProvider');\n }\n return context;\n}\n\n/**\n * Optional version of useCedrosLogin that returns null instead of throwing\n * when used outside a CedrosLoginProvider. Useful for components that need\n * to work in both provider and non-provider contexts (e.g., Storybook demos).\n */\nexport function useCedrosLoginOptional(): CedrosLoginContextValue | null {\n return useContext(CedrosLoginContext);\n}\n\n/**\n * Hook to access only auth state (user, authState, config, logout, refreshUser).\n *\n * Does NOT re-render on UI state changes (modal, error). Use this in components\n * that only need to know about authentication status.\n */\nexport function useAuthState(): AuthStateContextValue {\n const context = useContext(AuthStateContext);\n if (!context) {\n throw new Error('useAuthState must be used within a CedrosLoginProvider');\n }\n return context;\n}\n\n/**\n * Hook to access only UI state (isModalOpen, error, openModal, closeModal).\n *\n * Does NOT re-render on auth state changes (login, token refresh). Use this\n * in components that only control the login modal or display errors.\n */\nexport function useAuthUI(): AuthUIContextValue {\n const context = useContext(AuthUIContext);\n if (!context) {\n throw new Error('useAuthUI must be used within a CedrosLoginProvider');\n }\n return context;\n}\n"],"names":["MIN_CSRF_TOKEN_LENGTH","getCsrfToken","metaTag","content","cookies","cookie","name","rest","value","nameLower","decoded","DEFAULT_TIMEOUT_MS","DEFAULT_RETRY_ATTEMPTS","createAuthError","data","fallbackMessage","createNetworkError","fetchWithTimeout","url","options","timeoutMs","controller","timeoutId","isRetryableError","error","delay","ms","resolve","ApiClient","config","method","path","body","credentials","skipRetry","validator","maxAttempts","headers","accessToken","csrfToken","lastError","attempt","response","contentType","e","parseError","text","truncated","err","validationError","isApiErrorResponse","isAuthError","handleApiError","useCedrosLogin","context","useContext","CedrosLoginContext","useCedrosLoginOptional","useAuthState","AuthStateContext","useAuthUI","AuthUIContext"],"mappings":"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"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { useContext as h } from "react";
|
|
2
|
+
import { C as w, A as b, a as C } from "./LoadingSpinner-6vml-zwr.js";
|
|
3
|
+
const g = 32;
|
|
4
|
+
function v() {
|
|
5
|
+
if (typeof document > "u") return null;
|
|
6
|
+
const t = document.querySelector('meta[name="csrf-token"]');
|
|
7
|
+
if (t) {
|
|
8
|
+
const r = t.getAttribute("content");
|
|
9
|
+
if (r && r.length >= g)
|
|
10
|
+
return r;
|
|
11
|
+
}
|
|
12
|
+
const e = document.cookie.split(";");
|
|
13
|
+
for (const r of e) {
|
|
14
|
+
const [n, ...i] = r.trim().split("="), u = i.join("="), l = n.toLowerCase();
|
|
15
|
+
if (l === "xsrf-token" || l === "csrf-token")
|
|
16
|
+
try {
|
|
17
|
+
const a = decodeURIComponent(u.trim());
|
|
18
|
+
if (a.length >= g)
|
|
19
|
+
return a;
|
|
20
|
+
} catch {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const S = 1e4, k = 2;
|
|
27
|
+
function x(t, e) {
|
|
28
|
+
return {
|
|
29
|
+
code: t.code || "SERVER_ERROR",
|
|
30
|
+
message: t.message || e,
|
|
31
|
+
details: t.details
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function L() {
|
|
35
|
+
return {
|
|
36
|
+
code: "NETWORK_ERROR",
|
|
37
|
+
message: "Unable to connect to server"
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async function U(t, e, r) {
|
|
41
|
+
const n = new AbortController(), i = setTimeout(() => n.abort(), r);
|
|
42
|
+
try {
|
|
43
|
+
return await fetch(t, {
|
|
44
|
+
...e,
|
|
45
|
+
signal: n.signal
|
|
46
|
+
});
|
|
47
|
+
} finally {
|
|
48
|
+
clearTimeout(i);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function O(t) {
|
|
52
|
+
if (t instanceof Error) {
|
|
53
|
+
if (t.retryable) return !0;
|
|
54
|
+
if (t.name === "AbortError") return !1;
|
|
55
|
+
if (t.message.includes("fetch")) return !0;
|
|
56
|
+
}
|
|
57
|
+
return !1;
|
|
58
|
+
}
|
|
59
|
+
function I(t) {
|
|
60
|
+
return new Promise((e) => setTimeout(e, t));
|
|
61
|
+
}
|
|
62
|
+
class $ {
|
|
63
|
+
baseUrl;
|
|
64
|
+
timeoutMs;
|
|
65
|
+
retryAttempts;
|
|
66
|
+
getAccessToken;
|
|
67
|
+
constructor(e) {
|
|
68
|
+
this.baseUrl = e.baseUrl, this.timeoutMs = e.timeoutMs ?? S, this.retryAttempts = e.retryAttempts ?? k, this.getAccessToken = e.getAccessToken;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Make an API request with timeout and optional retry
|
|
72
|
+
*/
|
|
73
|
+
async request(e) {
|
|
74
|
+
const { method: r, path: n, body: i, credentials: u = "include", skipRetry: l = !1, validator: a } = e, R = `${this.baseUrl}${n}`, p = l || !(r === "GET" || r === "HEAD" || r === "PUT") ? 1 : this.retryAttempts + 1, f = {};
|
|
75
|
+
i !== void 0 && (f["Content-Type"] = "application/json");
|
|
76
|
+
const E = this.getAccessToken?.();
|
|
77
|
+
E && (f.Authorization = `Bearer ${E}`);
|
|
78
|
+
const A = v();
|
|
79
|
+
A && (f["X-CSRF-Token"] = A);
|
|
80
|
+
let T;
|
|
81
|
+
for (let d = 1; d <= p; d++)
|
|
82
|
+
try {
|
|
83
|
+
const s = await U(
|
|
84
|
+
R,
|
|
85
|
+
{
|
|
86
|
+
method: r,
|
|
87
|
+
headers: f,
|
|
88
|
+
credentials: u,
|
|
89
|
+
body: i !== void 0 ? JSON.stringify(i) : void 0
|
|
90
|
+
},
|
|
91
|
+
this.timeoutMs
|
|
92
|
+
), y = s.headers.get("content-type") || "";
|
|
93
|
+
let c = {};
|
|
94
|
+
if (y.includes("application/json")) {
|
|
95
|
+
if (s.status !== 204)
|
|
96
|
+
try {
|
|
97
|
+
c = await s.json();
|
|
98
|
+
} catch (o) {
|
|
99
|
+
const m = o instanceof Error ? o.message : "parse failed";
|
|
100
|
+
throw new Error(`Invalid JSON response: ${m}`);
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
const o = await s.text();
|
|
104
|
+
if (o) {
|
|
105
|
+
const m = o.length > 200 ? o.slice(0, 200) + "..." : o;
|
|
106
|
+
c = {
|
|
107
|
+
message: y.includes("text/html") || o.trimStart().startsWith("<") ? `Unexpected HTML response (${s.status}). The server may be unavailable.` : m
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (!s.ok) {
|
|
112
|
+
if (s.status >= 400 && s.status < 500)
|
|
113
|
+
throw { isApiError: !0, data: c, status: s.status };
|
|
114
|
+
const o = new Error(`Server error: ${s.status}`);
|
|
115
|
+
throw o.retryable = !0, o;
|
|
116
|
+
}
|
|
117
|
+
if (a)
|
|
118
|
+
try {
|
|
119
|
+
return a(c);
|
|
120
|
+
} catch (o) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
`Response validation failed: ${o instanceof Error ? o.message : "Invalid response shape"}`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
return c;
|
|
126
|
+
} catch (s) {
|
|
127
|
+
if (T = s, typeof s == "object" && s !== null && "isApiError" in s)
|
|
128
|
+
throw s;
|
|
129
|
+
if (d < p && O(s)) {
|
|
130
|
+
await I(100 * Math.pow(2, d - 1));
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
throw s;
|
|
134
|
+
}
|
|
135
|
+
throw T;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* POST request helper
|
|
139
|
+
*/
|
|
140
|
+
async post(e, r, n) {
|
|
141
|
+
return this.request({ method: "POST", path: e, body: r, ...n });
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* GET request helper
|
|
145
|
+
*/
|
|
146
|
+
async get(e, r) {
|
|
147
|
+
return this.request({ method: "GET", path: e, ...r });
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* PATCH request helper
|
|
151
|
+
*/
|
|
152
|
+
async patch(e, r, n) {
|
|
153
|
+
return this.request({ method: "PATCH", path: e, body: r, ...n });
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* DELETE request helper
|
|
157
|
+
*/
|
|
158
|
+
async delete(e, r) {
|
|
159
|
+
return this.request({ method: "DELETE", path: e, ...r });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function _(t) {
|
|
163
|
+
return typeof t == "object" && t !== null && "isApiError" in t;
|
|
164
|
+
}
|
|
165
|
+
function M(t) {
|
|
166
|
+
return typeof t == "object" && t !== null && "code" in t && "message" in t;
|
|
167
|
+
}
|
|
168
|
+
function H(t, e) {
|
|
169
|
+
if (M(t))
|
|
170
|
+
return t;
|
|
171
|
+
if (_(t))
|
|
172
|
+
return x(t.data, e);
|
|
173
|
+
if (t instanceof Error) {
|
|
174
|
+
if (t.name === "AbortError")
|
|
175
|
+
return {
|
|
176
|
+
code: "NETWORK_ERROR",
|
|
177
|
+
message: "Request timed out"
|
|
178
|
+
};
|
|
179
|
+
if (t.message.startsWith("Server error:") || t.message.startsWith("Invalid JSON response"))
|
|
180
|
+
return {
|
|
181
|
+
code: "SERVER_ERROR",
|
|
182
|
+
message: e
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return L();
|
|
186
|
+
}
|
|
187
|
+
function W() {
|
|
188
|
+
const t = h(w);
|
|
189
|
+
if (!t)
|
|
190
|
+
throw new Error("useCedrosLogin must be used within a CedrosLoginProvider");
|
|
191
|
+
return t;
|
|
192
|
+
}
|
|
193
|
+
function D() {
|
|
194
|
+
return h(w);
|
|
195
|
+
}
|
|
196
|
+
function F() {
|
|
197
|
+
const t = h(b);
|
|
198
|
+
if (!t)
|
|
199
|
+
throw new Error("useAuthState must be used within a CedrosLoginProvider");
|
|
200
|
+
return t;
|
|
201
|
+
}
|
|
202
|
+
function G() {
|
|
203
|
+
const t = h(C);
|
|
204
|
+
if (!t)
|
|
205
|
+
throw new Error("useAuthUI must be used within a CedrosLoginProvider");
|
|
206
|
+
return t;
|
|
207
|
+
}
|
|
208
|
+
export {
|
|
209
|
+
$ as A,
|
|
210
|
+
D as a,
|
|
211
|
+
F as b,
|
|
212
|
+
G as c,
|
|
213
|
+
v as g,
|
|
214
|
+
H as h,
|
|
215
|
+
W as u
|
|
216
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCedrosLogin-_94MmGGq.js","sources":["../src/utils/csrf.ts","../src/utils/apiClient.ts","../src/context/useCedrosLogin.ts"],"sourcesContent":["// UI-CSRF: Minimum CSRF token length to prevent weak/trivial tokens\n// UI-07: Raised from 20 to 32 bytes to meet minimum entropy requirements\nconst MIN_CSRF_TOKEN_LENGTH = 32;\n\nexport function getCsrfToken(): string | null {\n if (typeof document === 'undefined') return null;\n\n const metaTag = document.querySelector('meta[name=\"csrf-token\"]');\n if (metaTag) {\n const content = metaTag.getAttribute('content');\n // UI-CSRF: Reject weak tokens\n if (content && content.length >= MIN_CSRF_TOKEN_LENGTH) {\n return content;\n }\n }\n\n // UI-2 FIX: Use case-insensitive comparison for cookie names.\n // Server may set cookie with different casing (XSRF-TOKEN, xsrf-token, etc.)\n const cookies = document.cookie.split(';');\n for (const cookie of cookies) {\n const [name, ...rest] = cookie.trim().split('=');\n const value = rest.join('=');\n const nameLower = name.toLowerCase();\n if (nameLower === 'xsrf-token' || nameLower === 'csrf-token') {\n try {\n const decoded = decodeURIComponent(value.trim());\n // UI-CSRF: Reject weak tokens\n if (decoded.length >= MIN_CSRF_TOKEN_LENGTH) {\n return decoded;\n }\n } catch {\n // Malformed URL-encoded value - skip this cookie\n continue;\n }\n }\n }\n\n return null;\n}\n","import type { AuthError, AuthErrorCode } from '../types';\nimport { getCsrfToken } from './csrf';\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_RETRY_ATTEMPTS = 2;\n\nexport interface ApiClientConfig {\n baseUrl: string;\n timeoutMs?: number;\n retryAttempts?: number;\n getAccessToken?: () => string | null;\n}\n\n/**\n * M-02: Response validator function type.\n * Returns the validated data or throws on invalid shape.\n */\nexport type ResponseValidator<T> = (data: unknown) => T;\n\nexport interface RequestOptions<T = unknown> {\n method: 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n path: string;\n body?: unknown;\n credentials?: RequestCredentials;\n skipRetry?: boolean;\n /** M-02: Optional validator to verify response shape at runtime */\n validator?: ResponseValidator<T>;\n}\n\n/**\n * Creates an authentication error from response data\n */\nexport function createAuthError(\n data: { code?: string; message?: string; details?: Record<string, unknown> },\n fallbackMessage: string\n): AuthError {\n return {\n code: (data.code as AuthErrorCode) || 'SERVER_ERROR',\n message: data.message || fallbackMessage,\n details: data.details,\n };\n}\n\n/**\n * Creates a network error\n */\nexport function createNetworkError(): AuthError {\n return {\n code: 'NETWORK_ERROR',\n message: 'Unable to connect to server',\n };\n}\n\n/**\n * Fetch with timeout support\n */\nasync function fetchWithTimeout(\n url: string,\n options: RequestInit,\n timeoutMs: number\n): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n });\n return response;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Determines if an error is retryable\n * UI-8 FIX: AbortError (timeout) should NOT be retried - server may have processed request\n */\nfunction isRetryableError(error: unknown): boolean {\n if (error instanceof Error) {\n if ((error as { retryable?: boolean }).retryable) return true;\n // UI-8: AbortError from timeout should NOT be retried\n // Server may have processed the request (just responded slowly)\n // Retrying could cause duplicate operations\n if (error.name === 'AbortError') return false;\n // Network errors (connection failed) are safe to retry\n if (error.message.includes('fetch')) return true;\n }\n return false;\n}\n\n/**\n * Delays execution for the specified duration\n */\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * API client for making authenticated requests with timeout and retry support\n */\nexport class ApiClient {\n private baseUrl: string;\n private timeoutMs: number;\n private retryAttempts: number;\n private getAccessToken?: () => string | null;\n\n constructor(config: ApiClientConfig) {\n this.baseUrl = config.baseUrl;\n this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.retryAttempts = config.retryAttempts ?? DEFAULT_RETRY_ATTEMPTS;\n this.getAccessToken = config.getAccessToken;\n }\n\n /**\n * Make an API request with timeout and optional retry\n */\n async request<T>(options: RequestOptions<T>): Promise<T> {\n const { method, path, body, credentials = 'include', skipRetry = false, validator } = options;\n const url = `${this.baseUrl}${path}`;\n // S-10: DELETE excluded — retrying mid-flight DELETE failures risks double-deletion\n const isIdempotent = method === 'GET' || method === 'HEAD' || method === 'PUT';\n const maxAttempts = skipRetry || !isIdempotent ? 1 : this.retryAttempts + 1;\n\n // Build headers with CSRF token if available\n const headers: Record<string, string> = {};\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n const accessToken = this.getAccessToken?.();\n if (accessToken) {\n headers.Authorization = `Bearer ${accessToken}`;\n }\n const csrfToken = getCsrfToken();\n if (csrfToken) {\n headers['X-CSRF-Token'] = csrfToken;\n }\n\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const response = await fetchWithTimeout(\n url,\n {\n method,\n headers,\n credentials,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n },\n this.timeoutMs\n );\n\n const contentType = response.headers.get('content-type') || '';\n let data: { code?: string; message?: string; details?: Record<string, unknown> } = {};\n\n if (contentType.includes('application/json')) {\n if (response.status !== 204) {\n try {\n data = (await response.json()) as {\n code?: string;\n message?: string;\n details?: Record<string, unknown>;\n };\n } catch (e) {\n // UI-JSON: Include actual parse error for easier debugging\n const parseError = e instanceof Error ? e.message : 'parse failed';\n throw new Error(`Invalid JSON response: ${parseError}`);\n }\n }\n } else {\n // U-01: Handle non-JSON responses with informative error messages\n // Proxies/load balancers may return HTML error pages (502, 503)\n const text = await response.text();\n if (text) {\n // Truncate very long responses (e.g., HTML pages) for readability\n const truncated = text.length > 200 ? text.slice(0, 200) + '...' : text;\n const isHtml = contentType.includes('text/html') || text.trimStart().startsWith('<');\n data = {\n message: isHtml\n ? `Unexpected HTML response (${response.status}). The server may be unavailable.`\n : truncated,\n };\n }\n }\n\n if (!response.ok) {\n // Don't retry 4xx errors (client errors)\n if (response.status >= 400 && response.status < 500) {\n throw { isApiError: true, data, status: response.status };\n }\n // Retry 5xx errors\n const err = new Error(`Server error: ${response.status}`);\n (err as { retryable?: boolean }).retryable = true;\n throw err;\n }\n\n // M-02: Apply response validation if provided\n if (validator) {\n try {\n return validator(data);\n } catch (validationError) {\n throw new Error(\n `Response validation failed: ${validationError instanceof Error ? validationError.message : 'Invalid response shape'}`\n );\n }\n }\n\n return data as T;\n } catch (error) {\n lastError = error;\n\n // Don't retry API errors (4xx responses)\n if (typeof error === 'object' && error !== null && 'isApiError' in error) {\n throw error;\n }\n\n // Check if we should retry\n if (attempt < maxAttempts && isRetryableError(error)) {\n // Exponential backoff: 100ms, 200ms, 400ms...\n await delay(100 * Math.pow(2, attempt - 1));\n continue;\n }\n\n throw error;\n }\n }\n\n throw lastError;\n }\n\n /**\n * POST request helper\n */\n async post<T>(path: string, body: unknown, options?: Partial<RequestOptions<T>>): Promise<T> {\n return this.request<T>({ method: 'POST', path, body, ...options });\n }\n\n /**\n * GET request helper\n */\n async get<T>(path: string, options?: Partial<RequestOptions<T>>): Promise<T> {\n return this.request<T>({ method: 'GET', path, ...options });\n }\n\n /**\n * PATCH request helper\n */\n async patch<T>(path: string, body: unknown, options?: Partial<RequestOptions<T>>): Promise<T> {\n return this.request<T>({ method: 'PATCH', path, body, ...options });\n }\n\n /**\n * DELETE request helper\n */\n async delete<T>(path: string, options?: Partial<RequestOptions<T>>): Promise<T> {\n return this.request<T>({ method: 'DELETE', path, ...options });\n }\n}\n\ninterface ApiErrorResponse {\n isApiError: true;\n data: { code?: string; message?: string; details?: Record<string, unknown> };\n status: number;\n}\n\nfunction isApiErrorResponse(err: unknown): err is ApiErrorResponse {\n return typeof err === 'object' && err !== null && 'isApiError' in err;\n}\n\nfunction isAuthError(err: unknown): err is AuthError {\n return typeof err === 'object' && err !== null && 'code' in err && 'message' in err;\n}\n\n/**\n * M-02: Helper to create a basic object shape validator.\n * Checks that required keys exist and are of expected types.\n * @example\n * const validateUser = createValidator<User>({\n * id: 'string',\n * email: 'string',\n * role: 'string',\n * });\n */\nexport function createValidator<T>(\n shape: Record<keyof T & string, 'string' | 'number' | 'boolean' | 'object'>\n): ResponseValidator<T> {\n return (data: unknown): T => {\n if (typeof data !== 'object' || data === null) {\n throw new Error('Expected object response');\n }\n const obj = data as Record<string, unknown>;\n for (const [key, expectedType] of Object.entries(shape)) {\n if (!(key in obj)) {\n throw new Error(`Missing required field: ${key}`);\n }\n const actualType = typeof obj[key];\n if (actualType !== expectedType) {\n throw new Error(`Invalid type for ${key}: expected ${expectedType}, got ${actualType}`);\n }\n }\n return data as T;\n };\n}\n\n/**\n * Converts API errors to AuthError format\n */\nexport function handleApiError(err: unknown, fallbackMessage: string): AuthError {\n // Already an AuthError\n if (isAuthError(err)) {\n return err;\n }\n\n // API error response (4xx/5xx)\n if (isApiErrorResponse(err)) {\n return createAuthError(err.data, fallbackMessage);\n }\n\n if (err instanceof Error) {\n if (err.name === 'AbortError') {\n return {\n code: 'NETWORK_ERROR',\n message: 'Request timed out',\n };\n }\n if (\n err.message.startsWith('Server error:') ||\n err.message.startsWith('Invalid JSON response')\n ) {\n return {\n code: 'SERVER_ERROR',\n message: fallbackMessage,\n };\n }\n }\n\n // Network or timeout error\n return createNetworkError();\n}\n","import { useContext } from 'react';\nimport {\n AuthStateContext,\n AuthUIContext,\n CedrosLoginContext,\n type AuthStateContextValue,\n type AuthUIContextValue,\n type CedrosLoginContextValue,\n} from './CedrosLoginContext';\n\n/**\n * Hook to access the full Cedros Login context.\n * Must be used within a CedrosLoginProvider.\n *\n * For better performance, prefer `useAuthState()` or `useAuthUI()` when you\n * only need a subset of the context. This hook re-renders on any change.\n */\nexport function useCedrosLogin(): CedrosLoginContextValue {\n const context = useContext(CedrosLoginContext);\n if (!context) {\n throw new Error('useCedrosLogin must be used within a CedrosLoginProvider');\n }\n return context;\n}\n\n/**\n * Optional version of useCedrosLogin that returns null instead of throwing\n * when used outside a CedrosLoginProvider. Useful for components that need\n * to work in both provider and non-provider contexts (e.g., Storybook demos).\n */\nexport function useCedrosLoginOptional(): CedrosLoginContextValue | null {\n return useContext(CedrosLoginContext);\n}\n\n/**\n * Hook to access only auth state (user, authState, config, logout, refreshUser).\n *\n * Does NOT re-render on UI state changes (modal, error). Use this in components\n * that only need to know about authentication status.\n */\nexport function useAuthState(): AuthStateContextValue {\n const context = useContext(AuthStateContext);\n if (!context) {\n throw new Error('useAuthState must be used within a CedrosLoginProvider');\n }\n return context;\n}\n\n/**\n * Hook to access only UI state (isModalOpen, error, openModal, closeModal).\n *\n * Does NOT re-render on auth state changes (login, token refresh). Use this\n * in components that only control the login modal or display errors.\n */\nexport function useAuthUI(): AuthUIContextValue {\n const context = useContext(AuthUIContext);\n if (!context) {\n throw new Error('useAuthUI must be used within a CedrosLoginProvider');\n }\n return context;\n}\n"],"names":["MIN_CSRF_TOKEN_LENGTH","getCsrfToken","metaTag","content","cookies","cookie","name","rest","value","nameLower","decoded","DEFAULT_TIMEOUT_MS","DEFAULT_RETRY_ATTEMPTS","createAuthError","data","fallbackMessage","createNetworkError","fetchWithTimeout","url","options","timeoutMs","controller","timeoutId","isRetryableError","error","delay","ms","resolve","ApiClient","config","method","path","body","credentials","skipRetry","validator","maxAttempts","headers","accessToken","csrfToken","lastError","attempt","response","contentType","e","parseError","text","truncated","err","validationError","isApiErrorResponse","isAuthError","handleApiError","useCedrosLogin","context","useContext","CedrosLoginContext","useCedrosLoginOptional","useAuthState","AuthStateContext","useAuthUI","AuthUIContext"],"mappings":";;AAEA,MAAMA,IAAwB;AAEvB,SAASC,IAA8B;AAC5C,MAAI,OAAO,WAAa,IAAa,QAAO;AAE5C,QAAMC,IAAU,SAAS,cAAc,yBAAyB;AAChE,MAAIA,GAAS;AACX,UAAMC,IAAUD,EAAQ,aAAa,SAAS;AAE9C,QAAIC,KAAWA,EAAQ,UAAUH;AAC/B,aAAOG;AAAA,EAEX;AAIA,QAAMC,IAAU,SAAS,OAAO,MAAM,GAAG;AACzC,aAAWC,KAAUD,GAAS;AAC5B,UAAM,CAACE,GAAM,GAAGC,CAAI,IAAIF,EAAO,KAAA,EAAO,MAAM,GAAG,GACzCG,IAAQD,EAAK,KAAK,GAAG,GACrBE,IAAYH,EAAK,YAAA;AACvB,QAAIG,MAAc,gBAAgBA,MAAc;AAC9C,UAAI;AACF,cAAMC,IAAU,mBAAmBF,EAAM,KAAA,CAAM;AAE/C,YAAIE,EAAQ,UAAUV;AACpB,iBAAOU;AAAA,MAEX,QAAQ;AAEN;AAAA,MACF;AAAA,EAEJ;AAEA,SAAO;AACT;ACnCA,MAAMC,IAAqB,KACrBC,IAAyB;AA4BxB,SAASC,EACdC,GACAC,GACW;AACX,SAAO;AAAA,IACL,MAAOD,EAAK,QAA0B;AAAA,IACtC,SAASA,EAAK,WAAWC;AAAA,IACzB,SAASD,EAAK;AAAA,EAAA;AAElB;AAKO,SAASE,IAAgC;AAC9C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,EAAA;AAEb;AAKA,eAAeC,EACbC,GACAC,GACAC,GACmB;AACnB,QAAMC,IAAa,IAAI,gBAAA,GACjBC,IAAY,WAAW,MAAMD,EAAW,MAAA,GAASD,CAAS;AAEhE,MAAI;AAKF,WAJiB,MAAM,MAAMF,GAAK;AAAA,MAChC,GAAGC;AAAA,MACH,QAAQE,EAAW;AAAA,IAAA,CACpB;AAAA,EAEH,UAAA;AACE,iBAAaC,CAAS;AAAA,EACxB;AACF;AAMA,SAASC,EAAiBC,GAAyB;AACjD,MAAIA,aAAiB,OAAO;AAC1B,QAAKA,EAAkC,UAAW,QAAO;AAIzD,QAAIA,EAAM,SAAS,aAAc,QAAO;AAExC,QAAIA,EAAM,QAAQ,SAAS,OAAO,EAAG,QAAO;AAAA,EAC9C;AACA,SAAO;AACT;AAKA,SAASC,EAAMC,GAA2B;AACxC,SAAO,IAAI,QAAQ,CAACC,MAAY,WAAWA,GAASD,CAAE,CAAC;AACzD;AAKO,MAAME,EAAU;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAYC,GAAyB;AACnC,SAAK,UAAUA,EAAO,SACtB,KAAK,YAAYA,EAAO,aAAalB,GACrC,KAAK,gBAAgBkB,EAAO,iBAAiBjB,GAC7C,KAAK,iBAAiBiB,EAAO;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAWV,GAAwC;AACvD,UAAM,EAAE,QAAAW,GAAQ,MAAAC,GAAM,MAAAC,GAAM,aAAAC,IAAc,WAAW,WAAAC,IAAY,IAAO,WAAAC,EAAA,IAAchB,GAChFD,IAAM,GAAG,KAAK,OAAO,GAAGa,CAAI,IAG5BK,IAAcF,KAAa,EADZJ,MAAW,SAASA,MAAW,UAAUA,MAAW,SACxB,IAAI,KAAK,gBAAgB,GAGpEO,IAAkC,CAAA;AACxC,IAAIL,MAAS,WACXK,EAAQ,cAAc,IAAI;AAE5B,UAAMC,IAAc,KAAK,iBAAA;AACzB,IAAIA,MACFD,EAAQ,gBAAgB,UAAUC,CAAW;AAE/C,UAAMC,IAAYtC,EAAA;AAClB,IAAIsC,MACFF,EAAQ,cAAc,IAAIE;AAG5B,QAAIC;AAEJ,aAASC,IAAU,GAAGA,KAAWL,GAAaK;AAC5C,UAAI;AACF,cAAMC,IAAW,MAAMzB;AAAA,UACrBC;AAAA,UACA;AAAA,YACE,QAAAY;AAAA,YACA,SAAAO;AAAA,YACA,aAAAJ;AAAA,YACA,MAAMD,MAAS,SAAY,KAAK,UAAUA,CAAI,IAAI;AAAA,UAAA;AAAA,UAEpD,KAAK;AAAA,QAAA,GAGDW,IAAcD,EAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,YAAI5B,IAA+E,CAAA;AAEnF,YAAI6B,EAAY,SAAS,kBAAkB;AACzC,cAAID,EAAS,WAAW;AACtB,gBAAI;AACF,cAAA5B,IAAQ,MAAM4B,EAAS,KAAA;AAAA,YAKzB,SAASE,GAAG;AAEV,oBAAMC,IAAaD,aAAa,QAAQA,EAAE,UAAU;AACpD,oBAAM,IAAI,MAAM,0BAA0BC,CAAU,EAAE;AAAA,YACxD;AAAA,eAEG;AAGL,gBAAMC,IAAO,MAAMJ,EAAS,KAAA;AAC5B,cAAII,GAAM;AAER,kBAAMC,IAAYD,EAAK,SAAS,MAAMA,EAAK,MAAM,GAAG,GAAG,IAAI,QAAQA;AAEnE,YAAAhC,IAAO;AAAA,cACL,SAFa6B,EAAY,SAAS,WAAW,KAAKG,EAAK,UAAA,EAAY,WAAW,GAAG,IAG7E,6BAA6BJ,EAAS,MAAM,sCAC5CK;AAAA,YAAA;AAAA,UAER;AAAA,QACF;AAEA,YAAI,CAACL,EAAS,IAAI;AAEhB,cAAIA,EAAS,UAAU,OAAOA,EAAS,SAAS;AAC9C,kBAAM,EAAE,YAAY,IAAM,MAAA5B,GAAM,QAAQ4B,EAAS,OAAA;AAGnD,gBAAMM,IAAM,IAAI,MAAM,iBAAiBN,EAAS,MAAM,EAAE;AACvD,gBAAAM,EAAgC,YAAY,IACvCA;AAAA,QACR;AAGA,YAAIb;AACF,cAAI;AACF,mBAAOA,EAAUrB,CAAI;AAAA,UACvB,SAASmC,GAAiB;AACxB,kBAAM,IAAI;AAAA,cACR,+BAA+BA,aAA2B,QAAQA,EAAgB,UAAU,wBAAwB;AAAA,YAAA;AAAA,UAExH;AAGF,eAAOnC;AAAA,MACT,SAASU,GAAO;AAId,YAHAgB,IAAYhB,GAGR,OAAOA,KAAU,YAAYA,MAAU,QAAQ,gBAAgBA;AACjE,gBAAMA;AAIR,YAAIiB,IAAUL,KAAeb,EAAiBC,CAAK,GAAG;AAEpD,gBAAMC,EAAM,MAAM,KAAK,IAAI,GAAGgB,IAAU,CAAC,CAAC;AAC1C;AAAA,QACF;AAEA,cAAMjB;AAAA,MACR;AAGF,UAAMgB;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KAAQT,GAAcC,GAAeb,GAAkD;AAC3F,WAAO,KAAK,QAAW,EAAE,QAAQ,QAAQ,MAAAY,GAAM,MAAAC,GAAM,GAAGb,GAAS;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAOY,GAAcZ,GAAkD;AAC3E,WAAO,KAAK,QAAW,EAAE,QAAQ,OAAO,MAAAY,GAAM,GAAGZ,GAAS;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAASY,GAAcC,GAAeb,GAAkD;AAC5F,WAAO,KAAK,QAAW,EAAE,QAAQ,SAAS,MAAAY,GAAM,MAAAC,GAAM,GAAGb,GAAS;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAUY,GAAcZ,GAAkD;AAC9E,WAAO,KAAK,QAAW,EAAE,QAAQ,UAAU,MAAAY,GAAM,GAAGZ,GAAS;AAAA,EAC/D;AACF;AAQA,SAAS+B,EAAmBF,GAAuC;AACjE,SAAO,OAAOA,KAAQ,YAAYA,MAAQ,QAAQ,gBAAgBA;AACpE;AAEA,SAASG,EAAYH,GAAgC;AACnD,SAAO,OAAOA,KAAQ,YAAYA,MAAQ,QAAQ,UAAUA,KAAO,aAAaA;AAClF;AAoCO,SAASI,EAAeJ,GAAcjC,GAAoC;AAE/E,MAAIoC,EAAYH,CAAG;AACjB,WAAOA;AAIT,MAAIE,EAAmBF,CAAG;AACxB,WAAOnC,EAAgBmC,EAAI,MAAMjC,CAAe;AAGlD,MAAIiC,aAAe,OAAO;AACxB,QAAIA,EAAI,SAAS;AACf,aAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,MAAA;AAGb,QACEA,EAAI,QAAQ,WAAW,eAAe,KACtCA,EAAI,QAAQ,WAAW,uBAAuB;AAE9C,aAAO;AAAA,QACL,MAAM;AAAA,QACN,SAASjC;AAAA,MAAA;AAAA,EAGf;AAGA,SAAOC,EAAA;AACT;ACnUO,SAASqC,IAA0C;AACxD,QAAMC,IAAUC,EAAWC,CAAkB;AAC7C,MAAI,CAACF;AACH,UAAM,IAAI,MAAM,0DAA0D;AAE5E,SAAOA;AACT;AAOO,SAASG,IAAyD;AACvE,SAAOF,EAAWC,CAAkB;AACtC;AAQO,SAASE,IAAsC;AACpD,QAAMJ,IAAUC,EAAWI,CAAgB;AAC3C,MAAI,CAACL;AACH,UAAM,IAAI,MAAM,wDAAwD;AAE1E,SAAOA;AACT;AAQO,SAASM,IAAgC;AAC9C,QAAMN,IAAUC,EAAWM,CAAa;AACxC,MAAI,CAACP;AACH,UAAM,IAAI,MAAM,qDAAqD;AAEvE,SAAOA;AACT;"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { useState as f, useMemo as G, useRef as C, useEffect as S, useCallback as u } from "react";
|
|
2
|
+
import { A as K, h as g, u as x } from "./useCedrosLogin-_94MmGGq.js";
|
|
3
|
+
class M {
|
|
4
|
+
client;
|
|
5
|
+
constructor(e, t, h, l) {
|
|
6
|
+
this.client = new K({ baseUrl: e, timeoutMs: t, retryAttempts: h, getAccessToken: l });
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* List all organizations the current user belongs to
|
|
10
|
+
*/
|
|
11
|
+
async listOrgs() {
|
|
12
|
+
try {
|
|
13
|
+
return (await this.client.get("/orgs")).orgs.map((t) => ({
|
|
14
|
+
...t,
|
|
15
|
+
membership: {
|
|
16
|
+
orgId: t.id,
|
|
17
|
+
role: t.role
|
|
18
|
+
}
|
|
19
|
+
}));
|
|
20
|
+
} catch (e) {
|
|
21
|
+
throw g(e, "Failed to list organizations");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get a single organization by ID
|
|
26
|
+
*/
|
|
27
|
+
async getOrg(e) {
|
|
28
|
+
try {
|
|
29
|
+
return await this.client.get(`/orgs/${e}`);
|
|
30
|
+
} catch (t) {
|
|
31
|
+
throw g(t, "Failed to get organization");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create a new organization
|
|
36
|
+
*/
|
|
37
|
+
async createOrg(e) {
|
|
38
|
+
try {
|
|
39
|
+
return await this.client.post("/orgs", e);
|
|
40
|
+
} catch (t) {
|
|
41
|
+
throw g(t, "Failed to create organization");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Update an organization
|
|
46
|
+
*/
|
|
47
|
+
async updateOrg(e, t) {
|
|
48
|
+
try {
|
|
49
|
+
return await this.client.patch(`/orgs/${e}`, t);
|
|
50
|
+
} catch (h) {
|
|
51
|
+
throw g(h, "Failed to update organization");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Delete an organization
|
|
56
|
+
*/
|
|
57
|
+
async deleteOrg(e) {
|
|
58
|
+
try {
|
|
59
|
+
await this.client.delete(`/orgs/${e}`);
|
|
60
|
+
} catch (t) {
|
|
61
|
+
throw g(t, "Failed to delete organization");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Check authorization for an action
|
|
66
|
+
*/
|
|
67
|
+
async authorize(e) {
|
|
68
|
+
try {
|
|
69
|
+
return await this.client.post("/authorize", e);
|
|
70
|
+
} catch (t) {
|
|
71
|
+
throw g(t, "Failed to check authorization");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get current user's permissions in an organization
|
|
76
|
+
*/
|
|
77
|
+
async getPermissions(e) {
|
|
78
|
+
try {
|
|
79
|
+
return await this.client.post("/permissions", { orgId: e });
|
|
80
|
+
} catch (t) {
|
|
81
|
+
throw g(t, "Failed to get permissions");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const v = "cedros_active_org";
|
|
86
|
+
function V(a) {
|
|
87
|
+
try {
|
|
88
|
+
return localStorage.getItem(a);
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function P(a, e) {
|
|
94
|
+
try {
|
|
95
|
+
localStorage.setItem(a, e);
|
|
96
|
+
} catch {
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function j() {
|
|
100
|
+
const { config: a, user: e, authState: t, _internal: h } = x(), l = typeof window < "u" && !!window.localStorage, [m, E] = f([]), [_, w] = f(null), [A, y] = f([]), [T, O] = f(null), [k, c] = f(t === "authenticated"), [L, i] = f(null), z = G(
|
|
101
|
+
() => new M(
|
|
102
|
+
a.serverUrl,
|
|
103
|
+
a.requestTimeout,
|
|
104
|
+
a.retryAttempts,
|
|
105
|
+
h?.getAccessToken
|
|
106
|
+
),
|
|
107
|
+
[a.serverUrl, a.requestTimeout, a.retryAttempts, h]
|
|
108
|
+
), d = C(z);
|
|
109
|
+
S(() => {
|
|
110
|
+
d.current = z;
|
|
111
|
+
}, [z]);
|
|
112
|
+
const p = u(async (r) => {
|
|
113
|
+
try {
|
|
114
|
+
const s = await d.current.getPermissions(r);
|
|
115
|
+
y(s.permissions), O(s.role);
|
|
116
|
+
} catch {
|
|
117
|
+
y([]), O(null);
|
|
118
|
+
}
|
|
119
|
+
}, []), I = C(async () => {
|
|
120
|
+
}), o = u(async () => {
|
|
121
|
+
if (t !== "authenticated" || !e) {
|
|
122
|
+
E([]), w(null), y([]), O(null);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
c(!0), i(null);
|
|
126
|
+
try {
|
|
127
|
+
const r = await d.current.listOrgs();
|
|
128
|
+
E(r);
|
|
129
|
+
const s = l ? V(v) : null;
|
|
130
|
+
let n = r.find((R) => R.id === s);
|
|
131
|
+
!n && r.length > 0 && (n = r.find((R) => R.isPersonal) || r[0]), n ? (w(n), l && P(v, n.id), await p(n.id)) : (w(null), y([]), O(null));
|
|
132
|
+
} catch (r) {
|
|
133
|
+
i(r);
|
|
134
|
+
} finally {
|
|
135
|
+
c(!1);
|
|
136
|
+
}
|
|
137
|
+
}, [t, e, p, l]);
|
|
138
|
+
S(() => {
|
|
139
|
+
I.current = o;
|
|
140
|
+
}, [o]);
|
|
141
|
+
const F = C(!1);
|
|
142
|
+
S(() => {
|
|
143
|
+
t === "authenticated" && !F.current ? (F.current = !0, I.current()) : t !== "authenticated" && (F.current = !1);
|
|
144
|
+
}, [t]);
|
|
145
|
+
const N = u(
|
|
146
|
+
async (r) => {
|
|
147
|
+
const s = m.find((n) => n.id === r);
|
|
148
|
+
if (!s) {
|
|
149
|
+
i({ code: "UNKNOWN_ERROR", message: "Organization not found" });
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
w(s), l && P(v, r), await p(r);
|
|
153
|
+
},
|
|
154
|
+
[m, p, l]
|
|
155
|
+
), U = u(
|
|
156
|
+
async (r) => {
|
|
157
|
+
c(!0), i(null);
|
|
158
|
+
try {
|
|
159
|
+
const s = await d.current.createOrg(r);
|
|
160
|
+
return await o(), s;
|
|
161
|
+
} catch (s) {
|
|
162
|
+
throw i(s), s;
|
|
163
|
+
} finally {
|
|
164
|
+
c(!1);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
[o]
|
|
168
|
+
), $ = u(
|
|
169
|
+
async (r, s) => {
|
|
170
|
+
c(!0), i(null);
|
|
171
|
+
try {
|
|
172
|
+
const n = await d.current.updateOrg(r, s);
|
|
173
|
+
return await o(), n;
|
|
174
|
+
} catch (n) {
|
|
175
|
+
throw i(n), n;
|
|
176
|
+
} finally {
|
|
177
|
+
c(!1);
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
[o]
|
|
181
|
+
), b = u(
|
|
182
|
+
async (r) => {
|
|
183
|
+
c(!0), i(null);
|
|
184
|
+
try {
|
|
185
|
+
await d.current.deleteOrg(r), await o();
|
|
186
|
+
} catch (s) {
|
|
187
|
+
throw i(s), s;
|
|
188
|
+
} finally {
|
|
189
|
+
c(!1);
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
[o]
|
|
193
|
+
), q = u(
|
|
194
|
+
(r) => A.includes(r),
|
|
195
|
+
[A]
|
|
196
|
+
);
|
|
197
|
+
return {
|
|
198
|
+
orgs: m,
|
|
199
|
+
activeOrg: _,
|
|
200
|
+
permissions: A,
|
|
201
|
+
role: T,
|
|
202
|
+
isLoading: k,
|
|
203
|
+
error: L,
|
|
204
|
+
fetchOrgs: o,
|
|
205
|
+
switchOrg: N,
|
|
206
|
+
createOrg: U,
|
|
207
|
+
updateOrg: $,
|
|
208
|
+
deleteOrg: b,
|
|
209
|
+
hasPermission: q
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
export {
|
|
213
|
+
M as O,
|
|
214
|
+
j as u
|
|
215
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOrgs-C3pzMA9h.js","sources":["../src/utils/orgApi.ts","../src/hooks/useOrgs.ts"],"sourcesContent":["import type {\n Organization,\n OrgWithMembership,\n CreateOrgRequest,\n UpdateOrgRequest,\n ListOrgsResponse,\n AuthorizeRequest,\n AuthorizeResponse,\n PermissionsResponse,\n} from '../types';\nimport { ApiClient, handleApiError } from './apiClient';\n\n/**\n * API client for organization operations\n */\nexport class OrgApiClient {\n private client: ApiClient;\n\n constructor(\n baseUrl: string,\n timeoutMs?: number,\n retryAttempts?: number,\n getAccessToken?: () => string | null\n ) {\n this.client = new ApiClient({ baseUrl, timeoutMs, retryAttempts, getAccessToken });\n }\n\n /**\n * List all organizations the current user belongs to\n */\n async listOrgs(): Promise<OrgWithMembership[]> {\n try {\n const response = await this.client.get<ListOrgsResponse>('/orgs');\n return response.orgs.map((org) => ({\n ...org,\n membership: {\n orgId: org.id,\n role: org.role,\n },\n }));\n } catch (error) {\n throw handleApiError(error, 'Failed to list organizations');\n }\n }\n\n /**\n * Get a single organization by ID\n */\n async getOrg(orgId: string): Promise<Organization> {\n try {\n return await this.client.get<Organization>(`/orgs/${orgId}`);\n } catch (error) {\n throw handleApiError(error, 'Failed to get organization');\n }\n }\n\n /**\n * Create a new organization\n */\n async createOrg(data: CreateOrgRequest): Promise<Organization> {\n try {\n return await this.client.post<Organization>('/orgs', data);\n } catch (error) {\n throw handleApiError(error, 'Failed to create organization');\n }\n }\n\n /**\n * Update an organization\n */\n async updateOrg(orgId: string, data: UpdateOrgRequest): Promise<Organization> {\n try {\n return await this.client.patch<Organization>(`/orgs/${orgId}`, data);\n } catch (error) {\n throw handleApiError(error, 'Failed to update organization');\n }\n }\n\n /**\n * Delete an organization\n */\n async deleteOrg(orgId: string): Promise<void> {\n try {\n await this.client.delete<void>(`/orgs/${orgId}`);\n } catch (error) {\n throw handleApiError(error, 'Failed to delete organization');\n }\n }\n\n /**\n * Check authorization for an action\n */\n async authorize(data: AuthorizeRequest): Promise<AuthorizeResponse> {\n try {\n return await this.client.post<AuthorizeResponse>('/authorize', data);\n } catch (error) {\n throw handleApiError(error, 'Failed to check authorization');\n }\n }\n\n /**\n * Get current user's permissions in an organization\n */\n async getPermissions(orgId: string): Promise<PermissionsResponse> {\n try {\n return await this.client.post<PermissionsResponse>('/permissions', { orgId });\n } catch (error) {\n throw handleApiError(error, 'Failed to get permissions');\n }\n }\n}\n","import { useState, useCallback, useMemo, useRef, useEffect } from 'react';\nimport type {\n OrgWithMembership,\n Organization,\n CreateOrgRequest,\n UpdateOrgRequest,\n Permission,\n OrgRole,\n AuthError,\n} from '../types';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { OrgApiClient } from '../utils/orgApi';\n\nexport interface UseOrgsReturn {\n /** All organizations the user belongs to */\n orgs: OrgWithMembership[];\n /** Currently active organization */\n activeOrg: OrgWithMembership | null;\n /** User's permissions in the active org */\n permissions: Permission[];\n /** User's role in the active org */\n role: OrgRole | null;\n /** Loading state */\n isLoading: boolean;\n /** Error state */\n error: AuthError | null;\n /** Fetch/refresh organizations list */\n fetchOrgs: () => Promise<void>;\n /** Switch to a different organization */\n switchOrg: (orgId: string) => Promise<void>;\n /** Create a new organization */\n createOrg: (data: CreateOrgRequest) => Promise<Organization>;\n /** Update an organization */\n updateOrg: (orgId: string, data: UpdateOrgRequest) => Promise<Organization>;\n /** Delete an organization */\n deleteOrg: (orgId: string) => Promise<void>;\n /** Check if user has a specific permission */\n hasPermission: (permission: Permission) => boolean;\n}\n\nconst ACTIVE_ORG_KEY = 'cedros_active_org';\n\n// P-06: Safe localStorage helpers to handle private browsing and quota errors\nfunction safeGetItem(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction safeSetItem(key: string, value: string): void {\n try {\n localStorage.setItem(key, value);\n } catch {\n // Ignore - private browsing or quota exceeded\n }\n}\n\n/**\n * Hook for managing organizations, memberships, and permissions.\n *\n * @example\n * ```tsx\n * function OrgSelector() {\n * const { orgs, activeOrg, switchOrg, hasPermission } = useOrgs();\n *\n * return (\n * <select\n * value={activeOrg?.id}\n * onChange={(e) => switchOrg(e.target.value)}\n * >\n * {orgs.map(org => (\n * <option key={org.id} value={org.id}>{org.name}</option>\n * ))}\n * </select>\n * );\n * }\n * ```\n */\nexport function useOrgs(): UseOrgsReturn {\n const { config, user, authState, _internal } = useCedrosLogin();\n const hasStorage = typeof window !== 'undefined' && !!window.localStorage;\n\n const [orgs, setOrgs] = useState<OrgWithMembership[]>([]);\n const [activeOrg, setActiveOrg] = useState<OrgWithMembership | null>(null);\n const [permissions, setPermissions] = useState<Permission[]>([]);\n const [role, setRole] = useState<OrgRole | null>(null);\n const [isLoading, setIsLoading] = useState(authState === 'authenticated');\n const [error, setError] = useState<AuthError | null>(null);\n\n // M-03: Memoize API client and use ref to prevent callback dependency cascades\n const apiClient = useMemo(\n () =>\n new OrgApiClient(\n config.serverUrl,\n config.requestTimeout,\n config.retryAttempts,\n _internal?.getAccessToken\n ),\n [config.serverUrl, config.requestTimeout, config.retryAttempts, _internal]\n );\n\n // M-03: Store apiClient in ref to stabilize callback dependencies\n const apiClientRef = useRef(apiClient);\n useEffect(() => {\n apiClientRef.current = apiClient;\n }, [apiClient]);\n\n // M-03: Use ref in callback to break dependency chain\n const loadPermissions = useCallback(async (orgId: string) => {\n try {\n const response = await apiClientRef.current.getPermissions(orgId);\n setPermissions(response.permissions);\n setRole(response.role);\n } catch {\n // Permissions loading failure is non-fatal, just clear them\n setPermissions([]);\n setRole(null);\n }\n }, []);\n\n // Ref to latest fetchOrgs for stable auto-fetch effect\n const fetchOrgsRef = useRef<() => Promise<void>>(async () => {});\n\n // M-03: Use ref to break apiClient dependency chain\n const fetchOrgs = useCallback(async () => {\n if (authState !== 'authenticated' || !user) {\n setOrgs([]);\n setActiveOrg(null);\n setPermissions([]);\n setRole(null);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const fetchedOrgs = await apiClientRef.current.listOrgs();\n setOrgs(fetchedOrgs);\n\n // P-06: Restore active org from localStorage with safe access\n const savedOrgId = hasStorage ? safeGetItem(ACTIVE_ORG_KEY) : null;\n let selectedOrg = fetchedOrgs.find((org) => org.id === savedOrgId);\n\n if (!selectedOrg && fetchedOrgs.length > 0) {\n // Default to personal org or first org\n selectedOrg = fetchedOrgs.find((org) => org.isPersonal) || fetchedOrgs[0];\n }\n\n if (selectedOrg) {\n setActiveOrg(selectedOrg);\n if (hasStorage) {\n safeSetItem(ACTIVE_ORG_KEY, selectedOrg.id);\n }\n await loadPermissions(selectedOrg.id);\n } else {\n setActiveOrg(null);\n setPermissions([]);\n setRole(null);\n }\n } catch (err) {\n setError(err as AuthError);\n } finally {\n setIsLoading(false);\n }\n }, [authState, user, loadPermissions, hasStorage]);\n\n // Keep fetchOrgsRef current so auto-fetch always calls the latest version\n useEffect(() => {\n fetchOrgsRef.current = fetchOrgs;\n }, [fetchOrgs]);\n\n // Auto-fetch orgs when auth becomes ready.\n // Uses only `authState` (a string) as dep — immune to object-identity churn\n // from the AdminShell bridge recreating `user` objects.\n const hasAutoFetched = useRef(false);\n useEffect(() => {\n if (authState === 'authenticated' && !hasAutoFetched.current) {\n hasAutoFetched.current = true;\n fetchOrgsRef.current();\n } else if (authState !== 'authenticated') {\n hasAutoFetched.current = false;\n }\n }, [authState]);\n\n const switchOrg = useCallback(\n async (orgId: string) => {\n const org = orgs.find((o) => o.id === orgId);\n if (!org) {\n setError({ code: 'UNKNOWN_ERROR', message: 'Organization not found' });\n return;\n }\n\n setActiveOrg(org);\n if (hasStorage) {\n safeSetItem(ACTIVE_ORG_KEY, orgId);\n }\n await loadPermissions(orgId);\n },\n [orgs, loadPermissions, hasStorage]\n );\n\n // M-03: Use ref to break apiClient dependency chain\n const createOrg = useCallback(\n async (data: CreateOrgRequest): Promise<Organization> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const newOrg = await apiClientRef.current.createOrg(data);\n // Refresh orgs list to include the new org with membership\n await fetchOrgs();\n return newOrg;\n } catch (err) {\n setError(err as AuthError);\n throw err;\n } finally {\n setIsLoading(false);\n }\n },\n [fetchOrgs]\n );\n\n const updateOrg = useCallback(\n async (orgId: string, data: UpdateOrgRequest): Promise<Organization> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const updatedOrg = await apiClientRef.current.updateOrg(orgId, data);\n // Refresh orgs list to reflect changes\n await fetchOrgs();\n return updatedOrg;\n } catch (err) {\n setError(err as AuthError);\n throw err;\n } finally {\n setIsLoading(false);\n }\n },\n [fetchOrgs]\n );\n\n const deleteOrg = useCallback(\n async (orgId: string): Promise<void> => {\n setIsLoading(true);\n setError(null);\n\n try {\n await apiClientRef.current.deleteOrg(orgId);\n // Refresh orgs list\n await fetchOrgs();\n } catch (err) {\n setError(err as AuthError);\n throw err;\n } finally {\n setIsLoading(false);\n }\n },\n [fetchOrgs]\n );\n\n const hasPermission = useCallback(\n (permission: Permission): boolean => {\n return permissions.includes(permission);\n },\n [permissions]\n );\n\n return {\n orgs,\n activeOrg,\n permissions,\n role,\n isLoading,\n error,\n fetchOrgs,\n switchOrg,\n createOrg,\n updateOrg,\n deleteOrg,\n hasPermission,\n };\n}\n"],"names":["OrgApiClient","baseUrl","timeoutMs","retryAttempts","getAccessToken","ApiClient","org","error","handleApiError","orgId","data","ACTIVE_ORG_KEY","safeGetItem","key","safeSetItem","value","useOrgs","config","user","authState","_internal","useCedrosLogin","hasStorage","orgs","setOrgs","useState","activeOrg","setActiveOrg","permissions","setPermissions","role","setRole","isLoading","setIsLoading","setError","apiClient","useMemo","apiClientRef","useRef","useEffect","loadPermissions","useCallback","response","fetchOrgsRef","fetchOrgs","fetchedOrgs","savedOrgId","selectedOrg","err","hasAutoFetched","switchOrg","o","createOrg","newOrg","updateOrg","updatedOrg","deleteOrg","hasPermission","permission"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const s=require("react"),l=require("./useCedrosLogin-C9MrcZvh.cjs");class b{client;constructor(e,t,h,g){this.client=new l.ApiClient({baseUrl:e,timeoutMs:t,retryAttempts:h,getAccessToken:g})}async listOrgs(){try{return(await this.client.get("/orgs")).orgs.map(t=>({...t,membership:{orgId:t.id,role:t.role}}))}catch(e){throw l.handleApiError(e,"Failed to list organizations")}}async getOrg(e){try{return await this.client.get(`/orgs/${e}`)}catch(t){throw l.handleApiError(t,"Failed to get organization")}}async createOrg(e){try{return await this.client.post("/orgs",e)}catch(t){throw l.handleApiError(t,"Failed to create organization")}}async updateOrg(e,t){try{return await this.client.patch(`/orgs/${e}`,t)}catch(h){throw l.handleApiError(h,"Failed to update organization")}}async deleteOrg(e){try{await this.client.delete(`/orgs/${e}`)}catch(t){throw l.handleApiError(t,"Failed to delete organization")}}async authorize(e){try{return await this.client.post("/authorize",e)}catch(t){throw l.handleApiError(t,"Failed to check authorization")}}async getPermissions(e){try{return await this.client.post("/permissions",{orgId:e})}catch(t){throw l.handleApiError(t,"Failed to get permissions")}}}const S="cedros_active_org";function U(i){try{return localStorage.getItem(i)}catch{return null}}function z(i,e){try{localStorage.setItem(i,e)}catch{}}function $(){const{config:i,user:e,authState:t,_internal:h}=l.useCedrosLogin(),g=typeof window<"u"&&!!window.localStorage,[O,R]=s.useState([]),[F,f]=s.useState(null),[m,w]=s.useState([]),[v,y]=s.useState(null),[I,u]=s.useState(t==="authenticated"),[P,c]=s.useState(null),A=s.useMemo(()=>new b(i.serverUrl,i.requestTimeout,i.retryAttempts,h?.getAccessToken),[i.serverUrl,i.requestTimeout,i.retryAttempts,h]),d=s.useRef(A);s.useEffect(()=>{d.current=A},[A]);const p=s.useCallback(async r=>{try{const a=await d.current.getPermissions(r);w(a.permissions),y(a.role)}catch{w([]),y(null)}},[]),k=s.useRef(async()=>{}),o=s.useCallback(async()=>{if(t!=="authenticated"||!e){R([]),f(null),w([]),y(null);return}u(!0),c(null);try{const r=await d.current.listOrgs();R(r);const a=g?U(S):null;let n=r.find(E=>E.id===a);!n&&r.length>0&&(n=r.find(E=>E.isPersonal)||r[0]),n?(f(n),g&&z(S,n.id),await p(n.id)):(f(null),w([]),y(null))}catch(r){c(r)}finally{u(!1)}},[t,e,p,g]);s.useEffect(()=>{k.current=o},[o]);const C=s.useRef(!1);s.useEffect(()=>{t==="authenticated"&&!C.current?(C.current=!0,k.current()):t!=="authenticated"&&(C.current=!1)},[t]);const _=s.useCallback(async r=>{const a=O.find(n=>n.id===r);if(!a){c({code:"UNKNOWN_ERROR",message:"Organization not found"});return}f(a),g&&z(S,r),await p(r)},[O,p,g]),q=s.useCallback(async r=>{u(!0),c(null);try{const a=await d.current.createOrg(r);return await o(),a}catch(a){throw c(a),a}finally{u(!1)}},[o]),L=s.useCallback(async(r,a)=>{u(!0),c(null);try{const n=await d.current.updateOrg(r,a);return await o(),n}catch(n){throw c(n),n}finally{u(!1)}},[o]),T=s.useCallback(async r=>{u(!0),c(null);try{await d.current.deleteOrg(r),await o()}catch(a){throw c(a),a}finally{u(!1)}},[o]),N=s.useCallback(r=>m.includes(r),[m]);return{orgs:O,activeOrg:F,permissions:m,role:v,isLoading:I,error:P,fetchOrgs:o,switchOrg:_,createOrg:q,updateOrg:L,deleteOrg:T,hasPermission:N}}exports.OrgApiClient=b;exports.useOrgs=$;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOrgs-DDVRCaVi.cjs","sources":["../src/utils/orgApi.ts","../src/hooks/useOrgs.ts"],"sourcesContent":["import type {\n Organization,\n OrgWithMembership,\n CreateOrgRequest,\n UpdateOrgRequest,\n ListOrgsResponse,\n AuthorizeRequest,\n AuthorizeResponse,\n PermissionsResponse,\n} from '../types';\nimport { ApiClient, handleApiError } from './apiClient';\n\n/**\n * API client for organization operations\n */\nexport class OrgApiClient {\n private client: ApiClient;\n\n constructor(\n baseUrl: string,\n timeoutMs?: number,\n retryAttempts?: number,\n getAccessToken?: () => string | null\n ) {\n this.client = new ApiClient({ baseUrl, timeoutMs, retryAttempts, getAccessToken });\n }\n\n /**\n * List all organizations the current user belongs to\n */\n async listOrgs(): Promise<OrgWithMembership[]> {\n try {\n const response = await this.client.get<ListOrgsResponse>('/orgs');\n return response.orgs.map((org) => ({\n ...org,\n membership: {\n orgId: org.id,\n role: org.role,\n },\n }));\n } catch (error) {\n throw handleApiError(error, 'Failed to list organizations');\n }\n }\n\n /**\n * Get a single organization by ID\n */\n async getOrg(orgId: string): Promise<Organization> {\n try {\n return await this.client.get<Organization>(`/orgs/${orgId}`);\n } catch (error) {\n throw handleApiError(error, 'Failed to get organization');\n }\n }\n\n /**\n * Create a new organization\n */\n async createOrg(data: CreateOrgRequest): Promise<Organization> {\n try {\n return await this.client.post<Organization>('/orgs', data);\n } catch (error) {\n throw handleApiError(error, 'Failed to create organization');\n }\n }\n\n /**\n * Update an organization\n */\n async updateOrg(orgId: string, data: UpdateOrgRequest): Promise<Organization> {\n try {\n return await this.client.patch<Organization>(`/orgs/${orgId}`, data);\n } catch (error) {\n throw handleApiError(error, 'Failed to update organization');\n }\n }\n\n /**\n * Delete an organization\n */\n async deleteOrg(orgId: string): Promise<void> {\n try {\n await this.client.delete<void>(`/orgs/${orgId}`);\n } catch (error) {\n throw handleApiError(error, 'Failed to delete organization');\n }\n }\n\n /**\n * Check authorization for an action\n */\n async authorize(data: AuthorizeRequest): Promise<AuthorizeResponse> {\n try {\n return await this.client.post<AuthorizeResponse>('/authorize', data);\n } catch (error) {\n throw handleApiError(error, 'Failed to check authorization');\n }\n }\n\n /**\n * Get current user's permissions in an organization\n */\n async getPermissions(orgId: string): Promise<PermissionsResponse> {\n try {\n return await this.client.post<PermissionsResponse>('/permissions', { orgId });\n } catch (error) {\n throw handleApiError(error, 'Failed to get permissions');\n }\n }\n}\n","import { useState, useCallback, useMemo, useRef, useEffect } from 'react';\nimport type {\n OrgWithMembership,\n Organization,\n CreateOrgRequest,\n UpdateOrgRequest,\n Permission,\n OrgRole,\n AuthError,\n} from '../types';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { OrgApiClient } from '../utils/orgApi';\n\nexport interface UseOrgsReturn {\n /** All organizations the user belongs to */\n orgs: OrgWithMembership[];\n /** Currently active organization */\n activeOrg: OrgWithMembership | null;\n /** User's permissions in the active org */\n permissions: Permission[];\n /** User's role in the active org */\n role: OrgRole | null;\n /** Loading state */\n isLoading: boolean;\n /** Error state */\n error: AuthError | null;\n /** Fetch/refresh organizations list */\n fetchOrgs: () => Promise<void>;\n /** Switch to a different organization */\n switchOrg: (orgId: string) => Promise<void>;\n /** Create a new organization */\n createOrg: (data: CreateOrgRequest) => Promise<Organization>;\n /** Update an organization */\n updateOrg: (orgId: string, data: UpdateOrgRequest) => Promise<Organization>;\n /** Delete an organization */\n deleteOrg: (orgId: string) => Promise<void>;\n /** Check if user has a specific permission */\n hasPermission: (permission: Permission) => boolean;\n}\n\nconst ACTIVE_ORG_KEY = 'cedros_active_org';\n\n// P-06: Safe localStorage helpers to handle private browsing and quota errors\nfunction safeGetItem(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction safeSetItem(key: string, value: string): void {\n try {\n localStorage.setItem(key, value);\n } catch {\n // Ignore - private browsing or quota exceeded\n }\n}\n\n/**\n * Hook for managing organizations, memberships, and permissions.\n *\n * @example\n * ```tsx\n * function OrgSelector() {\n * const { orgs, activeOrg, switchOrg, hasPermission } = useOrgs();\n *\n * return (\n * <select\n * value={activeOrg?.id}\n * onChange={(e) => switchOrg(e.target.value)}\n * >\n * {orgs.map(org => (\n * <option key={org.id} value={org.id}>{org.name}</option>\n * ))}\n * </select>\n * );\n * }\n * ```\n */\nexport function useOrgs(): UseOrgsReturn {\n const { config, user, authState, _internal } = useCedrosLogin();\n const hasStorage = typeof window !== 'undefined' && !!window.localStorage;\n\n const [orgs, setOrgs] = useState<OrgWithMembership[]>([]);\n const [activeOrg, setActiveOrg] = useState<OrgWithMembership | null>(null);\n const [permissions, setPermissions] = useState<Permission[]>([]);\n const [role, setRole] = useState<OrgRole | null>(null);\n const [isLoading, setIsLoading] = useState(authState === 'authenticated');\n const [error, setError] = useState<AuthError | null>(null);\n\n // M-03: Memoize API client and use ref to prevent callback dependency cascades\n const apiClient = useMemo(\n () =>\n new OrgApiClient(\n config.serverUrl,\n config.requestTimeout,\n config.retryAttempts,\n _internal?.getAccessToken\n ),\n [config.serverUrl, config.requestTimeout, config.retryAttempts, _internal]\n );\n\n // M-03: Store apiClient in ref to stabilize callback dependencies\n const apiClientRef = useRef(apiClient);\n useEffect(() => {\n apiClientRef.current = apiClient;\n }, [apiClient]);\n\n // M-03: Use ref in callback to break dependency chain\n const loadPermissions = useCallback(async (orgId: string) => {\n try {\n const response = await apiClientRef.current.getPermissions(orgId);\n setPermissions(response.permissions);\n setRole(response.role);\n } catch {\n // Permissions loading failure is non-fatal, just clear them\n setPermissions([]);\n setRole(null);\n }\n }, []);\n\n // Ref to latest fetchOrgs for stable auto-fetch effect\n const fetchOrgsRef = useRef<() => Promise<void>>(async () => {});\n\n // M-03: Use ref to break apiClient dependency chain\n const fetchOrgs = useCallback(async () => {\n if (authState !== 'authenticated' || !user) {\n setOrgs([]);\n setActiveOrg(null);\n setPermissions([]);\n setRole(null);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const fetchedOrgs = await apiClientRef.current.listOrgs();\n setOrgs(fetchedOrgs);\n\n // P-06: Restore active org from localStorage with safe access\n const savedOrgId = hasStorage ? safeGetItem(ACTIVE_ORG_KEY) : null;\n let selectedOrg = fetchedOrgs.find((org) => org.id === savedOrgId);\n\n if (!selectedOrg && fetchedOrgs.length > 0) {\n // Default to personal org or first org\n selectedOrg = fetchedOrgs.find((org) => org.isPersonal) || fetchedOrgs[0];\n }\n\n if (selectedOrg) {\n setActiveOrg(selectedOrg);\n if (hasStorage) {\n safeSetItem(ACTIVE_ORG_KEY, selectedOrg.id);\n }\n await loadPermissions(selectedOrg.id);\n } else {\n setActiveOrg(null);\n setPermissions([]);\n setRole(null);\n }\n } catch (err) {\n setError(err as AuthError);\n } finally {\n setIsLoading(false);\n }\n }, [authState, user, loadPermissions, hasStorage]);\n\n // Keep fetchOrgsRef current so auto-fetch always calls the latest version\n useEffect(() => {\n fetchOrgsRef.current = fetchOrgs;\n }, [fetchOrgs]);\n\n // Auto-fetch orgs when auth becomes ready.\n // Uses only `authState` (a string) as dep — immune to object-identity churn\n // from the AdminShell bridge recreating `user` objects.\n const hasAutoFetched = useRef(false);\n useEffect(() => {\n if (authState === 'authenticated' && !hasAutoFetched.current) {\n hasAutoFetched.current = true;\n fetchOrgsRef.current();\n } else if (authState !== 'authenticated') {\n hasAutoFetched.current = false;\n }\n }, [authState]);\n\n const switchOrg = useCallback(\n async (orgId: string) => {\n const org = orgs.find((o) => o.id === orgId);\n if (!org) {\n setError({ code: 'UNKNOWN_ERROR', message: 'Organization not found' });\n return;\n }\n\n setActiveOrg(org);\n if (hasStorage) {\n safeSetItem(ACTIVE_ORG_KEY, orgId);\n }\n await loadPermissions(orgId);\n },\n [orgs, loadPermissions, hasStorage]\n );\n\n // M-03: Use ref to break apiClient dependency chain\n const createOrg = useCallback(\n async (data: CreateOrgRequest): Promise<Organization> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const newOrg = await apiClientRef.current.createOrg(data);\n // Refresh orgs list to include the new org with membership\n await fetchOrgs();\n return newOrg;\n } catch (err) {\n setError(err as AuthError);\n throw err;\n } finally {\n setIsLoading(false);\n }\n },\n [fetchOrgs]\n );\n\n const updateOrg = useCallback(\n async (orgId: string, data: UpdateOrgRequest): Promise<Organization> => {\n setIsLoading(true);\n setError(null);\n\n try {\n const updatedOrg = await apiClientRef.current.updateOrg(orgId, data);\n // Refresh orgs list to reflect changes\n await fetchOrgs();\n return updatedOrg;\n } catch (err) {\n setError(err as AuthError);\n throw err;\n } finally {\n setIsLoading(false);\n }\n },\n [fetchOrgs]\n );\n\n const deleteOrg = useCallback(\n async (orgId: string): Promise<void> => {\n setIsLoading(true);\n setError(null);\n\n try {\n await apiClientRef.current.deleteOrg(orgId);\n // Refresh orgs list\n await fetchOrgs();\n } catch (err) {\n setError(err as AuthError);\n throw err;\n } finally {\n setIsLoading(false);\n }\n },\n [fetchOrgs]\n );\n\n const hasPermission = useCallback(\n (permission: Permission): boolean => {\n return permissions.includes(permission);\n },\n [permissions]\n );\n\n return {\n orgs,\n activeOrg,\n permissions,\n role,\n isLoading,\n error,\n fetchOrgs,\n switchOrg,\n createOrg,\n updateOrg,\n deleteOrg,\n hasPermission,\n };\n}\n"],"names":["OrgApiClient","baseUrl","timeoutMs","retryAttempts","getAccessToken","ApiClient","org","error","handleApiError","orgId","data","ACTIVE_ORG_KEY","safeGetItem","key","safeSetItem","value","useOrgs","config","user","authState","_internal","useCedrosLogin","hasStorage","orgs","setOrgs","useState","activeOrg","setActiveOrg","permissions","setPermissions","role","setRole","isLoading","setIsLoading","setError","apiClient","useMemo","apiClientRef","useRef","useEffect","loadPermissions","useCallback","response","fetchOrgsRef","fetchOrgs","fetchedOrgs","savedOrgId","selectedOrg","err","hasAutoFetched","switchOrg","o","createOrg","newOrg","updateOrg","updatedOrg","deleteOrg","hasPermission","permission"],"mappings":"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"}
|