@stackframe/stack 2.8.12 → 2.8.17

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 (248) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/components/api-key-dialogs.js +5 -4
  3. package/dist/components/api-key-dialogs.js.map +1 -1
  4. package/dist/components/credential-sign-in.js +4 -4
  5. package/dist/components/credential-sign-up.js +3 -3
  6. package/dist/components/elements/maybe-full-page.js +1 -1
  7. package/dist/components/elements/sidebar-layout.js +1 -1
  8. package/dist/components/magic-link-sign-in.js +3 -3
  9. package/dist/components/message-cards/known-error-message-card.js +2 -2
  10. package/dist/components/message-cards/message-card.js +1 -1
  11. package/dist/components/message-cards/predefined-message-card.js +3 -3
  12. package/dist/components/oauth-button-group.js +2 -2
  13. package/dist/components/oauth-button.js +27 -16
  14. package/dist/components/oauth-button.js.map +1 -1
  15. package/dist/components/passkey-button.js +2 -2
  16. package/dist/components/profile-image-editor.js +87 -34
  17. package/dist/components/profile-image-editor.js.map +1 -1
  18. package/dist/components/selected-team-switcher.js +60 -14
  19. package/dist/components/selected-team-switcher.js.map +1 -1
  20. package/dist/components/team-icon.js +4 -0
  21. package/dist/components/team-icon.js.map +1 -1
  22. package/dist/components/{iframe-preventer.js → use-in-iframe.js} +9 -19
  23. package/dist/components/use-in-iframe.js.map +1 -0
  24. package/dist/components/user-button.js +41 -8
  25. package/dist/components/user-button.js.map +1 -1
  26. package/dist/components-page/account-settings/active-sessions/active-sessions-page.js +57 -12
  27. package/dist/components-page/account-settings/active-sessions/active-sessions-page.js.map +1 -1
  28. package/dist/components-page/account-settings/api-keys/api-keys-page.js +100 -12
  29. package/dist/components-page/account-settings/api-keys/api-keys-page.js.map +1 -1
  30. package/dist/components-page/account-settings/editable-text.js +1 -1
  31. package/dist/components-page/account-settings/email-and-auth/email-and-auth-page.js +12 -12
  32. package/dist/components-page/account-settings/email-and-auth/email-and-auth-page.js.map +1 -1
  33. package/dist/components-page/account-settings/email-and-auth/emails-section.js +14 -5
  34. package/dist/components-page/account-settings/email-and-auth/emails-section.js.map +1 -1
  35. package/dist/components-page/account-settings/email-and-auth/mfa-section.js +18 -5
  36. package/dist/components-page/account-settings/email-and-auth/mfa-section.js.map +1 -1
  37. package/dist/components-page/account-settings/email-and-auth/otp-section.js +18 -5
  38. package/dist/components-page/account-settings/email-and-auth/otp-section.js.map +1 -1
  39. package/dist/components-page/account-settings/email-and-auth/passkey-section.js +19 -6
  40. package/dist/components-page/account-settings/email-and-auth/passkey-section.js.map +1 -1
  41. package/dist/components-page/account-settings/email-and-auth/password-section.js +20 -7
  42. package/dist/components-page/account-settings/email-and-auth/password-section.js.map +1 -1
  43. package/dist/components-page/account-settings/notifications/notifications-page.js +59 -0
  44. package/dist/components-page/account-settings/notifications/notifications-page.js.map +1 -0
  45. package/dist/components-page/account-settings/profile-page/profile-page.js +18 -8
  46. package/dist/components-page/account-settings/profile-page/profile-page.js.map +1 -1
  47. package/dist/components-page/account-settings/settings/delete-account-section.js +19 -10
  48. package/dist/components-page/account-settings/settings/delete-account-section.js.map +1 -1
  49. package/dist/components-page/account-settings/settings/settings-page.js +6 -6
  50. package/dist/components-page/account-settings/settings/settings-page.js.map +1 -1
  51. package/dist/components-page/account-settings/settings/sign-out-section.js +15 -6
  52. package/dist/components-page/account-settings/settings/sign-out-section.js.map +1 -1
  53. package/dist/components-page/account-settings/teams/leave-team-section.js +3 -3
  54. package/dist/components-page/account-settings/teams/team-api-keys-section.js +5 -5
  55. package/dist/components-page/account-settings/teams/team-creation-page.js +19 -10
  56. package/dist/components-page/account-settings/teams/team-creation-page.js.map +1 -1
  57. package/dist/components-page/account-settings/teams/team-display-name-section.js +4 -4
  58. package/dist/components-page/account-settings/teams/team-member-invitation-section.js +4 -4
  59. package/dist/components-page/account-settings/teams/team-member-list-section.js +3 -3
  60. package/dist/components-page/account-settings/teams/team-page.js +8 -8
  61. package/dist/components-page/account-settings/teams/team-profile-image-section.js +4 -4
  62. package/dist/components-page/account-settings/teams/team-profile-user-section.js +4 -4
  63. package/dist/components-page/account-settings.js +43 -21
  64. package/dist/components-page/account-settings.js.map +1 -1
  65. package/dist/components-page/auth-page.js +11 -12
  66. package/dist/components-page/auth-page.js.map +1 -1
  67. package/dist/components-page/cli-auth-confirm.js +3 -3
  68. package/dist/components-page/email-verification.js +3 -3
  69. package/dist/components-page/error-page.js +6 -6
  70. package/dist/components-page/error-page.js.map +1 -1
  71. package/dist/components-page/forgot-password.js +6 -6
  72. package/dist/components-page/magic-link-callback.js +4 -4
  73. package/dist/components-page/mfa.js +190 -0
  74. package/dist/components-page/mfa.js.map +1 -0
  75. package/dist/components-page/oauth-callback.js +4 -4
  76. package/dist/components-page/password-reset.js +6 -6
  77. package/dist/components-page/sign-in.js +3 -2
  78. package/dist/components-page/sign-in.js.map +1 -1
  79. package/dist/components-page/sign-out.js +2 -2
  80. package/dist/components-page/sign-up.js +1 -1
  81. package/dist/components-page/stack-handler.js +25 -14
  82. package/dist/components-page/stack-handler.js.map +1 -1
  83. package/dist/components-page/team-creation.js +4 -4
  84. package/dist/components-page/team-invitation.js +3 -3
  85. package/dist/esm/components/api-key-dialogs.js +5 -4
  86. package/dist/esm/components/api-key-dialogs.js.map +1 -1
  87. package/dist/esm/components/credential-sign-in.js +4 -4
  88. package/dist/esm/components/credential-sign-up.js +3 -3
  89. package/dist/esm/components/elements/maybe-full-page.js +1 -1
  90. package/dist/esm/components/elements/sidebar-layout.js +1 -1
  91. package/dist/esm/components/magic-link-sign-in.js +3 -3
  92. package/dist/esm/components/message-cards/known-error-message-card.js +2 -2
  93. package/dist/esm/components/message-cards/message-card.js +1 -1
  94. package/dist/esm/components/message-cards/predefined-message-card.js +3 -3
  95. package/dist/esm/components/oauth-button-group.js +2 -2
  96. package/dist/esm/components/oauth-button.js +28 -17
  97. package/dist/esm/components/oauth-button.js.map +1 -1
  98. package/dist/esm/components/passkey-button.js +2 -2
  99. package/dist/esm/components/profile-image-editor.js +86 -34
  100. package/dist/esm/components/profile-image-editor.js.map +1 -1
  101. package/dist/esm/components/selected-team-switcher.js +60 -14
  102. package/dist/esm/components/selected-team-switcher.js.map +1 -1
  103. package/dist/esm/components/team-icon.js +4 -0
  104. package/dist/esm/components/team-icon.js.map +1 -1
  105. package/dist/esm/components/use-in-iframe.js +18 -0
  106. package/dist/esm/components/use-in-iframe.js.map +1 -0
  107. package/dist/esm/components/user-button.js +41 -8
  108. package/dist/esm/components/user-button.js.map +1 -1
  109. package/dist/esm/components-page/account-settings/active-sessions/active-sessions-page.js +57 -12
  110. package/dist/esm/components-page/account-settings/active-sessions/active-sessions-page.js.map +1 -1
  111. package/dist/esm/components-page/account-settings/api-keys/api-keys-page.js +100 -12
  112. package/dist/esm/components-page/account-settings/api-keys/api-keys-page.js.map +1 -1
  113. package/dist/esm/components-page/account-settings/editable-text.js +1 -1
  114. package/dist/esm/components-page/account-settings/email-and-auth/email-and-auth-page.js +12 -12
  115. package/dist/esm/components-page/account-settings/email-and-auth/email-and-auth-page.js.map +1 -1
  116. package/dist/esm/components-page/account-settings/email-and-auth/emails-section.js +14 -5
  117. package/dist/esm/components-page/account-settings/email-and-auth/emails-section.js.map +1 -1
  118. package/dist/esm/components-page/account-settings/email-and-auth/mfa-section.js +18 -5
  119. package/dist/esm/components-page/account-settings/email-and-auth/mfa-section.js.map +1 -1
  120. package/dist/esm/components-page/account-settings/email-and-auth/otp-section.js +18 -5
  121. package/dist/esm/components-page/account-settings/email-and-auth/otp-section.js.map +1 -1
  122. package/dist/esm/components-page/account-settings/email-and-auth/passkey-section.js +19 -6
  123. package/dist/esm/components-page/account-settings/email-and-auth/passkey-section.js.map +1 -1
  124. package/dist/esm/components-page/account-settings/email-and-auth/password-section.js +20 -7
  125. package/dist/esm/components-page/account-settings/email-and-auth/password-section.js.map +1 -1
  126. package/dist/esm/components-page/account-settings/notifications/notifications-page.js +34 -0
  127. package/dist/esm/components-page/account-settings/notifications/notifications-page.js.map +1 -0
  128. package/dist/esm/components-page/account-settings/profile-page/profile-page.js +18 -8
  129. package/dist/esm/components-page/account-settings/profile-page/profile-page.js.map +1 -1
  130. package/dist/esm/components-page/account-settings/settings/delete-account-section.js +19 -10
  131. package/dist/esm/components-page/account-settings/settings/delete-account-section.js.map +1 -1
  132. package/dist/esm/components-page/account-settings/settings/settings-page.js +6 -6
  133. package/dist/esm/components-page/account-settings/settings/settings-page.js.map +1 -1
  134. package/dist/esm/components-page/account-settings/settings/sign-out-section.js +15 -6
  135. package/dist/esm/components-page/account-settings/settings/sign-out-section.js.map +1 -1
  136. package/dist/esm/components-page/account-settings/teams/leave-team-section.js +3 -3
  137. package/dist/esm/components-page/account-settings/teams/team-api-keys-section.js +5 -5
  138. package/dist/esm/components-page/account-settings/teams/team-creation-page.js +19 -10
  139. package/dist/esm/components-page/account-settings/teams/team-creation-page.js.map +1 -1
  140. package/dist/esm/components-page/account-settings/teams/team-display-name-section.js +4 -4
  141. package/dist/esm/components-page/account-settings/teams/team-member-invitation-section.js +4 -4
  142. package/dist/esm/components-page/account-settings/teams/team-member-list-section.js +3 -3
  143. package/dist/esm/components-page/account-settings/teams/team-page.js +8 -8
  144. package/dist/esm/components-page/account-settings/teams/team-profile-image-section.js +4 -4
  145. package/dist/esm/components-page/account-settings/teams/team-profile-user-section.js +4 -4
  146. package/dist/esm/components-page/account-settings.js +43 -21
  147. package/dist/esm/components-page/account-settings.js.map +1 -1
  148. package/dist/esm/components-page/auth-page.js +11 -12
  149. package/dist/esm/components-page/auth-page.js.map +1 -1
  150. package/dist/esm/components-page/cli-auth-confirm.js +3 -3
  151. package/dist/esm/components-page/email-verification.js +3 -3
  152. package/dist/esm/components-page/error-page.js +6 -6
  153. package/dist/esm/components-page/error-page.js.map +1 -1
  154. package/dist/esm/components-page/forgot-password.js +6 -6
  155. package/dist/esm/components-page/magic-link-callback.js +4 -4
  156. package/dist/esm/components-page/mfa.js +174 -0
  157. package/dist/esm/components-page/mfa.js.map +1 -0
  158. package/dist/esm/components-page/oauth-callback.js +4 -4
  159. package/dist/esm/components-page/password-reset.js +6 -6
  160. package/dist/esm/components-page/sign-in.js +3 -2
  161. package/dist/esm/components-page/sign-in.js.map +1 -1
  162. package/dist/esm/components-page/sign-out.js +2 -2
  163. package/dist/esm/components-page/sign-up.js +1 -1
  164. package/dist/esm/components-page/stack-handler.js +25 -14
  165. package/dist/esm/components-page/stack-handler.js.map +1 -1
  166. package/dist/esm/components-page/team-creation.js +4 -4
  167. package/dist/esm/components-page/team-invitation.js +3 -3
  168. package/dist/esm/generated/global-css.js +1 -1
  169. package/dist/esm/generated/global-css.js.map +1 -1
  170. package/dist/esm/generated/quetzal-translations.js +3616 -2364
  171. package/dist/esm/generated/quetzal-translations.js.map +1 -1
  172. package/dist/esm/index.js +22 -22
  173. package/dist/esm/lib/auth.js +2 -2
  174. package/dist/esm/lib/cookie.js +1 -129
  175. package/dist/esm/lib/cookie.js.map +1 -1
  176. package/dist/esm/lib/hooks.js +1 -1
  177. package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js +11 -8
  178. package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
  179. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js +79 -21
  180. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
  181. package/dist/esm/lib/stack-app/apps/implementations/common.js +2 -1
  182. package/dist/esm/lib/stack-app/apps/implementations/common.js.map +1 -1
  183. package/dist/esm/lib/stack-app/apps/implementations/index.js +3 -3
  184. package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js +34 -8
  185. package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
  186. package/dist/esm/lib/stack-app/apps/index.js +3 -3
  187. package/dist/esm/lib/stack-app/apps/interfaces/admin-app.js +1 -1
  188. package/dist/esm/lib/stack-app/apps/interfaces/admin-app.js.map +1 -1
  189. package/dist/esm/lib/stack-app/apps/interfaces/client-app.js +1 -1
  190. package/dist/esm/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
  191. package/dist/esm/lib/stack-app/apps/interfaces/server-app.js +1 -1
  192. package/dist/esm/lib/stack-app/common.js.map +1 -1
  193. package/dist/esm/lib/stack-app/contact-channels/index.js.map +1 -1
  194. package/dist/esm/lib/stack-app/index.js +2 -2
  195. package/dist/esm/lib/stack-app/internal-api-keys/index.js.map +1 -1
  196. package/dist/esm/lib/stack-app/notification-categories/index.js +1 -0
  197. package/dist/esm/lib/stack-app/notification-categories/index.js.map +1 -0
  198. package/dist/esm/lib/stack-app/users/index.js.map +1 -1
  199. package/dist/esm/lib/translations.js +1 -1
  200. package/dist/esm/providers/stack-provider-client.js +2 -2
  201. package/dist/esm/providers/stack-provider.js +3 -3
  202. package/dist/esm/providers/theme-provider.js +3 -3
  203. package/dist/esm/providers/translation-provider.js +2 -2
  204. package/dist/esm/utils/browser-script.js +1 -1
  205. package/dist/generated/global-css.js +1 -1
  206. package/dist/generated/global-css.js.map +1 -1
  207. package/dist/generated/quetzal-translations.js +3616 -2364
  208. package/dist/generated/quetzal-translations.js.map +1 -1
  209. package/dist/index.d.mts +91 -6
  210. package/dist/index.d.ts +91 -6
  211. package/dist/index.js +23 -23
  212. package/dist/index.js.map +1 -1
  213. package/dist/lib/auth.js +2 -2
  214. package/dist/lib/cookie.js +4 -132
  215. package/dist/lib/cookie.js.map +1 -1
  216. package/dist/lib/hooks.js +1 -1
  217. package/dist/lib/stack-app/apps/implementations/admin-app-impl.js +11 -8
  218. package/dist/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
  219. package/dist/lib/stack-app/apps/implementations/client-app-impl.js +79 -21
  220. package/dist/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
  221. package/dist/lib/stack-app/apps/implementations/common.js +2 -1
  222. package/dist/lib/stack-app/apps/implementations/common.js.map +1 -1
  223. package/dist/lib/stack-app/apps/implementations/index.js +3 -3
  224. package/dist/lib/stack-app/apps/implementations/server-app-impl.js +34 -8
  225. package/dist/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
  226. package/dist/lib/stack-app/apps/index.js +3 -3
  227. package/dist/lib/stack-app/apps/interfaces/admin-app.js +1 -1
  228. package/dist/lib/stack-app/apps/interfaces/admin-app.js.map +1 -1
  229. package/dist/lib/stack-app/apps/interfaces/client-app.js +1 -1
  230. package/dist/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
  231. package/dist/lib/stack-app/apps/interfaces/server-app.js +1 -1
  232. package/dist/lib/stack-app/common.js.map +1 -1
  233. package/dist/lib/stack-app/contact-channels/index.js.map +1 -1
  234. package/dist/lib/stack-app/index.js +2 -2
  235. package/dist/lib/stack-app/internal-api-keys/index.js.map +1 -1
  236. package/dist/lib/stack-app/notification-categories/index.js +19 -0
  237. package/dist/lib/stack-app/notification-categories/index.js.map +1 -0
  238. package/dist/lib/stack-app/users/index.js.map +1 -1
  239. package/dist/lib/translations.js +1 -1
  240. package/dist/providers/stack-provider-client.js +2 -2
  241. package/dist/providers/stack-provider.js +3 -3
  242. package/dist/providers/theme-provider.js +3 -3
  243. package/dist/providers/translation-provider.js +2 -2
  244. package/dist/utils/browser-script.js +1 -1
  245. package/package.json +5 -5
  246. package/dist/components/iframe-preventer.js.map +0 -1
  247. package/dist/esm/components/iframe-preventer.js +0 -28
  248. package/dist/esm/components/iframe-preventer.js.map +0 -1
