@dloizides/auth-web 1.5.1 → 1.7.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/dist/index.d.ts CHANGED
@@ -1,8 +1,134 @@
1
- import { ReactElement, ReactNode } from 'react';
1
+ import { ReactNode, ReactElement } from 'react';
2
2
  import { BffAuthClient, BffUser, HttpClient, BffLoginRequest, BffForgotPasswordRequest, BffResetPasswordRequest, BffOtpRequestResult } from '@dloizides/auth-client';
3
3
  export { BffAuthClient, BffAuthClientOptions, BffForgotPasswordRequest, BffLoginRequest, BffOtpRequestRequest, BffOtpRequestResult, BffOtpVerifyRequest, BffPinLoginRequest, BffRegisterRequest, BffResetPasswordRequest, BffUser, HttpClient, HttpRequest, HttpResponse, createFetchHttpClient } from '@dloizides/auth-client';
4
4
  import { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
5
5
 
6
+ /**
7
+ * `AuthTheme` — the design-token contract every `@dloizides/auth-web` component
8
+ * is styled against.
9
+ *
10
+ * The package owns **no** brand. Katalogos, Erevna and Kefi each look different;
11
+ * each one maps its own theme system onto this flat token bag and passes it in
12
+ * (via the `theme` prop or `<AuthThemeProvider>`). The component code only ever
13
+ * reads these tokens — never an app-specific theme object — so the same
14
+ * `<LoginForm>` renders on-brand in three visually distinct apps.
15
+ *
16
+ * Keep this shape minimal and stable: it is a public API surface.
17
+ */
18
+ /** Colour tokens — the only colours the auth components reference. */
19
+ interface AuthThemeColors {
20
+ /** Page / screen background behind the form card. */
21
+ background: string;
22
+ /** The form card surface colour. */
23
+ surface: string;
24
+ /** Primary text (titles, labels, input text). */
25
+ text: string;
26
+ /** Muted / secondary text (subtitles, helper copy). */
27
+ textSecondary: string;
28
+ /** Input + card border colour; also the disabled-button fill. */
29
+ border: string;
30
+ /** Brand primary — the submit button fill and link colour. */
31
+ primary: string;
32
+ /** Text drawn on top of `primary` (e.g. the submit button label). */
33
+ onPrimary: string;
34
+ /** Error text and error-state borders. */
35
+ danger: string;
36
+ /** Success text (e.g. the forgot-password confirmation). */
37
+ success: string;
38
+ }
39
+ /** Corner-radius tokens. */
40
+ interface AuthThemeRadii {
41
+ /** Inputs and buttons. */
42
+ input: number;
43
+ /** The form card. */
44
+ card: number;
45
+ }
46
+ /** Spacing scale (in density-independent pixels). */
47
+ interface AuthThemeSpacing {
48
+ /** Tight gap — e.g. label-to-input. */
49
+ xs: number;
50
+ /** Small gap. */
51
+ sm: number;
52
+ /** Default gap — between stacked fields. */
53
+ md: number;
54
+ /** Large gap — section separation. */
55
+ lg: number;
56
+ /** Card inner padding. */
57
+ xl: number;
58
+ }
59
+ /** Font-size tokens. */
60
+ interface AuthThemeTypography {
61
+ /** Form title. */
62
+ title: number;
63
+ /** Form subtitle. */
64
+ subtitle: number;
65
+ /** Field labels. */
66
+ label: number;
67
+ /** Input text and button text. */
68
+ body: number;
69
+ /** Helper / error / footer copy. */
70
+ caption: number;
71
+ }
72
+ /** The full token bag a consuming app supplies. */
73
+ interface AuthTheme {
74
+ colors: AuthThemeColors;
75
+ radii: AuthThemeRadii;
76
+ spacing: AuthThemeSpacing;
77
+ typography: AuthThemeTypography;
78
+ }
79
+ /**
80
+ * A neutral, accessible light-mode theme. Apps are expected to override this —
81
+ * it exists so a component renders sensibly with zero configuration and so
82
+ * `<AuthThemeProvider>` has a default value.
83
+ */
84
+ declare const defaultAuthTheme: AuthTheme;
85
+
86
+ /**
87
+ * `<AuthScreen>` — the screen-level auth composition, extracted from Kefi's
88
+ * login card (the design reference) so every app gets ONE tidy card instead of
89
+ * hand-rolling its own wrapper.
90
+ *
91
+ * The package's forms (`<LoginForm>`, `<OtpForm>`, `<PinForm>`) each render their
92
+ * own `screen` + `card` chrome by default (drop-one-in-on-a-blank-route mode).
93
+ * When an app needs to compose several controls (method tabs + a form + a passkey
94
+ * button + a hint) onto ONE surface, it renders them as `chromeless` children of
95
+ * an `<AuthScreen>`: the AuthScreen owns the single card, the forms contribute
96
+ * fields only. This makes the P1-04 "loose wrapper" bug impossible by
97
+ * construction — the app can no longer assemble a card with the wrong (or no)
98
+ * surface, border, padding or width.
99
+ *
100
+ * Slots mirror Kefi's login composition exactly: an optional `title`/`subtitle`
101
+ * at the top of the card, the `children` (the auth controls), and an optional
102
+ * `footer` rendered OUTSIDE the card (Kefi's sign-up footer sits below it).
103
+ */
104
+
105
+ /**
106
+ * Default auth-card max width, in dp. Matches the package's standalone form card
107
+ * so a single `<LoginForm>` and an `<AuthScreen>`-composed login read the same
108
+ * width unless the app overrides it.
109
+ */
110
+ declare const DEFAULT_AUTH_CARD_MAX_WIDTH = 420;
111
+ interface AuthScreenProps {
112
+ /** The auth controls placed on the shared card (forms should be `chromeless`). */
113
+ children: ReactNode;
114
+ /** Explicit theme; overrides any `<AuthThemeProvider>`. Falls back to the default theme. */
115
+ theme?: AuthTheme;
116
+ /** testID on the outer full-screen centering container. */
117
+ testID?: string;
118
+ /** testID on the inner card surface. */
119
+ cardTestID?: string;
120
+ /** Max card width in dp. Defaults to {@link DEFAULT_AUTH_CARD_MAX_WIDTH}. */
121
+ maxWidth?: number;
122
+ /** Optional heading rendered at the top of the card. */
123
+ title?: string;
124
+ /** Optional sub-heading rendered under the title. */
125
+ subtitle?: string;
126
+ /** Optional content rendered below/outside the card (e.g. a sign-up footer). */
127
+ footer?: ReactNode;
128
+ }
129
+ /** The screen-level auth composition: one centered card the controls share. */
130
+ declare function AuthScreen({ children, theme: themeProp, testID, cardTestID, maxWidth, title, subtitle, footer, }: Readonly<AuthScreenProps>): ReactElement;
131
+
6
132
  /**
7
133
  * Label bags for the auth components.
8
134
  *
@@ -293,86 +419,6 @@ declare const DEFAULT_PREFERRED_METHOD_SETTINGS_LABELS: PreferredMethodSettingsL
293
419
  declare const DEFAULT_PREFERRED_METHOD_HINT_LABELS: PreferredMethodHintLabels;
294
420
  declare const DEFAULT_RESET_PASSWORD_LABELS: ResetPasswordFormLabels;
295
421
 
296
- /**
297
- * `AuthTheme` — the design-token contract every `@dloizides/auth-web` component
298
- * is styled against.
299
- *
300
- * The package owns **no** brand. Katalogos, Erevna and Kefi each look different;
301
- * each one maps its own theme system onto this flat token bag and passes it in
302
- * (via the `theme` prop or `<AuthThemeProvider>`). The component code only ever
303
- * reads these tokens — never an app-specific theme object — so the same
304
- * `<LoginForm>` renders on-brand in three visually distinct apps.
305
- *
306
- * Keep this shape minimal and stable: it is a public API surface.
307
- */
308
- /** Colour tokens — the only colours the auth components reference. */
309
- interface AuthThemeColors {
310
- /** Page / screen background behind the form card. */
311
- background: string;
312
- /** The form card surface colour. */
313
- surface: string;
314
- /** Primary text (titles, labels, input text). */
315
- text: string;
316
- /** Muted / secondary text (subtitles, helper copy). */
317
- textSecondary: string;
318
- /** Input + card border colour; also the disabled-button fill. */
319
- border: string;
320
- /** Brand primary — the submit button fill and link colour. */
321
- primary: string;
322
- /** Text drawn on top of `primary` (e.g. the submit button label). */
323
- onPrimary: string;
324
- /** Error text and error-state borders. */
325
- danger: string;
326
- /** Success text (e.g. the forgot-password confirmation). */
327
- success: string;
328
- }
329
- /** Corner-radius tokens. */
330
- interface AuthThemeRadii {
331
- /** Inputs and buttons. */
332
- input: number;
333
- /** The form card. */
334
- card: number;
335
- }
336
- /** Spacing scale (in density-independent pixels). */
337
- interface AuthThemeSpacing {
338
- /** Tight gap — e.g. label-to-input. */
339
- xs: number;
340
- /** Small gap. */
341
- sm: number;
342
- /** Default gap — between stacked fields. */
343
- md: number;
344
- /** Large gap — section separation. */
345
- lg: number;
346
- /** Card inner padding. */
347
- xl: number;
348
- }
349
- /** Font-size tokens. */
350
- interface AuthThemeTypography {
351
- /** Form title. */
352
- title: number;
353
- /** Form subtitle. */
354
- subtitle: number;
355
- /** Field labels. */
356
- label: number;
357
- /** Input text and button text. */
358
- body: number;
359
- /** Helper / error / footer copy. */
360
- caption: number;
361
- }
362
- /** The full token bag a consuming app supplies. */
363
- interface AuthTheme {
364
- colors: AuthThemeColors;
365
- radii: AuthThemeRadii;
366
- spacing: AuthThemeSpacing;
367
- typography: AuthThemeTypography;
368
- }
369
- /**
370
- * A neutral, accessible light-mode theme. Apps are expected to override this —
371
- * it exists so a component renders sensibly with zero configuration and so
372
- * `<AuthThemeProvider>` has a default value.
373
- */
374
- declare const defaultAuthTheme: AuthTheme;
375
-
376
422
  /**
377
423
  * `<LoginForm>` — the ready-made, themeable password-login form.
378
424
  *
@@ -407,9 +453,16 @@ interface LoginFormProps {
407
453
  onSignUp?: () => void;
408
454
  /** Prefix applied to every `testID` so multiple forms can share a screen. */
409
455
  testIdPrefix?: string;
456
+ /**
457
+ * When `true`, render the form fields WITHOUT the built-in `screen` + `card`
458
+ * chrome, so the form can be composed as a child of `<AuthScreen>` (which owns
459
+ * the single shared card). Defaults to `false` — standalone drop-in behaviour
460
+ * is byte-identical to before.
461
+ */
462
+ chromeless?: boolean;
410
463
  }
