@passflow/react 0.15.0 → 0.16.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +60 -20
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.es.js +133 -146
  6. package/dist/index.es.js.map +1 -1
  7. package/dist/src/components/flow/index.d.cts +1 -1
  8. package/dist/src/components/flow/index.d.ts +1 -1
  9. package/dist/src/components/flow/index.d.ts.map +1 -1
  10. package/dist/src/components/flow/passflow/flow-router.d.cts +39 -0
  11. package/dist/src/components/flow/passflow/flow-router.d.ts +39 -0
  12. package/dist/src/components/flow/passflow/flow-router.d.ts.map +1 -0
  13. package/dist/src/components/flow/passflow/index.d.cts +25 -3
  14. package/dist/src/components/flow/passflow/index.d.ts +25 -3
  15. package/dist/src/components/flow/passflow/index.d.ts.map +1 -1
  16. package/dist/src/components/flow/two-factor-setup-magic-link/index.d.cts +6 -1
  17. package/dist/src/components/flow/two-factor-setup-magic-link/index.d.ts +6 -1
  18. package/dist/src/components/flow/two-factor-setup-magic-link/index.d.ts.map +1 -1
  19. package/dist/src/components/form/forgot-password/forgot-password-success.d.ts.map +1 -1
  20. package/dist/src/components/form/forgot-password/forgot-password.d.ts.map +1 -1
  21. package/dist/src/components/form/invitation-join/index.d.ts.map +1 -1
  22. package/dist/src/components/form/reset-password/index.d.ts.map +1 -1
  23. package/dist/src/components/form/signin/index.d.ts.map +1 -1
  24. package/dist/src/components/form/signup/index.d.ts.map +1 -1
  25. package/dist/src/components/form/two-factor-verify/two-factor-verify-form.d.ts.map +1 -1
  26. package/dist/src/components/form/verify-challenge/verify-challenge-magic-link.d.ts.map +1 -1
  27. package/dist/src/components/form/verify-challenge/verify-challenge-otp-manual.d.ts.map +1 -1
  28. package/dist/src/components/ui/fields/field-otp.d.cts +19 -6
  29. package/dist/src/components/ui/fields/field-otp.d.ts +19 -6
  30. package/dist/src/components/ui/fields/field-otp.d.ts.map +1 -1
  31. package/dist/src/components/ui/fields/field-password.d.ts.map +1 -1
  32. package/dist/src/components/ui/fields/field-phone.d.ts.map +1 -1
  33. package/dist/src/context/index.d.cts +1 -0
  34. package/dist/src/context/index.d.ts +1 -0
  35. package/dist/src/context/index.d.ts.map +1 -1
  36. package/dist/src/context/search-context.d.cts +20 -0
  37. package/dist/src/context/search-context.d.ts +20 -0
  38. package/dist/src/context/search-context.d.ts.map +1 -0
  39. package/dist/src/hooks/use-app-settings.d.ts.map +1 -1
  40. package/dist/src/utils/cn.d.ts.map +1 -1
  41. package/dist/src/utils/get-auth-methods.d.ts.map +1 -1
  42. package/dist/src/utils/get-form-labels.d.ts.map +1 -1
  43. package/dist/src/utils/url-params.d.cts +2 -4
  44. package/dist/src/utils/url-params.d.ts +2 -4
  45. package/dist/src/utils/url-params.d.ts.map +1 -1
  46. package/dist/src/utils/validation-schemas/index.d.ts.map +1 -1
  47. package/dist/style.css +1 -1
  48. package/package.json +9 -8
package/CHANGELOG.md CHANGED
@@ -2,6 +2,53 @@
2
2
 
3
3
  All notable changes to `@passflow/react` are documented in this file.
4
4
 
