@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,52 @@
1
+ import { validateEmail, validatePassword, validateUsername } from "../validation";
2
+
3
+ describe("validateEmail", () => {
4
+ it.each(["a@b.co", "user.name+tag@example.com", "x@host.dev"])("accepts %s", (e) => {
5
+ expect(validateEmail(e)).toBe(true);
6
+ });
7
+
8
+ it.each(["", "no-at", "a@b", "a@b.c", "a@@b.co", " a@b.co", "a@b.co "])("rejects %s", (e) => {
9
+ expect(validateEmail(e)).toBe(false);
10
+ });
11
+ });
12
+
13
+ describe("validatePassword", () => {
14
+ it("accepts a strong password", () => {
15
+ const r = validatePassword("Aa1!2345");
16
+ expect(r.valid).toBe(true);
17
+ expect(r.errors).toEqual([]);
18
+ });
19
+
20
+ it("collects every rule violation in a single pass", () => {
21
+ const r = validatePassword("short");
22
+ expect(r.valid).toBe(false);
23
+ // length, upper, digit, symbol = 4 misses; lowercase is present.
24
+ expect(r.errors.length).toBe(4);
25
+ });
26
+
27
+ it("flags missing symbol only", () => {
28
+ const r = validatePassword("Aaaaaaa1");
29
+ expect(r.valid).toBe(false);
30
+ expect(r.errors).toHaveLength(1);
31
+ expect(r.errors[0]).toMatch(/special/i);
32
+ });
33
+ });
34
+
35
+ describe("validateUsername", () => {
36
+ it("accepts 3..30 ascii alnum/underscore", () => {
37
+ expect(validateUsername("abc")).toEqual({ valid: true });
38
+ expect(validateUsername("a_1")).toEqual({ valid: true });
39
+ expect(validateUsername("a".repeat(30))).toEqual({ valid: true });
40
+ });
41
+
42
+ it("rejects out-of-range length", () => {
43
+ expect(validateUsername("ab").valid).toBe(false);
44
+ expect(validateUsername("a".repeat(31)).valid).toBe(false);
45
+ });
46
+
47
+ it("rejects non-ascii / special chars", () => {
48
+ expect(validateUsername("ab-c").valid).toBe(false);
49
+ expect(validateUsername("ab c").valid).toBe(false);
50
+ expect(validateUsername("ab.c").valid).toBe(false);
51
+ });
52
+ });
@@ -0,0 +1,61 @@
1
+ import { Platform } from "react-native";
2
+ import { fetchWithTimeout } from "./fetch";
3
+ import { getBaseUrl, getSessionToken } from "../services/api-config";
4
+
5
+ const SAFE_METHODS = new Set(["GET", "HEAD", "OPTIONS"]);
6
+
7
+ const CSRF_COOKIE_NAME = "csrf_token";
8
+ const CSRF_HEADER_NAME = "X-CSRF-Token";
9
+
10
+ function readCookie(name: string): string | null {
11
+ if (typeof document === "undefined") {
12
+ return null;
13
+ }
14
+ const prefix = `${name}=`;
15
+ for (const part of document.cookie.split(";")) {
16
+ const trimmed = part.trim();
17
+ if (trimmed.startsWith(prefix)) {
18
+ return decodeURIComponent(trimmed.slice(prefix.length));
19
+ }
20
+ }
21
+ return null;
22
+ }
23
+
24
+ /**
25
+ * Cross-platform authed `fetch` for the entropy-ui auth flows.
26
+ *
27
+ * **Web**: HttpOnly session cookie (auto-attached, used in same-origin /
28
+ * production) + `X-CSRF-Token` on writes. Also attaches a `Bearer` token when
29
+ * one is stored (sessionStorage) — needed for split-origin dev where the Lax
30
+ * cookie can't ride cross-origin XHR.
31
+ * **Native**: Bearer token loaded from SecureStore.
32
+ */
33
+ export async function apiFetch(
34
+ path: string,
35
+ init: RequestInit = {},
36
+ timeoutMs?: number
37
+ ): Promise<Response> {
38
+ const url = `${getBaseUrl()}${path}`;
39
+ const method = (init.method ?? "GET").toUpperCase();
40
+ const headers = new Headers(init.headers);
41
+
42
+ if (Platform.OS === "web") {
43
+ if (!SAFE_METHODS.has(method)) {
44
+ const csrf = readCookie(CSRF_COOKIE_NAME);
45
+ if (csrf) {
46
+ headers.set(CSRF_HEADER_NAME, csrf);
47
+ }
48
+ }
49
+ const webToken = await getSessionToken();
50
+ if (webToken && !headers.has("Authorization")) {
51
+ headers.set("Authorization", `Bearer ${webToken}`);
52
+ }
53
+ return fetchWithTimeout(url, { ...init, headers, credentials: "include" }, timeoutMs);
54
+ }
55
+
56
+ const token = await getSessionToken();
57
+ if (token) {
58
+ headers.set("Authorization", `Bearer ${token}`);
59
+ }
60
+ return fetchWithTimeout(url, { ...init, headers }, timeoutMs);
61
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Centralised copy-to-clipboard with a single, module-scope auto-clear
3
+ * timer.
4
+ *
5
+ * Why module scope: tying the timer to a React component (via `useRef`)
6
+ * means it gets cancelled when the screen unmounts — which happens
7
+ * routinely (auto-lock on tab-switch, navigation to another item). The
8
+ * user's just-completed copy then either gets wiped immediately
9
+ * (component cleanup runs `setStringAsync("")`) or sits indefinitely.
10
+ *
11
+ * Behaviour:
12
+ * * `copyToClipboard(value)` writes the value, returns `true` on
13
+ * success.
14
+ * * On success, schedules a 30-second auto-wipe.
15
+ * * Subsequent copies cancel the prior timer and reschedule, so the
16
+ * wipe is always 30 seconds from the *last* copy regardless of
17
+ * which screen issued it.
18
+ *
19
+ * Limitation: we don't read the clipboard back (would require the
20
+ * clipboard-read permission), so if the user copies from another app
21
+ * between our copy and the auto-clear firing, we'll still wipe their
22
+ * value at the 30s mark. Acceptable trade-off for a password manager.
23
+ */
24
+
25
+ import * as Clipboard from "expo-clipboard";
26
+
27
+ const AUTO_CLEAR_MS = 30_000;
28
+
29
+ let clearTimer: ReturnType<typeof setTimeout> | null = null;
30
+
31
+ /** Write `value` to the clipboard. Returns `true` if the write succeeded. */
32
+ export async function copyToClipboard(value: string): Promise<boolean> {
33
+ let ok: boolean;
34
+ try {
35
+ ok = await Clipboard.setStringAsync(value);
36
+ } catch {
37
+ ok = false;
38
+ }
39
+ if (!ok) {
40
+ return false;
41
+ }
42
+
43
+ if (clearTimer !== null) {
44
+ clearTimeout(clearTimer);
45
+ }
46
+ clearTimer = setTimeout(() => {
47
+ clearTimer = null;
48
+ void Clipboard.setStringAsync("").catch(() => {
49
+ // Best-effort.
50
+ });
51
+ }, AUTO_CLEAR_MS);
52
+ return true;
53
+ }
54
+
55
+ /**
56
+ * Wipe the clipboard now (e.g. on explicit logout). Cancels the pending
57
+ * auto-clear so we don't fire a redundant wipe later.
58
+ */
59
+ export async function clearClipboardNow(): Promise<void> {
60
+ if (clearTimer !== null) {
61
+ clearTimeout(clearTimer);
62
+ clearTimer = null;
63
+ }
64
+ try {
65
+ await Clipboard.setStringAsync("");
66
+ } catch {
67
+ // Best-effort.
68
+ }
69
+ }
@@ -0,0 +1,12 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ /**
5
+ * Combines class names using clsx and merges Tailwind CSS classes intelligently.
6
+ *
7
+ * @param inputs - Class values to combine
8
+ * @returns Merged class string
9
+ */
10
+ export function cn(...inputs: ClassValue[]) {
11
+ return twMerge(clsx(inputs));
12
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Debounce a function. The returned function has a `.cancel()` method
3
+ * so a pending invocation can be cleared in a `useEffect` cleanup to
4
+ * avoid a setState-after-unmount on the trailing call.
5
+ *
6
+ * Intentionally a tiny, no-dependency implementation — `lodash.debounce`
7
+ * pulls 200kB of lodash with it; this is 15 lines, type-safe, and tree-
8
+ * shake-friendly.
9
+ */
10
+ export function debounce<T extends (...args: Parameters<T>) => void>(
11
+ func: T,
12
+ wait: number
13
+ ): ((...args: Parameters<T>) => void) & { cancel: () => void } {
14
+ let timeout: ReturnType<typeof setTimeout> | null = null;
15
+
16
+ const debounced = (...args: Parameters<T>) => {
17
+ if (timeout) {
18
+ clearTimeout(timeout);
19
+ }
20
+ timeout = setTimeout(() => {
21
+ func(...args);
22
+ }, wait);
23
+ };
24
+
25
+ debounced.cancel = () => {
26
+ if (timeout) {
27
+ clearTimeout(timeout);
28
+ timeout = null;
29
+ }
30
+ };
31
+
32
+ return debounced;
33
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Extract a human-readable message from an unknown error value.
3
+ *
4
+ * Use this in every `catch (e: unknown)` block instead of accessing
5
+ * `e?.message` directly — TypeScript's `unknown` type prevents property
6
+ * access without a type guard.
7
+ */
8
+ export function getErrorMessage(e: unknown, fallback = "An unexpected error occurred"): string {
9
+ return e instanceof Error ? e.message : fallback;
10
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * `fetch` with a timeout abort signal merged onto the caller's own signal.
3
+ * The default timeout is 15s. Pass `Infinity` to disable the timeout
4
+ * (the caller's own `signal` still applies). Passing `0` aborts
5
+ * immediately — almost never what you want — so we treat any
6
+ * non-positive value as "no timeout" to avoid the footgun.
7
+ *
8
+ * **Lockstep duplicate** of `vault/clients/shared/src/utils/fetch.ts`.
9
+ * `@vault/shared` is the vault-specific lower layer; `@entropy-softworks/ui`
10
+ * is the cross-portfolio UI package. Neither can depend on the other
11
+ * (shared → ui would invert layering; ui → shared would chain every
12
+ * downstream Entropy Softworks app to vault). The function is small,
13
+ * pure, and stable; keep both copies byte-identical and update both
14
+ * when changing.
15
+ */
16
+ export function fetchWithTimeout(
17
+ url: string,
18
+ options: RequestInit = {},
19
+ timeoutMs = 15000
20
+ ): Promise<Response> {
21
+ const controller = new AbortController();
22
+ const id =
23
+ timeoutMs > 0 && Number.isFinite(timeoutMs)
24
+ ? setTimeout(() => controller.abort(), timeoutMs)
25
+ : null;
26
+ const signals = [controller.signal, options.signal].filter((s): s is AbortSignal => s != null);
27
+ const combinedSignal = signals.length > 1 ? AbortSignal.any(signals) : controller.signal;
28
+ return fetch(url, { ...options, signal: combinedSignal }).finally(() => {
29
+ if (id !== null) {
30
+ clearTimeout(id);
31
+ }
32
+ });
33
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Parse an Entropy Softworks server error response into a single user-
3
+ * facing string. Falls back to `fallback` when the body isn't valid
4
+ * JSON or doesn't follow the expected `{ error: { message, details? } }`
5
+ * shape. Detail arrays longer than `MAX_DETAILS` are truncated so a
6
+ * runaway server response can't produce an unreadable toast.
7
+ */
8
+ export interface ServerErrorBody {
9
+ error: {
10
+ message: string;
11
+ details?: string[];
12
+ code?: string;
13
+ };
14
+ }
15
+
16
+ const MAX_DETAILS = 5;
17
+
18
+ /**
19
+ * Error subclass that carries the server's machine-readable
20
+ * `error.code` and HTTP `status` alongside the human-readable
21
+ * message. UI code can branch on `code` (e.g. `INVALID_CREDENTIALS`,
22
+ * `ACCOUNT_LOCKED`, `EMAIL_NOT_VERIFIED`) instead of substring-
23
+ * matching against an English message that breaks under i18n.
24
+ */
25
+ export class ApiError extends Error {
26
+ readonly code: string | undefined;
27
+ readonly status: number;
28
+ readonly details: readonly string[] | undefined;
29
+
30
+ constructor(
31
+ message: string,
32
+ options: { code?: string; status: number; details?: readonly string[] }
33
+ ) {
34
+ super(message);
35
+ this.name = "ApiError";
36
+ this.code = options.code;
37
+ this.status = options.status;
38
+ this.details = options.details;
39
+ }
40
+ }
41
+
42
+ export async function extractErrorMessage(response: Response, fallback: string): Promise<string> {
43
+ try {
44
+ const error: ServerErrorBody = await response.json();
45
+ const details = error.error.details;
46
+ if (details && details.length > 0) {
47
+ const shown = details.slice(0, MAX_DETAILS).join(", ");
48
+ return details.length > MAX_DETAILS ? `${shown}, …` : shown;
49
+ }
50
+ return error.error.message || fallback;
51
+ } catch {
52
+ return fallback;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Like `extractErrorMessage` but returns a structured `ApiError` so
58
+ * callers can branch on `code` / `status`. Use this when the caller
59
+ * is throwing and downstream consumers need machine-readable
60
+ * classification; use `extractErrorMessage` when all you need is a
61
+ * toast string.
62
+ */
63
+ export async function extractApiError(response: Response, fallback: string): Promise<ApiError> {
64
+ let message = fallback;
65
+ let code: string | undefined;
66
+ let details: string[] | undefined;
67
+ try {
68
+ const error: ServerErrorBody = await response.json();
69
+ if (error.error.details && error.error.details.length > 0) {
70
+ details = error.error.details.slice(0, MAX_DETAILS);
71
+ const shown = details.join(", ");
72
+ message = error.error.details.length > MAX_DETAILS ? `${shown}, …` : shown;
73
+ } else {
74
+ message = error.error.message || fallback;
75
+ }
76
+ code = error.error.code;
77
+ } catch {
78
+ // Non-JSON body — keep fallback message, no code.
79
+ }
80
+ return new ApiError(message, { code, status: response.status, details });
81
+ }
@@ -0,0 +1,23 @@
1
+ export { apiFetch } from "./api-fetch";
2
+ export { cn } from "./cn";
3
+ export { getErrorMessage } from "./error";
4
+ export {
5
+ darkPalette,
6
+ getPalette,
7
+ lightPalette,
8
+ usePalette,
9
+ type ColorScheme,
10
+ type Palette,
11
+ } from "./palette";
12
+ export { clearClipboardNow, copyToClipboard } from "./clipboard";
13
+ export { loadSetting, saveSetting } from "./settings-storage";
14
+ export { debounce } from "./debounce";
15
+ export { ApiError, extractApiError, extractErrorMessage } from "./http-error";
16
+ export { generatePassword, type GeneratePasswordOptions } from "./password";
17
+ export {
18
+ validateEmail,
19
+ validatePassword,
20
+ validateUsername,
21
+ type PasswordValidation,
22
+ type UsernameValidation,
23
+ } from "./validation";
@@ -0,0 +1,105 @@
1
+ /**
2
+ * JavaScript-side representation of the same color tokens NativeWind
3
+ * exposes as CSS variables in `styles/globals.css`. Components that need
4
+ * a palette object (RN-Web inline styles, SVG `stroke`, etc.) consume
5
+ * this so the values stay in lockstep with the className-based theming.
6
+ *
7
+ * Always read via `usePalette()` (or `getPalette(scheme)` for non-hook
8
+ * call sites) — the table mirrors `:root` for light and `.dark` for
9
+ * dark.
10
+ */
11
+
12
+ import { useMemo } from "react";
13
+ import { useColorScheme } from "nativewind";
14
+
15
+ export interface Palette {
16
+ background: string;
17
+ foreground: string;
18
+ card: string;
19
+ cardForeground: string;
20
+ popover: string;
21
+ popoverForeground: string;
22
+ primary: string;
23
+ primaryForeground: string;
24
+ secondary: string;
25
+ secondaryForeground: string;
26
+ muted: string;
27
+ mutedForeground: string;
28
+ accent: string;
29
+ accentForeground: string;
30
+ destructive: string;
31
+ destructiveForeground: string;
32
+ border: string;
33
+ input: string;
34
+ inputBorder: string;
35
+ ring: string;
36
+ placeholder: string;
37
+ }
38
+
39
+ export const lightPalette: Palette = {
40
+ background: "rgb(255, 255, 255)",
41
+ foreground: "rgb(0, 0, 0)",
42
+ card: "rgb(255, 255, 255)",
43
+ cardForeground: "rgb(0, 0, 0)",
44
+ popover: "rgb(255, 255, 255)",
45
+ popoverForeground: "rgb(0, 0, 0)",
46
+ primary: "rgb(0, 0, 0)",
47
+ primaryForeground: "rgb(255, 255, 255)",
48
+ secondary: "rgb(245, 245, 245)",
49
+ secondaryForeground: "rgb(0, 0, 0)",
50
+ muted: "rgb(245, 245, 245)",
51
+ mutedForeground: "rgb(115, 115, 115)",
52
+ accent: "rgb(245, 245, 245)",
53
+ accentForeground: "rgb(0, 0, 0)",
54
+ destructive: "rgb(239, 68, 68)",
55
+ destructiveForeground: "rgb(255, 255, 255)",
56
+ border: "rgb(229, 229, 229)",
57
+ input: "rgb(255, 255, 255)",
58
+ inputBorder: "rgb(229, 229, 229)",
59
+ ring: "rgb(0, 0, 0)",
60
+ placeholder: "rgb(163, 163, 163)",
61
+ };
62
+
63
+ export const darkPalette: Palette = {
64
+ background: "rgb(0, 0, 0)",
65
+ foreground: "rgb(255, 255, 255)",
66
+ card: "rgb(0, 0, 0)",
67
+ cardForeground: "rgb(255, 255, 255)",
68
+ popover: "rgb(0, 0, 0)",
69
+ popoverForeground: "rgb(255, 255, 255)",
70
+ primary: "rgb(255, 255, 255)",
71
+ primaryForeground: "rgb(0, 0, 0)",
72
+ secondary: "rgb(38, 38, 38)",
73
+ secondaryForeground: "rgb(255, 255, 255)",
74
+ muted: "rgb(38, 38, 38)",
75
+ mutedForeground: "rgb(163, 163, 163)",
76
+ accent: "rgb(38, 38, 38)",
77
+ accentForeground: "rgb(255, 255, 255)",
78
+ destructive: "rgb(127, 29, 29)",
79
+ destructiveForeground: "rgb(255, 255, 255)",
80
+ border: "rgb(38, 38, 38)",
81
+ input: "rgb(0, 0, 0)",
82
+ inputBorder: "rgb(38, 38, 38)",
83
+ ring: "rgb(255, 255, 255)",
84
+ placeholder: "rgb(115, 115, 115)",
85
+ };
86
+
87
+ export type ColorScheme = "light" | "dark";
88
+
89
+ export function getPalette(scheme: ColorScheme): Palette {
90
+ return scheme === "dark" ? darkPalette : lightPalette;
91
+ }
92
+
93
+ /**
94
+ * Returns the active palette + scheme. Re-renders the caller when the
95
+ * OS / class-toggled theme flips.
96
+ */
97
+ export function usePalette(): { scheme: ColorScheme; palette: Palette } {
98
+ const { colorScheme } = useColorScheme();
99
+ const scheme: ColorScheme = colorScheme === "dark" ? "dark" : "light";
100
+ // Memoize the wrapper so callers using the result as a useMemo /
101
+ // useCallback dep see a stable reference until the scheme actually
102
+ // changes. The contained `palette` is already a module-level cached
103
+ // singleton; only the wrapper object was being re-created.
104
+ return useMemo(() => ({ scheme, palette: getPalette(scheme) }), [scheme]);
105
+ }
@@ -0,0 +1,64 @@
1
+ import { randomBytes } from "@noble/ciphers/utils.js";
2
+
3
+ const UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
4
+ const LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
5
+ const NUMBERS = "0123456789";
6
+ const SYMBOLS = "!@#$%^&*()_+-=[]{}|;:,.<>?";
7
+ const SIMILAR = "il1Lo0O";
8
+
9
+ export interface GeneratePasswordOptions {
10
+ /** Output length in characters. */
11
+ length: number;
12
+ includeUppercase?: boolean;
13
+ includeLowercase?: boolean;
14
+ includeNumbers?: boolean;
15
+ includeSymbols?: boolean;
16
+ /** Drop ambiguous glyphs (`i l 1 L o 0 O`). */
17
+ excludeSimilar?: boolean;
18
+ }
19
+
20
+ /**
21
+ * Generate a random password using CSPRNG bytes (`@noble/ciphers`) with
22
+ * rejection sampling so the output distribution stays uniform across
23
+ * the chosen character set — `b % len` modulo-bias is rejected by
24
+ * trimming the byte range to a multiple of `len`.
25
+ *
26
+ * Throws when every character class is disabled (no charset).
27
+ */
28
+ export function generatePassword(opts: GeneratePasswordOptions): string {
29
+ let charset = "";
30
+ if (opts.includeUppercase ?? true) {
31
+ charset += UPPERCASE;
32
+ }
33
+ if (opts.includeLowercase ?? true) {
34
+ charset += LOWERCASE;
35
+ }
36
+ if (opts.includeNumbers ?? true) {
37
+ charset += NUMBERS;
38
+ }
39
+ if (opts.includeSymbols ?? true) {
40
+ charset += SYMBOLS;
41
+ }
42
+ if (opts.excludeSimilar) {
43
+ charset = charset
44
+ .split("")
45
+ .filter((c) => !SIMILAR.includes(c))
46
+ .join("");
47
+ }
48
+ if (charset.length === 0) {
49
+ throw new Error("generatePassword: at least one character class must be enabled");
50
+ }
51
+
52
+ const limit = Math.floor(256 / charset.length) * charset.length;
53
+ let out = "";
54
+ while (out.length < opts.length) {
55
+ const bytes = randomBytes(opts.length - out.length + 16);
56
+ for (let i = 0; i < bytes.length && out.length < opts.length; i++) {
57
+ const b = bytes[i] ?? 0;
58
+ if (b < limit) {
59
+ out += charset.charAt(b % charset.length);
60
+ }
61
+ }
62
+ }
63
+ return out;
64
+ }
@@ -0,0 +1,68 @@
1
+ import { Platform } from "react-native";
2
+ import * as SecureStore from "expo-secure-store";
3
+
4
+ /**
5
+ * Boolean preference storage shared across platforms.
6
+ *
7
+ * Web persists via `localStorage` so behaviour matches the native
8
+ * SecureStore path (also persistent). The web tier is origin-isolated
9
+ * but not encrypted at rest — don't use this for secrets, only for UX
10
+ * preferences whose disclosure is acceptable. Security-sensitive policy
11
+ * (e.g. enforced auto-lock) belongs server-side.
12
+ */
13
+
14
+ const WEB_STORAGE_AVAILABLE =
15
+ Platform.OS === "web" && typeof window !== "undefined" && !!window.localStorage;
16
+
17
+ let warnedNoStorage = false;
18
+ function warnNoStorage(action: "load" | "save"): void {
19
+ if (warnedNoStorage) {
20
+ return;
21
+ }
22
+ warnedNoStorage = true;
23
+ if (!__DEV__) {
24
+ return;
25
+ }
26
+
27
+ console.warn(
28
+ `[settings-storage] ${action} failed: localStorage unavailable; using fallbacks for this session.`
29
+ );
30
+ }
31
+
32
+ export async function loadSetting(key: string, fallback: boolean): Promise<boolean> {
33
+ try {
34
+ if (Platform.OS === "web") {
35
+ if (!WEB_STORAGE_AVAILABLE) {
36
+ warnNoStorage("load");
37
+ return fallback;
38
+ }
39
+ const v = window.localStorage.getItem(key);
40
+ return v !== null ? v === "true" : fallback;
41
+ }
42
+ const v = await SecureStore.getItemAsync(key);
43
+ return v !== null ? v === "true" : fallback;
44
+ } catch (e) {
45
+ if (__DEV__) {
46
+ console.warn(`[settings-storage] load(${key}) threw:`, e);
47
+ }
48
+ return fallback;
49
+ }
50
+ }
51
+
52
+ export async function saveSetting(key: string, value: boolean): Promise<void> {
53
+ try {
54
+ if (Platform.OS === "web") {
55
+ if (!WEB_STORAGE_AVAILABLE) {
56
+ warnNoStorage("save");
57
+ return;
58
+ }
59
+ window.localStorage.setItem(key, String(value));
60
+ return;
61
+ }
62
+ await SecureStore.setItemAsync(key, String(value));
63
+ } catch (e) {
64
+ if (__DEV__) {
65
+ console.warn(`[settings-storage] save(${key}) threw:`, e);
66
+ }
67
+ }
68
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Input validators used by the UI to surface preview feedback before
3
+ * the user hits the server. The Entropy Softworks application servers
4
+ * enforce these same rules authoritatively (see `entropy-auth`'s
5
+ * `password::policy` / `Validation`); these implementations are kept
6
+ * deliberately strict to avoid false positives on the client.
7
+ *
8
+ * Email: a permissive `local@host.tld` shape — anything stricter risks
9
+ * rejecting valid addresses, and the server validates anyway.
10
+ *
11
+ * Password: matches the default `entropy-auth` policy (≥8 chars, mixed
12
+ * case, digit, symbol). Apps that opt their server into a different
13
+ * policy can ignore the `valid` result and let the server be the source
14
+ * of truth.
15
+ *
16
+ * Username: 3–30 chars, ASCII alnum + underscore.
17
+ */
18
+
19
+ export interface PasswordValidation {
20
+ valid: boolean;
21
+ errors: string[];
22
+ }
23
+
24
+ export interface UsernameValidation {
25
+ valid: boolean;
26
+ error?: string;
27
+ }
28
+
29
+ const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
30
+ const UPPER = /[A-Z]/;
31
+ const LOWER = /[a-z]/;
32
+ const DIGIT = /[0-9]/;
33
+ const SYMBOL = /[!@#$%^&*()_+\-=[\]{}|;:,.<>?]/;
34
+ const USERNAME = /^[a-zA-Z0-9_]+$/;
35
+
36
+ export function validateEmail(email: string): boolean {
37
+ return EMAIL.test(email);
38
+ }
39
+
40
+ export function validatePassword(password: string): PasswordValidation {
41
+ const errors: string[] = [];
42
+ if (password.length < 8) {
43
+ errors.push("Password must be at least 8 characters");
44
+ }
45
+ if (!UPPER.test(password)) {
46
+ errors.push("Password must contain at least one uppercase letter");
47
+ }
48
+ if (!LOWER.test(password)) {
49
+ errors.push("Password must contain at least one lowercase letter");
50
+ }
51
+ if (!DIGIT.test(password)) {
52
+ errors.push("Password must contain at least one number");
53
+ }
54
+ if (!SYMBOL.test(password)) {
55
+ errors.push("Password must contain at least one special character");
56
+ }
57
+ return { valid: errors.length === 0, errors };
58
+ }
59
+
60
+ export function validateUsername(username: string): UsernameValidation {
61
+ if (username.length < 3 || username.length > 30) {
62
+ return { valid: false, error: "Username must be between 3 and 30 characters" };
63
+ }
64
+ if (!USERNAME.test(username)) {
65
+ return {
66
+ valid: false,
67
+ error: "Username can only contain letters, numbers, and underscores",
68
+ };
69
+ }
70
+ return { valid: true };
71
+ }