@tinyrack/issuary-server 0.22.0 → 0.23.0

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 (306) hide show
  1. package/dist/entities/oauth-client.entity.d.ts +0 -18
  2. package/dist/entities/oauth-client.entity.d.ts.map +1 -1
  3. package/dist/entities/oauth-code.entity.d.ts +0 -18
  4. package/dist/entities/oauth-code.entity.d.ts.map +1 -1
  5. package/dist/entities/oauth-device-code.entity.d.ts +0 -18
  6. package/dist/entities/oauth-device-code.entity.d.ts.map +1 -1
  7. package/dist/entities/revoked-token.entity.d.ts +0 -18
  8. package/dist/entities/revoked-token.entity.d.ts.map +1 -1
  9. package/dist/entities/terms-content.entity.d.ts +0 -9
  10. package/dist/entities/terms-content.entity.d.ts.map +1 -1
  11. package/dist/entities/terms.entity.d.ts +0 -9
  12. package/dist/entities/terms.entity.d.ts.map +1 -1
  13. package/dist/entities/user-consent.entity.d.ts +0 -18
  14. package/dist/entities/user-consent.entity.d.ts.map +1 -1
  15. package/dist/entities/user-terms-consent.entity.d.ts +0 -9
  16. package/dist/entities/user-terms-consent.entity.d.ts.map +1 -1
  17. package/dist/entities/user-totp-recovery-code.entity.d.ts +0 -9
  18. package/dist/entities/user-totp-recovery-code.entity.d.ts.map +1 -1
  19. package/dist/entities/user-totp.entity.d.ts +0 -9
  20. package/dist/entities/user-totp.entity.d.ts.map +1 -1
  21. package/dist/entities/user.entity.d.ts +0 -9
  22. package/dist/entities/user.entity.d.ts.map +1 -1
  23. package/dist/entities/user.entity.js +0 -4
  24. package/dist/entities/user.entity.js.map +1 -1
  25. package/dist/entrypoints/app.d.ts +0 -53
  26. package/dist/entrypoints/app.d.ts.map +1 -1
  27. package/dist/entrypoints/database/postgres/compiled-functions.d.ts +20 -23
  28. package/dist/entrypoints/database/postgres/compiled-functions.d.ts.map +1 -1
  29. package/dist/entrypoints/database/postgres/compiled-functions.js +104 -134
  30. package/dist/entrypoints/database/postgres/compiled-functions.js.map +1 -1
  31. package/dist/entrypoints/database/sqlite/compiled-functions.d.ts +20 -23
  32. package/dist/entrypoints/database/sqlite/compiled-functions.d.ts.map +1 -1
  33. package/dist/entrypoints/database/sqlite/compiled-functions.js +104 -134
  34. package/dist/entrypoints/database/sqlite/compiled-functions.js.map +1 -1
  35. package/dist/lib/config/resolved.d.ts +0 -1
  36. package/dist/lib/config/resolved.d.ts.map +1 -1
  37. package/dist/lib/config/security.d.ts +0 -2
  38. package/dist/lib/config/security.d.ts.map +1 -1
  39. package/dist/lib/config/security.js +0 -5
  40. package/dist/lib/config/security.js.map +1 -1
  41. package/dist/middleware/auth.d.ts +0 -8
  42. package/dist/middleware/auth.d.ts.map +1 -1
  43. package/dist/middleware/auth.js +0 -64
  44. package/dist/middleware/auth.js.map +1 -1
  45. package/dist/middleware/session.d.ts +0 -9
  46. package/dist/middleware/session.d.ts.map +1 -1
  47. package/dist/middleware/session.js +0 -18
  48. package/dist/middleware/session.js.map +1 -1
  49. package/dist/migrations/postgres/Migration20260825140000_drop_password_reset_required.d.ts +6 -0
  50. package/dist/migrations/postgres/Migration20260825140000_drop_password_reset_required.d.ts.map +1 -0
  51. package/dist/migrations/postgres/Migration20260825140000_drop_password_reset_required.js +19 -0
  52. package/dist/migrations/postgres/Migration20260825140000_drop_password_reset_required.js.map +1 -0
  53. package/dist/migrations/postgres/index.d.ts.map +1 -1
  54. package/dist/migrations/postgres/index.js +2 -0
  55. package/dist/migrations/postgres/index.js.map +1 -1
  56. package/dist/migrations/sqlite/Migration20260825140000_drop_password_reset_required.d.ts +6 -0
  57. package/dist/migrations/sqlite/Migration20260825140000_drop_password_reset_required.d.ts.map +1 -0
  58. package/dist/migrations/sqlite/Migration20260825140000_drop_password_reset_required.js +19 -0
  59. package/dist/migrations/sqlite/Migration20260825140000_drop_password_reset_required.js.map +1 -0
  60. package/dist/migrations/sqlite/index.d.ts.map +1 -1
  61. package/dist/migrations/sqlite/index.js +2 -0
  62. package/dist/migrations/sqlite/index.js.map +1 -1
  63. package/dist/repositories/oauth-code.repository.d.ts +2 -2
  64. package/dist/repositories/oauth-code.repository.d.ts.map +1 -1
  65. package/dist/repositories/oauth-code.repository.js +3 -7
  66. package/dist/repositories/oauth-code.repository.js.map +1 -1
  67. package/dist/repositories/oauth-device-code.repository.d.ts +4 -4
  68. package/dist/repositories/oauth-device-code.repository.d.ts.map +1 -1
  69. package/dist/repositories/oauth-device-code.repository.js +6 -18
  70. package/dist/repositories/oauth-device-code.repository.js.map +1 -1
  71. package/dist/repositories/user-totp-recovery-code.repository.d.ts +1 -7
  72. package/dist/repositories/user-totp-recovery-code.repository.d.ts.map +1 -1
  73. package/dist/repositories/user-totp-recovery-code.repository.js +1 -2
  74. package/dist/repositories/user-totp-recovery-code.repository.js.map +1 -1
  75. package/dist/repositories/user-totp.repository.d.ts +0 -9
  76. package/dist/repositories/user-totp.repository.d.ts.map +1 -1
  77. package/dist/routes/api/auth/index.d.ts +0 -52
  78. package/dist/routes/api/auth/index.d.ts.map +1 -1
  79. package/dist/routes/api/auth/index.js +0 -2
  80. package/dist/routes/api/auth/index.js.map +1 -1
  81. package/dist/routes/api/auth/passkey/verify/post.d.ts +0 -37
  82. package/dist/routes/api/auth/passkey/verify/post.d.ts.map +1 -1
  83. package/dist/routes/api/auth/passkey/verify/post.js +2 -8
  84. package/dist/routes/api/auth/passkey/verify/post.js.map +1 -1
  85. package/dist/routes/api/index.d.ts +0 -52
  86. package/dist/routes/api/index.d.ts.map +1 -1
  87. package/dist/routes/index.d.ts +0 -52
  88. package/dist/routes/index.d.ts.map +1 -1
  89. package/dist/routes/oauth/device/get-post.js +5 -5
  90. package/dist/routes/oauth/device/get-post.js.map +1 -1
  91. package/dist/schemas/error.d.ts +0 -25
  92. package/dist/schemas/error.d.ts.map +1 -1
  93. package/dist/schemas/error.js +0 -1
  94. package/dist/schemas/error.js.map +1 -1
  95. package/dist/schemas/response.d.ts +0 -22
  96. package/dist/schemas/response.d.ts.map +1 -1
  97. package/dist/schemas/response.js +0 -8
  98. package/dist/schemas/response.js.map +1 -1
  99. package/dist/seeders/config.seeder.js +0 -1
  100. package/dist/seeders/config.seeder.js.map +1 -1
  101. package/dist/services/container.d.ts +0 -1
  102. package/dist/services/container.d.ts.map +1 -1
  103. package/dist/services/container.js +0 -15
  104. package/dist/services/container.js.map +1 -1
  105. package/dist/services/oauth-client.service.d.ts +0 -1
  106. package/dist/services/oauth-client.service.d.ts.map +1 -1
  107. package/dist/services/oauth-client.service.js +2 -17
  108. package/dist/services/oauth-client.service.js.map +1 -1
  109. package/dist/services/oauth-connect.service.d.ts.map +1 -1
  110. package/dist/services/oauth-connect.service.js +0 -4
  111. package/dist/services/oauth-connect.service.js.map +1 -1
  112. package/dist/services/oauth-token.service.d.ts.map +1 -1
  113. package/dist/services/oauth-token.service.js +4 -4
  114. package/dist/services/oauth-token.service.js.map +1 -1
  115. package/dist/services/password-auth.service.d.ts.map +1 -1
  116. package/dist/services/password-auth.service.js +3 -12
  117. package/dist/services/password-auth.service.js.map +1 -1
  118. package/dist/services/security.service.d.ts +0 -8
  119. package/dist/services/security.service.d.ts.map +1 -1
  120. package/dist/services/security.service.js +21 -82
  121. package/dist/services/security.service.js.map +1 -1
  122. package/dist/services/totp.service.js +2 -2
  123. package/dist/services/totp.service.js.map +1 -1
  124. package/package.json +1 -1
  125. package/public/assets/2fa-B5vLgDXQ.js +2 -0
  126. package/public/assets/{2fa-BgveHSB_.js.map → 2fa-B5vLgDXQ.js.map} +1 -1
  127. package/public/assets/2fa-BqsM_D-X.js +2 -0
  128. package/public/assets/{2fa-kiqzGSz2.js.map → 2fa-BqsM_D-X.js.map} +1 -1
  129. package/public/assets/2fa-C5vUVgr_.js +2 -0
  130. package/public/assets/{2fa-UL_cNRnQ.js.map → 2fa-C5vUVgr_.js.map} +1 -1
  131. package/public/assets/2fa-CCOTIHDZ.js +2 -0
  132. package/public/assets/{2fa-C37MUkye.js.map → 2fa-CCOTIHDZ.js.map} +1 -1
  133. package/public/assets/{DialogHandle-Zvz8SgS0.js → DialogHandle-BmtK2pRn.js} +2 -2
  134. package/public/assets/{DialogHandle-Zvz8SgS0.js.map → DialogHandle-BmtK2pRn.js.map} +1 -1
  135. package/public/assets/admin-BPijkLw-.js +2 -0
  136. package/public/assets/{admin-DbDuu6AI.js.map → admin-BPijkLw-.js.map} +1 -1
  137. package/public/assets/admin-users-DvPbw1Vq.js +2 -0
  138. package/public/assets/{admin-users-CwTJdiih.js.map → admin-users-DvPbw1Vq.js.map} +1 -1
  139. package/public/assets/{auth-choice-row-BlSsUI7d.js → auth-choice-row-BCGjuedo.js} +2 -2
  140. package/public/assets/{auth-choice-row-BlSsUI7d.js.map → auth-choice-row-BCGjuedo.js.map} +1 -1
  141. package/public/assets/{auth-field-HwxENJnB.js → auth-field-DD8kvl--.js} +2 -2
  142. package/public/assets/{auth-field-HwxENJnB.js.map → auth-field-DD8kvl--.js.map} +1 -1
  143. package/public/assets/{auth-layout-CoeqNhTT.js → auth-layout-VtpryDof.js} +2 -2
  144. package/public/assets/{auth-layout-CoeqNhTT.js.map → auth-layout-VtpryDof.js.map} +1 -1
  145. package/public/assets/{auth-page-header-CIs08f5Y.js → auth-page-header-CIn2E4zI.js} +2 -2
  146. package/public/assets/{auth-page-header-CIs08f5Y.js.map → auth-page-header-CIn2E4zI.js.map} +1 -1
  147. package/public/assets/{authorization-context-banner-BbtQmMYH.js → authorization-context-banner-SOuBHWIb.js} +2 -2
  148. package/public/assets/{authorization-context-banner-BbtQmMYH.js.map → authorization-context-banner-SOuBHWIb.js.map} +1 -1
  149. package/public/assets/{avatar-CttWiIv3.js → avatar-BEQjf2x6.js} +2 -2
  150. package/public/assets/{avatar-CttWiIv3.js.map → avatar-BEQjf2x6.js.map} +1 -1
  151. package/public/assets/{checkbox-CJrRV8S1.js → checkbox-gdHnSFQ-.js} +2 -2
  152. package/public/assets/{checkbox-CJrRV8S1.js.map → checkbox-gdHnSFQ-.js.map} +1 -1
  153. package/public/assets/config-DTQCUlfZ.js +2 -0
  154. package/public/assets/config-DTQCUlfZ.js.map +1 -0
  155. package/public/assets/{consent-BIhxtXHx.js → consent-BqqFDdsr.js} +2 -2
  156. package/public/assets/{consent-BIhxtXHx.js.map → consent-BqqFDdsr.js.map} +1 -1
  157. package/public/assets/{consent-DRmENn26.js → consent-CfRbz2R7.js} +2 -2
  158. package/public/assets/{consent-DRmENn26.js.map → consent-CfRbz2R7.js.map} +1 -1
  159. package/public/assets/email-C5vUVgr_.js +2 -0
  160. package/public/assets/{email-UL_cNRnQ.js.map → email-C5vUVgr_.js.map} +1 -1
  161. package/public/assets/{email-CVRDeJ78.js → email-CKKakxPJ.js} +2 -2
  162. package/public/assets/{email-CVRDeJ78.js.map → email-CKKakxPJ.js.map} +1 -1
  163. package/public/assets/error-BMXxpgfR.js +2 -0
  164. package/public/assets/{error-2VPY5w8V.js.map → error-BMXxpgfR.js.map} +1 -1
  165. package/public/assets/{field-DDt6F9TZ.js → field-Cn4H1CUj.js} +2 -2
  166. package/public/assets/{field-DDt6F9TZ.js.map → field-Cn4H1CUj.js.map} +1 -1
  167. package/public/assets/forgot-C5vUVgr_.js +2 -0
  168. package/public/assets/{forgot-UL_cNRnQ.js.map → forgot-C5vUVgr_.js.map} +1 -1
  169. package/public/assets/forgot-rO9sByfV.js +2 -0
  170. package/public/assets/{forgot-DzDcduho.js.map → forgot-rO9sByfV.js.map} +1 -1
  171. package/public/assets/index-CS23Ozt6.js +11 -0
  172. package/public/assets/index-CS23Ozt6.js.map +1 -0
  173. package/public/assets/{initial-avatar-CZzHAPm3.js → initial-avatar-CtuDUz_W.js} +2 -2
  174. package/public/assets/{initial-avatar-CZzHAPm3.js.map → initial-avatar-CtuDUz_W.js.map} +1 -1
  175. package/public/assets/{input-CNRpSR08.js → input-CzQki7k6.js} +2 -2
  176. package/public/assets/{input-CNRpSR08.js.map → input-CzQki7k6.js.map} +1 -1
  177. package/public/assets/{labeled-separator-fnKySKv2.js → labeled-separator-CV7nb0XW.js} +2 -2
  178. package/public/assets/{labeled-separator-fnKySKv2.js.map → labeled-separator-CV7nb0XW.js.map} +1 -1
  179. package/public/assets/login-C5vUVgr_.js +2 -0
  180. package/public/assets/login-C5vUVgr_.js.map +1 -0
  181. package/public/assets/login-nukebli4.js +2 -0
  182. package/public/assets/login-nukebli4.js.map +1 -0
  183. package/public/assets/{logout-CHdmF_16.js → logout-B5T52P8u.js} +2 -2
  184. package/public/assets/{logout-CHdmF_16.js.map → logout-B5T52P8u.js.map} +1 -1
  185. package/public/assets/{modal-sCM_zT82.js → modal-BDQFucjz.js} +2 -2
  186. package/public/assets/{modal-sCM_zT82.js.map → modal-BDQFucjz.js.map} +1 -1
  187. package/public/assets/{mutationOptions-CrswitPs.js → mutationOptions-DyYFUbyn.js} +2 -2
  188. package/public/assets/{mutationOptions-CrswitPs.js.map → mutationOptions-DyYFUbyn.js.map} +1 -1
  189. package/public/assets/passkey-BE9JvJOP.js +2 -0
  190. package/public/assets/{passkey-CU8x9J2w.js.map → passkey-BE9JvJOP.js.map} +1 -1
  191. package/public/assets/passkey-C5vUVgr_.js +2 -0
  192. package/public/assets/{passkey-UL_cNRnQ.js.map → passkey-C5vUVgr_.js.map} +1 -1
  193. package/public/assets/passkey-CynCz1Dt.js +2 -0
  194. package/public/assets/{passkey-CEx1VZ5R.js.map → passkey-CynCz1Dt.js.map} +1 -1
  195. package/public/assets/passkey-J1qnYp9A.js +2 -0
  196. package/public/assets/{passkey-B2Blw47e.js.map → passkey-J1qnYp9A.js.map} +1 -1
  197. package/public/assets/password-C5vUVgr_.js +2 -0
  198. package/public/assets/password-C5vUVgr_.js.map +1 -0
  199. package/public/assets/password-RkUttbhD.js +2 -0
  200. package/public/assets/password-RkUttbhD.js.map +1 -0
  201. package/public/assets/password-reset-sU5KkBKi.js +2 -0
  202. package/public/assets/{password-reset-CIacHnvA.js.map → password-reset-sU5KkBKi.js.map} +1 -1
  203. package/public/assets/{password-strength-C0Zkrrct.js → password-strength-BeSOu_Ga.js} +2 -2
  204. package/public/assets/{password-strength-C0Zkrrct.js.map → password-strength-BeSOu_Ga.js.map} +1 -1
  205. package/public/assets/{pin-input-BlD4tPZJ.js → pin-input-Cc-VkrQ3.js} +2 -2
  206. package/public/assets/{pin-input-BlD4tPZJ.js.map → pin-input-Cc-VkrQ3.js.map} +1 -1
  207. package/public/assets/{profile-D43Yq-6S.js → profile-B5NaJfeS.js} +2 -2
  208. package/public/assets/{profile-D43Yq-6S.js.map → profile-B5NaJfeS.js.map} +1 -1
  209. package/public/assets/{profile-D1cEjfs8.js → profile-BcjsVYOx.js} +2 -2
  210. package/public/assets/{profile-D1cEjfs8.js.map → profile-BcjsVYOx.js.map} +1 -1
  211. package/public/assets/recovery-PnUATiUi.js +2 -0
  212. package/public/assets/{recovery-CLExnfYX.js.map → recovery-PnUATiUi.js.map} +1 -1
  213. package/public/assets/register-C5vUVgr_.js +2 -0
  214. package/public/assets/{register-UL_cNRnQ.js.map → register-C5vUVgr_.js.map} +1 -1
  215. package/public/assets/register-DQXhru0_.js +2 -0
  216. package/public/assets/{register-CZUyWXRO.js.map → register-DQXhru0_.js.map} +1 -1
  217. package/public/assets/reset-C5vUVgr_.js +2 -0
  218. package/public/assets/{reset-UL_cNRnQ.js.map → reset-C5vUVgr_.js.map} +1 -1
  219. package/public/assets/reset-CQgIa3yf.js +2 -0
  220. package/public/assets/reset-CQgIa3yf.js.map +1 -0
  221. package/public/assets/select-C5vUVgr_.js +2 -0
  222. package/public/assets/{select-UL_cNRnQ.js.map → select-C5vUVgr_.js.map} +1 -1
  223. package/public/assets/{select-CWdSzsVo.js → select-DqZynXSt.js} +2 -2
  224. package/public/assets/{select-CWdSzsVo.js.map → select-DqZynXSt.js.map} +1 -1
  225. package/public/assets/{separator-BWGym8uM.js → separator-B0Bvh1he.js} +2 -2
  226. package/public/assets/{separator-BWGym8uM.js.map → separator-B0Bvh1he.js.map} +1 -1
  227. package/public/assets/shell-header-bar-CjG5qvPg.js +2 -0
  228. package/public/assets/{shell-header-bar-C9AB5lT-.js.map → shell-header-bar-CjG5qvPg.js.map} +1 -1
  229. package/public/assets/{terms-D43Yq-6S.js → terms-B5NaJfeS.js} +2 -2
  230. package/public/assets/{terms-D43Yq-6S.js.map → terms-B5NaJfeS.js.map} +1 -1
  231. package/public/assets/{terms-COVFmzyl.js → terms-BHDAgn-H.js} +2 -2
  232. package/public/assets/{terms-COVFmzyl.js.map → terms-BHDAgn-H.js.map} +1 -1
  233. package/public/assets/{terms-checkbox-list-Cw0zB-lO.js → terms-checkbox-list-6jteUTIZ.js} +2 -2
  234. package/public/assets/{terms-checkbox-list-Cw0zB-lO.js.map → terms-checkbox-list-6jteUTIZ.js.map} +1 -1
  235. package/public/assets/{totp-BfCm6IWl.js → totp-BNZ904Zm.js} +2 -2
  236. package/public/assets/{totp-BfCm6IWl.js.map → totp-BNZ904Zm.js.map} +1 -1
  237. package/public/assets/totp-CJtfVNEq.js +2 -0
  238. package/public/assets/{totp-BFJEXxk0.js.map → totp-CJtfVNEq.js.map} +1 -1
  239. package/public/assets/totp-ECvOUcFx.js +2 -0
  240. package/public/assets/{totp-MINhsVxT.js.map → totp-ECvOUcFx.js.map} +1 -1
  241. package/public/assets/use-totp-setup-BbS3XcZA.js +3 -0
  242. package/public/assets/{use-totp-setup-C7Zk_2IY.js.map → use-totp-setup-BbS3XcZA.js.map} +1 -1
  243. package/public/assets/{useAriaLabelledBy-CiOCg6X5.js → useAriaLabelledBy-ByuMi3Ry.js} +2 -2
  244. package/public/assets/{useAriaLabelledBy-CiOCg6X5.js.map → useAriaLabelledBy-ByuMi3Ry.js.map} +1 -1
  245. package/public/assets/{useMutation-ZLFeJg75.js → useMutation-BWZ0G4Td.js} +2 -2
  246. package/public/assets/{useMutation-ZLFeJg75.js.map → useMutation-BWZ0G4Td.js.map} +1 -1
  247. package/public/assets/useQuery-Ckxjmvtl.js +2 -0
  248. package/public/assets/{useQuery-BvMz34BW.js.map → useQuery-Ckxjmvtl.js.map} +1 -1
  249. package/public/assets/{users-COM7u3A-.js → users-CmFxrCQT.js} +2 -2
  250. package/public/assets/{users-COM7u3A-.js.map → users-CmFxrCQT.js.map} +1 -1
  251. package/public/index.html +3 -4
  252. package/dist/routes/api/auth/password/reset-required/post.d.ts +0 -18
  253. package/dist/routes/api/auth/password/reset-required/post.d.ts.map +0 -1
  254. package/dist/routes/api/auth/password/reset-required/post.js +0 -39
  255. package/dist/routes/api/auth/password/reset-required/post.js.map +0 -1
  256. package/dist/services/legacy-credential-retirement.service.d.ts +0 -20
  257. package/dist/services/legacy-credential-retirement.service.d.ts.map +0 -1
  258. package/dist/services/legacy-credential-retirement.service.js +0 -90
  259. package/dist/services/legacy-credential-retirement.service.js.map +0 -1
  260. package/public/assets/2fa-BgveHSB_.js +0 -2
  261. package/public/assets/2fa-C37MUkye.js +0 -2
  262. package/public/assets/2fa-UL_cNRnQ.js +0 -2
  263. package/public/assets/2fa-kiqzGSz2.js +0 -2
  264. package/public/assets/admin-DbDuu6AI.js +0 -2
  265. package/public/assets/admin-users-CwTJdiih.js +0 -2
  266. package/public/assets/config-v3-oy_ef.js +0 -2
  267. package/public/assets/config-v3-oy_ef.js.map +0 -1
  268. package/public/assets/email-UL_cNRnQ.js +0 -2
  269. package/public/assets/error-2VPY5w8V.js +0 -2
  270. package/public/assets/forgot-DzDcduho.js +0 -2
  271. package/public/assets/forgot-UL_cNRnQ.js +0 -2
  272. package/public/assets/index-gliHKncz.js +0 -11
  273. package/public/assets/index-gliHKncz.js.map +0 -1
  274. package/public/assets/link-BN90emXr.js +0 -2
  275. package/public/assets/link-BN90emXr.js.map +0 -1
  276. package/public/assets/lock-keyhole-Cz72xkCn.js +0 -2
  277. package/public/assets/lock-keyhole-Cz72xkCn.js.map +0 -1
  278. package/public/assets/login-C6m2qSUt.js +0 -2
  279. package/public/assets/login-C6m2qSUt.js.map +0 -1
  280. package/public/assets/login-UL_cNRnQ.js +0 -2
  281. package/public/assets/login-UL_cNRnQ.js.map +0 -1
  282. package/public/assets/passkey-B2Blw47e.js +0 -2
  283. package/public/assets/passkey-CEx1VZ5R.js +0 -2
  284. package/public/assets/passkey-CU8x9J2w.js +0 -2
  285. package/public/assets/passkey-UL_cNRnQ.js +0 -2
  286. package/public/assets/password-BhU_a0cR.js +0 -2
  287. package/public/assets/password-BhU_a0cR.js.map +0 -1
  288. package/public/assets/password-UL_cNRnQ.js +0 -2
  289. package/public/assets/password-UL_cNRnQ.js.map +0 -1
  290. package/public/assets/password-reset-CIacHnvA.js +0 -2
  291. package/public/assets/recovery-CLExnfYX.js +0 -2
  292. package/public/assets/register-CZUyWXRO.js +0 -2
  293. package/public/assets/register-UL_cNRnQ.js +0 -2
  294. package/public/assets/reset-N3RQTYEB.js +0 -2
  295. package/public/assets/reset-N3RQTYEB.js.map +0 -1
  296. package/public/assets/reset-UL_cNRnQ.js +0 -2
  297. package/public/assets/reset-required-Cv5edDS6.js +0 -2
  298. package/public/assets/reset-required-Cv5edDS6.js.map +0 -1
  299. package/public/assets/reset-required-UL_cNRnQ.js +0 -2
  300. package/public/assets/reset-required-UL_cNRnQ.js.map +0 -1
  301. package/public/assets/select-UL_cNRnQ.js +0 -2
  302. package/public/assets/shell-header-bar-C9AB5lT-.js +0 -2
  303. package/public/assets/totp-BFJEXxk0.js +0 -2
  304. package/public/assets/totp-MINhsVxT.js +0 -2
  305. package/public/assets/use-totp-setup-C7Zk_2IY.js +0 -3
  306. package/public/assets/useQuery-BvMz34BW.js +0 -2