5
+ ## [0.16.0] - 2026-11-03
6
+
7
+ ### Breaking
8
+
9
+ - `react-router` is no longer a peer dependency, and the package does not import it. The only peers are `react` and `react-dom`, `^18 || ^19`. `PassflowFlow` keeps its own small router, so it works the same in an app that routes with React Router, TanStack Router, Wouter, another router, or none. A host that installed `react-router` only for this package can remove it.
10
+ - `PassflowFlow` no longer picks up a host React Router `<Router>`, and the console warning about `routerType` inside one is gone. Inside an app router in browser mode it shares the address bar with it: each side sees the other's navigations, and Back reaches both. Mount it on a catch-all route (`/auth/*` in React Router, `/auth/$` in TanStack Router, `<Route path="/auth" nest>` in Wouter).
11
+ - `pathPrefix` is an absolute path from the root of the URL. When it is not given, the flow takes it from the first URL it sees, so `<PassflowFlow />` on `/auth` or `/auth/signin` uses `/auth`. Before, inside a host router, it was relative to the parent route.
12
+ - `TwoFactorSetupMagicLinkFlow` no longer uses React Router's `useParams`. It reads the token from the URL segment after `/two-factor-setup-magic-link/`, or from a new `token` prop.
13
+
14
+ ### Added
15
+
16
+ - `<PassflowFlow />` with no props works: browser history, prefix from the URL, every screen at a fixed path under it (`/signin`, `/signup`, `/verify-challenge-otp`, `/two-factor-setup-magic-link/:token`, …).
17
+ - Full control: pass `location` and `navigate` together and the app's router owns the location, for example a TanStack Router memory history. Their types are exported as `PassflowFlowLocation` and `PassflowFlowNavigate`.
18
+ - `PassflowFlow` serves the 2FA setup magic link (`/two-factor-setup-magic-link/:token`) as one of its screens.
19
+
20
+ ### Fixed
21
+
22
+ - Forms inside `PassflowFlow` read search params from the flow's location. In memory or hash mode, or under `location` + `navigate`, they read the address bar, which never carried them; forgot-password then threw "Invalid default method param".
23
+
24
+ ### Internal
25
+
26
+ - Tests run the zero-config flow inside React Router, TanStack Router and Wouter (jsdom), and inside a TanStack Router app in Chromium. `@tanstack/react-router` and `wouter` are dev dependencies; `react-router` stays one, for those tests and the docs.
27
+ - The consumer type check installs no router.
28
+
29
+ ## [0.15.1] - 2026-11-02
30
+
31
+ ### Fixed
32
+
33
+ - `PassflowFlow` navigates through its own router whatever the provider's `router` prop says. With the default value, every step inside the flow did a full page load through `window.location` to an unprefixed path, leaving a memory, hash or host router. Hosts that pass `router="react-router"` were not affected.
34
+ - A navigation asked for with `replace: true` inside `PassflowFlow` replaces the history entry. It pushed a new one, because `replace` was handed to React Router as part of the destination.
35
+ - The passwordless OTP screen and the 2FA verify form name the code field with a visible "Verification code" label. They used hidden text only, which fails WCAG labelling (axe `label-title-only`) once a wrong code shows an error.
36
+
37
+ ### Added
38
+
39
+ - `FieldOtp` takes an optional `label`: the field then draws its own visible label above the boxes. `labelledBy` still works; a field takes one or the other. New stories: With label, With label error.
40
+ - `PassflowProps`, the props of `PassflowFlow`, is exported from the package root.
41
+
42
+ ### Changed
43
+
44
+ - `lodash` and `tailwind-merge` are no longer dependencies. lodash is CommonJS, so it could not be tree-shaken and added about 70 KB minified to an app's bundle; tailwind-merge resolved Tailwind class conflicts in a library that styles only with `passflow-*` classes.
45
+
46
+ ### Internal
47
+
48
+ - CI loads the packed package in plain Node through both `require` and `import`, and fails if either throws or the two expose different exports.
49
+ - README examples import from `@passflow/react` instead of the old package name.
50
+ - Two flaky UI tests fixed: the Storybook audits wait for the theme to settle, and the FieldOtp length test no longer depends on keystroke timing.
51
+
5
52
  ## [0.15.0] - 2026-11-02
6
53
 
7
54
  ### Breaking
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # @passflow/passflow-react-sdk
1
+ # @passflow/react
2
2
 
3
3
  This is a SDK for react application.
4
4
 
5
5
  ## Table of Contents
6
6
 