411
464
  /** Themeable password-login form built on `useBffAuth`. */
412
- declare function LoginForm({ client, theme: themeProp, labels: labelsProp, onSuccess, onForgotPassword, onSignUp, testIdPrefix, }: Readonly<LoginFormProps>): ReactElement;
465
+ declare function LoginForm({ client, theme: themeProp, labels: labelsProp, onSuccess, onForgotPassword, onSignUp, testIdPrefix, chromeless, }: Readonly<LoginFormProps>): ReactElement;
413
466
 
414
467
  /**
415
468
  * `<ForgotPasswordForm>` — the ready-made, themeable "request a reset link"
@@ -555,9 +608,15 @@ interface OtpFormProps {
555
608
  onSuccess: (user: BffUser) => void;
556
609
  /** Prefix applied to every `testID` so multiple forms can share a screen. */
557
610
  testIdPrefix?: string;
611
+ /**
612
+ * When `true`, render WITHOUT the built-in `screen` + `card` chrome so the
613
+ * form can be composed as a child of `<AuthScreen>`. Defaults to `false` —
614
+ * standalone drop-in behaviour is byte-identical to before.
615
+ */
616
+ chromeless?: boolean;
558
617
  }
559
618
  /** Themeable two-step email-OTP login form built on `useOtpLogin`. */
