@better-auth-ui/solid 1.6.19

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 (270) hide show
  1. package/dist/components/auth/email/email-changed.d.ts +59 -0
  2. package/dist/components/auth/email/email-changed.js +264 -0
  3. package/dist/components/auth/email/email-styles.d.ts +48 -0
  4. package/dist/components/auth/email/email-styles.js +107 -0
  5. package/dist/components/auth/email/email-verification.d.ts +51 -0
  6. package/dist/components/auth/email/email-verification.js +212 -0
  7. package/dist/components/auth/email/index.d.ts +9 -0
  8. package/dist/components/auth/email/magic-link.d.ts +53 -0
  9. package/dist/components/auth/email/magic-link.js +213 -0
  10. package/dist/components/auth/email/new-device.d.ts +77 -0
  11. package/dist/components/auth/email/new-device.js +326 -0
  12. package/dist/components/auth/email/organization-invitation.d.ts +70 -0
  13. package/dist/components/auth/email/organization-invitation.js +260 -0
  14. package/dist/components/auth/email/otp-email.d.ts +49 -0
  15. package/dist/components/auth/email/otp-email.js +191 -0
  16. package/dist/components/auth/email/password-changed.d.ts +56 -0
  17. package/dist/components/auth/email/password-changed.js +236 -0
  18. package/dist/components/auth/email/reset-password.d.ts +51 -0
  19. package/dist/components/auth/email/reset-password.js +212 -0
  20. package/dist/email.d.ts +18 -0
  21. package/dist/email.js +10 -0
  22. package/dist/hooks/auth/use-authenticate.d.ts +3 -0
  23. package/dist/hooks/auth/use-authenticate.js +18 -0
  24. package/dist/hooks/auth/use-sign-out.d.ts +2 -0
  25. package/dist/hooks/auth/use-sign-out.js +17 -0
  26. package/dist/hooks/auth/use-user.d.ts +213 -0
  27. package/dist/hooks/auth/use-user.js +13 -0
  28. package/dist/hooks/use-auth-mutation.d.ts +6 -0
  29. package/dist/hooks/use-auth-mutation.js +11 -0
  30. package/dist/hooks/use-auth-query.d.ts +5 -0
  31. package/dist/hooks/use-auth-query.js +17 -0
  32. package/dist/index.d.ts +48 -0
  33. package/dist/index.js +68 -0
  34. package/dist/lib/auth-client.d.ts +26 -0
  35. package/dist/lib/auth-config.d.ts +12 -0
  36. package/dist/lib/auth-config.js +21 -0
  37. package/dist/lib/auth-plugin.d.ts +16 -0
  38. package/dist/lib/auth-provider.d.ts +12 -0
  39. package/dist/lib/auth-provider.js +36 -0
  40. package/dist/lib/auth-server.d.ts +16 -0
  41. package/dist/lib/auth-utils.d.ts +3 -0
  42. package/dist/lib/auth-utils.js +12 -0
  43. package/dist/lib/fetch-options-provider.d.ts +23 -0
  44. package/dist/lib/fetch-options-provider.js +29 -0
  45. package/dist/lib/mutation-invalidator.d.ts +11 -0
  46. package/dist/lib/mutation-invalidator.js +20 -0
  47. package/dist/lib/provider-icons.d.ts +8 -0
  48. package/dist/lib/provider-icons.js +5 -0
  49. package/dist/mutations/api-key/create-api-key-mutation.d.ts +71 -0
  50. package/dist/mutations/api-key/create-api-key-mutation.js +12 -0
  51. package/dist/mutations/api-key/delete-api-key-mutation.d.ts +23 -0
  52. package/dist/mutations/api-key/delete-api-key-mutation.js +12 -0
  53. package/dist/mutations/auth/request-password-reset-mutation.d.ts +11 -0
  54. package/dist/mutations/auth/request-password-reset-mutation.js +8 -0
  55. package/dist/mutations/auth/reset-password-mutation.d.ts +13 -0
  56. package/dist/mutations/auth/reset-password-mutation.js +8 -0
  57. package/dist/mutations/auth/send-verification-email-mutation.d.ts +11 -0
  58. package/dist/mutations/auth/send-verification-email-mutation.js +8 -0
  59. package/dist/mutations/auth/sign-in-email-mutation.d.ts +15 -0
  60. package/dist/mutations/auth/sign-in-email-mutation.js +8 -0
  61. package/dist/mutations/auth/sign-in-social-mutation.d.ts +53 -0
  62. package/dist/mutations/auth/sign-in-social-mutation.js +8 -0
  63. package/dist/mutations/auth/sign-out-mutation.d.ts +6 -0
  64. package/dist/mutations/auth/sign-out-mutation.js +8 -0
  65. package/dist/mutations/auth/sign-up-email-mutation.d.ts +17 -0
  66. package/dist/mutations/auth/sign-up-email-mutation.js +8 -0
  67. package/dist/mutations/auth-mutation-options.d.ts +10 -0
  68. package/dist/mutations/auth-mutation-options.js +19 -0
  69. package/dist/mutations/create-auth-mutation.d.ts +10 -0
  70. package/dist/mutations/create-auth-mutation.js +20 -0
  71. package/dist/mutations/magic-link/sign-in-magic-link-mutation.d.ts +19 -0
  72. package/dist/mutations/magic-link/sign-in-magic-link-mutation.js +8 -0
  73. package/dist/mutations/multi-session/revoke-multi-session-mutation.d.ts +19 -0
  74. package/dist/mutations/multi-session/revoke-multi-session-mutation.js +12 -0
  75. package/dist/mutations/multi-session/set-active-session-mutation.d.ts +19 -0
  76. package/dist/mutations/multi-session/set-active-session-mutation.js +13 -0
  77. package/dist/mutations/organization/accept-invitation-mutation.d.ts +18 -0
  78. package/dist/mutations/organization/accept-invitation-mutation.js +12 -0
  79. package/dist/mutations/organization/cancel-invitation-mutation.d.ts +18 -0
  80. package/dist/mutations/organization/cancel-invitation-mutation.js +12 -0
  81. package/dist/mutations/organization/check-slug-mutation.d.ts +18 -0
  82. package/dist/mutations/organization/check-slug-mutation.js +15 -0
  83. package/dist/mutations/organization/create-organization-mutation.d.ts +38 -0
  84. package/dist/mutations/organization/create-organization-mutation.js +12 -0
  85. package/dist/mutations/organization/delete-organization-mutation.d.ts +18 -0
  86. package/dist/mutations/organization/delete-organization-mutation.js +12 -0
  87. package/dist/mutations/organization/index.d.ts +13 -0
  88. package/dist/mutations/organization/index.js +13 -0
  89. package/dist/mutations/organization/invite-member-mutation.d.ts +30 -0
  90. package/dist/mutations/organization/invite-member-mutation.js +12 -0
  91. package/dist/mutations/organization/leave-organization-mutation.d.ts +18 -0
  92. package/dist/mutations/organization/leave-organization-mutation.js +12 -0
  93. package/dist/mutations/organization/metadata.d.ts +13 -0
  94. package/dist/mutations/organization/metadata.js +27 -0
  95. package/dist/mutations/organization/reject-invitation-mutation.d.ts +18 -0
  96. package/dist/mutations/organization/reject-invitation-mutation.js +12 -0
  97. package/dist/mutations/organization/remove-member-mutation.d.ts +22 -0
  98. package/dist/mutations/organization/remove-member-mutation.js +12 -0
  99. package/dist/mutations/organization/set-active-organization-mutation.d.ts +24 -0
  100. package/dist/mutations/organization/set-active-organization-mutation.js +35 -0
  101. package/dist/mutations/organization/update-member-role-mutation.d.ts +26 -0
  102. package/dist/mutations/organization/update-member-role-mutation.js +12 -0
  103. package/dist/mutations/organization/update-organization-mutation.d.ts +42 -0
  104. package/dist/mutations/organization/update-organization-mutation.js +12 -0
  105. package/dist/mutations/organization/utils.d.ts +8 -0
  106. package/dist/mutations/organization/utils.js +17 -0
  107. package/dist/mutations/passkey/add-passkey-mutation.d.ts +73 -0
  108. package/dist/mutations/passkey/add-passkey-mutation.js +12 -0
  109. package/dist/mutations/passkey/delete-passkey-mutation.d.ts +19 -0
  110. package/dist/mutations/passkey/delete-passkey-mutation.js +12 -0
  111. package/dist/mutations/passkey/sign-in-passkey-mutation.d.ts +50 -0
  112. package/dist/mutations/passkey/sign-in-passkey-mutation.js +9 -0
  113. package/dist/mutations/settings/change-email-mutation.d.ts +11 -0
  114. package/dist/mutations/settings/change-email-mutation.js +8 -0
  115. package/dist/mutations/settings/change-password-mutation.d.ts +13 -0
  116. package/dist/mutations/settings/change-password-mutation.js +8 -0
  117. package/dist/mutations/settings/delete-user-mutation.d.ts +13 -0
  118. package/dist/mutations/settings/delete-user-mutation.js +8 -0
  119. package/dist/mutations/settings/link-social-mutation.d.ts +35 -0
  120. package/dist/mutations/settings/link-social-mutation.js +8 -0
  121. package/dist/mutations/settings/revoke-session-mutation.d.ts +19 -0
  122. package/dist/mutations/settings/revoke-session-mutation.js +12 -0
  123. package/dist/mutations/settings/unlink-account-mutation.d.ts +23 -0
  124. package/dist/mutations/settings/unlink-account-mutation.js +12 -0
  125. package/dist/mutations/settings/update-user-mutation.d.ts +6 -0
  126. package/dist/mutations/settings/update-user-mutation.js +8 -0
  127. package/dist/mutations/use-session-scoped-mutation.d.ts +17 -0
  128. package/dist/mutations/use-session-scoped-mutation.js +17 -0
  129. package/dist/mutations/username/is-username-available-mutation.d.ts +9 -0
  130. package/dist/mutations/username/is-username-available-mutation.js +8 -0
  131. package/dist/mutations/username/sign-in-username-mutation.d.ts +15 -0
  132. package/dist/mutations/username/sign-in-username-mutation.js +9 -0
  133. package/dist/plugins/captcha-plugin.d.ts +29 -0
  134. package/dist/plugins/captcha-plugin.js +26 -0
  135. package/dist/plugins.d.ts +2 -0
  136. package/dist/plugins.js +2 -0
  137. package/dist/queries/api-key/list-api-keys-query.d.ts +44 -0
  138. package/dist/queries/api-key/list-api-keys-query.js +17 -0
  139. package/dist/queries/auth/session-query.d.ts +22 -0
  140. package/dist/queries/auth/session-query.js +32 -0
  141. package/dist/queries/auth-query-options.d.ts +14 -0
  142. package/dist/queries/auth-query-options.js +17 -0
  143. package/dist/queries/create-user-scoped-query.d.ts +25 -0
  144. package/dist/queries/create-user-scoped-query.js +29 -0
  145. package/dist/queries/multi-session/list-device-sessions-query.d.ts +23 -0
  146. package/dist/queries/multi-session/list-device-sessions-query.js +17 -0
  147. package/dist/queries/organization/active-organization-query.d.ts +37 -0
  148. package/dist/queries/organization/active-organization-query.js +26 -0
  149. package/dist/queries/organization/full-organization-query.d.ts +35 -0
  150. package/dist/queries/organization/full-organization-query.js +25 -0
  151. package/dist/queries/organization/has-permission-query.d.ts +23 -0
  152. package/dist/queries/organization/has-permission-query.js +31 -0
  153. package/dist/queries/organization/index.d.ts +7 -0
  154. package/dist/queries/organization/index.js +7 -0
  155. package/dist/queries/organization/list-invitations-query.d.ts +29 -0
  156. package/dist/queries/organization/list-invitations-query.js +29 -0
  157. package/dist/queries/organization/list-members-query.d.ts +53 -0
  158. package/dist/queries/organization/list-members-query.js +29 -0
  159. package/dist/queries/organization/list-organizations-query.d.ts +23 -0
  160. package/dist/queries/organization/list-organizations-query.js +25 -0
  161. package/dist/queries/organization/list-user-invitations-query.d.ts +29 -0
  162. package/dist/queries/organization/list-user-invitations-query.js +25 -0
  163. package/dist/queries/organization/plugin.d.ts +1 -0
  164. package/dist/queries/organization/plugin.js +8 -0
  165. package/dist/queries/organization/utils.d.ts +16 -0
  166. package/dist/queries/organization/utils.js +18 -0
  167. package/dist/queries/passkey/list-passkeys-query.d.ts +23 -0
  168. package/dist/queries/passkey/list-passkeys-query.js +17 -0
  169. package/dist/queries/settings/account-info-query.d.ts +35 -0
  170. package/dist/queries/settings/account-info-query.js +17 -0
  171. package/dist/queries/settings/list-accounts-query.d.ts +23 -0
  172. package/dist/queries/settings/list-accounts-query.js +17 -0
  173. package/dist/queries/settings/list-sessions-query.d.ts +24 -0
  174. package/dist/queries/settings/list-sessions-query.js +17 -0
  175. package/dist/server/queries/auth/session-query.d.ts +19 -0
  176. package/dist/server/queries/auth/session-query.js +13 -0
  177. package/dist/server.d.ts +7 -0
  178. package/dist/server.js +7 -0
  179. package/package.json +63 -0
  180. package/src/components/auth/email/email-changed.tsx +305 -0
  181. package/src/components/auth/email/email-styles.tsx +139 -0
  182. package/src/components/auth/email/email-verification.tsx +249 -0
  183. package/src/components/auth/email/index.ts +9 -0
  184. package/src/components/auth/email/magic-link.tsx +254 -0
  185. package/src/components/auth/email/new-device.tsx +347 -0
  186. package/src/components/auth/email/organization-invitation.tsx +322 -0
  187. package/src/components/auth/email/otp-email.tsx +239 -0
  188. package/src/components/auth/email/password-changed.tsx +279 -0
  189. package/src/components/auth/email/reset-password.tsx +250 -0
  190. package/src/email.ts +49 -0
  191. package/src/hooks/auth/use-authenticate.ts +30 -0
  192. package/src/hooks/auth/use-sign-out.ts +23 -0
  193. package/src/hooks/auth/use-user.ts +19 -0
  194. package/src/hooks/use-auth-mutation.ts +35 -0
  195. package/src/hooks/use-auth-query.ts +33 -0
  196. package/src/index.ts +48 -0
  197. package/src/lib/auth-client.ts +43 -0
  198. package/src/lib/auth-config.ts +46 -0
  199. package/src/lib/auth-plugin.ts +20 -0
  200. package/src/lib/auth-provider.tsx +60 -0
  201. package/src/lib/auth-server.ts +25 -0
  202. package/src/lib/auth-utils.ts +25 -0
  203. package/src/lib/fetch-options-provider.tsx +77 -0
  204. package/src/lib/mutation-invalidator.tsx +82 -0
  205. package/src/lib/provider-icons.ts +10 -0
  206. package/src/mutations/api-key/create-api-key-mutation.ts +40 -0
  207. package/src/mutations/api-key/delete-api-key-mutation.ts +40 -0
  208. package/src/mutations/auth/request-password-reset-mutation.ts +15 -0
  209. package/src/mutations/auth/reset-password-mutation.ts +16 -0
  210. package/src/mutations/auth/send-verification-email-mutation.ts +15 -0
  211. package/src/mutations/auth/sign-in-email-mutation.ts +17 -0
  212. package/src/mutations/auth/sign-in-social-mutation.ts +16 -0
  213. package/src/mutations/auth/sign-out-mutation.ts +13 -0
  214. package/src/mutations/auth/sign-up-email-mutation.ts +17 -0
  215. package/src/mutations/auth-mutation-options.ts +62 -0
  216. package/src/mutations/create-auth-mutation.ts +44 -0
  217. package/src/mutations/magic-link/sign-in-magic-link-mutation.ts +15 -0
  218. package/src/mutations/multi-session/revoke-multi-session-mutation.ts +40 -0
  219. package/src/mutations/multi-session/set-active-session-mutation.ts +43 -0
  220. package/src/mutations/organization/accept-invitation-mutation.ts +38 -0
  221. package/src/mutations/organization/cancel-invitation-mutation.ts +38 -0
  222. package/src/mutations/organization/check-slug-mutation.ts +36 -0
  223. package/src/mutations/organization/create-organization-mutation.ts +37 -0
  224. package/src/mutations/organization/delete-organization-mutation.ts +37 -0
  225. package/src/mutations/organization/index.ts +13 -0
  226. package/src/mutations/organization/invite-member-mutation.ts +38 -0
  227. package/src/mutations/organization/leave-organization-mutation.ts +37 -0
  228. package/src/mutations/organization/metadata.ts +103 -0
  229. package/src/mutations/organization/reject-invitation-mutation.ts +38 -0
  230. package/src/mutations/organization/remove-member-mutation.ts +38 -0
  231. package/src/mutations/organization/set-active-organization-mutation.ts +110 -0
  232. package/src/mutations/organization/update-member-role-mutation.ts +38 -0
  233. package/src/mutations/organization/update-organization-mutation.ts +37 -0
  234. package/src/mutations/organization/utils.ts +51 -0
  235. package/src/mutations/passkey/add-passkey-mutation.ts +40 -0
  236. package/src/mutations/passkey/delete-passkey-mutation.ts +40 -0
  237. package/src/mutations/passkey/sign-in-passkey-mutation.ts +17 -0
  238. package/src/mutations/settings/change-email-mutation.ts +17 -0
  239. package/src/mutations/settings/change-password-mutation.ts +16 -0
  240. package/src/mutations/settings/delete-user-mutation.ts +16 -0
  241. package/src/mutations/settings/link-social-mutation.ts +16 -0
  242. package/src/mutations/settings/revoke-session-mutation.ts +38 -0
  243. package/src/mutations/settings/unlink-account-mutation.ts +38 -0
  244. package/src/mutations/settings/update-user-mutation.ts +17 -0
  245. package/src/mutations/use-session-scoped-mutation.ts +64 -0
  246. package/src/mutations/username/is-username-available-mutation.ts +15 -0
  247. package/src/mutations/username/sign-in-username-mutation.ts +17 -0
  248. package/src/plugins/captcha-plugin.tsx +64 -0
  249. package/src/plugins.ts +2 -0
  250. package/src/queries/api-key/list-api-keys-query.ts +93 -0
  251. package/src/queries/auth/session-query.ts +81 -0
  252. package/src/queries/auth-query-options.ts +57 -0
  253. package/src/queries/create-user-scoped-query.ts +107 -0
  254. package/src/queries/multi-session/list-device-sessions-query.ts +107 -0
  255. package/src/queries/organization/active-organization-query.ts +127 -0
  256. package/src/queries/organization/full-organization-query.ts +111 -0
  257. package/src/queries/organization/has-permission-query.ts +121 -0
  258. package/src/queries/organization/index.ts +7 -0
  259. package/src/queries/organization/list-invitations-query.ts +124 -0
  260. package/src/queries/organization/list-members-query.ts +124 -0
  261. package/src/queries/organization/list-organizations-query.ts +113 -0
  262. package/src/queries/organization/list-user-invitations-query.ts +114 -0
  263. package/src/queries/organization/plugin.ts +11 -0
  264. package/src/queries/organization/utils.ts +88 -0
  265. package/src/queries/passkey/list-passkeys-query.ts +93 -0
  266. package/src/queries/settings/account-info-query.ts +94 -0
  267. package/src/queries/settings/list-accounts-query.ts +94 -0
  268. package/src/queries/settings/list-sessions-query.ts +96 -0
  269. package/src/server/queries/auth/session-query.ts +56 -0
  270. package/src/server.ts +7 -0
