@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,120 @@
1
+ /**
2
+ * Have-I-Been-Pwned password lookup using `k`-anonymity.
3
+ *
4
+ * Sends only the first five hex characters of the SHA-1 hash to the HIBP
5
+ * API. The full hash and plaintext never leave the device. Designed to be
6
+ * called from a debounced password-input change handler — see `debounce`
7
+ * below.
8
+ */
9
+
10
+ import { Platform } from "react-native";
11
+ import * as Crypto from "expo-crypto";
12
+ import { fetchWithTimeout } from "../utils/fetch";
13
+ import { getCachedPolicy, type HibpPolicy } from "./hibp-cache";
14
+
15
+ export interface PwnedPasswordResult {
16
+ /** `true` = breached, `false` = safe, `null` = check failed (unknown). */
17
+ isPwned: boolean | null;
18
+ count: number;
19
+ error?: string;
20
+ }
21
+
22
+ export async function checkPasswordPwned(password: string): Promise<PwnedPasswordResult> {
23
+ try {
24
+ const hash = await Crypto.digestStringAsync(Crypto.CryptoDigestAlgorithm.SHA1, password, {
25
+ encoding: Crypto.CryptoEncoding.HEX,
26
+ });
27
+ const hashUpperCase = hash.toUpperCase();
28
+ const prefix = hashUpperCase.substring(0, 5);
29
+ const suffix = hashUpperCase.substring(5);
30
+
31
+ const response = await fetchWithTimeout(
32
+ `https://api.pwnedpasswords.com/range/${prefix}`,
33
+ {
34
+ method: "GET",
35
+ headers: {
36
+ // User-Agent is a forbidden header in browsers and cannot be set
37
+ // via fetch — the browser sends its own. On native, expo handles it.
38
+ ...(Platform.OS !== "web" ? { "User-Agent": "Entropy Softworks Client" } : {}),
39
+ "Add-Padding": "true",
40
+ },
41
+ },
42
+ 10_000
43
+ );
44
+
45
+ if (!response.ok) {
46
+ throw new Error(`HIBP API returned status ${response.status}`);
47
+ }
48
+
49
+ const text = await response.text();
50
+ const hashes = text.split("\r\n").filter((line) => line.length > 0);
51
+
52
+ for (const hashLine of hashes) {
53
+ const [hashSuffix, count] = hashLine.split(":");
54
+ if (hashSuffix === suffix && count) {
55
+ return { isPwned: true, count: parseInt(count, 10) };
56
+ }
57
+ }
58
+
59
+ return { isPwned: false, count: 0 };
60
+ } catch {
61
+ return {
62
+ isPwned: null,
63
+ count: 0,
64
+ error: "Unable to verify password security. Please try again later.",
65
+ };
66
+ }
67
+ }
68
+
69
+ export interface PwnedMessage {
70
+ title: string;
71
+ message: string;
72
+ severity: "error" | "warning" | "info";
73
+ }
74
+
75
+ /**
76
+ * Build a user-friendly message for a HIBP result. The policy comes from
77
+ * the shared cache populated by the consumer (see `hibp-cache.ts`). If no
78
+ * policy is cached, falls back to a non-enforcing default.
79
+ */
80
+ export function getPwnedMessage(result: PwnedPasswordResult): PwnedMessage {
81
+ if (result.error) {
82
+ return {
83
+ title: "Unable to Verify",
84
+ message: result.error,
85
+ severity: "info",
86
+ };
87
+ }
88
+
89
+ if (result.isPwned === false) {
90
+ return {
91
+ title: "Password Is Safe",
92
+ message: "This password has not been found in any known data breaches.",
93
+ severity: "info",
94
+ };
95
+ }
96
+
97
+ const policy: HibpPolicy = getCachedPolicy() ?? {
98
+ enabled: true,
99
+ enforce: false,
100
+ block_threshold: 0,
101
+ };
102
+
103
+ if (policy.enforce && result.count > policy.block_threshold) {
104
+ return {
105
+ title: "Password Compromised!",
106
+ message: `This password has appeared in ${result.count.toLocaleString()} known data breaches. Please choose a different password.`,
107
+ severity: "error",
108
+ };
109
+ }
110
+
111
+ return {
112
+ title: "Password Found in Breach",
113
+ message: `This password has appeared ${result.count.toLocaleString()} time${result.count > 1 ? "s" : ""} in data breaches. Consider choosing a different password.`,
114
+ severity: "warning",
115
+ };
116
+ }
117
+
118
+ // `debounce` moved to `utils/debounce`; re-exported here for back-compat
119
+ // with existing `@entropy-softworks/ui/services/hibp` consumers.
120
+ export { debounce } from "../utils/debounce";
@@ -0,0 +1,52 @@
1
+ export { default as AuthService } from "./auth.service";
2
+ export type {
3
+ User,
4
+ AuthResponse,
5
+ LoginRequest,
6
+ RegisterRequest,
7
+ ErrorResponse,
8
+ MfaSetupResponse,
9
+ ServerConfig,
10
+ } from "./auth.service";
11
+
12
+ export { isPasskeySupported, registerPasskey, authenticateWithPasskey } from "./passkey";
13
+
14
+ export {
15
+ configureApi,
16
+ getBaseUrl,
17
+ getDefaultBaseUrl,
18
+ getStoragePrefix,
19
+ setBaseUrl,
20
+ resetBaseUrl,
21
+ loadServerUrlOverride,
22
+ getSessionToken,
23
+ storeSessionToken,
24
+ clearSessionToken,
25
+ getAuthMode,
26
+ getTenant,
27
+ setTenant,
28
+ type ApiConfig,
29
+ } from "./api-config";
30
+
31
+ export {
32
+ checkPasswordPwned,
33
+ getPwnedMessage,
34
+ type PwnedPasswordResult,
35
+ type PwnedMessage,
36
+ } from "./hibp";
37
+
38
+ export {
39
+ getCachedPolicy as getCachedHibpPolicy,
40
+ fetchHibpPolicy,
41
+ getHibpPolicy,
42
+ type HibpPolicy,
43
+ } from "./hibp-cache";
44
+
45
+ export {
46
+ buildFaviconUrl,
47
+ customIconId,
48
+ customIconRef,
49
+ isCustomIconRef,
50
+ resolveIcon,
51
+ type ResolvedIcon,
52
+ } from "./favicon";
@@ -0,0 +1,213 @@
1
+ // WebAuthn passkey client (auth-only — no PRF / key-wrapping). Shared across
2
+ // Entropy Softworks Expo apps. Drives the OS authenticator (Face ID / Touch ID
3
+ // / device passkey) and talks to the server's /auth/passkey/* ceremony
4
+ // endpoints.
5
+ //
6
+ // Native (Expo dev build / store build) uses `react-native-passkey`, which
7
+ // consumes/produces the WebAuthn JSON shapes directly (base64url strings). Web
8
+ // uses the DOM `navigator.credentials` API, which needs ArrayBuffers, so the
9
+ // base64url fields are rewritten in and serialized back out.
10
+ //
11
+ // NOTE: passkeys require a native build (not Expo Go) and an associated-domain
12
+ // / asset-link binding the app to the server's WEBAUTHN_RP_ID. `react-native-
13
+ // passkey` is a lazy import so apps/platforms that never call these functions
14
+ // don't need it linked.
15
+
16
+ import { Platform } from "react-native";
17
+ import { getBaseUrl, getSessionToken, storeSessionToken } from "./api-config";
18
+ import type { AuthResponse } from "./auth.service";
19
+
20
+ // ── base64url ⇄ ArrayBuffer (web path only) ────────────────────────────────
21
+
22
+ function b64urlToBuffer(value: string): ArrayBuffer {
23
+ const pad = "=".repeat((4 - (value.length % 4)) % 4);
24
+ const base64 = (value + pad).replace(/-/g, "+").replace(/_/g, "/");
25
+ const bin = atob(base64);
26
+ const bytes = new Uint8Array(bin.length);
27
+ for (let i = 0; i < bin.length; i++) {
28
+ bytes[i] = bin.charCodeAt(i);
29
+ }
30
+ return bytes.buffer;
31
+ }
32
+
33
+ function bufferToB64url(buf: ArrayBuffer): string {
34
+ const bytes = new Uint8Array(buf);
35
+ let bin = "";
36
+ for (const b of bytes) {
37
+ bin += String.fromCharCode(b);
38
+ }
39
+ return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
40
+ }
41
+
42
+ // ── transport ───────────────────────────────────────────────────────────────
43
+
44
+ async function post(path: string, body: unknown): Promise<Response> {
45
+ const headers: Record<string, string> = { "Content-Type": "application/json" };
46
+ const token = await getSessionToken();
47
+ if (token) {
48
+ headers.Authorization = `Bearer ${token}`;
49
+ }
50
+ return fetch(`${getBaseUrl()}${path}`, {
51
+ method: "POST",
52
+ headers,
53
+ credentials: "include",
54
+ body: JSON.stringify(body),
55
+ });
56
+ }
57
+
58
+ async function postJson<T>(path: string, body: unknown): Promise<T> {
59
+ const res = await post(path, body);
60
+ if (!res.ok) {
61
+ let message = `request failed (${res.status})`;
62
+ try {
63
+ const data = await res.json();
64
+ message = data?.error?.message ?? message;
65
+ } catch {
66
+ // non-JSON error body; keep the status message
67
+ }
68
+ throw new Error(message);
69
+ }
70
+ return (await res.json()) as T;
71
+ }
72
+
73
+ interface BeginResponse {
74
+ ceremonyId: string;
75
+ publickeyOptions: Record<string, unknown>;
76
+ }
77
+
78
+ // react-native-passkey is a native module; import lazily so the web bundle and
79
+ // type-checking don't hard-depend on it being linked.
80
+ async function nativePasskey() {
81
+ const mod = await import("react-native-passkey");
82
+ return mod.Passkey;
83
+ }
84
+
85
+ /** Whether this platform can do WebAuthn at all. */
86
+ export async function isPasskeySupported(): Promise<boolean> {
87
+ if (Platform.OS === "web") {
88
+ return (
89
+ typeof globalThis !== "undefined" &&
90
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
91
+ !!(globalThis as any).PublicKeyCredential
92
+ );
93
+ }
94
+ try {
95
+ const Passkey = await nativePasskey();
96
+ return Boolean((Passkey as { isSupported?: () => boolean }).isSupported?.());
97
+ } catch {
98
+ return false;
99
+ }
100
+ }
101
+
102
+ /** The inner `publicKey` object webauthn-rs wraps its options in. */
103
+ function inner(options: Record<string, unknown>): Record<string, unknown> {
104
+ const pk = options.publicKey;
105
+ return (pk && typeof pk === "object" ? pk : options) as Record<string, unknown>;
106
+ }
107
+
108
+ async function createCredential(options: Record<string, unknown>): Promise<unknown> {
109
+ if (Platform.OS === "web") {
110
+ const pk = { ...inner(options) };
111
+ pk.challenge = b64urlToBuffer(pk.challenge as string);
112
+ const user = pk.user as Record<string, unknown> | undefined;
113
+ if (user && typeof user.id === "string") {
114
+ pk.user = { ...user, id: b64urlToBuffer(user.id) };
115
+ }
116
+ if (Array.isArray(pk.excludeCredentials)) {
117
+ pk.excludeCredentials = (pk.excludeCredentials as Record<string, unknown>[]).map((c) => ({
118
+ ...c,
119
+ id: typeof c.id === "string" ? b64urlToBuffer(c.id) : c.id,
120
+ }));
121
+ }
122
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
123
+ const cred = (await navigator.credentials.create({ publicKey: pk as any })) as any;
124
+ return serializeWebCredential(cred);
125
+ }
126
+ const Passkey = await nativePasskey();
127
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
128
+ return (Passkey as any).create(inner(options));
129
+ }
130
+
131
+ async function getCredential(options: Record<string, unknown>): Promise<unknown> {
132
+ if (Platform.OS === "web") {
133
+ const pk = { ...inner(options) };
134
+ pk.challenge = b64urlToBuffer(pk.challenge as string);
135
+ if (Array.isArray(pk.allowCredentials)) {
136
+ pk.allowCredentials = (pk.allowCredentials as Record<string, unknown>[]).map((c) => ({
137
+ ...c,
138
+ id: typeof c.id === "string" ? b64urlToBuffer(c.id) : c.id,
139
+ }));
140
+ }
141
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
142
+ const cred = (await navigator.credentials.get({ publicKey: pk as any })) as any;
143
+ return serializeWebCredential(cred);
144
+ }
145
+ const Passkey = await nativePasskey();
146
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
147
+ return (Passkey as any).get(inner(options));
148
+ }
149
+
150
+ // Convert a DOM PublicKeyCredential into the base64url JSON the server expects.
151
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
152
+ function serializeWebCredential(cred: any): unknown {
153
+ const r = cred.response;
154
+ const out: Record<string, unknown> = {
155
+ id: cred.id,
156
+ rawId: bufferToB64url(cred.rawId),
157
+ type: cred.type,
158
+ response: {} as Record<string, unknown>,
159
+ };
160
+ const resp = out.response as Record<string, unknown>;
161
+ if (r.attestationObject) {
162
+ resp.attestationObject = bufferToB64url(r.attestationObject);
163
+ }
164
+ if (r.clientDataJSON) {
165
+ resp.clientDataJSON = bufferToB64url(r.clientDataJSON);
166
+ }
167
+ if (r.authenticatorData) {
168
+ resp.authenticatorData = bufferToB64url(r.authenticatorData);
169
+ }
170
+ if (r.signature) {
171
+ resp.signature = bufferToB64url(r.signature);
172
+ }
173
+ if (r.userHandle) {
174
+ resp.userHandle = bufferToB64url(r.userHandle);
175
+ }
176
+ return out;
177
+ }
178
+
179
+ function clientExtensionResults(cred: unknown): Record<string, unknown> | undefined {
180
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
181
+ const c = cred as any;
182
+ if (typeof c?.getClientExtensionResults === "function") {
183
+ return c.getClientExtensionResults();
184
+ }
185
+ return c?.clientExtensionResults;
186
+ }
187
+
188
+ /** Register a new passkey for the signed-in user. Triggers the OS biometric. */
189
+ export async function registerPasskey(label: string): Promise<{ label: string }> {
190
+ const begin = await postJson<BeginResponse>("/auth/passkey/register/begin", { label });
191
+ const credential = await createCredential(begin.publickeyOptions);
192
+ return postJson<{ label: string }>("/auth/passkey/register/finish", {
193
+ ceremonyId: begin.ceremonyId,
194
+ label,
195
+ credential,
196
+ clientExtensionResults: clientExtensionResults(credential),
197
+ });
198
+ }
199
+
200
+ /** Sign in with a passkey. Returns the authenticated session. */
201
+ export async function authenticateWithPasskey(email?: string): Promise<AuthResponse> {
202
+ const begin = await postJson<BeginResponse>("/auth/passkey/authenticate/begin", { email });
203
+ const credential = await getCredential(begin.publickeyOptions);
204
+ const auth = await postJson<AuthResponse>("/auth/passkey/authenticate/finish", {
205
+ ceremonyId: begin.ceremonyId,
206
+ credential,
207
+ clientExtensionResults: clientExtensionResults(credential),
208
+ });
209
+ if (auth.session_token) {
210
+ await storeSessionToken(auth.session_token);
211
+ }
212
+ return auth;
213
+ }
@@ -0,0 +1,24 @@
1
+ declare module "react-native-qrcode-svg" {
2
+ import type { ComponentType } from "react";
3
+
4
+ export interface QRCodeProps {
5
+ value: string;
6
+ size?: number;
7
+ color?: string;
8
+ backgroundColor?: string;
9
+ quietZone?: number;
10
+ enableLinearGradient?: boolean;
11
+ linearGradient?: string[];
12
+ logo?: { uri: string } | number;
13
+ logoSize?: number;
14
+ logoBackgroundColor?: string;
15
+ logoMargin?: number;
16
+ logoBorderRadius?: number;
17
+ ecl?: "L" | "M" | "Q" | "H";
18
+ getRef?: (ref: unknown) => void;
19
+ onError?: (error: unknown) => void;
20
+ }
21
+
22
+ const QRCode: ComponentType<QRCodeProps>;
23
+ export default QRCode;
24
+ }
@@ -0,0 +1,59 @@
1
+ import * as Clipboard from "expo-clipboard";
2
+ import { clearClipboardNow, copyToClipboard } from "../clipboard";
3
+
4
+ jest.mock("expo-clipboard", () => {
5
+ let value = "";
6
+ return {
7
+ setStringAsync: jest.fn(async (v) => {
8
+ value = v;
9
+ return true;
10
+ }),
11
+ getStringAsync: async () => value,
12
+ __get: () => value,
13
+ };
14
+ });
15
+
16
+ const setStringAsync = Clipboard.setStringAsync as jest.MockedFunction<
17
+ typeof Clipboard.setStringAsync
18
+ >;
19
+
20
+ describe("copyToClipboard", () => {
21
+ beforeEach(() => {
22
+ setStringAsync.mockClear();
23
+ jest.useFakeTimers();
24
+ });
25
+ afterEach(() => {
26
+ jest.useRealTimers();
27
+ });
28
+
29
+ it("writes the value and schedules a 30s auto-clear", async () => {
30
+ const ok = await copyToClipboard("secret");
31
+ expect(ok).toBe(true);
32
+ expect(setStringAsync).toHaveBeenLastCalledWith("secret");
33
+ jest.advanceTimersByTime(30_000);
34
+ expect(setStringAsync).toHaveBeenLastCalledWith("");
35
+ });
36
+
37
+ it("re-scheduling cancels the prior auto-clear", async () => {
38
+ await copyToClipboard("first");
39
+ jest.advanceTimersByTime(15_000);
40
+ await copyToClipboard("second");
41
+ jest.advanceTimersByTime(15_000);
42
+ // Still inside the second copy's window — auto-clear hasn't fired.
43
+ expect(setStringAsync).toHaveBeenLastCalledWith("second");
44
+ });
45
+
46
+ it("clearClipboardNow wipes immediately and cancels pending timers", async () => {
47
+ await copyToClipboard("x");
48
+ await clearClipboardNow();
49
+ expect(setStringAsync).toHaveBeenLastCalledWith("");
50
+ setStringAsync.mockClear();
51
+ jest.advanceTimersByTime(60_000);
52
+ expect(setStringAsync).not.toHaveBeenCalled();
53
+ });
54
+
55
+ it("returns false when the platform refuses the write", async () => {
56
+ setStringAsync.mockResolvedValueOnce(false);
57
+ expect(await copyToClipboard("nope")).toBe(false);
58
+ });
59
+ });
@@ -0,0 +1,24 @@
1
+ import { cn } from "../cn";
2
+
3
+ describe("cn", () => {
4
+ it("joins truthy class names", () => {
5
+ expect(cn("a", "b")).toBe("a b");
6
+ });
7
+
8
+ it("drops falsy values", () => {
9
+ expect(cn("a", false, undefined, null, "", "b")).toBe("a b");
10
+ });
11
+
12
+ it("resolves tailwind conflicts in favour of the last class", () => {
13
+ expect(cn("px-2", "px-4")).toBe("px-4");
14
+ expect(cn("text-red-500", "text-blue-500")).toBe("text-blue-500");
15
+ });
16
+
17
+ it("preserves non-conflicting tailwind classes", () => {
18
+ expect(cn("flex", "items-center", "p-4")).toBe("flex items-center p-4");
19
+ });
20
+
21
+ it("flattens nested arrays and objects", () => {
22
+ expect(cn(["a", "b"], { c: true, d: false })).toBe("a b c");
23
+ });
24
+ });
@@ -0,0 +1,42 @@
1
+ import { debounce } from "../debounce";
2
+
3
+ describe("debounce", () => {
4
+ beforeEach(() => {
5
+ jest.useFakeTimers();
6
+ });
7
+ afterEach(() => {
8
+ jest.useRealTimers();
9
+ });
10
+
11
+ it("collapses rapid calls into a single trailing invocation", () => {
12
+ const fn = jest.fn();
13
+ const debounced = debounce(fn, 100);
14
+ debounced("a");
15
+ debounced("b");
16
+ debounced("c");
17
+ expect(fn).not.toHaveBeenCalled();
18
+ jest.advanceTimersByTime(100);
19
+ expect(fn).toHaveBeenCalledTimes(1);
20
+ expect(fn).toHaveBeenCalledWith("c");
21
+ });
22
+
23
+ it("cancels a pending invocation when cancel() runs first", () => {
24
+ const fn = jest.fn();
25
+ const debounced = debounce(fn, 100);
26
+ debounced("a");
27
+ debounced.cancel();
28
+ jest.advanceTimersByTime(200);
29
+ expect(fn).not.toHaveBeenCalled();
30
+ });
31
+
32
+ it("re-arms after a successful invocation", () => {
33
+ const fn = jest.fn();
34
+ const debounced = debounce(fn, 50);
35
+ debounced("a");
36
+ jest.advanceTimersByTime(50);
37
+ debounced("b");
38
+ jest.advanceTimersByTime(50);
39
+ expect(fn).toHaveBeenCalledTimes(2);
40
+ expect(fn.mock.calls.map((c) => c[0])).toEqual(["a", "b"]);
41
+ });
42
+ });
@@ -0,0 +1,18 @@
1
+ import { getErrorMessage } from "../error";
2
+
3
+ describe("getErrorMessage", () => {
4
+ it("extracts the message off an Error instance", () => {
5
+ expect(getErrorMessage(new Error("boom"))).toBe("boom");
6
+ });
7
+
8
+ it("falls back to the provided fallback for non-Error values", () => {
9
+ expect(getErrorMessage("nope", "default")).toBe("default");
10
+ expect(getErrorMessage(undefined, "default")).toBe("default");
11
+ expect(getErrorMessage({ thing: 1 }, "default")).toBe("default");
12
+ expect(getErrorMessage(123, "default")).toBe("default");
13
+ });
14
+
15
+ it("uses the default fallback when none is supplied", () => {
16
+ expect(getErrorMessage(123)).toBe("An unexpected error occurred");
17
+ });
18
+ });
@@ -0,0 +1,33 @@
1
+ import { extractErrorMessage } from "../http-error";
2
+
3
+ function jsonResponse(body: unknown): Response {
4
+ return new Response(JSON.stringify(body), {
5
+ status: 400,
6
+ headers: { "Content-Type": "application/json" },
7
+ });
8
+ }
9
+
10
+ describe("extractErrorMessage", () => {
11
+ it("returns the top-level message when no details are supplied", async () => {
12
+ const r = jsonResponse({ error: { message: "Bad request" } });
13
+ expect(await extractErrorMessage(r, "fb")).toBe("Bad request");
14
+ });
15
+
16
+ it("joins details with commas", async () => {
17
+ const r = jsonResponse({ error: { message: "ignored", details: ["a", "b", "c"] } });
18
+ expect(await extractErrorMessage(r, "fb")).toBe("a, b, c");
19
+ });
20
+
21
+ it("caps the number of details rendered and marks the truncation", async () => {
22
+ const details = Array.from({ length: 20 }, (_, i) => `e${i}`);
23
+ const r = jsonResponse({ error: { message: "ignored", details } });
24
+ const out = await extractErrorMessage(r, "fb");
25
+ expect(out.endsWith(", …")).toBe(true);
26
+ expect(out.split(", ").length).toBe(6); // 5 entries + the ellipsis marker
27
+ });
28
+
29
+ it("returns fallback for unparseable bodies", async () => {
30
+ const r = new Response("not json", { status: 500 });
31
+ expect(await extractErrorMessage(r, "fallback")).toBe("fallback");
32
+ });
33
+ });
@@ -0,0 +1,22 @@
1
+ import { darkPalette, getPalette, lightPalette } from "../palette";
2
+
3
+ describe("palette", () => {
4
+ it("returns lightPalette for the light scheme", () => {
5
+ expect(getPalette("light")).toBe(lightPalette);
6
+ });
7
+
8
+ it("returns darkPalette for the dark scheme", () => {
9
+ expect(getPalette("dark")).toBe(darkPalette);
10
+ });
11
+
12
+ it("light and dark cover the same token surface", () => {
13
+ expect(Object.keys(lightPalette).sort()).toEqual(Object.keys(darkPalette).sort());
14
+ });
15
+
16
+ it("light vs dark have inverted background / foreground", () => {
17
+ expect(lightPalette.background).toBe("rgb(255, 255, 255)");
18
+ expect(lightPalette.foreground).toBe("rgb(0, 0, 0)");
19
+ expect(darkPalette.background).toBe("rgb(0, 0, 0)");
20
+ expect(darkPalette.foreground).toBe("rgb(255, 255, 255)");
21
+ });
22
+ });
@@ -0,0 +1,55 @@
1
+ import { generatePassword } from "../password";
2
+
3
+ const CRYPTO_OK = typeof globalThis.crypto?.getRandomValues === "function";
4
+ const describeIfCrypto = CRYPTO_OK ? describe : describe.skip;
5
+
6
+ describeIfCrypto("generatePassword", () => {
7
+ it("returns exactly the requested length", () => {
8
+ expect(generatePassword({ length: 16 }).length).toBe(16);
9
+ expect(generatePassword({ length: 32 }).length).toBe(32);
10
+ expect(generatePassword({ length: 64 }).length).toBe(64);
11
+ });
12
+
13
+ it("draws only from the enabled character class", () => {
14
+ const pw = generatePassword({
15
+ length: 64,
16
+ includeUppercase: false,
17
+ includeLowercase: false,
18
+ includeNumbers: true,
19
+ includeSymbols: false,
20
+ });
21
+ expect(/^\d+$/.test(pw)).toBe(true);
22
+ });
23
+
24
+ it("excludes similar glyphs when requested", () => {
25
+ const pw = generatePassword({
26
+ length: 256,
27
+ includeUppercase: true,
28
+ includeLowercase: true,
29
+ includeNumbers: true,
30
+ includeSymbols: false,
31
+ excludeSimilar: true,
32
+ });
33
+ for (const ch of ["i", "l", "1", "L", "o", "0", "O"]) {
34
+ expect(pw).not.toContain(ch);
35
+ }
36
+ });
37
+
38
+ it("throws when every class is disabled", () => {
39
+ expect(() =>
40
+ generatePassword({
41
+ length: 16,
42
+ includeUppercase: false,
43
+ includeLowercase: false,
44
+ includeNumbers: false,
45
+ includeSymbols: false,
46
+ })
47
+ ).toThrow();
48
+ });
49
+
50
+ it("draws different output across calls (non-deterministic)", () => {
51
+ const a = generatePassword({ length: 32 });
52
+ const b = generatePassword({ length: 32 });
53
+ expect(a).not.toBe(b);
54
+ });
55
+ });
@@ -0,0 +1,15 @@
1
+ import { loadSetting, saveSetting } from "../settings-storage";
2
+
3
+ describe("settings-storage (native path)", () => {
4
+ it("returns the fallback when nothing is stored", async () => {
5
+ expect(await loadSetting("vault_setting_unset_42", true)).toBe(true);
6
+ expect(await loadSetting("vault_setting_unset_43", false)).toBe(false);
7
+ });
8
+
9
+ it("round-trips a boolean via SecureStore", async () => {
10
+ await saveSetting("vault_setting_alpha", true);
11
+ expect(await loadSetting("vault_setting_alpha", false)).toBe(true);
12
+ await saveSetting("vault_setting_alpha", false);
13
+ expect(await loadSetting("vault_setting_alpha", true)).toBe(false);
14
+ });
15
+ });