@voyantjs/auth-react 0.35.0 → 0.37.1

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 (56) hide show
  1. package/README.md +157 -1
  2. package/dist/hooks/index.d.ts +6 -0
  3. package/dist/hooks/index.d.ts.map +1 -1
  4. package/dist/hooks/index.js +6 -0
  5. package/dist/hooks/use-accept-invitation.d.ts +11 -0
  6. package/dist/hooks/use-accept-invitation.d.ts.map +1 -0
  7. package/dist/hooks/use-accept-invitation.js +35 -0
  8. package/dist/hooks/use-accept-invitation.test.d.ts +2 -0
  9. package/dist/hooks/use-accept-invitation.test.d.ts.map +1 -0
  10. package/dist/hooks/use-accept-invitation.test.js +36 -0
  11. package/dist/hooks/use-account-mutation.d.ts +41 -0
  12. package/dist/hooks/use-account-mutation.d.ts.map +1 -0
  13. package/dist/hooks/use-account-mutation.js +68 -0
  14. package/dist/hooks/use-account-mutation.test.d.ts +2 -0
  15. package/dist/hooks/use-account-mutation.test.d.ts.map +1 -0
  16. package/dist/hooks/use-account-mutation.test.js +67 -0
  17. package/dist/hooks/use-current-user.d.ts +2 -0
  18. package/dist/hooks/use-current-user.d.ts.map +1 -1
  19. package/dist/hooks/use-password-reset.d.ts +20 -0
  20. package/dist/hooks/use-password-reset.d.ts.map +1 -0
  21. package/dist/hooks/use-password-reset.js +83 -0
  22. package/dist/hooks/use-password-reset.test.d.ts +2 -0
  23. package/dist/hooks/use-password-reset.test.d.ts.map +1 -0
  24. package/dist/hooks/use-password-reset.test.js +55 -0
  25. package/dist/hooks/use-sign-in.d.ts +15 -0
  26. package/dist/hooks/use-sign-in.d.ts.map +1 -0
  27. package/dist/hooks/use-sign-in.js +80 -0
  28. package/dist/hooks/use-sign-in.test.d.ts +2 -0
  29. package/dist/hooks/use-sign-in.test.d.ts.map +1 -0
  30. package/dist/hooks/use-sign-in.test.js +49 -0
  31. package/dist/hooks/use-sign-up.d.ts +15 -0
  32. package/dist/hooks/use-sign-up.d.ts.map +1 -0
  33. package/dist/hooks/use-sign-up.js +80 -0
  34. package/dist/hooks/use-sign-up.test.d.ts +2 -0
  35. package/dist/hooks/use-sign-up.test.d.ts.map +1 -0
  36. package/dist/hooks/use-sign-up.test.js +49 -0
  37. package/dist/hooks/use-update-account-profile.d.ts +25 -0
  38. package/dist/hooks/use-update-account-profile.d.ts.map +1 -0
  39. package/dist/hooks/use-update-account-profile.js +37 -0
  40. package/dist/hooks/use-update-account-profile.test.d.ts +2 -0
  41. package/dist/hooks/use-update-account-profile.test.d.ts.map +1 -0
  42. package/dist/hooks/use-update-account-profile.test.js +62 -0
  43. package/dist/hooks/use-verify-email.d.ts +18 -0
  44. package/dist/hooks/use-verify-email.d.ts.map +1 -0
  45. package/dist/hooks/use-verify-email.js +86 -0
  46. package/dist/hooks/use-verify-email.test.d.ts +2 -0
  47. package/dist/hooks/use-verify-email.test.d.ts.map +1 -0
  48. package/dist/hooks/use-verify-email.test.js +50 -0
  49. package/dist/hooks/use-workspace-mutation.d.ts +2 -0
  50. package/dist/hooks/use-workspace-mutation.d.ts.map +1 -1
  51. package/dist/query-options.d.ts +8 -0
  52. package/dist/query-options.d.ts.map +1 -1
  53. package/dist/schemas.d.ts +2 -0
  54. package/dist/schemas.d.ts.map +1 -1
  55. package/dist/schemas.js +2 -0
  56. package/package.json +7 -7
package/README.md CHANGED
@@ -5,12 +5,23 @@ React runtime package for Voyant authentication and optional workspace state.
5
5
  This package wraps the shared Voyant auth HTTP contract:
6
6
 
7
7
  - `/auth/me`
8
+ - `PATCH /auth/me`
8
9
  - `/auth/status`
10
+ - `/auth/request-password-reset`
11
+ - `/auth/reset-password`
12
+ - `/auth/sign-in/email`
13
+ - `/auth/change-password`
14
+ - `/auth/email-otp/request-email-change`
15
+ - `/auth/email-otp/change-email`
16
+ - `/auth/sign-up/email`
17
+ - `/auth/verify-email`
18
+ - `/auth/email-otp/verify-email`
9
19
  - `/auth/workspace/current`
10
20
  - `/auth/workspace/active-organization`
11
21
  - `/auth/organization/list-members`
12
22
  - `/auth/organization/list-invitations`
13
23
  - `/auth/organization/invite-member`
