@cedros/login-react 0.0.1 → 0.0.2
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 +142 -2
- package/dist/AuthenticationSettings-CMQhep61.js +9 -0
- package/dist/AuthenticationSettings-CMQhep61.js.map +1 -0
- package/dist/AuthenticationSettings-Cro76kIC.cjs +1 -0
- package/dist/AuthenticationSettings-Cro76kIC.cjs.map +1 -0
- package/dist/CreditSystemSettings-AM7qDk1E.js +9 -0
- package/dist/CreditSystemSettings-AM7qDk1E.js.map +1 -0
- package/dist/CreditSystemSettings-CGR-uzuh.cjs +1 -0
- package/dist/CreditSystemSettings-CGR-uzuh.cjs.map +1 -0
- package/dist/DepositsSection-BVNd63B7.js +47 -0
- package/dist/DepositsSection-BVNd63B7.js.map +1 -0
- package/dist/DepositsSection-Ct1gjgIg.cjs +1 -0
- package/dist/DepositsSection-Ct1gjgIg.cjs.map +1 -0
- package/dist/EmailRegisterForm-B-ys4E3C.cjs +1 -0
- package/dist/EmailRegisterForm-B-ys4E3C.cjs.map +1 -0
- package/dist/EmailRegisterForm-BChCiZ5B.js +961 -0
- package/dist/EmailRegisterForm-BChCiZ5B.js.map +1 -0
- package/dist/EmailSettings-DFZ13JbX.cjs +1 -0
- package/dist/EmailSettings-DFZ13JbX.cjs.map +1 -0
- package/dist/EmailSettings-Di4GSWgb.js +9 -0
- package/dist/EmailSettings-Di4GSWgb.js.map +1 -0
- package/dist/EmbeddedWalletSettings-Cmn_aVL7.js +9 -0
- package/dist/EmbeddedWalletSettings-Cmn_aVL7.js.map +1 -0
- package/dist/EmbeddedWalletSettings-DRWeIJKb.cjs +1 -0
- package/dist/EmbeddedWalletSettings-DRWeIJKb.cjs.map +1 -0
- package/dist/{ErrorMessage-Bm1j5mBT.js → ErrorMessage-C8vKB0JG.js} +9 -9
- package/dist/ErrorMessage-C8vKB0JG.js.map +1 -0
- package/dist/ErrorMessage-CntMyn93.cjs.map +1 -1
- package/dist/{GoogleLoginButton-CvDoOc-0.js → GoogleLoginButton-Ceo2sYvX.js} +1 -1
- package/dist/{GoogleLoginButton-CvDoOc-0.js.map → GoogleLoginButton-Ceo2sYvX.js.map} +1 -1
- package/dist/ServerSettings-D8w8EpoE.cjs +1 -0
- package/dist/ServerSettings-D8w8EpoE.cjs.map +1 -0
- package/dist/ServerSettings-Wg_odW46.js +9 -0
- package/dist/ServerSettings-Wg_odW46.js.map +1 -0
- package/dist/{SolanaLoginButton-h32xN2PQ.js → SolanaLoginButton-CqVOcPa7.js} +1 -1
- package/dist/{SolanaLoginButton-h32xN2PQ.js.map → SolanaLoginButton-CqVOcPa7.js.map} +1 -1
- package/dist/TeamSection-Bj89WSC0.js +128 -0
- package/dist/TeamSection-Bj89WSC0.js.map +1 -0
- package/dist/TeamSection-DQ4nfUHN.cjs +1 -0
- package/dist/TeamSection-DQ4nfUHN.cjs.map +1 -0
- package/dist/UsersSection-BiZceDV3.cjs +1 -0
- package/dist/UsersSection-BiZceDV3.cjs.map +1 -0
- package/dist/UsersSection-W19ddPsw.js +81 -0
- package/dist/UsersSection-W19ddPsw.js.map +1 -0
- package/dist/WebhookSettings-a1c4iMvr.js +9 -0
- package/dist/WebhookSettings-a1c4iMvr.js.map +1 -0
- package/dist/WebhookSettings-onJWLytD.cjs +1 -0
- package/dist/WebhookSettings-onJWLytD.cjs.map +1 -0
- package/dist/WithdrawalsSection-BBw9gWMR.js +20 -0
- package/dist/WithdrawalsSection-BBw9gWMR.js.map +1 -0
- package/dist/WithdrawalsSection-Cws8inf6.cjs +1 -0
- package/dist/WithdrawalsSection-Cws8inf6.cjs.map +1 -0
- package/dist/admin/AdminShell.d.ts +38 -0
- package/dist/admin/icons.d.ts +2 -0
- package/dist/admin/index.d.ts +10 -0
- package/dist/admin/plugin.d.ts +3 -0
- package/dist/admin/sections/AuthenticationSettings.d.ts +3 -0
- package/dist/admin/sections/CreditSystemSettings.d.ts +3 -0
- package/dist/admin/sections/DepositsSection.d.ts +3 -0
- package/dist/admin/sections/EmailSettings.d.ts +3 -0
- package/dist/admin/sections/EmbeddedWalletSettings.d.ts +3 -0
- package/dist/admin/sections/FeatureSettings.d.ts +3 -0
- package/dist/admin/sections/InvitesSection.d.ts +3 -0
- package/dist/admin/sections/MembersSection.d.ts +3 -0
- package/dist/admin/sections/ServerSettings.d.ts +3 -0
- package/dist/admin/sections/SettingsSections.d.ts +7 -0
- package/dist/admin/sections/TeamSection.d.ts +3 -0
- package/dist/admin/sections/UsersSection.d.ts +3 -0
- package/dist/admin/sections/WebhookSettings.d.ts +3 -0
- package/dist/admin/sections/WithdrawalsSection.d.ts +3 -0
- package/dist/admin/types.d.ts +167 -0
- package/dist/components/admin/AdminUserDetail.d.ts +3 -1
- package/dist/components/admin/AdminUserList.d.ts +3 -7
- package/dist/components/admin/CedrosAdminDashboard.d.ts +7 -3
- package/dist/components/admin/PermissionsSection.d.ts +7 -0
- package/dist/components/admin/ProfileDropdown.d.ts +34 -0
- package/dist/components/admin/SetupWizard.d.ts +30 -0
- package/dist/components/admin/StatsBar.d.ts +17 -0
- package/dist/components/admin/settings/AuthenticationSettings.d.ts +4 -0
- package/dist/components/admin/settings/AutosaveStatus.d.ts +6 -0
- package/dist/components/admin/settings/CreditSystemSettings.d.ts +6 -0
- package/dist/components/admin/settings/EmailSettings.d.ts +4 -0
- package/dist/components/admin/settings/EmbeddedWalletSettings.d.ts +4 -0
- package/dist/components/admin/settings/FeatureSettings.d.ts +4 -0
- package/dist/components/admin/settings/MessagingSettings.d.ts +4 -0
- package/dist/components/admin/settings/SecuritySettings.d.ts +4 -0
- package/dist/components/admin/settings/ServerSettings.d.ts +4 -0
- package/dist/components/admin/settings/SettingsPageLayout.d.ts +16 -0
- package/dist/components/admin/settings/SsoProvidersSettings.d.ts +4 -0
- package/dist/components/admin/settings/WebhookSettings.d.ts +4 -0
- package/dist/components/admin/settings/index.d.ts +31 -0
- package/dist/components/admin/settings/settingsInputs.d.ts +79 -0
- package/dist/components/admin/settings/settingsMetadata.d.ts +18 -0
- package/dist/components/deposit/admin/AdminWithdrawalStats.d.ts +10 -0
- package/dist/components/deposit/admin/FeatureDisabledMessage.d.ts +15 -0
- package/dist/components/deposit/admin/featureDisabled.d.ts +9 -0
- package/dist/components/deposit/admin/index.d.ts +5 -0
- package/dist/components/invites/InviteForm.d.ts +1 -1
- package/dist/components/org/OrgSelector.d.ts +2 -1
- package/dist/components/profile/UserProfileSettings.d.ts +35 -0
- package/dist/components/profile/index.d.ts +2 -0
- package/dist/crypto/secureWipe.d.ts +0 -19
- package/dist/crypto/webauthnPrf.d.ts +0 -10
- package/dist/email-only.cjs +1 -1
- package/dist/email-only.js +2 -2
- package/dist/google-only.js +2 -2
- package/dist/hooks/useDashboardPermissions.d.ts +45 -0
- package/dist/hooks/useProfile.d.ts +35 -0
- package/dist/hooks/useServerFeatures.d.ts +55 -0
- package/dist/hooks/useSettingsAutosave.d.ts +29 -0
- package/dist/hooks/useSetup.d.ts +25 -0
- package/dist/hooks/useSsoProviders.d.ts +52 -0
- package/dist/index-BHR_WTP2.js +19636 -0
- package/dist/index-BHR_WTP2.js.map +1 -0
- package/dist/index-JsIJkPin.cjs +2061 -0
- package/dist/index-JsIJkPin.cjs.map +1 -0
- package/dist/index.cjs +1 -2061
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +21 -3
- package/dist/index.js +117 -14908
- package/dist/index.js.map +1 -1
- package/dist/login-react.css +1 -1
- package/dist/silentWalletEnroll-CQK5i65l.js +42 -0
- package/dist/silentWalletEnroll-CQK5i65l.js.map +1 -0
- package/dist/silentWalletEnroll-DBfS2sLe.cjs +1 -0
- package/dist/silentWalletEnroll-DBfS2sLe.cjs.map +1 -0
- package/dist/solana-only.js +2 -2
- package/dist/solanaKeypair-BD7Kq1Mw.js +1932 -0
- package/dist/solanaKeypair-BD7Kq1Mw.js.map +1 -0
- package/dist/solanaKeypair-CBQxm2hw.cjs +1 -0
- package/dist/solanaKeypair-CBQxm2hw.cjs.map +1 -0
- package/dist/types/adminUser.d.ts +56 -0
- package/dist/types/deposit.d.ts +2 -0
- package/dist/types/index.d.ts +4 -2
- package/dist/types/org.d.ts +34 -2
- package/dist/types/profile.d.ts +56 -0
- package/dist/types/setup.d.ts +45 -0
- package/dist/types/systemSettings.d.ts +5 -1
- package/dist/utils/adminUserApi.d.ts +10 -1
- package/dist/utils/profileApi.d.ts +26 -0
- package/dist/utils/setupApi.d.ts +21 -0
- package/package.json +1 -1
- package/dist/EmailRegisterForm-D_uCEdX9.cjs +0 -1
- package/dist/EmailRegisterForm-D_uCEdX9.cjs.map +0 -1
- package/dist/EmailRegisterForm-m3rX3A6X.js +0 -2923
- package/dist/EmailRegisterForm-m3rX3A6X.js.map +0 -1
- package/dist/ErrorMessage-Bm1j5mBT.js.map +0 -1
package/README.md
CHANGED
|
@@ -178,13 +178,25 @@ function AuthStatus() {
|
|
|
178
178
|
| `CredentialList` | List all authentication methods (passwords, passkeys, OAuth) |
|
|
179
179
|
| `CredentialCard` | Individual credential display with management options |
|
|
180
180
|
|
|
181
|
-
### Admin
|
|
181
|
+
### Admin Dashboard
|
|
182
182
|
|
|
183
183
|
| Component | Description |
|
|
184
184
|
|-----------|-------------|
|
|
185
|
-
| `
|
|
185
|
+
| `CedrosAdminDashboard` | Complete standalone admin panel with sidebar navigation and all sections |
|
|
186
|
+
| `AdminShell` | Plugin host for combining multiple admin dashboards (cedros-login + cedros-pay) |
|
|
187
|
+
| `AdminPanel` | Legacy admin dashboard with tabs for members, invites, sessions, system settings |
|
|
186
188
|
| `SystemSettings` | System settings editor (privacy, withdrawal, rate limits) - system admin only |
|
|
187
189
|
|
|
190
|
+
#### Admin Plugin System
|
|
191
|
+
|
|
192
|
+
The admin dashboard supports a plugin architecture for creating unified dashboards that combine sections from multiple Cedros packages (e.g., cedros-login + cedros-pay).
|
|
193
|
+
|
|
194
|
+
| Export | Description |
|
|
195
|
+
|--------|-------------|
|
|
196
|
+
| `cedrosLoginPlugin` | Plugin definition for cedros-login admin sections |
|
|
197
|
+
| `AdminShell` | Host component that renders plugins with unified sidebar |
|
|
198
|
+
| `useAdminShell` | Hook for accessing admin shell context |
|
|
199
|
+
|
|
188
200
|
### Shared
|
|
189
201
|
|
|
190
202
|
| Component | Description |
|
|
@@ -1093,6 +1105,126 @@ function AdminStatsWidget() {
|
|
|
1093
1105
|
}
|
|
1094
1106
|
```
|
|
1095
1107
|
|
|
1108
|
+
### Admin Dashboard
|
|
1109
|
+
|
|
1110
|
+
The `CedrosAdminDashboard` is a complete, ready-to-use admin panel with sidebar navigation:
|
|
1111
|
+
|
|
1112
|
+
```tsx
|
|
1113
|
+
import { CedrosAdminDashboard } from '@cedros/login-react';
|
|
1114
|
+
|
|
1115
|
+
function AdminPage() {
|
|
1116
|
+
return (
|
|
1117
|
+
<CedrosAdminDashboard
|
|
1118
|
+
title="My App Admin"
|
|
1119
|
+
sections={['users', 'team', 'deposits', 'withdrawals', 'settings-auth', 'settings-credits']}
|
|
1120
|
+
defaultSection="users"
|
|
1121
|
+
pageSize={20}
|
|
1122
|
+
refreshInterval={30000}
|
|
1123
|
+
/>
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
**Available sections:**
|
|
1129
|
+
- `users` - User management with stats (system admin only)
|
|
1130
|
+
- `team` - Organization members and pending invites
|
|
1131
|
+
- `deposits` - Browse all deposits with status filtering
|
|
1132
|
+
- `withdrawals` - Process company withdrawal queue
|
|
1133
|
+
- `settings-auth` - Authentication settings (OAuth, passkeys, etc.)
|
|
1134
|
+
- `settings-messaging` - Email/SMTP and webhook configuration
|
|
1135
|
+
- `settings-wallet` - User wallet settings
|
|
1136
|
+
- `settings-credits` - Credit system and treasury configuration
|
|
1137
|
+
- `settings-server` - Auth server settings
|
|
1138
|
+
|
|
1139
|
+
### Unified Admin Dashboard (Plugin System)
|
|
1140
|
+
|
|
1141
|
+
When using both `@cedros/login-react` and `@cedros/pay-react`, you can create a unified admin dashboard that combines sections from both packages using the plugin architecture:
|
|
1142
|
+
|
|
1143
|
+
```tsx
|
|
1144
|
+
import { AdminShell, cedrosLoginPlugin } from '@cedros/login-react';
|
|
1145
|
+
import { cedrosPayPlugin } from '@cedros/pay-react'; // hypothetical
|
|
1146
|
+
import { useCedrosLogin } from '@cedros/login-react';
|
|
1147
|
+
import { useOrgs } from '@cedros/login-react';
|
|
1148
|
+
|
|
1149
|
+
function UnifiedAdminDashboard() {
|
|
1150
|
+
const { user, getAccessToken } = useCedrosLogin();
|
|
1151
|
+
const { activeOrg, role, permissions } = useOrgs();
|
|
1152
|
+
|
|
1153
|
+
// Build host context from all auth providers
|
|
1154
|
+
const hostContext = {
|
|
1155
|
+
cedrosLogin: {
|
|
1156
|
+
user,
|
|
1157
|
+
getAccessToken,
|
|
1158
|
+
serverUrl: 'https://api.example.com/auth',
|
|
1159
|
+
},
|
|
1160
|
+
cedrosPay: {
|
|
1161
|
+
serverUrl: 'https://api.example.com/pay',
|
|
1162
|
+
// Add wallet/JWT context if using cedros-pay
|
|
1163
|
+
},
|
|
1164
|
+
org: activeOrg ? {
|
|
1165
|
+
orgId: activeOrg.id,
|
|
1166
|
+
role: role || 'member',
|
|
1167
|
+
permissions: permissions || [],
|
|
1168
|
+
} : undefined,
|
|
1169
|
+
};
|
|
1170
|
+
|
|
1171
|
+
return (
|
|
1172
|
+
<AdminShell
|
|
1173
|
+
title="Admin Dashboard"
|
|
1174
|
+
plugins={[cedrosLoginPlugin, cedrosPayPlugin]}
|
|
1175
|
+
hostContext={hostContext}
|
|
1176
|
+
defaultSection="cedros-login:users"
|
|
1177
|
+
pageSize={20}
|
|
1178
|
+
refreshInterval={30000}
|
|
1179
|
+
/>
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
```
|
|
1183
|
+
|
|
1184
|
+
**How it works:**
|
|
1185
|
+
|
|
1186
|
+
1. Each package exports an `AdminPlugin` that defines its sections and components
|
|
1187
|
+
2. `AdminShell` aggregates sections from all plugins into a unified sidebar
|
|
1188
|
+
3. Sections are grouped (Users, Store, Configuration) and sorted by order
|
|
1189
|
+
4. Each plugin's CSS is isolated via namespace scoping
|
|
1190
|
+
|
|
1191
|
+
**Plugin Structure:**
|
|
1192
|
+
|
|
1193
|
+
```tsx
|
|
1194
|
+
// Each plugin exports this structure
|
|
1195
|
+
export const cedrosLoginPlugin: AdminPlugin = {
|
|
1196
|
+
id: 'cedros-login',
|
|
1197
|
+
name: 'Cedros Login',
|
|
1198
|
+
version: '1.0.0',
|
|
1199
|
+
sections: [
|
|
1200
|
+
{ id: 'users', label: 'Users', icon: <UsersIcon />, group: 'Users', order: 0 },
|
|
1201
|
+
{ id: 'team', label: 'Team', icon: <TeamIcon />, group: 'Users', order: 1 },
|
|
1202
|
+
// ... more sections
|
|
1203
|
+
],
|
|
1204
|
+
components: {
|
|
1205
|
+
users: UsersSection,
|
|
1206
|
+
team: TeamSection,
|
|
1207
|
+
// ... lazy-loaded components
|
|
1208
|
+
},
|
|
1209
|
+
createPluginContext: (hostContext) => ({
|
|
1210
|
+
serverUrl: hostContext.cedrosLogin?.serverUrl || '',
|
|
1211
|
+
userId: hostContext.cedrosLogin?.user?.id,
|
|
1212
|
+
getAccessToken: hostContext.cedrosLogin?.getAccessToken || (() => null),
|
|
1213
|
+
hasPermission: (p) => /* permission check */,
|
|
1214
|
+
}),
|
|
1215
|
+
checkPermission: (permission, hostContext) => /* ... */,
|
|
1216
|
+
cssNamespace: 'cedros-dashboard',
|
|
1217
|
+
};
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1220
|
+
**Sidebar grouping in unified mode:**
|
|
1221
|
+
|
|
1222
|
+
| Group | Source | Sections |
|
|
1223
|
+
|-------|--------|----------|
|
|
1224
|
+
| Users | cedros-login | users, team, deposits, withdrawals |
|
|
1225
|
+
| Store | cedros-pay | products, subscriptions, transactions, coupons |
|
|
1226
|
+
| Configuration | both | All settings pages from both plugins |
|
|
1227
|
+
|
|
1096
1228
|
## TypeScript
|
|
1097
1229
|
|
|
1098
1230
|
All components and hooks are fully typed:
|
|
@@ -1149,6 +1281,14 @@ import type {
|
|
|
1149
1281
|
UpdateSystemSettingsResponse,
|
|
1150
1282
|
UseSystemSettingsReturn,
|
|
1151
1283
|
|
|
1284
|
+
// Admin plugin types
|
|
1285
|
+
AdminPlugin,
|
|
1286
|
+
AdminSectionConfig,
|
|
1287
|
+
AdminSectionProps,
|
|
1288
|
+
PluginContext,
|
|
1289
|
+
HostContext,
|
|
1290
|
+
PluginRegistry,
|
|
1291
|
+
|
|
1152
1292
|
// Config types
|
|
1153
1293
|
CedrosLoginConfig,
|
|
1154
1294
|
FeatureFlags,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as t } from "react/jsx-runtime";
|
|
2
|
+
import "react";
|
|
3
|
+
import { m as i } from "./index-BHR_WTP2.js";
|
|
4
|
+
function s(o) {
|
|
5
|
+
return /* @__PURE__ */ t("div", { className: "cedros-dashboard__section", children: /* @__PURE__ */ t(i, {}) });
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
s as default
|
|
9
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthenticationSettings-CMQhep61.js","sources":["../src/admin/sections/AuthenticationSettings.tsx"],"sourcesContent":["/**\n * Authentication Settings Section - Plugin wrapper\n */\n\nimport React from 'react';\nimport type { AdminSectionProps } from '../types';\nimport { AuthenticationSettings as Settings } from '../../components/admin/settings';\n\nexport default function AuthenticationSettings(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _props: AdminSectionProps\n): React.JSX.Element {\n return (\n <div className=\"cedros-dashboard__section\">\n <Settings />\n </div>\n );\n}\n"],"names":["AuthenticationSettings","_props","jsx","Settings"],"mappings":";;;AAQA,SAAwBA,EAEtBC,GACmB;AACnB,2BACG,OAAA,EAAI,WAAU,6BACb,UAAA,gBAAAC,EAACC,KAAS,GACZ;AAEJ;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime");require("react");const t=require("./index-JsIJkPin.cjs");function i(n){return e.jsx("div",{className:"cedros-dashboard__section",children:e.jsx(t.AuthenticationSettings,{})})}exports.default=i;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthenticationSettings-Cro76kIC.cjs","sources":["../src/admin/sections/AuthenticationSettings.tsx"],"sourcesContent":["/**\n * Authentication Settings Section - Plugin wrapper\n */\n\nimport React from 'react';\nimport type { AdminSectionProps } from '../types';\nimport { AuthenticationSettings as Settings } from '../../components/admin/settings';\n\nexport default function AuthenticationSettings(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _props: AdminSectionProps\n): React.JSX.Element {\n return (\n <div className=\"cedros-dashboard__section\">\n <Settings />\n </div>\n );\n}\n"],"names":["AuthenticationSettings","_props","jsx","Settings"],"mappings":"8KAQA,SAAwBA,EAEtBC,EACmB,CACnB,aACG,MAAA,CAAI,UAAU,4BACb,SAAAC,MAACC,EAAAA,yBAAS,EACZ,CAEJ"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as t } from "react/jsx-runtime";
|
|
2
|
+
import "react";
|
|
3
|
+
import { C as r } from "./index-BHR_WTP2.js";
|
|
4
|
+
function d(e) {
|
|
5
|
+
return /* @__PURE__ */ t("div", { className: "cedros-dashboard__section", children: /* @__PURE__ */ t(r, {}) });
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
d as default
|
|
9
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreditSystemSettings-AM7qDk1E.js","sources":["../src/admin/sections/CreditSystemSettings.tsx"],"sourcesContent":["/**\n * Credit System Settings Section - Plugin wrapper\n */\n\nimport React from 'react';\nimport type { AdminSectionProps } from '../types';\nimport { CreditSystemSettings as Settings } from '../../components/admin/settings';\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport default function CreditSystemSettings(_props: AdminSectionProps): React.JSX.Element {\n return (\n <div className=\"cedros-dashboard__section\">\n <Settings />\n </div>\n );\n}\n"],"names":["CreditSystemSettings","_props","jsx","Settings"],"mappings":";;;AASA,SAAwBA,EAAqBC,GAA8C;AACzF,2BACG,OAAA,EAAI,WAAU,6BACb,UAAA,gBAAAC,EAACC,KAAS,GACZ;AAEJ;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime");require("react");const t=require("./index-JsIJkPin.cjs");function r(s){return e.jsx("div",{className:"cedros-dashboard__section",children:e.jsx(t.CreditSystemSettings,{})})}exports.default=r;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreditSystemSettings-CGR-uzuh.cjs","sources":["../src/admin/sections/CreditSystemSettings.tsx"],"sourcesContent":["/**\n * Credit System Settings Section - Plugin wrapper\n */\n\nimport React from 'react';\nimport type { AdminSectionProps } from '../types';\nimport { CreditSystemSettings as Settings } from '../../components/admin/settings';\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport default function CreditSystemSettings(_props: AdminSectionProps): React.JSX.Element {\n return (\n <div className=\"cedros-dashboard__section\">\n <Settings />\n </div>\n );\n}\n"],"names":["CreditSystemSettings","_props","jsx","Settings"],"mappings":"8KASA,SAAwBA,EAAqBC,EAA8C,CACzF,aACG,MAAA,CAAI,UAAU,4BACb,SAAAC,MAACC,EAAAA,uBAAS,EACZ,CAEJ"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsxs as t, jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import { useState as o } from "react";
|
|
3
|
+
import { g as r, h as n } from "./index-BHR_WTP2.js";
|
|
4
|
+
function u({
|
|
5
|
+
pageSize: d = 20,
|
|
6
|
+
refreshInterval: s = 0
|
|
7
|
+
}) {
|
|
8
|
+
const [i, a] = o("");
|
|
9
|
+
return /* @__PURE__ */ t("div", { className: "cedros-dashboard__deposits", children: [
|
|
10
|
+
/* @__PURE__ */ e(r, { refreshInterval: s }),
|
|
11
|
+
/* @__PURE__ */ t("div", { className: "cedros-dashboard__deposits-list", children: [
|
|
12
|
+
/* @__PURE__ */ e("div", { className: "cedros-dashboard__toolbar", children: /* @__PURE__ */ t("div", { className: "cedros-dashboard__filter", children: [
|
|
13
|
+
/* @__PURE__ */ e("label", { className: "cedros-dashboard__filter-label", htmlFor: "status-filter", children: "Status" }),
|
|
14
|
+
/* @__PURE__ */ t(
|
|
15
|
+
"select",
|
|
16
|
+
{
|
|
17
|
+
id: "status-filter",
|
|
18
|
+
className: "cedros-dashboard__select",
|
|
19
|
+
value: i,
|
|
20
|
+
onChange: (l) => a(l.target.value),
|
|
21
|
+
children: [
|
|
22
|
+
/* @__PURE__ */ e("option", { value: "", children: "All statuses" }),
|
|
23
|
+
/* @__PURE__ */ e("option", { value: "pending", children: "Pending" }),
|
|
24
|
+
/* @__PURE__ */ e("option", { value: "detected", children: "Detected" }),
|
|
25
|
+
/* @__PURE__ */ e("option", { value: "processing", children: "Processing" }),
|
|
26
|
+
/* @__PURE__ */ e("option", { value: "completed", children: "Completed" }),
|
|
27
|
+
/* @__PURE__ */ e("option", { value: "withdrawn", children: "Withdrawn" }),
|
|
28
|
+
/* @__PURE__ */ e("option", { value: "expired", children: "Expired" }),
|
|
29
|
+
/* @__PURE__ */ e("option", { value: "failed", children: "Failed" })
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
] }) }),
|
|
34
|
+
/* @__PURE__ */ e(
|
|
35
|
+
n,
|
|
36
|
+
{
|
|
37
|
+
statusFilter: i || void 0,
|
|
38
|
+
pageSize: d,
|
|
39
|
+
refreshInterval: s
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
] })
|
|
43
|
+
] });
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
u as default
|
|
47
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DepositsSection-BVNd63B7.js","sources":["../src/admin/sections/DepositsSection.tsx"],"sourcesContent":["/**\n * Deposits Section - Plugin wrapper\n *\n * Admin deposit management with stats, list, and filter controls.\n */\n\nimport React, { useState } from 'react';\nimport type { AdminSectionProps } from '../types';\nimport { AdminDepositStats, AdminDepositList } from '../../components/deposit/admin';\n\nexport default function DepositsSection({\n pageSize = 20,\n refreshInterval = 0,\n}: AdminSectionProps): React.JSX.Element {\n const [statusFilter, setStatusFilter] = useState<string>('');\n\n return (\n <div className=\"cedros-dashboard__deposits\">\n {/* Stats summary */}\n <AdminDepositStats refreshInterval={refreshInterval} />\n\n {/* Filter and list */}\n <div className=\"cedros-dashboard__deposits-list\">\n <div className=\"cedros-dashboard__toolbar\">\n <div className=\"cedros-dashboard__filter\">\n <label className=\"cedros-dashboard__filter-label\" htmlFor=\"status-filter\">\n Status\n </label>\n <select\n id=\"status-filter\"\n className=\"cedros-dashboard__select\"\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value)}\n >\n <option value=\"\">All statuses</option>\n <option value=\"pending\">Pending</option>\n <option value=\"detected\">Detected</option>\n <option value=\"processing\">Processing</option>\n <option value=\"completed\">Completed</option>\n <option value=\"withdrawn\">Withdrawn</option>\n <option value=\"expired\">Expired</option>\n <option value=\"failed\">Failed</option>\n </select>\n </div>\n </div>\n\n <AdminDepositList\n statusFilter={statusFilter || undefined}\n pageSize={pageSize}\n refreshInterval={refreshInterval}\n />\n </div>\n </div>\n );\n}\n"],"names":["DepositsSection","pageSize","refreshInterval","statusFilter","setStatusFilter","useState","jsxs","jsx","AdminDepositStats","e","AdminDepositList"],"mappings":";;;AAUA,SAAwBA,EAAgB;AAAA,EACtC,UAAAC,IAAW;AAAA,EACX,iBAAAC,IAAkB;AACpB,GAAyC;AACvC,QAAM,CAACC,GAAcC,CAAe,IAAIC,EAAiB,EAAE;AAE3D,SACE,gBAAAC,EAAC,OAAA,EAAI,WAAU,8BAEb,UAAA;AAAA,IAAA,gBAAAC,EAACC,KAAkB,iBAAAN,GAAkC;AAAA,IAGrD,gBAAAI,EAAC,OAAA,EAAI,WAAU,mCACb,UAAA;AAAA,MAAA,gBAAAC,EAAC,SAAI,WAAU,6BACb,UAAA,gBAAAD,EAAC,OAAA,EAAI,WAAU,4BACb,UAAA;AAAA,QAAA,gBAAAC,EAAC,SAAA,EAAM,WAAU,kCAAiC,SAAQ,iBAAgB,UAAA,UAE1E;AAAA,QACA,gBAAAD;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,IAAG;AAAA,YACH,WAAU;AAAA,YACV,OAAOH;AAAA,YACP,UAAU,CAACM,MAAML,EAAgBK,EAAE,OAAO,KAAK;AAAA,YAE/C,UAAA;AAAA,cAAA,gBAAAF,EAAC,UAAA,EAAO,OAAM,IAAG,UAAA,gBAAY;AAAA,cAC7B,gBAAAA,EAAC,UAAA,EAAO,OAAM,WAAU,UAAA,WAAO;AAAA,cAC/B,gBAAAA,EAAC,UAAA,EAAO,OAAM,YAAW,UAAA,YAAQ;AAAA,cACjC,gBAAAA,EAAC,UAAA,EAAO,OAAM,cAAa,UAAA,cAAU;AAAA,cACrC,gBAAAA,EAAC,UAAA,EAAO,OAAM,aAAY,UAAA,aAAS;AAAA,cACnC,gBAAAA,EAAC,UAAA,EAAO,OAAM,aAAY,UAAA,aAAS;AAAA,cACnC,gBAAAA,EAAC,UAAA,EAAO,OAAM,WAAU,UAAA,WAAO;AAAA,cAC/B,gBAAAA,EAAC,UAAA,EAAO,OAAM,UAAS,UAAA,SAAA,CAAM;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MAC/B,EAAA,CACF,EAAA,CACF;AAAA,MAEA,gBAAAA;AAAA,QAACG;AAAA,QAAA;AAAA,UACC,cAAcP,KAAgB;AAAA,UAC9B,UAAAF;AAAA,UACA,iBAAAC;AAAA,QAAA;AAAA,MAAA;AAAA,IACF,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),o=require("react"),i=require("./index-JsIJkPin.cjs");function r({pageSize:d=20,refreshInterval:s=0}){const[t,a]=o.useState("");return e.jsxs("div",{className:"cedros-dashboard__deposits",children:[e.jsx(i.AdminDepositStats,{refreshInterval:s}),e.jsxs("div",{className:"cedros-dashboard__deposits-list",children:[e.jsx("div",{className:"cedros-dashboard__toolbar",children:e.jsxs("div",{className:"cedros-dashboard__filter",children:[e.jsx("label",{className:"cedros-dashboard__filter-label",htmlFor:"status-filter",children:"Status"}),e.jsxs("select",{id:"status-filter",className:"cedros-dashboard__select",value:t,onChange:l=>a(l.target.value),children:[e.jsx("option",{value:"",children:"All statuses"}),e.jsx("option",{value:"pending",children:"Pending"}),e.jsx("option",{value:"detected",children:"Detected"}),e.jsx("option",{value:"processing",children:"Processing"}),e.jsx("option",{value:"completed",children:"Completed"}),e.jsx("option",{value:"withdrawn",children:"Withdrawn"}),e.jsx("option",{value:"expired",children:"Expired"}),e.jsx("option",{value:"failed",children:"Failed"})]})]})}),e.jsx(i.AdminDepositList,{statusFilter:t||void 0,pageSize:d,refreshInterval:s})]})]})}exports.default=r;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DepositsSection-Ct1gjgIg.cjs","sources":["../src/admin/sections/DepositsSection.tsx"],"sourcesContent":["/**\n * Deposits Section - Plugin wrapper\n *\n * Admin deposit management with stats, list, and filter controls.\n */\n\nimport React, { useState } from 'react';\nimport type { AdminSectionProps } from '../types';\nimport { AdminDepositStats, AdminDepositList } from '../../components/deposit/admin';\n\nexport default function DepositsSection({\n pageSize = 20,\n refreshInterval = 0,\n}: AdminSectionProps): React.JSX.Element {\n const [statusFilter, setStatusFilter] = useState<string>('');\n\n return (\n <div className=\"cedros-dashboard__deposits\">\n {/* Stats summary */}\n <AdminDepositStats refreshInterval={refreshInterval} />\n\n {/* Filter and list */}\n <div className=\"cedros-dashboard__deposits-list\">\n <div className=\"cedros-dashboard__toolbar\">\n <div className=\"cedros-dashboard__filter\">\n <label className=\"cedros-dashboard__filter-label\" htmlFor=\"status-filter\">\n Status\n </label>\n <select\n id=\"status-filter\"\n className=\"cedros-dashboard__select\"\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value)}\n >\n <option value=\"\">All statuses</option>\n <option value=\"pending\">Pending</option>\n <option value=\"detected\">Detected</option>\n <option value=\"processing\">Processing</option>\n <option value=\"completed\">Completed</option>\n <option value=\"withdrawn\">Withdrawn</option>\n <option value=\"expired\">Expired</option>\n <option value=\"failed\">Failed</option>\n </select>\n </div>\n </div>\n\n <AdminDepositList\n statusFilter={statusFilter || undefined}\n pageSize={pageSize}\n refreshInterval={refreshInterval}\n />\n </div>\n </div>\n );\n}\n"],"names":["DepositsSection","pageSize","refreshInterval","statusFilter","setStatusFilter","useState","jsxs","jsx","AdminDepositStats","e","AdminDepositList"],"mappings":"0KAUA,SAAwBA,EAAgB,CACtC,SAAAC,EAAW,GACX,gBAAAC,EAAkB,CACpB,EAAyC,CACvC,KAAM,CAACC,EAAcC,CAAe,EAAIC,EAAAA,SAAiB,EAAE,EAE3D,OACEC,EAAAA,KAAC,MAAA,CAAI,UAAU,6BAEb,SAAA,CAAAC,MAACC,EAAAA,mBAAkB,gBAAAN,EAAkC,EAGrDI,EAAAA,KAAC,MAAA,CAAI,UAAU,kCACb,SAAA,CAAAC,EAAAA,IAAC,OAAI,UAAU,4BACb,SAAAD,EAAAA,KAAC,MAAA,CAAI,UAAU,2BACb,SAAA,CAAAC,MAAC,QAAA,CAAM,UAAU,iCAAiC,QAAQ,gBAAgB,SAAA,SAE1E,EACAD,EAAAA,KAAC,SAAA,CACC,GAAG,gBACH,UAAU,2BACV,MAAOH,EACP,SAAWM,GAAML,EAAgBK,EAAE,OAAO,KAAK,EAE/C,SAAA,CAAAF,EAAAA,IAAC,SAAA,CAAO,MAAM,GAAG,SAAA,eAAY,EAC7BA,EAAAA,IAAC,SAAA,CAAO,MAAM,UAAU,SAAA,UAAO,EAC/BA,EAAAA,IAAC,SAAA,CAAO,MAAM,WAAW,SAAA,WAAQ,EACjCA,EAAAA,IAAC,SAAA,CAAO,MAAM,aAAa,SAAA,aAAU,EACrCA,EAAAA,IAAC,SAAA,CAAO,MAAM,YAAY,SAAA,YAAS,EACnCA,EAAAA,IAAC,SAAA,CAAO,MAAM,YAAY,SAAA,YAAS,EACnCA,EAAAA,IAAC,SAAA,CAAO,MAAM,UAAU,SAAA,UAAO,EAC/BA,EAAAA,IAAC,SAAA,CAAO,MAAM,SAAS,SAAA,QAAA,CAAM,CAAA,CAAA,CAAA,CAC/B,CAAA,CACF,CAAA,CACF,EAEAA,EAAAA,IAACG,EAAAA,iBAAA,CACC,aAAcP,GAAgB,OAC9B,SAAAF,EACA,gBAAAC,CAAA,CAAA,CACF,CAAA,CACF,CAAA,EACF,CAEJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const e=require("react/jsx-runtime"),s=require("react"),j=require("./ErrorMessage-CntMyn93.cjs"),M=require("./validation-BeXIfuHB.cjs"),te=["https:"],B=["javascript:","data:","vbscript:","file:"];function re(r){if(!r||typeof r!="string")return;const l=r.trim();if(!l)return;const b=l.toLowerCase();for(const o of B)if(b.startsWith(o))return;try{const o=new URL(l);return te.includes(o.protocol)?l:void 0}catch{return}}function z(r){if(!r||typeof r!="string")return;const l=r.trim();if(!l)return;const b=l.toLowerCase();for(const o of B)if(b.startsWith(o))return;try{const o=new URL(l);return o.protocol!=="https:"&&o.protocol!=="http:"?void 0:l}catch{return}}function q(r={}){const{maxAttempts:l=5,windowMs:b=6e4}=r,o=s.useRef([]),[w,d]=s.useState(!1),[,k]=s.useState(0),u=s.useCallback(()=>{k(c=>c+1)},[]),p=s.useCallback(()=>{const c=Date.now();o.current=o.current.filter(h=>c-h<b)},[b]),f=s.useCallback(()=>{p(),d(c=>o.current.length===0&&c?!1:c)},[p]),v=s.useCallback(()=>(p(),Math.max(0,l-o.current.length)),[p,l]),m=s.useCallback(()=>{if(p(),o.current.length===0)return 0;const h=o.current[0]+b;return Math.max(0,h-Date.now())},[p,b]),g=s.useCallback(()=>(p(),o.current.length<l),[p,l]),y=s.useCallback(()=>{if(f(),o.current.length>=l){const c=m(),h=Math.ceil(c/1e3);throw new Error(`Too many attempts. Please wait ${h} second${h===1?"":"s"} before trying again.`)}o.current.push(Date.now()),d(c=>c||!0),u()},[f,l,m,u]),x=s.useCallback(()=>{o.current=[],d(c=>c&&!1),u()},[u]);return s.useEffect(()=>{if(!w)return;const c=window.setInterval(()=>{f(),u()},1e3);return()=>{window.clearInterval(c)}},[w,u,f]),{checkLimit:y,isAllowed:g,getRemainingAttempts:v,getTimeUntilReset:m,reset:x}}function oe(r){return"mfaRequired"in r&&r.mfaRequired===!0}function F(){const{config:r,_internal:l}=j.useCedrosLogin(),[b,o]=s.useState(!1),[w,d]=s.useState(null),{checkLimit:k,getRemainingAttempts:u,getTimeUntilReset:p,reset:f}=q({maxAttempts:5,windowMs:6e4}),v=s.useMemo(()=>new j.ApiClient({baseUrl:r.serverUrl,timeoutMs:r.requestTimeout,retryAttempts:r.retryAttempts}),[r.serverUrl,r.requestTimeout,r.retryAttempts]),m=r.callbacks,g=r.features?.walletEnrollment!==!1,y=r.serverUrl,x=s.useCallback(async(t,a)=>{if(!M.validateEmail(t)){const i={code:"VALIDATION_ERROR",message:"Please enter a valid email address"};throw d(i),i}try{k()}catch(i){const n={code:"RATE_LIMITED",message:i instanceof Error?i.message:"Too many attempts"};throw d(n),n}o(!0),d(null);try{const i=await v.post("/login",{email:t,password:a});if(oe(i))return{mfaRequired:!0,mfaToken:i.mfaToken,email:t,userId:i.userId};const n=i;return m?.onLoginSuccess?.(n.user,"email"),l?.handleLoginSuccess(n.user,n.tokens),f(),{mfaRequired:!1,response:n}}catch(i){const n=j.handleApiError(i,"Login failed");throw d(n),n}finally{o(!1)}},[v,m,l,k,f]),c=s.useCallback(async(t,a,i)=>{if(!M.validateEmail(t)){const n={code:"VALIDATION_ERROR",message:"Please enter a valid email address"};throw d(n),n}try{k()}catch(n){const C={code:"RATE_LIMITED",message:n instanceof Error?n.message:"Too many attempts"};throw d(C),C}o(!0),d(null);try{const n=await v.post("/register",{email:t,password:a,name:i});if(m?.onLoginSuccess?.(n.user,"email"),l?.handleLoginSuccess(n.user,n.tokens),f(),g){const C=n.tokens?.accessToken??"";Promise.resolve().then(()=>require("./silentWalletEnroll-DBfS2sLe.cjs")).then(({silentWalletEnroll:E})=>E({password:a,serverUrl:y,accessToken:C})).then(E=>{E.success||console.warn("[useEmailAuth] Wallet auto-enrollment failed:",E.error)}).catch(E=>{const A=E instanceof Error?E.message:"Unknown error";console.warn("[useEmailAuth] Wallet auto-enrollment unavailable:",A)})}return n}catch(n){const C=j.handleApiError(n,"Registration failed");throw d(C),C}finally{o(!1)}},[v,m,l,k,f,y,g]),h=s.useCallback(()=>d(null),[]);return{login:x,register:c,isLoading:b,error:w,clearError:h,remainingAttempts:u(),timeUntilReset:p()}}function ae(r){return typeof r=="object"&&r!==null&&"mfaRequired"in r&&r.mfaRequired===!0}function H(){const{config:r,_internal:l}=j.useCedrosLogin(),[b,o]=s.useState(!1),[w,d]=s.useState(!1),[k,u]=s.useState(null),p=s.useMemo(()=>new j.ApiClient({baseUrl:r.serverUrl,timeoutMs:r.requestTimeout,retryAttempts:r.retryAttempts}),[r.serverUrl,r.requestTimeout,r.retryAttempts]),{checkLimit:f,getRemainingAttempts:v}=q({maxAttempts:3,windowMs:3e5}),m=s.useCallback(async c=>{if(!M.validateEmail(c)){const h={code:"VALIDATION_ERROR",message:"Please enter a valid email address"};throw u(h),h}try{f()}catch(h){const t={code:"RATE_LIMITED",message:h instanceof Error?h.message:"Too many attempts"};throw u(t),t}o(!0),u(null),d(!1);try{await p.post("/instant-link",{email:c}),d(!0)}catch(h){const t=j.handleApiError(h,"Failed to send sign-in link");throw u(t),t}finally{o(!1)}},[p,f]),g=s.useCallback(async c=>{if(!c||c.trim().length===0){const h={code:"VALIDATION_ERROR",message:"Invalid or missing sign-in link token"};throw u(h),h}o(!0),u(null),d(!1);try{const h=await p.post("/instant-link/verify",{token:c});return ae(h)||(r.callbacks?.onLoginSuccess?.(h.user,"email"),l?.handleLoginSuccess(h.user,h.tokens)),h}catch(h){const t=j.handleApiError(h,"Failed to verify sign-in link");throw u(t),t}finally{o(!1)}},[p,r.callbacks,l]),y=s.useCallback(()=>u(null),[]),x=s.useCallback(()=>{u(null),d(!1),o(!1)},[]);return{sendInstantLink:m,verifyInstantLink:g,isLoading:b,isSuccess:w,error:k,clearError:y,reset:x,remainingAttempts:v()}}function _({label:r="Password",labelAction:l,showStrengthMeter:b=!1,onValidationChange:o,error:w,className:d="",onChange:k,value:u,...p}){const[f,v]=s.useState(!1),[m,g]=s.useState(null),y=s.useId(),x=h=>{const t=h.target.value;if(b||o){const a=M.validatePassword(t);g(a),o?.(a)}k?.(h)},c={weak:"var(--cedros-destructive, #ef4444)",fair:"var(--cedros-warning, #f59e0b)",good:"var(--cedros-success, #22c55e)",strong:"var(--cedros-success, #22c55e)"};return e.jsxs("div",{className:`cedros-password-input ${d}`,children:[e.jsxs("div",{className:"cedros-label-row",children:[e.jsx("label",{htmlFor:y,className:"cedros-label",children:r}),l]}),e.jsxs("div",{className:"cedros-password-wrapper",children:[e.jsx("input",{id:y,type:f?"text":"password",className:"cedros-input",onChange:x,value:u,"aria-invalid":w?"true":void 0,"aria-describedby":w?`${y}-error`:void 0,...p}),e.jsx("button",{type:"button",className:"cedros-password-toggle",onClick:()=>v(!f),"aria-label":f?"Hide password":"Show password","aria-pressed":f,children:f?e.jsxs("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none","aria-hidden":"true",children:[e.jsx("path",{d:"M2.5 10s3-6 7.5-6 7.5 6 7.5 6-3 6-7.5 6-7.5-6-7.5-6z",stroke:"currentColor",strokeWidth:"1.5"}),e.jsx("circle",{cx:"10",cy:"10",r:"2.5",stroke:"currentColor",strokeWidth:"1.5"}),e.jsx("path",{d:"M3 17L17 3",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})]}):e.jsxs("svg",{width:"20",height:"20",viewBox:"0 0 20 20",fill:"none","aria-hidden":"true",children:[e.jsx("path",{d:"M2.5 10s3-6 7.5-6 7.5 6 7.5 6-3 6-7.5 6-7.5-6-7.5-6z",stroke:"currentColor",strokeWidth:"1.5"}),e.jsx("circle",{cx:"10",cy:"10",r:"2.5",stroke:"currentColor",strokeWidth:"1.5"})]})})]}),w&&e.jsx("p",{id:`${y}-error`,className:"cedros-input-error",children:w}),b&&m&&u?.length>0&&e.jsxs("div",{className:"cedros-password-strength",children:[e.jsx("div",{className:"cedros-strength-bar",children:e.jsx("div",{className:"cedros-strength-fill",style:{width:`${m.strength==="weak"?25:m.strength==="fair"?50:m.strength==="good"?75:100}%`,backgroundColor:c[m.strength]}})}),e.jsx("span",{className:"cedros-strength-label",children:m.strength})]})]})}function K(){const{config:r,_internal:l}=j.useCedrosLogin(),[b,o]=s.useState("idle"),[w,d]=s.useState(!1),[k,u]=s.useState(null),{checkLimit:p,getRemainingAttempts:f,getTimeUntilReset:v,reset:m}=q({maxAttempts:5,windowMs:12e4}),g=s.useMemo(()=>new j.ApiClient({baseUrl:r.serverUrl,timeoutMs:r.requestTimeout,retryAttempts:r.retryAttempts}),[r.serverUrl,r.requestTimeout,r.retryAttempts]),y=s.useCallback(async(h,t)=>{const a=/^[A-Z0-9]{16}$/i.test(t)||/^[A-Z0-9]{4}(-[A-Z0-9]{4}){3}$/i.test(t);if(!(/^\d{6}$/.test(t)||a)){const n={code:"VALIDATION_ERROR",message:"Please enter a valid 6-digit code or recovery code"};throw u(n),n}try{p()}catch(n){const C={code:"RATE_LIMITED",message:n instanceof Error?n.message:"Too many attempts"};throw u(C),C}d(!0),u(null),o("verifying");try{const n=await g.post("/login/mfa",{mfaToken:h,code:t});return o("success"),m(),l&&n.user&&n.tokens&&l.handleLoginSuccess(n.user,n.tokens),n}catch(n){const C=j.handleApiError(n,"Invalid verification code");throw u(C),o("error"),C}finally{d(!1)}},[g,l,p,m]),x=s.useCallback(()=>u(null),[]),c=s.useCallback(()=>{u(null),o("idle"),d(!1)},[]);return{state:b,isLoading:w,error:k,verifyTotp:y,clearError:x,reset:c,remainingAttempts:f(),timeUntilReset:v()}}const N=6;function Z({value:r="",onChange:l,onComplete:b,disabled:o=!1,error:w,autoFocus:d=!1,className:k=""}){const u=s.useRef([]),[p,f]=s.useState(r.padEnd(N,"")),v=s.useId();s.useEffect(()=>{f(r.padEnd(N,""))},[r]);const m=s.useCallback(t=>{t>=0&&t<N&&u.current[t]?.focus()},[]),g=s.useCallback(t=>{const a=t.replace(/\D/g,"").slice(0,N);f(a.padEnd(N,"")),l?.(a),a.length===N&&b?.(a)},[l,b]),y=s.useCallback((t,a)=>{if(!/^\d?$/.test(a))return;const i=p.split("");i[t]=a;const n=i.join("").replace(/ /g,"");g(n),a&&t<N-1&&m(t+1)},[p,g,m]),x=s.useCallback((t,a)=>{if(a.key==="Backspace"){a.preventDefault();const i=p.split("");i[t]&&i[t]!==" "?(i[t]=" ",g(i.join("").replace(/ /g,""))):t>0&&(i[t-1]=" ",g(i.join("").replace(/ /g,"")),m(t-1))}else a.key==="ArrowLeft"&&t>0?(a.preventDefault(),m(t-1)):a.key==="ArrowRight"&&t<N-1&&(a.preventDefault(),m(t+1))},[p,g,m]),c=s.useCallback(t=>{t.preventDefault();const i=t.clipboardData.getData("text").replace(/\D/g,"").slice(0,N);i&&(g(i),m(Math.min(i.length,N-1)))},[g,m]),h=s.useCallback(t=>{t.target.select()},[]);return s.useEffect(()=>{d&&!o&&u.current[0]?.focus()},[d,o]),e.jsxs("div",{className:`cedros-otp-input ${k}`,children:[e.jsx("div",{className:"cedros-otp-slots",role:"group","aria-label":"One-time password",children:Array.from({length:N}).map((t,a)=>e.jsx("input",{ref:i=>{u.current[a]=i},id:`${v}-${a}`,type:"text",inputMode:"numeric",pattern:"[0-9]*",maxLength:1,className:`cedros-otp-slot ${w?"cedros-otp-slot-error":""}`,value:p[a]===" "?"":p[a]||"",onChange:i=>y(a,i.target.value),onKeyDown:i=>x(a,i),onPaste:c,onFocus:h,disabled:o,autoComplete:"one-time-code","aria-label":`Digit ${a+1}`,"aria-invalid":w?"true":void 0},a))}),w&&e.jsx("p",{className:"cedros-otp-error",role:"alert",children:w})]})}function G({mfaToken:r,email:l,onSuccess:b,onBack:o,className:w=""}){const{verifyTotp:d,isLoading:k,error:u,clearError:p}=K(),[f,v]=s.useState(""),[m,g]=s.useState(!1),[y,x]=s.useState(""),c=async a=>{const i=a||(m?y:f);if(i)try{await d(r,i),b?.()}catch{m?x(""):v("")}},h=a=>{c(a)},t=()=>{g(!m),p(),v(""),x("")};return e.jsxs("div",{className:`cedros-totp-verify ${w}`,children:[e.jsxs("div",{className:"cedros-totp-verify-header",children:[e.jsxs("svg",{className:"cedros-totp-verify-icon",width:"48",height:"48",viewBox:"0 0 48 48",fill:"none","aria-hidden":"true",children:[e.jsx("rect",{x:"8",y:"20",width:"32",height:"24",rx:"4",stroke:"currentColor",strokeWidth:"2"}),e.jsx("path",{d:"M16 20V14a8 8 0 1 1 16 0v6",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round"}),e.jsx("circle",{cx:"24",cy:"32",r:"3",fill:"currentColor"})]}),e.jsx("h3",{className:"cedros-totp-title",children:"Two-factor authentication"}),e.jsx("p",{className:"cedros-totp-description",children:m?"Enter one of your recovery codes to sign in.":"Enter the 6-digit code from your authenticator app."}),l&&e.jsx("p",{className:"cedros-totp-email",children:l})]}),m?e.jsxs("div",{className:"cedros-totp-backup-input",children:[e.jsx("input",{type:"text",className:`cedros-input ${u?"cedros-input-error":""}`,placeholder:"Enter recovery code",value:y,onChange:a=>{x(a.target.value.toUpperCase()),p()},onKeyDown:a=>{a.key==="Enter"&&y&&c()},disabled:k,autoFocus:!0,autoComplete:"one-time-code"}),u&&e.jsx("p",{className:"cedros-input-error",role:"alert",children:u.message})]}):e.jsx(Z,{value:f,onChange:a=>{v(a),p()},onComplete:h,disabled:k,error:u?.message,autoFocus:!0}),e.jsx("button",{type:"button",className:"cedros-button cedros-button-primary cedros-button-md cedros-button-full",onClick:()=>c(),disabled:k||(m?!y:f.length!==6),children:k?e.jsxs(e.Fragment,{children:[e.jsx(j.LoadingSpinner,{size:"sm"}),e.jsx("span",{children:"Verifying..."})]}):"Verify"}),e.jsxs("div",{className:"cedros-totp-verify-footer",children:[e.jsx("button",{type:"button",className:"cedros-link cedros-link-sm",onClick:t,disabled:k,children:m?"Use authenticator app":"Use a recovery code"}),o&&e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"cedros-totp-verify-divider",children:"•"}),e.jsx("button",{type:"button",className:"cedros-link cedros-link-sm",onClick:o,disabled:k,children:"Back to login"})]})]})]})}function ne({onSuccess:r,onSwitchToRegister:l,onForgotPassword:b,className:o=""}){const{config:w}=j.useCedrosLogin(),{login:d,isLoading:k,error:u,clearError:p}=F(),{sendInstantLink:f,isLoading:v,isSuccess:m,error:g,clearError:y,reset:x}=H(),[c,h]=s.useState(""),[t,a]=s.useState(""),[i,n]=s.useState(null),[C,E]=s.useState(""),A=w.forms?.forgotPassword?.mode??"reset",O=async S=>{S.preventDefault();try{const I=await d(c,t);I.mfaRequired?(n(I.mfaToken),E(I.email)):r?.()}catch{}},T=()=>{n(null),E(""),r?.()},V=()=>{n(null),E(""),a("")},U=async()=>{if(A==="instantLink")try{await f(c)}catch{}else b?.()};if(i)return e.jsx(G,{mfaToken:i,email:C,onSuccess:T,onBack:V,className:o});if(m)return e.jsxs("div",{className:`cedros-instant-link-success ${o}`,children:[e.jsxs("svg",{className:"cedros-success-icon",width:"48",height:"48",viewBox:"0 0 48 48",fill:"none","aria-hidden":"true",children:[e.jsx("circle",{cx:"24",cy:"24",r:"22",stroke:"currentColor",strokeWidth:"2"}),e.jsx("path",{d:"M14 24l7 7 13-13",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})]}),e.jsx("h3",{className:"cedros-success-title",children:"Check your email"}),e.jsxs("p",{className:"cedros-success-message",children:["We sent a sign-in link to ",e.jsx("strong",{children:c}),". Click the link to sign in."]}),e.jsx("button",{type:"button",className:"cedros-button cedros-button-md cedros-button-outline",onClick:x,children:"Back to login"})]});const P=u||g,$=()=>{p(),y()},R=k||v;return e.jsxs("form",{onSubmit:O,className:`cedros-form ${o}`,children:[e.jsxs("div",{className:"cedros-form-field",children:[e.jsx("label",{htmlFor:"email",className:"cedros-label",children:"Email"}),e.jsx("input",{id:"email",type:"email",className:"cedros-input",value:c,onChange:S=>h(S.target.value),placeholder:"you@example.com",required:!0,"aria-required":"true",autoComplete:"email",disabled:R})]}),e.jsx("div",{className:"cedros-form-field",children:e.jsx(_,{value:t,onChange:S=>a(S.target.value),placeholder:"Enter your password",required:!0,autoComplete:"current-password",disabled:R,labelAction:b||A==="instantLink"?e.jsx("button",{type:"button",className:"cedros-link cedros-link-sm",onClick:U,disabled:v,children:v?"Sending...":"Forgot your password?"}):void 0})}),e.jsx(j.ErrorMessage,{error:P,onDismiss:$}),e.jsx("button",{type:"submit",className:"cedros-button cedros-button-primary cedros-button-md cedros-button-full",disabled:R||!c||!t,"aria-busy":k,children:k?e.jsxs(e.Fragment,{children:[e.jsx(j.LoadingSpinner,{size:"sm",announce:!0,label:"Signing in"}),e.jsx("span",{children:"Signing in..."})]}):"Sign in"}),l&&e.jsxs("p",{className:"cedros-form-footer",children:["Don't have an account?"," ",e.jsx("button",{type:"button",className:"cedros-link",onClick:l,children:"Sign up"})]})]})}function ie({onSuccess:r,onSwitchToLogin:l,className:b=""}){const{config:o}=j.useCedrosLogin(),{register:w,isLoading:d,error:k,clearError:u}=F(),[p,f]=s.useState(""),[v,m]=s.useState(""),[g,y]=s.useState(""),[x,c]=s.useState(""),[h,t]=s.useState(null),[a,i]=s.useState(null),n=o.forms?.termsOfService,C=o.forms?.emailOptIn,E=n?.show??!1,A=n?.required??!0,O=n?.defaultChecked??!1,T=n?.label??"I agree to the Terms of Service",V=n?.url,U=z(V),P=C?.show??!1,$=C?.defaultChecked??!1,R=C?.label??"Send me updates and news",[S,I]=s.useState(O),[Y,J]=s.useState($),D=g===x,Q=h?.isValid??!1,W=v&&g&&x&&D&&Q&&(!E||!A||S)&&!d,X=async L=>{if(L.preventDefault(),i(null),E&&A&&!S){i({code:"VALIDATION_ERROR",message:"You must agree to the Terms of Service to continue"});return}if(W)try{await w(v,g,p||void 0),r?.()}catch{}},ee=k||a,se=()=>{u(),i(null)};return e.jsxs("form",{onSubmit:X,className:`cedros-form ${b}`,children:[e.jsxs("div",{className:"cedros-form-field",children:[e.jsxs("label",{htmlFor:"name",className:"cedros-label",children:["Name ",e.jsx("span",{className:"cedros-optional",children:"(optional)"})]}),e.jsx("input",{id:"name",type:"text",className:"cedros-input",value:p,onChange:L=>f(L.target.value),placeholder:"Your name",autoComplete:"name",disabled:d})]}),e.jsxs("div",{className:"cedros-form-field",children:[e.jsx("label",{htmlFor:"register-email",className:"cedros-label",children:"Email"}),e.jsx("input",{id:"register-email",type:"email",className:"cedros-input",value:v,onChange:L=>m(L.target.value),placeholder:"you@example.com",required:!0,"aria-required":"true",autoComplete:"email",disabled:d})]}),e.jsx("div",{className:"cedros-form-field",children:e.jsx(_,{value:g,onChange:L=>y(L.target.value),placeholder:"Create a password",required:!0,autoComplete:"new-password",disabled:d,showStrengthMeter:!0,onValidationChange:t})}),e.jsxs("div",{className:"cedros-form-field",children:[e.jsx("label",{htmlFor:"confirm-password",className:"cedros-label",children:"Confirm Password"}),e.jsx("input",{id:"confirm-password",type:"password",className:"cedros-input",value:x,onChange:L=>c(L.target.value),placeholder:"Confirm your password",required:!0,"aria-required":"true",autoComplete:"new-password",disabled:d,"aria-invalid":x&&!D?"true":void 0,"aria-describedby":x&&!D?"confirm-password-error":void 0}),x&&!D&&e.jsx("p",{id:"confirm-password-error",className:"cedros-input-error",role:"alert",children:"Passwords do not match"})]}),E&&e.jsx("div",{className:"cedros-form-field cedros-checkbox-field",children:e.jsxs("label",{className:"cedros-checkbox-label",children:[e.jsx("input",{type:"checkbox",className:"cedros-checkbox",checked:S,onChange:L=>I(L.target.checked),disabled:d,"aria-required":A}),e.jsxs("span",{className:"cedros-checkbox-text",children:[U?e.jsxs(e.Fragment,{children:[T.replace("Terms of Service","").trim()||"I agree to the"," ",e.jsx("a",{href:U,target:"_blank",rel:"noopener noreferrer",className:"cedros-link",children:"Terms of Service"})]}):T,A&&e.jsx("span",{className:"cedros-required",children:"*"})]})]})}),P&&e.jsx("div",{className:"cedros-form-field cedros-checkbox-field",children:e.jsxs("label",{className:"cedros-checkbox-label",children:[e.jsx("input",{type:"checkbox",className:"cedros-checkbox",checked:Y,onChange:L=>J(L.target.checked),disabled:d}),e.jsx("span",{className:"cedros-checkbox-text",children:R})]})}),e.jsx(j.ErrorMessage,{error:ee,onDismiss:se}),e.jsx("button",{type:"submit",className:"cedros-button cedros-button-primary cedros-button-md cedros-button-full",disabled:!W,"aria-busy":d,children:d?e.jsxs(e.Fragment,{children:[e.jsx(j.LoadingSpinner,{size:"sm",announce:!0,label:"Creating account"}),e.jsx("span",{children:"Creating account..."})]}):"Create account"}),l&&e.jsxs("p",{className:"cedros-form-footer",children:["Already have an account?"," ",e.jsx("button",{type:"button",className:"cedros-link",onClick:l,children:"Sign in"})]})]})}exports.EmailLoginForm=ne;exports.EmailRegisterForm=ie;exports.OtpInput=Z;exports.PasswordInput=_;exports.TotpVerify=G;exports.sanitizeExternalUrl=z;exports.sanitizeImageUrl=re;exports.useEmailAuth=F;exports.useInstantLink=H;exports.useRateLimiter=q;exports.useTotpVerify=K;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EmailRegisterForm-B-ys4E3C.cjs","sources":["../src/utils/sanitization.ts","../src/hooks/useRateLimiter.ts","../src/hooks/useEmailAuth.ts","../src/hooks/useInstantLink.ts","../src/components/email/PasswordInput.tsx","../src/hooks/useTotpVerify.ts","../src/components/totp/OtpInput.tsx","../src/components/totp/TotpVerify.tsx","../src/components/email/EmailLoginForm.tsx","../src/components/email/EmailRegisterForm.tsx"],"sourcesContent":["/**\n * Input sanitization utilities for security\n */\n\n/**\n * Allowed image URL protocols\n */\nconst SAFE_PROTOCOLS = ['https:'];\n\n/**\n * Dangerous protocols that should never be allowed\n */\nconst DANGEROUS_PROTOCOLS = ['javascript:', 'data:', 'vbscript:', 'file:'];\n\n/**\n * Sanitizes an image URL to prevent XSS and other attacks.\n *\n * ## Security Properties\n *\n * - Only allows HTTPS URLs (prevents protocol-based attacks)\n * - Blocks javascript:, data:, vbscript:, file: protocols\n * - Returns undefined for invalid URLs\n *\n * ## Limitations (S-20/SEC-08)\n *\n * This function validates the URL protocol but does NOT validate the domain.\n * Any HTTPS URL will pass validation, including URLs from untrusted domains.\n * This is intentional - domain allowlisting is application-specific and should\n * be implemented at the consumer level (see example below).\n *\n * If your application requires domain validation (e.g., only allowing images\n * from trusted CDNs), implement additional validation:\n *\n * @example Domain allowlisting (application-level)\n * ```ts\n * const ALLOWED_DOMAINS = ['cdn.example.com', 'images.trusted.org'];\n *\n * function isAllowedImageUrl(url: string): boolean {\n * const sanitized = sanitizeImageUrl(url);\n * if (!sanitized) return false;\n *\n * try {\n * const hostname = new URL(sanitized).hostname;\n * return ALLOWED_DOMAINS.some(d => hostname === d || hostname.endsWith('.' + d));\n * } catch {\n * return false;\n * }\n * }\n * ```\n *\n * @param url - The URL to sanitize\n * @returns The sanitized URL or undefined if invalid\n *\n * @example Basic usage\n * ```ts\n * sanitizeImageUrl('https://example.com/avatar.png') // 'https://example.com/avatar.png'\n * sanitizeImageUrl('javascript:alert(1)') // undefined\n * sanitizeImageUrl('data:image/svg+xml,...') // undefined\n * ```\n */\nexport function sanitizeImageUrl(url: string | undefined | null): string | undefined {\n if (!url || typeof url !== 'string') {\n return undefined;\n }\n\n // Trim whitespace\n const trimmedUrl = url.trim();\n if (!trimmedUrl) {\n return undefined;\n }\n\n // Check for dangerous protocols (case-insensitive)\n const lowerUrl = trimmedUrl.toLowerCase();\n for (const protocol of DANGEROUS_PROTOCOLS) {\n if (lowerUrl.startsWith(protocol)) {\n return undefined;\n }\n }\n\n // Try to parse as URL\n try {\n const parsed = new URL(trimmedUrl);\n\n // Only allow safe protocols\n if (!SAFE_PROTOCOLS.includes(parsed.protocol)) {\n return undefined;\n }\n\n // Return the original URL (preserves case)\n return trimmedUrl;\n } catch {\n // Invalid URL\n return undefined;\n }\n}\n\n/**\n * Sanitizes an external link URL for use in href attributes.\n *\n * Security goals:\n * - Block dangerous protocols (javascript:, data:, vbscript:, file:)\n * - Only allow http/https absolute URLs\n */\nexport function sanitizeExternalUrl(url: string | undefined | null): string | undefined {\n if (!url || typeof url !== 'string') {\n return undefined;\n }\n\n const trimmedUrl = url.trim();\n if (!trimmedUrl) {\n return undefined;\n }\n\n const lowerUrl = trimmedUrl.toLowerCase();\n for (const protocol of DANGEROUS_PROTOCOLS) {\n if (lowerUrl.startsWith(protocol)) {\n return undefined;\n }\n }\n\n try {\n const parsed = new URL(trimmedUrl);\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {\n return undefined;\n }\n return trimmedUrl;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Sanitizes user-provided text to prevent XSS in text content.\n * Escapes HTML special characters.\n *\n * @param text - The text to sanitize\n * @returns The sanitized text\n */\nexport function sanitizeText(text: string | undefined | null): string {\n if (!text || typeof text !== 'string') {\n return '';\n }\n\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n","import { useRef, useCallback, useState, useEffect } from 'react';\n\nexport interface UseRateLimiterOptions {\n /** Maximum number of attempts allowed within the window */\n maxAttempts?: number;\n /** Time window in milliseconds */\n windowMs?: number;\n}\n\nexport interface UseRateLimiterReturn {\n /**\n * Check if an action is allowed. Throws an error if rate limited.\n * Call this before performing the action.\n */\n checkLimit: () => void;\n /**\n * Check if an action is allowed without throwing.\n * Returns true if allowed, false if rate limited.\n */\n isAllowed: () => boolean;\n /**\n * Get remaining attempts in current window\n */\n getRemainingAttempts: () => number;\n /**\n * Get time until rate limit resets (in ms)\n */\n getTimeUntilReset: () => number;\n /**\n * Reset the rate limiter (e.g., after successful action)\n */\n reset: () => void;\n}\n\n/**\n * Rate limiting hook to prevent excessive API calls from the client.\n *\n * @param options - Rate limiter configuration\n * @returns Rate limiter functions\n *\n * @example\n * ```tsx\n * function LoginForm() {\n * const { checkLimit, getRemainingAttempts } = useRateLimiter({\n * maxAttempts: 5,\n * windowMs: 60000, // 1 minute\n * });\n *\n * const handleLogin = async () => {\n * try {\n * checkLimit(); // Throws if rate limited\n * await login(email, password);\n * } catch (err) {\n * if (err.message.includes('Too many attempts')) {\n * // Show rate limit message\n * }\n * }\n * };\n * }\n * ```\n */\nexport function useRateLimiter(options: UseRateLimiterOptions = {}): UseRateLimiterReturn {\n const { maxAttempts = 5, windowMs = 60000 } = options;\n\n // Store timestamps of recent attempts\n const attemptsRef = useRef<number[]>([]);\n const [hasAttempts, setHasAttempts] = useState(false);\n const [, setTick] = useState(0);\n\n const bump = useCallback(() => {\n setTick((value) => value + 1);\n }, []);\n\n /**\n * Remove expired attempts from the tracking array (no state update).\n * Safe to call during render for getter functions.\n */\n const cleanupAttemptsArray = useCallback(() => {\n const now = Date.now();\n attemptsRef.current = attemptsRef.current.filter((timestamp) => now - timestamp < windowMs);\n }, [windowMs]);\n\n /**\n * Remove expired attempts AND update hasAttempts state.\n * M-04: Use functional setState to avoid hasAttempts dependency cascade.\n * Only call from event handlers and effects, NOT during render.\n */\n const cleanupExpiredAttempts = useCallback(() => {\n cleanupAttemptsArray();\n // Functional update avoids dependency on hasAttempts\n setHasAttempts((current) => (attemptsRef.current.length === 0 && current ? false : current));\n }, [cleanupAttemptsArray]);\n\n /**\n * Get the number of remaining attempts\n * Uses cleanupAttemptsArray (no state) so safe to call during render.\n */\n const getRemainingAttempts = useCallback((): number => {\n cleanupAttemptsArray();\n return Math.max(0, maxAttempts - attemptsRef.current.length);\n }, [cleanupAttemptsArray, maxAttempts]);\n\n /**\n * Get time until rate limit resets\n * Uses cleanupAttemptsArray (no state) so safe to call during render.\n */\n const getTimeUntilReset = useCallback((): number => {\n cleanupAttemptsArray();\n if (attemptsRef.current.length === 0) {\n return 0;\n }\n const oldestAttempt = attemptsRef.current[0];\n const resetTime = oldestAttempt + windowMs;\n return Math.max(0, resetTime - Date.now());\n }, [cleanupAttemptsArray, windowMs]);\n\n /**\n * Check if an action is allowed without throwing\n * Uses cleanupAttemptsArray (no state) so safe to call during render.\n */\n const isAllowed = useCallback((): boolean => {\n cleanupAttemptsArray();\n return attemptsRef.current.length < maxAttempts;\n }, [cleanupAttemptsArray, maxAttempts]);\n\n /**\n * Check rate limit and throw if exceeded\n */\n const checkLimit = useCallback((): void => {\n cleanupExpiredAttempts();\n\n if (attemptsRef.current.length >= maxAttempts) {\n const waitTime = getTimeUntilReset();\n const waitSeconds = Math.ceil(waitTime / 1000);\n throw new Error(\n `Too many attempts. Please wait ${waitSeconds} second${waitSeconds === 1 ? '' : 's'} before trying again.`\n );\n }\n\n // Record this attempt\n attemptsRef.current.push(Date.now());\n // M-04: Functional update avoids hasAttempts dependency\n setHasAttempts((current) => (current ? current : true));\n bump();\n }, [cleanupExpiredAttempts, maxAttempts, getTimeUntilReset, bump]);\n\n /**\n * Reset the rate limiter\n */\n const reset = useCallback((): void => {\n attemptsRef.current = [];\n // M-04: Functional update avoids hasAttempts dependency\n setHasAttempts((current) => (current ? false : current));\n bump();\n }, [bump]);\n\n useEffect(() => {\n if (!hasAttempts) return;\n const intervalId = window.setInterval(() => {\n cleanupExpiredAttempts();\n bump();\n }, 1000);\n return () => {\n window.clearInterval(intervalId);\n };\n }, [hasAttempts, bump, cleanupExpiredAttempts]);\n\n return {\n checkLimit,\n isAllowed,\n getRemainingAttempts,\n getTimeUntilReset,\n reset,\n };\n}\n","import { useState, useCallback, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport { validateEmail } from '../utils/validation';\nimport { useRateLimiter } from './useRateLimiter';\nimport type { AuthResponse, AuthError } from '../types';\nimport type { MfaRequiredResponse } from '../types';\n\nfunction isMfaRequiredResponse(\n response: AuthResponse | MfaRequiredResponse\n): response is MfaRequiredResponse {\n return 'mfaRequired' in response && response.mfaRequired === true;\n}\n\n/** Result when MFA verification is required */\nexport interface MfaRequiredResult {\n mfaRequired: true;\n mfaToken: string;\n email: string;\n userId: string;\n}\n\n/** Result of successful login (no TOTP required or after TOTP verification) */\nexport interface LoginSuccessResult {\n mfaRequired: false;\n response: AuthResponse;\n}\n\n/** Union type for login result */\nexport type LoginResult = MfaRequiredResult | LoginSuccessResult;\n\nexport interface UseEmailAuthReturn {\n /** Login - may return mfaRequired if 2FA is enabled */\n login: (email: string, password: string) => Promise<LoginResult>;\n register: (email: string, password: string, name?: string) => Promise<AuthResponse>;\n isLoading: boolean;\n error: AuthError | null;\n clearError: () => void;\n /**\n * Number of remaining login attempts before rate limiting.\n *\n * M-10: Snapshot Behavior\n * This value is a point-in-time snapshot computed at render time.\n * It may be briefly stale during rapid requests or concurrent renders.\n * For UI display only - actual rate limiting is enforced inside login/register.\n */\n remainingAttempts: number;\n /**\n * Time in ms until rate limit resets (0 if not rate limited).\n *\n * M-10: Snapshot Behavior\n * This value is a point-in-time snapshot computed at render time.\n * It may be briefly stale - use for UI display, not for logic decisions.\n */\n timeUntilReset: number;\n}\n\n/**\n * Hook for email/password authentication.\n *\n * @example\n * ```tsx\n * function LoginForm() {\n * const { login, isLoading, error } = useEmailAuth();\n *\n * const handleSubmit = async (e) => {\n * e.preventDefault();\n * try {\n * await login(email, password);\n * } catch (err) {\n * // Handle error\n * }\n * };\n * }\n * ```\n */\nexport function useEmailAuth(): UseEmailAuthReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n // Rate limiter for login attempts (5 attempts per minute)\n const {\n checkLimit,\n getRemainingAttempts,\n getTimeUntilReset,\n reset: resetRateLimit,\n } = useRateLimiter({ maxAttempts: 5, windowMs: 60000 });\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n const callbacks = config.callbacks;\n const walletEnrollmentEnabled = config.features?.walletEnrollment !== false;\n const serverUrl = config.serverUrl;\n\n const login = useCallback(\n async (email: string, password: string): Promise<LoginResult> => {\n // Validate email format before API call\n if (!validateEmail(email)) {\n const validationError: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Please enter a valid email address',\n };\n setError(validationError);\n throw validationError;\n }\n\n // UI-7: Rate limit is checked BEFORE API call intentionally.\n // This prevents brute force attacks by limiting attempt frequency,\n // not just successful request frequency.\n try {\n checkLimit();\n } catch (err) {\n const rateLimitError: AuthError = {\n code: 'RATE_LIMITED',\n message: err instanceof Error ? err.message : 'Too many attempts',\n };\n setError(rateLimitError);\n throw rateLimitError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<AuthResponse | MfaRequiredResponse>('/login', {\n email,\n password,\n });\n\n // Check if MFA verification is required\n if (isMfaRequiredResponse(data)) {\n return {\n mfaRequired: true,\n mfaToken: data.mfaToken,\n email,\n userId: data.userId,\n };\n }\n\n // Normal login success\n const authResponse: AuthResponse = data;\n callbacks?.onLoginSuccess?.(authResponse.user, 'email');\n _internal?.handleLoginSuccess(authResponse.user, authResponse.tokens);\n resetRateLimit(); // Reset on successful login\n return {\n mfaRequired: false,\n response: authResponse,\n };\n } catch (err) {\n const authError = handleApiError(err, 'Login failed');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, callbacks, _internal, checkLimit, resetRateLimit]\n );\n\n const register = useCallback(\n async (email: string, password: string, name?: string): Promise<AuthResponse> => {\n // Validate email format before API call\n if (!validateEmail(email)) {\n const validationError: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Please enter a valid email address',\n };\n setError(validationError);\n throw validationError;\n }\n\n // UI-7: Rate limit is checked BEFORE API call intentionally.\n // This prevents brute force attacks by limiting attempt frequency,\n // not just successful request frequency.\n try {\n checkLimit();\n } catch (err) {\n const rateLimitError: AuthError = {\n code: 'RATE_LIMITED',\n message: err instanceof Error ? err.message : 'Too many attempts',\n };\n setError(rateLimitError);\n throw rateLimitError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<AuthResponse>('/register', { email, password, name });\n callbacks?.onLoginSuccess?.(data.user, 'email');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n resetRateLimit(); // Reset on successful registration\n\n // Auto-enroll embedded wallet in background (don't block registration)\n // Uses password for Share A encryption. Recovery phrase can be retrieved later.\n if (walletEnrollmentEnabled) {\n const accessToken = data.tokens?.accessToken ?? '';\n void import('../utils/silentWalletEnroll')\n .then(({ silentWalletEnroll }) =>\n silentWalletEnroll({\n password,\n serverUrl,\n accessToken,\n })\n )\n .then((result) => {\n if (!result.success) {\n // Log auto-enrollment failures for debugging\n console.warn('[useEmailAuth] Wallet auto-enrollment failed:', result.error);\n }\n })\n .catch((err) => {\n const message = err instanceof Error ? err.message : 'Unknown error';\n // Log enrollment errors for debugging\n console.warn('[useEmailAuth] Wallet auto-enrollment unavailable:', message);\n });\n }\n\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Registration failed');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [\n apiClient,\n callbacks,\n _internal,\n checkLimit,\n resetRateLimit,\n serverUrl,\n walletEnrollmentEnabled,\n ]\n );\n\n const clearError = useCallback(() => setError(null), []);\n\n return {\n login,\n register,\n isLoading,\n error,\n clearError,\n // M-10: Point-in-time snapshots for UI display (see interface JSDoc)\n remainingAttempts: getRemainingAttempts(),\n timeUntilReset: getTimeUntilReset(),\n };\n}\n","import { useState, useCallback, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { validateEmail } from '../utils/validation';\nimport { useRateLimiter } from './useRateLimiter';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport type { AuthError, AuthResponse, MfaRequiredResponse } from '../types';\n\nfunction isMfaRequiredResponse(data: unknown): data is MfaRequiredResponse {\n return (\n typeof data === 'object' &&\n data !== null &&\n 'mfaRequired' in data &&\n (data as { mfaRequired?: unknown }).mfaRequired === true\n );\n}\n\nexport interface UseInstantLinkReturn {\n /** Send an instant link email to the given address */\n sendInstantLink: (email: string) => Promise<void>;\n /** Verify an instant link token and sign in */\n verifyInstantLink: (token: string) => Promise<AuthResponse | MfaRequiredResponse>;\n /** Whether a request is in progress */\n isLoading: boolean;\n /** Whether the instant link was sent successfully */\n isSuccess: boolean;\n /** Error from the last request */\n error: AuthError | null;\n /** Clear the error state */\n clearError: () => void;\n /** Reset to initial state */\n reset: () => void;\n /** Number of remaining attempts before rate limiting */\n remainingAttempts: number;\n}\n\n/**\n * Hook for instant link (passwordless) authentication.\n *\n * Sends an instant link email that allows the user to sign in\n * without entering their password.\n *\n * @example\n * ```tsx\n * function InstantLinkForm() {\n * const { sendInstantLink, isLoading, isSuccess, error } = useInstantLink();\n *\n * const handleSubmit = async (e) => {\n * e.preventDefault();\n * await sendInstantLink(email);\n * };\n *\n * if (isSuccess) {\n * return <p>Check your email for the sign-in link</p>;\n * }\n * }\n * ```\n */\nexport function useInstantLink(): UseInstantLinkReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [isSuccess, setIsSuccess] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n // Rate limiter for instant link attempts (3 attempts per 5 minutes)\n const { checkLimit, getRemainingAttempts } = useRateLimiter({\n maxAttempts: 3,\n windowMs: 300000,\n });\n\n const sendInstantLink = useCallback(\n async (email: string): Promise<void> => {\n // Validate email format before API call\n if (!validateEmail(email)) {\n const validationError: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Please enter a valid email address',\n };\n setError(validationError);\n throw validationError;\n }\n\n // Check rate limit before API call\n try {\n checkLimit();\n } catch (err) {\n const rateLimitError: AuthError = {\n code: 'RATE_LIMITED',\n message: err instanceof Error ? err.message : 'Too many attempts',\n };\n setError(rateLimitError);\n throw rateLimitError;\n }\n\n setIsLoading(true);\n setError(null);\n setIsSuccess(false);\n\n try {\n await apiClient.post('/instant-link', { email });\n setIsSuccess(true);\n } catch (err) {\n const authError = handleApiError(err, 'Failed to send sign-in link');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, checkLimit]\n );\n\n const verifyInstantLink = useCallback(\n async (token: string): Promise<AuthResponse | MfaRequiredResponse> => {\n if (!token || token.trim().length === 0) {\n const validationError: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Invalid or missing sign-in link token',\n };\n setError(validationError);\n throw validationError;\n }\n\n setIsLoading(true);\n setError(null);\n setIsSuccess(false);\n\n try {\n const data = await apiClient.post<AuthResponse | MfaRequiredResponse>(\n '/instant-link/verify',\n {\n token,\n }\n );\n\n if (isMfaRequiredResponse(data)) {\n return data;\n }\n\n // Successful login\n config.callbacks?.onLoginSuccess?.(data.user, 'email');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Failed to verify sign-in link');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, config.callbacks, _internal]\n );\n\n const clearError = useCallback(() => setError(null), []);\n\n const reset = useCallback(() => {\n setError(null);\n setIsSuccess(false);\n setIsLoading(false);\n }, []);\n\n return {\n sendInstantLink,\n verifyInstantLink,\n isLoading,\n isSuccess,\n error,\n clearError,\n reset,\n remainingAttempts: getRemainingAttempts(),\n };\n}\n","import { useState, useId, type InputHTMLAttributes } from 'react';\nimport { validatePassword } from '../../utils/validation';\nimport type { PasswordValidation } from '../../types';\n\nexport interface PasswordInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {\n label?: string;\n /** Action element shown on the right side of the label (e.g., \"Forgot password?\" link) */\n labelAction?: React.ReactNode;\n showStrengthMeter?: boolean;\n onValidationChange?: (validation: PasswordValidation) => void;\n error?: string;\n}\n\n/**\n * Password input with visibility toggle and optional strength meter\n */\nexport function PasswordInput({\n label = 'Password',\n labelAction,\n showStrengthMeter = false,\n onValidationChange,\n error,\n className = '',\n onChange,\n value,\n ...props\n}: PasswordInputProps) {\n const [showPassword, setShowPassword] = useState(false);\n const [validation, setValidation] = useState<PasswordValidation | null>(null);\n const inputId = useId();\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n\n if (showStrengthMeter || onValidationChange) {\n const newValidation = validatePassword(newValue);\n setValidation(newValidation);\n onValidationChange?.(newValidation);\n }\n\n onChange?.(e);\n };\n\n const strengthColors = {\n weak: 'var(--cedros-destructive, #ef4444)',\n fair: 'var(--cedros-warning, #f59e0b)',\n good: 'var(--cedros-success, #22c55e)',\n strong: 'var(--cedros-success, #22c55e)',\n };\n\n return (\n <div className={`cedros-password-input ${className}`}>\n <div className=\"cedros-label-row\">\n <label htmlFor={inputId} className=\"cedros-label\">\n {label}\n </label>\n {labelAction}\n </div>\n <div className=\"cedros-password-wrapper\">\n <input\n id={inputId}\n type={showPassword ? 'text' : 'password'}\n className=\"cedros-input\"\n onChange={handleChange}\n value={value}\n aria-invalid={error ? 'true' : undefined}\n aria-describedby={error ? `${inputId}-error` : undefined}\n {...props}\n />\n <button\n type=\"button\"\n className=\"cedros-password-toggle\"\n onClick={() => setShowPassword(!showPassword)}\n aria-label={showPassword ? 'Hide password' : 'Show password'}\n aria-pressed={showPassword}\n >\n {showPassword ? (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M2.5 10s3-6 7.5-6 7.5 6 7.5 6-3 6-7.5 6-7.5-6-7.5-6z\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n />\n <circle cx=\"10\" cy=\"10\" r=\"2.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <path d=\"M3 17L17 3\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n ) : (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M2.5 10s3-6 7.5-6 7.5 6 7.5 6-3 6-7.5 6-7.5-6-7.5-6z\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n />\n <circle cx=\"10\" cy=\"10\" r=\"2.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n </svg>\n )}\n </button>\n </div>\n\n {error && (\n <p id={`${inputId}-error`} className=\"cedros-input-error\">\n {error}\n </p>\n )}\n\n {showStrengthMeter && validation && (value as string)?.length > 0 && (\n <div className=\"cedros-password-strength\">\n <div className=\"cedros-strength-bar\">\n <div\n className=\"cedros-strength-fill\"\n style={{\n width: `${\n validation.strength === 'weak'\n ? 25\n : validation.strength === 'fair'\n ? 50\n : validation.strength === 'good'\n ? 75\n : 100\n }%`,\n backgroundColor: strengthColors[validation.strength],\n }}\n />\n </div>\n <span className=\"cedros-strength-label\">{validation.strength}</span>\n </div>\n )}\n </div>\n );\n}\n","import { useState, useCallback, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport { useRateLimiter } from './useRateLimiter';\nimport type { AuthError, AuthResponse, TotpVerifyState } from '../types';\n\nexport interface UseTotpVerifyReturn {\n /** Verification state */\n state: TotpVerifyState;\n /** Whether verification is in progress */\n isLoading: boolean;\n /** Error from the last request */\n error: AuthError | null;\n /** Verify MFA code during login */\n verifyTotp: (mfaToken: string, code: string) => Promise<AuthResponse>;\n /** Clear error state */\n clearError: () => void;\n /** Reset to initial state */\n reset: () => void;\n /** Number of remaining verification attempts before rate limiting */\n remainingAttempts: number;\n /** Time in ms until rate limit resets (0 if not rate limited) */\n timeUntilReset: number;\n}\n\n/**\n * Hook for verifying TOTP codes during the login flow.\n *\n * Used when a user has TOTP enabled and needs to provide\n * their 6-digit code after password authentication.\n *\n * @example\n * ```tsx\n * function TotpVerifyStep({ mfaToken }) {\n * const { verifyTotp, isLoading, error } = useTotpVerify();\n *\n * const handleVerify = async (code: string) => {\n * const response = await verifyTotp(mfaToken, code);\n * // User is now authenticated\n * };\n * }\n * ```\n */\nexport function useTotpVerify(): UseTotpVerifyReturn {\n const { config, _internal } = useCedrosLogin();\n const [state, setState] = useState<TotpVerifyState>('idle');\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n // Rate limiter for TOTP verification (5 attempts per 2 minutes)\n // Stricter than login to prevent brute force on short codes\n const {\n checkLimit,\n getRemainingAttempts,\n getTimeUntilReset,\n reset: resetRateLimit,\n } = useRateLimiter({ maxAttempts: 5, windowMs: 120000 });\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n const verifyTotp = useCallback(\n async (mfaToken: string, code: string): Promise<AuthResponse> => {\n // Validate code format (6 digits or recovery code)\n const isRecoveryCode =\n /^[A-Z0-9]{16}$/i.test(code) || /^[A-Z0-9]{4}(-[A-Z0-9]{4}){3}$/i.test(code);\n const isValidCode = /^\\d{6}$/.test(code) || isRecoveryCode;\n if (!isValidCode) {\n const validationError: AuthError = {\n code: 'VALIDATION_ERROR',\n message: 'Please enter a valid 6-digit code or recovery code',\n };\n setError(validationError);\n throw validationError;\n }\n\n // Rate limit check before API call to prevent brute force\n try {\n checkLimit();\n } catch (err) {\n const rateLimitError: AuthError = {\n code: 'RATE_LIMITED',\n message: err instanceof Error ? err.message : 'Too many attempts',\n };\n setError(rateLimitError);\n throw rateLimitError;\n }\n\n setIsLoading(true);\n setError(null);\n setState('verifying');\n\n try {\n const response = await apiClient.post<AuthResponse>('/login/mfa', { mfaToken, code });\n\n setState('success');\n resetRateLimit(); // Reset on successful verification\n\n // Complete authentication via internal API\n if (_internal && response.user && response.tokens) {\n _internal.handleLoginSuccess(response.user, response.tokens);\n }\n\n return response;\n } catch (err) {\n const authError = handleApiError(err, 'Invalid verification code');\n setError(authError);\n setState('error');\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, _internal, checkLimit, resetRateLimit]\n );\n\n const clearError = useCallback(() => setError(null), []);\n\n const reset = useCallback(() => {\n setError(null);\n setState('idle');\n setIsLoading(false);\n }, []);\n\n return {\n state,\n isLoading,\n error,\n verifyTotp,\n clearError,\n reset,\n // Point-in-time snapshots for UI display\n remainingAttempts: getRemainingAttempts(),\n timeUntilReset: getTimeUntilReset(),\n };\n}\n","/**\n * OTP Input component (shadcn-style)\n *\n * A 6-digit input with separate boxes for each digit,\n * designed for TOTP verification codes.\n */\n\nimport { useRef, useCallback, useState, useEffect, useId } from 'react';\n\n/** Number of OTP digits */\nconst OTP_LENGTH = 6;\n\nexport interface OtpInputProps {\n /** Current value (up to 6 digits) */\n value?: string;\n /** Called when the value changes */\n onChange?: (value: string) => void;\n /** Called when all 6 digits are entered */\n onComplete?: (value: string) => void;\n /** Whether the input is disabled */\n disabled?: boolean;\n /** Error message to display */\n error?: string;\n /** Auto-focus the first input on mount */\n autoFocus?: boolean;\n /** Additional CSS class */\n className?: string;\n}\n\n/**\n * OTP input with separate boxes for each digit (shadcn pattern)\n *\n * Features:\n * - Auto-advances to next input on digit entry\n * - Backspace moves to previous input\n * - Supports paste of full code\n * - Numeric keyboard on mobile\n */\nexport function OtpInput({\n value = '',\n onChange,\n onComplete,\n disabled = false,\n error,\n autoFocus = false,\n className = '',\n}: OtpInputProps) {\n const inputRefs = useRef<(HTMLInputElement | null)[]>([]);\n const [localValue, setLocalValue] = useState(value.padEnd(OTP_LENGTH, ''));\n const id = useId();\n\n // Sync with controlled value\n useEffect(() => {\n setLocalValue(value.padEnd(OTP_LENGTH, ''));\n }, [value]);\n\n const focusInput = useCallback((index: number) => {\n if (index >= 0 && index < OTP_LENGTH) {\n inputRefs.current[index]?.focus();\n }\n }, []);\n\n const updateValue = useCallback(\n (newValue: string) => {\n const sanitized = newValue.replace(/\\D/g, '').slice(0, OTP_LENGTH);\n setLocalValue(sanitized.padEnd(OTP_LENGTH, ''));\n onChange?.(sanitized);\n\n if (sanitized.length === OTP_LENGTH) {\n onComplete?.(sanitized);\n }\n },\n [onChange, onComplete]\n );\n\n const handleChange = useCallback(\n (index: number, digit: string) => {\n if (!/^\\d?$/.test(digit)) return;\n\n const chars = localValue.split('');\n chars[index] = digit;\n const newValue = chars.join('').replace(/ /g, '');\n updateValue(newValue);\n\n // Move to next input if digit entered\n if (digit && index < OTP_LENGTH - 1) {\n focusInput(index + 1);\n }\n },\n [localValue, updateValue, focusInput]\n );\n\n const handleKeyDown = useCallback(\n (index: number, e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Backspace') {\n e.preventDefault();\n const chars = localValue.split('');\n\n if (chars[index] && chars[index] !== ' ') {\n // Clear current digit\n chars[index] = ' ';\n updateValue(chars.join('').replace(/ /g, ''));\n } else if (index > 0) {\n // Move to previous and clear it\n chars[index - 1] = ' ';\n updateValue(chars.join('').replace(/ /g, ''));\n focusInput(index - 1);\n }\n } else if (e.key === 'ArrowLeft' && index > 0) {\n e.preventDefault();\n focusInput(index - 1);\n } else if (e.key === 'ArrowRight' && index < OTP_LENGTH - 1) {\n e.preventDefault();\n focusInput(index + 1);\n }\n },\n [localValue, updateValue, focusInput]\n );\n\n const handlePaste = useCallback(\n (e: React.ClipboardEvent) => {\n e.preventDefault();\n const pasted = e.clipboardData.getData('text');\n const digits = pasted.replace(/\\D/g, '').slice(0, OTP_LENGTH);\n if (digits) {\n updateValue(digits);\n // Focus the next empty slot or the last one\n focusInput(Math.min(digits.length, OTP_LENGTH - 1));\n }\n },\n [updateValue, focusInput]\n );\n\n const handleFocus = useCallback((e: React.FocusEvent<HTMLInputElement>) => {\n e.target.select();\n }, []);\n\n // Auto-focus first input\n useEffect(() => {\n if (autoFocus && !disabled) {\n inputRefs.current[0]?.focus();\n }\n }, [autoFocus, disabled]);\n\n return (\n <div className={`cedros-otp-input ${className}`}>\n <div className=\"cedros-otp-slots\" role=\"group\" aria-label=\"One-time password\">\n {Array.from({ length: OTP_LENGTH }).map((_, index) => (\n <input\n key={index}\n ref={(el) => {\n inputRefs.current[index] = el;\n }}\n id={`${id}-${index}`}\n type=\"text\"\n inputMode=\"numeric\"\n pattern=\"[0-9]*\"\n maxLength={1}\n className={`cedros-otp-slot ${error ? 'cedros-otp-slot-error' : ''}`}\n value={localValue[index] === ' ' ? '' : localValue[index] || ''}\n onChange={(e) => handleChange(index, e.target.value)}\n onKeyDown={(e) => handleKeyDown(index, e)}\n onPaste={handlePaste}\n onFocus={handleFocus}\n disabled={disabled}\n autoComplete=\"one-time-code\"\n aria-label={`Digit ${index + 1}`}\n aria-invalid={error ? 'true' : undefined}\n />\n ))}\n </div>\n {error && (\n <p className=\"cedros-otp-error\" role=\"alert\">\n {error}\n </p>\n )}\n </div>\n );\n}\n","/**\n * TOTP Verification component for login flow\n *\n * Displayed when a user with 2FA enabled needs to\n * enter their verification code to complete login.\n */\n\nimport { useState } from 'react';\nimport { useTotpVerify } from '../../hooks/useTotpVerify';\nimport { OtpInput } from './OtpInput';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\n\nexport interface TotpVerifyProps {\n /** Temporary token from password authentication */\n mfaToken: string;\n /** Email address (for display) */\n email?: string;\n /** Called when verification succeeds */\n onSuccess?: () => void;\n /** Called when user wants to go back */\n onBack?: () => void;\n /** Additional CSS class */\n className?: string;\n}\n\n/**\n * Two-factor authentication verification for login.\n *\n * Accepts 6-digit codes from authenticator apps\n * or recovery codes for account recovery.\n */\nexport function TotpVerify({\n mfaToken,\n email,\n onSuccess,\n onBack,\n className = '',\n}: TotpVerifyProps) {\n const { verifyTotp, isLoading, error, clearError } = useTotpVerify();\n const [code, setCode] = useState('');\n const [useBackupCode, setUseBackupCode] = useState(false);\n const [backupCode, setBackupCode] = useState('');\n\n const handleVerify = async (verifyCode?: string) => {\n const codeToVerify = verifyCode || (useBackupCode ? backupCode : code);\n if (!codeToVerify) return;\n\n try {\n await verifyTotp(mfaToken, codeToVerify);\n onSuccess?.();\n } catch {\n // Error handled by hook, clear the input\n if (useBackupCode) {\n setBackupCode('');\n } else {\n setCode('');\n }\n }\n };\n\n const handleOtpComplete = (value: string) => {\n handleVerify(value);\n };\n\n const toggleBackupCode = () => {\n setUseBackupCode(!useBackupCode);\n clearError();\n setCode('');\n setBackupCode('');\n };\n\n return (\n <div className={`cedros-totp-verify ${className}`}>\n <div className=\"cedros-totp-verify-header\">\n <svg\n className=\"cedros-totp-verify-icon\"\n width=\"48\"\n height=\"48\"\n viewBox=\"0 0 48 48\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <rect x=\"8\" y=\"20\" width=\"32\" height=\"24\" rx=\"4\" stroke=\"currentColor\" strokeWidth=\"2\" />\n <path\n d=\"M16 20V14a8 8 0 1 1 16 0v6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n <circle cx=\"24\" cy=\"32\" r=\"3\" fill=\"currentColor\" />\n </svg>\n <h3 className=\"cedros-totp-title\">Two-factor authentication</h3>\n <p className=\"cedros-totp-description\">\n {useBackupCode\n ? 'Enter one of your recovery codes to sign in.'\n : 'Enter the 6-digit code from your authenticator app.'}\n </p>\n {email && <p className=\"cedros-totp-email\">{email}</p>}\n </div>\n\n {useBackupCode ? (\n <div className=\"cedros-totp-backup-input\">\n <input\n type=\"text\"\n className={`cedros-input ${error ? 'cedros-input-error' : ''}`}\n placeholder=\"Enter recovery code\"\n value={backupCode}\n onChange={(e) => {\n setBackupCode(e.target.value.toUpperCase());\n clearError();\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && backupCode) {\n handleVerify();\n }\n }}\n disabled={isLoading}\n autoFocus\n autoComplete=\"one-time-code\"\n />\n {error && (\n <p className=\"cedros-input-error\" role=\"alert\">\n {error.message}\n </p>\n )}\n </div>\n ) : (\n <OtpInput\n value={code}\n onChange={(value) => {\n setCode(value);\n clearError();\n }}\n onComplete={handleOtpComplete}\n disabled={isLoading}\n error={error?.message}\n autoFocus\n />\n )}\n\n <button\n type=\"button\"\n className=\"cedros-button cedros-button-primary cedros-button-md cedros-button-full\"\n onClick={() => handleVerify()}\n disabled={isLoading || (useBackupCode ? !backupCode : code.length !== 6)}\n >\n {isLoading ? (\n <>\n <LoadingSpinner size=\"sm\" />\n <span>Verifying...</span>\n </>\n ) : (\n 'Verify'\n )}\n </button>\n\n <div className=\"cedros-totp-verify-footer\">\n <button\n type=\"button\"\n className=\"cedros-link cedros-link-sm\"\n onClick={toggleBackupCode}\n disabled={isLoading}\n >\n {useBackupCode ? 'Use authenticator app' : 'Use a recovery code'}\n </button>\n\n {onBack && (\n <>\n <span className=\"cedros-totp-verify-divider\">•</span>\n <button\n type=\"button\"\n className=\"cedros-link cedros-link-sm\"\n onClick={onBack}\n disabled={isLoading}\n >\n Back to login\n </button>\n </>\n )}\n </div>\n </div>\n );\n}\n","import { useState, type FormEvent } from 'react';\nimport { useCedrosLogin } from '../../context/useCedrosLogin';\nimport { useEmailAuth } from '../../hooks/useEmailAuth';\nimport { useInstantLink } from '../../hooks/useInstantLink';\nimport { PasswordInput } from './PasswordInput';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\nimport { ErrorMessage } from '../shared/ErrorMessage';\nimport { TotpVerify } from '../totp/TotpVerify';\n// COMP-01: Email validation now handled by useInstantLink hook internally\n\nexport interface EmailLoginFormProps {\n onSuccess?: () => void;\n onSwitchToRegister?: () => void;\n /** Called when user clicks \"Forgot password?\" (only in 'reset' mode) */\n onForgotPassword?: () => void;\n className?: string;\n}\n\n/**\n * Email/password login form\n */\nexport function EmailLoginForm({\n onSuccess,\n onSwitchToRegister,\n onForgotPassword,\n className = '',\n}: EmailLoginFormProps) {\n const { config } = useCedrosLogin();\n const { login, isLoading, error, clearError } = useEmailAuth();\n const {\n sendInstantLink,\n isLoading: isInstantLinkLoading,\n isSuccess: isInstantLinkSuccess,\n error: instantLinkError,\n clearError: clearInstantLinkError,\n reset: resetInstantLink,\n } = useInstantLink();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n // MFA state for 2FA flow\n const [mfaToken, setMfaToken] = useState<string | null>(null);\n const [mfaEmail, setMfaEmail] = useState<string>('');\n\n const forgotPasswordMode = config.forms?.forgotPassword?.mode ?? 'reset';\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n try {\n const result = await login(email, password);\n if (result.mfaRequired) {\n // MFA verification needed\n setMfaToken(result.mfaToken);\n setMfaEmail(result.email);\n } else {\n // Login successful\n onSuccess?.();\n }\n } catch {\n // Error is handled by the hook\n }\n };\n\n const handleTotpSuccess = () => {\n setMfaToken(null);\n setMfaEmail('');\n onSuccess?.();\n };\n\n const handleTotpBack = () => {\n setMfaToken(null);\n setMfaEmail('');\n setPassword(''); // Clear password for security\n };\n\n const handleForgotPassword = async () => {\n if (forgotPasswordMode === 'instantLink') {\n // COMP-01: In instant link mode, call sendInstantLink directly.\n // The hook validates email and sets error state if invalid.\n try {\n await sendInstantLink(email);\n } catch {\n // Error handled by hook's error state\n }\n } else {\n // In reset mode, call the callback\n onForgotPassword?.();\n }\n };\n\n // Show TOTP verification step\n if (mfaToken) {\n return (\n <TotpVerify\n mfaToken={mfaToken}\n email={mfaEmail}\n onSuccess={handleTotpSuccess}\n onBack={handleTotpBack}\n className={className}\n />\n );\n }\n\n // Show instant link success state\n if (isInstantLinkSuccess) {\n return (\n <div className={`cedros-instant-link-success ${className}`}>\n <svg\n className=\"cedros-success-icon\"\n width=\"48\"\n height=\"48\"\n viewBox=\"0 0 48 48\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <circle cx=\"24\" cy=\"24\" r=\"22\" stroke=\"currentColor\" strokeWidth=\"2\" />\n <path\n d=\"M14 24l7 7 13-13\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n <h3 className=\"cedros-success-title\">Check your email</h3>\n <p className=\"cedros-success-message\">\n We sent a sign-in link to <strong>{email}</strong>. Click the link to sign in.\n </p>\n <button\n type=\"button\"\n className=\"cedros-button cedros-button-md cedros-button-outline\"\n onClick={resetInstantLink}\n >\n Back to login\n </button>\n </div>\n );\n }\n\n const combinedError = error || instantLinkError;\n const combinedClearError = () => {\n clearError();\n clearInstantLinkError();\n };\n const isAnyLoading = isLoading || isInstantLinkLoading;\n\n return (\n <form onSubmit={handleSubmit} className={`cedros-form ${className}`}>\n <div className=\"cedros-form-field\">\n <label htmlFor=\"email\" className=\"cedros-label\">\n Email\n </label>\n <input\n id=\"email\"\n type=\"email\"\n className=\"cedros-input\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"you@example.com\"\n required\n aria-required=\"true\"\n autoComplete=\"email\"\n disabled={isAnyLoading}\n />\n </div>\n\n <div className=\"cedros-form-field\">\n <PasswordInput\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n placeholder=\"Enter your password\"\n required\n autoComplete=\"current-password\"\n disabled={isAnyLoading}\n labelAction={\n onForgotPassword || forgotPasswordMode === 'instantLink' ? (\n <button\n type=\"button\"\n className=\"cedros-link cedros-link-sm\"\n onClick={handleForgotPassword}\n disabled={isInstantLinkLoading}\n >\n {isInstantLinkLoading ? 'Sending...' : 'Forgot your password?'}\n </button>\n ) : undefined\n }\n />\n </div>\n\n <ErrorMessage error={combinedError} onDismiss={combinedClearError} />\n\n <button\n type=\"submit\"\n className=\"cedros-button cedros-button-primary cedros-button-md cedros-button-full\"\n disabled={isAnyLoading || !email || !password}\n aria-busy={isLoading}\n >\n {isLoading ? (\n <>\n <LoadingSpinner size=\"sm\" announce label=\"Signing in\" />\n <span>Signing in...</span>\n </>\n ) : (\n 'Sign in'\n )}\n </button>\n\n {onSwitchToRegister && (\n <p className=\"cedros-form-footer\">\n Don't have an account?{' '}\n <button type=\"button\" className=\"cedros-link\" onClick={onSwitchToRegister}>\n Sign up\n </button>\n </p>\n )}\n </form>\n );\n}\n","import { useState, type FormEvent } from 'react';\nimport { useCedrosLogin } from '../../context/useCedrosLogin';\nimport { useEmailAuth } from '../../hooks/useEmailAuth';\nimport { PasswordInput } from './PasswordInput';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\nimport { ErrorMessage } from '../shared/ErrorMessage';\nimport { sanitizeExternalUrl } from '../../utils/sanitization';\nimport type { PasswordValidation, AuthError } from '../../types';\n\nexport interface EmailRegisterFormProps {\n onSuccess?: () => void;\n onSwitchToLogin?: () => void;\n className?: string;\n}\n\n/** Values collected from the registration form (for callback) */\nexport interface RegistrationData {\n termsAccepted: boolean;\n emailOptIn: boolean;\n}\n\n/**\n * Email/password registration form\n */\nexport function EmailRegisterForm({\n onSuccess,\n onSwitchToLogin,\n className = '',\n}: EmailRegisterFormProps) {\n const { config } = useCedrosLogin();\n const { register, isLoading, error, clearError } = useEmailAuth();\n const [name, setName] = useState('');\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [confirmPassword, setConfirmPassword] = useState('');\n const [passwordValidation, setPasswordValidation] = useState<PasswordValidation | null>(null);\n const [termsError, setTermsError] = useState<AuthError | null>(null);\n\n // Get form configuration\n const termsConfig = config.forms?.termsOfService;\n const emailOptInConfig = config.forms?.emailOptIn;\n\n const showTerms = termsConfig?.show ?? false;\n const termsRequired = termsConfig?.required ?? true;\n const termsDefaultChecked = termsConfig?.defaultChecked ?? false;\n const termsLabel = termsConfig?.label ?? 'I agree to the Terms of Service';\n const termsUrl = termsConfig?.url;\n const safeTermsUrl = sanitizeExternalUrl(termsUrl);\n\n const showEmailOptIn = emailOptInConfig?.show ?? false;\n const emailOptInDefaultChecked = emailOptInConfig?.defaultChecked ?? false;\n const emailOptInLabel = emailOptInConfig?.label ?? 'Send me updates and news';\n\n // Initialize checkbox states with defaults\n const [termsAccepted, setTermsAccepted] = useState(termsDefaultChecked);\n const [emailOptIn, setEmailOptIn] = useState(emailOptInDefaultChecked);\n\n const passwordsMatch = password === confirmPassword;\n const isPasswordValid = passwordValidation?.isValid ?? false;\n\n // Terms must be accepted if shown and required\n const termsValid = !showTerms || !termsRequired || termsAccepted;\n\n const canSubmit =\n email &&\n password &&\n confirmPassword &&\n passwordsMatch &&\n isPasswordValid &&\n termsValid &&\n !isLoading;\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n\n // Clear any previous terms error\n setTermsError(null);\n\n // Validate terms if required\n if (showTerms && termsRequired && !termsAccepted) {\n setTermsError({\n code: 'VALIDATION_ERROR',\n message: 'You must agree to the Terms of Service to continue',\n });\n return;\n }\n\n if (!canSubmit) return;\n\n try {\n // Note: termsAccepted and emailOptIn values can be passed to the backend\n // via the register function if needed. For now, we call register with the\n // standard params, but the backend could be extended to accept these values.\n await register(email, password, name || undefined);\n onSuccess?.();\n } catch {\n // Error is handled by the hook\n }\n };\n\n const combinedError = error || termsError;\n const combinedClearError = () => {\n clearError();\n setTermsError(null);\n };\n\n return (\n <form onSubmit={handleSubmit} className={`cedros-form ${className}`}>\n <div className=\"cedros-form-field\">\n <label htmlFor=\"name\" className=\"cedros-label\">\n Name <span className=\"cedros-optional\">(optional)</span>\n </label>\n <input\n id=\"name\"\n type=\"text\"\n className=\"cedros-input\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"Your name\"\n autoComplete=\"name\"\n disabled={isLoading}\n />\n </div>\n\n <div className=\"cedros-form-field\">\n <label htmlFor=\"register-email\" className=\"cedros-label\">\n Email\n </label>\n <input\n id=\"register-email\"\n type=\"email\"\n className=\"cedros-input\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"you@example.com\"\n required\n aria-required=\"true\"\n autoComplete=\"email\"\n disabled={isLoading}\n />\n </div>\n\n <div className=\"cedros-form-field\">\n <PasswordInput\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n placeholder=\"Create a password\"\n required\n autoComplete=\"new-password\"\n disabled={isLoading}\n showStrengthMeter\n onValidationChange={setPasswordValidation}\n />\n </div>\n\n <div className=\"cedros-form-field\">\n <label htmlFor=\"confirm-password\" className=\"cedros-label\">\n Confirm Password\n </label>\n <input\n id=\"confirm-password\"\n type=\"password\"\n className=\"cedros-input\"\n value={confirmPassword}\n onChange={(e) => setConfirmPassword(e.target.value)}\n placeholder=\"Confirm your password\"\n required\n aria-required=\"true\"\n autoComplete=\"new-password\"\n disabled={isLoading}\n aria-invalid={confirmPassword && !passwordsMatch ? 'true' : undefined}\n aria-describedby={\n confirmPassword && !passwordsMatch ? 'confirm-password-error' : undefined\n }\n />\n {confirmPassword && !passwordsMatch && (\n <p id=\"confirm-password-error\" className=\"cedros-input-error\" role=\"alert\">\n Passwords do not match\n </p>\n )}\n </div>\n\n {/* Terms of Service checkbox */}\n {showTerms && (\n <div className=\"cedros-form-field cedros-checkbox-field\">\n <label className=\"cedros-checkbox-label\">\n <input\n type=\"checkbox\"\n className=\"cedros-checkbox\"\n checked={termsAccepted}\n onChange={(e) => setTermsAccepted(e.target.checked)}\n disabled={isLoading}\n aria-required={termsRequired}\n />\n <span className=\"cedros-checkbox-text\">\n {safeTermsUrl ? (\n <>\n {termsLabel.replace('Terms of Service', '').trim() || 'I agree to the'}{' '}\n <a\n href={safeTermsUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"cedros-link\"\n >\n Terms of Service\n </a>\n </>\n ) : (\n termsLabel\n )}\n {termsRequired && <span className=\"cedros-required\">*</span>}\n </span>\n </label>\n </div>\n )}\n\n {/* Email opt-in checkbox */}\n {showEmailOptIn && (\n <div className=\"cedros-form-field cedros-checkbox-field\">\n <label className=\"cedros-checkbox-label\">\n <input\n type=\"checkbox\"\n className=\"cedros-checkbox\"\n checked={emailOptIn}\n onChange={(e) => setEmailOptIn(e.target.checked)}\n disabled={isLoading}\n />\n <span className=\"cedros-checkbox-text\">{emailOptInLabel}</span>\n </label>\n </div>\n )}\n\n <ErrorMessage error={combinedError} onDismiss={combinedClearError} />\n\n <button\n type=\"submit\"\n className=\"cedros-button cedros-button-primary cedros-button-md cedros-button-full\"\n disabled={!canSubmit}\n aria-busy={isLoading}\n >\n {isLoading ? (\n <>\n <LoadingSpinner size=\"sm\" announce label=\"Creating account\" />\n <span>Creating account...</span>\n </>\n ) : (\n 'Create account'\n )}\n </button>\n\n {onSwitchToLogin && (\n <p className=\"cedros-form-footer\">\n Already have an account?{' '}\n <button type=\"button\" className=\"cedros-link\" onClick={onSwitchToLogin}>\n Sign in\n </button>\n </p>\n )}\n </form>\n );\n}\n"],"names":["SAFE_PROTOCOLS","DANGEROUS_PROTOCOLS","sanitizeImageUrl","url","trimmedUrl","lowerUrl","protocol","parsed","sanitizeExternalUrl","useRateLimiter","options","maxAttempts","windowMs","attemptsRef","useRef","hasAttempts","setHasAttempts","useState","setTick","bump","useCallback","value","cleanupAttemptsArray","now","timestamp","cleanupExpiredAttempts","current","getRemainingAttempts","getTimeUntilReset","resetTime","isAllowed","checkLimit","waitTime","waitSeconds","reset","useEffect","intervalId","isMfaRequiredResponse","response","useEmailAuth","config","_internal","useCedrosLogin","isLoading","setIsLoading","error","setError","resetRateLimit","apiClient","useMemo","ApiClient","callbacks","walletEnrollmentEnabled","serverUrl","login","email","password","validateEmail","validationError","err","rateLimitError","data","authResponse","authError","handleApiError","register","name","accessToken","silentWalletEnroll","result","message","clearError","useInstantLink","isSuccess","setIsSuccess","sendInstantLink","verifyInstantLink","token","PasswordInput","label","labelAction","showStrengthMeter","onValidationChange","className","onChange","props","showPassword","setShowPassword","validation","setValidation","inputId","useId","handleChange","e","newValue","newValidation","validatePassword","strengthColors","jsxs","jsx","useTotpVerify","state","setState","verifyTotp","mfaToken","code","isRecoveryCode","OTP_LENGTH","OtpInput","onComplete","disabled","autoFocus","inputRefs","localValue","setLocalValue","id","focusInput","index","updateValue","sanitized","digit","chars","handleKeyDown","handlePaste","digits","handleFocus","_","el","TotpVerify","onSuccess","onBack","setCode","useBackupCode","setUseBackupCode","backupCode","setBackupCode","handleVerify","verifyCode","codeToVerify","handleOtpComplete","toggleBackupCode","Fragment","LoadingSpinner","EmailLoginForm","onSwitchToRegister","onForgotPassword","isInstantLinkLoading","isInstantLinkSuccess","instantLinkError","clearInstantLinkError","resetInstantLink","setEmail","setPassword","setMfaToken","mfaEmail","setMfaEmail","forgotPasswordMode","handleSubmit","handleTotpSuccess","handleTotpBack","handleForgotPassword","combinedError","combinedClearError","isAnyLoading","ErrorMessage","EmailRegisterForm","onSwitchToLogin","setName","confirmPassword","setConfirmPassword","passwordValidation","setPasswordValidation","termsError","setTermsError","termsConfig","emailOptInConfig","showTerms","termsRequired","termsDefaultChecked","termsLabel","termsUrl","safeTermsUrl","showEmailOptIn","emailOptInDefaultChecked","emailOptInLabel","termsAccepted","setTermsAccepted","emailOptIn","setEmailOptIn","passwordsMatch","isPasswordValid","canSubmit"],"mappings":"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"}
|