better-auth-ui 3.2.5

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 (226) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -0
  3. package/dist/auth-hooks-IOEvlYYv.d.cts +6966 -0
  4. package/dist/auth-hooks-IOEvlYYv.d.ts +6966 -0
  5. package/dist/auth-mutators-DdqOmQ32.d.cts +29 -0
  6. package/dist/auth-mutators-DdqOmQ32.d.ts +29 -0
  7. package/dist/auth-ui-provider-BsH3xJDw.d.ts +697 -0
  8. package/dist/auth-ui-provider-DhZfncd3.d.cts +697 -0
  9. package/dist/chunk-BDFQSFBU.js +750 -0
  10. package/dist/chunk-CRAHKL2C.cjs +801 -0
  11. package/dist/chunk-MJPOA6PK.js +801 -0
  12. package/dist/chunk-SV64DXGW.cjs +750 -0
  13. package/dist/index.cjs +12618 -0
  14. package/dist/index.d.cts +771 -0
  15. package/dist/index.d.ts +771 -0
  16. package/dist/index.js +12618 -0
  17. package/dist/instantdb.cjs +189 -0
  18. package/dist/instantdb.d.cts +36 -0
  19. package/dist/instantdb.d.ts +36 -0
  20. package/dist/instantdb.js +189 -0
  21. package/dist/metafile-cjs.json +1 -0
  22. package/dist/metafile-esm.json +1 -0
  23. package/dist/server.cjs +194 -0
  24. package/dist/server.d.cts +35 -0
  25. package/dist/server.d.ts +35 -0
  26. package/dist/server.js +194 -0
  27. package/dist/style.css +1 -0
  28. package/dist/tanstack.cjs +153 -0
  29. package/dist/tanstack.d.cts +18 -0
  30. package/dist/tanstack.d.ts +18 -0
  31. package/dist/tanstack.js +153 -0
  32. package/dist/triplit.cjs +201 -0
  33. package/dist/triplit.d.cts +31 -0
  34. package/dist/triplit.d.ts +31 -0
  35. package/dist/triplit.js +201 -0
  36. package/dist/utils-C5R37WDe.d.cts +3 -0
  37. package/dist/utils-C5R37WDe.d.ts +3 -0
  38. package/dist/view-paths-CHSJf5dv.d.cts +645 -0
  39. package/dist/view-paths-CHSJf5dv.d.ts +645 -0
  40. package/package.json +156 -0
  41. package/src/components/account/account-view.tsx +220 -0
  42. package/src/components/auth/auth-callback.tsx +36 -0
  43. package/src/components/auth/auth-form.tsx +277 -0
  44. package/src/components/auth/auth-view.tsx +389 -0
  45. package/src/components/auth/email-otp-button.tsx +53 -0
  46. package/src/components/auth/forms/email-otp-form.tsx +288 -0
  47. package/src/components/auth/forms/forgot-password-form.tsx +168 -0
  48. package/src/components/auth/forms/magic-link-form.tsx +191 -0
  49. package/src/components/auth/forms/recover-account-form.tsx +138 -0
  50. package/src/components/auth/forms/reset-password-form.tsx +215 -0
  51. package/src/components/auth/forms/sign-in-form.tsx +289 -0
  52. package/src/components/auth/forms/sign-up-form.tsx +788 -0
  53. package/src/components/auth/forms/two-factor-form.tsx +372 -0
  54. package/src/components/auth/magic-link-button.tsx +54 -0
  55. package/src/components/auth/one-tap.tsx +48 -0
  56. package/src/components/auth/otp-input-group.tsx +65 -0
  57. package/src/components/auth/passkey-button.tsx +85 -0
  58. package/src/components/auth/provider-button.tsx +141 -0
  59. package/src/components/auth/sign-out.tsx +25 -0
  60. package/src/components/auth-loading.tsx +21 -0
  61. package/src/components/captcha/captcha.tsx +79 -0
  62. package/src/components/captcha/recaptcha-badge.tsx +61 -0
  63. package/src/components/captcha/recaptcha-v2.tsx +58 -0
  64. package/src/components/captcha/recaptcha-v3.tsx +73 -0
  65. package/src/components/email/email-template.tsx +216 -0
  66. package/src/components/form-error.tsx +27 -0
  67. package/src/components/organization/accept-invitation-card.tsx +362 -0
  68. package/src/components/organization/create-organization-dialog.tsx +395 -0
  69. package/src/components/organization/delete-organization-card.tsx +101 -0
  70. package/src/components/organization/delete-organization-dialog.tsx +209 -0
  71. package/src/components/organization/invitation-cell.tsx +156 -0
  72. package/src/components/organization/invite-member-dialog.tsx +258 -0
  73. package/src/components/organization/leave-organization-dialog.tsx +150 -0
  74. package/src/components/organization/member-cell.tsx +187 -0
  75. package/src/components/organization/organization-cell-view.tsx +122 -0
  76. package/src/components/organization/organization-cell.tsx +154 -0
  77. package/src/components/organization/organization-invitations-card.tsx +94 -0
  78. package/src/components/organization/organization-logo-card.tsx +308 -0
  79. package/src/components/organization/organization-logo.tsx +120 -0
  80. package/src/components/organization/organization-members-card.tsx +155 -0
  81. package/src/components/organization/organization-name-card.tsx +204 -0
  82. package/src/components/organization/organization-settings-cards.tsx +67 -0
  83. package/src/components/organization/organization-slug-card.tsx +223 -0
  84. package/src/components/organization/organization-switcher.tsx +512 -0
  85. package/src/components/organization/organization-view.tsx +228 -0
  86. package/src/components/organization/organizations-card.tsx +72 -0
  87. package/src/components/organization/personal-account-view.tsx +115 -0
  88. package/src/components/organization/remove-member-dialog.tsx +144 -0
  89. package/src/components/organization/update-member-role-dialog.tsx +213 -0
  90. package/src/components/organization/user-invitations-card.tsx +238 -0
  91. package/src/components/password-input.tsx +56 -0
  92. package/src/components/provider-icons.tsx +385 -0
  93. package/src/components/redirect-to-sign-in.tsx +16 -0
  94. package/src/components/redirect-to-sign-up.tsx +16 -0
  95. package/src/components/settings/account/account-cell.tsx +158 -0
  96. package/src/components/settings/account/accounts-card.tsx +75 -0
  97. package/src/components/settings/account/delete-account-card.tsx +65 -0
  98. package/src/components/settings/account/delete-account-dialog.tsx +231 -0
  99. package/src/components/settings/account/update-avatar-card.tsx +198 -0
  100. package/src/components/settings/account/update-field-card.tsx +282 -0
  101. package/src/components/settings/account/update-name-card.tsx +39 -0
  102. package/src/components/settings/account/update-username-card.tsx +42 -0
  103. package/src/components/settings/account-settings-cards.tsx +123 -0
  104. package/src/components/settings/api-key/api-key-cell.tsx +108 -0
  105. package/src/components/settings/api-key/api-key-delete-dialog.tsx +162 -0
  106. package/src/components/settings/api-key/api-key-display-dialog.tsx +110 -0
  107. package/src/components/settings/api-key/api-keys-card.tsx +98 -0
  108. package/src/components/settings/api-key/create-api-key-dialog.tsx +376 -0
  109. package/src/components/settings/passkey/passkey-cell.tsx +113 -0
  110. package/src/components/settings/passkey/passkeys-card.tsx +111 -0
  111. package/src/components/settings/providers/provider-cell.tsx +152 -0
  112. package/src/components/settings/providers/providers-card.tsx +108 -0
  113. package/src/components/settings/security/change-email-card.tsx +200 -0
  114. package/src/components/settings/security/change-password-card.tsx +326 -0
  115. package/src/components/settings/security/session-cell.tsx +120 -0
  116. package/src/components/settings/security/sessions-card.tsx +58 -0
  117. package/src/components/settings/security-settings-cards.tsx +111 -0
  118. package/src/components/settings/shared/session-freshness-dialog.tsx +97 -0
  119. package/src/components/settings/shared/settings-action-button.tsx +51 -0
  120. package/src/components/settings/shared/settings-card-footer.tsx +94 -0
  121. package/src/components/settings/shared/settings-card-header.tsx +67 -0
  122. package/src/components/settings/shared/settings-card.tsx +106 -0
  123. package/src/components/settings/skeletons/input-field-skeleton.tsx +18 -0
  124. package/src/components/settings/skeletons/settings-cell-skeleton.tsx +37 -0
  125. package/src/components/settings/two-factor/backup-codes-dialog.tsx +113 -0
  126. package/src/components/settings/two-factor/two-factor-card.tsx +63 -0
  127. package/src/components/settings/two-factor/two-factor-password-dialog.tsx +226 -0
  128. package/src/components/signed-in.tsx +20 -0
  129. package/src/components/signed-out.tsx +20 -0
  130. package/src/components/ui/alert.tsx +66 -0
  131. package/src/components/ui/avatar.tsx +53 -0
  132. package/src/components/ui/button.tsx +59 -0
  133. package/src/components/ui/card.tsx +92 -0
  134. package/src/components/ui/checkbox.tsx +32 -0
  135. package/src/components/ui/dialog.tsx +143 -0
  136. package/src/components/ui/drawer.tsx +135 -0
  137. package/src/components/ui/dropdown-menu.tsx +257 -0
  138. package/src/components/ui/form.tsx +167 -0
  139. package/src/components/ui/input-otp.tsx +77 -0
  140. package/src/components/ui/input.tsx +21 -0
  141. package/src/components/ui/label.tsx +24 -0
  142. package/src/components/ui/select.tsx +185 -0
  143. package/src/components/ui/separator.tsx +28 -0
  144. package/src/components/ui/skeleton.tsx +13 -0
  145. package/src/components/ui/tabs.tsx +66 -0
  146. package/src/components/ui/textarea.tsx +18 -0
  147. package/src/components/user-avatar.tsx +147 -0
  148. package/src/components/user-button.tsx +409 -0
  149. package/src/components/user-view.tsx +138 -0
  150. package/src/hooks/use-auth-data.ts +184 -0
  151. package/src/hooks/use-authenticate.ts +62 -0
  152. package/src/hooks/use-captcha.tsx +138 -0
  153. package/src/hooks/use-current-organization.ts +59 -0
  154. package/src/hooks/use-hydrated.ts +13 -0
  155. package/src/hooks/use-lang.ts +32 -0
  156. package/src/hooks/use-success-transition.ts +51 -0
  157. package/src/hooks/use-theme.ts +39 -0
  158. package/src/index.ts +65 -0
  159. package/src/instantdb.ts +1 -0
  160. package/src/lib/auth-data-cache.ts +90 -0
  161. package/src/lib/auth-ui-provider.tsx +658 -0
  162. package/src/lib/gravatar-utils.ts +58 -0
  163. package/src/lib/image-utils.ts +55 -0
  164. package/src/lib/instantdb/model-names.ts +24 -0
  165. package/src/lib/instantdb/use-instant-options.ts +98 -0
  166. package/src/lib/instantdb/use-list-accounts.ts +38 -0
  167. package/src/lib/instantdb/use-list-sessions.ts +53 -0
  168. package/src/lib/instantdb/use-session.ts +55 -0
  169. package/src/lib/organization-refetcher.tsx +56 -0
  170. package/src/lib/social-providers.ts +144 -0
  171. package/src/lib/tanstack/auth-ui-provider-tanstack.tsx +49 -0
  172. package/src/lib/tanstack/use-tanstack-options.ts +112 -0
  173. package/src/lib/triplit/model-names.ts +24 -0
  174. package/src/lib/triplit/use-conditional-query.ts +82 -0
  175. package/src/lib/triplit/use-list-accounts.ts +31 -0
  176. package/src/lib/triplit/use-list-sessions.ts +33 -0
  177. package/src/lib/triplit/use-session.ts +42 -0
  178. package/src/lib/triplit/use-triplit-hooks.ts +68 -0
  179. package/src/lib/triplit/use-triplit-token.ts +44 -0
  180. package/src/lib/utils.ts +105 -0
  181. package/src/lib/view-paths.ts +55 -0
  182. package/src/localization/admin-error-codes.ts +20 -0
  183. package/src/localization/anonymous-error-codes.ts +6 -0
  184. package/src/localization/api-key-error-codes.ts +32 -0
  185. package/src/localization/auth-localization.ts +740 -0
  186. package/src/localization/base-error-codes.ts +27 -0
  187. package/src/localization/captcha-error-codes.ts +17 -0
  188. package/src/localization/email-otp-error-codes.ts +7 -0
  189. package/src/localization/generic-oauth-error-codes.ts +3 -0
  190. package/src/localization/haveibeenpwned-error-codes.ts +4 -0
  191. package/src/localization/multi-session-error-codes.ts +3 -0
  192. package/src/localization/organization-error-codes.ts +57 -0
  193. package/src/localization/passkey-error-codes.ts +10 -0
  194. package/src/localization/phone-number-error-codes.ts +10 -0
  195. package/src/localization/stripe-localization.ts +12 -0
  196. package/src/localization/two-factor-error-codes.ts +12 -0
  197. package/src/localization/username-error-codes.ts +9 -0
  198. package/src/server.ts +4 -0
  199. package/src/style.css +1 -0
  200. package/src/tanstack.ts +1 -0
  201. package/src/triplit.ts +1 -0
  202. package/src/types/account-options.ts +35 -0
  203. package/src/types/additional-fields.ts +21 -0
  204. package/src/types/any-auth-client.ts +6 -0
  205. package/src/types/api-key.ts +9 -0
  206. package/src/types/auth-client.ts +37 -0
  207. package/src/types/auth-hooks.ts +61 -0
  208. package/src/types/auth-mutators.ts +17 -0
  209. package/src/types/avatar-options.ts +29 -0
  210. package/src/types/captcha-options.ts +32 -0
  211. package/src/types/captcha-provider.ts +6 -0
  212. package/src/types/credentials-options.ts +32 -0
  213. package/src/types/delete-user-options.ts +7 -0
  214. package/src/types/fetch-error.ts +6 -0
  215. package/src/types/generic-oauth-options.ts +16 -0
  216. package/src/types/gravatar-options.ts +21 -0
  217. package/src/types/image.ts +7 -0
  218. package/src/types/invitation.ts +10 -0
  219. package/src/types/link.ts +7 -0
  220. package/src/types/organization-options.ts +106 -0
  221. package/src/types/password-validation.ts +16 -0
  222. package/src/types/profile.ts +15 -0
  223. package/src/types/refetch.ts +1 -0
  224. package/src/types/render-toast.ts +9 -0
  225. package/src/types/sign-up-options.ts +7 -0
  226. package/src/types/social-options.ts +16 -0