@@ -0,0 +1,347 @@
1
+ import {
2
+ Body,
3
+ Button,
4
+ Container,
5
+ Head,
6
+ Heading,
7
+ Hr,
8
+ Html,
9
+ Img,
10
+ Preview,
11
+ Section,
12
+ Tailwind,
13
+ Text
14
+ } from "@solidjs-email/main"
15
+ import type { JSX } from "solid-js"
16
+ import { Show } from "solid-js"
17
+ import {
18
+ type EmailClassNames,
19
+ type EmailColors,
20
+ EmailStyles
21
+ } from "./email-styles"
22
+
23
+ export interface DeviceInfo {
24
+ browser?: string
25
+ os?: string
26
+ location?: string
27
+ ipAddress?: string
28
+ timestamp?: string
29
+ }
30
+
31
+ function cn(...classes: Array<string | false | null | undefined>) {
32
+ return classes.filter(Boolean).join(" ")
33
+ }
34
+
35
+ const newDeviceEmailLocalization = {
36
+ NEW_SIGN_IN_DETECTED: "New sign-in detected",
37
+ LOGO: "Logo",
38
+ NEW_SIGN_IN_TO_YOUR_ACCOUNT:
39
+ "We detected a new sign-in to your {appName} account {userEmail} from a device we don't recognize.",
40
+ DEVICE_DETAILS: "Device details",
41
+ BROWSER: "Browser",
42
+ OPERATING_SYSTEM: "Operating System",
43
+ LOCATION: "Location",
44
+ IP_ADDRESS: "IP Address",
45
+ TIME: "Time",
46
+ IF_THIS_WAS_YOU:
47
+ "If this was you, you can safely ignore this email. If you don't recognize this activity, please secure your account immediately.",
48
+ SECURE_MY_ACCOUNT: "Secure my account",
49
+ EMAIL_SENT_BY: "Email sent by {appName}.",
50
+ IF_YOU_DIDNT_SIGN_IN:
51
+ "If you didn't sign in, please contact support immediately {supportEmail} to secure your account.",
52
+ POWERED_BY_BETTER_AUTH: "Powered by {betterAuth}"
53
+ }
54
+
55
+ export type NewDeviceEmailLocalization = typeof newDeviceEmailLocalization
56
+
57
+ export interface NewDeviceEmailProps {
58
+ userEmail?: string
59
+ deviceInfo?: DeviceInfo
60
+ secureAccountLink?: string
61
+ appName?: string
62
+ supportEmail?: string
63
+ logoURL?: string | { light: string; dark: string }
64
+ classNames?: EmailClassNames
65
+ colors?: EmailColors
66
+ poweredBy?: boolean
67
+ darkMode?: boolean
68
+ head?: JSX.Element
69
+ localization?: Partial<NewDeviceEmailLocalization>
70
+ }
71
+
72
+ function cleanSentence(value: string) {
73
+ return value.replace(/\s{2,}/g, " ").replace(" .", ".")
74
+ }
75
+
76
+ export function NewDeviceEmail(props: NewDeviceEmailProps) {
77
+ const darkMode = () => props.darkMode ?? true
78
+ const localization = () => ({
79
+ ...NewDeviceEmail.localization,
80
+ ...props.localization
81
+ })
82
+ const signInText = () =>
83
+ cleanSentence(
84
+ localization()
85
+ .NEW_SIGN_IN_TO_YOUR_ACCOUNT.replace("{appName}", props.appName || "")
86
+ .replace("{userEmail}", props.userEmail || "")
87
+ )
88
+ const supportText = () =>
89
+ cleanSentence(
90
+ localization().IF_YOU_DIDNT_SIGN_IN.replace(
91
+ "{supportEmail}",
92
+ props.supportEmail || ""
93
+ )
94
+ )
95
+
96
+ return (
97
+ <Html>
98
+ <Head>
99
+ <meta content="light dark" name="color-scheme" />
100
+ <meta content="light dark" name="supported-color-schemes" />
101
+ <EmailStyles colors={props.colors} darkMode={darkMode()} />
102
+ {props.head}
103
+ </Head>
104
+
105
+ <Preview>{localization().NEW_SIGN_IN_DETECTED}</Preview>
106
+
107
+ <Tailwind>
108
+ <Body
109
+ class={cn("bg-background", props.classNames?.body)}
110
+ style={{ "font-family": "Arial, Helvetica, sans-serif" }}
111
+ >
112
+ <Container
113
+ class={cn(
114
+ "mx-auto my-auto max-w-xl px-2 py-10",
115
+ props.classNames?.container
116
+ )}
117
+ >
118
+ <Section
119
+ class={cn(
120
+ "rounded-none border border-border bg-card p-8 text-card-foreground",
121
+ props.classNames?.card
122
+ )}
123
+ >
124
+ {props.logoURL &&
125
+ (typeof props.logoURL === "string" ? (
126
+ <Img
127
+ alt={props.appName || localization().LOGO}
128
+ class={cn("mx-auto mb-8", props.classNames?.logo)}
129
+ height={48}
130
+ src={props.logoURL}
131
+ width={48}
132
+ />
133
+ ) : (
134
+ <>
135
+ <Img
136
+ alt={props.appName || localization().LOGO}
137
+ class={cn(
138
+ "logo-light mx-auto mb-8",
139
+ props.classNames?.logo
140
+ )}
141
+ height={48}
142
+ src={props.logoURL.light}
143
+ width={48}
144
+ />
145
+ <Img
146
+ alt={props.appName || localization().LOGO}
147
+ class={cn(
148
+ "logo-dark hidden mx-auto mb-8",
149
+ props.classNames?.logo
150
+ )}
151
+ height={48}
152
+ src={props.logoURL.dark}
153
+ width={48}
154
+ />
155
+ </>
156
+ ))}
157
+
158
+ <Heading
159
+ class={cn(
160
+ "m-0 mb-5 font-semibold text-2xl",
161
+ props.classNames?.title
162
+ )}
163
+ >
164
+ {localization().NEW_SIGN_IN_DETECTED}
165
+ </Heading>
166
+
167
+ <Text
168
+ class={cn("font-normal text-sm", props.classNames?.content)}
169
+ >
170
+ {signInText()}
171
+ </Text>
172
+
173
+ <Show when={props.deviceInfo}>
174
+ {(deviceInfo) => (
175
+ <Section
176
+ class={cn(
177
+ "my-6 border border-border bg-muted p-4",
178
+ props.classNames?.codeBlock
179
+ )}
180
+ >
181
+ <Text
182
+ class={cn(
183
+ "m-0 mb-3 text-muted-foreground text-xs",
184
+ props.classNames?.description
185
+ )}
186
+ >
187
+ {localization().DEVICE_DETAILS}:
188
+ </Text>
189
+
190
+ <Show when={deviceInfo().browser}>
191
+ {(browser) => (
192
+ <Text
193
+ class={cn(
194
+ "m-0 mb-2 text-sm",
195
+ props.classNames?.content
196
+ )}
197
+ >
198
+ {localization().BROWSER}: {browser()}
199
+ </Text>
200
+ )}
201
+ </Show>
202
+
203
+ <Show when={deviceInfo().os}>
204
+ {(os) => (
205
+ <Text
206
+ class={cn(
207
+ "m-0 mb-2 text-sm",
208
+ props.classNames?.content
209
+ )}
210
+ >
211
+ {localization().OPERATING_SYSTEM}: {os()}
212
+ </Text>
213
+ )}
214
+ </Show>
215
+
216
+ <Show when={deviceInfo().location}>
217
+ {(location) => (
218
+ <Text
219
+ class={cn(
220
+ "m-0 mb-2 text-sm",
221
+ props.classNames?.content
222
+ )}
223
+ >
224
+ {localization().LOCATION}: {location()}
225
+ </Text>
226
+ )}
227
+ </Show>
228
+
229
+ <Show when={deviceInfo().ipAddress}>
230
+ {(ipAddress) => (
231
+ <Text
232
+ class={cn(
233
+ "m-0 mb-2 text-sm",
234
+ props.classNames?.content
235
+ )}
236
+ >
237
+ {localization().IP_ADDRESS}: {ipAddress()}
238
+ </Text>
239
+ )}
240
+ </Show>
241
+
242
+ <Show when={deviceInfo().timestamp}>
243
+ {(timestamp) => (
244
+ <Text
245
+ class={cn("m-0 text-sm", props.classNames?.content)}
246
+ >
247
+ {localization().TIME}: {timestamp()}
248
+ </Text>
249
+ )}
250
+ </Show>
251
+ </Section>
252
+ )}
253
+ </Show>
254
+
255
+ <Text
256
+ class={cn("font-normal text-sm", props.classNames?.content)}
257
+ >
258
+ {localization().IF_THIS_WAS_YOU}
259
+ </Text>
260
+
261
+ <Show when={props.secureAccountLink}>
262
+ {(secureAccountLink) => (
263
+ <Section class="mt-6">
264
+ <Button
265
+ class={cn(
266
+ "inline-block whitespace-nowrap rounded-none bg-primary px-6 py-2.5 font-medium text-primary-foreground text-sm no-underline",
267
+ props.classNames?.button
268
+ )}
269
+ href={secureAccountLink()}
270
+ >
271
+ {localization().SECURE_MY_ACCOUNT}
272
+ </Button>
273
+ </Section>
274
+ )}
275
+ </Show>
276
+
277
+ <Hr
278
+ class={cn(
279
+ "my-6 w-full border border-border border-solid",
280
+ props.classNames?.separator
281
+ )}
282
+ />
283
+
284
+ <Show when={props.appName}>
285
+ {(appName) => (
286
+ <Text
287
+ class={cn(
288
+ "mb-3 text-muted-foreground text-xs",
289
+ props.classNames?.description
290
+ )}
291
+ >
292
+ {localization().EMAIL_SENT_BY.replace(
293
+ "{appName}",
294
+ appName()
295
+ )}
296
+ </Text>
297
+ )}
298
+ </Show>
299
+
300
+ <Text
301
+ class={cn(
302
+ "mt-3 text-muted-foreground text-xs",
303
+ props.classNames?.description
304
+ )}
305
+ >
306
+ {supportText()}
307
+ </Text>
308
+
309
+ <Show when={props.poweredBy}>
310
+ <Text
311
+ class={cn(
312
+ "mt-4 mb-0 text-center text-[11px] text-muted-foreground",
313
+ props.classNames?.poweredBy
314
+ )}
315
+ >
316
+ {localization().POWERED_BY_BETTER_AUTH.replace(
317
+ "{betterAuth}",
318
+ "better-auth"
319
+ )}
320
+ </Text>
321
+ </Show>
322
+ </Section>
323
+ </Container>
324
+ </Body>
325
+ </Tailwind>
326
+ </Html>
327
+ )
328
+ }
329
+
330
+ NewDeviceEmail.localization = newDeviceEmailLocalization
331
+
332
+ NewDeviceEmail.PreviewProps = {
333
+ userEmail: "m@example.com",
334
+ deviceInfo: {
335
+ browser: "Chrome on macOS",
336
+ os: "macOS 26.2",
337
+ location: "San Francisco, CA, United States",
338
+ ipAddress: "127.0.0.1",
339
+ timestamp: "February 10, 2025 at 4:20 PM UTC"
340
+ },
341
+ secureAccountLink: "https://better-auth-ui.com/auth/secure-account",
342
+ appName: "Better Auth",
343
+ supportEmail: "support@example.com",
344
+ darkMode: true
345
+ } satisfies NewDeviceEmailProps
346
+
347
+ export default NewDeviceEmail
@@ -0,0 +1,322 @@
1
+ import {
2
+ Body,
3
+ Button,
4
+ Container,
5
+ Head,
6
+ Heading,
7
+ Hr,
8
+ Html,
9
+ Img,
10
+ Link,
11
+ Preview,
12
+ Section,
13
+ Tailwind,
14
+ Text
15
+ } from "@solidjs-email/main"
16
+ import type { JSX } from "solid-js"
17
+ import { Show } from "solid-js"
18
+ import {
19
+ type EmailClassNames,
20
+ type EmailColors,
21
+ EmailStyles
22
+ } from "./email-styles"
23
+
24
+ function cn(...classes: Array<string | false | null | undefined>) {
25
+ return classes.filter(Boolean).join(" ")
26
+ }
27
+
28
+ const organizationInvitationEmailLocalization = {
29
+ YOU_RE_INVITED_TO_ORGANIZATION: "You're invited to {organizationName}",
30
+ YOU_RE_INVITED: "You're invited",
31
+ LOGO: "Logo",
32
+ ORGANIZATION_LOGO: "Organization logo",
33
+ INVITED_TO_JOIN_ORGANIZATION:
34
+ "{inviterName} ({inviterEmail}) has invited you to join {organizationName} on {appName} as a {role}.",
35
+ ACCEPT_INVITATION: "Accept invitation",
36
+ VIEW_INVITATION: "View invitation",
37
+ OR_COPY_AND_PASTE_URL: "Or copy and paste this URL into your browser:",
38
+ THIS_INVITATION_EXPIRES_IN_HOURS:
39
+ "This invitation expires in {expirationHours} hours.",
40
+ EMAIL_SENT_BY: "Email sent by {appName}.",
41
+ IF_YOU_DIDNT_EXPECT_THIS_INVITATION:
42
+ "If you didn't expect this invitation, you can safely ignore this email.",
43
+ POWERED_BY_BETTER_AUTH: "Powered by {betterAuth}"
44
+ }
45
+
46
+ export type OrganizationInvitationEmailLocalization =
47
+ typeof organizationInvitationEmailLocalization
48
+
49
+ export interface OrganizationInvitationEmailProps {
50
+ url: string
51
+ email?: string
52
+ inviterName?: string
53
+ inviterEmail?: string
54
+ organizationName?: string
55
+ organizationLogoURL?: string | { light: string; dark: string }
56
+ role?: string
57
+ appName?: string
58
+ expirationHours?: number
59
+ logoURL?: string | { light: string; dark: string }
60
+ classNames?: EmailClassNames
61
+ colors?: EmailColors
62
+ poweredBy?: boolean
63
+ darkMode?: boolean
64
+ head?: JSX.Element
65
+ localization?: Partial<OrganizationInvitationEmailLocalization>
66
+ }
67
+
68
+ function cleanSentence(value: string) {
69
+ return value.replace(/\s{2,}/g, " ").replace(" .", ".")
70
+ }
71
+
72
+ export function OrganizationInvitationEmail(
73
+ props: OrganizationInvitationEmailProps
74
+ ) {
75
+ const expirationHours = () => props.expirationHours ?? 48
76
+ const darkMode = () => props.darkMode ?? true
77
+ const localization = () => ({
78
+ ...OrganizationInvitationEmail.localization,
79
+ ...props.localization
80
+ })
81
+ const title = () =>
82
+ props.organizationName
83
+ ? localization().YOU_RE_INVITED_TO_ORGANIZATION.replace(
84
+ "{organizationName}",
85
+ props.organizationName
86
+ )
87
+ : localization().YOU_RE_INVITED
88
+ const invitationText = () => {
89
+ const inviterName = props.inviterName || "Someone"
90
+ const inviterEmail = props.inviterEmail || ""
91
+
92
+ return cleanSentence(
93
+ localization()
94
+ .INVITED_TO_JOIN_ORGANIZATION.replace("{inviterName}", inviterName)
95
+ .replace("{inviterEmail}", inviterEmail)
96
+ .replace("{organizationName}", props.organizationName || "")
97
+ .replace("{appName}", props.appName || "")
98
+ .replace("{role}", props.role || "")
99
+ .replace("()", "")
100
+ )
101
+ }
102
+
103
+ return (
104
+ <Html>
105
+ <Head>
106
+ <meta content="light dark" name="color-scheme" />
107
+ <meta content="light dark" name="supported-color-schemes" />
108
+ <EmailStyles colors={props.colors} darkMode={darkMode()} />
109
+ {props.head}
110
+ </Head>
111
+
112
+ <Preview>{title()}</Preview>
113
+
114
+ <Tailwind>
115
+ <Body
116
+ class={cn("bg-background", props.classNames?.body)}
117
+ style={{ "font-family": "Arial, Helvetica, sans-serif" }}
118
+ >
119
+ <Container
120
+ class={cn(
121
+ "mx-auto my-auto max-w-xl px-2 py-10",
122
+ props.classNames?.container
123
+ )}
124
+ >
125
+ <Section
126
+ class={cn(
127
+ "rounded-none border border-border bg-card p-8 text-card-foreground",
128
+ props.classNames?.card
129
+ )}
130
+ >
131
+ {props.logoURL &&
132
+ (typeof props.logoURL === "string" ? (
133
+ <Img
134
+ alt={props.appName || localization().LOGO}
135
+ class={cn("mx-auto mb-8", props.classNames?.logo)}
136
+ height={48}
137
+ src={props.logoURL}
138
+ width={48}
139
+ />
140
+ ) : (
141
+ <>
142
+ <Img
143
+ alt={props.appName || localization().LOGO}
144
+ class={cn(
145
+ "logo-light mx-auto mb-8",
146
+ props.classNames?.logo
147
+ )}
148
+ height={48}
149
+ src={props.logoURL.light}
150
+ width={48}
151
+ />
152
+ <Img
153
+ alt={props.appName || localization().LOGO}
154
+ class={cn(
155
+ "logo-dark hidden mx-auto mb-8",
156
+ props.classNames?.logo
157
+ )}
158
+ height={48}
159
+ src={props.logoURL.dark}
160
+ width={48}
161
+ />
162
+ </>
163
+ ))}
164
+
165
+ <Heading
166
+ class={cn(
167
+ "m-0 mb-5 font-semibold text-2xl",
168
+ props.classNames?.title
169
+ )}
170
+ >
171
+ {title()}
172
+ </Heading>
173
+
174
+ {props.organizationLogoURL &&
175
+ (typeof props.organizationLogoURL === "string" ? (
176
+ <Img
177
+ alt={
178
+ props.organizationName || localization().ORGANIZATION_LOGO
179
+ }
180
+ class={cn("mb-5 rounded-md", props.classNames?.logo)}
181
+ height={56}
182
+ src={props.organizationLogoURL}
183
+ width={56}
184
+ />
185
+ ) : (
186
+ <>
187
+ <Img
188
+ alt={
189
+ props.organizationName ||
190
+ localization().ORGANIZATION_LOGO
191
+ }
192
+ class={cn(
193
+ "logo-light mb-5 rounded-md",
194
+ props.classNames?.logo
195
+ )}
196
+ height={56}
197
+ src={props.organizationLogoURL.light}
198
+ width={56}
199
+ />
200
+ <Img
201
+ alt={
202
+ props.organizationName ||
203
+ localization().ORGANIZATION_LOGO
204
+ }
205
+ class={cn(
206
+ "logo-dark hidden mb-5 rounded-md",
207
+ props.classNames?.logo
208
+ )}
209
+ height={56}
210
+ src={props.organizationLogoURL.dark}
211
+ width={56}
212
+ />
213
+ </>
214
+ ))}
215
+
216
+ <Text
217
+ class={cn("m-0 font-normal text-sm", props.classNames?.content)}
218
+ >
219
+ {invitationText()}
220
+ </Text>
221
+
222
+ <Section class="my-6">
223
+ <Button
224
+ class={cn(
225
+ "inline-block whitespace-nowrap rounded-none bg-primary px-6 py-2.5 font-medium text-primary-foreground text-sm no-underline",
226
+ props.classNames?.button
227
+ )}
228
+ href={props.url}
229
+ >
230
+ {localization().ACCEPT_INVITATION}
231
+ </Button>
232
+ </Section>
233
+
234
+ <Text
235
+ class={cn(
236
+ "m-0 mb-3 text-muted-foreground text-xs",
237
+ props.classNames?.description
238
+ )}
239
+ >
240
+ {localization().OR_COPY_AND_PASTE_URL}
241
+ </Text>
242
+
243
+ <Link
244
+ class={cn(
245
+ "break-all text-primary text-xs",
246
+ props.classNames?.link
247
+ )}
248
+ href={props.url}
249
+ >
250
+ {props.url}
251
+ </Link>
252
+
253
+ <Hr
254
+ class={cn(
255
+ "my-6 w-full border border-border border-solid",
256
+ props.classNames?.separator
257
+ )}
258
+ />
259
+
260
+ <Text
261
+ class={cn(
262
+ "m-0 mb-3 text-muted-foreground text-xs",
263
+ props.classNames?.description
264
+ )}
265
+ >
266
+ {localization().THIS_INVITATION_EXPIRES_IN_HOURS.replace(
267
+ "{expirationHours}",
268
+ expirationHours().toString()
269
+ )}
270
+ <Show when={props.appName}>
271
+ {(appName) =>
272
+ ` ${localization().EMAIL_SENT_BY.replace("{appName}", appName())}`
273
+ }
274
+ </Show>
275
+ </Text>
276
+
277
+ <Text
278
+ class={cn(
279
+ "m-0 text-muted-foreground text-xs",
280
+ props.classNames?.description
281
+ )}
282
+ >
283
+ {localization().IF_YOU_DIDNT_EXPECT_THIS_INVITATION}
284
+ </Text>
285
+
286
+ <Show when={props.poweredBy}>
287
+ <Text
288
+ class={cn(
289
+ "m-0 mt-4 text-center text-[11px] text-muted-foreground",
290
+ props.classNames?.poweredBy
291
+ )}
292
+ >
293
+ {localization().POWERED_BY_BETTER_AUTH.replace(
294
+ "{betterAuth}",
295
+ "better-auth"
296
+ )}
297
+ </Text>
298
+ </Show>
299
+ </Section>
300
+ </Container>
301
+ </Body>
302
+ </Tailwind>
303
+ </Html>
304
+ )
305
+ }
306
+
307
+ OrganizationInvitationEmail.localization =
308
+ organizationInvitationEmailLocalization
309
+
310
+ OrganizationInvitationEmail.PreviewProps = {
311
+ url: "https://better-auth-ui.com/settings/organizations",
312
+ email: "m@example.com",
313
+ inviterName: "Jane Doe",
314
+ inviterEmail: "jane@example.com",
315
+ organizationName: "Acme Inc.",
316
+ role: "member",
317
+ appName: "Better Auth",
318
+ expirationHours: 48,
319
+ darkMode: true
320
+ } satisfies OrganizationInvitationEmailProps
321
+
322
+ export default OrganizationInvitationEmail