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,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,82 @@
1
+ import type {
2
+ FetchResult,
3
+ Models,
4
+ SchemaQuery,
5
+ SubscriptionOptions,
6
+ SubscriptionSignalPayload,
7
+ TriplitClient
8
+ } from "@triplit/client"
9
+ import type { WorkerClient } from "@triplit/client/worker-client"
10
+ import { createStateSubscription } from "@triplit/react"
11
+ import { useCallback, useMemo, useState, useSyncExternalStore } from "react"
12
+
13
+ export function useConditionalQuery<
14
+ M extends Models<M>,
15
+ Q extends SchemaQuery<M>
16
+ >(
17
+ client: TriplitClient<M> | WorkerClient<M>,
18
+ query?: Q | false | null | "" | 0,
19
+ options?: Partial<SubscriptionOptions> & { disabled?: boolean }
20
+ ) {
21
+ const stringifiedQuery =
22
+ !options?.disabled && query && JSON.stringify(query)
23
+ const localOnly = !!options?.localOnly
24
+ const [remoteFulfilled, setRemoteFulfilled] = useState(false)
25
+
26
+ const defaultValue: SubscriptionSignalPayload<M, Q> = {
27
+ results: undefined,
28
+ fetching: true,
29
+ fetchingLocal: false,
30
+ fetchingRemote: false,
31
+ error: undefined
32
+ }
33
+
34
+ // biome-ignore lint/correctness/useExhaustiveDependencies: prevent infinite re-renders
35
+ const [subscribe, snapshot] = useMemo(
36
+ () =>
37
+ stringifiedQuery
38
+ ? createStateSubscription(client, query, {
39
+ ...options,
40
+ onRemoteFulfilled: () => setRemoteFulfilled(true)
41
+ })
42
+ : [() => () => {}, () => defaultValue],
43
+ [stringifiedQuery, localOnly]
44
+ )
45
+
46
+ const getServerSnapshot = useCallback(() => snapshot(), [snapshot])
47
+ const { fetching, ...rest } = useSyncExternalStore(
48
+ subscribe,
49
+ snapshot,
50
+ getServerSnapshot
51
+ )
52
+ return { fetching: fetching && !remoteFulfilled, ...rest }
53
+ }
54
+
55
+ type useConditionalQueryOnePayload<
56
+ M extends Models<M>,
57
+ Q extends SchemaQuery<M>
58
+ > = Omit<SubscriptionSignalPayload<M, Q>, "results"> & {
59
+ result: FetchResult<M, Q, "one">
60
+ }
61
+
62
+ export function useConditionalQueryOne<
63
+ M extends Models<M>,
64
+ Q extends SchemaQuery<M>
65
+ >(
66
+ client: TriplitClient<M> | WorkerClient<M>,
67
+ query?: Q | false | null | "" | 0,
68
+ options?: Partial<SubscriptionOptions> & { disabled?: boolean }
69
+ ): useConditionalQueryOnePayload<M, Q> {
70
+ const { fetching, fetchingLocal, fetchingRemote, results, error } =
71
+ useConditionalQuery(
72
+ client,
73
+ query ? ({ ...query, limit: 1 } as Q) : query,
74
+ options
75
+ )
76
+
77
+ const result = useMemo(() => {
78
+ return results?.[0] ?? null
79
+ }, [results])
80
+
81
+ return { fetching, fetchingLocal, fetchingRemote, result, error }
82
+ }
@@ -0,0 +1,31 @@
1
+ import type { AuthHooks } from "../../types/auth-hooks"
2
+ import { getModelName } from "./model-names"
3
+ import { useConditionalQuery } from "./use-conditional-query"
4
+ import type { UseTriplitOptionsProps } from "./use-triplit-hooks"
5
+ import { useTriplitToken } from "./use-triplit-token"
6
+
7
+ export function useListAccounts({
8
+ triplit,
9
+ modelNames,
10
+ usePlural,
11
+ isPending
12
+ }: UseTriplitOptionsProps): ReturnType<AuthHooks["useListAccounts"]> {
13
+ const modelName = getModelName({
14
+ namespace: "account",
15
+ modelNames,
16
+ usePlural
17
+ })
18
+
19
+ const { payload } = useTriplitToken(triplit)
20
+
21
+ const { results, error, fetching } = useConditionalQuery(
22
+ triplit,
23
+ payload?.sub && triplit.query(modelName)
24
+ )
25
+
26
+ return {
27
+ data: results,
28
+ isPending: isPending || fetching,
29
+ error
30
+ }
31
+ }
@@ -0,0 +1,33 @@
1
+ import type { Session } from "better-auth"
2
+ import type { AuthHooks } from "../../types/auth-hooks"
3
+ import { getModelName } from "./model-names"
4
+ import { useConditionalQuery } from "./use-conditional-query"
5
+ import type { UseTriplitOptionsProps } from "./use-triplit-hooks"
6
+ import { useTriplitToken } from "./use-triplit-token"
7
+
8
+ export function useListSessions({
9
+ triplit,
10
+ modelNames,
11
+ usePlural,
12
+ isPending
13
+ }: UseTriplitOptionsProps): ReturnType<AuthHooks["useListSessions"]> {
14
+ const modelName = getModelName({
15
+ namespace: "session",
16
+ modelNames,
17
+ usePlural
18
+ })
19
+
20
+ const { payload } = useTriplitToken(triplit)
21
+
22
+ const {
23
+ results: sessions,
24
+ error,
25
+ fetching
26
+ } = useConditionalQuery(triplit, payload?.sub && triplit.query(modelName))
27
+
28
+ return {
29
+ data: sessions as Session[] | undefined,
30
+ isPending: isPending || fetching,
31
+ error
32
+ }
33
+ }
@@ -0,0 +1,42 @@
1
+ import type { User } from "../../types/auth-client"
2
+ import type { AuthHooks } from "../../types/auth-hooks"
3
+ import { getModelName } from "./model-names"
4
+ import { useConditionalQueryOne } from "./use-conditional-query"
5
+ import type { UseTriplitOptionsProps } from "./use-triplit-hooks"
6
+ import { useTriplitToken } from "./use-triplit-token"
7
+
8
+ export function useSession({
9
+ triplit,
10
+ sessionData,
11
+ isPending,
12
+ refetch,
13
+ usePlural,
14
+ modelNames
15
+ }: UseTriplitOptionsProps): ReturnType<AuthHooks["useSession"]> {
16
+ const modelName = getModelName({
17
+ namespace: "user",
18
+ modelNames,
19
+ usePlural
20
+ })
21
+
22
+ const { payload } = useTriplitToken(triplit)
23
+
24
+ const { result: user, error } = useConditionalQueryOne(
25
+ triplit,
26
+ payload?.sub && triplit.query(modelName)
27
+ )
28
+
29
+ return {
30
+ data: sessionData
31
+ ? {
32
+ session: sessionData.session,
33
+ user: (sessionData?.user.id === user?.id
34
+ ? user
35
+ : sessionData.user) as User
36
+ }
37
+ : null,
38
+ error,
39
+ isPending: isPending,
40
+ refetch: refetch || (() => {})
41
+ }
42
+ }
@@ -0,0 +1,68 @@
1
+ import type { TriplitClient } from "@triplit/client"
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 { Refetch } from "../../types/refetch"
8
+ import { useListAccounts } from "./use-list-accounts"
9
+ import { useListSessions } from "./use-list-sessions"
10
+ import { useSession } from "./use-session"
11
+
12
+ const namespaces = ["user", "session", "account", "passkey"] as const
13
+ type Namespace = (typeof namespaces)[number]
14
+
15
+ type ModelNames = {
16
+ [key in Namespace]: string
17
+ }
18
+
19
+ export interface UseTriplitOptionsProps {
20
+ // biome-ignore lint/suspicious/noExplicitAny: ignore
21
+ triplit: TriplitClient<any>
22
+ modelNames?: Partial<ModelNames>
23
+ usePlural?: boolean
24
+ sessionData?: { user: User; session: Session } | null
25
+ refetch?: Refetch
26
+ isPending: boolean
27
+ }
28
+
29
+ export function useTriplitHooks({
30
+ triplit,
31
+ usePlural = true,
32
+ modelNames,
33
+ sessionData,
34
+ isPending
35
+ }: UseTriplitOptionsProps) {
36
+ const hooks = useMemo(() => {
37
+ return {
38
+ useSession: () =>
39
+ useSession({
40
+ triplit,
41
+ modelNames,
42
+ usePlural,
43
+ sessionData,
44
+ isPending
45
+ }),
46
+ useListAccounts: () =>
47
+ useListAccounts({
48
+ triplit,
49
+ modelNames,
50
+ usePlural,
51
+ sessionData,
52
+ isPending
53
+ }),
54
+ useListSessions: () =>
55
+ useListSessions({
56
+ triplit,
57
+ modelNames,
58
+ usePlural,
59
+ sessionData,
60
+ isPending
61
+ })
62
+ } as AuthHooks
63
+ }, [triplit, modelNames, usePlural, sessionData, isPending])
64
+
65
+ return {
66
+ hooks
67
+ }
68
+ }
@@ -0,0 +1,44 @@
1
+ import type { TriplitClient } from "@triplit/client"
2
+ import { useConnectionStatus } from "@triplit/react"
3
+ import { useMemo } from "react"
4
+
5
+ export function useTriplitToken(triplit: TriplitClient) {
6
+ const connectionStatus = useConnectionStatus(triplit)
7
+
8
+ // biome-ignore lint/correctness/useExhaustiveDependencies: update when connection status changes
9
+ const payload = useMemo(
10
+ () =>
11
+ triplit.token
12
+ ? (decodeJWT(triplit.token) as Record<string, unknown> & {
13
+ exp: number
14
+ iat: number
15
+ sub?: string
16
+ email?: string
17
+ name?: string
18
+ })
19
+ : undefined,
20
+ [connectionStatus]
21
+ )
22
+
23
+ return { token: payload && triplit.token, payload }
24
+ }
25
+
26
+ function decodeJWT(token: string) {
27
+ try {
28
+ const base64Url = token.split(".")[1]
29
+ const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/")
30
+ const jsonPayload = decodeURIComponent(
31
+ atob(base64)
32
+ .split("")
33
+ .map((char) => {
34
+ return `%${(`00${char.charCodeAt(0).toString(16)}`).slice(-2)}`
35
+ })
36
+ .join("")
37
+ )
38
+
39
+ return JSON.parse(jsonPayload)
40
+ } catch (error) {
41
+ console.error("Failed to decode JWT:", error)
42
+ return null
43
+ }
44
+ }
@@ -0,0 +1,105 @@
1
+ import { type ClassValue, clsx } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+ import * as z from "zod"
4
+ import type { AuthLocalization } from "../localization/auth-localization"
5
+ import type { PasswordValidation } from "../types/password-validation"
6
+
7
+ export function cn(...inputs: ClassValue[]) {
8
+ return twMerge(clsx(inputs))
9
+ }
10
+
11
+ export function isValidEmail(email: string) {
12
+ const emailRegex: RegExp = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
13
+ return emailRegex.test(email)
14
+ }
15
+
16
+ /**
17
+ * Converts error codes from SNAKE_CASE to camelCase
18
+ * Example: INVALID_TWO_FACTOR_COOKIE -> invalidTwoFactorCookie
19
+ */
20
+ export function errorCodeToCamelCase(errorCode: string): string {
21
+ return errorCode
22
+ .toLowerCase()
23
+ .replace(/_([a-z])/g, (_, char) => char.toUpperCase())
24
+ }
25
+
26
+ /**
27
+ * Gets a localized error message from an error object
28
+ */
29
+ export function getLocalizedError({
30
+ error,
31
+ localization
32
+ }: {
33
+ // biome-ignore lint/suspicious/noExplicitAny: ignore
34
+ error: any
35
+ localization?: Partial<AuthLocalization>
36
+ }) {
37
+ if (typeof error === "string") {
38
+ if (localization?.[error as keyof AuthLocalization])
39
+ return localization[error as keyof AuthLocalization]
40
+ }
41
+
42
+ if (error?.error) {
43
+ if (error.error.code) {
44
+ const errorCode = error.error.code as keyof AuthLocalization
45
+ if (localization?.[errorCode]) return localization[errorCode]
46
+ }
47
+
48
+ return (
49
+ error.error.message ||
50
+ error.error.code ||
51
+ error.error.statusText ||
52
+ localization?.REQUEST_FAILED
53
+ )
54
+ }
55
+
56
+ return error?.message || localization?.REQUEST_FAILED || "Request failed"
57
+ }
58
+
59
+ export function getSearchParam(paramName: string) {
60
+ return typeof window !== "undefined"
61
+ ? new URLSearchParams(window.location.search).get(paramName)
62
+ : null
63
+ }
64
+
65
+ export function getViewByPath<T extends object>(viewPaths: T, path?: string) {
66
+ for (const key in viewPaths) {
67
+ if (viewPaths[key] === path) {
68
+ return key
69
+ }
70
+ }
71
+ }
72
+
73
+ export function getKeyByValue<T extends Record<string, unknown>>(
74
+ object: T,
75
+ value?: T[keyof T]
76
+ ): keyof T | undefined {
77
+ return (Object.keys(object) as Array<keyof T>).find(
78
+ (key) => object[key] === value
79
+ )
80
+ }
81
+
82
+ export function getPasswordSchema(
83
+ passwordValidation?: PasswordValidation,
84
+ localization?: AuthLocalization
85
+ ) {
86
+ let schema = z.string().min(1, {
87
+ message: localization?.PASSWORD_REQUIRED
88
+ })
89
+ if (passwordValidation?.minLength) {
90
+ schema = schema.min(passwordValidation.minLength, {
91
+ message: localization?.PASSWORD_TOO_SHORT
92
+ })
93
+ }
94
+ if (passwordValidation?.maxLength) {
95
+ schema = schema.max(passwordValidation.maxLength, {
96
+ message: localization?.PASSWORD_TOO_LONG
97
+ })
98
+ }
99
+ if (passwordValidation?.regex) {
100
+ schema = schema.regex(passwordValidation.regex, {
101
+ message: localization?.INVALID_PASSWORD
102
+ })
103
+ }
104
+ return schema
105
+ }
@@ -0,0 +1,55 @@
1
+ export const authViewPaths = {
2
+ /** @default "callback" */
3
+ CALLBACK: "callback",
4
+ /** @default "email-otp" */
5
+ EMAIL_OTP: "email-otp",
6
+ /** @default "forgot-password" */
7
+ FORGOT_PASSWORD: "forgot-password",
8
+ /** @default "magic-link" */
9
+ MAGIC_LINK: "magic-link",
10
+ /** @default "recover-account" */
11
+ RECOVER_ACCOUNT: "recover-account",
12
+ /** @default "reset-password" */
13
+ RESET_PASSWORD: "reset-password",
14
+ /** @default "sign-in" */
15
+ SIGN_IN: "sign-in",
16
+ /** @default "sign-out" */
17
+ SIGN_OUT: "sign-out",
18
+ /** @default "sign-up" */
19
+ SIGN_UP: "sign-up",
20
+ /** @default "two-factor" */
21
+ TWO_FACTOR: "two-factor",
22
+ /** @default "accept-invitation" */
23
+ ACCEPT_INVITATION: "accept-invitation"
24
+ }
25
+
26
+ export type AuthViewPaths = typeof authViewPaths
27
+
28
+ // Account-scoped views (signed-in user)
29
+ export const accountViewPaths = {
30
+ /** @default "settings" */
31
+ SETTINGS: "settings",
32
+ /** @default "security" */
33
+ SECURITY: "security",
34
+ /** @default "api-keys" */
35
+ API_KEYS: "api-keys",
36
+ /** @default "organizations" */
37
+ ORGANIZATIONS: "organizations"
38
+ }
39
+
40
+ export type AccountViewPaths = typeof accountViewPaths
41
+
42
+ // Organization-scoped views
43
+ export const organizationViewPaths = {
44
+ /** @default "settings" */
45
+ SETTINGS: "settings",
46
+ /** @default "members" */
47
+ MEMBERS: "members",
48
+ /** @default "api-keys" */
49
+ API_KEYS: "api-keys"
50
+ }
51
+
52
+ export type OrganizationViewPaths = typeof organizationViewPaths
53
+ export type AuthViewPath = keyof AuthViewPaths
54
+ export type AccountViewPath = keyof AccountViewPaths
55
+ export type OrganizationViewPath = keyof OrganizationViewPaths
@@ -0,0 +1,20 @@
1
+ export const ADMIN_ERROR_CODES = {
2
+ FAILED_TO_CREATE_USER: "Failed to create user",
3
+ USER_ALREADY_EXISTS: "User already exists",
4
+ YOU_CANNOT_BAN_YOURSELF: "You cannot ban yourself",
5
+ YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE:
6
+ "You are not allowed to change users role",
7
+ YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS: "You are not allowed to create users",
8
+ YOU_ARE_NOT_ALLOWED_TO_LIST_USERS: "You are not allowed to list users",
9
+ YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS:
10
+ "You are not allowed to list users sessions",
11
+ YOU_ARE_NOT_ALLOWED_TO_BAN_USERS: "You are not allowed to ban users",
12
+ YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS:
13
+ "You are not allowed to impersonate users",
14
+ YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS:
15
+ "You are not allowed to revoke users sessions",
16
+ YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS: "You are not allowed to delete users",
17
+ YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD:
18
+ "You are not allowed to set users password",
19
+ BANNED_USER: "You have been banned from this application"
20
+ }
@@ -0,0 +1,6 @@
1
+ export const ANONYMOUS_ERROR_CODES = {
2
+ FAILED_TO_CREATE_USER: "Failed to create user",
3
+ COULD_NOT_CREATE_SESSION: "Could not create session",
4
+ ANONYMOUS_USERS_CANNOT_SIGN_IN_AGAIN_ANONYMOUSLY:
5
+ "Anonymous users cannot sign in again anonymously"
6
+ }
@@ -0,0 +1,32 @@
1
+ export const API_KEY_ERROR_CODES = {
2
+ INVALID_METADATA_TYPE: "metadata must be an object or undefined",
3
+ REFILL_AMOUNT_AND_INTERVAL_REQUIRED:
4
+ "refillAmount is required when refillInterval is provided",
5
+ REFILL_INTERVAL_AND_AMOUNT_REQUIRED:
6
+ "refillInterval is required when refillAmount is provided",
7
+ USER_BANNED: "User is banned",
8
+ UNAUTHORIZED_SESSION: "Unauthorized or invalid session",
9
+ KEY_NOT_FOUND: "API Key not found",
10
+ KEY_DISABLED: "API Key is disabled",
11
+ KEY_EXPIRED: "API Key has expired",
12
+ USAGE_EXCEEDED: "API Key has reached its usage limit",
13
+ KEY_NOT_RECOVERABLE: "API Key is not recoverable",
14
+ EXPIRES_IN_IS_TOO_SMALL:
15
+ "The expiresIn is smaller than the predefined minimum value.",
16
+ EXPIRES_IN_IS_TOO_LARGE:
17
+ "The expiresIn is larger than the predefined maximum value.",
18
+ INVALID_REMAINING: "The remaining count is either too large or too small.",
19
+ INVALID_PREFIX_LENGTH:
20
+ "The prefix length is either too large or too small.",
21
+ INVALID_NAME_LENGTH: "The name length is either too large or too small.",
22
+ METADATA_DISABLED: "Metadata is disabled.",
23
+ RATE_LIMIT_EXCEEDED: "Rate limit exceeded.",
24
+ NO_VALUES_TO_UPDATE: "No values to update.",
25
+ KEY_DISABLED_EXPIRATION: "Custom key expiration values are disabled.",
26
+ INVALID_API_KEY: "Invalid API key.",
27
+ INVALID_USER_ID_FROM_API_KEY: "The user id from the API key is invalid.",
28
+ INVALID_API_KEY_GETTER_RETURN_TYPE:
29
+ "API Key getter returned an invalid key type. Expected string.",
30
+ SERVER_ONLY_PROPERTY:
31
+ "The property you're trying to set can only be set from the server auth instance only."
32
+ }