@@ -25,20 +25,110 @@ __export(api_keys_page_exports, {
25
25
  module.exports = __toCommonJS(api_keys_page_exports);
26
26
  var import_stack_ui = require("@stackframe/stack-ui");
27
27
  var import_react = require("react");
28
- var import_api_key_dialogs = require("../../../components/api-key-dialogs");
29
- var import_api_key_table = require("../../../components/api-key-table");
30
- var import_hooks = require("../../../lib/hooks");
31
- var import_translations = require("../../../lib/translations");
32
- var import_page_layout = require("../page-layout");
28
+ var import_api_key_dialogs = require("../../../components/api-key-dialogs.js");
29
+ var import_api_key_table = require("../../../components/api-key-table.js");
30
+ var import_hooks = require("../../../lib/hooks.js");
31
+ var import_translations = require("../../../lib/translations.js");
32
+ var import_page_layout = require("../page-layout.js");
33
33
  var import_jsx_runtime = require("react/jsx-runtime");
34
- function ApiKeysPage() {
34
+ function ApiKeysPage(props) {
35
35
  const { t } = (0, import_translations.useTranslation)();
36
- const user = (0, import_hooks.useUser)({ or: "redirect" });
37
- const apiKeys = user.useApiKeys();
36
+ const isInMockMode = !!(props?.mockApiKeys || props?.mockMode);
37
+ const userFromHook = (0, import_hooks.useUser)({ or: isInMockMode ? "return-null" : "redirect" });
38
+ if (isInMockMode && !userFromHook) {
39
+ }
40
+ if (!isInMockMode && !userFromHook) {
41
+ return null;
42
+ }
43
+ const mockApiKeysData = props?.mockApiKeys ? props.mockApiKeys.map((mockKey) => ({
44
+ id: mockKey.id,
45
+ description: mockKey.description,
46
+ createdAt: new Date(mockKey.createdAt),
47
+ expiresAt: mockKey.expiresAt ? new Date(mockKey.expiresAt) : void 0,
48
+ manuallyRevokedAt: mockKey.manuallyRevokedAt ? new Date(mockKey.manuallyRevokedAt) : null,
49
+ value: {
50
+ lastFour: mockKey.id.slice(-4).padStart(4, "0")
51
+ // Use last 4 chars of ID or pad with zeros
52
+ },
53
+ type: "user",
54
+ userId: "mock-user-id",
55
+ update: async () => {
56
+ console.log("Mock API key update called");
57
+ },
58
+ revoke: async () => {
59
+ console.log("Mock API key revoke called");
60
+ },
61
+ isValid: () => {
62
+ const now = /* @__PURE__ */ new Date();
63
+ const isExpired = mockKey.expiresAt ? new Date(mockKey.expiresAt) < now : false;
64
+ const isRevoked = !!mockKey.manuallyRevokedAt;
65
+ return !isExpired && !isRevoked;
66
+ },
67
+ whyInvalid: () => {
68
+ const now = /* @__PURE__ */ new Date();
69
+ if (mockKey.manuallyRevokedAt) return "manually-revoked";
70
+ if (mockKey.expiresAt && new Date(mockKey.expiresAt) < now) return "expired";
71
+ return null;
72
+ }
73
+ })) : [
74
+ {
75
+ id: "key-1",
76
+ description: "Development Key",
77
+ createdAt: new Date(Date.now() - 1728e5),
78
+ // 2 days ago
79
+ expiresAt: void 0,
80
+ manuallyRevokedAt: null,
81
+ value: {
82
+ lastFour: "ey-1".slice(-4).padStart(4, "0")
83
+ },
84
+ type: "user",
85
+ userId: "mock-user-id",
86
+ update: async () => {
87
+ console.log("Mock API key update called");
88
+ },
89
+ revoke: async () => {
90
+ console.log("Mock API key revoke called");
91
+ },
92
+ isValid: () => true,
93
+ whyInvalid: () => null
94
+ }
95
+ ];
96
+ let apiKeys;
97
+ if (isInMockMode) {
98
+ apiKeys = mockApiKeysData;
99
+ } else if (userFromHook) {
100
+ apiKeys = userFromHook.useApiKeys();
101
+ } else {
102
+ apiKeys = [];
103
+ }
38
104
  const [isNewApiKeyDialogOpen, setIsNewApiKeyDialogOpen] = (0, import_react.useState)(false);
39
105
  const [returnedApiKey, setReturnedApiKey] = (0, import_react.useState)(null);
40
106
  const CreateDialog = import_api_key_dialogs.CreateApiKeyDialog;
41
107
  const ShowDialog = import_api_key_dialogs.ShowApiKeyDialog;
108
+ const handleCreateApiKey = async (data) => {
109
+ if (isInMockMode) {
110
+ const mockApiKey = {
111
+ id: `key-${Date.now()}`,
112
+ description: data.description,
113
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
114
+ expiresAt: data.expiresAt?.toISOString(),
115
+ value: "sk_dev_mock_key_" + Math.random().toString(36).substring(2),
116
+ update: async () => {
117
+ console.log("Mock API key update called");
118
+ },
119
+ revoke: async () => {
120
+ console.log("Mock API key revoke called");
121
+ },
122
+ isValid: () => true,
123
+ whyInvalid: () => null,
124
+ type: "user",
125
+ userId: "mock-user-id"
126
+ };
127
+ return mockApiKey;
128
+ }
129
+ if (!userFromHook) throw new Error("User not available");
130
+ return await userFromHook.createApiKey(data);
131
+ };
42
132
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_page_layout.PageLayout, { children: [
43
133
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { onClick: () => setIsNewApiKeyDialogOpen(true), children: t("Create API Key") }),
44
134
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_api_key_table.ApiKeyTable, { apiKeys }),
@@ -48,10 +138,8 @@ function ApiKeysPage() {
48
138
  open: isNewApiKeyDialogOpen,
49
139
  onOpenChange: setIsNewApiKeyDialogOpen,
50
140
  onKeyCreated: setReturnedApiKey,
51
- createApiKey: async (data) => {
52
- const apiKey = await user.createApiKey(data);
53
- return apiKey;
54
- }
141
+ createApiKey: handleCreateApiKey,
142
+ mockMode: isInMockMode
55
143
  }
56
144
  ),
57
145
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components-page/account-settings/api-keys/api-keys-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Button } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { CreateApiKeyDialog, ShowApiKeyDialog } from \"../../../components/api-key-dialogs\";\nimport { ApiKeyTable } from \"../../../components/api-key-table\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { ApiKey, ApiKeyCreationOptions } from \"../../../lib/stack-app/api-keys\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { PageLayout } from \"../page-layout\";\n\n\nexport function ApiKeysPage() {\n const { t } = useTranslation();\n\n const user = useUser({ or: 'redirect' });\n const apiKeys = user.useApiKeys();\n\n const [isNewApiKeyDialogOpen, setIsNewApiKeyDialogOpen] = useState(false);\n const [returnedApiKey, setReturnedApiKey] = useState<ApiKey<\"user\", true> | null>(null);\n\n const CreateDialog = CreateApiKeyDialog<\"user\">;\n const ShowDialog = ShowApiKeyDialog<\"user\">;\n\n return (\n <PageLayout>\n <Button onClick={() => setIsNewApiKeyDialogOpen(true)}>\n {t(\"Create API Key\")}\n </Button>\n <ApiKeyTable apiKeys={apiKeys} />\n <CreateDialog\n open={isNewApiKeyDialogOpen}\n onOpenChange={setIsNewApiKeyDialogOpen}\n onKeyCreated={setReturnedApiKey}\n createApiKey={async (data: ApiKeyCreationOptions<\"user\">) => {\n const apiKey = await user.createApiKey(data);\n return apiKey;\n }}\n />\n <ShowDialog\n apiKey={returnedApiKey}\n onClose={() => setReturnedApiKey(null)}\n />\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,sBAAuB;AACvB,mBAAyB;AACzB,6BAAqD;AACrD,2BAA4B;AAC5B,mBAAwB;AAExB,0BAA+B;AAC/B,yBAA2B;AAgBvB;AAbG,SAAS,cAAc;AAC5B,QAAM,EAAE,EAAE,QAAI,oCAAe;AAE7B,QAAM,WAAO,sBAAQ,EAAE,IAAI,WAAW,CAAC;AACvC,QAAM,UAAU,KAAK,WAAW;AAEhC,QAAM,CAAC,uBAAuB,wBAAwB,QAAI,uBAAS,KAAK;AACxE,QAAM,CAAC,gBAAgB,iBAAiB,QAAI,uBAAwC,IAAI;AAExF,QAAM,eAAe;AACrB,QAAM,aAAa;AAEnB,SACE,6CAAC,iCACC;AAAA,gDAAC,0BAAO,SAAS,MAAM,yBAAyB,IAAI,GACjD,YAAE,gBAAgB,GACrB;AAAA,IACA,4CAAC,oCAAY,SAAkB;AAAA,IAC/B;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,cAAc;AAAA,QACd,cAAc;AAAA,QACd,cAAc,OAAO,SAAwC;AAC3D,gBAAM,SAAS,MAAM,KAAK,aAAa,IAAI;AAC3C,iBAAO;AAAA,QACT;AAAA;AAAA,IACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ;AAAA,QACR,SAAS,MAAM,kBAAkB,IAAI;AAAA;AAAA,IACvC;AAAA,KACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/components-page/account-settings/api-keys/api-keys-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Button } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { CreateApiKeyDialog, ShowApiKeyDialog } from \"../../../components/api-key-dialogs\";\nimport { ApiKeyTable } from \"../../../components/api-key-table\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { ApiKey, ApiKeyCreationOptions } from \"../../../lib/stack-app/api-keys\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { PageLayout } from \"../page-layout\";\n\n\nexport function ApiKeysPage(props?: {\n mockApiKeys?: Array<{\n id: string,\n description: string,\n createdAt: string,\n expiresAt?: string,\n manuallyRevokedAt?: string,\n }>,\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n\n // Check if we're in any kind of mock mode first\n const isInMockMode = !!(props?.mockApiKeys || props?.mockMode);\n\n const userFromHook = useUser({ or: isInMockMode ? 'return-null' : 'redirect' });\n\n // In mock mode, we don't need a user - just show mock data\n if (isInMockMode && !userFromHook) {\n // This is expected in mock mode, continue with mock data\n }\n\n // Only return null if we're not in mock mode and don't have a user\n if (!isInMockMode && !userFromHook) {\n return null; // This shouldn't happen due to redirect, but just in case\n }\n\n // Use mock data if provided\n const mockApiKeysData = props?.mockApiKeys ? props.mockApiKeys.map(mockKey => ({\n id: mockKey.id,\n description: mockKey.description,\n createdAt: new Date(mockKey.createdAt),\n expiresAt: mockKey.expiresAt ? new Date(mockKey.expiresAt) : undefined,\n manuallyRevokedAt: mockKey.manuallyRevokedAt ? new Date(mockKey.manuallyRevokedAt) : null,\n value: {\n lastFour: mockKey.id.slice(-4).padStart(4, '0'), // Use last 4 chars of ID or pad with zeros\n },\n type: 'user' as const,\n userId: 'mock-user-id',\n update: async () => {\n console.log('Mock API key update called');\n },\n revoke: async () => {\n console.log('Mock API key revoke called');\n },\n isValid: () => {\n const now = new Date();\n const isExpired = mockKey.expiresAt ? new Date(mockKey.expiresAt) < now : false;\n const isRevoked = !!mockKey.manuallyRevokedAt;\n return !isExpired && !isRevoked;\n },\n whyInvalid: () => {\n const now = new Date();\n if (mockKey.manuallyRevokedAt) return 'manually-revoked';\n if (mockKey.expiresAt && new Date(mockKey.expiresAt) < now) return 'expired';\n return null;\n },\n })) : [\n {\n id: 'key-1',\n description: 'Development Key',\n createdAt: new Date(Date.now() - 172800000), // 2 days ago\n expiresAt: undefined,\n manuallyRevokedAt: null,\n value: {\n lastFour: 'ey-1'.slice(-4).padStart(4, '0'),\n },\n type: 'user' as const,\n userId: 'mock-user-id',\n update: async () => {\n console.log('Mock API key update called');\n },\n revoke: async () => {\n console.log('Mock API key revoke called');\n },\n isValid: () => true,\n whyInvalid: () => null,\n }\n ];\n\n // Determine which API keys to use\n let apiKeys: any[];\n if (isInMockMode) {\n apiKeys = mockApiKeysData;\n } else if (userFromHook) {\n apiKeys = userFromHook.useApiKeys();\n } else {\n apiKeys = [];\n }\n\n const [isNewApiKeyDialogOpen, setIsNewApiKeyDialogOpen] = useState(false);\n const [returnedApiKey, setReturnedApiKey] = useState<ApiKey<\"user\", true> | null>(null);\n\n const CreateDialog = CreateApiKeyDialog<\"user\">;\n const ShowDialog = ShowApiKeyDialog<\"user\">;\n\n const handleCreateApiKey = async (data: ApiKeyCreationOptions<\"user\">) => {\n if (isInMockMode) {\n // Mock API key creation\n const mockApiKey = {\n id: `key-${Date.now()}`,\n description: data.description,\n createdAt: new Date().toISOString(),\n expiresAt: data.expiresAt?.toISOString(),\n value: 'sk_dev_mock_key_' + Math.random().toString(36).substring(2),\n update: async () => {\n console.log('Mock API key update called');\n },\n revoke: async () => {\n console.log('Mock API key revoke called');\n },\n isValid: () => true,\n whyInvalid: () => null,\n type: 'user' as const,\n userId: 'mock-user-id',\n };\n return mockApiKey as any;\n }\n\n if (!userFromHook) throw new Error('User not available');\n return await userFromHook.createApiKey(data);\n };\n\n return (\n <PageLayout>\n <Button onClick={() => setIsNewApiKeyDialogOpen(true)}>\n {t(\"Create API Key\")}\n </Button>\n <ApiKeyTable apiKeys={apiKeys} />\n <CreateDialog\n open={isNewApiKeyDialogOpen}\n onOpenChange={setIsNewApiKeyDialogOpen}\n onKeyCreated={setReturnedApiKey}\n createApiKey={handleCreateApiKey}\n mockMode={isInMockMode}\n />\n <ShowDialog\n apiKey={returnedApiKey}\n onClose={() => setReturnedApiKey(null)}\n />\n </PageLayout>\n );\n}\n"],"mappings":"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","names":[]}
@@ -26,7 +26,7 @@ module.exports = __toCommonJS(editable_text_exports);
26
26
  var import_stack_ui = require("@stackframe/stack-ui");
