@alxia/react-router 0.24.1 → 0.26.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/README.md CHANGED
@@ -1028,6 +1028,22 @@ export const action = events.action; // POST
1028
1028
  See the [subscription guide](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/guide/subscriptions.md)
1029
1029
  for the rules, the options and the errors.
1030
1030
 
1031
+ ## A UI library's links through React Router
1032
+
1033
+ A design system that renders its links through a component of its own (a
1034
+ `LinkComponentProvider`) hands it an `href`, while React Router's `Link` takes
1035
+ `to`. `@alxia/react-router/link` (`react` and `react-router` are its only
1036
+ imports) exports the adapter, so no app copies it:
1037
+
1038
+ ```tsx
1039
+ // app/root.tsx
1040
+ import { RouterLink } from '@alxia/react-router/link';
1041
+
1042
+ <LinkComponentProvider component={RouterLink}>
1043
+ <Outlet />
1044
+ </LinkComponentProvider>
1045
+ ```
1046
+
1031
1047
  ## A list whose state is the URL
1032
1048
 
1033
1049
  `@alxia/react-router/lists` (`react` and `react-router` are its only imports; every hook renders on the server)
@@ -1238,9 +1254,9 @@ From `@alxia/react-router/session` (`react-router` and `/errors`' files are its
1238
1254
 
1239
1255
  | export | |
1240
1256
  | --- | --- |
1241
- | `sessionCookies({ session, challenge, secure, challengePath?, challengeSeconds? })`, `SessionCookies`, `SessionCookiesOptions`, `CookieSession` | the session and second-factor challenge cookies, `HttpOnly; SameSite=Lax`, `__Host-` and `__Secure-` when `secure` (required, never read from the environment): `commit(token, expiresAt)`, `read(request)`, `token(request)`, `destroy()`, `refreshed(session, expiresAt)` (a `Set-Cookie` only when the expiry moved), `commitChallenge`, `challengeOf`, `destroyChallenge`, `signInPageHeaders(deadSession)` and `names` (the cookie names as sent, prefixes included); a cookie written by an app's own code in `<expiry ms>.<token>` is read as it is |
1257
+ | `sessionCookies({ session, challenge?, secure, challengePath?, challengeSeconds? })`, `SessionCookies`, `SessionCookiesOptions`, `SessionCookiesWithoutChallengeOptions`, `SessionCookiesBase`, `ChallengeCookies`, `NoChallengeCookies`, `CookieSession` | the session and, when `challenge` is given, second-factor challenge cookies (without it the result is `SessionCookies<false>`, with no challenge helper on the type), `HttpOnly; SameSite=Lax`, `__Host-` and `__Secure-` when `secure` (required, never read from the environment): `commit(token, expiresAt)`, `read(request)`, `token(request)`, `destroy()`, `refreshed(session, expiresAt)` (a `Set-Cookie` only when the expiry moved), `commitChallenge`, `challengeOf`, `destroyChallenge` (these three: with a second factor), `signInPageHeaders(deadSession)` and `names` (the cookie names as sent, prefixes included); a cookie written by an app's own code in `<expiry ms>.<token>` is read as it is |
1242
1258
  | `serializeCookie(name, value, { maxAge, path?, secure })`, `readCookie(request, name)`, `secondsUntil(expiresAt, now?)`, `SerializeOptions` | the bytes `sessionCookies` writes, for the cookies of your own; a name that is not an RFC 6265 token throws a `TypeError` |
1243
- | `signInFlow({ cookies, errors, json, revoke, attemptsLeft, paths?, clear?, unavailable?, codes?, endedParam? })`, `SignInFlow`, `SignInFlowOptions` | the server side of the sign-in pages, which also exposes what it was built with (`cookies`, `errors`, `json`, `attemptsLeft`, `unavailable(language)`, `clear()`) and `guard(request)`, the check an app's own POST step runs first: `nextParam`, `signInRedirect`, `landing`, `landReplacing` (revokes the session the browser held), `signInStep` (a same-site JSON post only; an API refusal as the form's message and status, an unreachable API as a 503), `readCodeInput`, `codeStep`, `codeRefusal` (with the tries left), `revokeSession`, `signOut` (JSON and same-origin only; it clears the challenge cookie too), `viewer` (`null` for a 401, `undefined` when unknown) and `requireViewer` (throws the sign-in redirect); `revoke(token, request, context)` gets the caller context (`C`, inferred from the annotation of `revoke`'s third parameter) that `signOut`, `revokeSession`, `landReplacing` and `signInStep` take as `context` |
1259
+ | `signInFlow({ cookies, errors, json, revoke, attemptsLeft?, paths?, clear?, unavailable?, codes?, endedParam? })`, `SignInFlow`, `SignInFlowOptions`, `SignInFlowWithoutSecondFactorOptions`, `SignInFlowBase`, `SecondFactorSteps` | the server side of the sign-in pages; `attemptsLeft` and cookies with a `challenge` go together (giving only one is a compile error, and a `TypeError` at runtime), and without them the flow is `SignInFlow<L, C, false>`, with no `readCodeInput`, `codeStep`, `codeRefusal` or `attemptsLeft`. It also exposes what it was built with (`cookies`, `errors`, `json`, `attemptsLeft`, `unavailable(language)`, `clear()`) and `guard(request)`, the check an app's own POST step runs first: `nextParam`, `signInRedirect`, `landing`, `landReplacing` (revokes the session the browser held), `signInStep` (a same-site JSON post only; an API refusal as the form's message and status, an unreachable API as a 503), `readCodeInput`, `codeStep`, `codeRefusal` (with the tries left) (these four and `attemptsLeft`: with a second factor), `revokeSession`, `signOut` (JSON and same-origin only; it clears the challenge cookie too), `viewer` (`null` for a 401, `undefined` when unknown) and `requireViewer` (throws the sign-in redirect); `revoke(token, request, context)` gets the caller context (`C`, inferred from the annotation of `revoke`'s third parameter) that `signOut`, `revokeSession`, `landReplacing` and `signInStep` take as `context` |
1244
1260
  | `SignInAnswer`, `SignInFormError`, `SignInStayed`, `SignInCodes` | what a step answers (`signedIn`, `secondFactor` or `other`), a form step's answer when it stays on the page, and the `codes` option (`invalidCode`, `challengeOver`) |
1245
1261
  | `stepUpSteps({ signIn, spent, locked?, wording?, codes? })`, `StepUpSteps`, `StepUpStepsOptions`, `StepUpCall` | a step-up for a route's action, reading the flow's `errors`, `json` and `attemptsLeft`, each step taking `{ request, language }` and refusing a post that is not a same-site JSON one: `request(run, { request, language })` answers the challenge and how it is confirmed (a throttled mail or any 429 is `error: 'throttled'` with `retryAfter`), `confirm(run, { request, language, via? })` answers `error: 'invalid'` with the tries left (and `locked: true` for an app or recovery code with none left), `expired` or `failed`; `request` and `confirm` answer `throttled` with `retryAfter` on any 429 (on `confirm` the challenge and its tries stay, `locked: false`); and `isStepUpRequired(error)` |
1246
1262
  | `StepUpCodes`, `StepUpStayed`, `INVALID_CODE`, `CHALLENGE_OVER`, `MAIL_THROTTLED`, `STEP_UP_REQUIRED`, `STEP_UP_OVER` | the `codes` option (`invalidCode`, `challengeOver`, `stepUpRequired`, `mailThrottled`), a step's answer that stays on the page, and janus's default codes, which an app can extend |
@@ -1306,6 +1322,12 @@ From `@alxia/react-router/lists` (`react` and `react-router` are its only import
1306
1322
  | `usePagedRows(firstPage, rowsOf, { cursorParam?, nextOf? }?)`, `PagedRows`, `PagedRowsOptions` | `{ rows, next, loading, error, loadMore }`: appends the pages the same route answers to `?<cursorParam>=<next>` (default `after`, `nextOf` default `page.next`) through `useFetcher`; starts over when `firstPage` is another object; a failure keeps the rows and sets `error` (an answer that is not a page), and data that is not a page is ignored |
1307
1323
  | `orFailed(after, run)`, `FAILED`, `isFailedPage(value)`, `FailedPage` | the loader half of `usePagedRows`: `run()`'s answer; an error is rethrown when `after` is empty, and with an `after` it answers `FAILED` (`{ failed: true }`, typed `Promise<T \| FailedPage>`), except a 3xx `Response`, which is rethrown. `isFailedPage` narrows an answer |
1308
1324
 
1325
+ From `@alxia/react-router/link` (`react` and `react-router` are its only imports; it renders on the server):
1326
+
1327
+ | export | |
1328
+ | --- | --- |
1329
+ | `RouterLink`, `RouterLinkProps` | React Router's `Link` as a UI library's link component: takes `href` (or `to`), spreads `className`, `children`, `ref` and every anchor prop onto the `<a>`, keeps `Link`'s own props (`replace`, `prefetch`, `reloadDocument`, ...) |
1330
+
1309
1331
  From `@alxia/react-router/subscription` (no import, no peer):
1310
1332
 
1311
1333
  | export | |
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `@alxia/react-router/link`: React Router's `Link` as the link component of
3
+ * a UI library. `react` and `react-router` are its only imports, and it
4
+ * renders on the server.
5
+ */
6
+ export { RouterLink, type RouterLinkProps } from './router-link';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/link/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,16 @@
1
+ // src/link/router-link.ts
2
+ import { createElement } from "react";
3
+ import { Link } from "react-router";
4
+ function RouterLink({
5
+ href,
6
+ to,
7
+ ...props
8
+ }) {
9
+ return createElement(Link, { ...props, to: href ?? to });
10
+ }
11
+ export {
12
+ RouterLink
13
+ };
14
+
15
+ //# debugId=D9C7D4DF88CF5C0964756E2164756E21
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/link/router-link.ts"],
4
+ "sourcesContent": [
5
+ "import { createElement, type ReactElement, type RefAttributes } from 'react';\nimport { Link, type LinkProps, type To } from 'react-router';\n\n/**\n * The props of {@link RouterLink}: React Router's `Link` props, with the\n * destination given as `href` (what a design system's link component hands\n * over) or as `to` (React Router's own name), never both.\n */\nexport type RouterLinkProps = Omit<LinkProps, 'to'> &\n\tRefAttributes<HTMLAnchorElement> &\n\t({ href: To; to?: undefined } | { to: To; href?: undefined });\n\n/**\n * React Router's `Link` for a UI library that renders its links through a\n * component of its own (a `LinkComponentProvider`): it receives `href` and\n * spreads every other prop (`className`, `children`, `aria-*`, `ref`, ...)\n * onto the anchor. Navigation stays client-side and `<Link>`'s own props\n * (`prefetch`, `replace`, `reloadDocument`, ...) still apply.\n *\n * ```tsx\n * <LinkComponentProvider component={RouterLink}>...</LinkComponentProvider>\n * ```\n */\nexport function RouterLink({\n\thref,\n\tto,\n\t...props\n}: RouterLinkProps): ReactElement {\n\t// The union guarantees one of the two is set; TypeScript cannot see it\n\t// through the destructuring, hence the cast.\n\treturn createElement(Link, { ...props, to: (href ?? to) as To });\n}\n"
6
+ ],
7
+ "mappings": ";AAAA;AACA;AAsBO,SAAS,UAAU;AAAA,EACzB;AAAA,EACA;AAAA,KACG;AAAA,GAC8B;AAAA,EAGjC,OAAO,cAAc,MAAM,KAAK,OAAO,IAAK,QAAQ,GAAU,CAAC;AAAA;",
8
+ "debugId": "D9C7D4DF88CF5C0964756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,27 @@
1
+ import { type ReactElement, type RefAttributes } from 'react';
2
+ import { type LinkProps, type To } from 'react-router';
3
+ /**
4
+ * The props of {@link RouterLink}: React Router's `Link` props, with the
5
+ * destination given as `href` (what a design system's link component hands
6
+ * over) or as `to` (React Router's own name), never both.
7
+ */
8
+ export type RouterLinkProps = Omit<LinkProps, 'to'> & RefAttributes<HTMLAnchorElement> & ({
9
+ href: To;
10
+ to?: undefined;
11
+ } | {
12
+ to: To;
13
+ href?: undefined;
14
+ });
15
+ /**
16
+ * React Router's `Link` for a UI library that renders its links through a
17
+ * component of its own (a `LinkComponentProvider`): it receives `href` and
18
+ * spreads every other prop (`className`, `children`, `aria-*`, `ref`, ...)
19
+ * onto the anchor. Navigation stays client-side and `<Link>`'s own props
20
+ * (`prefetch`, `replace`, `reloadDocument`, ...) still apply.
21
+ *
22
+ * ```tsx
23
+ * <LinkComponentProvider component={RouterLink}>...</LinkComponentProvider>
24
+ * ```
25
+ */
26
+ export declare function RouterLink({ href, to, ...props }: RouterLinkProps): ReactElement;
27
+ //# sourceMappingURL=router-link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router-link.d.ts","sourceRoot":"","sources":["../../src/link/router-link.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAC7E,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,EAAE,EAAE,MAAM,cAAc,CAAC;AAE7D;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,GAClD,aAAa,CAAC,iBAAiB,CAAC,GAChC,CAAC;IAAE,IAAI,EAAE,EAAE,CAAC;IAAC,EAAE,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAE/D;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,EAC1B,IAAI,EACJ,EAAE,EACF,GAAG,KAAK,EACR,EAAE,eAAe,GAAG,YAAY,CAIhC"}
@@ -1,20 +1,30 @@
1
- export interface SessionCookiesOptions {
1
+ /** What every `sessionCookies` call takes, with or without a second factor. */
2
+ export interface SessionCookiesBaseOptions {
2
3
  /**
3
4
  * The base name of the session cookie, e.g. `'my-app_session'`. With
4
5
  * `secure`, the cookie is named `__Host-my-app_session`.
5
6
  */
6
7
  readonly session: string;
7
- /**
8
- * The base name of the second-factor challenge's cookie. With `secure`, it
9
- * is named `__Secure-<name>`.
10
- */
11
- readonly challenge: string;
12
8
  /**
13
9
  * The app is served over HTTPS. Required, and never read from the
14
10
  * environment: pass `!import.meta.env.DEV`. `false` drops `Secure` and the
15
11
  * prefixes (Safari drops a `Secure` cookie over `http://localhost`).
16
12
  */
17
13
  readonly secure: boolean;
14
+ }
15
+ /** The options of an app with no second factor: no challenge cookie. */
16
+ export interface SessionCookiesWithoutChallengeOptions extends SessionCookiesBaseOptions {
17
+ readonly challenge?: undefined;
18
+ readonly challengePath?: undefined;
19
+ readonly challengeSeconds?: undefined;
20
+ }
21
+ /** The options of an app with a second factor: the challenge cookie's name is given. */
22
+ export interface SessionCookiesOptions extends SessionCookiesBaseOptions {
23
+ /**
24
+ * The base name of the second-factor challenge's cookie. With `secure`, it
25
+ * is named `__Secure-<name>`.
26
+ */
27
+ readonly challenge: string;
18
28
  /** Where the browser sends the challenge. `'/sign-in'` by default. */
19
29
  readonly challengePath?: string | undefined;
20
30
  /** The most the challenge lives. 300, `@nxgt/janus`'s lifetime, by default. */
@@ -25,11 +35,11 @@ export interface CookieSession {
25
35
  readonly token: string;
26
36
  readonly expiresAt: number;
27
37
  }
28
- export interface SessionCookies {
38
+ /** The cookies every BFF has: the session's. */
39
+ export interface SessionCookiesBase {
29
40
  /** The names as sent, prefixes included. */
30
41
  readonly names: {
31
42
  readonly session: string;
32
- readonly challenge: string;
33
43
  };
34
44
  /** The session cookie of the request; `null` when absent or not `<ms>.<token>`. */
35
45
  read(request: Request): CookieSession | null;
@@ -41,20 +51,56 @@ export interface SessionCookies {
41
51
  destroy(): string;
42
52
  /** The `Set-Cookie` header that keeps the cookie as long as the session lives; empty when the expiry did not move, or is absent or not a date. */
43
53
  refreshed(session: CookieSession, expiresAt: string | Date | null): HeadersInit;
54
+ /**
55
+ * What a signed-out visit of the sign-in page sets: a dead session's
56
+ * cookie when `deadSession` (and, with a second factor, the challenge
57
+ * cleared blind: a client navigation fetches `/sign-in.data`, which the
58
+ * cookie's path does not match, so the request never shows it).
59
+ */
60
+ signInPageHeaders(deadSession: boolean): Headers;
61
+ }
62
+ /** The challenge cookie's helpers, present when `challenge` was given. */
63
+ export interface ChallengeCookies {
64
+ readonly names: {
65
+ readonly challenge: string;
66
+ };
44
67
  /** The challenge cookie of the request, or `null`. */
45
68
  challengeOf(request: Request): string | null;
46
69
  /** A `Set-Cookie` value keeping the challenge for the code page. */
47
70
  commitChallenge(challenge: string, expiresAt?: string | Date | null, now?: number): string;
48
71
  /** A `Set-Cookie` value removing the challenge. */
49
72
  destroyChallenge(): string;
50
- /**
51
- * What a signed-out visit of the sign-in page sets: the challenge cleared
52
- * blind (a client navigation fetches `/sign-in.data`, which the cookie's
53
- * path does not match, so the request never shows it), and a dead
54
- * session's cookie when `deadSession`.
55
- */
56
- signInPageHeaders(deadSession: boolean): Headers;
57
73
  }
58
- /** The cookies of a BFF's session and of its second-factor challenge. */
59
- export declare function sessionCookies(options: SessionCookiesOptions): SessionCookies;
74
+ /**
75
+ * The cookies `sessionCookies` returns: with the challenge helpers (the
76
+ * default) when `challenge` was given, `SessionCookies<false>` without.
77
+ */
78
+ export type SessionCookies<WithChallenge extends boolean = true> = WithChallenge extends true ? SessionCookiesBase & ChallengeCookies : SessionCookiesBase & NoChallengeCookies;
79
+ /**
80
+ * What `SessionCookies<false>` says of the challenge helpers: they are
81
+ * absent. Declared as `undefined` so that a set with a challenge is not
82
+ * assignable to it (a flow with no second factor refuses one at compile time).
83
+ */
84
+ export interface NoChallengeCookies {
85
+ readonly challengeOf?: undefined;
86
+ readonly commitChallenge?: undefined;
87
+ readonly destroyChallenge?: undefined;
88
+ }
89
+ /**
90
+ * The cookies of a BFF's session and, when `challenge` is given, of its
91
+ * second-factor challenge. Without `challenge`, the result has no challenge
92
+ * helpers and the type says so.
93
+ *
94
+ * @example
95
+ * const cookies = sessionCookies({ session: 'my-app_session', secure: true });
96
+ */
97
+ export declare function sessionCookies(options: SessionCookiesOptions): SessionCookies<true>;
98
+ export declare function sessionCookies(options: SessionCookiesWithoutChallengeOptions): SessionCookies<false>;
99
+ export declare function sessionCookies(options: SessionCookiesBaseOptions & {
100
+ readonly challenge?: string | undefined;
101
+ readonly challengePath?: string | undefined;
102
+ readonly challengeSeconds?: number | undefined;
103
+ }): SessionCookies<boolean>;
104
+ /** The `Set-Cookie` values that clear the challenge: none without a second factor. */
105
+ export declare function challengeCleared(cookies: SessionCookies<boolean>): string[];
60
106
  //# sourceMappingURL=cookies.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/session/cookies.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,qBAAqB;IACrC;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,+EAA+E;IAC/E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C;AAED,wFAAwF;AACxF,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC9B,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACzE,mFAAmF;IACnF,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAAC;IAC7C,mDAAmD;IACnD,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;IACvC,8DAA8D;IAC9D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtE,wDAAwD;IACxD,OAAO,IAAI,MAAM,CAAC;IAClB,kJAAkJ;IAClJ,SAAS,CACR,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAC7B,WAAW,CAAC;IACf,sDAAsD;IACtD,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;IAC7C,oEAAoE;IACpE,eAAe,CACd,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,EAChC,GAAG,CAAC,EAAE,MAAM,GACV,MAAM,CAAC;IACV,mDAAmD;IACnD,gBAAgB,IAAI,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,iBAAiB,CAAC,WAAW,EAAE,OAAO,GAAG,OAAO,CAAC;CACjD;AAiBD,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CA6E7E"}
1
+ {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/session/cookies.ts"],"names":[],"mappings":"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"}
@@ -32,8 +32,7 @@ export interface SignInCodes {
32
32
  /** The codes that end the challenge (`TOKEN_UNKNOWN`, `TOKEN_SPENT`, `TOKEN_EXPIRED`). */
33
33
  readonly challengeOver?: readonly string[] | undefined;
34
34
  }
35
- export interface SignInFlowOptions<L extends string, C = undefined> {
36
- readonly cookies: SessionCookies;
35
+ export interface SignInFlowBaseOptions<L extends string, C = undefined> {
37
36
  /** The app's `createApiErrors` instance. */
38
37
  readonly errors: ApiErrors<L>;
39
38
  /** The app's `jsonActions({ origin })`, so a body is read with the same CSRF check. */
@@ -45,8 +44,6 @@ export interface SignInFlowOptions<L extends string, C = undefined> {
45
44
  * cannot carry. `undefined` when the step got none.
46
45
  */
47
46
  revoke(token: string, request: Request, context: C | undefined): Promise<unknown>;
48
- /** A refused code's tries left, worded ("3 tries left"). */
49
- attemptsLeft(count: number, language: L): string;
50
47
  /** The sign-in pages' paths; `SIGN_IN_PATHS` by default. */
51
48
  readonly paths?: Partial<SignInPaths> | undefined;
52
49
  /**
@@ -63,17 +60,65 @@ export interface SignInFlowOptions<L extends string, C = undefined> {
63
60
  */
64
61
  readonly endedParam?: string | undefined;
65
62
  }
63
+ /**
64
+ * The options of a flow with a second factor: the cookies carry the
65
+ * challenge (`sessionCookies({ challenge })`) and `attemptsLeft` words the
66
+ * tries left.
67
+ */
68
+ export interface SignInFlowOptions<L extends string, C = undefined> extends SignInFlowBaseOptions<L, C> {
69
+ readonly cookies: SessionCookies<true>;
70
+ /** A refused code's tries left, worded ("3 tries left"). */
71
+ attemptsLeft(count: number, language: L): string;
72
+ }
73
+ /**
74
+ * The options of a flow with no second factor: cookies built without
75
+ * `challenge` and no `attemptsLeft`. The flow then offers no second-factor
76
+ * step.
77
+ */
78
+ export interface SignInFlowWithoutSecondFactorOptions<L extends string, C = undefined> extends SignInFlowBaseOptions<L, C> {
79
+ readonly cookies: SessionCookies<false>;
80
+ readonly attemptsLeft?: undefined;
81
+ }
66
82
  /** A form step's answer when it stays on the page: the form's message, with a status. */
67
83
  export type SignInStayed<T extends object> = ReturnType<typeof data<SignInFormError<T>>>;
68
- export interface SignInFlow<L extends string, C = undefined> {
84
+ /** The members of a flow built with a second factor, on top of the common ones. */
85
+ export interface SecondFactorSteps<L extends string> {
86
+ /** A refused code's tries left, worded (the `attemptsLeft` option). */
87
+ attemptsLeft(count: number, language: L): string;
88
+ /**
89
+ * A refused code's message with the tries left; `undefined` when the
90
+ * challenge is over (sign in again). With no try left, `whenNone` is
91
+ * added to the message instead, when given.
92
+ */
93
+ codeRefusal(error: ApiErrorLike, language: L, whenNone?: string): string | undefined;
94
+ /** The code a second-factor page posted, with how it is given; a 400 otherwise. */
95
+ readCodeInput(request: Request): Promise<{
96
+ kind: SecondFactorKind;
97
+ code: string;
98
+ }>;
99
+ /** The code against the challenge: `confirm` for an issued code, `recover` for a recovery code. */
100
+ codeStep(input: {
101
+ readonly kind: SecondFactorKind;
102
+ readonly code: string;
103
+ }, challenge: string, steps: {
104
+ confirm(challenge: string, code: string): Promise<SignInAnswer>;
105
+ recover(challenge: string, code: string): Promise<SignInAnswer>;
106
+ }): Promise<SignInAnswer>;
107
+ }
108
+ /**
109
+ * The flow `signInFlow` returns. `SignInFlow<L, C>` has a second factor (the
110
+ * default, as before); `SignInFlow<L, C, false>` has none, and no
111
+ * second-factor member (`attemptsLeft`, `codeRefusal`, `readCodeInput`,
112
+ * `codeStep`) is on its type.
113
+ */
114
+ export type SignInFlow<L extends string, C = undefined, WithSecondFactor extends boolean = true> = SignInFlowBase<L, C, WithSecondFactor> & (WithSecondFactor extends true ? SecondFactorSteps<L> : unknown);
115
+ export interface SignInFlowBase<L extends string, C = undefined, WithSecondFactor extends boolean = true> {
69
116
  /** The cookies the flow was built with. */
70
- readonly cookies: SessionCookies;
117
+ readonly cookies: SessionCookies<WithSecondFactor>;
71
118
  /** The `createApiErrors` instance the flow was built with. */
72
119
  readonly errors: ApiErrors<L>;
73
120
  /** The `jsonActions` instance the flow was built with. */
74
121
  readonly json: JsonActions;
75
- /** A refused code's tries left, worded (the `attemptsLeft` option). */
76
- attemptsLeft(count: number, language: L): string;
77
122
  /**
78
123
  * The guard every step of the flow that signs in, out, spends or mails
79
124
  * runs first, for an app's own step (a passkey's): a native form post is
@@ -123,25 +168,6 @@ export interface SignInFlow<L extends string, C = undefined> {
123
168
  /** The caller context handed to `revoke` when the answer replaces a session. */
124
169
  readonly context?: C | undefined;
125
170
  }): Promise<Response | SignInStayed<T>>;
126
- /**
127
- * A refused code's message with the tries left; `undefined` when the
128
- * challenge is over (sign in again). With no try left, `whenNone` is
129
- * added to the message instead, when given.
130
- */
131
- codeRefusal(error: ApiErrorLike, language: L, whenNone?: string): string | undefined;
132
- /** The code a second-factor page posted, with how it is given; a 400 otherwise. */
133
- readCodeInput(request: Request): Promise<{
134
- kind: SecondFactorKind;
135
- code: string;
136
- }>;
137
- /** The code against the challenge: `confirm` for an issued code, `recover` for a recovery code. */
138
- codeStep(input: {
139
- readonly kind: SecondFactorKind;
140
- readonly code: string;
141
- }, challenge: string, steps: {
142
- confirm(challenge: string, code: string): Promise<SignInAnswer>;
143
- recover(challenge: string, code: string): Promise<SignInAnswer>;
144
- }): Promise<SignInAnswer>;
145
171
  /**
146
172
  * Ends the request's session at the API; true when it carried one.
147
173
  * `context` is handed to `revoke`.
@@ -177,7 +203,7 @@ export interface SignInFlow<L extends string, C = undefined> {
177
203
  }
178
204
  /** The options once resolved, as the flow's functions take them. */
179
205
  export interface FlowContext<L extends string, C = undefined> {
180
- readonly options: SignInFlowOptions<L, C>;
206
+ readonly options: SignInFlowOptions<L, C> | SignInFlowWithoutSecondFactorOptions<L, C>;
181
207
  readonly paths: SignInPaths;
182
208
  readonly invalidCode: string;
183
209
  readonly challengeOver: readonly string[];
@@ -1 +1 @@
1
- {"version":3,"file":"flow-types.d.ts","sourceRoot":"","sources":["../../src/session/flow-types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"flow-types.d.ts","sourceRoot":"","sources":["../../src/session/flow-types.ts"],"names":[],"mappings":"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"}
@@ -5,8 +5,8 @@
5
5
  * nothing else.
6
6
  */
7
7
  export { CHALLENGE_OVER, INVALID_CODE, MAIL_THROTTLED } from './codes';
8
- export { type CookieSession, type SessionCookies, type SessionCookiesOptions, sessionCookies, } from './cookies';
9
- export type { SignInAnswer, SignInCodes, SignInFlow, SignInFlowOptions, SignInFormError, SignInStayed, } from './flow-types';
8
+ export { type ChallengeCookies, type CookieSession, type NoChallengeCookies, type SessionCookies, type SessionCookiesBase, type SessionCookiesOptions, type SessionCookiesWithoutChallengeOptions, sessionCookies, } from './cookies';
9
+ export type { SecondFactorSteps, SignInAnswer, SignInCodes, SignInFlow, SignInFlowBase, SignInFlowOptions, SignInFlowWithoutSecondFactorOptions, SignInFormError, SignInStayed, } from './flow-types';
10
10
  export { TOKEN_PAGE_HEADERS } from './link-token';
11
11
  export type { AuthLinks, AuthLinksCodes, AuthLinksOptions, LinkStayed, RequestLinkAnswer, SpendOptions, Spent, } from './link-types';
12
12
  export { authLinks } from './links';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/session/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,cAAc,GACd,MAAM,WAAW,CAAC;AACnB,YAAY,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,YAAY,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,YAAY,EACX,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,YAAY,EACZ,KAAK,GACL,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EACN,UAAU,EACV,KAAK,gBAAgB,EACrB,YAAY,EACZ,eAAe,GACf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACjE,YAAY,EACX,UAAU,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EACX,kBAAkB,GAClB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/session/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACvE,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,qCAAqC,EAC1C,cAAc,GACd,MAAM,WAAW,CAAC;AACnB,YAAY,EACX,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,oCAAoC,EACpC,eAAe,EACf,YAAY,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,YAAY,EACX,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,YAAY,EACZ,KAAK,GACL,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EACN,UAAU,EACV,KAAK,gBAAgB,EACrB,YAAY,EACZ,eAAe,GACf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACjE,YAAY,EACX,UAAU,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EACX,kBAAkB,GAClB,MAAM,iBAAiB,CAAC"}
@@ -87,7 +87,8 @@ function sessionCookies(options) {
87
87
  challengeSeconds = 5 * 60
88
88
  } = options;
89
89
  checkName("session", options.session);
90
- checkName("challenge", options.challenge);
90
+ if (options.challenge !== undefined)
91
+ checkName("challenge", options.challenge);
91
92
  if (!Number.isInteger(challengeSeconds) || challengeSeconds <= 0) {
92
93
  throw new TypeError(`sessionCookies(): challengeSeconds must be a positive integer; got ${String(challengeSeconds)}.`);
93
94
  }
@@ -95,7 +96,7 @@ function sessionCookies(options) {
95
96
  throw new TypeError(`sessionCookies(): challengePath must start with "/" and hold no ";" or control character; got ${JSON.stringify(challengePath)}.`);
96
97
  }
97
98
  const session = secure ? `__Host-${options.session}` : options.session;
98
- const challenge = secure ? `__Secure-${options.challenge}` : options.challenge;
99
+ const challengeName = options.challenge === undefined ? undefined : secure ? `__Secure-${options.challenge}` : options.challenge;
99
100
  const clear = (name, path) => serializeCookie(name, "", { maxAge: 0, secure, ...path && { path } });
100
101
  const read = (request) => {
101
102
  const value = readCookie(request, session);
@@ -111,8 +112,8 @@ function sessionCookies(options) {
111
112
  maxAge: secondsUntil(expiresAt, now),
112
113
  secure
113
114
  });
114
- return {
115
- names: { session, challenge },
115
+ const base = {
116
+ names: { session },
116
117
  read,
117
118
  token: (request) => read(request)?.token ?? null,
118
119
  commit,
@@ -123,21 +124,36 @@ function sessionCookies(options) {
123
124
  return {};
124
125
  return { "Set-Cookie": commit(current.token, expiresAt) };
125
126
  },
126
- challengeOf: (request) => readCookie(request, challenge),
127
- commitChallenge: (value, expiresAt, now) => serializeCookie(challenge, value, {
128
- maxAge: Math.min(challengeSeconds, expiresAt ? secondsUntil(expiresAt, now) : challengeSeconds),
129
- path: challengePath,
130
- secure
131
- }),
132
- destroyChallenge: () => clear(challenge, challengePath),
133
127
  signInPageHeaders(deadSession) {
134
128
  const headers = new Headers;
135
- headers.append("Set-Cookie", clear(challenge, challengePath));
129
+ if (challengeName) {
130
+ headers.append("Set-Cookie", clear(challengeName, challengePath));
131
+ }
136
132
  if (deadSession)
137
133
  headers.append("Set-Cookie", clear(session));
138
134
  return headers;
139
135
  }
140
136
  };
137
+ if (challengeName === undefined)
138
+ return base;
139
+ const challengeCookies = {
140
+ names: { challenge: challengeName },
141
+ challengeOf: (request) => readCookie(request, challengeName),
142
+ commitChallenge: (value, expiresAt, now) => serializeCookie(challengeName, value, {
143
+ maxAge: Math.min(challengeSeconds, expiresAt ? secondsUntil(expiresAt, now) : challengeSeconds),
144
+ path: challengePath,
145
+ secure
146
+ }),
147
+ destroyChallenge: () => clear(challengeName, challengePath)
148
+ };
149
+ return {
150
+ ...base,
151
+ ...challengeCookies,
152
+ names: { session, challenge: challengeName }
153
+ };
154
+ }
155
+ function challengeCleared(cookies) {
156
+ return cookies.destroyChallenge ? [cookies.destroyChallenge()] : [];
141
157
  }
142
158
  // src/session/link-token.ts
143
159
  import { data, replace } from "react-router";
@@ -228,13 +244,15 @@ function landing({ options, paths }, answer, next) {
228
244
  if (isLive(answer)) {
229
245
  const headers = new Headers;
230
246
  headers.append("Set-Cookie", cookies.commit(answer.token, answer.expiresAt));
231
- headers.append("Set-Cookie", cookies.destroyChallenge());
247
+ for (const cookie of challengeCleared(cookies)) {
248
+ headers.append("Set-Cookie", cookie);
249
+ }
232
250
  for (const cookie of options.clear?.() ?? []) {
233
251
  headers.append("Set-Cookie", cookie);
234
252
  }
235
253
  return redirect(safeNext2(next), { headers });
236
254
  }
237
- if (answer.status === "secondFactor" && answer.challenge) {
255
+ if (answer.status === "secondFactor" && answer.challenge && cookies.commitChallenge) {
238
256
  return redirect(secondFactorPath2(next, paths.secondFactor), {
239
257
  headers: {
240
258
  "Set-Cookie": cookies.commitChallenge(answer.challenge, answer.expiresAt)
@@ -298,7 +316,9 @@ async function afterReset(options, resetDone, request, { context } = {}) {
298
316
  await options.signIn.revokeSession(request, { context });
299
317
  const headers = withTokenHeaders();
300
318
  headers.append("Set-Cookie", options.cookies.destroy());
301
- headers.append("Set-Cookie", options.cookies.destroyChallenge());
319
+ for (const cookie of challengeCleared(options.cookies)) {
320
+ headers.append("Set-Cookie", cookie);
321
+ }
302
322
  for (const cookie of options.signIn.clear()) {
303
323
  headers.append("Set-Cookie", cookie);
304
324
  }
@@ -475,7 +495,9 @@ async function signOut(flow, request, {
475
495
  await revokeSession(flow, request, { context });
476
496
  const headers = new Headers;
477
497
  headers.append("Set-Cookie", options.cookies.destroy());
478
- headers.append("Set-Cookie", options.cookies.destroyChallenge());
498
+ for (const cookie of challengeCleared(options.cookies)) {
499
+ headers.append("Set-Cookie", cookie);
500
+ }
479
501
  for (const cookie of options.clear?.() ?? []) {
480
502
  headers.append("Set-Cookie", cookie);
481
503
  }
@@ -484,6 +506,9 @@ async function signOut(flow, request, {
484
506
 
485
507
  // src/session/sign-in.ts
486
508
  function resolve(options) {
509
+ if (options.attemptsLeft === undefined !== !options.cookies.commitChallenge) {
510
+ throw new TypeError("signInFlow(): a second factor needs both the challenge cookie (sessionCookies({ challenge })) and attemptsLeft; without one, give neither.");
511
+ }
487
512
  const paths = { ...SIGN_IN_PATHS2, ...options.paths };
488
513
  for (const key of ["signIn", "secondFactor"]) {
489
514
  sitePath(paths[key], { label: `signInFlow(): paths.${key}` });
@@ -501,27 +526,33 @@ function resolve(options) {
501
526
  function signInFlow(options) {
502
527
  const context = resolve(options);
503
528
  const { options: o } = context;
504
- return {
529
+ const base = {
505
530
  cookies: o.cookies,
506
531
  errors: o.errors,
507
532
  json: o.json,
508
533
  unavailable: (language) => unavailableMessage(o, language),
509
534
  clear: () => o.clear?.() ?? [],
510
- attemptsLeft: o.attemptsLeft,
511
535
  guard: (request) => samesitePost(o.json, request),
512
536
  nextParam,
513
537
  signInRedirect: (request) => signInRedirect(context, request),
514
538
  landing: (answer, next) => landing(context, answer, next),
515
539
  landReplacing: (answer, next, o) => landReplacing(context, answer, next, o),
516
540
  signInStep: (run, o) => signInStep(context, run, o),
517
- codeRefusal: (error, language, whenNone) => codeRefusal(context, error, language, whenNone),
518
- readCodeInput: (request) => readCodeInput(context, request),
519
- codeStep,
520
541
  revokeSession: (request, o) => revokeSession(context, request, o),
521
542
  signOut: (request, o) => signOut(context, request, o),
522
543
  viewer: (request, read, token) => viewer(context, request, read, token),
523
544
  requireViewer: (request, read, expiresAt) => requireViewer(context, request, read, expiresAt)
524
545
  };
546
+ if (!o.attemptsLeft)
547
+ return base;
548
+ const wording = { ...context, options: o };
549
+ return {
550
+ ...base,
551
+ attemptsLeft: o.attemptsLeft,
552
+ codeRefusal: (error, language, whenNone) => codeRefusal(wording, error, language, whenNone),
553
+ readCodeInput: (request) => readCodeInput(context, request),
554
+ codeStep
555
+ };
525
556
  }
526
557
  // src/session/step-up.ts
527
558
  import { data as data6 } from "react-router";
@@ -660,5 +691,5 @@ export {
660
691
  stepUpSteps
661
692
  };
662
693
 
663
- //# debugId=5700D84F1A8EF7CF64756E2164756E21
694
+ //# debugId=DC4CDFEB0777ED0864756E2164756E21
664
695
  //# sourceMappingURL=index.js.map
@@ -4,20 +4,20 @@
4
4
  "sourcesContent": [
5
5
  "/** `@nxgt/janus`'s code for a wrong second-factor or step-up code. */\nexport const INVALID_CODE = 'CODE_INVALID';\n\n/** `@nxgt/janus`'s codes for a challenge that is gone: unknown, spent or expired. */\nexport const CHALLENGE_OVER = [\n\t'TOKEN_UNKNOWN',\n\t'TOKEN_SPENT',\n\t'TOKEN_EXPIRED',\n] as const;\n\n/** `@nxgt/janus`'s code for a mail the API holds back: a link or a code asked for too often. */\nexport const MAIL_THROTTLED = 'MAIL_THROTTLED';\n",
6
6
  "/**\n * The bytes of a cookie, and the reading of one: `Name=v; Max-Age=n; Path=/;\n * HttpOnly[; Secure]; SameSite=Lax`, in this order, so a session set by an\n * app before it adopted this package is still read after, and the other way\n * round.\n */\n\nexport interface SerializeOptions {\n\t/** Seconds; floored, and never below 0 (0 removes the cookie, and so does a value that is not finite). */\n\treadonly maxAge: number;\n\t/** `/` by default. */\n\treadonly path?: string | undefined;\n\t/** Adds `Secure`. Required: it is never read from the environment. */\n\treadonly secure: boolean;\n}\n\n// RFC 6265 `token`: what a cookie name may be made of.\nconst TOKEN = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\n// A path of the site: no `;` to open another attribute, no control character.\n// biome-ignore lint/suspicious/noControlCharactersInRegex: that is the point\nconst PATH = /^\\/[^;\\x00-\\x1f\\x7f]*$/;\n\n/** Whether `name` can name a cookie (an RFC 6265 token). */\nexport const isCookieName = (name: string): boolean => TOKEN.test(name);\n/** Whether `path` can be a cookie's `Path`: absolute, no `;`, no control character. */\nexport const isCookiePath = (path: string): boolean => PATH.test(path);\n\n/**\n * A `Set-Cookie` value: always `HttpOnly` and `SameSite=Lax`, `Secure` when\n * asked. A name or a path that could open another attribute throws a\n * `TypeError`; a `maxAge` that is not finite is 0.\n */\nexport function serializeCookie(\n\tname: string,\n\tvalue: string,\n\t{ maxAge, path = '/', secure }: SerializeOptions,\n): string {\n\tif (!isCookieName(name)) {\n\t\tthrow new TypeError(\n\t\t\t`serializeCookie(): the name ${JSON.stringify(name)} is not a cookie name (an RFC 6265 token).`,\n\t\t);\n\t}\n\tif (!isCookiePath(path)) {\n\t\tthrow new TypeError(\n\t\t\t`serializeCookie(): the path ${JSON.stringify(path)} is not a cookie path: it must start with \"/\" and hold no \";\" or control character.`,\n\t\t);\n\t}\n\treturn [\n\t\t`${name}=${encodeURIComponent(value)}`,\n\t\t`Max-Age=${Number.isFinite(maxAge) ? Math.max(0, Math.floor(maxAge)) : 0}`,\n\t\t`Path=${path}`,\n\t\t'HttpOnly',\n\t\t...(secure ? ['Secure'] : []),\n\t\t'SameSite=Lax',\n\t].join('; ');\n}\n\n/**\n * A cookie of the request: the first one of that name that is not empty,\n * decoded; `null` when there is none, or when it is not valid percent-encoding.\n */\nexport function readCookie(request: Request, name: string): string | null {\n\tfor (const pair of request.headers.get('cookie')?.split(';') ?? []) {\n\t\tconst index = pair.indexOf('=');\n\t\tif (index < 0 || pair.slice(0, index).trim() !== name) continue;\n\t\tconst raw = pair.slice(index + 1).trim();\n\t\tif (!raw) continue;\n\t\ttry {\n\t\t\treturn decodeURIComponent(raw);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\treturn null;\n}\n\n/** An instant as milliseconds; 0 when it is not a date. */\nexport function millisOf(at: string | Date): number {\n\tconst time = typeof at === 'string' ? Date.parse(at) : at.getTime();\n\treturn Number.isNaN(time) ? 0 : time;\n}\n\n/** Seconds from `now` until `expiresAt`: 0 when past or unreadable. */\nexport function secondsUntil(\n\texpiresAt: string | Date,\n\tnow: number = Date.now(),\n): number {\n\tconst end = millisOf(expiresAt);\n\treturn end === 0 ? 0 : Math.max(0, (end - now) / 1000);\n}\n",
7
- "import {\n\tisCookieName,\n\tisCookiePath,\n\tmillisOf,\n\treadCookie,\n\tsecondsUntil,\n\tserializeCookie,\n} from './serialize';\n\nexport interface SessionCookiesOptions {\n\t/**\n\t * The base name of the session cookie, e.g. `'my-app_session'`. With\n\t * `secure`, the cookie is named `__Host-my-app_session`.\n\t */\n\treadonly session: string;\n\t/**\n\t * The base name of the second-factor challenge's cookie. With `secure`, it\n\t * is named `__Secure-<name>`.\n\t */\n\treadonly challenge: string;\n\t/**\n\t * The app is served over HTTPS. Required, and never read from the\n\t * environment: pass `!import.meta.env.DEV`. `false` drops `Secure` and the\n\t * prefixes (Safari drops a `Secure` cookie over `http://localhost`).\n\t */\n\treadonly secure: boolean;\n\t/** Where the browser sends the challenge. `'/sign-in'` by default. */\n\treadonly challengePath?: string | undefined;\n\t/** The most the challenge lives. 300, `@nxgt/janus`'s lifetime, by default. */\n\treadonly challengeSeconds?: number | undefined;\n}\n\n/** What the session cookie holds: the token, and the expiry (ms) it was last set to. */\nexport interface CookieSession {\n\treadonly token: string;\n\treadonly expiresAt: number;\n}\n\nexport interface SessionCookies {\n\t/** The names as sent, prefixes included. */\n\treadonly names: { readonly session: string; readonly challenge: string };\n\t/** The session cookie of the request; `null` when absent or not `<ms>.<token>`. */\n\tread(request: Request): CookieSession | null;\n\t/** The session token of the request, or `null`. */\n\ttoken(request: Request): string | null;\n\t/** A `Set-Cookie` value keeping `token` until `expiresAt`. */\n\tcommit(token: string, expiresAt: string | Date, now?: number): string;\n\t/** A `Set-Cookie` value removing the session cookie. */\n\tdestroy(): string;\n\t/** The `Set-Cookie` header that keeps the cookie as long as the session lives; empty when the expiry did not move, or is absent or not a date. */\n\trefreshed(\n\t\tsession: CookieSession,\n\t\texpiresAt: string | Date | null,\n\t): HeadersInit;\n\t/** The challenge cookie of the request, or `null`. */\n\tchallengeOf(request: Request): string | null;\n\t/** A `Set-Cookie` value keeping the challenge for the code page. */\n\tcommitChallenge(\n\t\tchallenge: string,\n\t\texpiresAt?: string | Date | null,\n\t\tnow?: number,\n\t): string;\n\t/** A `Set-Cookie` value removing the challenge. */\n\tdestroyChallenge(): string;\n\t/**\n\t * What a signed-out visit of the sign-in page sets: the challenge cleared\n\t * blind (a client navigation fetches `/sign-in.data`, which the cookie's\n\t * path does not match, so the request never shows it), and a dead\n\t * session's cookie when `deadSession`.\n\t */\n\tsignInPageHeaders(deadSession: boolean): Headers;\n}\n\nconst PREFIX = /^__(?:Host|Secure)-/;\n\nfunction checkName(label: string, name: string): void {\n\tif (!isCookieName(name)) {\n\t\tthrow new TypeError(\n\t\t\t`sessionCookies(): the ${label} name ${JSON.stringify(name)} is not a cookie name (an RFC 6265 token: letters, digits and ! # $ % & ' * + - . ^ _ \\` | ~). Pass a name such as \"my-app_${label}\".`,\n\t\t);\n\t}\n\tif (PREFIX.test(name)) {\n\t\tthrow new TypeError(\n\t\t\t`sessionCookies(): the ${label} name ${JSON.stringify(name)} already carries a prefix. Pass the base name: the package adds __Host- (session) or __Secure- (challenge) when secure is true.`,\n\t\t);\n\t}\n}\n\n/** The cookies of a BFF's session and of its second-factor challenge. */\nexport function sessionCookies(options: SessionCookiesOptions): SessionCookies {\n\tconst {\n\t\tsecure,\n\t\tchallengePath = '/sign-in',\n\t\tchallengeSeconds = 5 * 60,\n\t} = options;\n\tcheckName('session', options.session);\n\tcheckName('challenge', options.challenge);\n\tif (!Number.isInteger(challengeSeconds) || challengeSeconds <= 0) {\n\t\tthrow new TypeError(\n\t\t\t`sessionCookies(): challengeSeconds must be a positive integer; got ${String(challengeSeconds)}.`,\n\t\t);\n\t}\n\tif (!isCookiePath(challengePath)) {\n\t\tthrow new TypeError(\n\t\t\t`sessionCookies(): challengePath must start with \"/\" and hold no \";\" or control character; got ${JSON.stringify(challengePath)}.`,\n\t\t);\n\t}\n\t// `__Host-` holds only with `Path=/` and no `Domain`: the session cookie\n\t// sets neither option.\n\tconst session = secure ? `__Host-${options.session}` : options.session;\n\tconst challenge = secure\n\t\t? `__Secure-${options.challenge}`\n\t\t: options.challenge;\n\tconst clear = (name: string, path?: string) =>\n\t\tserializeCookie(name, '', { maxAge: 0, secure, ...(path && { path }) });\n\n\tconst read = (request: Request): CookieSession | null => {\n\t\tconst value = readCookie(request, session);\n\t\tconst dot = value?.indexOf('.') ?? -1;\n\t\tif (!value || dot < 1) return null;\n\t\tconst digits = value.slice(0, dot);\n\t\tconst expiresAt = Number(digits);\n\t\tconst token = value.slice(dot + 1);\n\t\treturn token && /^\\d+$/.test(digits) && Number.isSafeInteger(expiresAt)\n\t\t\t? { token, expiresAt }\n\t\t\t: null;\n\t};\n\t// The value is `<expiry ms>.<token>`: the expiry only tells a later request\n\t// whether the API moved it, so the cookie is set again then. It is never\n\t// trusted: the API alone decides whether a session is valid.\n\tconst commit = (token: string, expiresAt: string | Date, now?: number) =>\n\t\tserializeCookie(session, `${millisOf(expiresAt)}.${token}`, {\n\t\t\tmaxAge: secondsUntil(expiresAt, now),\n\t\t\tsecure,\n\t\t});\n\n\treturn {\n\t\tnames: { session, challenge },\n\t\tread,\n\t\ttoken: (request) => read(request)?.token ?? null,\n\t\tcommit,\n\t\tdestroy: () => clear(session),\n\t\trefreshed(current, expiresAt) {\n\t\t\t// An expiry that is not a date is garbage, not an end: keep the cookie.\n\t\t\tconst end = expiresAt ? millisOf(expiresAt) : 0;\n\t\t\tif (!expiresAt || end === 0 || end === current.expiresAt) return {};\n\t\t\treturn { 'Set-Cookie': commit(current.token, expiresAt) };\n\t\t},\n\t\tchallengeOf: (request) => readCookie(request, challenge),\n\t\tcommitChallenge: (value, expiresAt, now) =>\n\t\t\tserializeCookie(challenge, value, {\n\t\t\t\tmaxAge: Math.min(\n\t\t\t\t\tchallengeSeconds,\n\t\t\t\t\texpiresAt ? secondsUntil(expiresAt, now) : challengeSeconds,\n\t\t\t\t),\n\t\t\t\tpath: challengePath,\n\t\t\t\tsecure,\n\t\t\t}),\n\t\tdestroyChallenge: () => clear(challenge, challengePath),\n\t\tsignInPageHeaders(deadSession) {\n\t\t\tconst headers = new Headers();\n\t\t\theaders.append('Set-Cookie', clear(challenge, challengePath));\n\t\t\tif (deadSession) headers.append('Set-Cookie', clear(session));\n\t\t\treturn headers;\n\t\t},\n\t};\n}\n",
7
+ "import {\n\tisCookieName,\n\tisCookiePath,\n\tmillisOf,\n\treadCookie,\n\tsecondsUntil,\n\tserializeCookie,\n} from './serialize';\n\n/** What every `sessionCookies` call takes, with or without a second factor. */\nexport interface SessionCookiesBaseOptions {\n\t/**\n\t * The base name of the session cookie, e.g. `'my-app_session'`. With\n\t * `secure`, the cookie is named `__Host-my-app_session`.\n\t */\n\treadonly session: string;\n\t/**\n\t * The app is served over HTTPS. Required, and never read from the\n\t * environment: pass `!import.meta.env.DEV`. `false` drops `Secure` and the\n\t * prefixes (Safari drops a `Secure` cookie over `http://localhost`).\n\t */\n\treadonly secure: boolean;\n}\n\n/** The options of an app with no second factor: no challenge cookie. */\nexport interface SessionCookiesWithoutChallengeOptions\n\textends SessionCookiesBaseOptions {\n\treadonly challenge?: undefined;\n\treadonly challengePath?: undefined;\n\treadonly challengeSeconds?: undefined;\n}\n\n/** The options of an app with a second factor: the challenge cookie's name is given. */\nexport interface SessionCookiesOptions extends SessionCookiesBaseOptions {\n\t/**\n\t * The base name of the second-factor challenge's cookie. With `secure`, it\n\t * is named `__Secure-<name>`.\n\t */\n\treadonly challenge: string;\n\t/** Where the browser sends the challenge. `'/sign-in'` by default. */\n\treadonly challengePath?: string | undefined;\n\t/** The most the challenge lives. 300, `@nxgt/janus`'s lifetime, by default. */\n\treadonly challengeSeconds?: number | undefined;\n}\n\n/** What the session cookie holds: the token, and the expiry (ms) it was last set to. */\nexport interface CookieSession {\n\treadonly token: string;\n\treadonly expiresAt: number;\n}\n\n/** The cookies every BFF has: the session's. */\nexport interface SessionCookiesBase {\n\t/** The names as sent, prefixes included. */\n\treadonly names: { readonly session: string };\n\t/** The session cookie of the request; `null` when absent or not `<ms>.<token>`. */\n\tread(request: Request): CookieSession | null;\n\t/** The session token of the request, or `null`. */\n\ttoken(request: Request): string | null;\n\t/** A `Set-Cookie` value keeping `token` until `expiresAt`. */\n\tcommit(token: string, expiresAt: string | Date, now?: number): string;\n\t/** A `Set-Cookie` value removing the session cookie. */\n\tdestroy(): string;\n\t/** The `Set-Cookie` header that keeps the cookie as long as the session lives; empty when the expiry did not move, or is absent or not a date. */\n\trefreshed(\n\t\tsession: CookieSession,\n\t\texpiresAt: string | Date | null,\n\t): HeadersInit;\n\t/**\n\t * What a signed-out visit of the sign-in page sets: a dead session's\n\t * cookie when `deadSession` (and, with a second factor, the challenge\n\t * cleared blind: a client navigation fetches `/sign-in.data`, which the\n\t * cookie's path does not match, so the request never shows it).\n\t */\n\tsignInPageHeaders(deadSession: boolean): Headers;\n}\n\n/** The challenge cookie's helpers, present when `challenge` was given. */\nexport interface ChallengeCookies {\n\treadonly names: { readonly challenge: string };\n\t/** The challenge cookie of the request, or `null`. */\n\tchallengeOf(request: Request): string | null;\n\t/** A `Set-Cookie` value keeping the challenge for the code page. */\n\tcommitChallenge(\n\t\tchallenge: string,\n\t\texpiresAt?: string | Date | null,\n\t\tnow?: number,\n\t): string;\n\t/** A `Set-Cookie` value removing the challenge. */\n\tdestroyChallenge(): string;\n}\n\n/**\n * The cookies `sessionCookies` returns: with the challenge helpers (the\n * default) when `challenge` was given, `SessionCookies<false>` without.\n */\nexport type SessionCookies<WithChallenge extends boolean = true> =\n\tWithChallenge extends true\n\t\t? SessionCookiesBase & ChallengeCookies\n\t\t: SessionCookiesBase & NoChallengeCookies;\n\n/**\n * What `SessionCookies<false>` says of the challenge helpers: they are\n * absent. Declared as `undefined` so that a set with a challenge is not\n * assignable to it (a flow with no second factor refuses one at compile time).\n */\nexport interface NoChallengeCookies {\n\treadonly challengeOf?: undefined;\n\treadonly commitChallenge?: undefined;\n\treadonly destroyChallenge?: undefined;\n}\n\nconst PREFIX = /^__(?:Host|Secure)-/;\n\nfunction checkName(label: string, name: string): void {\n\tif (!isCookieName(name)) {\n\t\tthrow new TypeError(\n\t\t\t`sessionCookies(): the ${label} name ${JSON.stringify(name)} is not a cookie name (an RFC 6265 token: letters, digits and ! # $ % & ' * + - . ^ _ \\` | ~). Pass a name such as \"my-app_${label}\".`,\n\t\t);\n\t}\n\tif (PREFIX.test(name)) {\n\t\tthrow new TypeError(\n\t\t\t`sessionCookies(): the ${label} name ${JSON.stringify(name)} already carries a prefix. Pass the base name: the package adds __Host- (session) or __Secure- (challenge) when secure is true.`,\n\t\t);\n\t}\n}\n\n/**\n * The cookies of a BFF's session and, when `challenge` is given, of its\n * second-factor challenge. Without `challenge`, the result has no challenge\n * helpers and the type says so.\n *\n * @example\n * const cookies = sessionCookies({ session: 'my-app_session', secure: true });\n */\nexport function sessionCookies(\n\toptions: SessionCookiesOptions,\n): SessionCookies<true>;\nexport function sessionCookies(\n\toptions: SessionCookiesWithoutChallengeOptions,\n): SessionCookies<false>;\nexport function sessionCookies(\n\toptions: SessionCookiesBaseOptions & {\n\t\treadonly challenge?: string | undefined;\n\t\treadonly challengePath?: string | undefined;\n\t\treadonly challengeSeconds?: number | undefined;\n\t},\n): SessionCookies<boolean>;\nexport function sessionCookies(\n\toptions: SessionCookiesBaseOptions & {\n\t\treadonly challenge?: string | undefined;\n\t\treadonly challengePath?: string | undefined;\n\t\treadonly challengeSeconds?: number | undefined;\n\t},\n): SessionCookies<boolean> {\n\tconst {\n\t\tsecure,\n\t\tchallengePath = '/sign-in',\n\t\tchallengeSeconds = 5 * 60,\n\t} = options;\n\tcheckName('session', options.session);\n\tif (options.challenge !== undefined)\n\t\tcheckName('challenge', options.challenge);\n\tif (!Number.isInteger(challengeSeconds) || challengeSeconds <= 0) {\n\t\tthrow new TypeError(\n\t\t\t`sessionCookies(): challengeSeconds must be a positive integer; got ${String(challengeSeconds)}.`,\n\t\t);\n\t}\n\tif (!isCookiePath(challengePath)) {\n\t\tthrow new TypeError(\n\t\t\t`sessionCookies(): challengePath must start with \"/\" and hold no \";\" or control character; got ${JSON.stringify(challengePath)}.`,\n\t\t);\n\t}\n\t// `__Host-` holds only with `Path=/` and no `Domain`: the session cookie\n\t// sets neither option.\n\tconst session = secure ? `__Host-${options.session}` : options.session;\n\tconst challengeName =\n\t\toptions.challenge === undefined\n\t\t\t? undefined\n\t\t\t: secure\n\t\t\t\t? `__Secure-${options.challenge}`\n\t\t\t\t: options.challenge;\n\tconst clear = (name: string, path?: string) =>\n\t\tserializeCookie(name, '', { maxAge: 0, secure, ...(path && { path }) });\n\n\tconst read = (request: Request): CookieSession | null => {\n\t\tconst value = readCookie(request, session);\n\t\tconst dot = value?.indexOf('.') ?? -1;\n\t\tif (!value || dot < 1) return null;\n\t\tconst digits = value.slice(0, dot);\n\t\tconst expiresAt = Number(digits);\n\t\tconst token = value.slice(dot + 1);\n\t\treturn token && /^\\d+$/.test(digits) && Number.isSafeInteger(expiresAt)\n\t\t\t? { token, expiresAt }\n\t\t\t: null;\n\t};\n\t// The value is `<expiry ms>.<token>`: the expiry only tells a later request\n\t// whether the API moved it, so the cookie is set again then. It is never\n\t// trusted: the API alone decides whether a session is valid.\n\tconst commit = (token: string, expiresAt: string | Date, now?: number) =>\n\t\tserializeCookie(session, `${millisOf(expiresAt)}.${token}`, {\n\t\t\tmaxAge: secondsUntil(expiresAt, now),\n\t\t\tsecure,\n\t\t});\n\n\tconst base: SessionCookiesBase = {\n\t\tnames: { session },\n\t\tread,\n\t\ttoken: (request) => read(request)?.token ?? null,\n\t\tcommit,\n\t\tdestroy: () => clear(session),\n\t\trefreshed(current, expiresAt) {\n\t\t\t// An expiry that is not a date is garbage, not an end: keep the cookie.\n\t\t\tconst end = expiresAt ? millisOf(expiresAt) : 0;\n\t\t\tif (!expiresAt || end === 0 || end === current.expiresAt) return {};\n\t\t\treturn { 'Set-Cookie': commit(current.token, expiresAt) };\n\t\t},\n\t\tsignInPageHeaders(deadSession) {\n\t\t\tconst headers = new Headers();\n\t\t\tif (challengeName) {\n\t\t\t\theaders.append('Set-Cookie', clear(challengeName, challengePath));\n\t\t\t}\n\t\t\tif (deadSession) headers.append('Set-Cookie', clear(session));\n\t\t\treturn headers;\n\t\t},\n\t};\n\tif (challengeName === undefined) return base;\n\tconst challengeCookies: ChallengeCookies = {\n\t\tnames: { challenge: challengeName },\n\t\tchallengeOf: (request) => readCookie(request, challengeName),\n\t\tcommitChallenge: (value, expiresAt, now) =>\n\t\t\tserializeCookie(challengeName, value, {\n\t\t\t\tmaxAge: Math.min(\n\t\t\t\t\tchallengeSeconds,\n\t\t\t\t\texpiresAt ? secondsUntil(expiresAt, now) : challengeSeconds,\n\t\t\t\t),\n\t\t\t\tpath: challengePath,\n\t\t\t\tsecure,\n\t\t\t}),\n\t\tdestroyChallenge: () => clear(challengeName, challengePath),\n\t};\n\treturn {\n\t\t...base,\n\t\t...challengeCookies,\n\t\tnames: { session, challenge: challengeName },\n\t};\n}\n\n/** The `Set-Cookie` values that clear the challenge: none without a second factor. */\nexport function challengeCleared(cookies: SessionCookies<boolean>): string[] {\n\treturn cookies.destroyChallenge ? [cookies.destroyChallenge()] : [];\n}\n",
8
8
  "import { data, replace } from 'react-router';\nimport { isApiErrorLike } from '../errors/api-error';\nimport type { FormErrors } from '../errors/form-errors';\nimport type { InferOutput, StandardSchemaV1 } from '../errors/standard-schema';\nimport { samesitePost } from './guard';\nimport type {\n\tLinkStayed,\n\tLinksContext,\n\tSpendOptions,\n\tSpent,\n} from './link-types';\nimport { sitePath } from './model/site-path';\n\n/**\n * The response headers of a page a mail's link opens, whose URL carries a\n * token: no `Referer` sends it to another site (its fonts included), and\n * no search engine keeps it.\n */\nexport const TOKEN_PAGE_HEADERS = {\n\t'Referrer-Policy': 'no-referrer',\n\t'X-Robots-Tag': 'noindex, nofollow',\n} as const;\n\n/** A token page's path with no query or fragment, or a `TypeError` naming `call`. */\nconst pathOf = (path: string, call: string): string =>\n\tsitePath(path, { label: `${call}: a token page's path` });\n\nexport const statePath = (path: string, state: string): string =>\n\t`${pathOf(path, 'statePath()')}?${new URLSearchParams({ state })}`;\n\n/** `headers`, with the token page's own on top. */\nexport function withTokenHeaders(headers?: HeadersInit): Headers {\n\tconst merged = new Headers(headers);\n\tfor (const [name, value] of Object.entries(TOKEN_PAGE_HEADERS)) {\n\t\tmerged.set(name, value);\n\t}\n\treturn merged;\n}\n\n/** A redirect replacing the current history entry: the token's, so Back never opens it again. */\nexport const stateRedirect = (\n\tpath: string,\n\tstate: string,\n\theaders?: HeadersInit,\n): Response =>\n\treplace(statePath(path, state), { headers: withTokenHeaders(headers) });\n\nconst isTokenIssue = (issue: {\n\treadonly path?:\n\t\t| readonly (PropertyKey | { readonly key: PropertyKey })[]\n\t\t| undefined;\n}) => {\n\tconst first = issue.path?.[0];\n\treturn (typeof first === 'object' ? first.key : first) === 'token';\n};\n\nexport async function linkArgs<S extends StandardSchemaV1>(\n\t{ json }: Pick<LinksContext<string>, 'json'>,\n\tinvalidState: string,\n\tschema: S,\n\trequest: Request,\n\tpath: string,\n): Promise<InferOutput<S>> {\n\tsamesitePost(json, request);\n\tpathOf(path, 'linkArgs()');\n\tconst body = await json.readJson(request);\n\tconst result = await schema['~standard'].validate(body);\n\tif (!result.issues) return result.value as InferOutput<S>;\n\t// A token the API would refuse anyway (a link cut or padded on its way)\n\t// ends as an unknown one does; anything else is a bad request.\n\tif (result.issues.some(isTokenIssue)) {\n\t\tthrow stateRedirect(path, invalidState);\n\t}\n\tthrow data('Bad link request', { status: 400 });\n}\n\nexport async function spendToken<L extends string, C, T>(\n\toptions: LinksContext<L, C>,\n\trun: () => Promise<T>,\n\t{ request, language, path, states, fields: shown = [] }: SpendOptions<L>,\n): Promise<Spent<T>> {\n\tconst { errors } = options;\n\tsamesitePost(options.json, request);\n\tpathOf(path, 'spendToken()');\n\ttry {\n\t\treturn { ok: true, value: await run() };\n\t} catch (error) {\n\t\tconst refusal = errors.apiRefusal(error, language);\n\t\tif (!refusal) throw error;\n\t\tconst { message, status } = refusal;\n\t\tif (isApiErrorLike(error)) {\n\t\t\tconst state = error.code ? states[error.code] : undefined;\n\t\t\tif (state) return { ok: false, response: stateRedirect(path, state) };\n\t\t\tconst fields = errors.fieldErrors(error, language);\n\t\t\tconst named = Object.keys(fields);\n\t\t\t// A refusal under a field the page does not show would say nothing.\n\t\t\tif (named.length && named.every((field) => shown.includes(field))) {\n\t\t\t\treturn { ok: false, refusal: { fieldErrors: fields }, status };\n\t\t\t}\n\t\t}\n\t\treturn { ok: false, refusal: { formError: message }, status };\n\t}\n}\n\nexport const answerOf = (\n\tspent: Exclude<Spent<unknown>, { ok: true }>,\n): Response | LinkStayed =>\n\t'response' in spent\n\t\t? spent.response\n\t\t: data<FormErrors>(spent.refusal, { status: spent.status });\n",
9
9
  "import type { JsonActions } from '../errors/json-actions';\nimport { refuseCrossSite } from '../errors/same-site';\n\n/**\n * What every step that signs in, out, spends or mails runs first: a native\n * form post (before hydration) goes back to its page, which then posts JSON\n * (`json.requireJsonPost`), and a request from another site is a 403 even\n * when it is JSON (the `Origin` and `Sec-Fetch-Site` rule of `readJson`). It\n * reads headers only, never the body.\n */\nexport function samesitePost(json: JsonActions, request: Request): void {\n\tjson.requireJsonPost(request);\n\trefuseCrossSite(json, request);\n}\n",
10
10
  "import { data } from 'react-router';\nimport {\n\ttype ApiErrorLike,\n\tisApiErrorLike,\n\tretryAfterOf,\n} from '../errors/api-error';\nimport { MAIL_THROTTLED } from './codes';\nimport { samesitePost } from './guard';\nimport { afterReset, landByLink } from './link-land';\nimport {\n\tanswerOf,\n\tlinkArgs,\n\tspendToken,\n\tstatePath,\n\tstateRedirect,\n} from './link-token';\nimport type {\n\tAuthLinks,\n\tAuthLinksCodes,\n\tAuthLinksOptions,\n\tLinksContext,\n\tRequestLinkAnswer,\n} from './link-types';\nimport { sitePath } from './model/site-path';\n\nconst RESET_DONE = '/sign-in?password-reset=1';\n\n/**\n * Requests a mail's link with `run`. The API answers true whether or not\n * the e-mail holds an account, and so does the page: the same \"sent\" for\n * everyone. A wait or an unreachable API is the form's message; a malformed\n * address is the `email` field's.\n */\n/** The refusal's message: the app's own wording, else `errors`'. */\nconst worded = <L extends string, C>(\n\t{ wording, errors }: LinksContext<L, C>,\n\terror: ApiErrorLike,\n\tlanguage: L,\n): string =>\n\twording?.(error, language) ?? errors.apiErrorMessage(error, language);\n\n/** The codes that are a wait: the configured one or list, else janus's. */\nconst throttleCodes = (\n\tcodes: AuthLinksCodes | undefined,\n): readonly string[] => {\n\tconst configured = codes?.mailThrottled ?? MAIL_THROTTLED;\n\treturn typeof configured === 'string' ? [configured] : configured;\n};\n\nasync function requestLink<L extends string, C>(\n\toptions: LinksContext<L, C>,\n\trun: () => Promise<unknown>,\n\t{ request, language }: Parameters<AuthLinks<L, C>['requestLink']>[1],\n) {\n\tconst { errors } = options;\n\tsamesitePost(options.json, request);\n\ttry {\n\t\tawait run();\n\t\treturn data<RequestLinkAnswer>({ sent: true });\n\t} catch (error) {\n\t\tconst api = isApiErrorLike(error);\n\t\tif (api && throttleCodes(options.codes).includes(error.code ?? '')) {\n\t\t\tconst retryAfter = retryAfterOf(error);\n\t\t\treturn data<RequestLinkAnswer>(\n\t\t\t\t{\n\t\t\t\t\tformError: worded(options, error, language),\n\t\t\t\t\tthrottled: true,\n\t\t\t\t\t...(retryAfter === undefined ? {} : { retryAfter }),\n\t\t\t\t},\n\t\t\t\t{ status: 429 },\n\t\t\t);\n\t\t}\n\t\tif (api) {\n\t\t\tconst email = errors.fieldErrors(error, language)['email'];\n\t\t\tif (email) {\n\t\t\t\treturn data<RequestLinkAnswer>(\n\t\t\t\t\t{ fieldErrors: { email } },\n\t\t\t\t\t{ status: error.status },\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tconst refusal = errors.apiRefusal(error, language);\n\t\tif (!refusal) throw error;\n\t\treturn data<RequestLinkAnswer>(\n\t\t\t{ formError: api ? worded(options, error, language) : refusal.message },\n\t\t\t{ status: refusal.status },\n\t\t);\n\t}\n}\n\n/**\n * The pages a mail's link opens (a magic link, a password reset, an\n * invitation): the token page's end states, the arguments it posts, the\n * token spent against the API, the sign-in it leads to (replacing the\n * token's history entry), and the request for a link. Built once, beside the\n * app's `signInFlow`.\n *\n * @example\n * export const links = authLinks({ signIn });\n * // loader: return data({ ... }, { headers: TOKEN_PAGE_HEADERS });\n */\nexport function authLinks<L extends string, C = undefined>(\n\tlinks: AuthLinksOptions<L, C>,\n): AuthLinks<L, C> {\n\tconst { signIn } = links;\n\tconst options: LinksContext<L, C> = {\n\t\t...links,\n\t\tcookies: signIn.cookies,\n\t\terrors: signIn.errors,\n\t\tjson: signIn.json,\n\t};\n\tconst resetDone = options.resetDone ?? RESET_DONE;\n\tsitePath(resetDone, { label: 'authLinks(): resetDone', query: true });\n\tconst invalid = options.invalidState ?? 'invalid';\n\treturn {\n\t\tstatePath,\n\t\tended: stateRedirect,\n\t\tlinkArgs: (schema, request, path) =>\n\t\t\tlinkArgs(options, invalid, schema, request, path),\n\t\tspendToken: (run, o) => spendToken(options, run, o),\n\t\tanswerOf,\n\t\tlandByLink: (answer, o) => landByLink(options, answer, o),\n\t\tafterReset: (request, o) => afterReset(options, resetDone, request, o),\n\t\trequestLink: (run, o) => requestLink(options, run, o),\n\t};\n}\n",
11
- "import { data, replace } from 'react-router';\nimport type { FormErrors } from '../errors/form-errors';\nimport type { SignInAnswer } from './flow-types';\nimport { samesitePost } from './guard';\nimport { isLive } from './land';\nimport { stateRedirect, withTokenHeaders } from './link-token';\nimport type { AuthLinks, LinkStayed, LinksContext } from './link-types';\nimport { HOME, safeNext } from './model';\n\n/** Where a new session starts: the app's start page, else `/`; never an unsafe path. */\nasync function startOf<L extends string, C>(\n\t{ start }: LinksContext<L, C>,\n\trequest: Request,\n\ttoken: string,\n): Promise<string> {\n\tif (!start) return HOME;\n\ttry {\n\t\treturn safeNext(await start(token, request));\n\t} catch {\n\t\t// The token is spent: a failed lookup must not fail the sign-in.\n\t\treturn HOME;\n\t}\n}\n\n/**\n * Where a link's sign-in leads, replacing the token's history entry. Signed\n * in: the start page, with the new session's cookie; a second factor: its\n * page. Either way the session the browser held, whoever's, is revoked\n * first: one browser holds one session. Only a JSON post goes on, before\n * anything is revoked or landed.\n */\nexport async function landByLink<L extends string, C>(\n\toptions: LinksContext<L, C>,\n\tanswer: SignInAnswer,\n\t{\n\t\trequest,\n\t\tlanguage,\n\t\tother,\n\t\tcontext,\n\t}: Parameters<AuthLinks<L, C>['landByLink']>[1],\n): Promise<Response | LinkStayed> {\n\tsamesitePost(options.json, request);\n\tif (answer.status === 'other' && other) {\n\t\treturn stateRedirect(other.path, other.state);\n\t}\n\t// A dead session would be looked up with a token the API refuses.\n\tconst next = isLive(answer)\n\t\t? await startOf(options, request, answer.token)\n\t\t: HOME;\n\tconst landed = await options.signIn.landReplacing(answer, next, {\n\t\trequest,\n\t\trevokeOn: ['signedIn', 'secondFactor'],\n\t\tcontext,\n\t});\n\tif (!landed) {\n\t\treturn data<FormErrors>(\n\t\t\t{ formError: options.signIn.unavailable(language) },\n\t\t\t{ status: 503 },\n\t\t);\n\t}\n\tconst location = landed.headers.get('Location') ?? HOME;\n\treturn replace(location, { headers: withTokenHeaders(landed.headers) });\n}\n\n/**\n * After a password reset, which signs the account out everywhere: the\n * browser's session is revoked (it may be another account's) and its cookies\n * cleared (the session's, the challenge's, the flow's `clear()`), then the sign-in page says the password changed. Only a JSON post\n * goes on.\n */\nexport async function afterReset<L extends string, C>(\n\toptions: LinksContext<L, C>,\n\tresetDone: string,\n\trequest: Request,\n\t{ context }: { readonly context?: C | undefined } = {},\n): Promise<Response> {\n\tsamesitePost(options.json, request);\n\tawait options.signIn.revokeSession(request, { context });\n\tconst headers = withTokenHeaders();\n\theaders.append('Set-Cookie', options.cookies.destroy());\n\theaders.append('Set-Cookie', options.cookies.destroyChallenge());\n\tfor (const cookie of options.signIn.clear()) {\n\t\theaders.append('Set-Cookie', cookie);\n\t}\n\treturn replace(resetDone, { headers });\n}\n",
12
- "import { redirect } from 'react-router';\nimport type { FlowContext, SignInAnswer, SignInFlow } from './flow-types';\nimport { samesitePost } from './guard';\nimport { safeNext, secondFactorPath } from './model';\nimport { millisOf } from './serialize';\n\n/**\n * Ends the request's session at the API, when it carries one; a failure is\n * ignored, the caller clears the cookie anyway. True when there was one.\n */\nexport async function revokeSession<L extends string, C>(\n\t{ options }: FlowContext<L, C>,\n\trequest: Request,\n\t{ context }: { readonly context?: C | undefined } = {},\n): Promise<boolean> {\n\tsamesitePost(options.json, request);\n\tconst token = options.cookies.token(request);\n\tif (!token) return false;\n\ttry {\n\t\tawait options.revoke(token, request, context);\n\t} catch {\n\t\t// The old session ends when it expires.\n\t}\n\treturn true;\n}\n\n/** The condition a session opens on: a signed-in answer with a token and an end still to come. */\nexport const isLive = (\n\tanswer: SignInAnswer,\n): answer is Extract<SignInAnswer, { status: 'signedIn' }> =>\n\tanswer.status === 'signedIn' &&\n\tBoolean(answer.token) &&\n\tBoolean(answer.expiresAt) &&\n\tmillisOf(answer.expiresAt) > Date.now();\n\n/**\n * Where a sign-in's answer leads: signed in, to `next` with the session\n * cookie (and the challenge cleared); a second factor, to its page with the\n * challenge in its own cookie, never in the URL. Any other answer, or a\n * signed-in one with no token or an end that is missing, unreadable or past,\n * opens no session: `null`, the caller shows its own state.\n */\nexport function landing<L extends string, C>(\n\t{ options, paths }: FlowContext<L, C>,\n\tanswer: SignInAnswer,\n\tnext: string,\n): Response | null {\n\tconst { cookies } = options;\n\t// A session with no readable or past end would be set and dead at once.\n\tif (isLive(answer)) {\n\t\tconst headers = new Headers();\n\t\theaders.append(\n\t\t\t'Set-Cookie',\n\t\t\tcookies.commit(answer.token, answer.expiresAt),\n\t\t);\n\t\theaders.append('Set-Cookie', cookies.destroyChallenge());\n\t\tfor (const cookie of options.clear?.() ?? []) {\n\t\t\theaders.append('Set-Cookie', cookie);\n\t\t}\n\t\treturn redirect(safeNext(next), { headers });\n\t}\n\tif (answer.status === 'secondFactor' && answer.challenge) {\n\t\treturn redirect(secondFactorPath(next, paths.secondFactor), {\n\t\t\theaders: {\n\t\t\t\t'Set-Cookie': cookies.commitChallenge(\n\t\t\t\t\tanswer.challenge,\n\t\t\t\t\tanswer.expiresAt,\n\t\t\t\t),\n\t\t\t},\n\t\t});\n\t}\n\treturn null;\n}\n\n/**\n * `landing`, ending the session the browser held first: one browser holds\n * one session, so the one its cookie names, whoever's, is revoked when an\n * answer in `revokeOn` lands. A signed-in answer's own cookie replaces the\n * old one; any other answer clears it. A failed revoke is ignored.\n */\nexport async function landReplacing<L extends string, C>(\n\tflow: FlowContext<L, C>,\n\tanswer: SignInAnswer,\n\tnext: string,\n\t{\n\t\trequest,\n\t\trevokeOn = ['signedIn'],\n\t\tcontext,\n\t}: Parameters<SignInFlow<L, C>['landReplacing']>[2],\n): Promise<Response | null> {\n\tsamesitePost(flow.options.json, request);\n\tconst landed = landing(flow, answer, next);\n\tif (!landed || answer.status === 'other') return landed;\n\tif (!revokeOn.includes(answer.status)) return landed;\n\tconst hadSession = await revokeSession(flow, request, { context });\n\tif (hadSession && answer.status !== 'signedIn') {\n\t\tlanded.headers.append('Set-Cookie', flow.options.cookies.destroy());\n\t}\n\treturn landed;\n}\n",
11
+ "import { data, replace } from 'react-router';\nimport type { FormErrors } from '../errors/form-errors';\nimport { challengeCleared } from './cookies';\nimport type { SignInAnswer } from './flow-types';\nimport { samesitePost } from './guard';\nimport { isLive } from './land';\nimport { stateRedirect, withTokenHeaders } from './link-token';\nimport type { AuthLinks, LinkStayed, LinksContext } from './link-types';\nimport { HOME, safeNext } from './model';\n\n/** Where a new session starts: the app's start page, else `/`; never an unsafe path. */\nasync function startOf<L extends string, C>(\n\t{ start }: LinksContext<L, C>,\n\trequest: Request,\n\ttoken: string,\n): Promise<string> {\n\tif (!start) return HOME;\n\ttry {\n\t\treturn safeNext(await start(token, request));\n\t} catch {\n\t\t// The token is spent: a failed lookup must not fail the sign-in.\n\t\treturn HOME;\n\t}\n}\n\n/**\n * Where a link's sign-in leads, replacing the token's history entry. Signed\n * in: the start page, with the new session's cookie; a second factor: its\n * page. Either way the session the browser held, whoever's, is revoked\n * first: one browser holds one session. Only a JSON post goes on, before\n * anything is revoked or landed.\n */\nexport async function landByLink<L extends string, C>(\n\toptions: LinksContext<L, C>,\n\tanswer: SignInAnswer,\n\t{\n\t\trequest,\n\t\tlanguage,\n\t\tother,\n\t\tcontext,\n\t}: Parameters<AuthLinks<L, C>['landByLink']>[1],\n): Promise<Response | LinkStayed> {\n\tsamesitePost(options.json, request);\n\tif (answer.status === 'other' && other) {\n\t\treturn stateRedirect(other.path, other.state);\n\t}\n\t// A dead session would be looked up with a token the API refuses.\n\tconst next = isLive(answer)\n\t\t? await startOf(options, request, answer.token)\n\t\t: HOME;\n\tconst landed = await options.signIn.landReplacing(answer, next, {\n\t\trequest,\n\t\trevokeOn: ['signedIn', 'secondFactor'],\n\t\tcontext,\n\t});\n\tif (!landed) {\n\t\treturn data<FormErrors>(\n\t\t\t{ formError: options.signIn.unavailable(language) },\n\t\t\t{ status: 503 },\n\t\t);\n\t}\n\tconst location = landed.headers.get('Location') ?? HOME;\n\treturn replace(location, { headers: withTokenHeaders(landed.headers) });\n}\n\n/**\n * After a password reset, which signs the account out everywhere: the\n * browser's session is revoked (it may be another account's) and its cookies\n * cleared (the session's, the challenge's, the flow's `clear()`), then the sign-in page says the password changed. Only a JSON post\n * goes on.\n */\nexport async function afterReset<L extends string, C>(\n\toptions: LinksContext<L, C>,\n\tresetDone: string,\n\trequest: Request,\n\t{ context }: { readonly context?: C | undefined } = {},\n): Promise<Response> {\n\tsamesitePost(options.json, request);\n\tawait options.signIn.revokeSession(request, { context });\n\tconst headers = withTokenHeaders();\n\theaders.append('Set-Cookie', options.cookies.destroy());\n\tfor (const cookie of challengeCleared(options.cookies)) {\n\t\theaders.append('Set-Cookie', cookie);\n\t}\n\tfor (const cookie of options.signIn.clear()) {\n\t\theaders.append('Set-Cookie', cookie);\n\t}\n\treturn replace(resetDone, { headers });\n}\n",
12
+ "import { redirect } from 'react-router';\nimport { challengeCleared } from './cookies';\nimport type { FlowContext, SignInAnswer, SignInFlow } from './flow-types';\nimport { samesitePost } from './guard';\nimport { safeNext, secondFactorPath } from './model';\nimport { millisOf } from './serialize';\n\n/**\n * Ends the request's session at the API, when it carries one; a failure is\n * ignored, the caller clears the cookie anyway. True when there was one.\n */\nexport async function revokeSession<L extends string, C>(\n\t{ options }: FlowContext<L, C>,\n\trequest: Request,\n\t{ context }: { readonly context?: C | undefined } = {},\n): Promise<boolean> {\n\tsamesitePost(options.json, request);\n\tconst token = options.cookies.token(request);\n\tif (!token) return false;\n\ttry {\n\t\tawait options.revoke(token, request, context);\n\t} catch {\n\t\t// The old session ends when it expires.\n\t}\n\treturn true;\n}\n\n/** The condition a session opens on: a signed-in answer with a token and an end still to come. */\nexport const isLive = (\n\tanswer: SignInAnswer,\n): answer is Extract<SignInAnswer, { status: 'signedIn' }> =>\n\tanswer.status === 'signedIn' &&\n\tBoolean(answer.token) &&\n\tBoolean(answer.expiresAt) &&\n\tmillisOf(answer.expiresAt) > Date.now();\n\n/**\n * Where a sign-in's answer leads: signed in, to `next` with the session\n * cookie (and the challenge cleared); a second factor, to its page with the\n * challenge in its own cookie, never in the URL. Any other answer, or a\n * signed-in one with no token or an end that is missing, unreadable or past,\n * opens no session: `null`, the caller shows its own state.\n */\nexport function landing<L extends string, C>(\n\t{ options, paths }: FlowContext<L, C>,\n\tanswer: SignInAnswer,\n\tnext: string,\n): Response | null {\n\tconst { cookies } = options;\n\t// A session with no readable or past end would be set and dead at once.\n\tif (isLive(answer)) {\n\t\tconst headers = new Headers();\n\t\theaders.append(\n\t\t\t'Set-Cookie',\n\t\t\tcookies.commit(answer.token, answer.expiresAt),\n\t\t);\n\t\tfor (const cookie of challengeCleared(cookies)) {\n\t\t\theaders.append('Set-Cookie', cookie);\n\t\t}\n\t\tfor (const cookie of options.clear?.() ?? []) {\n\t\t\theaders.append('Set-Cookie', cookie);\n\t\t}\n\t\treturn redirect(safeNext(next), { headers });\n\t}\n\t// An app with no second factor has no challenge cookie: nothing to land on.\n\tif (\n\t\tanswer.status === 'secondFactor' &&\n\t\tanswer.challenge &&\n\t\tcookies.commitChallenge\n\t) {\n\t\treturn redirect(secondFactorPath(next, paths.secondFactor), {\n\t\t\theaders: {\n\t\t\t\t'Set-Cookie': cookies.commitChallenge(\n\t\t\t\t\tanswer.challenge,\n\t\t\t\t\tanswer.expiresAt,\n\t\t\t\t),\n\t\t\t},\n\t\t});\n\t}\n\treturn null;\n}\n\n/**\n * `landing`, ending the session the browser held first: one browser holds\n * one session, so the one its cookie names, whoever's, is revoked when an\n * answer in `revokeOn` lands. A signed-in answer's own cookie replaces the\n * old one; any other answer clears it. A failed revoke is ignored.\n */\nexport async function landReplacing<L extends string, C>(\n\tflow: FlowContext<L, C>,\n\tanswer: SignInAnswer,\n\tnext: string,\n\t{\n\t\trequest,\n\t\trevokeOn = ['signedIn'],\n\t\tcontext,\n\t}: Parameters<SignInFlow<L, C>['landReplacing']>[2],\n): Promise<Response | null> {\n\tsamesitePost(flow.options.json, request);\n\tconst landed = landing(flow, answer, next);\n\tif (!landed || answer.status === 'other') return landed;\n\tif (!revokeOn.includes(answer.status)) return landed;\n\tconst hadSession = await revokeSession(flow, request, { context });\n\tif (hadSession && answer.status !== 'signedIn') {\n\t\tlanded.headers.append('Set-Cookie', flow.options.cookies.destroy());\n\t}\n\treturn landed;\n}\n",
13
13
  "import { data } from 'react-router';\nimport type { FlowContext, SignInAnswer, SignInFlow } from './flow-types';\nimport { samesitePost } from './guard';\nimport type { SecondFactorKind } from './model';\n\nconst MAX_CODE = 40;\n\nfunction isMethod(value: unknown): value is SecondFactorKind {\n\treturn value === 'code' || value === 'recovery';\n}\n\n/**\n * The code a second-factor page posted, with how it is given\n * (`{ kind: 'code' | 'recovery', code }`, the code at most 40 characters). A\n * native form post goes back to its page, which then posts JSON; a\n * cross-site post is a 403; a body of another shape is a 400.\n */\nexport async function readCodeInput<L extends string, C>(\n\t{ options }: FlowContext<L, C>,\n\trequest: Request,\n): Promise<{ kind: SecondFactorKind; code: string }> {\n\tsamesitePost(options.json, request);\n\tconst body = (await options.json.readJson(request)) as {\n\t\tkind?: unknown;\n\t\tcode?: unknown;\n\t} | null;\n\tconst { kind, code } = body ?? {};\n\tif (!isMethod(kind) || typeof code !== 'string' || code.length > MAX_CODE) {\n\t\tthrow data('Bad code request', { status: 400 });\n\t}\n\treturn { kind, code };\n}\n\n/**\n * The code against the sign-in's challenge: an issued code (an app's, or\n * the one mailed) is confirmed, a recovery code is recovered. Either\n * answers like a sign-in.\n */\nexport function codeStep(\n\tinput: { readonly kind: SecondFactorKind; readonly code: string },\n\tchallenge: string,\n\tsteps: Parameters<SignInFlow<string>['codeStep']>[2],\n): Promise<SignInAnswer> {\n\treturn input.kind === 'code'\n\t\t? steps.confirm(challenge, input.code)\n\t\t: steps.recover(challenge, input.code);\n}\n",
14
14
  "import { data } from 'react-router';\nimport {\n\ttype ApiErrorLike,\n\tattemptsLeftOf,\n\tisApiErrorLike,\n} from '../errors/api-error';\nimport type {\n\tCodeWording,\n\tFlowContext,\n\tSignInAnswer,\n\tSignInFlow,\n\tSignInFlowOptions,\n\tSignInFormError,\n\tSignInStayed,\n} from './flow-types';\nimport { samesitePost } from './guard';\nimport { landReplacing } from './land';\n\n/**\n * A refused code's message, with the tries left when the API counts them;\n * `undefined` when the challenge is over (gone, or no try left and no\n * `whenNone` to say instead) and the user must sign in again.\n */\nexport function codeRefusal<L extends string>(\n\t{ options, invalidCode, challengeOver }: CodeWording<L>,\n\terror: ApiErrorLike,\n\tlanguage: L,\n\twhenNone?: string,\n): string | undefined {\n\tif (error.code !== invalidCode) {\n\t\treturn error.code && challengeOver.includes(error.code)\n\t\t\t? undefined\n\t\t\t: options.errors.apiErrorMessage(error, language);\n\t}\n\tconst message = options.errors.apiErrorMessage(error, language);\n\tconst count = attemptsLeftOf(error);\n\tif (count === undefined) return message;\n\tif (count === 0) {\n\t\treturn whenNone === undefined ? undefined : `${message} ${whenNone}`;\n\t}\n\treturn `${message} ${options.attemptsLeft(count, language)}`;\n}\n\n/** What a sign-in says when the API cannot be reached. */\nexport const unavailableMessage = <L extends string>(\n\t{ unavailable, errors }: Pick<SignInFlowOptions<L>, 'unavailable' | 'errors'>,\n\tlanguage: L,\n): string => unavailable?.(language) ?? errors.unavailableMessage(language);\n\n/**\n * Runs a sign-in step and redirects where its answer leads, a new session\n * replacing the browser's (`landReplacing`). A refusal is the form's\n * message (a wrong password, a lock with its delay); an unreachable API\n * says `unavailable`, with a 503.\n */\nexport async function signInStep<L extends string, C, T extends object>(\n\tflow: FlowContext<L, C>,\n\trun: () => Promise<SignInAnswer>,\n\t{\n\t\trequest,\n\t\tlanguage,\n\t\tnext,\n\t\textra,\n\t\trefused,\n\t\tcontext,\n\t}: Parameters<SignInFlow<L, C>['signInStep']>[1] & { extra: T },\n): Promise<Response | SignInStayed<T>> {\n\tconst { errors, json } = flow.options;\n\t// A cross-site post must never sign in or revoke (headers only).\n\tsamesitePost(json, request);\n\tconst stay = (formError: string, status: number) =>\n\t\tdata<SignInFormError<T>>({ ...extra, formError }, { status });\n\tconst unreachable = () =>\n\t\tstay(unavailableMessage(flow.options, language), 503);\n\tlet answer: SignInAnswer;\n\ttry {\n\t\tanswer = await run();\n\t} catch (error) {\n\t\tconst refusal = errors.apiRefusal(error, language);\n\t\tif (!refusal) throw error;\n\t\t// An API it cannot reach: sign-in's own words.\n\t\tif (!isApiErrorLike(error)) return unreachable();\n\t\tconst own = refused?.(error);\n\t\tif (own instanceof Response) return own;\n\t\treturn stay(own ?? refusal.message, refusal.status);\n\t}\n\treturn (\n\t\t(await landReplacing(flow, answer, next, { request, context })) ??\n\t\tunreachable()\n\t);\n}\n",
15
- "import { redirect } from 'react-router';\nimport { isApiErrorLike } from '../errors/api-error';\nimport { nextOf } from '../errors/page-path';\nimport { routeError } from '../errors/route-error';\nimport type { FlowContext } from './flow-types';\nimport { revokeSession } from './land';\nimport { safeNext, signInPath } from './model';\n\n/** The `next` a sign-in page was opened with, safe to redirect to. */\nexport const nextParam = (request: Request): string =>\n\tsafeNext(new URL(request.url).searchParams.get('next'));\n\n/**\n * The sign-in page, coming back to this request's page afterwards, with the\n * session cookie cleared: what a page answers without a (live) session. A\n * request that carried a session cookie says it ended when `endedParam` is\n * set.\n */\nexport function signInRedirect<L extends string, C>(\n\t{ options, paths }: FlowContext<L, C>,\n\trequest: Request,\n): Response {\n\tconst { endedParam, cookies } = options;\n\tconst query =\n\t\tendedParam && cookies.read(request) ? { [endedParam]: '1' } : undefined;\n\treturn redirect(\n\t\tsignInPath(nextOf(request.url), {\n\t\t\tpath: paths.signIn,\n\t\t\t...(query && { query }),\n\t\t}),\n\t\t{ headers: { 'Set-Cookie': cookies.destroy() } },\n\t);\n}\n\n/**\n * For the sign-in pages: the viewer when the cookie holds a live session,\n * `null` when the API refuses it (a 401), `undefined` without a cookie or\n * when the API cannot tell.\n */\nexport async function viewer<L extends string, C, V>(\n\t{ options }: FlowContext<L, C>,\n\trequest: Request,\n\tread: (token: string) => Promise<V | null>,\n\ttoken: string | null | undefined = options.cookies.token(request),\n): Promise<V | null | undefined> {\n\tif (!token) return undefined;\n\ttry {\n\t\treturn await read(token);\n\t} catch (error) {\n\t\treturn isApiErrorLike(error) && error.status === 401 ? null : undefined;\n\t}\n}\n\n/**\n * The signed-in viewer, else the sign-in redirect. Its `headers` move the\n * cookie's expiry to the session's when the API moved it.\n */\nexport async function requireViewer<L extends string, C, V>(\n\tcontext: FlowContext<L, C>,\n\trequest: Request,\n\tread: (token: string) => Promise<V | null>,\n\texpiresAt: (viewer: V) => string | null,\n): Promise<{ viewer: V; headers: HeadersInit }> {\n\tconst session = context.options.cookies.read(request);\n\tif (!session) throw signInRedirect(context, request);\n\tlet found: V | null;\n\ttry {\n\t\tfound = await read(session.token);\n\t} catch (error) {\n\t\tif (isApiErrorLike(error) && error.status === 401) {\n\t\t\tthrow signInRedirect(context, request);\n\t\t}\n\t\tthrow routeError(error);\n\t}\n\tif (!found) throw signInRedirect(context, request);\n\treturn {\n\t\tviewer: found,\n\t\theaders: context.options.cookies.refreshed(session, expiresAt(found)),\n\t};\n}\n\n/**\n * Ends the session and goes to the sign-in page (`?signed-out=1`), or to\n * `to`. The body is read as JSON first: JSON and same-origin only, so\n * another site cannot sign the user out.\n */\nexport async function signOut<L extends string, C>(\n\tflow: FlowContext<L, C>,\n\trequest: Request,\n\t{\n\t\tto,\n\t\tcontext,\n\t}: {\n\t\treadonly to?: string | undefined;\n\t\treadonly context?: C | undefined;\n\t} = {},\n): Promise<Response> {\n\tconst { options, paths } = flow;\n\tawait options.json.readJson(request);\n\tawait revokeSession(flow, request, { context });\n\tconst headers = new Headers();\n\theaders.append('Set-Cookie', options.cookies.destroy());\n\theaders.append('Set-Cookie', options.cookies.destroyChallenge());\n\tfor (const cookie of options.clear?.() ?? []) {\n\t\theaders.append('Set-Cookie', cookie);\n\t}\n\treturn redirect(\n\t\tto === undefined ? `${paths.signIn}?signed-out=1` : safeNext(to),\n\t\t{ headers },\n\t);\n}\n",
16
- "import { hasSiteCheck } from '../errors/same-site';\nimport { CHALLENGE_OVER, INVALID_CODE } from './codes';\nimport type { FlowContext, SignInFlow, SignInFlowOptions } from './flow-types';\nimport { samesitePost } from './guard';\nimport { landing, landReplacing, revokeSession } from './land';\nimport { SIGN_IN_PATHS, type SignInPaths } from './model';\nimport { sitePath } from './model/site-path';\nimport { codeStep, readCodeInput } from './second-factor';\nimport { codeRefusal, signInStep, unavailableMessage } from './step';\nimport {\n\tnextParam,\n\trequireViewer,\n\tsignInRedirect,\n\tsignOut,\n\tviewer,\n} from './viewer';\n\nfunction resolve<L extends string, C>(\n\toptions: SignInFlowOptions<L, C>,\n): FlowContext<L, C> {\n\tconst paths: SignInPaths = { ...SIGN_IN_PATHS, ...options.paths };\n\tfor (const key of ['signIn', 'secondFactor'] as const) {\n\t\tsitePath(paths[key], { label: `signInFlow(): paths.${key}` });\n\t}\n\tif (!hasSiteCheck(options.json)) {\n\t\tthrow new TypeError(\n\t\t\t\"signInFlow(): json must come from jsonActions(), which carries the site check every step runs; this object has none (a hand-made double?). Pass the app's jsonActions({ origin }) instance, or a spread copy of it.\",\n\t\t);\n\t}\n\tconst invalidCode = options.codes?.invalidCode ?? INVALID_CODE;\n\tconst challengeOver = options.codes?.challengeOver ?? CHALLENGE_OVER;\n\tif (challengeOver.includes(invalidCode)) {\n\t\tthrow new TypeError(\n\t\t\t`signInFlow(): the code ${JSON.stringify(invalidCode)} is both the wrong-code code (codes.invalidCode) and in codes.challengeOver. A wrong code that leaves tries cannot end the challenge.`,\n\t\t);\n\t}\n\treturn { options, paths, invalidCode, challengeOver };\n}\n\n/**\n * The server side of a BFF's sign-in, over the cookies, the app's\n * `createApiErrors` and its `jsonActions`: where an answer lands, the form\n * steps with their refusals, the second factor, the viewer and sign-out.\n * Built once, beside the app's cookies; it speaks `@nxgt/janus`'s words\n * (`signedIn`, `secondFactor`, `CODE_INVALID`) and the app maps its API's.\n *\n * @example\n * export const signIn = signInFlow({\n * \tcookies, errors, json,\n * \trevoke: (token, request, caller?: Caller) =>\n * \t\texecute(SignOutMutation, {}, { token, ...caller }),\n * \tattemptsLeft: (count, language) => t('auth.attempts-left', { count }, language),\n * });\n */\nexport function signInFlow<L extends string, C = undefined>(\n\toptions: SignInFlowOptions<L, C>,\n): SignInFlow<L, C> {\n\tconst context = resolve(options);\n\tconst { options: o } = context;\n\treturn {\n\t\tcookies: o.cookies,\n\t\terrors: o.errors,\n\t\tjson: o.json,\n\t\tunavailable: (language) => unavailableMessage(o, language),\n\t\tclear: () => o.clear?.() ?? [],\n\t\tattemptsLeft: o.attemptsLeft,\n\t\tguard: (request) => samesitePost(o.json, request),\n\t\tnextParam,\n\t\tsignInRedirect: (request) => signInRedirect(context, request),\n\t\tlanding: (answer, next) => landing(context, answer, next),\n\t\tlandReplacing: (answer, next, o) => landReplacing(context, answer, next, o),\n\t\tsignInStep: (run, o) => signInStep(context, run, o),\n\t\tcodeRefusal: (error, language, whenNone) =>\n\t\t\tcodeRefusal(context, error, language, whenNone),\n\t\treadCodeInput: (request) => readCodeInput(context, request),\n\t\tcodeStep,\n\t\trevokeSession: (request, o) => revokeSession(context, request, o),\n\t\tsignOut: (request, o) => signOut(context, request, o),\n\t\tviewer: (request, read, token) => viewer(context, request, read, token),\n\t\trequireViewer: (request, read, expiresAt) =>\n\t\t\trequireViewer(context, request, read, expiresAt),\n\t};\n}\n",
15
+ "import { redirect } from 'react-router';\nimport { isApiErrorLike } from '../errors/api-error';\nimport { nextOf } from '../errors/page-path';\nimport { routeError } from '../errors/route-error';\nimport { challengeCleared } from './cookies';\nimport type { FlowContext } from './flow-types';\nimport { revokeSession } from './land';\nimport { safeNext, signInPath } from './model';\n\n/** The `next` a sign-in page was opened with, safe to redirect to. */\nexport const nextParam = (request: Request): string =>\n\tsafeNext(new URL(request.url).searchParams.get('next'));\n\n/**\n * The sign-in page, coming back to this request's page afterwards, with the\n * session cookie cleared: what a page answers without a (live) session. A\n * request that carried a session cookie says it ended when `endedParam` is\n * set.\n */\nexport function signInRedirect<L extends string, C>(\n\t{ options, paths }: FlowContext<L, C>,\n\trequest: Request,\n): Response {\n\tconst { endedParam, cookies } = options;\n\tconst query =\n\t\tendedParam && cookies.read(request) ? { [endedParam]: '1' } : undefined;\n\treturn redirect(\n\t\tsignInPath(nextOf(request.url), {\n\t\t\tpath: paths.signIn,\n\t\t\t...(query && { query }),\n\t\t}),\n\t\t{ headers: { 'Set-Cookie': cookies.destroy() } },\n\t);\n}\n\n/**\n * For the sign-in pages: the viewer when the cookie holds a live session,\n * `null` when the API refuses it (a 401), `undefined` without a cookie or\n * when the API cannot tell.\n */\nexport async function viewer<L extends string, C, V>(\n\t{ options }: FlowContext<L, C>,\n\trequest: Request,\n\tread: (token: string) => Promise<V | null>,\n\ttoken: string | null | undefined = options.cookies.token(request),\n): Promise<V | null | undefined> {\n\tif (!token) return undefined;\n\ttry {\n\t\treturn await read(token);\n\t} catch (error) {\n\t\treturn isApiErrorLike(error) && error.status === 401 ? null : undefined;\n\t}\n}\n\n/**\n * The signed-in viewer, else the sign-in redirect. Its `headers` move the\n * cookie's expiry to the session's when the API moved it.\n */\nexport async function requireViewer<L extends string, C, V>(\n\tcontext: FlowContext<L, C>,\n\trequest: Request,\n\tread: (token: string) => Promise<V | null>,\n\texpiresAt: (viewer: V) => string | null,\n): Promise<{ viewer: V; headers: HeadersInit }> {\n\tconst session = context.options.cookies.read(request);\n\tif (!session) throw signInRedirect(context, request);\n\tlet found: V | null;\n\ttry {\n\t\tfound = await read(session.token);\n\t} catch (error) {\n\t\tif (isApiErrorLike(error) && error.status === 401) {\n\t\t\tthrow signInRedirect(context, request);\n\t\t}\n\t\tthrow routeError(error);\n\t}\n\tif (!found) throw signInRedirect(context, request);\n\treturn {\n\t\tviewer: found,\n\t\theaders: context.options.cookies.refreshed(session, expiresAt(found)),\n\t};\n}\n\n/**\n * Ends the session and goes to the sign-in page (`?signed-out=1`), or to\n * `to`. The body is read as JSON first: JSON and same-origin only, so\n * another site cannot sign the user out.\n */\nexport async function signOut<L extends string, C>(\n\tflow: FlowContext<L, C>,\n\trequest: Request,\n\t{\n\t\tto,\n\t\tcontext,\n\t}: {\n\t\treadonly to?: string | undefined;\n\t\treadonly context?: C | undefined;\n\t} = {},\n): Promise<Response> {\n\tconst { options, paths } = flow;\n\tawait options.json.readJson(request);\n\tawait revokeSession(flow, request, { context });\n\tconst headers = new Headers();\n\theaders.append('Set-Cookie', options.cookies.destroy());\n\tfor (const cookie of challengeCleared(options.cookies)) {\n\t\theaders.append('Set-Cookie', cookie);\n\t}\n\tfor (const cookie of options.clear?.() ?? []) {\n\t\theaders.append('Set-Cookie', cookie);\n\t}\n\treturn redirect(\n\t\tto === undefined ? `${paths.signIn}?signed-out=1` : safeNext(to),\n\t\t{ headers },\n\t);\n}\n",
16
+ "import { hasSiteCheck } from '../errors/same-site';\nimport { CHALLENGE_OVER, INVALID_CODE } from './codes';\nimport type {\n\tFlowContext,\n\tSignInFlow,\n\tSignInFlowBase,\n\tSignInFlowOptions,\n\tSignInFlowWithoutSecondFactorOptions,\n} from './flow-types';\nimport { samesitePost } from './guard';\nimport { landing, landReplacing, revokeSession } from './land';\nimport { SIGN_IN_PATHS, type SignInPaths } from './model';\nimport { sitePath } from './model/site-path';\nimport { codeStep, readCodeInput } from './second-factor';\nimport { codeRefusal, signInStep, unavailableMessage } from './step';\nimport {\n\tnextParam,\n\trequireViewer,\n\tsignInRedirect,\n\tsignOut,\n\tviewer,\n} from './viewer';\n\nfunction resolve<L extends string, C>(\n\toptions: SignInFlowOptions<L, C> | SignInFlowWithoutSecondFactorOptions<L, C>,\n): FlowContext<L, C> {\n\tif (\n\t\t(options.attemptsLeft === undefined) !==\n\t\t!options.cookies.commitChallenge\n\t) {\n\t\tthrow new TypeError(\n\t\t\t'signInFlow(): a second factor needs both the challenge cookie (sessionCookies({ challenge })) and attemptsLeft; without one, give neither.',\n\t\t);\n\t}\n\tconst paths: SignInPaths = { ...SIGN_IN_PATHS, ...options.paths };\n\tfor (const key of ['signIn', 'secondFactor'] as const) {\n\t\tsitePath(paths[key], { label: `signInFlow(): paths.${key}` });\n\t}\n\tif (!hasSiteCheck(options.json)) {\n\t\tthrow new TypeError(\n\t\t\t\"signInFlow(): json must come from jsonActions(), which carries the site check every step runs; this object has none (a hand-made double?). Pass the app's jsonActions({ origin }) instance, or a spread copy of it.\",\n\t\t);\n\t}\n\tconst invalidCode = options.codes?.invalidCode ?? INVALID_CODE;\n\tconst challengeOver = options.codes?.challengeOver ?? CHALLENGE_OVER;\n\tif (challengeOver.includes(invalidCode)) {\n\t\tthrow new TypeError(\n\t\t\t`signInFlow(): the code ${JSON.stringify(invalidCode)} is both the wrong-code code (codes.invalidCode) and in codes.challengeOver. A wrong code that leaves tries cannot end the challenge.`,\n\t\t);\n\t}\n\treturn { options, paths, invalidCode, challengeOver };\n}\n\n/**\n * The server side of a BFF's sign-in, over the cookies, the app's\n * `createApiErrors` and its `jsonActions`: where an answer lands, the form\n * steps with their refusals, the second factor, the viewer and sign-out.\n * Built once, beside the app's cookies; it speaks `@nxgt/janus`'s words\n * (`signedIn`, `secondFactor`, `CODE_INVALID`) and the app maps its API's.\n *\n * @example\n * export const signIn = signInFlow({\n * \tcookies, errors, json,\n * \trevoke: (token, request, caller?: Caller) =>\n * \t\texecute(SignOutMutation, {}, { token, ...caller }),\n * \tattemptsLeft: (count, language) => t('auth.attempts-left', { count }, language),\n * });\n */\nexport function signInFlow<L extends string, C = undefined>(\n\toptions: SignInFlowOptions<L, C>,\n): SignInFlow<L, C, true>;\nexport function signInFlow<L extends string, C = undefined>(\n\toptions: SignInFlowWithoutSecondFactorOptions<L, C>,\n): SignInFlow<L, C, false>;\nexport function signInFlow<L extends string, C = undefined>(\n\toptions: SignInFlowOptions<L, C> | SignInFlowWithoutSecondFactorOptions<L, C>,\n): SignInFlow<L, C, boolean> {\n\tconst context = resolve(options);\n\tconst { options: o } = context;\n\tconst base: SignInFlowBase<L, C, boolean> = {\n\t\tcookies: o.cookies,\n\t\terrors: o.errors,\n\t\tjson: o.json,\n\t\tunavailable: (language) => unavailableMessage(o, language),\n\t\tclear: () => o.clear?.() ?? [],\n\t\tguard: (request) => samesitePost(o.json, request),\n\t\tnextParam,\n\t\tsignInRedirect: (request) => signInRedirect(context, request),\n\t\tlanding: (answer, next) => landing(context, answer, next),\n\t\tlandReplacing: (answer, next, o) => landReplacing(context, answer, next, o),\n\t\tsignInStep: (run, o) => signInStep(context, run, o),\n\t\trevokeSession: (request, o) => revokeSession(context, request, o),\n\t\tsignOut: (request, o) => signOut(context, request, o),\n\t\tviewer: (request, read, token) => viewer(context, request, read, token),\n\t\trequireViewer: (request, read, expiresAt) =>\n\t\t\trequireViewer(context, request, read, expiresAt),\n\t};\n\tif (!o.attemptsLeft) return base as SignInFlow<L, C, false>;\n\tconst wording = { ...context, options: o };\n\treturn {\n\t\t...base,\n\t\tattemptsLeft: o.attemptsLeft,\n\t\tcodeRefusal: (error, language, whenNone) =>\n\t\t\tcodeRefusal(wording, error, language, whenNone),\n\t\treadCodeInput: (request) => readCodeInput(context, request),\n\t\tcodeStep,\n\t} as SignInFlow<L, C, true>;\n}\n",
17
17
  "import { data } from 'react-router';\nimport {\n\ttype ApiErrorLike,\n\tattemptsLeftOf,\n\tisApiErrorLike,\n\tretryAfterOf,\n} from '../errors/api-error';\nimport { INVALID_CODE, MAIL_THROTTLED } from './codes';\nimport type { CodeWording } from './flow-types';\nimport { samesitePost } from './guard';\nimport type {\n\tStepUpConfirmAnswer,\n\tStepUpError,\n\tStepUpRequestAnswer,\n\tStepUpVia,\n} from './model';\nimport { codeRefusal } from './step';\nimport { STEP_UP_OVER, STEP_UP_REQUIRED } from './step-up-codes';\nimport type {\n\tStepUpStayed,\n\tStepUpSteps,\n\tStepUpStepsOptions,\n} from './step-up-types';\n\ntype Refused = Extract<StepUpConfirmAnswer, { ok: false }>;\n\ninterface StepUpContext<L extends string, C> extends CodeWording<L> {\n\treadonly steps: StepUpStepsOptions<L, C>;\n\treadonly stepUpRequired: string;\n\treadonly mailThrottled: string;\n}\n\nfunction resolve<L extends string, C>(\n\tsteps: StepUpStepsOptions<L, C>,\n): StepUpContext<L, C> {\n\tconst invalidCode = steps.codes?.invalidCode ?? INVALID_CODE;\n\tconst challengeOver = steps.codes?.challengeOver ?? STEP_UP_OVER;\n\tif (challengeOver.includes(invalidCode)) {\n\t\tthrow new TypeError(\n\t\t\t`stepUpSteps(): the code ${JSON.stringify(invalidCode)} is both the wrong-code code (codes.invalidCode) and in codes.challengeOver. A wrong code that leaves tries cannot end the challenge.`,\n\t\t);\n\t}\n\treturn {\n\t\toptions: steps.signIn,\n\t\tsteps,\n\t\tinvalidCode,\n\t\tchallengeOver,\n\t\tstepUpRequired: steps.codes?.stepUpRequired ?? STEP_UP_REQUIRED,\n\t\tmailThrottled: steps.codes?.mailThrottled ?? MAIL_THROTTLED,\n\t};\n}\n\n/** The refusal's message: the app's own wording, else `errors`'. */\nfunction worded<L extends string, C>(\n\t{ steps }: StepUpContext<L, C>,\n\terror: ApiErrorLike,\n\tlanguage: L,\n): string {\n\treturn (\n\t\tsteps.wording?.(error, language) ??\n\t\tsteps.signIn.errors.apiErrorMessage(error, language)\n\t);\n}\n\n/** A code the API refused. With none left, a mailed one is over and an app's is locked. */\nfunction refusedCode<L extends string, C>(\n\tcontext: StepUpContext<L, C>,\n\terror: ApiErrorLike,\n\tvia: StepUpVia | undefined,\n\tlanguage: L,\n): Omit<Refused, 'ok' | 'intent'> {\n\tconst { steps } = context;\n\tconst attemptsLeft = attemptsLeftOf(error);\n\tif (attemptsLeft === 0 && via !== 'secondFactor') {\n\t\treturn {\n\t\t\terror: 'expired',\n\t\t\tattemptsLeft,\n\t\t\tlocked: false,\n\t\t\tmessage: steps.spent(language),\n\t\t};\n\t}\n\tconst locked = attemptsLeft === 0;\n\tconst message =\n\t\tcodeRefusal(\n\t\t\tcontext,\n\t\t\terror,\n\t\t\tlanguage,\n\t\t\tlocked ? steps.locked?.(language) : undefined,\n\t\t) ?? steps.signIn.errors.apiErrorMessage(error, language);\n\treturn {\n\t\terror: 'invalid',\n\t\t...(attemptsLeft === undefined ? {} : { attemptsLeft }),\n\t\tlocked,\n\t\tmessage,\n\t};\n}\n\n/** The mail throttle's code, or any 429: a rate limit asks to wait, whatever its code. */\nfunction isThrottle<L extends string, C>(\n\tcontext: StepUpContext<L, C>,\n\terror: ApiErrorLike,\n): boolean {\n\treturn error.status === 429 || error.code === context.mailThrottled;\n}\n\nasync function requestStep<L extends string, C>(\n\tcontext: StepUpContext<L, C>,\n\trun: Parameters<StepUpSteps<L>['request']>[0],\n\t{ request, language }: Parameters<StepUpSteps<L>['request']>[1],\n): Promise<StepUpRequestAnswer | StepUpStayed<StepUpRequestAnswer>> {\n\tsamesitePost(context.steps.signIn.json, request);\n\tconst intent = 'request-step-up';\n\ttry {\n\t\tconst { challenge, via } = await run();\n\t\treturn { ok: true, intent, challenge, via };\n\t} catch (error) {\n\t\tconst refusal = context.steps.signIn.errors.apiRefusal(error, language);\n\t\tif (!refusal) throw error;\n\t\tconst api = isApiErrorLike(error);\n\t\tconst message = api ? worded(context, error, language) : refusal.message;\n\t\tconst throttled = api && isThrottle(context, error);\n\t\tconst retryAfter = throttled ? retryAfterOf(error) : undefined;\n\t\treturn data<StepUpRequestAnswer>(\n\t\t\t{\n\t\t\t\tok: false,\n\t\t\t\tintent,\n\t\t\t\terror: throttled ? 'throttled' : 'failed',\n\t\t\t\tmessage,\n\t\t\t\t...(retryAfter === undefined ? {} : { retryAfter }),\n\t\t\t},\n\t\t\t{ status: refusal.status },\n\t\t);\n\t}\n}\n\nasync function confirmStep<L extends string, C>(\n\tcontext: StepUpContext<L, C>,\n\trun: () => Promise<unknown>,\n\t{ request, language, via }: Parameters<StepUpSteps<L>['confirm']>[1],\n): Promise<StepUpConfirmAnswer | StepUpStayed<StepUpConfirmAnswer>> {\n\tsamesitePost(context.steps.signIn.json, request);\n\tconst intent = 'confirm-step-up';\n\ttry {\n\t\tawait run();\n\t\treturn { ok: true, intent };\n\t} catch (error) {\n\t\tconst refusal = context.steps.signIn.errors.apiRefusal(error, language);\n\t\tif (!refusal) throw error;\n\t\tconst stay = (\n\t\t\tfailure: Omit<Refused, 'ok' | 'intent'>,\n\t\t): StepUpStayed<StepUpConfirmAnswer> =>\n\t\t\tdata<StepUpConfirmAnswer>(\n\t\t\t\t{ ok: false, intent, ...failure },\n\t\t\t\t{ status: refusal.status },\n\t\t\t);\n\t\tconst failed = (message: string) =>\n\t\t\tstay({ error: 'failed' satisfies StepUpError, locked: false, message });\n\t\tif (!isApiErrorLike(error)) return failed(refusal.message);\n\t\tif (error.status === 429) {\n\t\t\tconst retryAfter = retryAfterOf(error);\n\t\t\treturn stay({\n\t\t\t\terror: 'throttled',\n\t\t\t\tlocked: false,\n\t\t\t\tmessage: worded(context, error, language),\n\t\t\t\t...(retryAfter === undefined ? {} : { retryAfter }),\n\t\t\t});\n\t\t}\n\t\tif (error.code === context.invalidCode) {\n\t\t\treturn stay(refusedCode(context, error, via, language));\n\t\t}\n\t\tif (error.code && context.challengeOver.includes(error.code)) {\n\t\t\treturn stay({\n\t\t\t\terror: 'expired',\n\t\t\t\tlocked: false,\n\t\t\t\tmessage: context.steps.spent(language),\n\t\t\t});\n\t\t}\n\t\treturn failed(worded(context, error, language));\n\t}\n}\n\n/**\n * The server side of a BFF's step-up: `request` and `confirm` answer the\n * page's fetcher with `StepUpRequestAnswer` and `StepUpConfirmAnswer`, which\n * `stepUpReducer` takes as they are. It speaks `@nxgt/janus`'s words\n * (`CODE_INVALID`, `TOKEN_*`, `STEP_UP_REQUIRED`); the app maps the rest.\n *\n * @example\n * const stepUp = stepUpSteps({\n * \terrors,\n * \tattemptsLeft: (count, language) => t('code.attempts-left', { count }, language),\n * \tspent: (language) => t('step-up.spent', {}, language),\n * \tlocked: (language) => t('step-up.locked', {}, language),\n * });\n */\nexport function stepUpSteps<L extends string, C = undefined>(\n\toptions: StepUpStepsOptions<L, C>,\n): StepUpSteps<L> {\n\tconst context = resolve(options);\n\treturn {\n\t\trequest: (run, o) => requestStep(context, run, o),\n\t\tconfirm: (run, o) => confirmStep(context, run, o),\n\t\tisStepUpRequired: (error) =>\n\t\t\tisApiErrorLike(error) && error.code === context.stepUpRequired,\n\t};\n}\n",
18
18
  "import { CHALLENGE_OVER } from './codes';\n\n/** `@nxgt/janus`'s code for a mutation that needs a recent step-up. */\nexport const STEP_UP_REQUIRED = 'STEP_UP_REQUIRED';\n\n/**\n * The codes that end a step-up's challenge: unknown, spent, expired or stale,\n * and a second factor that changed since the request (`SECOND_FACTOR_ACTIVE`,\n * `SECOND_FACTOR_NOT_ENROLLED`). The user asks again.\n */\nexport const STEP_UP_OVER = [\n\t...CHALLENGE_OVER,\n\t'TOKEN_STALE',\n\t'SECOND_FACTOR_ACTIVE',\n\t'SECOND_FACTOR_NOT_ENROLLED',\n] as const;\n"
19
19
  ],
20
- "mappings": "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",
21
- "debugId": "5700D84F1A8EF7CF64756E2164756E21",
20
+ "mappings": "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",
21
+ "debugId": "DC4CDFEB0777ED0864756E2164756E21",
22
22
  "names": []
23
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"land.d.ts","sourceRoot":"","sources":["../../src/session/land.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAK1E;;;GAGG;AACH,wBAAsB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EACtD,EAAE,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EAC9B,OAAO,EAAE,OAAO,EAChB,EAAE,OAAO,EAAE,GAAE;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,SAAS,CAAA;CAAO,GACpD,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED,kGAAkG;AAClG,eAAO,MAAM,MAAM,GAClB,QAAQ,YAAY,KAClB,MAAM,IAAI,OAAO,CAAC,YAAY,EAAE;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,CAIjB,CAAC;AAEzC;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAC1C,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EACrC,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,MAAM,GACV,QAAQ,GAAG,IAAI,CA0BjB;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EACtD,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EACvB,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,MAAM,EACZ,EACC,OAAO,EACP,QAAuB,EACvB,OAAO,GACP,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,GACjD,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAU1B"}
1
+ {"version":3,"file":"land.d.ts","sourceRoot":"","sources":["../../src/session/land.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAK1E;;;GAGG;AACH,wBAAsB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EACtD,EAAE,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EAC9B,OAAO,EAAE,OAAO,EAChB,EAAE,OAAO,EAAE,GAAE;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,SAAS,CAAA;CAAO,GACpD,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED,kGAAkG;AAClG,eAAO,MAAM,MAAM,GAClB,QAAQ,YAAY,KAClB,MAAM,IAAI,OAAO,CAAC,YAAY,EAAE;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,CAIjB,CAAC;AAEzC;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAC1C,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EACrC,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,MAAM,GACV,QAAQ,GAAG,IAAI,CAiCjB;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EACtD,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EACvB,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,MAAM,EACZ,EACC,OAAO,EACP,QAAuB,EACvB,OAAO,GACP,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,GACjD,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAU1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"link-land.d.ts","sourceRoot":"","sources":["../../src/session/link-land.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAIjD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAkBxE;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EACnD,OAAO,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,EAC3B,MAAM,EAAE,YAAY,EACpB,EACC,OAAO,EACP,QAAQ,EACR,KAAK,EACL,OAAO,GACP,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,GAC7C,OAAO,CAAC,QAAQ,GAAG,UAAU,CAAC,CAsBhC;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EACnD,OAAO,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,EAC3B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,EAChB,EAAE,OAAO,EAAE,GAAE;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,SAAS,CAAA;CAAO,GACpD,OAAO,CAAC,QAAQ,CAAC,CAUnB"}
1
+ {"version":3,"file":"link-land.d.ts","sourceRoot":"","sources":["../../src/session/link-land.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAIjD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAkBxE;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EACnD,OAAO,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,EAC3B,MAAM,EAAE,YAAY,EACpB,EACC,OAAO,EACP,QAAQ,EACR,KAAK,EACL,OAAO,GACP,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,GAC7C,OAAO,CAAC,QAAQ,GAAG,UAAU,CAAC,CAsBhC;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EACnD,OAAO,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,EAC3B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,OAAO,EAChB,EAAE,OAAO,EAAE,GAAE;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,SAAS,CAAA;CAAO,GACpD,OAAO,CAAC,QAAQ,CAAC,CAYnB"}
@@ -17,7 +17,7 @@ export interface AuthLinksOptions<L extends string, C = undefined> {
17
17
  * The app's sign-in flow: it lands the sessions a link opens, and its
18
18
  * `cookies`, `errors` and `json` are the ones the links use.
19
19
  */
20
- readonly signIn: SignInFlow<L, C>;
20
+ readonly signIn: SignInFlow<L, C, boolean>;
21
21
  /** The codes the links tell apart; `@nxgt/janus`'s by default. */
22
22
  readonly codes?: AuthLinksCodes | undefined;
23
23
  /**
@@ -39,7 +39,7 @@ export interface AuthLinksOptions<L extends string, C = undefined> {
39
39
  readonly invalidState?: string | undefined;
40
40
  }
41
41
  /** The options with what they read of the flow: the functions take this. */
42
- export type LinksContext<L extends string, C = undefined> = AuthLinksOptions<L, C> & Pick<SignInFlow<L, C>, 'cookies' | 'errors' | 'json'>;
42
+ export type LinksContext<L extends string, C = undefined> = AuthLinksOptions<L, C> & Pick<SignInFlow<L, C, boolean>, 'cookies' | 'errors' | 'json'>;
43
43
  /** What a token page answers when it stays on the page. */
44
44
  export type LinkStayed = ReturnType<typeof data<FormErrors>>;
45
45
  /** A link request's answer: always "sent", whoever the e-mail is. */
@@ -1 +1 @@
1
- {"version":3,"file":"link-types.d.ts","sourceRoot":"","sources":["../../src/session/link-types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"link-types.d.ts","sourceRoot":"","sources":["../../src/session/link-types.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { SignInFlow, SignInFlowOptions } from './flow-types';
1
+ import type { SignInFlow, SignInFlowOptions, SignInFlowWithoutSecondFactorOptions } from './flow-types';
2
2
  /**
3
3
  * The server side of a BFF's sign-in, over the cookies, the app's
4
4
  * `createApiErrors` and its `jsonActions`: where an answer lands, the form
@@ -14,5 +14,6 @@ import type { SignInFlow, SignInFlowOptions } from './flow-types';
14
14
  * attemptsLeft: (count, language) => t('auth.attempts-left', { count }, language),
15
15
  * });
16
16
  */
17
- export declare function signInFlow<L extends string, C = undefined>(options: SignInFlowOptions<L, C>): SignInFlow<L, C>;
17
+ export declare function signInFlow<L extends string, C = undefined>(options: SignInFlowOptions<L, C>): SignInFlow<L, C, true>;
18
+ export declare function signInFlow<L extends string, C = undefined>(options: SignInFlowWithoutSecondFactorOptions<L, C>): SignInFlow<L, C, false>;
18
19
  //# sourceMappingURL=sign-in.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sign-in.d.ts","sourceRoot":"","sources":["../../src/session/sign-in.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAe,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAqC/E;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,GAAG,SAAS,EACzD,OAAO,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,GAC9B,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CA0BlB"}
1
+ {"version":3,"file":"sign-in.d.ts","sourceRoot":"","sources":["../../src/session/sign-in.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEX,UAAU,EAEV,iBAAiB,EACjB,oCAAoC,EACpC,MAAM,cAAc,CAAC;AA6CtB;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,GAAG,SAAS,EACzD,OAAO,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,GAC9B,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;AAC1B,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,GAAG,SAAS,EACzD,OAAO,EAAE,oCAAoC,CAAC,CAAC,EAAE,CAAC,CAAC,GACjD,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"viewer.d.ts","sourceRoot":"","sources":["../../src/session/viewer.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAIhD,sEAAsE;AACtE,eAAO,MAAM,SAAS,GAAI,SAAS,OAAO,KAAG,MACW,CAAC;AAEzD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EACjD,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EACrC,OAAO,EAAE,OAAO,GACd,QAAQ,CAWV;AAED;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAClD,EAAE,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EAC9B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,EAC1C,KAAK,GAAE,MAAM,GAAG,IAAI,GAAG,SAA0C,GAC/D,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,CAO/B;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EACzD,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EAC1B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,EAC1C,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,GACrC,OAAO,CAAC;IAAE,MAAM,EAAE,CAAC,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC,CAiB9C;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAChD,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EACvB,OAAO,EAAE,OAAO,EAChB,EACC,EAAE,EACF,OAAO,GACP,GAAE;IACF,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC;CAC5B,GACJ,OAAO,CAAC,QAAQ,CAAC,CAcnB"}
1
+ {"version":3,"file":"viewer.d.ts","sourceRoot":"","sources":["../../src/session/viewer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAIhD,sEAAsE;AACtE,eAAO,MAAM,SAAS,GAAI,SAAS,OAAO,KAAG,MACW,CAAC;AAEzD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EACjD,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EACrC,OAAO,EAAE,OAAO,GACd,QAAQ,CAWV;AAED;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAClD,EAAE,OAAO,EAAE,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EAC9B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,EAC1C,KAAK,GAAE,MAAM,GAAG,IAAI,GAAG,SAA0C,GAC/D,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,CAO/B;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EACzD,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EAC1B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,EAC1C,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,GACrC,OAAO,CAAC;IAAE,MAAM,EAAE,CAAC,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC,CAiB9C;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAChD,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,EACvB,OAAO,EAAE,OAAO,EAChB,EACC,EAAE,EACF,OAAO,GACP,GAAE;IACF,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC;CAC5B,GACJ,OAAO,CAAC,QAAQ,CAAC,CAgBnB"}
package/docs/README.md CHANGED
@@ -20,6 +20,7 @@ installable and usable offline, wording an API's refusals, the BFF session, a Gr
20
20
  | [Installable and offline](guide/pwa.md) | making the app installable and usable offline: the subpaths of `@alxia/react-router/pwa`, the service worker (`startWorker`, `/pwa/worker`), its build by `alxia()` or `buildWorker` (`/pwa/build`), its contract and cache names, which requests the worker caches and which responses it stores, the precache list read from a build, or clearing the caches at sign-out |
21
21
  | [An API's refusals](guide/errors.md) | wording a GraphQL API's refusals for the pages: recognising an `ApiError`, the request id, the page a request is for, the catalogue store, the form checks in the API's words, the provider, and the loaders, the error page and the actions that answer with it, the JSON actions with their CSRF check and fetcher hook, calling the API from a loader (`apiCallFailure`), and keeping the catalogue fresh (`@alxia/react-router/errors`) |
22
22
  | [A BFF session](guide/session.md) | redirecting after sign-in to a page of the site and nowhere else, the sign-in pages' paths, the session's cookies, the sign-in flow (where an answer lands, a refused code, the viewer, sign-out; `@alxia/react-router/session`), the pages a mail's link opens (a magic link, a password reset: `authLinks`), the page's hooks (sign-out, a mail link's parameter, a one-code field; `@alxia/react-router/session/react`), and the step-up's state and the props it gives the UI (`@alxia/react-router/session/model`) |
23
+ | [Links through React Router](guide/link.md) | `RouterLink`, React Router's `Link` as the link component of a UI library that hands `href` and spreads anchor props; `@alxia/react-router/link` |
23
24
  | [Lists whose state is the URL](guide/lists.md) | a search and filters in the address bar that drop the cursor (`useSearchChange`), a skeleton while the same page reloads (`useSameRouteLoading`), and a load-more button that appends pages by cursor and keeps the rows when one fails (`usePagedRows`); `@alxia/react-router/lists` |
24
25
  | [Subscriptions for a BFF](guide/subscriptions.md) | showing a GraphQL subscription in the browser without giving it the API's token or a way to write a document: a resource route with a map of operation names to the server's documents, the same-site check, the bearer from the session, extra upstream headers, a heartbeat, abort propagation, size limits and a 429 passed through with `retryAfter` (`@alxia/react-router/subscription`) |
25
26
  | [Troubleshooting](troubleshooting.md) | something went wrong and you have the message, or a page streams, hydrates or reads its context unlike you expected |
@@ -0,0 +1,32 @@
1
+ # Links through React Router
2
+
3
+ A UI library that renders its links through a component of its own (a
4
+ `LinkComponentProvider`) hands that component an `href` and spreads every other
5
+ prop onto the anchor. React Router's `Link` takes `to`, so an app adapts it.
6
+ `@alxia/react-router/link` exports that adapter. `react` and `react-router` are
7
+ its only imports, and it renders on the server.
8
+
9
+ ```tsx
10
+ // app/root.tsx
11
+ import { RouterLink } from '@alxia/react-router/link';
12
+
13
+ export default function App() {
14
+ return (
15
+ <LinkComponentProvider component={RouterLink}>
16
+ <Outlet />
17
+ </LinkComponentProvider>
18
+ );
19
+ }
20
+ ```
21
+
22
+ ## What it does
23
+
24
+ - `href` (or `to`, React Router's own name, never both) is the destination: a
25
+ string or a `To` object.
26
+ - An external target (`https://…`, `mailto:`, `//host`) renders a plain anchor with that `href`; React Router's `Link` already handles it.
27
+ - `className`, `children`, `ref`, `aria-*`, `target` and every other anchor prop
28
+ reach the `<a>`.
29
+ - `Link`'s own props (`replace`, `prefetch`, `reloadDocument`, `state`, ...) still
30
+ apply, so a link can opt out of client-side navigation.
31
+
32
+ `RouterLinkProps` is the type of its props, for a wrapper of your own.
@@ -342,7 +342,8 @@ decides when a session is over.
342
342
  `commit` takes the expiry as an ISO string or a `Date`, and sets `Max-Age`
343
343
  to the seconds left: floored, and 0 for a past or unreadable date.
344
344
 
345
- The challenge has its own three: `commitChallenge(challenge, expiresAt?)`,
345
+ With no `challenge` option the cookies have no challenge helpers, and `names`
346
+ holds the session alone (`SessionCookies<false>`). The challenge has its own three: `commitChallenge(challenge, expiresAt?)`,
346
347
  `challengeOf(request)`, `destroyChallenge()`.
347
348
 
348
349
  `cookies.names` holds the names as sent, prefixes included
@@ -543,6 +544,30 @@ without a cookie or when the API cannot tell.
543
544
 
544
545
  ## The second factor
545
546
 
547
+ A second factor is optional. An API with none builds the cookies without
548
+ `challenge` and the flow without `attemptsLeft`. Neither carries a
549
+ second-factor member on its type, a `secondFactor` answer lands nowhere
550
+ (`landing` returns `null`), and no challenge cookie is set or cleared:
551
+
552
+ ```ts
553
+ export const cookies = sessionCookies({ session: 'my-app_session', secure: true });
554
+ export const signIn = signInFlow({ cookies, errors, json, revoke });
555
+ // signIn.codeStep, signIn.readCodeInput and cookies.challengeOf do not exist.
556
+ ```
557
+
558
+ Give both or neither: `signInFlow` refuses cookies with a challenge and no
559
+ `attemptsLeft` at compile time, and throws a TypeError at runtime when only
560
+ one is given ([troubleshooting](../troubleshooting.md)). To type a helper
561
+ that takes the flow or the cookies, use `SignInFlow<L, C, false>` and
562
+ `SessionCookies<false>` (no second factor), `SignInFlow<L, C>` and
563
+ `SessionCookies` (one), or `SignInFlow<L, C, boolean>` and
564
+ `SessionCookies<boolean>` for either. `signInFlow` itself takes
565
+ `SessionCookies<true>` or `SessionCookies<false>`, not the union: when
566
+ `challenge` may be `undefined`, `sessionCookies` returns
567
+ `SessionCookies<boolean>`, so branch on `challenge` and call `sessionCookies`
568
+ in each branch. `stepUpSteps` takes a flow with a second factor; `authLinks`
569
+ takes either. The rest of this section is for an app that has one.
570
+
546
571
  The sign-in page's answer `secondFactor` leads to the second-factor page,
547
572
  whose loader needs the challenge cookie (`cookies.challengeOf(request)`; back
548
573
  to the sign-in page without one) and whose action posts the code.
package/docs/roadmap.md CHANGED
@@ -85,8 +85,8 @@ Nothing planned.
85
85
  - **`@alxia/react-router/session` and `/session/model`: a BFF session for a
86
86
  janus API.** An app that is the backend of its own pages and calls a GraphQL
87
87
  API that signs users in with `@nxgt/janus` gets its session from one place:
88
- `sessionCookies` (the session and the second-factor challenge, in an
89
- HttpOnly cookie), `signInFlow` (where a sign-in lands, the second factor,
88
+ `sessionCookies` (the session and, when the API has one, the second-factor
89
+ challenge, in an HttpOnly cookie), `signInFlow` (where a sign-in lands, the second factor,
90
90
  the viewer, sign-out), `stepUpSteps` (the code asked before a sensitive
91
91
  action), `authLinks` and `TOKEN_PAGE_HEADERS` (the pages a mail's link
92
92
  opens). `/session/model` imports nothing and is safe in a component: `safeNext`
@@ -32,6 +32,7 @@ a loader, a message React Router or the browser prints, or an error from
32
32
  - [`useNonce()` is undefined on the server](#usenonce-is-undefined-on-the-server)
33
33
  - [`TypeError: InlineScript(): the code contains "</script" or "<!--", which ends or confuses the script element; escape it, such as "<\/script" inside a string`](#typeerror-inlinescript-the-code-contains-script-or----which-ends-or-confuses-the-script-element-escape-it-such-as-script-inside-a-string)
34
34
  - [`TypeError: createPageLanguage(): the fallback … is not one of the languages; got ….`](#typeerror-createpagelanguage-the-fallback--is-not-one-of-the-languages-got-)
35
+ - [`TypeError: signInFlow(): a second factor needs both the challenge cookie (sessionCookies({ challenge })) and attemptsLeft; without one, give neither.`](#typeerror-signinflow-a-second-factor-needs-both-the-challenge-cookie-sessioncookies-challenge--and-attemptsleft-without-one-give-neither)
35
36
  - [The error page shows in the fallback language](#the-error-page-shows-in-the-fallback-language)
36
37
  - [`Error: You must render this element inside a <HydratedRouter> element`](#error-you-must-render-this-element-inside-a-hydratedrouter-element)
37
38
  - [`alxia-react-router: … already exists, and reveal leaves it as it is. Run alxia-react-router reveal --force to overwrite it.`](#alxia-react-router--already-exists-and-reveal-leaves-it-as-it-is-run-alxia-react-router-reveal---force-to-overwrite-it)
@@ -764,6 +765,33 @@ source than the fallback.
764
765
  createPageLanguage({ languages: ['en', 'fr'], fallback: 'en' });
765
766
  ```
766
767
 
768
+ ### `TypeError: signInFlow(): a second factor needs both the challenge cookie (sessionCookies({ challenge })) and attemptsLeft; without one, give neither.`
769
+
770
+ ```text
771
+ TypeError: signInFlow(): a second factor needs both the challenge cookie (sessionCookies({ challenge })) and attemptsLeft; without one, give neither.
772
+ ```
773
+
774
+ **When:** the module that calls `signInFlow` loads.
775
+
776
+ **Why:** a second factor is the challenge cookie (`sessionCookies({ challenge })`)
777
+ and the `attemptsLeft` wording together. The flow got only one: cookies with
778
+ a challenge and no `attemptsLeft`, or an `attemptsLeft` and cookies built
779
+ without `challenge`. TypeScript refuses the first when the cookies are typed
780
+ `SessionCookies<true>`; a set typed `SessionCookies<boolean>` (a `challenge`
781
+ that may be `undefined`) or a call from JavaScript reaches this check.
782
+
783
+ **Fix:** give both, or neither:
784
+
785
+ ```ts
786
+ // with a second factor
787
+ const cookies = sessionCookies({ session: 'my-app_session', challenge: 'my-app_challenge', secure });
788
+ signInFlow({ cookies, errors, json, revoke, attemptsLeft });
789
+
790
+ // without one
791
+ const plain = sessionCookies({ session: 'my-app_session', secure });
792
+ signInFlow({ cookies: plain, errors, json, revoke });
793
+ ```
794
+
767
795
  ### The error page shows in the fallback language
768
796
 
769
797
  **When:** on an error document, the root's `<html lang>` or text shows the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alxia/react-router",
3
- "version": "0.24.1",
3
+ "version": "0.26.0",
4
4
  "description": "alxia as the server of a React Router framework app: server rendering behind the app's hooks, loaders reading its typed context, the client's assets served; the code with no UI an installable app shares (`/pwa`), its hooks (`/pwa/react`), `noCache` for the worker's script (`/pwa/server`), the service worker on Workbox (`/pwa/worker`) and its build (`/pwa/build`), an API's refusals worded for the page (`/errors`), the BFF session: cookies, sign-in, step-up and mail links (`/session`), its page hooks (`/session/react`) and what it shares with the page (`/session/model`), a GraphQL subscription relayed to the browser over SSE (`/subscription`), the page's CSP nonce and inline boot scripts (`/document`) and a streaming `entry.server` (`/entry`)",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -87,6 +87,11 @@
87
87
  "import": "./dist/lists/index.js",
88
88
  "default": "./dist/lists/index.js"
89
89
  },
90
+ "./link": {
91
+ "types": "./dist/link/index.d.ts",
92
+ "import": "./dist/link/index.js",
93
+ "default": "./dist/link/index.js"
94
+ },
90
95
  "./subscription": {
91
96
  "types": "./dist/subscription/index.d.ts",
92
97
  "import": "./dist/subscription/index.js",
@@ -121,6 +126,7 @@
121
126
  "src/session/react/index.ts",
122
127
  "src/subscription/index.ts",
123
128
  "src/lists/index.ts",
129
+ "src/link/index.ts",
124
130
  "src/document/index.ts",
125
131
  "src/entry/index.ts",
126
132
  "src/pwa/worker/index.ts",