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.
- package/LICENSE +21 -0
- package/README.md +53 -0
- package/dist/auth-hooks-IOEvlYYv.d.cts +6966 -0
- package/dist/auth-hooks-IOEvlYYv.d.ts +6966 -0
- package/dist/auth-mutators-DdqOmQ32.d.cts +29 -0
- package/dist/auth-mutators-DdqOmQ32.d.ts +29 -0
- package/dist/auth-ui-provider-BsH3xJDw.d.ts +697 -0
- package/dist/auth-ui-provider-DhZfncd3.d.cts +697 -0
- package/dist/chunk-BDFQSFBU.js +750 -0
- package/dist/chunk-CRAHKL2C.cjs +801 -0
- package/dist/chunk-MJPOA6PK.js +801 -0
- package/dist/chunk-SV64DXGW.cjs +750 -0
- package/dist/index.cjs +12618 -0
- package/dist/index.d.cts +771 -0
- package/dist/index.d.ts +771 -0
- package/dist/index.js +12618 -0
- package/dist/instantdb.cjs +189 -0
- package/dist/instantdb.d.cts +36 -0
- package/dist/instantdb.d.ts +36 -0
- package/dist/instantdb.js +189 -0
- package/dist/metafile-cjs.json +1 -0
- package/dist/metafile-esm.json +1 -0
- package/dist/server.cjs +194 -0
- package/dist/server.d.cts +35 -0
- package/dist/server.d.ts +35 -0
- package/dist/server.js +194 -0
- package/dist/style.css +1 -0
- package/dist/tanstack.cjs +153 -0
- package/dist/tanstack.d.cts +18 -0
- package/dist/tanstack.d.ts +18 -0
- package/dist/tanstack.js +153 -0
- package/dist/triplit.cjs +201 -0
- package/dist/triplit.d.cts +31 -0
- package/dist/triplit.d.ts +31 -0
- package/dist/triplit.js +201 -0
- package/dist/utils-C5R37WDe.d.cts +3 -0
- package/dist/utils-C5R37WDe.d.ts +3 -0
- package/dist/view-paths-CHSJf5dv.d.cts +645 -0
- package/dist/view-paths-CHSJf5dv.d.ts +645 -0
- package/package.json +156 -0
- package/src/components/account/account-view.tsx +220 -0
- package/src/components/auth/auth-callback.tsx +36 -0
- package/src/components/auth/auth-form.tsx +277 -0
- package/src/components/auth/auth-view.tsx +389 -0
- package/src/components/auth/email-otp-button.tsx +53 -0
- package/src/components/auth/forms/email-otp-form.tsx +288 -0
- package/src/components/auth/forms/forgot-password-form.tsx +168 -0
- package/src/components/auth/forms/magic-link-form.tsx +191 -0
- package/src/components/auth/forms/recover-account-form.tsx +138 -0
- package/src/components/auth/forms/reset-password-form.tsx +215 -0
- package/src/components/auth/forms/sign-in-form.tsx +289 -0
- package/src/components/auth/forms/sign-up-form.tsx +788 -0
- package/src/components/auth/forms/two-factor-form.tsx +372 -0
- package/src/components/auth/magic-link-button.tsx +54 -0
- package/src/components/auth/one-tap.tsx +48 -0
- package/src/components/auth/otp-input-group.tsx +65 -0
- package/src/components/auth/passkey-button.tsx +85 -0
- package/src/components/auth/provider-button.tsx +141 -0
- package/src/components/auth/sign-out.tsx +25 -0
- package/src/components/auth-loading.tsx +21 -0
- package/src/components/captcha/captcha.tsx +79 -0
- package/src/components/captcha/recaptcha-badge.tsx +61 -0
- package/src/components/captcha/recaptcha-v2.tsx +58 -0
- package/src/components/captcha/recaptcha-v3.tsx +73 -0
- package/src/components/email/email-template.tsx +216 -0
- package/src/components/form-error.tsx +27 -0
- package/src/components/organization/accept-invitation-card.tsx +362 -0
- package/src/components/organization/create-organization-dialog.tsx +395 -0
- package/src/components/organization/delete-organization-card.tsx +101 -0
- package/src/components/organization/delete-organization-dialog.tsx +209 -0
- package/src/components/organization/invitation-cell.tsx +156 -0
- package/src/components/organization/invite-member-dialog.tsx +258 -0
- package/src/components/organization/leave-organization-dialog.tsx +150 -0
- package/src/components/organization/member-cell.tsx +187 -0
- package/src/components/organization/organization-cell-view.tsx +122 -0
- package/src/components/organization/organization-cell.tsx +154 -0
- package/src/components/organization/organization-invitations-card.tsx +94 -0
- package/src/components/organization/organization-logo-card.tsx +308 -0
- package/src/components/organization/organization-logo.tsx +120 -0
- package/src/components/organization/organization-members-card.tsx +155 -0
- package/src/components/organization/organization-name-card.tsx +204 -0
- package/src/components/organization/organization-settings-cards.tsx +67 -0
- package/src/components/organization/organization-slug-card.tsx +223 -0
- package/src/components/organization/organization-switcher.tsx +512 -0
- package/src/components/organization/organization-view.tsx +228 -0
- package/src/components/organization/organizations-card.tsx +72 -0
- package/src/components/organization/personal-account-view.tsx +115 -0
- package/src/components/organization/remove-member-dialog.tsx +144 -0
- package/src/components/organization/update-member-role-dialog.tsx +213 -0
- package/src/components/organization/user-invitations-card.tsx +238 -0
- package/src/components/password-input.tsx +56 -0
- package/src/components/provider-icons.tsx +385 -0
- package/src/components/redirect-to-sign-in.tsx +16 -0
- package/src/components/redirect-to-sign-up.tsx +16 -0
- package/src/components/settings/account/account-cell.tsx +158 -0
- package/src/components/settings/account/accounts-card.tsx +75 -0
- package/src/components/settings/account/delete-account-card.tsx +65 -0
- package/src/components/settings/account/delete-account-dialog.tsx +231 -0
- package/src/components/settings/account/update-avatar-card.tsx +198 -0
- package/src/components/settings/account/update-field-card.tsx +282 -0
- package/src/components/settings/account/update-name-card.tsx +39 -0
- package/src/components/settings/account/update-username-card.tsx +42 -0
- package/src/components/settings/account-settings-cards.tsx +123 -0
- package/src/components/settings/api-key/api-key-cell.tsx +108 -0
- package/src/components/settings/api-key/api-key-delete-dialog.tsx +162 -0
- package/src/components/settings/api-key/api-key-display-dialog.tsx +110 -0
- package/src/components/settings/api-key/api-keys-card.tsx +98 -0
- package/src/components/settings/api-key/create-api-key-dialog.tsx +376 -0
- package/src/components/settings/passkey/passkey-cell.tsx +113 -0
- package/src/components/settings/passkey/passkeys-card.tsx +111 -0
- package/src/components/settings/providers/provider-cell.tsx +152 -0
- package/src/components/settings/providers/providers-card.tsx +108 -0
- package/src/components/settings/security/change-email-card.tsx +200 -0
- package/src/components/settings/security/change-password-card.tsx +326 -0
- package/src/components/settings/security/session-cell.tsx +120 -0
- package/src/components/settings/security/sessions-card.tsx +58 -0
- package/src/components/settings/security-settings-cards.tsx +111 -0
- package/src/components/settings/shared/session-freshness-dialog.tsx +97 -0
- package/src/components/settings/shared/settings-action-button.tsx +51 -0
- package/src/components/settings/shared/settings-card-footer.tsx +94 -0
- package/src/components/settings/shared/settings-card-header.tsx +67 -0
- package/src/components/settings/shared/settings-card.tsx +106 -0
- package/src/components/settings/skeletons/input-field-skeleton.tsx +18 -0
- package/src/components/settings/skeletons/settings-cell-skeleton.tsx +37 -0
- package/src/components/settings/two-factor/backup-codes-dialog.tsx +113 -0
- package/src/components/settings/two-factor/two-factor-card.tsx +63 -0
- package/src/components/settings/two-factor/two-factor-password-dialog.tsx +226 -0
- package/src/components/signed-in.tsx +20 -0
- package/src/components/signed-out.tsx +20 -0
- package/src/components/ui/alert.tsx +66 -0
- package/src/components/ui/avatar.tsx +53 -0
- package/src/components/ui/button.tsx +59 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/checkbox.tsx +32 -0
- package/src/components/ui/dialog.tsx +143 -0
- package/src/components/ui/drawer.tsx +135 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/form.tsx +167 -0
- package/src/components/ui/input-otp.tsx +77 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/select.tsx +185 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/tabs.tsx +66 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/user-avatar.tsx +147 -0
- package/src/components/user-button.tsx +409 -0
- package/src/components/user-view.tsx +138 -0
- package/src/hooks/use-auth-data.ts +184 -0
- package/src/hooks/use-authenticate.ts +62 -0
- package/src/hooks/use-captcha.tsx +138 -0
- package/src/hooks/use-current-organization.ts +59 -0
- package/src/hooks/use-hydrated.ts +13 -0
- package/src/hooks/use-lang.ts +32 -0
- package/src/hooks/use-success-transition.ts +51 -0
- package/src/hooks/use-theme.ts +39 -0
- package/src/index.ts +65 -0
- package/src/instantdb.ts +1 -0
- package/src/lib/auth-data-cache.ts +90 -0
- package/src/lib/auth-ui-provider.tsx +658 -0
- package/src/lib/gravatar-utils.ts +58 -0
- package/src/lib/image-utils.ts +55 -0
- package/src/lib/instantdb/model-names.ts +24 -0
- package/src/lib/instantdb/use-instant-options.ts +98 -0
- package/src/lib/instantdb/use-list-accounts.ts +38 -0
- package/src/lib/instantdb/use-list-sessions.ts +53 -0
- package/src/lib/instantdb/use-session.ts +55 -0
- package/src/lib/organization-refetcher.tsx +56 -0
- package/src/lib/social-providers.ts +144 -0
- package/src/lib/tanstack/auth-ui-provider-tanstack.tsx +49 -0
- package/src/lib/tanstack/use-tanstack-options.ts +112 -0
- package/src/lib/triplit/model-names.ts +24 -0
- package/src/lib/triplit/use-conditional-query.ts +82 -0
- package/src/lib/triplit/use-list-accounts.ts +31 -0
- package/src/lib/triplit/use-list-sessions.ts +33 -0
- package/src/lib/triplit/use-session.ts +42 -0
- package/src/lib/triplit/use-triplit-hooks.ts +68 -0
- package/src/lib/triplit/use-triplit-token.ts +44 -0
- package/src/lib/utils.ts +105 -0
- package/src/lib/view-paths.ts +55 -0
- package/src/localization/admin-error-codes.ts +20 -0
- package/src/localization/anonymous-error-codes.ts +6 -0
- package/src/localization/api-key-error-codes.ts +32 -0
- package/src/localization/auth-localization.ts +740 -0
- package/src/localization/base-error-codes.ts +27 -0
- package/src/localization/captcha-error-codes.ts +17 -0
- package/src/localization/email-otp-error-codes.ts +7 -0
- package/src/localization/generic-oauth-error-codes.ts +3 -0
- package/src/localization/haveibeenpwned-error-codes.ts +4 -0
- package/src/localization/multi-session-error-codes.ts +3 -0
- package/src/localization/organization-error-codes.ts +57 -0
- package/src/localization/passkey-error-codes.ts +10 -0
- package/src/localization/phone-number-error-codes.ts +10 -0
- package/src/localization/stripe-localization.ts +12 -0
- package/src/localization/two-factor-error-codes.ts +12 -0
- package/src/localization/username-error-codes.ts +9 -0
- package/src/server.ts +4 -0
- package/src/style.css +1 -0
- package/src/tanstack.ts +1 -0
- package/src/triplit.ts +1 -0
- package/src/types/account-options.ts +35 -0
- package/src/types/additional-fields.ts +21 -0
- package/src/types/any-auth-client.ts +6 -0
- package/src/types/api-key.ts +9 -0
- package/src/types/auth-client.ts +37 -0
- package/src/types/auth-hooks.ts +61 -0
- package/src/types/auth-mutators.ts +17 -0
- package/src/types/avatar-options.ts +29 -0
- package/src/types/captcha-options.ts +32 -0
- package/src/types/captcha-provider.ts +6 -0
- package/src/types/credentials-options.ts +32 -0
- package/src/types/delete-user-options.ts +7 -0
- package/src/types/fetch-error.ts +6 -0
- package/src/types/generic-oauth-options.ts +16 -0
- package/src/types/gravatar-options.ts +21 -0
- package/src/types/image.ts +7 -0
- package/src/types/invitation.ts +10 -0
- package/src/types/link.ts +7 -0
- package/src/types/organization-options.ts +106 -0
- package/src/types/password-validation.ts +16 -0
- package/src/types/profile.ts +15 -0
- package/src/types/refetch.ts +1 -0
- package/src/types/render-toast.ts +9 -0
- package/src/types/sign-up-options.ts +7 -0
- 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
|
+
}
|
package/src/lib/utils.ts
ADDED
|
@@ -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,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
|
+
}
|