27
27
  var import_lucide_react = require("lucide-react");
28
28
  var import_react = require("react");
29
- var import_translations = require("../../lib/translations");
29
+ var import_translations = require("../../lib/translations.js");
30
30
  var import_jsx_runtime = require("react/jsx-runtime");
31
31
  function EditableText(props) {
32
32
  const [editing, setEditing] = (0, import_react.useState)(false);
@@ -23,20 +23,20 @@ __export(email_and_auth_page_exports, {
23
23
  EmailsAndAuthPage: () => EmailsAndAuthPage
24
24
  });
25
25
  module.exports = __toCommonJS(email_and_auth_page_exports);
26
- var import_page_layout = require("../page-layout");
27
- var import_emails_section = require("./emails-section");
28
- var import_mfa_section = require("./mfa-section");
29
- var import_otp_section = require("./otp-section");
30
- var import_passkey_section = require("./passkey-section");
31
- var import_password_section = require("./password-section");
26
+ var import_page_layout = require("../page-layout.js");
27
+ var import_emails_section = require("./emails-section.js");
28
+ var import_mfa_section = require("./mfa-section.js");
29
+ var import_otp_section = require("./otp-section.js");
30
+ var import_passkey_section = require("./passkey-section.js");
31
+ var import_password_section = require("./password-section.js");
32
32
  var import_jsx_runtime = require("react/jsx-runtime");
33
- function EmailsAndAuthPage() {
33
+ function EmailsAndAuthPage(props) {
34
34
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_page_layout.PageLayout, { children: [
35
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_emails_section.EmailsSection, {}),
36
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_password_section.PasswordSection, {}),
37
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_passkey_section.PasskeySection, {}),
38
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_otp_section.OtpSection, {}),
39
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_mfa_section.MfaSection, {})
35
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_emails_section.EmailsSection, { mockMode: props?.mockMode }),
36
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_password_section.PasswordSection, { mockMode: props?.mockMode }),
37
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_passkey_section.PasskeySection, { mockMode: props?.mockMode }),
38
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_otp_section.OtpSection, { mockMode: props?.mockMode }),
39
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_mfa_section.MfaSection, { mockMode: props?.mockMode })
40
40
  ] });
41
41
  }
42
42
  // Annotate the CommonJS export names for ESM import in node:
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/email-and-auth-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { PageLayout } from \"../page-layout\";\nimport { EmailsSection } from \"./emails-section\";\nimport { MfaSection } from \"./mfa-section\";\nimport { OtpSection } from \"./otp-section\";\nimport { PasskeySection } from \"./passkey-section\";\nimport { PasswordSection } from \"./password-section\";\n\nexport function EmailsAndAuthPage() {\n return (\n <PageLayout>\n <EmailsSection/>\n <PasswordSection />\n <PasskeySection />\n <OtpSection />\n <MfaSection />\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,yBAA2B;AAC3B,4BAA8B;AAC9B,yBAA2B;AAC3B,yBAA2B;AAC3B,6BAA+B;AAC/B,8BAAgC;AAI5B;AAFG,SAAS,oBAAoB;AAClC,SACE,6CAAC,iCACC;AAAA,gDAAC,uCAAa;AAAA,IACd,4CAAC,2CAAgB;AAAA,IACjB,4CAAC,yCAAe;AAAA,IAChB,4CAAC,iCAAW;AAAA,IACZ,4CAAC,iCAAW;AAAA,KACd;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/email-and-auth-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { PageLayout } from \"../page-layout\";\nimport { EmailsSection } from \"./emails-section\";\nimport { MfaSection } from \"./mfa-section\";\nimport { OtpSection } from \"./otp-section\";\nimport { PasskeySection } from \"./passkey-section\";\nimport { PasswordSection } from \"./password-section\";\n\nexport function EmailsAndAuthPage(props?: {\n mockMode?: boolean,\n}) {\n return (\n <PageLayout>\n <EmailsSection mockMode={props?.mockMode}/>\n <PasswordSection mockMode={props?.mockMode} />\n <PasskeySection mockMode={props?.mockMode} />\n <OtpSection mockMode={props?.mockMode} />\n <MfaSection mockMode={props?.mockMode} />\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,yBAA2B;AAC3B,4BAA8B;AAC9B,yBAA2B;AAC3B,yBAA2B;AAC3B,6BAA+B;AAC/B,8BAAgC;AAM5B;AAJG,SAAS,kBAAkB,OAE/B;AACD,SACE,6CAAC,iCACC;AAAA,gDAAC,uCAAc,UAAU,OAAO,UAAS;AAAA,IACzC,4CAAC,2CAAgB,UAAU,OAAO,UAAU;AAAA,IAC5C,4CAAC,yCAAe,UAAU,OAAO,UAAU;AAAA,IAC3C,4CAAC,iCAAW,UAAU,OAAO,UAAU;AAAA,IACvC,4CAAC,iCAAW,UAAU,OAAO,UAAU;AAAA,KACzC;AAEJ;","names":[]}
@@ -30,13 +30,22 @@ var import_promises = require("@stackframe/stack-shared/dist/utils/promises");
30
30
  var import_stack_ui = require("@stackframe/stack-ui");
31
31
  var import_react = require("react");
32
32
  var import_react_hook_form = require("react-hook-form");
33
- var import_form_warning = require("../../../components/elements/form-warning");
34
- var import_hooks = require("../../../lib/hooks");
35
- var import_translations = require("../../../lib/translations");
33
+ var import_form_warning = require("../../../components/elements/form-warning.js");
34
+ var import_hooks = require("../../../lib/hooks.js");
35
+ var import_translations = require("../../../lib/translations.js");
36
36
  var import_jsx_runtime = require("react/jsx-runtime");
37
- function EmailsSection() {
37
+ function EmailsSection(props) {
38
38
  const { t } = (0, import_translations.useTranslation)();
39
- const user = (0, import_hooks.useUser)({ or: "redirect" });
39
+ const user = (0, import_hooks.useUser)({ or: props?.mockMode ? "return-null" : "redirect" });
40
+ if (props?.mockMode && !user) {
41
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
42
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-col md:flex-row justify-between mb-4 gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { className: "font-medium", children: t("Emails") }) }),
43
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", children: t("Email management is not available in demo mode.") })
44
+ ] });
45
+ }
46
+ if (!user) {
47
+ return null;
48
+ }
40
49
  const contactChannels = user.useContactChannels();