560
- declare function OtpForm({ client, theme: themeProp, labels: labelsProp, onSuccess, testIdPrefix, }: Readonly<OtpFormProps>): ReactElement;
619
+ declare function OtpForm({ client, theme: themeProp, labels: labelsProp, onSuccess, testIdPrefix, chromeless, }: Readonly<OtpFormProps>): ReactElement;
561
620
 
562
621
  /**
563
622
  * `<PinForm>` — the ready-made, themeable single-step event-PIN login form.
@@ -593,9 +652,15 @@ interface PinFormProps {
593
652
  onSuccess: (user: BffUser) => void;
594
653
  /** Prefix applied to every `testID` so multiple forms can share a screen. */
595
654
  testIdPrefix?: string;
655
+ /**
656
+ * When `true`, render WITHOUT the built-in `screen` + `card` chrome so the
657
+ * form can be composed as a child of `<AuthScreen>`. Defaults to `false` —
658
+ * standalone drop-in behaviour is byte-identical to before.
659
+ */
660
+ chromeless?: boolean;
596
661
  }
597
662
  /** Themeable single-step event-PIN login form built on `usePinLogin`. */
598
- declare function PinForm({ client, eventExternalId, theme: themeProp, labels: labelsProp, onSuccess, testIdPrefix, }: Readonly<PinFormProps>): ReactElement;
663
+ declare function PinForm({ client, eventExternalId, theme: themeProp, labels: labelsProp, onSuccess, testIdPrefix, chromeless, }: Readonly<PinFormProps>): ReactElement;
599
664
 
600
665
  /**
601
666
  * A masked numeric PIN input for the device-PIN unlock + enrol surfaces.
@@ -1750,6 +1815,14 @@ interface RoleRoute {
1750
1815
  role: string;
1751
1816
  /** The route/path to send a user holding that role to. */
1752
1817
  route: string;
1818
+ /**
1819
+ * Optional translation key for a human label of this route — consumed by the
1820
+ * multi-role dashboard nav (`resolveAccessibleRoutes`) so an app can render a
1821
+ * localized link per accessible dashboard. Ignored by `resolvePostLoginRoute`
1822
+ * (landing only cares about the path). The package stays i18n-agnostic: it
1823
+ * carries the key, the app resolves it.
1824
+ */
1825
+ labelKey?: string;
1753
1826
  }
1754
1827
  /** An app-supplied, ordered role-routing table. */