24
+ - `/auth/organization/accept-invitation`
14
25
  - `/auth/organization/update-member-role`
15
26
  - `/auth/organization/remove-member`
16
27
  - `/auth/organization/cancel-invitation`
@@ -20,12 +31,157 @@ This package wraps the shared Voyant auth HTTP contract:
20
31
  It provides reusable React surfaces for:
21
32
 
22
33
  - current user state
34
+ - account profile, password, and email-change mutations
23
35
  - optional workspace and organization state
24
36
  - organization member listing
25
37
  - organization invitation listing
26
- - invite, cancel, remove, and role update mutations
38
+ - email/password sign-in
39
+ - email/password sign-up
40
+ - email verification by Better Auth token or email OTP
41
+ - password reset request and confirmation
42
+ - invite, accept, cancel, remove, and role update mutations
27
43
  - API token listing, creation, update, and deletion
28
44
 
45
+ ## Sign-In
46
+
47
+ `useSignIn()` exposes the shared email/password Better Auth flow:
48
+
49
+ ```tsx
50
+ const signIn = useSignIn()
51
+
52
+ await signIn.email.mutateAsync({
53
+ email,
54
+ password,
55
+ callbackURL: "/",
56
+ })
57
+ ```
58
+
59
+ After Better Auth accepts the credentials, the hook calls `/auth/status` to
60
+ provision the Voyant user profile if needed and invalidates the current auth
61
+ queries.
62
+
63
+ ## Account self-service
64
+
65
+ `useUpdateAccountProfile()` updates Voyant profile fields through
66
+ `PATCH /auth/me` and refreshes the current-user query:
67
+
68
+ ```tsx
69
+ const updateProfile = useUpdateAccountProfile()
70
+
71
+ await updateProfile.mutateAsync({
72
+ firstName: "Ana",
73
+ lastName: "Pop",
74
+ locale: "ro",
75
+ timezone: "Europe/Bucharest",
76
+ profilePictureUrl: null,
77
+ })
78
+ ```
79
+
80
+ Apps can mount `handleAccountProfileRequest(...)` from `@voyantjs/auth/server`
81
+ to provide this route without depending on a specific template. The mounted
82
+ route validates the session, calls `updateCurrentUserProfile(...)` from
83
+ `@voyantjs/auth/workspace`, and returns the updated current-user shape.
84
+
85
+ Password and email changes call the mounted Better Auth API:
86
+
87
+ ```tsx
88
+ const changePassword = useChangeAccountPassword()
89
+ await changePassword.mutateAsync({
90
+ currentPassword,
91
+ newPassword,
92
+ revokeOtherSessions: true,
93
+ })
94
+
95
+ const requestEmailChange = useRequestAccountEmailChange()
96
+ await requestEmailChange.mutateAsync({ newEmail })
97
+
98
+ const confirmEmailChange = useConfirmAccountEmailChange()
99
+ await confirmEmailChange.mutateAsync({ newEmail, otp })
100
+ ```
101
+
102
+ ## Sign-Up
103
+
104
+ `useSignUp()` exposes the shared email/password Better Auth registration flow:
105
+
106
+ ```tsx
107
+ const signUp = useSignUp()
108
+
109
+ await signUp.email.mutateAsync({
110
+ name,
111
+ email,
112
+ password,
113
+ callbackURL: "/",
114
+ })
115
+ ```
116
+
117
+ The hook posts to the mounted Better Auth `/auth/sign-up/email` endpoint, calls
118
+ `/auth/status` after success for profile provisioning fallback, and invalidates
119
+ the current auth queries. Invitation-backed registration should use the app's
120
+ invitation redemption endpoint, because Better Auth email sign-up cannot redeem
121
+ Voyant admin-issued invite tokens.
122
+
123
+ ## Invitation Acceptance
124
+
125
+ `useAcceptInvitation()` posts a Better Auth organization invitation token to the
126
+ mounted `/auth/organization/accept-invitation` endpoint:
127
+
128
+ ```tsx
129
+ const acceptInvitation = useAcceptInvitation()
130
+
131
+ await acceptInvitation.mutateAsync({ token: invitationId })
132
+ ```
133
+
134
+ The helper also accepts Better Auth's native field name:
135
+
136
+ ```tsx
137
+ await acceptInvitation.mutateAsync({ invitationId })
138
+ ```
139
+
140
+ On success, current-user, current-workspace, organization-member, and
141
+ organization-invitation queries are invalidated so app shells can refresh their
142
+ membership state.
143
+
144
+ ## Password Reset
145
+
146
+ `useRequestPasswordReset()` and `useConfirmPasswordReset()` expose the mounted
147
+ Better Auth reset-password endpoints:
148
+
149
+ ```tsx
150
+ const requestReset = useRequestPasswordReset()
151
+
152
+ await requestReset.mutateAsync({
153
+ email,
154
+ redirectTo: "https://operator.example/reset-password",
155
+ })
156
+
157
+ const confirmReset = useConfirmPasswordReset()
158
+
159
+ await confirmReset.mutateAsync({
160
+ token,
161
+ newPassword,
162
+ })
163
+ ```
164
+
165
+ The request hook posts to `/auth/request-password-reset` with `email` and
166
+ `redirectTo`. The confirm hook posts to `/auth/reset-password` with `token` and
167
+ `newPassword`, then invalidates the current auth queries.
168
+
169
+ ## Email Verification
170
+
171
+ `useVerifyEmail()` exposes the shared Better Auth verification flow. Token links
172
+ call the mounted Better Auth `/auth/verify-email` endpoint; OTP verification
173
+ uses the email OTP plugin route used by the templates.
174
+
175
+ ```tsx
176
+ const verifyEmail = useVerifyEmail()
177
+
178
+ await verifyEmail.mutateAsync({ token })
179
+ await verifyEmail.mutateAsync({ email, otp })
180
+ ```
181
+
182
+ After verification succeeds, the hook calls `/auth/status` to provision the
183
+ Voyant user profile if needed and invalidates the current auth queries.
184
+
29
185
  ## Single-Tenant Apps