41
50
  const [addingEmail, setAddingEmail] = (0, import_react.useState)(contactChannels.length === 0);
42
51
  const [addingEmailLoading, setAddingEmailLoading] = (0, import_react.useState)(false);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/emails-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { KnownErrors } from \"@stackframe/stack-shared/dist/known-errors\";\nimport { strictEmailSchema, yupObject } from \"@stackframe/stack-shared/dist/schema-fields\";\nimport { runAsynchronously } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { ActionCell, Badge, Button, Input, Table, TableBody, TableCell, TableRow, Typography } from \"@stackframe/stack-ui\";\nimport { useEffect, useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as yup from \"yup\";\nimport { FormWarningText } from \"../../../components/elements/form-warning\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\n\nexport function EmailsSection() {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n const contactChannels = user.useContactChannels();\n const [addingEmail, setAddingEmail] = useState(contactChannels.length === 0);\n const [addingEmailLoading, setAddingEmailLoading] = useState(false);\n const [addedEmail, setAddedEmail] = useState<string | null>(null);\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const isLastEmail = contactChannels.filter(x => x.usedForAuth && x.type === 'email').length === 1;\n\n useEffect(() => {\n if (addedEmail) {\n runAsynchronously(async () => {\n const cc = contactChannels.find(x => x.value === addedEmail);\n if (cc && !cc.isVerified) {\n await cc.sendVerificationEmail();\n }\n setAddedEmail(null);\n });\n }\n }, [contactChannels, addedEmail]);\n\n const emailSchema = yupObject({\n email: strictEmailSchema(t('Please enter a valid email address'))\n .notOneOf(contactChannels.map(x => x.value), t('Email already exists'))\n .defined()\n .nonEmpty(t('Email is required')),\n });\n\n const { register, handleSubmit, formState: { errors }, reset } = useForm({\n resolver: yupResolver(emailSchema)\n });\n\n const onSubmit = async (data: yup.InferType<typeof emailSchema>) => {\n setAddingEmailLoading(true);\n try {\n await user.createContactChannel({ type: 'email', value: data.email, usedForAuth: false });\n setAddedEmail(data.email);\n } finally {\n setAddingEmailLoading(false);\n }\n setAddingEmail(false);\n reset();\n };\n\n return (\n <div>\n <div className='flex flex-col md:flex-row justify-between mb-4 gap-4'>\n <Typography className='font-medium'>{t(\"Emails\")}</Typography>\n {addingEmail ? (\n <form\n onSubmit={(e) => {\n e.preventDefault();\n runAsynchronously(handleSubmit(onSubmit));\n }}\n className='flex flex-col'\n >\n <div className='flex gap-2'>\n <Input\n {...register(\"email\")}\n placeholder={t(\"Enter email\")}\n />\n <Button type=\"submit\" loading={addingEmailLoading}>\n {t(\"Add\")}\n </Button>\n <Button\n variant='secondary'\n onClick={() => {\n setAddingEmail(false);\n reset();\n }}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n {errors.email && <FormWarningText text={errors.email.message} />}\n </form>\n ) : (\n <div className='flex md:justify-end'>\n <Button variant='secondary' onClick={() => setAddingEmail(true)}>{t(\"Add an email\")}</Button>\n </div>\n )}\n </div>\n\n {contactChannels.length > 0 ? (\n <div className='border rounded-md'>\n <Table>\n <TableBody>\n {/*eslint-disable-next-line @typescript-eslint/no-unnecessary-condition*/}\n {contactChannels.filter(x => x.type === 'email')\n .sort((a, b) => {\n if (a.isPrimary !== b.isPrimary) return a.isPrimary ? -1 : 1;\n if (a.isVerified !== b.isVerified) return a.isVerified ? -1 : 1;\n return 0;\n })\n .map(x => (\n <TableRow key={x.id}>\n <TableCell>\n <div className='flex flex-col md:flex-row gap-2 md:gap-4'>\n {x.value}\n <div className='flex gap-2'>\n {x.isPrimary ? <Badge>{t(\"Primary\")}</Badge> : null}\n {!x.isVerified ? <Badge variant='destructive'>{t(\"Unverified\")}</Badge> : null}\n {x.usedForAuth ? <Badge variant='outline'>{t(\"Used for sign-in\")}</Badge> : null}\n </div>\n </div>\n </TableCell>\n <TableCell className=\"flex justify-end\">\n <ActionCell items={[\n ...(!x.isVerified ? [{\n item: t(\"Send verification email\"),\n onClick: async () => { await x.sendVerificationEmail(); },\n }] : []),\n ...(!x.isPrimary && x.isVerified ? [{\n item: t(\"Set as primary\"),\n onClick: async () => { await x.update({ isPrimary: true }); },\n }] :\n !x.isPrimary ? [{\n item: t(\"Set as primary\"),\n onClick: async () => {},\n disabled: true,\n disabledTooltip: t(\"Please verify your email first\"),\n }] : []),\n ...(!x.usedForAuth && x.isVerified ? [{\n item: t(\"Use for sign-in\"),\n onClick: async () => {\n try {\n await x.update({ usedForAuth: true });\n } catch (e) {\n if (KnownErrors.ContactChannelAlreadyUsedForAuthBySomeoneElse.isInstance(e)) {\n alert(t(\"This email is already used for sign-in by another user.\"));\n }\n }\n }\n }] : []),\n ...(x.usedForAuth && !isLastEmail ? [{\n item: t(\"Stop using for sign-in\"),\n onClick: async () => { await x.update({ usedForAuth: false }); },\n }] : x.usedForAuth ? [{\n item: t(\"Stop using for sign-in\"),\n onClick: async () => {},\n disabled: true,\n disabledTooltip: t(\"You can not remove your last sign-in email\"),\n }] : []),\n ...(!isLastEmail || !x.usedForAuth ? [{\n item: t(\"Remove\"),\n onClick: async () => { await x.delete(); },\n danger: true,\n }] : [{\n item: t(\"Remove\"),\n onClick: async () => {},\n disabled: true,\n disabledTooltip: t(\"You can not remove your last sign-in email\"),\n }]),\n ]}/>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,iBAA4B;AAC5B,0BAA4B;AAC5B,2BAA6C;AAC7C,sBAAkC;AAClC,sBAAoG;AACpG,mBAAoC;AACpC,6BAAwB;AAExB,0BAAgC;AAChC,mBAAwB;AACxB,0BAA+B;AAkDvB;AAhDD,SAAS,gBAAgB;AAC9B,QAAM,EAAE,EAAE,QAAI,oCAAe;AAC7B,QAAM,WAAO,sBAAQ,EAAE,IAAI,WAAW,CAAC;AACvC,QAAM,kBAAkB,KAAK,mBAAmB;AAChD,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAS,gBAAgB,WAAW,CAAC;AAC3E,QAAM,CAAC,oBAAoB,qBAAqB,QAAI,uBAAS,KAAK;AAClE,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAwB,IAAI;AAEhE,QAAM,cAAc,gBAAgB,OAAO,OAAK,EAAE,eAAe,EAAE,SAAS,OAAO,EAAE,WAAW;AAEhG,8BAAU,MAAM;AACd,QAAI,YAAY;AACd,6CAAkB,YAAY;AAC5B,cAAM,KAAK,gBAAgB,KAAK,OAAK,EAAE,UAAU,UAAU;AAC3D,YAAI,MAAM,CAAC,GAAG,YAAY;AACxB,gBAAM,GAAG,sBAAsB;AAAA,QACjC;AACA,sBAAc,IAAI;AAAA,MACpB,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,iBAAiB,UAAU,CAAC;AAEhC,QAAM,kBAAc,gCAAU;AAAA,IAC5B,WAAO,wCAAkB,EAAE,oCAAoC,CAAC,EAC7D,SAAS,gBAAgB,IAAI,OAAK,EAAE,KAAK,GAAG,EAAE,sBAAsB,CAAC,EACrE,QAAQ,EACR,SAAS,EAAE,mBAAmB,CAAC;AAAA,EACpC,CAAC;AAED,QAAM,EAAE,UAAU,cAAc,WAAW,EAAE,OAAO,GAAG,MAAM,QAAI,gCAAQ;AAAA,IACvE,cAAU,wBAAY,WAAW;AAAA,EACnC,CAAC;AAED,QAAM,WAAW,OAAO,SAA4C;AAClE,0BAAsB,IAAI;AAC1B,QAAI;AACF,YAAM,KAAK,qBAAqB,EAAE,MAAM,SAAS,OAAO,KAAK,OAAO,aAAa,MAAM,CAAC;AACxF,oBAAc,KAAK,KAAK;AAAA,IAC1B,UAAE;AACA,4BAAsB,KAAK;AAAA,IAC7B;AACA,mBAAe,KAAK;AACpB,UAAM;AAAA,EACR;AAEA,SACE,6CAAC,SACC;AAAA,iDAAC,SAAI,WAAU,wDACb;AAAA,kDAAC,8BAAW,WAAU,eAAe,YAAE,QAAQ,GAAE;AAAA,MAChD,cACC;AAAA,QAAC;AAAA;AAAA,UACC,UAAU,CAAC,MAAM;AACf,cAAE,eAAe;AACjB,mDAAkB,aAAa,QAAQ,CAAC;AAAA,UAC1C;AAAA,UACA,WAAU;AAAA,UAEV;AAAA,yDAAC,SAAI,WAAU,cACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACE,GAAG,SAAS,OAAO;AAAA,kBACpB,aAAa,EAAE,aAAa;AAAA;AAAA,cAC9B;AAAA,cACA,4CAAC,0BAAO,MAAK,UAAS,SAAS,oBAC5B,YAAE,KAAK,GACV;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,SAAS,MAAM;AACb,mCAAe,KAAK;AACpB,0BAAM;AAAA,kBACR;AAAA,kBAEC,YAAE,QAAQ;AAAA;AAAA,cACb;AAAA,eACF;AAAA,YACC,OAAO,SAAS,4CAAC,uCAAgB,MAAM,OAAO,MAAM,SAAS;AAAA;AAAA;AAAA,MAChE,IAEA,4CAAC,SAAI,WAAU,uBACb,sDAAC,0BAAO,SAAQ,aAAY,SAAS,MAAM,eAAe,IAAI,GAAI,YAAE,cAAc,GAAE,GACtF;AAAA,OAEJ;AAAA,IAEC,gBAAgB,SAAS,IACxB,4CAAC,SAAI,WAAU,qBACb,sDAAC,yBACC,sDAAC,6BAEE,0BAAgB,OAAO,OAAK,EAAE,SAAS,OAAO,EAC5C,KAAK,CAAC,GAAG,MAAM;AACd,UAAI,EAAE,cAAc,EAAE,UAAW,QAAO,EAAE,YAAY,KAAK;AAC3D,UAAI,EAAE,eAAe,EAAE,WAAY,QAAO,EAAE,aAAa,KAAK;AAC9D,aAAO;AAAA,IACT,CAAC,EACA,IAAI,OACH,6CAAC,4BACC;AAAA,kDAAC,6BACC,uDAAC,SAAI,WAAU,4CACZ;AAAA,UAAE;AAAA,QACH,6CAAC,SAAI,WAAU,cACZ;AAAA,YAAE,YAAY,4CAAC,yBAAO,YAAE,SAAS,GAAE,IAAW;AAAA,UAC9C,CAAC,EAAE,aAAa,4CAAC,yBAAM,SAAQ,eAAe,YAAE,YAAY,GAAE,IAAW;AAAA,UACzE,EAAE,cAAc,4CAAC,yBAAM,SAAQ,WAAW,YAAE,kBAAkB,GAAE,IAAW;AAAA,WAC9E;AAAA,SACF,GACF;AAAA,MACA,4CAAC,6BAAU,WAAU,oBACnB,sDAAC,8BAAW,OAAO;AAAA,QACjB,GAAI,CAAC,EAAE,aAAa,CAAC;AAAA,UACnB,MAAM,EAAE,yBAAyB;AAAA,UACjC,SAAS,YAAY;AAAE,kBAAM,EAAE,sBAAsB;AAAA,UAAG;AAAA,QAC1D,CAAC,IAAI,CAAC;AAAA,QACN,GAAI,CAAC,EAAE,aAAa,EAAE,aAAa,CAAC;AAAA,UAClC,MAAM,EAAE,gBAAgB;AAAA,UACxB,SAAS,YAAY;AAAE,kBAAM,EAAE,OAAO,EAAE,WAAW,KAAK,CAAC;AAAA,UAAG;AAAA,QAC9D,CAAC,IACC,CAAC,EAAE,YAAY,CAAC;AAAA,UACd,MAAM,EAAE,gBAAgB;AAAA,UACxB,SAAS,YAAY;AAAA,UAAC;AAAA,UACtB,UAAU;AAAA,UACV,iBAAiB,EAAE,gCAAgC;AAAA,QACrD,CAAC,IAAI,CAAC;AAAA,QACR,GAAI,CAAC,EAAE,eAAe,EAAE,aAAa,CAAC;AAAA,UACpC,MAAM,EAAE,iBAAiB;AAAA,UACzB,SAAS,YAAY;AACnB,gBAAI;AACF,oBAAM,EAAE,OAAO,EAAE,aAAa,KAAK,CAAC;AAAA,YACtC,SAAS,GAAG;AACV,kBAAI,gCAAY,8CAA8C,WAAW,CAAC,GAAG;AAC3E,sBAAM,EAAE,yDAAyD,CAAC;AAAA,cACpE;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC,IAAI,CAAC;AAAA,QACN,GAAI,EAAE,eAAe,CAAC,cAAc,CAAC;AAAA,UACnC,MAAM,EAAE,wBAAwB;AAAA,UAChC,SAAS,YAAY;AAAE,kBAAM,EAAE,OAAO,EAAE,aAAa,MAAM,CAAC;AAAA,UAAG;AAAA,QACjE,CAAC,IAAI,EAAE,cAAc,CAAC;AAAA,UACpB,MAAM,EAAE,wBAAwB;AAAA,UAChC,SAAS,YAAY;AAAA,UAAC;AAAA,UACtB,UAAU;AAAA,UACV,iBAAiB,EAAE,4CAA4C;AAAA,QACjE,CAAC,IAAI,CAAC;AAAA,QACN,GAAI,CAAC,eAAe,CAAC,EAAE,cAAc,CAAC;AAAA,UACpC,MAAM,EAAE,QAAQ;AAAA,UAChB,SAAS,YAAY;AAAE,kBAAM,EAAE,OAAO;AAAA,UAAG;AAAA,UACzC,QAAQ;AAAA,QACV,CAAC,IAAI,CAAC;AAAA,UACJ,MAAM,EAAE,QAAQ;AAAA,UAChB,SAAS,YAAY;AAAA,UAAC;AAAA,UACtB,UAAU;AAAA,UACV,iBAAiB,EAAE,4CAA4C;AAAA,QACjE,CAAC;AAAA,MACH,GAAE,GACJ;AAAA,SA3Da,EAAE,EA4DjB,CACD,GACL,GACF,GACF,IACE;AAAA,KACN;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/emails-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { KnownErrors } from \"@stackframe/stack-shared/dist/known-errors\";\nimport { strictEmailSchema, yupObject } from \"@stackframe/stack-shared/dist/schema-fields\";\nimport { runAsynchronously } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { ActionCell, Badge, Button, Input, Table, TableBody, TableCell, TableRow, Typography } from \"@stackframe/stack-ui\";\nimport { useEffect, useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as yup from \"yup\";\nimport { FormWarningText } from \"../../../components/elements/form-warning\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\n\nexport function EmailsSection(props?: {\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const user = useUser({ or: props?.mockMode ? 'return-null' : 'redirect' });\n\n // In mock mode, show a placeholder message\n if (props?.mockMode && !user) {\n return (\n <div>\n <div className='flex flex-col md:flex-row justify-between mb-4 gap-4'>\n <Typography className='font-medium'>{t(\"Emails\")}</Typography>\n </div>\n <Typography variant='secondary'>{t(\"Email management is not available in demo mode.\")}</Typography>\n </div>\n );\n }\n\n if (!user) {\n return null; // This shouldn't happen in non-mock mode due to redirect\n }\n\n const contactChannels = user.useContactChannels();\n const [addingEmail, setAddingEmail] = useState(contactChannels.length === 0);\n const [addingEmailLoading, setAddingEmailLoading] = useState(false);\n const [addedEmail, setAddedEmail] = useState<string | null>(null);\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const isLastEmail = contactChannels.filter(x => x.usedForAuth && x.type === 'email').length === 1;\n\n useEffect(() => {\n if (addedEmail) {\n runAsynchronously(async () => {\n const cc = contactChannels.find(x => x.value === addedEmail);\n if (cc && !cc.isVerified) {\n await cc.sendVerificationEmail();\n }\n setAddedEmail(null);\n });\n }\n }, [contactChannels, addedEmail]);\n\n const emailSchema = yupObject({\n email: strictEmailSchema(t('Please enter a valid email address'))\n .notOneOf(contactChannels.map(x => x.value), t('Email already exists'))\n .defined()\n .nonEmpty(t('Email is required')),\n });\n\n const { register, handleSubmit, formState: { errors }, reset } = useForm({\n resolver: yupResolver(emailSchema)\n });\n\n const onSubmit = async (data: yup.InferType<typeof emailSchema>) => {\n setAddingEmailLoading(true);\n try {\n await user.createContactChannel({ type: 'email', value: data.email, usedForAuth: false });\n setAddedEmail(data.email);\n } finally {\n setAddingEmailLoading(false);\n }\n setAddingEmail(false);\n reset();\n };\n\n return (\n <div>\n <div className='flex flex-col md:flex-row justify-between mb-4 gap-4'>\n <Typography className='font-medium'>{t(\"Emails\")}</Typography>\n {addingEmail ? (\n <form\n onSubmit={(e) => {\n e.preventDefault();\n runAsynchronously(handleSubmit(onSubmit));\n }}\n className='flex flex-col'\n >\n <div className='flex gap-2'>\n <Input\n {...register(\"email\")}\n placeholder={t(\"Enter email\")}\n />\n <Button type=\"submit\" loading={addingEmailLoading}>\n {t(\"Add\")}\n </Button>\n <Button\n variant='secondary'\n onClick={() => {\n setAddingEmail(false);\n reset();\n }}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n {errors.email && <FormWarningText text={errors.email.message} />}\n </form>\n ) : (\n <div className='flex md:justify-end'>\n <Button variant='secondary' onClick={() => setAddingEmail(true)}>{t(\"Add an email\")}</Button>\n </div>\n )}\n </div>\n\n {contactChannels.length > 0 ? (\n <div className='border rounded-md'>\n <Table>\n <TableBody>\n {/*eslint-disable-next-line @typescript-eslint/no-unnecessary-condition*/}\n {contactChannels.filter(x => x.type === 'email')\n .sort((a, b) => {\n if (a.isPrimary !== b.isPrimary) return a.isPrimary ? -1 : 1;\n if (a.isVerified !== b.isVerified) return a.isVerified ? -1 : 1;\n return 0;\n })\n .map(x => (\n <TableRow key={x.id}>\n <TableCell>\n <div className='flex flex-col md:flex-row gap-2 md:gap-4'>\n {x.value}\n <div className='flex gap-2'>\n {x.isPrimary ? <Badge>{t(\"Primary\")}</Badge> : null}\n {!x.isVerified ? <Badge variant='destructive'>{t(\"Unverified\")}</Badge> : null}\n {x.usedForAuth ? <Badge variant='outline'>{t(\"Used for sign-in\")}</Badge> : null}\n </div>\n </div>\n </TableCell>\n <TableCell className=\"flex justify-end\">\n <ActionCell items={[\n ...(!x.isVerified ? [{\n item: t(\"Send verification email\"),\n onClick: async () => { await x.sendVerificationEmail(); },\n }] : []),\n ...(!x.isPrimary && x.isVerified ? [{\n item: t(\"Set as primary\"),\n onClick: async () => { await x.update({ isPrimary: true }); },\n }] :\n !x.isPrimary ? [{\n item: t(\"Set as primary\"),\n onClick: async () => {},\n disabled: true,\n disabledTooltip: t(\"Please verify your email first\"),\n }] : []),\n ...(!x.usedForAuth && x.isVerified ? [{\n item: t(\"Use for sign-in\"),\n onClick: async () => {\n try {\n await x.update({ usedForAuth: true });\n } catch (e) {\n if (KnownErrors.ContactChannelAlreadyUsedForAuthBySomeoneElse.isInstance(e)) {\n alert(t(\"This email is already used for sign-in by another user.\"));\n }\n }\n }\n }] : []),\n ...(x.usedForAuth && !isLastEmail ? [{\n item: t(\"Stop using for sign-in\"),\n onClick: async () => { await x.update({ usedForAuth: false }); },\n }] : x.usedForAuth ? [{\n item: t(\"Stop using for sign-in\"),\n onClick: async () => {},\n disabled: true,\n disabledTooltip: t(\"You can not remove your last sign-in email\"),\n }] : []),\n ...(!isLastEmail || !x.usedForAuth ? [{\n item: t(\"Remove\"),\n onClick: async () => { await x.delete(); },\n danger: true,\n }] : [{\n item: t(\"Remove\"),\n onClick: async () => {},\n disabled: true,\n disabledTooltip: t(\"You can not remove your last sign-in email\"),\n }]),\n ]}/>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -41,14 +41,27 @@ var import_promises = require("@stackframe/stack-shared/dist/utils/promises");