1755
1828
  interface RoleRouteTable {
@@ -1779,6 +1852,15 @@ declare function collectUserRoles(user: BffUser): string[];
1779
1852
  */
1780
1853
  declare function resolvePostLoginRoute(user: BffUser, table: RoleRouteTable): string | null;
1781
1854
 
1855
+ /**
1856
+ * Resolve every route in `table` whose role the user holds, in table
1857
+ * (priority) order.
1858
+ *
1859
+ * @returns the matching `RoleRoute` entries (each carrying its optional
1860
+ * `labelKey`), or an empty array when the user holds none of the roles.
1861
+ */
1862
+ declare function resolveAccessibleRoutes(user: BffUser, table: RoleRouteTable): RoleRoute[];
1863
+
1782
1864
  /**
1783
1865
  * The discrete ways a password can fail the shared client-side policy.
1784
1866
  *
@@ -1824,4 +1906,4 @@ declare function validatePasswordPolicy(password: string): PasswordPolicyError[]
1824
1906
  /** `true` when the password satisfies every policy rule. */
1825
1907
  declare function isPasswordValid(password: string): boolean;
1826
1908
 
1827
- export { AuthTestIds, type AuthTheme, type AuthThemeColors, AuthThemeProvider, type AuthThemeProviderProps, type AuthThemeRadii, type AuthThemeSpacing, type AuthThemeTypography, BffAuthStatus, type BffDeviceState, type BffLoginConfig, type CreateBffAuthClientOptions, DEFAULT_DEVICE_PIN_ENROLL_LABELS, DEFAULT_DEVICE_PIN_SETTINGS_LABELS, DEFAULT_DEVICE_PIN_UNLOCK_LABELS, DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_LOGIN_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PASSKEY_LOGIN_LABELS, DEFAULT_PASSKEY_SETTINGS_LABELS, DEFAULT_PIN_LABELS, DEFAULT_PREFERRED_METHOD_HINT_LABELS, DEFAULT_PREFERRED_METHOD_SETTINGS_LABELS, DEFAULT_RESET_PASSWORD_LABELS, DEVICE_PIN_ALLOWED_DIGITS, DEVICE_PIN_DEFAULT_DIGITS, type DevicePinCapableClient, type DevicePinDisableCapableClient, type DevicePinEnrollCapableClient, DevicePinEnrollErrorKey, DevicePinEnrollForm, type DevicePinEnrollFormProps, type DevicePinEnrollLabels, type DevicePinEnrollResult, DevicePinErrorKey, DevicePinInput, type DevicePinInputProps, DevicePinLengthPicker, type DevicePinLengthPickerProps, DevicePinOffer, type DevicePinOfferProps, DevicePinSettingsCard, type DevicePinSettingsCardProps, type DevicePinSettingsLabels, type DevicePinUnlockCapableClient, type DevicePinUnlockLabels, type DevicePinUnlockResult, DevicePinUnlockScreen, type DevicePinUnlockScreenProps, type DevicePinUnlockedUser, ForgotPasswordFields, type ForgotPasswordFieldsLabels, type ForgotPasswordFieldsProps, ForgotPasswordForm, type ForgotPasswordFormLabels, type ForgotPasswordFormProps, type LoginConfigCapableClient, LoginForm, type LoginFormLabels, type LoginFormProps, OtpForm, type OtpFormLabels, type OtpFormProps, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, type PasskeyErrorCode, PasskeyLoginButton, type PasskeyLoginButtonProps, type PasskeyLoginLabels, PasskeySettingsCard, type PasskeySettingsCardProps, type PasskeySettingsLabels, PasswordPolicyError, PinForm, type PinFormLabels, type PinFormProps, type PreferencesClientOptions, type PreferredMethodClient, PreferredMethodHint, type PreferredMethodHintLabels, type PreferredMethodHintProps, PreferredMethodSettingsCard, type PreferredMethodSettingsCardProps, type PreferredMethodSettingsLabels, ResetPasswordError, ResetPasswordForm, type ResetPasswordFormLabels, type ResetPasswordFormProps, type RoleRoute, type RoleRouteTable, type UseBffAuthOptions, type UseBffAuthResult, type UseBffForgotPasswordOptions, type UseBffLoginConfigResult, type UseBffResetPasswordOptions, type UseDevicePinDisableArgs, type UseDevicePinDisableResult, type UseDevicePinEnrollArgs, type UseDevicePinEnrollResult, type UseDevicePinUnlockArgs, type UseDevicePinUnlockResult, type UseForgotPasswordSubmitArgs, type UseForgotPasswordSubmitResult, type UseOtpLoginOptions, type UseOtpLoginResult, type UsePinLoginOptions, type UsePinLoginResult, type UsePreferredMethodSettingArgs, type UsePreferredMethodSettingResult, type UseResetPasswordFormArgs, type UseResetPasswordFormResult, cachePreferredMethod, clearCachedPreferredMethod, collectUserRoles, createBffAuthClient, createPreferencesClient, defaultAuthTheme, isAllowedPinDigits, isPasswordValid, isValidForgotPasswordEmail, readCachedPreferredMethod, readPasskeyError, readPasskeyRegistered, resolvePostLoginRoute, startPasskeyLogin, startPasskeyRegistration, syncPreferredMethodFromServer, useAuthTheme, useBffAuth, useBffForgotPassword, useBffLoginConfig, useBffResetPassword, useDevicePinDisable, useDevicePinEnroll, useDevicePinUnlock, useForgotPasswordSubmit, useOtpLogin, usePinLogin, usePreferredMethodSetting, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
1909
+ export { AuthScreen, type AuthScreenProps, AuthTestIds, type AuthTheme, type AuthThemeColors, AuthThemeProvider, type AuthThemeProviderProps, type AuthThemeRadii, type AuthThemeSpacing, type AuthThemeTypography, BffAuthStatus, type BffDeviceState, type BffLoginConfig, type CreateBffAuthClientOptions, DEFAULT_AUTH_CARD_MAX_WIDTH, DEFAULT_DEVICE_PIN_ENROLL_LABELS, DEFAULT_DEVICE_PIN_SETTINGS_LABELS, DEFAULT_DEVICE_PIN_UNLOCK_LABELS, DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_LOGIN_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PASSKEY_LOGIN_LABELS, DEFAULT_PASSKEY_SETTINGS_LABELS, DEFAULT_PIN_LABELS, DEFAULT_PREFERRED_METHOD_HINT_LABELS, DEFAULT_PREFERRED_METHOD_SETTINGS_LABELS, DEFAULT_RESET_PASSWORD_LABELS, DEVICE_PIN_ALLOWED_DIGITS, DEVICE_PIN_DEFAULT_DIGITS, type DevicePinCapableClient, type DevicePinDisableCapableClient, type DevicePinEnrollCapableClient, DevicePinEnrollErrorKey, DevicePinEnrollForm, type DevicePinEnrollFormProps, type DevicePinEnrollLabels, type DevicePinEnrollResult, DevicePinErrorKey, DevicePinInput, type DevicePinInputProps, DevicePinLengthPicker, type DevicePinLengthPickerProps, DevicePinOffer, type DevicePinOfferProps, DevicePinSettingsCard, type DevicePinSettingsCardProps, type DevicePinSettingsLabels, type DevicePinUnlockCapableClient, type DevicePinUnlockLabels, type DevicePinUnlockResult, DevicePinUnlockScreen, type DevicePinUnlockScreenProps, type DevicePinUnlockedUser, ForgotPasswordFields, type ForgotPasswordFieldsLabels, type ForgotPasswordFieldsProps, ForgotPasswordForm, type ForgotPasswordFormLabels, type ForgotPasswordFormProps, type LoginConfigCapableClient, LoginForm, type LoginFormLabels, type LoginFormProps, OtpForm, type OtpFormLabels, type OtpFormProps, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, type PasskeyErrorCode, PasskeyLoginButton, type PasskeyLoginButtonProps, type PasskeyLoginLabels, PasskeySettingsCard, type PasskeySettingsCardProps, type PasskeySettingsLabels, PasswordPolicyError, PinForm, type PinFormLabels, type PinFormProps, type PreferencesClientOptions, type PreferredMethodClient, PreferredMethodHint, type PreferredMethodHintLabels, type PreferredMethodHintProps, PreferredMethodSettingsCard, type PreferredMethodSettingsCardProps, type PreferredMethodSettingsLabels, ResetPasswordError, ResetPasswordForm, type ResetPasswordFormLabels, type ResetPasswordFormProps, type RoleRoute, type RoleRouteTable, type UseBffAuthOptions, type UseBffAuthResult, type UseBffForgotPasswordOptions, type UseBffLoginConfigResult, type UseBffResetPasswordOptions, type UseDevicePinDisableArgs, type UseDevicePinDisableResult, type UseDevicePinEnrollArgs, type UseDevicePinEnrollResult, type UseDevicePinUnlockArgs, type UseDevicePinUnlockResult, type UseForgotPasswordSubmitArgs, type UseForgotPasswordSubmitResult, type UseOtpLoginOptions, type UseOtpLoginResult, type UsePinLoginOptions, type UsePinLoginResult, type UsePreferredMethodSettingArgs, type UsePreferredMethodSettingResult, type UseResetPasswordFormArgs, type UseResetPasswordFormResult, cachePreferredMethod, clearCachedPreferredMethod, collectUserRoles, createBffAuthClient, createPreferencesClient, defaultAuthTheme, isAllowedPinDigits, isPasswordValid, isValidForgotPasswordEmail, readCachedPreferredMethod, readPasskeyError, readPasskeyRegistered, resolveAccessibleRoutes, resolvePostLoginRoute, startPasskeyLogin, startPasskeyRegistration, syncPreferredMethodFromServer, useAuthTheme, useBffAuth, useBffForgotPassword, useBffLoginConfig, useBffResetPassword, useDevicePinDisable, useDevicePinEnroll, useDevicePinUnlock, useForgotPasswordSubmit, useOtpLogin, usePinLogin, usePreferredMethodSetting, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
package/dist/index.js CHANGED
@@ -1,12 +1,118 @@
1
1
  'use strict';
2
2
 
3
- var react = require('react');
4
3
  var reactNative = require('react-native');
4
+ var react = require('react');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
  var reactQuery = require('@tanstack/react-query');
7
7
  var authClient = require('@dloizides/auth-client');
8
8
 
9
- // src/components/LoginForm.tsx
9
+ // src/components/AuthScreen.tsx
10
+ var FULL_WIDTH = "100%";
11
+ var CARD_BORDER_WIDTH = 1;
12
+ function useAuthScreenStyles(theme) {
13
+ return react.useMemo(() => {
14
+ const { colors, radii, spacing, typography } = theme;
15
+ return reactNative.StyleSheet.create({
16
+ screen: {
17
+ flex: 1,
18
+ justifyContent: "center",
19
+ alignItems: "center",
20
+ padding: spacing.md,
21
+ backgroundColor: colors.background
22
+ },
23
+ card: {
24
+ width: FULL_WIDTH,
25
+ borderRadius: radii.card,
26
+ borderWidth: CARD_BORDER_WIDTH,
27
+ borderColor: colors.border,
28
+ padding: spacing.xl,
29
+ backgroundColor: colors.surface
30
+ },
31
+ title: {
32
+ fontSize: typography.title,
33
+ fontWeight: "bold",
34
+ marginBottom: spacing.xs,
35
+ color: colors.text
36
+ },
37
+ subtitle: {
38
+ fontSize: typography.subtitle,
39
+ marginBottom: spacing.lg,
40
+ color: colors.textSecondary
41
+ },
42
+ footer: {
43
+ marginTop: spacing.lg,
44
+ width: FULL_WIDTH,
45
+ alignItems: "center"
46
+ }
47
+ });
48
+ }, [theme]);
49
+ }
50
+
51
+ // src/theme/AuthTheme.ts
52
+ var defaultAuthTheme = {
53
+ colors: {
54
+ background: "#f5f5f7",
55
+ surface: "#ffffff",
56
+ text: "#1a1a1a",
57
+ textSecondary: "#6b6b6b",
58
+ border: "#d0d0d5",
59
+ primary: "#2563eb",
60
+ onPrimary: "#ffffff",
61
+ danger: "#d33333",
62
+ success: "#1f7a3d"
63
+ },
64
+ radii: {
65
+ input: 8,
66
+ card: 12
67
+ },
68
+ spacing: {
69
+ xs: 8,
70
+ sm: 12,
71
+ md: 20,
72
+ lg: 32,
73
+ xl: 24
74
+ },
75
+ typography: {
76
+ title: 28,
77
+ subtitle: 16,
78
+ label: 14,
79
+ body: 16,
80
+ caption: 12
81
+ }
82
+ };
83
+ var AuthThemeContext = react.createContext(defaultAuthTheme);
84
+ function AuthThemeProvider({
85
+ theme,
86
+ children
87
+ }) {
88
+ return /* @__PURE__ */ jsxRuntime.jsx(AuthThemeContext.Provider, { value: theme, children });
89
+ }
90
+ function useAuthTheme(themeProp) {
91
+ const contextTheme = react.useContext(AuthThemeContext);
92
+ return react.useMemo(() => themeProp ?? contextTheme, [themeProp, contextTheme]);
93
+ }
94
+ var DEFAULT_AUTH_CARD_MAX_WIDTH = 420;
95
+ function AuthScreen({
96
+ children,
97
+ theme: themeProp,
98
+ testID,
99
+ cardTestID,
100
+ maxWidth = DEFAULT_AUTH_CARD_MAX_WIDTH,
101
+ title,
102
+ subtitle,
103
+ footer
104
+ }) {
105
+ const theme = useAuthTheme(themeProp);
106
+ const styles3 = useAuthScreenStyles(theme);
107
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.screen, testID, children: [
108
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles3.card, { maxWidth }], testID: cardTestID, children: [
109
+ title !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.title, children: title }) : null,
110
+ subtitle !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.subtitle, children: subtitle }) : null,
111
+ children
112
+ ] }),
113
+ footer !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles3.footer, { maxWidth }], children: footer }) : null
114
+ ] });
115
+ }
10
116
 