30
186
 
31
187
  Single-tenant operator apps should bootstrap their shell from `useCurrentUser()`
@@ -1,3 +1,5 @@
1
+ export { type AcceptInvitationInput, type AcceptInvitationResult, acceptInvitation, useAcceptInvitation, } from "./use-accept-invitation.js";
2
+ export { type ChangeAccountPasswordInput, type ConfirmAccountEmailChangeInput, changeAccountPassword, confirmAccountEmailChange, type RequestAccountEmailChangeInput, requestAccountEmailChange, type UpdateAccountProfileInput, updateAccountProfile, useAccountMutation, useChangeAccountPassword, useConfirmAccountEmailChange, useRequestAccountEmailChange, useUpdateAccountProfile, } from "./use-account-mutation.js";
1
3
  export { type UseAuthStatusOptions, useAuthStatus } from "./use-auth-status.js";
2
4
  export { type UseCurrentUserOptions, useCurrentUser } from "./use-current-user.js";
3
5
  export { type UseCurrentWorkspaceOptions, useCurrentWorkspace } from "./use-current-workspace.js";
@@ -5,7 +7,11 @@ export { type CancelOrganizationInvitationInput, type InviteOrganizationMemberIn
5
7
  export { type UseOrganizationInvitationsOptions, useOrganizationInvitations, } from "./use-organization-invitations.js";
6
8
  export { type RemoveOrganizationMemberInput, type UpdateOrganizationMemberRoleInput, useOrganizationMemberMutation, } from "./use-organization-member-mutation.js";
7
9
  export { type UseOrganizationMembersOptions, useOrganizationMembers, } from "./use-organization-members.js";
10
+ export { type ConfirmPasswordResetInput, type ConfirmPasswordResetResult, confirmPasswordReset, type RequestPasswordResetInput, type RequestPasswordResetResult, requestPasswordReset, useConfirmPasswordReset, useRequestPasswordReset, } from "./use-password-reset.js";
8
11
  export { type CreateApiTokenInput, type CreateServiceApiKeyInput, type DeleteApiTokenInput, type DeleteServiceApiKeyInput, type UpdateApiTokenInput, type UpdateServiceApiKeyInput, useApiTokenMutation, useServiceApiKeyMutation, } from "./use-service-api-key-mutation.js";
9
12
  export { type UseApiTokensOptions, type UseServiceApiKeysOptions, useApiTokens, useServiceApiKeys, } from "./use-service-api-keys.js";
13
+ export { type SignInEmailInput, type SignInEmailResult, signInWithEmail, useSignIn, } from "./use-sign-in.js";
14
+ export { type SignUpEmailInput, type SignUpEmailResult, signUpWithEmail, useSignUp, } from "./use-sign-up.js";
15
+ export { useVerifyEmail, type VerifyEmailInput, type VerifyEmailOtpInput, type VerifyEmailResult, type VerifyEmailTokenInput, verifyEmail, } from "./use-verify-email.js";
10
16
  export { useWorkspaceMutation } from "./use-workspace-mutation.js";
11
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,KAAK,qBAAqB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAClF,OAAO,EAAE,KAAK,0BAA0B,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AACjG,OAAO,EACL,KAAK,iCAAiC,EACtC,KAAK,6BAA6B,EAClC,iCAAiC,GAClC,MAAM,2CAA2C,CAAA;AAClD,OAAO,EACL,KAAK,iCAAiC,EACtC,0BAA0B,GAC3B,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACL,KAAK,6BAA6B,EAClC,KAAK,iCAAiC,EACtC,6BAA6B,GAC9B,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EACL,KAAK,6BAA6B,EAClC,sBAAsB,GACvB,MAAM,+BAA+B,CAAA;AACtC,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,YAAY,EACZ,iBAAiB,GAClB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EACL,KAAK,0BAA0B,EAC/B,KAAK,8BAA8B,EACnC,qBAAqB,EACrB,yBAAyB,EACzB,KAAK,8BAA8B,EACnC,yBAAyB,EACzB,KAAK,yBAAyB,EAC9B,oBAAoB,EACpB,kBAAkB,EAClB,wBAAwB,EACxB,4BAA4B,EAC5B,4BAA4B,EAC5B,uBAAuB,GACxB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,KAAK,qBAAqB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAClF,OAAO,EAAE,KAAK,0BAA0B,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AACjG,OAAO,EACL,KAAK,iCAAiC,EACtC,KAAK,6BAA6B,EAClC,iCAAiC,GAClC,MAAM,2CAA2C,CAAA;AAClD,OAAO,EACL,KAAK,iCAAiC,EACtC,0BAA0B,GAC3B,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACL,KAAK,6BAA6B,EAClC,KAAK,iCAAiC,EACtC,6BAA6B,GAC9B,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EACL,KAAK,6BAA6B,EAClC,sBAAsB,GACvB,MAAM,+BAA+B,CAAA;AACtC,OAAO,EACL,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,oBAAoB,EACpB,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,oBAAoB,EACpB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,YAAY,EACZ,iBAAiB,GAClB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,eAAe,EACf,SAAS,GACV,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,eAAe,EACf,SAAS,GACV,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,cAAc,EACd,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,WAAW,GACZ,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA"}
@@ -1,3 +1,5 @@
1
+ export { acceptInvitation, useAcceptInvitation, } from "./use-accept-invitation.js";
2
+ export { changeAccountPassword, confirmAccountEmailChange, requestAccountEmailChange, updateAccountProfile, useAccountMutation, useChangeAccountPassword, useConfirmAccountEmailChange, useRequestAccountEmailChange, useUpdateAccountProfile, } from "./use-account-mutation.js";
1
3
  export { useAuthStatus } from "./use-auth-status.js";
2
4
  export { useCurrentUser } from "./use-current-user.js";
3
5
  export { useCurrentWorkspace } from "./use-current-workspace.js";
@@ -5,6 +7,10 @@ export { useOrganizationInvitationMutation, } from "./use-organization-invitatio
5
7
  export { useOrganizationInvitations, } from "./use-organization-invitations.js";
6
8
  export { useOrganizationMemberMutation, } from "./use-organization-member-mutation.js";
7
9
  export { useOrganizationMembers, } from "./use-organization-members.js";
10
+ export { confirmPasswordReset, requestPasswordReset, useConfirmPasswordReset, useRequestPasswordReset, } from "./use-password-reset.js";
8
11
  export { useApiTokenMutation, useServiceApiKeyMutation, } from "./use-service-api-key-mutation.js";
9
12
  export { useApiTokens, useServiceApiKeys, } from "./use-service-api-keys.js";
13
+ export { signInWithEmail, useSignIn, } from "./use-sign-in.js";
14
+ export { signUpWithEmail, useSignUp, } from "./use-sign-up.js";
15
+ export { useVerifyEmail, verifyEmail, } from "./use-verify-email.js";
10
16
  export { useWorkspaceMutation } from "./use-workspace-mutation.js";
@@ -0,0 +1,11 @@
1
+ import { type FetchWithValidationOptions } from "../client.js";
2
+ export interface AcceptInvitationInput {
3
+ invitationId?: string | undefined;
4
+ token?: string | undefined;
5
+ }
6
+ export interface AcceptInvitationResult {
7
+ data: unknown;
8
+ }
9
+ export declare function acceptInvitation(input: AcceptInvitationInput, client: FetchWithValidationOptions): Promise<AcceptInvitationResult>;
10
+ export declare function useAcceptInvitation(): import("@tanstack/react-query").UseMutationResult<AcceptInvitationResult, Error, AcceptInvitationInput, unknown>;
11
+ //# sourceMappingURL=use-accept-invitation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-accept-invitation.d.ts","sourceRoot":"","sources":["../../src/hooks/use-accept-invitation.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,0BAA0B,EAAuB,MAAM,cAAc,CAAA;AAInF,MAAM,WAAW,qBAAqB;IACpC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC3B;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,OAAO,CAAA;CACd;AAaD,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,qBAAqB,EAC5B,MAAM,EAAE,0BAA0B,GACjC,OAAO,CAAC,sBAAsB,CAAC,CAajC;AAED,wBAAgB,mBAAmB,qHAalC"}
@@ -0,0 +1,35 @@
1
+ "use client";
2
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
3
+ import { z } from "zod";
4
+ import { fetchWithValidation } from "../client.js";
5
+ import { useVoyantAuthContext } from "../provider.js";
6
+ import { authQueryKeys } from "../query-keys.js";
7
+ const acceptInvitationResponseSchema = z.unknown();
8
+ function invitationIdFromInput(input) {
9
+ const invitationId = input.invitationId ?? input.token;
10
+ if (!invitationId?.trim()) {
11
+ throw new Error("Invitation token is required.");
12
+ }
13
+ return invitationId.trim();
14
+ }
15
+ export async function acceptInvitation(input, client) {
16
+ const invitationId = invitationIdFromInput(input);
17
+ const data = await fetchWithValidation("/auth/organization/accept-invitation", acceptInvitationResponseSchema, client, {
18
+ method: "POST",
19
+ body: JSON.stringify({ invitationId }),
20
+ });
21
+ return { data };
22
+ }
23
+ export function useAcceptInvitation() {
24
+ const { baseUrl, fetcher } = useVoyantAuthContext();
25
+ const queryClient = useQueryClient();
26
+ return useMutation({
27
+ mutationFn: (input) => acceptInvitation(input, { baseUrl, fetcher }),
28
+ onSuccess: () => {
29
+ void queryClient.invalidateQueries({ queryKey: authQueryKeys.currentUser() });
30
+ void queryClient.invalidateQueries({ queryKey: authQueryKeys.currentWorkspace() });
31
+ void queryClient.invalidateQueries({ queryKey: authQueryKeys.organizationMembers() });
32
+ void queryClient.invalidateQueries({ queryKey: authQueryKeys.organizationInvitations() });
33
+ },
34
+ });
35
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=use-accept-invitation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-accept-invitation.test.d.ts","sourceRoot":"","sources":["../../src/hooks/use-accept-invitation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { acceptInvitation } from "./use-accept-invitation.js";
3
+ function jsonResponse(body, init) {
4
+ return new Response(JSON.stringify(body), {
5
+ headers: { "Content-Type": "application/json" },
6
+ ...init,
7
+ });
8
+ }
9
+ describe("acceptInvitation", () => {
10
+ it("posts the invitation token to the mounted Better Auth organization endpoint", async () => {
11
+ const invitationToken = ["invitation", "123"].join("_");
12
+ const fetcher = vi.fn().mockResolvedValueOnce(jsonResponse({ success: true }));
13
+ const result = await acceptInvitation({ token: invitationToken }, { baseUrl: "https://operator.example/api", fetcher });
14
+ expect(result).toEqual({ data: { success: true } });
15
+ expect(fetcher).toHaveBeenCalledWith("https://operator.example/api/auth/organization/accept-invitation", {
16
+ method: "POST",
17
+ body: JSON.stringify({ invitationId: invitationToken }),
18
+ headers: expect.any(Headers),
19
+ });
20
+ });
21
+ it("also accepts Better Auth's invitationId field name", async () => {
22
+ const invitationToken = ["invitation", "456"].join("_");
23
+ const fetcher = vi
24
+ .fn()
25
+ .mockResolvedValueOnce(jsonResponse({ id: ["member", "123"].join("_") }));
26
+ await acceptInvitation({ invitationId: invitationToken }, { baseUrl: "https://operator.example/api/", fetcher });
27
+ expect(fetcher).toHaveBeenCalledWith("https://operator.example/api/auth/organization/accept-invitation", expect.objectContaining({
28
+ body: JSON.stringify({ invitationId: invitationToken }),
29
+ }));
30
+ });
31
+ it("requires a token or invitationId", async () => {
32
+ const fetcher = vi.fn();
33
+ await expect(acceptInvitation({ token: " " }, { baseUrl: "https://operator.example/api", fetcher })).rejects.toThrow("Invitation token is required.");
34
+ expect(fetcher).not.toHaveBeenCalled();
35
+ });
36
+ });
@@ -0,0 +1,41 @@
1
+ import { type FetchWithValidationOptions } from "../client.js";
2
+ export type { UpdateAccountProfileInput, UpdateAccountProfileResult, } from "./use-update-account-profile.js";
3
+ export { updateAccountProfile, useUpdateAccountProfile, } from "./use-update-account-profile.js";
4
+ export interface ChangeAccountPasswordInput {
5
+ currentPassword: string;
6
+ newPassword: string;
7
+ revokeOtherSessions?: boolean;
8
+ }
9
+ export interface RequestAccountEmailChangeInput {
10
+ newEmail: string;
11
+ otp?: string;
12
+ }
13
+ export interface ConfirmAccountEmailChangeInput {
14
+ newEmail: string;
15
+ otp: string;
16
+ }
17
+ export declare function changeAccountPassword(input: ChangeAccountPasswordInput, client: FetchWithValidationOptions): Promise<unknown>;
18
+ export declare function requestAccountEmailChange(input: RequestAccountEmailChangeInput, client: FetchWithValidationOptions): Promise<unknown>;
19
+ export declare function confirmAccountEmailChange(input: ConfirmAccountEmailChangeInput, client: FetchWithValidationOptions): Promise<unknown>;
20
+ export declare function useChangeAccountPassword(): import("@tanstack/react-query").UseMutationResult<unknown, Error, ChangeAccountPasswordInput, unknown>;
21
+ export declare function useRequestAccountEmailChange(): import("@tanstack/react-query").UseMutationResult<unknown, Error, RequestAccountEmailChangeInput, unknown>;
22
+ export declare function useConfirmAccountEmailChange(): import("@tanstack/react-query").UseMutationResult<unknown, Error, ConfirmAccountEmailChangeInput, unknown>;
23
+ export declare function useAccountMutation(): {
24
+ updateProfile: import("@tanstack/react-query").UseMutationResult<{
25
+ id: string;
26
+ email: string | null;
27
+ firstName: string | null;
28
+ lastName: string | null;
29
+ locale: string;
30
+ timezone: string | null;
31
+ isSuperAdmin: boolean;
32
+ isSupportUser: boolean;
33
+ createdAt: string;
34
+ phoneNumber?: string | null | undefined;
35
+ profilePictureUrl?: string | null | undefined;
36
+ }, Error, import("./use-update-account-profile.js").UpdateAccountProfileInput, unknown>;
37
+ changePassword: import("@tanstack/react-query").UseMutationResult<unknown, Error, ChangeAccountPasswordInput, unknown>;
38
+ requestEmailChange: import("@tanstack/react-query").UseMutationResult<unknown, Error, RequestAccountEmailChangeInput, unknown>;
39
+ confirmEmailChange: import("@tanstack/react-query").UseMutationResult<unknown, Error, ConfirmAccountEmailChangeInput, unknown>;
40
+ };
41
+ //# sourceMappingURL=use-account-mutation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-account-mutation.d.ts","sourceRoot":"","sources":["../../src/hooks/use-account-mutation.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,0BAA0B,EAAuB,MAAM,cAAc,CAAA;AAKnF,YAAY,EACV,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,iCAAiC,CAAA;AACxC,OAAO,EACL,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,iCAAiC,CAAA;AAIxC,MAAM,WAAW,0BAA0B;IACzC,eAAe,EAAE,MAAM,CAAA;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAC9B;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,0BAA0B,EACjC,MAAM,EAAE,0BAA0B,oBAUnC;AAED,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,8BAA8B,EACrC,MAAM,EAAE,0BAA0B,oBAcnC;AAED,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,8BAA8B,EACrC,MAAM,EAAE,0BAA0B,oBASnC;AAED,wBAAgB,wBAAwB,2GAOvC;AAED,wBAAgB,4BAA4B,+GAO3C;AAED,wBAAgB,4BAA4B,+GAY3C;AAED,wBAAgB,kBAAkB;;;;;;;;;;;;;;;;;EAOjC"}
@@ -0,0 +1,68 @@
1
+ "use client";
2
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
3
+ import { z } from "zod";
4
+ import { fetchWithValidation } from "../client.js";
5
+ import { useVoyantAuthContext } from "../provider.js";
6
+ import { authQueryKeys } from "../query-keys.js";
7
+ import { useUpdateAccountProfile } from "./use-update-account-profile.js";
8
+ export { updateAccountProfile, useUpdateAccountProfile, } from "./use-update-account-profile.js";
9
+ const accountMutationResultSchema = z.unknown();
10
+ export async function changeAccountPassword(input, client) {
11
+ return fetchWithValidation("/auth/change-password", accountMutationResultSchema, client, {
12
+ method: "POST",
13
+ body: JSON.stringify({
14
+ currentPassword: input.currentPassword,
15
+ newPassword: input.newPassword,
16
+ revokeOtherSessions: input.revokeOtherSessions,
17
+ }),
18
+ });
19
+ }
20
+ export async function requestAccountEmailChange(input, client) {
21
+ return fetchWithValidation("/auth/email-otp/request-email-change", accountMutationResultSchema, client, {
22
+ method: "POST",
23
+ body: JSON.stringify({
24
+ newEmail: input.newEmail,
25
+ otp: input.otp,
26
+ }),
27
+ });
28
+ }
29
+ export async function confirmAccountEmailChange(input, client) {
30
+ return fetchWithValidation("/auth/email-otp/change-email", accountMutationResultSchema, client, {
31
+ method: "POST",
32
+ body: JSON.stringify({
33
+ newEmail: input.newEmail,
34
+ otp: input.otp,
35
+ }),
36
+ });
37
+ }
38
+ export function useChangeAccountPassword() {
39
+ const { baseUrl, fetcher } = useVoyantAuthContext();
40
+ return useMutation({
41
+ mutationFn: (input) => changeAccountPassword(input, { baseUrl, fetcher }),
42
+ });
43
+ }
44
+ export function useRequestAccountEmailChange() {
45
+ const { baseUrl, fetcher } = useVoyantAuthContext();
46
+ return useMutation({
47
+ mutationFn: (input) => requestAccountEmailChange(input, { baseUrl, fetcher }),
48
+ });
49
+ }
50
+ export function useConfirmAccountEmailChange() {
51
+ const { baseUrl, fetcher } = useVoyantAuthContext();
52
+ const queryClient = useQueryClient();
53
+ return useMutation({
54
+ mutationFn: (input) => confirmAccountEmailChange(input, { baseUrl, fetcher }),
55
+ onSuccess: () => {
56
+ void queryClient.invalidateQueries({ queryKey: authQueryKeys.currentUser() });
57
+ void queryClient.invalidateQueries({ queryKey: authQueryKeys.authStatus() });
58
+ },
59
+ });
60
+ }
61
+ export function useAccountMutation() {
62
+ return {
63
+ updateProfile: useUpdateAccountProfile(),
64
+ changePassword: useChangeAccountPassword(),
65
+ requestEmailChange: useRequestAccountEmailChange(),
66
+ confirmEmailChange: useConfirmAccountEmailChange(),
67
+ };
68
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=use-account-mutation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-account-mutation.test.d.ts","sourceRoot":"","sources":["../../src/hooks/use-account-mutation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,67 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { changeAccountPassword, confirmAccountEmailChange, requestAccountEmailChange, updateAccountProfile, } from "./use-account-mutation.js";
3
+ function jsonResponse(body, init) {
4
+ return new Response(JSON.stringify(body), {
5
+ headers: { "Content-Type": "application/json" },
6
+ ...init,
7
+ });
8
+ }
9
+ describe("account mutation request helpers", () => {
10
+ it("patches the Voyant profile endpoint", async () => {
11
+ const fetcher = vi.fn().mockResolvedValueOnce(jsonResponse({
12
+ id: "user_1",
13
+ email: "ana@example.com",
14
+ firstName: "Ana",
15
+ lastName: "Pop",
16
+ isSuperAdmin: false,
17
+ isSupportUser: false,
18
+ createdAt: "2026-05-12T00:00:00.000Z",
19
+ profilePictureUrl: null,
20
+ }));
21
+ await updateAccountProfile({ firstName: "Ana", lastName: "Pop", profilePictureUrl: null }, { baseUrl: "https://operator.example/api", fetcher });
22
+ expect(fetcher).toHaveBeenCalledWith("https://operator.example/api/auth/me", {
23
+ method: "PATCH",
24
+ headers: expect.any(Headers),
25
+ body: JSON.stringify({
26
+ firstName: "Ana",
27
+ lastName: "Pop",
28
+ profilePictureUrl: null,
29
+ }),
30
+ });
31
+ });
32
+ it("posts password changes to the mounted Better Auth endpoint", async () => {
33
+ const fetcher = vi.fn().mockResolvedValueOnce(jsonResponse({ success: true }));
34
+ await changeAccountPassword({
35
+ currentPassword: "old-password",
36
+ newPassword: "new-password",
37
+ revokeOtherSessions: true,
38
+ }, { baseUrl: "https://operator.example/api/", fetcher });
39
+ expect(fetcher).toHaveBeenCalledWith("https://operator.example/api/auth/change-password", {
40
+ method: "POST",
41
+ headers: expect.any(Headers),
42
+ body: JSON.stringify({
43
+ currentPassword: "old-password",
44
+ newPassword: "new-password",
45
+ revokeOtherSessions: true,
46
+ }),
47
+ });
48
+ });
49
+ it("posts email-change requests to the Email OTP endpoint", async () => {
50
+ const fetcher = vi.fn().mockResolvedValueOnce(jsonResponse({ success: true }));
51
+ await requestAccountEmailChange({ newEmail: "new@example.com" }, { baseUrl: "https://operator.example/api", fetcher });
52
+ expect(fetcher).toHaveBeenCalledWith("https://operator.example/api/auth/email-otp/request-email-change", {
53
+ method: "POST",
54
+ headers: expect.any(Headers),
55
+ body: JSON.stringify({ newEmail: "new@example.com" }),
56
+ });
57
+ });
58
+ it("posts email-change confirmations to the Email OTP endpoint", async () => {
59
+ const fetcher = vi.fn().mockResolvedValueOnce(jsonResponse({ success: true }));
60
+ await confirmAccountEmailChange({ newEmail: "new@example.com", otp: "123456" }, { baseUrl: "https://operator.example/api", fetcher });
61
+ expect(fetcher).toHaveBeenCalledWith("https://operator.example/api/auth/email-otp/change-email", {
62
+ method: "POST",
63
+ headers: expect.any(Headers),
64
+ body: JSON.stringify({ newEmail: "new@example.com", otp: "123456" }),
65
+ });
66
+ });
67
+ });
@@ -6,6 +6,8 @@ export declare function useCurrentUser(options?: UseCurrentUserOptions): import(
6
6
  email: string | null;
7
7
  firstName: string | null;
8
8
  lastName: string | null;
9
+ locale: string;
10
+ timezone: string | null;
9
11
  isSuperAdmin: boolean;
10
12
  isSupportUser: boolean;
11
13
  createdAt: string;
@@ -1 +1 @@
1
- {"version":3,"file":"use-current-user.d.ts","sourceRoot":"","sources":["../../src/hooks/use-current-user.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,wBAAgB,cAAc,CAAC,OAAO,GAAE,qBAA0B;;;;;;;;;;UAQjE"}
1
+ {"version":3,"file":"use-current-user.d.ts","sourceRoot":"","sources":["../../src/hooks/use-current-user.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,wBAAgB,cAAc,CAAC,OAAO,GAAE,qBAA0B;;;;;;;;;;;;UAQjE"}
@@ -0,0 +1,20 @@
1
+ import { type FetchWithValidationOptions } from "../client.js";
2
+ export interface RequestPasswordResetInput {
3
+ email: string;
4
+ redirectTo?: string;
5
+ }
6
+ export interface RequestPasswordResetResult {
7
+ data: unknown;
8
+ }
9
+ export interface ConfirmPasswordResetInput {
10
+ token: string;
11
+ newPassword: string;
12
+ }
13
+ export interface ConfirmPasswordResetResult {
14
+ data: unknown;
15
+ }
16
+ export declare function requestPasswordReset(input: RequestPasswordResetInput, client: FetchWithValidationOptions): Promise<RequestPasswordResetResult>;
17
+ export declare function confirmPasswordReset(input: ConfirmPasswordResetInput, client: FetchWithValidationOptions): Promise<ConfirmPasswordResetResult>;
18
+ export declare function useRequestPasswordReset(): import("@tanstack/react-query").UseMutationResult<RequestPasswordResetResult, Error, RequestPasswordResetInput, unknown>;
19
+ export declare function useConfirmPasswordReset(): import("@tanstack/react-query").UseMutationResult<ConfirmPasswordResetResult, Error, ConfirmPasswordResetInput, unknown>;
20
+ //# sourceMappingURL=use-password-reset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-password-reset.d.ts","sourceRoot":"","sources":["../../src/hooks/use-password-reset.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,0BAA0B,EAAkB,MAAM,cAAc,CAAA;AAI9E,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,OAAO,CAAA;CACd;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,OAAO,CAAA;CACd;AAwFD,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,yBAAyB,EAChC,MAAM,EAAE,0BAA0B,GACjC,OAAO,CAAC,0BAA0B,CAAC,CAMrC;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,yBAAyB,EAChC,MAAM,EAAE,0BAA0B,GACjC,OAAO,CAAC,0BAA0B,CAAC,CAMrC;AAED,wBAAgB,uBAAuB,6HAOtC;AAED,wBAAgB,uBAAuB,6HAatC"}
@@ -0,0 +1,83 @@
1
+ "use client";
2
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
3
+ import { VoyantApiError } from "../client.js";
4
+ import { useVoyantAuthContext } from "../provider.js";
5
+ import { authQueryKeys } from "../query-keys.js";
6
+ function joinUrl(baseUrl, path) {
7
+ const trimmedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
8
+ const trimmedPath = path.startsWith("/") ? path : `/${path}`;
9
+ return `${trimmedBase}${trimmedPath}`;
10
+ }
11
+ function extractBetterAuthErrorMessage(body, fallback) {
12
+ if (typeof body !== "object" || body === null) {
13
+ return fallback;
14
+ }
15
+ const candidate = body;
16
+ if (typeof candidate.error === "string") {
17
+ return candidate.error;
18
+ }
19
+ if (typeof candidate.error === "object" &&
20
+ candidate.error !== null &&
21
+ "message" in candidate.error) {
22
+ return String(candidate.error.message);
23
+ }
24
+ if (candidate.message !== undefined) {
25
+ return String(candidate.message);
26
+ }
27
+ return fallback;
28
+ }
29
+ function hasBetterAuthError(body) {
30
+ return (typeof body === "object" &&
31
+ body !== null &&
32
+ "error" in body &&
33
+ body.error !== undefined &&
34
+ body.error !== null);
35
+ }
36
+ async function readJson(response) {
37
+ const text = await response.text();
38
+ if (!text) {
39
+ return undefined;
40
+ }
41
+ try {
42
+ return JSON.parse(text);
43
+ }
44
+ catch {
45
+ return text;
46
+ }
47
+ }
48
+ async function betterAuthPost(path, input, client) {
49
+ const response = await client.fetcher(joinUrl(client.baseUrl, path), {
50
+ method: "POST",
51
+ headers: { "Content-Type": "application/json" },
52
+ body: JSON.stringify(input),
53
+ });
54
+ const body = await readJson(response);
55
+ if (!response.ok || hasBetterAuthError(body)) {
56
+ throw new VoyantApiError(extractBetterAuthErrorMessage(body, `Voyant API error: ${response.status} ${response.statusText}`), response.status, body);
57
+ }
58
+ return { data: body };
59
+ }
60
+ export function requestPasswordReset(input, client) {
61
+ return betterAuthPost("/auth/request-password-reset", input, client);
62
+ }
63
+ export function confirmPasswordReset(input, client) {
64
+ return betterAuthPost("/auth/reset-password", input, client);
65
+ }
66
+ export function useRequestPasswordReset() {
67
+ const { baseUrl, fetcher } = useVoyantAuthContext();
68
+ return useMutation({
69
+ mutationFn: (input) => requestPasswordReset(input, { baseUrl, fetcher }),
70
+ });
71
+ }
72
+ export function useConfirmPasswordReset() {
73
+ const { baseUrl, fetcher } = useVoyantAuthContext();
74
+ const queryClient = useQueryClient();
75
+ return useMutation({
76
+ mutationFn: (input) => confirmPasswordReset(input, { baseUrl, fetcher }),
77
+ onSuccess: () => {
78
+ void queryClient.invalidateQueries({ queryKey: authQueryKeys.authStatus() });
79
+ void queryClient.invalidateQueries({ queryKey: authQueryKeys.currentUser() });
80
+ void queryClient.invalidateQueries({ queryKey: authQueryKeys.currentWorkspace() });
81
+ },
82
+ });
83
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=use-password-reset.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-password-reset.test.d.ts","sourceRoot":"","sources":["../../src/hooks/use-password-reset.test.ts"],"names":[],"mappings":""}