41
41
  var import_stack_ui = require("@stackframe/stack-ui");
42
42
  var QRCode = __toESM(require("qrcode"));
43
43
  var import_react = require("react");
44
- var import_hooks = require("../../../lib/hooks");
45
- var import_translations = require("../../../lib/translations");
46
- var import_section = require("../section");
44
+ var import_hooks = require("../../../lib/hooks.js");
45
+ var import_translations = require("../../../lib/translations.js");
46
+ var import_section = require("../section.js");
47
47
  var import_jsx_runtime = require("react/jsx-runtime");
48
- function MfaSection() {
48
+ function MfaSection(props) {
49
49
  const { t } = (0, import_translations.useTranslation)();
50
50
  const project = (0, import_hooks.useStackApp)().useProject();
51
- const user = (0, import_hooks.useUser)({ or: "throw" });
51
+ const user = (0, import_hooks.useUser)({ or: props?.mockMode ? "return-null" : "throw" });
52
+ if (props?.mockMode && !user) {
53
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
54
+ import_section.Section,
55
+ {
56
+ title: t("Multi-factor authentication"),
57
+ description: t("MFA management is not available in demo mode."),
58
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", children: t("MFA management is not available in demo mode.") })
59
+ }
60
+ );
61
+ }
62
+ if (!user) {
63
+ return null;
64
+ }
52
65
  const [generatedSecret, setGeneratedSecret] = (0, import_react.useState)(null);
53
66
  const [qrCodeUrl, setQrCodeUrl] = (0, import_react.useState)(null);
54
67
  const [mfaCode, setMfaCode] = (0, import_react.useState)("");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/mfa-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { createTOTPKeyURI, verifyTOTP } from \"@oslojs/otp\";\nimport { useAsyncCallback } from '@stackframe/stack-shared/dist/hooks/use-async-callback';\nimport { generateRandomValues } from '@stackframe/stack-shared/dist/utils/crypto';\nimport { throwErr } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Button, Input, Typography } from \"@stackframe/stack-ui\";\nimport * as QRCode from 'qrcode';\nimport { useEffect, useState } from \"react\";\nimport { CurrentUser, Project } from '../../..';\nimport { useStackApp, useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function MfaSection() {\n const { t } = useTranslation();\n const project = useStackApp().useProject();\n const user = useUser({ or: \"throw\" });\n const [generatedSecret, setGeneratedSecret] = useState<Uint8Array | null>(null);\n const [qrCodeUrl, setQrCodeUrl] = useState<string | null>(null);\n const [mfaCode, setMfaCode] = useState<string>(\"\");\n const [isMaybeWrong, setIsMaybeWrong] = useState(false);\n const isEnabled = user.isMultiFactorRequired;\n\n const [handleSubmit, isLoading] = useAsyncCallback(async () => {\n await user.update({\n totpMultiFactorSecret: generatedSecret,\n });\n setGeneratedSecret(null);\n setQrCodeUrl(null);\n setMfaCode(\"\");\n }, [generatedSecret, user]);\n\n useEffect(() => {\n setIsMaybeWrong(false);\n runAsynchronouslyWithAlert(async () => {\n if (generatedSecret && verifyTOTP(generatedSecret, 30, 6, mfaCode)) {\n await handleSubmit();\n }\n setIsMaybeWrong(true);\n });\n }, [mfaCode, generatedSecret, handleSubmit]);\n\n return (\n <Section\n title={t(\"Multi-factor authentication\")}\n description={isEnabled\n ? t(\"Multi-factor authentication is currently enabled.\")\n : t(\"Multi-factor authentication is currently disabled.\")}\n >\n <div className='flex flex-col gap-4'>\n {!isEnabled && generatedSecret && (\n <>\n <Typography>{t(\"Scan this QR code with your authenticator app:\")}</Typography>\n <img width={200} height={200} src={qrCodeUrl ?? throwErr(\"TOTP QR code failed to generate\")} alt={t(\"TOTP multi-factor authentication QR code\")} />\n <Typography>{t(\"Then, enter your six-digit MFA code:\")}</Typography>\n <Input\n value={mfaCode}\n onChange={(e) => {\n setIsMaybeWrong(false);\n setMfaCode(e.target.value);\n }}\n placeholder=\"123456\"\n maxLength={6}\n disabled={isLoading}\n />\n {isMaybeWrong && mfaCode.length === 6 && (\n <Typography variant=\"destructive\">{t(\"Incorrect code. Please try again.\")}</Typography>\n )}\n <div className='flex'>\n <Button\n variant='secondary'\n onClick={() => {\n setGeneratedSecret(null);\n setQrCodeUrl(null);\n setMfaCode(\"\");\n }}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </>\n )}\n <div className='flex gap-2'>\n {isEnabled ? (\n <Button\n variant='secondary'\n onClick={async () => {\n await user.update({\n totpMultiFactorSecret: null,\n });\n }}\n >\n {t(\"Disable MFA\")}\n </Button>\n ) : !generatedSecret && (\n <Button\n variant='secondary'\n onClick={async () => {\n const secret = generateRandomValues(new Uint8Array(20));\n setQrCodeUrl(await generateTotpQrCode(project, user, secret));\n setGeneratedSecret(secret);\n }}\n >\n {t(\"Enable MFA\")}\n </Button>\n )}\n </div>\n </div>\n </Section>\n );\n}\n\n\nasync function generateTotpQrCode(project: Project, user: CurrentUser, secret: Uint8Array) {\n const uri = createTOTPKeyURI(project.displayName, user.primaryEmail ?? user.id, secret, 30, 6);\n return await QRCode.toDataURL(uri) as any;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/mfa-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { createTOTPKeyURI, verifyTOTP } from \"@oslojs/otp\";\nimport { useAsyncCallback } from '@stackframe/stack-shared/dist/hooks/use-async-callback';\nimport { generateRandomValues } from '@stackframe/stack-shared/dist/utils/crypto';\nimport { throwErr } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Button, Input, Typography } from \"@stackframe/stack-ui\";\nimport * as QRCode from 'qrcode';\nimport { useEffect, useState } from \"react\";\nimport { CurrentUser, Project } from '../../..';\nimport { useStackApp, useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function MfaSection(props?: {\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const project = useStackApp().useProject();\n const user = useUser({ or: props?.mockMode ? 'return-null' : \"throw\" });\n\n // In mock mode, show a placeholder message\n if (props?.mockMode && !user) {\n return (\n <Section\n title={t(\"Multi-factor authentication\")}\n description={t(\"MFA management is not available in demo mode.\")}\n >\n <Typography variant='secondary'>{t(\"MFA management is not available in demo mode.\")}</Typography>\n </Section>\n );\n }\n\n if (!user) {\n return null; // This shouldn't happen in non-mock mode due to throw\n }\n const [generatedSecret, setGeneratedSecret] = useState<Uint8Array | null>(null);\n const [qrCodeUrl, setQrCodeUrl] = useState<string | null>(null);\n const [mfaCode, setMfaCode] = useState<string>(\"\");\n const [isMaybeWrong, setIsMaybeWrong] = useState(false);\n const isEnabled = user.isMultiFactorRequired;\n\n const [handleSubmit, isLoading] = useAsyncCallback(async () => {\n await user.update({\n totpMultiFactorSecret: generatedSecret,\n });\n setGeneratedSecret(null);\n setQrCodeUrl(null);\n setMfaCode(\"\");\n }, [generatedSecret, user]);\n\n useEffect(() => {\n setIsMaybeWrong(false);\n runAsynchronouslyWithAlert(async () => {\n if (generatedSecret && verifyTOTP(generatedSecret, 30, 6, mfaCode)) {\n await handleSubmit();\n }\n setIsMaybeWrong(true);\n });\n }, [mfaCode, generatedSecret, handleSubmit]);\n\n return (\n <Section\n title={t(\"Multi-factor authentication\")}\n description={isEnabled\n ? t(\"Multi-factor authentication is currently enabled.\")\n : t(\"Multi-factor authentication is currently disabled.\")}\n >\n <div className='flex flex-col gap-4'>\n {!isEnabled && generatedSecret && (\n <>\n <Typography>{t(\"Scan this QR code with your authenticator app:\")}</Typography>\n <img width={200} height={200} src={qrCodeUrl ?? throwErr(\"TOTP QR code failed to generate\")} alt={t(\"TOTP multi-factor authentication QR code\")} />\n <Typography>{t(\"Then, enter your six-digit MFA code:\")}</Typography>\n <Input\n value={mfaCode}\n onChange={(e) => {\n setIsMaybeWrong(false);\n setMfaCode(e.target.value);\n }}\n placeholder=\"123456\"\n maxLength={6}\n disabled={isLoading}\n />\n {isMaybeWrong && mfaCode.length === 6 && (\n <Typography variant=\"destructive\">{t(\"Incorrect code. Please try again.\")}</Typography>\n )}\n <div className='flex'>\n <Button\n variant='secondary'\n onClick={() => {\n setGeneratedSecret(null);\n setQrCodeUrl(null);\n setMfaCode(\"\");\n }}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </>\n )}\n <div className='flex gap-2'>\n {isEnabled ? (\n <Button\n variant='secondary'\n onClick={async () => {\n await user.update({\n totpMultiFactorSecret: null,\n });\n }}\n >\n {t(\"Disable MFA\")}\n </Button>\n ) : !generatedSecret && (\n <Button\n variant='secondary'\n onClick={async () => {\n const secret = generateRandomValues(new Uint8Array(20));\n setQrCodeUrl(await generateTotpQrCode(project, user, secret));\n setGeneratedSecret(secret);\n }}\n >\n {t(\"Enable MFA\")}\n </Button>\n )}\n </div>\n </div>\n </Section>\n );\n}\n\n\nasync function generateTotpQrCode(project: Project, user: CurrentUser, secret: Uint8Array) {\n const uri = createTOTPKeyURI(project.displayName, user.primaryEmail ?? user.id, secret, 30, 6);\n return await QRCode.toDataURL(uri) as any;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,iBAA6C;AAC7C,gCAAiC;AACjC,oBAAqC;AACrC,oBAAyB;AACzB,sBAA2C;AAC3C,sBAA0C;AAC1C,aAAwB;AACxB,mBAAoC;AAEpC,mBAAqC;AACrC,0BAA+B;AAC/B,qBAAwB;AAgBhB;AAdD,SAAS,WAAW,OAExB;AACD,QAAM,EAAE,EAAE,QAAI,oCAAe;AAC7B,QAAM,cAAU,0BAAY,EAAE,WAAW;AACzC,QAAM,WAAO,sBAAQ,EAAE,IAAI,OAAO,WAAW,gBAAgB,QAAQ,CAAC;AAGtE,MAAI,OAAO,YAAY,CAAC,MAAM;AAC5B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,6BAA6B;AAAA,QACtC,aAAa,EAAE,+CAA+C;AAAA,QAE9D,sDAAC,8BAAW,SAAQ,aAAa,YAAE,+CAA+C,GAAE;AAAA;AAAA,IACtF;AAAA,EAEJ;AAEA,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AACA,QAAM,CAAC,iBAAiB,kBAAkB,QAAI,uBAA4B,IAAI;AAC9E,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAwB,IAAI;AAC9D,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAiB,EAAE;AACjD,QAAM,CAAC,cAAc,eAAe,QAAI,uBAAS,KAAK;AACtD,QAAM,YAAY,KAAK;AAEvB,QAAM,CAAC,cAAc,SAAS,QAAI,4CAAiB,YAAY;AAC7D,UAAM,KAAK,OAAO;AAAA,MAChB,uBAAuB;AAAA,IACzB,CAAC;AACD,uBAAmB,IAAI;AACvB,iBAAa,IAAI;AACjB,eAAW,EAAE;AAAA,EACf,GAAG,CAAC,iBAAiB,IAAI,CAAC;AAE1B,8BAAU,MAAM;AACd,oBAAgB,KAAK;AACrB,oDAA2B,YAAY;AACrC,UAAI,uBAAmB,uBAAW,iBAAiB,IAAI,GAAG,OAAO,GAAG;AAClE,cAAM,aAAa;AAAA,MACrB;AACA,sBAAgB,IAAI;AAAA,IACtB,CAAC;AAAA,EACH,GAAG,CAAC,SAAS,iBAAiB,YAAY,CAAC;AAE3C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,6BAA6B;AAAA,MACtC,aAAa,YACT,EAAE,mDAAmD,IACrD,EAAE,oDAAoD;AAAA,MAE1D,uDAAC,SAAI,WAAU,uBACZ;AAAA,SAAC,aAAa,mBACb,4EACE;AAAA,sDAAC,8BAAY,YAAE,gDAAgD,GAAE;AAAA,UACjE,4CAAC,SAAI,OAAO,KAAK,QAAQ,KAAK,KAAK,iBAAa,wBAAS,iCAAiC,GAAG,KAAK,EAAE,0CAA0C,GAAG;AAAA,UACjJ,4CAAC,8BAAY,YAAE,sCAAsC,GAAE;AAAA,UACvD;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,CAAC,MAAM;AACf,gCAAgB,KAAK;AACrB,2BAAW,EAAE,OAAO,KAAK;AAAA,cAC3B;AAAA,cACA,aAAY;AAAA,cACZ,WAAW;AAAA,cACX,UAAU;AAAA;AAAA,UACZ;AAAA,UACC,gBAAgB,QAAQ,WAAW,KAClC,4CAAC,8BAAW,SAAQ,eAAe,YAAE,mCAAmC,GAAE;AAAA,UAE5E,4CAAC,SAAI,WAAU,QACb;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,SAAS,MAAM;AACb,mCAAmB,IAAI;AACvB,6BAAa,IAAI;AACjB,2BAAW,EAAE;AAAA,cACf;AAAA,cAEC,YAAE,QAAQ;AAAA;AAAA,UACb,GACF;AAAA,WACF;AAAA,QAEF,4CAAC,SAAI,WAAU,cACZ,sBACC;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,SAAS,YAAY;AACnB,oBAAM,KAAK,OAAO;AAAA,gBAChB,uBAAuB;AAAA,cACzB,CAAC;AAAA,YACH;AAAA,YAEC,YAAE,aAAa;AAAA;AAAA,QAClB,IACE,CAAC,mBACH;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,SAAS,YAAY;AACnB,oBAAM,aAAS,oCAAqB,IAAI,WAAW,EAAE,CAAC;AACtD,2BAAa,MAAM,mBAAmB,SAAS,MAAM,MAAM,CAAC;AAC5D,iCAAmB,MAAM;AAAA,YAC3B;AAAA,YAEC,YAAE,YAAY;AAAA;AAAA,QACjB,GAEJ;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;AAGA,eAAe,mBAAmB,SAAkB,MAAmB,QAAoB;AACzF,QAAM,UAAM,6BAAiB,QAAQ,aAAa,KAAK,gBAAgB,KAAK,IAAI,QAAQ,IAAI,CAAC;AAC7F,SAAO,MAAa,iBAAU,GAAG;AACnC;","names":[]}