@@ -1,2 +1,2 @@
1
- import{i as e,n as t,q as n,s as r}from"./createLucideIcon-BPpAIXR_.js";import{t as i}from"./useMutation-ZLFeJg75.js";import{N as a,tt as o}from"./shell-header-bar-C9AB5lT-.js";import{t as s}from"./shield-check-D_p8H0ni.js";import{B as c,_ as l,v as u,y as d}from"./index-gliHKncz.js";import{t as f}from"./auth-page-header-CIs08f5Y.js";import{t as p}from"./initial-avatar-CZzHAPm3.js";import{t as m}from"./auth-layout-CoeqNhTT.js";var h=n();function g(){let{t:n}=e(),g=l.useSearch(),_=o(d({client_id:g.client_id,scope:g.scope})),v=i({...u,onSuccess:e=>{window.location.href=e.redirect_url}}),y=()=>{v.mutate({client_id:g.client_id,redirect_uri:g.redirect_uri,response_type:g.response_type,scope:g.scope,state:g.state,nonce:g.nonce,code_challenge:g.code_challenge,code_challenge_method:g.code_challenge_method,prompt:g.prompt,max_age:g.max_age,reauthenticated:g.reauthenticated,display:g.display,response_mode:g.response_mode,login_hint:g.login_hint,ui_locales:g.ui_locales,id_token_hint:g.id_token_hint,acr_values:g.acr_values,account_selected:g.account_selected,account_selection_state:g.account_selection_state,decision:`allow`})},b=()=>{v.mutate({client_id:g.client_id,redirect_uri:g.redirect_uri,response_type:g.response_type,scope:g.scope,state:g.state,nonce:g.nonce,code_challenge:g.code_challenge,code_challenge_method:g.code_challenge_method,prompt:g.prompt,max_age:g.max_age,reauthenticated:g.reauthenticated,display:g.display,response_mode:g.response_mode,login_hint:g.login_hint,ui_locales:g.ui_locales,id_token_hint:g.id_token_hint,acr_values:g.acr_values,account_selected:g.account_selected,account_selection_state:g.account_selection_state,decision:`deny`})},{client:x,scopes:S,user:C}=_.data;return(0,h.jsxs)(m,{width:`wide`,children:[(0,h.jsx)(f,{subtitle:n(`consent.subtitle`,{app:x.name}),title:n(`consent.title`)}),(0,h.jsx)(c.Root,{padding:`lg`,variant:`outlined`,children:(0,h.jsxs)(c.Content,{className:`flex items-center gap-tinyrack-md`,children:[(0,h.jsx)(p,{email:C.email,size:`sm`}),(0,h.jsxs)(`span`,{className:`flex min-w-0 flex-col`,children:[(0,h.jsx)(t,{color:`muted`,variant:`caption`,children:n(`consent.loggedInAs`)}),(0,h.jsx)(t,{"data-testid":`consent-user-email`,truncate:!0,variant:`body`,weight:`medium`,children:C.email})]})]})}),(0,h.jsxs)(`div`,{className:`flex flex-col gap-tinyrack-sm`,children:[(0,h.jsx)(t,{as:`h3`,variant:`label`,children:n(`consent.permissions.title`)}),(0,h.jsx)(`ul`,{className:`flex flex-col gap-tinyrack-xs`,"data-testid":`consent-scope-list`,children:S.map(e=>(0,h.jsxs)(`li`,{className:`flex items-center gap-tinyrack-sm rounded-tinyrack-md bg-tinyrack-surface-muted px-tinyrack-md py-tinyrack-sm`,children:[(0,h.jsx)(s,{"aria-hidden":!0,className:`size-tinyrack-lg shrink-0 text-tinyrack-info`}),(0,h.jsx)(t,{variant:`body`,children:n(`consent.scope.${e.name}`,{defaultValue:e.description})})]},e.name))})]}),(0,h.jsxs)(`div`,{className:`sticky bottom-0 flex gap-tinyrack-sm border-tinyrack-border border-t-tinyrack-default bg-tinyrack-surface/80 py-tinyrack-md backdrop-blur-sm`,children:[(0,h.jsxs)(r,{appearance:`outline`,className:`flex-1`,"data-testid":`consent-deny`,intent:`neutral`,loading:v.isPending,loadingLabel:n(`consent.deny`),onClick:b,type:`button`,uiSize:`lg`,children:[(0,h.jsx)(a,{"aria-hidden":!0,className:`size-tinyrack-lg`}),n(`consent.deny`)]}),(0,h.jsxs)(r,{className:`flex-1`,"data-testid":`consent-allow`,intent:`primary`,loading:v.isPending,loadingLabel:n(`consent.allow`),onClick:y,type:`button`,uiSize:`lg`,children:[(0,h.jsx)(s,{"aria-hidden":!0,className:`size-tinyrack-lg`}),n(`consent.allow`)]})]})]})}export{g as component};
2
- //# sourceMappingURL=consent-BIhxtXHx.js.map
1
+ import{i as e,n as t,q as n,s as r}from"./createLucideIcon-BPpAIXR_.js";import{t as i}from"./useMutation-BWZ0G4Td.js";import{N as a,tt as o}from"./shell-header-bar-CjG5qvPg.js";import{t as s}from"./shield-check-D_p8H0ni.js";import{B as c,_ as l,v as u,y as d}from"./index-CS23Ozt6.js";import{t as f}from"./auth-page-header-CIn2E4zI.js";import{t as p}from"./initial-avatar-CtuDUz_W.js";import{t as m}from"./auth-layout-VtpryDof.js";var h=n();function g(){let{t:n}=e(),g=l.useSearch(),_=o(d({client_id:g.client_id,scope:g.scope})),v=i({...u,onSuccess:e=>{window.location.href=e.redirect_url}}),y=()=>{v.mutate({client_id:g.client_id,redirect_uri:g.redirect_uri,response_type:g.response_type,scope:g.scope,state:g.state,nonce:g.nonce,code_challenge:g.code_challenge,code_challenge_method:g.code_challenge_method,prompt:g.prompt,max_age:g.max_age,reauthenticated:g.reauthenticated,display:g.display,response_mode:g.response_mode,login_hint:g.login_hint,ui_locales:g.ui_locales,id_token_hint:g.id_token_hint,acr_values:g.acr_values,account_selected:g.account_selected,account_selection_state:g.account_selection_state,decision:`allow`})},b=()=>{v.mutate({client_id:g.client_id,redirect_uri:g.redirect_uri,response_type:g.response_type,scope:g.scope,state:g.state,nonce:g.nonce,code_challenge:g.code_challenge,code_challenge_method:g.code_challenge_method,prompt:g.prompt,max_age:g.max_age,reauthenticated:g.reauthenticated,display:g.display,response_mode:g.response_mode,login_hint:g.login_hint,ui_locales:g.ui_locales,id_token_hint:g.id_token_hint,acr_values:g.acr_values,account_selected:g.account_selected,account_selection_state:g.account_selection_state,decision:`deny`})},{client:x,scopes:S,user:C}=_.data;return(0,h.jsxs)(m,{width:`wide`,children:[(0,h.jsx)(f,{subtitle:n(`consent.subtitle`,{app:x.name}),title:n(`consent.title`)}),(0,h.jsx)(c.Root,{padding:`lg`,variant:`outlined`,children:(0,h.jsxs)(c.Content,{className:`flex items-center gap-tinyrack-md`,children:[(0,h.jsx)(p,{email:C.email,size:`sm`}),(0,h.jsxs)(`span`,{className:`flex min-w-0 flex-col`,children:[(0,h.jsx)(t,{color:`muted`,variant:`caption`,children:n(`consent.loggedInAs`)}),(0,h.jsx)(t,{"data-testid":`consent-user-email`,truncate:!0,variant:`body`,weight:`medium`,children:C.email})]})]})}),(0,h.jsxs)(`div`,{className:`flex flex-col gap-tinyrack-sm`,children:[(0,h.jsx)(t,{as:`h3`,variant:`label`,children:n(`consent.permissions.title`)}),(0,h.jsx)(`ul`,{className:`flex flex-col gap-tinyrack-xs`,"data-testid":`consent-scope-list`,children:S.map(e=>(0,h.jsxs)(`li`,{className:`flex items-center gap-tinyrack-sm rounded-tinyrack-md bg-tinyrack-surface-muted px-tinyrack-md py-tinyrack-sm`,children:[(0,h.jsx)(s,{"aria-hidden":!0,className:`size-tinyrack-lg shrink-0 text-tinyrack-info`}),(0,h.jsx)(t,{variant:`body`,children:n(`consent.scope.${e.name}`,{defaultValue:e.description})})]},e.name))})]}),(0,h.jsxs)(`div`,{className:`sticky bottom-0 flex gap-tinyrack-sm border-tinyrack-border border-t-tinyrack-default bg-tinyrack-surface/80 py-tinyrack-md backdrop-blur-sm`,children:[(0,h.jsxs)(r,{appearance:`outline`,className:`flex-1`,"data-testid":`consent-deny`,intent:`neutral`,loading:v.isPending,loadingLabel:n(`consent.deny`),onClick:b,type:`button`,uiSize:`lg`,children:[(0,h.jsx)(a,{"aria-hidden":!0,className:`size-tinyrack-lg`}),n(`consent.deny`)]}),(0,h.jsxs)(r,{className:`flex-1`,"data-testid":`consent-allow`,intent:`primary`,loading:v.isPending,loadingLabel:n(`consent.allow`),onClick:y,type:`button`,uiSize:`lg`,children:[(0,h.jsx)(s,{"aria-hidden":!0,className:`size-tinyrack-lg`}),n(`consent.allow`)]})]})]})}export{g as component};
2
+ //# sourceMappingURL=consent-BqqFDdsr.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"consent-BIhxtXHx.js","names":["useMutation","useSuspenseQuery","TRButton","TRCard","TRText","ShieldCheckIcon","XIcon","useTranslation","AuthPageHeader","InitialAvatar","AuthLayout","consentDecisionMutationOptions","getConsentInfoQueryOptions","Route","Consent","t","search","useSearch","consentInfoQuery","client_id","scope","consentMutation","onSuccess","data","window","location","href","redirect_url","handleAllow","mutate","redirect_uri","response_type","state","nonce","code_challenge","code_challenge_method","prompt","max_age","reauthenticated","display","response_mode","login_hint","ui_locales","id_token_hint","acr_values","account_selected","account_selection_state","decision","handleDeny","client","scopes","user","app","name","email","map","description","defaultValue","isPending","component"],"sources":["../../../frontend/src/routes/consent/index.tsx?tsr-split=component"],"sourcesContent":["import { useMutation, useSuspenseQuery } from '@tanstack/react-query';\nimport {\n createFileRoute,\n type ErrorComponentProps,\n} from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRCard } from '@tinyrack/ui/components/card';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { ShieldCheckIcon, XIcon } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { InitialAvatar } from '#frontend/components/ui/initial-avatar.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport {\n buildAuthorizeUrl,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport {\n consentDecisionMutationOptions,\n getConsentInfoQueryOptions,\n} from '#frontend/queries/consent.ts';\n\nconst ConsentSearchSchema = OAuthSearchSchema.extend({\n client_id: z.string(),\n redirect_uri: z.string(),\n response_type: z.string(),\n});\n\nexport const Route = createFileRoute('/consent/')({\n component: Consent,\n errorComponent: ConsentError,\n validateSearch: ConsentSearchSchema,\n loaderDeps: ({ search }) => ({\n client_id: search.client_id,\n scope: search.scope,\n }),\n loader: async ({ context, deps }) => {\n await context.queryClient.ensureQueryData(\n getConsentInfoQueryOptions({\n client_id: deps.client_id,\n scope: deps.scope,\n }),\n );\n },\n});\n\nfunction ConsentError(props: ErrorComponentProps) {\n return (\n <RouteErrorFallback\n {...props}\n onUnauthorized={() => {\n // Re-enter the OAuth flow from the beginning so the\n // user can log in and be redirected back through\n // /oauth/authorize -> /consent/.\n const params = new URLSearchParams(window.location.search);\n const search = Object.fromEntries(params.entries());\n window.location.href = buildAuthorizeUrl(search);\n }}\n />\n );\n}\n\nfunction Consent() {\n const { t } = useTranslation();\n const search = Route.useSearch();\n\n const consentInfoQuery = useSuspenseQuery(\n getConsentInfoQueryOptions({\n client_id: search.client_id,\n scope: search.scope,\n }),\n );\n\n const consentMutation = useMutation({\n ...consentDecisionMutationOptions,\n onSuccess: (data) => {\n window.location.href = data.redirect_url;\n },\n });\n\n const handleAllow = () => {\n consentMutation.mutate({\n client_id: search.client_id,\n redirect_uri: search.redirect_uri,\n response_type: search.response_type,\n scope: search.scope,\n state: search.state,\n nonce: search.nonce,\n code_challenge: search.code_challenge,\n code_challenge_method: search.code_challenge_method,\n prompt: search.prompt,\n max_age: search.max_age,\n reauthenticated: search.reauthenticated,\n display: search.display,\n response_mode: search.response_mode,\n login_hint: search.login_hint,\n ui_locales: search.ui_locales,\n id_token_hint: search.id_token_hint,\n acr_values: search.acr_values,\n account_selected: search.account_selected,\n account_selection_state: search.account_selection_state,\n decision: 'allow',\n });\n };\n\n const handleDeny = () => {\n consentMutation.mutate({\n client_id: search.client_id,\n redirect_uri: search.redirect_uri,\n response_type: search.response_type,\n scope: search.scope,\n state: search.state,\n nonce: search.nonce,\n code_challenge: search.code_challenge,\n code_challenge_method: search.code_challenge_method,\n prompt: search.prompt,\n max_age: search.max_age,\n reauthenticated: search.reauthenticated,\n display: search.display,\n response_mode: search.response_mode,\n login_hint: search.login_hint,\n ui_locales: search.ui_locales,\n id_token_hint: search.id_token_hint,\n acr_values: search.acr_values,\n account_selected: search.account_selected,\n account_selection_state: search.account_selection_state,\n decision: 'deny',\n });\n };\n\n const { client, scopes, user } = consentInfoQuery.data;\n\n return (\n <AuthLayout width=\"wide\">\n <AuthPageHeader\n subtitle={t('consent.subtitle', { app: client.name })}\n title={t('consent.title')}\n />\n\n {/*\n Who is about to hand out access. The decision is meaningless without\n it, so it sits above the scopes rather than in fine print.\n */}\n <TRCard.Root padding=\"lg\" variant=\"outlined\">\n <TRCard.Content className=\"flex items-center gap-tinyrack-md\">\n <InitialAvatar email={user.email} size=\"sm\" />\n <span className=\"flex min-w-0 flex-col\">\n <TRText color=\"muted\" variant=\"caption\">\n {t('consent.loggedInAs')}\n </TRText>\n <TRText\n data-testid=\"consent-user-email\"\n truncate\n variant=\"body\"\n weight=\"medium\"\n >\n {user.email}\n </TRText>\n </span>\n </TRCard.Content>\n </TRCard.Root>\n\n {/* Requested permissions */}\n <div className=\"flex flex-col gap-tinyrack-sm\">\n <TRText as=\"h3\" variant=\"label\">\n {t('consent.permissions.title')}\n </TRText>\n <ul\n className=\"flex flex-col gap-tinyrack-xs\"\n data-testid=\"consent-scope-list\"\n >\n {scopes.map((scope: { name: string; description: string }) => (\n <li\n className=\"flex items-center gap-tinyrack-sm rounded-tinyrack-md bg-tinyrack-surface-muted px-tinyrack-md py-tinyrack-sm\"\n key={scope.name}\n >\n <ShieldCheckIcon\n aria-hidden\n className=\"size-tinyrack-lg shrink-0 text-tinyrack-info\"\n />\n <TRText variant=\"body\">\n {t(`consent.scope.${scope.name}`, {\n defaultValue: scope.description,\n })}\n </TRText>\n </li>\n ))}\n </ul>\n </div>\n\n {/*\n Sticky so the decision stays reachable when a client asks for a long\n list of scopes and the page scrolls.\n */}\n <div className=\"sticky bottom-0 flex gap-tinyrack-sm border-tinyrack-border border-t-tinyrack-default bg-tinyrack-surface/80 py-tinyrack-md backdrop-blur-sm\">\n <TRButton\n appearance=\"outline\"\n className=\"flex-1\"\n data-testid=\"consent-deny\"\n intent=\"neutral\"\n loading={consentMutation.isPending}\n loadingLabel={t('consent.deny')}\n onClick={handleDeny}\n type=\"button\"\n uiSize=\"lg\"\n >\n <XIcon aria-hidden className=\"size-tinyrack-lg\" />\n {t('consent.deny')}\n </TRButton>\n <TRButton\n className=\"flex-1\"\n data-testid=\"consent-allow\"\n intent=\"primary\"\n loading={consentMutation.isPending}\n loadingLabel={t('consent.allow')}\n onClick={handleAllow}\n type=\"button\"\n uiSize=\"lg\"\n >\n <ShieldCheckIcon aria-hidden className=\"size-tinyrack-lg\" />\n {t('consent.allow')}\n </TRButton>\n </div>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"consent-BqqFDdsr.js","names":["useMutation","useSuspenseQuery","TRButton","TRCard","TRText","ShieldCheckIcon","XIcon","useTranslation","AuthPageHeader","InitialAvatar","AuthLayout","consentDecisionMutationOptions","getConsentInfoQueryOptions","Route","Consent","t","search","useSearch","consentInfoQuery","client_id","scope","consentMutation","onSuccess","data","window","location","href","redirect_url","handleAllow","mutate","redirect_uri","response_type","state","nonce","code_challenge","code_challenge_method","prompt","max_age","reauthenticated","display","response_mode","login_hint","ui_locales","id_token_hint","acr_values","account_selected","account_selection_state","decision","handleDeny","client","scopes","user","app","name","email","map","description","defaultValue","isPending","component"],"sources":["../../../frontend/src/routes/consent/index.tsx?tsr-split=component"],"sourcesContent":["import { useMutation, useSuspenseQuery } from '@tanstack/react-query';\nimport {\n createFileRoute,\n type ErrorComponentProps,\n} from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRCard } from '@tinyrack/ui/components/card';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { ShieldCheckIcon, XIcon } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { InitialAvatar } from '#frontend/components/ui/initial-avatar.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport {\n buildAuthorizeUrl,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport {\n consentDecisionMutationOptions,\n getConsentInfoQueryOptions,\n} from '#frontend/queries/consent.ts';\n\nconst ConsentSearchSchema = OAuthSearchSchema.extend({\n client_id: z.string(),\n redirect_uri: z.string(),\n response_type: z.string(),\n});\n\nexport const Route = createFileRoute('/consent/')({\n component: Consent,\n errorComponent: ConsentError,\n validateSearch: ConsentSearchSchema,\n loaderDeps: ({ search }) => ({\n client_id: search.client_id,\n scope: search.scope,\n }),\n loader: async ({ context, deps }) => {\n await context.queryClient.ensureQueryData(\n getConsentInfoQueryOptions({\n client_id: deps.client_id,\n scope: deps.scope,\n }),\n );\n },\n});\n\nfunction ConsentError(props: ErrorComponentProps) {\n return (\n <RouteErrorFallback\n {...props}\n onUnauthorized={() => {\n // Re-enter the OAuth flow from the beginning so the\n // user can log in and be redirected back through\n // /oauth/authorize -> /consent/.\n const params = new URLSearchParams(window.location.search);\n const search = Object.fromEntries(params.entries());\n window.location.href = buildAuthorizeUrl(search);\n }}\n />\n );\n}\n\nfunction Consent() {\n const { t } = useTranslation();\n const search = Route.useSearch();\n\n const consentInfoQuery = useSuspenseQuery(\n getConsentInfoQueryOptions({\n client_id: search.client_id,\n scope: search.scope,\n }),\n );\n\n const consentMutation = useMutation({\n ...consentDecisionMutationOptions,\n onSuccess: (data) => {\n window.location.href = data.redirect_url;\n },\n });\n\n const handleAllow = () => {\n consentMutation.mutate({\n client_id: search.client_id,\n redirect_uri: search.redirect_uri,\n response_type: search.response_type,\n scope: search.scope,\n state: search.state,\n nonce: search.nonce,\n code_challenge: search.code_challenge,\n code_challenge_method: search.code_challenge_method,\n prompt: search.prompt,\n max_age: search.max_age,\n reauthenticated: search.reauthenticated,\n display: search.display,\n response_mode: search.response_mode,\n login_hint: search.login_hint,\n ui_locales: search.ui_locales,\n id_token_hint: search.id_token_hint,\n acr_values: search.acr_values,\n account_selected: search.account_selected,\n account_selection_state: search.account_selection_state,\n decision: 'allow',\n });\n };\n\n const handleDeny = () => {\n consentMutation.mutate({\n client_id: search.client_id,\n redirect_uri: search.redirect_uri,\n response_type: search.response_type,\n scope: search.scope,\n state: search.state,\n nonce: search.nonce,\n code_challenge: search.code_challenge,\n code_challenge_method: search.code_challenge_method,\n prompt: search.prompt,\n max_age: search.max_age,\n reauthenticated: search.reauthenticated,\n display: search.display,\n response_mode: search.response_mode,\n login_hint: search.login_hint,\n ui_locales: search.ui_locales,\n id_token_hint: search.id_token_hint,\n acr_values: search.acr_values,\n account_selected: search.account_selected,\n account_selection_state: search.account_selection_state,\n decision: 'deny',\n });\n };\n\n const { client, scopes, user } = consentInfoQuery.data;\n\n return (\n <AuthLayout width=\"wide\">\n <AuthPageHeader\n subtitle={t('consent.subtitle', { app: client.name })}\n title={t('consent.title')}\n />\n\n {/*\n Who is about to hand out access. The decision is meaningless without\n it, so it sits above the scopes rather than in fine print.\n */}\n <TRCard.Root padding=\"lg\" variant=\"outlined\">\n <TRCard.Content className=\"flex items-center gap-tinyrack-md\">\n <InitialAvatar email={user.email} size=\"sm\" />\n <span className=\"flex min-w-0 flex-col\">\n <TRText color=\"muted\" variant=\"caption\">\n {t('consent.loggedInAs')}\n </TRText>\n <TRText\n data-testid=\"consent-user-email\"\n truncate\n variant=\"body\"\n weight=\"medium\"\n >\n {user.email}\n </TRText>\n </span>\n </TRCard.Content>\n </TRCard.Root>\n\n {/* Requested permissions */}\n <div className=\"flex flex-col gap-tinyrack-sm\">\n <TRText as=\"h3\" variant=\"label\">\n {t('consent.permissions.title')}\n </TRText>\n <ul\n className=\"flex flex-col gap-tinyrack-xs\"\n data-testid=\"consent-scope-list\"\n >\n {scopes.map((scope: { name: string; description: string }) => (\n <li\n className=\"flex items-center gap-tinyrack-sm rounded-tinyrack-md bg-tinyrack-surface-muted px-tinyrack-md py-tinyrack-sm\"\n key={scope.name}\n >\n <ShieldCheckIcon\n aria-hidden\n className=\"size-tinyrack-lg shrink-0 text-tinyrack-info\"\n />\n <TRText variant=\"body\">\n {t(`consent.scope.${scope.name}`, {\n defaultValue: scope.description,\n })}\n </TRText>\n </li>\n ))}\n </ul>\n </div>\n\n {/*\n Sticky so the decision stays reachable when a client asks for a long\n list of scopes and the page scrolls.\n */}\n <div className=\"sticky bottom-0 flex gap-tinyrack-sm border-tinyrack-border border-t-tinyrack-default bg-tinyrack-surface/80 py-tinyrack-md backdrop-blur-sm\">\n <TRButton\n appearance=\"outline\"\n className=\"flex-1\"\n data-testid=\"consent-deny\"\n intent=\"neutral\"\n loading={consentMutation.isPending}\n loadingLabel={t('consent.deny')}\n onClick={handleDeny}\n type=\"button\"\n uiSize=\"lg\"\n >\n <XIcon aria-hidden className=\"size-tinyrack-lg\" />\n {t('consent.deny')}\n </TRButton>\n <TRButton\n className=\"flex-1\"\n data-testid=\"consent-allow\"\n intent=\"primary\"\n loading={consentMutation.isPending}\n loadingLabel={t('consent.allow')}\n onClick={handleAllow}\n type=\"button\"\n uiSize=\"lg\"\n >\n <ShieldCheckIcon aria-hidden className=\"size-tinyrack-lg\" />\n {t('consent.allow')}\n </TRButton>\n </div>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{q as e}from"./createLucideIcon-BPpAIXR_.js";import{I as t,M as n}from"./index-gliHKncz.js";var r=e();function i(e){return(0,r.jsx)(t,{...e,onUnauthorized:()=>{let e=new URLSearchParams(window.location.search),t=Object.fromEntries(e.entries());window.location.href=n(t)}})}export{i as errorComponent};
2
- //# sourceMappingURL=consent-DRmENn26.js.map
1
+ import{q as e}from"./createLucideIcon-BPpAIXR_.js";import{I as t,M as n}from"./index-CS23Ozt6.js";var r=e();function i(e){return(0,r.jsx)(t,{...e,onUnauthorized:()=>{let e=new URLSearchParams(window.location.search),t=Object.fromEntries(e.entries());window.location.href=n(t)}})}export{i as errorComponent};
2
+ //# sourceMappingURL=consent-CfRbz2R7.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"consent-DRmENn26.js","names":["RouteErrorFallback","buildAuthorizeUrl","ConsentError","props","ErrorComponentProps","params","URLSearchParams","window","location","search","Object","fromEntries","entries","href","errorComponent"],"sources":["../../../frontend/src/routes/consent/index.tsx?tsr-split=errorComponent"],"sourcesContent":["import { useMutation, useSuspenseQuery } from '@tanstack/react-query';\nimport {\n createFileRoute,\n type ErrorComponentProps,\n} from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRCard } from '@tinyrack/ui/components/card';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { ShieldCheckIcon, XIcon } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { InitialAvatar } from '#frontend/components/ui/initial-avatar.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport {\n buildAuthorizeUrl,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport {\n consentDecisionMutationOptions,\n getConsentInfoQueryOptions,\n} from '#frontend/queries/consent.ts';\n\nconst ConsentSearchSchema = OAuthSearchSchema.extend({\n client_id: z.string(),\n redirect_uri: z.string(),\n response_type: z.string(),\n});\n\nexport const Route = createFileRoute('/consent/')({\n component: Consent,\n errorComponent: ConsentError,\n validateSearch: ConsentSearchSchema,\n loaderDeps: ({ search }) => ({\n client_id: search.client_id,\n scope: search.scope,\n }),\n loader: async ({ context, deps }) => {\n await context.queryClient.ensureQueryData(\n getConsentInfoQueryOptions({\n client_id: deps.client_id,\n scope: deps.scope,\n }),\n );\n },\n});\n\nfunction ConsentError(props: ErrorComponentProps) {\n return (\n <RouteErrorFallback\n {...props}\n onUnauthorized={() => {\n // Re-enter the OAuth flow from the beginning so the\n // user can log in and be redirected back through\n // /oauth/authorize -> /consent/.\n const params = new URLSearchParams(window.location.search);\n const search = Object.fromEntries(params.entries());\n window.location.href = buildAuthorizeUrl(search);\n }}\n />\n );\n}\n\nfunction Consent() {\n const { t } = useTranslation();\n const search = Route.useSearch();\n\n const consentInfoQuery = useSuspenseQuery(\n getConsentInfoQueryOptions({\n client_id: search.client_id,\n scope: search.scope,\n }),\n );\n\n const consentMutation = useMutation({\n ...consentDecisionMutationOptions,\n onSuccess: (data) => {\n window.location.href = data.redirect_url;\n },\n });\n\n const handleAllow = () => {\n consentMutation.mutate({\n client_id: search.client_id,\n redirect_uri: search.redirect_uri,\n response_type: search.response_type,\n scope: search.scope,\n state: search.state,\n nonce: search.nonce,\n code_challenge: search.code_challenge,\n code_challenge_method: search.code_challenge_method,\n prompt: search.prompt,\n max_age: search.max_age,\n reauthenticated: search.reauthenticated,\n display: search.display,\n response_mode: search.response_mode,\n login_hint: search.login_hint,\n ui_locales: search.ui_locales,\n id_token_hint: search.id_token_hint,\n acr_values: search.acr_values,\n account_selected: search.account_selected,\n account_selection_state: search.account_selection_state,\n decision: 'allow',\n });\n };\n\n const handleDeny = () => {\n consentMutation.mutate({\n client_id: search.client_id,\n redirect_uri: search.redirect_uri,\n response_type: search.response_type,\n scope: search.scope,\n state: search.state,\n nonce: search.nonce,\n code_challenge: search.code_challenge,\n code_challenge_method: search.code_challenge_method,\n prompt: search.prompt,\n max_age: search.max_age,\n reauthenticated: search.reauthenticated,\n display: search.display,\n response_mode: search.response_mode,\n login_hint: search.login_hint,\n ui_locales: search.ui_locales,\n id_token_hint: search.id_token_hint,\n acr_values: search.acr_values,\n account_selected: search.account_selected,\n account_selection_state: search.account_selection_state,\n decision: 'deny',\n });\n };\n\n const { client, scopes, user } = consentInfoQuery.data;\n\n return (\n <AuthLayout width=\"wide\">\n <AuthPageHeader\n subtitle={t('consent.subtitle', { app: client.name })}\n title={t('consent.title')}\n />\n\n {/*\n Who is about to hand out access. The decision is meaningless without\n it, so it sits above the scopes rather than in fine print.\n */}\n <TRCard.Root padding=\"lg\" variant=\"outlined\">\n <TRCard.Content className=\"flex items-center gap-tinyrack-md\">\n <InitialAvatar email={user.email} size=\"sm\" />\n <span className=\"flex min-w-0 flex-col\">\n <TRText color=\"muted\" variant=\"caption\">\n {t('consent.loggedInAs')}\n </TRText>\n <TRText\n data-testid=\"consent-user-email\"\n truncate\n variant=\"body\"\n weight=\"medium\"\n >\n {user.email}\n </TRText>\n </span>\n </TRCard.Content>\n </TRCard.Root>\n\n {/* Requested permissions */}\n <div className=\"flex flex-col gap-tinyrack-sm\">\n <TRText as=\"h3\" variant=\"label\">\n {t('consent.permissions.title')}\n </TRText>\n <ul\n className=\"flex flex-col gap-tinyrack-xs\"\n data-testid=\"consent-scope-list\"\n >\n {scopes.map((scope: { name: string; description: string }) => (\n <li\n className=\"flex items-center gap-tinyrack-sm rounded-tinyrack-md bg-tinyrack-surface-muted px-tinyrack-md py-tinyrack-sm\"\n key={scope.name}\n >\n <ShieldCheckIcon\n aria-hidden\n className=\"size-tinyrack-lg shrink-0 text-tinyrack-info\"\n />\n <TRText variant=\"body\">\n {t(`consent.scope.${scope.name}`, {\n defaultValue: scope.description,\n })}\n </TRText>\n </li>\n ))}\n </ul>\n </div>\n\n {/*\n Sticky so the decision stays reachable when a client asks for a long\n list of scopes and the page scrolls.\n */}\n <div className=\"sticky bottom-0 flex gap-tinyrack-sm border-tinyrack-border border-t-tinyrack-default bg-tinyrack-surface/80 py-tinyrack-md backdrop-blur-sm\">\n <TRButton\n appearance=\"outline\"\n className=\"flex-1\"\n data-testid=\"consent-deny\"\n intent=\"neutral\"\n loading={consentMutation.isPending}\n loadingLabel={t('consent.deny')}\n onClick={handleDeny}\n type=\"button\"\n uiSize=\"lg\"\n >\n <XIcon aria-hidden className=\"size-tinyrack-lg\" />\n {t('consent.deny')}\n </TRButton>\n <TRButton\n className=\"flex-1\"\n data-testid=\"consent-allow\"\n intent=\"primary\"\n loading={consentMutation.isPending}\n loadingLabel={t('consent.allow')}\n onClick={handleAllow}\n type=\"button\"\n uiSize=\"lg\"\n >\n <ShieldCheckIcon aria-hidden className=\"size-tinyrack-lg\" />\n {t('consent.allow')}\n </TRButton>\n </div>\n </AuthLayout>\n );\n}\n"],"mappings":"4GAgDA,SAASE,EAAaC,EAA4B,CAChD,OACE,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,GAAIA,EACJ,mBAAsB,CAIpB,IAAME,EAAS,IAAIC,gBAAgBC,OAAOC,SAASC,MAAM,EACnDA,EAASC,OAAOC,YAAYN,EAAOO,QAAQ,CAAC,EAClDL,OAAOC,SAASK,KAAOZ,EAAkBQ,CAAM,CACjD,CAAE,CAAA,CAGR"}
1
+ {"version":3,"file":"consent-CfRbz2R7.js","names":["RouteErrorFallback","buildAuthorizeUrl","ConsentError","props","ErrorComponentProps","params","URLSearchParams","window","location","search","Object","fromEntries","entries","href","errorComponent"],"sources":["../../../frontend/src/routes/consent/index.tsx?tsr-split=errorComponent"],"sourcesContent":["import { useMutation, useSuspenseQuery } from '@tanstack/react-query';\nimport {\n createFileRoute,\n type ErrorComponentProps,\n} from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRCard } from '@tinyrack/ui/components/card';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { ShieldCheckIcon, XIcon } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { InitialAvatar } from '#frontend/components/ui/initial-avatar.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport {\n buildAuthorizeUrl,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport {\n consentDecisionMutationOptions,\n getConsentInfoQueryOptions,\n} from '#frontend/queries/consent.ts';\n\nconst ConsentSearchSchema = OAuthSearchSchema.extend({\n client_id: z.string(),\n redirect_uri: z.string(),\n response_type: z.string(),\n});\n\nexport const Route = createFileRoute('/consent/')({\n component: Consent,\n errorComponent: ConsentError,\n validateSearch: ConsentSearchSchema,\n loaderDeps: ({ search }) => ({\n client_id: search.client_id,\n scope: search.scope,\n }),\n loader: async ({ context, deps }) => {\n await context.queryClient.ensureQueryData(\n getConsentInfoQueryOptions({\n client_id: deps.client_id,\n scope: deps.scope,\n }),\n );\n },\n});\n\nfunction ConsentError(props: ErrorComponentProps) {\n return (\n <RouteErrorFallback\n {...props}\n onUnauthorized={() => {\n // Re-enter the OAuth flow from the beginning so the\n // user can log in and be redirected back through\n // /oauth/authorize -> /consent/.\n const params = new URLSearchParams(window.location.search);\n const search = Object.fromEntries(params.entries());\n window.location.href = buildAuthorizeUrl(search);\n }}\n />\n );\n}\n\nfunction Consent() {\n const { t } = useTranslation();\n const search = Route.useSearch();\n\n const consentInfoQuery = useSuspenseQuery(\n getConsentInfoQueryOptions({\n client_id: search.client_id,\n scope: search.scope,\n }),\n );\n\n const consentMutation = useMutation({\n ...consentDecisionMutationOptions,\n onSuccess: (data) => {\n window.location.href = data.redirect_url;\n },\n });\n\n const handleAllow = () => {\n consentMutation.mutate({\n client_id: search.client_id,\n redirect_uri: search.redirect_uri,\n response_type: search.response_type,\n scope: search.scope,\n state: search.state,\n nonce: search.nonce,\n code_challenge: search.code_challenge,\n code_challenge_method: search.code_challenge_method,\n prompt: search.prompt,\n max_age: search.max_age,\n reauthenticated: search.reauthenticated,\n display: search.display,\n response_mode: search.response_mode,\n login_hint: search.login_hint,\n ui_locales: search.ui_locales,\n id_token_hint: search.id_token_hint,\n acr_values: search.acr_values,\n account_selected: search.account_selected,\n account_selection_state: search.account_selection_state,\n decision: 'allow',\n });\n };\n\n const handleDeny = () => {\n consentMutation.mutate({\n client_id: search.client_id,\n redirect_uri: search.redirect_uri,\n response_type: search.response_type,\n scope: search.scope,\n state: search.state,\n nonce: search.nonce,\n code_challenge: search.code_challenge,\n code_challenge_method: search.code_challenge_method,\n prompt: search.prompt,\n max_age: search.max_age,\n reauthenticated: search.reauthenticated,\n display: search.display,\n response_mode: search.response_mode,\n login_hint: search.login_hint,\n ui_locales: search.ui_locales,\n id_token_hint: search.id_token_hint,\n acr_values: search.acr_values,\n account_selected: search.account_selected,\n account_selection_state: search.account_selection_state,\n decision: 'deny',\n });\n };\n\n const { client, scopes, user } = consentInfoQuery.data;\n\n return (\n <AuthLayout width=\"wide\">\n <AuthPageHeader\n subtitle={t('consent.subtitle', { app: client.name })}\n title={t('consent.title')}\n />\n\n {/*\n Who is about to hand out access. The decision is meaningless without\n it, so it sits above the scopes rather than in fine print.\n */}\n <TRCard.Root padding=\"lg\" variant=\"outlined\">\n <TRCard.Content className=\"flex items-center gap-tinyrack-md\">\n <InitialAvatar email={user.email} size=\"sm\" />\n <span className=\"flex min-w-0 flex-col\">\n <TRText color=\"muted\" variant=\"caption\">\n {t('consent.loggedInAs')}\n </TRText>\n <TRText\n data-testid=\"consent-user-email\"\n truncate\n variant=\"body\"\n weight=\"medium\"\n >\n {user.email}\n </TRText>\n </span>\n </TRCard.Content>\n </TRCard.Root>\n\n {/* Requested permissions */}\n <div className=\"flex flex-col gap-tinyrack-sm\">\n <TRText as=\"h3\" variant=\"label\">\n {t('consent.permissions.title')}\n </TRText>\n <ul\n className=\"flex flex-col gap-tinyrack-xs\"\n data-testid=\"consent-scope-list\"\n >\n {scopes.map((scope: { name: string; description: string }) => (\n <li\n className=\"flex items-center gap-tinyrack-sm rounded-tinyrack-md bg-tinyrack-surface-muted px-tinyrack-md py-tinyrack-sm\"\n key={scope.name}\n >\n <ShieldCheckIcon\n aria-hidden\n className=\"size-tinyrack-lg shrink-0 text-tinyrack-info\"\n />\n <TRText variant=\"body\">\n {t(`consent.scope.${scope.name}`, {\n defaultValue: scope.description,\n })}\n </TRText>\n </li>\n ))}\n </ul>\n </div>\n\n {/*\n Sticky so the decision stays reachable when a client asks for a long\n list of scopes and the page scrolls.\n */}\n <div className=\"sticky bottom-0 flex gap-tinyrack-sm border-tinyrack-border border-t-tinyrack-default bg-tinyrack-surface/80 py-tinyrack-md backdrop-blur-sm\">\n <TRButton\n appearance=\"outline\"\n className=\"flex-1\"\n data-testid=\"consent-deny\"\n intent=\"neutral\"\n loading={consentMutation.isPending}\n loadingLabel={t('consent.deny')}\n onClick={handleDeny}\n type=\"button\"\n uiSize=\"lg\"\n >\n <XIcon aria-hidden className=\"size-tinyrack-lg\" />\n {t('consent.deny')}\n </TRButton>\n <TRButton\n className=\"flex-1\"\n data-testid=\"consent-allow\"\n intent=\"primary\"\n loading={consentMutation.isPending}\n loadingLabel={t('consent.allow')}\n onClick={handleAllow}\n type=\"button\"\n uiSize=\"lg\"\n >\n <ShieldCheckIcon aria-hidden className=\"size-tinyrack-lg\" />\n {t('consent.allow')}\n </TRButton>\n </div>\n </AuthLayout>\n );\n}\n"],"mappings":"4GAgDA,SAASE,EAAaC,EAA4B,CAChD,OACE,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,GAAIA,EACJ,mBAAsB,CAIpB,IAAME,EAAS,IAAIC,gBAAgBC,OAAOC,SAASC,MAAM,EACnDA,EAASC,OAAOC,YAAYN,EAAOO,QAAQ,CAAC,EAClDL,OAAOC,SAASK,KAAOZ,EAAkBQ,CAAM,CACjD,CAAE,CAAA,CAGR"}
@@ -0,0 +1,2 @@
1
+ import{I as e}from"./index-CS23Ozt6.js";var t=e;export{t as errorComponent};
2
+ //# sourceMappingURL=email-C5vUVgr_.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"email-UL_cNRnQ.js","names":["RouteErrorFallback","VerifyEmailFormValues","token","SplitErrorComponent","errorComponent"],"sources":["../../../frontend/src/routes/verify/email/index.tsx?tsr-split=errorComponent"],"sourcesContent":["import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute, redirect, useNavigate } from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRSpinner } from '@tinyrack/ui/components/spinner';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { TRToast } from '@tinyrack/ui/components/toast';\nimport { CircleCheckIcon, KeyRoundIcon, MailIcon } from 'lucide-react';\nimport { useMemo, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthField } from '#frontend/components/auth/auth-field.tsx';\nimport { AuthOutcome } from '#frontend/components/auth/auth-outcome.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { LabeledSeparator } from '#frontend/components/ui/labeled-separator.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\n\nimport {\n buildAuthenticatedAuthorizeUrl,\n extractOAuthParams,\n isOAuthFlow,\n OAuthSearchSchema,\n type SecondFactorMethod,\n} from '#frontend/libs/oauth-search.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport {\n resendVerificationMutationOptions,\n verifyEmailMutationOptions,\n} from '#frontend/queries/verify-email.ts';\n\nconst SearchSchema = z.object({\n ...OAuthSearchSchema.shape,\n token: z.string().default(''),\n email: z.string().default(''),\n});\n\nexport const Route = createFileRoute('/verify/email/')({\n component: VerifyEmail,\n errorComponent: RouteErrorFallback,\n validateSearch: SearchSchema,\n beforeLoad: async ({ context }) => {\n const config = await context.queryClient.ensureQueryData(\n appConfigQueryOptions,\n );\n const isPasswordAuthEnabled = config.auth.password.enabled;\n if (!isPasswordAuthEnabled) {\n throw redirect({ to: '/login' });\n }\n },\n loader: async ({ context }) => {\n await context.queryClient.ensureQueryData(appConfigQueryOptions);\n },\n});\n\ntype VerifyEmailFormValues = {\n token: string;\n};\n\nfunction VerifyEmail() {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n const { token: queryToken, email } = search;\n const [verified, setVerified] = useState(false);\n const [resendCooldown, setResendCooldown] = useState(false);\n const toast = TRToast.useToastManager();\n const { data: appConfig } = useSuspenseQuery(appConfigQueryOptions);\n\n const verifyEmailSchema = useMemo(\n () =>\n z.object({\n token: z.string().min(1, t('validation.token.required')),\n }),\n [t],\n );\n\n const verifyEmailMutation = useMutation({\n ...verifyEmailMutationOptions,\n onSuccess: async (data) => {\n const user = data.user;\n\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: user,\n });\n await tick();\n\n if (user.second_factor_required) {\n const available_2fa_methods: SecondFactorMethod[] = [];\n if (appConfig.auth.password.totp.enabled) {\n available_2fa_methods.push('totp');\n }\n if (appConfig.auth.passkey.enabled) {\n available_2fa_methods.push('passkey');\n }\n\n if (available_2fa_methods.length === 1) {\n const method = available_2fa_methods[0];\n if (method === 'totp') {\n return navigate({\n to: '/setup/totp',\n search: extractOAuthParams(search),\n });\n } else {\n return navigate({\n to: '/setup/passkey',\n search: extractOAuthParams(search),\n });\n }\n } else {\n return navigate({\n to: '/setup/2fa',\n search: extractOAuthParams(search),\n });\n }\n }\n\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n setVerified(true);\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n const resendVerificationMutation = useMutation({\n ...resendVerificationMutationOptions,\n onSuccess: () => {\n toast.add({ title: t('verifyEmail.resendSuccess'), type: 'success' });\n // Separate from the message: a short cooldown so the button cannot be\n // used to spam the address with mail.\n setResendCooldown(true);\n setTimeout(() => setResendCooldown(false), 5000);\n },\n });\n\n const {\n register,\n setError,\n handleSubmit,\n formState: { errors },\n } = useForm<VerifyEmailFormValues>({\n defaultValues: {\n token: queryToken || '',\n },\n resolver: standardSchemaResolver(verifyEmailSchema),\n });\n\n const onSubmit = async (values: VerifyEmailFormValues) => {\n try {\n await verifyEmailMutation.mutateAsync(values);\n } catch (_error) {\n setError('token', {\n type: 'manual',\n message: t('verifyEmail.error.invalidToken'),\n });\n }\n };\n\n const handleResend = async () => {\n if (!email) {\n return;\n }\n try {\n await resendVerificationMutation.mutateAsync({ email });\n } catch (_error) {}\n };\n\n if (verified) {\n return (\n <AuthLayout>\n {/*\n The testid stays on a wrapper: e2e treats \"the success alert\" as the\n whole terminal state, which is now the outcome block rather than a\n banner stacked above a header.\n */}\n <div data-testid=\"alert-success\">\n <AuthOutcome\n description={\n <>\n {t('verifyEmail.success.subtitle')}\n <br />\n {t('verifyEmail.success.description')}\n </>\n }\n icon={CircleCheckIcon}\n title={t('verifyEmail.success.title')}\n tone=\"success\"\n >\n <TRButton\n className=\"w-full\"\n data-testid=\"email-verify-go-profile\"\n intent=\"primary\"\n onClick={() => navigate({ to: '/profile' })}\n type=\"button\"\n uiSize=\"lg\"\n >\n {t('verifyEmail.success.goToProfile')}\n </TRButton>\n </AuthOutcome>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('verifyEmail.subtitle')}\n title={t('verifyEmail.title')}\n />\n\n {email && (\n <Alert icon={MailIcon} type=\"info\">\n <span className=\"flex flex-col gap-tinyrack-3xs text-left\">\n <TRText variant=\"body\" weight=\"strong\">\n {t('register.success.subtitle')}\n </TRText>\n <TRText color=\"muted\" variant=\"caption\">\n {t('register.success.description', { email })}\n </TRText>\n </span>\n </Alert>\n )}\n\n <form\n className=\"flex flex-col gap-tinyrack-lg\"\n onSubmit={handleSubmit(onSubmit)}\n >\n <AuthField\n error={errors.token}\n icon={KeyRoundIcon}\n label={t('verifyEmail.token.label')}\n placeholder={t('verifyEmail.token.placeholder')}\n {...register('token')}\n type=\"text\"\n />\n\n <TRButton\n className=\"w-full\"\n intent=\"primary\"\n loading={verifyEmailMutation.isPending}\n loadingLabel={t('verifyEmail.submitting')}\n type=\"submit\"\n uiSize=\"lg\"\n >\n {t('verifyEmail.submit')}\n </TRButton>\n </form>\n\n {email && (\n <div className=\"flex flex-col gap-tinyrack-md\">\n <LabeledSeparator />\n\n <TRButton\n appearance=\"ghost\"\n className=\"w-full\"\n data-testid=\"email-verify-resend\"\n disabled={resendVerificationMutation.isPending || resendCooldown}\n intent=\"neutral\"\n onClick={handleResend}\n type=\"button\"\n >\n {resendVerificationMutation.isPending ? (\n <>\n <TRSpinner uiSize=\"sm\" />\n {t('verifyEmail.resending')}\n </>\n ) : (\n t('verifyEmail.resend')\n )}\n </TRButton>\n </div>\n )}\n </AuthLayout>\n );\n}\n"],"mappings":"wCAiEE,IAAAG,EA5COH"}
1
+ {"version":3,"file":"email-C5vUVgr_.js","names":["RouteErrorFallback","VerifyEmailFormValues","token","SplitErrorComponent","errorComponent"],"sources":["../../../frontend/src/routes/verify/email/index.tsx?tsr-split=errorComponent"],"sourcesContent":["import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute, redirect, useNavigate } from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRSpinner } from '@tinyrack/ui/components/spinner';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { TRToast } from '@tinyrack/ui/components/toast';\nimport { CircleCheckIcon, KeyRoundIcon, MailIcon } from 'lucide-react';\nimport { useMemo, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthField } from '#frontend/components/auth/auth-field.tsx';\nimport { AuthOutcome } from '#frontend/components/auth/auth-outcome.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { LabeledSeparator } from '#frontend/components/ui/labeled-separator.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\n\nimport {\n buildAuthenticatedAuthorizeUrl,\n extractOAuthParams,\n isOAuthFlow,\n OAuthSearchSchema,\n type SecondFactorMethod,\n} from '#frontend/libs/oauth-search.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport {\n resendVerificationMutationOptions,\n verifyEmailMutationOptions,\n} from '#frontend/queries/verify-email.ts';\n\nconst SearchSchema = z.object({\n ...OAuthSearchSchema.shape,\n token: z.string().default(''),\n email: z.string().default(''),\n});\n\nexport const Route = createFileRoute('/verify/email/')({\n component: VerifyEmail,\n errorComponent: RouteErrorFallback,\n validateSearch: SearchSchema,\n beforeLoad: async ({ context }) => {\n const config = await context.queryClient.ensureQueryData(\n appConfigQueryOptions,\n );\n const isPasswordAuthEnabled = config.auth.password.enabled;\n if (!isPasswordAuthEnabled) {\n throw redirect({ to: '/login' });\n }\n },\n loader: async ({ context }) => {\n await context.queryClient.ensureQueryData(appConfigQueryOptions);\n },\n});\n\ntype VerifyEmailFormValues = {\n token: string;\n};\n\nfunction VerifyEmail() {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n const { token: queryToken, email } = search;\n const [verified, setVerified] = useState(false);\n const [resendCooldown, setResendCooldown] = useState(false);\n const toast = TRToast.useToastManager();\n const { data: appConfig } = useSuspenseQuery(appConfigQueryOptions);\n\n const verifyEmailSchema = useMemo(\n () =>\n z.object({\n token: z.string().min(1, t('validation.token.required')),\n }),\n [t],\n );\n\n const verifyEmailMutation = useMutation({\n ...verifyEmailMutationOptions,\n onSuccess: async (data) => {\n const user = data.user;\n\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: user,\n });\n await tick();\n\n if (user.second_factor_required) {\n const available_2fa_methods: SecondFactorMethod[] = [];\n if (appConfig.auth.password.totp.enabled) {\n available_2fa_methods.push('totp');\n }\n if (appConfig.auth.passkey.enabled) {\n available_2fa_methods.push('passkey');\n }\n\n if (available_2fa_methods.length === 1) {\n const method = available_2fa_methods[0];\n if (method === 'totp') {\n return navigate({\n to: '/setup/totp',\n search: extractOAuthParams(search),\n });\n } else {\n return navigate({\n to: '/setup/passkey',\n search: extractOAuthParams(search),\n });\n }\n } else {\n return navigate({\n to: '/setup/2fa',\n search: extractOAuthParams(search),\n });\n }\n }\n\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n setVerified(true);\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n const resendVerificationMutation = useMutation({\n ...resendVerificationMutationOptions,\n onSuccess: () => {\n toast.add({ title: t('verifyEmail.resendSuccess'), type: 'success' });\n // Separate from the message: a short cooldown so the button cannot be\n // used to spam the address with mail.\n setResendCooldown(true);\n setTimeout(() => setResendCooldown(false), 5000);\n },\n });\n\n const {\n register,\n setError,\n handleSubmit,\n formState: { errors },\n } = useForm<VerifyEmailFormValues>({\n defaultValues: {\n token: queryToken || '',\n },\n resolver: standardSchemaResolver(verifyEmailSchema),\n });\n\n const onSubmit = async (values: VerifyEmailFormValues) => {\n try {\n await verifyEmailMutation.mutateAsync(values);\n } catch (_error) {\n setError('token', {\n type: 'manual',\n message: t('verifyEmail.error.invalidToken'),\n });\n }\n };\n\n const handleResend = async () => {\n if (!email) {\n return;\n }\n try {\n await resendVerificationMutation.mutateAsync({ email });\n } catch (_error) {}\n };\n\n if (verified) {\n return (\n <AuthLayout>\n {/*\n The testid stays on a wrapper: e2e treats \"the success alert\" as the\n whole terminal state, which is now the outcome block rather than a\n banner stacked above a header.\n */}\n <div data-testid=\"alert-success\">\n <AuthOutcome\n description={\n <>\n {t('verifyEmail.success.subtitle')}\n <br />\n {t('verifyEmail.success.description')}\n </>\n }\n icon={CircleCheckIcon}\n title={t('verifyEmail.success.title')}\n tone=\"success\"\n >\n <TRButton\n className=\"w-full\"\n data-testid=\"email-verify-go-profile\"\n intent=\"primary\"\n onClick={() => navigate({ to: '/profile' })}\n type=\"button\"\n uiSize=\"lg\"\n >\n {t('verifyEmail.success.goToProfile')}\n </TRButton>\n </AuthOutcome>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('verifyEmail.subtitle')}\n title={t('verifyEmail.title')}\n />\n\n {email && (\n <Alert icon={MailIcon} type=\"info\">\n <span className=\"flex flex-col gap-tinyrack-3xs text-left\">\n <TRText variant=\"body\" weight=\"strong\">\n {t('register.success.subtitle')}\n </TRText>\n <TRText color=\"muted\" variant=\"caption\">\n {t('register.success.description', { email })}\n </TRText>\n </span>\n </Alert>\n )}\n\n <form\n className=\"flex flex-col gap-tinyrack-lg\"\n onSubmit={handleSubmit(onSubmit)}\n >\n <AuthField\n error={errors.token}\n icon={KeyRoundIcon}\n label={t('verifyEmail.token.label')}\n placeholder={t('verifyEmail.token.placeholder')}\n {...register('token')}\n type=\"text\"\n />\n\n <TRButton\n className=\"w-full\"\n intent=\"primary\"\n loading={verifyEmailMutation.isPending}\n loadingLabel={t('verifyEmail.submitting')}\n type=\"submit\"\n uiSize=\"lg\"\n >\n {t('verifyEmail.submit')}\n </TRButton>\n </form>\n\n {email && (\n <div className=\"flex flex-col gap-tinyrack-md\">\n <LabeledSeparator />\n\n <TRButton\n appearance=\"ghost\"\n className=\"w-full\"\n data-testid=\"email-verify-resend\"\n disabled={resendVerificationMutation.isPending || resendCooldown}\n intent=\"neutral\"\n onClick={handleResend}\n type=\"button\"\n >\n {resendVerificationMutation.isPending ? (\n <>\n <TRSpinner uiSize=\"sm\" />\n {t('verifyEmail.resending')}\n </>\n ) : (\n t('verifyEmail.resend')\n )}\n </TRButton>\n </div>\n )}\n </AuthLayout>\n );\n}\n"],"mappings":"wCAiEE,IAAAG,EA5COH"}
@@ -1,2 +1,2 @@
1
- import{J as e,U as t,Z as n,i as r,n as i,q as a,s as o}from"./createLucideIcon-BPpAIXR_.js";import{f as s,i as c,r as l,sr as u,t as d}from"./config-v3-oy_ef.js";import{t as f}from"./mutationOptions-CrswitPs.js";import{t as p}from"./useMutation-ZLFeJg75.js";import{tt as m}from"./shell-header-bar-C9AB5lT-.js";import{t as h}from"./circle-check-6QTLhwM_.js";import{t as g}from"./auth-field-HwxENJnB.js";import{t as _}from"./key-round-CdvHb8vT.js";import{t as v}from"./mail-D0B_1Dzd.js";import{c as y,o as b}from"./zod-C87S6Tec.js";import{F as x,L as S,N as C,S as w,V as T,j as E,p as D}from"./index-gliHKncz.js";import{t as O}from"./auth-page-header-CIs08f5Y.js";import{t as k}from"./alert-leqyvHOp.js";import{t as A}from"./auth-layout-CoeqNhTT.js";import{t as j}from"./promise-OpBtq8tG.js";import{r as M,t as N}from"./standard-schema-e8gtSzGE.js";import{t as P}from"./labeled-separator-fnKySKv2.js";var F=n(e()),I=f({mutationFn:async e=>c(await l.api.auth.email.verify.$post({json:e}))}),L=f({mutationFn:async e=>c(await l.api.auth.email.resend.$post({json:e,header:{}}))}),R=a();function z(){let{t:e}=r(),n=T(),a=u(),c=D.useSearch(),{token:l,email:f}=c,[z,B]=(0,F.useState)(!1),[V,H]=(0,F.useState)(!1),U=s.useToastManager(),{data:W}=m(d),G=(0,F.useMemo)(()=>b({token:y().min(1,e(`validation.token.required`))}),[e]),K=p({...I,onSuccess:async e=>{let t=e.user;if(a.setQueryData(w.queryKey,{user:t}),await j(),t.second_factor_required){let e=[];return W.auth.password.totp.enabled&&e.push(`totp`),W.auth.passkey.enabled&&e.push(`passkey`),e.length===1?e[0]===`totp`?n({to:`/setup/totp`,search:C(c)}):n({to:`/setup/passkey`,search:C(c)}):n({to:`/setup/2fa`,search:C(c)})}x(c)?window.location.href=E(c):B(!0)},onSettled:()=>{a.invalidateQueries({queryKey:w.queryKey})}}),q=p({...L,onSuccess:()=>{U.add({title:e(`verifyEmail.resendSuccess`),type:`success`}),H(!0),setTimeout(()=>H(!1),5e3)}}),{register:J,setError:Y,handleSubmit:X,formState:{errors:Z}}=M({defaultValues:{token:l||``},resolver:N(G)}),Q=async t=>{try{await K.mutateAsync(t)}catch{Y(`token`,{type:`manual`,message:e(`verifyEmail.error.invalidToken`)})}},$=async()=>{if(f)try{await q.mutateAsync({email:f})}catch{}};return z?(0,R.jsx)(A,{children:(0,R.jsx)(`div`,{"data-testid":`alert-success`,children:(0,R.jsx)(S,{description:(0,R.jsxs)(R.Fragment,{children:[e(`verifyEmail.success.subtitle`),(0,R.jsx)(`br`,{}),e(`verifyEmail.success.description`)]}),icon:h,title:e(`verifyEmail.success.title`),tone:`success`,children:(0,R.jsx)(o,{className:`w-full`,"data-testid":`email-verify-go-profile`,intent:`primary`,onClick:()=>n({to:`/profile`}),type:`button`,uiSize:`lg`,children:e(`verifyEmail.success.goToProfile`)})})})}):(0,R.jsxs)(A,{children:[(0,R.jsx)(O,{subtitle:e(`verifyEmail.subtitle`),title:e(`verifyEmail.title`)}),f&&(0,R.jsx)(k,{icon:v,type:`info`,children:(0,R.jsxs)(`span`,{className:`flex flex-col gap-tinyrack-3xs text-left`,children:[(0,R.jsx)(i,{variant:`body`,weight:`strong`,children:e(`register.success.subtitle`)}),(0,R.jsx)(i,{color:`muted`,variant:`caption`,children:e(`register.success.description`,{email:f})})]})}),(0,R.jsxs)(`form`,{className:`flex flex-col gap-tinyrack-lg`,onSubmit:X(Q),children:[(0,R.jsx)(g,{error:Z.token,icon:_,label:e(`verifyEmail.token.label`),placeholder:e(`verifyEmail.token.placeholder`),...J(`token`),type:`text`}),(0,R.jsx)(o,{className:`w-full`,intent:`primary`,loading:K.isPending,loadingLabel:e(`verifyEmail.submitting`),type:`submit`,uiSize:`lg`,children:e(`verifyEmail.submit`)})]}),f&&(0,R.jsxs)(`div`,{className:`flex flex-col gap-tinyrack-md`,children:[(0,R.jsx)(P,{}),(0,R.jsx)(o,{appearance:`ghost`,className:`w-full`,"data-testid":`email-verify-resend`,disabled:q.isPending||V,intent:`neutral`,onClick:$,type:`button`,children:q.isPending?(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(t,{uiSize:`sm`}),e(`verifyEmail.resending`)]}):e(`verifyEmail.resend`)})]})]})}export{z as component};
2
- //# sourceMappingURL=email-CVRDeJ78.js.map
1
+ import{J as e,U as t,Z as n,i as r,n as i,q as a,s as o}from"./createLucideIcon-BPpAIXR_.js";import{Sr as s,f as c,i as l,r as u,t as d}from"./config-DTQCUlfZ.js";import{t as f}from"./mutationOptions-DyYFUbyn.js";import{t as p}from"./useMutation-BWZ0G4Td.js";import{tt as m}from"./shell-header-bar-CjG5qvPg.js";import{t as h}from"./circle-check-6QTLhwM_.js";import{t as g}from"./auth-field-DD8kvl--.js";import{t as _}from"./key-round-CdvHb8vT.js";import{t as v}from"./mail-D0B_1Dzd.js";import{c as y,o as b}from"./zod-C87S6Tec.js";import{F as x,L as S,N as C,S as w,V as T,j as E,p as D}from"./index-CS23Ozt6.js";import{t as O}from"./auth-page-header-CIn2E4zI.js";import{t as k}from"./alert-leqyvHOp.js";import{t as A}from"./auth-layout-VtpryDof.js";import{t as j}from"./promise-OpBtq8tG.js";import{r as M,t as N}from"./standard-schema-e8gtSzGE.js";import{t as P}from"./labeled-separator-CV7nb0XW.js";var F=n(e()),I=f({mutationFn:async e=>l(await u.api.auth.email.verify.$post({json:e}))}),L=f({mutationFn:async e=>l(await u.api.auth.email.resend.$post({json:e,header:{}}))}),R=a();function z(){let{t:e}=r(),n=T(),a=s(),l=D.useSearch(),{token:u,email:f}=l,[z,B]=(0,F.useState)(!1),[V,H]=(0,F.useState)(!1),U=c.useToastManager(),{data:W}=m(d),G=(0,F.useMemo)(()=>b({token:y().min(1,e(`validation.token.required`))}),[e]),K=p({...I,onSuccess:async e=>{let t=e.user;if(a.setQueryData(w.queryKey,{user:t}),await j(),t.second_factor_required){let e=[];return W.auth.password.totp.enabled&&e.push(`totp`),W.auth.passkey.enabled&&e.push(`passkey`),e.length===1?e[0]===`totp`?n({to:`/setup/totp`,search:C(l)}):n({to:`/setup/passkey`,search:C(l)}):n({to:`/setup/2fa`,search:C(l)})}x(l)?window.location.href=E(l):B(!0)},onSettled:()=>{a.invalidateQueries({queryKey:w.queryKey})}}),q=p({...L,onSuccess:()=>{U.add({title:e(`verifyEmail.resendSuccess`),type:`success`}),H(!0),setTimeout(()=>H(!1),5e3)}}),{register:J,setError:Y,handleSubmit:X,formState:{errors:Z}}=M({defaultValues:{token:u||``},resolver:N(G)}),Q=async t=>{try{await K.mutateAsync(t)}catch{Y(`token`,{type:`manual`,message:e(`verifyEmail.error.invalidToken`)})}},$=async()=>{if(f)try{await q.mutateAsync({email:f})}catch{}};return z?(0,R.jsx)(A,{children:(0,R.jsx)(`div`,{"data-testid":`alert-success`,children:(0,R.jsx)(S,{description:(0,R.jsxs)(R.Fragment,{children:[e(`verifyEmail.success.subtitle`),(0,R.jsx)(`br`,{}),e(`verifyEmail.success.description`)]}),icon:h,title:e(`verifyEmail.success.title`),tone:`success`,children:(0,R.jsx)(o,{className:`w-full`,"data-testid":`email-verify-go-profile`,intent:`primary`,onClick:()=>n({to:`/profile`}),type:`button`,uiSize:`lg`,children:e(`verifyEmail.success.goToProfile`)})})})}):(0,R.jsxs)(A,{children:[(0,R.jsx)(O,{subtitle:e(`verifyEmail.subtitle`),title:e(`verifyEmail.title`)}),f&&(0,R.jsx)(k,{icon:v,type:`info`,children:(0,R.jsxs)(`span`,{className:`flex flex-col gap-tinyrack-3xs text-left`,children:[(0,R.jsx)(i,{variant:`body`,weight:`strong`,children:e(`register.success.subtitle`)}),(0,R.jsx)(i,{color:`muted`,variant:`caption`,children:e(`register.success.description`,{email:f})})]})}),(0,R.jsxs)(`form`,{className:`flex flex-col gap-tinyrack-lg`,onSubmit:X(Q),children:[(0,R.jsx)(g,{error:Z.token,icon:_,label:e(`verifyEmail.token.label`),placeholder:e(`verifyEmail.token.placeholder`),...J(`token`),type:`text`}),(0,R.jsx)(o,{className:`w-full`,intent:`primary`,loading:K.isPending,loadingLabel:e(`verifyEmail.submitting`),type:`submit`,uiSize:`lg`,children:e(`verifyEmail.submit`)})]}),f&&(0,R.jsxs)(`div`,{className:`flex flex-col gap-tinyrack-md`,children:[(0,R.jsx)(P,{}),(0,R.jsx)(o,{appearance:`ghost`,className:`w-full`,"data-testid":`email-verify-resend`,disabled:q.isPending||V,intent:`neutral`,onClick:$,type:`button`,children:q.isPending?(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(t,{uiSize:`sm`}),e(`verifyEmail.resending`)]}):e(`verifyEmail.resend`)})]})]})}export{z as component};
2
+ //# sourceMappingURL=email-CKKakxPJ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"email-CVRDeJ78.js","names":["standardSchemaResolver","useMutation","useQueryClient","useSuspenseQuery","useNavigate","TRButton","TRSpinner","TRText","TRToast","CircleCheckIcon","KeyRoundIcon","MailIcon","useMemo","useState","useForm","useTranslation","z","AuthField","AuthOutcome","AuthPageHeader","Alert","LabeledSeparator","AuthLayout","buildAuthenticatedAuthorizeUrl","extractOAuthParams","isOAuthFlow","tick","appConfigQueryOptions","getSessionQueryOptions","resendVerificationMutationOptions","verifyEmailMutationOptions","Route","VerifyEmailFormValues","token","VerifyEmail","t","navigate","queryClient","search","useSearch","queryToken","email","verified","setVerified","resendCooldown","setResendCooldown","toast","useToastManager","data","appConfig","verifyEmailSchema","object","string","min","verifyEmailMutation","onSuccess","user","setQueryData","queryKey","second_factor_required","available_2fa_methods","SecondFactorMethod","auth","password","totp","enabled","push","passkey","length","method","to","window","location","href","onSettled","invalidateQueries","resendVerificationMutation","add","title","type","setTimeout","register","setError","handleSubmit","formState","errors","defaultValues","resolver","onSubmit","values","mutateAsync","_error","message","handleResend","isPending","component"],"sources":["../../../frontend/src/queries/verify-email.ts","../../../frontend/src/routes/verify/email/index.tsx?tsr-split=component"],"sourcesContent":["import { mutationOptions } from '@tanstack/react-query';\nimport type { InferRequestType, InferResponseType } from 'hono/client';\nimport { client, jsonOk } from '#frontend/libs/api.ts';\n\nexport type VerifyEmailParams = InferRequestType<\n (typeof client.api.auth.email.verify)['$post']\n>['json'];\n\nexport type VerifyEmailResponse = InferResponseType<\n (typeof client.api.auth.email.verify)['$post'],\n 200\n>;\n\nexport const verifyEmailMutationOptions = mutationOptions({\n mutationFn: async (values: VerifyEmailParams) => {\n const res = await client.api.auth.email.verify.$post({\n json: values,\n });\n return jsonOk(res);\n },\n});\n\nexport type ResendVerificationParams = InferRequestType<\n (typeof client.api.auth.email.resend)['$post']\n>['json'];\n\nexport type ResendVerificationResponse = InferResponseType<\n (typeof client.api.auth.email.resend)['$post'],\n 200\n>;\n\nexport const resendVerificationMutationOptions = mutationOptions({\n mutationFn: async (values: ResendVerificationParams) => {\n const res = await client.api.auth.email.resend.$post({\n json: values,\n header: {},\n });\n return jsonOk(res);\n },\n});\n","import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute, redirect, useNavigate } from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRSpinner } from '@tinyrack/ui/components/spinner';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { TRToast } from '@tinyrack/ui/components/toast';\nimport { CircleCheckIcon, KeyRoundIcon, MailIcon } from 'lucide-react';\nimport { useMemo, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthField } from '#frontend/components/auth/auth-field.tsx';\nimport { AuthOutcome } from '#frontend/components/auth/auth-outcome.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { LabeledSeparator } from '#frontend/components/ui/labeled-separator.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\n\nimport {\n buildAuthenticatedAuthorizeUrl,\n extractOAuthParams,\n isOAuthFlow,\n OAuthSearchSchema,\n type SecondFactorMethod,\n} from '#frontend/libs/oauth-search.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport {\n resendVerificationMutationOptions,\n verifyEmailMutationOptions,\n} from '#frontend/queries/verify-email.ts';\n\nconst SearchSchema = z.object({\n ...OAuthSearchSchema.shape,\n token: z.string().default(''),\n email: z.string().default(''),\n});\n\nexport const Route = createFileRoute('/verify/email/')({\n component: VerifyEmail,\n errorComponent: RouteErrorFallback,\n validateSearch: SearchSchema,\n beforeLoad: async ({ context }) => {\n const config = await context.queryClient.ensureQueryData(\n appConfigQueryOptions,\n );\n const isPasswordAuthEnabled = config.auth.password.enabled;\n if (!isPasswordAuthEnabled) {\n throw redirect({ to: '/login' });\n }\n },\n loader: async ({ context }) => {\n await context.queryClient.ensureQueryData(appConfigQueryOptions);\n },\n});\n\ntype VerifyEmailFormValues = {\n token: string;\n};\n\nfunction VerifyEmail() {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n const { token: queryToken, email } = search;\n const [verified, setVerified] = useState(false);\n const [resendCooldown, setResendCooldown] = useState(false);\n const toast = TRToast.useToastManager();\n const { data: appConfig } = useSuspenseQuery(appConfigQueryOptions);\n\n const verifyEmailSchema = useMemo(\n () =>\n z.object({\n token: z.string().min(1, t('validation.token.required')),\n }),\n [t],\n );\n\n const verifyEmailMutation = useMutation({\n ...verifyEmailMutationOptions,\n onSuccess: async (data) => {\n const user = data.user;\n\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: user,\n });\n await tick();\n\n if (user.second_factor_required) {\n const available_2fa_methods: SecondFactorMethod[] = [];\n if (appConfig.auth.password.totp.enabled) {\n available_2fa_methods.push('totp');\n }\n if (appConfig.auth.passkey.enabled) {\n available_2fa_methods.push('passkey');\n }\n\n if (available_2fa_methods.length === 1) {\n const method = available_2fa_methods[0];\n if (method === 'totp') {\n return navigate({\n to: '/setup/totp',\n search: extractOAuthParams(search),\n });\n } else {\n return navigate({\n to: '/setup/passkey',\n search: extractOAuthParams(search),\n });\n }\n } else {\n return navigate({\n to: '/setup/2fa',\n search: extractOAuthParams(search),\n });\n }\n }\n\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n setVerified(true);\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n const resendVerificationMutation = useMutation({\n ...resendVerificationMutationOptions,\n onSuccess: () => {\n toast.add({ title: t('verifyEmail.resendSuccess'), type: 'success' });\n // Separate from the message: a short cooldown so the button cannot be\n // used to spam the address with mail.\n setResendCooldown(true);\n setTimeout(() => setResendCooldown(false), 5000);\n },\n });\n\n const {\n register,\n setError,\n handleSubmit,\n formState: { errors },\n } = useForm<VerifyEmailFormValues>({\n defaultValues: {\n token: queryToken || '',\n },\n resolver: standardSchemaResolver(verifyEmailSchema),\n });\n\n const onSubmit = async (values: VerifyEmailFormValues) => {\n try {\n await verifyEmailMutation.mutateAsync(values);\n } catch (_error) {\n setError('token', {\n type: 'manual',\n message: t('verifyEmail.error.invalidToken'),\n });\n }\n };\n\n const handleResend = async () => {\n if (!email) {\n return;\n }\n try {\n await resendVerificationMutation.mutateAsync({ email });\n } catch (_error) {}\n };\n\n if (verified) {\n return (\n <AuthLayout>\n {/*\n The testid stays on a wrapper: e2e treats \"the success alert\" as the\n whole terminal state, which is now the outcome block rather than a\n banner stacked above a header.\n */}\n <div data-testid=\"alert-success\">\n <AuthOutcome\n description={\n <>\n {t('verifyEmail.success.subtitle')}\n <br />\n {t('verifyEmail.success.description')}\n </>\n }\n icon={CircleCheckIcon}\n title={t('verifyEmail.success.title')}\n tone=\"success\"\n >\n <TRButton\n className=\"w-full\"\n data-testid=\"email-verify-go-profile\"\n intent=\"primary\"\n onClick={() => navigate({ to: '/profile' })}\n type=\"button\"\n uiSize=\"lg\"\n >\n {t('verifyEmail.success.goToProfile')}\n </TRButton>\n </AuthOutcome>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('verifyEmail.subtitle')}\n title={t('verifyEmail.title')}\n />\n\n {email && (\n <Alert icon={MailIcon} type=\"info\">\n <span className=\"flex flex-col gap-tinyrack-3xs text-left\">\n <TRText variant=\"body\" weight=\"strong\">\n {t('register.success.subtitle')}\n </TRText>\n <TRText color=\"muted\" variant=\"caption\">\n {t('register.success.description', { email })}\n </TRText>\n </span>\n </Alert>\n )}\n\n <form\n className=\"flex flex-col gap-tinyrack-lg\"\n onSubmit={handleSubmit(onSubmit)}\n >\n <AuthField\n error={errors.token}\n icon={KeyRoundIcon}\n label={t('verifyEmail.token.label')}\n placeholder={t('verifyEmail.token.placeholder')}\n {...register('token')}\n type=\"text\"\n />\n\n <TRButton\n className=\"w-full\"\n intent=\"primary\"\n loading={verifyEmailMutation.isPending}\n loadingLabel={t('verifyEmail.submitting')}\n type=\"submit\"\n uiSize=\"lg\"\n >\n {t('verifyEmail.submit')}\n </TRButton>\n </form>\n\n {email && (\n <div className=\"flex flex-col gap-tinyrack-md\">\n <LabeledSeparator />\n\n <TRButton\n appearance=\"ghost\"\n className=\"w-full\"\n data-testid=\"email-verify-resend\"\n disabled={resendVerificationMutation.isPending || resendCooldown}\n intent=\"neutral\"\n onClick={handleResend}\n type=\"button\"\n >\n {resendVerificationMutation.isPending ? (\n <>\n <TRSpinner uiSize=\"sm\" />\n {t('verifyEmail.resending')}\n </>\n ) : (\n t('verifyEmail.resend')\n )}\n </TRButton>\n </div>\n )}\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"email-CKKakxPJ.js","names":["standardSchemaResolver","useMutation","useQueryClient","useSuspenseQuery","useNavigate","TRButton","TRSpinner","TRText","TRToast","CircleCheckIcon","KeyRoundIcon","MailIcon","useMemo","useState","useForm","useTranslation","z","AuthField","AuthOutcome","AuthPageHeader","Alert","LabeledSeparator","AuthLayout","buildAuthenticatedAuthorizeUrl","extractOAuthParams","isOAuthFlow","tick","appConfigQueryOptions","getSessionQueryOptions","resendVerificationMutationOptions","verifyEmailMutationOptions","Route","VerifyEmailFormValues","token","VerifyEmail","t","navigate","queryClient","search","useSearch","queryToken","email","verified","setVerified","resendCooldown","setResendCooldown","toast","useToastManager","data","appConfig","verifyEmailSchema","object","string","min","verifyEmailMutation","onSuccess","user","setQueryData","queryKey","second_factor_required","available_2fa_methods","SecondFactorMethod","auth","password","totp","enabled","push","passkey","length","method","to","window","location","href","onSettled","invalidateQueries","resendVerificationMutation","add","title","type","setTimeout","register","setError","handleSubmit","formState","errors","defaultValues","resolver","onSubmit","values","mutateAsync","_error","message","handleResend","isPending","component"],"sources":["../../../frontend/src/queries/verify-email.ts","../../../frontend/src/routes/verify/email/index.tsx?tsr-split=component"],"sourcesContent":["import { mutationOptions } from '@tanstack/react-query';\nimport type { InferRequestType, InferResponseType } from 'hono/client';\nimport { client, jsonOk } from '#frontend/libs/api.ts';\n\nexport type VerifyEmailParams = InferRequestType<\n (typeof client.api.auth.email.verify)['$post']\n>['json'];\n\nexport type VerifyEmailResponse = InferResponseType<\n (typeof client.api.auth.email.verify)['$post'],\n 200\n>;\n\nexport const verifyEmailMutationOptions = mutationOptions({\n mutationFn: async (values: VerifyEmailParams) => {\n const res = await client.api.auth.email.verify.$post({\n json: values,\n });\n return jsonOk(res);\n },\n});\n\nexport type ResendVerificationParams = InferRequestType<\n (typeof client.api.auth.email.resend)['$post']\n>['json'];\n\nexport type ResendVerificationResponse = InferResponseType<\n (typeof client.api.auth.email.resend)['$post'],\n 200\n>;\n\nexport const resendVerificationMutationOptions = mutationOptions({\n mutationFn: async (values: ResendVerificationParams) => {\n const res = await client.api.auth.email.resend.$post({\n json: values,\n header: {},\n });\n return jsonOk(res);\n },\n});\n","import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute, redirect, useNavigate } from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRSpinner } from '@tinyrack/ui/components/spinner';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { TRToast } from '@tinyrack/ui/components/toast';\nimport { CircleCheckIcon, KeyRoundIcon, MailIcon } from 'lucide-react';\nimport { useMemo, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthField } from '#frontend/components/auth/auth-field.tsx';\nimport { AuthOutcome } from '#frontend/components/auth/auth-outcome.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { LabeledSeparator } from '#frontend/components/ui/labeled-separator.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\n\nimport {\n buildAuthenticatedAuthorizeUrl,\n extractOAuthParams,\n isOAuthFlow,\n OAuthSearchSchema,\n type SecondFactorMethod,\n} from '#frontend/libs/oauth-search.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\nimport {\n resendVerificationMutationOptions,\n verifyEmailMutationOptions,\n} from '#frontend/queries/verify-email.ts';\n\nconst SearchSchema = z.object({\n ...OAuthSearchSchema.shape,\n token: z.string().default(''),\n email: z.string().default(''),\n});\n\nexport const Route = createFileRoute('/verify/email/')({\n component: VerifyEmail,\n errorComponent: RouteErrorFallback,\n validateSearch: SearchSchema,\n beforeLoad: async ({ context }) => {\n const config = await context.queryClient.ensureQueryData(\n appConfigQueryOptions,\n );\n const isPasswordAuthEnabled = config.auth.password.enabled;\n if (!isPasswordAuthEnabled) {\n throw redirect({ to: '/login' });\n }\n },\n loader: async ({ context }) => {\n await context.queryClient.ensureQueryData(appConfigQueryOptions);\n },\n});\n\ntype VerifyEmailFormValues = {\n token: string;\n};\n\nfunction VerifyEmail() {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n const { token: queryToken, email } = search;\n const [verified, setVerified] = useState(false);\n const [resendCooldown, setResendCooldown] = useState(false);\n const toast = TRToast.useToastManager();\n const { data: appConfig } = useSuspenseQuery(appConfigQueryOptions);\n\n const verifyEmailSchema = useMemo(\n () =>\n z.object({\n token: z.string().min(1, t('validation.token.required')),\n }),\n [t],\n );\n\n const verifyEmailMutation = useMutation({\n ...verifyEmailMutationOptions,\n onSuccess: async (data) => {\n const user = data.user;\n\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: user,\n });\n await tick();\n\n if (user.second_factor_required) {\n const available_2fa_methods: SecondFactorMethod[] = [];\n if (appConfig.auth.password.totp.enabled) {\n available_2fa_methods.push('totp');\n }\n if (appConfig.auth.passkey.enabled) {\n available_2fa_methods.push('passkey');\n }\n\n if (available_2fa_methods.length === 1) {\n const method = available_2fa_methods[0];\n if (method === 'totp') {\n return navigate({\n to: '/setup/totp',\n search: extractOAuthParams(search),\n });\n } else {\n return navigate({\n to: '/setup/passkey',\n search: extractOAuthParams(search),\n });\n }\n } else {\n return navigate({\n to: '/setup/2fa',\n search: extractOAuthParams(search),\n });\n }\n }\n\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n setVerified(true);\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n const resendVerificationMutation = useMutation({\n ...resendVerificationMutationOptions,\n onSuccess: () => {\n toast.add({ title: t('verifyEmail.resendSuccess'), type: 'success' });\n // Separate from the message: a short cooldown so the button cannot be\n // used to spam the address with mail.\n setResendCooldown(true);\n setTimeout(() => setResendCooldown(false), 5000);\n },\n });\n\n const {\n register,\n setError,\n handleSubmit,\n formState: { errors },\n } = useForm<VerifyEmailFormValues>({\n defaultValues: {\n token: queryToken || '',\n },\n resolver: standardSchemaResolver(verifyEmailSchema),\n });\n\n const onSubmit = async (values: VerifyEmailFormValues) => {\n try {\n await verifyEmailMutation.mutateAsync(values);\n } catch (_error) {\n setError('token', {\n type: 'manual',\n message: t('verifyEmail.error.invalidToken'),\n });\n }\n };\n\n const handleResend = async () => {\n if (!email) {\n return;\n }\n try {\n await resendVerificationMutation.mutateAsync({ email });\n } catch (_error) {}\n };\n\n if (verified) {\n return (\n <AuthLayout>\n {/*\n The testid stays on a wrapper: e2e treats \"the success alert\" as the\n whole terminal state, which is now the outcome block rather than a\n banner stacked above a header.\n */}\n <div data-testid=\"alert-success\">\n <AuthOutcome\n description={\n <>\n {t('verifyEmail.success.subtitle')}\n <br />\n {t('verifyEmail.success.description')}\n </>\n }\n icon={CircleCheckIcon}\n title={t('verifyEmail.success.title')}\n tone=\"success\"\n >\n <TRButton\n className=\"w-full\"\n data-testid=\"email-verify-go-profile\"\n intent=\"primary\"\n onClick={() => navigate({ to: '/profile' })}\n type=\"button\"\n uiSize=\"lg\"\n >\n {t('verifyEmail.success.goToProfile')}\n </TRButton>\n </AuthOutcome>\n </div>\n </AuthLayout>\n );\n }\n\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('verifyEmail.subtitle')}\n title={t('verifyEmail.title')}\n />\n\n {email && (\n <Alert icon={MailIcon} type=\"info\">\n <span className=\"flex flex-col gap-tinyrack-3xs text-left\">\n <TRText variant=\"body\" weight=\"strong\">\n {t('register.success.subtitle')}\n </TRText>\n <TRText color=\"muted\" variant=\"caption\">\n {t('register.success.description', { email })}\n </TRText>\n </span>\n </Alert>\n )}\n\n <form\n className=\"flex flex-col gap-tinyrack-lg\"\n onSubmit={handleSubmit(onSubmit)}\n >\n <AuthField\n error={errors.token}\n icon={KeyRoundIcon}\n label={t('verifyEmail.token.label')}\n placeholder={t('verifyEmail.token.placeholder')}\n {...register('token')}\n type=\"text\"\n />\n\n <TRButton\n className=\"w-full\"\n intent=\"primary\"\n loading={verifyEmailMutation.isPending}\n loadingLabel={t('verifyEmail.submitting')}\n type=\"submit\"\n uiSize=\"lg\"\n >\n {t('verifyEmail.submit')}\n </TRButton>\n </form>\n\n {email && (\n <div className=\"flex flex-col gap-tinyrack-md\">\n <LabeledSeparator />\n\n <TRButton\n appearance=\"ghost\"\n className=\"w-full\"\n data-testid=\"email-verify-resend\"\n disabled={resendVerificationMutation.isPending || resendCooldown}\n intent=\"neutral\"\n onClick={handleResend}\n type=\"button\"\n >\n {resendVerificationMutation.isPending ? (\n <>\n <TRSpinner uiSize=\"sm\" />\n {t('verifyEmail.resending')}\n </>\n ) : (\n t('verifyEmail.resend')\n )}\n </TRButton>\n </div>\n )}\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{i as e,n as t,q as n,s as r}from"./createLucideIcon-BPpAIXR_.js";import{Sn as i,wn as a}from"./config-DTQCUlfZ.js";import{L as o,R as s,z as c}from"./index-CS23Ozt6.js";import{t as l}from"./auth-layout-VtpryDof.js";import{n as u,t as d}from"./auth-footer-Cz5ivsDc.js";var f=n();function p(){let{t:n}=e(),p=a({from:`/error/`}),m=p.code||`UNKNOWN_ERROR`;return(0,f.jsxs)(l,{children:[(0,f.jsxs)(o,{description:p.message||n(`error.defaultMessage`),icon:s,title:n(`error.subtitle`),tone:`danger`,children:[(0,f.jsxs)(t,{color:`muted`,variant:`caption`,children:[n(`error.codeLabel`),` `,(0,f.jsx)(`span`,{className:`font-tinyrack-mono`,"data-testid":`error-code`,children:m})]}),(0,f.jsx)(c,{className:`w-full`,intent:`primary`,render:(0,f.jsx)(i,{to:`/login`}),uiSize:`lg`,children:n(`error.goToLogin`)}),(0,f.jsx)(r,{appearance:`ghost`,className:`w-full`,intent:`neutral`,onClick:()=>window.history.back(),type:`button`,children:n(`error.goBack`)})]}),(0,f.jsx)(d,{children:(0,f.jsx)(u,{link:(0,f.jsx)(`a`,{href:`mailto:support@example.com`,children:n(`error.footer.contactSupport`)}),text:n(`error.footer.needHelp`)})})]})}export{p as component};
2
+ //# sourceMappingURL=error-BMXxpgfR.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-2VPY5w8V.js","names":["Link","useSearch","TRButton","TRLinkButton","TRText","CircleAlertIcon","useTranslation","AuthFooter","AuthFooterLink","AuthOutcome","AuthLayout","ErrorPage","t","search","from","errorCode","code","errorMessage","message","window","history","back","component"],"sources":["../../../frontend/src/routes/error/index.tsx?tsr-split=component"],"sourcesContent":["import { createFileRoute, Link, useSearch } from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRLinkButton } from '@tinyrack/ui/components/link-button';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { CircleAlertIcon } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport {\n AuthFooter,\n AuthFooterLink,\n} from '#frontend/components/auth/auth-footer.tsx';\nimport { AuthOutcome } from '#frontend/components/auth/auth-outcome.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\n\nconst errorSearchSchema = z.object({\n code: z.string().optional(),\n message: z.string().optional(),\n});\n\nexport const Route = createFileRoute('/error/')({\n component: ErrorPage,\n validateSearch: errorSearchSchema,\n});\n\nfunction ErrorPage() {\n const { t } = useTranslation();\n const search = useSearch({ from: '/error/' });\n\n const errorCode = search.code || 'UNKNOWN_ERROR';\n const errorMessage = search.message || t('error.defaultMessage');\n\n return (\n <AuthLayout>\n {/*\n Nothing to do here but report what happened, so this is the shared\n terminal-state composition rather than an alert stacked on a header.\n */}\n <AuthOutcome\n description={errorMessage}\n icon={CircleAlertIcon}\n title={t('error.subtitle')}\n tone=\"danger\"\n >\n {/*\n Mono on the code itself, not the label: a font utility on `TRText`\n loses to the component's own per-variant `font-family` rule, and only\n the identifier needs fixed-width anyway.\n */}\n <TRText color=\"muted\" variant=\"caption\">\n {t('error.codeLabel')}{' '}\n <span className=\"font-tinyrack-mono\" data-testid=\"error-code\">\n {errorCode}\n </span>\n </TRText>\n\n <TRLinkButton\n className=\"w-full\"\n intent=\"primary\"\n render={<Link to=\"/login\" />}\n uiSize=\"lg\"\n >\n {t('error.goToLogin')}\n </TRLinkButton>\n <TRButton\n appearance=\"ghost\"\n className=\"w-full\"\n intent=\"neutral\"\n onClick={() => window.history.back()}\n type=\"button\"\n >\n {t('error.goBack')}\n </TRButton>\n </AuthOutcome>\n\n <AuthFooter>\n <AuthFooterLink\n link={\n <a href=\"mailto:support@example.com\">\n {t('error.footer.contactSupport')}\n </a>\n }\n text={t('error.footer.needHelp')}\n />\n </AuthFooter>\n </AuthLayout>\n );\n}\n"],"mappings":"4TAwBA,SAASW,GAAY,CACnB,GAAM,CAAEC,KAAMN,EAAe,EACvBO,EAASZ,EAAU,CAAEa,KAAM,SAAU,CAAC,EAEtCC,EAAYF,EAAOG,MAAQ,gBAGjC,OACE,EAAA,EAAA,KAAA,CAAC,EAAD,CAAA,SAAA,EAKE,EAAA,EAAA,KAAA,CAAC,EAAD,CACE,YATeH,EAAOK,SAAWN,EAAE,sBAAsB,EAUzD,KAAMP,EACN,MAAOO,EAAE,gBAAgB,EACzB,KAAK,kBAJP,EAWE,EAAA,EAAA,KAAA,CAAC,EAAD,CAAQ,MAAM,QAAQ,QAAQ,mBAA9B,CACGA,EAAE,iBAAiB,EAAG,KACvB,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,qBAAqB,cAAY,sBAC9CG,CACG,CAAA,CACA,KAER,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,UAAU,SACV,OAAO,UACP,QAAQ,EAAA,EAAA,IAAA,CAAC,EAAD,CAAM,GAAG,QAAQ,CAAA,EACzB,OAAO,cAENH,EAAE,iBAAiB,CACR,CAAA,GACd,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,WAAW,QACX,UAAU,SACV,OAAO,UACP,YAAeO,OAAOC,QAAQC,KAAK,EACnC,KAAK,kBAEJT,EAAE,cAAc,CACT,CAAA,CACC,KAEb,EAAA,EAAA,IAAA,CAAC,EAAD,CAAA,UACE,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,MACE,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,KAAK,sCACLA,EAAE,6BAA6B,CAC/B,CAAA,EAEL,KAAMA,EAAE,uBAAuB,CAAE,CAAA,CAEzB,CAAA,CACF,CAAA,CAAA,CAEhB"}
1
+ {"version":3,"file":"error-BMXxpgfR.js","names":["Link","useSearch","TRButton","TRLinkButton","TRText","CircleAlertIcon","useTranslation","AuthFooter","AuthFooterLink","AuthOutcome","AuthLayout","ErrorPage","t","search","from","errorCode","code","errorMessage","message","window","history","back","component"],"sources":["../../../frontend/src/routes/error/index.tsx?tsr-split=component"],"sourcesContent":["import { createFileRoute, Link, useSearch } from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRLinkButton } from '@tinyrack/ui/components/link-button';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { CircleAlertIcon } from 'lucide-react';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport {\n AuthFooter,\n AuthFooterLink,\n} from '#frontend/components/auth/auth-footer.tsx';\nimport { AuthOutcome } from '#frontend/components/auth/auth-outcome.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\n\nconst errorSearchSchema = z.object({\n code: z.string().optional(),\n message: z.string().optional(),\n});\n\nexport const Route = createFileRoute('/error/')({\n component: ErrorPage,\n validateSearch: errorSearchSchema,\n});\n\nfunction ErrorPage() {\n const { t } = useTranslation();\n const search = useSearch({ from: '/error/' });\n\n const errorCode = search.code || 'UNKNOWN_ERROR';\n const errorMessage = search.message || t('error.defaultMessage');\n\n return (\n <AuthLayout>\n {/*\n Nothing to do here but report what happened, so this is the shared\n terminal-state composition rather than an alert stacked on a header.\n */}\n <AuthOutcome\n description={errorMessage}\n icon={CircleAlertIcon}\n title={t('error.subtitle')}\n tone=\"danger\"\n >\n {/*\n Mono on the code itself, not the label: a font utility on `TRText`\n loses to the component's own per-variant `font-family` rule, and only\n the identifier needs fixed-width anyway.\n */}\n <TRText color=\"muted\" variant=\"caption\">\n {t('error.codeLabel')}{' '}\n <span className=\"font-tinyrack-mono\" data-testid=\"error-code\">\n {errorCode}\n </span>\n </TRText>\n\n <TRLinkButton\n className=\"w-full\"\n intent=\"primary\"\n render={<Link to=\"/login\" />}\n uiSize=\"lg\"\n >\n {t('error.goToLogin')}\n </TRLinkButton>\n <TRButton\n appearance=\"ghost\"\n className=\"w-full\"\n intent=\"neutral\"\n onClick={() => window.history.back()}\n type=\"button\"\n >\n {t('error.goBack')}\n </TRButton>\n </AuthOutcome>\n\n <AuthFooter>\n <AuthFooterLink\n link={\n <a href=\"mailto:support@example.com\">\n {t('error.footer.contactSupport')}\n </a>\n }\n text={t('error.footer.needHelp')}\n />\n </AuthFooter>\n </AuthLayout>\n );\n}\n"],"mappings":"6RAwBA,SAASW,GAAY,CACnB,GAAM,CAAEC,KAAMN,EAAe,EACvBO,EAASZ,EAAU,CAAEa,KAAM,SAAU,CAAC,EAEtCC,EAAYF,EAAOG,MAAQ,gBAGjC,OACE,EAAA,EAAA,KAAA,CAAC,EAAD,CAAA,SAAA,EAKE,EAAA,EAAA,KAAA,CAAC,EAAD,CACE,YATeH,EAAOK,SAAWN,EAAE,sBAAsB,EAUzD,KAAMP,EACN,MAAOO,EAAE,gBAAgB,EACzB,KAAK,kBAJP,EAWE,EAAA,EAAA,KAAA,CAAC,EAAD,CAAQ,MAAM,QAAQ,QAAQ,mBAA9B,CACGA,EAAE,iBAAiB,EAAG,KACvB,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,qBAAqB,cAAY,sBAC9CG,CACG,CAAA,CACA,KAER,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,UAAU,SACV,OAAO,UACP,QAAQ,EAAA,EAAA,IAAA,CAAC,EAAD,CAAM,GAAG,QAAQ,CAAA,EACzB,OAAO,cAENH,EAAE,iBAAiB,CACR,CAAA,GACd,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,WAAW,QACX,UAAU,SACV,OAAO,UACP,YAAeO,OAAOC,QAAQC,KAAK,EACnC,KAAK,kBAEJT,EAAE,cAAc,CACT,CAAA,CACC,KAEb,EAAA,EAAA,IAAA,CAAC,EAAD,CAAA,UACE,EAAA,EAAA,IAAA,CAAC,EAAD,CACE,MACE,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,KAAK,sCACLA,EAAE,6BAA6B,CAC/B,CAAA,EAEL,KAAMA,EAAE,uBAAuB,CAAE,CAAA,CAEzB,CAAA,CACF,CAAA,CAAA,CAEhB"}
@@ -1,3 +1,3 @@
1
- import{G as e,J as t,S as n,Z as r,_ as i,b as a,c as o,d as s,q as c,v as l,x as u,y as d}from"./createLucideIcon-BPpAIXR_.js";import{P as f,Xt as p,Y as m,_t as h,bn as g,pt as _,un as v,xn as y}from"./config-v3-oy_ef.js";import{C as b,O as x,S,T as C,X as w,_ as T,b as E,d as D,g as O,v as k,w as A,x as j,y as M}from"./shell-header-bar-C9AB5lT-.js";import{n as N,t as P}from"./FieldItemContext-BeNfhOsU.js";var F=c(),I=r(t(),1),L=I.createContext(void 0);function R(e=!1){let t=I.useContext(L);if(!t&&!e)throw Error(l(86));return t}var z=function(e){let t=x(),r=e.controlId===void 0?t:e.controlId,[i,a]=I.useState(r),[o,s]=I.useState(e.labelId),[c,l]=I.useState([]),d=n(()=>new Map),{messageIds:f}=M(),p=u((e,t)=>{let n=d.current;if(t===void 0){n.delete(e);return}n.set(e,t),a(e=>{if(n.size===0)return;let t;for(let r of n.values()){if(e!==void 0&&r===e)return e;t===void 0&&(t=r)}return t})}),m=I.useCallback(e=>{let t=e[`aria-describedby`]?e[`aria-describedby`].split(` `):[];return t.push(...f,...c),{...e,"aria-describedby":Array.from(new Set(t)).join(` `)||void 0}},[f,c]),h=I.useMemo(()=>({controlId:i,registerControlId:p,labelId:o,setLabelId:s,messageIds:c,setMessageIds:l,getDescriptionProps:m}),[i,p,o,s,c,l,m]);return(0,F.jsx)(k.Provider,{value:h,children:e.children})};function B(e,t){return{...e,state:{...e.state,valid:!t&&e.state.valid}}}var V=Object.keys(b);function H(e,t){return e.matches(`:disabled`)?!1:!t||e.form===t?!0:e.form===null&&!e.hasAttribute(`form`)}function U(e,t){let n=null;for(let r of e.keys())if(H(r,t)){if(!r.validity.valid)return r;n??=r}return n}function W(e,t){for(let e of t.keys())e.setCustomValidity(``);e?.setCustomValidity(``)}function ee(e){let{elementRef:t,formRef:r}=O(),{setValidityData:i,validate:a,validityData:o,validationDebounceTime:c,invalid:l,markedDirtyRef:f,state:p,shouldValidateOnChange:m,registeredFieldIdRef:h}=e,{controlId:g,getDescriptionProps:_}=M(),y=v(),x=I.useRef(null),S=n(()=>new Map).current,C=I.useRef(0),w=I.useCallback((e,t)=>(S.set(e,t),()=>{S.delete(e)}),[S]),T=u(()=>{let e=U(S,t.current);return e&&S.get(e)?.controlRef.current||null}),E=u(async(e,n=!1)=>{C.current+=1;let s=C.current;function c(e,t=l){let n=h.current??g;if(n==null)return;let i=r.current.fields.get(n);if(!i)return;let a=B(e,t);r.current.fields.set(n,{...i,validityData:a})}function u(t,n){let r={value:e,state:{...b,valid:!0},error:``,errors:[],initialValue:o.initialValue};W(t,S),c(r,n),i(r)}let d=S.size>0?U(S,t.current):x.current;if(n){if(p.valid!==!1||!d)return;let e=d.validity;if(!e.valueMissing){u(d,!1);return}for(let t of V)if(t!==`valid`&&t!==`valueMissing`&&t!==`customError`&&e[t])return}function _(e){let t=V.reduce((t,n)=>(t[n]=e.validity[n],t),{}),n=!1;for(let e of V)if(e!==`valid`){if(e===`valueMissing`&&t[e])n=!0;else if(t[e])return t}return n&&!f.current&&(t.valid=!0,t.valueMissing=!1),t}y.clear();let v=null,w=[],T=d?_(d):{...b,valid:!0},E,D=m();if(d&&d.validationMessage&&!D)E=d.validationMessage,w=[d.validationMessage];else{let t=Array.from(r.current.fields.values()).reduce((e,t)=>(t.name&&(e[t.name]=t.getValue()),e),{}),n=a(e,t);if(typeof n==`object`&&n&&`then`in n){if(v=await n,s!==C.current)return}else v=n;v===null?D&&(W(d,S),T.customError=!1,d&&d.validationMessage?(E=d.validationMessage,w=[d.validationMessage]):(!d||d.validity.valid)&&!T.valid&&(T.valid=!0)):(T.valid=!1,T.customError=!0,Array.isArray(v)?(w=v,d?.setCustomValidity(v.join(`
1
+ import{G as e,J as t,S as n,Z as r,_ as i,b as a,c as o,d as s,q as c,v as l,x as u,y as d}from"./createLucideIcon-BPpAIXR_.js";import{P as f,Xt as p,Y as m,_t as h,bn as g,pt as _,un as v,xn as y}from"./config-DTQCUlfZ.js";import{C as b,O as x,S,T as C,X as w,_ as T,b as E,d as D,g as O,v as k,w as A,x as j,y as M}from"./shell-header-bar-CjG5qvPg.js";import{n as N,t as P}from"./FieldItemContext-BeNfhOsU.js";var F=c(),I=r(t(),1),L=I.createContext(void 0);function R(e=!1){let t=I.useContext(L);if(!t&&!e)throw Error(l(86));return t}var z=function(e){let t=x(),r=e.controlId===void 0?t:e.controlId,[i,a]=I.useState(r),[o,s]=I.useState(e.labelId),[c,l]=I.useState([]),d=n(()=>new Map),{messageIds:f}=M(),p=u((e,t)=>{let n=d.current;if(t===void 0){n.delete(e);return}n.set(e,t),a(e=>{if(n.size===0)return;let t;for(let r of n.values()){if(e!==void 0&&r===e)return e;t===void 0&&(t=r)}return t})}),m=I.useCallback(e=>{let t=e[`aria-describedby`]?e[`aria-describedby`].split(` `):[];return t.push(...f,...c),{...e,"aria-describedby":Array.from(new Set(t)).join(` `)||void 0}},[f,c]),h=I.useMemo(()=>({controlId:i,registerControlId:p,labelId:o,setLabelId:s,messageIds:c,setMessageIds:l,getDescriptionProps:m}),[i,p,o,s,c,l,m]);return(0,F.jsx)(k.Provider,{value:h,children:e.children})};function B(e,t){return{...e,state:{...e.state,valid:!t&&e.state.valid}}}var V=Object.keys(b);function H(e,t){return e.matches(`:disabled`)?!1:!t||e.form===t?!0:e.form===null&&!e.hasAttribute(`form`)}function U(e,t){let n=null;for(let r of e.keys())if(H(r,t)){if(!r.validity.valid)return r;n??=r}return n}function W(e,t){for(let e of t.keys())e.setCustomValidity(``);e?.setCustomValidity(``)}function ee(e){let{elementRef:t,formRef:r}=O(),{setValidityData:i,validate:a,validityData:o,validationDebounceTime:c,invalid:l,markedDirtyRef:f,state:p,shouldValidateOnChange:m,registeredFieldIdRef:h}=e,{controlId:g,getDescriptionProps:_}=M(),y=v(),x=I.useRef(null),S=n(()=>new Map).current,C=I.useRef(0),w=I.useCallback((e,t)=>(S.set(e,t),()=>{S.delete(e)}),[S]),T=u(()=>{let e=U(S,t.current);return e&&S.get(e)?.controlRef.current||null}),E=u(async(e,n=!1)=>{C.current+=1;let s=C.current;function c(e,t=l){let n=h.current??g;if(n==null)return;let i=r.current.fields.get(n);if(!i)return;let a=B(e,t);r.current.fields.set(n,{...i,validityData:a})}function u(t,n){let r={value:e,state:{...b,valid:!0},error:``,errors:[],initialValue:o.initialValue};W(t,S),c(r,n),i(r)}let d=S.size>0?U(S,t.current):x.current;if(n){if(p.valid!==!1||!d)return;let e=d.validity;if(!e.valueMissing){u(d,!1);return}for(let t of V)if(t!==`valid`&&t!==`valueMissing`&&t!==`customError`&&e[t])return}function _(e){let t=V.reduce((t,n)=>(t[n]=e.validity[n],t),{}),n=!1;for(let e of V)if(e!==`valid`){if(e===`valueMissing`&&t[e])n=!0;else if(t[e])return t}return n&&!f.current&&(t.valid=!0,t.valueMissing=!1),t}y.clear();let v=null,w=[],T=d?_(d):{...b,valid:!0},E,D=m();if(d&&d.validationMessage&&!D)E=d.validationMessage,w=[d.validationMessage];else{let t=Array.from(r.current.fields.values()).reduce((e,t)=>(t.name&&(e[t.name]=t.getValue()),e),{}),n=a(e,t);if(typeof n==`object`&&n&&`then`in n){if(v=await n,s!==C.current)return}else v=n;v===null?D&&(W(d,S),T.customError=!1,d&&d.validationMessage?(E=d.validationMessage,w=[d.validationMessage]):(!d||d.validity.valid)&&!T.valid&&(T.valid=!0)):(T.valid=!1,T.customError=!0,Array.isArray(v)?(w=v,d?.setCustomValidity(v.join(`
2
2
  `))):v&&(w=[v],d?.setCustomValidity(v)))}let O={value:e,state:T,error:E??(Array.isArray(v)?v[0]:v??``),errors:w,initialValue:o.initialValue};c(O),i(O)}),D=u(e=>{y.clear();let t=m();t&&e!==``&&c?(C.current+=1,y.start(c,()=>{E(e)})):E(e,!t)}),k=I.useCallback((e,t={})=>d(_(t),p.valid===!1&&!p.disabled&&!e?{"aria-invalid":!0}:s),[_,p.disabled,p.valid]);return I.useMemo(()=>({getValidationProps:k,inputRef:x,registeredInputs:S,registerInput:w,getInputControl:T,commit:E,change:D}),[k,S,w,T,E,D])}function te(e){let{commit:t,invalid:n,markedDirtyRef:r,name:i,setRegisteredFieldName:o,registeredFieldIdRef:s,setValidityData:c,validityData:l}=e,{formRef:d}=O(),f=I.useRef(null),p=I.useRef(null),m=I.useRef(!1),h=u(()=>{let e=p.current;if(e)return e.getValue?e.getValue():e.value});function g(e){return e.value===void 0?h():e.value}let _=u(()=>{let e=p.current;if(r.current=!0,!e){t(l.value);return}t(g(e))});function v(){let e=p.current;!e||!e.id||d.current.fields.set(e.id,{getValue:h,name:i??e.name,controlRef:e.controlRef,validityData:B(l,n),validate:_})}function y(e=p.current?.id){e&&d.current.fields.delete(e)}function b(e){if(m.current)return;m.current=!0;let t=g(e);c(e=>e.initialValue===t?e:{...e,initialValue:t})}return a(()=>{let e=p.current;!e||!e.id||(o(i?void 0:e.name),d.current.fields.set(e.id,{getValue:h,name:i??e.name,controlRef:e.controlRef,validityData:B(l,n),validate:_}))},[d,h,n,i,o,_,l]),a(()=>{let e=d.current.fields;return()=>{let t=p.current?.id;t&&e.delete(t)}},[d]),[_,u((e,t)=>{if(!t){f.current===e&&(f.current=null,y(),p.current=null,o(void 0),s.current=void 0);return}let n=p.current?.id;f.current=e,p.current=t,i||o(t.name),s.current=t.id,n&&n!==t.id&&y(n),b(t),v()})]}var G=I.forwardRef(function(e,t){let{errors:n,validationMode:r,submitAttemptedRef:i}=O(),{render:s,className:c,validate:l,validationDebounceTime:d=0,validationMode:f=r,name:p,disabled:m=!1,invalid:h,dirty:g,touched:_,actionsRef:v,style:y,...x}=e,S=R(!0)?.disabled,C=u(l||(()=>null)),w=S||m,[T,E]=I.useState(!1),[D,k]=I.useState(!1),[M,N]=I.useState(!1),[P,L]=I.useState(!1),z=g??D,B=_??T,V=I.useRef(z),H=I.useRef(void 0),[U,W]=I.useState(),G=p??U;a(()=>{g!==void 0&&(V.current=g)},[g]);let K=u(e=>{g===void 0&&(e&&(V.current=!0),k(e))}),ne=u(e=>{_===void 0&&E(e)}),q=u(()=>f===`onChange`||f===`onSubmit`&&i.current),J=G&&Object.hasOwn(n,G)?n[G]:null,re=!!(Array.isArray(J)?J.length:J),Y=h===!0||re,[X,Z]=I.useState({state:b,error:``,errors:[],value:null,initialValue:null}),ie=!Y&&(w?null:X.state.valid),Q=I.useMemo(()=>({disabled:w,touched:B,dirty:z,valid:ie,filled:M,focused:P}),[w,B,z,ie,M,P]),$=ee({setValidityData:Z,validate:C,validityData:X,validationDebounceTime:d,invalid:Y,markedDirtyRef:V,state:Q,shouldValidateOnChange:q,registeredFieldIdRef:H}),[ae,oe]=te({commit:$.commit,invalid:Y,markedDirtyRef:V,name:p,setRegisteredFieldName:W,registeredFieldIdRef:H,setValidityData:Z,validityData:X});I.useImperativeHandle(v,()=>({validate:ae}),[ae]);let se=I.useMemo(()=>({invalid:Y,name:G,validityData:X,setValidityData:Z,disabled:w,setTouched:ne,setDirty:K,setFilled:N,setFocused:L,validationMode:f,shouldValidateOnChange:q,state:Q,registerFieldControl:oe,validation:$}),[Y,G,X,w,ne,K,N,L,f,q,Q,oe,$]),ce=o(`div`,e,{ref:t,state:Q,props:x,stateAttributesMapping:A});return(0,F.jsx)(j.Provider,{value:se,children:ce})}),K=I.forwardRef(function(e,t){return(0,F.jsx)(z,{children:(0,F.jsx)(G,{...e,ref:t})})}),ne=I.forwardRef(function(e,t){let{render:n,className:r,style:i,id:a,nativeLabel:s=!0,...c}=e,l=S(!1),u=N(),{labelId:d}=M(),f={...l.state,disabled:l.disabled||u.disabled},p=I.useRef(null),m=D({id:d??a,native:s});return o(`label`,e,{ref:[t,p],state:f,props:[m,c],stateAttributesMapping:A})}),q={...A,...h},J=I.forwardRef(function(e,t){let{render:n,id:r,className:i,match:s,style:c,...l}=e,u=x(r),{validityData:d,state:f,name:p}=S(!1),{setMessageIds:m}=M(),{errors:h}=O(),g=p&&Object.hasOwn(h,p)?h[p]:null,v=!!(Array.isArray(g)?g.length:g),y=typeof s==`string`,b=!1;b=s===!0?!0:f.disabled?!1:y?!!d.state[s]:v||d.state.valid===!1;let{mounted:C,transitionStatus:T,setMounted:E}=w(b);a(()=>{if(!(!b||!u))return m(e=>e.concat(u)),()=>{m(e=>e.filter(e=>e!==u))}},[b,u,m]);let D=I.useRef(null),[k,A]=I.useState(null),[j,N]=I.useState(null),P=d.error;!y&&v?P=g:d.errors.length>1&&(P=d.errors);let L=P;Array.isArray(P)&&(L=P.length>1?(0,F.jsx)(`ul`,{children:P.map(e=>(0,F.jsx)(`li`,{children:e},e))}):P[0]);let R=Array.isArray(P)?JSON.stringify(P):P;b&&R!==j&&(N(R),A(L)),_({open:b,ref:D,onComplete(){b||E(!1)}});let z={...f,transitionStatus:T},B=o(`div`,e,{ref:[t,D],state:z,props:[{id:u,children:b?L:k},l],stateAttributesMapping:q,enabled:C});return C?B:null}),re=I.forwardRef(function(e,t){let{render:n,id:r,className:i,style:s,...c}=e,l=x(r),u=S(!1),d=N(),{setMessageIds:f}=M(),p={...u.state,disabled:u.disabled||d.disabled};return a(()=>{if(l)return f(e=>e.concat(l)),()=>{f(e=>e.filter(e=>e!==l))}},[l,f]),o(`p`,e,{ref:t,state:p,props:[{id:l},c],stateAttributesMapping:A})}),Y=I.forwardRef(function(e,t){let{render:n,className:r,id:s,name:c,value:l,disabled:d=!1,onValueChange:h,defaultValue:g,autoFocus:_=!1,style:v,...y}=e,{state:b,name:x,disabled:w,setTouched:D,setDirty:k,validityData:j,setFocused:N,setFilled:P,validationMode:F,validation:L}=S(),{clearErrors:R}=O(),z=w||d,B=x??c,V={...b,disabled:z},{labelId:H}=M(),U=T({id:s});a(()=>{let e=l!=null;L.inputRef.current?.value||e&&l!==``?P(!0):e&&l===``&&P(!1)},[L.inputRef,P,l]);let W=I.useRef(null);a(()=>{_&&W.current===p(i(W.current))&&N(!0)},[_,N]);let[ee]=C({controlled:l,default:g,name:`FieldControl`,state:`value`}),te=l!==void 0,G=te?ee:void 0,K=u(()=>L.inputRef.current?.value);return E(L.inputRef,U,G,K,!z,c),o(`input`,e,{ref:[t,W],state:V,props:[{id:U,disabled:z,name:B,ref:L.inputRef,"aria-labelledby":H,autoFocus:_,...te?{value:G}:{defaultValue:g},onChange(e){let t=e.currentTarget.value;h?.(t,f(m,e.nativeEvent)),k(t!==(j.initialValue??``)),P(t!==``),e.nativeEvent.defaultPrevented||(R(B),L.change(t))},onFocus(){N(!0)},onBlur(e){D(!0),N(!1),F===`onBlur`&&L.commit(e.currentTarget.value)},onKeyDown(e){e.currentTarget.tagName===`INPUT`&&e.key===`Enter`&&(D(!0),L.commit(e.currentTarget.value))}},y,e=>L.getValidationProps(z,e)],stateAttributesMapping:A})}),X=function(e){let{children:t}=e,{validityData:n,invalid:r}=S(!1),i=I.useMemo(()=>B(n,r),[n,r]),{transitionStatus:a}=w(i.state.valid===!1),o=I.useMemo(()=>({...i,validity:i.state,transitionStatus:a}),[i,a]);return(0,F.jsx)(I.Fragment,{children:t(o)})},Z=I.forwardRef(function(e,t){let{render:n,className:r,style:i,disabled:a=!1,...s}=e,{state:c,disabled:l}=S(!1),u=l||a,d={...c,disabled:u},f=I.useMemo(()=>({disabled:u}),[u]),p=o(`div`,e,{ref:t,state:d,props:s,stateAttributesMapping:A});return(0,F.jsx)(z,{children:(0,F.jsx)(P.Provider,{value:f,children:p})})});function ie({className:t,uiSize:n=`md`,...r}){return y(),(0,F.jsx)(Y,{...r,className:e(`tr-field-control`,t),"data-ui-size":n})}var Q=g(re,`tr-field-description`),$=g(J,`tr-field-error`),ae=g(Z,`tr-field-item`),oe=g(ne,`tr-label`);function se({className:t,...n}){return(0,F.jsx)(K,{...n,className:e(`tr-field`,t)})}var ce={Root:se,Label:oe,Error:$,Description:Q,Control:ie,Validity:g(X,`tr-field-validity`),Item:ae};export{Y as n,ce as t};
3
- //# sourceMappingURL=field-DDt6F9TZ.js.map
3
+ //# sourceMappingURL=field-Cn4H1CUj.js.map