7
- - [@passflow/passflow-react-sdk](#passflowpassflow-react-sdk)
7
+ - [@passflow/react](#passflowreact)
8
8
  - [Table of Contents](#table-of-contents)
9
9
  - [Local Development](#local-development)
10
10
  - [Using Local Passflow JS SDK](#using-local-passflow-js-sdk)
@@ -137,17 +137,15 @@ Both scripts automatically start the demo app and Storybook. `pnpm test` uses th
137
137
  ## Installation
138
138
 
139
139
  ```bash
140
- pnpm add @passflow/react react-router
140
+ pnpm add @passflow/react
141
141
  ```
142
142
 
143
- `react-router` (`^7.0.0 || ^8.0.0`) is a required peer dependency and is not bundled, so install it even when the app routes with Wouter or TanStack Router. `react-router-dom` is not needed: from v7 on, `BrowserRouter` and the rest are exported by `react-router`.
143
+ No router is needed. `PassflowFlow` has its own small router, and works on its own or inside an app routed by React Router, TanStack Router, Wouter or anything else.
144
144
 
145
145
  ## Requirements
146
146
 
147
- - React 18+ (React 19.2.7+ with React Router 8)
148
- - React Router 7 or 8 (React Router 6 is not supported)
147
+ - React 18 or 19
149
148
  - Node `^22.12.0 || ^24.0.0 || >=26.0.0`
150
- - Your app may route with React Router, Wouter or TanStack Router
151
149
 
152
150
  ## Integration
153
151
 
@@ -246,21 +244,63 @@ authentication-context claims those tokens carry.
246
244
 
247
245
  ### PassflowFlow
248
246
 
247
+ The whole sign-in experience on its own pages: sign-in, sign-up, one-time codes, magic links, password reset, invitations and two-factor. It needs no router and no setup:
248
+
249
+ ```tsx
250
+ <PassflowProvider url="https://auth.example.com" appId="your-app-id">
251
+ <PassflowFlow />
252
+ </PassflowProvider>
253
+ ```
254
+
249
255
  | Prop | Type | Description |
250
256
  |------|------|-------------|
251
257
  | successAuthRedirect | string | URL to redirect after successful authentication |
252
- | federatedDisplayMode | "popup" \| "redirect" | Federated authentication display mode |
253
- | pathPrefix | string | Prefix for all routes (optional) |
258
+ | federatedDisplayMode | "modal" \| "redirect" | Federated authentication display mode |
259
+ | pathPrefix | string | Where the flow lives, such as `/auth`. Optional: worked out from the first URL |
260
+ | routerType | "browser" \| "hash" \| "memory" | The history the flow keeps. Default `browser` |
261
+ | location, navigate | see below | Full control by the app's router. Optional, always together |
262
+
263
+ **Paths.** The flow's screens are `<prefix>/signin`, `<prefix>/signup`, `<prefix>/verify-challenge-otp`, `<prefix>/verify-challenge-magic-link`, `<prefix>/forgot-password`, `<prefix>/forgot-password/success`, `<prefix>/password/reset`, `<prefix>/invitation`, `<prefix>/two-factor-verify`, `<prefix>/two-factor-setup` and `<prefix>/two-factor-setup-magic-link/:token`. Without `pathPrefix` the flow takes the prefix from the first URL it sees: `/auth/signin` and `/auth` both give `/auth`, `/` gives none. A path under the prefix that names no screen goes to sign-in.
264
+
265
+ **Inside an app's router.** Mount the flow on a catch-all route; nothing else is needed. The flow moves the address bar and announces it with `popstate`, the event React Router, TanStack Router and Wouter listen to, so the app's router follows the flow and the flow follows the app, Back and Forward included:
266
+
267
+ ```tsx
268
+ // TanStack Router
269
+ const authRoute = createRoute({ getParentRoute: () => rootRoute, path: '/auth/$', component: () => <PassflowFlow /> });
270
+
271
+ // React Router
272
+ <Route path="/auth/*" element={<PassflowFlow />} />
254
273
 
255
- Inside an app that already has a React Router `<Router>`, `PassflowFlow` uses that router rather than its own (`routerType` is ignored). Mount it under a splat route so its own routes can match, and `pathPrefix` is relative to that route:
274
+ // Wouter
275
+ <Route path="/auth" nest><PassflowFlow /></Route>
276
+ ```
277
+
278
+ **`routerType`.** `browser` (default) uses the address bar. `hash` keeps the flow's path after `#`. `memory` leaves the URL alone, for an embedded widget or tests.
279
+
280
+ **Full control.** When the app's router does not use the address bar (it runs in hash or memory mode), hand the flow its location and a way to move it. `routerType` is then not used:
256
281
 
257
282
  ```tsx
258
- <Routes>
259
- <Route path="/" element={<Home />} />
260
- <Route path="/auth/*" element={<PassflowFlow successAuthRedirect="/" />} />
261
- </Routes>
283
+ // TanStack Router with a memory or hash history
284
+ function AuthPage() {
285
+ const router = useRouter();
286
+ const location = useRouterState({ select: (state) => state.location });
287
+ return (
288
+ <PassflowFlow
289
+ location={{ pathname: location.pathname, search: location.searchStr }}
290
+ navigate={(to, { replace }) => {
291
+ const href = `${to.pathname}${to.search}`;
292
+ if (replace) router.history.replace(href);
293
+ else router.history.push(href);
294
+ }}
295
+ />
296
+ );
297
+ }
262
298
  ```
263
299
 
300
+ `TwoFactorSetupMagicLinkFlow` can also be mounted on its own page. It reads the token from the URL (the segment after `/two-factor-setup-magic-link/`), or takes it as a `token` prop.
301
+
302
+ The sections below wire the app's router into the **standalone form components** (`SignIn`, `SignUp`, …) through `PassflowProvider`'s `navigate` and `router` props. `PassflowFlow` does not need them.
303
+
264
304
  ### React Router
265
305
 
266
306
  Example of integration with React Router:
@@ -273,7 +313,7 @@ import {
273
313
  SignUp,
274
314
  ForgotPassword,
275
315
  ForgotPasswordSuccess,
276
- } from "@passflow/passflow-react-sdk";
316
+ } from "@passflow/react";
277
317
  import { BrowserRouter, Routes, Route, useNavigate } from "react-router";
278
318
 
279
319
  const passflowConfig = {
@@ -344,7 +384,7 @@ import {
344
384
  SignUp,
345
385
  ForgotPassword,
346
386
  ForgotPasswordSuccess,
347
- } from "@passflow/passflow-react-sdk";
387
+ } from "@passflow/react";
348
388
  import { Switch, Route, useLocation } from "wouter";
349
389
 
350
390
  const passflowConfig = {
@@ -410,7 +450,7 @@ export const App = () => (
410
450
  );
411
451
 
412
452
  // PassflowProviderWrapper.tsx
413
- import { PassflowProvider } from '@passflow/passflow-react-sdk';
453
+ import { PassflowProvider } from '@passflow/react';
414
454
 
415
455
  const passflowConfig = {
416
456
  url: import.meta.env.VITE_PASSFLOW_URL,
@@ -432,7 +472,7 @@ export const PassflowProviderWrapper = ({ children }) => (
432
472
  );
433
473
 
434
474
  // router/root.tsx
435
- import { useNavigation } from '@passflow/passflow-react-sdk';
475
+ import { useNavigation } from '@passflow/react';
436
476
  import { Outlet, useNavigate } from '@tanstack/react-router';
437
477
 
438
478
  export const Root = () => {
@@ -451,7 +491,7 @@ import { QueryClient } from '@tanstack/react-query';
451
491
  import { createRouter } from '@tanstack/react-router';
452
492
  import { queryClient } from '../query';
453
493
  import { routerTree } from './routes';
454
- import { Passflow } from '@passflow/passflow-react-sdk';
494
+ import { Passflow } from '@passflow/react';
455
495
 
456
496
  export interface RouterContext {
457
497
  queryClient: QueryClient;
@@ -479,7 +519,7 @@ import { Outlet, createRootRouteWithContext, createRoute, redirect } from '@tans
479
519
  import { RouterContext } from './router';
480
520
  import { Root } from './root';
481
521
  import { About, Home } from '@/pages';
482
- import { ForgotPassword, ForgotPasswordSuccess, SignIn, SignUp } from '@passflow/passflow-react-sdk';
522
+ import { ForgotPassword, ForgotPasswordSuccess, SignIn, SignUp } from '@passflow/react';
483
523
  import { RootLayout } from '@/layouts';
484
524
 
485
525
  const redirectToSignin = () => {