@dloizides/auth-web 1.2.2 → 1.3.0
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/CHANGELOG.md +32 -0
- package/dist/index.d.mts +97 -1
- package/dist/index.d.ts +97 -1
- package/dist/index.js +189 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +186 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.3.0 (2026-05-31)
|
|
4
|
+
|
|
5
|
+
Additive release for the auth-UX reuse roll-out (feature-reuse-roadmap #1).
|
|
6
|
+
Promotes the "Forgot password?" modal body the product apps each hand-rolled
|
|
7
|
+
into a single shared, **react-query-free** export so katalogos / erevna / kefi
|
|
8
|
+
stop duplicating it. No breaking changes; every 1.2.x export is unchanged.
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`<ForgotPasswordFields>`** — the embedded, themeable "request a reset link"
|
|
13
|
+
body, sized to live inside an app-owned modal shell (no screen/card wrapper,
|
|
14
|
+
no title — the host modal owns those). Adds the two modal affordances a
|
|
15
|
+
screen-shaped form lacks: a `Cancel` button beside submit and a `Close` button
|
|
16
|
+
on the success state, each rendered only when its handler is supplied. Clears
|
|
17
|
+
itself when `visible` flips to `false` so reopening starts fresh.
|
|
18
|
+
- **`useForgotPasswordSubmit`** — the headless, **react-query-free** forgot-
|
|
19
|
+
password logic: a plain `useState` + `client.forgotPassword(...)` promise
|
|
20
|
+
chain that runs on a provider-less login route (where the existing
|
|
21
|
+
`useBffForgotPassword` `useMutation` crashes with "No QueryClient set"). Email
|
|
22
|
+
validation gating via `canSubmit`, anti-enumeration success, `hasNetworkError`,
|
|
23
|
+
and `reset()`. Also exports the `isValidForgotPasswordEmail` helper.
|
|
24
|
+
- **`ForgotPasswordFieldsLabels`** + **`DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS`** —
|
|
25
|
+
the forgot-password label bag plus the modal `cancel` / `close` strings.
|
|
26
|
+
- New `AuthTestIds` entries `forgotPasswordCancelButton` /
|
|
27
|
+
`forgotPasswordCloseButton`.
|
|
28
|
+
|
|
29
|
+
### Notes
|
|
30
|
+
|
|
31
|
+
- The existing screen-shaped `<ForgotPasswordForm>` (react-query-based) is
|
|
32
|
+
unchanged. A future cleanup may re-base it on `useForgotPasswordSubmit` to drop
|
|
33
|
+
its react-query coupling; deferred to keep this release additive.
|
|
34
|
+
|
|
3
35
|
## 1.2.0 (2026-05-22)
|
|
4
36
|
|
|
5
37
|
Additive release for Phase 3d of the unified-auth plan — the event-scoped PIN
|
package/dist/index.d.mts
CHANGED
|
@@ -52,6 +52,17 @@ interface ForgotPasswordFormLabels {
|
|
|
52
52
|
/** Shown when the entered value is not a valid email. */
|
|
53
53
|
invalidEmail: string;
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Strings rendered by `<ForgotPasswordFields>` — the embedded modal body. It is
|
|
57
|
+
* the forgot-password form plus the two modal affordances (`cancel` alongside
|
|
58
|
+
* submit, `close` on the success state) that a screen-shaped form has no need of.
|
|
59
|
+
*/
|
|
60
|
+
interface ForgotPasswordFieldsLabels extends ForgotPasswordFormLabels {
|
|
61
|
+
/** The "cancel" button shown alongside submit. */
|
|
62
|
+
cancel: string;
|
|
63
|
+
/** The "close" button shown on the success state. */
|
|
64
|
+
close: string;
|
|
65
|
+
}
|
|
55
66
|
/** Strings rendered by `<OtpForm>` — the two-step email-OTP login form. */
|
|
56
67
|
interface OtpFormLabels {
|
|
57
68
|
/** Title shown on the request-a-code step. */
|
|
@@ -127,6 +138,7 @@ interface ResetPasswordFormLabels {
|
|
|
127
138
|
}
|
|
128
139
|
declare const DEFAULT_LOGIN_LABELS: LoginFormLabels;
|
|
129
140
|
declare const DEFAULT_FORGOT_PASSWORD_LABELS: ForgotPasswordFormLabels;
|
|
141
|
+
declare const DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS: ForgotPasswordFieldsLabels;
|
|
130
142
|
declare const DEFAULT_OTP_LABELS: OtpFormLabels;
|
|
131
143
|
declare const DEFAULT_PIN_LABELS: PinFormLabels;
|
|
132
144
|
declare const DEFAULT_RESET_PASSWORD_LABELS: ResetPasswordFormLabels;
|
|
@@ -284,6 +296,56 @@ interface ForgotPasswordFormProps {
|
|
|
284
296
|
/** Themeable "request a reset link" form built on `useBffForgotPassword`. */
|
|
285
297
|
declare function ForgotPasswordForm({ client, theme: themeProp, labels: labelsProp, resetUrlTemplate, onSuccess, testIdPrefix, }: Readonly<ForgotPasswordFormProps>): ReactElement;
|
|
286
298
|
|
|
299
|
+
/**
|
|
300
|
+
* `<ForgotPasswordFields>` — the embedded, themeable "request a reset link"
|
|
301
|
+
* body, sized to live INSIDE an app-owned modal shell (not a full screen).
|
|
302
|
+
*
|
|
303
|
+
* The product apps each show "Forgot password?" as a modal launched from their
|
|
304
|
+
* login route, wrapping their own modal chrome (title bar + close affordance)
|
|
305
|
+
* around an identical email field + submit/cancel body. This is that body,
|
|
306
|
+
* promoted into the package so the three apps stop hand-rolling it.
|
|
307
|
+
*
|
|
308
|
+
* Built on the react-query-free `useForgotPasswordSubmit` hook, so it renders +
|
|
309
|
+
* submits on a provider-less login route (where `useMutation` would crash). The
|
|
310
|
+
* backend is anti-enumeration, so a successful request swaps to a generic
|
|
311
|
+
* confirmation that never reveals whether the address is registered.
|
|
312
|
+
*
|
|
313
|
+
* Differs from the screen-shaped `<ForgotPasswordForm>`: no outer screen/card
|
|
314
|
+
* wrapper and no title (the host modal owns those), plus two modal affordances —
|
|
315
|
+
* a `Cancel` button beside submit and a `Close` button on the success state —
|
|
316
|
+
* each rendered only when its handler is supplied.
|
|
317
|
+
*/
|
|
318
|
+
|
|
319
|
+
interface ForgotPasswordFieldsProps {
|
|
320
|
+
/** The same-origin BFF client (build it with `createBffAuthClient`). */
|
|
321
|
+
client: BffAuthClient;
|
|
322
|
+
/** Explicit theme; overrides any `<AuthThemeProvider>`. */
|
|
323
|
+
theme?: AuthTheme;
|
|
324
|
+
/** Localised copy. Partial — unspecified keys fall back to English defaults. */
|
|
325
|
+
labels?: Partial<ForgotPasswordFieldsLabels>;
|
|
326
|
+
/**
|
|
327
|
+
* Full URL with a `{token}` placeholder, forwarded to the backend so it can
|
|
328
|
+
* build the reset-email link without hardcoding a frontend host.
|
|
329
|
+
*/
|
|
330
|
+
resetUrlTemplate?: string;
|
|
331
|
+
/**
|
|
332
|
+
* The host modal's open state. When it flips to `false` the body clears
|
|
333
|
+
* itself, so reopening shows a fresh form regardless of which affordance
|
|
334
|
+
* closed it. Defaults to `true` for non-modal embedding.
|
|
335
|
+
*/
|
|
336
|
+
visible?: boolean;
|
|
337
|
+
/** Called once the request succeeds (the generic-confirmation state). */
|
|
338
|
+
onSuccess?: () => void;
|
|
339
|
+
/** When supplied, renders a Cancel button beside submit. */
|
|
340
|
+
onCancel?: () => void;
|
|
341
|
+
/** When supplied, renders a Close button on the success state. */
|
|
342
|
+
onClose?: () => void;
|
|
343
|
+
/** Prefix applied to every `testID`. */
|
|
344
|
+
testIdPrefix?: string;
|
|
345
|
+
}
|
|
346
|
+
/** Embedded "request a reset link" body for an app-owned modal. */
|
|
347
|
+
declare function ForgotPasswordFields({ client, theme: themeProp, labels: labelsProp, resetUrlTemplate, visible, onSuccess, onCancel, onClose, testIdPrefix, }: Readonly<ForgotPasswordFieldsProps>): ReactElement;
|
|
348
|
+
|
|
287
349
|
/**
|
|
288
350
|
* `<ResetPasswordForm>` — the ready-made, themeable "choose a new password"
|
|
289
351
|
* form.
|
|
@@ -438,6 +500,8 @@ declare const AuthTestIds: {
|
|
|
438
500
|
readonly forgotPasswordForm: "auth-forgot-form";
|
|
439
501
|
readonly forgotPasswordEmailInput: "auth-forgot-email";
|
|
440
502
|
readonly forgotPasswordSubmitButton: "auth-forgot-submit";
|
|
503
|
+
readonly forgotPasswordCancelButton: "auth-forgot-cancel";
|
|
504
|
+
readonly forgotPasswordCloseButton: "auth-forgot-close";
|
|
441
505
|
readonly forgotPasswordError: "auth-forgot-error";
|
|
442
506
|
readonly forgotPasswordSuccess: "auth-forgot-success";
|
|
443
507
|
readonly resetPasswordForm: "auth-reset-form";
|
|
@@ -590,6 +654,38 @@ interface UseResetPasswordFormResult {
|
|
|
590
654
|
*/
|
|
591
655
|
declare function useResetPasswordForm({ client, token, onSuccess, }: UseResetPasswordFormArgs): UseResetPasswordFormResult;
|
|
592
656
|
|
|
657
|
+
/** `true` when `value` (after trimming) looks like an email address. */
|
|
658
|
+
declare function isValidForgotPasswordEmail(value: string): boolean;
|
|
659
|
+
interface UseForgotPasswordSubmitArgs {
|
|
660
|
+
/** The same-origin BFF client (build it with `createBffAuthClient`). */
|
|
661
|
+
client: BffAuthClient;
|
|
662
|
+
/**
|
|
663
|
+
* Full URL with a `{token}` placeholder, forwarded to the backend so it can
|
|
664
|
+
* build the reset-email link without hardcoding a frontend host.
|
|
665
|
+
*/
|
|
666
|
+
resetUrlTemplate?: string;
|
|
667
|
+
/** Invoked once the request succeeds (the generic-confirmation state). */
|
|
668
|
+
onSuccess?: () => void;
|
|
669
|
+
}
|
|
670
|
+
interface UseForgotPasswordSubmitResult {
|
|
671
|
+
email: string;
|
|
672
|
+
setEmail: (value: string) => void;
|
|
673
|
+
/** `true` once the request has succeeded (show the generic confirmation). */
|
|
674
|
+
submitted: boolean;
|
|
675
|
+
/** `true` while the request is in flight. */
|
|
676
|
+
isSubmitting: boolean;
|
|
677
|
+
/** `true` when the last submit hit a network / 5xx failure. */
|
|
678
|
+
hasNetworkError: boolean;
|
|
679
|
+
/** `true` when the email is valid and no request is in flight. */
|
|
680
|
+
canSubmit: boolean;
|
|
681
|
+
/** Validate, then fire the request (no-op when `canSubmit` is false). */
|
|
682
|
+
submit: () => void;
|
|
683
|
+
/** Clear all state — call when the modal closes. */
|
|
684
|
+
reset: () => void;
|
|
685
|
+
}
|
|
686
|
+
/** Headless, react-query-free "request a reset link" logic. */
|
|
687
|
+
declare function useForgotPasswordSubmit({ client, resetUrlTemplate, onSuccess, }: UseForgotPasswordSubmitArgs): UseForgotPasswordSubmitResult;
|
|
688
|
+
|
|
593
689
|
/**
|
|
594
690
|
* The two discrete steps of the email-OTP login flow.
|
|
595
691
|
*
|
|
@@ -814,4 +910,4 @@ declare function validatePasswordPolicy(password: string): PasswordPolicyError[]
|
|
|
814
910
|
/** `true` when the password satisfies every policy rule. */
|
|
815
911
|
declare function isPasswordValid(password: string): boolean;
|
|
816
912
|
|
|
817
|
-
export { AuthTestIds, type AuthTheme, type AuthThemeColors, AuthThemeProvider, type AuthThemeProviderProps, type AuthThemeRadii, type AuthThemeSpacing, type AuthThemeTypography, BffAuthStatus, type CreateBffAuthClientOptions, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_LOGIN_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PIN_LABELS, DEFAULT_RESET_PASSWORD_LABELS, ForgotPasswordForm, type ForgotPasswordFormLabels, type ForgotPasswordFormProps, LoginForm, type LoginFormLabels, type LoginFormProps, OtpForm, type OtpFormLabels, type OtpFormProps, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, PasswordPolicyError, PinForm, type PinFormLabels, type PinFormProps, ResetPasswordError, ResetPasswordForm, type ResetPasswordFormLabels, type ResetPasswordFormProps, type RoleRoute, type RoleRouteTable, type UseBffAuthOptions, type UseBffAuthResult, type UseBffForgotPasswordOptions, type UseBffResetPasswordOptions, type UseOtpLoginOptions, type UseOtpLoginResult, type UsePinLoginOptions, type UsePinLoginResult, type UseResetPasswordFormArgs, type UseResetPasswordFormResult, collectUserRoles, createBffAuthClient, defaultAuthTheme, isPasswordValid, resolvePostLoginRoute, useAuthTheme, useBffAuth, useBffForgotPassword, useBffResetPassword, useOtpLogin, usePinLogin, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
|
|
913
|
+
export { AuthTestIds, type AuthTheme, type AuthThemeColors, AuthThemeProvider, type AuthThemeProviderProps, type AuthThemeRadii, type AuthThemeSpacing, type AuthThemeTypography, BffAuthStatus, type CreateBffAuthClientOptions, DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_LOGIN_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PIN_LABELS, DEFAULT_RESET_PASSWORD_LABELS, ForgotPasswordFields, type ForgotPasswordFieldsLabels, type ForgotPasswordFieldsProps, ForgotPasswordForm, type ForgotPasswordFormLabels, type ForgotPasswordFormProps, LoginForm, type LoginFormLabels, type LoginFormProps, OtpForm, type OtpFormLabels, type OtpFormProps, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, PasswordPolicyError, PinForm, type PinFormLabels, type PinFormProps, ResetPasswordError, ResetPasswordForm, type ResetPasswordFormLabels, type ResetPasswordFormProps, type RoleRoute, type RoleRouteTable, type UseBffAuthOptions, type UseBffAuthResult, type UseBffForgotPasswordOptions, type UseBffResetPasswordOptions, type UseForgotPasswordSubmitArgs, type UseForgotPasswordSubmitResult, type UseOtpLoginOptions, type UseOtpLoginResult, type UsePinLoginOptions, type UsePinLoginResult, type UseResetPasswordFormArgs, type UseResetPasswordFormResult, collectUserRoles, createBffAuthClient, defaultAuthTheme, isPasswordValid, isValidForgotPasswordEmail, resolvePostLoginRoute, useAuthTheme, useBffAuth, useBffForgotPassword, useBffResetPassword, useForgotPasswordSubmit, useOtpLogin, usePinLogin, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
|
package/dist/index.d.ts
CHANGED
|
@@ -52,6 +52,17 @@ interface ForgotPasswordFormLabels {
|
|
|
52
52
|
/** Shown when the entered value is not a valid email. */
|
|
53
53
|
invalidEmail: string;
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Strings rendered by `<ForgotPasswordFields>` — the embedded modal body. It is
|
|
57
|
+
* the forgot-password form plus the two modal affordances (`cancel` alongside
|
|
58
|
+
* submit, `close` on the success state) that a screen-shaped form has no need of.
|
|
59
|
+
*/
|
|
60
|
+
interface ForgotPasswordFieldsLabels extends ForgotPasswordFormLabels {
|
|
61
|
+
/** The "cancel" button shown alongside submit. */
|
|
62
|
+
cancel: string;
|
|
63
|
+
/** The "close" button shown on the success state. */
|
|
64
|
+
close: string;
|
|
65
|
+
}
|
|
55
66
|
/** Strings rendered by `<OtpForm>` — the two-step email-OTP login form. */
|
|
56
67
|
interface OtpFormLabels {
|
|
57
68
|
/** Title shown on the request-a-code step. */
|
|
@@ -127,6 +138,7 @@ interface ResetPasswordFormLabels {
|
|
|
127
138
|
}
|
|
128
139
|
declare const DEFAULT_LOGIN_LABELS: LoginFormLabels;
|
|
129
140
|
declare const DEFAULT_FORGOT_PASSWORD_LABELS: ForgotPasswordFormLabels;
|
|
141
|
+
declare const DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS: ForgotPasswordFieldsLabels;
|
|
130
142
|
declare const DEFAULT_OTP_LABELS: OtpFormLabels;
|
|
131
143
|
declare const DEFAULT_PIN_LABELS: PinFormLabels;
|
|
132
144
|
declare const DEFAULT_RESET_PASSWORD_LABELS: ResetPasswordFormLabels;
|
|
@@ -284,6 +296,56 @@ interface ForgotPasswordFormProps {
|
|
|
284
296
|
/** Themeable "request a reset link" form built on `useBffForgotPassword`. */
|
|
285
297
|
declare function ForgotPasswordForm({ client, theme: themeProp, labels: labelsProp, resetUrlTemplate, onSuccess, testIdPrefix, }: Readonly<ForgotPasswordFormProps>): ReactElement;
|
|
286
298
|
|
|
299
|
+
/**
|
|
300
|
+
* `<ForgotPasswordFields>` — the embedded, themeable "request a reset link"
|
|
301
|
+
* body, sized to live INSIDE an app-owned modal shell (not a full screen).
|
|
302
|
+
*
|
|
303
|
+
* The product apps each show "Forgot password?" as a modal launched from their
|
|
304
|
+
* login route, wrapping their own modal chrome (title bar + close affordance)
|
|
305
|
+
* around an identical email field + submit/cancel body. This is that body,
|
|
306
|
+
* promoted into the package so the three apps stop hand-rolling it.
|
|
307
|
+
*
|
|
308
|
+
* Built on the react-query-free `useForgotPasswordSubmit` hook, so it renders +
|
|
309
|
+
* submits on a provider-less login route (where `useMutation` would crash). The
|
|
310
|
+
* backend is anti-enumeration, so a successful request swaps to a generic
|
|
311
|
+
* confirmation that never reveals whether the address is registered.
|
|
312
|
+
*
|
|
313
|
+
* Differs from the screen-shaped `<ForgotPasswordForm>`: no outer screen/card
|
|
314
|
+
* wrapper and no title (the host modal owns those), plus two modal affordances —
|
|
315
|
+
* a `Cancel` button beside submit and a `Close` button on the success state —
|
|
316
|
+
* each rendered only when its handler is supplied.
|
|
317
|
+
*/
|
|
318
|
+
|
|
319
|
+
interface ForgotPasswordFieldsProps {
|
|
320
|
+
/** The same-origin BFF client (build it with `createBffAuthClient`). */
|
|
321
|
+
client: BffAuthClient;
|
|
322
|
+
/** Explicit theme; overrides any `<AuthThemeProvider>`. */
|
|
323
|
+
theme?: AuthTheme;
|
|
324
|
+
/** Localised copy. Partial — unspecified keys fall back to English defaults. */
|
|
325
|
+
labels?: Partial<ForgotPasswordFieldsLabels>;
|
|
326
|
+
/**
|
|
327
|
+
* Full URL with a `{token}` placeholder, forwarded to the backend so it can
|
|
328
|
+
* build the reset-email link without hardcoding a frontend host.
|
|
329
|
+
*/
|
|
330
|
+
resetUrlTemplate?: string;
|
|
331
|
+
/**
|
|
332
|
+
* The host modal's open state. When it flips to `false` the body clears
|
|
333
|
+
* itself, so reopening shows a fresh form regardless of which affordance
|
|
334
|
+
* closed it. Defaults to `true` for non-modal embedding.
|
|
335
|
+
*/
|
|
336
|
+
visible?: boolean;
|
|
337
|
+
/** Called once the request succeeds (the generic-confirmation state). */
|
|
338
|
+
onSuccess?: () => void;
|
|
339
|
+
/** When supplied, renders a Cancel button beside submit. */
|
|
340
|
+
onCancel?: () => void;
|
|
341
|
+
/** When supplied, renders a Close button on the success state. */
|
|
342
|
+
onClose?: () => void;
|
|
343
|
+
/** Prefix applied to every `testID`. */
|
|
344
|
+
testIdPrefix?: string;
|
|
345
|
+
}
|
|
346
|
+
/** Embedded "request a reset link" body for an app-owned modal. */
|
|
347
|
+
declare function ForgotPasswordFields({ client, theme: themeProp, labels: labelsProp, resetUrlTemplate, visible, onSuccess, onCancel, onClose, testIdPrefix, }: Readonly<ForgotPasswordFieldsProps>): ReactElement;
|
|
348
|
+
|
|
287
349
|
/**
|
|
288
350
|
* `<ResetPasswordForm>` — the ready-made, themeable "choose a new password"
|
|
289
351
|
* form.
|
|
@@ -438,6 +500,8 @@ declare const AuthTestIds: {
|
|
|
438
500
|
readonly forgotPasswordForm: "auth-forgot-form";
|
|
439
501
|
readonly forgotPasswordEmailInput: "auth-forgot-email";
|
|
440
502
|
readonly forgotPasswordSubmitButton: "auth-forgot-submit";
|
|
503
|
+
readonly forgotPasswordCancelButton: "auth-forgot-cancel";
|
|
504
|
+
readonly forgotPasswordCloseButton: "auth-forgot-close";
|
|
441
505
|
readonly forgotPasswordError: "auth-forgot-error";
|
|
442
506
|
readonly forgotPasswordSuccess: "auth-forgot-success";
|
|
443
507
|
readonly resetPasswordForm: "auth-reset-form";
|
|
@@ -590,6 +654,38 @@ interface UseResetPasswordFormResult {
|
|
|
590
654
|
*/
|
|
591
655
|
declare function useResetPasswordForm({ client, token, onSuccess, }: UseResetPasswordFormArgs): UseResetPasswordFormResult;
|
|
592
656
|
|
|
657
|
+
/** `true` when `value` (after trimming) looks like an email address. */
|
|
658
|
+
declare function isValidForgotPasswordEmail(value: string): boolean;
|
|
659
|
+
interface UseForgotPasswordSubmitArgs {
|
|
660
|
+
/** The same-origin BFF client (build it with `createBffAuthClient`). */
|
|
661
|
+
client: BffAuthClient;
|
|
662
|
+
/**
|
|
663
|
+
* Full URL with a `{token}` placeholder, forwarded to the backend so it can
|
|
664
|
+
* build the reset-email link without hardcoding a frontend host.
|
|
665
|
+
*/
|
|
666
|
+
resetUrlTemplate?: string;
|
|
667
|
+
/** Invoked once the request succeeds (the generic-confirmation state). */
|
|
668
|
+
onSuccess?: () => void;
|
|
669
|
+
}
|
|
670
|
+
interface UseForgotPasswordSubmitResult {
|
|
671
|
+
email: string;
|
|
672
|
+
setEmail: (value: string) => void;
|
|
673
|
+
/** `true` once the request has succeeded (show the generic confirmation). */
|
|
674
|
+
submitted: boolean;
|
|
675
|
+
/** `true` while the request is in flight. */
|
|
676
|
+
isSubmitting: boolean;
|
|
677
|
+
/** `true` when the last submit hit a network / 5xx failure. */
|
|
678
|
+
hasNetworkError: boolean;
|
|
679
|
+
/** `true` when the email is valid and no request is in flight. */
|
|
680
|
+
canSubmit: boolean;
|
|
681
|
+
/** Validate, then fire the request (no-op when `canSubmit` is false). */
|
|
682
|
+
submit: () => void;
|
|
683
|
+
/** Clear all state — call when the modal closes. */
|
|
684
|
+
reset: () => void;
|
|
685
|
+
}
|
|
686
|
+
/** Headless, react-query-free "request a reset link" logic. */
|
|
687
|
+
declare function useForgotPasswordSubmit({ client, resetUrlTemplate, onSuccess, }: UseForgotPasswordSubmitArgs): UseForgotPasswordSubmitResult;
|
|
688
|
+
|
|
593
689
|
/**
|
|
594
690
|
* The two discrete steps of the email-OTP login flow.
|
|
595
691
|
*
|
|
@@ -814,4 +910,4 @@ declare function validatePasswordPolicy(password: string): PasswordPolicyError[]
|
|
|
814
910
|
/** `true` when the password satisfies every policy rule. */
|
|
815
911
|
declare function isPasswordValid(password: string): boolean;
|
|
816
912
|
|
|
817
|
-
export { AuthTestIds, type AuthTheme, type AuthThemeColors, AuthThemeProvider, type AuthThemeProviderProps, type AuthThemeRadii, type AuthThemeSpacing, type AuthThemeTypography, BffAuthStatus, type CreateBffAuthClientOptions, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_LOGIN_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PIN_LABELS, DEFAULT_RESET_PASSWORD_LABELS, ForgotPasswordForm, type ForgotPasswordFormLabels, type ForgotPasswordFormProps, LoginForm, type LoginFormLabels, type LoginFormProps, OtpForm, type OtpFormLabels, type OtpFormProps, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, PasswordPolicyError, PinForm, type PinFormLabels, type PinFormProps, ResetPasswordError, ResetPasswordForm, type ResetPasswordFormLabels, type ResetPasswordFormProps, type RoleRoute, type RoleRouteTable, type UseBffAuthOptions, type UseBffAuthResult, type UseBffForgotPasswordOptions, type UseBffResetPasswordOptions, type UseOtpLoginOptions, type UseOtpLoginResult, type UsePinLoginOptions, type UsePinLoginResult, type UseResetPasswordFormArgs, type UseResetPasswordFormResult, collectUserRoles, createBffAuthClient, defaultAuthTheme, isPasswordValid, resolvePostLoginRoute, useAuthTheme, useBffAuth, useBffForgotPassword, useBffResetPassword, useOtpLogin, usePinLogin, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
|
|
913
|
+
export { AuthTestIds, type AuthTheme, type AuthThemeColors, AuthThemeProvider, type AuthThemeProviderProps, type AuthThemeRadii, type AuthThemeSpacing, type AuthThemeTypography, BffAuthStatus, type CreateBffAuthClientOptions, DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_LOGIN_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PIN_LABELS, DEFAULT_RESET_PASSWORD_LABELS, ForgotPasswordFields, type ForgotPasswordFieldsLabels, type ForgotPasswordFieldsProps, ForgotPasswordForm, type ForgotPasswordFormLabels, type ForgotPasswordFormProps, LoginForm, type LoginFormLabels, type LoginFormProps, OtpForm, type OtpFormLabels, type OtpFormProps, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, PasswordPolicyError, PinForm, type PinFormLabels, type PinFormProps, ResetPasswordError, ResetPasswordForm, type ResetPasswordFormLabels, type ResetPasswordFormProps, type RoleRoute, type RoleRouteTable, type UseBffAuthOptions, type UseBffAuthResult, type UseBffForgotPasswordOptions, type UseBffResetPasswordOptions, type UseForgotPasswordSubmitArgs, type UseForgotPasswordSubmitResult, type UseOtpLoginOptions, type UseOtpLoginResult, type UsePinLoginOptions, type UsePinLoginResult, type UseResetPasswordFormArgs, type UseResetPasswordFormResult, collectUserRoles, createBffAuthClient, defaultAuthTheme, isPasswordValid, isValidForgotPasswordEmail, resolvePostLoginRoute, useAuthTheme, useBffAuth, useBffForgotPassword, useBffResetPassword, useForgotPasswordSubmit, useOtpLogin, usePinLogin, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,11 @@ var DEFAULT_FORGOT_PASSWORD_LABELS = {
|
|
|
34
34
|
networkError: "Something went wrong. Please try again.",
|
|
35
35
|
invalidEmail: "Enter a valid email address."
|
|
36
36
|
};
|
|
37
|
+
var DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS = {
|
|
38
|
+
...DEFAULT_FORGOT_PASSWORD_LABELS,
|
|
39
|
+
cancel: "Cancel",
|
|
40
|
+
close: "Close"
|
|
41
|
+
};
|
|
37
42
|
var DEFAULT_OTP_LABELS = {
|
|
38
43
|
requestTitle: "Sign in with a code",
|
|
39
44
|
requestDescription: "Enter your email and we will send you a one-time code.",
|
|
@@ -92,6 +97,8 @@ var AuthTestIds = {
|
|
|
92
97
|
forgotPasswordForm: "auth-forgot-form",
|
|
93
98
|
forgotPasswordEmailInput: "auth-forgot-email",
|
|
94
99
|
forgotPasswordSubmitButton: "auth-forgot-submit",
|
|
100
|
+
forgotPasswordCancelButton: "auth-forgot-cancel",
|
|
101
|
+
forgotPasswordCloseButton: "auth-forgot-close",
|
|
95
102
|
forgotPasswordError: "auth-forgot-error",
|
|
96
103
|
forgotPasswordSuccess: "auth-forgot-success",
|
|
97
104
|
resetPasswordForm: "auth-reset-form",
|
|
@@ -612,6 +619,182 @@ function ForgotPasswordForm({
|
|
|
612
619
|
}
|
|
613
620
|
) });
|
|
614
621
|
}
|
|
622
|
+
var EMAIL_REGEX2 = /^[^@\s]+@[^.\s]+\.[^\s]+$/;
|
|
623
|
+
function isValidForgotPasswordEmail(value) {
|
|
624
|
+
return EMAIL_REGEX2.test(value.trim());
|
|
625
|
+
}
|
|
626
|
+
function useForgotPasswordSubmit({
|
|
627
|
+
client,
|
|
628
|
+
resetUrlTemplate,
|
|
629
|
+
onSuccess
|
|
630
|
+
}) {
|
|
631
|
+
const [email, setEmail] = react.useState("");
|
|
632
|
+
const [submitted, setSubmitted] = react.useState(false);
|
|
633
|
+
const [isSubmitting, setIsSubmitting] = react.useState(false);
|
|
634
|
+
const [hasNetworkError, setHasNetworkError] = react.useState(false);
|
|
635
|
+
const canSubmit = isValidForgotPasswordEmail(email) && !isSubmitting;
|
|
636
|
+
const submit = react.useCallback(() => {
|
|
637
|
+
const target = email.trim();
|
|
638
|
+
if (!isValidForgotPasswordEmail(target) || isSubmitting) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
setHasNetworkError(false);
|
|
642
|
+
setIsSubmitting(true);
|
|
643
|
+
const request = { email: target, resetUrlTemplate };
|
|
644
|
+
client.forgotPassword(request).then(() => {
|
|
645
|
+
setSubmitted(true);
|
|
646
|
+
onSuccess?.();
|
|
647
|
+
}).catch(() => setHasNetworkError(true)).finally(() => setIsSubmitting(false));
|
|
648
|
+
}, [client, email, isSubmitting, resetUrlTemplate, onSuccess]);
|
|
649
|
+
const reset = react.useCallback(() => {
|
|
650
|
+
setEmail("");
|
|
651
|
+
setSubmitted(false);
|
|
652
|
+
setIsSubmitting(false);
|
|
653
|
+
setHasNetworkError(false);
|
|
654
|
+
}, []);
|
|
655
|
+
return {
|
|
656
|
+
email,
|
|
657
|
+
setEmail,
|
|
658
|
+
submitted,
|
|
659
|
+
isSubmitting,
|
|
660
|
+
hasNetworkError,
|
|
661
|
+
canSubmit,
|
|
662
|
+
submit,
|
|
663
|
+
reset
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
var SECONDARY_BORDER_WIDTH = 1;
|
|
667
|
+
function useModalStyles(theme) {
|
|
668
|
+
return react.useMemo(
|
|
669
|
+
() => reactNative.StyleSheet.create({
|
|
670
|
+
body: { padding: theme.spacing.md },
|
|
671
|
+
actions: {
|
|
672
|
+
flexDirection: "row",
|
|
673
|
+
justifyContent: "flex-end",
|
|
674
|
+
alignItems: "center",
|
|
675
|
+
marginTop: theme.spacing.md,
|
|
676
|
+
gap: theme.spacing.sm
|
|
677
|
+
},
|
|
678
|
+
secondaryButton: {
|
|
679
|
+
borderRadius: theme.radii.input,
|
|
680
|
+
paddingVertical: theme.spacing.sm,
|
|
681
|
+
paddingHorizontal: theme.spacing.md,
|
|
682
|
+
borderWidth: SECONDARY_BORDER_WIDTH,
|
|
683
|
+
borderColor: theme.colors.border
|
|
684
|
+
},
|
|
685
|
+
secondaryButtonText: {
|
|
686
|
+
fontSize: theme.typography.body,
|
|
687
|
+
fontWeight: "600",
|
|
688
|
+
color: theme.colors.text
|
|
689
|
+
}
|
|
690
|
+
}),
|
|
691
|
+
[theme]
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
function ForgotPasswordFields({
|
|
695
|
+
client,
|
|
696
|
+
theme: themeProp,
|
|
697
|
+
labels: labelsProp,
|
|
698
|
+
resetUrlTemplate,
|
|
699
|
+
visible = true,
|
|
700
|
+
onSuccess,
|
|
701
|
+
onCancel,
|
|
702
|
+
onClose,
|
|
703
|
+
testIdPrefix
|
|
704
|
+
}) {
|
|
705
|
+
const theme = useAuthTheme(themeProp);
|
|
706
|
+
const styles = useAuthStyles(theme);
|
|
707
|
+
const modalStyles = useModalStyles(theme);
|
|
708
|
+
const labels = react.useMemo(
|
|
709
|
+
() => ({ ...DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS, ...labelsProp }),
|
|
710
|
+
[labelsProp]
|
|
711
|
+
);
|
|
712
|
+
const form = useForgotPasswordSubmit({ client, resetUrlTemplate, onSuccess });
|
|
713
|
+
const { reset } = form;
|
|
714
|
+
react.useEffect(() => {
|
|
715
|
+
if (!visible) {
|
|
716
|
+
reset();
|
|
717
|
+
}
|
|
718
|
+
}, [visible, reset]);
|
|
719
|
+
const submitButtonStyle = form.canSubmit ? styles.primaryButton : [styles.primaryButton, styles.primaryButtonDisabled];
|
|
720
|
+
const handleCancel = () => {
|
|
721
|
+
form.reset();
|
|
722
|
+
onCancel?.();
|
|
723
|
+
};
|
|
724
|
+
const handleClose = () => {
|
|
725
|
+
form.reset();
|
|
726
|
+
onClose?.();
|
|
727
|
+
};
|
|
728
|
+
if (form.submitted) {
|
|
729
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: modalStyles.body, testID: withTestIdPrefix(AuthTestIds.forgotPasswordForm, testIdPrefix), children: [
|
|
730
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.successText, testID: withTestIdPrefix(AuthTestIds.forgotPasswordSuccess, testIdPrefix), children: labels.successMessage }),
|
|
731
|
+
onClose !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: modalStyles.actions, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
732
|
+
reactNative.TouchableOpacity,
|
|
733
|
+
{
|
|
734
|
+
accessibilityHint: labels.close,
|
|
735
|
+
accessibilityLabel: labels.close,
|
|
736
|
+
accessibilityRole: "button",
|
|
737
|
+
style: styles.primaryButton,
|
|
738
|
+
testID: withTestIdPrefix(AuthTestIds.forgotPasswordCloseButton, testIdPrefix),
|
|
739
|
+
onPress: handleClose,
|
|
740
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.primaryButtonText, children: labels.close })
|
|
741
|
+
}
|
|
742
|
+
) }) : null
|
|
743
|
+
] });
|
|
744
|
+
}
|
|
745
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: modalStyles.body, testID: withTestIdPrefix(AuthTestIds.forgotPasswordForm, testIdPrefix), children: [
|
|
746
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.subtitle, children: labels.description }),
|
|
747
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.fieldGroup, children: [
|
|
748
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.label, children: labels.emailLabel }),
|
|
749
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
750
|
+
reactNative.TextInput,
|
|
751
|
+
{
|
|
752
|
+
accessibilityHint: labels.emailPlaceholder,
|
|
753
|
+
accessibilityLabel: labels.emailLabel,
|
|
754
|
+
autoCapitalize: "none",
|
|
755
|
+
autoCorrect: false,
|
|
756
|
+
editable: !form.isSubmitting,
|
|
757
|
+
keyboardType: "email-address",
|
|
758
|
+
placeholder: labels.emailPlaceholder,
|
|
759
|
+
placeholderTextColor: theme.colors.textSecondary,
|
|
760
|
+
style: styles.input,
|
|
761
|
+
testID: withTestIdPrefix(AuthTestIds.forgotPasswordEmailInput, testIdPrefix),
|
|
762
|
+
value: form.email,
|
|
763
|
+
onChangeText: form.setEmail
|
|
764
|
+
}
|
|
765
|
+
)
|
|
766
|
+
] }),
|
|
767
|
+
form.hasNetworkError ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.errorText, testID: withTestIdPrefix(AuthTestIds.forgotPasswordError, testIdPrefix), children: labels.networkError }) : null,
|
|
768
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: modalStyles.actions, children: [
|
|
769
|
+
onCancel !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
770
|
+
reactNative.TouchableOpacity,
|
|
771
|
+
{
|
|
772
|
+
accessibilityHint: labels.cancel,
|
|
773
|
+
accessibilityLabel: labels.cancel,
|
|
774
|
+
accessibilityRole: "button",
|
|
775
|
+
disabled: form.isSubmitting,
|
|
776
|
+
style: modalStyles.secondaryButton,
|
|
777
|
+
testID: withTestIdPrefix(AuthTestIds.forgotPasswordCancelButton, testIdPrefix),
|
|
778
|
+
onPress: handleCancel,
|
|
779
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: modalStyles.secondaryButtonText, children: labels.cancel })
|
|
780
|
+
}
|
|
781
|
+
) : null,
|
|
782
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
783
|
+
reactNative.TouchableOpacity,
|
|
784
|
+
{
|
|
785
|
+
accessibilityHint: labels.submit,
|
|
786
|
+
accessibilityLabel: form.isSubmitting ? labels.submitting : labels.submit,
|
|
787
|
+
accessibilityRole: "button",
|
|
788
|
+
disabled: !form.canSubmit,
|
|
789
|
+
style: submitButtonStyle,
|
|
790
|
+
testID: withTestIdPrefix(AuthTestIds.forgotPasswordSubmitButton, testIdPrefix),
|
|
791
|
+
onPress: form.submit,
|
|
792
|
+
children: form.isSubmitting ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.primaryButtonText, children: labels.submit })
|
|
793
|
+
}
|
|
794
|
+
)
|
|
795
|
+
] })
|
|
796
|
+
] });
|
|
797
|
+
}
|
|
615
798
|
|
|
616
799
|
// src/hooks/ResetPasswordError.ts
|
|
617
800
|
var ResetPasswordError = /* @__PURE__ */ ((ResetPasswordError2) => {
|
|
@@ -1113,9 +1296,9 @@ function useOtpLogin(options) {
|
|
|
1113
1296
|
]
|
|
1114
1297
|
);
|
|
1115
1298
|
}
|
|
1116
|
-
var
|
|
1299
|
+
var EMAIL_REGEX3 = /^[^@\s]+@[^.\s]+\.[^\s]+$/;
|
|
1117
1300
|
function isValidEmail2(value) {
|
|
1118
|
-
return
|
|
1301
|
+
return EMAIL_REGEX3.test(value);
|
|
1119
1302
|
}
|
|
1120
1303
|
function transportErrorFor(step, error, labels) {
|
|
1121
1304
|
if (error === null) {
|
|
@@ -1396,11 +1579,13 @@ Object.defineProperty(exports, "createFetchHttpClient", {
|
|
|
1396
1579
|
exports.AuthTestIds = AuthTestIds;
|
|
1397
1580
|
exports.AuthThemeProvider = AuthThemeProvider;
|
|
1398
1581
|
exports.BffAuthStatus = BffAuthStatus;
|
|
1582
|
+
exports.DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS = DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS;
|
|
1399
1583
|
exports.DEFAULT_FORGOT_PASSWORD_LABELS = DEFAULT_FORGOT_PASSWORD_LABELS;
|
|
1400
1584
|
exports.DEFAULT_LOGIN_LABELS = DEFAULT_LOGIN_LABELS;
|
|
1401
1585
|
exports.DEFAULT_OTP_LABELS = DEFAULT_OTP_LABELS;
|
|
1402
1586
|
exports.DEFAULT_PIN_LABELS = DEFAULT_PIN_LABELS;
|
|
1403
1587
|
exports.DEFAULT_RESET_PASSWORD_LABELS = DEFAULT_RESET_PASSWORD_LABELS;
|
|
1588
|
+
exports.ForgotPasswordFields = ForgotPasswordFields;
|
|
1404
1589
|
exports.ForgotPasswordForm = ForgotPasswordForm;
|
|
1405
1590
|
exports.LoginForm = LoginForm;
|
|
1406
1591
|
exports.OtpForm = OtpForm;
|
|
@@ -1415,11 +1600,13 @@ exports.collectUserRoles = collectUserRoles;
|
|
|
1415
1600
|
exports.createBffAuthClient = createBffAuthClient;
|
|
1416
1601
|
exports.defaultAuthTheme = defaultAuthTheme;
|
|
1417
1602
|
exports.isPasswordValid = isPasswordValid;
|
|
1603
|
+
exports.isValidForgotPasswordEmail = isValidForgotPasswordEmail;
|
|
1418
1604
|
exports.resolvePostLoginRoute = resolvePostLoginRoute;
|
|
1419
1605
|
exports.useAuthTheme = useAuthTheme;
|
|
1420
1606
|
exports.useBffAuth = useBffAuth;
|
|
1421
1607
|
exports.useBffForgotPassword = useBffForgotPassword;
|
|
1422
1608
|
exports.useBffResetPassword = useBffResetPassword;
|
|
1609
|
+
exports.useForgotPasswordSubmit = useForgotPasswordSubmit;
|
|
1423
1610
|
exports.useOtpLogin = useOtpLogin;
|
|
1424
1611
|
exports.usePinLogin = usePinLogin;
|
|
1425
1612
|
exports.useResetPasswordForm = useResetPasswordForm;
|