11
117
  // src/components/labels.ts
12
118
  var DEFAULT_LOGIN_LABELS = {
@@ -247,7 +353,7 @@ var AuthTestIds = {
247
353
  function withTestIdPrefix(baseId, prefix) {
248
354
  return prefix === void 0 || prefix === "" ? baseId : `${prefix}-${baseId}`;
249
355
  }
250
- var FULL_WIDTH = "100%";
356
+ var FULL_WIDTH2 = "100%";
251
357
  var CARD_MAX_WIDTH = 420;
252
358
  var BUTTON_VERTICAL_PADDING = 14;
253
359
  function useAuthStyles(theme) {
@@ -262,7 +368,7 @@ function useAuthStyles(theme) {
262
368
  backgroundColor: colors.background
263
369
  },
264
370
  card: {
265
- width: FULL_WIDTH,
371
+ width: FULL_WIDTH2,
266
372
  maxWidth: CARD_MAX_WIDTH,
267
373
  borderRadius: radii.card,
268
374
  padding: spacing.xl,
@@ -305,11 +411,6 @@ function useAuthStyles(theme) {
305
411
  primaryButton: {
306
412
  borderRadius: radii.input,
307
413
  paddingVertical: BUTTON_VERTICAL_PADDING,
308
- paddingHorizontal: spacing.md,
309
- // Never shrink below the label width: in a row of actions (e.g. the
310
- // forgot-password modal's Cancel + submit), shrinking would clip a long
311
- // label like "Send reset link" past the button's rounded edge.
312
- flexShrink: 0,
313
414
  alignItems: "center",
314
415
  justifyContent: "center",
315
416
  marginTop: spacing.xs,
@@ -352,8 +453,6 @@ function useAuthStyles(theme) {
352
453
  borderWidth: 1,
353
454
  borderColor: colors.primary,
354
455
  paddingVertical: BUTTON_VERTICAL_PADDING,
355
- paddingHorizontal: spacing.md,
356
- flexShrink: 0,
357
456
  alignItems: "center",
358
457
  justifyContent: "center",
359
458
  marginTop: spacing.sm,
@@ -476,50 +575,6 @@ function useBffAuth(options) {
476
575
  [session.user, session.status, isSubmitting, error, login, logout, refresh]
477
576
  );
478
577
  }
479
-
480
- // src/theme/AuthTheme.ts
481
- var defaultAuthTheme = {
482
- colors: {
483
- background: "#f5f5f7",
484
- surface: "#ffffff",
485
- text: "#1a1a1a",
486
- textSecondary: "#6b6b6b",
487
- border: "#d0d0d5",
488
- primary: "#2563eb",
489
- onPrimary: "#ffffff",
490
- danger: "#d33333",
491
- success: "#1f7a3d"
492
- },
493
- radii: {
494
- input: 8,
495
- card: 12
496
- },
497
- spacing: {
498
- xs: 8,
499
- sm: 12,
500
- md: 20,
501
- lg: 32,
502
- xl: 24
503
- },
504
- typography: {
505
- title: 28,
506
- subtitle: 16,
507
- label: 14,
508
- body: 16,
509
- caption: 12
510
- }
511
- };
512
- var AuthThemeContext = react.createContext(defaultAuthTheme);
513
- function AuthThemeProvider({
514
- theme,
515
- children
516
- }) {
517
- return /* @__PURE__ */ jsxRuntime.jsx(AuthThemeContext.Provider, { value: theme, children });
518
- }
519
- function useAuthTheme(themeProp) {
520
- const contextTheme = react.useContext(AuthThemeContext);
521
- return react.useMemo(() => themeProp ?? contextTheme, [themeProp, contextTheme]);
522
- }
523
578
  function LoginForm({
524
579
  client,
525
580
  theme: themeProp,
@@ -527,7 +582,8 @@ function LoginForm({
527
582
  onSuccess,
528
583
  onForgotPassword,
529
584
  onSignUp,
530
- testIdPrefix
585
+ testIdPrefix,
586
+ chromeless = false
531
587
  }) {
532
588
  const theme = useAuthTheme(themeProp);
533
589
  const styles3 = useAuthStyles(theme);
@@ -558,7 +614,7 @@ function LoginForm({
558
614
  void runLogin();
559
615
  }, [runLogin]);
560
616
  const submitButtonStyle = isSubmitting ? [styles3.primaryButton, styles3.primaryButtonDisabled] : styles3.primaryButton;
561
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.screen, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.card, testID: withTestIdPrefix(AuthTestIds.loginForm, testIdPrefix), children: [
617
+ const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
562
618
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.title, children: labels.title }),
563
619
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.subtitle, children: labels.subtitle }),
564
620
  /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.fieldGroup, children: [
@@ -647,7 +703,16 @@ function LoginForm({
647
703
  children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.linkText, children: labels.signUp })
648
704
  }
649
705
  ) : null
650
- ] }) });
706
+ ] });
707
+ const card = /* @__PURE__ */ jsxRuntime.jsx(
708
+ reactNative.View,
709
+ {
710
+ style: chromeless ? void 0 : styles3.card,
711
+ testID: withTestIdPrefix(AuthTestIds.loginForm, testIdPrefix),
712
+ children: content
713
+ }
714
+ );
715
+ return chromeless ? card : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.screen, children: card });
651
716
  }