@@ -0,0 +1,55 @@
1
+ export async function resizeAndCropImage(
2
+ file: File,
3
+ name: string,
4
+ size: number,
5
+ extension: string
6
+ ): Promise<File> {
7
+ const image = await loadImage(file)
8
+
9
+ const canvas = document.createElement("canvas")
10
+ canvas.width = canvas.height = size
11
+
12
+ const ctx = canvas.getContext("2d")
13
+
14
+ const minEdge = Math.min(image.width, image.height)
15
+
16
+ const sx = (image.width - minEdge) / 2
17
+ const sy = (image.height - minEdge) / 2
18
+ const sWidth = minEdge
19
+ const sHeight = minEdge
20
+
21
+ ctx?.drawImage(image, sx, sy, sWidth, sHeight, 0, 0, size, size)
22
+
23
+ const resizedImageBlob = await new Promise<Blob | null>((resolve) =>
24
+ canvas.toBlob(resolve, `image/${extension}`)
25
+ )
26
+
27
+ return new File([resizedImageBlob as BlobPart], `${name}.${extension}`, {
28
+ type: `image/${extension}`
29
+ })
30
+ }
31
+
32
+ async function loadImage(file: File): Promise<HTMLImageElement> {
33
+ return new Promise((resolve, reject) => {
34
+ const image = new Image()
35
+ const reader = new FileReader()
36
+
37
+ reader.onload = (e) => {
38
+ image.src = e.target?.result as string
39
+ }
40
+
41
+ image.onload = () => resolve(image)
42
+ image.onerror = (err) => reject(err)
43
+
44
+ reader.readAsDataURL(file)
45
+ })
46
+ }
47
+
48
+ export async function fileToBase64(file: File): Promise<string> {
49
+ return new Promise((resolve, reject) => {
50
+ const reader = new FileReader()
51
+ reader.onloadend = () => resolve(reader.result as string)
52
+ reader.onerror = reject
53
+ reader.readAsDataURL(file)
54
+ })
55
+ }
@@ -0,0 +1,24 @@
1
+ const namespaces = [
2
+ "user",
3
+ "session",
4
+ "account",
5
+ "passkey",
6
+ "twoFactor"
7
+ ] as const
8
+ type Namespace = (typeof namespaces)[number]
9
+
10
+ export type ModelNames = {
11
+ [key in Namespace]: string
12
+ }
13
+
14
+ export const getModelName = ({
15
+ namespace,
16
+ modelNames,
17
+ usePlural = false
18
+ }: {
19
+ namespace: Namespace
20
+ modelNames?: Partial<ModelNames>
21
+ usePlural?: boolean
22
+ }) => {
23
+ return modelNames?.[namespace] || `${namespace}${usePlural ? "s" : ""}`
24
+ }
@@ -0,0 +1,98 @@
1
+ import type { InstantReactWebDatabase } from "@instantdb/react"
2
+ import type { User } from "better-auth"
3
+ import { useMemo } from "react"
4
+
5
+ import type { Session } from "../../types/auth-client"
6
+ import type { AuthHooks } from "../../types/auth-hooks"
7
+ import type { AuthMutators } from "../../types/auth-mutators"
8
+ import type { Refetch } from "../../types/refetch"
9
+ import { getModelName } from "./model-names"
10
+ import { useListAccounts } from "./use-list-accounts"
11
+ import { useListSessions } from "./use-list-sessions"
12
+ import { useSession } from "./use-session"
13
+
14
+ const namespaces = ["user", "session", "account", "passkey"] as const
15
+ type Namespace = (typeof namespaces)[number]
16
+
17
+ type ModelNames = {
18
+ [key in Namespace]: string
19
+ }
20
+
21
+ export interface UseInstantOptionsProps {
22
+ // biome-ignore lint/suspicious/noExplicitAny: ignore
23
+ db: InstantReactWebDatabase<any>
24
+ modelNames?: Partial<ModelNames>
25
+ usePlural?: boolean
26
+ sessionData?: { user: User; session: Session }
27
+ refetch?: Refetch
28
+ user?: { id: string } | null
29
+ isPending: boolean
30
+ }
31
+
32
+ export function useInstantOptions({
33
+ db,
34
+ usePlural = true,
35
+ modelNames,
36
+ sessionData,
37
+ isPending,
38
+ user
39
+ }: UseInstantOptionsProps) {
40
+ const userId = user?.id || sessionData?.user.id
41
+
42
+ const hooks = useMemo(() => {
43
+ return {
44
+ useSession: () =>
45
+ useSession({
46
+ db,
47
+ modelNames,
48
+ usePlural,
49
+ sessionData,
50
+ isPending
51
+ }),
52
+ useListAccounts: () =>
53
+ useListAccounts({
54
+ db,
55
+ modelNames,
56
+ usePlural,
57
+ sessionData,
58
+ isPending
59
+ }),
60
+ useListSessions: () =>
61
+ useListSessions({
62
+ db,
63
+ modelNames,
64
+ usePlural,
65
+ sessionData,
66
+ isPending
67
+ })
68
+ } as AuthHooks
69
+ }, [db, modelNames, usePlural, sessionData, isPending])
70
+
71
+ const mutators = useMemo(() => {
72
+ return {
73
+ updateUser: async (data) => {
74
+ if (!userId) {
75
+ throw new Error("Unauthenticated")
76
+ }
77
+
78
+ const modelName = getModelName({
79
+ namespace: "user",
80
+ modelNames,
81
+ usePlural
82
+ })
83
+
84
+ db.transact([
85
+ db.tx[modelName][userId].update({
86
+ ...data,
87
+ updatedAt: Date.now()
88
+ })
89
+ ])
90
+ }
91
+ } as AuthMutators
92
+ }, [db, userId, modelNames, usePlural])
93
+
94
+ return {
95
+ hooks,
96
+ mutators
97
+ }
98
+ }
@@ -0,0 +1,38 @@
1
+ import type { BetterFetchError } from "@better-fetch/fetch"
2
+ import type { Account } from "better-auth"
3
+ import { useMemo } from "react"
4
+ import type { AuthHooks } from "../../types/auth-hooks"
5
+ import { getModelName } from "./model-names"
6
+ import type { UseInstantOptionsProps } from "./use-instant-options"
7
+
8
+ export function useListAccounts({
9
+ db,
10
+ modelNames,
11
+ usePlural,
12
+ isPending
13
+ }: UseInstantOptionsProps): ReturnType<AuthHooks["useListAccounts"]> {
14
+ const { user: authUser, isLoading: authLoading } = db.useAuth()
15
+
16
+ const modelName = getModelName({
17
+ namespace: "account",
18
+ modelNames,
19
+ usePlural
20
+ })
21
+
22
+ const { data, isLoading, error } = db.useQuery(
23
+ authUser
24
+ ? { [modelName]: { $: { where: { userId: authUser?.id } } } }
25
+ : null
26
+ )
27
+
28
+ const accounts = useMemo(
29
+ () => data?.[modelName] as Account[],
30
+ [data, modelName]
31
+ )
32
+
33
+ return {
34
+ data: accounts,
35
+ isPending: !accounts && (isPending || authLoading || isLoading),
36
+ error: (error as BetterFetchError) || null
37
+ }
38
+ }
@@ -0,0 +1,53 @@
1
+ import type { Session } from "better-auth"
2
+ import { useMemo } from "react"
3
+ import type { AuthHooks } from "../../types/auth-hooks"
4
+ import { getModelName } from "./model-names"
5
+ import type { UseInstantOptionsProps } from "./use-instant-options"
6
+
7
+ export function useListSessions({
8
+ db,
9
+ modelNames,
10
+ usePlural,
11
+ isPending
12
+ }: UseInstantOptionsProps): ReturnType<AuthHooks["useListSessions"]> {
13
+ const { user: authUser, isLoading: authLoading } = db.useAuth()
14
+
15
+ const modelName = getModelName({
16
+ namespace: "session",
17
+ modelNames,
18
+ usePlural
19
+ })
20
+
21
+ const now = useMemo(() => Date.now(), [])
22
+
23
+ const { data, isLoading } = db.useQuery(
24
+ authUser
25
+ ? {
26
+ [modelName]: {
27
+ $: {
28
+ where: {
29
+ userId: authUser?.id,
30
+ expiresAt: { $gte: now }
31
+ }
32
+ }
33
+ }
34
+ }
35
+ : null
36
+ )
37
+
38
+ const sessions = useMemo(() => {
39
+ if (data?.[modelName]) {
40
+ return data[modelName].map((session) => ({
41
+ ...session,
42
+ expiresAt: new Date(session.expiresAt as string),
43
+ createdAt: new Date(session.createdAt as string),
44
+ updatedAt: new Date(session.updatedAt as string)
45
+ })) as Session[]
46
+ }
47
+ }, [data, modelName])
48
+
49
+ return {
50
+ data: sessions,
51
+ isPending: !sessions && (isPending || authLoading || isLoading)
52
+ }
53
+ }
@@ -0,0 +1,55 @@
1
+ import type { BetterFetchError } from "better-auth/react"
2
+ import { useMemo } from "react"
3
+ import type { User } from "../../types/auth-client"
4
+ import type { AuthHooks } from "../../types/auth-hooks"
5
+ import { getModelName } from "./model-names"
6
+ import type { UseInstantOptionsProps } from "./use-instant-options"
7
+
8
+ export function useSession({
9
+ db,
10
+ sessionData,
11
+ isPending,
12
+ refetch,
13
+ usePlural,
14
+ modelNames
15
+ }: UseInstantOptionsProps): ReturnType<AuthHooks["useSession"]> {
16
+ const { user: authUser, error } = db.useAuth()
17
+
18
+ const modelName = getModelName({
19
+ namespace: "user",
20
+ modelNames,
21
+ usePlural
22
+ })
23
+
24
+ const { data } = db.useQuery(
25
+ authUser
26
+ ? { [modelName]: { $: { where: { id: authUser?.id } } } }
27
+ : null
28
+ )
29
+
30
+ const user = useMemo(() => {
31
+ if (data?.[modelName]?.length) {
32
+ const user = data[modelName][0]
33
+ return {
34
+ ...user,
35
+ createdAt: new Date(user.createdAt as string),
36
+ updatedAt: new Date(user.updatedAt as string)
37
+ } as User
38
+ }
39
+ return null
40
+ }, [data, modelName])
41
+
42
+ return {
43
+ data: sessionData
44
+ ? {
45
+ session: sessionData.session,
46
+ user: (sessionData?.user.id === user?.id
47
+ ? user
48
+ : sessionData.user) as User
49
+ }
50
+ : null,
51
+ isPending,
52
+ refetch: refetch || (() => {}),
53
+ error: (error as BetterFetchError) || null
54
+ }
55
+ }
@@ -0,0 +1,56 @@
1
+ import { useContext, useEffect } from "react"
2
+ import { useCurrentOrganization } from "../hooks/use-current-organization"
3
+ import { AuthUIContext } from "./auth-ui-provider"
4
+
5
+ export const OrganizationRefetcher = () => {
6
+ const {
7
+ hooks: { useListOrganizations, useSession },
8
+ organization: organizationOptions,
9
+ navigate,
10
+ redirectTo
11
+ } = useContext(AuthUIContext)
12
+
13
+ const { slug, pathMode, personalPath } = organizationOptions || {}
14
+
15
+ const { data: sessionData } = useSession()
16
+
17
+ const {
18
+ data: organization,
19
+ isPending: organizationPending,
20
+ isRefetching: organizationRefetching,
21
+ refetch: refetchOrganization
22
+ } = useCurrentOrganization()
23
+
24
+ const { refetch: refetchListOrganizations } = useListOrganizations()
25
+
26
+ const { data: organizations } = useListOrganizations()
27
+
28
+ // biome-ignore lint/correctness/useExhaustiveDependencies: Refetch fix
29
+ useEffect(() => {
30
+ if (!sessionData?.user.id) return
31
+
32
+ if (organization || organizations) {
33
+ refetchOrganization?.()
34
+ refetchListOrganizations?.()
35
+ }
36
+ }, [sessionData?.user.id])
37
+
38
+ useEffect(() => {
39
+ if (organizationRefetching || organizationPending) return
40
+
41
+ if (slug && pathMode === "slug" && !organization) {
42
+ navigate(personalPath || redirectTo)
43
+ }
44
+ }, [
45
+ organization,
46
+ organizationRefetching,
47
+ organizationPending,
48
+ slug,
49
+ pathMode,
50
+ personalPath,
51
+ navigate,
52
+ redirectTo
53
+ ])
54
+
55
+ return null
56
+ }
@@ -0,0 +1,144 @@
1
+ import {
2
+ AppleIcon,
3
+ DiscordIcon,
4
+ DropboxIcon,
5
+ FacebookIcon,
6
+ GitHubIcon,
7
+ GitLabIcon,
8
+ GoogleIcon,
9
+ HuggingFaceIcon,
10
+ KickIcon,
11
+ LinearIcon,
12
+ LinkedInIcon,
13
+ MicrosoftIcon,
14
+ NotionIcon,
15
+ type ProviderIcon,
16
+ RedditIcon,
17
+ RobloxIcon,
18
+ SlackIcon,
19
+ SpotifyIcon,
20
+ TikTokIcon,
21
+ TwitchIcon,
22
+ VKIcon,
23
+ XIcon,
24
+ ZoomIcon
25
+ } from "../components/provider-icons"
26
+
27
+ export const socialProviders = [
28
+ {
29
+ provider: "apple",
30
+ name: "Apple",
31
+ icon: AppleIcon
32
+ },
33
+ {
34
+ provider: "discord",
35
+ name: "Discord",
36
+ icon: DiscordIcon
37
+ },
38
+ {
39
+ provider: "dropbox",
40
+ name: "Dropbox",
41
+ icon: DropboxIcon
42
+ },
43
+ {
44
+ provider: "facebook",
45
+ name: "Facebook",
46
+ icon: FacebookIcon
47
+ },
48
+ {
49
+ provider: "github",
50
+ name: "GitHub",
51
+ icon: GitHubIcon
52
+ },
53
+ {
54
+ provider: "gitlab",
55
+ name: "GitLab",
56
+ icon: GitLabIcon
57
+ },
58
+ {
59
+ provider: "google",
60
+ name: "Google",
61
+ icon: GoogleIcon
62
+ },
63
+ {
64
+ provider: "huggingface",
65
+ name: "Hugging Face",
66
+ icon: HuggingFaceIcon
67
+ },
68
+ {
69
+ provider: "kick",
70
+ name: "Kick",
71
+ icon: KickIcon
72
+ },
73
+ {
74
+ provider: "linear",
75
+ name: "Linear",
76
+ icon: LinearIcon
77
+ },
78
+ {
79
+ provider: "linkedin",
80
+ name: "LinkedIn",
81
+ icon: LinkedInIcon
82
+ },
83
+ {
84
+ provider: "microsoft",
85
+ name: "Microsoft",
86
+ icon: MicrosoftIcon
87
+ },
88
+ {
89
+ provider: "notion",
90
+ name: "Notion",
91
+ icon: NotionIcon
92
+ },
93
+ {
94
+ provider: "reddit",
95
+ name: "Reddit",
96
+ icon: RedditIcon
97
+ },
98
+ {
99
+ provider: "roblox",
100
+ name: "Roblox",
101
+ icon: RobloxIcon
102
+ },
103
+ {
104
+ provider: "slack",
105
+ name: "Slack",
106
+ icon: SlackIcon
107
+ },
108
+ {
109
+ provider: "spotify",
110
+ name: "Spotify",
111
+ icon: SpotifyIcon
112
+ },
113
+ {
114
+ provider: "tiktok",
115
+ name: "TikTok",
116
+ icon: TikTokIcon
117
+ },
118
+ {
119
+ provider: "twitch",
120
+ name: "Twitch",
121
+ icon: TwitchIcon
122
+ },
123
+ {
124
+ provider: "vk",
125
+ name: "VK",
126
+ icon: VKIcon
127
+ },
128
+ {
129
+ provider: "twitter",
130
+ name: "X",
131
+ icon: XIcon
132
+ },
133
+ {
134
+ provider: "zoom",
135
+ name: "Zoom",
136
+ icon: ZoomIcon
137
+ }
138
+ ] as const
139
+
140
+ export type Provider = {
141
+ provider: string
142
+ name: string
143
+ icon?: ProviderIcon
144
+ }
@@ -0,0 +1,49 @@
1
+ import { useCallback, useMemo } from "react"
2
+ import {
3
+ AuthUIProvider,
4
+ type AuthUIProviderProps
5
+ } from "../../lib/auth-ui-provider"
6
+ import { useTanstackOptions } from "./use-tanstack-options"
7
+
8
+ export function AuthUIProviderTanstack({
9
+ children,
10
+ authClient,
11
+ hooks: hooksProp,
12
+ mutators: mutatorsProp,
13
+ onSessionChange: onSessionChangeProp,
14
+ ...props
15
+ }: AuthUIProviderProps) {
16
+ const {
17
+ hooks: contextHooks,
18
+ mutators: contextMutators,
19
+ onSessionChange,
20
+ optimistic
21
+ } = useTanstackOptions({ authClient })
22
+
23
+ const hooks = useMemo(
24
+ () => ({ ...contextHooks, ...hooksProp }),
25
+ [contextHooks, hooksProp]
26
+ )
27
+ const mutators = useMemo(
28
+ () => ({ ...contextMutators, ...mutatorsProp }),
29
+ [contextMutators, mutatorsProp]
30
+ )
31
+
32
+ const onSessionChangeCallback = useCallback(async () => {
33
+ await onSessionChange()
34
+ await onSessionChangeProp?.()
35
+ }, [onSessionChangeProp, onSessionChange])
36
+
37
+ return (
38
+ <AuthUIProvider
39
+ authClient={authClient}
40
+ hooks={hooks}
41
+ mutators={mutators}
42
+ onSessionChange={onSessionChangeCallback}
43
+ optimistic={optimistic}
44
+ {...props}
45
+ >
46
+ {children}
47
+ </AuthUIProvider>
48
+ )
49
+ }
@@ -0,0 +1,112 @@
1
+ import {
2
+ AuthQueryContext,
3
+ createAuthHooks
4
+ } from "@daveyplate/better-auth-tanstack"
5
+ import { useIsRestoring, useQueryClient } from "@tanstack/react-query"
6
+ import { useCallback, useContext, useMemo } from "react"
7
+ import type { AuthHooks } from "../../types/auth-hooks"
8
+ import type { AuthMutators } from "../../types/auth-mutators"
9
+
10
+ export function useTanstackOptions({
11
+ authClient
12
+ }: {
13
+ // biome-ignore lint/suspicious/noExplicitAny: ignore
14
+ authClient: any
15
+ }) {
16
+ const {
17
+ useUnlinkAccount,
18
+ useUpdateUser,
19
+ useDeletePasskey,
20
+ useRevokeSession,
21
+ useRevokeDeviceSession,
22
+ useSetActiveSession
23
+ } = createAuthHooks(authClient)
24
+ const queryClient = useQueryClient()
25
+
26
+ const { mutateAsync: updateUserAsync } = useUpdateUser()
27
+ const { mutateAsync: deletePasskeyAsync } = useDeletePasskey()
28
+ const { mutateAsync: unlinkAccountAsync } = useUnlinkAccount()
29
+ const { mutateAsync: revokeSessionAsync } = useRevokeSession()
30
+ const { mutateAsync: revokeDeviceSessionAsync } = useRevokeDeviceSession()
31
+ const { setActiveSessionAsync } = useSetActiveSession()
32
+ const { sessionKey } = useContext(AuthQueryContext)
33
+
34
+ const hooks = useMemo(
35
+ () => ({
36
+ ...(createAuthHooks(authClient) as Partial<AuthHooks>),
37
+ useIsRestoring
38
+ }),
39
+ [authClient]
40
+ )
41
+
42
+ const mutators = useMemo(
43
+ () =>
44
+ ({
45
+ updateUser: async (params) => {
46
+ const { error } = await updateUserAsync({
47
+ ...params,
48
+ fetchOptions: { throw: false }
49
+ })
50
+ if (error) throw error
51
+ },
52
+ unlinkAccount: async (params) => {
53
+ const { error } = await unlinkAccountAsync({
54
+ ...params,
55
+ fetchOptions: { throw: false }
56
+ })
57
+ if (error) throw error
58
+ },
59
+ deletePasskey: async (params) => {
60
+ const { error } = await deletePasskeyAsync({
61
+ ...params,
62
+ fetchOptions: { throw: false }
63
+ })
64
+ if (error) throw error
65
+ },
66
+ revokeSession: async (params) => {
67
+ const { error } = await revokeSessionAsync({
68
+ ...params,
69
+ fetchOptions: { throw: false }
70
+ })
71
+ if (error) throw error
72
+ },
73
+ setActiveSession: async (params) => {
74
+ const { error } = await setActiveSessionAsync({
75
+ ...params,
76
+ fetchOptions: { throw: false }
77
+ })
78
+ if (error) throw error
79
+ },
80
+ revokeDeviceSession: async (params) => {
81
+ const { error } = await revokeDeviceSessionAsync({
82
+ ...params,
83
+ fetchOptions: { throw: false }
84
+ })
85
+ if (error) throw error
86
+ }
87
+ }) as AuthMutators,
88
+ [
89
+ updateUserAsync,
90
+ deletePasskeyAsync,
91
+ unlinkAccountAsync,
92
+ revokeSessionAsync,
93
+ revokeDeviceSessionAsync,
94
+ setActiveSessionAsync
95
+ ]
96
+ )
97
+
98
+ const onSessionChange = useCallback(async () => {
99
+ await queryClient.refetchQueries({ queryKey: sessionKey })
100
+
101
+ queryClient.invalidateQueries({
102
+ predicate: (query) => query.queryKey !== sessionKey
103
+ })
104
+ }, [queryClient, sessionKey])
105
+
106
+ return {
107
+ hooks,
108
+ mutators,
109
+ onSessionChange,
110
+ optimistic: true
111
+ }
112
+ }