@entropy-softworks/ui 2026.7.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (590) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/LICENSE +21 -0
  3. package/README.md +115 -0
  4. package/lib/components/auth/LockOverlay.d.ts +22 -0
  5. package/lib/components/auth/LockOverlay.d.ts.map +1 -0
  6. package/lib/components/auth/LockOverlay.jsx +82 -0
  7. package/lib/components/auth/LockOverlay.jsx.map +1 -0
  8. package/lib/components/auth/PasswordStrengthMeter.d.ts +12 -0
  9. package/lib/components/auth/PasswordStrengthMeter.d.ts.map +1 -0
  10. package/lib/components/auth/PasswordStrengthMeter.jsx +81 -0
  11. package/lib/components/auth/PasswordStrengthMeter.jsx.map +1 -0
  12. package/lib/components/auth/ProtectedRoute.d.ts +24 -0
  13. package/lib/components/auth/ProtectedRoute.d.ts.map +1 -0
  14. package/lib/components/auth/ProtectedRoute.jsx +56 -0
  15. package/lib/components/auth/ProtectedRoute.jsx.map +1 -0
  16. package/lib/components/auth/PwnedPasswordWarning.d.ts +13 -0
  17. package/lib/components/auth/PwnedPasswordWarning.d.ts.map +1 -0
  18. package/lib/components/auth/PwnedPasswordWarning.jsx +29 -0
  19. package/lib/components/auth/PwnedPasswordWarning.jsx.map +1 -0
  20. package/lib/components/auth/index.d.ts +5 -0
  21. package/lib/components/auth/index.d.ts.map +1 -0
  22. package/lib/components/auth/index.js +5 -0
  23. package/lib/components/auth/index.js.map +1 -0
  24. package/lib/components/bottom-nav.d.ts +25 -0
  25. package/lib/components/bottom-nav.d.ts.map +1 -0
  26. package/lib/components/bottom-nav.jsx +51 -0
  27. package/lib/components/bottom-nav.jsx.map +1 -0
  28. package/lib/components/button.d.ts +16 -0
  29. package/lib/components/button.d.ts.map +1 -0
  30. package/lib/components/button.jsx +73 -0
  31. package/lib/components/button.jsx.map +1 -0
  32. package/lib/components/card.d.ts +22 -0
  33. package/lib/components/card.d.ts.map +1 -0
  34. package/lib/components/card.jsx +36 -0
  35. package/lib/components/card.jsx.map +1 -0
  36. package/lib/components/checkbox.d.ts +12 -0
  37. package/lib/components/checkbox.d.ts.map +1 -0
  38. package/lib/components/checkbox.jsx +16 -0
  39. package/lib/components/checkbox.jsx.map +1 -0
  40. package/lib/components/connection-settings.d.ts +41 -0
  41. package/lib/components/connection-settings.d.ts.map +1 -0
  42. package/lib/components/connection-settings.jsx +354 -0
  43. package/lib/components/connection-settings.jsx.map +1 -0
  44. package/lib/components/context-menu.d.ts +52 -0
  45. package/lib/components/context-menu.d.ts.map +1 -0
  46. package/lib/components/context-menu.jsx +24 -0
  47. package/lib/components/context-menu.jsx.map +1 -0
  48. package/lib/components/data-table.d.ts +59 -0
  49. package/lib/components/data-table.d.ts.map +1 -0
  50. package/lib/components/data-table.jsx +101 -0
  51. package/lib/components/data-table.jsx.map +1 -0
  52. package/lib/components/dialog.d.ts +58 -0
  53. package/lib/components/dialog.d.ts.map +1 -0
  54. package/lib/components/dialog.jsx +197 -0
  55. package/lib/components/dialog.jsx.map +1 -0
  56. package/lib/components/dropdown-menu.d.ts +55 -0
  57. package/lib/components/dropdown-menu.d.ts.map +1 -0
  58. package/lib/components/dropdown-menu.jsx +38 -0
  59. package/lib/components/dropdown-menu.jsx.map +1 -0
  60. package/lib/components/entropy-boot-splash.d.ts +24 -0
  61. package/lib/components/entropy-boot-splash.d.ts.map +1 -0
  62. package/lib/components/entropy-boot-splash.jsx +83 -0
  63. package/lib/components/entropy-boot-splash.jsx.map +1 -0
  64. package/lib/components/entropy-mark-animated.d.ts +20 -0
  65. package/lib/components/entropy-mark-animated.d.ts.map +1 -0
  66. package/lib/components/entropy-mark-animated.jsx +148 -0
  67. package/lib/components/entropy-mark-animated.jsx.map +1 -0
  68. package/lib/components/entropy-mark-geometry.d.ts +8 -0
  69. package/lib/components/entropy-mark-geometry.d.ts.map +1 -0
  70. package/lib/components/entropy-mark-geometry.js +10 -0
  71. package/lib/components/entropy-mark-geometry.js.map +1 -0
  72. package/lib/components/entropy-mark-sequential.d.ts +12 -0
  73. package/lib/components/entropy-mark-sequential.d.ts.map +1 -0
  74. package/lib/components/entropy-mark-sequential.jsx +53 -0
  75. package/lib/components/entropy-mark-sequential.jsx.map +1 -0
  76. package/lib/components/entropy-mark.d.ts +29 -0
  77. package/lib/components/entropy-mark.d.ts.map +1 -0
  78. package/lib/components/entropy-mark.jsx +66 -0
  79. package/lib/components/entropy-mark.jsx.map +1 -0
  80. package/lib/components/error-boundary.d.ts +31 -0
  81. package/lib/components/error-boundary.d.ts.map +1 -0
  82. package/lib/components/error-boundary.jsx +40 -0
  83. package/lib/components/error-boundary.jsx.map +1 -0
  84. package/lib/components/index.d.ts +41 -0
  85. package/lib/components/index.d.ts.map +1 -0
  86. package/lib/components/index.js +43 -0
  87. package/lib/components/index.js.map +1 -0
  88. package/lib/components/input.d.ts +26 -0
  89. package/lib/components/input.d.ts.map +1 -0
  90. package/lib/components/input.jsx +95 -0
  91. package/lib/components/input.jsx.map +1 -0
  92. package/lib/components/item-icon.d.ts +32 -0
  93. package/lib/components/item-icon.d.ts.map +1 -0
  94. package/lib/components/item-icon.jsx +54 -0
  95. package/lib/components/item-icon.jsx.map +1 -0
  96. package/lib/components/label.d.ts +13 -0
  97. package/lib/components/label.d.ts.map +1 -0
  98. package/lib/components/label.jsx +9 -0
  99. package/lib/components/label.jsx.map +1 -0
  100. package/lib/components/legal-dialog.d.ts +18 -0
  101. package/lib/components/legal-dialog.d.ts.map +1 -0
  102. package/lib/components/legal-dialog.jsx +31 -0
  103. package/lib/components/legal-dialog.jsx.map +1 -0
  104. package/lib/components/license-gate.d.ts +17 -0
  105. package/lib/components/license-gate.d.ts.map +1 -0
  106. package/lib/components/license-gate.jsx +21 -0
  107. package/lib/components/license-gate.jsx.map +1 -0
  108. package/lib/components/logo.d.ts +15 -0
  109. package/lib/components/logo.d.ts.map +1 -0
  110. package/lib/components/logo.jsx +20 -0
  111. package/lib/components/logo.jsx.map +1 -0
  112. package/lib/components/onboarding-wizard.d.ts +89 -0
  113. package/lib/components/onboarding-wizard.d.ts.map +1 -0
  114. package/lib/components/onboarding-wizard.jsx +185 -0
  115. package/lib/components/onboarding-wizard.jsx.map +1 -0
  116. package/lib/components/pagination.d.ts +43 -0
  117. package/lib/components/pagination.d.ts.map +1 -0
  118. package/lib/components/pagination.jsx +72 -0
  119. package/lib/components/pagination.jsx.map +1 -0
  120. package/lib/components/password-generator.d.ts +15 -0
  121. package/lib/components/password-generator.d.ts.map +1 -0
  122. package/lib/components/password-generator.jsx +273 -0
  123. package/lib/components/password-generator.jsx.map +1 -0
  124. package/lib/components/pin-pad.d.ts +20 -0
  125. package/lib/components/pin-pad.d.ts.map +1 -0
  126. package/lib/components/pin-pad.jsx +93 -0
  127. package/lib/components/pin-pad.jsx.map +1 -0
  128. package/lib/components/popover.d.ts +27 -0
  129. package/lib/components/popover.d.ts.map +1 -0
  130. package/lib/components/popover.jsx +18 -0
  131. package/lib/components/popover.jsx.map +1 -0
  132. package/lib/components/profile/ProfileDialog.d.ts +25 -0
  133. package/lib/components/profile/ProfileDialog.d.ts.map +1 -0
  134. package/lib/components/profile/ProfileDialog.jsx +71 -0
  135. package/lib/components/profile/ProfileDialog.jsx.map +1 -0
  136. package/lib/components/profile/ProfileTab.d.ts +12 -0
  137. package/lib/components/profile/ProfileTab.d.ts.map +1 -0
  138. package/lib/components/profile/ProfileTab.jsx +112 -0
  139. package/lib/components/profile/ProfileTab.jsx.map +1 -0
  140. package/lib/components/profile/SecurityTab.d.ts +32 -0
  141. package/lib/components/profile/SecurityTab.d.ts.map +1 -0
  142. package/lib/components/profile/SecurityTab.jsx +145 -0
  143. package/lib/components/profile/SecurityTab.jsx.map +1 -0
  144. package/lib/components/profile/index.d.ts +4 -0
  145. package/lib/components/profile/index.d.ts.map +1 -0
  146. package/lib/components/profile/index.js +4 -0
  147. package/lib/components/profile/index.js.map +1 -0
  148. package/lib/components/segmented-toggle.d.ts +28 -0
  149. package/lib/components/segmented-toggle.d.ts.map +1 -0
  150. package/lib/components/segmented-toggle.jsx +64 -0
  151. package/lib/components/segmented-toggle.jsx.map +1 -0
  152. package/lib/components/separator.d.ts +13 -0
  153. package/lib/components/separator.d.ts.map +1 -0
  154. package/lib/components/separator.jsx +16 -0
  155. package/lib/components/separator.jsx.map +1 -0
  156. package/lib/components/server-settings.d.ts +27 -0
  157. package/lib/components/server-settings.d.ts.map +1 -0
  158. package/lib/components/server-settings.jsx +307 -0
  159. package/lib/components/server-settings.jsx.map +1 -0
  160. package/lib/components/settings/AccountSection.d.ts +26 -0
  161. package/lib/components/settings/AccountSection.d.ts.map +1 -0
  162. package/lib/components/settings/AccountSection.jsx +32 -0
  163. package/lib/components/settings/AccountSection.jsx.map +1 -0
  164. package/lib/components/settings/DangerZoneSection.d.ts +13 -0
  165. package/lib/components/settings/DangerZoneSection.d.ts.map +1 -0
  166. package/lib/components/settings/DangerZoneSection.jsx +16 -0
  167. package/lib/components/settings/DangerZoneSection.jsx.map +1 -0
  168. package/lib/components/settings/GeneralSection.d.ts +11 -0
  169. package/lib/components/settings/GeneralSection.d.ts.map +1 -0
  170. package/lib/components/settings/GeneralSection.jsx +12 -0
  171. package/lib/components/settings/GeneralSection.jsx.map +1 -0
  172. package/lib/components/settings/SupportSection.d.ts +11 -0
  173. package/lib/components/settings/SupportSection.d.ts.map +1 -0
  174. package/lib/components/settings/SupportSection.jsx +22 -0
  175. package/lib/components/settings/SupportSection.jsx.map +1 -0
  176. package/lib/components/settings/index.d.ts +5 -0
  177. package/lib/components/settings/index.d.ts.map +1 -0
  178. package/lib/components/settings/index.js +5 -0
  179. package/lib/components/settings/index.js.map +1 -0
  180. package/lib/components/settings-row.d.ts +28 -0
  181. package/lib/components/settings-row.d.ts.map +1 -0
  182. package/lib/components/settings-row.jsx +68 -0
  183. package/lib/components/settings-row.jsx.map +1 -0
  184. package/lib/components/skeleton.d.ts +8 -0
  185. package/lib/components/skeleton.d.ts.map +1 -0
  186. package/lib/components/skeleton.jsx +24 -0
  187. package/lib/components/skeleton.jsx.map +1 -0
  188. package/lib/components/social-connections.d.ts +15 -0
  189. package/lib/components/social-connections.d.ts.map +1 -0
  190. package/lib/components/social-connections.jsx +31 -0
  191. package/lib/components/social-connections.jsx.map +1 -0
  192. package/lib/components/stat-card.d.ts +29 -0
  193. package/lib/components/stat-card.d.ts.map +1 -0
  194. package/lib/components/stat-card.jsx +36 -0
  195. package/lib/components/stat-card.jsx.map +1 -0
  196. package/lib/components/tag-chip.d.ts +18 -0
  197. package/lib/components/tag-chip.d.ts.map +1 -0
  198. package/lib/components/tag-chip.jsx +54 -0
  199. package/lib/components/tag-chip.jsx.map +1 -0
  200. package/lib/components/tag-multi-select.d.ts +27 -0
  201. package/lib/components/tag-multi-select.d.ts.map +1 -0
  202. package/lib/components/tag-multi-select.jsx +65 -0
  203. package/lib/components/tag-multi-select.jsx.map +1 -0
  204. package/lib/components/text.d.ts +22 -0
  205. package/lib/components/text.d.ts.map +1 -0
  206. package/lib/components/text.jsx +43 -0
  207. package/lib/components/text.jsx.map +1 -0
  208. package/lib/components/toast/ToastPortal.d.ts +11 -0
  209. package/lib/components/toast/ToastPortal.d.ts.map +1 -0
  210. package/lib/components/toast/ToastPortal.jsx +15 -0
  211. package/lib/components/toast/ToastPortal.jsx.map +1 -0
  212. package/lib/components/toast/ToastPortal.web.d.ts +14 -0
  213. package/lib/components/toast/ToastPortal.web.d.ts.map +1 -0
  214. package/lib/components/toast/ToastPortal.web.jsx +39 -0
  215. package/lib/components/toast/ToastPortal.web.jsx.map +1 -0
  216. package/lib/components/toast/index.d.ts +3 -0
  217. package/lib/components/toast/index.d.ts.map +1 -0
  218. package/lib/components/toast/index.js +3 -0
  219. package/lib/components/toast/index.js.map +1 -0
  220. package/lib/components/toast/toast-config.d.ts +14 -0
  221. package/lib/components/toast/toast-config.d.ts.map +1 -0
  222. package/lib/components/toast/toast-config.jsx +24 -0
  223. package/lib/components/toast/toast-config.jsx.map +1 -0
  224. package/lib/components/tooltip.d.ts +30 -0
  225. package/lib/components/tooltip.d.ts.map +1 -0
  226. package/lib/components/tooltip.jsx +14 -0
  227. package/lib/components/tooltip.jsx.map +1 -0
  228. package/lib/components/totp-display.d.ts +23 -0
  229. package/lib/components/totp-display.d.ts.map +1 -0
  230. package/lib/components/totp-display.jsx +132 -0
  231. package/lib/components/totp-display.jsx.map +1 -0
  232. package/lib/components/user-avatar.d.ts +18 -0
  233. package/lib/components/user-avatar.d.ts.map +1 -0
  234. package/lib/components/user-avatar.jsx +25 -0
  235. package/lib/components/user-avatar.jsx.map +1 -0
  236. package/lib/context/AppConfigContext.d.ts +56 -0
  237. package/lib/context/AppConfigContext.d.ts.map +1 -0
  238. package/lib/context/AppConfigContext.jsx +14 -0
  239. package/lib/context/AppConfigContext.jsx.map +1 -0
  240. package/lib/context/NavigationContext.d.ts +42 -0
  241. package/lib/context/NavigationContext.d.ts.map +1 -0
  242. package/lib/context/NavigationContext.jsx +53 -0
  243. package/lib/context/NavigationContext.jsx.map +1 -0
  244. package/lib/context/NotificationContext.d.ts +32 -0
  245. package/lib/context/NotificationContext.d.ts.map +1 -0
  246. package/lib/context/NotificationContext.jsx +49 -0
  247. package/lib/context/NotificationContext.jsx.map +1 -0
  248. package/lib/context/ThemeContext.d.ts +12 -0
  249. package/lib/context/ThemeContext.d.ts.map +1 -0
  250. package/lib/context/ThemeContext.jsx +66 -0
  251. package/lib/context/ThemeContext.jsx.map +1 -0
  252. package/lib/context/index.d.ts +5 -0
  253. package/lib/context/index.d.ts.map +1 -0
  254. package/lib/context/index.js +5 -0
  255. package/lib/context/index.js.map +1 -0
  256. package/lib/flags.d.ts +9 -0
  257. package/lib/flags.d.ts.map +1 -0
  258. package/lib/flags.js +37 -0
  259. package/lib/flags.js.map +1 -0
  260. package/lib/forms/form-error.d.ts +12 -0
  261. package/lib/forms/form-error.d.ts.map +1 -0
  262. package/lib/forms/form-error.jsx +14 -0
  263. package/lib/forms/form-error.jsx.map +1 -0
  264. package/lib/forms/index.d.ts +3 -0
  265. package/lib/forms/index.d.ts.map +1 -0
  266. package/lib/forms/index.js +3 -0
  267. package/lib/forms/index.js.map +1 -0
  268. package/lib/forms/shake-field.d.ts +18 -0
  269. package/lib/forms/shake-field.d.ts.map +1 -0
  270. package/lib/forms/shake-field.jsx +30 -0
  271. package/lib/forms/shake-field.jsx.map +1 -0
  272. package/lib/hooks/index.d.ts +6 -0
  273. package/lib/hooks/index.d.ts.map +1 -0
  274. package/lib/hooks/index.js +6 -0
  275. package/lib/hooks/index.js.map +1 -0
  276. package/lib/hooks/useAuth.d.ts +109 -0
  277. package/lib/hooks/useAuth.d.ts.map +1 -0
  278. package/lib/hooks/useAuth.jsx +404 -0
  279. package/lib/hooks/useAuth.jsx.map +1 -0
  280. package/lib/hooks/useAutoLock.d.ts +22 -0
  281. package/lib/hooks/useAutoLock.d.ts.map +1 -0
  282. package/lib/hooks/useAutoLock.js +85 -0
  283. package/lib/hooks/useAutoLock.js.map +1 -0
  284. package/lib/hooks/useDebouncedValue.d.ts +19 -0
  285. package/lib/hooks/useDebouncedValue.d.ts.map +1 -0
  286. package/lib/hooks/useDebouncedValue.js +29 -0
  287. package/lib/hooks/useDebouncedValue.js.map +1 -0
  288. package/lib/hooks/useShakeAnimation.d.ts +17 -0
  289. package/lib/hooks/useShakeAnimation.d.ts.map +1 -0
  290. package/lib/hooks/useShakeAnimation.js +22 -0
  291. package/lib/hooks/useShakeAnimation.js.map +1 -0
  292. package/lib/hooks/useUploadBlobUrl.d.ts +13 -0
  293. package/lib/hooks/useUploadBlobUrl.d.ts.map +1 -0
  294. package/lib/hooks/useUploadBlobUrl.js +60 -0
  295. package/lib/hooks/useUploadBlobUrl.js.map +1 -0
  296. package/lib/index.d.ts +10 -0
  297. package/lib/index.d.ts.map +1 -0
  298. package/lib/index.js +47 -0
  299. package/lib/index.js.map +1 -0
  300. package/lib/screens/auth/AccountPickerScreen.d.ts +24 -0
  301. package/lib/screens/auth/AccountPickerScreen.d.ts.map +1 -0
  302. package/lib/screens/auth/AccountPickerScreen.jsx +47 -0
  303. package/lib/screens/auth/AccountPickerScreen.jsx.map +1 -0
  304. package/lib/screens/auth/AuthScreen.d.ts +59 -0
  305. package/lib/screens/auth/AuthScreen.d.ts.map +1 -0
  306. package/lib/screens/auth/AuthScreen.jsx +594 -0
  307. package/lib/screens/auth/AuthScreen.jsx.map +1 -0
  308. package/lib/screens/auth/ConsentScreen.d.ts +27 -0
  309. package/lib/screens/auth/ConsentScreen.d.ts.map +1 -0
  310. package/lib/screens/auth/ConsentScreen.jsx +55 -0
  311. package/lib/screens/auth/ConsentScreen.jsx.map +1 -0
  312. package/lib/screens/auth/ErrorScreen.d.ts +20 -0
  313. package/lib/screens/auth/ErrorScreen.d.ts.map +1 -0
  314. package/lib/screens/auth/ErrorScreen.jsx +43 -0
  315. package/lib/screens/auth/ErrorScreen.jsx.map +1 -0
  316. package/lib/screens/auth/ExpiredLinkScreen.d.ts +19 -0
  317. package/lib/screens/auth/ExpiredLinkScreen.d.ts.map +1 -0
  318. package/lib/screens/auth/ExpiredLinkScreen.jsx +38 -0
  319. package/lib/screens/auth/ExpiredLinkScreen.jsx.map +1 -0
  320. package/lib/screens/auth/ForgotPasswordScreen.d.ts +4 -0
  321. package/lib/screens/auth/ForgotPasswordScreen.d.ts.map +1 -0
  322. package/lib/screens/auth/ForgotPasswordScreen.jsx +123 -0
  323. package/lib/screens/auth/ForgotPasswordScreen.jsx.map +1 -0
  324. package/lib/screens/auth/ImportDataScreen.d.ts +17 -0
  325. package/lib/screens/auth/ImportDataScreen.d.ts.map +1 -0
  326. package/lib/screens/auth/ImportDataScreen.jsx +73 -0
  327. package/lib/screens/auth/ImportDataScreen.jsx.map +1 -0
  328. package/lib/screens/auth/LoginScreen.d.ts +38 -0
  329. package/lib/screens/auth/LoginScreen.d.ts.map +1 -0
  330. package/lib/screens/auth/LoginScreen.jsx +326 -0
  331. package/lib/screens/auth/LoginScreen.jsx.map +1 -0
  332. package/lib/screens/auth/MfaChallengeScreen.d.ts +4 -0
  333. package/lib/screens/auth/MfaChallengeScreen.d.ts.map +1 -0
  334. package/lib/screens/auth/MfaChallengeScreen.jsx +139 -0
  335. package/lib/screens/auth/MfaChallengeScreen.jsx.map +1 -0
  336. package/lib/screens/auth/MfaEnrollScreen.d.ts +12 -0
  337. package/lib/screens/auth/MfaEnrollScreen.d.ts.map +1 -0
  338. package/lib/screens/auth/MfaEnrollScreen.jsx +267 -0
  339. package/lib/screens/auth/MfaEnrollScreen.jsx.map +1 -0
  340. package/lib/screens/auth/PasskeyEnrollScreen.d.ts +18 -0
  341. package/lib/screens/auth/PasskeyEnrollScreen.d.ts.map +1 -0
  342. package/lib/screens/auth/PasskeyEnrollScreen.jsx +51 -0
  343. package/lib/screens/auth/PasskeyEnrollScreen.jsx.map +1 -0
  344. package/lib/screens/auth/PrivacyPolicyScreen.d.ts +19 -0
  345. package/lib/screens/auth/PrivacyPolicyScreen.d.ts.map +1 -0
  346. package/lib/screens/auth/PrivacyPolicyScreen.jsx +143 -0
  347. package/lib/screens/auth/PrivacyPolicyScreen.jsx.map +1 -0
  348. package/lib/screens/auth/RegisterScreen.d.ts +14 -0
  349. package/lib/screens/auth/RegisterScreen.d.ts.map +1 -0
  350. package/lib/screens/auth/RegisterScreen.jsx +296 -0
  351. package/lib/screens/auth/RegisterScreen.jsx.map +1 -0
  352. package/lib/screens/auth/ResetPasswordScreen.d.ts +12 -0
  353. package/lib/screens/auth/ResetPasswordScreen.d.ts.map +1 -0
  354. package/lib/screens/auth/ResetPasswordScreen.jsx +276 -0
  355. package/lib/screens/auth/ResetPasswordScreen.jsx.map +1 -0
  356. package/lib/screens/auth/ServerConfigScreen.d.ts +18 -0
  357. package/lib/screens/auth/ServerConfigScreen.d.ts.map +1 -0
  358. package/lib/screens/auth/ServerConfigScreen.jsx +259 -0
  359. package/lib/screens/auth/ServerConfigScreen.jsx.map +1 -0
  360. package/lib/screens/auth/TermsOfServiceScreen.d.ts +30 -0
  361. package/lib/screens/auth/TermsOfServiceScreen.d.ts.map +1 -0
  362. package/lib/screens/auth/TermsOfServiceScreen.jsx +130 -0
  363. package/lib/screens/auth/TermsOfServiceScreen.jsx.map +1 -0
  364. package/lib/screens/auth/VerifyEmailScreen.d.ts +4 -0
  365. package/lib/screens/auth/VerifyEmailScreen.d.ts.map +1 -0
  366. package/lib/screens/auth/VerifyEmailScreen.jsx +249 -0
  367. package/lib/screens/auth/VerifyEmailScreen.jsx.map +1 -0
  368. package/lib/screens/auth/WelcomeScreen.d.ts +17 -0
  369. package/lib/screens/auth/WelcomeScreen.d.ts.map +1 -0
  370. package/lib/screens/auth/WelcomeScreen.jsx +58 -0
  371. package/lib/screens/auth/WelcomeScreen.jsx.map +1 -0
  372. package/lib/screens/auth/index.d.ts +19 -0
  373. package/lib/screens/auth/index.d.ts.map +1 -0
  374. package/lib/screens/auth/index.js +19 -0
  375. package/lib/screens/auth/index.js.map +1 -0
  376. package/lib/screens/index.d.ts +2 -0
  377. package/lib/screens/index.d.ts.map +1 -0
  378. package/lib/screens/index.js +2 -0
  379. package/lib/screens/index.js.map +1 -0
  380. package/lib/services/api-config.d.ts +87 -0
  381. package/lib/services/api-config.d.ts.map +1 -0
  382. package/lib/services/api-config.js +202 -0
  383. package/lib/services/api-config.js.map +1 -0
  384. package/lib/services/auth.service.d.ts +140 -0
  385. package/lib/services/auth.service.d.ts.map +1 -0
  386. package/lib/services/auth.service.js +376 -0
  387. package/lib/services/auth.service.js.map +1 -0
  388. package/lib/services/favicon.d.ts +46 -0
  389. package/lib/services/favicon.d.ts.map +1 -0
  390. package/lib/services/favicon.js +84 -0
  391. package/lib/services/favicon.js.map +1 -0
  392. package/lib/services/hibp-cache.d.ts +23 -0
  393. package/lib/services/hibp-cache.d.ts.map +1 -0
  394. package/lib/services/hibp-cache.js +56 -0
  395. package/lib/services/hibp-cache.js.map +1 -0
  396. package/lib/services/hibp.d.ts +28 -0
  397. package/lib/services/hibp.d.ts.map +1 -0
  398. package/lib/services/hibp.js +92 -0
  399. package/lib/services/hibp.js.map +1 -0
  400. package/lib/services/index.d.ts +8 -0
  401. package/lib/services/index.d.ts.map +1 -0
  402. package/lib/services/index.js +7 -0
  403. package/lib/services/index.js.map +1 -0
  404. package/lib/services/passkey.d.ts +10 -0
  405. package/lib/services/passkey.d.ts.map +1 -0
  406. package/lib/services/passkey.js +192 -0
  407. package/lib/services/passkey.js.map +1 -0
  408. package/lib/utils/api-fetch.d.ts +11 -0
  409. package/lib/utils/api-fetch.d.ts.map +1 -0
  410. package/lib/utils/api-fetch.js +52 -0
  411. package/lib/utils/api-fetch.js.map +1 -0
  412. package/lib/utils/clipboard.d.ts +31 -0
  413. package/lib/utils/clipboard.d.ts.map +1 -0
  414. package/lib/utils/clipboard.js +66 -0
  415. package/lib/utils/clipboard.js.map +1 -0
  416. package/lib/utils/cn.d.ts +9 -0
  417. package/lib/utils/cn.d.ts.map +1 -0
  418. package/lib/utils/cn.js +12 -0
  419. package/lib/utils/cn.js.map +1 -0
  420. package/lib/utils/debounce.d.ts +13 -0
  421. package/lib/utils/debounce.d.ts.map +1 -0
  422. package/lib/utils/debounce.js +28 -0
  423. package/lib/utils/debounce.js.map +1 -0
  424. package/lib/utils/error.d.ts +9 -0
  425. package/lib/utils/error.d.ts.map +1 -0
  426. package/lib/utils/error.js +11 -0
  427. package/lib/utils/error.js.map +1 -0
  428. package/lib/utils/fetch.d.ts +17 -0
  429. package/lib/utils/fetch.d.ts.map +1 -0
  430. package/lib/utils/fetch.js +29 -0
  431. package/lib/utils/fetch.js.map +1 -0
  432. package/lib/utils/http-error.d.ts +41 -0
  433. package/lib/utils/http-error.d.ts.map +1 -0
  434. package/lib/utils/http-error.js +63 -0
  435. package/lib/utils/http-error.js.map +1 -0
  436. package/lib/utils/index.d.ts +11 -0
  437. package/lib/utils/index.d.ts.map +1 -0
  438. package/lib/utils/index.js +11 -0
  439. package/lib/utils/index.js.map +1 -0
  440. package/lib/utils/palette.d.ts +46 -0
  441. package/lib/utils/palette.d.ts.map +1 -0
  442. package/lib/utils/palette.js +75 -0
  443. package/lib/utils/palette.js.map +1 -0
  444. package/lib/utils/password.d.ts +20 -0
  445. package/lib/utils/password.d.ts.map +1 -0
  446. package/lib/utils/password.js +51 -0
  447. package/lib/utils/password.js.map +1 -0
  448. package/lib/utils/settings-storage.d.ts +3 -0
  449. package/lib/utils/settings-storage.d.ts.map +1 -0
  450. package/lib/utils/settings-storage.js +62 -0
  451. package/lib/utils/settings-storage.js.map +1 -0
  452. package/lib/utils/validation.d.ts +29 -0
  453. package/lib/utils/validation.d.ts.map +1 -0
  454. package/lib/utils/validation.js +58 -0
  455. package/lib/utils/validation.js.map +1 -0
  456. package/package.json +204 -0
  457. package/src/components/__tests__/data-table.test.ts +94 -0
  458. package/src/components/__tests__/pagination.test.ts +73 -0
  459. package/src/components/__tests__/stat-card.test.ts +21 -0
  460. package/src/components/auth/LockOverlay.tsx +122 -0
  461. package/src/components/auth/PasswordStrengthMeter.tsx +96 -0
  462. package/src/components/auth/ProtectedRoute.tsx +91 -0
  463. package/src/components/auth/PwnedPasswordWarning.tsx +54 -0
  464. package/src/components/auth/index.ts +10 -0
  465. package/src/components/bottom-nav.tsx +99 -0
  466. package/src/components/button.tsx +144 -0
  467. package/src/components/card.tsx +82 -0
  468. package/src/components/checkbox.tsx +48 -0
  469. package/src/components/connection-settings.tsx +583 -0
  470. package/src/components/context-menu.tsx +95 -0
  471. package/src/components/data-table.tsx +223 -0
  472. package/src/components/dialog.tsx +376 -0
  473. package/src/components/dropdown-menu.tsx +120 -0
  474. package/src/components/entropy-boot-splash.tsx +114 -0
  475. package/src/components/entropy-mark-animated.tsx +260 -0
  476. package/src/components/entropy-mark-geometry.ts +11 -0
  477. package/src/components/entropy-mark-sequential.tsx +132 -0
  478. package/src/components/entropy-mark.tsx +135 -0
  479. package/src/components/error-boundary.tsx +70 -0
  480. package/src/components/index.ts +156 -0
  481. package/src/components/input.tsx +246 -0
  482. package/src/components/item-icon.tsx +111 -0
  483. package/src/components/label.tsx +25 -0
  484. package/src/components/legal-dialog.tsx +47 -0
  485. package/src/components/license-gate.tsx +28 -0
  486. package/src/components/logo.tsx +34 -0
  487. package/src/components/onboarding-wizard.tsx +353 -0
  488. package/src/components/pagination.tsx +154 -0
  489. package/src/components/password-generator.tsx +393 -0
  490. package/src/components/pin-pad.tsx +156 -0
  491. package/src/components/popover.tsx +38 -0
  492. package/src/components/profile/ProfileDialog.tsx +129 -0
  493. package/src/components/profile/ProfileTab.tsx +151 -0
  494. package/src/components/profile/SecurityTab.tsx +229 -0
  495. package/src/components/profile/index.ts +3 -0
  496. package/src/components/segmented-toggle.tsx +119 -0
  497. package/src/components/separator.tsx +41 -0
  498. package/src/components/server-settings.tsx +442 -0
  499. package/src/components/settings/AccountSection.tsx +76 -0
  500. package/src/components/settings/DangerZoneSection.tsx +37 -0
  501. package/src/components/settings/GeneralSection.tsx +39 -0
  502. package/src/components/settings/SupportSection.tsx +53 -0
  503. package/src/components/settings/index.ts +4 -0
  504. package/src/components/settings-row.tsx +126 -0
  505. package/src/components/skeleton.tsx +48 -0
  506. package/src/components/social-connections.tsx +55 -0
  507. package/src/components/stat-card.tsx +64 -0
  508. package/src/components/tag-chip.tsx +78 -0
  509. package/src/components/tag-multi-select.tsx +136 -0
  510. package/src/components/text.tsx +77 -0
  511. package/src/components/toast/ToastPortal.tsx +16 -0
  512. package/src/components/toast/ToastPortal.web.tsx +47 -0
  513. package/src/components/toast/index.ts +2 -0
  514. package/src/components/toast/toast-config.tsx +31 -0
  515. package/src/components/tooltip.tsx +30 -0
  516. package/src/components/totp-display.tsx +207 -0
  517. package/src/components/user-avatar.tsx +46 -0
  518. package/src/context/AppConfigContext.tsx +76 -0
  519. package/src/context/NavigationContext.tsx +75 -0
  520. package/src/context/NotificationContext.tsx +69 -0
  521. package/src/context/ThemeContext.tsx +83 -0
  522. package/src/context/__tests__/NavigationContext.test.tsx +53 -0
  523. package/src/context/index.ts +24 -0
  524. package/src/flags.ts +44 -0
  525. package/src/forms/form-error.tsx +20 -0
  526. package/src/forms/index.ts +2 -0
  527. package/src/forms/shake-field.tsx +52 -0
  528. package/src/hooks/__tests__/useUploadBlobUrl.test.tsx +77 -0
  529. package/src/hooks/index.ts +18 -0
  530. package/src/hooks/useAuth.tsx +567 -0
  531. package/src/hooks/useAutoLock.ts +110 -0
  532. package/src/hooks/useDebouncedValue.ts +32 -0
  533. package/src/hooks/useShakeAnimation.ts +42 -0
  534. package/src/hooks/useUploadBlobUrl.ts +75 -0
  535. package/src/index.ts +48 -0
  536. package/src/screens/auth/AccountPickerScreen.tsx +103 -0
  537. package/src/screens/auth/AuthScreen.tsx +941 -0
  538. package/src/screens/auth/ConsentScreen.tsx +118 -0
  539. package/src/screens/auth/ErrorScreen.tsx +96 -0
  540. package/src/screens/auth/ExpiredLinkScreen.tsx +87 -0
  541. package/src/screens/auth/ForgotPasswordScreen.tsx +182 -0
  542. package/src/screens/auth/ImportDataScreen.tsx +112 -0
  543. package/src/screens/auth/LoginScreen.tsx +484 -0
  544. package/src/screens/auth/MfaChallengeScreen.tsx +216 -0
  545. package/src/screens/auth/MfaEnrollScreen.tsx +388 -0
  546. package/src/screens/auth/PasskeyEnrollScreen.tsx +96 -0
  547. package/src/screens/auth/PrivacyPolicyScreen.tsx +249 -0
  548. package/src/screens/auth/RegisterScreen.tsx +430 -0
  549. package/src/screens/auth/ResetPasswordScreen.tsx +377 -0
  550. package/src/screens/auth/ServerConfigScreen.tsx +388 -0
  551. package/src/screens/auth/TermsOfServiceScreen.tsx +236 -0
  552. package/src/screens/auth/VerifyEmailScreen.tsx +310 -0
  553. package/src/screens/auth/WelcomeScreen.tsx +78 -0
  554. package/src/screens/auth/index.ts +36 -0
  555. package/src/screens/index.ts +1 -0
  556. package/src/services/__tests__/api-config.test.ts +67 -0
  557. package/src/services/__tests__/favicon.test.ts +88 -0
  558. package/src/services/__tests__/hibp-cache.test.ts +37 -0
  559. package/src/services/__tests__/hibp.test.ts +39 -0
  560. package/src/services/api-config.ts +260 -0
  561. package/src/services/auth.service.ts +498 -0
  562. package/src/services/favicon.ts +102 -0
  563. package/src/services/hibp-cache.ts +71 -0
  564. package/src/services/hibp.ts +120 -0
  565. package/src/services/index.ts +52 -0
  566. package/src/services/passkey.ts +213 -0
  567. package/src/types/external.d.ts +24 -0
  568. package/src/utils/__tests__/clipboard.test.ts +59 -0
  569. package/src/utils/__tests__/cn.test.ts +24 -0
  570. package/src/utils/__tests__/debounce.test.ts +42 -0
  571. package/src/utils/__tests__/error.test.ts +18 -0
  572. package/src/utils/__tests__/http-error.test.ts +33 -0
  573. package/src/utils/__tests__/palette.test.ts +22 -0
  574. package/src/utils/__tests__/password.test.ts +55 -0
  575. package/src/utils/__tests__/settings-storage.test.ts +15 -0
  576. package/src/utils/__tests__/validation.test.ts +52 -0
  577. package/src/utils/api-fetch.ts +61 -0
  578. package/src/utils/clipboard.ts +69 -0
  579. package/src/utils/cn.ts +12 -0
  580. package/src/utils/debounce.ts +33 -0
  581. package/src/utils/error.ts +10 -0
  582. package/src/utils/fetch.ts +33 -0
  583. package/src/utils/http-error.ts +81 -0
  584. package/src/utils/index.ts +23 -0
  585. package/src/utils/palette.ts +105 -0
  586. package/src/utils/password.ts +64 -0
  587. package/src/utils/settings-storage.ts +68 -0
  588. package/src/utils/validation.ts +71 -0
  589. package/styles/globals.css +262 -0
  590. package/tailwind-preset.js +163 -0