652
717
  function useBffForgotPassword(options) {
653
718
  const { client, ...rest } = options;
@@ -830,9 +895,6 @@ function useModalStyles(theme) {
830
895
  flexDirection: "row",
831
896
  justifyContent: "flex-end",
832
897
  alignItems: "center",
833
- // Wrap (rather than clip) when a long submit label + Cancel can't
834
- // fit a narrow modal on one line.
835
- flexWrap: "wrap",
836
898
  marginTop: theme.spacing.md,
837
899
  gap: theme.spacing.sm
838
900
  },
@@ -840,8 +902,6 @@ function useModalStyles(theme) {
840
902
  borderRadius: theme.radii.input,
841
903
  paddingVertical: theme.spacing.sm,
842
904
  paddingHorizontal: theme.spacing.md,
843
- // Match the submit button: never shrink below the label width.
844
- flexShrink: 0,
845
905
  borderWidth: SECONDARY_BORDER_WIDTH,
846
906
  borderColor: theme.colors.border
847
907
  },
@@ -1502,7 +1562,8 @@ function OtpForm({
1502
1562
  theme: themeProp,
1503
1563
  labels: labelsProp,
1504
1564
  onSuccess,
1505
- testIdPrefix
1565
+ testIdPrefix,
1566
+ chromeless = false
1506
1567
  }) {
1507
1568
  const theme = useAuthTheme(themeProp);
1508
1569
  const styles3 = useAuthStyles(theme);
@@ -1523,7 +1584,7 @@ function OtpForm({
1523
1584
  otp.reset();
1524
1585
  }, [otp]);
1525
1586
  const errorText = localError ?? transportErrorFor(otp.step, otp.error, labels);
1526
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.screen, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.card, testID: withTestIdPrefix(AuthTestIds.otpForm, testIdPrefix), children: otp.step === "requestCode" /* RequestCode */ ? /* @__PURE__ */ jsxRuntime.jsx(
1587
+ const content = otp.step === "requestCode" /* RequestCode */ ? /* @__PURE__ */ jsxRuntime.jsx(
1527
1588
  OtpRequestStep,
1528
1589
  {
1529
1590
  errorText,
@@ -1548,7 +1609,16 @@ function OtpForm({
1548
1609
  onResend: handleResend,
1549
1610
  onSubmit: handleVerify
1550
1611
  }
1551
- ) }) });
1612
+ );
1613
+ const card = /* @__PURE__ */ jsxRuntime.jsx(
1614
+ reactNative.View,
1615
+ {
1616
+ style: chromeless ? void 0 : styles3.card,
1617
+ testID: withTestIdPrefix(AuthTestIds.otpForm, testIdPrefix),
1618
+ children: content
1619
+ }
1620
+ );
1621
+ return chromeless ? card : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.screen, children: card });
1552
1622
  }