@@ -25,13 +25,26 @@ __export(otp_section_exports, {
25
25
  module.exports = __toCommonJS(otp_section_exports);
26
26
  var import_stack_ui = require("@stackframe/stack-ui");
27
27
  var import_react = require("react");
28
- var import_hooks = require("../../../lib/hooks");
29
- var import_translations = require("../../../lib/translations");
30
- var import_section = require("../section");
28
+ var import_hooks = require("../../../lib/hooks.js");
29
+ var import_translations = require("../../../lib/translations.js");
30
+ var import_section = require("../section.js");
31
31
  var import_jsx_runtime = require("react/jsx-runtime");
32
- function OtpSection() {
32
+ function OtpSection(props) {
33
33
  const { t } = (0, import_translations.useTranslation)();
34
- const user = (0, import_hooks.useUser)({ or: "throw" });
34
+ const user = (0, import_hooks.useUser)({ or: props?.mockMode ? "return-null" : "throw" });
35
+ if (props?.mockMode && !user) {
36
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
37
+ import_section.Section,
38
+ {
39
+ title: t("One-Time Password"),
40
+ description: t("OTP management is not available in demo mode."),
41
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", children: t("OTP management is not available in demo mode.") })
42
+ }
43
+ );
44
+ }
45
+ if (!user) {
46
+ return null;
47
+ }
35
48
  const project = (0, import_hooks.useStackApp)().useProject();
36
49
  const contactChannels = user.useContactChannels();
37
50
  const isLastAuth = user.otpAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.passkeyAuthEnabled;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/otp-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Button, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useStackApp, useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function OtpSection() {\n const { t } = useTranslation();\n const user = useUser({ or: \"throw\" });\n const project = useStackApp().useProject();\n const contactChannels = user.useContactChannels();\n const isLastAuth = user.otpAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.passkeyAuthEnabled;\n const [disabling, setDisabling] = useState(false);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const hasValidEmail = contactChannels.filter(x => x.type === 'email' && x.isVerified && x.usedForAuth).length > 0;\n\n if (!project.config.magicLinkEnabled) {\n return null;\n }\n\n const handleDisableOTP = async () => {\n await user.update({ otpAuthEnabled: false });\n setDisabling(false);\n };\n\n return (\n <Section title={t(\"OTP sign-in\")} description={user.otpAuthEnabled ? t(\"OTP/magic link sign-in is currently enabled.\") : t(\"Enable sign-in via magic link or OTP sent to your sign-in emails.\")}>\n <div className='flex md:justify-end'>\n {hasValidEmail ? (\n user.otpAuthEnabled ? (\n !isLastAuth ? (\n !disabling ? (\n <Button\n variant='secondary'\n onClick={() => setDisabling(true)}\n >\n {t(\"Disable OTP\")}\n </Button>\n ) : (\n <div className='flex flex-col gap-2'>\n <Typography variant='destructive'>\n {t(\"Are you sure you want to disable OTP sign-in? You will not be able to sign in with only emails anymore.\")}\n </Typography>\n <div className='flex gap-2'>\n <Button\n variant='destructive'\n onClick={handleDisableOTP}\n >\n {t(\"Disable\")}\n </Button>\n <Button\n variant='secondary'\n onClick={() => setDisabling(false)}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </div>\n )\n ) : (\n <Typography variant='secondary' type='label'>{t(\"OTP sign-in is enabled and cannot be disabled as it is currently the only sign-in method\")}</Typography>\n )\n ) : (\n <Button\n variant='secondary'\n onClick={async () => {\n await user.update({ otpAuthEnabled: true });\n }}\n >\n {t(\"Enable OTP\")}\n </Button>\n )\n ) : (\n <Typography variant='secondary' type='label'>{t(\"To enable OTP sign-in, please add a verified sign-in email.\")}</Typography>\n )}\n </div>\n </Section>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,sBAAmC;AACnC,mBAAyB;AACzB,mBAAqC;AACrC,0BAA+B;AAC/B,qBAAwB;AA6BR;AA3BT,SAAS,aAAa;AAC3B,QAAM,EAAE,EAAE,QAAI,oCAAe;AAC7B,QAAM,WAAO,sBAAQ,EAAE,IAAI,QAAQ,CAAC;AACpC,QAAM,cAAU,0BAAY,EAAE,WAAW;AACzC,QAAM,kBAAkB,KAAK,mBAAmB;AAChD,QAAM,aAAa,KAAK,kBAAkB,CAAC,KAAK,eAAe,KAAK,eAAe,WAAW,KAAK,CAAC,KAAK;AACzG,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,KAAK;AAGhD,QAAM,gBAAgB,gBAAgB,OAAO,OAAK,EAAE,SAAS,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS;AAEhH,MAAI,CAAC,QAAQ,OAAO,kBAAkB;AACpC,WAAO;AAAA,EACT;AAEA,QAAM,mBAAmB,YAAY;AACnC,UAAM,KAAK,OAAO,EAAE,gBAAgB,MAAM,CAAC;AAC3C,iBAAa,KAAK;AAAA,EACpB;AAEA,SACE,4CAAC,0BAAQ,OAAO,EAAE,aAAa,GAAG,aAAa,KAAK,iBAAiB,EAAE,8CAA8C,IAAI,EAAE,mEAAmE,GAC5L,sDAAC,SAAI,WAAU,uBACZ,0BACC,KAAK,iBACH,CAAC,aACC,CAAC,YACC;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAS,MAAM,aAAa,IAAI;AAAA,MAE/B,YAAE,aAAa;AAAA;AAAA,EAClB,IAEA,6CAAC,SAAI,WAAU,uBACb;AAAA,gDAAC,8BAAW,SAAQ,eACjB,YAAE,yGAAyG,GAC9G;AAAA,IACA,6CAAC,SAAI,WAAU,cACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAS;AAAA,UAER,YAAE,SAAS;AAAA;AAAA,MACd;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAS,MAAM,aAAa,KAAK;AAAA,UAEhC,YAAE,QAAQ;AAAA;AAAA,MACb;AAAA,OACF;AAAA,KACF,IAGF,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,0FAA0F,GAAE,IAG9I;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAS,YAAY;AACnB,cAAM,KAAK,OAAO,EAAE,gBAAgB,KAAK,CAAC;AAAA,MAC5C;AAAA,MAEC,YAAE,YAAY;AAAA;AAAA,EACjB,IAGF,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,6DAA6D,GAAE,GAEnH,GACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/otp-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Button, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useStackApp, useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function OtpSection(props?: {\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const user = useUser({ or: props?.mockMode ? 'return-null' : \"throw\" });\n\n // In mock mode, show a placeholder message\n if (props?.mockMode && !user) {\n return (\n <Section\n title={t(\"One-Time Password\")}\n description={t(\"OTP management is not available in demo mode.\")}\n >\n <Typography variant='secondary'>{t(\"OTP management is not available in demo mode.\")}</Typography>\n </Section>\n );\n }\n\n if (!user) {\n return null; // This shouldn't happen in non-mock mode due to throw\n }\n const project = useStackApp().useProject();\n const contactChannels = user.useContactChannels();\n const isLastAuth = user.otpAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.passkeyAuthEnabled;\n const [disabling, setDisabling] = useState(false);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const hasValidEmail = contactChannels.filter(x => x.type === 'email' && x.isVerified && x.usedForAuth).length > 0;\n\n if (!project.config.magicLinkEnabled) {\n return null;\n }\n\n const handleDisableOTP = async () => {\n await user.update({ otpAuthEnabled: false });\n setDisabling(false);\n };\n\n return (\n <Section title={t(\"OTP sign-in\")} description={user.otpAuthEnabled ? t(\"OTP/magic link sign-in is currently enabled.\") : t(\"Enable sign-in via magic link or OTP sent to your sign-in emails.\")}>\n <div className='flex md:justify-end'>\n {hasValidEmail ? (\n user.otpAuthEnabled ? (\n !isLastAuth ? (\n !disabling ? (\n <Button\n variant='secondary'\n onClick={() => setDisabling(true)}\n >\n {t(\"Disable OTP\")}\n </Button>\n ) : (\n <div className='flex flex-col gap-2'>\n <Typography variant='destructive'>\n {t(\"Are you sure you want to disable OTP sign-in? You will not be able to sign in with only emails anymore.\")}\n </Typography>\n <div className='flex gap-2'>\n <Button\n variant='destructive'\n onClick={handleDisableOTP}\n >\n {t(\"Disable\")}\n </Button>\n <Button\n variant='secondary'\n onClick={() => setDisabling(false)}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </div>\n )\n ) : (\n <Typography variant='secondary' type='label'>{t(\"OTP sign-in is enabled and cannot be disabled as it is currently the only sign-in method\")}</Typography>\n )\n ) : (\n <Button\n variant='secondary'\n onClick={async () => {\n await user.update({ otpAuthEnabled: true });\n }}\n >\n {t(\"Enable OTP\")}\n </Button>\n )\n ) : (\n <Typography variant='secondary' type='label'>{t(\"To enable OTP sign-in, please add a verified sign-in email.\")}</Typography>\n )}\n </div>\n </Section>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,sBAAmC;AACnC,mBAAyB;AACzB,mBAAqC;AACrC,0BAA+B;AAC/B,qBAAwB;AAehB;AAbD,SAAS,WAAW,OAExB;AACD,QAAM,EAAE,EAAE,QAAI,oCAAe;AAC7B,QAAM,WAAO,sBAAQ,EAAE,IAAI,OAAO,WAAW,gBAAgB,QAAQ,CAAC;AAGtE,MAAI,OAAO,YAAY,CAAC,MAAM;AAC5B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,mBAAmB;AAAA,QAC5B,aAAa,EAAE,+CAA+C;AAAA,QAE9D,sDAAC,8BAAW,SAAQ,aAAa,YAAE,+CAA+C,GAAE;AAAA;AAAA,IACtF;AAAA,EAEJ;AAEA,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AACA,QAAM,cAAU,0BAAY,EAAE,WAAW;AACzC,QAAM,kBAAkB,KAAK,mBAAmB;AAChD,QAAM,aAAa,KAAK,kBAAkB,CAAC,KAAK,eAAe,KAAK,eAAe,WAAW,KAAK,CAAC,KAAK;AACzG,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,KAAK;AAGhD,QAAM,gBAAgB,gBAAgB,OAAO,OAAK,EAAE,SAAS,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS;AAEhH,MAAI,CAAC,QAAQ,OAAO,kBAAkB;AACpC,WAAO;AAAA,EACT;AAEA,QAAM,mBAAmB,YAAY;AACnC,UAAM,KAAK,OAAO,EAAE,gBAAgB,MAAM,CAAC;AAC3C,iBAAa,KAAK;AAAA,EACpB;AAEA,SACE,4CAAC,0BAAQ,OAAO,EAAE,aAAa,GAAG,aAAa,KAAK,iBAAiB,EAAE,8CAA8C,IAAI,EAAE,mEAAmE,GAC5L,sDAAC,SAAI,WAAU,uBACZ,0BACC,KAAK,iBACH,CAAC,aACC,CAAC,YACC;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAS,MAAM,aAAa,IAAI;AAAA,MAE/B,YAAE,aAAa;AAAA;AAAA,EAClB,IAEA,6CAAC,SAAI,WAAU,uBACb;AAAA,gDAAC,8BAAW,SAAQ,eACjB,YAAE,yGAAyG,GAC9G;AAAA,IACA,6CAAC,SAAI,WAAU,cACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAS;AAAA,UAER,YAAE,SAAS;AAAA;AAAA,MACd;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAS,MAAM,aAAa,KAAK;AAAA,UAEhC,YAAE,QAAQ;AAAA;AAAA,MACb;AAAA,OACF;AAAA,KACF,IAGF,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,0FAA0F,GAAE,IAG9I;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAS,YAAY;AACnB,cAAM,KAAK,OAAO,EAAE,gBAAgB,KAAK,CAAC;AAAA,MAC5C;AAAA,MAEC,YAAE,YAAY;AAAA;AAAA,EACjB,IAGF,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,6DAA6D,GAAE,GAEnH,GACF;AAEJ;","names":[]}
@@ -25,14 +25,27 @@ __export(passkey_section_exports, {
25
25
  module.exports = __toCommonJS(passkey_section_exports);
26
26
  var import_stack_ui = require("@stackframe/stack-ui");
27
27
  var import_react = require("react");
28
- var import__ = require("../../..");
29
- var import_hooks = require("../../../lib/hooks");
30
- var import_translations = require("../../../lib/translations");
31
- var import_section = require("../section");
28
+ var import__ = require("../../../index.js");
29
+ var import_hooks = require("../../../lib/hooks.js");
30
+ var import_translations = require("../../../lib/translations.js");
31
+ var import_section = require("../section.js");
32
32
  var import_jsx_runtime = require("react/jsx-runtime");
33
- function PasskeySection() {
33
+ function PasskeySection(props) {
34
34
  const { t } = (0, import_translations.useTranslation)();
35
- const user = (0, import_hooks.useUser)({ or: "throw" });
35
+ const user = (0, import_hooks.useUser)({ or: props?.mockMode ? "return-null" : "throw" });
36
+ if (props?.mockMode && !user) {
37
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
38
+ import_section.Section,
39
+ {
40
+ title: t("Passkey"),
41
+ description: t("Passkey management is not available in demo mode."),
42
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", children: t("Passkey management is not available in demo mode.") })
43
+ }
44
+ );
45
+ }
46
+ if (!user) {
47
+ return null;
48
+ }
36
49
  const stackApp = (0, import__.useStackApp)();
37
50
  const project = stackApp.useProject();
38
51
  const contactChannels = user.useContactChannels();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/passkey-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Button, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useStackApp } from \"../../..\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function PasskeySection() {\n const { t } = useTranslation();\n const user = useUser({ or: \"throw\" });\n const stackApp = useStackApp();\n const project = stackApp.useProject();\n const contactChannels = user.useContactChannels();\n\n\n // passkey is enabled if there is a passkey\n const hasPasskey = user.passkeyAuthEnabled;\n\n const isLastAuth = user.passkeyAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.otpAuthEnabled;\n const [showConfirmationModal, setShowConfirmationModal] = useState(false);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const hasValidEmail = contactChannels.filter(x => x.type === 'email' && x.isVerified && x.usedForAuth).length > 0;\n\n if (!project.config.passkeyEnabled) {\n return null;\n }\n\n const handleDeletePasskey = async () => {\n await user.update({ passkeyAuthEnabled: false });\n setShowConfirmationModal(false);\n };\n\n\n const handleAddNewPasskey = async () => {\n await user.registerPasskey();\n };\n\n return (\n <>\n <Section title={t(\"Passkey\")} description={hasPasskey ? t(\"Passkey registered\") : t(\"Register a passkey\")}>\n <div className='flex md:justify-end gap-2'>\n {!hasValidEmail && (\n <Typography variant='secondary' type='label'>{t(\"To enable Passkey sign-in, please add a verified sign-in email.\")}</Typography>\n )}\n {hasValidEmail && hasPasskey && isLastAuth && (\n <Typography variant='secondary' type='label'>{t(\"Passkey sign-in is enabled and cannot be disabled as it is currently the only sign-in method\")}</Typography>\n )}\n {!hasPasskey && hasValidEmail && (\n <div>\n <Button onClick={handleAddNewPasskey} variant='secondary'>{t(\"Add new passkey\")}</Button>\n </div>\n )}\n {hasValidEmail && hasPasskey && !isLastAuth && !showConfirmationModal && (\n <Button\n variant='secondary'\n onClick={() => setShowConfirmationModal(true)}\n >\n {t(\"Delete Passkey\")}\n </Button>\n )}\n {hasValidEmail && hasPasskey && !isLastAuth && showConfirmationModal && (\n <div className='flex flex-col gap-2'>\n <Typography variant='destructive'>\n {t(\"Are you sure you want to disable Passkey sign-in? You will not be able to sign in with your passkey anymore.\")}\n </Typography>\n <div className='flex gap-2'>\n <Button\n variant='destructive'\n onClick={handleDeletePasskey}\n >\n {t(\"Disable\")}\n </Button>\n <Button\n variant='secondary'\n onClick={() => setShowConfirmationModal(false)}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </div>\n )}\n </div>\n </Section>\n\n\n </>\n\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,sBAAmC;AACnC,mBAAyB;AACzB,eAA4B;AAC5B,mBAAwB;AACxB,0BAA+B;AAC/B,qBAAwB;AAkCpB;AAhCG,SAAS,iBAAiB;AAC/B,QAAM,EAAE,EAAE,QAAI,oCAAe;AAC7B,QAAM,WAAO,sBAAQ,EAAE,IAAI,QAAQ,CAAC;AACpC,QAAM,eAAW,sBAAY;AAC7B,QAAM,UAAU,SAAS,WAAW;AACpC,QAAM,kBAAkB,KAAK,mBAAmB;AAIhD,QAAM,aAAa,KAAK;AAExB,QAAM,aAAa,KAAK,sBAAsB,CAAC,KAAK,eAAe,KAAK,eAAe,WAAW,KAAK,CAAC,KAAK;AAC7G,QAAM,CAAC,uBAAuB,wBAAwB,QAAI,uBAAS,KAAK;AAGxE,QAAM,gBAAgB,gBAAgB,OAAO,OAAK,EAAE,SAAS,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS;AAEhH,MAAI,CAAC,QAAQ,OAAO,gBAAgB;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,sBAAsB,YAAY;AACtC,UAAM,KAAK,OAAO,EAAE,oBAAoB,MAAM,CAAC;AAC/C,6BAAyB,KAAK;AAAA,EAChC;AAGA,QAAM,sBAAsB,YAAY;AACtC,UAAM,KAAK,gBAAgB;AAAA,EAC7B;AAEA,SACE,2EACE,sDAAC,0BAAQ,OAAO,EAAE,SAAS,GAAG,aAAa,aAAa,EAAE,oBAAoB,IAAI,EAAE,oBAAoB,GACtG,uDAAC,SAAI,WAAU,6BACZ;AAAA,KAAC,iBACA,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,iEAAiE,GAAE;AAAA,IAEpH,iBAAiB,cAAc,cAC9B,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,8FAA8F,GAAE;AAAA,IAEjJ,CAAC,cAAc,iBACd,4CAAC,SACC,sDAAC,0BAAO,SAAS,qBAAqB,SAAQ,aAAa,YAAE,iBAAiB,GAAE,GAClF;AAAA,IAED,iBAAiB,cAAc,CAAC,cAAc,CAAC,yBAC9C;AAAA,MAAC;AAAA;AAAA,QACC,SAAQ;AAAA,QACR,SAAS,MAAM,yBAAyB,IAAI;AAAA,QAE3C,YAAE,gBAAgB;AAAA;AAAA,IACrB;AAAA,IAED,iBAAiB,cAAc,CAAC,cAAc,yBAC7C,6CAAC,SAAI,WAAU,uBACb;AAAA,kDAAC,8BAAW,SAAQ,eACjB,YAAE,8GAA8G,GACnH;AAAA,MACA,6CAAC,SAAI,WAAU,cACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,SAAS;AAAA,YAER,YAAE,SAAS;AAAA;AAAA,QACd;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,SAAS,MAAM,yBAAyB,KAAK;AAAA,YAE5C,YAAE,QAAQ;AAAA;AAAA,QACb;AAAA,SACF;AAAA,OACF;AAAA,KAEJ,GACF,GAGF;AAGJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/passkey-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Button, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useStackApp } from \"../../..\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function PasskeySection(props?: {\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const user = useUser({ or: props?.mockMode ? 'return-null' : \"throw\" });\n\n // In mock mode, show a placeholder message\n if (props?.mockMode && !user) {\n return (\n <Section\n title={t(\"Passkey\")}\n description={t(\"Passkey management is not available in demo mode.\")}\n >\n <Typography variant='secondary'>{t(\"Passkey management is not available in demo mode.\")}</Typography>\n </Section>\n );\n }\n\n if (!user) {\n return null; // This shouldn't happen in non-mock mode due to throw\n }\n const stackApp = useStackApp();\n const project = stackApp.useProject();\n const contactChannels = user.useContactChannels();\n\n\n // passkey is enabled if there is a passkey\n const hasPasskey = user.passkeyAuthEnabled;\n\n const isLastAuth = user.passkeyAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.otpAuthEnabled;\n const [showConfirmationModal, setShowConfirmationModal] = useState(false);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const hasValidEmail = contactChannels.filter(x => x.type === 'email' && x.isVerified && x.usedForAuth).length > 0;\n\n if (!project.config.passkeyEnabled) {\n return null;\n }\n\n const handleDeletePasskey = async () => {\n await user.update({ passkeyAuthEnabled: false });\n setShowConfirmationModal(false);\n };\n\n\n const handleAddNewPasskey = async () => {\n await user.registerPasskey();\n };\n\n return (\n <>\n <Section title={t(\"Passkey\")} description={hasPasskey ? t(\"Passkey registered\") : t(\"Register a passkey\")}>\n <div className='flex md:justify-end gap-2'>\n {!hasValidEmail && (\n <Typography variant='secondary' type='label'>{t(\"To enable Passkey sign-in, please add a verified sign-in email.\")}</Typography>\n )}\n {hasValidEmail && hasPasskey && isLastAuth && (\n <Typography variant='secondary' type='label'>{t(\"Passkey sign-in is enabled and cannot be disabled as it is currently the only sign-in method\")}</Typography>\n )}\n {!hasPasskey && hasValidEmail && (\n <div>\n <Button onClick={handleAddNewPasskey} variant='secondary'>{t(\"Add new passkey\")}</Button>\n </div>\n )}\n {hasValidEmail && hasPasskey && !isLastAuth && !showConfirmationModal && (\n <Button\n variant='secondary'\n onClick={() => setShowConfirmationModal(true)}\n >\n {t(\"Delete Passkey\")}\n </Button>\n )}\n {hasValidEmail && hasPasskey && !isLastAuth && showConfirmationModal && (\n <div className='flex flex-col gap-2'>\n <Typography variant='destructive'>\n {t(\"Are you sure you want to disable Passkey sign-in? You will not be able to sign in with your passkey anymore.\")}\n </Typography>\n <div className='flex gap-2'>\n <Button\n variant='destructive'\n onClick={handleDeletePasskey}\n >\n {t(\"Disable\")}\n </Button>\n <Button\n variant='secondary'\n onClick={() => setShowConfirmationModal(false)}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </div>\n )}\n </div>\n </Section>\n\n\n </>\n\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,sBAAmC;AACnC,mBAAyB;AACzB,eAA4B;AAC5B,mBAAwB;AACxB,0BAA+B;AAC/B,qBAAwB;AAehB;AAbD,SAAS,eAAe,OAE5B;AACD,QAAM,EAAE,EAAE,QAAI,oCAAe;AAC7B,QAAM,WAAO,sBAAQ,EAAE,IAAI,OAAO,WAAW,gBAAgB,QAAQ,CAAC;AAGtE,MAAI,OAAO,YAAY,CAAC,MAAM;AAC5B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,SAAS;AAAA,QAClB,aAAa,EAAE,mDAAmD;AAAA,QAElE,sDAAC,8BAAW,SAAQ,aAAa,YAAE,mDAAmD,GAAE;AAAA;AAAA,IAC1F;AAAA,EAEJ;AAEA,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AACA,QAAM,eAAW,sBAAY;AAC7B,QAAM,UAAU,SAAS,WAAW;AACpC,QAAM,kBAAkB,KAAK,mBAAmB;AAIhD,QAAM,aAAa,KAAK;AAExB,QAAM,aAAa,KAAK,sBAAsB,CAAC,KAAK,eAAe,KAAK,eAAe,WAAW,KAAK,CAAC,KAAK;AAC7G,QAAM,CAAC,uBAAuB,wBAAwB,QAAI,uBAAS,KAAK;AAGxE,QAAM,gBAAgB,gBAAgB,OAAO,OAAK,EAAE,SAAS,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS;AAEhH,MAAI,CAAC,QAAQ,OAAO,gBAAgB;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,sBAAsB,YAAY;AACtC,UAAM,KAAK,OAAO,EAAE,oBAAoB,MAAM,CAAC;AAC/C,6BAAyB,KAAK;AAAA,EAChC;AAGA,QAAM,sBAAsB,YAAY;AACtC,UAAM,KAAK,gBAAgB;AAAA,EAC7B;AAEA,SACE,2EACE,sDAAC,0BAAQ,OAAO,EAAE,SAAS,GAAG,aAAa,aAAa,EAAE,oBAAoB,IAAI,EAAE,oBAAoB,GACtG,uDAAC,SAAI,WAAU,6BACZ;AAAA,KAAC,iBACA,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,iEAAiE,GAAE;AAAA,IAEpH,iBAAiB,cAAc,cAC9B,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,8FAA8F,GAAE;AAAA,IAEjJ,CAAC,cAAc,iBACd,4CAAC,SACC,sDAAC,0BAAO,SAAS,qBAAqB,SAAQ,aAAa,YAAE,iBAAiB,GAAE,GAClF;AAAA,IAED,iBAAiB,cAAc,CAAC,cAAc,CAAC,yBAC9C;AAAA,MAAC;AAAA;AAAA,QACC,SAAQ;AAAA,QACR,SAAS,MAAM,yBAAyB,IAAI;AAAA,QAE3C,YAAE,gBAAgB;AAAA;AAAA,IACrB;AAAA,IAED,iBAAiB,cAAc,CAAC,cAAc,yBAC7C,6CAAC,SAAI,WAAU,uBACb;AAAA,kDAAC,8BAAW,SAAQ,eACjB,YAAE,8GAA8G,GACnH;AAAA,MACA,6CAAC,SAAI,WAAU,cACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,SAAS;AAAA,YAER,YAAE,SAAS;AAAA;AAAA,QACd;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,SAAS,MAAM,yBAAyB,KAAK;AAAA,YAE5C,YAAE,QAAQ;AAAA;AAAA,QACb;AAAA,SACF;AAAA,OACF;AAAA,KAEJ,GACF,GAGF;AAGJ;","names":[]}
@@ -41,15 +41,28 @@ var import_stack_ui = require("@stackframe/stack-ui");
41
41
  var import_react = require("react");
42
42
  var import_react_hook_form = require("react-hook-form");
43
43
  var yup = __toESM(require("yup"));
44
- var import__ = require("../../..");
45
- var import_form_warning = require("../../../components/elements/form-warning");
46
- var import_hooks = require("../../../lib/hooks");
47
- var import_translations = require("../../../lib/translations");
48
- var import_section = require("../section");
44
+ var import__ = require("../../../index.js");
45
+ var import_form_warning = require("../../../components/elements/form-warning.js");
46
+ var import_hooks = require("../../../lib/hooks.js");
47
+ var import_translations = require("../../../lib/translations.js");
48
+ var import_section = require("../section.js");
49
49
  var import_jsx_runtime = require("react/jsx-runtime");
50
- function PasswordSection() {
50
+ function PasswordSection(props) {
51
51
  const { t } = (0, import_translations.useTranslation)();
52
- const user = (0, import_hooks.useUser)({ or: "throw" });
52
+ const user = (0, import_hooks.useUser)({ or: props?.mockMode ? "return-null" : "throw" });
53
+ if (props?.mockMode && !user) {
54
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
55
+ import_section.Section,
56
+ {
57
+ title: t("Password"),
58
+ description: t("Password management is not available in demo mode."),
59
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", children: t("Password management is not available in demo mode.") })
60
+ }
61
+ );
62
+ }
63
+ if (!user) {
64
+ return null;
65
+ }
53
66
  const contactChannels = user.useContactChannels();
54
67
  const [changingPassword, setChangingPassword] = (0, import_react.useState)(false);
55
68
  const [loading, setLoading] = (0, import_react.useState)(false);