@@ -0,0 +1,567 @@
1
+ import React, {
2
+ createContext,
3
+ useCallback,
4
+ useContext,
5
+ useEffect,
6
+ useMemo,
7
+ useRef,
8
+ useState,
9
+ } from "react";
10
+ import { type Href } from "expo-router";
11
+ import { useNavigation } from "../context/NavigationContext";
12
+ import { useAppConfig } from "../context/AppConfigContext";
13
+ import AuthService, { type AuthResponse, type User } from "../services/auth.service";
14
+
15
+ /**
16
+ * Outcome of a passkey login attempt. Used by apps with a client-side
17
+ * unlock step (e.g. vault's symmetric-key derivation) so the login
18
+ * screen can route to either the home tab or a password-prompt dialog.
19
+ *
20
+ * `unlocked: true` — the authenticator honored PRF and the local
21
+ * unlock work is done. Route to `routes.home`.
22
+ * `unlocked: false` — server accepted the assertion and the session
23
+ * is established, but the consumer still needs
24
+ * the master password. The login screen renders
25
+ * its unlock dialog and calls `unlockWithPassword`.
26
+ */
27
+ export interface PasskeyLoginResult {
28
+ unlocked: boolean;
29
+ user: User;
30
+ }
31
+
32
+ interface AuthContextType {
33
+ user: User | null;
34
+ isLoading: boolean;
35
+ isAuthenticated: boolean;
36
+ login: (email: string, password: string) => Promise<void>;
37
+ /**
38
+ * Drive a passkey ceremony end-to-end. Available when the consumer
39
+ * provided `loginWithPasskey` to `AuthProvider`; throws otherwise.
40
+ * `signal` aborts an in-flight WebAuthn ceremony on web — wire it
41
+ * to a `useEffect` cleanup so unmounting the screen tears the OS
42
+ * prompt down instead of leaving an orphaned resolution.
43
+ */
44
+ loginWithPasskey: (email?: string, signal?: AbortSignal) => Promise<PasskeyLoginResult>;
45
+ /**
46
+ * Complete a non-PRF passkey unlock by deriving the local key from
47
+ * the master password. Available when the consumer provided
48
+ * `unlockWithPassword` to `AuthProvider`.
49
+ */
50
+ unlockWithPassword: (password: string) => Promise<void>;
51
+ register: (
52
+ email: string,
53
+ username: string | undefined,
54
+ password: string,
55
+ displayName?: string
56
+ ) => Promise<void>;
57
+ logout: () => Promise<void>;
58
+ refreshUser: () => Promise<void>;
59
+ deleteAccount: (password: string) => Promise<void>;
60
+ checkAuth: () => Promise<void>;
61
+ error: string | null;
62
+ clearError: () => void;
63
+ }
64
+
65
+ const AuthContext = createContext<AuthContextType | undefined>(undefined);
66
+
67
+ export interface AuthProviderProps {
68
+ children: React.ReactNode;
69
+ /**
70
+ * Optional hook called after a successful login. Vault wires its
71
+ * `KeyManager.unlock(password)` here; bootstrap leaves it unset.
72
+ * Throwing rolls the login back via `AuthService.logout()` and bubbles
73
+ * the error to the caller.
74
+ */
75
+ onAfterLogin?: (password: string, response: AuthResponse) => Promise<void> | void;
76
+ /** Optional post-register hook (mirror of `onAfterLogin`). */
77
+ onAfterRegister?: (password: string, response: AuthResponse) => Promise<void> | void;
78
+ /**
79
+ * Optional pre-logout hook. Vault wires `KeyManager.lock()` and
80
+ * websocket teardown here; failures are swallowed to keep logout
81
+ * idempotent.
82
+ */
83
+ onBeforeLogout?: () => Promise<void> | void;
84
+ /**
85
+ * Optional pre-checkAuth hook. Vault uses this to fetch the server's
86
+ * HIBP policy alongside the initial session probe.
87
+ */
88
+ onBeforeCheckAuth?: () => Promise<void> | void;
89
+ /**
90
+ * Optional passkey-ceremony driver. When provided, `useAuth()` exposes
91
+ * `loginWithPasskey`; the consumer owns the WebAuthn round-trip and the
92
+ * post-assertion vault unlock. Returning `{ unlocked: true }` finishes
93
+ * the flow; returning `{ unlocked: false }` keeps the session but tells
94
+ * the screen to collect the master password and call `unlockWithPassword`.
95
+ */
96
+ loginWithPasskey?: (email?: string, signal?: AbortSignal) => Promise<PasskeyLoginResult>;
97
+ /**
98
+ * Optional master-password unlock. Used by the passkey flow when the
99
+ * authenticator did not honor PRF.
100
+ */
101
+ unlockWithPassword?: (password: string) => Promise<void>;
102
+ /**
103
+ * Optional telemetry hook. Invoked with every error AuthProvider
104
+ * would otherwise swallow into a `__DEV__`-gated `console.warn` —
105
+ * post-login hook throw, post-register hook throw, refreshUser
106
+ * failure, logout API failure, etc. Wire this to Sentry / Rollbar /
107
+ * your structured-log shipper to get production visibility into
108
+ * paths the UI hides from the user.
109
+ *
110
+ * The `context` string identifies the call site (`"login-hook"`,
111
+ * `"register-hook"`, `"refresh-user"`, `"logout"`, …) so the
112
+ * consumer can route or filter without parsing the error message.
113
+ */
114
+ onError?: (error: unknown, context: string) => void;
115
+ }
116
+
117
+ export function AuthProvider({
118
+ children,
119
+ onAfterLogin,
120
+ onAfterRegister,
121
+ onBeforeLogout,
122
+ onBeforeCheckAuth,
123
+ loginWithPasskey: providedLoginWithPasskey,
124
+ unlockWithPassword: providedUnlockWithPassword,
125
+ onError,
126
+ }: AuthProviderProps) {
127
+ // Stable wrapper so `console.warn`-then-onError stays a single
128
+ // pattern across every silent catch in this provider.
129
+ const reportError = useCallback(
130
+ (error: unknown, context: string) => {
131
+ if (__DEV__) {
132
+ console.warn(`[auth:${context}]`, error);
133
+ }
134
+ try {
135
+ onError?.(error, context);
136
+ } catch {
137
+ // Telemetry must never crash the auth flow.
138
+ }
139
+ },
140
+ [onError]
141
+ );
142
+ const [user, setUser] = useState<User | null>(null);
143
+ const [isLoading, setIsLoading] = useState(true);
144
+ const [isAuthenticated, setIsAuthenticated] = useState(false);
145
+ const [error, setError] = useState<string | null>(null);
146
+ const router = useNavigation();
147
+ const { routes } = useAppConfig();
148
+ const authCheckRef = useRef(false);
149
+ const sessionGenRef = useRef(0);
150
+
151
+ const clearError = useCallback(() => {
152
+ setError(null);
153
+ }, []);
154
+
155
+ const routeAfterAuth = useCallback(
156
+ (response: AuthResponse, email: string) => {
157
+ const step = response.user.onboarding_step;
158
+ if (step === "email_verification") {
159
+ router.replace({ pathname: routes.verifyEmail, params: { email } } as Href);
160
+ } else if (step === "mfa_enrollment") {
161
+ router.replace(routes.mfaEnroll);
162
+ } else if (step === "import") {
163
+ router.replace(routes.importData);
164
+ } else if (step === "complete" || step == null) {
165
+ router.replace(routes.home);
166
+ } else {
167
+ // Unknown step (server added a new onboarding step the
168
+ // client doesn't recognize). Route home — the consumer's
169
+ // route guards will catch the still-incomplete state and
170
+ // either prompt an app update or re-route to welcome.
171
+ // Don't strand the user in the auth flow.
172
+ router.replace(routes.home);
173
+ }
174
+ },
175
+ [router, routes]
176
+ );
177
+
178
+ const checkAuth = useCallback(async () => {
179
+ if (authCheckRef.current) {
180
+ return;
181
+ }
182
+ authCheckRef.current = true;
183
+ const gen = ++sessionGenRef.current;
184
+
185
+ try {
186
+ setIsLoading(true);
187
+ const isAuth = await AuthService.isAuthenticated();
188
+ if (gen !== sessionGenRef.current) {
189
+ return;
190
+ }
191
+
192
+ if (isAuth) {
193
+ const userData = await AuthService.getCurrentUser();
194
+ if (gen !== sessionGenRef.current) {
195
+ return;
196
+ }
197
+ if (userData) {
198
+ // Unblock navigation immediately with cached data.
199
+ setUser(userData);
200
+ setIsAuthenticated(true);
201
+ setIsLoading(false);
202
+ authCheckRef.current = false;
203
+
204
+ // Refresh from the server in the background.
205
+ AuthService.refreshUserData()
206
+ .then((fresh) => {
207
+ if (fresh && gen === sessionGenRef.current) {
208
+ setUser(fresh);
209
+ }
210
+ })
211
+ .catch((e) => reportError(e, "background-refresh"));
212
+ return;
213
+ } else {
214
+ await AuthService.logout();
215
+ if (gen !== sessionGenRef.current) {
216
+ return;
217
+ }
218
+ setUser(null);
219
+ setIsAuthenticated(false);
220
+ }
221
+ } else {
222
+ setUser(null);
223
+ setIsAuthenticated(false);
224
+ }
225
+ } catch (e) {
226
+ // Distinguish a transport failure (server unreachable on cold
227
+ // start) from a real auth failure. Falling through to
228
+ // "logged out" on transient network errors paints the welcome
229
+ // screen unjustly to a user whose cookie is still valid, then
230
+ // flips back the moment a request lands. Keep any persisted
231
+ // user record while we wait for the next probe.
232
+ if (gen !== sessionGenRef.current) {
233
+ return;
234
+ }
235
+ const persisted = await AuthService.getCurrentUser().catch(() => null);
236
+ if (gen !== sessionGenRef.current) {
237
+ return;
238
+ }
239
+ if (persisted) {
240
+ setUser(persisted);
241
+ setIsAuthenticated(true);
242
+ reportError(e instanceof Error ? e : new Error(String(e)), "check-auth-transient");
243
+ } else {
244
+ setUser(null);
245
+ setIsAuthenticated(false);
246
+ }
247
+ } finally {
248
+ if (gen === sessionGenRef.current) {
249
+ setIsLoading(false);
250
+ }
251
+ authCheckRef.current = false;
252
+ }
253
+ }, [reportError]);
254
+
255
+ useEffect(() => {
256
+ AuthService.loadServerUrlOverride()
257
+ .then(() => (onBeforeCheckAuth ? onBeforeCheckAuth() : undefined))
258
+ .catch((e) => reportError(e, "pre-check-auth"))
259
+ .then(() => checkAuth());
260
+ }, [checkAuth, onBeforeCheckAuth, reportError]);
261
+
262
+ const login = useCallback(
263
+ async (email: string, password: string) => {
264
+ try {
265
+ setIsLoading(true);
266
+ setError(null);
267
+
268
+ const response = await AuthService.login(email, password);
269
+
270
+ if (onAfterLogin) {
271
+ try {
272
+ await onAfterLogin(password, response);
273
+ } catch (hookErr) {
274
+ await AuthService.logout();
275
+ const message = hookErr instanceof Error ? hookErr.message : "Post-login hook failed";
276
+ // Deliberately do NOT attach `cause: hookErr` — the inner
277
+ // error from the consumer's post-login hook may carry
278
+ // key-derivation context (raw masterPassword references,
279
+ // server payloads) that would be exposed if downstream
280
+ // logging surfaces `err.cause` (Sentry, devtools, …).
281
+
282
+ throw new Error(message);
283
+ }
284
+ }
285
+
286
+ setUser(response.user);
287
+ setIsAuthenticated(true);
288
+ routeAfterAuth(response, email);
289
+ } catch (err) {
290
+ const message = err instanceof Error ? err.message : "Login failed";
291
+ setError(message);
292
+ throw err;
293
+ } finally {
294
+ setIsLoading(false);
295
+ }
296
+ },
297
+ [onAfterLogin, routeAfterAuth]
298
+ );
299
+
300
+ const register = useCallback(
301
+ async (email: string, username: string | undefined, password: string, displayName?: string) => {
302
+ try {
303
+ setIsLoading(true);
304
+ setError(null);
305
+
306
+ const response = await AuthService.register(email, username, password, displayName);
307
+
308
+ if (onAfterRegister) {
309
+ try {
310
+ await onAfterRegister(password, response);
311
+ } catch (hookErr) {
312
+ await AuthService.logout();
313
+ const message =
314
+ hookErr instanceof Error ? hookErr.message : "Post-register hook failed";
315
+ // No `cause` chain — see login() above for rationale.
316
+
317
+ throw new Error(message);
318
+ }
319
+ }
320
+
321
+ setUser(response.user);
322
+ setIsAuthenticated(true);
323
+ routeAfterAuth(response, email);
324
+ } catch (err) {
325
+ const message = err instanceof Error ? err.message : "Registration failed";
326
+ setError(message);
327
+ throw err;
328
+ } finally {
329
+ setIsLoading(false);
330
+ }
331
+ },
332
+ [onAfterRegister, routeAfterAuth]
333
+ );
334
+
335
+ const logout = useCallback(async () => {
336
+ sessionGenRef.current++;
337
+ setIsLoading(true);
338
+ // Flip React auth state FIRST so any in-flight queries that
339
+ // re-render against `user` / `isAuthenticated` see "logged out"
340
+ // immediately. Previously this happened after `AuthService.logout()`
341
+ // resolved, opening a window where the pre-logout hook had already
342
+ // torn down KeyManager / queryClient / WS but components still
343
+ // rendered as authenticated — any React-Query refetch in that
344
+ // window would hit a half-disassembled client.
345
+ setUser(null);
346
+ setIsAuthenticated(false);
347
+ if (onBeforeLogout) {
348
+ try {
349
+ await onBeforeLogout();
350
+ } catch (e) {
351
+ // pre-logout hook errors are non-fatal — proceed to clear
352
+ // state. Telemetry sees it via the onError surface.
353
+ reportError(e, "pre-logout-hook");
354
+ }
355
+ }
356
+ router.replace(routes.login);
357
+ // Server-side revoke runs in the background. Failure is also
358
+ // surfaced through the onError hook so consumers can detect
359
+ // half-broken logouts (server session still alive after the
360
+ // user thinks they're gone).
361
+ try {
362
+ await AuthService.logout();
363
+ } catch (e) {
364
+ reportError(e, "logout-revoke");
365
+ }
366
+ setIsLoading(false);
367
+ }, [onBeforeLogout, router, routes, reportError]);
368
+
369
+ const refreshUser = useCallback(async () => {
370
+ try {
371
+ const userData = await AuthService.refreshUserData();
372
+ if (userData) {
373
+ setUser(userData);
374
+ setIsAuthenticated(true);
375
+ } else {
376
+ setUser(null);
377
+ setIsAuthenticated(false);
378
+ router.replace(routes.login);
379
+ }
380
+ } catch {
381
+ // Refresh failed — try again on next interval.
382
+ }
383
+ }, [router, routes]);
384
+
385
+ const deleteAccount = useCallback(
386
+ async (password: string) => {
387
+ sessionGenRef.current++;
388
+ try {
389
+ setIsLoading(true);
390
+ await AuthService.deleteAccount(password);
391
+ if (onBeforeLogout) {
392
+ try {
393
+ await onBeforeLogout();
394
+ } catch {
395
+ // ignore
396
+ }
397
+ }
398
+ setUser(null);
399
+ setIsAuthenticated(false);
400
+ router.replace(routes.welcome);
401
+ } catch (err) {
402
+ const message = err instanceof Error ? err.message : "Failed to delete account";
403
+ setError(message);
404
+ throw err;
405
+ } finally {
406
+ setIsLoading(false);
407
+ }
408
+ },
409
+ [onBeforeLogout, router, routes]
410
+ );
411
+
412
+ const loginWithPasskey = useCallback(
413
+ async (email?: string, signal?: AbortSignal): Promise<PasskeyLoginResult> => {
414
+ if (!providedLoginWithPasskey) {
415
+ throw new Error(
416
+ "entropy-ui: loginWithPasskey() called but AuthProvider wasn't given a loginWithPasskey handler."
417
+ );
418
+ }
419
+ try {
420
+ setIsLoading(true);
421
+ setError(null);
422
+ const result = await providedLoginWithPasskey(email, signal);
423
+ setUser(result.user);
424
+ setIsAuthenticated(true);
425
+ return result;
426
+ } catch (err) {
427
+ const message = err instanceof Error ? err.message : "Passkey sign-in failed";
428
+ setError(message);
429
+ throw err;
430
+ } finally {
431
+ setIsLoading(false);
432
+ }
433
+ },
434
+ [providedLoginWithPasskey]
435
+ );
436
+
437
+ const unlockWithPassword = useCallback(
438
+ async (password: string): Promise<void> => {
439
+ if (!providedUnlockWithPassword) {
440
+ throw new Error(
441
+ "entropy-ui: unlockWithPassword() called but AuthProvider wasn't given an unlockWithPassword handler."
442
+ );
443
+ }
444
+ await providedUnlockWithPassword(password);
445
+ },
446
+ [providedUnlockWithPassword]
447
+ );
448
+
449
+ const value = useMemo<AuthContextType>(
450
+ () => ({
451
+ user,
452
+ isLoading,
453
+ isAuthenticated,
454
+ login,
455
+ loginWithPasskey,
456
+ unlockWithPassword,
457
+ register,
458
+ logout,
459
+ refreshUser,
460
+ deleteAccount,
461
+ checkAuth,
462
+ error,
463
+ clearError,
464
+ }),
465
+ [
466
+ user,
467
+ isLoading,
468
+ isAuthenticated,
469
+ login,
470
+ loginWithPasskey,
471
+ unlockWithPassword,
472
+ register,
473
+ logout,
474
+ refreshUser,
475
+ deleteAccount,
476
+ checkAuth,
477
+ error,
478
+ clearError,
479
+ ]
480
+ );
481
+
482
+ return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
483
+ }
484
+
485
+ export function useAuth(): AuthContextType {
486
+ const context = useContext(AuthContext);
487
+ if (context === undefined) {
488
+ throw new Error("useAuth must be used within an AuthProvider");
489
+ }
490
+ return context;
491
+ }
492
+
493
+ export type { AuthContextType };
494
+
495
+ export function useRole(requiredRole: "Admin" | "User"): boolean {
496
+ const { user } = useAuth();
497
+ if (!user) {
498
+ return false;
499
+ }
500
+ if (requiredRole === "User") {
501
+ return true;
502
+ }
503
+ return user.role === "Admin";
504
+ }
505
+
506
+ export function useRequireAuth(redirectTo?: Href) {
507
+ const { isAuthenticated, isLoading } = useAuth();
508
+ const router = useNavigation();
509
+ const { routes } = useAppConfig();
510
+ const target = redirectTo ?? routes.login;
511
+
512
+ useEffect(() => {
513
+ if (!isLoading && !isAuthenticated) {
514
+ router.replace(target);
515
+ }
516
+ }, [isAuthenticated, isLoading, router, target]);
517
+
518
+ return { isAuthenticated, isLoading };
519
+ }
520
+
521
+ export function useSession() {
522
+ const { user, refreshUser, isAuthenticated } = useAuth();
523
+ const intervalRef = useRef<ReturnType<typeof setInterval> | undefined>(undefined);
524
+ const refreshingRef = useRef(false);
525
+
526
+ useEffect(() => {
527
+ if (isAuthenticated) {
528
+ intervalRef.current = setInterval(
529
+ () => {
530
+ if (refreshingRef.current) {
531
+ return;
532
+ }
533
+ refreshingRef.current = true;
534
+ refreshUser()
535
+ .catch((e) => {
536
+ // `useSession` lives outside the provider's
537
+ // reportError closure; auto-refresh failures only
538
+ // surface in dev. The `refreshUser()` impl itself
539
+ // reports via onError when wired.
540
+ if (__DEV__) {
541
+ console.warn("[auth:session-refresh-interval]", e);
542
+ }
543
+ })
544
+ .finally(() => {
545
+ refreshingRef.current = false;
546
+ });
547
+ },
548
+ 10 * 60 * 1000
549
+ );
550
+ }
551
+
552
+ return () => {
553
+ if (intervalRef.current) {
554
+ clearInterval(intervalRef.current);
555
+ }
556
+ };
557
+ }, [isAuthenticated, refreshUser]);
558
+
559
+ return { user };
560
+ }
561
+
562
+ // Validators live in `utils/validation` and are re-exported via the
563
+ // utils barrel. Existing consumers that imported them from this module
564
+ // pull them from the package root barrel instead.
565
+ export { validateEmail, validatePassword, validateUsername } from "../utils/validation";
566
+
567
+ export default useAuth;
@@ -0,0 +1,110 @@
1
+ import { useEffect } from "react";
2
+ import { AppState as RNAppState, Platform } from "react-native";
3
+
4
+ const DEFAULT_IDLE_LOCK_MS = 10 * 60 * 1000;
5
+
6
+ export interface UseAutoLockOptions {
7
+ /**
8
+ * Reports whether the session is currently "unlocked" (vault-style
9
+ * apps) or any other state where the auto-lock should still fire.
10
+ * Returning `false` short-circuits the lock call.
11
+ */
12
+ isUnlocked: () => boolean;
13
+ /** Called when the lock should fire. App owns side-effects. */
14
+ onLock: () => void;
15
+ /** Reports whether auto-lock is enabled for this user (settings flag). */
16
+ isEnabled?: () => boolean | Promise<boolean>;
17
+ /** Idle threshold in milliseconds. Defaults to 10 minutes. */
18
+ idleLockMs?: number;
19
+ }
20
+
21
+ /**
22
+ * Auto-lock a session after a period of inactivity. Generic over the
23
+ * lock predicate — vault wires `KeyManager.isUnlocked` / `KeyManager.lock`,
24
+ * other apps wire their own state. Web uses an idle-timer only; native
25
+ * additionally locks immediately when the app goes to the background.
26
+ */
27
+ export function useAutoLock(options: UseAutoLockOptions): void {
28
+ const { isUnlocked, onLock, isEnabled, idleLockMs = DEFAULT_IDLE_LOCK_MS } = options;
29
+
30
+ useEffect(() => {
31
+ let idleTimer: ReturnType<typeof setTimeout> | null = null;
32
+ let lastActivityAt = Date.now();
33
+
34
+ const lockIfEnabled = () => {
35
+ if (idleTimer !== null) {
36
+ clearTimeout(idleTimer);
37
+ idleTimer = null;
38
+ }
39
+ if (!isUnlocked()) {
40
+ return;
41
+ }
42
+ Promise.resolve(isEnabled ? isEnabled() : true)
43
+ .then((enabled) => {
44
+ if (enabled && isUnlocked()) {
45
+ onLock();
46
+ }
47
+ })
48
+ .catch(() => {});
49
+ };
50
+
51
+ const armIdleTimer = () => {
52
+ if (idleTimer !== null) {
53
+ clearTimeout(idleTimer);
54
+ }
55
+ const remaining = idleLockMs - (Date.now() - lastActivityAt);
56
+ if (remaining <= 0) {
57
+ lockIfEnabled();
58
+ return;
59
+ }
60
+ idleTimer = setTimeout(lockIfEnabled, remaining);
61
+ };
62
+
63
+ const onActivity = () => {
64
+ lastActivityAt = Date.now();
65
+ armIdleTimer();
66
+ };
67
+
68
+ // On web, RN Web's AppState mirrors document visibility — tab-hidden
69
+ // becomes 'background', which would otherwise fire an immediate lock.
70
+ // Only treat 'background' as a lock signal on real native platforms.
71
+ const subscription = RNAppState.addEventListener("change", (nextState) => {
72
+ if (nextState === "background" && Platform.OS !== "web") {
73
+ lockIfEnabled();
74
+ } else if (nextState === "active") {
75
+ armIdleTimer();
76
+ }
77
+ });
78
+
79
+ const cleanups: (() => void)[] = [];
80
+
81
+ if (Platform.OS === "web" && typeof document !== "undefined") {
82
+ const onVisibility = () => {
83
+ if (document.visibilityState === "visible") {
84
+ armIdleTimer();
85
+ }
86
+ };
87
+ document.addEventListener("visibilitychange", onVisibility);
88
+ cleanups.push(() => document.removeEventListener("visibilitychange", onVisibility));
89
+
90
+ const activityEvents = ["mousemove", "mousedown", "keydown", "touchstart", "scroll"] as const;
91
+ for (const e of activityEvents) {
92
+ window.addEventListener(e, onActivity, { passive: true });
93
+ cleanups.push(() => window.removeEventListener(e, onActivity));
94
+ }
95
+ }
96
+
97
+ armIdleTimer();
98
+
99
+ return () => {
100
+ subscription.remove();
101
+ for (const fn of cleanups) {
102
+ fn();
103
+ }
104
+ if (idleTimer !== null) {
105
+ clearTimeout(idleTimer);
106
+ }
107
+ };
108
+ // eslint-disable-next-line react-hooks/exhaustive-deps
109
+ }, []);
110
+ }
@@ -0,0 +1,32 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ import { debounce } from "../utils/debounce";
4
+
5
+ /**
6
+ * Returns a debounced copy of `value` that only updates after `value` has
7
+ * stopped changing for `delayMs`. Use it to keep a controlled input
8
+ * responsive (bind the input to the raw value) while throttling the
9
+ * expensive work it drives — a network query, a react-query cache key, a
10
+ * filter over a large list.
11
+ *
12
+ * The pending update is cancelled on unmount and whenever `value` changes
13
+ * again, so a fast typer never fires an update per keystroke and there is no
14
+ * setState-after-unmount on the trailing edge.
15
+ *
16
+ * @example
17
+ * const [term, setTerm] = useState("");
18
+ * const debouncedTerm = useDebouncedValue(term, 300);
19
+ * // <Input value={term} onChangeText={setTerm} /> — instant feedback
20
+ * // useQuery({ queryKey: ["search", debouncedTerm], ... }) — one fetch per pause
21
+ */
22
+ export function useDebouncedValue<T>(value: T, delayMs = 300): T {
23
+ const [debounced, setDebounced] = useState(value);
24
+
25
+ useEffect(() => {
26
+ const update = debounce((next: T) => setDebounced(next), delayMs);
27
+ update(value);
28
+ return update.cancel;
29
+ }, [value, delayMs]);
30
+
31
+ return debounced;
32
+ }