1553
1623
  function toError3(value) {
1554
1624
  return value instanceof Error ? value : new Error(String(value));
@@ -1627,7 +1697,8 @@ function PinForm({
1627
1697
  theme: themeProp,
1628
1698
  labels: labelsProp,
1629
1699
  onSuccess,
1630
- testIdPrefix
1700
+ testIdPrefix,
1701
+ chromeless = false
1631
1702
  }) {
1632
1703
  const theme = useAuthTheme(themeProp);
1633
1704
  const styles3 = useAuthStyles(theme);
@@ -1641,7 +1712,7 @@ function PinForm({
1641
1712
  const handleSubmit = useSubmitHandler(pin, labels, setLocalError, onSuccess);
1642
1713
  const errorText = localError ?? transportErrorFor2(pin.error, labels);
1643
1714
  const buttonStyle = pin.isSubmitting ? [styles3.primaryButton, styles3.primaryButtonDisabled] : styles3.primaryButton;
1644
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.screen, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.card, testID: withTestIdPrefix(AuthTestIds.pinForm, testIdPrefix), children: [
1715
+ const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1645
1716
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.title, children: labels.title }),
1646
1717
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.subtitle, children: labels.description }),
1647
1718
  /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.fieldGroup, children: [
@@ -1686,7 +1757,16 @@ function PinForm({
1686
1757
  children: pin.isSubmitting ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.primaryButtonText, children: labels.submit })
1687
1758
  }
1688
1759
  )
1689
- ] }) });
1760
+ ] });
1761
+ const card = /* @__PURE__ */ jsxRuntime.jsx(
1762
+ reactNative.View,
1763
+ {
1764
+ style: chromeless ? void 0 : styles3.card,
1765
+ testID: withTestIdPrefix(AuthTestIds.pinForm, testIdPrefix),
1766
+ children: content
1767
+ }
1768
+ );
1769
+ return chromeless ? card : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.screen, children: card });
1690
1770
  }
1691
1771
  var DOT_SIZE = 18;
1692
1772
  var DOT_BORDER_WIDTH = 2;
@@ -2920,6 +3000,12 @@ function resolvePostLoginRoute(user, table) {
2920
3000
  return table.fallback ?? null;
2921
3001
  }
2922
3002
 
3003
+ // src/router/resolveAccessibleRoutes.ts
3004
+ function resolveAccessibleRoutes(user, table) {
3005
+ const userRoles = new Set(collectUserRoles(user));
3006
+ return table.routes.filter((entry) => userRoles.has(entry.role));
3007
+ }
3008
+
2923
3009
  Object.defineProperty(exports, "BffAuthClient", {
2924
3010
  enumerable: true,
2925
3011
  get: function () { return authClient.BffAuthClient; }
@@ -2928,9 +3014,11 @@ Object.defineProperty(exports, "createFetchHttpClient", {
2928
3014
  enumerable: true,
2929
3015
  get: function () { return authClient.createFetchHttpClient; }
2930
3016
  });
3017
+ exports.AuthScreen = AuthScreen;
2931
3018
  exports.AuthTestIds = AuthTestIds;
2932
3019
  exports.AuthThemeProvider = AuthThemeProvider;
2933
3020
  exports.BffAuthStatus = BffAuthStatus;
3021
+ exports.DEFAULT_AUTH_CARD_MAX_WIDTH = DEFAULT_AUTH_CARD_MAX_WIDTH;
2934
3022
  exports.DEFAULT_DEVICE_PIN_ENROLL_LABELS = DEFAULT_DEVICE_PIN_ENROLL_LABELS;
2935
3023
  exports.DEFAULT_DEVICE_PIN_SETTINGS_LABELS = DEFAULT_DEVICE_PIN_SETTINGS_LABELS;
2936
3024
  exports.DEFAULT_DEVICE_PIN_UNLOCK_LABELS = DEFAULT_DEVICE_PIN_UNLOCK_LABELS;
@@ -2981,6 +3069,7 @@ exports.isValidForgotPasswordEmail = isValidForgotPasswordEmail;
2981
3069
  exports.readCachedPreferredMethod = readCachedPreferredMethod;
2982
3070
  exports.readPasskeyError = readPasskeyError;
2983
3071
  exports.readPasskeyRegistered = readPasskeyRegistered;
3072
+ exports.resolveAccessibleRoutes = resolveAccessibleRoutes;
2984
3073
  exports.resolvePostLoginRoute = resolvePostLoginRoute;
2985
3074
  exports.startPasskeyLogin = startPasskeyLogin;
2986
3075
  exports.startPasskeyRegistration = startPasskeyRegistration;